// Shared primitives and hooks const { useState, useEffect, useRef, useMemo, createContext, useContext } = React; // IntersectionObserver reveal hook function useReveal() { const ref = useRef(null); const [shown, setShown] = useState(false); useEffect(() => { if (!ref.current) return; const io = new IntersectionObserver((entries) => { entries.forEach(e => { if (e.isIntersecting) { setShown(true); io.disconnect(); } }); }, { threshold: 0.12, rootMargin: '0px 0px -60px 0px' }); io.observe(ref.current); return () => io.disconnect(); }, []); return [ref, shown]; } function FadeUp({ children, delay = 0, as = 'div', className = '', ...rest }) { const [ref, shown] = useReveal(); const Cmp = as; return ( {children} ); } // Count-up hook function useCountUp(target, duration = 1600, start = false) { const [val, setVal] = useState(0); useEffect(() => { if (!start) return; const t0 = performance.now(); let raf; const tick = (t) => { const p = Math.min(1, (t - t0) / duration); const eased = 1 - Math.pow(1 - p, 3); setVal(target * eased); if (p < 1) raf = requestAnimationFrame(tick); }; raf = requestAnimationFrame(tick); return () => cancelAnimationFrame(raf); }, [target, duration, start]); return val; } function ArrowIcon({ size = 16 }) { return ( ); } function Logo({ mode = 'light', onClick }) { return ( MentVecity ); } Object.assign(window, { FadeUp, useReveal, useCountUp, ArrowIcon, Logo });