/* global React */ const { useState, useEffect, useRef, useMemo, useContext, createContext } = React; /* ========================================================= App context — cart, lang, density, palette, navigation ========================================================= */ const AppCtx = createContext(null); const useApp = () => useContext(AppCtx); /* ========================================================= Hash routing ========================================================= */ function parseHash() { const raw = (location.hash || "#/").replace(/^#/, ""); const [path, query = ""] = raw.split("?"); const params = Object.fromEntries(new URLSearchParams(query)); const segments = path.split("/").filter(Boolean); return { path: "/" + segments.join("/"), segments, params }; } function useRoute() { const [route, setRoute] = useState(parseHash()); useEffect(() => { const on = () => { setRoute(parseHash()); window.scrollTo({ top: 0, behavior: "instant" }); }; window.addEventListener("hashchange", on); return () => window.removeEventListener("hashchange", on); }, []); return route; } function navigate(path) { location.hash = "#" + path; } /* ========================================================= Currency formatter — USD verbatim (no invention) ========================================================= */ function fmtUSD(n) { return n.toFixed(2).replace(/\.00$/, ""); } /* ========================================================= Logo / sparrow mark ========================================================= */ function SparrowMark({ size = 28, color = "currentColor", className = "" }) { return ( ); } function Logo({ size = 24, onClick }) { return ( { if (onClick) onClick(e); }}> Orient 499 ); } /* ========================================================= Navigation ========================================================= */ function Nav() { const { route, lang, setLang, cart, openCart } = useApp(); const path = route.path; const cartCount = cart.reduce((s, it) => s + it.qty, 0); const [mobOpen, setMobOpen] = useState(false); useEffect(() => { setMobOpen(false); }, [path]); const links = [ { p: "/story", en: "The Story", ar: "الحكاية" }, { p: "/fashion", en: "Fashion", ar: "الأزياء" }, { p: "/home-decor", en: "Home & Décor", ar: "المنزل والديكور" }, { p: "/sustainability", en: "Sustainability", ar: "الاستدامة" }, { p: "/journal", en: "Journal", ar: "اليوميات" }, { p: "/boutique", en: "Boutique", ar: "البوتيك" }, ]; const isActive = (p) => path === p || (p !== "/" && path.startsWith(p)); return (
{links.slice(0, 3).map((l) => ( {lang === "ar" ? l.ar : l.en} ))}
{links.slice(3).map((l) => ( {lang === "ar" ? l.ar : l.en} ))}
{mobOpen && (
{links.map((l) => ( {lang === "ar" ? l.ar : l.en} ))}
)}
); } /* ========================================================= Footer ========================================================= */ function Footer() { const { contact, social } = window.O499.contact; const c = window.O499.contact; return ( ); } /* ========================================================= Product card ========================================================= */ function ProductCard({ p, eager = false }) { return (
{p.name} {p.ooak && One of a kind} View piece
{p.type} · {p.material.split("·")[0].trim()} {p.name} ${fmtUSD(p.price)} USD
); } /* ========================================================= Cart drawer ========================================================= */ function CartDrawer() { const { cart, cartOpen, closeCart, addToCart, removeFromCart, setQty } = useApp(); const total = cart.reduce((s, it) => s + it.price * it.qty, 0); return (
); } /* ========================================================= Toast ========================================================= */ function Toast() { const { toast } = useApp(); return
{toast}
; } /* ========================================================= Section header ========================================================= */ function SectionHeader({ chapter, eyebrow, title, right }) { return (
{chapter && {chapter}} {eyebrow && {eyebrow}}

{title}

{right &&
{right}
}
); } /* ========================================================= Sparrow divider ========================================================= */ function SparrowDivider() { return (
); } /* ========================================================= Marquee strip ========================================================= */ function Marquee({ items }) { const doubled = items.concat(items); return (
{doubled.map((t, i) => {t})}
); } /* ========================================================= Placeholder (warm-striped) — for flagged missing imagery ========================================================= */ function Placeholder({ label, aspect = "4 / 5", style = {}, stockSeed, stockTags }) { // If a stockSeed is given, show a real stock photo (Lorem Picsum, // seeded → deterministic) instead of the striped placeholder, with a small // "stock stand-in" pill so it stays honest. if (stockSeed) { const ratio = (typeof aspect === "string" && aspect.includes("/")) ? aspect.split("/").map(s => parseFloat(s.trim())) : [4, 5]; const w = 1000, h = Math.round(w * (ratio[1] / ratio[0])); const src = `https://picsum.photos/seed/${encodeURIComponent(stockSeed)}/${w}/${h}`; return (
{stockTags || "Stock stand-in · to replace"}
); } return (
{label}
); } /* ========================================================= Notice (for hours/landline conflicts) ========================================================= */ function Notice({ children }) { return
{children}
; } /* Expose to other files */ Object.assign(window, { AppCtx, useApp, useRoute, navigate, parseHash, fmtUSD, SparrowMark, Logo, Nav, Footer, ProductCard, CartDrawer, SectionHeader, SparrowDivider, Marquee, Placeholder, Notice, Toast });