{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "aurora",
  "type": "registry:ui",
  "title": "Aurora",
  "description": "Soft northern-lights background: folding light curtains drawn in a shader with premultiplied alpha, so it glows on dark and washes on light.",
  "dependencies": [],
  "registryDependencies": [
    "https://kuro.abstergo.fyi/r/kura-theme.json",
    "https://kuro.abstergo.fyi/r/kura-merge-refs.json",
    "https://kuro.abstergo.fyi/r/kura-shader-color.json",
    "https://kuro.abstergo.fyi/r/shader-canvas.json",
    "https://kuro.abstergo.fyi/r/use-reduced-motion.json",
    "utils"
  ],
  "files": [
    {
      "path": "registry/kura/aurora/aurora.glsl.ts",
      "type": "registry:ui",
      "target": "components/kura/aurora/aurora.glsl.ts",
      "content": "import { GLSL_COLOR } from \"@/lib/kura/shader-color\";\n\n/**\n * Aurora: three layers of light curtains. Each curtain has a bright, sharp lower edge that wanders\n * along an fbm curve, folds sideways (x is warped by noise) and fades upward through the palette.\n * Vertical \"rays\" come from noise stretched along y. Light is accumulated in linear RGB and written\n * as premultiplied alpha, so the aurora composites over any background — luminous on dark,\n * watercolour-like on light. Optional stars twinkle behind it on dark backgrounds.\n */\nexport const AURORA_FRAG = /* glsl */ `\nuniform vec3 u_low;   // lower edge colour (linear RGB)\nuniform vec3 u_mid;\nuniform vec3 u_high;  // upper fringe colour\nuniform float u_intensity;\nuniform float u_stars;\nuniform float u_light; // 1 when the background is light\n\n${GLSL_COLOR}\n\nfloat hash12(vec2 p) {\n  vec3 p3 = fract(vec3(p.xyx) * 0.1031);\n  p3 += dot(p3, p3.yzx + 33.33);\n  return fract((p3.x + p3.y) * p3.z);\n}\nfloat vnoise(vec2 p) {\n  vec2 i = floor(p), f = fract(p);\n  vec2 u = f * f * (3.0 - 2.0 * f);\n  return mix(mix(hash12(i), hash12(i + vec2(1, 0)), u.x),\n             mix(hash12(i + vec2(0, 1)), hash12(i + vec2(1, 1)), u.x), u.y);\n}\nfloat fbm(vec2 p) {\n  float v = 0.0, a = 0.5;\n  for (int i = 0; i < 4; i++) { v += a * vnoise(p); p = p * 2.03 + 17.1; a *= 0.5; }\n  return v;\n}\n\nvec3 curtain(vec2 uv, float aspect, float t, float i) {\n  float x = uv.x * aspect;\n  // Folds: warp x by slow noise so the ribbon bends like fabric.\n  float fold = fbm(vec2(x * 0.7 + i * 3.1, t * 0.1 + i * 2.0)) - 0.5;\n  float xw = x + fold * 1.1;\n  // The lower edge snakes across the sky.\n  float edge = 0.42 + 0.09 * i\n    + 0.26 * (fbm(vec2(xw * 0.9 - t * 0.05 + i * 7.3, i * 1.7)) - 0.5)\n    + 0.04 * sin(xw * 2.3 + t * 0.15 + i * 2.0);\n  float d = uv.y - edge;\n  // Ribbons break up along their length, leaving dark sky between them.\n  float seg = smoothstep(0.3, 0.56, fbm(vec2(xw * 0.45 + i * 5.3 + t * 0.03, i * 3.3)));\n  // Rays: noise stretched vertically, drifting along the ribbon; they also set each ray's height.\n  float rays = fbm(vec2(xw * 16.0 + t * 0.3 * (1.0 + i * 0.3), uv.y * 0.5 - t * 0.04));\n  rays = smoothstep(0.2, 0.8, rays);\n  float height = 0.06 + 0.16 * rays * (1.0 - i * 0.25);\n  float body = smoothstep(-0.008, 0.012, d) * exp(-max(d, 0.0) / height);\n  float rim = exp(-abs(d) * 70.0) * 0.35; // the bright, crisp lower edge\n  // On light backgrounds strong ray contrast reads as grey streaks, so soften it into a wash.\n  float rr = mix(rays, 0.55 + 0.45 * rays, u_light);\n  float a = (body * (0.45 + 0.75 * rr) + rim) * seg * (1.0 - i * 0.25);\n  vec3 c = mix(u_low, u_mid, smoothstep(0.0, 0.08, d));\n  c = mix(c, u_high, smoothstep(0.06, 0.24, d));\n  return c * a;\n}\n\nvoid main() {\n  float aspect = u_resolution.x / u_resolution.y;\n  vec2 uv = gl_FragCoord.xy / u_resolution;\n  float t = u_time;\n\n  vec3 light = vec3(0.0);\n  for (int i = 0; i < 3; i++) light += curtain(uv, aspect, t, float(i));\n  light *= u_intensity * 1.2;\n\n  // Stars on dark backgrounds, hidden behind bright curtains.\n  float stars = 0.0;\n  if (u_stars > 0.0) {\n    vec2 g = gl_FragCoord.xy / 3.0;\n    vec2 cell = floor(g);\n    float h = hash12(cell);\n    if (h > 0.992) {\n      vec2 o = vec2(hash12(cell + 3.1), hash12(cell + 7.7));\n      float dd = length(fract(g) - o);\n      float tw = 0.6 + 0.4 * sin(t * (1.0 + h * 3.0) + h * 40.0);\n      stars = smoothstep(0.5, 0.0, dd) * tw * (h - 0.992) * 110.0 * smoothstep(0.15, 0.9, uv.y);\n    }\n    stars *= u_stars * (1.0 - u_light) * (1.0 - clamp(dot(light, vec3(1.0)), 0.0, 1.0));\n  }\n  light += vec3(stars) * 0.8;\n\n  // Light → premultiplied colour. On light backgrounds the curtains become a saturated ink wash\n  // (deeper, never desaturated toward grey) with a gentler alpha curve.\n  float peak = max(light.r, max(light.g, light.b));\n  float a = clamp(1.0 - exp(-peak * mix(1.8, 1.5, u_light)), 0.0, 1.0) * mix(1.0, 0.92, u_light);\n  vec3 c = peak > 0.0 ? light / peak : vec3(0.0);\n  vec3 ink = c * 0.8;\n  c = mix(c, ink, u_light);\n  vec3 premul = linearToSrgb(c) * a;\n  premul += (ign(gl_FragCoord.xy) - 0.5) / 255.0 * a;\n  fragColor = vec4(premul, a);\n}\n`;\n"
    },
    {
      "path": "registry/kura/aurora/aurora.tsx",
      "type": "registry:ui",
      "target": "components/kura/aurora/aurora.tsx",
      "content": "\"use client\";\n\nimport * as React from \"react\";\nimport { useReducedMotion } from \"@/hooks/kura/use-reduced-motion\";\nimport { cn } from \"@/lib/utils\";\nimport { mergeRefs } from \"@/lib/kura/merge-refs\";\nimport { useResolvedColors } from \"@/lib/kura/shader-color\";\nimport { ShaderCanvas } from \"@/components/kura/shader-canvas\";\nimport { AURORA_FRAG } from \"./aurora.glsl\";\n\nexport type AuroraPreset = \"signal\" | \"polar\" | \"ember\" | \"mist\";\n\n/** Palettes as [lower edge, middle, upper fringe]. `signal` follows the theme's accent. */\nexport const auroraPresets: Record<AuroraPreset, readonly [string, string, string]> = {\n  signal: [\n    \"var(--kura-accent)\",\n    \"color-mix(in oklab, var(--kura-accent) 55%, oklch(0.62 0.11 215))\",\n    \"oklch(0.45 0.09 245)\",\n  ],\n  polar: [\"oklch(0.86 0.19 152)\", \"oklch(0.74 0.13 190)\", \"oklch(0.48 0.12 255)\"],\n  ember: [\"oklch(0.88 0.14 88)\", \"oklch(0.72 0.16 52)\", \"oklch(0.45 0.1 30)\"],\n  mist: [\"oklch(0.92 0.01 85)\", \"oklch(0.75 0.015 220)\", \"oklch(0.5 0.02 240)\"],\n};\n\nexport interface AuroraProps extends React.ComponentProps<\"div\"> {\n  /** A preset, or three CSS colours `[lower edge, middle, upper fringe]`. Default \"signal\". */\n  colors?: AuroraPreset | readonly [string, string, string];\n  /** Brightness of the curtains, 0–1. Default 0.8. */\n  intensity?: number;\n  /** Animation speed multiplier. Default 1. */\n  speed?: number;\n  /** Twinkling stars behind the aurora (only drawn on dark backgrounds). Default true. */\n  stars?: boolean;\n  /** Freeze the animation on the current frame. Default false. */\n  paused?: boolean;\n  /** Device-pixel-ratio cap. Default 1.5. */\n  maxDpr?: number;\n}\n\n/**\n * A soft aurora (northern lights) background: folding light curtains with crisp lower edges and\n * vertical rays, drawn in a fragment shader with premultiplied alpha so it sits on any surface —\n * luminous on dark, a watercolour wash on light. Children render on top. CSS fallback without\n * WebGL; a still frame under reduced motion.\n */\nexport function Aurora({\n  colors = \"signal\",\n  intensity = 0.8,\n  speed = 1,\n  stars = true,\n  paused = false,\n  maxDpr = 1.5,\n  className,\n  children,\n  ref,\n  ...props\n}: AuroraProps) {\n  const rootRef = React.useRef<HTMLDivElement>(null);\n  const reducedMotion = useReducedMotion();\n  const palette = typeof colors === \"string\" ? auroraPresets[colors] : colors;\n  const list = React.useMemo(() => [...palette, \"var(--kura-canvas)\"], [palette]);\n  const { colors: resolved, version } = useResolvedColors(rootRef, list);\n\n  // `version` bumps when the theme flips: a new uniforms object repaints a still frame.\n  // biome-ignore lint/correctness/useExhaustiveDependencies: version is the repaint trigger\n  const uniforms = React.useMemo(() => {\n    const col = (i: number) => () =>\n      (resolved.current[i]?.linear ?? [0.3, 0.6, 0.5]) as [number, number, number];\n    return {\n      u_low: col(0),\n      u_mid: col(1),\n      u_high: col(2),\n      u_intensity: Math.min(1, Math.max(0, intensity)),\n      u_stars: stars ? 1 : 0,\n      u_light: () => ((resolved.current[3]?.oklab[0] ?? 1) > 0.55 ? 1 : 0),\n    };\n  }, [resolved, version, intensity, stars]);\n\n  const isStill = reducedMotion || paused || speed === 0;\n  const [low, mid, high] = palette;\n\n  return (\n    <div\n      ref={mergeRefs(rootRef, ref)}\n      data-slot=\"aurora\"\n      data-state={isStill ? \"still\" : \"animating\"}\n      className={cn(\"relative isolate overflow-hidden\", className)}\n      {...props}\n    >\n      <ShaderCanvas\n        data-slot=\"aurora-canvas\"\n        fragmentShader={AURORA_FRAG}\n        uniforms={uniforms}\n        paused={paused || speed === 0}\n        speed={speed * 0.6}\n        maxDpr={maxDpr}\n        staticTime={12}\n        className=\"-z-10 absolute inset-0\"\n        fallback={\n          <div\n            aria-hidden\n            data-slot=\"aurora-fallback\"\n            className=\"absolute inset-0 opacity-70 blur-2xl\"\n            style={{\n              background: `radial-gradient(60% 28% at 30% 62%, ${low}, transparent 70%), radial-gradient(50% 30% at 70% 55%, ${mid}, transparent 70%), radial-gradient(70% 30% at 50% 80%, ${high}, transparent 75%)`,\n            }}\n          />\n        }\n      />\n      {children}\n    </div>\n  );\n}\n"
    },
    {
      "path": "registry/kura/aurora/index.ts",
      "type": "registry:ui",
      "target": "components/kura/aurora/index.ts",
      "content": "export { Aurora, type AuroraProps } from \"./aurora\";\n"
    }
  ],
  "categories": [
    "effects",
    "aurora",
    "northern-lights",
    "background",
    "webgl"
  ],
  "docs": "Docs: https://kuro.abstergo.fyi/components/aurora · Markdown for agents: https://kuro.abstergo.fyi/components/aurora.md",
  "meta": {
    "kura": {
      "status": "beta",
      "whenToUse": [
        "A sign-in, landing or 'welcome back' screen that should feel calm and a little magical.",
        "A night-themed hero or a header band above an agent dashboard.",
        "A background that must sit on top of an existing surface colour (it is transparent where there is no light)."
      ],
      "whenNotToUse": [
        "A colourful blob background filling the whole area — use mesh-gradient.",
        "Showing that an agent is busy — use border-beam or thinking-indicator.",
        "A voice agent's presence — use agent-orb.",
        "Behind dense UI or long text: the curtains move; keep content short and centred."
      ]
    }
  }
}
