| python3 -m json.tool | head -80`. OpenAPI: `localhost:8311/api/v1/docs`.
- Postgres aggregates can arrive as **strings** (`Num = number | string | null`): always go through `num()` / `fmtInt()` etc. from `@/lib/format`.
- **No hardcoded statistics, counts, timestamps or fake data.** If something is missing, show "Unavailable" (``).
## Design system (dark, scientific, premium — not a crypto dashboard, not generic SaaS cards)
Tokens in `src/app/globals.css` → Tailwind utilities: `bg-space | bg-plane | bg-plane-2 | bg-plane-3`, `text-ink | text-ink-2 | text-ink-3`,
`border-rule | border-rule-strong`, `text-accent | bg-accent | text-accent-ink | bg-accent-soft`, `text-accent-2`, status
`text-active | text-warn | text-danger | text-inactive` (+ `-soft` backgrounds), orbit classes `text-leo | text-meo | text-geo | text-heo | text-other`,
chart series `series-1..8`. Utility classes: `.panel` (translucent bordered surface — use sparingly, prefer spatial composition and hairlines),
`.eyebrow` (small caps label), `.display` (big headline), `.mono` / `.tnum` (telemetry, IDs, numbers), `.container-x`,
`.data-table` (+ `.stack` to transform rows into stacked cards under 768 px — put `data-label="…"` on each `| `, `className="primary"` on the name cell, `.num` for numbers),
`.grid-bg`, `.dot` / `.pulse`, `.link`, `.scrollbar-thin`, `.no-scrollbar`.
Shared components (do **not** modify; build local ones in your own folder if you need variants):
- `@/components/ui/section` → `Container`, `PageHeader`, `Section` (eyebrow/title/action), `Stat` (big number tile)
- `@/components/ui/badges` → `StatusBadge`, `OrbitBadge`, `TypeBadge`, `MissionLabel`, `FreshnessBadge`
- `@/components/ui/pagination` → `Pagination` (URL-driven), `@/components/ui/unavailable` → `Unavailable`
- `@/components/charts/charts` → `HBars`, `Bars`, `StackedBars`, `AreaChart`, `Donut`, `Sparkline`, `Histogram` (pure SVG, server-safe)
- `@/components/map/world-map` → `WorldMap` (SVG equirectangular; `tracks`, `markers`; `splitTrack` for antimeridian), `MAP_SIZE`
- `@/lib/site` → `routes.*`, `SITE_NAME/SITE_URL/TAGLINE`, `ORBIT_CLASS_COLORS`, `STATUS_COLORS`, `MISSION_LABELS`, `OBJECT_TYPE_LABELS`, `EVENT_TYPE_LABELS`
- `@/lib/format` → `fmtInt fmt1 fmt2 fmtCompact fmtKm fmtDeg fmtMinutes fmtPct fmtDate fmtDateTime fmtAgo fmtYear titleCase num`
- Layout (header, mobile tab bar, footer, ⌘K search dialog) is already mounted in `src/app/layout.tsx`. Pages render inside ``
**without** padding: wrap content in `` (max 1280) or `` (1600), or go full-bleed on purpose (globe).
Typography: Inter (UI) + JetBrains Mono (IDs/telemetry). Big display numbers use `tnum`. Icons: `lucide-react`.
## Mobile rules (mandatory — every feature is tested at 390 px before it is complete)
- No horizontal overflow at 320–430 px. Tables → `.data-table.stack` or purpose-built list rows. Long IDs wrap or truncate.
- Tap targets ≥ 44 px. The fixed bottom tab bar (58 px + safe area) is reserved by `` padding; don't add another fixed bottom bar.
- Desktop should use wide screens (two-column "terminal" layouts on detail pages: main visualisation + right telemetry panel).
- DOM order = visual order on all breakpoints (no `order:` tricks). Heavy globe/3D features lazy-load (`next/dynamic`, `ssr: false`).
## SEO & sharing
Every public page exports `generateMetadata` (title, description, `alternates.canonical`, `openGraph`, `twitter`). Detail pages should
add JSON-LD where sensible (` |