/* Training UI kit. A small VANILLA design system shared by the admin console + learner app — enterprise
   compliance software restraint (Stripe/Linear/Okta), no framework, no build step.

   STRUCTURE vs COLOR. This file owns the STRUCTURAL scale — spacing, type, radii, control sizing, elevation,
   motion — as custom properties, so every surface is on the same rhythm instead of ad-hoc pixel values. It
   deliberately does NOT define palette tokens (--brand, --ink, --card, --b1, ...): each page declares those
   for its own light/dark themes and this file consumes them, so both themes work automatically.

   Everything is `ui-` prefixed so pages can migrate screen by screen. See docs/training-ux-redesign-plan.md. */

/* ── Design tokens ──────────────────────────────────────────────────────────────────────────────────── */
:root {
  /* Spacing — a 4px rhythm, named by quarter: --sp-3 is 3x4 = 12px. Use these instead of one-off
     paddings so blocks align across screens. */
  --sp-1: 4px; --sp-2: 8px; --sp-3: 12px; --sp-4: 16px; --sp-5: 20px; --sp-6: 24px; --sp-8: 32px; --sp-10: 40px;

  /* AND THE HALF STEPS, which were already the design and were not in the system.
     Counted 2026-10-09 across the two pages: 115 inline spacing values were OFF the 4px grid, and 99 of
     them were one of exactly four numbers — 10px (35), 6px (33), 2px (17), 14px (14). A scale that
     115 places ignore is not being ignored by accident; below 16px the real rhythm here is 2px, and the
     documented one was too coarse to express it, so people reached for raw px instead.
     Naming follows the same quarter rule: --sp-15 is 1.5 x 4 = 6px.
     Rounding those 99 to the nearest documented step would have been a visual change across the whole
     console that nobody could review without looking at it. This changes nothing on screen and makes the
     system describe what is actually built. */
  --sp-05: 2px; --sp-15: 6px; --sp-25: 10px; --sp-35: 14px;

  /* Type scale. Body is 14px; each step is a deliberate rung, so hierarchy reads without shouting. */
  --fs-micro: 11px;   /* table headers, eyebrows — the ONLY sizes that get uppercase treatment */
  --fs-meta: 12px;    /* secondary metadata under a title */
  --fs-sm: 13px;      /* dense UI: buttons, table cells, controls */
  --fs-body: 14px;    /* default body copy */
  --fs-lg: 16px;      /* card/section titles */
  --fs-h2: 19px;      /* sub-page headings */
  --fs-h1: 25px;      /* page titles — must clearly out-rank body */
  --lh-tight: 1.25; --lh-body: 1.55;

  /* Radii — four rungs, nothing in between. */
  --r-sm: 7px; --r-md: 10px; --r-lg: 14px; --r-pill: 999px;

  /* Control sizing — every button/input/select lines up on one baseline. */
  --ctl-h: 36px; --ctl-h-sm: 30px;

  /* Elevation. Dark-theme values; the light overrides below keep shadows believable on a pale ground.
     Used sparingly — cards get --sh-1, popovers/overlays get --sh-3. */
  --sh-1: 0 1px 2px rgba(0,0,0,.20);
  --sh-2: 0 4px 10px -2px rgba(0,0,0,.28), 0 1px 3px rgba(0,0,0,.16);
  --sh-3: 0 16px 40px -12px rgba(0,0,0,.55), 0 2px 8px rgba(0,0,0,.24);

  /* Motion — one easing, two durations. Anything longer feels sluggish. */
  --t: .15s cubic-bezier(.4, 0, .2, 1);
  --t-slow: .28s cubic-bezier(.4, 0, .2, 1);
}
:root[data-theme="light"] {
  --sh-1: 0 1px 2px rgba(16,24,40,.06), 0 1px 1px rgba(16,24,40,.04);
  --sh-2: 0 4px 10px -2px rgba(16,24,40,.08), 0 1px 3px rgba(16,24,40,.06);
  --sh-3: 0 16px 40px -12px rgba(16,24,40,.18), 0 2px 8px rgba(16,24,40,.08);
}

/* ── Page structure ─────────────────────────────────────────────────────────────────────────────────── */
/* One page header shape everywhere: title + one-line purpose on the left, primary action(s) on the right. */
.ui-page-head { display: flex; align-items: flex-start; justify-content: space-between; gap: var(--sp-4); flex-wrap: wrap; margin: 0 0 var(--sp-6); }
.ui-page-head .ui-ph-text { min-width: 0; }
.ui-page-head h1 { font-size: var(--fs-h1); font-weight: 650; margin: 0 0 var(--sp-1); letter-spacing: -.021em; line-height: var(--lh-tight); }
.ui-page-head .ui-ph-sub { color: var(--ink2); font-size: var(--fs-body); margin: 0; max-width: 68ch; line-height: 1.5; }
.ui-page-head .ui-ph-actions { display: flex; align-items: center; gap: var(--sp-2); flex-wrap: wrap; flex-shrink: 0; }

.ui-toolbar { display: flex; align-items: center; gap: var(--sp-2); flex-wrap: wrap; margin: 0 0 var(--sp-4); }
.ui-toolbar .ui-spacer { flex: 1 1 auto; }

/* ── Cards / sections ───────────────────────────────────────────────────────────────────────────────── */
.ui-card { background: var(--card); border: 1px solid var(--b1); border-radius: var(--r-lg); box-shadow: var(--sh-1); }
/* .card is the same surface under the name all three pages already write. It was defined three times,
   once per page, and the copies had drifted: the manager guide had a hardcoded 12px radius against the
   other two at --r-lg (14px) and no elevation at all, so the same class rendered as a different object
   depending which page you were on. SURFACE only. Density is the one thing these pages may legitimately
   differ on - the console packs 48 cards onto a screen the guide uses for two paragraphs - so padding
   and margin stay with the page, declared, and ui-consistency.test.js holds that line. */
.card { background: var(--card); border: 1px solid var(--b1); border-radius: var(--r-lg); box-shadow: var(--sh-1); }
.ui-card > .ui-card-head { display: flex; align-items: center; justify-content: space-between; gap: var(--sp-3); flex-wrap: wrap; padding: var(--sp-4) var(--sp-5); border-bottom: 1px solid var(--b1); }
.ui-card > .ui-card-head h2 { font-size: var(--fs-lg); font-weight: 600; margin: 0; letter-spacing: -.012em; }
.ui-card > .ui-card-body { padding: var(--sp-5); }
/* The one place uppercase micro-type earns its keep: a quiet label above a group of fields/rows. */
.ui-section-label { font-size: var(--fs-micro); font-weight: 600; text-transform: uppercase; letter-spacing: .07em; color: var(--ink3); margin: 0 0 var(--sp-2); }

/* ── Buttons — one hierarchy, one baseline height ───────────────────────────────────────────────────── */
.ui-btn {
  display: inline-flex; align-items: center; justify-content: center; gap: var(--sp-2);
  height: var(--ctl-h); padding: 0 14px; border-radius: var(--r-md);
  border: 1px solid var(--b2); background: var(--card); color: var(--ink);
  font: inherit; font-size: var(--fs-sm); font-weight: 550; letter-spacing: -.002em;
  cursor: pointer; white-space: nowrap; text-decoration: none;
  transition: background var(--t), border-color var(--t), color var(--t), box-shadow var(--t), transform .08s ease;
}
.ui-btn:hover { border-color: var(--b3); background: var(--hover); }
.ui-btn:active { transform: translateY(1px); }
.ui-btn.ui-primary { background: var(--brand2); border-color: var(--brand2); color: #fff; box-shadow: var(--sh-1); }
.ui-btn.ui-primary:hover { filter: brightness(1.07); background: var(--brand2); border-color: var(--brand2); }
.ui-btn.ui-ghost { border-color: transparent; background: transparent; color: var(--ink2); box-shadow: none; }
.ui-btn.ui-ghost:hover { background: var(--hover); color: var(--ink); border-color: transparent; }
.ui-btn.ui-danger { color: var(--bad-fg); border-color: var(--bad-bd); background: var(--bad-bg); }
.ui-btn.ui-danger:hover { background: var(--bad-bg); border-color: var(--bad-fg); filter: brightness(1.08); }
.ui-btn.ui-sm { height: var(--ctl-h-sm); padding: 0 10px; font-size: var(--fs-meta); }
.ui-btn:disabled { opacity: .5; cursor: default; transform: none; filter: none; }
.ui-btn:focus-visible { outline: 2px solid var(--brand2); outline-offset: 2px; }

/* ── Form controls — same height + radius as buttons so rows line up ────────────────────────────────── */
.ui-input, .ui-select {
  height: var(--ctl-h); padding: 0 12px; border: 1px solid var(--b2); border-radius: var(--r-md);
  background: var(--card); color: var(--ink); font: inherit; font-size: var(--fs-sm);
  transition: border-color var(--t), box-shadow var(--t);
}
.ui-input::placeholder { color: var(--ink3); }
.ui-input:hover, .ui-select:hover { border-color: var(--b3); }
.ui-input:focus, .ui-select:focus { outline: none; border-color: var(--brand2); box-shadow: 0 0 0 3px color-mix(in srgb, var(--brand2) 22%, transparent); }
.ui-check { display: inline-flex; align-items: center; gap: var(--sp-2); font-size: var(--fs-sm); color: var(--ink2); white-space: nowrap; cursor: pointer; }
/* A checkbox WITH an explanation under it. .ui-check is nowrap by design, because it is built for short
   inline labels like "Show archived"; a sentence inside one cannot wrap and runs straight off the panel,
   which is what happened to the "they must take it again" option. This is the same control with room to
   explain itself: it wraps, it stays inside its container, and the text block can shrink. */
.ui-check-block { display: flex; align-items: flex-start; gap: var(--sp-2); width: 100%; max-width: 100%;
  font-size: var(--fs-sm); color: var(--ink2); white-space: normal; cursor: pointer; }
.ui-check-block > input { flex: 0 0 auto; margin-top: 3px; }
.ui-check-block > span { min-width: 0; }
.ui-check-block .hint { display: block; color: var(--ink3); font-size: var(--fs-meta); line-height: 1.45; margin-top: 2px; }
.ui-field { display: flex; flex-direction: column; gap: var(--sp-1); }
.ui-field > label { font-size: var(--fs-meta); color: var(--ink2); font-weight: 500; }
.ui-help { font-size: var(--fs-meta); color: var(--ink3); line-height: 1.45; }

/* ── Status badges — THE status map. Never colour alone: every badge carries a dot + a word. ────────── */
.ui-badge { display: inline-flex; align-items: center; gap: 6px; height: 22px; padding: 0 9px; border-radius: var(--r-pill); font-size: var(--fs-micro); font-weight: 600; letter-spacing: .005em; border: 1px solid transparent; white-space: nowrap; }
.ui-badge::before { content: ""; width: 6px; height: 6px; border-radius: 50%; background: currentColor; flex-shrink: 0; }
.ui-badge.ui-ok { background: var(--ok-bg); color: var(--ok-fg); border-color: var(--ok-bd); }           /* completed / active */
.ui-badge.ui-bad { background: var(--bad-bg); color: var(--bad-fg); border-color: var(--bad-bd); }        /* overdue / error */
.ui-badge.ui-warn { background: var(--warn-bg); color: var(--warn-fg); border-color: var(--warn-bd); }    /* due soon / draft */
.ui-badge.ui-muted { background: var(--muted-bg); color: var(--ink3); }                                   /* not started / archived */

/* ── Chips: a VALUE you chose, with a way to take it off ────────────────────────────────────────────
   NOT .ui-badge. A badge is a STATUS the system is reporting - overdue, draft, complete - and it has a
   coloured dot and no background of its own until a tone class is added. Three surfaces were building
   removable chips out of it, so an email address came out as a bare dot, the text, and a loose x: loose
   text with a bullet, not a chip. "This is still messy", and it was.
   A chip is the other thing entirely: something the administrator PUT there and can take off. It has a
   surface, it does not have a status dot, and the remove control is visibly a control rather than a
   character sitting after the words. One class, used by the recipient list, the course tags, a person's
   groups and the add-a-person group picker, because they are all the same object. */
.ui-chip { display: inline-flex; align-items: center; gap: var(--sp-2); max-width: 100%;
  background: var(--card2); color: var(--ink); border: 1px solid var(--b1); border-radius: var(--r-pill);
  padding: 3px var(--sp-2) 3px var(--sp-3); font-size: var(--fs-meta); font-weight: 500; line-height: 1.4; }
.ui-chip > .ui-chip-label { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.ui-chip.is-auto { background: var(--muted-bg); color: var(--ink2); border-style: dashed; }
/* A BUTTON, and it looks like one. Separated by its own left border so it cannot read as the last
   character of the value - which is how "jmartell@integrafs.com x" happens. */
.ui-chip-x { display: inline-flex; align-items: center; justify-content: center; width: 18px; height: 18px;
  padding: 0; margin: 0; border: 0; border-radius: 50%; background: transparent; color: var(--ink3);
  font: inherit; font-size: 13px; line-height: 1; cursor: pointer; flex-shrink: 0;
  transition: background var(--t), color var(--t); }
.ui-chip-x:hover, .ui-chip-x:focus-visible { background: var(--bad-bg); color: var(--bad-fg); }
/* Clickable chips (a group that opens) get the affordance; a plain value chip does not. */
button.ui-chip { cursor: pointer; font: inherit; font-size: var(--fs-meta); }
button.ui-chip:hover { border-color: var(--brand2); color: var(--brand2); }

/* ── Requires-attention panel (the Overview hero) ───────────────────────────────────────────────────── */
.ui-attention { display: grid; grid-template-columns: repeat(auto-fit, minmax(236px, 1fr)); gap: 1px; background: var(--b1); border: 1px solid var(--b1); border-radius: var(--r-lg); overflow: hidden; box-shadow: var(--sh-1); }
.ui-attn-item { display: flex; align-items: center; gap: var(--sp-3); padding: var(--sp-4) var(--sp-5); background: var(--card); border: 0; text-align: left; font: inherit; cursor: pointer; color: inherit; width: 100%; transition: background var(--t); }
.ui-attn-item:hover { background: var(--hover); }
.ui-attn-item[disabled] { cursor: default; } .ui-attn-item[disabled]:hover { background: var(--card); }
.ui-attn-num { font-size: 28px; font-weight: 680; font-variant-numeric: tabular-nums; line-height: 1; letter-spacing: -.03em; min-width: 1.3em; }
.ui-attn-item.ui-urgent .ui-attn-num { color: var(--bad-fg); }
.ui-attn-item.ui-clear .ui-attn-num { color: var(--ok-fg); }
.ui-attn-label { font-size: var(--fs-sm); color: var(--ink2); line-height: 1.35; }
.ui-attn-item .ui-attn-go { margin-left: auto; color: var(--ink3); font-size: var(--fs-lg); opacity: 0; transition: opacity var(--t), transform var(--t); }
.ui-attn-item:hover .ui-attn-go { opacity: 1; transform: translateX(2px); }

/* ── KPI strip (compact, secondary to the attention panel) ──────────────────────────────────────────── */
.ui-kpis { display: grid; grid-template-columns: repeat(auto-fit, minmax(118px, 1fr)); gap: 1px; padding: 0; border: 1px solid var(--b1); border-radius: var(--r-lg); background: var(--b1); box-shadow: var(--sh-1); overflow: hidden; }
.ui-kpi { display: flex; flex-direction: column; gap: 3px; padding: var(--sp-4) var(--sp-5); background: var(--card); }
.ui-kpi .ui-kpi-num { font-size: var(--fs-h2); font-weight: 650; font-variant-numeric: tabular-nums; line-height: 1.1; letter-spacing: -.02em; }
.ui-kpi .ui-kpi-label { font-size: var(--fs-micro); text-transform: uppercase; letter-spacing: .07em; color: var(--ink3); font-weight: 600; }
.ui-kpi.ui-good .ui-kpi-num { color: var(--brand2); } .ui-kpi.ui-bad .ui-kpi-num { color: var(--bad-fg); }

/* ── Data table — comfortable density, quiet header, right-aligned numerics ─────────────────────────── */
.ui-table-wrap { overflow-x: auto; }
.ui-table { width: 100%; border-collapse: collapse; font-size: var(--fs-sm); }
.ui-table th { text-align: left; font-size: var(--fs-micro); font-weight: 600; text-transform: uppercase; letter-spacing: .06em; color: var(--ink3); padding: var(--sp-3) var(--sp-4); background: var(--card2); border-bottom: 1px solid var(--b1); white-space: nowrap; }
.ui-table th:first-child { border-top-left-radius: var(--r-md); } .ui-table th:last-child { border-top-right-radius: var(--r-md); }
.ui-table td { padding: var(--sp-3) var(--sp-4); border-bottom: 1px solid var(--b1); vertical-align: middle; line-height: 1.45; }
.ui-table tbody tr:last-child td { border-bottom: 0; }
.ui-table tbody tr.ui-clickable { cursor: pointer; transition: background var(--t); }
.ui-table tbody tr.ui-clickable:hover { background: var(--hover); }
.ui-table .ui-num { text-align: right; font-variant-numeric: tabular-nums; }
.ui-table .ui-dim { color: var(--ink3); }

/* ── Table footer: range + page size + page nav ─────────────────────────────────────────────────────
   Every data grid gets one, so a long list never dumps hundreds of rows into the page. Hidden entirely
   when the list fits on a single page, so small tables stay clean. */
.ui-pager { display: flex; align-items: center; gap: var(--sp-4); flex-wrap: wrap; padding: var(--sp-3) var(--sp-4); border-top: 1px solid var(--b1); font-size: var(--fs-meta); color: var(--ink2); }
.ui-pager .ui-pager-info { font-variant-numeric: tabular-nums; white-space: nowrap; }
.ui-pager .ui-pager-size { display: flex; align-items: center; gap: var(--sp-2); white-space: nowrap; }
.ui-pager .ui-pager-size select { height: var(--ctl-h-sm); padding: 0 var(--sp-2); border: 1px solid var(--b2); border-radius: var(--r-sm); background: var(--card); color: var(--ink); font: inherit; font-size: var(--fs-meta); }
.ui-pager .ui-pager-nav { display: flex; align-items: center; gap: 2px; margin-left: auto; }
.ui-pager .ui-pager-nav button { min-width: 30px; height: 30px; padding: 0 var(--sp-2); border: 1px solid transparent; border-radius: var(--r-sm); background: transparent; color: var(--ink2); font: inherit; font-size: var(--fs-meta); font-variant-numeric: tabular-nums; cursor: pointer; transition: background var(--t), color var(--t), border-color var(--t); }
.ui-pager .ui-pager-nav button:hover:not(:disabled) { background: var(--hover); color: var(--ink); }
.ui-pager .ui-pager-nav button.ui-current { background: var(--brand2); border-color: var(--brand2); color: #fff; font-weight: 600; }
.ui-pager .ui-pager-nav button:disabled { opacity: .35; cursor: default; }
.ui-pager .ui-pager-gap { padding: 0 2px; color: var(--ink3); }

/* ── Overflow (⋯) action menu ───────────────────────────────────────────────────────────────────────── */
.ui-kebab { display: inline-flex; align-items: center; justify-content: center; width: 32px; height: 32px; border-radius: var(--r-sm); border: 1px solid transparent; background: transparent; color: var(--ink3); font-size: 17px; line-height: 1; cursor: pointer; transition: background var(--t), color var(--t), border-color var(--t); }
.ui-kebab:hover { background: var(--hover); color: var(--ink); border-color: var(--b2); }
.ui-menu { position: fixed; min-width: 190px; background: var(--card); border: 1px solid var(--b2); border-radius: var(--r-md); box-shadow: var(--sh-3); padding: var(--sp-1); z-index: 300; }
.ui-menu button { display: flex; width: 100%; align-items: center; gap: var(--sp-2); background: none; border: 0; color: var(--ink); font: inherit; font-size: var(--fs-sm); text-align: left; padding: var(--sp-2) var(--sp-3); border-radius: var(--r-sm); cursor: pointer; transition: background var(--t); }
.ui-menu button:hover { background: var(--hover); }
.ui-menu button:disabled { color: var(--ink3); cursor: default; } .ui-menu button:disabled:hover { background: none; }
.ui-menu button.ui-danger-item { color: var(--bad-fg); }
.ui-menu .ui-menu-sep { height: 1px; background: var(--b1); margin: var(--sp-1) 2px; }

/* ── Bulk action bar (appears when rows are selected) ───────────────────────────────────────────────── */
.ui-bulk-bar { display: flex; align-items: center; gap: var(--sp-3); padding: var(--sp-2) var(--sp-4); background: var(--card2); border: 1px solid var(--b2); border-radius: var(--r-md); margin: 0 0 var(--sp-3); box-shadow: var(--sh-1); }
.ui-bulk-bar .ui-bulk-count { font-weight: 600; font-size: var(--fs-sm); }
.ui-bulk-bar .ui-spacer { flex: 1 1 auto; }

/* ── Empty state — give it room; a cramped empty state reads like a bug ─────────────────────────────── */
.ui-empty { text-align: center; padding: var(--sp-10) var(--sp-5); color: var(--ink2); }
.ui-empty .ui-empty-title { font-weight: 600; font-size: var(--fs-lg); color: var(--ink); margin: 0 0 var(--sp-1); letter-spacing: -.01em; }
.ui-empty .ui-empty-sub { font-size: var(--fs-sm); margin: 0 0 var(--sp-4); max-width: 46ch; margin-inline: auto; line-height: 1.5; }

/* ── Responsive ─────────────────────────────────────────────────────────────────────────────────────── */
@media (max-width: 640px) {
  :root { --fs-h1: 21px; }
  .ui-attention { grid-template-columns: 1fr; }
  .ui-kpis { grid-template-columns: repeat(2, 1fr); }
  .ui-card > .ui-card-body, .ui-card > .ui-card-head { padding: var(--sp-4); }
}

/* Respect a reduced-motion preference: keep state changes, drop movement. */
@media (prefers-reduced-motion: reduce) {
  * { transition-duration: .01ms !important; animation-duration: .01ms !important; }
  .ui-btn:active { transform: none; }
}

/* ── Report a problem (P6) ──────────────────────────────────────────────────────────────────────────────
   The reporter was ported from the compliance app with its layout baked into inline styles, so it ignored
   every token on this page: wrong type scale, wrong spacing, wrong buttons. The CIO described it as "the
   fonts and buttons are weird on that", which is exactly what a foreign stylesheet looks like.

   Here instead, on the same tokens as everything else, so it inherits the design system including dark
   mode. `.ri-*` rather than generic names because this overlay is injected into TWO different pages and
   must not collide with either. */
.ri-overlay { position: fixed; inset: 0; z-index: 9000; display: none; align-items: flex-start;
  justify-content: center; padding: 6vh var(--sp-4); overflow: auto;
  background: color-mix(in srgb, #000 55%, transparent); }
.ri-overlay.on { display: flex; }
.ri-card { background: var(--card); color: var(--ink); border: 1px solid var(--b1);
  border-radius: var(--r-lg, 12px); box-shadow: var(--shadow-lg, 0 18px 50px rgba(0,0,0,.35));
  max-width: 560px; width: 100%; padding: var(--sp-5) var(--sp-6); font-family: var(--font); }
.ri-card h2 { margin: 0 0 var(--sp-1); font-size: var(--fs-h2); font-weight: 600; letter-spacing: -.012em; }
.ri-card .ri-lede { margin: 0 0 var(--sp-5); color: var(--ink2); font-size: var(--fs-sm); line-height: 1.5; }
.ri-card label { display: block; font-size: var(--fs-meta); font-weight: 600; color: var(--ink2);
  margin: var(--sp-4) 0 var(--sp-1); }
.ri-card label:first-of-type { margin-top: 0; }
.ri-card textarea, .ri-card select, .ri-card input[type=file] {
  width: 100%; box-sizing: border-box; font: inherit; font-size: var(--fs-sm);
  color: var(--ink); background: var(--card2, var(--card)); border: 1px solid var(--b2);
  border-radius: var(--r-md); padding: var(--sp-2) var(--sp-3); }
.ri-card textarea { resize: vertical; line-height: 1.5; }
.ri-card select { height: var(--ctl-h); padding: 0 var(--sp-3); }
.ri-card input[type=file] { font-size: var(--fs-meta); padding: var(--sp-2); }
.ri-card textarea:focus, .ri-card select:focus { outline: none; border-color: var(--brand2);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--brand2) 22%, transparent); }
.ri-card .ri-note { font-size: var(--fs-meta); color: var(--ink3); margin-top: var(--sp-1); line-height: 1.45; }
.ri-card .ri-msg { margin-top: var(--sp-4); font-size: var(--fs-sm); min-height: 18px; }
.ri-card .ri-msg.err { color: var(--red); }
.ri-card .ri-msg.ok { color: var(--green); }
.ri-card .ri-actions { display: flex; gap: var(--sp-2); justify-content: flex-end; margin-top: var(--sp-5); }

/* ── Exceptions (N5) ────────────────────────────────────────────────────────────────────────────────
   A finding states what is wrong, why it matters and what to do. The colour sits on the left edge rather
   than behind the text, so a list of them stays readable when several are open at once. */
.exc-card { margin: 0 0 var(--sp-5); padding: var(--sp-4); border: 1px solid var(--b1);
  border-left: 3px solid var(--warn-bd); border-radius: var(--r-2); background: var(--card); }
.exc-card.exc-bad { border-left-color: var(--bad-bd); }
.exc-card .exc-title { font-weight: 650; display: flex; align-items: center; gap: var(--sp-2); flex-wrap: wrap; }
.exc-card .exc-line { font-size: var(--fs-meta); color: var(--ink3); margin: var(--sp-1) 0 0; line-height: 1.55; }
.exc-card .exc-line:first-of-type { margin-top: var(--sp-2); }
.exc-card .exc-line strong { color: var(--ink2); font-weight: 600; }
.exc-card details { margin-top: var(--sp-3); }
.exc-card summary { cursor: pointer; font-size: var(--fs-meta); color: var(--brand2); }
.exc-card details ul { margin: var(--sp-2) 0 0; padding-left: var(--sp-6); font-size: var(--fs-meta); }
.exc-card details li { margin: 2px 0; }
/* The chip carries the number while the section is collapsed, which is the whole point of collapsing it. */
.count-chip.chip-bad { color: var(--bad-fg); background: var(--bad-bg); border-color: var(--bad-bd); }

/* ── Show more (X6) ──────────────────────────────────────────────────────────────────────────────────
   The first ten, then a way to see the rest. Full width and centred so it reads as the END of a section
   rather than as another row in it, which is what makes a list feel finished at ten instead of truncated. */
.show-more { display: flex; justify-content: center; margin: var(--sp-3) 0 var(--sp-5); }
.show-more-cell { text-align: center; padding: var(--sp-3) 0 !important; }
.show-more-btn { font-size: var(--fs-meta); }

/* P3: the quoted passage, in the console queue and on the learner's report panel. Set apart from the
   surrounding text because it is somebody else's words, not ours. */
.flag-quote { margin: var(--sp-2) 0; padding: var(--sp-2) var(--sp-3); border-left: 3px solid var(--brand2);
  background: var(--card2); font-size: var(--fs-meta); line-height: 1.55; max-height: 140px; overflow: auto; }

/* ── Buttons, defined once (UC1) ─────────────────────────────────────────────────────────────────────
   There were THREE definitions of the primary button and they had drifted: the learner's was 2px wider,
   carried a layout margin, and had no hover or active state at all, so the buttons a learner uses did not
   respond to the mouse. The console's had the states but a different ghost entirely.

   These are the single source. They are element-agnostic on purpose: anchors use them too, so nothing here
   assumes a <button>.

   A button owns its own appearance and NOT its position. The learner's .primary carried
   margin-top, which is why seven of its eighteen call sites had to override it with margin:0 — a
   component fighting its own layout. Spacing belongs to the container; .btn-row below is that container. */
.primary, .ghost, .danger {
  display: inline-flex; align-items: center; justify-content: center; gap: var(--sp-2);
  height: var(--ctl-h); padding: 0 14px; border-radius: var(--r-md);
  font: inherit; font-size: var(--fs-sm); font-weight: 550; letter-spacing: -.002em;
  cursor: pointer; white-space: nowrap; text-decoration: none; border: 1px solid transparent;
  transition: background var(--t), border-color var(--t), color var(--t), box-shadow var(--t), transform .08s ease;
}
.primary { background: var(--brand2); border-color: var(--brand2); color: #fff; box-shadow: var(--sh-1); }
.primary:hover { filter: brightness(1.07); }
.ghost { background: var(--card); border-color: var(--b2); color: var(--ink); }
.ghost:hover { border-color: var(--b3); background: var(--hover); }
.danger { background: var(--bad-bg); border-color: var(--bad-bd); color: var(--bad-fg); }
.danger:hover { border-color: var(--bad-fg); filter: brightness(1.04); }
.primary:active, .ghost:active, .danger:active { transform: translateY(1px); }
.primary:disabled, .ghost:disabled, .danger:disabled,
.primary[aria-disabled="true"], .ghost[aria-disabled="true"] {
  opacity: .5; cursor: default; filter: none; box-shadow: none; transform: none;
}
/* Keyboard focus only — a mouse click should not leave a ring behind, but a Tab must always show where it
   landed. This was missing from both pages, so a keyboard user had nothing to follow. */
.primary:focus-visible, .ghost:focus-visible, .danger:focus-visible {
  outline: none; box-shadow: 0 0 0 3px color-mix(in srgb, var(--brand2) 35%, transparent);
}
/* The dense variant, for a button sitting inside a table row or a toolbar where full height would stretch
   the row. Named, rather than being a second definition of .ghost that happens to be smaller. */
.btn-sm { height: var(--ctl-h-sm); padding: 0 11px; font-size: var(--fs-meta); gap: 6px; }
/* For a single button that genuinely needs separating from what is above it — the submit at the end of a
   stacked form. Explicit at the call site, rather than every button everywhere carrying a margin and the
   ones that do not want it overriding it back to zero. */
.spaced { margin-top: var(--sp-4); }
/* A row of buttons. Owns the spacing the buttons used to carry themselves. */
.btn-row { display: flex; gap: var(--sp-2); align-items: center; flex-wrap: wrap; margin-top: var(--sp-4); }

/* ── Form controls, defined once (UC2) ───────────────────────────────────────────────────────────────
   There were three systems: the kit's .ui-input, and a bare input/select rule inside EACH page. They had
   different backgrounds, different hover colours and — the one that matters — different focus rings. The
   learner's focused with an outline, the console's with a border and a ring, so the thing a keyboard user
   navigates by changed depending which page they were on.

   These style the ELEMENTS, not just the class, because that is how the pages actually write them: 0 of
   13 learner inputs and 10 of 67 console inputs carried .ui-input. Styling only the class would have left
   the inconsistency exactly where it was and added a third opinion. The class stays as an alias so the
   existing markup keeps working.

   Only this stylesheet's own pages are affected: the compliance app does not load it. */
/* input:not([type]) is NOT redundant. An attribute selector matches the literal attribute, never the
   browser's default, so <input id="newTagLabel"> behaves as a text box and matches none of the rules
   below. 23 console inputs were unstyled for exactly this reason, the sign-in box among them, while the
   13 that happened to carry type="text" looked right. The CIO spotted it as "why are some boxes like
   that" — two visibly different text fields on one screen with nothing to explain the difference. */
input:not([type]),
input[type="text"], input[type="email"], input[type="password"], input[type="search"],
input[type="number"], input[type="date"], input[type="url"], input[type="tel"],
select, textarea, .ui-input, .ui-select {
  height: var(--ctl-h); padding: 0 12px; border: 1px solid var(--b2); border-radius: var(--r-md);
  background: var(--card2); color: var(--ink); font: inherit; font-size: var(--fs-sm);
  transition: border-color var(--t), box-shadow var(--t);
}
textarea { height: auto; padding: var(--sp-2) 12px; line-height: 1.5; resize: vertical; }
input[type="file"] { height: auto; padding: var(--sp-2) var(--sp-3); }
input[type="checkbox"], input[type="radio"] { height: auto; padding: 0; accent-color: var(--brand2); }
input:hover, select:hover, textarea:hover, .ui-input:hover, .ui-select:hover { border-color: var(--b3); }
/* ONE focus treatment, and it is a ring rather than an outline so it reads the same on every control and
   never clips inside a scrolling panel. */
input:focus, select:focus, textarea:focus, .ui-input:focus, .ui-select:focus {
  outline: none; border-color: var(--brand2);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--brand2) 22%, transparent);
}
input::placeholder, textarea::placeholder { color: var(--ink3); }
input:disabled, select:disabled, textarea:disabled { opacity: .6; cursor: default; }

/* ── Tables, defined once (UC3) ──────────────────────────────────────────────────────────────────────
   My first reading of this was wrong and the correction is worth keeping. The three Overview leaderboards
   looked like they "bypassed the table kit", but the console also carried a GLOBAL `th, td` rule whose
   padding and border were character-for-character the same as `.ui-table td`. So bare tables already
   rendered identically, which is why nobody ever noticed that five of thirty-three lacked the class.

   The defect was the duplication, not the appearance: two rules saying the same thing in two files, where
   changing one would have silently diverged the other. This is now the only copy. `.ui-table` adds the
   wrapper, the sticky header and the rounded corners on top. */
table { width: 100%; border-collapse: collapse; }
th, td {
  text-align: left; padding: var(--sp-3) var(--sp-4); border-bottom: 1px solid var(--b1);
  font-size: var(--fs-sm); vertical-align: middle; line-height: 1.45;
}
th {
  color: var(--ink3); font-weight: 600; font-size: var(--fs-micro);
  text-transform: uppercase; letter-spacing: .06em; background: var(--card2); white-space: nowrap;
}

/* The exact words a notification will use, shown before anybody turns it on for everybody. Monospace and
   pre-wrapped because it is a quotation, not prose: line breaks in it are the ones that will be sent. */
.notif-preview {
  margin: var(--sp-2) 0 0; padding: var(--sp-3); white-space: pre-wrap; overflow-wrap: anywhere;
  background: var(--card2); border: 1px solid var(--b1); border-left: 3px solid var(--b3);
  border-radius: var(--r-2); font-family: var(--mono, ui-monospace, Menlo, monospace);
  font-size: var(--fs-micro); line-height: 1.6; color: var(--ink2);
}

/* ── The in-app guide (js/lib/help-drawer.js) ───────────────────────────────────────────────────────
   A drawer rather than a page, because somebody opens help while stuck in the middle of something and a
   page would lose their place. Both training pages render the same component with different content. */
#hdBackdrop { position: fixed; inset: 0; background: rgba(16,24,40,.42); opacity: 0; pointer-events: none;
  transition: opacity var(--t-slow); z-index: 70; }
body.hd-open #hdBackdrop { opacity: 1; pointer-events: auto; }

#hdDrawer { position: fixed; top: 0; right: 0; bottom: 0; width: min(520px, 100vw); z-index: 71;
  display: flex; flex-direction: column; background: var(--card); border-left: 1px solid var(--line);
  box-shadow: var(--sh-3); transform: translateX(100%); transition: transform var(--t-slow);
  visibility: hidden; }
body.hd-open #hdDrawer { transform: none; visibility: visible; }

.hd-head { display: flex; align-items: center; justify-content: space-between; gap: var(--sp-3);
  padding: var(--sp-4) var(--sp-5); border-bottom: 1px solid var(--line); flex: 0 0 auto; }
.hd-head h2 { margin: 0; font-size: var(--fs-h2); line-height: var(--lh-tight); }

.hd-search { padding: var(--sp-3) var(--sp-5); border-bottom: 1px solid var(--line); flex: 0 0 auto; }
.hd-search input { width: 100%; }

/* The section tabs wrap rather than scroll sideways: a hidden tab is a section nobody finds. */
.hd-tabs { display: flex; flex-wrap: wrap; gap: var(--sp-1); padding: var(--sp-3) var(--sp-5);
  border-bottom: 1px solid var(--line); flex: 0 0 auto; }
.hd-tabs:empty { display: none; }
.hd-tab { height: var(--ctl-h-sm); padding: 0 var(--sp-3); border: 1px solid var(--line); border-radius: var(--r-pill);
  background: transparent; color: var(--ink2); font: inherit; font-size: var(--fs-sm); cursor: pointer;
  transition: background var(--t), color var(--t), border-color var(--t); }
.hd-tab:hover { background: var(--hover); color: var(--ink); }
.hd-tab.on { background: var(--brand); border-color: var(--brand); color: #fff; }

.hd-body { flex: 1 1 auto; overflow-y: auto; padding: var(--sp-5); outline: none; }
.hd-item { margin: 0 0 var(--sp-6); scroll-margin-top: var(--sp-4); }
.hd-item:last-child { margin-bottom: 0; }
.hd-item h3 { margin: 0 0 var(--sp-2); font-size: var(--fs-lg); line-height: var(--lh-tight); color: var(--ink); }
.hd-item p { margin: 0 0 var(--sp-3); line-height: var(--lh-body); color: var(--ink2); }
.hd-item p:last-child, .hd-item ul:last-child, .hd-item ol:last-child { margin-bottom: 0; }
.hd-item ul, .hd-item ol { margin: 0 0 var(--sp-3); padding-left: var(--sp-5); color: var(--ink2); line-height: var(--lh-body); }
.hd-item li { margin-bottom: var(--sp-1); }
.hd-item strong { color: var(--ink); }
.hd-item code { font-size: var(--fs-meta); padding: 1px 5px; border-radius: var(--r-sm);
  background: var(--hover); border: 1px solid var(--line); }
.hd-item a { color: var(--brand2); }

/* Deep-linked topic (?help=id). A tinted rule down the left, not a flashing highlight: it has to still be
   readable after it has done its job of saying "here". */
.hd-item.hd-hit { border-left: 3px solid var(--brand2); padding-left: var(--sp-4); margin-left: calc(-1 * var(--sp-4)); }

.hd-where { font-size: var(--fs-micro); text-transform: uppercase; letter-spacing: .06em; color: var(--ink3); margin-bottom: var(--sp-1); }
.hd-count { margin: 0 0 var(--sp-4); font-size: var(--fs-sm); color: var(--ink2); }
.hd-empty { padding: var(--sp-6) 0; color: var(--ink2); font-size: var(--fs-sm); line-height: var(--lh-body); }

@media (max-width: 640px) { #hdDrawer { width: 100vw; border-left: 0; } }
@media (prefers-reduced-motion: reduce) { #hdDrawer, #hdBackdrop { transition: none; } }
