{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "lumen-count",
  "title": "Lumen Count",
  "description": "A glowing 200 counts up with a soft floor reflection and a caption beneath.",
  "dependencies": ["remotion", "@remotion/google-fonts"],
  "files": [
    {
      "path": "registry/remocn/lumen-count/index.tsx",
      "content": "\"use client\";\n\nimport { loadFont as loadGeist } from \"@remotion/google-fonts/Geist\";\nimport type React from \"react\";\nimport { useEffect, useId, useRef, useState } from \"react\";\nimport {\n  AbsoluteFill,\n  continueRender,\n  delayRender,\n  Easing,\n  interpolate,\n  useCurrentFrame,\n  useVideoConfig,\n} from \"remotion\";\n\nconst geist = loadGeist(\"normal\", {\n  weights: [\"300\", \"400\"],\n  subsets: [\"latin\"],\n});\n\nexport interface LumenCountProps {\n  value?: number;\n  from?: number;\n  suffix?: string;\n  label?: string;\n  eyebrow?: string;\n  accent?: string;\n  speed?: number;\n  theme?: \"dark\" | \"light\";\n}\n\nconst BEAM = [\"#f4f7ff\", \"#8fb4ff\", \"#3d5bff\", \"#1a1660\"] as const;\n\nconst THEMES = {\n  dark: {\n    bg: \"#040406\",\n    off: \"#15161C\",\n    on: \"#DCE5FF\",\n    fill: (accent: string) =>\n      `linear-gradient(180deg, ${BEAM[0]} 18%, ${BEAM[1]} 62%, ${accent} 100%)`,\n    flare: \"#FFFFFF\",\n    label: \"#9A9AA3\",\n    eyebrow: \"#C9CBD6\",\n    bloom: true,\n    reflect: 0.24,\n    pool: (b: number, accent: string) =>\n      `radial-gradient(48% 24% at 50% 0%, rgba(157,180,255,${(0.32 * b).toFixed(3)}) 0%, color-mix(in oklab, ${accent} ${(30 * b).toFixed(1)}%, transparent) 38%, transparent 76%)`,\n    haze: (b: number) =>\n      `radial-gradient(60% 55% at 50% 46%, rgba(143,180,255,${(0.08 * b).toFixed(3)}) 0%, transparent 70%)`,\n    floor: \"linear-gradient(180deg, rgba(20,22,34,0.55) 0%, rgba(4,4,6,0) 70%)\",\n    grain: 0.05,\n  },\n  light: {\n    bg: \"#F5F4F0\",\n    off: \"#E7E4DD\",\n    on: \"#15151A\",\n    fill: (accent: string) =>\n      `linear-gradient(180deg, ${BEAM[3]} 10%, #2B2F8F 50%, ${accent} 100%)`,\n    flare: \"#5E7BFF\",\n    label: \"#6E6C67\",\n    eyebrow: \"#3F3E3A\",\n    bloom: false,\n    reflect: 0.12,\n    pool: (b: number, accent: string) =>\n      `radial-gradient(48% 24% at 50% 0%, color-mix(in oklab, ${accent} ${(14 * b).toFixed(1)}%, transparent) 0%, transparent 72%)`,\n    haze: (b: number) =>\n      `radial-gradient(70% 60% at 50% 40%, rgba(255,255,255,${(0.7 * b).toFixed(3)}) 0%, transparent 70%)`,\n    floor:\n      \"linear-gradient(180deg, rgba(222,217,207,0.6) 0%, rgba(245,244,240,0) 70%)\",\n    grain: 0.035,\n  },\n} as const;\n\nconst TRACK = -0.045;\nconst FONT_PX = 200;\nconst COUNT_AT = 6;\nconst COUNT_END = 58;\nconst FLARE_AT = 56;\nconst LABEL_AT = 68;\nconst SHIFT = 8;\n\nconst EASE_OUT = Easing.bezier(0.22, 1, 0.36, 1);\nconst EASE_COUNT = Easing.bezier(0.5, 0, 0.25, 1);\nconst CLAMP = {\n  extrapolateLeft: \"clamp\",\n  extrapolateRight: \"clamp\",\n} as const;\n\ninterface Metrics {\n  digit: number;\n  width: (s: string) => number;\n  label: (s: string) => number;\n  baseline: number;\n  capTop: number;\n}\n\nfunction useMetrics() {\n  const [m, setM] = useState<Metrics | null>(null);\n  const [handle] = useState(() => delayRender(\"lumen-count: measuring\"));\n  const released = useRef(false);\n  useEffect(() => {\n    let live = true;\n    Promise.all([\n      geist.waitUntilDone(),\n      document.fonts.load(`400 100px \"${geist.fontFamily}\"`),\n      document.fonts.load(`300 100px \"${geist.fontFamily}\"`),\n    ])\n      .catch(() => undefined)\n      .then(() => {\n        if (!live) return;\n        const canvas = document.createElement(\"canvas\");\n        const ctx = canvas.getContext(\"2d\");\n        if (!ctx) throw new Error(\"lumen-count: no 2d context\");\n        ctx.font = `400 ${FONT_PX}px \"${geist.fontFamily}\"`;\n        const fm = ctx.measureText(\"0\");\n        const asc = fm.fontBoundingBoxAscent / FONT_PX;\n        const desc = fm.fontBoundingBoxDescent / FONT_PX;\n        const digit = Math.max(\n          ...\"0123456789\".split(\"\").map((d) => ctx.measureText(d).width),\n        );\n        const adv = (ch: string) =>\n          (/[0-9]/.test(ch) ? digit : ctx.measureText(ch).width) / FONT_PX +\n          TRACK;\n        const width = (s: string) =>\n          Array.from(s).reduce((a, ch) => a + adv(ch), 0) - TRACK;\n        const capTop = fm.actualBoundingBoxAscent / FONT_PX;\n        const label = (s: string) => {\n          ctx.font = `400 ${FONT_PX}px \"${geist.fontFamily}\"`;\n          return ctx.measureText(s).width / FONT_PX;\n        };\n        setM({\n          digit: digit / FONT_PX,\n          width,\n          label,\n          baseline: (1 - (asc + desc)) / 2 + asc,\n          capTop,\n        });\n        if (!released.current) {\n          released.current = true;\n          continueRender(handle);\n        }\n      });\n    return () => {\n      live = false;\n    };\n  }, [handle]);\n  return m;\n}\n\nfunction fmt(v: number) {\n  return Math.round(v).toLocaleString(\"en-US\");\n}\n\nfunction Grain({ opacity }: { opacity: number }) {\n  const id = `lc${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        pointerEvents: \"none\",\n      }}\n      aria-hidden\n    >\n      <filter id={id}>\n        <feTurbulence\n          type=\"fractalNoise\"\n          baseFrequency=\"0.9\"\n          numOctaves={2}\n          seed={3}\n        />\n        <feColorMatrix type=\"saturate\" values=\"0\" />\n      </filter>\n      <rect width=\"100%\" height=\"100%\" filter={`url(#${id})`} />\n    </svg>\n  );\n}\n\nfunction mixHex(a: string, b: string, t: number) {\n  const pa = Number.parseInt(a.slice(1), 16);\n  const pb = Number.parseInt(b.slice(1), 16);\n  const ch = (p: number, sh: number) => (p >> sh) & 255;\n  const m = (sh: number) =>\n    Math.round(ch(pa, sh) + (ch(pb, sh) - ch(pa, sh)) * t);\n  return `rgb(${m(16)}, ${m(8)}, ${m(0)})`;\n}\n\nexport function LumenCount({\n  value = 200,\n  from = 1,\n  suffix = \"\",\n  label = \"components, ready to light up your launch video\",\n  eyebrow = \"\",\n  accent = \"#3B5BFD\",\n  speed = 1,\n  theme = \"dark\",\n}: LumenCountProps) {\n  const t = useCurrentFrame() * speed;\n  const { width: W, height: H } = useVideoConfig();\n  const c = THEMES[theme];\n  const m = useMetrics();\n\n  if (!m) {\n    return <div style={{ position: \"absolute\", inset: 0, background: c.bg }} />;\n  }\n\n  const lo = Math.max(1, Math.min(from, value));\n  const hi = Math.max(lo + 1, value);\n  const valueAt = (u: number) => lo * (hi / lo) ** u;\n  const uAt = (tt: number) =>\n    EASE_COUNT(interpolate(tt, [COUNT_AT, COUNT_END], [0, 1], CLAMP));\n  const u = uAt(t);\n  const v = u >= 1 ? value : valueAt(u);\n  const text = `${fmt(v)}${suffix}`;\n  const finalText = `${fmt(value)}${suffix}`;\n\n  const b0 = Math.log(v / lo) / Math.log(hi / lo);\n  const flare =\n    interpolate(t, [FLARE_AT, FLARE_AT + 5], [0, 1], {\n      ...CLAMP,\n      easing: EASE_OUT,\n    }) *\n    interpolate(t, [FLARE_AT + 6, FLARE_AT + 24], [1, 0], {\n      ...CLAMP,\n      easing: Easing.bezier(0.65, 0, 0.35, 1),\n    });\n  const b = Math.max(0.04, b0);\n\n  const F = Math.min(H * 0.46, (W * 0.84) / m.width(finalText));\n  let wEff = m.width(`${fmt(lo)}${suffix}`);\n  {\n    let prev = wEff;\n    for (let s = COUNT_AT; s <= COUNT_END + 1; s += 0.5) {\n      const uu = uAt(s);\n      const w = m.width(`${fmt(uu >= 1 ? value : valueAt(uu))}${suffix}`);\n      if (w !== prev) {\n        wEff +=\n          (w - prev) *\n          interpolate(t, [s, s + SHIFT], [0, 1], {\n            ...CLAMP,\n            easing: EASE_OUT,\n          });\n        prev = w;\n      }\n    }\n  }\n  const lineW = wEff * F;\n  const capH = m.capTop * F;\n  const hasLabel = label.length > 0;\n  const labSize = Math.min(H * 0.042, Math.max(H * 0.03, F * 0.13));\n  const labLines = Math.ceil((m.label(label) * labSize) / (W * 0.8));\n  const blockH =\n    capH + (hasLabel ? F * 0.36 + labSize * 1.25 * labLines : F * 0.2);\n  const capY = (H - blockH) / 2 + (eyebrow ? H * 0.03 : 0);\n  const baseY = capY + capH;\n  const floorY = baseY + F * 0.035;\n  const left = (W - lineW) / 2;\n  const top = baseY - m.baseline * F;\n\n  const chars = Array.from(text);\n  const finalChars = Array.from(finalText);\n  const lead = finalChars.length - chars.length;\n\n  const glyphs = (key: string, color: string, extra?: React.CSSProperties) => (\n    <div\n      key={key}\n      style={{\n        position: \"absolute\",\n        left,\n        top,\n        height: F,\n        display: \"flex\",\n        fontFamily: geist.fontFamily,\n        fontWeight: 400,\n        fontSize: F,\n        lineHeight: `${F}px`,\n        color,\n        whiteSpace: \"pre\",\n        ...extra,\n      }}\n    >\n      {chars.map((ch, i) => {\n        const isDigit = /[0-9]/.test(ch);\n        const w = ((isDigit ? m.digit : m.width(ch)) + TRACK) * F;\n        let dy = 0;\n        let op = 1;\n        if (i === 0 && lead >= 0 && t > COUNT_AT) {\n          let since = 99;\n          for (let s = t; s > t - SHIFT && s >= COUNT_AT; s -= 0.5) {\n            const uu = uAt(s - 0.5);\n            const before = fmt(uu >= 1 ? value : valueAt(uu)).length;\n            if (before < fmt(v).length) {\n              since = t - s;\n              break;\n            }\n          }\n          if (since < SHIFT) {\n            const p = EASE_OUT(since / SHIFT);\n            dy = (1 - p) * F * 0.18;\n            op = p;\n          }\n        }\n        return (\n          <span\n            key={`${i}-${ch}`}\n            style={{\n              display: \"inline-block\",\n              width: i === chars.length - 1 ? w - TRACK * F : w,\n              textAlign: isDigit ? \"center\" : \"left\",\n              transform: dy ? `translateY(${dy.toFixed(2)}px)` : undefined,\n              opacity: op,\n            }}\n          >\n            {ch}\n          </span>\n        );\n      })}\n    </div>\n  );\n\n  const baseCol = mixHex(c.off, c.on, Math.min(1, b));\n  const fillOn = Math.min(1, Math.max(0, (b - 0.35) / 0.65)) * (1 - flare);\n  const flareCol = flare;\n  const glowB = Math.min(1.6, b + flare * 0.6);\n  const labP = interpolate(t, [LABEL_AT, LABEL_AT + 18], [0, 1], {\n    ...CLAMP,\n    easing: EASE_OUT,\n  });\n  const eyeP = interpolate(t, [COUNT_END - 10, COUNT_END + 8], [0, 1], {\n    ...CLAMP,\n    easing: EASE_OUT,\n  });\n  const reflMask = `linear-gradient(0deg, black ${(H - floorY).toFixed(1)}px, transparent ${(H - floorY + capH * 0.6).toFixed(1)}px)`;\n  const sunFlash = theme === \"light\" ? flare * 0.55 : 0;\n\n  return (\n    <AbsoluteFill style={{ background: c.bg, overflow: \"hidden\" }}>\n      <div\n        style={{\n          position: \"absolute\",\n          left: 0,\n          right: 0,\n          top: floorY,\n          bottom: 0,\n          background: c.floor,\n        }}\n      />\n      <div\n        style={{\n          position: \"absolute\",\n          left: 0,\n          right: 0,\n          top: floorY,\n          height: H - floorY,\n          background: c.pool(Math.min(1.3, glowB), accent),\n        }}\n      />\n      <div\n        style={{\n          position: \"absolute\",\n          inset: 0,\n          background: c.haze(Math.min(1.3, glowB)),\n        }}\n      />\n      <div\n        style={{\n          position: \"absolute\",\n          inset: 0,\n          transformOrigin: `50% ${floorY.toFixed(2)}px`,\n          transform: \"scaleY(-1)\",\n          opacity: c.reflect * Math.min(1, glowB),\n          filter: `blur(${(F * 0.012).toFixed(2)}px)`,\n          WebkitMaskImage: reflMask,\n          maskImage: reflMask,\n        }}\n      >\n        {glyphs(\"refl\", theme === \"dark\" ? \"#AFC3FF\" : \"#2B2F8F\")}\n      </div>\n      {c.bloom ? (\n        <>\n          {glyphs(\"bloom2\", accent, {\n            filter: `blur(${(F * 0.16).toFixed(2)}px)`,\n            opacity: 0.45 * glowB ** 3,\n          })}\n          {glyphs(\"bloom1\", BEAM[1], {\n            filter: `blur(${(F * 0.045).toFixed(2)}px)`,\n            opacity: Math.min(1, 0.6 * glowB ** 2),\n          })}\n        </>\n      ) : (\n        glyphs(\"shade\", accent, {\n          filter: `blur(${(F * 0.05).toFixed(2)}px)`,\n          opacity: 0.16 * b,\n          transform: `translateY(${(F * 0.02).toFixed(2)}px)`,\n        })\n      )}\n      {glyphs(\"base\", baseCol)}\n      {glyphs(\"fill\", \"transparent\", {\n        backgroundImage: c.fill(accent),\n        WebkitBackgroundClip: \"text\",\n        backgroundClip: \"text\",\n        opacity: fillOn,\n      })}\n      {flareCol > 0.001\n        ? glyphs(\"flare\", c.flare, { opacity: flareCol })\n        : null}\n      {eyebrow ? (\n        <div\n          style={{\n            position: \"absolute\",\n            left: 0,\n            width: W,\n            top: capY - H * 0.075,\n            textAlign: \"center\",\n            fontFamily: geist.fontFamily,\n            fontWeight: 400,\n            fontSize: Math.max(H * 0.026, labSize * 0.72),\n            letterSpacing: \"0.02em\",\n            color: c.eyebrow,\n            opacity: eyeP,\n          }}\n        >\n          {eyebrow}\n        </div>\n      ) : null}\n      {hasLabel ? (\n        <div\n          style={{\n            position: \"absolute\",\n            left: W * 0.1,\n            width: W * 0.8,\n            top: floorY + F * 0.36,\n            textAlign: \"center\",\n            textWrap: \"balance\",\n            lineHeight: 1.25,\n            fontFamily: geist.fontFamily,\n            fontWeight: 300,\n            fontSize: labSize,\n            letterSpacing: \"-0.01em\",\n            color: c.label,\n            opacity: labP,\n            transform: `translateY(${((1 - labP) * labSize * 0.7).toFixed(2)}px)`,\n          }}\n        >\n          {label}\n        </div>\n      ) : null}\n      {sunFlash > 0.001 ? (\n        <div\n          style={{\n            position: \"absolute\",\n            inset: 0,\n            background:\n              \"radial-gradient(80% 70% at 50% 35%, #ffffff 0%, rgba(255,255,255,0) 75%)\",\n            opacity: sunFlash,\n          }}\n        />\n      ) : null}\n      <Grain opacity={c.grain} />\n    </AbsoluteFill>\n  );\n}\n",
      "type": "registry:component",
      "target": "components/remocn/lumen-count.tsx"
    }
  ],
  "type": "registry:component"
}
