/* global React */ function Reveal({ children, delay = 0, className = "", as = "div", style }) { const ref = React.useRef(null); React.useEffect(() => { const el = ref.current; if (!el) return; const io = new IntersectionObserver( (entries) => { entries.forEach((e) => { if (e.isIntersecting) { el.classList.add("in"); io.unobserve(el); } }); }, { threshold: 0.12 } ); io.observe(el); return () => io.disconnect(); }, []); const Tag = as; const cls = `reveal ${delay ? `reveal-delay-${delay}` : ""} ${className}`.trim(); return {children}; } function Parallax({ children, speed = 0.1 }) { const ref = React.useRef(null); React.useEffect(() => { const el = ref.current; if (!el) return; let raf; const onScroll = () => { cancelAnimationFrame(raf); raf = requestAnimationFrame(() => { const r = el.getBoundingClientRect(); const c = (r.top + r.height / 2) - window.innerHeight / 2; el.style.transform = `translate3d(0, ${-c * speed}px, 0)`; }); }; onScroll(); window.addEventListener("scroll", onScroll, { passive: true }); return () => { window.removeEventListener("scroll", onScroll); cancelAnimationFrame(raf); }; }, [speed]); return
{children}
; } function Counter({ to, suffix = "" }) { const [v, setV] = React.useState(0); const ref = React.useRef(null); React.useEffect(() => { const el = ref.current; if (!el) return; const io = new IntersectionObserver((entries) => { entries.forEach((e) => { if (e.isIntersecting) { const start = performance.now(); const dur = 1200; const tick = (t) => { const p = Math.min(1, (t - start) / dur); const eased = 1 - Math.pow(1 - p, 3); setV(Math.round(to * eased)); if (p < 1) requestAnimationFrame(tick); }; requestAnimationFrame(tick); io.unobserve(el); } }); }, { threshold: 0.5 }); io.observe(el); return () => io.disconnect(); }, [to]); return {v}{suffix}; } function ScrollProgress() { const [p, setP] = React.useState(0); React.useEffect(() => { const onScroll = () => { const h = document.documentElement; const max = h.scrollHeight - h.clientHeight; setP(max > 0 ? window.scrollY / max : 0); }; onScroll(); window.addEventListener("scroll", onScroll, { passive: true }); return () => window.removeEventListener("scroll", onScroll); }, []); return
; } function useElementScrollProgress(ref) { const [p, setP] = React.useState(0); React.useEffect(() => { const onScroll = () => { const el = ref.current; if (!el) return; const r = el.getBoundingClientRect(); const vh = window.innerHeight; const total = r.height + vh; const seen = vh - r.top; setP(Math.max(0, Math.min(1, seen / total))); }; onScroll(); window.addEventListener("scroll", onScroll, { passive: true }); return () => window.removeEventListener("scroll", onScroll); }, [ref]); return p; } window.Reveal = Reveal; window.Parallax = Parallax; window.Counter = Counter; window.ScrollProgress = ScrollProgress; window.useElementScrollProgress = useElementScrollProgress;