{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "corona",
  "type": "registry:ui",
  "title": "Corona",
  "description": "A classic aurora: draping vertical light curtains fold and sway with height, threaded with shimmering ray filaments over a faint twinkling starfield, in teal, violet, and ice blue. The curtains lean gently toward the pointer. Original OGL fragment shader, offscreen-paused and reduced-motion-safe; the curtains idle on their own when untouched.",
  "dependencies": [
    "ogl"
  ],
  "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/corona/corona.tsx",
      "type": "registry:ui",
      "target": "components/crucible/corona.tsx",
      "content": "\"use client\";\n\nimport * as React from \"react\";\n\nimport { cn } from \"@/lib/utils\";\nimport { OglCanvas } from \"@/lib/ogl-canvas\";\n\nexport interface CoronaProps {\n  /**\n   * Three tones for the ribbon gradient: `[crest, mid, base]`.\n   * Any hex string. Defaults to aurora teal / violet / ice blue.\n   */\n  colors?: [string, string, string];\n  /** Number of ribbon bands, clamped 3–6. Default 4. */\n  bands?: number;\n  /** Flow speed multiplier. 1 = default. Default 1. */\n  speed?: number;\n  /** Overall brightness / bloom of the ribbons. 1 = default. Default 1. */\n  intensity?: number;\n  /** Freeze the animation, holding the current frame. */\n  paused?: boolean;\n  /**\n   * Idle autoplay: after ~1.5s with no pointer present, a seeded ghost drifts\n   * slowly across the field and leans the curtains, so thumbnails and heroes\n   * never look still; a real pointer takes over instantly. Never runs under\n   * reduced motion. Default true.\n   */\n  idle?: boolean;\n  /** Extra classes for the wrapper. Positioned `absolute inset-0 -z-10`. */\n  className?: string;\n}\n\nconst DEFAULT_COLORS: [string, string, string] = [\n  \"#5eead4\", // aurora teal — crest\n  \"#8b5cf6\", // violet — mid\n  \"#93c5fd\", // ice blue — base lift\n];\n\n/** Hex → linear-ish [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.37, 0.92, 0.83];\n  return [((int >> 16) & 255) / 255, ((int >> 8) & 255) / 255, (int & 255) / 255];\n}\n\n/**\n * Original aurora-curtain fragment shader. Real northern lights are hanging\n * vertical sheets, not horizontal bands — so each \"band\" here is a draping\n * light CURTAIN: a soft vertical column whose horizontal position folds and\n * sways with height (domain-warped fbm) like fabric in a slow wind, threaded\n * with fine vertical ray filaments that shimmer, brightest along a ragged\n * lower edge and fading up into darkness. Layers are stacked at different\n * parallax depths for atmosphere, over a deep-space gradient and a faint\n * twinkling starfield. The vertical color ramp runs teal -> violet -> ice.\n * Exponential tone curve + film-grain dithering kill 8-bit banding — a real\n * aurora has none. GLSL ES 1.00 (WebGL1/2 compatible). No fract() on spatial\n * coordinates (only inside the hash), so there are no wrap seams.\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/ghost presence\n\nuniform vec3 uColorA; // crest — the bright teal body near the base\nuniform vec3 uColorB; // mid   — violet through the middle\nuniform vec3 uColorC; // base  — ice blue at the ragged tips\nuniform float uBands;\nuniform float uSpeed;\nuniform float uIntensity;\n\nconst int MAX_BANDS = 6;\n\n// --- compact value noise + fbm (original) ---\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\nfloat vnoise(vec2 p) {\n  vec2 i = floor(p);\n  vec2 f = fract(p);\n  float a = hash21(i);\n  float b = hash21(i + vec2(1.0, 0.0));\n  float c = hash21(i + vec2(0.0, 1.0));\n  float d = hash21(i + vec2(1.0, 1.0));\n  vec2 u = f * f * (3.0 - 2.0 * f);\n  return mix(mix(a, b, u.x), mix(c, d, u.x), u.y);\n}\n\nfloat fbm(vec2 p) {\n  float v = 0.0;\n  float amp = 0.5;\n  // Rotated lacunarity kills the axis-aligned look of raw value noise.\n  mat2 m = mat2(1.6, 1.2, -1.2, 1.6);\n  for (int i = 0; i < 4; i++) {\n    v += amp * vnoise(p);\n    p = m * p;\n    amp *= 0.5;\n  }\n  return v;\n}\n\n// Vertical color ramp along a curtain: teal body low, violet mid, ice tips.\nvec3 auroraRamp(float y) {\n  vec3 c = mix(uColorA, uColorB, smoothstep(0.10, 0.52, y));\n  c = mix(c, uColorC, smoothstep(0.52, 0.92, y));\n  return c;\n}\n\nvoid main() {\n  vec2 uv = vUv;\n  float aspect = uResolution.x / max(uResolution.y, 1.0);\n  float t = uTime * uSpeed;\n\n  int bandCount = int(clamp(uBands, 3.0, float(MAX_BANDS)));\n\n  // Curtains lean gently toward the pointer — a lean, never a spotlight.\n  // Scaled by presence so the sky settles upright when nobody is there.\n  float lean = clamp((uPointer.x - 0.5) * 2.0, -1.0, 1.0) * uPointerActive;\n\n  // ---- deep space: a cool vertical gradient, darker toward the top ----\n  vec3 col = mix(vec3(0.020, 0.030, 0.052), vec3(0.006, 0.009, 0.020),\n                 smoothstep(0.0, 1.0, uv.y));\n  // Faint teal airglow hugging the horizon so the base never reads as dead.\n  col += uColorA * 0.055 * pow(1.0 - uv.y, 3.0);\n\n  // ---- starfield behind the aurora, strongest up in the dark sky ----\n  vec2 sg = uv * vec2(aspect, 1.0) * 118.0;\n  vec2 sid = floor(sg);\n  float pick = hash21(sid);\n  if (pick > 0.985) {\n    vec2 sp = vec2(hash21(sid + 3.17), hash21(sid + 7.73));\n    float sd = length(fract(sg) - sp);\n    float star = pow(max(0.0, 1.0 - sd * 3.0), 5.0);\n    float twinkle = 0.55 + 0.45 * sin(t * 1.6 + pick * 51.0);\n    col += vec3(0.72, 0.80, 1.0) * star * twinkle * smoothstep(0.18, 0.6, uv.y) * 0.9;\n  }\n\n  // ---- draping light curtains ----\n  for (int i = 0; i < MAX_BANDS; i++) {\n    if (i >= bandCount) break;\n    float fi = float(i);\n    float depth = fi / float(MAX_BANDS - 1); // 0 near .. 1 far\n\n    float par = mix(1.0, 1.9, depth);   // farther curtains: tighter, more of them\n    float bri = mix(1.0, 0.42, depth);  // and dimmer with distance\n    // Slow bodily drift of the whole curtain sideways across the sky.\n    float drift = t * (0.045 + 0.022 * fi);\n    // A slow clock feeds the domain warp — this is the sheet's WAVER. Kept\n    // gentle so the folds drape and evolve gracefully; a fast clock here makes\n    // the wisps whip around frantically, which is the opposite of a real aurora.\n    float warpT = t * (0.055 + 0.018 * fi);\n\n    // Horizontal FOLD: the curtain's x position bends as a function of height,\n    // domain-warped in three octaves so the sheet drapes and gathers like a\n    // hanging veil in a slow wind. The higher octaves evolve only a little\n    // faster than the base (not multiples faster) and carry less weight, so the\n    // sheet reads as one cohesive, fluid drape rather than a mess of ripples.\n    float foldA = fbm(vec2(uv.y * 1.05 + fi * 3.1, warpT)) - 0.5;\n    float foldB = fbm(vec2(uv.y * 2.4 - fi * 1.7, warpT * 1.3 + 4.0)) - 0.5;\n    float foldC = fbm(vec2(uv.y * 4.6 + fi * 2.3, warpT * 1.7 + 9.0)) - 0.5;\n    float fold = foldA * 1.4 + foldB * 0.42 + foldC * 0.14;\n\n    // Slow lateral undulation: the whole sheet breathes side to side, a couple\n    // of low sine modes phase-offset up its height so it sways rather than\n    // sliding rigidly. This is the big, calm aurora \"wander\" — kept unhurried.\n    float sway = sin(uv.y * 2.3 + t * 0.12 + fi * 1.7) * 0.32\n               + sin(uv.y * 4.1 - t * 0.18 + fi) * 0.12;\n\n    // Sheet coordinate: pointer nudges the whole curtain sideways.\n    float sx = (uv.x - 0.5) * aspect * par + fold + sway + drift * 0.5\n             + fi * 5.3 + lean * 0.22;\n\n    // Envelope: wide soft columns carved out of low-frequency noise, so the\n    // aurora is a few distinct curtains with dark gaps, not a wall. A slow time\n    // term lets each curtain widen and narrow — breathing — as it drifts.\n    float env = fbm(vec2(sx * 0.85, fi * 9.0 + warpT * 0.55));\n    float sheet = smoothstep(0.44, 0.74, env);\n\n    // Ray filaments: fine vertical striations threading the sheet, their phase\n    // jittered by noise so they never read as a comb. They shimmer and stream\n    // upward, giving the wispy, flickering texture of real curtain rays.\n    float rn = vnoise(vec2(sx * 3.2, uv.y * 1.4 - warpT * 1.6));\n    float rays = 0.60 + 0.40 * sin(sx * 17.0 + rn * 7.0 - uv.y * 5.0 + t * 1.15);\n    sheet *= mix(1.0, rays, 0.46);\n\n    // Vertical profile: fade in off the base, ragged top edge that now sways\n    // and frays in time with the sheet, and a brightness bias toward the body.\n    float topEdge = 0.86 + 0.15 * (fbm(vec2(sx * 0.7, warpT * 0.6 + 3.0)) - 0.5);\n    float vprof = smoothstep(0.04, 0.20, uv.y)\n                * (1.0 - smoothstep(topEdge - 0.34, topEdge, uv.y));\n    vprof *= mix(1.30, 0.55, smoothstep(0.12, 0.85, uv.y));\n\n    float lum = sheet * vprof * bri;\n\n    vec3 grad = auroraRamp(uv.y);\n    col += grad * lum * 1.55 * uIntensity;\n\n    // Hot lower spine: a tighter, brighter additive pass along the bright\n    // teal body so each curtain has a luminous core, not a flat wash.\n    vec3 hot = mix(uColorA, vec3(0.80, 1.0, 0.94), 0.32);\n    col += hot * pow(lum, 1.7) * 0.55 * uIntensity;\n  }\n\n  // Soft exponential tone curve so stacked curtains bloom toward white and\n  // roll off asymptotically instead of clipping to flat patches.\n  col = 1.0 - exp(-col * 1.55);\n\n  // Gentle vignette to sink the frame edges into space.\n  vec2 vg = (uv - vec2(0.5, 0.46)) * vec2(aspect, 1.0);\n  col *= 1.0 - 0.32 * dot(vg, vg);\n\n  // Film-grain dithering to defeat 8-bit banding in the smooth gradients.\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 * Corona — a real aurora. Draping vertical light curtains fold and sway with\n * height, threaded with shimmering ray filaments, brightest along a ragged\n * lower edge and fading up into a deep-space starfield. Vertical color ramp\n * of teal / violet / ice by default. Built on the `ogl-canvas` harness:\n * GPU-light, offscreen-paused, reduced-motion-safe. Drop it inside any\n * `relative` container; it fills and sits behind content.\n */\nexport function Corona({\n  colors = DEFAULT_COLORS,\n  bands = 4,\n  speed = 1,\n  intensity = 1,\n  paused = false,\n  idle = true,\n  className,\n}: CoronaProps) {\n  const uniforms = React.useMemo(\n    () => ({\n      uColorA: { value: hexToRgb(colors[0]) },\n      uColorB: { value: hexToRgb(colors[1]) },\n      uColorC: { value: hexToRgb(colors[2]) },\n      uBands: { value: bands },\n      uSpeed: { value: speed },\n      uIntensity: { value: intensity },\n    }),\n    [colors, bands, speed, intensity]\n  );\n\n  return (\n    <OglCanvas\n      aria-hidden\n      data-crucible=\"corona\"\n      className={cn(\"absolute inset-0 -z-10\", className)}\n      fragment={FRAGMENT}\n      uniforms={uniforms}\n      paused={paused}\n      ghost={idle}\n      reducedMotionTime={3.4}\n    />\n  );\n}\n"
    }
  ]
}