/* ui.jsx — shared primitives. Exports to window. Loaded first. */ const { useState, useEffect, useRef, useContext, createContext, useMemo, useCallback } = React; /* design-system components (from the loaded bundle) */ const DS = window.OLIVEARCHDesignSystem_cfb58f || {}; const { Badge } = DS; /* ---- language context ---------------------------------------------------- */ const LangContext = createContext("en"); const useLang = () => useContext(LangContext); /* localized-text helper bound to current lang */ const useT = () => { const l = useLang(); return (v) => window.t(v, l); }; /* ---- icon set (Lucide-style, 1.6px stroke, currentColor) ----------------- */ const ICONS = { "arrow-right": , "arrow-up-right": , plus: , menu: , x: , phone: , mail: <>, "map-pin": <>, globe: <>, filter: , grid: <>, ring: <>, "chevron-down": , "chevron-left": <>, images: <>, "chevron-right": , target: <>, shield: , "shield-check": <>, users: <>, clock: <>, compass: <>, medal: <>, "trending-up": , layers: , cpu: <>, "hard-hat": , ruler: , building: <>, hospital: <>, trees: , sofa: , flame: , leaf: , quote: , check: , "badge-check": <>, drafting: <>, sparkle: , "chevrons-right": , handshake: , award: <>, }; function Icon({ name, size = 20, stroke = 1.6, style, className }) { return ( ); } /* ---- reveal on scroll ---------------------------------------------------- */ function Reveal({ children, as = "div", delay, className = "", style, ...rest }) { const ref = useRef(null); useEffect(() => { const el = ref.current; if (!el) return; if (!("IntersectionObserver" in window)) { el.classList.add("is-in"); return; } const io = new IntersectionObserver(([e]) => { if (e.isIntersecting) { el.classList.add("is-in"); io.disconnect(); } }, { threshold: 0.14, rootMargin: "0px 0px -6% 0px" }); io.observe(el); return () => io.disconnect(); }, []); const Tag = as; return {children}; } /* ---- overline + section header ------------------------------------------- */ function Overline({ children, style }) { return {children}; } /* ---- masked line-by-line headline reveal (signature load motion) --------- */ function Headline({ text, as = "h1", className = "", style, startDelay = 140, lineDelay = 95, onScroll = false }) { const ref = useRef(null); const lines = String(text).split("\n"); useEffect(() => { const el = ref.current; if (!el) return; if (window.matchMedia("(prefers-reduced-motion:reduce)").matches) { el.classList.add("in"); return; } if (onScroll && "IntersectionObserver" in window) { const io = new IntersectionObserver(([e]) => { if (e.isIntersecting) { el.classList.add("in"); io.disconnect(); } }, { threshold: 0.2, rootMargin: "0px 0px -8% 0px" }); io.observe(el); return () => io.disconnect(); } const t = setTimeout(() => el.classList.add("in"), startDelay); return () => clearTimeout(t); }, [text]); const Tag = as; return ( {lines.map((ln, i) => ( {ln || "\u00a0"} ))} ); } function SectionHead({ overline, title, lead, align = "left", maxWidth }) { return (
{overline} {lead &&

{lead}

}
); } /* ---- number count-up ----------------------------------------------------- */ function CountUp({ value, prefix = "", suffix = "", duration = 1700 }) { const [n, setN] = useState(0); const ref = useRef(null); useEffect(() => { const el = ref.current; if (!el) return; const io = new IntersectionObserver(([e]) => { if (e.isIntersecting) { const start = performance.now(); const step = (t) => { const p = Math.min(1, (t - start) / duration); setN(Math.round(value * (1 - Math.pow(1 - p, 3)))); if (p < 1) requestAnimationFrame(step); }; requestAnimationFrame(step); io.disconnect(); } }, { threshold: 0.5 }); io.observe(el); return () => io.disconnect(); }, [value]); return {prefix}{n.toLocaleString("en-IN")}{suffix}; } /* ---- primary CTA button (DS-styled) -------------------------------------- */ function Cta({ children, onClick, variant = "primary", size = "lg", icon = "arrow-right", type }) { const [h, setH] = useState(false); const styles = { primary: { background: h ? "var(--olv-gold-champagne)" : "var(--olv-gold)", color: "var(--olv-black)", border: "1px solid transparent" }, outline: { background: h ? "rgba(212,175,55,.08)" : "transparent", color: "var(--olv-gold)", border: "1px solid var(--border-gold-strong)" }, secondary: { background: "var(--surface-card-elevated)", color: h ? "var(--olv-gold-champagne)" : "var(--text-heading)", border: "1px solid var(--border-gold)" }, }; const H = { lg: 56, md: 48, sm: 40 }[size]; return ( ); } /* ---- link button (underline reveal, reference style) --------------------- */ function LinkBtn({ children, onClick, active, plus = true, style }) { return ( ); } /* ---- custom cursor (fine pointers) --------------------------------------- */ function CustomCursor() { useEffect(() => { const fine = window.matchMedia("(hover:hover) and (pointer:fine)").matches; if (!fine) return; const dot = document.getElementById("cDot"), ring = document.getElementById("cRing"), torch = document.getElementById("cTorch"); if (!dot || !ring) return; document.body.classList.add("cursor-on"); let mx = window.innerWidth / 2, my = window.innerHeight / 2, rx = mx, ry = my, tx = mx, ty = my, raf; const move = (e) => { mx = e.clientX; my = e.clientY; dot.style.transform = `translate(${mx}px,${my}px) translate(-50%,-50%)`; }; const loop = () => { rx += (mx - rx) * 0.18; ry += (my - ry) * 0.18; ring.style.transform = `translate(${rx}px,${ry}px) translate(-50%,-50%)`; if (torch) { tx += (mx - tx) * 0.12; ty += (my - ty) * 0.12; torch.style.transform = `translate(${tx}px,${ty}px) translate(-50%,-50%)`; } raf = requestAnimationFrame(loop); }; const over = (e) => { const t = e.target.closest("a,button,[data-cursor='grab'],input,select,textarea,label"); if (!t) { ring.style.width = "34px"; ring.style.height = "34px"; ring.style.borderColor = "var(--border-gold-strong)"; return; } if (t.getAttribute("data-cursor") === "grab") { ring.style.width = "58px"; ring.style.height = "58px"; ring.style.borderColor = "var(--olv-gold)"; } else { ring.style.width = "46px"; ring.style.height = "46px"; ring.style.borderColor = "var(--olv-gold)"; } }; window.addEventListener("mousemove", move); window.addEventListener("mouseover", over); loop(); return () => { window.removeEventListener("mousemove", move); window.removeEventListener("mouseover", over); cancelAnimationFrame(raf); document.body.classList.remove("cursor-on"); }; }, []); return null; } /* ---- scroll to a section id ---------------------------------------------- */ function scrollToId(id) { const el = document.getElementById(id); if (!el) return; const y = el.getBoundingClientRect().top + window.scrollY - 64; window.scrollTo({ top: y, behavior: "smooth" }); } Object.assign(window, { LangContext, useLang, useT, Icon, Reveal, Overline, Headline, SectionHead, CountUp, Cta, LinkBtn, CustomCursor, scrollToId, Badge });