/* global React, ReactDOM, loadConfig, applyConfig, EBV_CONFIG_KEY, EmergencyBar, Nav, Hero, Services, About, Hours, Testimonials, Gallery, Appointment, Contact, Footer, FAB, CallModal */ // ── Reveal on scroll ──────────────────────────────────────────────────────── function useRevealOnScroll(deps) { React.useEffect(() => { const io = new IntersectionObserver( (entries) => { entries.forEach((e) => { if (e.isIntersecting) { e.target.classList.add("is-in"); io.unobserve(e.target); } }); }, { threshold: 0.12, rootMargin: "0px 0px -40px 0px" } ); const t = setTimeout(() => { document.querySelectorAll(".reveal:not(.is-in)").forEach((el) => io.observe(el)); }, 50); return () => { clearTimeout(t); io.disconnect(); }; }, deps || []); } function useSmoothScroll() { React.useEffect(() => { const onClick = (e) => { const a = e.target.closest('a[href^="#"]'); if (!a) return; const id = a.getAttribute("href").slice(1); if (!id) return; const el = document.getElementById(id); if (!el) return; e.preventDefault(); const top = el.getBoundingClientRect().top + window.scrollY - 80; window.scrollTo({ top, behavior: "smooth" }); }; document.addEventListener("click", onClick); return () => document.removeEventListener("click", onClick); }, []); } // ── Live config: async load (URL ?f=slug or localStorage) ───────────────── function useLiveConfig() { const [cfg, setCfg] = React.useState(null); const slug = React.useMemo(() => window.parseConfigSlug(), []); // Initial async load React.useEffect(() => { let alive = true; window.loadConfigAsync().then((c) => { if (!alive) return; window.applyConfig(c); setCfg(c); }); return () => { alive = false; }; }, []); // Apply on changes (after initial load) React.useLayoutEffect(() => { if (cfg) window.applyConfig(cfg); }, [cfg]); // Listen for live edits from the ayarlar page (same browser, localStorage) // — but only when no slug is set, since a slug means the source of truth // is the JSON file, not localStorage. React.useEffect(() => { if (slug) return undefined; const onStorage = (e) => { if (e.key === window.EBV_CONFIG_KEY) setCfg(window.loadConfig()); }; const onCustom = () => setCfg(window.loadConfig()); const onMessage = (e) => { if (e.data?.type === "ebv-config-change") setCfg(window.loadConfig()); }; window.addEventListener("storage", onStorage); window.addEventListener("ebv-config-change", onCustom); window.addEventListener("message", onMessage); return () => { window.removeEventListener("storage", onStorage); window.removeEventListener("ebv-config-change", onCustom); window.removeEventListener("message", onMessage); }; }, [slug]); // Update document title from config React.useEffect(() => { if (cfg) document.title = `${cfg.brandName} · ${cfg.brandSub} · 7/24 Açık`; }, [cfg?.brandName, cfg?.brandSub]); return cfg; } // ── App ───────────────────────────────────────────────────────────────────── function App() { const cfg = useLiveConfig(); const [callOpen, setCallOpen] = React.useState(false); useSmoothScroll(); useRevealOnScroll([cfg?.palette, cfg?.fontPair, cfg?.density]); if (!cfg) return null; // brief splash; CSS variables already applied default const onCall = () => setCallOpen(true); return ( <>