// ============ Icons ============ const Icon = ({ name, size = 22, stroke = "currentColor", sw = 1.6, style }) => { const c = { width:size, height:size, viewBox:"0 0 24 24", fill:"none", stroke, strokeWidth:sw, strokeLinecap:"round", strokeLinejoin:"round", style }; switch(name){ case "bolt": return (); case "chat": return (); case "calendar": return (); case "clock": return (); case "whatsapp": return (); case "instagram": return (); case "spark": return (); case "shield": return (); case "heart": return (); case "target": return (); case "brain": return (); case "plug": return (); case "graph": return (); case "play": return (); case "arrow": return (); case "check": return (); case "plus": return (); case "mail": return (); case "pin": return (); case "phone": return (); case "globe": return (); case "users": return (); case "star": return (); case "infinity": return (); case "logo": return ( ); default: return null; } }; // ============ Reveal hook (IntersectionObserver) ============ function useRevealRoot() { React.useEffect(() => { const els = Array.from(document.querySelectorAll("[data-reveal]")); const io = new IntersectionObserver((entries) => { entries.forEach(e => { if (e.isIntersecting){ e.target.classList.add("in"); io.unobserve(e.target);} }); }, { threshold: 0.12, rootMargin: "0px 0px -8% 0px" }); els.forEach(el => io.observe(el)); return () => io.disconnect(); }); } // ============ Lightweight smooth scroll (Lenis-style) ============ function useSmoothScroll() { React.useEffect(() => { if (window.matchMedia("(prefers-reduced-motion: reduce)").matches) return; let target = window.scrollY, current = window.scrollY, raf; let active = false; const ease = 0.085; const onWheel = (e) => { // let inner scrollers handle their own wheel if (e.target.closest("[data-native-scroll]")) return; e.preventDefault(); target = Math.max(0, Math.min(target + e.deltaY, document.body.scrollHeight - window.innerHeight)); if (!active){ active = true; loop(); } }; const loop = () => { current += (target - current) * ease; if (Math.abs(target - current) < 0.4){ current = target; active = false; window.scrollTo(0, current); return; } window.scrollTo(0, current); raf = requestAnimationFrame(loop); }; const onResize = () => { target = current = window.scrollY; }; window.addEventListener("wheel", onWheel, { passive:false }); window.addEventListener("resize", onResize); return () => { window.removeEventListener("wheel", onWheel); window.removeEventListener("resize", onResize); cancelAnimationFrame(raf); }; }, []); } // parallax helper: returns ref; element translates on scroll function useParallax(speed = 0.15) { const ref = React.useRef(null); React.useEffect(() => { let raf; const update = () => { const el = ref.current; if (!el) return; const r = el.getBoundingClientRect(); const center = r.top + r.height/2 - window.innerHeight/2; el.style.transform = `translateY(${center * -speed}px)`; raf = requestAnimationFrame(update); }; raf = requestAnimationFrame(update); return () => cancelAnimationFrame(raf); }, [speed]); return ref; } Object.assign(window, { Icon, useRevealRoot, useSmoothScroll, useParallax });