/* global React */ const { useState, useEffect, useRef, useCallback } = React; /* ============ Hook: reveal on scroll ============ */ function useReveal(options = {}) { const ref = useRef(null); 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: options.threshold ?? 0.15, rootMargin: options.rootMargin ?? "0px 0px -8% 0px" } ); io.observe(el); return () => io.disconnect(); }, []); return ref; } /* ============ Hook: count up ============ */ function useCountUp(target, duration = 1600) { const [val, setVal] = useState(0); const ref = useRef(null); const started = useRef(false); useEffect(() => { const el = ref.current; if (!el) return; const io = new IntersectionObserver((entries) => { entries.forEach((e) => { if (e.isIntersecting && !started.current) { started.current = true; const start = performance.now(); const tick = (now) => { const t = Math.min(1, (now - start) / duration); const eased = 1 - Math.pow(1 - t, 3); setVal(Math.round(eased * target)); if (t < 1) requestAnimationFrame(tick); }; requestAnimationFrame(tick); } }); }, { threshold: 0.4 }); io.observe(el); return () => io.disconnect(); }, [target, duration]); return [val, ref]; } /* ============ Icons (line, custom) ============ */ const I = { arrow: (s = 16) => ( ), check: (s = 16) => ( ), plus: (s = 16) => ( ), minus: (s = 16) => ( ), stethoscope: ( ), shield: ( ), book: ( ), pulse: ( ), clipboard: ( ), mail: ( ), pin: ( ), whatsapp: ( ), phone: ( ), clock: ( ), }; /* ============ Decorative circles SVG ============ */ function DecoCircles({ size = 600, opacity = 0.5, color = "#0d8c72" }) { return ( ); } window.SLCommon = { useReveal, useCountUp, I, DecoCircles };