/* ==========================================================================
   ASCENT Report Generator — design system + application shell
   Brand: ASCENT by Logic. Palette verified against Ascent-logo-800x267.png,
   see docs/10-brand-tokens.md (facts) and docs/11-dashboard-design-spec.md
   (implementation spec — this file implements spec §2, §3, §4, §8).
   System font stack only — no external requests, no CDN, no webfonts,
   no gradients, no glassmorphism, no purple/indigo.
   ========================================================================== */

/* --------------------------------------------------------------------
   §2 — Design tokens (paste-ready block from the design spec)
   -------------------------------------------------------------------- */

:root {
  /* Colour - brand */
  --primary: #2e8192;
  --primary-dark: #1f5c69;
  --primary-tint: #eaf3f4;
  --accent: #22b24c;        /* brand-only, see brand-tokens.md §6 whitelist */
  --accent-dark: #034638;
  --accent-tint: #edf8f0;
  --accent-ink: #063a17;    /* only safe text colour on raw --accent */
  --accent-border: #3d9159; /* quiet boundary tone for --accent-tint UI; same
                                hue/lightness family as --accent (H140 vs
                                H137.5, L40.4% vs L41.6%), just desaturated —
                                3.58:1 on --accent-tint, 3.89:1 on --surface,
                                both clear the 3:1 non-text contrast minimum */

  /* Colour - status (never borrow --accent for these) */
  --success: #1d7256;
  --success-tint: #e9f7f2;
  --success-border: #bfe3d3;
  --warning: #855b00;
  --warning-tint: #fdf3df;
  --warning-border: #f0dfab;
  --danger: #b3261e;
  --danger-tint: #fbeae8;
  --danger-border: #f0c6c1;
  --info-border: #c7dde1;

  /* Colour - neutrals */
  --ink: #1a2a2d;
  --ink-muted: #55686c;
  --ink-faint: #5e7478;
  --bg: #f5f8f8;
  --surface: #ffffff;
  --border: #dde6e7;
  --border-strong: #c3d1d2;

  /* Radii */
  --radius-sm: 4px;
  --radius-md: 8px;
  --radius-lg: 12px;
  --radius-pill: 999px;

  /* Shadow - exactly two steps, resting vs. elevated. Do not add a third. */
  --shadow-sm: 0 1px 2px rgba(26, 42, 45, 0.06);
  --shadow-md: 0 4px 16px rgba(26, 42, 45, 0.08);

  /* Type - system stack only, no webfont, no CDN */
  --font-sans: -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial,
    "Noto Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji";
  --font-mono: ui-monospace, "Cascadia Code", "Segoe UI Mono", Consolas,
    monospace;

  /* Type scale - rem, off a 16px root (html { font-size: 100%; }) */
  --text-2xs: 0.6875rem;   /* 11px - micro uppercase labels, tag/chip text */
  --text-xs: 0.75rem;      /* 12px - meta, timestamps, table micro-labels */
  --text-sm: 0.8125rem;    /* 13px - secondary body, hints, dense table cells */
  --text-base: 0.9375rem;  /* 15px - default body text */
  --text-md: 1rem;         /* 16px - emphasised body, rail item label, card title (small) */
  --text-lg: 1.1875rem;    /* 19px - card/section headers, client name */
  --text-xl: 1.4375rem;    /* 23px - page h1, narrow viewport */
  --text-2xl: 1.625rem;    /* 26px - page h1, desktop */
  --text-3xl: 1.875rem;    /* 30px - stat-card values, login/error headline */

  /* Weight */
  --weight-regular: 400;
  --weight-medium: 500;
  --weight-semibold: 600;
  --weight-bold: 700;

  /* Line height */
  --leading-tight: 1.15;   /* headings */
  --leading-snug: 1.3;     /* sub-heads, labels */
  --leading-normal: 1.5;   /* body */
  --leading-relaxed: 1.6;  /* long-form paragraph - intros, empty-state copy */

  /* Letter spacing */
  --tracking-tight: -0.01em;  /* large headings (h1) */
  --tracking-normal: 0;
  --tracking-wide: 0.04em;    /* button labels, small uppercase-adjacent text */
  --tracking-wider: 0.06em;   /* eyebrow labels, table column headers, tags */

  /* Spacing - strict 4px grid */
  --space-1: 4px;
  --space-2: 8px;
  --space-3: 12px;
  --space-4: 16px;
  --space-5: 20px;
  --space-6: 24px;
  --space-7: 32px;
  --space-8: 40px;
  --space-9: 48px;
  --space-10: 64px;

  /* Layout */
  --rail-width: 240px;
  --rail-width-collapsed-bar: 56px;   /* height of the horizontal bar at <=780px */
  --content-max-w-narrow: 480px;      /* login, error page */
  --content-max-w-form: 720px;        /* new-client form */
  --content-max-w-normal: 960px;      /* client detail */
  --content-max-w-wide: 1120px;       /* client index grid, batch results */

  /* Motion */
  --duration-fast: 120ms;
  --duration-base: 200ms;
  --easing-standard: ease;

  /* Focus */
  --focus-ring-color: var(--accent);
  --focus-ring-width: 3px;
  --focus-ring-offset: 2px;
}

html {
  font-size: 100%;
}

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 0.001ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.001ms !important;
    scroll-behavior: auto !important;
  }
}

/* --------------------------------------------------------------------
   Reset / base
   -------------------------------------------------------------------- */

* {
  box-sizing: border-box;
}

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

body {
  margin: 0;
  font-family: var(--font-sans);
  color: var(--ink);
  background: var(--bg);
  font-size: var(--text-base);
  line-height: var(--leading-normal);
  -webkit-font-smoothing: antialiased;
}

img {
  max-width: 100%;
  display: block;
}

h1, h2, h3, p, dl, dd {
  margin: 0;
}

a {
  color: var(--primary-dark);
}

a:hover {
  color: var(--primary);
}

/* --------------------------------------------------------------------
   §8 — Accessibility utilities
   -------------------------------------------------------------------- */

:focus-visible {
  outline: var(--focus-ring-width) solid var(--focus-ring-color);
  outline-offset: var(--focus-ring-offset);
  border-radius: 2px;
}

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

.skip-link {
  position: absolute;
  top: -48px;
  left: var(--space-3);
  z-index: 100;
  background: var(--surface);
  color: var(--primary-dark);
  border: 1px solid var(--primary);
  border-radius: var(--radius-sm);
  padding: var(--space-2) var(--space-4);
  font-size: var(--text-sm);
  font-weight: var(--weight-semibold);
  text-decoration: none;
  box-shadow: var(--shadow-md);
  transition: top var(--duration-fast) var(--easing-standard);
}

.skip-link:focus {
  top: var(--space-3);
}

/* --------------------------------------------------------------------
   §3 — Layout system: application shell + rail
   -------------------------------------------------------------------- */

.app-shell {
  display: flex;
  align-items: stretch;
  min-height: 100vh;
}

/* ----- Rail (nav), expanded desktop state, §3.3 ----- */

.rail {
  flex: 0 0 var(--rail-width);
  width: var(--rail-width);
  position: sticky;
  top: 0;
  height: 100vh;
  display: flex;
  flex-direction: column;
  background: var(--surface);
  border-right: 1px solid var(--border);
  overflow: hidden;
}

.rail__brand {
  display: flex;
  align-items: center;
  min-height: 48px;
  padding: var(--space-6) var(--space-6) var(--space-4);
  text-decoration: none;
  flex-shrink: 0;
}

.rail__brand-full {
  display: block;
}

.rail__brand-compact {
  display: none;
  align-items: center;
  gap: var(--space-2);
}

.rail__brand-word {
  font-size: var(--text-md);
  font-weight: var(--weight-bold);
  color: var(--ink);
  letter-spacing: var(--tracking-wide);
}

.rail__new-client {
  flex-shrink: 0;
  margin: 0 var(--space-4) var(--space-4);
  height: 36px;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-2);
  background: var(--accent-tint);
  color: var(--accent-dark);
  border: 1px solid var(--accent-border);
  border-radius: var(--radius-sm);
  font-size: var(--text-sm);
  font-weight: var(--weight-semibold);
  letter-spacing: var(--tracking-wide);
  text-decoration: none;
  transition: border-color var(--duration-fast) var(--easing-standard);
}

.rail__new-client:hover {
  /* Background and text stay put — a quiet border-darken is enough
     feedback here per principle 1 (§1: "no hover effects that exist
     just to feel alive"); this also avoids inventing another one-off
     background hex the way the old #dcf3e3 did. */
  border-color: var(--accent-dark);
}

.rail__new-client-icon {
  flex-shrink: 0;
}

.rail__new-client--active,
.rail__new-client--active:hover {
  background: var(--primary-tint);
  color: var(--primary-dark);
  border-color: var(--primary);
  box-shadow: inset 3px 0 0 var(--primary);
  font-weight: var(--weight-bold);
}

.rail__divider {
  flex-shrink: 0;
  border: 0;
  border-top: 1px solid var(--border);
  margin: 0 var(--space-4);
}

.rail__section-label {
  flex-shrink: 0;
  font-size: var(--text-2xs);
  font-weight: var(--weight-bold);
  letter-spacing: var(--tracking-wider);
  text-transform: uppercase;
  color: var(--ink-faint);
  margin: var(--space-5) 0 var(--space-2) var(--space-3);
  padding: 0 var(--space-1);
}

/* Desktop-only static nav: the "Clients" label + scrollable list,
   always rendered in the flow — no <details>, nothing to force open.
   This is the fix for the rail being empty at desktop widths: modern
   Chromium hides non-summary <details> content via an internal
   ::details-content box with content-visibility, which author CSS
   cannot reliably override by targeting the children's `display`.
   Rather than fight that, desktop gets a plain always-visible copy and
   mobile gets its own <details>-based copy (below) — exactly one of
   the two is ever visible per breakpoint, toggled with plain
   `display: none`, which removes the hidden copy from the
   accessibility tree and tab order so nothing is announced twice. */
.rail-nav {
  flex: 1 1 auto;
  min-height: 0;
  display: flex;
  flex-direction: column;
}

.rail-switcher {
  display: none;
}

.rail-switcher__summary {
  display: none;
}

.rail-switcher__panel {
  flex: 1 1 auto;
  min-height: 0;
  display: flex;
  flex-direction: column;
}

.rail__list {
  list-style: none;
  margin: 0;
  padding: 0 var(--space-4) var(--space-4);
  flex: 1 1 auto;
  min-height: 0;
  overflow-y: auto;
}

.rail__list-empty {
  padding: 0 var(--space-3);
  font-size: var(--text-sm);
  color: var(--ink-muted);
}

.rail__item {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  height: 40px;
  padding: 0 var(--space-3);
  border-radius: var(--radius-md);
  text-decoration: none;
  color: var(--ink-muted);
  transition: background-color var(--duration-fast) var(--easing-standard),
    color var(--duration-fast) var(--easing-standard);
}

.rail__item:hover {
  background: var(--bg);
  color: var(--ink);
}

.rail__item--active {
  background: var(--primary-tint);
  color: var(--primary-dark);
  font-weight: var(--weight-bold);
  box-shadow: inset 3px 0 0 var(--primary);
}

.rail__item--active:hover {
  background: var(--primary-tint);
  color: var(--primary-dark);
}

.rail__item-chip {
  flex-shrink: 0;
  width: 28px;
  height: 28px;
  border-radius: var(--radius-sm);
  background: var(--primary-tint);
  color: var(--primary-dark);
  font-weight: var(--weight-bold);
  font-size: var(--text-sm);
  display: flex;
  align-items: center;
  justify-content: center;
}

.rail__item-label {
  flex: 1 1 auto;
  min-width: 0;
  font-size: var(--text-md);
  font-weight: var(--weight-medium);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.rail__item--active .rail__item-label {
  font-weight: var(--weight-bold);
}

.rail__item-flag {
  flex-shrink: 0;
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: var(--warning);
}

.rail-kebab {
  display: none;
  flex-shrink: 0;
}

.rail-kebab__summary {
  display: none;
}

/* Standalone desktop session block — direct child of .rail, always
   visible at desktop. The mobile copy lives nested one level deeper,
   inside .rail-kebab__panel, so ".rail > .rail__session" below can
   target only this one without a second class name. */
.rail__session {
  margin-top: auto;
  padding: var(--space-4) var(--space-5) var(--space-5);
  border-top: 1px solid var(--border);
}

.rail__session-line {
  font-size: var(--text-xs);
  color: var(--ink-muted);
  margin: 0 0 var(--space-1);
}

.rail__signout {
  display: inline-block;
  font-size: var(--text-sm);
  font-weight: var(--weight-semibold);
  color: var(--primary-dark);
  text-decoration: none;
  background: none;
  border: 0;
  padding: 0;
  cursor: pointer;
  font-family: inherit;
}

.rail__signout:hover {
  text-decoration: underline;
}

/* ----- Main content column ----- */

.page {
  flex: 1 1 auto;
  min-width: 0;
  padding: var(--space-7) var(--space-6) var(--space-10);
}

.content-max-wide {
  max-width: var(--content-max-w-wide);
  margin-inline: auto;
}

.content-max-normal {
  max-width: var(--content-max-w-normal);
  margin-inline: auto;
}

.content-max-form {
  max-width: var(--content-max-w-form);
  margin-inline: auto;
}

.content-max-narrow {
  max-width: var(--content-max-w-narrow);
  margin-inline: auto;
}

/* ----- Rail collapse, §3.4 — CSS only, no JS -----
   Desktop gets the always-in-flow .rail-nav / standalone .rail__session
   (defined above). Below, exactly one representation is swapped for the
   other with plain `display: none` — no attempt to force <details>
   content open above 780px, since modern Chromium renders non-summary
   <details> children through an internal ::details-content box with
   content-visibility, which authored `display` on the children cannot
   override. The mobile <details> markup is only ever shown at ≤780px,
   where it behaves with zero CSS trickery: native, closed by default,
   opened by the operator tapping the summary. */

@media (max-width: 780px) {
  .app-shell {
    display: block;
  }

  .rail {
    position: sticky;
    top: 0;
    width: auto;
    height: auto;
    flex-direction: row;
    align-items: center;
    border-right: 0;
    border-bottom: 1px solid var(--border);
    padding: 0 var(--space-4);
    gap: var(--space-3);
    z-index: 30;
    overflow: visible;
  }

  .rail__brand {
    order: 1;
    padding: 0;
    margin-right: auto;
    min-height: var(--rail-width-collapsed-bar);
  }

  .rail__brand-full {
    display: none;
  }

  .rail__brand-compact {
    display: flex;
  }

  .rail__new-client {
    order: 3;
    margin: 0;
    flex-shrink: 0;
    width: auto;
    padding: 0 var(--space-4);
    min-height: 44px;
  }

  .rail__divider {
    display: none;
  }

  .rail__section-label {
    margin: var(--space-4) 0 var(--space-2) var(--space-1);
  }

  /* Desktop-only static nav and standalone session block hide;
     the <details>-based mobile equivalents take over below. */
  .rail-nav {
    display: none;
  }

  .rail > .rail__session {
    display: none;
  }

  .rail-switcher,
  .rail-kebab {
    display: block;
    position: static;
    flex: 0 0 auto;
  }

  .rail-switcher {
    order: 2;
  }

  .rail-kebab {
    order: 4;
  }

  .rail-switcher__summary,
  .rail-kebab__summary {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: var(--space-2);
    min-width: 44px;
    height: var(--rail-width-collapsed-bar);
    padding: 0 var(--space-2);
    font-size: var(--text-sm);
    font-weight: var(--weight-semibold);
    color: var(--ink);
    cursor: pointer;
    list-style: none;
  }

  .rail-switcher__summary::-webkit-details-marker,
  .rail-kebab__summary::-webkit-details-marker {
    display: none;
  }

  .rail-switcher__current {
    max-width: 140px;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
  }

  .rail-switcher__caret {
    flex-shrink: 0;
    transition: transform var(--duration-fast) var(--easing-standard);
  }

  .rail-switcher[open] .rail-switcher__caret {
    transform: rotate(180deg);
  }

  .rail-switcher__panel {
    position: absolute;
    top: 100%;
    left: 0;
    right: 0;
    background: var(--surface);
    border-bottom: 1px solid var(--border);
    box-shadow: var(--shadow-md);
    max-height: calc(100vh - var(--rail-width-collapsed-bar));
    overflow-y: auto;
    padding-bottom: var(--space-2);
  }

  .rail__list {
    padding: 0 var(--space-3) var(--space-3);
  }

  .rail__item {
    height: 44px;
  }

  /* §8 minimum tap area at touch-relevant breakpoints */
  .pdf-link {
    min-height: 44px;
  }

  .rail-kebab__panel {
    position: absolute;
    top: 100%;
    right: 0;
    min-width: 220px;
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--radius-md);
    box-shadow: var(--shadow-md);
    margin: var(--space-1) var(--space-3) 0 0;
  }

  .rail__session {
    border-top: 0;
    padding: var(--space-4);
  }

  .page {
    padding: var(--space-6) var(--space-4) var(--space-9);
  }
}

@media (max-width: 620px) {
  .rail__brand-word {
    display: none;
  }

  .rail__new-client-label {
    display: none;
  }

  .rail__new-client {
    /* Spec §3.4 illustrates a 36x36 icon button here, but §8's touch
       target rule explicitly names this control as needing a 44x44
       minimum tap area at this breakpoint — the accessibility
       requirement wins; the icon inside still reads as compact. */
    width: 44px;
    height: 44px;
    padding: 0;
    flex-shrink: 0;
  }

  .rail-switcher__current {
    max-width: 96px;
  }

  .page {
    padding-left: var(--space-4);
    padding-right: var(--space-4);
  }
}

/* --------------------------------------------------------------------
   Chromeless centred shell (login, base_bare.html)
   -------------------------------------------------------------------- */

.bare-shell {
  min-height: 100vh;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  padding: var(--space-7) var(--space-4);
}

.bare-shell__logo {
  display: block;
  margin: 0 auto var(--space-7);
}

.bare-shell__content {
  width: 100%;
}

/* --------------------------------------------------------------------
   Typography — page heading block
   -------------------------------------------------------------------- */

.page-head {
  margin-bottom: var(--space-7);
}

.page-head__eyebrow {
  display: block;
  font-size: var(--text-2xs);
  font-weight: var(--weight-bold);
  letter-spacing: var(--tracking-wider);
  text-transform: uppercase;
  color: var(--primary);
  margin: 0 0 var(--space-2);
}

.page-head h1 {
  font-size: var(--text-2xl);
  font-weight: var(--weight-bold);
  letter-spacing: var(--tracking-tight);
  line-height: var(--leading-tight);
  margin: 0 0 var(--space-2);
}

.page-head p {
  color: var(--ink-muted);
  max-width: 62ch;
  line-height: var(--leading-relaxed);
}

.page-head__row {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: var(--space-4);
  flex-wrap: wrap;
}

.back-link {
  display: inline-flex;
  align-items: center;
  gap: var(--space-1);
  font-size: var(--text-sm);
  font-weight: var(--weight-medium);
  color: var(--ink-muted);
  text-decoration: none;
  margin-bottom: var(--space-4);
}

.back-link:hover {
  color: var(--primary-dark);
  text-decoration: underline;
}

@media (max-width: 620px) {
  .page-head h1 {
    font-size: var(--text-xl);
  }
}

/* --------------------------------------------------------------------
   §4.6 — Alert / banner
   -------------------------------------------------------------------- */

.alert {
  display: flex;
  align-items: flex-start;
  gap: var(--space-3);
  padding: var(--space-4);
  border-radius: var(--radius-md);
  margin-bottom: var(--space-6);
  font-size: var(--text-sm);
  border: 1px solid transparent;
}

.alert--error {
  background: var(--danger-tint);
  border-color: #f0c6c1;
  color: #7a241d;
}

.alert--warning {
  background: var(--warning-tint);
  border-color: #f0dfab;
  color: #5f4200;
}

.alert--info {
  background: var(--primary-tint);
  border-color: var(--info-border);
  color: var(--primary-dark);
}

.alert__icon {
  flex-shrink: 0;
  margin-top: 1px;
}

.alert__body {
  flex: 1;
  min-width: 0;
}

.alert__title {
  font-weight: var(--weight-bold);
  margin: 0 0 2px;
}

/* --------------------------------------------------------------------
   §4.1 — Buttons
   -------------------------------------------------------------------- */

.btn {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  font-family: var(--font-sans);
  font-size: var(--text-sm);
  font-weight: var(--weight-semibold);
  letter-spacing: var(--tracking-wide);
  padding: 10px 18px;
  border-radius: var(--radius-sm);
  border: 1px solid transparent;
  line-height: 1.2;
  cursor: pointer;
  text-decoration: none;
  transition: background-color var(--duration-fast) var(--easing-standard),
    border-color var(--duration-fast) var(--easing-standard),
    transform 60ms var(--easing-standard);
}

.btn:active {
  transform: translateY(1px);
}

.btn-primary {
  background: var(--primary);
  border-color: var(--primary);
  color: #fff;
}

.btn-primary:hover {
  background: var(--primary-dark);
  border-color: var(--primary-dark);
  color: #fff;
}

.btn-primary:disabled {
  background: var(--border-strong);
  border-color: var(--border-strong);
  color: var(--ink-faint);
  cursor: not-allowed;
  transform: none;
}

.btn-accent {
  background: var(--accent);
  border-color: var(--accent);
  color: var(--accent-ink);
}

.btn-accent:hover {
  background: var(--accent-dark);
  border-color: var(--accent-dark);
  color: #fff;
}

.btn-accent:disabled {
  background: var(--border-strong);
  border-color: var(--border-strong);
  color: var(--ink-faint);
  cursor: not-allowed;
  transform: none;
}

.btn-secondary {
  background: var(--surface);
  border-color: var(--border-strong);
  color: var(--ink);
}

.btn-secondary:hover {
  background: var(--primary-tint);
  border-color: var(--primary);
  color: var(--primary-dark);
}

.btn-secondary:disabled {
  background: var(--surface);
  border-color: var(--border);
  color: var(--ink-faint);
  cursor: not-allowed;
  transform: none;
}

.btn-danger {
  background: var(--danger);
  border-color: var(--danger);
  color: #fff;
}

.btn-danger:hover {
  background: #8f1e18;
  border-color: #8f1e18;
  color: #fff;
}

.btn-danger:disabled {
  background: var(--border-strong);
  border-color: var(--border-strong);
  color: var(--ink-faint);
  cursor: not-allowed;
  transform: none;
}

/* Busy state — keeps the resting variant's colour, never greys out.
   Specificity is deliberately raised above the plain :disabled rules
   above so aria-busy wins even though disabled is set at the same time. */

.btn[aria-busy="true"] {
  cursor: progress;
}

.btn .btn__spinner {
  display: none;
}

.btn[aria-busy="true"] .btn__spinner {
  display: inline-block;
}

.btn-primary:disabled[aria-busy="true"] {
  background: var(--primary);
  border-color: var(--primary);
  color: #fff;
}

.btn-accent:disabled[aria-busy="true"] {
  background: var(--accent);
  border-color: var(--accent);
  color: var(--accent-ink);
}

.btn-secondary:disabled[aria-busy="true"] {
  background: var(--surface);
  border-color: var(--border-strong);
  color: var(--ink);
}

.btn-danger:disabled[aria-busy="true"] {
  background: var(--danger);
  border-color: var(--danger);
  color: #fff;
}

.btn__spinner {
  width: 14px;
  height: 14px;
  border-radius: 50%;
  border: 2px solid currentColor;
  border-top-color: transparent;
  opacity: 0.85;
  animation: btn-spin 0.6s linear infinite;
  flex-shrink: 0;
}

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

@media (prefers-reduced-motion: reduce) {
  .btn__spinner {
    animation: none;
    border-top-color: currentColor;
    border-right-color: currentColor;
    opacity: 0.5;
  }
}

/* --------------------------------------------------------------------
   §4.2 — Cards
   -------------------------------------------------------------------- */

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

.card--padded {
  padding: var(--space-6);
}

.card + .card {
  margin-top: var(--space-5);
}

/* Demoted/secondary card — no shadow, no full border, reads as a
   subsection rather than a competing panel (§4.2, used for "Client
   settings" §5.4). */
.card--demoted {
  background: transparent;
  border: 0;
  border-top: 1px solid var(--border);
  border-radius: 0;
  box-shadow: none;
  padding-top: var(--space-5);
}

.section-title {
  font-size: var(--text-2xs);
  font-weight: var(--weight-bold);
  letter-spacing: var(--tracking-wider);
  text-transform: uppercase;
  color: var(--ink-muted);
  margin: 0 0 var(--space-4);
}

/* --------------------------------------------------------------------
   §4.3 — Form fields
   -------------------------------------------------------------------- */

.form-grid {
  display: grid;
  gap: var(--space-6);
}

.field {
  display: grid;
  gap: var(--space-2);
}

.field label,
.fieldset legend {
  font-size: var(--text-sm);
  font-weight: var(--weight-bold);
  color: var(--ink);
}

.field-hint {
  font-size: var(--text-sm);
  color: var(--ink-muted);
  margin: 0;
}

.optional-badge {
  font-weight: var(--weight-medium);
  font-size: var(--text-2xs);
  text-transform: uppercase;
  letter-spacing: var(--tracking-wide);
  color: var(--ink-faint);
  margin-left: var(--space-2);
}

input[type="text"],
input[type="password"],
textarea {
  font-family: inherit;
  font-size: var(--text-base);
  padding: 10px 12px;
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-sm);
  background: var(--surface);
  color: var(--ink);
  width: 100%;
}

input[type="text"]:focus,
input[type="password"]:focus,
textarea:focus {
  border-color: var(--primary);
}

input[type="text"]:disabled,
input[type="password"]:disabled,
textarea:disabled {
  background: var(--bg);
  border-color: var(--border);
  color: var(--ink-faint);
  cursor: not-allowed;
}

input[aria-invalid="true"],
textarea[aria-invalid="true"] {
  border-color: var(--danger);
}

textarea {
  min-height: 110px;
  resize: vertical;
  font-family: inherit;
  line-height: var(--leading-normal);
}

.field-error {
  display: flex;
  align-items: flex-start;
  gap: 6px;
  font-size: var(--text-sm);
  color: var(--danger);
  margin: 0;
}

.field-error svg {
  flex-shrink: 0;
  margin-top: 2px;
}

/* --------------------------------------------------------------------
   §4.4 — File input
   -------------------------------------------------------------------- */

.file-input {
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
  padding: var(--space-4);
  border: 1px dashed var(--border-strong);
  border-radius: var(--radius-md);
  background: var(--primary-tint);
}

.file-input input[type="file"] {
  font-family: inherit;
  font-size: var(--text-sm);
  color: var(--ink);
}

.file-input input[type="file"]:disabled {
  color: var(--ink-faint);
  cursor: not-allowed;
}

.file-input--error {
  border-color: var(--danger);
  background: var(--danger-tint);
}

/* --------------------------------------------------------------------
   §4.5 — Fieldsets & option lists
   -------------------------------------------------------------------- */

.fieldset {
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  padding: var(--space-4);
  margin: 0;
}

.fieldset legend {
  padding: 0 var(--space-1);
}

.fieldset--error {
  border-color: var(--danger);
}

.option-list {
  display: grid;
  gap: var(--space-2);
  margin-top: var(--space-2);
}

.option-item {
  display: flex;
  align-items: flex-start;
  gap: var(--space-2);
  font-size: var(--text-base);
  font-weight: var(--weight-medium);
}

.option-item input {
  margin-top: 3px;
  width: 16px;
  height: 16px;
  accent-color: var(--primary);
}

.option-item input:disabled {
  cursor: not-allowed;
}

.form-actions {
  display: flex;
  gap: var(--space-3);
  justify-content: flex-start;
  margin-top: var(--space-1);
  flex-wrap: wrap;
}

/* --------------------------------------------------------------------
   §4.14 — Empty states
   -------------------------------------------------------------------- */

.empty-state {
  text-align: center;
  padding: 56px var(--space-6);
  border: 1px dashed var(--border-strong);
  border-radius: var(--radius-lg);
  background: var(--surface);
}

.empty-state__icon {
  color: var(--ink-faint);
  margin: 0 auto var(--space-3);
}

.empty-state h2 {
  font-size: var(--text-lg);
  margin: 0 0 var(--space-2);
}

.empty-state p {
  color: var(--ink-muted);
  margin: 0 0 var(--space-5);
  line-height: var(--leading-relaxed);
}

.empty-state--inline {
  text-align: left;
  padding: 0;
  border: 0;
  border-radius: 0;
  background: transparent;
  display: flex;
  align-items: flex-start;
  gap: var(--space-3);
}

.empty-state--inline .empty-state__icon {
  width: 24px;
  height: 24px;
  margin: 2px 0 0;
  flex-shrink: 0;
}

.empty-state--inline p {
  margin: 0;
  color: var(--ink-muted);
  font-size: var(--text-sm);
  line-height: var(--leading-normal);
}

/* --------------------------------------------------------------------
   §5.2 — Client list (index)
   -------------------------------------------------------------------- */

.list-toolbar {
  display: flex;
  align-items: center;
  justify-content: flex-end;
  margin-bottom: var(--space-4);
}

.client-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(260px, 1fr));
  gap: var(--space-4);
}

/* --------------------------------------------------------------------
   §4.9 — Client cards
   -------------------------------------------------------------------- */

.client-card {
  display: block;
  text-decoration: none;
  color: inherit;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  padding: var(--space-5);
  box-shadow: var(--shadow-sm);
  transition: border-color var(--duration-fast) var(--easing-standard),
    box-shadow var(--duration-fast) var(--easing-standard),
    transform var(--duration-fast) var(--easing-standard);
}

.client-card:hover,
.client-card:focus-visible {
  border-color: var(--primary);
  box-shadow: var(--shadow-md);
  transform: translateY(-1px);
}

.client-card__name {
  font-size: var(--text-lg);
  font-weight: var(--weight-bold);
  color: var(--ink);
  margin: 0 0 var(--space-2);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.client-card__tags {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  margin-bottom: var(--space-3);
}

.client-card__meta {
  margin-top: var(--space-3);
}

.client-card__activity {
  font-size: var(--text-xs);
  color: var(--ink-muted);
  margin: 0;
}

.client-card__batch-count {
  font-size: var(--text-xs);
  color: var(--ink-faint);
  margin: 2px 0 0;
}

.client-card__flag {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  margin-top: var(--space-2);
  padding: 2px 8px;
  border-radius: var(--radius-pill);
  background: var(--warning-tint);
  border: 1px solid var(--warning-border);
  color: var(--warning);
  font-size: var(--text-2xs);
  font-weight: var(--weight-bold);
}

/* --------------------------------------------------------------------
   §4.11 — Tags / chips (non-status)
   -------------------------------------------------------------------- */

.tag {
  display: inline-flex;
  align-items: center;
  padding: 3px 9px;
  border-radius: var(--radius-pill);
  background: var(--primary-tint);
  color: var(--primary-dark);
  font-size: var(--text-xs);
  font-weight: var(--weight-semibold);
}

/* --------------------------------------------------------------------
   §4.7 — Status chips
   Shared shell for both chip families (batch-level: complete /
   completed-with-errors; row-level: generated / failed / skipped).
   The spec's own token tables assign identical colour+icon treatment
   to "complete"/"Generated" and to "completed with errors"/"Skipped",
   so the two families share rules rather than duplicating them.
   Semantic aliases let a template express batch-level intent without
   the row-level name reading as a lie: use `.status-chip--complete`
   for the batch "Complete" chip and `.status-chip--complete-errors`
   for the batch "Completed with errors" chip. The original
   `.status-chip--success` / `.status-chip--skipped` names are
   unchanged and still work for row-level chips — nothing that already
   references them needs to change.
   -------------------------------------------------------------------- */

.status-chip {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 4px 10px;
  border-radius: var(--radius-pill);
  font-size: var(--text-xs);
  font-weight: var(--weight-bold);
  border: 1px solid transparent;
  white-space: nowrap;
}

.status-chip svg {
  flex-shrink: 0;
  width: 12px;
  height: 12px;
}

.status-chip--success,
.status-chip--complete {
  background: var(--success-tint);
  border-color: var(--success-border);
  color: var(--success);
}

.status-chip--failed {
  background: var(--danger-tint);
  border-color: var(--danger-border);
  color: var(--danger);
}

.status-chip--skipped,
.status-chip--complete-errors {
  background: var(--warning-tint);
  border-color: var(--warning-border);
  color: var(--warning);
}

/* --------------------------------------------------------------------
   §4.8 — Stat cards
   -------------------------------------------------------------------- */

.summary-bar {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(170px, 1fr));
  gap: var(--space-4);
  margin-bottom: var(--space-7);
}

.stat-card {
  background: var(--surface);
  border: 1px solid var(--border);
  border-left: 4px solid var(--border-strong);
  border-radius: var(--radius-md);
  padding: var(--space-4) var(--space-5);
  box-shadow: var(--shadow-sm);
}

.stat-card--success {
  border-left-color: var(--success);
}

.stat-card--failed {
  border-left-color: var(--danger);
}

.stat-card--skipped {
  border-left-color: var(--warning);
}

.stat-card--total {
  border-left-color: var(--primary);
  background: var(--primary-tint);
}

.stat-card__label {
  display: flex;
  align-items: center;
  gap: 6px;
  font-size: var(--text-2xs);
  font-weight: var(--weight-bold);
  letter-spacing: var(--tracking-wider);
  text-transform: uppercase;
  color: var(--ink-muted);
  margin-bottom: var(--space-2);
}

.stat-card__value {
  font-size: var(--text-3xl);
  font-weight: var(--weight-bold);
  line-height: var(--leading-tight);
  color: var(--ink);
}

.summary-reconcile {
  font-size: var(--text-sm);
  color: var(--ink-muted);
  margin: calc(var(--space-4) * -1) 0 var(--space-6);
}

.summary-reconcile strong {
  color: var(--ink);
}

.summary-reconcile--balanced::before {
  content: "\2713 ";
  color: var(--success);
  font-weight: var(--weight-bold);
}

/* --------------------------------------------------------------------
   §5.4 — Client detail
   -------------------------------------------------------------------- */

.client-detail {
  display: grid;
  grid-template-columns: 1fr 280px;
  gap: var(--space-6);
  align-items: start;
}

@media (max-width: 780px) {
  .client-detail {
    grid-template-columns: 1fr;
  }

  .client-detail__settings {
    order: 2;
  }

  .client-detail__generate {
    order: 1;
  }
}

.summary-logo {
  width: 100%;
  aspect-ratio: 16 / 9;
  object-fit: contain;
  background: var(--bg);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  padding: var(--space-3);
  margin-bottom: var(--space-4);
}

.summary-logo--empty {
  display: flex;
  align-items: center;
  justify-content: center;
  color: var(--ink-faint);
  font-size: var(--text-sm);
}

.summary-row {
  display: grid;
  grid-template-columns: 90px 1fr;
  gap: var(--space-2);
  font-size: var(--text-sm);
  padding: var(--space-2) 0;
  border-bottom: 1px solid var(--border);
}

.summary-row:last-child {
  border-bottom: none;
}

.summary-row dt {
  color: var(--ink-muted);
  font-weight: var(--weight-semibold);
}

.summary-row dd {
  margin: 0;
  color: var(--ink);
}

.chip-row {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
}

/* --------------------------------------------------------------------
   §4.10 — Tables
   -------------------------------------------------------------------- */

.batch-table,
.results-table,
.skipped-table {
  width: 100%;
  border-collapse: collapse;
  font-size: var(--text-sm);
}

.batch-table th,
.results-table th,
.skipped-table th {
  text-align: left;
  padding: var(--space-2) 14px;
  font-size: var(--text-2xs);
  font-weight: var(--weight-bold);
  letter-spacing: var(--tracking-wider);
  text-transform: uppercase;
  color: var(--ink-muted);
  border-bottom: 2px solid var(--border-strong);
  white-space: nowrap;
}

.batch-table td,
.results-table td,
.skipped-table td {
  padding: 12px 14px;
  border-bottom: 1px solid var(--border);
  vertical-align: middle;
}

.batch-table tbody tr:hover,
.results-table tbody tr:hover,
.skipped-table tbody tr:hover {
  background: var(--primary-tint);
}

.batch-table a.row-link {
  color: var(--ink);
  text-decoration: none;
  font-weight: var(--weight-semibold);
}

.batch-table a.row-link:hover {
  color: var(--primary-dark);
  text-decoration: underline;
}

.batch-table__label {
  display: block;
}

.batch-table__id {
  display: block;
  margin-top: 2px;
}

.table-wrap {
  overflow-x: auto;
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  background: var(--surface);
}

.table-wrap table {
  min-width: 560px;
}

/* .pdf-link tap target — the whole cell is clickable, padding moves
   from <td> onto the link itself (§4.10). */
.results-table td.results-table__pdf-cell {
  padding: 0;
}

.pdf-link {
  display: flex;
  align-items: center;
  gap: 6px;
  height: 100%;
  min-height: 40px;
  padding: 12px 14px;
  font-weight: var(--weight-semibold);
  color: var(--primary-dark);
  text-decoration: none;
}

.pdf-link:hover {
  text-decoration: underline;
}

.mono {
  font-family: var(--font-mono);
  font-size: var(--text-xs);
  color: var(--ink-muted);
}

.batch-actions {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-4);
  margin-top: var(--space-2);
  flex-wrap: wrap;
}

.results-section {
  margin-bottom: var(--space-8);
}

.results-section__head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--space-3);
  margin-bottom: var(--space-3);
}

.results-section__head h2 {
  font-size: var(--text-md);
  margin: 0;
}

.results-section__count {
  font-size: var(--text-sm);
  color: var(--ink-muted);
}

/* --------------------------------------------------------------------
   §4.13 — Failure / skip reason: plain English + technical detail
   -------------------------------------------------------------------- */

.reason-text {
  color: var(--ink-muted);
  font-size: var(--text-sm);
  display: block;
}

.reason-text--failed {
  color: var(--danger);
  font-weight: var(--weight-medium);
}

.reason-text--skipped {
  color: var(--warning);
  font-weight: var(--weight-medium);
}

.reason-detail {
  margin-top: 4px;
}

.reason-detail summary {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  font-size: var(--text-xs);
  color: var(--ink-faint);
  cursor: pointer;
  list-style: none;
}

.reason-detail summary::-webkit-details-marker {
  display: none;
}

.reason-detail summary::after {
  content: "";
  width: 6px;
  height: 6px;
  border-right: 1.5px solid currentColor;
  border-bottom: 1.5px solid currentColor;
  transform: rotate(-45deg);
  transition: transform var(--duration-fast) var(--easing-standard);
  margin-left: 2px;
}

.reason-detail[open] summary::after {
  transform: rotate(45deg);
}

.reason-detail .mono {
  display: block;
  margin: var(--space-2) 0 0;
  padding: var(--space-2) var(--space-3);
  background: var(--bg);
  border-radius: var(--radius-sm);
  white-space: pre-wrap;
  word-break: break-word;
}

/* --------------------------------------------------------------------
   §4.12 — Busy / progress panel
   -------------------------------------------------------------------- */

.generate-hint {
  font-size: var(--text-sm);
  color: var(--ink-muted);
  margin-top: var(--space-3);
}

.progress-panel {
  margin-top: var(--space-3);
  margin-bottom: 0;
  flex-direction: column;
  align-items: stretch;
  gap: var(--space-2);
}

.progress-panel__head {
  display: flex;
  align-items: center;
  gap: var(--space-2);
}

.progress-panel__icon {
  flex-shrink: 0;
  color: var(--primary-dark);
}

.progress-panel__title {
  font-weight: var(--weight-bold);
  color: var(--primary-dark);
  margin: 0;
}

/* Indeterminate fill: a flat-colour segment sweeping the track. A
   diagonal-stripe fill is the obvious textbook rendering for this, but
   that pattern is conventionally built with a CSS gradient function,
   which is banned brand-wide (see docs/10-brand-tokens.md §8 / the
   logo's own wedge is a flat fill, never a gradient). This keeps the
   same "indeterminate, animated, honest" requirement from §4.12 with
   two flat colours and a position sweep instead. */
.progress-bar {
  position: relative;
  height: 8px;
  border-radius: var(--radius-pill);
  background: var(--primary-tint);
  overflow: hidden;
}

.progress-bar__fill {
  position: absolute;
  top: 0;
  bottom: 0;
  left: -40%;
  width: 40%;
  border-radius: var(--radius-pill);
  background: var(--primary);
  animation: progress-sweep 1.4s ease-in-out infinite;
}

@keyframes progress-sweep {
  0% {
    left: -40%;
  }
  100% {
    left: 100%;
  }
}

@media (prefers-reduced-motion: reduce) {
  .progress-bar__fill {
    animation: none;
    left: 0;
    width: 50%;
  }
}

.progress-panel__elapsed {
  font-size: var(--text-sm);
  font-weight: var(--weight-semibold);
  color: var(--ink);
  margin: 0;
}

.progress-panel__body {
  font-size: var(--text-sm);
  color: var(--primary-dark);
  margin: 0;
  line-height: var(--leading-normal);
}

/* --------------------------------------------------------------------
   Footer
   -------------------------------------------------------------------- */

.site-footer {
  max-width: var(--content-max-w-wide);
  margin: 0 auto;
  padding: var(--space-6);
  color: var(--ink-faint);
  font-size: var(--text-xs);
}

/* --------------------------------------------------------------------
   Utility
   -------------------------------------------------------------------- */

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

.spinner {
  display: inline-block;
  width: 14px;
  height: 14px;
  border-radius: 50%;
  border: 2px solid currentColor;
  border-top-color: transparent;
  animation: btn-spin 0.6s linear infinite;
}

@media (prefers-reduced-motion: reduce) {
  .spinner {
    animation: none;
    border-top-color: currentColor;
    opacity: 0.5;
  }
}
