/* ===========================================================================
   The assessment -- the screens on the Arc, and one spine.

   Redesign, 2026-09-22 (docs/team/DESIGN.md, PLAN.md T4/T5). What changed:
   the twelve-set colour journey is the design of the test. Each question is
   read on its set's room (24% of the set colour over its field), the chapter
   card is the one colour event -- full-bleed set colour with the set numeral
   as the visual -- and the Spine reads as twelve coloured beads. Buttons and
   links are the ink of the surface they sit on: plum on light, porcelain on
   plum and on the arc. No shadows; depth is a ground change or a 2px edge. No
   grain, no drift, no entrances: the test is an Operate surface and nothing on
   it moves except what confirms an action.

   THREE LAWS THIS FILE IS WRITTEN UNDER. Breaking any of them is a defect.

   1. GREYSCALE FIRST. Every screen kind is tellable apart with colour removed:
        scale     dark edge to edge, light type straight on it (no
                  page), and the answer as light objects: five equal bars
                  (cards), one light rail with five discs down it and a
                  line of words beside each (dial), or a row of three (or
                  two) light blocks over a short list (steps); the footer
                  follows on the dark
        set_card  dark edge to edge, a numeral and a motif, one button
        story     dark ground with a paper page on it       (in-flight only)
        beat      dark, centred, no control                 (in-flight only)
        ask       the scale's frame, ragged light pills
        rank      the scale's frame with five light rows (two-tap), or
                  paper edge to edge with a numbered stack (in-flight
                  ordering)
        guess/words        paper edge to edge, no card

   2. NO RAMP ACROSS THE FIVE, PER GEOMETRY. The five categories are identical
      to each other on every screen -- the five rows, the five dial stops, the
      three leads, the two words and the five legend words of the steps.
      There is not one
      :nth-child(), sibling combinator, [data-value], :last-child or :has()
      under any .scale__opts--* in this file, and tests/test_palette.py runs
      the real cascade per geometry to prove they compute identically in
      every state.

   3. COLOUR SAYS WHERE, NEVER HOW WELL. --arc-now says which set you are in.
      It is identical for every person, so it cannot be read as a result. It is
      never applied to a score, a band or a level.

   Tokens are defined once in styles.css; this file only consumes them.
   --arc-now and --field-now are set from JS via CSSOM. Everything that varies
   by set varies through the --sc-* aliases below -- there are no per-set rules.
   No inline styles anywhere. No CDN. No third-party anything.
   =========================================================================== */

/* `hidden` is only display:none in the UA sheet; any author `display` beats
   it. Asserted once here. */
[hidden] {
  display: none !important;
}

/* ---------------------------------------------------------------------------
   1 · FOUNDATIONS -- the shell, the aliases, the frame
   --------------------------------------------------------------------------- */

.assess-body {
  /* The live set, read through one name each. Declared on both .assess-body
     and .screen so whichever element the client hangs --arc-now on resolves
     it and the other inherits. */
  --sc-arc: var(--arc-now, var(--arc-01, #1957d2));
  --sc-field: var(--field-now, var(--field-01, #cddfff));

  /* THE ROOM: the ground a measured item is read on. 24% of the set's own
     colour over its field. 24 is the last stop before something breaks:
     at 30% --sc-arc on its own room falls under 3:1 on sets 9 and 10 and the
     chosen row's edge stops being a marker. tests/test_palette.py reads the
     24, the 86 and the 66 out of this file and recomputes every floor. */
  --sc-room: color-mix(in srgb, var(--sc-arc) 24%, var(--sc-field));
  /* Two inks derived from the room so they follow it: the quiet line
     (6.37:1 at worst) and the control edge (3.95:1 at worst on the room,
     7.69:1 against a paper row). */
  --sc-quiet: color-mix(in srgb, var(--night-plum) 86%, var(--sc-room));
  --sc-edge: color-mix(in srgb, var(--night-plum) 66%, var(--sc-room));

  /* Local names for the light grounds: a hairline and a control edge that
     both clear the non-text floor where they are used. */
  --sc-rule: color-mix(in srgb, var(--night-plum) 16%, var(--porcelain));
  --sc-edge-light: color-mix(in srgb, var(--night-plum) 50%, var(--paper));
  --sc-focus-ink: var(--night-plum);
  --bead-track: var(--sc-edge);

  /* The set numeral on the chapter card is counted off the Spine: every
     walked bead adds one, so the number on the card is the number the beads
     already show. No JS, no per-set rule. */
  counter-reset: knoi-set;

  min-height: 100vh;
  min-height: 100svh;
  display: flex;
  flex-direction: column;
  background: var(--porcelain);
}

/* The frame: wordmark, Spine, Pause on one row at every width; "Set 4 of 12"
   sits under the beads. The middle track is the Spine, capped at 26rem so it
   stays a progress object on a wide screen and takes what is left on a phone
   (about 180px at 375 -- twelve beads of 11px). */
.assess-header {
  display: grid;
  grid-template-columns: 1fr minmax(0, 26rem) 1fr;
  grid-template-areas:
    "mark spine pause"
    "mark label pause";
  align-items: center;
  column-gap: 12px;
  row-gap: 4px;
  min-height: 56px;
  padding: 6px 24px;
  border-bottom: 1px solid var(--sc-rule);
  background: var(--porcelain);
  color: var(--night-plum);
  position: sticky;
  top: 0;
  z-index: 10;
  transition: background-color var(--t-room, 320ms cubic-bezier(.22, 1, .36, 1)),
              color var(--t-room, 320ms cubic-bezier(.22, 1, .36, 1)),
              border-color var(--t-room, 320ms cubic-bezier(.22, 1, .36, 1));
}

.assess-header .wordmark {
  grid-area: mark;
  justify-self: start;
  font-size: 1.5rem;
  color: currentColor;
}

/* Pause. Esc reaches it too. A word, not a glyph. */
.assess-header [data-pause],
.pause-button {
  grid-area: pause;
  justify-self: end;
  min-width: 44px;
  min-height: 44px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: 0 4px;
  border: 0;
  background: none;
  color: inherit;
  font-family: var(--body);
  font-size: var(--fs-xs, 0.9375rem);
  font-weight: 700;
  line-height: 1;
  text-decoration: underline;
  text-decoration-thickness: 2px;
  text-underline-offset: 0.15em;
  cursor: pointer;
}

/* The stage is a bare full-bleed column. Each screen owns its own ground. */
.assess-stage {
  flex: 1;
  display: flex;
  flex-direction: column;
  min-height: 0;
  overflow-y: auto;
}

/* The three panels that are not in the queue share the test column. */
.panel {
  width: 100%;
  max-width: 46rem;
  margin: 0 auto;
  padding: 32px 24px 64px;
}

/* Selection on the dark grounds inverts with everything else. */
.screen--setcard ::selection,
.screen--story ::selection,
.screen--beat ::selection,
.done ::selection {
  background: var(--porcelain);
  color: var(--night-plum);
}

/* Scene changes cross-fade, never cut: nothing flashes more than once per
   400ms and there is never a hard cut between two saturated grounds. */
@keyframes screen-in {
  from { opacity: 0; }
  to   { opacity: 1; }
}

@keyframes ground-in {
  from { opacity: 0; }
  to   { opacity: 1; }
}

/* The consequence beat's three staged animations (section 7, in-flight
   sessions only). */
@keyframes beat-rise {
  from { opacity: 0; transform: translateY(20px); }
  to   { opacity: 1; transform: none; }
}

@keyframes beat-draw {
  from { transform: scaleX(0); }
  to   { transform: scaleX(1); }
}

@keyframes beat-fade {
  from { opacity: 0; }
  to   { opacity: 1; }
}

/* ---------------------------------------------------------------------------
   1b · THE INTRO'S ONE DESTRUCTIVE CONTROL

   The loud control is the safe one. With a saved session in this browser,
   site/assess.mjs makes Continue the button and moves it above Start; Start
   becomes the quiet "Start over instead" link and opens a confirmation that
   says what is lost. The markup cannot be right in both states, so the
   classes are swapped at boot and styled here.
   --------------------------------------------------------------------------- */

.intro .intro__continue {
  display: inline-flex;
  align-items: center;
  justify-content: center;
}

/* Demoted, and it has to LOOK demoted. */
.intro .intro__restart {
  min-height: 44px;
  display: inline-flex;
  align-items: center;
  padding: 0;
  border: 0;
  background: none;
  color: var(--muted);
  font-family: var(--body);
  font-size: var(--fs-xs, 0.9375rem);
  font-weight: 700;
  text-decoration: underline;
  text-decoration-thickness: 2px;
  text-underline-offset: 0.15em;
  cursor: pointer;
}

.intro .intro__restart:hover {
  color: var(--night-plum);
}

.intro__confirm {
  display: grid;
  gap: 12px;
  padding: 24px;
  border: 2px solid var(--night-plum);
  border-radius: var(--r-card, 16px);
  background: var(--paper);
}

.intro__confirm-head {
  margin: 0;
  font-family: var(--display);
  font-optical-sizing: auto;
  font-size: var(--fs-h3, 1.5rem);
  font-weight: 700;
  line-height: 1.2;
  letter-spacing: -0.01em;
  color: var(--night-plum);
}

.intro__confirm-body {
  margin: 0;
  font-size: var(--fs-body, 1.0625rem);
  line-height: 1.55;
  color: var(--night-plum);
}

.intro__confirm-actions {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 16px 24px;
  margin-top: 4px;
}

/* ---------------------------------------------------------------------------
   1c · THE AGE ASK, AND THE REFUSAL UNDER THE FLOOR

   Built by site/assess.mjs and inserted before Start. It is the one field
   between a person and the product, so it is the destination of the intro:
   a card on the first field of the Arc -- the ground the journey starts on
   -- with the reason and the minimum before the field, and Start directly
   under it. The refusal takes the same card in the ordinary ink: it is not
   an error state, and nothing on it invites a different number.
   --------------------------------------------------------------------------- */

.intro__age {
  display: grid;
  gap: 8px;
  padding: 24px;
  border-radius: var(--r-card, 16px);
  background: var(--field-01, #cddfff);
  color: var(--night-plum);
}

.intro__age-eyebrow {
  margin: 0;
}

.intro__age-head {
  margin: 0;
  font-family: var(--display);
  font-optical-sizing: auto;
  font-size: var(--fs-h3, 1.5rem);
  font-weight: 700;
  line-height: 1.2;
  letter-spacing: -0.01em;
  color: var(--night-plum);
}

.intro__age-why {
  margin: 0;
  font-size: var(--fs-body, 1.0625rem);
  line-height: 1.5;
  color: var(--night-plum);
}

/* "You need to be 16 or older." The one sentence here that can end a visit:
   bold, not coloured. A red line above an empty field reads as an error the
   person has already made. */
.intro__age-floor {
  margin: 0;
  font-size: var(--fs-body, 1.0625rem);
  font-weight: 700;
  line-height: 1.5;
  color: var(--night-plum);
}

/* Label and field on one line; the field is small because the answer is. */
.intro__age-row {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 12px 16px;
  margin: 8px 0 0;
}

.intro__age-label {
  font-size: var(--fs-body, 1.0625rem);
  font-weight: 700;
  color: var(--night-plum);
}

.intro__age-field {
  width: 5.5rem;
  min-height: 56px;
  padding: 8px 12px;
  border: 2px solid var(--night-plum);
  border-radius: var(--r-control, 8px);
  background: var(--paper);
  color: var(--night-plum);
  font: inherit;
  font-size: 1.25rem;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
  text-align: center;
  caret-color: var(--night-plum);
}

/* Error: the message under the field, and the edge follows it. */
.intro__age:has(.intro__age-message.is-error) .intro__age-field {
  border-color: var(--alert-600, #a81f06);
}

.intro__age-note {
  margin: 0;
  font-size: var(--fs-xs, 0.9375rem);
  line-height: 1.5;
}

.intro__age .intro__age-message {
  margin: 0;
  min-height: 0;
}

/* -- the refusal --------------------------------------------------------- */

.intro__refusal {
  display: grid;
  justify-items: start;
  gap: 12px;
  padding: 24px;
  border-radius: var(--r-card, 16px);
  background: var(--field-01, #cddfff);
  color: var(--night-plum);
}

.intro__refusal-head {
  margin: 0;
  font-family: var(--display);
  font-optical-sizing: auto;
  font-size: clamp(1.5rem, 3.5vw, 1.75rem);
  font-weight: 700;
  line-height: 1.2;
  letter-spacing: -0.01em;
  color: var(--night-plum);
}

.intro__refusal-body {
  margin: 0;
  max-width: 34rem;
  font-size: var(--fs-body, 1.0625rem);
  line-height: 1.55;
  color: var(--night-plum);
}

/* The way out: a real link at a real size, the only control left, so the
   filled one. */
.intro__refusal-home {
  min-height: 56px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  margin-top: 4px;
  padding: 12px 24px;
  border: 2px solid var(--night-plum);
  border-radius: var(--r-control, 8px);
  background: var(--night-plum);
  color: var(--porcelain);
  font-family: var(--body);
  font-size: var(--fs-body, 1.0625rem);
  font-weight: 700;
  line-height: 1.2;
  text-decoration: none;
  transition: background-color var(--t-state, 150ms cubic-bezier(.2, 0, 0, 1)),
              border-color var(--t-state, 150ms cubic-bezier(.2, 0, 0, 1));
}

.intro__refusal-home:hover {
  background: var(--plum-lift, #3a2a45);
  border-color: var(--plum-lift, #3a2a45);
}

/* ---------------------------------------------------------------------------
   2 · THE SPINE -- twelve beads

   One bead per set; the current one fills left to right and stands taller,
   so "where am I" survives greyscale and a colour-vision difference (fill,
   height and the "Set 4 of 12" line are three channels for one fact). It can
   never lengthen: the form is fixed length.

   MARKUP REQUIREMENTS CSS cannot enforce: .spine__bead elements are the ONLY
   children of .spine (the twelve colours bind by :nth-child); the current
   bead's fill is --bead-fill, set per bead from JS; role="progressbar" and
   aria-valuetext live on .spine itself.
   --------------------------------------------------------------------------- */

.spine {
  grid-area: spine;
  display: flex;
  align-items: center;
  gap: 4px;
  width: 100%;
  min-width: 0;
  height: 12px;
  border-radius: var(--r-pill, 999px);
}

.spine__bead {
  flex: 1 1 0;
  min-width: 0;
  height: 8px;
  border-radius: var(--r-pill, 999px);
  background: var(--bead-track);
  /* Only the colour transitions; the current bead's extra height snaps
     (height is never animated). */
  transition: background-color var(--t-state, 150ms cubic-bezier(.2, 0, 0, 1));
}

/* A completed bead is solid in ITS OWN set colour: the strip is a record of
   the walk. */
.spine__bead--done {
  background: var(--bead-done, var(--bead-arc, var(--sc-arc, #1957d2)));
}

/* The current bead: taller, filled left to right over the track. */
.spine__bead--now {
  height: 12px;
  background:
    linear-gradient(
      to right,
      var(--bead-done, var(--bead-arc, var(--sc-arc, #1957d2))) var(--bead-fill, 0%),
      var(--bead-track) 0
    );
}

/* Every walked bead counts one toward the chapter card's numeral. */
.spine__bead--done,
.spine__bead--now {
  counter-increment: knoi-set;
}

/* The twelve set colours, bound by position. The one place in the product
   an --arc-* token is written out by index, and it is legitimate: bead 7 IS
   set 7, always, for everyone. */
.spine__bead:nth-child(1)  { --bead-arc: var(--arc-01, #1957d2); }
.spine__bead:nth-child(2)  { --bead-arc: var(--arc-02, #4b4cd1); }
.spine__bead:nth-child(3)  { --bead-arc: var(--arc-03, #6741ca); }
.spine__bead:nth-child(4)  { --bead-arc: var(--arc-04, #7c37be); }
.spine__bead:nth-child(5)  { --bead-arc: var(--arc-05, #8d2ead); }
.spine__bead:nth-child(6)  { --bead-arc: var(--arc-06, #9c2499); }
.spine__bead:nth-child(7)  { --bead-arc: var(--arc-07, #a81982); }
.spine__bead:nth-child(8)  { --bead-arc: var(--arc-08, #b10b69); }
.spine__bead:nth-child(9)  { --bead-arc: var(--arc-09, #b80049); }
.spine__bead:nth-child(10) { --bead-arc: var(--arc-10, #bb0a10); }
.spine__bead:nth-child(11) { --bead-arc: var(--arc-11, #a14301); }
.spine__bead:nth-child(12) { --bead-arc: var(--arc-12, #8a5404); }

/* "Set 4 of 12". Never a percentage, never a remaining count. Shown only
   while the Spine is: the intro and the finish have no set to name. */
.spine[hidden] + .spine__label {
  display: none;
}

.spine__label,
.assess-header [data-spine-label] {
  grid-area: label;
  justify-self: center;
  margin: 0;
  font-family: var(--body);
  font-size: var(--fs-xs, 0.9375rem);
  font-weight: 700;
  font-variant-numeric: tabular-nums;
  line-height: 1;
  color: var(--muted);
  white-space: nowrap;
}

/* ---------------------------------------------------------------------------
   3 · THE SCREEN FRAME -- shared by every kind
   --------------------------------------------------------------------------- */

.screen {
  --sc-arc: var(--arc-now, var(--arc-01, #1957d2));
  --sc-field: var(--field-now, var(--field-01, #cddfff));
  --sc-room: color-mix(in srgb, var(--sc-arc) 24%, var(--sc-field));
  --sc-quiet: color-mix(in srgb, var(--night-plum) 86%, var(--sc-room));
  --sc-edge: color-mix(in srgb, var(--night-plum) 66%, var(--sc-room));
  --sc-focus-ground: var(--paper);
  --sc-focus-ink: var(--night-plum);
  /* The shared focus rule in styles.css reads --ring: the ink of this
     surface, 3px, 3px out. */
  --ring: var(--sc-focus-ink);
  --sc-measure: 46rem;

  position: relative;
  flex: 1 0 auto;
  width: 100%;
  padding: 24px 24px 32px;
  color: var(--night-plum);
  /* The advance is a 200ms crossfade, opacity only. */
  animation: screen-in var(--t-fade, 200ms cubic-bezier(.2, 0, 0, 1)) both;
  /* The chapter card's colour is the wash of the room that follows it. */
  transition: background-color var(--t-room, 320ms cubic-bezier(.22, 1, .36, 1));

  /* One centred column for every kind. Grid, so a left-aligned control on a
     centred column means the column's left edge. Phones read from the top so
     the five rows sit in the same place on every screen; from 768px the column
     is vertically centred. */
  display: grid;
  grid-template-columns: min(var(--sc-measure), 100%);
  justify-content: center;
  align-content: start;
  gap: 12px;
}

.screen > * {
  min-width: 0;
}

@media (min-width: 768px) {
  .screen {
    padding: 48px clamp(24px, 5vw, 48px);
    align-content: center;
    align-content: safe center;
  }
}

/* The item text is set in the reading face: Bricolage is a display face doing
   a reading job 100 times in a row, and Atkinson was drawn for low vision.
   The reserve holds three lines so the options never jump between a one-line
   item and a three-line one. */
.scale__stem,
.screen--scale h1,
.screen--scale h2 {
  margin: 0;
  font-family: var(--body);
  font-size: var(--fs-item, 1.25rem);
  font-weight: 400;
  line-height: 1.35;
  letter-spacing: 0;
  text-wrap: balance;
  min-height: 3em;
}

@media (min-width: 768px) {
  .scale__stem,
  .screen--scale h1,
  .screen--scale h2 {
    font-size: 1.375rem;
  }
}

/* "I..." and the eyebrow: the quiet ink derived from the room (6.37:1 at
   worst); --muted is 3.02:1 there and is never used on a room element. */
.scale__lead,
.screen .eyebrow {
  margin: 0;
  font-family: var(--body);
  font-size: var(--fs-body, 1.0625rem);
  font-weight: 400;
  letter-spacing: 0;
  text-transform: none;
  color: var(--sc-quiet);
}

/* ---------------------------------------------------------------------------
   4 · SCALE -- the question card. Runs 100 times.

   THE CARD IS THE PREVIEW HE TAPPED (D-063, 2026-10-01;
   docs/team/preview/questions.html). The set's ink edge to edge, flat, the
   same ground the chapter card arrives on, and NO PAGE on it: "I…", the
   stem and the gloss are porcelain type straight on the ink, the set's motif
   in its own pale field tint beside "I…", and the answers are WHITE objects
   with plum words on them. The paper page of PLAN-assess 5.2 read as a form
   ("A card, not a form" -- the brief, item 4) and is gone; so is the 24%
   room as a fill. The five categories are given in one of three geometries,
   fixed per set and identical for everyone in it: cards (five white rows),
   a dial (a white rail of five stops with the item's words in porcelain
   beside them, as the preview's B draws it), steps (three white leads, then
   two white words, over a porcelain legend of all five).

   The chosen state is the preview's: the set colour's edge and an inset
   ring on the white object, the keycap filled with the set colour, a tick.
   On the dial the chosen stop's words sit in a white pill beside the plum
   thumb; in the steps' legend the recorded word is the white pill.

   The footer (Back, the depends pill, Skip) follows the control in the
   flow, porcelain on the ink, so the hand travels the shortest way to it.
   The ask card and the two-tap placement sit on the same frame (sections
   6c and 8).

   In greyscale: dark edge to edge, light type on it, and for cards five
   equal light bars; for the dial one light rail with five discs down it and
   a line of words beside each; for steps a row of three (or two) light
   blocks over a short list. The chapter card is the same dark ground with a
   numeral and one button, so the two never read alike.

   NO RAMP ACROSS THE FIVE, per geometry. Under any geometry's group there
   is no positional pseudo-class, no sibling combinator, no data-value
   attribute and no has(): classes and descendant combinators only.
   tests/test_palette.py runs the cascade over each geometry's DOM.
   --------------------------------------------------------------------------- */

/* The frame: the set's ink, flat, edge to edge -- the chapter card's own
   ground, so the colour that arrives on the chapter card simply stays. No
   vignette (the preview has none, and a darkened edge under a flat header
   drew a seam), no page, no band. Porcelain is the ink of everything that
   sits straight on the ground: 5.83:1 on the darkest-reading stop (arc-12)
   up to 6.26:1, so every line on the ink clears 4.5:1 at body size, not
   only the large stem (tests/test_redesign.py PAIRS). Written with
   `.screen--scale` last so the sheet's own pin -- `.screen--scale {`
   followed by `background-color: var(--sc-arc)` -- reads off this block. */
.screen--ask,
.screen--picks,
.screen--scale {
  /* The ring is porcelain everywhere on the card: a ring sits 3px OUTSIDE
     the thing it marks, so even round a white answer it is drawn on the
     ink, where plum would measure about 2.7:1. */
  --sc-focus-ground: var(--sc-arc);
  --sc-focus-ink: var(--porcelain);
  gap: 8px;
  color: var(--porcelain);
  background-color: var(--sc-arc);
  transition: background-color var(--t-room, 320ms cubic-bezier(.22, 1, .36, 1));
}

/* FROM 768PX THE FRAMED SCREENS HANG FROM THE TOP (design review, assess
   finish round 1, DF1-1). Centred, any tap that made the card taller --
   "Not me" opening the second row, "Something else…" opening its field --
   re-centred the whole column: at 1280x800 the motif, stem and control
   jumped 26px, and the chip just clicked slid 30px out from under the
   pointer. Hung from a fixed top, a tap grows the card downward and nothing
   above it moves. The chapter card keeps its centre: nothing on it grows.
   Short viewports (12b, below) still give the top padding up first. */
@media (min-width: 768px) {
  .screen--ask,
  .screen--picks,
  .screen--scale {
    align-content: start;
    padding-top: clamp(48px, 8vh, 96px);
  }
}

/* Text selected on the ink inverts the way it does on the chapter card; on
   a white answer it goes back to plum on paper's opposite. */
.screen--ask ::selection,
.screen--picks ::selection,
.screen--scale ::selection {
  background: var(--porcelain);
  color: var(--night-plum);
}

.screen--ask .ask__chip ::selection,
.screen--ask .ask__other::selection,
.screen--picks .ordering__item ::selection,
.screen--scale .scale__opt ::selection,
.screen--scale .steps__opt ::selection {
  background: var(--night-plum);
  color: var(--porcelain);
}

/* The top row: "I…" at the left, the set's motif at the right, both in the
   first grid row so the stem sits at the same height on every card. The
   motif is drawn in the set's pale field tint on its ink (4.64:1 at worst,
   a graphic, floor 3:1) -- the same drawing the chapter card carried in
   porcelain, identical for everyone in the set, so it is never a result. */
.scale__motif {
  grid-row: 1;
  grid-column: 1;
  justify-self: end;
  align-self: end;
  width: 96px;
  height: 48px;
  color: var(--sc-field);
  pointer-events: none;
}

.screen--scale .scale__lead {
  grid-row: 1;
  grid-column: 1;
  align-self: end;
  min-height: 48px;
  display: flex;
  align-items: flex-end;
  margin: 0;
  font-family: var(--body);
  font-size: 1.25rem;
  font-weight: 400;
  line-height: 1;
  color: var(--porcelain);
}

/* The stem: the reading face, big and light on the ink, three lines
   reserved so the control never jumps between a one-line item and a
   three-line one. 28px at 375 (DESIGN-assess 4), 36 from 768. At most 24
   characters a line at 375. */
.screen--scale .scale__stem {
  margin: 0;
  font-family: var(--body);
  font-size: clamp(1.75rem, 7.4vw, 2.25rem);
  font-weight: 400;
  line-height: 1.25;
  letter-spacing: 0;
  text-wrap: balance;
  min-height: 3.75em;
  color: var(--porcelain);
}

@media (min-width: 768px) {
  .screen--scale .scale__stem {
    font-size: 2.25rem;
  }
  .scale__motif {
    width: 160px;
    height: 80px;
  }
}

/* THE GLOSS -- "Five answers, from not true of you to true of you." --
   between the stem and the control. A SIBLING of .scale__opts, never a
   descendant of an option: a word of explanation attached to one category is
   a salience ramp made of type. Quieter than the thing it explains by size
   alone -- full porcelain, because any alpha on the ink falls under 4.5:1
   (88% measured 4.91 on arc-12; 82% 4.44 on arc-10). It is SHOWN on the
   first statement of a set only; on statements 2..n the server sends the
   same string with `gloss_shown: false` and the builder draws it HELD
   (below): same box, same height, unseen and unheard. So its height at a
   given width is a constant on every screen of a set -- including after
   Back -- and it cannot reintroduce the jump the stem's reserve exists to
   stop (design review r1, DP1-1: 34px at 375, 52px at 360 when it was
   simply dropped). Balanced, so a two-line wrap at 360 does not leave
   "of you." alone on the second line. */
.scale__gloss {
  margin: 0 0 4px;
  max-width: 46ch;
  color: var(--porcelain);
  font-family: var(--body);
  font-size: var(--fs-xs, 0.9375rem);
  font-weight: 400;
  line-height: 1.45;
  letter-spacing: 0;
  text-wrap: balance;
}

/* THE HELD GLOSS. The line's place on statements 2..n of a set: laid out
   exactly as the shown one (it is the same element class and the same
   string), and hidden from sight. `aria-hidden` and no id are on the node
   itself, so a screen reader neither reads it nor is pointed at it. */
.scale__gloss--held {
  visibility: hidden;
}

/* EQUAL HEIGHT BY CONSTRUCTION. The item's five words are not the same
   length, so on a narrow phone one row wraps first and would be the tallest.
   `grid-auto-rows: 1fr` resolves every row to the tallest at every width --
   and on the dial and the steps it does the same for the geometry's rows.

   THE GROUP'S CLASS. The cards are today's DOM and their group is
   `.scale__opts` alone -- the base rules below ARE the cards. The dial and
   the steps add `.scale__opts--dial` / `.scale__opts--steps` on the same
   group, so their rules key on a parent and never on a position. */
.scale__opts {
  display: grid;
  grid-auto-rows: 1fr;
  gap: 8px;
}

/* -- A · cards: five identical rows ---------------------------------------- */

/* THE FIVE ARE IDENTICAL. Same edge, same fill, same weight, same height, on
   every screen. Position and the item's own words carry ordinality; nothing
   else does. Each row is a white card on the ink -- paper is 6.27:1 at
   worst against the twelve arcs as a shape -- with plum words on it at
   17:1. The edge is the light frame's quiet edge. Do not add :nth-child
   here. Do not "warm up" the agree end. */
.screen--scale .scale__opt {
  display: flex;
  align-items: center;
  gap: 12px;
  width: 100%;
  min-height: 52px;
  padding: 6px 12px;
  border: 2px solid var(--sc-edge-light);
  border-radius: var(--r-control, 8px);
  background: var(--paper);
  color: var(--night-plum);
  font-family: var(--body);
  font-size: var(--fs-body, 1.0625rem);
  font-weight: 400;
  line-height: 1.3;
  text-align: left;
  cursor: pointer;
  transition: border-color var(--t-state, 150ms cubic-bezier(.2, 0, 0, 1)),
              background-color var(--t-state, 150ms cubic-bezier(.2, 0, 0, 1)),
              transform var(--t-press, 100ms cubic-bezier(.4, 0, 1, 1));
}

/* Hover: the edge goes to the ink, all five equally. */
.screen--scale .scale__opt:hover {
  border-color: var(--night-plum);
}

.screen--scale .scale__opt:active {
  transform: translateY(1px);
}

/* The keycap: the digit the key is mapped to, on the set's field tint. The
   same square on all five rows, so it cannot say which row you are looking
   at; it makes the chosen state's jump -- pale tint to solid set colour --
   large instead of a border tweak. Night plum on the field is 12.58:1 at
   worst. */
.screen--scale .scale__num {
  flex: none;
  width: 32px;
  height: 32px;
  display: grid;
  place-items: center;
  border: 1px solid var(--sc-edge-light);
  border-radius: 6px;
  background: var(--sc-field);
  color: var(--night-plum);
  font-family: var(--body);
  font-size: var(--fs-xs, 0.9375rem);
  font-style: normal;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
  line-height: 1;
  transition: background-color var(--t-feedback, 120ms cubic-bezier(.2, 0, 0, 1)),
              border-color var(--t-feedback, 120ms cubic-bezier(.2, 0, 0, 1)),
              color var(--t-feedback, 120ms cubic-bezier(.2, 0, 0, 1));
}

/* Pressing fills the keycap with the set colour, 120ms. */
.screen--scale .scale__opt:active .scale__num {
  border-color: var(--sc-arc);
  background: var(--sc-arc);
  color: var(--porcelain);
}

.scale__anchor {
  flex: 1;
}

/* No answer ends on one word alone on its last line (design review r1,
   DP1-4: O2-01 "The world would manage without / it" on the dial at 375). */
.screen--scale .scale__anchor {
  text-wrap: pretty;
}

/* The one place colour touches an answer, exactly as the preview marks it.
   It says "this one", never "this is the good one": it is the SET colour,
   identical whichever of the five was chosen -- the edge and an inset ring
   on the white row (6.27:1 at worst on paper), the keycap filled with it
   (porcelain digit, 5.83:1 at worst), and a tick, so it survives greyscale
   and dichromacy. At --t-feedback so the fill is fully drawn well inside the
   auto-advance and the person sees what was recorded. */
.screen--scale .scale__opt--chosen {
  border-color: var(--sc-arc);
  box-shadow: inset 0 0 0 1px var(--sc-arc);
  transition: border-color var(--t-feedback, 120ms cubic-bezier(.2, 0, 0, 1)),
              background-color var(--t-feedback, 120ms cubic-bezier(.2, 0, 0, 1)),
              box-shadow var(--t-feedback, 120ms cubic-bezier(.2, 0, 0, 1));
}

.screen--scale .scale__opt--chosen .scale__num {
  border-color: var(--sc-arc);
  background: var(--sc-arc);
  color: var(--porcelain);
}

.screen--scale .scale__opt--chosen::after {
  content: "\2713";
  margin-left: auto;
  padding-left: 12px;
  color: var(--night-plum);
  font-size: 1.1rem;
  line-height: 1;
}

/* -- B · dial: a vertical rail of five stops, a thumb that snaps ------------- */
/*
   The rail the preview's B draws (DESIGN-assess 5c; review round 1, DR-A3):
   five full-width rows, each a 44px stop on a WHITE rail at the left with
   the item's words beside it in porcelain on the ink, at the cards' reading
   size, so the five words are read, not squeezed into five columns. The
   stops are pale discs on the rail with the digit in plum -- the cards'
   keycap, round. The thumb -- one plum disc in a paper ring, absent until a
   stop is pressed, moved by a custom property the client sets through
   CSSOM -- covers the stop it rests on, and the chosen stop's words sit in
   a white pill beside it: the preview's "at" mark. Nothing positional: the
   thumb's position is `--at`, never a per-stop rule. */
.scale__opts--dial {
  position: relative;
  grid-template-columns: minmax(0, 1fr);
  gap: 8px;
  padding-top: 0;
  /* The rows are the full width of the card, so a finger that lands on the
     words is as likely to be scrolling as answering: a vertical swipe there
     belongs to the page (review round 2, DR2-1). The drag belongs to the
     discs -- `touch-action: none` on the keycap and the thumb below -- so a
     drag that starts on a disc still moves the thumb. When the page takes a
     swipe the browser cancels the pointer and the thumb goes back to what
     is on record (`assess-geometries.mjs::buildDial`). A tap anywhere on a
     row is still the answer. */
  touch-action: pan-y;
  user-select: none;
  -webkit-user-select: none;
  /* Its own stacking context: the track under the five stops, the thumb
     over them, whatever the card's own layering does. */
  isolation: isolate;
}

/* The rail: white on the ink (6.27:1 at worst as a shape), from the first
   stop's centre to the last's, under the discs. */
.dial__track {
  position: absolute;
  z-index: 0;
  top: 10%;
  bottom: 10%;
  left: 16px;
  width: 12px;
  box-sizing: border-box;
  border-radius: var(--r-pill, 999px);
  background: var(--paper);
  pointer-events: none;
}

.screen--scale .scale__opts--dial .scale__opt {
  position: relative;
  z-index: 1;
  grid-column: 1 / -1;
  flex-direction: row;
  align-items: center;
  gap: 12px;
  /* The cards' row height: the 44px stop and 2px either side, so the rail
     folds about where the cards do (56px stop to stop with the gap). */
  min-height: 48px;
  padding: 2px 0;
  border: 0;
  border-radius: var(--r-control, 8px);
  background: none;
  /* The words are on the ink, so they are porcelain (5.83:1 at worst). */
  color: var(--porcelain);
  text-align: left;
}

/* No press-down on a stop: under a drag the thumb is the only thing that
   moves, so the stop the press began on does not look chosen beside it. */
.screen--scale .scale__opts--dial .scale__opt:active {
  transform: none;
}

.screen--scale .scale__opts--dial .scale__num {
  width: 44px;
  height: 44px;
  border: 2px solid var(--sc-edge-light);
  border-radius: var(--r-pill, 999px);
  background: var(--sc-field);
  font-size: 1rem;
  /* The disc is the handle: a drag that starts here is the dial's, not the
     page's. */
  touch-action: none;
}

.screen--scale .scale__opts--dial .scale__opt:hover .scale__num {
  border-color: var(--night-plum);
}

/* The cards fill the keycap with the set colour while pressed; on the rail
   that fill would sit beside the thumb and read as a second answer. A
   pressed stop keeps its rest keycap until it is the recorded one. */
.screen--scale .scale__opts--dial .scale__opt:active:not(.scale__opt--chosen) .scale__num {
  border-color: var(--sc-edge-light);
  background: var(--sc-field);
  color: var(--night-plum);
}

.screen--scale .scale__opts--dial .scale__opt:active:not(.scale__opt--chosen):hover .scale__num {
  border-color: var(--night-plum);
}

/* The words beside the stop, at the cards' reading size, in a box that is
   the same at rest and chosen -- only its fill and ink change -- so the
   chosen pill never moves a word. */
.screen--scale .scale__opts--dial .scale__anchor {
  flex: 1;
  align-self: stretch;
  display: flex;
  align-items: center;
  padding: 4px 12px;
  border-radius: var(--r-control, 8px);
  font-size: var(--fs-body, 1.0625rem);
  line-height: 1.3;
  transition: background-color var(--t-feedback, 120ms cubic-bezier(.2, 0, 0, 1)),
              color var(--t-feedback, 120ms cubic-bezier(.2, 0, 0, 1));
}

/* The chosen stop is the thumb over its disc and its words in a white pill
   with plum ink (17:1): the preview's "at" mark. The cards' row ring and
   tick would box a rail row, so they are off here. */
.screen--scale .scale__opts--dial .scale__opt--chosen {
  border-color: transparent;
  box-shadow: none;
}

.screen--scale .scale__opts--dial .scale__opt--chosen .scale__anchor {
  background: var(--paper);
  color: var(--night-plum);
}

.screen--scale .scale__opts--dial .scale__opt--chosen::after {
  content: none;
}

/* The thumb: a plum disc with the digit, one row tall, so a translate by
   its own height plus the gap is one stop. Plum on the ink alone is under
   3:1, so the disc wears a 2px paper ring (paper on the ink 6.27:1, plum on
   paper 17:1). It has no rest declaration because it does not exist at
   rest; it is appended on the first press. */
.dial__thumb {
  position: absolute;
  top: 0;
  left: 0;
  z-index: 2;
  width: 44px;
  height: calc((100% - 4 * 8px) / 5);
  display: grid;
  place-items: center;
  isolation: isolate;
  pointer-events: none;
  /* Under the finger it is the disc it covers: a drag, never a scroll. */
  touch-action: none;
  color: var(--porcelain);
  font-family: var(--display);
  font-optical-sizing: auto;
  font-size: 1.25rem;
  font-weight: 700;
  line-height: 1;
  transform: translateY(calc(var(--at, 0) * (100% + 8px)));
  transition: transform var(--t-feedback, 120ms cubic-bezier(.2, 0, 0, 1));
}

.dial__thumb::before {
  content: "";
  position: absolute;
  z-index: -1;
  top: 50%;
  left: 50%;
  width: 44px;
  height: 44px;
  box-sizing: border-box;
  border: 2px solid var(--paper);
  border-radius: var(--r-pill, 999px);
  background: var(--night-plum);
  transform: translate(-50%, -50%);
}

/* -- C · steps: Not me / the item's middle words / That's me, then two ----- */
/*
   One row at a time. The first is three identical white leads: "Not me",
   the item's own middle words (category 3, recorded at once) and "That's
   me". An outer lead SWAPS the row for that side's two white words, 1 then
   2 or 4 then 5, left to right; "Change" under the group swaps back. Under
   it all the legend, porcelain on the ink: the item's five words, 1 to 5,
   display only, always visible -- the pair a lead opened carries a 1px
   porcelain edge, the recorded word is the white pill (DESIGN-assess 5c;
   the preview's C). One row tall, so the legend and the footer stay inside
   the fold at 375x812. */
.scale__opts--steps {
  gap: 8px;
}

.steps__row {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 8px;
}

/* THE MIDDLE LEAD GETS THE ROOM (design review, polish r2, DP2-1). Three
   equal thirds of a phone left the item's own middle words 75-80px, so
   "Sometimes", "somewhere" and "background," broke inside the word once the
   lead was held to its card. The side leads only ever say "Not me" and
   "That's me", so the middle track takes 1.4 shares to their 0.8; and no
   track is ever narrower than its longest word (`min-content`), so on the
   narrowest phone the grid gives the middle lead what it needs rather than
   cutting a word. The three leads keep one state styling
   (tests/test_palette.py); only the tracks differ. */
.steps__row--coarse {
  grid-template-columns:
    minmax(min-content, 0.8fr) minmax(min-content, 1.4fr) minmax(min-content, 0.8fr);
}

/* The second row: absent until a lead is pressed, then in the first row's
   place. */
.steps__row--fine {
  grid-template-columns: repeat(2, minmax(0, 1fr));
  display: none;
}

.steps__row--fine.steps__row--open {
  display: grid;
}

/* `position: relative` so the chosen lead's tick is drawn on the corner,
   out of the column's flow (review round 3, DR3-1: in the flow it made all
   three leads 20px taller in the 220ms before the advance and pushed the
   legend down). The tick itself is the ask chip's disc, below. 12px above
   and below the words, not 6: the disc reaches 14px down from the lead's
   top edge, so the first line now starts at or under it whatever the
   words. Measured: a word too wide for a third of a phone ("Sometimes,"
   at 375) runs into the right padding, and at 6px the disc sat on it in 17
   of the 300 middle words. A one-line lead is the same 64px as before, a
   two-line one 4px taller, a longer one 12px (the steps' fold worst is
   still the open pair, 596 at 375x812). */
.screen--scale .steps__opt {
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  justify-content: center;
  gap: 2px;
  width: 100%;
  min-height: 64px;
  padding: 12px 10px;
  border: 2px solid var(--sc-edge-light);
  border-radius: var(--r-control, 8px);
  background: var(--paper);
  color: var(--night-plum);
  font-family: var(--body);
  font-size: 1rem;
  font-weight: 700;
  line-height: 1.25;
  text-align: left;
  cursor: pointer;
  transition: border-color var(--t-state, 150ms cubic-bezier(.2, 0, 0, 1)),
              background-color var(--t-state, 150ms cubic-bezier(.2, 0, 0, 1)),
              transform var(--t-press, 100ms cubic-bezier(.4, 0, 1, 1));
}

.screen--scale .steps__opt:hover {
  border-color: var(--night-plum);
}

.screen--scale .steps__opt:active {
  transform: translateY(1px);
}

/* The lead that is open: the set colour's edge, like a chosen row. */
.screen--scale .steps__opt--on {
  border-color: var(--sc-arc);
  box-shadow: inset 0 0 0 1px var(--sc-arc);
}

/* The middle lead recorded 3: edge, tick. */
.screen--scale .steps__opt--chosen {
  border-color: var(--sc-arc);
  box-shadow: inset 0 0 0 1px var(--sc-arc);
}

/* THE TICK SITS ON THE CORNER, OFF THE WORDS (design review, assess finish
   round 1, DF1-2a). Drawn at 6px/8px inside the corner it landed on the
   first line of 54 of the 300 middle words ("Sometimes I think the worst"
   lost 13px of it), in the 220ms before the advance and for as long as Back
   kept an "in between" answer on screen. It is now the ask chip's mark: a
   20px plum disc in a 2px paper ring, its centre 4px inside the corner, so
   it covers the lead's border and padding and never its words. It reaches
   6px into the 8px gap between leads, so it never touches a neighbour. The
   second row's cards carry the same disc (below). */
.screen--scale .steps__opt--chosen::after,
.screen--scale .scale__opts--steps .scale__opt--chosen::after {
  content: "\2713";
  position: absolute;
  top: -6px;
  right: -6px;
  width: 20px;
  height: 20px;
  box-sizing: border-box;
  display: grid;
  place-items: center;
  margin: 0;
  padding: 0;
  border: 2px solid var(--paper);
  border-radius: var(--r-pill, 999px);
  background: var(--night-plum);
  color: var(--porcelain);
  font-size: 0.6875rem;
  font-weight: 700;
  line-height: 1;
  pointer-events: none;
}

/* The lead's words: "Not me", the item's middle words, "That's me" -- one
   size for all three, so the middle one is not quieter than the ladder. */
.steps__lead {
  display: block;
  overflow-wrap: break-word;
}

/* THE LEAD WRAPS INSIDE ITS CARD (design review r1, DP1-2). The card is a
   column flexbox that hangs its words from the left, so the span was as wide
   as its longest unbroken run and a long middle ("On in the background,
   mostly") ran 17px past the content edge at 360 -- into the padding, and
   there under the corner tick. Capped at the card's content width, it wraps
   between words.
   AND NEVER INSIDE ONE (polish r2, DP2-1). Round 1 let a word too long for
   the line break anywhere, with a hyphen where a dictionary knew one; on
   phones that cut the item's own words in half ("Sometime / s I think the
   worst"). `normal` and `manual` break only between words (and after a
   hyphen the words already have), and since `anywhere` no longer shrinks the
   lead's min-content to one letter, the track above is always at least its
   longest word wide. */
.screen--scale .steps__lead {
  max-width: 100%;
  overflow-wrap: normal;
  hyphens: manual;
  text-wrap: balance;   /* DP3-1: no lone last word on a phone */
}

/* The second row's two: keycap on the left, "a lot" / "a little" over the
   item's words. The tick sits in the corner rather than in the grid. Both
   cards hang their words from the top, so "a lot" and "a little" sit on
   one line however differently the item's words wrap (review round 3,
   DR3-3); and the keycaps hang from the top beside them, so the two sit on
   one line too (DF1-2b: centred on their own card's words, C2-06's two
   keycaps sat 20px apart at 375). */
.screen--scale .scale__opts--steps .scale__opt {
  position: relative;
  display: grid;
  grid-template-columns: auto minmax(0, 1fr);
  column-gap: 8px;
  row-gap: 0;
  align-items: start;
  align-content: start;
  min-height: 64px;
  padding: 6px 10px;
}

.screen--scale .scale__opts--steps .scale__num {
  grid-row: 1 / span 2;
  align-self: start;
  margin-top: 2px;
  width: 28px;
  height: 28px;
}

/* "a lot" / "a little" on the white card: the quiet ink, 12.7:1 on paper. */
.steps__how {
  display: block;
  font-size: 0.8125rem;
  font-weight: 700;
  line-height: 1.25;
  color: var(--sc-quiet);
}

/* The item's words at reading size: no geometry sets them under the 16px
   body floor (review round 2, DR2-3). 16px here and in the legend, not the
   cards' 17px, because the steps put the words in two places at once and
   the pair's cards are half the width. */
.screen--scale .scale__opts--steps .scale__anchor {
  font-size: 1rem;
  line-height: 1.25;
}

/* "Change": back to the first row. A text link on the ink, under the
   group; present only while a side is open. */
.screen--scale .steps__change {
  justify-self: start;
  min-height: 44px;
  color: var(--porcelain);
  font-family: var(--body);
  font-size: var(--fs-xs, 0.9375rem);
  font-weight: 700;
}

/* THE LEGEND: the item's five words, 1 to 5, always visible. A list, not
   five more controls, in porcelain on the ink. The five items are identical
   at rest, edged (`--in`) and filled (`--on`) by class alone -- nothing
   positional, so it cannot carry a ramp any more than the controls can. */
.steps__legend {
  display: grid;
  gap: 2px;
  margin: 4px 0 0;
  padding: 0;
  list-style: none;
}

@media (min-width: 768px) {
  /* No fold pressure from here up: the two outlined words of a pair get a
     little air between them (DR3-3, optional part). */
  .steps__legend {
    gap: 4px;
  }
}

.steps__legend-item {
  display: flex;
  align-items: baseline;
  gap: 8px;
  padding: 1px 8px;
  border: 1px solid transparent;
  border-radius: var(--r-control, 8px);
  color: var(--porcelain);
  font-family: var(--body);
  /* Reading size: the legend is the one place all five words are on the
     screen together, so it is never under the 16px body floor (DR2-3). */
  font-size: 1rem;
  font-weight: 400;
  line-height: 1.35;
  transition: border-color var(--t-feedback, 120ms cubic-bezier(.2, 0, 0, 1)),
              background-color var(--t-feedback, 120ms cubic-bezier(.2, 0, 0, 1)),
              color var(--t-feedback, 120ms cubic-bezier(.2, 0, 0, 1));
}

.steps__legend-num {
  flex: none;
  min-width: 1ch;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
}

.steps__legend-text {
  flex: 1;
}

/* The pair a lead opened: a porcelain edge on the ink. */
.steps__legend-item--in {
  border-color: var(--porcelain);
}

/* The recorded word: the white pill, plum ink, as the preview marks it. */
.steps__legend-item--on {
  border-color: var(--paper);
  background: var(--paper);
  color: var(--night-plum);
}

/* -- the depends pill: in the footer, on the ink ----------------------------- */

/* "That one depends on the situation for me". Dashed, quiet, and in the
   footer beside Back and Skip -- unmistakably NOT a sixth scale point. It is
   built only once the 1-5 answer is recorded and never enters theta. The
   footer sits on the set's ink, so it is porcelain. */
.screen .scale__varies {
  min-height: 44px;
  display: inline-flex;
  align-items: center;
  gap: 8px;
  padding: 8px 14px;
  border: 1px dashed var(--porcelain);
  border-radius: var(--r-pill, 999px);
  background: none;
  color: var(--porcelain);
  font-family: var(--body);
  font-size: var(--fs-xs, 0.9375rem);
  font-weight: 700;
  line-height: 1.3;
  text-align: left;
  /* Two even lines, never "... situation for / me" with one word alone on
     the last (design review, polish r2, DP2-2; seen at 360). */
  text-wrap: balance;
  text-decoration: none;
  cursor: pointer;
  transition: border-color var(--t-state, 150ms cubic-bezier(.2, 0, 0, 1)),
              background-color var(--t-state, 150ms cubic-bezier(.2, 0, 0, 1)),
              color var(--t-state, 150ms cubic-bezier(.2, 0, 0, 1));
}

.screen .scale__varies:hover {
  border-style: solid;
}

/* Flagged: a solid porcelain fill, and the line says it is listed back.
   `pretty`, not `balance`, for this longer line: balanced, it put the dash
   at the head of the second line ("... your result / -- your report ..."
   at 360); `pretty` keeps it on the first and still leaves no word alone. */
.screen .scale__varies--on {
  border-style: solid;
  background: var(--porcelain);
  color: var(--night-plum);
  text-wrap: pretty;
}

.screen .scale__varies:disabled {
  cursor: default;
  opacity: 1;
}

/* ---------------------------------------------------------------------------
   5 · SET CARD -- the chapter break, the one colour event in the test.

   Full-bleed set colour, porcelain ink, the set numeral as the visual, and
   exactly one control. It never auto-advances. Nothing on it may describe the
   person (the whitelist is enforced in Python; this is the screen the
   temptation lives on).

   THE NUMERAL is counted off the Spine (section 2): it is the number of
   walked beads, which on a chapter card is the set the card opens -- the same
   set the header's "Set 5 of 12" and the card's own colour already name. It
   is drawn only while a bead is current, and it is hidden from assistive
   technology: the live region already says "Set 4 of 12 done."

   In greyscale: dark to all four edges, one very large numeral, no card,
   one button.
   --------------------------------------------------------------------------- */

.screen--setcard {
  --sc-focus-ground: var(--sc-arc);
  --sc-focus-ink: var(--porcelain);
  --ink: var(--porcelain);
  --ink-quiet: var(--porcelain);
  --ink-fill: var(--night-plum);
  --ink-lift: var(--porcelain-lift, #e9e9e2);
  background-color: var(--sc-arc);
  color: var(--porcelain);
  gap: 8px;
  animation: ground-in var(--t-room, 320ms cubic-bezier(.22, 1, .36, 1)) both;
}

.assess-body:has(.spine__bead--now) .screen--setcard::before {
  content: counter(knoi-set);
  content: counter(knoi-set) / "";
  display: block;
  grid-row: 1;
  grid-column: 1;
  justify-self: start;
  align-self: center;
  margin: -0.06em 0 8px -0.04em;
  font-family: var(--display);
  font-optical-sizing: auto;
  font-size: var(--fs-numeral, clamp(8rem, 40vw, 18rem));
  font-weight: 800;
  font-variant-numeric: tabular-nums;
  line-height: 0.85;
  letter-spacing: -0.04em;
  color: var(--porcelain);
}

/* The set's motif, porcelain, at the right of the numeral: the same drawing
   the set's question cards carry at 48px, so the colour AND the drawing
   carry across the break. Identical for everyone in a set; never a result.
   Sized so "12" at the numeral's size and the motif share the row at 375. */
.setcard__motif {
  grid-row: 1;
  grid-column: 1;
  justify-self: end;
  align-self: center;
  width: clamp(96px, 36vw, 192px);
  height: auto;
  aspect-ratio: 2 / 1;
  color: var(--porcelain);
  pointer-events: none;
}

/* No eyebrow. The server's "SET 4 OF 12 · DONE" is no longer drawn on the
   chapter card (DESIGN-assess 5 and 8; review round 1, DR-A4): it was a
   middle-dot meta string under a numeral one higher. The numeral, the head
   and the live region say where you are. */

.setcard__head {
  margin: 0;
  max-width: 16ch;
  font-family: var(--display);
  font-optical-sizing: auto;
  font-size: var(--fs-h2, clamp(2rem, 4.5vw, 3.5rem));
  font-weight: 700;
  line-height: 1;
  letter-spacing: -0.03em;
  text-wrap: balance;
  color: var(--porcelain);
}

/* Full porcelain, no alpha: 82% measured 4.44:1 on --arc-10, under the floor.
   THE FOLD IS FULL. The fullest chapter card (fact, anchors, look line,
   allocation line, nudge) runs to 18 lines with Keep going at 808 of 812 at
   375x812 (review round 2, DR2-5). If a line is ever added to this card,
   set `.setcard__fact` alone to 15px, as DESIGN-assess 4 specifies: that
   frees about 20px and lets the look and allocation lines lead. */
.setcard__fact,
.setcard__look {
  margin: 0;
  max-width: 38rem;
  font-family: var(--body);
  font-size: var(--fs-body, 1.0625rem);
  line-height: 1.55;
  color: var(--porcelain);
}

/* The five fixed steps, named once per chapter and never on a card: the
   server's `anchor_gloss`, in the fact's size. Null on the handoff card. */
.setcard__anchors {
  margin: 0;
  max-width: 38rem;
  font-family: var(--body);
  font-size: var(--fs-xs, 0.9375rem);
  line-height: 1.5;
  color: var(--porcelain);
}

/* "Your answers point at <parts>, so we asked more about them." The
   server's `allocation_note`: parts only, never a direction or a level. */
.setcard__allocation {
  margin: 0;
  max-width: 38rem;
  font-family: var(--body);
  font-size: var(--fs-body, 1.0625rem);
  line-height: 1.5;
  color: var(--porcelain);
}

/* The rushing nudge: once per session, non-blocking, dismissible by doing
   nothing. It never shames and never gates. An edged note, not a left rule. */
.setcard__nudge {
  margin: 4px 0 0;
  max-width: 38rem;
  padding: 12px 16px;
  border: 1px solid var(--porcelain);
  border-radius: var(--r-control, 8px);
  font-size: var(--fs-xs, 0.9375rem);
  line-height: 1.5;
  color: var(--porcelain);
}

/* Keep going and Back on ONE row (review round 1, DR-A7): `paint()` puts
   the footer inside this row, so a card that also carries the nudge or an
   allocation line keeps Back inside 812 at 375. Back takes what is left of
   the row and wraps its own words onto a second line rather than the row
   wrapping. */
.setcard__actions {
  display: flex;
  align-items: center;
  gap: 16px;
  margin-top: 8px;
}

.screen--setcard .setcard__actions .screen__footer {
  flex: 1 1 0;
  min-width: 0;
  margin: 0;
}

/* The one control: the primary button on a dark ground -- porcelain fill,
   plum label, the same shape as every other primary. */
.screen--setcard .setcard__go {
  flex: none;
  min-height: 56px;
  justify-self: start;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  margin-top: 0;
  padding: 12px 32px;
  border: 2px solid var(--porcelain);
  border-radius: var(--r-control, 8px);
  background: var(--porcelain);
  box-shadow: none;
  color: var(--night-plum);
  font-family: var(--body);
  font-size: var(--fs-body, 1.0625rem);
  font-weight: 700;
  line-height: 1.2;
  text-decoration: none;
  cursor: pointer;
  transition: background-color var(--t-state, 150ms cubic-bezier(.2, 0, 0, 1)),
              border-color var(--t-state, 150ms cubic-bezier(.2, 0, 0, 1)),
              transform var(--t-press, 100ms cubic-bezier(.4, 0, 1, 1));
}

.screen--setcard .setcard__go:hover {
  background: var(--porcelain-lift, #e9e9e2);
  border-color: var(--porcelain-lift, #e9e9e2);
}

.screen--setcard .setcard__go:active {
  transform: translateY(1px);
}

/* ---------------------------------------------------------------------------
   6 · S3 · STORY — the scenario screen. The lights go down.

   Saturated arc ground with a plum vignette, and a --paper PAGE on it. The
   ground VALUE is what makes this land before a word is read; the page is what
   makes it a different object rather than a recoloured question.

   The page is drawn as ::before, so no wrapper element is required and the
   client can render .story__tag / .story__body / .story__ask / .story__rule /
   .story__choices / .story__chip as direct children of .screen--story.

   In greyscale: a dark frame with a large light page inside it, carrying 2-4
   TALL blocks. Nothing else in the product has that silhouette.

   THE READING ORDER, AND THE TYPE SCALE THAT ENFORCES IT. Moshe, after the
   live test: "I have no idea what I am being asked here". The card he quoted
   read as one wall -- a dateline, then a paragraph of scene, then buttons --
   and nothing on it asked anything. The scale below is the fix made visual:

       .story__tag    0.75rem mono, muted        set dressing. QUIETEST.
       .story__body   1.375-1.75rem, regular     the situation. LARGEST.
       .story__ask    1.25-1.5rem, BOLD          the ask. Last full-size line
                                                 before the answer.
       .story__rule   0.8125rem mono, muted      the control's caption.

   The tag stays first in reading order -- a screen reader needs "Six weeks
   later" before the scene it dates, and the same slot carries the recap
   eyebrow ("Last time: you'd ...") on a branched card, which must precede
   the card it explains -- but it is half the body's size and muted, so the
   eye lands on the situation and reads the dateline second, the way a
   caption is read. The ask is bold and the body is not, and the ask is the
   same weight and size as `.write__prompt`, the write card's own ask, so
   "this is the question" looks the same on every card in the product.
   --------------------------------------------------------------------------- */

.screen--story {
  --page-inset: clamp(14px, 3.5vw, 44px);
  --page-pad: clamp(20px, 4vw, 48px);
  --sc-focus-ground: var(--paper);
  --sc-focus-ink: var(--night-plum);

  padding: calc(var(--page-inset) + var(--page-pad));
  gap: clamp(14px, 2.4vw, 24px);
  color: var(--night-plum);
  background-color: var(--sc-arc);
  background-image:
    radial-gradient(120% 90% at 50% 35%,
      transparent 20%, rgba(36, 23, 45, 0.62) 100%);
  animation: ground-in var(--t-scene, 520ms cubic-bezier(.65, 0, .35, 1)) both;
}

.screen--story::before {
  content: "";
  position: absolute;
  top: var(--page-inset);
  bottom: var(--page-inset);
  left: 50%;
  transform: translateX(-50%);
  width: min(
    calc(100% - 2 * var(--page-inset)),
    calc(var(--sc-measure) + 2 * var(--page-pad))
  );
  z-index: 0;
  border-radius: var(--r-page, 28px);
  background: var(--paper);
  box-shadow: 0 30px 90px rgba(36, 23, 45, 0.45);
}

.story__tag {
  margin: 0 0 clamp(2px, 0.6vw, 6px);
  font-family: var(--mono);
  font-size: 0.75rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--muted);
}

/* A div of paragraphs, not a single paragraph -- the scene can run to two or
   three beats. The type size is set here and inherited; the spacing is set on
   the children so the block ends flush with the choices below it. */
.story__body {
  margin: 0 0 clamp(6px, 1.5vw, 12px);
  font-family: var(--body);
  font-size: clamp(1.375rem, 3vw, 1.75rem);
  font-weight: 400;
  line-height: 1.35;
  text-wrap: pretty;
  color: var(--night-plum);
}

.story__body > p {
  margin: 0 0 0.55em;
}

.story__body > p:last-child {
  margin-bottom: 0;
}

/* THE ASK. "What do you do?" -- the one sentence a card cannot be without,
   and the last full-size thing a person reads before the answer. Bold where
   the scene is regular, so the eye can tell the question from the story at a
   glance; the same face, size and weight as `.write__prompt`, the write
   card's ask, so the question looks like the question on every card. No arc
   colour: --sc-arc marks selection and focus and nothing else on this page,
   and a coloured question would read as an option. */
.story__ask {
  margin: clamp(2px, 0.8vw, 8px) 0 0;
  font-family: var(--body);
  font-size: clamp(1.25rem, 2.6vw, 1.5rem);
  font-weight: 700;
  line-height: 1.3;
  text-wrap: balance;
  color: var(--night-plum);
}

/* With an ask under it the scene needs no trailing space of its own. */
.story__body:has(+ .story__ask) {
  margin-bottom: 0;
}

.story__choices {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(15rem, 1fr));
  gap: 12px;
}

.screen--story .story__choice {
  position: relative;
  display: flex;
  align-items: flex-start;
  gap: 12px;
  min-height: 7rem;
  padding: 16px 18px;
  border: 2px solid rgba(36, 23, 45, 0.5);
  border-radius: var(--r-card, 16px);
  background: var(--paper);
  color: var(--night-plum);
  font-family: var(--body);
  font-size: 1.0625rem;
  font-weight: 500;
  line-height: 1.4;
  text-align: left;
  cursor: pointer;
  transition: border-color var(--t-quick, 180ms cubic-bezier(.2, 0, 0, 1)),
              background-color var(--t-quick, 180ms cubic-bezier(.2, 0, 0, 1)),
              transform var(--t-instant, 90ms cubic-bezier(.4, 0, 1, 1));
}

/* The keyboard numeral, drawn the same way the scale's is so 1-4 here and 1-5
   there read as the same instruction. The client emits it; nothing here
   generates a second one. */
.screen--story .story__key {
  flex: none;
  width: 24px;
  height: 24px;
  display: grid;
  place-items: center;
  border-radius: 6px;
  background: rgba(36, 23, 45, 0.07);
  color: var(--muted);
  font-family: var(--mono);
  font-size: 0.75rem;
  font-style: normal;
  line-height: 1;
}

.story__text {
  flex: 1;
}

.screen--story .story__choice:hover {
  border-color: var(--sc-arc);
  background: var(--porcelain);
}

.screen--story .story__choice:active {
  transform: translateY(1px);
}

/* Back arrives on a story whose choice is already made. Marked in the set
   colour with a heavier edge and a glyph, exactly as the scale marks its
   chosen row -- "this is the one you picked", never "this was the good one". */
.screen--story .story__choice--chosen {
  border-color: var(--sc-arc);
  background: var(--porcelain);
}

.screen--story .story__choice--chosen .story__key {
  background: var(--sc-arc);
  color: var(--porcelain);
}

.screen--story .story__choice--chosen::after {
  content: "\2713";
  flex: none;
  align-self: flex-start;
  margin-left: 4px;
  color: var(--sc-arc);
  font-size: 1.1rem;
  line-height: 1.3;
}

/* The honesty chip. Permanent, on every story page, never a tooltip:
   "Not scored. This changes what you're asked next, and what your report
   quotes back to you." (spec 2.5 S3) */
.story__chip {
  justify-self: start;
  margin-top: clamp(8px, 2vw, 20px);
  padding: 8px 12px;
  border: 1px solid rgba(36, 23, 45, 0.18);
  border-radius: var(--r-control, 8px);
  background: var(--porcelain);
  font-family: var(--mono);
  font-size: 0.75rem;
  line-height: 1.45;
  color: var(--muted);
}

/* ---------------------------------------------------------------------------
   6b · THE SIX ANSWER GEOMETRIES — one scene, six shapes of answer

   `api/knoi_assess/content/arcs/_contract.py` lets a story card declare what
   SHAPE its answer is: choose, binary, order, choose_multi, write,
   choose_write. Four-button cards went from 93% of the corpus to 12% because
   of it. Everything below is the other 88% having somewhere to be drawn.

   THE SCENE HALF DOES NOT MOVE. Ground, page, tag, body and honesty chip are
   identical in all six -- it is one screen kind and it has to keep reading as
   one, or the arc stops being a story and becomes a quiz with themes. What
   changes is the answer object, and it changes completely:

     choose        2-4 tall cards in a responsive grid          (section 6)
     binary        TWO cards, side by side, full height, with a
                   hairline between them and no third option
     order         a numbered stack on top, a pool underneath,
                   split by a dashed rule
     choose_multi  a ragged wrap of pills with check glyphs and
                   a live count
     write         no buttons at all: one rule-underlined field
     choose_write  short cards, then an inset panel with its own
                   left rule holding a field and a confirm

   In greyscale, on the same dark ground with the same light page, those five
   silhouettes are five different objects before a word is read -- which is law
   1 at the top of this file, applied one level down.

   LAW 3 STILL HOLDS INSIDE THE PAGE. Nothing here colours an option by what it
   says. --sc-arc marks selection and focus and nothing else, exactly as it does
   on a scale row: "this is the one you picked", never "this was the good one".
   --------------------------------------------------------------------------- */

/* The rule line: how to answer THIS card. Drawn for every geometry except
   `choose`, where four labelled buttons under a scene explain themselves and a
   line telling you to pick one would be noise on the commonest card there is. */
.story__rule {
  justify-self: start;
  margin: 0;
  padding: 6px 0 0;
  font-family: var(--mono);
  font-size: 0.8125rem;
  letter-spacing: 0.02em;
  line-height: 1.5;
  color: var(--muted);
}

/* -- binary: two ways, and both of them cost something --------------------- */
/*
   Two, side by side, equal in every dimension. The equality is the content:
   this geometry exists for the beat where neither way is the easy one, and a
   layout that made one of them wider or first-among-equals would answer the
   question for the person. They stay side by side down to the narrowest phone
   -- stacking them would make the top one read as the default. */
/* ONE FRAMED OBJECT SPLIT DOWN THE MIDDLE, not two cards that happen to be
   adjacent. That silhouette exists nowhere else in the product, which is what
   makes the geometry readable with the colour removed -- and it is also the
   picture the content is making: one decision, two ways out of it, and the
   divider is the thing you cannot get around.

   Drawn as a frame with a divider rather than as two bordered cards because a
   `choose` card with two options would otherwise look identical. */
.story__ways {
  display: grid;
  grid-template-columns: 1fr 1fr;
  align-items: stretch;
  gap: 0;
  border: 2px solid rgba(36, 23, 45, 0.5);
  border-radius: var(--r-card, 16px);
  background: var(--paper);
  overflow: hidden;
}

.screen--story .story__way {
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 12px;
  min-height: 9.5rem;
  padding: 18px clamp(14px, 2.4vw, 22px);
  border: 0;
  border-radius: 0;
  background: none;
  color: var(--night-plum);
  font-family: var(--body);
  font-size: clamp(1.0625rem, 1.8vw, 1.1875rem);
  font-weight: 500;
  line-height: 1.4;
  text-align: left;
  cursor: pointer;
  transition: background-color var(--t-quick, 180ms cubic-bezier(.2, 0, 0, 1));
}

.story__ways > .story__way + .story__way {
  border-left: 2px solid rgba(36, 23, 45, 0.5);
}

.screen--story .story__way:hover {
  background: var(--porcelain);
}

/* Selection carries a fill AND a glyph, never colour alone. */
.screen--story .story__way--chosen {
  background: var(--porcelain);
}

.screen--story .story__way--chosen .story__key {
  background: var(--sc-arc);
  color: var(--porcelain);
}

.screen--story .story__way--chosen::after {
  content: "\2713";
  margin-top: auto;
  color: var(--sc-arc);
  font-size: 1.1rem;
  line-height: 1;
}

/* -- order: put them in the order you'd actually do them -------------------- */
/*
   TWO REGIONS, AND BOTH OF THEM ARE NAMED IN WORDS.

   The previous version of this had the same two regions with a dashed rule
   between them and no label on either, and the comment here called the rule
   "the whole diagram". It was not: a rule is a line, and a line does not say
   what is above it. What the screen actually showed, with two of five placed,
   was a single column of numerals reading 1, 2 / 2, 4, 5 -- the placed rows
   numbered by rank and the pool rows numbered by keyboard key, in the same
   badge, at the same x. Nobody has to be confused by that on purpose.

   So: the built order carries a heading, the remaining options carry the
   question being asked of them, and the key badge moved to the far right where
   it cannot be read as a position. The rule stays, quiet, and only once there
   is something above it to separate.

   Nothing is pre-placed. A list that starts in the author's order records the
   author's order for everybody who agrees with the first thing they read, on
   the one screen whose point is finding out what YOURS is. */
.ordering {
  display: grid;
  gap: 12px;
}

/* "Your order so far" -- and "Your order" once there is no "so far" left. */
.ordering__built {
  margin: 0;
  font-family: var(--mono);
  font-size: 0.75rem;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--muted);
}

.ordering__placed {
  display: grid;
  gap: 8px;
  margin: 0;
  padding: 0;
  list-style: none;
  counter-reset: none;
}

.ordering__row {
  display: block;
}

/* THE QUESTION, and it is a question rather than a hint, so it is not drawn as
   fine print. This is the only instruction on the screen and it asks for one
   thing: "Which one is most true of you?", then "And the next most true?" It
   is the line that replaced two wrapped lines of standing procedure. */
.ordering__step {
  margin: 0;
  font-size: 1rem;
  font-weight: 600;
  line-height: 1.45;
  color: var(--night-plum);
}

/* The one-left state is the tallest the card ever gets: the built list is at
   its longest and the pool is a single row, already under its own floor, so
   `fitOrdering` has nothing left to squeeze. Measured at 375x812, the commit
   control sat 3-15px under the fold there and only there.

   So the step line goes, and only then, and only because at one-left there is
   no decision to word. The built order is never capped -- seeing what you have
   chosen is the whole point of the rebuild. */
.ordering__step--dropped {
  display: none;
}

/* The rule separates what you have said from what is left, so it belongs above
   the question -- and only once there is something above it. With nothing
   placed the question is the first thing on the block and a rule over it would
   be separating it from the heading. */
.ordering__placed:not([hidden]) ~ .ordering__step {
  padding-top: 14px;
  border-top: 1px dashed rgba(36, 23, 45, 0.28);
}

.ordering__pool {
  display: grid;
  gap: 8px;
}

/* The fold fix's cap (`fitOrdering`). It engages only when the commit control
   would otherwise sit under the fold, it relaxes as rows leave the pool, and
   it leaves the row it cuts through visibly cut -- the same cue a capped
   choose_write list gives, and one nobody has to be taught.

   `grid-auto-rows: max-content` IS THE LOAD-BEARING LINE, and it was found by
   measuring rather than by reading. A `max-height` on a grid is available space
   for track sizing, so `auto` rows shrink to fit it: the first version of this
   cap did not scroll AT ALL. Measured in Chrome at 375x667 with the pool capped
   to 270px, `grid-template-rows` came back "46px 48.66px 46px 48.66px 48.66px"
   -- the two-line statements squeezed from 66px to 49px, their text spilling
   past its own border -- and `scrollHeight` equalled `clientHeight`, so no
   scrollbar appeared and nothing told anybody there was more. `align-content:
   start` does not fix it; the track sizing does. With max-content rows the same
   270px cap reports scrollHeight 322 and scrolls, which is what a cap is
   supposed to mean. */
.ordering__pool--capped {
  grid-auto-rows: max-content;
  align-content: start;
  overflow-y: auto;
  scrollbar-width: thin;
  scroll-padding: 4px 0;
}

.screen .ordering__item {
  display: flex;
  width: 100%;
  align-items: center;
  gap: 12px;
  min-height: 52px;
  padding: 10px 14px;
  border: 2px solid rgba(36, 23, 45, 0.32);
  border-radius: var(--r-card, 16px);
  background: var(--paper);
  color: var(--night-plum);
  font-family: var(--body);
  font-size: 1.0625rem;
  font-weight: 500;
  line-height: 1.35;
  text-align: left;
  cursor: pointer;
  transition: border-color var(--t-quick, 180ms cubic-bezier(.2, 0, 0, 1)),
              background-color var(--t-quick, 180ms cubic-bezier(.2, 0, 0, 1));
}

.screen .ordering__item:hover {
  border-color: var(--sc-arc);
  background: var(--porcelain);
}

/* A placed row is filled, and its badge carries the rank rather than the key.
   The rank is a position in THEIR order and nothing else -- it is not a score,
   and no colour anywhere says one position is better than another. */
.screen .ordering__item--placed {
  border-color: var(--sc-arc);
  background: var(--porcelain);
}

.screen .ordering__rank,
.screen .ordering__key {
  flex: none;
  display: grid;
  place-items: center;
  font-family: var(--mono);
  line-height: 1;
}

/* THE KEY IS ON THE RIGHT, AND THAT IS THE WHOLE FIX FOR THE NUMERAL
   COLLISION. It is a keyboard accelerator, it is fixed for the life of the
   screen, and it is emphatically NOT this option's position in anything. On
   the left of a vertical list, in the slot a rank badge also uses, it read as
   one: with two of five placed the column ran 1, 2 / 2, 4, 5 and the gap where
   3 had been made the screen look broken. Drawn small, at the far end, with a
   keycap's border rather than a badge's fill, it reads as what it is. */
.screen .ordering__key {
  width: 22px;
  height: 22px;
  border: 1px solid rgba(36, 23, 45, 0.22);
  border-radius: 5px;
  background: none;
  color: var(--muted);
  font-size: 0.75rem;
}

.screen .ordering__rank {
  width: 26px;
  height: 26px;
  border-radius: 7px;
  background: var(--sc-arc);
  color: var(--porcelain);
  font-size: 0.8125rem;
}

/* The undo, on the row it undoes. It is drawn quietly and it is drawn ALWAYS,
   because a row in "your order" is exactly the case where there is something
   to take out -- and before there is one, nothing on the screen mentions
   undoing at all. That is the half of the old hint that was arriving before it
   could mean anything. */
.screen .ordering__undo {
  flex: none;
  font-family: var(--body);
  font-size: 0.8125rem;
  font-weight: 700;
  color: var(--muted);
  text-decoration: underline;
  text-underline-offset: 0.25em;
}

.ordering__text {
  flex: 1;
}

.ordering__actions {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 14px;
  margin-top: 4px;
}

.screen .ordering__reset {
  min-height: 44px;
  padding: 0;
  border: 0;
  background: none;
  color: var(--muted);
  font-family: var(--body);
  font-size: 0.9375rem;
  font-weight: 700;
  text-decoration: underline;
  text-underline-offset: 0.25em;
  cursor: pointer;
}

/* The commit. It is never disabled: an incomplete order is refused in a
   sentence that says how many are left, because a greyed-out button somebody
   keeps pressing tells them nothing at all. `--ready` is the outline filling
   in, so the control looks finishable exactly when it is. */
.screen .ordering__confirm,
.screen .picks__confirm,
.screen .say__confirm,
.screen .guess__confirm {
  justify-self: start;
  min-height: 50px;
  min-width: 10rem;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: 13px 26px;
  border: 2px solid var(--iris-600, #6242e8);
  border-radius: var(--r-pill, 999px);
  background: none;
  color: var(--iris-700, #5137df);
  font-family: var(--body);
  font-size: 1rem;
  font-weight: 700;
  line-height: 1;
  cursor: pointer;
  transition: background-color var(--t-quick, 180ms cubic-bezier(.2, 0, 0, 1)),
              color var(--t-quick, 180ms cubic-bezier(.2, 0, 0, 1));
}

.screen .ordering__confirm--ready,
.screen .picks__confirm:hover,
.screen .say__confirm:hover,
.screen .guess__confirm:hover,
.screen .ordering__confirm:hover {
  background: var(--iris-600, #6242e8);
  color: var(--paper);
}

/* RETIRED, AND KEPT. Both screens drew a standing hint paragraph above the
   control -- "Tap them in order, truest of you first. Tap one again to take it
   back out." -- and both now draw `.ordering__step` instead, which asks one
   question at a time. The classes are no longer emitted by `buildOrder` or
   `buildRank`; the rule stays so that a build which re-emits either (a rolled
   back client, a cached bundle) still renders a legible line rather than an
   unstyled one. */
.order__hint,
.rank__hint {
  margin: 0;
  font-size: 0.9375rem;
  line-height: 1.5;
  color: var(--muted);
}

/* -- choose_multi: pick several -------------------------------------------- */
/*
   Pills that wrap ragged, so the block never reads as a list of equals in a
   grid -- that shape belongs to `order`, where position means something. Here
   it does not, and the layout says so. */
.story__picks {
  display: flex;
  flex-wrap: wrap;
  gap: 10px;
}

.screen--story .story__pick {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  min-height: 48px;
  padding: 10px 16px;
  border: 2px solid rgba(36, 23, 45, 0.32);
  border-radius: var(--r-pill, 999px);
  background: var(--paper);
  color: var(--night-plum);
  font-family: var(--body);
  font-size: 1rem;
  font-weight: 500;
  line-height: 1.3;
  text-align: left;
  cursor: pointer;
  transition: border-color var(--t-quick, 180ms cubic-bezier(.2, 0, 0, 1)),
              background-color var(--t-quick, 180ms cubic-bezier(.2, 0, 0, 1));
}

.screen--story .story__pick:hover {
  border-color: var(--sc-arc);
  background: var(--porcelain);
}

.screen--story .story__pick--on {
  border-color: var(--sc-arc);
  background: var(--porcelain);
}

.screen--story .story__pick--on .story__key {
  background: var(--sc-arc);
  color: var(--porcelain);
}

/* A check glyph as well as the fill, because selection may never be carried by
   colour alone -- the same rule the scale's chosen row and the ask card's
   chosen chip already follow. */
.screen--story .story__pick--on::after {
  content: "\2713";
  margin-left: 2px;
  color: var(--sc-arc);
  font-size: 1rem;
  line-height: 1;
}

.picks__count {
  justify-self: start;
  margin: 0;
  font-family: var(--mono);
  font-size: 0.8125rem;
  letter-spacing: 0.04em;
  color: var(--muted);
}

/* -- write: no options at all ---------------------------------------------- */
/*
   The one story card with no buttons above the fold. The field is the same
   object as the words screen's -- a rule, not a box -- so somebody who has met
   one recognises the other, but it sits on the story page rather than on white,
   because this is still the scene talking. */
.write__prompt {
  margin: 0;
  font-family: var(--body);
  font-size: clamp(1.25rem, 2.6vw, 1.5rem);
  font-weight: 700;
  line-height: 1.3;
  text-wrap: pretty;
  color: var(--night-plum);
}

.screen--story .write__field,
.screen--story .say__prompt + .write__field {
  display: block;
  width: 100%;
  min-height: 3.4em;
  padding: 6px 2px 10px;
  border: 0;
  border-bottom: 1.5px solid rgba(36, 23, 45, 0.5);
  border-radius: 0;
  background: none;
  color: var(--night-plum);
  font-family: var(--body);
  font-size: clamp(1.0625rem, 2.2vw, 1.25rem);
  line-height: 1.5;
  resize: none;
  overflow: hidden;
  field-sizing: content;
}

.screen--story .write__field:hover {
  border-bottom-color: var(--night-plum);
}

.screen--story .write__field:focus,
.screen--story .write__field:focus-visible {
  border-bottom-color: var(--sc-arc);
  outline: none;
}

.write__guide,
.say__guide {
  margin: 0;
  font-size: 0.9375rem;
  line-height: 1.5;
  color: var(--muted);
}

/* The two permanent promises (`screens.py::WORDS_NOTES`): what happens to the
   sentence, and who can see it. Darker than the guidance above them on purpose
   -- guidance is advice and can be skimmed, these are commitments and are the
   reason somebody is willing to type at all. Shown at the moment of answering,
   never in a policy page. */
.write__note {
  margin: 0;
  max-width: 34rem;
  font-size: 0.9375rem;
  line-height: 1.55;
  color: var(--night-plum);
}

.write__actions {
  display: flex;
  flex-wrap: wrap;
  gap: 12px;
  margin-top: clamp(6px, 1.6vw, 14px);
}

/* Same size, same height, same weight of presence, for the same reason the
   words screen weights them that way: a writing screen somebody cannot leave
   is an abandonment point. Fill versus outline is affordance, not emphasis. */
.screen--story .write__skip,
.screen--story .write__done {
  min-height: 50px;
  min-width: 9rem;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: 13px 26px;
  border: 2px solid var(--iris-600, #6242e8);
  border-radius: var(--r-pill, 999px);
  font-family: var(--body);
  font-size: 1rem;
  font-weight: 700;
  line-height: 1;
  cursor: pointer;
  transition: background-color var(--t-quick, 180ms cubic-bezier(.2, 0, 0, 1)),
              color var(--t-quick, 180ms cubic-bezier(.2, 0, 0, 1));
}

.screen--story .write__skip {
  background: none;
  color: var(--iris-700, #5137df);
}

.screen--story .write__done {
  background: var(--iris-600, #6242e8);
  color: var(--paper);
}

.screen--story .write__skip:hover {
  background: var(--iris-050, #eeeaff);
}

.screen--story .write__done:hover {
  background: var(--iris-700, #5137df);
  border-color: var(--iris-700, #5137df);
}

/* -- choose_write: pick one, then say why ---------------------------------- */
/*
   The choices are shorter than a plain `choose` card's, because they are half
   the answer rather than all of it, and the panel underneath carries its own
   left rule so it reads as a second movement of the same screen instead of a
   second screen. The box is present from the first frame: a field that appears
   under the thumb after a tap is how a mis-tap happens, and seeing it early is
   what warns somebody the card is going to ask them to write.

   PRESENT IS NOT THE SAME AS VISIBLE, AND THAT WAS THE DEFECT. Measured live at
   1100x720 on session 36FGWKHN33DGR8214NMSFMTR4D: page 1203 tall, scrollY 0 on
   arrival, `.write__field` top at 736 and the Done button at 938. The box the
   paragraph above insists on was in the document and under the fold, which is
   the same thing as absent for anybody who does not scroll a page that looks
   finished. Everything from here to the end of this subsection exists to put
   the field and its commit control on screen the moment the card arrives, at
   1100x720 and on a 375x812 phone, and it is measured rather than eyeballed:
   `tests/frontend.test.mjs` pins the mechanism and the walk pins the pixels.

   Three moves, in the order they do work:

     1. The card is denser than a `choose` card, because it carries two
        movements rather than one. Nothing is dropped -- not the scene, not the
        rule line, not the two promises, not the honesty chip.
     2. Above 52rem the two movements sit SIDE BY SIDE. Pick on the left, say
        why on the right; the say panel's own left rule becomes the divider
        between them. That alone is the whole of the desktop fix: 1100x720 goes
        from a 1203px page to Done at 628 with 92px to spare.
     3. Below that there is only one column, and on the shortest phones the
        options list is given a cap by `assess.mjs::fitSayCard` -- computed from
        the card's own measured height, never a guess, and set to nothing at all
        whenever the card already fits. A capped list scrolls; the option card it
        cuts through is left visibly cut, which is the cue. */
.screen--story.story--say .story__choices {
  grid-template-columns: repeat(auto-fit, minmax(13rem, 1fr));
}

/* 48px is the touch floor (spec 4.8) and this is the one place on a story card
   where the temptation to go under it is real. It does not go under it. */
.screen--story.story--say .story__choice {
  min-height: 48px;
  padding: 10px 12px;
  font-size: 1rem;
  line-height: 1.32;
  align-items: center;
}

.screen--story.story--say {
  gap: clamp(8px, 1.4vw, 14px);
}

/* Smaller than a `choose` card's scene, and only here: this scene shares its
   card with a second movement, which the others do not. */
.screen--story.story--say .story__body {
  font-size: clamp(1.0625rem, 2.2vw, 1.375rem);
  line-height: 1.32;
}

/* The ask shrinks with the scene it follows, and stays bold. */
.screen--story.story--say .story__ask {
  font-size: clamp(1.0625rem, 2.2vw, 1.25rem);
}

/* The two halves of the answer, in one box so they can be put side by side. */
.story__answer {
  display: grid;
  gap: 12px;
  min-width: 0;
}

.screen--story.story--say .story__choices,
.screen--story.story--say .story__say {
  min-width: 0;
}

.story__say {
  display: grid;
  gap: 8px;
  padding-left: clamp(12px, 2vw, 18px);
  border-left: 3px solid var(--sc-arc);
}

.say__prompt {
  margin: 0;
  font-family: var(--body);
  font-size: clamp(0.9375rem, 1.6vw, 1.125rem);
  font-weight: 700;
  line-height: 1.3;
  color: var(--night-plum);
}

.screen--story.story--say .write__field {
  min-height: 2.5em;
  font-size: clamp(1rem, 1.8vw, 1.125rem);
}

.screen--story.story--say .say__guide,
.screen--story.story--say .write__note {
  font-size: 0.8125rem;
  line-height: 1.4;
}

/* -- wide enough for two columns ------------------------------------------- */

@media (min-width: 52rem) {
  .screen--story.story--say .story__answer {
    grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
    align-items: start;
    column-gap: 22px;
  }

  /* One option per row inside its own column: at half the measure a two-up
     grid makes 165px cards that wrap a 60-character label onto six lines. */
  .screen--story.story--say .story__choices {
    grid-template-columns: minmax(0, 1fr);
  }
}

/* -- one column: the phone ------------------------------------------------- */
/*
   Stacked, so the card is denser again. Every reduction here was measured
   against the two longest real cards in the corpus (`close::P-B2-DOOR`, 294
   characters of scene, and `change::C-B4-FADED`, a 169-character prompt) at
   375x812. */

@media (max-width: 52rem) {
  .screen--story.story--say {
    --page-inset: 8px;
    --page-pad: 12px;
    gap: 6px;
  }

  .screen--story.story--say .story__tag {
    font-size: 0.75rem;
  }

  .screen--story.story--say .story__body {
    font-size: 1.0625rem;
    line-height: 1.28;
  }

  .screen--story.story--say .story__ask {
    font-size: 1.0625rem;
    line-height: 1.28;
  }

  .screen--story.story--say .story__rule {
    font-size: 0.75rem;
  }

  .screen--story.story--say .story__answer {
    gap: 7px;
  }

  .screen--story.story--say .story__choices {
    gap: 5px;
  }

  .screen--story.story--say .story__choice {
    padding: 8px 11px;
    font-size: 0.9375rem;
    line-height: 1.28;
  }

  .screen--story.story--say .story__key {
    width: 20px;
    height: 20px;
    font-size: 0.6875rem;
  }

  .screen--story.story--say .story__say {
    gap: 5px;
    padding-left: 10px;
  }

  .screen--story.story--say .say__prompt {
    font-size: 0.9375rem;
    line-height: 1.26;
  }

  .screen--story.story--say .write__field {
    min-height: 2.2em;
    font-size: 1rem;
    padding: 4px 2px 6px;
  }

  .screen--story.story--say .say__guide,
  .screen--story.story--say .write__note {
    font-size: 0.75rem;
    line-height: 1.35;
  }

  .screen--story.story--say .say__confirm {
    min-height: 48px;
    min-width: 8rem;
    padding: 10px 18px;
  }
}

/* -- the cap, when the card still will not fit ----------------------------- */
/*
   `max-height` is set in pixels by `assess.mjs::fitSayCard` through CSSOM, and
   only when the measured card would otherwise push Done past the fold. The
   class is set with it, so the two can never disagree: no class, no cap.

   `overscroll-behavior` is deliberately NOT `contain`. A 200px scroller across
   nearly the full width of a phone would otherwise swallow the flick that was
   meant to scroll the page, and being unable to reach the rest of a card is a
   worse trap than the one this is fixing. Chaining is the default; it stays. */
.story__choices--capped {
  overflow-y: auto;
  scrollbar-width: thin;
  scroll-padding: 4px 0;
}

/* ---------------------------------------------------------------------------
   6c · THE OPENING BLOCK -- the two self-placement screens

   They sit before the Spine starts, and everything about their drawing says
   so: paper edge to edge, no set colour on the ground (there is no set yet to
   be in), no card. The order somebody gives here is what the report puts
   beside the order their answers gave, and neither is treated as the true
   one -- so nothing in this section ranks, scores or valences anything. The
   numbers on a placed row are positions in the person's own list, drawn
   identically at every position, and the five steps of the guess are equal.

   The ordering control's base rules live in section 6b (shared with the
   in-flight story `order` geometry). This section restyles it for the rank
   screen only, in the ink: plum edges, plum ranks, the primary button when
   the order is complete.

   In greyscale: paper to all four edges, no card, one numbered stack (rank)
   or one ladder of five equal steps under an edged note (guess).
   --------------------------------------------------------------------------- */

.screen--rank,
.screen--guess {
  --sc-focus-ground: var(--paper);
  --sc-focus-ink: var(--night-plum);
  --sc-measure: 40rem;
  background: var(--paper);
  gap: 12px;
}

.rank__eyebrow,
.guess__eyebrow {
  margin: 0;
  font-family: var(--body);
  font-size: var(--fs-xs, 0.9375rem);
  font-weight: 700;
  letter-spacing: 0;
  text-transform: none;
  color: var(--muted);
}

.rank__head,
.guess__question {
  margin: 0;
  font-family: var(--display);
  font-optical-sizing: auto;
  font-size: clamp(1.625rem, 4vw, 2.125rem);
  font-weight: 700;
  line-height: 1.08;
  letter-spacing: -0.03em;
  text-wrap: balance;
  color: var(--night-plum);
}

.rank__sub {
  margin: 0;
  font-size: var(--fs-body, 1.0625rem);
  line-height: 1.5;
  color: var(--night-plum);
}

/* -- the ordering control, on the rank screen --------------------------- */

.screen--rank .ordering__built {
  font-family: var(--body);
  font-size: var(--fs-xs, 0.9375rem);
  font-weight: 700;
  letter-spacing: 0;
  text-transform: none;
  color: var(--muted);
}

.screen--rank .ordering__placed:not([hidden]) ~ .ordering__step {
  border-top-color: var(--sc-rule);
  border-top-style: solid;
}

.screen--rank .ordering__item {
  min-height: 48px;
  padding: 8px 12px;
  border: 2px solid var(--sc-edge-light);
  border-radius: var(--r-control, 8px);
  background: var(--paper);
  font-size: var(--fs-body, 1.0625rem);
  font-weight: 400;
  line-height: 1.3;
}

.screen--rank .ordering__item:hover {
  border-color: var(--night-plum);
  background: var(--paper);
}

.screen--rank .ordering__item--placed {
  border-color: var(--night-plum);
  background: var(--porcelain);
}

.screen--rank .ordering__rank {
  width: 32px;
  height: 32px;
  border-radius: 6px;
  background: var(--night-plum);
  color: var(--porcelain);
  font-family: var(--body);
  font-size: var(--fs-xs, 0.9375rem);
  font-weight: 700;
  font-variant-numeric: tabular-nums;
}

.screen--rank .ordering__key {
  width: 32px;
  height: 32px;
  border: 1px solid var(--sc-edge-light);
  border-radius: 6px;
  background: var(--porcelain);
  color: var(--night-plum);
  font-family: var(--body);
  font-size: var(--fs-xs, 0.9375rem);
  font-weight: 700;
  font-variant-numeric: tabular-nums;
}

.screen--rank .ordering__undo {
  color: var(--night-plum);
  text-decoration-thickness: 2px;
  text-underline-offset: 0.15em;
}

.screen--rank .ordering__reset {
  color: var(--night-plum);
  text-decoration-thickness: 2px;
  text-underline-offset: 0.15em;
}

/* The commit: outlined until the order is complete, then the primary. */
.screen--rank .ordering__confirm,
.screen--guess .guess__confirm {
  justify-self: start;
  min-height: 56px;
  min-width: 10rem;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: 12px 24px;
  border: 2px solid var(--night-plum);
  border-radius: var(--r-control, 8px);
  background: var(--paper);
  color: var(--night-plum);
  font-family: var(--body);
  font-size: var(--fs-body, 1.0625rem);
  font-weight: 700;
  line-height: 1.2;
  cursor: pointer;
  transition: background-color var(--t-state, 150ms cubic-bezier(.2, 0, 0, 1)),
              border-color var(--t-state, 150ms cubic-bezier(.2, 0, 0, 1)),
              color var(--t-state, 150ms cubic-bezier(.2, 0, 0, 1)),
              transform var(--t-press, 100ms cubic-bezier(.4, 0, 1, 1));
}

.screen--rank .ordering__confirm--ready,
.screen--rank .ordering__confirm:hover,
.screen--guess .guess__confirm {
  background: var(--night-plum);
  color: var(--porcelain);
}

.screen--rank .ordering__confirm--ready:hover,
.screen--guess .guess__confirm:hover {
  background: var(--plum-lift, #3a2a45);
  border-color: var(--plum-lift, #3a2a45);
  color: var(--porcelain);
}

.screen--rank .ordering__confirm:active,
.screen--guess .guess__confirm:active {
  transform: translateY(1px);
}

/* -- the guess ----------------------------------------------------------- */

/* Their own words, quoted back by the server: a quotation, not a finding. */
.guess__lead {
  margin: 0;
  font-family: var(--body);
  font-size: var(--fs-body, 1.0625rem);
  font-style: italic;
  line-height: 1.5;
  color: var(--muted);
}

/* Five equal steps. EQUAL is the requirement: a self-report of where somebody
   thinks they sit, with no norm sample to say otherwise. */
.guess__ladder {
  display: grid;
  gap: 8px;
}

.screen .guess__opt {
  display: flex;
  width: 100%;
  align-items: center;
  gap: 12px;
  min-height: 48px;
  padding: 8px 12px;
  border: 2px solid var(--sc-edge-light);
  border-radius: var(--r-control, 8px);
  background: var(--paper);
  color: var(--night-plum);
  font-family: var(--body);
  font-size: var(--fs-body, 1.0625rem);
  font-weight: 400;
  line-height: 1.3;
  text-align: left;
  cursor: pointer;
  transition: border-color var(--t-state, 150ms cubic-bezier(.2, 0, 0, 1)),
              background-color var(--t-state, 150ms cubic-bezier(.2, 0, 0, 1));
}

.screen .guess__opt:hover {
  border-color: var(--night-plum);
}

.screen .guess__opt--on {
  border-color: var(--night-plum);
  background: var(--porcelain);
}

.screen .guess__opt--on::after {
  content: "\2713";
  margin-left: auto;
  color: var(--night-plum);
  font-size: 1.1rem;
  line-height: 1;
}

.screen .guess__key {
  flex: none;
  width: 32px;
  height: 32px;
  display: grid;
  place-items: center;
  border: 1px solid var(--sc-edge-light);
  border-radius: 6px;
  background: var(--porcelain);
  color: var(--night-plum);
  font-family: var(--body);
  font-size: var(--fs-xs, 0.9375rem);
  font-weight: 700;
  font-variant-numeric: tabular-nums;
  line-height: 1;
}

.screen .guess__opt--on .guess__key {
  border-color: var(--night-plum);
  background: var(--night-plum);
  color: var(--porcelain);
}

.guess__label {
  flex: 1;
}

/* The note. Permanent: the product admitting, before the person answers,
   that it cannot answer this one yet. Edged rather than muted-to-invisible,
   because it is the most load-bearing sentence on the screen. */
.guess__note {
  margin: 0;
  padding: 12px 16px;
  border: 1px solid var(--sc-edge-light);
  border-radius: var(--r-control, 8px);
  background: var(--paper);
  font-size: var(--fs-xs, 0.9375rem);
  line-height: 1.5;
  color: var(--night-plum);
}

/* -- the two-tap placement, on the question card ------------------------ */
/*
   `max_picks: 2` (PLAN-assess.md 6.7, DESIGN-assess.md 5). The host carries
   `screen--rank screen--picks`; section 4's frame gives it the ink, and this
   block re-says the ground and the ring because `.screen--rank` above
   paints paper. The question is the headline, in porcelain at the stem's
   step; the five lines are the cards' white rows in place; the pick names
   replace the keycap on a picked row; the one line sits under the rows in
   porcelain; the confirm is outlined in porcelain until both are picked,
   then filled. */
.screen--picks {
  --sc-measure: 46rem;
  --sc-focus-ground: var(--sc-arc);
  --sc-focus-ink: var(--porcelain);
  gap: 8px;
  color: var(--porcelain);
  background-color: var(--sc-arc);
}

.screen--picks .rank__eyebrow {
  color: var(--porcelain);
}

/* The question, in the stem's step; two lines reserved so the rows do not
   move between "most" and "least". */
.screen--picks .rank__head {
  font-family: var(--body);
  font-size: clamp(1.75rem, 7.4vw, 2.25rem);
  font-weight: 400;
  line-height: 1.25;
  letter-spacing: 0;
  min-height: 2.5em;
  color: var(--porcelain);
}

.screen--picks .rank__sub {
  margin: 0;
  font-size: var(--fs-xs, 0.9375rem);
  line-height: 1.5;
  color: var(--porcelain);
}

.screen--picks .ordering {
  gap: 8px;
}

.screen--picks .ordering__item {
  min-height: 52px;
  padding: 6px 12px;
  border: 2px solid var(--sc-edge-light);
  border-radius: var(--r-control, 8px);
  background: var(--paper);
  color: var(--night-plum);
  font-size: var(--fs-body, 1.0625rem);
  font-weight: 400;
  line-height: 1.3;
}

.screen--picks .ordering__item:hover {
  border-color: var(--night-plum);
}

/* Most like you: the white row takes the set's field tint, edge and all
   (the preview's "taken" row; plum on it 12.58:1). Least like you: the set
   colour's edge and ring on the white row, the chosen mark. Both carry the
   name as a pill where the keycap was, so neither is colour alone. */
.screen--picks .ordering__item--placed {
  border-color: var(--night-plum);
  background: var(--paper);
}

.screen--picks .ordering__item--most {
  border-color: var(--sc-field);
  background: var(--sc-field);
}

.screen--picks .ordering__item--least {
  border-color: var(--sc-arc);
  box-shadow: inset 0 0 0 1px var(--sc-arc);
}

/* THE SLOT: keycap and pill share one fixed 52px column at the row's start,
   so the statement starts at the same x whether its row is picked or not
   (review round 2, DR2-4). The keycap stays 32px, at the slot's left, with
   the rest of the slot as margin; the pill fills the slot with its word
   centred. */
.screen--picks .ordering__key {
  width: 32px;
  height: 32px;
  margin-right: 20px;
  border: 1px solid var(--sc-edge-light);
  border-radius: 6px;
  background: var(--sc-field);
  color: var(--night-plum);
}

/* "Most" / "Least" in the 32px keycap slot: a short pill, so a picked row
   keeps the two lines an unpicked one has (review round 1, DR-A1). The full
   name ("Most like you") is the row's accessible name. */
.screen--picks .ordering__rank {
  box-sizing: border-box;
  width: 52px;
  min-width: 52px;
  height: 32px;
  padding: 0 4px;
  border-radius: var(--r-pill, 999px);
  background: var(--night-plum);
  color: var(--porcelain);
  font-size: 0.8125rem;
  white-space: nowrap;
}

.screen--picks .ordering__actions {
  margin-top: 4px;
}

/* "Start again", on the ink. */
.screen--picks .ordering__reset {
  color: var(--porcelain);
}

/* The confirm, on the ink: outlined in porcelain until both are picked,
   then the porcelain fill with plum words -- the chapter card's primary. A
   plum fill would be under 3:1 against the ink as a shape. */
.screen--picks .ordering__confirm {
  border-color: var(--porcelain);
  background: none;
  color: var(--porcelain);
}

.screen--picks .ordering__confirm--ready,
.screen--picks .ordering__confirm:hover,
.screen--picks .ordering__confirm--ready:hover {
  border-color: var(--porcelain);
  background: var(--porcelain);
  color: var(--night-plum);
}

.screen--picks .ordering__confirm--ready:hover {
  border-color: var(--porcelain-lift, #e9e9e2);
  background: var(--porcelain-lift, #e9e9e2);
}

/* ---------------------------------------------------------------------------
   7 · S4 · CONSEQUENCE BEAT — 1.2s, skippable by any key or tap.

   The chosen clause rises to the centre in display type; a hairline draws
   beneath it; the next story's opening fragment fades in below.

   CONTENT RULE, ABSOLUTE: the beat may only RESTATE the choice, never interpret
   it. "You'd say it out loud." is the person's own answer read back. "You're
   direct." is a trait claim from an uncalibrated item. CSS cannot enforce that
   -- it is enforced in the content module -- but this is where it would show.

   In greyscale: dark to all four edges, centred, and the only screen in the
   product with no control of any kind on it.
   --------------------------------------------------------------------------- */

.screen--beat {
  --sc-focus-ground: var(--sc-arc);
  --sc-focus-ink: var(--porcelain);
  justify-items: center;
  gap: clamp(16px, 3vw, 28px);
  text-align: center;
  color: var(--porcelain);
  background-color: var(--sc-arc);
  background-image:
    radial-gradient(110% 80% at 50% 50%,
      transparent 10%, rgba(36, 23, 45, 0.55) 100%);
  cursor: pointer;
  animation: ground-in var(--t-scene, 520ms cubic-bezier(.65, 0, .35, 1)) both;
}

.beat__line {
  margin: 0;
  max-width: 34rem;
  font-family: var(--display);
  font-size: clamp(1.75rem, 5vw, 3rem);
  font-weight: 700;
  line-height: 1.05;
  letter-spacing: -0.035em;
  text-wrap: balance;
  color: var(--porcelain);
  animation: beat-rise var(--t-beat, 900ms cubic-bezier(.16, 1, .3, 1)) both;
}

.beat__rule {
  width: min(12rem, 40%);
  height: 1px;
  margin: 0;
  border: 0;
  background: rgba(247, 247, 242, 0.7);
  transform-origin: center;
  animation: beat-draw var(--t-scene, 520ms cubic-bezier(.65, 0, .35, 1)) 260ms both;
}

.beat__next {
  margin: 0;
  max-width: 30rem;
  font-size: 1.0625rem;
  line-height: 1.5;
  color: var(--porcelain);
  opacity: 0.86;
  animation: beat-fade var(--t-page, 320ms cubic-bezier(.22, 1, .36, 1)) 520ms both;
}

/* ---------------------------------------------------------------------------
   8 · S5 · ASK — "You tell us": a direct question with its own answers.

   The question card's frame (section 4: the set's ink, no page, the footer
   in the flow), the question in porcelain at the stem's step, the rule line
   in porcelain, the chips as white pills with plum words, a REQUIRED
   confirm because a multi-select has no natural terminator. Ask cards are
   interleaved with the measured items in a set, so the same frame says no
   set change happened.

   In greyscale: the scale's dark frame with light pills that flow several
   to a row at ragged widths. The ragged rows are the tell -- the cards are
   five equal full-width rows and can never be confused with this.
   --------------------------------------------------------------------------- */

.screen--ask {
  gap: 8px;
}

/* THE FOLD AT 375x812 (PLAN-assess 5.5: the confirm inside 640). The ask
   card has no "I…" row, so its question is the first thing on the card; the
   longest card (D9: a three-line question, a two-line rule, six one-a-row
   chips plus "None of these" / "Something else…") measured its confirm at
   653 with 24px above the question and 4px margins on the rule and the
   confirm. 16px above, and the card's own 8px rhythm between every part:
   637. */
@media (max-width: 767px) {
  .screen--ask {
    padding-top: 16px;
  }
}

.ask__question {
  margin: 0;
  font-family: var(--body);
  font-size: clamp(1.375rem, 5.9vw, 2rem);
  font-weight: 400;
  line-height: 1.25;
  text-wrap: balance;
  color: var(--porcelain);
}

/* "Pick one. Or none. Not scored. Your report quotes it back." Stated
   plainly above the chips -- never as placeholder text and never as a
   tooltip. Porcelain on the ink, full strength (no alpha on the ink). */
.ask__rule {
  margin: 0;
  font-family: var(--body);
  font-size: var(--fs-xs, 0.9375rem);
  line-height: 1.5;
  color: var(--porcelain);
}

.ask__chips {
  display: flex;
  flex-wrap: wrap;
  /* 8px is the minimum gap between adjacent targets (spec 4.8, Touch); the
     picked chip's ring is inset, so the gap never closes. */
  gap: 8px;
}

/* A white pill with plum words: the answer object of the frame, the same
   paper and edge as the cards' rows. */
.screen--ask .ask__chip {
  /* The picked tick is drawn in the corner, out of the flow (below). */
  position: relative;
  min-height: 44px;
  display: inline-flex;
  align-items: center;
  gap: 8px;
  padding: 8px 16px;
  border: 2px solid var(--sc-edge-light);
  border-radius: var(--r-pill, 999px);
  background: var(--paper);
  color: var(--night-plum);
  font-family: var(--body);
  font-size: 1rem;
  font-weight: 400;
  line-height: 1.25;
  text-align: left;
  cursor: pointer;
  transition: border-color var(--t-state, 150ms cubic-bezier(.2, 0, 0, 1)),
              background-color var(--t-state, 150ms cubic-bezier(.2, 0, 0, 1)),
              transform var(--t-press, 100ms cubic-bezier(.4, 0, 1, 1));
}

.screen--ask .ask__chip:hover {
  border-color: var(--night-plum);
}

.screen--ask .ask__chip:active {
  transform: translateY(1px);
}

/* Picked = the set colour's edge + ring + a glyph, the same mark a chosen
   row carries, on the field tint -- a fill of its own, so the picked chip
   reads as taken beside the white ones (review round 1, DR-A5). Plum on the
   field is 12.58:1 at worst; the arc edge on the field 4.89:1. Two channels
   besides the fill, so it never depends on hue alone. */
.screen--ask .ask__chip--on {
  border-color: var(--sc-arc);
  box-shadow: inset 0 0 0 1px var(--sc-arc);
  background: var(--sc-field);
}

/* The tick: a small plum disc on the chip's top-right corner, OUT OF THE
   FLOW, the way the steps' second-row cards carry theirs. In the flow it
   made a picked chip about 20px wider, so the rows reflowed under the
   finger and "Something else…" dropped to a row of its own (review round
   2, DR2-2). A pick never changes a chip's width. The disc's centre sits on
   the pill's curve and it reaches 4px into the 8px gap, so it never touches
   a neighbour; the paper ring parts it from the chip's edge and from the
   ink. */
.screen--ask .ask__chip--on::before {
  content: "\2713";
  position: absolute;
  top: -4px;
  right: -4px;
  width: 20px;
  height: 20px;
  box-sizing: border-box;
  display: grid;
  place-items: center;
  border: 2px solid var(--paper);
  border-radius: var(--r-pill, 999px);
  background: var(--night-plum);
  color: var(--porcelain);
  font-size: 0.6875rem;
  font-weight: 700;
  line-height: 1;
  pointer-events: none;
}

/* "Something else…" opens one field under the chips: a white field with a
   porcelain edge on the ink, so it reads as one of the answers rather than
   a form. */
.ask__other {
  width: 100%;
  max-width: 26rem;
  min-height: 48px;
  margin-top: 4px;
  padding: 8px 12px;
  border: 2px solid var(--porcelain);
  border-radius: var(--r-control, 8px);
  background: var(--paper);
  color: var(--night-plum);
  font-family: var(--body);
  font-size: 1.0625rem;
  line-height: 1.5;
  caret-color: var(--night-plum);
}

/* "Type it in a few words." -- the quiet ink on the white field, 12.7:1. */
.ask__other::placeholder {
  color: var(--sc-quiet);
  opacity: 1;
}

.ask__other:hover {
  border-color: var(--night-plum);
}

/* The confirm on the ink: the porcelain fill with plum words, the chapter
   card's primary and the preview's. A plum fill would be under 3:1 against
   the ink as a shape. */
.ask__confirm {
  min-height: 56px;
  justify-self: start;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  margin-top: 0;
  padding: 12px 24px;
  border: 2px solid var(--porcelain);
  border-radius: var(--r-control, 8px);
  background: var(--porcelain);
  box-shadow: none;
  color: var(--night-plum);
  font-family: var(--body);
  font-size: var(--fs-body, 1.0625rem);
  font-weight: 700;
  line-height: 1.2;
  cursor: pointer;
  transition: background-color var(--t-state, 150ms cubic-bezier(.2, 0, 0, 1)),
              border-color var(--t-state, 150ms cubic-bezier(.2, 0, 0, 1)),
              transform var(--t-press, 100ms cubic-bezier(.4, 0, 1, 1));
}

.ask__confirm:hover {
  background: var(--porcelain-lift, #e9e9e2);
  border-color: var(--porcelain-lift, #e9e9e2);
}

.ask__confirm:active {
  transform: translateY(1px);
}


/* ---------------------------------------------------------------------------
   9 · WORDS -- free text. The calmest screen in the product.

   Paper, edge to edge. No tint, no set colour: the ABSENCE of colour is the
   signal. The field is a ruled line, not a box, and it grows. Guidance, not
   a limit: no counter, no minimum. Skip and Save are the same size and the
   same weight -- a forced writing screen is an abandonment point.

   In greyscale: paper to all four edges, no card, one full-width rule.
   --------------------------------------------------------------------------- */

.screen--words {
  --sc-focus-ground: var(--paper);
  --sc-focus-ink: var(--night-plum);
  --sc-measure: 38rem;
  background: var(--paper);
  gap: 16px;
}

.words__prompt {
  margin: 0;
  font-family: var(--display);
  font-optical-sizing: auto;
  font-size: clamp(1.625rem, 4vw, 2.125rem);
  font-weight: 700;
  line-height: 1.08;
  letter-spacing: -0.03em;
  text-wrap: balance;
  color: var(--night-plum);
}

.words__field {
  display: block;
  width: 100%;
  min-height: 3.4em;
  padding: 6px 2px 10px;
  border: 0;
  border-bottom: 2px solid var(--sc-edge-light);
  border-radius: 0;
  background: none;
  color: var(--night-plum);
  font-family: var(--body);
  font-size: clamp(1.125rem, 2.6vw, 1.375rem);
  line-height: 1.5;
  resize: none;
  overflow: hidden;
  field-sizing: content;
  caret-color: var(--night-plum);
  transition: border-color var(--t-state, 150ms cubic-bezier(.2, 0, 0, 1));
}

.words__field::placeholder {
  color: var(--muted);
  opacity: 1;
}

/* The rule is the whole affordance, so it answers the pointer and the caret.
   Only the colour changes; a rule that thickens on focus moves the lines
   beneath it. */
.words__field:hover,
.screen--words .words__field:focus {
  border-bottom-color: var(--night-plum);
}

.words__guide {
  margin: 0;
  font-size: var(--fs-body, 1.0625rem);
  line-height: 1.5;
  color: var(--muted);
}

/* the worked examples */
.words__example {
  margin: -8px 0 0;
  font-family: var(--body);
  font-size: var(--fs-xs, 0.9375rem);
  line-height: 1.5;
  color: var(--muted);
}

/* The two permanent lines: where this text goes, and who can see it. Plum,
   because they are the promise, not a footnote. */
.words__note {
  margin: 0;
  max-width: 34rem;
  font-size: var(--fs-xs, 0.9375rem);
  line-height: 1.55;
  color: var(--night-plum);
}

.words__actions {
  display: flex;
  flex-wrap: wrap;
  gap: 12px;
  margin-top: 8px;
}

/* Same size, same height, same presence. Fill versus outline is affordance,
   not emphasis. */
.screen--words .words__skip,
.screen--words .words__done {
  min-height: 56px;
  min-width: 9.5rem;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: 12px 24px;
  border: 2px solid var(--night-plum);
  border-radius: var(--r-control, 8px);
  box-shadow: none;
  font-family: var(--body);
  font-size: var(--fs-body, 1.0625rem);
  font-weight: 700;
  line-height: 1.2;
  cursor: pointer;
  transition: background-color var(--t-state, 150ms cubic-bezier(.2, 0, 0, 1)),
              border-color var(--t-state, 150ms cubic-bezier(.2, 0, 0, 1)),
              color var(--t-state, 150ms cubic-bezier(.2, 0, 0, 1)),
              transform var(--t-press, 100ms cubic-bezier(.4, 0, 1, 1));
}

.screen--words .words__skip {
  background: var(--paper);
  color: var(--night-plum);
}

.screen--words .words__skip:hover {
  background: var(--porcelain);
}

.screen--words .words__done {
  background: var(--night-plum);
  color: var(--porcelain);
}

.screen--words .words__done:hover {
  background: var(--plum-lift, #3a2a45);
  border-color: var(--plum-lift, #3a2a45);
}

.screen--words .words__skip:active,
.screen--words .words__done:active {
  transform: translateY(1px);
}

/* ---------------------------------------------------------------------------
   10 · SHARED FURNITURE -- the footer every screen carries

   The footer, the skip menu and the message line are named by the client;
   these selectors match what site/assess.mjs emits.
   --------------------------------------------------------------------------- */

.screen__footer,
.screen footer,
.question-footer {
  display: flex;
  align-items: center;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: 12px;
  margin-top: 12px;
  font-size: var(--fs-xs, 0.9375rem);
}

/* Back exists on every screen kind; a changed answer is stored beside the
   first rather than prevented. Both are text links in the ink. */
.screen .screen__back,
.screen .screen__skip {
  min-height: 44px;
  display: inline-flex;
  align-items: center;
  gap: 8px;
  padding: 0;
  border: 0;
  background: none;
  color: inherit;
  font-family: var(--body);
  font-size: var(--fs-xs, 0.9375rem);
  font-weight: 700;
  text-align: left;
  text-decoration: underline;
  text-decoration-thickness: 2px;
  text-underline-offset: 0.15em;
  cursor: pointer;
}

.screen .screen__back:hover,
.screen .screen__skip:hover {
  text-decoration-thickness: 3px;
}

/* Back first in the DOM, Skip last: opposite ends even when only one shows. */
.screen .screen__skip,
.screen .screen__skipped-out,
.screen .skipmenu {
  margin-left: auto;
}

/* Past the skip cap the control becomes a line saying so -- never a disabled
   button. On a question screen that footer is the room, so the quiet ink. */
.screen .screen__skipped-out {
  margin-top: 0;
  margin-bottom: 0;
  font-family: var(--body);
  font-size: var(--fs-xs, 0.9375rem);
  color: var(--sc-quiet);
}

/* The skip-reason group. */
.skipmenu {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 8px;
}

.skipmenu__ask {
  margin: 0;
  flex: 1 0 100%;
}

.skipmenu button {
  min-height: 44px;
  padding: 8px 14px;
  border: 2px solid var(--sc-edge);
  border-radius: var(--r-control, 8px);
  background: var(--paper);
  color: var(--night-plum);
  font-family: var(--body);
  font-size: var(--fs-xs, 0.9375rem);
  font-weight: 700;
  cursor: pointer;
}

.skipmenu button:hover {
  border-color: var(--night-plum);
}

.skipmenu .skipmenu__cancel {
  border: 0;
  background: none;
  text-decoration: underline;
  text-decoration-thickness: 2px;
  text-underline-offset: 0.15em;
}

/* On the two dark grounds the footer inverts with everything else. */
.screen--setcard .screen__footer,
.screen--beat .screen__footer,
.screen--setcard .screen__skipped-out,
.screen--beat .screen__skipped-out {
  color: var(--porcelain);
}

/* THE FOOTER OF A FRAMED SCREEN (the question card, the ask card, the
   two-tap) follows the control in the flow, porcelain on the set's ink:
   there is no page to sit outside of any more, and under the control is
   the shortest way for the hand to reach Back and Skip. */
.screen--ask .screen__footer,
.screen--picks .screen__footer,
.screen--scale .screen__footer {
  color: var(--porcelain);
}

.screen--ask .screen__skipped-out,
.screen--picks .screen__skipped-out,
.screen--scale .screen__skipped-out {
  color: var(--porcelain);
}

.screen--ask .skipmenu button,
.screen--picks .skipmenu button,
.screen--scale .skipmenu button {
  border-color: var(--porcelain);
  background: var(--porcelain);
  color: var(--night-plum);
}

.screen--ask .skipmenu .skipmenu__cancel,
.screen--picks .skipmenu .skipmenu__cancel,
.screen--scale .skipmenu .skipmenu__cancel {
  background: none;
  color: var(--porcelain);
}

/* "No problem. Which is it?" carries `.muted` -- the quiet ink drawn for a
   light ground, about 1.6:1 on the set's ink. On the ink it is porcelain. */
.screen--ask .skipmenu__ask,
.screen--picks .skipmenu__ask,
.screen--scale .skipmenu__ask {
  color: var(--porcelain);
}

/* One aria-live region per screen, never two. The client keeps its announcer
   and message line as siblings of the screen host. */
.screen .form-message {
  min-height: 1.5em;
  margin: 0;
}

/* A NON-EMPTY MESSAGE LEAVES THE FLOW AND BECOMES A TOAST pinned to the
   bottom of the viewport, above the fold by construction (the screen host
   grows to the viewport, so an in-flow line under it starts exactly where the
   viewport ends -- measured). An empty one stays in flow, reserving nothing.
   `:empty` is exact: `say()` writes textContent, and "" removes the only
   child. It carries no control and takes no pointer, so it can never be in
   the way of one. */
.assess-stage > .form-message {
  width: 100%;
  max-width: 46rem;
  margin: 0 auto;
  padding: 0;
  min-height: 0;
}

.assess-stage > .form-message:not(:empty) {
  position: fixed;
  left: 50%;
  bottom: 0;
  transform: translateX(-50%);
  z-index: 5;
  pointer-events: none;
  width: max-content;
  max-width: min(38rem, calc(100vw - 32px));
  margin: 0;
  padding: 12px 18px;
  border: 2px solid var(--night-plum);
  border-bottom: 0;
  border-radius: var(--r-control, 8px) var(--r-control, 8px) 0 0;
  background: var(--night-plum);
  color: var(--porcelain);
  font-size: var(--fs-xs, 0.9375rem);
  font-weight: 400;
  line-height: 1.45;
  text-align: left;
}

/* The band under the footer is reserved only while there is a message, on
   the press that produced it. A browser without :has() keeps the flat layout;
   the toast takes no pointer, so nothing under it becomes unreachable. The
   framed screens' footer is in the flow like every other, so the same
   margin lifts it clear of the toast. */
.assess-body:has(.assess-stage > .form-message:not(:empty)) .screen__footer {
  margin-bottom: 52px;
}

/* An error keeps the same object and changes only its edge: the text is the
   whole message, and the warning ink on plum is 9.33:1. */
.assess-stage > .form-message.is-error:not(:empty) {
  border-color: var(--alert-on-deep, #ffab98);
  color: var(--porcelain);
}

@media (prefers-reduced-motion: no-preference) {
  .assess-stage > .form-message:not(:empty) {
    animation: message-in var(--t-fade, 200ms cubic-bezier(.2, 0, 0, 1)) both;
  }
}

@keyframes message-in {
  from { opacity: 0; }
  to   { opacity: 1; }
}

/* Under reduced motion the consequence beat (in-flight sessions) is not a
   screen; its restated clause is carried onto the top of the next screen as
   a static line, in the quiet ink of the room. */
.screen:not(.screen--beat) .beat__line {
  margin: 0 0 8px;
  font-family: var(--body);
  font-size: var(--fs-xs, 0.9375rem);
  font-weight: 400;
  letter-spacing: 0;
  line-height: 1.5;
  text-align: left;
  color: var(--sc-quiet);
  animation: none;
}

.screen.screen--setcard .beat__line,
.screen.screen--scale .beat__line,
.screen.screen--ask .beat__line,
.screen.screen--picks .beat__line {
  color: var(--porcelain);
}

/* ---------------------------------------------------------------------------
   11 · THE DARK FRAME -- progressive enhancement

   When a dark screen is on stage the header goes with it, so a full-bleed arc
   screen is genuinely full bleed rather than a colour panel under a light
   bar. :has(), so a browser without it keeps the porcelain header: correct,
   just less atmospheric. The beads and the ring read the flipped tokens.
   --------------------------------------------------------------------------- */

.assess-body:has(.screen--setcard:not([hidden])),
.assess-body:has(.screen--story:not([hidden])),
.assess-body:has(.screen--beat:not([hidden])),
.assess-body:has(.screen--scale:not([hidden])),
.assess-body:has(.screen--ask:not([hidden])),
.assess-body:has(.screen--picks:not([hidden])),
.assess-body:has(.done:not([hidden])) {
  --bead-track: rgba(247, 247, 242, 0.28);
  --bead-done: var(--porcelain);
  --ring: var(--porcelain);
  background: var(--night-plum);
}

.assess-body:has(.screen--setcard:not([hidden])) .assess-header,
.assess-body:has(.screen--story:not([hidden])) .assess-header,
.assess-body:has(.screen--beat:not([hidden])) .assess-header,
.assess-body:has(.screen--scale:not([hidden])) .assess-header,
.assess-body:has(.screen--ask:not([hidden])) .assess-header,
.assess-body:has(.screen--picks:not([hidden])) .assess-header,
.assess-body:has(.done:not([hidden])) .assess-header {
  background: var(--night-plum);
  border-bottom-color: rgba(247, 247, 242, 0.2);
  color: var(--porcelain);
}

/* The chapter card's header is the card's own colour, and so is the question
   card's, the ask card's and the two-tap's: the set colour arrives on the
   chapter card and does not change again until the next one. The story and
   beat grounds are vignetted, so theirs match the edge. On the flat ink the
   header has no edge of its own: the 20% porcelain hairline drew a lighter
   seam under it on every question screen and chapter card, and the preview
   he tapped has none (design review, assess finish round 1, DF1-4). */
.assess-body:has(.screen--setcard:not([hidden])) .assess-header,
.assess-body:has(.screen--scale:not([hidden])) .assess-header,
.assess-body:has(.screen--ask:not([hidden])) .assess-header,
.assess-body:has(.screen--picks:not([hidden])) .assess-header {
  background: var(--sc-arc);
  border-bottom-color: transparent;
}

.assess-body:has(.screen--story:not([hidden])) .assess-header,
.assess-body:has(.screen--beat:not([hidden])) .assess-header {
  background: color-mix(in srgb, var(--sc-arc) 45%, var(--night-plum));
}

.assess-body:has(.screen--setcard:not([hidden])) .wordmark,
.assess-body:has(.screen--story:not([hidden])) .wordmark,
.assess-body:has(.screen--beat:not([hidden])) .wordmark,
.assess-body:has(.screen--scale:not([hidden])) .wordmark,
.assess-body:has(.screen--ask:not([hidden])) .wordmark,
.assess-body:has(.screen--picks:not([hidden])) .wordmark,
.assess-body:has(.done:not([hidden])) .wordmark,
.assess-body:has(.screen--setcard:not([hidden])) .wordmark span,
.assess-body:has(.screen--story:not([hidden])) .wordmark span,
.assess-body:has(.screen--beat:not([hidden])) .wordmark span,
.assess-body:has(.screen--scale:not([hidden])) .wordmark span,
.assess-body:has(.screen--ask:not([hidden])) .wordmark span,
.assess-body:has(.screen--picks:not([hidden])) .wordmark span,
.assess-body:has(.done:not([hidden])) .wordmark span {
  color: var(--porcelain);
}

.assess-body:has(.screen--setcard:not([hidden])) .spine__label,
.assess-body:has(.screen--story:not([hidden])) .spine__label,
.assess-body:has(.screen--beat:not([hidden])) .spine__label,
.assess-body:has(.screen--scale:not([hidden])) .spine__label,
.assess-body:has(.screen--ask:not([hidden])) .spine__label,
.assess-body:has(.screen--picks:not([hidden])) .spine__label,
.assess-body:has(.screen--scale:not([hidden])) .assess-header [data-spine-label],
.assess-body:has(.screen--ask:not([hidden])) .assess-header [data-spine-label],
.assess-body:has(.screen--picks:not([hidden])) .assess-header [data-spine-label] {
  color: var(--porcelain);
}

/* 11b, THE LIGHT FRAME, IS RETIRED (2026-09-29). The question card, the
   ask card and the two-tap no longer sit on the room; they are the set's
   ink edge to edge (no page since D-063), so they take the dark frame
   above, header in the set colour. */

/* ---------------------------------------------------------------------------
   12 · THE INTRO, THE PAUSE AND THE FINISH -- the screens not in the queue

   THE INTRO'S ORDER. The markup reads: headline, lead, the six promises, how
   to answer, then the age ask (inserted by the script before Start) and
   Start. That is the reading order and the order the script and the tests
   depend on. The screen shows the age ask and Start straight after the lead
   -- inside the first viewport on a phone -- and lets the promises and "How
   to answer" follow: `order` on a grid, so the DOM never moves. Tab order is
   unaffected (nothing in the promises or the how-to takes focus), and the
   how-to stays visible, never folded away.
   --------------------------------------------------------------------------- */

.intro {
  display: grid;
  grid-template-columns: 100%;
  row-gap: 0;
}

/* Spacing is per child rather than a row gap, so the empty status line
   under Start (a live region: it stays in the tree) costs one line and no
   more. */
.intro > * {
  margin: 0;
  min-width: 0;
}

.intro h1 {
  order: -3;
  max-width: 20ch;
  font-size: var(--fs-h2, clamp(2rem, 4.5vw, 3.5rem));
  font-weight: 700;
  line-height: 1;
  letter-spacing: -0.03em;
}

.intro .lead {
  order: -2;
  margin-top: 16px;
  max-width: 34rem;
  font-size: var(--fs-lead, 1.25rem);
  line-height: 1.45;
}

/* The gate: the age ask, the confirmation and the refusal the script may
   insert, Start, its status line and the resume line -- in their own DOM
   order, straight after the lead. */
.intro > .intro__age,
.intro > .intro__confirm,
.intro > .intro__refusal,
.intro > [data-start],
.intro > [data-intro-message],
.intro > [data-resume] {
  order: -1;
}

.intro > .intro__age,
.intro > .intro__confirm,
.intro > .intro__refusal {
  margin-top: 24px;
}

.intro > [data-start] {
  justify-self: start;
  margin-top: 16px;
}

.intro > [data-intro-message] {
  margin-top: 8px;
}

.intro > [data-resume] {
  margin-top: 16px;
}

.intro .resume-line {
  margin: 0;
}

/* The six promises: a ruled list, one promise per row, the promise itself in
   the display face and the thing that qualifies it in the reading face. Two
   columns from 768px. Not numbered -- they are not a sequence. */
.intro-facts {
  list-style: none;
  margin: 32px 0 0;
  padding: 0;
  display: grid;
  border-top: 1px solid var(--sc-rule);
}

.intro-facts li {
  padding: 16px 0;
  border-bottom: 1px solid var(--sc-rule);
  font-size: var(--fs-body, 1.0625rem);
  line-height: 1.55;
}

.intro-facts strong {
  display: block;
  margin-bottom: 4px;
  font-family: var(--display);
  font-optical-sizing: auto;
  font-size: 1.25rem;
  font-weight: 700;
  line-height: 1.2;
  letter-spacing: -0.01em;
  text-wrap: balance;
}

@media (min-width: 768px) {
  .intro-facts {
    grid-template-columns: 1fr 1fr;
    column-gap: 32px;
  }
}

/* How to answer: secondary in size, never in visibility. */
.how-to {
  margin-top: 16px;
  max-width: 60ch;
}

.how-to h2 {
  margin-bottom: 12px;
  font-size: var(--fs-h3, 1.5rem);
  line-height: 1.2;
  letter-spacing: -0.01em;
}

.how-to p {
  margin: 0 0 12px;
}

.how-to p:last-child {
  margin-bottom: 0;
}

/* -- the pause ----------------------------------------------------------- */

.paused {
  display: grid;
  row-gap: 16px;
  margin-block: auto;
}

.paused > * {
  margin: 0;
}

.paused__state {
  font-size: var(--fs-xs, 0.9375rem);
  font-weight: 700;
  color: var(--muted);
}

.paused h1 {
  max-width: 20ch;
  font-size: var(--fs-h2, clamp(2rem, 4.5vw, 3.5rem));
  font-weight: 700;
  line-height: 1;
  letter-spacing: -0.03em;
}

.paused .lead {
  max-width: 34rem;
  font-size: var(--fs-lead, 1.25rem);
  line-height: 1.45;
}

.paused > [data-unpause] {
  justify-self: start;
  margin-top: 8px;
}

/* -- the finish ---------------------------------------------------------- */

/* An arrival moment: plum ground, porcelain ink, every control the ink of
   the surface. The code is the keepsake -- a card on the last field of the
   Arc with the twelve beads walked above it and the code in the reading face
   with figures that line up. The notice under it is plum, not red: it is
   advice about keeping the code, not an error. */
.done {
  --ground: var(--night-plum);
  --ink: var(--porcelain);
  --ink-quiet: var(--plum-quiet, #c4b8cf);
  --ink-fill: var(--night-plum);
  --ink-lift: var(--porcelain-lift, #e9e9e2);
  --ring: var(--porcelain);
  --alert-ink: var(--alert-on-deep, #ffab98);
  display: grid;
  row-gap: 16px;
  margin-block: auto;
  background: var(--night-plum);
  color: var(--porcelain);
}

.done > * {
  margin: 0;
  min-width: 0;
}

.done h1 {
  max-width: 20ch;
  font-size: var(--fs-h2, clamp(2rem, 4.5vw, 3.5rem));
  font-weight: 700;
  line-height: 1;
  letter-spacing: -0.03em;
  color: var(--porcelain);
}

.done .lead {
  max-width: 34rem;
  font-size: var(--fs-lead, 1.25rem);
  line-height: 1.45;
  color: var(--porcelain);
}

.done__keep {
  --ground: var(--field-12, #f5d9bd);
  --ink: var(--night-plum);
  --ink-quiet: var(--muted);
  --ring: var(--night-plum);
  --alert-ink: var(--alert-600, #a81f06);
  display: grid;
  gap: 12px;
  margin-top: 8px;
  padding: 24px;
  border-radius: var(--r-card, 16px);
  background: var(--field-12, #f5d9bd);
  color: var(--night-plum);
}

.done__keep > * {
  margin: 0;
}

/* Twelve beads, all walked: one pill and eleven copies of it. Decorative and
   identical for everyone, so it can never be mistaken for a result. */
.done__walk {
  width: 18px;
  height: 8px;
  margin-bottom: 4px;
  border-radius: var(--r-pill, 999px);
  background: var(--arc-01, #1957d2);
  box-shadow:
    22px 0 0 var(--arc-02, #4b4cd1),
    44px 0 0 var(--arc-03, #6741ca),
    66px 0 0 var(--arc-04, #7c37be),
    88px 0 0 var(--arc-05, #8d2ead),
    110px 0 0 var(--arc-06, #9c2499),
    132px 0 0 var(--arc-07, #a81982),
    154px 0 0 var(--arc-08, #b10b69),
    176px 0 0 var(--arc-09, #b80049),
    198px 0 0 var(--arc-10, #bb0a10),
    220px 0 0 var(--arc-11, #a14301),
    242px 0 0 var(--arc-12, #8a5404);
}

.code-display {
  font-family: var(--body);
  font-size: clamp(1.5rem, 6vw, 1.75rem);
  font-weight: 700;
  font-variant-numeric: tabular-nums;
  letter-spacing: 0.04em;
  line-height: 1.3;
  word-break: break-all;
  color: var(--night-plum);
}

.done__keep .warn {
  font-size: var(--fs-xs, 0.9375rem);
  font-weight: 400;
  line-height: 1.5;
  color: var(--night-plum);
}

.done-actions {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 16px 24px;
}

.email-form {
  display: grid;
  gap: 8px;
  max-width: 26rem;
  margin-top: 8px;
  padding-top: 24px;
  border-top: 1px solid rgba(247, 247, 242, 0.24);
}

.email-form label {
  font-size: var(--fs-body, 1.0625rem);
  font-weight: 700;
}

.email-form input {
  min-height: 56px;
  padding: 8px 16px;
  border: 2px solid var(--porcelain);
  border-radius: var(--r-control, 8px);
  background: var(--paper);
  color: var(--night-plum);
  font: inherit;
  caret-color: var(--night-plum);
}

.email-form input::placeholder {
  color: var(--muted);
  opacity: 1;
}

.email-form .button-small {
  justify-self: start;
  margin-top: 4px;
}

.done .form-message {
  margin: 0;
}

.done .muted {
  max-width: 34rem;
  font-size: var(--fs-xs, 0.9375rem);
  line-height: 1.5;
}

/* ---------------------------------------------------------------------------
   12a · NARROW VIEWPORTS -- a phone
   --------------------------------------------------------------------------- */

@media (max-width: 560px) {
  /* The primary is full width on a phone. */
  .intro > [data-start],
  .intro .intro__continue,
  .paused > [data-unpause],
  .done-actions .button {
    width: 100%;
  }

  /* The two ways stay side by side on the narrowest phone (in-flight story
     sessions). */
  .screen--story .story__way {
    min-height: 8rem;
    padding: 14px 12px;
    font-size: 0.9375rem;
  }

  .story__ways {
    gap: 0;
  }

  /* THE FOOTER OF A SCALE SCREEN, ON A PHONE, ONCE ONE ANSWER IS IN. From
     the second item on it holds Back, the "depends" flag and Skip; in DOM
     order they wrap onto three rows and Skip falls under the fold (measured
     at 375x812, D-045). The flag is the wide one and the one that is not
     navigation, so it goes last: Back and Skip share the first row, the flag
     wraps onto the second. Visual order only; the DOM stays Back, flag,
     Skip, which is what the tests and the keyboard read. */
  .screen--scale .scale__varies {
    order: 1;
  }

  /* The guess screen at its tallest -- the longest statement quoted in the
     lead -- keeps its commit above the fold. */
  .screen .guess__opt {
    min-height: 46px;
    padding: 6px 12px;
  }

  .guess__note {
    padding: 10px 12px;
  }
}

/* ---------------------------------------------------------------------------
   12b · SHORT VIEWPORTS -- a laptop with browser chrome, a phone in landscape.
   Tighten the frame before the content.
   --------------------------------------------------------------------------- */

@media (max-height: 780px) {
  .screen {
    padding-block: clamp(16px, 3vh, 32px);
    gap: clamp(8px, 1.4vh, 12px);
  }

  /* The framed screens keep their 8px rhythm and give up top padding
     first. */
  .screen--ask,
  .screen--picks,
  .screen--scale {
    padding-top: 16px;
    gap: 8px;
  }

  .scale__stem,
  .screen--scale h1,
  .screen--scale h2 {
    /* the anti-jump reserve shrinks; it does not disappear */
    min-height: 2.4em;
  }

  .screen--scale .scale__stem {
    min-height: 2.5em;
  }

  .scale__gloss {
    margin-bottom: 0;
  }

  .setcard__head {
    font-size: clamp(1.75rem, 5vw, 3rem);
  }

  .assess-body:has(.spine__bead--now) .screen--setcard::before {
    font-size: clamp(6rem, 30vw, 12rem);
  }

  .screen--story .story__choice {
    min-height: 5.5rem;
  }

  .screen--story .story__way {
    min-height: 7rem;
  }

  .screen .ordering__item,
  .screen .guess__opt {
    min-height: 46px;
    padding: 6px 12px;
  }

  .rank__head,
  .guess__question,
  .words__prompt {
    font-size: clamp(1.375rem, 3.4vw, 1.75rem);
  }
}

/* Below 700px of viewport the motif goes and "I…" takes its row alone, so
   the control keeps its place inside the fold (PLAN-assess.md 5.5). */
@media (max-height: 700px) {
  .scale__motif {
    display: none;
  }

  .screen--scale .scale__lead {
    min-height: 0;
  }
}

/* ---------------------------------------------------------------------------
   13 · REDUCED MOTION -- fewer and gentler, not none

   No global kill. Each thing that moves is named: the crossfades shorten to
   the durations DESIGN.md sets, the room change becomes a 120ms colour
   change, the beat's staged rise resolves at once (the client folds the beat
   into a static line anyway), and every control keeps its colour and state
   changes because those confirm an action.
   --------------------------------------------------------------------------- */

@media (prefers-reduced-motion: reduce) {
  .screen {
    animation: none;
    transition: background-color 120ms linear;
  }

  .screen--setcard,
  .screen--story,
  .screen--beat {
    animation: ground-in 150ms linear both;
  }

  .assess-header {
    transition-duration: 120ms;
  }

  .beat__line,
  .beat__rule,
  .beat__next {
    animation: none;
    opacity: 1;
    transform: none;
  }

  .spine__bead {
    transition-duration: 100ms;
  }

  .dial__thumb {
    transition: none;
  }

  .screen .scale__opt,
  .screen .scale__num,
  .screen .scale__varies,
  .screen .steps__opt,
  .screen .ask__confirm,
  .screen .ask__other,
  .screen .story__choice,
  .screen .story__way,
  .screen .story__pick,
  .screen .ordering__item,
  .screen .guess__opt,
  .screen .ask__chip,
  .screen .setcard__go,
  .screen .ordering__confirm,
  .screen .guess__confirm,
  .screen .words__skip,
  .screen .words__done,
  .words__field {
    transition-property: opacity, background-color, border-color, color;
    transition-duration: 100ms;
  }

  .assess-stage > .form-message:not(:empty) {
    animation: none;
  }
}

/* ---------------------------------------------------------------------------
   14 · FORCED COLOURS -- the same geometry, the system's palette

   The arc is gone by definition. The geometry already carries the format and
   the glyphs already carry selection; this only makes sure the boundaries
   survive.
   --------------------------------------------------------------------------- */

@media (forced-colors: active) {
  /* The story card's page (in-flight sessions). The question card, the ask
     card and the two-tap have no page since D-063: their white objects are
     the boundaries, and they get real borders below. */
  .screen--story::before {
    border: 1px solid CanvasText;
    box-shadow: none;
  }

  /* The motifs are strokes in currentColor: the system's text colour. */
  .scale__motif,
  .setcard__motif {
    color: CanvasText;
    forced-color-adjust: none;
  }

  .dial__track {
    border: 2px solid ButtonText;
  }

  .dial__thumb::before {
    border: 2px solid ButtonText;
    background: Highlight;
    forced-color-adjust: none;
  }

  /* The chosen stop's white pill is a fill the system palette removes; an
     outline drawn inside it keeps the mark without moving a word. */
  .screen--scale .scale__opts--dial .scale__opt--chosen .scale__anchor {
    outline: 2px solid Highlight;
    outline-offset: -2px;
  }

  .steps__opt--on,
  .steps__opt--chosen,
  .steps__legend-item--in,
  .steps__legend-item--on,
  .ordering__item--most,
  .ordering__item--least {
    border-color: Highlight;
  }

  .scale__opt,
  .steps__opt,
  .ask__other,
  .story__choice,
  .story__pick,
  .ordering__item,
  .guess__opt,
  .ask__chip,
  .setcard__go,
  .ask__confirm,
  .ordering__confirm,
  .picks__confirm,
  .say__confirm,
  .guess__confirm,
  .words__skip,
  .words__done,
  .write__skip,
  .write__done,
  .skipmenu button,
  .intro__keep,
  .intro__refusal-home {
    border: 2px solid ButtonText;
  }

  .scale__opt,
  .scale__opt--chosen,
  .scale__opt:hover {
    box-shadow: none;
  }

  .screen--scale .scale__num,
  .screen .guess__key,
  .screen .ordering__key {
    border: 1px solid ButtonText;
  }

  /* Selection already carries a glyph on every one of these. */
  .scale__opt--chosen,
  .story__choice--chosen,
  .story__way--chosen,
  .story__pick--on,
  .ordering__item--placed,
  .guess__opt--on,
  .ask__chip--on,
  .scale__varies--on {
    border-color: Highlight;
  }

  .ordering__placed:not([hidden]) ~ .ordering__step {
    border-top: 1px dashed CanvasText;
  }

  .story__ways {
    border: 2px solid ButtonText;
  }

  .story__ways > .story__way + .story__way {
    border-left: 2px solid ButtonText;
  }

  .guess__note,
  .setcard__nudge,
  .intro__confirm,
  .intro__age,
  .intro__refusal,
  .done__keep,
  .assess-stage > .form-message:not(:empty) {
    border: 1px solid CanvasText;
  }

  .intro__age-field,
  .email-form input {
    border: 2px solid CanvasText;
  }

  .spine__bead,
  .done__walk {
    border: 1px solid CanvasText;
  }

  .done__walk {
    box-shadow: none;
  }

  .spine__bead--done,
  .spine__bead--now {
    background: Highlight;
    forced-color-adjust: none;
  }

  .words__field,
  .write__field,
  .ask__other {
    border-bottom: 2px solid CanvasText;
  }
}
