{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "silk-aurora",
  "type": "registry:ui",
  "title": "Silk Aurora",
  "dependencies": [],
  "devDependencies": [],
  "registryDependencies": [],
  "description": "Component for silk-aurora",
  "files": [
    {
      "path": "components/ui/silk-aurora.tsx",
      "content": "\"use client\";\n\nimport * as React from \"react\";\nimport { cn } from \"@/lib/utils\";\nimport {\n  WebGLErrorBoundary,\n  WebGLFallback,\n} from \"./webgl-error-boundary\";\n\nconst VERTEX_SHADER = `\nattribute vec2 position;\n\nvoid main() {\n  gl_Position = vec4(position, 0.0, 1.0);\n}\n`;\n\nconst FRAGMENT_SHADER = `\nprecision highp float;\n\nuniform vec2 u_res;\nuniform vec2 u_mouse;\nuniform float u_time;\nuniform float u_speed;\nuniform float u_intensity;\nuniform float u_grain;\nuniform float u_vignette;\nuniform float u_mouseInfluence;\nuniform vec3 u_base;\nuniform vec3 u_mid;\nuniform vec3 u_sheen;\nuniform vec3 u_accent;\n\nfloat hash(vec2 p) {\n  return fract(sin(dot(p, vec2(41.93, 289.17))) * 43758.5453123);\n}\n\nfloat noise(vec2 p) {\n  vec2 i = floor(p);\n  vec2 f = fract(p);\n  vec2 u = f * f * (3.0 - 2.0 * f);\n\n  float a = hash(i);\n  float b = hash(i + vec2(1.0, 0.0));\n  float c = hash(i + vec2(0.0, 1.0));\n  float d = hash(i + vec2(1.0, 1.0));\n\n  return mix(mix(a, b, u.x), mix(c, d, u.x), u.y);\n}\n\nfloat fbm(vec2 p) {\n  float value = 0.0;\n  float amp = 0.5;\n  mat2 rot = mat2(0.82, 0.57, -0.57, 0.82);\n\n  for (int i = 0; i < 5; i++) {\n    value += amp * noise(p);\n    p = rot * p * 2.03;\n    amp *= 0.5;\n  }\n\n  return value;\n}\n\nfloat ribbon(vec2 p, float offset, float width, float softness) {\n  float y = p.y + sin(p.x * 1.8 + offset) * 0.18;\n  y += sin(p.x * 4.2 - offset * 0.7) * 0.045;\n  return smoothstep(width + softness, width, abs(y));\n}\n\nvoid main() {\n  vec2 uv = gl_FragCoord.xy / u_res;\n  float aspect = u_res.x / max(u_res.y, 1.0);\n  vec2 p = (uv - 0.5) * vec2(aspect, 1.0);\n\n  vec2 mouse = (u_mouse - 0.5) * vec2(aspect, 1.0);\n  float t = u_time * 0.12 * u_speed;\n  float pointerFalloff = smoothstep(0.72, 0.0, length(p - mouse));\n  p += (mouse - p) * pointerFalloff * 0.05 * u_mouseInfluence;\n\n  vec2 silk = p;\n  silk.x += fbm(p * 1.6 + vec2(t * 0.8, -t * 0.35)) * 0.16;\n  silk.y += fbm(p * 2.2 + vec2(-t * 0.25, t * 0.7)) * 0.10;\n\n  float veilA = ribbon(silk + vec2(-0.18, 0.08), t * 2.1, 0.055, 0.22);\n  float veilB = ribbon(silk * vec2(0.86, 1.18) + vec2(0.2, -0.14), -t * 2.8 + 1.7, 0.038, 0.18);\n  float veilC = ribbon(silk * vec2(1.18, 0.9) + vec2(-0.08, 0.24), t * 1.4 - 2.1, 0.03, 0.16);\n\n  float atmosphere = fbm(p * 1.35 + vec2(t * 0.22, -t * 0.1));\n  float pearlescent = pow(max(0.0, sin((p.x - p.y) * 7.5 + atmosphere * 4.0 - t * 2.5)), 5.0);\n  float glint = pow(max(0.0, noise(gl_FragCoord.xy * 0.065 + t * 18.0) - 0.72), 5.0);\n\n  vec3 col = u_base;\n  col = mix(col, u_mid, smoothstep(-0.45, 0.75, p.y + atmosphere * 0.75));\n  col += u_accent * veilA * 0.72 * u_intensity;\n  col += u_sheen * veilB * 0.64 * u_intensity;\n  col += mix(u_sheen, u_accent, 0.35) * veilC * 0.42 * u_intensity;\n  col += u_sheen * pearlescent * 0.075 * u_intensity;\n  col += vec3(1.0, 0.93, 0.82) * glint * 0.22 * u_intensity;\n  col += u_sheen * pointerFalloff * 0.08 * u_mouseInfluence;\n\n  float vignette = smoothstep(1.25, 0.22, length(p));\n  col *= mix(1.0 - u_vignette * 0.42, 1.06, vignette);\n\n  float grain = (hash(gl_FragCoord.xy + t * 90.0) - 0.5) * 0.08 * u_grain;\n  col += grain;\n\n  gl_FragColor = vec4(clamp(col, 0.0, 1.0), 1.0);\n}\n`;\n\nconst HEX_COLOR_REGEX = /^#?[0-9a-fA-F]{6}$/;\n\nconst DEFAULT_BASE = \"#050507\";\nconst DEFAULT_MID = \"#14151d\";\nconst DEFAULT_SHEEN = \"#f4dfb8\";\nconst DEFAULT_ACCENT = \"#6ed6c9\";\n\nfunction sanitizeHexColor(value: string, fallback: string) {\n  const trimmed = value.trim();\n  if (!HEX_COLOR_REGEX.test(trimmed)) {\n    return fallback;\n  }\n\n  return trimmed.startsWith(\"#\") ? trimmed : `#${trimmed}`;\n}\n\nfunction hexToRgb01(hex: string, fallback: string): [number, number, number] {\n  const normalized = sanitizeHexColor(hex, fallback).replace(\"#\", \"\");\n  const r = parseInt(normalized.slice(0, 2), 16) / 255;\n  const g = parseInt(normalized.slice(2, 4), 16) / 255;\n  const b = parseInt(normalized.slice(4, 6), 16) / 255;\n\n  return [r, g, b];\n}\n\nexport interface SilkAuroraProps extends React.HTMLAttributes<HTMLDivElement> {\n  title?: string;\n  subtitle?: string;\n  description?: string;\n  baseColor?: string;\n  midColor?: string;\n  sheenColor?: string;\n  accentColor?: string;\n  speed?: number;\n  intensity?: number;\n  grain?: number;\n  vignette?: number;\n  mouseInfluence?: number;\n  interactive?: boolean;\n  children?: React.ReactNode;\n}\n\nconst HEADLINE_CLASS =\n  \"max-w-[820px] text-[13cqi] font-semibold leading-[0.86] tracking-normal text-white md:text-[8cqi] lg:text-[6.4cqi]\";\n\nexport function SilkAurora({\n  title,\n  subtitle,\n  description,\n  baseColor = DEFAULT_BASE,\n  midColor = DEFAULT_MID,\n  sheenColor = DEFAULT_SHEEN,\n  accentColor = DEFAULT_ACCENT,\n  speed = 1,\n  intensity = 1,\n  grain = 0.85,\n  vignette = 1,\n  mouseInfluence = 1,\n  interactive = true,\n  className,\n  children,\n  style,\n  ...props\n}: SilkAuroraProps) {\n  const containerRef = React.useRef<HTMLDivElement | null>(null);\n  const canvasRef = React.useRef<HTMLCanvasElement | null>(null);\n  const mouseRef = React.useRef({ x: 0.5, y: 0.5 });\n  const targetMouseRef = React.useRef({ x: 0.5, y: 0.5 });\n  const [hasWebGLError, setHasWebGLError] = React.useState(false);\n\n  const settings = React.useMemo(\n    () => ({\n      baseColor,\n      midColor,\n      sheenColor,\n      accentColor,\n      speed,\n      intensity,\n      grain,\n      vignette,\n      mouseInfluence,\n      interactive,\n    }),\n    [\n      baseColor,\n      midColor,\n      sheenColor,\n      accentColor,\n      speed,\n      intensity,\n      grain,\n      vignette,\n      mouseInfluence,\n      interactive,\n    ],\n  );\n\n  React.useEffect(() => {\n    if (hasWebGLError) {\n      return;\n    }\n\n    const container = containerRef.current;\n    const canvas = canvasRef.current;\n    if (!container || !canvas) {\n      return;\n    }\n\n    const reducedMotion = window.matchMedia(\"(prefers-reduced-motion: reduce)\").matches;\n\n    const handlePointerMove = (event: PointerEvent) => {\n      if (!settings.interactive) {\n        return;\n      }\n\n      const rect = container.getBoundingClientRect();\n      targetMouseRef.current = {\n        x: (event.clientX - rect.left) / rect.width,\n        y: 1 - (event.clientY - rect.top) / rect.height,\n      };\n    };\n\n    const handlePointerLeave = () => {\n      targetMouseRef.current = { x: 0.5, y: 0.5 };\n    };\n\n    container.addEventListener(\"pointermove\", handlePointerMove);\n    container.addEventListener(\"pointerleave\", handlePointerLeave);\n\n    try {\n      const gl = canvas.getContext(\"webgl\", { antialias: false, alpha: false });\n      if (!gl) {\n        setHasWebGLError(true);\n        return () => {\n          container.removeEventListener(\"pointermove\", handlePointerMove);\n          container.removeEventListener(\"pointerleave\", handlePointerLeave);\n        };\n      }\n\n      const compileShader = (type: number, source: string) => {\n        const shader = gl.createShader(type);\n        if (!shader) {\n          return null;\n        }\n\n        gl.shaderSource(shader, source);\n        gl.compileShader(shader);\n        if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {\n          gl.deleteShader(shader);\n          return null;\n        }\n\n        return shader;\n      };\n\n      const vertexShader = compileShader(gl.VERTEX_SHADER, VERTEX_SHADER);\n      const fragmentShader = compileShader(gl.FRAGMENT_SHADER, FRAGMENT_SHADER);\n      if (!vertexShader || !fragmentShader) {\n        setHasWebGLError(true);\n        return;\n      }\n\n      const program = gl.createProgram();\n      if (!program) {\n        gl.deleteShader(vertexShader);\n        gl.deleteShader(fragmentShader);\n        setHasWebGLError(true);\n        return;\n      }\n\n      gl.attachShader(program, vertexShader);\n      gl.attachShader(program, fragmentShader);\n      gl.linkProgram(program);\n      if (!gl.getProgramParameter(program, gl.LINK_STATUS)) {\n        gl.deleteProgram(program);\n        gl.deleteShader(vertexShader);\n        gl.deleteShader(fragmentShader);\n        setHasWebGLError(true);\n        return;\n      }\n\n      gl.useProgram(program);\n\n      const position = gl.getAttribLocation(program, \"position\");\n      const buffer = gl.createBuffer();\n      gl.bindBuffer(gl.ARRAY_BUFFER, buffer);\n      gl.bufferData(\n        gl.ARRAY_BUFFER,\n        new Float32Array([-1, -1, 1, -1, -1, 1, 1, 1]),\n        gl.STATIC_DRAW,\n      );\n      gl.enableVertexAttribArray(position);\n      gl.vertexAttribPointer(position, 2, gl.FLOAT, false, 0, 0);\n\n      const uRes = gl.getUniformLocation(program, \"u_res\");\n      const uMouse = gl.getUniformLocation(program, \"u_mouse\");\n      const uTime = gl.getUniformLocation(program, \"u_time\");\n      const uSpeed = gl.getUniformLocation(program, \"u_speed\");\n      const uIntensity = gl.getUniformLocation(program, \"u_intensity\");\n      const uGrain = gl.getUniformLocation(program, \"u_grain\");\n      const uVignette = gl.getUniformLocation(program, \"u_vignette\");\n      const uMouseInfluence = gl.getUniformLocation(program, \"u_mouseInfluence\");\n      const uBase = gl.getUniformLocation(program, \"u_base\");\n      const uMid = gl.getUniformLocation(program, \"u_mid\");\n      const uSheen = gl.getUniformLocation(program, \"u_sheen\");\n      const uAccent = gl.getUniformLocation(program, \"u_accent\");\n\n      if (\n        !uRes ||\n        !uMouse ||\n        !uTime ||\n        !uSpeed ||\n        !uIntensity ||\n        !uGrain ||\n        !uVignette ||\n        !uMouseInfluence ||\n        !uBase ||\n        !uMid ||\n        !uSheen ||\n        !uAccent\n      ) {\n        gl.deleteBuffer(buffer);\n        gl.deleteProgram(program);\n        gl.deleteShader(vertexShader);\n        gl.deleteShader(fragmentShader);\n        setHasWebGLError(true);\n        return;\n      }\n\n      const resize = () => {\n        const dpr = Math.min(window.devicePixelRatio || 1, 2);\n        const { width, height } = container.getBoundingClientRect();\n        canvas.width = Math.max(1, Math.floor(width * dpr));\n        canvas.height = Math.max(1, Math.floor(height * dpr));\n        gl.viewport(0, 0, canvas.width, canvas.height);\n        gl.uniform2f(uRes, canvas.width, canvas.height);\n      };\n\n      resize();\n      const resizeObserver = new ResizeObserver(resize);\n      resizeObserver.observe(container);\n\n      const base = hexToRgb01(settings.baseColor, DEFAULT_BASE);\n      const mid = hexToRgb01(settings.midColor, DEFAULT_MID);\n      const sheen = hexToRgb01(settings.sheenColor, DEFAULT_SHEEN);\n      const accent = hexToRgb01(settings.accentColor, DEFAULT_ACCENT);\n\n      gl.uniform3f(uBase, base[0], base[1], base[2]);\n      gl.uniform3f(uMid, mid[0], mid[1], mid[2]);\n      gl.uniform3f(uSheen, sheen[0], sheen[1], sheen[2]);\n      gl.uniform3f(uAccent, accent[0], accent[1], accent[2]);\n\n      let rafId = 0;\n      const start = performance.now();\n\n      const render = (now: number) => {\n        mouseRef.current.x += (targetMouseRef.current.x - mouseRef.current.x) * 0.045;\n        mouseRef.current.y += (targetMouseRef.current.y - mouseRef.current.y) * 0.045;\n\n        const elapsed = reducedMotion ? 8 : (now - start) / 1000;\n\n        gl.uniform2f(uMouse, mouseRef.current.x, mouseRef.current.y);\n        gl.uniform1f(uTime, elapsed);\n        gl.uniform1f(uSpeed, reducedMotion ? 0 : settings.speed);\n        gl.uniform1f(uIntensity, settings.intensity);\n        gl.uniform1f(uGrain, settings.grain);\n        gl.uniform1f(uVignette, settings.vignette);\n        gl.uniform1f(\n          uMouseInfluence,\n          settings.interactive && !reducedMotion ? settings.mouseInfluence : 0,\n        );\n        gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);\n\n        rafId = requestAnimationFrame(render);\n      };\n\n      rafId = requestAnimationFrame(render);\n\n      return () => {\n        container.removeEventListener(\"pointermove\", handlePointerMove);\n        container.removeEventListener(\"pointerleave\", handlePointerLeave);\n        cancelAnimationFrame(rafId);\n        resizeObserver.disconnect();\n        gl.deleteBuffer(buffer);\n        gl.deleteProgram(program);\n        gl.deleteShader(vertexShader);\n        gl.deleteShader(fragmentShader);\n      };\n    } catch {\n      setHasWebGLError(true);\n      return () => {\n        container.removeEventListener(\"pointermove\", handlePointerMove);\n        container.removeEventListener(\"pointerleave\", handlePointerLeave);\n      };\n    }\n  }, [hasWebGLError, settings]);\n\n  const fallbackContent = (\n    <div\n      className={cn(\n        \"relative flex min-h-screen w-full items-center overflow-hidden bg-[#050507] text-white\",\n        className,\n      )}\n      style={{ containerType: \"size\", ...style }}\n      {...props}\n    >\n      <WebGLFallback className=\"absolute inset-0 h-full w-full\" />\n      {(title || subtitle || description || children) && (\n        <div className=\"relative z-10 mx-auto w-full max-w-[1240px] px-6 py-20 md:px-10 md:py-28\">\n          <div className=\"max-w-[760px]\">\n            {subtitle && (\n              <p className=\"mb-5 text-xs font-medium uppercase tracking-[0.24em] text-white/50\">\n                {subtitle}\n              </p>\n            )}\n            {title && <h1 className={HEADLINE_CLASS}>{title}</h1>}\n            {description && (\n              <p className=\"mt-7 max-w-[620px] text-base leading-relaxed text-white/68 md:text-xl\">\n                {description}\n              </p>\n            )}\n            {children && <div className=\"mt-10\">{children}</div>}\n          </div>\n        </div>\n      )}\n    </div>\n  );\n\n  if (hasWebGLError) {\n    return fallbackContent;\n  }\n\n  return (\n    <WebGLErrorBoundary fallback={fallbackContent}>\n      <div\n        ref={containerRef}\n        className={cn(\n          \"relative flex min-h-screen w-full items-center overflow-hidden bg-[#050507] text-white\",\n          className,\n        )}\n        style={{ containerType: \"size\", ...style }}\n        {...props}\n      >\n        <canvas\n          ref={canvasRef}\n          aria-hidden=\"true\"\n          className=\"pointer-events-none absolute inset-0 h-full w-full\"\n          style={{ width: \"100%\", height: \"100%\", display: \"block\" }}\n        />\n        <div className=\"pointer-events-none absolute inset-0 bg-[radial-gradient(circle_at_72%_34%,rgba(255,255,255,0.16),transparent_24%),radial-gradient(circle_at_18%_74%,rgba(110,214,201,0.13),transparent_30%)]\" />\n        <div className=\"pointer-events-none absolute inset-0 bg-[linear-gradient(90deg,rgba(0,0,0,0.46),rgba(0,0,0,0.14)_42%,rgba(0,0,0,0.42))]\" />\n        <div className=\"pointer-events-none absolute inset-x-0 bottom-0 h-1/3 bg-gradient-to-t from-black/55 to-transparent\" />\n\n        {(title || subtitle || description || children) && (\n          <div className=\"relative z-10 mx-auto w-full max-w-[1240px] px-6 py-20 md:px-10 md:py-28\">\n            <div className=\"max-w-[760px]\">\n              {subtitle && (\n                <p className=\"mb-5 text-xs font-medium uppercase tracking-[0.24em] text-white/50\">\n                  {subtitle}\n                </p>\n              )}\n              {title && <h1 className={HEADLINE_CLASS}>{title}</h1>}\n              {description && (\n                <p className=\"mt-7 max-w-[620px] text-base leading-relaxed text-white/68 md:text-xl\">\n                  {description}\n                </p>\n              )}\n              {children && <div className=\"mt-10\">{children}</div>}\n            </div>\n          </div>\n        )}\n      </div>\n    </WebGLErrorBoundary>\n  );\n}\n\nexport default SilkAurora;\n",
      "type": "registry:ui"
    },
    {
      "path": "components/ui/webgl-error-boundary.tsx",
      "content": "\"use client\";\n\nimport { cn } from \"@/lib/utils\";\nimport * as React from \"react\";\n\ninterface WebGLErrorBoundaryProps {\n  children: React.ReactNode;\n  fallback?: React.ReactNode;\n  onError?: (error: Error, errorInfo: React.ErrorInfo) => void;\n}\n\ninterface WebGLErrorBoundaryState {\n  hasError: boolean;\n}\n\nexport class WebGLErrorBoundary extends React.Component<\n  WebGLErrorBoundaryProps,\n  WebGLErrorBoundaryState\n> {\n  public state: WebGLErrorBoundaryState = { hasError: false };\n\n  static getDerivedStateFromError(): WebGLErrorBoundaryState {\n    return { hasError: true };\n  }\n\n  componentDidCatch(error: Error, errorInfo: React.ErrorInfo) {\n    this.props.onError?.(error, errorInfo);\n  }\n\n  render() {\n    if (this.state.hasError) {\n      return this.props.fallback ?? <WebGLFallback />;\n    }\n    return this.props.children;\n  }\n}\n\ninterface WebGLFallbackProps {\n  className?: string;\n  message?: string;\n}\n\nexport function WebGLFallback({\n  className,\n  message = \"Interactive WebGL content is unavailable on this device/browser.\",\n}: WebGLFallbackProps) {\n  return (\n    <div\n      className={cn(\n        \"flex h-full w-full items-center justify-center bg-gradient-to-br from-zinc-950 via-slate-900 to-zinc-900 px-4 text-center text-sm text-white/75\",\n        className,\n      )}\n      role=\"status\"\n      aria-live=\"polite\"\n    >\n      <p>{message}</p>\n    </div>\n  );\n}\n",
      "type": "registry:ui"
    }
  ]
}
