
@font-face {
  font-family:'Atkinson Hyperlegible'; font-style:normal; font-weight:400;
  font-display:swap; src:url('/fonts/atkinson-hyperlegible-400.woff2') format('woff2');
}
@font-face {
  font-family:'Atkinson Hyperlegible'; font-style:normal; font-weight:700;
  font-display:swap; src:url('/fonts/atkinson-hyperlegible-700.woff2') format('woff2');
}
@font-face {
  font-family:'JetBrains Mono'; font-style:normal; font-weight:400;
  font-display:swap; src:url('/fonts/jetbrains-mono-400.woff2') format('woff2');
}
@font-face {
  font-family:'JetBrains Mono'; font-style:normal; font-weight:500;
  font-display:swap; src:url('/fonts/jetbrains-mono-500.woff2') format('woff2');
}

/* Orchid Noir — the fleet ground, dark by default. Light is opt-in only (the rail
   toggle sets data-theme="light"); there is deliberately no prefers-color-scheme rule,
   so the page a first-time visitor sees is the one that looks like the platform.
   Both themes declare the same token names, so no rule below knows which is on. */
:root {
  --bg:#150f1c; --surface:#201829; --surface-alt:#2c2138; --surface-sunk:#0e0a14;
  --border:#3d2f4d; --border-strong:#553f6b;
  --fg:#ece6f5; --fg-dim:#a99cbd; --fg-muted:#63566f;
  --accent:#c084fc; --on-accent:#0e0a14;
  --accent-soft:color-mix(in srgb, #c084fc 14%, transparent);
  --teal:#2dd4bf; --amber:#ffab00;
  /* No shadow tokens: the apps' surfaces are separated by border and ground alone, and
     a lifted card would be the one element on the page that is not from the preset.
     Highlighted source keeps the dark ground in BOTH themes — the Pygments style is
     tuned for a dark background, and re-tinting its token colors per theme would fork
     the palette. A code block is a code block. */
  --code-bg:#0e0a14; --code-border:#3d2f4d;
  --nav-w:13rem; --nav-w-collapsed:3.25rem; --nav-w-drawer:15rem;
  /* Stepped by the wide-display ramp below. `--wrap` is the whole column (grid, section
     rules, stat rows); `--measure` and `--lede-measure` cap prose independently, so a
     wider viewport widens the CARD WALL without stretching a paragraph past readability. */
  --wrap:58rem; --measure:72ch; --lede-measure:64ch;
}
:root[data-theme="light"] {
  --bg:#faf8fc; --surface:#ffffff; --surface-alt:#f2ecf8; --surface-sunk:#f6f2fa;
  --border:#e4dcee; --border-strong:#cfc0e0;
  --fg:#1d1526; --fg-dim:#544a63; --fg-muted:#7d7189;
  --accent:#7326b8; --on-accent:#ffffff;
  --accent-soft:color-mix(in srgb, #7326b8 8%, transparent);
  --teal:#0f6b62; --amber:#8a4a08;
}

/* ---- the display ramp ----
   Two mechanisms, and they must not double-count.

   Below 2560px, extra width is extra ROOM: step the column so the twelve-card wall goes
   2 -> 3 across instead of leaving the surplus as gutter. Prose steps far less than the
   column does, because an unbounded line length is worse than an empty margin.

   At and above 2560px, extra width is DENSITY, not room (see the rule's "operator's
   screen" — a 27" 4K panel is 163 PPI at the same 2-2.5ft, so a glyph needs ~1.5x the
   CSS px to be the same physical size). That is the root-font-size step, transcribed
   from program/shared/frontend/src/theme.css. Everything here is rem-based, so it scales
   type, controls, gaps and the column together.

   So the column stops widening at 1920 and the root step takes over. Widening it again
   at 2560 would apply both levers to the same surplus. */
@media (min-width: 1280px) { :root { --wrap:62rem; } }
@media (min-width: 1600px) { :root { --wrap:68rem; --measure:76ch; --lede-measure:68ch; } }
@media (min-width: 1920px) { :root { --wrap:76rem; --measure:78ch; --lede-measure:70ch; } }
@media (min-width: 2560px) { :root { font-size: 18px; } }
@media (min-width: 3840px) { :root { font-size: 22px; } }

* { box-sizing:border-box; }
html { -webkit-text-size-adjust:100%; }
body {
  margin:0; background:var(--bg); color:var(--fg);
  font-family:'Atkinson Hyperlegible',-apple-system,BlinkMacSystemFont,'Segoe UI',sans-serif;
  line-height:1.6; font-size:0.9rem; -webkit-font-smoothing:antialiased;
}
a { color:var(--accent); text-decoration:none; }
a:hover { text-decoration:underline; }
:focus-visible { outline:2px solid var(--accent); outline-offset:2px; }
code, .mono { font-family:'JetBrains Mono',Consolas,monospace; font-size:0.92em; }
/* Tabular mono for every figure — the fleet's `.num`. */
.num { font-family:'JetBrains Mono',Consolas,monospace; font-variant-numeric:tabular-nums; }

/* Rail + code gutters, matching the apps' 6px thumb. */
* { scrollbar-width:thin; scrollbar-color:var(--border) var(--surface-sunk); }
::-webkit-scrollbar { width:6px; height:6px; }
::-webkit-scrollbar-track { background:var(--surface-sunk); }
::-webkit-scrollbar-thumb { background:var(--border); border-radius:3px; }

.skip {
  position:absolute; left:-9999px; top:0; background:var(--accent); color:var(--on-accent);
  padding:0.5rem 0.9rem; border-radius:0 0 0.125rem 0; z-index:10; font-weight:500;
}
.skip:focus { left:0; text-decoration:none; }

/* ---- small-screen chrome: top bar + drawer scrim ----
   Both are inert above the drawer breakpoint (display:none), so the wide layout carries
   no extra boxes. The bar is in normal flow and sticky, so it reserves its own space and
   the layout below needs no compensating padding. */
.topbar { display:none; }
.scrim { display:none; }
@media (max-width: 832px) {
  .topbar {
    display:flex; align-items:center; gap:0.5rem;
    position:sticky; top:0; z-index:20;
    padding:0.4rem 0.6rem;
    background:var(--surface-sunk); border-bottom:1px solid var(--border);
  }
  .topbar-text {
    font-family:'JetBrains Mono',Consolas,monospace; font-weight:700; font-size:0.8rem;
    letter-spacing:0.02em;
  }
  .scrim {
    display:block; position:fixed; inset:0; z-index:30; background:rgba(0,0,0,0.6);
    opacity:0; pointer-events:none; transition:opacity 180ms ease;
  }
  html.nav-open .scrim { opacity:1; pointer-events:auto; }
  html.nav-open body { overflow:hidden; }
}

/* ---- layout: full-height rail, sticky contents ----
   The rail must stretch the whole document, not one viewport: a 100vh rail on a long
   page ends in a visible seam partway down, which reads as a rendering bug. The column
   stretches; the links inside it are what sticks. */
.layout { display:flex; align-items:stretch; min-height:100vh; }
nav.side {
  width:var(--nav-w); flex:0 0 var(--nav-w);
  background:var(--surface-sunk); border-right:1px solid var(--border);
  transition:width 140ms ease, flex-basis 140ms ease;
}
.nav-inner {
  position:sticky; top:0; max-height:100vh; overflow-y:auto; overflow-x:hidden;
  padding:0.6rem 0.45rem;
}
html.nav-collapsed nav.side { width:var(--nav-w-collapsed); flex-basis:var(--nav-w-collapsed); }
html.nav-collapsed .nav-label, html.nav-collapsed .nav-head-text { display:none; }

.nav-head { display:flex; align-items:center; gap:0.4rem; padding:0.1rem 0.15rem 0.6rem; }
.nav-head-text {
  font-weight:700; font-size:0.8rem; white-space:nowrap; letter-spacing:0.02em;
  font-family:'JetBrains Mono',Consolas,monospace;
}
.nav-btn {
  background:var(--surface-alt); border:1px solid var(--border); border-radius:0.125rem;
  color:var(--fg-dim); cursor:pointer; padding:0.3rem; line-height:0; flex:0 0 auto;
  transition:color 150ms, background-color 150ms, border-color 150ms;
}
.nav-foot { margin-top:0.75rem; padding:0 0.15rem; }
html.nav-collapsed .nav-foot { display:flex; justify-content:center; }
/* Both glyphs ship; only the one matching the live theme is shown, so the control is
   correct on first paint without script measuring anything. Dark is the default, so the
   dark-mode glyph is the one visible until the visitor opts into light. */
.theme-light-only { display:none; }
:root[data-theme="light"] .theme-light-only { display:inline; }
:root[data-theme="light"] .theme-dark-only { display:none; }

nav.side a.nav-item {
  display:flex; align-items:center; gap:0.55rem; white-space:nowrap;
  padding:0.35rem 0.5rem; margin-bottom:0.05rem; border-radius:0.125rem;
  color:var(--fg-dim); font-size:0.8rem; text-decoration:none;
  transition:background-color 100ms, color 100ms;
}
nav.side a.nav-item .ico { flex:0 0 16px; }
nav.side a.nav-item[aria-current="page"] {
  background:var(--accent-soft); color:var(--accent); font-weight:700;
}
.nav-sep {
  margin:0.8rem 0.5rem 0.35rem; padding-top:0.65rem; border-top:1px solid var(--border);
  font-size:0.625rem; text-transform:uppercase; letter-spacing:0.025em; color:var(--fg-muted);
  white-space:nowrap; font-weight:700;
}
html.nav-collapsed .nav-sep { font-size:0; padding-top:0.65rem; margin-bottom:0.35rem; }

main { flex:1 1 auto; min-width:0; }
.wrap { max-width:var(--wrap); margin:0 auto; padding:1.75rem 1.5rem 3rem; }

/* Type ramp compressed toward the apps': one bold title, then `.text-section`-style
   uppercase rules for everything below it. */
h1 { font-size:1.6rem; line-height:1.2; margin:0 0 0.4rem; letter-spacing:-0.02em; }
h2 {
  font-size:0.75rem; text-transform:uppercase; letter-spacing:0.025em; font-weight:700;
  color:var(--fg-dim); border-bottom:1px solid var(--border);
  margin:2rem 0 0.75rem; padding-bottom:0.35rem;
}
.lede { color:var(--fg-dim); font-size:1rem; line-height:1.55; max-width:var(--lede-measure); }
p { max-width:var(--measure); }
.eyebrow {
  font-size:0.75rem; text-transform:uppercase; letter-spacing:0.025em;
  color:var(--fg-dim); margin-bottom:0.25rem; font-weight:700;
}

/* ---- hero ---- */
.hero { display:flex; gap:1.25rem; align-items:flex-start; flex-wrap:wrap; }
.hero-text { flex:1 1 26rem; min-width:0; }
.avatar {
  flex:0 0 auto; width:6rem; height:6rem; border-radius:0.375rem;
  object-fit:cover; background:var(--accent-soft); border:1px solid var(--border);
  display:flex; align-items:center; justify-content:center;
  font-size:1.6rem; font-weight:700; color:var(--accent); letter-spacing:-0.02em;
}
.hero .greeting { color:var(--accent); font-weight:700; margin:0 0 0.1rem; font-size:0.9rem; }

.stats {
  display:flex; flex-wrap:wrap; gap:1rem; margin:1.25rem 0 0;
}
.stat {
  background:var(--surface); border:1px solid var(--border); border-radius:0.375rem;
  padding:0.6rem 0.9rem; min-width:7rem;
}
.stat .n {
  font-family:'JetBrains Mono',Consolas,monospace; font-variant-numeric:tabular-nums;
  font-size:1.25rem; font-weight:500; color:var(--fg); line-height:1.15;
}
/* fg-dim, not the fleet's fg-muted: muted-on-Orchid-Noir is 2.8:1, fine for chrome an
   operator glances past, too low for a figure a stranger is meant to read. Same reason
   the footer below is dim. The rail's own labels keep the muted house value. */
.stat .k {
  font-size:0.625rem; text-transform:uppercase; letter-spacing:0.025em; color:var(--fg-dim);
  margin-top:0.15rem; font-weight:700;
}
.note {
  border-left:2px solid var(--amber); background:var(--surface);
  border-top:1px solid var(--border); border-right:1px solid var(--border);
  border-bottom:1px solid var(--border);
  padding:0.75rem 1rem; margin:1rem 0; color:var(--fg-dim); font-size:0.875rem;
  border-radius:0 0.375rem 0.375rem 0; max-width:var(--measure);
}
.note strong { color:var(--fg); }
.callout {
  background:var(--surface); border:1px solid var(--border);
  border-left:2px solid var(--accent);
  border-radius:0 0.375rem 0.375rem 0; padding:1rem; margin:1.25rem 0; max-width:var(--measure);
}
.callout h3 {
  margin:0 0 0.3rem; font-size:0.75rem; text-transform:uppercase; letter-spacing:0.025em;
  color:var(--accent);
}
.callout p { margin:0; color:var(--fg-dim); font-size:0.875rem; }
.callout a { font-weight:500; }

/* Buttons are the preset's `.btn-primary` (accent ground, DARK ink — orchid is a light
   accent and white-on-orchid fails contrast) and `.btn` (`.ghost` here). */
.btn-row { display:flex; flex-wrap:wrap; gap:0.5rem; margin-top:1.25rem; }
.btn {
  display:inline-block; padding:0.25rem 0.75rem; border-radius:0.125rem;
  background:var(--accent); color:var(--on-accent); font-weight:500; font-size:0.875rem;
  border:1px solid var(--accent); transition:opacity 150ms;
}
.btn.ghost {
  background:var(--surface-alt); color:var(--fg); border:1px solid var(--border);
  transition:color 150ms, background-color 150ms, border-color 150ms; opacity:1;
}
/* Phone tap-target floor (WCAG 2.5.5), ported from the preset. Desktop density stays. */
@media (max-width: 639px) {
  .btn, .nav-btn {
    min-height:2.75rem; display:inline-flex; align-items:center; justify-content:center;
  }
}
/* The preset's `.badge`. */
.tag {
  display:inline-block; font-size:0.625rem; text-transform:uppercase; letter-spacing:0.025em;
  font-weight:700; color:var(--teal); background:var(--surface-alt);
  border:1px solid var(--border); border-radius:0.125rem; padding:0.125rem 0.375rem;
  vertical-align:middle; white-space:nowrap;
}
.progs { display:grid; grid-template-columns:repeat(auto-fit,minmax(19rem,1fr));
  gap:0.5rem; margin-top:0.75rem; }
/* The preset's `.card`. */
.prog {
  display:block; background:var(--surface-sunk); border:1px solid var(--border);
  border-radius:0.375rem; padding:1rem; color:inherit; text-decoration:none;
  transition:background-color 100ms, border-color 100ms;
}
.prog-h { display:flex; align-items:center; gap:0.45rem; margin-bottom:0.35rem; flex-wrap:wrap; }
.prog-h .ico { color:var(--accent); flex:0 0 auto; }
.prog-n { font-weight:700; color:var(--fg); font-size:0.95rem; }
.prog p { margin:0; color:var(--fg-dim); font-size:0.85rem; line-height:1.5; max-width:none; }
ul.steps { padding-left:1.1rem; color:var(--fg-dim); max-width:var(--measure); }
ul.steps li { margin:0.35rem 0; }
ul.steps strong { color:var(--fg); }
/* The file tree is the page's one data table, so it gets the apps' `.table-zebra`
   treatment: an even-row tint to track a row across the width, accent on hover. */
ul.tree { list-style:none; padding:0; margin:0.5rem 0 0;
  font-family:'JetBrains Mono',Consolas,monospace; font-size:0.8rem;
  border:1px solid var(--border); border-radius:0.375rem; overflow:hidden; }
ul.tree li {
  padding:0.25rem 0.6rem; display:flex; gap:1rem; align-items:baseline;
  transition:background-color 100ms;
}
ul.tree li:nth-of-type(even) { background:color-mix(in srgb, var(--fg) 4%, transparent); }
ul.tree li a { flex:1; }
ul.tree li .loc {
  color:var(--fg-dim); font-size:0.75rem; font-variant-numeric:tabular-nums;
}
footer {
  border-top:1px solid var(--border); margin-top:2.5rem; padding-top:1rem;
  color:var(--fg-dim); font-size:0.8rem; max-width:var(--measure);
}
pre.snippet {
  background:var(--code-bg); border:1px solid var(--code-border); border-radius:0.375rem;
  padding:0.9rem; overflow-x:auto; color:#a99cbd; white-space:pre-wrap;
  font-family:'JetBrains Mono',Consolas,monospace; font-size:0.8rem; line-height:1.55;
}
/* Pygments' stylesheet is linked AFTER this one and sets its own `.hl` background at the
   same specificity, so the ground has to be forced. */
/* `.hl` is the horizontal scroll container, NOT `overflow:hidden`. The inner <pre> sits
   in a table cell, which sizes to max-content, so the pre never gets a constrained width
   and never scrolls — the table just grows past the block. Clipping that instead of
   scrolling it silently truncates every long line: on a 390px phone this block is
   1071px wide, so two thirds of the source would be unreachable. */
.hl {
  margin:1.25rem 0; background:var(--code-bg) !important;
  border:1px solid var(--code-border); border-radius:0.375rem; overflow-x:auto;
}
.hl table { border-collapse:collapse; width:100%; }
.hl pre {
  margin:0; padding:0.9rem; overflow-x:auto; border:none; border-radius:0;
  font-family:'JetBrains Mono',Consolas,monospace; font-size:0.8rem; line-height:1.55;
}
.hl td.linenos { user-select:none; width:1%; }
/* The Pygments style ships its own gutter background; override it so the line numbers
   sit on the code ground rather than in a band of their own. Literal hex, not a token:
   the code ground is theme-independent, so the gutter ink has to be too. #877a96 is
   fg-muted lifted to 4.9:1 against #0e0a14 — muted itself is 2.9:1 there, which is not
   a readable line number. */
.hl td.linenos, .hl td.linenos div, .hl td.linenos pre,
.hl td.linenos .normal, .hl td.linenos .special {
  background:transparent !important; color:#877a96 !important;
}
.hl td.linenos pre { text-align:right; padding-right:0.8rem; }
.hl td.code pre { border-left:1px solid var(--code-border); }
.hl a { color:inherit; }

/* ---- the drawer breakpoint ----
   832px, in PIXELS on purpose: the JS reads the same number through matchMedia, and `rem`
   inside a media query resolves against the INITIAL font size rather than :root, so a rem
   breakpoint here and a rem breakpoint there are not guaranteed to agree once the
   large-viewport root steps are in play. One literal, two consumers.

   Below it the rail leaves the flow entirely and becomes an overlay drawer, so the
   content gets the full viewport width — 52px back on a 390px phone. The drawer always
   shows LABELS, whatever the desktop collapse preference was: an icon-only rail is
   navigable by hover tooltip, and touch has no hover. Body type gets a floor here too;
   this is the one surface in the fleet not read on a 27" 4K panel. */
@media (max-width: 832px) {
  nav.side {
    position:fixed; top:0; left:0; bottom:0; z-index:40;
    width:var(--nav-w-drawer); flex:0 0 var(--nav-w-drawer);
    transform:translateX(-100%); transition:transform 180ms ease;
    box-shadow:0 0 2rem rgba(0,0,0,0.5);
  }
  html.nav-open nav.side { transform:translateX(0); }
  /* The collapse preference is a desktop concept; the drawer overrides it wholesale. */
  html.nav-collapsed nav.side {
    width:var(--nav-w-drawer); flex-basis:var(--nav-w-drawer);
  }
  html.nav-collapsed .nav-label { display:inline; }
  html.nav-collapsed .nav-head-text { display:inline; }
  html.nav-collapsed .nav-sep { font-size:0.625rem; }
  html.nav-collapsed .nav-foot { display:block; }
  body { font-size:1rem; }
  .wrap { padding:1.25rem 1rem 2.5rem; }
  h1 { font-size:1.4rem; }
  .avatar { width:4.5rem; height:4.5rem; font-size:1.25rem; }
  .stats { gap:0.5rem; }
}

/* ---- input, not width ----
   Orthogonal to every breakpoint above: a touchscreen laptop is coarse-pointer at
   1920px. Per the fleet's narrow-layout decision — breakpoints select the LAYOUT,
   pointer queries select the AFFORDANCES, and neither ever sniffs the user agent. */
@media (pointer: coarse) {
  nav.side a.nav-item { min-height:2.75rem; }
  ul.tree li { min-height:2.75rem; align-items:center; }
}
/* Hover styling is gated on a device that can actually hover. On touch, :hover latches
   after a tap and leaves the last-tapped card or row highlighted for the rest of the
   page — which reads as a stuck selection. */
@media (hover: hover) {
  .nav-btn:hover { color:var(--accent); border-color:var(--border-strong); background:var(--surface); }
  nav.side a.nav-item:hover {
    background:color-mix(in srgb, var(--accent) 12%, transparent);
    color:var(--fg); text-decoration:none;
  }
  .btn:hover { text-decoration:none; opacity:0.9; }
  .btn.ghost:hover { background:var(--surface); color:var(--fg); border-color:var(--border-strong); }
  .prog:hover {
    border-color:var(--border-strong); text-decoration:none;
    background:color-mix(in srgb, var(--accent) 8%, var(--surface-sunk));
  }
  ul.tree li:hover { background:color-mix(in srgb, var(--accent) 12%, transparent); }
}
@media (prefers-reduced-motion: reduce) {
  * { transition:none !important; }
}
