{
	"$schema": "https://ui.shadcn.com/schema/registry-item.json",
	"name": "GradientWaves-JS-CSS",
	"title": "GradientWaves",
	"description": "Raymarched sine waves rolling toward a soft, hazy horizon.",
	"type": "registry:component",
	"files": [
		{
			"type": "registry:file",
			"path": "GradientWaves.css",
			"target": "@components/GradientWaves.css",
			"content": ".gradient-waves-container {\n  position: relative;\n  width: 100%;\n  height: 100%;\n  overflow: hidden;\n}\n"
		},
		{
			"type": "registry:component",
			"path": "GradientWaves.jsx",
			"content": "import { useEffect, useRef } from 'react';\nimport { Renderer, Program, Mesh, Triangle } from 'ogl';\nimport './GradientWaves.css';\n\nconst hexToRgb = hex => {\n  const result = /^#?([a-f\\d]{2})([a-f\\d]{2})([a-f\\d]{2})$/i.exec(hex);\n  if (!result) return [1, 1, 1];\n  return [parseInt(result[1], 16) / 255, parseInt(result[2], 16) / 255, parseInt(result[3], 16) / 255];\n};\n\nconst detailToSteps = detail => {\n  if (detail === 'low') return 40.0;\n  if (detail === 'high') return 110.0;\n  return 70.0;\n};\n\nconst vertex = `#version 300 es\nin vec2 position;\nvoid main() {\n  gl_Position = vec4(position, 0.0, 1.0);\n}\n`;\n\nconst fragment = `#version 300 es\nprecision highp float;\nuniform vec2 iResolution;\nuniform float iTime;\nuniform float uSpeed;\nuniform float uAmplitude;\nuniform float uWaveScale;\nuniform float uWaveRatio;\nuniform float uSwell;\nuniform float uTurbulence;\nuniform float uTilt;\nuniform float uZoom;\nuniform float uHeight;\nuniform float uFogDepth;\nuniform float uSteps;\nuniform float uBrightness;\nuniform float uOpacity;\nuniform float uGrain;\nuniform float uGrainIntensity;\nuniform vec2 uMouse;\nuniform float uParallax;\nuniform bool uEnableMouse;\nuniform vec3 uHorizonColor;\nuniform vec3 uWaveColor;\nuniform vec3 uCrestColor;\nout vec4 fragColor;\n\nconst float MAX_DIST = 20000.0;\n\nfloat hash21(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}\n\nfloat plasma(vec3 r, vec2 freq, vec4 tc) {\n  float mx = r.x + tc.x;\n  mx += uSwell * sin((r.y + mx) / 20.0 + tc.y);\n  float my = r.y - tc.z;\n  my += uTurbulence * cos(r.x / 23.0 + tc.w);\n  return r.z - (sin(mx * freq.x) * uAmplitude + sin(my * freq.y) * uAmplitude + uHeight);\n}\n\nfloat raymarch(vec3 pos, vec3 dir, vec2 freq, vec4 tc) {\n  float dist = 0.0;\n  for (int i = 0; i < 128; i++) {\n    if (float(i) >= uSteps) break;\n    float dscene = plasma(pos + dist * dir, freq, tc);\n    if (abs(dscene) < 0.1) break;\n    dist += 0.9 * dscene;\n    if (!(abs(dist) < MAX_DIST)) return MAX_DIST;\n  }\n  return dist;\n}\n\nvoid main() {\n  float T = iTime * uSpeed;\n  vec2 freq = vec2(uWaveScale / 7.0, (uWaveScale * uWaveRatio) / 3.0);\n  vec4 tc = vec4(T / 0.130, T / 0.810, T / 0.200, T / 0.710);\n  float c, s;\n  float vfov = (3.14159 / 2.3) / max(uZoom, 0.05);\n  vec3 cam = vec3(0.0, 0.0, 30.0);\n  vec2 uv = (gl_FragCoord.xy / iResolution.xy) - 0.5;\n  uv.x *= iResolution.x / iResolution.y;\n  uv.y *= -1.0;\n\n  vec3 dir = vec3(0.0, 0.0, -1.0);\n  float ulen = length(uv);\n  float xrot = vfov * ulen;\n  c = cos(xrot); s = sin(xrot);\n  dir = mat3(1.0, 0.0, 0.0, 0.0, c, -s, 0.0, s, c) * dir;\n  vec2 nuv = ulen > 1e-5 ? uv / ulen : vec2(1.0, 0.0);\n  c = nuv.x; s = nuv.y;\n  dir = mat3(c, -s, 0.0, s, c, 0.0, 0.0, 0.0, 1.0) * dir;\n  c = cos(uTilt); s = sin(uTilt);\n  dir = mat3(c, 0.0, s, 0.0, 1.0, 0.0, -s, 0.0, c) * dir;\n\n  if (uEnableMouse) {\n    float yaw = (uMouse.x - 0.5) * uParallax * 0.4;\n    float pitch = (uMouse.y - 0.5) * uParallax * 0.4;\n    c = cos(yaw); s = sin(yaw);\n    dir = mat3(c, 0.0, s, 0.0, 1.0, 0.0, -s, 0.0, c) * dir;\n    c = cos(pitch); s = sin(pitch);\n    dir = mat3(1.0, 0.0, 0.0, 0.0, c, -s, 0.0, s, c) * dir;\n  }\n\n  float dist = raymarch(cam, dir, freq, tc);\n  vec3 pos = cam + dist * dir;\n\n  float t = clamp(uFogDepth / max(dist, 0.001), 0.0, 1.0);\n  vec3 body = mix(uWaveColor, uCrestColor, clamp(pos.z * 0.08 + 0.5, 0.0, 1.0));\n  vec3 col = mix(uHorizonColor, body, t);\n  col *= uBrightness;\n  col = clamp(col, 0.0, 1.0);\n\n  float alpha = clamp(t, 0.0, 1.0) * uOpacity;\n  if (uGrain > 0.5) {\n    float g = hash21(gl_FragCoord.xy + mod(iTime, 64.0) * 11.0);\n    alpha += (g - 0.5) * uGrainIntensity;\n  }\n  alpha = clamp(alpha, 0.0, 1.0);\n  fragColor = vec4(col * alpha, alpha);\n}\n`;\n\nconst ctxMap = new WeakMap();\n\nconst GradientWaves = ({\n  horizonColor = '#5227FF',\n  waveColor = '#FF9FFC',\n  crestColor = '#FFFFFF',\n  speed = 0.4,\n  amplitude = 2.5,\n  waveScale = 0.6,\n  waveRatio = 0.9,\n  swell = 35,\n  turbulence = 20,\n  tilt = 1.11,\n  zoom = 1.0,\n  height = 5.5,\n  fogDepth = 15,\n  detail = 'medium',\n  brightness = 1.0,\n  opacity = 1.0,\n  mouseInteraction = true,\n  parallaxStrength = 0.5,\n  grain = true,\n  grainIntensity = 0.05,\n  className = ''\n}) => {\n  const containerRef = useRef(null);\n  const enableMouseRef = useRef(mouseInteraction);\n\n  useEffect(() => {\n    const container = containerRef.current;\n    if (!container) return;\n\n    const renderer = new Renderer({\n      webgl: 2,\n      alpha: true,\n      premultipliedAlpha: true,\n      antialias: false,\n      dpr: Math.min(window.devicePixelRatio || 1, 2)\n    });\n\n    const gl = renderer.gl;\n    gl.clearColor(0, 0, 0, 0);\n    const canvas = gl.canvas;\n    canvas.style.width = '100%';\n    canvas.style.height = '100%';\n    canvas.style.display = 'block';\n    container.appendChild(canvas);\n\n    const geometry = new Triangle(gl);\n    const program = new Program(gl, {\n      vertex,\n      fragment,\n      uniforms: {\n        iTime: { value: 0 },\n        iResolution: { value: new Float32Array([1, 1]) },\n        uSpeed: { value: 0.4 },\n        uAmplitude: { value: 2.5 },\n        uWaveScale: { value: 0.6 },\n        uWaveRatio: { value: 0.9 },\n        uSwell: { value: 35 },\n        uTurbulence: { value: 20 },\n        uTilt: { value: 1.11 },\n        uZoom: { value: 1.0 },\n        uHeight: { value: 5.5 },\n        uFogDepth: { value: 15 },\n        uSteps: { value: 70.0 },\n        uBrightness: { value: 1.0 },\n        uOpacity: { value: 1.0 },\n        uGrain: { value: 1.0 },\n        uGrainIntensity: { value: 0.05 },\n        uMouse: { value: new Float32Array([0.5, 0.5]) },\n        uParallax: { value: 0.5 },\n        uEnableMouse: { value: true },\n        uHorizonColor: { value: new Float32Array([1, 1, 1]) },\n        uWaveColor: { value: new Float32Array([1, 1, 1]) },\n        uCrestColor: { value: new Float32Array([1, 1, 1]) }\n      }\n    });\n\n    const mesh = new Mesh(gl, { geometry, program });\n    ctxMap.set(container, { renderer, program, mesh });\n\n    const setSize = () => {\n      const rect = container.getBoundingClientRect();\n      const w = Math.max(1, Math.floor(rect.width));\n      const h = Math.max(1, Math.floor(rect.height));\n      renderer.setSize(w, h);\n      const res = program.uniforms.iResolution.value;\n      res[0] = gl.drawingBufferWidth;\n      res[1] = gl.drawingBufferHeight;\n      renderer.render({ scene: mesh });\n    };\n\n    const ro = new ResizeObserver(setSize);\n    ro.observe(container);\n    setSize();\n\n    const currentMouse = [0.5, 0.5];\n    const targetMouse = [0.5, 0.5];\n\n    const onPointerMove = e => {\n      const rect = canvas.getBoundingClientRect();\n      targetMouse[0] = (e.clientX - rect.left) / rect.width;\n      targetMouse[1] = 1.0 - (e.clientY - rect.top) / rect.height;\n    };\n    const onPointerLeave = () => {\n      targetMouse[0] = 0.5;\n      targetMouse[1] = 0.5;\n    };\n    canvas.addEventListener('pointermove', onPointerMove);\n    canvas.addEventListener('pointerleave', onPointerLeave);\n\n    let raf = 0;\n    let isVisible = true;\n    let isPageVisible = !document.hidden;\n    const t0 = performance.now();\n\n    const loop = t => {\n      program.uniforms.iTime.value = (t - t0) * 0.001;\n      const tx = enableMouseRef.current ? targetMouse[0] : 0.5;\n      const ty = enableMouseRef.current ? targetMouse[1] : 0.5;\n      currentMouse[0] += 0.05 * (tx - currentMouse[0]);\n      currentMouse[1] += 0.05 * (ty - currentMouse[1]);\n      program.uniforms.uMouse.value[0] = currentMouse[0];\n      program.uniforms.uMouse.value[1] = currentMouse[1];\n      renderer.render({ scene: mesh });\n      raf = requestAnimationFrame(loop);\n    };\n\n    const tryStart = () => {\n      if (isVisible && isPageVisible && raf === 0) raf = requestAnimationFrame(loop);\n    };\n    const tryStop = () => {\n      if (raf !== 0) {\n        cancelAnimationFrame(raf);\n        raf = 0;\n      }\n    };\n\n    const io = new IntersectionObserver(\n      ([entry]) => {\n        isVisible = entry.isIntersecting;\n        isVisible ? tryStart() : tryStop();\n      },\n      { threshold: 0 }\n    );\n    io.observe(container);\n\n    const onVisibility = () => {\n      isPageVisible = !document.hidden;\n      isPageVisible ? tryStart() : tryStop();\n    };\n    document.addEventListener('visibilitychange', onVisibility);\n\n    tryStart();\n\n    return () => {\n      tryStop();\n      ro.disconnect();\n      io.disconnect();\n      document.removeEventListener('visibilitychange', onVisibility);\n      canvas.removeEventListener('pointermove', onPointerMove);\n      canvas.removeEventListener('pointerleave', onPointerLeave);\n      ctxMap.delete(container);\n      try {\n        container.removeChild(canvas);\n      } catch {}\n      gl.getExtension('WEBGL_lose_context')?.loseContext();\n    };\n  }, []);\n\n  useEffect(() => {\n    const container = containerRef.current;\n    if (!container) return;\n    const ctx = ctxMap.get(container);\n    if (!ctx) return;\n    const { program } = ctx;\n    const u = program.uniforms;\n\n    enableMouseRef.current = mouseInteraction;\n\n    u.uSpeed.value = speed;\n    u.uAmplitude.value = amplitude;\n    u.uWaveScale.value = waveScale;\n    u.uWaveRatio.value = waveRatio;\n    u.uSwell.value = swell;\n    u.uTurbulence.value = turbulence;\n    u.uTilt.value = tilt;\n    u.uZoom.value = zoom;\n    u.uHeight.value = height;\n    u.uFogDepth.value = fogDepth;\n    u.uSteps.value = detailToSteps(detail);\n    u.uBrightness.value = brightness;\n    u.uOpacity.value = opacity;\n    u.uGrain.value = grain ? 1.0 : 0.0;\n    u.uGrainIntensity.value = grainIntensity;\n    u.uParallax.value = parallaxStrength;\n    u.uEnableMouse.value = mouseInteraction;\n    const hc = u.uHorizonColor.value;\n    const wc = u.uWaveColor.value;\n    const cc = u.uCrestColor.value;\n    const h = hexToRgb(horizonColor);\n    const w = hexToRgb(waveColor);\n    const cr = hexToRgb(crestColor);\n    hc[0] = h[0];\n    hc[1] = h[1];\n    hc[2] = h[2];\n    wc[0] = w[0];\n    wc[1] = w[1];\n    wc[2] = w[2];\n    cc[0] = cr[0];\n    cc[1] = cr[1];\n    cc[2] = cr[2];\n  }, [\n    horizonColor,\n    waveColor,\n    crestColor,\n    speed,\n    amplitude,\n    waveScale,\n    waveRatio,\n    swell,\n    turbulence,\n    tilt,\n    zoom,\n    height,\n    fogDepth,\n    detail,\n    brightness,\n    opacity,\n    grain,\n    grainIntensity,\n    mouseInteraction,\n    parallaxStrength\n  ]);\n\n  return <div ref={containerRef} className={`gradient-waves-container ${className}`.trim()} />;\n};\n\nexport default GradientWaves;\n"
		}
	],
	"registryDependencies": [],
	"dependencies": [
		"ogl@^1.0.11"
	]
}