import { ReactNode } from 'react' import { useMatch } from 'react-router' import BottomNav from '../components/Layout/BottomNav' import ErrorBoundary from '../components/shared/ErrorBoundary' import MBottomNav from './components/MBottomNav' import MToastHost from './components/MToastHost' import './mobile.css' interface MobileShellProps { isPhone: boolean children: ReactNode } /** * Shell around every protected page. Below the md breakpoint it is the root * of the new mobile experience: scopes the --m-* design tokens, paints the * screen gradient, hosts the bottom dock, the sheet portal target and the * mobile toast presenter. From 768px up it renders the exact legacy wrapper * unchanged. Both branches share the div > div > children shape so pages keep * their state when the viewport crosses the breakpoint (rotation, * split-screen) instead of remounting. * * On the phone the shell deliberately owns no scroll container (#1809): the * document is the scroller, because that is the only movement iOS Safari * minimises its address bar for. Screens that want a full-viewport layout * (map screens, the trip planner) set their own height instead of borrowing * one from here. */ export default function MobileShell({ isPhone, children }: MobileShellProps) { // The trip planner (/trips/:id) is a full-screen takeover that ships its own // in-trip dock (MTripShell). The global dock must not render underneath it: // on iOS Safari a position:fixed nav paints THROUGH the trip overlay's higher // stacking context, so the wrong (global) bar shows on top of the trip screen. const inTripPlanner = useMatch('/trips/:id') if (!isPhone) { return (