// ============================================================
// App shell + hash router - FreshWave Digital Studio
// ============================================================
function parseHash() {
const raw = (window.location.hash || '#/').replace(/^#/, '');
// support "/" "/work" "/sleepers" "/sleepers/catalogue" "/process" "/about" "/commission" "/contact" and "/#process"
const [path] = raw.split('#');
const clean = path === '' ? '/' : path;
return { route: clean, anchor: raw.includes('#') ? raw.split('#')[1] : null };
}
const TWEAK_DEFAULTS = /*EDITMODE-BEGIN*/{
"neon": "teal",
"glow": 1.15,
"showMarquee": true,
"marqueeSpeed": 26
}/*EDITMODE-END*/;
const NEON_HUES = { teal: '181 96% 44%', magenta: '318 94% 53%', violet: '281 91% 62%', sunset: '18 95% 56%' };
function App() {
const [{ route, anchor }, setLoc] = useState(parseHash());
const [activeProject, setActiveProject] = useState(null);
const [introKey, setIntroKey] = useState(0);
const [t, setTweak] = useTweaks(TWEAK_DEFAULTS);
useEffect(() => {
document.body.classList.add('lights-off');
return () => document.body.classList.remove('lights-off');
}, []);
// apply tweaks to CSS variables
useEffect(() => {
const root = document.documentElement;
const hue = NEON_HUES[t.neon] || NEON_HUES.teal;
root.style.setProperty('--cyan', hue);
root.style.setProperty('--primary', hue);
root.style.setProperty('--ring', hue);
root.style.setProperty('--glow', String(t.glow));
root.style.setProperty('--marquee-dur', t.marqueeSpeed + 's');
}, [t.neon, t.glow, t.marqueeSpeed]);
const replayIntro = () => {
try { sessionStorage.removeItem('fw-intro-seen'); } catch (e) {}
setIntroKey((k) => k + 1);
};
useEffect(() => {
const onHash = () => {
const loc = parseHash();
setLoc(loc);
setActiveProject(null);
if (loc.anchor) {
// let the view render, then scroll to the section
setTimeout(() => {
const el = document.getElementById(loc.anchor);
if (el) el.scrollIntoView({ behavior: 'smooth' });
}, 60);
} else {
window.scrollTo({ top: 0, behavior: 'smooth' });
}
};
window.addEventListener('hashchange', onHash);
return () => window.removeEventListener('hashchange', onHash);
}, []);
let page;
if (route === '/work') page =