// Bishop Design — shared UI primitives const { useState, useEffect, useRef } = React; // ---------- Locale ---------- const L = { en: { nav: { projects: "Projects", sectors: "Sectors", studio: "Studio", awards: "Awards", contact: "Contact" }, cta: "Start a project", eyebrow: "Bishop Design — Dubai & Miami, since 2004", heroLine1: "Revolutionary", heroLine2: "Design", heroSub: "International multi-award-winning interior design and architecture studio. Hospitality, residential, commercial and retail — across the region and on a global scale.", sectors: "Sectors", sectorIntro: "Five disciplines, one studio.", work: "Selected Work", workIntro: "A deeper look at the projects that shaped the studio.", seeAll: "See all 88 projects", awards: "Recognition", awardsIntro: "100+ awards regionally and internationally.", startBig1: "Start a project", startBig2: "with the studio.", startSub: "Tell us about your site, your operator and your brief. We typically respond within two working days.", }, ar: { nav: { projects: "المشاريع", sectors: "القطاعات", studio: "الاستوديو", awards: "الجوائز", contact: "تواصل" }, cta: "ابدأ مشروعًا", eyebrow: "بيشوب ديزاين — دبي وميامي، منذ ٢٠٠٤", heroLine1: "تصميم", heroLine2: "ثوري", heroSub: "استوديو تصميم داخلي وعمارة حائز على جوائز دولية متعددة. الضيافة، السكني، التجاري والتجزئة — في المنطقة وعلى المستوى العالمي.", sectors: "القطاعات", sectorIntro: "خمسة تخصصات، استوديو واحد.", work: "أعمال مختارة", workIntro: "نظرة أعمق على المشاريع التي شكّلت الاستوديو.", seeAll: "عرض جميع المشاريع الـ ٨٨", awards: "تقدير", awardsIntro: "أكثر من ١٠٠ جائزة إقليمية ودولية.", startBig1: "ابدأ مشروعًا", startBig2: "مع الاستوديو.", startSub: "حدثنا عن موقعك، مشغلك، ومتطلباتك. عادةً ما نرد خلال يومَي عمل.", }, }; function useLocaleStrings(locale) { return L[locale] || L.en; } // ---------- Logo ---------- function Logo({ onClick, big }) { return ( { e.preventDefault(); onClick && onClick(); }} className="logo-mark" style={{ display: "inline-block" }}> BISHOP By Paul Bishop ); } // ---------- Nav ---------- function Nav({ route, navigate, mode = "auto", locale, setLocale }) { const [scrolled, setScrolled] = useState(false); useEffect(() => { const onScroll = () => setScrolled(window.scrollY > 40); onScroll(); window.addEventListener("scroll", onScroll, { passive: true }); return () => window.removeEventListener("scroll", onScroll); }, []); const t = useLocaleStrings(locale); const darkOver = mode === "dark"; const dataMode = (darkOver && !scrolled) ? "dark" : "light"; return ( ); } // ---------- Footer ---------- function Footer({ navigate }) { return ( ); } // ---------- Cards ---------- function ProjectCard({ project, ratio = "tall", onClick }) { return ( { e.preventDefault(); onClick && onClick(project); }}>
{project.name}
{project.sector} · {project.scope}
{project.location.split(",")[0]} · {project.year}
); } function SectorTile({ sector, count, onClick }) { return ( { e.preventDefault(); onClick && onClick(sector); }}>
{sector.name}
{sector.sub}
{String(count).padStart(2, "0")} · Projects
); } // ---------- Eyebrow ---------- function Eyebrow({ children, dot, light }) { return (
{dot !== false && } {children}
); } // ---------- Marquee award strip ---------- function AwardMarquee({ items }) { const doubled = [...items, ...items]; return (
{doubled.map((a, i) => ( {a} ))}
); } // ---------- Sticky CTA (mobile) ---------- function StickyCTA({ navigate }) { return (
Bishop Design { e.preventDefault(); navigate("contact"); }} style={{ display: "inline-flex", gap: 10, alignItems: "center" }}> Start a project
); } // ---------- Disclaimer ---------- function Disclaimer() { return (
Internal prototype · imagery & metadata pending client + photographer rights confirmation
); } // ---------- Lightbox ---------- function Lightbox({ images, index, onClose, setIndex }) { useEffect(() => { const onKey = (e) => { if (e.key === "Escape") onClose(); if (e.key === "ArrowRight") setIndex((index + 1) % images.length); if (e.key === "ArrowLeft") setIndex((index - 1 + images.length) % images.length); }; window.addEventListener("keydown", onKey); return () => window.removeEventListener("keydown", onKey); }, [index, images.length]); return (
{String(index + 1).padStart(2, "0")} / {String(images.length).padStart(2, "0")}
e.stopPropagation()} />
); } // Expose Object.assign(window, { Logo, Nav, Footer, ProjectCard, SectorTile, Eyebrow, AwardMarquee, StickyCTA, Disclaimer, Lightbox, useLocaleStrings, L });