import type { CSSProperties, ReactNode } from "react"; import { Link, NavLink, Outlet, useLocation } from "react-router"; import { GameClock } from "../clock/GameClock"; import { SCREENS, type ScreenPath } from "../routes"; import { Icon } from "../ui"; import { useTrackScreen } from "./lastScreen"; import { MapBackdrop } from "./MapBackdrop"; import { panelIsWide, PanelHost } from "./PanelHost"; export interface LayoutProps { /** Shown at the top right. Credits arrive with the player state (Step 8.5). */ credits?: ReactNode; clock?: ReactNode; /** Shown under the header, e.g. the offline notice. */ banner?: ReactNode; /** Attention badge counts per tab (decision 0.5.49). */ attention?: Partial>; } /** * The shell (CR-029): the map behind everything, a floating glass header with * credits and game clock, the current screen in a panel, and icon navigation: * a bottom bar on phones, a floating rail from `md`. */ export function Layout({ credits = "—", clock = , banner, attention = {}, }: LayoutProps) { useTrackScreen(); const { pathname } = useLocation(); const panelWidth = panelIsWide(pathname) ? "45rem" : "26.25rem"; return (
{/* The wordmark only fits beside the chips from 640 px; on phones the mark alone. */}
DEADHEAD
{clock} {credits}
{banner && (
{banner}
)}
); } function NavTab({ path, label, icon, badge, }: { path: ScreenPath; label: string; icon: (typeof SCREENS)[number]["icon"]; badge: number; }) { return ( `relative flex min-w-0 flex-1 flex-col items-center justify-center gap-0.5 text-xs font-medium transition-colors md:flex-none md:py-2 ${ isActive ? "text-action" : "text-ink-300 hover:text-ink-50" }` } > {badge > 0 && ( )} {label} ); } /** The game's mark: an orbit ring around an arrowhead. */ function LogoMark() { return ( ); }