{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "split-word",
  "title": "Split Word",
  "description": "A huge word splits open and a second word slides into the gap with a gradient band.",
  "dependencies": ["remotion"],
  "files": [
    {
      "path": "registry/remocn/split-word/index.tsx",
      "content": "\"use client\";\n\nimport { useId } from \"react\";\nimport {\n  AbsoluteFill,\n  Easing,\n  interpolate,\n  useCurrentFrame,\n  useVideoConfig,\n} from \"remotion\";\n\nexport interface SplitWordProps {\n  word?: string;\n  insert?: string;\n  accent?: string;\n  theme?: \"dark\" | \"light\";\n  speed?: number;\n}\n\n// [advance, inkLeft, inkRight, inkTop, inkBottom, outline] per 1000 em,\n// baseline at y = 0, y down. Overlaps removed (fontTools + skia-pathops).\ntype Glyph = readonly [number, number, number, number, number, string];\n\nconst GLYPHS: Record<string, Glyph> = {\n  A: [\n    632,\n    21,\n    610,\n    700,\n    0,\n    \"M21 0 221 -700H411L610 0H499L453 -165H179L133 0ZM206 -265H426L324 -633H308Z\",\n  ],\n  B: [\n    663,\n    47,\n    615,\n    700,\n    0,\n    \"M47 0V-96H141V-604H47V-700H402Q465 -700 512 -678Q559 -657 586 -618Q612 -578 612 -524V-514Q612 -466 594 -435Q576 -404 550 -387Q525 -370 502 -363V-347Q525 -341 552 -324Q578 -306 596 -275Q615 -244 615 -194V-184Q615 -126 588 -85Q561 -44 514 -22Q466 0 404 0ZM250 -98H391Q445 -98 476 -124Q507 -150 507 -196V-205Q507 -251 476 -277Q446 -303 391 -303H250ZM250 -402H391Q442 -402 473 -428Q504 -453 504 -497V-507Q504 -551 474 -576Q443 -602 391 -602H250Z\",\n  ],\n  C: [\n    643,\n    55,\n    596,\n    714,\n    14,\n    \"M333 14Q205 14 130 -58Q55 -131 55 -268V-432Q55 -569 130 -642Q205 -714 333 -714Q459 -714 528 -644Q596 -574 596 -453V-447H489V-456Q489 -527 450 -572Q412 -617 333 -617Q253 -617 208 -568Q163 -520 163 -434V-266Q163 -181 208 -132Q253 -83 333 -83Q412 -83 450 -128Q489 -174 489 -244V-261H596V-247Q596 -126 528 -56Q459 14 333 14Z\",\n  ],\n  D: [\n    665,\n    47,\n    616,\n    700,\n    0,\n    \"M47 0V-96H141V-604H47V-700H327Q467 -700 542 -629Q616 -558 616 -415V-284Q616 -141 542 -70Q467 0 327 0ZM250 -98H328Q419 -98 463 -144Q507 -190 507 -281V-419Q507 -511 463 -556Q419 -601 328 -601H250Z\",\n  ],\n  E: [\n    559,\n    73,\n    519,\n    700,\n    0,\n    \"M73 0V-700H514V-602H181V-401H486V-303H181V-98H519V0Z\",\n  ],\n  F: [537, 73, 505, 700, 0, \"M73 0V-700H505V-602H181V-400H479V-302H181V0Z\"],\n  G: [\n    661,\n    55,\n    595,\n    714,\n    14,\n    \"M316 14Q241 14 182 -18Q123 -51 89 -114Q55 -177 55 -268V-432Q55 -569 130 -642Q205 -714 333 -714Q459 -714 527 -646Q595 -577 595 -462V-457H488V-465Q488 -508 471 -542Q454 -577 420 -597Q386 -617 333 -617Q253 -617 208 -568Q163 -520 163 -434V-266Q163 -181 208 -132Q253 -82 334 -82Q414 -82 451 -126Q488 -170 488 -240V-253H300V-346H595V0H495V-71H479Q470 -52 452 -32Q435 -12 402 1Q370 14 316 14Z\",\n  ],\n  H: [\n    656,\n    73,\n    583,\n    700,\n    0,\n    \"M73 0V-700H181V-400H475V-700H583V0H475V-302H181V0Z\",\n  ],\n  I: [254, 73, 181, 700, 0, \"M73 0V-700H181V0Z\"],\n  J: [\n    602,\n    36,\n    559,\n    700,\n    14,\n    \"M265 14Q160 14 98 -44Q36 -103 36 -210V-270H144V-210Q144 -154 174 -120Q205 -87 263 -87Q319 -87 346 -120Q373 -153 373 -210V-602H251V-700H559V-602H481V-210Q481 -101 424 -44Q368 14 265 14Z\",\n  ],\n  K: [\n    618,\n    73,\n    610,\n    700,\n    0,\n    \"M73 0V-700H181V-409H197L458 -700H599L283 -355L610 0H465L197 -297H181V0Z\",\n  ],\n  L: [544, 73, 518, 700, 0, \"M73 0V-700H181V-98H518V0Z\"],\n  M: [\n    874,\n    73,\n    801,\n    700,\n    0,\n    \"M73 0V-700H276L429 -72H445L598 -700H801V0H696V-624H680L528 0H346L194 -624H178V0Z\",\n  ],\n  N: [\n    667,\n    73,\n    594,\n    700,\n    0,\n    \"M73 0V-700H281L471 -72H487V-700H594V0H386L196 -629H180V0Z\",\n  ],\n  O: [\n    673,\n    55,\n    618,\n    714,\n    14,\n    \"M336 14Q207 14 131 -58Q55 -131 55 -268V-432Q55 -569 131 -642Q207 -714 336 -714Q465 -714 542 -642Q618 -569 618 -432V-268Q618 -131 542 -58Q465 14 336 14ZM336 -83Q419 -83 464 -132Q509 -180 509 -264V-436Q509 -520 464 -568Q419 -617 336 -617Q255 -617 209 -568Q163 -520 163 -436V-264Q163 -180 209 -132Q255 -83 336 -83Z\",\n  ],\n  P: [\n    603,\n    73,\n    573,\n    700,\n    0,\n    \"M73 0V-700H351Q415 -700 466 -674Q516 -649 544 -602Q573 -556 573 -491V-478Q573 -413 544 -366Q514 -319 464 -294Q414 -269 351 -269H181V0ZM181 -367H340Q397 -367 431 -397Q465 -427 465 -479V-489Q465 -542 431 -572Q397 -602 340 -602H181Z\",\n  ],\n  Q: [\n    673,\n    55,\n    618,\n    714,\n    171,\n    \"M379 171Q336 171 310 144Q283 118 283 73V11Q191 -2 131 -59Q55 -131 55 -268V-432Q55 -569 131 -642Q207 -714 336 -714Q465 -714 542 -642Q618 -569 618 -432V-268Q618 -131 541 -59Q481 -1 389 11V55Q389 85 417 85H479V171ZM336 -83Q419 -83 464 -132Q509 -180 509 -264V-436Q509 -520 464 -568Q419 -617 336 -617Q255 -617 209 -568Q163 -520 163 -436V-264Q163 -180 209 -132Q255 -83 336 -83Z\",\n  ],\n  R: [\n    632,\n    73,\n    586,\n    700,\n    0,\n    \"M73 0V-700H367Q432 -700 482 -678Q531 -655 558 -613Q586 -571 586 -513V-502Q586 -436 554 -396Q523 -357 478 -341V-325Q516 -323 539 -300Q562 -276 562 -232V0H453V-217Q453 -244 439 -260Q425 -276 394 -276H181V0ZM181 -374H355Q413 -374 445 -404Q477 -433 477 -484V-492Q477 -543 446 -572Q414 -602 355 -602H181Z\",\n  ],\n  S: [\n    609,\n    41,\n    569,\n    714,\n    14,\n    \"M311 14Q232 14 171 -14Q110 -42 76 -96Q41 -151 41 -229V-254H148V-229Q148 -155 192 -118Q237 -81 311 -81Q386 -81 424 -112Q462 -144 462 -194Q462 -227 444 -248Q426 -269 393 -282Q360 -294 314 -305L277 -314Q210 -330 160 -354Q111 -377 84 -416Q57 -455 57 -516Q57 -578 88 -622Q118 -666 172 -690Q226 -714 299 -714Q372 -714 430 -689Q487 -664 520 -615Q553 -566 553 -492V-456H446V-492Q446 -537 428 -565Q409 -593 376 -606Q343 -619 299 -619Q235 -619 200 -592Q164 -566 164 -519Q164 -487 180 -466Q195 -446 225 -433Q255 -420 300 -410L337 -401Q405 -386 457 -362Q509 -339 539 -299Q569 -259 569 -196Q569 -133 538 -86Q506 -39 448 -12Q390 14 311 14Z\",\n  ],\n  T: [588, 25, 563, 700, 0, \"M240 0V-602H25V-700H563V-602H348V0Z\"],\n  U: [\n    669,\n    68,\n    601,\n    700,\n    14,\n    \"M335 14Q252 14 192 -16Q133 -46 100 -102Q68 -159 68 -240V-700H177V-237Q177 -163 218 -123Q259 -83 335 -83Q411 -83 452 -123Q493 -163 493 -237V-700H601V-240Q601 -159 569 -102Q537 -46 477 -16Q417 14 335 14Z\",\n  ],\n  V: [\n    619,\n    21,\n    597,\n    700,\n    0,\n    \"M216 0 21 -700H133L303 -68H316L485 -700H597L403 0Z\",\n  ],\n  W: [\n    887,\n    33,\n    853,\n    700,\n    0,\n    \"M136 0 33 -700H141L225 -73H241L349 -700H538L646 -73H662L745 -700H853L750 0H561L451 -636H435L326 0Z\",\n  ],\n  X: [\n    641,\n    27,\n    614,\n    700,\n    0,\n    \"M27 0 242 -353 30 -700H154L312 -430H328L487 -700H611L399 -353L614 0H489L328 -276H312L151 0Z\",\n  ],\n  Y: [\n    617,\n    11,\n    606,\n    700,\n    0,\n    \"M254 0V-254L11 -700H133L301 -383H316L484 -700H606L363 -254V0Z\",\n  ],\n  Z: [\n    577,\n    48,\n    531,\n    700,\n    0,\n    \"M48 0V-140L416 -589V-603H59V-700H527V-560L159 -111V-97H531V0Z\",\n  ],\n};\nconst KERN_PAIRS =\n  \"AC-8 AG-8 AO-8 AQ-8 AT-70 AU-12 AV-49 AW-22 AY-74 BA-5 BT-13 BV-12 BX-14 BY-15 CA-7 CT-15 CV-7 CX-9 CY-17 CZ-6 DA-8 DB-23 DD-23 DT-29 DV-15 DW-7 DX-24 DY-28 DZ-14 FA-44 FC-7 FG-7 FO-7 FQ-7 GT-29 GV-18 GW-4 GY-33 JA-35 JC-16 JG-16 JJ-31 JO-16 JQ-16 KC-30 KG-30 KO-30 KQ-30 KS-6 LC-22 LG-22 LO-22 LQ-22 LT-93 LU-18 LV-74 LW-25 LY-118 OA-8 OB-23 OD-23 OT-29 OV-15 OW-7 OX-24 OY-28 OZ-14 PA-55 PB-4 PD-4 PJ-48 PV-5 PX-21 PY-7 PZ-19 QA-8 QB-23 QD-23 QT-29 QV-15 QW-7 QX-24 QY-28 QZ-14 RT-11 RV-12 RY-16 SB-4 SD-4 ST-26 SV-11 SX-10 SY-21 TA-70 TC-29 TG-29 TJ-87 TO-29 TQ-29 TS-15 UA-12 VA-49 VC-15 VG-15 VO-15 VQ-15 VS-9 WA-22 WC-7 WG-7 WO-7 WQ-7 XC-24 XG-24 XJ-8 XO-24 XQ-24 XS-9 YA-74 YC-28 YG-28 YJ-95 YO-28 YQ-28 YS-19 ZC-14 ZG-14 ZO-14 ZQ-14\";\n\nconst KERN = new Map(\n  KERN_PAIRS.split(\" \").map((p) => [p.slice(0, 2), Number(p.slice(2))]),\n);\n// Space Grotesk 500: display caps want a hair of negative tracking.\nconst TRACK = -14;\nconst SPACE = 255;\nconst CAP = 700;\nconst HALF = CAP / 2;\n// Band height in insert cap-heights once the slit settles.\nconst BAND = 1.66;\n\nconst EASE_IO = Easing.bezier(0.65, 0, 0.35, 1);\nconst EASE_OUT = Easing.bezier(0.22, 1, 0.36, 1);\nconst CLAMP = {\n  extrapolateLeft: \"clamp\",\n  extrapolateRight: \"clamp\",\n} as const;\n\nconst THEMES = {\n  dark: {\n    bg: \"#0A0A0B\",\n    key: \"rgba(255,255,255,0.05)\",\n    fg: \"#F3F2EF\",\n    sky: \"#03050b\",\n    skyAlpha: 58,\n    ins: \"#F7F6F2\",\n    grain: 0.07,\n  },\n  light: {\n    bg: \"#F5F4F0\",\n    key: \"rgba(255,255,255,0.9)\",\n    fg: \"#121214\",\n    sky: \"#03050b\",\n    skyAlpha: 62,\n    ins: \"#F7F6F2\",\n    grain: 0.045,\n  },\n} as const;\n\n// gradients.sh \"aurora\": the slit opens onto this sky.\nconst AURORA = [\"#a0f0c8\", \"#3fb5a3\", \"#5147b6\", \"#b25c9c\"] as const;\n\ninterface SetWord {\n  glyphs: { d: string; x: number }[];\n  left: number;\n  ink: number;\n  first: number;\n}\n\nfunction setWord(word: string): SetWord {\n  const glyphs: { d: string; x: number }[] = [];\n  let x = 0;\n  let prev = \"\";\n  let left = 0;\n  let right = 0;\n  let first = 0;\n  for (const ch of word.toUpperCase()) {\n    const g = GLYPHS[ch];\n    if (!g) {\n      x += SPACE;\n      prev = \"\";\n      continue;\n    }\n    x += KERN.get(prev + ch) ?? 0;\n    if (glyphs.length === 0) {\n      left = x + g[1];\n      first = g[0] + TRACK;\n    }\n    right = x + g[2];\n    glyphs.push({ d: g[5], x });\n    x += g[0] + TRACK;\n    prev = ch;\n  }\n  return { glyphs, left, ink: Math.max(1, right - left), first };\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={17}\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\nexport function SplitWord({\n  word = \"Now\",\n  insert = \"Available\",\n  accent = \"#3B5BFD\",\n  theme = \"dark\",\n  speed = 1,\n}: SplitWordProps) {\n  const t = useCurrentFrame() * speed;\n  const { width: W, height: H } = useVideoConfig();\n  const u = H / 720;\n  const c = THEMES[theme];\n  const uid = useId().replace(/[^a-zA-Z0-9_-]/g, \"\");\n  const ids = {\n    box: `b${uid}`,\n    top: `t${uid}`,\n    bottom: `d${uid}`,\n    band: `s${uid}`,\n  };\n\n  const hero = setWord(word || \"remocn\");\n  const ins = setWord(insert || \" \");\n  const m = Math.round(Math.min(W, H) * 0.115);\n  const Wc = W - m * 2;\n  const Hc = H - m * 2;\n\n  let sh = Wc / (hero.ink + hero.first);\n  let si = Math.min(Wc / ins.ink, sh * 0.56);\n  const blockH = (a: number, b: number) => CAP * a + CAP * b * BAND;\n  const fit = Math.min(1, Hc / blockH(sh, si));\n  sh *= fit;\n  si *= fit;\n\n  const capI = CAP * si;\n  const off = hero.first * sh;\n  const heroW = hero.ink * sh;\n  const insW = ins.ink * si;\n  const gapEnd = capI * BAND;\n  const gapOpen = gapEnd * 1.3;\n  const x0 = (W - heroW) / 2;\n  const cy = H / 2;\n\n  const cut = interpolate(t, [20, 30], [0, 1], { ...CLAMP, easing: EASE_IO });\n  const open = interpolate(t, [30, 54], [0, 1], { ...CLAMP, easing: EASE_IO });\n  const back = interpolate(t, [68, 94], [0, 1], { ...CLAMP, easing: EASE_IO });\n\n  const shift = (off / 2) * (open - 2 * back);\n  const gap = gapOpen * open + (gapEnd - gapOpen) * back;\n\n  const topX = x0 - shift;\n  const bottomX = x0 + shift;\n  const topY = cy - gap / 2;\n  const bottomY = cy + gap / 2;\n\n  const rise = (i: number) =>\n    interpolate(t, [i * 3 - 6, i * 3 + 12], [1, 0], {\n      ...CLAMP,\n      easing: EASE_OUT,\n    });\n\n  const letters = hero.glyphs.map((g, i) => (\n    <path\n      key={`${i}-${g.x}`}\n      d={g.d}\n      transform={`translate(${g.x} ${rise(i) * (CAP + 40)})`}\n    />\n  ));\n\n  const half = (x: number, baselineShift: number, clip: string) => (\n    <g\n      transform={`translate(${x - hero.left * sh} ${baselineShift}) scale(${sh})`}\n      clipPath={`url(#${clip})`}\n      fill={c.fg}\n    >\n      <g clipPath={`url(#${ids.box})`}>{letters}</g>\n    </g>\n  );\n\n  const insX = (W - insW) / 2 - ins.left * si;\n  const hair = Math.max(1.5, 2 * u);\n\n  // The aurora is a fixed full-frame sky; only the slit shows it. Blobs\n  // drift a few percent across the shot so the window feels alive.\n  const drift = interpolate(t, [0, 126], [0, 1], CLAMP);\n  const mixA = (col: string, a: number) =>\n    `color-mix(in oklab, ${col} ${a}%, transparent)`;\n  const sky = [\n    ...AURORA.map((col, i) => {\n      const x = 15 + (70 * i) / (AURORA.length - 1) + (i % 2 ? -4 : 4) * drift;\n      const y = 50 + (i % 2 ? 6 : -6) * (1 - drift);\n      return `radial-gradient(34% 80% at ${x}% ${y}%, ${mixA(col, c.skyAlpha)} 0%, transparent 70%)`;\n    }),\n    `linear-gradient(180deg, transparent 38%, ${mixA(AURORA[2], 30)} 50%, transparent 72%)`,\n    c.sky,\n  ].join(\", \");\n  const bandH = Math.max(0, bottomY - topY);\n\n  return (\n    <AbsoluteFill style={{ background: c.bg, overflow: \"hidden\" }}>\n      <AbsoluteFill\n        style={{\n          background: `radial-gradient(ellipse 75% 85% at 35% 30%, ${c.key}, transparent 70%)`,\n        }}\n      />\n      <div\n        style={{\n          position: \"absolute\",\n          left: 0,\n          top: topY,\n          width: W,\n          height: bandH,\n          overflow: \"hidden\",\n        }}\n      >\n        <div\n          style={{\n            position: \"absolute\",\n            left: 0,\n            top: -topY,\n            width: W,\n            height: H,\n            background: sky,\n          }}\n        />\n      </div>\n      <svg\n        width={W}\n        height={H}\n        viewBox={`0 0 ${W} ${H}`}\n        style={{ position: \"absolute\", inset: 0 }}\n        aria-hidden\n      >\n        <defs>\n          <clipPath id={ids.box}>\n            <rect\n              x={-1000}\n              y={-CAP - 60}\n              width={hero.ink + 4000}\n              height={CAP + 80}\n            />\n          </clipPath>\n          <clipPath id={ids.top}>\n            <rect\n              x={-1000}\n              y={-CAP - 200}\n              width={hero.ink + 4000}\n              height={HALF + 200}\n            />\n          </clipPath>\n          <clipPath id={ids.bottom}>\n            <rect\n              x={-1000}\n              y={-HALF}\n              width={hero.ink + 4000}\n              height={HALF + 200}\n            />\n          </clipPath>\n          <clipPath id={ids.band}>\n            <rect\n              x={0}\n              y={topY}\n              width={W}\n              height={Math.max(0, bottomY - topY)}\n            />\n          </clipPath>\n        </defs>\n\n        <g clipPath={`url(#${ids.band})`} fill={c.ins}>\n          <g transform={`translate(${insX} ${cy + capI / 2}) scale(${si})`}>\n            {ins.glyphs.map((g, i) => (\n              <path\n                key={`${i}-${g.x}`}\n                d={g.d}\n                transform={`translate(${g.x} 0)`}\n              />\n            ))}\n          </g>\n        </g>\n\n        {half(topX, topY + HALF * sh, ids.top)}\n        {half(bottomX, bottomY + HALF * sh, ids.bottom)}\n\n        {cut > 0 && open === 0 ? (\n          <rect\n            x={x0 - 8 * u}\n            y={cy - hair / 2}\n            width={(heroW + 16 * u) * cut}\n            height={hair}\n            fill={accent}\n          />\n        ) : null}\n      </svg>\n      <Grain opacity={c.grain} />\n    </AbsoluteFill>\n  );\n}\n",
      "type": "registry:component",
      "target": "components/remocn/split-word.tsx"
    }
  ],
  "type": "registry:component"
}
