{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "render-sdk",
  "title": "render-sdk — One render API",
  "description": "remocn demo composition \"render-sdk — One render API\" — installs the full Remotion composition. Generated with AI from the prompt in demos/render-sdk/prompt.md.",
  "dependencies": [
    "@remotion/google-fonts",
    "@remotion/transitions",
    "culori",
    "lucide-react",
    "remotion"
  ],
  "registryDependencies": [
    "https://remocn.dev/r/backdrop.json",
    "https://remocn.dev/r/blur-in.json",
    "https://remocn.dev/r/logo-enter.json",
    "https://remocn.dev/r/remocn-ui.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/render-sdk/index.tsx",
      "content": "import React, { type CSSProperties, type ReactNode } from \"react\";\nimport { AbsoluteFill, Easing, Series, interpolate, interpolateColors, 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 { Backdrop } from \"@/components/remocn/backdrop\";\nimport { GlassCodeBlock } from \"@/components/remocn/glass-code-block\";\nimport { SharedAxisZ } from \"@/components/remocn/shared-axis-z\";\nimport { LogoEnter, type Logo } from \"@/components/remocn/logo-enter\";\nimport { useBlurInTransition } from \"@/components/remocn/use-blur-in-transition\";\nimport { BlurIn } from \"@/components/remocn/blur-in\";\nimport { TerminalSimulator } from \"@/components/remocn/terminal-simulator\";\nimport { CheckIcon, CopyIcon } from \"lucide-react\";\n\n// Bind both fonts to the CSS variables the remocn components read.\nconst { fontFamily: SANS_FAMILY } = loadSans(\"normal\", {\n  subsets: [\"latin\"],\n  weights: [\"400\", \"500\", \"600\", \"700\", \"800\"],\n});\nconst { fontFamily: MONO_FAMILY } = loadMono(\"normal\", {\n  subsets: [\"latin\"],\n  weights: [\"400\", \"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// Render-engine palette — Remotion blue primary, cyan secondary.\nconst BLUE = \"#5b9dff\";\nconst CYAN = \"#34d9e6\";\nconst INK = \"#fafafa\";\nconst FAINT = \"rgba(250,250,250,0.42)\";\n\n// ---------------------------------------------------------------------------\n// Scene timings (frames @ 30fps), one per beat. Transitions overlap.\n// ---------------------------------------------------------------------------\nconst S_INTRO = 140; // three questions via shared-axis-z\nconst S_STOP = 46; //   STOP — scale-down-fade\nconst S_ONEAPI = 70; //  One API, different providers\nconst S_ADAPTERS = 85; // render + lambda cards\nconst S_INSTALL = 72; // terminal install\nconst S_CODE = 135; //   code example (GlassCodeBlock)\nconst S_WORKING = 80; // working with — logo-enter\nconst S_OUTRO = 100; //  render-sdk + install pill\n\nconst T_ZOOM = 18; //  intro → stop (punch in)\nconst T_X = 14; //      generic crossfade\nconst T_BLUR = 16; //   adapters → install (background clash)\nconst T_OUT = 20; //    working → outro\n\nexport const RENDER_SDK_DURATION =\n  S_INTRO +\n  S_STOP +\n  S_ONEAPI +\n  S_ADAPTERS +\n  S_INSTALL +\n  S_CODE +\n  S_WORKING +\n  S_OUTRO -\n  (T_ZOOM + T_X + T_X + T_BLUR + T_X + T_X + T_OUT);\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?: 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 — inline marker swipe behind a phrase.\n// ---------------------------------------------------------------------------\nconst Mark: React.FC<{\n  children: string;\n  color?: string;\n  startFrame?: number;\n}> = ({ children, color = BLUE, startFrame = 8 }) => {\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, \"#0a0a0a\"],\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.02em -0.12em\",\n          background: color,\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 — Intro. Three pain questions, each entering via shared-axis-z, in a\n// single accent color. A Series resets the frame so each transition plays clean.\n// ===========================================================================\nconst Q1 = \"Building a render queue?\";\nconst Q2 = \"Two renderers — self-hosted and Lambda?\";\nconst Q3 = \"Reusing render code across projects?\";\n\nconst IntroScene: React.FC = () => (\n  <AbsoluteFill style={{ padding: \"0 90px\" }}>\n    <Series>\n      <Series.Sequence durationInFrames={42} layout=\"none\">\n        <SharedAxisZ\n          fromText=\"\"\n          toText={Q1}\n          fontSize={44}\n          fontWeight={600}\n          color={\"#FFFFFF\"}\n        />\n      </Series.Sequence>\n      <Series.Sequence durationInFrames={46} layout=\"none\">\n        <SharedAxisZ\n          fromText={Q1}\n          toText={Q2}\n          fontSize={44}\n          fontWeight={600}\n          color={\"#FFFFFF\"}\n        />\n      </Series.Sequence>\n      <Series.Sequence durationInFrames={52} layout=\"none\">\n        <SharedAxisZ\n          fromText={Q2}\n          toText={Q3}\n          fontSize={44}\n          fontWeight={600}\n          color={\"#FFFFFF\"}\n        />\n      </Series.Sequence>\n    </Series>\n  </AbsoluteFill>\n);\n\n// ===========================================================================\n// Scene 2 — STOP. scale-down-fade settles a single hard word, then lets it go.\n// ===========================================================================\nconst StopScene: React.FC = () => (\n  <AbsoluteFill style={{ alignItems: \"center\", justifyContent: \"center\" }}>\n    <SharedAxisZ\n      fromText={\"\"}\n      toText={\"Stop\"}\n      fontSize={200}\n      fontWeight={600}\n      color={\"#FFFFFF\"}\n    />\n  </AbsoluteFill>\n);\n\n// ===========================================================================\n// Scene 3 — One API, different providers.\n// ===========================================================================\nconst OneApiScene: React.FC = () => (\n  <AbsoluteFill\n    style={{\n      alignItems: \"center\",\n      justifyContent: \"center\",\n      flexDirection: \"column\",\n      gap: 14,\n    }}\n  >\n    <Reveal delay={4} distance={20} blur={12} duration={22}>\n      <h1\n        style={{\n          margin: 0,\n          fontFamily: SANS,\n          fontWeight: 500,\n          fontSize: 48,\n          color: INK,\n          textAlign: \"center\",\n        }}\n      >\n        Use Render SDK\n      </h1>\n    </Reveal>\n    <Reveal delay={16} distance={16} blur={10}>\n      <h2\n        style={{\n          margin: 0,\n          fontFamily: SANS,\n          fontWeight: 600,\n          fontSize: 32,\n          color: INK,\n          textAlign: \"center\",\n        }}\n      >\n        <Mark color={BLUE} startFrame={26}>\n          One API\n        </Mark>{\" \"}\n        Different providers\n      </h2>\n    </Reveal>\n  </AbsoluteFill>\n);\n\n// ===========================================================================\n// Scene 4 — Adapters: render + lambda cards.\n// ===========================================================================\nconst AdapterCard: React.FC<{\n  accent: string;\n  title: string;\n  pkg: string;\n  desc: string;\n  call: string;\n  delay: number;\n}> = ({ accent, title, pkg, desc, call, delay }) => (\n  <Reveal delay={delay} distance={36} blur={12} duration={20}>\n    <div\n      style={{\n        width: 360,\n        display: \"flex\",\n        flexDirection: \"column\",\n        gap: 14,\n        padding: \"28px 30px\",\n        borderRadius: 20,\n        background: \"rgba(10,10,10,0.55)\",\n        border: `1px solid ${accent}44`,\n        boxShadow: \"0 20px 50px rgba(0,0,0,0.45)\",\n      }}\n    >\n      <div style={{ display: \"flex\", alignItems: \"center\", gap: 12 }}>\n        <span\n          style={{\n            width: 11,\n            height: 11,\n            borderRadius: \"50%\",\n            background: accent,\n          }}\n        />\n        <span\n          style={{\n            fontFamily: SANS,\n            fontWeight: 700,\n            fontSize: 28,\n            color: INK,\n          }}\n        >\n          {title}\n        </span>\n      </div>\n      <span style={{ fontFamily: MONO, fontSize: 16, color: \"#a1a1aa\" }}>\n        {pkg}\n      </span>\n      <span\n        style={{\n          fontFamily: SANS,\n          fontSize: 18,\n          color: \"rgba(255,255,255,0.7)\",\n        }}\n      >\n        {desc}\n      </span>\n      <span\n        style={{\n          marginTop: 4,\n          alignSelf: \"flex-start\",\n          fontFamily: MONO,\n          fontSize: 15,\n          color: accent,\n          padding: \"7px 14px\",\n          borderRadius: 10,\n          background: `${accent}1f`,\n        }}\n      >\n        {call}\n      </span>\n    </div>\n  </Reveal>\n);\n\nconst AdaptersScene: React.FC = () => (\n  <AbsoluteFill\n    style={{\n      alignItems: \"center\",\n      justifyContent: \"center\",\n      flexDirection: \"column\",\n      gap: 36,\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          textAlign: \"center\",\n        }}\n      >\n        Two adapters, one interface\n      </h2>\n    </Reveal>\n    <div style={{ display: \"flex\", gap: 36 }}>\n      <AdapterCard\n        accent={BLUE}\n        title=\"Render\"\n        pkg=\"@remotion/renderer\"\n        desc=\"Local & self-hosted rendering.\"\n        call=\"serverAdapter()\"\n        delay={8}\n      />\n      <AdapterCard\n        accent={CYAN}\n        title=\"Lambda\"\n        pkg=\"@remotion/lambda\"\n        desc=\"Serverless rendering at scale.\"\n        call=\"lambdaAdapter()\"\n        delay={16}\n      />\n    </div>\n  </AbsoluteFill>\n);\n\n// ===========================================================================\n// Scene 5 — Install (no heading). terminal-cursor-zoom dollies across the typed\n// install command.\n// ===========================================================================\nconst InstallScene: React.FC = () => (\n  <AbsoluteFill>\n    <TerminalSimulator\n      lines={[{ text: \"bun install @remocn/render-sdk\", type: \"command\", delay: 0 }]}\n      fontSize={22}\n      title=\"~/code/my-app\"\n      charsPerFrame={2}\n    />\n  </AbsoluteFill>\n);\n\n// ===========================================================================\n// Scene 6 — Code Example (from the docs), shown in a GlassCodeBlock.\n// ===========================================================================\nconst CODE_EXAMPLE = `import { RenderSdk } from \"@remocn/render-sdk\";\nimport { serverAdapter } from \"@remocn/render-sdk/server\";\n\nconst sdk = new RenderSdk({ adapter: serverAdapter() });\n\nconst renderId = await sdk.start({\n  compositionId: \"MyVideo\",\n  inputProps: { title: \"Hello\" },\n});\n\nawait sdk.waitForCompletion(renderId, {\n  onProgress: (p) => console.log(p),\n});\n\nconst url = await sdk.getUrl(renderId);`;\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          textAlign: \"center\",\n        }}\n      >\n        From start to{\" \"}\n        <Mark color={BLUE} startFrame={12}>\n          URL\n        </Mark>\n      </h2>\n    </Reveal>\n    <Reveal delay={8} distance={40} blur={14} duration={22}>\n      <div style={{ position: \"relative\", width: 760, height: 470 }}>\n        <GlassCodeBlock\n          code={CODE_EXAMPLE}\n          title=\"render.ts\"\n          width={760}\n          height={470}\n          fontSize={15}\n          staggerFrames={2}\n        />\n      </div>\n    </Reveal>\n  </AbsoluteFill>\n);\n\n// ===========================================================================\n// Scene 7 — Working with. Framework brand chips spring in via logo-enter.\n// ===========================================================================\nconst NextMark: React.FC = () => (\n  <svg width={54} height={54} viewBox=\"0 0 180 180\">\n    <mask\n      height={180}\n      id=\"rsdk_next_mask\"\n      maskUnits=\"userSpaceOnUse\"\n      width={180}\n      x={0}\n      y={0}\n      style={{ maskType: \"alpha\" }}\n    >\n      <circle cx={90} cy={90} fill=\"black\" r={90} />\n    </mask>\n    <g mask=\"url(#rsdk_next_mask)\">\n      <circle cx={90} cy={90} fill=\"black\" r={90} />\n      <path\n        d=\"M149.508 157.52L69.142 54H54V125.97H66.1136V69.3836L139.999 164.845C143.333 162.614 146.509 160.165 149.508 157.52Z\"\n        fill=\"url(#rsdk_next_p0)\"\n      />\n      <rect fill=\"url(#rsdk_next_p1)\" height={72} width={12} x={115} y={54} />\n    </g>\n    <defs>\n      <linearGradient\n        gradientUnits=\"userSpaceOnUse\"\n        id=\"rsdk_next_p0\"\n        x1={109}\n        x2={144.5}\n        y1={116.5}\n        y2={160.5}\n      >\n        <stop stopColor=\"white\" />\n        <stop offset={1} stopColor=\"white\" stopOpacity={0} />\n      </linearGradient>\n      <linearGradient\n        gradientUnits=\"userSpaceOnUse\"\n        id=\"rsdk_next_p1\"\n        x1={121}\n        x2={120.799}\n        y1={54}\n        y2={106.875}\n      >\n        <stop stopColor=\"white\" />\n        <stop offset={1} stopColor=\"white\" stopOpacity={0} />\n      </linearGradient>\n    </defs>\n  </svg>\n);\n\nconst ReactMark: React.FC = () => (\n  <svg width={60} height={54} viewBox=\"0 0 569 512\">\n    <g fill=\"#58C4DC\" fillRule=\"nonzero\">\n      <path d=\"M285.5,201 C255.400481,201 231,225.400481 231,255.5 C231,285.599519 255.400481,310 285.5,310 C315.599519,310 340,285.599519 340,255.5 C340,225.400481 315.599519,201 285.5,201\" />\n      <path d=\"M568.959856,255.99437 C568.959856,213.207656 529.337802,175.68144 466.251623,150.985214 C467.094645,145.423543 467.85738,139.922107 468.399323,134.521063 C474.621631,73.0415145 459.808523,28.6686204 426.709856,9.5541429 C389.677085,-11.8291748 337.36955,3.69129898 284.479928,46.0162134 C231.590306,3.69129898 179.282771,-11.8291748 142.25,9.5541429 C109.151333,28.6686204 94.3382249,73.0415145 100.560533,134.521063 C101.102476,139.922107 101.845139,145.443621 102.708233,151.02537 C97.4493791,153.033193 92.2908847,155.161486 87.3331099,157.39017 C31.0111824,182.708821 0,217.765415 0,255.99437 C0,298.781084 39.6220545,336.307301 102.708233,361.003527 C101.845139,366.565197 101.102476,372.066633 100.560533,377.467678 C94.3382249,438.947226 109.151333,483.32012 142.25,502.434597 C153.629683,508.887578 166.52439,512.186771 179.603923,511.991836 C210.956328,511.991836 247.567589,495.487529 284.479928,465.972527 C321.372196,495.487529 358.003528,511.991836 389.396077,511.991836 C402.475265,512.183856 415.36922,508.884856 426.75,502.434597 C459.848667,483.32012 474.661775,438.947226 468.439467,377.467678 C467.897524,372.066633 467.134789,366.565197 466.291767,361.003527 C529.377946,336.347457 569,298.761006 569,255.99437 M389.155214,27.1025182 C397.565154,26.899606 405.877839,28.9368502 413.241569,33.0055186 C436.223966,46.2772304 446.540955,82.2775015 441.522965,131.770345 C441.181741,135.143488 440.780302,138.556788 440.298575,141.990165 C414.066922,134.08804 387.205771,128.452154 360.010724,125.144528 C343.525021,103.224055 325.192524,82.7564475 305.214266,63.9661533 C336.586743,39.7116483 366.032313,27.1025182 389.135142,27.1025182 M378.356498,310.205598 C368.204912,327.830733 357.150626,344.919965 345.237759,361.405091 C325.045049,363.479997 304.758818,364.51205 284.459856,364.497299 C264.167589,364.51136 243.888075,363.479308 223.702025,361.405091 C211.820914,344.919381 200.80007,327.83006 190.683646,310.205598 C180.532593,292.629285 171.306974,274.534187 163.044553,255.99437 C171.306974,237.454554 180.532593,219.359455 190.683646,201.783142 C200.784121,184.229367 211.770999,167.201087 223.601665,150.764353 C243.824636,148.63809 264.145559,147.579168 284.479928,147.591877 C304.772146,147.579725 325.051559,148.611772 345.237759,150.68404 C357.109048,167.14607 368.136094,184.201112 378.27621,201.783142 C388.419418,219.363718 397.644825,237.458403 405.915303,255.99437 C397.644825,274.530337 388.419418,292.625022 378.27621,310.205598 M419.724813,290.127366 C426.09516,307.503536 431.324985,325.277083 435.380944,343.334682 C417.779633,348.823635 399.836793,353.149774 381.668372,356.285142 C388.573127,345.871232 395.263781,335.035679 401.740334,323.778483 C408.143291,312.655143 414.144807,301.431411 419.805101,290.207679 M246.363271,390.377981 C258.848032,391.140954 271.593728,391.582675 284.5,391.582675 C297.406272,391.582675 310.232256,391.140954 322.737089,390.377981 C310.880643,404.583418 298.10766,417.997563 284.5,430.534446 C270.921643,417.999548 258.18192,404.585125 246.363271,390.377981 Z M187.311556,356.244986 C169.137286,353.123646 151.187726,348.810918 133.578912,343.334682 C137.618549,325.305649 142.828222,307.559058 149.174827,290.207679 C154.754833,301.431411 160.736278,312.655143 167.239594,323.778483 C173.74291,334.901824 180.467017,345.864539 187.311556,356.285142 M149.174827,221.760984 C142.850954,204.473938 137.654787,186.794745 133.619056,168.834762 C151.18418,163.352378 169.085653,159.013101 187.211197,155.844146 C180.346585,166.224592 173.622478,176.986525 167.139234,188.210257 C160.65599,199.433989 154.734761,210.517173 149.074467,221.760984 M322.616657,121.590681 C310.131896,120.827708 297.3862,120.385987 284.379568,120.385987 C271.479987,120.385987 258.767744,120.787552 246.242839,121.590681 C258.061488,107.383537 270.801211,93.9691137 284.379568,81.4342157 C297.99241,93.9658277 310.765727,107.380324 322.616657,121.590681 Z M401.70019,188.210257 C395.196875,176.939676 388.472767,166.09743 381.527868,155.68352 C399.744224,158.819049 417.734224,163.151949 435.380944,168.654058 C431.331963,186.680673 426.122466,204.426664 419.785029,221.781062 C414.205023,210.55733 408.203506,199.333598 401.720262,188.230335 M127.517179,131.790423 C122.438973,82.3176579 132.816178,46.2973086 155.778503,33.0255968 C163.144699,28.9632474 171.455651,26.9264282 179.864858,27.1225964 C202.967687,27.1225964 232.413257,39.7317265 263.785734,63.9862316 C243.794133,82.7898734 225.448298,103.270812 208.949132,125.204763 C181.761691,128.528025 154.90355,134.14313 128.661281,141.990165 C128.199626,138.556788 127.778115,135.163566 127.456963,131.790423 M98.4529773,182.106474 C101.54406,180.767925 104.695358,179.429376 107.906872,178.090828 C114.220532,204.735668 122.781793,230.7969 133.498624,255.99437 C122.761529,281.241316 114.193296,307.357063 107.8868,334.058539 C56.7434387,313.076786 27.0971497,284.003505 27.0971497,255.99437 C27.0971497,229.450947 53.1907013,202.526037 98.4529773,182.106474 Z M155.778503,478.963143 C132.816178,465.691432 122.438973,429.671082 127.517179,380.198317 C127.838331,376.825174 128.259842,373.431953 128.721497,369.978497 C154.953686,377.878517 181.814655,383.514365 209.009348,386.824134 C225.500295,408.752719 243.832321,429.233234 263.805806,448.042665 C220.069,481.834331 180.105722,492.97775 155.838719,478.963143 M441.502893,380.198317 C446.520883,429.691161 436.203894,465.691432 413.221497,478.963143 C388.974566,493.017906 348.991216,481.834331 305.274481,448.042665 C325.241364,429.232737 343.566681,408.752215 360.050868,386.824134 C387.245915,383.516508 414.107066,377.880622 440.338719,369.978497 C440.820446,373.431953 441.221885,376.825174 441.563109,380.198317 M461.193488,334.018382 C454.869166,307.332523 446.294494,281.231049 435.561592,255.99437 C446.289797,230.744081 454.857778,204.629101 461.173416,177.930202 C512.216417,198.911955 541.942994,227.985236 541.942994,255.99437 C541.942994,284.003505 512.296705,313.076786 461.153344,334.058539\" />\n    </g>\n  </svg>\n);\n\nconst RemixMark: React.FC = () => (\n  <svg width={50} height={58} viewBox=\"0 0 256 297\">\n    <path\n      d=\"M141.675 0C218.047 0 256 36.35 256 94.414c0 43.43-26.707 71.753-62.785 76.474 30.455 6.137 48.259 23.604 51.54 58.065l.474 6.337.415 5.924.358 5.542.249 4.179.267 4.93.138 2.814.198 4.47.159 4.222.079 2.427.107 3.888.092 4.446.033 2.148.06 6.226.02 6.496v3.885h-78.758l.004-1.62.028-3.147.047-3.065.136-7.424.035-2.489.027-3.902-.004-2.496-.023-2.617-.032-2.054-.064-2.876-.094-3.05-.125-3.242-.16-3.455-.096-1.813-.16-2.833-.186-2.976-.287-4.204-.247-3.342a116.56 116.56 0 0 0-.247-3.02l-.202-1.934c-2.6-22.827-11.655-32.157-27.163-35.269l-1.307-.245a60.184 60.184 0 0 0-2.704-.408l-1.397-.164c-.236-.025-.472-.05-.71-.073l-1.442-.127-1.471-.103-1.502-.081-1.514-.058-1.544-.039-1.574-.018L0 198.74V136.9h127.62c2.086 0 4.108-.04 6.066-.12l1.936-.095 1.893-.122 1.85-.15c.305-.028.608-.056.909-.086l1.785-.193a86.3 86.3 0 0 0 3.442-.475l1.657-.28c20.709-3.755 31.063-14.749 31.063-36.2 0-24.075-16.867-38.666-50.602-38.666H0V0h141.675ZM83.276 250.785c10.333 0 14.657 5.738 16.197 11.23l.203.79.167.782.109.617.046.306.078.603.058.59.023.29.031.569.01.278.008.54v29.507H0v-46.102h83.276Z\"\n      fill=\"#ffffff\"\n    />\n  </svg>\n);\n\nconst RouterMark: React.FC = () => (\n  <svg width={58} height={38} viewBox=\"0 0 94 61\" fill=\"none\">\n    <path\n      d=\"M72.7315 20.9357C70.0548 20.0941 68.6725 20.3778 65.8649 20.071C61.5246 19.5976 59.7954 17.9013 59.0619 13.5356C58.6514 11.0985 59.1361 7.53022 58.0881 5.32106C56.0839 1.10875 51.3943 -0.780439 46.6828 0.297843C42.7049 1.20956 39.3951 5.18518 39.2117 9.266C39.0021 13.9254 41.657 17.901 46.2156 19.273C48.3814 19.9261 50.6825 20.2548 52.9444 20.4214C57.0925 20.7238 57.4113 23.0297 58.5335 24.9277C59.2409 26.1243 59.9264 27.3034 59.9264 30.8714C59.9264 34.4394 59.2365 35.6185 58.5335 36.8151C57.4113 38.7087 56.0271 39.9491 51.879 40.2559C49.6171 40.4225 47.3116 40.7513 45.1502 41.4044C40.5916 42.7807 37.9367 46.7519 38.1463 51.4113C38.3297 55.4921 41.6395 59.4678 45.6174 60.3795C50.3289 61.4621 55.0185 59.5686 57.0227 55.3563C58.075 53.1471 58.6514 50.6443 59.0619 48.2072C59.7998 43.8414 61.5289 42.1451 65.8649 41.6717C68.6725 41.3649 71.5783 41.6717 74.2093 40.177C76.9895 38.1456 79.4734 35.0968 79.4734 30.8714C79.4734 26.6459 76.7967 22.2156 72.7315 20.9357Z\"\n      fill=\"#F44250\"\n    />\n    <path\n      d=\"M28.1997 40.7739C22.7285 40.7739 18.2656 36.3027 18.2656 30.8213C18.2656 25.3399 22.7285 20.8687 28.1997 20.8687C33.6709 20.8687 38.1338 25.3399 38.1338 30.8213C38.1338 36.2983 33.6665 40.7739 28.1997 40.7739Z\"\n      fill=\"#ffffff\"\n    />\n    <path\n      d=\"M9.899 61C4.43661 60.9868 -0.0130938 56.498 2.89511e-05 51.0122C0.0132099 45.5353 4.4936 41.0773 9.96914 41.0948C15.4359 41.108 19.8856 45.5968 19.8681 51.0825C19.8549 56.5551 15.3745 61.0131 9.899 61Z\"\n      fill=\"#ffffff\"\n    />\n    <path\n      d=\"M83.7137 60.9998C78.2339 61.0304 73.7361 56.5901 73.7052 51.122C73.6747 45.632 78.1068 41.1258 83.5646 41.0949C89.0444 41.0643 93.5423 45.5046 93.5731 50.9727C93.6036 56.4583 89.1716 60.9689 83.7137 60.9998Z\"\n      fill=\"#ffffff\"\n    />\n  </svg>\n);\n\nconst FRAMEWORK_LOGOS: Logo[] = [\n  { mark: <NextMark />, bg: \"#000000\" },\n  { mark: <ReactMark />, bg: \"#0A0A0A\" },\n  { mark: <RemixMark />, bg: \"#1A1A1A\" },\n  { mark: <RouterMark />, bg: \"#FFFFFF\" },\n];\n\nconst WorkingScene: React.FC = () => (\n  <AbsoluteFill\n    style={{\n      alignItems: \"center\",\n      justifyContent: \"center\",\n      flexDirection: \"column\",\n      gap: 44,\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          textAlign: \"center\",\n        }}\n      >\n        Works with your stack\n      </h2>\n    </Reveal>\n    <div style={{ position: \"relative\", height: 130 }}>\n      <LogoEnter\n        logos={FRAMEWORK_LOGOS}\n        diameter={112}\n        overlap={34}\n        ringColor=\"#0A0A0A\"\n        stagger={7}\n      />\n    </div>\n  </AbsoluteFill>\n);\n\n// ===========================================================================\n// Scene 8 — Outro. A box mark draws itself on (typography-style stroke draw),\n// then the wordmark, version, and tagline settle with tight spacing.\n// ===========================================================================\nconst BoxIcon: React.FC<{ size?: number }> = ({ size = 78 }) => {\n  const frame = useCurrentFrame();\n  const appear = interpolate(frame, [0, 8], [0, 1], {\n    extrapolateLeft: \"clamp\",\n    extrapolateRight: \"clamp\",\n  });\n  const draw1 = interpolate(frame, [2, 34], [1, 0], {\n    extrapolateLeft: \"clamp\",\n    extrapolateRight: \"clamp\",\n    easing: Easing.inOut(Easing.cubic),\n  });\n  const draw2 = interpolate(frame, [18, 44], [1, 0], {\n    extrapolateLeft: \"clamp\",\n    extrapolateRight: \"clamp\",\n    easing: Easing.inOut(Easing.cubic),\n  });\n  const draw3 = interpolate(frame, [26, 50], [1, 0], {\n    extrapolateLeft: \"clamp\",\n    extrapolateRight: \"clamp\",\n    easing: Easing.inOut(Easing.cubic),\n  });\n  return (\n    <svg\n      width={size}\n      height={size}\n      viewBox=\"0 0 24 24\"\n      fill=\"none\"\n      stroke={INK}\n      strokeWidth={1.5}\n      strokeLinecap=\"round\"\n      strokeLinejoin=\"round\"\n      style={{ opacity: appear }}\n    >\n      <path\n        pathLength={1}\n        strokeDasharray={1}\n        strokeDashoffset={draw1}\n        d=\"M21 13.6376V10.3624C21 8.71559 21 7.89217 20.6166 7.20744C20.2332 6.52271 19.5317 6.09334 18.1287 5.2346L15.1287 3.39836C13.6056 2.46612 12.8441 2 12 2C11.1559 2 10.3944 2.46612 8.8713 3.39836L5.8713 5.2346C4.46832 6.09334 3.76683 6.52271 3.38341 7.20744C3 7.89217 3 8.71559 3 10.3624V13.6376C3 15.2844 3 16.1078 3.38341 16.7926C3.76683 17.4773 4.46832 17.9067 5.8713 18.7654L8.8713 20.6016C10.3944 21.5339 11.1559 22 12 22C12.8441 22 13.6056 21.5339 15.1287 20.6016L18.1287 18.7654C19.5317 17.9067 20.2332 17.4773 20.6166 16.7926C21 16.1078 21 15.2844 21 13.6376Z\"\n      />\n      <path\n        pathLength={1}\n        strokeDasharray={1}\n        strokeDashoffset={draw2}\n        d=\"M3.5 7L12 12L20.5 7\"\n      />\n      <path\n        pathLength={1}\n        strokeDasharray={1}\n        strokeDashoffset={draw3}\n        d=\"M12 12V22\"\n      />\n    </svg>\n  );\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  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,\n        fontSize: 18,\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\" : \"rgba(250,250,250,0.55)\",\n          transform: copied ? `scale(${pop})` : \"scale(1)\",\n        }}\n      >\n        {copied ? <CheckIcon size={17} /> : <CopyIcon size={17} />}\n      </span>\n    </div>\n  );\n};\n\nconst OutroScene: React.FC = () => (\n  <AbsoluteFill style={{ alignItems: \"center\", justifyContent: \"center\" }}>\n    <div\n      style={{\n        display: \"flex\",\n        flexDirection: \"column\",\n        alignItems: \"center\",\n        gap: 16,\n      }}\n    >\n      <Reveal delay={2} distance={10} blur={6} duration={14}>\n        <BoxIcon size={78} />\n      </Reveal>\n      <Reveal delay={10} distance={16} blur={14} duration={22}>\n        <div\n          style={{\n            display: \"flex\",\n            alignItems: \"baseline\",\n            fontFamily: MONO,\n            fontWeight: 700,\n            fontSize: 92,\n            letterSpacing: \"-0.04em\",\n            color: INK,\n          }}\n        >\n          render-sdk\n        </div>\n      </Reveal>\n      <Reveal delay={32} distance={12} blur={8}>\n        <InstallPill\n          command=\"bun add @remocn/render-sdk\"\n          delay={32}\n          copyAt={58}\n        />\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 BlurCrossfade: React.FC<\n  TransitionPresentationComponentProps<EmptyProps>\n> = ({ children, presentationProgress, presentationDirection }) => {\n  const entering = presentationDirection === \"entering\";\n  const p = presentationProgress;\n  const style: React.CSSProperties = entering\n    ? {\n        opacity: p,\n        transform: `scale(${0.96 + p * 0.04})`,\n        filter: p < 1 ? `blur(${(1 - p) * 14}px)` : undefined,\n      }\n    : {\n        opacity: 1 - p,\n        transform: `scale(${1 + p * 0.04})`,\n        filter: p > 0 ? `blur(${p * 14}px)` : undefined,\n      };\n  return <AbsoluteFill style={style}>{children}</AbsoluteFill>;\n};\nconst blurCrossfade = (): TransitionPresentation<EmptyProps> => ({\n  component: BlurCrossfade,\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.84 + p * 0.16})`,\n        filter: p < 1 ? `blur(${(1 - p) * 20}px)` : undefined,\n      }\n    : {\n        opacity: 1 - p,\n        transform: `translateY(${-p * rise}px) scale(${1 + p * 0.22})`,\n        filter: p > 0 ? `blur(${p * 20}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 RenderSdkDemo: React.FC = () => {\n  return (\n    <RemocnUIProvider>\n      <AbsoluteFill\n        style={\n          {\n            \"--font-geist-sans\": SANS_FAMILY,\n            \"--font-geist-mono\": MONO_FAMILY,\n          } as React.CSSProperties\n        }\n      >\n        {/* Persistent image background for the whole video. */}\n        <Backdrop fill={{ type: \"image\", src: demoAsset(\"bg.png\") }} />\n        {/* Scrim to deepen contrast for foreground content. */}\n        <AbsoluteFill\n          style={{\n            background:\n              \"radial-gradient(120% 120% at 50% 40%, rgba(0,0,0,0.2) 0%, rgba(0,0,0,0.5) 100%)\",\n          }}\n        />\n\n        <TransitionSeries>\n          {/* 1 — Intro questions */}\n          <TransitionSeries.Sequence durationInFrames={S_INTRO}>\n            <IntroScene />\n          </TransitionSeries.Sequence>\n          <TransitionSeries.Transition\n            timing={linearTiming({ durationInFrames: T_ZOOM })}\n            presentation={zoomBlur(40)}\n          />\n\n          {/* 2 — STOP */}\n          <TransitionSeries.Sequence durationInFrames={S_STOP}>\n            <StopScene />\n          </TransitionSeries.Sequence>\n          <TransitionSeries.Transition\n            timing={linearTiming({ durationInFrames: T_X })}\n            presentation={crossfade()}\n          />\n\n          {/* 3 — One API, different providers */}\n          <TransitionSeries.Sequence durationInFrames={S_ONEAPI}>\n            <OneApiScene />\n          </TransitionSeries.Sequence>\n          <TransitionSeries.Transition\n            timing={linearTiming({ durationInFrames: T_X })}\n            presentation={crossfade()}\n          />\n\n          {/* 4 — Adapters cards */}\n          <TransitionSeries.Sequence durationInFrames={S_ADAPTERS}>\n            <AdaptersScene />\n          </TransitionSeries.Sequence>\n          <TransitionSeries.Transition\n            timing={linearTiming({ durationInFrames: T_BLUR })}\n            presentation={blurCrossfade()}\n          />\n\n          {/* 5 — Install (terminal-cursor-zoom) */}\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          {/* 6 — Code example */}\n          <TransitionSeries.Sequence durationInFrames={S_CODE}>\n            <CodeScene />\n          </TransitionSeries.Sequence>\n          <TransitionSeries.Transition\n            timing={linearTiming({ durationInFrames: T_X })}\n            presentation={crossfade()}\n          />\n\n          {/* 7 — Working with */}\n          <TransitionSeries.Sequence durationInFrames={S_WORKING}>\n            <WorkingScene />\n          </TransitionSeries.Sequence>\n          <TransitionSeries.Transition\n            timing={linearTiming({ durationInFrames: T_OUT })}\n            presentation={zoomBlur(70)}\n          />\n\n          {/* 8 — Outro */}\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/render-sdk/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/render-sdk/prompt.md",
      "content": "<!-- TODO(draft): placeholder written by AI — replace with the real prompt used to generate this video -->\n\nMake a launch video for @remocn/render-sdk. Open with a few pain questions about juggling different render provider APIs, hit a hard \"Stop.\" beat, then land \"One API, different providers\" and show cards for the render and lambda adapters. Zoom into a terminal install (bun install @remocn/render-sdk), pop open a glass code block with a short usage example, and build out a logo wall of the frameworks it works with — Next.js, React, Remix, React Router. Close with a simple drawn-on box mark, the render-sdk wordmark, the v1.0.0 tag, and the tagline. Keep it clean and technical, built with remocn components.\n",
      "type": "registry:file",
      "target": "demos/render-sdk/prompt.md"
    }
  ],
  "docs": "Register the composition in your Remotion Root:\n\n  import { RenderSdkDemo } from \"@/demos/render-sdk\";\n  <Composition id=\"render-sdk\" component={RenderSdkDemo} durationInFrames={618} fps={30} width={1280} height={720} />\n\nRequires Tailwind v4 wired into Remotion (@remotion/tailwind-v4).\nRender locally: npx remotion render render-sdk out/render-sdk.mp4",
  "type": "registry:block"
}