# Shell: App shell

Source: `apps/oshun/web/src/app/layout.tsx`, `apps/oshun/web/src/components/`

Global chrome that wraps every view in the customer surface. If a check here
fails, it fails everywhere. Walk this once per release.

## Root layout composition

The root layout (`layout.tsx`) mounts the following providers and shell
components, in this order:

1. `OshunProviders` — runtime config, experimentation, auth, query, profile sync
2. `ToastProvider` — toast queue
3. `LilithThemeBootstrap` — cream-paper / dark-paper theme selection
4. `PwaBootstrap` — service worker registration, update prompt, install prompt
5. `QueryErrorHandler` — react-query global error sink
6. `OfflineBanner` — top-of-viewport offline indicator
7. `PwaOfflineFallback` — fallback view when SW can't serve
8. `AccessibilityShell` — wraps `{children}` with skip links + keyboard
   shortcuts
9. `CookieConsentBanner` — first-visit consent, bottom-of-viewport

## States

- [ ] **First paint (cold cache)** — fonts load with `display: swap`; no FOUC;
      theme color applied before paint
- [ ] **Returning visit (warm cache)** — instant paint; theme persisted
- [ ] **Cookie consent not yet given** — banner visible; PWA update prompt
      suppressed until consent resolves
- [ ] **Offline** — `<OfflineBanner>` visible; `<PwaOfflineFallback>` ready for
      non-cached navigation
- [ ] **PWA update available** — `<PwaUpdatePrompt>` appears bottom-right (after
      consent); never competes with cookie banner
- [ ] **Standalone PWA mode** — launched from home-screen icon; URL bar absent;
      safe-area insets respected; `display: standalone` honored

## Fonts

The root layout loads three Google fonts and exposes them as CSS variables.

- [ ] `Cormorant_Garamond` → `--l-serif-loaded` (weights 400/500/600, italic)
- [ ] `Inter` → `--l-sans-loaded` (weights 400/500/600/700)
- [ ] `JetBrains_Mono` → `--l-mono-loaded` (weights 400/500)
- [ ] `display: 'swap'` on all three — no blocking
- [ ] Variables present on `<html>` element

## Metadata and SEO

Set by `export const metadata` in `layout.tsx`.

- [ ] **Title template** — pages set `title: '…'` and shell appends ` | OSHUN`
- [ ] **Description** — default copy mentions all six domains
- [ ] **Manifest** — `<link rel="manifest" href="/manifest.json">`
- [ ] **Icons** — favicon.svg + 192px PNG + 180px apple-touch
- [ ] **`metadataBase`** — `https://oshun.app`; canonical resolves correctly
- [ ] **`apple-mobile-web-app-*` meta tags** — capable=yes,
      status-bar=black-translucent, title=OSHUN
- [ ] **OpenGraph** — `og:title`, `og:description`, `og:image` at
      `/opengraph-image`
- [ ] **Twitter card** — summary_large_image at `/twitter-image`
- [ ] **JSON-LD WebApplication** — schema.org/WebApplication; six domain
      `featureList` entries

## Viewport / theme color

- [ ] **Light mode theme color** — `#f1ebdd` (cream paper)
- [ ] **Dark mode theme color** — `#241c12` (dark paper)
- [ ] **`width=device-width, initial-scale=1`** — present
- [ ] **Browser tab color** matches active theme on Chromium/Safari
- [ ] **iOS status bar** matches theme (black-translucent in PWA mode)

## Header

The top-of-viewport shell header (mounted inside the page or a layout group, not
the root layout). Variants:

- **Shell header** — primary nav: Home, Explore, Activity, Library, Profile
- **Domain header** — domain switcher + crumb when inside a domain
- **Minimal header** — public/marketing routes (`/welcome`, `/landing`, legal)
- **Hidden** — onboarding flow; some scene/atelier modes

- [ ] **Shell header** renders on all five primary shell routes
- [ ] **Active route** is indicated visually + via `aria-current="page"`
- [ ] **Search affordance** opens `/search` (or in-place search) via `⌘K` /
      `Ctrl+K`
- [ ] **Profile menu** opens with chevron + avatar; closes on outside click +
      Esc
- [ ] **Domain switcher** lists 6 domains; current domain disabled
- [ ] **Notifications bell** opens `<NotificationsCenterPanel>`; unread count
      visible
- [ ] **Assistant trigger** opens assistant overlay via
      `dispatchOshunAssistantOpen`

## Footer

- [ ] **Legal links** — Privacy, Terms, Cookies, Accessibility, CCPA, DPA
- [ ] **Status link** — `/status`
- [ ] **Mobile**: footer collapses or is replaced by `<MobileBottomNav>`

## Mobile bottom nav

`apps/oshun/web/src/components/MobileBottomNav.tsx`

- [ ] **Visible only on coarse pointer** + viewport ≤ 640 px
- [ ] **Five tabs** matching `WEB_SHELL_ROUTE_PATHS`
- [ ] **Active tab** highlighted; safe-area inset honored
- [ ] **Persists across navigation** (no remount flash)

## Breakpoints

Walk each at: 320 px, 390 px, 640 px, 768 px, 1024 px, 1440 px, 1920 px.

- [ ] **320 px** — no horizontal scroll; one-column layout
- [ ] **390 px** (iPhone 14) — mobile bottom nav visible; safe area honored
- [ ] **640 px** — transition between mobile and tablet layouts
- [ ] **768 px** — tablet; aside panels appear where designed
- [ ] **1024 px** — desktop layouts complete
- [ ] **1440 px** — content reaches max-width; gutters balanced
- [ ] **1920 px** — content doesn't stretch beyond design max-width

## Theming

`@/design-system/lilith` and `globals-v2.css`.

- [ ] **System preference respected** on first visit (no stored choice)
- [ ] **Manual override** persists across reload (`localStorage`)
- [ ] **Token consistency** — paper/cream/terracotta tokens on all surfaces
- [ ] **Forced-colors mode** (Windows high contrast) — text readable, borders
      visible

## Cross-references

- [02-routing-layouts.md](./02-routing-layouts.md) — how routes mount under this
  shell
- [03-pwa-behavior.md](./03-pwa-behavior.md) — PWA install / SW behavior
- [04-auth-session.md](./04-auth-session.md) — what gates the header's
  role-specific items
- [06-keyboard-a11y.md](./06-keyboard-a11y.md) — keyboard shortcuts, skip links,
  focus
- Feature spec: [`V1/features.md`](../../V1/features.md#product-surfaces)
- Architecture:
  [`V1/ARCHITECTURE.md`](../../V1/ARCHITECTURE.md#customer-web--appsoshunweb)

## Open questions / known gaps

- [ ] `/lilith-studio/*` may run under a different header variant — confirm
      whether it inherits the shell header or uses Studio's own header
- [ ] `/atelier/*` and `/scene/*` likely use a focus-mode header — note which
      routes hide the global header entirely
