{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "agent-skills",
  "title": "Agent Skills — Claude Code makes the video",
  "description": "remocn demo composition \"Agent Skills — Claude Code makes the video\" — installs the full Remotion composition. Generated with AI from the prompt in demos/agent-skills/prompt.md.",
  "dependencies": [
    "@remotion/google-fonts",
    "@remotion/transitions",
    "culori",
    "date-fns",
    "remotion"
  ],
  "registryDependencies": [
    "https://remocn.dev/r/alert-dialog.json",
    "https://remocn.dev/r/backdrop.json",
    "https://remocn.dev/r/blur-in.json",
    "https://remocn.dev/r/button.json",
    "https://remocn.dev/r/caret.json",
    "https://remocn.dev/r/checkbox.json",
    "https://remocn.dev/r/confetti.json",
    "https://remocn.dev/r/drawer.json",
    "https://remocn.dev/r/github-stars.json",
    "https://remocn.dev/r/kinetic-center-build.json",
    "https://remocn.dev/r/number-wheel.json",
    "https://remocn.dev/r/remocn-ui.json",
    "https://remocn.dev/r/select-item.json",
    "https://remocn.dev/r/select.json",
    "https://remocn.dev/r/sheet.json",
    "https://remocn.dev/r/short-slide-down.json",
    "https://remocn.dev/r/spinner.json",
    "https://remocn.dev/r/terminal-simulator.json"
  ],
  "files": [
    {
      "path": "src/components/remocn/claude-code.tsx",
      "content": "\"use client\";\n\nimport { loadFont } from \"@remotion/google-fonts/JetBrainsMono\";\nimport {\n  AbsoluteFill,\n  interpolate,\n  spring,\n  useCurrentFrame,\n  useVideoConfig,\n} from \"remotion\";\nimport { Caret } from \"@/components/remocn/caret\";\nimport { useTypewriter } from \"@/lib/remocn-ui\";\n\nconst { fontFamily: MONO_FAMILY } = loadFont();\n\nexport interface ClaudeCodeProps {\n  title?: string;\n  userName?: string;\n  model?: string;\n  cwd?: string;\n  placeholder?: string;\n  prompt?: string;\n  accentColor?: string;\n  speed?: number;\n  /**\n   * When true, the prompt is \"submitted\" after it finishes typing and the CLI\n   * enters a thinking phase — a pulsing status line (spinner + cycling verb +\n   * elapsed/token counters) over a stream of activity lines. Off by default so\n   * existing usage of the welcome screen is unchanged.\n   */\n  thinking?: boolean;\n  /**\n   * Effective frame at which the thinking phase begins. Defaults to just after\n   * the typewriter finishes (derived from `prompt`, `TYPING_START_FRAME` and\n   * `TYPING_CPS`).\n   */\n  thinkingStartFrame?: number;\n  /** Status verbs cycled in the thinking line (one every ~0.8s). */\n  thinkingVerbs?: string[];\n  /** Activity lines streamed in one-by-one beneath the status line. */\n  thinkingActivity?: string[];\n  /** Frames between each streamed activity line. */\n  thinkingLineStagger?: number;\n}\n\ninterface Theme {\n  page: string;\n  windowBar: string;\n  windowBody: string;\n  fg: string;\n  fgMuted: string;\n  fgDim: string;\n  boxBorder: string;\n}\n\nexport const THEMES: Record<\"light\" | \"dark\", Theme> = {\n  light: {\n    page: \"#E8E5DD\",\n    windowBar: \"#D8D3CA\",\n    windowBody: \"#FBFAF7\",\n    fg: \"#1F1E1D\",\n    fgMuted: \"#73726C\",\n    fgDim: \"#A3A097\",\n    boxBorder: \"#D97757\",\n  },\n  dark: {\n    page: \"#2B2A28\",\n    windowBar: \"#3A3633\",\n    windowBody: \"#1B1A18\",\n    fg: \"#E8E5DD\",\n    fgMuted: \"#8A857C\",\n    fgDim: \"#6B6660\",\n    boxBorder: \"#D97757\",\n  },\n};\n\nexport const TYPING_START_FRAME = 48;\n\nexport const TYPING_CPS = 18;\n\nexport const WHATS_NEW: string[] = [\n  \"/agents to create subagents\",\n  \"/security-review for review agent\",\n  \"ctrl+b to background bashes\",\n];\n\n// Pulsing star used by the thinking status line — cycles outward then back so\n// it \"breathes\" in place rather than spinning.\nconst THINKING_GLYPHS = [\"·\", \"✢\", \"✳\", \"✶\", \"✺\", \"✶\", \"✳\", \"✢\"];\n\nexport const DEFAULT_THINKING_VERBS: string[] = [\n  \"Thinking\",\n  \"Composing\",\n  \"Animating\",\n  \"Rendering\",\n  \"Polishing\",\n];\n\nexport const DEFAULT_THINKING_ACTIVITY: string[] = [\n  \"Loading skill: remocn\",\n  \"Reading components catalog\",\n  \"Composing scenes — backdrop, titles, code\",\n  \"Wiring kinetic transitions\",\n  \"✓ Created the composition\",\n];\n\n/** Format a token count as Claude Code does: \"1.2k\". */\nfunction formatTokens(n: number): string {\n  if (n < 1000) return String(Math.round(n));\n  return `${(n / 1000).toFixed(1)}k`;\n}\n\nfunction introBounceIn(\n  frame: number,\n  fps: number,\n): { translateY: number; scale: number } {\n  const s = spring({\n    fps,\n    frame,\n    config: { damping: 14, stiffness: 110, mass: 0.7 },\n  });\n  const translateY = interpolate(s, [0, 1], [28, 0]);\n  const scale = interpolate(s, [0, 1], [0.97, 1]);\n  return { translateY, scale };\n}\n\nfunction fadeUpAt(\n  frame: number,\n  range: [number, number],\n): { opacity: number; translateY: number } {\n  const opts = {\n    extrapolateLeft: \"clamp\" as const,\n    extrapolateRight: \"clamp\" as const,\n  };\n  return {\n    opacity: interpolate(frame, range, [0, 1], opts),\n    translateY: interpolate(frame, range, [12, 0], opts),\n  };\n}\n\nfunction Mascot({ accent, size = 96 }: { accent: string; size?: number }) {\n  return (\n    <svg\n      height={size}\n      viewBox=\"0 0 24 24\"\n      width={size}\n      xmlns=\"http://www.w3.org/2000/svg\"\n    >\n      <title>Claude Code</title>\n      <path\n        clipRule=\"evenodd\"\n        d=\"M20.998 10.949H24v3.102h-3v3.028h-1.487V20H18v-2.921h-1.487V20H15v-2.921H9V20H7.488v-2.921H6V20H4.487v-2.921H3V14.05H0V10.95h3V5h17.998v5.949zM6 10.949h1.488V8.102H6v2.847zm10.51 0H18V8.102h-1.49v2.847z\"\n        fill={accent}\n        fillRule=\"evenodd\"\n      />\n    </svg>\n  );\n}\n\ninterface ThinkingStreamProps {\n  /** Effective frames elapsed since the thinking phase began (>= 0). */\n  local: number;\n  fps: number;\n  prompt: string;\n  accentColor: string;\n  theme: Theme;\n  verbs: string[];\n  activity: string[];\n  lineStagger: number;\n}\n\nfunction ThinkingStream({\n  local,\n  fps,\n  prompt,\n  accentColor,\n  theme,\n  verbs,\n  activity,\n  lineStagger,\n}: ThinkingStreamProps) {\n  const glyph = THINKING_GLYPHS[Math.floor(local / 4) % THINKING_GLYPHS.length];\n  const verb = verbs[Math.floor(local / 24) % verbs.length];\n  const seconds = Math.max(0, Math.floor(local / fps));\n  // Tokens ramp up smoothly while thinking, easing toward a plausible total.\n  const tokens = formatTokens(interpolate(local, [0, 150], [120, 3400], {\n    extrapolateLeft: \"clamp\",\n    extrapolateRight: \"clamp\",\n  }));\n\n  return (\n    <div style={{ fontFamily: MONO_FAMILY }}>\n      {/* The submitted command, echoed back as a sent message. */}\n      <div\n        style={{\n          display: \"flex\",\n          flexDirection: \"row\",\n          alignItems: \"center\",\n          fontSize: 17,\n          whiteSpace: \"pre\",\n          color: theme.fg,\n        }}\n      >\n        <span style={{ color: theme.fgMuted }}>{\"> \"}</span>\n        {prompt}\n      </div>\n\n      {/* Status line: breathing star + cycling verb + meta. */}\n      <div\n        style={{\n          display: \"flex\",\n          flexDirection: \"row\",\n          alignItems: \"center\",\n          gap: 10,\n          marginTop: 18,\n          fontSize: 16,\n          whiteSpace: \"pre\",\n        }}\n      >\n        <span style={{ color: accentColor, width: 14, display: \"inline-block\" }}>\n          {glyph}\n        </span>\n        <span style={{ color: theme.fg }}>{verb}…</span>\n        <span style={{ color: theme.fgDim }}>\n          {`(esc to interrupt · ${seconds}s · ↑ ${tokens} tokens)`}\n        </span>\n      </div>\n\n      {/* Streamed activity lines, each fading up as it arrives. */}\n      <div\n        style={{\n          marginTop: 14,\n          display: \"flex\",\n          flexDirection: \"column\",\n          gap: 8,\n        }}\n      >\n        {activity.map((line, i) => {\n          const at = (i + 1) * lineStagger;\n          const fade = fadeUpAt(local, [at, at + 14]);\n          const done = line.trimStart().startsWith(\"✓\");\n          return (\n            <div\n              key={line}\n              style={{\n                display: \"flex\",\n                flexDirection: \"row\",\n                alignItems: \"center\",\n                gap: 8,\n                fontSize: 14,\n                whiteSpace: \"pre\",\n                opacity: fade.opacity,\n                transform: `translateY(${fade.translateY}px)`,\n              }}\n            >\n              <span style={{ color: done ? \"#28C840\" : accentColor }}>\n                {done ? \"⎿  \" : \"⎿  \"}\n              </span>\n              <span style={{ color: done ? theme.fg : theme.fgMuted }}>\n                {line}\n              </span>\n            </div>\n          );\n        })}\n      </div>\n    </div>\n  );\n}\n\nexport function ClaudeCode({\n  title = \"Claude Code v2.0.0\",\n  userName = \"Meaghan\",\n  model = \"Opus 4.8 • Max 20x\",\n  cwd = \"/users/meaghan/code/apps\",\n  placeholder = 'Try \"edit <filepath> to ...\"',\n  prompt = \"edit src/theme.ts to add a dark mode toggle\",\n  accentColor = \"#D97757\",\n  speed = 1,\n  thinking = false,\n  thinkingStartFrame,\n  thinkingVerbs = DEFAULT_THINKING_VERBS,\n  thinkingActivity = DEFAULT_THINKING_ACTIVITY,\n  thinkingLineStagger = 22,\n}: ClaudeCodeProps) {\n  const frame = useCurrentFrame();\n  const { width, height, fps } = useVideoConfig();\n  const t = THEMES.dark;\n\n  const refW = 1280;\n  const refH = 720;\n  const stageScale = Math.min(width / refW, height / refH);\n\n  const tw = useTypewriter(prompt, {\n    cps: TYPING_CPS,\n    speed,\n    startFrame: TYPING_START_FRAME,\n  });\n  const showText = tw.count > 0;\n\n  // Effective (speed-adjusted) frame, and when the thinking phase kicks in —\n  // by default right after the typewriter finishes the prompt.\n  const effFrame = frame * speed;\n  const typingDoneFrame =\n    TYPING_START_FRAME + (prompt.length / TYPING_CPS) * fps;\n  const thinkStart = thinkingStartFrame ?? typingDoneFrame + 12;\n  const isThinking = thinking && effFrame >= thinkStart;\n  // Once thinking begins, fade the welcome box back so the stream is the focus.\n  const welcomeDim = thinking\n    ? interpolate(effFrame, [thinkStart, thinkStart + 16], [1, 0.22], {\n        extrapolateLeft: \"clamp\",\n        extrapolateRight: \"clamp\",\n      })\n    : 1;\n\n  const intro = introBounceIn(frame * speed, fps);\n  const leftFade = fadeUpAt(frame * speed, [6, 22]);\n  const rightFade = fadeUpAt(frame * speed, [12, 30]);\n  const promptFade = fadeUpAt(frame * speed, [18, 36]);\n\n  const border = accentColor;\n\n  const winLeft = 90;\n  const winTop = 40;\n  const winWidth = 1100;\n  const winHeight = 620;\n  const barHeight = 40;\n\n  return (\n    <AbsoluteFill style={{ background: \"transparent\" }}>\n      <div\n        style={{\n          position: \"absolute\",\n          left: \"50%\",\n          top: \"50%\",\n          width: refW,\n          height: refH,\n          transform: `translate(-50%, -50%) scale(${stageScale})`,\n        }}\n      >\n        <div\n          style={{\n            position: \"absolute\",\n            left: winLeft,\n            top: winTop,\n            width: winWidth,\n            height: winHeight,\n            background: t.windowBody,\n            borderRadius: 12,\n            overflow: \"hidden\",\n            boxShadow: \"0 24px 60px -20px rgba(0,0,0,0.6)\",\n            opacity: intro.scale,\n            transform: `translateY(${intro.translateY}px) scale(${intro.scale})`,\n            transformOrigin: \"center top\",\n            display: \"flex\",\n            flexDirection: \"column\",\n            boxSizing: \"border-box\",\n          }}\n        >\n          <div\n            style={{\n              height: barHeight,\n              background: t.windowBar,\n              display: \"flex\",\n              alignItems: \"center\",\n              gap: 8,\n              paddingLeft: 16,\n              flexShrink: 0,\n            }}\n          >\n            <div\n              style={{\n                width: 12,\n                height: 12,\n                borderRadius: \"50%\",\n                background: \"#FF5F57\",\n              }}\n            />\n            <div\n              style={{\n                width: 12,\n                height: 12,\n                borderRadius: \"50%\",\n                background: \"#FEBC2E\",\n              }}\n            />\n            <div\n              style={{\n                width: 12,\n                height: 12,\n                borderRadius: \"50%\",\n                background: \"#28C840\",\n              }}\n            />\n          </div>\n\n          <div\n            style={{\n              flex: 1,\n              position: \"relative\",\n              padding: 28,\n              boxSizing: \"border-box\",\n              display: \"flex\",\n              flexDirection: \"column\",\n            }}\n          >\n            <div\n              style={{\n                position: \"relative\",\n                border: `1px dashed ${border}`,\n                borderRadius: 6,\n                padding: \"28px 24px 24px\",\n                opacity: leftFade.opacity * welcomeDim,\n                transform: `translateY(${leftFade.translateY}px)`,\n              }}\n            >\n              <span\n                style={{\n                  position: \"absolute\",\n                  top: -11,\n                  left: 22,\n                  padding: \"0 10px\",\n                  background: t.windowBody,\n                  color: accentColor,\n                  fontFamily: MONO_FAMILY,\n                  fontSize: 16,\n                  fontWeight: 700,\n                }}\n              >\n                {title}\n              </span>\n\n              <div style={{ display: \"flex\", flexDirection: \"row\" }}>\n                <div\n                  style={{\n                    width: \"42%\",\n                    display: \"flex\",\n                    flexDirection: \"column\",\n                    alignItems: \"flex-start\",\n                    gap: 16,\n                    paddingRight: 24,\n                    boxSizing: \"border-box\",\n                  }}\n                >\n                  <div\n                    style={{\n                      fontFamily: MONO_FAMILY,\n                      fontSize: 20,\n                      color: t.fg,\n                    }}\n                  >\n                    Welcome back {userName}!\n                  </div>\n                  <div style={{ alignSelf: \"center\" }}>\n                    <Mascot accent={accentColor} />\n                  </div>\n                  <div\n                    style={{\n                      display: \"flex\",\n                      flexDirection: \"column\",\n                      gap: 4,\n                    }}\n                  >\n                    <div\n                      style={{\n                        fontFamily: MONO_FAMILY,\n                        fontSize: 15,\n                        color: t.fgMuted,\n                      }}\n                    >\n                      {model}\n                    </div>\n                    <div\n                      style={{\n                        fontFamily: MONO_FAMILY,\n                        fontSize: 15,\n                        color: t.fgMuted,\n                      }}\n                    >\n                      {cwd}\n                    </div>\n                  </div>\n                </div>\n\n                <div\n                  style={{\n                    width: \"58%\",\n                    borderLeft: `1px dashed ${border}`,\n                    paddingLeft: 24,\n                    boxSizing: \"border-box\",\n                    display: \"flex\",\n                    flexDirection: \"column\",\n                    opacity: rightFade.opacity,\n                    transform: `translateY(${rightFade.translateY}px)`,\n                  }}\n                >\n                  <div>\n                    <div\n                      style={{\n                        fontFamily: MONO_FAMILY,\n                        fontSize: 15,\n                        fontWeight: 700,\n                        color: accentColor,\n                        marginBottom: 10,\n                      }}\n                    >\n                      What's new\n                    </div>\n                    <div\n                      style={{\n                        display: \"flex\",\n                        flexDirection: \"column\",\n                        gap: 6,\n                      }}\n                    >\n                      {WHATS_NEW.map((line) => (\n                        <div\n                          key={line}\n                          style={{\n                            fontFamily: MONO_FAMILY,\n                            fontSize: 14,\n                            color: t.fg,\n                          }}\n                        >\n                          {line}\n                        </div>\n                      ))}\n                      <div\n                        style={{\n                          fontFamily: MONO_FAMILY,\n                          fontSize: 14,\n                          color: t.fgDim,\n                        }}\n                      >\n                        ... /help for more\n                      </div>\n                    </div>\n                  </div>\n                </div>\n              </div>\n            </div>\n\n            <div style={{ height: 32 }} />\n\n            <div\n              style={{\n                opacity: promptFade.opacity,\n                transform: `translateY(${promptFade.translateY}px)`,\n              }}\n            >\n              <div\n                style={{\n                  height: 1,\n                  background: t.fgDim,\n                  opacity: 0.4,\n                  marginBottom: 16,\n                }}\n              />\n              {isThinking ? (\n                <ThinkingStream\n                  local={effFrame - thinkStart}\n                  fps={fps}\n                  prompt={prompt}\n                  accentColor={accentColor}\n                  theme={t}\n                  verbs={thinkingVerbs}\n                  activity={thinkingActivity}\n                  lineStagger={thinkingLineStagger}\n                />\n              ) : (\n              <div\n                style={{\n                  display: \"flex\",\n                  flexDirection: \"row\",\n                  alignItems: \"center\",\n                  fontFamily: MONO_FAMILY,\n                  fontSize: 17,\n                  whiteSpace: \"pre\",\n                }}\n              >\n                <span style={{ color: t.fgMuted }}>{\"> \"}</span>\n                {showText ? (\n                  <span\n                    style={{\n                      position: \"relative\",\n                      color: t.fg,\n                      display: \"inline-flex\",\n                      alignItems: \"center\",\n                    }}\n                  >\n                    {tw.text}\n                    <span style={{ opacity: 0.55, display: \"inline-flex\" }}>\n                      <Caret\n                        width={11}\n                        height={22}\n                        color={t.fg}\n                        blink={!tw.typing}\n                        speed={speed}\n                        marginLeft={2}\n                      />\n                    </span>\n                  </span>\n                ) : (\n                  <span\n                    style={{\n                      position: \"relative\",\n                      color: t.fgMuted,\n                      display: \"inline-flex\",\n                      alignItems: \"center\",\n                    }}\n                  >\n                    <span style={{ opacity: 0.55, display: \"inline-flex\" }}>\n                      <Caret\n                        width={11}\n                        height={22}\n                        color={t.fg}\n                        blink={!tw.typing}\n                        speed={speed}\n                      />\n                    </span>\n                    <span style={{ marginLeft: 6 }}>{placeholder}</span>\n                  </span>\n                )}\n              </div>\n              )}\n            </div>\n          </div>\n        </div>\n      </div>\n    </AbsoluteFill>\n  );\n}\n",
      "type": "registry:component",
      "target": "components/remocn/claude-code.tsx"
    },
    {
      "path": "src/components/remocn/glass-code-block.tsx",
      "content": "\"use client\";\n\nimport {\n  Sequence,\n  interpolate,\n  useCurrentFrame,\n  useVideoConfig,\n} from \"remotion\";\n\nexport interface GlassCodeBlockProps {\n  code?: string;\n  title?: string;\n  width?: number;\n  height?: number;\n  fontSize?: number;\n  glassColor?: string;\n  staggerFrames?: number;\n  showTrafficLights?: boolean;\n  /** The soft cyan/purple gradient glow behind the glass. On by default. */\n  aura?: boolean;\n  /**\n   * Reveal the first line as a left-to-right typewriter (with a block cursor)\n   * instead of the default fade-up; the remaining lines then stagger in only\n   * after the first line finishes typing. Off by default.\n   */\n  typeFirstLine?: boolean;\n  /** Typing speed (characters/second) when `typeFirstLine` is on. */\n  firstLineCps?: number;\n  speed?: number;\n  className?: string;\n}\n\n/** Frames the first-line typewriter takes — exported so a caller can sync a\n *  camera move to the moment typing completes. */\nexport function firstLineTypeFrames(\n  firstLine: string,\n  firstLineCps: number,\n  fps: number,\n): number {\n  return Math.ceil((firstLine.length / firstLineCps) * fps);\n}\n\nconst FONT_MONO =\n  \"var(--font-geist-mono), ui-monospace, SFMono-Regular, monospace\";\n\nconst DEFAULT_CODE = `import { motion } from \"remotion\";\n\n// Generate a hero scene\nexport function Hero() {\n  const frame = useCurrentFrame();\n  const opacity = frame / 30;\n  return <h1 style={{ opacity }}>Hello</h1>;\n}`;\n\n// Minimal regex tokenizer. NOT a real syntax highlighter — just enough to\n// give the eye color anchors. Order matters: comments → strings → keywords.\nconst KEYWORDS = new Set([\n  \"import\",\n  \"from\",\n  \"export\",\n  \"function\",\n  \"const\",\n  \"let\",\n  \"var\",\n  \"return\",\n  \"if\",\n  \"else\",\n  \"for\",\n  \"while\",\n  \"new\",\n  \"class\",\n  \"extends\",\n  \"default\",\n  \"true\",\n  \"false\",\n  \"null\",\n  \"undefined\",\n]);\n\ntype Token = {\n  text: string;\n  kind: \"code\" | \"comment\" | \"string\" | \"keyword\" | \"number\";\n};\n\nfunction tokenizeLine(line: string): Token[] {\n  // Whole-line comment.\n  const trimmed = line.trimStart();\n  if (trimmed.startsWith(\"//\")) {\n    return [{ text: line, kind: \"comment\" }];\n  }\n\n  const tokens: Token[] = [];\n  // Split keeping delimiters: words, strings, numbers, everything else.\n  const re = /(\"[^\"]*\"|'[^']*'|`[^`]*`|\\b\\d+\\b|\\b[A-Za-z_$][\\w$]*\\b|[^\\w\"']+)/g;\n  let match: RegExpExecArray | null;\n  while ((match = re.exec(line)) !== null) {\n    const t = match[0];\n    const first = t[0];\n    if (first === '\"' || first === \"'\" || first === \"`\") {\n      tokens.push({ text: t, kind: \"string\" });\n    } else if (/^\\d+$/.test(t)) {\n      tokens.push({ text: t, kind: \"number\" });\n    } else if (/^[A-Za-z_$][\\w$]*$/.test(t) && KEYWORDS.has(t)) {\n      tokens.push({ text: t, kind: \"keyword\" });\n    } else {\n      tokens.push({ text: t, kind: \"code\" });\n    }\n  }\n  return tokens;\n}\n\nconst TOKEN_COLORS: Record<Token[\"kind\"], string> = {\n  code: \"#e4e4e7\",\n  comment: \"#52525b\",\n  string: \"#86efac\",\n  keyword: \"#c4b5fd\",\n  number: \"#fcd34d\",\n};\n\nexport function GlassCodeBlock({\n  code = DEFAULT_CODE,\n  title = \"hero.tsx\",\n  width = 760,\n  height = 460,\n  fontSize = 16,\n  glassColor = \"rgba(10, 10, 10, 0.6)\",\n  staggerFrames = 4,\n  showTrafficLights = true,\n  aura = true,\n  typeFirstLine = false,\n  firstLineCps = 30,\n  speed = 1,\n  className,\n}: GlassCodeBlockProps) {\n  const { fps } = useVideoConfig();\n  const lines = code.split(\"\\n\");\n  // When the first line types, every later line waits until typing is done.\n  const typeDur = typeFirstLine\n    ? firstLineTypeFrames(lines[0] ?? \"\", firstLineCps, fps)\n    : 0;\n\n  return (\n    <div\n      className={className}\n      style={{\n        position: \"absolute\",\n        inset: 0,\n        display: \"flex\",\n        alignItems: \"center\",\n        justifyContent: \"center\",\n      }}\n    >\n      {/* Animated background hint behind the glass so the blur has something\n          to chew on. Pure CSS — no extra deps. */}\n      {aura && <BackdropAura />}\n\n      {/* 1px gradient ring acting as a microborder */}\n      <div\n        style={{\n          position: \"relative\",\n          padding: 1,\n          borderRadius: 16,\n          background:\n            \"linear-gradient(180deg, rgba(255,255,255,0.18) 0%, rgba(255,255,255,0) 100%)\",\n          width,\n          height,\n          boxShadow: \"0 50px 120px rgba(0,0,0,0.55)\",\n        }}\n      >\n        <div\n          style={{\n            width: \"100%\",\n            height: \"100%\",\n            borderRadius: 15,\n            background: glassColor,\n            backdropFilter: \"blur(16px)\",\n            WebkitBackdropFilter: \"blur(16px)\",\n            boxShadow: \"inset 0 1px 0 rgba(255,255,255,0.06)\",\n            display: \"flex\",\n            flexDirection: \"column\",\n            overflow: \"hidden\",\n            fontFamily: FONT_MONO,\n          }}\n        >\n          {/* Chrome */}\n          <div\n            style={{\n              height: 40,\n              display: \"flex\",\n              alignItems: \"center\",\n              gap: 8,\n              padding: \"0 16px\",\n              borderBottom: \"1px solid rgba(255,255,255,0.06)\",\n            }}\n          >\n            {showTrafficLights && (\n              <>\n                <Light color=\"#ff5f57\" />\n                <Light color=\"#febc2e\" />\n                <Light color=\"#28c840\" />\n              </>\n            )}\n            <div\n              style={{\n                flex: 1,\n                textAlign: \"center\",\n                color: \"#a1a1aa\",\n                fontSize: 12,\n                letterSpacing: \"0.02em\",\n              }}\n            >\n              {title}\n            </div>\n          </div>\n\n          {/* Code body */}\n          <div\n            style={{\n              flex: 1,\n              padding: \"20px 24px\",\n              display: \"flex\",\n              flexDirection: \"column\",\n              gap: 4,\n              fontSize,\n              lineHeight: 1.55,\n            }}\n          >\n            {lines.map((line, i) => {\n              if (typeFirstLine && i === 0) {\n                return (\n                  <Sequence key={i} from={0} layout=\"none\">\n                    <TypedCodeLine\n                      line={line}\n                      index={i}\n                      fontSize={fontSize}\n                      cps={firstLineCps}\n                      fps={fps}\n                      speed={speed}\n                    />\n                  </Sequence>\n                );\n              }\n              const baseFrom = typeFirstLine\n                ? typeDur + (i - 1) * staggerFrames\n                : i * staggerFrames;\n              return (\n                <Sequence\n                  key={i}\n                  from={Math.round(baseFrom / speed)}\n                  layout=\"none\"\n                >\n                  <CodeLine line={line} index={i} fontSize={fontSize} />\n                </Sequence>\n              );\n            })}\n          </div>\n        </div>\n      </div>\n    </div>\n  );\n}\n\nfunction Light({ color }: { color: string }) {\n  return (\n    <div\n      style={{\n        width: 12,\n        height: 12,\n        borderRadius: \"50%\",\n        background: color,\n        opacity: 0.6,\n      }}\n    />\n  );\n}\n\nfunction CodeLine({\n  line,\n  index,\n  fontSize,\n}: {\n  line: string;\n  index: number;\n  fontSize: number;\n}) {\n  const frame = useCurrentFrame();\n  const opacity = interpolate(frame, [0, 8], [0, 1], {\n    extrapolateLeft: \"clamp\",\n    extrapolateRight: \"clamp\",\n  });\n  const ty = interpolate(frame, [0, 8], [4, 0], {\n    extrapolateLeft: \"clamp\",\n    extrapolateRight: \"clamp\",\n  });\n\n  const tokens = tokenizeLine(line);\n  // Render an empty line as a half-height spacer so blank lines still take\n  // visual space without collapsing the gap.\n  if (tokens.length === 0) {\n    return <div style={{ height: fontSize * 0.8, opacity }} />;\n  }\n  return (\n    <div\n      style={{\n        opacity,\n        transform: `translateY(${ty}px)`,\n        whiteSpace: \"pre\",\n        display: \"flex\",\n        gap: 0,\n      }}\n    >\n      <span style={{ width: 28, color: \"#3f3f46\", userSelect: \"none\" }}>\n        {String(index + 1).padStart(2, \" \")}\n      </span>\n      <span>\n        {tokens.map((t, i) => (\n          <span key={i} style={{ color: TOKEN_COLORS[t.kind] }}>\n            {t.text}\n          </span>\n        ))}\n      </span>\n    </div>\n  );\n}\n\nfunction TypedCodeLine({\n  line,\n  index,\n  fontSize,\n  cps,\n  fps,\n  speed,\n}: {\n  line: string;\n  index: number;\n  fontSize: number;\n  cps: number;\n  fps: number;\n  speed: number;\n}) {\n  const frame = useCurrentFrame() * speed;\n  const len = line.length;\n  const revealed = Math.floor(\n    interpolate(frame, [0, (len / cps) * fps], [0, len], {\n      extrapolateLeft: \"clamp\",\n      extrapolateRight: \"clamp\",\n    }),\n  );\n  const visible = line.substring(0, revealed);\n  const done = revealed >= len;\n  // 2 Hz blink at any framerate.\n  const cursorOn = Math.floor((frame / fps) * 2) % 2 === 0;\n  const tokens = tokenizeLine(visible);\n\n  return (\n    <div\n      style={{\n        whiteSpace: \"pre\",\n        display: \"flex\",\n        gap: 0,\n        alignItems: \"center\",\n      }}\n    >\n      <span style={{ width: 28, color: \"#3f3f46\", userSelect: \"none\" }}>\n        {String(index + 1).padStart(2, \" \")}\n      </span>\n      <span>\n        {tokens.map((t, i) => (\n          <span key={i} style={{ color: TOKEN_COLORS[t.kind] }}>\n            {t.text}\n          </span>\n        ))}\n        {!done && cursorOn && (\n          <span\n            style={{\n              display: \"inline-block\",\n              width: fontSize * 0.55,\n              height: fontSize,\n              background: \"#e4e4e7\",\n              marginLeft: 1,\n              transform: \"translateY(2px)\",\n            }}\n          />\n        )}\n      </span>\n    </div>\n  );\n}\n\nfunction BackdropAura() {\n  // Slow, low-amplitude blob behind the glass so the backdrop blur has\n  // perceptible content to refract.\n  const frame = useCurrentFrame();\n  const t = frame / 60;\n  const x = 50 + Math.sin(t) * 20;\n  const y = 50 + Math.cos(t * 0.7) * 15;\n  return (\n    <div\n      style={{\n        position: \"absolute\",\n        inset: 0,\n        pointerEvents: \"none\",\n        background: `radial-gradient(circle at ${x}% ${y}%, rgba(56,189,248,0.22), transparent 50%), radial-gradient(circle at ${100 - x}% ${100 - y}%, rgba(168,85,247,0.18), transparent 55%)`,\n      }}\n    />\n  );\n}\n",
      "type": "registry:component",
      "target": "components/remocn/glass-code-block.tsx"
    },
    {
      "path": "src/components/remocn/input.tsx",
      "content": "\"use client\";\n\nimport { mixOklch, type RemocnTheme, useRemocnTheme } from \"@/lib/remocn-ui\";\n\nexport type InputState =\n  | \"idle\"\n  | \"hover\"\n  | \"active\"\n  | \"typing\"\n  | \"blur\"\n  | \"invalid\";\n\ntype InputSize = \"sm\" | \"default\" | \"lg\";\n\nexport interface InputProps {\n  state?: InputState;\n  style?: InputStyle;\n  placeholder?: string;\n  value?: string;\n  size?: InputSize;\n  theme?: Partial<RemocnTheme>;\n  primary?: string;\n  mode?: \"light\" | \"dark\";\n  fullWidth?: boolean;\n  className?: string;\n}\n\nconst FIELD_WIDTH = 320;\n\nconst SIZE_STYLES: Record<\n  InputSize,\n  { height: number; padding: number; fontSize: number }\n> = {\n  sm: { height: 36, padding: 12, fontSize: 13 },\n  default: { height: 40, padding: 14, fontSize: 15 },\n  lg: { height: 48, padding: 16, fontSize: 17 },\n};\n\nexport interface InputStyle {\n  borderColor: string;\n  ringColor: string;\n  ringWidth: number;\n  background: string;\n  caretOpacity: number;\n  valueReveal: number;\n  placeholderOpacity: number;\n}\n\nexport interface InputStyleContext {\n  idleBorder: string;\n  hoverBorder: string;\n  activeBorder: string;\n  invalidBorder: string;\n  ring: string;\n  invalidRing: string;\n  background: string;\n  hoverBackground: string;\n  foreground: string;\n  mutedForeground: string;\n}\n\nexport function inputStyleContext(theme: RemocnTheme): InputStyleContext {\n  return {\n    idleBorder: theme.input,\n    hoverBorder: mixOklch(theme.input, theme.foreground, 0.18),\n    activeBorder: theme.ring,\n    invalidBorder: theme.destructive,\n    ring: mixOklch(theme.background, theme.ring, 0.5),\n    invalidRing: mixOklch(theme.background, theme.destructive, 0.4),\n    background: theme.background,\n    hoverBackground: mixOklch(theme.background, theme.muted, 0.4),\n    foreground: theme.foreground,\n    mutedForeground: theme.mutedForeground,\n  };\n}\n\nexport function inputStyle(\n  state: InputState,\n  ctx: InputStyleContext,\n): InputStyle {\n  switch (state) {\n    case \"hover\":\n      return {\n        borderColor: ctx.hoverBorder,\n        ringColor: ctx.ring,\n        ringWidth: 0,\n        background: ctx.hoverBackground,\n        caretOpacity: 0,\n        valueReveal: 0,\n        placeholderOpacity: 1,\n      };\n    case \"active\":\n      return {\n        borderColor: ctx.activeBorder,\n        ringColor: ctx.ring,\n        ringWidth: 3,\n        background: ctx.background,\n        caretOpacity: 1,\n        valueReveal: 0,\n        placeholderOpacity: 1,\n      };\n    case \"typing\":\n      return {\n        borderColor: ctx.activeBorder,\n        ringColor: ctx.ring,\n        ringWidth: 3,\n        background: ctx.background,\n        caretOpacity: 1,\n        valueReveal: 1,\n        placeholderOpacity: 0,\n      };\n    case \"blur\":\n      return {\n        borderColor: ctx.idleBorder,\n        ringColor: ctx.ring,\n        ringWidth: 0,\n        background: ctx.background,\n        caretOpacity: 0,\n        valueReveal: 1,\n        placeholderOpacity: 0,\n      };\n    case \"invalid\":\n      return {\n        borderColor: ctx.invalidBorder,\n        ringColor: ctx.invalidRing,\n        ringWidth: 3,\n        background: ctx.background,\n        caretOpacity: 0,\n        valueReveal: 1,\n        placeholderOpacity: 0,\n      };\n    default:\n      return {\n        borderColor: ctx.idleBorder,\n        ringColor: ctx.ring,\n        ringWidth: 0,\n        background: ctx.background,\n        caretOpacity: 0,\n        valueReveal: 0,\n        placeholderOpacity: 1,\n      };\n  }\n}\n\nexport function Input({\n  state = \"idle\",\n  style,\n  placeholder = \"you@example.com\",\n  value = \"remotion@remocn.dev\",\n  size = \"default\",\n  theme: themeOverride,\n  primary,\n  mode,\n  fullWidth = false,\n  className,\n}: InputProps) {\n  const theme = useRemocnTheme(\n    { ...themeOverride, ...(primary ? { primary } : {}) },\n    mode,\n  );\n\n  const sizeStyle = SIZE_STYLES[size];\n  const ctx = inputStyleContext(theme);\n  const v = style ?? inputStyle(state, ctx);\n  const revealed = value.slice(0, Math.round(value.length * v.valueReveal));\n\n  return (\n    <div\n      style={{\n        position: \"absolute\",\n        inset: 0,\n        display: \"flex\",\n        alignItems: \"center\",\n        justifyContent: \"center\",\n        background: theme.background,\n        fontFamily:\n          \"var(--font-geist-sans), -apple-system, BlinkMacSystemFont, sans-serif\",\n      }}\n    >\n      <div\n        className={className}\n        style={{\n          position: \"relative\",\n          display: \"flex\",\n          alignItems: \"center\",\n          width: fullWidth ? \"100%\" : FIELD_WIDTH,\n          height: sizeStyle.height,\n          padding: `0 ${sizeStyle.padding}px`,\n          fontSize: sizeStyle.fontSize,\n          letterSpacing: \"-0.01em\",\n          background: v.background,\n          border: `1px solid ${v.borderColor}`,\n          borderRadius: theme.radius,\n          boxShadow: `0 0 0 ${v.ringWidth}px ${v.ringColor}`,\n        }}\n      >\n        {}\n        <span\n          style={{\n            position: \"absolute\",\n            left: sizeStyle.padding,\n            color: ctx.mutedForeground,\n            opacity: v.valueReveal > 0 ? 0 : v.placeholderOpacity,\n            pointerEvents: \"none\",\n            whiteSpace: \"nowrap\",\n          }}\n        >\n          {placeholder}\n        </span>\n        {}\n        <div style={{ display: \"flex\", alignItems: \"center\", minWidth: 0 }}>\n          <span style={{ whiteSpace: \"nowrap\", color: ctx.foreground }}>\n            {revealed}\n          </span>\n          <span\n            style={{\n              flexShrink: 0,\n              width: 2,\n              height: Math.round(sizeStyle.fontSize * 1.1),\n              borderRadius: 1,\n              background: ctx.foreground,\n              opacity: v.caretOpacity,\n              marginLeft: revealed.length > 0 ? 4 : 0,\n            }}\n          />\n        </div>\n      </div>\n    </div>\n  );\n}\n",
      "type": "registry:component",
      "target": "components/remocn/input.tsx"
    },
    {
      "path": "src/components/remocn/use-input-transition.ts",
      "content": "\"use client\";\n\nimport {\n  easings,\n  mixOklch,\n  type RemocnTheme,\n  type Step,\n  useRemocnTheme,\n  useStateTransition,\n} from \"@/lib/remocn-ui\";\nimport {\n  inputStyle,\n  inputStyleContext,\n  type InputState,\n  type InputStyle,\n} from \"@/components/remocn/input\";\n\nexport const DEFAULT_DURATION = 8;\n\nexport function tweenInputStyle(\n  a: InputStyle,\n  b: InputStyle,\n  t: number,\n): InputStyle {\n  return {\n    ringWidth: a.ringWidth + (b.ringWidth - a.ringWidth) * t,\n    caretOpacity: a.caretOpacity + (b.caretOpacity - a.caretOpacity) * t,\n    valueReveal: a.valueReveal + (b.valueReveal - a.valueReveal) * t,\n    placeholderOpacity:\n      a.placeholderOpacity + (b.placeholderOpacity - a.placeholderOpacity) * t,\n    borderColor: mixOklch(a.borderColor, b.borderColor, t),\n    ringColor: mixOklch(a.ringColor, b.ringColor, t),\n    background: mixOklch(a.background, b.background, t),\n  };\n}\n\nexport interface InputTransitionOptions {\n  theme?: Partial<RemocnTheme>;\n  mode?: \"light\" | \"dark\";\n  primary?: string;\n  speed?: number;\n  defaultDuration?: number;\n}\n\nexport function useInputTransition(\n  steps: Step<InputState>[],\n  opts: InputTransitionOptions = {},\n): InputStyle {\n  const {\n    theme: themeOverride,\n    mode,\n    primary,\n    speed = 1,\n    defaultDuration = DEFAULT_DURATION,\n  } = opts;\n  const theme = useRemocnTheme(\n    { ...themeOverride, ...(primary ? { primary } : {}) },\n    mode,\n  );\n  const ctx = inputStyleContext(theme);\n  const { from, to, progress } = useStateTransition(\n    steps,\n    \"idle\",\n    speed,\n    defaultDuration,\n  );\n  const t = easings.out(progress);\n  return tweenInputStyle(inputStyle(from, ctx), inputStyle(to, ctx), t);\n}\n",
      "type": "registry:component",
      "target": "components/remocn/use-input-transition.ts"
    },
    {
      "path": "src/components/remocn/x-followers-overview.tsx",
      "content": "\"use client\";\n\nimport { useState } from \"react\";\nimport { loadFont as loadSans } from \"@remotion/google-fonts/Manrope\";\nimport {\n  AbsoluteFill,\n  Img,\n  interpolate,\n  spring,\n  useCurrentFrame,\n  useVideoConfig,\n} from \"remotion\";\nimport { Confetti } from \"@/components/remocn/confetti\";\n\nexport interface FollowerNotification {\n  name: string;\n  verified: boolean;\n  /** Relative time label, e.g. \"7h\", \"1d\". */\n  time: string;\n}\n\nexport interface XFollowersOverviewProps {\n  notifications?: FollowerNotification[];\n  totalFollowers?: number;\n  handle?: string;\n  avatarUrl?: string;\n  accentColor?: string;\n  orientation?: \"horizontal\" | \"vertical\";\n  speed?: number;\n}\n\nconst { fontFamily: SANS_FAMILY } = loadSans();\nconst FONT_FAMILY = SANS_FAMILY;\n\ninterface Theme {\n  bg: string;\n  fg: string;\n  fgMuted: string;\n  border: string;\n}\n\nconst THEMES: Record<\"light\" | \"dark\", Theme> = {\n  light: {\n    bg: \"#ffffff\",\n    fg: \"#0f1419\",\n    fgMuted: \"#536471\",\n    border: \"#eff3f4\",\n  },\n  dark: {\n    bg: \"#0a0a0a\",\n    fg: \"#fff\",\n    fgMuted: \"#fff\",\n    border: \"#2f3336\",\n  },\n};\n\n/**\n * Hardcoded sample notifications so the composition renders immediately from\n * defaults. A future revision swaps this for live X API data.\n */\nexport const SAMPLE_FOLLOWERS: FollowerNotification[] = [\n  { name: \"Andre Vitorio\", verified: true, time: \"7h\" },\n  { name: \"Sarah Chen\", verified: true, time: \"7h\" },\n  { name: \"marcus\", verified: false, time: \"8h\" },\n  { name: \"Lena Powell\", verified: true, time: \"9h\" },\n  { name: \"dev_jay\", verified: false, time: \"10h\" },\n  { name: \"Priya Nair\", verified: true, time: \"11h\" },\n  { name: \"Tomás Rivera\", verified: true, time: \"13h\" },\n  { name: \"hana.eth\", verified: false, time: \"15h\" },\n  { name: \"Will Carter\", verified: true, time: \"18h\" },\n  { name: \"Yuki Tanaka\", verified: true, time: \"21h\" },\n  { name: \"benoit\", verified: false, time: \"23h\" },\n  { name: \"Amara Okafor\", verified: true, time: \"1d\" },\n  { name: \"Leo Martins\", verified: true, time: \"1d\" },\n  { name: \"sol\", verified: false, time: \"2d\" },\n];\n\n// --- Pure helpers (unit-tested) -------------------------------------------\n\n/** Map an effective frame to the active notification index + in-slot fraction. */\nexport function slotProgress(\n  fc: number,\n  slotFrames: number,\n  count: number,\n): { idx: number; frac: number } {\n  if (slotFrames <= 0 || count <= 0) return { idx: 0, frac: 0 };\n  const pos = fc / slotFrames;\n  const idx = Math.max(0, Math.min(Math.floor(pos), count - 1));\n  const frac = Math.max(0, Math.min(pos - idx, 1));\n  return { idx, frac };\n}\n\n/** Smoothstep flip ramp: rest for `hold` of the slot, then flip 0→1. */\nexport function flipEase(frac: number, hold: number): number {\n  if (hold >= 1) return 0;\n  const raw = (frac - hold) / (1 - hold);\n  const c = Math.max(0, Math.min(raw, 1));\n  return c * c * (3 - 2 * c);\n}\n\nexport function blurIn(\n  frame: number,\n  start: number,\n  end: number,\n): { blur: number; opacity: number; translateY: number } {\n  const range: [number, number] = [start, end];\n  const opts = {\n    extrapolateLeft: \"clamp\" as const,\n    extrapolateRight: \"clamp\" as const,\n  };\n  return {\n    blur: interpolate(frame, range, [10, 0], opts),\n    opacity: interpolate(frame, range, [0, 1], opts),\n    translateY: interpolate(frame, range, [12, 0], opts),\n  };\n}\n\n// --- Sub-components --------------------------------------------------------\n\nfunction VerifiedBadge({ accent, size }: { accent: string; size: number }) {\n  return (\n    <svg\n      xmlns=\"http://www.w3.org/2000/svg\"\n      viewBox=\"0 0 22 22\"\n      width={size}\n      height={size}\n      fill={accent}\n      style={{ flexShrink: 0 }}\n    >\n      <title>Verified</title>\n      <path d=\"M20.396 11c-.018-.646-.215-1.275-.57-1.816-.354-.54-.852-.972-1.438-1.246.223-.607.27-1.264.14-1.897-.131-.634-.437-1.218-.882-1.687-.47-.445-1.053-.75-1.687-.882-.633-.13-1.29-.083-1.897.14-.273-.587-.704-1.086-1.245-1.44S11.647 1.62 11 1.604c-.646.017-1.273.213-1.813.568s-.969.854-1.24 1.44c-.608-.223-1.267-.272-1.902-.14-.635.13-1.22.436-1.69.882-.445.47-.749 1.055-.878 1.688-.13.633-.08 1.29.144 1.896-.587.274-1.087.705-1.443 1.245-.356.54-.555 1.17-.574 1.817.02.647.218 1.276.574 1.817.356.54.856.972 1.443 1.245-.224.606-.274 1.263-.144 1.896.13.634.433 1.218.877 1.688.47.443 1.054.747 1.687.878.633.132 1.29.084 1.897-.136.274.586.705 1.084 1.246 1.439.54.354 1.17.551 1.816.569.647-.016 1.276-.213 1.817-.567s.972-.854 1.245-1.44c.604.239 1.266.296 1.903.164.636-.132 1.22-.447 1.68-.907.46-.46.776-1.044.908-1.681s.075-1.299-.165-1.903c.586-.274 1.084-.705 1.439-1.246.354-.54.551-1.17.569-1.816zM9.662 14.85l-3.429-3.428 1.293-1.302 2.072 2.072 4.4-4.794 1.347 1.246z\" />\n    </svg>\n  );\n}\n\nfunction Avatar({\n  avatarUrl,\n  handle,\n  size,\n  theme,\n}: {\n  avatarUrl: string;\n  handle: string;\n  size: number;\n  theme: Theme;\n}) {\n  const [errored, setErrored] = useState(false);\n  const ringStyle = {\n    width: size,\n    height: size,\n    borderRadius: 9999,\n    border: `1px solid ${theme.border}`,\n    flexShrink: 0,\n  } as const;\n\n  if (errored || !avatarUrl) {\n    return (\n      <div\n        style={{\n          ...ringStyle,\n          background: theme.border,\n          display: \"flex\",\n          alignItems: \"center\",\n          justifyContent: \"center\",\n          color: theme.fgMuted,\n          fontSize: size * 0.42,\n          fontWeight: 700,\n          fontFamily: FONT_FAMILY,\n        }}\n      >\n        {handle.charAt(0).toUpperCase()}\n      </div>\n    );\n  }\n\n  return (\n    <Img\n      src={avatarUrl}\n      crossOrigin=\"anonymous\"\n      onError={() => setErrored(true)}\n      style={{ ...ringStyle, objectFit: \"cover\" }}\n    />\n  );\n}\n\n/** Username + verified badge — the only part that flips. Absolutely overlaid\n *  and centered inside a slot sized to the active name, so the line as a whole\n *  stays centered while names swap. */\nfunction NameBlock({\n  item,\n  fontSize,\n  height,\n  theme,\n  accent,\n  deg,\n  opacity,\n}: {\n  item: FollowerNotification;\n  fontSize: number;\n  height: number;\n  theme: Theme;\n  accent: string;\n  deg: number;\n  opacity: number;\n}) {\n  return (\n    <div\n      style={{\n        position: \"absolute\",\n        left: 0,\n        right: 0,\n        top: 0,\n        height,\n        display: \"flex\",\n        alignItems: \"center\",\n        justifyContent: \"center\",\n        gap: fontSize * 0.2,\n        whiteSpace: \"nowrap\",\n        transformOrigin: \"center\",\n        backfaceVisibility: \"hidden\",\n        transform: `rotateX(${deg}deg)`,\n        opacity,\n      }}\n    >\n      <span\n        style={{\n          fontFamily: FONT_FAMILY,\n          fontSize,\n          fontWeight: 800,\n          color: theme.fg,\n          lineHeight: 1,\n        }}\n      >\n        {item.name}\n      </span>\n      {item.verified && (\n        <VerifiedBadge accent={accent} size={fontSize * 0.66} />\n      )}\n    </div>\n  );\n}\n\n// --- Main composition ------------------------------------------------------\n\nexport function XFollowersOverview({\n  notifications = SAMPLE_FOLLOWERS,\n  totalFollowers = 1709,\n  handle = \"remocn\",\n  avatarUrl = \"/logo.svg\",\n  accentColor = \"#1d9bf0\",\n  orientation = \"horizontal\",\n  speed = 1,\n}: XFollowersOverviewProps) {\n  const frame = useCurrentFrame();\n  const { durationInFrames, width, height, fps } = useVideoConfig();\n  const t = THEMES.dark;\n  const isVertical = orientation === \"vertical\";\n\n  const refW = isVertical ? 720 : 1280;\n  const refH = isVertical ? 1280 : 720;\n  const stageScale = Math.min(width / refW, height / refH);\n\n  const notifSize = isVertical ? 38 : 46;\n  const countNum = isVertical ? 92 : 110;\n  const countLabel = isVertical ? 40 : 46;\n  const perspective = isVertical ? 1000 : 1200;\n  const stageH = isVertical ? 160 : 180;\n  const lineH = notifSize * 1.4;\n  // Fast cadence: ~0.43s per username at speed 1; flip occupies the back half.\n  const SLOT = 13;\n  const HOLD = 0.45;\n\n  const fc = frame * speed;\n  const items = notifications.length > 0 ? notifications : SAMPLE_FOLLOWERS;\n  const count = items.length;\n\n  // Notifications cycle first, then the total blurs in. The reveal starts once\n  // the list is exhausted, capped so it always fits before the timeline ends.\n  const revealStart = Math.min(\n    count * SLOT,\n    Math.round(durationInFrames * 0.82),\n  );\n\n  const { idx, frac } = slotProgress(fc, SLOT, count);\n  const flipP = flipEase(frac, HOLD);\n  const hasNext = idx + 1 < count;\n\n  const current = items[idx];\n  const next = hasNext ? items[idx + 1] : undefined;\n  const activeItem = flipP >= 0.5 && next ? next : current;\n\n  const currentDeg = hasNext ? -90 * flipP : 0;\n  const currentOpacity = hasNext\n    ? interpolate(flipP, [0, 0.55], [1, 0], {\n        extrapolateLeft: \"clamp\",\n        extrapolateRight: \"clamp\",\n      })\n    : 1;\n  const nextDeg = 90 * (1 - flipP);\n  const nextOpacity = interpolate(flipP, [0.45, 1], [0, 1], {\n    extrapolateLeft: \"clamp\",\n    extrapolateRight: \"clamp\",\n  });\n\n  const notifOpacity = interpolate(\n    fc,\n    [revealStart - 4, revealStart + 8],\n    [1, 0],\n    { extrapolateLeft: \"clamp\", extrapolateRight: \"clamp\" },\n  );\n  const countIn = blurIn(fc, revealStart + 4, revealStart + 28);\n  // Dynamic pop: spring overshoot on the total as it blurs in.\n  const revealSpring = spring({\n    fps,\n    frame: fc - (revealStart + 2),\n    config: { damping: 10, stiffness: 170, mass: 0.8 },\n  });\n  const revealScale = interpolate(revealSpring, [0, 1], [0.5, 1]);\n\n  // Avatar + handle reveal in sync with the total: same window as countIn,\n  // avatar sliding in from the left, handle from the right.\n  const avatarSize = isVertical ? 72 : 64;\n  const handleSize = isVertical ? 30 : 34;\n  const avatarIn = blurIn(fc, revealStart + 4, revealStart + 28);\n  const handleIn = avatarIn;\n  const slideOpts = {\n    extrapolateLeft: \"clamp\" as const,\n    extrapolateRight: \"clamp\" as const,\n  };\n  const avatarX = interpolate(\n    fc,\n    [revealStart + 4, revealStart + 28],\n    [-36, 0],\n    slideOpts,\n  );\n  const handleX = interpolate(\n    fc,\n    [revealStart + 4, revealStart + 28],\n    [36, 0],\n    slideOpts,\n  );\n\n  // Confetti fires on the real frame the reveal begins (sync across speeds),\n  // centered on the canvas.\n  const confettiStart = Math.round((revealStart + 2) / speed);\n  const confettiColors = [\n    accentColor,\n    \"#ff5da2\",\n    \"#ffd23f\",\n    \"#22c55e\",\n    \"#a855f7\",\n  ];\n\n\n  return (\n    <AbsoluteFill style={{ background: \"transparent\" }}>\n      <div\n        style={{\n          position: \"absolute\",\n          left: \"50%\",\n          top: \"50%\",\n          width: refW,\n          height: refH,\n          transform: `translate(-50%, -50%) scale(${stageScale})`,\n        }}\n      >\n        <div\n          style={{\n            position: \"absolute\",\n            inset: 0,\n            display: \"flex\",\n            alignItems: \"center\",\n            justifyContent: \"center\",\n          }}\n        >\n          <div style={{ position: \"relative\", width: refW, height: stageH }}>\n            {/* Cycling notifications — only the username flips in 3D, the\n                \"followed you · <time>\" suffix stays put. */}\n            <div\n              style={{\n                position: \"absolute\",\n                inset: 0,\n                display: \"flex\",\n                alignItems: \"center\",\n                justifyContent: \"center\",\n                gap: notifSize * 0.28,\n                opacity: notifOpacity,\n              }}\n            >\n              <div\n                style={{\n                  position: \"relative\",\n                  height: lineH,\n                  perspective,\n                  display: \"inline-flex\",\n                }}\n              >\n                {/* Invisible spacer sizes the slot to the active name so the\n                    whole line stays centered; flips overlay it absolutely. */}\n                <div\n                  aria-hidden=\"true\"\n                  style={{\n                    visibility: \"hidden\",\n                    display: \"flex\",\n                    alignItems: \"center\",\n                    gap: notifSize * 0.2,\n                    whiteSpace: \"nowrap\",\n                    fontFamily: FONT_FAMILY,\n                    fontSize: notifSize,\n                    fontWeight: 800,\n                  }}\n                >\n                  {activeItem.name}\n                  {activeItem.verified && (\n                    <span style={{ width: notifSize * 0.66 }} />\n                  )}\n                </div>\n                <NameBlock\n                  item={current}\n                  fontSize={notifSize}\n                  height={lineH}\n                  theme={t}\n                  accent={accentColor}\n                  deg={currentDeg}\n                  opacity={currentOpacity}\n                />\n                {next && (\n                  <NameBlock\n                    item={next}\n                    fontSize={notifSize}\n                    height={lineH}\n                    theme={t}\n                    accent={accentColor}\n                    deg={nextDeg}\n                    opacity={nextOpacity}\n                  />\n                )}\n              </div>\n              <span\n                style={{\n                  fontFamily: FONT_FAMILY,\n                  fontSize: notifSize,\n                  fontWeight: 500,\n                  color: t.fgMuted,\n                  whiteSpace: \"nowrap\",\n                  lineHeight: 1,\n                }}\n              >\n                followed you · {activeItem.time}\n              </span>\n            </div>\n\n            {/* Total reveal: avatar + handle above the count */}\n            <div\n              style={{\n                position: \"absolute\",\n                inset: 0,\n                display: \"flex\",\n                flexDirection: \"column\",\n                alignItems: \"center\",\n                justifyContent: \"center\",\n                gap: isVertical ? 26 : 22,\n              }}\n            >\n              <div\n                style={{\n                  display: \"flex\",\n                  alignItems: \"center\",\n                  gap: 14,\n                }}\n              >\n                <div\n                  style={{\n                    opacity: avatarIn.opacity,\n                    filter:\n                      avatarIn.blur > 0 ? `blur(${avatarIn.blur}px)` : \"none\",\n                    transform: `translateX(${avatarX}px)`,\n                  }}\n                >\n                  <Avatar\n                    avatarUrl={avatarUrl}\n                    handle={handle}\n                    size={avatarSize}\n                    theme={t}\n                  />\n                </div>\n                <span\n                  style={{\n                    fontFamily: FONT_FAMILY,\n                    fontSize: handleSize,\n                    fontWeight: 700,\n                    color: t.fg,\n                    whiteSpace: \"nowrap\",\n                    opacity: handleIn.opacity,\n                    filter:\n                      handleIn.blur > 0 ? `blur(${handleIn.blur}px)` : \"none\",\n                    transform: `translateX(${handleX}px)`,\n                  }}\n                >\n                  @{handle}\n                </span>\n              </div>\n\n              <div\n                style={{\n                  display: \"flex\",\n                  alignItems: \"baseline\",\n                  justifyContent: \"center\",\n                  gap: 18,\n                  opacity: countIn.opacity,\n                  filter: countIn.blur > 0 ? `blur(${countIn.blur}px)` : \"none\",\n                  transform: `translateY(${countIn.translateY}px) scale(${revealScale})`,\n                }}\n              >\n                <span\n                  style={{\n                    fontFamily: FONT_FAMILY,\n                    fontSize: countNum,\n                    fontWeight: 800,\n                    color: t.fg,\n                    letterSpacing: \"-0.03em\",\n                    fontVariantNumeric: \"tabular-nums\",\n                    lineHeight: 1,\n                  }}\n                >\n                  {totalFollowers.toLocaleString(\"en-US\")}\n                </span>\n                <span\n                  style={{\n                    fontFamily: FONT_FAMILY,\n                    fontSize: countLabel,\n                    fontWeight: 500,\n                    color: t.fgMuted,\n                    lineHeight: 1,\n                  }}\n                >\n                  Followers\n                </span>\n              </div>\n            </div>\n          </div>\n        </div>\n      </div>\n\n      <Confetti\n        startFrame={confettiStart}\n        originX={0.5}\n        originY={0.5}\n        colors={confettiColors}\n        particleCount={160}\n      />\n    </AbsoluteFill>\n  );\n}\n",
      "type": "registry:component",
      "target": "components/remocn/x-followers-overview.tsx"
    },
    {
      "path": "src/demos/agent-skills/index.tsx",
      "content": "import React, { type ReactNode } from \"react\";\nimport { AbsoluteFill, Easing, Sequence, interpolate, spring, 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\";\n\nimport { RemocnUIProvider } from \"@/lib/remocn-ui\";\nimport { ClaudeCode } from \"@/components/remocn/claude-code\";\nimport { Backdrop } from \"@/components/remocn/backdrop\";\nimport { KineticCenterBuild } from \"@/components/remocn/kinetic-center-build\";\nimport { ShortSlideDown } from \"@/components/remocn/short-slide-down\";\nimport { TerminalSimulator } from \"@/components/remocn/terminal-simulator\";\nimport { GlassCodeBlock } from \"@/components/remocn/glass-code-block\";\nimport {\n  BlurIn,\n  type BlurInDirection,\n} from \"@/components/remocn/blur-in\";\nimport { useBlurInTransition } from \"@/components/remocn/use-blur-in-transition\";\nimport { Checkbox } from \"@/components/remocn/checkbox\";\nimport { useCheckboxTransition } from \"@/components/remocn/use-checkbox-transition\";\nimport { Input } from \"@/components/remocn/input\";\nimport { useInputTransition } from \"@/components/remocn/use-input-transition\";\nimport { Drawer } from \"@/components/remocn/drawer\";\nimport { useDrawerTransition } from \"@/components/remocn/use-drawer-transition\";\nimport { AlertDialog } from \"@/components/remocn/alert-dialog\";\nimport { useAlertDialogTransition } from \"@/components/remocn/use-alert-dialog-transition\";\nimport { Select } from \"@/components/remocn/select\";\nimport { useSelectTransition } from \"@/components/remocn/use-select-transition\";\nimport { Sheet } from \"@/components/remocn/sheet\";\nimport { useSheetTransition } from \"@/components/remocn/use-sheet-transition\";\nimport { GitHubStars, SAMPLE_STARGAZERS } from \"@/components/remocn/github-stars\";\nimport { XFollowersOverview } from \"@/components/remocn/x-followers-overview\";\n\n// Bind the fonts the remocn components read from CSS variables.\nconst { fontFamily: SANS } = loadSans(\"normal\", {\n  subsets: [\"latin\"],\n  weights: [\"400\", \"600\", \"700\", \"800\"],\n});\nconst { fontFamily: MONO } = loadMono(\"normal\", {\n  subsets: [\"latin\"],\n  weights: [\"400\", \"700\"],\n});\n\n// ---------------------------------------------------------------------------\n// Palette — remocn dark with the Claude terracotta as the through-line tying\n// the \"request\" frame to the video it produces.\n// ---------------------------------------------------------------------------\nconst ACCENT = \"#D97757\";\nconst INK = \"#FAFAFA\";\nconst PAGE = \"#0B0B0C\";\n\n// ---------------------------------------------------------------------------\n// Scene timings (frames @ 30fps). Transitions overlap consecutive scenes.\n// ---------------------------------------------------------------------------\nconst S_PROMPT = 292; // Claude Code: type request → think → \"produce\" the video\nconst S_TERMINAL = 82; // zoomed terminal typing the install command\nconst S_CODE = 134; // glass code block — scan top→bottom, pull back, then hold\nconst S_DIV = 58; // text divider between product blocks\nconst S_SHOWCASE = 228; // six component examples, blur-in transitions\nconst S_AI = 110; // \"works with your AI\" — stacked avatar group\nconst S_GH = 130; // github-stars fly-through\nconst S_XF = 300; // x-followers overview — trimmed to exit just after the total settles (no dead air)\nconst S_OUTRO = 120; // closing wordmark + install pill (from the typography demo)\n\nconst T_HANDOFF = 24; // CC → video: \"render complete, here it is\"\nconst T_FADE = 16;\nconst T_BF = 16; // blur-fade between the back-half scenes\nconst T_RISE = 18;\n\nexport const AGENT_SKILLS_DURATION =\n  S_PROMPT +\n  S_TERMINAL +\n  S_CODE +\n  S_DIV +\n  S_SHOWCASE +\n  S_AI +\n  S_DIV +\n  S_GH +\n  S_DIV +\n  S_XF +\n  S_OUTRO -\n  (T_HANDOFF + T_FADE + 7 * T_BF + T_RISE);\n\n// ===========================================================================\n// Shared helpers\n// ===========================================================================\n\n// ===========================================================================\n// Scene 1 — the request: Claude Code types the skill command, then thinks and\n// \"produces\" the video. The thinking phase is the new feature being demoed.\n// ===========================================================================\nconst PromptScene: React.FC = () => (\n  <AbsoluteFill style={{ background: \"transparent\" }}>\n    <ClaudeCode\n      title=\"Claude Code v2.0.0\"\n      userName=\"you\"\n      model=\"Opus 4.8 • Max\"\n      cwd=\"~/code/remocn-demo\"\n      placeholder='Try \"/remocn ...\"'\n      prompt=\"/remocn make a greate demo video for my product\"\n      accentColor={ACCENT}\n      thinking\n      thinkingVerbs={[\n        \"Thinking\",\n        \"Reading the brief\",\n        \"Composing scenes\",\n        \"Animating\",\n        \"Rendering\",\n      ]}\n      thinkingActivity={[\n        \"Loading skill: remocn\",\n        \"Reading components catalog — 70+ components\",\n        \"Composing scenes — title, install, showcase\",\n        \"Wiring kinetic transitions @ 1280×720 · 30fps\",\n        \"✓ Rendered demo.mp4\",\n      ]}\n      thinkingLineStagger={24}\n    />\n  </AbsoluteFill>\n);\n\n// ===========================================================================\n// Scene 2 — install: a zoomed terminal typing the shadcn command. The camera\n// is pinned to the typing cursor — it starts on the left of the line and dollies\n// right as each character appears.\n// ===========================================================================\nconst INSTALL_CMD = \"npx shadcn add @remocn/terminal-simulator\";\n\nconst TerminalZoomScene: React.FC = () => {\n  const frame = useCurrentFrame();\n\n  // Terminal geometry (matches terminal-simulator's internal 900×480 window,\n  // centred on the 1280×720 stage).\n  const TERM_FONT = 20;\n  const CHAR_W = TERM_FONT * 0.6; // monospace advance ≈ 0.6em\n  const WIN_LEFT = (1280 - 900) / 2; // 190\n  const WIN_TOP = (720 - 480) / 2; // 120\n  const TEXT_START_X = WIN_LEFT + 20 + (CHAR_W + 8); // content pad + \"$ \"\n  const LINE_HEIGHT = Math.round(TERM_FONT * 1.6); // 32\n  const CURSOR_Y = WIN_TOP + 40 + 20 + LINE_HEIGHT / 2; // chrome + pad + half line\n  const LINE_START = 10; // terminal-simulator's first line begins at frame 10\n  const CHARS_PER_FRAME = 1;\n\n  // Camera: maximum zoom, cursor pinned to a fixed screen point.\n  const Z = 2.8;\n  const TARGET_X = 640; // screen X the cursor rides on\n  const TARGET_Y = 360;\n\n  const revealed = interpolate(\n    frame,\n    [LINE_START, LINE_START + INSTALL_CMD.length / CHARS_PER_FRAME],\n    [0, INSTALL_CMD.length],\n    { extrapolateLeft: \"clamp\", extrapolateRight: \"clamp\" },\n  );\n  const cursorX = TEXT_START_X + revealed * CHAR_W;\n  const tx = TARGET_X - Z * cursorX;\n  const ty = TARGET_Y - Z * CURSOR_Y;\n\n  return (\n    <AbsoluteFill style={{ background: PAGE, overflow: \"hidden\" }}>\n      <div\n        style={{\n          position: \"absolute\",\n          left: 0,\n          top: 0,\n          width: 1280,\n          height: 720,\n          transform: `translate(${tx}px, ${ty}px) scale(${Z})`,\n          transformOrigin: \"0 0\",\n        }}\n      >\n        <TerminalSimulator\n          lines={[{ text: INSTALL_CMD, type: \"command\", delay: 0 }]}\n          prompt=\"$\"\n          title=\"~/code/remocn-demo\"\n          fontSize={TERM_FONT}\n          charsPerFrame={CHARS_PER_FRAME}\n          chunkSize={1}\n        />\n      </div>\n    </AbsoluteFill>\n  );\n};\n\n// ===========================================================================\n// Scene 3b — the component's code in a glass block (no gradient aura behind it,\n// so the photographic backdrop refracts through the glass instead).\n// ===========================================================================\nconst COMPONENT_CODE = `import { TerminalSimulator } from \"@/components/remocn\";\n\nexport function BuildScene() {\n  return (\n    <TerminalSimulator\n      lines={[{ text: \"npm run build\", type: \"command\" }]}\n    />\n  );\n}`;\n\nconst CodeScene: React.FC = () => {\n  const frame = useCurrentFrame();\n\n  const codeLines = COMPONENT_CODE.split(\"\\n\");\n  const n = codeLines.length;\n  const STAGGER = 10; // frames between each line's reveal\n  const PULL = 24; // zoom-out duration\n\n  // Glass-block geometry on the 1280×720 stage (880×420, centred).\n  const BLOCK_LEFT = (1280 - 880) / 2; // 200\n  const BLOCK_TOP = (720 - 420) / 2; // 150\n  const BODY_TOP = BLOCK_TOP + 1 + 40 + 20; // ring + chrome + body padding\n  const LINE_FS = 18;\n  const GAP = 4;\n  const lineH = (l: string) =>\n    l.trim() === \"\" ? LINE_FS * 0.8 : LINE_FS * 1.55;\n\n  // Per-line vertical centres, accounting for the short blank-line spacer.\n  const lineCenters: number[] = [];\n  let yy = BODY_TOP;\n  for (const l of codeLines) {\n    lineCenters.push(yy + lineH(l) / 2);\n    yy += lineH(l) + GAP;\n  }\n\n  const ANCHOR_WORLD_X = BLOCK_LEFT + 25; // start of the line-number gutter\n  const ANCHOR_SCREEN_X = 110;\n  const TARGET_Y = 360;\n  const Z_IN = 2.6;\n\n  // Camera scans top→bottom, tracking the line currently revealing, and holds\n  // on the second-to-last line (index n-2).\n  const lp = interpolate(frame, [0, (n - 2) * STAGGER], [0, n - 2], {\n    extrapolateLeft: \"clamp\",\n    extrapolateRight: \"clamp\",\n  });\n  const i0 = Math.floor(lp);\n  const i1 = Math.min(i0 + 1, n - 1);\n  const currentLineY =\n    lineCenters[i0] + (lineCenters[i1] - lineCenters[i0]) * (lp - i0);\n\n  const pinnedX = ANCHOR_SCREEN_X - Z_IN * ANCHOR_WORLD_X;\n  const pinnedY = TARGET_Y - Z_IN * currentLineY;\n\n  // On the second-to-last line, pull the zoom back to the standard centred view.\n  const qPull = interpolate(\n    frame,\n    [(n - 2) * STAGGER, (n - 2) * STAGGER + PULL],\n    [0, 1],\n    {\n      extrapolateLeft: \"clamp\",\n      extrapolateRight: \"clamp\",\n      easing: Easing.inOut(Easing.cubic),\n    },\n  );\n  const scale = Z_IN + qPull * (1 - Z_IN);\n  const tx = (1 - qPull) * pinnedX;\n  const ty = (1 - qPull) * pinnedY;\n\n  return (\n    <AbsoluteFill style={{ background: \"transparent\" }}>\n      <AbsoluteFill\n        style={{\n          opacity: qPull,\n          background:\n            \"radial-gradient(120% 120% at 50% 55%, rgba(11,11,12,0.2) 0%, rgba(11,11,12,0.62) 100%)\",\n        }}\n      />\n      <div\n        style={{\n          position: \"absolute\",\n          left: 0,\n          top: 0,\n          width: 1280,\n          height: 720,\n          transform: `translate(${tx}px, ${ty}px) scale(${scale})`,\n          transformOrigin: \"0 0\",\n        }}\n      >\n        <GlassCodeBlock\n          code={COMPONENT_CODE}\n          title=\"terminal-simulator.tsx\"\n          width={880}\n          height={420}\n          fontSize={LINE_FS}\n          staggerFrames={STAGGER}\n          aura={false}\n        />\n      </div>\n     \n    </AbsoluteFill>\n  );\n};\n\n// ===========================================================================\n// Divider scenes — white text between the product blocks (no accent colour),\n// alternating kinetic-center-build and short-slide-down.\n// ===========================================================================\nconst DividerScrim: React.FC = () => (\n  <AbsoluteFill\n    style={{\n      background:\n        \"radial-gradient(120% 120% at 50% 50%, rgba(11,11,12,0.25) 0%, rgba(11,11,12,0.72) 100%)\",\n    }}\n  />\n);\n\nconst useDividerFadeOut = (): number => {\n  const frame = useCurrentFrame();\n  return interpolate(frame, [S_DIV - 10, S_DIV], [1, 0], {\n    extrapolateLeft: \"clamp\",\n    extrapolateRight: \"clamp\",\n  });\n};\n\nconst DividerKinetic: React.FC<{ text: string }> = ({ text }) => (\n  <AbsoluteFill style={{ background: \"transparent\", opacity: useDividerFadeOut() }}>\n    <DividerScrim />\n    <KineticCenterBuild\n      text={text}\n      fontSize={92}\n      color={INK}\n      fontWeight={800}\n      entryOffset={70}\n      speed={1.25}\n    />\n  </AbsoluteFill>\n);\n\nconst DividerSlide: React.FC<{ text: string }> = ({ text }) => (\n  <AbsoluteFill style={{ background: \"transparent\", opacity: useDividerFadeOut() }}>\n    <DividerScrim />\n    <ShortSlideDown\n      text={text}\n      fontSize={84}\n      color={INK}\n      fontWeight={800}\n      speed={1.2}\n    />\n  </AbsoluteFill>\n);\n\n// ===========================================================================\n// Showcase — six component examples on a light \"app card\", each revealed with a\n// dynamic blur-in (no captions). The blur-in carries the transition.\n// ===========================================================================\nconst SHOW_STEP = 36; // frames between each component's entrance\nconst SHOW_PER = 48; // each component's on-screen window\n\nconst AppCard: React.FC<{ children: ReactNode }> = ({ children }) => (\n  <div\n    style={{\n      position: \"relative\",\n      width: 800,\n      height: 480,\n      borderRadius: 20,\n      overflow: \"hidden\",\n      background: \"#ffffff\",\n      boxShadow: \"0 50px 120px rgba(0,0,0,0.55)\",\n    }}\n  >\n    {children}\n  </div>\n);\n\nconst BlurItem: React.FC<{ dir: BlurInDirection; children: ReactNode }> = ({\n  dir,\n  children,\n}) => {\n  const style = useBlurInTransition(\n    [\n      { at: 2, state: \"revealed\", duration: 14 },\n      { at: SHOW_PER - 16, state: \"hidden\", duration: 14 },\n    ],\n    { direction: dir, distance: 44, blur: 16 },\n  );\n  return (\n    <AbsoluteFill style={{ alignItems: \"center\", justifyContent: \"center\" }}>\n      <BlurIn style={style} display=\"block\">\n        {children}\n      </BlurIn>\n    </AbsoluteFill>\n  );\n};\n\n// Each example drives its own state animation via its transition hook (the\n// checkbox checks, the input types, the drawer/sheet/dialog/select open), and\n// the whole card blurs in/out around it.\nconst OPEN_AT = 6;\nconst OPEN_DUR = 18;\n\nconst ShowCheckbox: React.FC = () => {\n  const style = useCheckboxTransition([\n    { at: 0, state: \"unchecked\" },\n    { at: 10, state: \"checked\", duration: 16 },\n  ]);\n  return (\n    <AppCard>\n      <div style={{ position: \"absolute\", inset: 0, transform: \"scale(3.4)\" }}>\n        <Checkbox style={style} state=\"checked\" size=\"lg\" />\n      </div>\n    </AppCard>\n  );\n};\n\nconst ShowInput: React.FC = () => {\n  const frame = useCurrentFrame();\n  const full = \"hello@remocn.dev\";\n  const revealed = Math.floor(\n    interpolate(frame, [OPEN_AT, OPEN_AT + 22], [0, full.length], {\n      extrapolateLeft: \"clamp\",\n      extrapolateRight: \"clamp\",\n    }),\n  );\n  const style = useInputTransition([\n    { at: 0, state: \"idle\" },\n    { at: 3, state: \"active\", duration: 6 },\n    { at: 9, state: \"typing\", duration: 8 },\n  ]);\n  return (\n    <AppCard>\n      <div\n        style={{\n          position: \"absolute\",\n          inset: 0,\n          display: \"flex\",\n          alignItems: \"center\",\n          justifyContent: \"center\",\n        }}\n      >\n        <div style={{ position: \"relative\", width: \"50%\", height: 80 }}>\n          <Input\n            style={style}\n            state=\"typing\"\n            value={full.substring(0, revealed)}\n            placeholder=\"Email\"\n            size=\"lg\"\n            fullWidth\n          />\n        </div>\n      </div>\n    </AppCard>\n  );\n};\n\nconst ShowDrawer: React.FC = () => {\n  const style = useDrawerTransition([\n    { at: 0, state: \"closed\" },\n    { at: OPEN_AT, state: \"opened\", duration: OPEN_DUR },\n  ]);\n  return (\n    <AppCard>\n      <Drawer style={style} state=\"opened\" />\n    </AppCard>\n  );\n};\n\nconst ShowAlertDialog: React.FC = () => {\n  const style = useAlertDialogTransition([\n    { at: 0, state: \"closed\" },\n    { at: OPEN_AT, state: \"opened\", duration: OPEN_DUR },\n  ]);\n  return (\n    <AppCard>\n      <AlertDialog style={style} state=\"opened\" />\n    </AppCard>\n  );\n};\n\nconst ShowSelect: React.FC = () => {\n  const style = useSelectTransition([\n    { at: 0, state: \"closed\" },\n    { at: OPEN_AT, state: \"opened\", duration: OPEN_DUR },\n  ]);\n  return (\n    <AppCard>\n      <Select\n        style={style}\n        state=\"opened\"\n        label=\"Theme\"\n        items={[\"Light\", \"Dark\", \"System\"]}\n        selectedIndex={0}\n        highlightedIndex={1}\n      />\n    </AppCard>\n  );\n};\n\nconst ShowSheet: React.FC = () => {\n  const style = useSheetTransition([\n    { at: 0, state: \"closed\" },\n    { at: OPEN_AT, state: \"opened\", duration: OPEN_DUR },\n  ]);\n  return (\n    <AppCard>\n      <Sheet style={style} state=\"opened\" />\n    </AppCard>\n  );\n};\n\nconst SHOW_ITEMS: { Comp: React.FC; dir: BlurInDirection }[] = [\n  { Comp: ShowCheckbox, dir: \"up\" },\n  { Comp: ShowInput, dir: \"right\" },\n  { Comp: ShowDrawer, dir: \"down\" },\n  { Comp: ShowAlertDialog, dir: \"left\" },\n  { Comp: ShowSelect, dir: \"up\" },\n  { Comp: ShowSheet, dir: \"right\" },\n];\n\nconst ShowcaseScene: React.FC = () => (\n  <AbsoluteFill style={{ background: \"transparent\" }}>\n    {SHOW_ITEMS.map(({ Comp, dir }, i) => (\n      <Sequence key={i} from={i * SHOW_STEP} durationInFrames={SHOW_PER}>\n        <BlurItem dir={dir}>\n          <Comp />\n        </BlurItem>\n      </Sequence>\n    ))}\n  </AbsoluteFill>\n);\n\n// ===========================================================================\n// Works with your AI — a stacked avatar group of AI-tool marks, staggered in.\n// ===========================================================================\nconst ClaudeMark: React.FC<{ size: number }> = ({ size }) => (\n  <svg width={size} height={size} preserveAspectRatio=\"xMidYMid\" viewBox=\"0 0 256 257\">\n    <path\n      fill=\"#D97757\"\n      d=\"m50.228 170.321 50.357-28.257.843-2.463-.843-1.361h-2.462l-8.426-.518-28.775-.778-24.952-1.037-24.175-1.296-6.092-1.297L0 125.796l.583-3.759 5.12-3.434 7.324.648 16.202 1.101 24.304 1.685 17.629 1.037 26.118 2.722h4.148l.583-1.685-1.426-1.037-1.101-1.037-25.147-17.045-27.22-18.017-14.258-10.37-7.713-5.25-3.888-4.925-1.685-10.758 7-7.713 9.397.649 2.398.648 9.527 7.323 20.35 15.75L94.817 91.9l3.889 3.24 1.555-1.102.195-.777-1.75-2.917-14.453-26.118-15.425-26.572-6.87-11.018-1.814-6.61c-.648-2.723-1.102-4.991-1.102-7.778l7.972-10.823L71.42 0 82.05 1.426l4.472 3.888 6.61 15.101 10.694 23.786 16.591 32.34 4.861 9.592 2.592 8.879.973 2.722h1.685v-1.556l1.36-18.211 2.528-22.36 2.463-28.776.843-8.1 4.018-9.722 7.971-5.25 6.222 2.981 5.12 7.324-.713 4.73-3.046 19.768-5.962 30.98-3.889 20.739h2.268l2.593-2.593 10.499-13.934 17.628-22.036 7.778-8.749 9.073-9.657 5.833-4.601h11.018l8.1 12.055-3.628 12.443-11.342 14.388-9.398 12.184-13.48 18.147-8.426 14.518.778 1.166 2.01-.194 30.46-6.481 16.462-2.982 19.637-3.37 8.88 4.148.971 4.213-3.5 8.62-20.998 5.184-24.628 4.926-36.682 8.685-.454.324.519.648 16.526 1.555 7.065.389h17.304l32.21 2.398 8.426 5.574 5.055 6.805-.843 5.184-12.962 6.611-17.498-4.148-40.83-9.721-14-3.5h-1.944v1.167l11.666 11.406 21.387 19.314 26.767 24.887 1.36 6.157-3.434 4.86-3.63-.518-23.526-17.693-9.073-7.972-20.545-17.304h-1.36v1.814l4.73 6.935 25.017 37.59 1.296 11.536-1.814 3.76-6.481 2.268-7.13-1.297-14.647-20.544-15.1-23.138-12.185-20.739-1.49.843-7.194 77.448-3.37 3.953-7.778 2.981-6.48-4.925-3.436-7.972 3.435-15.749 4.148-20.544 3.37-16.333 3.046-20.285 1.815-6.74-.13-.454-1.49.194-15.295 20.999-23.267 31.433-18.406 19.702-4.407 1.75-7.648-3.954.713-7.064 4.277-6.286 25.47-32.405 15.36-20.092 9.917-11.6-.065-1.686h-.583L44.07 198.125l-12.055 1.555-5.185-4.86.648-7.972 2.463-2.593 20.35-13.999-.064.065Z\"\n    />\n  </svg>\n);\n\nconst CodexMark: React.FC<{ size: number }> = ({ size }) => (\n  <svg\n    width={size}\n    height={size}\n    fill=\"#fff\"\n    fillRule=\"evenodd\"\n    viewBox=\"0 0 24 24\"\n  >\n    <path\n      clipRule=\"evenodd\"\n      d=\"M8.086.457a6.105 6.105 0 013.046-.415c1.333.153 2.521.72 3.564 1.7a.117.117 0 00.107.029c1.408-.346 2.762-.224 4.061.366l.063.03.154.076c1.357.703 2.33 1.77 2.918 3.198.278.679.418 1.388.421 2.126a5.655 5.655 0 01-.18 1.631.167.167 0 00.04.155 5.982 5.982 0 011.578 2.891c.385 1.901-.01 3.615-1.183 5.14l-.182.22a6.063 6.063 0 01-2.934 1.851.162.162 0 00-.108.102c-.255.736-.511 1.364-.987 1.992-1.199 1.582-2.962 2.462-4.948 2.451-1.583-.008-2.986-.587-4.21-1.736a.145.145 0 00-.14-.032c-.518.167-1.04.191-1.604.185a5.924 5.924 0 01-2.595-.622 6.058 6.058 0 01-2.146-1.781c-.203-.269-.404-.522-.551-.821a7.74 7.74 0 01-.495-1.283 6.11 6.11 0 01-.017-3.064.166.166 0 00.008-.074.115.115 0 00-.037-.064 5.958 5.958 0 01-1.38-2.202 5.196 5.196 0 01-.333-1.589 6.915 6.915 0 01.188-2.132c.45-1.484 1.309-2.648 2.577-3.493.282-.188.55-.334.802-.438.286-.12.573-.22.861-.304a.129.129 0 00.087-.087A6.016 6.016 0 015.635 2.31C6.315 1.464 7.132.846 8.086.457zm-.804 7.85a.848.848 0 00-1.473.842l1.694 2.965-1.688 2.848a.849.849 0 001.46.864l1.94-3.272a.849.849 0 00.007-.854l-1.94-3.393zm5.446 6.24a.849.849 0 000 1.695h4.848a.849.849 0 000-1.696h-4.848z\"\n    />\n  </svg>\n);\n\nconst CursorMark: React.FC<{ size: number }> = ({ size }) => (\n  <svg width={size} height={size} fill=\"#fff\" viewBox=\"0 0 466.73 532.09\">\n    <path d=\"M457.43,125.94L244.42,2.96c-6.84-3.95-15.28-3.95-22.12,0L9.3,125.94c-5.75,3.32-9.3,9.46-9.3,16.11v247.99c0,6.65,3.55,12.79,9.3,16.11l213.01,122.98c6.84,3.95,15.28,3.95,22.12,0l213.01-122.98c5.75-3.32,9.3-9.46,9.3-16.11v-247.99c0-6.65-3.55-12.79-9.3-16.11h-.01ZM444.05,151.99l-205.63,356.16c-1.39,2.4-5.06,1.42-5.06-1.36v-233.21c0-4.66-2.49-8.97-6.53-11.31L24.87,145.67c-2.4-1.39-1.42-5.06,1.36-5.06h411.26c5.84,0,9.49,6.33,6.57,11.39h-.01Z\" />\n  </svg>\n);\n\nconst GrokMark: React.FC<{ size: number }> = ({ size }) => (\n  <svg width={size} height={size} fill=\"none\" viewBox=\"0 0 1024 1024\">\n    <path\n      fill=\"#fff\"\n      d=\"M395.479 633.828L735.91 381.105C752.599 368.715 776.454 373.548 784.406 392.792C826.26 494.285 807.561 616.253 724.288 699.996C641.016 783.739 525.151 802.104 419.247 760.277L303.556 814.143C469.49 928.202 670.987 899.995 796.901 773.282C896.776 672.843 927.708 535.937 898.785 412.476L899.047 412.739C857.105 231.37 909.358 158.874 1016.4 10.6326C1018.93 7.11771 1021.47 3.60279 1024 0L883.144 141.651V141.212L395.392 633.916\"\n    />\n    <path\n      fill=\"#fff\"\n      d=\"M325.226 695.251C206.128 580.84 226.662 403.776 328.285 301.668C403.431 226.097 526.549 195.254 634.026 240.596L749.454 186.994C728.657 171.88 702.007 155.623 671.424 144.2C533.19 86.9942 367.693 115.465 255.323 228.382C147.234 337.081 113.244 504.215 171.613 646.833C215.216 753.423 143.739 828.818 71.7385 904.916C46.2237 931.893 20.6216 958.87 0 987.429L325.139 695.339\"\n    />\n  </svg>\n);\n\nconst AI_TOOLS: { Mark: React.FC<{ size: number }>; bg: string }[] = [\n  { Mark: ClaudeMark, bg: \"#F0EEE6\" },\n  { Mark: CodexMark, bg: \"#0A0A0A\" },\n  { Mark: CursorMark, bg: \"#0A0A0A\" },\n  { Mark: GrokMark, bg: \"#0A0A0A\" },\n];\n\nconst AVATAR_D = 118; // avatar diameter\nconst AVATAR_OVERLAP = 38; // px the avatars tuck under each other\nconst AVATAR_STAGGER = 7; // frames between each avatar springing in\n\nconst AIToolsScene: React.FC = () => {\n  const frame = useCurrentFrame();\n  const { fps } = useVideoConfig();\n  const heading = interpolate(frame, [AI_TOOLS.length * AVATAR_STAGGER + 6, AI_TOOLS.length * AVATAR_STAGGER + 24], [0, 1], {\n    extrapolateLeft: \"clamp\",\n    extrapolateRight: \"clamp\",\n    easing: Easing.out(Easing.cubic),\n  });\n\n  return (\n    <AbsoluteFill\n      style={{ alignItems: \"center\", justifyContent: \"center\", background: \"transparent\" }}\n    >\n      <AbsoluteFill\n        style={{\n          background:\n            \"radial-gradient(120% 120% at 50% 50%, rgba(11,11,12,0.3) 0%, rgba(11,11,12,0.78) 100%)\",\n        }}\n      />\n      <div\n        style={{\n          display: \"flex\",\n          flexDirection: \"column\",\n          alignItems: \"center\",\n          gap: 44,\n        }}\n      >\n        {/* Stacked avatar group */}\n        <div style={{ display: \"flex\", flexDirection: \"row\" }}>\n          {AI_TOOLS.map(({ Mark, bg }, i) => {\n            const s = spring({\n              fps,\n              frame: frame - i * AVATAR_STAGGER,\n              config: { damping: 13, stiffness: 130, mass: 0.8 },\n            });\n            const scale = interpolate(s, [0, 1], [0.5, 1]);\n            const translateY = interpolate(s, [0, 1], [22, 0]);\n            return (\n              <div\n                key={i}\n                style={{\n                  width: AVATAR_D,\n                  height: AVATAR_D,\n                  borderRadius: \"50%\",\n                  marginLeft: i === 0 ? 0 : -AVATAR_OVERLAP,\n                  zIndex: AI_TOOLS.length - i,\n                  background: bg,\n                  border: \"5px solid #0B0B0C\",\n                  boxShadow: \"0 20px 48px rgba(0,0,0,0.5)\",\n                  display: \"flex\",\n                  alignItems: \"center\",\n                  justifyContent: \"center\",\n                  opacity: s,\n                  transform: `translateY(${translateY}px) scale(${scale})`,\n                }}\n              >\n                <Mark size={AVATAR_D * 0.52} />\n              </div>\n            );\n          })}\n        </div>\n\n        {/* Caption */}\n        <div\n          style={{\n            opacity: heading,\n            transform: `translateY(${(1 - heading) * 12}px)`,\n            fontFamily: `${SANS}, sans-serif`,\n            fontSize: 46,\n            fontWeight: 700,\n            color: INK,\n            letterSpacing: \"-0.01em\",\n          }}\n        >\n          Works with your AI\n        </div>\n      </div>\n    </AbsoluteFill>\n  );\n};\n\n// ===========================================================================\n// GitHub stars — fly-through of the repo's stargazers locking on the total.\n// ===========================================================================\nconst GitHubStarsScene: React.FC = () => (\n  <AbsoluteFill style={{ background: PAGE }}>\n    <GitHubStars\n      repo=\"kapishdima/remocn\"\n      totalStars={688}\n      repoAvatarUrl=\"https://avatars.githubusercontent.com/u/23422228?v=4\"\n      stargazers={SAMPLE_STARGAZERS}\n      orientation=\"horizontal\"\n      theme=\"dark\"\n      accentColor={\"#fff\"}\n    />\n  </AbsoluteFill>\n);\n\n// ===========================================================================\n// X followers — notification cycle into the total follower count.\n// ===========================================================================\nconst XFollowersScene: React.FC = () => (\n  <AbsoluteFill style={{ background: PAGE }}>\n    <XFollowersOverview avatarUrl=\"https://pbs.twimg.com/profile_images/2028105659359674368/VrsS1zcJ_400x400.jpg\"/>\n  </AbsoluteFill>\n);\n\n// ===========================================================================\n// Scene 6 — outro: closing wordmark + install pill, ported from the typography\n// demo. The whole group lifts away and blurs out at the very end.\n// ===========================================================================\nconst CopyIcon: React.FC<{ size: number }> = ({ size }) => (\n  <svg\n    width={size}\n    height={size}\n    viewBox=\"0 0 24 24\"\n    fill=\"none\"\n    stroke=\"currentColor\"\n    strokeWidth={2}\n    strokeLinecap=\"round\"\n    strokeLinejoin=\"round\"\n  >\n    <rect width={14} height={14} x={8} y={8} rx={2} ry={2} />\n    <path d=\"M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2\" />\n  </svg>\n);\n\nconst CheckIcon: React.FC<{ size: number }> = ({ size }) => (\n  <svg\n    width={size}\n    height={size}\n    viewBox=\"0 0 24 24\"\n    fill=\"none\"\n    stroke=\"currentColor\"\n    strokeWidth={2.4}\n    strokeLinecap=\"round\"\n    strokeLinejoin=\"round\"\n  >\n    <path d=\"M20 6 9 17l-5-5\" />\n  </svg>\n);\n\nconst InstallPill: React.FC<{\n  command: string;\n  delay: number;\n  copyAt: number;\n}> = ({ command, delay, copyAt }) => {\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\n  return (\n    <div\n      style={{\n        display: \"inline-flex\",\n        alignItems: \"center\",\n        gap: 13,\n        height: 52,\n        padding: \"0 22px\",\n        borderRadius: 999,\n        border: `1px solid rgba(255,255,255,${copied ? 0.22 : 0.14})`,\n        background: \"rgba(255,255,255,0.05)\",\n        backdropFilter: \"blur(10px)\",\n        WebkitBackdropFilter: \"blur(10px)\",\n        boxShadow: \"0 12px 34px rgba(0,0,0,0.3)\",\n        fontFamily: `${MONO}, ui-monospace, SFMono-Regular, Menlo, monospace`,\n        fontSize: 18,\n        opacity: enter,\n        translate: `0px ${(1 - enter) * 12}px`,\n      }}\n    >\n      <span style={{ color: \"rgba(250,250,250,0.4)\" }}>$</span>\n      <span style={{ color: INK }}>{command}</span>\n      <span\n        style={{\n          display: \"inline-flex\",\n          marginLeft: 3,\n          color: copied ? \"#4ade80\" : \"rgba(250,250,250,0.55)\",\n          scale: copied ? `${pop}` : \"1\",\n        }}\n      >\n        {copied ? <CheckIcon size={17} /> : <CopyIcon size={17} />}\n      </span>\n    </div>\n  );\n};\n\nconst OutroScene: React.FC = () => {\n  const frame = useCurrentFrame();\n\n  const word = interpolate(frame, [6, 30], [0, 1], {\n    extrapolateLeft: \"clamp\",\n    extrapolateRight: \"clamp\",\n    easing: Easing.bezier(0.22, 1, 0.36, 1),\n  });\n\n  // The whole closing group lifts away and blurs out at the very end.\n  const exit = interpolate(frame, [S_OUTRO - 18, S_OUTRO], [0, 1], {\n    extrapolateLeft: \"clamp\",\n    extrapolateRight: \"clamp\",\n    easing: Easing.in(Easing.cubic),\n  });\n\n  return (\n    <AbsoluteFill\n      style={{\n        alignItems: \"center\",\n        justifyContent: \"center\",\n        opacity: 1 - exit,\n        translate: `0px ${-exit * 60}px`,\n        filter: exit > 0.001 ? `blur(${exit * 16}px)` : undefined,\n      }}\n    >\n      <AbsoluteFill\n        style={{\n          background:\n            \"radial-gradient(120% 120% at 50% 48%, rgba(11,11,12,0.25) 0%, rgba(11,11,12,0.8) 100%)\",\n        }}\n      />\n      <div\n        style={{\n          display: \"flex\",\n          flexDirection: \"column\",\n          alignItems: \"center\",\n          gap: 30,\n        }}\n      >\n        <span\n          style={{\n            fontFamily: `${SANS}, sans-serif`,\n            fontSize: 96,\n            fontWeight: 600,\n            color: INK,\n            lineHeight: 1,\n            opacity: word,\n            filter: word < 1 ? `blur(${(1 - word) * 12}px)` : undefined,\n            translate: `0px ${(1 - word) * 10}px`,\n          }}\n        >\n          remocn\n        </span>\n\n        <InstallPill\n          command=\"npx shadcn add @remocn/terminal-simulator\"\n          delay={52}\n          copyAt={86}\n        />\n      </div>\n    </AbsoluteFill>\n  );\n};\n\n// ===========================================================================\n// Transition: CC → video. The request frame scales up + blurs away while the\n// rendered video arrives from slightly behind — \"here's the video I made\".\n// ===========================================================================\nconst HandoffPresentation: React.FC<\n  TransitionPresentationComponentProps<Record<string, never>>\n> = ({ children, presentationProgress, presentationDirection }) => {\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\n  const style: React.CSSProperties = entering\n    ? {\n        opacity: p,\n        transform: `scale(${interpolate(p, [0, 1], [0.92, 1])})`,\n        filter: p < 1 ? `blur(${(1 - p) * 14}px)` : undefined,\n      }\n    : {\n        opacity: 1 - p,\n        transform: `scale(${interpolate(p, [0, 1], [1, 1.08])})`,\n        filter: p > 0 ? `blur(${p * 16}px)` : undefined,\n      };\n\n  return <AbsoluteFill style={style}>{children}</AbsoluteFill>;\n};\n\nconst handoff = (): TransitionPresentation<Record<string, never>> => ({\n  component: HandoffPresentation,\n  props: {},\n});\n\n// Closing rise: outro descends, un-blurs and settles; outgoing dissolves.\nconst RisePresentation: React.FC<\n  TransitionPresentationComponentProps<Record<string, never>>\n> = ({ children, presentationProgress, presentationDirection }) => {\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        filter: p < 1 ? `blur(${(1 - p) * 14}px)` : undefined,\n        transform: `translateY(${-(1 - p) * 70}px)`,\n      }\n    : {\n        opacity: 1 - p,\n        filter: p > 0 ? `blur(${p * 14}px)` : undefined,\n      };\n  return <AbsoluteFill style={style}>{children}</AbsoluteFill>;\n};\n\nconst rise = (): TransitionPresentation<Record<string, never>> => ({\n  component: RisePresentation,\n  props: {},\n});\n\n// Blur-fade between the back-half scenes — dynamic, in keeping with the\n// component blur-ins.\nconst BlurFadePresentation: React.FC<\n  TransitionPresentationComponentProps<Record<string, never>>\n> = ({ children, presentationProgress, presentationDirection }) => {\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    ? { opacity: p, filter: p < 1 ? `blur(${(1 - p) * 16}px)` : undefined }\n    : { opacity: 1 - p, filter: p > 0 ? `blur(${p * 16}px)` : undefined };\n  return <AbsoluteFill style={style}>{children}</AbsoluteFill>;\n};\nconst blurFade = (): TransitionPresentation<Record<string, never>> => ({\n  component: BlurFadePresentation,\n  props: {},\n});\n\n// Fade-THROUGH (not a crossfade): the outgoing scene fades out fully before the\n// incoming one fades in, so two zoomed scenes never overlap as a double-exposure.\nconst FadeThroughPresentation: React.FC<\n  TransitionPresentationComponentProps<Record<string, never>>\n> = ({ children, presentationProgress, presentationDirection }) => {\n  const entering = presentationDirection === \"entering\";\n  const opacity = entering\n    ? interpolate(presentationProgress, [0.5, 1], [0, 1], {\n        extrapolateLeft: \"clamp\",\n        extrapolateRight: \"clamp\",\n        easing: Easing.out(Easing.cubic),\n      })\n    : interpolate(presentationProgress, [0, 0.5], [1, 0], {\n        extrapolateLeft: \"clamp\",\n        extrapolateRight: \"clamp\",\n        easing: Easing.in(Easing.cubic),\n      });\n  return <AbsoluteFill style={{ opacity }}>{children}</AbsoluteFill>;\n};\nconst fadeThrough = (): TransitionPresentation<Record<string, never>> => ({\n  component: FadeThroughPresentation,\n  props: {},\n});\n\n// ===========================================================================\n// Composition root\n// ===========================================================================\nexport const AgentSkillsDemo: React.FC = () => {\n  return (\n    <RemocnUIProvider>\n      <AbsoluteFill\n        style={\n          {\n            background: PAGE,\n            \"--font-geist-sans\": SANS,\n            \"--font-geist-mono\": MONO,\n          } as React.CSSProperties\n        }\n      >\n        {/* Persistent photographic backdrop behind every scene. */}\n        <Backdrop fill={{ type: \"image\", src: demoAsset(\"bg.png\") }} />\n\n        <TransitionSeries>\n          <TransitionSeries.Sequence durationInFrames={S_PROMPT}>\n            <PromptScene />\n          </TransitionSeries.Sequence>\n\n          <TransitionSeries.Transition\n            timing={linearTiming({ durationInFrames: T_HANDOFF })}\n            presentation={handoff()}\n          />\n\n          <TransitionSeries.Sequence durationInFrames={S_TERMINAL}>\n            <TerminalZoomScene />\n          </TransitionSeries.Sequence>\n\n          <TransitionSeries.Transition\n            timing={linearTiming({ durationInFrames: T_FADE })}\n            presentation={fadeThrough()}\n          />\n\n          <TransitionSeries.Sequence durationInFrames={S_CODE}>\n            <CodeScene />\n          </TransitionSeries.Sequence>\n\n          <TransitionSeries.Transition\n            timing={linearTiming({ durationInFrames: T_BF })}\n            presentation={blurFade()}\n          />\n\n          <TransitionSeries.Sequence durationInFrames={S_DIV}>\n            <DividerKinetic text=\"Components\" />\n          </TransitionSeries.Sequence>\n\n          <TransitionSeries.Transition\n            timing={linearTiming({ durationInFrames: T_BF })}\n            presentation={blurFade()}\n          />\n\n          <TransitionSeries.Sequence durationInFrames={S_SHOWCASE}>\n            <ShowcaseScene />\n          </TransitionSeries.Sequence>\n\n          <TransitionSeries.Transition\n            timing={linearTiming({ durationInFrames: T_BF })}\n            presentation={blurFade()}\n          />\n\n          <TransitionSeries.Sequence durationInFrames={S_AI}>\n            <AIToolsScene />\n          </TransitionSeries.Sequence>\n\n          <TransitionSeries.Transition\n            timing={linearTiming({ durationInFrames: T_BF })}\n            presentation={blurFade()}\n          />\n\n          <TransitionSeries.Sequence durationInFrames={S_DIV}>\n            <DividerSlide text=\"Loved on GitHub\" />\n          </TransitionSeries.Sequence>\n\n          <TransitionSeries.Transition\n            timing={linearTiming({ durationInFrames: T_BF })}\n            presentation={blurFade()}\n          />\n\n          <TransitionSeries.Sequence durationInFrames={S_GH}>\n            <GitHubStarsScene />\n          </TransitionSeries.Sequence>\n\n          <TransitionSeries.Transition\n            timing={linearTiming({ durationInFrames: T_BF })}\n            presentation={blurFade()}\n          />\n\n          <TransitionSeries.Sequence durationInFrames={S_DIV}>\n            <DividerKinetic text=\"Join the community\" />\n          </TransitionSeries.Sequence>\n\n          <TransitionSeries.Transition\n            timing={linearTiming({ durationInFrames: T_BF })}\n            presentation={blurFade()}\n          />\n\n          <TransitionSeries.Sequence durationInFrames={S_XF}>\n            <XFollowersScene />\n          </TransitionSeries.Sequence>\n\n          <TransitionSeries.Transition\n            timing={linearTiming({ durationInFrames: T_RISE })}\n            presentation={rise()}\n          />\n\n          <TransitionSeries.Sequence durationInFrames={S_OUTRO}>\n            <OutroScene />\n          </TransitionSeries.Sequence>\n        </TransitionSeries>\n      </AbsoluteFill>\n    </RemocnUIProvider>\n  );\n};\n",
      "type": "registry:component",
      "target": "demos/agent-skills/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/agent-skills/prompt.md",
      "content": "<!-- TODO(draft): placeholder written by AI — replace with the real prompt used to generate this video -->\n\nMake a meta demo video about the remocn agent skill itself — the pitch is basically \"you ask Claude Code to make a video and it does.\" Show it as if Claude Code just received a prompt like \"/remocn make a great demo video\", briefly working through the skill, and then producing a polished remocn showcase as the result: a zoomed-in terminal typing the install command, the component's source getting scanned top to bottom in a glass code block, then a handful of component examples blurring in one after another — checkbox, input, drawer, alert-dialog, select, sheet. Add a GitHub stars beat and a quick X followers overview, use plain white text dividers between sections, and close on the remocn wordmark. Build the whole thing with remocn components.\n",
      "type": "registry:file",
      "target": "demos/agent-skills/prompt.md"
    }
  ],
  "docs": "Register the composition in your Remotion Root:\n\n  import { AgentSkillsDemo } from \"@/demos/agent-skills\";\n  <Composition id=\"agent-skills\" component={AgentSkillsDemo} durationInFrames={1400} fps={30} width={1280} height={720} />\n\nRequires Tailwind v4 wired into Remotion (@remotion/tailwind-v4).\nRender locally: npx remotion render agent-skills out/agent-skills.mp4",
  "type": "registry:block"
}