/* =====================================================================
   DESIGN TOKENS — see DESIGN_SYSTEM.md for the full extraction and the
   derivation of the two status colors the landing page didn't define.

   These live at :root (not on .landing) so the landing page and every app
   page read one set of variables. .landing keeps only its own composition
   rules; app-page classes are unprefixed and consume the same tokens.
   ===================================================================== */
:root {
  color-scheme: dark;

  /* surfaces */
  --l-bg: #0a0a0b;
  --l-bg-2: #0e0e10;
  --l-panel: #111114;
  --l-code-bg: #0c0c0e;
  --l-line: rgba(255, 255, 255, 0.08);
  --l-line-strong: rgba(255, 255, 255, 0.14);
  --l-fill: linear-gradient(180deg, rgba(255, 255, 255, 0.028), rgba(255, 255, 255, 0.006));

  /* text */
  --l-text: #f3f3f4;
  --l-muted: #8b8b92;
  --l-faint: #5c5c63;

  /* accent */
  --l-accent: #ff5a00;
  --l-accent-soft: #ff7a29;
  --l-accent-deep: #b33d00;
  --l-accent-glow: rgba(255, 90, 0, 0.55);
  --l-accent-haze: rgba(255, 90, 0, 0.14);
  /* Accent tints that were literals in button-hover and focus rules. */
  --l-accent-line: rgba(255, 122, 41, 0.5);
  --l-accent-wash: rgba(255, 90, 0, 0.06);

  /* status. --l-ok is the design's own terminal green (Tailwind green-400);
     --l-warn/--l-danger are the same ramp at the same step, because the
     landing page defines no warning or danger color and app pages have
     states that must not borrow the accent. See DESIGN_SYSTEM.md §1. */
  --l-ok: #4ade80;
  --l-ok-fill: rgba(74, 222, 128, 0.12);
  --l-ok-line: rgba(74, 222, 128, 0.30);
  --l-warn: #fbbf24;
  --l-warn-fill: rgba(251, 191, 36, 0.12);
  --l-warn-line: rgba(251, 191, 36, 0.30);
  --l-danger: #f87171;
  --l-danger-fill: rgba(248, 113, 113, 0.12);
  --l-danger-line: rgba(248, 113, 113, 0.30);

  /* Surfaces that were written literally before light mode existed, and
     therefore assumed a dark canvas. A white overlay at 2% is invisible on
     a light background, which is exactly how a half-converted theme looks.
     --l-raise appeared 8 times in app rules, --l-header-bg is the sticky
     header's translucent backdrop, --l-dot the code-panel window buttons,
     --l-on-accent the text that sits ON the orange. */
  --l-raise: rgba(255, 255, 255, 0.02);
  --l-header-bg: rgba(10, 10, 11, 0.72);
  --l-dot: #2a2a2e;
  --l-on-accent: #0a0a0b;

  /* geometry + motion */
  --l-radius: 16px;
  --l-maxw: 1200px;
  --l-ease: cubic-bezier(0.22, 1, 0.36, 1);

  /* Three families, loaded once in base.html. Nothing below this line may
     name a family literally — every rule reads these tokens, so changing a
     face is a one-line edit here. The landing section used to re-declare
     families inline, which is how --font-mono ended up wrong in five
     separate places at once. See DESIGN_SYSTEM.md §2. */
  --font-display: "Sora", "Geist", sans-serif;
  --font-body: "Geist", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
  /* A REAL monospace. This was "Space Grotesk" — a proportional face — so
     every stack trace, trace ID and API key rendered unaligned: measured
     in-browser, "iiiiiiii" came out 78px against "MMMMMMMM" at 277px. */
  --font-mono: "JetBrains Mono", ui-monospace, SFMono-Regular, Menlo, monospace;
}

/* =====================================================================
   LIGHT THEME

   Dark is the primary mode and stays the default above: an unknown or
   missing preference renders dark, so nothing here can make the site
   light by accident.

   These are re-derived, not inverted. Three values could not simply flip:

   - The accent. #ff5a00 on white measures ~2.9:1, which fails text
     contrast, so light takes the existing --l-accent-deep (#b33d00,
     ~5.9:1) as its accent and derives a new deep from there.
   - The status colors. The terminal green #4ade80 is ~1.7:1 on white —
     effectively unreadable — so ok/warn/danger all drop to their 600/700
     steps.
   - --l-on-accent flips to white: it is the text sitting ON the orange,
     and near-black on #b33d00 is not legible.

   The theme is applied server-side (see base.html) so there is no flash
   of the wrong theme on load — the first paint is already correct.
   ===================================================================== */
:root[data-theme="light"] {
  color-scheme: light;

  --l-bg: #fbfbfc;
  --l-bg-2: #ffffff;
  --l-panel: #ffffff;
  --l-code-bg: #f5f5f7;
  --l-line: rgba(9, 9, 15, 0.10);
  --l-line-strong: rgba(9, 9, 15, 0.17);
  --l-fill: linear-gradient(180deg, rgba(9, 9, 15, 0.022), rgba(9, 9, 15, 0.006));

  --l-text: #16161c;
  --l-muted: #5b5b66;
  --l-faint: #8a8a95;

  --l-accent: #b33d00;
  --l-accent-soft: #cc4a06;
  --l-accent-deep: #8a2f00;
  --l-accent-glow: rgba(179, 61, 0, 0.35);
  --l-accent-haze: rgba(179, 61, 0, 0.10);
  --l-accent-line: rgba(179, 61, 0, 0.45);
  --l-accent-wash: rgba(179, 61, 0, 0.055);

  --l-ok: #157f3d;
  --l-ok-fill: rgba(21, 127, 61, 0.10);
  --l-ok-line: rgba(21, 127, 61, 0.28);
  --l-warn: #9a5b00;
  --l-warn-fill: rgba(154, 91, 0, 0.10);
  --l-warn-line: rgba(154, 91, 0, 0.28);
  --l-danger: #c02626;
  --l-danger-fill: rgba(192, 38, 38, 0.09);
  --l-danger-line: rgba(192, 38, 38, 0.28);

  --l-raise: rgba(9, 9, 15, 0.028);
  --l-header-bg: rgba(255, 255, 255, 0.80);
  --l-dot: #d6d6dc;
  --l-on-accent: #ffffff;
}

/* "System" follows the OS. Written as a separate selector rather than
   folded into the block above because the two must not collapse: an
   explicit "dark" choice has to survive a light OS setting, which it does
   here only because [data-theme="system"] excludes it. */
@media (prefers-color-scheme: light) {
  :root[data-theme="system"] {
    color-scheme: light;

    --l-bg: #fbfbfc;
    --l-bg-2: #ffffff;
    --l-panel: #ffffff;
    --l-code-bg: #f5f5f7;
    --l-line: rgba(9, 9, 15, 0.10);
    --l-line-strong: rgba(9, 9, 15, 0.17);
    --l-fill: linear-gradient(180deg, rgba(9, 9, 15, 0.022), rgba(9, 9, 15, 0.006));

    --l-text: #16161c;
    --l-muted: #5b5b66;
    --l-faint: #8a8a95;

    --l-accent: #b33d00;
    --l-accent-soft: #cc4a06;
    --l-accent-deep: #8a2f00;
    --l-accent-glow: rgba(179, 61, 0, 0.35);
    --l-accent-haze: rgba(179, 61, 0, 0.10);
    --l-accent-line: rgba(179, 61, 0, 0.45);
    --l-accent-wash: rgba(179, 61, 0, 0.055);

    --l-ok: #157f3d;
    --l-ok-fill: rgba(21, 127, 61, 0.10);
    --l-ok-line: rgba(21, 127, 61, 0.28);
    --l-warn: #9a5b00;
    --l-warn-fill: rgba(154, 91, 0, 0.10);
    --l-warn-line: rgba(154, 91, 0, 0.28);
    --l-danger: #c02626;
    --l-danger-fill: rgba(192, 38, 38, 0.09);
    --l-danger-line: rgba(192, 38, 38, 0.28);

    --l-raise: rgba(9, 9, 15, 0.028);
    --l-header-bg: rgba(255, 255, 255, 0.80);
    --l-dot: #d6d6dc;
    --l-on-accent: #ffffff;
  }
}

* {
  box-sizing: border-box;
}

body {
  margin: 0;
  font-family: var(--font-body);
  line-height: 1.6;
  color: var(--l-text);
  background: var(--l-bg);
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}

::selection { background: var(--l-accent); color: var(--l-on-accent); }

h1, h2, h3, h4 {
  font-family: var(--font-display);
  font-weight: 700;
  letter-spacing: -0.03em;
  line-height: 1.1;
}

h1 { font-size: 1.9rem; }
h2 { font-size: 1.4rem; }
h3 { font-size: 1.15rem; letter-spacing: -0.02em; }

a { color: var(--l-accent); }
a:hover { color: var(--l-accent-soft); }

code, pre, .mono {
  font-family: var(--font-mono);
}

/* Prose measure. The design's own pattern is a wide frame with narrow
   text inside it (.l-section-head 640px, .l-sub 560px) rather than one
   width for everything — app pages do the same. */
.measure { max-width: 68ch; }

/* --- app shell ---------------------------------------------------------- */

.site-header {
  border-bottom: 1px solid var(--l-line);
  background: var(--l-header-bg);
  backdrop-filter: blur(18px) saturate(140%);
  position: sticky;
  top: 0;
  z-index: 50;
}

.site-header-inner {
  max-width: var(--l-maxw);
  margin: 0 auto;
  padding: 14px 24px;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
}

.brand {
  display: inline-flex;
  align-items: center;
  gap: 9px;  /* matches .l-brand — see the note there */
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 21px;
  letter-spacing: -0.02em;
  text-decoration: none;
  color: var(--l-text);
}
.brand:hover { color: var(--l-text); }

/* Same single-source-of-truth sizing the landing brand mark uses. */
.brand-mark {
  /* 28px — same reasoning as .l-brand-mark: the mark's transparent padding
     was cropped out of the PNG, so this box size is now the mark's actual
     rendered size rather than ~47% of it. */
  width: 28px;
  height: 28px;
  flex-shrink: 0;
  object-fit: contain;
  filter: drop-shadow(0 0 12px var(--l-accent-haze));
}

.site-nav {
  display: flex;
  align-items: center;
  gap: 22px;
  font-size: 14px;
}

.site-nav a {
  color: var(--l-muted);
  text-decoration: none;
  transition: color 0.2s var(--l-ease);
}
.site-nav a:hover { color: var(--l-text); }
.site-nav a.is-active { color: var(--l-text); font-weight: 600; }
.site-nav form { margin: 0; display: inline-flex; }

.nav-avatar { position: relative; display: inline-flex; }
.nav-avatar-btn {
  display: inline-flex; align-items: center; justify-content: center;
  width: 32px; height: 32px; border-radius: 50%;
  background: var(--l-panel); border: 1px solid var(--l-line);
  color: var(--l-muted); cursor: pointer; padding: 0;
}
.nav-avatar-btn:hover { color: var(--l-text); border-color: var(--l-accent); }
.nav-avatar-menu {
  position: absolute; top: calc(100% + 8px); right: 0; z-index: 60;
  background: var(--l-panel); border: 1px solid var(--l-line);
  border-radius: 10px; padding: 8px; min-width: 130px;
  box-shadow: 0 12px 28px rgba(0, 0, 0, 0.35);
}
.nav-avatar-menu form { display: block; margin: 0; }
.nav-avatar-menu button { width: 100%; }

/* --- eyebrow / micro-label --------------------------------------------- */

.eyebrow {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  font-family: var(--font-display);
  font-size: 12px;
  letter-spacing: 0.28em;
  text-transform: uppercase;
  color: var(--l-muted);
  padding: 8px 16px;
  border: 1px solid var(--l-line);
  border-radius: 999px;
  background: var(--l-raise);
}

.eyebrow-dot {
  width: 6px;
  height: 6px;
  border-radius: 50%;
  flex: none;
  background: var(--l-accent);
  box-shadow: 0 0 10px 2px var(--l-accent-glow);
  animation: landing-pulse 2.4s var(--l-ease) infinite;
}

.micro-label {
  font-size: 11px;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--l-faint);
}

/* --- buttons ------------------------------------------------------------ */

button,
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 10px;
  font-family: var(--font-display);
  font-weight: 600;
  font-size: 15px;
  letter-spacing: -0.01em;
  padding: 12px 22px;
  border-radius: 12px;
  cursor: pointer;
  text-decoration: none;
  border: 1px solid transparent;
  background: var(--l-accent);
  color: var(--l-on-accent);
  box-shadow: 0 0 0 0 var(--l-accent-glow), 0 10px 40px -12px var(--l-accent-glow);
  transition: transform 0.25s var(--l-ease), box-shadow 0.35s var(--l-ease),
    background 0.25s var(--l-ease), border-color 0.25s var(--l-ease);
}

button:hover:not([disabled]),
.btn:hover {
  transform: translateY(-2px);
  color: var(--l-on-accent);
  box-shadow: 0 0 34px -2px var(--l-accent-glow), 0 18px 50px -12px var(--l-accent-glow);
}

.btn-ghost,
button.btn-ghost {
  background: var(--l-raise);
  border-color: var(--l-line-strong);
  color: var(--l-text);
  box-shadow: none;
}
.btn-ghost:hover,
button.btn-ghost:hover:not([disabled]) {
  border-color: var(--l-accent-line);
  background: var(--l-accent-wash);
  color: var(--l-text);
  box-shadow: none;
}

/* Destructive actions read as destructive — revoke, delete. */
.btn-danger,
button.btn-danger {
  background: transparent;
  border-color: var(--l-danger-line);
  color: var(--l-danger);
  box-shadow: none;
}
.btn-danger:hover,
button.btn-danger:hover:not([disabled]) {
  background: var(--l-danger-fill);
  border-color: var(--l-danger);
  color: var(--l-danger);
  box-shadow: none;
}

.btn-sm,
button.btn-sm {
  padding: 7px 14px;
  font-size: 13px;
  border-radius: 10px;
}

:focus-visible {
  outline: 2px solid var(--l-accent);
  outline-offset: 2px;
}

/* --- card --------------------------------------------------------------- */

.card {
  border: 1px solid var(--l-line);
  border-radius: var(--l-radius);
  background: var(--l-fill);
  padding: 24px;
}

.card-pad-lg { padding: 30px; }

/* --- code panel --------------------------------------------------------- */
/* The design's one code-display pattern (.l-term). Reused verbatim here so
   the SDK snippet and the remediation patch read as the same object as the
   landing page's terminal. */

.code-panel {
  border: 1px solid var(--l-line-strong);
  border-radius: 14px;
  background: var(--l-code-bg);
  overflow: hidden;
  /* Tokenized so the code panel's glow follows the accent; the dark
     accent bloomed orange under a white panel in light mode. */
  box-shadow: 0 40px 100px -40px var(--l-accent-glow),
    inset 0 0 0 1px var(--l-raise);
  margin: 1rem 0;
}

.code-panel-bar {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 12px 16px;
  border-bottom: 1px solid var(--l-line);
  background: var(--l-raise);
}

.code-panel-bar i {
  width: 11px;
  height: 11px;
  border-radius: 50%;
  background: var(--l-dot);
  flex: none;
}
.code-panel-bar i:first-child {
  background: var(--l-accent);
  box-shadow: 0 0 8px var(--l-accent-glow);
}

.code-panel-path {
  margin-left: 10px;
  font-family: var(--font-mono);
  font-size: 12px;
  color: var(--l-faint);
}

.code-panel-bar .code-panel-action { margin-left: auto; }

.code-panel pre {
  margin: 0;
  padding: 22px;
  overflow-x: auto;
  font-family: var(--font-mono);
  font-size: 13.5px;
  line-height: 1.9;
  color: var(--l-text);
}
.code-panel pre code { font-size: inherit; color: inherit; }

/* --- tables (hairline grid, per .l-metrics-grid) ------------------------ */

table {
  width: 100%;
  border-collapse: collapse;
}

th, td {
  text-align: left;
  padding: 0.7rem 0.85rem;
  border-bottom: 1px solid var(--l-line);
  font-size: 0.9rem;
}

thead th {
  font-family: var(--font-display);
  font-size: 11px;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--l-faint);
  font-weight: 600;
  border-bottom-color: var(--l-line-strong);
}

tbody tr { transition: background 0.2s var(--l-ease); }
tbody tr:hover { background: var(--l-raise); }

/* --- status badges ------------------------------------------------------ */

.status-completed,
.status-queued,
.status-failed,
.status-analysis_failed,
.status-captured {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font-family: var(--font-display);
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  padding: 4px 10px;
  border-radius: 999px;
  border: 1px solid currentColor;
}

.status-completed { color: var(--l-ok); background: var(--l-ok-fill); }
.status-queued { color: var(--l-warn); background: var(--l-warn-fill); }
.status-captured { color: var(--l-accent); background: var(--l-accent-haze); }
.status-failed,
.status-analysis_failed { color: var(--l-danger); background: var(--l-danger-fill); }

/* --- banners ------------------------------------------------------------ */
/* Four distinct weights. A "3 left" nudge and a "0 left, upgrade now" stop
   must not read the same — see DESIGN_SYSTEM.md §5. */

.banner {
  padding: 0.85rem 1.1rem;
  border-radius: 12px;
  margin-bottom: 1rem;
  font-size: 0.92rem;
  border: 1px solid var(--l-line);
  background: var(--l-raise);
  color: var(--l-muted);
}
.banner strong { color: var(--l-text); }
.banner a { font-weight: 600; }

.banner-info {
  border-color: var(--l-line);
  background: var(--l-raise);
  color: var(--l-muted);
}

.banner-warn {
  border-color: var(--l-warn-line);
  background: var(--l-warn-fill);
  color: var(--l-text);
}
.banner-warn strong { color: var(--l-warn); }
.banner-warn a { color: var(--l-warn); }

.banner-danger {
  border-left: 3px solid var(--l-danger);
  border-color: var(--l-danger-line);
  border-left-color: var(--l-danger);
  background: var(--l-danger-fill);
  color: var(--l-text);
}
.banner-danger strong { color: var(--l-danger); }
.banner-danger a { color: var(--l-danger); }

.banner-ok {
  border-color: var(--l-ok-line);
  background: var(--l-ok-fill);
  color: var(--l-text);
}
.banner-ok strong { color: var(--l-ok); }

/* `body > main`, not bare `main`: the landing page (templates/landing.html)
   has its own full-bleed <main> nested inside .landing, and this 960px
   dashboard measure was leaking into it — pinning the whole landing content
   column to 960px against the left edge while the nav and footer, which sit
   outside <main>, correctly spanned the full 1200px grid. Every other
   template renders <main> as a direct child of <body> via base.html, so
   this selector still covers all of them. */
/* Width: the app now uses the design's own 1200px container
   (--l-maxw) rather than the 960px it inherited before the redesign —
   that number was a pre-redesign default, not a decision. The design's
   pattern is a wide frame with narrow measures inside it (.measure,
   form inputs at 32rem, .measure prose at 68ch), so content that should
   stay readable is capped individually rather than by the shell. */
body > main {
  padding: 2rem 24px 4rem;
  max-width: var(--l-maxw);
  margin: 0 auto;
}

/* htmx 2.x dropped the auto-injected indicator stylesheet 1.x shipped, so
   this has to be declared here or hx-indicator does nothing visible.
   display (not opacity) so a hidden indicator reserves no layout space. */
.htmx-indicator { display: none; }
.htmx-request .htmx-indicator,
.htmx-request.htmx-indicator { display: inline-flex; }

/* Point 7b.6: in-flight feedback for the synchronous diagnose call.
   Laid out as a panel rather than an inline run of text — a 30-60s wait
   is a page state, not a footnote next to a button. */
.htmx-indicator {
  align-items: center;
  gap: 14px;
  margin-top: 1rem;
  padding: 16px 20px;
  border: 1px solid var(--l-line-strong);
  border-radius: 12px;
  background: var(--l-fill);
  font-size: 0.92rem;
  color: var(--l-muted);
}
.htmx-indicator strong { color: var(--l-text); display: block; }
.diagnose-stage { display: block; margin-top: 4px; font-size: 0.85em; color: var(--l-faint); }

/* The design's signature motif is the chronometer ring (.l-ring-1: a
   conic sweep masked to a 2px annulus, with an accent glow). This is that
   same arc at button scale, so a diagnosis in flight reads as the same
   visual language as the hero rather than a generic spinner. */
.spinner,
.ring-loader {
  width: 26px;
  height: 26px;
  flex: none;
  border-radius: 50%;
  background: conic-gradient(from 0deg,
    transparent 0deg,
    transparent 140deg,
    var(--l-accent-glow) 280deg,
    var(--l-accent-soft) 340deg,
    transparent 360deg);
  -webkit-mask: radial-gradient(farthest-side, transparent calc(100% - 3px), #000 calc(100% - 3px));
  mask: radial-gradient(farthest-side, transparent calc(100% - 3px), #000 calc(100% - 3px));
  filter: drop-shadow(0 0 6px var(--l-accent-glow));
  animation: spin 1.1s linear infinite;
}

@keyframes spin { to { transform: rotate(360deg); } }

/* A 30-60s spin is exactly the kind of motion that triggers vestibular
   symptoms; the copy alone still carries the meaning without it. The ring
   stays visible as a static arc so the panel doesn't lose its marker. */
@media (prefers-reduced-motion: reduce) {
  .spinner, .ring-loader { animation: none; }
}

/* hx-disabled-elt only sets the disabled attribute — without this the
   button looks identical while it's inert. */
button[disabled] {
  opacity: 0.55;
  cursor: not-allowed;
}

.diagnoses-remaining {
  font-size: 1.02rem;
}

/* --- Points 15-17: account forms ---------------------------------------- */

.form-hint {
  font-size: 0.85rem;
  color: var(--l-muted);
  margin: 0.35rem 0 1rem;
  max-width: 46ch;
}

form label {
  display: inline-block;
  margin-top: 1rem;
  font-family: var(--font-display);
  font-weight: 600;
  font-size: 12px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--l-faint);
}

form input[type="text"],
form input[type="email"],
form input[type="password"] {
  display: block;
  width: 100%;
  max-width: 32rem;
  padding: 0.7rem 0.85rem;
  margin: 0.4rem 0 0;
  font: inherit;
  color: var(--l-text);
  background: var(--l-raise);
  border: 1px solid var(--l-line-strong);
  border-radius: 10px;
  transition: border-color 0.2s var(--l-ease), background 0.2s var(--l-ease);
}

form input[type="text"]:focus,
form input[type="email"]:focus,
form input[type="password"]:focus {
  outline: none;
  border-color: var(--l-accent);
  background: var(--l-accent-wash);
  box-shadow: 0 0 0 3px var(--l-accent-haze);
}

form input::placeholder { color: var(--l-faint); }

/* The auth pages are a single narrow column, centered — a 1200px shell
   with a 32rem form flush left reads as a broken page. */
.auth-panel {
  max-width: 30rem;
  margin: 3rem auto;
}
.auth-panel form input { max-width: none; }
.auth-panel button[type="submit"] { width: 100%; margin-top: 1.5rem; }
.auth-panel h1 { margin-bottom: 0.35rem; }
.auth-lede { color: var(--l-muted); margin: 0 0 1.75rem; font-size: 0.95rem; }
.auth-alt {
  margin-top: 1.75rem;
  padding-top: 1.25rem;
  border-top: 1px solid var(--l-line);
}

/* --- Point 25: OAuth login buttons --------------------------------------- */

.auth-oauth {
  display: flex;
  flex-direction: column;
  gap: 0.75rem;
}
.auth-oauth .btn-ghost,
.auth-oauth a.btn-ghost {
  width: 100%;
  text-align: center;
}
.auth-oauth input { max-width: none; margin-bottom: 0.5rem; }
.auth-divider {
  display: flex;
  align-items: center;
  gap: 0.75rem;
  color: var(--l-faint);
  font-size: 0.85rem;
  margin: 1.5rem 0;
}
.auth-divider::before,
.auth-divider::after {
  content: "";
  flex: 1;
  height: 1px;
  background: var(--l-line);
}

/* --- Points 16-17: revealing an API key --------------------------------- */

.key-reveal {
  background: var(--l-fill);
  border: 1px solid var(--l-line-strong);
  border-radius: var(--l-radius);
  padding: 22px;
  margin: 1rem 0;
  max-width: 44rem;
}

.key-reveal-label {
  font-family: var(--font-display);
  font-size: 11px;
  text-transform: uppercase;
  letter-spacing: 0.18em;
  color: var(--l-faint);
  margin: 0 0 0.7rem;
}

.key-reveal-value {
  display: block;
  font-family: var(--font-mono);
  font-size: 0.95rem;
  word-break: break-all;
  color: var(--l-accent-soft);
  background: var(--l-code-bg);
  border: 1px solid var(--l-line);
  border-radius: 10px;
  padding: 0.8rem;
  margin-bottom: 0.8rem;
}

.key-table {
  border-collapse: collapse;
  width: 100%;
  max-width: 48rem;
  margin: 1rem 0;
}

.key-table th,
.key-table td {
  text-align: left;
  padding: 0.7rem 0.85rem;
  border-bottom: 1px solid var(--l-line);
  font-size: 0.9rem;
}
.key-table td code { font-family: var(--font-mono); color: var(--l-text); }

.key-inactive { color: var(--l-faint); text-decoration: line-through; }
.key-current { font-weight: 600; color: var(--l-accent-soft); }

/* Shoulder-surfing protection only — the key IS in the page source. */
.key-masked {
  color: transparent;
  text-shadow: 0 0 0.5rem rgba(255, 255, 255, 0.5);
  user-select: none;
}

.rotation-steps { max-width: 46rem; line-height: 1.7; color: var(--l-muted); }
.rotation-steps li { margin-bottom: 0.9rem; }
.rotation-steps li::marker { color: var(--l-accent); font-weight: 700; }
.rotation-steps form { display: inline-block; margin-top: 0.5rem; }
.key-table form { display: inline; margin: 0; }

.sign-in-methods { list-style: none; padding: 0; max-width: 28rem; }
.sign-in-methods li {
  display: flex; justify-content: space-between; align-items: center;
  padding: 0.6rem 0; border-bottom: 1px solid var(--l-border);
  color: var(--l-muted);
}

/* --- page header row (title + actions) ---------------------------------- */

.page-head {
  display: flex;
  justify-content: space-between;
  align-items: flex-end;
  gap: 1rem;
  flex-wrap: wrap;
  margin-bottom: 1.75rem;
}
.page-head p { color: var(--l-muted); margin: 0.4rem 0 0; font-size: 0.95rem; }

.stack > * + * { margin-top: 1rem; }

/* --- empty state -------------------------------------------------------- */

.empty-state {
  border: 1px solid var(--l-line);
  border-radius: var(--l-radius);
  background: var(--l-fill);
  padding: 40px;
  text-align: left;
}
.empty-state > p { color: var(--l-muted); max-width: 60ch; }
.empty-state h2 { margin: 0 0 0.5rem; }

/* --- definition rows (trace meta, diagnosis fields) --------------------- */

.field { margin: 0 0 1.4rem; }
.field-label {
  font-family: var(--font-display);
  font-size: 11px;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--l-faint);
  margin: 0 0 0.35rem;
}
.field-value { margin: 0; color: var(--l-text); max-width: 78ch; }
.field-value.mono { font-family: var(--font-mono); color: var(--l-accent-soft); }
.field-value-stat {
  font-family: var(--font-display);
  font-size: 1.9rem;
  font-weight: 700;
  letter-spacing: -0.02em;
}

.meta-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
  gap: 1px;
  background: var(--l-line);
  border: 1px solid var(--l-line);
  border-radius: var(--l-radius);
  overflow: hidden;
  margin-bottom: 1.75rem;
}
.meta-grid .meta-cell { background: var(--l-bg-2); padding: 18px 20px; }
.meta-grid .field { margin: 0; }

@media (max-width: 640px) {
  body > main { padding: 1.5rem 18px 3rem; }
  .site-header-inner { padding: 12px 18px; }
  .page-head { align-items: flex-start; }

  /* The header did not fit a phone: the brand lockup plus four nav links
     plus the avatar measured 469px inside a 375px viewport, so EVERY page
     scrolled sideways — the nav simply overflowed, since nothing here
     wrapped, shrank or scrolled.

     Dropping the wordmark is what buys the room; the mark alone is still
     unambiguous at this size, and the accessible name survives because
     .brand-word is hidden visually rather than removed. Tightening the
     gap and type closes the rest. */
  .brand-word {
    position: absolute;
    width: 1px;
    height: 1px;
    margin: -1px;
    padding: 0;
    overflow: hidden;
    clip-path: inset(50%);
    white-space: nowrap;
  }
  .site-nav { gap: 15px; font-size: 13px; }
  .site-header-inner { gap: 12px; }
}

/* =====================================================================
   LANDING PAGE (templates/landing.html)
   Everything below is scoped under .landing so it can't bleed into the
   dashboard/settings/billing pages, which already own --bg/--accent/etc.
   in the :root block above. Every class is l-prefixed for the same
   reason. Ported from a static design (plain CSS, no build step) that
   already matched this repo's own conventions.
   ===================================================================== */
.landing, .landing * {
  margin: 0;
  padding: 0;
  box-sizing: border-box;
}

/* Tokens are no longer declared here — they moved to :root so the app
   pages read the same set. This block keeps only the landing page's own
   composition. */
.landing {
  position: relative;
  font-family: var(--font-body);
  background: var(--l-bg);
  color: var(--l-text);
  line-height: 1.6;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
  overflow-x: hidden;
  min-height: 100vh;
}

.landing ::selection { background: var(--l-accent); color: #0a0a0b; }
.landing a { color: inherit; text-decoration: none; }
.landing h1, .landing h2, .landing h3, .landing h4 {
  font-family: var(--font-display);
  line-height: 1.02;
  letter-spacing: -0.03em;
  font-weight: 700;
}

.landing .l-wrap {
  width: 100%;
  max-width: var(--l-maxw);
  margin: 0 auto;
  padding: 0 24px;
}

.landing .l-mono {
  font-family: var(--font-mono);
  font-feature-settings: "tnum" 1;
}

.landing .l-accent { color: var(--l-accent); }

.landing .l-eyebrow {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  font-family: var(--font-display);
  font-size: 12px;
  letter-spacing: 0.28em;
  text-transform: uppercase;
  color: var(--l-muted);
  padding: 8px 16px;
  border: 1px solid var(--l-line);
  border-radius: 999px;
  background: rgba(255, 255, 255, 0.02);
  backdrop-filter: blur(6px);
}
.landing .l-eyebrow .l-dot {
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: var(--l-accent);
  box-shadow: 0 0 10px 2px var(--l-accent-glow);
  animation: landing-pulse 2.4s var(--l-ease) infinite;
}
@keyframes landing-pulse {
  0%, 100% { opacity: 1; transform: scale(1); }
  50% { opacity: 0.45; transform: scale(0.75); }
}

.landing .l-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 10px;
  font-family: var(--font-display);
  font-weight: 600;
  font-size: 15px;
  letter-spacing: -0.01em;
  padding: 14px 26px;
  border-radius: 12px;
  cursor: pointer;
  border: 1px solid transparent;
  transition: transform 0.25s var(--l-ease), box-shadow 0.35s var(--l-ease),
    background 0.25s var(--l-ease), border-color 0.25s var(--l-ease);
}
.landing .l-btn-primary {
  background: var(--l-accent);
  color: #0a0a0b;
  box-shadow: 0 0 0 0 var(--l-accent-glow), 0 10px 40px -12px var(--l-accent-glow);
}
.landing .l-btn-primary:hover {
  transform: translateY(-2px);
  box-shadow: 0 0 34px -2px var(--l-accent-glow), 0 18px 50px -12px var(--l-accent-glow);
}
.landing .l-btn-ghost {
  background: rgba(255, 255, 255, 0.02);
  border-color: var(--l-line-strong);
  color: var(--l-text);
}
.landing .l-btn-ghost:hover {
  transform: translateY(-2px);
  border-color: rgba(255, 122, 41, 0.5);
  background: rgba(255, 90, 0, 0.06);
}
.landing .l-btn svg { width: 16px; height: 16px; }

/* "Soon" — a plan that is advertised but not yet purchasable. Rendered as a
   <span>, not an <a>, because it goes nowhere; these rules make it LOOK
   like it goes nowhere too. It keeps .l-btn and .l-metric-cta so the card's
   flex alignment (align-self: stretch, margin-top: auto) is unchanged and
   every CTA in the row still lines up. */
.landing .l-btn.is-soon,
.landing .l-btn-primary.is-soon,
.landing .l-btn-ghost.is-soon {
  background: rgba(255, 255, 255, 0.02);
  border-color: var(--l-line);
  color: var(--l-muted);
  box-shadow: none;
  cursor: default;
  /* Belt and braces with the <span>: no hover lift, no focus, no click. */
  pointer-events: none;
}
.landing .l-btn.is-soon:hover {
  transform: none;
  box-shadow: none;
}

/* ---------- nav ---------- */
.landing .l-nav {
  position: fixed;
  inset: 0 0 auto 0;
  z-index: 100;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 16px 0;
  border-bottom: 1px solid transparent;
}
.landing .l-nav.l-nav-scrolled {
  background: rgba(10, 10, 11, 0.72);
  backdrop-filter: blur(18px) saturate(140%);
  border-bottom: 1px solid var(--l-line);
}
.landing .l-nav-inner {
  width: 100%;
  max-width: var(--l-maxw);
  padding: 0 24px;
  display: flex;
  align-items: center;
  justify-content: space-between;
}
.landing .l-brand {
  display: flex;
  align-items: center;
  /* The apparent gap used to be ~21px: 11px here plus ~10px of transparent
     padding baked into the mark's right edge. Cropping the PNG removed
     that dead space; 9px is the deliberate remainder. */
  gap: 9px;
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 21px;
  letter-spacing: -0.02em;
}
/* Single source of truth for the brand mark's size wherever .l-brand
   appears (nav, footer) — previously the nav copy relied on its own
   inline width/height while the footer copy had a second, separate CSS
   override with a different value, so the two drifted out of sync. */
.landing .l-brand-mark {
  /* 30px, not the old 38px, because the artwork changed under it: the PNG
     used to carry ~26% transparent padding on every edge, so a 38px box
     only ever showed a 17.9px mark. The padding is cropped out now, so the
     box size IS the mark size — 30px renders a visibly larger mark than
     before while taking less room. See DECISIONS.md. */
  width: 30px;
  height: 30px;
  flex-shrink: 0;
  object-fit: contain;
  filter: drop-shadow(0 0 12px var(--l-accent-haze));
}
.landing .l-tag {
  font-size: 10px;
  letter-spacing: 0.24em;
  color: var(--l-faint);
  border-left: 1px solid var(--l-line-strong);
  padding-left: 12px;
  margin-left: 4px;
  text-transform: uppercase;
}
.landing .l-nav-links {
  display: flex;
  align-items: center;
  gap: 34px;
  font-size: 14px;
  color: var(--l-muted);
}
.landing .l-nav-links a { transition: color 0.2s var(--l-ease); }
.landing .l-nav-links a:hover { color: var(--l-text); }
.landing .l-nav-cta { display: flex; align-items: center; gap: 14px; }
.landing .l-nav-cta .l-btn { padding: 10px 20px; font-size: 14px; }
.landing .l-nav-signin { font-size: 14px; color: var(--l-muted); }
.landing .l-nav-signin:hover { color: var(--l-text); }

/* ---------- hero ---------- */
.landing .l-hero {
  position: relative;
  min-height: 100vh;
  display: flex;
  align-items: center;
  justify-content: center;
  text-align: left;
  padding: 120px 0 88px;
  overflow: hidden;
  isolation: isolate;
}
.landing .l-hero::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -3;
  background:
    radial-gradient(60% 45% at 50% 42%, rgba(255, 90, 0, 0.10), transparent 70%),
    radial-gradient(40% 30% at 78% 18%, rgba(255, 90, 0, 0.06), transparent 70%),
    radial-gradient(40% 30% at 20% 80%, rgba(255, 90, 0, 0.05), transparent 70%);
}
.landing .l-hero::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -3;
  background-image:
    linear-gradient(var(--l-line) 1px, transparent 1px),
    linear-gradient(90deg, var(--l-line) 1px, transparent 1px);
  background-size: 64px 64px;
  mask-image: radial-gradient(70% 60% at 50% 45%, #000 0%, transparent 78%);
  -webkit-mask-image: radial-gradient(70% 60% at 50% 45%, #000 0%, transparent 78%);
  opacity: 0.5;
}

/* Centred on the diagnosis card rather than the page, so the card reads as
   sitting inside the reactor instead of colliding with its rings. Returns
   to centre below 900px, where the layout is a single column again. */
.landing .l-chrono {
  position: absolute;
  top: 50%;
  left: 72%;
  translate: -50% -50%;
  width: min(120vw, 1020px);
  aspect-ratio: 1;
  z-index: -2;
  pointer-events: none;
}
@media (max-width: 900px) {
  .landing .l-chrono { top: 42%; left: 50%; }
}
.landing .l-ring { position: absolute; inset: 0; border-radius: 50%; }
.landing .l-ring-1 {
  background:
    conic-gradient(from 0deg,
      transparent 0deg,
      rgba(255, 90, 0, 0) 200deg,
      rgba(255, 90, 0, 0.5) 320deg,
      rgba(255, 122, 41, 0.9) 352deg,
      transparent 360deg);
  -webkit-mask: radial-gradient(farthest-side, transparent calc(100% - 2px), #000 calc(100% - 2px));
  mask: radial-gradient(farthest-side, transparent calc(100% - 2px), #000 calc(100% - 2px));
  filter: drop-shadow(0 0 12px var(--l-accent-glow));
  animation: landing-spin 18s linear infinite;
  opacity: 0.9;
}
.landing .l-ring-2 {
  inset: 9%;
  background: repeating-conic-gradient(
    from 0deg,
    rgba(255, 255, 255, 0.16) 0deg 0.4deg,
    transparent 0.4deg 6deg
  );
  -webkit-mask: radial-gradient(farthest-side, transparent calc(100% - 16px), #000 calc(100% - 16px) calc(100% - 2px), transparent calc(100% - 2px));
  mask: radial-gradient(farthest-side, transparent calc(100% - 16px), #000 calc(100% - 16px) calc(100% - 2px), transparent calc(100% - 2px));
  animation: landing-spin-rev 120s linear infinite;
}
.landing .l-ring-3 {
  inset: 20%;
  border: 1px solid var(--l-line-strong);
  box-shadow: inset 0 0 60px rgba(255, 90, 0, 0.05);
}
.landing .l-ring-4 {
  inset: 30%;
  background:
    conic-gradient(from 90deg,
      rgba(255, 122, 41, 0.85),
      transparent 60deg,
      transparent 300deg,
      rgba(255, 90, 0, 0.4) 360deg);
  -webkit-mask: radial-gradient(farthest-side, transparent calc(100% - 1.5px), #000 calc(100% - 1.5px));
  mask: radial-gradient(farthest-side, transparent calc(100% - 1.5px), #000 calc(100% - 1.5px));
  filter: drop-shadow(0 0 8px var(--l-accent-glow));
  animation: landing-spin-rev 26s linear infinite;
}
.landing .l-ring-core {
  inset: 40%;
  background: radial-gradient(circle, rgba(255, 90, 0, 0.16), transparent 68%);
  filter: blur(6px);
  animation: landing-breathe 6s var(--l-ease) infinite;
}
.landing .l-orbit {
  position: absolute;
  inset: 9%;
  border-radius: 50%;
  animation: landing-spin 14s linear infinite;
}
.landing .l-orbit.l-orbit-rev { inset: 20%; animation: landing-spin-rev 22s linear infinite; }
.landing .l-orbit .l-sat {
  position: absolute;
  top: -4px;
  left: calc(50% - 4px);
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: var(--l-accent);
  box-shadow: 0 0 14px 3px var(--l-accent-glow);
}
.landing .l-orbit.l-orbit-rev .l-sat {
  background: var(--l-accent-soft);
  width: 6px;
  height: 6px;
}

@keyframes landing-spin { to { transform: rotate(360deg); } }
@keyframes landing-spin-rev { to { transform: rotate(-360deg); } }
@keyframes landing-spin-slow { to { transform: rotate(360deg); } }
@keyframes landing-breathe {
  0%, 100% { opacity: 0.6; transform: scale(1); }
  50% { opacity: 1; transform: scale(1.12); }
}

/* Split hero: copy left, a real diagnosis right. The previous centred
   single column paired a 900px container with a 120px headline, so each
   .l-line wrapped and the "two line" headline rendered as four — 490px of
   type that pushed the sub, both CTAs and the ticker below the fold on any
   laptop. Two columns spend the width instead of the height. */
.landing .l-hero-content {
  position: relative;
  z-index: 2;
  display: grid;
  grid-template-columns: 1.05fr 0.95fr;
  align-items: center;
  gap: 64px;
  max-width: var(--l-maxw);
  margin: 0 auto;
}
.landing .l-hero-copy {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  min-width: 0;
}
.landing .l-hero h1 {
  font-size: clamp(2.25rem, 3.9vw, 3.25rem);
  margin: 24px 0 0;
  letter-spacing: -0.045em;
  font-weight: 700;
}
.landing .l-hero h1 .l-line { display: block; }
/* Solid brand orange, not the white-to-orange gradient this used to be.
   The gradient needed background-clip:text with color:transparent, which
   dragged along two problems a plain color simply doesn't have: an
   @supports guard, because without clipping support the line rendered
   invisible against the dark hero; and a ban on `filter`, because
   drop-shadow on a clipped-background element makes Chrome rasterize the
   glyphs from their transparent fill and paint the line nearly black.
   The hero glow still comes from .l-hero::before. */
.landing .l-hero h1 .l-accent-line {
  color: var(--l-accent);
}
.landing .l-hero p.l-sub {
  margin-top: 22px;
  max-width: 520px;
  font-size: clamp(1rem, 1.4vw, 1.12rem);
  color: var(--l-muted);
  text-wrap: balance;
}
.landing .l-hero-cta {
  margin-top: 32px;
  display: flex;
  gap: 16px;
  flex-wrap: wrap;
  justify-content: flex-start;
}
/* The centred call-to-action band lower down reuses .l-hero-cta and still
   wants its buttons centred — restored at .l-cta .l-hero-cta below. */

/* Was a four-cell bordered grid ~140px tall. In the split hero the same four
   facts ride as one wrapped text row under the buttons, which is ~90px of
   height back and reads as a caption rather than competing with the card. */
.landing .l-ticker {
  position: relative;
  z-index: 2;
  margin-top: 32px;
  padding: 0;
  list-style: none;
  display: flex;
  flex-wrap: wrap;
  gap: 8px 22px;
  font-size: 12.5px;
  color: var(--l-faint);
}
.landing .l-ticker li {
  display: flex;
  align-items: baseline;
  gap: 6px;
}
.landing .l-ticker li + li::before {
  content: "";
  width: 3px;
  height: 3px;
  border-radius: 50%;
  background: var(--l-line-strong);
  margin-right: 16px;
  align-self: center;
}
.landing .l-ticker b {
  font-family: var(--font-display);
  font-size: 15px;
  font-weight: 700;
  letter-spacing: -0.02em;
  color: var(--l-text);
}

/* ---------- hero diagnosis card ---------- */
.landing .l-hero-proof {
  position: relative;
  z-index: 2;
  min-width: 0;
}
.landing .l-diag {
  margin: 0;
  border: 1px solid var(--l-line-strong);
  border-radius: var(--l-radius);
  background: var(--l-panel);
  box-shadow: 0 30px 70px -30px rgba(0, 0, 0, 0.9), 0 0 0 1px rgba(255, 255, 255, 0.02);
  overflow: hidden;
}
.landing .l-diag-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  padding: 13px 18px;
  border-bottom: 1px solid var(--l-line);
  background: linear-gradient(180deg, rgba(255, 255, 255, 0.035), rgba(255, 255, 255, 0));
}
.landing .l-diag-badge {
  font-size: 10.5px;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--l-accent-soft);
}
.landing .l-diag-lang {
  font-size: 11px;
  color: var(--l-faint);
  border: 1px solid var(--l-line);
  border-radius: 6px;
  padding: 2px 8px;
}
.landing .l-diag-field { padding: 15px 18px 0; }
.landing .l-diag-field:last-of-type { padding-bottom: 16px; }
.landing .l-diag-k {
  display: block;
  font-size: 10.5px;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--l-faint);
  margin-bottom: 7px;
}
.landing .l-diag-v {
  margin: 0;
  font-size: 13.5px;
  line-height: 1.55;
  color: var(--l-text);
}
.landing .l-diag-v code {
  font-size: 0.94em;
  color: var(--l-accent-soft);
  background: none;
  padding: 0;
}
.landing .l-diag-loc { color: var(--l-accent-soft); font-size: 13px; }
.landing .l-diag-patch {
  margin: 0;
  padding: 12px 14px;
  border: 1px solid var(--l-line);
  border-radius: 10px;
  background: var(--l-code-bg);
  font-size: 12.5px;
  line-height: 1.6;
  color: var(--l-muted);
  overflow-x: auto;
}
.landing .l-diag-patch .l-add { color: var(--l-ok); display: block; }
.landing .l-diag-more {
  display: block;
  padding: 13px 18px;
  border-top: 1px solid var(--l-line);
  font-size: 12.5px;
  color: var(--l-muted);
  transition: color 0.18s var(--l-ease), background 0.18s var(--l-ease);
}
.landing .l-diag-more:hover { color: var(--l-accent-soft); background: rgba(255, 255, 255, 0.02); }

.landing .l-scroll-hint {
  position: absolute;
  bottom: 28px;
  left: 50%;
  translate: -50% 0;
  z-index: 2;
  font-size: 11px;
  letter-spacing: 0.24em;
  text-transform: uppercase;
  color: var(--l-faint);
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 10px;
}
.landing .l-scroll-hint .l-scroll-bar {
  width: 1px;
  height: 40px;
  background: linear-gradient(var(--l-accent), transparent);
  animation: landing-drop 2s var(--l-ease) infinite;
}
@keyframes landing-drop {
  0% { transform: scaleY(0); transform-origin: top; opacity: 0; }
  40% { transform: scaleY(1); opacity: 1; }
  100% { transform: scaleY(1); transform-origin: bottom; opacity: 0; }
}

/* ---------- section scaffold ---------- */
.landing section { position: relative; }
.landing .l-section-pad { padding: 120px 0; }
.landing .l-section-head { max-width: 640px; margin-bottom: 64px; }
.landing .l-section-head.l-center { margin-left: auto; margin-right: auto; text-align: center; }
.landing .l-section-head h2 {
  font-size: clamp(2rem, 4.5vw, 3.4rem);
  margin-top: 20px;
  text-wrap: balance;
}
.landing .l-section-head p {
  color: var(--l-muted);
  margin-top: 20px;
  font-size: 1.05rem;
  text-wrap: pretty;
}

/* ---------- bento features ---------- */
.landing .l-bento {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  grid-auto-rows: minmax(220px, auto);
  gap: 18px;
}
.landing .l-card {
  position: relative;
  border: 1px solid var(--l-line);
  border-radius: var(--l-radius);
  background: linear-gradient(180deg, rgba(255, 255, 255, 0.028), rgba(255, 255, 255, 0.006));
  padding: 30px;
  overflow: hidden;
  transition: border-color 0.35s var(--l-ease), transform 0.35s var(--l-ease),
    box-shadow 0.35s var(--l-ease);
}
.landing .l-card:hover {
  border-color: rgba(255, 122, 41, 0.4);
  transform: translateY(-4px);
  box-shadow: 0 24px 60px -30px var(--l-accent-glow), inset 0 0 40px rgba(255, 90, 0, 0.04);
}
.landing .l-card::after {
  content: "";
  position: absolute;
  top: -40%;
  right: -30%;
  width: 60%;
  height: 120%;
  background: radial-gradient(circle, var(--l-accent-haze), transparent 65%);
  opacity: 0;
  transition: opacity 0.4s var(--l-ease);
  pointer-events: none;
}
.landing .l-card:hover::after { opacity: 1; }
.landing .l-card .l-ico {
  width: 46px;
  height: 46px;
  border-radius: 12px;
  display: grid;
  place-items: center;
  border: 1px solid var(--l-line-strong);
  background: rgba(255, 90, 0, 0.08);
  margin-bottom: 22px;
  color: var(--l-accent);
}
.landing .l-card .l-ico svg { width: 22px; height: 22px; }
.landing .l-card h3 { font-size: 1.28rem; margin-bottom: 10px; letter-spacing: -0.02em; }
.landing .l-card p { color: var(--l-muted); font-size: 0.96rem; }
.landing .l-card code {
  font-family: var(--font-mono);
  color: var(--l-accent-soft);
}
.landing .l-card.l-span-2 { grid-column: span 2; }
.landing .l-card.l-tall { grid-row: span 2; }

.landing .l-card.l-feature-lead {
  display: flex;
  flex-direction: column;
  justify-content: space-between;
}
.landing .l-mini-graph {
  margin-top: 26px;
  height: 120px;
  display: flex;
  align-items: flex-end;
  gap: 5px;
}
.landing .l-mini-graph i {
  flex: 1;
  background: linear-gradient(180deg, var(--l-accent), var(--l-accent-deep));
  border-radius: 3px 3px 0 0;
  opacity: 0.85;
  box-shadow: 0 0 12px -2px var(--l-accent-glow);
  animation: landing-bar 3s var(--l-ease) infinite;
  transform-origin: bottom;
}
@keyframes landing-bar {
  0%, 100% { transform: scaleY(0.55); }
  50% { transform: scaleY(1); }
}
.landing .l-mini-graph i:nth-child(1) { animation-delay: 0s; }
.landing .l-mini-graph i:nth-child(2) { animation-delay: 0.15s; }
.landing .l-mini-graph i:nth-child(3) { animation-delay: 0.3s; }
.landing .l-mini-graph i:nth-child(4) { animation-delay: 0.45s; }
.landing .l-mini-graph i:nth-child(5) { animation-delay: 0.6s; }
.landing .l-mini-graph i:nth-child(6) { animation-delay: 0.75s; }
.landing .l-mini-graph i:nth-child(7) { animation-delay: 0.9s; }
.landing .l-mini-graph i:nth-child(8) { animation-delay: 1.05s; }
.landing .l-mini-graph i:nth-child(9) { animation-delay: 1.2s; }
.landing .l-mini-graph i:nth-child(10) { animation-delay: 1.35s; }
.landing .l-mini-graph i:nth-child(11) { animation-delay: 1.5s; }
.landing .l-mini-graph i:nth-child(12) { animation-delay: 1.65s; }

/* ---------- plan-tier metrics ---------- */
.landing .l-metrics {
  border-top: 1px solid var(--l-line);
  border-bottom: 1px solid var(--l-line);
  background:
    radial-gradient(50% 120% at 50% 0%, rgba(255, 90, 0, 0.07), transparent 70%),
    var(--l-bg-2);
}
.landing .l-metrics-grid {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 1px;
  background: var(--l-line);
  border: 1px solid var(--l-line);
  border-radius: var(--l-radius);
  overflow: hidden;
}
.landing .l-metric {
  background: var(--l-bg-2);
  padding: 44px 28px;
  text-align: center;
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: center;
}
/* The one tier with a self-serve checkout path — the trial and enterprise
   CTAs both route through it too (signup or a human), so this is the tier
   worth visually favoring, not just labeling. */
.landing .l-metric-featured {
  background: var(--l-panel);
  box-shadow: inset 0 2px 0 0 var(--l-accent);
}
.landing .l-metric-tag {
  position: absolute;
  top: 14px;
  right: 14px;
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--l-accent-soft);
  background: var(--l-accent-haze);
  border: 1px solid var(--l-accent-glow);
  border-radius: 999px;
  padding: 3px 10px;
}
.landing .l-metric-big {
  font-family: var(--font-display);
  font-size: clamp(1.8rem, 3.6vw, 2.6rem);
  font-weight: 700;
  letter-spacing: -0.03em;
  /* Solid brand orange — see .l-accent-line above for why the gradient
     (and its @supports guard) is gone. */
  color: var(--l-accent);
}
.landing .l-metric-price {
  margin-top: 14px;
  font-size: 1.4rem;
  font-weight: 600;
  color: var(--l-text);
}
.landing .l-metric-price span {
  font-size: 0.85rem;
  font-weight: 500;
  color: var(--l-muted);
}
.landing .l-metric-cap {
  margin-top: 10px;
  margin-bottom: 24px;
  font-size: 13px;
  color: var(--l-muted);
}
.landing .l-metric-cta {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  align-self: stretch;
  margin-top: auto;
  padding: 12px 20px;
  font-size: 14px;
  line-height: 1.3;
}

/* ---------- diagnostics terminal showcase ---------- */
.landing .l-showcase .l-wrap {
  display: grid;
  grid-template-columns: 1fr 1.15fr;
  gap: 64px;
  align-items: center;
}
.landing .l-term {
  position: relative;
  border: 1px solid var(--l-line-strong);
  border-radius: 14px;
  background: #0c0c0e;
  overflow: hidden;
  box-shadow: 0 40px 100px -40px rgba(255, 90, 0, 0.28),
    inset 0 0 0 1px rgba(255, 255, 255, 0.02);
}
.landing .l-term-bar {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 14px 16px;
  border-bottom: 1px solid var(--l-line);
  background: rgba(255, 255, 255, 0.02);
}
.landing .l-term-bar i { width: 11px; height: 11px; border-radius: 50%; background: #2a2a2e; }
.landing .l-term-bar i:first-child { background: var(--l-accent); box-shadow: 0 0 8px var(--l-accent-glow); }
.landing .l-term-path {
  margin-left: 10px;
  font-family: var(--font-mono);
  font-size: 12px;
  color: var(--l-faint);
}
.landing .l-term-body {
  padding: 22px;
  font-family: var(--font-mono);
  font-size: 13.5px;
  line-height: 1.9;
}
.landing .l-term-line { display: block; white-space: pre-wrap; color: var(--l-muted); }
.landing .l-c-p { color: var(--l-accent); }
.landing .l-c-g { color: #4ade80; }
.landing .l-c-d { color: var(--l-faint); }
.landing .l-c-w { color: var(--l-text); }
.landing .l-cursor {
  display: inline-block;
  width: 8px;
  height: 15px;
  background: var(--l-accent);
  vertical-align: middle;
  animation: landing-blink 1.1s steps(1) infinite;
}
@keyframes landing-blink { 50% { opacity: 0; } }

.landing .l-showcase .l-feat-list { margin-top: 34px; display: grid; gap: 22px; }
.landing .l-showcase .l-feat { display: flex; gap: 16px; align-items: flex-start; }
.landing .l-showcase .l-feat .l-feat-k {
  flex: none;
  width: 34px; height: 34px;
  border-radius: 9px;
  display: grid; place-items: center;
  border: 1px solid var(--l-line-strong);
  background: rgba(255, 90, 0, 0.08);
  color: var(--l-accent);
}
.landing .l-showcase .l-feat .l-feat-k svg { width: 16px; height: 16px; }
.landing .l-showcase .l-feat h4 { font-size: 1.05rem; margin-bottom: 3px; }
.landing .l-showcase .l-feat p { color: var(--l-muted); font-size: 0.93rem; }

/* ---------- final cta ---------- */
.landing .l-cta { position: relative; overflow: hidden; text-align: center; }
.landing .l-cta::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -1;
  background: radial-gradient(60% 70% at 50% 60%, rgba(255, 90, 0, 0.14), transparent 70%);
}
.landing .l-panel {
  position: relative;
  border: 1px solid var(--l-line);
  border-radius: 24px;
  padding: 80px 40px;
  background: linear-gradient(180deg, rgba(255, 255, 255, 0.03), rgba(255, 255, 255, 0.005));
  overflow: hidden;
}
.landing .l-halo {
  position: absolute;
  top: -60%;
  left: 50%;
  translate: -50% 0;
  width: 700px; height: 700px;
  border-radius: 50%;
  border: 1px solid rgba(255, 90, 0, 0.14);
  box-shadow: 0 0 120px -20px var(--l-accent-haze);
  animation: landing-spin-slow 60s linear infinite;
  pointer-events: none;
}
.landing .l-halo::before {
  content: "";
  position: absolute;
  top: -4px; left: calc(50% - 4px);
  width: 8px; height: 8px; border-radius: 50%;
  background: var(--l-accent);
  box-shadow: 0 0 16px 4px var(--l-accent-glow);
}
.landing .l-cta h2 { font-size: clamp(2.2rem, 5.5vw, 4.2rem); position: relative; }
.landing .l-cta p {
  color: var(--l-muted);
  margin: 22px auto 40px;
  max-width: 480px;
  position: relative;
}
.landing .l-cta .l-hero-cta { justify-content: center; }

/* ---------- footer ---------- */
.landing .l-footer { border-top: 1px solid var(--l-line); padding: 64px 0 40px; background: var(--l-bg-2); }
.landing .l-footer-top {
  display: grid;
  grid-template-columns: 1.6fr 1fr 1fr;
  gap: 40px;
  padding-bottom: 48px;
  border-bottom: 1px solid var(--l-line);
}
.landing .l-footer-col h5 {
  font-family: var(--font-display);
  font-size: 12px;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--l-faint);
  margin-bottom: 18px;
}
.landing .l-footer-col a {
  display: block;
  color: var(--l-muted);
  font-size: 14px;
  margin-bottom: 12px;
  transition: color 0.2s var(--l-ease);
}
.landing .l-footer-col a:hover { color: var(--l-accent); }
.landing .l-footer-about { color: var(--l-muted); font-size: 14px; margin-top: 18px; max-width: 280px; }
.landing .l-footer-bottom {
  display: flex;
  align-items: center;
  padding-top: 28px;
  color: var(--l-faint);
  font-size: 13px;
  flex-wrap: wrap;
  gap: 14px;
}

/* ---------- responsive ---------- */
@media (max-width: 1100px) {
  .landing .l-metrics-grid { grid-template-columns: repeat(2, 1fr); }
}
@media (max-width: 900px) {
  .landing .l-nav-links { display: none; }
  .landing .l-bento { grid-template-columns: repeat(2, 1fr); }
  .landing .l-card.l-tall { grid-row: span 1; }
  .landing .l-metrics-grid { grid-template-columns: 1fr; }
  .landing .l-showcase .l-wrap { grid-template-columns: 1fr; gap: 40px; }
  .landing .l-footer-top { grid-template-columns: 1fr 1fr; }
  /* Split hero collapses to one column, card under the buttons. Centred
     here because a single narrow column has no second column to balance. */
  .landing .l-hero { text-align: center; }
  .landing .l-hero-content { grid-template-columns: 1fr; gap: 44px; }
  .landing .l-hero-copy { align-items: center; }
  .landing .l-hero-cta { justify-content: center; }
  .landing .l-ticker { justify-content: center; }
  .landing .l-diag { text-align: left; }
}
@media (max-width: 560px) {
  .landing .l-wrap, .landing .l-nav-inner { padding: 0 18px; }
  /* The "crash diagnosis" tag makes the brand lockup 310px wide. Inside a
     339px nav that leaves 29px for a 77px "Start free" button, so 30px of
     the landing page's primary CTA was cut off — invisibly, because
     .landing has overflow-x: hidden, so the page never scrolled and no
     overflow test caught it. The tag is a nicety; the CTA is the whole
     point of the page. */
  .landing .l-tag { display: none; }
  .landing .l-section-pad { padding: 84px 0; }
  .landing .l-bento { grid-template-columns: 1fr; }
  .landing .l-card.l-span-2 { grid-column: span 1; }
  .landing .l-footer-top { grid-template-columns: 1fr; }
  .landing .l-nav-signin { display: none; }
  .landing .l-hero { padding-top: 118px; }
  /* The longer sentence has to stay on ONE line here or the headline wraps
     to three and pushes the CTAs off a phone screen. The column is 322px.
     Sora runs ~8% wider than the Space Grotesk this replaced, which ate the
     old margin entirely: at 2rem it measures 322.0px in that 322px column —
     it "fits" by nothing at all, and any rounding difference breaks it.
     1.85rem measures ~298px, restoring a real ~24px of slack. Measured in
     the browser with letter-spacing applied, not from canvas metrics —
     measureText ignores letter-spacing and overstates this by ~19px. */
  .landing .l-hero h1 { font-size: 1.85rem; }
  /* Four bulleted facts on one phone-width row is unreadable; drop the
     dot separators and let them stack two-up instead. */
  .landing .l-ticker { gap: 8px 18px; }
  .landing .l-ticker li + li::before { display: none; }
}

/* 320px phones (iPhone SE 1st gen, older Android) leave only 284px for the
   headline, and the longest sentence needs 298px at the size above — so it
   wrapped to three lines and pushed the CTAs off-screen. This predates the
   Sora change: Space Grotesk needed the same 298px, so it was broken here
   too and simply never measured this narrow. Only 359px and down pays the
   smaller size; 360px+ keeps the larger headline, where it already has
   26px+ of slack. 1.65rem measures ~266px against that 284px. */
@media (max-width: 359px) {
  .landing .l-hero h1 { font-size: 1.65rem; }
}

@media (prefers-reduced-motion: reduce) {
  .landing *, .landing *::before, .landing *::after {
    animation: none !important;
    scroll-behavior: auto !important;
  }
}

/* --- dashboard ---------------------------------------------------------- */

/* The trace table sits in a card frame; the table's own hairlines provide
   the internal rules, so the card contributes only the outer edge. */
/* overflow-x:auto, not hidden: at narrow widths the trace table is wider
   than the card, and `hidden` silently clipped the Status and Error columns
   with no way to reach them. The table scrolls inside its own frame instead,
   so the page itself never scrolls sideways. */
.table-card {
  border: 1px solid var(--l-line);
  border-radius: var(--l-radius);
  background: var(--l-fill);
  overflow-x: auto;
}
.table-card table { margin: 0; }
.table-card tbody tr:last-child td { border-bottom: none; }

#trace-table td a { color: var(--l-text); text-decoration: none; font-weight: 500; }
#trace-table td a:hover { color: var(--l-accent); }
#trace-table td:nth-child(1) { color: var(--l-faint); font-family: var(--font-mono); font-size: 0.82rem; white-space: nowrap; }
#trace-table td:nth-child(3) { font-family: var(--font-mono); font-size: 0.82rem; color: var(--l-muted); }
#trace-table td:nth-child(5) { color: var(--l-muted); }

/* New rows arrive by htmx swap every 10s — a brief accent wash marks what
   just landed without moving anything. */
#trace-rows tr { animation: row-in 1.6s var(--l-ease); }
@keyframes row-in {
  /* Tokenized: hardcoded, this flashed the dark theme's #ff5a00 across a
     white row in light mode. */
  0% { background: var(--l-accent-haze); }
  100% { background: transparent; }
}
@media (prefers-reduced-motion: reduce) {
  #trace-rows tr { animation: none; }
}

.pager {
  display: flex;
  gap: 1.5rem;
  margin-top: 1.25rem;
  font-size: 0.9rem;
}

.filter-bar {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.75rem;
  margin-bottom: 1.25rem;
}
/* One control shape for the whole bar. These previously disagreed on
   every axis that matters — three heights (34.8 / 39.3 / 33px), two font
   families (the select fell through to Geist while the others took Sora),
   two sizes, two radii and two text colors — because each element picked
   up defaults from a different place: the select from the base form
   styles, the date input from the label wrapping it, the button from
   .btn-sm. An explicit height is what actually aligns them; matching
   padding alone cannot, since the native select and date widgets size
   their internals differently. */
.filter-bar select,
.filter-bar input[type="date"],
.filter-bar button {
  height: 34px;
  box-sizing: border-box;
  font-family: var(--font-body);
  font-size: 0.88rem;
  font-weight: 500;
  letter-spacing: normal;
  color: var(--l-text);
  background: var(--l-fill);
  border: 1px solid var(--l-line-strong);
  border-radius: 8px;
  padding: 0 0.65rem;
}

/* Overrides .btn-sm's 10px radius / 13px type inside this bar only. The
   button belongs to the control group here, not to the page's button
   scale — .btn-sm keeps its own geometry everywhere else. */
.filter-bar button.btn-sm {
  border-radius: 8px;
  font-size: 0.88rem;
  padding: 0 0.9rem;
}

/* `form label` (see the auth form styles) makes every label in a form
   uppercase, letter-spaced, display-face and margin-top: 1rem. That is
   right for a stacked auth field and wrong for an inline prefix inside a
   toolbar — it read as a section heading and pushed the control 5.7px
   below its neighbours. */
.filter-since {
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
  margin-top: 0;
  font-family: var(--font-body);
  font-size: 0.88rem;
  font-weight: 400;
  letter-spacing: normal;
  text-transform: none;
  color: var(--l-muted);
}

/* --- trace detail ------------------------------------------------------- */

.back-link { font-size: 0.9rem; margin: 0 0 1.25rem; }
.back-link a { color: var(--l-muted); text-decoration: none; }
.back-link a:hover { color: var(--l-accent); }

.trace-id {
  font-size: 1.5rem;
  color: var(--l-text);
  margin: 0.3rem 0 0;
  word-break: break-all;
}

.muted-note { color: var(--l-muted); font-size: 0.92rem; }
.upgrade-link { margin-top: 1rem; font-size: 0.92rem; }

.diagnosis-head {
  display: flex;
  align-items: center;
  gap: 14px;
  margin: 2rem 0 1rem;
}
.diagnosis-head h2 { margin: 0; }

.diagnosis-card .field-last { margin-bottom: 0; }
.diagnosis-card .code-panel { margin: 0.5rem 0 0; }

/* The pending state is a page-level status, styled as the same panel the
   in-flight indicator uses so "waiting" looks identical however it began. */
.pending-panel {
  display: flex;
  align-items: center;
  gap: 14px;
  padding: 16px 20px;
  border: 1px solid var(--l-line-strong);
  border-radius: 12px;
  background: var(--l-fill);
  font-size: 0.92rem;
  color: var(--l-muted);
}
.pending-panel strong { color: var(--l-text); display: block; }

.raw-section { margin-top: 3rem; }
.raw-section h2 { margin-bottom: 0.75rem; }

/* --- settings ----------------------------------------------------------- */

/* A freshly issued key is the one moment on this page that is good news
   rather than neutral, so it gets the ok treatment. */
.key-reveal-new {
  border-color: var(--l-ok-line);
  background: var(--l-ok-fill);
}
.key-reveal-new .key-reveal-label { color: var(--l-ok); }

.key-table td .form-hint { display: inline; margin: 0 0 0 0.5rem; font-size: 0.78rem; }

/* --- billing ------------------------------------------------------------ */

.section-h2 { margin: 2.5rem 0 1rem; }
/* Subsections inside a section (the Go SDK's flush/background-job blocks on
   /docs). Deliberately less top margin than .section-h2 — an h3 that
   separated as hard as its parent h2 would read as a sibling of it. */
.section-h3 { margin: 1.75rem 0 0.6rem; }
.sdk-heading { margin: 2rem 0 0.75rem; font-size: 1.25rem; }

.plan-name {
  font-family: var(--font-display);
  font-size: 1.35rem;
  font-weight: 700;
  letter-spacing: -0.02em;
}

.plan-card { max-width: 34rem; }
.plan-card-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
  gap: 1.25rem;
  margin-bottom: 1.5rem;
}
.plan-card-grid .plan-card { max-width: none; }
/* Stand-in for the upgrade button while paid plans are disabled. Sized like
   the button it replaces so the cards keep the same height, but visibly
   inert — the real enforcement is server-side in app/routes/billing.py. */
.btn-soon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  font-family: var(--font-display);
  font-weight: 600;
  font-size: 15px;
  letter-spacing: -0.01em;
  /* Matches the button/.btn box exactly so swapping it in doesn't resize
     the card. */
  padding: 12px 22px;
  border-radius: 12px;
  border: 1px dashed var(--l-line-strong);
  background: transparent;
  color: var(--l-muted);
  cursor: default;
  user-select: none;
}
.plan-price {
  font-family: var(--font-display);
  font-size: 2.4rem;
  font-weight: 700;
  letter-spacing: -0.03em;
  margin: 0.35rem 0 0;
  color: var(--l-text);
}
.plan-price span { font-size: 0.95rem; font-weight: 500; color: var(--l-faint); letter-spacing: 0; }
.plan-blurb { color: var(--l-muted); font-size: 0.95rem; margin: 0.9rem 0 1.5rem; }

/* Confirmation / error pages: same centered column as the auth pages, a
   little wider because several carry a key panel or a list of options. */
.auth-panel-wide { max-width: 38rem; }
.auth-panel-wide .key-reveal { max-width: none; }

/* =====================================================================
   DASHBOARD STAT ROW (templates/dashboard.html)

   Replaces .meta-grid here. That component is a generic label/value grid
   shared with billing.html — three of its cells stretched across the
   1200px container put a one-character number in a ~380px box, which is
   why the dashboard read as a metadata panel rather than a control
   surface. This is denser on purpose: the strip should be scannable in
   one glance, not walked through.

   The Chronexis part is the rail. Each cell carries a hairline on its
   left edge that stays dim until the stat means something, then lights
   in the status color and slides its number a hair toward it — the same
   "something is live here" language as .eyebrow-dot, and the reason the
   row reads as instrument panel rather than three boxes.
   ===================================================================== */
.stat-row {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(140px, 1fr));
  gap: 1px;
  background: var(--l-line);
  border: 1px solid var(--l-line);
  border-radius: var(--l-radius);
  overflow: hidden;
  margin-bottom: 1.25rem;
  /* Capped, not full-bleed. Left to stretch, three cells span the whole
     1200px container and a one-digit number sits alone in a ~383px box —
     which is what made the strip read as a metadata panel. Held to 620px
     the numbers sit near each other and scan as one instrument, and the
     empty space to the right reads as deliberate rather than unfilled. */
  max-width: 620px;
}

.stat-cell {
  position: relative;
  background: var(--l-bg-2);
  padding: 13px 18px 14px;
  text-decoration: none;
  display: block;
  transition: background 0.18s var(--l-ease);
}

/* The rail. Inset from the edges so it reads as a mark rather than a
   border, and animated on transform so the hover cost stays off the
   paint path. */
.stat-cell::before {
  content: "";
  position: absolute;
  left: 0;
  top: 12px;
  bottom: 12px;
  width: 2px;
  background: var(--l-line-strong);
  transform: scaleY(0.55);
  transform-origin: center;
  transition: transform 0.18s var(--l-ease), background 0.18s var(--l-ease);
}

.stat-label {
  font-family: var(--font-display);
  font-size: 10px;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--l-faint);
  margin: 0 0 0.3rem;
  white-space: nowrap;
}

/* Tabular figures so the numbers hold their column as they tick over on
   the 10s poll — proportional digits make a live counter jitter. */
.stat-value {
  font-family: var(--font-display);
  font-size: 1.45rem;
  font-weight: 700;
  line-height: 1.1;
  letter-spacing: -0.02em;
  color: var(--l-text);
  font-variant-numeric: tabular-nums;
  font-feature-settings: "tnum" 1;
  margin: 0;
  transition: color 0.18s var(--l-ease), transform 0.18s var(--l-ease);
}

/* Zero is the good state on both colored stats, so it recedes instead of
   competing with a number that actually wants attention. */
.stat-value-zero { color: var(--l-faint); }

.stat-warn .stat-value { color: var(--l-warn); }
.stat-warn::before { background: var(--l-warn); transform: scaleY(1); }
.stat-ok .stat-value { color: var(--l-ok); }
.stat-ok::before { background: var(--l-ok); transform: scaleY(1); }

.stat-cell-link:hover { background: var(--l-panel); }
.stat-cell-link:hover .stat-value { transform: translateX(2px); }
.stat-cell-link:focus-visible {
  outline: 2px solid var(--l-accent);
  outline-offset: -2px;
}

/* Only the actionable stats get the affordance, and only on a device that
   can hover — on touch the arrow is noise, since the whole cell is the
   target. */
@media (hover: hover) {
  .stat-cell-link .stat-label::after {
    content: " →";
    color: var(--l-faint);
    opacity: 0;
    transition: opacity 0.18s var(--l-ease);
  }
  .stat-cell-link:hover .stat-label::after { opacity: 1; }
}

/* --- filter bar: tame the native date control ------------------------- */
/* color-scheme: dark (see :root) already makes the picker render dark, but
   an empty date field still shows "mm/dd/yyyy" at full text weight, which
   reads as a value rather than as a prompt. Dimming it is conditional on
   the field actually being empty — the class comes from the template,
   because there is no CSS selector for "a date input with no value"
   (:placeholder-shown does not apply to date). */
.filter-bar input[type="date"] { min-width: 9.5rem; }
.filter-bar input[type="date"].is-empty { color: var(--l-muted); }

.filter-bar select:focus-visible,
.filter-bar input[type="date"]:focus-visible,
.filter-bar button:focus-visible {
  outline: none;
  border-color: var(--l-accent);
  color: var(--l-text);
}
.filter-bar input[type="date"]::-webkit-calendar-picker-indicator {
  opacity: 0.45;
  cursor: pointer;
  transition: opacity 0.18s var(--l-ease);
}
.filter-bar input[type="date"]:hover::-webkit-calendar-picker-indicator { opacity: 0.9; }

/* --- trace table: keep rows one line high ----------------------------- */
/* error_summary is a full sentence from the model. At one trace it merely
   looks chatty; at twenty it turns the table into prose and destroys the
   scan. The full text is one click away on the trace page, and title=
   keeps it reachable without one. */
#trace-table td:nth-child(5) {
  max-width: 44ch;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* --- info banners are a footnote, not a headline ---------------------- */
/* The trial shared-pool note is the largest block on a healthy dashboard:
   two full-measure lines at body size, directly under the stat row it
   dwarfs. It carries no action and no urgency, so it reads at footnote
   weight now.

   Scoped to .banner-info only. banner-warn and banner-danger keep their
   size deliberately — "0 diagnoses left" and "trial expired" are stops,
   and the two-weight split between them is load-bearing (see
   partials/quota_banner.html). */
.banner-info {
  font-size: 0.85rem;
  line-height: 1.55;
  max-width: 82ch;
  padding: 0.7rem 1rem;
}

/* Below ~520px only two cells fit, which leaves the third as an orphan at
   half width. Making it span the full row turns an accident into a
   layout. */
@media (max-width: 520px) {
  .stat-row { grid-template-columns: repeat(2, 1fr); }
  .stat-row > :last-child { grid-column: 1 / -1; }
}

/* --- appearance picker (templates/settings.html) ---------------------- */
/* Three radios styled as a segmented control. The radio itself stays in
   the DOM and focusable — hidden with opacity rather than display:none,
   because display:none removes it from tab order and from the
   accessibility tree, which would leave the control unusable by keyboard
   for the sake of a border. */
.theme-picker {
  display: flex;
  flex-wrap: wrap;
  gap: 10px;
  margin: 0 0 0.75rem;
}
.theme-option {
  position: relative;
  display: flex;
  flex-direction: column;
  gap: 2px;
  min-width: 140px;
  padding: 11px 14px;
  margin-top: 0;
  border: 1px solid var(--l-line-strong);
  border-radius: 10px;
  background: var(--l-fill);
  cursor: pointer;
  /* Overrides `form label`, which is built for stacked auth fields:
     uppercase, letter-spaced and margin-top: 1rem. */
  font-family: var(--font-body);
  letter-spacing: normal;
  text-transform: none;
  transition: border-color 0.18s var(--l-ease), background 0.18s var(--l-ease);
}
.theme-option:hover { border-color: var(--l-accent); }
.theme-option input {
  position: absolute;
  opacity: 0;
  width: 1px;
  height: 1px;
  margin: 0;
}
.theme-option-name {
  font-family: var(--font-display);
  font-size: 0.9rem;
  font-weight: 600;
  color: var(--l-text);
  text-transform: none;
  letter-spacing: normal;
}
.theme-option-hint {
  font-size: 0.78rem;
  color: var(--l-muted);
  text-transform: none;
  letter-spacing: normal;
}
.theme-option.is-active {
  border-color: var(--l-accent);
  background: var(--l-accent-haze);
}
/* The focus ring has to come from the hidden input, since that is what
   actually receives focus. */
.theme-option:focus-within {
  outline: 2px solid var(--l-accent);
  outline-offset: 2px;
}
