/* VarnaOps redesign, stage 1: the nine TEXT PAGES only.
   methods, weights, versions, attribution, terms, privacy, data-handling, dpa, notices.

   NOTHING ELSE LOADS THIS FILE. index.html, login.html, account.html, runs.html, 404.html
   and the report wrappers keep styles.css + layout-app.css untouched, so this sheet may not
   assume the old tokens, the old chrome or the old class names exist beside it. It is the
   whole sheet for the pages that link it: tokens, chrome, and the reading column.

   THE TEXT OF THESE PAGES DID NOT CHANGE, and neither did the markup inside <main>: this is
   a stylesheet plus new chrome around an untouched document. Every selector below is aimed
   at a class the live pages already carried (.page-head, .prose, .lead, .toc, .toc-h,
   .tbl-card, .ltable, .micro, .pmute, .role, .lic, .note, .ruo-callout, .ver-ref, .pbtn,
   .dl-table, .licence-text, and the .dl-* hooks weights.js fills), which is why no page
   needed an edit between <main> and </main>. Guard:
   tests/test_web_text_pages_redesign_text_unchanged.py.

   DARK MODE IS OFF IN PRODUCTION. The rest of the site is light-only, so a text page that
   followed the system theme would be the one dark page on a light site. The dark tokens are
   kept (the design has them, and stage 2 will turn them on site-wide) but they are scoped to
   <html class="theme-auto">, which none of the nine pages sets, and none of them ships a
   theme toggle. The pages also keep <meta name="color-scheme" content="light">.

   FONTS ARE SELF-HOSTED, and they have to be: the site's CSP is
   `style-src 'self' 'unsafe-inline'; font-src 'self' data:`, so a fonts.googleapis.com
   stylesheet is blocked and a gstatic font file is blocked. Every family below is served
   from web/vendor/fonts/, recorded in web/vendor/vendor.json and credited on notices.html.
   Do not reintroduce a Google Fonts link here: it would not render, and quietly. */

/* ---- self-hosted fonts, SIL OFL 1.1 ----------------------------------------
   Upstream release files, byte for byte, not subsetted: a subset is a modification, and
   these carry Reserved Font Names. Only the weights the pages actually use are declared,
   and each family's own OFL text sits beside it as web/vendor/fonts/<family>/LICENSE. */
@font-face {
  font-family: "Geist";
  font-style: normal;
  font-weight: 600;
  font-display: swap;
  src: url("vendor/fonts/geist/Geist-SemiBold.woff2") format("woff2");
}
@font-face {
  font-family: "Geist";
  font-style: normal;
  font-weight: 700;
  font-display: swap;
  src: url("vendor/fonts/geist/Geist-Bold.woff2") format("woff2");
}
@font-face {
  font-family: "Public Sans";
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url("vendor/fonts/public-sans/PublicSans-Regular.woff2") format("woff2");
}
@font-face {
  font-family: "Public Sans";
  font-style: italic;
  font-weight: 400;
  font-display: swap;
  src: url("vendor/fonts/public-sans/PublicSans-Italic.woff2") format("woff2");
}
@font-face {
  font-family: "Public Sans";
  font-style: normal;
  font-weight: 500;
  font-display: swap;
  src: url("vendor/fonts/public-sans/PublicSans-Medium.woff2") format("woff2");
}
@font-face {
  font-family: "Public Sans";
  font-style: normal;
  font-weight: 600;
  font-display: swap;
  src: url("vendor/fonts/public-sans/PublicSans-SemiBold.woff2") format("woff2");
}
@font-face {
  font-family: "JetBrains Mono";
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url("vendor/fonts/jetbrains-mono/JetBrainsMono-Regular.woff2") format("woff2");
}
@font-face {
  font-family: "JetBrains Mono";
  font-style: normal;
  font-weight: 500;
  font-display: swap;
  src: url("vendor/fonts/jetbrains-mono/JetBrainsMono-Medium.woff2") format("woff2");
}
@font-face {
  font-family: "JetBrains Mono";
  font-style: normal;
  font-weight: 600;
  font-display: swap;
  src: url("vendor/fonts/jetbrains-mono/JetBrainsMono-SemiBold.woff2") format("woff2");
}

/* ---- tokens ---- */
:root {
  --bg: #f3f6f5;
  --surface: #ffffff;
  --sunk: #e9efed;
  --ink: #0d1b18;
  --muted: #55655f;
  --line: #d6e0dc;
  --accent: #0e8563;        /* the brand green, deepened for AA on white */
  --accent-hover: #0a6e52;
  --accent-ink: #ffffff;
  --accent-soft: #dff0ea;
  --violet: #5a2fb0;        /* from the spiral mark; used sparingly */
  --warn: #9a5b00;
  --warn-soft: #fbf0dc;
  --mark-bg: transparent;
  --shadow: 0 1px 2px rgb(13 27 24 / .05), 0 8px 24px -12px rgb(13 27 24 / .18);

  --f-display: "Geist", "Helvetica Neue", "Segoe UI", system-ui, sans-serif;
  --f-body: "Public Sans", "Segoe UI", system-ui, -apple-system, sans-serif;
  --f-mono: "JetBrains Mono", ui-monospace, "SFMono-Regular", Menlo, monospace;

  --r-s: 6px;
  --r-m: 10px;
  --r-l: 16px;
  --wrap: 1180px;
  --gutter: clamp(16px, 4vw, 40px);
}
/* Both dark blocks are gated on .theme-auto, which stage 1 never sets. See the head note. */
@media (prefers-color-scheme: dark) {
  :root.theme-auto:not([data-theme="light"]) {
    --bg: #0a1210; --surface: #111b18; --sunk: #0d1614; --ink: #e5eeea; --muted: #90a39c;
    --line: #22332e; --accent: #34c192; --accent-hover: #52d3a7; --accent-ink: #04140e;
    --accent-soft: #133128; --violet: #a88cf2; --mark-bg: #e5eeea; --warn: #f0b458; --warn-soft: #2e2413;
    --shadow: 0 1px 2px rgb(0 0 0 / .4), 0 10px 30px -14px rgb(0 0 0 / .7);
    color-scheme: dark;
  }
}
:root.theme-auto[data-theme="dark"] {
  --bg: #0a1210; --surface: #111b18; --sunk: #0d1614; --ink: #e5eeea; --muted: #90a39c;
  --line: #22332e; --accent: #34c192; --accent-hover: #52d3a7; --accent-ink: #04140e;
  --accent-soft: #133128; --violet: #a88cf2; --mark-bg: #e5eeea; --warn: #f0b458; --warn-soft: #2e2413;
  --shadow: 0 1px 2px rgb(0 0 0 / .4), 0 10px 30px -14px rgb(0 0 0 / .7);
  color-scheme: dark;
}

/* ---- base ---- */
*, *::before, *::after { box-sizing: border-box; }
html { scroll-behavior: smooth; -webkit-text-size-adjust: 100%; padding-top: env(safe-area-inset-top, 0px); }
@media (prefers-reduced-motion: reduce) { html { scroll-behavior: auto; } }
body {
  margin: 0; background: var(--bg); color: var(--ink);
  font: 400 16px/1.6 var(--f-body);
  -webkit-font-smoothing: antialiased;
}
img { max-width: 100%; display: block; }
a { color: var(--accent); text-underline-offset: 3px; }
a:hover { color: var(--accent-hover); }
code, .mono { font-family: var(--f-mono); font-size: .88em; }
:focus-visible { outline: 2px solid var(--accent); outline-offset: 3px; border-radius: 4px; }
h1, h2, h3, h4 { font-family: var(--f-display); font-weight: 600; letter-spacing: -.03em; line-height: 1.08; margin: 0; text-wrap: balance; }
p { margin: 0; }

.wrap { max-width: var(--wrap); margin: 0 auto; padding-inline: var(--gutter); }
.eyebrow {
  font: 500 12px/1 var(--f-mono); letter-spacing: .08em; text-transform: uppercase; color: var(--muted);
  display: inline-flex; align-items: center; gap: 8px;
}
.eyebrow::before { content: ""; width: 6px; height: 6px; border-radius: 50%; background: var(--accent); }

/* ---- buttons ---- */
.btn {
  --b-bg: var(--surface); --b-fg: var(--ink); --b-bd: var(--line);
  display: inline-flex; align-items: center; justify-content: center; gap: 8px;
  min-height: 44px; padding: 0 20px; border-radius: 999px;
  background: var(--b-bg); color: var(--b-fg); border: 1px solid var(--b-bd);
  font: 600 15px/1 var(--f-body); text-decoration: none; cursor: pointer;
  transition: background .15s, border-color .15s, transform .15s;
  white-space: nowrap;
}
.btn:hover { color: var(--b-fg); border-color: var(--muted); }
.btn:active { transform: translateY(1px); }
.btn-primary { --b-bg: var(--accent); --b-fg: var(--accent-ink); --b-bd: var(--accent); }
.btn-primary:hover { --b-bg: var(--accent-hover); border-color: var(--accent-hover); }
.btn-quiet { --b-bg: transparent; --b-bd: transparent; }
.btn-quiet:hover { --b-bg: var(--sunk); border-color: transparent; }
.btn-sm { min-height: 36px; padding: 0 14px; font-size: 14px; }
.btn[disabled] { opacity: .45; cursor: not-allowed; }

/* ---- top bar ----------------------------------------------------------------
   CHROME FOR TODAY'S SITE. The design's bar links to landing-page sections and to an
   app.html that does not exist yet, so stage 1 ships the two destinations that do:
   Analyse (index.html, which IS the app) and My runs. The right-hand control is the
   site's existing session-aware pair and is NOT rebuilt here: the markup, the classes
   and the behaviour are web/nav.js's and web/session.js's, which is why `.nav-links`,
   `.nav-dd`, `.nav-acct` and the `.acct-*` rows are styled below rather than renamed.
   session.js stamps data-auth on <html> in <head>, and the two rules that pick the
   painted control are the same ones styles.css has (display, never [hidden]: a hidden
   attribute loses to any display rule). */
.topbar {
  position: sticky; top: env(safe-area-inset-top, 0px); z-index: 30;
  background: color-mix(in srgb, var(--bg) 82%, transparent);
  backdrop-filter: saturate(1.4) blur(12px); -webkit-backdrop-filter: saturate(1.4) blur(12px);
  border-bottom: 1px solid color-mix(in srgb, var(--line) 70%, transparent);
}
.topbar-inner { display: flex; align-items: center; gap: 24px; min-height: 64px; }
.brand { display: inline-flex; align-items: center; gap: 10px; text-decoration: none; color: var(--ink); }
.brand img { width: 30px; height: 30px; padding: 2px; border-radius: 50%; background: var(--mark-bg); }
.brand-word { font: 700 20px/1 var(--f-display); letter-spacing: -.02em; }
.brand-word b { color: var(--accent); font-weight: 700; }

.topbar .nav-links { display: flex; align-items: center; gap: 4px; margin-left: 12px; flex: 1; }
.topbar .nav-links > a.nav-app,
.topbar .nav-links > a.navsite {
  color: var(--muted); text-decoration: none; font-weight: 500; font-size: 15px;
  padding: 8px 12px; border-radius: 999px;
}
.topbar .nav-links > a.nav-app:hover,
.topbar .nav-links > a.navsite:hover { color: var(--ink); background: var(--sunk); }
/* you-are-here, for the day one of these two links is the page you are on */
.topbar .nav-links > a[aria-current] { color: var(--accent); background: var(--accent-soft); }
/* The account pair sits at the far end. BOTH carry the margin, because only one of them is
   ever painted (the other is `display: none`, which takes no space and consumes no auto
   margin) and a rule that reset the second one would leave the account control stranded
   mid-row on exactly the pages a signed-in reader sees. */
.topbar .nav-links > a.signin, .topbar .nav-links > .nav-acct { margin-left: auto; }
.topbar .nav-links a.signin {
  display: inline-flex; align-items: center; justify-content: center;
  min-height: 36px; padding: 0 16px; border-radius: 999px;
  border: 1px solid var(--line); background: var(--surface); color: var(--ink);
  font: 600 14px/1 var(--f-body); text-decoration: none; white-space: nowrap;
}
.topbar .nav-links a.signin:hover { background: var(--sunk); border-color: var(--muted); }

/* the dropdowns (the account menu, and the compact site menu nav.js builds on a phone) */
.nav-dd { position: relative; }
.nav-dd > summary {
  list-style: none; cursor: pointer; color: var(--muted); font-size: 14px; font-weight: 500;
  display: flex; align-items: center; gap: 7px;
}
.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: .55; transition: transform .15s ease; flex: none;
}
.nav-dd[open] > summary::after { transform: rotate(225deg) translateY(-1px); }
.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: var(--r-m);
  box-shadow: var(--shadow); z-index: 60;
}
.nav-dd-menu a {
  padding: 9px 12px; border-radius: var(--r-s); color: var(--muted); font-size: 14.5px;
  text-decoration: none; white-space: nowrap;
}
.nav-dd-menu a:hover { background: var(--sunk); color: var(--ink); }

/* TWO CONTROLS IN THE MARKUP, ONE PAINTED (session.js stamps data-auth before the first
   paint, so the right one is there in frame one). */
.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; }
.nav-acct > summary {
  padding: 3px 8px 3px 3px; border: 1px solid var(--line); border-radius: 999px;
  background: var(--surface); color: var(--ink);
}
.nav-acct > summary:hover { background: var(--sunk); }
.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: 13px; font-weight: 600; line-height: 1;
}
.acct-avatar svg { width: 15px; height: 15px; }
.nav-dd-menu--acct { left: auto; right: 0; transform: none; width: 248px; min-width: 0; }
.acct-hd { padding: 8px 10px 9px; }
.acct-hd-name { font-size: 14.5px; font-weight: 500; color: var(--ink); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.acct-hd-note { margin: 3px 0 0; font-size: 12.5px; color: var(--muted); }
.acct-sep { height: 1px; margin: 5px 4px; background: var(--line); }
.acct-note { margin: 4px 10px 8px; font-size: 12.5px; line-height: 1.5; color: var(--muted); }
.nav-dd-menu--acct .acct-item {
  display: flex; align-items: center; gap: 10px; width: 100%; min-height: 36px;
  padding: 0 10px; border: 0; border-radius: var(--r-s); background: none;
  font: 500 14.5px/1.3 var(--f-body); color: var(--ink); text-align: left;
  text-decoration: none; white-space: nowrap; cursor: pointer;
}
.nav-dd-menu--acct .acct-item:hover { background: var(--sunk); color: var(--ink); }
.nav-dd-menu--acct .acct-item span { overflow: hidden; text-overflow: ellipsis; }
.acct-ico { flex: none; width: 16px; height: 16px; color: var(--muted); }

/* The compact site menu. nav.js creates `.nav-more` on every page and moves the site
   links into it below 1100px, so this file hides it above that width and paints it below.
   The breakpoint is nav.js's own, character for character: two spellings of one breakpoint
   is a width at which the menu and its stylesheet disagree about whether it exists. */
.topbar .nav-more { display: none; }
.topbar .nav-more > summary {
  width: 36px; height: 36px; padding: 0; border: 1px solid var(--line); border-radius: 999px;
  background: var(--surface); color: var(--muted); display: none; place-items: center;
}
.topbar .nav-more > summary::after { content: none; }
.topbar .nav-more > summary:hover { background: var(--sunk); color: var(--ink); }
.nav-more-ico { width: 16px; height: 16px; display: block; }
.topbar .nav-more > .nav-dd-menu { left: 0; transform: none; }
@media (max-width: 1099.98px) {
  .topbar .nav-more { display: block; }
  .topbar .nav-more > summary { display: grid; }
  /* Inside the menu the site links are rows, not pills in a row. */
  .topbar .nav-more .nav-dd-menu > a.nav-app,
  .topbar .nav-more .nav-dd-menu > a.navsite {
    padding: 9px 12px; border-radius: var(--r-s); font-size: 14.5px; background: none;
  }
  .topbar .nav-more .nav-dd-menu > a.nav-app:hover,
  .topbar .nav-more .nav-dd-menu > a.navsite:hover { background: var(--sunk); color: var(--ink); }
  /* YOU-ARE-HERE SURVIVES THE MOVE INTO THE MENU. The rule above sets `background: none`
     on every row, and the you-are-here rule further up is written as a CHILD of
     `.nav-links` -- which the row stops being the moment nav.js reparents it into the
     compact menu. So the mark has to be restated here, or the one width at which a reader
     has the least other context about which page they are on is the width that stops
     telling them. It only ever applies on the app page, which is the only page whose
     `Analyse` row carries aria-current. Guard:
     tests/test_analyse_nav_link.py::test_on_a_phone_the_link_is_inside_the_compact_menu_with_its_pill. */
  .topbar .nav-more .nav-dd-menu > a[aria-current] { color: var(--accent); background: var(--accent-soft); }
}

/* ---- footer ---- */
.foot { border-top: 1px solid var(--line); padding-block: 56px 40px; margin-top: 96px; color: var(--muted); font-size: 14px; }
.foot-grid { display: grid; grid-template-columns: 1.4fr repeat(3, 1fr); gap: 32px; }
.foot h4 { font: 600 12px/1 var(--f-mono); letter-spacing: .08em; text-transform: uppercase; color: var(--ink); margin: 0 0 14px; }
.foot ul { list-style: none; margin: 0; padding: 0; display: grid; gap: 8px; }
.foot a { color: var(--muted); text-decoration: none; }
.foot a:hover { color: var(--ink); }
.foot-note { margin-top: 14px; max-width: 32ch; }
.foot-base { margin-top: 40px; padding-top: 20px; border-top: 1px solid var(--line); display: flex; flex-wrap: wrap; gap: 8px 24px; justify-content: space-between; }
@media (max-width: 800px) { .foot-grid { grid-template-columns: 1fr 1fr; } .foot-grid > :first-child { grid-column: 1 / -1; } }

/* ---- page shell: reading column + sticky contents rail ---- */
.docgrid { display: grid; grid-template-columns: minmax(0, 1fr) 250px; gap: 0 56px; align-items: start; }
.doccol { min-width: 0; grid-column: 1; grid-row: 1; padding-block: 34px 0; }
.pagetop { margin-bottom: 18px; }

/* The live pages keep their own <section class="page-head"> / <section class="prose">
   wrappers and the .wrap inside each, untouched: <main> is the document this redesign did
   not edit. Here they stop being layout and become plain blocks, so .doc owns the column. */
.doc > section { margin: 0; padding: 0; background: none; border: 0; }
.doc .wrap { max-width: none; margin: 0; padding-inline: 0; }

/* ---- the reading column ---- */
.doc { padding-bottom: 32px; }
.doc h1 { font-size: clamp(30px, 3.7vw, 44px); margin-bottom: 18px; }
.doc .lead { font-size: 19px; line-height: 1.55; color: var(--muted); max-width: 62ch; }
.doc .lead a { color: var(--accent); }
/* The section level is h2, except on the pages whose own outline starts at h3 (versions,
   privacy, data-handling, dpa). Those carry .doc-h3 on <main>, which is a class on the
   element and not an edit inside it. */
.doc h3 { font-size: 19px; margin: 32px 0 10px; }
.doc h2, .doc-h3 h3 {
  font-size: clamp(21px, 2vw, 26px); margin: 54px 0 14px; max-width: 52ch;
}
.doc h4 { font-size: 16.5px; margin: 26px 0 8px; letter-spacing: -.02em; }
.doc h2, .doc h3, .doc h4, .doc [id] { scroll-margin-top: 92px; }
.doc p { max-width: 72ch; margin: 0 0 16px; font-size: 16.5px; line-height: 1.75; }
.doc p + p { margin-top: -2px; }
.doc ul, .doc ol { max-width: 72ch; margin: 0 0 18px; padding-left: 22px; }
.doc li { margin-bottom: 10px; line-height: 1.7; font-size: 16.5px; }
.doc li::marker { color: var(--accent); }
.doc b, .doc strong { font-weight: 600; color: var(--ink); }
.doc i, .doc em { font-style: italic; }
.doc a { text-decoration: underline; text-decoration-thickness: 1px; }

/* Section numbers, added only where the page's own headings carry none. terms.html and
   dpa.html number their own clauses in the text ("1. Research use only"), so they do not
   get .numbered and no second number appears in front of theirs. */
.doc.numbered { counter-reset: sec; }
.doc.numbered h2, .doc.numbered.doc-h3 h3 { counter-increment: sec; position: relative; }
.doc.numbered h2::before, .doc.numbered.doc-h3 h3::before {
  content: counter(sec);
  font: 500 14px/1.9 var(--f-mono); color: var(--accent);
  letter-spacing: 0; display: block; margin-bottom: 4px;
}

/* ---- inline bits ---- */
.doc code {
  font-family: var(--f-mono); font-size: .86em; background: var(--sunk);
  border: 1px solid color-mix(in srgb, var(--line) 60%, transparent);
  border-radius: 5px; padding: .08em .35em; overflow-wrap: anywhere;
}
.doc .micro { font-size: 13.5px; line-height: 1.6; color: var(--muted); max-width: 72ch; }
.doc .micro b { color: var(--muted); }
.doc .pmute { font-size: 13.5px; line-height: 1.6; color: var(--muted); }
.doc p.pmute { margin-bottom: 0; }
.doc time { font-variant-numeric: tabular-nums; }
span.role { font: 500 11.5px/1 var(--f-mono); color: var(--accent); vertical-align: super; }

/* maths: methods.html only (KaTeX typesets it; these are the pre-render styles) */
.formula {
  font-family: var(--f-mono); font-size: 13px; line-height: 1.7; color: var(--ink);
  white-space: pre-wrap; overflow-x: auto; overflow-wrap: anywhere;
  background: var(--sunk); border: 1px solid var(--line); border-left: 3px solid var(--accent);
  border-radius: var(--r-m); padding: 14px 16px; margin: 4px 0 20px; max-width: 100%;
}
.formula .katex { font-family: KaTeX_Main, "Times New Roman", serif; }
ol.refs { max-width: 74ch; margin: 6px 0 0; padding-left: 28px; font-size: 14.5px; color: var(--muted); }
.doc ol.refs li { margin-bottom: 10px; line-height: 1.6; font-size: 14.5px; }
ol.refs li::marker { font-family: var(--f-mono); font-size: 12.5px; color: var(--accent); }

/* ---- callouts ---- */
.ruo-callout {
  margin: 4px 0 26px; padding: 15px 18px; font-size: 15.5px; line-height: 1.6;
  color: var(--ink); background: var(--warn-soft);
  border: 1px solid color-mix(in srgb, var(--warn) 35%, transparent);
  border-left: 3px solid var(--warn); border-radius: var(--r-m);
}
.ruo-callout strong { color: var(--warn); }
.note {
  margin: 22px 0 28px; padding: 18px 22px; border-radius: var(--r-l);
  background: var(--accent-soft); border: 1px solid color-mix(in srgb, var(--accent) 28%, transparent);
}
.note > strong {
  display: block; font: 500 12px/1 var(--f-mono); letter-spacing: .08em;
  text-transform: uppercase; color: var(--accent); margin-bottom: 12px;
}
.doc .note ul { margin: 0 0 12px; padding-left: 20px; max-width: none; }
.doc .note li { font-size: 15.5px; line-height: 1.6; margin-bottom: 7px; }
.doc .note .micro { max-width: none; }

/* ---- the page's own Contents panel (.toc / .toc-h), which is page text ----------------
   THE CLASS NAMES STAY .toc / .toc-h (owner ruling, 2026-09-10: one boxed panel, shared,
   so all the pages that carry one move together; and nav.js folds `details.toc[open]` on a
   phone by that selector). The rule lives HERE, in the shared sheet, and not in a per-page
   <style> block: a per-page rule is exactly how these pages lost the boxed panel once.
   Guard: tests/test_legal_pages_contents_panel.py. */
.toc {
  display: block; background: var(--surface); border: 1px solid var(--line);
  border-radius: var(--r-l); padding: 18px 22px 20px; margin: 26px 0 10px;
  box-shadow: var(--shadow);
}
.toc-h {
  font: 500 12px/1 var(--f-mono); letter-spacing: .08em; text-transform: uppercase;
  color: var(--muted); margin-bottom: 14px;
}
details.toc > summary.toc-h { cursor: pointer; margin-bottom: 0; list-style: none; }
details.toc > summary.toc-h::-webkit-details-marker { display: none; }
details.toc > summary.toc-h::after {
  content: ""; display: inline-block; width: 6px; height: 6px; margin-left: 8px;
  border-right: 1.5px solid currentColor; border-bottom: 1.5px solid currentColor;
  transform: rotate(45deg) translateY(-2px); opacity: .6;
}
details.toc[open] > summary.toc-h { margin-bottom: 14px; }
details.toc[open] > summary.toc-h::after { transform: rotate(225deg) translateY(-1px); }
.doc .toc ol, .doc .toc ul {
  margin: 0; padding-left: 24px; columns: 2; column-gap: 40px; max-width: none;
}
.doc .toc li { margin: 0 0 9px; break-inside: avoid; font-size: 15px; line-height: 1.5; }
.toc li::marker { color: var(--muted); font-family: var(--f-mono); font-size: 12.5px; }
.toc a { color: var(--ink); text-decoration: none; }
.toc a:hover { color: var(--accent); text-decoration: underline; }

/* ---- tables ----------------------------------------------------------------
   The card scrolls, never the page: max-width keeps it inside the reading column and
   overflow-x lets a wide table move inside it. Prose tables stay at 100% and wrap; the
   weights table has nine columns and a 64-hex digest, so it gets max-content and scrolls.
   Below 700px every table switches to max-content: four crushed columns on a phone are
   less readable than a table you push sideways. */
.tbl-card {
  border: 1px solid var(--line); border-radius: var(--r-l); background: var(--surface);
  overflow-x: auto; overscroll-behavior-x: contain; scrollbar-width: thin;
  margin: 6px 0 24px; box-shadow: var(--shadow); max-width: 100%;
}
.ltable { width: 100%; border-collapse: collapse; font-size: 14.5px; }
.ltable th, .ltable td { text-align: left; padding: 11px 16px; vertical-align: top; line-height: 1.6; }
.ltable thead th {
  font: 500 11.5px/1.4 var(--f-mono); letter-spacing: .06em; text-transform: uppercase;
  color: var(--muted); white-space: nowrap; border-bottom: 1px solid var(--line);
  background: var(--sunk);
}
.ltable tbody tr + tr td { border-top: 1px solid color-mix(in srgb, var(--line) 70%, transparent); }
.ltable tbody tr:nth-child(even) { background: color-mix(in srgb, var(--sunk) 55%, transparent); }
.ltable tbody td:first-child { font-weight: 600; }
/* The auto table algorithm hands the first column its min-content width, which on a
   four-column table with one long prose column breaks a name like "HuBMAP ASCT+B tables"
   one word per line. A floor on the first column costs the prose columns little. */
.ltable th:first-child, .ltable td:first-child { min-width: 14ch; }
.dl-table th:first-child, .dl-table td:first-child { min-width: 0; }
.ltable td.role { color: var(--muted); }
.ltable td.lic { color: var(--ink); font-weight: 500; font-size: 13.5px; }
.ltable td .pmute { display: inline-block; }
.dl-table { width: max-content; min-width: 100%; font-size: 13.5px; }
.dl-table th, .dl-table td { max-width: 40ch; }
.dl-digest { font-size: 11.5px; word-break: break-all; color: var(--muted); }

/* weights.html's two pre-rendered Download buttons. Not faded, because the reason they do
   nothing is not the reader's (nothing is published to point them at yet) and a faded
   button tells them nothing. weights.js fills the row when an index.json is published. */
.pbtn {
  display: inline-flex; align-items: center; gap: 6px; padding: 6px 13px;
  border: 1px solid var(--line); border-radius: 999px; background: var(--sunk);
  color: var(--ink); font: 500 13px/1 var(--f-body); text-decoration: none; white-space: nowrap;
}
.pbtn[aria-disabled="true"] { cursor: default; }
.doc a.pbtn, .doc a.dl { text-decoration: none; }
.doc a.dl {
  display: inline-flex; align-items: center; gap: 8px; padding: 9px 16px;
  border: 1px solid var(--line); border-radius: 999px; background: var(--surface);
  color: var(--ink); font-weight: 600; font-size: 15px;
}
.doc a.dl:hover { border-color: var(--accent); color: var(--accent); }

/* ---- code and licence blocks ---- */
.doc pre {
  font-family: var(--f-mono); font-size: 13px; line-height: 1.7; color: var(--ink);
  background: var(--sunk); border: 1px solid var(--line); border-left: 3px solid var(--accent);
  border-radius: var(--r-m); padding: 15px 17px; margin: 6px 0 22px;
  max-width: 100%; overflow-x: auto; overscroll-behavior-x: contain; scrollbar-width: thin;
}
.doc pre code { background: none; border: 0; padding: 0; font-size: inherit; white-space: pre; }
.doc pre.licence-text {
  white-space: pre-wrap; overflow-wrap: anywhere; font-size: 12.5px; color: var(--muted);
  border-left-color: var(--line);
}

/* ---- one card per published reference (versions.html) ---- */
.ver-ref {
  display: block; background: var(--surface); border: 1px solid var(--line);
  border-radius: var(--r-m); padding: 14px 18px 16px; margin: 0 0 10px; max-width: 72ch;
}
.doc .ver-ref h4 { margin: 0 0 8px; font-size: 16px; }
.ver-ref .lic {
  font: 500 11.5px/1.6 var(--f-mono); letter-spacing: .02em; padding: 2px 8px;
  border-radius: 999px; background: var(--accent-soft); color: var(--accent);
  vertical-align: 2px; margin-left: 6px;
}
.doc .ver-ref p { margin-bottom: 6px; font-size: 15px; line-height: 1.65; }
.doc .ver-ref p.role { color: var(--muted); font-size: 13.5px; }

/* ---- sticky "On this page" rail (chrome, desktop only) ---- */
.side { grid-column: 2; grid-row: 1; position: sticky; top: 92px; padding-block: 40px 0; }
.side-h {
  font: 500 12px/1 var(--f-mono); letter-spacing: .08em; text-transform: uppercase;
  color: var(--muted); margin-bottom: 12px; padding-left: 13px;
}
.side nav { max-height: calc(100vh - 190px); overflow-y: auto; scroll-behavior: auto; }
.side ol { list-style: none; counter-reset: t; margin: 0; padding: 0; border-left: 1px solid var(--line); }
.side li { counter-increment: t; }
.side a {
  display: flex; gap: 8px; padding: 5px 10px 5px 12px; margin-left: -1px;
  border-left: 2px solid transparent; color: var(--muted);
  font-size: 13.5px; line-height: 1.45; text-decoration: none;
}
.side.numbered a::before { content: counter(t); font: 500 11px/1.7 var(--f-mono); color: var(--line); flex: none; }
.side li.sub { counter-increment: none; }
.side li.sub a { padding-left: 26px; font-size: 12.5px; }
.side.numbered li.sub a::before { content: none; }
.side a:hover { color: var(--ink); }
.side a.here { color: var(--accent); border-left-color: var(--accent); font-weight: 600; }
.side.numbered a.here::before { color: var(--accent); }

/* ---- back to top ---- */
.totop {
  position: fixed; right: 20px; bottom: 20px; z-index: 20;
  opacity: 0; visibility: hidden; transition: opacity .18s, visibility .18s;
  box-shadow: var(--shadow); background: var(--surface);
}
.totop.on { opacity: 1; visibility: visible; }

@media (max-width: 1099px) {
  .docgrid { grid-template-columns: minmax(0, 1fr); }
  .side { display: none; }
}
@media (max-width: 700px) {
  .doc .toc ol, .doc .toc ul { columns: 1; }
  .toc { padding: 16px 18px; }
  .doc h2, .doc-h3 h3 { margin-top: 42px; }
  .ltable { width: max-content; min-width: 100%; }
  .ltable th, .ltable td { padding: 10px 12px; max-width: 34ch; }
  .note { padding: 15px 16px; }
  .totop { right: 12px; bottom: 12px; }
}
@media (max-width: 420px) { .topbar-inner { gap: 12px; } }
/* large desktop monitors: a wider column and a slightly larger base size */
@media (min-width: 1800px) {
  :root { --wrap: 1360px; }
  body { font-size: 17px; }
  .docgrid { grid-template-columns: minmax(0, 1fr) 290px; }
}

/* ============================================================================
   STAGE 2, PART 1 (2026-09-29): THE LANDING PAGE (web/index.html) AND THE APP
   PAGE (web/app.html).

   WHAT MOVED. Until today web/index.html WAS the app: a four-step wizard whose
   default view was a "How it works" panel. The app is web/app.html now, styled
   below from the same tokens the text pages use, and index.html is the landing
   page the design calls for. Nothing about the app's BEHAVIOUR changed with it:
   same four steps in the same order, same controls, same API calls, same gates,
   same words. This file is the whole stylesheet for both pages -- neither loads
   styles.css or layout-app.css any more -- which is why the app section below
   styles the class names walkthrough.js already writes (.panel, .pbtn, .chk,
   .mod-extra-*, .confirm-*, .cr-*, .sc-*) instead of renaming them. A renamed
   hook is a JS edit; a restyled hook is not.

   DARK MODE IS STILL OFF: both pages ship <meta name="color-scheme"
   content="light"> and neither sets .theme-auto, so the dark block at the top of
   this file stays dormant. No theme toggle on either page (the design mock had
   one; the site does not).
   ============================================================================ */

/* Two additions to the token set, for states the text pages never had. Nothing
   above this line changes. */
:root {
  /* A HARD ERROR, which the design has no colour for: its palette answers
     "something needs your attention" with --warn (amber). A failed submit, a
     column answer the file contradicts and an expired upload are not warnings,
     they are refusals, and the app has said so in red since it shipped. Kept to
     one token so there is one red. */
  --danger: #b3261e;
  --danger-soft: #fdeceb;
}

/* Shared components the design draws on both pages and stage 1 did not need. */
.chip {
  display: inline-flex; align-items: center; gap: 6px; padding: 4px 10px; border-radius: 999px;
  background: var(--sunk); color: var(--ink); font-size: 13px; line-height: 1.4; border: 1px solid transparent;
}
.chip i { width: 8px; height: 8px; border-radius: 50%; flex: none; }
.tag {
  display: inline-block; padding: 2px 8px; border-radius: 999px; font: 500 11.5px/1.6 var(--f-mono);
  letter-spacing: .02em; background: var(--accent-soft); color: var(--accent);
}
.tag-warn { background: var(--warn-soft); color: var(--warn); }
.btn .arr { transition: transform .15s; }
.btn:hover .arr { transform: translateX(3px); }
@media (prefers-reduced-motion: reduce) { .btn:hover .arr { transform: none; } }

/* The landing page's own bar entries: the four section links reuse `.navsite`
   (stage 1's plain site link, which nav.js also folds into the compact menu on a
   phone), and the app's door is a filled pill rather than a text link. It is NOT
   a `.signin`, so nav.js's straight-to-the-hosted-form handler leaves it alone. */
.topbar .nav-links > a.nav-start { margin-left: 4px; }
/* IT SHRINKS ON A TABLET AND GOES ON A PHONE. Measured at 320px: brand + menu + Sign in +
   this pill is 383px of content in a 320px viewport, and a landing page that scrolls
   sideways is the one failure a reader cannot work around. Below 420px the hero's "Start
   for free" is the call to action and the footer carries "New analysis"; the bar keeps the
   two controls a stranger needs, which are the brand and the way in. */
@media (max-width: 520px) {
  .topbar .nav-links > a.nav-start { margin-left: 0; padding: 0 12px; font-size: 13.5px; }
}
@media (max-width: 419.98px) { .topbar .nav-links > a.nav-start { display: none; } }
/* SIGNED IN, THE AVATAR IS THE LAST THING ON THE RIGHT (owner, 2026-09-29), the usual SaaS
   order: signed out it is Sign in, then the filled door; signed in it is the door, then the
   account menu. The markup carries that order (door before menu), so the door takes the
   push to the right once Sign in is hidden, and the menu sits beside it. On a phone the
   door is hidden (above), so the menu takes the push back and stays at the right edge. */
[data-auth="in"] .topbar .nav-links > a.nav-start,
[data-auth="blocked"] .topbar .nav-links > a.nav-start { margin-left: auto; }
.topbar .nav-links > a.nav-start + .nav-acct { margin-left: 8px; }
@media (max-width: 419.98px) { .topbar .nav-links > a.nav-start + .nav-acct { margin-left: auto; } }

/* ---------------------------------------------------------------- landing: hero */
.hero { padding-block: 56px 72px; }
.hero-grid { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1.08fr); gap: 56px; align-items: center; }
.hero h1 { font-size: clamp(34px, 5.6vw, 70px); margin-top: 18px; }
.hero h1 em { font-style: normal; color: var(--accent); }
.hero-lede { font-size: 18.5px; color: var(--muted); margin-top: 22px; max-width: 34em; }
.hero-cta { display: flex; flex-wrap: wrap; gap: 12px; margin-top: 32px; }
.hero-offer { margin-top: 16px; font-size: 15px; color: var(--muted); }
.hero-offer b { color: var(--accent); font-weight: 600; }
.hero-fine { margin-top: 14px; font-size: 13.5px; color: var(--muted); display: flex; flex-wrap: wrap; gap: 6px 18px; }
.hero-fine span { display: inline-flex; align-items: center; gap: 6px; }
.hero-fine svg { color: var(--accent); flex: none; }

/* the instrument panel: a simulated cell map that labels itself */
.scope {
  margin: 0;
  background: var(--surface); border: 1px solid var(--line); border-radius: var(--r-l);
  box-shadow: var(--shadow); overflow: hidden; min-width: 0;
}
.scope-head {
  display: flex; align-items: center; gap: 10px; padding: 12px 16px; border-bottom: 1px solid var(--line);
  font: 500 12.5px/1.3 var(--f-mono); color: var(--muted); flex-wrap: wrap;
}
.scope-head b { color: var(--ink); font-weight: 500; }
.scope-state { margin-left: auto; display: inline-flex; align-items: center; gap: 6px; }
.scope-state i { width: 7px; height: 7px; border-radius: 50%; background: var(--accent); }
.scope-state.busy i { animation: pulse 1s ease-in-out infinite; background: var(--violet); }
@keyframes pulse { 50% { opacity: .25; } }
@media (prefers-reduced-motion: reduce) { .scope-state.busy i { animation: none; } }
.scope-body { display: grid; grid-template-columns: minmax(0, 1fr) 190px; }
.scope-canvas { position: relative; min-height: 340px; max-width: 100%; }
.scope-canvas canvas { position: absolute; inset: 0; width: 100%; height: 100%; display: block; cursor: crosshair; }
.scope-axis { position: absolute; left: 14px; bottom: 10px; font: 500 10.5px/1 var(--f-mono); color: var(--muted); letter-spacing: .06em; }
.tip {
  position: absolute; pointer-events: none; z-index: 2; min-width: 170px;
  background: var(--ink); color: var(--bg); border-radius: var(--r-s); padding: 8px 10px;
  font-size: 12.5px; line-height: 1.35; transform: translate(12px, -50%);
}
.tip b { display: block; font-weight: 600; }
.tip span { font-family: var(--f-mono); font-size: 11.5px; opacity: .8; }
/* `.legend-types`, not `.legend`: the app page below has a `.legend` of its own
   (the reference's cell-type chips) and one name for two components in one
   stylesheet is how a rule ends up on the wrong element. */
.legend-types { border-left: 1px solid var(--line); padding: 10px 8px; display: flex; flex-direction: column; gap: 1px; overflow: auto; }
.legend-types button {
  all: unset; box-sizing: border-box; display: flex; align-items: center; gap: 8px; width: 100%;
  padding: 5px 8px; border-radius: var(--r-s); font-size: 12.5px; line-height: 1.25; cursor: pointer; color: var(--ink);
}
.legend-types button:hover, .legend-types button.on { background: var(--sunk); }
.legend-types button:focus-visible { outline: 2px solid var(--accent); }
.legend-types i { width: 9px; height: 9px; border-radius: 50%; flex: none; }
.legend-types small { margin-left: auto; font: 11px var(--f-mono); color: var(--muted); }
.scope-foot {
  display: flex; align-items: center; gap: 12px; padding: 10px 16px; border-top: 1px solid var(--line);
  font-size: 12.5px; color: var(--muted); flex-wrap: wrap;
}
.scope-foot .btn { margin-left: auto; }
@media (max-width: 980px) { .hero-grid { grid-template-columns: 1fr; gap: 40px; } }
@media (max-width: 560px) {
  .scope-body { grid-template-columns: 1fr; }
  .scope-canvas { min-height: 0; aspect-ratio: 1 / 1; }
  .legend-types { border-left: 0; border-top: 1px solid var(--line); flex-direction: row; flex-wrap: wrap; }
  .legend-types button { width: auto; }
  .legend-types small { display: none; }
}

/* ------------------------------------------------------- landing: figures strip */
.figures { border-block: 1px solid var(--line); background: var(--surface); }
.figures-grid { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); }
.fig { padding: 26px 24px; border-left: 1px solid var(--line); }
.fig:first-child { border-left: 0; padding-left: 0; }
.fig strong { display: block; font: 600 34px/1 var(--f-display); letter-spacing: -.02em; font-variant-numeric: tabular-nums; }
.fig span { display: block; margin-top: 8px; color: var(--muted); font-size: 14px; line-height: 1.4; }
@media (max-width: 820px) {
  .figures-grid { grid-template-columns: 1fr 1fr; }
  .fig:nth-child(3) { border-left: 0; padding-left: 0; }
  .fig:nth-child(n+3) { border-top: 1px solid var(--line); }
}

/* ----------------------------------------------------------- landing: sections */
section.block { padding-top: 104px; scroll-margin-top: 64px; }
.block-head { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); gap: 24px 56px; align-items: end; margin-bottom: 44px; }
.block-head h2 { font-size: clamp(28px, 3.6vw, 44px); margin-top: 14px; }
.block-head p { color: var(--muted); font-size: 17px; max-width: 34em; }
@media (max-width: 820px) { .block-head { grid-template-columns: 1fr; } }

/* the four steps: a real sequence, so they are numbered. `.step-card`, not the
   design's `.step`: `.step` is the app's stepper button in the same sheet. */
.steps { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 16px; counter-reset: step; }
.step-card { position: relative; padding: 22px 22px 24px; background: var(--surface); border: 1px solid var(--line); border-radius: var(--r-m); }
.step-card::before {
  counter-increment: step; content: counter(step);
  display: grid; place-items: center; width: 30px; height: 30px; border-radius: 50%;
  background: var(--accent-soft); color: var(--accent); font: 600 14px/1 var(--f-mono);
}
.step-card h3 { font-size: 20px; margin-top: 18px; }
.step-card p { color: var(--muted); font-size: 15px; margin-top: 8px; }
.step-card .mono { display: block; margin-top: 16px; padding-top: 12px; border-top: 1px dashed var(--line); color: var(--ink); font-size: 12.5px; }
@media (max-width: 980px) { .steps { grid-template-columns: 1fr 1fr; } }
@media (max-width: 560px) { .steps { grid-template-columns: 1fr; } }

/* what you can ask: a scan list, not a card wall */
.outcomes { display: grid; grid-template-columns: 1fr 1fr; column-gap: 56px; border-top: 1px solid var(--line); }
.oc { display: grid; grid-template-columns: 28px minmax(0, 1fr); align-content: start; gap: 4px 14px; padding: 20px 0; border-bottom: 1px solid var(--line); }
.oc svg { grid-row: span 2; color: var(--accent); margin-top: 2px; }
.oc h3 { font: 600 17px/1.3 var(--f-body); letter-spacing: 0; }
.oc p { color: var(--muted); font-size: 15px; }
@media (max-width: 820px) { .outcomes { grid-template-columns: 1fr; } }

/* sample report */
.sample { display: grid; grid-template-columns: minmax(0, .8fr) minmax(0, 1.2fr); gap: 56px; align-items: center; }
.sample h2 { font-size: clamp(28px, 3.6vw, 44px); margin-top: 14px; }
.sample > div > p { color: var(--muted); font-size: 17px; margin-top: 16px; }
.sample dl { display: grid; grid-template-columns: 1fr 1fr; gap: 0; margin: 28px 0; border-top: 1px solid var(--line); }
.sample dl div { padding: 14px 0; border-bottom: 1px solid var(--line); }
.sample dl div:nth-child(odd) { padding-right: 16px; }
.sample dt { font-size: 13px; color: var(--muted); }
/* THE FIGURES ARE THE TEXT FACE, NOT THE MONO ONE (owner, 2026-09-29): a price or
   a count on this site is read, not aligned in a column of code. Tabular digits
   keep the four cells from jittering. */
.sample dd { margin: 2px 0 0; font: 600 22px/1.2 var(--f-display); letter-spacing: -.02em; font-variant-numeric: tabular-nums; }
.frame { border-radius: var(--r-l); border: 1px solid var(--line); background: var(--surface); box-shadow: var(--shadow); overflow: hidden; }
.frame-bar { display: flex; align-items: center; gap: 6px; padding: 10px 14px; border-bottom: 1px solid var(--line); }
.frame-bar i { width: 10px; height: 10px; border-radius: 50%; background: var(--line); }
.frame-bar span { margin-left: 10px; font: 12px var(--f-mono); color: var(--muted); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.frame img { width: 100%; height: auto; background: #fff; }
@media (max-width: 900px) { .sample { grid-template-columns: 1fr; } }

/* atlases */
.atlas-tools { display: flex; flex-wrap: wrap; gap: 12px; align-items: center; margin-bottom: 20px; }
.seg { display: inline-flex; padding: 3px; background: var(--sunk); border-radius: 999px; }
.seg button {
  all: unset; padding: 7px 14px; border-radius: 999px; font-size: 14px; font-weight: 500; color: var(--muted); cursor: pointer;
}
.seg button[aria-pressed="true"] { background: var(--surface); color: var(--ink); box-shadow: 0 1px 2px rgb(0 0 0 / .08); }
.seg button:focus-visible { outline: 2px solid var(--accent); }
.search {
  flex: 1 1 220px; max-width: 320px; min-height: 40px; padding: 0 14px; border-radius: 999px;
  border: 1px solid var(--line); background: var(--surface); color: var(--ink); font: inherit; font-size: 14.5px;
}
.atlas-count { margin-left: auto; font: 13px var(--f-mono); color: var(--muted); }
.atlases { display: grid; grid-template-columns: repeat(auto-fill, minmax(210px, 1fr)); gap: 12px; }
.atlas-none { color: var(--muted); }
.atlas {
  all: unset; box-sizing: border-box; cursor: pointer; display: flex; flex-direction: column; gap: 6px;
  padding: 16px; background: var(--surface); border: 1px solid var(--line); border-radius: var(--r-m);
  transition: border-color .15s;
}
.atlas:hover { border-color: var(--muted); }
.atlas:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.atlas[aria-pressed="true"] { border-color: var(--accent); box-shadow: 0 0 0 1px var(--accent); }
.atlas-top { display: flex; justify-content: space-between; gap: 8px; align-items: baseline; }
.atlas b { font: 600 17px/1.2 var(--f-display); letter-spacing: -.01em; }
.atlas small { color: var(--muted); font-size: 12.5px; }
.atlas .mono { color: var(--ink); font-size: 13px; }
.stripe { display: flex; height: 6px; border-radius: 3px; overflow: hidden; margin-top: 8px; }
.stripe i { flex: 1; }
.atlas-detail {
  margin-top: 16px; padding: 24px; background: var(--surface); border: 1px solid var(--line); border-radius: var(--r-m);
  display: grid; grid-template-columns: minmax(0, 1fr) auto; gap: 16px 32px;
}
.atlas-detail h3 { font-size: 22px; }
.atlas-detail p { color: var(--muted); margin-top: 6px; max-width: 60ch; }
.atlas-detail .chips { grid-column: 1 / -1; display: flex; flex-wrap: wrap; gap: 6px; }
@media (max-width: 560px) {
  .atlases { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 8px; }
  .atlas { min-width: 0; overflow-wrap: anywhere; padding: 12px; }
  .atlas b { font-size: 15px; }
}
@media (max-width: 640px) { .atlas-detail { grid-template-columns: 1fr; } .atlas-count { margin-left: 0; } }

/* your data */
.trust { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 32px; }
.trust h3 { font: 600 17px/1.3 var(--f-body); letter-spacing: 0; margin-top: 14px; }
.trust p { color: var(--muted); font-size: 15px; margin-top: 6px; }
.trust svg { color: var(--violet); }
@media (max-width: 900px) { .trust { grid-template-columns: 1fr 1fr; } }
@media (max-width: 520px) { .trust { grid-template-columns: 1fr; } }

/* q&a: the live copy, click a question to open its answer */
.hiw-qa { max-width: 820px; margin-top: 20px; border-top: 1px solid var(--line); }
.hiw-qa-item { border-bottom: 1px solid var(--line); }
.hiw-q {
  list-style: none; cursor: pointer; display: flex; justify-content: space-between; gap: 16px;
  padding: 20px 0; font: 600 17.5px/1.35 var(--f-body);
}
.hiw-q::-webkit-details-marker { display: none; }
.hiw-q::after {
  content: ""; flex: none; width: 22px; height: 22px; margin-top: 1px; border-radius: 50%; background: var(--sunk)
    url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 22 22'%3E%3Cpath d='M7 11h8M11 7v8' stroke='%2355655f' stroke-width='1.6' stroke-linecap='round'/%3E%3C/svg%3E") center/100% no-repeat;
  transition: transform .2s;
}
.hiw-qa-item[open] .hiw-q::after { transform: rotate(45deg); }
.hiw-a { color: var(--muted); padding: 0 40px 22px 0; max-width: 72ch; }
.hiw-a a { color: var(--accent); }
.hiw-a code { font-family: var(--f-mono); font-size: .88em; }

/* closing band */
.closing {
  margin-top: 104px; border-radius: var(--r-l); padding: clamp(28px, 5vw, 64px);
  background: var(--ink); color: var(--bg);
  display: grid; grid-template-columns: minmax(0, 1fr) auto; gap: 24px 48px; align-items: center;
}
.closing h2 { font-size: clamp(26px, 3.4vw, 42px); }
.closing p { opacity: .75; margin-top: 10px; max-width: 44ch; }
.closing .btn-primary { --b-bg: var(--bg); --b-fg: var(--ink); border-color: var(--bg); }
.closing .btn-primary:hover { --b-bg: var(--accent-soft); border-color: var(--accent-soft); }
@media (max-width: 720px) { .closing { grid-template-columns: 1fr; } }

/* ============================================================================
   STAGE 2, PART 2: THE APP PAGE (web/app.html)

   SAME PAGE, SAME BEHAVIOUR, NEW CLOTHES. Every selector below aims at a class
   web/app.html ships or web/walkthrough.js writes -- .panel, .panel-h,
   .panel-foot, .pbtn, .ctx-field, .mod-item/.chk, .mod-extra-*, .drop,
   .confirm-*, .autodetect, .rsum, .cr-*, .sc-*, .resume-*, .card-box -- so the
   look is a stylesheet swap and not a DOM edit. The step ORDER, the gating, the
   defaults and the wording are untouched (that is Release A's whole rule), and
   the folding/reordering in the design mock is Release B.

   THREE INVARIANTS THAT ARE BEHAVIOUR, NOT DECORATION, AND MUST SURVIVE ANY
   EDIT HERE:
     1. `[hidden]` HAS TO WIN. The `hidden` attribute works through a user-agent
        rule, and ANY author rule that sets `display` outranks it: that is how
        the drop zone stayed on screen beside a loaded file for two days in
        2026-08. Every class below that sets `display` on an element the page
        hides carries its own `.cls[hidden] { display: none }`. The derived guard
        is tests/test_hidden_actually_hides.py, which resolves the cascade rather
        than grepping for the rule.
     2. `.run-hint:empty { display: none }`. updateLoadHint() writes textContent,
        so an empty hint must take no space -- and `.foot-next { margin-left:
        auto }` keeps Next at the right edge whatever else the row holds (owner,
        2026-09-16).
     3. `.ref-info[aria-disabled="true"]` stays FULL strength and dead, not
        faded: the reason it does nothing is ours, not the reader's.
   ============================================================================ */

/* ---- the page frame ---- */
.app { padding-block: 32px 24px; }
.page-h { margin-bottom: 26px; }
.page-h h1 { font-size: clamp(28px, 3.4vw, 40px); }
.page-h p { color: var(--muted); margin-top: 8px; font-size: 16.5px; }
/* No card around the wizard (owner, 2026-09-03): the steps are the page, not a
   box floating on it. The stepper is the rule under the title; each step's panel
   is its own card below it. */
.app-box { min-width: 0; }

/* THE STEPPER AND THE PRE-PAINT RULES ARE GONE (Release B, owner ruling B1, 2026-09-29;
   docs/decisions.md 0ao). There are no switched panels to paint and no step to restore: the
   three cards are all on the page at once, and which of them is open is a function of the
   work done rather than of a saved index. What went with them: `.stepper`, `.step`, `.sdot`,
   `.sconn`, `.slabel`, every pre-paint rule keyed on the attribute boot.js used to stamp on
   <html>, and boot.js itself. `.step-cta` / `.step-ctas` stay: the two links they style moved
   to the page header, which is the one thing the strip carried that was not the strip. */


.panel-h { font: 600 21px/1.25 var(--f-display); letter-spacing: -.02em; color: var(--ink); margin: 0 0 16px; }
.panel-h-row { display: flex; align-items: baseline; justify-content: space-between; gap: 12px; }
.panel-h-row .panel-h { margin: 0 0 16px; }
.panel-foot { display: flex; align-items: center; justify-content: space-between; gap: 12px; margin-top: 24px; flex-wrap: wrap; }
/* NEXT IS PINNED RIGHT AND NEVER MOVES (owner, 2026-09-16): space-between spreads
   whatever the row happens to hold, and step 3's gate note came and went with the
   confirmation, dragging the button across. The note has its own line (.foot-hint). */
.panel-foot > .foot-next { margin-left: auto; }
.foot-hint { margin: 22px 0 0; }

/* ---- buttons inside the app (.pbtn is walkthrough.js's name for the design's .btn) ---- */
.pbtn {
  display: inline-flex; align-items: center; justify-content: center; gap: 8px;
  min-height: 44px; padding: 0 20px; border-radius: 999px;
  border: 1px solid var(--line); background: var(--surface); color: var(--ink);
  font: 600 15px/1 var(--f-body); text-decoration: none; white-space: nowrap; cursor: pointer;
  transition: background .15s, border-color .15s, transform .15s;
}
.pbtn:hover { border-color: var(--muted); }
.pbtn:active { transform: translateY(1px); }
.pbtn.primary { background: var(--accent); border-color: var(--accent); color: var(--accent-ink); }
.pbtn.primary:hover { background: var(--accent-hover); border-color: var(--accent-hover); }
.pbtn.ghost { background: transparent; border-color: transparent; color: var(--muted); }
.pbtn.ghost:hover { background: var(--sunk); border-color: transparent; color: var(--ink); }
.pbtn[disabled] { opacity: .45; cursor: not-allowed; }
/* `.pbtn` sets `display`, so it needs this or hiding it does nothing (invariant 1). */
.pbtn[hidden] { display: none; }
.pbtn.btn-sm, .pbtn.upcancel { min-height: 36px; padding: 0 14px; font-size: 14px; }
.pmute { color: var(--muted); font-size: 13.5px; line-height: 1.6; }
.pmute a { color: var(--accent); }
.muted { color: var(--muted); }
.feedback-note { margin-top: 18px; }
/* "Explore context": the same filled pill as Next, and deliberately NOT faded while
   disabled (invariant 3). */
.ref-info { text-decoration: none; white-space: nowrap; }
.ref-info[aria-disabled="true"] { cursor: default; pointer-events: none; }

/* ---- step 1: the biological context ---- */
.ctx-selectors { display: flex; flex-wrap: wrap; gap: 14px; margin-bottom: 18px; }
.ctx-field { display: flex; flex-direction: column; gap: 6px; min-width: 0; }
.ctx-field > span { font: 500 12px/1 var(--f-mono); letter-spacing: .08em; text-transform: uppercase; color: var(--muted); }
.ctx-field select {
  min-width: 190px; min-height: 42px; padding: 0 12px;
  border: 1px solid var(--line); border-radius: var(--r-s); background: var(--surface);
  color: var(--ink); font: inherit; font-size: 15px; cursor: pointer;
}
.ctx-field select:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
.ctx-privacy { margin: 0 0 4px; }
.ctx-body { margin-top: 18px; }
.legend-head { margin: 0 0 10px; font: 500 12px/1 var(--f-mono); letter-spacing: .08em; text-transform: uppercase; color: var(--muted); }
/* The reference's cell types: a spec sheet, each with the colour that type carries in
   the reference's own UMAP (build_reference_catalog.py::_umap_colors). */
.legend { display: flex; flex-wrap: wrap; gap: 6px; }
.lchip {
  display: inline-flex; align-items: center; gap: 7px; padding: 5px 10px;
  border: 1px solid var(--line); border-radius: 999px; background: var(--surface);
  color: var(--ink); font-size: 12.5px; line-height: 1.4;
}
.lchip .ld { width: 8px; height: 8px; border-radius: 50%; flex: none; }
#atlasLegend.legend { display: grid; grid-template-columns: repeat(auto-fill, minmax(190px, 1fr)); gap: 8px; margin: 0 0 18px; }
#atlasLegend.legend .lchip { border-radius: var(--r-s); white-space: normal; }
#atlasLegend.legend .ld { width: 9px; height: 9px; }

/* ---- step 2: the outcomes -------------------------------------------------
   THE ROWS ARE SWITCHES NOW, which is the design's language for "this is on".
   `.chk` is still walkthrough.js's element and `.chk.on` still its one state
   class -- only the drawing changed, from a tick box to a track and a knob. */
.outcomes-layout { display: grid; grid-template-columns: minmax(0, 1fr) 340px; gap: 18px; align-items: start; }
@media (max-width: 900px) { .outcomes-layout { grid-template-columns: 1fr; } }
.mod-toggle-all {
  background: none; border: 0; padding: 0; font: 600 13.5px/1 var(--f-body);
  color: var(--accent); cursor: pointer;
}
.mod-toggle-all:hover { text-decoration: underline; }
.mod-list { list-style: none; margin: 0; padding: 0; display: grid; gap: 8px; min-width: 0; }
.mod-item {
  display: flex; align-items: flex-start; gap: 14px; padding: 14px 16px; cursor: pointer;
  border: 1px solid var(--line); border-radius: var(--r-m); background: var(--bg);
  transition: border-color .15s, background .15s;
}
.mod-item:hover { border-color: var(--muted); }
.mod-item.active { background: var(--surface); }
.mod-item .chk {
  flex: none; position: relative; width: 36px; height: 22px; margin-top: 1px;
  border-radius: 999px; background: var(--line); transition: background .15s;
}
.mod-item .chk::after {
  content: ""; position: absolute; top: 3px; left: 3px; width: 16px; height: 16px;
  border-radius: 50%; background: var(--surface); box-shadow: 0 1px 2px rgb(0 0 0 / .25);
  transition: transform .15s;
}
.mod-item .chk.on { background: var(--accent); }
.mod-item .chk.on::after { transform: translateX(14px); }
.mod-item.active { border-color: color-mix(in srgb, var(--accent) 45%, var(--line)); }
.mod-item .mt { font-size: 15.5px; color: var(--ink); }
.mod-item.active .mt { font-weight: 600; }
@media (prefers-reduced-motion: reduce) { .mod-item .chk, .mod-item .chk::after { transition: none; } }

/* the detail box beside the list */
.mod-extra { border: 1px solid var(--line); border-radius: var(--r-m); padding: 16px; background: var(--bg); min-width: 0; }
.mod-extra-title { font: 600 15.5px/1.3 var(--f-body); color: var(--ink); margin: 0 0 6px; }
.mod-extra .pmute { margin: 0 0 12px; }
.mod-extra-sub { font-size: 13.5px; font-weight: 600; color: var(--ink); margin: 14px 0 8px; }
.mod-extra-note { font-size: 13px; }
.mod-extra-list { margin: 0 0 4px; padding-left: 18px; font-size: 14px; color: var(--muted); }
.mod-extra-list li { margin-bottom: 4px; }
.mod-extra-chips.legend { gap: 5px; margin-bottom: 2px; }
.mod-extra-chips .lchip { font-size: 12px; padding: 3px 8px; }
/* SUB-OPTIONS ARE SQUARE TICK CHIPS (the design's `.pill`). The markup is a label
   with a real checkbox inside it, so the box is the browser's, tinted with
   accent-color, and `:has()` lights the chip it sits in. A browser without `:has()`
   still gets a tick box in a chip, which is the same answer one shade quieter. */
.mod-extra-checklist { display: flex; flex-wrap: wrap; gap: 6px; margin-bottom: 6px; }
.mod-extra-toggle {
  display: inline-flex; align-items: center; gap: 8px; padding: 6px 12px;
  border: 1px solid var(--line); border-radius: 999px; background: var(--surface);
  font-size: 13.5px; line-height: 1.35; color: var(--ink); cursor: pointer;
}
.mod-extra-toggle input[type=checkbox] { flex: none; width: 14px; height: 14px; accent-color: var(--accent); cursor: pointer; }
.mod-extra-toggle:has(input:checked) { border-color: var(--accent); background: var(--accent-soft); color: var(--accent); }
.mod-extra-toggle:has(input:focus-visible) { outline: 2px solid var(--accent); outline-offset: 2px; }
.mod-extra-toggle.disabled { opacity: .55; cursor: not-allowed; }
.mod-extra-toggle.disabled input { cursor: not-allowed; }
.mod-extra-toggle .dep-req { margin-left: 4px; }
.mod-extra-toggle .dep-link { color: var(--accent); cursor: pointer; border-bottom: 1px dotted currentColor; }
/* ONE-OF-N (the label mode). The design draws this as a segmented control; here each
   option keeps its own note UNDER it (two options, two different notes, and one of them
   explains why the option is unavailable), which a single segmented track has no room
   for. So: selectable rows in the same pill language, and the radio stays visible
   because it is what says "one of these". A segmented control with the notes moved out
   is a markup change in walkthrough.js and belongs to Release B. */
.mod-extra-mode { margin: 4px 0; }
.mod-extra-radio {
  display: inline-flex; align-items: center; gap: 8px; margin: 10px 0 2px; padding: 6px 12px;
  border: 1px solid var(--line); border-radius: 999px; background: var(--surface);
  font: 500 13.5px/1.35 var(--f-body); color: var(--ink); cursor: pointer;
}
.mod-extra-radio input { flex: none; accent-color: var(--accent); cursor: pointer; }
.mod-extra-radio:has(input:checked) { border-color: var(--accent); background: var(--accent-soft); color: var(--accent); }
.mod-extra-radio:has(input:focus-visible) { outline: 2px solid var(--accent); outline-offset: 2px; }
.mod-extra-radio.disabled { opacity: .55; cursor: not-allowed; }
.mod-extra-radio.disabled input { cursor: not-allowed; }
.mod-extra-radio-note { font-size: 13px; margin: 0 0 6px 26px; }
.mod-extra-field { margin-top: 10px; }
.mod-extra-field label { display: block; font-size: 13.5px; font-weight: 600; color: var(--ink); margin-bottom: 8px; }
.mod-extra-field .opt-tag { font-weight: 400; color: var(--muted); }
.mod-extra-field textarea, .mod-extra-field input[type=text] {
  width: 100%; border: 1px solid var(--line); border-radius: var(--r-s); padding: 10px 12px;
  background: var(--surface); color: var(--ink); font: inherit; font-size: 14.5px; box-sizing: border-box;
}
.mod-extra-field textarea { min-height: 90px; resize: vertical; }
.mod-extra-file { margin-top: 6px; }
.mod-extra-drop {
  display: block; border: 1.5px dashed var(--line); border-radius: var(--r-s); padding: 14px;
  text-align: center; font-size: 13.5px; color: var(--muted); cursor: pointer; background: var(--surface);
}
.mod-extra-drop:hover { border-color: var(--accent); color: var(--accent); }
.mod-extra-filename { font-size: 13px; margin: 6px 0 0; }

/* dependency + file-side notes on an outcome row. `.dep-req` is hidden on an unlocked
   row and `.obs-req` is the one that stays: the rules and their order are styles.css's,
   carried over unchanged, because order is what decides them. */
.mod-item.dep-locked { cursor: default; }
.mod-item.dep-locked:hover { border-color: var(--line); }
.mod-item.dep-locked .chk { opacity: .45; }
.mod-item.dep-locked .mt { color: var(--muted); }
.dep-req { margin-left: auto; font-size: 12.5px; color: var(--muted); white-space: nowrap; }
.mod-item:not(.dep-locked) .dep-req { display: none; }
.dep-req .dep-link { color: var(--accent); cursor: pointer; text-decoration: none; border-bottom: 1px dotted currentColor; }
.mod-item:not(.dep-locked) .obs-req:not([hidden]) { display: inline; }
.obs-req.obs-soft { font-style: italic; }
/* chosen, and cannot run: the row stays where the customer put it, struck through,
   with the reason on it */
.mod-item.cannot-run .mt, .mod-extra-toggle.cannot-run { text-decoration: line-through; text-decoration-thickness: 1px; color: var(--muted); }
.cannot-run-why {
  margin-left: 6px; font-size: 12.5px; color: var(--warn); background: var(--warn-soft);
  border: 1px solid color-mix(in srgb, var(--warn) 35%, transparent); border-radius: 999px;
  padding: 1px 8px; text-decoration: none; display: inline-block;
}

/* ---- step 3: the file ---- */
.drop {
  display: grid; place-items: center; gap: 10px; text-align: center; padding: 32px 20px;
  border: 1.5px dashed var(--line); border-radius: var(--r-m); background: var(--bg);
  color: var(--muted); cursor: pointer; transition: border-color .15s, background .15s;
}
.drop[hidden] { display: none; }
.drop.drag, .drop:active { border-color: var(--accent); background: var(--accent-soft); }
.drop .upic { width: 26px; height: 26px; stroke: var(--accent); fill: none; stroke-width: 1.6; stroke-linecap: round; stroke-linejoin: round; }
.drop .drop-txt { font-size: 16px; color: var(--ink); font-weight: 600; }
.drop .drop-txt code { color: var(--accent); font-family: var(--f-mono); }
.dropbtn {
  display: inline-flex; align-items: center; justify-content: center; min-height: 40px;
  padding: 0 18px; border: 1px solid var(--accent); border-radius: 999px;
  background: var(--accent); color: var(--accent-ink); font: 600 14.5px/1 var(--f-body);
  cursor: pointer; transition: background .15s;
}
.dropbtn:hover { background: var(--accent-hover); border-color: var(--accent-hover); }
.dropbtn:active { transform: translateY(1px); }
.dropbtn:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.dropbtn[hidden] { display: none; }
.dropbtn--ghost { background: var(--surface); color: var(--ink); border-color: var(--line); }
.dropbtn--ghost:hover { background: var(--sunk); border-color: var(--muted); }
.uprow { display: flex; align-items: center; gap: 12px; }
.uprow .upbar { flex: 1 1 auto; }
.upbar { height: 6px; margin-top: 12px; border-radius: 999px; background: var(--sunk); 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.
   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% { transform: translateX(-100%); } 100% { transform: translateX(285%); } }
@media (prefers-reduced-motion: reduce) { .upbar--busy i { animation: none; width: 75%; opacity: .55; } }
.upcancel { flex: none; margin-top: 12px; }
.drop-notes { margin-top: 12px; display: grid; gap: 6px; }
.drop-formats { margin: 0; font-size: 13px; color: var(--muted); }
.drop-formats code { color: var(--ink); font-family: var(--f-mono); font-size: .9em; }
.drop-formats a { color: var(--accent); }
/* the loaded file, which REPLACES the drop zone: one state, two views */
.loaded-file {
  display: flex; align-items: center; gap: 14px; padding: 14px 16px;
  border: 1px solid var(--line); border-radius: var(--r-m); background: var(--bg);
}
.loaded-file .upic { width: 24px; height: 24px; flex: none; stroke: var(--accent); fill: none; stroke-width: 1.6; stroke-linecap: round; stroke-linejoin: round; }
.loaded-name { flex: 1; min-width: 0; font-size: 15px; font-weight: 600; overflow-wrap: anywhere; }
.loaded-state { display: block; font: 400 12.5px/1.5 var(--f-mono); color: var(--muted); }
.loaded-acts { display: flex; gap: 8px; flex: none; }

/* the two disclosure boxes (earlier uploads, and the data citation) share one shell */
.prev-uploads, .cite-box {
  border: 1px solid var(--line); border-radius: var(--r-m); padding: 16px 18px; background: var(--surface);
}
.prev-uploads { margin-top: 16px; max-height: 24rem; overflow-y: auto; }
.cite-box { margin: 16px 0; }
.cite-box[hidden] { display: none; }
.prev-uploads > summary, .cite-box > summary {
  display: flex; align-items: center; justify-content: space-between; gap: 10px;
  cursor: pointer; font: 600 14.5px/1.3 var(--f-body); color: var(--ink); 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; width: 6px; height: 6px; margin-right: 4px;
  border-right: 1.5px solid currentColor; border-bottom: 1.5px solid currentColor;
  transform: rotate(45deg) translateY(-2px); opacity: .6; transition: transform .15s;
}
.prev-uploads[open] > summary::after, .cite-box[open] > summary::after { transform: rotate(225deg) translateY(-1px); }
.prev-uploads[open] > summary, .cite-box[open] > summary { margin-bottom: 8px; }
.cite-box .mod-extra-field textarea { min-height: 0; }
.cite-note { margin: 6px 0 0; font-size: 12.5px; color: var(--muted); }

/* earlier uploads / a run being resumed */
.resume-banner {
  border: 1px solid var(--line); border-left: 3px solid var(--accent); border-radius: var(--r-m);
  padding: 12px 14px; margin-bottom: 14px; font-size: 14px; background: var(--surface);
  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(--ink); margin-bottom: 4px; }
.resume-row { display: flex; align-items: center; justify-content: space-between; gap: 10px; padding: 8px 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: 12.5px; font-weight: 400; }
/* TWO LINES ON THE LEFT, ACTIONS ON THE RIGHT (owner, 2026-09-08). The row never wraps:
   the left column may shrink and break a long filename mid-word, so Delete keeps its
   place at any width (guarded in tests/test_phone_layout.py). */
.prev-uploads .resume-row { flex-wrap: nowrap; padding: 10px 0; }
.resume-main { min-width: 0; overflow-wrap: anywhere; }
.resume-name { display: block; font-weight: 600; color: var(--ink); font-size: 14.5px; }
.resume-meta { display: block; margin-top: 2px; font: 400 12.5px/1.5 var(--f-mono); color: var(--muted); }
.resume-acts { display: flex; align-items: center; gap: 12px; flex: none; }
.resume-act { background: none; border: 0; padding: 0; font: 600 13.5px/1 var(--f-body); color: var(--accent); cursor: pointer; }
.resume-act:hover { text-decoration: underline; }
.resume-del { color: var(--muted); }
.resume-exp { color: var(--muted); }
.resume-exp--gone { color: var(--danger); font-weight: 600; }

/* confirm the columns: picker left, the file's own values right */
.confirm-cols { max-width: 780px; }
.confirm-title { font: 600 15px/1.3 var(--f-body); margin: 18px 0 10px; }
.confirm-subtitle { font-size: 13.5px; font-weight: 600; margin: 16px 0 8px; }
.confirm-field { display: block; margin: 0 0 12px; }
.confirm-field span { display: block; font-size: 13.5px; font-weight: 600; margin-bottom: 6px; }
.confirm-field select, .confirm-field input[type=text] {
  width: 100%; min-height: 40px; border: 1px solid var(--line); border-radius: var(--r-s);
  padding: 0 10px; background: var(--surface); color: var(--ink); font: inherit; font-size: 14.5px; box-sizing: border-box;
}
.confirm-field input[type=text] { padding: 9px 10px; }
/* A PICKER THAT IS SHOWN BUT NOT LIVE. The covariate pickers stay on the page while the
   column they depend on is missing (owner, 2026-09-10): hiding them hid the only place
   the customer could see what to change. Keyed on :disabled, the attribute that actually
   stops the input, so a row cannot look locked while accepting a choice. */
.confirm-field select:disabled, .confirm-field input[type=text]:disabled {
  background: var(--sunk); color: var(--muted); cursor: not-allowed;
}
.confirm-row { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1.05fr); column-gap: 18px; align-items: center; margin: 0 0 12px; }
.confirm-row .confirm-field { margin: 0; }
.confirm-row .col-preview { margin: 0; }
.col-preview { margin: 2px 0 0; font: 400 12px/1.5 var(--f-mono); color: var(--muted); overflow-wrap: anywhere; }
/* A BOUGHT OPINION, marked as ours: tinted so it cannot be mistaken for something read
   out of the file the way the value preview is. */
.col-proposal { margin: 2px 0 8px; font-size: 13px; line-height: 1.5; color: var(--ink); background: var(--bg); border-left: 2px solid var(--accent); padding: 4px 10px; }
.col-proposal b { color: var(--ink); }
.col-proposal--none { border-left-color: var(--line); color: var(--muted); }
/* What is lost by leaving a role unset. NOT muted: it is the one line that changes which
   analyses run. */
.col-consequence {
  margin: 4px 0 0; padding: 8px 11px; border-radius: var(--r-s);
  border: 1px solid color-mix(in srgb, var(--warn) 35%, transparent); background: var(--warn-soft);
  color: var(--warn); font-size: 13px; line-height: 1.5;
}
.confirm-note { font-size: 13px; margin: 4px 0 10px; }
.confirm-ok { color: var(--accent); font-weight: 600; margin-bottom: 0; }
.confirm-err { color: var(--danger); font-weight: 600; margin-bottom: 0; }
.confirm-updated { color: var(--accent); font-weight: 600; }
.cov-unused { color: var(--muted); }
.confirm-actions { display: flex; flex-wrap: wrap; gap: 10px; margin-top: 12px; }
.confirm-align { border-top: 1px solid var(--line); padding-top: 10px; margin-bottom: 12px; }
.confirm-alignrow { display: flex; align-items: flex-start; gap: 8px; font-size: 14px; margin: 6px 0; }
.confirm-alignrow input { margin-top: 3px; flex: none; accent-color: var(--accent); }
/* THE OFFER, on its own ground under the title: Confirm and Reset are things the customer
   does with their own answers, and this is something we sell them. */
.autodetect {
  display: flex; flex-wrap: wrap; align-items: center; gap: 10px;
  background: var(--accent-soft); border: 1px solid color-mix(in srgb, var(--accent) 28%, transparent);
  border-radius: var(--r-m); padding: 14px 16px; margin: 0 0 18px;
}
.autodetect-lead { flex: 1 1 100%; margin: 0; font-size: 14px; line-height: 1.5; color: var(--ink); }
.autodetect-price { font-size: 13.5px; color: var(--ink); font-weight: 600; font-variant-numeric: tabular-nums; }
.autodetect-bal { font-weight: 400; color: var(--muted); }
.autodetect-off { flex: 1 1 100%; margin: 0; font-size: 13px; }
.autodetect-note { flex: 1 1 100%; margin: 0; font-size: 13px; line-height: 1.5; }
.autodetect-note--ok { color: var(--accent); }
.autodetect-note--err { color: var(--danger); font-weight: 600; }
/* bring-your-own labels (optional) */
.byo { margin-top: 16px; padding-top: 16px; border-top: 1px solid var(--line); }
.byo-toggle, .byo-check { display: inline-flex; align-items: center; gap: 8px; font-size: 14.5px; color: var(--ink); cursor: pointer; }
.byo-toggle input, .byo-check input { width: 15px; height: 15px; accent-color: var(--accent); cursor: pointer; flex: none; }
.byo-check.disabled { color: var(--muted); cursor: not-allowed; }
.byo-fields { margin-top: 12px; padding-left: 23px; }
.byo-field { display: flex; align-items: center; gap: 10px; font-size: 14px; color: var(--muted); }
.byo-field code { color: var(--accent); font-family: var(--f-mono); }
.byo-field input {
  flex: 1; max-width: 250px; min-height: 38px; padding: 0 11px; border: 1px solid var(--line);
  border-radius: var(--r-s); font: inherit; font-size: 14px; color: var(--ink); background: var(--surface);
}
.byo-field input:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
.byo-help { margin: 8px 0 12px; font-size: 13px; color: var(--muted); }
.byo-sub { color: var(--muted); font-size: .9em; }

/* ---- step 4: the run ------------------------------------------------------- */
/* THREE LABELLED ROWS, ON THE PRICE BOX'S OWN SURFACE (owner, 2026-09-12): reference,
   dataset, outcomes, each value saying what it is. `.credits-bar` directly below is the
   quiet ground with a hairline and the medium radius, and a white shadowed card sitting on
   top of it read as a different kind of object. No shadow: neither box is lifted. The two
   are pinned to each other in tests/test_run_summary_box.py so they cannot drift apart. */
.run-sum { margin: 0 0 18px; }
.run-sum .rsum {
  border: 1px solid var(--line);
  border-radius: var(--r-m);
  background: var(--bg);
  box-shadow: none;
  padding: 4px 16px;
}
/* Label column then value. A FIXED label column, not `auto`: the three labels differ in
   width, and an auto column re-aligns the values every time the Dataset row comes and goes
   (it is absent until a file is loaded). `minmax(0, 1fr)` is what lets a long file name
   shrink instead of pushing the box wider than the panel. */
.rsum-row { display: grid; grid-template-columns: 168px minmax(0, 1fr); gap: 4px 18px; align-items: baseline; padding: 11px 0; }
.rsum-row + .rsum-row { border-top: 1px solid var(--line); }
.rsum-k { font: 500 11px/1.6 var(--f-mono); letter-spacing: .08em; text-transform: uppercase; color: var(--muted); }
/* The name is already middle-truncated by refreshRunSummary(), so this is the second line
   of defence, for the narrow phone rather than the long name. */
.rsum-v { font-size: 15px; color: var(--muted); min-width: 0; overflow-wrap: anywhere; }
.rsum-v b { color: var(--ink); font-weight: 600; }
/* THE PRICE BOX. Money is the text faces with tabular digits, never the mono face
   (owner, 2026-09-29): a price is read, not aligned with code. */
.credits-bar {
  display: flex; flex-wrap: wrap; align-items: center; gap: 8px 18px; margin-top: 18px;
  padding: 16px 18px; border: 1px solid var(--line); border-radius: var(--r-m);
  background: var(--bg); font-size: 14.5px; color: var(--muted);
}
.credits-bar[hidden] { display: none; }
.credits-bar .cr-cost b, .credits-bar .cr-bal b { color: var(--ink); font-weight: 700; font-variant-numeric: tabular-nums; }
.credits-bar .cr-bal { margin-left: auto; }
.credits-bar .cr-note { color: var(--muted); font-weight: 400; font-size: 13px; }
.credits-bar .cr-over { color: var(--danger); font-weight: 600; }
.cr-items { flex-basis: 100%; margin-top: 12px; width: 100%; font-size: 14px; }
.cr-item { display: flex; justify-content: space-between; gap: 16px; padding: 4px 0; color: var(--muted); font-variant-numeric: tabular-nums; }
.cr-item > span { min-width: 0; overflow-wrap: anywhere; }
.cr-item b { color: var(--ink); font-weight: 600; white-space: nowrap; font-variant-numeric: tabular-nums; }
.cr-items .cr-item > span { color: var(--ink); }
/* (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 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); }
.cr-items .cr-item.cr-item--note span { color: inherit; }
.cr-items .cr-item .cr-note { font-size: inherit; }
.cr-items .cr-item.cr-item--omitted span { text-decoration: line-through; text-decoration-thickness: 1px; color: var(--muted); }
.cr-item--omitted .cr-note { text-decoration: none; }
.cr-item--sub { padding-left: 20px; color: var(--muted); }
.cr-item--fixed { color: var(--muted); }
.cr-item--note {
  color: var(--warn); background: var(--warn-soft); border-radius: var(--r-s);
  border: 1px solid color-mix(in srgb, var(--warn) 35%, transparent); padding: 7px 10px; margin-bottom: 6px;
}
.cr-item--fixed + .cr-item--total, .cr-item--fixed:first-of-type { border-top: 1px solid var(--line); }
.cr-item--total { margin-top: 8px; padding-top: 9px; border-top: 1px solid var(--line); font-weight: 600; }
.cr-item--total b { font: 600 20px/1.2 var(--f-display); letter-spacing: -.02em; font-variant-numeric: tabular-nums; }
.cr-tag { font-size: 12.5px; color: var(--muted); background: var(--sunk); border: 1px solid var(--line); border-radius: var(--r-s); padding: 1px 7px; white-space: nowrap; }
.cr-tag--shared { font-style: italic; }
.cr-how { flex-basis: 100%; margin-top: 14px; font-size: 13px; }
.cr-how > summary { cursor: pointer; color: var(--muted); list-style: none; width: max-content; }
.cr-how > summary::-webkit-details-marker { display: none; }
.cr-how > summary::after { content: " ▾"; color: var(--muted); }
.cr-how[open] > summary { color: var(--ink); }
.cr-how[open] > summary::after { content: " ▴"; }
.cr-how > summary:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; border-radius: 4px; }
.cr-how-b { margin-top: 8px; max-width: 62ch; line-height: 1.6; color: var(--muted); }
.cr-how-b b { color: var(--ink); font-weight: 600; }
.run-hint { color: var(--danger); font-size: 13.5px; line-height: 1.6; }
/* updateLoadHint() writes textContent, so an empty hint takes no space (invariant 2) */
.run-hint:empty { display: none; }
.run-hint--omitted {
  margin: 10px 0 0; padding: 9px 12px; border-radius: var(--r-s); line-height: 1.5;
  border: 1px solid color-mix(in srgb, var(--warn) 35%, transparent); background: var(--warn-soft); color: var(--warn);
}
/* the submit success card, which takes the price box's place: 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 */
.submit-card {
  margin: 0 0 18px; padding: 18px 20px; border-radius: var(--r-m);
  border: 1px solid color-mix(in srgb, var(--accent) 30%, transparent); background: var(--accent-soft);
}
.sc-head { display: flex; align-items: center; gap: 9px; margin: 0; font: 600 17px/1.3 var(--f-body); color: var(--accent); }
.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: 10px 0 0; font-size: 14.5px; color: var(--muted); overflow-wrap: anywhere; }
.sc-sum b { color: var(--ink); font-weight: 600; }
.sc-note { margin: 10px 0 0; font-size: 14.5px; line-height: 1.6; color: var(--ink); }
.sc-note a { color: var(--accent); }
.sc-acts { display: flex; flex-wrap: wrap; align-items: center; gap: 10px; margin-top: 16px; }
.sc-acts a.pbtn { text-decoration: none; }
.sc-again { background: var(--surface); }
/* the run status line, and the one gate box the app can put in front of a step */
.progress { height: 7px; border-radius: 999px; 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: 14.5px; }
.status-stage .ic { width: 18px; height: 18px; border-radius: 50%; border: 1.5px solid var(--line); flex: none; display: inline-flex; align-items: center; justify-content: center; }
.status-stage.done { color: var(--ink); }
.status-stage.done .ic { background: var(--accent); border-color: var(--accent); }
.status-stage.active { color: var(--ink); font-weight: 600; }
.card-box { border: 1px solid var(--line); border-radius: var(--r-l); background: var(--surface); padding: 22px; box-shadow: var(--shadow); }
.card-box h3 { margin: 0 0 14px; font-size: 19px; }
.terms-check { display: flex; align-items: flex-start; gap: 9px; font-size: 14.5px; color: var(--ink); cursor: pointer; line-height: 1.5; }
.terms-check input { margin-top: 3px; accent-color: var(--accent); }
/* the warn callout the two /inspect verdicts use (raw counts, donors per condition). It
   sets no `display`, so `hidden` still hides it. */
.note {
  margin: 16px 0; padding: 14px 16px; border-radius: var(--r-m); font-size: 14.5px; line-height: 1.6;
  background: var(--accent-soft); border: 1px solid color-mix(in srgb, var(--accent) 28%, transparent); color: var(--ink);
}
.note.warn { background: var(--warn-soft); border-color: color-mix(in srgb, var(--warn) 35%, transparent); color: var(--ink); }
.note strong { color: inherit; }

/* ---- the app on a narrow screen ---- */
@media (max-width: 620px) {
  .rsum-row { grid-template-columns: minmax(0, 1fr); gap: 2px; }
  .run-sum .rsum { padding: 2px 14px; }
  .confirm-row { grid-template-columns: minmax(0, 1fr); row-gap: 6px; }
}

/* ============================================================================
   STAGE 2, PART 2 (RELEASE B, 2026-09-29): THE APP'S NEW FLOW.

   Ten owner rulings, recorded in docs/decisions.md 0ao-0ax. What changes here is
   the SHAPE of the app page: three question cards in one column instead of four
   switched panels, and a sticky run box beside them instead of a fourth step.
   Everything inside a card is the markup walkthrough.js already writes (.drop,
   .confirm-*, .mod-*, .cr-*, .sc-*), styled by the section above and untouched
   here -- the same rule that section states: a renamed hook is a JS edit, a
   restyled hook is not.

   Taken from the approved prototype (varnaops-redesign/design/web-redesign-2026-09/
   app.html) and mapped onto the token set above rather than copied with its own
   colours, so there is still one palette on this site.
   ============================================================================ */

/* ---- the two-column page (form + run box) ---- */
.app-layout {
  display: grid; grid-template-columns: minmax(0, 1fr) 340px; gap: 26px 36px;
  align-items: start; padding-block: 8px 72px;
}
.app-layout > .page-h { grid-column: 1 / -1; }
#formCol { min-width: 0; }
.qcards { display: grid; gap: 18px; min-width: 0; }
/* The two secondary links that used to sit at the far end of the stepper. Same pills, on
   their own row under the title now that there is no strip to hold them. */
.app-ctas { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 14px; }

/* ---- a question card ---- */
.q {
  background: var(--surface); border: 1px solid var(--line); border-radius: var(--r-l);
  padding: 22px; scroll-margin-top: 80px; min-width: 0;
}
.q-head { display: flex; align-items: flex-start; gap: 14px; }
.q-num {
  flex: none; width: 30px; height: 30px; border-radius: 50%; display: grid; place-items: center;
  font: 600 13px/1 var(--f-mono); background: var(--sunk); color: var(--muted);
}
.q.done .q-num { background: var(--accent); color: var(--accent-ink); }
.q-head h2 { font: 600 21px/1.25 var(--f-display); letter-spacing: -.02em; }
.q-head .q-desc, .q-head .q-sum { color: var(--muted); font-size: 14.5px; margin-top: 4px; }
.q-body { margin-top: 18px; padding-left: 44px; min-width: 0; }
@media (max-width: 640px) { .q { padding: 16px; } .q-body { padding-left: 0; } }

/* ---- B2: a locked step says why, in one line ---- */
.q.locked { background: var(--sunk); border-style: dashed; }
.q.locked .q-head h2 { color: var(--muted); }
.q.locked .q-num { background: transparent; border: 1px dashed var(--line); }
.lock-hint {
  display: inline-flex; align-items: center; gap: 8px; margin-top: 8px;
  font: 500 12.5px/1.45 var(--f-mono); color: var(--muted);
}
.lock-hint svg { flex: none; }
.q.locked .q-desc { display: none; }
.q:not(.locked) .lock-hint { display: none; }
.q.locked .q-act { display: none; }

/* ---- B3: a finished step folds to one line, and Change opens it again ---- */
.q-sum { display: none; overflow-wrap: anywhere; }
.q.collapsed { padding-block: 15px; }
.q.collapsed .q-desc { display: none; }
.q.collapsed .q-sum { display: block; }
.q.collapsed .q-head h2 { font-size: 18px; }
.q-change { margin-left: auto; flex: none; align-self: center; }
.q-act { margin-left: auto; flex: none; align-self: center; white-space: nowrap; }
/* B5's reason is a second line of the row, not a second column of it: as a flex sibling of
   the title it squeezed "Analyze disease states and conditions" into five lines. */
/* A grid item's automatic minimum size is its MIN-CONTENT, so one long unbreakable phrase in
   a row sizes the whole list's column and the card scrolls sideways on a phone. The reason
   line B5 adds is exactly such a phrase. */
.mod-item { flex-wrap: wrap; min-width: 0; }
.mod-item > * { min-width: 0; overflow-wrap: anywhere; }
/* The title takes the rest of the FIRST line and wraps inside it. Without a flex basis it
   kept its one-line width, and because the row wraps (for the reason line below) a title
   longer than the space beside the switch dropped whole onto a line of its own. */
.mod-item .mt { flex: 1 1 0; }
.mod-item .obs-req {
  /* padding, not margin: a margin sits OUTSIDE the 100% and makes the row wider than its
     column. `normal` because .dep-req -- which this shares a class with, for the dependency
     hint that IS one short phrase -- sets nowrap, and B5's sentence is a sentence. */
  flex-basis: 100%; padding-left: 40px; white-space: normal; margin-left: 0;
}
.q.collapsed .q-num { font-size: 0; }
.q.collapsed .q-num::after { content: "\2713"; font-size: 14px; }
.q.flash { animation: qflash 1.2s ease-out; }
@keyframes qflash { 0%, 40% { box-shadow: 0 0 0 3px var(--accent); } 100% { box-shadow: 0 0 0 0 transparent; } }
@media (prefers-reduced-motion: reduce) { .q.flash { animation: none; outline: 2px solid var(--accent); } }
.step-next { display: flex; justify-content: flex-end; gap: 10px; margin-top: 16px; flex-wrap: wrap; }

/* ---- B7: species switch + search + one tile per atlas ---- */
.ctx-tools { display: flex; flex-wrap: wrap; gap: 10px; align-items: center; margin-bottom: 14px; }
.seg { display: inline-flex; padding: 3px; background: var(--sunk); border-radius: 999px; }
.seg button {
  appearance: none; border: 0; background: transparent; padding: 6px 14px; border-radius: 999px;
  font: 500 14px/1.4 var(--f-body); color: var(--muted); cursor: pointer;
}
.seg button[aria-pressed="true"] { background: var(--surface); color: var(--ink); box-shadow: 0 1px 2px rgb(13 27 24 / .08); }
.seg button:focus-visible { outline: 2px solid var(--accent); }
.ctx-search {
  flex: 1 1 200px; min-width: 0; min-height: 38px; padding: 0 14px; border-radius: 999px;
  border: 1px solid var(--line); background: var(--bg); color: var(--ink); font: inherit; font-size: 14.5px;
}
.tiles { display: grid; grid-template-columns: repeat(auto-fill, minmax(150px, 1fr)); gap: 8px; }
.tile {
  appearance: none; text-align: left; cursor: pointer; padding: 12px 14px; border-radius: var(--r-m);
  border: 1px solid var(--line); background: var(--bg); display: grid; gap: 2px; min-width: 0;
}
.tile:hover { border-color: var(--muted); }
.tile:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.tile[aria-checked="true"] { border-color: var(--accent); background: var(--accent-soft); box-shadow: 0 0 0 1px var(--accent); }
.tile b { font: 600 15px/1.3 var(--f-body); color: var(--ink); }
.tile span { font: 12px/1.4 var(--f-mono); color: var(--muted); }
.tiles-none { font-size: 14px; color: var(--muted); }
/* The tiles are replaced on every species switch; never let the browser pin the
   scroll to one of them. */
#atlasPick, #atlasPick * { overflow-anchor: none; }

/* ---- B6: the run box ---- */
.sum {
  position: sticky; top: 84px; background: var(--surface); border: 1px solid var(--line);
  border-radius: var(--r-l); box-shadow: var(--shadow); overflow: hidden; min-width: 0;
  transition: box-shadow .25s, border-color .25s;
}
.sum.reviewing, .sum.submitted { border-color: var(--accent); box-shadow: 0 0 0 1px var(--accent), var(--shadow); }
.sum-h { padding: 16px 18px 4px; }
.sum-h h2 { font-size: 18px; }
.sum-body { padding: 0 18px; }
.sum .rsum { gap: 0; }
.sum .rsum-row {
  display: grid; gap: 3px; padding: 11px 0; border-bottom: 1px solid var(--line);
}
.sum .rsum-k { font: 500 11px/1.4 var(--f-mono); letter-spacing: .08em; text-transform: uppercase; color: var(--muted); }
.sum .rsum-v { font-size: 14.5px; overflow-wrap: anywhere; }
/* In the run box each row is label ABOVE value: the run step's 168px label column left the
   value ~100px here and broke a file name mid-word. And no box inside the box. */
.sum .rsum-row { grid-template-columns: minmax(0, 1fr); }
.sum .rsum { border: 0; background: none; padding: 0; }
.sum .rsum-v .muted { color: var(--muted); }
.sum-go { padding: 14px 18px 18px; display: grid; gap: 9px; }
.sum-go[hidden] { display: none; }
.sum-go .pbtn, .sum-review .pbtn { width: 100%; }
.sum-why { font-size: 13px; color: var(--muted); }
.sum-why .todo {
  appearance: none; border: 0; background: transparent; padding: 0; cursor: pointer;
  font: inherit; color: var(--accent); text-decoration: underline; text-underline-offset: 3px;
}
.sum-stale { font-size: 13px; color: var(--warn); }
.sum-review { padding: 0 18px 18px; }
.sum-after { display: flex; justify-content: space-between; gap: 12px; font-size: 13.5px; color: var(--muted); margin-top: 10px; }
.sum-after[hidden] { display: none; }
.sum-after b { color: var(--ink); font-variant-numeric: tabular-nums; }
/* The itemisation, the summary line and the submit card are walkthrough.js's own
   markup (.cr-*, .sc-*), already styled above; these two rules only fit them to
   the narrower column. */
.sum .credits-bar { padding: 14px 18px; background: var(--bg); border-top: 1px solid var(--line); border-bottom: 0; }
.sum .submit-card { margin: 0 18px 18px; }

/* ---- the phone bar (the run box collapses into it) ---- */
.runbar { display: none; }
@media (max-width: 960px) {
  .app-layout { grid-template-columns: 1fr; padding-bottom: 116px; }
  .sum { position: static; }
  .runbar[hidden] { display: none; }
  .runbar {
    display: flex; align-items: center; gap: 12px; position: fixed; left: 0; right: 0; bottom: 0; z-index: 40;
    padding: 12px var(--gutter) calc(12px + env(safe-area-inset-bottom, 0px));
    background: var(--surface); border-top: 1px solid var(--line);
  }
  .runbar-txt { min-width: 0; font-size: 13px; color: var(--muted); line-height: 1.35; }
  .runbar-txt b { display: block; color: var(--ink); font-size: 14px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
  .runbar .pbtn { margin-left: auto; flex: none; }
}

/* ---- B10: a 'choose one' block is a segmented switch ---- */
.opt-seg { display: inline-flex; flex-wrap: wrap; gap: 2px; padding: 3px; background: var(--sunk); border-radius: 999px; }
.seg-o { position: relative; }
.seg-o input { position: absolute; opacity: 0; pointer-events: none; }
.seg-o span {
  display: inline-flex; padding: 6px 14px; border-radius: 999px; font: 500 13.5px/1.4 var(--f-body);
  color: var(--muted); cursor: pointer;
}
.seg-o input:checked + span { background: var(--surface); color: var(--ink); box-shadow: 0 1px 2px rgb(13 27 24 / .1); }
.seg-o input:disabled + span { opacity: .45; cursor: not-allowed; }
.seg-o input:focus-visible + span { outline: 2px solid var(--accent); outline-offset: 1px; }
.mode-hint { font-weight: 400; color: var(--muted); }
.mode-note { font-size: 13px; color: var(--muted); margin: 6px 0 0; }
.mode-note--blocked { color: var(--warn); }

/* ---- B5: an outcome the confirmed columns cannot support, and the switch-off note ---- */
.mod-item.dep-locked .obs-req { color: var(--warn); }
.switched-off {
  display: flex; gap: 12px; align-items: flex-start; margin-bottom: 12px; padding: 10px 14px;
  border-radius: var(--r-s); background: var(--warn-soft); color: var(--ink); font-size: 13.5px;
}
/* `hidden` has to beat the `display` above it, or the note is permanently on screen with
   nothing in it -- see tests/test_hidden_actually_hides.py, which derives this list. */
.switched-off[hidden] { display: none; }
.switched-off button {
  appearance: none; border: 0; background: transparent; margin-left: auto; cursor: pointer;
  color: var(--muted); font-weight: 600; line-height: 1;
}
/* The three visible role fields each say what they are for (B4, owner addition). */
.role-purpose { font-size: 13px; color: var(--muted); margin: 2px 0 0; }
.cov-fold { margin-top: 14px; }
.cov-fold > summary { cursor: pointer; font-weight: 600; font-size: 14px; }
