// 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}
setTweak('dark', v)} />
setTweak('accent', v)} />
setTweak('fontPair', v)} />
setTweak('density', v)} />
>
);
}