/* global React */ const { useState, useEffect, useRef } = React; // ============ ICONS ============ const Icon = { Book: () => , Shield: () => , Sun: () => , Crown: () => , People: () => , Link: () => , Cross: () => , Pin: () => , Clock: () => , Mail: () => , Phone: () => , Caret: () => , Play: () => , }; // ============ NAVIGATION CONFIG ============ const NAV = [ { id: 'home', label: 'Home' }, { id: 'about', label: 'About', children: [ { id: 'about-us', label: 'About Us' }, { id: 'beliefs', label: 'What We Believe' }, { id: 'leadership', label: 'Leadership' }, { id: 'new-here', label: 'New Here' }, ]}, { id: 'connect', label: 'Connect', children: [ { id: 'visit', label: 'Visit Us' }, { id: 'prayer', label: 'Prayer / Connect' }, { id: 'contact', label: 'Contact' }, ]}, { id: 'grow', label: 'Grow', children: [ { id: 'ministries', label: 'Ministries' }, { id: 'sermons', label: 'Sermons / Teaching' }, { id: 'events', label: 'Events' }, { id: 'gallery', label: 'Photo Gallery' }, ]}, { id: 'give', label: 'Give' }, ]; // flat lookup of all routes const ROUTES = (() => { const r = {}; NAV.forEach(n => { r[n.id] = { parent: n.id, label: n.label }; if (n.children) n.children.forEach(c => { r[c.id] = { parent: n.id, label: c.label, parentLabel: n.label }; }); }); return r; })(); // ============ ROUTER (hash-based) ============ function useRoute() { const [route, setRoute] = useState(() => (location.hash.slice(1) || 'home')); useEffect(() => { const onHash = () => { setRoute(location.hash.slice(1) || 'home'); window.scrollTo({ top: 0, behavior: 'instant' }); }; window.addEventListener('hashchange', onHash); return () => window.removeEventListener('hashchange', onHash); }, []); return route; } function nav(to) { location.hash = '#' + to; } // ============ NAV BAR ============ function Topbar() { return (
815 Private Road 4717, Kempner, TX 76539 Sundays · 10:00 AM
hello@brokenbarriers.church
); } function NavBar({ route }) { const parent = ROUTES[route]?.parent || 'home'; const [openMenu, setOpenMenu] = useState(null); const [mobileOpen, setMobileOpen] = useState(false); const wrapRef = useRef(null); useEffect(() => { setMobileOpen(false); }, [route]); useEffect(() => { document.body.classList.toggle('nav-locked', mobileOpen); const onKey = e => { if (e.key === 'Escape') setMobileOpen(false); }; window.addEventListener('keydown', onKey); return () => window.removeEventListener('keydown', onKey); }, [mobileOpen]); useEffect(() => { const onClick = e => { if (wrapRef.current && !wrapRef.current.contains(e.target)) setOpenMenu(null); }; document.addEventListener('click', onClick); return () => document.removeEventListener('click', onClick); }, []); return ( ); } // ============ FOOTER ============ function Footer() { return ( <> ); } // ============ SCROLL REVEAL ============ function useScrollReveal(route) { useEffect(() => { const io = new IntersectionObserver((entries) => { entries.forEach(e => { if (e.isIntersecting) { e.target.classList.add('in'); io.unobserve(e.target); } }); }, { threshold: 0.05, rootMargin: '0px 0px -10px 0px' }); const observeAll = () => { document.querySelectorAll('.reveal:not(.in)').forEach((el, i) => { if (!el.style.getPropertyValue('--reveal-delay')) { el.style.setProperty('--reveal-delay', Math.min(i * 60, 360) + 'ms'); } io.observe(el); }); }; // initial observe shortly after React paints const t1 = setTimeout(observeAll, 30); // mutation observer — pick up reveal elements added after initial paint const mo = new MutationObserver(() => observeAll()); mo.observe(document.body, { childList: true, subtree: true }); // safety net — after 4s, force-reveal anything still stuck (handles slow // scrolls, fast scrolls past, or any quirk where IO didn't fire) const t2 = setTimeout(() => { document.querySelectorAll('.reveal:not(.in)').forEach(el => el.classList.add('in')); }, 4000); return () => { clearTimeout(t1); clearTimeout(t2); mo.disconnect(); io.disconnect(); }; }, [route]); } Object.assign(window, { Icon, NAV, ROUTES, useRoute, nav, Topbar, NavBar, Footer, useScrollReveal });