{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "column-slide",
  "title": "Column Slide",
  "description": "Vertical columns slide in a stagger to swap one headline for another.",
  "dependencies": ["remotion", "@remotion/google-fonts"],
  "files": [
    {
      "path": "registry/remocn/column-slide/index.tsx",
      "content": "\"use client\";\n\nimport { loadFont as loadDisplay } from \"@remotion/google-fonts/BricolageGrotesque\";\nimport type React from \"react\";\nimport { useId } from \"react\";\nimport {\n  AbsoluteFill,\n  Easing,\n  interpolate,\n  useCurrentFrame,\n  useVideoConfig,\n} from \"remotion\";\n\nconst { fontFamily: display } = loadDisplay(\"normal\", {\n  weights: [\"400\", \"500\"],\n  subsets: [\"latin\"],\n});\n\nexport interface ColumnSlideProps {\n  from?: string;\n  to?: string;\n  columns?: number;\n  accent?: string;\n  theme?: \"dark\" | \"light\";\n  speed?: number;\n}\n\nconst CLAMP = {\n  extrapolateLeft: \"clamp\",\n  extrapolateRight: \"clamp\",\n} as const;\n\nconst MOVE_AT = 6;\nconst STEP = 4;\nconst LAND = 42;\nconst SETTLE_END = 66;\n\nconst SLIDE = Easing.bezier(0.72, 0, 0.18, 1);\nconst SETTLE = Easing.bezier(0.22, 1, 0.36, 1);\n\nconst TRACK = -0.035;\n\n/** gradients.sh palettes: \"beam\" (dark) and \"pixel-glass\" (light). */\nconst STAGES = {\n  dark: {\n    bg: \"#0A0A0B\",\n    fg: \"#F3F2EF\",\n    dim: \"rgba(243,242,239,0.1)\",\n    light: \"rgba(255,255,255,0.06)\",\n    ground: \"#02030a\",\n    colors: [\"#f4f7ff\", \"#8fb4ff\", \"#3d5bff\", \"#1a1660\"],\n    blobs: [\n      [80, 4, 30, 34, 0.2],\n      [84, 16, 48, 58, 0.55],\n      [60, 66, 96, 96, 0.9],\n      [8, 16, 72, 84, 1],\n    ],\n    toFg: \"#F7F8FF\",\n  },\n  light: {\n    bg: \"#F5F4F0\",\n    fg: \"#121214\",\n    dim: \"rgba(18,18,20,0.08)\",\n    light: \"rgba(255,255,255,0.9)\",\n    ground: \"#f4f3f1\",\n    colors: [\"#ffffff\", \"#ff4fb8\", \"#3d6bff\", \"#7a3cff\"],\n    blobs: [\n      [74, 8, 46, 40, 0.9],\n      [86, 34, 62, 70, 0.55],\n      [22, 88, 80, 85, 0.72],\n      [8, 18, 70, 80, 0.8],\n    ],\n    toFg: \"#121214\",\n  },\n};\n\ntype Stage = {\n  colors: string[];\n  blobs: number[][];\n  ground: string;\n};\n\n/** Bricolage Grotesque 500 advances (opsz 96), ASCII 32–126, per 1000 em. */\nconst ADV = [\n  211, 220, 274, 584, 563, 913, 677, 149, 267, 271, 391, 486, 150, 300, 178,\n  330, 594, 264, 534, 556, 572, 551, 591, 471, 599, 592, 182, 179, 486, 486,\n  486, 398, 878, 621, 588, 625, 614, 531, 507, 645, 625, 209, 306, 594, 450,\n  784, 664, 657, 566, 656, 591, 573, 504, 620, 606, 897, 579, 556, 534, 276,\n  329, 276, 516, 588, 283, 514, 552, 524, 554, 526, 340, 546, 534, 197, 203,\n  504, 197, 830, 534, 560, 551, 552, 359, 497, 339, 532, 515, 775, 502, 522,\n  454, 323, 193, 331, 486,\n];\n\nfunction widthEm(text: string) {\n  let w = 0;\n  for (const ch of text) {\n    const c = ch.charCodeAt(0);\n    w += (c >= 32 && c < 127 ? ADV[c - 32] : 560) / 1000 + TRACK;\n  }\n  return Math.max(w - TRACK, 0.5);\n}\n\n/** Mesh-style layered radial blobs; positions drift a few % over the shot. */\nfunction ground(stage: Stage, k: number) {\n  const drift = [\n    [-6, 4],\n    [-4, -3],\n    [5, -4],\n    [3, 5],\n  ];\n  const mix = (c: string, a: number) =>\n    `color-mix(in oklab, ${c} ${Math.round(a * 100)}%, transparent)`;\n  return [\n    ...stage.colors.map((c, i) => {\n      const [x, y, w, h, a] = stage.blobs[i];\n      return `radial-gradient(${w}% ${h}% at ${x + drift[i][0] * k}% ${y + drift[i][1] * k}%, ${mix(c, a)} 0%, transparent 68%)`;\n    }),\n    stage.ground,\n  ].join(\", \");\n}\n\nfunction Grain({ opacity }: { opacity: number }) {\n  const id = `g${useId().replace(/[^a-zA-Z0-9_-]/g, \"\")}`;\n  return (\n    <svg\n      width=\"100%\"\n      height=\"100%\"\n      style={{ position: \"absolute\", inset: 0, opacity, pointerEvents: \"none\" }}\n      aria-hidden\n    >\n      <filter id={id}>\n        <feTurbulence\n          type=\"fractalNoise\"\n          baseFrequency=\"0.9\"\n          numOctaves={2}\n          seed={13}\n          stitchTiles=\"stitch\"\n        />\n        <feColorMatrix values=\"0 0 0 0 0.5  0 0 0 0 0.5  0 0 0 0 0.5  0 0 0 0.9 0\" />\n      </filter>\n      <rect width=\"100%\" height=\"100%\" filter={`url(#${id})`} />\n    </svg>\n  );\n}\n\nfunction Scene({\n  W,\n  H,\n  left,\n  top,\n  background,\n  overlay,\n  text,\n  size,\n  color,\n  scale,\n}: {\n  W: number;\n  H: number;\n  left: number;\n  top: number;\n  background: string;\n  overlay: string;\n  text: string;\n  size: number;\n  color: string;\n  scale: number;\n}) {\n  const label: React.CSSProperties = {\n    position: \"absolute\",\n    left: 0,\n    width: W,\n    top: H / 2 - 0.56 * size,\n    textAlign: \"center\",\n    fontFamily: display,\n    fontWeight: 500,\n    fontSize: size,\n    letterSpacing: `${TRACK}em`,\n    lineHeight: 1,\n    whiteSpace: \"nowrap\",\n    color,\n    paddingBottom: 0.12 * size,\n    transform: `scale(${scale})`,\n    transformOrigin: `${W / 2}px ${0.56 * size}px`,\n  };\n  return (\n    <div\n      style={{\n        position: \"absolute\",\n        left,\n        top,\n        width: W,\n        height: H,\n        background,\n      }}\n    >\n      <div style={{ position: \"absolute\", inset: 0, background: overlay }} />\n      <div style={label}>{text}</div>\n    </div>\n  );\n}\n\nexport function ColumnSlide({\n  from = \"Plan the launch\",\n  to = \"Launch day\",\n  columns = 5,\n  accent = \"#3B5BFD\",\n  theme = \"dark\",\n  speed = 1,\n}: ColumnSlideProps) {\n  const t = useCurrentFrame() * speed;\n  const { width: W, height: H } = useVideoConfig();\n  const u = H / 720;\n  const stage = STAGES[theme];\n  const n = Math.min(7, Math.max(3, Math.round(columns)));\n\n  const fsA = Math.min(128 * u, (0.8 * W) / widthEm(from));\n  const fsB = Math.min(140 * u, (0.8 * W) / widthEm(to));\n  const driftA = 1 + 0.03 * SLIDE(interpolate(t, [0, LAND], [0, 1], CLAMP));\n  const settle = SETTLE(interpolate(t, [LAND - 14, SETTLE_END], [0, 1], CLAMP));\n  const scaleB = 1.05 - 0.05 * settle;\n  const seam = Math.max(2, 3 * u);\n\n  const lightA = `radial-gradient(ellipse 80% 90% at 24% 0%, ${stage.light}, transparent 70%)`;\n  const sheenB =\n    \"linear-gradient(180deg, rgba(255,255,255,0.05), rgba(255,255,255,0) 45%, rgba(0,0,0,0.08))\";\n  const guides = interpolate(t, [0, MOVE_AT], [0.35, 1], CLAMP);\n  const groundB = ground(stage, interpolate(t, [0, 96], [0, 1], CLAMP));\n\n  const cols = Array.from({ length: n }, (_, k) => {\n    const x0 = Math.round((k * W) / n);\n    const x1 = Math.round(((k + 1) * W) / n);\n    const order = k % 2 === 0 ? k / 2 : Math.floor(n / 2) + (k + 1) / 2;\n    const start = MOVE_AT + order * STEP;\n    const p = SLIDE(interpolate(t, [start, LAND], [0, 1], CLAMP));\n    const up = k % 2 === 0;\n    const yA = up ? -p * H : p * H;\n    const yB = up ? H - p * H : -H + p * H;\n    const moving = p > 0 && p < 1;\n    const seamY = up ? yB : yA;\n    return { k, x0, x1, yA, yB, p, moving, seamY, up };\n  });\n\n  return (\n    <AbsoluteFill style={{ background: stage.bg, overflow: \"hidden\" }}>\n      {cols.map((c) => (\n        <div\n          key={c.k}\n          style={{\n            position: \"absolute\",\n            left: c.x0,\n            top: 0,\n            width: c.x1 - c.x0,\n            height: H,\n            overflow: \"hidden\",\n          }}\n        >\n          {c.p < 1 ? (\n            <Scene\n              W={W}\n              H={H}\n              left={-c.x0}\n              top={c.yA}\n              background={stage.bg}\n              overlay={lightA}\n              text={from}\n              size={fsA}\n              color={stage.fg}\n              scale={driftA}\n            />\n          ) : null}\n          {c.p > 0 ? (\n            <Scene\n              W={W}\n              H={H}\n              left={-c.x0}\n              top={c.yB}\n              background={groundB}\n              overlay={sheenB}\n              text={to}\n              size={fsB}\n              color={stage.toFg}\n              scale={scaleB}\n            />\n          ) : null}\n          {c.moving ? (\n            <div\n              style={{\n                position: \"absolute\",\n                left: 0,\n                right: 0,\n                top: c.up ? c.seamY : c.seamY - seam,\n                height: seam,\n                background: accent,\n              }}\n            />\n          ) : null}\n          {c.k > 0 && c.p < 1 ? (\n            <div\n              style={{\n                position: \"absolute\",\n                left: 0,\n                top: c.yA,\n                width: Math.max(1, u),\n                height: H,\n                background: stage.dim,\n                opacity: guides,\n              }}\n            />\n          ) : null}\n        </div>\n      ))}\n      <Grain opacity={theme === \"light\" ? 0.04 : 0.055} />\n    </AbsoluteFill>\n  );\n}\n",
      "type": "registry:component",
      "target": "components/remocn/column-slide.tsx"
    }
  ],
  "type": "registry:component"
}
