// Facette — App shell
// Renders the desktop site + an iPhone-framed mobile mirror
const { useState, useEffect } = React;
function Site({ page, setPage, branch, setBranch, openBook, openQuiz, openLightbox, isMobile }) {
// Page header sticks
return (
{page === 'home' && (
)}
{page === 'treatments' && (
)}
{page === 'memberships' && (
)}
{page === 'locations' && (
)}
{isMobile && }
);
}
// ──────────────────────────────────────────────────────────
// Stage chrome — top bar over both viewports
// ──────────────────────────────────────────────────────────
function StageChrome({ branch, setBranch, mobileVisible, setMobileVisible, palette, setPalette, openTweaks }) {
return (
Facette · Hi-fi prototype
BRANCH
setBranch('jumeirah')}>Jumeirah
setBranch('business_bay')}>Business Bay
VIEW
setMobileVisible(!mobileVisible)}>
{mobileVisible ? 'Hide mobile' : 'Show mobile'}
Tweaks
);
}
// ──────────────────────────────────────────────────────────
// Mobile shell — wraps the Site in an iPhone frame
// ──────────────────────────────────────────────────────────
function MobileShell(props) {
return (
);
}
// ──────────────────────────────────────────────────────────
// App
// ──────────────────────────────────────────────────────────
const TWEAK_DEFAULTS = /*EDITMODE-BEGIN*/{
"palette": "luminous",
"heroLayout": "split",
"mobileVisible": true,
"showFlyout": true
}/*EDITMODE-END*/;
function App() {
const initialPage = window.__BYTESGLUE_SHOWCASE_ROUTE || 'home';
const [page, setPage] = useState(initialPage);
const [branch, setBranch] = useState('jumeirah');
const [bookOpen, setBookOpen] = useState(false);
const [bookTreatment, setBookTreatment] = useState(null);
const [quizOpen, setQuizOpen] = useState(false);
const [lightboxSrc, setLightboxSrc] = useState(null);
const [tweaks, setTweak] = useTweaks(TWEAK_DEFAULTS);
const openTweaks = () => {
window.postMessage({ type: '__activate_edit_mode' }, '*');
};
// Apply palette to root
useEffect(() => {
document.documentElement.setAttribute('data-palette', tweaks.palette);
}, [tweaks.palette]);
// Reset page scroll when changing page (within each scroll container)
useEffect(() => {
document.querySelectorAll('.stage-desktop, .ios-scroll').forEach((el) => {
el.scrollTo({ top: 0, behavior: 'smooth' });
});
}, [page]);
// Sticky book CTA visibility tied to desktop scroll
const [showSticky, setShowSticky] = useState(false);
useEffect(() => {
const el = document.querySelector('.stage-desktop');
if (!el) return;
const handler = () => setShowSticky(el.scrollTop > 600);
el.addEventListener('scroll', handler);
return () => el.removeEventListener('scroll', handler);
}, []);
// Scale-to-fit: stage has a fixed design width; we scale it to viewport.
useEffect(() => {
const DESIGN_W = tweaks.mobileVisible ? 1680 : 1280;
const CHROME_H = 52;
const update = () => {
const vw = window.innerWidth;
const vh = window.innerHeight - CHROME_H;
const scale = Math.min(1, vw / DESIGN_W);
document.documentElement.style.setProperty('--stage-scale', scale);
document.documentElement.style.setProperty('--stage-w', DESIGN_W + 'px');
document.documentElement.style.setProperty('--stage-h', (vh / scale) + 'px');
};
update();
window.addEventListener('resize', update);
return () => window.removeEventListener('resize', update);
}, [tweaks.mobileVisible]);
const openBook = (treatment) => {
setBookTreatment(treatment || null);
setBookOpen(true);
};
const openQuiz = () => setQuizOpen(true);
const openLightbox = (src) => setLightboxSrc(src);
const siteProps = {
page, setPage, branch, setBranch,
openBook, openQuiz, openLightbox,
};
return (
setTweak('mobileVisible', v)}
palette={tweaks.palette}
setPalette={(p) => setTweak('palette', p)}
openTweaks={openTweaks}
/>
{tweaks.showFlyout && (
)}
{tweaks.mobileVisible && (
)}
{/* Modals */}
{bookOpen && (
setBookOpen(false)}
onBranchChange={setBranch}
/>
)}
{quizOpen && (
setQuizOpen(false)}
onBook={(treatment) => {
setQuizOpen(false);
openBook(treatment);
}}
/>
)}
{lightboxSrc && (
setLightboxSrc(null)} />
)}
{/* Tweaks panel — self-manages open state */}
setTweak('palette', v)}
options={[
{ value: 'luminous', label: 'Luminous' },
{ value: 'clinical', label: 'Clinical' },
{ value: 'boutique', label: 'Boutique' },
]}
/>
setTweak('mobileVisible', v)}
/>
setTweak('showFlyout', v)}
/>
openBook()} />
setQuizOpen(true)} />
);
}
ReactDOM.createRoot(document.getElementById('root')).render( );