{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "zero-stretch",
  "title": "Zero Stretch",
  "description": "The zero in '200' stretches into a glossy pill that holds the word 'components'.",
  "dependencies": ["remotion", "@remotion/google-fonts"],
  "files": [
    {
      "path": "registry/remocn/zero-stretch/index.tsx",
      "content": "\"use client\";\n\nimport { loadFont as loadOutfit } from \"@remotion/google-fonts/Outfit\";\nimport type React from \"react\";\nimport { useEffect, useId, useMemo, useRef, useState } from \"react\";\nimport {\n  AbsoluteFill,\n  continueRender,\n  delayRender,\n  Easing,\n  interpolate,\n  spring,\n  useCurrentFrame,\n  useVideoConfig,\n} from \"remotion\";\n\nconst outfit = loadOutfit(\"normal\", {\n  weights: [\"300\", \"400\", \"500\"],\n  subsets: [\"latin\"],\n});\n\nexport interface ZeroStretchProps {\n  value?: string;\n  label?: string;\n  caption?: string;\n  accent?: string;\n  speed?: number;\n  theme?: \"dark\" | \"light\";\n}\n\nconst THEMES = {\n  dark: { bg: \"#0A0A0B\", ink: \"#F4F3F0\", muted: \"#8E8D94\", grain: 0.05 },\n  light: { bg: \"#F5F4F0\", ink: \"#111113\", muted: \"#6E6C67\", grain: 0.035 },\n} as const;\n\nconst MESH = {\n  bg: \"#0a0d24\",\n  colors: [\"#ecdcf2\", \"#8796ee\", \"#2e45b7\", \"#0e1645\"],\n} as const;\n\nconst TRACK = -0.03;\nconst LABEL_TRACK = -0.015;\nconst LABEL_SIZE = 0.36;\n\nconst RISE_AT = 2;\nconst RISE_LEN = 18;\nconst RISE_GAP = 3;\nconst STRETCH_AT = 34;\nconst LABEL_AT = 50;\nconst CAPTION_AT = 72;\n\nconst EASE_OUT = Easing.bezier(0.22, 1, 0.36, 1);\nconst CLAMP = {\n  extrapolateLeft: \"clamp\",\n  extrapolateRight: \"clamp\",\n} as const;\n\ninterface ZeroShape {\n  adv: number;\n  cx: number;\n  oTop: number;\n  iTop: number;\n  iBot: number;\n  oBot: number;\n  rx: number;\n}\n\ninterface Metrics {\n  zero: ZeroShape;\n  adv: number[];\n  label: number;\n  caption: number;\n  baseline: number;\n}\n\nconst FONT_PX = 1000;\n\nfunction scanZero(ctx: CanvasRenderingContext2D, glyph: string): ZeroShape {\n  const W = 1600;\n  const H = 1600;\n  const x0 = 300;\n  const base = 1250;\n  ctx.clearRect(0, 0, W, H);\n  ctx.fillStyle = \"#000\";\n  ctx.letterSpacing = \"0px\";\n  ctx.fillText(glyph, x0, base);\n  const data = ctx.getImageData(0, 0, W, H).data;\n  const ink = (x: number, y: number) => data[(y * W + x) * 4 + 3] > 127;\n  let top = -1;\n  let bot = -1;\n  for (let y = 0; y < H && top < 0; y++)\n    for (let x = 0; x < W; x += 2)\n      if (ink(x, y)) {\n        top = y;\n        break;\n      }\n  for (let y = H - 1; y >= 0 && bot < 0; y--)\n    for (let x = 0; x < W; x += 2)\n      if (ink(x, y)) {\n        bot = y;\n        break;\n      }\n  const midY = Math.round((top + bot) / 2);\n  const row: number[] = [];\n  let prev = false;\n  for (let x = 0; x < W; x++) {\n    const v = ink(x, midY);\n    if (v !== prev) row.push(x);\n    prev = v;\n  }\n  const oL = row[0] ?? x0;\n  const iL = row[1] ?? oL;\n  const iR = row[2] ?? iL;\n  const oR = row[3] ?? iR;\n  const cx = Math.round((oL + oR) / 2);\n  const col: number[] = [];\n  prev = false;\n  for (let y = 0; y < H; y++) {\n    const v = ink(cx, y);\n    if (v !== prev) col.push(y);\n    prev = v;\n  }\n  const oT = col[0] ?? top;\n  const iT = col[1] ?? oT;\n  const iB = col[2] ?? iT;\n  const oB = col[3] ?? bot;\n  const em = (v: number) => v / FONT_PX;\n  return {\n    adv: em(ctx.measureText(glyph).width),\n    cx: em(cx - x0),\n    oTop: em(oT - base),\n    iTop: em(iT - base),\n    iBot: em(iB - base),\n    oBot: em(oB - base),\n    rx: em(iR - iL) / 2,\n  };\n}\n\nfunction measure(\n  chars: string[],\n  zeroGlyph: string,\n  label: string,\n  caption: string,\n): Metrics {\n  const canvas = document.createElement(\"canvas\");\n  canvas.width = 1600;\n  canvas.height = 1600;\n  const ctx = canvas.getContext(\"2d\", { willReadFrequently: true });\n  if (!ctx) throw new Error(\"zero-stretch: no 2d context\");\n  const fam = `\"${outfit.fontFamily}\"`;\n  ctx.font = `500 ${FONT_PX}px ${fam}`;\n  ctx.textBaseline = \"alphabetic\";\n  const zero = scanZero(ctx, zeroGlyph);\n  const fm = ctx.measureText(\"0\");\n  const asc = fm.fontBoundingBoxAscent / FONT_PX;\n  const desc = fm.fontBoundingBoxDescent / FONT_PX;\n  const adv = chars.map((ch) => ctx.measureText(ch).width / FONT_PX + TRACK);\n  ctx.font = `400 ${FONT_PX}px ${fam}`;\n  const lab =\n    ctx.measureText(label).width / FONT_PX +\n    LABEL_TRACK * Array.from(label).length;\n  const cap = caption ? ctx.measureText(caption).width / FONT_PX : 0;\n  return {\n    zero: { ...zero, adv: zero.adv + TRACK },\n    adv,\n    label: lab,\n    caption: cap,\n    baseline: (1 - (asc + desc)) / 2 + asc,\n  };\n}\n\nfunction useMetrics(\n  chars: string[],\n  zeroGlyph: string,\n  label: string,\n  caption: string,\n) {\n  const [metrics, setMetrics] = useState<Metrics | null>(null);\n  const [handle] = useState(() => delayRender(\"zero-stretch: measuring\"));\n  const released = useRef(false);\n  useEffect(() => {\n    let live = true;\n    Promise.all([\n      outfit.waitUntilDone(),\n      document.fonts.load(`500 100px \"${outfit.fontFamily}\"`),\n      document.fonts.load(`400 100px \"${outfit.fontFamily}\"`),\n    ])\n      .catch(() => undefined)\n      .then(() => {\n        if (!live) return;\n        setMetrics(measure(chars, zeroGlyph, label, caption));\n        if (!released.current) {\n          released.current = true;\n          continueRender(handle);\n        }\n      });\n    return () => {\n      live = false;\n    };\n  }, [chars, zeroGlyph, label, caption, handle]);\n  return metrics;\n}\n\nfunction Grain({ opacity }: { opacity: number }) {\n  const id = `zs${useId().replace(/[^a-zA-Z0-9_-]/g, \"\")}`;\n  return (\n    <svg\n      width=\"100%\"\n      height=\"100%\"\n      style={{\n        position: \"absolute\",\n        inset: 0,\n        opacity,\n        mixBlendMode: \"overlay\",\n      }}\n      aria-hidden\n    >\n      <filter id={id}>\n        <feTurbulence\n          type=\"fractalNoise\"\n          baseFrequency=\"0.9\"\n          numOctaves={2}\n          seed={7}\n        />\n        <feColorMatrix type=\"saturate\" values=\"0\" />\n      </filter>\n      <rect width=\"100%\" height=\"100%\" filter={`url(#${id})`} />\n    </svg>\n  );\n}\n\nexport function ZeroStretch({\n  value = \"200\",\n  label = \"components\",\n  caption = \"Ready to ship. Copy one in, own every frame.\",\n  accent = \"#3B5BFD\",\n  speed = 1,\n  theme = \"dark\",\n}: ZeroStretchProps) {\n  const t = useCurrentFrame() * speed;\n  const { width: W, height: H, fps } = useVideoConfig();\n  const c = THEMES[theme];\n\n  const chars = useMemo(() => Array.from(value), [value]);\n  const zi = useMemo(() => {\n    for (let i = chars.length - 1; i >= 0; i--)\n      if (/[0oO]/.test(chars[i])) return i;\n    return -1;\n  }, [chars]);\n  const zeroGlyph = zi >= 0 ? chars[zi] : \"0\";\n  const m = useMetrics(chars, zeroGlyph, label, caption);\n\n  if (!m) {\n    return <div style={{ position: \"absolute\", inset: 0, background: c.bg }} />;\n  }\n\n  const z = m.zero;\n  const innerH = z.iBot - z.iTop;\n  const ry = innerH / 2;\n  const labelEm = LABEL_SIZE * innerH;\n  const gapEm = Math.max(0, m.label * labelEm + innerH * 0.62 - 2 * z.rx * 0.9);\n\n  const glyphs = chars.map((ch, i) => ({\n    ch,\n    adv: m.adv[i],\n    zero: i === zi,\n    lead: 0,\n  }));\n  if (zi < 0) glyphs.push({ ch: zeroGlyph, adv: z.adv, zero: true, lead: 0.2 });\n\n  const restEm = glyphs.reduce((s, g) => s + g.adv + g.lead, 0) - TRACK;\n  const F = Math.min(H * 0.5, (W * 0.86) / (restEm + gapEm));\n\n  const open = spring({\n    frame: t - STRETCH_AT,\n    fps,\n    config: { damping: 14, stiffness: 70, mass: 1 },\n  });\n  const gap = gapEm * F * open;\n  const totalW = restEm * F + gap;\n\n  const midEm = (z.oTop + z.oBot) / 2;\n  const centerY = caption ? H * 0.46 : H * 0.5;\n  const baseY = centerY - midEm * F;\n  const left0 = (W - totalW) / 2;\n\n  const fillOn = interpolate(t, [STRETCH_AT + 2, STRETCH_AT + 16], [0, 1], {\n    ...CLAMP,\n    easing: EASE_OUT,\n  });\n\n  const drift = t / 30;\n  const blob = (bx: number, by: number, k: number) =>\n    `${(bx + Math.sin(drift * 0.7 + k) * 6).toFixed(2)}% ${(by + Math.cos(drift * 0.55 + k * 1.3) * 8).toFixed(2)}%`;\n  const meshColors = [\n    \"color-mix(in oklab, #ecdcf2 62%, transparent)\",\n    MESH.colors[1],\n    accent,\n    MESH.colors[3],\n  ];\n  const meshPos = [\n    [16, 20],\n    [84, 26],\n    [72, 86],\n    [22, 80],\n  ];\n  const field = [\n    ...meshColors.map(\n      (col, i) =>\n        `radial-gradient(70% 120% at ${blob(meshPos[i][0], meshPos[i][1], i)}, ${col} 0%, transparent 68%)`,\n    ),\n    MESH.bg,\n  ].join(\", \");\n\n  let x = left0;\n  const lineTop = baseY - m.baseline * F;\n  const maskTop = baseY + z.oTop * F - F * 0.06;\n  const maskBot = baseY + z.oBot * F + F * 0.02;\n\n  const pieces = glyphs.map((g, i) => {\n    const gx = x + g.lead * F;\n    x = gx + g.adv * F + (g.zero ? gap : 0);\n    const riseAt = g.lead > 0 ? STRETCH_AT - 6 : RISE_AT + i * RISE_GAP;\n    const p = interpolate(t, [riseAt, riseAt + RISE_LEN], [0, 1], {\n      ...CLAMP,\n      easing: EASE_OUT,\n    });\n    const dy = (1 - p) * (maskBot - maskTop) * 1.05;\n    const glyphStyle: React.CSSProperties = {\n      position: \"absolute\",\n      top: lineTop - maskTop + dy,\n      fontFamily: outfit.fontFamily,\n      fontWeight: 500,\n      fontSize: F,\n      lineHeight: `${F}px`,\n      letterSpacing: `${TRACK}em`,\n      color: c.ink,\n      whiteSpace: \"pre\",\n    };\n    if (!g.zero) {\n      return (\n        <div key={`${g.ch}-${i}`} style={{ ...glyphStyle, left: gx }}>\n          {g.ch}\n        </div>\n      );\n    }\n    const cxPx = z.cx * F;\n    const rail = (y0: number, y1: number, key: string) => (\n      <div\n        key={key}\n        style={{\n          position: \"absolute\",\n          left: gx + cxPx - 1,\n          width: gap + 2,\n          top: baseY + y0 * F - maskTop + dy,\n          height: (y1 - y0) * F,\n          background: c.ink,\n        }}\n      />\n    );\n    const e = (z.iTop - z.oTop) * F * 0.4;\n    const winL = gx + cxPx - z.rx * F - e;\n    const winT = baseY + z.iTop * F - maskTop + dy - e;\n    const winW = 2 * z.rx * F + gap + 2 * e;\n    const winH = innerH * F + 2 * e;\n    const rX = z.rx * F + e;\n    const rY = ry * F + e;\n    const labelSize = labelEm * F;\n    const words = label.split(\" \");\n    return (\n      <div key={`zero-${i}`}>\n        <div\n          style={{\n            position: \"absolute\",\n            left: winL,\n            top: winT,\n            width: winW,\n            height: winH,\n            clipPath: `inset(0 round ${rX}px / ${rY}px)`,\n            background: field,\n            opacity: fillOn,\n          }}\n        />\n        <div\n          style={{\n            position: \"absolute\",\n            left: winL,\n            top: winT,\n            width: winW,\n            height: winH,\n            clipPath: `inset(0 round ${rX}px / ${rY}px)`,\n            display: \"flex\",\n            alignItems: \"center\",\n            justifyContent: \"center\",\n            fontFamily: outfit.fontFamily,\n            fontWeight: 400,\n            fontSize: labelSize,\n            letterSpacing: `${LABEL_TRACK}em`,\n            lineHeight: 1,\n            color: \"#F7F5FF\",\n            whiteSpace: \"pre\",\n          }}\n        >\n          {words.map((w, k) => {\n            const lp = interpolate(\n              t,\n              [LABEL_AT + k * 3, LABEL_AT + k * 3 + 16],\n              [0, 1],\n              {\n                ...CLAMP,\n                easing: EASE_OUT,\n              },\n            );\n            return (\n              <span\n                key={`${w}-${k}`}\n                style={{\n                  display: \"inline-block\",\n                  opacity: lp,\n                  transform: `translateY(${((1 - lp) * labelSize * 0.7).toFixed(2)}px)`,\n                }}\n              >\n                {w}\n                {k < words.length - 1 ? \" \" : \"\"}\n              </span>\n            );\n          })}\n        </div>\n        <div\n          style={{\n            position: \"absolute\",\n            left: gx,\n            top: 0,\n            width: cxPx + 0.5,\n            height: maskBot - maskTop,\n            overflow: \"hidden\",\n          }}\n        >\n          <div style={{ ...glyphStyle, left: 0 }}>{g.ch}</div>\n        </div>\n        <div\n          style={{\n            position: \"absolute\",\n            left: gx + cxPx + gap - 0.5,\n            top: 0,\n            width: g.adv * F - cxPx + F * 0.1,\n            height: maskBot - maskTop,\n            overflow: \"hidden\",\n          }}\n        >\n          <div style={{ ...glyphStyle, left: -cxPx + 0.5 }}>{g.ch}</div>\n        </div>\n        {gap > 0.5 ? rail(z.oTop, z.iTop, \"top\") : null}\n        {gap > 0.5 ? rail(z.iBot, z.oBot, \"bot\") : null}\n      </div>\n    );\n  });\n\n  const capSize = Math.min(\n    Math.max(F * 0.1, H * 0.034),\n    H * 0.046,\n    (W * 0.86) / Math.max(0.1, m.caption),\n  );\n  const capP = interpolate(t, [CAPTION_AT, CAPTION_AT + 18], [0, 1], {\n    ...CLAMP,\n    easing: EASE_OUT,\n  });\n\n  return (\n    <AbsoluteFill style={{ background: c.bg, overflow: \"hidden\" }}>\n      <div\n        style={{\n          position: \"absolute\",\n          left: 0,\n          width: W,\n          top: maskTop,\n          height: maskBot - maskTop,\n          overflow: \"hidden\",\n        }}\n      >\n        {pieces}\n      </div>\n      {caption ? (\n        <div\n          style={{\n            position: \"absolute\",\n            left: 0,\n            width: W,\n            top: maskBot + Math.max(F * 0.16, H * 0.05),\n            textAlign: \"center\",\n            fontFamily: outfit.fontFamily,\n            fontWeight: 300,\n            fontSize: capSize,\n            letterSpacing: \"-0.005em\",\n            color: c.muted,\n            opacity: capP,\n            transform: `translateY(${((1 - capP) * capSize * 0.6).toFixed(2)}px)`,\n          }}\n        >\n          {caption}\n        </div>\n      ) : null}\n      <Grain opacity={c.grain} />\n    </AbsoluteFill>\n  );\n}\n",
      "type": "registry:component",
      "target": "components/remocn/zero-stretch.tsx"
    }
  ],
  "type": "registry:component"
}
