/* helpers.jsx — shared primitives: Placeholder, line Icons, useReveal */ const { useState, useEffect, useRef, useCallback } = React; /* Reveal-on-scroll: adds .in when element enters viewport. Uses a scroll/resize check (robust across iframes & offscreen previews). */ function useReveal() { useEffect(() => { document.documentElement.classList.add("js-reveal"); let raf = 0; const check = () => { raf = 0; const vh = window.innerHeight || document.documentElement.clientHeight; document.querySelectorAll(".reveal:not(.in)").forEach((el) => { const r = el.getBoundingClientRect(); // reveal once the element's top crosses ~88% of the viewport if (r.top < vh * 0.88 && r.bottom > 0) el.classList.add("in"); }); }; const onScroll = () => { if (!raf) raf = requestAnimationFrame(check); }; // initial passes (let layout + fonts settle) check(); requestAnimationFrame(check); const t1 = setTimeout(check, 250); window.addEventListener("scroll", onScroll, { passive: true }); window.addEventListener("resize", onScroll, { passive: true }); return () => { window.removeEventListener("scroll", onScroll); window.removeEventListener("resize", onScroll); clearTimeout(t1); if (raf) cancelAnimationFrame(raf); }; }, []); } /* Marked image placeholder */ function Placeholder({ label, sub, dark, style, className = "" }) { return (
[ {label} ] {sub && <>
{sub}}
); } /* Minimal stroke icons (line-art, medical-tech) */ function Icon({ name, size = 26, stroke = 1.6 }) { const common = { width: size, height: size, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: stroke, strokeLinecap: "round", strokeLinejoin: "round", }; const paths = { scan: <>, layers: <>, cpu: <>, target: <>, shield: <>, pulse: <>, tooth: <>, chart: <>, award: <>, book: <>, globe: <>, arrow: , check: , play: , plus: , quote: <>, phone: , mail: <>, pin: <>, instagram: <>, card: <>, search: <>, }; return ; } Object.assign(window, { useReveal, Placeholder, Icon });