/* ---------------------------------------------------------------------------------------------
   Design tokens (CAP-201).

   Every colour, space and type size the portal uses is declared here and nowhere else. A component
   that hard-codes a colour is a bug, because it is the one that will not change when the customer
   confirms the palette (customer prerequisite 11).

   The course tokens are the only ones that vary at runtime. Each MiniMBA course carries its own
   colour (Marketing #ED1846, Brand Management #2196F2, confirmed by Luke 22 Jul 2026), and the
   assessment is part of that course's experience, so the page takes the colour of the framework being
   answered. The default below is deliberately neutral: a framework with no colour should look plain,
   not quietly borrow Marketing's. CourseTheme.razor overrides all three per assessment.
   --------------------------------------------------------------------------------------------- */

@font-face {
    font-family: "Plus Jakarta Sans";
    src: url("/fonts/PlusJakartaSans-variable.woff2") format("woff2-variations");
    font-weight: 400 700;
    font-style: normal;
    font-display: swap;
}

@font-face {
    font-family: "Hedvig Letters Serif";
    src: url("/fonts/HedvigLettersSerif.woff2") format("woff2");
    font-weight: 400;
    font-style: normal;
    font-display: swap;
}

:root {
    /* Course identity. Overridden per framework; see CourseTheme.razor.

       Three tokens, not two, and the distinction is load-bearing:
         --cap-course       the brand colour exactly as supplied. Fills, bars, borders, the roundel.
         --cap-course-ink   black or white, whichever is legible ON that fill.
         --cap-course-text  the same colour darkened until it is legible AS text on the page.

       The third exists because Brave Bison's real colours are saturated mid-tones: #ED1846 is 4.36:1
       against white and #2196F2 is 3.13:1, so both fail WCAG AA for body text while being perfectly
       good as fills. Using --cap-course for a link would ship an unreadable link. The brand colour is
       never altered; only glyphs on the page background use the derived one. */
    --cap-course: #3F3F46;
    --cap-course-ink: #FFFFFF;
    --cap-course-text: #3F3F46;

    /* Neutrals. */
    --cap-ink: #18181B;
    --cap-ink-muted: #52525B;
    --cap-ink-faint: #71717A;
    --cap-surface: #FFFFFF;
    --cap-surface-sunken: #F4F4F5;
    --cap-border: #D4D4D8;
    --cap-border-strong: #A1A1AA;

    /* Status. Used by validation and by the score bands later. */
    --cap-danger: #B91C1C;
    --cap-success: #15803D;

    /* Typography, confirmed by Luke 22 Jul 2026: predominantly Plus Jakarta Sans, with Hedvig Letters
       Serif for some headings. Both are Google Fonts under the Open Font Licence, so the licensing
       question prerequisite 11 was carrying disappears: there is nothing to ask permission for.

       Self-hosted from /fonts rather than loaded from Google. The CSP has no font-src, so fonts fall
       back to default-src 'self' and a Google-hosted face would simply be blocked - and self-hosting
       also means no third party sees a request for every page view. Latin subset only, variable
       weight for the sans, 48 KB for both faces together.

       MiniMBA Sans, which the earlier stack named, is not the current brand face. */
    --cap-font: "Plus Jakarta Sans", system-ui, -apple-system, "Segoe UI", Roboto, Arial, sans-serif;
    --cap-font-display: "Hedvig Letters Serif", Georgia, "Times New Roman", serif;
    --cap-text-sm: 0.875rem;
    --cap-text-base: 1rem;
    --cap-text-lg: 1.25rem;
    --cap-text-xl: 1.75rem;
    --cap-text-2xl: 2.25rem;

    /* Space and shape. */
    --cap-space-1: 0.25rem;
    --cap-space-2: 0.5rem;
    --cap-space-3: 1rem;
    --cap-space-4: 1.5rem;
    --cap-space-5: 2.5rem;
    --cap-radius: 6px;
    --cap-focus: 3px solid #2563EB;

    --cap-measure: 68ch;

    /* The content column. Capped, because a form row stretched across a 4K monitor puts the statement
       and the buttons that answer it a foot apart, and prose becomes unreadable past about 90
       characters. But 1140px was chosen against a 1140px browser and never looked at on a real desktop:
       on a 3840px screen it is under a third of the width, with bare canvas either side. 1440 keeps the
       row tight and stops the page looking abandoned on a large monitor. Anything that needs to be
       narrower than this says so itself (--cap-measure). */
    --cap-page: 1440px;

    /* The content column inside the page width, once the gutters are taken off. The masthead and footer
       get the same measurement from padding; the main grid needs it as a number. */
    --cap-content: calc(var(--cap-page) - (2 * var(--cap-space-3)));
}

/* --------------------------------------------------------------------------------------------- */

*, *::before, *::after { box-sizing: border-box; }

body {
    margin: 0;
    font-family: var(--cap-font);
    font-size: var(--cap-text-base);
    line-height: 1.55;
    color: var(--cap-ink);
    background: var(--cap-surface-sunken);
    -webkit-text-size-adjust: 100%;
}

h1, h2, h3 { line-height: 1.2; margin: 0 0 var(--cap-space-3); }
h1 { font-size: var(--cap-text-2xl); }
h2 { font-size: var(--cap-text-xl); }
p { margin: 0 0 var(--cap-space-3); max-width: var(--cap-measure); }

a { color: var(--cap-course-text); }
a:hover { text-decoration: none; }

/* One visible focus ring everywhere. The questionnaire is a long keyboard-driven form and losing the
   caret in it is the single most likely accessibility complaint. */
:is(a, button, input, select, textarea, [tabindex]):focus-visible {
    outline: var(--cap-focus);
    outline-offset: 2px;
}

/* FocusOnNavigate moves focus to the h1 on every navigation, for screen-reader users, and does it by
   giving the heading a tabindex. That makes the heading match the rule above and draws a focus ring
   around the page title on arrival, which is not what the ring is for. The focus itself stays: it is
   the announcement that matters, not the outline.

   The selector has to name the attribute, and this is why: CAP-201 wrote this rule as `h1:focus-visible`,
   which is specificity (0,1,1), and the rule above is `[tabindex]:focus-visible`, which is (0,2,0). The
   attribute selector wins, so the suppression has never once taken effect and every page in the portal
   has been drawing a blue box round its own title since CAP-201. Nobody saw it because it only appears
   on the heading of the page you have just navigated to, which is exactly where an eye is not looking for
   a defect. Found by screenshotting the review step. `h1[tabindex]` is (0,2,1) and beats it. */
h1[tabindex]:focus, h1[tabindex]:focus-visible { outline: none; }

/* --- Shell ----------------------------------------------------------------------------------- */

/* CourseTheme exists to carry --cap-course on an element, not to be one. Without this it would be a
   box in the middle of the entry page's card grid, and every card would become a grid item's only
   child instead of the grid item. Custom properties inherit down the element tree, not the box tree,
   so the colour still reaches the card. */
.cap-theme { display: contents; }

.cap-shell { min-height: 100dvh; display: flex; flex-direction: column; }

/* White, per Figures 6 and 7, and not the course colour.
   CAP-201 painted the whole bar in var(--cap-course), which had two problems. The shell belongs to no
   course (a learner may be on two, D-020), so the bar was the neutral fallback grey: a heavy dark slab
   across the top of every page that looks nothing like the wireframes. And it forced the sign-out button
   to be drawn in the masthead's own background colour, which is why it was invisible until CAP-202
   patched round it. A white bar fixes both, and the course colour goes where it belongs: on the
   wordmark, and on the page beneath. */
.cap-masthead {
    background: var(--cap-surface);
    color: var(--cap-ink);
    border-block-end: 1px solid var(--cap-border);
}

.cap-masthead__inner,
.cap-footer__inner {
    width: 100%;
    max-width: var(--cap-page);
    margin-inline: auto;
    padding-inline: var(--cap-space-3);
}

.cap-masthead__inner {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--cap-space-3);
    flex-wrap: wrap;
    padding-block: var(--cap-space-3);
}

.cap-brand {
    display: flex;
    align-items: center;
    gap: var(--cap-space-2);
    color: var(--cap-course-text);
    text-decoration: none;
    font-weight: 700;
    font-size: var(--cap-text-lg);
}

/* The real MiniMBA lockup (prerequisite 11, received 22 Jul 2026), replacing the "MM" placeholder.

   Sized in the stylesheet and given intrinsic width/height in the markup, so the masthead does not
   reflow while the image loads. The file is 120px tall for a 40px slot, which covers 3x displays; it
   is deliberately not an SVG because Brave Bison supplied PNG, and tracing their mark to fake one
   would be inventing brand assets rather than using them. */
.cap-brand__logo {
    height: 2.5rem;
    width: auto;
    flex: none;
}

.cap-brand__sub { color: var(--cap-ink); font-weight: 400; font-size: var(--cap-text-base); }

.cap-nav { display: flex; align-items: center; gap: var(--cap-space-3); flex-wrap: wrap; }
.cap-nav a { color: var(--cap-ink-muted); text-decoration: none; font-weight: 600; }
.cap-nav a:hover { color: var(--cap-course-text); text-decoration: underline; }
.cap-nav__who { color: var(--cap-ink-muted); font-weight: 400; }

/* A full-bleed grid: three columns, where ordinary page content sits in the middle one and anything
   that wants the whole width (the entry page's hero) spans all three.

   The usual way to break an element out of a centred container is margin-inline: calc(50% - 50vw). It
   is wrong here, and measurably so: 100vw includes the scrollbar, so at 360px the hero was eight pixels
   wider than the viewport and the whole page scrolled sideways. This grid does the same job with no
   viewport arithmetic in it, so there is nothing for a scrollbar to be missing from. */
.cap-main {
    flex: 1;
    display: grid;
    align-content: start;
    grid-template-columns:
        [full-start] minmax(var(--cap-space-3), 1fr)
        [content-start] minmax(0, var(--cap-content)) [content-end]
        minmax(var(--cap-space-3), 1fr) [full-end];
    padding-block: var(--cap-space-5);
}

.cap-main > * { grid-column: content; }

/* CourseTheme is display: contents, so it is not a grid item: its children are. A child selector does
   not reach through it, so a page wrapped in CourseTheme had its heading, progress bar, item list and
   navigation auto-placed into the three columns of the full-bleed grid, side by side, at a third of the
   width each. It only looked right on the entry page because there the theme wraps individual cards
   inside a grid of their own. */
.cap-main > .cap-theme > * { grid-column: content; }

.cap-footer {
    background: var(--cap-surface);
    border-top: 1px solid var(--cap-border);
    color: var(--cap-ink-muted);
    font-size: var(--cap-text-sm);
}

.cap-footer__inner {
    padding-block: var(--cap-space-3);
    display: flex;
    justify-content: space-between;
    gap: var(--cap-space-3);
    flex-wrap: wrap;
}

/* The staff sign-in link (D-104). Deliberately quiet: it inherits the footer's muted ink and only
   underlines on hover, because it is a route for a handful of people and must not read as a call to
   action beside the learner's own sign-in. Given its own colour rather than the browser default, or it
   arrives bright blue in the one place on the page that is meant to recede. */
.cap-footer__staff {
    color: inherit;
    text-decoration: none;
}

.cap-footer__staff:hover,
.cap-footer__staff:focus-visible {
    text-decoration: underline;
}

/* --- Primitives the admin area reuses (CAP-201 -> CAP-401) ------------------------------------ */

.cap-card {
    background: var(--cap-surface);
    border: 1px solid var(--cap-border);
    border-radius: var(--cap-radius);
    padding: var(--cap-space-4);
}

.cap-card__accent { border-top: 4px solid var(--cap-course); }

.cap-cards {
    display: grid;
    gap: var(--cap-space-3);
    grid-template-columns: repeat(auto-fill, minmax(min(100%, 20rem), 1fr));
}

.cap-button {
    display: inline-block;
    padding: var(--cap-space-2) var(--cap-space-4);
    border: 1px solid transparent;
    border-radius: var(--cap-radius);
    background: var(--cap-course);
    color: var(--cap-course-ink);
    font: inherit;
    font-weight: 600;
    text-decoration: none;
    cursor: pointer;
}

.cap-button:hover { filter: brightness(1.12); }

.cap-button--quiet {
    background: transparent;
    border-color: currentColor;
    color: var(--cap-course-text);
}

.cap-badge {
    display: inline-block;
    padding: var(--cap-space-1) var(--cap-space-2);
    border-radius: 999px;
    background: var(--cap-surface-sunken);
    border: 1px solid var(--cap-border);
    font-size: var(--cap-text-sm);
    font-weight: 600;
}

.cap-muted { color: var(--cap-ink-muted); }

/* --- Entry page (CAP-202, Figure 6) ----------------------------------------------------------- */

/* The hero spans the page rather than the content column, as the wireframe has it. It sits inside
   .cap-main, so the negative inline margin pulls it back out to the viewport edge and the padding puts
   the text back on the content grid. */
.cap-hero {
    /* The one thing on any page that spans the full width. */
    grid-column: full;

    background: var(--cap-course);
    color: var(--cap-course-ink);
    padding-block: var(--cap-space-5);
    margin-block-end: var(--cap-space-5);

    /* .cap-main pads the top of every page, which would leave a strip of background between the
       masthead and the hero. The hero is the only thing that wants to sit flush against it. */
    margin-block-start: calc(-1 * var(--cap-space-5));
}

.cap-hero > * {
    width: 100%;
    max-width: var(--cap-page);
    margin-inline: auto;
    padding-inline: var(--cap-space-3);
}

.cap-hero__eyebrow {
    margin-block-end: var(--cap-space-2);
    font-size: var(--cap-text-sm);
    letter-spacing: 0.08em;
    text-transform: uppercase;
    opacity: 0.85;
}

.cap-hero h1 { margin-block-end: var(--cap-space-2); }
.cap-hero__lede { margin-block-end: 0; opacity: 0.9; }

/* The card is a column so the action sits on the baseline whatever the length of the status line, and
   two cards side by side line their buttons up. */
.cap-card { display: flex; flex-direction: column; }
.cap-card > :last-child { margin-block-end: 0; }

.cap-card__title { margin-block-end: var(--cap-space-1); }
.cap-card__meta { font-size: var(--cap-text-sm); margin-block-end: var(--cap-space-3); }

.cap-card__status {
    display: flex;
    align-items: center;
    gap: var(--cap-space-2);
    flex-wrap: wrap;
    margin-block-end: var(--cap-space-4);
}

/* Pushes the action to the bottom of the card. */
.cap-card__status + * { margin-block-start: auto; }

.cap-card .cap-button { align-self: flex-start; }

.cap-note {
    margin-block-start: var(--cap-space-4);
    padding: var(--cap-space-3);
    border: 1px dashed var(--cap-border);
    border-radius: var(--cap-radius);
    color: var(--cap-ink-muted);
    font-size: var(--cap-text-sm);
    max-width: none;
}

/* --- Questionnaire (CAP-203, Figure 7) -------------------------------------------------------- */

/* The questionnaire sits on a white panel, as Figure 7 has it, not directly on the page canvas. It is
   not decoration: the page is a long form with a fixed content width, so on a wide monitor the grey
   either side has to look like a surrounding surface rather than like the form failed to fill the
   window. It also gives the row separators something to end against. */
.cap-panel {
    background: var(--cap-surface);
    border: 1px solid var(--cap-border);
    border-radius: var(--cap-radius);
    padding: var(--cap-space-4) var(--cap-space-5);
    margin-block-end: var(--cap-space-3);
}

@media (max-width: 40rem) {
    .cap-panel { padding: var(--cap-space-3); }
}

/* Visible to a screen reader, not to an eye. The rating pills read "3" and mean "Can apply with some
   guidance", and a learner who cannot see the tooltip is entitled to the label rather than the number. */
.cap-visually-hidden {
    position: absolute;
    width: 1px;
    height: 1px;
    padding: 0;
    margin: -1px;
    overflow: hidden;
    clip-path: inset(50%);
    white-space: nowrap;
    border: 0;
}

.cap-step__head {
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
    gap: var(--cap-space-3);
    flex-wrap: wrap;
    margin-block-end: var(--cap-space-3);
}

/* The title takes the room that is left, and is allowed to be narrower than its longest word, or the
   progress figure is pushed onto its own line by a long domain name. */
.cap-step__head > :first-child { flex: 1 1 20rem; min-inline-size: 0; }

.cap-step__eyebrow {
    margin-block-end: var(--cap-space-1);
    color: var(--cap-course-text);
    font-size: var(--cap-text-sm);
    font-weight: 700;
    letter-spacing: 0.08em;
    text-transform: uppercase;
}

/* The step title is a page heading, not a hero. At 2.25rem a five-word domain name fills the line and
   leaves nowhere for the progress figure to sit. */
.cap-step__head h1 { font-size: var(--cap-text-xl); margin-block-end: 0; }

.cap-step__progress {
    flex: none;
    margin: 0;
    text-align: end;
    font-size: var(--cap-text-sm);
}

.cap-step__percent {
    display: block;
    color: var(--cap-course-text);
    font-size: var(--cap-text-lg);
}

.cap-progress {
    block-size: 6px;
    border-radius: 999px;
    background: var(--cap-border);
    overflow: hidden;
    margin-block-end: var(--cap-space-3);
}

.cap-progress__fill {
    block-size: 100%;
    background: var(--cap-course);
}

.cap-step__intro { color: var(--cap-ink-muted); font-size: var(--cap-text-sm); }

/* The capability scale's key. Body size and full-strength ink, not fine print: it is what every row on
   the page is rated against, and it was smaller than the rows themselves. */
.cap-step__scale {
    margin-block: 0 var(--cap-space-2);
    max-inline-size: var(--cap-measure);
}

.cap-items { list-style: none; margin: 0 0 var(--cap-space-4); padding: 0; }

.cap-item { border-block-end: 1px solid var(--cap-border); padding-block: var(--cap-space-3); }
.cap-item:first-child { border-block-start: 1px solid var(--cap-border); }

/* The gap indicator when Next is refused. A left border rather than a red background: the row is
   unanswered, not wrong, and colouring it like an error tells the learner they have made a mistake. */
.cap-item--gap {
    border-inline-start: 3px solid var(--cap-danger);
    padding-inline-start: var(--cap-space-3);
}

.cap-item__row {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--cap-space-3);
    flex-wrap: wrap;
}

.cap-item__text { margin: 0; max-width: var(--cap-measure); }
.cap-item__name { font-weight: 600; }

.cap-scale { display: flex; gap: var(--cap-space-2); flex: none; }

/* The input carries the state and the keyboard behaviour; the label carries the appearance. Native
   radios give arrow-key navigation within the group for free, which a div of buttons would not. */
.cap-scale__input {
    position: absolute;
    opacity: 0;
    inline-size: 1px;
    block-size: 1px;
}

.cap-scale__point {
    display: grid;
    place-items: center;
    inline-size: 2.25rem;
    block-size: 2.25rem;
    border: 1px solid var(--cap-border-strong);
    border-radius: 50%;
    color: var(--cap-ink-muted);
    font-size: var(--cap-text-sm);
    cursor: pointer;
}

.cap-scale__point:hover { border-color: var(--cap-course); }

.cap-scale__input:checked + .cap-scale__point {
    background: var(--cap-course);
    border-color: var(--cap-course);
    color: var(--cap-course-ink);
    font-weight: 700;
}

/* The ring belongs on the pill, because the input it belongs to is invisible. */
.cap-scale__input:focus-visible + .cap-scale__point { outline: var(--cap-focus); outline-offset: 2px; }

.cap-item__note { margin-block-start: var(--cap-space-2); font-size: var(--cap-text-sm); }
.cap-item__note summary { color: var(--cap-ink-muted); cursor: pointer; }

.cap-textarea {
    inline-size: 100%;
    max-inline-size: var(--cap-measure);
    margin-block-start: var(--cap-space-2);
    padding: var(--cap-space-2);
    border: 1px solid var(--cap-border);
    border-radius: var(--cap-radius);
    font: inherit;
    font-size: var(--cap-text-sm);
}

/* Clear of what it sits between, on BOTH sides, which is what put the buttons in the same visual block
   as the final answer (Luke Groves, UAT, 5 August 2026: "there should be a space between"). Measured at
   zero above and zero below before this. Both margins are needed because the same row is used twice and
   the two uses have it the other way up: on the questionnaire it follows the questions, and in the
   administrator's preview it precedes the panel it pages through. */
.cap-step__nav {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--cap-space-3);
    margin-block: var(--cap-space-4);
    flex-wrap: wrap;
}

.cap-step__validation { margin: 0; flex: 1; text-align: center; font-size: var(--cap-text-sm); }

.cap-error { color: var(--cap-danger); font-weight: 600; }

.cap-pending {
    border-style: dashed;
    color: var(--cap-ink-muted);
    margin-block-end: var(--cap-space-4);
}

/* At 360px the statement and its scale cannot share a line, and squeezing them makes the pills too
   small to hit. They stack, and the scale keeps its touch targets. */
@media (max-width: 40rem) {
    .cap-item__row { flex-direction: column; align-items: stretch; }
    .cap-scale { justify-content: space-between; }
    .cap-step__nav .cap-button { flex: 1; text-align: center; }
    .cap-step__validation { order: 3; flex-basis: 100%; }
}

/* --- Part C: role and workplace questions (CAP-208) ------------------------------------------ */

.cap-questions { list-style: none; margin: 0; padding: 0; }

.cap-question { border-block-end: 1px solid var(--cap-border); padding-block: var(--cap-space-4); }
.cap-question:first-child { border-block-start: 1px solid var(--cap-border); }

/* Block, and said explicitly: Part A's item text is a <label> (it names the input below it) where Parts
   C to E use a <p>, and a label is inline by default - which laid every field out on its own label's
   line, at a width that varied with how long the label was. Found by looking at the page (CAP-204). */
.cap-question__text {
    display: block;
    margin: 0 0 var(--cap-space-3);
    max-width: var(--cap-measure);
    font-weight: 600;
}

/* Single choice and the satisfaction scale render as a vertical list of full-width options, because the
   option text is a sentence, not a number. The whole label is the target, and native radios keep the
   arrow-key navigation a div of buttons would lose.

   BLOCK, NOT FLEX, AND THAT IS THE WHOLE OF D-105. An absolutely positioned child of a FLEX container
   is placed at the container's start corner, not where it would have sat in flow - so every hidden
   radio in the list parked at the top of the list, one on top of the other. Clicking a label hands
   focus to its radio, the browser scrolls a focused element into view, and the page jumped back up to
   the top of the option list. Measured at 319px on an eight-option list and around a thousand on Part
   E's twenty-one, which is what Luke Groves reported on 5 August 2026 as "the screen jumped to the
   top ... 4 out of the 5 times I tried it". Intermittent for a real reason: nothing moves when the top
   of the list is already on screen. In normal flow the static position is the option's own line, so
   the radio is where the eye already is and there is nothing to scroll to.

   The spacing therefore has to come from margins rather than gap. Keep it that way. */
.cap-choices { display: block; max-inline-size: var(--cap-measure); }

.cap-choice__input {
    position: absolute;
    opacity: 0;
    inline-size: 1px;
    block-size: 1px;
}

.cap-choice {
    display: block;
    margin-block-start: var(--cap-space-2);
    padding: var(--cap-space-2) var(--cap-space-3);
    border: 1px solid var(--cap-border-strong);
    border-radius: var(--cap-radius);
    cursor: pointer;
}

/* The list's own spacing sits above it, so the first option must not push itself down. */
.cap-choice:first-of-type { margin-block-start: 0; }

.cap-choice:hover { border-color: var(--cap-course); }

.cap-choice__input:checked + .cap-choice {
    background: var(--cap-course);
    border-color: var(--cap-course);
    color: var(--cap-course-ink);
    font-weight: 600;
}

.cap-choice__input:focus-visible + .cap-choice { outline: var(--cap-focus); outline-offset: 2px; }

/* A multi-select at its cap (Part E's E3, "select up to 3") disables the unticked options rather than
   letting a fourth click through to a refusal. The ticked ones stay live, so unticking re-opens the rest. */
.cap-choice__input:disabled + .cap-choice {
    color: var(--cap-ink-muted);
    border-color: var(--cap-border);
    cursor: not-allowed;
}

.cap-choices__count { margin-block-start: var(--cap-space-2); }

/* The employer statement matrix. Wide, so it scrolls inside its own container and the page never scrolls
   sideways. The visible control is the native radio (accent-coloured to the course); its accessible name
   is the statement and the point, carried on the input's aria-label. */
.cap-matrix__scroll { overflow-x: auto; }

.cap-matrix { border-collapse: collapse; inline-size: 100%; min-inline-size: 32rem; }

.cap-matrix th, .cap-matrix td {
    padding: var(--cap-space-2);
    text-align: center;
    border-block-end: 1px solid var(--cap-border);
}

.cap-matrix thead th {
    font-size: var(--cap-text-sm);
    color: var(--cap-ink-muted);
    font-weight: 600;
    vertical-align: bottom;
}

.cap-matrix th[scope="row"] {
    text-align: start;
    font-weight: 500;
    max-inline-size: 20rem;
}

.cap-matrix__input {
    inline-size: 1.15rem;
    block-size: 1.15rem;
    accent-color: var(--cap-course);
    cursor: pointer;
}

/* The read-only view of the matrix: one line per statement with the point the learner chose. */
.cap-summary__matrix { list-style: none; margin: var(--cap-space-2) 0 0; padding: 0; font-size: var(--cap-text-sm); }
.cap-summary__matrix li { padding-block: 2px; color: var(--cap-ink-muted); }

/* --- Review, confirmation and the read-only view (CAP-205, spec 6.1.3) ------------------------ */

/* The counts of spec 6.1.3, across the top of the review. auto-fit rather than three fixed columns, so
   an instrument with more or fewer things worth counting does not need this rule changed. */
.cap-summary {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(10rem, 1fr));
    gap: var(--cap-space-3);
    margin: 0 0 var(--cap-space-4);
    padding: var(--cap-space-3);
    background: var(--cap-surface-sunken);
    border-radius: var(--cap-radius);
}

.cap-summary__item dt {
    color: var(--cap-ink-muted);
    font-size: var(--cap-text-sm);
}

.cap-summary__item dd {
    margin: 0;
    color: var(--cap-course-text);
    font-size: var(--cap-text-lg);
    font-weight: 700;
}

.cap-summary__heading { font-size: var(--cap-text-lg); margin-block: var(--cap-space-4) var(--cap-space-2); }

/* Metadata about one record: who, when, which version (CAP-408). The same auto-fit grid as .cap-summary
   above, and deliberately not that class - .cap-summary's value is a large bold course-coloured number,
   which is right for "21 rated" and wrong for an email address. Wide values (an email, a long
   organisation name) are allowed to wrap rather than push the grid past the page. */
.cap-facts {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(14rem, 1fr));
    gap: var(--cap-space-3);
    margin: 0;
}

.cap-facts dt {
    color: var(--cap-ink-muted);
    font-size: var(--cap-text-sm);
}

/* break-word, not anywhere. `anywhere` broke every long value at whatever character reached the column
   edge, so an email read as "chloe.barnes@northwind.e / xample" on a 1440px screen - found by looking.
   break-word only breaks a word that cannot fit on a line of its own, which at 14rem is a genuinely
   long address rather than an ordinary one. */
.cap-facts dd {
    margin: 0;
    overflow-wrap: break-word;
}

.cap-parts { list-style: none; margin: 0 0 var(--cap-space-4); padding: 0; }

.cap-part {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: var(--cap-space-3);
    flex-wrap: wrap;
    padding-block: var(--cap-space-2);
    border-block-end: 1px solid var(--cap-border);
    font-size: var(--cap-text-sm);
}

.cap-part__name { font-weight: 600; }
.cap-part__done { color: var(--cap-success); font-weight: 600; }

/* Red-edged, so the block naming what is still to answer does not read as another summary panel. The
   name is about assessment gaps (unanswered items), not spacing - a neutral card that only wants the
   margin uses cap-card--spaced, or the danger edge follows it onto a card that is not a warning. */
.cap-card--gap {
    border-inline-start: 3px solid var(--cap-danger);
    margin-block-end: var(--cap-space-4);
}

/* Just the bottom margin, no edge: for stacking neutral cards with no warning meaning. */
.cap-card--spaced { margin-block-end: var(--cap-space-4); }

.cap-gaps { margin: 0; padding-inline-start: var(--cap-space-4); font-size: var(--cap-text-sm); }
.cap-gaps li { margin-block-end: var(--cap-space-1); }

/* The read-only view's answer: the number, then what it means. The number alone is not something a
   learner recognises three months later.

   Fixed width, so every pill sits on the same vertical line down the page. Left to size itself, the
   block is right-aligned and the labels are all different lengths ("Expert - can lead and teach others
   in this area" against "Basic awareness"), so the pills land at a different x on every row and the
   column reads as ragged rather than as a column. */
.cap-answer {
    flex: none;
    inline-size: 24rem;
    margin: 0;
    display: flex;
    align-items: center;
    gap: var(--cap-space-2);
    font-size: var(--cap-text-sm);
}

.cap-answer__value {
    display: grid;
    place-items: center;
    inline-size: 2.25rem;
    block-size: 2.25rem;
    border-radius: 50%;
    background: var(--cap-course);
    color: var(--cap-course-ink);
    font-weight: 700;
    flex: none;
}

.cap-answer__note {
    margin: var(--cap-space-2) 0 0;
    color: var(--cap-ink-muted);
    font-size: var(--cap-text-sm);
    font-style: italic;
}

.cap-confirm__lede { font-size: var(--cap-text-lg); }

.cap-confirm__nav {
    display: flex;
    gap: var(--cap-space-3);
    flex-wrap: wrap;
    margin-block-start: var(--cap-space-4);
}

/* The fixed answer column is wider than a 360px phone. On a stacked row there is nothing to align it
   against anyway, so it gives the width back. */
@media (max-width: 40rem) {
    .cap-answer { inline-size: auto; align-self: flex-start; }
}

/* --- Admin: course presentations (CAP-407), and the form + table primitives CAP-401 will reuse -- */

/* --- Admin area (CAP-401) ---------------------------------------------------------------------------

   The back office wears the portal's own shell (MainLayout): same white masthead, top nav, light
   surface and tokens as every learner and corporate page, so it reads as one product. All the admin
   area adds is a section sub-nav under the masthead and a set of vitals tiles - both built from the
   existing card, button and colour tokens, nothing bespoke. */

.cap-subnav {
    display: flex;
    flex-wrap: wrap;
    gap: var(--cap-space-1) var(--cap-space-3);
    margin-block-end: var(--cap-space-5);
    border-block-end: 1px solid var(--cap-border);
}

.cap-subnav a,
.cap-subnav span {
    display: inline-flex;
    align-items: center;
    gap: var(--cap-space-2);
    padding: var(--cap-space-2) 0;
    margin-block-end: -1px;                 /* the active underline sits on the container border */
    border-block-end: 2px solid transparent;
    color: var(--cap-ink-muted);
    text-decoration: none;
    font-weight: 600;
    font-size: var(--cap-text-sm);
}

.cap-subnav a:hover { color: var(--cap-course-text); }

/* NavLink stamps .active on the current route (nav-state-active). */
.cap-subnav a.active {
    color: var(--cap-ink);
    border-block-end-color: var(--cap-course);
}

/* A section the rest of Epic 6 fills in: shown so the shape of the back office is visible, but not a
   link (empty-nav-state: explain rather than silently hide). */
.cap-subnav .cap-subnav__soon { color: var(--cap-ink-faint); cursor: default; }

.cap-subnav__tag {
    font-size: 0.68rem;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.05em;
    color: var(--cap-ink-faint);
}

/* Vitals tiles: the portal card, with a large display-face figure. */
.cap-stat-group { margin-block-end: var(--cap-space-5); }
.cap-stat-group:last-child { margin-block-end: 0; }

.cap-stat-group__title {
    font-size: var(--cap-text-sm);
    font-weight: 600;
    color: var(--cap-ink-muted);
    text-transform: uppercase;
    letter-spacing: 0.05em;
    margin-block-end: var(--cap-space-3);
}

.cap-stat {
    display: flex;
    flex-direction: column;
    gap: var(--cap-space-1);
}

.cap-stat__figure {
    font-family: var(--cap-font-display);
    font-size: var(--cap-text-2xl);
    font-weight: 700;
    line-height: 1;
    font-variant-numeric: tabular-nums;     /* figures line up across the row */
}

.cap-stat__label { font-weight: 600; }
.cap-stat__note { color: var(--cap-ink-muted); font-size: var(--cap-text-sm); }

/* A row of labelled fields that wraps to a column on a narrow screen. The add-a-presentation form is
   the first of these; the rest of the admin editors (CAP-401 onwards) are the same shape. */
.cap-form__row {
    display: flex;
    flex-wrap: wrap;
    gap: var(--cap-space-3);
    margin-block: var(--cap-space-3);
}

.cap-field { display: flex; flex-direction: column; gap: var(--cap-space-1); flex: 1 1 14rem; }
.cap-field__label { font-size: var(--cap-text-sm); font-weight: 600; }
.cap-field__optional { font-weight: 400; color: var(--cap-ink-muted); }

.cap-field__control {
    padding: var(--cap-space-2);
    border: 1px solid var(--cap-border);
    border-radius: var(--cap-radius);
    font: inherit;
    font-size: var(--cap-text-sm);
    background: var(--cap-surface);
    color: var(--cap-ink);
}

.cap-field__control:focus-visible { outline: var(--cap-focus); outline-offset: 1px; }

.cap-table {
    inline-size: 100%;
    border-collapse: collapse;
    margin-block-start: var(--cap-space-3);
    font-size: var(--cap-text-sm);
}

.cap-table th,
.cap-table td {
    text-align: start;
    padding: var(--cap-space-2);
    border-block-end: 1px solid var(--cap-border);
    vertical-align: baseline;
}

.cap-table th { color: var(--cap-ink-muted); font-weight: 600; }
.cap-table code { font-size: var(--cap-text-sm); color: var(--cap-ink); }

/* A finished presentation is still shown - it is the record of a real intake - but it recedes, because
   the active ones are what an administrator is here to manage. */
.cap-table__row--muted td { color: var(--cap-ink-muted); }
.cap-table__actions { text-align: end; white-space: nowrap; }

/* The draft editor's domain header: a title on one line and its actions on the other, wrapping on a
   narrow screen (CAP-405).

   It needs its own rule rather than reusing .cap-table__actions, which was the first attempt and gave the
   page a horizontal scrollbar at 390px. That class carries `white-space: nowrap`, which is right inside a
   table that scrolls in its own wrapper and wrong for a row of buttons standing free in a card: four
   buttons that refuse to wrap are wider than a phone. */
.cap-editor__head {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    justify-content: space-between;
    gap: var(--cap-space-2);
}

.cap-editor__actions {
    display: flex;
    flex-wrap: wrap;
    gap: var(--cap-space-2);
}

/* An actions cell holds a link and a form (the form is what carries the antiforgery token, CAP-206), and
   a form is a block, so the button inside it dropped onto its own line and sat indented from the link
   beside it. Every admin table has looked like that since CAP-402. Inline-block puts them back on one
   line, and the alignment rule above still right-aligns the pair. */
.cap-table__actions > form { display: inline-block; margin-inline-start: 0.5rem; }

/* The resource id column does not wrap usefully on a phone, so the whole table scrolls rather than
   crushing every column (the page body itself must never scroll sideways). */
/* `position: relative` is load-bearing, not decoration. An absolutely positioned descendant is clipped
   by an ancestor's overflow only when that ancestor is its containing block, and the visually-hidden
   "Actions" heading in the last column is absolutely positioned. Without this it keeps its static
   position out at the full table width, escapes the scroller, and gives the whole PAGE a horizontal
   scrollbar on a phone - exactly what the wrapper exists to prevent. Found at 390px on CAP-403, whose
   table is the first one wider than a phone. */
.cap-table-wrap { overflow-x: auto; position: relative; }

@media (max-width: 40rem) {
    .cap-field { flex-basis: 100%; }
}

/* --- Learner results overview (CAP-303, spec 6.1.4, Figure 8) ---------------------------------- */

/* The headline cards. auto-fit like .cap-summary, so three cards (a single phase) and four (the
   comparison) both fill the row without a rule change. */
.cap-results__cards {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(11rem, 1fr));
    gap: var(--cap-space-3);
    margin: 0 0 var(--cap-space-4);
}

.cap-stat {
    padding: var(--cap-space-3);
    background: var(--cap-surface-sunken);
    border-radius: var(--cap-radius);
}

.cap-stat dt { color: var(--cap-ink-muted); font-size: var(--cap-text-sm); }

.cap-stat dd {
    margin: var(--cap-space-1) 0 0;
    font-size: var(--cap-text-xl);
    font-weight: 700;
    color: var(--cap-course);
    display: flex;
    align-items: baseline;
    gap: var(--cap-space-2);
    flex-wrap: wrap;
}

/* A domain name is a sentence, not a figure; it must not shout at headline size. */
.cap-stat__name { font-size: var(--cap-text-base); color: var(--cap-ink); }
.cap-stat__of { font-size: var(--cap-text-sm); font-weight: 400; color: var(--cap-ink-muted); }
.cap-stat__figure { font-size: var(--cap-text-base); font-weight: 700; color: var(--cap-course); }
.cap-stat__band { margin: var(--cap-space-1) 0 0; }
.cap-stat__basis { margin: var(--cap-space-1) 0 0; font-size: var(--cap-text-sm); color: var(--cap-ink-muted); }

/* The change figures. Green up and red down lean on colour, so the sign is always in the text too
   (+1.48 / -0.50): colour reinforces, never carries, the meaning (WCAG 1.4.1). */
.cap-delta { font-weight: 700; font-size: var(--cap-text-base); white-space: nowrap; }
.cap-delta--up { color: var(--cap-success); }
.cap-delta--down { color: var(--cap-danger); }

/* Radar beside the table where there is room, stacked where there is not. The chart column is capped:
   a radar stretched across a 4K monitor is a decoration, not a reading. */
.cap-results__layout {
    display: grid;
    grid-template-columns: minmax(0, 30rem) minmax(0, 1fr);
    gap: var(--cap-space-4);
    align-items: start;
}

@media (max-width: 56rem) {
    .cap-results__layout { grid-template-columns: minmax(0, 1fr); }
}

.cap-results__table th[scope="row"] { color: var(--cap-ink); font-weight: 600; }
.cap-results__overall th,
.cap-results__overall td { border-block-start: 2px solid var(--cap-ink); border-block-end: none; font-weight: 700; }

.cap-results__dates { font-size: var(--cap-text-sm); display: flex; gap: var(--cap-space-3); flex-wrap: wrap; }

/* --- Domain breakdown (CAP-304, spec 6.1.5, Figure 9) ------------------------------------------ */

.cap-breakdown {
    display: grid;
    grid-template-columns: minmax(0, 1fr) minmax(0, 18rem);
    gap: var(--cap-space-4);
    align-items: start;
}

@media (max-width: 56rem) {
    .cap-breakdown { grid-template-columns: minmax(0, 1fr); }
}

.cap-breakdown__aside {
    padding: var(--cap-space-3);
    background: var(--cap-surface-sunken);
    border-radius: var(--cap-radius);
}

.cap-breakdown__aside .cap-summary__heading { margin-block-start: 0; }

.cap-breakdown__score {
    margin: 0 0 var(--cap-space-2);
    font-size: var(--cap-text-2xl);
    font-weight: 700;
    color: var(--cap-course);
    display: flex;
    align-items: baseline;
    gap: var(--cap-space-2);
}

.cap-bars { list-style: none; margin: 0; padding: 0; }

.cap-bars__row {
    padding-block: var(--cap-space-3);
    border-block-end: 1px solid var(--cap-border);
}

.cap-bars__row:first-child { padding-block-start: 0; }

.cap-bars__text { margin: 0 0 var(--cap-space-2); }
.cap-bars__name { font-weight: 600; }
.cap-bars__desc { color: var(--cap-ink-muted); font-size: var(--cap-text-sm); }
.cap-bars__desc::before { content: " - "; }

/* The bars themselves: plain divs whose widths are the persisted ratings against the version's own
   scale, so the picture cannot disagree with the stored numbers. Pre is neutral, post is the course
   colour - the same series colouring as the radar (D-060). */
.cap-bars__track {
    background: var(--cap-surface-sunken);
    border-radius: 999px;
    block-size: 10px;
    margin-block-end: var(--cap-space-1);
    overflow: hidden;
}

.cap-bars__fill { block-size: 100%; border-radius: inherit; }
.cap-bars__fill--pre { background: #A1A1AA; }
.cap-bars__fill--post { background: var(--cap-course); }

.cap-bars__figures {
    margin: var(--cap-space-1) 0 0;
    font-size: var(--cap-text-sm);
    display: flex;
    gap: var(--cap-space-2);
    align-items: baseline;
    flex-wrap: wrap;
}

/* --- Framework defaults ----------------------------------------------------------------------- */

.valid.modified:not([type=checkbox]) { outline: 1px solid var(--cap-success); }
.invalid { outline: 1px solid var(--cap-danger); }
.validation-message { color: var(--cap-danger); }

.blazor-error-boundary {
    background: var(--cap-danger);
    padding: var(--cap-space-3);
    color: #FFFFFF;
}

.blazor-error-boundary::after { content: "An error has occurred."; }

#blazor-error-ui {
    display: none;
    position: fixed;
    inset-inline: 0;
    bottom: 0;
    z-index: 1000;
    padding: var(--cap-space-3);
    background: #FEF3C7;
    border-top: 1px solid var(--cap-border-strong);
    box-shadow: 0 -1px 3px rgba(0, 0, 0, 0.15);
}

/* The cohort dashboard's filter bar, sections and pager (CAP-311, spec 6.2). */

.cap-filters {
    display: flex;
    flex-wrap: wrap;
    align-items: end;
    gap: var(--cap-space-3);
    padding: var(--cap-space-3);
    margin: 0 0 var(--cap-space-4);
    background: var(--cap-surface-sunken);
    border: 1px solid var(--cap-border);
    border-radius: var(--cap-radius);
}

.cap-filters__field { display: flex; flex-direction: column; gap: 0.25rem; }

.cap-filters__field label {
    font-size: var(--cap-text-sm);
    font-weight: 600;
}

.cap-filters__field select,
.cap-filters__field input {
    padding: 0.5rem;
    font: inherit;
    background: var(--cap-surface);
    border: 1px solid var(--cap-border-strong);
    border-radius: var(--cap-radius);
    /* Wide enough for the longest programme name without the bar reflowing as the filter changes. */
    min-inline-size: 12rem;
}

.cap-dash__section { margin-block-start: var(--cap-space-4); }

.cap-pager {
    display: flex;
    align-items: center;
    gap: var(--cap-space-3);
    margin-block-start: var(--cap-space-3);
}

/* Part A's inputs (CAP-204). A native select for a list of 33 industries, a native date picker, and text
   for a name: the platform's own controls, which come with keyboard behaviour, locale-aware date entry
   and autofill that a bespoke widget would have to reimplement badly. Sized to the reading measure so a
   name box is not the width of the page. */
.cap-select,
.cap-input {
    inline-size: 100%;
    max-inline-size: var(--cap-measure);
    margin-block-start: var(--cap-space-2);
    padding: var(--cap-space-2);
    font: inherit;
    background: var(--cap-surface);
    border: 1px solid var(--cap-border-strong);
    border-radius: var(--cap-radius);
}

.cap-select:focus-visible,
.cap-input:focus-visible { outline: var(--cap-focus); outline-offset: 2px; }

/* The required marker and its key (CAP-204, asked for at the 21 July review). The asterisk carries the
   danger colour so it reads as a marker rather than as punctuation, and the key sits under the section
   introduction in the muted voice - it is instruction, not content. */
.cap-required {
    color: var(--cap-danger);
    font-weight: 700;
    margin-inline-start: 0.15em;
}

.cap-step__required {
    margin-block: 0 var(--cap-space-3);
    color: var(--cap-ink-muted);
    font-size: var(--cap-text-sm);
    max-inline-size: var(--cap-measure);
}
