/* ============================================================
   CONTROL MATE 2.0 — COMPONENT SYSTEM
   ------------------------------------------------------------
   ONE canonical treatment for every shared control in the app.

   WHY THIS FILE RESTYLES EXISTING CLASS NAMES RATHER THAN ADDING NEW ONES
   ----------------------------------------------------------------------
   A survey of every class attribute across assets/js/pages/*.js found the
   application already shares one vocabulary, used thousands of times:

       .card 456   .btn 435   .card-sub 386   .card-title 330
       .mini-note 247   .field 225   .card-head 188   .page-head 159
       .section-kicker 130   .num 107   .table-wrap 92   .card-tag 91
       .status-chip 86   .table 73   .meta-label 86 …

   That vocabulary IS the design system — it was simply never given one
   consistent visual definition, because .card and .btn are each defined
   in four different stylesheets from four different design generations
   (dashboard.css → tactical.css → v2/primitives.css → v2/pages/*.css),
   and whichever loads last wins. Defining them once, here, re-skins
   every screen at once. Inventing a parallel .cm-card set would have
   changed nothing until all 116 pages were hand-converted.

   New .cm-* classes appear below ONLY where the app has no existing
   equivalent at all (toolbars, skeletons, drawers, workflow stages).

   LOAD ORDER — THIS FILE MUST BE LAST
   -----------------------------------
   Every rule is scoped under body[data-theme="v2"] (forced on for all
   users at tactical.html:48). That gives specificity (0,2,1) against the
   bare .card / .btn selectors it supersedes, and the file is linked
   after tactical.css in every shell so it also wins on equal specificity.
   Moving it earlier in the <link> order silently reverts the redesign.

   COLOUR
   ------
   Nothing here contains a literal colour. Every value is a token from
   tokens.css, so the work surface can move dark → light by flipping
   tokens (see the note there) without touching a single rule below.
   ============================================================ */


/* ════════════════════════════════════════════════════════════
   1. PAGE FRAME — page head, section headers, grids
   ════════════════════════════════════════════════════════════ */

body[data-theme="v2"] .page {
  padding: var(--page-gutter);
  max-width: 1680px;          /* large monitors get used, but a 3440px
                                 display must not stretch a table to an
                                 unreadable 200-character line length */
  margin: 0 auto;
}

body[data-theme="v2"] .page-head {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: var(--space-4);
  flex-wrap: wrap;
  margin-bottom: var(--section-gap);
  padding-bottom: var(--space-4);
  border-bottom: 1px solid var(--border);
}

body[data-theme="v2"] .page-head h1,
body[data-theme="v2"] .page-head h2 {
  font: var(--font-page-title);
  letter-spacing: var(--tracking-tight);
  color: var(--ink);
  margin: 0;
}

body[data-theme="v2"] .page-head p,
body[data-theme="v2"] .page-sub {
  font: var(--font-body);
  color: var(--ink-3);
  margin: var(--space-1) 0 0;
  max-width: 70ch;            /* subtitles are prose — cap the measure */
}

/* Right-hand action cluster in a page head. */
body[data-theme="v2"] .page-head-actions {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  flex-wrap: wrap;
}

/* The kicker above a section title — small, tracked, quiet. */
body[data-theme="v2"] .section-kicker {
  font: var(--font-label);
  letter-spacing: var(--tracking-caps);
  text-transform: uppercase;
  color: var(--ink-3);
  margin-bottom: var(--space-2);
}

body[data-theme="v2"] .grid-2,
body[data-theme="v2"] .grid-3,
body[data-theme="v2"] .grid-4 {
  display: grid;
  gap: var(--space-4);
}
body[data-theme="v2"] .grid-2 { grid-template-columns: repeat(auto-fit, minmax(320px, 1fr)); }
body[data-theme="v2"] .grid-3 { grid-template-columns: repeat(auto-fit, minmax(260px, 1fr)); }
body[data-theme="v2"] .grid-4 { grid-template-columns: repeat(auto-fit, minmax(210px, 1fr)); }


/* ════════════════════════════════════════════════════════════
   2. CARDS — the most-used component in the app (456 uses)
   ════════════════════════════════════════════════════════════ */

body[data-theme="v2"] .card {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: var(--card-pad);
  box-shadow: var(--shadow-1);
  /* No transform/scale on hover. A card is a container, not a button —
     lifting it on hover was the single biggest source of the "template
     dashboard" feel the 2.0 brief rules out. */
}

body[data-theme="v2"] .card-head {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: var(--space-3);
  margin-bottom: var(--space-3);
}

body[data-theme="v2"] .card-title {
  font: var(--font-card-title);
  color: var(--ink);
  margin: 0;
}

body[data-theme="v2"] .card-sub {
  font: var(--font-meta);
  color: var(--ink-3);
  margin: var(--space-1) 0 0;
}

/* Small pill in a card head — a count, a source, a freshness stamp. */
body[data-theme="v2"] .card-tag {
  font: var(--font-badge);
  letter-spacing: var(--tracking-caps);
  text-transform: uppercase;
  color: var(--ink-3);
  background: var(--surface-2);
  border: 1px solid var(--border);
  border-radius: var(--radius-pill);
  padding: 3px 9px;
  white-space: nowrap;
}

/* Footnote under a card body. 247 uses — it carries most of the app's
   explanatory copy, so it has to be quiet but genuinely readable. */
body[data-theme="v2"] .mini-note {
  font: var(--font-helper);
  color: var(--ink-3);
  margin-top: var(--space-3);
}

body[data-theme="v2"] .meta        { font: var(--font-meta); color: var(--ink-3); }
body[data-theme="v2"] .meta-label  { font: var(--font-label); letter-spacing: var(--tracking-caps);
                                     text-transform: uppercase; color: var(--ink-3); }
body[data-theme="v2"] .meta-value  { font: var(--font-body); color: var(--ink); }

/* Any number the app prints. Tabular figures stop columns of rands
   from dancing as the digits change on refresh. */
body[data-theme="v2"] .num,
body[data-theme="v2"] .kpi-value,
body[data-theme="v2"] .metric-value {
  font-family: var(--font-mono);
  font-variant-numeric: tabular-nums;
  letter-spacing: -.01em;
}


/* ════════════════════════════════════════════════════════════
   3. KPI CARDS — the dashboard's headline numbers
   ════════════════════════════════════════════════════════════ */

body[data-theme="v2"] .kpi {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: var(--space-4);
  box-shadow: var(--shadow-1);
  display: flex;
  flex-direction: column;
  gap: var(--space-1);
  position: relative;
  overflow: hidden;
}

/* A 2px accent edge instead of a coloured card. Keeps the KPI row
   scannable without turning the dashboard into a colour chart. */
body[data-theme="v2"] .kpi::before {
  content: "";
  position: absolute;
  inset: 0 auto 0 0;
  width: 2px;
  background: var(--accent);
}
body[data-theme="v2"] .kpi.is-ok::before   { background: var(--ok); }
body[data-theme="v2"] .kpi.is-warn::before { background: var(--warn); }
body[data-theme="v2"] .kpi.is-risk::before { background: var(--risk); }

body[data-theme="v2"] .kpi-label,
body[data-theme="v2"] .metric-label {
  font: var(--font-label);
  letter-spacing: var(--tracking-caps);
  text-transform: uppercase;
  color: var(--ink-3);
}

body[data-theme="v2"] .kpi-value,
body[data-theme="v2"] .metric-value {
  font-size: 26px;
  font-weight: 600;
  line-height: 1.1;
  color: var(--ink);
}

body[data-theme="v2"] .kpi-sub { font: var(--font-meta); color: var(--ink-3); }


/* ════════════════════════════════════════════════════════════
   4. BUTTONS — 435 uses
   ════════════════════════════════════════════════════════════ */

body[data-theme="v2"] .btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-2);
  height: var(--control-h);
  padding: 0 14px;
  border-radius: var(--radius-sm);
  border: 1px solid var(--border-strong);
  background: var(--surface-2);
  color: var(--ink);
  font: var(--font-card-title);
  cursor: pointer;
  white-space: nowrap;
  transition: background var(--motion-control) var(--ease-out),
              border-color var(--motion-control) var(--ease-out),
              color var(--motion-control) var(--ease-out);
}

body[data-theme="v2"] .btn:hover:not(:disabled) {
  background: var(--surface-3);
  border-color: var(--accent-border);
}
body[data-theme="v2"] .btn:active:not(:disabled) { transform: translateY(.5px); }

/* Primary — exactly one per screen region. The single-accent system
   means an accented button is always THE action. */
body[data-theme="v2"] .btn-primary,
body[data-theme="v2"] .btn.primary {
  background: var(--accent);
  border-color: var(--accent);
  color: var(--accent-on);
}
body[data-theme="v2"] .btn-primary:hover:not(:disabled),
body[data-theme="v2"] .btn.primary:hover:not(:disabled) {
  background: var(--accent-hover);
  border-color: var(--accent-hover);
}

body[data-theme="v2"] .btn-secondary {
  background: transparent;
  border-color: var(--border-strong);
  color: var(--ink-2);
}
body[data-theme="v2"] .btn-secondary:hover:not(:disabled) {
  background: var(--surface-2);
  color: var(--ink);
}

body[data-theme="v2"] .btn-danger {
  background: var(--risk-wash);
  border-color: var(--risk-border);
  color: var(--risk);
}

body[data-theme="v2"] .btn-sm {
  height: var(--control-h-sm);
  padding: 0 10px;
  font: var(--font-badge);
}

body[data-theme="v2"] .ghost-btn {
  background: transparent;
  border: 1px solid var(--border);
  color: var(--ink-2);
  border-radius: var(--radius-sm);
  height: var(--control-h);
  padding: 0 12px;
  font: var(--font-card-title);
  cursor: pointer;
  transition: background var(--motion-control) var(--ease-out);
}
body[data-theme="v2"] .ghost-btn:hover { background: var(--surface-2); color: var(--ink); }

/* Disabled must read as disabled without relying on colour alone —
   the cursor and the reduced contrast both carry the message. */
body[data-theme="v2"] .btn:disabled,
body[data-theme="v2"] .ghost-btn:disabled {
  opacity: .5;
  cursor: not-allowed;
}

/* Square icon-only button. */
body[data-theme="v2"] .cm-icon-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: var(--control-h);
  height: var(--control-h);
  padding: 0;
  border-radius: var(--radius-sm);
  border: 1px solid var(--border);
  background: transparent;
  color: var(--ink-2);
  cursor: pointer;
  transition: background var(--motion-control) var(--ease-out);
}
body[data-theme="v2"] .cm-icon-btn:hover { background: var(--surface-2); color: var(--ink); }


/* ════════════════════════════════════════════════════════════
   5. FORMS — .field is used 225 times
   ════════════════════════════════════════════════════════════ */

body[data-theme="v2"] .field {
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
  margin-bottom: var(--space-4);
}

body[data-theme="v2"] .field > label,
body[data-theme="v2"] .lbl,
body[data-theme="v2"] .field-label {
  font: var(--font-label);
  color: var(--ink-2);
}

body[data-theme="v2"] .field input[type="text"],
body[data-theme="v2"] .field input[type="email"],
body[data-theme="v2"] .field input[type="password"],
body[data-theme="v2"] .field input[type="number"],
body[data-theme="v2"] .field input[type="tel"],
body[data-theme="v2"] .field input[type="date"],
body[data-theme="v2"] .field input[type="search"],
body[data-theme="v2"] .field select,
body[data-theme="v2"] .field textarea,
body[data-theme="v2"] .cm-input,
body[data-theme="v2"] .cm-select {
  width: 100%;
  min-height: var(--control-h);
  padding: 7px 11px;
  background: var(--surface-2);
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-sm);
  color: var(--ink);
  font: var(--font-body);
  transition: border-color var(--motion-control) var(--ease-out),
              background var(--motion-control) var(--ease-out);
}

body[data-theme="v2"] .field textarea { min-height: 88px; resize: vertical; line-height: 1.5; }

body[data-theme="v2"] .field input::placeholder,
body[data-theme="v2"] .field textarea::placeholder {
  color: var(--ink-3);
}

body[data-theme="v2"] .field input:hover:not(:disabled),
body[data-theme="v2"] .field select:hover:not(:disabled),
body[data-theme="v2"] .field textarea:hover:not(:disabled) {
  border-color: var(--accent-border);
}

body[data-theme="v2"] .field input:disabled,
body[data-theme="v2"] .field select:disabled,
body[data-theme="v2"] .field textarea:disabled {
  opacity: .55;
  cursor: not-allowed;
}

/* Invalid state carries a border AND a message — never colour alone. */
body[data-theme="v2"] .field.is-invalid input,
body[data-theme="v2"] .field.is-invalid select,
body[data-theme="v2"] .field.is-invalid textarea {
  border-color: var(--risk);
}
body[data-theme="v2"] .field-error {
  font: var(--font-error);
  color: var(--risk);
}
body[data-theme="v2"] .field-help,
body[data-theme="v2"] .login-field-hint {
  font: var(--font-helper);
  color: var(--ink-3);
}

body[data-theme="v2"] input[type="checkbox"],
body[data-theme="v2"] input[type="radio"] {
  width: 16px;
  height: 16px;
  accent-color: var(--accent);
  cursor: pointer;
}

/* ONE focus ring for every interactive element in the app. Keyboard
   focus is never removed — only restyled. */
body[data-theme="v2"] :is(button, a, input, select, textarea, [tabindex]):focus-visible {
  outline: var(--ring-width) solid var(--ring);
  outline-offset: var(--ring-offset);
  border-radius: var(--radius-xs);
}


/* ════════════════════════════════════════════════════════════
   6. TABLES — the highest-value surface in the product
   ════════════════════════════════════════════════════════════ */

/* The scroll container. Large operational tables scroll horizontally
   rather than collapsing into cards — column readability beats
   reflow when someone is reconciling 40 columns of debit data. */
body[data-theme="v2"] .table-wrap {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  overflow: auto;
  max-height: 72vh;           /* enables the sticky header below */
  box-shadow: var(--shadow-1);
}

body[data-theme="v2"] .table {
  width: 100%;
  border-collapse: separate;  /* separate, not collapse — collapsed
                                 borders drop out from under a sticky
                                 header in every engine */
  border-spacing: 0;
}

body[data-theme="v2"] .table thead th {
  position: sticky;
  top: 0;
  z-index: 2;
  background: var(--surface-2);
  color: var(--ink-3);
  font: var(--font-table-head);
  letter-spacing: var(--tracking-caps);
  text-transform: uppercase;
  text-align: left;
  padding: 10px 12px;
  border-bottom: 1px solid var(--border-strong);
  white-space: nowrap;
}

body[data-theme="v2"] .table tbody td {
  height: var(--row-h);
  padding: 8px 12px;
  font: var(--font-table-cell);
  color: var(--ink-2);
  border-bottom: 1px solid var(--border);
  vertical-align: middle;
}

/* First column is the row's identity — give it the ink weight. */
body[data-theme="v2"] .table tbody td:first-child { color: var(--ink); font-weight: 500; }

body[data-theme="v2"] .table tbody tr:last-child td { border-bottom: 0; }

body[data-theme="v2"] .table tbody tr:hover td { background: var(--surface-2); }

body[data-theme="v2"] .table tbody tr.is-selected td,
body[data-theme="v2"] .table tbody tr[aria-selected="true"] td {
  background: var(--accent-wash);
}
/* The accent marker goes on the FIRST cell only. Applying it to every td
   drew a bar down the left edge of each column, which reads as column
   dividers rather than as a selected row. */
body[data-theme="v2"] .table tbody tr.is-selected td:first-child,
body[data-theme="v2"] .table tbody tr[aria-selected="true"] td:first-child {
  box-shadow: inset 2px 0 0 var(--accent);
}

/* Numeric columns right-align and use tabular figures. */
body[data-theme="v2"] .table td.num,
body[data-theme="v2"] .table th.num,
body[data-theme="v2"] .table td.numeric,
body[data-theme="v2"] .table th.numeric {
  text-align: right;
  font-family: var(--font-mono);
  font-variant-numeric: tabular-nums;
}

body[data-theme="v2"] .table th.sortable { cursor: pointer; user-select: none; }
body[data-theme="v2"] .table th.sortable:hover { color: var(--ink); background: var(--surface-3); }

/* Toolbar above a table: search left, filters centre, actions right. */
body[data-theme="v2"] .cm-toolbar {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  flex-wrap: wrap;
  padding: var(--space-3) 0;
}
body[data-theme="v2"] .cm-toolbar-spacer { margin-left: auto; }

body[data-theme="v2"] .cm-search {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  height: var(--control-h);
  padding: 0 11px;
  min-width: 260px;
  background: var(--surface-2);
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-sm);
  color: var(--ink-3);
}
body[data-theme="v2"] .cm-search input {
  border: 0;
  background: transparent;
  color: var(--ink);
  font: var(--font-body);
  width: 100%;
  outline: none;
}

/* Pagination — rendered by lib/table-utils.js renderPagination(). */
body[data-theme="v2"] .pagination {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  padding: var(--space-3);
  flex-wrap: wrap;
}
body[data-theme="v2"] .pagination button {
  min-width: var(--control-h-sm);
  height: var(--control-h-sm);
  padding: 0 10px;
  background: var(--surface);
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-sm);
  color: var(--ink-2);
  font: var(--font-badge);
  cursor: pointer;
}
body[data-theme="v2"] .pagination button:hover:not(:disabled) { background: var(--surface-2); color: var(--ink); }
body[data-theme="v2"] .pagination button.active,
body[data-theme="v2"] .pagination button[aria-current="page"] {
  background: var(--accent);
  border-color: var(--accent);
  color: var(--accent-on);
}
body[data-theme="v2"] .pagination button:disabled { opacity: .45; cursor: not-allowed; }


/* ════════════════════════════════════════════════════════════
   7. STATUS — badges and chips. NEVER colour alone.
   ------------------------------------------------------------
   Every status carries its written label as text content, so the
   meaning survives greyscale, colour-vision deficiency and the
   printouts management still take into meetings. The colour is
   reinforcement, not the signal.
   ════════════════════════════════════════════════════════════ */

body[data-theme="v2"] .status-chip,
body[data-theme="v2"] .badge,
body[data-theme="v2"] .cm-status {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  padding: 2px 9px;
  border-radius: var(--radius-pill);
  font: var(--font-badge);
  letter-spacing: .02em;
  white-space: nowrap;
  background: var(--neutral-wash);
  border: 1px solid var(--neutral-border);
  color: var(--neutral);
}

/* Leading dot — a second, non-colour cue that this is a status. */
body[data-theme="v2"] .status-chip::before,
body[data-theme="v2"] .cm-status::before {
  content: "";
  width: 5px;
  height: 5px;
  border-radius: 50%;
  background: currentColor;
  flex: none;
}

body[data-theme="v2"] :is(.status-chip, .badge, .cm-status).ok,
body[data-theme="v2"] :is(.status-chip, .badge, .cm-status).success,
body[data-theme="v2"] :is(.status-chip, .badge, .cm-status).is-ok,
body[data-theme="v2"] :is(.status-chip, .badge, .cm-status).green {
  background: var(--ok-wash); border-color: var(--ok-border); color: var(--ok);
}
body[data-theme="v2"] :is(.status-chip, .badge, .cm-status).warn,
body[data-theme="v2"] :is(.status-chip, .badge, .cm-status).warning,
body[data-theme="v2"] :is(.status-chip, .badge, .cm-status).is-warn,
body[data-theme="v2"] :is(.status-chip, .badge, .cm-status).amber,
body[data-theme="v2"] :is(.status-chip, .badge, .cm-status).yellow {
  background: var(--warn-wash); border-color: var(--warn-border); color: var(--warn);
}
body[data-theme="v2"] :is(.status-chip, .badge, .cm-status).danger,
body[data-theme="v2"] :is(.status-chip, .badge, .cm-status).error,
body[data-theme="v2"] :is(.status-chip, .badge, .cm-status).is-risk,
body[data-theme="v2"] :is(.status-chip, .badge, .cm-status).red {
  background: var(--risk-wash); border-color: var(--risk-border); color: var(--risk);
}
body[data-theme="v2"] :is(.status-chip, .badge, .cm-status).info,
body[data-theme="v2"] :is(.status-chip, .badge, .cm-status).blue {
  background: var(--info-wash); border-color: var(--info-border); color: var(--info);
}

/* Role indicator — flat, uppercase, no dot. Distinct from a status so
   "admin_staff" is never mistaken for a workflow state. */
body[data-theme="v2"] .role-badge {
  display: inline-flex;
  align-items: center;
  padding: 2px 8px;
  border-radius: var(--radius-xs);
  background: var(--surface-3);
  border: 1px solid var(--border);
  color: var(--ink-3);
  font: var(--font-badge);
  letter-spacing: var(--tracking-caps);
  text-transform: uppercase;
}

body[data-theme="v2"] .avatar {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 32px;
  height: 32px;
  border-radius: 50%;
  background: var(--accent-wash);
  border: 1px solid var(--accent-border);
  color: var(--accent);
  font: var(--font-badge);
  flex: none;
}


/* ════════════════════════════════════════════════════════════
   8. WORKFLOW — stage indicators and timelines
   ------------------------------------------------------------
   The debt-removal pipeline is a chain of specialist desks, so
   "where is this file" is the question staff ask most. These render
   that as a horizontal stage rail and a vertical history.
   ════════════════════════════════════════════════════════════ */

body[data-theme="v2"] .cm-stages {
  display: flex;
  align-items: stretch;
  gap: 2px;
  flex-wrap: wrap;
}
body[data-theme="v2"] .cm-stage {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  padding: 7px 14px;
  background: var(--surface-2);
  border: 1px solid var(--border);
  color: var(--ink-3);
  font: var(--font-badge);
  letter-spacing: .02em;
}
body[data-theme="v2"] .cm-stage:first-child { border-radius: var(--radius-sm) 0 0 var(--radius-sm); }
body[data-theme="v2"] .cm-stage:last-child  { border-radius: 0 var(--radius-sm) var(--radius-sm) 0; }
/* Done and current are distinguished by weight and a glyph, not hue
   alone — the checkmark is added by the renderer as text content. */
body[data-theme="v2"] .cm-stage.is-done {
  background: var(--ok-wash); border-color: var(--ok-border); color: var(--ok);
}
body[data-theme="v2"] .cm-stage.is-current {
  background: var(--accent-wash); border-color: var(--accent); color: var(--accent);
  font-weight: 700;
}

body[data-theme="v2"] .cm-timeline { position: relative; padding-left: var(--space-5); }
body[data-theme="v2"] .cm-timeline::before {
  content: "";
  position: absolute;
  left: 5px; top: 4px; bottom: 4px;
  width: 1px;
  background: var(--border);
}
body[data-theme="v2"] .cm-timeline-item { position: relative; padding: 0 0 var(--space-4); }
body[data-theme="v2"] .cm-timeline-item::before {
  content: "";
  position: absolute;
  left: calc(var(--space-5) * -1 + 2px);
  top: 5px;
  width: 7px; height: 7px;
  border-radius: 50%;
  background: var(--surface);
  border: 1.5px solid var(--border-strong);
}
body[data-theme="v2"] .cm-timeline-item.is-current::before { border-color: var(--accent); background: var(--accent); }
body[data-theme="v2"] .cm-timeline-time { font: var(--font-meta); color: var(--ink-3); }
body[data-theme="v2"] .cm-timeline-body { font: var(--font-body); color: var(--ink-2); }


/* ════════════════════════════════════════════════════════════
   9. OVERLAYS — modal, drawer, confirm
   ════════════════════════════════════════════════════════════ */

body[data-theme="v2"] .modal-backdrop,
body[data-theme="v2"] .cm-backdrop {
  position: fixed;
  inset: 0;
  background: rgba(3, 7, 18, .62);
  z-index: 9000;
  animation: cm-fade var(--motion-overlay) var(--ease-out);
}

body[data-theme="v2"] .modal,
body[data-theme="v2"] .cm-modal {
  background: var(--surface);
  border: 1px solid var(--border-strong);
  border-radius: var(--radius);
  box-shadow: var(--shadow-2);
  max-width: min(680px, calc(100vw - 32px));
  max-height: calc(100vh - 64px);
  overflow: auto;
  animation: cm-rise var(--motion-overlay) var(--ease-out);
}

body[data-theme="v2"] .modal-head,
body[data-theme="v2"] .cm-modal-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-3);
  padding: var(--space-4);
  border-bottom: 1px solid var(--border);
  font: var(--font-section-title);
  color: var(--ink);
}
body[data-theme="v2"] .modal-body, body[data-theme="v2"] .cm-modal-body { padding: var(--space-4); }
body[data-theme="v2"] .modal-foot, body[data-theme="v2"] .cm-modal-foot {
  display: flex;
  justify-content: flex-end;
  gap: var(--space-2);
  padding: var(--space-4);
  border-top: 1px solid var(--border);
}

/* Side drawer — the approval drawer and any future side panel. */
body[data-theme="v2"] .cm-drawer {
  position: fixed;
  top: 0; right: 0; bottom: 0;
  width: min(460px, 100vw);
  background: var(--surface);
  border-left: 1px solid var(--border-strong);
  box-shadow: var(--shadow-2);
  z-index: 9100;
  display: flex;
  flex-direction: column;
  animation: cm-slide-in var(--motion-overlay) var(--ease-out);
}


/* ════════════════════════════════════════════════════════════
   10. FEEDBACK — alerts, toasts, empty, error, loading
   ════════════════════════════════════════════════════════════ */

body[data-theme="v2"] .alert,
body[data-theme="v2"] .cm-alert {
  display: flex;
  align-items: flex-start;
  gap: var(--space-3);
  padding: var(--space-3) var(--space-4);
  border-radius: var(--radius-sm);
  border: 1px solid var(--info-border);
  background: var(--info-wash);
  color: var(--info);
  font: var(--font-body);
}
body[data-theme="v2"] :is(.alert, .cm-alert).ok      { background: var(--ok-wash);   border-color: var(--ok-border);   color: var(--ok); }
body[data-theme="v2"] :is(.alert, .cm-alert).warn    { background: var(--warn-wash); border-color: var(--warn-border); color: var(--warn); }
body[data-theme="v2"] :is(.alert, .cm-alert).danger,
body[data-theme="v2"] :is(.alert, .cm-alert).error   { background: var(--risk-wash); border-color: var(--risk-border); color: var(--risk); }

body[data-theme="v2"] .toast {
  background: var(--surface);
  border: 1px solid var(--border-strong);
  border-left: 3px solid var(--accent);
  border-radius: var(--radius-sm);
  box-shadow: var(--shadow-2);
  color: var(--ink);
  font: var(--font-body);
  padding: var(--space-3) var(--space-4);
  animation: cm-slide-in var(--motion-panel) var(--ease-out);
}
body[data-theme="v2"] .toast.success { border-left-color: var(--ok); }
body[data-theme="v2"] .toast.warning { border-left-color: var(--warn); }
body[data-theme="v2"] .toast.error   { border-left-color: var(--risk); }

body[data-theme="v2"] .empty-state {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: var(--space-2);
  padding: var(--space-9) var(--space-5);
  text-align: center;
  color: var(--ink-3);
}
body[data-theme="v2"] .empty-title {
  font: var(--font-section-title);
  color: var(--ink-2);
}
body[data-theme="v2"] .empty-state p { font: var(--font-body); max-width: 46ch; margin: 0; }

/* Skeleton — shows the SHAPE of what is loading, so the layout does
   not jump when data lands. */
body[data-theme="v2"] .cm-skeleton {
  background: linear-gradient(90deg, var(--surface-2) 25%, var(--surface-3) 37%, var(--surface-2) 63%);
  background-size: 400% 100%;
  border-radius: var(--radius-xs);
  animation: cm-shimmer 1.4s ease-in-out infinite;
}
body[data-theme="v2"] .cm-skeleton-row { height: 12px; margin-bottom: var(--space-2); }
body[data-theme="v2"] .cm-skeleton-row:nth-child(3n) { width: 72%; }

body[data-theme="v2"] .cm-spinner {
  width: 16px; height: 16px;
  border: 2px solid var(--border-strong);
  border-top-color: var(--accent);
  border-radius: 50%;
  animation: cm-spin .7s linear infinite;
  display: inline-block;
}


/* ════════════════════════════════════════════════════════════
   11. TABS
   ════════════════════════════════════════════════════════════ */

body[data-theme="v2"] .tabs, body[data-theme="v2"] .cm-tabs {
  display: flex;
  gap: var(--space-1);
  border-bottom: 1px solid var(--border);
  margin-bottom: var(--section-gap);
  overflow-x: auto;
}
body[data-theme="v2"] :is(.tabs, .cm-tabs) > button,
body[data-theme="v2"] :is(.tabs, .cm-tabs) > .tab {
  padding: 9px 14px;
  background: transparent;
  border: 0;
  border-bottom: 2px solid transparent;
  color: var(--ink-3);
  font: var(--font-card-title);
  cursor: pointer;
  white-space: nowrap;
  transition: color var(--motion-control) var(--ease-out),
              border-color var(--motion-control) var(--ease-out);
}
body[data-theme="v2"] :is(.tabs, .cm-tabs) > button:hover { color: var(--ink); }
body[data-theme="v2"] :is(.tabs, .cm-tabs) > button.active,
body[data-theme="v2"] :is(.tabs, .cm-tabs) > button[aria-selected="true"] {
  color: var(--accent);
  border-bottom-color: var(--accent);
}


/* ════════════════════════════════════════════════════════════
   12. MOTION — every keyframe used above, in one place
   ------------------------------------------------------------
   READ THIS BEFORE WONDERING WHY MODALS DO NOT ANIMATE.

   no-blink.css loads after this file and sets
       *, *::before, *::after { animation-name: none !important }
   on the owner's standing directive of 2026-08-21 ("remove the entire
   blinking effect from the whole entire website immediately" — staff
   read this dashboard all day and independently-pulsing panels make
   genuinely urgent state impossible to spot). That kill-switch is
   deliberate and outranks the 2.0 brief's request for modal and drawer
   entry motion, because it is the newer instruction and it is about
   people's eyes rather than polish.

   So of the five keyframes below, exactly one runs today: cm-spin, and
   only because no-blink re-enables anything matching [class*="spinner"]
   — a frozen spinner reads as a hung page, so progress feedback is
   explicitly exempted. cm-fade, cm-rise and cm-slide-in are suppressed;
   cm-shimmer is suppressed too, leaving skeletons as a static gradient,
   which is the intended outcome.

   They are kept rather than deleted for two reasons: every TRANSITION in
   this file still runs (hover, focus, tabs, inputs, the rail — those use
   transition, not animation, and no-blink does not touch them), so the
   interface is not motionless; and if the directive is ever relaxed the
   entry motion is already specified and tuned. Deleting them would mean
   re-deriving the timings later from nothing.
   ════════════════════════════════════════════════════════════ */

@keyframes cm-fade     { from { opacity: 0 } to { opacity: 1 } }
@keyframes cm-rise     { from { opacity: 0; transform: translateY(8px) } to { opacity: 1; transform: none } }
@keyframes cm-slide-in { from { opacity: 0; transform: translateX(12px) } to { opacity: 1; transform: none } }
@keyframes cm-spin     { to { transform: rotate(360deg) } }
@keyframes cm-shimmer  { 0% { background-position: 100% 50% } 100% { background-position: 0 50% } }

@media (prefers-reduced-motion: reduce) {
  body[data-theme="v2"] :is(.modal, .cm-modal, .cm-drawer, .toast, .modal-backdrop, .cm-backdrop) {
    animation: none;
  }
  body[data-theme="v2"] .cm-skeleton { animation: none; }
  body[data-theme="v2"] .cm-spinner  { animation-duration: 2s; }
}


/* ════════════════════════════════════════════════════════════
   13. RESPONSIVE
   ------------------------------------------------------------
   Operational tables keep their columns and scroll sideways at every
   width. Collapsing a 40-column reconciliation table into stacked
   cards destroys the comparison the table exists to support.
   ════════════════════════════════════════════════════════════ */

@media (max-width: 1280px) {
  body[data-theme="v2"] { --page-gutter: 20px; --card-pad: 14px; }
}

@media (max-width: 900px) {
  body[data-theme="v2"] { --page-gutter: 14px; --section-gap: 16px; }
  body[data-theme="v2"] .page-head { flex-direction: column; align-items: stretch; }
  body[data-theme="v2"] .page-head-actions > .btn { flex: 1 1 auto; }
  body[data-theme="v2"] .cm-search { min-width: 0; width: 100%; }
  body[data-theme="v2"] .cm-toolbar > * { flex: 1 1 auto; }
  body[data-theme="v2"] .cm-toolbar-spacer { margin-left: 0; }
  /* Touch targets stay at least 40px on small screens. */
  body[data-theme="v2"] { --control-h: 40px; --control-h-sm: 36px; }
}


/* ════════════════════════════════════════════════════════════
   14. THE COMMAND SHELL — sidebar, top bar, work canvas
   ------------------------------------------------------------
   This is the half of ControlMate 2.0 staff see first, and it only
   became reachable once the horizon.html auto-redirect was retired
   (tactical.html) — before that, every signed-in user was sent to the
   Event Horizon console and never saw a sidebar at all.

   SPECIFICITY NOTE — why every selector is doubled below.
   tactical.css styles the chrome as
       body[data-skin="tactical"][data-theme="v2"] .sidebar   → (0,3,1)
   which outranks a plain body[data-theme="v2"] .sidebar → (0,2,1). Under
   that skin the sidebar computes to 380px wide with a fully transparent
   background, which is why it read as "no sidebar" against the dark page
   even while rendering all 11 nav groups and 114 role-filtered items.
   Each rule is therefore written twice: once unqualified for the shells
   that set no skin (index.html, classic.html), and once with
   [data-skin="tactical"] to tie tactical.css and win on load order,
   since components.css is linked after it.

   The atmosphere layers are neutralised rather than deleted. The markup
   stays in the shells and the tactical skin still exists for anyone who
   loads it deliberately; 2.0 simply does not paint plasma, scanlines and
   HUD brackets behind an operations console. The brief rules out neon
   and heavy glass, and a reconciliation screen is read for eight hours
   a day.
   ════════════════════════════════════════════════════════════ */

/* ── Work canvas ─────────────────────────────────────────────── */
body[data-theme="v2"],
body[data-theme="v2"][data-skin="tactical"] {
  background: var(--canvas);
  color: var(--ink-2);
}

body[data-theme="v2"] .app-shell,
body[data-theme="v2"][data-entry="app"] .app-shell,
body[data-theme="v2"][data-skin="tactical"] .app-shell {
  display: grid;
  grid-template-columns: var(--rail-w) minmax(0, 1fr);
  min-height: 100vh;
  align-items: stretch;
}

body[data-theme="v2"] .main,
body[data-theme="v2"][data-entry="app"] .main,
body[data-theme="v2"][data-skin="tactical"] .main {
  background: var(--canvas);
  min-width: 0;                /* lets wide tables scroll instead of
                                  stretching the grid column */
}

/* Ambient decoration — off. Kept in the DOM, simply not painted. */
body[data-theme="v2"] :is(.tac-atmo, .tac-gridfield, .tac-scan, .tac-plasma,
                          .cc-worldmap, .cc-orb-field, .cc-frame, .cc-status-pill),
body[data-theme="v2"][data-skin="tactical"] :is(.tac-atmo, .tac-gridfield, .tac-scan,
                          .tac-plasma, .cc-worldmap, .cc-orb-field, .cc-frame, .cc-status-pill) {
  display: none !important;
}

/* ── Sidebar ─────────────────────────────────────────────────── */
body[data-theme="v2"] .sidebar,
body[data-theme="v2"][data-entry="app"] .sidebar,
body[data-theme="v2"][data-skin="tactical"] .sidebar {
  width: var(--rail-w);
  max-width: var(--rail-w);
  flex: none;
  display: flex;
  flex-direction: column;
  /* Solid colour FIRST, gradient second. If the gradient ever fails to
     rasterise the rail still paints navy rather than falling through to
     the page canvas — which is exactly the failure mode that made the
     rail look absent during verification. */
  background-color: var(--shell-bg);
  background-image: linear-gradient(180deg, var(--shell-bg) 0%, var(--shell-bg-2) 100%);
  border-right: 1px solid var(--shell-line);
  border-radius: 0;
  box-shadow: none;
  backdrop-filter: none;
  padding: 0;
  /* Sticky + align-self:start rather than height:100vh + overflow:hidden.
     The grid stretches a child to the full row height by default, and a
     stretched sticky child with its own clipping context does not repaint
     reliably — the rail computed correctly (240px, gradient applied,
     visibility visible) while painting nothing at all. Pinning it to the
     start of the row and capping it at the viewport keeps the rail fixed
     while the work surface scrolls, with #sidebar-nav as the scroller. */
  align-self: start;
  position: sticky;
  top: 0;
  max-height: 100vh;
}

body[data-theme="v2"] .brand-block,
body[data-theme="v2"][data-entry="app"] .brand-block,
body[data-theme="v2"][data-skin="tactical"] .brand-block {
  padding: 16px 16px 14px;
  border-bottom: 1px solid var(--shell-line);
  background: none;
}
body[data-theme="v2"] .brand-block-top { display: flex; align-items: center; gap: 10px; }
body[data-theme="v2"] .brand-mark img { width: 28px; height: 28px; border-radius: var(--radius-sm); }
body[data-theme="v2"] .brand-title,
body[data-theme="v2"][data-entry="app"] .brand-title,
body[data-theme="v2"][data-skin="tactical"] .brand-title {
  font-size: 13px;
  font-weight: 600;
  line-height: 1.25;
  color: var(--shell-text);
  letter-spacing: var(--tracking-tight);
  text-shadow: none;
}
body[data-theme="v2"] .brand-short,
body[data-theme="v2"][data-entry="app"] .brand-short,
body[data-theme="v2"][data-skin="tactical"] .brand-short {
  font-size: 12px;
  color: var(--shell-text-muted);
  letter-spacing: .04em;
  text-transform: none;
}

/* "Control Areas" caption above the nav list. */
body[data-theme="v2"] .sidebar .nav-label,
body[data-theme="v2"][data-entry="app"] .sidebar .nav-label,
body[data-theme="v2"][data-skin="tactical"] .sidebar .nav-label {
  font: var(--font-label);
  letter-spacing: var(--tracking-caps);
  text-transform: uppercase;
  color: var(--shell-text-dim);
  padding: 14px 16px 4px;
}

body[data-theme="v2"] #sidebar-nav,
body[data-theme="v2"][data-entry="app"] #sidebar-nav,
body[data-theme="v2"][data-skin="tactical"] #sidebar-nav {
  flex: 1;
  overflow-y: auto;
  overflow-x: hidden;
  padding: 0 8px 12px;
  scrollbar-width: thin;
  scrollbar-color: var(--shell-line) transparent;
  /* GRID BLOWOUT GUARD.
     dashboard.css makes #sidebar-nav a grid. A grid track sized `auto`
     takes its minimum from the content's min-content width, so the
     column grew to whatever the widest nav row wanted — 251px inside a
     240px rail — and overflow-x:hidden then sliced the last 19px off.
     That is why every badge read "LIVI", "SELF-CHECI", "SAVE SAL".
     minmax(0, 1fr) drops the track minimum to zero, so the column is
     the rail's content box and long rows ellipsis instead of spilling.
     The same guard on the group wrappers keeps a long label from
     re-inflating the row from the inside. */
  grid-template-columns: minmax(0, 1fr);
}
body[data-theme="v2"] #sidebar-nav .nav-group,
body[data-theme="v2"] #sidebar-nav .nav-group-items,
body[data-theme="v2"] #sidebar-nav .nav-item { min-width: 0; max-width: 100%; }
body[data-theme="v2"] #sidebar-nav::-webkit-scrollbar { width: 8px; }
body[data-theme="v2"] #sidebar-nav::-webkit-scrollbar-thumb {
  background: var(--shell-line); border-radius: var(--radius-pill);
}

/* Group header — the section label, clickable to collapse. */
body[data-theme="v2"] .nav-group-header,
body[data-theme="v2"][data-entry="app"] .nav-group-header,
body[data-theme="v2"][data-skin="tactical"] .nav-group-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  width: 100%;
  gap: 8px;
  padding: 9px 8px;
  margin-top: 4px;
  background: transparent;
  border: 0;
  border-radius: var(--radius-sm);
  color: var(--shell-text-muted);
  font: var(--font-label);
  letter-spacing: var(--tracking-caps);
  text-transform: uppercase;
  cursor: pointer;
  transition: color var(--motion-control) var(--ease-out),
              background var(--motion-control) var(--ease-out);
}
body[data-theme="v2"] .nav-group-header:hover { background: var(--shell-bg-3); color: var(--shell-text); }
body[data-theme="v2"] .nav-group-title { display: flex; align-items: center; gap: 9px; min-width: 0; }
body[data-theme="v2"] .nav-group-icon { display: inline-flex; opacity: .85; flex: none; }
body[data-theme="v2"] .nav-group-chevron { font-size: 12px; opacity: .6; flex: none; }

/* GOVERNANCE SEPARATION — the brief asks for visual separation between
   operational modules and management/governance. Governance & System is
   the last group and holds Access Control, Audit Log, Roles &
   Permissions and System Settings, so it is set below a rule rather
   than sitting in the same run as the day-to-day desks. */
body[data-theme="v2"] .nav-group[data-group="governance-system"],
body[data-theme="v2"][data-entry="app"] .nav-group[data-group="governance-system"],
body[data-theme="v2"][data-skin="tactical"] .nav-group[data-group="governance-system"] {
  margin-top: 12px;
  padding-top: 10px;
  border-top: 1px solid var(--shell-line);
}

/* Nav item. */
body[data-theme="v2"] #sidebar-nav .nav-item,
body[data-theme="v2"][data-entry="app"] #sidebar-nav .nav-item,
body[data-theme="v2"][data-skin="tactical"] #sidebar-nav .nav-item {
  display: flex;
  align-items: center;
  gap: 9px;
  width: 100%;
  padding: 7px 8px 7px 10px;
  margin: 1px 0;
  background: transparent;
  border: 0;
  border-radius: var(--radius-sm);
  color: var(--shell-text-muted);
  font-size: 12.5px;
  font-weight: 500;
  text-align: left;
  cursor: pointer;
  position: relative;
  transition: background var(--motion-control) var(--ease-out),
              color var(--motion-control) var(--ease-out);
}
body[data-theme="v2"] #sidebar-nav .nav-item:hover,
body[data-theme="v2"][data-entry="app"] #sidebar-nav .nav-item:hover,
body[data-theme="v2"][data-skin="tactical"] #sidebar-nav .nav-item:hover {
  background: var(--shell-bg-3);
  color: var(--shell-text);
}

/* ACTIVE — filled, with a left marker. Two cues, not one: staff scan
   this rail dozens of times an hour. */
body[data-theme="v2"] #sidebar-nav .nav-item.active,
body[data-theme="v2"][data-entry="app"] #sidebar-nav .nav-item.active,
body[data-theme="v2"][data-skin="tactical"] #sidebar-nav .nav-item.active {
  background: var(--accent);
  color: #fff;
  font-weight: 600;
  box-shadow: none;
}
body[data-theme="v2"] #sidebar-nav .nav-item.active::before {
  content: "";
  position: absolute;
  left: 0; top: 6px; bottom: 6px;
  width: 3px;
  border-radius: 0 2px 2px 0;
  background: #fff;
  opacity: .9;
}
body[data-theme="v2"] #sidebar-nav .nav-item .nav-icon { opacity: .55; flex: none; display: inline-flex; }
body[data-theme="v2"] #sidebar-nav .nav-item.active .nav-icon { opacity: 1; }
body[data-theme="v2"] #sidebar-nav .nav-label,
body[data-theme="v2"] #sidebar-nav .nav-item .nav-label {
  flex: 1;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  padding: 0;
  font: inherit;
  color: inherit;
  text-transform: none;
  letter-spacing: 0;
}

/* Badges in the rail stay quiet — they label, they do not shout. */
body[data-theme="v2"] #sidebar-nav .nav-badge,
body[data-theme="v2"][data-entry="app"] #sidebar-nav .nav-badge,
body[data-theme="v2"][data-skin="tactical"] #sidebar-nav .nav-badge {
  flex: none;
  padding: 1px 6px;
  border-radius: var(--radius-pill);
  background: rgba(255, 255, 255, .08);
  border: 1px solid var(--shell-line);
  color: var(--shell-text-dim);
  font-size: 12px;
  font-weight: 600;
  letter-spacing: .03em;
  text-transform: uppercase;
}
body[data-theme="v2"] #sidebar-nav .nav-item.active .nav-badge {
  background: rgba(255, 255, 255, .22);
  border-color: transparent;
  color: #fff;
}

/* Sidebar footer — who is signed in, their scope, and the way out. */
body[data-theme="v2"] .sidebar-footer,
body[data-theme="v2"][data-entry="app"] .sidebar-footer,
body[data-theme="v2"][data-skin="tactical"] .sidebar-footer {
  border-top: 1px solid var(--shell-line);
  padding: 12px;
  background: rgba(0, 0, 0, .18);
}
body[data-theme="v2"] .sidebar-user { display: flex; align-items: center; gap: 10px; margin-bottom: 10px; }
body[data-theme="v2"] .sidebar-user-name,
body[data-theme="v2"][data-entry="app"] .sidebar-user-name,
body[data-theme="v2"][data-skin="tactical"] .sidebar-user-name {
  font-size: 12.5px; font-weight: 600; color: var(--shell-text);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
body[data-theme="v2"] .sidebar-user-role,
body[data-theme="v2"][data-entry="app"] .sidebar-user-role,
body[data-theme="v2"][data-skin="tactical"] .sidebar-user-role {
  font-size: 12px; color: var(--shell-text-muted);
  letter-spacing: var(--tracking-caps); text-transform: uppercase;
}
body[data-theme="v2"] .sidebar .avatar { background: var(--accent); border-color: transparent; color: #fff; }
body[data-theme="v2"] #sidebar-scope,
body[data-theme="v2"][data-entry="app"] #sidebar-scope,
body[data-theme="v2"][data-skin="tactical"] #sidebar-scope {
  font-size: 12px; color: var(--shell-text-dim); margin-bottom: 8px; line-height: 1.4;
}
body[data-theme="v2"] #logout-btn,
body[data-theme="v2"][data-entry="app"] #logout-btn,
body[data-theme="v2"][data-skin="tactical"] #logout-btn {
  width: 100%;
  height: 32px;
  background: transparent;
  border: 1px solid var(--shell-line);
  border-radius: var(--radius-sm);
  color: var(--shell-text-muted);
  font-size: 12px;
  font-weight: 600;
  cursor: pointer;
  transition: background var(--motion-control) var(--ease-out);
}
body[data-theme="v2"] #logout-btn:hover { background: var(--shell-bg-3); color: var(--shell-text); }

/* ── Top command bar ─────────────────────────────────────────── */
body[data-theme="v2"] .topbar,
body[data-theme="v2"][data-entry="app"] .topbar,
body[data-theme="v2"][data-skin="tactical"] .topbar {
  display: flex;
  align-items: center;
  gap: var(--space-4);
  min-height: var(--topbar-h);
  padding: 10px var(--page-gutter);
  background: var(--shell-bg-2);
  border: 0;
  border-bottom: 1px solid var(--shell-line);
  border-radius: 0;
  box-shadow: none;
  backdrop-filter: none;
  position: sticky;
  top: 0;
  z-index: 50;
  flex-wrap: wrap;
}
body[data-theme="v2"] .topbar-title-group { min-width: 0; flex: 0 1 auto; }
body[data-theme="v2"] .breadcrumbs,
body[data-theme="v2"][data-entry="app"] .breadcrumbs,
body[data-theme="v2"][data-skin="tactical"] .breadcrumbs {
  font-size: 12px; color: var(--shell-text-dim);
  letter-spacing: var(--tracking-caps); text-transform: uppercase; margin-bottom: 1px;
}
body[data-theme="v2"] .breadcrumb-link { color: var(--shell-text-dim); text-decoration: none; }
body[data-theme="v2"] .breadcrumb-link:hover { color: var(--shell-text); }
body[data-theme="v2"] .topbar-title,
body[data-theme="v2"][data-entry="app"] .topbar-title,
body[data-theme="v2"][data-skin="tactical"] .topbar-title {
  font-size: 16px;
  font-weight: 600;
  color: var(--shell-text);
  letter-spacing: var(--tracking-tight);
  text-transform: none;
  text-shadow: none;
}
/* tactical.css hangs a glowing bracket off the title — 2.0 does not. */
body[data-theme="v2"][data-skin="tactical"] .topbar-title::before { content: none; }
body[data-theme="v2"] .topbar-sub,
body[data-theme="v2"][data-entry="app"] .topbar-sub,
body[data-theme="v2"][data-skin="tactical"] .topbar-sub {
  font-size: 12px; color: var(--shell-text-muted); letter-spacing: 0; text-transform: none;
}

body[data-theme="v2"] .topbar-search,
body[data-theme="v2"][data-entry="app"] .topbar-search,
body[data-theme="v2"][data-skin="tactical"] .topbar-search {
  display: flex;
  align-items: center;
  gap: 8px;
  flex: 1 1 280px;
  max-width: 480px;
  height: 34px;
  padding: 0 11px;
  background: rgba(255, 255, 255, .05);
  border: 1px solid var(--shell-line);
  border-radius: var(--radius-sm);
  color: var(--shell-text-muted);
  box-shadow: none;
}
body[data-theme="v2"] .topbar-search:focus-within,
body[data-theme="v2"][data-entry="app"] .topbar-search:focus-within,
body[data-theme="v2"][data-skin="tactical"] .topbar-search:focus-within {
  border-color: var(--accent);
  background: rgba(255, 255, 255, .08);
  box-shadow: none;
}
body[data-theme="v2"] .topbar-search input {
  flex: 1; min-width: 0; border: 0; background: transparent;
  color: var(--shell-text); font-size: 12.5px; outline: none;
}
body[data-theme="v2"] .topbar-search input::placeholder { color: var(--shell-text-dim); }
body[data-theme="v2"] .keyboard-hint {
  font-size: 12px; color: var(--shell-text-dim);
  border: 1px solid var(--shell-line); border-radius: var(--radius-xs); padding: 1px 5px;
}

body[data-theme="v2"] .topbar-actions { display: flex; align-items: center; gap: 8px; margin-left: auto; flex-wrap: wrap; }

body[data-theme="v2"] .topbar-icon,
body[data-theme="v2"] .topbar-home-btn,
body[data-theme="v2"][data-skin="tactical"] .topbar-icon,
body[data-theme="v2"][data-skin="tactical"] .topbar-home-btn {
  display: inline-flex; align-items: center; justify-content: center;
  height: 32px;
  background: transparent;
  border: 1px solid var(--shell-line);
  border-radius: var(--radius-sm);
  color: var(--shell-text-muted);
  font-size: 12px; font-weight: 600;
  cursor: pointer; position: relative;
  box-shadow: none;
  transition: background var(--motion-control) var(--ease-out),
              color var(--motion-control) var(--ease-out);
}
body[data-theme="v2"] .topbar-icon { width: 32px; }
body[data-theme="v2"] .topbar-home-btn { padding: 0 12px; }
body[data-theme="v2"] .topbar-icon:hover,
body[data-theme="v2"] .topbar-home-btn:hover,
body[data-theme="v2"][data-skin="tactical"] .topbar-icon:hover,
body[data-theme="v2"][data-skin="tactical"] .topbar-home-btn:hover {
  background: var(--shell-bg-3); color: var(--shell-text); border-color: var(--border-strong);
}
body[data-theme="v2"] #notification-count {
  position: absolute; top: -5px; right: -5px;
  min-width: 15px; height: 15px; padding: 0 3px;
  border-radius: var(--radius-pill);
  background: var(--risk); color: #fff;
  font-size: 12px; font-weight: 700; line-height: 15px; text-align: center;
}
body[data-theme="v2"] .topbar-pill,
body[data-theme="v2"][data-entry="app"] .topbar-pill,
body[data-theme="v2"][data-skin="tactical"] .topbar-pill {
  padding: 5px 10px; border-radius: var(--radius-sm);
  background: rgba(255, 255, 255, .05); border: 1px solid var(--shell-line);
  color: var(--shell-text-muted); font-size: 12px; font-weight: 600;
  letter-spacing: .03em; box-shadow: none;
}
body[data-theme="v2"] .appearance-switch {
  display: inline-flex; padding: 2px; gap: 2px;
  background: rgba(255, 255, 255, .05);
  border: 1px solid var(--shell-line); border-radius: var(--radius-sm);
}
body[data-theme="v2"] .appr-opt {
  display: inline-flex; align-items: center; justify-content: center;
  width: 26px; height: 26px; padding: 0;
  background: transparent; border: 0; border-radius: var(--radius-xs);
  color: var(--shell-text-dim); cursor: pointer;
}
body[data-theme="v2"] .appr-opt.is-active { background: var(--accent); color: #fff; }

body[data-theme="v2"] .topbar-profile {
  display: flex; align-items: center; gap: 9px;
  padding: 4px 10px 4px 4px;
  border: 1px solid var(--shell-line); border-radius: var(--radius-pill);
  cursor: pointer;
}
body[data-theme="v2"] .topbar-profile:hover { background: var(--shell-bg-3); }
body[data-theme="v2"] .topbar-profile .avatar { width: 26px; height: 26px; background: var(--accent); border-color: transparent; color: #fff; }
body[data-theme="v2"] .topbar-profile-name { font-size: 12px; font-weight: 600; color: var(--shell-text); }
body[data-theme="v2"] .topbar-profile-role {
  font-size: 12px; color: var(--shell-text-muted);
  letter-spacing: var(--tracking-caps); text-transform: uppercase;
}

/* Horizontal nav mode — same tokens, laid out along the top. */
body[data-theme="v2"] .topbar-nav,
body[data-theme="v2"][data-entry="app"] .topbar-nav,
body[data-theme="v2"][data-skin="tactical"] .topbar-nav {
  background: var(--shell-bg-2);
  border-bottom: 1px solid var(--shell-line);
  padding: 0 var(--page-gutter);
}
body[data-theme="v2"] .tnav-trigger {
  background: transparent; border: 0; color: var(--shell-text-muted);
  font-size: 12.5px; font-weight: 600; padding: 10px 12px; cursor: pointer;
}
body[data-theme="v2"] .tnav-group.is-active .tnav-trigger { color: var(--accent); box-shadow: inset 0 -2px 0 var(--accent); }
body[data-theme="v2"] .tnav-menu {
  background: var(--shell-bg-2);
  border: 1px solid var(--shell-line);
  border-radius: var(--radius-sm);
  box-shadow: var(--shadow-2);
  padding: 6px;
}

/* Embed mode still hides the chrome — the Horizon console and any other
   iframe host depends on it, and retiring the auto-redirect did not
   remove that path. */
body[data-theme="v2"][data-embed="1"] .sidebar,
body[data-theme="v2"][data-embed="1"] .topbar,
body[data-theme="v2"][data-embed="1"] #topbar-nav { display: none !important; }
body[data-theme="v2"][data-embed="1"] .app-shell { grid-template-columns: minmax(0, 1fr); }

/* ── Shell responsiveness ────────────────────────────────────── */
/* NARROW WIDTHS — the rail narrows, it does NOT go icon-only.
   ------------------------------------------------------------
   This used to collapse to a 64px icon rail below 1100px, hiding every
   label. That was wrong twice over, and it is what a signed-in user
   actually hit on live ("there is no tabs or texts that's visible in the
   side panel").

   First, 1100px is not narrow — a windowed browser on a 1280 or 1366
   laptop lands under it routinely, so the collapse fired for ordinary
   desktop users rather than for tablets.

   Second, and worse: an icon-only rail needs meaningful icons, and this
   app does not have them. chrome.js renders every nav item with
   NAV_ITEM_DOT — a 12px dot, identical for all 114 items. Collapsing to
   icons therefore produced a strip of indistinguishable dots with no
   text: not a compact navigation, just an unusable one.

   So the rail keeps its labels at every width where it is present at
   all. It narrows to 200px, the labels ellipsis, and the nav list
   scrolls. Below 760px the existing mobile drawer behaviour takes over. */
@media (max-width: 1180px) {
  body[data-theme="v2"] .app-shell,
  body[data-theme="v2"][data-entry="app"] .app-shell,
  body[data-theme="v2"][data-skin="tactical"] .app-shell { grid-template-columns: 200px minmax(0, 1fr); }
  body[data-theme="v2"] .sidebar,
  body[data-theme="v2"][data-entry="app"] .sidebar,
  body[data-theme="v2"][data-skin="tactical"] .sidebar { width: 200px; max-width: 200px; }
  body[data-theme="v2"] #sidebar-nav .nav-item { font-size: 12px; padding: 6px 6px 6px 9px; }
  body[data-theme="v2"] .brand-title { font-size: 12px; }
  /* Badges are the one thing worth dropping — they are decoration next to
     the label, and they are what pushes a long item name into the gutter. */
  body[data-theme="v2"] #sidebar-nav .nav-badge { display: none; }
  /* Group headings are set in wide-tracked caps, which is the first thing
     to wrap in a 200px rail. Tighten the tracking here so "SALES &
     PAYMENTS" and its siblings stay on one line. */
  body[data-theme="v2"] .nav-group-header,
  body[data-theme="v2"][data-entry="app"] .nav-group-header,
  body[data-theme="v2"][data-skin="tactical"] .nav-group-header {
    padding: 8px 6px;
    letter-spacing: .01em;
    font-size: 12px;
  }
  body[data-theme="v2"] .nav-group-title { min-width: 0; }
}

@media (max-width: 760px) {
  body[data-theme="v2"] .topbar { padding: 8px 12px; }
  body[data-theme="v2"] .topbar-search { order: 3; flex-basis: 100%; max-width: none; }
  body[data-theme="v2"] .topbar-actions { margin-left: auto; }
}


/* ════════════════════════════════════════════════════════════
   15. THE DASHBOARD — bringing the v3 landing page into 2.0
   ------------------------------------------------------------
   The `dashboard` route resolves to pages/dashboard-v3.js, which
   injects its own stylesheet at runtime (const V3_CSS → a <style>
   element created in the renderer). That sheet is a separate design
   generation: Orbitron and Rajdhani display faces, cyan #00d4ff glow,
   hot-magenta and pulse-red accents, and an animated atmosphere layer.
   Against the 2.0 shell it read as two different products bolted
   together, and it is squarely what the brief rules out — neon,
   glow, sci-fi display type.

   RESTYLED WITHOUT TOUCHING THE RENDERER. V3_CSS uses bare class
   selectors (.v3-kpi-mega is 0,1,0), so body[data-theme="v2"] .v3-…
   at 0,2,1 wins on specificity no matter that the injected <style>
   lands in the head later than this file. Nothing in dashboard-v3.js
   changes: same markup, same data, same RPCs, same refresh loop —
   only the paint.

   v3-dashboard.css additionally redefines --line, --blue, --green,
   --amber, --red and --gold on :root. Those stay defined for anything
   still reading them; the 2.0 tokens sit on body[data-theme="v2"],
   which is more specific, so the 2.0 layer is unaffected.
   ════════════════════════════════════════════════════════════ */

/* Layout + atmosphere */
body[data-theme="v2"] .v3-page,
body[data-theme="v2"] .v3-wrap {
  background: transparent;
  padding: 0;
  max-width: none;
  font-family: var(--font-sans);
}
body[data-theme="v2"] .v3-atmos,
body[data-theme="v2"] .v3-grid-bg,
body[data-theme="v2"] .v3-scan { display: none !important; }

/* LAUNCHER TILES — the welcome-screen module grid.
   dashboard-v3.js injects `.v3-launch-label{flex:1}` with no min-width, so a
   long module name shoves the badge past the tile edge ("SAVE SALE" rendered
   as "SAVE SAL"). Letting the label shrink instead just moved the damage —
   the text kept painting over the badge. So the tile wraps: label and badge
   share a row when they fit, and the badge drops to its own line when they
   do not. Nothing is truncated and nothing overlaps. Styled here rather than
   in the renderer so no page logic is touched. */
body[data-theme="v2"] .v3-launch-card { flex-wrap: wrap; row-gap: 4px; }
body[data-theme="v2"] .v3-launch-label { overflow-wrap: break-word; }
body[data-theme="v2"] .v3-launch-badge { flex: none; white-space: nowrap; }

/* Section headers */
body[data-theme="v2"] .v3-section {
  background: transparent;
  border: 0;
  padding: 0;
  margin-bottom: var(--section-gap);
  box-shadow: none;
}
body[data-theme="v2"] .v3-section-header {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  flex-wrap: wrap;
  padding: 0 0 var(--space-3);
  margin-bottom: var(--space-4);
  border-bottom: 1px solid var(--border);
  background: none;
  font: var(--font-section-title);
  letter-spacing: 0;
  text-transform: none;
  color: var(--ink);
  text-shadow: none;
}

/* KPI tiles — the headline row */
body[data-theme="v2"] .v3-kpi-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
  gap: var(--space-4);
}
body[data-theme="v2"] .v3-kpi-mega {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  box-shadow: var(--shadow-1);
  padding: var(--space-4);
  position: relative;
  overflow: hidden;
  backdrop-filter: none;
}
/* The v3 tile hangs a 2px glowing bar off ::before. Keep the bar as a
   quiet accent edge; drop the glow. */
body[data-theme="v2"] .v3-kpi-mega::before {
  background: var(--accent) !important;
  box-shadow: none !important;
  opacity: 1;
}
body[data-theme="v2"] .v3-kpi-mega .lbl,
body[data-theme="v2"] .v3-ops-card .ops-title,
body[data-theme="v2"] .br-lbl {
  font: var(--font-label);
  font-family: var(--font-sans);
  letter-spacing: var(--tracking-caps);
  text-transform: uppercase;
  color: var(--ink-3);
  text-shadow: none;
}
body[data-theme="v2"] .v3-kpi-mega .val,
body[data-theme="v2"] .ops-val,
body[data-theme="v2"] .br-val {
  font-family: var(--font-mono);
  font-variant-numeric: tabular-nums;
  font-weight: 600;
  font-size: 24px;
  line-height: 1.15;
  letter-spacing: -.01em;
  color: var(--ink);
  text-shadow: none;
}
body[data-theme="v2"] .v3-kpi-mega .val .u {
  font-family: var(--font-sans);
  font-size: 13px;
  font-weight: 500;
  color: var(--ink-3);
}
body[data-theme="v2"] .v3-kpi-mega .meta,
body[data-theme="v2"] .ops-sub {
  font: var(--font-meta);
  color: var(--ink-3);
  text-shadow: none;
}

/* Deltas — direction is carried by the arrow glyph the renderer
   already emits, so colour is reinforcement rather than the signal. */
body[data-theme="v2"] .delta {
  font: var(--font-badge);
  font-family: var(--font-mono);
  padding: 1px 6px;
  border-radius: var(--radius-xs);
  background: var(--neutral-wash);
  color: var(--neutral);
  text-shadow: none;
}
body[data-theme="v2"] .delta.up   { background: var(--ok-wash);   color: var(--ok); }
body[data-theme="v2"] .delta.down { background: var(--risk-wash); color: var(--risk); }
body[data-theme="v2"] .delta-flat { background: var(--neutral-wash); color: var(--ink-3); }

/* Ops strip */
body[data-theme="v2"] .v3-ops-strip {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
  gap: var(--space-4);
}
body[data-theme="v2"] .v3-ops-card,
body[data-theme="v2"] .v3-queue-block,
body[data-theme="v2"] .br-stat {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  box-shadow: var(--shadow-1);
  padding: var(--space-4);
  backdrop-filter: none;
}
body[data-theme="v2"] .ops-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-2);
  margin-bottom: var(--space-2);
}

/* Live / status pills inside the dashboard */
body[data-theme="v2"] .v3-page .badge,
body[data-theme="v2"] .v3-page .live,
body[data-theme="v2"] .v3-mgmt-badge {
  font: var(--font-badge);
  font-family: var(--font-sans);
  letter-spacing: .02em;
  text-transform: uppercase;
  padding: 2px 8px;
  border-radius: var(--radius-pill);
  background: var(--neutral-wash);
  border: 1px solid var(--neutral-border);
  color: var(--ink-3);
  text-shadow: none;
  box-shadow: none;
}
body[data-theme="v2"] .v3-page .live {
  background: var(--ok-wash); border-color: var(--ok-border); color: var(--ok);
}

/* Controls */
body[data-theme="v2"] .v3-refresh-btn {
  height: var(--control-h-sm);
  padding: 0 12px;
  background: var(--surface-2);
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-sm);
  color: var(--ink-2);
  font: var(--font-badge);
  font-family: var(--font-sans);
  letter-spacing: .03em;
  text-shadow: none;
  box-shadow: none;
  cursor: pointer;
}
body[data-theme="v2"] .v3-refresh-btn:hover { background: var(--surface-3); color: var(--ink); }

body[data-theme="v2"] .v3-loading-overlay {
  background: var(--canvas);
  backdrop-filter: none;
  color: var(--ink-3);
  font: var(--font-body);
}

/* Sentiment / distribution bars */
body[data-theme="v2"] .cc-sent-bar-shell {
  background: var(--surface-2);
  border-radius: var(--radius-pill);
  overflow: hidden;
}
body[data-theme="v2"] .cc-sent-bar { background: var(--accent); box-shadow: none; }
body[data-theme="v2"] .cc-sent-lbl { font: var(--font-label); color: var(--ink-3); }
body[data-theme="v2"] .cc-sent-val { font-family: var(--font-mono); color: var(--ink); }

/* The cc-* home cards used by pages/dashboard.js share the same
   treatment, so the two dashboard generations finally match. */
body[data-theme="v2"] .cc-card {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  box-shadow: var(--shadow-1);
  backdrop-filter: none;
}
body[data-theme="v2"] .cc-card-head { padding: var(--card-pad) var(--card-pad) 0; }
body[data-theme="v2"] .cc-card-body { padding: var(--card-pad); }
body[data-theme="v2"] .cc-card-title { font: var(--font-card-title); color: var(--ink); text-shadow: none; }
body[data-theme="v2"] .cc-card-sub { font: var(--font-meta); color: var(--ink-3); }
body[data-theme="v2"] .cc-kpi-val,
body[data-theme="v2"] .cc-stat-val,
body[data-theme="v2"] .cc-hero-stat {
  font-family: var(--font-mono); font-variant-numeric: tabular-nums;
  font-weight: 600; color: var(--ink); text-shadow: none;
}
body[data-theme="v2"] .cc-kpi-lbl,
body[data-theme="v2"] .cc-stat-lbl {
  font: var(--font-label); letter-spacing: var(--tracking-caps);
  text-transform: uppercase; color: var(--ink-3);
}
body[data-theme="v2"] .cc-kpi-meta,
body[data-theme="v2"] .cc-stat-meta { font: var(--font-meta); color: var(--ink-3); }
body[data-theme="v2"] .cc-empty {
  color: var(--ink-3); font: var(--font-body);
  padding: var(--space-7) var(--space-4); text-align: center;
}


/* ════════════════════════════════════════════════════════════
   16. LOGIN — the first thing staff see
   ------------------------------------------------------------
   login.html shipped as a sci-fi bridge: a full-screen space video
   (assets/horizon/backdrops/gate-bridge.mp4), nebula glow, drifting
   sparks and a magenta-to-violet gradient sign-in button. It is the
   clearest example of what the 2.0 brief rules out — neon, heavy
   gradient, and a look that belongs to a game rather than to a
   financial-services operations centre. It is also the screen every
   staff member sees before anything else, so it sets the expectation
   for the whole product.

   Rebuilt as a restrained enterprise gate: the navy command palette
   from the shell, one accent, real type hierarchy, and a card that
   looks like it belongs to the application behind it.

   NOTHING FUNCTIONAL IS TOUCHED. #login-form, #login-user,
   #login-pass, #login-btn, #forgot-password-link, .login-error and
   the password-toggle keep their ids, classes, order and handlers —
   auth/login.js is not opened. The motion decoration is hidden rather
   than deleted, so reverting is a matter of dropping this section.

   Selectors carry the body.horizon-gate prefix to match
   horizon-gate.css at (0,2,1); components.css loads after it, so
   equal specificity resolves in favour of 2.0.
   ════════════════════════════════════════════════════════════ */

/* Backdrop — the space scene comes off. */
body.horizon-gate .hg-bg,
body.horizon-gate .hg-glow,
body.horizon-gate .hg-vignette,
body.horizon-gate .login-ambient,
body.horizon-gate .login-wave,
body.horizon-gate .login-orbit,
body.horizon-gate .login-streaks,
body.horizon-gate .login-particles,
body.horizon-gate .login-focus-field,
body.horizon-gate .login-focus-spark,
body.horizon-gate .logo-float-field,
body.horizon-gate .cp-comet { display: none !important; }

/* A quiet command-navy field with a faint grid. Enough presence to feel
   deliberate, not so much that it competes with the form. */
body.horizon-gate {
  background:
    radial-gradient(900px 600px at 50% -10%, rgba(59, 130, 246, .10), transparent 60%),
    linear-gradient(180deg, var(--shell-bg-2) 0%, var(--shell-bg) 55%, #070C17 100%);
  background-attachment: fixed;
  color: var(--shell-text);
  font-family: var(--font-sans);
}
body.horizon-gate::before {
  content: "";
  position: fixed;
  inset: 0;
  pointer-events: none;
  background-image:
    linear-gradient(rgba(157, 189, 255, .045) 1px, transparent 1px),
    linear-gradient(90deg, rgba(157, 189, 255, .045) 1px, transparent 1px);
  background-size: 56px 56px;
  mask-image: radial-gradient(1100px 700px at 50% 40%, #000 30%, transparent 78%);
  -webkit-mask-image: radial-gradient(1100px 700px at 50% 40%, #000 30%, transparent 78%);
}

/* Brand lockup above the card.
   .login-top ships display:none, because the old design had the wordmark
   baked into the background artwork rather than set as text. Removing that
   artwork therefore removed the only branding on the page — so the real
   lockup is brought back and set properly. Text beats a picture of text:
   it scales, it is selectable, and it survives a failed image load. */

body.horizon-gate .cc-header-band,
body.horizon-gate .login-top {
  background: none !important;
  border: 0 !important;
  box-shadow: none !important;
}
/* Last of the HUD furniture: the tactical skin also hangs corner
   brackets and a status pill on the gate. */
body.horizon-gate .cc-corner,
body.horizon-gate .cc-frame,
body.horizon-gate .cc-status-pill,
body.horizon-gate .tac-corner,
body.horizon-gate .tac-frame,
body.horizon-gate .tac-scan,
body.horizon-gate .tac-atmo,
body.horizon-gate .tac-gridfield,
body.horizon-gate .tac-reticle { display: none !important; }
body.horizon-gate .login-top {
  display: flex !important;
  align-items: center;
  justify-content: center;
  padding: var(--space-7) var(--space-5) 0;
  background: none;
  border: 0;
}
body.horizon-gate .cc-brand-stack {
  display: grid;
  justify-items: center;
  gap: 6px;
  text-align: center;
}
body.horizon-gate .cc-brand-mark { display: grid; place-items: center; }
body.horizon-gate .cc-brand-mark img,
body.horizon-gate .cc-brand-mark svg { width: 40px; height: 40px; filter: none; }
/* Partner logo rail stays out — on the gate it is clutter, and it is
   already shown in the app once signed in. */
body.horizon-gate .cc-header-rail { display: none; }
body.horizon-gate .login-center { padding-top: var(--space-5); }

body.horizon-gate .cc-wordmark {
  font-size: clamp(26px, 4vw, 38px);
  font-weight: 700;
  letter-spacing: -.02em;
  color: var(--shell-text);
  background: none;
  -webkit-text-fill-color: currentColor;
  text-shadow: none;
  filter: none;
}
body.horizon-gate .cc-subwordmark {
  background: none;
  border: 0;
  padding: 0;
  font: var(--font-label);
  letter-spacing: var(--tracking-caps);
  text-transform: uppercase;
  color: var(--shell-text-muted);
  text-shadow: none;
}

/* The card. */
body.horizon-gate .login-card {
  background: rgba(14, 23, 41, .96);
  border: 1px solid var(--shell-line);
  border-radius: var(--radius);
  box-shadow: var(--shadow-2);
  backdrop-filter: none;
  padding: var(--space-7);
  max-width: 420px;
  width: 100%;
  animation: none;
}
body.horizon-gate .login-card::before,
body.horizon-gate .login-card::after { content: none; }

body.horizon-gate .login-card-head { margin-bottom: var(--space-6); text-align: left; justify-items: start; }
body.horizon-gate .login-emblem { display: none; }
body.horizon-gate .login-badge {
  font: var(--font-badge);
  letter-spacing: var(--tracking-caps);
  text-transform: uppercase;
  color: var(--shell-text-muted);
  background: rgba(255, 255, 255, .05);
  border: 1px solid var(--shell-line);
  border-radius: var(--radius-pill);
  padding: 3px 10px;
  text-shadow: none;
}
body.horizon-gate .login-card h2,
body.horizon-gate .login-card-head h2 {
  font-size: 21px;
  font-weight: 600;
  letter-spacing: var(--tracking-tight);
  color: var(--shell-text);
  text-shadow: none;
  margin: var(--space-3) 0 0;
}

/* Fields. */
body.horizon-gate .login-card .field { margin-bottom: var(--space-4); }
body.horizon-gate .login-card .field > label {
  font: var(--font-label);
  letter-spacing: var(--tracking-caps);
  text-transform: uppercase;
  color: var(--shell-text-muted);
}
body.horizon-gate .login-card input[type="text"],
body.horizon-gate .login-card input[type="password"],
body.horizon-gate .login-card input[type="email"] {
  width: 100%;
  height: 42px;
  padding: 0 12px;
  background: rgba(255, 255, 255, .04);
  border: 1px solid var(--shell-line);
  border-radius: var(--radius-sm);
  color: var(--shell-text);
  font: var(--font-body);
  box-shadow: none;
  transition: border-color var(--motion-control) var(--ease-out),
              background var(--motion-control) var(--ease-out);
}
body.horizon-gate .login-card input::placeholder { color: var(--shell-text-dim); }
body.horizon-gate .login-card input:focus {
  border-color: var(--accent);
  background: rgba(255, 255, 255, .07);
  outline: none;
  box-shadow: 0 0 0 3px rgba(59, 130, 246, .18);
}
body.horizon-gate .login-field-hint {
  font: var(--font-helper);
  color: var(--shell-text-dim);
}
body.horizon-gate .password-toggle {
  background: transparent;
  border: 0;
  color: var(--shell-text-dim);
  cursor: pointer;
}
body.horizon-gate .password-toggle:hover { color: var(--shell-text); }

/* The one accent on the page. */
body.horizon-gate .login-btn {
  width: 100%;
  height: 44px;
  margin-top: var(--space-2);
  background: var(--accent);
  border: 1px solid var(--accent);
  border-radius: var(--radius-sm);
  color: #fff;
  font-size: 14px;
  font-weight: 600;
  letter-spacing: .02em;
  text-transform: none;
  text-shadow: none;
  box-shadow: none;
  cursor: pointer;
  transition: background var(--motion-control) var(--ease-out);
}
body.horizon-gate .login-btn:hover { background: var(--accent-hover); border-color: var(--accent-hover); }
body.horizon-gate .login-btn:active { transform: none; background: var(--accent-active); }

body.horizon-gate .login-link-button,
body.horizon-gate #forgot-password-link {
  background: none;
  border: 0;
  color: var(--accent);
  font: var(--font-body);
  font-weight: 500;
  cursor: pointer;
  text-decoration: none;
}
body.horizon-gate .login-link-button:hover { text-decoration: underline; }

body.horizon-gate .login-meta,
body.horizon-gate .login-protected,
body.horizon-gate .holo-foot {
  font: var(--font-meta);
  color: var(--shell-text-dim);
  text-shadow: none;
}

/* Errors read as errors: colour plus weight plus a bordered block. */
body.horizon-gate .login-error {
  background: rgba(185, 28, 28, .14);
  border: 1px solid var(--risk-border);
  border-radius: var(--radius-sm);
  color: var(--danger-soft);
  font: var(--font-error);
  padding: var(--space-3);
  margin-bottom: var(--space-3);
  text-shadow: none;
}

@media (max-width: 560px) {
  body.horizon-gate .login-card { padding: var(--space-5); }
}


/* ════════════════════════════════════════════════════════════
   17. CLIENT SCREENS — closing the last gaps
   ------------------------------------------------------------
   Surveying the class attributes across the client-facing work
   surfaces — client-lookup, client-profile, queries-hub, callbacks,
   svc-initiation, qa-reviews and intake-hub — showed they are built
   almost entirely from the shared vocabulary this file already
   defines: .card 66, .btn 66, .card-sub 56, .card-title 41,
   .mini-note 32, .field 21, .card-head 14, .meta-label 11,
   .metric-value 9, .section-kicker 8, .page-head 8, .card-tag 8,
   .table-wrap 4, .status-chip 3, .grid-4 3.

   So those screens already inherit 2.0 — which is the whole reason
   the component layer was written against the existing vocabulary
   rather than a parallel set. What remains is a short tail of
   page-local helpers, handled below. This section is deliberately
   small: a long list here would mean the strategy had failed.

   intake-hub, the email/SMS pages and the NuPay pages are covered by
   the same shared rules with NO per-page JavaScript touched, which is
   the stylesheet-only treatment agreed for files other agents own.
   ════════════════════════════════════════════════════════════ */

/* Action rows — the button cluster under a form or a record. */
body[data-theme="v2"] .wg-actions-row,
body[data-theme="v2"] .action-row {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  flex-wrap: wrap;
  padding-top: var(--space-3);
  margin-top: var(--space-3);
  border-top: 1px solid var(--border);
}

/* Filter and form grids — one column rule instead of four variants. */
body[data-theme="v2"] .wg-filter-grid,
body[data-theme="v2"] .wg-form-grid,
body[data-theme="v2"] .wg-detail-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(210px, 1fr));
  gap: var(--space-3) var(--space-4);
  align-items: start;
}
body[data-theme="v2"] .wg-full-width { grid-column: 1 / -1; }

/* "Restricted" notice — shown where a role may see that something
   exists but not its contents. It must read as a boundary, not as an
   error, so it takes the neutral wash rather than the risk one. */
body[data-theme="v2"] .restricted {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  padding: var(--space-3) var(--space-4);
  background: var(--neutral-wash);
  border: 1px solid var(--neutral-border);
  border-radius: var(--radius-sm);
  color: var(--ink-3);
  font: var(--font-body);
}

/* Tone helpers. Colour only — the surrounding markup supplies the
   words, so these never become the sole carrier of meaning. */
body[data-theme="v2"] .tone-green { color: var(--ok);      background: var(--ok-wash); }
body[data-theme="v2"] .tone-amber { color: var(--warn);    background: var(--warn-wash); }
body[data-theme="v2"] .tone-red   { color: var(--risk);    background: var(--risk-wash); }
body[data-theme="v2"] .tone-blue  { color: var(--info);    background: var(--info-wash); }
body[data-theme="v2"] :is(.tone-green, .tone-amber, .tone-red, .tone-blue) {
  padding: 1px 7px;
  border-radius: var(--radius-xs);
  font-weight: 600;
}

/* Initiation Station local helpers. */
body[data-theme="v2"] .si-drop {
  background: var(--surface-2);
  border: 1px dashed var(--border-strong);
  border-radius: var(--radius-sm);
  padding: var(--space-5);
  color: var(--ink-3);
  font: var(--font-body);
  text-align: center;
}
body[data-theme="v2"] .si-tbl { width: 100%; border-collapse: separate; border-spacing: 0; }
body[data-theme="v2"] .si-tbl th {
  font: var(--font-table-head);
  letter-spacing: var(--tracking-caps);
  text-transform: uppercase;
  color: var(--ink-3);
  text-align: left;
  padding: 8px 10px;
  background: var(--surface-2);
  border-bottom: 1px solid var(--border-strong);
}
body[data-theme="v2"] .si-tbl td {
  padding: 8px 10px;
  font: var(--font-table-cell);
  color: var(--ink-2);
  border-bottom: 1px solid var(--border);
}

/* Threaded client queries — a reply is indented and rule-marked, so the
   conversation structure survives without relying on colour. */
body[data-theme="v2"] .thread-child {
  margin-left: var(--space-5);
  padding-left: var(--space-4);
  border-left: 2px solid var(--border-strong);
}

/* Client picker row in the portfolio component. */
body[data-theme="v2"] .cp-pick {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-3);
  padding: var(--space-3);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  background: var(--surface);
  cursor: pointer;
  transition: background var(--motion-control) var(--ease-out),
              border-color var(--motion-control) var(--ease-out);
}
body[data-theme="v2"] .cp-pick:hover { background: var(--surface-2); border-color: var(--accent-border); }

/* ── Client portfolio overlay ──────────────────────────────────────────────
   The full-file drill-in opened from a callback row (and from Lead Pipeline).
   components/client-portfolio.js has rendered this markup since it was
   written, but NO stylesheet ever defined these classes — so the overlay
   appended an unstyled wall of text to <body> while also setting
   body{overflow:hidden}, which reads as a broken page rather than a panel.
   Added 2026-09-02 alongside the getClientAudit fix that made it load at all.

   Scoped under body[data-theme="v2"] like every other rule in this file, and
   built only from existing tokens so it inherits light/dark with the rest. */

body[data-theme="v2"] .client-portfolio-overlay {
  position: fixed;
  inset: 0;
  z-index: 900;
  display: flex;
  flex-direction: column;
  background: var(--bg);
  overflow: hidden;
}

body[data-theme="v2"] .cp-header {
  display: flex;
  align-items: center;
  gap: var(--space-4);
  padding: var(--space-4) var(--space-5);
  border-bottom: 1px solid var(--border);
  background: var(--surface);
  flex: 0 0 auto;
}
body[data-theme="v2"] .cp-title { font-size: 15px; font-weight: 600; color: var(--text); }
body[data-theme="v2"] .cp-back-btn {
  border: 1px solid var(--border);
  background: var(--surface-2, var(--surface));
  color: var(--text);
  border-radius: 8px;
  padding: 6px 12px;
  font: inherit;
  font-size: 13px;
  cursor: pointer;
}
body[data-theme="v2"] .cp-back-btn:hover { border-color: var(--border-strong); }

/* The body is the only scrolling region — the header stays put on a long file. */
body[data-theme="v2"] .cp-body {
  flex: 1 1 auto;
  overflow-y: auto;
  padding: var(--space-5);
}
body[data-theme="v2"] .cp-loading { color: var(--muted); font-size: 14px; }
body[data-theme="v2"] .cp-empty { color: var(--muted); font-size: 13px; padding: var(--space-4) 0; }

body[data-theme="v2"] .cp-identity {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: var(--space-3) var(--space-5);
  margin-bottom: var(--space-5);
}
body[data-theme="v2"] .cp-client-name { font-size: 20px; font-weight: 650; color: var(--text); }
body[data-theme="v2"] .cp-client-meta,
body[data-theme="v2"] .cp-id-details { color: var(--muted); font-size: 13px; }
body[data-theme="v2"] .cp-id-main { font-size: 14px; color: var(--text); }
body[data-theme="v2"] .cp-id-badges { display: flex; flex-wrap: wrap; gap: var(--space-2); }

body[data-theme="v2"] .cp-summary-cards,
body[data-theme="v2"] .cp-compliance-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
  gap: var(--space-3);
  margin-bottom: var(--space-5);
}
body[data-theme="v2"] .cp-card {
  border: 1px solid var(--border);
  border-radius: 10px;
  background: var(--surface);
  padding: var(--space-4);
}
body[data-theme="v2"] .cp-card-label { font-size: 12px; text-transform: uppercase; letter-spacing: .04em; color: var(--muted); }
body[data-theme="v2"] .cp-card-value { font-size: 20px; font-weight: 650; color: var(--text); margin-top: 4px; }

body[data-theme="v2"] .cp-tabs {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2);
  border-bottom: 1px solid var(--border);
  margin-bottom: var(--space-5);
}
body[data-theme="v2"] .cp-tab {
  border: 0;
  background: transparent;
  color: var(--muted);
  font: inherit;
  font-size: 13px;
  padding: 8px 14px;
  cursor: pointer;
  border-bottom: 2px solid transparent;
}
body[data-theme="v2"] .cp-tab:hover { color: var(--text); }
body[data-theme="v2"] .cp-tab.active { color: var(--text); border-bottom-color: var(--accent, var(--text)); }

/* Wide financial tables scroll sideways rather than crushing — same rule the
   rest of the estate follows, because squeezing a comparison destroys it. */
body[data-theme="v2"] .cp-table { width: 100%; border-collapse: collapse; font-size: 13px; display: block; overflow-x: auto; }
body[data-theme="v2"] .cp-table th,
body[data-theme="v2"] .cp-table td {
  text-align: left;
  padding: 8px 10px;
  border-bottom: 1px solid var(--border-subtle, var(--border));
  white-space: nowrap;
}
body[data-theme="v2"] .cp-table th { color: var(--muted); font-weight: 600; font-size: 12px; text-transform: uppercase; letter-spacing: .04em; }

body[data-theme="v2"] .cp-field { margin-bottom: var(--space-3); font-size: 13px; }
body[data-theme="v2"] .cp-check-item {
  display: flex; justify-content: space-between; gap: var(--space-4);
  padding: 7px 0; border-bottom: 1px solid var(--border-subtle, var(--border)); font-size: 13px;
}
body[data-theme="v2"] .cp-check-label { color: var(--muted); }
body[data-theme="v2"] .cp-check-value { color: var(--text); }

body[data-theme="v2"] .cp-query-card,
body[data-theme="v2"] .cp-timeline-card {
  border: 1px solid var(--border);
  border-radius: 10px;
  background: var(--surface);
  padding: var(--space-4);
  margin-bottom: var(--space-3);
}
body[data-theme="v2"] .cp-query-header { display: flex; justify-content: space-between; gap: var(--space-3); flex-wrap: wrap; }
body[data-theme="v2"] .cp-query-subject { font-weight: 600; color: var(--text); font-size: 14px; }
body[data-theme="v2"] .cp-query-meta { color: var(--muted); font-size: 12px; }

body[data-theme="v2"] .cp-tl-icon { flex: 0 0 auto; }
body[data-theme="v2"] .cp-tl-date { color: var(--muted); font-size: 12px; min-width: 130px; }
body[data-theme="v2"] .cp-tl-body { display: flex; gap: var(--space-3); align-items: baseline; }
body[data-theme="v2"] .cp-tl-text { color: var(--text); font-size: 13px; }

body[data-theme="v2"] .tone-green  { color: var(--ok, #4ade80); }
body[data-theme="v2"] .tone-red    { color: var(--risk, #f87171); }
body[data-theme="v2"] .tone-amber  { color: var(--warn, #fbbf24); }
body[data-theme="v2"] .tone-orange { color: var(--warn, #fb923c); }

@media (max-width: 640px) {
  body[data-theme="v2"] .cp-body { padding: var(--space-4); }
  body[data-theme="v2"] .cp-client-name { font-size: 17px; }
}

/* ── Alert banners ───────────────────────────────────────────────────────────
   `class="alert warn|danger|info"` is used in eight places across the page
   renderers to carry a warning ABOVE the data it is warning about — and no
   `.alert` rule has ever existed in this stylesheet. Every one of them has been
   rendering as unstyled body text.

   That is not cosmetic. The Client Communication Hub used one to tell staff that
   a row in the email list is not proof of delivery; it read as ordinary prose
   beside the table, and DROPPED sends went unnoticed for weeks while clients and
   creditors were never reached.

   `.cc-tab.alert` in dashboard.css is a DIFFERENT thing — a red-dot modifier on
   a Command Centre tab, and `.cc-tab` is sometimes a <div>. It is excluded here;
   do not remove that guard.                                                    */
body[data-theme="v2"] .alert:not(.cc-tab) {
  display: block;
  background: var(--warn-wash);
  border: 1px solid var(--warn-border);
  border-left: 3px solid var(--warn);
  border-radius: var(--radius-2, 8px);
  padding: 12px 14px;
  font-size: 13px;
  line-height: 1.5;
}
body[data-theme="v2"] .alert.danger:not(.cc-tab) {
  background: var(--risk-wash);
  border-color: var(--risk-border);
  border-left-color: var(--risk);
}
body[data-theme="v2"] .alert.info:not(.cc-tab) {
  background: var(--ok-wash);
  border-color: var(--ok-border);
  border-left-color: var(--ok);
}
