/* V2 Header + Footer — softer, plain-language nav. Reuses cart drawer from V1 parts.jsx. */ /* Official brand mark — animated orbiting cell. Used in the header, footer and hero. */ function VCOrbitMark({ size = 44, color = 'var(--v2-accent)', opacity = 1, style = {} }) { return ( ); } /* Site search — overlay panel that finds products and pages. */ function SearchOverlayV2({ open, onClose, onNav }) { const [q, setQ] = React.useState(''); const inputRef = React.useRef(null); React.useEffect(() => { if (!open) return; setQ(''); const t = setTimeout(() => inputRef.current && inputRef.current.focus(), 40); const esc = (e) => { if (e.key === 'Escape') onClose(); }; window.addEventListener('keydown', esc); return () => { clearTimeout(t); window.removeEventListener('keydown', esc); }; }, [open]); if (!open) return null; const term = q.trim().toLowerCase(); const products = (window.VC_PRODUCTS || []).filter((p) => !term || (p.name + ' ' + (p.catLabel || '') + ' ' + (p.blurb || '')).toLowerCase().includes(term) ).slice(0, 8); const pages = [ ['Shop all products', 'compounds'], ['How it works', 'how'], ['Learn', 'insights'], ['About us', 'about'], ['Contact', 'contact'], ].filter(([t]) => !term || t.toLowerCase().includes(term)); return (
e.stopPropagation()} style={{ width: '100%', maxWidth: 640, background: '#fff', border: '1px solid var(--v2-line)', borderRadius: 20, overflow: 'hidden', boxShadow: '0 40px 90px -40px rgba(0,0,0,0.5)', }}>
setQ(e.target.value)} placeholder="Search products and pages…" style={{ flex: 1, border: 0, outline: 'none', fontSize: 16, fontFamily: 'var(--font-body-v2)', color: 'var(--v2-ink)', background: 'transparent' }} />
{products.length === 0 && pages.length === 0 && (
Nothing matched “{q}”.
)} {products.length > 0 && (
Products
)} {products.map((p) => (
{ onClose(); window.dispatchEvent(new CustomEvent('vc-open-product', { detail: p.name })); }} style={{ display: 'flex', alignItems: 'center', gap: 14, padding: '10px 20px', cursor: 'pointer' }} onMouseEnter={(e) => (e.currentTarget.style.background = 'var(--v2-cream)')} onMouseLeave={(e) => (e.currentTarget.style.background = 'transparent')}>
{p.name}
{p.catLabel}
))} {pages.length > 0 && (
Pages
)} {pages.map(([t, k]) => (
{ onClose(); onNav(k); }} style={{ padding: '10px 20px', cursor: 'pointer', fontSize: 14.5, color: 'var(--v2-ink)' }} onMouseEnter={(e) => (e.currentTarget.style.background = 'var(--v2-cream)')} onMouseLeave={(e) => (e.currentTarget.style.background = 'transparent')}>{t}
))}
); } /* Tagline line that stretches its letters to fill the wordmark width exactly. */ function TaglineLine({ text }) { return ( {text.split('').map((ch, i) => ( {ch} ))} ); } function HeaderV2({ page, onNav }) { const [cartCount, setCartCount] = React.useState(() => window.VC_CART.count()); const [cartOpen, setCartOpen] = React.useState(false); const [menuOpen, setMenuOpen] = React.useState(false); const [searchOpen, setSearchOpen] = React.useState(false); const [openMenu, setOpenMenu] = React.useState(null); // desktop dropdown const [mobileSub, setMobileSub] = React.useState(null); // mobile accordion const closeTimer = React.useRef(null); React.useEffect(() => { const onChange = (e) => setCartCount(e.detail?.count ?? window.VC_CART.count()); const onOpen = () => setCartOpen(true); window.addEventListener('vc-cart-change', onChange); window.addEventListener('vc-cart-open', onOpen); return () => { window.removeEventListener('vc-cart-change', onChange); window.removeEventListener('vc-cart-open', onOpen); }; }, []); React.useEffect(() => { window.lucide && window.lucide.createIcons(); }, [cartCount, cartOpen, menuOpen, searchOpen, page, openMenu, mobileSub]); React.useEffect(() => { setMenuOpen(false); setOpenMenu(null); setMobileSub(null); }, [page]); const items = [ { k: 'home', t: 'Home' }, { k: 'compounds', t: 'Shop', sub: [ ['All products', 'compounds'], ['Skin & Glow', 'compounds'], ['Recovery', 'compounds'], ['Metabolic reset', 'compounds'], ['Longevity', 'compounds'], ['Focus', 'compounds'], ] }, { k: 'how', t: 'How It Works', sub: [ ['The process', 'how'], ['Mixing & storage', 'how'], ['Common questions', 'how'], ] }, { k: 'insights', t: 'Learn', sub: [ ['The science', 'insights'], ['Our standards', 'insights'], ['The Journal', 'insights'], ] }, { k: 'about', t: 'About', sub: [ ['Our story', 'about'], ['Where we ship from', 'about'], ['Contact us', 'contact'], ] }, { k: 'contact', t: 'Contact', sub: [ ['Get in touch', 'contact'], ['Shipping & returns', 'policy'], ['Payment options', 'policy'], ] }, ]; const hoverOpen = (k) => { clearTimeout(closeTimer.current); setOpenMenu(k); }; const hoverClose = () => { clearTimeout(closeTimer.current); closeTimer.current = setTimeout(() => setOpenMenu(null), 140); }; return (
{/* Wordmark */}
onNav('home')} className="v2-hdr-brand" style={{ display: 'flex', alignItems: 'center', gap: 16, cursor: 'pointer', flex: '0 0 auto', minWidth: 0 }}>
My Vital Cell
{/* Nav */} {/* Actions */}
{menuOpen && (
{items.map((it) => (
{ onNav(it.k); setMenuOpen(false); }} style={{ flex: 1, display: 'block', padding: '15px 4px', cursor: 'pointer', fontFamily: 'var(--font-body-v2)', fontWeight: 600, fontSize: 16, color: page === it.k ? 'var(--v2-accent)' : 'var(--v2-ink)', textDecoration: 'none' }}>{it.t} {it.sub && ( )}
{it.sub && mobileSub === it.k && (
{it.sub.map(([label, dest]) => ( { onNav(dest); setMenuOpen(false); }} style={{ display: 'block', padding: '11px 12px', fontSize: 14.5, color: 'var(--v2-ink-2)', cursor: 'pointer', textDecoration: 'none', borderLeft: '2px solid var(--v2-line)' }}>{label} ))}
)}
))}
)} setCartOpen(false)} onNav={onNav} /> setSearchOpen(false)} onNav={onNav} />
); } function FooterV2({ onNav }) { const cols = [ { h: 'Shop', links: [['All products', 'compounds'], ['Skin & Glow', 'compounds'], ['Recovery', 'compounds'], ['Longevity', 'compounds'], ['Focus', 'compounds']] }, { h: 'Learn', links: [['How it works', 'how'], ['The science', 'insights'], ['Our standards', 'insights'], ['The Journal', 'insights'], ['About us', 'about']] }, { h: 'Support', links: [['Contact', 'contact'], ['Shipping & returns', 'policy'], ['Payment options', 'policy'], ['Get in touch', 'contact']] }, ]; return ( ); } Object.assign(window, { HeaderV2, FooterV2, VCOrbitMark, SearchOverlayV2 });