{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "retro-forge",
  "type": "registry:ui",
  "title": "Retro Forge",
  "description": "A synthwave horizon: a perspective plane of glowing magenta grid lines scrolling toward the viewer, melting into a layered violet horizon bloom, with an optional slit-scanned cyan sun, faint star field, and an optional neon outrun car cruising on the grid; the horizon light leans toward the cursor. Original OGL fragment shader, offscreen-paused and reduced-motion-safe; a ghost pointer keeps it moving when untouched.",
  "dependencies": [
    "ogl",
    "motion"
  ],
  "registryDependencies": [
    "https://crucibleui.com/r/ogl-canvas.json",
    "https://crucibleui.com/r/use-visibility-pause.json",
    "https://crucibleui.com/r/use-reduced-motion.json"
  ],
  "files": [
    {
      "path": "registry/default/backgrounds/retro-forge/retro-forge.tsx",
      "type": "registry:ui",
      "target": "components/crucible/retro-forge.tsx",
      "content": "\"use client\";\n\nimport { motion } from \"motion/react\";\nimport * as React from \"react\";\n\nimport { cn } from \"@/lib/utils\";\nimport { useReducedMotion } from \"@/hooks/use-reduced-motion\";\nimport { OglCanvas } from \"@/lib/ogl-canvas\";\n\nexport interface RetroForgeProps extends React.ComponentPropsWithoutRef<\"div\"> {\n  /**\n   * Three tones: `[line, glow, sun]`. `line` is the grid-line color\n   * (magenta), `glow` is the horizon glow (violet), `sun` is the horizon\n   * semicircle (cyan). Any hex string.\n   */\n  colors?: [string, string, string];\n  /** Grid-scroll speed multiplier. 1 = default, 2 = twice as fast. Default 1. */\n  speed?: number;\n  /**\n   * Heat 0–1: scales the brightness of the horizon glow, the rolling glow\n   * wave on the grid, and the sun together. Default 1.\n   */\n  heat?: number;\n  /** Grid cell size in px (larger = coarser grid). Default 60. */\n  cellSize?: number;\n  /** Render the horizon \"sun\" semicircle with scanline slits. Default true. */\n  sun?: boolean;\n  /** Render the neon outrun sports car cruising on the grid. Default true. */\n  car?: boolean;\n  /**\n   * Idle autoplay: after ~1.5s with no real pointer, a seeded ghost pointer\n   * leans the horizon light on a slow drift so it never looks dead in thumbnails and\n   * heroes; the real pointer takes over instantly. Never runs under reduced\n   * motion. @default true\n   */\n  idle?: boolean;\n  /** Freeze the animation at its current frame. */\n  paused?: boolean;\n}\n\nconst DEFAULT_COLORS: [string, string, string] = [\n  \"#ec4899\", // magenta — grid lines\n  \"#8b5cf6\", // violet — horizon glow\n  \"#22d3ee\", // cyan — sun\n];\n\n/** Hex → [r,g,b] in 0–1. Accepts #rgb / #rrggbb. */\nfunction hexToRgb(hex: string): [number, number, number] {\n  let h = hex.replace(\"#\", \"\").trim();\n  if (h.length === 3) {\n    h = h[0] + h[0] + h[1] + h[1] + h[2] + h[2];\n  }\n  const int = parseInt(h, 16);\n  if (Number.isNaN(int) || h.length !== 6) return [0.93, 0.28, 0.6];\n  return [((int >> 16) & 255) / 255, ((int >> 8) & 255) / 255, (int & 255) / 255];\n}\n\n/**\n * Original synthwave-horizon fragment shader. The lower half is a receding\n * ground plane reconstructed per-pixel (uv projected through the horizon into\n * world x/z), with glowing grid lines drawn as exponential falloff around the\n * nearest cell edge — bright core plus a wide halo, perspective-correct width\n * (thicker near, thinner far), and an anti-moire fade where cells shrink\n * below a few pixels. A glow wave rolls down the plane toward the viewer with\n * an eased surge. The horizon is layered bloom (tight hot line + wide\n * breathing violet wash), topped by an optional slit-scanned sun disc and a\n * restrained star field. Film-grain dithering and a soft tone curve keep the\n * bloom clean. GLSL ES 1.00 (WebGL1/2 compatible).\n */\nconst FRAGMENT = /* glsl */ `\nprecision highp float;\n\nvarying vec2 vUv;\n\nuniform float uTime;\nuniform vec2 uResolution;\nuniform vec2 uPointer;\nuniform float uPointerActive; // eased 0..1 pointer (or ghost) presence\n\nuniform vec3 uLine;   // grid lines\nuniform vec3 uGlow;   // horizon glow\nuniform vec3 uSun;    // sun disc\nuniform float uSpeed;\nuniform float uHeat;\nuniform float uCell;  // world cell scale, 1.0 at the 60px default\nuniform float uSunOn; // 1.0 = draw the sun\n\nconst float HORIZON = 0.5;\nconst float CAM = 0.09; // camera height over the plane; sets perspective rate\n\nfloat hash21(vec2 p) {\n  p = fract(p * vec2(123.34, 456.21));\n  p += dot(p, p + 45.32);\n  return fract(p.x * p.y);\n}\n\n// Glowing line profile from a pixel-space distance: a tight gaussian core\n// plus a wide exponential halo. This is what separates neon from wireframe.\nfloat glowLine(float px) {\n  return exp(-px * px * 0.32) + 0.30 * exp(-px * 0.28);\n}\n\nvoid main() {\n  vec2 uv = vUv;\n  float aspect = uResolution.x / max(uResolution.y, 1.0);\n  float resY = max(uResolution.y, 1.0);\n  float t = uTime * uSpeed;\n  // Pointer influence scales with presence so it releases when nobody is there.\n  vec2 ptr = mix(vec2(0.5), uPointer, uPointerActive);\n\n  // Pointer lean, -1..1. The horizon glow and grid brightness follow it.\n  float lean = clamp((ptr.x - 0.5) * 2.0, -1.0, 1.0);\n\n  // Near-black base with the faintest violet cast so the frame never reads\n  // as a dead #000 rectangle.\n  vec3 col = vec3(0.004, 0.004, 0.010) + uGlow * 0.012;\n\n  // ---------------------------------------------------------------- grid --\n  float h = HORIZON - uv.y; // 0 at the horizon, 0.5 at the bottom edge\n  if (h > 0.0) {\n    float z = CAM / max(h, 0.0022); // world depth of this pixel's plane hit\n    float worldX = (uv.x - 0.5) * aspect * z;\n    // Eased scroll: a slow sinusoid over the linear run makes the plane\n    // surge and relax instead of conveyor-belting at constant speed.\n    // Grid drift runs well under the animation clock (gt = t * 0.18) so the\n    // lines advance calmly and hypnotically like an open cruise; this replays\n    // the exact same surge/relax trajectory at that slow pace, leaving\n    // horizon/sun/stars at full speed. The speed prop still scales it on top.\n    float gt = t * 0.18;\n    float scroll = gt * 1.35 + 0.45 * sin(gt * 0.8);\n    float worldZ = z + scroll;\n\n    float cell = 0.034 * uCell;\n\n    // World-units-per-pixel at this depth, per axis. These make line width\n    // perspective-correct and expose the on-screen cell size for the\n    // anti-moire fade.\n    float wppX = aspect * z / max(uResolution.x, 1.0);\n    float wppZ = (z * z / CAM) / resY;\n\n    // Distance to the nearest grid line, in world units, then pixels.\n    float dx = abs(fract(worldX / cell + 0.5) - 0.5) * cell;\n    float dz = abs(fract(worldZ / cell + 0.5) - 0.5) * cell;\n    // Thicker near the viewer, thin at distance.\n    float widen = mix(0.8, 2.0, smoothstep(0.0, 0.5, h));\n    float pxX = dx / (wppX * widen);\n    float pxZ = dz / (wppZ * widen);\n\n    // Anti-moire: once a cell spans only a few pixels the lattice can only\n    // alias, so fade each line family out as its spacing collapses. The\n    // distance fog below finishes the job.\n    float cellPxX = cell / wppX;\n    float cellPxZ = cell / wppZ;\n    float keepX = smoothstep(2.5, 9.0, cellPxX);\n    float keepZ = smoothstep(2.5, 9.0, cellPxZ);\n\n    float grid = glowLine(pxX) * keepX + glowLine(pxZ) * keepZ;\n\n    // Distance fog: lines melt into the horizon glow instead of ending.\n    float fog = smoothstep(0.0, 0.16, h);\n    grid *= fog;\n\n    // Lines shade from glow-violet at depth to full line color up close.\n    vec3 lineCol = mix(uGlow, uLine, smoothstep(0.02, 0.30, h));\n\n    // Grid brightness leans subtly toward the pointer side.\n    float side = clamp((uv.x - 0.5) * 2.0, -1.0, 1.0);\n    float leanBoost = 1.0 + 0.16 * lean * side;\n\n    col += lineCol * grid * 0.85 * leanBoost;\n\n    // Rolling glow wave: a soft band of extra light traveling down the\n    // plane toward the viewer, tinted with the glow color. Replaces a hard\n    // stripe with light that lives on the lines themselves.\n    float wavePhase = fract(worldZ / (cell * 9.0));\n    float wave = exp(-pow((wavePhase - 0.5) * 3.2, 2.0));\n    col += uGlow * grid * wave * 0.7 * uHeat;\n\n    // Faint plane sheen so the ground is not pure void between lines.\n    col += uGlow * fog * exp(-h * 9.0) * 0.10 * uHeat;\n  }\n\n  // ------------------------------------------------------------- horizon --\n  float dy = uv.y - HORIZON;\n  // Horizontal emphasis follows the pointer: a wide, subtle hot spot.\n  float px2 = (uv.x - ptr.x) * aspect;\n  float pointerSpot = exp(-px2 * px2 * 2.4);\n  float emphasis = 0.8 + 0.35 * pointerSpot;\n  // Breathing on the wide bloom only; the core line stays steady.\n  float breathe = 0.86 + 0.14 * sin(t * 0.55);\n\n  float coreLine = exp(-abs(dy) * 260.0);\n  float innerBloom = exp(-abs(dy) * 34.0);\n  float wideBloom = exp(-abs(dy) * 8.5);\n\n  vec3 hotCore = mix(uGlow, vec3(1.0), 0.55);\n  col += hotCore * coreLine * 1.1 * emphasis * uHeat;\n  col += mix(uGlow, uLine, 0.35) * innerBloom * 0.40 * emphasis * uHeat;\n  col += uGlow * wideBloom * 0.34 * breathe * emphasis * uHeat;\n\n  // ----------------------------------------------------------------- sun --\n  if (uSunOn > 0.5) {\n    vec2 sp = vec2((uv.x - 0.5) * aspect, dy);\n    float pulse = 1.0 + 0.035 * sin(t * 0.9);\n    float R = 0.20 * pulse;\n    float sd = length(sp) - R;\n    float aa = 1.6 / resY;\n\n    // Only the upper semicircle, with a hair of forgiveness at the base so\n    // the disc visually sits ON the horizon line.\n    float upper = smoothstep(-aa, aa, sp.y);\n    float sy = clamp(sp.y / R, 0.0, 1.0); // 0 at the base, 1 at the top\n\n    // Scanline slits: horizontal cuts across the disc that widen toward\n    // the base and close up toward the top - the classic synthwave sun.\n    float f = fract(sp.y / R * 7.0 - t * 0.12);\n    float gap = mix(0.48, 0.0, smoothstep(0.05, 0.8, sy));\n    float slit = smoothstep(gap, gap + 0.10, f);\n\n    float disc = smoothstep(aa, -aa, sd) * upper * slit;\n\n    // Radial gradient: bright sun color at the top, melting into the\n    // horizon glow at the base.\n    vec3 sunTop = mix(uSun, vec3(1.0), 0.30);\n    vec3 sunCol = mix(uGlow, sunTop, smoothstep(0.0, 0.9, sy));\n    col += sunCol * disc * 1.15 * uHeat;\n\n    // Soft outer bloom hugging the disc, above the horizon only.\n    float bloom = exp(-max(sd, 0.0) * 11.0);\n    col += uSun * bloom * upper * 0.30 * uHeat;\n  }\n\n  // ----------------------------------------------------------------- sky --\n  if (dy > 0.0) {\n    // Restrained star sparkle, kept clear of the horizon glow.\n    float skyMask = smoothstep(0.10, 0.30, dy);\n    vec2 sc = uv * vec2(aspect, 1.0) * 70.0;\n    vec2 id = floor(sc);\n    float pick = hash21(id);\n    if (pick > 0.994) {\n      vec2 starPos = vec2(hash21(id + 7.31), hash21(id + 13.7));\n      float d = length(fract(sc) - starPos);\n      float star = pow(max(0.0, 1.0 - d * 2.4), 4.0);\n      float twinkle = 0.55 + 0.45 * sin(t * 1.4 + pick * 61.0);\n      col += vec3(0.82, 0.86, 1.0) * star * twinkle * skyMask * 0.55;\n    }\n  }\n\n  // ------------------------------------------------------------- surface --\n  // Whisper-quiet CRT scanlines across the whole frame.\n  col *= 1.0 - 0.03 * (0.5 + 0.5 * sin(gl_FragCoord.y * 1.6));\n\n  // Soft tone curve so the stacked bloom rolls off instead of clipping.\n  col = col / (col + vec3(0.62));\n\n  // Gentle vignette to sink the plane into black at the frame edges.\n  vec2 vg = (uv - vec2(0.5, 0.42)) * vec2(aspect, 1.0);\n  col *= 1.0 - 0.30 * dot(vg, vg);\n\n  // Film-grain dithering to defeat 8-bit banding in the glows.\n  float grain = hash21(gl_FragCoord.xy + fract(t) * 137.0) - 0.5;\n  col += grain * (1.8 / 255.0);\n\n  gl_FragColor = vec4(max(col, 0.0), 1.0);\n}\n`;\n\n/**\n * A neon rear-view outrun sports car sitting on the grid — pure SVG, so it\n * stays crisp at any size. Body tones follow the grid palette; the tail-light\n * bar and underglow read as classic driving-game chrome.\n */\nfunction OutrunCar({ line, sun }: { line: string; sun: string }) {\n  return (\n    <svg viewBox=\"0 0 200 118\" className=\"h-auto w-full\" fill=\"none\">\n      {/* cyan underglow pooling on the road */}\n      <ellipse cx=\"100\" cy=\"100\" rx=\"82\" ry=\"10\" fill={sun} opacity=\"0.30\"\n        style={{ filter: \"blur(6px)\" }} />\n      {/* rear wheels */}\n      <rect x=\"24\" y=\"70\" width=\"26\" height=\"30\" rx=\"7\" fill=\"#08060f\" />\n      <rect x=\"150\" y=\"70\" width=\"26\" height=\"30\" rx=\"7\" fill=\"#08060f\" />\n      <rect x=\"27\" y=\"76\" width=\"20\" height=\"6\" rx=\"3\" fill={line} opacity=\"0.5\" />\n      <rect x=\"153\" y=\"76\" width=\"20\" height=\"6\" rx=\"3\" fill={line} opacity=\"0.5\" />\n      {/* body */}\n      <g style={{ filter: `drop-shadow(0 0 4px ${line})` }}>\n        <path\n          d=\"M20 84 L30 62 Q34 55 44 54 L156 54 Q166 55 170 62 L180 84 Q181 90 174 90 L26 90 Q19 90 20 84 Z\"\n          fill=\"#170a2b\" stroke={line} strokeWidth=\"1.6\" />\n        {/* cabin + rear glass */}\n        <path d=\"M58 54 L70 39 Q72 36 78 36 L122 36 Q128 36 130 39 L142 54 Z\"\n          fill=\"#12081f\" stroke={line} strokeWidth=\"1.4\" />\n        <path d=\"M70 51 L79 41 L121 41 L130 51 Z\" fill=\"#0a1436\" opacity=\"0.9\" />\n      </g>\n      {/* signature tail-light bar */}\n      <g style={{ filter: \"drop-shadow(0 0 6px #ff2d6b)\" }}>\n        <rect x=\"40\" y=\"64\" width=\"120\" height=\"7\" rx=\"3.5\" fill=\"#ff2d6b\" />\n        <rect x=\"46\" y=\"66\" width=\"108\" height=\"3\" rx=\"1.5\" fill=\"#ffd0dd\" opacity=\"0.85\" />\n      </g>\n      {/* diffuser fins */}\n      <g stroke={sun} strokeWidth=\"1.4\" opacity=\"0.7\" strokeLinecap=\"round\">\n        <path d=\"M78 84 v4\" /><path d=\"M90 84 v4\" /><path d=\"M100 84 v4\" />\n        <path d=\"M110 84 v4\" /><path d=\"M122 84 v4\" />\n      </g>\n    </svg>\n  );\n}\n\n/**\n * Retro Forge — a synthwave DRIVE in GL. A perspective ground plane of glowing\n * magenta grid lines scrolls toward the viewer and melts into a layered violet\n * horizon bloom; an optional slit-scanned cyan sun sits on the line under a\n * faint star field, and a neon rear-view sports car cruises on the grid.\n * Built on the `ogl-canvas` harness: GPU-light, offscreen-paused,\n * reduced-motion-safe. Position it inside any `relative` container; it fills\n * and sits behind content.\n */\nexport function RetroForge({\n  colors = DEFAULT_COLORS,\n  speed = 1,\n  heat = 1,\n  cellSize = 60,\n  sun = true,\n  car = true,\n  paused = false,\n  idle = true,\n  className,\n  ...props\n}: RetroForgeProps) {\n  const reduced = useReducedMotion();\n  const uniforms = React.useMemo(\n    () => ({\n      uLine: { value: hexToRgb(colors[0]) },\n      uGlow: { value: hexToRgb(colors[1]) },\n      uSun: { value: hexToRgb(colors[2]) },\n      uSpeed: { value: speed },\n      uHeat: { value: Math.min(Math.max(heat, 0), 1) },\n      uCell: { value: Math.max(cellSize, 8) / 60 },\n      uSunOn: { value: sun ? 1 : 0 },\n    }),\n    [colors, speed, heat, cellSize, sun]\n  );\n\n  return (\n    <div\n      aria-hidden\n      className={cn(\"absolute inset-0 -z-10 overflow-hidden bg-neutral-950\", className)}\n      {...props}\n    >\n      <OglCanvas\n        data-crucible=\"retro-forge\"\n        className=\"absolute inset-0\"\n        fragment={FRAGMENT}\n        uniforms={uniforms}\n        paused={paused}\n      ghost={idle}\n        reducedMotionTime={5.2}\n      />\n      {car && (\n        <motion.div\n          className=\"pointer-events-none absolute bottom-[6%] left-1/2 w-[32%] max-w-[400px] min-w-[210px]\"\n          style={{ x: \"-50%\" }}\n          animate={paused || reduced ? undefined : { y: [0, -5, 0] }}\n          transition={{ duration: 4.5, repeat: Infinity, ease: \"easeInOut\" }}\n        >\n          <OutrunCar line={colors[0]} sun={colors[2]} />\n        </motion.div>\n      )}\n    </div>\n  );\n}\n"
    }
  ]
}