/* ==========================================================================
   rank.css — the rank scale. sections/rank-scale.html only.

   UNLAYERED ON PURPOSE. site.css puts everything in cascade layers, so an
   unlayered rule here wins over its older single-lane .rank block without a
   single !important (site.css header, rule 2a). The elements that carry
   geometry have new class names — .rank__ladder, .rank__rung, .rank__range,
   .rank__bracket, .rank__tag, .rank__pin, .rank__guide — so none of the old
   rules can reach them at all. The parts that did not change shape keep their
   old names and their site.css styling: .rank__intro, .rank__foot,
   .rank__keys, .rank__chain, .rank__step*, .rank__name, .rank__short.

   ## What the drawing has to say, and how it says it

   The section makes the only claim on the site nobody measured. So:

     1. ONE MEASURED NUMBER, with its confidence interval, in the intro. The
        template drops the number whenever either bound of the interval is
        missing, so it can never appear bare.
     2. ONE INFERRED RANGE, drawn as a wash across WHOLE TIERS plus a bracket
        over them. Never a pin: a pin on a scale reads as a measurement.
     3. ONE REFERENCE POINT, Nexto, on a dashed guide that runs the full height
        of the drawing. Left of the line and right of the line is then a single
        glance, not a memory test.

   ## Three lanes, so nothing can share a pixel

       lane --est     the bracket and the name over it
       __ladder       the tiers, with the range washed behind them
       lane --ref     the reference caret and its name

   The estimate and the reference cannot land on the same pixel even when they
   name the same tier: one is a range on the row above, the other is a point on
   the row below. That was the bug this file exists to end.

   ## One coordinate system

   Every lane is a block of the same width, so a percentage means the same x in
   all three. The ladder is an 8-up grid with no gap and no padding, so a tier
   edge sits at exactly k/N of the track and the range — drawn from the left
   edge of one tier to the right edge of another — is quantised to the tiers by
   construction. Nothing here counts tiers in CSS; retiering the axis in JSON
   cannot silently move the bot.

   Marks are centred on their position with `translate`, not `transform`, so
   the reveal below can use `transform` without fighting them.

   Colour is never the only signal: the range is written out above the ladder
   and again under it, the drawing carries one aria-label saying the same, and
   every tier keeps its name.
   ========================================================================== */

.rank {
  display: flex;
  flex-direction: column;
  gap: var(--space-5);

  /* the ladder itself. Tall enough to hold a tier name inside a rung, which is
     what keeps the names attached to their cells at every width. */
  --rank-h: clamp(3rem, 4.6vw, 4rem);
  /* the two label lanes. Each is a micro line plus the mark that points at the
     ladder, reserved as height so nothing can collide with anything. */
  --lane-est: 2.5rem;
  --lane-ref: 2.75rem;
}

/* --- the claim, in words, before the drawing ----------------------------- *
 * Left-aligned in the flow rather than floating over the band: at 390px a
 * centred label this long ran off the right of the viewport, which is how the
 * old drawing came to look like it was marking three tiers at once. Here it
 * wraps like any other line of text and cannot overflow anything. The words
 * come from the same two tier names the drawing is built from, so the sentence
 * and the picture cannot drift apart.                                       */
.rank__claim {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: var(--space-2) var(--space-4);
  margin: 0;
}
.rank__claim-mark {
  align-self: center;
  flex: none;
  width: var(--space-6);
  height: var(--space-2);
  border-radius: var(--r-xs);
  background: var(--bot-wash);
  border: var(--stroke-bold) solid var(--bot-mark);
}
.rank__claim-name {
  font-family: var(--font-mono);
  font-size: var(--t-micro);
  letter-spacing: var(--tr-caps);
  text-transform: uppercase;
  color: var(--text-dim);
}
.rank__claim-range {
  font-family: var(--font-display);
  font-size: var(--t-head);
  font-weight: var(--wt-semi);
  letter-spacing: var(--tr-snug);
  line-height: var(--lh-snug);
  color: var(--bot-text);
}

/* --- the drawing --------------------------------------------------------- */
.rank__scale {
  position: relative;
  display: flex;
  flex-direction: column;
  gap: 0;
}

/* the reference guide, full height. Dashed and neutral: Nexto is the yardstick
   and a consensus, not a side and not a measurement. */
.rank__guide {
  position: absolute;
  left: var(--at, 50%);
  translate: -50%;
  top: 0;
  bottom: 0;
  width: var(--stroke);
  border-left: var(--stroke) dashed var(--line-strong);
  pointer-events: none;
}
.rank__guide[data-accent="bot"]   { border-left-color: var(--bot-mark); }
.rank__guide[data-accent="human"] { border-left-color: var(--human-mark); }

.rank__lane { position: relative; }
.rank__lane--est { height: var(--lane-est); }
.rank__lane--ref { height: var(--lane-ref); }

/* the bracket over the range: ends turned down onto the two tier edges it
   spans, so the width of the claim is drawn, not implied. Flat, with no
   shading anywhere across it — a bell shape would claim a distribution nothing
   here measured. */
.rank__bracket {
  position: absolute;
  left: var(--low, 0%);
  width: calc(var(--high, 100%) - var(--low, 0%));
  bottom: 0;
  height: var(--space-2);
  border: var(--stroke-bold) solid var(--bot-mark);
  border-bottom: 0;
  border-radius: var(--r-xs) var(--r-xs) 0 0;
  pointer-events: none;
}

/* the estimate's name, centred over the range it labels */
.rank__tag {
  position: absolute;
  left: var(--at, 50%);
  translate: -50%;
  top: 0;
  font-family: var(--font-mono);
  font-size: var(--t-micro);
  font-weight: var(--wt-medium);
  letter-spacing: var(--tr-caps);
  text-transform: uppercase;
  line-height: var(--lh-flat);
  white-space: nowrap;
  color: var(--bot-text);
  pointer-events: none;
}

/* --- the ladder ---------------------------------------------------------- */
.rank__ladder {
  position: relative;
  display: grid;
  grid-template-columns: repeat(var(--tiers, 8), minmax(0, 1fr));
  height: var(--rank-h);
  background: var(--ground-panel);
  border-radius: var(--r-sm);
  /* clips the range wash to the ladder's corners, so the range needs no
     radius of its own and still cannot bleed past the last tier */
  overflow: hidden;
}

/* THE RANGE: a wash behind the rungs, from one tier edge to another. Quantised
   to the tiers because both ends ARE tier edges — this is what makes it
   impossible for the range to sit half over a rung at any width. */
.rank__range {
  position: absolute;
  left: var(--low, 0%);
  width: calc(var(--high, 100%) - var(--low, 0%));
  top: 0;
  bottom: 0;
  z-index: 0;
  background: color-mix(in oklab, var(--bot-mark) 20%, var(--ground-panel));
  border-left: var(--stroke-bold) solid var(--bot-mark);
  pointer-events: none;
}

.rank__rung {
  position: relative;
  z-index: 1;
  display: grid;
  place-items: center;
  min-width: 0;
  padding-inline: var(--space-1);
  border-left: var(--stroke) solid var(--line);
}
.rank__rung:first-child { border-left: 0; }
/* the tiers the estimate covers: their names come up to full body contrast, so
   the range is legible without relying on the wash alone */
.rank__rung[data-in-range] .rank__name,
.rank__rung[data-in-range] .rank__short { color: var(--text); }

.rank__name,
.rank__short {
  font-family: var(--font-mono);
  font-size: var(--t-micro);
  letter-spacing: var(--tr-wide);
  line-height: var(--lh-flat);
  color: var(--text-dim);
  text-align: center;
  text-wrap: balance;
}

/* a point estimate, only ever drawn when story.json gives one. Kept to a
   hairline: where a guess centres, not where the bot is. */
.rank__tick {
  position: absolute;
  left: var(--at, 50%);
  translate: -50%;
  top: 0;
  bottom: 0;
  width: var(--stroke-bold);
  z-index: 2;
  background: var(--bot-mark);
  pointer-events: none;
}

/* --- the reference point ------------------------------------------------- *
 * A caret touching the rung it names, plus a short stem. The caret is what
 * makes the placement unambiguous at 390px, where a rung is about 44px wide
 * and any label is wider than that.                                          */
.rank__pin {
  position: absolute;
  left: var(--at, 50%);
  translate: -50%;
  top: 0;
  width: var(--stroke);
  height: var(--space-4);
  background: var(--line-strong);
  pointer-events: none;
}
.rank__pin::before {
  content: "";
  position: absolute;
  top: 0;
  left: 50%;
  translate: -50%;
  width: var(--space-3);
  height: var(--space-2);
  background: var(--line-strong);
  clip-path: polygon(50% 0, 100% 100%, 0 100%);
}
.rank__pin[data-accent="bot"],
.rank__pin[data-accent="bot"]::before   { background: var(--bot-mark); }
.rank__pin[data-accent="human"],
.rank__pin[data-accent="human"]::before { background: var(--human-mark); }

.rank__pin-name {
  position: absolute;
  top: calc(var(--space-4) + var(--space-1));
  left: 50%;
  translate: -50%;
  font-family: var(--font-mono);
  font-size: var(--t-micro);
  letter-spacing: var(--tr-caps);
  text-transform: uppercase;
  line-height: var(--lh-flat);
  color: var(--text-dim);
  white-space: nowrap;
}

/* --- the intro: head beside the reasoning -------------------------------- *
 * The right column carries the measured number and then the three steps of
 * the argument, so a tall heading on the left cannot leave several hundred
 * pixels of nothing beside it. That hole was the complaint.                  */
.rank__intro {
  display: grid;
  gap: var(--space-6);
  margin-bottom: var(--space-6);
}
@media (min-width: 62rem) {
  .rank__intro { grid-template-columns: 0.85fr 1.15fr; align-items: start; }
}
/* the grid gap owns the spacing here; site.css's head margin would double it */
.rank__intro .section__head { margin-bottom: 0; }
.rank__lede,
.rank__reason {
  display: flex;
  flex-direction: column;
  gap: var(--space-5);
  max-width: var(--w-prose);
}

/* the one measured number the estimate rests on, with its interval beside it */
.rank__measured {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: var(--space-2) var(--space-3);
  margin: 0;
}
.rank__measured .num { font-size: var(--t-lead); }
/* reserve the width so a live update cannot reflow the line beside it */
.rank__measured .num--slot { --slot: 6; }
.rank__measured-label { font-size: var(--t-small); color: var(--text-dim); }
.rank__measured-note { margin: calc(-1 * var(--space-4)) 0 0; }

.rank__basis {
  max-width: var(--w-prose);
  font-size: var(--t-small);
  line-height: var(--lh-loose);
  color: var(--text-dim);
}

/* the key's swatch lines up with the FIRST line of its caption, not with the
   middle of a three-line one — site.css centres it, which reads as a misprint
   once a caption wraps */
.rank__key-mark { align-self: start; margin-top: 0.5em; }
/* a key with no swatch still lines up with the ones that have one */
.rank__key[data-accent="none"] { padding-left: calc(var(--space-5) + var(--space-3)); }

/* the chain: the step number sits on the title's line rather than on one of
   its own. Reads as a numbered list instead of three stacked blocks, and gives
   back a line per step at 390px, where this column is the tallest thing in the
   section. */
.rank__step {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr);
  align-items: baseline;
  column-gap: var(--space-3);
  row-gap: var(--space-2);
}
.rank__step-n     { grid-column: 1; }
.rank__step-title,
.rank__step-body  { grid-column: 2; }

/* --- narrow -------------------------------------------------------------- *
 * Eight full tier names cannot be read in 44px rungs, so site.css's own
 * breakpoint swaps them for the short codes. Nothing scrolls sideways at any
 * width; every mark is a percentage of a track that is itself a percentage of
 * the wrap.
 * ponytail: a marker label wider than about 2.5 rungs would reach past the
 * gutter at 390px. Nexto's is 5 characters and clears it by 30px, and the key
 * under the ladder repeats every label in words, so nothing is lost if a
 * future one is clipped. Per-rung label anchoring is the fix if that happens. */
@media (max-width: 56rem) {
  .rank { --lane-ref: 2.5rem; }
  .rank__claim-range { font-size: var(--t-lead); }
}

/* --- THE REVEAL ---------------------------------------------------------- *
 * story.js puts data-shown on a section once it crosses the line. The range
 * wipes in from its low end — the direction the ladder is read — and the
 * caret, the name and the guide arrive after it, so the eye gets the range
 * first and the reference second.
 *
 * THE WHOLE HIDDEN STATE LIVES INSIDE no-preference, which is load-bearing
 * rather than tidy: under reduce, story.js never sets data-shown, so anything
 * hidden outside this block would stay hidden forever. The
 * html:not([data-story]) arm covers story.js not running at all.             */
@media (prefers-reduced-motion: no-preference) {
  .rank__range,
  .rank__bracket {
    transform: scaleX(0);
    transform-origin: left center;
    transition: transform var(--dur-line) var(--ease-out) 100ms;
  }
  .rank__tag,
  .rank__tick,
  .rank__guide,
  .rank__pin {
    opacity: 0;
    transition: opacity var(--dur-slow) var(--ease-out) 700ms;
  }

  .section[data-shown] .rank__range,
  .section[data-shown] .rank__bracket,
  html:not([data-story]) .rank__range,
  html:not([data-story]) .rank__bracket { transform: scaleX(1); }
  .section[data-shown] .rank__tag,
  .section[data-shown] .rank__tick,
  .section[data-shown] .rank__guide,
  .section[data-shown] .rank__pin,
  html:not([data-story]) .rank__tag,
  html:not([data-story]) .rank__tick,
  html:not([data-story]) .rank__guide,
  html:not([data-story]) .rank__pin { opacity: 1; }
}
