// 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();