// theme-store.jsx — localStorage-backed theme + floating FAB toggle // Loaded after router.jsx, before components.jsx. const THEME_KEY = "saraslot.theme"; const THEMES = ["gold", "neon", "jade"]; function readStoredTheme() { try { const v = localStorage.getItem(THEME_KEY); return THEMES.includes(v) ? v : null; } catch (e) { return null; } } function useThemeStore(initial) { const [theme, setThemeState] = React.useState(() => readStoredTheme() || initial || "gold"); // Tracks whether the visitor has an explicit stored choice. The operator's // default_theme (from branding) is only applied when this is false, so a // returning visitor's FAB pick always wins over the operator default. const hasStored = React.useRef(readStoredTheme() !== null); // persist=true → explicit user choice (written to localStorage). // persist=false → apply operator default without claiming an explicit choice. const setTheme = React.useCallback((v, persist = true) => { if (!THEMES.includes(v)) return; setThemeState(v); if (persist) { try { localStorage.setItem(THEME_KEY, v); } catch (e) {} hasStored.current = true; } }, []); React.useEffect(() => { const onStorage = (e) => { if (e.key === THEME_KEY && THEMES.includes(e.newValue)) { setThemeState(e.newValue); hasStored.current = true; } }; window.addEventListener("storage", onStorage); return () => window.removeEventListener("storage", onStorage); }, []); return [theme, setTheme, hasStored]; } function ThemeFab({ theme, onChange }) { const [open, setOpen] = React.useState(false); const rootRef = React.useRef(null); React.useEffect(() => { if (!open) return; const onDoc = (e) => { if (rootRef.current && !rootRef.current.contains(e.target)) { setOpen(false); } }; document.addEventListener("mousedown", onDoc); document.addEventListener("touchstart", onDoc); return () => { document.removeEventListener("mousedown", onDoc); document.removeEventListener("touchstart", onDoc); }; }, [open]); const label = { gold: "G", neon: "N", jade: "J" }[theme] || "G"; return (