/* Site-wide motion layer: scroll reveal with stagger, metric count-up, pointer-tracked hero glow. All effects are opt-out under prefers-reduced-motion. */ function Motion({ route }) { React.useEffect(() => { const reduce = window.matchMedia('(prefers-reduced-motion: reduce)').matches; if (reduce) return; const root = document.getElementById('root'); if (!root) return; const targets = []; root.querySelectorAll('section').forEach(sec => { const holder = sec.classList.contains('goat-container') ? sec : (sec.firstElementChild || sec); Array.from(holder.children).forEach(el => { if (el.dataset.rv) return; el.dataset.rv = '1'; el.classList.add('w-rv'); targets.push(el); }); }); const countUp = (el) => { const raw = el.textContent.trim(); const m = raw.match(/^([^\d]*)([\d][\d.,]*)(.*)$/); if (!m || el.dataset.counted) return; el.dataset.counted = '1'; const [, pre, num, post] = m; const decimals = (num.split('.')[1] || '').length; const end = parseFloat(num.replace(/,/g, '')); if (!isFinite(end) || end === 0) return; const grouped = num.indexOf(',') > -1; const t0 = performance.now(), dur = 1100; const tick = (t) => { const p = Math.min(1, (t - t0) / dur); const v = end * (1 - Math.pow(1 - p, 3)); let s = decimals ? v.toFixed(decimals) : Math.round(v).toString(); if (grouped) s = Number(s).toLocaleString('en-US'); el.textContent = pre + s + post; if (p < 1) requestAnimationFrame(tick); }; requestAnimationFrame(tick); }; const io = new IntersectionObserver((entries) => { entries.forEach(e => { if (!e.isIntersecting) return; const el = e.target; const sibs = Array.from(el.parentElement.children).filter(c => c.classList.contains('w-rv')); el.style.transitionDelay = Math.min(sibs.indexOf(el), 6) * 70 + 'ms'; el.classList.add('w-rv-in'); el.querySelectorAll('.goat-metric').forEach(countUp); io.unobserve(el); }); }, { rootMargin: '0px 0px -8% 0px', threshold: 0.08 }); targets.forEach(t => io.observe(t)); const reveal = (el) => { if (el.classList.contains('w-rv-in')) return; el.classList.add('w-rv-in'); el.querySelectorAll('.goat-metric').forEach(countUp); io.unobserve(el); }; requestAnimationFrame(() => targets.forEach((el, i) => { const r = el.getBoundingClientRect(); if (r.top < window.innerHeight && r.bottom > 0) { el.style.transitionDelay = Math.min(i, 6) * 70 + 'ms'; reveal(el); } })); const safety = setTimeout(() => targets.forEach(reveal), 1400); const hero = root.querySelector('section'); const onMove = (ev) => { const r = hero.getBoundingClientRect(); hero.style.setProperty('--mx', ((ev.clientX - r.left) / r.width * 100) + '%'); hero.style.setProperty('--my', ((ev.clientY - r.top) / r.height * 100) + '%'); }; if (hero) { hero.classList.add('w-pointerglow'); hero.addEventListener('pointermove', onMove); } return () => { clearTimeout(safety); io.disconnect(); if (hero) hero.removeEventListener('pointermove', onMove); }; }, [route]); return null; } Object.assign(window, { Motion });