/* ═══════════════════════════════════════════════════════════════
   RESEARCH-VIZ — diagram set for uxresearch.html
   ───────────────────────────────────────────────────────────────
   This study has no product screens to show: the evidence is
   organisational, not visual. These components carry that weight
   instead — a pair of verdict cards, a narrowing funnel, an
   escalation, a reframe, and a score. All drawn in CSS; the only
   raster is the bank's own logo over the hero pair.

   Theme-agnostic: every hue reads from --study-1..4, so the set
   drops onto any case study, not just gold. The single exception
   is the "after" verdict card, which reproduces a real WEVO
   scorecard and therefore keeps that vendor's sampled colours
   under any study theme. Nothing here invents UI that never
   existed: the empty card is explicitly an absence, not a mock
   screen.
   ═══════════════════════════════════════════════════════════════ */

/* Shared shell — sits inside .study-visual, which supplies the
   border/background. These reset that padding to go edge-to-edge. */
.rv {
  width: 100%;
  height: 100%;
  display: flex;
  flex-direction: column;
  padding: clamp(20px, 3.2vw, 34px);
  gap: var(--sp-3);
  text-align: left;
  color: var(--text-primary);
  font-family: var(--font-sans);
  letter-spacing: normal;
}
/* The .study-visual aspect-ratio variants (is-hero, is-wide, …) exist to
   reserve space for images of known dimensions. These diagrams are drawn
   from text, so their height has to come from their content instead, or
   long labels clip at narrow widths.

   Being loaded after case-study.css is NOT enough on its own: the hero's
   ratio is set by `.is-visual-lead .study-visual.is-hero` at (0,3,0), and
   `:has(.rv)` only scores (0,2,0) because :has() inherits the specificity
   of its argument. That rule silently lost, and the hero stayed a 640/470
   box; it just happened to be taller than the diagram until the verdict
   cards arrived, at which point content overflowed the panel top and
   bottom. The second selector below outranks it properly. */
.study-visual:has(.rv) {
  padding: 0;
  align-items: stretch;
  aspect-ratio: auto;
}
.is-visual-lead .study-visual.is-hero:has(.rv) { aspect-ratio: auto; }

/* Eyebrow label — mono, uppercase, the portfolio's caption voice */
.rv-eyebrow {
  font-family: var(--font-mono);
  font-size: 0.7rem;
  font-weight: 700;
  letter-spacing: .01em;
  color: var(--study-2);
  margin: 0;
}
.rv-caption {
  margin: 0;
  font-family: var(--font-mono);
  font-size: 0.74rem;
  line-height: 1.5;
  color: var(--text-muted);
}

/* ── 1. VERDICT CARDS ──────────────────────────────────────────
   Hero visual. The thesis as a scorecard the bank never used to
   have: the same card twice, once with every field empty because
   nothing had ever been measured, once carrying the real result.
   The absence is the point, so the empty card keeps the full
   skeleton rather than collapsing to a smaller placeholder.

   The "after" face reproduces the WEVO scorecard for the Checking
   product page. Its three colours are sampled from the real report
   (assets/images/cards/research/ux-research-score-90.png) and pinned
   as literals: this is the one element in the set that is a record
   of someone else's artifact, so it does NOT follow --study-1..4.
   Everything around it themes normally.

   ⚑ THAT PNG AND ITS 87 SIBLING ARE NOW REFERENCED ONLY BY THIS COMMENT.
   They were the homepage research card's artwork until 2026-09-07, when it
   was rebuilt as a drawn before/after gauge row (see the guard on
   .fc-art--join in index.html). Keep both files: they are the provenance
   for the three pinned colours above, and an orphan sweep that deletes
   them takes the record of where those literals came from with them.

   Type inside a card is sized in cqw against the card itself, so
   both faces stay identical at any column width and neither has to
   be re-tuned per breakpoint.
   ────────────────────────────────────────────────────────────── */
.rv-verdict {
  justify-content: center;
  gap: var(--sp-3);
  padding-block: clamp(34px, 5vw, 58px);   /* the pair wants air, more than .rv gives */
}

/* The bank's own mark rather than a mono eyebrow, centred over the pair.
   Needs to outrank .study-visual img {width:100%;height:100%;cover}. */
.study-visual .rv-verdict-logo {
  display: block;
  width: auto;
  height: clamp(30px, 3.7vw, 42px);
  margin: 0 auto var(--sp-1);
  object-fit: contain;
}

.rv-verdict-pair {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto minmax(0, 1fr);
  align-items: start;
  gap: clamp(8px, 1.6vw, 18px);
}

.rv-verdict-arrow {
  align-self: center;
  margin-top: 8%;
  font-size: clamp(1rem, 2.2vw, 1.4rem);
  line-height: 1;
  color: var(--study-2);
}

.rv-card { margin: 0; display: flex; flex-direction: column; gap: 9px; }

.rv-card-era {
  font-family: var(--font-mono);
  font-size: 0.68rem;
  font-weight: 700;
  letter-spacing: .01em;
  color: var(--text-muted);
}
.rv-card.is-real .rv-card-era { color: var(--study-1); }

/* The card face. Geometry is measured off the real scorecard PNG, not
   eyeballed: the visible face inside that 250x310 export is 234x294, its
   arc is 51.7% of the face width with a stroke 7.7% of it, and its five
   text bands sit at fixed percentages of the face height. Children are
   therefore positioned absolutely by percentage rather than flowed, so
   the reproduction holds at any size. Each `top` is the measured band top
   minus that element's own line-box leading, which is why the numbers
   here read 1-2% shy of the measurements above.

   cqw on a descendant resolves against this element's content box. Padding
   is 0 so 1cqw == 1% of the face width and the measured percentages drop
   in unchanged. Never set this element's own padding or gap in cqw: those
   resolve against the OUTER container (none here, so the viewport) and
   silently blow the layout out. */
.rv-card-face {
  container-type: inline-size;
  position: relative;
  /* Every child is absolutely positioned, so this box has no intrinsic
     width. Without an explicit one it collapses to zero the moment it
     stops being stretched (e.g. the centred phone layout below). */
  width: 100%;
  aspect-ratio: 234 / 294;
  border-radius: 3.2%;              /* matches the real card's corner */
  padding: 0;
  overflow: hidden;
}
.rv-card.is-empty .rv-card-face {
  border: 2px dashed color-mix(in srgb, var(--study-2) 42%, transparent);
  background: transparent;
  color: var(--text-muted);
}
.rv-card.is-real .rv-card-face {
  background: #3e6eab;                    /* sampled: card ground */
  color: #fff;
  box-shadow: 0 6px 16px rgba(0, 0, 0, 0.18);
}

.rv-card-face > * {
  position: absolute;
  left: 0;
  right: 0;
  margin: 0;
  line-height: 1;
  text-align: center;
}

/* Vendor wordmark, real card only. The empty card has no logo well at all:
   a dashed placeholder there read as an empty form field. */
.rv-card-brand {
  top: 6.6%;
  font-family: var(--font-sans);
  font-size: 7.4cqw;
  font-weight: 700;
  letter-spacing: 0.01em;
}
.rv-card-lead   { top: 15.7%; font-size: 4.5cqw; opacity: 0.92; }
.rv-card-verdict{ top: 21.5%; font-size: 8.1cqw; font-weight: 700; }
.rv-card.is-empty .rv-card-verdict { opacity: 0.8; }

.rv-card-gauge {
  top: 40.3%;
  width: 55%;
  height: auto;
  margin: 0 auto;
}
/* Arc length = π × 80 ≈ 251.3. 90/100 → 226.2 drawn. */
.rv-card-track { stroke: #8ba3c1; stroke-width: 28; }        /* sampled */
.rv-card-fill  { stroke: #6aac5b; stroke-width: 28;          /* sampled */
  stroke-dasharray: 226.2 251.3;
}
/* Solid and faint, not dashed: ticking the empty arc made it read as a
   gauge mid-load rather than a measurement that was never taken. */
.rv-card.is-empty .rv-card-track {
  stroke: currentColor;
  opacity: 0.16;
}

.rv-card-score { top: 61.4%; font-size: 16.6cqw; font-weight: 400; }
.rv-card-note  { top: 81.5%; font-size: 5.6cqw; opacity: 0.92; }

.rv-card-foot {
  margin: 0;
  font-size: 0.84rem;
  line-height: 1.5;
  color: var(--text-secondary);
}

@media (max-width: 560px) {
  /* Two cards plus an arrow will not hold their ratio at phone widths.
     Stack them and let the era labels carry the sequence. Stacked, a
     card would otherwise take the full column and stand taller than the
     screen, so the face is capped near its native size and centred; the
     foot copy still runs the full measure. */
  .rv-verdict-pair { grid-template-columns: minmax(0, 1fr); row-gap: 22px; }
  .rv-verdict-arrow { transform: rotate(90deg); margin: 0 auto; }
  .rv-card { align-items: center; }
  .rv-card-face { max-width: 250px; }
  .rv-card-foot { align-self: stretch; }
}

/* ── 2b. PROCESS LAYOUT ───────────────────────────────────────
   Process is two columns: the five steps run down the left, every
   visual sits in the same grid row as the step it belongs to on the
   right. Nothing pins and nothing swaps; all of it is on the page at
   once.

   Placement is by data-row rather than source order so the DOM can
   stay in narrative order (step, its visual, next step). That single
   ordering serves both layouts: the grid places it in columns here,
   and the phone layout below just lets it flow.

   Every step has its own visual, so there are no empty cells and no
   row spans: step N sits beside cell N.
   ────────────────────────────────────────────────────────────── */
.rv-process {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  column-gap: clamp(28px, 3.6vw, 56px);
  row-gap: var(--sp-4);
  align-items: start;
}

.rv-process > .study-step      { grid-column: 1; }
.rv-process > .rv-process-visual { grid-column: 2; }

.rv-process > [data-row="1"]   { grid-row: 1; }
.rv-process > [data-row="2"]   { grid-row: 2; }
.rv-process > [data-row="3"]   { grid-row: 3; }
.rv-process > [data-row="4"]   { grid-row: 4; }
.rv-process > [data-row="5"]   { grid-row: 5; }
/* Four steps, four visual cells, one row each. The 1-2 / 4-5 spans
   went away with the five-step structure. */

/* A cell is a beat, not a single graphic: the funnel cell also carries
   the matrix that did the narrowing, the ladder cell the aside
   explaining why the climb had to go that high. */
/* Centred, not top-aligned. Now that the graphics are shorter than the
   steps they sit beside, pinning them to the top of the row left a void
   underneath; centring also lands each one beside the paragraph it
   actually illustrates (the matrix beside the sentence that names it,
   the cycle beside the worked example). */
.rv-process-visual {
  display: flex;
  flex-direction: column;
  justify-content: center;
  gap: var(--sp-3);
  min-height: 100%;
}

/* These diagrams sit bare on the page, so .rv's panel padding (which
   existed to inset it from the .study-visual border) has nothing to
   inset from. */
.rv-process .rv { padding: 0; }

@media (max-width: 860px) {
  /* One column. Source order is already narrative, so dropping the
     explicit placement is the whole change.

     align-items has to be reset with it. In the grid above, `start`
     controls the BLOCK axis (visuals sit at the top of their row).
     Here the container is a column flex, so the same property controls
     the INLINE axis instead, and every visual cell shrank to its own
     content width. The finalists cell was the visible casualty: 317px
     against 390px for the prose column and every other cell at a 430px
     viewport, i.e. a graphic that looked randomly indented. */
  .rv-process {
    display: flex;
    flex-direction: column;
    gap: var(--sp-4);
    align-items: stretch;
  }
}

/* ── 2c. THE TWO PROPOSALS ─────────────────────────────────────
   Step 2's visual. (Headed "Step 3" until 2026-08-05; the numbers here
   were left over from the pre-2026-07-26 five-step shape.) The funnel
   used to end on "WEVO / Recommended", which put Step 2's conclusion
   inside Step 1's graphic. It reads as its own
   beat instead: the winner lit and itemised, the runner-up recessive
   and honest. UserTesting names no gaps at all: the note that used to
   sit here ("couldn't match on price or reporting clarity") said what
   the prose across the gutter already says, which is the one thing
   this section forbids. Its absence is the point. A card with three
   checked items beside a card with none needs no caption, and naming
   nothing cannot overclaim: it was not beaten on service.

   The Runner-up tag is structure, not a reason: it says where the
   card sits in the outcome, which the prose never states, so it adds
   rather than repeats. It also stops an itemless card reading as
   unfinished. "Finalist" would have been wrong here, since both
   cards are finalists and the tag appears on only one.
   ────────────────────────────────────────────────────────────── */
.rv-finalists { gap: 10px; justify-content: center; }

.rv-final-card {
  padding: 12px 14px;
  border: 2px solid var(--study-1);
  border-radius: var(--radius-xs);
  background: #fff;
}
.rv-final-card.is-chosen { background: var(--study-4); }

.rv-final-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 12px;
  margin-bottom: 8px;
}
/* Defensive since 2026-08-05: the runner-up card gained a list, so the
   head is no longer :last-child on either card. Kept so a card with a
   head and nothing else does not hang an 8px gutter under its name. */
.rv-final-head:last-child {
  margin-bottom: 0;
}
.rv-final-name {
  font-family: var(--font-serif);
  font-size: 0.98rem;
  font-weight: 700;
  color: var(--study-1);
}
.rv-final-tag {
  font-family: var(--font-mono);
  font-size: 0.62rem;
  font-weight: 700;
  letter-spacing: .01em;
  color: var(--study-2);
}

/* The chosen card fills with --study-4 (#EFAF21 on this page), and
   --study-2 (#725108) over it is 3.74:1 at 9.9px, under the 4.5 floor
   that applies to text this small even at 700 weight. --study-1
   (#261B03) over the same fill is 8.72:1. Scoped to .is-chosen so the
   runner-up tag keeps --study-2 on white (7.25:1, already passing) and
   stays the recessive of the two, which is the point of the pair. */
.rv-final-card.is-chosen .rv-final-tag,
.rv-final-card.is-chosen .rv-final-list li::before {
  color: var(--study-1);
}

.rv-final-list { margin: 0; padding: 0; list-style: none;
  display: flex; flex-direction: column; gap: 6px; }
.rv-final-list li {
  display: grid;
  grid-template-columns: 15px minmax(0, 1fr);
  gap: 9px;
  font-size: 0.82rem;
  line-height: 1.4;
  color: var(--study-1);
}
.rv-final-list li::before {
  content: "\2713";
  font-size: 0.85rem;
  line-height: 1.5;
  color: var(--study-2);
}
/* The runner-up's rows are shortfalls, not features, so they cannot carry
   the same tick as the recommendation across the gutter. --study-2 on white
   is 7.25:1, the same pairing the runner-up tag already uses. */
.rv-final-list.is-short li::before { content: "\2715"; }

/* ── 2d. FROM FINDING TO SHIPPED ──────────────────────────────
   Step 5's visual, and as of 2026-09-01 the ONLY figure in that row:
   the before/after pair that used to sit below it moved to Solution
   Decision 2, where its caption's propagation argument belongs. (It was
   Step 4's until the 2026-08-05 split moved both to the step they
   evidence.) ⚑ That move also left the step's .study-seealso arrow
   pointing at THIS diagram, which proves nothing about an image beside
   copy; the tie was struck. Move a figure, grep the row it left. Every other diagram in this column is a vertical stack of
   boxes; a fourth would have made the right column one texture
   repeated. This one runs horizontally instead, which is what earns
   it a place beside the three stacks.

   It is a path, not a cycle. It used to close on itself, on the
   argument that the chain came back around and the next study started
   higher up; that argument was false (see the note on the removed
   .rv-cycle-return below). The four nodes carry one study from finding
   to shipped change, which is exactly what the prose beside them
   walks through. The .rv-cycle class name is left alone rather than
   renamed: it is referenced from the HTML and from three comments,
   and the churn buys nothing.

   Labels are two words each on purpose. The paragraph beside this
   already narrates the stages, so spelling them out again here was
   the same both-columns-say-it-twice problem as the Step 2 bullets.
   ────────────────────────────────────────────────────────────── */
.rv-cycle { gap: 10px; }

.rv-cycle-track {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: 22px;
  margin: 0;
  padding: 0;
  list-style: none;
}

/* One stripe of the page ribbon per stage. js/ribbon.js draws the band
   edging from --study-1..4, so these are literally the same four colours
   wrapping the section, run bright to dark so the darkest lands on the
   stage that ships. That direction matches the climb directly above it,
   which also darkens toward its payoff; opposing ramps on two adjacent
   diagrams read as an accident. Borders come off, because a border
   over a filled ribbon colour just muddies it. */
.rv-cycle-node {
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 5px;
  padding: 10px 7px;
  border-radius: var(--radius-xs);
  background: var(--fill);
  color: var(--ink);
  text-align: center;
}
.rv-cycle-track .rv-cycle-node:nth-child(1) { --fill: var(--study-4); --ink: var(--study-1); }
.rv-cycle-track .rv-cycle-node:nth-child(2) { --fill: var(--study-3); --ink: var(--study-1); }
.rv-cycle-track .rv-cycle-node:nth-child(3) { --fill: var(--study-2); --ink: #fff; }
.rv-cycle-track .rv-cycle-node:nth-child(4) { --fill: var(--study-1); --ink: #fff; }

/* Arrow into every node but the first. The stem spans the whole gutter
   so the chain reads as connected rather than as marks floating between
   boxes; the head lands on the node edge. Drawn on the later node, so
   the last one trails nothing.

   One element, not two. It was a 2px ::before stem plus a border-triangle
   ::after, both centred with top:50%. That is exact on paper and visibly
   off on screen: the node lands on a fractional height (111.0625px here),
   so the centreline falls mid-pixel, and a 2px line antialiased across
   three device rows reads as sitting off the crisp tip of the triangle.
   Same centre, two independent roundings. Drawing stem and head as one
   clipped box gives the browser a single box to round, so they cannot
   disagree at any node height. */
.rv-cycle-node + .rv-cycle-node::before {
  content: "";
  position: absolute;
  left: -22px;
  top: 50%;
  width: 22px;
  height: 10px;
  margin-top: -5px;
  background: var(--study-2);
  clip-path: polygon(
    0    40%,  14px 40%,
    14px 0,    22px 50%,
    14px 100%, 14px 60%,
    0    60%
  );
}

.rv-cycle-no {
  font-family: var(--font-mono);
  font-size: 0.58rem;
  font-weight: 700;
  letter-spacing: 0.12em;
  opacity: 0.7;
}
.rv-cycle-what {
  font-family: var(--font-sans);
  font-size: 0.78rem;
  line-height: 1.3;
}
/* The instance line under each stage. Four boxes reading opportunity →
   exploration → decision → shipped describe any design process at any
   company; the same four with one real study running through them are
   evidence. The stage name stays because the loop happened nine times
   and only one of them is drawn here.

   Hairline rule rather than a gap: at this size the two lines need a
   boundary to read as label-then-instance instead of one wrapped
   phrase. currentColor because the ink flips to white on nodes 3 and 4.

   ⚑ UNUSED as of 2026-08-05, both this rule and its 900px override below.
   The four .rv-cycle-real subtitles came out of the markup that day: each
   restated the paragraph beside it, and at a measured 9.9px they were the
   graphic's unreadable layer. Kept one round rather than deleted, same as
   the .tpb block in case-study.css. Delete on review if the stripped cycle
   holds. Do NOT re-add the class to bring the rule back into use. */
.rv-cycle-real {
  font-family: var(--font-mono);
  font-size: 0.62rem;
  line-height: 1.35;
  padding-top: 6px;
  border-top: 1px solid color-mix(in srgb, currentColor 28%, transparent);
  width: 100%;
  opacity: 0.88;
  text-wrap: balance;
}

/* ── 2e. THE TEST SLIP ─────────────────────────────────────────
   Step 4. The section's whole risk is that it proves procurement and
   asserts research, so one study gets shown the way it was written:
   goal, question, screening. Styled as a slip rather than a card —
   ruled rows, mono labels, no fill — because it is a record being
   quoted, not a component being designed. It sits directly above the
   cycle it explains, and the two share the rail cell.
   ────────────────────────────────────────────────────────────── */
.rv-testslip { gap: 10px; }

.rv-slip-name {
  margin: 0;
  display: flex;
  align-items: baseline;
  flex-wrap: wrap;
  gap: 10px;
  font-family: var(--font-sans);
  font-size: 1.02rem;
  font-weight: 700;
  line-height: 1.25;
  color: var(--text-primary);
}
.rv-slip-device {
  font-family: var(--font-mono);
  font-size: 0.6rem;
  font-weight: 700;
  letter-spacing: .01em;
  padding: 3px 7px;
  border-radius: var(--radius-xs);
  background: var(--study-4);
  color: var(--study-1);
}

/* Label column is fixed so the three definitions align on one edge;
   it wraps to a single column on phones, where 82px of gutter costs
   more than the alignment buys. */
.rv-slip-spec { margin: 0; display: flex; flex-direction: column; }
.rv-slip-row {
  display: grid;
  grid-template-columns: 82px minmax(0, 1fr);
  gap: 14px;
  padding: 9px 0;
  border-top: 1px solid color-mix(in srgb, var(--study-2) 22%, transparent);
}
.rv-slip-row:last-child { border-bottom: 1px solid color-mix(in srgb, var(--study-2) 22%, transparent); }
.rv-slip-row dt {
  font-family: var(--font-mono);
  font-size: 0.62rem;
  font-weight: 700;
  letter-spacing: .01em;
  line-height: 1.7;
  color: var(--study-2);
}
.rv-slip-row dd {
  margin: 0;
  font-family: var(--font-sans);
  font-size: 0.84rem;
  line-height: 1.45;
  color: var(--text-secondary);
}

.rv-slip-link {
  align-self: start;
  font-family: var(--font-mono);
  font-size: 0.72rem;
  font-weight: 700;
  letter-spacing: 0.06em;
  color: var(--study-2);
  text-decoration: none;
  border-bottom: 1px solid color-mix(in srgb, var(--study-2) 40%, transparent);
  padding-bottom: 2px;
  transition: color 0.18s ease, border-color 0.18s ease;
}
.rv-slip-link:hover,
.rv-slip-link:focus-visible { color: var(--study-1); border-bottom-color: var(--study-1); }

/* The return arc (.rv-cycle-return) was removed 2026-07-29, along with
   the bracket-and-arrowhead geometry that drew it. It ran from node 04
   back to node 01, which draws a ratchet: each study starting where the
   last one shipped. That is not what happened. All nine studies ran
   June to November 2025 and the first ticket reached the live site in
   February 2026, so nothing shipped between studies and there was no
   loop to close. It is the same claim the caption "This path ran nine
   times" was already removed for. Do not re-add either. */

/* ── 2f. THE CARD, BEFORE AND AFTER ───────────────────────────
   ⚑ SOLUTION DECISION 2's visual as of 2026-09-01. It was Step 5's, and
   Step 4's before the 2026-08-05 split. The rules in this section are
   still the figure's own; what changed is the column it renders in, so
   see the .study-solution override further down for the cap and the
   zeroed padding that move required. This is the only place the height change is quantified. As
   of 2026-07-29 the prose no longer carries a competing pixel pair,
   so the caption here owns the number, and it sits directly under the
   two screenshots that evidence it: one card that used to fill a phone
   screen, and the card it became.

   Stacked, not side by side. The cell is one column of a two-column
   grid, so a side-by-side pair would render each shot at ~45% of an
   already-narrow column and neither would be legible. Stacking also
   lets the two heights read against each other directly, which is
   the comparison the visual exists to make.

   The shots are illustrative of the shape change, not a calibrated
   height comparison; the measured numbers in the labels carry that.
   ────────────────────────────────────────────────────────────── */
/* The cap belongs on the whole block, not just the pair. With it on
   the pair alone, the eyebrow and the caption ran the full column
   while the shots stopped at 400px, so the caption trailed past the
   right edge of the thing it captions and the group had no shared
   edge to sit against. */
.rv-ba {
  gap: 10px;
  max-width: 400px;
}

/* ⚑⚑ 360px IN SOLUTION, AND THE NUMBER IS MEASURED, NOT CHOSEN. When this
   figure moved to Decision 2 on 2026-09-01 the cap was taken off first, on
   the argument that the cap above exists to stop the pair out-shouting the
   slip and the cycle, which are Process's other occupants and not this
   row's. That argument is sound and the result was still wrong: uncapped at
   the 576px column the figure ran 752px against 455px of prose, 297px
   taller than the words it illustrates.
   The rest of the section sets the target. Decision 1's quote pair sits 97px
   taller than its prose and Decision 3's crops 80px taller, so a figure that
   overhangs its paragraph by roughly 90px is this section's rhythm rather
   than a defect. Measured across the range: none +297, 520 +254, 460 +190,
   400 +125, 360 +100, 320 +57. 360 is the width that puts row 2 in line with
   rows 1 and 3.
   ⚑ SO THE CAP SURVIVED THE MOVE FOR A DIFFERENT REASON THAN IT WAS WRITTEN
   FOR. Do not restore max-width:none on the original argument; it was tried,
   and it is the height that kills it, not the width. .study-visual centres
   its child, so the narrower pair sits with even gutters rather than adrift
   to one side. RE-MEASURE IF DECISION 2'S PROSE IS EVER EDITED.
   ⚑ AND IT IS LEFT-ALIGNED, NOT CENTRED, WHICH IS THE PART THAT IS NOT
   ARBITRARY. .study-visual is a centring flex container, so a capped child
   sits in the middle of the column by default. That put this figure's
   eyebrow 87px inside the column while Decision 1's and Decision 3's both
   started at its left edge: three figures, two on a rail and one adrift.
   margin-right:auto absorbs the slack on the right instead, so all three
   eyebrows share one left rail down the section.
   ⚑ padding:0 GOES WITH IT, for the same reason .rv-desk and .rv-quicklinks
   zero theirs: .rv's clamp(20px,3.2vw,34px) only ever existed to inset the
   block from a card border, and this column has no card. Left in, it was
   both the last 34px of eyebrow misalignment AND 68px eaten out of the cap,
   so the shots rendered at 292px inside a 360px box. The measurements above
   were taken WITH the padding; with it gone, 360 lands at +87. */
.study-solution .rv-ba {
  max-width: 360px;
  margin-right: auto;
  padding: 0;
}

/* Capped, not full-bleed. At full column width these are the largest
   objects in the section, which puts the most weight on the least
   analytical thing in it: the slip and the cycle are the reasoning, and
   a pair of screenshots was out-shouting both. 400px is enough to read
   the card's shape, which is all they have to say. */
.rv-ba-pair {
  display: flex;
  flex-direction: column;
  gap: 12px;
}

.rv-ba-shot { margin: 0; }

/* The radius tracks the card inside the screenshot, not the panel
   convention. A 6px outline around a ~14px card corner reads as two
   unrelated rectangles; concentric corners read as a frame. The crop
   leaves ~2px of page around the card, so the outer radius runs
   slightly wider than the inner one. */
.rv-ba-shot img {
  display: block;
  width: 100%;
  height: auto;
  border: 1px solid var(--study-4);
  border-radius: 9px;
}

/* The "now" shot is the one that shipped, so it gets the same
   emphasis border the cycle's shipped node gets.

   Its radius is smaller because the two screenshots are not the same
   intrinsic width: 699px for the old card, 776px for the new one, both
   rendered at the pair's width. The card's own corner is a fixed CSS
   radius in the source, so it shrinks by a different factor in each
   frame and one radius cannot sit concentric on both. 699/776 of 9px
   is what keeps this frame parallel to the card it holds. The two
   always share a display width, so the ratio holds at every viewport. */
.rv-ba-shot.is-now img {
  border-color: var(--study-2);
  border-radius: 8px;
}

.rv-ba-shot figcaption {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 10px;
  padding-top: 6px;
  font-family: var(--font-mono);
  font-size: 0.7rem;
  letter-spacing: .01em;
  color: var(--text-muted);
}

.rv-ba-shot figcaption b {
  font-weight: 700;
  letter-spacing: 0.02em;
  color: var(--study-2);
}

/* ── 3. THE AUTHORITY LINE ────────────────────────────────────
   Step 3. A team-level idea had to travel to the CEO, and the reason
   is the point: spend authority for external research existed only at
   the top, so the distance was structural rather than persuasive.

   ⚑ THIS WAS A FIVE-BAR ASCENDING CHART until 2026-08-05. Bars assert
   a rising QUANTITY and nothing here is measured; a reader who reads
   charts hunts for the axis and finds none. It also encoded four
   people and one outcome (the contract) in one bar series.

   ⚑ AN ORG CHART WAS CONSIDERED AND REJECTED. Org charts encode
   reporting structure, branches and siblings and spans of control,
   none of which this story has or needs. It would draw the wrong
   variable: the claim is about who could SPEND, not who reported to
   whom, and those are not the same axis.

   ⚑ RUNS HORIZONTALLY TO HOLD THE OLD HEIGHT (Daniel, 2026-08-05).
   The bars were 167px in a 474px column with 62px of slack, and five
   stacked stops cannot fit that. The vertical form is the better
   drawing of a climb and it does not fit, so this is a constraint
   answer, not a preference. Do not "improve" it back to vertical
   without re-checking the row.

   ⚑ The bracket label is THREE WORDS on purpose; the aside above
   carries the reason in prose. Growing it into a sentence recreates
   the both-columns-say-it-twice problem this section forbids.
   ────────────────────────────────────────────────────────────── */
.rv-authority { gap: 12px; }

/* 3 stops, the gate, then 2 stops on row 1; the bracket spans the
   first three on row 2. Explicit columns rather than auto-flow so the
   bracket can be told exactly which stops it encloses. */
.rv-auth-track {
  margin: 0;
  padding: 0;
  list-style: none;
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr)) auto repeat(2, minmax(0, 1fr));
  align-items: start;
}

.rv-auth-stop {
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 8px;
  padding-right: 10px;
  min-width: 0;
}
/* the path, drawn stop to stop rather than as one rule behind them, so
   the gate interrupts it instead of being painted over */
.rv-auth-stop::after {
  content: "";
  position: absolute;
  left: 13px;
  right: 0;
  top: 5px;
  height: 1px;
  background: var(--study-3);
}
.rv-auth-stop.is-signed::after { display: none; }

.rv-auth-dot {
  flex: none;
  width: 11px;
  height: 11px;
  border-radius: 50%;
  border: 1.5px solid var(--study-3);
  background: #fff;
  box-sizing: border-box;
}
/* where authority begins, and where it produced something */
.rv-auth-stop.is-authority .rv-auth-dot { border-color: var(--study-2); background: var(--study-4); }
.rv-auth-stop.is-signed    .rv-auth-dot { border-color: var(--study-1); background: var(--study-1); }

.rv-auth-who {
  font-family: var(--font-sans);
  font-size: 0.72rem;
  line-height: 1.25;
  color: var(--study-1);
}
.rv-auth-stop.is-signed .rv-auth-who { font-weight: 700; }
.rv-auth-when {
  display: block;
  font-family: var(--font-mono);
  font-size: 0.62rem;
  font-weight: 400;
  color: var(--text-muted);
}

/* the threshold. stretch, so it reads as a wall across the path rather
   than a tick on it */
.rv-auth-gate {
  align-self: stretch;
  width: 0;
  margin: 0 14px 0 4px;
  border-left: 1px dashed var(--study-2);
}

.rv-auth-bracket {
  grid-column: 1 / span 3;
  grid-row: 2;
  position: relative;
  margin: 10px 10px 0 0;
  padding-top: 7px;
  border-top: 1px solid var(--study-3);
  font-family: var(--font-mono);
  /* 0.66rem, not 0.6rem. Raised 2026-08-06: at 0.6rem this rendered 9.6px,
     which is SMALLER than the 9.9px .rv-cycle-real subtitles deleted the day
     before for being the unreadable layer. This label carries the whole
     argument of the graphic, so it cannot be the smallest type in the
     section. 0.66rem matches .rv-auth-who at the <=600px breakpoint. */
  font-size: 0.66rem;
  font-weight: 700;
  letter-spacing: .01em;
  line-height: 1.3;
  color: var(--study-2);
}
/* ticks rising toward the three stops the bracket encloses */
.rv-auth-bracket::before,
.rv-auth-bracket::after {
  content: "";
  position: absolute;
  top: -5px;
  width: 1px;
  height: 5px;
  background: var(--study-3);
}
.rv-auth-bracket::before { left: 0; }
.rv-auth-bracket::after  { right: 0; }

/* Below ~520px the six columns stop fitting. The path becomes a single
   column, the gate turns into a horizontal rule, and the bracket spans
   the whole thing. Height stops mattering here: the layout is already
   one column and the row constraint that forced horizontal is gone. */
@media (max-width: 520px) {
  .rv-auth-track { grid-template-columns: minmax(0, 1fr); }
  .rv-auth-stop { flex-direction: row; align-items: center; gap: 11px; padding: 5px 0; }
  .rv-auth-stop::after { display: none; }
  .rv-auth-gate {
    align-self: auto; width: auto; height: 0; margin: 6px 0;
    border-left: 0; border-top: 1px dashed var(--study-2);
  }
  /* grid-row: auto puts the bracket wherever its SOURCE position lands it.
     That is why the <li> was moved to sit after the three stops it encloses;
     see the note in the HTML. Do not reorder the list to "tidy" it. */
  .rv-auth-bracket { grid-column: 1; grid-row: auto; margin-right: 0; }
  .rv-auth-when { display: inline; margin-left: 8px; }
}

/* ── 4. WHAT REACHED THE EXECUTIVES ────────────────────────────
   Solution, Decision 1. One study in the two forms it could have
   arrived in: what 126 testers wrote, and the one readout the
   business acted on.

   ⚑⚑ THIS HEADER DESCRIBED A BUILD THAT WAS REPLACED ON 2026-07-30 AND
   SAID SO FOR A MONTH, DIRECTLY ABOVE THE CSS FOR THE ONE THAT SHIPPED.
   Corrected 2026-09-01. Three claims were wrong, and each is worth
   knowing because each was true of the thing it replaced:
   - "the transcript nobody reads" — WEVO collects WRITTEN OPEN-ENDS,
     not transcripts, which is the wrong noun on the one page whose
     subject is research competence.
   - "The left panel is unreadable by construction. It is rules, not
     text" — it holds three real, readable participant quotes, verbatim
     from the report. Unreadability was the POINT of the gray skeleton
     wall that used to sit there; it is the opposite of the point now.
   - "The pair stretches (flex: 1)" — contradicted 45 lines below by
     this same file: "flex:none, not flex:1. Growing to fill the column
     is what produced the dead gold."
   ⚑ THE LESSON, and it generalises past this file: a SECTION HEADER
   comment rots silently because the fix lands on the rules underneath
   it and nobody re-reads the banner. When you replace what a block
   renders, re-read the header above it.
   ────────────────────────────────────────────────────────────── */
/* Bare on the page, no .study-visual card behind it. Same call as the
   Process diagrams (§2b): the panels are already outlined boxes, so a
   surface + border around them was a frame around a frame. Scoped to
   the section rather than :has(.rv-desk), because :has() takes the
   specificity of its argument and this file has lost that fight before.
   Padding goes with the card; without it the eyebrow lines up with the
   prose column opposite. */
.study-solution .study-visual,
.study-impact .study-visual {
  border: 0;
  border-radius: 0;
  background: none;
  overflow: visible;
}
/* Impact's visual cell holds two things now, the gauge and the diagnostics
   note under it, so it stacks. .study-visual is flex with the default row
   direction, which would have put them side by side. */
.study-impact .study-visual {
  flex-direction: column;
  justify-content: center;
  /* sp-3, one step above .rv-gauge's own internal sp-2 rhythm. sp-4 was
     too far: the note read as a separate block and, being full width and
     left-aligned at the time, paired with the prose across the gutter
     instead. Matching the internal 16px exactly went too far the other
     way and made it a fourth element of the gauge. 24px against 16px is
     the standard grouping signal: related, but its own fact. */
  gap: var(--sp-3);
}
/* .rv is height:100% for the framed slots it was written for. In a flex
   column with a sibling it has to size to its own content instead.

   padding-block goes for the same reason the card did: .rv's padding only
   ever existed to inset it from a border. Left in, it put 34px under the
   legend on top of the 16px flex gap, so the note sat 50px off a stack
   whose own internal rhythm is 16px, and read as a separate block no
   matter how it was styled. */
.study-impact .rv-gauge { height: auto; padding-block: 0; }

/* Bare, like everything else in this column. A filled callout under an
   unframed gauge would be the only carded object in the section.

   Sized and set to the gauge, not to the prose opposite. The gauge figure
   is min(100%, 300px) centred with a centred eyebrow and legend; at full
   column width, left-aligned, at 0.92rem, this note matched the paragraph
   across the gutter on every one of those counts and read as its second
   column. Narrow, centred and a step quieter puts it in the gauge's
   composition instead. */
.rv-diag {
  width: 100%;
  max-width: 360px;
  margin-inline: auto;
  text-align: center;
}
.rv-diag-body {
  margin: 7px 0 0;
  font-family: var(--font-sans);
  font-size: 0.8rem;
  line-height: 1.55;
  color: var(--text-secondary);
}
.rv-diag-body b { color: var(--study-2); }
/* height:auto, against .rv's height:100%. The prose column roughly
   doubled when Decision 2 landed, and a stretched pair grew with it:
   the quotes filled the top third and the readout ran a column of bare
   gold down the rest. The pair now sizes to its own content.

   It does NOT end up centred: case-study.css's
   `.study-solution .study-two-col.is-center { align-items: stretch }`
   is (0,3,0) and outranks the .is-center default, so this column is
   stretched and the graphic sits at the TOP of it.

   ⚑ THE REST OF THIS NOTE IS SUPERSEDED, 2026-09-01. It read: top-aligned
   puts the pair beside Decision 1, which is the decision it illustrates,
   and the words set the row height, so do not "fix" the empty column
   below it. That was a workaround, not a render to defend. Decision 1 now
   has its OWN row and the pair sits in it, so the alignment is structural
   and the empty column is gone. Top alignment is still correct and still
   wanted; it just no longer has to compensate for two other decisions
   sharing the column. */
.rv-desk { height: auto; gap: var(--sp-2); padding: 0; }

/* Columns flipped 2026-07-30. The raw panel used to be the narrow one
   (0.82fr) because it held an abstract texture that read fine at any
   width. It now holds three real participant quotes and needs the room;
   the readout is a score, three figures and a line, and does not. */
/* flex:none, not flex:1. Growing to fill the column is what produced
   the dead gold; align-items:stretch still keeps the two panels the
   same height as each other, which is the part worth having. */
.rv-desk-pair {
  flex: none;
  min-height: 0;
  display: grid;
  grid-template-columns: 1.14fr 0.86fr;
  align-items: stretch;
  gap: clamp(10px, 1.6vw, 18px);
}

.rv-desk-panel {
  display: flex;
  flex-direction: column;
  gap: 12px;
  min-height: 0;
  margin: 0;
  padding: clamp(14px, 2.1vw, 20px);
  border: 2px solid var(--border);
  border-radius: var(--radius-xs);
  background: #fff;
}
.rv-desk-panel.is-out {
  border-color: var(--study-1);
  background: var(--study-4);
}

.rv-desk-tag {
  font-family: var(--font-mono);
  font-size: 0.64rem;
  font-weight: 700;
  letter-spacing: .01em;
  color: var(--text-muted);
}
.rv-desk-panel.is-out .rv-desk-tag { color: var(--study-1); }

/* The quote stack. Real participant responses, so it is type, not
   texture, and it sets the height of the pair rather than absorbing
   whatever is left over.

   flex:none and no mask, both deliberate. flex:1 let the readout's
   shorter content decide the height, which clipped the third quote's
   rating label halfway through and read as a rendering fault rather
   than as "there is more". The "three of 126" signal does not need a
   fade: the panel tag says the count in words. (126 is the study's
   tester count, which is what the tag counts. 125 is a different real
   number, the written responses to the layout question. Daniel chose
   126 on 2026-07-30 so the page carries one number throughout. Do not
   "correct" it to 125.) */
.rv-desk-quotes {
  flex: none;
  display: flex;
  flex-direction: column;
  gap: 13px;
}
.rv-desk-quotes blockquote {
  margin: 0;
  padding-left: 11px;
  border-left: 2px solid var(--border);
}
.rv-desk-quotes p {
  margin: 0;
  font-size: 0.78rem;
  line-height: 1.5;
  color: var(--text-secondary);
}
.rv-desk-quotes cite {
  display: block;
  margin-top: 5px;
  font-family: var(--font-mono);
  font-style: normal;
  font-size: 0.62rem;
  letter-spacing: .01em;
  color: var(--text-muted);
}

/* DEAD as of 2026-07-30 on uxresearch.html, kept because
   archive/case-studies-2026-07-28/research-extras.html still renders the
   skeleton wall against this stylesheet. Delete both together or neither.

   The wall. Ragged right edges and blank rows read as paragraphs
   rather than as a barcode. The mask fades the last rows out so it
   reads as continuing past the panel instead of stopping neatly,
   which is the point: there is always more transcript. */
.rv-desk-wall {
  flex: 1;
  min-height: 96px;
  display: flex;
  flex-direction: column;
  justify-content: flex-start;
  gap: 6px;
  overflow: hidden;
  -webkit-mask-image: linear-gradient(#000 62%, transparent 100%);
          mask-image: linear-gradient(#000 62%, transparent 100%);
}
.rv-desk-wall span {
  display: block;
  flex: none;
  height: 5px;
  width: var(--w, 100%);
  border-radius: 2px;
  background: var(--border);
}
.rv-desk-wall .is-gap {
  height: 7px;
  background: none;
}

.rv-desk-score {
  display: flex;
  align-items: baseline;
  gap: 10px;
}
.rv-desk-num {
  font-family: var(--font-serif);
  font-size: clamp(1.9rem, 4.2vw, 2.5rem);
  font-weight: 700;
  line-height: 1;
  color: var(--study-1);
}
/* Replaced .rv-desk-bar 2026-07-30. The bar was width:83% beside a
   numeral reading 83, which is one number encoded twice inside one
   graphic. Same call the Impact gauge already took. A word beside the
   numeral says what it is, which the bar never did. */
.rv-desk-scorelab {
  font-family: var(--font-mono);
  font-size: 0.62rem;
  letter-spacing: .01em;
  color: var(--study-1);
  opacity: 0.75;
}

/* Three values, not two. 75/21 alone reads as a closed split and a
   reader adds them to 96 and goes looking for the other 4. The 4 is
   neutral, so it is now on the page and the row sums to 100. */
.rv-desk-split {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 8px;
  margin: 0;
  padding-top: 2px;
  border-top: 1px solid rgba(0, 0, 0, 0.12);
}
.rv-desk-split dt {
  font-family: var(--font-serif);
  font-size: 1.05rem;
  font-weight: 700;
  color: var(--study-1);
}
.rv-desk-split dd {
  margin: 0;
  font-size: 0.72rem;
  line-height: 1.3;
  color: var(--text-secondary);
}

/* margin-top:auto anchors the theme to the bottom of the panel. The
   readout is the shorter side of the pair, and .rv-desk-panel stretches,
   so without this the leftover height fell below the last line and left
   ~100px of empty gold. It now sits between the figures and the theme,
   where it reads as composition. */
.rv-desk-theme {
  margin: auto 0 0;
  padding-left: 12px;
  border-left: 3px solid var(--study-2);
  font-family: var(--font-serif);
  font-size: clamp(0.92rem, 1.8vw, 1.05rem);
  font-weight: 700;
  line-height: 1.35;
  color: var(--study-1);
}

.rv-desk-foot {
  margin: auto 0 0;
  font-family: var(--font-mono);
  font-size: 0.62rem;
  letter-spacing: 0.04em;
  color: var(--text-muted);
}

/* ── 5. SCORE GAUGE ────────────────────────────────────────────
   Impact. An arc for the 84 average with the 90 peak ticked on
   it. Drawn with stroke-dasharray so the number and the geometry
   can never drift apart.
   ────────────────────────────────────────────────────────────── */
.rv-gauge { align-items: center; justify-content: center; gap: var(--sp-2); }

.rv-gauge-figure { position: relative; width: min(100%, 300px); }
.rv-gauge-svg { display: block; width: 100%; height: auto; overflow: visible; }

.rv-gauge-track { stroke: var(--border); }

/* The drawn state is the RESTING state, not a transitioned-to state.
   initScrollReveal() strips .reveal/.is-visible ~1.1s after the element
   enters view, so anything keyed on those classes reverts once they go.
   An animation sidesteps that: it plays from empty to full and leaves the
   element at the resting value, which is already correct if JS never runs.
   Duration stays under the 1.1s strip so the sweep can't be cut short. */
.rv-gauge-fill {
  stroke: var(--study-3);
  /* Butt, not round. Two reasons, and the second is the real one.
     It matches .rv-gauge-track, which has always used the default cap,
     so both ends of the band now finish on the same line. And a round
     cap paints half a stroke-width past each end: at stroke-width 14
     that is 7px at both ends, so a dash drawn to exactly 84 was being
     painted about 225 of 251.3, which reads as 90. The gold was
     arriving at the peak tick it exists to fall short of. */
  stroke-linecap: butt;
  /* Arc length = π × 80 ≈ 251.3. 84/100 → 211.1 drawn, and with a butt
     cap 211.1 is what gets painted. */
  stroke-dasharray: 211.1 251.3;
  stroke-dashoffset: 0;
}
.reveal.is-visible .rv-gauge-fill {
  animation: rv-gauge-draw 0.95s cubic-bezier(0.22, 1, 0.36, 1) both;
}
@keyframes rv-gauge-draw {
  from { stroke-dashoffset: 251.3; }
  to   { stroke-dashoffset: 0; }
}

.rv-gauge-peak { stroke: var(--study-1); stroke-width: 3; }

.rv-gauge-readout {
  position: absolute;
  inset: auto 0 4%;
  text-align: center;
}
.rv-gauge-value {
  display: block;
  font-family: var(--font-serif);
  font-size: clamp(2.4rem, 6vw, 3.4rem);
  font-weight: 700;
  line-height: 1;
  color: var(--study-2);
}
.rv-gauge-unit {
  display: block;
  margin-top: 6px;
  font-family: var(--font-mono);
  font-size: 0.7rem;
  letter-spacing: .01em;
  color: var(--text-muted);
}

.rv-gauge-legend {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: var(--sp-1) var(--sp-3);
  font-family: var(--font-mono);
  font-size: 0.72rem;
  color: var(--text-secondary);
}
.rv-gauge-legend span { display: inline-flex; align-items: center; gap: 7px; }
.rv-gauge-legend i {
  width: 10px;
  height: 10px;
  border-radius: 2px;
  background: var(--study-3);
  font-style: normal;
}
/* Keyed to .is-peak, not :last-child. The legend is down to the single
   entry that needs a key, so a positional selector would be true only by
   accident and would silently mis-colour if an entry were ever added
   back. --study-1 matches .rv-gauge-peak's stroke above. */
.rv-gauge-legend .is-peak i { background: var(--study-1); }

/* ── 6. ARTIFACT CARD ──────────────────────────────────────────
   A supporting document referenced in passing, not presented.
   Deliberately small: the thumbnail is a recognisable sliver of
   the real page rather than a readable rendering, so the card
   reads as "this exists, open it if you want" instead of asking
   to be studied inline. Sits inside a step column, never full width.
   ────────────────────────────────────────────────────────────── */
/* Referenced, not presented: the matrix is evidence Daniel built, shown
   small on purpose. Restyled 2026-07-26 when it moved into the rail. The
   old card used none of the study vocabulary (no mono eyebrow, no serif
   title, no pill affordance), so beside the diagrams it read as a stray
   file attachment rather than part of the set. */
.rv-artifact {
  display: grid;
  grid-template-columns: 68px minmax(0, 1fr);
  gap: 14px;
  padding: 13px;
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  background: var(--surface);
  text-decoration: none;
  transition: border-color 0.18s ease, background 0.18s ease, transform 0.18s ease;
}
.rv-artifact:hover,
.rv-artifact:focus-visible {
  border-color: var(--study-3);
  background: #fff;
  transform: translateY(-2px);
}

.rv-artifact-thumb {
  height: 88px;
  border: 1px solid var(--border);
  border-radius: var(--radius-xs);
  overflow: hidden;
  background: #fff;
}
.rv-artifact-thumb img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: top center;
}

.rv-artifact-text {
  display: flex;
  flex-direction: column;
  gap: 5px;
  min-width: 0;
}
.rv-artifact-eyebrow {
  font-family: var(--font-mono);
  font-size: 0.62rem;
  font-weight: 700;
  letter-spacing: .01em;
  color: var(--study-2);
}
.rv-artifact-label {
  font-family: var(--font-serif);
  font-size: 1.02rem;
  font-weight: 700;
  line-height: 1.3;
  color: var(--study-1);
}
.rv-artifact-cta {
  align-self: flex-start;
  margin-top: 4px;
  padding: 6px 12px;
  border: 1px solid var(--study-2);
  border-radius: 999px;
  font-family: var(--font-mono);
  font-size: 0.68rem;
  letter-spacing: 0.04em;
  color: var(--study-2);
  transition: background 0.18s ease, color 0.18s ease;
}
.rv-artifact:hover .rv-artifact-cta,
.rv-artifact:focus-visible .rv-artifact-cta {
  background: var(--study-2);
  color: #fff;
}
.rv-artifact-meta {
  font-family: var(--font-mono);
  font-size: 0.68rem;
  line-height: 1.5;
  color: var(--text-muted);
}

/* ── 7. STUDY LIBRARY ──────────────────────────────────────────
   The nine studies as an evidence index: score, name, and the
   ways in to read it. Doubles as the score chart, so the bars are
   scaled 0–100 rather than to a truncated axis. The scores cluster
   between 78 and 90, so a zoomed axis would exaggerate small gaps
   and, on a page about research rigour, that's the wrong trade:
   the honest reading is "consistently high", which full scale shows.
   ────────────────────────────────────────────────────────────── */
/* Was sp-6, when this div also carried the heading and needed clearance
   from the two-col row above. It is the list alone now and sits directly
   under the headline that introduces it, so it wants the tighter gap. */
.rv-library { margin-top: var(--sp-4); }

/* The library's heading block, living in the Impact column. sp-4 off the
   paragraph above so "Read the studies" reads as a new heading rather
   than as another paragraph of the Impact prose. */
.rv-lib-head { margin-top: var(--sp-4); }

.rv-lib-title {
  margin: 0 0 6px;
  font-family: var(--font-mono);
  font-size: 1.05rem;
  font-weight: 700;
  color: var(--text-primary);
}
.rv-lib-intro {
  margin: 0 0 var(--sp-3);
  max-width: 620px;
  font-family: var(--font-sans);
  font-size: 1rem;
  line-height: 1.65;
  color: var(--text-secondary);
}

.rv-lib-list {
  list-style: none;
  margin: 0;
  padding: 0;
  border-top: 1px solid var(--border);
}

/* The links column is fixed rather than auto: rows carrying a Deck
   link are wider than rows without one, and an auto column let that
   difference ripple back into the bars, leaving the benchmark pips
   ragged down the list. Fixed width keeps every bar and pip aligned. */
.rv-lib-row {
  display: grid;
  grid-template-columns: 54px minmax(0, 1fr) 196px;
  align-items: center;
  gap: clamp(12px, 2vw, 24px);
  padding: 14px 4px;
  border-bottom: 1px solid var(--border);
}

.rv-lib-score {
  font-family: var(--font-serif);
  font-size: 1.5rem;
  font-weight: 700;
  line-height: 1;
  color: var(--study-2);
  text-align: right;
  font-variant-numeric: tabular-nums;
}
/* Top scorer earns the filled treatment, nothing else does. */
.rv-lib-row.is-top .rv-lib-score { color: var(--study-1); }

.rv-lib-name {
  margin: 0 0 7px;
  font-family: var(--font-sans);
  font-size: 0.98rem;
  font-weight: 500;
  line-height: 1.35;
  color: var(--text-primary);
}
.rv-lib-device {
  display: inline-block;
  margin-left: 8px;
  padding: 2px 7px;
  border: 1px solid var(--border);
  border-radius: 999px;
  font-family: var(--font-mono);
  font-size: 0.62rem;
  letter-spacing: .01em;
  color: var(--text-muted);
  vertical-align: 1px;
}

/* What the study produced. The library listed nine scores and nine
   links, which proves the studies happened and says nothing about what
   they changed. The improvement opportunity each one raised is the part
   that answers "so what". ⚑ THIS ONCE SAID "six of them carry a Shipped mark
   because they reached the live site". FIVE DO (90, 88, 86, 83, 78), and the
   rendered headline says "Five of the nine reached the live site." SIX IS THE
   DECK COUNT, not the shipped count, and the two are one line apart in the
   markup. Corrected 2026-09-02 — the same defect as the chip guard twenty lines
   below, which was teaching back a headline Daniel had corrected. ⚑ A COMMENT
   THAT CARRIES A NUMBER GOES STALE THE MOMENT THE PAGE'S NUMBER MOVES; prefer
   recording why a rule exists over what it currently counts. */
.rv-lib-outcome {
  margin: 7px 0 0;
  font-family: var(--font-mono);
  font-size: 0.72rem;
  line-height: 1.45;
  color: var(--text-muted);
}
/* Status reads as the tail of the outcome sentence, not as a badge.
   These were filled and outlined pills, and at nine rows the pills were
   the loudest thing in the list: a saturated lozenge repeating down the
   right of every line pulls rank on the score, which is what the list is
   actually sorted and read by. Now they inherit the outcome's size and
   family and separate on colour and weight alone.

   The colon is CSS rather than markup so there is one place to change
   the separator. It needs the space in the HTML gone, or it renders as
   "card :Shipped".

   Three states, three weights of quiet: SHIPPED is live and carries the
   accent, QUEUED is designed and handed over so it takes the accent
   without the weight, RAISED never became a ticket and stays in the
   outcome's own colour. Vocabulary matches the library headline.
   ⚑⚑ THIS ONCE ENDED "every study raised one opportunity, some were queued, some
   shipped", which TEACHES BACK THE EXACT ERROR Daniel corrected on 2026-09-02: some
   studies raised more than one, and the old headline saying otherwise had been false
   under a lock since July. The headline now reads "Every study raised at least one
   improvement opportunity, and some raised more."
   ⚑ A COMMENT THAT QUOTES A SENTENCE HAS TO BE RE-READ WHEN THAT SENTENCE IS
   CORRECTED, or it hands the next editor the old version as the rule. */
.rv-lib-shipped,
.rv-lib-queued,
.rv-lib-raised {
  font-family: inherit;
  font-size: inherit;
  letter-spacing: inherit;
  text-transform: none;
}
.rv-lib-shipped::before,
.rv-lib-queued::before,
.rv-lib-raised::before {
  content: ": ";
  color: var(--text-muted);
  font-weight: 400;
}
.rv-lib-shipped {
  color: var(--study-2);
  font-weight: 700;
}
.rv-lib-queued {
  color: var(--study-2);
  font-weight: 400;
}
.rv-lib-raised {
  color: var(--text-muted);
  font-weight: 400;
}

.rv-lib-bar {
  height: 7px;
  border-radius: 999px;
  background: color-mix(in srgb, var(--border) 60%, transparent);
  overflow: hidden;
}

.rv-lib-headline {
  margin: 0 0 var(--sp-1);
  max-width: 620px;
  font-family: var(--font-sans);
  font-size: 1rem;
  line-height: 1.6;
  color: var(--text-primary);
}
.rv-lib-headline b { color: var(--study-2); }
.rv-lib-bar i {
  display: block;
  height: 100%;
  width: calc(var(--v) * 1%);
  border-radius: 999px;
  background: var(--study-3);
}

/* Competitor mark. One row has this: the menus study, the only one WEVO
   ran as a head-to-head. The bar already clips to its own rounded ends,
   so the tick can be a child and never escape the track. */
.rv-lib-bar.is-marked { position: relative; }
.rv-lib-bar.is-marked::after {
  content: "";
  position: absolute;
  top: 0;
  bottom: 0;
  left: calc(var(--m) * 1%);
  width: 2px;
  background: var(--study-1);
}
/* Whose score, above the bar. The value itself, below it.

   Both were one string above the tick and it read as a timeline: a name
   and a number sitting over a mark is axis language, so "Bank of America
   67" scanned as a year rather than as a competitor's score. Splitting
   them across the bar makes the tick the thing that joins them, and puts
   the number on the side of the track where a value belongs.

   Neither takes a line of flow. Both are absolute, so the row stays 88px
   like the other eight. The name is positioned against .rv-lib-name and
   the value against .rv-lib-barwrap; both are blocks sharing a parent
   with the bar and therefore exactly as wide, so one --m lands the label,
   the tick and the value on a single vertical. */
.rv-lib-name { position: relative; }
.rv-lib-mark {
  position: absolute;
  left: calc(var(--m) * 1%);
  bottom: 1px;
  transform: translateX(-50%);
  font-family: var(--font-mono);
  font-size: 0.62rem;
  font-weight: 400;
  line-height: 1.2;
  color: var(--text-muted);
  white-space: nowrap;
}

.rv-lib-barwrap { position: relative; }
/* Serif, bold and tabular, the same voice as .rv-lib-score, at roughly
   half its size so it never competes with the row's own 78. Coloured to
   match the tick, so the mark and its number read as one object. */
.rv-lib-markval {
  position: absolute;
  left: calc(var(--m) * 1%);
  top: calc(100% + 4px);
  transform: translateX(-50%);
  font-family: var(--font-serif);
  font-size: 0.85rem;
  font-weight: 700;
  line-height: 1;
  font-variant-numeric: tabular-nums;
  color: var(--study-1);
  white-space: nowrap;
}
.rv-lib-row.is-top .rv-lib-bar i { background: var(--study-2); }

/* Right-aligned, and Report is the LAST child in the markup on purpose:
   the final item holds the right edge, so every Report button lands on
   the same axis whether or not the row also has a deck. */
.rv-lib-links { display: flex; justify-content: flex-end; gap: 8px; white-space: nowrap; }

.rv-lib-link {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  padding: 7px 12px;
  border: 1px solid var(--study-2);
  border-radius: 999px;
  font-family: var(--font-mono);
  font-size: 0.72rem;
  letter-spacing: 0.04em;
  color: var(--study-2);
  text-decoration: none;
  background: transparent;
  transition: background 0.18s ease, color 0.18s ease;
}
.rv-lib-link:hover,
.rv-lib-link:focus-visible {
  background: var(--study-2);
  color: #fff;
}
.rv-lib-link.is-deck {
  border-style: dashed;
}
.rv-lib-meta {
  margin: var(--sp-2) 0 0;
  font-family: var(--font-mono);
  font-size: 0.72rem;
  line-height: 1.6;
  color: var(--text-muted);
}

@media (max-width: 720px) {
  .rv-lib-row {
    grid-template-columns: 44px minmax(0, 1fr);
    row-gap: 10px;
  }
  /* The Bank of America comparison marks are position:absolute, so they
     take no flow space. That is correct at desktop, where the row is wide
     enough to absorb them. On a phone the name label landed on top of the
     DEVICE chip and the value printed inside the outcome sentence ("in the
     header,67 and a rebuild of"), so the one row that says he beat a
     national bank by 11 points rendered as a broken component. Reserve the
     two bands they occupy rather than repositioning them, so the tick, the
     label and the value stay on the single vertical the desktop comment
     describes. */
  .rv-lib-name:has(.rv-lib-mark) { padding-bottom: 14px; }
  .rv-lib-barwrap:has(.rv-lib-markval) { margin-bottom: 18px; }
  /* Links drop to their own line under the name, aligned with it,
     rather than squeezing the study name to a couple of words.
     They pack left here, so the row-reverse flips Report back to the
     outer edge and keeps it on one axis the way it is on desktop. */
  .rv-lib-links {
    grid-column: 2;
    flex-direction: row-reverse;
    justify-content: flex-end;
  }
  .rv-lib-score { font-size: 1.25rem; }
}

/* ── Impact roadmap ───────────────────────────────────────────
   Follows the Impact two-col row, which carries no trailing row
   gap of its own, so the separation is set here. Held to a
   readable measure rather than stretched to the band width.
   ────────────────────────────────────────────────────────────── */
.rv-roadmap-row { margin-top: var(--sp-5); }
/* max-width comes off: the column is the measure now. */
.rv-roadmap { max-width: none; }

/* "What we were told". Three identical rows, which is the whole point:
   the promise did not change, only its date did, so the repetition does
   the arguing and no adjective has to.

   No closing line. It used to end "It was never signed", which is the one
   thing nobody told them, so it sat outside its own title, and with the
   outcome restored to the prose it would have been the same fact twice a
   few inches apart. The ledger stops on the third promise and the
   paragraph beside it resolves what happened.

   Reuses .study-aside, the same callout that carries the cafe objection
   in Users & Audience and the CEO note in Process.

   align-self: start, not centred: the aside annotates the paragraph that
   introduces the second ask, so it should sit beside it rather than drift
   down opposite the bullets. */
.rv-roadmap-row .rv-told { align-self: start; }
.rv-told-list {
  margin: 0;
  display: flex;
  flex-direction: column;
  gap: 9px;
}
.rv-told-list > div {
  display: grid;
  grid-template-columns: minmax(0, 8.5em) minmax(0, 1fr);
  gap: 14px;
  align-items: baseline;
}
.rv-told-list dt {
  font-family: var(--font-mono);
  font-size: 0.72rem;
  font-weight: 700;
  letter-spacing: 0.02em;
  color: var(--study-2);
}
.rv-told-list dd {
  margin: 0;
  font-family: var(--font-sans);
  font-size: 0.95rem;
  line-height: 1.5;
  color: var(--text-secondary);
}

/* ── Responsive ────────────────────────────────────────────────
   The authority line is the tightest component: five columns of
   label in a phone-width cell. It gives up type size here and
   collapses to one column at 520px, in its own block above.
   ────────────────────────────────────────────────────────────── */
@media (max-width: 600px) {
  /* .rv-authority carries its own <=520px rule; the climb bars it
     replaced needed shrinking here and it does not. */
  .rv-auth-who { font-size: 0.66rem; }
  .rv-cycle-track { gap: 14px; }
  /* The arrow is sized to the gutter it crosses. Left at 22px it
     overhung the previous node by 8px once the gap tightened. */
  .rv-cycle-node + .rv-cycle-node::before {
    left: -14px;
    width: 14px;
    clip-path: polygon(
      0   40%, 7px 40%,
      7px 0,   14px 50%,
      7px 100%, 7px 60%,
      0   60%
    );
  }
  .rv-cycle-what { font-size: 0.7rem; }
  /* Four columns of two-line label plus an instance line is the
     tightest thing on the page at phone width. The instance keeps its
     size and the stage name gives way, because the instance is the
     part that is worth reading. */
  .rv-cycle-real { font-size: 0.58rem; padding-top: 5px; }

  /* Label above value: 82px of dt column leaves ~120px for the goal
     sentence, which breaks it to five words a line. */
  .rv-slip-row { grid-template-columns: minmax(0, 1fr); gap: 3px; }
  .rv-slip-row dt { line-height: 1.4; }
}

/* 1279, not 640. .study-two-col holds two columns down to 860
   (case-study.css), so from there up to ~1280 the pair was splitting an
   already-halved column into two panels 156-262px wide. The quotes
   wrapped to a fourth line and beyond, drove the pair height up, and
   the readout stretched to match: 416px of empty gold at 861px, 65% of
   that panel. Collapsing the pair EARLIER than the section collapses is
   the fix.

   1280 is measured, not chosen for roundness: it is exactly where the
   raw panel reaches 280px and the quotes settle at three lines. Empty
   gold by width, before → after: 861 416→12, 960 337→12, 1100 173→12,
   1200 190→12. From 1280 up it is a flat 117px at every width tested to
   1920, which at that size reads as composition rather than as a hole,
   so desktop is deliberately untouched. */
@media (max-width: 1279px) {
  .rv-desk-pair { grid-template-columns: 1fr; }

  /* Payoff first once stacked. Source order is raw → readout because
     that is the argument's order and the order a desktop reader takes
     it left to right. Stacked, that order buries the readout and opens
     the graphic on the material it exists to replace. order: is
     presentation only, so the DOM and the tab order still read
     raw → readout for a screen reader. */
  .rv-desk-panel.is-out { order: -1; }
}

@media (max-width: 640px) {
  /* Stacked, the wall no longer needs to hold a column's height, and
     a tall block of grey rules above the readout would push the part
     worth reading off the first screen. It keeps just enough rows to
     read as transcript. (.rv-desk-wall is the archive's path now; see
     the dead-code note above.) */
  .rv-desk-wall { min-height: 0; max-height: 108px; }
}

/* Respect reduced-motion: the gauge lands drawn, nothing sweeps. */
@media (prefers-reduced-motion: reduce) {
  .reveal.is-visible .rv-gauge-fill { animation: none; }
}

/* ══════════════════════════════════════════════════════════════
   IMPACT CLOSING BEAT  ·  .rv-unmeasured
   The "we never measured it" paragraph runs outside the two-column
   row above it, because a closing beat should not be a column and
   because inside that row it pushed the aside's deliberate 312px of
   air to 478px. Running full width is only defensible if it still
   gets a measure and a break: unstyled it set ~117 characters per
   line against the 74-82 the rest of the page holds, which read as
   the paragraph losing its column rather than as a deliberate beat.
   ══════════════════════════════════════════════════════════════ */
.rv-unmeasured{
  max-width: 68ch;
  margin-top: var(--sp-5);
  padding-top: var(--sp-3);
  border-top: 1px solid var(--border);
}
/* .study-cta is display:inline by default and gets its spacing from the
   flex gap of whatever column holds it. This one is a direct child of the
   section, so it needs both, the same way .study-step .study-cta does.
   Inline also let it fragment across a line break, which split the hit
   area in two. */
.rv-unmeasured-cta{
  display: inline-block;
  margin-top: 18px;
}


/* ── DECISION 3: THE HEADER AGAINST THE FOOTER ──────────────────
   Two crops from ONE capture of the July 2025 homepage, stacked
   rather than paired side by side. Side by side, a 1400x104 strip and
   a 1400x460 block scale to the same width and the header becomes a
   sliver; stacked, both hold their own proportions and the reader
   compares two places on one page instead of two rectangles.

   No border and no surface, per the standing rule that visual
   support sits bare. These crops are already dark panels with their
   own edges, so an outline would be a frame around a frame, the same
   call §2b makes for the Process diagrams and the quote pair. */
.rv-quicklinks { height: auto; padding: 0; gap: var(--sp-2); }

.rvq-pair {
  display: flex;
  flex-direction: column;
  gap: var(--sp-3);
}

.rvq-shot { margin: 0; }

/* The intrinsic widths differ (1400x104 against 1400x460) but both are cut
   at 1400px, so width:100% renders them at ONE scale and the link text
   measures the same in both. That is the whole reason the crops were cut
   to a shared width rather than to their content.
   ⚑ FIGURES UPDATED 2026-09-02 AFTER THE 2x RE-CUT. They said 700x52,
   700x230 and "cut at 700px", which described the ORIGINAL crops; those
   rendered at 0.61 device pixels per CSS pixel at dpr 2, against 0.97 or
   better for every other raster on the page, and were re-cut at native 2x.
   THE RULE THIS COMMENT DEFENDS IS UNCHANGED and the CSS is untouched; only
   its numbers were stale. ⚑ The defect survived three review passes because
   each verified that the width and height ATTRIBUTES MATCHED THE FILES,
   which is true of a half-resolution image: checking that a number is
   consistent is not checking that it is adequate. */
.rvq-shot img {
  display: block;
  width: 100%;
  height: auto;
}

/* 11px, the page-space label size, not the figure scale: these caption
   the figure from outside it rather than annotating anything inside it. */
.rvq-shot figcaption {
  margin: 6px 0 0;
  font-family: var(--font-mono);
  font-size: 0.6875rem;
  letter-spacing: .01em;
  color: var(--text-muted);
}
