# In-game shell — navigation model This doc covers the chrome that wraps every in-game view: the responsive layout shell, the active-view router built on SvelteKit's file-system routes, the sidebar with three tools and its state-preservation rule, and the mobile bottom-tabs. The user-facing spec — view list, breakpoint diagrams, history-mode plans — lives in [`../PLAN.md`](../PLAN.md), section `Information Architecture and Navigation`. This doc is the source of truth for how those rules are implemented. ## Active-view model The client renders **one active view at a time**. Every active view is a SvelteKit route under `routes/games/[id]/`; the route file is a two-line wrapper that mounts the matching content component from `src/lib/active-view/.svelte`. The "view router" mentioned in the plan is the file system plus those wrappers — there is no separate dispatch component. | URL | Active view component | Phase that fills it | | ------------------------------------- | ---------------------------------------------- | ----------------------- | | `/games/:id/map` | `lib/active-view/map.svelte` | Phase 11 | | `/games/:id/table/:entity` | `lib/active-view/table.svelte` | Phase 11 / 17 / 19 / 22 | | `/games/:id/report` | `lib/active-view/report.svelte` | Phase 23 | | `/games/:id/battle/:battleId?` | `lib/active-view/battle.svelte` | Phase 27 | | `/games/:id/mail` | `lib/active-view/mail.svelte` | Phase 28 | | `/games/:id/designer/ship-class/:id?` | `lib/active-view/designer-ship-class.svelte` | Phase 17 / 18 | | `/games/:id/designer/science/:id?` | `lib/active-view/designer-science.svelte` | Phase 21 | `/games/:id` (no trailing view) redirects to `/games/:id/map`. The optional `:id?` segments on the designer routes match SvelteKit's `[[id]]` syntax — they accept both the new-draft and editing URLs; later phases read the param when wiring real content. The `entity` slug on the table route is kebab-case (`planets`, `ship-classes`, `ship-groups`, `fleets`, `sciences`, `races`); the table stub maps it to the matching `game.view.table.` i18n key. ## Sidebar tools and state preservation The desktop sidebar hosts three tools: | Tool | Component | Phase that fills it | | ---------- | -------------------------------------- | -------------------- | | Calculator | `lib/sidebar/calculator-tab.svelte` | Phase 30 | | Inspector | `lib/sidebar/inspector-tab.svelte` | Phase 13 / 19 | | Order | `lib/sidebar/order-tab.svelte` | Phase 12 / 14 | The sidebar's selected-tab state is a `$state` rune inside `lib/sidebar/sidebar.svelte`. The component is mounted by the layout at `routes/games/[id]/+layout.svelte`, and SvelteKit keeps that layout instance alive while the user navigates between child routes (`/games/:id/map` → `/games/:id/report` → …). The rune therefore survives every active-view switch automatically, with no URL coupling needed. A `?sidebar=calc|calculator|inspector|order` URL param is read once on mount and seeds the initial tab. Later phases that want to land the user on a particular tool (for example, Phase 14's first end-to-end command flow) can set it on navigation. ## Layout breakpoints Three discrete CSS modes matched to the IA section diagrams: - **≥ 1024 px (desktop)** — the sidebar sits beside the active view and is always rendered. The header view-menu trigger uses the dropdown icon (▾). Bottom-tabs and the tablet sidebar-toggle are CSS-hidden. - **768–1024 px (tablet)** — the sidebar collapses behind a click toggle in the header right corner. Tapping the toggle slides the sidebar in as a fixed overlay above the active view; a close button on the sidebar dismisses it. The full swipe-from-right gesture in the IA section is deferred to Phase 35 polish — the click toggle satisfies the "layout switches at 768 px" acceptance criterion on Phase 10. - **< 768 px (mobile)** — the sidebar is hidden entirely and the bottom-tabs row appears at the bottom of the viewport. The view-menu trigger swaps to a hamburger icon (☰) that opens the drop-down as a full-width drawer below the header. Inspector is intentionally unreachable on mobile in Phase 10. Per the IA section the mobile inspector is a bottom-sheet raised by tapping a map object, and that mechanism waits for Phase 13. ## Mobile bottom-tabs and tool overlay The bottom-tabs row is `[Map, Calc, Order, More]`. Map navigates to `/games/:id/map` and clears any tool overlay. Calc and Order navigate to `/games/:id/map` too — but they also flip the layout's `mobileTool` state to `calc` / `order`, which the layout uses to swap the active-view slot for the Calculator / Order tool component. The tool overlay only applies when the URL is `/map`. Navigating to any other view through the More drawer or the header view-menu makes the layout's derived `effectiveTool` collapse back to `map`, so the user always sees the URL's active view rather than a stale overlay. The next time the user taps a Calc or Order bottom-tab, the navigation re-routes them to `/map` and re-applies the overlay. The `More` button opens a drawer that mirrors the header view-menu content. The IA section's narrower "More" list (Mail, Battle log, Tables, History, Settings, Logout) is the polish target for Phase 35 — Phase 10 keeps a single source of truth for destinations. ## Transient map overlays Some views can push a transient overlay onto `/map` with a back affordance — for example, the ship-class designer pushes a range-preview overlay onto the map. The transient overlay clears when the user navigates to any other view via the header or the bottom-tabs. Phase 10 documents this concept but does not implement the back-stack mechanism. Phase 34 lands the back-stack alongside its first user (multi-turn projection, range circles in the ship-class designer). ## Auth gate The root `+layout.svelte` redirects `anonymous → /login` for any non-`/__debug/` path; the in-game shell inherits that gate without any extra check. When a session is revoked while the user is in the shell, the same redirect fires through the existing revocation watcher.