/* LuxuryProperty.com — investor microsite (Palm Jumeirah + brand shell).
Cinematic dark hero · warm editorial body · feed-driven listing UX.
Reads i18n.js (LP_LOCALES) and data.js (LP_DATA). */
const { useState, useEffect, useRef, useMemo, useCallback, Fragment } = React;
const TWEAK_DEFAULTS = /*EDITMODE-BEGIN*/{
"locale": "en",
"palette": "warm-stone",
"showSampleBadges": true,
"density": "luxe",
"heroVariant": "palm"
}/*EDITMODE-END*/;
/* ────────────────── icons (hand-rolled minimal set) ─────────────────── */
const Icon = {
arrow: (p) => ,
arrowSm: (p) => ,
whatsapp: (p) => ,
phone: (p) => ,
pin: (p) => ,
close: (p) => ,
globe: (p) => ,
bed: (p) => ,
bath: (p) => ,
area: (p) => ,
check: (p) => ,
};
/* ───────────────────────── tweaks integration ───────────────────────── */
const PALETTES = {
'warm-stone': { name: 'Warm stone', swatch: ['#0E1114','#B8995E','#F7F5F1'] },
'graphite': { name: 'Graphite', swatch: ['#0B0D10','#8FA8B2','#EFEEEA'] },
'desert-bronze':{ name: 'Desert bronze', swatch: ['#1A130C','#C8843A','#F4ECDF'] },
'midnight-teal':{ name: 'Midnight teal', swatch: ['#0A1A22','#C9A24B','#EFEFE9'] },
};
function applyPalette(name) {
const p = name;
const r = document.documentElement;
if (p === 'graphite') {
r.style.setProperty('--bg', '#0B0D10');
r.style.setProperty('--surface', '#EFEEEA');
r.style.setProperty('--primary', '#8FA8B2');
r.style.setProperty('--secondary', '#13202A');
r.style.setProperty('--accent', '#D8D5CE');
r.style.setProperty('--focus', '#A6BFC9');
} else if (p === 'desert-bronze') {
r.style.setProperty('--bg', '#1A130C');
r.style.setProperty('--surface', '#F4ECDF');
r.style.setProperty('--primary', '#C8843A');
r.style.setProperty('--secondary', '#2A1E12');
r.style.setProperty('--accent', '#E8D5B8');
r.style.setProperty('--focus', '#E4A968');
} else if (p === 'midnight-teal') {
r.style.setProperty('--bg', '#0A1A22');
r.style.setProperty('--surface', '#EFEFE9');
r.style.setProperty('--primary', '#C9A24B');
r.style.setProperty('--secondary', '#0F2A36');
r.style.setProperty('--accent', '#E0DAC8');
r.style.setProperty('--focus', '#E8C25C');
} else {
// warm-stone (default per brief)
r.style.setProperty('--bg', '#0E1114');
r.style.setProperty('--surface', '#F7F5F1');
r.style.setProperty('--primary', '#B8995E');
r.style.setProperty('--secondary', '#16313F');
r.style.setProperty('--accent', '#DED1BD');
r.style.setProperty('--focus', '#D2AD68');
}
}
/* ─────────────────────────── building blocks ────────────────────────── */
function Logo({ light }) {
// Wordmark — original SVG; on dark surfaces use the white SVG asset,
// on light use a typeset placeholder until the dark SVG is traced.
if (light) {
return
;
}
return (
LuxuryProperty.com®
);
}
function Eyebrow({ children, light, className }) {
return
{children}
;
}
function Pill({ children, light, accent, className, onClick, active }) {
return (
);
}
function Btn({ kind = 'primary', light, onClick, type = 'button', children, full, sm, as: As = 'button', href, target }) {
const cls = `lp-btn lp-btn-${kind} ${light ? 'on-dark' : ''} ${full ? 'full' : ''} ${sm ? 'sm' : ''}`;
if (As === 'a') {
return {children};
}
return ;
}
function SectionHead({ eyebrow, title, sub, light, align = 'start' }) {
return (
{eyebrow ?
{eyebrow} : null}
{title}
{sub ?
{sub}
: null}
);
}
/* ─────────────────────────────── NAV ────────────────────────────────── */
function Nav({ t, locale, onLocale, onOpenViewing }) {
const [open, setOpen] = useState(false);
const [scrolled, setScrolled] = useState(false);
useEffect(() => {
const onScroll = () => setScrolled(window.scrollY > 60);
window.addEventListener('scroll', onScroll, { passive: true });
return () => window.removeEventListener('scroll', onScroll);
}, []);
return (
);
}
function LocaleSwitcher({ locale, onLocale }) {
const [open, setOpen] = useState(false);
const ref = useRef();
useEffect(() => {
const onDoc = (e) => { if (ref.current && !ref.current.contains(e.target)) setOpen(false); };
document.addEventListener('mousedown', onDoc);
return () => document.removeEventListener('mousedown', onDoc);
}, []);
return (
{open ? (
{Object.entries(window.LP_LOCALES).map(([code, l]) => (
))}
) : null}
);
}
/* ─────────────────────────────── HERO ───────────────────────────────── */
const HERO_IMAGES = {
palm: 'media/editorial-palm-district-one.webp',
skyline: 'media/editorial-dubai-skyline.webp',
villa: 'media/listing-sample-villa.webp',
about: 'media/brand-about-us-hero.webp',
};
function Hero({ t, onOpenViewing, onOpenValuation, heroVariant }) {
const img = HERO_IMAGES[heroVariant] || HERO_IMAGES.palm;
return (
{t.hero.eyebrow}
{t.hero.title}
{t.hero.sub}
{t.hero.ctaPrimary}
{t.hero.ctaSecondary}
Microsite
Palm Jumeirah
Brokerage
LuxuryProperty.com
Status
Demo build · feed-ready
);
}
/* ───────────────────────── SEARCH BAR (under hero) ────────────────────── */
function SearchBar({ t }) {
const [tab, setTab] = useState('buy');
return (
{['buy','rent','offplan'].map(k => (
))}
{t.search.hint}
);
}
function SearchField({ label, options, t }) {
return (
);
}
/* ─────────────────────────── STATS STRIP ─────────────────────────────── */
function Stats({ t }) {
return (
The brokerage
{t.stats.title}
{t.stats.sub}
{t.stats.items.map((s, i) => (
))}
);
}
/* ─────────────────────────── COMMUNITIES ─────────────────────────────── */
function Communities({ t }) {
return (
);
}
/* ─────────────────────── COMMUNITY EDITORIAL (Palm) ──────────────────── */
function CommunityStory({ t }) {
return (
);
}
/* ─────────────────────────── LISTINGS GRID ───────────────────────────── */
function Listings({ t, showSampleBadges, onOpenViewing }) {
const [filter, setFilter] = useState('all');
const items = useMemo(() => {
if (filter === 'all') return window.LP_DATA.listings;
return window.LP_DATA.listings.filter(l => l.type === filter);
}, [filter]);
return (
Live inventory
{t.listings.title}
{t.listings.sub}
{t.listings.live}
{['all','sale','rent','offplan'].map(k => (
setFilter(k)}>{t.listings.filters[k]}
))}
{items.map(l => )}
{t.misc.sampleNotice}
);
}
function ListingCard({ l, t, showSampleBadges, onOpenViewing }) {
return (
{l.tag}
{showSampleBadges ?
{t.listings.sampleBadge}
: null}
{l.communityLabel}
{l.ref}
{l.title}
{l.beds}
{l.baths}
{l.sqft}
);
}
/* ───────────────────────────── OFF-PLAN ──────────────────────────────── */
function OffPlan({ t, onBrochure }) {
const op = window.LP_DATA.offplan;
return (
Off-plan microsite preview
{t.offplan.eyebrow}
{op.name}
{op.sub}
{op.summary}
{op.facts.map((f, i) => (
- {f.k}
- {f.v}
))}
{t.offplan.gate}
{t.offplan.cta}
{t.offplan.gateNote}
);
}
/* ─────────────────────────── TEAM / AGENTS ───────────────────────────── */
function Team({ t }) {
return (
{window.LP_DATA.agents.map(a => (
{a.name}
{a.title}
{a.langs}
{a.brn}
))}
Headshots and titles confirmed against the live /team directory (2026-05). Individual publication remains permissioned.
);
}
/* ─────────────────────────── EDITORIAL ───────────────────────────────── */
function Editorial({ t }) {
return (
);
}
/* ───────────────────────── SELL / VALUATION ──────────────────────────── */
function Sell({ t, onOpenValuation }) {
return (
{t.sell.eyebrow}
{t.sell.title}
{t.sell.sub}
);
}
/* ─────────────────────────────── CONTACT ─────────────────────────────── */
function Contact({ t }) {
const [sent, setSent] = useState(false);
const [interest, setInterest] = useState(t.contact.form.opts[0]);
return (
);
}
function Field({ label, type = 'text', as }) {
if (as === 'textarea') {
return (
);
}
return (
);
}
function SelectField({ label, value, onChange, opts }) {
return (
);
}
/* ──────────────────────────────── FOOTER ─────────────────────────────── */
function Footer({ t }) {
return (
);
}
function FooterCol({ title, items }) {
return (
);
}
/* ────────────────────────── STICKY MOBILE BAR ────────────────────────── */
function MobileLeadBar({ t, onOpenViewing }) {
return (
);
}
/* ──────────────────────────────── MODALS ─────────────────────────────── */
function Modal({ open, onClose, title, sub, children, wide }) {
useEffect(() => {
if (!open) return;
const onKey = (e) => { if (e.key === 'Escape') onClose(); };
document.addEventListener('keydown', onKey);
document.body.style.overflow = 'hidden';
return () => { document.removeEventListener('keydown', onKey); document.body.style.overflow = ''; };
}, [open, onClose]);
if (!open) return null;
return (
e.stopPropagation()}>
{title}
{sub ?
{sub}
: null}
{children}
);
}
function ViewingModal({ open, onClose, t }) {
const [sent, setSent] = useState(false);
return (
{ onClose(); setTimeout(() => setSent(false), 200); }} title={t.modal.viewing.title} sub={t.modal.viewing.sub}>
{sent ? (
) : (
)}
);
}
function ValuationModal({ open, onClose, t }) {
const [sent, setSent] = useState(false);
const [timeline, setTimeline] = useState(t.modal.valuation.timelineOpts[0]);
return (
{ onClose(); setTimeout(() => setSent(false), 200); }} title={t.modal.valuation.title} sub={t.modal.valuation.sub}>
{sent ? (
) : (
)}
);
}
function BrochureModal({ open, onClose, t }) {
const [sent, setSent] = useState(false);
return (
{ onClose(); setTimeout(() => setSent(false), 200); }} title="Download the brochure" sub={t.offplan.gateNote}>
{sent ? (
Brochure sent to your inbox.
A private-client advisor will follow up within one business day.
) : (
)}
);
}
/* ────────────────────────────── APP ROOT ─────────────────────────────── */
function App() {
const [tweaks, setTweak] = useTweaks(TWEAK_DEFAULTS);
const locale = tweaks.locale;
const t = window.LP_LOCALES[locale];
useEffect(() => {
document.documentElement.setAttribute('lang', locale);
document.documentElement.setAttribute('dir', t.dir);
document.documentElement.dataset.density = tweaks.density;
}, [locale, t.dir, tweaks.density]);
useEffect(() => { applyPalette(tweaks.palette); }, [tweaks.palette]);
const [viewingOpen, setViewingOpen] = useState(false);
const [valuationOpen, setValuationOpen] = useState(false);
const [brochureOpen, setBrochureOpen] = useState(false);
return (
);
}
ReactDOM.createRoot(document.getElementById('root')).render();