// Marketing site — shared helpers (scroll reveal, count-up, marquee, icons, logo). const { useState, useEffect, useRef } = React; const EASE = "cubic-bezier(.16,1,.3,1)"; const reduceMo = () => window.matchMedia("(prefers-reduced-motion: reduce)").matches; function Icon({ name, size = 18, style = {} }) { return ; } /** Reveal: staggered fade + rise. Fires on mount via a short timer so it is reliable in every environment (offscreen capture iframes throttle IO/scroll). Real-browser feel: sections fade up in sequence as the page settles. */ function Reveal({ children, delay = 0, y = 26, style = {}, as = "div" }) { const [inv, setInv] = useState(false); useEffect(() => { const t = setTimeout(() => setInv(true), reduceMo() ? 0 : delay + 60); return () => clearTimeout(t); }, []); const El = as; return ( {children} ); } /** Kinetic headline: each line masked, slides up in sequence on mount. */ function Kinetic({ lines, style = {}, lineStyle = {}, baseDelay = 120, step = 90 }) { const [go, setGo] = useState(false); useEffect(() => { const t = setTimeout(() => setGo(true), 60); return () => clearTimeout(t); }, []); const reduce = reduceMo(); return (

{lines.map((ln, i) => ( {ln} ))}

); } /** Count up to value when scrolled into view. */ function Stat({ value, suffix = "", label, accent = false }) { const ref = useRef(null); const [n, setN] = useState(0); useEffect(() => { if (reduceMo()) { setN(value); return; } let raf, start; const t = setTimeout(() => { const tick = (ts) => { if (start == null) start = ts; const p = Math.min(1, (ts - start) / 1100); setN(value * (1 - Math.pow(1 - p, 3))); if (p < 1) raf = requestAnimationFrame(tick); }; raf = requestAnimationFrame(tick); }, 280); return () => { clearTimeout(t); cancelAnimationFrame(raf); }; }, [value]); return (
{Math.round(n).toLocaleString()}{suffix} {label}
); } /** Infinite horizontal marquee band. */ function Marquee({ items, sep = "✺", reverse = false, style = {}, fontSize = "clamp(22px,3.5vw,51px)" }) { const run = items.concat(items); return (
{run.map((it, i) => ( {it} {sep} ))}
); } function Logo({ size = 19, light = false }) { return ( G THE GANGSA ); } Object.assign(window, { Icon, Reveal, Kinetic, Stat, Marquee, Logo });