/* =========================================================================
   IntelliLine AI Coaching Academy — the portal design system.

   ONE STYLESHEET for the participant portal, the facilitator dashboard, the
   administrator console, the sign-in page and the verification page. Shared so
   the four surfaces cannot drift apart, and small enough to read.

   DESIGN INTENT
   The same dark ground, the same IntelliLine blue and the same brass the main
   website uses — so an owner who signs in here recognises where they are. Blue
   is the ACTION colour (it is what you press); brass is the Academy's own
   secondary accent (labels, selected details, the focus ring). Premium and
   quiet — a room of business owners spends eight hours looking at this, so it
   favours legibility and generous spacing over decoration. No neon, no robot
   imagery, no gamification, no walls of text.

   WHERE THE COLOURS COME FROM
   Every core value below is copied from the website's own :root in
   /index.html. They are not approximations. The only tokens invented here are
   the recessed/raised shades the dashboard needs and the website has no use
   for, and they are derived from the website's --ink.

   ACCESSIBILITY, DELIBERATELY
   * Every foreground/background pair below was chosen to clear WCAG 2.2 AA:
     4.5:1 for body text, 3:1 for large text and for UI component boundaries.
     The values are recorded in comments beside the tokens so the next person
     can check rather than trust.
   * Focus is ALWAYS visible. There is no `outline: none` in this file without a
     replacement immediately after it — a keyboard user must never lose their
     place. The focus ring stays BRASS rather than moving to blue: blue is now
     the primary action colour, and a focus ring the same colour as the button
     it lands on is a focus ring nobody can see. Brass on the dark ground is
     9.2:1 and brass over the blue button is 3.6:1, and the ring's inner 2px of
     page colour separates it from whatever sits underneath.
   * Interactive targets are at least 24x24 CSS px (WCAG 2.2 Target Size
     minimum), and the primary actions are 44px tall.
   * `prefers-reduced-motion` removes every transition and animation. It is not
     a shortening — it is a removal.
   * Nothing conveys meaning by colour alone: states carry an icon or a word.
   ========================================================================= */

:root {
  /* ---- GROUND. The website's own light system. ------------------------
     --canvas    the application canvas. NOT pure white: a participant looks
                 at this for eight hours, and a full field of #FFFFFF glares.
                 White is reserved for the cards that sit on it, which is what
                 gives the page its depth without a single shadow.
     --paper     cards, inputs, dialogs — the reading surface
     --paper-2   the canvas value again, for wells and recessed areas
     --paper-3   a step further down: table heads, code blocks, drop zones
     --warm      the premium warm ground for an occasional callout
     --ink       an INTENTIONALLY dark surface — a hero, a footer, an
     --ink-2     instructor panel. Never the default for coursework.

     Contrast of --text on each: paper 19.3  canvas 17.9  paper-3 16.8       */
  --paper:    #FFFFFF;
  --paper-2:  #F5F6F8;
  --paper-3:  #EDEFF3;
  --warm:     #FAF6F0;
  --canvas:   var(--paper-2);
  --ink:      #0A0D12;
  --ink-2:    #12171F;

  /* ---- TEXT. The website's ramp, value for value. ---------------------
     --text    headings and anything that must be read first
     --text-2  THE DEFAULT for body copy. #0A0E14 on white is 19.3:1, which
               is harsher than it sounds over a full workshop day; 12.5:1 is
               still far past AA and much easier to sit with.
     --muted   supporting text, labels, help
     --muted-2 kept for fidelity with the website, but NOT used as a text
               colour here: it is 4.23:1 on --paper-3 and 4.38:1 on
               --blue-wash, both under AA, and this build has surfaces the
               marketing site does not.
       on --canvas:  text 17.9   text-2 11.5   muted 6.8                    */
  --text:     #0A0E14;
  --text-2:   #2B3540;
  --muted:    #4E5764;
  --muted-2:  #69727F;

  /* ---- STRUCTURE ------------------------------------------------------
     --line        the standard border
     --line-soft   a soft divider, and the header's underline
     --line-dk     the website's hairline for dark surfaces
     --line-strong the boundary of an actual CONTROL. WCAG 1.4.11 wants 3:1
                   for those and --line is 1.26:1 — right for a divider
                   between two cards, useless as the edge of an input. So
                   there are separate tokens and controls use the strong one:
                   4.05:1 on --paper, 3.74:1 on --canvas, 3.51:1 on --paper-3 */
  --line:        #E2E5EA;
  --line-soft:   #EDEFF3;
  --line-dk:     rgba(255, 255, 255, .12);
  --line-strong: #767F8D;

  /* ---- INTELLILINE BLUE — the action colour --------------------------
     --blue      primary buttons, links, selected tabs, active navigation
     --blue-deep the pressed/hover fill
     --blue-lt   DARK SURFACES ONLY. 2.39:1 on white — it exists for --ink.
     --blue-wash selected and informational backgrounds
       --blue on --paper 7.70   --blue-deep on --paper 9.74
       white on --blue   7.70   white on --blue-deep    9.74                */
  --blue:      #134BB8;
  --blue-lt:   #7FA8F2;
  --blue-deep: #12408F;
  --blue-wash: #EEF3FD;

  /* ---- INTELLILINE BRASS — the premium secondary accent ---------------
     The Academy earns more brass than the marketing site does, but it is an
     ACCENT: eyebrows, milestones, the INSTALL letters, certificate moments,
     a thin rule on a card. Never a button, never a paragraph.
     --brass     brass on a LIGHT surface — 6.23:1 on --paper
     --brass-dk  the heavier weight, and the focus ring — 9.21:1 on --paper
     --brass-lt  DARK SURFACES ONLY. 2.12:1 on white; 9.16:1 on --ink.
     --brass-wash the soft premium ground                                   */
  --brass:      #7E5A22;
  --brass-lt:   #D2AC72;
  --brass-dk:   #5F421A;
  --brass-wash: #F8F3EA;

  /* ---- STATUS. Each is paired with an icon or a word, never colour alone.
     --live is the website's own success green. The other two have no
     counterpart on the marketing site, so they are the darkest amber and red
     that stay unmistakably amber and red on a light ground.
       --ok on --canvas 4.87 (on its wash 4.72)
       --warn 5.95 (5.87)    --bad 6.71 (6.38)    --info 7.12 (6.92)
     --warn sits near --brass in hue by necessity — every AA-passing amber on
     white is brownish — so the two are separated by their washes and by the
     fact that a status always carries a word.                              */
  --live:      #1E7A5A;
  --ok:        #1E7A5A;
  --ok-wash:   #EAF5F0;
  --warn:      #8F4E0A;
  --warn-wash: #FDF3E7;
  --bad:       #A4262C;
  --bad-wash:  #FCEDEC;
  --info:      #134BB8;
  --info-wash: #EEF3FD;

  /* ---- Text ON an intentionally dark surface -------------------------- */
  --on-dark:   #FFFFFF;
  --on-dark-2: #C9D3E0;   /* 12.9:1 on --ink */
  --on-dark-3: #A6B0BF;   /*  8.9:1 on --ink */


  /* ---- THE DARK-THEME HALF OF THE PALETTE ----------------------------
     Raw values only; nothing here decides when they are used — the semantic
     layer below does that. The marketing site has no dark UI to copy, so
     these are derived from its --ink and measured. On --ink / --ink-2:
       --d-text 16.7/15.4   --d-text-2 12.5/11.5   --d-muted 9.9/9.2
       --d-subtle 6.4/5.9   --d-ok 8.7/8.0  --d-warn 10.4/9.6  --d-bad 7.7/7.1
     --ink-3 and --ink-4 are the only invented surfaces: one step below --ink
     for wells and code, one above --ink-2 for the things that float.        */
  --ink-3:     #070A0F;
  --ink-4:     #1A212B;
  --ink-warm:  #191510;
  --d-text:    #E9EEF4;
  --d-text-2:  #C6D0DD;
  --d-muted:   #AFBACA;
  --d-subtle:  #8B95A5;
  --d-ok:      #5FBF95;
  --d-warn:    #E8B563;
  --d-bad:     #F0857A;
  /* A #134BB8 fill is 2.53:1 against --ink, so in the dark theme the primary
     button needs a rim to have any boundary at all. 4.20:1 on --ink. */
  --blue-edge: #3A72D4;

  /* ---- Spacing scale --------------------------------------------------- */
  --s1: 4px;  --s2: 8px;  --s3: 12px; --s4: 16px; --s5: 24px;
  --s6: 32px; --s7: 48px; --s8: 64px; --s9: 96px;

  --r:    4px;
  --r-lg: 8px;

  /* No web font is requested and none is bundled: the CSP is script-src and
     font-src 'self', and a workshop must not wait on a font CDN. */
  --sans: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
  --mono: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, monospace;

  /* ---- Type scale. One named level per job. ---------------------------
     Sized for a working day rather than a landing page: body is 17px, the
     smallest text on any page is 14px, and headings are deliberately modest
     so they do not eat the workspace. */
  --t-hero:  clamp(1.8rem, 3.2vw, 2.5rem);
  --t-h1:    clamp(1.45rem, 2.4vw, 1.85rem);
  --t-h2:    1.3rem;
  --t-h3:    1.1rem;
  --t-body:  1.0625rem;  /* 17px */
  --t-sm:    .95rem;     /* 15.2px */
  --t-xs:    .875rem;    /* 14px — the floor */
  --t-label: .76rem;     /* 12.2px, uppercase and letter-spaced only */

  --lh:    1.62;   /* inside the 1.55-1.7 band that reads well all day */
  --wrap:  1180px;
  --read:  66ch;   /* nothing prose-like exceeds this */

  /* =====================================================================
     THE SEMANTIC LAYER.

     Everything above is PALETTE: brand constants that never change meaning.
     Everything below is SEMANTIC: what a colour is FOR. Components name only
     these, so the whole interface changes theme by redefining this one block
     — which is exactly what the dark media query underneath does. There is
     no second copy of the stylesheet and no JavaScript involved.

     LIGHT IS THE DEFAULT. These are the values a browser gets with no
     preference expressed at all.
     ===================================================================== */

  /* Surfaces. A soft grey canvas with white cards on it, rather than a full
     field of white — eight hours of #FFFFFF glares. */
  --app-background:    var(--paper-2);
  --surface:           var(--paper);
  --surface-raised:    var(--paper);
  --surface-recessed:  var(--paper-3);
  --surface-warm:      var(--warm);
  --surface-selected:  var(--blue-wash);
  --surface-gold:      var(--brass-wash);

  /* Text. --text-secondary is the DEFAULT for running copy: 12.5:1 rather
     than 19.3:1, still far past AA and much easier over a whole day. */
  --text-primary:      var(--text);
  --text-secondary:    var(--text-2);
  --text-muted:        var(--muted);
  /* The website's fourth grey, carried through for fidelity. It is NOT used
     as a text colour anywhere in the Academy and a test enforces that: on
     --surface-recessed it is 4.23:1 and on every tinted wash 4.28-4.44:1,
     all under AA. This build has surfaces the marketing site does not. Where
     the old design reached for it, it uses --text-muted (6.35:1 at worst). */
  --text-subtle:       var(--muted-2);
  --text-on-accent:    var(--paper);

  /* Structure. --border is a divider; --border-strong is the edge of a real
     CONTROL, which 1.4.11 wants at 3:1 and --border cannot give. */
  --border:            var(--line);
  --border-soft:       var(--line-soft);
  --border-strong:     var(--line-strong);

  /* Action and accent. */
  --link:               var(--blue);
  --link-hover:         var(--blue-deep);
  --primary-action:        var(--blue);
  --primary-action-hover:  var(--blue-deep);
  --primary-action-border: var(--blue);
  --accent-gold:        var(--brass);
  --accent-gold-strong: var(--brass-dk);
  --text-on-gold:       var(--paper);      /* white on #7E5A22 = 6.23:1 */
  --text-on-status:     var(--paper);      /* white on #A4262C = 7.26:1 */

  /* Status. Each is always paired with a word or an icon. */
  --status-ok:        var(--ok);        --status-ok-wash:   var(--ok-wash);
  --status-warn:      var(--warn);      --status-warn-wash: var(--warn-wash);
  --status-bad:       var(--bad);       --status-bad-wash:  var(--bad-wash);
  --status-info:      var(--info);      --status-info-wash: var(--info-wash);

  /* The header. */
  --header-background: rgba(255, 255, 255, .94);
  --header-text:       var(--text);
  --header-muted:      var(--muted);
  --header-border:     var(--line-soft);

  /* An intentionally dark band — a hero, a footer, an instructor panel. It
     is dark in BOTH themes, which is the point of it; in the dark theme it
     steps UP off the page instead of down, or it would vanish into it. */
  --hero-background:   var(--ink);
  --hero-surface:      var(--ink-2);

  /* THE MARK. One variable, and the only thing that decides its colour. */
  --brand-logo-color:  var(--blue);

  /* Soft borders rather than heavy shadows. The one shadow left is for the
     things that genuinely float: dialogs and toasts. */
  --shadow: 0 1px 2px rgba(10, 14, 20, .06), 0 8px 24px rgba(10, 14, 20, .08);
  --scrim:  rgba(10, 13, 18, .46);

  /* TWO rings. The inner band is the surface colour so the ring never touches
     the control it surrounds; the outer is brass, 8.5:1 against the canvas.
     On a blue button the white inner band is 7.7:1 against the fill, so the
     ring stays visible whatever it lands on. */
  --focus-ring: 0 0 0 2px var(--surface), 0 0 0 4px var(--accent-gold-strong);
  --ring:       var(--focus-ring);
  --ring-dark:  0 0 0 2px var(--ink), 0 0 0 4px var(--brass-lt);

  /* Native controls — scrollbars, date pickers, the caret, form widgets the
     browser draws itself — follow this and nothing else. */
  color-scheme: light;
}

/* =========================================================================
   THE DARK THEME.

   One media query, redefining the semantic layer and nothing else. No second
   stylesheet, no duplicated components, no class on <html>, and no
   JavaScript: the page follows the operating system, and follows it live —
   change the system appearance with a page open and it changes underneath
   you, because a media query is always live.

   Not pure black, and not neon. --ink is the website's own dark ground, the
   cards sit one step up from it, and the accents are the light weights of
   the same two brand colours the light theme uses.
   ========================================================================= */

@media (prefers-color-scheme: dark) {
  :root {
    --app-background:    var(--ink);
    --surface:           var(--ink-2);
    --surface-raised:    var(--ink-4);
    --surface-recessed:  var(--ink-3);
    --surface-warm:      var(--ink-warm);
    --surface-selected:  rgba(127, 168, 242, .14);
    --surface-gold:      rgba(210, 172, 114, .14);

    --text-primary:      var(--d-text);
    --text-secondary:    var(--d-text-2);
    --text-muted:        var(--d-muted);
    --text-subtle:       var(--d-subtle);
    --text-on-accent:    var(--paper);

    --border:            var(--line-dk);
    --border-soft:       rgba(255, 255, 255, .08);
    /* The same value serves both themes: 4.8:1 on --ink, 4.0:1 on --paper. */
    --border-strong:     var(--line-strong);

    /* The light blue is the READABLE one on a dark ground — 8.1:1 on --ink,
       where --blue itself is 2.5:1 and unusable as text. The primary button
       keeps the brand blue as its FILL (white on it is 7.7:1 either way) and
       gains a rim, because the fill alone is 2.5:1 against the page. */
    --link:               var(--blue-lt);
    --link-hover:         var(--d-text);
    --primary-action:        var(--blue);
    --primary-action-hover:  var(--blue-deep);
    --primary-action-border: var(--blue-edge);
    --accent-gold:        var(--brass-lt);
    --accent-gold-strong: var(--brass-lt);
    --text-on-gold:       var(--ink);      /* #0A0D12 on #D2AC72 = 9.16:1 */
    /* The dark theme's statuses are LIGHT colours, so what sits on them is
       the page's own dark ground, not white: #0A0D12 on #F0857A = 7.73:1,
       where white would be 2.52:1. */
    --text-on-status:     var(--ink);

    --status-ok:   var(--d-ok);   --status-ok-wash:   rgba(95, 191, 149, .14);
    --status-warn: var(--d-warn); --status-warn-wash: rgba(232, 181, 99, .14);
    --status-bad:  var(--d-bad);  --status-bad-wash:  rgba(240, 133, 122, .14);
    --status-info: var(--blue-lt); --status-info-wash: rgba(127, 168, 242, .14);

    --header-background: rgba(10, 13, 18, .94);
    --header-text:       var(--d-text);
    --header-muted:      var(--d-muted);
    --header-border:     var(--line-dk);

    --hero-background:   var(--ink-2);
    --hero-surface:      var(--ink-4);

    --brand-logo-color:  #FFFFFF;

    --shadow: 0 1px 2px rgba(0, 0, 0, .40), 0 8px 24px rgba(0, 0, 0, .34);
    --scrim:  rgba(0, 0, 0, .66);
    --focus-ring: 0 0 0 2px var(--app-background), 0 0 0 4px var(--brass-lt);
    --ring:       var(--focus-ring);

    color-scheme: dark;
  }
}

/* Motion is removed, not shortened, for anyone who has asked for that. */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: .001ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: .001ms !important;
    scroll-behavior: auto !important;
  }
}

/* =========================================================================
   Base
   ========================================================================= */

*, *::before, *::after { box-sizing: border-box; }

html { -webkit-text-size-adjust: 100%; scroll-behavior: smooth; }

body {
  margin: 0;
  /* The canvas is the soft grey, not white. Cards are white and sit on it —
     that is where the page gets its depth, rather than from shadows. */
  background: var(--app-background);
  /* --text-2 rather than --text for running copy: 11.5:1 instead of 17.9:1
     is still far past AA and much kinder over a full workshop day. Headings
     take the darker value back, immediately below. */
  color: var(--text-secondary);
  font: 400 var(--t-body)/var(--lh) var(--sans);
  /* Anchored content never hides under the sticky header. */
  scroll-padding-top: 84px;
  /* The page itself never scrolls sideways. Anything genuinely wide — a table,
     a diagram, a code block — scrolls inside its own container instead. */
  overflow-x: hidden;
}

h1, h2, h3, h4 { margin: 0; line-height: 1.25; font-weight: 700; color: var(--text-primary); text-wrap: balance; }
h1 { font-size: var(--t-h1); }
h2 { font-size: var(--t-h2); }
h3 { font-size: var(--t-h3); }
p  { margin: 0 0 var(--s4); max-width: var(--read); overflow-wrap: break-word; }
ul, ol { margin: 0 0 var(--s4); padding-left: 1.35em; }
li { margin: 0 0 var(--s2); max-width: var(--read); overflow-wrap: break-word; }
a { color: var(--link); text-underline-offset: 3px; }   /* 7.7:1 light, 8.1:1 dark */
a:hover { color: var(--link-hover); }
img { max-width: 100%; height: auto; }
hr { border: 0; border-top: 1px solid var(--border-soft); margin: var(--s6) 0; }

/* THE focus rule. Visible on every surface, never removed. */
:focus-visible {
  outline: none;
  box-shadow: var(--focus-ring);
  border-radius: var(--r);
}
/* Older engines without :focus-visible still get a ring rather than nothing. */
:focus:not(:focus-visible) { outline: none; }

/* Skip link — the first thing a keyboard user reaches. */
.skip {
  position: absolute; left: var(--s4); top: -100px; z-index: 100;
  padding: var(--s3) var(--s5); background: var(--primary-action); color: var(--text-on-accent);
  font-weight: 700; border-radius: var(--r); text-decoration: none;
  transition: top .12s ease;
}
.skip:focus { top: var(--s4); }

.visually-hidden {
  position: absolute !important; width: 1px; height: 1px;
  padding: 0; margin: -1px; overflow: hidden; clip: rect(0 0 0 0);
  white-space: nowrap; border: 0;
}

/* =========================================================================
   Layout
   ========================================================================= */

.wrap { width: 100%; max-width: var(--wrap); margin: 0 auto; padding: 0 var(--s5); }
.wrap-narrow { max-width: 760px; }

.app-header {
  position: sticky; top: 0; z-index: 40;
  /* The website's own header treatment, value for value: translucent white
     and the same saturate/blur in light, the page's own dark ground in dark,
     and a SOFT line underneath rather than a rule. Every part of it names a
     semantic token, so the whole bar turns over with the system setting. */
  background: var(--header-background);
  -webkit-backdrop-filter: saturate(180%) blur(14px);
          backdrop-filter: saturate(180%) blur(14px);
  border-bottom: 1px solid var(--header-border);
  color: var(--header-text);
}
/* The vertical padding only. `padding: var(--s3) 0` used to be the shorthand
   here, which quietly zeroed the horizontal padding the .wrap class on this
   same element supplies — so below 1180px the header content sat hard against
   the viewport edge while everything else on the page was inset 24px. Nobody
   noticed while the header was the same colour as the page. It is a solid blue
   block now, and a white mark touching the screen edge is obvious. */
/* WRAPS AT EVERY WIDTH, not only on a phone. The narrow-screen rule below
   used to be the only thing that let this bar wrap, which left a dead band
   between 641px and about 900px — a tablet — where the portal header (mark,
   a full cohort name, a certificate badge, the save state, a name and two
   buttons) simply ran off the side of the screen. Measured at 753px before
   this: content reached 808px. */
.app-header .bar {
  display: flex; align-items: center; flex-wrap: wrap; gap: var(--s3) var(--s5);
  min-height: 64px;
  padding-top: var(--s3); padding-bottom: var(--s3);
}
/* `align-items: flex-start` keeps the mark at its own width rather than
   stretching it to whatever the subtitle happens to be — ADMINISTRATOR CONSOLE
   is a much longer word than STAFF SIGN-IN, and a stretched mask would scale
   the artwork with it. */
.brand {
  display: flex; flex-direction: column; align-items: flex-start;
  gap: var(--s1); text-decoration: none; flex: 0 0 auto;
}

/* =========================================================================
   THE MARK — transparent, and recoloured rather than reprinted.

   THERE IS NO PLATE. No background, no border, no padding panel, no shadow:
   the logo sits directly on whatever it is placed on, with its own
   transparency intact. Anything that draws a box here is a bug.

   /logo.png is black artwork on transparency and is not ours to redraw, so
   the colour comes from CSS instead of from a second file:

     * The SPAN is masked by the artwork and filled with a background-colour.
       The mask carries the alpha channel, so the fill lands only where the
       mark has ink and nowhere else — transparent stays transparent, and the
       colour is exact rather than approximated by a filter chain.
     * The IMG inside is the sizing strut. Its width/height attributes are the
       file's true 749x160, so the box is the right shape before a byte
       arrives, and `height`/`width: auto` mean it can never be distorted.
       Once the mask is applied the img itself paints nothing.

   THE COLOUR IS ONE VARIABLE. --brand-logo-color is #134BB8 in the light
   theme and #FFFFFF in the dark one, set in the semantic layer, so the mark
   turns over with the system appearance on its own — no second image, no
   class change, no script. .brand-logo--blue is the default and follows the
   theme; .brand-logo--white is the override for an always-dark surface such
   as the hero, which is dark in BOTH themes and so cannot follow.

   THE FALLBACK IS ALSO TRANSPARENT. Everything that paints a colour lives
   inside @supports. An engine without masks gets the real PNG on its own
   transparency — white via a filter that touches only the colour channels on
   dark, black-on-transparent on light — never a plate, and never a blank
   coloured rectangle where the mark should be.
   ========================================================================= */

.brand-logo {
  display: inline-block;
  background: none; border: 0; padding: 0; box-shadow: none; border-radius: 0;
  line-height: 0;
}
.brand-logo img { display: block; height: 24px; width: auto; }

/* Fallbacks for engines with no mask support. Neither touches alpha, so the
   transparency survives either way, and neither draws a plate.
     --white  brightness(0) forces every pixel of the artwork to black
              (including the anti-aliased greys), invert(1) turns that white.
     --blue   the artwork is already black on transparency, which is legible
              on a light surface; it simply is not recoloured. */
.brand-logo--white img { filter: brightness(0) invert(1); }
.brand-logo--blue  img { filter: brightness(0); }
@media (prefers-color-scheme: dark) {
  /* Without masks the artwork cannot be tinted to an exact blue, but it can
     still be made legible on a dark ground — and still with no plate. */
  .brand-logo--blue img { filter: brightness(0) invert(1); }
}

@supports ((-webkit-mask-image: url("/logo.png")) or (mask-image: url("/logo.png"))) {
  /* -webkit- first, unprefixed second: Safari needs the prefix, and every
     engine that understands both takes the later, standard one. */
  .brand-logo {
    -webkit-mask-image: url("/logo.png");
            mask-image: url("/logo.png");
    -webkit-mask-repeat: no-repeat;
            mask-repeat: no-repeat;
    -webkit-mask-position: center;
            mask-position: center;
    -webkit-mask-size: contain;
            mask-size: contain;
  }
  /* The strut stops painting, but stays in the box and in the layout. The
     accessible name is on the span (role="img"), so nothing is lost. */
  .brand-logo img { opacity: 0; filter: none; }

  /* The themed default. Every header on every page uses this one. */
  .brand-logo,
  .brand-logo--blue  { background-color: var(--brand-logo-color); }
  /* The only fixed variant, for a surface that is dark in both themes. */
  .brand-logo--white { background-color: #FFFFFF; }
}

/* =========================================================================
   AN INTENTIONALLY DARK SURFACE.

   The website uses --ink for exactly this: a hero, a closing band, a footer.
   The Academy uses it the same way and no further — a hero, a major workshop
   callout, an instructor panel. NOT for coursework, forms, tables, consoles
   or anything somebody reads for an hour, which all stay on the light canvas.

   Everything a dark surface needs is scoped here rather than left to the
   page's own ramp, because that ramp is built for a light ground: --text on
   --ink is 1.01:1, --muted is 3.0:1, --brass is 2.2:1. Measured on --ink:
     --on-dark 19.5   --on-dark-2 12.9   --on-dark-3 8.9   --brass-lt 9.2
   ========================================================================= */

.surface-dark {
  background: var(--hero-background); color: var(--on-dark-2);
  border-radius: var(--r-lg); padding: var(--s8) var(--s7);
}
.surface-dark h1, .surface-dark h2, .surface-dark h3, .surface-dark h4,
.surface-dark strong                { color: var(--on-dark); }
.surface-dark .lede                 { color: var(--on-dark-2); }
.surface-dark .muted,
.surface-dark .small,
.surface-dark .xs                   { color: var(--on-dark-3); }
/* Light brass is the ONLY brass allowed here; the light-surface brass is
   2.2:1 on --ink. */
.surface-dark .eyebrow              { color: var(--brass-lt); }
.surface-dark a                     { color: var(--on-dark); }
.surface-dark a:hover               { color: var(--brass-lt); }
.surface-dark hr                    { border-top-color: var(--line-dk); }
.surface-dark .card,
.surface-dark .well                 { background: var(--hero-surface); border-color: var(--line-dk); color: var(--on-dark-2); }
.surface-dark .btn-secondary        { background: transparent; color: var(--on-dark); border-color: rgba(255, 255, 255, .34); }
.surface-dark .btn-secondary:hover:not(:disabled) { border-color: var(--on-dark); color: var(--on-dark); }
/* The page ring's inner band is --paper, which vanishes on --ink. */
.surface-dark :focus-visible        { box-shadow: var(--ring-dark); }
.surface-dark .totp-qr-wrap         { border-color: var(--line-dk); }

.brand .sub {
  font: 600 .7rem/1 var(--sans); letter-spacing: .18em; color: var(--text-muted);
}

.header-right {
  margin-left: auto; display: flex; align-items: center; gap: var(--s3);
  flex-wrap: wrap; min-width: 0;
}
/* A cohort name is the one piece of header content with no natural limit, so
   it is the one that gets told to give way first. `.nowrap` is right for it in
   a wide bar and wrong on a phone: measured at min-content, the portal header
   would not fit a viewport under 467px with the name unbreakable, which is a
   small phone overflowing. Below the breakpoint it wraps like ordinary text. */
.app-header .nowrap { max-width: 100%; }

/* On a phone the bar becomes two lines: brand on the first, everything else on
   the second, full width. Without this the portal header — logo, cohort name,
   save state, two buttons — would either overflow or crush the logo. */
@media (max-width: 640px) {
  .app-header .bar { gap: var(--s3) var(--s4); }
  .brand-logo img { height: 20px; }
  .header-right { margin-left: 0; width: 100%; }
  .app-header .nowrap { white-space: normal; }
  /* A badge is short enough to survive, but not at any length anybody might
     put in one. */
  .app-header .badge { white-space: normal; }
}

.app-main { padding: var(--s7) 0 var(--s9); }

/* A grid ITEM defaults to `min-width: auto`, which means a long word, a wide
   table, or a <pre> can push a `1fr` column past its share and give the whole
   page a horizontal scrollbar. `min-width: 0` is the fix, and it has to be on
   every grid child — not just the ones that look risky today. */
.grid { display: grid; gap: var(--s5); }
.grid > * { min-width: 0; }
.grid-2 { grid-template-columns: repeat(auto-fit, minmax(min(300px, 100%), 1fr)); }
.grid-3 { grid-template-columns: repeat(auto-fit, minmax(min(240px, 100%), 1fr)); }
.grid-4 { grid-template-columns: repeat(auto-fit, minmax(min(190px, 100%), 1fr)); }

.stack > * + * { margin-top: var(--s5); }
.row { display: flex; gap: var(--s3); flex-wrap: wrap; align-items: center; }
.row-end { margin-left: auto; }

/* Two-column workspace: rail plus content. Collapses on narrow screens. */
.workspace { display: grid; grid-template-columns: 268px minmax(0, 1fr); gap: var(--s6); align-items: start; }
.workspace > * { min-width: 0; }
@media (max-width: 940px) {
  .workspace { grid-template-columns: 1fr; }
  .rail { position: static !important; max-height: none !important; }
}

.rail {
  position: sticky; top: 84px; max-height: calc(100vh - 108px); overflow-y: auto;
  padding-right: var(--s2);
}

/* =========================================================================
   Type helpers
   ========================================================================= */

.eyebrow {
  font: 600 var(--t-label)/1 var(--sans); letter-spacing: .16em;
  text-transform: uppercase; color: var(--accent-gold); margin-bottom: var(--s3);
}
.lede { font-size: 1.14rem; line-height: 1.6; color: var(--text-secondary); max-width: var(--read); }
.muted { color: var(--text-muted); }
.small { font-size: var(--t-sm); }
.xs { font-size: var(--t-xs); }
.mono { font-family: var(--mono); font-size: .82rem; letter-spacing: .01em; }
.center { text-align: center; }
.nowrap { white-space: nowrap; }

/* =========================================================================
   Cards and surfaces
   ========================================================================= */

.card {
  background: var(--surface); border: 1px solid var(--border);
  border-radius: var(--r-lg); padding: var(--s6);
}
.card-flush { padding: 0; overflow: hidden; }
.card-accent { border-top: 3px solid var(--accent-gold); }
.card h3 { margin-bottom: var(--s2); }

.well {
  background: var(--surface-recessed); border: 1px solid var(--border-soft);
  border-radius: var(--r); padding: var(--s5);
}

/* An occasional warm premium ground — a takeaway, a milestone, a member
   detail. Not a status, so it carries no status colour. */
.well-warm { background: var(--surface-warm); border-color: var(--surface-gold); }

.note {
  background: var(--surface-gold);
  border: 1px solid var(--border-soft); border-left: 3px solid var(--accent-gold);
  border-radius: 0 var(--r) var(--r) 0; padding: var(--s5);
  color: var(--text-secondary); font-size: var(--t-body);
}
.note strong { color: var(--text-primary); }
.note > :last-child { margin-bottom: 0; }
.note.is-ok   { background: var(--status-ok-wash);   border-left-color: var(--status-ok); }
.note.is-warn { background: var(--status-warn-wash); border-left-color: var(--status-warn); }
.note.is-bad  { background: var(--status-bad-wash);  border-left-color: var(--status-bad); }
.note.is-info { background: var(--status-info-wash); border-left-color: var(--status-info); }

/* =========================================================================
   Buttons. Minimum 44px tall for primary actions; never below 24px.
   ========================================================================= */

.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: var(--s2);
  min-height: 44px; min-width: 44px; padding: 0 var(--s5);
  font: 600 var(--t-sm)/1 var(--sans);
  border-radius: var(--r); border: 1px solid transparent;
  cursor: pointer; text-decoration: none; text-align: center;
  transition: background-color .12s ease, border-color .12s ease, color .12s ease;
}
/* The primary action is the website's blue with white on it (7.7:1), and the
   website's deep blue when pressed (9.7:1) — the same two lines the marketing
   site uses. On a light ground the fill is 7.7:1 against the page, so the
   button carries its own boundary and needs no rim. */
.btn-primary {
  background: var(--primary-action); color: var(--text-on-accent);
  border-color: var(--primary-action-border);
}
.btn-primary:hover:not(:disabled) {
  background: var(--primary-action-hover); color: var(--text-on-accent);
  border-color: var(--primary-action-hover);
}

.btn-secondary { background: var(--surface); color: var(--text-primary); border-color: var(--border-strong); }
.btn-secondary:hover:not(:disabled) { border-color: var(--link); color: var(--link); }

.btn-quiet { background: transparent; color: var(--text-muted); padding: 0 var(--s3); }
.btn-quiet:hover:not(:disabled) { color: var(--text-primary); }

.btn-danger { background: var(--surface); color: var(--status-bad); border-color: var(--status-bad); }
.btn-danger:hover:not(:disabled) { background: var(--status-bad); color: var(--text-on-status); }

/* DISABLED IS NOT JUST FADED. Opacity alone drops a 7.7:1 label to roughly
   3.4:1, so a disabled control states itself instead: a flat grey ground, a
   real border, and text that still clears AA. */
.btn:disabled, .btn[aria-disabled="true"] {
  background: var(--surface-recessed); color: var(--text-muted); border-color: var(--border);
  cursor: not-allowed;
}

.btn-sm { min-height: 34px; padding: 0 var(--s4); font-size: var(--t-xs); }
.btn-block { width: 100%; }

/* =========================================================================
   Forms
   ========================================================================= */

.field { margin-bottom: var(--s5); }
.field > label, .field-label {
  display: block; margin-bottom: var(--s2);
  font: 600 var(--t-body)/1.45 var(--sans); color: var(--text-primary);
}
.field-help { margin: 0 0 var(--s3); font-size: var(--t-sm); color: var(--text-muted); max-width: var(--read); }
.field-example {
  margin: var(--s2) 0 0; font-size: var(--t-sm); color: var(--text-muted); font-style: italic;
}

/* PASSWORD IS IN THIS LIST DELIBERATELY. It was missing, so the one password
   box in the Academy — the staff sign-in — rendered as an unstyled browser
   default: white, black text, 21px tall, ignoring both the palette and the
   44px target minimum. Found by looking at the page rather than at the file. */
input[type="text"], input[type="email"], input[type="password"], input[type="number"],
input[type="search"], input[type="date"], textarea, select {
  width: 100%; min-height: 44px; padding: var(--s3) var(--s4);
  /* White, so a field reads as somewhere to write on the grey canvas. The
     border is --line-strong rather than --line: an input is a CONTROL, and
     1.4.11 wants 3:1 for its boundary — --line is 1.26:1 on white, which is
     right for a divider between cards and useless as the edge of a field. */
  background: var(--surface); color: var(--text-primary);
  border: 1px solid var(--border-strong); border-radius: var(--r);
  font: 400 var(--t-body)/1.55 var(--sans);
  transition: border-color .12s ease;
}
textarea { min-height: 128px; resize: vertical; line-height: 1.65; }
input::placeholder, textarea::placeholder { color: var(--text-muted); opacity: 1; }
input:hover, textarea:hover, select:hover { border-color: var(--text-secondary); }
input:focus-visible, textarea:focus-visible, select:focus-visible { border-color: var(--link); }
input[aria-invalid="true"], textarea[aria-invalid="true"] {
  border-color: var(--status-bad); background: var(--status-bad-wash);
}
/* A disabled field is grey and bordered rather than ghosted, for the same
   reason a disabled button is. */
input:disabled, textarea:disabled, select:disabled {
  background: var(--surface-recessed); color: var(--text-muted);
  border-color: var(--border); cursor: not-allowed;
}

select {
  appearance: none;
  /* Inline SVG chevron: no network request, and it inherits nothing —
     which is the problem, because a data: URL cannot read a custom property.
     So there are two, and the dark one is swapped in by the media query
     below rather than by anything at runtime. */
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='8' viewBox='0 0 12 8'%3E%3Cpath fill='%234E5764' d='M6 8 0 0h12z'/%3E%3C/svg%3E");
  background-repeat: no-repeat; background-position: right var(--s4) center;
  padding-right: var(--s7);
}
@media (prefers-color-scheme: dark) {
  select {
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='8' viewBox='0 0 12 8'%3E%3Cpath fill='%23AFBACA' d='M6 8 0 0h12z'/%3E%3C/svg%3E");
  }
}

/* Choice and multichoice rows. The whole row is the target, not just the box. */
.choice {
  display: flex; align-items: flex-start; gap: var(--s3);
  padding: var(--s3) var(--s4); margin-bottom: var(--s2);
  background: var(--surface); border: 1px solid var(--border-strong);
  border-radius: var(--r); cursor: pointer; min-height: 44px;
  transition: border-color .12s ease, background-color .12s ease;
}
.choice:hover { border-color: var(--text-secondary); }
/* SELECTED is blue, not brass. On the website blue is what "chosen" looks
   like; brass is reserved for milestones and premium detail. */
.choice input { flex: 0 0 auto; width: 18px; height: 18px; margin: 3px 0 0; accent-color: var(--primary-action); }
.choice span { font-size: var(--t-body); line-height: 1.55; }
.choice:has(input:checked) { border-color: var(--link); background: var(--surface-selected); }
.choice:has(input:focus-visible) { box-shadow: var(--focus-ring); }

/* Repeating list input */
.list-input .list-row { display: flex; gap: var(--s2); margin-bottom: var(--s2); align-items: flex-start; }
.list-input .list-row input { flex: 1; }
.list-input .list-remove {
  flex: 0 0 auto; width: 44px; height: 44px; display: inline-flex;
  align-items: center; justify-content: center;
  background: transparent; color: var(--text-muted); border: 1px solid var(--border);
  border-radius: var(--r); cursor: pointer; font-size: 1.1rem;
}
.list-input .list-remove:hover { color: var(--status-bad); border-color: var(--status-bad); }

.field-error { margin: var(--s2) 0 0; font-size: var(--t-sm); font-weight: 600; color: var(--status-bad); }
.field-warn  { margin: var(--s2) 0 0; font-size: var(--t-sm); font-weight: 600; color: var(--status-warn); }

fieldset { border: 0; padding: 0; margin: 0 0 var(--s5); }
legend { padding: 0; margin-bottom: var(--s3); font: 600 var(--t-body)/1.4 var(--sans); color: var(--text-primary); }

/* =========================================================================
   Badges and pills. Every one carries a WORD, never colour alone.
   ========================================================================= */

.badge {
  display: inline-flex; align-items: center; gap: var(--s1);
  padding: 3px var(--s3); border-radius: 999px;
  font: 600 var(--t-label)/1.6 var(--sans); letter-spacing: .04em;
  border: 1px solid currentColor; white-space: nowrap;
}
.badge-ok    { color: var(--status-ok);    background: var(--status-ok-wash); }
.badge-warn  { color: var(--status-warn);  background: var(--status-warn-wash); }
.badge-bad   { color: var(--status-bad);   background: var(--status-bad-wash); }
.badge-info  { color: var(--status-info);  background: var(--status-info-wash); }
.badge-brass { color: var(--accent-gold); background: var(--surface-gold); }
.badge-quiet { color: var(--text-muted); background: var(--surface-recessed); border-color: var(--border-strong); }

/* =========================================================================
   Progress
   ========================================================================= */

.progress {
  height: 8px; background: var(--surface-recessed); border-radius: 999px; overflow: hidden;
}
.progress > span {
  display: block; height: 100%; background: var(--accent-gold);
  transition: width .3s ease;
}
.progress-label { display: flex; justify-content: space-between; gap: var(--s3); margin-bottom: var(--s2); font-size: var(--t-sm); color: var(--text-muted); }

.ring { display: inline-grid; place-items: center; position: relative; width: 92px; height: 92px; }
.ring svg { transform: rotate(-90deg); width: 92px; height: 92px; }
.ring circle { fill: none; stroke-width: 7; }
.ring .track { stroke: var(--border); }
.ring .fill { stroke: var(--accent-gold); stroke-linecap: round; transition: stroke-dashoffset .4s ease; }
.ring .value { position: absolute; font: 700 1.15rem/1 var(--sans); color: var(--text-primary); }

/* =========================================================================
   Course map and timeline
   ========================================================================= */

.map { list-style: none; margin: 0; padding: 0; }
.map li { margin: 0 0 var(--s1); max-width: none; }
.map button {
  display: grid; grid-template-columns: 52px 1fr; gap: var(--s3); align-items: start;
  width: 100%; padding: var(--s3); min-height: 44px;
  background: transparent; border: 1px solid transparent; border-radius: var(--r);
  color: var(--text-secondary); text-align: left; cursor: pointer; font: inherit;
  transition: background-color .12s ease, border-color .12s ease;
}
.map button:hover { background: var(--surface-recessed); }
/* ACTIVE navigation is blue — the website's language for "you are here". */
.map button[aria-current="true"] {
  background: var(--surface-selected); border-color: var(--link); color: var(--text-primary);
}
.map .clock { font: 500 var(--t-xs)/1.5 var(--mono); color: var(--text-muted); padding-top: 3px; }
.map .title { font-size: var(--t-sm); line-height: 1.45; color: var(--text-secondary); }
.map .done { color: var(--status-ok); }
.map li.is-break button { background: transparent; }
.map li.is-break button { opacity: .68; }
.map li.is-break .title { font-style: italic; }

.timeline { display: flex; flex-direction: column; gap: 2px; }
.tl-row {
  display: grid; grid-template-columns: 116px 1fr auto; gap: var(--s4);
  align-items: center; padding: var(--s3) var(--s4);
  border-left: 3px solid var(--border); background: var(--surface);
}
.tl-row.is-now { border-left-color: var(--accent-gold); background: var(--surface-gold); }
.tl-row.is-break { background: transparent; }
.tl-clock { font: 500 var(--t-xs)/1.4 var(--mono); color: var(--text-muted); }
.tl-title { font-size: var(--t-sm); color: var(--text-secondary); }
.tl-mins { font: 500 var(--t-xs)/1 var(--mono); color: var(--text-muted); }
@media (max-width: 620px) {
  .tl-row { grid-template-columns: 1fr auto; }
  .tl-clock { grid-column: 1 / -1; }
}

/* =========================================================================
   The I.N.S.T.A.L.L. strip — the visual spine of the programme
   ========================================================================= */

.install {
  display: grid; grid-template-columns: repeat(7, 1fr); gap: var(--s2);
}
@media (max-width: 900px) { .install { grid-template-columns: repeat(4, 1fr); } }
@media (max-width: 560px) { .install { grid-template-columns: repeat(2, 1fr); } }

.install-step {
  background: var(--surface); border: 1px solid var(--border);
  border-top: 3px solid var(--accent-gold); border-radius: var(--r);
  padding: var(--s4) var(--s3); text-align: left;
  transition: border-color .12s ease, transform .12s ease;
}
.install-step.is-active { border-top-color: var(--accent-gold); background: var(--surface-gold); }
.install-letter { font: 700 1.6rem/1 var(--sans); color: var(--accent-gold); }
.install-title { margin-top: var(--s2); font: 700 var(--t-sm)/1.35 var(--sans); color: var(--text-primary); }
.install-q { margin-top: var(--s2); font-size: var(--t-xs); line-height: 1.5; color: var(--text-muted); }

/* =========================================================================
   Flow diagram — Trigger -> ... -> Measurement
   ========================================================================= */

.flow { display: flex; flex-wrap: wrap; gap: var(--s2); align-items: stretch; }
.flow-step {
  flex: 1 1 118px; min-width: 118px;
  background: var(--surface); border: 1px solid var(--border);
  border-radius: var(--r); padding: var(--s3);
}
.flow-step .n {
  font: 600 var(--t-label)/1 var(--mono); letter-spacing: .1em; color: var(--accent-gold); text-transform: uppercase;
}
.flow-step .t { margin-top: var(--s2); font: 700 var(--t-sm)/1.35 var(--sans); color: var(--text-primary); }
.flow-step .d { margin-top: var(--s1); font-size: var(--t-xs); line-height: 1.5; color: var(--text-muted); }
.flow-step.is-human { border-color: var(--accent-gold); background: var(--surface-gold); }

/* =========================================================================
   Maturity ladder
   ========================================================================= */

.ladder { display: flex; flex-direction: column-reverse; gap: var(--s2); }
.rung {
  display: grid; grid-template-columns: 44px 1fr; gap: var(--s4);
  padding: var(--s4); background: var(--surface);
  border: 1px solid var(--border); border-radius: var(--r);
}
.rung.is-self { border-color: var(--accent-gold); background: var(--surface-gold); }
.rung .lvl {
  display: grid; place-items: center; width: 36px; height: 36px; border-radius: 50%;
  background: var(--surface-recessed); color: var(--accent-gold); font: 700 1rem/1 var(--sans);
}
.rung.is-self .lvl { background: var(--accent-gold); color: var(--text-on-gold); }
.rung h4 { font-size: var(--t-body); }
.rung p { margin: var(--s1) 0 0; font-size: var(--t-sm); color: var(--text-muted); }
.rung .eg { margin-top: var(--s2); font-size: var(--t-sm); color: var(--text-secondary); font-style: italic; }

/* =========================================================================
   Tables
   ========================================================================= */

.table-scroll { overflow-x: auto; -webkit-overflow-scrolling: touch; }
table.data {
  width: 100%; border-collapse: collapse; font-size: var(--t-sm); min-width: 640px;
  background: var(--surface);
}
table.data th, table.data td {
  padding: var(--s4); text-align: left; vertical-align: top;
  border-bottom: 1px solid var(--border-soft);
}
table.data thead th {
  position: sticky; top: 0; z-index: 1;
  background: var(--surface-recessed); color: var(--text-primary);
  font: 600 var(--t-label)/1.6 var(--sans); letter-spacing: .08em; text-transform: uppercase;
  white-space: nowrap; border-bottom: 1px solid var(--border);
}
table.data tbody tr:hover { background: var(--surface-selected); }
table.data td.num { font-family: var(--mono); font-size: .82rem; text-align: right; }

/* =========================================================================
   Tabs
   ========================================================================= */

.tabs { display: flex; gap: var(--s1); border-bottom: 1px solid var(--border); overflow-x: auto; }
.tabs button {
  padding: var(--s3) var(--s4); min-height: 44px;
  background: transparent; border: 0; border-bottom: 2px solid transparent;
  color: var(--text-muted); font: 600 var(--t-sm)/1 var(--sans); cursor: pointer; white-space: nowrap;
}
.tabs button:hover { color: var(--text-primary); }
/* A SELECTED tab is blue, like every other "this one" on the website. */
.tabs button[aria-selected="true"] { color: var(--link); border-bottom-color: var(--link); }

/* =========================================================================
   Toasts and dialogs
   ========================================================================= */

.toasts {
  position: fixed; right: var(--s5); bottom: var(--s5); z-index: 60;
  display: flex; flex-direction: column; gap: var(--s2);
  max-width: min(400px, calc(100vw - 2 * var(--s5)));
}
.toast {
  padding: var(--s3) var(--s4); border-radius: var(--r);
  background: var(--surface); border: 1px solid var(--border);
  box-shadow: var(--shadow); font-size: var(--t-sm); color: var(--text-secondary);
}
.toast.is-ok  { border-color: var(--status-ok); }
.toast.is-bad { border-color: var(--status-bad); }

dialog {
  padding: 0; border: 1px solid var(--border); border-radius: var(--r-lg);
  background: var(--surface); color: var(--text-secondary);
  max-width: min(560px, calc(100vw - 2 * var(--s5))); width: 100%;
  box-shadow: var(--shadow);
}
dialog::backdrop { background: var(--scrim); }
dialog .dlg-body { padding: var(--s6); }
dialog h2 { margin-bottom: var(--s3); }

/* =========================================================================
   Save indicator — quiet, and never in the way
   ========================================================================= */

.saver {
  display: inline-flex; align-items: center; gap: var(--s2);
  font-size: var(--t-sm); color: var(--text-muted); min-height: 24px;
}
.saver .dot { width: 7px; height: 7px; border-radius: 50%; background: var(--border-strong); }
.saver.is-saving .dot { background: var(--status-warn); }
.saver.is-saved  .dot { background: var(--status-ok); }
.saver.is-error  .dot { background: var(--status-bad); }

/* =========================================================================
   Drag-and-drop workflow mapper
   ========================================================================= */

.dnd { display: grid; gap: var(--s4); }
.dnd-col {
  background: var(--surface-recessed); border: 1px dashed var(--border-strong);
  border-radius: var(--r); padding: var(--s5); min-height: 96px;
}
.dnd-col.is-over { border-color: var(--link); background: var(--surface-selected); }
.dnd-col h4 { font-size: var(--t-label); color: var(--text-primary); margin-bottom: var(--s3); letter-spacing: .08em; text-transform: uppercase; }
.dnd-item {
  display: flex; align-items: center; gap: var(--s3);
  padding: var(--s3) var(--s4); margin-bottom: var(--s2); min-height: 44px;
  background: var(--surface); border: 1px solid var(--border-strong);
  border-radius: var(--r); cursor: grab; font-size: var(--t-body);
}
.dnd-item:active { cursor: grabbing; }
.dnd-item.is-dragging { opacity: .45; }
.dnd-item .handle { color: var(--text-muted); font-family: var(--mono); }
/* Keyboard alternative to dragging. Always present, never a fallback. */
.dnd-item .move { display: flex; gap: var(--s1); margin-left: auto; }
.dnd-item .move button {
  width: 28px; height: 28px; display: grid; place-items: center;
  background: transparent; border: 1px solid var(--border-strong); border-radius: var(--r);
  color: var(--text-muted); cursor: pointer; font-size: .7rem;
}
.dnd-item .move button:hover { color: var(--link); border-color: var(--link); }

/* =========================================================================
   Version comparison
   ========================================================================= */

.compare { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); gap: var(--s4); }
.compare > * { min-width: 0; }
@media (max-width: 780px) { .compare { grid-template-columns: 1fr; } }
.compare-pane { background: var(--surface-recessed); border: 1px solid var(--border); border-radius: var(--r); padding: var(--s5); }
.compare-pane h4 { font-size: var(--t-label); color: var(--text-primary); letter-spacing: .08em; text-transform: uppercase; margin-bottom: var(--s3); }
.compare-pane pre { margin: 0; white-space: pre-wrap; word-wrap: break-word; font-family: var(--mono); font-size: var(--t-xs); line-height: 1.65; color: var(--text-secondary); }
.compare-pane.is-v2 { border-color: var(--accent-gold); }

/* =========================================================================
   Code and prompt blocks
   ========================================================================= */

pre.block {
  margin: 0 0 var(--s4); padding: var(--s4) var(--s5);
  background: var(--surface-recessed); border: 1px solid var(--border); border-radius: var(--r);
  font: 400 var(--t-xs)/1.7 var(--mono); color: var(--text-secondary);
  white-space: pre-wrap; word-wrap: break-word; overflow-wrap: anywhere;
  max-height: 460px; overflow-y: auto;
}
.copy-row { display: flex; gap: var(--s2); align-items: center; margin-bottom: var(--s2); }

/* Authenticator setup. The image is generated by our own API as a data URL;
   no QR service receives the private setup value.

   FIXED LIGHT, DELIBERATELY, IN BOTH THEMES. This is one of the three places
   in the Academy where a colour does not follow the system setting, and the
   reason is not aesthetic: a QR code is read by a camera, which needs the
   dark-on-light polarity and the quiet zone around the code to lock on. A
   dark-mode QR plate is a QR code that will not scan. The padding IS the
   quiet zone; neither it nor the white may be themed away. */
.totp-qr-wrap {
  display: inline-flex; padding: var(--s3); margin-bottom: var(--s2);
  background: #FFFFFF; border: 1px solid var(--border); border-radius: var(--r);
}
.totp-qr { display: block; width: min(256px, 72vw); height: auto; }

/* =========================================================================
   Score bars
   ========================================================================= */

.score-row { display: grid; grid-template-columns: 1fr 120px 56px; gap: var(--s3); align-items: center; margin-bottom: var(--s3); font-size: var(--t-sm); }
.score-row .nm { color: var(--text-secondary); }
.score-row .num { font-family: var(--mono); text-align: right; color: var(--text-secondary); }
@media (max-width: 560px) { .score-row { grid-template-columns: 1fr 56px; } .score-row .progress { grid-column: 1 / -1; } }

/* =========================================================================
   Print — the portal is not a print target, but a worksheet might be.
   ========================================================================= */

/* PAPER IS ALWAYS LIGHT. A certificate or a worksheet printed from a machine
   set to dark mode must not come out as a black rectangle, so this block
   forces the light values back — including for the hero, which is dark by
   design in both themes on screen. `color-scheme: light` also stops the
   browser from tinting native controls and scrollbars in the output.
   Nothing here touches the QR code: it is already, and stays, white. */
@media print {
  @page { margin: 16mm; }
  :root { color-scheme: light; }
  body { background: #fff; color: #000; }
  .app-header, .rail, .toasts, .btn, .tabs, .skip { display: none !important; }
  .card, .well, .note { background: #fff; border: 1px solid #ccc; color: #000; }
  a { color: #000; }
  h1, h2, h3, h4, strong { color: #000 !important; }
  .surface-dark, .surface-dark .card, .surface-dark .well {
    background: #fff !important; color: #000 !important; border-color: #ccc !important;
  }
  .totp-qr-wrap { background: #fff !important; border-color: #ccc !important; }
}
