/* Wazapod server-rendered UI — the only hand-written stylesheet.
 *
 * Colour rules, and they are not stylistic:
 *   1. Every colour in this file is a var(--wzp-*) from tokens.css. There is
 *      not one hex literal below, and test/views.test.js fails the build if one
 *      appears. The token set was rebuilt after an audit found a real WCAG
 *      1.4.3 failure (text/muted at 4.4963 on sunken, rounded into a pass) and
 *      a focus ring that was invisible because border/focus was the same hex as
 *      accent/default. A hand-picked colour here re-opens both.
 *   2. Light and dark both come free, because tokens.css redefines the same
 *      variables under prefers-color-scheme and [data-theme].
 *
 * Type sizes are NOT in the token export -- they live as Figma text styles --
 * so the ramp below is transcribed from DESIGN-SYSTEM.md (display/lg 40/48
 * down to label/sm 11/14). Line heights are absolute so vertical rhythm
 * survives a font swap, which is the same reason the export uses them.
 */

:root {
  /* NOT IN tokens.css ON PURPOSE: that file is the Figma export, vendored verbatim,
   * and test/views.test.js fails the build if a hand-edit lands in it. color-scheme is
   * UA behaviour rather than a design token, so it belongs in this file.
   *
   * WITHOUT IT the UA keeps its LIGHT-mode defaults on a dark page -- scrollbars, form
   * controls, and the one that was actually visible: the default link colour. Measured
   * on /account before this line existed, rgb(0,0,238) on rgb(20,20,18) is 1.96:1,
   * against the 4.5:1 WCAG 2.1 AA requires for body text. site/site.css:26 has always
   * had this; app.css never did, so the marketing site's links were correct and the
   * product's were very nearly invisible. Found by looking at a screenshot -- axe-core
   * cannot catch it, because color-contrast is the one rule jsdom's lack of a layout
   * engine makes unrunnable. */
  color-scheme: light dark;

  --wzp-font-sans: Inter, ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
  --wzp-font-mono: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;

  --wzp-display-lg: 40px;   --wzp-display-lg-lh: 48px;
  --wzp-heading-lg: 28px;   --wzp-heading-lg-lh: 36px;
  --wzp-heading-md: 20px;   --wzp-heading-md-lh: 28px;
  --wzp-heading-sm: 16px;   --wzp-heading-sm-lh: 24px;
  --wzp-body-lg: 18px;      --wzp-body-lg-lh: 28px;
  --wzp-body-md: 16px;      --wzp-body-md-lh: 24px;
  --wzp-body-sm: 14px;      --wzp-body-sm-lh: 20px;
  --wzp-label-sm: 11px;     --wzp-label-sm-lh: 14px;

  --wzp-measure: 68ch;
}

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

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

body {
  margin: 0;
  background: var(--wzp-bg-canvas);
  color: var(--wzp-text-primary);
  font-family: var(--wzp-font-sans);
  font-size: var(--wzp-body-md);
  line-height: var(--wzp-body-md-lh);
}

/* Layout primitives set an explicit transparent background. In Figma,
 * auto-layout frames default to a white fill and bled through the tinted
 * cards on the readiness view; the same class of bug is available in CSS the
 * moment somebody adds a default background to a wrapper. */
.wzp-main, .wzp-section, .wzp-form, .wzp-actions { background: transparent; }

/* ---- focus ---------------------------------------------------------------
 * Two-tone, and it has to be. border/focus is the SAME hex as accent/default,
 * so a single-colour ring on the primary button computes to 1.00:1 -- an
 * invisible focus indicator, which is a 2.4.7 failure and the exact bug the
 * token audit found. The inner ring uses border/focus-inverse, which exists
 * for this and only this, so one of the two tones always separates the ring
 * from the fill underneath it. */
:where(a, button, input, select, textarea, summary, [tabindex]):focus-visible {
  outline: var(--wzp-size-focus-ring) solid var(--wzp-border-focus-inverse);
  outline-offset: 0;
  box-shadow: 0 0 0 calc(var(--wzp-size-focus-ring) * 2) var(--wzp-border-focus);
  border-radius: var(--wzp-radius-sm);
}

/* Windows High Contrast throws away both author colours; the system focus
 * colour is the only one that survives, so ask for it explicitly. */
@media (forced-colors: active) {
  :where(a, button, input, select, textarea, summary, [tabindex]):focus-visible {
    outline: 3px solid Highlight;
    outline-offset: 2px;
  }
}

.wzp-skip-link {
  position: absolute;
  left: var(--wzp-space-200);
  top: calc(var(--wzp-space-200) * -10);
  z-index: 10;
  padding: var(--wzp-space-200) var(--wzp-space-300);
  background: var(--wzp-bg-surface-raised);
  color: var(--wzp-accent-text);
  border: 1px solid var(--wzp-border-interactive);
  border-radius: var(--wzp-radius-sm);
}
.wzp-skip-link:focus { top: var(--wzp-space-200); }

/* The visually-hidden pattern that survives being focused and does not
 * collapse whitespace between words the way width:0 does. */
.wzp-vh {
  position: absolute !important;
  width: 1px; height: 1px;
  margin: -1px; padding: 0; border: 0;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

/* ---- shell --------------------------------------------------------------- */

.wzp-header {
  padding: var(--wzp-space-400) var(--wzp-space-500);
  background: var(--wzp-bg-surface);
  border-bottom: 1px solid var(--wzp-border-subtle);
}
.wzp-main {
  max-width: var(--wzp-measure);
  margin: 0 auto;
  padding: var(--wzp-space-500) var(--wzp-space-400) var(--wzp-space-800);
}
/* .wzp-main takes focus from the skip link via tabindex="-1". No
 * outline:none here: a non-interactive target does not match
 * :focus-visible in practice, and suppressing the ring by hand is how a
 * keyboard user ends up with no idea where the skip link landed them. */

.wzp-eyebrow, .wzp-wordmark {
  margin: 0;
  font-size: var(--wzp-label-sm);
  line-height: var(--wzp-label-sm-lh);
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--wzp-text-muted);
}
.wzp-kata-title { margin: var(--wzp-space-100) 0 0; font-size: var(--wzp-heading-md); line-height: var(--wzp-heading-md-lh); font-weight: 600; }
.wzp-subtitle { margin: var(--wzp-space-100) 0 0; color: var(--wzp-text-secondary); font-size: var(--wzp-body-sm); line-height: var(--wzp-body-sm-lh); }

.wzp-nav { display: flex; flex-wrap: wrap; gap: var(--wzp-space-400); list-style: none; margin: var(--wzp-space-200) 0 0; padding: 0; }
.wzp-nav a { color: var(--wzp-accent-text); text-decoration: underline; text-underline-offset: 2px; display: inline-block; min-height: var(--wzp-size-target-min); }
.wzp-nav a[aria-current="page"] { color: var(--wzp-text-primary); text-decoration-thickness: 2px; }

/* ---- type ---------------------------------------------------------------- */

.wzp-h1 { font-size: var(--wzp-heading-lg); line-height: var(--wzp-heading-lg-lh); font-weight: 600; margin: 0 0 var(--wzp-space-300); }
.wzp-h2 { font-size: var(--wzp-heading-md); line-height: var(--wzp-heading-md-lh); font-weight: 600; margin: 0 0 var(--wzp-space-300); }
.wzp-h3 { font-size: var(--wzp-heading-sm); line-height: var(--wzp-heading-sm-lh); font-weight: 600; margin: 0 0 var(--wzp-space-200); }
.wzp-h4 { font-size: var(--wzp-body-sm); line-height: var(--wzp-body-sm-lh); font-weight: 600; margin: 0 0 var(--wzp-space-100); text-transform: none; }
.wzp-lede { font-size: var(--wzp-body-lg); line-height: var(--wzp-body-lg-lh); color: var(--wzp-text-secondary); margin: 0 0 var(--wzp-space-400); }
.wzp-body { margin: 0 0 var(--wzp-space-300); }
.wzp-hint { margin: var(--wzp-space-100) 0 var(--wzp-space-200); color: var(--wzp-text-muted); font-size: var(--wzp-body-sm); line-height: var(--wzp-body-sm-lh); }
/* THE BASE LINK RULE. app.css styled links only inside .wzp-nav and .wzp-errors, so
 * every link in body prose, every list and every hint -- 20 of them across these views
 * -- fell through to the browser default. --wzp-text-link was declared in tokens.css
 * and consumed by nothing here; site/site.css:456 was the only rule that ever read it.
 * Mirrors that rule deliberately, so the product and the marketing site cannot diverge. */
a { color: var(--wzp-text-link, var(--wzp-accent-text)); text-underline-offset: 3px; }
a:hover { text-decoration-thickness: 2px; }

.wzp-muted { color: var(--wzp-text-muted); }
/* HAD NO RULE AT ALL until 2026-09-02, with eight uses in admin.js and two more
 * added the same day. Every "Closed", "none", "Deactivated", "This is you" and
 * "pinned" label rendered in full-strength body text, indistinguishable from real
 * data -- the reader could not tell an absent value from a present one. Same defect
 * class as the .wzp-evidence border-left note below: invisible to reading, obvious
 * on screen. scripts/check-css-classes.py now fails the build on a view class with
 * no rule. */
.wzp-list { padding-left: var(--wzp-space-500); }
.wzp-code { font-family: var(--wzp-font-mono); background: var(--wzp-bg-surface-sunken); padding: var(--wzp-space-50) var(--wzp-space-100); border-radius: var(--wzp-radius-sm); }

.wzp-section {
  margin: 0 0 var(--wzp-space-600);
  padding: var(--wzp-space-400);
  background: var(--wzp-bg-surface);
  border: 1px solid var(--wzp-border-subtle);
  border-radius: var(--wzp-radius-lg);
}
.wzp-card {
  margin: 0 0 var(--wzp-space-300);
  padding: var(--wzp-space-400);
  background: var(--wzp-bg-surface-sunken);
  border: 1px solid var(--wzp-border-subtle);
  border-radius: var(--wzp-radius-md);
}
.wzp-cards { list-style: none; margin: 0; padding: 0; }

/* ---- forms --------------------------------------------------------------- */

.wzp-field { margin: 0 0 var(--wzp-space-400); }
.wzp-field--check { display: grid; grid-template-columns: auto 1fr; gap: var(--wzp-space-200); align-items: start; }
.wzp-field--check .wzp-hint { grid-column: 2; margin-top: 0; }

.wzp-label { display: block; font-weight: 600; margin-bottom: var(--wzp-space-100); }
.wzp-label--inline { display: inline; font-weight: 400; }
.wzp-required { font-weight: 400; color: var(--wzp-text-secondary); }

.wzp-input {
  display: block;
  width: 100%;
  min-height: calc(var(--wzp-size-target-min) * 1.75);
  padding: var(--wzp-space-200) var(--wzp-space-300);
  font: inherit;
  color: var(--wzp-text-primary);
  background: var(--wzp-bg-surface);
  /* border/interactive, never border/default: a control a user must find and
   * operate may not be identified by a decorative divider colour. */
  border: 1px solid var(--wzp-border-interactive);
  border-radius: var(--wzp-radius-sm);
}
.wzp-textarea { min-height: calc(var(--wzp-size-target-min) * 3); resize: vertical; }
.wzp-file { padding: var(--wzp-space-200); }
.wzp-checkbox, .wzp-radio-input {
  /* 2.5.8: 24px is the floor for anything you have to hit. */
  width: var(--wzp-size-target-min);
  height: var(--wzp-size-target-min);
  margin: 0;
  accent-color: var(--wzp-accent-default);
}
.wzp-field--invalid .wzp-input, .wzp-field--invalid .wzp-select { border-color: var(--wzp-status-error); border-width: 2px; }

.wzp-fieldset { margin: 0 0 var(--wzp-space-500); padding: var(--wzp-space-400); border: 1px solid var(--wzp-border-subtle); border-radius: var(--wzp-radius-md); background: transparent; }
.wzp-legend { padding: 0 var(--wzp-space-200); font-weight: 600; }
.wzp-radios { display: grid; gap: var(--wzp-space-300); }
.wzp-radio { display: grid; grid-template-columns: auto 1fr; gap: var(--wzp-space-200); align-items: center; }
.wzp-radio .wzp-hint { grid-column: 2; margin: 0; }

.wzp-field-error { margin: var(--wzp-space-100) 0; color: var(--wzp-status-error-text); font-size: var(--wzp-body-sm); line-height: var(--wzp-body-sm-lh); }
.wzp-field-error-flag { font-weight: 600; }

.wzp-errors {
  margin: 0 0 var(--wzp-space-500);
  padding: var(--wzp-space-400);
  border: 2px solid var(--wzp-status-error);
  border-radius: var(--wzp-radius-md);
  background: var(--wzp-bg-surface);
}
.wzp-errors-title { margin: 0 0 var(--wzp-space-200); font-size: var(--wzp-heading-sm); line-height: var(--wzp-heading-sm-lh); color: var(--wzp-status-error-text); }
.wzp-errors a { color: var(--wzp-status-error-text); }

.wzp-actions { display: flex; flex-wrap: wrap; gap: var(--wzp-space-300); margin: var(--wzp-space-400) 0 0; }

.wzp-button {
  min-height: calc(var(--wzp-size-target-min) * 1.75);
  padding: var(--wzp-space-200) var(--wzp-space-500);
  font: inherit;
  font-weight: 600;
  border-radius: var(--wzp-radius-sm);
  border: 1px solid var(--wzp-accent-default);
  background: var(--wzp-accent-default);
  color: var(--wzp-text-on-accent);
  cursor: pointer;
}
.wzp-button:hover { background: var(--wzp-accent-hover); border-color: var(--wzp-accent-hover); }
.wzp-button--secondary { background: var(--wzp-bg-surface); color: var(--wzp-accent-text); border-color: var(--wzp-border-interactive); }
.wzp-button--secondary:hover { background: var(--wzp-bg-accent-subtle); }
.wzp-button--quiet { background: transparent; color: var(--wzp-text-secondary); border-color: var(--wzp-border-interactive); }
.wzp-button[disabled] { opacity: 0.6; cursor: not-allowed; }

/* ---- notes, pills, chips, badges ----------------------------------------- */

.wzp-note {
  margin: 0 0 var(--wzp-space-300);
  padding: var(--wzp-space-300) var(--wzp-space-400);
  border-left: 4px solid var(--wzp-status-info);
  border-radius: var(--wzp-radius-sm);
  background: var(--wzp-bg-surface-sunken);
}
.wzp-note-tone { margin: 0; font-size: var(--wzp-label-sm); line-height: var(--wzp-label-sm-lh); letter-spacing: 0.08em; text-transform: uppercase; color: var(--wzp-text-muted); }
.wzp-note-title { margin: var(--wzp-space-100) 0 0; font-weight: 600; }
.wzp-note-body { margin: var(--wzp-space-200) 0 0; color: var(--wzp-text-secondary); }
.wzp-note--success { border-left-color: var(--wzp-status-success); }
.wzp-note--attention { border-left-color: var(--wzp-status-attention); }
.wzp-note--error { border-left-color: var(--wzp-status-error); }
.wzp-note--success .wzp-note-tone { color: var(--wzp-status-success-text); }
.wzp-note--attention .wzp-note-tone { color: var(--wzp-status-attention-text); }
.wzp-note--error .wzp-note-tone { color: var(--wzp-status-error-text); }

.wzp-pill, .wzp-chip, .wzp-appbadge {
  display: inline-flex;
  align-items: center;
  gap: var(--wzp-space-200);
  min-height: var(--wzp-size-target-min);
  padding: var(--wzp-space-50) var(--wzp-space-300);
  border-radius: var(--wzp-radius-pill);
  font-size: var(--wzp-body-sm);
  line-height: var(--wzp-body-sm-lh);
}
.wzp-pill-dot, .wzp-chip-dot, .wzp-appbadge-dot {
  width: var(--wzp-space-200); height: var(--wzp-space-200);
  border-radius: var(--wzp-radius-pill);
  background: currentColor;
  flex: none;
}
/* Each pill's label has its own token. The state colour on its own tint failed
 * for two of the five states, which is why *-text exists at all. */
.wzp-pill--not-started { background: var(--wzp-mastery-not-started-bg); color: var(--wzp-mastery-not-started-text); }
.wzp-pill--attempted { background: var(--wzp-mastery-attempted-bg); color: var(--wzp-mastery-attempted-text); }
.wzp-pill--practiced { background: var(--wzp-mastery-practiced-bg); color: var(--wzp-mastery-practiced-text); }
.wzp-pill--ready { background: var(--wzp-mastery-ready-bg); color: var(--wzp-mastery-ready-text); }
.wzp-pill--mastered { background: var(--wzp-mastery-mastered-bg); color: var(--wzp-mastery-mastered-text); }

.wzp-chip { background: var(--wzp-bg-surface-sunken); }
.wzp-chip--deck { color: var(--wzp-provenance-deck); }
.wzp-chip--agent { color: var(--wzp-provenance-agent); }
.wzp-chip--instructor { color: var(--wzp-provenance-instructor); }
.wzp-chip--recording { color: var(--wzp-provenance-recording); }
.wzp-chip--gap { color: var(--wzp-provenance-gap-text); }

.wzp-appbadge { background: var(--wzp-bg-accent-subtle); color: var(--wzp-accent-text); }
.wzp-appbadge-surface { color: var(--wzp-text-secondary); }

.wzp-accom {
  margin: var(--wzp-space-300) 0 0;
  padding: var(--wzp-space-300);
  background: var(--wzp-bg-accent-subtle);
  border-radius: var(--wzp-radius-md);
}
.wzp-accom-title { margin: 0; font-weight: 600; font-size: var(--wzp-body-sm); line-height: var(--wzp-body-sm-lh); }
.wzp-accom-list { margin: var(--wzp-space-100) 0 0; padding-left: var(--wzp-space-500); color: var(--wzp-text-secondary); font-size: var(--wzp-body-sm); }

/* ---- learner surfaces ----------------------------------------------------- */

.wzp-extlink { color: var(--wzp-accent-text); }
.wzp-extlink-note, .wzp-url-note { font-size: var(--wzp-body-sm); color: var(--wzp-text-muted); }
.wzp-url--unusable { color: var(--wzp-status-attention-text); }
.wzp-route-kind { margin: 0 0 var(--wzp-space-200); font-weight: 600; }
.wzp-route-path-label, .wzp-expected-label, .wzp-route-path { color: var(--wzp-text-secondary); }
.wzp-caveats ul { margin: var(--wzp-space-100) 0 0; padding-left: var(--wzp-space-500); }
.wzp-expected { padding: var(--wzp-space-200) var(--wzp-space-300); background: var(--wzp-bg-accent-subtle); border-radius: var(--wzp-radius-sm); }
.wzp-expected-label { font-weight: 600; }

.wzp-frames { list-style: none; margin: 0; padding: 0; }
.wzp-frame { margin: 0 0 var(--wzp-space-500); }
.wzp-shot { margin: 0 0 var(--wzp-space-300); }
.wzp-shot-img { max-width: 100%; height: auto; border: 1px solid var(--wzp-border-default); border-radius: var(--wzp-radius-md); }
.wzp-shot-missing { padding: var(--wzp-space-300); background: var(--wzp-bg-surface-sunken); border: 1px dashed var(--wzp-border-default); border-radius: var(--wzp-radius-md); }
.wzp-shot-caption { margin-top: var(--wzp-space-200); color: var(--wzp-text-secondary); font-size: var(--wzp-body-sm); }
.wzp-callout-text { padding: var(--wzp-space-200) var(--wzp-space-300); background: var(--wzp-annotate-callout-bg); color: var(--wzp-annotate-callout-text); border-radius: var(--wzp-radius-sm); }
.wzp-details summary { min-height: var(--wzp-size-target-min); cursor: pointer; color: var(--wzp-accent-text); }
.wzp-feedback:empty { display: none; }

.wzp-datalist { margin: 0; }
.wzp-datarow { display: grid; grid-template-columns: minmax(9rem, 1fr) 2fr; gap: var(--wzp-space-300); padding: var(--wzp-space-200) 0; border-bottom: 1px solid var(--wzp-border-subtle); }
.wzp-datarow dt { color: var(--wzp-text-secondary); }
.wzp-datarow dd { margin: 0; }

.wzp-evidence { list-style: none; margin: 0; padding: 0; }
.wzp-evidence li { padding: var(--wzp-space-300); margin-bottom: var(--wzp-space-200); border-radius: var(--wzp-radius-md); background: var(--wzp-bg-surface-sunken); border-left: 4px solid var(--wzp-border-default); }
/* QUALIFIED WITH `li` ON PURPOSE -- do not simplify these selectors.
 *
 * These were written as bare `.wzp-evidence--pass`, specificity (0,1,0), while
 * the rule above them is `.wzp-evidence li`, specificity (0,1,1). The base rule
 * therefore won, its `border-left` SHORTHAND reset the colour, and not one
 * verdict colour has ever rendered -- every evidence row has been the same
 * neutral grey since this shipped. I found it by looking at a screenshot, not by
 * reading the file.
 *
 * The text label carries the verdict regardless, which is why this was invisible
 * rather than an accessibility failure: WCAG 1.4.1 was satisfied by the label
 * doing the work colour was supposed to reinforce. */
.wzp-evidence li.wzp-evidence--pass { border-left-color: var(--wzp-status-success); }
.wzp-evidence li.wzp-evidence--fail { border-left-color: var(--wzp-status-attention); }
/* The third verdict. It must not read as a weaker failure -- info, not attention,
 * and a dashed edge so the difference survives greyscale and colour blindness
 * (WCAG 1.4.1: never colour alone). Nothing could be checked here, so nothing is
 * being said about the learner's work. */
.wzp-evidence li.wzp-evidence--unevaluated { border-left-color: var(--wzp-status-info); border-left-style: dashed; }
.wzp-evidence-datum { margin: var(--wzp-space-100) 0 0; font-size: var(--wzp-body-sm); line-height: var(--wzp-body-sm-lh); color: var(--wzp-text-secondary); }
.wzp-evidence-datum span { display: inline-block; min-width: 5rem; color: var(--wzp-text-muted); text-transform: uppercase; font-size: var(--wzp-label-sm); letter-spacing: 0.08em; }
.wzp-evidence-verdict { margin: 0; font-size: var(--wzp-label-sm); line-height: var(--wzp-label-sm-lh); text-transform: uppercase; letter-spacing: 0.08em; color: var(--wzp-text-muted); }
.wzp-evidence-criterion { margin: var(--wzp-space-100) 0 0; font-weight: 600; }
.wzp-evidence-feedback { margin: var(--wzp-space-200) 0 0; color: var(--wzp-text-secondary); }
.wzp-score { font-size: var(--wzp-heading-md); line-height: var(--wzp-heading-md-lh); font-weight: 600; margin: 0 0 var(--wzp-space-300); }

/* ---- instructor surfaces --------------------------------------------------- */

.wzp-roster, .wzp-ttf { list-style: none; margin: 0; padding: 0; }
.wzp-roster-row { margin: 0 0 var(--wzp-space-500); }
.wzp-sources { padding: var(--wzp-space-300); background: var(--wzp-bg-surface-sunken); border-radius: var(--wzp-radius-md); margin-bottom: var(--wzp-space-400); }
.wzp-sources-title { margin: 0 0 var(--wzp-space-100); font-size: var(--wzp-label-sm); line-height: var(--wzp-label-sm-lh); text-transform: uppercase; letter-spacing: 0.08em; color: var(--wzp-text-muted); }
.wzp-sources ul { margin: 0; padding-left: var(--wzp-space-500); }
.wzp-source-scope { color: var(--wzp-text-secondary); }
.wzp-effective { margin-bottom: var(--wzp-space-400); }

.wzp-counts { display: flex; flex-wrap: wrap; gap: var(--wzp-space-500); list-style: none; margin: 0; padding: 0; }
.wzp-count { display: flex; align-items: center; gap: var(--wzp-space-200); }
.wzp-count-n { font-size: var(--wzp-display-lg); line-height: var(--wzp-display-lg-lh); font-weight: 600; }
.wzp-count-gloss { color: var(--wzp-text-secondary); font-size: var(--wzp-body-sm); }

.wzp-tablewrap { overflow-x: auto; }
.wzp-heatmap { width: 100%; border-collapse: collapse; }
.wzp-heatmap th, .wzp-heatmap td { text-align: left; padding: var(--wzp-space-200) var(--wzp-space-300); min-height: var(--wzp-size-target-min); border-bottom: 1px solid var(--wzp-border-subtle); vertical-align: top; }
.wzp-heatmap thead th { color: var(--wzp-text-secondary); font-size: var(--wzp-body-sm); }
.wzp-cell { min-width: var(--wzp-size-target-min); font-variant-numeric: tabular-nums; font-weight: 600; }
/* Severity fill. The count and the severity word carry the meaning; the fill
 * is redundant reinforcement, which is the only role colour is allowed here. */
.wzp-cell--sev0 { background: var(--wzp-mastery-not-started-bg); color: var(--wzp-mastery-not-started-text); }
.wzp-cell--sev1 { background: var(--wzp-mastery-ready-bg); color: var(--wzp-mastery-ready-text); }
.wzp-cell--sev2 { background: var(--wzp-mastery-practiced-bg); color: var(--wzp-mastery-practiced-text); }
.wzp-cell--sev3 { background: var(--wzp-mastery-attempted-bg); color: var(--wzp-mastery-attempted-text); }
.wzp-cell--sev4 { background: var(--wzp-mastery-attempted-bg); color: var(--wzp-status-error-text); }
/* The app switch is its own row and reads as its own row. */
.wzp-row--boundary { background: var(--wzp-bg-accent-subtle); }
.wzp-boundary-flag { font-size: var(--wzp-label-sm); line-height: var(--wzp-label-sm-lh); text-transform: uppercase; letter-spacing: 0.08em; color: var(--wzp-accent-text); }

/* ---- motion --------------------------------------------------------------- */
/* There are no transitions or animations in this stylesheet, so there is
 * nothing for a reduced-motion query to switch off. This block is here so the
 * next person to add one sees the requirement before they do. */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after { animation-duration: 0.001ms !important; transition-duration: 0.001ms !important; }
}
