/* Rink Research — the web view.

   Two themes, one set of names. Every colour in this file is a token defined
   three times: once for light, once for the OS preference, once for an
   explicit choice. Components never name a colour, so switching themes is a
   change to this block and nothing else.

   The rule the palette follows has not changed. Black (or, in dark, near-white)
   is the interface: buttons, headings, active state. The brand red is reserved
   for what is *live* or what the eye should reach first — a running job, the
   section you are reading, an error, and the data in a chart. If everything is
   accented, nothing is.

   The dark theme is dark, not a dimmed light theme. The page ground is
   #0b0b0e and cards sit above it at #141419, so surfaces separate by getting
   *lighter* as they come forward — the same direction light mode uses, with
   the sign flipped. Grey-on-grey was the thing to avoid.

   Chart colours live here too (--viz-*). The ordinal ramp is four steps of the
   brand hue, generated at the brand's own OKLCH hue and chroma and checked
   with the data-viz validator against each theme's real chart surface:
   monotone lightness, every adjacent gap over 0.06, and the step nearest the
   surface clearing 2:1 (2.39:1 light, 2.43:1 dark). --viz-ink-N is which of
   white or near-black stays legible *inside* step N, measured rather than
   guessed. */

:root {
  color-scheme: light;

  --ground:  #f4f4f6;   /* the page behind everything */
  --paper:   #ffffff;   /* cards, the document surface */
  --sunk:    #f0f0f3;   /* table headers, code, inset things */
  --ink:     #16161b;   /* body text, and the interface fill */
  --on-ink:  #ffffff;   /* text sitting on an ink-filled surface */
  --ink-hard:#000000;   /* the primary button, pressed */
  --muted:   #64646d;   /* secondary text */
  --faint:   #86868f;   /* meta, counts, timestamps */
  --rule:    #e4e4e9;   /* hairlines */
  --rule-2:  #d5d5dc;   /* hairlines that need to be seen */
  --brand:   #eb3459;
  --brand-ink: #c02248;
  --wash:    #fdf0f3;
  --term:    #1c1b21;   /* the machine's own voice */
  --term-ink:#e2dee4;
  --term-dim:#a8a3ae;
  --good:    #1c6b4b;
  --good-bg: #e9f4ef;
  --warn:    #8a5210;
  --warn-bg: #fbf1e3;

  --raised:    #ffffff;   /* fields and chips, on top of a card */
  --bar-bg:    rgba(255, 255, 255, .88);
  --row-hover: #fafafb;
  --backdrop:  rgba(28, 27, 33, .34);
  --pulse:     235, 52, 89;
  --shadow: 0 1px 2px rgba(20, 20, 30, .05), 0 8px 24px -16px rgba(20, 20, 30, .18);
  --shadow-rail: -12px 0 32px -24px rgba(20, 20, 30, .5);
  --shadow-fab:  0 -12px 32px -24px rgba(20, 20, 30, .5);
  --shadow-modal: 0 30px 70px -30px rgba(20, 20, 30, .45);
  --shadow-tip:  0 8px 26px -12px rgba(20, 20, 30, .6);

  /* charts — the ordinal ramp runs pale to deep as the value rises */
  --viz-surface: #ffffff;
  --viz-1: #fe828e;  --viz-ink-1: #0b0b0b;
  --viz-2: #f4305a;  --viz-ink-2: #0b0b0b;
  --viz-3: #c2003e;  --viz-ink-3: #ffffff;
  --viz-4: #890029;  --viz-ink-4: #ffffff;
  --viz-series: #eb3459;
  --viz-grid:  #e4e4e9;
  --viz-axis:  #c9c9d2;
  --viz-muted: #86868f;
  --viz-ink:   #16161b;
}

/* The OS preference, unless an explicit light choice overrides it. */
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    color-scheme: dark;

    --ground:  #0b0b0e;
    --paper:   #141419;
    --sunk:    #1c1c23;
    --ink:     #eceaf0;
    --on-ink:  #0b0b0e;
    --ink-hard:#ffffff;
    --muted:   #a3a2ae;
    --faint:   #82818d;
    --rule:    #26262f;
    --rule-2:  #363642;
    --brand:   #ff5a78;
    --brand-ink: #ff7f92;
    --wash:    #2a1119;
    --term:    #08080a;
    --term-ink:#ddd9e2;
    --term-dim:#8f8a97;
    --good:    #3fca7d;
    --good-bg: #12281d;
    --warn:    #e0a33c;
    --warn-bg: #2d2113;

    --raised:    #1c1c23;
    --raised:    #1c1c23;
  --bar-bg:    rgba(11, 11, 14, .86);
    --row-hover: #1a1a21;
    --backdrop:  rgba(0, 0, 0, .62);
    --pulse:     255, 90, 120;
    --shadow: 0 1px 2px rgba(0, 0, 0, .5), 0 8px 24px -16px rgba(0, 0, 0, .9);
    --shadow-rail: -12px 0 32px -24px rgba(0, 0, 0, .9);
    --shadow-fab:  0 -12px 32px -24px rgba(0, 0, 0, .9);
    --shadow-modal: 0 30px 70px -30px rgba(0, 0, 0, .95);
    --shadow-tip:  0 8px 26px -12px rgba(0, 0, 0, .8);

    /* On a dark ground weight runs the other way: dim to bright as the value
       rises. Same hue, re-stepped for this surface and re-validated. */
    --viz-surface: #141419;
    --viz-1: #ab0035;  --viz-ink-1: #ffffff;
    --viz-2: #d60045;  --viz-ink-2: #ffffff;
    --viz-3: #f8355d;  --viz-ink-3: #0b0b0b;
    --viz-4: #ff7684;  --viz-ink-4: #0b0b0b;
    --viz-series: #f8355d;
    --viz-grid:  #26262f;
    --viz-axis:  #3d3d49;
    --viz-muted: #82818d;
    --viz-ink:   #eceaf0;
  }
}

/* An explicit choice wins over the OS in both directions. */
:root[data-theme="dark"] {
  color-scheme: dark;

  --ground:  #0b0b0e;
  --paper:   #141419;
  --sunk:    #1c1c23;
  --ink:     #eceaf0;
  --on-ink:  #0b0b0e;
  --ink-hard:#ffffff;
  --muted:   #a3a2ae;
  --faint:   #82818d;
  --rule:    #26262f;
  --rule-2:  #363642;
  --brand:   #ff5a78;
  --brand-ink: #ff7f92;
  --wash:    #2a1119;
  --term:    #08080a;
  --term-ink:#ddd9e2;
  --term-dim:#8f8a97;
  --good:    #3fca7d;
  --good-bg: #12281d;
  --warn:    #e0a33c;
  --warn-bg: #2d2113;

  --raised:    #1c1c23;
  --bar-bg:    rgba(11, 11, 14, .86);
  --row-hover: #1a1a21;
  --backdrop:  rgba(0, 0, 0, .62);
  --pulse:     255, 90, 120;
  --shadow: 0 1px 2px rgba(0, 0, 0, .5), 0 8px 24px -16px rgba(0, 0, 0, .9);
  --shadow-rail: -12px 0 32px -24px rgba(0, 0, 0, .9);
  --shadow-fab:  0 -12px 32px -24px rgba(0, 0, 0, .9);
  --shadow-modal: 0 30px 70px -30px rgba(0, 0, 0, .95);
  --shadow-tip:  0 8px 26px -12px rgba(0, 0, 0, .8);

  --viz-surface: #141419;
  --viz-1: #ab0035;  --viz-ink-1: #ffffff;
  --viz-2: #d60045;  --viz-ink-2: #ffffff;
  --viz-3: #f8355d;  --viz-ink-3: #0b0b0b;
  --viz-4: #ff7684;  --viz-ink-4: #0b0b0b;
  --viz-series: #f8355d;
  --viz-grid:  #26262f;
  --viz-axis:  #3d3d49;
  --viz-muted: #82818d;
  --viz-ink:   #eceaf0;
}

:root {
  --ui: "Segoe UI", -apple-system, system-ui, sans-serif;
  --mono: "Cascadia Mono", "JetBrains Mono", Consolas, ui-monospace, monospace;
  --bar-h: 56px;
  --rail: 400px;   /* the docked run log */
  --radius: 8px;
}

* { box-sizing: border-box; }

/* The browser's own `[hidden] { display: none }` loses to any class that sets
   a display, so every flex element here would ignore the attribute. The JS
   toggles `hidden`, so this rule is what makes that actually work. */
[hidden] { display: none !important; }

body {
  margin: 0;
  background: var(--ground);
  color: var(--ink);
  font: 15px/1.62 var(--ui);
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}

a { color: var(--ink); text-decoration: none; }
a:hover { text-decoration: underline; text-underline-offset: 2px; }
:focus-visible { outline: 2px solid var(--brand); outline-offset: 2px; border-radius: 4px; }
.grow { flex: 1; }
.muted { color: var(--muted); }
.small { font-size: 13px; }

/* ---------- top bar ---------- */
.bar {
  position: sticky; top: 0; z-index: 20;
  display: flex; align-items: center; gap: 20px;
  height: var(--bar-h); padding: 0 20px;
  background: var(--bar-bg);
  backdrop-filter: saturate(1.6) blur(10px);
  border-bottom: 1px solid var(--rule);
}

/* Centred on the bar itself rather than between the two groups of controls:
   the left is a wordmark, the right is a theme button and two buttons, and
   they are nowhere near the same width -- a flex spacer would put this near
   the middle rather than in it. Absolute against .bar, which is already
   `position: sticky` and so already a containing block.

   Hidden below 720px, where the bar has no room to spare. */
.bar-mark {
  position: absolute; left: 50%; top: 50%; transform: translate(-50%, -50%);
  display: block; line-height: 0; border-radius: 8px;
}
.bar-mark img { display: block; width: 30px; height: 30px; border-radius: 7px; }
.bar-mark:hover { opacity: .82; }
@media (max-width: 720px) { .bar-mark { display: none; } }

.wordmark {
  font-family: var(--mono); font-weight: 700; font-size: 13px;
  letter-spacing: .2em; color: var(--ink); text-transform: lowercase;
  white-space: nowrap; flex: 0 0 auto;
}
.wordmark:hover { text-decoration: none; color: var(--brand-ink); }
.wordmark img { display: block; width: 30px; height: 30px; border-radius: 7px; }
.wordmark:hover img { opacity: .82; }

/* Selected is an underline rather than a filled pill. A solid block in the bar
   competes with the model chip beside it and reads as a button you can press
   rather than as where you already are. */
.tabs { display: flex; gap: 20px; }
.tabs a {
  padding: 6px 1px; color: var(--muted);
  font-size: 14px; font-weight: 500;
  border-bottom: 2px solid transparent;
}
.tabs a:hover {
  background: none; color: var(--ink); text-decoration: none;
  border-bottom-color: var(--rule-2);
}
.tabs a.on { background: none; color: var(--ink); border-bottom-color: var(--ink); }

/* Navigation sits in the middle of the bar at every width. The tabs take the
   space between the mark and the account controls and centre inside it, which
   also pins the account to the right edge -- so the flex spacer that used to
   do that job has nothing left to do.

   #bar, not .bar: the public header has its own two buttons on the right and
   wants its spacer left alone. */
/* Centred on the bar itself, not in the space left over. The mark on the left
   is 30px; the model chip, theme button and account on the right come to
   several times that -- so `flex: 1` on the tabs put them near the middle and
   never in it (608px against a centre of 713). Absolute against .bar, which is
   already `position: sticky` and therefore already a containing block.

   The flex spacer stays: with the tabs out of flow, it is the only thing left
   pushing the account to the right edge. */
#bar .tabs { position: absolute; left: 50%; transform: translateX(-50%); }

.model {
  display: flex; align-items: center; gap: 8px;
  font: 12.5px var(--ui); color: var(--muted);
  padding: 5px 11px; border: 1px solid var(--rule); border-radius: 999px;
  background: var(--paper);
}
.dot { width: 7px; height: 7px; border-radius: 50%; background: var(--faint); flex: none; }
/* The select is styled down to look like the label it replaced: the bar is a
   status strip, and a control that announces itself as a form field there
   pulls the eye away from the thing it is reporting on. */
#model-pick {
  appearance: none; -webkit-appearance: none;
  font: 12.5px var(--ui); color: var(--muted);
  background: none; border: 0; padding: 0 15px 0 0; margin: 0;
  cursor: pointer; max-width: 190px; text-overflow: ellipsis;
  background-image: linear-gradient(45deg, transparent 50%, currentColor 50%),
                    linear-gradient(135deg, currentColor 50%, transparent 50%);
  background-position: calc(100% - 6px) center, calc(100% - 2px) center;
  background-size: 4px 4px, 4px 4px;
  background-repeat: no-repeat;
}
#model-pick:hover { color: var(--ink); }
#model-pick:disabled { cursor: default; opacity: .7; }
/* The dropdown itself is painted by the OS, which does not inherit the bar's
   transparent background -- without this the list is unreadable in dark mode
   on Windows. */
#model-pick option { background: var(--paper); color: var(--ink); }
.dot.up { background: var(--good); }
.dot.down { background: var(--brand); }

/* ---------- buttons ---------- */
.btn {
  font: 500 14px/1 var(--ui); padding: 10px 16px; border-radius: 6px;
  border: 1px solid transparent; cursor: pointer; background: none;
  color: var(--ink); transition: background .12s ease, border-color .12s ease;
}
.btn.primary { background: var(--ink); color: var(--on-ink); }
.btn.primary:hover { background: var(--ink-hard); }
.btn.ghost { border-color: var(--rule-2); background: var(--paper); }
.btn.ghost:hover { background: var(--sunk); }
.btn.small { padding: 6px 11px; font-size: 12.5px; }
.btn:disabled { opacity: .45; cursor: not-allowed; }

/* ---------- the live run: a rail docked to the right ----------
   Full height rather than a strip across the top, so a long log is actually
   readable and the document underneath keeps its own scroll position. The
   page gets right padding while it is open, so the rail never covers content
   -- overlaying the report you are reading is the thing that makes a docked
   panel annoying. */
.live {
  position: fixed; top: 0; right: 0; bottom: 0; width: var(--rail);
  z-index: 30; display: flex; flex-direction: column;
  background: var(--paper); border-left: 1px solid var(--rule);
  box-shadow: var(--shadow-rail);
}
body.has-live { padding-right: var(--rail); }

.live-head {
  flex: none; display: flex; align-items: center; gap: 10px; flex-wrap: wrap;
  padding: 0 16px; min-height: var(--bar-h);
  border-bottom: 1px solid var(--rule);
}
.live-title { display: flex; align-items: center; gap: 9px; min-width: 0; }
.live-head strong {
  font-weight: 600; font-size: 14px; white-space: nowrap;
  overflow: hidden; text-overflow: ellipsis;
}
.live-head #live-meta {
  flex-basis: 100%; margin: -6px 0 10px; font-variant-numeric: tabular-nums;
}
.live-head .btn { margin-left: auto; }

.pulse {
  width: 8px; height: 8px; border-radius: 50%; background: var(--brand);
  flex: none; box-shadow: 0 0 0 0 rgba(var(--pulse), .5);
  animation: pulse 1.8s ease-out infinite;
}
@keyframes pulse {
  70% { box-shadow: 0 0 0 7px rgba(var(--pulse), 0); }
  100% { box-shadow: 0 0 0 0 rgba(var(--pulse), 0); }
}
@media (prefers-reduced-motion: reduce) { .pulse { animation: none; } }

.log {
  flex: 1; margin: 0; padding: 14px 16px 18px; overflow: auto;
  background: var(--term); color: var(--term-ink);
  font: 11.5px/1.7 var(--mono); white-space: pre-wrap; word-break: break-word;
}

/* the way back once the rail is hidden */
.live-fab {
  position: fixed; right: 20px; bottom: 20px; z-index: 30;
  display: flex; align-items: center; gap: 9px;
  padding: 11px 16px; border-radius: 999px; cursor: pointer;
  border: 1px solid var(--rule-2); background: var(--paper); color: var(--ink);
  font: 500 13.5px/1 var(--ui); box-shadow: var(--shadow);
}
.live-fab:hover { background: var(--sunk); }

/* narrow screens have no room for a rail: dock it to the bottom instead */
@media (max-width: 1040px) {
  .live {
    top: auto; left: 0; width: auto; height: min(46vh, 380px);
    border-left: 0; border-top: 1px solid var(--rule);
    box-shadow: var(--shadow-fab);
  }
  body.has-live { padding-right: 0; padding-bottom: min(46vh, 380px); }
  /* Docked at the bottom, this row is the panel's top edge rather than a bar
     inside a column, so it wants a little air above it. box-sizing is
     border-box here, so the min-height rises with the padding instead of the
     padding eating into it. */
  /* Docked at the bottom, this row is the panel's top edge rather than a bar
     inside a column, so it gets a little air above it. min-height rises with
     the padding rather than absorbing it, because box-sizing is border-box.
     align-content keeps the wrapped line from riding up against the padding. */
  .live-head {
    min-height: 58px; padding-top: 10px; align-content: center;
  }
}

/* ---------- layout ---------- */
main { padding: 30px 20px 80px; max-width: 1320px; margin: 0 auto; }
/* The public page sets its own width -- wider than the product view, because
   a marketing page is read across and a work surface is read down. */
body.public main { max-width: none; padding-left: 0; padding-right: 0; }
.loading { color: var(--faint); padding: 48px 0; }

.page-head {
  display: flex; align-items: flex-start; gap: 18px; margin-bottom: 24px;
  flex-wrap: wrap;
}
.page-head h1 {
  margin: 0 0 5px; font-size: 24px; font-weight: 600; letter-spacing: -.02em;
  line-height: 1.2;
}
.page-head .sub { color: var(--muted); font-size: 13.5px; }
.head-actions { display: flex; gap: 9px; flex-wrap: wrap; }

/* The export menu: same anchored-popup shape as .who-menu in the top bar,
   just opening from a button inside the page instead of the account corner. */
.export { position: relative; }
.export-menu {
  position: absolute; top: calc(100% + 6px); right: 0; z-index: 40;
  min-width: 190px; padding: 5px;
  background: var(--paper); border: 1px solid var(--rule);
  border-radius: 10px; box-shadow: var(--shadow-modal);
  display: flex; flex-direction: column;
}
.export-menu button {
  all: unset; box-sizing: border-box; cursor: pointer; width: 100%;
  padding: 9px 11px; border-radius: 7px; color: var(--ink); font-size: 13.5px;
}
.export-menu button:hover { background: var(--sunk); }
.export-menu .kb { color: var(--faint); font-size: 11.5px; margin-left: 6px; }

/* ---------- market cards ---------- */
.cards { display: grid; gap: 16px; grid-template-columns: repeat(auto-fill, minmax(320px, 1fr)); }
.card {
  background: var(--paper); border: 1px solid var(--rule); border-radius: var(--radius);
  padding: 18px; display: flex; flex-direction: column; gap: 11px;
  color: inherit; box-shadow: var(--shadow);
  transition: transform .12s ease, border-color .12s ease;
}
a.card:hover {
  border-color: var(--rule-2); text-decoration: none; transform: translateY(-2px);
}

/* An outline where a card would be, first in the grid: the way to start a new
   report is the same shape and the same place as the reports it will join,
   rather than a button in the chrome that is on every screen equally. */
.card-new {
  /* --paper, the same fill as the report cards beside it: white on the light
     theme, the dark card on the dark one. The dashed border is what marks it
     out as the empty slot, not the absence of a background. */
  background: var(--paper); box-shadow: none; cursor: pointer; text-align: left;
  border: 1.5px dashed var(--rule-2);
  align-items: flex-start; justify-content: center; gap: 9px;
  min-height: 168px; font: inherit; color: var(--muted);
}
/* Hover lifts the whole card: the card is the button, so the affordance
   belongs to the card rather than to a control inside it. */
.card-new:hover {
  border-color: var(--brand); color: var(--ink); transform: translateY(-2px);
  box-shadow: var(--shadow);
}
.card-new:hover .card-new-steps { color: var(--brand-ink); }
.card-new:focus-visible { outline: 2px solid var(--brand); outline-offset: 2px; }
.card-new strong { font-size: 15px; color: var(--ink); }
.card-new .small { line-height: 1.5; max-width: 32ch; }

/* What the run will do, in three words. The card used to say only what it was;
   this says what happens after the click, which is the question somebody
   hesitating over a paid report is actually asking. */
.card-new-steps {
  display: flex; flex-wrap: wrap; align-items: center; gap: 6px;
  margin-top: auto; padding-top: 4px;
  font: 500 10.5px/1 var(--ui); letter-spacing: .07em;
  text-transform: uppercase; color: var(--faint);
  transition: color .14s ease;
}
.card-new-steps span + span::before {
  content: "→"; margin-right: 6px; opacity: .6;
}
.card h3 { margin: 0; font-size: 16px; font-weight: 600; line-height: 1.32; letter-spacing: -.01em; }
.card .meta { display: flex; flex-wrap: wrap; gap: 5px; }
/* The best opening this market turned up. The score leads because it is what
   sorts two markets against each other; the segment follows because it is what
   makes the score mean anything. */
.card .best {
  margin: 0; display: flex; align-items: baseline; gap: 8px;
  font-size: 12.5px; color: var(--muted); line-height: 1.35;
}
.card .best-score {
  flex: none; font: 600 13px/1 var(--mono); color: var(--ink);
  background: var(--sunk); padding: 5px 7px; border-radius: 4px;
  font-variant-numeric: tabular-nums;
}
.chip {
  font: 500 11px/1 var(--mono); padding: 5px 8px; border-radius: 4px;
  background: var(--sunk); color: var(--muted); letter-spacing: .02em;
}
.chip.on { background: var(--wash); color: var(--brand-ink); }
.chip.good { background: var(--good-bg); color: var(--good); }
.card .foot {
  display: flex; gap: 14px; font-size: 12.5px; color: var(--faint);
  padding-top: 11px; border-top: 1px solid var(--rule);
}

.empty {
  background: var(--paper); border: 1px dashed var(--rule-2);
  border-radius: var(--radius); padding: 44px 28px; text-align: center;
  color: var(--muted);
}
.empty strong { color: var(--ink); }

/* ---------- market detail ---------- */
.split { display: grid; grid-template-columns: 254px minmax(0, 1fr); gap: 24px; align-items: start; }

.toc {
  position: sticky; top: calc(var(--bar-h) + 20px);
  background: var(--paper); border: 1px solid var(--rule);
  border-radius: var(--radius); padding: 8px;
  max-height: calc(100vh - var(--bar-h) - 46px); overflow: auto;
  box-shadow: var(--shadow);
}
.toc .part {
  font: 600 10px/1 var(--ui); letter-spacing: .14em; text-transform: uppercase;
  color: var(--faint); padding: 14px 9px 7px;
}
.toc .part:first-child { padding-top: 7px; }
.toc button {
  display: flex; width: 100%; gap: 8px; align-items: baseline;
  padding: 7px 9px; border: 0; background: none; border-radius: 5px;
  font: 13.5px/1.4 var(--ui); color: var(--ink); cursor: pointer; text-align: left;
}
.toc button:hover { background: var(--sunk); }
.toc button.on { background: var(--wash); color: var(--brand-ink); font-weight: 600; }
.toc button .kb { color: var(--faint); font: 10.5px var(--mono); margin-left: auto; }

/* Wide enough for two columns: the contents are simply there, and the pill and
   its scrim do not exist. */
.toc-toggle, .toc-scrim { display: none; }

@media (max-width: 900px) {
  .split { grid-template-columns: 1fr; }

  /* The pill sits under the bar and stays there while the report scrolls, so
     the contents are one tap away from anywhere in the document. */
  .toc-toggle {
    display: inline-flex; align-items: center; gap: 8px;
    position: sticky; top: calc(var(--bar-h) + 10px); z-index: 25;
    margin: 0 0 14px; padding: 9px 15px 9px 13px;
    border: 1px solid var(--rule-2); border-radius: 999px;
    background: var(--paper); color: var(--ink);
    font: 600 13px var(--ui); cursor: pointer;
    box-shadow: var(--shadow);
  }
  .toc-toggle:hover { border-color: var(--ink); }

  /* Fixed rather than in flow. The button that opens it is sticky, so an
     in-flow panel would unfold somewhere above the viewport as soon as you
     had scrolled into the report. */
  .toc {
    position: fixed; z-index: 40; display: none;
    left: 12px; right: 12px; top: calc(var(--bar-h) + 58px);
    max-height: calc(100dvh - var(--bar-h) - 78px);
    box-shadow: var(--shadow-modal);
  }
  .toc-scrim { position: fixed; inset: 0; z-index: 35; background: var(--backdrop); }

  body.toc-open .toc,
  body.toc-open .toc-scrim { display: block; }
  /* Nothing behind the panel should move while it is open. */
  body.toc-open { overflow: hidden; }
}

.doc {
  background: var(--paper); border: 1px solid var(--rule);
  border-radius: var(--radius); padding: 34px 38px; min-height: 60vh;
  box-shadow: var(--shadow);
}
@media (max-width: 640px) { .doc { padding: 22px 18px; } }

/* ---------- rendered markdown ---------- */
/* A generated report occasionally drops in a long unbroken token -- a bare
   URL, a hash, a run-together identifier -- with no space or hyphen for the
   browser's default line-breaking to catch. Without this, one such token
   forces the whole page wider than the nav bar, and only scrolling right or
   zooming out reveals why. overflow-wrap here, not just on .md a, since the
   token is prose text far more often than it is a link. */
.md { font-size: 15px; line-height: 1.7; overflow-wrap: anywhere; }
.md > *:first-child { margin-top: 0; }
/* Prose gets a reading measure; tables and code stay full width, because a
   competitor table squeezed into 68 characters is unreadable. */
.md > p, .md > ul, .md > ol, .md > blockquote { max-width: 68ch; }
.md h1 { font-size: 25px; font-weight: 600; letter-spacing: -.022em; margin: 0 0 16px; }
.md h2 {
  font-size: 17.5px; font-weight: 600; margin: 34px 0 12px;
  padding-bottom: 7px; border-bottom: 1px solid var(--rule); letter-spacing: -.01em;
}
.md h3 { font-size: 15px; font-weight: 600; margin: 24px 0 8px; }
.md p, .md li { margin: 10px 0; }
.md ul, .md ol { padding-left: 22px; }
.md strong { font-weight: 600; }
.md code {
  font: 12.5px var(--mono); background: var(--sunk);
  padding: 2px 5px; border-radius: 4px; color: var(--ink);
}
.md pre {
  background: var(--sunk); border: 1px solid var(--rule); border-radius: 6px;
  padding: 14px 16px; overflow-x: auto; font: 12.5px/1.65 var(--mono);
}
.md pre code { background: none; padding: 0; }
.md blockquote {
  margin: 14px 0; padding: 3px 0 3px 16px;
  border-left: 2px solid var(--rule-2); color: var(--muted);
}
.md hr { border: 0; border-top: 1px solid var(--rule); margin: 28px 0; }
.md a { color: var(--brand-ink); word-break: break-word; }

/* tables carry most of the evidence, so they get the most care */
.table-scroll {
  overflow-x: auto; margin: 14px 0;
  border: 1px solid var(--rule); border-radius: 6px;
}

/* the continuous-reading chain: each new section lands below the last with a
   quiet divider naming it, so the reader knows a boundary was crossed without
   needing the contents drawer */
.doc-next {
  border-top: 1px solid var(--rule); margin: 42px 0 4px; padding-top: 26px;
  text-align: center; font-size: 11px; letter-spacing: .16em;
  text-transform: uppercase; color: var(--muted);
}
.doc-end {
  text-align: center; color: var(--muted); font-size: 11.5px;
  letter-spacing: .16em; text-transform: uppercase; margin: 44px 0 10px;
}
.md table { border-collapse: collapse; width: 100%; font-size: 12.5px; }
.md th, .md td { padding: 8px 11px; text-align: left; vertical-align: top; }
.md th {
  background: var(--sunk); font-weight: 600; color: var(--ink);
  position: sticky; top: 0; white-space: nowrap;
  border-bottom: 1px solid var(--rule-2);
}
.md td { border-top: 1px solid var(--rule); }
.md tbody tr:hover td { background: var(--row-hover); }
.md td a { overflow-wrap: anywhere; }

/* ---------- runs ---------- */
.panel {
  background: var(--paper); border: 1px solid var(--rule);
  border-radius: var(--radius); box-shadow: var(--shadow); overflow: hidden;
}
table.grid { border-collapse: collapse; width: 100%; font-size: 13.5px; }
table.grid th {
  text-align: left; padding: 11px 14px; background: var(--sunk);
  font: 600 10px/1 var(--ui); letter-spacing: .13em; text-transform: uppercase;
  color: var(--faint); border-bottom: 1px solid var(--rule-2);
}
table.grid td { padding: 10px 14px; border-bottom: 1px solid var(--rule); }
table.grid tr:last-child td { border-bottom: 0; }
table.grid tbody tr:hover td { background: var(--row-hover); }
table.grid td.num { font-family: var(--mono); font-variant-numeric: tabular-nums; }

.status {
  font: 500 11px/1 var(--mono); padding: 4px 7px; border-radius: 4px;
  background: var(--sunk); color: var(--muted);
}
.status.done { background: var(--good-bg); color: var(--good); }
.status.error, .status.max_steps { background: var(--wash); color: var(--brand-ink); }
.status.timeout { background: var(--warn-bg); color: var(--warn); }
/* A market is running, queued or stopped where a run is done or errored, so
   these three carry the same weight as the run statuses above them. */
.status.running, .status.queued { background: var(--wash); color: var(--brand-ink); }
.status.stopped { background: var(--warn-bg); color: var(--warn); }
/* In a .sub line the badge sits inline with the text rather than on its own. */
.sub .status { margin-right: 6px; vertical-align: 1px; }

.step {
  border: 1px solid var(--rule); border-radius: 6px; background: var(--paper);
  margin-bottom: 10px;
}
.step > summary {
  padding: 11px 14px; cursor: pointer; font-size: 13.5px;
  display: flex; gap: 11px; align-items: center; list-style: none;
}
.step > summary::-webkit-details-marker { display: none; }
.step > summary:hover { background: var(--sunk); border-radius: 5px; }
.step[open] > summary { border-bottom: 1px solid var(--rule); border-radius: 5px 5px 0 0; }
.step .body { padding: 4px 14px 14px; }
.step pre, .inset {
  background: var(--sunk); border-radius: 5px; padding: 11px 13px;
  font: 12px/1.65 var(--mono); overflow-x: auto; white-space: pre-wrap;
  word-break: break-word; margin: 9px 0 0;
}
.call { color: var(--brand-ink); font: 600 12.5px var(--mono); margin-top: 10px; }

/* ---------- dialogs ---------- */
dialog {
  border: 1px solid var(--rule-2); border-radius: 10px; padding: 0;
  background: var(--paper); color: var(--ink);
  width: min(580px, 94vw); max-height: 92vh;
  /* max-height makes every dialog a scroll container, and the UA default of
     overflow:auto then paints a horizontal track it never needs. Vertical
     scrolling is real -- a tall dialog has to scroll -- horizontal is not. */
  overflow-x: hidden; overflow-y: auto;
  box-shadow: var(--shadow-modal);
}
dialog::backdrop { background: var(--backdrop); backdrop-filter: blur(2px); }
dialog form { padding: 26px 28px 22px; display: flex; flex-direction: column; gap: 14px; }
dialog h2 { margin: 0; font-size: 19px; font-weight: 600; letter-spacing: -.015em; }
dialog label { display: flex; flex-direction: column; gap: 6px; font-size: 12.5px; font-weight: 600; }
dialog input, dialog select {
  font: 14px var(--ui); padding: 9px 11px; border-radius: 6px;
  border: 1px solid var(--rule-2); background: var(--raised); color: var(--ink);
}
dialog input:focus, dialog select:focus { border-color: var(--ink); }
dialog input::placeholder { color: var(--faint); }
dialog small { color: var(--faint); font-weight: 400; font-size: 12px; }
/* The label is a flex column, so this pair needs its own row box or the
   badge drops under the words it is meant to be qualifying. */
.lbl { display: flex; align-items: baseline; gap: 5px; }
.req {
  color: var(--brand-ink); font: 600 10px/1 var(--ui); letter-spacing: .1em;
  text-transform: uppercase;
}
.row { display: grid; grid-template-columns: 1fr 1fr; gap: 14px; }
.switches {
  border: 1px solid var(--rule); border-radius: 6px; padding: 12px 14px 14px;
  margin: 0; background: color-mix(in srgb, var(--sunk) 60%, var(--paper));
}
.switches legend {
  font: 600 10px/1 var(--ui); letter-spacing: .13em; text-transform: uppercase;
  color: var(--faint); padding: 0 6px;
}
.check {
  flex-direction: row !important; align-items: center; gap: 9px !important;
  font-weight: 400 !important; font-size: 13.5px !important; cursor: pointer;
}
.check input { width: auto; accent-color: var(--ink); }
.actions { display: flex; justify-content: flex-end; gap: 10px; margin-top: 4px; }
.error {
  margin: 0; padding: 10px 13px; border-radius: 6px; font-size: 13px;
  background: var(--wash); color: var(--brand-ink);
}

/* ==========================================================================
   Accounts, the wizard, and what things cost.

   Same rule as the rest of the file: black is the interface, the brand red is
   only for what is live or what needs the eye. A price is not an emergency, so
   money is set in the ink colour and carried by weight, not by colour. The one
   exception is the cost line at the end of the wizard, which is the last thing
   between somebody and a charge.
   ========================================================================== */

/* ---------- signed out ---------- */

.signin {
  display: grid; grid-template-columns: minmax(0, 1fr);
  gap: 34px; align-items: start;
  max-width: 560px; margin: 0 auto; padding: 7vh 24px 40px;
}
/* The public header is sticky, so the card has to start below it. 7vh alone
   put the wordmark and the back link underneath the bar on a short window --
   and the back link sits above the card, so it went first. */
body.public .signin { padding-top: calc(var(--bar-h) + 52px); }
.signin-card { background: var(--paper); border: 1px solid var(--rule);
  border-radius: var(--radius); box-shadow: var(--shadow); padding: 34px 32px;
  text-align: center; }
/* Everything on the card centres except the form's own labels. A label centred
   over a full-width input reads as a caption for the card rather than a name
   for the field under it, and the eye loses the left edge it needs to run down
   a stack of three. */
.signin-card .pw-form label { text-align: left; }
.signin-card .btn.google { width: 100%; justify-content: center; }
.signin-card .pw-actions { justify-content: center; }
.signin h1 { margin: 0 0 12px; font-size: 27px; line-height: 1.24;
  letter-spacing: -.02em; font-weight: 600; }
.signin p { margin: 0 0 18px; }

/* Google's own mark sits on white by their brand rules, so this button is the
   one place the page uses a bordered white surface as a primary action. */
.btn.google {
  display: inline-flex; align-items: center; gap: 10px;
  background: var(--paper); border: 1px solid var(--rule-2); color: var(--ink);
  font-weight: 600; padding: 11px 18px;
}
/* Google's mark is drawn for a light plate, so on dark the button keeps one
   rather than inverting into four muddy hues on near-black. */
:root[data-theme="dark"] .btn.google,
:root:not([data-theme="light"]) .btn.google {
  background: #ffffff; color: #16161b; border-color: #ffffff;
}
:root[data-theme="dark"] .btn.google:hover,
:root:not([data-theme="light"]) .btn.google:hover { background: #eceaf0; }
.btn.google:hover { background: var(--sunk); text-decoration: none; }

.notice {
  margin: 0 0 16px; padding: 12px 14px; border-radius: 6px;
  background: var(--warn-bg); color: var(--warn); font-size: 13.5px;
}
.notice.good { background: var(--good-bg); color: var(--good); }
.notice p { margin: 6px 0 0; }
.notice code { font: 12px var(--mono); }

/* ==========================================================================
   The dashboard, watched markets, updates and the chat.

   Same palette rule as everywhere: the kind of an update is told apart by a
   quiet fill, not by colour alone -- each carries its label in words -- and
   the brand red marks only what is new.
   ========================================================================== */

.feed { margin-bottom: 26px; }
.feed-head {
  display: flex; align-items: baseline; gap: 6px 12px; flex-wrap: wrap;
  padding: 15px 18px 12px; border-bottom: 1px solid var(--rule);
}
.feed-head h2 { margin: 0; font-size: 16px; font-weight: 600; }
.feed-head .grow { flex: 1; min-width: 200px; }
.feed-list { list-style: none; margin: 0; padding: 0; }
.feed-list li + li { border-top: 1px solid var(--rule); }
.feed-list a {
  display: flex; gap: 12px; align-items: flex-start; padding: 13px 18px;
  color: inherit;
}
.feed-list a:hover { background: var(--row-hover); text-decoration: none; }
.feed-text { display: flex; flex-direction: column; gap: 3px; min-width: 0; }
.feed-text strong { font-weight: 600; font-size: 14px; line-height: 1.35; }
.feed-text .small { color: var(--muted); line-height: 1.45; }
.feed-text .feed-where { color: var(--faint); }
.feed-list .unread strong::after,
.update-items .unread > strong::after {
  content: ""; display: inline-block; width: 7px; height: 7px;
  border-radius: 50%; background: var(--brand); margin-left: 7px;
  vertical-align: 2px;
}
.feed-empty {
  padding: 16px 18px; display: flex; gap: 14px; align-items: center;
  flex-wrap: wrap;
}
.feed-empty p { margin: 0; color: var(--muted); font-size: 13.5px; flex: 1; min-width: 220px; }

.kind {
  flex: none; margin-top: 1px; white-space: nowrap;
  font: 600 10px/1 var(--ui); letter-spacing: .08em; text-transform: uppercase;
  padding: 5px 7px; border-radius: 4px; background: var(--sunk); color: var(--ink);
}
.kind-recommendation { background: var(--good-bg); color: var(--good); }
.kind-idea { background: var(--wash); color: var(--brand-ink); }
.kind-gap { background: var(--warn-bg); color: var(--warn); }

/* A card whose title link covers it, with two links raised above that. */
.market-card { position: relative; }
.market-card:hover { border-color: var(--rule-2); transform: translateY(-2px); }
.market-card .card-link { color: inherit; }
.market-card .card-link:hover { text-decoration: none; }
.market-card .card-link::after {
  content: ""; position: absolute; inset: 0; border-radius: inherit;
}
.market-card .card-link:focus-visible { outline: none; }
.market-card:has(.card-link:focus-visible) {
  outline: 2px solid var(--brand); outline-offset: 2px;
}
.card-watch { margin: 0; font-size: 12.5px; color: var(--faint); }
.card-watch.on { color: var(--good); }
.card-update { margin: 0; font-size: 13px; line-height: 1.45; color: var(--ink); }
.card-update .kicker, .update-head .kicker {
  display: block; margin-bottom: 5px; font: 600 10px/1 var(--ui);
  letter-spacing: .1em; text-transform: uppercase; color: var(--faint);
}
.card .foot .grow { flex: 1; }
.card-action { position: relative; z-index: 1; color: var(--ink); font-weight: 600; }
.card-action:focus-visible { outline: 2px solid var(--brand); outline-offset: 2px; border-radius: 3px; }
.toc button .kb.new { color: var(--brand-ink); font-weight: 600; }
.watch-chip { align-self: center; }
.watch-chip:hover { text-decoration: none; }
.tier-buy .price .per { margin-left: 4px; font-size: 12px; font-weight: 400; color: var(--faint); }
.notify-panel {
  display: flex; align-items: center; gap: 12px; flex-wrap: wrap;
  padding: 14px 18px; margin: 16px 0;
}

/* ---------- plans ----------
   Four plans side by side, as a price list is read, in the landing page's own
   .plan card so the product and the page that sold it look alike. One switch
   flips the four prices between a single report and the yearly package. */
.plans-bar {
  display: flex; flex-wrap: wrap; align-items: center; gap: 10px 16px;
  margin: 2px 0 22px;
}
.plans-bar .small { color: var(--muted); }
.price-grid {
  display: grid; gap: 16px; align-items: stretch;
  grid-template-columns: repeat(4, minmax(0, 1fr));
}
@media (max-width: 1180px) { .price-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (min-width: 1181px) and (max-width: 1600px) {
  body.has-live .price-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
@media (max-width: 640px) { .price-grid { grid-template-columns: minmax(0, 1fr); } }
.price-grid .plan { padding: 26px 22px 22px; }
.price-grid .plan-price { margin-bottom: 6px; }
.plan-per { margin: 0 0 16px; font-size: 12.5px; color: var(--faint); }
.plan-held {
  margin: 0 0 12px; font-size: 12.5px; font-weight: 600; color: var(--good);
}
.plan-actions { margin-top: auto; display: flex; flex-direction: column; gap: 9px; }
.plan-actions .btn { width: 100%; text-align: center; }
.plan-actions .linkish { align-self: center; }
.plans-note { margin: 16px 0 34px; max-width: 80ch; }

.package-banner {
  display: flex; align-items: center; gap: 12px; margin: 0 0 18px;
  padding: 12px 16px; border-radius: var(--radius);
  background: var(--good-bg); color: var(--good); font-size: 13.5px;
}
.package-banner .grow { flex: 1; }
.package-banner .small { color: var(--ink); opacity: .75; }

.account-grid {
  display: grid; gap: 16px; align-items: start;
  grid-template-columns: repeat(2, minmax(0, 1fr));
}
@media (max-width: 860px) { .account-grid { grid-template-columns: minmax(0, 1fr); } }
.account-grid .panel > h2 {
  margin: 0; padding: 14px 20px; font-size: 15px; font-weight: 600;
  border-bottom: 1px solid var(--rule);
}
.credit-list { list-style: none; margin: 0; padding: 4px 20px 8px; }
.credit-list li {
  display: flex; justify-content: space-between; align-items: baseline;
  padding: 10px 0; font-size: 14px;
}
.credit-list li + li { border-top: 1px solid var(--rule); }
.credit-list strong { font-variant-numeric: tabular-nums; }
.empty-line { margin: 0; padding: 16px 20px; font-size: 13.5px; color: var(--muted); }

/* ---------- the dashboard ----------
   A header with the one primary action, four numbers, then two columns: the
   markets, and what came in. Cards carry one quiet meta line instead of a row
   of chips, and a badge only when something is not simply finished. */
.dash-head { align-items: center; margin-bottom: 20px; }
.dash-head h1 { margin: 0; }

/* "kpi", not "stat": the landing page's .stat rules come later in this file
   and would take the tiles' padding and every span's size. */
.kpis {
  display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 12px;
  margin: 0 0 26px;
}
.kpi {
  display: flex; flex-direction: column; gap: 6px; min-width: 0;
  padding: 14px 16px; color: inherit;
  background: var(--paper); border: 1px solid var(--rule);
  border-radius: var(--radius); box-shadow: var(--shadow);
}
.kpi-label {
  font: 600 10.5px/1 var(--ui); letter-spacing: .1em; text-transform: uppercase;
  color: var(--faint);
}
.kpi-value {
  font: 600 26px/1.1 var(--ui); letter-spacing: -.02em; color: var(--ink);
  font-variant-numeric: tabular-nums;
}
.kpi-sub {
  font-size: 12.5px; color: var(--muted);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.kpi.alert { border-color: color-mix(in srgb, var(--brand) 45%, var(--rule)); }
.kpi.alert .kpi-value { color: var(--brand-ink); }
a.kpi-link { transition: border-color .12s ease; }
a.kpi-link:hover { border-color: var(--rule-2); text-decoration: none; }

.dash { display: grid; grid-template-columns: minmax(0, 1fr); gap: 26px; align-items: start; }
@media (min-width: 1100px) {
  .dash { grid-template-columns: minmax(0, 1fr) 330px; }
  .dash-side { position: sticky; top: calc(var(--bar-h) + 18px); }
}
/* With the run log docked the page is 400px narrower than the window says,
   so the side column folds under the markets a breakpoint earlier. */
@media (min-width: 1100px) and (max-width: 1520px) {
  body.has-live .dash { grid-template-columns: minmax(0, 1fr); }
  body.has-live .dash-side { position: static; }
}
/* Stacked, what came in goes first -- but only when some of it is unread. */
@media (max-width: 1099px) {
  .dash.has-unread .dash-side { order: -1; }
}

.section-head {
  display: flex; flex-wrap: wrap; align-items: center; gap: 10px 16px;
  margin: 0 0 14px;
}
.section-head h2 { flex: 1; margin: 0; font-size: 16px; font-weight: 600; }
.section-gap { margin-top: 34px; }

.seg {
  display: inline-flex; gap: 2px; padding: 3px; border-radius: 8px;
  background: var(--sunk);
}
.seg button {
  all: unset; box-sizing: border-box; cursor: pointer;
  padding: 6px 11px; border-radius: 6px;
  font: 500 12.5px/1 var(--ui); color: var(--muted);
}
.seg button span {
  margin-left: 6px; color: var(--faint); font-variant-numeric: tabular-nums;
}
.seg button:hover { color: var(--ink); }
.seg button[aria-pressed="true"] {
  background: var(--paper); color: var(--ink); box-shadow: var(--shadow);
}
.seg button:focus-visible { outline: 2px solid var(--brand); outline-offset: 1px; }

.mcards { grid-template-columns: repeat(auto-fill, minmax(280px, 1fr)); gap: 14px; }

/* Search, beside the filter. The "/" hint steps aside once the box is used. */
.dash-search {
  position: relative; display: flex; align-items: center;
  flex: 0 1 250px; min-width: 180px;
}
.dash-search svg {
  position: absolute; left: 10px; width: 15px; height: 15px;
  color: var(--faint); pointer-events: none;
}
.dash-search input {
  width: 100%; padding: 7px 30px 7px 32px; border-radius: 8px;
  border: 1px solid var(--rule-2); background: var(--paper); color: var(--ink);
  font: 13.5px var(--ui);
}
.dash-search input::placeholder { color: var(--faint); }
.dash-search input:focus { outline: none; border-color: var(--ink); }
.dash-search kbd { position: absolute; right: 8px; pointer-events: none; }
.dash-search input:focus ~ kbd,
.dash-search input:not(:placeholder-shown) ~ kbd { display: none; }
.mcards-empty {
  grid-column: 1 / -1; padding: 28px; text-align: center;
  border: 1px dashed var(--rule-2); border-radius: var(--radius);
  font-size: 14px; color: var(--muted);
}
.mcards-empty p { margin: 0 0 8px; }

/* Live progress on a running card. */
.mcard-progress { display: flex; flex-direction: column; gap: 7px; margin-top: 4px; }
.progress-bar {
  height: 5px; overflow: hidden; border-radius: 999px; background: var(--sunk);
}
.progress-bar span {
  display: block; height: 100%; border-radius: inherit; background: var(--ink);
  transition: width .6s ease;
}
.progress-line {
  display: flex; justify-content: space-between; align-items: baseline; gap: 10px;
  margin: 0; font-size: 12.5px;
}
.progress-line .stage {
  min-width: 0; color: var(--muted); font-weight: 500;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.progress-line .eta {
  flex: none; color: var(--faint); font-variant-numeric: tabular-nums;
}
@media (prefers-reduced-motion: reduce) { .progress-bar span { transition: none; } }
@media (max-width: 760px) { .dash-search { flex: 1 1 100%; } }
.mcard { position: relative; gap: 8px; padding: 16px 18px 12px; }
.mcard:hover { border-color: var(--rule-2); transform: translateY(-2px); }
.mcard.has-new { border-color: color-mix(in srgb, var(--brand) 40%, var(--rule)); }
.mcard-top { display: flex; align-items: flex-start; gap: 10px; }

/* The notification dot: this market has updates nobody has opened yet. It
   sits on the card's corner, ringed in the page colour so it reads as a dot
   on top of the card rather than a stain on its border, and pulses softly --
   never for anyone who has asked for less motion. It goes away when the
   market's Updates page is opened, which is what marks them read. */
.notif-dot {
  position: absolute; top: -5px; right: -5px; z-index: 2;
  width: 13px; height: 13px; border-radius: 50%;
  background: var(--brand); box-shadow: 0 0 0 3px var(--ground);
  pointer-events: none;
}
.notif-dot::after {
  content: ""; position: absolute; inset: 0; border-radius: inherit;
  background: var(--brand); animation: notif-ping 2.2s ease-out infinite;
}
@keyframes notif-ping {
  0% { transform: scale(1); opacity: .55; }
  75%, 100% { transform: scale(2.3); opacity: 0; }
}
@media (prefers-reduced-motion: reduce) {
  .notif-dot::after { animation: none; opacity: 0; }
}
.link-dot {
  display: inline-block; width: 6px; height: 6px; margin-left: 5px;
  border-radius: 50%; background: var(--brand); vertical-align: 2px;
}
.mcard .card-link { flex: 1; min-width: 0; color: inherit; }
.mcard .card-link:hover { text-decoration: none; }
.mcard .card-link::after {
  content: ""; position: absolute; inset: 0; border-radius: inherit;
}
.mcard .card-link:focus-visible { outline: none; }
.mcard:has(.card-link:focus-visible) { outline: 2px solid var(--brand); outline-offset: 2px; }
/* The title is the one thing on a card in the ink colour; everything under
   it is grey, so the eye lands on which market before what it says. */
.mcard h3 {
  font-size: 15.5px; color: var(--ink);
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical;
  overflow: hidden;
}
.mcard-status {
  flex: none; display: inline-flex; align-items: center; gap: 6px;
  margin-top: 1px; padding: 4px 8px; border-radius: 999px;
  font: 500 11px/1 var(--ui); background: var(--sunk); color: var(--muted);
}
.mcard-status.busy { background: var(--wash); color: var(--brand-ink); }
.mcard-meta { margin: 0; font-size: 12.5px; color: var(--faint); }
.mcard-update {
  margin: 2px 0 0; font-size: 13.5px; line-height: 1.45; color: var(--muted);
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical;
  overflow: hidden;
}
.new-badge, .mcard-score {
  display: inline-block; margin-right: 7px; padding: 2px 6px; border-radius: 4px;
  vertical-align: 1px;
}
.new-badge { background: var(--brand); color: var(--on-ink); font: 600 10.5px/1.4 var(--ui); }
.mcard-score {
  background: var(--sunk); color: var(--muted); font: 600 11.5px/1.4 var(--mono);
  font-variant-numeric: tabular-nums;
}
.mcard-foot {
  display: flex; align-items: center; gap: 10px;
  margin-top: auto; padding-top: 10px; border-top: 1px solid var(--rule);
  font-size: 12.5px;
}
.mcard-watch {
  flex: 1; min-width: 0; color: var(--muted);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.dot {
  display: inline-block; width: 7px; height: 7px; margin-right: 7px;
  border-radius: 50%; background: var(--rule-2); vertical-align: 1px;
}
.dot.on { background: var(--good); }
.mcard-actions { position: relative; z-index: 1; display: flex; gap: 2px; margin-right: -6px; }
.mcard-actions a {
  padding: 5px 8px; border-radius: 6px; color: var(--muted); font-weight: 500;
}
.mcard-actions a:hover { background: var(--sunk); color: var(--ink); text-decoration: none; }
.mcard-actions a:focus-visible { outline: 2px solid var(--brand); outline-offset: 1px; }
/* Ask is an icon: a chat bubble is understood on sight, and it leaves the one
   word on the card's foot to Updates, the action that has news behind it. */
.mcard-actions { align-items: center; }
.mcard-actions .icon-link {
  display: grid; place-items: center; width: 30px; height: 28px; padding: 0;
  color: var(--muted);
}
.mcard-actions .icon-link:hover { color: var(--ink); }
.mcard-actions .icon-link svg { display: block; }

.side-head {
  display: flex; align-items: center; gap: 10px;
  padding: 14px 16px 12px; border-bottom: 1px solid var(--rule);
}
.side-head h2 {
  flex: 1; display: flex; align-items: center; gap: 8px;
  margin: 0; font-size: 15px; font-weight: 600;
}
.side-head .count {
  padding: 3px 7px; border-radius: 999px; font: 600 11px/1 var(--ui);
  background: var(--brand); color: var(--on-ink);
}
.side-list { list-style: none; margin: 0; padding: 0; }
.side-list li + li { border-top: 1px solid var(--rule); }
.side-list a {
  display: flex; flex-direction: column; align-items: flex-start; gap: 5px;
  padding: 12px 16px; color: inherit;
}
.side-list a:hover { background: var(--row-hover); text-decoration: none; }
.side-list strong { font-size: 13.5px; font-weight: 600; line-height: 1.35; }
.side-list .unread strong::after {
  content: ""; display: inline-block; width: 7px; height: 7px;
  margin-left: 7px; border-radius: 50%; background: var(--brand); vertical-align: 2px;
}
.side-kind {
  padding: 4px 6px; border-radius: 4px; background: var(--sunk); color: var(--ink);
  font: 600 9.5px/1 var(--ui); letter-spacing: .08em; text-transform: uppercase;
}
.side-where { font-size: 12px; color: var(--faint); }
.side-empty { padding: 14px 16px 16px; }
.side-empty p { margin: 0 0 12px; font-size: 13.5px; line-height: 1.5; color: var(--muted); }
.skel-side { display: flex; flex-direction: column; gap: 12px; padding: 16px; }

.welcome {
  display: grid; grid-template-columns: minmax(0, 1.25fr) minmax(0, 1fr);
  gap: 30px; align-items: center; padding: 32px;
}
.welcome h2 { margin: 0 0 10px; font-size: 22px; font-weight: 600; letter-spacing: -.015em; }
.welcome p { margin: 0 0 22px; max-width: 52ch; line-height: 1.6; color: var(--muted); }
.welcome-key { margin-left: 12px; }
kbd {
  padding: 2px 6px; border: 1px solid var(--rule-2); border-bottom-width: 2px;
  border-radius: 4px; background: var(--raised); color: var(--ink);
  font: 600 11px/1.3 var(--mono);
}
.welcome-steps {
  list-style: none; margin: 0; padding: 0; counter-reset: step;
  display: flex; flex-direction: column; gap: 16px;
}
.welcome-steps li {
  counter-increment: step;
  display: grid; grid-template-columns: 30px minmax(0, 1fr); column-gap: 12px; row-gap: 2px;
}
.welcome-steps li::before {
  content: counter(step); grid-row: span 2;
  width: 30px; height: 30px; display: grid; place-items: center; border-radius: 50%;
  background: var(--wash); color: var(--brand-ink); font: 600 13px/1 var(--ui);
}
.welcome-steps strong { font-size: 14px; }
.welcome-steps span { font-size: 13px; color: var(--muted); }

@media (max-width: 760px) {
  .kpis { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 10px; }
  .kpi-value { font-size: 22px; }
  .welcome { grid-template-columns: minmax(0, 1fr); padding: 22px 20px; }
  .seg { width: 100%; }
  .seg button { flex: 1; text-align: center; }
}

/* ---------- updates ---------- */
.watch-panel {
  display: flex; gap: 12px 16px; align-items: center; flex-wrap: wrap;
  padding: 14px 16px; margin: 0 0 24px; border-radius: 8px; background: var(--sunk);
}
.watch-panel.on { background: var(--good-bg); }
.watch-panel.on strong { color: var(--good); }
.watch-panel .grow { flex: 1; min-width: 220px; }
.md .watch-panel p { margin: 4px 0 0; color: var(--muted); font-size: 13px; line-height: 1.5; }
.update { padding: 2px 0 20px; }
.update + .update { border-top: 1px solid var(--rule); padding-top: 24px; }
.md .update-head h2 {
  margin: 0 0 4px; padding: 0; border: 0; font-size: 19px; line-height: 1.3;
}
.md .update-head p { margin: 0; }
.md .update-group {
  margin: 20px 0 6px; font: 600 10.5px/1 var(--ui); letter-spacing: .1em;
  text-transform: uppercase; color: var(--faint);
}
.md .update-items { list-style: none; padding: 0; margin: 0; }
.md .update-items li { margin: 0; padding: 10px 0; border-top: 1px solid var(--rule); }
.md .update-items li:first-child { border-top: 0; }
.md .update-items p { margin: 3px 0 6px; color: var(--muted); font-size: 14px; }
.update-links { display: flex; gap: 14px; flex-wrap: wrap; font-size: 12.5px; }
.linkish {
  all: unset; cursor: pointer; color: var(--brand-ink); font-size: 12.5px;
}
.linkish:hover { text-decoration: underline; }
.linkish:focus-visible { outline: 2px solid var(--brand); outline-offset: 2px; border-radius: 3px; }

/* ---------- versions ---------- */
.md .version-intro { margin: 0 0 18px; }
.md .versions { list-style: none; margin: 0; padding: 0; }
.md .versions li {
  display: flex; align-items: flex-start; gap: 14px; margin: 0;
  padding: 14px 0; border-top: 1px solid var(--rule);
}
.md .versions li:first-child { border-top: 0; }
.md .versions p { margin: 4px 0 0; }
.versions .grow { flex: 1; min-width: 0; }
.version-num {
  flex: none; margin-top: 1px; padding: 6px 8px; border-radius: 5px;
  font: 600 12px/1 var(--mono); background: var(--sunk); color: var(--ink);
}
.version.current .version-num { background: var(--good-bg); color: var(--good); }
.version-head { display: flex; flex-wrap: wrap; align-items: baseline; gap: 4px 10px; }
.md .version-list { color: var(--muted); }
.version-bar {
  display: flex; flex-wrap: wrap; align-items: center; gap: 10px 12px;
  padding: 12px 14px; margin: 0 0 18px; border-radius: 8px;
  background: var(--warn-bg);
}
.version-bar.current { background: var(--good-bg); }
.version-bar .grow { flex: 1; min-width: 220px; }
.version-bar .kicker {
  display: block; margin-bottom: 5px; font: 600 10px/1 var(--ui);
  letter-spacing: .1em; text-transform: uppercase; color: var(--warn);
}
.version-bar.current .kicker { color: var(--good); }
.version-pick { display: flex; flex-wrap: wrap; align-items: center; gap: 10px; margin: 0 0 16px; }
.version-pick select {
  max-width: 100%; padding: 7px 10px; border-radius: 6px;
  border: 1px solid var(--rule-2); background: var(--raised); color: var(--ink);
  font: 14px var(--ui);
}
.md .version-change {
  display: inline-block; margin: 0 0 14px; padding: 5px 9px; border-radius: 4px;
  background: var(--wash); color: var(--brand-ink);
}
/* The version switcher in a market's header. */
.vpick { position: relative; }
.vpick-btn { display: inline-flex; align-items: center; gap: 7px; }
.vpick-btn svg { width: 15px; height: 15px; flex: none; }
.vpick-btn .chev { width: 13px; height: 13px; color: var(--faint); }
.vpick-menu {
  position: absolute; top: calc(100% + 6px); left: 0; z-index: 40;
  width: 320px; max-height: min(70vh, 480px); overflow-y: auto; padding: 5px;
  background: var(--paper); border: 1px solid var(--rule);
  border-radius: 10px; box-shadow: var(--shadow-modal);
}
.vpick-menu a {
  display: flex; flex-direction: column; gap: 2px;
  padding: 8px 10px; border-radius: 7px; color: var(--ink);
}
.vpick-menu a:hover { background: var(--sunk); text-decoration: none; }
.vpick-menu a.on { background: var(--wash); }
.vpick-menu strong { font-size: 13.5px; font-weight: 600; }
.vpick-menu em {
  margin-left: 6px; font-style: normal; font-size: 11px; font-weight: 600; color: var(--good);
}
.vpick-menu span { font-size: 12px; color: var(--muted); }
.vpick-sep {
  margin-top: 4px; padding: 10px 10px 4px; border-top: 1px solid var(--rule);
  font: 600 10px/1 var(--ui); letter-spacing: .1em; text-transform: uppercase;
  color: var(--faint);
}
.vpick-empty { padding: 10px; font-size: 13px; color: var(--muted); }
@media (max-width: 640px) {
  .vpick-menu { left: 0; width: min(320px, calc(100vw - 32px)); }
}

/* ---------- differences between versions ----------
   Green for what the later version has, red for what it no longer has, from
   the theme's own good and brand colours so both themes carry them. The
   words always say which is which too: "+ added" and "− removed" in the
   summary, and the ins/del elements for anything reading the page aloud. */
.version-tools { display: flex; flex-wrap: wrap; gap: 4px 18px; }
.diff-summary {
  display: flex; flex-wrap: wrap; align-items: center; gap: 8px 10px;
  margin: 0 0 18px; font-size: 13px; color: var(--muted);
}
.diff-range { font-weight: 600; color: var(--ink); margin-right: 4px; }
.diff-key {
  padding: 3px 8px; border-radius: 4px; background: var(--sunk);
  font-weight: 600; font-variant-numeric: tabular-nums;
}
.diff-key.add { background: color-mix(in srgb, var(--good) 16%, transparent); color: var(--good); }
.diff-key.del { background: color-mix(in srgb, var(--brand) 16%, transparent); color: var(--brand-ink); }
.diff-summary .diff-only { margin-left: auto; font-size: 13px; }
.md .version-change.same { background: var(--sunk); color: var(--muted); }

.md .diff-block {
  display: block; margin: 12px 0; padding: 2px 14px;
  border-left: 3px solid transparent; border-radius: 0 6px 6px 0;
  text-decoration: none; color: inherit;
}
.md .diff-add {
  background: color-mix(in srgb, var(--good) 11%, transparent); border-left-color: var(--good);
}
.md .diff-del {
  background: color-mix(in srgb, var(--brand) 10%, transparent); border-left-color: var(--brand);
}
.md .diff-del > * { opacity: .8; }
.md .diff-changed { border-left-color: var(--rule-2); }
.md .diff-add::before, .md .diff-del::before {
  display: block; margin: 8px 0 -4px;
  font: 600 10px/1 var(--ui); letter-spacing: .1em; text-transform: uppercase;
}
.md .diff-add::before { content: "Added"; color: var(--good); }
.md .diff-del::before { content: "Removed"; color: var(--brand-ink); }
.md .diff-word { padding: 0 2px; border-radius: 3px; }
.md ins.diff-word {
  text-decoration: none; color: var(--good);
  background: color-mix(in srgb, var(--good) 22%, transparent);
}
.md del.diff-word {
  color: var(--brand-ink); text-decoration-thickness: 1px;
  background: color-mix(in srgb, var(--brand) 18%, transparent);
}
.md .diff-gap { display: none; }
.md .only-changes .diff-same { display: none; }
.md .only-changes .diff-gap {
  display: block; margin: 10px 0; padding-top: 6px; text-align: center;
  border-top: 1px dashed var(--rule-2); font-size: 12px; color: var(--faint);
}

.version-pager {
  display: flex; flex-wrap: wrap; justify-content: space-between; gap: 10px;
  margin-top: 28px; padding-top: 16px; border-top: 1px solid var(--rule);
}

/* ---------- the chat ---------- */
.chat-head {
  display: flex; align-items: center; gap: 8px 16px; flex-wrap: wrap;
  margin-bottom: 18px;
}
.md .chat-head h1 { margin: 0; }
.md .chat-head p { margin: 0; flex-basis: 100%; }
.chat-left {
  font: 500 11px/1 var(--mono); padding: 5px 8px; border-radius: 4px;
  background: var(--sunk); color: var(--muted);
}
.chat-left.out { background: var(--wash); color: var(--brand-ink); }
.chat-thread { display: flex; flex-direction: column; gap: 14px; margin-bottom: 16px; }
.msg.user {
  align-self: flex-end; max-width: min(560px, 88%);
  padding: 10px 14px; border-radius: 12px 12px 4px 12px;
  background: var(--ink); color: var(--on-ink);
  white-space: pre-wrap; overflow-wrap: anywhere;
}
.md .msg.user p { margin: 0; font-size: 14.5px; line-height: 1.55; }
.msg.bot {
  padding: 12px 16px; border: 1px solid var(--rule);
  border-radius: 12px 12px 12px 4px; background: var(--paper);
}
.md .md-reply > *:first-child { margin-top: 0; }
.md .md-reply > *:last-child { margin-bottom: 0; }
.msg-foot {
  display: flex; flex-wrap: wrap; align-items: center; gap: 6px 12px;
  margin-top: 10px; padding-top: 9px; border-top: 1px solid var(--rule);
  font-size: 12.5px;
}
.msg-foot .grow { flex: 1; }
.msg-foot a { color: var(--muted); }
.msg.pending { display: flex; align-items: center; gap: 9px; color: var(--faint); font-size: 13.5px; }
.md .chat-intro { margin: 0 0 10px; color: var(--muted); font-size: 14px; }
.chat-starters { display: flex; flex-wrap: wrap; gap: 8px; }
.chat-starters button {
  all: unset; box-sizing: border-box; cursor: pointer;
  padding: 8px 12px; border: 1px solid var(--rule-2); border-radius: 999px;
  background: var(--paper); color: var(--ink); font-size: 13px; line-height: 1.3;
}
.chat-starters button:hover { background: var(--sunk); }
.chat-starters button:focus-visible { outline: 2px solid var(--brand); outline-offset: 2px; }
.chat-form {
  position: sticky; bottom: 12px; padding: 10px;
  background: var(--paper); border: 1px solid var(--rule-2); border-radius: 12px;
  box-shadow: var(--shadow);
}
.chat-form:focus-within { border-color: var(--ink); }
.chat-form textarea {
  display: block; width: 100%; box-sizing: border-box; min-height: 64px;
  padding: 4px 6px; border: 0; outline: none; resize: vertical;
  background: transparent; color: var(--ink); font: 14.5px/1.5 var(--ui);
}
.chat-form textarea::placeholder { color: var(--faint); }
.chat-actions { display: flex; align-items: center; gap: 10px; }
.chat-actions .grow { flex: 1; }
.chat-form[aria-disabled="true"] { opacity: .6; }

@media (max-width: 640px) {
  .msg.user { max-width: 94%; }
  /* Pinned to the bottom of a phone screen, the full-height box covered a
     fifth of it. It still grows with the text; it just starts smaller. */
  .chat-form textarea { min-height: 42px; }
  .feed-list a { flex-direction: column; gap: 7px; }
  .market-card .foot { flex-wrap: wrap; }
}

/* ---------- password sign-in / register ---------- */

.divider {
  display: flex; align-items: center; gap: 12px; margin: 20px 0 16px;
  color: var(--faint); font-size: 12px; text-transform: uppercase;
  letter-spacing: .08em;
}
.divider::before, .divider::after {
  content: ""; flex: 1; height: 1px; background: var(--rule);
}

/* ---------- the attachments step ---------- */

.drop {
  display: flex; flex-direction: column; align-items: center; gap: 4px;
  padding: 26px 18px; cursor: pointer; text-align: center;
  border: 1.5px dashed var(--rule-2); border-radius: 12px;
  background: var(--sunk); color: var(--muted);
  transition: border-color .12s ease, background .12s ease;
}
.drop strong { font-size: 14.5px; color: var(--ink); }
.drop:hover, .drop:focus-visible { border-color: var(--brand); outline: none; }
/* The whole panel answers the drag, not just its border: a 1.5px edge is not
   a target anyone can see they are over. */
.drop.over { border-color: var(--brand); background: var(--wash); }
.drop-limits { color: var(--faint); margin-top: 6px; }

.attach-list { list-style: none; margin: 12px 0 0; padding: 0; }
.attach-list li {
  display: flex; align-items: center; gap: 10px;
  padding: 9px 4px 9px 10px; border-bottom: 1px solid var(--rule);
  font-size: 13px;
}
.attach-name { overflow-wrap: anywhere; }
.attach-size {
  margin-left: auto; color: var(--faint); font: 11.5px var(--mono);
  white-space: nowrap;
}
.attach-x {
  flex: none; width: 24px; height: 24px; border-radius: 6px;
  border: 0; background: none; cursor: pointer;
  color: var(--faint); font-size: 17px; line-height: 1;
}
.attach-x:hover { background: var(--wash); color: var(--brand-ink); }
.note.bad { color: var(--brand-ink); }

.pw-form { display: flex; flex-direction: column; gap: 13px; }
.pw-form label {
  display: flex; flex-direction: column; gap: 6px;
  font-size: 12.5px; font-weight: 600;
}
.pw-form input {
  font: 14px var(--ui); padding: 10px 12px; border-radius: 6px;
  border: 1px solid var(--rule-2); background: var(--raised); color: var(--ink);
}
.pw-form input:focus { border-color: var(--ink); outline: none; }
.pw-form input::placeholder { color: var(--faint); font-weight: 400; }
.lbl-small { font-weight: 400; color: var(--faint); }
/* The two actions stack, each the width of the card: a primary and a quieter
   second choice, rather than a button with a text link tacked beside it. */
.pw-actions {
  display: flex; flex-direction: column; align-items: stretch;
  gap: 8px; margin-top: 6px;
}
.pw-actions .btn { width: 100%; justify-content: center; }
.pw-actions .link {
  background: none; border: 0; cursor: pointer; width: 100%;
  padding: 11px 12px; border-radius: 8px;
  font: 500 14px var(--ui);
  /* --muted rather than a literal grey: it resolves to a dark grey on the
     light theme and a light one on the dark, which is what "quieter than the
     primary" has to mean in each. A hard-coded #555 would disappear at night. */
  color: var(--muted);
}
.pw-actions .link:hover {
  color: var(--ink); background: var(--row-hover); text-decoration: none;
}

/* Room between the two actions and the line that offers the other route. */
.signin-card > p.small { margin-top: 22px; }

/* change password on the account page */
.pw-panel { padding: 0; margin-top: 18px; }
.pw-panel summary {
  cursor: pointer; font-size: 13px; font-weight: 600; color: var(--muted);
  padding: 14px 20px; list-style: none; display: flex; align-items: center;
  gap: 8px;
}
.pw-panel summary::-webkit-details-marker { display: none; }
.pw-panel summary::before { content: "+"; font: 700 14px var(--mono); }
.pw-panel[open] summary { border-bottom: 1px solid var(--rule); }
.pw-panel[open] summary::before { content: "\2212"; }
.pw-panel summary:hover { color: var(--ink); }
.pw-panel form {
  display: grid; gap: 13px; padding: 18px 20px 20px;
  grid-template-columns: 1fr 1fr;
}
.pw-panel form label {
  display: flex; flex-direction: column; gap: 6px;
  font-size: 12.5px; font-weight: 600;
}
.pw-panel form input {
  font: 14px var(--ui); padding: 9px 11px; border-radius: 6px;
  border: 1px solid var(--rule-2); background: var(--raised); color: var(--ink);
}
.pw-panel form input:focus { border-color: var(--ink); outline: none; }
.pw-panel form .btn { justify-self: start; }
.pw-panel form .error { grid-column: 1 / -1; margin: 0; }
@media (max-width: 560px) {
  .pw-panel form { grid-template-columns: 1fr; }
}

/* ---------- personalisation on the account page ----------
   A head band and a padded body, the same shape the tier list and the change-
   password panel use, so the three cards on this page share one grid: content
   starts at 20px from the card edge in all of them. The panel itself carries
   no padding -- .panel never does.

   The three boxes are one row of equal columns while there is room for it.
   They are three halves of one answer, asked in parallel, so they read better
   side by side than as a tall stack of wide, two-line slots -- and at this
   page width a full-bleed field is a 1200px line to write one sentence in. */

.profile-panel { margin-top: 18px; }
.profile-head { padding: 16px 20px; border-bottom: 1px solid var(--rule); }
.profile-panel h2 {
  margin: 0 0 4px; font-size: 15px; font-weight: 700;
}
.profile-head p { margin: 0; max-width: 68ch; }
.profile-panel form { padding: 18px 20px 20px; }
.profile-fields {
  display: grid; gap: 14px;
  grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
}
/* flex-end does nothing while every label is one line, which is the whole
   point of the 250px floor. It is here for the day a label wraps anyway --
   a translation, a wider font -- so the row grows and the three boxes still
   line up instead of one sliding down a line. */
.profile-panel form label {
  display: flex; flex-direction: column; justify-content: flex-end; gap: 6px;
  font-size: 12.5px; font-weight: 600;
}
.profile-panel form textarea {
  font: 13.5px var(--ui); padding: 9px 11px; border-radius: 6px;
  border: 1px solid var(--rule-2); background: var(--raised); color: var(--ink);
  resize: vertical; line-height: 1.45;
}
.profile-panel form textarea:focus { border-color: var(--ink); outline: none; }
.profile-panel form textarea::placeholder {
  color: var(--faint); font-weight: 400;
}
.profile-panel form .error { margin: 0 0 14px; }
/* Save keeps its own width. It was a grid item before, which stretched it the
   full width of the card and made a footnote-sized action look like the whole
   point of the page. */
.profile-actions {
  display: flex; align-items: center; flex-wrap: wrap; gap: 12px;
  margin-top: 16px;
}
.profile-actions .hint { margin-left: auto; }

/* the fill-you-in nudge on the markets page ----------
   An invitation, not a warning, so it wears the interface colours instead of
   the amber .notice wears: a card like the ones under it, with the brand on
   one edge for what the eye should reach first, and the action set in ink.
   The dismiss sits in the flow with a 28px target -- the copy used to end in
   "or press the small x", which is what a control says when it is too small
   to find on its own. */
.profile-alert {
  display: flex; align-items: center; gap: 16px;
  margin: 0 0 16px; padding: 13px 14px 13px 17px;
  background: var(--paper); border: 1px solid var(--rule);
  border-left: 3px solid var(--brand);
  border-radius: var(--radius); box-shadow: var(--shadow);
  font-size: 13.5px;
}
.profile-alert p { margin: 3px 0 0; color: var(--muted); }
.profile-alert .btn { flex: none; }
.profile-alert .btn:hover { text-decoration: none; }
.profile-alert .dismiss {
  flex: none; width: 28px; height: 28px; display: grid; place-items: center;
  background: none; border: 0; border-radius: 6px; cursor: pointer;
  font: 400 17px/1 var(--ui); color: var(--faint);
  transition: background .12s ease, color .12s ease;
}
.profile-alert .dismiss:hover { background: var(--sunk); color: var(--ink); }

@media (max-width: 620px) {
  /* the x stays level with the headline; the action takes its own line */
  .profile-alert { flex-wrap: wrap; align-items: flex-start; padding: 13px 14px; }
  .profile-alert .grow { order: 1; }
  .profile-alert .dismiss { order: 2; margin: -3px -4px 0 0; }
  .profile-alert .btn { order: 3; flex: 1 1 100%; text-align: center; }
}
@media (max-width: 560px) {
  .profile-actions .hint { margin-left: 0; }
}

/* ---------- the welcome credit ----------

   The one place in this interface that is allowed to be pleased with itself.
   Everything else here reports on work; this hands somebody something, once,
   and a flat grey card would undersell it.

   Green is used here and nowhere else as a button fill. That is a deliberate
   exception to the palette rule at the top of this file -- black is the
   interface -- because this is not an interface action. It is the only button
   in the product that gives rather than does, and it should not look like
   Save. */




/* ---------- the research graph ---------- */

.graph-head { display: flex; align-items: flex-start; gap: 16px; }
.graph-head h3 { margin: 0 0 4px; font-size: 15px; font-weight: 600; }
.graph-head p { margin: 0; max-width: 76ch; }
.graph-warn { color: var(--brand-ink); }

.graph-legend {
  display: flex; flex-wrap: wrap; gap: 14px; margin: 14px 0 10px;
  font-size: 12px; color: var(--muted);
}
.graph-key { display: flex; align-items: center; gap: 6px; }
.graph-key i {
  width: 9px; height: 9px; border-radius: 50%; flex: none;
  background: var(--faint);
}

/* Tall enough that a force layout has room to separate. A short box makes
   every graph look like one clump regardless of what is in it. */
.graph-canvas {
  /* 820, not 640. The graph fits itself to this box, so the box is what sets
     the zoom -- and at 640 a 27-node graph landed near 0.5, where the labels
     render at half their stated size. */
  height: min(78vh, 820px);
  border: 1px solid var(--rule); border-radius: var(--radius);
  background: var(--sunk);
  overflow: hidden;   /* cytoscape draws to the edges; the radius must bite */
}
.graph-tip {
  margin: 8px 0 0; min-height: 1.2em;
  font-variant-numeric: tabular-nums;
}

/* ==========================================================================
   The public page.

   Same tokens, same components, more air. The product view is dense because
   somebody is working in it; this is read once, standing up, deciding. The
   only new idea here is vertical rhythm -- everything else is .btn, .chip and
   the colour rules already set at the top of this file.

   The palette discipline holds and matters more here than anywhere: on a page
   trying to sell something, the temptation is to make every panel brand red.
   Red is reserved for the one action and the one price we want read first.
   ========================================================================== */

body.public { background: var(--ground); }
.public-bar { gap: 14px; }
.public-tabs a { font-size: 13.5px; }
.public-bar .btn { padding: 7px 14px; font-size: 13.5px; }

/* One spacing scale for the whole page. Every vertical gap below is one of
   these four values, so the rhythm survives a section being added and there is
   a single place to change how loudly the page breathes.

   The measure tracks the viewport up to a ceiling instead of sitting at a
   fixed width: on a 3440 monitor a 1280 column is a third of the screen with
   two empty thirds beside it. 92vw fills the glass, 2040 is where it stops --
   past that the grids gain columns nobody asked for. Long lines are prevented
   per element (sec-lead 34ch, statement 66ch, faq 68ch) rather than by
   strangling the container, so the grids get the width and the prose does not. */
.landing {
  --band: clamp(72px, 7vw, 120px);   /* one section to the next */
  --head: 12px;                      /* heading to its lead line */
  --lead: clamp(26px, 2.6vw, 40px);  /* lead line to what it opens */
  --grid: clamp(16px, 1.6vw, 28px);  /* between items in any grid */
  --col: clamp(24px, 2.6vw, 44px);   /* between grid columns */

  /* Type. The page used to run 12.5-15px throughout with headings capped at
     23px -- the sizes the app chrome uses, on a page selling a report that
     costs as much as a car. Set once here so the whole page moves together. */
  --t-h2:   clamp(26px, 2.2vw, 36px);   /* section titles */
  --t-item: clamp(15.5px, 1.15vw, 17px);/* the name of a thing in a list */
  --t-lead: clamp(15px, 1.1vw, 17px);   /* section leads */
  --t-body: clamp(14.5px, 1.05vw, 16px);/* everything else */
  width: min(92vw, 2040px); margin: 0 auto;
  padding: 0 clamp(18px, 2vw, 40px) 72px;
}

/* ---------- hero ---------- */

.hero-eyebrow {
  margin: 0 0 20px; font: 600 11px/1 var(--mono);
  letter-spacing: .16em; text-transform: uppercase; color: var(--brand-ink);
}
.hero-lead {
  margin: 0 0 30px; font-size: clamp(15.5px, 1.2vw, 18px); line-height: 1.6;
  color: var(--muted); max-width: 60ch;
}
.hero-actions { display: flex; flex-wrap: wrap; gap: 12px; }
.btn.big { padding: 13px 22px; font-size: 15px; border-radius: 8px; }
.hero-foot {
  margin: 20px 0 0; font-size: 13.5px; color: var(--faint); max-width: 56ch;
}

/* ---------- bands ---------- */

/* ---------- the section grid ----------

   Every section is a numbered heading on the left and its content on the
   right. One shell, assembled once in landing.js, so the page reads as a
   numbered document -- which is what is being sold -- instead of as a stack of
   equally loud full-width bands. It is also the only layout here that does
   something with a wide monitor other than leave it empty.

   The heading sticks while its own content scrolls past, so you always know
   which section you are in. It unsticks below 900px, where the two columns
   become one and stickiness would just cover the content. */
/* ---------- the bento ----------

   Tiles on twelve columns: eight and four, five and seven, seven and five.
   The page reads as an arrangement instead of a stack, and the varying spans
   give it a rhythm that a run of identical full-width sections cannot.

   Two breakpoints, not one. Below 1000px the wide tiles go full width and the
   narrow ones pair up; below 700px everything is a single column. A twelve-
   column grid that collapses straight from four columns to one loses the
   arrangement before it needs to. */
.bento {
  display: grid; gap: var(--grid);
  grid-template-columns: repeat(12, minmax(0, 1fr));
  padding: clamp(34px, 3.6vw, 60px) 0 0;
}
.tile {
  grid-column: span 12; min-width: 0;
  background: var(--paper); border: 1px solid var(--rule);
  border-radius: 18px; padding: clamp(24px, 2.2vw, 34px);
  display: flex; flex-direction: column; gap: 20px;
}
@media (min-width: 700px) {
  .t4, .t5 { grid-column: span 6; }
}
@media (min-width: 1000px) {
  .t4 { grid-column: span 4; }
  .t5 { grid-column: span 5; }
  .t7 { grid-column: span 7; }
  .t8 { grid-column: span 8; }
}
.tile-head { display: flex; flex-direction: column; gap: 9px; }
.tile-label {
  font: 600 11px/1 var(--mono); letter-spacing: .18em;
  text-transform: uppercase; color: var(--brand-ink);
}
.tile h2 {
  margin: 0; font-size: clamp(20px, 1.55vw, 26px); font-weight: 700;
  letter-spacing: -.025em; line-height: 1.18;
}
.tile-lead {
  margin: 0; color: var(--muted); font-size: var(--t-body); line-height: 1.55;
}
.tile-body { display: flex; flex-direction: column; gap: 20px; min-width: 0; }

/* The offline claim gets a tile with no header -- just the mark and the
   sentence, on the brand wash so it reads as the aside it is. */
.tile-quiet {
  justify-content: center; gap: 16px;
  /* --paper, the same fill and hairline as every other tile: white on the
     light theme, and the dark card on the dark one rather than a white slab
     glowing in the middle of the grid. The green shield is what marks this
     tile out now, which is enough. */
  background: var(--paper); border-color: var(--rule);
}
.tile-quiet .statement {
  margin: 0; padding: 0; max-width: none; display: block;
  font-size: clamp(15.5px, 1.2vw, 18px); line-height: 1.5;
  letter-spacing: -.012em; color: var(--ink);
}

/* The sample sits in the grid itself rather than inside a tile: it is already
   a panel, and a panel inside a panel is a border nobody asked for. */
.bento > .sample { grid-column: span 12; }
@media (min-width: 1000px) { .bento > .sample { grid-column: span 8; } }

/* Tiles are narrower than the page was, so what is inside them takes fewer
   columns. Stated here rather than in each component, because the component
   does not know it is in a tile. */
/* Full width, so three columns rather than two: fifteen entries five deep is
   a calmer shape than eight, and it leaves the row beneath it less to fill. */
.t12 .deliverables { columns: 300px 3; }
.tile .sources { columns: 300px 2; }
.tile .stages { grid-template-columns: repeat(2, minmax(0, 1fr)); }
.tile .stages li::after { display: none; }
.tile .showcase { grid-template-columns: minmax(0, 1fr); }
/* Shorter inside a five-column tile than it was running full width: the tile
   beside it holds four stages, and a row is only as balanced as its tallest
   member. */
.tile .showcase .shotframe img { max-height: clamp(240px, 20vw, 340px); }
.tile .stat-row { grid-template-columns: repeat(auto-fit, minmax(120px, 1fr)); }

/* ---------- slabs ----------
   Plans, the analysts and the questions are not tiles: each is one thing that
   needs the whole measure, so they keep a plain heading and run full width. */
.slab { padding: var(--band) 0 0; }
.slab-head {
  max-width: 60ch; margin-bottom: clamp(26px, 2.8vw, 44px);
  display: flex; flex-direction: column; gap: 10px;
}
.slab-head h2 {
  margin: 0; font-size: var(--t-h2); font-weight: 700;
  letter-spacing: -.028em; line-height: 1.12;
}
.slab-head p {
  margin: 0; color: var(--muted); font-size: var(--t-lead); line-height: 1.55;
}

/* The one filled panel on the page, and it keeps its fill because it is the
   single thing a visitor can leave with. It lives inside "what lands on your
   desk" rather than in a section of its own: handing over the real deliverable
   belongs next to the list of what that deliverable contains. */
.sample {
  display: flex; flex-wrap: wrap; align-items: center;
  justify-content: space-between; gap: clamp(18px, 2vw, 32px);
  background: var(--sunk); border-radius: 14px;
  padding: clamp(22px, 2.4vw, 32px);
}
.sample-copy strong {
  display: block; font-size: var(--t-item); margin-bottom: 7px;
}
.sample-copy p {
  margin: 0 0 14px; font-size: var(--t-body); color: var(--muted);
  line-height: 1.55; max-width: 46ch;
}

/* The one band that gets a surface, because it is the claim most likely to be
   the reason somebody chooses this over a hosted tool. */
.statement-mark {
  flex: none; width: 34px; height: 34px; border-radius: 9px; margin-top: 3px;
  display: grid; place-items: center;
  background: var(--good-bg); color: var(--good);
}
.statement-mark svg { width: 18px; height: 18px; }

/* ---------- the four stages ---------- */

/* A flow, not four boxes. The rule running behind the numbers is the whole
   point of the section -- these are stages of one process, and a plain grid
   said "four features" instead. */
.stages {
  list-style: none; margin: 0; padding: 0; position: relative;
  display: grid; gap: var(--grid);
  grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
}
.stages li {
  display: flex; gap: 13px; align-items: flex-start; position: relative;
}
/* The connector is drawn from each item rather than as one line behind the
   grid, so it wraps and disappears at the row end on its own. */
.stages li::after {
  content: ""; position: absolute; top: 13px; left: 26px; right: -18px;
  height: 1px; background: var(--rule); z-index: 0;
}
.stages li:last-child::after { display: none; }
@media (max-width: 700px) { .stages li::after { display: none; } }
.stage-n {
  flex: none; width: 26px; height: 26px; border-radius: 50%;
  display: grid; place-items: center; position: relative; z-index: 1;
  background: var(--ink); color: var(--on-ink);
  font: 600 12px/1 var(--mono);
  box-shadow: 0 0 0 4px var(--ground);   /* punches a hole in the connector */
}
.stages strong { display: block; margin-bottom: 6px; font-size: var(--t-item); }
.stages p {
  margin: 0; font-size: var(--t-body); color: var(--muted); line-height: 1.55;
}

/* ---------- plans ---------- */

.plans {
  display: grid; gap: var(--grid);
  grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
  align-items: stretch;
}
.plan {
  position: relative;
  background: var(--paper); border: 1px solid var(--rule);
  border-radius: 12px; padding: clamp(24px, 2vw, 30px) clamp(22px, 1.8vw, 26px);
  display: flex; flex-direction: column; gap: 0;
  box-shadow: var(--shadow);
  transition: transform .14s ease, border-color .14s ease;
}
.plan:hover { transform: translateY(-2px); border-color: var(--rule-2); }
.plan-featured:hover { border-color: var(--brand); }
/* One card is emphasised, with the border rather than a fill: a solid brand
   panel would out-shout the price it is trying to draw attention to. */
.plan-featured { border-color: var(--brand); }
.plan-flag {
  position: absolute; top: -9px; left: 22px;
  background: var(--brand); color: #ffffff;
  font: 600 10px/1 var(--ui); letter-spacing: .08em; text-transform: uppercase;
  padding: 5px 9px; border-radius: 4px;
}
.plan h3 { margin: 0 0 10px; font-size: 15px; font-weight: 600; }
.plan-price {
  font: 700 clamp(32px, 2.7vw, 40px)/1 var(--ui); letter-spacing: -.035em;
  font-variant-numeric: tabular-nums; margin-bottom: 14px;
}
.plan-price .cur {
  font-size: 18px; font-weight: 600; color: var(--muted); margin-right: 3px;
}
.plan-meta {
  display: flex; flex-wrap: wrap; gap: 6px 14px; margin-bottom: 16px;
  font-size: 12.5px; color: var(--muted);
}
.plan-meta strong { color: var(--ink); font-weight: 600; }
.plan-blurb {
  margin: 0 0 16px; font-size: 13.5px; line-height: 1.55; color: var(--muted);
}
.plan-points {
  list-style: none; margin: 0 0 18px; padding: 0;
  display: flex; flex-direction: column; gap: 9px;
  font-size: 13px; color: var(--muted);
}
.plan-points li { display: flex; gap: 9px; align-items: flex-start; }
.plan-points li::before {
  content: ""; flex: none; margin-top: 4px;
  width: 5px; height: 9px;
  border: solid var(--good); border-width: 0 2px 2px 0;
  transform: rotate(42deg);
}
.plan-note {
  margin: 0 0 18px; font-size: 12.5px; color: var(--faint); font-style: italic;
}
.plan-experts {
  display: flex; flex-wrap: wrap; gap: 8px; margin: 0 0 18px;
}
.expert-chip {
  display: flex; align-items: center; gap: 7px;
  background: var(--sunk); border-radius: 999px; padding: 4px 11px 4px 4px;
  font-size: 12.5px; color: var(--muted);
}
/* A circle holding the initial, with the photograph laid over it. Both live
   in the same grid cell, so a photo that fails to load is removed by
   photoFallback and the letter beneath is simply what you see -- no
   broken-image icon and no layout shift. */
.avatar {
  flex: none; width: 24px; height: 24px; border-radius: 50%;
  background: var(--wash); overflow: hidden;
  display: grid; place-items: center;
  font: 600 11px/1 var(--ui); color: var(--brand-ink);
}
.avatar > * { grid-area: 1 / 1; }
.avatar img {
  width: 100%; height: 100%; object-fit: cover; display: block;
  border-radius: 50%;
}
/* Belt to the DOM-order braces: the letter is written before the photograph
   so the photograph paints over it, and this keeps that true even if the two
   are ever reordered. */
.avatar i { font-style: normal; z-index: 0; }
.avatar img { z-index: 1; }
.avatar.big { width: 46px; height: 46px; font-size: 17px; }

.plan-cta { margin-top: auto; text-align: center; }
.plan-cta:hover { text-decoration: none; }
.plans-foot {
  margin: var(--lead) 0 0; font-size: 13px; color: var(--faint); max-width: 62ch;
}

/* ---------- experts ---------- */

/* Two columns of substance rather than two name badges. These credentials are
   the reason the vetted tiers cost what they cost, so they get the room to be
   read -- with the full biography behind a disclosure, because the reader who
   wants four hundred words and the reader who wants four facts are not the
   same person and neither should be served at the other's expense. */
.expert-cards {
  display: grid; gap: var(--grid);
  grid-template-columns: repeat(auto-fit, minmax(340px, 1fr));
  align-items: start;
}
.expert-card {
  background: var(--paper); border: 1px solid var(--rule);
  border-radius: 12px; padding: clamp(22px, 2vw, 30px);
  box-shadow: var(--shadow);
}
.expert-card header {
  display: flex; align-items: center; gap: 14px; margin-bottom: 16px;
}
.expert-card strong { display: block; font-size: 15.5px; letter-spacing: -.01em; }
.expert-lead {
  margin: 0 0 16px; font-size: var(--t-body); line-height: 1.6;
  color: var(--muted);
}
.expert-creds {
  list-style: none; margin: 0 0 16px; padding: 15px 0 0;
  border-top: 1px solid var(--rule);
  display: flex; flex-direction: column; gap: 10px;
  font-size: 13.5px; color: var(--muted);
}
.expert-creds li { display: flex; gap: 9px; align-items: flex-start; line-height: 1.45; }
/* A short brand rule rather than a tick: these are posts held, not features
   delivered, and a green check would read as a product comparison. */
.expert-creds li::before {
  content: ""; flex: none; margin-top: 7px;
  width: 10px; height: 2px; border-radius: 1px; background: var(--brand);
}
.expert-bio summary {
  cursor: pointer; list-style: none; font-size: 12.5px; font-weight: 600;
  color: var(--muted); display: inline-flex; align-items: center; gap: 6px;
}
.expert-bio summary::-webkit-details-marker { display: none; }
.expert-bio summary::before { content: "+"; font: 400 14px/1 var(--mono); }
.expert-bio[open] summary::before { content: "\2212"; }
.expert-bio summary:hover { color: var(--ink); }
.expert-bio p {
  margin: 14px 0 0; font-size: 12.5px; line-height: 1.65; color: var(--muted);
}

/* ---------- close ---------- */

/* A flex column, not a text block with a button dropped in it. .btn is
   inline-flex, and an inline-level box takes a top margin without it pushing
   anything -- so the button sat on top of the sentence above it. As flex items
   every child is block-level and the margins do what they say. */
.close {
  text-align: center; border-top: 1px solid var(--rule);
  padding: var(--band) 0;
  display: flex; flex-direction: column; align-items: center;
}
.close h2 {
  margin: 0 0 14px; font-size: clamp(28px, 3.2vw, 46px); font-weight: 700;
  letter-spacing: -.03em; line-height: 1.1;
}
.close p {
  margin: 0; max-width: 46ch; color: var(--muted);
  font-size: clamp(14.5px, 1.05vw, 16px); line-height: 1.6;
}
.close .btn { margin-top: 28px; }
.close-foot { margin-top: 18px; font-size: 12.5px; color: var(--faint); }
.landing-foot {
  display: flex; flex-wrap: wrap; align-items: baseline;
  gap: 14px clamp(20px, 3vw, 48px);
  padding: 30px 0 10px; border-top: 1px solid var(--rule);
}
.foot-brand {
  display: flex; align-items: baseline; flex-wrap: wrap; gap: 14px;
}
/* <address> is the element for the owner's own contact details, and browsers
   italicise it by default -- which is the one thing this line must not be. */
.foot-legal {
  margin-left: auto; font-style: normal; text-align: right;
  max-width: 62ch; font-size: 12.5px; line-height: 1.6; color: var(--faint);
}
.foot-legal strong { color: var(--muted); font-weight: 600; }
@media (max-width: 820px) {
  /* Nothing to push against once the footer stacks. */
  .foot-legal { margin-left: 0; text-align: left; }
}

/* ---------- destructive actions ----------
   The trash sits at the far left of the header actions, a button's width away
   from "Give it more time", because the two things you must never confuse are
   the one that spends money and the one that destroys work. It carries no
   colour until it is hovered: a permanently red control in a row of three
   reads as the one to press. */
.btn.icon {
  padding: 8px 10px; line-height: 0;
}
.btn.icon svg { width: 16px; height: 16px; display: block; }
.btn.icon:hover { color: var(--brand-ink); border-color: var(--brand-ink); }

/* In the dialog, where it IS the thing to press, it says so. */
.btn.danger { background: var(--wash); color: var(--brand-ink); border-color: transparent; }
.btn.danger:hover { background: var(--brand); color: var(--on-ink); }

/* ---------- the sign-in card, now a destination ---------- */

.signin-card { position: relative; }
.signin-back {
  position: absolute; top: -30px; left: 0;
  font-size: 13px; color: var(--muted);
}
.signin-back:hover { color: var(--ink); }

/* ---------- hero, now two columns ---------- */

/* The hero is the one section that is a surface rather than a stretch of the
   page. Everything below it sits on the ground and is structured by hairlines;
   lifting the opening onto --paper is what makes it read as the front of the
   document instead of the first of eleven equal bands.

   --paper, not #fff: it resolves to white in the light theme and to #141419 in
   the dark one, so the panel is white where white is right and does not become
   a slab of glare on the dark ground. */
/* The panel carries no padding of its own any more: the picture runs to its
   edges, so the inset has to belong to the copy instead. `overflow: hidden` is
   what lets the image take the panel's corner radius rather than square off
   over it, and `align-items: stretch` is what makes it full height. */
.hero {
  margin-top: clamp(14px, 1.8vw, 26px);
  padding: 0;
  display: grid; gap: 0; align-items: stretch;
  grid-template-columns: minmax(0, 0.9fr) minmax(0, 1fr);
  background: var(--paper);
  border: 1px solid var(--rule);
  border-radius: clamp(18px, 1.8vw, 28px);
  box-shadow: var(--shadow);
  overflow: hidden;
}
/* Second in the grid, first in the markup. The heading should still be the
   first thing a screen reader meets; only the picture moves. */
.hero-copy {
  order: 2; align-self: center; max-width: 820px;
  padding: clamp(38px, 4vw, 68px) clamp(26px, 3.2vw, 60px);
}
.hero h1 {
  margin: 0 0 22px; font-size: clamp(30px, 4vw, 64px); line-height: 1.08;
  letter-spacing: -.032em; font-weight: 700;
  /* balance keeps the browser from leaving a single orphaned word on the last
     line: at this size one stray word reads as a mistake rather than a wrap. */
  text-wrap: balance;
}

/* The product shot. A real evidence folder and a real scored opening, drawn
   in the same tokens as the app -- so it is a picture of the thing rather
   than an artist's impression of it, and it cannot drift out of date in the
   way a screenshot does. */
/* On the ground this only needed a hairline. On the hero panel it is paper on
   paper, so the border steps up to --rule-2 and the deeper shadow comes back --
   without both, the window dissolves into the surface behind it. */
/* The image is very nearly square (1268x1240), so left to fill its column it
   would set the height of the whole hero and leave the copy floating beside a
   great slab of picture. Capped and centred instead. */
.shot { order: 1; margin: 0; width: 100%; height: 100%; }
/* cover, not contain: the cell is whatever height the copy beside it turns out
   to be, and letterboxing a near-square picture into it would leave bars down
   both sides of the panel. */
.shot img {
  display: block; width: 100%; height: 100%; object-fit: cover;
}
/* ---------- the trust strip ----------
   A band, not cards: four facts read across in one movement, and boxing them
   would make each one look like a feature rather than a credential. */
/* Four credentials read across in one movement, directly under the hero and
   before any section number -- they are the reason to keep reading, not a
   chapter of their own. */
.proof {
  display: grid; gap: clamp(22px, 2.4vw, 40px) var(--col);
  grid-template-columns: repeat(auto-fit, minmax(190px, 1fr));
  padding: clamp(40px, 4.5vw, 72px) 0 clamp(36px, 4vw, 64px);
}
.proof-item { display: flex; flex-direction: column; gap: 7px; }
.proof-item strong { font-size: var(--t-item); line-height: 1.35; }
.proof-item span {
  font-size: var(--t-body); color: var(--muted); line-height: 1.5;
}

/* ---------- measured numbers ---------- */

.numbers-cap {
  margin: 0 0 var(--grid); font-size: var(--t-body); color: var(--muted);
}
.stat-row {
  display: grid; gap: var(--grid) var(--col);
  grid-template-columns: repeat(auto-fit, minmax(140px, 1fr));
}
.stat { padding: 15px 0 0; border-top: 1px solid var(--rule); }
.stat b {
  display: block; font: 700 clamp(26px, 2.4vw, 34px)/1 var(--ui);
  letter-spacing: -.035em; font-variant-numeric: tabular-nums;
  margin-bottom: 8px;
}
.stat span { font-size: var(--t-body); color: var(--muted); line-height: 1.45; }
.stat-foot {
  margin: var(--lead) 0 0; font-size: 13px; color: var(--faint); max-width: 62ch;
}



/* ---------- the sample report ----------
   Given a panel of its own rather than a link in a paragraph. For a product
   nobody can try before buying, handing over a real deliverable is the
   strongest thing the page does, and it should not read like a footnote. */

.sample {
  display: grid; gap: clamp(20px, 3vw, 48px); align-items: center;
  grid-template-columns: minmax(0, 1fr) auto;
  background: var(--sunk); border-radius: 16px;
}
.sample h2 { margin-bottom: 10px; }
.sample-facts {
  display: flex; flex-wrap: wrap; gap: 8px; margin-bottom: 24px;
}
.sample-facts span {
  font: 500 11.5px/1 var(--mono); padding: 6px 10px; border-radius: 5px;
  background: var(--sunk); color: var(--muted); letter-spacing: .02em;
}
.sample .btn { text-decoration: none; }
.sample .btn:hover { text-decoration: none; }

.hero-foot a {
  color: var(--brand-ink); text-decoration: underline;
  text-underline-offset: 3px; text-decoration-thickness: 1px;
}
.hero-foot a:hover { color: var(--ink); }

/* ---------- the real screenshot ----------
   Full container width, in the same window chrome as the drawn hero shot so
   the two read as the same object at two scales. */

.shotframe {
  margin: 0; border: 1px solid var(--rule); border-radius: 12px;
  overflow: hidden; background: var(--paper); box-shadow: var(--shadow);
}
.shotframe-bar {
  display: flex; gap: 6px; padding: 11px 13px;
  background: var(--sunk); border-bottom: 1px solid var(--rule);
}
.shotframe-bar span {
  width: 9px; height: 9px; border-radius: 50%; background: var(--rule-2);
}
.shotframe img { display: block; width: 100%; height: auto; }
/* The capture is portrait -- 1038x1191 -- so at column width it ran 938px tall
   beside 302px of notes, and the right-hand column just stopped. Cropped from
   the top instead: a window into the product rather than the whole page
   reproduced, which is also what the chrome bar above it already implies. */
.showcase .shotframe img {
  max-height: clamp(340px, 34vw, 560px);
  object-fit: cover; object-position: top center;
}

/* The capture beside what it proves rather than above it. Run full width the
   screenshot is a very large picture of very small type -- nobody reads a
   10px axis label at 2,000px wide -- and the three notes were stranded
   underneath it. In a column it stays legible and the notes get somewhere to
   live. 1.5fr against 1fr: the image needs the width, the notes do not. */
.showcase {
  display: grid; gap: var(--col); align-items: start;
  grid-template-columns: minmax(0, 1.5fr) minmax(0, 1fr);
}
@media (max-width: 1100px) {
  .showcase { grid-template-columns: minmax(0, 1fr); }
}

/* A column now, not a row of three across the page. */
.showcase-notes {
  list-style: none; margin: 0; padding: 0;
  display: grid; gap: var(--grid);
  grid-template-columns: minmax(0, 1fr);
}
.showcase-notes li { display: flex; flex-direction: column; gap: 5px; }
.showcase-notes strong { font-size: var(--t-item); }
.showcase-notes span {
  font-size: var(--t-body); color: var(--muted); line-height: 1.55;
}

/* ---------- sources ---------- */

/* Two columns, not four. Ten short entries across four columns at 13px is a
   spreadsheet; across two at 16px it is a list you actually read, and ten
   items divide into two evenly. */
.sources { columns: 620px 2; column-gap: var(--col); }
.source {
  padding: 16px 0; border-bottom: 1px solid var(--rule);
  display: grid; gap: 4px; break-inside: avoid;
}
.source strong { font-size: var(--t-item); }
.source p {
  margin: 0; font-size: var(--t-body); line-height: 1.55; color: var(--muted);
}

/* ---------- the document list ---------- */

/* Fifteen documents over two columns rather than three. This is the section
   that answers "what do I actually get", so it is the last place on the page
   to be economical with type. */
/* CSS columns, not a grid: a grid fills row-wise, so a numbered index read
   01|02 across the page instead of 01..08 down the left. Columns flow the way
   the numbers do, and collapse to one on a narrow screen for free. */
.deliverables {
  list-style: none; margin: 0; padding: 0;
  columns: 520px 2; column-gap: var(--col);
}
.deliverables li {
  display: flex; gap: 16px; align-items: baseline;
  padding: 15px 0; border-bottom: 1px solid var(--rule);
  break-inside: avoid;
}
.d-n {
  flex: none; font: 600 12px var(--mono); color: var(--brand-ink);
  font-variant-numeric: tabular-nums;
}
.deliverables strong { font-size: var(--t-item); margin-right: 10px; }
.deliverables span {
  font-size: var(--t-body); color: var(--muted); line-height: 1.5;
}
.showcase-notes li { padding-top: 15px; border-top: 1px solid var(--rule); }

/* ---------- comparison ---------- */

.compare-h {
  margin: var(--band) 0 var(--lead); font-size: 15px; font-weight: 600;
  color: var(--muted);
}
table.compare { border-collapse: collapse; width: 100%; font-size: 13px; }
table.compare th, table.compare td {
  padding: 11px 14px; text-align: left; vertical-align: top;
  border-bottom: 1px solid var(--rule);
}
table.compare thead th {
  font: 600 13px var(--ui); border-bottom: 1px solid var(--rule-2);
}
table.compare tbody th {
  font: 500 12.5px var(--ui); color: var(--muted); white-space: nowrap;
}
table.compare td { color: var(--muted); }
/* The featured column is tinted the whole way down rather than only in its
   header, so the eye follows one column instead of hunting for the row. */
table.compare .on { background: var(--wash); }
table.compare thead .on { color: var(--brand-ink); }
table.compare .yes { color: var(--good); font-weight: 600; }

/* ---------- faq ---------- */

.faq {
  display: grid; gap: 0;
  border-top: 1px solid var(--rule);
}
.faq-item { border-bottom: 1px solid var(--rule); }
.faq-item summary {
  cursor: pointer; list-style: none; padding: 20px 40px 20px 0;
  font-size: var(--t-item); font-weight: 600; position: relative;
}
.faq-item summary::-webkit-details-marker { display: none; }
.faq-item summary::after {
  content: "+"; position: absolute; right: 6px; top: 17px;
  font: 400 18px/1 var(--mono); color: var(--faint);
}
.faq-item[open] summary::after { content: "\2212"; }
.faq-item summary:hover { color: var(--brand-ink); }
.faq-item p {
  margin: 0 0 22px; font-size: var(--t-body); line-height: 1.65;
  color: var(--muted); max-width: 72ch;
}

@media (max-width: 900px) {
  /* One column: the picture on top, the copy beneath it. Its height is capped
     here because in a single column it has no neighbour to take its height
     from, and a near-square image at full width would fill the screen before
     the headline got a look in. */
  .hero { grid-template-columns: minmax(0, 1fr); }
  .shot img { height: auto; max-height: clamp(220px, 40vw, 340px); }
}

@media (max-width: 720px) {
  /* main carries 30px of top padding for the product screens. On a phone the
     hero is meant to start where the bar stops, so on the public side it goes.
     The sign-in card sets its own generous top padding and does not miss it. */
  body.public main { padding-top: 0; }

  /* Full bleed. .landing is 92vw with its own side padding, so the hero has to
     climb back out of both: margin-left of (50% - 50vw) puts its left edge on
     the viewport edge whatever that padding happens to be, rather than
     guessing at a number that would go stale the next time it changed.

     No radius and no border either -- a corner needs something beside it to be
     a corner, and the bar above already draws the only line needed here. */
  .hero {
    margin-top: 0;
    width: 100vw; margin-left: calc(50% - 50vw);
    border: 0; border-radius: 0; box-shadow: none;
  }
  .hero-copy { padding: 30px 20px 34px; }
  .public-bar .public-tabs { display: none; }
}


/* ==========================================================================
   Movement.

   The page rendered fully formed and completely still, which reads as a
   document rather than a product. What follows is deliberately small: a rise
   of a few pixels and a fade, staggered so the eye is led down the hero
   instead of arriving everywhere at once, and one reveal per section as it
   comes into view.

   Two rules it holds to. Nothing moves more than 14px, because a landing page
   that slides things across the screen is a landing page you have to wait for.
   And every one of these is inside a reduced-motion guard -- with the
   preference set, the whole file below collapses to "everything is already
   visible", which is the correct end state rather than a degraded one.
   ========================================================================== */

@media (prefers-reduced-motion: no-preference) {

  /* --- the hero, on load --- */
  .hero-eyebrow, .hero h1, .hero-lead, .hero-actions, .hero-foot, .shot,
  .proof {
    opacity: 0;
    animation: rise .62s cubic-bezier(.22, .68, .36, 1) forwards;
  }
  .hero-eyebrow { animation-delay: .04s; }
  .hero h1      { animation-delay: .10s; }
  .hero-lead    { animation-delay: .18s; }
  .hero-actions { animation-delay: .26s; }
  .hero-foot    { animation-delay: .33s; }
  /* The shot comes from the right and a touch later: it is the payoff of the
     sentence above it, so it should arrive after the sentence has landed. */
  .shot         { animation-delay: .22s; animation-name: rise-right; }
  .proof        { animation-delay: .40s; }

  @keyframes rise {
    from { opacity: 0; transform: translateY(14px); }
    to   { opacity: 1; transform: none; }
  }
  @keyframes rise-right {
    from { opacity: 0; transform: translateY(10px) translateX(18px); }
    to   { opacity: 1; transform: none; }
  }

  /* --- everything below, on scroll ---
     `.reveal` is added by app.js to each band; `.seen` is added when it
     crosses into view. A band that never gets observed stays visible, so a
     failure of the observer is invisible rather than a blank page. */
  .reveal {
    opacity: 0; transform: translateY(16px);
    transition: opacity .55s ease, transform .55s cubic-bezier(.22,.68,.36,1);
  }
  .reveal.seen { opacity: 1; transform: none; }

  /* Children of a revealed band step in behind it, so a grid of ten sources
     arrives as a wave rather than a slab. */
  .reveal .source, .reveal .stat, .reveal .plan, .reveal .stages li,
  .reveal .expert-card {
    opacity: 0; transform: translateY(10px);
    transition: opacity .5s ease, transform .5s cubic-bezier(.22,.68,.36,1);
  }
  .reveal.seen .source, .reveal.seen .stat, .reveal.seen .plan,
  .reveal.seen .stages li, .reveal.seen .expert-card {
    opacity: 1; transform: none;
  }
  .reveal.seen > * > :nth-child(1) { transition-delay: .02s; }
  .reveal.seen > * > :nth-child(2) { transition-delay: .06s; }
  .reveal.seen > * > :nth-child(3) { transition-delay: .10s; }
  .reveal.seen > * > :nth-child(4) { transition-delay: .14s; }
  .reveal.seen > * > :nth-child(5) { transition-delay: .18s; }
  .reveal.seen > * > :nth-child(6) { transition-delay: .22s; }

  /* The one selected row in the product shot breathes, so the picture reads
     as something running rather than a still. Slow and low-contrast on
     purpose -- it is scenery, not a notification. */
  .shot-file.on { animation: shot-breathe 3.4s ease-in-out infinite; }
  @keyframes shot-breathe {
    0%, 100% { background: var(--wash); }
    50%      { background: var(--sunk); }
  }
}

/* ==========================================================================
   Depth behind the hero.

   One soft brand-coloured wash sitting under the product shot, built from
   --pulse (the brand as an rgb triple, already in the palette for the live-run
   dot). No new colour enters the file: it is the same red at four percent,
   which in dark mode is the difference between a card floating on the ground
   and a card lying on it.
   ========================================================================== */

/* Anchored to the page, not to the hero.

   It was on .hero::before, and .hero lives inside .landing -- which is capped
   at 1460px and centred. So on anything wider the wash stopped dead at the
   content column and drew a visible vertical edge exactly where it was
   supposed to be softest. A full-bleed decoration has to hang off a
   full-bleed element, and in public mode `main` is the one: it drops the
   product view's max-width, so `right: 0` here means the right edge of the
   window.

   Kept inside that box rather than hanging past it -- a decoration at
   negative offset widens the document and puts a scrollbar across the whole
   page, and because a pseudo-element never appears in querySelectorAll, the
   usual sweep for what is sticking out comes back empty. */
body.public main { position: relative; }
body.public main::before {
  content: ""; position: absolute; z-index: 0;
  top: 0; right: 0; width: min(64%, 1000px); aspect-ratio: 1;
  background: radial-gradient(circle at 64% 40%,
              rgba(var(--pulse), .10), rgba(var(--pulse), 0) 60%);
  pointer-events: none;
}
/* Content above the wash. Without this the hero sits underneath it and the
   headline picks up a faint red cast. */
body.public .landing { position: relative; z-index: 1; }

@media (max-width: 900px) {
  body.public main::before { width: 128%; }
}

/* ---------- who is signed in ---------- */

.who { position: relative; display: flex; align-items: center; font-size: 13px; }
.who img { width: 24px; height: 24px; border-radius: 50%; }
.who-btn {
  display: flex; align-items: center; gap: 9px; cursor: pointer;
  padding: 5px 9px 5px 5px; border: 1px solid transparent; border-radius: 999px;
  background: none; color: var(--ink); font: inherit; font-size: 13px;
}
.who-btn:hover { border-color: var(--rule); background: var(--paper); }
.who[data-open] .who-btn, .who-btn[aria-expanded="true"] {
  border-color: var(--rule-2); background: var(--paper);
}

/* Anchored to the account control and pinned to its right edge, so it opens
   inward from the corner rather than off the side of the window. */
.who-menu {
  position: absolute; top: calc(100% + 8px); right: 0; z-index: 40;
  min-width: 172px; padding: 5px;
  background: var(--paper); border: 1px solid var(--rule);
  border-radius: 10px; box-shadow: var(--shadow-modal);
  display: flex; flex-direction: column;
}
.who-menu a {
  padding: 9px 11px; border-radius: 7px;
  color: var(--ink); font-size: 13.5px; white-space: nowrap;
}
.who-menu a:hover { background: var(--sunk); text-decoration: none; }
/* The one destructive item, marked by colour rather than by position. */
.who-menu a#signout { color: var(--brand-ink); }
.who-menu a#signout:hover { background: var(--wash); }
.wallet { display: flex; gap: 6px; align-items: center; }
.wallet:hover { text-decoration: none; }

/* ---------- account ---------- */

.tiers-list { padding: 0; }
.tier-row {
  display: flex; gap: 20px; align-items: flex-start;
  padding: 18px 20px; border-bottom: 1px solid var(--rule);
}
.tier-row:last-child { border-bottom: 0; }
.tier-row h3 { margin: 0 0 4px; font-size: 15px; font-weight: 600; }
.tier-row p { margin: 0 0 4px; }
.tier-buy { display: flex; flex-direction: column; align-items: flex-end; gap: 7px;
  min-width: 130px; }
.price { font-weight: 600; font-variant-numeric: tabular-nums; }
.asked { margin-top: 8px; display: flex; flex-wrap: wrap; gap: 6px;
  align-items: center; font-size: 12.5px; color: var(--muted); }

/* ---------- the wizard ---------- */

#new-dialog { width: min(680px, 95vw); }
.wiz-head { display: flex; align-items: flex-start; gap: 16px; }
.wiz-head h2 { margin-bottom: 4px; }
.wiz-head p { margin: 0; }
.step-count {
  margin-left: auto; font: 600 11px/1 var(--mono); color: var(--faint);
  letter-spacing: .08em; white-space: nowrap; padding-top: 5px;
}

.stepper {
  display: flex; gap: 4px; list-style: none; margin: 2px 0 6px; padding: 0;
  overflow-x: auto;
}
.stepper li {
  flex: 1; min-width: 0; font: 600 10.5px/1.3 var(--ui); letter-spacing: .04em;
  text-transform: uppercase; color: var(--faint);
  padding-top: 8px; border-top: 2px solid var(--rule-2);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.stepper li.done { color: var(--muted); border-top-color: var(--ink); }
.stepper li.on { color: var(--ink); border-top-color: var(--brand); }

.step { display: flex; flex-direction: column; gap: 14px; }
dialog textarea {
  font: 14px/1.55 var(--ui); padding: 9px 11px; border-radius: 6px;
  border: 1px solid var(--rule-2); background: var(--raised); color: var(--ink);
  resize: vertical; min-height: 62px;
}
dialog textarea:focus { border-color: var(--ink); }
dialog textarea::placeholder { color: var(--faint); }

/* ---------- what you want out of it ---------- */

.chips { display: grid; grid-template-columns: 1fr 1fr; gap: 8px; }
.chip-btn {
  display: flex; flex-direction: column; gap: 3px; text-align: left;
  padding: 10px 12px; border: 1px solid var(--rule-2); border-radius: 7px;
  background: var(--raised); color: var(--ink); cursor: pointer;
  font: 400 12px/1.45 var(--ui);
}
.chip-btn strong { font-size: 13px; font-weight: 600; }
.chip-btn span { color: var(--muted); }
.chip-btn:hover { background: var(--sunk); }
.chip-btn.on {
  border-color: var(--ink); background: var(--ink); color: var(--on-ink);
  box-shadow: inset 0 0 0 1px var(--ink);
}
.chip-btn.on span { color: color-mix(in srgb, var(--on-ink) 74%, transparent); }
.note { margin: 0; font-size: 12.5px; color: var(--muted); }

/* ---------- depth ---------- */

.tiers { display: grid; grid-template-columns: 1fr 1fr; gap: 9px; }
.tier {
  text-align: left; padding: 12px 13px; border-radius: 7px; cursor: pointer;
  border: 1px solid var(--rule-2); background: var(--raised); color: var(--ink);
  font: 400 12px/1.45 var(--ui);
}
.tier:hover { background: var(--sunk); }
.tier.on { border-color: var(--ink); box-shadow: inset 0 0 0 1px var(--ink); }
.tier-top { display: flex; align-items: baseline; gap: 8px; }
.tier-top strong { font-size: 13.5px; font-weight: 600; }
.tier-top .price { margin-left: auto; font-size: 12px; }
.tier p { margin: 5px 0 6px; color: var(--muted); }
.tier-facts { font: 11.5px var(--mono); color: var(--faint); }

/* ---------- the last screen before it costs something ---------- */

.sum { margin: 0; display: flex; flex-direction: column; gap: 0; }
.sum-row {
  display: grid; grid-template-columns: 150px minmax(0, 1fr); gap: 14px;
  padding: 9px 0; border-bottom: 1px solid var(--rule);
}
.sum dt { font: 600 12px/1.5 var(--ui); color: var(--muted); }
.sum dd { margin: 0; font-size: 13.5px; overflow-wrap: anywhere; }
.sum dd ul { margin: 0; padding-left: 17px; }

.cost {
  display: flex; flex-direction: column; gap: 3px;
  margin-top: 14px; padding: 13px 15px; border-radius: 7px;
  background: var(--sunk);
}
.cost.good { background: var(--good-bg); color: var(--good); }
.cost.pay { background: var(--wash); color: var(--brand-ink); }
.cost strong { font-size: 14.5px; }

/* ---------- narrow ---------- */

@media (max-width: 780px) {
  .signin { padding-top: 4vh; }
  .chips, .tiers, .row { grid-template-columns: 1fr; }
  .sum-row { grid-template-columns: 1fr; gap: 2px; }
  .tier-row { flex-direction: column; }
  .tier-buy { align-items: flex-start; flex-direction: row; }
}

/* ---------- the bar on a phone ----------
   The model chip is diagnostic, not navigation, so it is the first thing to
   go. The account name follows: the avatar and the Account tab both still say
   who is signed in, and losing the New research button instead would be
   losing the only thing anyone came to press. */

@media (max-width: 720px) {
  .bar { gap: 10px; padding: 0 12px; }
  .bar .btn { white-space: nowrap; }
  .bar .model { display: none; }
  .who #who-name { display: none; }
  .who-btn { padding: 4px; }
  .wordmark { font-size: 11px; letter-spacing: .12em; }

  /* The underline is the width of the word, so horizontal padding here would
     draw a rule wider than what it underlines. Space them with the gap. */
  .tabs { gap: 14px; }
  .tabs a { padding: 6px 1px; font-size: 13px; }
}

@media (max-width: 480px) {
  /* The spacer used to be dropped here, along with the wordmark, to make room
     for New research. That button now lives on the markets page as a card, and
     both bars need their spacer: it is what holds the account controls against
     the right edge. */
  .wordmark img { width: 26px; height: 26px; }
  .public-bar .btn { padding: 6px 11px; font-size: 12.5px; }
}

/* ==========================================================================
   Charts.

   The page's own rule holds here too: the data is the only thing allowed to
   be loud. Gridlines are hairlines a step off the surface, axis text is muted,
   and the brand red never appears - it means "live" everywhere else on this
   page, and a red bar would read as an alarm rather than a number.

   The colours themselves live in charts.js, where they are applied to marks.
   Text never wears them: a value beside a bar is ink, and the coloured mark
   next to it carries the identity.
   ========================================================================== */

.viz-strip { display: flex; flex-direction: column; gap: 34px; }

.viz { margin: 0; }
.viz figcaption { margin-bottom: 14px; }
.viz h3 {
  margin: 0 0 3px; font-size: 16.5px; font-weight: 600; letter-spacing: -.012em;
}
.viz-q { margin: 0; font-size: 13.5px; color: var(--muted); max-width: 66ch; }

.viz-plot {
  padding: 4px 0 2px;
  /* A wide chart scrolls inside its own box rather than pushing the document
     sideways -- the same rule the evidence tables follow. */
  overflow-x: auto;
}
.viz-plot svg { display: block; min-width: 420px; }

.viz-note {
  margin: 10px 0 0; font-size: 12.5px; line-height: 1.6; color: var(--muted);
  max-width: 74ch;
}
.viz-note strong { color: var(--ink); font-weight: 600; }
.viz-note code {
  font: 11.5px var(--mono); background: var(--sunk); padding: 1px 5px;
  border-radius: 4px;
}

/* ---------- legend ---------- */

.viz-legend {
  display: flex; flex-wrap: wrap; gap: 6px 20px;
  list-style: none; margin: 12px 0 0; padding: 0;
  font-size: 12.5px;
}
.viz-legend li { display: flex; align-items: center; gap: 7px; }
.viz-legend .key {
  width: 11px; height: 11px; border-radius: 3px; flex: 0 0 auto;
}
.viz-legend strong { font-weight: 500; }
.viz-val { color: var(--faint); font-variant-numeric: tabular-nums; }

/* ---------- the numbers behind the picture ---------- */

.viz-numbers { margin-top: 12px; }
.viz-numbers summary {
  cursor: pointer; font-size: 12.5px; font-weight: 600; color: var(--muted);
  list-style: none; display: inline-flex; align-items: center; gap: 6px;
  padding: 4px 0;
}
.viz-numbers summary::-webkit-details-marker { display: none; }
.viz-numbers summary::before { content: "+"; font: 700 13px var(--mono); }
.viz-numbers[open] summary::before { content: "\2212"; }
.viz-numbers summary:hover { color: var(--ink); }
.viz-numbers .table-scroll { margin-top: 8px; }

/* ---------- hover ---------- */

.viz-seg, .viz-row, .viz-dot, .viz-col { cursor: default; }
.viz-seg:hover rect, .viz-row:hover rect { filter: brightness(1.07); }
.viz-dot:hover { r: 7; }

.viz-tip {
  position: fixed; z-index: 60; pointer-events: none;
  max-width: 280px; padding: 8px 11px; border-radius: 6px;
  background: var(--term); color: var(--term-ink);
  font: 12.5px/1.5 var(--ui);
  box-shadow: var(--shadow-tip);
}
.viz-tip .dim { color: var(--term-dim); }

/* ==========================================================================
   Buying more time
   ========================================================================== */

.focus-set .chips { grid-template-columns: 1fr 1fr; margin-bottom: 12px; }
.focus-other { margin-top: 2px; }

/* A range input is the honest control here: every position on it is a price
   the server already quoted, so there is no value to type that the page would
   have to price itself. */
#run-minutes {
  width: 100%; accent-color: var(--ink); margin: 6px 0 2px; height: 22px;
}
.minutes-out {
  font: 600 13px var(--ui); color: var(--ink);
  font-variant-numeric: tabular-nums;
}

#run-dialog .cost { margin-top: 6px; }

@media (max-width: 780px) {
  .focus-set .chips { grid-template-columns: 1fr; }
  .viz-strip { gap: 26px; }
}

/* ---------- the theme switch ---------- */

.theme-btn {
  display: inline-flex; align-items: center; justify-content: center;
  width: 30px; height: 30px; padding: 0; flex: 0 0 auto;
  border: 1px solid var(--rule-2); border-radius: 6px;
  background: var(--raised); color: var(--muted); cursor: pointer;
}
.theme-btn:hover { color: var(--ink); background: var(--sunk); }
.theme-btn svg { width: 15px; height: 15px; }
/* One button, three states, so only the glyph for the current one is drawn. */
.theme-btn [data-when] { display: none; }
:root:not([data-theme]) .theme-btn [data-when="system"],
:root[data-theme="light"] .theme-btn [data-when="light"],
:root[data-theme="dark"]  .theme-btn [data-when="dark"] { display: block; }

@media (max-width: 720px) { .bar .theme-btn { display: none; } }

/* ---------- the notification bell ---------- */
.notif { position: relative; display: flex; }
.bar-icon {
  position: relative; display: inline-flex; align-items: center; justify-content: center;
  width: 30px; height: 30px; padding: 0; flex: 0 0 auto;
  border: 1px solid var(--rule-2); border-radius: 6px;
  background: var(--raised); color: var(--muted); cursor: pointer;
}
.bar-icon:hover, .bar-icon[aria-expanded="true"] { color: var(--ink); background: var(--sunk); }
.bar-icon:focus-visible { outline: 2px solid var(--brand); outline-offset: 2px; }
.bar-icon svg { width: 15px; height: 15px; }
.notif-count {
  position: absolute; top: -7px; right: -8px; min-width: 17px; height: 17px;
  padding: 0 4px; border-radius: 999px; pointer-events: none;
  background: var(--brand); color: var(--on-ink); box-shadow: 0 0 0 2px var(--ground);
  font: 600 10px/17px var(--ui); text-align: center; font-variant-numeric: tabular-nums;
}
.notif-menu {
  position: absolute; top: calc(100% + 8px); right: -40px; z-index: 40;
  width: 360px; max-height: min(70vh, 520px); overflow-y: auto;
  background: var(--paper); border: 1px solid var(--rule);
  border-radius: 10px; box-shadow: var(--shadow-modal);
}
.notif-head {
  position: sticky; top: 0; z-index: 1;
  display: flex; align-items: center; justify-content: space-between; gap: 10px;
  padding: 12px 14px; font-size: 14px;
  background: var(--paper); border-bottom: 1px solid var(--rule);
}
.notif-list { list-style: none; margin: 0; padding: 0; }
.notif-list li + li { border-top: 1px solid var(--rule); }
.notif-list a {
  display: flex; flex-direction: column; align-items: flex-start; gap: 4px;
  padding: 11px 14px; color: inherit;
}
.notif-list a:hover { background: var(--row-hover); text-decoration: none; }
.notif-list strong { font-size: 13.5px; font-weight: 600; line-height: 1.35; color: var(--ink); }
.notif-empty { margin: 0; padding: 18px 14px; font-size: 13.5px; color: var(--muted); }
@media (max-width: 720px) {
  .notif { position: static; }
  .notif-menu {
    position: fixed; top: calc(var(--bar-h) + 6px); left: 12px; right: 12px; width: auto;
  }
}

.logotext{
  text-transform: uppercase !important;
}

/* The share dialog. `dialog form` supplies the padding and the column gap, so
   these rules only do the parts that are this dialog's own: the list of live
   links, and the two boxes that hold a URL. */
.share-live { display: flex; flex-direction: column; gap: 6px; }
#share-list {
  border: 1px solid var(--rule); border-radius: 6px;
  padding: 4px 12px; background: var(--raised);
}
#share-list > p { margin: 8px 0; }
#share-made { display: flex; flex-direction: column; gap: 10px; }

/* One share per row: who it went to, whether they opened it, and the way to
   take it back. The URL is a <code> rather than a link because clicking your
   own share link in your own session is never what you meant to do. */
.share-row {
  display: flex; align-items: center; gap: 10px;
  padding: 10px 0; border-bottom: 1px solid var(--rule);
}
.share-row:last-child { border-bottom: 0; }
.share-row code { font: 11px var(--mono); color: var(--muted); word-break: break-all; }
#share-link { font: 12px var(--mono); width: 100%; }

/* Somebody else's report on your dashboard. Marked rather than hidden: the
   difference between what you own and what you were shown is the difference
   between what you can spend money on and what you cannot, so it has to be
   visible before the click, not discovered after it. */
.card.shared { border-style: dashed; }
.shared-tag {
  font: 500 10px/1 var(--ui); letter-spacing: .06em; text-transform: uppercase;
  color: var(--muted); margin-bottom: 6px;
}
.shared-head { margin-top: 26px; }
.shared-head h2 { margin: 0; font-size: 17px; font-weight: 600; }

/* ---------- skeletons ----------
   Shapes that match what is coming, so the page does not jump when it lands.
   Built from --sunk and --rule rather than fixed greys, so they follow the
   theme without a second set of rules for dark. */
.skel-line {
  display: block; height: 11px; margin: 0 0 9px; border-radius: 4px;
  background: var(--sunk);
  position: relative; overflow: hidden;
}
.skel-line::after {
  content: ""; position: absolute; inset: 0;
  transform: translateX(-100%);
  background: linear-gradient(90deg,
    transparent, var(--rule) 45%, var(--rule) 55%, transparent);
  animation: skel-sweep 1.35s ease-in-out infinite;
}
@keyframes skel-sweep { to { transform: translateX(100%); } }

.page-head .skel-line:first-child { height: 21px; margin-bottom: 11px; }
.skel-gap { display: block; height: 13px; }
.skel-doc { padding: 4px 0 40px; }

.skel-card {
  min-height: 168px; justify-content: flex-start; gap: 9px;
  box-shadow: none; pointer-events: none;
}
.skel-card .skel-line:first-child { height: 15px; margin-bottom: 12px; }
.skel-chips { display: flex; gap: 5px; margin-bottom: 6px; }
.skel-chip {
  width: 54px; height: 18px; border-radius: 999px; background: var(--sunk);
}
.skel-toc { pointer-events: none; }
.skel-toc-item { padding: 7px 10px; }

/* A sweeping gradient is precisely the kind of decoration that makes some
   people ill. The shapes still communicate "not here yet" without it, so the
   animation goes and nothing else changes. */
@media (prefers-reduced-motion: reduce) {
  .skel-line::after { animation: none; display: none; }
}
