{"$schema":"https://ui.shadcn.com/schema/registry-item.json","name":"envelope-send","type":"registry:component","title":"Envelope send","description":"Folds your note into an envelope, seals it and sends it off.","author":"Yash Bavadiya <https://xevrion.dev>","dependencies":["motion"],"registryDependencies":["utils"],"files":[{"path":"components/envelope-send.tsx","type":"registry:component","content":"\"use client\";\n\nimport {\n  useEffect,\n  useId,\n  useRef,\n  useState,\n  useSyncExternalStore,\n} from \"react\";\nimport {\n  animate,\n  motion,\n  useMotionValue,\n  useTransform,\n  type AnimationPlaybackControls,\n  type MotionValue,\n  type ValueAnimationTransition,\n} from \"motion/react\";\nimport { useReducedMotion } from \"@/hooks/use-reduced-motion\";\nimport { cn } from \"@/lib/utils\";\n\ntype Phase = \"compose\" | \"sending\" | \"sent\";\n\nconst EASE_OUT = [0.23, 1, 0.32, 1] as const;\nconst EASE_IN_OUT = [0.77, 0, 0.175, 1] as const;\n// Gravity: the note drops into the pocket and the envelope is flung away,\n// so both gather speed rather than settle.\nconst EASE_IN = [0.55, 0, 1, 0.45] as const;\n\n// Envelope geometry in px. The paper area is 200 tall, so a 168 tall\n// envelope sits centered in it with 16px to spare above and below.\nconst PAPER_H = 200;\nconst ENV_W = 272;\nconst ENV_H = 168;\nconst ENV_TOP = (PAPER_H - ENV_H) / 2;\n// The flap tip lands just past the pocket's V (92), so it fully covers it.\nconst FLAP_H = 96;\nconst POCKET_V = 92;\n// The note shrinks to this width: 20px clear of each side of the envelope.\nconst NOTE_W = 232;\nconst MONTHS = \"JAN FEB MAR APR MAY JUN JUL AUG SEP OCT NOV DEC\".split(\" \");\n\n// Wax seal outline: a circle with 13 soft lobes, like wax squeezed out\n// under the stamp. Built once at module load.\nconst SEAL_R = 17;\nconst SEAL_PATH =\n  Array.from({ length: 52 }, (_, i) => {\n    const a = (i / 52) * Math.PI * 2;\n    const r = SEAL_R - 1.6 + Math.sin(a * 13) * 1.1 + Math.sin(a * 5 + 1) * 0.5;\n    const x = (SEAL_R + Math.cos(a) * r).toFixed(2);\n    const y = (SEAL_R + Math.sin(a) * r).toFixed(2);\n    return `${i === 0 ? \"M\" : \"L\"}${x} ${y}`;\n  }).join(\" \") + \" Z\";\n\n// Physical materials, not UI colors: sealing wax, and the red and blue of\n// airmail borders and postage ink. Each pair is tuned per theme so the\n// print sits on the paper instead of glowing on it.\nconst WAX = \"light-dark(#b3261e, #c4382c)\";\nconst WAX_LIGHT = \"light-dark(#d9594b, #e06a5c)\";\nconst AIR_RED = \"light-dark(#d4483b, #d9594b)\";\nconst AIR_BLUE = \"light-dark(#2d5bb8, #5b85d6)\";\n\nconst noop = () => () => {};\nconst readModifier = () =>\n  /Mac|iPhone|iPad/.test(navigator.platform) ? \"⌘\" : \"Ctrl\";\n\nexport function EnvelopeSend({\n  onSend,\n  title = \"Send feedback\",\n  description = \"Tell us what to fix. We read every note.\",\n  placeholder = \"What could be better?\",\n  className,\n}: {\n  onSend?: (message: string) => void;\n  title?: string;\n  description?: string;\n  placeholder?: string;\n  className?: string;\n}) {\n  const reduceMotion = useReducedMotion();\n  const modifier = useSyncExternalStore(noop, readModifier, () => \"Ctrl\");\n  const fieldId = useId();\n  const [phase, setPhase] = useState<Phase>(\"compose\");\n  const [message, setMessage] = useState(\"\");\n  const paperRef = useRef<HTMLDivElement>(null);\n  const textRef = useRef<HTMLTextAreaElement>(null);\n  const againRef = useRef<HTMLButtonElement>(null);\n  const running = useRef<AnimationPlaybackControls[]>([]);\n  const svgId = useId().replace(/[^a-zA-Z0-9_-]/g, \"\");\n  const [sentOn, setSentOn] = useState(\"\");\n\n  // The header and footer fade as one; the note, envelope and the group\n  // they fly off in each get their own values so steps can overlap.\n  const chrome = useMotionValue(1);\n  const noteScale = useMotionValue(1);\n  const noteY = useMotionValue(0);\n  const envOpacity = useMotionValue(0);\n  const envY = useMotionValue(16);\n  // 180deg is fully open, folded up behind the note.\n  const flap = useMotionValue(180);\n  const seal = useMotionValue(0);\n  const groupX = useMotionValue(0);\n  const groupY = useMotionValue(0);\n  const groupRotate = useMotionValue(0);\n  const groupOpacity = useMotionValue(1);\n  const groupBlur = useMotionValue(0);\n  const groupFilter = useTransform(groupBlur, (b) =>\n    b > 0.01 ? `blur(${b}px)` : \"none\",\n  );\n  // Open, the flap sits behind the note; past vertical it folds over it.\n  const flapZ = useTransform(flap, (r) => (r > 90 ? 1 : 4));\n  // Pressed down from above: the seal lands a little large and settles\n  // flat, rather than growing out of nothing.\n  const sealScale = useTransform(seal, [0, 1], [1.35, 1]);\n\n  useEffect(() => {\n    const list = running;\n    return () => list.current.forEach((c) => c.stop());\n  }, []);\n\n  const play = (\n    value: MotionValue<number>,\n    to: number,\n    transition: ValueAnimationTransition<number>,\n  ) => {\n    const controls = animate(value, to, transition);\n    running.current.push(controls);\n    return controls;\n  };\n\n  const finish = () => {\n    running.current = [];\n    setPhase(\"sent\");\n    // Wait a frame for the sent panel to lose inert before focusing it.\n    requestAnimationFrame(() => againRef.current?.focus());\n  };\n\n  const send = async () => {\n    const text = message.trim();\n    const paper = paperRef.current;\n    if (phase !== \"compose\" || !text || !paper) return;\n    setPhase(\"sending\");\n    const now = new Date();\n    setSentOn(`${now.getDate()} ${MONTHS[now.getMonth()]}`);\n    onSend?.(text);\n\n    if (reduceMotion) {\n      chrome.jump(0);\n      groupOpacity.jump(0);\n      finish();\n      return;\n    }\n\n    const scale = NOTE_W / paper.offsetWidth;\n    const noteH = PAPER_H * scale;\n\n    // 1. 240ms: the note shrinks and lifts over the envelope's mouth while\n    // the envelope rises in beneath it.\n    await Promise.all([\n      play(chrome, 0, { duration: 0.15, ease: EASE_OUT }),\n      play(noteScale, scale, { duration: 0.24, ease: EASE_IN_OUT }),\n      play(noteY, ENV_TOP - noteH * 0.55, {\n        duration: 0.24,\n        ease: EASE_IN_OUT,\n      }),\n      play(envOpacity, 1, { duration: 0.18, ease: EASE_OUT }),\n      play(envY, 0, { duration: 0.24, ease: EASE_OUT }),\n    ]);\n    // 2. 160ms: it drops into the pocket, 10px below the envelope's top.\n    await play(noteY, ENV_TOP + 10, { duration: 0.16, ease: EASE_IN });\n    // 3. 180ms: the flap folds shut over it.\n    await play(flap, 0, { duration: 0.18, ease: EASE_IN_OUT });\n    // 4 and 5. The wax presses on, holds for a beat so you see it land,\n    // then the envelope is flung up and away. The flight runs 340ms, longer\n    // than a UI transition, because it is the one-off payoff of the send.\n    play(seal, 1, { type: \"spring\", duration: 0.3, bounce: 0.25 });\n    const away = { duration: 0.34, ease: EASE_IN, delay: 0.22 };\n    await Promise.all([\n      play(groupX, 180, away),\n      play(groupY, -60, away),\n      play(groupRotate, -10, away),\n      play(groupOpacity, 0, away),\n    ]);\n    finish();\n  };\n\n  const writeAnother = () => {\n    setMessage(\"\");\n    setPhase(\"compose\");\n    // Everything resets out of sight, then the blank note fades back in.\n    noteScale.jump(1);\n    noteY.jump(0);\n    envOpacity.jump(0);\n    envY.jump(16);\n    flap.jump(180);\n    seal.jump(0);\n    groupX.jump(0);\n    groupRotate.jump(0);\n    if (reduceMotion) {\n      groupY.jump(0);\n      groupOpacity.jump(1);\n      chrome.jump(1);\n    } else {\n      groupY.jump(8);\n      groupBlur.jump(4);\n      const enter = { duration: 0.3, ease: EASE_OUT };\n      play(groupY, 0, enter);\n      play(groupBlur, 0, enter);\n      play(groupOpacity, 1, enter);\n      play(chrome, 1, enter);\n    }\n    requestAnimationFrame(() => textRef.current?.focus());\n  };\n\n  const empty = message.trim() === \"\";\n  const FLAP_PATH = `M1 1 L${ENV_W - 1} 1 L${ENV_W / 2 + 8} ${FLAP_H - 6} Q${ENV_W / 2} ${FLAP_H} ${ENV_W / 2 - 8} ${FLAP_H - 6} Z`;\n  // Starts at the very corners (not 6px down) so no sliver of the note\n  // shows between the closed flap and the pocket's slanted edges.\n  const POCKET_PATH = `M0.5 0.5 L${ENV_W / 2} ${POCKET_V} L${ENV_W - 0.5} 0.5 L${ENV_W - 0.5} ${ENV_H - 6} Q${ENV_W - 0.5} ${ENV_H - 0.5} ${ENV_W - 6} ${ENV_H - 0.5} L6 ${ENV_H - 0.5} Q0.5 ${ENV_H - 0.5} 0.5 ${ENV_H - 6} Z`;\n  const envelopeBox = {\n    left: `calc(50% - ${ENV_W / 2}px)`,\n    top: ENV_TOP,\n    width: ENV_W,\n  };\n\n  return (\n    <div\n      className={cn(\n        \"relative h-[392px] w-[min(440px,100%)] rounded-[20px] bg-muted p-5 shadow-raised\",\n        className,\n      )}\n    >\n      <div inert={phase !== \"compose\"} className=\"flex h-full flex-col\">\n        <motion.div style={{ opacity: chrome }}>\n          <h2 className=\"text-base font-medium text-foreground\">{title}</h2>\n          <p className=\"mt-1 text-sm text-muted-foreground\">{description}</p>\n        </motion.div>\n\n        <motion.div\n          className=\"relative mt-4 shrink-0\"\n          style={{\n            height: PAPER_H,\n            x: groupX,\n            y: groupY,\n            rotate: groupRotate,\n            opacity: groupOpacity,\n            filter: groupFilter,\n          }}\n        >\n          {/* Envelope back: the inside you see behind the note. */}\n          <motion.svg\n            aria-hidden\n            viewBox={`0 0 ${ENV_W} ${ENV_H}`}\n            className=\"pointer-events-none absolute z-[1] overflow-visible\"\n            style={{\n              ...envelopeBox,\n              height: ENV_H,\n              opacity: envOpacity,\n              y: envY,\n            }}\n          >\n            <rect\n              x=\"0.5\"\n              y=\"0.5\"\n              width={ENV_W - 1}\n              height={ENV_H - 1}\n              rx=\"6\"\n              className=\"fill-background stroke-foreground/15\"\n            />\n            {/* A shade darker than the pocket, so the inside reads as\n                being in shadow. */}\n            <rect\n              x=\"0.5\"\n              y=\"0.5\"\n              width={ENV_W - 1}\n              height={ENV_H - 1}\n              rx=\"6\"\n              className=\"fill-foreground/[0.06]\"\n            />\n            <defs>\n              {/* Airmail border: red and blue bars at 45deg with paper\n                  between, the one print every letter recognises. */}\n              <pattern\n                id={`${svgId}-air`}\n                width=\"20\"\n                height=\"20\"\n                patternUnits=\"userSpaceOnUse\"\n                patternTransform=\"rotate(45)\"\n              >\n                <rect width=\"6\" height=\"20\" style={{ fill: AIR_RED }} />\n                <rect x=\"10\" width=\"6\" height=\"20\" style={{ fill: AIR_BLUE }} />\n              </pattern>\n            </defs>\n          </motion.svg>\n\n          {/* The flap hinges on the envelope's top edge. */}\n          <motion.svg\n            aria-hidden\n            viewBox={`0 0 ${ENV_W} ${FLAP_H}`}\n            className=\"pointer-events-none absolute overflow-visible\"\n            style={{\n              ...envelopeBox,\n              height: FLAP_H,\n              opacity: envOpacity,\n              y: envY,\n              rotateX: flap,\n              zIndex: flapZ,\n              transformPerspective: 600,\n              originY: 0,\n            }}\n          >\n            <clipPath id={`${svgId}-flap`}>\n              <path d={FLAP_PATH} />\n            </clipPath>\n            <path\n              d={FLAP_PATH}\n              strokeLinejoin=\"round\"\n              className=\"fill-background stroke-foreground/20\"\n            />\n            {/* The airmail band along the top edge prints on the flap, so\n                it stays unbroken once the flap is closed. */}\n            <rect\n              x=\"0\"\n              y=\"0\"\n              width={ENV_W}\n              height={ENV_H}\n              fill=\"none\"\n              stroke={`url(#${svgId}-air)`}\n              strokeWidth=\"10\"\n              clipPath={`url(#${svgId}-flap)`}\n              opacity=\"0.9\"\n            />\n          </motion.svg>\n\n          <motion.div\n            ref={paperRef}\n            className=\"absolute inset-0 z-[2] rounded-[12px] bg-background shadow-raised\"\n            style={{\n              scale: noteScale,\n              y: noteY,\n              originY: 0,\n              // Ruled writing lines under each 24px line of text: the first\n              // sits 38px down, just under the first line's descenders.\n              backgroundImage:\n                \"repeating-linear-gradient(to bottom, transparent 0 23px, color-mix(in oklab, var(--foreground) 9%, transparent) 23px 24px)\",\n              backgroundRepeat: \"no-repeat\",\n              backgroundPosition: \"16px 15px\",\n              backgroundSize: \"calc(100% - 32px) calc(100% - 30px)\",\n            }}\n          >\n            <Stamp className=\"pointer-events-none absolute top-3 right-3 rotate-[4deg]\" />\n            <label htmlFor={fieldId} className=\"sr-only\">\n              {title}\n            </label>\n            <textarea\n              id={fieldId}\n              ref={textRef}\n              value={message}\n              onChange={(e) => setMessage(e.target.value)}\n              onKeyDown={(e) => {\n                if (e.key === \"Enter\" && (e.metaKey || e.ctrlKey)) {\n                  e.preventDefault();\n                  send();\n                }\n              }}\n              placeholder={placeholder}\n              readOnly={phase !== \"compose\"}\n              className=\"block size-full resize-none rounded-[12px] bg-transparent py-4 pr-[68px] pl-4 text-[15px] leading-6 text-foreground outline-hidden placeholder:text-muted-foreground focus-visible:outline-2 focus-visible:outline-solid focus-visible:outline-offset-2 focus-visible:outline-foreground\"\n            />\n          </motion.div>\n\n          {/* Pocket front: covers everything but the V the note slides into. */}\n          <motion.svg\n            aria-hidden\n            viewBox={`0 0 ${ENV_W} ${ENV_H}`}\n            className=\"pointer-events-none absolute z-[3] overflow-visible\"\n            style={{\n              ...envelopeBox,\n              height: ENV_H,\n              opacity: envOpacity,\n              y: envY,\n            }}\n          >\n            <clipPath id={`${svgId}-pocket`}>\n              <path d={POCKET_PATH} />\n            </clipPath>\n            <path\n              d={POCKET_PATH}\n              strokeLinejoin=\"round\"\n              className=\"fill-background stroke-foreground/15\"\n            />\n            <rect\n              x=\"0\"\n              y=\"0\"\n              width={ENV_W}\n              height={ENV_H}\n              fill=\"none\"\n              stroke={`url(#${svgId}-air)`}\n              strokeWidth=\"10\"\n              clipPath={`url(#${svgId}-pocket)`}\n              opacity=\"0.9\"\n            />\n            {/* The side folds meeting under the V. */}\n            <path\n              d={`M1 ${ENV_H - 1} L${ENV_W / 2 - 14} ${POCKET_V + 18} M${ENV_W - 1} ${ENV_H - 1} L${ENV_W / 2 + 14} ${POCKET_V + 18}`}\n              className=\"fill-none stroke-foreground/10\"\n            />\n          </motion.svg>\n\n          <motion.svg\n            aria-hidden\n            viewBox={`0 0 ${SEAL_R * 2} ${SEAL_R * 2}`}\n            className=\"pointer-events-none absolute z-[5] overflow-visible\"\n            style={{\n              width: SEAL_R * 2,\n              height: SEAL_R * 2,\n              left: `calc(50% - ${SEAL_R}px)`,\n              // Centered on the flap tip, where the seal holds it shut.\n              top: ENV_TOP + FLAP_H - SEAL_R - 4,\n              scale: sealScale,\n              opacity: seal,\n            }}\n          >\n            <path\n              d={SEAL_PATH}\n              style={{ fill: WAX }}\n              className=\"drop-shadow-[0_1px_1px_rgb(0_0_0/0.25)]\"\n            />\n            {/* The pressed ring and emboss catch light on their upper edge. */}\n            <circle\n              cx={SEAL_R}\n              cy={SEAL_R}\n              r={SEAL_R - 6}\n              fill=\"none\"\n              strokeWidth=\"1.5\"\n              style={{ stroke: WAX_LIGHT }}\n              opacity=\"0.7\"\n            />\n            <path\n              d={`M${SEAL_R - 5} ${SEAL_R + 0.5} l3.5 3.5 l6.5 -7`}\n              fill=\"none\"\n              strokeWidth=\"2\"\n              strokeLinecap=\"round\"\n              strokeLinejoin=\"round\"\n              style={{ stroke: WAX_LIGHT }}\n            />\n          </motion.svg>\n        </motion.div>\n\n        <motion.div\n          className=\"mt-auto flex items-center justify-between gap-3\"\n          style={{ opacity: chrome }}\n        >\n          <span className=\"text-xs text-muted-foreground\">\n            <kbd className=\"font-sans\">{modifier}</kbd>\n            {\" + \"}\n            <kbd className=\"font-sans\">Enter</kbd> to send\n          </span>\n          <button\n            type=\"button\"\n            onClick={send}\n            disabled={empty}\n            className=\"flex h-9 touch-manipulation items-center gap-1.5 rounded-full bg-foreground pr-4 pl-3.5 text-sm font-medium text-background outline-hidden transition-[scale,opacity] duration-150 ease-out select-none focus-visible:outline-2 focus-visible:outline-solid focus-visible:outline-offset-2 focus-visible:outline-foreground active:scale-[0.96] disabled:opacity-40 disabled:active:scale-100\"\n          >\n            <svg\n              aria-hidden\n              viewBox=\"0 0 16 16\"\n              className=\"size-4\"\n              fill=\"none\"\n              stroke=\"currentColor\"\n              strokeWidth={1.75}\n              strokeLinecap=\"round\"\n              strokeLinejoin=\"round\"\n            >\n              <rect x=\"1.75\" y=\"3.5\" width=\"12.5\" height=\"9\" rx=\"1.5\" />\n              <path d=\"m2.25 4.25 5.75 4.25 5.75-4.25\" />\n            </svg>\n            Send\n          </button>\n        </motion.div>\n      </div>\n\n      {/* Enters only after the envelope has gone; leaves quickly so writing\n          another is never kept waiting. */}\n      <div\n        inert={phase !== \"sent\"}\n        className={cn(\n          \"absolute inset-0 flex flex-col items-center justify-center text-center\",\n          \"transition-[opacity,filter,translate] ease-[cubic-bezier(0.23,1,0.32,1)] motion-reduce:transition-[opacity]\",\n          phase === \"sent\"\n            ? \"translate-y-0 opacity-100 filter-none duration-300\"\n            : \"pointer-events-none translate-y-1.5 opacity-0 blur-[4px] duration-150 motion-reduce:translate-y-0 motion-reduce:filter-none\",\n        )}\n      >\n        <Postmark date={sentOn} />\n        <p className=\"mt-4 text-lg font-medium text-foreground\">\n          On its way\n        </p>\n        <p className=\"mt-1 text-sm text-muted-foreground\">\n          Thanks for the note. We read every one.\n        </p>\n        <button\n          ref={againRef}\n          type=\"button\"\n          onClick={writeAnother}\n          className=\"mt-5 h-9 touch-manipulation rounded-full bg-background px-4 text-sm font-medium text-foreground shadow-raised outline-hidden transition-[scale] duration-150 ease-out select-none focus-visible:outline-2 focus-visible:outline-solid focus-visible:outline-offset-2 focus-visible:outline-foreground active:scale-[0.96]\"\n        >\n          Write another\n        </button>\n      </div>\n\n      <span className=\"sr-only\" aria-live=\"polite\">\n        {phase === \"sent\" ? \"Feedback sent\" : \"\"}\n      </span>\n    </div>\n  );\n}\n\n// A postage stamp: perforated edge, a printed scene in postage ink and\n// its value. It rides into the envelope with the note.\nfunction Stamp({ className }: { className?: string }) {\n  const id = useId().replace(/[^a-zA-Z0-9_-]/g, \"\");\n  const W = 44;\n  const H = 52;\n  // Perforation holes every 6px around the edge, cut out with a mask.\n  const holes: [number, number][] = [];\n  for (let x = 3; x <= W - 3; x += 5.5) holes.push([x, 0], [x, H]);\n  for (let y = 3; y <= H - 3; y += 5.5) holes.push([0, y], [W, y]);\n  return (\n    <svg\n      aria-hidden\n      viewBox={`0 0 ${W} ${H}`}\n      width={W}\n      height={H}\n      className={cn(\"drop-shadow-[0_1px_1.5px_rgb(0_0_0/0.18)]\", className)}\n    >\n      <mask id={`${id}-perf`}>\n        <rect width={W} height={H} fill=\"white\" />\n        {holes.map(([x, y], i) => (\n          <circle key={i} cx={x} cy={y} r=\"1.9\" fill=\"black\" />\n        ))}\n      </mask>\n      <g mask={`url(#${id}-perf)`}>\n        <rect width={W} height={H} className=\"fill-background\" />\n        <rect\n          x=\"0\"\n          y=\"0\"\n          width={W}\n          height={H}\n          className=\"fill-foreground/[0.04]\"\n        />\n        {/* Printed area: sky, sun and two hills in postage ink. */}\n        <rect x=\"5\" y=\"5\" width={W - 10} height={H - 10} rx=\"1\" style={{ fill: AIR_BLUE }} opacity=\"0.9\" />\n        <circle cx=\"29\" cy=\"17\" r=\"4.5\" style={{ fill: \"light-dark(#f2c14e, #e8b73f)\" }} />\n        <path d={`M5 36 Q14 25 23 33 T39 29 V${H - 5} H5 Z`} style={{ fill: AIR_RED }} />\n        <path d={`M5 41 Q17 33 39 38 V${H - 5} H5 Z`} className=\"fill-background\" opacity=\"0.35\" />\n        <text\n          x=\"8.5\"\n          y=\"15\"\n          fontSize=\"8\"\n          fontWeight=\"600\"\n          className=\"fill-background font-sans\"\n        >\n          2c\n        </text>\n      </g>\n    </svg>\n  );\n}\n\n// Postmark stamped on the sent screen: a double ring with the day's date,\n// and the wavy cancellation lines that run off to its right.\nfunction Postmark({ date }: { date: string }) {\n  return (\n    <svg\n      aria-hidden\n      viewBox=\"0 0 150 76\"\n      className=\"h-[76px] w-[150px] -rotate-6 text-foreground\"\n      fill=\"none\"\n      stroke=\"currentColor\"\n    >\n      <circle cx=\"38\" cy=\"38\" r=\"35\" strokeWidth=\"1.75\" opacity=\"0.8\" />\n      <circle cx=\"38\" cy=\"38\" r=\"28\" strokeWidth=\"1\" opacity=\"0.5\" />\n      <text\n        x=\"38\"\n        y=\"35\"\n        textAnchor=\"middle\"\n        fontSize=\"12\"\n        fontWeight=\"600\"\n        letterSpacing=\"1.5\"\n        className=\"fill-current font-sans\"\n        stroke=\"none\"\n      >\n        SENT\n      </text>\n      <text\n        x=\"38\"\n        y=\"50\"\n        textAnchor=\"middle\"\n        fontSize=\"12\"\n        className=\"fill-current font-sans tabular-nums\"\n        stroke=\"none\"\n        opacity=\"0.7\"\n      >\n        {date}\n      </text>\n      {[22, 32, 42, 52].map((y) => (\n        <path\n          key={y}\n          d={`M78 ${y} q8 -5 16 0 t16 0 t16 0 t16 0`}\n          strokeWidth=\"1.5\"\n          strokeLinecap=\"round\"\n          opacity=\"0.55\"\n        />\n      ))}\n    </svg>\n  );\n}\n\nexport default function EnvelopeSendDemo() {\n  return <EnvelopeSend />;\n}\n"},{"path":"hooks/use-reduced-motion.ts","type":"registry:hook","target":"hooks/use-reduced-motion.ts","content":"\"use client\";\n\nimport { useSyncExternalStore } from \"react\";\n\n// Motion's useReducedMotion reads the media query on the very first client\n// render, but the server can't know it, so every component that renders\n// differently under reduced motion broke hydration for those users. This\n// reports false during hydration, matching the server HTML, then the real\n// preference straight after, and follows it if it changes.\nconst query = \"(prefers-reduced-motion: reduce)\";\n\nfunction subscribe(onChange: () => void) {\n  const media = window.matchMedia(query);\n  media.addEventListener(\"change\", onChange);\n  return () => media.removeEventListener(\"change\", onChange);\n}\n\nexport function useReducedMotion() {\n  return useSyncExternalStore(\n    subscribe,\n    () => window.matchMedia(query).matches,\n    () => false,\n  );\n}\n"}],"cssVars":{"light":{"shadow-raised":"0 0 0 1px oklch(0 0 0 / 0.06), 0 1px 2px oklch(0 0 0 / 0.06), 0 6px 16px -6px oklch(0 0 0 / 0.12)"},"dark":{"shadow-raised":"0 0 0 1px oklch(1 0 0 / 0.08), 0 1px 2px oklch(0 0 0 / 0.4), 0 6px 16px -6px oklch(0 0 0 / 0.6)"},"theme":{"shadow-raised":"var(--shadow-raised)"}},"categories":["objects"],"docs":"From ui lab: https://lab.xevrion.dev/lab/envelope-send"}