/* Palette and type scale lifted from docs/wireframes.html.
   System fonts rather than the wireframe's webfonts: the app binds localhost
   on a droplet and may have no outbound access, and a font that fails to load
   is worse than one that was never asked for. */

:root { --ink: #16181c;
  --paper: #f5f6f4;
  --panel: #ffffff;
  --sunk: #eceeeb;
  --rule: #c9cdc7;
  --rule-soft: #dfe2dd;
  --slate: #5c6360;
  --faint: #8b918b;
  --signal: #2f5d50;
  --signal-bg: #e3ebe7;
  --noise: #8e958d;
  --noise-bg: #eaece7;
  --flag: #a34a2a;
  --flag-bg: #f6e7e0;
  --sans: system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
  --mono: ui-monospace, "SF Mono", "Cascadia Mono", Menlo, monospace; }

* { box-sizing: border-box; }

body { margin: 0;
  background: var(--paper);
  color: var(--ink);
  font-family: var(--sans);
  font-size: 15px;
  line-height: 1.6; }

.wrap { max-width: 900px; margin: 0 auto; padding: 0 24px 80px; }

header.masthead { max-width: 900px;
  margin: 0 auto;
  padding: 40px 24px 18px;
  border-bottom: 1px solid var(--ink); }
header.masthead h1 { margin: 0 0 6px; font-size: 34px; letter-spacing: -0.03em; }
header.masthead p { margin: 0; color: var(--slate); max-width: 62ch; }

/* Wraps rather than scrolls. Five tabs at 375px overran the viewport by 84px
   -- the only thing on any screen that did, so the whole app scrolled sideways
   on a phone. A scroll container would have fixed the overflow and hidden two
   of the five screens behind a gesture nobody is told about; this is the
   primary navigation, so nothing in it may be off-screen. */
nav.tabs { display: flex; flex-wrap: wrap; gap: 2px; border-bottom: 1px solid var(--rule); background: var(--sunk); }
nav.tabs a, nav.tabs span { font-family: var(--mono);
  font-size: 10px;
  letter-spacing: 0.07em;
  text-transform: uppercase;
  color: var(--faint);
  padding: 9px 14px;
  text-decoration: none;
  border-right: 1px solid var(--rule-soft); }
/* `nav.tabs span` styles a tab that is not a link, and the step number is now
   also a span inside one -- so it inherited the tab's own padding and right
   border and rendered as a cell of its own. */
nav.tabs .step { padding: 0; border-right: 0; display: inline; }
nav.tabs .on { background: var(--panel); color: var(--signal); font-weight: 600; box-shadow: inset 0 -2px 0 var(--signal); }

.panel { background: var(--panel); border: 1px solid var(--rule); border-radius: 5px; padding: 20px; margin: 22px 0; }

/* Sentence case in the body font. These label prose, and mono uppercase made
   every caption read like console output -- the single biggest reason the app
   felt clinical. Mono is kept for numbers, where tabular figures earn it. */
.lbl { font-size: 11.5px; letter-spacing: 0.01em; color: var(--faint); font-weight: 500; }
.val { font-family: var(--mono); font-size: 19px; font-weight: 500; }
.mono { font-family: var(--mono); font-variant-numeric: tabular-nums; }

/* The separators are drawn by the tiles, not by a grey panel behind them.
   With the colour on the container, a row that does not fill -- four tiles in
   three columns, which is what 375px gives -- left two thirds of a row as a
   bare grey rectangle that reads as a tile that failed to load. A ring on each
   tile meets its neighbour's across the 1px gap, so the lines are identical
   and an unfilled cell shows nothing at all. */
.stats { display: grid; grid-template-columns: repeat(auto-fit, minmax(110px, 1fr)); gap: 1px; margin-bottom: 18px; }
.stats > div { background: var(--panel); padding: 11px 13px; box-shadow: 0 0 0 1px var(--rule-soft); }

.dropzone { background: var(--sunk);
  border: 1px dashed var(--rule);
  border-radius: 4px;
  padding: 28px;
  text-align: center;
  color: var(--slate); }
.dropzone.over { border-color: var(--signal); background: var(--signal-bg); }
.dropzone p { margin: 0 0 12px; }

/* Keep the file input reachable by assistive tech and by input.click(),
   while the visible affordance is a real button -- Safari renders a bare
   file input as a small control that reads as nothing on a phone. */
.visually-hidden { position: absolute;
  width: 1px; height: 1px;
  padding: 0; margin: -1px;
  overflow: hidden;
  clip: rect(0 0 0 0);
  white-space: nowrap;
  border: 0; }

.htmx-indicator { opacity: 0; transition: opacity 150ms; }
.htmx-request .htmx-indicator, .htmx-request.htmx-indicator { opacity: 1; }

/* Drag and drop does not exist on a touch device; do not advertise it. */
@media (hover: none) { .desktop-only { display: none; } }

button, .btn { font: inherit;
  font-size: 13px;
  font-weight: 500;
  padding: 7px 14px;
  border: 1px solid var(--ink);
  background: transparent;
  color: var(--ink);
  border-radius: 3px;
  cursor: pointer; }
button.pri { background: var(--ink); color: var(--paper); }
button[disabled] { opacity: 0.4; cursor: not-allowed; }

table { width: 100%; border-collapse: collapse; font-size: 13px; }
th { font-size: 11.5px; color: var(--faint); text-align: left; font-weight: 500; padding: 6px 8px 6px 0; border-bottom: 1px solid var(--rule); }
td { padding: 8px 8px 8px 0; border-bottom: 1px solid var(--rule-soft); color: var(--slate); }
td:first-child { color: var(--ink); }

.tag { font-size: 11px; font-weight: 500; padding: 3px 9px; border-radius: 3px; display: inline-block; white-space: nowrap; }
.t-measured { background: var(--signal-bg); color: var(--signal); }
.t-assumed { background: var(--flag-bg); color: var(--flag); }
.t-noise { background: var(--noise-bg); color: var(--noise); }

.note { border-left: 2px solid var(--flag); background: var(--flag-bg); padding: 11px 15px; font-size: 13.5px; color: #6e3116; }
/* Method, not alarm. The correction is how the page was built rather than
   something gone wrong with it, so it gets the neutral treatment and leaves the
   rust colouring to the cards it actually took away. */
.note.correction { border-left-color: var(--rule); background: var(--sunk); color: var(--slate); }
.note.correction p { margin: 0 0 6px; }
.note.correction p:last-child { margin-bottom: 0; }
/* The sentence that takes the finding away, on the card that lost it. */
p.unproven { margin: 4px 0 8px; font-size: 13.5px; color: var(--flag); }

/* Collapsed by default: the advice is worth having and worth not shouting.
   <details> because it needs no JS -- the guidance would otherwise be the one
   part of the page that vanishes on the browsers where scripts fail. */
details.guide { margin-top: 14px; }
details.guide > summary { cursor: pointer; font-size: 13px; color: var(--signal); }
details.guide[open] > summary { margin-bottom: 6px; }
details.guide table { margin-top: 10px; }
/* Secondary prose, not console output. It was set in the mono face at 11px,
   which is what made whole paragraphs of explanation read like a log tail --
   the same mistake as `.lbl`, one size down and across far more text. */
.quiet { color: var(--slate); font-size: 12.5px; line-height: 1.55; }

.bar { height: 3px; background: var(--sunk); margin: 8px 0; }
.bar > i { display: block; height: 100%; background: var(--signal); }

.row { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; }
.right { margin-left: auto; }
hr.hair { border: none; border-top: 1px solid var(--rule-soft); margin: 16px 0; }

/* --- 03 Findings ---------------------------------------------------------
   Three states have to be distinguishable at a glance without colour alone
   carrying the meaning: each also has a word in its tag and a different left
   border weight, so the page survives a monochrome print and a colour-blind
   reader. */

h2.section { margin: 34px 0 4px; font-size: 15px; letter-spacing: -0.01em; }
h2.section .count { font-family: var(--mono); font-size: 11px; color: var(--faint); margin-left: 6px; }
p.section-note { margin: 0 0 6px; font-size: 13px; }
.muted { color: var(--slate); }
.small { font-size: 12px; }
.empty { border-style: dashed; }
.big { font-size: 20px; line-height: 1.45; margin: 4px 0 14px; }

.headline { border-left: 4px solid var(--noise); }
.headline.is-moving { border-left-color: var(--signal); background: linear-gradient(var(--signal-bg), var(--panel) 120px); }
.verdict { font-size: 13px; letter-spacing: 0.01em; color: var(--signal); margin: 2px 0 0; font-weight: 600; }
.verdict.muted { color: var(--faint); }

/* Then and now. Descriptive only -- reporting/findings.py explains why it may
   never carry a verdict, and THEN_NOW_NOTE says so on the page itself. */
.thennow { display: flex; align-items: center; gap: 16px; flex-wrap: wrap; background: var(--sunk); border: 1px solid var(--rule-soft); border-radius: 4px; padding: 12px 16px; margin: 10px 0; }
.thennow .val { font-size: 26px; display: block; line-height: 1.1; }
.thennow .lbl { display: block; }
.thennow .muted { font-size: 11px; }
.thennow .arrow { font-size: 22px; color: var(--faint); }
.thennow .delta { margin-left: auto; text-align: right; font-family: var(--mono); font-size: 17px; font-variant-numeric: tabular-nums; }
.thennow.compact { padding: 8px 14px; margin: 8px 0; }
.thennow.compact .val { font-size: 19px; }
.thennow.quiet { background: transparent; border-style: dashed; color: var(--slate); }

.card { border-left: 4px solid var(--rule); padding: 16px 20px; margin: 10px 0; }
.card.is-moving { border-left-color: var(--signal); }
/* Between a finding and a non-finding, and coloured as neither. Reusing the
   signal green would put a card the correction refused in the same visual
   class as one it blessed, which is the confusion the fourth state exists to
   remove. */
.card.is-suggestive { border-left-color: var(--flag); }
.card.is-stable { border-left-color: var(--noise); }
/* Not a footnote. CLAUDE.md §1 makes "couldn't measure" a first-class result,
   displayed as prominently as a finding -- and a grey card on a sunk ground is
   the one treatment on the page that says the opposite. Same panel ground as
   every other state; slate rather than rule, so it reads as deliberate. */
.card.is-unmeasured { border-left-color: var(--slate); background: var(--panel); }
.cardhead { display: flex; align-items: flex-start; justify-content: space-between; gap: 12px; }
.cardhead > div { flex: 1 1 auto; min-width: 0; }
.cardhead img.region.big { order: -1; }
.cardhead h3 { margin: 0; font-size: 16px; letter-spacing: -0.01em; line-height: 1.3; }
.cardhead .area { font-size: 11px; color: var(--faint); }
.t-moving { background: var(--signal-bg); color: var(--signal); }
.t-suggestive { background: var(--flag-bg); color: var(--flag); }
.t-stable { background: var(--noise-bg); color: var(--noise); }
.t-unmeasured { background: var(--sunk); color: var(--faint); border: 1px solid var(--rule-soft); }

.anchor { margin: 6px 0 2px; font-size: 14px; color: var(--slate); font-style: italic; }
.magnitude { margin: 4px 0; font-family: var(--mono); font-size: 13px; font-variant-numeric: tabular-nums; }
.card p { }
.card p.muted { font-size: 13px; margin: 4px 0; }

.caveat { border-top: 1px solid var(--rule-soft); padding-top: 10px; margin-top: 14px; font-size: 13px; color: var(--slate); }
.badge { font-size: 10.5px; background: var(--flag-bg); color: var(--flag); padding: 2px 7px; border-radius: 3px; }

dl.detail { display: grid; grid-template-columns: minmax(120px, max-content) 1fr; gap: 4px 18px; margin: 10px 0 0; font-size: 13px; }
dl.detail dt { font-size: 12px; color: var(--faint); padding-top: 2px; }
dl.detail dd { margin: 0; color: var(--slate); }

details { margin-top: 10px; }
details summary { font-size: 12px; color: var(--slate); cursor: pointer; }
details[open] summary { color: var(--slate); }

.skincheck { border-left: 4px solid var(--flag); background: var(--flag-bg); }
.skincheck p:last-child { margin-bottom: 0; }

@media (max-width: 560px) { .thennow .delta { margin-left: 0; text-align: left; width: 100%; }
  dl.detail { grid-template-columns: 1fr; gap: 1px 0; }
  dl.detail dd { margin-bottom: 8px; } }

.notice { background: var(--sunk); border-left: 3px solid var(--rule); padding: 9px 14px; margin: 18px 0 0; font-size: 13px; color: var(--slate); }

.tablewrap { padding: 0; overflow-x: auto; }
table.held { border-collapse: collapse; width: 100%; min-width: 640px; font-size: 13px; }
table.held th { text-align: left; font-size: 11px; color: var(--faint); font-weight: 500; padding: 10px 12px; border-bottom: 1px solid var(--rule); white-space: normal; line-height: 1.3; }
table.held td { padding: 10px 12px; border-bottom: 1px solid var(--rule-soft); vertical-align: top; }
table.held td:first-child { min-width: 150px; }
table.held tr:last-child td { border-bottom: 0; }
table.held .num { text-align: right; font-family: var(--mono); font-variant-numeric: tabular-nums; white-space: nowrap; }
table.held th.num { text-align: right; }
table.held .arrow { color: var(--faint); padding: 10px 2px; text-align: center; }
table.held .lbl { font-size: 10px; }
table.held .small { font-size: 10px; }
.flagnote { font-size: 10.5px; color: var(--flag); }
details.wide { margin: 8px 0 0; }

/* --- 02 Review ----------------------------------------------------------- */

.strip { display: flex; align-items: flex-end; gap: 2px; height: 56px; padding: 4px 0; }
.strip .tick { flex: 1 1 0; min-width: 3px; background: var(--signal); border-radius: 1px; }
.strip .tick.thin { background: var(--rule); }

ul.plain { margin: 4px 0 0; padding-left: 18px; }
ul.plain li { margin: 2px 0; }

.grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(150px, 1fr)); gap: 12px; margin-top: 10px; }
figure.shot { margin: 0; background: var(--panel); border: 1px solid var(--rule-soft); border-radius: 4px; overflow: hidden; }
figure.shot img { display: block; width: 100%; height: auto; aspect-ratio: 1; object-fit: cover; background: var(--sunk); }
figure.shot .noimg { aspect-ratio: 1; display: grid; place-items: center; background: var(--sunk); color: var(--faint); font-size: 11px; }
figure.shot figcaption { padding: 7px 9px 9px; font-size: 11.5px; line-height: 1.4; }
figure.shot .when { display: block; font-family: var(--mono); color: var(--slate); }
figure.shot .why { display: block; color: var(--flag); margin-top: 2px; }
figure.shot.skipped { border-color: var(--rule); background: var(--sunk); }
figure.shot.skipped img { opacity: 0.45; filter: grayscale(1); }
figure.shot.dropped { border-style: dashed; }
figure.shot.dropped img { opacity: 0.4; }
button.tiny { margin-top: 6px; font: inherit; font-size: 11px; padding: 2px 8px; border: 1px solid var(--rule); background: var(--panel); border-radius: 3px; cursor: pointer; color: var(--slate); }
button.tiny:hover { border-color: var(--slate); color: var(--ink); }

a.btn { display: inline-block; font-size: 13px; padding: 7px 13px; border: 1px solid var(--rule); border-radius: 4px; text-decoration: none; color: var(--ink); background: var(--panel); }
a.btn:hover { border-color: var(--slate); }
a.btn.pri { background: var(--ink); color: var(--paper); border-color: var(--ink); }
details.blocked { margin-top: 4px; }
details.blocked > summary { font-size: 11px; color: var(--flag); }
details.blocked ul.plain { font-size: 10.5px; color: var(--slate); padding-left: 14px; margin: 4px 0 0; }

/* Conversation, not a clinical document. */
.guess { font-size: 27px; line-height: 1.25; margin: 4px 0 10px; letter-spacing: -0.02em; }
.guess b { font-weight: 700; }
.guess.muted { color: var(--slate); font-size: 21px; }
.qa { margin: 14px 0 0; }
.qa .ask { font-weight: 600; margin: 12px 0 2px; }
.qa p { margin: 0; }
.qa .ask + p { color: var(--slate); }
p.what { margin: 0; color: var(--slate); font-size: 12.5px; line-height: 1.35; }
p.finding { font-size: 16px; font-weight: 600; margin: 8px 0 4px; }
.card p { margin: 6px 0; }

/* The series, drawn. */
/* The axes live inside the viewBox, so this one must scale uniformly: a fixed
   height plus preserveAspectRatio="none" would squash every tick label by
   whatever ratio the card happened to be. height:auto keeps the drawing square
   with itself at any width. */
figure.plot { margin: 12px 0 6px; }
figure.plot svg { width: 100%; height: auto; display: block; }
.plot-bg { fill: var(--sunk); }
/* Each rule sets the SAME property its presentation attribute sets. Using the
   `opacity` shorthand here instead multiplies against the attribute -- the band
   was styled 0.14 and drawn 0.14, landing at 0.02, which is why the caption
   promised shading that nothing on the chart delivered. */
.plot-band { fill: var(--signal); fill-opacity: 0.16; }
.plot-line { stroke: var(--signal); stroke-width: 1.6; }
.plot-dot { fill: var(--ink); fill-opacity: 0.55; }
.plot-grid { stroke: var(--ink); stroke-opacity: 0.10; }
.plot-axis { stroke: var(--ink); stroke-opacity: 0.30; }
.plot-tick { fill: var(--faint); fill-opacity: 1; font-family: var(--mono); font-size: 9px; }
.plot-axis-title { fill: var(--faint); fill-opacity: 1; font-family: var(--sans); font-size: 8.5px; letter-spacing: 0.04em; }
figure.plot figcaption { margin-top: 4px; font-size: 10.5px; color: var(--faint); text-align: center; }

/* Where you sit on the instrument, not just how far you moved. */
/* display:block is load-bearing. `.onscale` is a <span>, and width and
   height do not apply to an inline box -- so the bar collapsed to zero
   and its absolutely positioned markers stacked up at the origin as a
   single black blob against the left edge of the cell. */
.onscale { display: block; position: relative; height: 14px; width: 100%; min-width: 96px; margin-bottom: 2px; }
.onscale .track { position: absolute; top: 6px; left: 0; right: 0; height: 3px; border-radius: 2px; background: linear-gradient(to right, var(--rule-soft), var(--flag)); }
.onscale .was, .onscale .is { position: absolute; top: 2px; width: 2px; height: 11px; margin-left: -1px; }
.onscale .was { background: var(--faint); }
.onscale .is { background: var(--ink); width: 3px; margin-left: -1.5px; }

p.skincheck { margin: 30px 0 0; padding: 10px 14px; border-left: 3px solid var(--flag); background: var(--flag-bg); font-size: 13px; color: var(--ink); }
/* On every screen now, below the content. Kept in the flag colour: it is the
   one thing on any page the reader must not skim past, and it is the only
   place --flag is used for something that is not a caveat about a number. */
p.skincheck.standing { margin: 34px 0 0; }

/* ---- The identity band. -------------------------------------------------
   Whose face this is, above the fold on all five screens. The avatar carries
   most of the work: a name can be misread at a glance and a face cannot, which
   matters because the failure this prevents is acting on the wrong person's
   archive rather than merely being confused about it. */
.whois { display: flex; align-items: center; gap: 12px; max-width: 1040px;
  margin: 0 auto; padding: 10px 24px 0; }
.whois .face { border-radius: 50%; object-fit: cover; background: var(--sunk);
  box-shadow: 0 0 0 2px var(--signal); flex: 0 0 40px; }
.whois .who { display: flex; align-items: baseline; gap: 10px; min-width: 0; }
.whois .name { font-size: 15px; font-weight: 600; color: var(--ink); }
.whois .ctx { font-size: 12.5px; color: var(--faint); white-space: nowrap;
  overflow: hidden; text-overflow: ellipsis; }
.whois .switcher { margin-left: auto; position: relative; }
.whois .switcher > summary { font-size: 12.5px; color: var(--signal);
  cursor: pointer; list-style: none; padding: 4px 2px; }
.whois .switcher > summary::-webkit-details-marker { display: none; }
.switchpanel { position: absolute; right: 0; top: 100%; z-index: 20;
  width: 280px; background: var(--panel); border: 1px solid var(--rule);
  border-radius: 5px; padding: 14px; box-shadow: 0 6px 24px rgba(0,0,0,.10); }
.switchpanel .lbl { margin: 0 0 8px; }
.switchpanel .people { list-style: none; margin: 0 0 10px; padding: 0; }
.switchpanel .people li { margin: 0; }
.switchpanel .person { display: flex; align-items: center; gap: 9px; width: 100%;
  background: none; border: 0; padding: 6px 4px; cursor: pointer; text-align: left;
  font: inherit; color: var(--ink); border-radius: 4px; }
.switchpanel .person:hover { background: var(--sunk); }
.switchpanel .person img { border-radius: 50%; object-fit: cover; background: var(--sunk); }
.switchpanel .person.current { font-weight: 600; }
.switchpanel .tick { margin-left: auto; font-size: 11px; color: var(--faint); }
.switchpanel .quiet { margin: 0; font-size: 12px; }
@media (max-width: 560px) {
  .whois { padding: 8px 16px 0; }
  .whois .ctx { display: none; }
}

/* "Not enough data", made visible. */
.marks { position: relative; height: 18px; margin: 10px 0 2px; border-bottom: 1px solid var(--rule); }
.marks span { position: absolute; bottom: 0; width: 2px; height: 12px; margin-left: -1px; background: var(--slate); border-radius: 1px; }
td.spark { width: 110px; min-width: 90px; }
td.spark svg { width: 100%; height: 30px; display: block; }
.spark-path { fill: none; stroke: var(--ink); stroke-opacity: 0.35; stroke-width: 2.4; vector-effect: non-scaling-stroke; }
td.spark .plot-line { stroke: var(--signal); stroke-width: 2; vector-effect: non-scaling-stroke; }

/* Year labels under the coverage strip, one cell per period so the two rows
   stay aligned without either knowing the other's widths. */
.years { display: flex; gap: 2px; margin-top: 2px; }
.years span { position: relative; font-size: 10px; color: var(--faint); font-family: var(--mono); padding-top: 5px; white-space: nowrap; overflow: hidden; }
.years span i { position: absolute; top: 0; left: 0; width: 1px; height: 4px; background: var(--rule); }
.years .y2 { display: none; }

/* Brand bar. Present on every screen, above the page's own title. */
.brand { display: flex; align-items: center; gap: 9px; max-width: 900px; margin: 0 auto; padding: 18px 24px 0; text-decoration: none; color: var(--ink); }
.brand .mark { width: 30px; height: 18px; flex: none; color: var(--signal); }
.brand .wordmark { font-size: 17px; font-weight: 700; letter-spacing: -0.02em; }
.brand .tagline { font-size: 13px; color: var(--faint); letter-spacing: 0.01em; }
.brand .tagline::before { content: "— "; }
header.masthead { padding-top: 14px; }
@media (max-width: 560px) { .brand .tagline { display: none; } }

table.held td.what { min-width: 150px; }
table.held td.sits { min-width: 110px; }

/* What the order was decided on, and the button that keeps the plan. Both
   belong to the plan rather than to any one card, so they sit above the first. */
ul.why { margin: 6px 0 0; padding-left: 18px; font-size: 13px; color: var(--ink); }
ul.why li { margin: 3px 0; }

details.tail summary { cursor: pointer; font-size: 14px; font-weight: 600; color: var(--ink); }
details.tail[open] summary { margin-bottom: 8px; }

/* In-page tabs, CSS only. Radio inputs plus `:checked ~` sibling selectors, so
   the sections work with JavaScript off and need no script -- which matters on
   a droplet that may have no outbound access and vendors everything it uses.

   The inputs stay in the document (visually hidden, not display:none) so they
   remain focusable and arrow keys move between them, which is the keyboard
   behaviour people expect from tabs. No `role=tablist` here: that role promises
   a full keyboard contract this does not implement, and a half-kept promise to
   a screen reader is worse than none.

   Laid out with flex so `.panes` can claim a whole row of its own with
   `flex: 0 0 100%` -- the labels are free to wrap above it without the panel
   sliding up beside them. DOM order is untouched, which the `~` selectors
   below depend on absolutely. */
.tabset { display: flex; flex-wrap: wrap; align-items: flex-end; margin-top: 18px; }
.tabset > input[type=radio] { position: absolute; width: 1px; height: 1px; opacity: 0; pointer-events: none; }
.tabset > label { padding: 8px 13px 9px; font-size: 13px; font-weight: 600; color: var(--faint); cursor: pointer; border-bottom: 2px solid transparent; white-space: nowrap; }
.tabset > label:hover { color: var(--ink); }
.tabset > label .count { font-weight: 500; font-size: 11px; color: var(--faint); margin-left: 3px; }
.tabset > input:checked + label { color: var(--ink); border-bottom-color: var(--signal); }
.tabset > input:checked + label .count { color: var(--signal); }
.tabset > input:focus-visible + label { outline: 2px solid var(--signal); outline-offset: 2px; border-radius: 3px; }
/* Always its own row, whatever the labels above it did. */
.tabset .panes { flex: 0 0 100%; border-top: 1px solid var(--rule); padding-top: 18px; margin-top: -1px; }
.tabset .pane { display: none; }
.tabset .pane > .section-note:first-child { margin-top: 0; }
#pt-do:checked ~ .panes .pane-do,
#pt-more:checked ~ .panes .pane-more,
#pt-surgery:checked ~ .panes .pane-surgery,
#pt-reach:checked ~ .panes .pane-reach,
#pt-saved:checked ~ .panes .pane-saved { display: block; }

/* One procedure. Ruled rather than carded, because a card in the ranked list's
   visual language would imply it had been ranked. */
.surgical { border-top: 1px solid var(--rule-soft); padding-top: 12px; margin-top: 12px; }
.surgical:first-of-type { border-top: 0; padding-top: 0; margin-top: 6px; }
.surgical h4 { margin: 0; font-size: 15px; letter-spacing: -0.01em; }
.surgical .detail { margin: 8px 0 0; }

/* Four labels do not fit one phone-width row, so they wrap. Tightening the
   padding keeps that to two tidy rows rather than three ragged ones; the strip
   is not made scrollable, because a horizontally scrolling row of tabs hides
   whichever one is off-screen and gives no sign it is there. */
@media (max-width: 560px) { .tabset > label { padding: 7px 9px 8px; font-size: 12.5px; } }

/* Admin: taking a copy out. */
.exportbar { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; margin: 14px 0 6px; }
.exportbar label { font-size: 12px; color: var(--faint); }
.exportbar select { padding: 6px 8px; font: inherit; font-size: 13px; border: 1px solid var(--rule); border-radius: 3px; background: var(--panel); color: var(--ink); }
.exportbar button { padding: 7px 16px; font: inherit; font-size: 13px; font-weight: 500; border: 0; border-radius: 3px; background: var(--signal); color: #fff; cursor: pointer; }
.exportbar button:hover { filter: brightness(1.08); }
ul.plain { margin: 8px 0; padding-left: 20px; max-width: 66ch; }
ul.plain li { margin: 5px 0; font-size: 14px; color: var(--slate); }
pre.cmd { margin: 8px 0 0; padding: 12px 14px; background: var(--sunk); border-radius: 3px; font-family: var(--mono); font-size: 12.5px; overflow-x: auto; color: var(--ink); }

/* One measure per row, in three groups, each row opening onto its full card.
   Grid rather than <table>: a table row cannot contain a disclosure, and the
   columns only have to line up, which one shared template does. */
:root { --mcols: minmax(200px, 2.3fr) 140px 120px 164px; }
.mtable { padding: 0; overflow: hidden; }
.mhead, .mrow > summary {
  display: grid; grid-template-columns: var(--mcols); gap: 14px;
  align-items: start; padding: 11px 14px;
}
.mhead { font-size: 11px; color: var(--faint); border-bottom: 1px solid var(--rule); }
.mrow { border-bottom: 1px solid var(--rule-soft); }
.mrow:last-child { border-bottom: 0; }
.mrow > summary { cursor: pointer; font-size: 13px; list-style: none; }
.mrow > summary::-webkit-details-marker { display: none; }
.mrow > summary:hover { background: var(--sunk); }
.mrow[open] > summary { background: var(--sunk); border-bottom: 1px solid var(--rule-soft); }
.mrow > summary .num { text-align: right; font-family: var(--mono); font-variant-numeric: tabular-nums; }
.mrow > summary .what { line-height: 1.35; display: flex; align-items: flex-start; gap: 9px; }
.mrow > summary .naming { min-width: 0; }

/* That a row opens was carried by the hover state alone, which does not exist
   on a phone -- and the native <summary> marker is hidden because it becomes a
   grid item and breaks the columns. So: an explicit caret, first in the row,
   aligned down the left edge of the whole list where a reader meets it before
   anything else. */
.caret { flex: 0 0 auto; width: 9px; height: 9px; margin-top: 22px;
  border-right: 2px solid var(--faint); border-bottom: 2px solid var(--faint);
  transform: rotate(-45deg); transition: transform 120ms ease, border-color 120ms ease; }
.mrow[open] > summary .caret { transform: rotate(45deg); border-color: var(--ink); }
.mrow > summary:hover .caret { border-color: var(--ink); }
/* Keyboard focus has to be visible: <summary> is the only control on this
   screen and its default ring is suppressed by the grid in some browsers. */
.mrow > summary:focus-visible { outline: 2px solid var(--signal); outline-offset: -2px; }
@media (prefers-reduced-motion: reduce) { .caret { transition: none; } }
/* 56px is the floor, measured rather than picked: at 40px the fine-lines
   swatch reads as a grey block and the crow's-feet marks are gone. Sized in
   the markup as well as here so the row does not reflow while the SVG loads. */
img.region { flex: 0 0 auto; width: 56px; height: 67px; }
img.region.big { width: 64px; height: 77px; margin-top: 1px; }
.mrow > summary .spark svg { width: 100%; height: 26px; display: block; }
.mrow > summary .sits { display: block; }
/* The four states, marked in the same colours the cards use. `--find` was
   never a declared token: the shorthand was invalid at computed-value time, so
   border-left-style fell back to none and the *established* rows were the only
   group on the page with no rule at all -- while `is-suggestive`, whose token
   does exist, was marked. The weakest result looked like the strongest. */
.mrow.is-moving { border-left: 3px solid var(--signal); }
.mrow.is-suggestive { border-left: 3px solid var(--flag); }
.mrow.is-stable { border-left: 3px solid var(--noise); }
.mbody { padding: 4px 14px 16px; background: var(--sunk); }
/* The card inside an open row is already framed by the row. */
.mbody .panel.card { border: 0; box-shadow: none; background: transparent; padding: 0; margin: 0; }
@media (max-width: 720px) {
  :root { --mcols: 1fr 1fr; }
  .caret { margin-top: 20px; }
  /* Every screen using this has four tiles, so two columns fill exactly. */
  .stats { grid-template-columns: repeat(2, 1fr); }
  /* A year gets about 20px of axis here, and "2023" needs 24 at 10px mono, so
     every label was clipped to "202" or "20". Two digits fit; the tick mark
     and the title attribute carry the rest. */
  .years span { font-size: 9px; }
  .years .y4 { display: none; }
  .years .y2 { display: inline; }
  /* The step numbers go: they are scaffolding for a first visit and the widest
     characters in each tab, so dropping them is what keeps five tabs on two
     lines instead of three. */
  nav.tabs .step { display: none; }
  /* The name gets the whole row. Sharing it with "then to now" left about 95px
     for the text once the caret and the drawing had theirs, which wrapped
     "Crow's feet, smiling" onto five lines. */
  .mrow > summary .what { grid-column: 1 / -1; }
  .mrow > summary .num { text-align: left; }
  .mhead { display: none; }
  .mrow > summary .spark, .mrow > summary .sits { grid-column: 1 / -1; }
  img.region { width: 48px; height: 58px; }
}


/* The ranked list. Its own class rather than `.held`, which is sized for a
   three-column table: reusing it gave a 150px minimum to the rank number, a
   640px floor that overflowed the panel at seven columns, and a Measure cell
   narrow enough to break "Midface volume" across two lines. */
table.ranked { border-collapse: collapse; width: 100%; font-size: 13px; }
table.ranked th { text-align: left; font-size: 11px; color: var(--faint); font-weight: 500;
  padding: 8px 10px; border-bottom: 1px solid var(--rule); line-height: 1.25; }
table.ranked td { padding: 8px 10px; border-bottom: 1px solid var(--rule-soft);
  vertical-align: baseline; }
table.ranked tr:last-child td { border-bottom: 0; }
table.ranked .rank { width: 2.5em; text-align: right; color: var(--faint);
  font-variant-numeric: tabular-nums; }
table.ranked td.what { width: 40%; }
table.ranked .num, table.ranked th.num { text-align: right; font-family: var(--mono);
  font-variant-numeric: tabular-nums; white-space: nowrap; }

/* The norm comparison. Set apart from the movement sentence above it because
   it is a different kind of claim -- that one is about this face, this one is
   about this face against everybody else's -- and run in rather than panelled,
   because the caveat that travels with it is long and a box would invite
   skipping the half that does the work. */
p.compare { margin: 0 0 10px; font-size: 14px; color: var(--slate); }
p.compare b { color: var(--ink); }

/* The route from a finding to what to do about it. Inside the open card body
   rather than on the summary row: a nested anchor in a <summary> toggles the
   row on click, and would need JavaScript to stop. */
p.act { margin: 12px 0 0; }

/* Arriving from a finding. Says which measure, how much of the list reaches
   it, and how to get back -- a filter that cannot be cleared is a trap. */
.focusbar { border-left: 4px solid var(--signal); }
.focusbar .big { margin: 0 0 6px; }
.focusbar .quiet { margin: 0; }

/* One line, not a panel: the cause belongs beside the measures it describes. */
p.cause { margin: 0 0 8px; }


/* Clickable plot dots. The hit target is larger than the mark, because 2.4px
   is a fair drawing of a datum and an unfair thing to ask anybody to click. */
svg a .plot-dot { transition: fill-opacity 90ms ease, r 90ms ease; }
svg a:hover .plot-dot, svg a:focus .plot-dot { fill-opacity: 1; r: 3.6; }
svg a:focus { outline: none; }
/* Same undeclared token, and here it cost the keyboard focus ring entirely. */
svg a:focus-visible .plot-dot { stroke: var(--signal); stroke-width: 2; }
