// primitives.jsx — shared UI bits used across sections (Arabic / RTL). const { useEffect, useRef, useState, useMemo } = React; // IntersectionObserver-driven reveal function Reveal({ children, delay = 0, as: Tag = "div", className = "", style }) { const ref = useRef(null); const [shown, setShown] = useState(false); useEffect(() => { const el = ref.current; if (!el) return; const io = new IntersectionObserver((entries) => { entries.forEach((e) => { if (e.isIntersecting) { setTimeout(() => setShown(true), delay); io.unobserve(el); } }); }, { threshold: 0.12 }); io.observe(el); return () => io.disconnect(); }, [delay]); return ( {children} ); } function Nav() { const [scrolled, setScrolled] = useState(false); const [open, setOpen] = useState(false); useEffect(() => { const onScroll = () => setScrolled(window.scrollY > 24); window.addEventListener("scroll", onScroll, { passive: true }); return () => window.removeEventListener("scroll", onScroll); }, []); useEffect(() => { document.body.style.overflow = open ? "hidden" : ""; return () => { document.body.style.overflow = ""; }; }, [open]); const items = [ { label: "النظام", href: "#system" }, { label: "كيف نعمل", href: "#process" }, { label: "الأسعار", href: "#pricing" }, { label: "النتائج", href: "#proof" }, { label: "أسئلة شائعة", href: "#faq" }, ]; return (
Clinic Growth Ops Predictable Growth Systems
{/* Desktop nav */} {/* Mobile toggle */}
{/* Mobile menu sheet */} {open && (
{items.map((it) => ( setOpen(false)} style={{ fontSize: 22, fontFamily: "var(--f-display)", fontWeight: 500, padding: "18px 0", borderBottom: "1px solid var(--hairline)", color: "var(--text)", }}> {it.label} ))} setOpen(false)} style={{ marginTop: 24 }}> احجز مكالمة تشخيص
)}
); } function BranchMark() { return ( ); } // Arrow — flipped for RTL by default (points to the start of the line, like ←). function Arrow({ size = 14, flip = true }) { return ( ); } function SectionId({ n, label }) { return
{n}  ·  {label}
; } function Sparkline({ seed = 1, w = 120, h = 32, up = true, color }) { const pts = useMemo(() => { const rng = (s) => { let x = s; return () => (x = (x * 9301 + 49297) % 233280) / 233280; }; const r = rng(seed); const n = 24; const arr = []; let v = 0.5; for (let i = 0; i < n; i++) { v += (r() - 0.5) * 0.18 + (up ? 0.012 : -0.012); v = Math.max(0.05, Math.min(0.95, v)); arr.push(v); } return arr; }, [seed, up]); const c = color || "var(--accent)"; const path = pts.map((v, i) => { const x = (i / (pts.length - 1)) * w; const y = h - v * h; return `${i === 0 ? "M" : "L"} ${x.toFixed(1)} ${y.toFixed(1)}`; }).join(" "); const fill = `${path} L ${w} ${h} L 0 ${h} Z`; return ( ); } function CountUp({ to, suffix = "", duration = 1400, decimals = 0 }) { const ref = useRef(null); const [val, setVal] = useState(0); useEffect(() => { const el = ref.current; if (!el) return; let started = false; const io = new IntersectionObserver((es) => { es.forEach((e) => { if (e.isIntersecting && !started) { started = true; const t0 = performance.now(); const tick = (t) => { const p = Math.min(1, (t - t0) / duration); const eased = 1 - Math.pow(1 - p, 3); setVal(to * eased); if (p < 1) requestAnimationFrame(tick); }; requestAnimationFrame(tick); } }); }, { threshold: 0.4 }); io.observe(el); return () => io.disconnect(); }, [to, duration]); return ( {decimals === 0 ? Math.round(val).toLocaleString("en-US") : val.toFixed(decimals)}{suffix} ); } Object.assign(window, { Reveal, Nav, BranchMark, Arrow, SectionId, Sparkline, CountUp, });