{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "diagonal-split",
  "title": "Diagonal Split",
  "description": "A diagonal seam slides one scene away to reveal the next with a warm band.",
  "dependencies": ["remotion", "@remotion/google-fonts"],
  "files": [
    {
      "path": "registry/remocn/diagonal-split/index.tsx",
      "content": "\"use client\";\n\nimport { loadFont as loadDisplay } from \"@remotion/google-fonts/Geist\";\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: [\"500\"],\n  subsets: [\"latin\"],\n});\n\nexport interface DiagonalSplitProps {\n  from?: React.ReactNode;\n  to?: React.ReactNode;\n  angle?: 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 SEAM_AT = 12;\nconst SEAM_END = 22;\nconst SLIDE_AT = 16;\nconst SLIDE_END = 52;\n\nconst SEAM = Easing.bezier(0.22, 1, 0.36, 1);\nconst SLIDE = Easing.bezier(0.66, 0, 0.36, 1);\nconst BAND_OUT = Easing.bezier(0.5, 0, 0.2, 1);\nconst SETTLE = Easing.bezier(0.22, 1, 0.36, 1);\n\nconst B_TONE = { bg: \"#050507\", fg: \"#F3F2EF\", light: \"rgba(255,255,255,0)\" };\n\nconst STAGES = {\n  dark: {\n    a: { bg: \"#0A0A0B\", fg: \"#F3F2EF\", light: \"rgba(255,255,255,0.055)\" },\n    b: B_TONE,\n    edge: \"rgba(255,226,196,0.5)\",\n    shadow: \"rgba(0,0,0,0.5)\",\n  },\n  light: {\n    a: { bg: \"#F5F4F0\", fg: \"#121214\", light: \"rgba(255,255,255,0.9)\" },\n    b: B_TONE,\n    edge: \"rgba(255,226,196,0.9)\",\n    shadow: \"rgba(0,0,0,0.36)\",\n  },\n};\n\n// \"fluted\" palette from gradients.sh (bg #050507, #1d2a8f, #2f7bff → accent, #ff7a3d, #ffe2c4):\n// cool light from the top-left, a warm streak running parallel to the seam, and faint flutes\n// along the same angle, so the revealed ground carries the cut's direction. Drifts a few percent.\nfunction flutedGround(angle: number, accent: string, k: number, u: number) {\n  const n = 180 - angle;\n  const c = 80 - 3 * k;\n  // Unit normal of the seam in screen space: the warm streak sits on its + side, the cool\n  // light on the opposite side, so the two never mix into mud whatever the angle.\n  const nx = Math.sin((n * Math.PI) / 180);\n  const ny = -Math.cos((n * Math.PI) / 180);\n  const at = (d: number) =>\n    `${(50 + d * nx).toFixed(1)}% ${(50 + d * ny).toFixed(1)}%`;\n  return [\n    `repeating-linear-gradient(${n}deg, rgba(255,255,255,0.03) 0px, rgba(255,255,255,0.03) ${u}px, rgba(255,255,255,0) ${u}px, rgba(255,255,255,0) ${22 * u}px)`,\n    `linear-gradient(${n}deg, rgba(255,122,61,0) ${c - 16}%, rgba(255,122,61,0.5) ${c - 5}%, rgba(255,226,196,0.72) ${c}%, rgba(255,122,61,0.45) ${c + 5}%, rgba(255,122,61,0) ${c + 15}%)`,\n    `radial-gradient(70% 80% at ${at(-34 + 4 * k)}, color-mix(in oklab, ${accent} 55%, transparent) 0%, rgba(47,123,255,0) 70%)`,\n    `radial-gradient(95% 110% at ${at(-62)}, rgba(29,42,143,0.95) 0%, rgba(29,42,143,0) 72%)`,\n    `radial-gradient(60% 70% at ${at(66)}, rgba(255,122,61,0.28) 0%, rgba(255,122,61,0) 70%)`,\n    \"#050507\",\n  ].join(\", \");\n}\n\n// Geist 500 advances (1/1000 em, ASCII 32–126), from HarfBuzz.\nconst GEIST_ADV = [\n  243, 228, 361, 515, 643, 810, 649, 186, 290, 290, 427, 562, 213, 418, 213,\n  494, 673, 406, 630, 625, 629, 641, 604, 531, 624, 606, 302, 302, 546, 544,\n  546, 570, 925, 689, 688, 713, 701, 609, 595, 713, 716, 280, 607, 656, 583,\n  890, 745, 751, 657, 745, 680, 654, 568, 694, 688, 968, 633, 594, 561, 361,\n  470, 361, 438, 558, 258, 565, 608, 563, 608, 576, 412, 607, 591, 256, 284,\n  609, 282, 885, 591, 588, 608, 608, 394, 537, 410, 586, 560, 829, 607, 553,\n  552, 395, 274, 395, 523,\n];\nconst TRACK = -0.035;\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 ? GEIST_ADV[c - 32] : 560) / 1000 + TRACK;\n  }\n  return Math.max(w, 0.5);\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={3}\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  content,\n  tone,\n  max,\n  bare = false,\n  ground,\n}: {\n  content: React.ReactNode;\n  tone: { bg: string; fg: string; light: string };\n  max: number;\n  bare?: boolean;\n  ground?: string;\n}) {\n  const { width: W, height: H } = useVideoConfig();\n  const u = H / 720;\n  return (\n    <AbsoluteFill\n      style={{ background: bare ? undefined : (ground ?? tone.bg) }}\n    >\n      {bare || ground ? null : (\n        <AbsoluteFill\n          style={{\n            background: `radial-gradient(ellipse 80% 90% at 24% 0%, ${tone.light}, transparent 70%)`,\n          }}\n        />\n      )}\n      {typeof content === \"string\" ? (\n        <AbsoluteFill\n          style={{ alignItems: \"center\", justifyContent: \"center\" }}\n        >\n          <div\n            style={{\n              fontFamily: display,\n              fontWeight: 500,\n              fontSize: Math.min(max * u, (0.82 * W) / widthEm(content)),\n              letterSpacing: `${TRACK}em`,\n              lineHeight: 1,\n              whiteSpace: \"nowrap\",\n              color: tone.fg,\n              paddingBottom: 0.06 * Math.min(max * u, W / widthEm(content)),\n            }}\n          >\n            {content}\n          </div>\n        </AbsoluteFill>\n      ) : (\n        <AbsoluteFill>{content}</AbsoluteFill>\n      )}\n    </AbsoluteFill>\n  );\n}\n\nexport function DiagonalSplit({\n  from = \"Built in a weekend\",\n  to = \"Launched on Monday\",\n  angle = 28,\n  accent = \"#3B5BFD\",\n  theme = \"dark\",\n  speed = 1,\n}: DiagonalSplitProps) {\n  const t = useCurrentFrame() * speed;\n  const { width: W, height: H } = useVideoConfig();\n  const u = H / 720;\n  const stage = STAGES[theme];\n\n  const rad = (angle * Math.PI) / 180;\n  const dx = Math.cos(rad);\n  const dy = -Math.sin(rad);\n  const nx = dy;\n  const ny = -dx;\n  const cx = W / 2;\n  const cy = H / 2;\n  const L = 2 * (W + H);\n\n  const seam = SEAM(interpolate(t, [SEAM_AT, SEAM_END], [0, 1], CLAMP));\n  const slide = SLIDE(interpolate(t, [SLIDE_AT, SLIDE_END], [0, 1], CLAMP));\n  const reachN = (W * Math.abs(nx) + H * Math.abs(ny)) / 2;\n  const travel = 0.32 * W * slide;\n  const gap = 18 * u * seam + 2.3 * reachN * slide;\n  const bandOut = BAND_OUT(interpolate(t, [24, 42], [0, 1], CLAMP));\n  const band = 18 * u * seam * (1 - bandOut);\n  const settle = SETTLE(\n    interpolate(t, [SLIDE_AT, SLIDE_END + 18], [0, 1], CLAMP),\n  );\n  const bScale = 1.05 - 0.05 * settle;\n  const gone = slide >= 0.999;\n  const deg = -angle;\n\n  const halfPoly = (side: 1 | -1) => {\n    const ax = cx - dx * L;\n    const ay = cy - dy * L;\n    const bx = cx + dx * L;\n    const by = cy + dy * L;\n    const ox = nx * L * side;\n    const oy = ny * L * side;\n    return `polygon(${[\n      [ax, ay],\n      [bx, by],\n      [bx + ox, by + oy],\n      [ax + ox, ay + oy],\n    ]\n      .map(([x, y]) => `${x.toFixed(2)}px ${y.toFixed(2)}px`)\n      .join(\", \")})`;\n  };\n\n  const half = (side: 1 | -1) => {\n    const along = side * travel;\n    const tx = dx * along + nx * side * (gap / 2);\n    const ty = dy * along + ny * side * (gap / 2);\n    return (\n      <AbsoluteFill\n        style={{\n          transform: `translate(${tx}px, ${ty}px)`,\n          filter:\n            seam > 0\n              ? `drop-shadow(${nx * side * 8 * u}px ${ny * side * 8 * u}px ${22 * u}px ${stage.shadow})`\n              : undefined,\n        }}\n      >\n        <AbsoluteFill style={{ clipPath: halfPoly(side) }}>\n          <div\n            style={{\n              position: \"absolute\",\n              left: -W,\n              top: -H,\n              width: 3 * W,\n              height: 3 * H,\n              background: `radial-gradient(ellipse ${0.8 * W}px ${0.9 * H}px at ${1.24 * W}px ${H}px, ${stage.a.light}, transparent 70%), ${stage.a.bg}`,\n            }}\n          />\n          <Scene content={from} tone={stage.a} max={134} bare />\n          <div\n            style={{\n              position: \"absolute\",\n              left: cx - L / 2,\n              top: cy - (side === 1 ? 1 : 0) * u,\n              width: L,\n              height: u,\n              background: stage.edge,\n              opacity: seam,\n              transformOrigin: \"50% 0\",\n              transform: `rotate(${deg}deg)`,\n            }}\n          />\n        </AbsoluteFill>\n      </AbsoluteFill>\n    );\n  };\n\n  return (\n    <AbsoluteFill style={{ background: stage.b.bg, overflow: \"hidden\" }}>\n      <AbsoluteFill style={{ transform: `scale(${bScale})` }}>\n        <Scene\n          content={to}\n          tone={stage.b}\n          max={134}\n          ground={flutedGround(\n            angle,\n            accent,\n            interpolate(t, [SLIDE_AT, SLIDE_END + 30], [0, 1], CLAMP),\n            u,\n          )}\n        />\n      </AbsoluteFill>\n      {band > 0.2 ? (\n        <div\n          style={{\n            position: \"absolute\",\n            left: cx - L / 2,\n            top: cy - band / 2,\n            width: L,\n            height: band,\n            background:\n              \"linear-gradient(180deg, #ff7a3d 0%, #ffe2c4 50%, #ff7a3d 100%)\",\n            transform: `rotate(${deg}deg)`,\n          }}\n        />\n      ) : null}\n      {seam <= 0 ? <Scene content={from} tone={stage.a} max={134} /> : null}\n      {gone || seam <= 0 ? null : (\n        <>\n          {half(1)}\n          {half(-1)}\n        </>\n      )}\n      <Grain opacity={theme === \"light\" ? 0.04 : 0.055} />\n    </AbsoluteFill>\n  );\n}\n",
      "type": "registry:component",
      "target": "components/remocn/diagonal-split.tsx"
    }
  ],
  "type": "registry:component"
}
