{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "sponsor-shieldcn",
  "title": "remocn ✕ shieldcn — New sponsor",
  "description": "remocn demo composition \"remocn ✕ shieldcn — New sponsor\" — installs the full Remotion composition. Generated with AI from the prompt in demos/sponsor-shieldcn/prompt.md.",
  "dependencies": [
    "@paper-design/shaders-react",
    "@remotion/google-fonts",
    "@remotion/transitions",
    "remotion"
  ],
  "registryDependencies": [
    "https://remocn.dev/r/shader-god-rays.json",
    "https://remocn.dev/r/soft-blur-in.json"
  ],
  "files": [
    {
      "path": "src/demos/sponsor-shieldcn/index.tsx",
      "content": "import React from \"react\";\nimport { AbsoluteFill, Easing, Img, Sequence, interpolate, spring, useCurrentFrame, useVideoConfig } from \"remotion\";\nimport { demoAsset } from \"@/lib/demo-assets\";\nimport {\n  TransitionSeries,\n  linearTiming,\n  type TransitionPresentation,\n  type TransitionPresentationComponentProps,\n} from \"@remotion/transitions\";\nimport { loadFont as loadSans } from \"@remotion/google-fonts/Geist\";\n\nimport { ShaderGodRays } from \"@/components/remocn/shader-god-rays\";\nimport { SoftBlurIn } from \"@/components/remocn/soft-blur-in\";\n\n// shieldcn speaks shadcn's language — Geist Sans everywhere.\nconst { fontFamily: SANS_FAMILY } = loadSans(\"normal\", {\n  subsets: [\"latin\"],\n  weights: [\"400\", \"500\", \"600\", \"700\", \"800\"],\n});\n\nconst SANS =\n  \"var(--font-geist-sans), -apple-system, BlinkMacSystemFont, sans-serif\";\n\n// shieldcn palette — the shadcn zinc register plus the badge-value green.\nconst BG = \"#09090b\";\nconst INK = \"#fafafa\";\nconst MUTED = \"#a1a1aa\";\nconst FAINT = \"rgba(250,250,250,0.5)\";\n\nconst clampOpts = {\n  extrapolateLeft: \"clamp\" as const,\n  extrapolateRight: \"clamp\" as const,\n};\n\n// ---------------------------------------------------------------------------\n// Scene timings (frames @ 30fps). Transitions overlap.\n// ---------------------------------------------------------------------------\nconst S_INTRO = 60; //   centered real badge, springing in with a float\nconst S_HOOK = 72; //    text animation\nconst S_MAIN = 112; //   Remocn ✕ shieldcn lockup\nconst S_BALLET = 220; // badge ballet: blur-formed circle → spin → row → exit\n\nconst T_X = 16; //  crossfade\nconst T_ZB = 18; // zoom-blur\nconst T_XB = 14; // crossfade into the ballet\n\nexport const SPONSOR_SHIELDCN_DURATION =\n  S_INTRO + S_HOOK + S_MAIN + S_BALLET - (T_ZB + T_X + T_XB);\n\n// The real shieldcn logo mark. When drawProgress is provided, the two paths\n// draw themselves on as strokes and the fill resolves via fillProgress.\nconst LOGO_PATHS = [\n  \"M148.02,363.76c-4.48,0-8.64-2.42-10.86-6.32l-54.29-95.68c-2.15-3.8-2.15-8.52,0-12.32l54.29-95.68c2.21-3.9,6.37-6.32,10.86-6.32h18.51c4.44,0,8.45,2.28,10.73,6.09,2.27,3.82,2.37,8.43.25,12.33l-42.23,77.99c-3.98,7.36-3.98,16.14,0,23.49l22.22,41.02c4.25,7.85,12.43,12.8,21.36,12.92,0,0,45.08.61,45.11.61,8.68,0,16.83-4.64,21.26-12.12l24.87-41.99c2.23-3.77,6.34-6.11,10.72-6.12l19.47-.04c4.48,0,8.49,2.29,10.76,6.12,2.27,3.83,2.35,8.45.21,12.35l-42.2,77.17c-2.19,4-6.39,6.49-10.95,6.49h-110.08Z\",\n  \"M346.7,363.69c-4.44,0-8.45-2.28-10.73-6.09-2.27-3.82-2.37-8.43-.25-12.33l42.23-77.99c3.98-7.35,3.98-16.14,0-23.49l-22.22-41.02c-4.25-7.85-12.44-12.8-21.36-12.92,0,0-46.51-.63-46.53-.63-8.88,0-17.12,4.81-21.48,12.54l-23.35,41.36c-2.2,3.9-6.36,6.34-10.84,6.35l-19.21.04c-4.48,0-8.49-2.29-10.76-6.12-2.27-3.83-2.35-8.45-.22-12.36l42.2-77.17c2.19-4.01,6.39-6.5,10.95-6.5h110.08c4.48,0,8.64,2.42,10.86,6.32l54.29,95.68c2.16,3.8,2.16,8.52,0,12.32l-54.29,95.68c-2.21,3.9-6.37,6.32-10.86,6.32h-18.51Z\",\n];\n\nconst ShieldcnLogo: React.FC<{\n  size?: number;\n  color?: string;\n  drawProgress?: number;\n  fillProgress?: number;\n}> = ({ size = 96, color = INK, drawProgress, fillProgress }) => {\n  const drawing = drawProgress !== undefined;\n  return (\n    <svg\n      xmlns=\"http://www.w3.org/2000/svg\"\n      viewBox=\"0 0 512 512\"\n      width={size}\n      height={size}\n      aria-hidden\n    >\n      {LOGO_PATHS.map((d, i) => (\n        <path\n          key={i}\n          d={d}\n          fill={color}\n          {...(drawing\n            ? {\n                pathLength: 1,\n                strokeDasharray: 1,\n                strokeDashoffset: 1 - drawProgress,\n                stroke: color,\n                strokeWidth: 10,\n                strokeLinecap: \"round\" as const,\n                fillOpacity: fillProgress ?? 1,\n              }\n            : {})}\n        />\n      ))}\n    </svg>\n  );\n};\n\nconst RealBadge: React.FC<{ name: string; height?: number }> = ({\n  name,\n  height = 26,\n}) => (\n  <Img\n    src={demoAsset(`shieldcn/${name}.svg`)}\n    style={{ height, width: \"auto\", display: \"block\", flex: \"none\" }}\n  />\n);\n\n// ===========================================================================\n// Scene 1 — Intro. A single real shieldcn badge, centered, springing in with\n// a gentle float — the badge plays the role sponsor-ln gave the emoji.\n// ===========================================================================\nconst IntroScene: React.FC = () => {\n  const frame = useCurrentFrame();\n  const { fps } = useVideoConfig();\n  const s = spring({\n    frame,\n    fps,\n    config: { damping: 12, stiffness: 150, mass: 0.9 },\n  });\n  const scale = interpolate(s, [0, 1], [0.4, 1]);\n  const float = Math.sin(frame / 14) * 8;\n  return (\n    <AbsoluteFill style={{ alignItems: \"center\", justifyContent: \"center\" }}>\n      <div\n        style={{\n          opacity: s,\n          transform: `translateY(${float}px) scale(${scale})`,\n          filter: \"drop-shadow(0 22px 44px rgba(0,0,0,0.5))\",\n        }}\n      >\n        <RealBadge name=\"stars-shieldcn\" height={84} />\n      </div>\n    </AbsoluteFill>\n  );\n};\n\n// ===========================================================================\n// Scene 2 — Hook. Text animation via remocn's soft-blur-in typography.\n// ===========================================================================\nconst HookScene: React.FC = () => (\n  <AbsoluteFill style={{ padding: \"0 90px\" }}>\n    <SoftBlurIn\n      text=\"Say hello to my new sponsor\"\n      fontSize={54}\n      fontWeight={600}\n      color={INK}\n      blur={14}\n    />\n  </AbsoluteFill>\n);\n\n// ===========================================================================\n// Scene 3 — Main. The sponsor lockup, same choreography as sponsor-ln:\n//   1. The shield mark + name pop in, centered, and hold briefly.\n//   2. The (mark + name) group slides right while the Remocn wordmark pushes\n//      in from the left and a cross appears between them.\n// ===========================================================================\nconst MARK_SIZE = 168; //      DOM (entrance) size of the shield chip\nconst MARK_FINAL_SIZE = 84; // shrinks to this once settled inline\nconst FINAL_SCALE = MARK_FINAL_SIZE / MARK_SIZE;\n\n// Final-layout horizontal centres (canvas is 1280 wide → centre at 640).\nconst REMOCN_X = 420; // centre of the \"Remocn\" wordmark\n// Right edge of \"Remocn\" as rendered at fontSize 76 / weight 700 (measured).\nconst REMOCN_RIGHT = REMOCN_X + 141;\nconst MARK_X = 750; // centre of the shield chip\n// The SVG box carries ~14px of dead space around the drawn mark at inline\n// size, so a tiny gap keeps the name visually snug against the logo.\nconst NAME_GAP = 2;\nconst CROSS_X = Math.round(\n  (REMOCN_RIGHT + (MARK_X - MARK_FINAL_SIZE / 2)) / 2,\n);\nconst ROW_Y = 350; // vertical centre of the row\n\n// Frame the group starts sliding right (and Remocn / cross start entering) —\n// after the logo has finished drawing itself on.\nconst SPLIT_AT = 60;\n\n// The bare logo mark, exactly as the shieldcn site renders it: currentColor\n// (foreground ink), no chip, no border. Draws itself on as a stroke.\nconst ShieldMark: React.FC<{ draw: number; fill: number }> = ({\n  draw,\n  fill,\n}) => (\n  <div\n    style={{\n      width: MARK_SIZE,\n      height: MARK_SIZE,\n      display: \"flex\",\n      alignItems: \"center\",\n      justifyContent: \"center\",\n      filter: \"drop-shadow(0 18px 40px rgba(0,0,0,0.5))\",\n    }}\n  >\n    <ShieldcnLogo size={MARK_SIZE} drawProgress={draw} fillProgress={fill} />\n  </div>\n);\n\nconst MainScene: React.FC = () => {\n  const frame = useCurrentFrame();\n  const { fps } = useVideoConfig();\n\n  // 1 — The logo draws itself on, centered; the fill and the name resolve\n  // while the stroke completes. Starts after the incoming crossfade ends so\n  // the stroke never draws over the outgoing hook text.\n  const draw = interpolate(frame, [16, 46], [0, 1], {\n    ...clampOpts,\n    easing: Easing.inOut(Easing.cubic),\n  });\n  const fill = interpolate(frame, [38, 54], [0, 1], clampOpts);\n  const nameIn = interpolate(frame, [30, 46], [0, 1], clampOpts);\n\n  // 2 — The group slides from screen centre (640) to its final slot (MARK_X).\n  const slide = spring({\n    frame: frame - SPLIT_AT,\n    fps,\n    config: { damping: 16, stiffness: 110, mass: 1 },\n  });\n  const groupX = interpolate(slide, [0, 1], [640 - MARK_X, 0]);\n  // As it slides right, the chip shrinks down to the inline size.\n  const shrink = interpolate(slide, [0, 1], [1, FINAL_SCALE], clampOpts);\n  const markScale = shrink;\n  // \"shieldcn\" follows the chip's (shrinking) right edge at a fixed gap.\n  const nameLeft = MARK_X + (MARK_SIZE * shrink) / 2 + NAME_GAP;\n\n  // Remocn pushes in from the left, in sync with the slide.\n  const remocnOpacity = interpolate(\n    frame,\n    [SPLIT_AT + 2, SPLIT_AT + 24],\n    [0, 1],\n    clampOpts,\n  );\n  const remocnX = interpolate(frame, [SPLIT_AT + 2, SPLIT_AT + 26], [-36, 0], {\n    ...clampOpts,\n    easing: Easing.out(Easing.cubic),\n  });\n\n  // The cross appears between them, slightly after the split begins.\n  const crossSpring = spring({\n    frame: frame - (SPLIT_AT + 8),\n    fps,\n    config: { damping: 11, stiffness: 170, mass: 0.7 },\n  });\n  const crossOpacity = interpolate(\n    frame,\n    [SPLIT_AT + 8, SPLIT_AT + 20],\n    [0, 1],\n    clampOpts,\n  );\n  const crossScale = interpolate(crossSpring, [0, 1], [0.5, 1]);\n\n  return (\n    <AbsoluteFill>\n      {/* Remocn wordmark — enters from the left. */}\n      <span\n        style={{\n          position: \"absolute\",\n          left: REMOCN_X,\n          top: ROW_Y,\n          transform: `translate(-50%, -50%) translateX(${remocnX}px)`,\n          fontFamily: SANS,\n          fontWeight: 700,\n          fontSize: 76,\n          letterSpacing: \"-0.03em\",\n          color: INK,\n          opacity: remocnOpacity,\n          whiteSpace: \"nowrap\",\n        }}\n      >\n        Remocn\n      </span>\n\n      {/* Cross — appears between the two. */}\n      <span\n        style={{\n          position: \"absolute\",\n          left: CROSS_X,\n          top: ROW_Y,\n          transform: `translate(-50%, -50%) scale(${crossScale})`,\n          fontFamily: SANS,\n          fontWeight: 400,\n          fontSize: 52,\n          color: FAINT,\n          opacity: crossOpacity,\n        }}\n      >\n        ✕\n      </span>\n\n      {/* Shield mark — draws on centred, then slides right with the name. */}\n      <div\n        style={{\n          position: \"absolute\",\n          left: MARK_X,\n          top: ROW_Y,\n          transform: `translate(-50%, -50%) translateX(${groupX}px) scale(${markScale})`,\n        }}\n      >\n        <ShieldMark draw={draw} fill={fill} />\n      </div>\n\n      {/* Sponsor name — sits to the right of the chip and travels with it. */}\n      <span\n        style={{\n          position: \"absolute\",\n          left: nameLeft,\n          top: ROW_Y,\n          transform: `translate(0, -50%) translateX(${groupX}px)`,\n          fontFamily: SANS,\n          fontWeight: 500,\n          fontSize: 44,\n          color: INK,\n          letterSpacing: \"-0.02em\",\n          opacity: nameIn,\n        }}\n      >\n        shieldcn\n      </span>\n    </AbsoluteFill>\n  );\n};\n\n// ===========================================================================\n// Scene 4 — Finale. The badge ballet from the shieldcn demo, with a more\n// cinematic formation: instead of a scattered pop, each badge resolves out of\n// blur + opacity already on the circle, drifting inward from a wider radius\n// while the whole ring slowly winds into place. Then the full turn, the fall\n// into a row, and the ride off-screen — closed by the shieldcn.dev tag.\n// ===========================================================================\nconst BALLET_BADGES = [\n  \"npm-react\",\n  \"stars-nextjs\",\n  \"views-shieldcn\",\n  \"license-shieldcn\",\n  \"stars-shieldcn\",\n  \"npm-react-secondary\",\n  \"npm-react-outline\",\n  \"npm-react-ghost\",\n  \"npm-react-destructive\",\n  \"npm-typescript\",\n];\n\nconst BALLET_N = BALLET_BADGES.length;\nconst BALLET_R = 190; //  circle radius\nconst BALLET_SLOT = 116; // row slot width\n\n// Ballet timeline (scene-local frames)\nconst B_FORM_STAGGER = 3; // per-badge delay of the blur resolve\nconst B_FORM_DUR = 26; //   per-badge blur/opacity resolve length\nconst B_WIND: [number, number] = [0, 64]; // ring slowly winds into place\nconst B_SPIN: [number, number] = [66, 116];\nconst B_ALIGN: [number, number] = [116, 138];\nconst B_EXIT: [number, number] = [140, 160];\n// The outro rides in on the tail of the exiting badge row.\nconst OUTRO_FROM = 154;\n\nconst BadgeBallet: React.FC = () => {\n  const frame = useCurrentFrame();\n  const ease = Easing.inOut(Easing.cubic);\n  // The ring settles from a slight counter-rotation while badges resolve.\n  const wind = interpolate(frame, B_WIND, [-0.4, 0], {\n    ...clampOpts,\n    easing: Easing.out(Easing.cubic),\n  });\n  const rot = interpolate(frame, B_SPIN, [0, Math.PI * 2], {\n    ...clampOpts,\n    easing: ease,\n  });\n  const aP = interpolate(frame, B_ALIGN, [0, 1], {\n    ...clampOpts,\n    easing: ease,\n  });\n  const eP = interpolate(frame, B_EXIT, [0, 1], {\n    ...clampOpts,\n    easing: Easing.in(Easing.cubic),\n  });\n  return (\n    <>\n      {BALLET_BADGES.map((name, i) => {\n        const t0 = 4 + i * B_FORM_STAGGER;\n        // Cinematic formation — each badge resolves out of blur + opacity,\n        // drifting inward from a wider radius onto its circle slot.\n        const form = interpolate(frame, [t0, t0 + B_FORM_DUR], [0, 1], {\n          ...clampOpts,\n          easing: Easing.out(Easing.cubic),\n        });\n        const radius = BALLET_R * (1.35 - 0.35 * form);\n        const blurVal = (1 - form) * 18;\n        const formScale = 1.16 - 0.16 * form;\n\n        const base = -Math.PI / 2 + (i * 2 * Math.PI) / BALLET_N;\n        const cx = Math.cos(base + wind + rot) * radius;\n        const cy = Math.sin(base + wind + rot) * radius;\n        // circle (carrying the spin) → row → exit\n        const rowX = (i - (BALLET_N - 1) / 2) * BALLET_SLOT;\n        const x = cx + (rowX - cx) * aP - eP * 1750;\n        const y = cy + (0 - cy) * aP;\n        return (\n          <div\n            key={name}\n            style={{\n              position: \"absolute\",\n              left: \"50%\",\n              top: \"50%\",\n              transform: `translate(-50%, -50%) translate(${x}px, ${y}px) scale(${formScale})`,\n              opacity: form,\n              filter: blurVal > 0.2 ? `blur(${blurVal}px)` : undefined,\n            }}\n          >\n            <RealBadge name={name} height={26} />\n          </div>\n        );\n      })}\n    </>\n  );\n};\n\n// Outro line — resolves out of blur + opacity. By default it rises gently;\n// with fromX it glides in from the right, as if the exiting badge row pulled\n// it onto the stage.\nconst OutroLine: React.FC<{\n  children: React.ReactNode;\n  delay: number;\n  fromX?: number;\n}> = ({ children, delay, fromX = 0 }) => {\n  const frame = useCurrentFrame();\n  const p = interpolate(frame, [delay, delay + (fromX ? 28 : 20)], [0, 1], {\n    ...clampOpts,\n    easing: Easing.out(Easing.cubic),\n  });\n  const transform = fromX\n    ? `translateX(${(1 - p) * fromX}px)`\n    : `translateY(${(1 - p) * 14}px)`;\n  return (\n    <div\n      style={{\n        opacity: p,\n        transform,\n        filter: p < 1 ? `blur(${(1 - p) * 10}px)` : undefined,\n      }}\n    >\n      {children}\n    </div>\n  );\n};\n\nconst BalletScene: React.FC = () => (\n  <AbsoluteFill style={{ alignItems: \"center\", justifyContent: \"center\" }}>\n    <BadgeBallet />\n    <Sequence from={OUTRO_FROM} layout=\"none\">\n      <div\n        style={{\n          display: \"flex\",\n          flexDirection: \"column\",\n          alignItems: \"center\",\n          gap: 6,\n        }}\n      >\n        <OutroLine delay={0} fromX={420}>\n          <div style={{ display: \"flex\", alignItems: \"center\", gap: 4 }}>\n            <ShieldcnLogo size={76} />\n            <span\n              style={{\n                fontFamily: SANS,\n                fontWeight: 500,\n                fontSize: 54,\n                letterSpacing: \"-0.02em\",\n                color: INK,\n              }}\n            >\n              shieldcn\n            </span>\n          </div>\n        </OutroLine>\n        <OutroLine delay={16}>\n          <span style={{ fontFamily: SANS, fontSize: 19, color: MUTED }}>\n            Badges, charts, headers — as real shadcn/ui components.\n          </span>\n        </OutroLine>\n      </div>\n    </Sequence>\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<TransitionPresentationComponentProps<EmptyProps>> = ({\n  children,\n  presentationProgress,\n  presentationDirection,\n}) => {\n  const entering = presentationDirection === \"entering\";\n  const p = presentationProgress;\n  const style: React.CSSProperties = entering\n    ? {\n        opacity: p,\n        transform: `scale(${0.9 + p * 0.1})`,\n        filter: p < 1 ? `blur(${(1 - p) * 16}px)` : undefined,\n      }\n    : {\n        opacity: 1 - p,\n        transform: `scale(${1 + p * 0.12})`,\n        filter: p > 0 ? `blur(${p * 16}px)` : undefined,\n      };\n  return <AbsoluteFill style={style}>{children}</AbsoluteFill>;\n};\nconst zoomBlur = (): TransitionPresentation<EmptyProps> => ({\n  component: ZoomBlur,\n  props: {},\n});\n\n// ===========================================================================\n// Composition root.\n// ===========================================================================\nexport const SponsorShieldcnDemo: React.FC = () => {\n  return (\n    <AbsoluteFill\n      style={\n        {\n          \"--font-geist-sans\": SANS_FAMILY,\n          background: BG,\n        } as React.CSSProperties\n      }\n    >\n      {/* Persistent shader backdrop — god rays in the zinc register with a\n          whisper of badge green: a slow spotlight for the sponsor reveal. */}\n      <ShaderGodRays\n        speed={0.35}\n        colorBack={BG}\n        colorBloom=\"#14352a\"\n        colors={[\"#1a1a1f\", \"#232329\", \"#2f4a3c\", \"#3b3b44\"]}\n        intensity={0.55}\n        density={0.28}\n        bloom={0.35}\n      />\n      {/* Vignette scrim to focus the center. */}\n      <AbsoluteFill\n        style={{\n          background:\n            \"radial-gradient(120% 120% at 50% 42%, rgba(9,9,11,0.35) 0%, rgba(9,9,11,0.85) 100%)\",\n        }}\n      />\n\n      <TransitionSeries>\n        {/* 1 — Intro badge */}\n        <TransitionSeries.Sequence durationInFrames={S_INTRO}>\n          <IntroScene />\n        </TransitionSeries.Sequence>\n        <TransitionSeries.Transition\n          timing={linearTiming({ durationInFrames: T_ZB })}\n          presentation={zoomBlur()}\n        />\n\n        {/* 2 — Hook text */}\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        {/* 3 — Main sponsor lockup */}\n        <TransitionSeries.Sequence durationInFrames={S_MAIN}>\n          <MainScene />\n        </TransitionSeries.Sequence>\n        <TransitionSeries.Transition\n          timing={linearTiming({ durationInFrames: T_XB })}\n          presentation={crossfade()}\n        />\n\n        {/* 4 — Badge ballet finale */}\n        <TransitionSeries.Sequence durationInFrames={S_BALLET}>\n          <BalletScene />\n        </TransitionSeries.Sequence>\n      </TransitionSeries>\n    </AbsoluteFill>\n  );\n};\n",
      "type": "registry:component",
      "target": "demos/sponsor-shieldcn/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/sponsor-shieldcn/prompt.md",
      "content": "<!-- TODO(draft): placeholder written by AI — replace with the real prompt used to generate this video -->\n\nMake a sponsor shoutout for shieldcn, in their zinc-and-badge-green look, with a slow god-rays shader as a spotlight behind everything. Open on one of their real star-history badges springing in with a gentle float, then \"Say hello to my new sponsor\" resolving in with a soft blur. For the main beat, assemble a Remocn x shieldcn lockup — wordmark slides in, a cross fades between the two names, and their green shield-check chip springs into a ringed circle next to the name. For the finale, do a badge ballet — pull maybe ten of their real badges, have them resolve onto a slowly turning circle, let it do one full turn, then drop the badges into a row that rides off screen, closing on shieldcn.dev. Use remocn components for the badge and ring animations.\n",
      "type": "registry:file",
      "target": "demos/sponsor-shieldcn/prompt.md"
    }
  ],
  "docs": "Register the composition in your Remotion Root:\n\n  import { SponsorShieldcnDemo } from \"@/demos/sponsor-shieldcn\";\n  <Composition id=\"sponsor-shieldcn\" component={SponsorShieldcnDemo} durationInFrames={416} fps={30} width={1280} height={720} />\n\nRequires Tailwind v4 wired into Remotion (@remotion/tailwind-v4).\nRender locally: npx remotion render sponsor-shieldcn out/sponsor-shieldcn.mp4 --gl=angle",
  "type": "registry:block"
}