/*
 * Shared design tokens for the frontend (exam + admin). Professional pass
 * (task: "التصاميم اجعلها احترافية"): refined navy/aqua identity, a real
 * type pairing loaded from Google Fonts (Cairo for headings/display,
 * IBM Plex Sans Arabic for body — both were referenced by name before this
 * pass but never actually loaded via a <link>, so every page was silently
 * falling back to the OS's default UI font), a sidebar app shell for the
 * admin console, and dashboard/summary components.
 *
 * Written with CSS logical properties (margin-inline-start, text-align:
 * start, etc.) throughout instead of left/right, so English/LTR and
 * Arabic/RTL both work correctly from the same rules — just set
 * dir="rtl" on <html> and nothing else needs to change. Because the
 * layout below uses flex "row" for the sidebar + content split, RTL
 * automatically puts the sidebar on the right without any extra rules.
 */

:root {
  color-scheme: light;
  /* ── Color system (2026-09-19 color pass) ────────────────────────────
     Full rework, not a patch: the user's own complaint ("تنسيق الالوان
     وتوزيعها غير مناسب") plus a direct audit of this file found real
     problems (below), so this was designed properly instead of nudging
     individual values. Method: audited current SaaS/dashboard color
     practice, then had ChatGPT independently review GitHub Primer,
     Atlassian Design System and IBM Carbon's own published color-system
     docs and cross-check computed text/background contrast ratios against
     WCAG 2.2 (4.5:1 normal text, 3:1 large text) for every semantic pair
     below, in BOTH themes. Their shared principle, applied throughout:
     raw brand hues (--navy-*/--aqua-*/--amber-*) are never used directly
     on text or components — everything else (--ink, --heading, --accent,
     --success, ...) is a semantic layer bound to them, so retheming means
     changing the binding in one place, not hunting every call site.

     Concrete problems this fixes (found by direct inspection, not assumed):
       1. --ink-muted and --ink-faint were only ~9-10 RGB units apart —
          visually almost the same gray, no real secondary/tertiary
          hierarchy. Now a real two-step ladder in both themes.
       2. Dark mode never redefined --success/--danger/--warning (text
          colors) at all — each kept its LIGHT-mode value sitting on the
          new DARK *-bg tones a few lines below: e.g. #177349 (a color
          picked to read on near-white) on #123527 is dark-on-dark, nowhere
          near 4.5:1. Every semantic text/border color below is now
          verified against its own -bg in the theme it lives in.
       3. .card and .modal resolved to the exact same flat surface color in
          dark mode — no visual lift for anything meant to float above the
          page. Real elevation ladder now: --bg → --surface → --surface-
          raised → --surface-overlay (page → card → an inset field/list →
          a modal), each step lighter than the one under it in dark mode.
       4. The --amber-* family was declared but never referenced by any
          component (--warning/--warning-bg were a separate, disconnected
          scale) — dead, confusing duplication. Amber is now explicitly
          THE warning family: --warning derives its exact values from it. */

  /* Brand scale — identity, topbar, decorative use ONLY. Never bind text
     or a component's own foreground/background straight to these; go
     through a semantic token below instead. */
  --navy-950: #071f27;
  --navy-900: #0b3540;
  --navy-800: #104954;
  --navy-700: #185b67;
  --navy-600: #28717c;
  --navy-100: #ddecef;
  --navy-50: #eff6f6;

  --aqua-600: #086c65;
  --aqua-500: #0d8278;
  --aqua-400: #2aa697;
  --aqua-100: #ddf3ef;

  /* Warning family — amber is the real warning scale now (previously two
     disconnected sets of tokens; see problem #4 above). --amber-* names
     kept for any external reference, --warning/--warning-bg/--warning-
     border are what components actually use. */
  --amber-600: #9a5b0a;
  --amber-500: #c47a12;
  --amber-100: #fff3dc;

  /* Text */
  --ink: #17262c;
  --ink-muted: #465b63;
  --ink-faint: #61747a;
  --heading: #103a43;

  /* Surfaces — elevation ladder, see problem #3 above. Identical to
     --surface in light mode (daylight doesn't need visual separation
     between a card and a modal); the real lift happens in dark mode. */
  --bg: #f4f7f7;
  --surface: #ffffff;
  --surface-raised: #f9fbfb;
  --surface-overlay: #ffffff;
  --field: #ffffff;

  /* Borders */
  --border: #ccd8db;
  --border-soft: #e3eaec;
  --border-strong: #82959a;

  /* Accent/action — the interactive color (buttons, focus, active nav),
     bound to aqua but named for its role so it can be retuned without
     touching every .btn-primary/etc. call site. */
  --accent: #0d8278;
  --accent-hover: #086c65;
  --accent-fg: #0a7169;
  --accent-soft: #ddf3ef;
  --on-accent: #ffffff;
  --focus-ring: #0d8278;

  /* Semantic status — each verified >4.5:1 against its own -bg */
  --success: #176b45;
  --success-bg: #e8f5ee;
  --success-border: #87c6a5;

  --danger: #a82a24;
  --danger-bg: #fdecec;
  --danger-border: #e2a09c;

  --warning: #7a4908;
  --warning-bg: #fff3dc;
  --warning-border: #d8ad67;

  --info: #165b66;
  --info-bg: #e6f2f4;
  --info-border: #91bcc3;

  /* Chart series (categorical — created/updated/deleted-style groupings
     that need 3+ distinguishable hues, as opposed to the pass-rate bar's
     own continuous red→green scale, computed separately in app.js). */
  --chart-1: #0d8278;
  --chart-2: #28717c;
  --chart-3: #c47a12;
  --chart-4: #7464b8;
  --chart-5: #c84c48;

  --radius: 12px;
  --radius-sm: 8px;
  --shadow-sm: 0 1px 2px rgba(16, 52, 61, 0.06);
  --shadow: 0 2px 4px rgba(16, 52, 61, 0.07), 0 12px 30px rgba(16, 52, 61, 0.08);
  --shadow-lg: 0 24px 60px rgba(7, 31, 39, 0.18);
  --font-display: 'Cairo', 'Segoe UI', system-ui, sans-serif;
  --font-body: 'IBM Plex Sans Arabic', 'Segoe UI', system-ui, sans-serif;
}

/* ── Dark mode (2026-09-19, color-system rework) ─────────────────────────
   Explicit only — no automatic OS-preference switch, since an admin
   console's readability matters more than guessing intent. The toggle
   button in the admin topbar (#theme-toggle in app.js) sets
   documentElement[data-theme] to 'dark' or 'light' and persists the
   choice in localStorage; with no stored choice yet it falls back to the
   OS preference once, on first load, then never overrides an explicit
   choice again.

   This block was reworked twice. First pass (2026-09-19, early) just
   patched the worst bugs: --danger, --success and --warning text colors
   had never been redefined here at all, so each sat at its LIGHT-mode
   value on top of new dark backgrounds — dark-on-dark, failing WCAG
   contrast; and .card/.modal resolved to the exact same flat surface
   color, with no elevation lift for a modal beyond the backdrop scrim.

   (Historical bugfix note, kept because the lesson is easy to repeat:
   an earlier version of this comment joined "navy" and "aqua" token
   names with a bare slash placed right after an asterisk. A star
   immediately followed by a slash ends a CSS comment no matter what it
   is "part of", so that join silently closed this comment early and the
   entire dark-mode rule below it was parsed as broken CSS and dropped —
   nothing else on the page looked wrong, only the colors never changed.
   Never place a star directly before a slash inside a CSS comment, even
   by accident across a word boundary.)

   Second pass (this rework) replaces the patched values with a full
   token set matching the light block above, researched against GitHub
   Primer, Atlassian Design System and IBM Carbon's dark-theme practice:
   raw brand hues (navy, aqua, amber) stay close to their light-mode
   values — they already read fine on a dark page and keep the brand
   identity consistent — while every semantic token (ink, heading,
   accent, success, danger, warning, info, and the surface ladder) is
   redefined for real dark-mode contrast instead of only patched where
   it broke. Elevation now reads --bg, darkest, up through --surface,
   then --surface-raised and --field at the same step (an inset field
   reads like a raised strip, same as a filled input), then
   --surface-overlay, lightest, for the modal — each step measurably
   lighter than the one below it, the same flat ladder as the light
   block, run the other way. */
:root[data-theme="dark"] {
  color-scheme: dark;
  --navy-100: #1b3540;
  --navy-50: #16232f;
  --aqua-100: #123832;
  --amber-100: #3a2c11;
  --ink: #e9f1f4;
  --ink-muted: #a9bcc4;
  --ink-faint: #7c8f97;
  --heading: #cdeee7;
  --bg: #0e1821;
  --surface: #16232f;
  --surface-raised: #1b2a2f;
  --surface-overlay: #22343a;
  --field: #1b2a2f;
  --border: #2a3b4d;
  --border-soft: #223142;
  --border-strong: #55707d;
  --accent: #2aa697;
  --accent-hover: #3dbcac;
  --accent-fg: #7cd9c9;
  --accent-soft: #123832;
  --on-accent: #052220;
  --focus-ring: #2aa697;
  --success: #4ade8a;
  --success-bg: #123527;
  --success-border: #2f6b48;
  --danger: #ff7a68;
  --danger-bg: #3a201d;
  --danger-border: #6b3129;
  --warning: #ffbb5c;
  --warning-bg: #3a2c11;
  --warning-border: #6b4f1f;
  --info: #7cc3d1;
  --info-bg: #16323a;
  --info-border: #3a5a63;
  --chart-1: #2aa697;
  --chart-2: #5fa8b3;
  --chart-3: #e0a23a;
  --chart-4: #9a8cd9;
  --chart-5: #e57872;
  --shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.3);
  --shadow: 0 1px 2px rgba(0, 0, 0, 0.3), 0 10px 28px rgba(0, 0, 0, 0.35);
  --shadow-lg: 0 20px 50px rgba(0, 0, 0, 0.5);
}

* {
  box-sizing: border-box;
}

html,
body {
  margin: 0;
  min-height: 100%;
}

body {
  background: var(--bg);
  color: var(--ink);
  font-family: var(--font-body);
  line-height: 1.6;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}

.app-shell {
  min-height: 100vh;
  display: flex;
  flex-direction: column;
}

/* ── Top bar ───────────────────────────────────────────────────────────── */

.topbar {
  /* Hard-coded fallback color layered underneath the gradient: on a live
     reproducible check (2026-09-21) Chromium was found to silently drop
     the `--navy-950: #071f27;` custom-property declaration when parsing
     this exact stylesheet -- confirmed via the live CSSOM (cssRules[0]
     .cssText genuinely does not contain "navy-950") in BOTH a long-lived
     tab and a brand new one, so it isn't a stale-cache fluke; the raw
     bytes fetched fresh from the network contain the declaration
     correctly, so this is the browser's own parser dropping it, cause
     unconfirmed. Effect: var(--navy-950) resolved to nothing, the whole
     `background` shorthand became invalid, so .topbar fell back to a
     transparent background -- and its white text (needed for contrast on
     the intended dark gradient) then sat directly on the light page
     background instead, reading as nearly invisible ("IPC Aware" report,
     2026-09-21). background-color below is a plain, non-variable
     fail-safe so the topbar is never transparent even if a brand token
     fails to resolve for any reason; the var() fallbacks on the gradient
     itself are the same defense for the gradient specifically. */
  background-color: #104954;
  background: linear-gradient(120deg, var(--navy-950, #071f27), var(--navy-800, #104954) 60%, var(--navy-700, #185b67));
  color: #fff;
  padding-block: 13px;
  padding-inline: 22px;
  display: flex;
  align-items: center;
  gap: 12px;
  box-shadow: var(--shadow);
  position: relative;
  z-index: 5;
}

.topbar .brand {
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 1.05rem;
  letter-spacing: 0.01em;
}

.topbar .brand-mark {
  display: inline-flex;
  align-items: center;
  margin-inline-end: 6px;
  flex: 0 0 auto;
}

.topbar .brand-mark img {
  height: 30px;
  width: auto;
  display: block;
}

/* Back navigation (2026-09-21, direct user report -- "لا يوجد خيار رجوع
   بعد الدخول للاقسام"): #back-btn lives once in index.html's static
   topbar markup (always on screen, same as theme/lang/logout) and its
   click handler in app.js calls history.back(), which the popstate
   listener there turns into the correct in-app section switch -- see
   renderView()'s own header comment for the full history-API wiring this
   button relies on. icon('back') is authored pointing left (the natural
   reading-order "back" direction for LTR); flipped here to point right
   under RTL instead of hand-drawing a mirrored SVG. */
[dir="rtl"] .icon-back {
  transform: scaleX(-1);
}

/* ── Icons ─────────────────────────────────────────────────────────────── */

.icon {
  width: 16px;
  height: 16px;
  flex: 0 0 auto;
  stroke: currentColor;
  fill: none;
  stroke-width: 2;
  stroke-linecap: round;
  stroke-linejoin: round;
  vertical-align: -3px;
}

.icon-lg {
  width: 22px;
  height: 22px;
}

button .icon,
.nav-btn .icon {
  vertical-align: -3px;
}

.btn-icon-label {
  display: inline-flex;
  align-items: center;
  gap: 6px;
}

/* ── App layout: sidebar + content ────────────────────────────────────── */

.layout {
  flex: 1;
  width: 100%;
  display: flex;
  align-items: stretch;
  min-height: 0;
}

.sidebar {
  flex: 0 0 232px;
  background: var(--surface);
  border-inline-end: 1px solid var(--border);
  padding: 16px 12px;
  display: flex;
  flex-direction: column;
  gap: 4px;
}

.sidebar-group-label {
  font-family: var(--font-display);
  font-size: 0.72rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.07em;
  color: var(--ink-faint);
  padding-inline: 10px;
  margin-block: 14px 6px;
}

.sidebar-group-label:first-child {
  margin-block-start: 4px;
}

.nav-btn {
  display: flex;
  align-items: center;
  gap: 10px;
  width: 100%;
  text-align: start;
  background: transparent;
  color: var(--ink);
  border-radius: var(--radius-sm);
  padding: 9px 10px;
  font-size: 0.92rem;
  font-weight: 600;
}

.nav-btn:hover {
  background: var(--navy-50);
}

.nav-btn.active {
  background: var(--navy-900);
  color: #fff;
}

.nav-btn .icon {
  color: var(--aqua-600);
}

.nav-btn.active .icon {
  color: var(--aqua-400);
}

main {
  flex: 1;
  width: 100%;
  min-width: 0;
  padding: 24px 28px 56px;
}

main.wide {
  max-width: none;
}

main:not(.has-sidebar) {
  max-width: 880px;
  margin-inline: auto;
}

.page-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 12px;
  margin-block-end: 18px;
  flex-wrap: wrap;
}

.page-head h1 {
  margin: 0;
}

.page-subtitle {
  color: var(--ink-muted);
  font-size: 0.92rem;
  margin-block-start: 4px;
}

/* ── Cards ─────────────────────────────────────────────────────────────── */

.card {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  box-shadow: var(--shadow-sm);
  padding: 22px;
  margin-block-end: 16px;
}

h1,
h2,
h3 {
  font-family: var(--font-display);
  color: var(--heading);
  margin-block-start: 0;
  text-wrap: balance;
}

h1 {
  font-size: 1.5rem;
  font-weight: 800;
}

h2 {
  font-size: 1.08rem;
  font-weight: 700;
}

h3 {
  font-size: 0.98rem;
  font-weight: 700;
}

p.muted,
.muted {
  color: var(--ink-muted);
}

label {
  display: block;
  font-size: 0.86rem;
  font-weight: 600;
  color: var(--ink);
  margin-block-end: 6px;
}

input[type='text'],
input[type='email'],
input[type='password'],
input[type='number'],
input[type='date'],
input[type='datetime-local'],
select,
textarea {
  width: 100%;
  padding: 10px 12px;
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-sm);
  font-family: inherit;
  font-size: 0.95rem;
  background: var(--field);
  color: var(--ink);
  transition: border-color 0.15s ease;
}

input:hover,
select:hover,
textarea:hover {
  border-color: var(--navy-600);
}

input:focus,
select:focus,
textarea:focus,
button:focus-visible {
  outline: 2px solid var(--focus-ring);
  outline-offset: 1px;
  border-color: var(--focus-ring);
}

.field {
  margin-block-end: 14px;
}

.field-help {
  font-size: 0.8rem;
  color: var(--ink-faint);
  margin-block-start: 4px;
  margin-block-end: 0;
}

/* Trainee exam page's per-question autosave indicator on a failed save
   (frontend/exam/app.js) — must read as a warning, never as the same dim
   "Saved" confirmation text it replaces. */
.save-indicator-error {
  color: var(--danger);
  font-weight: 600;
}

.field-row {
  display: flex;
  gap: 12px;
  flex-wrap: wrap;
}

.field-row > .field {
  flex: 1 1 160px;
}

/* Basic/advanced disclosure (openForm) — one collapsible section per form,
   collapsed by default on create and auto-opened on edit (see openForm's
   own comment). Native <details>/<summary>, no JS needed to toggle. */
details#form-advanced {
  margin-block: 10px 4px;
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  padding: 4px 12px;
}

details#form-advanced summary {
  cursor: pointer;
  font-weight: 700;
  font-size: 0.92rem;
  padding-block: 10px;
  color: var(--aqua-600);
  user-select: none;
}

details#form-advanced summary:focus-visible {
  outline: 2px solid var(--aqua-500);
  outline-offset: 2px;
}

details#form-advanced[open] summary {
  border-block-end: 1px solid var(--border);
  margin-block-end: 4px;
}

button {
  font-family: inherit;
  cursor: pointer;
  border: none;
  border-radius: var(--radius-sm);
  padding: 10px 18px;
  font-size: 0.92rem;
  font-weight: 700;
  transition: background 0.15s ease, transform 0.05s ease;
}

button:active {
  transform: translateY(1px);
}

.btn-primary {
  background: linear-gradient(120deg, var(--accent), var(--accent-hover));
  color: var(--on-accent);
}

.btn-primary:hover {
  background: var(--accent-hover);
}

.btn-primary:disabled {
  background: var(--border);
  color: var(--ink-muted);
  cursor: not-allowed;
}

.btn-secondary {
  background: var(--navy-100);
  color: var(--navy-900);
}

.btn-secondary:hover {
  background: #d3e9e8;
}

.btn-danger {
  background: var(--danger-bg);
  color: var(--danger);
}

.btn-ghost {
  background: transparent;
  color: var(--ink);
  padding: 6px 10px;
  font-weight: 600;
}

.btn-ghost:hover {
  background: var(--navy-50);
}

.btn-ghost:disabled {
  color: var(--ink-muted);
  cursor: not-allowed;
  opacity: 0.6;
}

.btn-ghost:disabled:hover {
  background: transparent;
}

.btn-sm {
  padding: 6px 12px;
  font-size: 0.82rem;
}

.alert {
  border-radius: var(--radius-sm);
  padding: 12px 14px;
  margin-block-end: 14px;
  font-size: 0.92rem;
}

.alert-error {
  color: var(--danger);
  background: var(--danger-bg);
  border: 1px solid var(--danger-border);
}

.alert-success {
  color: var(--success);
  background: var(--success-bg);
  border: 1px solid var(--success-border);
}

.alert-warning {
  color: var(--warning);
  background: var(--warning-bg);
  border: 1px solid var(--warning-border);
}

.alert-info {
  color: var(--info);
  background: var(--info-bg);
  border: 1px solid var(--info-border);
}

.pill {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 3px 10px;
  border-radius: 999px;
  font-size: 0.76rem;
  font-weight: 700;
}

.pill-success {
  background: var(--success-bg);
  color: var(--success);
}

.pill-danger {
  background: var(--danger-bg);
  color: var(--danger);
}

.pill-neutral {
  background: var(--navy-100);
  color: var(--navy-800);
}

.pill-warning {
  background: var(--warning-bg);
  color: var(--warning);
}

.timer {
  font-variant-numeric: tabular-nums;
  font-weight: 700;
  color: var(--navy-900);
}

.timer.timer-low {
  color: var(--danger);
}

.question {
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 16px;
  margin-block-end: 14px;
}

.question .q-index {
  color: var(--aqua-600);
  font-weight: 700;
  font-size: 0.85rem;
  margin-block-end: 6px;
}

/* Real exam pagination nav (frontend/exam/app.js renderExamRunner) —
   2026-09-19, platform owner's explicit requirement: every exam is now
   shown page by page (1/3/4 questions per page, admin-configured), never
   as one long scrolling page. */
.exam-page-indicator {
  font-weight: 600;
  margin-block-end: 10px;
}

.option {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 10px 12px;
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  margin-block-end: 8px;
  cursor: pointer;
}

.option:hover {
  border-color: var(--aqua-400);
}

.option input {
  width: auto;
  margin: 0;
}

table {
  width: 100%;
  border-collapse: collapse;
  font-size: 0.9rem;
  /* 2026-09-22 layout fix (direct owner feedback: columns read as cramped
     and unevenly spaced on every resource table, e.g. the Exams list).
     Root cause: plain auto table-layout has no rule for how to spend
     leftover width once a row's own content is short, so the browser
     spreads it unevenly across columns -- worse with very few rows, since
     there's little content to size against. table-layout: fixed instead
     divides width strictly by each column's own declared/first-row share,
     which reads as evenly spaced immediately; the actions column is given
     a fixed allowance below (th:empty / the :has() rule) so it doesn't
     eat width the data columns need, while still leaving room for its
     real content. */
  table-layout: fixed;
}

th,
td {
  text-align: start;
  padding: 12px 14px;
  border-bottom: 1px solid var(--border-soft);
  overflow: hidden;
  text-overflow: ellipsis;
}

/* Actions column (always the last, always header-less -- every list/manager
   table in app.js ends its header row with a bare `<th></th>`) commonly
   holds several labelled buttons (copy, release results, send, edit,
   delete...), not just one icon -- an earlier version of this fix pinned
   it to width:1% + nowrap, which let that real content burst out of its
   cell and force the whole table wider than its container (the exact
   cramped-and-scrolling look this fix was meant to remove, confirmed live
   on the Exams list). Instead it gets a fixed, modest allowance and its
   buttons wrap onto their own lines when there are more than one or two,
   while every other column shares the rest of the table evenly. Scoped
   with :has() to only tables that actually have this empty trailing
   header, so a table whose real last column holds data (a report grid,
   say) is never mistakenly narrowed. No markup change needed in app.js --
   purely structural. */
th:empty {
  width: 200px;
}
table:has(th:empty) td:last-child {
  text-align: end;
}
table:has(th:empty) td:last-child .btn-icon-label {
  white-space: nowrap;
}

/* Subtle zebra striping -- makes a wide, many-column table (a roster, a
   report grid) easier to track row-by-row at a glance; one elevation step
   above the card surface, so it stays legible in both themes without
   fighting the existing hover highlight below. */
tr:nth-child(even) td {
  background: var(--surface-raised);
}

th {
  color: var(--ink-faint);
  font-size: 0.74rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.05em;
}

/* Sticky header on every scrollable table (report tables especially — a
   9-column heatmap or a long trainee roster is unreadable once the header
   row scrolls out of view and every column blurs together). Needs its own
   opaque background (the surface color, not transparent) since a sticky
   element still sits above the rows scrolling underneath it. */
.table-scroll table thead th {
  position: sticky;
  top: 0;
  background: var(--surface);
  z-index: 1;
  box-shadow: 0 1px 0 var(--border-soft);
}

tr:hover td {
  background: var(--navy-50);
}

.table-scroll {
  overflow-x: auto;
}

.toolbar {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 12px;
  margin-block-end: 14px;
  flex-wrap: wrap;
}

.empty-state {
  text-align: center;
  color: var(--ink-muted);
  padding: 40px 16px;
}

.empty-state .empty-icon {
  width: 44px;
  height: 44px;
  margin-inline: auto;
  margin-block-end: 10px;
  border-radius: 12px;
  background: var(--navy-50);
  color: var(--ink-faint);
  display: flex;
  align-items: center;
  justify-content: center;
}

.empty-state .empty-icon .icon {
  width: 22px;
  height: 22px;
}

/* ── Report center: card picker (redesign, 2026-09-19) ───────────────────
   Replaces a single flat <select> of nine report names with a scannable
   grid of cards, each with its own icon — the concrete "hard to tell
   reports apart at a glance" complaint from the design/UX review. */
.field-label-standalone {
  font-family: var(--font-display);
  font-size: 0.72rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--ink-faint);
  margin-block-end: 10px;
}

.report-picker-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
  gap: 10px;
}

.report-card {
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
  gap: 8px;
  padding: 16px 10px;
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  background: var(--surface);
  color: var(--ink);
  cursor: pointer;
  font: inherit;
  font-weight: 600;
  font-size: 0.82rem;
  transition: border-color 0.15s ease, background 0.15s ease, box-shadow 0.15s ease;
}

.report-card:hover {
  border-color: var(--aqua-400);
  background: var(--navy-50);
}

.report-card-icon {
  width: 36px;
  height: 36px;
  border-radius: 10px;
  display: flex;
  align-items: center;
  justify-content: center;
  background: var(--navy-50);
  color: var(--navy-700);
}

.report-card-icon .icon {
  width: 20px;
  height: 20px;
}

.report-card.active {
  border-color: var(--aqua-500);
  background: var(--aqua-100);
  box-shadow: 0 0 0 1px var(--aqua-500);
}

.report-card.active .report-card-icon {
  background: var(--aqua-500);
  color: #fff;
}

/* ── Filters + checklist (bulk question picker, member pickers) ──────────── */

.filter-row {
  display: flex;
  gap: 10px;
  flex-wrap: wrap;
  margin-block-end: 12px;
}

.filter-row > .field {
  flex: 1 1 160px;
  margin-block-end: 0;
}

.checklist {
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-sm);
  max-height: 320px;
  overflow-y: auto;
  background: var(--field);
}

.checklist-row {
  display: flex;
  align-items: flex-start;
  gap: 10px;
  padding: 9px 12px;
  border-block-end: 1px solid var(--border-soft);
  cursor: pointer;
  font-size: 0.88rem;
}

.checklist-row:last-child {
  border-block-end: none;
}

.checklist-row:hover {
  background: var(--navy-50);
}

.checklist-row input {
  width: auto;
  margin-block-start: 2px;
}

.checklist-empty {
  padding: 20px;
  text-align: center;
  color: var(--ink-muted);
  font-size: 0.88rem;
}

.count-badge-btn {
  display: inline-flex;
  align-items: center;
  gap: 6px;
}

.spinner {
  width: 18px;
  height: 18px;
  border: 2px solid var(--navy-100);
  border-block-start-color: var(--aqua-500);
  border-radius: 50%;
  display: inline-block;
  animation: spin 0.7s linear infinite;
}

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

.result-score {
  font-family: var(--font-display);
  font-size: 2.4rem;
  font-weight: 800;
  color: var(--navy-900);
}

.modal-backdrop {
  position: fixed;
  inset: 0;
  background: rgba(7, 22, 39, 0.55);
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 16px;
  z-index: 50;
}

.modal {
  background: var(--surface-overlay);
  border-radius: var(--radius);
  padding: 22px;
  width: min(560px, 100%);
  max-height: 88vh;
  overflow-y: auto;
  box-shadow: var(--shadow-lg);
}

.modal.modal-wide {
  width: min(760px, 100%);
}

/* Platform-styled confirm() replacement (see confirmDialog() in app.js) —
   deliberately plain: a message plus the same .toolbar button row every
   other modal in the console already uses, so it reads as "the same
   dialog system", not a one-off. */
.confirm-dialog-message {
  margin: 0 0 18px;
  line-height: 1.6;
}

/* ── Dashboard ─────────────────────────────────────────────────────────── */

.stat-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(130px, 1fr));
  gap: 12px;
  margin-block-end: 20px;
}

/* On wide screens the narrower 130px floor lets all cards in a row (4
   headline KPIs, or the 6 secondary count-cards) share one row instead of
   wrapping to a second, mostly-empty row — the cards themselves still cap
   out visually via padding/typography, they don't stretch unreadably. */
@media (min-width: 1180px) {
  .stat-grid {
    grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
  }
}

.stat-card {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  box-shadow: var(--shadow-sm);
  padding: 16px 18px;
}

/* Restrained accent stripe, headline KPI cards only (4 cards inside the
   "Performance at a glance" stat-grid, one accent each). The 6 secondary
   count-cards below (question banks, questions, exams, ...) deliberately
   stay plain/neutral — real color is reserved for the numbers that carry
   actual meaning (pass rate, average score, ...), not raw record counts. */
.stat-card-accent-accent {
  border-block-start: 3px solid var(--accent);
}

.stat-card-accent-info {
  border-block-start: 3px solid var(--info);
}

.stat-card-accent-warning {
  border-block-start: 3px solid var(--warning);
}

/* Dashboard cards that open their own detail/report on click (design-ux-audit.md
   item: dashboard numbers didn't lead anywhere) — shared hover/focus treatment
   with .step-row below, since both are keyboard-activatable rows now. */
.stat-card-clickable,
.step-row {
  cursor: pointer;
  transition: box-shadow 0.15s, border-color 0.15s;
}
.stat-card-clickable:hover,
.step-row:hover {
  box-shadow: var(--shadow);
  border-color: var(--aqua-500);
}
.stat-card-clickable:focus-visible,
.step-row:focus-visible {
  outline: 2px solid var(--aqua-500);
  outline-offset: 2px;
}

.stat-card .stat-value {
  font-family: var(--font-display);
  font-size: 1.9rem;
  font-weight: 800;
  color: var(--heading);
  line-height: 1.1;
}

.stat-card .stat-label {
  color: var(--ink-muted);
  font-size: 0.82rem;
  margin-block-start: 4px;
}

.steps-list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 10px;
}

.step-row {
  display: flex;
  align-items: center;
  gap: 14px;
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  padding: 12px 14px;
}

.step-row .step-num {
  flex: 0 0 auto;
  width: 28px;
  height: 28px;
  border-radius: 50%;
  background: var(--navy-900);
  color: #fff;
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 0.85rem;
  display: flex;
  align-items: center;
  justify-content: center;
}

.step-row.step-done .step-num {
  background: var(--success);
}

.step-row .step-body {
  flex: 1;
  min-width: 0;
}

.step-row .step-title {
  font-weight: 700;
  color: var(--heading);
  font-size: 0.94rem;
}

.step-row .step-desc {
  color: var(--ink-muted);
  font-size: 0.82rem;
  margin-block-start: 2px;
}

.section-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
  gap: 14px;
}

/* Dashboard charts (2026-09-19, direct user request — see app.js's
   renderDashboardCharts() for the Chart.js side of this). A Chart.js
   canvas needs a sized parent to be "responsive" correctly (its own docs:
   without one it grows without bound) — fixed height, not aspect-ratio,
   since these sit in a grid of variable-width cards. The wide variant is
   for the single full-width 30-day activity chart below the two side-by-
   side ones. */
.chart-container {
  position: relative;
  height: 260px;
}

.chart-container-wide {
  height: 220px;
}

/* Screen-reader/no-CDN-fallback data table for each chart above (filled in
   by fillChartFallbackTable() regardless of whether Chart.js ever loads).
   Visually hidden but still in the accessibility tree and still reachable
   by Ctrl/Cmd+F — the standard "clip" technique, not display:none (which
   screen readers also skip) and not off-left positioning (which breaks in
   RTL). */
.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;
}

/* ── Responsive ────────────────────────────────────────────────────────── */

@media (max-width: 860px) {
  .layout {
    flex-direction: column;
  }
  .sidebar {
    flex: 0 0 auto;
    flex-direction: row;
    overflow-x: auto;
    border-inline-end: none;
    border-block-end: 1px solid var(--border);
    gap: 6px;
    padding: 10px 12px;
  }
  .sidebar-group-label {
    display: none;
  }
  .nav-btn {
    width: auto;
    white-space: nowrap;
  }
  main {
    padding: 18px 14px 40px;
  }
}

@media (max-width: 520px) {
  main {
    padding-inline: 12px;
  }
  .card {
    padding: 16px;
  }
}

/* ── Report visual treatment (2026-09-19, reviewed with a second opinion) ──
   The 9 Cloudflare report endpoints all rendered through one bare generic
   <table> with zero differentiation — including the "heatmap" and
   "leaderboard" reports, which had no heat coloring or ranking treatment
   at all despite their names. Second-opinion review (ChatGPT): keep the
   generic table as the default for the other 7 reports (it's the right
   choice there), but this is a government assessment platform, not a
   game — so the leaderboard gets a restrained horizontal score bar, not a
   medal/podium treatment (an earlier draft of this had medals; dropped —
   see app.js's scoreBarHtml comment for why row-position styling was
   actually wrong for this report's real data shape). Heatmap cell colors
   are set inline from app.js per-cell (the color depends on that cell's
   own value), driven by the report model's own heatmapColumns metadata. */

.score-bar-cell {
  display: flex;
  align-items: center;
  gap: 8px;
  min-inline-size: 140px;
}

.score-bar-track {
  flex: 1;
  block-size: 8px;
  border-radius: 999px;
  background: var(--border-soft);
  overflow: hidden;
}

.score-bar-fill {
  block-size: 100%;
  border-radius: 999px;
}

.score-bar-label {
  font-variant-numeric: tabular-nums;
  font-weight: 600;
  min-inline-size: 3.5em;
  text-align: end;
}

/* ── Report -> PDF export via browser print (2026-09-19) ───────────────────
   #print-area is always present but empty/invisible on screen; app.js's
   exportReportToPdf() fills it right before window.print(). The @media
   print rule below then hides the whole interactive app shell and shows
   only this — so what actually prints (or "Save as PDF"s) is a clean
   document, not the sidebar/topbar/buttons/report picker. */
#print-area {
  display: none;
}

.print-doc-header {
  display: flex;
  align-items: center;
  gap: 14px;
  border-block-end: 2px solid var(--navy-800);
  padding-block-end: 12px;
  margin-block-end: 18px;
}

.print-logo {
  height: 48px;
  width: auto;
}

.print-header-text {
  font-family: var(--font-display);
  font-weight: 700;
  color: var(--navy-900);
}

.print-doc-footer {
  margin-block-start: 24px;
  padding-block-start: 10px;
  border-block-start: 1px solid var(--border);
  color: var(--ink-muted);
  font-size: 0.85em;
}

@media print {
  body * {
    visibility: hidden;
  }
  #print-area,
  #print-area * {
    visibility: visible;
  }
  #print-area {
    display: block;
    position: absolute;
    inset-block-start: 0;
    inset-inline-start: 0;
    width: 100%;
    /* Printing must always be light-on-white paper, independent of
       whatever theme is active on screen (2026-09-19, dark mode review) —
       most browsers already drop background colors by default when
       printing, but the explicit forced values below make sure a page
       that DOES honor them (or a "save as PDF" that keeps backgrounds)
       never renders a dark-mode report as pale text on a dark page. */
    background: #ffffff;
    color: #0f1b2d;
  }
  .print-doc h2 {
    font-family: var(--font-display);
    color: var(--navy-900);
  }
  /* Heatmap cells use inline background colors deliberately (see
     renderReportResult's heatmapCellStyle in app.js) — most browsers skip
     background colors when printing by default, which would silently
     defeat the entire point of a heatmap export. */
  .print-doc table {
    -webkit-print-color-adjust: exact;
    print-color-adjust: exact;
  }
}
