1'use client';2import { Activity, Compass, Home, MoreHorizontal, Search } from 'lucide-react';3import Link from 'next/link';4import { usePathname } from 'next/navigation';5import { useEffect, useState } from 'react';6import { cn } from '@/lib/cn';7import { exploreNav, moreNav, primaryNav, routes } from '@/lib/site';8import { useOpenSearch } from './search-context';910/**11 * Bottom tab bar (< md): Home · Explore · Changes · Search · More. Safe-area aware; <body> reserves12 * `pb-[calc(var(--tabbar-h)+env(safe-area-inset-bottom))]` so it never covers content.13 */14export function MobileTabBar() {15 const pathname = usePathname();16 const openSearch = useOpenSearch();17 const [more, setMore] = useState(false);18 useEffect(() => setMore(false), [pathname]);19 const exploreActive = pathname.startsWith('/explore') || exploreNav.some((n) => pathname.startsWith(n.href));20 const cls = (active: boolean) => cn('flex h-full w-full flex-col items-center justify-center gap-0.5 text-[10.5px]', active ? 'text-accent' : 'text-ink-2');21 return (22 <>23 {more && (24 <div className="fixed inset-0 z-[45] bg-black/40 md:hidden" onClick={() => setMore(false)}>25 <div className="panel absolute inset-x-2 bottom-[calc(var(--tabbar-h)+env(safe-area-inset-bottom,0px)+8px)] max-h-[72vh] overflow-y-auto p-3" onClick={(e) => e.stopPropagation()}>26 <p className="eyebrow px-2 pb-1">Explore</p>27 <ul className="grid grid-cols-2 gap-0.5">28 {exploreNav.map((n) => (29 <li key={n.href}>30 <Link href={n.href} className="block px-3 py-2.5 text-[15px] text-ink-2 hover:text-ink">31 {n.label}32 </Link>33 </li>34 ))}35 </ul>36 <p className="eyebrow px-2 pt-3 pb-1">Track & about</p>37 <ul className="grid grid-cols-2 gap-0.5">38 {[...primaryNav.filter((n) => n.href !== '/changes'), ...moreNav.filter((n) => n.href !== '/developers')].map((n) => (39 <li key={n.href}>40 <Link href={n.href} className="block px-3 py-2.5 text-[15px] text-ink-2 hover:text-ink">41 {n.label}42 </Link>43 </li>44 ))}45 </ul>46 </div>47 </div>48 )}49 <nav aria-label="Primary (mobile)" className="safe-bottom fixed inset-x-0 bottom-0 z-[46] border-t border-rule bg-canvas/95 backdrop-blur-md md:hidden">50 <ul className="grid h-[var(--tabbar-h)] grid-cols-5">51 <li className="min-w-0">52 <Link href={routes.home()} aria-current={pathname === '/' ? 'page' : undefined} className={cls(pathname === '/')}>53 <Home size={21} aria-hidden strokeWidth={pathname === '/' ? 2.25 : 1.75} />54 <span className="truncate">Home</span>55 </Link>56 </li>57 <li className="min-w-0">58 <Link href={routes.explore()} aria-current={exploreActive ? 'page' : undefined} className={cls(exploreActive)}>59 <Compass size={21} aria-hidden strokeWidth={exploreActive ? 2.25 : 1.75} />60 <span className="truncate">Explore</span>61 </Link>62 </li>63 <li className="min-w-0">64 <Link href={routes.changes()} aria-current={pathname.startsWith('/changes') ? 'page' : undefined} className={cls(pathname.startsWith('/changes'))}>65 <Activity size={21} aria-hidden strokeWidth={pathname.startsWith('/changes') ? 2.25 : 1.75} />66 <span className="truncate">Changes</span>67 </Link>68 </li>69 <li className="min-w-0">70 <button type="button" onClick={openSearch} className={cls(false)}>71 <Search size={21} aria-hidden strokeWidth={1.75} />72 <span className="truncate">Search</span>73 </button>74 </li>75 <li className="min-w-0">76 <button type="button" onClick={() => setMore((m) => !m)} aria-expanded={more} className={cls(more)}>77 <MoreHorizontal size={21} aria-hidden strokeWidth={1.75} />78 <span className="truncate">More</span>79 </button>80 </li>81 </ul>82 </nav>83 </>84 );85}86