/* VarnaOps — shared styles (light theme, brand palette) */
:root {
  --bg: #ffffff; --surface: #ffffff; --canvas: #f5f6f8; --subtle: #f7f8fa;
  --text: #111827; --muted: #586072; --faint: #97a0ad; --line: #e6e8ec;
  --accent: #1d9e75; --accent-ink: #ffffff; --accent-soft: rgba(29,158,117,0.08);
  --red: #e24b4a; --ink-soft: #303846;
}
* { box-sizing: border-box; }
html { scroll-behavior: smooth; scrollbar-gutter: stable; }
body {
  margin: 0; background: var(--bg); color: var(--text);
  font-family: Inter, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
  line-height: 1.6; -webkit-font-smoothing: antialiased;
}
code { font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: 0.92em; }
.wrap { width: 100%; max-width: 860px; margin: 0 auto; padding: 0 28px; }
/* A table-led page (model weights) gets more room than reading prose does. */
.wrap.wrap-wide { max-width: 1040px; }
.sr-only { position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden; clip: rect(0,0,0,0); white-space: nowrap; border: 0; }

/* nav / brand lockup */
.nav { border-bottom: 1px solid var(--line); }
.nav-inner { position: relative; display: flex; align-items: center; gap: 20px; padding: 14px 0; }
/* Brand lockup — same measures as the reference/query report header
   (report.html.j2 .report-mark/.report-word): spiral at 76px height with its
   native aspect preserved, wordmark pulled up tight beneath it (−14px). */
/* compact, left-aligned lockup: small spiral to the left of the wordmark, 1px gap */
.brand { display: flex; flex-direction: row; align-items: center; gap: 1px; text-decoration: none; }
.mark { height: 42px; width: auto; display: block; }
.word { font-size: 1.35rem; font-weight: 700; letter-spacing: -0.02em; color: var(--text); line-height: 1; }
.word .ops { color: var(--accent); }

/* explicit top nav (replaces the hamburger dropdown) */
.nav-links { display: flex; flex-wrap: wrap; align-items: center; gap: 22px; margin-left: auto; }
.nav-links > a { font-family: Inter, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif; color: var(--muted); text-decoration: none; font-size: 0.86rem; font-weight: 500; }
.nav-links > a:hover { color: var(--text); }
.nav-links a.signin { padding: 8px 16px; border: 1px solid var(--line); border-radius: 9px; color: var(--text); }
.nav-links a.signin:hover { background: var(--subtle); }

/* THE APP'S OWN ENTRY. index.html IS the app, and from every other page the only way
   back used to be the brand mark. `Analyse` sits first among the site links on every
   page and is marked as the current page on index.html only, with aria-current so a
   screen reader is told the same thing the pill says.

   The pill reuses the tokens the rail's active step uses (layout-app.css:
   `.app-rail .stepper .step.active { background: var(--accent-soft); }`, and
   `var(--accent)` for the mark it carries), so the header and the stepper agree about
   what "you are here" looks like and no new colour enters the palette. It is tinted,
   not filled: a solid green button here would outrank Sign in, which is the one call
   to action in this bar.

   Written as a DESCENDANT of .nav-links, not a child, on purpose: below 1100px nav.js
   moves this link inside .nav-more, where it stops being a child and would lose the
   pill exactly where the reader has the least other context about which page they are
   on. The padding is the child rule's alone, because inside the menu the row's own
   9px/12px already gives the pill its shape. */
.nav-links a.nav-app[aria-current="page"] {
  background: var(--accent-soft);
  color: var(--accent);
  border-radius: 8px;
}
.nav-links > a.nav-app[aria-current="page"] { padding: 6px 10px; }

/* THE PARENT OF THE CURRENT PAGE. On methods.html the "Methods" row inside Reproducibility
   is marked .active, but the reader sees a closed menu whose trigger looked like every
   other link (owner, 2026-09-06). The trigger carries the same pill when any row inside it
   is the current page, so "you are here" reads at the level the bar shows. `:has()` keeps
   this in the stylesheet: no page edit, no script, and it holds after nav.js moves the
   menu into the phone's compact menu. */
.nav-links .nav-dd:has(.nav-dd-menu a.active) > summary {
  background: var(--accent-soft);
  color: var(--accent);
  border-radius: 8px;
}
.nav-links > .nav-dd:has(.nav-dd-menu a.active) > summary { padding: 6px 10px; }

/* EVERY current link gets the same pill (owner, 2026-09-06): a plain site link on its own
   page (Contact on contact.html) and the row inside a dropdown that is the current page,
   so the mark reads the same whether the destination is top-level or nested. */
.nav-links > a.active { background: var(--accent-soft); color: var(--accent); border-radius: 8px; padding: 6px 10px; }
.nav-dd-menu a.active { background: var(--accent-soft); color: var(--accent); }

/* buttons */
.cta { display: inline-block; padding: 11px 22px; border-radius: 9px; background: var(--accent); color: var(--accent-ink); font-weight: 600; text-decoration: none; transition: .18s ease; }
.cta:hover { background: #178b66; transform: translateY(-1px); box-shadow: 0 8px 20px rgba(29,158,117,0.22); }
.cta-sm { padding: 9px 16px; font-size: 0.9rem; }
.ghost { color: var(--muted); text-decoration: none; font-weight: 600; font-size: 0.95rem; }
.ghost:hover { color: var(--accent); }
.micro { color: var(--faint); font-size: 0.88rem; margin: 0; }

/* home hero */
.hero { padding: 64px 0 36px; }
.hero h1 { margin: 0 0 16px; font-size: clamp(2rem, 4.4vw, 3rem); line-height: 1.08; letter-spacing: -0.04em; max-width: 18ch; }
.hero .lead { margin: 0 0 28px; color: var(--muted); font-size: 1.15rem; max-width: 62ch; }
.hero-cta { display: flex; align-items: center; gap: 18px; flex-wrap: wrap; }

/* home cards */
.cards-sec { padding: 8px 0 80px; }
.cards { display: grid; grid-template-columns: repeat(3, 1fr); gap: 18px; }
.ucard { display: block; padding: 22px; border: 1px solid var(--line); border-radius: 14px; background: var(--surface); text-decoration: none; color: inherit; transition: .16s ease; box-shadow: 0 1px 2px rgba(17,24,39,0.04); }
.ucard:hover { border-color: #cfd3da; transform: translateY(-2px); box-shadow: 0 10px 30px rgba(17,24,39,0.07); }
.ucard h3 { margin: 0 0 8px; font-size: 1.08rem; }
.ucard p { margin: 0 0 14px; color: var(--muted); font-size: 0.94rem; }
.ucard .more { color: var(--accent); font-weight: 600; font-size: 0.9rem; }

/* generic page header + prose */
.page-head { padding: 52px 0 6px; }
.page-head h1 { margin: 0 0 10px; font-size: clamp(1.8rem, 3.6vw, 2.4rem); letter-spacing: -0.03em; }
.page-head .lead { margin: 0; color: var(--muted); font-size: 1.15rem; max-width: 70ch; }
.prose { padding: 26px 0 80px; }
.prose h2 { font-size: 1.3rem; margin: 38px 0 10px; letter-spacing: -0.02em; }
.prose h3 { font-size: 1.04rem; margin: 28px 0 8px; }
.prose p { color: var(--ink-soft); margin: 0 0 14px; max-width: 72ch; }
.prose ul { color: var(--ink-soft); padding-left: 20px; margin: 0 0 14px; max-width: 72ch; }
.prose .wrap > h2:first-child, .prose .wrap > h3:first-child { margin-top: 4px; }
.prose li { margin-bottom: 7px; }
.prose strong { color: var(--text); font-weight: 600; }
.prose a { color: var(--accent); }
.prose .pull { font-size: 1.4rem; line-height: 1.3; font-weight: 600; color: var(--text); letter-spacing: -0.02em; margin: 26px 0; }
.prose .pull .ops { color: var(--accent); }

/* note / callout */
.note { background: var(--accent-soft); border: 1px solid rgba(29,158,117,0.25); border-radius: 10px; padding: 14px 16px; color: #2f5d4f; font-size: 0.92rem; margin: 18px 0; }
.note.warn { background: #fff7ed; border-color: #fed7aa; color: #9a3412; }
.note strong { color: inherit; }
/* "In short" summary at the head of each legal page: a plain-language orientation box, built on
   .note so it needs no colours of its own. These three lines exist only because .prose ul and
   .micro are styled for body copy and look wrong at the note's smaller size. */
.note.in-short ul { color: inherit; margin: 8px 0 10px; padding-left: 20px; }
.note.in-short li { margin-bottom: 5px; }
.note.in-short .micro { color: inherit; opacity: 0.75; }

/* license / data tables */
.tbl-card { padding: 6px 4px; border: 1px solid var(--line); border-radius: 14px; background: var(--surface); overflow-x: auto; box-shadow: 0 1px 2px rgba(17,24,39,0.04); margin: 16px 0; }
.ltable { width: 100%; border-collapse: collapse; font-size: 0.92rem; }
.ltable th, .ltable td { text-align: left; padding: 11px 14px; border-bottom: 1px solid var(--line); vertical-align: top; }
.ltable thead th { color: var(--muted); font-weight: 600; font-size: 0.78rem; text-transform: uppercase; letter-spacing: 0.04em; }
.ltable tbody tr:last-child td { border-bottom: none; }
.ltable td.lic { white-space: nowrap; color: var(--ink-soft); font-weight: 500; }
.ltable td .role { color: var(--muted); }
/* Weights downloads (weights.html). Every artifact cell is filled by web/weights.js from
   the published index.json, so the checked-in state is "not yet published" and that is
   also what a reader sees while referenceReportBaseUrl is unset. The link reuses
   .ref-info's disabled rule below verbatim: solid, never faded, because the reason it is
   dark is ours rather than anything the reader can act on. */
.dl-table td { vertical-align: middle; }
.dl-table td.lic { white-space: normal; }   /* a build may ship under more than one */
/* The full 64-hex digest, not a prefix: it is here to be checked against the file. */
.dl-digest { font-size: 0.72rem; word-break: break-all; color: var(--muted); }
/* Weights and Report are two identical outline "Download" buttons (owner, 2026-09-03); the
   column header names the artifact. Both carry .ref-info, so they stay dark rather than
   faded while no origin is configured. */
.dl-table { font-size: 0.94rem; }
.dl-table th, .dl-table td { padding: 12px 14px; }
.dl-table .dl-link, .dl-table .rpt-link { padding: 7px 14px; font-size: 0.86rem; font-weight: 600; }
/* Verbatim licence text. Reproduced as-is, so it wraps but is never restyled into prose. */
.licence-text { white-space: pre-wrap; font-size: 0.8rem; line-height: 1.55; color: var(--ink-soft); background: var(--surface); border: 1px solid var(--line); border-radius: 12px; padding: 16px 18px; margin: 14px 0 26px; overflow-x: auto; }

/* ===== interactive walkthrough (Home / app) ===== */
.flow { padding: 30px 0 80px; }
.step-head { display: flex; align-items: center; gap: 12px; font-weight: 600; font-size: 1.08rem; margin: 40px 0 16px; }
.flow .wrap > .step-head:first-child { margin-top: 8px; }
.step-head .num { display: inline-flex; align-items: center; justify-content: center; width: 26px; height: 26px; border-radius: 50%; background: var(--accent-soft); color: var(--accent); font-size: 0.85rem; font-weight: 700; }

.organ-row { display: flex; gap: 10px; flex-wrap: wrap; margin-bottom: 18px; }
.organ { padding: 9px 16px; border: 1px solid #d6d9e0; border-radius: 999px; background: #e7e9ee; color: #3f4756; cursor: pointer; font-size: 0.92rem; font-weight: 600; font-family: inherit; transition: .15s; }
.organ:hover { border-color: #c2c6cf; background: #dfe2e8; color: var(--text); }
.organ.active { border-color: var(--accent); color: var(--accent); background: rgba(29,158,117,0.12); }
.organ .st { color: var(--faint); font-size: 0.74rem; font-weight: 500; margin-left: 7px; text-transform: uppercase; letter-spacing: 0.04em; }
.organ.active .st { color: var(--accent); }

.card { background: var(--surface); border: 1px solid var(--line); border-radius: 14px; padding: 18px; box-shadow: 0 1px 2px rgba(17,24,39,0.04), 0 10px 30px rgba(17,24,39,0.05); }
.preview.atlas { display: grid; grid-template-columns: 340px 1fr; gap: 26px; align-items: start; }
.pv-svg svg { width: 100%; height: auto; display: block; border-radius: 10px; background: var(--canvas); }
.pv-text h3 { margin: 0 0 8px; font-size: 1.08rem; }
.pv-text p { margin: 0; color: var(--muted); font-size: 0.96rem; }
.illus { display: inline-block; margin-top: 12px; color: var(--faint); font-size: 0.78rem; }

.legend-head { margin: 12px 0 8px; font-size: 0.85rem; color: var(--text); font-weight: 600; }
.legend { display: flex; flex-wrap: wrap; gap: 6px; }
.lchip { display: inline-flex; align-items: center; gap: 6px; padding: 3px 9px; border: 1px solid var(--line); border-radius: 999px; font-size: 0.77rem; color: var(--muted); background: var(--subtle); }
.lchip .ld { width: 7px; height: 7px; border-radius: 50%; flex: none; }

.compute { display: grid; grid-template-columns: 0.95fr 1.05fr; gap: 24px; align-items: start; }
.mod-list { list-style: none; margin: 0; padding: 0; border: 1px solid var(--line); border-radius: 14px; overflow: hidden; background: var(--surface); }
.mod-item { display: flex; align-items: center; gap: 13px; padding: 13px 16px; cursor: pointer; border-bottom: 1px solid var(--line); transition: .12s; }
.mod-item:last-child { border-bottom: none; }
.mod-item:hover { background: var(--subtle); }
.mod-item.active { background: var(--accent-soft); }
.mod-item .chk { width: 17px; height: 17px; border-radius: 5px; border: 1.5px solid #c4c9d2; background: #fff; flex: none; position: relative; transition: .12s; }
.mod-item .chk.on { border-color: var(--accent); background: var(--accent); }
.mod-item .chk.on::after { content: ""; position: absolute; left: 5px; top: 1.5px; width: 4px; height: 8.5px; border: solid #fff; border-width: 0 1.7px 1.7px 0; transform: rotate(45deg); }
.mod-item .mt { font-size: 0.95rem; color: var(--muted); }
.mod-item.active .mt { color: var(--text); font-weight: 600; }
.compute .pv-svg { margin-bottom: 14px; }

.drop { display: flex; align-items: center; justify-content: center; gap: 12px; border: 1.5px dashed #c9ced6; border-radius: 12px; padding: 18px 22px; background: var(--subtle); transition: .18s ease; }
.drop.drag { border-color: var(--accent); background: var(--accent-soft); }
.drop .upic { width: 22px; height: 22px; stroke: var(--accent); fill: none; stroke-width: 1.7; stroke-linecap: round; stroke-linejoin: round; flex: none; }
.drop .drop-txt { font-size: 0.96rem; color: var(--muted); }
.drop .drop-txt code { color: var(--accent); }
/* The press affordance the zone never had. `.drop` stays the drag target and stays
   clickable; this is what a first-time user actually aims at, and both now move on
   :active so a click is visibly registered. */
.dropbtn { font: inherit; font-size: 0.92rem; font-weight: 600; color: #fff; background: var(--accent); border: 0; border-radius: 8px; padding: 8px 16px; cursor: pointer; transition: .12s ease; }
.dropbtn:hover { filter: brightness(1.08); }
.dropbtn:active { transform: translateY(1px); filter: brightness(0.94); }
.dropbtn:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.drop { cursor: pointer; }
/* WITHOUT THIS, `drop.hidden = true` DOES NOTHING. The `hidden` attribute works through a
   UA rule, `[hidden] { display: none }`, and any class rule that sets `display` outranks it —
   so `.drop { display: flex }` above kept the drop zone on screen beside the loaded file for
   two days while the JS that hides it read as correct. Any element this page hides through
   `.hidden` and styles with a `display` rule needs its own [hidden] rule;
   tests/test_hidden_actually_hides.py derives the list rather than trusting this comment. */
.drop[hidden] { display: none; }
.drop:active { border-color: var(--accent); background: var(--accent-soft); }
/* Upload progress. Width is set from XHR progress events, so it moves only when bytes
   actually move; an indeterminate animation would claim progress we cannot observe. */
/* Column explorer: the values and level count of the selected column, under its picker. */
.col-preview { margin: 2px 0 0; font-size: 0.8rem; color: var(--faint); overflow-wrap: anywhere; }
/* What is lost by leaving a role unset. NOT muted: it is the one line that changes which
   analyses run, and a customer who set a column to none needs to read it. */
.col-consequence { margin: 4px 0 0; padding: 7px 10px; border-radius: 8px; border: 1px solid #f0d8a8; background: #fdf6e6; color: #7a5a12; font-size: 0.82rem; line-height: 1.45; }
.confirm-updated { color: #166534; font-weight: 600; }
/* The loaded file, which REPLACES the drop zone (#6). One state, two views. */
.loaded-file { display: flex; align-items: center; gap: 12px; border: 1.5px solid #c9ced6; border-radius: 12px; padding: 14px 16px; background: var(--subtle); }
.loaded-file .upic { width: 22px; height: 22px; stroke: var(--accent); fill: none; stroke-width: 1.7; stroke-linecap: round; stroke-linejoin: round; flex: none; }
.loaded-name { flex: 1; min-width: 0; font-size: 0.96rem; overflow-wrap: anywhere; }
.loaded-state { display: block; font-size: 0.8rem; color: var(--faint); }
.loaded-acts { display: flex; gap: 8px; flex: none; }
/* Earlier uploads (#8): inside step 3, never in the run step. Visible enough to be found
   (#3) — a faint summary line hid the one control that saves a second 100 MB upload — but
   still a disclosure, so it yields to the file the customer is actually working on.

   ONE SHELL FOR BOTH DISCLOSURE BOXES (owner, 2026-09-08). .prev-uploads and .cite-box are
   the same idea -- a closed box that opens to content -- and had two near-identical rules
   plus the browser's default triangle, a marker nothing else on this site uses. The shell
   is .card's border, radius and padding, copied rather than invented, so the boxes sit in
   the page's existing visual language. The chevron is drawn here because hiding the default
   marker takes both `list-style` (Firefox) and `::-webkit-details-marker` (Safari), and a
   summary with neither a marker nor a replacement does not read as clickable at all. */
.prev-uploads, .cite-box { border: 1px solid var(--line); border-radius: 14px; padding: 18px; background: var(--surface); }
.prev-uploads { margin-top: 14px; }
.prev-uploads > summary, .cite-box > summary {
  display: flex; align-items: center; justify-content: space-between; gap: 10px;
  cursor: pointer; font-size: 0.9rem; color: var(--text); font-weight: 600; list-style: none; }
.prev-uploads > summary::-webkit-details-marker, .cite-box > summary::-webkit-details-marker { display: none; }
.prev-uploads > summary::after, .cite-box > summary::after {
  content: "▾"; flex: none; color: var(--muted); font-size: 0.8rem; line-height: 1;
  transition: transform .15s ease; }
.prev-uploads[open] > summary::after, .cite-box[open] > summary::after { transform: rotate(180deg); }
.prev-uploads[open] > summary { margin-bottom: 4px; }
/* A row's own credits, from the quote. Muted and small: it is information, not the headline,
   and the headline is the total. */
.cr-tag { font-size: 0.78rem; color: var(--muted); background: var(--subtle); border: 1px solid var(--line); border-radius: 6px; padding: 1px 6px; white-space: nowrap; }
/* Fixed lines sit under the analyses, separated, so the floor reads as the floor. */
.cr-tag--shared { font-style: italic; }
/* Inherits the list's size like every other row — it was a THIRD size in the same list,
   found by the guard rather than by looking. */
.cr-item--note { color: #7a5a12; background: #fdf6e6; border: 1px solid #f0d8a8; border-radius: 8px; padding: 6px 10px; margin-bottom: 6px; }
.cr-item--fixed { color: var(--muted); }
/* ONE SIZE FOR THE LIST. Depth is indent and colour, never size — a nested row was
   rendering LARGER than the row it nests under (0.88 inside 0.86), which is the part that
   read as broken rather than merely inconsistent. */
/* Indent + a left rule: the indent alone was easy to miss at this size, and the rule
   makes "belongs to the row above" visible without another font size. */
.cr-item--sub { padding-left: 20px; color: var(--muted); }
/* MORE SPECIFIC THAN `.credits-bar .cr-note`, which sets 0.82rem and sits later in this
   file — the list lives inside a .credits-bar, so an equal-specificity rule would lose the
   tie and the marker would stay a third size. */
.cr-items .cr-item .cr-note { font-size: inherit; }
/* `.cr-items` prefixed, so this ties the winner and sits later. Without it (0,1,1) against
   `.cr-items .cr-item > span` (0,2,1)) the struck row resolved to bold and full contrast —
   the analysis that is NOT running ended up the most emphatic row in the list, with the
   strikethrough surviving only because nothing competes for text-decoration. */
.cr-items .cr-item.cr-item--omitted span { text-decoration: line-through; text-decoration-thickness: 1px; color: var(--faint); }
.cr-item--omitted .cr-note { text-decoration: none; }
/* No font-size of its own: it is the same paragraph as .run-hint (0.84rem) and differed
   from it by 0.01rem, which is an accident rather than a distinction. */
.run-hint--omitted { margin: 8px 0 0; padding: 8px 12px; border-radius: 8px; border: 1px solid #f0d8a8; background: #fdf6e6; color: #7a5a12; line-height: 1.5; }
.cr-item--fixed + .cr-item--total, .cr-item--fixed:first-of-type { border-top: 1px solid var(--line); }
/* Chosen, and cannot run. The row stays where the customer put it, struck through, with
   the reason on it — a warning elsewhere on the page names the analysis but not the row,
   and the row is where the remedy is. */
.mod-item.cannot-run .mt, .mod-extra-toggle.cannot-run { text-decoration: line-through; text-decoration-thickness: 1px; color: var(--faint); }
.cannot-run-why { margin-left: 6px; font-size: 0.78rem; color: #7a5a12; background: #fdf6e6; border: 1px solid #f0d8a8; border-radius: 6px; padding: 1px 6px; text-decoration: none; display: inline-block; }
.resume-exp { color: var(--muted); }
.resume-exp--gone { color: var(--red); font-weight: 600; }
.dropbtn--ghost { background: transparent; color: var(--accent); border: 1px solid var(--accent); }
/* TWO LINES ON THE LEFT, ACTIONS ON THE RIGHT (owner, 2026-09-08). The name, the age and
   the expiry used to be three spans inside one line, which on a phone wrapped into a
   tangle and pushed Delete off the right edge (guarded in tests/test_phone_layout.py). The
   row never wraps now: the left column is allowed to shrink (`min-width: 0`) and to break a
   long filename mid-word, so the actions keep their place at any width. */
.prev-uploads .resume-row { display: flex; flex-wrap: nowrap; align-items: center; justify-content: space-between; gap: 12px; padding: 10px 0; font-size: 0.9rem; border-top: 1px solid var(--line); }
.prev-uploads .resume-row:first-of-type { border-top: none; }
.resume-main { min-width: 0; overflow-wrap: anywhere; }
.resume-name { display: block; font-weight: 600; color: var(--text); }
.resume-meta { display: block; margin-top: 2px; font-size: 0.78rem; color: var(--muted); }
/* Text buttons, not ghost buttons: two bordered pills per row, repeated down the list, read
   as the loudest thing in a box that is a footnote to the drop zone above it. */
.resume-acts { display: flex; align-items: center; gap: 12px; flex: none; }
.resume-act { background: none; border: 0; padding: 0; font: inherit; font-size: 0.85rem; font-weight: 600; color: var(--accent); cursor: pointer; }
.resume-act:hover { text-decoration: underline; }
.resume-del { color: var(--muted); }
/* The bar and its Cancel on one line. The row is `display: flex`, so both children keep
   their own `hidden` behaviour (`.upbar` has no display rule of its own and `.pbtn[hidden]`
   is already declared below) and an empty row collapses to nothing. */
.uprow { display: flex; align-items: center; gap: 12px; }
.uprow .upbar { flex: 1 1 auto; }
.upcancel { flex: none; font-size: 0.85rem; padding: 4px 8px; margin-top: 10px; }
.upbar { height: 6px; margin-top: 10px; border-radius: 999px; background: var(--subtle); overflow: hidden; }
.upbar i { display: block; height: 100%; width: 0; border-radius: 999px; background: var(--accent); transition: width .15s linear; }
/* INDETERMINATE, for work whose progress cannot be measured: /inspect is one request, and
   the alternative is a page that looks idle while it reads the customer's columns. The
   animation deliberately shows no percentage — it says "working", never "this far done". */
.upbar--busy i { width: 35%; transition: none; animation: upbar-slide 1.1s ease-in-out infinite; }
@keyframes upbar-slide { 0% { margin-left: -35%; } 100% { margin-left: 100%; } }
@media (prefers-reduced-motion: reduce) {
  /* A static three-quarter bar rather than a moving one: still says "busy", moves nobody who
     asked not to be moved. */
  .upbar--busy i { animation: none; width: 75%; opacity: 0.55; }
}
/* One line under the drop panel (owner, 2026-09-08). The gap lives on the CONTAINER, not as
   a bottom margin on each line: the two status lines above it are hidden most of the time,
   and a per-paragraph margin left a stray gap under the last (and usually only) line. The
   .drop-privacy rules went with the isolation sentence they styled. */
.drop-notes { margin-top: 10px; display: grid; gap: 6px; }
.drop-formats { margin: 0; font-size: 0.82rem; color: var(--faint); }
.drop-formats code { color: var(--muted); font-size: 0.9em; }
/* bring-your-own labels (optional, step 3) */
.byo { margin-top: 14px; padding-top: 14px; border-top: 1px solid var(--line); }
.byo-toggle { display: inline-flex; align-items: center; gap: 8px; font-size: 0.92rem; color: var(--text); cursor: pointer; }
.byo-toggle input, .byo-check input { width: 15px; height: 15px; accent-color: var(--accent); cursor: pointer; flex: none; }
.byo-fields { margin-top: 12px; padding-left: 23px; }
.byo-field { display: flex; align-items: center; gap: 10px; font-size: 0.9rem; color: var(--muted); }
.byo-field code { color: var(--accent); }
.byo-field input { flex: 1; max-width: 240px; padding: 8px 11px; border: 1px solid var(--line); border-radius: 8px; font: inherit; font-size: 0.9rem; color: var(--text); background: var(--surface); }
.byo-field input:focus { outline: none; border-color: var(--accent); box-shadow: 0 0 0 3px var(--accent-soft); }
.byo-help { margin: 8px 0 12px; font-size: 0.82rem; color: var(--faint); }
.byo-check { display: inline-flex; align-items: center; gap: 8px; font-size: 0.9rem; color: var(--text); cursor: pointer; }
.byo-check.disabled { color: var(--faint); cursor: not-allowed; }
.byo-sub { color: var(--faint); font-size: 0.86em; }

/* Terms acceptance on login.html (2026-09-02). Hidden by a CLASS and shown by one:
   the `hidden` attribute loses to any display rule, so a box hidden that way would sit
   on screen while a test asserting `.hidden` passed. Same shape as .byo-check above. */
.terms-gate { display: none; }
.terms-gate.show { display: block; }
.terms-check { display: flex; align-items: flex-start; gap: 9px; font-size: 0.9rem; color: var(--text); cursor: pointer; line-height: 1.45; }
.terms-check input { margin-top: 3px; }
.closing { display: flex; align-items: center; gap: 16px; margin-top: 16px; flex-wrap: wrap; }

/* footer */
footer { border-top: 1px solid var(--line); padding: 26px 0; color: var(--faint); font-size: 0.85rem; }
.foot-inner { display: flex; justify-content: space-between; gap: 16px; flex-wrap: wrap; align-items: center; }
footer a { color: var(--muted); text-decoration: none; }
footer a:hover { color: var(--accent); }
.foot-links { display: inline-flex; gap: 8px; flex-wrap: wrap; }
/* research-use-only compliance callout (terms page + anywhere the boundary is stated) */
.ruo-callout { margin: 8px 0 26px; padding: 16px 18px; border: 1px solid var(--line); border-left: 3px solid var(--accent); border-radius: 10px; background: var(--subtle); font-size: 0.95rem; line-height: 1.55; color: var(--ink-soft); }
.ruo-callout strong { color: var(--text); }

/* methods page */
.toc { border: 1px solid var(--line); border-radius: 14px; background: var(--subtle); padding: 16px 20px; margin: 10px 0 30px; }
.toc-h { font-size: 0.78rem; text-transform: uppercase; letter-spacing: 0.05em; color: var(--muted); font-weight: 600; margin-bottom: 10px; }
.toc ol { margin: 0; padding-left: 22px; columns: 2; column-gap: 34px; }
.toc li { margin: 0 0 7px; break-inside: avoid; }
.toc a { color: var(--ink-soft); text-decoration: none; }
.toc a:hover { color: var(--accent); }
.prose h2 { scroll-margin-top: 24px; }
.prose h2 .secnum { color: var(--accent); font-variant-numeric: tabular-nums; margin-right: 12px; }
.formula { background: var(--subtle); border: 1px solid var(--line); border-left: 3px solid var(--accent); border-radius: 8px; padding: 10px 18px; margin: 16px 0; color: var(--text); overflow-x: auto; overflow-y: hidden; }
.formula .katex-display { margin: 10px 0; }
.formula .fn { color: var(--muted); font-size: 0.86rem; margin-top: 4px; text-align: left; line-height: 1.5; }
.figure { margin: 22px 0; }
.fig-ph { border: 1px dashed #c9ced6; border-radius: 12px; background: var(--subtle); height: 210px; display: flex; align-items: center; justify-content: center; color: var(--faint); font-size: 0.85rem; text-align: center; padding: 0 20px; }
.fig-cap { margin: 9px 2px 0; font-size: 0.85rem; color: var(--muted); line-height: 1.5; }
.fig-cap b { color: var(--text); font-weight: 600; }
.refs { font-size: 0.88rem; color: var(--muted); padding-left: 26px; }
.refs li { margin-bottom: 8px; }

/* nav dropdown (native <details>, no JS required to open) */
.nav-dd { position: relative; }
.nav-dd > summary { font-family: Inter, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif; list-style: none; cursor: pointer; color: var(--muted); font-size: 0.86rem; font-weight: 500; display: flex; align-items: center; justify-content: space-between; gap: 8px; }
.nav-dd > summary::-webkit-details-marker { display: none; }
.nav-dd > summary::after { content: ""; width: 6px; height: 6px; border-right: 1.5px solid currentColor; border-bottom: 1.5px solid currentColor; transform: rotate(45deg) translateY(-1px); opacity: 0.55; transition: transform .15s ease; flex: none; }
.nav-dd[open] > summary::after { transform: rotate(225deg) translateY(-1px); }
.nav-dd > summary:hover { color: var(--text); }
.nav-dd-menu { position: absolute; top: calc(100% + 12px); left: 50%; transform: translateX(-50%); display: flex; flex-direction: column; min-width: 186px; padding: 6px; background: var(--surface); border: 1px solid var(--line); border-radius: 12px; box-shadow: 0 10px 32px rgba(17,24,39,0.13); z-index: 60; }
.nav-dd-menu a { padding: 9px 12px; border-radius: 8px; color: var(--muted); font-size: 0.92rem; text-decoration: none; white-space: nowrap; }
.nav-dd-menu a:hover { background: var(--subtle); color: var(--text); }
.nav-dd-menu a.active { color: var(--accent); }

/* ---- the account menu ------------------------------------------------------
   TWO CONTROLS IN THE MARKUP, ONE PAINTED. session.js stamps data-auth on <html> in
   <head>, so the first frame already shows the right one. Building the menu in JS after
   the nav paints would swap "Sign in" for a whole panel a frame later, on every page.
   Chosen with display, not [hidden]: a `hidden` attribute loses to any display rule,
   including the one three lines below, so the element would stay visible while the
   attribute said otherwise. */
.nav-acct { display: none; }
[data-auth="in"] .nav-acct, [data-auth="blocked"] .nav-acct { display: block; }
[data-auth="in"] .nav-links a.signin, [data-auth="blocked"] .nav-links a.signin { display: none; }
/* THE TRIGGER IS AN AVATAR, NOT THE WORD "Account" (owner ruling, 2026-08-29): a button
   labelled Account opening a menu whose first item said Account was the control naming
   itself twice. The circle is sized HERE and never by its contents — nav.js swaps a
   neutral glyph for the reader's initial after the first paint, and a trigger that
   resized when that landed would be the flash this design exists to avoid, arriving by a
   different door. Same reason the email is not in the trigger: it is in the menu header,
   which nobody has to look at until they open it. */
.nav-acct > summary { padding: 3px 8px 3px 3px; border: 1px solid var(--line); border-radius: 999px; color: var(--text); gap: 7px; }
.nav-acct > summary:hover { background: var(--subtle); }
.nav-acct > summary:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.acct-avatar { flex: none; display: inline-flex; align-items: center; justify-content: center; width: 26px; height: 26px; border-radius: 999px; background: var(--accent); color: var(--accent-ink); font-size: 0.8rem; font-weight: 600; line-height: 1; }
.acct-avatar svg { width: 15px; height: 15px; }
/* Once the initial is in, the glyph's stroke is gone and only the letter remains — the
   class exists so this rule can drop the optical padding the glyph needed. */
.acct-avatar--filled { letter-spacing: 0.02em; }
/* A FIXED 248px (owner ruling, 2026-08-29), which is the third width this box has had and
   the reason matters more than the number:
     268px  — the panel era, when it held an email, a balance and a free/paid split.
     max-content (2026-08-28) — after the figures left for account.html, because three
       short words in a 268px box reads as a panel that lost its contents, which is
       exactly what had happened. The 186px floor .nav-dd-menu sets for the site's other
       dropdowns was cleared rather than inherited, or the box could not shrink at all.
     248px (now) — the menu has a header carrying the email again, so max-content means
       "as wide as this reader's address", and the menu changes width per person and per
       row. A SaaS account menu is a fixed panel; the email truncates inside it instead.
   The min-width: 0 stays for the same reason it arrived: without it the 186px floor is
   still in force, and it is a floor this box should never consult. The viewport cap is
   what keeps the blocked-account paragraph on screen on a narrow layout, where the panel
   is right-aligned under a control already at the edge of the nav. */
.nav-dd-menu--acct { left: auto; right: 0; transform: none; width: 248px; min-width: 0;
  max-width: calc(100vw - 24px); padding: 6px; gap: 0; border-radius: 10px;
  box-shadow: 0 8px 24px rgba(0,0,0,.08); }
/* The header is not a control and must not look like one: no hover, no pointer. The
   address truncates rather than wrapping — a two-line email would push the whole menu's
   rows down by a line for some readers and not others. */
.acct-hd { padding: 8px 10px 9px; }
.acct-hd-name { font-size: 0.9rem; font-weight: 500; color: var(--text); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.acct-hd-note { margin: 3px 0 0; font-size: 0.78rem; color: var(--muted); }
.acct-sep { height: 1px; margin: 5px 4px; background: var(--line); }
/* The rows. `.nav-dd-menu a` (0,1,1) sets padding, colour and font-size for the site's
   other dropdowns, so these have to be written under the --acct parent to outrank it. */
.nav-dd-menu--acct .acct-item { display: flex; align-items: center; gap: 10px; width: 100%; height: 36px; padding: 0 10px; border: 0; border-radius: 6px; background: none; font: inherit; font-size: 0.92rem; color: var(--text); text-align: left; text-decoration: none; white-space: nowrap; cursor: pointer; }
.nav-dd-menu--acct .acct-item:hover { background: var(--subtle); color: var(--text); }
.nav-dd-menu--acct .acct-item:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }
.nav-dd-menu--acct .acct-item span { overflow: hidden; text-overflow: ellipsis; }
.acct-ico { flex: none; width: 16px; height: 16px; color: var(--faint); }
.nav-dd-menu--acct .acct-item:hover .acct-ico { color: var(--muted); }
/* Sign out is a row, in the row's own colours (owner ruling, 2026-08-29). It was a grey
   full-width button, which read as the menu's call to action; it is not a destructive
   action either, so it is not red. Written out rather than left to .acct-item so that
   restyling it takes a deliberate edit here. */
.nav-dd-menu--acct .acct-signout { color: var(--text); font-weight: 400; }
/* Rendered on the account page too, so they keep rules of their own. */
.acct-who { padding: 12px 12px 10px; }
.acct-label { display: block; font-size: 0.74rem; text-transform: uppercase; letter-spacing: 0.04em; color: var(--muted); margin-bottom: 3px; }
.acct-who b { font-size: 0.92rem; word-break: break-all; }
.acct-row { display: flex; align-items: baseline; justify-content: space-between; gap: 12px; font-size: 0.9rem; }
.acct-row + .acct-row { margin-top: 6px; }
.acct-row span { color: var(--muted); }
.acct-sub, .acct-note { margin: 5px 0 0; font-size: 0.79rem; line-height: 1.45; color: var(--muted); }
.acct-pending { color: var(--faint); font-weight: 500; }
/* The blocked-account sentence is the one thing in this menu that is a paragraph, so it
   wraps where every row truncates. */
.nav-dd-menu--acct .acct-note { margin: 0; padding: 2px 10px 6px; white-space: normal; }
.nav-dd-menu--acct .acct-note a { display: inline; padding: 0; font-size: inherit; color: var(--accent); text-decoration: underline; white-space: normal; }
.nav-dd-menu--acct .acct-note a:hover { background: none; }

/* auto-numbered methods sections (CSS counter — survives reordering) */
.prose.numbered { counter-reset: sec; }
.prose.numbered h2 { counter-increment: sec; }
.prose.numbered h2::before { content: counter(sec); color: var(--accent); font-variant-numeric: tabular-nums; margin-right: 12px; }

/* ===== home intro (above the app box) ===== */
.alpha-pill { display: inline-flex; align-items: center; gap: 7px; padding: 4px 12px; border-radius: 999px; background: var(--accent-soft); border: 1px solid rgba(29,158,117,0.28); color: #157a5a; font-size: 0.78rem; font-weight: 600; letter-spacing: 0.02em; text-transform: uppercase; }
.alpha-pill::before { content: ""; width: 6px; height: 6px; border-radius: 50%; background: var(--accent); }

/* ===== home trust strip (below the app box) ===== */
.home-trust { padding: 4px 0 64px; }
.home-trust p { margin: 0 0 14px; color: var(--muted); font-size: 0.96rem; max-width: 68ch; }
.ht-links { display: flex; flex-wrap: wrap; gap: 8px 22px; }
.ht-links a { color: var(--accent); font-weight: 600; font-size: 0.92rem; text-decoration: none; }
.ht-links a:hover { text-decoration: underline; }

/* "Explore context": same solid-green .pbtn.primary as Next (a lighter tint tried first was
   still easy to miss next to it). .ref-info only adds what .primary doesn't already cover. */
.ref-info { text-decoration: none; white-space: nowrap; }
/* Deliberately NOT faded while disabled, unlike .pbtn[disabled] elsewhere. This button is
   disabled for a reason the user cannot act on (the reference-report host isn't deployed
   yet), not because of anything they did or a state they can change, so dimming it just
   makes it hard to see while telling them nothing. It keeps .primary's exact green; the
   "Coming soon" tooltip and the dead click carry the state instead. Revert to the usual
   opacity: 0.5 if it ever becomes disabled by user-controllable state. */
.ref-info[aria-disabled="true"] { cursor: default; pointer-events: none; }

/* ===== example-report CTA (between the lead and the stepper, left-anchored) ===== */
.app-cta { display: flex; flex-wrap: wrap; gap: 12px; justify-content: flex-start; margin-top: 22px; }
.scta-primary { display: inline-flex; align-items: center; padding: 11px 20px; border-radius: 10px; background: var(--accent); color: var(--accent-ink); font-weight: 600; font-size: 0.96rem; text-decoration: none; transition: filter .12s; }
.scta-primary:hover { filter: brightness(1.06); }
.scta-secondary { display: inline-flex; align-items: center; padding: 11px 20px; border-radius: 10px; background: transparent; color: var(--text); border: 1px solid var(--line); font-weight: 600; font-size: 0.96rem; text-decoration: none; transition: border-color .12s, color .12s; }
.scta-secondary:hover { border-color: var(--accent); color: var(--accent); }

/* step 4 results block (report + downloadable bundle) */
.results { margin-top: 16px; }
.results-head { display: flex; align-items: flex-start; gap: 12px; }
.results-check { flex: none; width: 26px; height: 26px; border-radius: 50%; background: var(--accent); color: #fff; display: inline-flex; align-items: center; justify-content: center; font-size: 0.9rem; }
.results-head strong { display: block; margin-bottom: 2px; }
.results-actions { display: flex; flex-wrap: wrap; gap: 12px; margin-top: 14px; }
.results-bundle { margin: 12px 0 0; font-size: 0.85rem; }

/* contact page — prominent email link */
.contact-email { margin: 2px 0 0; font-size: 1.05rem; font-weight: 400; }
.contact-email a { color: var(--accent); text-decoration: none; }
.contact-email a:hover { text-decoration: underline; }

/* ===== home app box (stepper) ===== */
.app { padding: 20px 0 44px; }
.app-box { border: 1px solid var(--line); border-radius: 18px; background: var(--surface); box-shadow: 0 1px 2px rgba(17,24,39,0.04), 0 18px 50px rgba(17,24,39,0.06); overflow: hidden; }
/* app-box title bar (title left, actions right) */
.app-head { display: flex; align-items: center; justify-content: space-between; gap: 16px; flex-wrap: wrap; padding: 15px 22px; background: var(--subtle); border-bottom: 1px solid var(--line); }
.app-head h2 { margin: 0; font-size: 1.02rem; font-weight: 600; color: var(--text); }
.app-head .app-cta { margin: 0; }
.app-head .scta-primary, .app-head .scta-secondary { padding: 8px 14px; font-size: 0.86rem; }

/* app-box header as underline tabs (SaaS tab-bar look) */
.stepper { display: flex; align-items: stretch; gap: 26px; padding: 0 22px; border-bottom: 1px solid var(--line); background: var(--surface); overflow-x: auto; overflow-y: hidden; }
.stepper .step { position: relative; display: inline-flex; align-items: center; flex: none; padding: 14px 2px; margin-bottom: -1px; border: 0; border-bottom: 2px solid transparent; background: transparent; cursor: pointer; color: var(--muted); font-family: inherit; font-size: 0.9rem; white-space: nowrap; transition: color .12s, border-color .12s; }
.stepper .step:hover { color: var(--text); }
.stepper .step.done { color: var(--text); }
.stepper .step.active { color: var(--accent); font-weight: 600; border-bottom-color: var(--accent); }
.stepper .sdot { display: none; }
.sconn { display: none; }
.slabel { white-space: nowrap; }

.panels { padding: 20px 22px; }
.panel { display: none; }
.panel.show { display: block; }
/* Pre-paint step restore (#4). boot.js stamps the saved step on <html> before the body is
   parsed; these rules put the right panel and the right stepper chips on the FIRST paint,
   so an F5 on step 2/3/4 no longer flashes step 1. walkthrough.js removes the attribute as
   soon as it takes over, and `.show` is the only mechanism from then on — two mechanisms
   both live would fight the moment the customer clicked a step.
   Enumerated per step because CSS cannot compare an attribute to an index;
   tests/test_step_restore_and_copy.py fails if index.html ever grows a panel these rules do not
   cover, which would otherwise hide the page. */
/* The pre-rendered frame is the How it works landing view, so the pre-paint attribute has
   to put it away for EVERY step, including 0. One rule on the attribute's presence rather
   than four enumerated ones: there is exactly one landing view and it is never a step. */
html[data-step] .panel[data-p="-1"] { display: none; }
html[data-step="0"] .panel[data-p="0"] { display: block; }
html[data-step="1"] .panel[data-p="0"], html[data-step="2"] .panel[data-p="0"], html[data-step="3"] .panel[data-p="0"] { display: none; }
html[data-step="1"] .panel[data-p="1"], html[data-step="2"] .panel[data-p="2"], html[data-step="3"] .panel[data-p="3"] { display: block; }
html[data-step="1"] .stepper .step[data-i="0"] { color: var(--text); }
html[data-step="2"] .stepper .step[data-i="0"], html[data-step="2"] .stepper .step[data-i="1"],
html[data-step="3"] .stepper .step[data-i="0"], html[data-step="3"] .stepper .step[data-i="1"], html[data-step="3"] .stepper .step[data-i="2"] { color: var(--text); }
html[data-step="1"] .stepper .step[data-i="0"], html[data-step="2"] .stepper .step[data-i="0"], html[data-step="3"] .stepper .step[data-i="0"] { font-weight: 400; border-bottom-color: transparent; }
html[data-step="1"] .stepper .step[data-i="1"], html[data-step="2"] .stepper .step[data-i="2"], html[data-step="3"] .stepper .step[data-i="3"] { color: var(--accent); font-weight: 600; border-bottom-color: var(--accent); }
.panel-h { font-size: 1.08rem; font-weight: 600; color: var(--text); margin: 0 0 14px; }
.panel-h-row { display: flex; align-items: baseline; justify-content: space-between; gap: 12px; }
.panel-h-row .panel-h { margin: 0 0 14px; }
.mod-toggle-all { background: none; border: none; padding: 0; font: inherit; font-size: 0.82rem; font-weight: 600; color: var(--accent); cursor: pointer; }
.mod-toggle-all:hover { text-decoration: underline; }
.ctx-selectors { display: flex; flex-wrap: wrap; gap: 14px; margin-bottom: 18px; }
.ctx-field { display: flex; flex-direction: column; gap: 6px; }
.ctx-field > span { font-size: 0.82rem; font-weight: 500; color: var(--muted); }
.ctx-field select { font-family: Inter, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif; font-size: 0.95rem; color: var(--text); background: var(--surface); border: 1px solid var(--line); border-radius: 9px; padding: 9px 12px; min-width: 168px; cursor: pointer; }
.ctx-field select:focus { outline: none; border-color: var(--accent); }
.panel-foot { display: flex; align-items: center; justify-content: space-between; gap: 12px; margin-top: 20px; flex-wrap: wrap; }
.pbtn { display: inline-flex; align-items: center; gap: 7px; padding: 9px 16px; border-radius: 10px; border: 1px solid var(--line); background: #fff; color: var(--text); font-family: inherit; font-size: 0.92rem; cursor: pointer; transition: .12s; }
.pbtn:hover { border-color: #cfd4db; background: var(--subtle); }
.pbtn.primary { background: var(--accent); border-color: var(--accent); color: #fff; }
.pbtn.primary:hover { background: #178b66; }
.pbtn.ghost { border-color: transparent; color: var(--muted); padding-left: 6px; }
.pbtn.ghost:hover { background: transparent; color: var(--text); }
.pbtn[disabled] { opacity: 0.55; cursor: default; }
/* `.pbtn` sets `display: inline-flex`, which outranks the user-agent `[hidden]` rule -- the
   exact defect .drop had in 2026-08. The submit card hides #runBtn, so without this the Run
   button would stay on screen under a run that has already been submitted.
   Derived guard: tests/test_hidden_actually_hides.py. */
.pbtn[hidden] { display: none; }
.pmute { color: var(--faint); font-size: 0.83rem; }
.pmute a { color: var(--accent); }
.feedback-note { margin-top: 14px; }

.ctx-body { margin-top: 8px; }
.ctx-body h3 { margin: 0 0 6px; font-size: 1rem; }
.ctx-body p { margin: 0 0 12px; color: var(--muted); font-size: 0.93rem; }
/* Cell-type grid: even card layout (not the pill/wrap look .lchip uses elsewhere) so a
   20-item reference reads as a scannable spec-sheet, each with the color dot the reference's
   own UMAP uses for that type (see build_reference_catalog.py::_umap_colors). */
#atlasLegend.legend { display: grid; grid-template-columns: repeat(auto-fill, minmax(180px, 1fr)); gap: 8px; margin: 0 0 16px; }
#atlasLegend.legend .lchip { display: flex; align-items: center; gap: 8px; width: auto; padding: 7px 10px; border-radius: 8px; background: var(--surface); color: var(--text); font-size: 0.82rem; white-space: normal; }
#atlasLegend.legend .ld { width: 9px; height: 9px; }
.ref-table { width: 100%; border-collapse: collapse; font-size: 0.85rem; }
.ref-table th, .ref-table td { text-align: left; padding: 7px 10px; border-bottom: 1px solid var(--line); vertical-align: top; }
.ref-table th { color: var(--muted); font-weight: 500; white-space: nowrap; width: 46%; }
.ref-table td { color: var(--text); }
.ref-table tr:last-child th, .ref-table tr:last-child td { border-bottom: none; }

.outcomes-layout { display: grid; grid-template-columns: minmax(0, 1fr) 340px; gap: 16px; align-items: start; }
.mod-extra { border: 1px solid var(--line); border-radius: 10px; padding: 14px; background: var(--subtle); }
.mod-extra-title { font-size: 0.85rem; font-weight: 600; color: var(--text); margin: 0 0 6px; }
.mod-extra .pmute { margin: 0 0 10px; }
.mod-extra-field label { display: block; font-size: 0.85rem; font-weight: 600; color: var(--text); margin-bottom: 8px; }
.mod-extra-field .opt-tag { font-weight: 400; color: var(--faint); }
.mod-extra-field textarea { width: 100%; min-height: 90px; border: 1px solid var(--line); border-radius: 8px; padding: 8px 10px; font: inherit; font-size: 0.88rem; resize: vertical; background: #fff; }
.mod-extra-field input[type=text] { width: 100%; border: 1px solid var(--line); border-radius: 8px; padding: 8px 10px; font: inherit; font-size: 0.88rem; background: #fff; box-sizing: border-box; }
.mod-extra-mode { margin: 4px 0 4px; }
.mod-extra-radio { display: flex; align-items: flex-start; gap: 8px; font-size: 0.84rem; font-weight: 600; color: var(--text); cursor: pointer; margin: 10px 0 2px; }
.mod-extra-radio input { margin-top: 2px; flex: none; }
.mod-extra-radio-note { font-size: 0.8rem; margin: 0 0 4px 24px; }
/* confirm-columns step (after upload) */
/* NO BOUNDING BOX (Leonardo, 2026-08-20). This panel is not a card floating on the page:
   it is the body of step 3, already inside the step's own frame, so its border drew a box
   inside a box. `max-width` is wider than the old 520px because the row below puts the
   value preview in a second column, which needs the room. */
.confirm-cols { max-width: 760px; }
/* PICKER LEFT, PREVIEW RIGHT. Centred against the field rather than top- or
   bottom-aligned, because the preview's height is the customer's data (1 line for a
   2-value column, 3 for a listed 8) and only centring looks deliberate at every height. */
.confirm-row { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1.05fr); column-gap: 16px; align-items: center; margin: 0 0 12px; }
.confirm-row .confirm-field { margin: 0; }
.confirm-row .col-preview { margin: 0; }
/* One column when there is no room for two: the preview returns to under its picker,
   which is worse but readable, where a squeezed second column is neither. */
@media (max-width: 620px) { .confirm-row { grid-template-columns: minmax(0, 1fr); row-gap: 2px; } }
.confirm-title { font-size: 0.95rem; font-weight: 700; margin: 0 0 10px; }
.confirm-field { display: block; margin: 0 0 12px; }
.confirm-field span { display: block; font-size: 0.84rem; font-weight: 600; margin-bottom: 5px; }
.confirm-field select, .confirm-field input[type=text] { width: 100%; border: 1px solid var(--line); border-radius: 8px; padding: 8px 10px; font: inherit; font-size: 0.88rem; background: #fff; box-sizing: border-box; }
.confirm-note { font-size: 0.8rem; margin: 4px 0 10px; }
/* THE OFFER. On its own ground under the title, not a third button in the actions row:
   Confirm and Reset are things the customer does with their own answers, and this is
   something we sell them. The tint is what separates the two kinds. */
.autodetect { background: var(--accent-soft); border: 1px solid var(--line); border-radius: 10px; padding: 12px 14px; margin: 0 0 16px; display: flex; flex-wrap: wrap; align-items: center; gap: 10px; }
.autodetect-lead { flex: 1 1 100%; margin: 0; font-size: 0.84rem; line-height: 1.5; color: var(--muted); }
.autodetect-price { font-size: 0.84rem; color: var(--text); font-weight: 600; }
.autodetect-bal { font-weight: 400; color: var(--muted); }
.autodetect-off { flex: 1 1 100%; margin: 0; font-size: 0.82rem; }
.autodetect-note { flex: 1 1 100%; margin: 0; font-size: 0.82rem; line-height: 1.45; }
.autodetect-note--ok { color: var(--accent); }
.autodetect-note--err { color: var(--red); font-weight: 600; }
/* A BOUGHT OPINION, marked as ours. Tinted rather than plain so it cannot be mistaken for
   something read out of the file the way the value preview is, and quiet rather than loud
   because it is a draft the customer is about to overrule or accept. */
.col-proposal { margin: 2px 0 8px; font-size: 0.82rem; line-height: 1.45; color: var(--muted); border-left: 2px solid var(--accent); padding-left: 8px; }
.col-proposal b { color: var(--text); }
.col-proposal--none { border-left-color: var(--line); }
.confirm-actions { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 8px; }
.confirm-subtitle { font-size: 0.84rem; font-weight: 600; margin: 14px 0 8px; }
.confirm-align { border-top: 1px solid var(--line); padding-top: 6px; margin-bottom: 10px; }
.confirm-alignrow { display: flex; align-items: flex-start; gap: 8px; font-size: 0.85rem; margin: 5px 0; }
.confirm-alignrow input { margin-top: 3px; flex: none; }
/* confirm-columns now lives in step 3 (Load your data), inline under the drop zone */
#confirmCols { margin-top: 16px; }
.confirm-ok { color: var(--accent); font-weight: 600; margin-bottom: 0; }
.confirm-err { color: #b91c1c; font-weight: 600; margin-bottom: 0; }
/* a step the customer has walked past but not finished reads as unfinished, not done */
.stepper .step.incomplete:not(.active) { color: var(--faint); }
.stepper .step.incomplete:not(.active)::after { content: '!'; margin-left: 5px; font-weight: 700; color: #b91c1c; }

.resume-banner { border: 1px solid var(--line); border-left: 3px solid var(--accent, #2a6); border-radius: 8px; padding: 10px 14px; margin-bottom: 12px; font-size: 0.88rem; background: #fff; display: flex; flex-wrap: wrap; align-items: center; gap: 8px; }
.resume-banner--list { flex-direction: column; align-items: stretch; gap: 0; }
.resume-head { font-weight: 600; color: var(--text); margin-bottom: 4px; }
.resume-row { display: flex; align-items: center; justify-content: space-between; gap: 10px; padding: 6px 0; border-top: 1px solid var(--line); }
.resume-row:first-of-type { border-top: none; }
.resume-foot { margin-top: 6px; }
.resume-banner .muted { color: var(--muted); font-size: 0.8rem; font-weight: 400; }
.mod-extra-sub { font-size: 0.82rem; font-weight: 600; color: var(--text); margin: 12px 0 8px; }
.mod-extra-note { font-size: 0.82rem; }
.mod-extra-chips.legend { gap: 5px; margin-bottom: 2px; }
.mod-extra-chips .lchip { font-size: 0.73rem; padding: 2px 8px; }
.mod-extra-list { margin: 0 0 4px; padding-left: 18px; font-size: 0.85rem; color: var(--muted); }
.mod-extra-list li { margin-bottom: 3px; }
.mod-extra-toggle { display: flex; align-items: flex-start; gap: 8px; font-size: 0.84rem; color: var(--text); cursor: pointer; margin-bottom: 8px; }
.mod-extra-toggle input { margin-top: 2px; flex: none; }
.mod-extra-checklist { margin-bottom: 4px; }
.mod-extra-file { margin-top: 4px; }
.mod-extra-drop { display: block; border: 1px dashed var(--line); border-radius: 8px; padding: 12px; text-align: center; font-size: 0.82rem; color: var(--muted); cursor: pointer; background: #fff; }
.mod-extra-drop:hover { border-color: var(--accent); color: var(--accent); }
.mod-extra-filename { font-size: 0.78rem; margin: 6px 0 0; }

.run-sum { color: var(--muted); font-size: 0.94rem; margin: 0 0 16px; }
.run-sum b { color: var(--text); font-weight: 600; }

/* dependency: gated rows are greyed + non-clickable until their prerequisite is on (flat list, no indent).
   UNUSED since 2026-09-03: no outcome declares `requires` any more (marker validation, the last
   one, became a sub-option of "Assign cell types"), so nothing gets .dep-locked and no .dep-req
   note is rendered on an outcome row. Kept for the next outcome that needs a prerequisite.
   .obs-req below IS live: it is the file-side gate's slot and shares the .dep-req class. */
.mod-item.dep-locked { cursor: default; }
.mod-item.dep-locked:hover { background: transparent; }
.mod-item.dep-locked .chk { opacity: .45; }
.mod-item.dep-locked .mt { color: var(--faint); }
.dep-req { margin-left: auto; font-size: 0.76rem; color: var(--faint); white-space: nowrap; }
.mod-item:not(.dep-locked) .dep-req { display: none; }              /* only shown while the dependent is gated */
.dep-req .dep-link { color: var(--accent); cursor: pointer; text-decoration: none; border-bottom: 1px dotted currentColor; }
.dep-req .dep-link:hover { opacity: .8; }
/* File-side reasons stay visible on an UNLOCKED row. Must sit AFTER the rule above, which
   hides .dep-req unless the row is locked; equal specificity, so order decides.
   Rationale: walkthrough.js, roleBlockedReason. */
.mod-item:not(.dep-locked) .obs-req:not([hidden]) { display: inline; }
.obs-req.obs-soft { font-style: italic; }                           /* degrades, not blocked */
.run-hint { color: var(--red); font-size: 0.84rem; }
.run-hint:empty { display: none; }
/* greyed sub-option (checklist item gated on its own prerequisite) */
.mod-extra-toggle.disabled { color: var(--faint); cursor: not-allowed; }
.mod-extra-toggle.disabled input { cursor: not-allowed; }
.mod-extra-toggle .dep-req { margin-left: 6px; }
.mod-extra-toggle .dep-link { color: var(--accent); cursor: pointer; border-bottom: 1px dotted currentColor; }
/* greyed radio option (mode option gated on the uploaded file carrying .obs columns) */
.mod-extra-radio.disabled { color: var(--faint); cursor: not-allowed; }
.mod-extra-radio.disabled input { cursor: not-allowed; }

/* running-total bar (step 4 only, and only when creditsUi is enabled). Step 2 has no cost
   styling at all: the relative-cost dots that lived there were deleted 2026-08-27 with the
   browser-side rate table they were ordered by. */
/* itemized breakdown (step 4 only) */
.cr-items { margin-top: 10px; width: 100%; font-size: 0.86rem; }
.cr-item { display: flex; justify-content: space-between; gap: 16px; padding: 3px 0; color: var(--muted); }
.cr-item b { color: var(--text); font-weight: 600; white-space: nowrap; }
/* NO ROW IS EMPHASISED OVER ANOTHER (Leonardo, 2026-08-19). The bolding is gone: the
   colour difference plus the sub-rows' indent carry the hierarchy, and the amounts are
   already bold via `.cr-item b`, so a bold LABEL beside a bold number gave the list two
   competing emphases. Only `color` here now. */
.cr-items .cr-item > span { color: var(--text); }
/* MATCHES THE WINNER'S SPECIFICITY AND SITS LATER. As written first — `.cr-item--sub >
   span` at (0,1,1) against `.cr-items .cr-item > span` at (0,2,1) — this rule LOST, so
   every row rendered bold and the sub-rows had only their indent left. Same tie the
   .cr-note rule two commits above had to win, and the same class of defect as
   `el.hidden` losing to `display: flex`: present in the file, no effect on the element. */
/* (0,3,1), which BEATS `.cr-items .cr-item > span` rather than tying it. A tie is decided
   by document order, and depending on order across a 40kB stylesheet is how the same
   override silently loses again the next time a rule is inserted above it. */
.cr-items .cr-item.cr-item--sub > span, .cr-items .cr-item.cr-item--fixed > span { color: var(--muted); }
/* The note keeps its own warning colour: the generic row rule would repaint it var(--text)
   and it would stop looking like a callout. */
.cr-items .cr-item.cr-item--note span { color: inherit; }
/* The label wraps, the amount does not move. Prefixing sub-ticks with their analysis makes
   these rows longer (up to ~52 characters), and without min-width:0 a flex child refuses to
   shrink below its content and pushes the amount off the row. */
.cr-item > span { min-width: 0; overflow-wrap: anywhere; }
.cr-item--total { margin-top: 6px; padding-top: 7px; border-top: 1px solid var(--line); }
/* lives inside .credits-bar (a wrapping flex row), so it takes its own line */
.cr-items { flex-basis: 100%; }
.credits-bar { display: flex; flex-wrap: wrap; align-items: center; gap: 8px 18px; margin-top: 16px; padding: 12px 16px; border: 1px solid var(--line); border-radius: 10px; background: var(--subtle); font-size: 0.9rem; color: var(--muted); }
/* Same trap as .drop: `display: flex` above outranks the UA [hidden] rule, so
   `runCredits.hidden = true` would leave the bar on screen. Nobody has seen it because the
   whole credits UI is behind VARNAOPS_CONFIG.creditsUi, which is false in every deployed
   config — it would have surfaced on the day that flag was first turned on, as a price
   showing where the code says it is hidden. Found by the derived scan in
   tests/test_hidden_actually_hides.py, not by looking. */
.credits-bar[hidden] { display: none; }
.credits-bar .cr-cost b, .credits-bar .cr-bal b { color: var(--text); font-weight: 700; }
.credits-bar .cr-bal { margin-left: auto; }
.credits-bar .cr-note { color: var(--faint); font-weight: 400; font-size: 0.82rem; }
.credits-bar .cr-over { color: var(--red); font-weight: 600; }
#runCredits { margin: 0 0 16px; }

/* -- the submit success card (step 4) -------------------------------------------------
   Takes #runCredits' place once a run is queued, so the same margin: the box above the
   button is either the price of a run you are about to make or the receipt for one you
   just made, never both. No `display` of its own, deliberately: the card is shown and
   hidden with `.hidden`, and a display rule here would silently defeat that. */
.submit-card { margin: 0 0 16px; padding: 16px 18px; border: 1px solid rgba(29,158,117,0.3); border-radius: 10px; background: var(--accent-soft); }
.sc-head { display: flex; align-items: center; gap: 9px; margin: 0; font-size: 1rem; font-weight: 600; color: #1a6a51; }
/* Same treatment as .upic: hand-drawn, stroked, no fill, sized in the text. */
.sc-ic { width: 20px; height: 20px; flex: none; stroke: var(--accent); fill: none; stroke-width: 1.9; stroke-linecap: round; stroke-linejoin: round; }
.sc-sum { margin: 9px 0 0; font-size: 0.9rem; color: var(--muted); overflow-wrap: anywhere; }
.sc-sum b { color: var(--text); font-weight: 600; }
.sc-note { margin: 10px 0 0; font-size: 0.88rem; line-height: 1.5; color: var(--ink-soft); }
.sc-note a { color: var(--accent); }
/* Side by side while they fit; the phone rule in layout-app.css lets them take a row each. */
.sc-acts { display: flex; flex-wrap: wrap; align-items: center; gap: 10px; margin-top: 14px; }
/* Explicitly white rather than .ghost: on the card's green tint a borderless button reads
   as a link, and this one is the second of a pair. */
.sc-again { background: #fff; }
/* "View in My runs" is an <a>, and the browser underlines it. Every other .pbtn on the
   page is a <button>, so .pbtn never had to say this. */
.sc-acts a.pbtn { text-decoration: none; }
.progress { height: 7px; border-radius: 4px; background: var(--line); overflow: hidden; margin: 18px 0 14px; }
.progress > i { display: block; height: 100%; background: var(--accent); width: 0; transition: width .5s ease; }
.status-stage { display: flex; align-items: center; gap: 11px; padding: 6px 0; color: var(--muted); font-size: 0.92rem; }
.status-stage .ic { width: 18px; height: 18px; border-radius: 50%; border: 1.5px solid #d3d7dd; flex: none; display: inline-flex; align-items: center; justify-content: center; }
.status-stage.done { color: var(--text); }
.status-stage.done .ic { background: var(--accent); border-color: var(--accent); }
.status-stage.done .ic::after { content: ""; width: 4px; height: 8px; border: solid #fff; border-width: 0 1.6px 1.6px 0; transform: rotate(45deg) translateY(-1px); }
.status-stage.active { color: var(--text); font-weight: 600; }
.status-stage.active .ic { border-color: var(--accent); border-right-color: transparent; animation: spin .7s linear infinite; }
@keyframes spin { to { transform: rotate(360deg); } }


/* ===== login page ===== */
/* One column since 2026-09-03: the preview table that filled the right column is gone. */
.login-grid { display: grid; grid-template-columns: minmax(0, 420px); justify-content: center; gap: 30px; align-items: start; }
.card-box { border: 1px solid var(--line); border-radius: 14px; background: var(--surface); padding: 22px; box-shadow: 0 1px 2px rgba(17,24,39,0.04); }
.card-box h3 { margin: 0 0 14px; font-size: 1.05rem; }
.field { display: block; margin-bottom: 13px; }
.field span { display: block; font-size: 0.82rem; color: var(--muted); margin-bottom: 5px; }
.field input { width: 100%; padding: 10px 12px; border: 1px solid var(--line); border-radius: 9px; font-family: inherit; font-size: 0.93rem; background: #fff; }
.field input:focus { outline: none; border-color: var(--accent); }
.runs-table { width: 100%; border-collapse: collapse; font-size: 0.9rem; }
.runs-table th, .runs-table td { text-align: left; padding: 11px 12px; border-bottom: 1px solid var(--line); vertical-align: middle; }
.runs-table thead th { color: var(--muted); font-weight: 600; font-size: 0.76rem; text-transform: uppercase; letter-spacing: 0.04em; }
.runs-table tbody tr:last-child td { border-bottom: none; }
.badge { display: inline-flex; align-items: center; gap: 6px; padding: 3px 10px; border-radius: 999px; font-size: 0.8rem; font-weight: 500; }
.badge.run { background: var(--accent-soft); color: var(--accent); }
.badge.done { background: #eef2f6; color: var(--muted); }
.badge .dot { width: 6px; height: 6px; border-radius: 50%; background: currentColor; flex: none; }

/* responsive */
@media (max-width: 820px) {
  .preview.atlas, .compute { grid-template-columns: 1fr; gap: 18px; }
  .cards { grid-template-columns: 1fr; }
  .ctx-body, .login-grid, .outcomes-layout { grid-template-columns: 1fr; }
}
@media (max-width: 660px) {
  .stepper { padding: 0 16px; gap: 18px; }   /* underline tabs scroll horizontally on narrow screens */
}
@media (max-width: 560px) {
  .mark { height: 36px; }
  .word { font-size: 1.2rem; }
  .nav-links { gap: 14px; }
}

/* keep the button label white in every context — .nav-links a / .prose a would
   otherwise win on specificity and grey-out (or green-on-green) the CTA text */
a.cta, a.cta:hover { color: var(--accent-ink); }
/* The same trap, one selector over: account.html's body is .prose, and `.prose a` (0,1,1)
   beats `.scta-primary` (0,1,0), so the Buy credits button rendered accent text on the
   accent ground (owner saw it on the first live click, 2026-08-29). Scoped to .prose so
   it outranks the rule that caused it, whatever order the file ends up in. */
.prose a.scta-primary, .prose a.scta-primary:hover { color: var(--accent-ink); }

/* ---- the account page ----------------------------------------------------
   The old dropdown's rows (.acct-row / .acct-sub / .acct-note / .acct-pending) on a
   page instead of in a popover: same rows, given width and a rule to sit on. Reusing
   them rather than inventing page-only twins is what keeps the two readings of a
   balance identical now that only one of them exists. */
/* The identity is one line above the credits block, not a section: no heading, no card,
   because "which address, and is the account enabled" is a caption for the page rather
   than a second subject on it. */
.acct-ident { margin: 0 0 4px; font-size: 0.95rem; color: var(--muted); }
.acct-ident b { color: var(--text); font-weight: 600; word-break: break-word; }
#acctProfile { margin: 0 0 22px; max-width: 520px; }
.acct-page { margin: 0 0 26px; padding: 14px 16px; border: 1px solid var(--line);
  border-radius: 12px; background: var(--subtle); max-width: 520px; }
.acct-page .acct-row { font-size: 0.95rem; }
.acct-page .acct-row b { font-weight: 600; }

/* The notification preference: one checkbox and its explanation. A row so the control
   sits beside its label rather than above it. The failure line below reuses .acct-err,
   which the deletion card already defines. */
.acct-check { display: flex; align-items: flex-start; gap: 9px; font-size: 0.95rem;
  cursor: pointer; }
.acct-check input { margin: 3px 0 0; flex: 0 0 auto; }

/* Deleting the account, and the destruction record it produces. Same card as the
   credits block so the page keeps one shape; the left rule is red rather than the
   accent because this is the one control here that destroys something. */
.acct-danger, .acct-record { border-left: 3px solid var(--red); }
.acct-del-idle { margin: 6px 0 26px; }
.acct-record-hd { margin: 0 0 8px; font-size: 1rem; font-weight: 600; }
.acct-record-stmt { border-left: 2px solid var(--line); padding-left: 10px; }
.acct-resid { margin: 6px 0 0; padding-left: 18px; font-size: 0.79rem; line-height: 1.5;
  color: var(--muted); }
.acct-resid li { margin: 0 0 4px; }
.acct-del-input { width: 100%; max-width: 320px; padding: 9px 11px; border: 1px solid var(--line);
  border-radius: 8px; background: var(--bg); color: var(--text); font: inherit; font-size: 0.92rem; }
.acct-del-input:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
.acct-err { margin: 8px 0 0; font-size: 0.85rem; line-height: 1.5; color: var(--red); }
.acct-copied { font-size: 0.79rem; color: var(--muted); }
/* THE ONE DESTRUCTIVE CONTROL ON THE SITE, so it is the one that looks it. Same box as
   .scta-primary; only the ground changes, because a button that is a different SHAPE
   from every other button reads as a different kind of thing to click rather than as a
   warning. Disabled until the phrase matches, and the disabled state has to be visible
   or the reader cannot tell that typing is what unlocks it. */
.scta-danger { display: inline-flex; align-items: center; padding: 11px 20px; border: 0;
  border-radius: 10px; background: var(--red); color: #fff; font: inherit; font-weight: 600;
  font-size: 0.96rem; cursor: pointer; transition: filter .12s; }
.scta-danger:hover:not([disabled]) { filter: brightness(1.06); }
.scta-danger[disabled] { opacity: .5; cursor: default; }
.acct-copy, .acct-del-cancel { cursor: pointer; font-family: inherit; font-size: 0.88rem;
  padding: 9px 16px; }
.acct-del-go { font-size: 0.92rem; }

/* ---- My runs -------------------------------------------------------------
   ITS OWN PAGE since 2026-08-28, so the card chrome went away with the panel: the
   collapsible head, the chevron and the .runs-box shell existed to keep a list from
   competing with the stepper on the app page, and there is nothing to compete with
   here. The row styles below are unchanged and are the reason the list looks the same
   as it did in the panel. */
.runs-count { color: var(--muted); font-weight: 500; }
.runs-bar { display: flex; align-items: center; justify-content: space-between; gap: 12px;
  padding: 0 0 10px; border-bottom: 1px solid var(--line); }
/* The liveness line is the whole point of the panel not lying about itself, so it is
   readable rather than fine print — see renderRunsLive(). */
.runs-live { font-size: 0.82rem; color: var(--muted); }
.runs-refresh { padding: 4px 12px; font-size: 0.84rem; }
.runs-empty { padding: 18px 0; color: var(--muted); font-size: 0.9rem; }
.runs-empty--err { color: var(--red); }

.run-row { border-bottom: 1px solid var(--line); padding: 12px 0 14px; }
.run-row:last-child { border-bottom: 0; }
/* NOT A BUTTON any more, and no chevron column: the row opened to reach the report, and
   the report is now on the row. Five columns since 2026-09-07 (status, file, reference,
   when, price) -- the fifth is the run's price, never a chevron. */
.run-line { display: grid; grid-template-columns: 92px minmax(0,1fr) auto auto auto;
  align-items: center; gap: 12px; width: 100%; color: var(--text); text-align: left; }
.run-status { font-size: 0.74rem; font-weight: 700; letter-spacing: .04em; text-transform: uppercase; }
.run-status--ok { color: var(--accent); }
.run-status--bad { color: var(--red); }
.run-status--live { color: var(--muted); }
/* The filename can be anything a customer named a file, so it truncates rather than
   pushing the date off the row. minmax(0,1fr) above is what makes this bite. */
.run-file { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-size: 0.9rem; }
.run-ref { color: var(--muted); font-size: 0.82rem; white-space: nowrap; }
.run-when { color: var(--faint); font-size: 0.8rem; white-space: nowrap; }
/* The price, and on a failed run "Refunded <amount>". Muted like the reference rather
   than emphasised: it is a fact about the run, not a call to action, and the row's
   loudest thing must stay the status. Empty when the run has no ledgered charge. */
.run-price { color: var(--muted); font-size: 0.82rem; white-space: nowrap; }
.run-foot { padding: 8px 0 0; }
.run-foot p { margin: 0 0 6px; font-size: 0.86rem; }
.run-actions { display: flex; flex-wrap: wrap; gap: 10px; margin: 0 0 10px; }
.run-act { cursor: pointer; font-family: inherit; font-size: 0.88rem; padding: 9px 16px; }
.run-act.scta-primary { border: 0; }
.run-act[disabled] { opacity: .55; cursor: default; }
.run-id code { font-size: 0.8rem; }
/* Delete is in the same row of controls as Open report, so it is the same size and shape
   as its neighbours; only the label is red. A destructive control shaped differently from
   the ones beside it reads as a different KIND of thing rather than as a warning, and the
   confirm dialog is what actually stands between the reader and the deletion. */
.run-del { color: var(--red); border-color: var(--line); }
.run-del:hover { border-color: var(--red); color: var(--red); }
.run-del-err { align-self: center; font-size: 0.82rem; color: var(--red); }
.runs-expiry { margin: 14px 0 0; font-size: 0.78rem; color: var(--faint); }
/* One column below the row's own breakpoint: four columns in ~340px turns the filename
   into two characters and an ellipsis, which is worse than a taller row. */
@media (max-width: 640px) {
  .run-line { grid-template-columns: 1fr; grid-auto-rows: min-content; row-gap: 2px; }
  .run-file, .run-ref, .run-when, .run-price { grid-column: 1 / -1; }
  .run-foot .run-actions { flex-direction: column; align-items: stretch; }
  .run-act { text-align: center; }
}
/* The data credit on the run step. LITERALLY the same disclosure shell as .prev-uploads --
   they share one rule set up beside it now -- rather than a second visual language for the
   same idea: a closed box that opens to a form. The field inside reuses .mod-extra-field,
   which is the textarea style every other free-text answer on this page already uses. */
.cite-box { margin: 14px 0; }
/* rows="2" is the height asked for; .mod-extra-field's shared min-height (90px, sized for
   the long free-text answers on the outcomes step) would otherwise decide it instead. */
.cite-box .mod-extra-field textarea { min-height: 0; }
.cite-box[open] > summary { margin-bottom: 8px; }
/* The format is explained ONCE, under the field, where it reads as a caption to the thing
   it describes. It used to be said twice: a four-line paragraph above the box AND the
   placeholder inside it (owner, 2026-09-08). */
.cite-note { margin: 6px 0 0; font-size: 0.78rem; color: var(--muted); }
/* Explicit, because `hidden` is an attribute an author `display` rule silently beats:
   the submit card hides this box, and a box that stayed visible over a queued run
   would read as a field still waiting to be filled in. */
.cite-box[hidden] { display: none; }
