// Main App with hash routing + Tweaks panel
const { useState: useStateA, useEffect: useEffectA, useMemo: useMemoA } = React;
// Parse window.location.hash into a route.
// Supported shapes (any leading "#" is fine, with or without a slash):
// "" → home, no section
// "#/" → home, no section
// "#/about" → home, section "about"
// "#about" → home, section "about" (legacy)
// "#/#about" → home, section "about" (legacy)
// "#/trainer/:slug" → trainer detail
function parseHash() {
const raw = (window.location.hash || "").replace(/^#/, ""); // strip the one leading #
if (raw.startsWith("/trainer/")) {
return { route: "trainer", slug: raw.split("/")[2] || "", section: null };
}
// Tolerate /#about, /about, #about, about — anything ending with letters wins.
const m = raw.match(/([a-z]+)$/i);
return { route: "home", slug: null, section: m ? m[1].toLowerCase() : null };
}
// Smoothly scroll to a section id, accounting for the fixed nav.
function scrollToSection(id) {
if (!id) return;
const el = document.getElementById(id);
if (!el) return;
const navH = (document.querySelector(".nav") || {}).offsetHeight || 80;
const top = el.getBoundingClientRect().top + window.scrollY - (navH + 8);
window.scrollTo({ top, behavior: "smooth" });
}
function App() {
const [route, setRoute] = useStateA(parseHash());
// Track the last section we scrolled to so clicking the same link twice
// (or clicking a section while already on home) re-scrolls.
const lastScrolled = React.useRef(null);
useEffectA(() => {
const apply = () => {
const next = parseHash();
setRoute(next);
if (next.route === "home" && next.section) {
// Wait one frame so the section is in the DOM after a route change.
setTimeout(() => scrollToSection(next.section), 80);
lastScrolled.current = next.section;
} else if (next.route === "home" && !next.section) {
window.scrollTo({ top: 0, behavior: "smooth" });
lastScrolled.current = null;
}
};
// Run once for any initial hash (e.g. opening "/#pricing" directly).
apply();
window.addEventListener("hashchange", apply);
return () => window.removeEventListener("hashchange", apply);
}, []);
// Tweaks
const TWEAK_DEFAULTS = /*EDITMODE-BEGIN*/{
"accent": "#d4a437",
"headlineMode": "stencil",
"showWaFab": true,
"language": "en"
}/*EDITMODE-END*/;
const tweaks = useTweaks(TWEAK_DEFAULTS);
const [tw, setTweak] = tweaks;
const [lang, setLang] = useStateA(tw.language || "en");
useEffectA(() => { setLang(tw.language || "en"); }, [tw.language]);
const onSetLang = (l) => { setLang(l); setTweak("language", l); };
// Apply accent color
useEffectA(() => {
document.documentElement.style.setProperty("--gold", tw.accent);
// derive a slightly deeper variant
document.documentElement.style.setProperty("--gold-2", tw.accent);
document.documentElement.style.setProperty("--gold-tint", tw.accent + "20");
}, [tw.accent]);
// Apply headline mode (stencil = stroke variant on certain words; solid = filled)
useEffectA(() => {
document.body.dataset.headline = tw.headlineMode;
}, [tw.headlineMode]);
// Reveal observer (refresh on route change)
useReveal();
return (
{route.route === "home" && }
{route.route === "trainer" && }
{tw.showWaFab && }
setTweak("accent", v)}
options={["#d4a437", "#e6b647", "#d44a37", "#c9c9c9"]} />
setTweak("headlineMode", v)}
options={[{ value: "stencil", label: "Stencil" }, { value: "solid", label: "Solid" }]} />
{ setTweak("language", v); }}
options={[{ value: "mk", label: "MK" }, { value: "sq", label: "SQ" }, { value: "en", label: "EN" }]} />
setTweak("showWaFab", v)} />
);
}
ReactDOM.createRoot(document.getElementById("root")).render();