/* Microsite shell: topbar, hero, story, architecture, residences, amenities, location, gallery, connect, footer, sticky dock, gallery lightbox. All copy & data come from data.js. */ const { useState: useS, useEffect: useE, useRef, useMemo: useM, useCallback: useCb } = React; // ============ TOPBAR ============ function TopBar({ project, locale, setLocale, activeChapter, onCTA, onProjectSwitch }) { const [solid, setSolid] = useS(false); const L = LOCALES[locale]; useE(() => { const onScroll = () => setSolid(window.scrollY > 80); window.addEventListener("scroll", onScroll, { passive: true }); return () => window.removeEventListener("scroll", onScroll); }, []); const chapters = [ { id: "story", label: L.nav.story }, { id: "architecture", label: L.nav.architecture }, { id: "residences", label: L.nav.residences }, { id: "amenities", label: L.nav.amenities }, { id: "location", label: L.nav.location }, { id: "gallery", label: L.nav.gallery }, ]; return (
{project.name} · {project.district.split("·")[0].trim()}
{["en", "ar", "ru", "zh"].map(l => ( ))}
800 15
); } function Wordmark() { // Reproduce the wordmark via the provided SVG file as an . return Binghatti; } function WhatsAppGlyph() { return ( ); } // ============ HERO ============ function Hero({ project, locale, onCTA }) { const L = LOCALES[locale]; const [loaded, setLoaded] = useS(false); useE(() => { setLoaded(false); requestAnimationFrame(() => setLoaded(true)); }, [project.id]); const today = new Date().toLocaleDateString(locale === "ar" ? "ar-AE" : locale, { year: "numeric", month: "short", day: "numeric" }); return (
{project.type === "branded" ? "Branded residence" : "Signature collection"}

{project.name.split(" ").map((w, i, arr) => i === arr.length - 1 ? {w} : {w} )}

{project.tagline}
{project.tags.map(t => {t})}
{L.hero.scroll}
Status{L.hero.live}
District{project.district.split("·")[0].trim()}
DeveloperBinghatti Developers
Date{today}
Microsite refBG-{project.id.toUpperCase()}-2026
); } // ============ STORY ============ function StorySection({ project, locale }) { const L = LOCALES[locale]; return (
{L.chapter.num01}
Developer

{L.story.title}

{L.story.lede}
{Object.entries(L.story.meta).map(([k, v]) => (
{v[0]} {v[1]}
))}
Source attribution{L.hero.attribOfficial}

{L.story.p1}

{L.story.p2}

{L.story.p3}

"{L.story.pull}"
); } // ============ ARCHITECTURE ============ function ArchSection({ project, locale }) { const L = LOCALES[locale]; return (
{L.chapter.num02}

{L.arch.title}

{L.arch.caption} {project.rightsFlag && (
⚠ {project.rightsFlag}
)}
); } // ============ RESIDENCES ============ function ResidencesSection({ project, locale, onCTA }) { const L = LOCALES[locale]; const [activeIdx, setActive] = useS(0); // Reset on project switch useE(() => { setActive(0); }, [project.id]); const concept = project.concepts[activeIdx]; return (
{L.chapter.num03}
{L.res.title}

A concept brief — never a contractual spec.

{L.res.lede}
{project.concepts.map((c, i) => ( ))}
{/* placeholder — confirmed floor plans not yet supplied */}
{concept} · plan placeholder
Concept · {activeIdx + 1} / {project.concepts.length}

{concept}

A {concept.toLowerCase()} typology within {project.name} — final layout, area schedule, ceiling heights, finishes specification and pricing released on signed brochure request.

Net area{L.res.tbc}
Outdoor{L.res.tbc}
Aspect{L.res.tbc}
Ceiling{L.res.tbc}
Indicative price{L.res.tbc}
Handover{L.res.tbc}
{L.res.no_specs}
); } // ============ AMENITIES ============ function AmenityIcon({ kind }) { const C = "currentColor"; const sw = 1.2; switch (kind) { case 0: return ; case 1: return ; case 2: return ; case 3: return ; case 4: return ; case 5: return ; case 6: return ; case 7: return ; default: return null; } } function AmenitiesSection({ locale }) { const L = LOCALES[locale]; const cells = [L.amen.a1, L.amen.a2, L.amen.a3, L.amen.a4, L.amen.a5, L.amen.a6, L.amen.a7, L.amen.a8]; return (
{L.chapter.num04}
{L.amen.title}

Curated, by category — confirmed, by brochure.

{L.amen.lede}
{cells.map(([title, body], i) => (

{title}

{body}

Subject to confirmation
))}
); } // ============ LOCATION ============ function LocationSection({ project, locale }) { const L = LOCALES[locale]; // Stylised abstract map background — not a true Dubai map return (
{L.chapter.num05}
{L.loc.title}

A district line drawn around the project.

{L.loc.lede}
{/* Abstract waterway */} {/* Roads */} {/* District plots */} {Array.from({ length: 16 }).map((_, i) => { const x = 40 + (i % 4) * 80; const y = 40 + Math.floor(i / 4) * 80; const w = 30 + Math.random() * 30; const h = 30 + Math.random() * 30; return ; })} {/* Nearby labels */} DOWNTOWN BUSINESS BAY CREEK DIFC
{L.loc.pin} {project.district.split("·")[0].trim()}
{L.loc.nearby}
{NEARBY.map((d) => (
{d.name}
~ {d.mins} min
))}
Source notes
Map is illustrative — not a Binghatti site plan and not to scale. Final coordinates, building number and drive times are confirmed against the signed brochure and current traffic conditions. Address on file: Sheikh Zayed Road, Dubai, UAE — 341186 (exact building / floor TBC).
); } // ============ GALLERY ============ function GallerySection({ locale, onOpen }) { const L = LOCALES[locale]; const tiles = ["t-a", "t-b", "t-c", "t-d", "t-e", "t-f"]; return ( ); } function Lightbox({ idx, onClose, onPrev, onNext }) { useE(() => { const onKey = (e) => { if (e.key === "Escape") onClose(); if (e.key === "ArrowLeft") onPrev(); if (e.key === "ArrowRight") onNext(); }; window.addEventListener("keydown", onKey); return () => window.removeEventListener("keydown", onKey); }, [onClose, onPrev, onNext]); const g = GALLERY[idx]; return (
{g.cap} e.stopPropagation()}/>
{idx + 1} / {GALLERY.length} · {g.cap}
); } // ============ CONNECT ============ function ConnectSection({ locale, onCTA }) { const L = LOCALES[locale]; const cards = [ [...L.conn.c1, "register"], [...L.conn.c2, "viewing"], [...L.conn.c3, "brochure"], [...L.conn.c4, "broker"], ]; return (
{L.chapter.num07}

{L.conn.title}

{L.conn.sub}

{L.cta.whatsapp} +971 800 15
{REGIONS.map(r => (
{r.flag} {r.desk}
{r.line}
{r.email}
))}
{cards.map(([num, title, sub, act]) => ( ))}
); } // ============ FOOTER ============ function Footer({ project, locale, onProjectSwitch }) { const L = LOCALES[locale]; return ( ); } // ============ STICKY DOCK ============ function CTADock({ project, locale, onCTA, hidden }) { const [show, setShow] = useS(false); if (hidden) return null; const L = LOCALES[locale]; useE(() => { const onScroll = () => { const sh = window.scrollY; const vh = window.innerHeight; const dh = document.body.scrollHeight; // show after hero, hide near footer setShow(sh > vh * 0.9 && sh + vh < dh - 200); }; window.addEventListener("scroll", onScroll, { passive: true }); onScroll(); return () => window.removeEventListener("scroll", onScroll); }, []); return (
{project.name} · enquiry
); } // ============ SIDE RAIL ============ function SideRail({ activeChapter, locale }) { const L = LOCALES[locale]; const chapters = [ ["story", L.nav.story], ["architecture", L.nav.architecture], ["residences", L.nav.residences], ["amenities", L.nav.amenities], ["location", L.nav.location], ["gallery", L.nav.gallery], ["connect", L.nav.connect], ]; return ( ); } // ============ TWEAKS ============ const TWEAK_DEFAULTS = /*EDITMODE-BEGIN*/{ "accent": "#00A7C8", "accentLabel": "Cyan", "showDock": true, "showFab": true, "showSideRail": true, "heroOverlay": 0.55, "loremMode": false, "darkSurface": false }/*EDITMODE-END*/; function MicrositeTweaks({ t, setTweak, projectId, setProjectId, locale, setLocale }) { const { TweaksPanel, TweakSection, TweakRadio, TweakColor, TweakToggle, TweakSlider, TweakSelect } = window; if (!TweaksPanel) return null; return ( PROJECTS[id].name} onChange={(v) => setProjectId(v)} /> setLocale(v)} /> setTweak("accent", v)} /> setTweak("showDock", v)} /> setTweak("showFab", v)} /> setTweak("showSideRail", v)} /> setTweak("darkSurface", v)} /> setTweak("heroOverlay", v)} /> ); } // ============ APP ============ function App() { const [projectId, setProjectId] = useS("etherea"); const [locale, setLocale] = useS("en"); const [activeChapter, setActiveChapter] = useS("story"); const [modal, setModal] = useS(null); const [lightboxIdx, setLightboxIdx] = useS(null); const [projectPickerOpen, setPickerOpen] = useS(false); const useTweaks = window.useTweaks || ((d) => [d, () => {}]); const [t, setTweak] = useTweaks(TWEAK_DEFAULTS); const project = PROJECTS[projectId]; // Push accent + surface tweaks to CSS vars useE(() => { const r = document.documentElement.style; r.setProperty("--primary", t.accent); r.setProperty("--hero-overlay", String(t.heroOverlay)); // Compute a deeper hover variant by appending an alpha sample; simple: same if (t.darkSurface) { r.setProperty("--bg", "#0E1318"); r.setProperty("--bg-deep", "#161B22"); r.setProperty("--surface", "#181E25"); r.setProperty("--text", "#F0EEE8"); r.setProperty("--text-soft", "#C8C6C0"); r.setProperty("--muted", "#9097A0"); r.setProperty("--muted-2", "#6A7079"); r.setProperty("--border", "#272D34"); r.setProperty("--border-soft", "#1F252C"); } else { ["--bg","--bg-deep","--surface","--text","--text-soft","--muted","--muted-2","--border","--border-soft"].forEach(v => r.removeProperty(v)); } }, [t.accent, t.darkSurface]); // Set RTL on body for arabic useE(() => { document.body.setAttribute("data-locale", locale); document.documentElement.lang = locale; document.documentElement.dir = locale === "ar" ? "rtl" : "ltr"; }, [locale]); // Scrollspy useE(() => { const ids = ["story", "architecture", "residences", "amenities", "location", "gallery", "connect"]; const observer = new IntersectionObserver( (entries) => { const visible = entries.filter(e => e.isIntersecting).sort((a,b) => b.intersectionRatio - a.intersectionRatio); if (visible[0]) setActiveChapter(visible[0].target.id); }, { rootMargin: "-30% 0px -55% 0px", threshold: [0, 0.25, 0.5, 0.75, 1] } ); ids.forEach(id => { const el = document.getElementById(id); if (el) observer.observe(el); }); return () => observer.disconnect(); }, [projectId]); // Fade-in observer useE(() => { const obs = new IntersectionObserver((entries) => { entries.forEach(e => { if (e.isIntersecting) e.target.classList.add("seen"); }); }, { threshold: 0.12 }); document.querySelectorAll(".fade-in").forEach(el => obs.observe(el)); return () => obs.disconnect(); }, [projectId, locale]); // Scroll-to-top on project switch useE(() => { window.scrollTo({ top: 0, behavior: "smooth" }); }, [projectId]); const onCTA = useCb((kind) => setModal(kind), []); const onOpenLB = useCb((i) => setLightboxIdx(i), []); // Listen for tweak panel project / locale / accent updates useE(() => { const fn = (e) => { if (!e?.data) return; if (e.data.type === "__bg_set_project" && PROJECTS[e.data.id]) setProjectId(e.data.id); if (e.data.type === "__bg_set_locale") setLocale(e.data.locale); }; window.addEventListener("message", fn); return () => window.removeEventListener("message", fn); }, []); return ( <> setPickerOpen(true)} /> {t.showSideRail && }