{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "neon-sign",
  "title": "Neon Sign",
  "description": "A glass neon tube stutters alive segment by segment with a hot white core and wall glow, buzzes off once, then holds lit.",
  "dependencies": ["remotion", "@remotion/paths"],
  "files": [
    {
      "path": "registry/remocn/neon-sign/index.tsx",
      "content": "\"use client\";\n\nimport { getBoundingBox, getSubpaths } from \"@remotion/paths\";\nimport { useId, useMemo } from \"react\";\nimport {\n  AbsoluteFill,\n  interpolate,\n  random,\n  useCurrentFrame,\n  useVideoConfig,\n} from \"remotion\";\n\nexport type NeonSignProps = {\n  /** SVG path data for the tube. Each subpath (M command) flickers on its own. */\n  path?: string;\n  /** viewBox for the path. Defaults to the path's bounding box. */\n  viewBox?: string;\n  /** Tube color when lit. */\n  color?: string;\n  /** Color of the halo and the glow on the wall. */\n  glowColor?: string;\n  /** Unlit glass tube. */\n  tubeColor?: string;\n  /** Color of the hot core inside the tube. */\n  coreColor?: string;\n  backgroundColor?: string;\n  /** Tube thickness in path units. */\n  strokeWidth?: number;\n  /** Share of the frame width the sign spans. */\n  fit?: number;\n  seed?: number;\n  speed?: number;\n};\n\n// Handwritten \"hello\" with an underline swash, one subpath per tube.\nconst HELLO = [\n  \"M 30 132 C 52 112 74 70 70 46 C 67 28 50 30 47 52 C 44 76 42 112 40 140 C 46 116 58 100 72 100 C 86 100 84 122 86 134 C 88 144 100 144 110 132\",\n  \"M 110 132 C 124 124 142 110 136 98 C 130 88 112 96 112 116 C 112 138 134 146 156 130\",\n  \"M 156 130 C 176 110 196 66 190 46 C 186 32 170 36 168 60 C 166 90 168 126 172 136 C 176 146 188 142 196 132\",\n  \"M 196 132 C 216 110 232 66 226 46 C 222 32 206 36 204 60 C 202 90 204 126 208 136 C 212 146 224 142 232 132\",\n  \"M 232 132 C 238 116 248 100 262 100 C 278 100 280 124 268 136 C 256 146 242 136 248 118 C 252 106 268 104 290 104\",\n  \"M 52 170 C 120 160 210 162 296 152\",\n].join(\" \");\n\nexport const neonSignDefaults = {\n  path: HELLO,\n  color: \"#E5322D\",\n  glowColor: \"#E5322D\",\n  tubeColor: \"#2A2A2A\",\n  coreColor: \"#FFFFFF\",\n  backgroundColor: \"#111111\",\n  strokeWidth: 6,\n  fit: 0.62,\n  seed: 7,\n  speed: 1,\n};\n\nconst FIRST = 3;\nconst STEP = 3.4;\nconst STUTTER = 7;\n// Whole sign drops out once after everything is lit, then holds.\nconst BUZZ: [number, number][] = [\n  [29, 0.1],\n  [30, 0],\n  [31, 0.55],\n  [32, 0.2],\n];\nconst clamp = {\n  extrapolateLeft: \"clamp\" as const,\n  extrapolateRight: \"clamp\" as const,\n};\n\nfunction segmentLevel(t: number, i: number, seed: number) {\n  const onset = FIRST + i * STEP + random(`neon-sign-${seed}-${i}`) * 2;\n  if (t < onset) return 0;\n  if (t < onset + STUTTER) {\n    const tick = Math.floor(t);\n    const v = random(`neon-sign-${seed}-${i}-${tick}`);\n    const ramp = (t - onset) / STUTTER;\n    return v < 0.5 - ramp * 0.3 ? 0.04 : 0.55 + 0.45 * v;\n  }\n  return 1;\n}\n\nexport function NeonSign(props: NeonSignProps) {\n  const {\n    path,\n    viewBox,\n    color,\n    glowColor,\n    tubeColor,\n    coreColor,\n    backgroundColor,\n    strokeWidth,\n    fit,\n    seed,\n    speed,\n  } = { ...neonSignDefaults, ...props };\n  const uid = `neon-sign-${useId().replace(/[^a-zA-Z0-9_-]/g, \"\")}`;\n  const frame = useCurrentFrame();\n  const { width, height } = useVideoConfig();\n  const t = frame * Math.max(0, speed);\n\n  const segments = useMemo(() => getSubpaths(path), [path]);\n  const box = useMemo(() => {\n    if (viewBox) {\n      const [x, y, w, h] = viewBox.split(/[\\s,]+/).map(Number);\n      return { x, y, w, h };\n    }\n    const b = getBoundingBox(path);\n    const pad = strokeWidth * 6;\n    return {\n      x: b.x1 - pad,\n      y: b.y1 - pad,\n      w: b.width + pad * 2,\n      h: b.height + pad * 2,\n    };\n  }, [path, viewBox, strokeWidth]);\n\n  const tick = Math.floor(t);\n  const buzz = BUZZ.find(([f]) => f === tick)?.[1] ?? 1;\n  const levels = segments.map((_, i) => segmentLevel(t, i, seed) * buzz);\n  const average =\n    levels.reduce((a, b) => a + b, 0) / Math.max(1, levels.length);\n\n  const scale = Math.min((width * fit) / box.w, (height * 0.7) / box.h);\n  const drawW = box.w * scale;\n  const drawH = box.h * scale;\n  const settle = interpolate(t, [32, 40], [0.85, 1], clamp);\n\n  return (\n    <AbsoluteFill style={{ backgroundColor }}>\n      <AbsoluteFill\n        style={{\n          background: `radial-gradient(ellipse ${drawW * 0.75}px ${drawH * 0.85}px at 50% 50%, ${glowColor} 0%, transparent 100%)`,\n          opacity: 0.28 * average * settle,\n        }}\n      />\n      <AbsoluteFill style={{ alignItems: \"center\", justifyContent: \"center\" }}>\n        <svg\n          width={drawW}\n          height={drawH}\n          viewBox={`${box.x} ${box.y} ${box.w} ${box.h}`}\n          style={{ overflow: \"visible\" }}\n        >\n          <defs>\n            <filter\n              id={`${uid}-wide`}\n              filterUnits=\"userSpaceOnUse\"\n              x={box.x - box.w}\n              y={box.y - box.h}\n              width={box.w * 3}\n              height={box.h * 3}\n            >\n              <feGaussianBlur stdDeviation={strokeWidth * 2.2} />\n            </filter>\n            <filter\n              id={`${uid}-near`}\n              filterUnits=\"userSpaceOnUse\"\n              x={box.x - box.w}\n              y={box.y - box.h}\n              width={box.w * 3}\n              height={box.h * 3}\n            >\n              <feGaussianBlur stdDeviation={strokeWidth * 0.6} />\n            </filter>\n          </defs>\n          <g fill=\"none\" strokeLinecap=\"round\" strokeLinejoin=\"round\">\n            {segments.map((d, i) => (\n              <path\n                key={`tube-${i}`}\n                d={d}\n                stroke={tubeColor}\n                strokeWidth={strokeWidth}\n              />\n            ))}\n            {segments.map((d, i) => {\n              const level = levels[i];\n              if (level <= 0) return null;\n              return (\n                <g key={`lit-${i}`}>\n                  <path\n                    d={d}\n                    stroke={glowColor}\n                    strokeWidth={strokeWidth * 3}\n                    opacity={0.7 * level}\n                    filter={`url(#${uid}-wide)`}\n                  />\n                  <path\n                    d={d}\n                    stroke={color}\n                    strokeWidth={strokeWidth * 1.6}\n                    opacity={0.9 * level}\n                    filter={`url(#${uid}-near)`}\n                  />\n                  <path\n                    d={d}\n                    stroke={color}\n                    strokeWidth={strokeWidth}\n                    opacity={level}\n                  />\n                  <path\n                    d={d}\n                    stroke={coreColor}\n                    strokeWidth={strokeWidth * 0.36}\n                    opacity={0.9 * level}\n                  />\n                </g>\n              );\n            })}\n          </g>\n        </svg>\n      </AbsoluteFill>\n    </AbsoluteFill>\n  );\n}\n",
      "type": "registry:component",
      "target": "components/remocn/neon-sign.tsx"
    }
  ],
  "type": "registry:component"
}
