// Facette — shared UI atoms
const { BRANCHES, WHATSAPP, BRAND_PARTNERS } = window.FacetteData;
// ──────────────────────────────────────────────────────────
// Logo
// ──────────────────────────────────────────────────────────
function FFMark({ size = 28, color = 'var(--gold)' }) {
// Reproduces the FACETTE mark: two mirrored Fs sharing a top bar.
return (
);
}
function FacetteLogo({ size = 'md', wordmarkOnly = false, vertical = false }) {
const compact = size === 'sm';
if (vertical) {
return (
);
}
return (
{!wordmarkOnly &&
}
Facette
);
}
// ──────────────────────────────────────────────────────────
// Nav / header
// ──────────────────────────────────────────────────────────
function Header({ page, setPage, branch, setBranch, openBook, isMobile }) {
return (
{!isMobile &&
}
{isMobile && (
)}
);
}
function Pageset({ items, active, onChange }) {
return (
);
}
function BranchToggle({ branch, setBranch }) {
return (
{Object.values(BRANCHES).map((b) => (
))}
);
}
// ──────────────────────────────────────────────────────────
// Trust strip — sits below nav
// ──────────────────────────────────────────────────────────
function TrustStrip({ branch }) {
const b = BRANCHES[branch];
return (
★ 4.9
Fresha · 794 reviews
2019
Dubai’s first facial bar
2 branches
Jumeirah · Business Bay
);
}
// ──────────────────────────────────────────────────────────
// Brand row
// ──────────────────────────────────────────────────────────
function BrandRow() {
return (
The premium brands
behind every protocol
{BRAND_PARTNERS.map((p, i) => (
{p.name}
{p.sub}
))}
);
}
// ──────────────────────────────────────────────────────────
// Footer
// ──────────────────────────────────────────────────────────
function Footer({ setPage, branch, setBranch }) {
const b = BRANCHES[branch];
return (
);
}
function SocialIcon({ kind, href }) {
const inner = {
ig: ,
tt: ,
wa: ,
}[kind];
const C = href ? 'a' : 'div';
const props = href ? { href, target: '_blank', rel: 'noopener' } : {};
return (
);
}
// ──────────────────────────────────────────────────────────
// Flyout (WhatsApp + sticky book on desktop)
// ──────────────────────────────────────────────────────────
function Flyout({ openBook, showSticky, stickyTreatment }) {
return (
Ready when you are
{stickyTreatment || 'Book your facial'}
);
}
// ──────────────────────────────────────────────────────────
// Mobile bottom bar (inside iPhone frame)
// ──────────────────────────────────────────────────────────
function MobileBottomBar({ branch, openBook }) {
const b = BRANCHES[branch];
return (
);
}
// ──────────────────────────────────────────────────────────
// Striped image with optional real img
// ──────────────────────────────────────────────────────────
function Img({ src, alt, label, style, className }) {
if (src) {
return
;
}
return (
{label || 'Photo TBC'}
);
}
// ──────────────────────────────────────────────────────────
// Section head
// ──────────────────────────────────────────────────────────
function SectionHead({ eyebrow, title, lede, action, center }) {
return (
{eyebrow &&
{eyebrow}
}
{(lede || action) && (
{lede &&
{lede}
}
{action}
)}
);
}
// ──────────────────────────────────────────────────────────
// Price formatter
// ──────────────────────────────────────────────────────────
function formatPrice(item) {
if (item.tbc) {
return Price on request;
}
const value = item.price ?? item.priceFrom;
const showFrom = item.priceFrom != null;
return (
{showFrom && From}
{' '}{value.toLocaleString()}AED
);
}
// Export everything
Object.assign(window, {
FFMark, FacetteLogo, Header, BranchToggle, TrustStrip, BrandRow, Footer,
Flyout, MobileBottomBar, Img, SectionHead, formatPrice,
});