// --------------------------------------------------------------------------- // App shell — hash router, page state, booking modal, locale switcher, Tweaks // --------------------------------------------------------------------------- const { useState: useStateA, useEffect: useEffectA, useCallback: useCallbackA } = React; const PAGES = ['home', 'treatments', 'doctors', 'about', 'contact']; const TWEAK_DEFAULTS = /*EDITMODE-BEGIN*/{ "palette": "luce", "displayFont": "Cormorant Garamond", "showVerifyBadges": true }/*EDITMODE-END*/; const PALETTE_PRESETS = { luce: { '--gold': '#B8995A', '--gold-deep': '#8C7137', '--mist': '#EFEAE3', '--blush': '#E8D9CF', '--line': '#E4DED5', '--porcelain': '#FAF8F5' }, ivory: { '--gold': '#9A8348', '--gold-deep': '#6E5C2E', '--mist': '#F2EEE6', '--blush': '#E8DFD0', '--line': '#E8E2D6', '--porcelain': '#FDFBF6' }, derm: { '--gold': '#6E7860', '--gold-deep': '#4A5340', '--mist': '#EDEEEA', '--blush': '#DCE2D5', '--line': '#E0E2DC', '--porcelain': '#F8F8F4' }, noir: { '--gold': '#C2A05E', '--gold-deep': '#9A7E3F', '--mist': '#E8E2D7', '--blush': '#D9CDB8', '--line': '#D9D1BF', '--porcelain': '#F4EFE5' }, }; const FONT_OPTIONS = [ 'Cormorant Garamond', 'Fraunces', 'Playfair Display', 'Libre Caslon Text', ]; function App() { const [page, setPage] = useStateA('home'); const [pageParams, setPageParams] = useStateA({}); const [bookingOpen, setBookingOpen] = useStateA(false); const [bookingPrefill, setBookingPrefill] = useStateA(null); const [locale, setLocale] = useStateA('EN'); const tweaks = window.useTweaks ? window.useTweaks(TWEAK_DEFAULTS) : [TWEAK_DEFAULTS, () => {}]; const t = tweaks[0]; const setTweak = tweaks[1]; // Apply palette tweak useEffectA(() => { const preset = PALETTE_PRESETS[t.palette] || PALETTE_PRESETS.luce; Object.entries(preset).forEach(([k, v]) => document.documentElement.style.setProperty(k, v)); }, [t.palette]); // Load + apply font tweak useEffectA(() => { if (!t.displayFont) return; const family = t.displayFont; const id = 'font-' + family.replace(/\s+/g, '-'); if (!document.getElementById(id)) { const link = document.createElement('link'); link.id = id; link.rel = 'stylesheet'; const url = 'https://fonts.googleapis.com/css2?family=' + family.replace(/\s+/g, '+') + ':ital,wght@0,300;0,400;0,500;0,600;1,300;1,400;1,500&display=swap'; link.href = url; document.head.appendChild(link); } document.documentElement.style.setProperty('--serif-font', `"${family}", serif`); // Patch the serif rule via inline style on body const styleId = '__display-font-override'; let styleEl = document.getElementById(styleId); if (!styleEl) { styleEl = document.createElement('style'); styleEl.id = styleId; document.head.appendChild(styleEl); } styleEl.textContent = `.serif, h1, h2, h3, h4 { font-family: "${family}", serif !important; }`; }, [t.displayFont]); // Locale → RTL toggle useEffectA(() => { if (locale === 'AR') document.body.classList.add('rtl'); else document.body.classList.remove('rtl'); }, [locale]); // Hash routing useEffectA(() => { const parseHash = () => { const raw = window.location.hash.replace(/^#/, '') || 'home'; const [path, queryStr] = raw.split('?'); const params = {}; if (queryStr) { queryStr.split('&').forEach(p => { const [k, v] = p.split('='); params[k] = decodeURIComponent(v || ''); }); } const target = PAGES.includes(path) ? path : 'home'; setPage(target); setPageParams(params); window.scrollTo({ top: 0, behavior: 'auto' }); }; parseHash(); window.addEventListener('hashchange', parseHash); return () => window.removeEventListener('hashchange', parseHash); }, []); const navigate = useCallbackA((p, params = {}) => { const qs = Object.entries(params).map(([k, v]) => k + '=' + encodeURIComponent(v)).join('&'); window.location.hash = '#' + p + (qs ? '?' + qs : ''); }, []); const openBooking = (prefill = null) => { setBookingPrefill(prefill); setBookingOpen(true); }; return ( <>