/* global React */ // Jullia — the hero backdrop: a generative, monochrome trace, not footage. // Three hairline waveforms breathe behind the headline, same register as an // oscilloscope reading a signal. On a fine pointer (desktop, trackpad) the // trace answers to the cursor — amplitude and pitch bend toward where you // look, "dynamics" made literal. On touch, there is no cursor to answer to, // so the same trace follows a slow, fixed orbit instead — a closed loop, not // a random idle, exactly as this brand's motion is always damped and never // bouncy (see tokens/motion.css). Holds a single still frame for // prefers-reduced-motion. function useFinePointer() { const [fine, setFine] = React.useState(true); React.useEffect(() => { if (!window.matchMedia) return undefined; const mq = window.matchMedia('(pointer: fine)'); const update = () => setFine(mq.matches); update(); mq.addEventListener('change', update); return () => mq.removeEventListener('change', update); }, []); return fine; } const MUSIC_GRAIN_URL = "data:image/svg+xml;utf8,"; function MusicBackground({ style = {} }) { const wrapRef = React.useRef(null); const canvasRef = React.useRef(null); const isFinePointer = useFinePointer(); const reduced = React.useMemo( () => !!(window.matchMedia && window.matchMedia('(prefers-reduced-motion: reduce)').matches), [] ); React.useEffect(() => { const wrap = wrapRef.current; const canvas = canvasRef.current; if (!wrap || !canvas) return undefined; const ctx = canvas.getContext('2d'); let w = 0, h = 0; const dpr = Math.min(window.devicePixelRatio || 1, 2); const resize = () => { const rect = wrap.getBoundingClientRect(); w = rect.width; h = rect.height; canvas.width = Math.max(1, w * dpr); canvas.height = Math.max(1, h * dpr); ctx.setTransform(dpr, 0, 0, dpr, 0, 0); }; resize(); const ro = new ResizeObserver(resize); ro.observe(wrap); // pointer target, normalized to [-1, 1] on each axis const target = { x: 0, y: 0 }; const current = { x: 0, y: 0 }; const onMove = (e) => { const rect = wrap.getBoundingClientRect(); target.x = Math.max(-1, Math.min(1, ((e.clientX - rect.left) / rect.width) * 2 - 1)); target.y = Math.max(-1, Math.min(1, ((e.clientY - rect.top) / rect.height) * 2 - 1)); }; if (isFinePointer) window.addEventListener('mousemove', onMove, { passive: true }); const LINES = 3; const t0 = performance.now(); let raf = null; const render = (time) => { const t = (time - t0) / 1000; if (isFinePointer) { // damped follow — slow, settling, never a snap (brand's --ease-door in motion terms) current.x += (target.x - current.x) * 0.035; current.y += (target.y - current.y) * 0.035; } else { // no pointer to answer to on touch — a slow, fixed, closed orbit: "just looped" current.x = Math.sin(t * 0.12) * 0.7; current.y = Math.sin(t * 0.08) * 0.5 * Math.cos(t * 0.05); } ctx.clearRect(0, 0, w, h); const baseAmp = h * (0.045 + 0.03 * Math.abs(current.y)); const freq = (1.0 + current.x * 0.45) / Math.max(w, 1); for (let i = 0; i < LINES; i += 1) { const spread = i - (LINES - 1) / 2; const amp = baseAmp * (1 - Math.abs(spread) * 0.18); const phase = t * (0.5 + i * 0.06) + spread * 0.9; const yOff = h / 2 + spread * h * 0.06 + current.y * h * 0.05; const opacity = 0.34 - Math.abs(spread) * 0.08; ctx.beginPath(); for (let x = 0; x <= w; x += 5) { const y = yOff + Math.sin(x * freq * Math.PI * 2 + phase) * amp + Math.sin(x * freq * Math.PI * 4.3 + phase * 1.6) * amp * 0.22; if (x === 0) ctx.moveTo(x, y); else ctx.lineTo(x, y); } ctx.strokeStyle = `rgba(236, 236, 232, ${opacity})`; ctx.lineWidth = i === Math.floor(LINES / 2) ? 1.25 : 1; ctx.stroke(); } raf = requestAnimationFrame(render); }; if (reduced) render(t0); else raf = requestAnimationFrame(render); return () => { ro.disconnect(); if (isFinePointer) window.removeEventListener('mousemove', onMove); if (raf) cancelAnimationFrame(raf); }; }, [isFinePointer, reduced]); return (