// app.jsx — Raffles ECC root with state + Tweaks /* global React, ReactDOM, RAFFLES_DATA */ const { useState, useRef, useEffect } = React; // Palettes — each is a {primary, accent, surfaceTone} triple applied to CSS vars. const PALETTES = { plum: { name: "Plum & gold", "--plum": "#5B2A6B", "--plum-deep": "#3E1B4C", "--plum-soft": "#F3E9F0", "--gold": "#C8932A", "--gold-soft": "#F4E6BE", "--gold-deep": "#8E6517", "--cream": "#F7EFE5", "--cream-2": "#EFE3D2", }, maroon: { name: "Maroon & ochre", "--plum": "#7A2A2F", "--plum-deep": "#561D21", "--plum-soft": "#F5E6E5", "--gold": "#C8932A", "--gold-soft": "#F4E6BE", "--gold-deep": "#8E6517", "--cream": "#F8F0E3", "--cream-2": "#EFE0C8", }, forest: { name: "Forest & honey", "--plum": "#2F5C45", "--plum-deep": "#1F3F30", "--plum-soft": "#E4EEE7", "--gold": "#D29C2B", "--gold-soft": "#F5E7BB", "--gold-deep": "#8E6517", "--cream": "#F5F0E5", "--cream-2": "#E8E0CC", }, ink: { name: "Ink & coral", "--plum": "#1F3450", "--plum-deep": "#13223B", "--plum-soft": "#E4ECF2", "--gold": "#E0673E", "--gold-soft": "#FAE0D2", "--gold-deep": "#A24322", "--cream": "#F4F0E8", "--cream-2": "#E5DECF", }, }; const TWEAK_DEFAULTS = /*EDITMODE-BEGIN*/{ "palette": "plum", "displayFont": "Source Serif 4", "showFloater": true, "showConsentChip": true, "heroMode": "split" }/*EDITMODE-END*/; function App() { const data = RAFFLES_DATA; const [t, setTweak] = useTweaks(TWEAK_DEFAULTS); const [branch, setBranch] = useState(data.branches[0]); const tourRef = useRef(null); // Apply palette tokens at runtime useEffect(() => { const p = PALETTES[t.palette] || PALETTES.plum; Object.entries(p).forEach(([k, v]) => { if (k.startsWith("--")) document.documentElement.style.setProperty(k, v); }); }, [t.palette]); useEffect(() => { document.documentElement.style.setProperty("--font-display", `"${t.displayFont}", Georgia, serif`); }, [t.displayFont]); // Apply hero "consent chip" + floater toggles via classes useEffect(() => { document.body.classList.toggle("no-chip", !t.showConsentChip); document.body.classList.toggle("no-floater", !t.showFloater); }, [t.showConsentChip, t.showFloater]); const onTourClick = () => { tourRef.current?.scrollTo ? tourRef.current.scrollTo({ top: 0 }) : tourRef.current && tourRef.current.scrollIntoView ? null : null; const el = document.getElementById("tour"); if (el) { const y = el.getBoundingClientRect().top + window.scrollY - 60; window.scrollTo({ top: y, behavior: "smooth" }); } }; const cycleBranch = () => { const i = data.branches.findIndex(b => b.id === branch.id); setBranch(data.branches[(i + 1) % data.branches.length]); }; return ( p.name)} onChange={v => setTweak("palette", v)} /> setTweak("displayFont", v)} /> setTweak("showConsentChip", v)} /> setTweak("showFloater", v)} /> ); } // Toggles for chip/floater const style = document.createElement("style"); style.textContent = ` body.no-chip .consent-chip { display: none; } body.no-floater .hero-floater { display: none; } `; document.head.appendChild(style); ReactDOM.createRoot(document.getElementById("root")).render();