/* ============================================================================
   audit.css - PC Audit page only.

   Loads AFTER ook.css (tokens, reset, nav, footer, .btn, .card/.panel surface,
   .hero + .hero-grid + .hero-copy, .shead, .rail-card, .tile / .tile-grid /
   .tile-kicker / .tile-meta, .eyebrow, .stepnum, code chips, focus,
   preference blocks) and after optimization/style.css (principles grid,
   .dl-row). Nothing here redeclares a shared component: every panel carries
   ook.css's .panel/.card class and this file adds layout only.

   One axis: every section head runs full width above its content, and every
   section's content terminates on the container edge. One beat: every section
   boundary is a stated hairline with equal space above and below it.
   ========================================================================== */


/* ============================================================================
   1. PAGE RHYTHM - the boundary between chapters is drawn, not implied
   Sections are full-bleed bands (so the hero light is anchored to the viewport,
   not to a column); the chapter hairline is drawn inside .wrap at the text
   column's width, so it never runs edge to edge.
   ========================================================================== */

main > section + section { margin-top: calc(var(--section-y) / 2); }

main > section + section > .wrap {
  position: relative;
  padding-top: calc(var(--section-y) / 2);
}

main > section + section > .wrap::before {
  content: "";
  position: absolute;
  top: 0;
  left: var(--gutter);
  right: var(--gutter);
  height: 1px;
  background: var(--line-1);
}

/* Hero rhythm - the SAME two lines on all six sub-site surface pages (guide,
   config, audit, commands, binds, changes): a tighter band than the shared one,
   because every hero here is now a heading, one line and one action, and the
   tool or list under it is the point. Set page-locally rather than in
   ook.css because the gallery pages consume the shared band too. */
.hero { padding-block: clamp(32px, 3.5vw, 48px) var(--s5); }
main > .hero + section { margin-top: var(--s6); }


/* ============================================================================
   2. HERO - copy left, the download beside it, nothing else
   ========================================================================== */

/* This fold is a download, not a chapter opener: the shared 112px lead-in
   spends the first screen on empty ground above a two-word headline. Tighter
   at the top, and tighter at the foot, so the whole panel - both files and the
   rule that binds them - clears a 900px screen. */

/* The facts belong to the tool, not to the download control: on the copy side
   they fill the column the lede leaves short, and the panel is left holding
   nothing but the two files. Chips only - the hairline .tile-meta carries
   inside a card would draw a second fold rule 90px above the real one. */
.hero-facts {
  margin-top: var(--s5);
  padding-top: 0;
  border-top: 0;
}

/* Below the two-column breakpoint the source order IS the reading order:
   headline, lede, then the download. Nothing else is allowed onto this fold. */
@media (min-width: 64em) {
  /* Both columns hang from the same top edge, and the panel is then dropped by
     the cap offset inside the display line, so its top edge lands ON the H1 cap
     line rather than on the box the line sits in. It was 84px while a breadcrumb
     row and an eyebrow stood above the headline; both are gone, and the offset
     is now the cap alone. */
  .hero-grid { row-gap: var(--s5); align-items: start; }
  .hero-copy { grid-column: 1; grid-row: 1; }

  .dl-panel {
    grid-column: 2;
    grid-row: 1;
    align-self: start;
    margin-top: 14px;
  }
}


/* ============================================================================
   3. DOWNLOAD PANEL - the point of the page, on the first screen
   Surface and header type come from ook.css (.panel + .rail-card); this is
   layout and the button column only.
   ========================================================================== */

/* The h2 is a DIRECT child of .rail-card so ook.css's one rail-card header
   shape reaches it. Ordinary flow, so this file states only the gaps between
   the header, the button pair and the two lines under it. No eyebrow: the
   panel holds one thing and its heading already names it. */
.dl-panel { box-shadow: var(--sh-2), var(--hairline); }

.dl-panel > h2 { margin-bottom: var(--s2); }

/* Stacked and full width: in a column this narrow, two side-by-side buttons
   would both be too small to read. */
.dl-panel .dl-row {
  display: grid;
  gap: var(--s3);
  margin-top: var(--s5);
}

/* EQUAL WEIGHT, on purpose. Both files are mandatory, so neither may wear the
   quiet variant: a bright primary above a dark secondary teaches that the
   second one is optional, and a reader who acts on that leaves with half a
   tool. Each button names its own file and carries its place in the pair. */
.dl-panel .btn { width: 100%; }

.dl-ack {
  display: grid;
  grid-template-columns: 18px minmax(0, 1fr) max-content;
  align-items: center;
  gap: var(--s3);
  min-width: 0;
  text-align: left;
}

.dl-name { min-width: 0; overflow-wrap: anywhere; }

/* The pair counter. It stays on the button after the click so the state is
   "1 of 2 requested", never a bare check that could be read as "finished". */
.dl-ord {
  padding: 2px 8px;
  border-radius: var(--r-full);
  background: rgba(16, 14, 12, .20);
  border: 1px solid rgba(16, 14, 12, .16);
  font-size: var(--fs-2xs);
  font-weight: 600;
  letter-spacing: var(--tk-label);
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}

/* The rule the panel exists to carry, on the surface and unconditional. */
.dl-panel .dl-pair {
  margin: var(--s4) 0 0;
  color: var(--fg-2);
  font-size: var(--fs-sm);
  line-height: var(--lh-snug);
}

/* The tablet band: the panel is full width under the copy, so the two buttons
   sit side by side rather than as one tall stack under a half-empty row. No
   split column, so there is no second baseline to miss. */
@media (min-width: 45em) and (max-width: 63.99em) {
  .dl-panel { padding: var(--s6); }
  .dl-panel .dl-row { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

/* On the narrowest phones the label needs the width more than the button needs
   the padding. */
@media (max-width: 26em) {
  .dl-panel .btn { padding-inline: var(--s4); }
}

/* The note is announced through the page's shared live region, so it stays out
   of the layout until there is something to say. Two states, and only one of
   them is the success hue: while a file is still missing the note is warm and
   names what is missing, so nothing on this panel reads as "done" until both
   files have actually been fetched. */
.dl-panel .dl-note {
  margin: var(--s3) 0 0;
  min-height: 0;
  font-size: var(--fs-sm);
  line-height: var(--lh-snug);
  color: var(--accent-300);
}

.dl-panel .dl-note[data-state="done"] { color: var(--ok); }
.dl-panel .dl-note:empty { display: none; }

/* A downloaded file is otherwise a silent event: acknowledge it in place, in a
   fixed-width icon slot so the label never reflows. */
.dl-ack .ci,
.copy-btn .ci {
  position: relative;
  flex: none;
  width: 18px;
  height: 18px;
}

.dl-ack .ci svg,
.copy-btn .ci svg {
  position: absolute;
  inset: 0;
  transition: opacity var(--dur-base) var(--ease-std);
}

.dl-ack .ci-done,
.copy-btn .ci-done { opacity: 0; }

.dl-ack.is-got .ci-main,
.copy-btn[data-copied="true"] .ci-main { opacity: 0; }

.dl-ack.is-got .ci-done,
.copy-btn[data-copied="true"] .ci-done { opacity: 1; }

/* A fetched file goes quiet rather than triumphant: the check and the green
   edge confirm it arrived, and dropping the fill leaves the file still missing
   as the only lit control in the panel. A full green button here would be the
   page certifying a download that is only half done. */
.dl-ack.is-got {
  background-image: none;
  background-color: rgba(134, 201, 142, .10);
  border-color: var(--ok);
  color: var(--fg-1);
  box-shadow: none;
}

.dl-ack.is-got:hover {
  background-color: rgba(134, 201, 142, .16);
  transform: none;
  box-shadow: none;
}

.dl-ack.is-got .ci-done { color: var(--ok); }

.dl-ack.is-got .dl-ord {
  background: var(--bg-3);
  border-color: var(--line-2);
  color: var(--fg-2);
}


/* ============================================================================
   4. PRINCIPLE CARDS - each one ends in a reveal, not in a restatement
   The grid, the surface and the bare accent icon are shared (style.css
   .principles/.principle, ook.css .card). The three cards used to close on a
   meta line that restated the sentence above it; the card foot now holds the
   affordance that opens the depth instead - the enumerations, the temp-file
   disclosure and the checksum all live inside these three reveals, which is
   why the page has four sections rather than six.
   ========================================================================== */

/* The card foot. A stated gap, never `margin-top: auto`: pushing the summary
   to the bottom of a stretched card ties its position to the tallest card in
   the row, so opening any reveal dragged all three controls to new places -
   including the one under the cursor. The feet are aligned by the grid below
   instead, which survives an open card. */
.principle > .more {
  margin-top: var(--s5);
  padding-top: var(--s4);
  border-top: 1px solid var(--line-1);
}

/* Foot alignment WITHOUT coupling: each card hands its four rows back to the
   band's grid, so the row that holds the reveal starts on the same line in all
   three columns. When one opens, that row grows downward and every summary -
   its own included - stays exactly where it was clicked. Without subgrid the
   cards simply run at their own heights; nothing moves there either. */
@media (min-width: 40em) {
  @supports (grid-template-rows: subgrid) {
    /* Two card rows of four tracks each at this band; the row-gap that used to
       separate the cards would land between a card's own rows, so the second
       row states its own. */
    .principles {
      grid-template-rows: repeat(8, auto);
      row-gap: 0;
    }

    .principle {
      display: grid;
      grid-template-rows: subgrid;
      grid-row: span 4;
    }

    .principles > :nth-child(3) { margin-top: var(--s6); }
  }
}

/* Two tracks, three cards: the third would otherwise leave a half-width hole
   the height of a card beside it. It takes the full row instead - a wide
   variant of the same card, with its surface copy still held to the measure so
   the extra width buys air, not a longer line to read. */
@media (min-width: 40em) and (max-width: 61.99em) {
  .principles > :nth-child(3) { grid-column: 1 / -1; }
  .principles > :nth-child(3) > .pbody { max-width: var(--measure); }

  /* Full width here, so the hash keeps the four-column matrix rather than
     leaving half the box empty next to two runs. */
  .principles > :nth-child(3) .checksum-value {
    grid-template-columns: repeat(4, max-content);
  }
}

@media (min-width: 62em) {
  @supports (grid-template-rows: subgrid) {
    .principles { grid-template-rows: repeat(4, auto); }
    .principles > :nth-child(3) { margin-top: 0; }
  }
}

/* style.css states `.principle p { margin: 0 }` at (0,1,1), which outranks the
   single-class spacing the shared disclosure content ships with. Restate the
   same values one class deeper rather than restyling anything. */
.principle p + p,
.principle .more-body > * + * { margin-top: var(--s3); }
.principle .never-foot { margin-top: var(--s4); }
.principle .checksum-msg { margin-top: var(--s4); color: var(--ok); }

/* A quiet label inside a reveal: the body holds two lists and they need naming
   without spending an h4 rung on them. */
.disc-label {
  color: var(--fg-3);
  font-size: var(--fs-2xs);
  line-height: var(--lh-label);
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: var(--tk-label);
}

/* Plain lines, one per thing collected. A hairline down the left groups them as one block, so they
   read as a list without a glyph doing the work - at --line-2, the page's
   stated hairline: at --line-1 the rule vanished on a phone and the lines
   read as an unexplained indent. */
.flatlist {
  list-style: none;
  margin: 0;
  padding: 0 0 0 var(--s4);
  border-left: 1px solid var(--line-2);
  display: grid;
  gap: var(--s1);
  color: var(--fg-2);
  font-size: var(--fs-sm);
  line-height: var(--lh-snug);
}

.never-list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  gap: var(--s2);
}

.never-list li {
  display: flex;
  align-items: flex-start;
  gap: var(--s2);
  color: var(--fg-2);
  font-size: var(--fs-sm);
  line-height: var(--lh-snug);
}

.never-list svg { flex: none; color: var(--fg-3); margin-top: 3px; }

.never-foot {
  padding-top: var(--s4);
  border-top: 1px solid var(--line-1);
  color: var(--fg-3);
  font-size: var(--fs-xs);
  line-height: var(--lh-snug);
}


/* ============================================================================
   5. CHECKSUM - a trust artifact, so it gets a deliberate block, not a chip
   It lives inside the Readable card's reveal now. Surface from ook.css's
   .panel. Padding is uniform on all four sides: the status line collapses when
   empty rather than reserving a hole under the hash.
   ========================================================================== */

.checksum { padding: var(--s5); }

.checksum-top {
  display: flex;
  align-items: center;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: var(--s3) var(--s4);
}

/* The label names a file, so it takes the row's slack and wraps inside it -
   otherwise the longer of the two filenames pushes its own Copy button onto a
   second line and the two hash blocks stop matching. */
.checksum .eyebrow { margin: 0; flex: 1 1 8ch; min-width: 0; overflow-wrap: anywhere; }

.checksum .copy-btn { flex: none; font-size: var(--fs-xs); min-height: 36px; padding-inline: var(--s4); }
.checksum .copy-btn .ci { width: 16px; height: 16px; }

/* Eight runs of eight, always in a matrix. Never a wrapping inline run: a hash
   that lands 3/3/2 on a phone and 4/4 on a laptop cannot be compared line by
   line against a terminal, which is the only reason this block exists. Two
   columns is the shape that survives the narrowest card. */
.checksum-value {
  display: grid;
  grid-template-columns: repeat(2, max-content);
  justify-content: start;
  gap: var(--s2) var(--s5);
  margin-top: var(--s4);
  background: var(--bg-0);
  border: 1px solid var(--line-2);
  border-radius: var(--r-md);
  padding: var(--s3) var(--s4);
  color: var(--fg-1);
  font-size: var(--fs-sm);
  line-height: var(--lh-snug);
  overflow-wrap: anywhere;
}

/* With room, four across - the same matrix, half the rows. */
@media (min-width: 30em) {
  .checksum-value { grid-template-columns: repeat(4, max-content); }
}

/* The two hashes are one artifact, so they share a panel; the second is set off
   by space, not by another rule inside an already-ruled card. */
.ck-file + .ck-file { margin-top: var(--s5); }

/* The card column is a third of the container, so four runs of eight would
   overflow the panel. Back to two columns, four rows, same comparison - packed
   left, never spread to the panel edges, so the pair reads as one matrix at
   every width. */
@media (min-width: 40em) {
  .principle .checksum-value { grid-template-columns: repeat(2, max-content); }
}

.checksum-msg {
  margin-top: var(--s4);
  font-size: var(--fs-sm);
  line-height: var(--lh-snug);
  color: var(--ok);
}

.checksum-msg:empty { display: none; }
.checksum-msg[data-state="fail"] { color: var(--warn-fg); }

.verify-line {
  color: var(--fg-2);
  font-size: var(--fs-sm);
  line-height: var(--lh-body);
  max-width: var(--measure);
}


/* ============================================================================
   6. RUN IT - three numbered steps joined by a rail
   The counter chip is ook.css's .stepnum token, drawn on a ::before because a
   CSS counter cannot carry a class.
   ========================================================================== */

/* The page's second colour event, and the only one below the fold: the same
   warm key the hero is lit by, re-lit over the procedure - the one band where
   the reader is doing something rather than reading about it. Same technique
   as .hero (a -1 layer inside a z-index:0 box), so nothing above it is
   composited differently. */
#run {
  position: relative;
  z-index: 0;
}

#run::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -1;
  pointer-events: none;
  background: radial-gradient(ellipse 55% 420px at 22% 0, rgba(216, 112, 60, .10), transparent 70%);
}

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

.steps > li {
  position: relative;
  display: grid;
  grid-template-columns: 28px minmax(0, 1fr);
  column-gap: var(--s4);
  align-content: start;
}

.steps > li::before {
  counter-increment: step;
  content: counter(step);
  grid-column: 1;
  align-self: start;
  width: 28px;
  height: 28px;
  display: grid;
  place-items: center;
  border-radius: var(--r-full);
  background: rgba(216, 112, 60, .14);
  border: 1px solid var(--accent-500);
  color: var(--accent-300);
  font-size: var(--fs-sm);
  font-weight: 600;
  font-variant-numeric: tabular-nums;
  letter-spacing: 0;
  line-height: 1;
}

/* The thread between one number and the next, so the procedure reads as one
   continuous run instead of three unrelated blocks. It runs from under the
   badge into the gap below, stopping short of the next badge. */
.steps > li::after {
  content: "";
  position: absolute;
  left: 13px;
  top: 34px;
  bottom: calc(var(--s4) * -1);
  width: 1px;
  background: var(--line-1);
  pointer-events: none;
}

.steps > li:last-child::after { display: none; }

.steps > li > * { grid-column: 2; }

.steps h3 {
  display: flex;
  align-items: center;
  min-height: 28px;
  font-size: var(--fs-body);
  margin-bottom: var(--s1);
}

.steps p {
  margin: 0;
  color: var(--fg-2);
  font-size: var(--fs-sm);
  line-height: var(--lh-snug);
  max-width: 62ch;
}

.steps b { color: var(--fg-1); }

/* Three steps in one column left the right half of the band empty while every
   other band on the page closes on the container edge - the same fault the old
   six-step layout used two columns to avoid. Three across instead, one step per
   track, and the rail turns with them: the badge lifts above its heading so the
   thread can run through the badge line without crossing any type. */
@media (min-width: 75em) {
  .steps {
    grid-template-columns: repeat(3, minmax(0, 1fr));
    column-gap: var(--s7);
  }

  .steps > li { display: block; }
  .steps > li > * { grid-column: auto; }
  .steps > li::before { margin-bottom: var(--s4); }

  .steps > li::after {
    left: 40px;
    right: calc(var(--s7) * -1);
    top: 13px;
    bottom: auto;
    width: auto;
    height: 1px;
  }

  .steps h3 { min-height: 0; }

  /* Each track IS the measure here, so the copy closes on the grid line instead
     of ragging out short of it. */
  .steps p { max-width: none; }

  /* Same subgrid contract as the principles band: the row holding a step's
     reveal starts on one line across all three steps, so the two "if this
     happens" affordances read as one tier instead of two stray links, and
     opening either leaves the other exactly where it was.

     FIVE rows, not four: step 3 carries an action as well as a reveal, and with
     four the reveal was clamped into the action's track and drawn on top of the
     button. Row 4 is the action tier (empty in steps 1 and 2), row 5 the reveal
     tier, so the three disclosures still start on one line. */
  @supports (grid-template-rows: subgrid) {
    .steps {
      grid-template-rows: repeat(5, auto);
      row-gap: 0;
    }

    /* One column here: the badge is stacked above the heading in this band, so
       the two-column shape the phone layout uses must not come back with the
       display change. */
    .steps > li {
      display: grid;
      grid-template-columns: minmax(0, 1fr);
      grid-template-rows: subgrid;
      grid-row: span 5;
    }

    .steps > li > .step-go { grid-row: 4; }
    .steps > li > .more { grid-row: 5; }
  }
}


/* ============================================================================
   7. KEEP GOING - the three sibling tools, on the shared .tile grid
   Three destinations since the Command Reference joined, so the band rides the
   shared tile-grid tracks unmodified: three across at 62em. The card itself is
   ook.css's .tile, unmodified.
   ========================================================================== */

/* The kicker row is the chevron alone now that the labels are gone, and the
   chevron belongs on the outer edge where it always sat: space-between with one
   child would park it at the left. The tile's closing margin is the space a
   .tile-meta hairline used to sit in; with no chips there is nothing under it. */
.keepgoing .tile-kicker { justify-content: flex-end; }
.keepgoing .tile > p:last-child { margin-bottom: 0; }


/* ============================================================================
   8. THE REVIEW - load control, machine profile, five buckets
   Layout only. The row is ook.css's .entry card, the badges are .badge /
   .ev-label, the panels are .panel / .rail-card, the reveals are .more. This
   file adds the grid those sit in and the page-local labels the guide keeps in
   its own stylesheet (.glabel, .slabel) - nothing shared is redeclared.
   ========================================================================== */

/* -- the load control: the Config Tuner's recipe, so the two tools read as one
   product. Dashed edge belongs to a drop target and to nothing else. -------- */

.review-grid {
  display: grid;
  gap: var(--s6);
  align-items: start;
}

@media (min-width: 62em) {
  .review-grid {
    grid-template-columns: minmax(0, 1fr) var(--aside);
    gap: var(--s7);
  }
}

.drop {
  position: relative;
  display: grid;
  justify-items: center;
  gap: var(--s1);
  width: 100%;
  padding: var(--s7) var(--s5);
  background: var(--bg-1);
  border: 1px dashed var(--stroke);
  border-radius: var(--r-lg);
  cursor: pointer;
  transition: border-color var(--dur-base) var(--ease-std),
              background-color var(--dur-base) var(--ease-std),
              transform var(--dur-fast) var(--ease-std);
}

.drop:hover { border-color: var(--accent-500); }

/* Visually hidden but still focusable: display:none would drop the only file
   picker in this section out of the tab order entirely. */
.drop input[type="file"] {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: 0;
  opacity: 0;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

.drop .icon {
  margin: 0 0 var(--s2);
  color: var(--fg-3);
  transition: transform var(--dur-base) var(--ease-out),
              color var(--dur-base) var(--ease-std);
}

.drop:hover .icon { color: var(--accent-400); }
.drop.over { transform: scale(1.005); border-color: var(--accent-400); }
.drop.over .icon { transform: translateY(-2px); color: var(--accent-400); }

.drop-title { color: var(--fg-1); font-weight: 500; text-align: center; }
.drop-title code { font-size: .875em; }

.drop-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-height: 34px;
  margin-top: var(--s2);
  padding-inline: var(--s4);
  border: 1px solid var(--line-2);
  border-radius: var(--r-sm);
  color: var(--fg-1);
  font-size: var(--fs-sm);
  font-weight: 500;
  transition: border-color var(--dur-base) var(--ease-std),
              background-color var(--dur-base) var(--ease-std);
}

.drop:hover .drop-btn { border-color: var(--stroke); background: var(--bg-2); }

/* The file input is visually hidden but stays focusable, so its ring is drawn
   on the visible affordance instead. */
.drop:has(input:focus-visible) { outline: none; border-color: var(--accent-500); }

.drop:has(input:focus-visible) .drop-btn {
  outline: 2px solid var(--focus);
  outline-offset: 3px;
  border-color: var(--accent-400);
}

.drop.loaded { border-style: solid; border-color: var(--ok); }
.drop.loaded .icon { color: var(--ok); }

/* Touching the target it answers for: "did that work?" is answered where the
   question was asked. Never display:none - a live region removed from the tree
   is a live region a screen reader may not announce. */
.file-status {
  margin-top: var(--s3);
  color: var(--ok);
  font-size: var(--fs-sm);
  line-height: var(--lh-snug);
}

.file-status:empty { margin: 0; min-height: 0; }
.file-status.is-error { color: var(--warn-fg); }

.paste-alt { margin-top: var(--s4); }
.paste-alt > summary { display: inline-flex; align-items: center; gap: var(--s2); color: var(--fg-2); font-size: var(--fs-sm); }
.paste-body { margin-top: var(--s3); }

.paste-alt textarea {
  width: 100%;
  margin-bottom: var(--s3);
  padding: var(--s3);
  background: var(--bg-1);
  border: 1px solid var(--stroke);
  border-radius: var(--r-sm);
  color: var(--fg-1);
  font-family: var(--font-mono);
  font-size: var(--fs-xs);
  line-height: var(--lh-snug);
  resize: vertical;
}

/* The promise rail is a heading and one line now, so it states the same header
   gap the download panel does; .rail-card itself declares none. */
.review-promise > h2 { margin-bottom: var(--s2); }
.review-promise > p { color: var(--fg-2); font-size: var(--fs-sm); line-height: var(--lh-snug); }

/* -- the output ---------------------------------------------------------- */

.review-out { margin-top: var(--s7); }

.rv-placeholder {
  max-width: var(--measure);
  padding-left: var(--s4);
  border-left: 2px solid var(--line-2);
  color: var(--fg-3);
  font-size: var(--fs-sm);
}

.rv-top {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: var(--s3);
  margin-bottom: var(--s5);
  padding-bottom: var(--s4);
  border-bottom: 1px solid var(--line-1);
}

.rv-top h3 { margin: 0; font-size: var(--fs-h2); }
.rv-top h3:focus-visible { outline: 2px solid var(--focus); outline-offset: 4px; }

.rv-actions { display: flex; flex-wrap: wrap; gap: var(--s2); }
.rv-actions .btn { min-height: 36px; font-size: var(--fs-sm); }

/* Two copy buttons, one behaviour: the short review and the whole archive. */
.rv-actions .ci { position: relative; display: inline-flex; width: 14px; height: 14px; }
.rv-actions .ci svg { position: absolute; top: 0; left: 0; transition: opacity var(--dur-fast) var(--ease-std); }
.rv-actions .ci-check { opacity: 0; color: var(--ok); }
.rv-actions [data-copied="true"] .ci-copy { opacity: 0; }
.rv-actions [data-copied="true"] .ci-check { opacity: 1; }
.rv-actions [data-copied="true"] { border-color: var(--ok); }

.rv-block { margin-bottom: var(--s6); }
.rv-block.panel { padding: var(--s5); }
.rv-block h4 { margin: 0; font-size: var(--fs-h3-lg); letter-spacing: var(--tk-h2); }

.rv-lede {
  margin: var(--s2) 0 0;
  max-width: var(--measure);
  color: var(--fg-2);
  font-size: var(--fs-sm);
  line-height: var(--lh-snug);
}

/* The machine profile. Value on top, the arithmetic that produced it directly
   under - that pairing is the whole argument that this was computed. */
.rv-facts {
  display: grid;
  gap: var(--s4);
  margin: var(--s5) 0 0;
}

@media (min-width: 48em) {
  .rv-facts { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: var(--s4) var(--s6); }
}

/* align-content:start, or the two rows stretch apart to fill whatever the
   tallest cell in the grid row made the track. */
.rv-facts > div { display: grid; align-content: start; gap: 2px; min-width: 0; }

.rv-facts dt {
  color: var(--fg-3);
  font-size: var(--fs-2xs);
  font-weight: 600;
  line-height: var(--lh-label);
  text-transform: uppercase;
  letter-spacing: var(--tk-label);
}

.rv-facts dd { margin: 0; min-width: 0; }

.rv-facts dd b {
  display: block;
  color: var(--fg-0);
  font-weight: 600;
  font-variant-numeric: tabular-nums;
  overflow-wrap: anywhere;
}

.rv-how {
  display: block;
  margin-top: 2px;
  color: var(--fg-3);
  font-size: var(--fs-xs);
  line-height: var(--lh-snug);
  overflow-wrap: anywhere;
}

/* Preflight. One tone per consequence: warn for something that undermines the
   findings, ok for a clean collection, note for a stated assumption. */
.rv-flags {
  list-style: none;
  display: grid;
  gap: var(--s3);
  margin: var(--s4) 0 0;
  padding: 0;
}

.rv-flags li {
  max-width: var(--measure);
  padding: var(--s1) 0 var(--s1) var(--s4);
  border-left: 2px solid var(--line-2);
  color: var(--fg-2);
  font-size: var(--fs-sm);
  line-height: var(--lh-snug);
}

.rv-flags li b { color: var(--fg-0); }
.rv-flags .is-warn { border-left-color: var(--warn-line); }
.rv-flags .is-warn b { color: var(--warn-fg); }
.rv-flags .is-ok { border-left-color: var(--ok); }
.rv-flags .is-ok b { color: var(--ok); }
.rv-flags .is-note { border-left-color: var(--accent-500); }
.rv-sub { margin: var(--s2) 0 0; padding-left: var(--s5); }
.rv-sub li { border: 0; padding: 0; max-width: none; }

/* How to apply this. Same register as the preflight list above it, but
   numbered, because the order IS the content: restore point, then one safety
   group at a time, then re-collect. */
.rv-apply-steps {
  display: grid;
  gap: var(--s3);
  margin: var(--s4) 0 0;
  padding-left: var(--s5);
  max-width: var(--measure);
  color: var(--fg-2);
  font-size: var(--fs-sm);
  line-height: var(--lh-snug);
}

.rv-apply-steps li::marker {
  color: var(--fg-3);
  font-variant-numeric: tabular-nums;
}

.rv-apply-steps b { color: var(--fg-0); }

/* -- the five buckets ---------------------------------------------------- */

.rv-bucket { margin-top: var(--s8); }
.rv-bhead { margin-bottom: var(--s4); }
.rv-bhead h4 { display: flex; align-items: center; gap: var(--s3); }
.rv-bhead p { color: var(--fg-3); font-size: var(--fs-sm); }

.rv-count {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 30px;
  height: 24px;
  padding-inline: 8px;
  border: 1px solid var(--line-2);
  border-radius: var(--r-full);
  background: var(--bg-1);
  color: var(--fg-2);
  font-size: var(--fs-2xs);
  font-weight: 600;
}

.rv-bucket[data-bucket="wrong"] .rv-count {
  border-color: var(--imp-crit-line);
  background: var(--imp-crit-bg);
  color: var(--imp-crit-fg);
}

.rv-empty {
  max-width: var(--measure);
  padding-left: var(--s4);
  border-left: 2px solid var(--line-2);
  color: var(--fg-2);
  font-size: var(--fs-sm);
  line-height: var(--lh-snug);
}

.rv-rows { display: grid; gap: var(--s3); }

/* The row is the guide's row: chevron, title, the two axes that rule it in or
   out, and one line of claim. Everything else is inside. */
.rv-entry > summary {
  display: grid;
  grid-template-columns: 16px minmax(0, 1fr) max-content;
  grid-template-areas:
    "mark title badges"
    "mark sum   sum";
  align-items: center;
  gap: var(--s1) var(--s3);
  padding: var(--s3) var(--s4);
  min-height: 56px;
  border-radius: var(--r-lg);
}

.rv-entry > summary > .marker { grid-area: mark; align-self: center; color: var(--fg-3); }
.rv-entry > summary:hover > .marker,
.rv-entry[open] > summary > .marker { color: var(--accent-400); }

.rv-entry .etitle {
  grid-area: title;
  margin: 0;
  min-width: 0;
  color: var(--fg-0);
  font-size: var(--fs-body);
  font-weight: 600;
  line-height: var(--lh-snug);
  text-wrap: pretty;
}

.rv-entry .etitle code { font-size: .875em; background: var(--bg-1); color: var(--accent-300); }

.rv-entry .entry-badges {
  grid-area: badges;
  display: inline-flex;
  align-items: center;
  gap: var(--s3);
}

.rv-entry .esum {
  grid-area: sum;
  margin: 0;
  color: var(--fg-2);
  font-size: var(--fs-sm);
  line-height: var(--lh-snug);
  text-wrap: pretty;
}

.rv-entry .esum code { font-size: .875em; background: var(--bg-1); color: var(--accent-300); }

.rv-entry[open] { border-color: var(--accent-600); }
.rv-entry[open] > summary { border-bottom: 1px solid var(--line-1); border-radius: var(--r-lg) var(--r-lg) 0 0; }

.rv-entry .entry-body { padding: var(--s4) var(--s5) var(--s5); }
.rv-entry .entry-body > * { max-width: var(--measure); }
.rv-entry .entry-body p { color: var(--fg-1); font-size: var(--fs-sm); line-height: var(--lh-body); }

/* A safety grade a reader must not have to open a row to see. */
.safety-tag {
  display: inline-block;
  margin-right: var(--s2);
  padding: 1px 7px;
  border: 1px solid var(--line-2);
  border-radius: var(--r-full);
  background: var(--bg-1);
  color: var(--fg-2);
  font-size: var(--fs-2xs);
  letter-spacing: .02em;
  white-space: nowrap;
  vertical-align: 1px;
}

.safety-tag.sf-c { border-color: var(--warn-line); background: var(--warn-bg); color: var(--warn-fg); }

/* Page-local micro-labels, exactly as the guide keeps its own. */
.glabel {
  color: var(--fg-3);
  font-size: var(--fs-2xs);
  font-weight: 600;
  line-height: var(--lh-label);
  text-transform: uppercase;
  letter-spacing: var(--tk-label);
}

.slabel {
  margin: 0 0 var(--s2);
  color: var(--fg-3);
  font-size: var(--fs-2xs);
  font-weight: 600;
  line-height: var(--lh-label);
  text-transform: uppercase;
  letter-spacing: var(--tk-label);
}

.rv-entry .entry-meta {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--s2) var(--s4);
  margin: 0 0 var(--s4);
  max-width: none;
}

.rv-entry .entry-meta dt { color: var(--fg-3); }
.rv-entry .entry-meta dd { margin: 0; }

/* The quoted evidence. This is the part that makes a finding checkable, so it
   is set as data, not as prose, and it wraps rather than scrolling away. */
.rv-quotes {
  list-style: none;
  display: grid;
  gap: var(--s2);
  margin: 0 0 var(--s4);
  padding: 0;
}

.rv-quotes code {
  display: block;
  padding: var(--s2) var(--s3);
  background: var(--bg-1);
  border: 1px solid var(--line-1);
  border-radius: var(--r-sm);
  color: var(--accent-300);
  font-size: var(--fs-xs);
  line-height: var(--lh-snug);
  white-space: pre-wrap;
  overflow-wrap: anywhere;
}

.rv-entry .reversible,
.rv-entry .rv-caveat {
  margin: var(--s3) 0 0;
  color: var(--fg-2);
  font-size: var(--fs-sm);
  line-height: var(--lh-snug);
}

.rv-entry .reversible b,
.rv-entry .rv-caveat b { color: var(--fg-1); }

.rv-links { margin-top: var(--s4); max-width: none; }
.rv-links ul { list-style: none; display: flex; flex-wrap: wrap; gap: var(--s2) var(--s5); margin: 0; padding: 0; }

.rv-links a {
  display: inline-flex;
  align-items: center;
  gap: var(--s2);
  color: var(--accent-300);
  font-size: var(--fs-sm);
  text-decoration: none;
}

.rv-links a:hover { color: var(--accent-400); text-decoration: underline; }
.rv-links a .icon { flex: none; }
.rv-links code { font-size: .9em; }

.rv-unseen .etitle { color: var(--fg-1); }

/* An unseen row carries no evidence or impact label, because it makes no
   claim. What it does carry is WHICH KIND of unseen it is: a limit of the
   collector that no run will ever fill in, or a check that came up short on
   this file. Same object, one accent, so the pair reads as one axis. */
.rv-tag {
  display: inline-flex;
  align-items: center;
  padding: 1px 8px;
  border: 1px solid var(--line-2);
  border-radius: var(--r-full);
  background: var(--bg-1);
  color: var(--fg-3);
  font-size: var(--fs-2xs);
  font-weight: 600;
  letter-spacing: var(--tk-label);
  text-transform: uppercase;
  white-space: nowrap;
}

.rv-tag-gap { border-color: var(--accent-600); color: var(--accent-300); }

/* The standing blind spots are not defects of this collection, so the row is
   marked as a limit rather than tinted like a warning. */
.rv-gap { border-color: var(--line-2); }
.rv-gap > summary { border-left: 2px solid var(--accent-600); border-radius: var(--r-lg); }
.rv-gap[open] > summary { border-radius: var(--r-lg) var(--r-lg) 0 0; }

/* "Leave it alone" behind the go-deeper disclosure: the rows inside are the
   same width as every other bucket's, so the shared measure cap comes off. */
.rv-more { margin-top: 0; }
.rv-more > .more-body { max-width: none; margin-top: var(--s4); }

/* The refusals, each with its argument. A bare link list was a rule; this is
   the position, which is the thing a reader can disagree with. */
.rv-neverwhy { list-style: none; display: grid; gap: var(--s4); margin: var(--s4) 0 0; padding: 0; }
.rv-neverwhy > li { max-width: var(--measure); padding-left: var(--s4); border-left: 2px solid var(--line-2); }
.rv-never-t { margin: 0 0 var(--s1); color: var(--fg-0); font-size: var(--fs-sm); font-weight: 600; line-height: var(--lh-snug); }
.rv-never-t code { font-size: .875em; background: var(--bg-1); color: var(--accent-300); }
.rv-never-kind { margin-left: var(--s2); vertical-align: 1px; font-weight: 600; }
.rv-neverwhy a { color: var(--accent-300); }
.rv-neverwhy a:hover { color: var(--accent-400); }
.rv-neverwhy p { margin: 0; color: var(--fg-2); font-size: var(--fs-sm); line-height: var(--lh-snug); }
.rv-never-go { margin-top: var(--s2) !important; }
.rv-never-go a { display: inline-flex; align-items: center; gap: var(--s2); color: var(--accent-300); text-decoration: none; }
.rv-never-go a:hover { color: var(--accent-400); text-decoration: underline; }
.rv-never-go .icon { flex: none; }

/* The phone. The guide's row already solved this and recorded why: below 48em
   the two badges take half the width of the scan line and crush the title to
   three words a line, so they drop to their own grid row underneath. This is
   that fix, ported to the copy of the row that did not carry it. */
@media (max-width: 48em) {
  .rv-entry > summary {
    grid-template-columns: 16px minmax(0, 1fr);
    grid-template-areas:
      "mark title"
      "mark sum"
      "mark badges";
    align-items: start;
    gap: var(--s1) var(--s2);
    padding: var(--s3);
    min-height: 52px;
  }

  .rv-entry > summary > .marker { align-self: start; margin-top: 3px; }

  .rv-entry .entry-badges {
    justify-content: start;
    gap: var(--s3);
    margin-top: var(--s1);
  }

  /* The body keeps aligning under the title: marker column plus the gap. */
  .rv-entry .entry-body { padding: 0 var(--s3) var(--s5) calc(var(--s3) + 16px + var(--s2)); }
}

/* -- the foot ------------------------------------------------------------ */

.rv-foot {
  margin-top: var(--s8);
  padding-top: var(--s5);
  border-top: 1px solid var(--line-1);
}

.rv-foot > p { max-width: var(--measure); color: var(--fg-3); font-size: var(--fs-sm); }
.rv-nevers { margin-top: var(--s3); }
.rv-neverlist ul { display: grid; }
.rv-neverlist { display: block; }
.rv-neverlist li { margin-bottom: var(--s2); }

.rv-refuse { padding: var(--s5); border-color: var(--warn-line); }
.rv-refuse h4 { margin: 0 0 var(--s2); color: var(--warn-fg); font-size: var(--fs-h3-lg); }
.rv-refuse p { max-width: var(--measure); color: var(--fg-1); }

.step-go { margin-top: var(--s3); }


/* ============================================================================
   9. FORCED COLORS - restore the boundaries the UA paints over
   ========================================================================== */

@media (forced-colors: active) {
  .checksum-value,
  .steps > li::before { border: 1px solid CanvasText; }

  .steps > li::after { background: CanvasText; }
  .flatlist { border-left: 1px solid CanvasText; }
  .principle > .more { border-top: 1px solid CanvasText; }
  .dl-ack.is-got { border: 2px solid Highlight; }

  .drop,
  .drop-btn,
  .rv-count,
  .safety-tag,
  .rv-tag,
  .rv-quotes code { border: 1px solid CanvasText; }

  .rv-gap > summary,
  .rv-neverwhy > li { border-left: 2px solid CanvasText; }

  .rv-flags li,
  .rv-empty,
  .rv-placeholder { border-left: 2px solid CanvasText; }

  .drop.loaded { border: 2px solid Highlight; }
}


/* ============================================================================
   10. PRINT - the review is the artifact worth putting on paper
   The load control and the buttons are interactions; on paper they are noise.
   review.js opens every row before printing, so nothing is lost to a closed
   disclosure.
   ========================================================================== */

@media print {
  .review-grid,
  .rv-actions,
  .step-go,
  /* Every row is open on paper, so "Show the 37 settings..." is an instruction
     for something that has already happened. */
  .rv-more > summary,
  .rv-placeholder { display: none !important; }

  .rv-more > .more-body { margin-top: 0; }

  .review-out { margin-top: 0; }
  .rv-entry,
  .rv-facts > div { break-inside: avoid; }
  .rv-bucket { break-before: auto; }
  .rv-quotes code { white-space: pre-wrap; }
}
