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

/* ============================================================
   THEME
   Every colour in this file is a variable, so a theme is a matter of
   redefining them rather than restyling components.
   ------------------------------------------------------------
   The variables are split by ROLE, not by hue, and the reason is the whole
   trick behind supporting two themes without auditing every rule twice:

     --primary / --accent-fill / --success-fill   solid FILLS carrying white text.
       These stay dark in BOTH themes, because white-on-dark works on a light page
       and on a dark one. A "dark mode" version of a filled button is a trap: make
       it lighter to suit the background and the white label stops being readable.

     --heading / --accent / --*-text              TEXT colours.
       These MUST flip. `color: var(--primary)` — a near-black navy — was used for
       every heading, and on a dark background it is invisible. Splitting the text
       role out from the fill role is what made this feature tractable at all.

     --tint-*                                     pale panel backgrounds and the
       text and borders that sit on them (notices, badges, answer feedback).

   Every foreground/background pair below was checked numerically against WCAG
   2.1 AA rather than judged by eye — see DESIGN.md. Eyeballing dark palettes is
   unreliable in a way that eyeballing light ones is not: light text on a dark
   field reads as higher contrast than it measures.
   ============================================================ */

:root {
  /* ============================================================
     BRAND ALIGNMENT (2026-08-14): retinted from this app's original
     navy/blue palette to match the gold/cream/ink identity used across
     the rest of stjohnlynch.com (see css/style.css at the site root) —
     landing on this course from the main site no longer feels like a
     different product. Every pairing below was re-checked against WCAG
     2.1 AA numerically, not judged by eye (see the test suite's
     accessibility groups, which re-verify this on every run); the retint
     only touches NEUTRAL/BRAND roles (primary, accent, text, bg, border,
     heading) — the semantic status colours below (success/danger/warning
     and the tint-* family) are left exactly as they were, since their
     meaning (pass/fail/warning) is independent of brand identity. */
  --primary: #1a1a2e;
  /* "ink" — matches the main site's --ink. 17.06:1 as text on white. */
  --primary-light: #3a3a5c;
  /* "ink-mid" — matches the main site's --ink-mid. */
  --accent: #2d4a7a;
  /* Navy-blue link colour, matches the main site's --accent exactly.
     8.13:1 as text on --bg, 8.85:1 on white — both well clear of the old
     value's already-fixed 4.74:1 worst case. */
  --accent-light: #3d5f99;
  /* Matches the main site's --accent-lt. */
  --success: #1e8449;
  --success-light: #27ae60;
  --danger: #c0392b;
  --warning: #d35400;
  --bg: #f7f5f0;
  /* "cream" — matches the main site's --cream exactly. */
  --bg-white: #ffffff;
  --text: #1a1a2e;
  /* "ink" — 15.66:1 on --bg, 17.06:1 on white. */
  --text-muted: #3a3a5c;
  /* "ink-mid" — 9.94:1 on --bg, 10.83:1 on white. Comfortably clears the
     old value's already-fixed 5.96:1/6.46:1 worst case, and still reads
     as a clear step lighter than --text the way the muted/primary
     hierarchy needs. */
  --border: #d8d4cc;
  /* "rule" — matches the main site's --rule. */
  --shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
  --shadow-md: 0 4px 16px rgba(0, 0, 0, 0.12);
  --radius: 8px;
  --radius-lg: 12px;
  --gold: #b08c5a;
  /* Matches the main site's --gold exactly — the primary CTA colour (see
     .btn-primary below), mirroring css/style.css's .btn-gold convention
     of gold fill + ink text rather than white text (gold does not clear
     4.5:1 against white). 5.48:1 with ink text. */
  --gold-light: #c9a97a;
  /* Matches the main site's --gold-lt — hover state for gold fills.
     7.67:1 with ink text. */

  /* --- Role variables (light theme) --- */
  --heading: #1a1a2e;
  /* heading text; was color: var(--primary) */
  --heading-sub: #3a3a5c;
  /* sub-heading text; 9.94:1 on --bg */
  --accent-fill: #2d4a7a;
  /* fill behind white text: 8.85:1 */
  --success-fill: #1a7a4a;
  /* 5.35:1 with white — see the .btn-success fix */
  --success-fill-hover: #156b3c;
  /* 6.56:1 */
  --success-text: #18703f;
  /* 5.56:1 on the pale green tint */
  --danger-text: #c0392b;
  --warning-text: #b34700;
  /* 5.02:1 on the orange tint; #d35400 was 3.80:1 */

  /* Pale panel tints. Near-identical shades were collapsed into one variable
     each: #e8f4fd/#eaf3fb and #fdf3f2/#fdf0ee differ by a couple of points per
     channel and are indistinguishable on screen, but every extra variable is
     another value to keep honest in two themes. */
  --tint-info: #eaf3fb;
  --tint-info-text: #1a5fa0;
  /* 5.87:1 */
  --tint-ok: #eaf7f0;
  --tint-ok-text: #1a7a4a;
  /* 4.85:1 */
  --tint-ok-border: #a9dfbf;
  --tint-warn: #fef9e7;
  --tint-warn-text: #6b4e00;
  /* 7.34:1 */
  --tint-warn-border: #e0cd8a;
  --tint-bad: #fdecea;
  --tint-bad-soft: #fdf3f2;
  --tint-bad-text: #a93226;
  /* 5.95:1 */
  --tint-orange: #fdf3e8;
  --notice-bar: #8a6d1f;
  /* white label on it: 4.90:1 */
  --notice-marker: #c9a227;
  /* Fixed like --notice-bar, not theme-variable like --danger: --danger is a
     TEXT-role red (it must flip to something visible on a dark background,
     which makes it too light — 2.99:1 — for white text on top of it). This is
     a FILL, so it stays this same dark shade in both themes; see the colour
     variable comment block at the top of this file for why that split exists. */
  --danger-fill: #a93226;
  /* white text on it: 6.62:1 */
}

/* DARK THEME
   Applied by a data-theme attribute on <html> rather than by a media query
   alone, so a visitor can override their system setting. The default still
   follows the system — see the block below and theme.js.

   The page background is #12171e rather than pure black: on an LCD, white text
   on #000 produces halation that makes long passages harder to read, and this
   site is mostly long passages. The card surface is one step lighter so panels
   remain distinguishable from the page (1.11:1 — deliberately subtle, since
   contrast between two backgrounds is depth, not information). */
[data-theme="dark"] {
  /* Same brand-alignment retint as the light theme above — dark mode has no
     main-site equivalent to match hex-for-hex (the main site has no dark
     theme), so these are original values extrapolated from the same
     gold/cream/ink family, each re-checked against WCAG 2.1 AA rather than
     judged by eye. */
  --primary: #1f1a2e;
  /* dark "ink" */
  --primary-light: #2f2a42;
  --accent: #d9b892;
  /* warm gold — link/border/focus text role: 9.62:1 on --bg, 8.66:1 on a card */
  --accent-light: #e6cead;
  --success: #27ae60;
  --success-light: #2ecc71;
  --danger: #e57373;
  --warning: #eda45f;
  --bg: #12171e;
  --bg-white: #1b2129;
  --text: #f0ece0;
  /* warm cream — 13.72:1 on a card */
  --text-muted: #b8ae9a;
  /* warm muted tan — 7.37:1 on a card, still clears AA */
  --border: #3a3428;
  /* Shadows are nearly invisible on a dark field, so they lean on opacity
     rather than colour to keep giving cards their lift. */
  --shadow: 0 2px 8px rgba(0, 0, 0, 0.45);
  --shadow-md: 0 4px 16px rgba(0, 0, 0, 0.6);
  --gold: #b08c5a;
  /* unchanged: a FILL, stays the same bright gold in both themes, always
     paired with --primary (dark ink) text — see .btn-primary. 5.42:1. */
  --gold-light: #c9a97a;
  /* unchanged: 7.58:1 with --primary text */

  --heading: #f0ece0;
  /* 13.72:1 on a card */
  --heading-sub: #d9cbb0;
  /* 10.12:1 on a card */
  --accent-fill: #2d4a7a;
  /* same navy-blue fill as the light theme (this role stays dark in both
     themes by design — see the THEME comment block at the top of this
     file): white text needs 8.85:1 */
  --success-fill: #1a7a4a;
  --success-fill-hover: #156b3c;
  --success-text: #5fc98d;
  /* 7.89:1 */
  --danger-text: #f08b82;
  /* 6.73:1 */
  --warning-text: #eda45f;
  /* 7.77:1 */

  /* Tints become dark, low-saturation fields rather than pale ones. Inverting
     the lightness is not enough on its own — a saturated dark red reads as an
     error state far more loudly than a pale pink does, so these are muted. */
  --tint-info: #16293a;
  --tint-info-text: #9ccdf0;
  /* 8.78:1 */
  --tint-ok: #14291f;
  --tint-ok-text: #79d0a0;
  /* 8.30:1 */
  --tint-ok-border: #2a5240;
  --tint-warn: #2a2413;
  --tint-warn-text: #e8cf8a;
  /* 10.09:1 */
  --tint-warn-border: #574a25;
  --tint-bad: #2e1a19;
  --tint-bad-soft: #2a1817;
  --tint-bad-text: #f0a49c;
  /* 8.21:1 */
  --tint-orange: #2d1f12;
  --notice-bar: #8a6d1f;
  --notice-marker: #d9b545;
  --danger-fill: #a93226;
  /* unchanged: still 6.62:1 for white text */
}

/* NO-JAVASCRIPT FALLBACK.
   theme.js normally sets data-theme explicitly, so the rule above is all that
   runs. But if JavaScript is unavailable no attribute is ever set, and someone
   whose system is in dark mode would get the light theme with no way to change
   it. `:root:not([data-theme])` covers exactly that case and nothing else: once
   the attribute exists — whether from the saved preference or the toggle — this
   block stops applying and the visitor's explicit choice wins over the system's.

   The variables are duplicated rather than shared with a common class because a
   media query cannot be re-scoped, and `@import` or a preprocessor would mean a
   build step. This file is hand-written CSS on purpose. */
@media (prefers-color-scheme: dark) {
  :root:not([data-theme]) {
    --primary: #1f1a2e;
    --primary-light: #2f2a42;
    --accent: #d9b892;
    --accent-light: #e6cead;
    --success: #27ae60;
    --success-light: #2ecc71;
    --danger: #e57373;
    --warning: #eda45f;
    --bg: #12171e;
    --bg-white: #1b2129;
    --text: #f0ece0;
    --text-muted: #b8ae9a;
    --border: #3a3428;
    --shadow: 0 2px 8px rgba(0, 0, 0, 0.45);
    --shadow-md: 0 4px 16px rgba(0, 0, 0, 0.6);
    --gold: #b08c5a;
    --gold-light: #c9a97a;
    --heading: #f0ece0;
    --heading-sub: #d9cbb0;
    --accent-fill: #2d4a7a;
    --success-fill: #1a7a4a;
    --success-fill-hover: #156b3c;
    --success-text: #5fc98d;
    --danger-text: #f08b82;
    --warning-text: #eda45f;
    --tint-info: #16293a;
    --tint-info-text: #9ccdf0;
    --tint-ok: #14291f;
    --tint-ok-text: #79d0a0;
    --tint-ok-border: #2a5240;
    --tint-warn: #2a2413;
    --tint-warn-text: #e8cf8a;
    --tint-warn-border: #574a25;
    --tint-bad: #2e1a19;
    --tint-bad-soft: #2a1817;
    --tint-bad-text: #f0a49c;
    --tint-orange: #2d1f12;
    --notice-bar: #8a6d1f;
    --notice-marker: #d9b545;
    --danger-fill: #a93226;
  }
}

body {
  font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
  background: var(--bg);
  color: var(--text);
  line-height: 1.6;
  font-size: 16px;
  min-height: 100vh;
  display: flex;
  flex-direction: column;
}

main {
  flex: 1;
  padding: 48px 0;
}

a {
  color: var(--accent);
}

/* ===== LAYOUT ===== */

.container {
  max-width: 1320px;
  margin: 0 auto;
  padding: 0 24px;
}

/* ===== PROMOTIONAL STRIP =====
   A single banner-style card, sized to the same .container max-width as
   everything else on the page (never a separate, mismatched width), holding
   both partners side by side inside one card so it reads as one deliberate
   placement rather than two unrelated widgets. Visible at every viewport —
   unlike the ultra-wide-only side rails this replaced, there is no
   min-width breakpoint gating it, so it needs no separate check in the
   responsive test suite beyond the usual overflow/accessibility passes. */
.promo-strip {
  margin-top: 32px;
  background: var(--bg-white);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow);
  overflow: hidden;
}

.promo-strip-bar {
  display: flex;
  align-items: center;
  gap: 8px;
  background: var(--primary);
  color: white;
  padding: 10px 24px;
  font-size: 0.78rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.06em;
}

.promo-strip-icon {
  font-size: 0.85rem;
}

.promo-strip-body {
  display: grid;
  grid-template-columns: 1fr 1px 1fr;
}

.promo-card {
  padding: 24px 28px;
}

/* A hairline divider between the two cards, not a border on each — a
   border on each half would double up in the middle and look uneven next
   to the single-pixel outer border. */
.promo-divider {
  background: var(--border);
}

.promo-eyebrow {
  display: block;
  font-size: 0.78rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--text-muted);
  margin-bottom: 8px;
}

.promo-heading {
  font-size: 1.1rem;
  font-weight: 700;
  color: var(--heading);
  margin-bottom: 10px;
}

.promo-list {
  list-style: none;
  margin-bottom: 16px;
}

.promo-list li {
  font-size: 0.9rem;
  line-height: 1.55;
  color: var(--text);
  padding-left: 16px;
  position: relative;
  margin-bottom: 8px;
}

.promo-list li::before {
  content: '\2192';
  position: absolute;
  left: 0;
  color: var(--accent);
  font-size: 0.8rem;
}

.promo-cta {
  display: inline-block;
  font-size: 0.9rem;
  font-weight: 600;
  color: var(--accent);
  text-decoration: underline;
}

.promo-cta:hover {
  text-decoration: none;
}

/* Only the St John Lynch & Co card has two CTAs (internal About link,
   external stjohnlynch.com link) — AskRiskIE keeps its single external
   link, so this only ever wraps one .promo-cta pair. */
.promo-cta-row {
  display: flex;
  flex-wrap: wrap;
  gap: 6px 20px;
}

/* Below a two-column-comfortable width the two halves stack instead of
   squeezing side by side — the divider becomes a horizontal rule between
   them rather than a column, and disappears as its own grid track. */
@media (max-width: 700px) {
  .promo-strip-body {
    grid-template-columns: 1fr;
  }

  .promo-divider {
    display: none;
  }

  .promo-card+.promo-card {
    border-top: 1px solid var(--border);
  }
}

/* ===== HEADER ===== */

.site-header {
  background: var(--primary);
  color: white;
  position: sticky;
  top: 0;
  z-index: 100;
  box-shadow: 0 2px 12px rgba(0, 0, 0, 0.2);
}

.header-inner {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 16px 24px;
}

.logo {
  display: flex;
  align-items: center;
  gap: 12px;
  text-decoration: none;
  color: white;
}

.logo-icon {
  font-size: 2rem;
  line-height: 1;
}

.logo-text {
  display: flex;
  flex-direction: column;
}

.logo-title {
  font-size: 1.1rem;
  font-weight: 700;
  letter-spacing: -0.02em;
}

/* VERSION CHIP — states which edition of the standard the course teaches.
   "IEC 62304" on its own is ambiguous now that Edition 2 is in development, and
   a learner who lands straight on the quiz would otherwise never be told. White
   on the navy header is 11.6:1, so it is legible despite being small. */
.version-chip {
  display: inline-block;
  align-self: flex-start;
  margin-top: 5px;
  padding: 2px 8px;
  background: rgba(255, 255, 255, 0.14);
  border: 1px solid rgba(255, 255, 255, 0.4);
  border-radius: 20px;
  color: #ffffff;
  font-size: 0.68rem;
  font-weight: 600;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  white-space: nowrap;
}

.logo-sub {
  /* Raised from 0.75rem (12px), which was the smallest text anywhere on the
     site. The opacity below already marks it as secondary, so it did not need
     to be shrunk as well — two signals for the same thing made it look weak. */
  font-size: 0.8rem;
  opacity: 0.7;
  letter-spacing: 0.02em;
}

/* ===== NAVIGATION ===== */

.site-nav {
  display: flex;
  gap: 4px;
  align-items: center;
}

/* ===== NAV TOGGLE (HAMBURGER) =====
   Hidden entirely above the mobile breakpoint — see the display:none here
   and the display:inline-flex that overrides it inside the @media block in
   RESPONSIVE, the same "hidden until narrow" pattern the rest of this file
   uses rather than a JS-driven show/hide. Sized and styled identically to
   .theme-toggle so the two read as a matched pair when they do appear
   together on a narrow screen. */
.nav-toggle {
  display: none;
  align-items: center;
  justify-content: center;
  width: 40px;
  height: 40px;
  padding: 0;
  background: rgba(255, 255, 255, 0.12);
  border: 1px solid rgba(255, 255, 255, 0.4);
  border-radius: var(--radius);
  color: white;
  cursor: pointer;
  transition: background 0.2s, border-color 0.2s;
}

.nav-toggle:hover {
  background: rgba(255, 255, 255, 0.22);
  border-color: rgba(255, 255, 255, 0.7);
}

.nav-toggle-icon svg {
  display: block;
  width: 20px;
  height: 20px;
  stroke: currentColor;
}

/* ===== THEME TOGGLE ===== */
/* Sits at the end of the nav on every page. It is a real <button> with
   aria-pressed rather than a checkbox styled to look like a switch, because a
   button already carries "activate me" semantics and aria-pressed already
   carries the on/off state — no extra ARIA needed to explain a custom widget.
   The icon is aria-hidden and the accessible name comes from the visually
   hidden label, so a screen reader announces "Dark theme, toggle button,
   pressed" rather than reading out a moon emoji. */
.theme-toggle {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  /* 40px square: comfortably above the 24px minimum for a touch target, and
     matching the height of the nav links beside it. */
  width: 40px;
  height: 40px;
  margin-left: 8px;
  padding: 0;
  background: rgba(255, 255, 255, 0.12);
  border: 1px solid rgba(255, 255, 255, 0.4);
  border-radius: var(--radius);
  color: white;
  font-size: 1.05rem;
  line-height: 1;
  cursor: pointer;
  transition: background 0.2s, border-color 0.2s;
}

.theme-toggle:hover {
  background: rgba(255, 255, 255, 0.22);
  border-color: rgba(255, 255, 255, 0.7);
}

/* The icon used to be a Unicode glyph (☽ / ☀). Fonts do not draw those
   glyphs centred in their own character box — the crescent moon in
   particular sits visibly left-and-up of centre in most fonts, which is
   what someone spotted. An SVG has no such font-dependent metrics: its
   viewBox is symmetric, so centring the box centres the icon, on every
   platform, the same way the book icon on the Learn page was fixed earlier.
   Both icons are always in the markup; only one is ever displayed, chosen
   by data-theme rather than by JavaScript swapping element content — see
   theme.js, which only has to keep the tooltip and accessible-name text in
   sync now, not the icon itself. */
.theme-toggle-icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
}

/* width/height/stroke only — NOT display. A "one class + one element"
   selector like this one is MORE specific than the single-class
   .icon-sun/.icon-moon rules below, so a display value set here would beat
   theirs regardless of source order and both icons would show at once
   (exactly what happened the first time this was written). */
.theme-toggle-icon svg {
  width: 18px;
  height: 18px;
  stroke: currentColor;
}

.icon-moon {
  display: block;
}

.icon-sun {
  display: none;
}

:root[data-theme="dark"] .icon-moon {
  display: none;
}

:root[data-theme="dark"] .icon-sun {
  display: block;
}

/* The toggle lives on the navy header in both themes, so it is styled against
   that rather than against the page background — which is why these are
   rgba(255,255,255,…) values and not theme variables. */

.nav-link {
  background: transparent;
  border: none;
  color: rgba(255, 255, 255, 0.7);
  padding: 8px 18px;
  border-radius: var(--radius);
  cursor: pointer;
  font-size: 0.95rem;
  /* raised from 0.9rem to sit closer to the 16px body copy */
  font-weight: 500;
  text-decoration: none;
  transition: background 0.2s, color 0.2s;
  display: inline-block;
}

.nav-link:hover {
  background: rgba(255, 255, 255, 0.1);
  color: white;
}

.nav-link.active {
  background: rgba(255, 255, 255, 0.2);
  color: white;
}

/* ===== BUTTONS ===== */

.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: 10px 24px;
  border-radius: var(--radius);
  font-size: 0.9rem;
  font-weight: 600;
  cursor: pointer;
  border: 2px solid transparent;
  transition: background 0.2s, border-color 0.2s, color 0.2s;
  text-decoration: none;
  line-height: 1.4;
}

/* Gold fill + ink text, not white text on a dark fill — matching the main
   site's .btn-gold (css/style.css) exactly, including its reason: gold does
   not clear 4.5:1 against white, so ink is the accessible pairing, and it is
   also what makes this button read as THE call to action rather than one of
   several blue ones. Text colour is --primary, not --heading: --primary is a
   FILL-family token that stays dark ink in both themes (see the THEME
   comment block at the top of this file), which is what a gold fill needs
   in dark mode too — --heading flips to light cream there and would leave
   near-invisible 2.64:1 text on the gold. 5.48:1 light / 5.42:1 dark
   default, 7.67:1 / 7.58:1 on hover. */
.btn-primary {
  background: var(--gold);
  color: var(--primary);
  border-color: var(--gold);
}

.btn-primary:hover {
  background: var(--gold-light);
  border-color: var(--gold-light);
  color: var(--primary);
}

.btn-secondary {
  background: transparent;
  color: var(--heading);
  border-color: var(--heading);
}

.btn-secondary:hover {
  background: var(--primary);
  color: white;
}

.btn-large {
  padding: 14px 36px;
  font-size: 1rem;
}

.btn-full {
  width: 100%;
}

/* FIX: was --success-light (#27ae60), which gave white text only 2.87:1 —
   below AA and below even the large-text threshold. This is the certificate
   download button, so it only ever appeared on a quiz PASS: a state the axe
   audit never drove the page into, which is why it went unnoticed. */
.btn-success {
  background: var(--success-fill);
  color: white;
  border-color: var(--success-fill);
}

.btn-success:hover {
  background: var(--success-fill-hover);
  border-color: var(--success-fill-hover);
  color: white;
}

/* ===== PAGE HEADER ===== */

.page-header {
  margin-bottom: 36px;
  max-width: 680px;
}

.page-header h1 {
  font-size: 1.9rem;
  color: var(--heading);
  margin-bottom: 10px;
}

.page-header p {
  color: var(--text-muted);
  font-size: 1rem;
}

/* ===== HOME: HERO ===== */

.hero {
  display: grid;
  grid-template-columns: 1fr auto auto;
  gap: 40px;
  align-items: center;
  padding: 48px 0;
  border-bottom: 1px solid var(--border);
  margin-bottom: 48px;
}

/* Sits in the middle column that opened up once .container went to 1320px
   (see the comment there) — without it, that column was just dead air
   between the hero text and the stat cards. Shown in its original colour
   rather than the site's navy palette, by request — a tinted version was
   mocked up alongside this one if that's revisited later. Purely
   decorative (empty alt), so it is skipped in the tab order rather than
   given one.

   The PNG has an opaque white background baked into its pixels (checked:
   no alpha/tRNS chunk), so it cannot blend into the page the way an
   .info-card does by inheriting var(--bg-white) — that variable turns dark
   in dark theme, but the image's own pixels can't follow it. Framing it in
   a literal, always-white card (like a photo in a white mount) turns what
   would otherwise be a glaring rectangle in dark mode into a deliberate
   card, matching the bordered/shadowed language every other panel on this
   page already uses. */
.hero-image-wrap {
  width: 300px;
  background: #ffffff;
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow);
  padding: 14px;
}

.hero-image-wrap img {
  display: block;
  width: 100%;
  height: auto;
  border-radius: calc(var(--radius-lg) - 6px);
}

/* Version statement beneath the hero subtitle. Bordered rather than merely
   indented so it reads as a distinct fact about the course, not more prose. */
.hero-version {
  border-left: 3px solid var(--accent);
  background: var(--bg-white);
  border-radius: 0 var(--radius) var(--radius) 0;
  padding: 12px 16px;
  margin-bottom: 26px;
  font-size: 0.9rem;
  line-height: 1.65;
  color: var(--text);
}

.hero-version strong {
  color: var(--heading);
}

.hero-tag {
  display: inline-block;
  background: var(--tint-info);
  color: var(--accent);
  font-size: 0.75rem;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  padding: 4px 12px;
  border-radius: 20px;
  margin-bottom: 16px;
}

.hero-title {
  font-size: 2.8rem;
  font-weight: 800;
  color: var(--heading);
  line-height: 1.1;
  margin-bottom: 16px;
  letter-spacing: -0.03em;
}

.hero-subtitle {
  font-size: 1.05rem;
  color: var(--text-muted);
  margin-bottom: 32px;
  max-width: 520px;
}

.hero-actions {
  display: flex;
  gap: 12px;
}

.hero-stats {
  display: flex;
  flex-direction: column;
  gap: 16px;
}

.stat-card {
  background: var(--bg-white);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 20px 28px;
  text-align: center;
  box-shadow: var(--shadow);
}

.stat-number {
  display: block;
  font-size: 2rem;
  font-weight: 800;
  color: var(--heading);
  line-height: 1;
}

.stat-label {
  display: block;
  font-size: 0.8rem;
  color: var(--text-muted);
  margin-top: 4px;
  text-transform: uppercase;
  letter-spacing: 0.05em;
}

/* ===== HOME: LIFECYCLE FLOW =====
   A small diagram in the same .stat-card shell as the three stat cards
   beside it — one more card in the stack, not a hero redesign. Six nodes,
   Clause 4 through Clause 9, one line with an arrow at the end: the
   standard is a single continuous lifecycle, not six unrelated clauses.
   Built from <line>/<circle>/<marker> only, no hand-authored path data
   beyond the small triangular arrowhead. */
.graphic-card {
  padding: 18px 20px 16px;
}

.flow-svg {
  display: block;
  margin: 0 auto;
}

.flow-caption {
  display: block;
  font-size: 0.78rem;
  color: var(--text-muted);
  margin-top: 10px;
  line-height: 1.5;
}

/* ===== HOME: INFO CARDS ===== */

.info-cards {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 24px;
}

.info-card {
  background: var(--bg-white);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  padding: 28px;
  box-shadow: var(--shadow);
}

.info-card h2 {
  font-size: 1rem;
  color: var(--heading);
  margin-bottom: 12px;
  font-weight: 700;
}

.info-card p {
  font-size: 0.95rem;
  color: var(--text-muted);
  line-height: 1.7;
}

/* ===== UPDATE BANNER ===== */

/* The outer element no longer paints anything — it only provides the vertical
   gap around the card. Previously it was a full-bleed pale band with a bottom
   border, which is the visual language of a page SECTION, so readers took it for
   part of the content. A bounded, shadowed card reads as something placed on top
   of the page instead. */
.update-banner {
  padding: 24px 0 8px;
}

.update-banner-card {
  max-width: 820px;
  margin: 0 auto;
  background: var(--tint-warn);
  border: 1px solid var(--tint-warn-border);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-md);
  overflow: hidden;
  /* keeps the bar's square corners inside the rounded card */
}

/* The solid header strip is the main "this is a notice" signal — the one thing
   the old design lacked. #8a6d1f is a darker shade of the amber used elsewhere
   in this block, chosen because white label text on it reaches 4.90:1 and so
   clears WCAG AA. The brighter #c9a227 would have needed dark text. */
.update-banner-bar {
  display: flex;
  align-items: center;
  gap: 10px;
  background: var(--notice-bar);
  padding: 9px 16px;
}

.update-banner-icon {
  font-size: 1rem;
  color: #ffffff;
  line-height: 1;
}

.update-banner-label {
  color: #ffffff;
  font-size: 0.78rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.09em;
  margin-right: auto;
  /* pushes the close button to the far edge */
}

.update-banner-content {
  padding: 18px 22px 20px;
}

.update-banner-heading {
  display: block;
  font-size: 1.02rem;
  font-weight: 700;
  color: var(--tint-warn-text);
  margin-bottom: 8px;
  line-height: 1.35;
}

.update-banner-intro {
  font-size: 0.88rem;
  color: var(--text);
  margin-bottom: 12px;
  line-height: 1.6;
}

/* Draws the eye to the version statement, which is the sentence readers most
   need to take away from this notice. */
.update-banner-intro strong {
  color: var(--tint-warn-text);
}

.update-banner-list {
  list-style: none;
  margin-bottom: 14px;
}

.update-banner-list li {
  font-size: 0.86rem;
  color: var(--text);
  padding: 5px 0 5px 18px;
  position: relative;
  line-height: 1.55;
}

.update-banner-list li::before {
  content: '\2192';
  position: absolute;
  left: 0;
  color: var(--notice-marker);
  font-size: 0.8rem;
}

.update-banner-link {
  font-size: 0.86rem;
  font-weight: 600;
  color: var(--accent);
  text-decoration: underline;
}

.update-banner-link:hover {
  text-decoration: none;
}

/* Attribution and review date. Quieter than the notice above it — it is a
   citation, not part of the warning — but "quieter" here means smaller and
   lighter in weight, NOT lower in contrast. --text-muted measures 5.04:1 against
   the card's cream background, so it still clears AA. Fading a source line until
   it is hard to read would defeat the point of having one. */
.update-banner-source {
  margin: 16px 0 0;
  padding-top: 12px;
  border-top: 1px solid var(--border);
  font-size: 0.78rem;
  line-height: 1.6;
  color: var(--text-muted);
}

.update-banner-source strong {
  color: var(--text);
}

/* A clause tag quoted from the standard, e.g. [Class A, B, C]. Monospace marks
   it as verbatim rather than prose. */
.legal-code {
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  font-size: 0.92em;
  padding: 1px 5px;
  background: var(--bg);
  border: 1px solid var(--border);
  border-radius: 3px;
}

/* Sits inside the coloured bar, so it is styled for a dark background. */
.update-banner-close {
  background: rgba(255, 255, 255, 0.15);
  border: 1px solid rgba(255, 255, 255, 0.55);
  cursor: pointer;
  color: #ffffff;
  font-size: 0.8rem;
  line-height: 1;
  padding: 5px 9px;
  border-radius: var(--radius);
  flex-shrink: 0;
  transition: background 0.2s;
}

.update-banner-close:hover {
  background: rgba(255, 255, 255, 0.3);
}

/* ===== LEARN: DELIVERABLES PANEL ===== */
/* The documented outputs required at the selected safety class. Same bounded-card
   language as the other notices on this page, so it reads as the site telling you
   something rather than as page furniture. */

.deliverables {
  background: var(--bg-white);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow);
  overflow: hidden;
  margin-bottom: 28px;
}

.deliverables-bar {
  display: flex;
  align-items: center;
  gap: 12px;
  flex-wrap: wrap;
  /* the two buttons drop below the heading on narrow screens */
  background: var(--primary);
  padding: 10px 18px;
}

.deliverables-icon {
  font-size: 1rem;
  line-height: 1;
}

.deliverables-heading {
  color: #ffffff;
  font-size: 0.85rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.07em;
  margin: 0;
}

.deliverables-count {
  color: rgba(255, 255, 255, 0.85);
  font-size: 0.84rem;
  margin-right: auto;
  /* pushes the buttons to the far edge */
}

.deliverables-btn {
  background: rgba(255, 255, 255, 0.15);
  border: 1px solid rgba(255, 255, 255, 0.5);
  color: #ffffff;
  border-radius: var(--radius);
  padding: 5px 12px;
  font-size: 0.84rem;
  font-weight: 600;
  font-family: inherit;
  cursor: pointer;
  transition: background 0.2s;
}

.deliverables-btn:hover {
  background: rgba(255, 255, 255, 0.3);
}

.deliverables-body {
  padding: 4px 22px 22px;
}

.dl-clause {
  font-size: 0.95rem;
  color: var(--heading);
  margin: 22px 0 8px;
  padding-bottom: 6px;
  border-bottom: 2px solid var(--border);
}

.dl-table {
  width: 100%;
  border-collapse: collapse;
  font-size: 0.9rem;
}

.dl-table thead th {
  text-align: left;
  font-size: 0.78rem;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  color: var(--text-muted);
  padding: 5px 10px;
  border-bottom: 1px solid var(--border);
}

.dl-table tbody th,
.dl-table tbody td {
  padding: 8px 10px;
  border-bottom: 1px solid var(--border);
  vertical-align: top;
  line-height: 1.55;
}

.dl-ref {
  text-align: left;
  font-weight: 600;
  color: var(--heading);
  white-space: nowrap;
  width: 4.5em;
}

.dl-req {
  color: var(--text);
  width: 30%;
}

.dl-out {
  color: var(--text);
}

/* A requirement the standard attaches no artefact to. Dimmed, but present —
   removing it would imply the requirement does not exist, when in fact these rows
   are where the manufacturer has to decide what evidence to keep. */
.dl-row-activity .dl-req {
  color: var(--text-muted);
}

.dl-none {
  color: var(--text-muted);
  font-style: italic;
}

/* Where the standard points outside itself — 4.1 to a quality management system,
   4.2 to ISO 14971. Set apart from the cell above it so it reads as a citation
   rather than as part of the requirement, but kept in the same cell so it travels
   with the row when the table wraps on a narrow screen. */
.dl-seealso {
  margin: 8px 0 0;
  padding: 8px 10px;
  background: var(--bg);
  border-left: 3px solid var(--accent);
  border-radius: 0 4px 4px 0;
  font-size: 0.8rem;
  font-style: normal;
  line-height: 1.6;
  color: var(--text);
}

.dl-seealso-label {
  font-weight: 600;
  /* 4.89:1 against --bg, so it clears AA for normal-size text. */
  color: var(--accent);
}

.dl-footnote {
  margin-top: 22px;
  padding: 14px 16px;
  background: var(--bg);
  border-left: 4px solid var(--accent);
  border-radius: 0 var(--radius) var(--radius) 0;
  font-size: 0.84rem;
  line-height: 1.65;
  color: var(--text);
}

/* ===== PRINT IS ALWAYS LIGHT =====
   Paper has no dark mode. Printing the dark theme would either waste an enormous
   amount of toner on a black background, or — more likely, since most browsers
   drop background colours when printing — emit light grey text on white paper and
   come out nearly blank. Neither is acceptable for a certificate or a deliverables
   checklist someone puts in a design history file.

   So the print stylesheet resets the palette to the light values regardless of
   the theme on screen. This works because the rest of the file only ever refers
   to variables: one override here fixes every rule at once, with no need to
   revisit individual components. `[data-theme="dark"]` is included in the
   selector because the attribute carries higher specificity than :root alone and
   would otherwise win. */
@media print {

  :root,
  :root:not([data-theme]),
  [data-theme="dark"] {
    --primary: #1a1a2e;
    --primary-light: #3a3a5c;
    --accent: #2d4a7a;
    --accent-light: #3d5f99;
    --success: #1e8449;
    --success-light: #27ae60;
    --danger: #c0392b;
    --warning: #d35400;
    --bg: #ffffff;
    --bg-white: #ffffff;
    --text: #000000;
    --text-muted: #333333;
    --border: #999999;
    --shadow: none;
    --shadow-md: none;
    --gold: #b08c5a;
    --gold-light: #c9a97a;
    --heading: #1a1a2e;
    --heading-sub: #3a3a5c;
    --accent-fill: #2d4a7a;
    --success-fill: #1a7a4a;
    --success-fill-hover: #156b3c;
    --success-text: #18703f;
    --danger-text: #c0392b;
    --warning-text: #b34700;
    --tint-info: #ffffff;
    --tint-info-text: #1a5fa0;
    --tint-ok: #ffffff;
    --tint-ok-text: #1a7a4a;
    --tint-ok-border: #999999;
    --tint-warn: #ffffff;
    --tint-warn-text: #6b4e00;
    --tint-warn-border: #999999;
    --tint-bad: #ffffff;
    --tint-bad-soft: #ffffff;
    --tint-bad-text: #a93226;
    --tint-orange: #ffffff;
    --notice-bar: #8a6d1f;
    --notice-marker: #8a6d1f;
  }

  /* The toggle is meaningless on paper. */
  .theme-toggle {
    display: none !important;
  }

  /* The promo strip is decorative advertising, not course content — leave
     it out of anything printed to paper (a certificate, a deliverables
     checklist for a design history file). */
  .promo-strip {
    display: none !important;
  }
}

/* Printing the panel: an obvious thing to want, since the list is exactly what
   would go into a gap analysis or a design history file. Everything else on the
   page is dropped, and the table is allowed to break across pages without
   splitting a row down the middle. */
@media print {

  /* On the Learn page, printing should give the deliverables list and nothing
     else — that is the only part anyone would put on paper. The surrounding
     furniture is hidden explicitly rather than by a visibility trick, because
     display:none actually reclaims the space instead of leaving it blank. */
  .site-header,
  .site-footer,
  .skip-link,
  .update-banner,
  .page-header,
  .learn-controls,
  .filter-notice,
  .data-status,
  .data-error,
  .phases-grid {
    display: none !important;
  }

  .deliverables {
    border: none;
    box-shadow: none;
  }

  .deliverables-bar {
    background: none;
    padding: 0 0 12px;
  }

  .deliverables-heading,
  .deliverables-count {
    color: #000;
  }

  .deliverables-btn {
    display: none;
    /* buttons are meaningless on paper */
  }

  .dl-table {
    page-break-inside: auto;
  }

  .dl-table tr {
    page-break-inside: avoid;
  }

  .dl-clause {
    page-break-after: avoid;
  }

  /* The cross-reference has to survive printing: on paper the 4.1 and 4.2 rows
     are the two a reader is most likely to query, and the pointer is the answer.
     Border and background are kept as a thin rule only — filled panels waste
     toner and print as grey mud. */
  .dl-seealso {
    background: none;
    border-left: 2px solid #000;
    padding: 4px 0 4px 8px;
    color: #000;
  }

  .dl-seealso-label {
    color: #000;
  }
}

/* ===== LEARN: SUB-CLAUSE APPLICABILITY TABLE ===== */
/* Inside each expanded topic card: which safety classes each individual
   requirement applies to. A real table, because this is real tabular data. */

/* An expanded card spans the full grid width.
   The grid packs cards at a minimum of 320px, which is fine for a collapsed
   summary but far too narrow for a five-column requirements table — the text
   wrapped to one or two words per line. Letting the open card take the whole row
   gives the table room, and has the side benefit of making the expanded card
   obviously the thing being read. */
.phase-card.expanded {
  grid-column: 1 / -1;
}

.phase-subclauses {
  margin-top: 18px;
  padding-top: 16px;
  border-top: 1px solid var(--border);
  overflow-x: auto;
  /* very narrow screens scroll the table, not the page */
}

.sc-table {
  width: 100%;
  border-collapse: collapse;
  font-size: 0.88rem;
}

/* The caption doubles as the table's accessible name, so it is kept visible
   rather than hidden — it carries the provenance of the data, which a reader
   checking against their own copy of the standard will want. */
.sc-table caption {
  caption-side: top;
  text-align: left;
  font-size: 0.82rem;
  color: var(--text-muted);
  line-height: 1.5;
  padding-bottom: 10px;
}

.sc-table thead th {
  text-align: left;
  font-size: 0.78rem;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  color: var(--heading);
  border-bottom: 2px solid var(--border);
  padding: 6px 8px;
}

/* The three class columns are narrow and centred; the first two are text. */
.sc-table thead th:nth-child(n + 3) {
  text-align: center;
  width: 2.6em;
}

.sc-table tbody th,
.sc-table tbody td {
  padding: 6px 8px;
  border-bottom: 1px solid var(--border);
  vertical-align: top;
}

.sc-ref {
  text-align: left;
  font-weight: 600;
  color: var(--heading);
  white-space: nowrap;
}

.sc-title {
  color: var(--text);
  line-height: 1.5;
}

.sc-note {
  display: block;
  color: var(--text-muted);
  font-size: 0.9em;
  font-style: italic;
  margin-top: 2px;
}

.sc-cell {
  text-align: center;
  font-weight: 700;
}

/* Applies. Green tick — but the sr-only text in the markup is what actually
   conveys the meaning, so the colour is reinforcement, never the sole signal. */
.sc-yes {
  color: var(--success-text);
}

/* Does not apply. A muted middot rather than a cross: a cross reads as "failed",
   whereas this requirement is simply not assigned at that class. */
.sc-no {
  color: var(--border);
}

/* Removed by Amendment 1. */
.sc-void {
  color: var(--text-muted);
}

.sc-row-void .sc-ref,
.sc-row-void .sc-title {
  color: var(--text-muted);
  text-decoration: line-through;
}

/* Dimmed while a class filter is active and this requirement is not assigned at
   that class. Opacity only — the row stays present and readable, because hiding
   it would recreate the original problem of invisible omissions. */
.sc-row-filtered {
  opacity: 0.4;
}

/* Badge on a card whose clause applies only in part at the active class. */
.partial-badge {
  align-self: flex-start;
  flex-shrink: 0;
  background: var(--tint-warn);
  color: var(--tint-warn-text);
  border: 1px solid var(--tint-warn-border);
  border-radius: 20px;
  padding: 3px 10px;
  font-size: 0.68rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  white-space: nowrap;
  margin-right: 8px;
}

.phase-card.partial-applicability {
  border-left: 3px solid var(--notice-marker);
}

/* ===== LEARN: SAFETY CLASS FILTER NOTICE ===== */
/* Appears directly beneath the filter buttons when a class filter is active.
   Deliberately styled like the Edition 2 notice — bounded card, solid header
   strip — so both read as "the site is telling you something" in the same
   visual language. The strip is navy rather than amber because this is
   informational rather than a warning; the genuinely cautionary part is the
   amber block nested inside it. */

.filter-notice {
  background: var(--bg-white);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow);
  overflow: hidden;
  margin-bottom: 28px;
}

.filter-notice-bar {
  display: flex;
  align-items: center;
  gap: 10px;
  background: var(--primary);
  padding: 9px 18px;
}

.filter-notice-icon {
  color: #ffffff;
  font-size: 0.95rem;
  line-height: 1;
}

.filter-notice-label {
  color: #ffffff;
  font-size: 0.78rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.09em;
}

.filter-notice-body {
  padding: 18px 22px 20px;
}

.filter-notice-count {
  font-size: 0.9rem;
  line-height: 1.6;
  color: var(--text);
  margin-bottom: 12px;
}

/* Sub-heading above each group in the notice: applies-in-part vs does-not-apply. */
.filter-notice-subheading {
  font-size: 0.86rem;
  font-weight: 600;
  color: var(--heading);
  margin: 14px 0 6px;
}

.filter-notice-list {
  list-style: none;
  margin: 0 0 4px;
}

.filter-notice-list li {
  font-size: 0.88rem;
  color: var(--text);
  padding: 5px 0 5px 18px;
  position: relative;
  line-height: 1.55;
}

.filter-notice-list li::before {
  content: '\2013';
  /* en dash marker; the list is a set, not a sequence */
  position: absolute;
  left: 0;
  color: var(--accent);
}

.filter-notice-applies {
  color: var(--text-muted);
  font-size: 0.85em;
}

/* The regulatory caution. Nested inside the informational panel but visually
   separated, because it is the part that carries professional consequence: the
   filter is a convenience, whereas an out-of-date safety classification is a
   compliance problem. */
.filter-notice-caution {
  margin-top: 16px;
  padding: 14px 16px;
  background: var(--tint-warn);
  border-left: 4px solid var(--notice-bar);
  border-radius: 0 var(--radius) var(--radius) 0;
}

.filter-notice-caution-heading {
  display: block;
  font-size: 0.9rem;
  color: var(--tint-warn-text);
  margin-bottom: 8px;
}

.filter-notice-caution p {
  font-size: 0.86rem;
  line-height: 1.65;
  color: var(--text);
  margin-bottom: 10px;
}

.filter-notice-caution p:last-child {
  margin-bottom: 0;
}

/* ===== LEARN: CONTROLS ===== */

.learn-controls {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 24px;
  margin-bottom: 32px;
  padding: 20px 24px;
  background: var(--bg-white);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow);
  flex-wrap: wrap;
}

.progress-tracker {
  flex: 1;
  min-width: 200px;
}

.progress-label {
  font-size: 0.85rem;
  color: var(--text-muted);
  display: block;
  margin-bottom: 8px;
}

.progress-bar-container {
  background: var(--bg);
  border-radius: 20px;
  height: 8px;
  overflow: hidden;
  border: 1px solid var(--border);
}

.progress-bar {
  height: 100%;
  background: linear-gradient(90deg, var(--accent), var(--success-light));
  border-radius: 20px;
  transition: width 0.4s ease;
  width: 0%;
}

.filter-group {
  display: flex;
  align-items: center;
  gap: 8px;
  flex-wrap: wrap;
}

.filter-label {
  font-size: 0.85rem;
  color: var(--text-muted);
  font-weight: 500;
}

.filter-btn {
  background: var(--bg);
  border: 1px solid var(--border);
  color: var(--text-muted);
  padding: 6px 16px;
  border-radius: 20px;
  cursor: pointer;
  font-size: 0.82rem;
  font-weight: 600;
  transition: background 0.2s, color 0.2s, border-color 0.2s;
}

.filter-btn:hover,
.filter-btn.active {
  background: var(--primary);
  border-color: var(--primary);
  color: white;
}

/* ===== LEARN: LEVEL TOGGLE ===== */
/* Segmented control: both buttons sit inside a shared pill container.
   The active choice gets a filled background — immediately readable as
   "one is selected" rather than two separate independent buttons. */

.level-group {
  display: flex;
  align-items: center;
  gap: 8px;
}

/* The outer pill — provides the shared border and background track. */
.level-toggle {
  display: flex;
  background: var(--bg);
  border: 2px solid var(--primary);
  border-radius: 24px;
  padding: 3px;
  gap: 2px;
}

.level-btn {
  background: transparent;
  border: none;
  color: var(--heading);
  padding: 5px 16px;
  border-radius: 20px;
  cursor: pointer;
  font-size: 0.82rem;
  font-weight: 600;
  transition: background 0.2s, color 0.2s;
  white-space: nowrap;
}

/* Active state: filled pill using the same primary navy as the filter buttons,
   so both controls feel like part of the same family. */
.level-btn.active {
  background: var(--primary);
  color: white;
}

.level-btn:not(.active):hover {
  background: rgba(26, 58, 92, 0.1);
}

/* ===== LEARN: PHASE CARDS ===== */

.phases-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(320px, 1fr));
  gap: 20px;
}

/* display:flex + height:100% so the footer (Read More / Mark as Studied)
   can be pinned to the bottom via margin-top: auto below. Without this, a
   card with a short summary is a shorter block of content but still gets
   stretched to match the tallest card in its grid row (the grid's default
   align-items: stretch) — the footer just sat wherever the content ended,
   floating mid-card with blank space beneath it. Pinning it keeps every
   footer in the row on the same line regardless of how much summary text
   is above it. */
.phase-card {
  display: flex;
  flex-direction: column;
  height: 100%;
  background: var(--bg-white);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow);
  overflow: hidden;
  transition: box-shadow 0.2s, transform 0.2s;
}

.phase-card.hidden {
  display: none;
}

.phase-card:hover {
  box-shadow: var(--shadow-md);
  transform: translateY(-2px);
}

.phase-card.studied {
  border-left: 4px solid var(--success-light);
}

.phase-header {
  display: flex;
  align-items: flex-start;
  gap: 16px;
  padding: 20px 20px 16px;
  cursor: pointer;
  user-select: none;
}

.phase-header:focus-visible {
  outline: 3px solid var(--accent);
  outline-offset: -3px;
  border-radius: var(--radius-lg) var(--radius-lg) 0 0;
}

.phase-icon {
  flex-shrink: 0;
  line-height: 1;
}

/* Book icon: one consistent glyph for every clause (replacing the old
   per-topic emoji, which varied in weight/tone across browsers and read as
   decoration rather than meaning). Two paths are stacked in the same SVG —
   only one is ever visible, swapped by the 'opened' class on .phase-card:
     - default: outline path, blue stroke over a page coloured to match the
       card background, so it reads as a closed, unread book.
     - .opened: solid path, filled in --primary — the same navy the
       Mark as Studied button fills with once studied (see
       .phase-card.studied .mark-studied-btn below), so the "you've been
       here" book and the "you've certified this" button read as one
       colour rather than two different blues (see togglePhaseCard/
       markStudied in learn.js). */
.book-icon-outline {
  fill: var(--bg-white);
  stroke: var(--accent-fill);
  stroke-width: 0.6;
  stroke-linejoin: round;
}

.book-icon-filled {
  fill: var(--primary);
  display: none;
}

.phase-card.opened .book-icon-outline {
  display: none;
}

.phase-card.opened .book-icon-filled {
  display: block;
}

.phase-meta {
  flex: 1;
}

.phase-clause {
  font-size: 0.78rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: var(--accent);
  display: block;
  margin-bottom: 2px;
}

.phase-title {
  font-size: 1.02rem;
  font-weight: 700;
  color: var(--heading);
  margin-bottom: 6px;
}

.phase-summary {
  font-size: 0.9rem;
  color: var(--text-muted);
  line-height: 1.5;
}

.phase-classes {
  display: flex;
  gap: 4px;
  margin-top: 8px;
  flex-wrap: wrap;
}

.class-badge {
  font-size: 0.74rem;
  font-weight: 700;
  padding: 2px 8px;
  border-radius: 10px;
}

.class-badge.A {
  background: var(--tint-ok);
  color: var(--tint-ok-text);
}

.class-badge.B {
  background: var(--tint-info);
  color: var(--tint-info-text);
}

.class-badge.C {
  background: var(--tint-bad-soft);
  color: var(--tint-bad-text);
}

.phase-chevron {
  flex-shrink: 0;
  font-size: 0.65rem;
  color: var(--text-muted);
  transition: transform 0.2s;
  padding-top: 6px;
}

.phase-card.expanded .phase-chevron {
  transform: rotate(180deg);
}

.phase-details {
  display: none;
  padding: 4px 20px 8px;
}

.phase-card.expanded .phase-details {
  display: block;
}

.phase-details ul {
  list-style: none;
  margin-bottom: 16px;
  border-top: 1px solid var(--bg);
  padding-top: 8px;
}

.phase-details ul li {
  font-size: 0.9rem;
  color: var(--text);
  padding: 7px 0 7px 22px;
  border-bottom: 1px solid var(--bg);
  position: relative;
}

.phase-details ul li::before {
  content: '\2192';
  position: absolute;
  left: 0;
  color: var(--accent);
  font-size: 0.8rem;
}

.phase-footer {
  display: flex;
  flex-wrap: wrap;
  /* lets .field-error below drop to its own full-width row */
  align-items: center;
  justify-content: flex-end;
  padding: 12px 20px;
  border-top: 1px solid var(--bg);
  gap: 4px 12px;
  margin-top: auto;
  /* pins the footer to the card's bottom edge — see the
                        .phase-card comment above */
}

/* The "preview the document first" message. flex-basis: 100% forces it onto
   its own row above the badge/button regardless of source order, matching
   how a form field's error sits on its own line under the field it belongs
   to rather than squeezed into the same row. */
.phase-footer .field-error {
  flex: 1 0 100%;
  text-align: right;
  margin-top: 0;
}

.read-more-btn,
.mark-studied-btn {
  font-size: 0.78rem;
  padding: 6px 14px;
}

/* Only one of Read More / Mark as Studied is ever shown: Read More before
   the card has been opened (see the 'opened' class added by
   togglePhaseCard/markStudied in learn.js), Mark as Studied once it has.
   Asking the reader to certify unread content was the problem — this keeps
   the certifying button off the card until there is something to certify. */
.mark-studied-btn {
  display: none;
}

.phase-card.opened .read-more-btn {
  display: none;
}

.phase-card.opened .mark-studied-btn {
  display: inline-block;
}

/* Filled navy once studied — the same fill .btn-secondary:hover already
   uses (line 624), just made permanent instead of hover-only, and matching
   .book-icon-filled above so the two "you've done this" signals on the card
   are one colour. Still overrides opacity itself: the generic .btn:disabled
   rule (opacity: 0.6, meant for a button mid-async-request elsewhere on the
   site — Send Message, Begin Assessment) would otherwise fade this on top,
   since markStudied() also sets the disabled attribute. A faded fill read as
   "unavailable" rather than "successfully completed" — the wrong signal for
   a state the reader should feel good about reaching. cursor:default (not
   not-allowed) says the same thing: nothing left to click, nothing wrong. */
.phase-card.studied .mark-studied-btn {
  background: var(--primary);
  color: white;
  border-color: var(--primary);
  cursor: default;
  opacity: 1;
}

/* ===== QUIZ: LEVEL NOTICE ===== */

.quiz-level-notice {
  background: var(--bg);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 12px 16px;
  font-size: 0.85rem;
  color: var(--text-muted);
  margin: 0 auto 24px;
  max-width: 360px;
  text-align: left;
  line-height: 1.5;
}

.level-notice-badge {
  display: inline-block;
  font-size: 0.75rem;
  font-weight: 700;
  padding: 2px 10px;
  border-radius: 12px;
  margin-right: 6px;
  vertical-align: middle;
}

.level-notice-intro {
  background: var(--tint-info);
  color: var(--tint-info-text);
}

.level-notice-advanced {
  background: var(--tint-ok);
  color: var(--success-text);
}

/* ===== QUIZ: NAME FIELD ===== */

/* Constrains the name input to match the quiz-rules width and left-aligns
   the label, overriding .quiz-start-card's centred text alignment. */
.quiz-name-group {
  max-width: 360px;
  margin: 0 auto 24px;
  text-align: left;
}

/* ===== QUIZ: LAYOUT ===== */

/* The 740px reading/card width used to live here, centring the whole
   screen — including the heading, which every other page shows full-width
   and left-aligned via .page-header. That width now lives on each screen's
   own card instead (.quiz-start-card, .quiz-header, .question-card,
   .question-feedback, .results-card), so .page-header can sit above them
   at the same left-aligned position it has on every other page. */
.quiz-screen {
  display: none;
}

.quiz-screen.active {
  display: block;
}

/* ===== QUIZ: START SCREEN ===== */

/* Info panel (checklist) beside the action card, the same idiom
   .contact-layout already uses for Contact's info-beside-form split — a
   narrower 1fr column for the descriptive content, a wider 2fr column for
   the actual task. Without this, the checklist and the (deliberately not
   full-width) card both sat in a single centred column, leaving the right
   two-thirds of a wide screen empty. Collapses to one column at the same
   768px breakpoint .contact-layout does (see the shared media block). */
.quiz-layout {
  display: grid;
  grid-template-columns: 1fr 2fr;
  gap: 40px;
  align-items: start;
}

/* Was .quiz-intro, and held the <h1> too — renamed now that the heading
   has moved out to .page-header above, leaving this as what it actually
   is: the card holding the level notice, name field and Begin button. No
   longer centred with its own max-width — it fills its .quiz-layout column
   instead, the same way .contact-form fills its column in .contact-layout. */
.quiz-start-card {
  background: var(--bg-white);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  padding: 40px 48px;
  text-align: center;
  box-shadow: var(--shadow);
}

/* Fills the left .quiz-layout column. Needs its own border now that it
   isn't sitting inside the white card — var(--bg) alone is the page
   background and would otherwise be invisible (the card used to give it
   contrast for free). */
.quiz-rules {
  list-style: none;
  background: var(--bg);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 20px 28px;
  text-align: left;
}

.quiz-rules li {
  font-size: 0.9rem;
  color: var(--text);
  padding: 6px 0 6px 24px;
  border-bottom: 1px solid var(--border);
  position: relative;
}

.quiz-rules li:last-child {
  border-bottom: none;
}

.quiz-rules li::before {
  content: '\2713';
  position: absolute;
  left: 0;
  /* A decorative list marker rather than text, so the 3:1 non-text threshold
     applies — but --success-light managed only 2.87:1 on a white card, under even
     that. --success-text is the role with a checked ratio in both themes. */
  color: var(--success-text);
  font-weight: 700;
}

/* ===== QUIZ: ACTIVE SCREEN ===== */

.quiz-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 20px;
  max-width: 740px;
  margin: 0 auto 24px;
  background: var(--bg-white);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 16px 20px;
  box-shadow: var(--shadow);
}

.quiz-progress-info {
  flex: 1;
}

#question-counter {
  display: block;
  font-size: 0.8rem;
  color: var(--text-muted);
  font-weight: 600;
  margin-bottom: 8px;
  text-transform: uppercase;
  letter-spacing: 0.05em;
}

.quiz-progress-bar-container {
  background: var(--bg);
  border-radius: 20px;
  height: 6px;
  overflow: hidden;
}

.quiz-progress-bar {
  height: 100%;
  background: var(--accent);
  border-radius: 20px;
  transition: width 0.3s ease;
  width: 0%;
}

.quiz-timer {
  display: flex;
  align-items: center;
  gap: 6px;
  font-size: 1.05rem;
  font-weight: 700;
  color: var(--heading);
  background: var(--bg);
  padding: 8px 16px;
  border-radius: var(--radius);
  min-width: 76px;
  justify-content: center;
  transition: background 0.3s, color 0.3s;
}

.quiz-timer.warning {
  color: var(--warning-text);
  background: var(--tint-orange);
}

.quiz-timer.danger {
  color: var(--danger-text);
  background: var(--tint-bad);
}

.question-card {
  max-width: 740px;
  margin: 0 auto 20px;
  background: var(--bg-white);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  padding: 36px;
  box-shadow: var(--shadow);
}

.question-text {
  font-size: 1.05rem;
  color: var(--heading);
  font-weight: 600;
  margin-bottom: 28px;
  line-height: 1.5;
}

.options-grid {
  display: grid;
  gap: 12px;
}

.option-btn {
  text-align: left;
  background: var(--bg);
  border: 2px solid var(--border);
  border-radius: var(--radius);
  padding: 14px 20px;
  cursor: pointer;
  font-size: 0.9rem;
  color: var(--text);
  font-family: inherit;
  transition: border-color 0.15s, background 0.15s;
  line-height: 1.4;
  width: 100%;
}

.option-btn:hover:not(:disabled) {
  border-color: var(--accent);
  background: var(--tint-info);
}

.option-btn.correct {
  background: var(--tint-ok);
  border-color: var(--success-light);
  color: var(--success-text);
  font-weight: 600;
}

.option-btn.incorrect {
  background: var(--tint-bad);
  border-color: var(--danger);
  color: var(--danger-text);
  font-weight: 600;
}

.option-btn:disabled {
  cursor: default;
}

.question-feedback {
  max-width: 740px;
  margin: 0 auto;
  background: var(--bg-white);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  padding: 24px;
  box-shadow: var(--shadow);
  display: none;
  align-items: flex-start;
  justify-content: space-between;
  gap: 20px;
}

.question-feedback.visible {
  display: flex;
}

.feedback-text {
  font-size: 0.9rem;
  color: var(--text);
  line-height: 1.6;
  flex: 1;
}

/* ===== QUIZ: RESULTS SCREEN ===== */

.results-card {
  max-width: 740px;
  margin: 0 auto;
  background: var(--bg-white);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  padding: 56px 48px;
  text-align: center;
  box-shadow: var(--shadow);
}

.results-icon {
  font-size: 4rem;
  margin-bottom: 20px;
}

/* The results heading is an <h1> because the results screen replaces the start
   screen entirely — it is the main heading of that view, not a subsection of
   one. Each of the three quiz screens carries its own h1 for that reason. */
.results-card h1 {
  font-size: 1.8rem;
  color: var(--heading);
  margin-bottom: 24px;
}

.results-score {
  display: flex;
  align-items: baseline;
  justify-content: center;
  gap: 8px;
  margin-bottom: 20px;
}

.score-number {
  font-size: 4rem;
  font-weight: 900;
  color: var(--heading);
  line-height: 1;
}

.score-label {
  font-size: 1rem;
  color: var(--text-muted);
}

#results-message {
  color: var(--text-muted);
  font-size: 1rem;
  margin-bottom: 32px;
  max-width: 480px;
  margin-left: auto;
  margin-right: auto;
}

.results-breakdown {
  background: var(--bg);
  border-radius: var(--radius);
  padding: 20px;
  margin-bottom: 32px;
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 16px;
}

.breakdown-item {
  text-align: center;
}

.breakdown-value {
  display: block;
  font-size: 1.6rem;
  font-weight: 800;
  color: var(--heading);
}

.breakdown-label {
  font-size: 0.8rem;
  color: var(--text-muted);
  text-transform: uppercase;
  letter-spacing: 0.05em;
}

.results-actions {
  display: flex;
  gap: 12px;
  justify-content: center;
}

/* ===== CONTACT ===== */

.contact-layout {
  display: grid;
  grid-template-columns: 1fr 2fr;
  gap: 40px;
  align-items: start;
}

.contact-info h2 {
  font-size: 1.1rem;
  color: var(--heading);
  margin-bottom: 16px;
}

.contact-reasons {
  list-style: none;
  margin-bottom: 24px;
}

.contact-reasons li {
  font-size: 0.87rem;
  color: var(--text-muted);
  padding: 8px 0 8px 20px;
  border-bottom: 1px solid var(--border);
  position: relative;
}

.contact-reasons li::before {
  content: '\2022';
  position: absolute;
  left: 0;
  color: var(--accent);
}

.contact-detail {
  font-size: 0.82rem;
  color: var(--text-muted);
  background: var(--bg);
  padding: 12px 16px;
  border-radius: var(--radius);
  border: 1px solid var(--border);
}

/* ===== CONTACT FORM ===== */

.contact-form {
  background: var(--bg-white);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  padding: 36px;
  box-shadow: var(--shadow);
}

.form-group {
  margin-bottom: 20px;
}

.form-group label {
  display: block;
  font-size: 0.9rem;
  font-weight: 600;
  color: var(--text);
  margin-bottom: 6px;
}

.required {
  color: var(--danger-text);
}

/* font-size MUST stay at 1rem (16px) or above.
   iOS Safari automatically zooms the whole page when the user focuses an input
   whose font-size is below 16px, which yanks the layout around on every tap.
   This was 0.9rem (14.4px) and did exactly that. Nothing else forces the value
   — it is purely to stop that behaviour. */
.form-group input,
.form-group select,
.form-group textarea {
  width: 100%;
  padding: 10px 14px;
  border: 2px solid var(--border);
  border-radius: var(--radius);
  font-size: 1rem;
  /* 16px — do not reduce; see note above */
  color: var(--text);
  background: var(--bg);
  transition: border-color 0.2s, background 0.2s;
  font-family: inherit;
}

/* FIX: placeholder text was never given an explicit colour, so every browser
   fell back to its own UA default (Chrome/Edge: #757575) — a value nobody
   here ever chose and nobody had numerically checked. Measured against the
   field's own --bg background it came out to 4.26:1 in light mode and
   3.91:1 in dark mode, both under the 4.5:1 AA minimum for normal text —
   found by measuring every text/background pair on the Contact page by
   hand, the same way the dark-theme audit found the earlier --success-light
   and --tint-ok icon failures, because axe-core and eyeballing both missed
   it (a placeholder is easy to skim past as "just a hint" rather than text
   that has to clear the same bar as everything else). --text-muted reaches
   4.91:1 on this background in light mode and comfortably more in dark, and
   reusing it keeps the same "secondary text" role wired to one variable
   instead of inventing a new one-off colour. opacity: 1 overrides Firefox's
   own default placeholder opacity, which would otherwise fade an explicitly
   set colour back down again. */
.form-group input::placeholder,
.form-group textarea::placeholder {
  color: var(--text-muted);
  opacity: 1;
}

.form-group input:focus,
.form-group select:focus,
.form-group textarea:focus {
  outline: 3px solid var(--accent-light);
  outline-offset: 1px;
  border-color: var(--accent);
  background: var(--bg-white);
}

.form-group input.error,
.form-group textarea.error {
  border-color: var(--danger);
}

.field-error {
  display: block;
  /* Raised from 0.78rem (12.5px). Error messages tell the user how to fix a
     problem, so they were the worst text on the page to have set smallest. */
  font-size: 0.85rem;
  color: var(--danger-text);
  margin-top: 4px;
  min-height: 1em;
}

.form-success {
  background: var(--bg-white);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  padding: 56px 36px;
  text-align: center;
  box-shadow: var(--shadow);
}

/* FIX: the tick was --success-light (#27ae60) on the pale green tint — 2.61:1,
   which fails AA even at this size. It confirms the message was actually sent,
   so it is meaningful rather than decorative. It went unnoticed because the axe
   audit covered the contact form's ERROR state but never its SUCCESS state. */
.success-icon {
  font-size: 2rem;
  color: var(--tint-ok-text);
  background: var(--tint-ok);
  width: 72px;
  height: 72px;
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
  margin: 0 auto 24px;
  font-weight: 700;
}

.form-success h2 {
  font-size: 1.5rem;
  color: var(--heading);
  margin-bottom: 12px;
}

.form-success p {
  color: var(--text-muted);
  margin-bottom: 28px;
}

/* ===== ASYNC STATES: LOADING AND ERROR ===== */
/* Styles for the three states every asynchronous operation has: loading,
   success and failure. The success state is just the normal page content;
   these rules cover the other two. */

/* LOADING — a centred spinner with a label, shown while data is fetched. */
.data-status {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 16px;
  padding: 72px 24px;
  text-align: center;
}

.data-status-text {
  color: var(--text-muted);
  font-size: 0.92rem;
  margin: 0;
}

/* The spinner is a circle with a transparent border on three sides and a
   coloured border on the fourth, rotated continuously. Because only the
   top edge is coloured, the rotation reads as a moving arc. */
.spinner {
  width: 36px;
  height: 36px;
  border: 3px solid var(--border);
  border-top-color: var(--accent);
  border-radius: 50%;
  animation: spinner-rotate 0.8s linear infinite;
}

@keyframes spinner-rotate {
  to {
    transform: rotate(360deg);
  }
}

/* Respect the operating system "reduce motion" setting. Some people get
   genuinely unwell from spinning and moving interfaces, so instead of
   rotating we fade gently — still clearly "in progress", but calm. */
@media (prefers-reduced-motion: reduce) {
  .spinner {
    animation: spinner-pulse 1.4s ease-in-out infinite;
    border-top-color: var(--border);
    background: var(--accent-light);
  }

  @keyframes spinner-pulse {

    0%,
    100% {
      opacity: 0.3;
    }

    50% {
      opacity: 1;
    }
  }
}

/* FAILURE — a bordered panel explaining what went wrong, with a retry button.
   Styled in the danger colour but kept calm rather than alarming: the user
   has done nothing wrong, the network has. */
.data-error {
  max-width: 560px;
  margin: 48px auto;
  padding: 28px 32px;
  background: var(--bg-white);
  border: 1px solid var(--border);
  border-left: 4px solid var(--danger);
  border-radius: var(--radius);
  box-shadow: var(--shadow);
  text-align: left;
}

.data-error-heading {
  font-weight: 600;
  color: var(--danger-text);
  font-size: 1rem;
  margin: 0 0 8px;
}

.data-error-message {
  color: var(--text-muted);
  font-size: 0.88rem;
  line-height: 1.6;
  margin: 0 0 20px;
}

/* Inline form error, shown above the Send button on the contact page. */
.form-error {
  display: block;
  padding: 12px 16px;
  margin-bottom: 16px;
  background: var(--tint-bad-soft);
  border: 1px solid var(--danger);
  border-radius: var(--radius);
  font-size: 0.85rem;
  line-height: 1.5;
}

.form-error-heading {
  display: block;
  color: var(--danger-text);
  margin-bottom: 4px;
}

.form-error-message {
  color: var(--text);
}

/* BUSY / DISABLED BUTTONS
   Applies while an async operation is in flight. The button is disabled to
   block double-submission, so it must also LOOK unavailable — otherwise the
   user keeps clicking a control that silently ignores them.
   `cursor: not-allowed` and reduced opacity are the conventional signals. */
.btn:disabled {
  opacity: 0.6;
  cursor: not-allowed;
}

/* aria-busy="true" is set by the JavaScript alongside `disabled`. Hooking a
   style onto the ARIA attribute rather than an extra class keeps the
   accessibility information and the visual state in a single place — they
   cannot drift apart if one is updated and the other forgotten. */
.btn[aria-busy="true"] {
  opacity: 0.75;
  cursor: progress;
}

/* ===== LEGAL / POLICY PAGE ===== */
/* Long-form prose, which the rest of the site does not have. The measure is
   capped at 760px because lines much longer than about 90 characters are
   measurably harder to read — the eye loses its place returning to the left. */

.legal-page {
  max-width: 760px;
  margin: 0 auto 64px;
  background: var(--bg-white);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  padding: 44px 48px;
  box-shadow: var(--shadow);
}

.legal-updated {
  font-size: 0.8rem;
  color: var(--text-muted);
  margin-bottom: 28px;
  padding-bottom: 20px;
  border-bottom: 1px solid var(--border);
}

/* The plain-language summary, set apart so a reader in a hurry finds it first. */
.legal-summary {
  background: var(--bg);
  border-left: 4px solid var(--accent);
  border-radius: var(--radius);
  padding: 20px 24px;
  margin-bottom: 36px;
}

.legal-summary h2 {
  margin-top: 0;
  font-size: 1.05rem;
}

.legal-summary ul {
  margin-bottom: 0;
}

.legal-page h2 {
  font-size: 1.25rem;
  color: var(--heading);
  margin: 40px 0 14px;
}

/* --heading-sub, not --primary-light. --primary-light is a FILL (it backs the
   primary button on hover); as text on a dark card it measured 2.02:1. */
.legal-page h3 {
  font-size: 1rem;
  color: var(--heading-sub);
  margin: 26px 0 10px;
}

.legal-page p {
  margin-bottom: 16px;
  line-height: 1.75;
}

.legal-page ul {
  margin: 0 0 20px 22px;
}

.legal-page li {
  margin-bottom: 9px;
  line-height: 1.7;
}

.legal-contact {
  background: var(--bg);
  border-radius: var(--radius);
  padding: 16px 20px;
  line-height: 1.7;
}

.legal-table {
  width: 100%;
  border-collapse: collapse;
  margin-bottom: 20px;
  font-size: 0.88rem;
}

.legal-table th,
.legal-table td {
  text-align: left;
  padding: 10px 14px;
  border-bottom: 1px solid var(--border);
  vertical-align: top;
}

.legal-table th {
  color: var(--heading);
  font-size: 0.82rem;
  text-transform: uppercase;
  letter-spacing: 0.04em;
}

.legal-table code {
  font-size: 0.85em;
  background: var(--bg);
  padding: 2px 6px;
  border-radius: 4px;
}

/* ===== ABOUT PAGE =====
  
.about-intro {
  max-width: 760px;
  margin: 0 0 40px;
}

.about-section {
  margin-bottom: 48px;
}

.about-section > h2 {
  font-size: 1.4rem;
  color: var(--heading);
  margin-bottom: 10px;
}

/* 960px, not the 760px reading-measure cap most prose on this site uses
   (.about-intro, .legal-page, .about-proof, .about-cta all keep 760px on
   purpose — long-form text stays readable regardless of how wide the
   container gets). This is a one-sentence lede sitting directly above
   .qual-grid, which spans the full container — at 760px, next to a grid
   nearly twice that wide, it reads as a formatting mistake rather than a
   deliberately measured line length. Widened just enough to feel like part
   of the same layout as the grid below it, short of matching it exactly. */
.about-section>p.about-lede {
  max-width: 960px;
  color: var(--text-muted);
  line-height: 1.7;
  margin-bottom: 24px;
}

/* Shared by the two qualification grids (technical / experiential) — same
   card shape as .info-card on the home page, reused rather than
   reinvented, but three-up with room for a longer body than a home page
   teaser needs. */
.qual-grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 20px;
}

.qual-card {
  background: var(--bg-white);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  padding: 24px;
  box-shadow: var(--shadow);
}

.qual-card-eyebrow {
  display: block;
  font-size: 0.78rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--accent);
  margin-bottom: 8px;
}

.qual-card h3 {
  font-size: 1.02rem;
  color: var(--heading);
  margin-bottom: 10px;
}

.qual-card p {
  font-size: 0.93rem;
  color: var(--text-muted);
  line-height: 1.65;
}

/* The "proof" section was removed 14 Aug 2026.  Can be reintroduced if better proof is available.
/* .about-proof {
  max-width: 760px;
  margin: 0 auto;
  background: var(--bg);
  border-left: 4px solid var(--accent);
  border-radius: var(--radius);
  padding: 24px 28px;
} */

/* .about-proof p {
  color: var(--text);
  line-height: 1.75;
  margin-bottom: 14px;
} */

/* .about-proof p:last-child {
  margin-bottom: 0;
} */

.about-cta {
  max-width: 760px;
  margin: 0 auto;
  text-align: center;
  background: var(--bg-white);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow);
  padding: 32px;
}

.about-cta h2 {
  font-size: 1.2rem;
  color: var(--heading);
  margin-bottom: 8px;
}

.about-cta p {
  color: var(--text-muted);
  margin-bottom: 20px;
}

.about-cta-actions {
  display: flex;
  justify-content: center;
  gap: 12px;
  flex-wrap: wrap;
}

/* ===== DOC PREVIEW PAGES (docs/*.html) =====
   The rendered example lifecycle documents reuse .legal-page for typography —
   it already had the right long-form-prose styling for privacy.html — extended
   here with the three block types privacy.html never needed: block quotes,
   ordered lists, and a generic inline <code>, plus a scrollable wrapper for
   the wide requirements tables so a 4-column table cannot force the page to
   scroll sideways on a 360px screen (the same problem .phase-subclauses
   solves for the sub-clause tables on the Learn page). */
.doc-table-wrap {
  overflow-x: auto;
  margin-bottom: 20px;
}

/* The wrapper carries tabindex="0" (see docs/render.py) so a keyboard user can
   reach and scroll a table too wide for the viewport — matching .phase-header
   elsewhere on the site, the default browser outline is replaced with the
   same visible ring rather than removed. */
.doc-table-wrap:focus-visible {
  outline: 3px solid var(--accent);
  outline-offset: 1px;
}

/* PRINT / PDF (see docs/render_pdf.py — this is what "Download" actually
   produces for a document page, so it has to look like a document on paper,
   not a web page with the chrome cropped off). .site-header, .site-footer
   and .skip-link are already hidden print-wide by the unscoped rule further
   down this file; only the button row is specific to this page type. */
@media print {
  .doc-preview-actions {
    display: none !important;
  }

  .doc-table-wrap {
    overflow-x: visible;
    /* a fixed page has no viewport to scroll within */
  }

  .doc-table-wrap .legal-table tr {
    page-break-inside: avoid;
  }
}

.doc-table-wrap .legal-table {
  margin-bottom: 0;
}

.legal-page blockquote {
  background: var(--tint-info);
  border-left: 4px solid var(--accent);
  color: var(--tint-info-text);
  border-radius: var(--radius);
  padding: 14px 20px;
  margin: 0 0 24px;
  font-size: 0.92rem;
  line-height: 1.7;
}

.legal-page blockquote p {
  margin-bottom: 10px;
}

.legal-page blockquote p:last-child {
  margin-bottom: 0;
}

.legal-page blockquote strong {
  color: inherit;
}

.legal-page ol {
  margin: 0 0 20px 22px;
}

.legal-page ol li {
  margin-bottom: 9px;
  line-height: 1.7;
}

.legal-page code {
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  font-size: 0.88em;
  background: var(--bg);
  padding: 1px 6px;
  border-radius: 4px;
}

/* Back-to-Learn / register / download row, above the fold on every doc page. */
.doc-preview-actions {
  max-width: 760px;
  margin: 0 auto 20px;
  display: flex;
  flex-wrap: wrap;
  gap: 10px;
}

.doc-preview-actions .btn {
  padding: 8px 18px;
  font-size: 0.85rem;
}

/* The repeat of .doc-preview-actions at the bottom of the document — a
   border rather than just space, so it reads as "end of document, here's
   where to go next" rather than a stray, unexplained row of buttons. */
.doc-preview-actions-bottom {
  margin-top: 36px;
  padding-top: 24px;
  border-top: 1px solid var(--border);
}

/* Same bounded-card, coloured-strip language as .update-banner and
   .filter-notice, so this reads as the site telling the reader something
   important rather than as decoration — deliberately reused rather than
   invented fresh, so a visitor who has already learned to notice one of
   these notices recognises this one as the same kind of thing. Red/danger
   toned rather than amber: this notice is a correctness boundary (this is
   NOT a real regulatory document), not an informational aside. */
.example-banner-card {
  max-width: 760px;
  margin: 0 auto 28px;
  background: var(--tint-bad-soft);
  border: 1px solid var(--tint-bad);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow);
  overflow: hidden;
}

.example-banner-bar {
  display: flex;
  align-items: center;
  gap: 10px;
  background: var(--danger-fill);
  padding: 9px 16px;
}

.example-banner-icon {
  font-size: 1rem;
  line-height: 1;
  color: #ffffff;
}

.example-banner-label {
  color: #ffffff;
  font-size: 0.82rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.06em;
}

.example-banner-content {
  padding: 16px 20px;
}

.example-banner-content p {
  margin: 0;
  font-size: 0.93rem;
  line-height: 1.65;
  color: var(--text);
}

.example-banner-content a {
  font-weight: 600;
}

@media (max-width: 768px) {

  .doc-preview-actions,
  .example-banner-card {
    max-width: none;
  }
}

/* ===== LEARN CARD: EXAMPLE ARTEFACT LINKS =====
   Deliberately placed at the CARD level (Software Development Planning, the
   whole clause), not inside the per-sub-clause deliverables rows — those rows
   intentionally never invent a document name (see DESIGN.md, Deliverables
   list), and attaching a concrete example file to them would blur exactly the
   line that feature draws. The card already carries a title for the whole
   process area, so an example document belongs here instead. */
.example-doc {
  margin-top: 18px;
  padding-top: 16px;
  border-top: 1px solid var(--border);
}

.example-doc-label {
  font-size: 0.9rem;
  line-height: 1.6;
  color: var(--text-muted);
  margin-bottom: 10px;
}

.example-doc-label strong {
  color: var(--text);
}

.example-doc-actions {
  display: flex;
  flex-wrap: wrap;
  gap: 10px;
}

.example-doc-actions .btn {
  padding: 8px 16px;
  font-size: 0.86rem;
}

/* Deliberately quieter than the Preview/Download buttons above it — this is
   a pointer to a real St John Lynch & Co product, not a third action of
   equal weight, and not part of what Mark as Studied requires (see
   buildExampleDocBlock in learn.js). */
.example-doc-real-template {
  margin-top: 10px;
  font-size: 0.86rem;
  color: var(--text-muted);
}

.example-doc-real-template a {
  color: var(--accent);
  font-weight: 600;
}

/* Privacy notice beneath the contact form's Send button. Small, but not so
   small or so faint that it reads as something being hidden from the user. */
.form-privacy-note {
  margin: 16px 0 0;
  /* Raised from 0.78rem. A data protection notice set in the smallest type on
     the page invites the obvious accusation, however unintended. */
  font-size: 0.85rem;
  line-height: 1.6;
  color: var(--text-muted);
  text-align: center;
}

@media (max-width: 768px) {
  .legal-page {
    padding: 28px 22px;
  }
}

/* ===== FOOTER ===== */

.site-footer {
  background: var(--primary);
  color: rgba(255, 255, 255, 0.75);
  padding: 20px 0;
  /* Raised from 0.82rem (13.1px). The footer sat 3px below the 16px body copy,
     which made it look cramped rather than merely secondary — and it now carries
     the privacy link, which should not be the hardest text on the page to read. */
  font-size: 0.875rem;
  text-align: center;
}

.site-footer a {
  color: rgba(255, 255, 255, 0.9);
}

.site-footer a:hover {
  color: white;
}

/* Second footer line: attribution and the privacy link sharing one row
   instead of two, separated by a middot. On a wide screen three stacked
   centred lines (citation / "Developed by" / privacy link) read as sparse
   and left the row mostly empty; merging the last two into one line uses
   the width instead of the height. flex-wrap means a narrow viewport where
   this doesn't fit on one line still degrades gracefully — the privacy
   link drops to its own line rather than overflowing or being clipped. */
.footer-meta {
  margin-top: 8px;
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  align-items: center;
  gap: 4px 10px;
}

.footer-sep {
  opacity: 0.5;
}

/* The privacy link stays underlined — a legally required notice should not
   be hard to find, even sitting inline with the attribution text next to
   it rather than alone on its own line. */
.footer-links a {
  text-decoration: underline;
}

/* ===== UTILITIES ===== */

.hidden {
  display: none !important;
}

/* Visually hidden but still read by screen readers. */
.sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}

/* Skip link — invisible until focused by keyboard, then slides in at the top. */
.skip-link {
  position: absolute;
  top: -100%;
  left: 0;
  padding: 10px 20px;
  background: var(--primary);
  color: white;
  font-weight: 600;
  font-size: 0.9rem;
  z-index: 200;
  text-decoration: none;
  border-bottom-right-radius: var(--radius);
  transition: top 0.1s;
}

.skip-link:focus {
  top: 0;
}

/* ===== RESPONSIVE ===== */

@media (max-width: 768px) {

  /* Was flex-direction: column (logo, then the full nav row stacked below
     it) — replaced by the hamburger. The logo and toggle now share one row
     (flex-wrap, not column, so they sit side by side); .site-nav gets
     flex-basis: 100% below so that when it's open it drops onto its own
     full-width row under that, rather than needing a direction change. */
  .header-inner {
    flex-wrap: wrap;
    gap: 12px;
    padding: 16px;
  }

  .nav-toggle {
    display: inline-flex;
  }

  /* Collapsed by default below the breakpoint; .nav-open (added by
     nav.js) is what actually reveals it. Above the breakpoint neither
     class nor display:none here ever applies — .site-nav stays the
     ordinary flex row it always was. */
  .site-nav {
    display: none;
    flex-basis: 100%;
    flex-direction: column;
    align-items: stretch;
    gap: 4px;
    margin-top: 4px;
    padding-top: 12px;
    border-top: 1px solid rgba(255, 255, 255, 0.2);
  }

  .site-nav.nav-open {
    display: flex;
  }

  .nav-link {
    padding: 12px 14px;
  }

  .theme-toggle {
    align-self: flex-start;
    margin-left: 0;
    margin-top: 6px;
  }

  .hero {
    grid-template-columns: 1fr;
    gap: 32px;
    padding: 32px 0;
  }

  .hero-image-wrap {
    width: 200px;
    margin: 0 auto;
  }

  .hero-stats {
    flex-direction: row;
    flex-wrap: wrap;
  }

  .stat-card {
    flex: 1;
    padding: 16px;
  }

  /* The flow graphic is wider than the three number cards it sits beside
     (it needs room for six labelled nodes) — flex:1 like them would
     squeeze it illegibly at this width. Full width of its own row instead,
     with the three stat cards wrapping onto the row beneath. */
  .graphic-card {
    flex: 1 1 100%;
  }

  .hero-title {
    font-size: 2rem;
  }

  .info-cards {
    grid-template-columns: 1fr;
  }

  .qual-grid {
    grid-template-columns: 1fr;
  }

  .learn-controls {
    flex-direction: column;
    align-items: stretch;
  }

  .phases-grid {
    grid-template-columns: 1fr;
  }

  .contact-layout {
    grid-template-columns: 1fr;
  }

  .quiz-layout {
    grid-template-columns: 1fr;
  }

  .quiz-start-card {
    padding: 36px 24px;
  }

  .results-card {
    padding: 40px 24px;
  }

  .results-actions {
    flex-direction: column;
  }

  .question-feedback {
    flex-direction: column;
  }
}

/* ===== CERTIFICATE ===== */

/* Hidden on screen — only rendered during window.print(). */
#certificate {
  display: none;
}

.cert-page {
  min-height: 100vh;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 40px;
  background: white;
}

.cert-inner {
  border: 4px solid var(--primary);
  padding: 56px 72px;
  text-align: center;
  max-width: 700px;
  width: 100%;
  position: relative;
  background: white;
}

/* Decorative inset line inside the outer border. */
.cert-inner::before {
  content: '';
  position: absolute;
  inset: 10px;
  border: 1px solid var(--accent-light);
  pointer-events: none;
}

.cert-logo-icon {
  display: block;
  font-size: 2.5rem;
  margin-bottom: 4px;
  color: var(--heading);
}

.cert-site-name {
  font-size: 0.75rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.12em;
  color: var(--text-muted);
  margin-bottom: 28px;
}

.cert-heading {
  font-size: 1.5rem;
  font-weight: 300;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: var(--heading);
  padding-bottom: 20px;
  margin-bottom: 24px;
  border-bottom: 1px solid var(--border);
}

.cert-presented {
  font-size: 0.85rem;
  color: var(--text-muted);
  margin-bottom: 10px;
}

.cert-name {
  font-size: 2.2rem;
  font-weight: 700;
  color: var(--heading);
  font-style: italic;
  margin-bottom: 16px;
  line-height: 1.2;
}

.cert-completed {
  font-size: 0.85rem;
  color: var(--text-muted);
  margin-bottom: 10px;
}

.cert-course-name {
  font-size: 1.25rem;
  font-weight: 700;
  color: var(--heading);
  margin-bottom: 8px;
}

.cert-standard {
  font-size: 0.78rem;
  color: var(--text-muted);
  line-height: 1.7;
  margin-bottom: 28px;
}

.cert-meta {
  display: flex;
  justify-content: center;
  gap: 48px;
  border-top: 1px solid var(--border);
  padding-top: 20px;
  margin-bottom: 20px;
}

.cert-meta-label {
  display: block;
  font-size: 0.68rem;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: var(--text-muted);
  margin-bottom: 4px;
}

.cert-meta-value {
  display: block;
  font-size: 0.95rem;
  font-weight: 700;
  color: var(--heading);
}

.cert-disclaimer {
  font-size: 0.68rem;
  color: var(--text-muted);
  line-height: 1.5;
}

/* ===== PRINT ===== */

@media print {

  /* Force background colours and borders to render in print/PDF output. */
  * {
    -webkit-print-color-adjust: exact;
    print-color-adjust: exact;
  }

  /* Hide everything except the certificate — SCOPED TO THE QUIZ PAGE.
     These rules used to apply everywhere, which meant `main` was hidden on every
     page and printing the Learn or Contact page produced a blank sheet. The
     page-quiz class on <body> in quiz.html confines them to where the
     certificate actually exists. */
  .page-quiz .site-header,
  .page-quiz main,
  .page-quiz .site-footer,
  .page-quiz .skip-link {
    display: none !important;
  }

  .page-quiz #certificate {
    display: block !important;
  }
}

@media (max-width: 480px) {
  .hero-stats {
    flex-direction: column;
  }

  .hero-actions {
    flex-direction: column;
  }

  /* .site-nav's gap and .nav-link's padding/font-size used to be shrunk
     here to keep the whole row squeezed onto one line — no longer needed
     now .site-nav is a full-width dropdown below the hamburger (see the
     768px breakpoint above): a dropdown wants full-size, easy-to-tap rows,
     not smaller text. */

  main {
    padding: 32px 0;
  }

  .results-breakdown {
    grid-template-columns: 1fr;
    gap: 12px;
  }
}