/* Hero — dark VSL with WhatsApp phone mockup + ethereal animated shadow bg */ function mapRange(value, fromLow, fromHigh, toLow, toHigh) { if (fromLow === fromHigh) return toLow; return toLow + (value - fromLow) / (fromHigh - fromLow) * (toHigh - toLow); } function EtherealShadow({ color = 'var(--lime)', animationScale = 60, animationSpeed = 35, noiseOpacity = 0.08, noiseScale = 1.4, opacity = 0.22, fixed = false }) { const reactId = React.useId(); const id = `ether-${reactId.replace(/:/g, '')}`; const feColorMatrixRef = React.useRef(null); const feTurbRef = React.useRef(null); const animationEnabled = animationScale > 0; const displacementScale = mapRange(animationScale, 1, 100, 20, 100); const animationDuration = mapRange(animationSpeed, 1, 100, 1000, 50); React.useEffect(() => { if (!animationEnabled) return; let raf; const start = performance.now(); const cycleSec = animationDuration / 25; const baseFreqX = mapRange(animationScale, 0, 100, 0.001, 0.0005); const baseFreqY = mapRange(animationScale, 0, 100, 0.004, 0.002); const tick = (now) => { const elapsed = (now - start) / 1000; const v = elapsed / cycleSec * 360 % 360; if (feColorMatrixRef.current) { feColorMatrixRef.current.setAttribute('values', String(v)); } if (feTurbRef.current) { // Wave-like motion: oscillate baseFrequency to make the shape breathe const wave = Math.sin(elapsed / (cycleSec * 0.8)) * 0.4 + 1; feTurbRef.current.setAttribute( 'baseFrequency', `${baseFreqX * wave},${baseFreqY * wave}` ); } raf = requestAnimationFrame(tick); }; raf = requestAnimationFrame(tick); return () => cancelAnimationFrame(raf); }, [animationEnabled, animationDuration, animationScale]); const wrapStyle = fixed ? { position: 'fixed', inset: 0, overflow: 'hidden', pointerEvents: 'none', zIndex: 0 } : { position: 'absolute', inset: 0, overflow: 'hidden', pointerEvents: 'none', zIndex: 0 }; return (