{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "tegami",
  "title": "Tegami — Releasing, as simple as writing a note",
  "description": "remocn demo composition \"Tegami — Releasing, as simple as writing a note\" — installs the full Remotion composition. Generated with AI from the prompt in demos/tegami/prompt.md.",
  "dependencies": [
    "@remotion/google-fonts",
    "@remotion/transitions",
    "culori",
    "lucide-react",
    "remotion"
  ],
  "registryDependencies": [
    "https://remocn.dev/r/blur-in.json",
    "https://remocn.dev/r/remocn-ui.json",
    "https://remocn.dev/r/strikethrough-replace.json"
  ],
  "files": [
    {
      "path": "src/demos/tegami/index.tsx",
      "content": "import React, { type CSSProperties, type ReactNode } from \"react\";\nimport { AbsoluteFill, Easing, Img, interpolate, useCurrentFrame, useVideoConfig } from \"remotion\";\nimport { demoAsset } from \"@/lib/demo-assets\";\nimport {\n  TransitionSeries,\n  linearTiming,\n  type TransitionPresentation,\n  type TransitionPresentationComponentProps,\n} from \"@remotion/transitions\";\nimport { loadFont as loadSans } from \"@remotion/google-fonts/Manrope\";\nimport { loadFont as loadMono } from \"@remotion/google-fonts/GeistMono\";\nimport { loadFont as loadHand } from \"@remotion/google-fonts/Caveat\";\nimport { CheckIcon, CopyIcon } from \"lucide-react\";\n\nimport { RemocnUIProvider } from \"@/lib/remocn-ui\";\nimport { BlurIn } from \"@/components/remocn/blur-in\";\nimport { useBlurInTransition } from \"@/components/remocn/use-blur-in-transition\";\nimport { StrikethroughReplace } from \"@/components/remocn/strikethrough-replace\";\n\n// ---------------------------------------------------------------------------\n// Fonts — the Tegami brand: a bold geometric sans (Manrope) for the wordmark\n// and headlines, a handwritten script (Caveat) for the margin notes / \"record\n// your changes!\" annotations, and GeistMono for code. Bound to the CSS vars\n// the remocn text components read.\n// ---------------------------------------------------------------------------\nconst { fontFamily: SANS_FAMILY } = loadSans(\"normal\", {\n  subsets: [\"latin\"],\n  weights: [\"400\", \"500\", \"600\", \"700\", \"800\"],\n});\nconst { fontFamily: MONO_FAMILY } = loadMono(\"normal\", {\n  subsets: [\"latin\"],\n  weights: [\"400\", \"500\", \"700\"],\n});\nconst { fontFamily: HAND_FAMILY } = loadHand(\"normal\", {\n  subsets: [\"latin\"],\n  weights: [\"400\", \"500\", \"600\", \"700\"],\n});\n\nconst SANS = \"var(--font-geist-sans), -apple-system, sans-serif\";\nconst MONO = \"var(--font-geist-mono), ui-monospace, monospace\";\nconst HAND = \"var(--font-hand), cursive\";\n\n// Monochrome ink-on-paper palette. Pure black ink on warm white paper — the\n// brand uses no color accent at all, and neither do we.\nconst PAPER = \"#FFFFFF\";\nconst INK = \"#161616\";\nconst FAINT = \"rgba(22,22,22,0.52)\";\nconst HAIR = \"rgba(22,22,22,0.16)\";\nconst DOT = \"#EEEEEE\";\n\nconst BANNER = demoAsset(\"tegami-banner.png\");\nconst LOGO = demoAsset(\"tegami-logo.png\");\n\n// ---------------------------------------------------------------------------\n// Scene timings (frames @ 30fps). Transitions overlap and are subtracted.\n// ---------------------------------------------------------------------------\nconst S_AVALANCHE = 150; // hook — release chores pile up\nconst S_BREAKS = 120; //    the old fixes fail\nconst S_MEET = 110; //      Meet Tegami (banner reveal)\nconst S_SCRIPT = 150; //    a script you own (paper code)\nconst S_NOTE = 165; //      write the change like a note\nconst S_VERSION = 150; //   version & lock\nconst S_SHIP = 175; //      merge -> ship\nconst S_REGISTRY = 130; //  not just npm (constellation)\nconst S_STACK = 150; //     retry-safe / plugins / programmable\nconst S_AGENT = 120; //     agent writes the changelog\nconst S_MIGRATE = 110; //   coming from Changesets\nconst S_CTA = 130; //       record your changes\n\nconst T_X = 14; //   crossfade (same visual world)\nconst T_Z1 = 18; //  problem -> solution (zoom-through)\nconst T_Z2 = 16; //  intro -> demo\nconst T_P = 14; //   push-slide between demo steps\nconst T_Z3 = 16; //  demo -> benefits\nconst T_Z4 = 18; //  -> CTA\n\nexport const TEGAMI_DURATION =\n  S_AVALANCHE +\n  S_BREAKS +\n  S_MEET +\n  S_SCRIPT +\n  S_NOTE +\n  S_VERSION +\n  S_SHIP +\n  S_REGISTRY +\n  S_STACK +\n  S_AGENT +\n  S_MIGRATE +\n  S_CTA -\n  (T_X + T_Z1 + T_Z2 + T_X + T_P + T_P + T_Z3 + T_X + T_X + T_X + T_Z4);\n\n// ===========================================================================\n// Shared primitives\n// ===========================================================================\n\n// Blur-in reveal, driven by the remocn-ui timeline hook.\nconst Reveal: React.FC<{\n  children: ReactNode;\n  delay?: number;\n  distance?: number;\n  blur?: number;\n  duration?: number;\n  direction?: \"up\" | \"down\" | \"left\" | \"right\";\n  display?: CSSProperties[\"display\"];\n}> = ({\n  children,\n  delay = 0,\n  distance = 16,\n  blur = 10,\n  duration = 20,\n  direction = \"up\",\n  display = \"block\",\n}) => {\n  const style = useBlurInTransition(\n    [{ at: delay, state: \"revealed\", duration }],\n    { direction, distance, blur },\n  );\n  return (\n    <BlurIn style={style} display={display}>\n      {children}\n    </BlurIn>\n  );\n};\n\n// Handwritten annotation (Caveat) — the brand's margin-note voice.\nconst Hand: React.FC<{\n  children: ReactNode;\n  size?: number;\n  color?: string;\n  style?: CSSProperties;\n}> = ({ children, size = 34, color = INK, style }) => (\n  <span\n    style={{\n      fontFamily: HAND,\n      fontSize: size,\n      fontWeight: 600,\n      color,\n      lineHeight: 1.1,\n      display: \"inline-block\",\n      ...style,\n    }}\n  >\n    {children}\n  </span>\n);\n\n// Inline, normal-flow typewriter (mono, left-aligned) — deterministic char\n// reveal with a blinking block caret. Replaces the scene-centered Typewriter\n// component so it can sit inside a card / prompt line.\nconst MonoType: React.FC<{\n  text: string;\n  delay?: number;\n  cps?: number;\n  fontSize?: number;\n  color?: string;\n  weight?: number;\n}> = ({ text, delay = 0, cps = 16, fontSize = 20, color = INK, weight = 500 }) => {\n  const frame = useCurrentFrame();\n  const { fps } = useVideoConfig();\n  const chars = Math.floor(\n    interpolate(frame, [delay, delay + (text.length / cps) * fps], [0, text.length], {\n      extrapolateLeft: \"clamp\",\n      extrapolateRight: \"clamp\",\n    }),\n  );\n  const shown = text.slice(0, Math.max(0, chars));\n  const done = chars >= text.length;\n  const blinkOn = Math.floor((frame / fps) * 2) % 2 === 0;\n  const caretVisible = frame >= delay && (!done || blinkOn);\n  return (\n    <span\n      style={{\n        fontFamily: MONO,\n        fontSize,\n        fontWeight: weight,\n        color,\n        whiteSpace: \"pre\",\n        display: \"inline-flex\",\n        alignItems: \"center\",\n      }}\n    >\n      {shown}\n      <span\n        style={{\n          display: \"inline-block\",\n          width: fontSize * 0.5,\n          height: fontSize,\n          background: color,\n          marginLeft: 2,\n          opacity: caretVisible ? 1 : 0,\n          transform: \"translateY(1px)\",\n        }}\n      />\n    </span>\n  );\n};\n\nconst Heading: React.FC<{\n  children: ReactNode;\n  size?: number;\n  weight?: number;\n  style?: CSSProperties;\n}> = ({ children, size = 44, weight = 700, style }) => (\n  <h2\n    style={{\n      margin: 0,\n      fontFamily: SANS,\n      fontWeight: weight,\n      fontSize: size,\n      letterSpacing: \"-0.02em\",\n      color: INK,\n      textAlign: \"center\",\n      lineHeight: 1.12,\n      ...style,\n    }}\n  >\n    {children}\n  </h2>\n);\n\n// Stroke-draw hook — pairs with pathLength={1} strokeDasharray={1}.\nconst useDrawOffset = (delay: number, dur: number) => {\n  const frame = useCurrentFrame();\n  return interpolate(frame, [delay, delay + dur], [1, 0], {\n    extrapolateLeft: \"clamp\",\n    extrapolateRight: \"clamp\",\n    easing: Easing.inOut(Easing.cubic),\n  });\n};\n\n// A single inked path that draws itself on.\nconst Stroke: React.FC<{\n  d: string;\n  delay?: number;\n  dur?: number;\n  width?: number;\n  color?: string;\n}> = ({ d, delay = 0, dur = 24, width = 2.5, color = INK }) => {\n  const off = useDrawOffset(delay, dur);\n  return (\n    <path\n      d={d}\n      pathLength={1}\n      strokeDasharray={1}\n      strokeDashoffset={off}\n      fill=\"none\"\n      stroke={color}\n      strokeWidth={width}\n      strokeLinecap=\"round\"\n      strokeLinejoin=\"round\"\n    />\n  );\n};\n\n// A marker swipe that draws left-to-right — the brand's brush stroke.\nconst BrushUnderline: React.FC<{\n  width?: number;\n  delay?: number;\n  dur?: number;\n  thickness?: number;\n}> = ({ width = 320, delay = 6, dur = 18, thickness = 9 }) => {\n  const off = useDrawOffset(delay, dur);\n  // A slightly wavy stroke so it reads as a hand swipe, not a ruler line.\n  const d = `M6 ${thickness + 6} C ${width * 0.28} ${thickness - 2}, ${\n    width * 0.6\n  } ${thickness + 11}, ${width - 6} ${thickness + 2}`;\n  return (\n    <svg width={width} height={thickness + 18} style={{ display: \"block\" }}>\n      <path\n        d={d}\n        pathLength={1}\n        strokeDasharray={1}\n        strokeDashoffset={off}\n        fill=\"none\"\n        stroke={INK}\n        strokeWidth={thickness}\n        strokeLinecap=\"round\"\n      />\n    </svg>\n  );\n};\n\n// Empty sketch checkbox that ticks at `checkAt`.\nconst SketchCheck: React.FC<{\n  size?: number;\n  checkAt?: number | null;\n}> = ({ size = 26, checkAt = null }) => {\n  const boxOff = useDrawOffset(0, 12);\n  const tickOff = useDrawOffset(checkAt ?? 1e9, 9);\n  return (\n    <svg width={size} height={size} viewBox=\"0 0 26 26\">\n      <path\n        d=\"M4 5 Q3 4 5 4 L21 3.5 Q23 4 22.5 6 L22 21 Q22 23 20 22.5 L5 23 Q3 22.5 3.5 20 Z\"\n        pathLength={1}\n        strokeDasharray={1}\n        strokeDashoffset={boxOff}\n        fill=\"none\"\n        stroke={INK}\n        strokeWidth={2}\n        strokeLinecap=\"round\"\n        strokeLinejoin=\"round\"\n      />\n      {checkAt !== null && (\n        <path\n          d=\"M7 13 L11.5 18 L20 7\"\n          pathLength={1}\n          strokeDasharray={1}\n          strokeDashoffset={tickOff}\n          fill=\"none\"\n          stroke={INK}\n          strokeWidth={2.6}\n          strokeLinecap=\"round\"\n          strokeLinejoin=\"round\"\n        />\n      )}\n    </svg>\n  );\n};\n\n// A paper card with a hand-inked border and a small tilt.\nconst PaperCard: React.FC<{\n  children: ReactNode;\n  width?: number | string;\n  tilt?: number;\n  padding?: number | string;\n  style?: CSSProperties;\n}> = ({ children, width = 360, tilt = 0, padding = \"26px 28px\", style }) => (\n  <div\n    style={{\n      width,\n      padding,\n      background: PAPER,\n      border: `2px solid ${INK}`,\n      borderRadius: 16,\n      transform: `rotate(${tilt}deg)`,\n      boxShadow: `5px 6px 0 ${INK}`,\n      ...style,\n    }}\n  >\n    {children}\n  </div>\n);\n\n// A light, ink-on-paper code card with line-by-line reveal.\nconst PaperCode: React.FC<{\n  title: string;\n  code: string;\n  width?: number;\n  fontSize?: number;\n  startDelay?: number;\n  stagger?: number;\n  tilt?: number;\n}> = ({\n  title,\n  code,\n  width = 560,\n  fontSize = 18,\n  startDelay = 0,\n  stagger = 6,\n  tilt = -1,\n}) => {\n  const lines = code.split(\"\\n\");\n  return (\n    <div\n      style={{\n        width,\n        background: PAPER,\n        border: `2px solid ${INK}`,\n        borderRadius: 14,\n        overflow: \"hidden\",\n        transform: `rotate(${tilt}deg)`,\n        boxShadow: `6px 7px 0 ${INK}`,\n        fontFamily: MONO,\n      }}\n    >\n      <div\n        style={{\n          height: 38,\n          display: \"flex\",\n          alignItems: \"center\",\n          gap: 8,\n          padding: \"0 16px\",\n          borderBottom: `2px solid ${INK}`,\n        }}\n      >\n        <Dot /> <Dot /> <Dot />\n        <span\n          style={{\n            flex: 1,\n            textAlign: \"center\",\n            fontFamily: MONO,\n            fontSize: 14,\n            color: FAINT,\n          }}\n        >\n          {title}\n        </span>\n      </div>\n      <div\n        style={{\n          padding: \"20px 24px\",\n          display: \"flex\",\n          flexDirection: \"column\",\n          gap: 4,\n          fontSize,\n          lineHeight: 1.6,\n        }}\n      >\n        {lines.map((line, i) => (\n          <PaperCodeLine\n            key={i}\n            line={line}\n            delay={startDelay + i * stagger}\n            fontSize={fontSize}\n          />\n        ))}\n      </div>\n    </div>\n  );\n};\n\nconst Dot: React.FC = () => (\n  <span\n    style={{\n      width: 10,\n      height: 10,\n      borderRadius: \"50%\",\n      border: `2px solid ${INK}`,\n      display: \"inline-block\",\n    }}\n  />\n);\n\nconst PaperCodeLine: React.FC<{\n  line: string;\n  delay: number;\n  fontSize: number;\n}> = ({ line, delay, fontSize }) => {\n  const frame = useCurrentFrame();\n  const opacity = interpolate(frame, [delay, delay + 8], [0, 1], {\n    extrapolateLeft: \"clamp\",\n    extrapolateRight: \"clamp\",\n  });\n  const ty = interpolate(frame, [delay, delay + 8], [4, 0], {\n    extrapolateLeft: \"clamp\",\n    extrapolateRight: \"clamp\",\n  });\n  const isComment = line.trimStart().startsWith(\"//\");\n  if (line.trim() === \"\") return <div style={{ height: fontSize * 0.7 }} />;\n  return (\n    <div\n      style={{\n        opacity,\n        transform: `translateY(${ty}px)`,\n        whiteSpace: \"pre\",\n        color: isComment ? FAINT : INK,\n      }}\n    >\n      {line}\n    </div>\n  );\n};\n\n// ===========================================================================\n// Scene 1 — Release-day avalanche. Chore cards pile in from every edge.\n// ===========================================================================\nconst CHORES: { label: string; x: number; y: number; tilt: number; d: number }[] =\n  [\n    { label: \"Bump the versions\", x: 150, y: 120, tilt: -6, d: 6 },\n    { label: \"Fix dependency ranges\", x: 760, y: 96, tilt: 5, d: 22 },\n    { label: \"Write the changelog\", x: 96, y: 420, tilt: 4, d: 38 },\n    { label: \"Cut the release\", x: 800, y: 430, tilt: -5, d: 54 },\n    { label: \"Post the announcement\", x: 430, y: 540, tilt: 2, d: 70 },\n  ];\n\nconst ChoreCard: React.FC<{\n  label: string;\n  x: number;\n  y: number;\n  tilt: number;\n  delay: number;\n}> = ({ label, x, y, tilt, delay }) => (\n  <div style={{ position: \"absolute\", left: x, top: y }}>\n    <Reveal delay={delay} distance={26} blur={11} duration={16}>\n      <div\n        style={{\n          transform: `rotate(${tilt}deg)`,\n          display: \"flex\",\n          alignItems: \"center\",\n          gap: 14,\n          padding: \"16px 22px\",\n          background: PAPER,\n          border: `2px solid ${INK}`,\n          borderRadius: 13,\n          boxShadow: `4px 5px 0 ${INK}`,\n        }}\n      >\n        <SketchCheck size={24} />\n        <span\n          style={{ fontFamily: SANS, fontWeight: 600, fontSize: 24, color: INK }}\n        >\n          {label}\n        </span>\n      </div>\n    </Reveal>\n  </div>\n);\n\nconst AvalancheScene: React.FC = () => (\n  <AbsoluteFill>\n    {CHORES.map((c) => (\n      <ChoreCard key={c.label} {...c} delay={c.d} />\n    ))}\n    <AbsoluteFill style={{ alignItems: \"center\", justifyContent: \"center\" }}>\n      <div style={{ textAlign: \"center\", marginTop: 14 }}>\n        <Reveal delay={88} distance={14} blur={9}>\n          <Hand size={68} style={{ fontWeight: 700 }}>\n            every single time you ship\n          </Hand>\n        </Reveal>\n        <div style={{ display: \"flex\", justifyContent: \"center\", marginTop: 2 }}>\n          <BrushUnderline width={420} delay={100} dur={16} thickness={8} />\n        </div>\n      </div>\n    </AbsoluteFill>\n  </AbsoluteFill>\n);\n\n// ===========================================================================\n// Scene 2 — The old fixes break.\n// ===========================================================================\nconst BreaksScene: React.FC = () => {\n  // Decorations are anchored to the emphasized word (an inline-block span) and\n  // scale to its width via preserveAspectRatio=\"none\"; non-scaling-stroke keeps\n  // the ink weight constant, so the crack/box always line up with the text.\n  const crackOff = useDrawOffset(26, 16);\n  const boxOff = useDrawOffset(64, 22);\n  return (\n    <AbsoluteFill\n      style={{\n        alignItems: \"center\",\n        justifyContent: \"center\",\n        flexDirection: \"column\",\n        gap: 56,\n      }}\n    >\n      <Reveal delay={4} distance={18} blur={10}>\n        <Heading size={46} weight={600}>\n          So you write a release script —{\" \"}\n          <span\n            style={{\n              position: \"relative\",\n              display: \"inline-block\",\n              fontWeight: 800,\n            }}\n          >\n            it breaks.\n            <svg\n              viewBox=\"0 0 200 14\"\n              preserveAspectRatio=\"none\"\n              width=\"100%\"\n              height={13}\n              style={{\n                position: \"absolute\",\n                left: 0,\n                bottom: -17,\n                display: \"block\",\n                overflow: \"visible\",\n              }}\n            >\n              <path\n                d=\"M4 7 L42 12 L80 3 L118 12 L156 3 L196 8\"\n                pathLength={1}\n                strokeDasharray={1}\n                strokeDashoffset={crackOff}\n                fill=\"none\"\n                stroke={INK}\n                strokeWidth={3}\n                strokeLinecap=\"round\"\n                strokeLinejoin=\"round\"\n                vectorEffect=\"non-scaling-stroke\"\n              />\n            </svg>\n          </span>\n        </Heading>\n      </Reveal>\n      <Reveal delay={44} distance={18} blur={10}>\n        <Heading size={46} weight={600}>\n          Or you reach for Changesets —{\" \"}\n          <span\n            style={{\n              position: \"relative\",\n              display: \"inline-block\",\n              fontWeight: 800,\n            }}\n          >\n            and hit its walls.\n            <svg\n              viewBox=\"0 0 100 100\"\n              preserveAspectRatio=\"none\"\n              style={{\n                position: \"absolute\",\n                left: -16,\n                top: -11,\n                width: \"calc(100% + 32px)\",\n                height: \"calc(100% + 22px)\",\n                display: \"block\",\n                overflow: \"visible\",\n              }}\n            >\n              <path\n                d=\"M4 8 L96 5 L97 93 L3 95 Z\"\n                pathLength={1}\n                strokeDasharray={1}\n                strokeDashoffset={boxOff}\n                fill=\"none\"\n                stroke={INK}\n                strokeWidth={2.6}\n                strokeLinecap=\"round\"\n                strokeLinejoin=\"round\"\n                vectorEffect=\"non-scaling-stroke\"\n              />\n            </svg>\n          </span>\n        </Heading>\n      </Reveal>\n    </AbsoluteFill>\n  );\n};\n\n// ===========================================================================\n// Scene 3 — Meet Tegami. The official brand lockup reveals on paper.\n// ===========================================================================\nconst MeetScene: React.FC = () => {\n  const frame = useCurrentFrame();\n  const scale = interpolate(frame, [0, 26], [0.94, 1], {\n    extrapolateLeft: \"clamp\",\n    extrapolateRight: \"clamp\",\n    easing: Easing.bezier(0.22, 1, 0.36, 1),\n  });\n  const opacity = interpolate(frame, [0, 18], [0, 1], {\n    extrapolateLeft: \"clamp\",\n    extrapolateRight: \"clamp\",\n  });\n  const blur = interpolate(frame, [0, 20], [14, 0], {\n    extrapolateLeft: \"clamp\",\n    extrapolateRight: \"clamp\",\n  });\n  return (\n    <AbsoluteFill style={{ alignItems: \"center\", justifyContent: \"center\" }}>\n      <div\n        style={{\n          display: \"flex\",\n          flexDirection: \"column\",\n          alignItems: \"center\",\n          gap: 22,\n        }}\n      >\n        <div\n          style={{\n            opacity,\n            filter: blur > 0.2 ? `blur(${blur}px)` : undefined,\n            transform: `scale(${scale})`,\n          }}\n        >\n          <Img src={BANNER} style={{ width: 800, height: \"auto\", display: \"block\" }} />\n        </div>\n        <Reveal delay={40} distance={12} blur={8}>\n          <Heading size={34} weight={600}>\n            Releasing, as simple as writing a note.\n          </Heading>\n        </Reveal>\n      </div>\n    </AbsoluteFill>\n  );\n};\n\n// ===========================================================================\n// Scene 4 — A script you own.\n// ===========================================================================\nconst SCRIPT_CODE = `import { tegami } from \"tegami\";\nimport { runCli } from \"tegami/cli\";\nimport { github } from \"tegami/plugins/github\";\n\nconst paper = tegami({\n  plugins: [github({ repo: \"acme/ui\" })],\n});\n\nawait runCli(paper);`;\n\nconst ScriptScene: React.FC = () => (\n  <AbsoluteFill\n    style={{\n      alignItems: \"center\",\n      justifyContent: \"center\",\n      flexDirection: \"column\",\n      gap: 30,\n    }}\n  >\n    <Reveal delay={2} distance={16} blur={10}>\n      <Heading size={42}>\n        Not a CLI you fight.{\" \"}\n        <span style={{ color: FAINT }}>A script you own.</span>\n      </Heading>\n    </Reveal>\n    <div style={{ position: \"relative\" }}>\n      <Reveal delay={12} distance={34} blur={14} duration={22}>\n        <PaperCode\n          title=\"scripts/tegami.mts\"\n          code={SCRIPT_CODE}\n          width={620}\n          fontSize={18}\n          startDelay={20}\n          stagger={6}\n          tilt={-1}\n        />\n      </Reveal>\n      <div\n        style={{\n          position: \"absolute\",\n          right: -240,\n          top: 118,\n          width: 220,\n          textAlign: \"left\",\n        }}\n      >\n        <Reveal delay={86} distance={10} blur={6} direction=\"left\">\n          <Hand size={34} color={INK} style={{ display: \"block\" }}>\n            yours — read it,\n            <br />\n            change it.\n          </Hand>\n        </Reveal>\n        {/* arrow curves left from the note and points back into the card */}\n        <svg\n          width={110}\n          height={70}\n          viewBox=\"0 0 110 70\"\n          style={{ position: \"absolute\", left: -84, top: 34, overflow: \"visible\" }}\n        >\n          <Stroke\n            d=\"M104 50 C 64 56, 30 46, 8 22 M8 22 L31 23 M8 22 L19 41\"\n            delay={98}\n            dur={15}\n            width={2.6}\n          />\n        </svg>\n      </div>\n    </div>\n  </AbsoluteFill>\n);\n\n// ===========================================================================\n// Scene 5 — Write the change like a note.\n// ===========================================================================\nconst NoteScene: React.FC = () => (\n  <AbsoluteFill\n    style={{\n      alignItems: \"center\",\n      justifyContent: \"center\",\n      flexDirection: \"column\",\n      gap: 26,\n    }}\n  >\n    <Reveal delay={2} distance={16} blur={10}>\n      <Heading size={42}>Describe the change like a note.</Heading>\n    </Reveal>\n\n    <div\n      style={{\n        display: \"flex\",\n        alignItems: \"center\",\n        gap: 46,\n        position: \"relative\",\n      }}\n    >\n      <Reveal delay={14} distance={20} blur={10}>\n        <div style={{ display: \"flex\", flexDirection: \"column\", gap: 12 }}>\n          <div\n            style={{\n              fontFamily: MONO,\n              fontSize: 20,\n              color: INK,\n              display: \"flex\",\n              alignItems: \"center\",\n              gap: 10,\n            }}\n          >\n            <span style={{ color: FAINT }}>$</span>\n            <MonoType text=\"tegami\" delay={8} cps={9} fontSize={20} />\n          </div>\n          <Hand size={30} color={FAINT}>\n            ...drops into <span style={{ color: INK }}>.tegami/</span>\n          </Hand>\n        </div>\n      </Reveal>\n\n      <Reveal delay={40} distance={30} blur={12} duration={20}>\n        <PaperCard width={460} tilt={1.5} padding=\"24px 30px\">\n          <div\n            style={{\n              fontFamily: MONO,\n              fontSize: 17,\n              color: FAINT,\n              lineHeight: 1.7,\n            }}\n          >\n            <div>---</div>\n            <div>\n              packages:\n            </div>\n            <div style={{ color: INK }}>\n              {\"  \"}&quot;@acme/ui&quot;: minor\n            </div>\n            <div>---</div>\n          </div>\n          <div\n            style={{\n              marginTop: 14,\n              fontFamily: SANS,\n              fontWeight: 700,\n              fontSize: 26,\n              color: INK,\n            }}\n          >\n            ## Fix button hover state\n          </div>\n          <div\n            style={{\n              marginTop: 6,\n              fontFamily: SANS,\n              fontSize: 18,\n              color: FAINT,\n            }}\n          >\n            The hover color now matches the design system.\n          </div>\n        </PaperCard>\n      </Reveal>\n\n      <div style={{ position: \"absolute\", right: -256, bottom: -56 }}>\n        <Reveal delay={96} distance={10} blur={6}>\n          <Hand size={36} style={{ transform: \"rotate(-7deg)\" }}>\n            record your changes!\n          </Hand>\n        </Reveal>\n      </div>\n    </div>\n  </AbsoluteFill>\n);\n\n// ===========================================================================\n// Scene 6 — Version & lock.\n// ===========================================================================\nconst BumpRow: React.FC<{\n  pkg: string;\n  from: string;\n  to: string;\n  delay: number;\n}> = ({ pkg, from, to, delay }) => {\n  const arrowOff = useDrawOffset(delay + 8, 10);\n  return (\n    <Reveal delay={delay} distance={16} blur={9}>\n      <div\n        style={{\n          display: \"flex\",\n          alignItems: \"center\",\n          gap: 16,\n          fontFamily: MONO,\n          fontSize: 22,\n        }}\n      >\n        <span style={{ color: INK, width: 168 }}>{pkg}</span>\n        <span style={{ color: FAINT }}>{from}</span>\n        <svg width={40} height={20}>\n          <path\n            d=\"M4 10 L30 10 M30 10 L23 5 M30 10 L23 15\"\n            pathLength={1}\n            strokeDasharray={1}\n            strokeDashoffset={arrowOff}\n            fill=\"none\"\n            stroke={INK}\n            strokeWidth={2.4}\n            strokeLinecap=\"round\"\n            strokeLinejoin=\"round\"\n          />\n        </svg>\n        <span style={{ color: INK, fontWeight: 700 }}>{to}</span>\n      </div>\n    </Reveal>\n  );\n};\n\nconst VersionScene: React.FC = () => (\n  <AbsoluteFill\n    style={{\n      alignItems: \"center\",\n      justifyContent: \"center\",\n      flexDirection: \"column\",\n      gap: 30,\n    }}\n  >\n    <Reveal delay={2} distance={16} blur={10}>\n      <Heading size={42}>\n        Compute every bump.{\" \"}\n        <span style={{ fontWeight: 800 }}>Lock the release.</span>\n      </Heading>\n    </Reveal>\n\n    <div\n      style={{ display: \"flex\", flexDirection: \"column\", gap: 14, marginTop: 6 }}\n    >\n      <BumpRow pkg=\"@acme/ui\" from=\"1.4.0\" to=\"1.5.0\" delay={20} />\n      <BumpRow pkg=\"@acme/utils\" from=\"2.1.3\" to=\"2.1.4\" delay={34} />\n      <BumpRow pkg=\"@acme/core\" from=\"0.9.0\" to=\"0.9.1\" delay={48} />\n    </div>\n\n    <Reveal delay={78} distance={26} blur={12}>\n      <div\n        style={{\n          marginTop: 8,\n          display: \"flex\",\n          alignItems: \"center\",\n          gap: 14,\n          padding: \"14px 22px\",\n          background: PAPER,\n          border: `2px solid ${INK}`,\n          borderRadius: 12,\n          boxShadow: `4px 5px 0 ${INK}`,\n          fontFamily: MONO,\n          fontSize: 20,\n          color: INK,\n        }}\n      >\n        <svg width={26} height={26} viewBox=\"0 0 26 26\">\n          <Stroke\n            d=\"M7 12 L7 8 Q7 3 13 3 Q19 3 19 8 L19 12\"\n            delay={86}\n            dur={10}\n            width={2.4}\n          />\n          <Stroke\n            d=\"M5 12 L21 12 L21 23 L5 23 Z\"\n            delay={90}\n            dur={10}\n            width={2.4}\n          />\n        </svg>\n        .tegami/publish-lock.yaml\n      </div>\n    </Reveal>\n  </AbsoluteFill>\n);\n\n// ===========================================================================\n// Scene 7 — Merge, and it ships.\n// ===========================================================================\nconst ShipScene: React.FC = () => {\n  const mergeTickOff = useDrawOffset(70, 12);\n  return (\n    <AbsoluteFill\n      style={{\n        alignItems: \"center\",\n        justifyContent: \"center\",\n        flexDirection: \"column\",\n        gap: 26,\n      }}\n    >\n      <Reveal delay={2} distance={16} blur={10}>\n        <Heading size={42}>Merge once — it ships itself.</Heading>\n      </Reveal>\n\n      <Reveal delay={14} distance={28} blur={12} duration={20}>\n        <PaperCard width={560} tilt={-1} padding=\"22px 26px\">\n          <div\n            style={{\n              display: \"flex\",\n              alignItems: \"center\",\n              gap: 12,\n              fontFamily: SANS,\n            }}\n          >\n            <SketchCheck size={26} checkAt={62} />\n            <span style={{ fontWeight: 700, fontSize: 24, color: INK }}>\n              Version Packages\n            </span>\n            <span style={{ fontFamily: MONO, fontSize: 18, color: FAINT }}>\n              #42\n            </span>\n            <span style={{ flex: 1 }} />\n            <span\n              style={{\n                display: \"inline-flex\",\n                alignItems: \"center\",\n                gap: 8,\n                fontFamily: SANS,\n                fontWeight: 700,\n                fontSize: 17,\n                color: PAPER,\n                background: INK,\n                padding: \"6px 16px\",\n                borderRadius: 8,\n              }}\n            >\n              Merge\n              <svg width={18} height={18} viewBox=\"0 0 24 24\">\n                <path\n                  d=\"M4 13 L10 19 L21 5\"\n                  pathLength={1}\n                  strokeDasharray={1}\n                  strokeDashoffset={mergeTickOff}\n                  fill=\"none\"\n                  stroke={PAPER}\n                  strokeWidth={3}\n                  strokeLinecap=\"round\"\n                  strokeLinejoin=\"round\"\n                />\n              </svg>\n            </span>\n          </div>\n        </PaperCard>\n      </Reveal>\n\n      <div style={{ display: \"flex\", gap: 16, marginTop: 4 }}>\n        <Reveal delay={96} distance={18} blur={9}>\n          <ShipTag label=\"3 packages published\" />\n        </Reveal>\n        <Reveal delay={110} distance={18} blur={9}>\n          <ShipTag label=\"GitHub releases created\" />\n        </Reveal>\n      </div>\n    </AbsoluteFill>\n  );\n};\n\nconst ShipTag: React.FC<{ label: string }> = ({ label }) => (\n  <div\n    style={{\n      display: \"flex\",\n      alignItems: \"center\",\n      gap: 10,\n      padding: \"10px 18px\",\n      border: `2px solid ${INK}`,\n      borderRadius: 999,\n      background: PAPER,\n      fontFamily: SANS,\n      fontWeight: 600,\n      fontSize: 19,\n      color: INK,\n    }}\n  >\n    <svg width={20} height={20} viewBox=\"0 0 24 24\">\n      <path\n        d=\"M4 13 L10 19 L21 5\"\n        fill=\"none\"\n        stroke={INK}\n        strokeWidth={3}\n        strokeLinecap=\"round\"\n        strokeLinejoin=\"round\"\n      />\n    </svg>\n    {label}\n  </div>\n);\n\n// ===========================================================================\n// Scene 8 — Not just npm (constellation).\n// ===========================================================================\nconst REGISTRIES: { label: string; x: number; y: number; delay: number }[] = [\n  { label: \"npm\", x: -300, y: -120, delay: 30 },\n  { label: \"Cargo\", x: 300, y: -120, delay: 42 },\n  { label: \"PyPI\", x: -300, y: 120, delay: 54 },\n  { label: \"+ more\", x: 300, y: 120, delay: 66 },\n];\n\nconst RegistryScene: React.FC = () => (\n  <AbsoluteFill style={{ alignItems: \"center\", justifyContent: \"center\" }}>\n    <div style={{ position: \"absolute\", top: 70 }}>\n      <Reveal delay={2} distance={16} blur={10}>\n        <Heading size={42}>One pipeline. Every registry.</Heading>\n      </Reveal>\n    </div>\n\n    <div style={{ position: \"relative\", width: 1, height: 1 }}>\n      {/* connector lines drawing outward from the hub */}\n      <svg\n        width={760}\n        height={420}\n        viewBox=\"-380 -210 760 420\"\n        style={{\n          position: \"absolute\",\n          left: -380,\n          top: -210,\n          overflow: \"visible\",\n        }}\n      >\n        {REGISTRIES.map((r) => (\n          <Stroke\n            key={r.label}\n            d={`M0 0 L ${r.x * 0.78} ${r.y * 0.74}`}\n            delay={r.delay - 8}\n            dur={12}\n            width={2.2}\n            color={HAIR}\n          />\n        ))}\n      </svg>\n\n      {/* hub */}\n      <div\n        style={{\n          position: \"absolute\",\n          left: -64,\n          top: -64,\n          width: 128,\n          height: 128,\n        }}\n      >\n        <Reveal delay={8} distance={0} blur={10} duration={16}>\n          <div\n            style={{\n              width: 128,\n              height: 128,\n              borderRadius: \"50%\",\n              border: `2px solid ${INK}`,\n              background: PAPER,\n              boxShadow: `5px 6px 0 ${INK}`,\n              display: \"flex\",\n              alignItems: \"center\",\n              justifyContent: \"center\",\n            }}\n          >\n            <Img src={LOGO} style={{ width: 78, height: 78 }} />\n          </div>\n        </Reveal>\n      </div>\n\n      {/* registry nodes */}\n      {REGISTRIES.map((r) => (\n        <div\n          key={r.label}\n          style={{\n            position: \"absolute\",\n            left: r.x - 70,\n            top: r.y - 30,\n            width: 140,\n          }}\n        >\n          <Reveal delay={r.delay} distance={18} blur={9}>\n            <div\n              style={{\n                display: \"flex\",\n                justifyContent: \"center\",\n                alignItems: \"center\",\n                height: 60,\n                border: `2px solid ${INK}`,\n                borderRadius: 12,\n                background: PAPER,\n                boxShadow: `3px 4px 0 ${INK}`,\n                fontFamily: MONO,\n                fontWeight: 500,\n                fontSize: 24,\n                color: INK,\n              }}\n            >\n              {r.label}\n            </div>\n          </Reveal>\n        </div>\n      ))}\n    </div>\n  </AbsoluteFill>\n);\n\n// ===========================================================================\n// Scene 9 — Retry-safe / plugins / programmable.\n// ===========================================================================\nconst StackCard: React.FC<{\n  title: string;\n  body: ReactNode;\n  delay: number;\n  tilt: number;\n}> = ({ title, body, delay, tilt }) => (\n  <Reveal delay={delay} distance={30} blur={12} duration={20}>\n    <PaperCard width={320} tilt={tilt} padding=\"26px 26px\">\n      <div\n        style={{\n          fontFamily: SANS,\n          fontWeight: 800,\n          fontSize: 27,\n          color: INK,\n          marginBottom: 12,\n        }}\n      >\n        {title}\n      </div>\n      <div style={{ fontFamily: SANS, fontSize: 19, color: FAINT, lineHeight: 1.45 }}>\n        {body}\n      </div>\n    </PaperCard>\n  </Reveal>\n);\n\nconst StackScene: React.FC = () => (\n  <AbsoluteFill\n    style={{\n      alignItems: \"center\",\n      justifyContent: \"center\",\n      flexDirection: \"column\",\n      gap: 34,\n    }}\n  >\n    <Reveal delay={2} distance={16} blur={10}>\n      <Heading size={42}>Safe. Pluggable. Programmable.</Heading>\n    </Reveal>\n    <div style={{ display: \"flex\", gap: 30, alignItems: \"flex-start\" }}>\n      <StackCard\n        delay={14}\n        tilt={-1.5}\n        title=\"Retry-safe\"\n        body={\n          <>\n            The publish lock lives in git — a failed publish is just a{\" \"}\n            <span style={{ color: INK, fontWeight: 700 }}>retry</span>.\n          </>\n        }\n      />\n      <StackCard\n        delay={24}\n        tilt={1}\n        title=\"Plugins\"\n        body={\n          <span style={{ fontFamily: MONO, fontSize: 18, color: INK }}>\n            github · npm · cargo · git · gitlab\n          </span>\n        }\n      />\n      <StackCard\n        delay={34}\n        tilt={-0.5}\n        title=\"Programmable\"\n        body={\n          <span style={{ fontFamily: MONO, fontSize: 17, color: INK }}>\n            willPublish() · draft() · publish()\n          </span>\n        }\n      />\n    </div>\n  </AbsoluteFill>\n);\n\n// ===========================================================================\n// Scene 10 — Let the agent write it.\n// ===========================================================================\nconst RobotDoodle: React.FC<{ delay: number }> = ({ delay }) => (\n  <svg width={108} height={120} viewBox=\"0 0 108 120\">\n    {/* antenna */}\n    <Stroke d=\"M54 8 L54 22\" delay={delay} dur={6} width={2.4} />\n    <circle\n      cx={54}\n      cy={6}\n      r={4}\n      fill=\"none\"\n      stroke={INK}\n      strokeWidth={2.4}\n    />\n    {/* head */}\n    <Stroke\n      d=\"M24 24 L84 24 L84 70 L24 70 Z\"\n      delay={delay + 4}\n      dur={14}\n      width={2.6}\n    />\n    {/* eyes */}\n    <Stroke d=\"M40 44 L40 52\" delay={delay + 16} dur={5} width={3} />\n    <Stroke d=\"M68 44 L68 52\" delay={delay + 18} dur={5} width={3} />\n    {/* mouth */}\n    <Stroke d=\"M42 60 Q54 66 66 60\" delay={delay + 20} dur={6} width={2.4} />\n    {/* body + arm holding the brush */}\n    <Stroke\n      d=\"M34 70 L34 104 L74 104 L74 70\"\n      delay={delay + 22}\n      dur={12}\n      width={2.6}\n    />\n    <Stroke d=\"M74 86 L98 96\" delay={delay + 28} dur={8} width={2.6} />\n  </svg>\n);\n\nconst AgentScene: React.FC = () => (\n  <AbsoluteFill\n    style={{\n      alignItems: \"center\",\n      justifyContent: \"center\",\n      flexDirection: \"column\",\n      gap: 26,\n    }}\n  >\n    <Reveal delay={2} distance={14} blur={9}>\n      <div style={{ fontFamily: MONO, fontSize: 22, color: INK }}>\n        <span style={{ color: FAINT }}>$ </span>tegami init-agent\n      </div>\n    </Reveal>\n\n    <div style={{ display: \"flex\", alignItems: \"center\", gap: 34 }}>\n      <Reveal delay={16} distance={18} blur={10}>\n        <RobotDoodle delay={20} />\n      </Reveal>\n\n      <Reveal delay={28} distance={26} blur={12} duration={18}>\n        <PaperCard width={520} tilt={1} padding=\"24px 30px\">\n          <MonoType\n            text=\"## Fix flaky upload retry\"\n            delay={36}\n            cps={15}\n            fontSize={20}\n            weight={600}\n          />\n          <div style={{ marginTop: 14 }}>\n            <Hand size={30} color={FAINT}>\n              ...your agent writes the changelog.\n            </Hand>\n          </div>\n        </PaperCard>\n      </Reveal>\n    </div>\n  </AbsoluteFill>\n);\n\n// ===========================================================================\n// Scene 11 — Coming from Changesets?\n// ===========================================================================\nconst MigrateScene: React.FC = () => (\n  <AbsoluteFill\n    style={{\n      alignItems: \"center\",\n      justifyContent: \"center\",\n      flexDirection: \"column\",\n      gap: 30,\n    }}\n  >\n    <Reveal delay={2} distance={14} blur={9}>\n      <Hand size={56}>Already on Changesets?</Hand>\n    </Reveal>\n\n    <Reveal delay={18} distance={20} blur={11}>\n      <div style={{ position: \"relative\", width: 620, height: 92 }}>\n        <StrikethroughReplace\n          from=\".changeset/\"\n          to=\".tegami/\"\n          lineColor={INK}\n          color={INK}\n          fontSize={56}\n          fontWeight={700}\n        />\n      </div>\n    </Reveal>\n\n    <Reveal delay={40} distance={14} blur={9}>\n      <Heading size={32} weight={600}>\n        Same flow. <span style={{ color: FAINT }}>Migrate in an afternoon.</span>\n      </Heading>\n    </Reveal>\n  </AbsoluteFill>\n);\n\n// ===========================================================================\n// Scene 12 — CTA: record your changes.\n// ===========================================================================\nconst InstallPill: React.FC<{ delay: number; copyAt: number }> = ({\n  delay,\n  copyAt,\n}) => {\n  const frame = useCurrentFrame();\n  const enter = interpolate(frame, [delay, delay + 16], [0, 1], {\n    extrapolateLeft: \"clamp\",\n    extrapolateRight: \"clamp\",\n    easing: Easing.bezier(0.22, 1, 0.36, 1),\n  });\n  const copied = frame >= copyAt;\n  const pop = interpolate(frame - copyAt, [0, 8], [0.5, 1], {\n    extrapolateLeft: \"clamp\",\n    extrapolateRight: \"clamp\",\n    easing: Easing.bezier(0.34, 1.56, 0.64, 1),\n  });\n  return (\n    <div\n      style={{\n        display: \"inline-flex\",\n        alignItems: \"center\",\n        gap: 13,\n        height: 54,\n        padding: \"0 22px\",\n        borderRadius: 999,\n        border: `2px solid ${INK}`,\n        background: PAPER,\n        boxShadow: `4px 5px 0 ${INK}`,\n        fontFamily: MONO,\n        fontSize: 20,\n        opacity: enter,\n        transform: `translateY(${(1 - enter) * 12}px)`,\n      }}\n    >\n      <span style={{ color: FAINT }}>$</span>\n      <span style={{ color: INK }}>npm install tegami -D</span>\n      <span\n        style={{\n          display: \"inline-flex\",\n          marginLeft: 3,\n          color: INK,\n          transform: copied ? `scale(${pop})` : \"scale(1)\",\n        }}\n      >\n        {copied ? <CheckIcon size={18} /> : <CopyIcon size={18} />}\n      </span>\n    </div>\n  );\n};\n\nconst CtaScene: React.FC = () => (\n  <AbsoluteFill style={{ alignItems: \"center\", justifyContent: \"center\" }}>\n    <div\n      style={{ display: \"flex\", flexDirection: \"column\", alignItems: \"center\" }}\n    >\n      <Reveal delay={2} distance={10} blur={8} duration={14}>\n        <Img src={LOGO} style={{ width: 104, height: 104 }} />\n      </Reveal>\n      <Reveal delay={10} distance={14} blur={10}>\n        <div\n          style={{\n            fontFamily: SANS,\n            fontWeight: 800,\n            fontSize: 86,\n            letterSpacing: \"-0.03em\",\n            color: INK,\n            marginTop: 6,\n          }}\n        >\n          Tegami\n        </div>\n      </Reveal>\n      <div style={{ marginTop: -2, marginBottom: 24 }}>\n        <BrushUnderline width={300} delay={26} dur={16} thickness={9} />\n      </div>\n      <Reveal delay={34} distance={12} blur={8}>\n        <InstallPill delay={34} copyAt={64} />\n      </Reveal>\n      <Reveal delay={50} distance={10} blur={6}>\n        <div\n          style={{\n            marginTop: 18,\n            fontFamily: SANS,\n            fontSize: 22,\n            color: FAINT,\n          }}\n        >\n          tegami.fuma-nama.dev\n        </div>\n      </Reveal>\n      <div style={{ position: \"absolute\", bottom: 70, right: 150 }}>\n        <Reveal delay={70} distance={10} blur={6}>\n          <Hand size={40} style={{ transform: \"rotate(-6deg)\" }}>\n            record your changes!\n          </Hand>\n        </Reveal>\n      </div>\n    </div>\n  </AbsoluteFill>\n);\n\n// ===========================================================================\n// Transition presentations (ink-on-white — backgrounds never clash).\n// ===========================================================================\ntype EmptyProps = Record<string, never>;\n\nconst Crossfade: React.FC<TransitionPresentationComponentProps<EmptyProps>> = ({\n  children,\n  presentationProgress,\n  presentationDirection,\n}) => {\n  const entering = presentationDirection === \"entering\";\n  const opacity = entering ? presentationProgress : 1 - presentationProgress;\n  return <AbsoluteFill style={{ opacity }}>{children}</AbsoluteFill>;\n};\nconst crossfade = (): TransitionPresentation<EmptyProps> => ({\n  component: Crossfade,\n  props: {},\n});\n\nconst ZoomBlur: React.FC<\n  TransitionPresentationComponentProps<{ rise: number }>\n> = ({ children, presentationProgress, presentationDirection, passedProps }) => {\n  const { rise } = passedProps;\n  const entering = presentationDirection === \"entering\";\n  const p = interpolate(presentationProgress, [0, 1], [0, 1], {\n    easing: entering ? Easing.out(Easing.cubic) : Easing.in(Easing.cubic),\n  });\n  const style: React.CSSProperties = entering\n    ? {\n        opacity: p,\n        transform: `translateY(${(1 - p) * rise}px) scale(${0.86 + p * 0.14})`,\n        filter: p < 1 ? `blur(${(1 - p) * 16}px)` : undefined,\n      }\n    : {\n        opacity: 1 - p,\n        transform: `translateY(${-p * rise}px) scale(${1 + p * 0.18})`,\n        filter: p > 0 ? `blur(${p * 16}px)` : undefined,\n      };\n  return <AbsoluteFill style={style}>{children}</AbsoluteFill>;\n};\nconst zoomBlur = (rise = 0): TransitionPresentation<{ rise: number }> => ({\n  component: ZoomBlur,\n  props: { rise },\n});\n\nconst PushSlide: React.FC<\n  TransitionPresentationComponentProps<{ dist: number }>\n> = ({ children, presentationProgress, presentationDirection, passedProps }) => {\n  const { dist } = passedProps;\n  const entering = presentationDirection === \"entering\";\n  const p = interpolate(presentationProgress, [0, 1], [0, 1], {\n    easing: Easing.inOut(Easing.cubic),\n  });\n  const x = entering ? (1 - p) * dist : -p * dist;\n  const opacity = entering ? p : 1 - p;\n  return (\n    <AbsoluteFill style={{ transform: `translateX(${x}px)`, opacity }}>\n      {children}\n    </AbsoluteFill>\n  );\n};\nconst pushSlide = (dist = 70): TransitionPresentation<{ dist: number }> => ({\n  component: PushSlide,\n  props: { dist },\n});\n\n// ===========================================================================\n// Persistent paper background — warm white with a very faint notebook dot grid.\n// ===========================================================================\nconst PaperBg: React.FC = () => (\n  <AbsoluteFill style={{ background: PAPER }}>\n    <AbsoluteFill\n      style={{\n        backgroundImage: `radial-gradient(${DOT} 1.4px, transparent 1.4px)`,\n        backgroundSize: \"30px 30px\",\n        opacity: 0.7,\n      }}\n    />\n  </AbsoluteFill>\n);\n\n// ===========================================================================\n// Composition root.\n// ===========================================================================\nexport const TegamiDemo: React.FC = () => {\n  return (\n    <RemocnUIProvider>\n      <AbsoluteFill\n        style={\n          {\n            \"--font-geist-sans\": SANS_FAMILY,\n            \"--font-geist-mono\": MONO_FAMILY,\n            \"--font-hand\": HAND_FAMILY,\n          } as React.CSSProperties\n        }\n      >\n        <PaperBg />\n\n        <TransitionSeries>\n          {/* 1 — Release-day avalanche */}\n          <TransitionSeries.Sequence durationInFrames={S_AVALANCHE}>\n            <AvalancheScene />\n          </TransitionSeries.Sequence>\n          <TransitionSeries.Transition\n            timing={linearTiming({ durationInFrames: T_X })}\n            presentation={crossfade()}\n          />\n\n          {/* 2 — The old fixes break */}\n          <TransitionSeries.Sequence durationInFrames={S_BREAKS}>\n            <BreaksScene />\n          </TransitionSeries.Sequence>\n          <TransitionSeries.Transition\n            timing={linearTiming({ durationInFrames: T_Z1 })}\n            presentation={zoomBlur(46)}\n          />\n\n          {/* 3 — Meet Tegami */}\n          <TransitionSeries.Sequence durationInFrames={S_MEET}>\n            <MeetScene />\n          </TransitionSeries.Sequence>\n          <TransitionSeries.Transition\n            timing={linearTiming({ durationInFrames: T_Z2 })}\n            presentation={zoomBlur(40)}\n          />\n\n          {/* 4 — A script you own */}\n          <TransitionSeries.Sequence durationInFrames={S_SCRIPT}>\n            <ScriptScene />\n          </TransitionSeries.Sequence>\n          <TransitionSeries.Transition\n            timing={linearTiming({ durationInFrames: T_X })}\n            presentation={crossfade()}\n          />\n\n          {/* 5 — Write the note */}\n          <TransitionSeries.Sequence durationInFrames={S_NOTE}>\n            <NoteScene />\n          </TransitionSeries.Sequence>\n          <TransitionSeries.Transition\n            timing={linearTiming({ durationInFrames: T_P })}\n            presentation={pushSlide(80)}\n          />\n\n          {/* 6 — Version & lock */}\n          <TransitionSeries.Sequence durationInFrames={S_VERSION}>\n            <VersionScene />\n          </TransitionSeries.Sequence>\n          <TransitionSeries.Transition\n            timing={linearTiming({ durationInFrames: T_P })}\n            presentation={pushSlide(80)}\n          />\n\n          {/* 7 — Merge & ship */}\n          <TransitionSeries.Sequence durationInFrames={S_SHIP}>\n            <ShipScene />\n          </TransitionSeries.Sequence>\n          <TransitionSeries.Transition\n            timing={linearTiming({ durationInFrames: T_Z3 })}\n            presentation={zoomBlur(46)}\n          />\n\n          {/* 8 — Not just npm */}\n          <TransitionSeries.Sequence durationInFrames={S_REGISTRY}>\n            <RegistryScene />\n          </TransitionSeries.Sequence>\n          <TransitionSeries.Transition\n            timing={linearTiming({ durationInFrames: T_X })}\n            presentation={crossfade()}\n          />\n\n          {/* 9 — Safe / pluggable / programmable */}\n          <TransitionSeries.Sequence durationInFrames={S_STACK}>\n            <StackScene />\n          </TransitionSeries.Sequence>\n          <TransitionSeries.Transition\n            timing={linearTiming({ durationInFrames: T_X })}\n            presentation={crossfade()}\n          />\n\n          {/* 10 — Agent writes it */}\n          <TransitionSeries.Sequence durationInFrames={S_AGENT}>\n            <AgentScene />\n          </TransitionSeries.Sequence>\n          <TransitionSeries.Transition\n            timing={linearTiming({ durationInFrames: T_X })}\n            presentation={crossfade()}\n          />\n\n          {/* 11 — Coming from Changesets */}\n          <TransitionSeries.Sequence durationInFrames={S_MIGRATE}>\n            <MigrateScene />\n          </TransitionSeries.Sequence>\n          <TransitionSeries.Transition\n            timing={linearTiming({ durationInFrames: T_Z4 })}\n            presentation={zoomBlur(56)}\n          />\n\n          {/* 12 — CTA */}\n          <TransitionSeries.Sequence durationInFrames={S_CTA}>\n            <CtaScene />\n          </TransitionSeries.Sequence>\n        </TransitionSeries>\n      </AbsoluteFill>\n    </RemocnUIProvider>\n  );\n};\n",
      "type": "registry:component",
      "target": "demos/tegami/index.tsx"
    },
    {
      "path": "src/lib/demo-assets.ts",
      "content": "import { staticFile } from \"remotion\";\n\n// Demo assets are loaded from absolute URLs so a demo installed into another\n// project via the registry renders identically without copying this repo's\n// public/ directory. The same URLs are used on the site and in local renders,\n// which keeps a single code path (network access is required for rendering).\n//\n// Override with REMOTION_DEMO_ASSETS_BASE — only REMOTION_-prefixed env vars\n// reach Remotion compositions (next.config.js additionally inlines it for the\n// site's <Player>). Two override forms:\n//   - \"local\" — serve straight from this repo's public/ via staticFile();\n//     works in Remotion Studio, CLI renders and the Next dev site. Set it in\n//     the gitignored .env so previews never depend on pushed assets.\n//   - any URL — e.g. a local static server (http://127.0.0.1:8123).\nexport const DEMO_ASSETS_BASE =\n  process.env.REMOTION_DEMO_ASSETS_BASE ||\n  \"https://raw.githubusercontent.com/Remocn/remocn-collections/main/public\";\n\nexport const demoAsset = (path: string): string => {\n  const clean = path.replace(/^\\/+/, \"\");\n  if (DEMO_ASSETS_BASE === \"local\") return staticFile(clean);\n  return `${DEMO_ASSETS_BASE}/${clean}`;\n};\n",
      "type": "registry:component",
      "target": "lib/demo-assets.ts"
    },
    {
      "path": "src/demos/tegami/prompt.md",
      "content": "<!-- TODO(draft): placeholder written by AI — replace with the real prompt used to generate this video -->\n\nMake an introducing video for Tegami, a tool for managing changelogs, versioning, and publishing in monorepos. Lean fully into its hand-drawn brand — black ink on white paper, sketchy doodles, the てがみ mascot, handwritten Caveat-style annotations, nothing digital-looking. Do a before/after arc: release day chores piling up, the old fixes breaking down (a brittle script, fighting Changesets), then \"Meet Tegami\" — a script you actually own, writing a change like a little note in .tegami/, tegami version computing the bumps, merging the Version Packages PR and it ships with GitHub releases automatically. Cover that it's one pipeline across npm, Cargo, and PyPI, retry-safe and pluggable, and that an AI agent can write the changelog for you. Mention migrating from Changesets, and close with \"record your changes!\" and tegami.fuma-nama.dev. Build the paper/ink motion with remocn.\n",
      "type": "registry:file",
      "target": "demos/tegami/prompt.md"
    }
  ],
  "docs": "Register the composition in your Remotion Root:\n\n  import { TegamiDemo } from \"@/demos/tegami\";\n  <Composition id=\"tegami\" component={TegamiDemo} durationInFrames={1494} fps={30} width={1280} height={720} />\n\nRequires Tailwind v4 wired into Remotion (@remotion/tailwind-v4).\nRender locally: npx remotion render tegami out/tegami.mp4",
  "type": "registry:block"
}