'use client'; import { useState } from 'react'; import { usePathname } from 'next/navigation'; /** * VYNDR 2.0 mobile tab bar (§6, Session 37). The PWA's primary navigation — * 5 tabs: Slate · Explore · Scan · Ledger · More. Scan is prominent (raised, * grade-green) because it's the core action. More opens a bottom sheet with * every secondary route. Hidden ≥768px via globals.css (.mobile-tab-bar). * * Session 57 (Phase 0): the Terminal tab is gone — that surface was fabricated * and now redirects to /dashboard. Explore (real graded-props browser) takes * its slot so the bar keeps 5 tabs. * * Shown for everyone (anon included): Slate/Explore/Scan are open routes and * this is the only mobile nav. Gated routes (Ledger, Account…) bounce through * the client AuthGate when an anon user taps them. */ type TabDef = { id: string; label: string; icon: React.ComponentType<{ color: string }>; href?: string; primary?: boolean; isSheet?: boolean; }; const TABS: TabDef[] = [ { id: 'slate', label: 'Slate', href: '/dashboard', icon: SlateIcon }, { id: 'explore', label: 'Explore', href: '/explore', icon: ExploreIcon }, { id: 'scan', label: 'Scan', href: '/scan', icon: ScanIcon, primary: true }, { id: 'ledger', label: 'Ledger', href: '/ledger', icon: LedgerIcon }, { id: 'more', label: 'More', icon: MoreIcon, isSheet: true }, ]; const MORE_ITEMS = [ { label: 'Compare', href: '/compare' }, { label: 'Tracker', href: '/tracker' }, { label: 'The Report', href: '/blog' }, { label: 'Invite Friends', href: '/invite' }, { label: 'Pricing', href: '/pricing' }, { label: 'Account', href: '/account' }, { label: 'Settings', href: '/settings/security' }, { label: 'Help & FAQ', href: '/help' }, { label: 'About', href: '/about' }, { label: 'Responsible Play', href: '/responsible-gambling' }, ]; // Auth flows own the full screen — no app chrome. // Session 59 (work-order 3.1) — '/' REMOVED from this set. Hiding the bar on // the landing left anonymous phones with ZERO navigation (the desktop links // hide <768px and the hamburger was retired in S37) — the audit's vanished- // nav-at-390px finding. The tab bar is the only mobile nav; it shows // everywhere except true auth flows. const HIDE_ON = new Set(['/login', '/signup', '/auth/callback']); function isActive(pathname: string, href?: string) { if (!href) return false; return pathname === href || pathname.startsWith(`${href}/`); } export default function BottomTabBar() { const pathname = usePathname() || '/'; const [moreOpen, setMoreOpen] = useState(false); if (HIDE_ON.has(pathname)) return null; return ( <> {/* More bottom sheet */} {moreOpen && (
setMoreOpen(false)} style={{ position: 'fixed', inset: 0, zIndex: 60, background: 'rgba(6,6,11,.6)', backdropFilter: 'blur(3px)', WebkitBackdropFilter: 'blur(3px)', display: 'flex', flexDirection: 'column', justifyContent: 'flex-end' }} >
e.stopPropagation()} className="sheet-up scanlines" style={{ background: 'var(--bg-1)', borderTop: '1px solid var(--border-hi)', borderRadius: '18px 18px 0 0', maxHeight: '82%', overflowY: 'auto', paddingBottom: 'calc(16px + env(safe-area-inset-bottom, 0px))' }} >
MORE
{/* S6 (A1 board) — global search, first item in the sheet. Same modal as ⌘K / the Nav icon (window.__search). */} {MORE_ITEMS.map((it) => ( setMoreOpen(false)} className="mono" style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', minHeight: 48, padding: '0 18px', color: isActive(pathname, it.href) ? 'var(--g-a)' : 'var(--text-0)', textDecoration: 'none', fontSize: 13, fontWeight: 700, letterSpacing: '0.06em', textTransform: 'uppercase', borderBottom: '1px solid var(--border)', }} > {it.label} ))}
)} ); } /* ── inline SVG icons (slim, no icon lib) ── */ function SlateIcon({ color }: { color: string }) { return ( ); } function ExploreIcon({ color }: { color: string }) { return ( ); } function ScanIcon({ color }: { color: string }) { return ( ); } function LedgerIcon({ color }: { color: string }) { return ( ); } function MoreIcon({ color }: { color: string }) { return ( ); }