/* Assessment page delta.
 *
 * Loaded on top of the vendored tokens.css + components.css and reading ONLY
 * design-system tokens — no literal colours, sizes or fonts (ADR_004, and the
 * CSS discipline recorded in docs/architecture-baseline.md).
 *
 * SCOPE: page layout only. Anything that is a reusable *component* goes
 * upstream to /git/component_library first and is vendored back — never
 * authored here. That explicitly covers the two components this Assessment
 * still owes: the four-statement selector (the library's rating-slider is a
 * three-stop control and unsuitable as-is) and the six-axis radar chart.
 *
 * The classes below are page scaffolding — a two-column wizard layout — and
 * are not candidates for the library unless a second page needs them.
 */

/* ── Frame states (JOB-AUS1-0001kF) ───────────────────────────────────────
 * The network-failure banner and the loading indicator sit above the wizard,
 * aligned to the same content column. The banner itself is the vendored
 * .banner--error; the spinner is the vendored .spinner. Layout only here. */
#assessment-frame-error {
  max-width: var(--bs-content-max);
  margin-inline: auto;
  padding-block-start: var(--space-6);
  padding-inline: var(--space-6);
}
/* Hidden at rest; htmx adds .htmx-request (hx-indicator) while a request is in
 * flight. Display toggling is ours, not htmx's injected opacity rule, so the row
 * also takes no space at rest.
 *
 * OUT OF FLOW, and that is the point (2026-07-30). It used to be display:none →
 * display:flex with its own block padding, so appearing pushed the whole wizard
 * down and vanishing snapped it back. On a navigation the jolt was masked by the
 * screen changing underneath it; on an autosave nothing else moved, so releasing
 * a slider bounced the page. Position it over the top of the content column
 * instead: it still reads as "above the wizard, in the same column", and it can
 * no longer move anything. */
#assessment-busy {
  display: none;
}
#assessment-busy.htmx-request {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-3);
  position: absolute;
  inset-block-start: 0;
  inset-inline: 0;
  z-index: 2;
  padding-block: var(--space-3);
  color: var(--be-text-2);
  /* Legible over whatever it lands on, without reading as a banner. */
  background: color-mix(in srgb, var(--fe-surface) 88%, transparent);
}
/* The containing block for the absolute position above. */
.app-shell__main {
  position: relative;
}

/* The two-column wizard frame is the VENDORED .app-layout / .stepper-rail /
 * .app-content (components.css, wizard-layout.md, locked 2026-07-30 — seeded by
 * this page). It owns the 260px rail, the sticky rail offset, the page padding
 * and the under-720px stack where the steps flow as a wrapped row.
 *
 * This file used to define .app-layout itself, with a narrower rail, no sticky,
 * and `:not(:has(.stepper-rail))` in place of the component's --solo modifier.
 * Both definitions carried the same class name, so the local one won and the
 * component's behaviour was unreachable. Deleted rather than reconciled
 * (frontend-design review, 2026-07-30): the page now composes .bs-container
 * .app-layout, adds .app-layout--solo when a screen carries no rail, and wraps
 * the mount in .app-content. Nothing about the grid is authored here. */

/* Scaffold-only affordance: marks the placeholder copy that must be gone
 * before launch. Removing the [data-skeleton-note] elements removes this. */
[data-skeleton-note] {
  color: var(--be-text-2);
}

/* ── Stepper sub-steps ────────────────────────────────────────────────────
 * The report is three sections, and the rail says which one is showing
 * (JOB-AUS1-0001mE, review finding 5). The vendored .stepper has no sub-step
 * support: stepper.md documents a FLAT list with three states, CSS-only, host
 * owns the state. Decision (Tyson, 2026-07-30): extend it here, the way this
 * page already extends the design system, rather than edit the vendored copy —
 * site-assets/css is a pinned preview of what the site serves and this repo
 * never authors into it.
 *
 * UPSTREAM-PROMOTION CANDIDATE for component_library's own .stepper.
 *
 * The markup contract, which _stepper.html renders and the CSS below assumes:
 *
 *   <li>
 *     <a class="stepper__step is-active" data-step-key="report"> … </a>
 *     <ol class="stepper__substeps">
 *       <li><a class="stepper__substep is-complete" data-substep-key="result">
 *             <span class="stepper__substep-marker" aria-hidden="true"></span>
 *             <span>Your result</span></a></li>
 *     </ol>
 *   </li>
 *
 * Sub-steps sit INSIDE the parent's <li>, so the rail stays one ordered list of
 * top-level steps and a sub-step can never be mistaken for one — the numbering
 * the header reads (RAIL_STEP_NUMBER_BY_KEY) counts .stepper__step only. */
.stepper__substeps {
  list-style: none;
  /* Aligned with the parent's LABEL, not its marker: the step's own padding,
     plus the marker, plus the gap the component puts between the two. Reading
     down the rail, a sub-step starts where its parent's words start. */
  margin: var(--space-1) 0 0;
  padding: 0 0 0 calc(var(--space-3) + var(--step-marker) + var(--space-3));
  display: flex;
  flex-direction: column;
  gap: 2px;
}
.stepper__substep {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  padding: var(--space-1) var(--space-3) var(--space-1) 0;
  border-radius: var(--radius);
  font-size: var(--fs-caption);
  line-height: var(--lh-caption);
  color: var(--fe-text-2);
  text-decoration: none;
}
a.stepper__substep:hover,
button.stepper__substep:hover {
  background: var(--row-hover);
}
a.stepper__substep:focus-visible,
button.stepper__substep:focus-visible {
  outline: none;
  box-shadow: 0 0 0 2px var(--accent);
}
/* The sub-step is a BUTTON in a form, not the anchor the contract above draws.
   Navigation in the Assessment is a platform event post carrying a no-JS
   `action` (ADR_010), so the rail's top-level steps are already buttons and the
   sub-steps follow them. These are the UA resets the vendored `.stepper__step`
   applies for exactly the same reason (components.css: `background:none;
   border:0; width:100%; text-align:left; font:inherit; color:inherit`) — the
   local `.stepper__substep` rule above was written against the reference's
   anchors and carries none of them, so without this a sub-step arrives with a
   button border, the UA's font and its own width. */
button.stepper__substep {
  width: 100%;
  background: none;
  border: 0;
  font: inherit;
  text-align: left;
  cursor: pointer;
}

/* The component's marker at sub-scale. A check glyph is unreadable at 8px, so
   a done sub-step is a filled pip and the CURRENT one is a filled pip inside a
   ring — the two states stay distinguishable without colour alone carrying it,
   because the current one also takes the ink weight below. */
.stepper__substep-marker {
  flex: none;
  width: 8px;
  height: 8px;
  border-radius: 50%;
  border: 1.5px solid var(--radio-ring);
  background: var(--surface);
}
.stepper__substep.is-complete .stepper__substep-marker,
.stepper__substep.is-active .stepper__substep-marker {
  background: var(--accent);
  border-color: var(--accent);
}
.stepper__substep.is-active .stepper__substep-marker {
  box-shadow: 0 0 0 2px var(--surface), 0 0 0 3.5px var(--accent);
}
.stepper__substep.is-active {
  color: var(--ink);
  font-weight: var(--fw-semibold);
}
.stepper__substep.is-complete {
  color: var(--ink);
}

/* Narrow: the vendored rail becomes a wrapped row of title-only chips, and it
   already drops its own secondary line there (.stepper-rail .stepper__sub).
   Sub-steps are the same kind of detail and would break the row, so they go the
   same way. Nothing is lost: the report's own Next/Back is on screen, and the
   section heading names where you are. */
@media (max-width: 720px) {
  .stepper__substeps {
    display: none;
  }
}

/* ── Dimension screen — one scroll region, sticky bars (JOB-AUS1-0001kL) ────
 * THE DOCUMENT IS THE ONLY THING THAT SCROLLS. The screen's title bar and its
 * Back/Next bar stay put with position:sticky, which is not a scroll container,
 * so nothing nests and the page keeps a single scrollbar.
 *
 * This replaces a nested scroll region (max-height: calc(100dvh - 4rem) plus
 * overflow-y:auto on the body). Measured before the change, at 1440x900: the
 * document had 428px of scroll, .screen__body had 1577px, and the "pinned"
 * footer sat 36px BELOW the fold — because the card was allowed 100dvh - 64px
 * while its actual slot is 100dvh minus the 68px topbar minus 64px of layout
 * padding. The 4rem predated the site chrome. Two scrollbars fought, and the
 * Back/Next bar was unreachable without scrolling the page.
 *
 * Sticky needs an unclipped ancestor, so the card must set no overflow at all —
 * which is why the old max-height/overflow pair is gone rather than tuned.
 *
 * Below 45rem the bars flow instead: the site topbar can wrap to three rows at
 * that width (see the app-shell note in components.css), so a fixed sticky
 * offset would be wrong, and the card is short enough not to need them. */
.screen--locked {
  padding: 0;
}
.screen__header {
  padding: var(--space-6) var(--space-6) var(--space-5);
  border-bottom: 1px solid var(--fe-border);
}
/* The percent-complete tracker under the Dimension title. Placement only — the
   bar, its fill and the label row are the vendored .progress component. It sits
   in the sticky header, so it is visible from anywhere in a long Dimension. */
.screen__progress {
  margin-top: var(--space-5);
}
.screen__body {
  padding: var(--space-6);
  display: flex;
  flex-direction: column;
  gap: var(--space-7);
}
/* What this Dimension's questions cover, under the heading (finding 8). Body
   prose, so it takes the same reading measure the rest of the page's prose does
   and the secondary ink that keeps it behind the questions rather than beside
   them. It is in the body and not the sticky header on purpose — see the
   template's note. */
.screen__intro {
  margin: 0;
  max-width: 68ch;
  color: var(--fe-text-2);
}
/* END-ALIGNED, and space-between would not do it. Every one of the five
   templates that render this footer — dimension_screen, report,
   link_not_usable, claim_confirmed, session_expired — puts its buttons inside a
   single .screen__footer-actions child, so space-between had nothing to
   distribute and resolved to a left-crowded row in all five (measured
   2026-09-09: actions ending at 677px in a footer ending at 1379px). Same
   reasoning as .cta-row--end below, ratified as review finding 1 on
   2026-07-30: the flow reads left to right and the action belongs where the eye
   leaves. Aligning the WRAPPER rather than restyling it keeps Back beside Next
   as one group, which is the alignment Tyson called on 2026-09-09. */
.screen__footer {
  display: flex;
  align-items: center;
  justify-content: flex-end;
  flex-wrap: wrap;
  gap: var(--space-4);
  padding: var(--space-5) var(--space-6);
  border-top: 1px solid var(--fe-border);
}
.screen__footer-actions {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: var(--space-3);
}

/* THE NEXT GATE'S REFUSAL (DEF-23, TASK-AUS1-000DDI). It is a child of
   .screen__footer-actions so that an answer's out-of-band repaint of that bar
   clears it with no script — see screen_actions.hbs. That bar is a flex row, so
   the refusal has to claim a whole line of its own or it would sit in the row
   beside Back and Next as if it were a third control; flex-basis does that
   against the wrap already set above.

   .field__error inside it is the design system's, unchanged: circle-x icon,
   --status-error, and align-items:center, which is right for its one line of
   text. The list is separate rather than inside that paragraph because a <ul>
   cannot live in a <p>, and because the icon should mark the refusal once
   rather than once per question. */
.screen__refusal {
  flex-basis: 100%;
}
.screen__refusal-list {
  margin: var(--space-2) 0 0;
  /* Indented past the icon above it, so the questions read as belonging to the
     refusal rather than as a new list starting under it. */
  padding-left: var(--space-6);
  color: var(--status-error);
  font: var(--fw-regular) var(--fs-caption)/1.5 var(--font-sans);
}
.screen__refusal-list li + li {
  margin-top: var(--space-1);
}
.btn.is-disabled {
  opacity: 0.5;
  pointer-events: none;
}

/* THE FOOTER IS STICKY AT 45rem AND ABOVE. This reverses the reversal recorded
   here before it, and that earlier reasoning is kept rather than deleted because
   it is still the argument against going further than this.

   What it said: the bar had been pinned once already on a reachability argument
   that belonged to a nested scroll region a later change DELETED, and with the
   document as the only scroller a pinned Back/Next bar floats over the questions
   for the whole screen, covering the last one, reading as chrome rather than as
   the end of the form. So the bar was returned to the last row of the card,
   reached by finishing the questions.

   What outweighed it (2026-08-21 UX review, DEF-10): every Dimension screen is
   five questions, and the review measured the footer resolving at 2080px on a
   900px viewport — 1180px of scrolling to reach Next. On a first pass that
   distance is the form itself and costs nothing, but a Respondent returning to a
   completed screen, or changing one answer near the top, then scrolls the full
   length again with nothing to do en route. Pinning was chosen over the narrower
   alternative of revealing the bar only once all five are answered.

   The cost the earlier note names is real and is accepted: the bar does occupy a
   strip over the questions for the whole screen. Keep it to one row — if this
   bar ever grows tall enough to crowd the question under it, revisit the
   answered-only variant rather than shrinking the questions.

   Below 45rem it stays unpinned: the bar wraps to two rows at a phone width and
   pinning it there would take a real share of the viewport, which is the same
   objection that removed it from the whole screen the first time.

   The header stays sticky — the Dimension title and its progress bar are
   orientation, and orientation is exactly what should follow you. */
@media (min-width: 45rem) {
  /* The card's own surface, so scrolling questions pass behind the bar rather
     than through it. --fe-surface, not --surface: that is what .card paints, and
     a near-white mismatch shows as a seam. */
  .screen__header {
    position: sticky;
    z-index: 1;
    background: var(--fe-surface);
    /* The card sets no overflow (sticky would be clipped by it), so a square
       corner on a sticky bar paints over the card's rounded one. */
    top: 0;
    border-start-start-radius: var(--card-radius);
    border-start-end-radius: var(--card-radius);
  }
  /* Same surface and the same rounded-corner problem as the header, mirrored:
     the card sets no overflow, so a square corner on a pinned bar paints over
     the card's rounded one at the BOTTOM edge. */
  .screen__footer {
    position: sticky;
    z-index: 1;
    background: var(--fe-surface);
    bottom: 0;
    border-end-start-radius: var(--card-radius);
    border-end-end-radius: var(--card-radius);
  }
}

/* ── Reading measure ──────────────────────────────────────────────────────
 * The screen column is as wide as the page (the report needs it for the radar
 * and the worked-example table), and running prose in it was measured at 129
 * characters a line against a comfortable 65 to 75. Prose gets a measure;
 * tables, the radar and score rows keep the full column.
 *
 * The question screen caps the whole card instead: its statements are the
 * reading, and a 120-character statement on one line is the thing being fixed.
 * Left-aligned, so the card still lines up with the rail. */
#assessment-mount .card__body > p {
  max-width: 68ch;
}
.screen--locked {
  max-width: 48rem;
}

/* ── Action rows ──────────────────────────────────────────────────────────
 * wizard-layout.md gives a screen card's buttons the shared .cta-row with the
 * --start variant, and the library ships no right-aligned sibling. Review
 * finding 1 (2026-07-30) puts Start at the end of its row instead: it is the
 * last thing on the screen, the flow reads left to right, and the action
 * belongs where the eye leaves. Same row, same button — only the justification
 * differs, which is why this is an --end sibling of the library's --start and
 * not a restyle of .btn.
 *
 * UPSTREAM-PROMOTION CANDIDATE: this belongs beside --start in the library's
 * own .cta-row. It is here because site-assets/css is a preview-only copy the
 * site vendors for itself — this repo never edits components.css. */
.cta-row--end {
  justify-content: flex-end;
  margin-top: var(--space-6);
}

/* ── Form-level messages ──────────────────────────────────────────────────
 * .banner carries no margin by design — the library leaves placement to the
 * host (banner.md: "it sits in the flow of the layout"). Ours sits directly
 * above a form, and with nothing here it butted against the first field's
 * label with no gap at all. Matches the gap .field already puts below itself. */
#assessment-mount .banner {
  margin-bottom: var(--space-4);
}

/* Questions grouped by Practice within the Dimension. */
.practice {
  display: flex;
  flex-direction: column;
  gap: var(--space-7);
}
.rate-q {
  border: 0;
  margin: 0;
  padding: 0;
}
.rate-q__prompt {
  padding: 0;
  margin: 0 0 var(--space-4);
  font-weight: var(--fw-semibold);
}
/* The (i) that opens the question's help modal (JOB-AUS1-0001mD, finding 7).
   The button IS the icon-button component (.ib .ib--info .ib--28), which is
   already inline-flex, so it flows at the end of the prompt with no layout of
   its own. Two deltas only: it sits on the text's line rather than its baseline
   box, and the negative block margin keeps a 28px circle from growing the line
   height of every prompt on the screen. */
.rate-q__help {
  vertical-align: text-bottom;
  margin-inline-start: var(--space-1);
  margin-block: -4px;
}

/* The answer control (.rating-slider*) and the don't-know box (.chk*) are both
 * vendored design-system components (JOB-AUS1-0001m6) — their styling lives in
 * components.css. What is here is only the delta this screen needs, carried over
 * from the design reference's own token-only delta.
 *
 * The component is built for a compact right-aligned readout naming a stop
 * ("Skilled"). Ours carries the whole statement, which is the thing being read,
 * so it becomes a left-aligned line of body text. The reserved height stops the
 * card reflowing every time the handle moves between a one-line and a two-line
 * statement. */
.rate-q .rating-slider {
  padding: 0; /* the component insets by 10px to fit its handle; the labels
                 below are positioned, so the inset only misaligns it with the
                 prompt above. */
}
.rate-q .rating-slider__readout {
  justify-content: flex-start;
  min-height: calc(var(--lh-body) * 2em);
  margin-bottom: var(--space-3);
}
.rate-q .rating-slider__value {
  font: var(--fw-regular) var(--fs-body) / var(--lh-body) var(--font-sans);
  color: var(--ink);
  text-align: left;
}
.rate-q .rating-slider.is-unset .rating-slider__value {
  color: var(--fe-text-2);
  font-style: italic;
}
/* "I don't know" greys the dial out (review, 2026-07-30). It is still an answer,
 * so the CHECKBOX stays fully legible and checked — what fades is the scale, which
 * this answer says nothing about. Without this the green fill sat there at full
 * strength beside a ticked box, reading as though a Tier had also been chosen.
 *
 * Greyed, not disabled: the control stays operable because moving the handle is
 * how you change your mind, and disabling it would make that two steps. The
 * component's .is-unset (grey track, hollow thumb) does the real work; this
 * fades what remains. */
.rate-q .rating-slider.is-dk {
  opacity: 0.5;
}
.rate-q__dk {
  margin-top: var(--space-3);
}

/* The scored Assessment's read-only answer (review, 2026-07-31). Set as the
 * statement itself rather than a Tier name, because the statement is what was
 * chosen and reading back "Assisted" would make the Respondent translate.
 * Typographically the .rating-slider__value it replaces, so a Dimension revisited
 * after completion reads as the same screen gone quiet rather than a new one. */
.rate-q__answer {
  margin: 0;
  font: var(--fw-regular) var(--fs-body) / var(--lh-body) var(--font-sans);
  color: var(--ink);
}
.rate-q__answer--unanswered {
  color: var(--fe-text-2);
  font-style: italic;
}

/* Stop labels, positioned on their stops.
 *
 * BELONGS UPSTREAM. The component spreads its labels with flex — each one
 * flex:1, the first left, the last right, the rest centred — which puts a
 * three-stop scale's middle label at exactly 50% and is why it was never
 * noticed. With four stops the handle rests at 0 / 33 / 67 / 100% while the
 * labels sit at 12.5 / 37.5 / 62.5 / 87.5%, so "Assisted" and "Augmented" are
 * measurably beside their stops rather than under them. Fixing it in
 * components.css would change a locked component and re-open the site's design
 * system pin, so it is scoped to .rate-q here and left as an upstream candidate
 * (JOB-AUS1-0001m6).
 *
 * Four rules rather than a per-label custom property because BRule7 fixes the
 * count at four, and nth-child keeps the component's markup contract untouched.
 * The thumb offset is the component's own: the handle's centre can only travel
 * between half a thumb-width of each end. First and last align to the edges
 * instead of centring, so neither overhangs the card. */
.rate-q .rating-slider__labels {
  position: relative;
  display: block;
  min-height: 1.4em;
}
.rate-q .rating-slider__label {
  position: absolute;
  flex: none;
  white-space: nowrap;
  text-align: center;
}
.rate-q .rating-slider__label:nth-child(1) {
  left: 0;
}
.rate-q .rating-slider__label:nth-child(2) {
  left: calc(var(--slider-thumb) / 2 + (100% - var(--slider-thumb)) / 3);
  transform: translateX(-50%);
}
.rate-q .rating-slider__label:nth-child(3) {
  left: calc(var(--slider-thumb) / 2 + (100% - var(--slider-thumb)) * 2 / 3);
  transform: translateX(-50%);
}
.rate-q .rating-slider__label:nth-child(4) {
  right: 0;
}

/* ── Progress rail (JOB-AUS1-0001kL) ──────────────────────────────────────
 * The rail steps are links (free back-navigation), so replicate the vendored
 * button.stepper__step interaction affordance for anchors. */
a.stepper__step {
  cursor: pointer;
  text-decoration: none;
}
a.stepper__step:hover {
  background: var(--row-hover);
}
/* "Out with a colleague": a delegated-pending Dimension, shown distinctly and
 * never as completion progress (job note 4). Assessment-specific status
 * semantics — no other surface has a delegated step, so it lives here rather
 * than in the shared component. The bright line (pending never counts) is
 * enforced in the status mapping, not this rule. */
.stepper__step.is-delegated .stepper__marker {
  border-color: var(--status-neutral);
  color: var(--status-neutral);
  background: var(--status-neutral-fill);
}
.stepper__step.is-delegated .stepper__title {
  color: var(--ink);
}
/* "Your result" before it unlocks. A span, not a link — there is nothing to
 * navigate to until all six Dimensions are answered — so it needs to read as the
 * destination rather than as a step you have skipped past. */
.stepper__step[data-locked] {
  cursor: default;
}
.stepper__step[data-locked] .stepper__title {
  color: var(--fe-text-2);
}

/* ── Maturity Report (JOB-AUS1-0001kN) ────────────────────────────────────
 * Page scaffolding for the report frame, reading only design-system tokens.
 * The radar (JOB-AUS1-0001kO) and the Recommendations list (JOB-AUS1-0001kP)
 * are vendored/authored by their own jobs; the regions below are their seams. */
/* HEADER / BODY / FOOTER since JOB-AUS1-0001mI, the same three-part card the
 * Dimension screens use, so the report gets their sticky chrome: the three
 * actions stay on screen, and so do Back and Next. The card sets no padding of
 * its own for the same reason .screen--locked does not — the bars have to reach
 * the card's edges. No max-width, unlike .screen--locked: the radar and the two
 * value-gap figures need the full column. */
.report {
  padding: 0;
}
.report .screen__body {
  display: flex;
  flex-direction: column;
  gap: var(--space-7);
}
/* The three actions, always in reach. Wraps rather than scrolls: at a phone width
 * three buttons become three rows, which is long but legible, and the header is
 * not sticky below 45rem so it costs nothing from the screen. */
.report__cta-bar {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-3);
  margin-top: var(--space-5);
}
.report__headline {
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
}
.report__tier {
  margin: 0;
  font-size: var(--fs-h2);
  font-weight: var(--fw-bold);
  display: flex;
  align-items: center;
  gap: var(--space-2);
}
/* The (i) beside the headline Tier. Sized to the heading's own line so it reads
 * as an affordance on the number rather than a control after it. */
.report__help {
  flex: none;
}
.report__cap {
  margin: 0;
  color: var(--be-text-2);
}

/* Radar (JOB-AUS1-0001kO) — the Respondent's own six-Dimension profile, rendered
 * server-side as an inline SVG. Colour comes only from tokens (ADR_004). */
/* Scrolls rather than shrinks below the radar's legible minimum — see .radar. */
.report__radar {
  display: flex;
  justify-content: center;
  overflow-x: auto;
}
.report__radar:empty {
  display: none;
}
/* The figure is the centrepiece of the report, so it is sized like one (review,
 * 2026-07-30). This is a plot 260 units across in a viewBox 460 wide, so the
 * max-width below buys a hexagon about 340px across — where the old 320px box
 * held a plot of 187px, because two thirds of it was label gutter. The viewBox
 * now contains the labels (radar.py), so this figure no longer paints outside
 * the element and the number here is the whole footprint. */
/* max-width is the design width, where the viewBox scale is 1 and the axis labels
 * are the 13px the rest of the page reads at (radar.py). min-width is the point
 * below which they stop being readable: SVG type scales with the box, so at a
 * phone's 253px column an unconstrained figure painted its labels at 5.5px. The
 * floor holds them above 11px and the figure scrolls inside .report__radar instead.
 *
 * Scrolling a figure on a phone is a poor answer; it is a better one than type
 * nobody can read, and it fails visibly rather than silently. A layout that suits a
 * narrow screen properly is still owed. */
.radar {
  width: 100%;
  min-width: 520px;
  max-width: 600px;
  height: auto;
}
.radar__ring {
  fill: none;
  stroke: var(--fe-border);
  stroke-width: 1;
}
.radar__spoke {
  stroke: var(--fe-border);
  stroke-width: 1;
}
.radar__area {
  fill: var(--accent-fill);
  fill-opacity: 0.5;
  stroke: var(--accent);
  stroke-width: 2;
  stroke-linejoin: round;
}
.radar__vertex {
  fill: var(--accent);
}
/* The Dimension that pins the headline Tier — same signal as the score row's
 * is-capping emphasis, so graphic and table agree (AC4).
 *
 * A ring AROUND the vertex, which keeps its series colour. This used to recolour
 * the dot to --status-warning, and --status-warning is the same hex as --data-3:
 * the design system's SECOND chart series. Several capped Dimensions therefore
 * drew several second-series-coloured points onto a first-series line, and read as
 * two datasets in total overlap (radar.py has the full note). A ring says "this
 * one, on the line you are already looking at". */
.radar__cap-ring {
  fill: none;
  stroke: var(--status-warning);
  stroke-width: 2;
}
.radar__label {
  fill: var(--be-text-2);
  font-size: var(--fs-caption);
}

/* The key. The figure carries three distinct marks and named none of them, which
 * is the whole of "I can't tell what they mean". Laid out as a wrapping row so it
 * reads as a caption to the figure rather than a list in its own right. */
.report__radar-key {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: var(--space-2) var(--space-5);
  margin: var(--space-3) 0 0;
  padding: 0;
  list-style: none;
  font-size: var(--fs-caption);
  color: var(--fe-text-2);
}
.report__radar-key li {
  display: flex;
  align-items: center;
  gap: var(--space-2);
}
/* Drawn with the same tokens as the marks they stand for, so the key cannot
 * drift from the figure without someone noticing here. */
.report__radar-key svg {
  flex: none;
  overflow: visible;
}

/* Per-Dimension scores — the radar's accessible equivalent, which radar.py's
 * <desc> points at by name.
 *
 * NOT PRINTED ANY MORE WHERE HOVER EXISTS (JOB-AUS1-0001mI). Review, 2026-07-31:
 * "We should be able to remove the report__score-list div because the hover
 * section should be good enough. No need to duplicate twice." The tooltips now
 * carry every field this list carries, so on a pointer device it was the same six
 * rows said again underneath the figure.
 *
 * It stays in the MARKUP, and the rule is written the safe way round — hidden only
 * where hover is known to exist, rather than hidden by default and revealed. Two
 * readers still need it and neither has a pointer: a screen reader, for which this
 * is the figure's text equivalent, and a phone, where nothing can be hovered at
 * all. A browser that does not understand the query shows the list, which is the
 * failure worth having.
 *
 * Same values as the library's .sr-only (components.css), restated because the
 * hiding is conditional and a class cannot be. It has to come AFTER the base rule
 * below, which sets a margin of its own. */
.report__score-list {
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
  margin: 0;
}
@media (hover: hover) {
  .report__score-list {
    position: absolute;
    width: 1px;
    height: 1px;
    padding: 0;
    margin: -1px;
    overflow: hidden;
    clip: rect(0, 0, 0, 0);
    white-space: nowrap;
    border: 0;
  }
}
.report__score-row {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: var(--space-4);
  padding-block: var(--space-2);
  border-bottom: 1px solid var(--fe-border);
}
.report__score-row.is-capping {
  font-weight: var(--fw-semibold);
}
.report__dim-name {
  margin: 0;
}
.report__dim-result {
  margin: 0;
  display: flex;
  align-items: baseline;
  gap: var(--space-3);
}
.report__dim-tier {
  color: var(--be-text-2);
}
/* The don't-know disclosure: a caption under the row, full width so it reads as a
   note on the score rather than a third column. Regular weight even on a capping
   row, which is semibold. */
.report__dim-coverage {
  flex-basis: 100%;
  margin: var(--space-1) 0 0;
  color: var(--be-text-2);
  font-size: var(--fs-caption);
  line-height: var(--lh-caption);
  font-weight: var(--fw-regular);
}
.report__value-gap h3,
.report__recommendations h3 {
  margin: 0 0 var(--space-3);
}
/* The Thesis (JOB-AUS1-0001kQ): the value-gap narrative, then the cap sentence. */
.report__value-gap p {
  margin: 0;
}
.report__value-gap [data-thesis-cap] {
  margin-top: var(--space-3);
}

/* ── Value-gap figures (review, 2026-07-30) ───────────────────────────────
 * The value curve and the weakest-link floor chart, each under the paragraph it
 * draws. Server-rendered SVG (src/assessment_api/value_gap_charts.py) — same as
 * the radar, no chart library and no build step.
 *
 * Full column width, like the radar region. Not a stylistic choice: the floor
 * chart labels all six Dimensions on its X axis in full — CONTEXT.md's ubiquitous
 * language, never initialled — and at a narrower measure adjacent names such as
 * "Process & Operating Model" and "Governance & Risk" collide even wrapped to two
 * lines. The figures are wide and short where the radar is a large square, so they
 * read as supporting figures rather than as competing centrepieces. */
.report__figure {
  margin: var(--space-4) 0 0;
  /* Same legibility floor as the radar, same reason: below the min-width set on the
   * charts their labels stop being readable, so the figure scrolls in its own box
   * and the page body never scrolls sideways. */
  overflow-x: auto;
}
.report__figure figcaption {
  margin-top: var(--space-2);
  color: var(--be-text-2);
  font-size: var(--fs-caption);
  line-height: var(--lh-caption);
}
.value-curve,
.floor-chart {
  display: block;
  width: 100%;
  /* The viewBox is 840 wide (value_gap_charts.py), which is the content column at an
   * ordinary desktop window. Capped there so the type never renders LARGER than the
   * page's caption size on a wide screen, floored below it so it never renders much
   * smaller on a narrow one — and the floor is under 840 so an ordinary window never
   * scrolls a figure. */
  min-width: 700px;
  max-width: 840px;
  height: auto;
}

/* The curve. --data-1 is the design system's first chart series and the radar's
 * polygon colour, which is deliberate: both figures plot the same Respondent, so
 * they are the same series seen two ways. */
.value-curve__axis {
  stroke: var(--fe-border);
  stroke-width: 1;
}
.value-curve__axis-label,
.value-curve__tier {
  fill: var(--be-text-2);
  font-size: var(--fs-caption);
}
/* The Y axis, which since JOB-AUS1-0001mL is the RES_0016 productivity ranges
 * named in the gutter, level with the rung each one belongs to. A label and
 * nothing else — shaded bands across the plot were drawn and dropped on review
 * the same day, because a band has a height, a height invites measuring, and
 * the rungs sit where the compounding claim puts them rather than at a
 * percentage anyone could scale off. */
.value-curve__band-label {
  fill: var(--be-text-2);
  font-size: var(--fs-caption);
}
.value-curve__band-label.is-current {
  fill: var(--ink);
  font-weight: var(--fw-semibold);
}
/* Where they have not got to yet — the figure's whole argument, so it is a fill
 * rather than an outline, and faint enough that the line stays the subject. */
.value-curve__ahead {
  fill: var(--accent-fill);
  fill-opacity: 0.35;
}
.value-curve__line {
  stroke: var(--data-1);
  stroke-width: 2;
  stroke-linejoin: round;
  stroke-linecap: round;
}
.value-curve__marker {
  fill: var(--data-1);
  fill-opacity: 0.45;
}
.value-curve__marker.is-current {
  fill: var(--data-1);
  fill-opacity: 1;
}
/* The rung a single lift would reach. Same ring grammar as the radar's capping
 * mark: on both figures a ring means "this point, here". */
.value-curve__counterfactual {
  fill: none;
  stroke: var(--status-warning);
  stroke-width: 2;
  stroke-dasharray: 3 3;
}
.value-curve__tier.is-current,
.value-curve__you {
  fill: var(--accent-text);
  font-size: var(--fs-caption);
  font-weight: var(--fw-semibold);
}

/* The floor chart. The published ladder is the Y axis, so its gridlines are
 * labelled — unlike the radar's rings, this figure's entire claim is about one
 * particular height. */
.floor-chart__threshold {
  stroke: var(--fe-border);
  stroke-width: 1;
}
.floor-chart__threshold-label,
.floor-chart__tick {
  fill: var(--be-text-2);
  font-size: var(--fs-caption);
}
.floor-chart__floor {
  stroke: var(--status-warning);
  stroke-width: 2;
  stroke-dasharray: 5 4;
}
.floor-chart__floor-label {
  fill: var(--status-warning-ink);
  font-size: var(--fs-caption);
  font-weight: var(--fw-semibold);
}
.floor-chart__line {
  stroke: var(--data-1);
  stroke-width: 2;
  stroke-linejoin: round;
}
.floor-chart__point {
  fill: var(--data-1);
}
.floor-chart__cap-ring {
  fill: none;
  stroke: var(--status-warning);
  stroke-width: 2;
}

/* ── Interrogating a figure (JOB-AUS1-0001mI) ─────────────────────────────
 * The hover layer the three figures share. Review, 2026-07-31: "Improve the hover
 * interactivity for the graphs... Make sure it's a proper visualisation, like
 * you'd see in Tableau or Power BI."
 *
 * NO JAVASCRIPT ANYWHERE IN IT. Everything below is :hover on a server-rendered
 * <g>, which is why it works on the frozen share copy — _page.html wraps that,
 * and that wrapper loads stylesheets and, apart from the help modal, no
 * behaviour. It is also why the layer is painted last in each SVG: there is no
 * z-index in SVG, so a scrim can only fade what came before it (chart_hover.py).
 *
 * At rest every part of a hover group is transparent except its hit region,
 * which stays in the markup with a transparent fill so it can still catch a
 * pointer. On hover the group's own scrim fades the figure and its marks come
 * up over it. `:not(.chart-hit)` is what keeps the hit region out of that
 * fade — it must be there whether or not anything is hovered. */
.chart-hit {
  fill: transparent;
  cursor: pointer;
}
.radar__hover-group > :not(.chart-hit),
.chart-hover-group > :not(.chart-hit) {
  opacity: 0;
  pointer-events: none;
  transition: opacity 140ms ease;
}
.radar__hover-group:hover > :not(.chart-hit),
.chart-hover-group:hover > :not(.chart-hit) {
  opacity: 1;
}
/* Not to 1: the scrim is a wash over the figure, not a lid on it. The rings, the
 * other Dimensions and the shape of the profile stay legible behind the one being
 * read, which is the whole difference between focusing a chart and blanking it. */
.radar__hover-group:hover > .chart-scrim,
.chart-hover-group:hover > .chart-scrim {
  opacity: 0.76;
}
/* THE VALUE CURVE NEEDS A THINNER WASH, and it is the rule above that says why.
 * 0.76 was set against the radar, whose filled area is `--green` at 0.55 with a
 * 2px stroke — plenty survives a 24% pass. This figure's shaded region is
 * `--data-1` at low alpha, and measured on screen 2026-09-10 the same 0.76 took
 * rgb(200 230 201) to roughly rgb(242 248 242): white. The AHEAD-SHADING IS THE
 * FIGURE'S WHOLE ARGUMENT — "the shaded area is the part of the curve still
 * ahead of you", says its own caption — so at 0.76 pointing at a rung deleted
 * the claim the rung is evidence for, and the figure read as disabled rather
 * than focused. That is precisely the lid the comment above refuses.
 *
 * Scoped to this figure rather than lowered globally: the radar and the floor
 * chart were both checked at 0.76 on the same day and both hold, and weakening
 * their focus to rescue a third figure would be paying for it twice. */
.value-curve__hover .chart-hover-group:hover > .chart-scrim {
  opacity: 0.5;
}
.chart-scrim {
  fill: var(--fe-surface);
}
/* The crosshair down to the baseline on the two line charts. Dashed, so it never
 * reads as a plotted rule the way the floor chart's own dashed floor does — that
 * one is the same weight as the data and this one is scaffolding. */
.chart-guide {
  stroke: var(--fe-text-2);
  stroke-width: 1;
  stroke-dasharray: 3 3;
}
/* The card. Ink on white everywhere else on this page, so the tooltip inverts:
 * it is the one surface that has to read as floating above the figure rather
 * than as part of it. */
.chart-tip__box {
  fill: var(--ink);
}
.chart-tip__line {
  fill: var(--surface);
}
.chart-tip__line--title {
  font-weight: var(--fw-semibold);
}
/* The qualifications — the don't-know coverage, the capping mark. Muted by
 * opacity rather than a colour token: there is no "secondary ink on ink" in the
 * design system, and inventing a hex here would be the one place this file
 * stopped reading tokens. Set on the text, not the group, so it survives the
 * group's own reveal. */
.chart-tip__line--note {
  opacity: 0.74;
}

/* The radar's highlight: this Dimension's slice of the filled area, its vertex
 * and its name, all at full strength over the scrim. The slice is the piece of
 * the shape the score owns — light it and the answer to "why is there a dent
 * there" is the dent itself. It used to be joined by a lead line from the centre
 * out to the vertex; that was dropped on review (JOB-AUS1-0001mL), because the
 * slice already points at its own Dimension and the lead put a second line
 * through the middle of the shape competing with it. */
.radar__slice {
  fill: var(--accent);
  fill-opacity: 0.55;
  stroke: var(--accent);
  stroke-width: 2;
  stroke-linejoin: round;
}
/* A halo in the card's own surface colour, so the dot stays crisp against
 * whatever ring or spoke it happens to be sitting on. */
.radar__focus {
  fill: var(--accent);
  stroke: var(--fe-surface);
  stroke-width: 2.5;
}
.value-curve__focus,
.floor-chart__focus {
  fill: var(--data-1);
  stroke: var(--fe-surface);
  stroke-width: 2.5;
}
.radar__label.is-focus,
.value-curve__tier.is-focus,
.floor-chart__tick.is-focus {
  fill: var(--ink);
  font-weight: var(--fw-semibold);
}

/* A touch screen has no hover, so the whole layer is dead weight there — and
 * worse than dead: six transparent sectors over the radar would swallow taps.
 * Removed outright, and the score list below comes back visible in the same
 * breath, so a phone is never left with a figure it cannot read the numbers off. */
@media (hover: none) {
  .radar__hover,
  .value-curve__hover,
  .floor-chart__hover {
    display: none;
  }
}

/* Recommendations (JOB-AUS1-0001kP) — the prioritised gaps, then any sustains.
 * Page scaffolding for the report frame; reads only design-system tokens. */
/* Redesigned 2026-07-31 (JOB-AUS1-0001mL). The section was three stacked
 * paragraphs per item, two of them in muted secondary, repeated three times —
 * so the eyebrow, the move and the explanation all carried the same visual
 * weight and nothing said which was the point. Review: "I don't like that
 * var(--be-text-2) is being used in this way... this page isn't clear and
 * doesn't communicate what it's trying to."
 *
 * The rule now: MUTED IS FOR WHAT SITS BEHIND THE CONTENT, never the content.
 * The Dimension label, the sustains, the provenance and the rule version are
 * muted; the move and its explanation are ink. */
.report__recommendations-lede {
  margin: 0 0 var(--space-5);
  max-width: 62ch;
}
/* How the number was reached, printed under the Dimensions that cap the Tier
 * (JOB-AUS1-0002Qv). Ink, not muted, and measured to the same 62ch as the other
 * report prose: the reference PRD's AC3 lists the published calculation among
 * what is visible on the ungated report, so it is content and takes the register
 * content takes. The muted rules in this file are for what sits behind it — the
 * rule version, the provenance line. */
.report__calculation {
  margin: var(--space-4) 0 0;
  max-width: 62ch;
}
.report__recommendation-list {
  list-style: none;
  counter-reset: recommendation;
  display: flex;
  flex-direction: column;
  gap: var(--space-6);
  margin: 0;
  padding: 0;
}
.recommendation {
  display: flex;
  flex-direction: column;
  gap: var(--space-1);
  max-width: 68ch;
}
/* Number the prioritised gaps — the order IS the published rule (weakest
 * Dimension first, because the weakest is what caps the headline), so the
 * numeral states a fact rather than decorating a list. Sized and coloured to be
 * read as an ordinal: green, because that is the one accent this report has, and
 * hung in its own column so the text block stays a clean edge. */
.report__recommendation-list > .recommendation {
  counter-increment: recommendation;
  position: relative;
  padding-left: var(--space-7);
}
.report__recommendation-list > .recommendation::before {
  content: counter(recommendation);
  position: absolute;
  top: -2px;
  left: 0;
  width: var(--space-6);
  color: var(--accent-text);
  font-size: var(--fs-h3);
  line-height: var(--lh-h3);
  font-weight: var(--fw-bold);
  font-variant-numeric: tabular-nums;
}
.recommendation__dimension {
  margin: 0;
  color: var(--be-text-2);
  font-size: var(--fs-caption);
  line-height: var(--lh-caption);
}
/* The move itself — the line the reader acts on, so it is set as a subhead
 * rather than as bold body. */
.recommendation__headline {
  margin: 0;
  font-size: var(--fs-h3);
  line-height: var(--lh-h3);
  font-weight: var(--fw-semibold);
}
.recommendation__body {
  margin: var(--space-2) 0 0;
}
/* What the move gets you, in the report's own ladder vocabulary. This replaced
 * "ASSISTED → AUGMENTED" in the eyebrow: the destination is the useful half, and
 * where they are today is already on the radar, in the score list and on the
 * value curve. */
.recommendation__outcome {
  margin: var(--space-3) 0 0;
  color: var(--be-text-2);
  font-size: var(--fs-body-sm);
  line-height: var(--lh-body-sm);
}
.recommendation__outcome-tier {
  color: var(--accent-text);
  font-weight: var(--fw-semibold);
}
/* A different kind of thing from the moves above — nothing to do, something to
 * protect — so it reads as an aside rather than as items 4 to 6. */
.report__sustain {
  display: flex;
  flex-direction: column;
  gap: var(--space-4);
  margin-top: var(--space-7);
  padding-top: var(--space-5);
  border-top: 1px solid var(--fe-border);
}
.report__sustain-heading {
  margin: 0;
  font-size: var(--fs-body);
  line-height: var(--lh-body);
  font-weight: var(--fw-semibold);
}
.report__sustain-list {
  list-style: none;
  display: flex;
  flex-direction: column;
  /* Wider than the gap inside an item, so two sustains read as two things. */
  gap: var(--space-5);
  margin: 0;
  padding: 0;
}
.recommendation--sustain .recommendation__headline {
  font-size: var(--fs-body);
  line-height: var(--lh-body);
}
.recommendation--sustain .recommendation__body {
  color: var(--be-text-2);
}
/* The single broad provenance line; per-recommendation citations are held back for
 * the Detailed Report (FR3.5). */
.report__provenance {
  margin: var(--space-5) 0 0;
  color: var(--be-text-2);
  font-size: var(--fs-caption);
}
/* The conversion ladder (JOB-AUS1-0001kV). Since JOB-AUS1-0001mI the rungs are
 * buttons in the report's header and this region is only where a TAKEN rung lands
 * — review_requested.html and detailed_report_sent.html swap themselves in over
 * it. It sits at the top of the report body, directly under the buttons, so a
 * confirmation appears where the press happened.
 *
 * Empty until then, and an empty region must not paint: the rule and the spacing
 * below would otherwise draw a stray line across the top of every report nobody
 * has acted on. */
.report__ladder:empty {
  display: none;
}
.report__ladder {
  padding-bottom: var(--space-5);
  border-bottom: 1px solid var(--be-border);
}
.report__ladder h3 {
  margin: 0 0 var(--space-3);
}
.report__ladder p {
  margin: 0;
}
.report__ladder .report__ladder-secondary {
  margin-top: var(--space-5);
  color: var(--be-text-2);
}

/* The case for taking a rung, on the last section only (_ladder_pitch.html). It
 * is prose, not a control — the buttons are in the header — so it is separated
 * from the recommendations above it by a rule, the way the ladder region used to
 * be from the report. */
.report__pitch {
  margin-top: var(--space-6);
  padding-top: var(--space-5);
  border-top: 1px solid var(--be-border);
}
.report__pitch h3 {
  margin: 0 0 var(--space-3);
}
.report__pitch p {
  margin: 0;
}
.report__pitch-secondary {
  margin-top: var(--space-5);
  color: var(--be-text-2);
}

/* The Email Gate under the result (report.hbs, TASK-AUS1-000CZd). Same
 * separated-block treatment as .report__pitch above, and for the same reason:
 * it follows the result rather than interrupting it. The rule above it is
 * doing real work here — the report is ungated (ADR_0004), so the gate has to
 * read as something offered after the answer, never as a step in front of it.
 * The form is left at its natural width; a full-bleed control would make one
 * email field look like the point of the screen. */
/* Grouped with .report__share below it, which takes the identical treatment
 * for the identical reason: they are two offers stacked at the end of one
 * section, and a rule above only the first would read as one block containing
 * both. Grouping rather than repeating is this file's own habit — see
 * `.report__value-gap h3, .report__recommendations h3` above. */
.report__claim,
.report__share {
  margin-top: var(--space-6);
  padding-top: var(--space-5);
  border-top: 1px solid var(--be-border);
}
.report__claim h3,
.report__share h3 {
  margin: 0 0 var(--space-3);
}
.report__claim .screen__intro,
.report__share .screen__intro {
  margin: 0 0 var(--space-4);
}
.report__claim .field {
  max-width: 24rem;
  margin-bottom: var(--space-4);
}

/* The share action under the Email Gate (report.hbs, TASK-AUS1-000DDC). Its
 * block, heading and intro rules are grouped onto .report__claim above, because
 * the treatment is identical and has to be: these are two offers stacked at the
 * end of one section, and a rule above only the first would read as one block
 * containing both.
 *
 * It is the LAST block in "What to do next" on purpose. The gate is the
 * report's primary conversion and this is the secondary action, so putting
 * share between the moves and the gate would interrupt the one path the section
 * exists to open.
 *
 * The minted link lands INSIDE this element — assessment.share_mint answers a
 * browser with share_link.hbs, swapped innerHTML into #assessment-share — so
 * the grouped heading and intro rules style BOTH states, the offer and the
 * answer, and only the link itself needs anything of its own.
 *
 * That one rule is below: .field__help's muted caption register, because the
 * link is the answer to a press and not a control. It wraps rather than
 * scrolls — a share URL is one long unbroken token, and at a phone width an
 * unbroken word would push the report card sideways. */
.report__share .field__help {
  margin: 0;
  overflow-wrap: anywhere;
}

/* Still used by report_unavailable.html, which has actions and no header bar to
 * put them in. The live report's own actions moved into .report__cta-bar and
 * .screen__footer (JOB-AUS1-0001mI). */
.report__actions {
  display: flex;
  gap: var(--space-3);
  flex-wrap: wrap;
}
.report-incomplete__list {
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
  margin: var(--space-4) 0 0;
  padding-left: var(--space-5);
}
.report-incomplete__note {
  color: var(--be-text-2);
  font-size: var(--fs-caption);
}

/* Welcome back's status board is the vendored .kv list. Two overrides, because
 * .kv emphasises its value and mutes its term, and here the Dimension is the
 * thing being scanned and the status is the note on it. */
.welcome-back__row dt {
  color: var(--ink);
}
.welcome-back__status {
  font-weight: var(--fw-regular);
  color: var(--be-text-2);
}

/* ── Surfaces that were styled by nothing ─────────────────────────────────
 * Every class below was in the markup with no rule anywhere — in the vendored
 * design system, in this file, or in tokens.css — so it rendered as a browser
 * default. Found by auditing every class in the templates against the CSS
 * actually loaded (frontend-design review, 2026-07-30). */

/* The score digits: the number the whole product exists to produce, previously
 * inline body text indistinguishable from the Tier label beside it. */
.report__score {
  font-weight: var(--fw-semibold);
  font-variant-numeric: tabular-nums;
}
.report__version {
  margin: var(--space-4) 0 0;
  color: var(--be-text-2);
  font-size: var(--fs-caption);
}

/* REMOVED 2026-08-21, JOB-AUS1-0002Qv: `.report__scoring-link`.
 *
 * It styled the porting reference's <noscript> link to
 * `/v1/assessment/scoring-rule` — a public, sessionless page publishing the full
 * rule, its threshold table and a worked example. ADR_010 has no such route:
 * every assessment request is an `assessment.*` event on the platform
 * dispatcher, and there is no `/v1/assessment/*` REST route anywhere. So this
 * rule could never have had a matching element here, and it read as a seam
 * waiting to be filled — the bug job that removed it initially took it for one.
 *
 * The reference reached that link by a reversal of its own: on 2026-07-31 it
 * moved the calculation OFF the report and behind an (i) modal, with this link
 * as the no-script route to the same page. Its own template comment records that
 * this deviated from its AC3, and that the deviation was written down only in
 * template comments. We did not port the reversal — the destination it depends
 * on does not exist here — so the calculation stays ON the report, which is what
 * AC3 asked for in the first place. It is rendered by as-core's `report.hbs` and
 * styled by `.report__calculation` above. */

/* The published scoring rule's two tables (_scoring_rule.html). Browser
 * defaults before this: no cell padding, no header treatment, no alignment.
 * The worked example's min-content width was 380px, which forced its card to
 * 496px inside a 390px viewport — the sole cause of the report's horizontal
 * scroll on a phone. Hence the scroll wrapper AND the stack below 40rem. */
.rule-steps {
  display: flex;
  flex-direction: column;
  gap: var(--space-4);
  margin: var(--space-5) 0 0;
  padding-left: var(--space-5);
}
.rule-thresholds,
.rule-worked-example {
  width: 100%;
  margin-block: var(--space-4);
  border-collapse: collapse;
  font-size: var(--fs-body-sm);
}
.rule-thresholds th,
.rule-thresholds td,
.rule-worked-example th,
.rule-worked-example td {
  padding: var(--space-3);
  text-align: left;
  vertical-align: top;
  border-bottom: 1px solid var(--fe-border);
}
.rule-thresholds thead th,
.rule-worked-example thead th {
  font-size: var(--fs-caption);
  letter-spacing: var(--tracking-eyebrow);
  text-transform: uppercase;
  color: var(--be-text-2);
}
.rule-thresholds tbody th,
.rule-worked-example tbody th {
  font-weight: var(--fw-semibold);
}
.rule-thresholds td,
.rule-worked-example td:not(:first-child) {
  font-variant-numeric: tabular-nums;
}
.rule-worked-example__note {
  display: block;
  color: var(--be-text-2);
  font-size: var(--fs-caption);
}
.rule-version {
  margin: var(--space-5) 0 0;
  color: var(--be-text-2);
  font-size: var(--fs-caption);
}
/* The worked example is four columns of real data; below 40rem it stacks to
   labelled rows rather than scrolling sideways. */
@media (max-width: 40rem) {
  .rule-worked-example thead {
    display: none;
  }
  .rule-worked-example tr {
    display: grid;
    gap: var(--space-1);
    padding-block: var(--space-3);
    border-bottom: 1px solid var(--fe-border);
  }
  .rule-worked-example th,
  .rule-worked-example td {
    padding: 0;
    border: 0;
  }
  .rule-worked-example td::before {
    content: attr(data-label) ": ";
    color: var(--be-text-2);
  }
}

/* A capability link the Respondent has to keep: monospace, bordered, and
 * wrapping. Uses the vendored .code-block, whose .code-block__code is
 * white-space:pre — right for code, wrong for a 78-character URL in a narrow
 * column, where it becomes a second scroll region. This modifier belongs
 * upstream in the library if a copy button is ever added here. */
.code-block--wrap .code-block__code {
  white-space: normal;
  overflow-wrap: anywhere;
}

/* ── The Detailed Report as a document ────────────────────────────────────
   JOB-AUS1-0001kY / TASK-AUS1-000Cby. The one gated artifact is structured as a
   report rather than a longer web page (docs/plans/detailed-report.md §15): a
   title block, a contents list, numbered sections, a captioned figure and table,
   numbered references, and a stated scope.

   These rules are additive and scoped to .report--document, so nothing here can
   reach the free Maturity Report — the same separation the template keeps by not
   including _maturity_report.html.

   It is also the one surface built to be printed. FR1.8's "or downloadable" is
   met for V1 by the print rules at the end of this block; generated PDF is
   JOB-AUS1-0001mJ.

   COLOUR HERE READS REAL TOKENS (TASK-AUS1-000EXJ, 2026-09-14). It used to read
   an undefined `--color-*` namespace — `--color-text`, `--color-text-muted`,
   `--color-border` and `--color-accent` are declared nowhere in tokens.css, so
   all twelve references fell through to their hard-coded fallbacks and the block
   rendered `#1a1a1a` / `#5f5a54` / `#d9d5cf` / `#b8442a` in every mode. That was
   a shipped defect before the site went dark: `#b8442a` is a terracotta that
   appears in no palette this brand has ever had, so the capping marker — the
   report's single most consequential signal — was a colour nobody chose. Going
   dark is what made it visible rather than what caused it.

   The mapping follows what the rest of this file already uses, so the document
   and the free Maturity Report age together: muted text is `--be-text-2`, rules
   are `--fe-border`, the title rule is `--ink`. The accent splits by use — the
   two 3px capping markers are non-text and take `--accent`, while `.doc__flag`
   is 15px bold copy and takes `--accent-text`, the contrast-safe variant
   (6.22:1 light, 8.88:1 dark).

   The report is DARK, per ruling 3 (docs/plans/dark-site.md §Decisions), which
   went against the recommendation that this block stay a light "paper" island.
   The consequence the ruling names — a dark report printed straight is a black
   page, and this is the one surface that LEAVES the site — is handled in the
   @media print block at the end. */

.report--document {
  --doc-rule: var(--fe-border);
}

.doc__title-block {
  border-bottom: 3px solid var(--ink);
  padding-bottom: var(--space-4, 1rem);
  margin-bottom: var(--space-5, 1.5rem);
}

.doc__title {
  margin: 0.25rem 0 0.5rem;
  font-size: clamp(1.75rem, 4vw, 2.5rem);
  line-height: 1.15;
}

.doc__subtitle {
  margin: 0 0 var(--space-4, 1rem);
  font-size: 1.0625rem;
}

/* The title block's key/value strip: it is what tells a reader which edition
   they are holding, so it stays legible rather than shrinking to fine print. */
.doc__meta {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(13rem, 1fr));
  gap: var(--space-3, 0.75rem) var(--space-5, 1.5rem);
  margin: 0;
  font-size: 0.9375rem;
}

.doc__meta dt {
  font-weight: 700;
  letter-spacing: 0.02em;
  text-transform: uppercase;
  font-size: 0.75rem;
  color: var(--be-text-2);
}

.doc__meta dd {
  margin: 0.125rem 0 0;
}

.doc__contents {
  border: 1px solid var(--doc-rule);
  padding: var(--space-4, 1rem) var(--space-5, 1.5rem);
  margin-bottom: var(--space-6, 2rem);
}

.doc__contents-heading {
  margin: 0 0 0.5rem;
  font-size: 0.8125rem;
  text-transform: uppercase;
  letter-spacing: 0.08em;
}

.doc__contents-list {
  margin: 0;
  padding-left: 1.25rem;
}

.doc__contents-list li {
  margin: 0.125rem 0;
}

.doc__section {
  margin-bottom: var(--space-7, 2.5rem);
}

.doc__section-heading {
  border-bottom: 1px solid var(--doc-rule);
  padding-bottom: 0.35rem;
  margin-bottom: var(--space-4, 1rem);
}

.doc__subsection {
  margin-bottom: var(--space-5, 1.5rem);
}

.doc__subsection-heading {
  margin: 0 0 0.4rem;
  font-size: 1.0625rem;
}

/* The chapter's one-sentence finding, and the note that the chapter is about the
   industry rather than the reader. Both are load-bearing for honesty, so they
   read as document furniture rather than as body copy. */
.doc__standfirst {
  font-size: 1.125rem;
  line-height: 1.5;
  margin: 0 0 0.5rem;
}

.doc__note {
  font-size: 0.875rem;
  color: var(--be-text-2);
  margin: 0 0 var(--space-4, 1rem);
}

.doc__summary-list {
  padding-left: 1.1rem;
}

.doc__summary-list li {
  margin-bottom: 0.5rem;
}

.doc__lead {
  font-size: 1.0625rem;
}

.doc__headline-tier {
  display: block;
  font-size: 1.5rem;
  font-weight: 700;
}

.doc__figure {
  margin: var(--space-5, 1.5rem) 0;
  /* The value curve and the floor chart carry min-width: 700px so their labels stay
   * readable. Without this the artifact's page body would scroll sideways on a
   * narrow window instead of the figure scrolling in its own box — the same reason
   * .report__figure has it. */
  overflow-x: auto;
}

.doc__figure figcaption,
.doc__table caption {
  font-size: 0.875rem;
  color: var(--be-text-2);
  text-align: left;
  margin-top: 0.5rem;
}

.doc__table caption {
  margin: 0 0 0.5rem;
  caption-side: top;
}

.doc__table {
  width: 100%;
  border-collapse: collapse;
  font-size: 0.9375rem;
}

.doc__table th,
.doc__table td {
  text-align: left;
  padding: 0.5rem 0.75rem 0.5rem 0;
  border-bottom: 1px solid var(--doc-rule);
  vertical-align: top;
}

.doc__table thead th {
  border-bottom-width: 2px;
  font-size: 0.75rem;
  text-transform: uppercase;
  letter-spacing: 0.05em;
}

/* The capping Dimension is the report's single most consequential row, and the
   marker is a rule rather than a colour so it survives greyscale printing. */
.doc__table tr.is-capping th[scope="row"] {
  box-shadow: inset 3px 0 0 0 var(--accent);
  padding-left: 0.6rem;
}

.dimension-reading.is-capping {
  border-left: 3px solid var(--accent);
  padding-left: var(--space-4, 1rem);
}

.dimension-reading__result {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem 1rem;
  align-items: baseline;
  margin: 0 0 0.5rem;
  font-size: 0.9375rem;
}

.dimension-reading__result .doc__score {
  font-size: 1.25rem;
  font-weight: 700;
}

.dimension-reading__result .doc__ladder,
.dimension-reading__result .doc__flag {
  color: var(--be-text-2);
}

.dimension-reading__result .doc__flag {
  font-weight: 700;
  color: var(--accent-text);
}

/* A dated row is a timeline; the same content as a paragraph is not. */
.doc__timeline {
  border-left: 3px solid var(--doc-rule);
  padding-left: var(--space-4, 1rem);
  margin: var(--space-4, 1rem) 0;
}

.doc__timeline-row {
  margin-bottom: var(--space-4, 1rem);
}

.doc__timeline dt {
  font-weight: 700;
  margin-bottom: 0.15rem;
}

.doc__timeline dd {
  margin: 0;
}

.doc__citation {
  font-size: 0.875rem;
  color: var(--be-text-2);
  margin: 0.5rem 0 0;
}

.doc__references {
  font-size: 0.9375rem;
  padding-left: 1.5rem;
}

.doc__references li {
  margin-bottom: 0.4rem;
}

.doc__reference-id {
  color: var(--be-text-2);
}

.doc__footer {
  border-top: 1px solid var(--doc-rule);
  margin-top: var(--space-6, 2rem);
  padding-top: var(--space-3, 0.75rem);
  font-size: 0.8125rem;
  color: var(--be-text-2);
}

/* Printing. Browser print engines do not implement the CSS features that place
   varying content in each page's margin box (GCPM running elements, footnotes),
   so per-page footnotes are not reachable from here — which is why sources sit
   at the foot of the section that carries them, and why the same data shape lets
   JOB-AUS1-0001mJ's paginator promote them to real footnotes later
   (docs/plans/detailed-report.md §15.13 R9). */
@media print {
  @page {
    margin: 18mm 16mm 20mm;
  }

  /* The site chrome does not go on the paper. A printed artifact is the
     document and nothing else — the topbar's nav and the footer's four link
     columns are affordances for a browser, and on paper they are dead text
     wrapped around the thing the reader actually wanted.

     They are also unreadable, which is what makes this a correctness rule
     rather than a tidiness one. The footer and the status bar are dark in BOTH
     modes by design (tokens.css: "the footer is how the page closes, not a
     theme"), so they carry the dark values locally and sit outside the
     `@media screen` scoping that sends the rest of the palette light for print.
     Measured 2026-09-14 on this shell: 68px of topbar and 341px of footer
     printing at 2.12:1 and 1.10:1 against white paper — about a third of a page
     of furniture nobody can read. Hiding beats re-theming: there is no version
     of a nav column that earns its space on a printed report. */
  .topbar,
  .footer {
    display: none;
  }

  .report--document {
    border: 0;
    box-shadow: none;
    padding: 0;
    max-width: none;
  }

  .doc__contents {
    break-after: page;
  }

  /* A heading stranded at the foot of a page, or a timeline split across two,
     is what makes a printed document feel unmade. */
  .doc__section-heading,
  .doc__subsection-heading {
    break-after: avoid;
  }

  .doc__subsection,
  .doc__timeline-row,
  .doc__figure,
  .doc__table,
  .recommendation {
    break-inside: avoid;
  }

  .doc__section {
    break-inside: auto;
  }

  /* On paper there is no scrolling to fall back on, so the charts' 700px floor
     would be clipped by the box that holds them: an A4 column at these margins is
     narrower than that. Let them scale to the page instead. The labels shrink with
     the figure rather than disappearing, which is the trade a printed page makes. */
  .doc__figure {
    overflow-x: visible;
  }
  .doc__figure .value-curve,
  .doc__figure .floor-chart {
    min-width: 0;
  }

  #s-references {
    break-before: page;
  }

  /* Links print as text; the reference list already carries the document
     names, so a bare URL would be noise. */
  .doc__citation a {
    text-decoration: none;
    color: inherit;
  }

  .doc__footer {
    position: fixed;
    bottom: 0;
    left: 0;
    right: 0;
  }
}

/* ── Column canvas (Declarative Column Builder, DPAT-AUS1-000DLu) ──────────
 * Phase 6 Task 1 (JOB-AUS1-0001t1). A page-local flex row wrapping the
 * VENDORED .app-layout grid, with a column either side of it. Page-local on
 * purpose: .app-layout is a two-track grid owned by components.css and locked
 * 2026-07-30, and this file has already been burned once for redefining a
 * vendored class (see the .app-layout note above). Nothing here touches it —
 * the grid is a flex ITEM now, and is otherwise untouched.
 *
 * Both columns are hidden at rest, so with one visible child this row lays out
 * exactly as the bare .bs-container did.
 *
 * The sentinel: an element that generates no box (display:contents) and exists
 * only so a sibling selector can collapse the column beside it. The server
 * flips a column by OOB-swapping ~80 bytes of sentinel; the column's children
 * are never re-sent. `.col-state` and its data-state values are the wire
 * contract as-core's column_ops writes — do not rename them.
 *
 * DELIBERATELY NOT PORTED from eligtas: the companion
 * `#col:has(#col-body:empty) { display: none }` rule. That is a SECOND hiding
 * mechanism on top of the sentinel, and it works there only because their
 * detail column is always replace_col'd before show_col. A chat panel is meant
 * to open on an empty state, so that rule would make a show_col on an unfilled
 * body flip the sentinel visible and leave the column hidden with no error —
 * the same silent class of failure as DPAT-AUS1-000DRV. One hiding mechanism:
 * the sentinel. */
.assessment-canvas {
  display: flex;
  align-items: stretch;
  /* Containing block for the guidance peek, which overlays this row rather than
     taking a track in it (see the end of this file). The columns' own grips are
     positioned against .assessment-col, so nothing else changes. */
  position: relative;
}
/* The wizard frame keeps its own max-width and auto margins; it just yields
   the columns' fixed tracks first. min-width:0 so a wide screen inside it
   cannot push the columns off. */
.assessment-canvas > .app-layout {
  flex: 1 1 auto;
  min-width: 0;
}

.col-state {
  display: contents;
}
.col-state[data-state="hidden"] + .assessment-col {
  display: none;
}

.assessment-col {
  display: flex;
  flex-direction: column;
  background: var(--fe-surface);
  overflow-y: auto;
  /* Explicit: per the overflow spec, overflow-y:auto alone computes overflow-x
     to auto as well, so a pane that never scrolls sideways can still earn a
     horizontal scrollbar from a few stray px. */
  overflow-x: hidden;
  /* Containing block for the absolutely-positioned resize grip. */
  position: relative;
}

#assessment-col-chat {
  flex: 0 0 300px;
  border-right: 1px solid var(--fe-border);
}
#assessment-col-display {
  flex: 0 0 340px;
  border-left: 1px solid var(--fe-border);
}

.assessment-col__header {
  padding: var(--space-3) var(--space-4);
  border-bottom: 1px solid var(--fe-border);
  font: var(--fw-semibold) var(--fs-eyebrow)/var(--lh-eyebrow) var(--font-sans);
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--fe-text-2);
  flex-shrink: 0;
}
.assessment-col__body {
  flex: 1;
  padding: var(--space-4);
  overflow-y: auto;
}

/* Drag-to-resize grip, injected by the inline script in index.html so it is a
   SIBLING of the -body slot and survives every innerHTML swap into it. It hugs
   the column's inner edge: the right edge for the left column, the left edge
   for the right one. The width maths is mirrored to match (see the script).

   INSIDE the column, not overhanging it. eligtas hangs its grip 3px past the
   edge; with the overflow-x:hidden above — which their column carries too —
   half the target is clipped and the grip's own midpoint is unclickable.
   Measured 2026-08-15: a synthetic drag from the grip's centre moved the right
   column and did nothing at all to the left one. Sitting flush gives the whole
   6px as a target and leaves nothing to clip. */
.assessment-col__resize {
  position: absolute;
  top: 0;
  width: 6px;
  height: 100%;
  cursor: col-resize;
  z-index: 5;
}
#assessment-col-chat .assessment-col__resize {
  right: 0;
}
#assessment-col-display .assessment-col__resize {
  left: 0;
}
.assessment-col__resize:hover,
body.assessment-resizing .assessment-col__resize {
  background: var(--accent);
  opacity: 0.4;
}
/* Kill text selection and hold the resize cursor for the whole drag. */
body.assessment-resizing {
  cursor: col-resize;
  user-select: none;
}

/* Narrow screens: the wizard frame already collapses its own two tracks to one
   under 720px, so three side-by-side columns cannot hold either. Stack them and
   drop the grips — a dragged flex-basis is meaningless in a column flow, so the
   script's inline flex-basis is overridden rather than left to fight. */
@media (max-width: 60rem) {
  .assessment-canvas {
    flex-direction: column;
  }
  .assessment-col {
    flex: 0 0 auto !important;
    border: 0;
    border-block-end: 1px solid var(--fe-border);
  }
  .assessment-col__resize {
    display: none;
  }
}

/* --------------------------------------------------------------------------
   Guidance dialog   (TASK-AUS1-000DDM; DEF-01 and DEF-10)

   THERE IS ALMOST NOTHING HERE, WHICH IS THE POINT. What stood in this place
   was a ~90-line `.guidance-peek` surface: fixed to the viewport, 340px, pinned
   to the inline-end edge, translated off-screen at rest, with a
   visibility-not-display dance so a closed panel would not hit-test the edge
   the slider handle travels to, a flex column so the body was the only
   scroller, a bottom-sheet rewrite under 60rem, a z-index above the column
   resize grips, and a rule ceding the ground when the display column opened
   over it. Every line of that was the cost of a bespoke surface.

   The dialog is `.modal-overlay > .modal` from components.css. Centred over a
   dim, 656px, `max-height: calc(100vh - var(--space-6))`, `.modal__body`
   scrolling — so the scroller, the edge cases and the narrow-viewport
   behaviour all come from the component, and there is no second implementation
   to keep in step with it. Nothing below overrides the component; the only
   rules left are for the cloned `.guide__*` content, which the component has
   never seen.
   -------------------------------------------------------------------------- */

/* --------------------------------------------------------------------------
   The cloned content (.guide__*). Authored by as-core's
   templates/assessment/_question_guide.hbs and inert until the dialog clones it
   — so nothing styled it, in either repo. Picked up here because the reveal
   script is what first puts it on screen, and shipping it unstyled ships a
   broken dialog.

   SCOPED TO THE DIALOG BY ID, not to .modal__body: these are the only rules in
   this file that reach inside a vendored component, and every other modal the
   site grows must not inherit them.

   Existing tokens only, no new ones, and nothing that shadows a vendored class:
   these selectors exist nowhere else. The scale is a <dl> because that is what
   it is — a tier named, and what it means here — and the pairing has to survive
   at a glance, hence the tier label sitting tight above its statement with the
   air between PAIRS rather than inside them.
   -------------------------------------------------------------------------- */
#assessment-guidance-dialog .guide__prompt {
  margin: 0 0 var(--space-4);
  font: var(--fw-semibold) var(--fs-body)/var(--lh-body) var(--font-sans);
  color: var(--fe-text);
}
#assessment-guidance-dialog .guide__scale {
  margin: 0;
}
#assessment-guidance-dialog .guide__tier {
  font: var(--fw-semibold) var(--fs-eyebrow)/var(--lh-eyebrow) var(--font-sans);
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--fe-text-2);
}
#assessment-guidance-dialog .guide__statement {
  margin: var(--space-1) 0 var(--space-4);
  font: var(--fs-body-sm)/var(--lh-body-sm) var(--font-sans);
  color: var(--fe-text);
}
/* The authored halves are commentary on the question, not part of the scale —
   a rule separates them from the four statements they follow. */
#assessment-guidance-dialog .guide__explanation,
#assessment-guidance-dialog .guide__example {
  margin: var(--space-3) 0 0;
  font: var(--fs-body-sm)/var(--lh-body-sm) var(--font-sans);
  color: var(--fe-text-2);
}
#assessment-guidance-dialog .guide__explanation {
  padding-top: var(--space-4);
  border-top: 1px solid var(--fe-border);
}
#assessment-guidance-dialog .guide__example-label {
  display: block;
  font-weight: var(--fw-semibold);
  text-transform: uppercase;
  letter-spacing: 0.06em;
  font-size: var(--fs-eyebrow);
}

