{
	"$schema": "https://ui.shadcn.com/schema/registry-item.json",
	"name": "AcidSquares-JS-CSS",
	"title": "AcidSquares",
	"description": "A crystalline corridor of stacked squares receding into depth.",
	"type": "registry:component",
	"files": [
		{
			"type": "registry:file",
			"path": "AcidSquares.css",
			"target": "@components/AcidSquares.css",
			"content": ".acid-squares-container {\n  position: relative;\n  width: 100%;\n  height: 100%;\n  overflow: hidden;\n}\n"
		},
		{
			"type": "registry:component",
			"path": "AcidSquares.jsx",
			"content": "import { useEffect, useRef } from 'react';\nimport { Renderer, Program, Mesh, Triangle, RenderTarget } from 'ogl';\nimport './AcidSquares.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 DETAIL_STEPS = { low: 20, medium: 32, high: 48 };\nconst stepsFor = detail => DETAIL_STEPS[detail] || DETAIL_STEPS.medium;\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 uWaveDepth;\nuniform float uZoom;\nuniform float uDensity;\nuniform float uSpread;\nuniform float uStepSize;\nuniform float uGlow;\nuniform float uExposure;\nuniform float uColorShift;\nuniform float uContrast;\nuniform float uBrightness;\nuniform float uOpacity;\nuniform float uSteps;\nuniform vec3 uColor1;\nuniform vec3 uColor2;\nuniform vec3 uColor3;\nuniform vec2 uMouse;\nuniform float uMouseStrength;\nuniform float uMouseRadius;\nuniform float uEnableMouse;\nuniform float uMouseActive;\nuniform float uGrain;\nuniform float uGrainIntensity;\nuniform float uLightMode;\nout vec4 fragColor;\n\nvoid main() {\n  vec2 frag = gl_FragCoord.xy;\n  float zoom = max(uZoom, 0.05);\n  float aspect = iResolution.x / iResolution.y;\n  vec2 ndc = (2.0 * frag - iResolution.xy) / iResolution.y;\n  vec2 dir = ndc * (0.5 / zoom);\n\n  vec2 mouseNdc = vec2(uMouse.x * aspect, uMouse.y);\n  float mr = max(uMouseRadius, 0.01);\n  vec2 md = ndc - mouseNdc;\n  float dent = exp(-dot(md, md) / (mr * mr)) * (3.0 * uMouseStrength * uEnableMouse * uMouseActive);\n\n  float travel = sin(iTime * uSpeed) * uWaveDepth;\n  float density = max(uDensity, 1.0);\n  float spread = clamp(uSpread, 0.05, 0.6);\n  float stepSize = max(uStepSize, 0.0005);\n  float glowGain = max(uGlow, 0.0);\n\n  vec3 tOffset = vec3(0.0, dent, travel);\n  vec3 p = vec3(0.0);\n  float s = 0.0;\n  float glow = 0.0;\n\n  for (int i = 0; i < 64; i++) {\n    if (float(i) >= uSteps) break;\n    p += vec3(dir * s, s);\n    vec3 q = p + tOffset;\n    s += density - length(q.xz) + length(ceil(q).xy);\n    s = stepSize + abs(s) * spread;\n    glow += glowGain / s;\n  }\n\n  float e = glow / max(uExposure, 1.0);\n  float shimmer = 0.5 + 0.5 * dot(cos(iTime * uColorShift + p), vec3(0.3333));\n  float v = tanh(e * uBrightness * mix(0.7, 1.05, shimmer));\n  v = clamp((v - 0.5) * uContrast + 0.5, 0.0, 1.0);\n\n  vec3 col = mix(uColor1, uColor2, smoothstep(0.0, 0.55, v));\n  col = mix(col, uColor3, smoothstep(0.55, 1.0, v));\n  col *= v;\n\n  float a = clamp(v, 0.0, 1.0) * uOpacity;\n  vec3 outRgb = col * a;\n  if (uGrain > 0.5) {\n    float gv = (fract(sin(dot(gl_FragCoord.xy, vec2(12.9898, 78.233)) + iTime) * 43758.5453) - 0.5) * uGrainIntensity;\n    outRgb = clamp(outRgb + gv, 0.0, 1.0);\n    a = clamp(a + gv, 0.0, 1.0);\n  }\n  if (uLightMode > 0.5) {\n    float peak = max(col.r, max(col.g, col.b));\n    vec3 chroma = pow(clamp(col / max(peak, 0.0001), 0.0, 1.0), vec3(1.16));\n    fragColor = vec4(mix(vec3(1.0), chroma, a * 0.94), 1.0);\n  } else {\n    fragColor = vec4(outRgb, a);\n  }\n}\n`;\n\nconst postFragment = `#version 300 es\nprecision highp float;\nuniform sampler2D tMap;\nuniform vec2 iResolution;\nuniform vec2 uDirection;\nuniform float uRadius;\nuniform float uGrain;\nuniform float uGrainIntensity;\nuniform float iTime;\nout vec4 fragColor;\n\nvec4 samp(vec2 uv) {\n  return texture(tMap, uv);\n}\n\nvoid main() {\n  vec2 uv = gl_FragCoord.xy / iResolution;\n  vec2 texel = uDirection / iResolution;\n  float st = uRadius * 0.25;\n  vec4 sum = samp(uv) * 0.2026;\n  sum += (samp(uv + texel * st) + samp(uv - texel * st)) * 0.179;\n  sum += (samp(uv + texel * (st * 2.0)) + samp(uv - texel * (st * 2.0))) * 0.124;\n  sum += (samp(uv + texel * (st * 3.0)) + samp(uv - texel * (st * 3.0))) * 0.0672;\n  sum += (samp(uv + texel * (st * 4.0)) + samp(uv - texel * (st * 4.0))) * 0.0285;\n  vec4 col = sum;\n  if (uGrain > 0.5) {\n    float gv = (fract(sin(dot(gl_FragCoord.xy, vec2(12.9898, 78.233)) + iTime) * 43758.5453) - 0.5) * uGrainIntensity;\n    col.rgb = clamp(col.rgb + gv, 0.0, 1.0);\n    col.a = clamp(col.a + gv, 0.0, 1.0);\n  }\n  fragColor = col;\n}\n`;\n\nconst ctxMap = new WeakMap();\n\nconst AcidSquares = ({\n  color1 = '#5227FF',\n  color2 = '#A855F7',\n  color3 = '#FFFFFF',\n  detail = 'medium',\n  speed = 0.7,\n  waveDepth = 1,\n  zoom = 1.3,\n  density = 10.0,\n  glow = 1.0,\n  exposure = 2700,\n  spread = 0.3,\n  stepSize = 0.002,\n  colorShift = 0,\n  contrast = 1,\n  brightness = 1.0,\n  opacity = 1.0,\n  mouseInteraction = true,\n  mouseStrength = 0.1,\n  mouseRadius = 0.35,\n  blur = 0,\n  grain = true,\n  grainIntensity = 0.05,\n  lightMode = false,\n  className = ''\n}) => {\n  const containerRef = useRef(null);\n  const mouseTarget = useRef([0, 0]);\n  const mouseCurrent = useRef([0, 0]);\n  const enableMouseRef = useRef(mouseInteraction);\n  const mouseStrengthRef = useRef(mouseStrength);\n  const mouseActive = useRef(0);\n  const mouseActiveTarget = useRef(0);\n  const blurRef = useRef(blur);\n  const grainRef = useRef(grain);\n  const grainIntensityRef = useRef(grainIntensity);\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.7 },\n        uWaveDepth: { value: 1 },\n        uZoom: { value: 1.3 },\n        uDensity: { value: 10.0 },\n        uSpread: { value: 0.3 },\n        uStepSize: { value: 0.002 },\n        uGlow: { value: 1.0 },\n        uExposure: { value: 2700 },\n        uColorShift: { value: 0 },\n        uContrast: { value: 1 },\n        uBrightness: { value: 1.0 },\n        uOpacity: { value: 1.0 },\n        uSteps: { value: 32 },\n        uColor1: { value: new Float32Array([1, 1, 1]) },\n        uColor2: { value: new Float32Array([1, 1, 1]) },\n        uColor3: { value: new Float32Array([1, 1, 1]) },\n        uMouse: { value: new Float32Array([0, 0]) },\n        uMouseStrength: { value: 0.1 },\n        uMouseRadius: { value: 0.35 },\n        uEnableMouse: { value: 1.0 },\n        uMouseActive: { value: 0.0 },\n        uGrain: { value: 1.0 },\n        uGrainIntensity: { value: 0.05 },\n        uLightMode: { value: 0.0 }\n      }\n    });\n\n    const mesh = new Mesh(gl, { geometry, program });\n\n    const postProgram = new Program(gl, {\n      vertex,\n      fragment: postFragment,\n      uniforms: {\n        tMap: { value: null },\n        iResolution: { value: new Float32Array([1, 1]) },\n        uDirection: { value: new Float32Array([1, 0]) },\n        uRadius: { value: 0 },\n        uGrain: { value: 0 },\n        uGrainIntensity: { value: 0.05 },\n        iTime: { value: 0 }\n      }\n    });\n    const postMesh = new Mesh(gl, { geometry, program: postProgram });\n\n    let rtA = null;\n    let rtB = null;\n    const ensureTargets = () => {\n      if (!rtA) {\n        const bw = gl.drawingBufferWidth;\n        const bh = gl.drawingBufferHeight;\n        rtA = new RenderTarget(gl, { width: bw, height: bh, depth: false });\n        rtB = new RenderTarget(gl, { width: bw, height: bh, depth: false });\n      }\n    };\n\n    const renderFrame = () => {\n      const grainOn = grainRef.current ? 1.0 : 0.0;\n      const grainAmt = grainIntensityRef.current;\n      program.uniforms.uGrainIntensity.value = grainAmt;\n      postProgram.uniforms.uGrainIntensity.value = grainAmt;\n      if (blurRef.current > 0) {\n        ensureTargets();\n        program.uniforms.uGrain.value = 0.0;\n        renderer.render({ scene: mesh, target: rtA });\n        const pu = postProgram.uniforms;\n        pu.uRadius.value = blurRef.current * 14.0;\n        pu.tMap.value = rtA.texture;\n        pu.uDirection.value[0] = 1;\n        pu.uDirection.value[1] = 0;\n        pu.uGrain.value = 0.0;\n        renderer.render({ scene: postMesh, target: rtB });\n        pu.tMap.value = rtB.texture;\n        pu.uDirection.value[0] = 0;\n        pu.uDirection.value[1] = 1;\n        pu.uGrain.value = grainOn;\n        renderer.render({ scene: postMesh });\n      } else {\n        program.uniforms.uGrain.value = grainOn;\n        renderer.render({ scene: mesh });\n      }\n    };\n\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 bw = gl.drawingBufferWidth;\n      const bh = gl.drawingBufferHeight;\n      const res = program.uniforms.iResolution.value;\n      res[0] = bw;\n      res[1] = bh;\n      const pres = postProgram.uniforms.iResolution.value;\n      pres[0] = bw;\n      pres[1] = bh;\n      if (rtA) {\n        rtA.setSize(bw, bh);\n        rtB.setSize(bw, bh);\n      }\n      renderFrame();\n    };\n\n    const ro = new ResizeObserver(setSize);\n    ro.observe(container);\n    setSize();\n\n    const handleMouseMove = e => {\n      const rect = container.getBoundingClientRect();\n      const x = ((e.clientX - rect.left) / rect.width - 0.5) * 2.0;\n      const y = -((e.clientY - rect.top) / rect.height - 0.5) * 2.0;\n      mouseTarget.current = [x, y];\n      mouseActiveTarget.current = 1;\n    };\n    const handleMouseLeave = () => {\n      mouseActiveTarget.current = 0;\n    };\n    container.addEventListener('mousemove', handleMouseMove);\n    container.addEventListener('mouseleave', handleMouseLeave);\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\n      const cur = mouseCurrent.current;\n      const tgt = mouseTarget.current;\n      cur[0] += 0.05 * (tgt[0] - cur[0]);\n      cur[1] += 0.05 * (tgt[1] - cur[1]);\n      const m = program.uniforms.uMouse.value;\n      m[0] = cur[0];\n      m[1] = cur[1];\n      const activeTarget = enableMouseRef.current ? mouseActiveTarget.current : 0;\n      mouseActive.current += 0.05 * (activeTarget - mouseActive.current);\n      program.uniforms.uMouseActive.value = mouseActive.current;\n      program.uniforms.uEnableMouse.value = enableMouseRef.current ? 1.0 : 0.0;\n      program.uniforms.uMouseStrength.value = mouseStrengthRef.current;\n\n      postProgram.uniforms.iTime.value = program.uniforms.iTime.value;\n      renderFrame();\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      container.removeEventListener('mousemove', handleMouseMove);\n      container.removeEventListener('mouseleave', handleMouseLeave);\n      ctxMap.delete(container);\n      if (rtA) {\n        gl.deleteFramebuffer(rtA.buffer);\n        gl.deleteFramebuffer(rtB.buffer);\n        rtA.textures.forEach(tex => gl.deleteTexture(tex.texture));\n        rtB.textures.forEach(tex => gl.deleteTexture(tex.texture));\n      }\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    u.uSpeed.value = speed;\n    u.uWaveDepth.value = waveDepth;\n    u.uZoom.value = zoom;\n    u.uDensity.value = density;\n    u.uSpread.value = spread;\n    u.uStepSize.value = stepSize;\n    u.uGlow.value = glow;\n    u.uExposure.value = exposure;\n    u.uColorShift.value = colorShift;\n    u.uContrast.value = contrast;\n    u.uBrightness.value = brightness;\n    u.uOpacity.value = opacity;\n    u.uLightMode.value = lightMode ? 1.0 : 0.0;\n    u.uSteps.value = stepsFor(detail);\n    u.uMouseRadius.value = mouseRadius;\n    const c1 = hexToRgb(color1);\n    const a1 = u.uColor1.value;\n    a1[0] = c1[0];\n    a1[1] = c1[1];\n    a1[2] = c1[2];\n    const c2 = hexToRgb(color2);\n    const a2 = u.uColor2.value;\n    a2[0] = c2[0];\n    a2[1] = c2[1];\n    a2[2] = c2[2];\n    const c3 = hexToRgb(color3);\n    const a3 = u.uColor3.value;\n    a3[0] = c3[0];\n    a3[1] = c3[1];\n    a3[2] = c3[2];\n\n    enableMouseRef.current = mouseInteraction;\n    mouseStrengthRef.current = mouseStrength;\n    blurRef.current = blur;\n    grainRef.current = grain;\n    grainIntensityRef.current = grainIntensity;\n  }, [\n    color1,\n    color2,\n    color3,\n    detail,\n    speed,\n    waveDepth,\n    zoom,\n    density,\n    glow,\n    exposure,\n    spread,\n    stepSize,\n    colorShift,\n    contrast,\n    brightness,\n    opacity,\n    mouseInteraction,\n    mouseStrength,\n    mouseRadius,\n    blur,\n    grain,\n    grainIntensity,\n    lightMode\n  ]);\n\n  return <div ref={containerRef} className={`acid-squares-container ${className}`.trim()} />;\n};\n\nexport default AcidSquares;\n"
		}
	],
	"registryDependencies": [],
	"dependencies": [
		"ogl@^1.0.11"
	]
}