// Shared UI primitives + nav + footer + reveal-on-scroll hook. const { useEffect, useRef, useState, useMemo } = React; // ----- Reveal on scroll ----- function useReveal() { useEffect(() => { const els = document.querySelectorAll( '.reveal:not(.in), .reveal-left:not(.in), .reveal-right:not(.in), .reveal-scale:not(.in)' ); const io = new IntersectionObserver((entries) => { entries.forEach((e) => { if (e.isIntersecting) { e.target.classList.add('in'); io.unobserve(e.target); } }); }, { threshold: 0.10 }); els.forEach((el) => io.observe(el)); return () => io.disconnect(); }); } // ----- Buttons ----- const PrimaryButton = ({ children, className = "", icon, ...rest }) => ( ); const GhostButton = ({ children, className = "", icon, ...rest }) => ( ); // ----- Eyebrow label ----- const Eyebrow = ({ children, className = "" }) => (
{children}
); // ----- Section heading ----- const SectionTitle = ({ kicker, title, sub, align = "left" }) => (
{kicker && {kicker}}

{title}

{sub &&

{sub}

}
); // ----- Marker placeholder (architectural) ----- const ArchPh = ({ variant = 1, label, className = "" }) => (
{/* layered architectural shapes */} {variant === 1 && ( {[...Array(20)].map((_,i)=>())} )} {variant === 2 && ( )} {variant === 3 && ( {[...Array(18)].map((_,i)=>())} )} {variant === 4 && ( )} {label && (
{label}
)}
); // ----- Logo ----- const Logo = ({ className = "", onClick }) => (
AMG Logo
AMG
Ashok Malhotra Group
); // ----- Theme toggle ----- const ThemeToggle = ({ colorMode, toggle }) => ( ); // ----- Nav ----- const NAV = [ { id: 'home', label: 'Home' }, { id: 'commercial', label: 'Commercial' }, { id: 'residential', label: 'Residential' }, { id: 'brands', label: 'Brands' }, { id: 'leadership', label: 'Leadership' }, { id: 'contact', label: 'Contact' }, ]; const Nav = ({ page, setPage, colorMode, toggleColorMode }) => { const [scrolled, setScrolled] = useState(false); const [mobileOpen, setMobileOpen] = useState(false); useEffect(() => { const onS = () => setScrolled(window.scrollY > 16); onS(); window.addEventListener('scroll', onS, { passive: true }); return () => window.removeEventListener('scroll', onS); }, []); const go = (id) => { setPage(id); setMobileOpen(false); window.scrollTo({ top: 0, behavior: 'smooth' }); }; const unscrolledCls = colorMode === 'light' ? 'py-2 bg-white/40 backdrop-blur-md border border-black/10 shadow-[0_4px_24px_rgba(0,0,0,0.10)]' : 'py-2 bg-black/30 backdrop-blur-md border border-white/10 shadow-[0_4px_32px_rgba(0,0,0,0.45)]'; return (
go('home')} />
WhatsApp
{/* mobile sheet */} {mobileOpen && (
{NAV.map((n) => ( ))} Chat on WhatsApp
)}
); }; // ----- Marquee strip ----- const Marquee = ({ items }) => (
{[...items, ...items, ...items].map((t, i) => (
{t}
))}
); // ----- Footer ----- const Footer = ({ setPage }) => { const go = (id) => { setPage(id); window.scrollTo({ top: 0, behavior: 'smooth' }); }; return ( ); }; // ----- Centered modal portal (escapes Framer Motion transform stacking context) ----- const ModalPortal = ({ onClose, children }) => { React.useEffect(() => { const prev = document.body.style.overflow; document.body.style.overflow = 'hidden'; return () => { document.body.style.overflow = prev; }; }, []); return ReactDOM.createPortal(
{ if (e.target === e.currentTarget) onClose(); }} >
{children}
, document.body ); }; // Expose Object.assign(window, { useReveal, PrimaryButton, GhostButton, Eyebrow, SectionTitle, ArchPh, Logo, Nav, Marquee, Footer, NAV, ThemeToggle, ModalPortal, });