/* ============================================================ EDITORIAL INDEX — components + hooks ============================================================ */ const { useState, useEffect, useRef, useCallback } = React; /* scroll-driven in-view (reliable across embeds) + safety reveal */ function useInView(opts) { const ref = useRef(null); const [seen, setSeen] = useState(false); const margin = (opts && opts.margin != null) ? opts.margin : 0.92; useEffect(() => { const el = ref.current; if (!el) return; if (window.matchMedia && window.matchMedia("(prefers-reduced-motion: reduce)").matches) { setSeen(true); return; } let done = false; const check = () => { if (done) return; const r = el.getBoundingClientRect(); const vh = window.innerHeight || document.documentElement.clientHeight; if (r.top < vh * margin && r.bottom > 0) { done = true; setSeen(true); window.removeEventListener("scroll", check); window.removeEventListener("resize", check); } }; const r1 = requestAnimationFrame(check); const r2 = requestAnimationFrame(() => requestAnimationFrame(check)); window.addEventListener("scroll", check, { passive: true }); window.addEventListener("resize", check); const safety = setTimeout(() => { if (!done) { done = true; setSeen(true); } }, 2600); return () => { cancelAnimationFrame(r1); cancelAnimationFrame(r2); clearTimeout(safety); window.removeEventListener("scroll", check); window.removeEventListener("resize", check); }; }, []); return [ref, seen]; } /* reveal wrapper — stamps rv-done shortly after to hard-lock end state */ function Reveal({ children, delay = 0, as = "div", className = "", style = {}, ...rest }) { const [ref, seen] = useInView(); const [done, setDone] = useState(false); useEffect(() => { if (!seen) return; const id = setTimeout(() => setDone(true), 1400 + delay); return () => clearTimeout(id); }, [seen, delay]); const Tag = as; return ( {children} ); } /* clip-up reveal for display lines. `lines` = array of nodes/strings. each line wipes up from a mask; staggered. */ function ClipLines({ lines, className = "", style, lineStyle, stagger = 90, base = 0 }) { const [ref, seen] = useInView(); const [done, setDone] = useState(false); useEffect(() => { if (!seen) return; const id = setTimeout(() => setDone(true), 2000); return () => clearTimeout(id); }, [seen]); return (

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

); } /* count-up */ function CountUp({ end, duration = 1300, suffix = "", className = "", style }) { const [ref, seen] = useInView({ margin: 0.82 }); const [val, setVal] = useState(0); useEffect(() => { if (!seen) return; if (window.matchMedia && window.matchMedia("(prefers-reduced-motion: reduce)").matches) { setVal(end); return; } let raf, start; const tick = (t) => { if (!start) start = t; const p = Math.min((t - start) / duration, 1); setVal(Math.round((1 - Math.pow(1 - p, 3)) * end)); if (p < 1) raf = requestAnimationFrame(tick); }; raf = requestAnimationFrame(tick); const safety = setTimeout(() => setVal(end), duration + 400); return () => { cancelAnimationFrame(raf); clearTimeout(safety); }; }, [seen, end]); return {val}{suffix}; } function Prose({ text, className = "body", style }) { return (
{String(text).split("\n\n").map((p, i) =>

{p}

)}
); } /* striped placeholder */ function Placeholder({ label = "image", style, dark }) { return (
{label}
); } /* infinite marquee ticker */ function Marquee({ items, sep = "✳", className = "" }) { const run = items.concat(items, items); return ( ); } const Arw = () => ; /* true once the page is scrolled past a small threshold */ function useScrolled(threshold) { const [s, setS] = useState(false); useEffect(() => { const t = threshold || 24; const f = () => setS(window.scrollY > t); f(); window.addEventListener("scroll", f, { passive: true }); return () => window.removeEventListener("scroll", f); }, []); return s; } Object.assign(window, { useInView, Reveal, ClipLines, CountUp, Prose, Placeholder, Marquee, Arw, useScrolled });