// app-page.jsx — shared React app bootstrap for all non-home pages // window.PAGE_CONFIG tells us which page this is and (for detail pages) the slug function isLightHex(hex) { const h = String(hex).replace('#', ''); const x = h.length === 3 ? h.replace(/./g, c => c + c) : h.padEnd(6, '0'); const n = parseInt(x.slice(0, 6), 16); if (Number.isNaN(n)) return true; const r = (n >> 16) & 255, g = (n >> 8) & 255, b = n & 255; return r * 299 + g * 587 + b * 114 > 148000; } const ACCENTS = ['#D4A24C', '#F4D35E', '#F39137', '#E63E1F', '#E8FF5C', '#41E0B8']; const FONT_PAIRS = [ { value: 'butler', label: 'Butler' }, { value: 'brand', label: 'Brand' }, { value: 'editorial', label: 'Editorial' }, { value: 'sans', label: 'Sans' }, { value: 'technical', label: 'Mono' }, ]; const DENSITIES = ['compact', 'comfortable', 'spacious']; // All page URLs — checked first in setRoute so direct links always win const PAGE_HREFS = { home: '/', services: '/services', work: '/work', about: '/about', contact: '/contact', careers: '/contact', book: '/book', privacy: '/privacy', disclaimer: '/disclaimer', 'data-deletion': '/data-deletion', // Individual service detail pages 'service/brand-identity': '/service-brand-identity', 'service/packaging-print': '/service-packaging-print', 'service/real-estate': '/service-real-estate', 'service/political': '/service-political', 'service/performance': '/service-performance', 'service/content-video': '/service-content-video', 'service/automation': '/service-automation', 'service/web-seo': '/service-web-seo', // Individual case study pages 'case/township-launch': '/case-township-launch', 'case/mla-campaign': '/case-mla-campaign', 'case/sona-foods': '/case-sona-foods', 'case/heritage-resorts': '/case-heritage-resorts', }; function AppPage() { const [tweaks, setTweak] = useTweaks(window.TWEAK_DEFAULTS); const cfg = window.PAGE_CONFIG || {}; const getRouteFromHash = () => { const h = window.location.hash.replace(/^#\/?/, ''); return h || cfg.page || 'home'; }; const [route, setRouteState] = React.useState(getRouteFromHash); const setRoute = React.useCallback((newRoute) => { // Direct page mapping always wins — navigates to the target HTML file if (PAGE_HREFS[newRoute]) { window.location.href = PAGE_HREFS[newRoute]; return; } // Same-page sub-route (careers within contact, etc.) if (newRoute === cfg.page) { setRouteState(newRoute); window.history.pushState({ route: newRoute }, '', '#' + newRoute); return; } // Fallback: treat as a page navigation window.location.href = '/' + newRoute; }, [cfg]); // History API — back/forward support React.useEffect(() => { window.history.scrollRestoration = 'manual'; window.history.replaceState( { route: getRouteFromHash() }, '', window.location.hash || window.location.pathname ); const onPop = (e) => { const r = (e.state && e.state.route) || getRouteFromHash(); if (PAGE_HREFS[r]) { window.location.href = PAGE_HREFS[r]; return; } setRouteState(r); }; window.addEventListener('popstate', onPop); return () => window.removeEventListener('popstate', onPop); }, []); // Dismiss preloader React.useEffect(() => { const el = document.getElementById('preloader'); if (!el) return; const t = setTimeout(() => { el.classList.add('pre-done'); setTimeout(() => el.remove(), 700); }, 800); return () => clearTimeout(t); }, []); // Apply theme React.useEffect(() => { const root = document.documentElement; root.dataset.theme = tweaks.dark ? 'dark' : 'light'; root.dataset.density = tweaks.density; root.dataset.font = tweaks.fontPair; root.style.setProperty('--accent', tweaks.accent); root.style.setProperty('--accent-ink', isLightHex(tweaks.accent) ? '#0a0a0a' : '#fafafa'); }, [tweaks.dark, tweaks.density, tweaks.fontPair, tweaks.accent]); // Scroll to top on route change React.useEffect(() => { window.scrollTo({ top: 0, behavior: 'instant' }); }, [route]); // GSAP React.useEffect(() => { if (!window.initGSAPAnimations) return; const cleanup = window.initGSAPAnimations(); return () => { if (typeof cleanup === 'function') cleanup(); }; }, [route]); // Render the correct page let page; const p = cfg.page; if (p === 'services') { page = ; } else if (p === 'service-detail') { page = ; } else if (p === 'work') { page = ; } else if (p === 'case-study') { page = ; } else if (p === 'about') { page = ; } else if (p === 'contact') { if (route === 'careers') { page = ; } else { page = ; } } else if (p === 'book') { page = ; } else if (p === 'privacy') { page = ; } else if (p === 'disclaimer') { page = ; } else if (p === 'data-deletion') { page = ; } const navRoute = p === 'service-detail' ? 'services' : p === 'case-study' ? 'work' : route === 'careers' ? 'contact' : route; return ( <>
setTweak('dark', v)} />
{page}