/**
 * Aura fragment shader — faithful port of LiveKit's AgentAudioVisualizerAura.
 *
 * Uses 36 iterations of turbulence-warped SDF evaluation to produce a
 * luminous ring through accumulation. The ring shape emerges naturally
 * from the turbulence + circle SDF interaction — not from drawing a
 * ring directly.
 *
 * Uniforms (all from Aura component):
 *   u_color       vec3  Base RGB colour (0-1)
 *   u_colorShift  float HSV hue shift range across iterations (0-1)
 *   u_speed       float Animation speed multiplier
 *   u_amplitude   float Turbulence displacement strength
 *   u_frequency   float Wave complexity (remapped to 2.0-15.0)
 *   u_scale       float Circle SDF radius
 *   u_blur        float Edge softness
 *   u_brightness  float Overall brightness/opacity
 *   u_bloom       float Glow intensity (dark mode only)
 *   u_spacing     float Iteration spacing (remapped to 1.0-TAU)
 *   u_variance    float Amplitude variance per turbulence layer
 *   u_smoothing   float Dynamic blur smoothing factor
 *   u_lightMode   float 0.0 = dark mode, 1.0 = light mode
 *
 * Built-in from ShaderCanvas:
 *   iTime          float Elapsed seconds
 *   iResolution    vec2  Viewport pixel dimensions
 */
export declare const auraFragmentShader = "#version 300 es\nprecision highp float;\n\nuniform float iTime;\nuniform vec2  iResolution;\n\nuniform vec3  u_color;\nuniform float u_colorShift;\nuniform float u_speed;\nuniform float u_amplitude;\nuniform float u_frequency;\nuniform float u_scale;\nuniform float u_blur;\nuniform float u_brightness;\nuniform float u_bloom;\nuniform float u_spacing;\nuniform float u_variance;\nuniform float u_smoothing;\nuniform float u_lightMode;\n\nout vec4 fragColor;\n\n/* \u2500\u2500 Constants \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n\nconst float TAU = 6.283185;\n\n/* \u2500\u2500 Utilities \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n\nvec2 randFibo(vec2 p) {\n  p = fract(p * vec2(443.897, 441.423));\n  p += dot(p, p.yx + 19.19);\n  return fract((p.xx + p.yx) * p.xy);\n}\n\nvec3 Tonemap(vec3 x) {\n  x *= 4.0;\n  return x / (1.0 + x);\n}\n\nfloat luma(vec3 color) {\n  return dot(color, vec3(0.299, 0.587, 0.114));\n}\n\nvec3 rgb2hsv(vec3 c) {\n  vec4 K = vec4(0.0, -1.0 / 3.0, 2.0 / 3.0, -1.0);\n  vec4 p = mix(vec4(c.bg, K.wz), vec4(c.gb, K.xy), step(c.b, c.g));\n  vec4 q = mix(vec4(p.xyw, c.r), vec4(c.r, p.yzx), step(p.x, c.r));\n  float d = q.x - min(q.w, q.y);\n  float e = 1.0e-10;\n  return vec3(abs(q.z + (q.w - q.y) / (6.0 * d + e)), d / (q.x + e), q.x);\n}\n\nvec3 hsv2rgb(vec3 c) {\n  vec4 K = vec4(1.0, 2.0 / 3.0, 1.0 / 3.0, 3.0);\n  vec3 p = abs(fract(c.xxx + K.xyz) * 6.0 - K.www);\n  return c.z * mix(K.xxx, clamp(p - K.xxx, 0.0, 1.0), c.y);\n}\n\n/* \u2500\u2500 SDF \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n\nfloat sdCircle(vec2 st, float r) {\n  return length(st) - r;\n}\n\n/* \u2500\u2500 Turbulence \u2014 4 layers of sinusoidal displacement with rotation \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n\nvec2 turb(vec2 pos, float t, float it) {\n  mat2 rotation = mat2(0.6, -0.25, 0.25, 0.9);\n  mat2 layerRotation = mat2(0.6, -0.8, 0.8, 0.6);\n\n  float frequency = mix(2.0, 15.0, u_frequency);\n  float amplitude = u_amplitude;\n  float frequencyGrowth = 1.4;\n  float animTime = t * 0.1 * u_speed;\n\n  const int LAYERS = 4;\n  for (int i = 0; i < LAYERS; i++) {\n    vec2 rotatedPos = pos * rotation;\n    vec2 wave = sin(frequency * rotatedPos + float(i) * animTime + it);\n\n    pos += (amplitude / frequency) * rotation[0] * wave;\n\n    rotation *= layerRotation;\n    amplitude *= mix(1.0, max(wave.x, wave.y), u_variance);\n    frequency *= frequencyGrowth;\n  }\n\n  return pos;\n}\n\n/* \u2500\u2500 Main \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n\nconst float ITERATIONS = 36.0;\n\nvoid main() {\n  vec2 uv = gl_FragCoord.xy / iResolution.xy;\n\n  vec3 pp = vec3(0.0);\n  vec3 bloom = vec3(0.0);\n  float t = iTime * 0.5;\n  vec2 pos = uv - 0.5;\n\n  vec2 prevPos = turb(pos, t, 0.0 - 1.0 / ITERATIONS);\n  float spacing = mix(1.0, TAU, u_spacing);\n\n  for (float i = 1.0; i < ITERATIONS + 1.0; i++) {\n    float iter = i / ITERATIONS;\n    vec2 st = turb(pos, t, iter * spacing);\n    float d = abs(sdCircle(st, u_scale));\n    float pd = distance(st, prevPos);\n    prevPos = st;\n    float dynamicBlur = exp2(pd * 2.0 * 1.4426950408889634) - 1.0;\n    float ds = smoothstep(0.0, u_blur * 0.05 + max(dynamicBlur * u_smoothing, 0.001), d);\n\n    vec3 col = u_color;\n    if (u_colorShift > 0.01) {\n      vec3 hsv = rgb2hsv(col);\n      hsv.x = fract(hsv.x + (1.0 - iter) * u_colorShift * 0.3);\n      col = hsv2rgb(hsv);\n    }\n\n    float invd = 1.0 / max(d + dynamicBlur, 0.001);\n    pp += (ds - 1.0) * col;\n    bloom += clamp(invd, 0.0, 250.0) * col;\n  }\n\n  pp *= 1.0 / ITERATIONS;\n\n  vec3 color;\n\n  // Dark mode (default)\n  if (u_lightMode < 0.5) {\n    bloom = bloom / (bloom + 2e4);\n    color = (-pp + bloom * 3.0 * u_bloom) * 1.2;\n    color += (randFibo(gl_FragCoord.xy).x - 0.5) / 255.0;\n    color = Tonemap(color);\n    float alpha = luma(color) * u_brightness;\n    fragColor = vec4(color * u_brightness, alpha);\n  }\n  // Light mode\n  else {\n    color = -pp;\n    color += (randFibo(gl_FragCoord.xy).x - 0.5) / 255.0;\n\n    float brightness = length(color);\n    vec3 direction = brightness > 0.0 ? color / brightness : color;\n\n    float factor = 2.0;\n    float mappedBrightness = (brightness * factor) / (1.0 + brightness * factor);\n    color = direction * mappedBrightness;\n\n    float gray = dot(color, vec3(0.2, 0.5, 0.1));\n    float saturationBoost = 3.0;\n    color = mix(vec3(gray), color, saturationBoost);\n\n    color = clamp(color, 0.0, 1.0);\n\n    float alpha = mappedBrightness * clamp(u_brightness, 1.0, 2.0);\n    fragColor = vec4(color, alpha);\n  }\n}\n";
