{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "magnetron-racetrack-sweep",
  "title": "Magnetron Racetrack Sweep",
  "description": "A full-bleed background built on sputter target erosion: a magnetron's plasma racetrack sweeps back and forth across a target face, slowly grooving it wherever the ring has lingered, rendered as a persistent radial erosion field under a live, non-accumulated glow ring.",
  "dependencies": [],
  "files": [
    {
      "path": "registry/loud/magnetron-racetrack-sweep/component.tsx",
      "content": "\"use client\";\n\nimport { useEffect, useId, useRef } from \"react\";\n\n// ---------------------------------------------------------------------------\n// MagnetronRacetrackSweep — a full-bleed background eroded by sputter target\n// wear: a magnetron's magnetic field confines plasma into an annular\n// \"racetrack\" band on a planar sputter target's face. Production cathodes\n// sweep that racetrack's radius back and forth (rotating/oscillating magnet\n// assembly) so erosion doesn't dig one fixed groove and waste the rest of the\n// target.\n//\n// EROSION IS RADIALLY SYMMETRIC, SO THE ACCUMULATION BUFFER IS 1D. The\n// racetrack is a full annulus at every instant, never a spot — the whole\n// erosion history is therefore a function of radius alone, never angle. That\n// lets the \"persistent, never-cleared accumulation buffer\" (same non-clearing\n// technique as rime-creep and edm-crater-field, monotonic here rather than\n// decaying — real erosion never heals) live in a 256-texel 1D LUMINANCE\n// texture instead of a full 2D grid: cheap to stamp, cheap to sample, and it\n// is the mechanic's own physical symmetry that licenses the simplification,\n// not a performance shortcut taken against the spec.\n//\n// The composite pass derives a surface normal from the 1D depth field's\n// RADIAL gradient only (there is no tangential slope, by construction) and\n// projects it back into 2D along the local radial direction — the target\n// reads as a real machined groove, brighter/darker as it catches the\n// achromatic studio's light bands, not a flat painted ring.\n//\n// The live glow ring is a SEPARATE, non-accumulated quantity: it is redrawn\n// every frame from the sweep's current phase and added to luminance only\n// (never colour — real sputtering plasma glows violet/pink, discarded on\n// purpose per the monochrome constraint). A pointer brightens the ring\n// locally by angle, never by hue, and never touches the sweep clock itself.\n// ---------------------------------------------------------------------------\n\nexport interface MagnetronRacetrackSweepProps {\n  /** Freezes the surface on a composed steady-state frame without unmounting. */\n  paused?: boolean;\n  /** Rendered in the DOM over the field — eyebrow, headline, CTA. */\n  children?: React.ReactNode;\n  className?: string;\n  style?: React.CSSProperties;\n}\n\nconst VERT_SRC = `\nattribute vec2 a_pos;\nvoid main() { gl_Position = vec4(a_pos, 0.0, 1.0); }\n`;\n\nconst FRAG_SRC = `\nprecision highp float;\n\nuniform vec2 u_size;       // css px\nuniform float u_dpr;\nuniform vec2 u_center;     // css px, target centre\nuniform float u_targetR;   // css px, target radius\nuniform sampler2D u_erosion; // 1D depth field, N x 1\nuniform float u_erosionN;\nuniform float u_ringR;         // current live sweep radius, normalized 0..1\nuniform float u_ringHalfWidth; // normalized\nuniform float u_pointerAngle;\nuniform float u_pointerActive;\nuniform float u_time;\nuniform vec3 u_c0;\nuniform vec3 u_c1;\nuniform vec3 u_c2;\nuniform vec3 u_c3;\nuniform vec3 u_c4;\nuniform vec3 u_bg;\nuniform float u_bias;\nuniform float u_contrast;\n\nfloat sampleDepth(float r) {\n  return texture2D(u_erosion, vec2(clamp(r, 0.0, 1.0), 0.5)).r;\n}\n\nfloat strip(float el, float at, float width) {\n  float d = (el - at) / width;\n  return exp(-d * d);\n}\n\n// simplified achromatic studio — a machined-metal read needs fewer bands\n// than a mirror pool, same family as edm-crater-field's env().\nfloat env(vec3 r) {\n  float el = r.y;\n  float az = atan(r.x, r.z);\n  float L = 0.42;\n  L += 0.20 * smoothstep(0.05, 0.62, el);\n  L -= 0.18 * smoothstep(0.0, -0.55, el);\n  L += 0.28 * strip(el, 0.14, 0.06);\n  L += 0.14 * strip(el, 0.46, 0.09);\n  L += 0.08 * exp(-pow(sin(az * 1.0 + u_time * 0.03) / 0.42, 2.0));\n  return L;\n}\n\nvec3 ramp(float x) {\n  vec3 c = mix(u_c0, u_c1, smoothstep(0.0, 0.30, x));\n  c = mix(c, u_c2, smoothstep(0.28, 0.58, x));\n  c = mix(c, u_c3, smoothstep(0.55, 0.85, x));\n  c = mix(c, u_c4, smoothstep(0.82, 1.0, x));\n  return c;\n}\n\nvoid main() {\n  vec2 p = vec2(gl_FragCoord.x, u_size.y * u_dpr - gl_FragCoord.y) / u_dpr - u_center;\n  float dist = length(p);\n  float r = dist / u_targetR;\n\n  float edge = smoothstep(1.02, 0.985, r);\n  if (edge <= 0.0) {\n    gl_FragColor = vec4(u_bg, 1.0);\n    return;\n  }\n\n  vec2 rDir = dist > 0.0005 ? p / dist : vec2(0.0, 1.0);\n  float texel = 1.0 / u_erosionN;\n  float d0 = sampleDepth(r);\n  float dPlus = sampleDepth(r + texel);\n  float dMinus = sampleDepth(r - texel);\n  float slope = (dPlus - dMinus) / (2.0 * texel);\n  float relief = 2.2;\n  vec3 n = normalize(vec3(-slope * relief * rDir, 1.0));\n  vec2 vp = p / u_targetR;\n  vec3 v = normalize(vec3(vp.x * 0.5, -vp.y * 0.5, 1.0));\n  vec3 refl = reflect(-v, n);\n\n  float L = env(refl);\n  vec3 l1 = normalize(vec3(-0.28, 0.7, 0.6));\n  float s1 = pow(max(dot(refl, l1), 0.0), 90.0);\n  L += s1 * 0.6;\n\n  // material removed reads as LOWER relative luminance than the fresh face,\n  // in both themes — direction never flips, only bias/contrast do.\n  float depthVis = clamp(d0 / 1.4, 0.0, 1.0);\n  L -= depthVis * 0.34;\n\n  // live glow ring — additive luminance only, never accumulated, never hue.\n  float ringMask = 1.0 - smoothstep(u_ringHalfWidth * 0.7, u_ringHalfWidth * 1.15, abs(r - u_ringR));\n  L += ringMask * 0.5;\n\n  if (u_pointerActive > 0.5) {\n    float ang = atan(p.x, -p.y);\n    float diff = ang - u_pointerAngle;\n    diff = mod(diff + 3.14159265, 6.2831853) - 3.14159265;\n    float angularBoost = exp(-pow(diff / 0.35, 2.0));\n    L += ringMask * angularBoost * 0.35;\n  }\n\n  float fres = pow(1.0 - max(dot(n, v), 0.0), 5.0);\n  L += fres * 0.08;\n\n  float Lc = clamp((L - 0.5) * u_contrast + 0.5 + u_bias, 0.0, 1.0);\n  vec3 col = mix(u_bg, ramp(Lc), edge);\n  gl_FragColor = vec4(col, 1.0);\n}\n`;\n\ntype RGB = [number, number, number];\n\nfunction parseHex(raw: string): RGB | null {\n  const m = /^#([0-9a-f]{3}|[0-9a-f]{6})$/i.exec(raw.trim());\n  if (!m) return null;\n  let h = m[1];\n  if (h.length === 3) h = h.split(\"\").map((c) => c + c).join(\"\");\n  const n = parseInt(h, 16);\n  return [((n >> 16) & 255) / 255, ((n >> 8) & 255) / 255, (n & 255) / 255];\n}\n\nfunction mixRGB(a: RGB, b: RGB, t: number): RGB {\n  return [a[0] + (b[0] - a[0]) * t, a[1] + (b[1] - a[1]) * t, a[2] + (b[2] - a[2]) * t];\n}\n\nfunction luminance([r, g, b]: RGB): number {\n  return 0.2126 * r + 0.7152 * g + 0.0722 * b;\n}\n\nfunction compile(gl: WebGLRenderingContext, type: number, src: string): WebGLShader {\n  const s = gl.createShader(type)!;\n  gl.shaderSource(s, src);\n  gl.compileShader(s);\n  if (!gl.getShaderParameter(s, gl.COMPILE_STATUS)) {\n    const info = gl.getShaderInfoLog(s);\n    gl.deleteShader(s);\n    throw new Error(`magnetron-racetrack-sweep: shader compile failed: ${info ?? \"\"}`);\n  }\n  return s;\n}\n\n// Minimal full-bleed fragment-shader host, same shape as weld-pool /\n// edm-crater-field's GLSurface: one program, one fullscreen triangle pair,\n// lazily-resolved uniform locations.\nclass GLSurface {\n  gl: WebGLRenderingContext | null = null;\n  private program: WebGLProgram | null = null;\n  private vs: WebGLShader | null = null;\n  private fs: WebGLShader | null = null;\n  private buffer: WebGLBuffer | null = null;\n  private locs = new Map<string, WebGLUniformLocation | null>();\n\n  constructor(private canvas: HTMLCanvasElement, private frag: string) {}\n\n  init(): boolean {\n    const gl = this.canvas.getContext(\"webgl\", {\n      alpha: false,\n      antialias: false,\n      premultipliedAlpha: false,\n      powerPreference: \"high-performance\",\n    }) as WebGLRenderingContext | null;\n    if (!gl) return false;\n    this.gl = gl;\n    try {\n      this.vs = compile(gl, gl.VERTEX_SHADER, VERT_SRC);\n      this.fs = compile(gl, gl.FRAGMENT_SHADER, this.frag);\n      const program = gl.createProgram();\n      if (!program) {\n        this.destroy();\n        return false;\n      }\n      this.program = program;\n      gl.attachShader(program, this.vs);\n      gl.attachShader(program, this.fs);\n      gl.linkProgram(program);\n      if (!gl.getProgramParameter(program, gl.LINK_STATUS)) {\n        this.destroy();\n        return false;\n      }\n    } catch {\n      this.destroy();\n      return false;\n    }\n    gl.useProgram(this.program);\n    this.buffer = gl.createBuffer();\n    gl.bindBuffer(gl.ARRAY_BUFFER, this.buffer);\n    gl.bufferData(\n      gl.ARRAY_BUFFER,\n      new Float32Array([-1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, 1]),\n      gl.STATIC_DRAW\n    );\n    const loc = gl.getAttribLocation(this.program!, \"a_pos\");\n    gl.enableVertexAttribArray(loc);\n    gl.vertexAttribPointer(loc, 2, gl.FLOAT, false, 0, 0);\n    this.locs.clear();\n    return true;\n  }\n\n  private loc(name: string): WebGLUniformLocation | null {\n    if (!this.locs.has(name)) {\n      this.locs.set(name, this.gl!.getUniformLocation(this.program!, name));\n    }\n    return this.locs.get(name) ?? null;\n  }\n\n  f(name: string, x: number) {\n    this.gl?.uniform1f(this.loc(name), x);\n  }\n  i(name: string, x: number) {\n    this.gl?.uniform1i(this.loc(name), x);\n  }\n  v2(name: string, x: number, y: number) {\n    this.gl?.uniform2f(this.loc(name), x, y);\n  }\n  v3(name: string, c: RGB) {\n    this.gl?.uniform3f(this.loc(name), c[0], c[1], c[2]);\n  }\n\n  draw(pixelW: number, pixelH: number) {\n    const gl = this.gl;\n    if (!gl || !this.program) return;\n    gl.viewport(0, 0, pixelW, pixelH);\n    gl.drawArrays(gl.TRIANGLES, 0, 6);\n  }\n\n  destroy() {\n    const gl = this.gl;\n    if (!gl) return;\n    if (this.buffer) gl.deleteBuffer(this.buffer);\n    if (this.program) gl.deleteProgram(this.program);\n    if (this.vs) gl.deleteShader(this.vs);\n    if (this.fs) gl.deleteShader(this.fs);\n    this.buffer = null;\n    this.program = null;\n    this.vs = null;\n    this.fs = null;\n    this.locs.clear();\n    this.gl = null;\n  }\n}\n\n// ---------------------------------------------------------------------------\n// Real numbers (docs/specs/r10/magnetron-racetrack-sweep.md):\n//  - racetrack radius oscillates between 0.3 and 0.7 of target radius\n//  - oscillation period: 22s\n//  - racetrack ring width: 0.08 * targetRadius\n//  - erosion accumulation rate: 0.002 depth-units/s wherever the ring\n//    currently sits — monotonic, never decays\n// ---------------------------------------------------------------------------\nconst RING_MID = 0.5; // (0.3 + 0.7) / 2\nconst RING_AMPLITUDE = 0.2; // (0.7 - 0.3) / 2\nconst SWEEP_PERIOD_S = 22;\nconst RING_WIDTH = 0.08;\nconst EROSION_RATE = 0.002; // depth-units/s within the band\nconst EROSION_BUCKETS = 256;\nconst WARMUP_STEP = 0.25;\nconst LIVE_WARMUP_S = 40; // \"existing erosion groove faint\" at t0\nconst STATIC_WARMUP_S = 480; // deep enough for the dual-band asymmetry to read at a glance\nconst STATIC_PHASE_S = SWEEP_PERIOD_S * 0.5; // sweep phase 0.5 -> \"racetrack-mid\"\n\nfunction ringRAt(tSeconds: number): number {\n  return RING_MID + RING_AMPLITUDE * Math.sin((2 * Math.PI * tSeconds) / SWEEP_PERIOD_S);\n}\n\nexport function MagnetronRacetrackSweep({\n  paused = false,\n  children,\n  className = \"\",\n  style,\n}: MagnetronRacetrackSweepProps) {\n  const wrapRef = useRef<HTMLDivElement | null>(null);\n  const canvasRef = useRef<HTMLCanvasElement | null>(null);\n  const uid = useId();\n  const pausedRef = useRef(paused);\n  pausedRef.current = paused;\n\n  useEffect(() => {\n    const wrap = wrapRef.current;\n    const canvas = canvasRef.current;\n    if (!wrap || !canvas) return;\n\n    const surface = new GLSurface(canvas, FRAG_SRC);\n    let raf = 0;\n    let running = false;\n    let staticMode = false;\n    let cssW = 0;\n    let cssH = 0;\n    let dpr = 1;\n    let disposed = false;\n    let lastMs = performance.now();\n\n    // persistent, never-cleared radial erosion buffer — 1D because the\n    // mechanic itself is radially symmetric (see header comment).\n    const depthBuf = new Float32Array(EROSION_BUCKETS);\n    const uploadBuf = new Uint8Array(EROSION_BUCKETS);\n    let erosionTex: WebGLTexture | null = null;\n\n    let simTime = 0;\n\n    let hoverAngle = 0;\n    let pointerActive = 0; // eased 0..1\n    let pointerTarget = 0;\n\n    const SCALES = [1, 0.7];\n    const BUDGET_OVER = 24;\n    let scaleIdx = 0;\n    let frameEma = 16.7;\n    let overMs = 0;\n    let underMs = 0;\n    let upWindow = 8000;\n\n    let c0: RGB = [0.03, 0.03, 0.03];\n    let c1: RGB = [0.18, 0.18, 0.18];\n    let c2: RGB = [0.56, 0.56, 0.56];\n    let c3: RGB = [0.93, 0.93, 0.93];\n    let c4: RGB = [1, 1, 1];\n    let bg: RGB = [0, 0, 0];\n    let bias = 0;\n    let contrast = 1.15;\n\n    const readColors = () => {\n      const cs = getComputedStyle(document.documentElement);\n      const bgTok = parseHex(cs.getPropertyValue(\"--background\")) ?? [1, 1, 1];\n      const fg = parseHex(cs.getPropertyValue(\"--foreground\")) ?? [0.09, 0.09, 0.09];\n      const muted = parseHex(cs.getPropertyValue(\"--ns-muted\")) ?? [0.55, 0.55, 0.55];\n      const border = parseHex(cs.getPropertyValue(\"--border\")) ?? [0.18, 0.18, 0.18];\n      const black: RGB = [0, 0, 0];\n      const white: RGB = [1, 1, 1];\n      bg = bgTok;\n      if (luminance(bgTok) < 0.5) {\n        c0 = mixRGB(bgTok, black, 0.5);\n        c1 = mixRGB(border, bgTok, 0.15);\n        c2 = muted;\n        c3 = fg;\n        c4 = mixRGB(fg, white, 0.82);\n        bias = -0.06;\n        contrast = 1.18;\n      } else {\n        c0 = mixRGB(fg, black, 0.32);\n        c1 = mixRGB(fg, muted, 0.55);\n        c2 = mixRGB(muted, bgTok, 0.6);\n        c3 = mixRGB(bgTok, muted, 0.16);\n        c4 = bgTok;\n        bias = 0.03;\n        contrast = 1.2;\n      }\n    };\n    readColors();\n\n    // advance the erosion field by dt at absolute sim time t — pure function\n    // of the sweep clock, no randomness, so warmup and the live loop use the\n    // exact same function and a reduced-motion replay is deterministic.\n    const simulate = (dt: number, t: number) => {\n      const ringR = ringRAt(t);\n      const half = RING_WIDTH / 2;\n      const lo = ringR - half;\n      const hi = ringR + half;\n      const loIdx = Math.max(0, Math.floor(lo * EROSION_BUCKETS));\n      const hiIdx = Math.min(EROSION_BUCKETS - 1, Math.ceil(hi * EROSION_BUCKETS));\n      for (let i = loIdx; i <= hiIdx; i++) {\n        depthBuf[i] += EROSION_RATE * dt;\n      }\n    };\n\n    const warmup = (seconds: number) => {\n      depthBuf.fill(0);\n      let t = 0;\n      while (t < seconds) {\n        const step = Math.min(WARMUP_STEP, seconds - t);\n        simulate(step, t);\n        t += step;\n      }\n    };\n\n    const uploadTexture = () => {\n      const gl = surface.gl;\n      if (!gl) return;\n      for (let i = 0; i < EROSION_BUCKETS; i++) {\n        const v = Math.max(0, Math.min(1.4, depthBuf[i]!)) / 1.4;\n        uploadBuf[i] = Math.round(v * 255);\n      }\n      if (!erosionTex) {\n        erosionTex = gl.createTexture();\n        gl.bindTexture(gl.TEXTURE_2D, erosionTex);\n        gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);\n        gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);\n        gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);\n        gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);\n      } else {\n        gl.bindTexture(gl.TEXTURE_2D, erosionTex);\n      }\n      gl.texImage2D(\n        gl.TEXTURE_2D,\n        0,\n        gl.LUMINANCE,\n        EROSION_BUCKETS,\n        1,\n        0,\n        gl.LUMINANCE,\n        gl.UNSIGNED_BYTE,\n        uploadBuf\n      );\n      gl.activeTexture(gl.TEXTURE0);\n      gl.bindTexture(gl.TEXTURE_2D, erosionTex);\n    };\n\n    const draw = (t: number) => {\n      if (!surface.gl || cssW <= 0 || cssH <= 0) return;\n      uploadTexture();\n      const targetR = Math.min(cssW, cssH) * 0.42;\n      surface.v2(\"u_size\", cssW, cssH);\n      surface.f(\"u_dpr\", dpr);\n      surface.v2(\"u_center\", cssW / 2, cssH / 2);\n      surface.f(\"u_targetR\", targetR);\n      surface.f(\"u_erosionN\", EROSION_BUCKETS);\n      surface.f(\"u_ringR\", ringRAt(t));\n      surface.f(\"u_ringHalfWidth\", RING_WIDTH / 2);\n      surface.f(\"u_pointerAngle\", hoverAngle);\n      surface.f(\"u_pointerActive\", pointerActive);\n      surface.f(\"u_time\", t);\n      surface.v3(\"u_c0\", c0);\n      surface.v3(\"u_c1\", c1);\n      surface.v3(\"u_c2\", c2);\n      surface.v3(\"u_c3\", c3);\n      surface.v3(\"u_c4\", c4);\n      surface.v3(\"u_bg\", bg);\n      surface.f(\"u_bias\", bias);\n      surface.f(\"u_contrast\", contrast);\n      surface.i(\"u_erosion\", 0);\n      surface.draw(canvas.width, canvas.height);\n    };\n\n    const loop = (nowMs: number) => {\n      const rawMs = nowMs - lastMs;\n      const dt = Math.min(0.05, Math.max(0, rawMs / 1000));\n      lastMs = nowMs;\n      simTime += dt;\n\n      pointerActive += (pointerTarget - pointerActive) * (1 - Math.exp(-dt / 0.25));\n\n      simulate(dt, simTime);\n      draw(simTime);\n\n      const clamped = Math.min(50, rawMs);\n      frameEma += (clamped - frameEma) * (1 - Math.exp(-clamped / 120));\n      if (frameEma > BUDGET_OVER) {\n        overMs += clamped;\n        underMs = 0;\n      } else {\n        underMs += clamped;\n        overMs = 0;\n      }\n      const down = overMs > 900 && scaleIdx < SCALES.length - 1;\n      const up = underMs > upWindow && scaleIdx > 0;\n      if (down || up) {\n        scaleIdx += down ? 1 : -1;\n        if (down) upWindow = Math.min(64000, upWindow * 2);\n        overMs = 0;\n        underMs = 0;\n        frameEma = 16.7;\n        applyBacking();\n      }\n      raf = requestAnimationFrame(loop);\n    };\n    const wake = () => {\n      if (running || disposed) return;\n      running = true;\n      lastMs = performance.now();\n      raf = requestAnimationFrame(loop);\n    };\n    const sleep = () => {\n      cancelAnimationFrame(raf);\n      running = false;\n    };\n\n    const applyBacking = () => {\n      if (cssW < 2 || cssH < 2) return;\n      dpr = Math.min(window.devicePixelRatio || 1, 1.5) * SCALES[scaleIdx]!;\n      const pw = Math.round(cssW * dpr);\n      const ph = Math.round(cssH * dpr);\n      if (canvas.width !== pw || canvas.height !== ph) {\n        canvas.width = pw;\n        canvas.height = ph;\n      }\n      canvas.style.width = `${cssW}px`;\n      canvas.style.height = `${cssH}px`;\n      draw(staticMode ? STATIC_PHASE_S : simTime);\n    };\n\n    const resize = () => {\n      const rect = wrap.getBoundingClientRect();\n      if (rect.width < 2 || rect.height < 2) return;\n      cssW = rect.width;\n      cssH = rect.height;\n      scaleIdx = 0;\n      overMs = 0;\n      underMs = 0;\n      upWindow = 8000;\n      frameEma = 16.7;\n      applyBacking();\n    };\n\n    let rectLeft = 0;\n    let rectTop = 0;\n    let rectDirty = true;\n    const syncRect = () => {\n      if (!rectDirty) return;\n      const rect = wrap.getBoundingClientRect();\n      rectLeft = rect.left;\n      rectTop = rect.top;\n      rectDirty = false;\n    };\n    const markRectDirty = () => {\n      rectDirty = true;\n    };\n\n    // pointer only sets WHERE the ring reads brighter (angle), never the\n    // sweep clock — the sweep is a fixed physical cycle, not pointer-driven.\n    const onPointerMove = (e: PointerEvent) => {\n      syncRect();\n      const cx = rectLeft + cssW / 2;\n      const cy = rectTop + cssH / 2;\n      const dx = e.clientX - cx;\n      const dy = e.clientY - cy;\n      hoverAngle = Math.atan2(dx, -dy);\n      pointerTarget = 1;\n      if (staticMode) draw(STATIC_PHASE_S);\n    };\n    const onPointerLeave = () => {\n      pointerTarget = 0;\n    };\n\n    if (!surface.init()) return; // no WebGL: children still render over the page bg\n    const ro = new ResizeObserver(resize);\n    ro.observe(wrap);\n    resize();\n\n    wrap.addEventListener(\"pointermove\", onPointerMove);\n    wrap.addEventListener(\"pointerleave\", onPointerLeave);\n    window.addEventListener(\"scroll\", markRectDirty, { passive: true, capture: true });\n    window.addEventListener(\"resize\", markRectDirty, { passive: true });\n\n    const mq = window.matchMedia(\"(prefers-reduced-motion: reduce)\");\n    let reduced = mq.matches;\n    const applyMode = () => {\n      if (reduced || pausedRef.current) {\n        staticMode = true;\n        sleep();\n        warmup(STATIC_WARMUP_S);\n        draw(STATIC_PHASE_S);\n      } else {\n        staticMode = false;\n        simTime = LIVE_WARMUP_S;\n        warmup(LIVE_WARMUP_S);\n        wake();\n      }\n    };\n    const onMq = () => {\n      reduced = mq.matches;\n      applyMode();\n    };\n    mq.addEventListener(\"change\", onMq);\n\n    let onScreen = true;\n    const io = new IntersectionObserver(\n      (entries) => {\n        onScreen = entries.some((en) => en.isIntersecting);\n        if (!onScreen) sleep();\n        else if (!staticMode && !document.hidden) wake();\n      },\n      { threshold: 0 }\n    );\n    io.observe(wrap);\n\n    const onVis = () => {\n      if (document.hidden) sleep();\n      else if (!staticMode && onScreen) wake();\n    };\n    document.addEventListener(\"visibilitychange\", onVis);\n    applyMode();\n\n    let lastPolledPaused = pausedRef.current;\n    let poll = 0;\n    const tick = () => {\n      if (pausedRef.current !== lastPolledPaused) {\n        lastPolledPaused = pausedRef.current;\n        applyMode();\n      }\n      poll = window.setTimeout(tick, 140);\n    };\n    tick();\n\n    const themeObserver = new MutationObserver(() => {\n      readColors();\n      if (staticMode) draw(STATIC_PHASE_S);\n    });\n    themeObserver.observe(document.documentElement, {\n      attributes: true,\n      attributeFilter: [\"class\"],\n    });\n\n    const onLost = (e: Event) => {\n      e.preventDefault();\n      sleep();\n    };\n    const onRestored = () => {\n      erosionTex = null;\n      if (surface.init()) {\n        resize();\n        applyMode();\n      }\n    };\n    canvas.addEventListener(\"webglcontextlost\", onLost);\n    canvas.addEventListener(\"webglcontextrestored\", onRestored);\n\n    return () => {\n      disposed = true;\n      ro.disconnect();\n      io.disconnect();\n      mq.removeEventListener(\"change\", onMq);\n      document.removeEventListener(\"visibilitychange\", onVis);\n      themeObserver.disconnect();\n      canvas.removeEventListener(\"webglcontextlost\", onLost);\n      canvas.removeEventListener(\"webglcontextrestored\", onRestored);\n      wrap.removeEventListener(\"pointermove\", onPointerMove);\n      wrap.removeEventListener(\"pointerleave\", onPointerLeave);\n      window.removeEventListener(\"scroll\", markRectDirty, { capture: true } as EventListenerOptions);\n      window.removeEventListener(\"resize\", markRectDirty);\n      window.clearTimeout(poll);\n      sleep();\n      if (erosionTex && surface.gl) surface.gl.deleteTexture(erosionTex);\n      erosionTex = null;\n      surface.destroy();\n    };\n  }, []);\n\n  return (\n    <div\n      ref={wrapRef}\n      data-magnetron-racetrack-sweep={uid}\n      className={`relative isolate h-full w-full touch-none overflow-hidden bg-background ${className}`}\n      style={style}\n    >\n      <canvas ref={canvasRef} aria-hidden=\"true\" className=\"absolute inset-0 block h-full w-full\" />\n      {children ? <div className=\"relative z-[1] h-full w-full\">{children}</div> : null}\n    </div>\n  );\n}\n\nMagnetronRacetrackSweep.displayName = \"MagnetronRacetrackSweep\";\n",
      "type": "registry:ui",
      "target": "components/ui/magnetron-racetrack-sweep.tsx"
    }
  ],
  "cssVars": {
    "theme": {
      "color-ns-muted": "var(--ns-muted)"
    },
    "light": {
      "ns-muted": "#4d4d4d"
    },
    "dark": {
      "ns-muted": "#8f8f8f"
    }
  },
  "meta": {
    "collection": "loud",
    "tags": [
      "background",
      "canvas",
      "webgl",
      "shader",
      "metal",
      "erosion",
      "lab-instrument",
      "cursor",
      "mono",
      "ambient",
      "generative"
    ],
    "instruction": "Build <MagnetronRacetrackSweep paused? children? className?> as a full-bleed WebGL background rendering sputter-target erosion under an oscillating magnetron racetrack. THE EROSION FIELD IS 1D: because the racetrack is a full annulus at every instant, its erosion history is a function of radius alone, never angle — the persistent, never-cleared accumulation buffer (same non-clearing technique as rime-creep/edm-crater-field, but MONOTONIC here, never decaying, matching real physical erosion) is therefore a 256-texel 1D LUMINANCE texture (EROSION_BUCKETS=256), not a full 2D grid. ringRAt(t) = 0.5 + 0.2*sin(2*pi*t/22) is the sweep's live normalized radius (oscillating between 0.3 and 0.7 of target radius, 22s period); every simulate(dt,t) call adds EROSION_RATE=0.002 depth-units/s to every bucket whose normalized radius falls within [ringRAt(t) - 0.04, ringRAt(t) + 0.04] (RING_WIDTH=0.08 total). Because the sinusoid dwells longest at its own turning points (0.3 and 0.7) and passes through the middle twice per cycle at full speed, the two turning-point bands erode measurably deeper than the middle band with ZERO extra logic — the dual-band asymmetry is the sweep's own kinematics, not a scripted bias. GROOVE SHADING: the composite fragment shader derives a surface normal from the 1D depth field's RADIAL gradient only (central difference at r +/- 1/256, no tangential term exists by construction) and projects it back into 2D along the local radial direction (rDir = p/length(p)), so the groove reads as a real machined cut catching a small achromatic multi-band studio (env(), same family as edm-crater-field's, fewer bands than a mirror pool needs) rather than a flat painted ring. Material removed subtracts directly from luminance (depthVis*0.34) — lower relative luminance than the fresh face, same direction in both themes, only bias/contrast differ per theme (dark: bias -0.06, contrast 1.18; light: bias 0.03, contrast 1.2). LIVE GLOW RING: a second, non-accumulated quantity — ringMask, a smoothstep band around the CURRENT ringRAt(t), added straight to luminance (+0.5) every frame, explicitly never colour (real sputtering plasma glows violet/pink; that hue is discarded on purpose per the monochrome constraint) and never composited into the persistent erosion texture. POINTER: pointermove computes the angle from the target centre to the pointer (never its radius) and eases pointerActive 0->1 over a 250ms exponential; the shader adds an angular gaussian boost (falloff 0.35 rad) on top of ringMask, brightening the glow ring locally by luminance only, never accent, and it must never alter ringRAt/simTime — the sweep is a fixed physical cycle, not pointer-driven. HOST: mirrors edm-crater-field's scaffolding — a GLSurface host class (fullscreen triangle pair, lazily-resolved uniform locations), a two-tier adaptive DPR ladder (SCALES=[1,0.7], steps down after a sustained >24ms EMA for 900ms, back up after a much longer clean stretch that doubles each time it trips), DPR capped at 1.5, a ResizeObserver on the wrapper (never window.resize), IntersectionObserver(threshold 0) + visibilitychange both gating the single rAF loop, webglcontextlost/restored rebuilding cleanly, and a paused prop polled every 140ms (props can't be read fresh inside a long-lived rAF closure without a ref). Tokens (--background, --foreground, --ns-muted, --border) are read via getComputedStyle(document.documentElement) before any paint and re-read on a MutationObserver watching documentElement's class. prefers-reduced-motion (or paused) sets a STATIC_WARMUP_S=480s deterministic warmup (the same simulate() function stepped in fixed 0.25s chunks from an all-zero buffer, no randomness anywhere in this component) then draws exactly one frame at STATIC_PHASE_S = 22*0.5 (sweep phase 0.5, ring at mid-radius, dual-band asymmetry already visible), named \"racetrack-mid\" — never a blank disc. The live (non-reduced) mode instead warms up only LIVE_WARMUP_S=40s before its first paint, so the groove starts faint rather than either pristine or already deep."
  },
  "type": "registry:ui"
}