{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "hero-isobar-contours",
  "title": "Hero Isobar Contours",
  "description": "Hero background of drifting, breathing isobar-like contour lines whose density bunches tightly around the primary CTA and whose whole field leans toward the pointer, so the layout physically reads as a live pressure system centered on the one action that matters.",
  "dependencies": [],
  "files": [
    {
      "path": "registry/loud/hero-isobar-contours/component.tsx",
      "content": "\"use client\";\n\nimport { useEffect, useRef } from \"react\";\n\n// ---------------------------------------------------------------------------\n// PressureFront — a hero background of isobar-like closed contour lines that\n// bunch tightly around the primary CTA, so the composition physically leans\n// toward the one action that matters. Pure SVG: every ring is a <path> whose\n// `d` is recomputed directly (no canvas, no interpolation) on a throttled\n// rAF loop, so `stroke` can reference --border/--ns-muted/--foreground natively\n// and both themes repaint for free — no getComputedStyle color parsing at\n// all. Line DENSITY (radial spacing that compresses near the CTA) is the\n// hierarchy device, not color or a gradient fill.\n//\n// The field is genuinely alive at rest, not just on interaction: a\n// continuous drift rotates the wobble, an independent faster breathing\n// pulse swells/contracts the low-pressure centre, and it leans toward the\n// pointer wherever it roams over the hero (not only when hovering the CTA).\n// All three are additive perturbations on top of the same closed-form\n// radius, never a point-attractor toward absolute coordinates — that keeps\n// ~30 nested closed curves from ever converging into each other.\n// ---------------------------------------------------------------------------\n\nconst RINGS = 30;\nconst ANGLES = 56; // vertices per ring — smooth at 1px stroke, cheap to redraw\nconst COMPRESSION = 2.15; // >1 bunches inner rings tightly, spreads outer ones\nconst ELLIPSE_X = 1.28; // rings are gently wide, matching a landscape hero\nconst PULL_PX = 8; // max inward pull on CTA hover/focus (deepening low)\nconst FRAME_INTERVAL = 1000 / 20; // throttled redraw rate — isobars drift slow, but smooth enough to read the pointer lean\nconst SPRING_K = 90;\nconst SPRING_ZETA = 0.8;\nconst BREATH_MS = 7000; // independent, faster period so the low visibly pulses instead of only slowly rotating\nconst BREATH_FRAC = 0.22; // +/- fraction minR itself swells by — a single global scalar, see drawRings for why\nconst POINTER_PULL_PX = 30; // max outward lean toward the pointer — stronger than the CTA's isotropic pull\nconst POINTER_GAP_CLAMP = 0.5; // never lean a ring past this fraction of its gap to the next ring; kept well under 1 because the existing wobble term already consumes part of that gap on its own\n\n// Deterministic low-frequency harmonic sum standing in for 2D noise: it's\n// exactly periodic in theta so every ring closes without a seam at 0/2π,\n// which a sampled value-noise field can't guarantee without extra stitching.\nfunction ringWobble(theta: number, phase: number, seed: number) {\n  return (\n    0.5 * Math.sin(3 * theta + phase + seed * 0.7) +\n    0.3 * Math.sin(5 * theta - phase * 1.3 + seed * 1.3 + 1.7) +\n    0.2 * Math.sin(8 * theta + phase * 0.6 + seed * 2.1 + 4.1)\n  );\n}\n\n// Ring color steps toward the CTA — discrete steps, never a gradient. Colors\n// are CSS custom properties resolved natively by the browser (no JS parsing).\nfunction ringColor(i: number) {\n  if (i < 2) return \"color-mix(in srgb, var(--foreground) 25%, transparent)\";\n  if (i < 5) return \"var(--ns-muted)\";\n  return \"var(--border)\";\n}\n\nexport interface PressureFrontCta {\n  label: string;\n  href?: string;\n  onClick?: () => void;\n}\n\nexport interface PressureFrontProps {\n  /** mono eyebrow label above the headline */\n  eyebrow?: string;\n  /** headline text; an array renders one line per entry */\n  headline?: string | string[];\n  /** supporting copy under the headline */\n  subcopy?: string;\n  /** required primary CTA button/link */\n  primaryCta: PressureFrontCta;\n  /** optional secondary CTA rendered beside the primary one */\n  secondaryCta?: PressureFrontCta;\n  /** number of contour rings. default 30 */\n  rings?: number;\n  /** ms for one full drift loop of the noise phase. default 20000 */\n  driftMs?: number;\n  /** extra classes merged onto the rendered root element */\n  className?: string;\n}\n\nexport function PressureFront({\n  eyebrow,\n  headline = \"Ship the thing\",\n  subcopy,\n  primaryCta,\n  secondaryCta,\n  rings = RINGS,\n  driftMs = 20000,\n  className = \"\",\n}: PressureFrontProps) {\n  const rootRef = useRef<HTMLDivElement>(null);\n  const svgRef = useRef<SVGSVGElement>(null);\n  const ctaRef = useRef<HTMLElement | null>(null);\n  const pathRefs = useRef<(SVGPathElement | null)[]>([]);\n  const dimsRef = useRef({ w: 0, h: 0 });\n  const ctaCenterRef = useRef({ x: 0, y: 0 });\n\n  const headlineLines = Array.isArray(headline) ? headline : [headline];\n\n  useEffect(() => {\n    const root = rootRef.current;\n    const svg = svgRef.current;\n    const cta = ctaRef.current;\n    if (!root || !svg || !cta) return;\n\n    const reduced = window.matchMedia(\"(prefers-reduced-motion: reduce)\").matches;\n\n    // -- measurement: CTA position re-derived on every resize --------------\n    const measure = () => {\n      const rootRect = root.getBoundingClientRect();\n      const w = rootRect.width;\n      const h = rootRect.height;\n      dimsRef.current = { w, h };\n      svg.setAttribute(\"viewBox\", `0 0 ${Math.max(1, w)} ${Math.max(1, h)}`);\n      const ctaRect = cta.getBoundingClientRect();\n      ctaCenterRef.current = {\n        x: ctaRect.left - rootRect.left + ctaRect.width / 2,\n        y: ctaRect.top - rootRect.top + ctaRect.height / 2,\n      };\n    };\n    measure();\n\n    // -- geometry: recomputed and written directly to each <path d> --------\n    // `breathPhase` runs on its own faster clock so the low-pressure centre\n    // visibly pulses instead of only slowly rotating with the drift. Pointer\n    // deformation is deliberately RADIAL and per-ring (never \"toward a fixed\n    // point in screen space\"): each ring only ever swells along its own\n    // center-relative radius, one-sided toward the cursor's bearing, clamped\n    // to a fraction of its actual gap to the next ring. That keeps neighbor\n    // rings moving in parallel — a point-attractor on ~30 nested closed\n    // curves would make rings near the cursor's radius converge and cross.\n    const drawRings = (\n      phase: number,\n      pullPx: number,\n      breathPhase: number,\n      pointerX: number,\n      pointerY: number,\n      pointerStrength: number\n    ) => {\n      const { w, h } = dimsRef.current;\n      if (w < 1 || h < 1) return;\n      const { x: ctaX, y: ctaY } = ctaCenterRef.current;\n      const maxR = Math.max(w, h) * 0.92;\n      const minR = Math.min(28, maxR * 0.05);\n\n      // Breathing modulates minR as ONE global scalar, not a per-ring term —\n      // deliberately, because the innermost rings are already packed within\n      // a fraction of a px of each other (t^2.15 is nearly flat near t=0), so\n      // any independent per-ring perturbation there risks flipping their\n      // order. Folding the pulse into minR instead keeps every ring's radius\n      // exactly (maxR - minRBreath) * t^COMPRESSION + minRBreath: the gap\n      // between any two rings is (maxR - minRBreath) * (t_i^C - t_(i-1)^C),\n      // always positive since maxR always exceeds minRBreath by construction\n      // (BREATH_FRAC is a small fraction) — so the pulse can never cross\n      // rings, only grow/shrink the whole low in lockstep, tapering to zero\n      // at the outer edge for free via the same (1 - t^C) the compression\n      // curve already has.\n      const minRBreath = minR * (1 + BREATH_FRAC * Math.sin(breathPhase));\n\n      // Pass 1: centers + base radii for every ring, so pass 2 can clamp the\n      // pointer lean against each ring's *real* (post-breathing) neighbor gap.\n      const centers: { x: number; y: number }[] = new Array(rings);\n      const baseRs: number[] = new Array(rings);\n      for (let i = 0; i < rings; i++) {\n        const t = i / (rings - 1);\n        const driftR = minR * 0.9;\n        centers[i] = {\n          x: ctaX + driftR * Math.cos(phase * 0.5 + i * 0.05),\n          y: ctaY + driftR * 0.6 * Math.sin(phase * 0.5 + i * 0.05),\n        };\n        baseRs[i] = minRBreath + (maxR - minRBreath) * Math.pow(t, COMPRESSION);\n      }\n\n      for (let i = 0; i < rings; i++) {\n        const el = pathRefs.current[i];\n        if (!el) continue;\n        const t = i / (rings - 1);\n        const { x: cx, y: cy } = centers[i];\n        const baseR = baseRs[i];\n        const amp = (maxR - minR) * 0.045 * (0.35 + 0.65 * t);\n        const seed = i * 0.53;\n        const ringPhase = phase + i * 0.045;\n        const pullHere = pullPx * (1 - t);\n\n        // How much (and toward what bearing) this ring leans toward the\n        // pointer: gaussian in RADIUS-space (how close the pointer's distance\n        // from this ring's own center is to this ring's own radius), not\n        // screen-space distance to a point — so the lean is inherently a\n        // per-ring radial swell, not a convergent pull.\n        let pointerAmp = 0;\n        let pointerAngle = 0;\n        if (pointerStrength > 0.001) {\n          const dxp = pointerX - cx;\n          const dyp = pointerY - cy;\n          const distToCenter = Math.hypot(dxp, dyp);\n          pointerAngle = Math.atan2(dyp, dxp);\n          const distToRing = distToCenter - baseR;\n          const sigma = Math.max(24, (maxR - minR) * 0.09);\n          const ringFalloff = Math.exp(-(distToRing * distToRing) / (2 * sigma * sigma));\n          const gapPrev = i > 0 ? Math.abs((baseRs[i] ?? baseR) - (baseRs[i - 1] ?? baseR)) : Math.abs((baseRs[1] ?? baseR) - (baseRs[0] ?? baseR));\n          const gapNext = i < rings - 1 ? Math.abs((baseRs[i + 1] ?? baseR) - (baseRs[i] ?? baseR)) : gapPrev;\n          const localGap = Math.min(gapPrev, gapNext);\n          pointerAmp = Math.min(POINTER_PULL_PX, localGap * POINTER_GAP_CLAMP) * pointerStrength * ringFalloff;\n        }\n\n        let d = \"\";\n        for (let k = 0; k < ANGLES; k++) {\n          const theta = (k / ANGLES) * Math.PI * 2;\n          const wob = amp * ringWobble(theta, ringPhase, seed);\n          let r = baseR + wob - pullHere;\n          if (pointerAmp > 0.01) {\n            const align = Math.max(0, Math.cos(theta - pointerAngle));\n            r += pointerAmp * align * align; // one-sided swell on the cursor-facing arc only\n          }\n          r = Math.max(4, r);\n          const x = cx + r * Math.cos(theta) * ELLIPSE_X;\n          const y = cy + r * Math.sin(theta);\n          d += k === 0 ? `M${x.toFixed(1)},${y.toFixed(1)}` : `L${x.toFixed(1)},${y.toFixed(1)}`;\n        }\n        d += \"Z\";\n        el.setAttribute(\"d\", d);\n      }\n    };\n\n    if (reduced) {\n      // Static density gradient only: no phase drift, no continuous spring —\n      // this is the strongest reduced-motion story in the set, since the\n      // hierarchy is encoded structurally (ring spacing), not by motion.\n      // No root-wide pointer tracking here on purpose — a redraw on every\n      // pointermove is exactly the continuous motion prefers-reduced-motion\n      // opts out of. Only the CTA's own enter/leave/focus/blur still toggles\n      // a single static redraw, same as before.\n      let staticPull = 0;\n      drawRings(0, 0, 0, 0, 0, 0);\n      const onEnter = () => {\n        staticPull = PULL_PX;\n        drawRings(0, staticPull, 0, 0, 0, 0);\n      };\n      const onLeave = () => {\n        staticPull = 0;\n        drawRings(0, staticPull, 0, 0, 0, 0);\n      };\n      cta.addEventListener(\"pointerenter\", onEnter);\n      cta.addEventListener(\"pointerleave\", onLeave);\n      cta.addEventListener(\"focus\", onEnter);\n      cta.addEventListener(\"blur\", onLeave);\n      const ro = new ResizeObserver(() => {\n        measure();\n        drawRings(0, staticPull, 0, 0, 0, 0);\n      });\n      ro.observe(root);\n      const ctaRo = new ResizeObserver(() => {\n        measure();\n        drawRings(0, staticPull, 0, 0, 0, 0);\n      });\n      ctaRo.observe(cta);\n      return () => {\n        ro.disconnect();\n        ctaRo.disconnect();\n        cta.removeEventListener(\"pointerenter\", onEnter);\n        cta.removeEventListener(\"pointerleave\", onLeave);\n        cta.removeEventListener(\"focus\", onEnter);\n        cta.removeEventListener(\"blur\", onLeave);\n      };\n    }\n\n    // -- full loop: ambient drift + breathing + damped-spring hover pull +\n    // pointer-follow lean -----------------------------------------------\n    let raf = 0;\n    let elVisible = true;\n    let pageVisible = document.visibilityState === \"visible\";\n    let visible = elVisible && pageVisible;\n    let hoverOn = false;\n    let startTime = 0;\n    let lastTick = 0;\n    let lastDraw = 0;\n    let pull = 0;\n    let pullVel = 0;\n\n    // Pointer follow: raw target from the event, a smoothed trailing\n    // position (framerate-normalized lerp, same idiom as chart-ridgeline-terrain),\n    // and a spring-eased 0..1 strength so entering/leaving ramps rather than\n    // snaps. Snaps straight to the raw position on the frame pointer\n    // tracking (re)starts, so it doesn't sweep in from wherever it last was.\n    let pointerRawX = 0;\n    let pointerRawY = 0;\n    let pointerX = 0;\n    let pointerY = 0;\n    let pointerActive = false;\n    let pointerTracking = false;\n    let pointerStrength = 0;\n    let pointerStrengthVel = 0;\n\n    const tick = (now: number) => {\n      if (!startTime) startTime = now;\n      const dt = lastTick ? Math.min(0.05, (now - lastTick) / 1000) : 1 / 60;\n      lastTick = now;\n\n      const target = hoverOn ? PULL_PX : 0;\n      const c = 2 * SPRING_ZETA * Math.sqrt(SPRING_K);\n      const accel = -SPRING_K * (pull - target) - c * pullVel;\n      pullVel += accel * dt;\n      pull += pullVel * dt;\n      if (Math.abs(pull - target) < 0.02 && Math.abs(pullVel) < 0.02) {\n        pull = target;\n        pullVel = 0;\n      }\n\n      if (pointerActive) {\n        if (!pointerTracking) {\n          pointerX = pointerRawX;\n          pointerY = pointerRawY;\n          pointerTracking = true;\n        } else {\n          const k = 1 - Math.pow(0.88, dt * 60);\n          pointerX += (pointerRawX - pointerX) * k;\n          pointerY += (pointerRawY - pointerY) * k;\n        }\n      } else {\n        pointerTracking = false;\n      }\n      const pointerTarget = pointerActive ? 1 : 0;\n      const cp = 2 * SPRING_ZETA * Math.sqrt(SPRING_K);\n      const accelP = -SPRING_K * (pointerStrength - pointerTarget) - cp * pointerStrengthVel;\n      pointerStrengthVel += accelP * dt;\n      pointerStrength += pointerStrengthVel * dt;\n      if (Math.abs(pointerStrength - pointerTarget) < 0.002 && Math.abs(pointerStrengthVel) < 0.002) {\n        pointerStrength = pointerTarget;\n        pointerStrengthVel = 0;\n      }\n\n      if (now - lastDraw >= FRAME_INTERVAL) {\n        lastDraw = now;\n        const phase = ((now - startTime) / driftMs) * Math.PI * 2;\n        const breathPhase = ((now - startTime) / BREATH_MS) * Math.PI * 2;\n        drawRings(phase, pull, breathPhase, pointerX, pointerY, pointerStrength);\n      }\n      raf = visible ? requestAnimationFrame(tick) : 0;\n    };\n    const wake = () => {\n      if (!raf && visible) raf = requestAnimationFrame(tick);\n    };\n\n    const onCtaOn = () => {\n      hoverOn = true;\n      wake();\n    };\n    const onCtaOff = () => {\n      hoverOn = false;\n      wake();\n    };\n    cta.addEventListener(\"pointerenter\", onCtaOn);\n    cta.addEventListener(\"pointerleave\", onCtaOff);\n    cta.addEventListener(\"focus\", onCtaOn);\n    cta.addEventListener(\"blur\", onCtaOff);\n\n    // Whole-hero pointer tracking — the field leans toward the cursor\n    // wherever it is, not just when hovering the CTA. Mouse/pen only: touch\n    // has no hover state and pointermove-during-scroll would read as jitter.\n    // rect is re-measured on every move (matching chart-ridgeline-terrain's idiom)\n    // rather than cached, so page scroll never throws the coordinates off.\n    const onRootMove = (e: PointerEvent) => {\n      if (e.pointerType === \"touch\") return;\n      const rect = root.getBoundingClientRect();\n      pointerRawX = e.clientX - rect.left;\n      pointerRawY = e.clientY - rect.top;\n      pointerActive = true;\n      wake();\n    };\n    const onRootLeave = (e: PointerEvent) => {\n      if (e.pointerType === \"touch\") return;\n      pointerActive = false;\n      wake();\n    };\n    root.addEventListener(\"pointermove\", onRootMove);\n    root.addEventListener(\"pointerleave\", onRootLeave);\n\n    const ro = new ResizeObserver(measure);\n    ro.observe(root);\n    const ctaRo = new ResizeObserver(measure);\n    ctaRo.observe(cta);\n\n    const io = new IntersectionObserver((entries) => {\n      elVisible = entries[0]?.isIntersecting ?? true;\n      visible = elVisible && pageVisible;\n      wake();\n    });\n    io.observe(root);\n\n    const onVisibility = () => {\n      pageVisible = document.visibilityState === \"visible\";\n      visible = elVisible && pageVisible;\n      wake();\n    };\n    document.addEventListener(\"visibilitychange\", onVisibility);\n\n    wake();\n\n    return () => {\n      cancelAnimationFrame(raf);\n      ro.disconnect();\n      ctaRo.disconnect();\n      io.disconnect();\n      document.removeEventListener(\"visibilitychange\", onVisibility);\n      cta.removeEventListener(\"pointerenter\", onCtaOn);\n      cta.removeEventListener(\"pointerleave\", onCtaOff);\n      cta.removeEventListener(\"focus\", onCtaOn);\n      cta.removeEventListener(\"blur\", onCtaOff);\n      root.removeEventListener(\"pointermove\", onRootMove);\n      root.removeEventListener(\"pointerleave\", onRootLeave);\n    };\n  }, [rings, driftMs]);\n\n  const ctaFocusRing =\n    \"focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-ns-accent\";\n\n  return (\n    <section\n      ref={rootRef}\n      className={`relative isolate overflow-hidden bg-background ${className}`}\n    >\n      <svg\n        ref={svgRef}\n        aria-hidden=\"true\"\n        className=\"pointer-events-none absolute inset-0 h-full w-full\"\n      >\n        {Array.from({ length: rings }).map((_, i) => (\n          <path\n            key={i}\n            ref={(el) => {\n              pathRefs.current[i] = el;\n            }}\n            fill=\"none\"\n            style={{ stroke: ringColor(i), strokeWidth: 1 }}\n          />\n        ))}\n      </svg>\n\n      <div className=\"relative z-10 mx-auto flex w-full max-w-5xl flex-col items-center px-6 pb-16 pt-24 text-center sm:pb-24 sm:pt-32\">\n        {eyebrow ? (\n          <p className=\"mb-6 font-mono text-[11px] tracking-widest text-ns-muted\">\n            {eyebrow}\n          </p>\n        ) : null}\n        <h1\n          className=\"font-semibold text-foreground\"\n          style={{\n            fontSize: \"clamp(2.5rem, 6.5vw, 4.5rem)\",\n            lineHeight: 1.06,\n            letterSpacing: \"-0.03em\",\n          }}\n        >\n          {headlineLines.map((line, i) => (\n            <span key={i} className=\"block\">\n              {line}\n            </span>\n          ))}\n        </h1>\n        {subcopy ? (\n          <p className=\"mt-6 max-w-xl text-base leading-relaxed text-ns-muted\">\n            {subcopy}\n          </p>\n        ) : null}\n        <div className=\"mt-9 flex flex-wrap items-center justify-center gap-3\">\n          {primaryCta.href ? (\n            <a\n              ref={(el) => {\n                ctaRef.current = el;\n              }}\n              href={primaryCta.href}\n              data-cta=\"primary\"\n              onClick={primaryCta.onClick}\n              className={`rounded-sm bg-foreground px-5 py-2.5 text-sm font-medium text-background transition-opacity duration-200 hover:opacity-90 ${ctaFocusRing}`}\n            >\n              {primaryCta.label}\n            </a>\n          ) : (\n            <button\n              ref={(el) => {\n                ctaRef.current = el;\n              }}\n              type=\"button\"\n              data-cta=\"primary\"\n              onClick={primaryCta.onClick}\n              className={`rounded-sm bg-foreground px-5 py-2.5 text-sm font-medium text-background transition-opacity duration-200 hover:opacity-90 ${ctaFocusRing}`}\n            >\n              {primaryCta.label}\n            </button>\n          )}\n          {secondaryCta ? (\n            secondaryCta.href ? (\n              <a\n                href={secondaryCta.href}\n                onClick={secondaryCta.onClick}\n                className={`rounded-sm border border-border px-5 py-2.5 text-sm font-medium text-ns-muted transition-colors duration-200 hover:border-foreground/20 hover:text-foreground ${ctaFocusRing}`}\n              >\n                {secondaryCta.label}\n              </a>\n            ) : (\n              <button\n                type=\"button\"\n                onClick={secondaryCta.onClick}\n                className={`rounded-sm border border-border px-5 py-2.5 text-sm font-medium text-ns-muted transition-colors duration-200 hover:border-foreground/20 hover:text-foreground ${ctaFocusRing}`}\n              >\n                {secondaryCta.label}\n              </button>\n            )\n          ) : null}\n        </div>\n      </div>\n    </section>\n  );\n}\n",
      "type": "registry:ui",
      "target": "components/ui/hero-isobar-contours.tsx"
    }
  ],
  "cssVars": {
    "theme": {
      "color-ns-muted": "var(--ns-muted)",
      "color-ns-accent": "var(--ns-accent)"
    },
    "light": {
      "ns-muted": "#4d4d4d",
      "ns-accent": "#006bff"
    },
    "dark": {
      "ns-muted": "#8f8f8f"
    }
  },
  "meta": {
    "collection": "loud",
    "tags": [
      "svg",
      "hero",
      "contour",
      "isobar",
      "cta",
      "hierarchy",
      "ambient",
      "drift",
      "pointer-reactive",
      "signup"
    ],
    "instruction": "A hero whose background is ~30 closed isobar-like contour <path> rings, each one a plain SVG element whose stroke reads --border/--ns-muted/--foreground (and color-mix(in srgb, var(--foreground) 25%, transparent) for the innermost two) directly as CSS custom properties on the `style` attribute — no getComputedStyle, no canvas, no color parsing at all, so both themes repaint for free. Ring radius follows radius(t) = minR + (maxR - minR) * t^2.15 for t in [0,1] across the ring index, a convex power curve that packs the inner rings tightly and spreads the outer ones — this radial compression toward the CTA's measured center is the entire hierarchy device; color only steps twice (border to muted to foreground@25%) as secondary reinforcement, never a continuous gradient. Each ring is additionally perturbed by a deterministic low-frequency harmonic sum (three sine terms at ascending integer frequencies with descending amplitude, a closed-form stand-in for 2D noise) sampled directly in theta, which is exactly periodic so every ring closes without a stitching seam; each ring also carries a small per-index phase offset and a slightly orbiting center (a few px, cosine/sine of the drift phase) so the rings read as a layered weather-map field rather than N identical concentric circles. The CTA's on-screen center is measured via getBoundingClientRect relative to the hero's own bounding rect and re-measured on a ResizeObserver watching both the hero container and the CTA element itself, so layout reflow (font load, viewport resize, content wrap) never leaves the field anchored to a stale position. Geometry is recomputed directly every throttled tick (~20fps via a rAF accumulator, no interpolation between cached keyframes — the recompute itself is cheap closed-form trig, on the order of a couple thousand sin/cos calls, so no Web Worker is warranted) and the phase advances (elapsed / 20s) * 2*pi in a continuous loop that never settles, giving the field constant drift (tightened from an original 40s once that read as too subtle to register as alive at rest); the loop pauses on IntersectionObserver (offscreen) and document visibilitychange (backgrounded tab). A second, independent phase clock (7s period) drives a breathing pulse by modulating minR itself as a single global +/-22% scalar (never per-ring), so the low-pressure centre visibly swells and contracts on its own faster rhythm instead of the field reading as a slow uniform rotation; folding the pulse into minR rather than perturbing each ring's radius independently keeps every ring's gap to its neighbor at (maxR - minR_breathing) * (t_i^2.15 - t_(i-1)^2.15), always positive, so the pulse cannot invert ring order even where the innermost rings are packed within a fraction of a pixel of each other, and it tapers to zero at the outer edge for free via the same (1 - t^2.15) the compression curve already has. Hovering or focusing the primary CTA raises a target inward pull of 8px, integrated every animation frame (not just the throttled draw ticks) through a damped spring (k=90, zeta=0.8) and applied per ring scaled by (1 - t), so inner rings pull hardest and the effect reads as the low visibly deepening; releasing focus/hover springs it back. Separately, the whole hero tracks the pointer (mouse/pen, not touch) and every ring leans a bounded outward swell toward the cursor's bearing — one-sided via max(0, cos(theta - angleToPointer))^2, gaussian-weighted by how close the pointer's distance from that ring's own center is to that ring's own radius, and clamped to 50% of its real base gap to the neighboring ring, deliberately well under 100% because the pre-existing wobble term already consumes part of that same gap on its own — so the pointer term alone is always a parallel, non-convergent lean (never a point-attractor that would pull rings across each other), and in practice adjacent contours stay clearly separated on the cursor-facing arc even directly over the densely-packed centre; a spring-eased 0..1 strength ramps the effect in/out on pointer enter/leave rather than snapping. The SVG layer is aria-hidden and pointer-events-none, so it never interferes with the real interactive CTA underneath — pointer tracking is bound to the hero section itself, not the SVG. The breathing pulse and pointer-lean are both full-motion-only: under reduced motion neither the faster breathing clock nor root-wide pointer tracking ever starts (a continuous redraw on every pointermove is exactly the motion that preference opts out of), leaving only the CTA's existing single-toggle hover/focus pull. The primary CTA is a solid bg-foreground/text-background button — deliberately neutral, since --ns-accent is reserved for exactly one appearance in this whole component: the CTA's focus-visible outline, the same outline-2/outline-offset-2/outline-ns-accent pattern used registry-wide. The secondary CTA is a bordered ghost button with the same focus treatment (in --border/--foreground, not accent, keeping accent scarce). Under prefers-reduced-motion the phase is frozen at a single fixed instant and the continuous spring/rAF loop never starts at all — the static ring spacing alone still does the complete hierarchy job, and hover/focus on the CTA instantly toggles between the resting and pulled-in ring layout (a single redraw, not an animation) so the interaction stays legible without motion. Zero dependencies.",
    "rank": 15
  },
  "type": "registry:ui"
}