/* ==========================================================================
   Raider Market Data MCP - design system
   ==========================================================================

   One stylesheet. Before this, `base.html`, `dashboard.html`, `connect.html`,
   `legal.html` and `delete_account.html` each carried their own <style> block
   and between them redefined `.card`, `.copy`, `.row`, `.endpoint`, `.eyebrow`
   and `pre.snippet` five times over, with values that had already drifted
   apart. 626 lines of CSS, most of it a copy of another line of CSS.

   WHAT THIS IS TRYING TO BE

   An instrument. This service is read by somebody deciding whether to put
   money at risk, quite often at an hour when their eyes are tired. So: no
   decoration that does not carry information, numbers that never shift under
   the cursor, and a colour vocabulary where every hue means exactly one thing.

   COLOUR DISCIPLINE, which is the rule most likely to be broken later

   Green, red, amber and slate-blue are SEMANTIC. They mean valid, invalid,
   degraded and vendor-down, and nothing else may borrow them - a green button
   next to a green health dot teaches the eye that green is decoration, and
   then the health dot stops being read.

   THE INTERFACE IS MONOCHROME. The accent is near-black in light and near-white
   in dark. That is not minimalism for its own sake, it is what the numbers
   allowed: this started as a teal, which measured 1.01:1 against the success
   green - the same shade in grayscale, and therefore the same shade to a
   deuteranope. Every chromatic replacement failed identically (violet 1.11,
   magenta 1.10, plum 1.07, deep teal 1.13 against their nearest status), because
   four semantic hues already cover the wheel at those lightnesses.

   A chromatic accent cannot coexist with four semantic colours. So colour means
   DATA here, and the interface is shape, weight and space. If a future change
   wants "a nice colour" for a button, the answer is no, and
   `test_stylesheet_contrast.py` will say so.

   TOKENS ONLY

   Nothing below the token block hardcodes a colour. A raw hex outside `:root`
   is a bug, because it will be right in one theme and wrong in the other.
   ========================================================================== */

/* --------------------------------------------------------------------------
   1. Tokens
   -------------------------------------------------------------------------- */

:root {
  color-scheme: light dark;

  /* Surfaces, light. Paper rather than pure white: #fff under a dark-adapted
     eye at 2am is a flashlight. */
  --page: #f4f5f7;
  --panel: #ffffff;
  --panel-sunken: #eef0f3;
  --line: #dcdfe4;
  --line-strong: #c3c8d0;

  /* Ink */
  --ink: #14161a;
  --ink-soft: #4a515c;
  /* #6b7280 measured 4.43 on the page and 4.23 on the sunken surface - under
     AA on both, and this tier carries real prose, not just labels. This is the
     LIGHTEST value that clears 4.5 on all three light surfaces; going darker
     would collapse it into --ink-soft and cost a level of hierarchy. */
  --ink-faint: #656d7b;

  /* Provider artwork keeps its original brand colours on one neutral tile.
     The tile variables are deliberately separate from semantic status hues. */
  --provider-mark-surface: #ffffff;
  --provider-mark-ink: #14161a;
  --provider-mark-outline: rgba(0, 0, 0, 0.1);

  /* Accent: ink with a faint indigo cast. Not a colour - see the header. */
  --accent: #1e1b4b;
  --accent-hover: #14122f;
  --accent-ink: #ffffff;
  --accent-wash: #eceaf5;
  --focus: #1e1b4b;

  /* Semantic health states. These four are spoken for. */
  /* #1a7f47 measured 4.45:1 on its own wash, just under AA. */
  --ok: #136434;
  --ok-wash: #e7f4ec;
  --warn: #9a6100;
  --warn-wash: #fbf1dd;
  --bad: #b3261e;
  --bad-wash: #fbe9e8;
  --down: #33499e;
  --down-wash: #e7ecfb;
  --idle: #5c6470;
  --idle-wash: #eef0f3;

  /* Type. Instrument Sans has an engineered, slightly narrow cut that suits a
     dense read; JetBrains Mono carries every number, token and endpoint. Both
     fall back to a real stack, so a blocked font CDN costs character and not
     legibility. */
  --font-ui: "Instrument Sans", ui-sans-serif, -apple-system, BlinkMacSystemFont,
    "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
  --font-mono: "JetBrains Mono", ui-monospace, SFMono-Regular, "SF Mono", Menlo,
    Consolas, "Liberation Mono", monospace;

  /* A fourth-based scale. Sizes are named for their job, not their pixels, so
     a heading cannot quietly become a different size in one template. */
  --text-xs: 0.8125rem;
  --text-sm: 0.875rem;
  --text-base: 1rem;
  --text-md: 1.125rem;
  --text-lg: 1.375rem;
  --text-xl: 1.875rem;
  --text-display: 2.5rem;

  /* Space, 4px-based */
  --space-1: 0.25rem;
  --space-2: 0.5rem;
  --space-3: 0.75rem;
  --space-4: 1rem;
  --space-5: 1.5rem;
  --space-6: 2rem;
  --space-7: 3rem;
  --space-8: 4rem;

  /* Radii. Concentric: an element inset by `--space-3` inside a `--radius-lg`
     surface takes `--radius-md`, so the two curves never fight. */
  --radius-sm: 5px;
  --radius-md: 8px;
  --radius-lg: 12px;
  --radius-pill: 999px;

  /* Depth is layered shadow, not a border. A shadow adapts to whatever is
     behind it; a solid line does not. */
  --shadow-1: 0 1px 2px rgba(16, 20, 28, 0.05),
    0 1px 1px rgba(16, 20, 28, 0.04);
  --shadow-2: 0 2px 4px rgba(16, 20, 28, 0.06),
    0 4px 12px rgba(16, 20, 28, 0.06);
  --shadow-3: 0 8px 24px rgba(16, 20, 28, 0.1),
    0 2px 6px rgba(16, 20, 28, 0.06);

  --header-height: 3.5rem;
  --measure: 42rem;
  --shell: 74rem;

  --ease: cubic-bezier(0.2, 0, 0, 1);
  --fast: 120ms;
  --medium: 200ms;
}

@media (prefers-color-scheme: dark) {
  :root {
    --page: #0b0c0e;
    --panel: #15171a;
    --panel-sunken: #101215;
    --line: #272b31;
    --line-strong: #3a3f47;

    --ink: #e9ebee;
    --ink-soft: #a6adb8;
    --ink-faint: #7d858f;

    --provider-mark-surface: #ffffff;
    --provider-mark-ink: #14161a;
    --provider-mark-outline: rgba(255, 255, 255, 0.1);

    --accent: #f4f6f9;
    --accent-hover: #ffffff;
    --accent-ink: #101215;
    --accent-wash: #23262c;
    --focus: #f4f6f9;

    --ok: #5bd08a;
    --ok-wash: #14251b;
    --warn: #e0b25a;
    --warn-wash: #2a2213;
    --bad: #f0857c;
    --bad-wash: #2c1615;
    --down: #90a8f0;
    --down-wash: #191f31;
    --idle: #9aa3b0;
    --idle-wash: #1c1f24;

    /* Shadows carry almost nothing on a near-black ground; separation comes
       from the panel being lighter than the page instead. */
    --shadow-1: 0 1px 2px rgba(0, 0, 0, 0.4);
    --shadow-2: 0 2px 6px rgba(0, 0, 0, 0.45);
    --shadow-3: 0 10px 30px rgba(0, 0, 0, 0.55);
  }
}

/* --------------------------------------------------------------------------
   2. Reset and base
   -------------------------------------------------------------------------- */

*,
*::before,
*::after {
  box-sizing: border-box;
}

html {
  -webkit-text-size-adjust: 100%;
}

body {
  margin: 0;
  min-height: 100vh;
  background: var(--page);
  color: var(--ink);
  font-family: var(--font-ui);
  font-size: var(--text-base);
  line-height: 1.55;
  /* Crisper text on macOS, where the default synthetic weight reads muddy. */
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
}

h1,
h2,
h3,
h4 {
  margin: 0;
  line-height: 1.2;
  letter-spacing: -0.015em;
  /* Headings break into even lines instead of leaving one orphaned word. */
  text-wrap: balance;
}

h1 { font-size: var(--text-xl); font-weight: 650; }
h2 { font-size: var(--text-lg); font-weight: 600; }
h3 { font-size: var(--text-md); font-weight: 600; }
h4 { font-size: var(--text-base); font-weight: 600; }

p {
  margin: 0 0 var(--space-4);
  text-wrap: pretty;
}

p:last-child { margin-bottom: 0; }

a {
  color: var(--accent);
  text-decoration-color: color-mix(in srgb, var(--accent) 35%, transparent);
  text-underline-offset: 0.2em;
}

/* Prose links that perform a product action still need a full pointer target.
   Inline flex keeps the words in the sentence while giving the anchor itself,
   not an overlapping pseudo-element, a measurable 40px hit area. */
.inline-action {
  display: inline-flex;
  align-items: center;
  min-height: 44px;
}

@media (hover: hover) and (pointer: fine) {
  a:hover { text-decoration-color: currentColor; }
}

code, pre, kbd, samp {
  font-family: var(--font-mono);
  font-size: 0.9em;
}

/* Every number that can change gets a fixed advance width, so a figure
   updating in place never nudges the column beside it. */
.num,
td.num,
.stat-value,
table.data td {
  font-variant-numeric: tabular-nums;
}

hr {
  border: 0;
  border-top: 1px solid var(--line);
  margin: var(--space-6) 0;
}

/* One focus treatment everywhere, and it is never removed. Keyboard users are
   the reason this exists; a dashboard that cannot be driven from the keyboard
   is unfinished, not merely unpolished. */
:where(a, button, input, select, textarea, summary, [tabindex]):focus-visible {
  outline: 2px solid var(--focus);
  outline-offset: 2px;
  border-radius: var(--radius-sm);
}

::selection {
  background: var(--accent-wash);
  color: var(--ink);
}

/* --------------------------------------------------------------------------
   3. Shell: header, main, footer
   -------------------------------------------------------------------------- */

.shell {
  display: flex;
  flex-direction: column;
  min-height: 100vh;
}

.masthead {
  position: sticky;
  top: 0;
  z-index: 20;
  height: var(--header-height);
  display: flex;
  align-items: center;
  background: color-mix(in srgb, var(--panel) 88%, transparent);
  backdrop-filter: saturate(140%) blur(8px);
  border-bottom: 1px solid var(--line);
}

/* The bar spans the window; its CONTENTS share the exact container `main`
   uses. Before this the header was padded to the window edge while the page
   below it was centred in a 74rem column, so the mark and the first heading
   started on two different left edges and the whole shell read crooked. */
.masthead-inner,
.footer-inner {
  width: 100%;
  max-width: var(--shell);
  margin-inline: auto;
  padding: 0 var(--space-5);
}

.masthead-inner {
  display: flex;
  align-items: center;
  gap: var(--space-5);
  /* The mark hangs below the bar. Nothing here may clip him. */
  overflow: visible;
}

.mark {
  display: inline-flex;
  /* He hangs DOWN from the bar, so the lockup is anchored at the top and the
     wordmark keeps the bar's own optical centre beside him. */
  align-items: flex-start;
  gap: var(--space-2);
  font-weight: 650;
  letter-spacing: -0.02em;
  color: var(--ink);
  text-decoration: none;
  white-space: nowrap;
}

/* The wordmark's line box is the bar, so it centres against the header rather
   than against the mark's much taller flex line. */
.mark > span {
  line-height: var(--header-height);
}

/* The Raider is deliberately bigger than the bar he sits in. He overhangs it
   by roughly half his height, the way a badge sits on a lapel, rather than
   shrinking to a 30px favicon nobody notices. The ring in the page colour is
   what makes him read as sitting ON the page instead of being pasted into it.
   The negative bottom margin keeps that overhang out of the layout, so the bar
   stays exactly `--header-height` tall. */
.mark-icon {
  width: 6.5rem;
  height: 6.5rem;
  flex: none;
  margin: 0 var(--space-3) -3.25rem 0;
  border-radius: 50%;
  box-shadow: var(--shadow-3);
  outline: 3px solid var(--page);
  outline-offset: 0;
  /* Named properties, never the catch-all shorthand: that one animates
     whatever a later change happens to add, including layout properties that
     then jank. */
  transition-property: width, height, margin, box-shadow;
  transition-duration: var(--medium);
  transition-timing-function: var(--ease);
}

/* Scrolled state, set by `app.js`. The badge is a greeting, not a permanent
   fixture: once the reader is into the document the bar becomes a bar again,
   and the overhang stops riding down the page over the prose. `main` keeps its
   padding either way, so nothing below reflows when he shrinks. */
.masthead.is-scrolled .mark-icon {
  width: 2.75rem;
  height: 2.75rem;
  margin-bottom: 0;
  box-shadow: var(--shadow-1);
  outline-width: 0;
}

@media (prefers-reduced-motion: reduce) {
  .mark-icon { transition: none; }
}

@media (max-width: 48rem) {
  .mark-icon {
    width: 4.5rem;
    height: 4.5rem;
    margin: 0 var(--space-2) -2rem 0;
    outline-width: 2px;
  }
}

.hero-mark {
  width: 7rem;
  height: 7rem;
  margin-bottom: var(--space-4);
  border-radius: 50%;
  box-shadow: var(--shadow-3);
}

@media (max-width: 48rem) {
  .hero-mark { width: 5rem; height: 5rem; }
}

.nav {
  display: flex;
  align-items: center;
  gap: var(--space-1);
  margin-right: auto;
  overflow-x: auto;
  scrollbar-width: none;
}

.nav::-webkit-scrollbar { display: none; }

.nav a {
  padding: var(--space-2) var(--space-3);
  border-radius: var(--radius-sm);
  color: var(--ink-soft);
  font-size: var(--text-sm);
  font-weight: 500;
  text-decoration: none;
  white-space: nowrap;
}

@media (hover: hover) and (pointer: fine) {
  .nav a {
    transition: color var(--fast) var(--ease),
      background-color var(--fast) var(--ease);
  }

  .nav a:hover {
    color: var(--ink);
    background: var(--panel-sunken);
  }
}

.nav a[aria-current="page"] {
  color: var(--accent);
  background: var(--accent-wash);
}

/* The compact navigation is native disclosure, not a scripted popover. It is
   absent from the desktop layout and becomes the same five destinations in a
   keyboard-operable sheet when horizontal space runs out. */
.mobile-nav {
  display: none;
  position: relative;
  margin-left: auto;
}

.mobile-nav > summary {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-2);
  min-height: 44px;
  padding: var(--space-2) var(--space-3);
  border-radius: var(--radius-md);
  color: var(--ink-soft);
  font-size: var(--text-sm);
  font-weight: 600;
  cursor: pointer;
  list-style: none;
}

.mobile-nav > summary::-webkit-details-marker { display: none; }

@media (hover: hover) and (pointer: fine) {
  .mobile-nav > summary:hover { color: var(--ink); background: var(--panel-sunken); }
}

.mobile-nav-panel {
  position: absolute;
  right: 0;
  top: calc(100% + var(--space-2));
  z-index: 30;
  display: grid;
  width: min(15rem, calc(100vw - var(--space-4) * 2));
  padding: var(--space-2);
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  background: var(--panel);
  box-shadow: var(--shadow-3);
}

.mobile-nav-panel a {
  display: flex;
  align-items: center;
  min-height: 44px;
  padding: var(--space-2) var(--space-3);
  border-radius: var(--radius-md);
  color: var(--ink-soft);
  font-size: var(--text-sm);
  font-weight: 500;
  text-decoration: none;
}

@media (hover: hover) and (pointer: fine) {
  .mobile-nav-panel a:hover { color: var(--ink); background: var(--panel-sunken); }
}

.mobile-nav-panel a.is-current {
  color: var(--accent);
  background: var(--accent-wash);
}

.masthead-end {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  font-size: var(--text-sm);
}

.who {
  color: var(--ink-faint);
  max-width: 16rem;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

main {
  flex: 1;
  width: 100%;
  max-width: var(--shell);
  margin: 0 auto;
  /* The extra 2rem is the mark's overhang. Without it the Raider covers the
     first heading on every page, which is a worse trade than a little air. */
  padding: calc(var(--space-6) + 2rem) var(--space-5) var(--space-8);
}

/* A centred card for sign-in, the goodbye page, and anything else that is one
   short decision rather than a working surface. */
main.centred {
  display: flex;
  align-items: center;
  justify-content: center;
  max-width: none;
  padding: var(--space-7) var(--space-5);
}

main.centred > .card {
  width: 100%;
  max-width: 26rem;
}

main.reading {
  max-width: 48rem;
}

.page-footer {
  padding: var(--space-5) 0;
  border-top: 1px solid var(--line);
  color: var(--ink-faint);
  font-size: var(--text-sm);
  text-align: center;
}

.page-footer a { color: var(--ink-soft); }

/* Account menu. A <details> rather than a JS popover: it opens with the
   keyboard for free, survives JS being blocked, and needs no focus trap. */
.account-menu { position: relative; }

.account-menu > summary {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  min-height: 44px;
  padding: var(--space-2) var(--space-3);
  border-radius: var(--radius-md);
  color: var(--ink-soft);
  font-size: var(--text-sm);
  font-weight: 500;
  cursor: pointer;
  list-style: none;
}

.account-menu > summary::-webkit-details-marker { display: none; }

@media (hover: hover) and (pointer: fine) {
  .account-menu > summary:hover { background: var(--panel-sunken); color: var(--ink); }
}
.account-menu-label { display: none; }

.account-sheet {
  position: absolute;
  right: 0;
  top: calc(100% + var(--space-2));
  z-index: 30;
  min-width: 14rem;
  padding: var(--space-2);
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  background: var(--panel);
  box-shadow: var(--shadow-3);
  display: grid;
  gap: 2px;
}

.account-sheet p {
  margin: 0;
  padding: var(--space-2) var(--space-3);
  border-bottom: 1px solid var(--line);
  word-break: break-all;
}

.account-sheet a {
  padding: var(--space-2) var(--space-3);
  /* Concentric: the sheet is --radius-lg with --space-2 padding, so an item
     inside it takes the next radius down rather than the same one. */
  border-radius: var(--radius-md);
  color: var(--ink);
  font-size: var(--text-sm);
  text-decoration: none;
}

.account-sheet a.danger { color: var(--bad); }

@media (hover: hover) and (pointer: fine) {
  .account-sheet a:hover { background: var(--panel-sunken); }
  .account-sheet a.danger:hover { background: var(--bad-wash); }
}

@media (max-width: 48rem) {
  .account-menu .who { display: inline; max-width: 8rem; }
}

/* --------------------------------------------------------------------------
   4. Page furniture
   -------------------------------------------------------------------------- */

.eyebrow {
  margin: 0 0 var(--space-2);
  font-size: var(--text-xs);
  font-weight: 650;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--accent);
}

.lede {
  max-width: var(--measure);
  font-size: var(--text-md);
  color: var(--ink-soft);
}

.section {
  margin-top: var(--space-7);
  /* The masthead is sticky, so an in-page anchor lands its target UNDERNEATH
     it - the nav scrolls you to a heading you then cannot see. This reserves
     the header's height, the mark's overhang, and a little air. */
  scroll-margin-top: calc(var(--header-height) + 2rem + var(--space-4));
}

/* Same for anything else the nav or a link can target directly. The mark's
   2rem overhang is part of what an anchored heading has to clear. */
[id] { scroll-margin-top: calc(var(--header-height) + 2rem + var(--space-4)); }

/* Deliberately NOT a flex row.
   As flex, the description carried `flex: 1 0 100%` to force its own line and
   STILL sat beside the heading, because `max-width` clamps a flex item's
   hypothetical main size - and the hypothetical main size is what line
   breaking measures. The 100% basis never survived to be measured. A block
   container makes "the description goes underneath" a fact rather than a
   negotiation. */
.section-head {
  margin-bottom: var(--space-3);
}

.section-head h2,
.section-head h3 {
  display: inline-flex;
  align-items: baseline;
  gap: var(--space-3);
  flex-wrap: wrap;
}

.section-head p {
  margin: var(--space-2) 0 0;
  color: var(--ink-soft);
  font-size: var(--text-sm);
  max-width: var(--measure);
}

.count {
  color: var(--ink-faint);
  font-weight: 400;
  font-size: var(--text-base);
  font-variant-numeric: tabular-nums;
}

.muted { color: var(--ink-soft); }
.faint { color: var(--ink-faint); }
.small { font-size: var(--text-sm); }
.xs { font-size: var(--text-xs); }
.mono { font-family: var(--font-mono); }

.label {
  display: block;
  font-size: var(--text-xs);
  font-weight: 600;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--ink-faint);
}

.row {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  flex-wrap: wrap;
}

.row .grow { flex: 1 1 18rem; min-width: 0; }
.row.tight { gap: var(--space-2); }
.stack > * + * { margin-top: var(--space-3); }

.grid {
  display: grid;
  gap: var(--space-3);
  grid-template-columns: repeat(auto-fill, minmax(19rem, 1fr));
}

/* One long description must not set the height of everything beside it. The
   full text stays in the title attribute, so nothing is lost - it is just not
   allowed to stretch its whole row. */
.clamp-3 {
  display: -webkit-box;
  -webkit-line-clamp: 3;
  -webkit-box-orient: vertical;
  overflow: hidden;
}

/* Hero: the landing page's one oversized opening. Lives here rather than in a
   page <style> block so the "one stylesheet" rule stays absolute. */
.hero { padding: var(--space-6) 0 var(--space-4); }

.hero-title {
  max-width: 20ch;
  margin-bottom: var(--space-4);
  font-size: var(--text-display);
  line-height: 1.08;
  letter-spacing: -0.03em;
}

@media (max-width: 48rem) {
  .hero-title { font-size: var(--text-xl); }
}

/* --------------------------------------------------------------------------
   5. Card
   -------------------------------------------------------------------------- */

.card {
  padding: var(--space-5);
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  background: var(--panel);
  box-shadow: var(--shadow-1);
}

.card + .card { margin-top: var(--space-3); }
.card.flush { padding: 0; overflow: hidden; }

.card-head {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: var(--space-3);
  margin-bottom: var(--space-3);
}

.card-foot {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-2);
  flex-wrap: wrap;
  margin-top: var(--space-4);
  padding-top: var(--space-4);
  border-top: 1px solid var(--line);
}

/* The rotate disclosure grows to fill the row; the remove button keeps its own
   width beside it. Pinned to 100% these stacked, which left a lone x floating
   under "Rotate key" on every vendor card. */
.card-foot > details.disclosure {
  flex: 1 1 auto;
  min-width: 0;
  border: 0;
  background: transparent;
}

.card-foot > details.disclosure > summary { padding-left: 0; }
.card-foot > details.disclosure > *:not(summary) { padding-left: 0; padding-bottom: 0; }
.card-foot > form { flex: 0 0 auto; }

/* --------------------------------------------------------------------------
   6. Buttons
   -------------------------------------------------------------------------- */

.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-2);
  /* 44px minimum hit area for touch without inflating desktop layouts. */
  min-height: 44px;
  padding: var(--space-2) var(--space-4);
  border: 1px solid var(--line-strong);
  border-radius: var(--radius-md);
  background: var(--panel);
  color: var(--ink);
  font: inherit;
  font-size: var(--text-sm);
  font-weight: 600;
  text-decoration: none;
  cursor: pointer;
  /* Release snaps back faster than the deliberate press. Colour transitions
     are added only for devices that can genuinely hover. */
  transition: scale 80ms var(--ease);
}

/* 0.96 is the whole range: below 0.95 reads as the button flinching. */
.btn:active:not(:focus-visible):not([disabled]):not([aria-disabled="true"]) {
  scale: 0.96;
  transition-duration: 140ms;
}

@media (hover: hover) and (pointer: fine) {
  .btn {
    /* Named properties only. `transition: all` animates things nobody chose,
       including layout properties that then jank. */
    transition: background-color var(--fast) var(--ease),
      border-color var(--fast) var(--ease), color var(--fast) var(--ease),
      scale 80ms var(--ease);
  }

  .btn:hover { background: var(--panel-sunken); border-color: var(--ink-faint); }
}

.btn.primary {
  background: var(--accent);
  border-color: var(--accent);
  color: var(--accent-ink);
}

@media (hover: hover) and (pointer: fine) {
  .btn.primary:hover {
    background: var(--accent-hover);
    border-color: var(--accent-hover);
  }
}

.btn.danger { color: var(--bad); border-color: var(--bad); }
.btn.quiet { border-color: transparent; background: transparent; color: var(--ink-soft); }

@media (hover: hover) and (pointer: fine) {
  .btn.danger:hover { background: var(--bad-wash); }
  .btn.quiet:hover { background: var(--panel-sunken); color: var(--ink); }
}
.btn.wide { width: 100%; }

.btn[disabled],
.btn[aria-disabled="true"] {
  opacity: 0.55;
  cursor: not-allowed;
  scale: 1;
}

/* A button that is busy is not a button that is off. The 0.55 wash is right for
   a control the reader cannot use and wrong for one that is working: it drops
   the label on the primary button to roughly 3:1, and that label is the only
   thing on screen naming what is running. Full opacity on the idle grey keeps
   it near 6:1 in light and better in dark, and `progress` says the wait is the
   page's rather than the reader's mistake. */
.btn.primary[aria-busy="true"] {
  opacity: 1;
  background: var(--idle);
  border-color: var(--idle);
  cursor: progress;
}

/* An icon-only control still needs a full hit area even though it looks small. */
.icon-btn {
  display: inline-grid;
  place-items: center;
  width: 44px;
  height: 44px;
  padding: 0;
  border: 1px solid transparent;
  border-radius: var(--radius-md);
  background: transparent;
  color: var(--ink-faint);
  font-size: 1.125rem;
  line-height: 1;
  cursor: pointer;
  transition: scale 80ms var(--ease);
}

.icon-btn:active:not(:focus-visible) {
  scale: 0.96;
  transition-duration: 140ms;
}

@media (hover: hover) and (pointer: fine) {
  .icon-btn {
    transition: background-color var(--fast) var(--ease),
      color var(--fast) var(--ease), scale 80ms var(--ease);
  }

  .icon-btn:hover { background: var(--bad-wash); color: var(--bad); }
}

/* Security-sensitive removals reveal their consequence before submitting.
   Native details keeps that review step operable when scripts are blocked. */
.action-review {
  display: inline-block;
  text-align: left;
}

.action-review > summary { list-style: none; }
.action-review > summary::-webkit-details-marker { display: none; }

.action-review-panel {
  min-width: 15rem;
  margin-top: var(--space-2);
  padding: var(--space-3);
  border: 1px solid var(--line);
  border-radius: var(--radius-md);
  background: var(--panel);
  box-shadow: var(--shadow-2);
}

.action-review-panel > * + * { margin-top: var(--space-2); }

.copy {
  min-height: 44px;
  padding: var(--space-2) var(--space-3);
  border: 1px solid var(--line-strong);
  border-radius: var(--radius-md);
  background: var(--panel);
  color: var(--ink);
  font: inherit;
  font-size: var(--text-sm);
  font-weight: 600;
  cursor: pointer;
  white-space: nowrap;
  transition: scale 80ms var(--ease);
}

.copy:active:not(:focus-visible) {
  scale: 0.96;
  transition-duration: 140ms;
}

@media (hover: hover) and (pointer: fine) {
  .copy {
    transition: background-color var(--fast) var(--ease),
      border-color var(--fast) var(--ease), color var(--fast) var(--ease),
      scale 80ms var(--ease);
  }

  .copy:hover { background: var(--panel-sunken); }
}
.copy[data-copied="yes"] {
  border-color: var(--ok);
  color: var(--ok);
  background: var(--ok-wash);
}

/* --------------------------------------------------------------------------
   7. Forms
   -------------------------------------------------------------------------- */

.field { margin: 0 0 var(--space-4); }

/* A boolean credential field. It used to render as a masked password box like
   every other field, so "Production" was an unlabelled black rectangle and the
   natural things to type into it - false, no, off - all stored a STRING that
   the adapter read as true. The control now matches the type. */
.field-check { margin: 0 0 var(--space-4); }
.check {
  display: flex;
  align-items: flex-start;
  gap: var(--space-3);
  cursor: pointer;
  /* Comfortably past the 40px minimum hit area without looking padded. */
  min-height: 40px;
  padding: var(--space-2) 0;
}
.check input[type="checkbox"] {
  /* Optically aligned to the first line of the label rather than geometrically
     centred on a box whose text may wrap to two lines. */
  margin-top: 0.15em;
  width: 1.05rem;
  height: 1.05rem;
  flex: none;
  accent-color: var(--accent);
  cursor: pointer;
}
.check span { line-height: var(--leading-snug, 1.4); }
.field-check .hint { margin: var(--space-1) 0 0 calc(1.05rem + var(--space-3)); }
.field .label { margin-bottom: var(--space-2); }
.field .hint {
  margin: var(--space-2) 0 0;
  color: var(--ink-faint);
  font-size: var(--text-sm);
}

.input {
  width: 100%;
  min-height: 44px;
  padding: var(--space-2) var(--space-3);
  border: 1px solid var(--line-strong);
  border-radius: var(--radius-md);
  background: var(--panel);
  color: var(--ink);
  font: inherit;
  font-size: var(--text-base);
}

.input::placeholder { color: var(--ink-faint); }

.input:focus {
  outline: none;
  border-color: var(--accent);
  box-shadow: 0 0 0 3px var(--accent-wash);
}

.input[aria-invalid="true"] {
  border-color: var(--bad);
}

.input.mono { font-family: var(--font-mono); font-size: var(--text-sm); }

/* `.input` was written for text boxes. A select and a textarea carry the same
   frame so a generated form does not look assembled from three design systems,
   which is what a tool schema with a mix of types would otherwise produce. */
select.input {
  padding-right: var(--space-3);
  cursor: pointer;
}

textarea.input {
  min-height: 5rem;
  resize: vertical;
  line-height: 1.6;
}

.field-error {
  margin: var(--space-2) 0 0;
  color: var(--bad);
  font-size: var(--text-sm);
  font-weight: 500;
}

/* --------------------------------------------------------------------------
   8. Status vocabulary
   -------------------------------------------------------------------------- */

.chip {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  padding: var(--space-1) var(--space-3);
  border-radius: var(--radius-pill);
  font-size: var(--text-xs);
  font-weight: 650;
  letter-spacing: 0.02em;
  white-space: nowrap;
}

.dot {
  width: 0.5rem;
  height: 0.5rem;
  border-radius: 50%;
  background: currentColor;
  flex: none;
}

.chip[data-status="valid"]       { color: var(--ok);   background: var(--ok-wash); }
.chip[data-status="degraded"]    { color: var(--warn); background: var(--warn-wash); }
/* `partial` is a plan boundary, not a fault: it wears the same gold as
   `degraded` and never the red, because there is nothing here to repair. */
.chip[data-status="partial"]     { color: var(--warn); background: var(--warn-wash); }
.chip[data-status="invalid"]     { color: var(--bad);  background: var(--bad-wash); }
.chip[data-status="vendor_down"] { color: var(--down); background: var(--down-wash); }
.chip[data-status="unverified"]  { color: var(--idle); background: var(--idle-wash); }
.chip[data-status="live"]        { color: var(--ok);   background: var(--ok-wash); }
.chip[data-status="dark"]        { color: var(--idle); background: var(--idle-wash); }

/* Call-log statuses, in the failures table. `refused` is deliberately NOT red:
   it means the account has not connected a vendor for that capability, which is
   a thing the user can go and fix, and painting it as a fault sends people to
   re-enter a key that was never the problem. */
.chip[data-status="error"]       { color: var(--bad);  background: var(--bad-wash); }
.chip[data-status="ok"]          { color: var(--warn); background: var(--warn-wash); }
.chip[data-status="refused"]     { color: var(--idle); background: var(--idle-wash); }

/* The sentence under a health chip explains the state. Bounded and left-read,
   because right-aligned prose running the full card width is a paragraph
   pretending to be a label. */
.chip-sub {
  max-width: 22rem;
  margin-left: auto;
  text-align: right;
}

@media (max-width: 48rem) {
  .chip-sub { max-width: none; text-align: left; }
}

.badge {
  display: inline-block;
  padding: 0 var(--space-2);
  /* Transparent, not absent: the playground variant below draws a 1px
     border, and a badge that only has one when outlined is two pixels
     taller than its neighbours, so a column of them jitters row to row. */
  border: 1px solid transparent;
  border-radius: var(--radius-sm);
  background: var(--idle-wash);
  color: var(--idle);
  font-size: var(--text-xs);
  font-weight: 650;
  line-height: 1.6;
}

.badge.bad { background: var(--bad-wash); color: var(--bad); }

/* Which writer produced a call-log row. Told apart by SHAPE rather than by
   colour: the accent palette here is spoken for by health state, and a second
   vocabulary in the same colours would have a reader deciding whether a green
   badge meant "working" or "agent". A playground run is the exception, so it
   gets the outline and the wire default stays flat. */
.badge[data-source="playground"] {
  background: transparent;
  border-style: dashed;
  border-color: var(--ink-faint);
  color: var(--ink-faint);
}

.tags {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2);
}

.tag {
  padding: var(--space-1) var(--space-2);
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  background: var(--panel-sunken);
  color: var(--ink-soft);
  font-family: var(--font-mono);
  font-size: var(--text-xs);
}

.tag.lit {
  border-color: color-mix(in srgb, var(--ok) 40%, transparent);
  background: var(--ok-wash);
  color: var(--ok);
}

/* A capability a source CLAIMS and is not serving right now. Dashed and faded,
   the same treatment `.tool.dark` already gives a tool that cannot be called,
   so the two read as the same idea in two places. No new colour: this is an
   absence, and giving it a red would put an alarm next to something the reader
   can do nothing about and that comes back on its own. Dropping the tag
   entirely was the alternative and it is worse - it reads as "this source
   never did that" rather than "not at the moment". */
.tag.dark {
  border-style: dashed;
  color: var(--ink-faint);
  background: transparent;
}

/* A tag that is a link to the thing it names. Sits in rows beside tags that are
   NOT links - a capability chip, a prompt's tone - and with no underline the
   two are indistinguishable until you hover one, so the only way to find the
   clickable ones is to sweep the row with a mouse. Dotted rather than solid,
   and offset off the text, so a row of them still reads as tags rather than as
   prose. */
a.tag {
  text-decoration: underline dotted;
  text-underline-offset: 3px;
}

@media (hover: hover) and (pointer: fine) {
  a.tag { transition: border-color var(--fast) var(--ease); }
  a.tag:hover { border-color: currentColor; }
}

/* --------------------------------------------------------------------------
   9. Notices
   -------------------------------------------------------------------------- */

.notice {
  padding: var(--space-4);
  border: 1px solid var(--line);
  border-left: 3px solid var(--idle);
  border-radius: var(--radius-md);
  background: var(--panel-sunken);
  font-size: var(--text-sm);
  line-height: 1.55;
}

.notice + .notice { margin-top: var(--space-3); }
.notice p { margin: var(--space-2) 0 0; }
.notice p:first-child { margin-top: 0; }
.notice strong { color: var(--ink); }
.notice ul { margin: var(--space-2) 0 0; padding-left: 1.15rem; }
.notice li { margin: var(--space-1) 0; }

.notice.warn { border-left-color: var(--warn); background: var(--warn-wash); }
.notice.bad  { border-left-color: var(--bad);  background: var(--bad-wash); }
.notice.ok   { border-left-color: var(--ok);   background: var(--ok-wash); }
.notice.info { border-left-color: var(--accent); background: var(--accent-wash); }

/* --------------------------------------------------------------------------
   10. Code, endpoints, snippets
   -------------------------------------------------------------------------- */

.endpoint {
  display: block;
  padding: var(--space-3);
  border: 1px solid var(--line);
  border-radius: var(--radius-md);
  background: var(--panel-sunken);
  font-family: var(--font-mono);
  font-size: var(--text-sm);
  word-break: break-all;
}

pre.snippet {
  margin: var(--space-3) 0 0;
  padding: var(--space-4);
  border: 1px solid var(--line);
  border-radius: var(--radius-md);
  background: var(--panel-sunken);
  /* Wide content scrolls inside its own box; the page never scrolls sideways. */
  overflow-x: auto;
  font-size: var(--text-sm);
  line-height: 1.6;
}

pre.prompt {
  margin: 0;
  padding: var(--space-4);
  border-top: 1px solid var(--line);
  background: var(--panel-sunken);
  white-space: pre-wrap;
  font-size: var(--text-sm);
  line-height: 1.6;
}

/* --------------------------------------------------------------------------
   11. Tables
   -------------------------------------------------------------------------- */

.table-wrap {
  overflow-x: auto;
  border: 1px solid var(--line);
  border-radius: var(--radius-md);
}

table.data {
  width: 100%;
  border-collapse: collapse;
  font-size: var(--text-sm);
}

table.data th,
table.data td {
  padding: var(--space-3);
  text-align: left;
  border-bottom: 1px solid var(--line);
  white-space: nowrap;
}

/* The one cell that carries prose. `nowrap` was written for numeric cells;
   applied to a vendor's error message it turns a 1000-character failure
   into a table six screens wide, with the reason for the failure off to the
   right of everything. Bounded and wrapped instead, `anywhere` so an
   unbroken URL or token cannot re-widen it. */
table.data td.wrap {
  white-space: normal;
  min-width: 18rem;
  max-width: 36rem;
  overflow-wrap: anywhere;
}

table.data thead th {
  position: sticky;
  top: 0;
  background: var(--panel-sunken);
  color: var(--ink-faint);
  font-size: var(--text-xs);
  font-weight: 650;
  letter-spacing: 0.06em;
  text-transform: uppercase;
}

table.data tbody tr:last-child td { border-bottom: 0; }
table.data tbody tr:hover { background: var(--panel-sunken); }
table.data td.r, table.data th.r { text-align: right; }

/* --------------------------------------------------------------------------
   12. Stats
   -------------------------------------------------------------------------- */

.stats {
  display: grid;
  gap: var(--space-3);
  grid-template-columns: repeat(auto-fit, minmax(9rem, 1fr));
}

.stat {
  padding: var(--space-4);
  border: 1px solid var(--line);
  border-radius: var(--radius-md);
  background: var(--panel-sunken);
}

.stat-value {
  font-size: var(--text-lg);
  font-weight: 650;
  letter-spacing: -0.02em;
  line-height: 1.15;
}

.stat .label { margin-top: var(--space-1); }

/* --------------------------------------------------------------------------
   13. Empty and loading states
   -------------------------------------------------------------------------- */

/* An empty panel must say what it is empty OF and what to do about it. A blank
   rectangle is indistinguishable from a broken one. */
.empty {
  padding: var(--space-6) var(--space-5);
  border: 1px dashed var(--line-strong);
  border-radius: var(--radius-md);
  background: var(--panel-sunken);
  text-align: center;
  color: var(--ink-soft);
}

.empty h3 { margin-bottom: var(--space-2); color: var(--ink); }
.empty p { max-width: 32rem; margin-inline: auto; font-size: var(--text-sm); }
.empty .btn { margin-top: var(--space-4); }

/* One compact pending mark, adapted from the product's established dotted-orb
   treatment. It lives beside a sentence that names the work, so the mark is
   decorative and the copy remains the accessible status. Slow ambient motion
   uses only transform; no layout or paint property changes while work runs. */
.pending-orb {
  position: relative;
  display: inline-block;
  flex: none;
  width: 1.25rem;
  height: 1.25rem;
  color: var(--accent);
  opacity: 0.8;
  perspective: 11.25rem;
  vertical-align: -0.3rem;
  animation: pending-orb-breathe 2700ms cubic-bezier(0.77, 0, 0.175, 1) infinite;
}

.pending-orb-ring,
.pending-orb-spin {
  position: absolute;
  inset: 0;
}

.pending-orb-ring { transform-style: preserve-3d; }
.pending-orb-spin { animation: pending-orb-turn 7200ms linear infinite; }

.pending-orb-dot {
  position: absolute;
  top: 50%;
  left: 50%;
  width: 0.15625rem;
  height: 0.15625rem;
  margin: -0.078125rem 0 0 -0.078125rem;
  border-radius: var(--radius-pill);
  background: currentColor;
}

@keyframes pending-orb-turn {
  from { transform: rotate(0deg); }
  to { transform: rotate(360deg); }
}

@keyframes pending-orb-breathe {
  0%, 100% { transform: scale(0.94); }
  50% { transform: scale(1.02); }
}

/* --------------------------------------------------------------------------
   13b. First-run steps
   --------------------------------------------------------------------------
   Shown only until the account has actually made a call. Each step ticks from
   real state - a token that exists, a call that happened - never from a cookie,
   so it cannot congratulate somebody for a step they did not take. */

.steps {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  gap: var(--space-3);
  counter-reset: step;
}

.step {
  display: flex;
  gap: var(--space-4);
  padding: var(--space-5);
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  background: var(--panel);
  box-shadow: var(--shadow-1);
}

.step-num {
  display: grid;
  place-items: center;
  flex: none;
  width: 1.75rem;
  height: 1.75rem;
  border-radius: 50%;
  background: var(--panel-sunken);
  color: var(--ink-faint);
  font-family: var(--font-mono);
  font-size: var(--text-sm);
  font-weight: 600;
}

.step.done { border-color: color-mix(in srgb, var(--ok) 35%, var(--line)); }
.step.done .step-num { background: var(--ok-wash); color: var(--ok); }
.step-body { min-width: 0; flex: 1; }
.step-body h3 { margin-bottom: var(--space-1); }

/* A health chip that has just changed value flashes once, so a status moving
   under a reader's eyes is noticed rather than silently different. */
.chip.changed { animation: chip-in 200ms var(--ease); }

@keyframes chip-in {
  from { opacity: 0.4; scale: 0.94; }
  to { opacity: 1; scale: 1; }
}

@media (prefers-reduced-motion: reduce) {
  .chip.changed { animation: none; }
}

/* A dark tool is legible, not hidden. Somebody deciding whether to buy a key
   needs to read what they would be buying. */
.tool.dark { border-style: dashed; }
.tool.dark h3 { color: var(--ink-soft); }

/* --------------------------------------------------------------------------
   13c. Playground live runs
   --------------------------------------------------------------------------
   What a run looks like while it is still happening. Every rule here is
   temporary furniture: the panel is replaced by the server-rendered result the
   moment the run ends, so it draws the SAME separator the result partial draws
   and the swap does not move the page under the reader.

   No new colour. A row is ink, the accent, or one of the two reserved states,
   and the glyph column carries the meaning that a colour alone would not. */

.run-progress {
  margin-top: var(--space-5);
  border-top: 1px solid var(--line);
  padding-top: var(--space-4);
}

/* The head of the panel: what state the run is in, how long it has been going,
   and the way out of watching it. It lays itself out rather than borrowing
   `.row`, whose `.grow` child carries an 18rem flex-basis. That basis is right
   for a form field and wrong for a clock: on a 360px phone this panel is about
   300px wide, and a 288px basis on the figure broke the chip, the clock and
   the button onto three lines each of their own. Here the clock takes whatever
   is left, and the button sits at the end of the line the chip is already on. */
.run-head {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  flex-wrap: wrap;
}

.run-elapsed { flex: 1 1 auto; min-width: 0; }
.run-head [data-role="stop-waiting"] { margin-left: auto; }

/* Two more statuses in the existing chip vocabulary. `running` is the accent
   rather than a health colour: a run in flight is not good news or bad news
   yet. `stopped` is idle, because leaving the page is not a failure. */
.chip[data-status="running"] { color: var(--accent); background: var(--accent-wash); }
.chip[data-status="stopped"] { color: var(--idle);   background: var(--idle-wash); }

/* How long this usually takes, drawn against how long it is taking. */
.run-expect { margin-top: var(--space-3); }
.run-expect p { margin: var(--space-2) 0 0; }

.run-bar {
  height: 4px;
  border-radius: var(--radius-pill);
  background: var(--panel-sunken);
  overflow: hidden;
}

/* Full width, scaled down. The fill is the only thing on the site redrawn ten
   times a second, and animating `width` puts every one of those frames through
   layout; a transform is composited and touches nothing around it. The script
   writes `scaleX(f)`, so the resting state has to be a scale rather than a
   width or the bar would open full. */
.run-bar-fill {
  height: 100%;
  width: 100%;
  border-radius: inherit;
  background: var(--accent);
  transform: scaleX(0);
  transform-origin: left;
  transition: transform var(--medium) var(--ease);
}

/* No estimate, or one the run has already blown through. The bar moves and
   never arrives, which is the honest shape for "we do not know". The scale is
   dropped here: this fill is a sliding 35%, and a leftover `scaleX` from the
   determinate phase would shrink it to nothing on the way past. */
.run-bar.indeterminate .run-bar-fill {
  width: 35%;
  transform: none;
  animation: run-bar-slide 1.2s linear infinite;
}

@keyframes run-bar-slide {
  from { translate: -100% 0; }
  to { translate: 300% 0; }
}

/* The timeline. Three columns, because the glyph and the time have to line up
   down the list or the rows read as a paragraph. */
.run-steps {
  list-style: none;
  margin: var(--space-3) 0 0;
  padding: 0;
  display: grid;
  gap: var(--space-1);
}

.run-step {
  display: grid;
  grid-template-columns: 1.25rem minmax(0, 1fr) auto;
  column-gap: var(--space-2);
  align-items: baseline;
  font-size: var(--text-sm);
  color: var(--ink-soft);
}

/* A row that resolved is full ink, whichever way it went. A failure is not
   dimmed and is never folded away: it is the most useful line on the page when
   something is wrong. */
.run-step[data-state="ok"] { color: var(--ink); }
.run-step[data-state="bad"] { color: var(--ink); }

.run-glyph {
  font-family: var(--font-mono);
  text-align: center;
  color: var(--ink-faint);
}

.run-step[data-state="ok"] .run-glyph { color: var(--ok); }
.run-step[data-state="bad"] .run-glyph { color: var(--bad); }

/* A vendor's failure reason is arbitrary text at arbitrary length, and it is
   the one string here nobody can shorten. It wraps rather than widening its
   column and taking the page sideways with it. */
.run-text { min-width: 0; overflow-wrap: anywhere; }
.run-time { color: var(--ink-faint); font-size: var(--text-xs); }

/* The orb takes the existing glyph column, so a pending row and its settled
   check or cross keep exactly the same geometry. */
.run-glyph .pending-orb { display: block; }

/* The narration after the run: kept, and collapsed, because it is evidence
   rather than the answer. */
details.run-log { margin-top: var(--space-3); }
details.run-log .run-steps { margin-top: 0; }

/* A refusal the script writes needs the same air the server-rendered one gets.
   Both land in places nothing else styles: the slot is empty until a run puts
   something in it, and a notice appended to a live panel would otherwise sit
   flush against the last step of the timeline it is explaining. The slot gets
   the wider gap because it is standing in for a whole result panel; inside the
   panel the notice is one more line in an argument already under way. */
[data-role="run-slot"] > .notice { margin-top: var(--space-4); }
.run-progress > .notice { margin-top: var(--space-3); }

/* The panel is the only part of the site that moves for several seconds at a
   time, so it is also the part that most needs to hold still on request.
   Everything still CHANGES; it just stops moving. */
@media (prefers-reduced-motion: reduce) {
  /* Still a bar, still obviously not a measurement. 0.35 dropped it to a wash
     a reader could take for a disabled control on the sunken track behind it;
     half strength reads as deliberate. */
  .run-bar.indeterminate .run-bar-fill { animation: none; width: 100%; opacity: 0.5; }
  .run-bar-fill { transition: none; }
}

/* --------------------------------------------------------------------------
   14. Details / disclosure
   -------------------------------------------------------------------------- */

details.disclosure {
  border: 1px solid var(--line);
  border-radius: var(--radius-md);
  background: var(--panel);
}

details.disclosure > summary {
  padding: var(--space-3) var(--space-4);
  cursor: pointer;
  font-size: var(--text-sm);
  font-weight: 600;
  list-style: none;
}

details.disclosure > summary::-webkit-details-marker { display: none; }

details.disclosure > summary::after {
  content: "+";
  float: right;
  color: var(--ink-faint);
  font-family: var(--font-mono);
}

details.disclosure[open] > summary::after { content: "\2212"; }
details.disclosure > *:not(summary) { padding: 0 var(--space-4) var(--space-4); }

/* Definition list used for document metadata: operator, effective date, contact. */
.meta-list { margin: 0; display: grid; gap: var(--space-3); }
.meta-list > div { display: grid; gap: var(--space-1); }
.meta-list dt { margin: 0; }
.meta-list dd { margin: 0; }

@media (min-width: 40rem) {
  .meta-list { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

/* --------------------------------------------------------------------------
   15. Long-form legal copy
   -------------------------------------------------------------------------- */

.legal-body h2 { margin: var(--space-6) 0 var(--space-3); }
.legal-body h3 { margin: var(--space-5) 0 var(--space-2); }
.legal-body ul, .legal-body ol { padding-left: 1.25rem; }
.legal-body li { margin: var(--space-2) 0; }
.legal-body p, .legal-body li { max-width: var(--measure); }

/* --------------------------------------------------------------------------
   16. Utilities
   -------------------------------------------------------------------------- */

.sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}

/* Visible once focused, so a keyboard user can jump the nav. */
.skip-link {
  position: absolute;
  left: var(--space-3);
  top: -3rem;
  z-index: 50;
  padding: var(--space-2) var(--space-3);
  border-radius: var(--radius-md);
  background: var(--accent);
  color: var(--accent-ink);
  font-weight: 600;
  text-decoration: none;
}

.skip-link:focus { top: var(--space-3); }

[hidden] { display: none !important; }

/* --------------------------------------------------------------------------
   16b. Nothing may push the page sideways
   --------------------------------------------------------------------------
   A flex or grid item defaults to `min-width: auto`, which means it refuses to
   shrink below its CONTENT. One long endpoint URL or one `claude mcp add`
   command then widens its container, which widens the page, and the whole
   document scrolls sideways on a phone. Measured at 360px before this block:
   scrollWidth 591.

   Wide content is still readable - it scrolls inside its own box, which is what
   `.table-wrap` and `pre.snippet` already do. What it may not do is take the
   page with it. */

.nav,
.step,
.step-body,
.grid > *,
.stack > *,
.row > *,
.card,
.masthead-end {
  min-width: 0;
}

.steps { grid-template-columns: minmax(0, 1fr); }

/* Do not mask regressions with document-level clipping. Every wide component
   owns its overflow so keyboard users can still reach all of its content. */

/* --------------------------------------------------------------------------
   17. Responsive
   -------------------------------------------------------------------------- */

@media (max-width: 48rem) {
  :root {
    --space-5: 1rem;
    --space-6: 1.5rem;
    --space-7: 2rem;
  }

  .masthead-inner { padding: 0 var(--space-4); gap: var(--space-3); }
  .footer-inner { padding: 0 var(--space-4); }

  .desktop-nav { display: none; }
  .mobile-nav { display: block; }

  .account-sheet {
    max-width: calc(100vw - var(--space-4) * 2);
  }
  main {
    padding: calc(var(--space-5) + 1rem) var(--space-4) var(--space-7);
  }
  .card { padding: var(--space-4); }
  h1 { font-size: var(--text-lg); }
}

/* --------------------------------------------------------------------------
   18. Reduced motion
   --------------------------------------------------------------------------
   Not a nicety. Vestibular disorders are real and a dashboard is not the place
   to insist on movement. Everything below still CHANGES; it just stops moving. */

@media (prefers-reduced-motion: reduce) {
  *,
  *::before,
  *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
    scroll-behavior: auto !important;
  }

  .btn:active:not(:focus-visible):not([disabled]):not([aria-disabled="true"]),
  .copy:active:not(:focus-visible),
  .icon-btn:active:not(:focus-visible) { scale: 1; }

  .pending-orb,
  .pending-orb-ring,
  .pending-orb-spin { animation: none !important; }

  .pending-orb-ring {
    opacity: 0.8;
    transform: rotate(22.5deg);
  }
}

/* --------------------------------------------------------------------------
   19. Print
   -------------------------------------------------------------------------- */

@media print {
  .masthead, .nav, .mobile-nav, .btn, .copy, .icon-btn, .skip-link { display: none !important; }
  body { background: #fff; color: #000; }
  .card { border: 1px solid #999; box-shadow: none; }
}

/* --------------------------------------------------------------------------
   20. Source catalog
   -------------------------------------------------------------------------- */

/* A free source is the answer to "what do I have to buy", so it reads
   differently from one that needs a key - without using a status colour to
   say so. */
.provider.free { background: var(--panel-sunken); }

.setup-steps {
  margin: 0;
  padding-left: 1.25rem;
}

.setup-steps li { margin: var(--space-2) 0; }

/* An outbound link leaves the product. Spacing it from the auth badge stops
   "One API key" and "Open Polygon.io" reading as one control. */
.provider .badge { align-self: center; }

/* --------------------------------------------------------------------------
   21. Activity sparkline
   --------------------------------------------------------------------------
   One series, so no legend and no categorical palette - the caption names it.
   The bar is ink; the failure foot is the reserved failure colour, which is a
   STATUS and therefore allowed to be one of the four. Nothing else here gets a
   hue. */

.spark {
  margin: 0 0 var(--space-4);
}

.spark svg {
  display: block;
  max-width: 100%;
  /* hidden, not visible: a mark that paints outside its own box lands on
     whatever is above it, which here was the sticky masthead. */
  overflow: hidden;
}

.spark-bar { fill: var(--ink-soft); }
.spark-bar-error { fill: var(--bad); }

/* The hit target is the whole column, not the drawn bar, so a quiet hour that
   renders one pixel tall is still hoverable. */
.spark g { cursor: default; }
.spark g:hover .spark-bar { fill: var(--ink); }

.spark figcaption { margin-top: var(--space-2); }

/* --------------------------------------------------------------------------
   22. MCP workspace
   --------------------------------------------------------------------------
   Overview, Sources, Tools and Activity are separate destinations, but they
   should still feel like one instrument. These rules share the same density,
   selection language and responsive breakpoints without turning every piece
   of information into another decorative card. */

/* The product mark and every navigation destination keep a full pointer and
   thumb target even when their visible contents are compact. */
.mark,
.nav a,
.account-sheet a,
.page-footer a {
  min-height: 44px;
}

.nav a,
.account-sheet a,
.page-footer a {
  display: inline-flex;
  align-items: center;
}

.page-footer a {
  min-width: 44px;
  justify-content: center;
}

.mobile-nav[open] > summary,
.account-menu[open] > summary {
  color: var(--ink);
  background: var(--panel-sunken);
}

/* Overview: one bounded summary per destination, with the action consistently
   anchored at the bottom. Metric dividers carry structure without nesting
   smaller cards inside the cards. */
[data-overview-root] > .lede {
  margin-top: var(--space-3);
}

.overview-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 18rem), 1fr));
  gap: var(--space-3);
}

.overview-grid > * {
  min-width: 0;
}

.overview-card {
  display: flex;
  flex-direction: column;
  min-height: 17rem;
}

.overview-card-head {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: var(--space-3);
  margin-bottom: var(--space-4);
}

.overview-card-head > * {
  min-width: 0;
}

.overview-card-head .eyebrow {
  margin-bottom: var(--space-1);
}

.overview-metrics {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(5.25rem, 1fr));
  margin: 0 0 var(--space-4);
  padding: var(--space-3) 0;
  border-top: 1px solid var(--line);
  border-bottom: 1px solid var(--line);
}

.overview-metrics > div {
  min-width: 0;
  padding: 0 var(--space-3);
}

.overview-metrics > div:first-child {
  padding-left: 0;
}

.overview-metrics > div + div {
  border-left: 1px solid var(--line);
}

.overview-metrics dt {
  color: var(--ink-faint);
  font-size: var(--text-xs);
  font-weight: 600;
  letter-spacing: 0.06em;
  text-transform: uppercase;
}

.overview-metrics dd {
  margin: var(--space-1) 0 0;
  color: var(--ink);
  font-size: var(--text-lg);
  font-weight: 650;
  font-variant-numeric: tabular-nums;
  line-height: 1.15;
}

.overview-primary-metric {
  display: flex;
  align-items: baseline;
  gap: var(--space-2);
  margin: 0 0 var(--space-3);
  color: var(--ink-soft);
}

.overview-primary-metric strong {
  color: var(--ink);
  font-size: var(--text-xl);
  font-variant-numeric: tabular-nums;
  letter-spacing: -0.03em;
  line-height: 1;
}

.overview-card > .btn {
  align-self: flex-start;
  margin-top: auto;
}

.overview-recommendation {
  display: flex;
  align-items: flex-end;
  justify-content: space-between;
  gap: var(--space-5);
  margin-top: var(--space-4);
}

.overview-recommendation > .grow {
  min-width: 0;
  max-width: var(--measure);
}

.overview-recommendation > .row {
  flex: 0 0 auto;
  justify-content: flex-end;
}

/* The guide is one progress rail instead of four unrelated elevated cards.
   Current and completed steps retain the established accent and status
   vocabulary, respectively. */
[data-overview-onboarding] .steps {
  gap: 1px;
  overflow: hidden;
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  background: var(--line);
}

[data-overview-onboarding] .step {
  align-items: center;
  gap: var(--space-3);
  padding: var(--space-3);
  border: 0;
  border-radius: 0;
  box-shadow: none;
}

[data-overview-onboarding] .step[aria-current="step"] {
  background: var(--accent-wash);
  box-shadow: inset 3px 0 0 var(--accent);
}

/* Tool master index. The name and description are deliberately constrained to
   one line each, so every row remains a predictable two-line scan target. */
.tool-explorer {
  padding: var(--space-5);
  border-radius: var(--radius-lg);
  background: var(--panel);
  box-shadow: var(--shadow-2);
}

.tool-explorer > .section-head {
  margin-bottom: var(--space-4);
}

.tool-catalog-controls {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  align-items: end;
  gap: var(--space-3);
  padding-bottom: var(--space-4);
  border-bottom: 1px solid var(--line);
}

.tool-catalog-controls .field {
  min-width: 0;
  margin: 0;
}

.tool-filters {
  display: flex;
  align-items: center;
  gap: var(--space-1);
  flex-wrap: wrap;
}

.tool-filters .btn[aria-pressed="true"] {
  border-color: var(--accent);
  background: var(--accent-wash);
  color: var(--accent);
  box-shadow: inset 0 0 0 1px var(--accent);
}

.tool-explorer [data-role="tool-result-count"] {
  margin: var(--space-3) 0 0;
}

.tool-index {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 1px;
  overflow: hidden;
  margin: var(--space-2) 0 0;
  padding: 0;
  border: 1px solid var(--line);
  border-radius: var(--radius-md);
  background: var(--line);
  list-style: none;
}

.tool-index > li {
  min-width: 0;
  background: var(--panel);
}

.tool-index-link {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  min-height: 3.75rem;
  padding: var(--space-2) var(--space-3);
  background: var(--panel);
  color: var(--ink);
  text-decoration: none;
}

@media (hover: hover) and (pointer: fine) {
  .tool-index-link:hover {
    background: var(--panel-sunken);
  }
}

.tool-index-copy {
  display: grid;
  flex: 1 1 auto;
  gap: 2px;
  min-width: 0;
  line-height: 1.25;
}

.tool-index-copy > strong,
.tool-index-copy > .small {
  display: block;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.tool-index-link > .chip {
  flex: none;
}

/* Selection means "this detail is open". A full inset ring instead means the
   tool is executing, including when its detail is not the selected one. */
.tool-index > li[aria-current="true"] .tool-index-link {
  background: var(--accent-wash);
  box-shadow: inset 3px 0 0 var(--accent);
}

.tool-index > li[data-run-active="true"] .tool-index-link {
  box-shadow: inset 0 0 0 2px var(--accent);
}

.tool-index > li[aria-current="true"][data-run-active="true"] .tool-index-link {
  background: var(--accent-wash);
  box-shadow: inset 3px 0 0 var(--accent),
    inset 0 0 0 2px var(--accent);
}

.tool-explorer > .empty {
  margin-top: var(--space-3);
}

.tool-panels {
  display: block;
  margin-top: var(--space-5);
}

.tool-panels > [data-role="tool-panel"] {
  box-shadow: var(--shadow-2);
}

.tool-panels > [data-role="tool-panel"]:focus {
  outline: 2px solid var(--focus);
  outline-offset: 2px;
}

/* Source discovery controls become a single scan row when space allows. The
   vendor cards remain one per row because their setup forms and disclosures
   need reading width, not a masonry effect. */
[data-source-catalog] [data-source-controls] {
  display: grid;
  grid-template-columns: minmax(16rem, 1.25fr) minmax(0, 2fr);
  align-items: end;
  column-gap: var(--space-4);
  row-gap: var(--space-3);
  padding: var(--space-4);
  box-shadow: var(--shadow-2);
}

[data-source-catalog] [data-source-controls] > .field,
[data-source-catalog] [data-source-controls] .grid.two > .field {
  min-width: 0;
  margin: 0;
}

[data-source-catalog] [data-source-controls] .grid.two {
  grid-template-columns: repeat(2, minmax(0, 1fr));
}

[data-source-catalog] [data-source-result-count],
[data-source-catalog] [data-source-controls] > noscript {
  grid-column: 1 / -1;
  margin: 0;
  padding-top: var(--space-3);
  border-top: 1px solid var(--line);
}

[data-source-catalog] > [data-source-empty] {
  margin-top: var(--space-4);
}

[data-source-vendor] {
  box-shadow: var(--shadow-1);
}

[data-source-vendor] .card-head h3 {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  flex-wrap: wrap;
}

/* Marks are compact orientation aids. The fixed tile reserves the same space
   for a local image and a permission-safe text fallback, preventing row shift
   as images decode. The adjacent visible provider name is the accessible
   label, so the artwork itself stays decorative. */
.source-heading-with-mark,
.source-provider-identity {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  min-width: 0;
}

.source-heading-with-mark > span:last-child,
.source-heading-label,
.source-provider-name {
  min-width: 0;
  text-wrap: balance;
}

.source-mark {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex: 0 0 2.75rem;
  inline-size: 2.75rem;
  block-size: 2.25rem;
  overflow: hidden;
  border-radius: var(--radius-md);
  outline: 1px solid var(--provider-mark-outline);
  outline-offset: -1px;
  background: var(--provider-mark-surface);
  box-shadow: var(--shadow-1);
  color: var(--provider-mark-ink);
}

.source-mark img {
  display: block;
  inline-size: 100%;
  block-size: 100%;
  padding: var(--space-1);
  object-fit: contain;
}

.source-mark[data-provider-slug="alpaca"] img { padding: 0.1875rem; }

.source-mark[data-provider-slug="tradier"] img {
  padding: var(--space-1) 0.1875rem;
}

.source-mark-fallback {
  font-size: var(--text-xs);
  font-weight: 750;
  letter-spacing: 0.035em;
  line-height: 1;
}

[data-source-vendor]:target {
  outline: 2px solid var(--focus);
  outline-offset: 2px;
}

[data-source-vendor] h3[tabindex="-1"]:focus {
  outline: 2px solid var(--focus);
  outline-offset: 3px;
}

/* Signed-in Sources is a connection manager first. Discovery and the large
   buying guide remain available without dominating the page. */
.source-management-list {
  display: grid;
  gap: var(--space-3);
}

.source-automatic-sources {
  margin-top: var(--space-3);
}

.source-automatic-list {
  display: grid;
  gap: var(--space-2);
  padding: var(--space-3) 0 0;
}

.source-automatic-row {
  display: grid;
  grid-template-columns: minmax(12rem, 1fr) auto minmax(12rem, 1.4fr);
  align-items: center;
  gap: var(--space-3);
  padding: var(--space-2) 0;
  border-top: 1px solid var(--line);
}

.source-your-sources {
  margin-top: var(--space-5);
}

.source-management-card {
  padding: var(--space-4);
}

.source-management-card-head {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: var(--space-4);
}

.source-management-card-head > * {
  min-width: 0;
}

.source-management-card h3 {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  flex-wrap: wrap;
  margin-bottom: var(--space-2);
}

.source-management-actions,
.source-connect-action {
  display: flex;
  align-items: center;
  justify-content: flex-end;
  gap: var(--space-2);
  flex-wrap: wrap;
}

.source-management-actions {
  flex: 0 0 auto;
}

.source-management-actions form {
  margin: 0;
}

.source-connection-summary {
  display: grid;
  grid-template-columns: minmax(16rem, 1.25fr) minmax(18rem, 1fr);
  gap: var(--space-4);
  margin-top: var(--space-3);
  padding-top: var(--space-3);
  border-top: 1px solid var(--line);
}

.source-status-explanation > :first-child,
.source-status-explanation > :last-child {
  margin-top: 0;
  margin-bottom: 0;
}

.source-status-explanation p {
  margin: var(--space-1) 0 0;
}

.source-connection-summary .source-account-facts {
  align-content: start;
  margin: 0;
}

.source-credential-editor,
.source-connect-disclosure {
  margin: 0;
}

.source-credential-editor > summary,
.source-connect-disclosure > summary {
  min-height: 40px;
  list-style: none;
}

.source-credential-editor > summary::-webkit-details-marker,
.source-connect-disclosure > summary::-webkit-details-marker {
  display: none;
}

.source-credential-editor > summary::after,
.source-connect-disclosure > summary::after {
  content: none;
}

.source-credential-editor > div {
  position: absolute;
  z-index: 5;
  right: var(--space-4);
  width: min(30rem, calc(100vw - (2 * var(--space-4))));
  margin-top: var(--space-2);
  padding: var(--space-4);
  border-radius: var(--radius-lg);
  background: var(--panel);
  box-shadow: var(--shadow-3);
}

.source-connect-disclosure > div {
  margin-top: var(--space-3);
}

.source-catalog-disclosure,
.source-comparison-disclosure {
  border-radius: var(--radius-lg);
  background: var(--panel);
  box-shadow: var(--shadow-1);
}

.source-catalog-disclosure > summary,
.source-comparison-disclosure > summary {
  display: flex;
  align-items: center;
  min-height: 4.5rem;
  padding: var(--space-3) var(--space-4);
  cursor: pointer;
}

.source-catalog-disclosure > summary > span,
.source-comparison-disclosure > summary > span {
  display: grid;
  gap: var(--space-1);
}

.source-catalog-disclosure > summary span span,
.source-comparison-disclosure > summary span span {
  color: var(--ink-faint);
  font-size: var(--text-sm);
}

.source-catalog-disclosure-body,
.source-comparison-disclosure-body {
  padding: 0 var(--space-4) var(--space-4);
  border-top: 1px solid var(--line);
}

.source-available-sources {
  margin-top: var(--space-5);
}

.source-comparison-disclosure {
  margin-top: var(--space-5);
}

.source-no-connections {
  margin: 0;
}

/* The buying guide is a comparison instrument, not a second collection of
   vendor cards. A quiet frame and row dividers preserve column alignment on
   wide screens; the narrow layout below turns every access row into a labeled
   record without introducing document-level horizontal scrolling. */
.source-comparison-frame {
  overflow: hidden;
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  background: var(--panel);
  box-shadow: var(--shadow-1);
}

.source-comparison {
  width: 100%;
  table-layout: fixed;
  border-collapse: collapse;
}

.source-comparison th,
.source-comparison td {
  min-width: 0;
  padding: var(--space-3);
  border-bottom: 1px solid var(--line);
  vertical-align: top;
  overflow-wrap: anywhere;
  text-align: left;
}

.source-comparison th {
  color: var(--ink-faint);
  background: var(--panel-sunken);
  font-size: var(--text-xs);
  font-weight: 650;
  letter-spacing: 0.05em;
  text-transform: uppercase;
}

.source-comparison tbody:last-child tr:last-child > * {
  border-bottom: 0;
}

.source-comparison tbody + tbody tr:first-child > * {
  border-top: 1px solid var(--line-strong);
}

.source-comparison-provider {
  width: 14%;
}

.source-comparison-product {
  width: 17%;
}

.source-comparison-access {
  width: 20%;
}

.source-comparison-freshness {
  width: 12%;
}

.source-comparison-limit {
  width: 12%;
}

.source-comparison-price {
  width: 11%;
}

.source-comparison-rights {
  width: 14%;
}

.source-comparison td > :first-child {
  margin-top: 0;
}

.source-comparison td > :last-child {
  margin-bottom: 0;
}

.source-comparison-summary > * {
  color: var(--ink-soft);
  background: var(--panel);
  font-size: var(--text-sm);
}

.source-summary-product {
  display: block;
}

.source-summary-product + .source-summary-product {
  margin-top: var(--space-2);
}

.source-comparison [data-fact-state="verify"] {
  color: var(--warn);
  font-weight: 650;
}

.source-comparison [data-rights-state="cleared"] {
  color: var(--ok);
}

.source-comparison [data-rights-state]:not([data-rights-state="cleared"]) {
  color: var(--warn);
}

.source-provider-name {
  display: block;
  color: var(--ink);
  font-size: var(--text-md);
  line-height: 1.25;
  letter-spacing: normal;
  text-transform: none;
}

.source-provider-contribution,
.source-product-meta,
.source-fact-scope {
  display: block;
  margin-top: var(--space-1);
  color: var(--ink-faint);
  font-size: var(--text-xs);
  line-height: 1.45;
  letter-spacing: normal;
  text-transform: none;
}

.source-comparison-links {
  display: flex;
  gap: var(--space-2);
  flex-wrap: wrap;
  margin-top: var(--space-2);
}

.source-comparison-links a {
  display: inline-flex;
  align-items: center;
  min-height: 44px;
}

.source-requirements {
  margin: var(--space-2) 0 0;
  padding-left: 1rem;
  color: var(--ink-soft);
  font-size: var(--text-xs);
}

.source-requirements li + li {
  margin-top: var(--space-1);
}

.source-guide-note {
  margin-bottom: var(--space-4);
}

.source-management-heading {
  margin-top: var(--space-6);
}

.source-account-facts {
  display: flex;
  gap: var(--space-3) var(--space-5);
  flex-wrap: wrap;
  margin: var(--space-3) 0 0;
  padding: var(--space-3);
  border-radius: var(--radius-md);
  background: var(--panel-sunken);
  box-shadow: inset 0 0 0 1px var(--line);
}

.source-account-facts > div {
  min-width: 10rem;
}

.source-account-facts dt {
  color: var(--ink-faint);
  font-size: var(--text-xs);
  font-weight: 650;
  letter-spacing: 0.04em;
  text-transform: uppercase;
}

.source-account-facts dd {
  margin: var(--space-1) 0 0;
  color: var(--ink);
  font-size: var(--text-sm);
  font-variant-numeric: tabular-nums;
}

.source-demo-warning {
  margin-top: var(--space-3);
}

/* Activity is evidence, so the chart and tables use quiet structural surfaces.
   Wide tables scroll inside their own frames instead of compressing labels or
   widening the document. */
[data-activity-root] > .section-head {
  display: flex;
  align-items: flex-end;
  justify-content: space-between;
  gap: var(--space-4);
}

[data-activity-root] > .section-head > * {
  min-width: 0;
}

[data-activity-root] [data-role="activity-freshness"],
[data-activity-root] [data-role="activity-failure-count"] {
  flex: 0 0 auto;
  margin: 0;
  text-align: right;
  font-variant-numeric: tabular-nums;
}

[data-activity-root] [data-role="activity-freshness"][data-refreshing="true"] {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
}

[data-activity-root] > .spark {
  margin: var(--space-4) 0 0;
  padding: var(--space-4);
  border-radius: var(--radius-md);
  background: var(--panel-sunken);
}

[data-activity-root] > .spark svg {
  width: min(100%, 38rem);
  height: auto;
}

[data-activity-root] .table-wrap {
  background: var(--panel);
  box-shadow: var(--shadow-1);
}

[data-activity-root] [data-role="activity-tools-table"] table {
  min-width: 38rem;
}

[data-activity-root] [data-role="activity-failures-table"] table {
  min-width: 54rem;
}

[data-activity-root] td a {
  display: inline-flex;
  align-items: center;
  min-height: 44px;
}

[data-activity-root][aria-busy="true"] {
  cursor: progress;
}

/* Linked tags are visually compact but retain the same 40px interaction floor
   as buttons and navigation. */
a.tag {
  display: inline-flex;
  align-items: center;
  min-height: 44px;
}

@media (min-width: 64rem) {
  [data-overview-onboarding] .steps {
    grid-template-columns: repeat(4, minmax(0, 1fr));
  }

  [data-overview-onboarding] .step[aria-current="step"] {
    box-shadow: inset 0 3px 0 var(--accent);
  }
}

/* Seven evidence columns become a wall of narrow text on tablets. Switch the
   guide to labeled records before that happens, not one pixel after it. */
@media (max-width: 64rem) {
  .source-comparison-frame {
    border-radius: var(--radius-md);
  }

  .source-comparison,
  .source-comparison tbody,
  .source-comparison tr,
  .source-comparison td,
  .source-comparison th[scope="row"],
  .source-comparison th[scope="rowgroup"] {
    display: block;
    width: 100%;
  }

  .source-comparison thead {
    position: absolute;
    width: 1px;
    height: 1px;
    padding: 0;
    margin: -1px;
    overflow: hidden;
    clip: rect(0, 0, 0, 0);
    white-space: nowrap;
    border: 0;
  }

  .source-comparison tbody + tbody tr:first-child > * {
    border-top: 0;
  }

  .source-comparison tbody + tbody {
    border-top: 2px solid var(--line-strong);
  }

  .source-comparison tr {
    padding: var(--space-3) var(--space-4);
  }

  .source-comparison tr + tr {
    border-top: 1px solid var(--line);
  }

  .source-comparison td,
  .source-comparison th[scope="row"] {
    display: grid;
    grid-template-columns: minmax(6.75rem, 0.4fr) minmax(0, 1fr);
    gap: var(--space-3);
    padding: var(--space-2) 0;
    border: 0;
  }

  .source-comparison td::before,
  .source-comparison th[scope="row"]::before {
    content: attr(data-label);
    color: var(--ink-faint);
    font-size: var(--text-xs);
    font-weight: 650;
    letter-spacing: 0.04em;
    text-transform: uppercase;
  }

  .source-comparison th[scope="row"] {
    color: var(--ink);
    background: transparent;
    font-size: var(--text-sm);
    font-weight: 400;
    letter-spacing: normal;
    text-transform: none;
  }

  .source-comparison th.source-comparison-provider {
    display: block;
    width: 100%;
    padding-bottom: var(--space-3);
    text-transform: none;
  }

  .source-comparison th.source-comparison-provider::before {
    content: none;
  }
}

@media (max-width: 48rem) {
  .overview-card {
    min-height: 0;
  }

  .overview-recommendation {
    align-items: stretch;
    flex-direction: column;
  }

  .overview-recommendation > .row {
    justify-content: flex-start;
  }

  .tool-explorer {
    padding: var(--space-4);
  }

  .tool-catalog-controls {
    grid-template-columns: minmax(0, 1fr);
  }

  .tool-filters {
    display: grid;
    grid-template-columns: repeat(3, minmax(0, 1fr));
  }

  .tool-filters .btn {
    width: 100%;
    padding-inline: var(--space-2);
  }

  .tool-index {
    grid-template-columns: minmax(0, 1fr);
  }

  [data-source-catalog] [data-source-controls],
  [data-source-catalog] [data-source-controls] .grid.two {
    grid-template-columns: minmax(0, 1fr);
  }

  .source-management-card-head,
  .source-connection-summary {
    grid-template-columns: minmax(0, 1fr);
    align-items: stretch;
  }

  .source-management-card-head {
    flex-direction: column;
  }

  .source-management-actions,
  .source-connect-action {
    justify-content: flex-start;
  }

  .source-automatic-row {
    grid-template-columns: minmax(0, 1fr) auto;
  }

  .source-automatic-row > .small {
    grid-column: 1 / -1;
  }

  .source-credential-editor > div {
    position: static;
    width: auto;
  }

  [data-activity-root] > .section-head {
    align-items: flex-start;
    flex-direction: column;
    gap: var(--space-2);
  }

  [data-activity-root] [data-role="activity-freshness"],
  [data-activity-root] [data-role="activity-failure-count"] {
    text-align: left;
  }
}

@media (max-width: 32rem) {
  /* Terms and Privacy remain in the footer. Repeating them beside the public
     CTA made the signed-out masthead 10px wider than a 375px viewport and
     pushed Sign in off the right edge. */
  .public-masthead .masthead-legal { display: none; }

  /* Preserve the account name for assistive technology while freeing enough
     horizontal room for the mark and the two 40px disclosure controls. */
  .account-menu .who {
    position: absolute;
    width: 1px;
    height: 1px;
    padding: 0;
    margin: -1px;
    overflow: hidden;
    clip: rect(0, 0, 0, 0);
    white-space: nowrap;
    border: 0;
  }

  .account-menu-label { display: inline; }

  .overview-metrics > div {
    padding-inline: var(--space-2);
  }

  .tool-index-link {
    align-items: flex-start;
  }

  .tool-index-link > .chip {
    margin-top: 1px;
  }
}

@media (max-width: 24rem) {
  .masthead-inner {
    gap: var(--space-2);
    padding-inline: var(--space-2);
  }

  .footer-inner { padding-inline: var(--space-2); }

  .mark-icon { width: 4rem; height: 4rem; margin-bottom: -1.75rem; }
  .mobile-nav > summary,
  .account-menu > summary { padding-inline: var(--space-2); }
}

.legacy-settings-target { display: none; }
.legacy-settings-target:target {
  display: block;
  margin-bottom: var(--space-4);
}

/* --------------------------------------------------------------------------
   23. Landing
   --------------------------------------------------------------------------
   The spec calls this section "9. Landing". Sections 9 through 22 were already
   taken by the time it was written, so it lands at the end of the file where a
   new section belongs; the name is the part that matters.

   The signed-in application follows the reader's colour scheme, because it is a
   working surface somebody sits in front of for an hour. The landing page does
   not. It is a poster: one ground, one illustration, two accents, and type
   doing the rest. So the whole ledger palette is declared HERE, scoped, and
   `:root` is left exactly as it was - retheming the app to sell the app is how
   a design system dies.

   Burgundy and gold are the only accents. They are decoration, not status: no
   rule in this section may borrow green, red, amber or slate-blue, because
   those four still mean valid, invalid, degraded and vendor-down everywhere
   else in the product. */

main.landing,
body:has(main.landing) {
  /* Warm near-black. The neutral #0b0c0e this page used before is the right
     ground for a dashboard and the wrong one for a portrait: it made the
     Raider's browns read as dirt on the screen. */
  --page: #171311;
  --panel: #1f1a17;
  --panel-sunken: #120f0d;
  --line: rgba(247, 240, 225, 0.12);
  --line-strong: rgba(247, 240, 225, 0.22);

  --ink: #f7f0e1;
  --ink-soft: #d6cdba;
  --ink-faint: #a99f8c;

  /* Gold is the accent, and therefore the primary button and the numerals.
     Measured against the ground it clears AA comfortably at every size used
     here, which is why it can carry a figure and not just a flourish. */
  --accent: #c9a24a;
  --accent-hover: #d9b463;
  --accent-ink: #171311;
  --accent-wash: rgba(201, 162, 74, 0.14);
  --focus: #e2c67e;

  /* Burgundy. Never a text colour on this ground; it is the rule, the step
     numeral's field, and the band that separates one idea from the next. */
  --lp-burgundy: #711f32;
  --lp-burgundy-soft: rgba(113, 31, 50, 0.55);

  --lp-outline: rgba(255, 255, 255, 0.1);
  --lp-panel-pad: var(--space-4);
  --lp-panel-radius: 20px;

  /* Fraunces carries every display line. The fallback is a real serif stack,
     so a blocked font host costs character and not legibility. */
  --font-display: "Fraunces", Georgia, "Times New Roman", serif;
}

body:has(main.landing) {
  background: var(--page);
  color: var(--ink);
}

/* The masthead and footer inherit the ledger ground from the body rather than
   each carrying an override, so there is exactly one place that decides what
   colour this page is. */
body:has(main.landing) .masthead {
  border-bottom-color: var(--line);
}

/* The hero is full bleed; the sections inside it keep the shell's column. */
main.landing {
  max-width: none;
  padding: 0;
}

main.landing .lp-hero-inner,
main.landing .lp-band-inner,
main.landing .lp-section,
main.landing .lp-close {
  width: 100%;
  max-width: var(--shell);
  margin-inline: auto;
  padding-inline: var(--space-5);
}

.lp-display,
.lp-h2,
.lp-close-line,
.lp-terms dt {
  font-family: var(--font-display);
  font-variation-settings: "SOFT" 30;
  font-weight: 600;
  text-wrap: balance;
}

/* 23.1 Hero
   -------------------------------------------------------------------------- */

.lp-hero {
  position: relative;
  display: flex;
  align-items: center;
  height: 100svh;
  min-height: 640px;
  max-height: 900px;
  overflow: hidden;
  background: var(--page);
  border-bottom: 1px solid var(--line);
}

.lp-hero-media {
  position: absolute;
  inset: 0;
  z-index: 0;
}

/* The poster is the floor, not a placeholder: it is what a phone and a reader
   who asked for less motion actually see, so it is always painted and the loop
   sits on top of it. */
.lp-hero-poster,
.lp-hero-video {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  /* He stands in the left third and the copy sits opposite him. The first cut
     of this put the type over him, which meant the one illustration on the
     page was a storm with a headline on it. */
  object-position: 18% center;
}

/* A one-directional wash rather than a flat overlay. Flat would have cost the
   left-hand half of the image, which is the half with the Raider in it, to buy
   contrast the right-hand half needed. */
.lp-hero-scrim {
  position: absolute;
  inset: 0;
  background: linear-gradient(
    270deg,
    rgba(23, 19, 17, 0.94) 0%,
    rgba(23, 19, 17, 0.6) 42%,
    rgba(23, 19, 17, 0.08) 100%
  );
}

.lp-hero-inner {
  position: relative;
  z-index: 1;
  display: grid;
  grid-template-columns: minmax(0, 54fr) minmax(0, 46fr);
  gap: var(--space-6);
  align-items: center;
}

/* Right column, but the text inside it stays left-aligned. Centred display
   type at this size reads as a poster for a film rather than a product. */
.lp-hero-copy {
  grid-column: 2;
  max-width: 34rem;
}

.lp-eyebrow {
  margin: 0 0 var(--space-4);
  font-family: var(--font-mono);
  font-size: var(--text-xs);
  font-weight: 600;
  letter-spacing: 0.28em;
  text-transform: uppercase;
  color: var(--accent);
}

/* The clamp is sized against the RIGHT COLUMN, not the window. At the old
   full-width maximum the headline broke into five ragged lines in a 46% column
   and orphaned "your AI" on one of them. */
.lp-display {
  margin: 0 0 var(--space-5);
  font-size: clamp(2.5rem, 4.4vw, 4rem);
  line-height: 1.02;
  letter-spacing: -0.025em;
  color: var(--ink);
}

.lp-sub {
  margin: 0 0 var(--space-6);
  max-width: 34rem;
  font-size: var(--text-lg);
  line-height: 1.5;
  color: color-mix(in srgb, var(--ink) 80%, transparent);
  text-wrap: pretty;
}

.lp-cta {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-3);
  margin-bottom: var(--space-5);
}

.lp-cta-centre { justify-content: center; }

.lp-btn-gold,
.lp-btn-ghost {
  padding: var(--space-3) var(--space-5);
  font-size: var(--text-base);
}

.lp-btn-ghost {
  background: transparent;
  border-color: var(--line-strong);
  color: var(--ink);
}

@media (hover: hover) and (pointer: fine) {
  .lp-btn-ghost:hover {
    background: rgba(247, 240, 225, 0.07);
    border-color: var(--ink-faint);
  }
}

/* The stats strip. Tabular figures because these are numbers a reader compares,
   and because a proportional 1 beside a proportional 4 makes a count look
   approximate. */
.lp-figures {
  margin: 0;
  font-family: var(--font-mono);
  font-size: var(--text-sm);
  font-variant-numeric: tabular-nums;
  color: var(--ink-faint);
}

.lp-figure {
  color: var(--accent);
  font-weight: 600;
  font-variant-numeric: tabular-nums;
}

.lp-figure-sep { padding: 0 0.35rem; color: var(--lp-burgundy); }

/* Entrance. One pass, 90ms apart, twelve pixels. Anything longer and the page
   is performing rather than arriving. */
@keyframes lp-rise {
  from { opacity: 0; transform: translateY(12px); }
  to { opacity: 1; transform: none; }
}

.lp-rise {
  animation: lp-rise 420ms var(--ease) both;
}

.lp-rise-1 { animation-delay: 0ms; }
.lp-rise-2 { animation-delay: 90ms; }
.lp-rise-3 { animation-delay: 180ms; }
.lp-rise-4 { animation-delay: 270ms; }
.lp-rise-5 { animation-delay: 360ms; }

/* 23.2 Bands and sections
   -------------------------------------------------------------------------- */

.lp-band {
  padding: var(--space-8) 0;
  background: var(--panel-sunken);
  border-bottom: 1px solid var(--line);
}

.lp-band-inner {
  display: grid;
  gap: var(--space-7);
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  align-items: center;
}

.lp-band-media {
  width: 100%;
  max-height: 520px;
  object-fit: cover;
  border-radius: var(--lp-panel-radius);
  outline: 1px solid var(--lp-outline);
  outline-offset: -1px;
}

.lp-section {
  padding-block: var(--space-8);
  border-bottom: 1px solid var(--line);
  scroll-margin-top: calc(var(--header-height) + 2rem + var(--space-4));
}

.lp-measure { max-width: 46rem; }

.lp-h2 {
  margin: 0 0 var(--space-3);
  font-size: clamp(1.75rem, 3.2vw, 2.75rem);
  line-height: 1.12;
  letter-spacing: -0.02em;
  color: var(--ink);
}

.lp-lede {
  margin: 0;
  font-size: var(--text-md);
  color: var(--ink-soft);
  text-wrap: pretty;
}

.lp-note {
  margin: var(--space-5) 0 0;
  font-size: var(--text-sm);
  color: var(--ink-faint);
}

.lp-link {
  display: inline-flex;
  align-items: center;
  min-height: 44px;
  color: var(--accent);
  font-size: var(--text-sm);
  font-weight: 600;
}

/* The numbered steps. A gold numeral in the mono face, so the sequence reads as
   a sequence without a bullet glyph doing the work. */
.lp-steps {
  margin: var(--space-6) 0 0;
  padding: 0;
  list-style: none;
  display: grid;
  gap: var(--space-4);
}

.lp-steps li {
  display: grid;
  grid-template-columns: 2rem minmax(0, 1fr);
  gap: var(--space-3);
  align-items: baseline;
}

.lp-steps p {
  margin: 0;
  color: var(--ink-soft);
  text-wrap: pretty;
}

.lp-step-n {
  font-family: var(--font-mono);
  font-size: var(--text-sm);
  font-weight: 600;
  font-variant-numeric: tabular-nums;
  color: var(--accent);
  border-bottom: 2px solid var(--lp-burgundy);
  padding-bottom: 2px;
}

/* 23.3 The keyless ledger
   --------------------------------------------------------------------------
   These were cards. A card per tool turned one list with three columns into a
   wall of boxes, and the reader had to re-find the name column on every box. */

.lp-ledger {
  margin: var(--space-6) 0 0;
  padding: 0;
  list-style: none;
  border-top: 1px solid var(--line);
}

.lp-ledger-row {
  display: grid;
  grid-template-columns: minmax(10rem, 16rem) minmax(0, 1fr) auto;
  gap: var(--space-4);
  align-items: baseline;
  padding: var(--space-4) var(--space-3);
  border-bottom: 1px solid var(--line);
}

@media (hover: hover) and (pointer: fine) {
  .lp-ledger-row {
    transition: background-color var(--fast) var(--ease);
  }

  .lp-ledger-row:hover {
    background: rgba(247, 240, 225, 0.04);
  }
}

.lp-ledger-name {
  font-family: var(--font-mono);
  font-size: var(--text-sm);
  font-weight: 500;
  color: var(--ink);
}

.lp-ledger-summary {
  color: var(--ink-soft);
  font-size: var(--text-sm);
  text-wrap: pretty;
}

.lp-ledger-chip {
  justify-self: end;
  padding: 0.15rem var(--space-2);
  border: 1px solid color-mix(in srgb, var(--accent) 45%, transparent);
  border-radius: var(--radius-pill);
  background: var(--accent-wash);
  color: var(--accent);
  font-family: var(--font-mono);
  font-size: var(--text-xs);
  white-space: nowrap;
}

/* 23.4 The client panel
   -------------------------------------------------------------------------- */

.lp-panel {
  margin-top: var(--space-6);
  padding: var(--lp-panel-pad);
  border: 1px solid var(--line);
  border-radius: var(--lp-panel-radius);
  background: var(--panel);
  box-shadow: var(--shadow-2);
}

.lp-panel-head {
  display: flex;
  gap: var(--space-4);
  justify-content: space-between;
  align-items: flex-start;
  flex-wrap: wrap;
}

.lp-panel-title {
  margin: 0;
  font-size: var(--text-md);
  color: var(--ink);
}

.lp-panel-sub {
  margin: var(--space-1) 0 0;
  font-size: var(--text-sm);
  color: var(--ink-soft);
}

.lp-badge {
  padding: 0.15rem var(--space-2);
  border: 1px solid var(--line-strong);
  border-radius: var(--radius-pill);
  font-size: var(--text-xs);
  color: var(--ink-faint);
  white-space: nowrap;
}

.lp-command {
  display: flex;
  gap: var(--space-3);
  align-items: stretch;
  margin-top: var(--space-4);
}

/* Concentric: the inner radius is the panel's minus the panel's padding, so
   the two curves are parallel instead of arguing. */
.lp-command-text {
  flex: 1 1 auto;
  min-width: 0;
  margin: 0;
  padding: var(--space-3);
  border: 1px solid var(--line);
  border-radius: calc(var(--lp-panel-radius) - var(--lp-panel-pad));
  background: var(--panel-sunken);
  color: var(--ink);
  font-size: var(--text-sm);
  overflow-x: auto;
}

.lp-copy { flex: none; }

.lp-panel-note {
  margin: var(--space-4) 0 0;
  font-size: var(--text-sm);
  color: var(--ink-faint);
}

.lp-panel-note strong { color: var(--ink); }

.lp-panel .lp-btn-ghost { margin-top: var(--space-4); }

/* 23.5 Terms
   -------------------------------------------------------------------------- */

.lp-terms {
  margin: var(--space-6) 0 0;
  display: grid;
  gap: 0;
  border-top: 1px solid var(--line);
}

.lp-term {
  display: grid;
  grid-template-columns: minmax(12rem, 20rem) minmax(0, 1fr);
  gap: var(--space-5);
  padding: var(--space-5) var(--space-3);
  border-bottom: 1px solid var(--line);
}

.lp-terms dt {
  margin: 0;
  font-style: italic;
  font-size: var(--text-md);
  color: var(--ink);
}

.lp-terms dd {
  margin: 0;
  color: var(--ink-soft);
  text-wrap: pretty;
}

/* 23.6 Close
   -------------------------------------------------------------------------- */

.lp-close {
  padding-block: var(--space-8);
  text-align: center;
}

.lp-close-line {
  margin: 0 0 var(--space-6);
  font-size: clamp(1.75rem, 4vw, 3rem);
  line-height: 1.1;
  letter-spacing: -0.02em;
  color: var(--ink);
}

.lp-close .lp-note { margin-top: var(--space-4); }

/* 23.7 Small screens
   --------------------------------------------------------------------------
   The loop is gone below 48rem: the poster carries the whole image at that
   size, and a 1.5MB video is somebody's data allowance. */

@media (max-width: 48rem) {
  .lp-hero-video { display: none; }

  /* Stacked, not layered. A 390px column has no room for a picture and a
     paragraph in the same place, and the compromise is always the paragraph. */
  .lp-hero {
    display: block;
    height: auto;
    min-height: 0;
    max-height: none;
    padding-block: 0 var(--space-8);
  }

  .lp-hero-media {
    position: relative;
    inset: auto;
    width: 100%;
    aspect-ratio: 16 / 10;
    border-bottom: 1px solid var(--line);
  }

  .lp-hero-poster { object-position: 20% center; }

  /* The scrim exists to make type legible over the picture. Nothing is over
     the picture any more. */
  .lp-hero-scrim { display: none; }

  .lp-hero-inner {
    display: block;
    padding-top: var(--space-6);
  }

  .lp-hero-copy { grid-column: auto; }

  .lp-band-inner { grid-template-columns: minmax(0, 1fr); }

  .lp-band-media { max-height: 320px; }

  .lp-ledger-row {
    grid-template-columns: minmax(0, 1fr) auto;
  }

  .lp-ledger-summary { grid-column: 1 / -1; }

  .lp-term { grid-template-columns: minmax(0, 1fr); gap: var(--space-2); }

  .lp-sub { font-size: var(--text-md); }
}

/* 23.8 Reduced motion
   --------------------------------------------------------------------------
   The loop is motion nobody asked for, so it goes entirely and the poster
   stands in its place. The entrance stagger resolves to its final state rather
   than being skipped, so nothing arrives invisible. */

@media (prefers-reduced-motion: reduce) {
  .lp-hero-video { display: none; }

  .lp-rise {
    animation: none;
    opacity: 1;
    transform: none;
  }

  .lp-ledger-row { transition: none; }
}
