{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "shadcn-ui",
  "title": "shadcn/ui — Not a library. Your code.",
  "description": "remocn demo composition \"shadcn/ui — Not a library. Your code.\" — installs the full Remotion composition. Generated with AI from the prompt in demos/shadcn-ui/prompt.md.",
  "dependencies": [
    "@remotion/google-fonts",
    "@remotion/transitions",
    "class-variance-authority",
    "culori",
    "lucide-react",
    "remotion"
  ],
  "registryDependencies": [
    "https://remocn.dev/r/blur-in.json",
    "https://remocn.dev/r/remocn-ui.json",
    "https://remocn.dev/r/rolling-number.json",
    "https://remocn.dev/r/shared-axis-z.json",
    "https://remocn.dev/r/terminal-simulator.json"
  ],
  "files": [
    {
      "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/demos/_ui/video-scope.tsx",
      "content": "import React from \"react\";\n\n// Pinned copy of the shadcn zinc tokens the demo videos were authored against.\n// Rendered as an inline <style> so it works identically in the Next.js site,\n// the Remotion CLI bundle, and any project a demo is installed into via the\n// registry — regardless of what that project's own shadcn theme looks like.\n// Apply className=\"video-scope\" on the composition root next to this tag.\nconst CSS = `\n.video-scope.video-scope {\n  --radius: 0.625rem;\n  --background: oklch(1 0 0);\n  --foreground: oklch(0.145 0 0);\n  --card: oklch(1 0 0);\n  --card-foreground: oklch(0.145 0 0);\n  --popover: oklch(1 0 0);\n  --popover-foreground: oklch(0.145 0 0);\n  --primary: oklch(0.205 0 0);\n  --primary-foreground: oklch(0.985 0 0);\n  --secondary: oklch(0.97 0 0);\n  --secondary-foreground: oklch(0.205 0 0);\n  --muted: oklch(0.97 0 0);\n  --muted-foreground: oklch(0.556 0 0);\n  --accent: oklch(0.97 0 0);\n  --accent-foreground: oklch(0.205 0 0);\n  --destructive: oklch(0.577 0.245 27.325);\n  --border: oklch(0.922 0 0);\n  --input: oklch(0.922 0 0);\n  --ring: oklch(0.708 0 0);\n  --chart-1: oklch(0.87 0 0);\n  --chart-2: oklch(0.556 0 0);\n  --chart-3: oklch(0.439 0 0);\n  --chart-4: oklch(0.371 0 0);\n  --chart-5: oklch(0.269 0 0);\n  --sidebar: oklch(0.985 0 0);\n  --sidebar-foreground: oklch(0.145 0 0);\n  --sidebar-primary: oklch(0.205 0 0);\n  --sidebar-primary-foreground: oklch(0.985 0 0);\n  --sidebar-accent: oklch(0.97 0 0);\n  --sidebar-accent-foreground: oklch(0.205 0 0);\n  --sidebar-border: oklch(0.922 0 0);\n  --sidebar-ring: oklch(0.708 0 0);\n  --font-sans: ui-sans-serif, system-ui, sans-serif, \"Apple Color Emoji\", \"Segoe UI Emoji\", \"Segoe UI Symbol\", \"Noto Color Emoji\";\n  --font-mono: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, \"Liberation Mono\", \"Courier New\", monospace;\n  font-family: var(--font-sans);\n}\n.video-scope .dark,\n.video-scope.dark.video-scope {\n  --background: oklch(0.145 0 0);\n  --foreground: oklch(0.985 0 0);\n  --card: oklch(0.205 0 0);\n  --card-foreground: oklch(0.985 0 0);\n  --popover: oklch(0.205 0 0);\n  --popover-foreground: oklch(0.985 0 0);\n  --primary: oklch(0.922 0 0);\n  --primary-foreground: oklch(0.205 0 0);\n  --secondary: oklch(0.269 0 0);\n  --secondary-foreground: oklch(0.985 0 0);\n  --muted: oklch(0.269 0 0);\n  --muted-foreground: oklch(0.708 0 0);\n  --accent: oklch(0.269 0 0);\n  --accent-foreground: oklch(0.985 0 0);\n  --destructive: oklch(0.704 0.191 22.216);\n  --border: oklch(1 0 0 / 10%);\n  --input: oklch(1 0 0 / 15%);\n  --ring: oklch(0.556 0 0);\n  --chart-1: oklch(0.87 0 0);\n  --chart-2: oklch(0.556 0 0);\n  --chart-3: oklch(0.439 0 0);\n  --chart-4: oklch(0.371 0 0);\n  --chart-5: oklch(0.269 0 0);\n  --sidebar: oklch(0.205 0 0);\n  --sidebar-foreground: oklch(0.985 0 0);\n  --sidebar-primary: oklch(0.488 0.243 264.376);\n  --sidebar-primary-foreground: oklch(0.985 0 0);\n  --sidebar-accent: oklch(0.269 0 0);\n  --sidebar-accent-foreground: oklch(0.985 0 0);\n  --sidebar-border: oklch(1 0 0 / 10%);\n  --sidebar-ring: oklch(0.556 0 0);\n}\n/* CSS transitions/animations run on wall clock, not Remotion's frame clock:\n   a hard theme/var flip starts a real-time transition and the frame screenshot\n   catches it mid-flight (nondeterministic across frames and render workers).\n   All motion in demos is frame-driven via interpolate, so kill them outright. */\n.video-scope *,\n.video-scope *::before,\n.video-scope *::after {\n  transition: none !important;\n  animation: none !important;\n}\n`;\n\nexport const VideoScopeStyle: React.FC = () => <style>{CSS}</style>;\n",
      "type": "registry:component",
      "target": "demos/_ui/video-scope.tsx"
    },
    {
      "path": "src/demos/shadcn-ui/index.tsx",
      "content": "import React, { type ReactNode } from \"react\";\nimport {\n  AbsoluteFill,\n  Easing,\n  Series,\n  interpolate,\n  interpolateColors,\n  spring,\n  useCurrentFrame,\n  useVideoConfig,\n} from \"remotion\";\nimport {\n  TransitionSeries,\n  linearTiming,\n  type TransitionPresentation,\n  type TransitionPresentationComponentProps,\n} from \"@remotion/transitions\";\nimport { loadFont as loadSans } from \"@remotion/google-fonts/Geist\";\nimport { loadFont as loadMono } from \"@remotion/google-fonts/GeistMono\";\n\nimport { RemocnUIProvider } from \"@/lib/remocn-ui\";\nimport { VideoScopeStyle } from \"@/demos/_ui/video-scope\";\nimport { SharedAxisZ } from \"@/components/remocn/shared-axis-z\";\nimport { TerminalSimulator } from \"@/components/remocn/terminal-simulator\";\nimport { GlassCodeBlock } from \"@/components/remocn/glass-code-block\";\nimport { RollingNumber } from \"@/components/remocn/rolling-number\";\nimport { BlurIn } from \"@/components/remocn/blur-in\";\nimport { useBlurInTransition } from \"@/components/remocn/use-blur-in-transition\";\nimport { CheckIcon, CopyIcon } from \"lucide-react\";\n\n// Bind shadcn's real typefaces to the CSS variables the remocn components read.\nconst { fontFamily: SANS_FAMILY } = loadSans(\"normal\", {\n  subsets: [\"latin\"],\n  weights: [\"400\", \"500\", \"600\", \"700\"],\n});\nconst { fontFamily: MONO_FAMILY } = loadMono(\"normal\", {\n  subsets: [\"latin\"],\n  weights: [\"400\", \"500\", \"700\"],\n});\n\nconst SANS =\n  \"var(--font-geist-sans), -apple-system, BlinkMacSystemFont, sans-serif\";\nconst MONO = \"var(--font-geist-mono), ui-monospace, SFMono-Regular, monospace\";\n\n// shadcn/ui palette — monochrome zinc, white ink, hairline borders. No accent\n// color: the brand IS the restraint. The marker highlight is white-on-black.\nconst BG = \"#09090b\"; //     zinc-950 canvas\nconst INK = \"#fafafa\"; //    zinc-50 text\nconst MUTED = \"#a1a1aa\"; //  zinc-400\nconst FAINT = \"rgba(250,250,250,0.45)\";\nconst BORDER = \"rgba(255,255,255,0.10)\";\nconst CARD = \"rgba(255,255,255,0.03)\";\n\n// ---------------------------------------------------------------------------\n// Scene timings (frames @ 30fps), one per beat. Transitions overlap.\n// ---------------------------------------------------------------------------\nconst S_HOOK = 130; //    ticker-takeover — library? package? → your code\nconst S_PAIN = 95; //     kinetic pain lines\nconst S_INTRO = 90; //    Open Source. Open Code.\nconst S_INSTALL = 125; // terminal — npx shadcn add button\nconst S_CODE = 135; //    glass code block — it's yours now\nconst S_BENEFIT = 120; // four pillar cards\nconst S_DISTRIB = 115; // ship your own — @acme registry\nconst S_PROOF = 130; //   frameworks + rolling stats\nconst S_CTA = 100; //     wordmark + init pill\n\nconst T_X = 14; //    crossfade\nconst T_ZOOM = 18; // section turn (zoom-through)\n\nexport const SHADCN_DURATION =\n  S_HOOK +\n  S_PAIN +\n  S_INTRO +\n  S_INSTALL +\n  S_CODE +\n  S_BENEFIT +\n  S_DISTRIB +\n  S_PROOF +\n  S_CTA -\n  (T_X + T_ZOOM + T_ZOOM + T_X + T_ZOOM + T_X + T_ZOOM + T_ZOOM);\n\n// ---------------------------------------------------------------------------\n// Reveal — blur-in wrapper driven by useBlurInTransition.\n// ---------------------------------------------------------------------------\nconst Reveal: React.FC<{\n  children: ReactNode;\n  delay?: number;\n  distance?: number;\n  blur?: number;\n  duration?: number;\n  display?: React.CSSProperties[\"display\"];\n}> = ({\n  children,\n  delay = 0,\n  distance = 16,\n  blur = 10,\n  duration = 20,\n  display = \"block\",\n}) => {\n  const style = useBlurInTransition(\n    [{ at: delay, state: \"revealed\", duration }],\n    { direction: \"up\", distance, blur },\n  );\n  return (\n    <BlurIn style={style} display={display}>\n      {children}\n    </BlurIn>\n  );\n};\n\n// ---------------------------------------------------------------------------\n// Mark — white marker swipe behind a phrase, text flips to black. Pure shadcn.\n// ---------------------------------------------------------------------------\nconst Mark: React.FC<{ children: string; startFrame?: number }> = ({\n  children,\n  startFrame = 8,\n}) => {\n  const frame = useCurrentFrame();\n  const { fps } = useVideoConfig();\n  const scaleX = spring({\n    frame: frame - startFrame,\n    fps,\n    config: { damping: 15, mass: 0.8 },\n  });\n  const textColor = interpolateColors(\n    interpolate(scaleX, [0.45, 0.85], [0, 1], {\n      extrapolateLeft: \"clamp\",\n      extrapolateRight: \"clamp\",\n    }),\n    [0, 1],\n    [INK, \"#09090b\"],\n  );\n  return (\n    <span\n      style={{\n        position: \"relative\",\n        display: \"inline-block\",\n        whiteSpace: \"nowrap\",\n      }}\n    >\n      <span\n        aria-hidden\n        style={{\n          position: \"absolute\",\n          inset: \"-0.04em -0.14em\",\n          background: INK,\n          borderRadius: 6,\n          transformOrigin: \"left center\",\n          transform: `scaleX(${scaleX})`,\n          zIndex: 0,\n        }}\n      />\n      <span style={{ position: \"relative\", zIndex: 1, color: textColor }}>\n        {children}\n      </span>\n    </span>\n  );\n};\n\n// ===========================================================================\n// Scene 1 — Hook. Ticker-takeover: two labels cycle through one slot, then the\n// \"No.\" crashes in and resolves to \"It's your code.\"\n// ===========================================================================\nconst Q1 = \"A component library?\";\nconst Q2 = \"A package you install?\";\n\nconst PayoffScene: React.FC = () => (\n  <AbsoluteFill\n    style={{\n      alignItems: \"center\",\n      justifyContent: \"center\",\n      flexDirection: \"column\",\n      gap: 10,\n    }}\n  >\n    <Reveal delay={0} distance={10} blur={6} duration={10}>\n      <span\n        style={{\n          fontFamily: SANS,\n          fontWeight: 500,\n          fontSize: 30,\n          color: MUTED,\n        }}\n      >\n        No.\n      </span>\n    </Reveal>\n    <Reveal delay={6} distance={26} blur={16} duration={20}>\n      <h1\n        style={{\n          margin: 0,\n          fontFamily: SANS,\n          fontWeight: 600,\n          fontSize: 76,\n          letterSpacing: \"-0.03em\",\n          color: INK,\n          textAlign: \"center\",\n        }}\n      >\n        It&apos;s <Mark startFrame={22}>your code</Mark>.\n      </h1>\n    </Reveal>\n  </AbsoluteFill>\n);\n\nconst HookScene: React.FC = () => (\n  <AbsoluteFill style={{ padding: \"0 90px\" }}>\n    <Series>\n      <Series.Sequence durationInFrames={40} layout=\"none\">\n        <SharedAxisZ\n          fromText=\"\"\n          toText={Q1}\n          fontSize={52}\n          fontWeight={600}\n          color={INK}\n        />\n      </Series.Sequence>\n      <Series.Sequence durationInFrames={42} layout=\"none\">\n        <SharedAxisZ\n          fromText={Q1}\n          toText={Q2}\n          fontSize={52}\n          fontWeight={600}\n          color={INK}\n        />\n      </Series.Sequence>\n      <Series.Sequence durationInFrames={48} layout=\"none\">\n        <PayoffScene />\n      </Series.Sequence>\n    </Series>\n  </AbsoluteFill>\n);\n\n// ===========================================================================\n// Scene 2 — Pain. Four short lines land solo, the last one emphasized.\n// ===========================================================================\nconst PAIN_LINES = [\n  \"You install the library.\",\n  \"You fight its styles.\",\n  \"You override its CSS.\",\n];\n\nconst PainScene: React.FC = () => (\n  <AbsoluteFill\n    style={{\n      alignItems: \"center\",\n      justifyContent: \"center\",\n      flexDirection: \"column\",\n      gap: 14,\n    }}\n  >\n    {PAIN_LINES.map((line, i) => (\n      <Reveal key={line} delay={4 + i * 14} distance={14} blur={9}>\n        <span\n          style={{\n            fontFamily: SANS,\n            fontWeight: 500,\n            fontSize: 38,\n            color: MUTED,\n          }}\n        >\n          {line}\n        </span>\n      </Reveal>\n    ))}\n    <Reveal delay={4 + PAIN_LINES.length * 14 + 6} distance={18} blur={12}>\n      <span\n        style={{\n          fontFamily: SANS,\n          fontWeight: 600,\n          fontSize: 44,\n          color: INK,\n        }}\n      >\n        And it&apos;s still not yours.\n      </span>\n    </Reveal>\n  </AbsoluteFill>\n);\n\n// ===========================================================================\n// Scene 3 — Product intro. Introducing → shadcn/ui → Open Source. Open Code.\n// ===========================================================================\nconst IntroScene: React.FC = () => (\n  <AbsoluteFill\n    style={{\n      alignItems: \"center\",\n      justifyContent: \"center\",\n      flexDirection: \"column\",\n      gap: 18,\n    }}\n  >\n    <Reveal delay={2} distance={12} blur={8} duration={16}>\n      <span\n        style={{\n          fontFamily: SANS,\n          fontWeight: 500,\n          fontSize: 24,\n          letterSpacing: \"0.01em\",\n          color: MUTED,\n        }}\n      >\n        Introducing\n      </span>\n    </Reveal>\n    <Reveal delay={10} distance={24} blur={16} duration={22}>\n      <h1\n        style={{\n          margin: 0,\n          fontFamily: MONO,\n          fontWeight: 600,\n          fontSize: 96,\n          letterSpacing: \"-0.04em\",\n          color: INK,\n        }}\n      >\n        shadcn/ui\n      </h1>\n    </Reveal>\n    <Reveal delay={26} distance={16} blur={10}>\n      <span\n        style={{\n          fontFamily: SANS,\n          fontWeight: 500,\n          fontSize: 30,\n          color: FAINT,\n        }}\n      >\n        Open Source.{\" \"}\n        <span style={{ color: INK, fontWeight: 600 }}>Open Code.</span>\n      </span>\n    </Reveal>\n  </AbsoluteFill>\n);\n\n// ===========================================================================\n// Scene 4 — Install. A real `add` lands the code in the project.\n// ===========================================================================\nconst InstallScene: React.FC = () => (\n  <AbsoluteFill style={{ alignItems: \"center\", justifyContent: \"center\" }}>\n    <div style={{ width: 860 }}>\n      <TerminalSimulator\n        title=\"~/my-app\"\n        fontSize={20}\n        charsPerFrame={2}\n        chunkSize={2}\n        lines={[\n          { text: \"npx shadcn@latest add button\", type: \"command\", delay: 0 },\n          { text: \"✓ Checking registry...\", type: \"log\", delay: 12, pause: 10 },\n          {\n            text: \"✓ Created components/ui/button.tsx\",\n            type: \"success\",\n            delay: 8,\n          },\n          { text: \"It's in your project now.\", type: \"log\", delay: 12 },\n        ]}\n      />\n    </div>\n  </AbsoluteFill>\n);\n\n// ===========================================================================\n// Scene 5 — It's yours now. The button file opens; it's plain React + Tailwind.\n// ===========================================================================\nconst BUTTON_CODE = `import { cva } from \"class-variance-authority\";\n\nconst buttonVariants = cva(\n  \"inline-flex items-center justify-center rounded-md\",\n  {\n    variants: {\n      variant: {\n        default: \"bg-primary text-primary-foreground\",\n        outline: \"border border-input bg-background\",\n        ghost: \"hover:bg-accent\",\n      },\n      size: {\n        default: \"h-9 px-4 py-2\",\n        sm: \"h-8 px-3\",\n        lg: \"h-10 px-6\",\n      },\n    },\n  },\n);`;\n\nconst CodeScene: React.FC = () => (\n  <AbsoluteFill\n    style={{\n      alignItems: \"center\",\n      justifyContent: \"center\",\n      flexDirection: \"column\",\n      gap: 26,\n    }}\n  >\n    <Reveal delay={2} distance={16} blur={10}>\n      <h2\n        style={{\n          margin: 0,\n          fontFamily: SANS,\n          fontWeight: 600,\n          fontSize: 38,\n          color: INK,\n        }}\n      >\n        Change anything. It&apos;s <Mark startFrame={14}>yours</Mark>.\n      </h2>\n    </Reveal>\n    <Reveal delay={8} distance={40} blur={14} duration={22}>\n      <div style={{ position: \"relative\", width: 720, height: 430 }}>\n        <GlassCodeBlock\n          code={BUTTON_CODE}\n          title=\"components/ui/button.tsx\"\n          width={720}\n          height={430}\n          fontSize={15}\n          aura={false}\n          staggerFrames={2}\n        />\n      </div>\n    </Reveal>\n  </AbsoluteFill>\n);\n\n// ===========================================================================\n// Scene 6 — Built right by default. Four pillar cards self-assemble.\n// ===========================================================================\nconst PillarCard: React.FC<{\n  title: string;\n  desc: string;\n  delay: number;\n}> = ({ title, desc, delay }) => (\n  <Reveal delay={delay} distance={30} blur={12} duration={20}>\n    <div\n      style={{\n        width: 300,\n        display: \"flex\",\n        flexDirection: \"column\",\n        gap: 8,\n        padding: \"24px 26px\",\n        borderRadius: 14,\n        background: CARD,\n        border: `1px solid ${BORDER}`,\n      }}\n    >\n      <span\n        style={{\n          fontFamily: SANS,\n          fontWeight: 600,\n          fontSize: 24,\n          color: INK,\n        }}\n      >\n        {title}\n      </span>\n      <span style={{ fontFamily: SANS, fontSize: 17, color: MUTED }}>\n        {desc}\n      </span>\n    </div>\n  </Reveal>\n);\n\nconst BenefitScene: React.FC = () => (\n  <AbsoluteFill\n    style={{\n      alignItems: \"center\",\n      justifyContent: \"center\",\n      flexDirection: \"column\",\n      gap: 34,\n    }}\n  >\n    <Reveal delay={2} distance={16} blur={10}>\n      <h2\n        style={{\n          margin: 0,\n          fontFamily: SANS,\n          fontWeight: 600,\n          fontSize: 40,\n          color: INK,\n        }}\n      >\n        Built right by default\n      </h2>\n    </Reveal>\n    <div\n      style={{\n        display: \"grid\",\n        gridTemplateColumns: \"300px 300px\",\n        gap: 22,\n      }}\n    >\n      <PillarCard title=\"Open Code\" desc=\"Edit any line you want.\" delay={8} />\n      <PillarCard\n        title=\"Composable\"\n        desc=\"One shared interface.\"\n        delay={13}\n      />\n      <PillarCard\n        title=\"Themeable\"\n        desc=\"CSS variables, dark mode.\"\n        delay={18}\n      />\n      <PillarCard\n        title=\"AI-Ready\"\n        desc=\"Open for your agent to read.\"\n        delay={23}\n      />\n    </div>\n  </AbsoluteFill>\n);\n\n// ===========================================================================\n// Scene 7 — Ship your own. The same CLI distributes your team's components.\n// ===========================================================================\nconst NsPill: React.FC<{ label: string; delay: number }> = ({\n  label,\n  delay,\n}) => (\n  <Reveal delay={delay} distance={14} blur={9} display=\"inline-block\">\n    <span\n      style={{\n        display: \"inline-flex\",\n        alignItems: \"center\",\n        height: 40,\n        padding: \"0 18px\",\n        borderRadius: 999,\n        border: `1px solid ${BORDER}`,\n        background: CARD,\n        fontFamily: MONO,\n        fontSize: 17,\n        color: INK,\n      }}\n    >\n      {label}\n    </span>\n  </Reveal>\n);\n\nconst DistribScene: React.FC = () => (\n  <AbsoluteFill\n    style={{\n      alignItems: \"center\",\n      justifyContent: \"center\",\n      flexDirection: \"column\",\n      gap: 30,\n    }}\n  >\n    <Reveal delay={2} distance={16} blur={10}>\n      <h2\n        style={{\n          margin: 0,\n          fontFamily: SANS,\n          fontWeight: 600,\n          fontSize: 40,\n          color: INK,\n        }}\n      >\n        Then ship your own\n      </h2>\n    </Reveal>\n    <Reveal delay={10} distance={18} blur={12} duration={20}>\n      <div\n        style={{\n          display: \"inline-flex\",\n          alignItems: \"center\",\n          gap: 12,\n          height: 56,\n          padding: \"0 24px\",\n          borderRadius: 12,\n          border: `1px solid ${BORDER}`,\n          background: CARD,\n          fontFamily: MONO,\n          fontSize: 20,\n        }}\n      >\n        <span style={{ color: FAINT }}>$</span>\n        <span style={{ color: INK }}>npx shadcn add @acme/card</span>\n      </div>\n    </Reveal>\n    <div style={{ display: \"flex\", gap: 14 }}>\n      <NsPill label=\"@acme/card\" delay={22} />\n      <NsPill label=\"@acme/chart\" delay={27} />\n      <NsPill label=\"@acme/auth\" delay={32} />\n    </div>\n    <Reveal delay={40} distance={12} blur={8}>\n      <span style={{ fontFamily: SANS, fontSize: 19, color: MUTED }}>\n        One registry for your whole team.\n      </span>\n    </Reveal>\n  </AbsoluteFill>\n);\n\n// ===========================================================================\n// Scene 8 — Everyone's building on it. Frameworks + the proof numbers.\n// ===========================================================================\nconst Stat: React.FC<{ to: number; label: string; delayPlus: number }> = ({\n  to,\n  label,\n  delayPlus,\n}) => (\n  <div\n    style={{\n      display: \"flex\",\n      flexDirection: \"column\",\n      alignItems: \"center\",\n      gap: 6,\n    }}\n  >\n    <div style={{ display: \"flex\", alignItems: \"flex-start\" }}>\n      <div style={{ position: \"relative\", width: 300, height: 78 }}>\n        <RollingNumber from={0} to={to} fontSize={64} color={INK} />\n      </div>\n      <Reveal delay={delayPlus} distance={8} blur={6}>\n        <span\n          style={{\n            fontFamily: MONO,\n            fontWeight: 700,\n            fontSize: 64,\n            color: INK,\n            lineHeight: \"78px\",\n          }}\n        >\n          +\n        </span>\n      </Reveal>\n    </div>\n    <span style={{ fontFamily: SANS, fontSize: 18, color: MUTED }}>\n      {label}\n    </span>\n  </div>\n);\n\nconst FRAMEWORKS = [\"Next.js\", \"Vite\", \"Remix\", \"Astro\", \"Laravel\"];\n\nconst ProofScene: React.FC = () => (\n  <AbsoluteFill\n    style={{\n      alignItems: \"center\",\n      justifyContent: \"center\",\n      flexDirection: \"column\",\n      gap: 30,\n    }}\n  >\n    <Reveal delay={2} distance={16} blur={10}>\n      <h2\n        style={{\n          margin: 0,\n          fontFamily: SANS,\n          fontWeight: 600,\n          fontSize: 40,\n          color: INK,\n        }}\n      >\n        Everyone&apos;s building on it\n      </h2>\n    </Reveal>\n    <div style={{ display: \"flex\", gap: 12 }}>\n      {FRAMEWORKS.map((fw, i) => (\n        <NsPill key={fw} label={fw} delay={8 + i * 4} />\n      ))}\n    </div>\n    <div style={{ display: \"flex\", gap: 80, marginTop: 8 }}>\n      <Stat to={100000} label=\"GitHub stars\" delayPlus={70} />\n      <Stat to={20000} label=\"projects building\" delayPlus={70} />\n    </div>\n  </AbsoluteFill>\n);\n\n// ===========================================================================\n// Scene 9 — CTA. The mark condenses straight into the one command you run.\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  return (\n    <div\n      style={{\n        display: \"inline-flex\",\n        alignItems: \"center\",\n        gap: 13,\n        height: 54,\n        padding: \"0 22px\",\n        borderRadius: 999,\n        border: `1px solid ${copied ? \"rgba(255,255,255,0.22)\" : BORDER}`,\n        background: CARD,\n        fontFamily: MONO,\n        fontSize: 19,\n        opacity: enter,\n        transform: `translateY(${(1 - enter) * 12}px)`,\n      }}\n    >\n      <span style={{ color: FAINT }}>$</span>\n      <span style={{ color: INK }}>{command}</span>\n      <span\n        style={{\n          display: \"inline-flex\",\n          marginLeft: 3,\n          color: copied ? \"#4ade80\" : FAINT,\n          transform: copied ? `scale(${pop})` : \"scale(1)\",\n        }}\n      >\n        {copied ? <CheckIcon size={18} /> : <CopyIcon size={18} />}\n      </span>\n    </div>\n  );\n};\n\nconst CtaScene: React.FC = () => (\n  <AbsoluteFill style={{ alignItems: \"center\", justifyContent: \"center\" }}>\n    <div\n      style={{\n        display: \"flex\",\n        flexDirection: \"column\",\n        alignItems: \"center\",\n        gap: 22,\n      }}\n    >\n      <Reveal delay={2} distance={16} blur={14} duration={22}>\n        <div\n          style={{\n            fontFamily: MONO,\n            fontWeight: 600,\n            fontSize: 84,\n            letterSpacing: \"-0.04em\",\n            color: INK,\n          }}\n        >\n          shadcn/ui\n        </div>\n      </Reveal>\n      <Reveal delay={18} distance={12} blur={8}>\n        <InstallPill command=\"npx shadcn@latest init\" delay={18} copyAt={46} />\n      </Reveal>\n      <Reveal delay={36} distance={10} blur={6}>\n        <span style={{ fontFamily: SANS, fontSize: 20, color: MUTED }}>\n          ui.shadcn.com\n        </span>\n      </Reveal>\n    </div>\n  </AbsoluteFill>\n);\n\n// ===========================================================================\n// Transition presentations.\n// ===========================================================================\ntype EmptyProps = Record<string, never>;\n\nconst Crossfade: React.FC<TransitionPresentationComponentProps<EmptyProps>> = ({\n  children,\n  presentationProgress,\n  presentationDirection,\n}) => {\n  const entering = presentationDirection === \"entering\";\n  const opacity = entering ? presentationProgress : 1 - presentationProgress;\n  return <AbsoluteFill style={{ opacity }}>{children}</AbsoluteFill>;\n};\nconst crossfade = (): TransitionPresentation<EmptyProps> => ({\n  component: Crossfade,\n  props: {},\n});\n\nconst ZoomBlur: React.FC<\n  TransitionPresentationComponentProps<{ rise: number }>\n> = ({\n  children,\n  presentationProgress,\n  presentationDirection,\n  passedProps,\n}) => {\n  const { rise } = passedProps;\n  const entering = presentationDirection === \"entering\";\n  const p = interpolate(presentationProgress, [0, 1], [0, 1], {\n    easing: entering ? Easing.out(Easing.cubic) : Easing.in(Easing.cubic),\n  });\n  const style: React.CSSProperties = entering\n    ? {\n        opacity: p,\n        transform: `translateY(${(1 - p) * rise}px) scale(${0.86 + p * 0.14})`,\n        filter: p < 1 ? `blur(${(1 - p) * 18}px)` : undefined,\n      }\n    : {\n        opacity: 1 - p,\n        transform: `translateY(${-p * rise}px) scale(${1 + p * 0.18})`,\n        filter: p > 0 ? `blur(${p * 18}px)` : undefined,\n      };\n  return <AbsoluteFill style={style}>{children}</AbsoluteFill>;\n};\nconst zoomBlur = (rise = 0): TransitionPresentation<{ rise: number }> => ({\n  component: ZoomBlur,\n  props: { rise },\n});\n\n// ===========================================================================\n// Composition root.\n// ===========================================================================\nexport const ShadcnDemo: React.FC = () => {\n  return (\n    <RemocnUIProvider>\n      <AbsoluteFill\n        className=\"video-scope\"\n        style={\n          {\n            background: BG,\n            \"--font-geist-sans\": SANS_FAMILY,\n            \"--font-geist-mono\": MONO_FAMILY,\n          } as React.CSSProperties\n        }\n      >\n        <VideoScopeStyle />\n        {/* Hairline grid — the quiet shadcn dotted/lined backdrop. */}\n        <AbsoluteFill\n          style={{\n            backgroundImage: `linear-gradient(${BORDER} 1px, transparent 1px), linear-gradient(90deg, ${BORDER} 1px, transparent 1px)`,\n            backgroundSize: \"52px 52px\",\n            opacity: 0.5,\n          }}\n        />\n        {/* Vignette scrim to focus the center. */}\n        <AbsoluteFill\n          style={{\n            background:\n              \"radial-gradient(120% 120% at 50% 45%, rgba(9,9,11,0) 30%, rgba(9,9,11,0.85) 100%)\",\n          }}\n        />\n\n        <TransitionSeries>\n          {/* 1 — Hook */}\n          <TransitionSeries.Sequence durationInFrames={S_HOOK}>\n            <HookScene />\n          </TransitionSeries.Sequence>\n          <TransitionSeries.Transition\n            timing={linearTiming({ durationInFrames: T_X })}\n            presentation={crossfade()}\n          />\n\n          {/* 2 — Pain */}\n          <TransitionSeries.Sequence durationInFrames={S_PAIN}>\n            <PainScene />\n          </TransitionSeries.Sequence>\n          <TransitionSeries.Transition\n            timing={linearTiming({ durationInFrames: T_ZOOM })}\n            presentation={zoomBlur(44)}\n          />\n\n          {/* 3 — Intro */}\n          <TransitionSeries.Sequence durationInFrames={S_INTRO}>\n            <IntroScene />\n          </TransitionSeries.Sequence>\n          <TransitionSeries.Transition\n            timing={linearTiming({ durationInFrames: T_ZOOM })}\n            presentation={zoomBlur(44)}\n          />\n\n          {/* 4 — Install */}\n          <TransitionSeries.Sequence durationInFrames={S_INSTALL}>\n            <InstallScene />\n          </TransitionSeries.Sequence>\n          <TransitionSeries.Transition\n            timing={linearTiming({ durationInFrames: T_X })}\n            presentation={crossfade()}\n          />\n\n          {/* 5 — Code (it's yours) */}\n          <TransitionSeries.Sequence durationInFrames={S_CODE}>\n            <CodeScene />\n          </TransitionSeries.Sequence>\n          <TransitionSeries.Transition\n            timing={linearTiming({ durationInFrames: T_ZOOM })}\n            presentation={zoomBlur(44)}\n          />\n\n          {/* 6 — Benefits */}\n          <TransitionSeries.Sequence durationInFrames={S_BENEFIT}>\n            <BenefitScene />\n          </TransitionSeries.Sequence>\n          <TransitionSeries.Transition\n            timing={linearTiming({ durationInFrames: T_X })}\n            presentation={crossfade()}\n          />\n\n          {/* 7 — Distribute */}\n          <TransitionSeries.Sequence durationInFrames={S_DISTRIB}>\n            <DistribScene />\n          </TransitionSeries.Sequence>\n          <TransitionSeries.Transition\n            timing={linearTiming({ durationInFrames: T_ZOOM })}\n            presentation={zoomBlur(44)}\n          />\n\n          {/* 8 — Social proof */}\n          <TransitionSeries.Sequence durationInFrames={S_PROOF}>\n            <ProofScene />\n          </TransitionSeries.Sequence>\n          <TransitionSeries.Transition\n            timing={linearTiming({ durationInFrames: T_ZOOM })}\n            presentation={zoomBlur(60)}\n          />\n\n          {/* 9 — CTA */}\n          <TransitionSeries.Sequence durationInFrames={S_CTA}>\n            <CtaScene />\n          </TransitionSeries.Sequence>\n        </TransitionSeries>\n      </AbsoluteFill>\n    </RemocnUIProvider>\n  );\n};\n",
      "type": "registry:component",
      "target": "demos/shadcn-ui/index.tsx"
    },
    {
      "path": "src/demos/shadcn-ui/prompt.md",
      "content": "<!-- TODO(draft): placeholder written by AI — replace with the real prompt used to generate this video -->\n\nMake a proper product demo for shadcn/ui, styled exactly like the real thing — zinc-950 canvas, Geist Sans and Geist Mono, hairline grid, white marker highlights, no color. Open with a ticker-style hook that cycles \"a component library? a package?\" and lands on \"No — it's your code\", then a beat on the pain of fighting an installed black-box library. Introduce shadcn/ui with the \"Open Source. Open Code.\" line, run a terminal typing npx shadcn add button and show the file landing in the project, then open button.tsx in a glass code block with the cva variants to sell \"change anything, it's yours\". Add four pillar cards (Open Code, Composable, Themeable, AI-Ready), show shipping a custom component with npx shadcn add @acme/card, roll up the proof numbers (100,000+ stars, 20,000+ projects, works across Next.js/Vite/Remix/Astro/Laravel), and close with the wordmark and npx shadcn init pointing to ui.shadcn.com. Use remocn components throughout.\n",
      "type": "registry:file",
      "target": "demos/shadcn-ui/prompt.md"
    }
  ],
  "docs": "Register the composition in your Remotion Root:\n\n  import { ShadcnDemo } from \"@/demos/shadcn-ui\";\n  <Composition id=\"shadcn-ui\" component={ShadcnDemo} durationInFrames={908} fps={30} width={1280} height={720} />\n\nRequires Tailwind v4 wired into Remotion (@remotion/tailwind-v4).\nRender locally: npx remotion render shadcn-ui out/shadcn-ui.mp4",
  "type": "registry:block"
}