/* ==========================================================================
   StrataHawk — shared.css
   Every component that appears on more than one page.

   LOAD ORDER — this matters:
       <link rel="stylesheet" href="css/tokens.css">     ← must be first
       <link rel="stylesheet" href="css/shared.css">
       <link rel="stylesheet" href="css/app-shell.css">  ← the page's own remainder

   tokens.css carries the permission-gate rules and must load before anything
   that could paint a gated control. It is a separate file rather than the top of
   this one so the auth pages, which have no permission model, can still load the
   palette without carrying the gate.

   WHY THIS FILE EXISTS
   app-shell.css, user-panel-CURRENT-LIVE.css, settings.css and
   api-connections.css shipped four near-identical copies of the banner, the user
   menu, the button family, the form fields and the modal. A fix landed in one and
   not the others roughly every time. This is one copy.

   HOUSE RULES OBSERVED HERE
   - Every colour is a token. No literal hex below this comment.
   - Icons are inline SVG, single-colour, stroked, on currentColor.
   - !important appears only in media queries that must beat an inline style
     attribute written by JS. Each one is marked with the module that writes it.
   - State classes (.active, .open, .selected …) are restyled, never renamed.
   ========================================================================== */


/* ══════════════════════════════════════════════════════════════════════════
   BASE
   ══════════════════════════════════════════════════════════════════════════ */

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

html { overflow-x: clip; }

body {
  font-family: var(--font-sans);
  font-size: var(--text-base);
  line-height: var(--leading-snug);
  background: var(--bg-page);
  color: var(--text-primary);
  min-height: 100vh;
  overflow-x: clip;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  transition: background var(--transition), color var(--transition);
}

/* Numbers that sit in a column — tract ids, volumes, pages, interests, money —
   must align on the digit, not on the glyph. Tabular figures are the difference
   between a scannable runsheet and a ragged one. */
.td-num, .list-table td.td-num, .sv-num, .rt-table td, .plot-tbl td,
input[type="number"], .doc-party-pct, .doc-pct-fnum, .doc-pct-fden,
.doc-party-royalty, .doc-royalty-fnum, .doc-royalty-fden {
  font-variant-numeric: tabular-nums;
}

/* One focus treatment for the whole product, keyboard-only. There was almost
   none before, which made the app unusable without a mouse. */
:focus-visible {
  outline: var(--focus-width) solid var(--focus-color);
  outline-offset: var(--focus-offset);
  border-radius: var(--radius-xs);
}
/* Controls that already draw their own ring do not need a second one. */
.form-input:focus-visible, .form-select:focus-visible, .form-textarea:focus-visible,
.toolbar-search:focus-visible { outline: none; }

::selection { background: var(--blue-200); color: var(--text-primary); }

/* Scrollbars in the dense panels, so a dark surface does not get a light gutter. */
* { scrollbar-color: var(--border-strong) transparent; scrollbar-width: thin; }
*::-webkit-scrollbar { width: 10px; height: 10px; }
*::-webkit-scrollbar-track { background: transparent; }
*::-webkit-scrollbar-thumb { background: var(--border-strong); border-radius: var(--radius-pill); border: 2px solid transparent; background-clip: content-box; }

/* The scanned record must look like the record. No inversion in dark mode, no
   filters, no transforms on the PDF canvas — this is a legal document. */
.doc-viewer-body canvas, .doc-viewer canvas {
  filter: none;
  background: #fff;   /* the one deliberate literal: paper is paper in both themes */
  box-shadow: var(--shadow-card);
  max-width: 100%; height: auto;
  user-select: none; -webkit-user-drag: none;
}


/* ══════════════════════════════════════════════════════════════════════════
   BUTTONS
   One geometry, four intents. The old sheet had .btn, .btn-new-project,
   .tos-gate-btn, .btn-submit, .doc-add-btn, .card-btn and .col-select-btn all
   describing roughly the same object at seven slightly different sizes.
   ══════════════════════════════════════════════════════════════════════════ */

.btn, .btn-new-project, .tos-gate-btn, .btn-submit, .doc-add-btn,
.col-select-btn, .btn-back, .btn-link, .plot-btn, .lp-btn {
  display: inline-flex; align-items: center; justify-content: center;
  gap: var(--space-3);
  font-family: inherit;
  font-size: var(--text-base);
  font-weight: var(--weight-semibold);
  line-height: 1;
  padding: var(--space-4) var(--space-6);
  min-height: var(--target-min-dense);
  border: 1px solid transparent;
  border-radius: var(--radius-btn);
  cursor: pointer;
  white-space: nowrap;
  text-decoration: none;
  transition: background var(--dur-fast) var(--ease-out),
              border-color var(--dur-fast) var(--ease-out),
              color var(--dur-fast) var(--ease-out);
}

.btn svg, .btn-new-project svg, .btn-back svg, .btn-link svg, .doc-add-btn svg {
  width: 15px; height: 15px; flex-shrink: 0;
  fill: none; stroke: currentColor; stroke-width: 2;
  stroke-linecap: round; stroke-linejoin: round;
}

.btn-primary, .btn-new-project, .tos-gate-btn, .btn-submit, .doc-add-btn, .btn-save {
  background: var(--blue-700); color: var(--text-on-accent);
}
.btn-secondary, .col-select-btn, .btn-back, .btn-link, .btn-update, .btn-cancel, .plot-btn {
  background: var(--bg-card); color: var(--text-secondary); border-color: var(--border);
}
.btn-danger { background: var(--danger); color: var(--text-on-dark); }
.btn-ghost  { background: transparent; color: var(--text-secondary); }

/* Destructive-but-quiet: the Delete that sits at the end of a working-window
   footer. Reads as available, not as the thing you came here to do. */
.btn-danger-quiet, .ow-name-card-rm, .ow-alias-rm, .doc-remove-btn {
  background: transparent; color: var(--danger-fg); border-color: var(--danger-border);
}

@media (hover: hover) {
  .btn-primary:hover, .btn-new-project:hover, .tos-gate-btn:hover,
  .btn-submit:hover, .doc-add-btn:hover, .btn-save:hover { background: var(--blue-600); }
  .btn-secondary:hover, .col-select-btn:hover, .btn-back:hover,
  .btn-link:hover, .plot-btn:hover {
    background: var(--bg-subtle); color: var(--text-primary); border-color: var(--border-strong);
  }
  .btn-danger:hover { filter: brightness(0.92); }
  .btn-ghost:hover  { background: var(--bg-hover); color: var(--text-primary); }
  .btn-danger-quiet:hover { background: var(--danger-bg); color: var(--danger-fg); }
}

.btn:disabled, .btn-submit:disabled, .btn-save:disabled, .btn-primary:disabled {
  opacity: 0.55; cursor: not-allowed;
}

.btn-sm, .lp-btn { font-size: var(--text-sm); padding: var(--space-3) var(--space-5); gap: var(--space-2); }
.btn-sm svg { width: 13px; height: 13px; }

/* Icon-only buttons: square, and never below the touch minimum on a phone. */
.btn-icon, .card-btn, .doc-zoom-btn, .modal-close, .doc-viewer-close, .tos-close {
  display: inline-flex; align-items: center; justify-content: center;
  width: 32px; height: 32px; padding: 0; flex-shrink: 0;
  border: 1px solid transparent; border-radius: var(--radius-sm);
  background: transparent; color: var(--text-muted);
  cursor: pointer; font-family: inherit; line-height: 1;
  transition: background var(--dur-fast) var(--ease-out), color var(--dur-fast) var(--ease-out);
}
.btn-icon svg, .card-btn svg, .doc-zoom-btn svg, .modal-close svg {
  width: 16px; height: 16px; fill: none; stroke: currentColor; stroke-width: 2;
  stroke-linecap: round; stroke-linejoin: round;
}
@media (hover: hover) {
  .card-btn:hover, .doc-zoom-btn:hover, .modal-close:hover,
  .doc-viewer-close:hover, .tos-close:hover { background: var(--bg-hover); color: var(--text-primary); }
  .card-btn.edit:hover   { background: var(--info-bg);   color: var(--info-fg);   border-color: var(--info-border); }
  .card-btn.delete:hover { background: var(--danger-bg); color: var(--danger-fg); border-color: var(--danger-border); }
}

/* On the banner the icon buttons sit on a dark ground and need their own values —
   the banner is always dark, in every theme. */
.banner .btn-icon {
  width: 34px; height: 34px; border-radius: var(--radius-circle);
  border-color: rgba(255,255,255,0.16); background: rgba(255,255,255,0.09); color: var(--text-on-dark);
}
@media (hover: hover) { .banner .btn-icon:hover { background: rgba(255,255,255,0.18); } }

@media (max-width: 640px) {
  .btn, .btn-primary, .btn-secondary, .btn-danger, .btn-new-project { min-height: var(--target-min); }
  .btn-icon, .card-btn { width: 40px; height: 40px; }
}


/* ══════════════════════════════════════════════════════════════════════════
   PILLS AND SEGMENTED CONTROLS
   Three visual families doing three different jobs. Previously eight class names
   sat somewhere between them, differing only by which bar they happened to be in.

   1. PILL      — a filter. Rounded, toggles a subset of a list.
   2. SUB-TAB   — a section. Squarer, switches what you are looking at.
   3. SEGMENT   — a mode. Recessed track, mutually exclusive, no navigation.
   ══════════════════════════════════════════════════════════════════════════ */

/* ── 1. Pills ─────────────────────────────────────────────────────────── */
.filter-btn, .me-filter-badge, .chat-tier-btn, .drng-preset, .rqc-preset, .sv-pill {
  display: inline-flex; align-items: center; gap: var(--space-3);
  font-family: inherit; font-size: var(--text-sm); font-weight: var(--weight-medium);
  padding: var(--space-3) var(--space-5);
  min-height: var(--target-min-dense);
  border: 1px solid var(--border); border-radius: var(--radius-pill);
  background: var(--bg-card); color: var(--text-secondary);
  cursor: pointer; white-space: nowrap;
  -webkit-tap-highlight-color: transparent;
  transition: background var(--dur-fast) var(--ease-out),
              border-color var(--dur-fast) var(--ease-out),
              color var(--dur-fast) var(--ease-out);
}
@media (hover: hover) {
  .filter-btn:hover, .me-filter-badge:hover, .chat-tier-btn:hover, .sv-pill:hover {
    border-color: var(--border-strong); color: var(--text-primary); background: var(--bg-subtle);
  }
}
/* Active reads by FILL. The old active state was a 1px border-colour change,
   which is invisible in a wrapping row of nine org-defined statuses — the exact
   situation this control is in on the Curative screen. */
.filter-btn.active, .me-filter-badge.active, .chat-tier-btn.active,
.drng-preset.active, .rqc-preset.on, .sv-pill.active {
  background: var(--blue-700); border-color: var(--blue-700);
  color: var(--text-on-accent); font-weight: var(--weight-semibold);
}

/* ── 2. Sub-tabs ──────────────────────────────────────────────────────── */
.mw-type-btn, .dash-nav-btn {
  display: inline-flex; align-items: center; justify-content: center; gap: var(--space-3);
  font-family: inherit; font-size: var(--text-base); font-weight: var(--weight-semibold);
  padding: var(--space-4) var(--space-7);
  min-height: var(--target-min-dense);
  border: 1px solid var(--border); border-radius: var(--radius-btn);
  background: var(--bg-card); color: var(--text-secondary);
  cursor: pointer; white-space: nowrap;
  transition: background var(--dur-fast) var(--ease-out),
              border-color var(--dur-fast) var(--ease-out),
              color var(--dur-fast) var(--ease-out);
}
@media (hover: hover) {
  .mw-type-btn:hover, .dash-nav-btn:hover { border-color: var(--border-strong); color: var(--text-primary); background: var(--bg-subtle); }
}
.mw-type-btn.active, .dash-nav-btn.active {
  background: var(--blue-700); border-color: var(--blue-700); color: var(--text-on-accent);
}

/* ── 3. Segmented controls ────────────────────────────────────────────── */
.view-toggle, .td-view-seg, .sv-view-seg, .doc-scope-toggle, .owner-type-toggle, .plot-seg {
  display: inline-flex; align-items: center; gap: var(--space-1);
  padding: var(--space-1);
  background: var(--bg-sunken); border: 1px solid var(--border);
  border-radius: var(--radius-md);
}
.view-btn, .td-view-btn, .sv-view-btn, .doc-scope-btn, .doc-dtype-btn, .doc-cert-btn,
.owner-type-btn, .owner-status-btn, .ow-type-btn {
  display: inline-flex; align-items: center; justify-content: center; gap: var(--space-2);
  font-family: inherit; font-size: var(--text-sm); font-weight: var(--weight-medium);
  padding: var(--space-3) var(--space-5);
  min-height: 30px; min-width: 30px;
  border: 1px solid transparent; border-radius: var(--radius-sm);
  background: transparent; color: var(--text-muted);
  cursor: pointer; white-space: nowrap;
  transition: background var(--dur-fast) var(--ease-out), color var(--dur-fast) var(--ease-out);
}
.view-btn svg, .td-view-btn svg {
  width: 15px; height: 15px; fill: none; stroke: currentColor; stroke-width: 2;
  stroke-linecap: round; stroke-linejoin: round;
}
/* The selected segment is RAISED, not filled with accent. A filled segment inside
   a filter bar reads as "this filter is applied"; a raised one reads as "this is
   the view you are in", which is what it means. */
.view-btn.active, .td-view-btn.active, .sv-view-btn.active, .doc-scope-btn.active,
.doc-dtype-btn.active, .doc-cert-btn.active, .owner-type-btn.active,
.owner-status-btn.active, .ow-type-btn.active {
  background: var(--bg-card); color: var(--text-primary);
  border-color: var(--border); box-shadow: var(--shadow-card);
  font-weight: var(--weight-semibold);
}
@media (hover: hover) {
  .view-btn:hover, .td-view-btn:hover, .doc-scope-btn:hover { color: var(--text-primary); }
}
@media (max-width: 640px) {
  .filter-btn, .me-filter-badge, .mw-type-btn, .dash-nav-btn { min-height: var(--target-min); }
  .view-btn, .td-view-btn, .doc-scope-btn { min-height: 38px; min-width: 38px; }
}

/* ── The My/Org scope switch ──────────────────────────────────────────────
   Rendered into its host by scope.js. Deliberately a SEGMENT and not a pill: it
   sits inches from .mw-type-btn and a pill-shaped scope switch would read as a
   fifth sub-tab. Marked up role="tablist" with two role="tab" children — whatever
   it looks like, a screen reader announces one control with two positions. */
.scope-switch {
  display: inline-flex; align-items: center; gap: var(--space-1);
  padding: var(--space-1);
  background: var(--bg-sunken); border: 1px solid var(--border);
  border-radius: var(--radius-md);
}
.scope-seg {
  display: inline-flex; align-items: center; gap: var(--space-3);
  font-family: inherit; font-size: var(--text-sm); font-weight: var(--weight-semibold);
  padding: var(--space-3) var(--space-5); min-height: 30px;
  border: 1px solid transparent; border-radius: var(--radius-sm);
  background: transparent; color: var(--text-muted);
  cursor: pointer; white-space: nowrap;
  transition: background var(--dur-fast) var(--ease-out), color var(--dur-fast) var(--ease-out);
}
.scope-seg[aria-selected="true"], .scope-seg.active {
  background: var(--bg-card); color: var(--text-primary);
  border-color: var(--border); box-shadow: var(--shadow-card);
}
/* Only the ORG position is marked with an accent rail. Personal data is the safe
   default; org-wide data is the position you can misread, and marking both would
   mark neither. */
.scope-seg[data-scope="org"][aria-selected="true"],
.scope-seg[data-scope="org"].active {
  box-shadow: var(--shadow-card), inset 3px 0 0 var(--blue-600);
  padding-left: var(--space-6);
}
.scope-switch-thumb { display: none; }   /* superseded; the segment carries its own fill */

.scope-strip {
  display: flex; align-items: center; gap: var(--space-5); flex-wrap: wrap;
  padding: var(--space-5) var(--gutter);
  background: var(--bg-card); border-bottom: 1px solid var(--border);
}
/* A non-admin gets no toggle at all — scope.js renders nothing. The strip must
   then collapse completely rather than leaving a bordered empty band that reads
   as "something failed to load". */
.scope-strip:empty { display: none; padding: 0; border: none; }

@media (max-width: 640px) {
  .scope-switch { width: 100%; }
  .scope-seg { flex: 1; justify-content: center; min-height: 38px; }
}


/* ══════════════════════════════════════════════════════════════════════════
   BADGES AND CHIPS
   Statuses are org-configurable and generate their own class names
   (.badge-status-not-satisfied---priority — a slugified label, triple hyphen and
   all), so they cannot be enumerated at design time. The base .badge therefore
   CARRIES THE NEUTRAL TONE, and a status the stylesheet has never heard of lands
   there looking intentional instead of unstyled.
   ══════════════════════════════════════════════════════════════════════════ */

.badge, .badge-category, .badge-req-status, .badge-reimbursed, .doc-type-chip,
.tract-chip, .unit-chip, .org-role-badge, .task-count-badge, .ow-stat-pill,
.timeline-pill, .doc-step-badge, .tc-pill, .rt-pill, .dc-survey-chip, .sv-chip-note {
  display: inline-flex; align-items: center; gap: var(--space-2);
  font-size: var(--text-2xs); font-weight: var(--weight-semibold);
  line-height: 1.4; letter-spacing: 0.01em;
  padding: var(--space-2) var(--space-4);
  border: 1px solid var(--neutral-border); border-radius: var(--radius-sm);
  background: var(--neutral-bg); color: var(--neutral-fg);
  white-space: nowrap;
}
.badge svg, .timeline-pill svg {
  width: 12px; height: 12px; flex-shrink: 0;
  fill: none; stroke: currentColor; stroke-width: 2; stroke-linecap: round; stroke-linejoin: round;
}

/* Semantic modifiers. New code should use these. */
.badge.is-success, .is-tone-success { background: var(--success-bg); color: var(--success-fg); border-color: var(--success-border); }
.badge.is-warning, .is-tone-warning { background: var(--warning-bg); color: var(--warning-fg); border-color: var(--warning-border); }
.badge.is-danger,  .is-tone-danger  { background: var(--danger-bg);  color: var(--danger-fg);  border-color: var(--danger-border); }
.badge.is-info,    .is-tone-info    { background: var(--info-bg);    color: var(--info-fg);    border-color: var(--info-border); }
.badge.is-neutral, .is-tone-neutral { background: var(--neutral-bg); color: var(--neutral-fg); border-color: var(--neutral-border); }
.badge.is-ai,      .is-tone-ai      { background: var(--ai-bg);      color: var(--ai-fg);      border-color: var(--ai-border); }
.badge.is-shared,  .is-tone-shared  { background: var(--shared-bg);  color: var(--shared-fg);  border-color: var(--shared-border); }

/* The statuses that ship with the product, mapped onto tones. Each of these was
   previously four literals across two theme blocks; they are now one line and
   they follow the theme. Org-defined statuses need no rule at all. */
.badge-status-planning,
.badge-status-advisory-only              { background: var(--info-bg);    color: var(--info-fg);    border-color: var(--info-border); }
.badge-status-in-progress                { background: var(--info-bg);    color: var(--info-fg);    border-color: var(--info-border); }
.badge-status-on-hold,
.badge-status-cautionary-recommended,
.badge-status-not-satisfied---may-be-deferred { background: var(--warning-bg); color: var(--warning-fg); border-color: var(--warning-border); }
.badge-status-completed,
.badge-reimbursed                        { background: var(--success-bg); color: var(--success-fg); border-color: var(--success-border); }
.badge-status-cancelled,
.badge-status-not-satisfied---priority   { background: var(--danger-bg);  color: var(--danger-fg);  border-color: var(--danger-border); }
.badge-status-conditional                { background: var(--ai-bg);      color: var(--ai-fg);      border-color: var(--ai-border); }

.badge-category, .badge-primary { background: var(--info-bg); color: var(--info-fg); border-color: var(--info-border); }
.badge-req-status { background: var(--ai-bg); color: var(--ai-fg); border-color: var(--ai-border); align-self: flex-start; width: fit-content; }

.pill-active    { background: var(--info-bg);    color: var(--info-fg);    border-color: var(--info-border); }
.pill-remaining { background: var(--success-bg); color: var(--success-fg); border-color: var(--success-border); }
.pill-overdue   { background: var(--danger-bg);  color: var(--danger-fg);  border-color: var(--danger-border); font-weight: var(--weight-bold); }

/* Match markers on owner/survey rows — small, circular, and now legible. */
.badge-match-m, .badge-match-s {
  display: inline-flex; align-items: center; justify-content: center;
  width: 18px; height: 18px; padding: 0; flex-shrink: 0;
  border-radius: var(--radius-circle);
  font-size: var(--text-2xs); font-weight: var(--weight-bold); line-height: 1;
  cursor: pointer; vertical-align: middle;
}
.badge-match-m { background: var(--info-bg);   color: var(--info-fg);   border: 1px solid var(--info-border); }
.badge-match-s { background: var(--shared-bg); color: var(--shared-fg); border: 1px solid var(--shared-border); }

/* Chips carrying an identifier keep the pill silhouette — they are objects, not
   statuses, and the different shape is the distinction. */
.tract-chip, .unit-chip, .dc-tract-chip, .case-file-chip, .tc-chip {
  border-radius: var(--radius-pill); font-size: var(--text-sm); padding: var(--space-2) var(--space-5);
}
.tract-chip { background: var(--ai-bg);   color: var(--ai-fg);   border-color: var(--ai-border); }
.unit-chip  { background: var(--info-bg); color: var(--info-fg); border-color: var(--info-border); }
.tc-remove, .uc-remove, .doc-alias-chip-rm, .sv-alias-x {
  cursor: pointer; opacity: 0.55; line-height: 1; margin-left: var(--space-1);
  background: none; border: none; color: inherit; font-size: var(--text-base); padding: 0;
}
.tc-remove:hover, .uc-remove:hover, .doc-alias-chip-rm:hover { opacity: 1; }

.org-role-badge {
  background: rgba(255,255,255,0.15); color: rgba(255,255,255,0.88);
  border-color: transparent; text-transform: uppercase; letter-spacing: var(--tracking-wide);
}


/* ══════════════════════════════════════════════════════════════════════════
   HIGH-STAKES SIGNALS
   Ranked above every decorative element in the product, because each answers a
   question where being wrong has a professional consequence.
   ══════════════════════════════════════════════════════════════════════════ */

/* ── AI-extracted values ──────────────────────────────────────────────────
   Was a 7%-opacity tint that disappeared on a laptop screen at an angle. Now a
   solid rail in a tone used nowhere else. These values feed a legal title chain;
   a landman must be able to see at a glance which of them a machine guessed at.
   The !important is inherited from the original rule and is still required —
   user-tracts.js writes an inline background on the same element. Removing that
   inline style at source (see NEEDS-JS-CHANGES.md) retires this !important. */
.ai-filled {
  background: var(--ai-bg) !important;
  box-shadow: inset 3px 0 0 var(--ai-rail);
  border-radius: var(--radius-xs);
}
.ai-banner {
  display: flex; align-items: center; gap: var(--space-4);
  padding: var(--space-4) var(--space-5); margin-top: var(--space-5);
  font-size: var(--text-sm);
  background: var(--ai-bg); color: var(--ai-fg);
  border: 1px solid var(--ai-border); border-radius: var(--radius-sm);
}

/* AI TRANSLATION. Related to .ai-banner and deliberately NOT the same thing.
   .ai-banner is transient advice about a fill you are about to review, and it goes away.
   This states a permanent property of the record — these words are a machine's rendering of
   another language — so it is rendered from the saved row every time the document is opened,
   and it carries the warning tone rather than the AI tone: the reader is being told not to
   mistake this for the instrument's own wording. */
.doc-xlate-banner {
  display: flex; align-items: center; gap: var(--space-4); flex-wrap: wrap;
  padding: var(--space-4) var(--space-5); margin-top: var(--space-5);
  font-size: var(--text-sm);
  background: var(--warning-bg); color: var(--warning-fg);
  border: 1px solid var(--warning-border); border-radius: var(--radius-sm);
}
.doc-xlate-chip {
  flex-shrink: 0; padding: var(--space-1) var(--space-3); border-radius: var(--radius-xs);
  background: var(--warning-fg); color: var(--bg-card);
  font-size: var(--text-2xs); font-weight: var(--weight-bold); letter-spacing: var(--tracking-wide);
}
.doc-xlate-txt { flex: 1; min-width: 0; }
.doc-xlate-toggle {
  flex-shrink: 0; cursor: pointer; font-family: inherit;
  padding: var(--space-2) var(--space-4); border-radius: var(--radius-btn, 8px);
  border: 1px solid var(--warning-border); background: var(--bg-card); color: var(--warning-fg);
  font-size: var(--text-xs); font-weight: var(--weight-semibold);
}
.doc-xlate-toggle:hover { background: var(--warning-bg); }
/* On the document card in the list — visible BEFORE opening, which is the point. */
.doc-xlate-badge {
  display: inline-flex; align-items: center; flex-shrink: 0;
  padding: var(--space-1) var(--space-3); border-radius: var(--radius-xs);
  background: var(--warning-bg); color: var(--warning-fg);
  border: 1px solid var(--warning-border);
  font-size: var(--text-2xs); font-weight: var(--weight-bold); letter-spacing: var(--tracking-wide);
}

/* Confidence. .confirmed means a human has checked it; .likely means the machine
   is not certain. The tones are success and warning, so the meaning is legible
   without reading the word. */
.ai-mark, .doc-aka-chip, .ow-alias-pill, .doc-entry-alias {
  display: inline-flex; align-items: center; gap: var(--space-2);
  font-size: var(--text-2xs); font-weight: var(--weight-bold);
  text-transform: uppercase; letter-spacing: var(--tracking-wide);
  padding: var(--space-1) var(--space-3); border-radius: var(--radius-xs);
  background: var(--ai-bg); color: var(--ai-fg); white-space: nowrap;
}
.ai-mark svg { width: 11px; height: 11px; fill: none; stroke: currentColor; stroke-width: 2.2; stroke-linecap: round; stroke-linejoin: round; }
.ai-mark.confirmed, .doc-aka-chip.confirmed, .ow-alias-pill.confirmed, .doc-entry-alias.confirmed {
  background: var(--success-bg); color: var(--ai-confirmed);
}
.ai-mark.likely, .doc-aka-chip.likely, .ow-alias-pill.likely, .doc-entry-alias.likely {
  background: var(--warning-bg); color: var(--ai-likely);
}
/* An AI-suggested party the system could not match to a known owner. Amber rail,
   distinct from the violet "AI filled this" rail — different problem. */
.doc-party-row.ai-nomatch .doc-party-input,
.ai-nomatch { box-shadow: inset 3px 0 0 var(--warning); background: var(--warning-bg); }
.ai-party-warn { display: flex; align-items: center; gap: var(--space-2); font-size: var(--text-xs); color: var(--warning-fg); margin-top: var(--space-1); }

/* ── Shared-across-organisations ──────────────────────────────────────────
   Owners and surveys are not scoped to one customer. Editing one changes it for
   every firm that touches that chain, which is a consequence a grey chip was not
   carrying. Its own tone, used for nothing else. */
.is-shared-record, .ow-doc-scope-badge.shared, .doc-cert-badge {
  background: var(--shared-bg); color: var(--shared-fg); border-color: var(--shared-border);
}
.doc-cert-badge {
  position: absolute; top: var(--space-2); right: var(--space-2);
  width: 16px; height: 16px; border-radius: var(--radius-circle);
  display: inline-flex; align-items: center; justify-content: center;
  font-size: var(--text-2xs); font-weight: var(--weight-bold); line-height: 1;
  background: var(--success); color: var(--text-on-dark); border: none;
  pointer-events: none;
}

/* ── Save state ───────────────────────────────────────────────────────────
   NEW. The requirement working window autosaves with no Save button, so today a
   landman changing a legal record gets no confirmation it persisted. Three
   states in one slot. Presentational only — it needs a JS hook to drive it.
   Replaces #tdAutoSaveStatus's opacity fade, which said nothing about outcome. */
.save-state {
  display: inline-flex; align-items: center; gap: var(--space-3);
  font-size: var(--text-sm); font-weight: var(--weight-medium);
  color: var(--text-muted); white-space: nowrap;
}
.save-state svg { width: 14px; height: 14px; fill: none; stroke: currentColor; stroke-width: 2; stroke-linecap: round; stroke-linejoin: round; }
.save-state.is-saving { color: var(--text-muted); }
.save-state.is-saved  { color: var(--success-fg); }
.save-state.is-error  { color: var(--danger-fg); font-weight: var(--weight-semibold); }
.save-state.is-saving svg { animation: sh-spin 0.9s linear infinite; }
@keyframes sh-spin { to { transform: rotate(360deg); } }

/* ── Load-bearing environment banners ─────────────────────────────────────
   Nobody may edit real title data believing it is a test, and nobody may be
   recorded without knowing. Both outrank every other layer. */
.sandbox-banner, .dr-capture-banner {
  display: flex; align-items: center; gap: var(--space-4);
  padding: var(--space-4) var(--gutter);
  font-size: var(--text-sm); font-weight: var(--weight-semibold);
  position: relative; z-index: var(--z-critical);
}
.sandbox-banner    { background: var(--sandbox-bg);   color: var(--sandbox-fg); }
.dr-capture-banner { background: var(--recording-bg); color: var(--recording-fg); border-bottom: none; }
.dr-capture-banner strong { color: inherit; font-weight: var(--weight-bold); }
.dr-capture-banner::before {
  content: ''; width: 9px; height: 9px; flex-shrink: 0;
  border-radius: var(--radius-circle); background: currentColor;
  animation: sh-pulse 1.6s ease-in-out infinite;
}
@keyframes sh-pulse { 0%, 100% { opacity: 1; } 50% { opacity: 0.3; } }

/* ── Is my text leaving the building? ─────────────────────────────────────
   A safety indicator, not a decorative chip. Internal and external deliberately
   do not share a colour family: you should not have to read the word. */
.chat-model-badge {
  display: inline-flex; align-items: center; gap: var(--space-3);
  font-size: var(--text-xs); font-weight: var(--weight-bold);
  text-transform: uppercase; letter-spacing: var(--tracking-wide);
  padding: var(--space-3) var(--space-5);
  border: 1px solid transparent; border-radius: var(--radius-sm);
  white-space: nowrap;
}
.chat-model-badge svg { width: 13px; height: 13px; fill: none; stroke: currentColor; stroke-width: 2.4; stroke-linecap: round; stroke-linejoin: round; }
.chat-model-badge.secure    { background: var(--success-bg); color: var(--success-fg); border-color: var(--success-border); }
.chat-model-badge.connected { background: var(--warning-bg); color: var(--warning-fg); border-color: var(--warning-border); }
.ai-conn-status-chip.always { background: var(--success-bg); color: var(--success-fg); border: 1px solid var(--success-border); }
.ai-conn-check { color: var(--success-fg); font-weight: var(--weight-bold); font-size: var(--text-sm); }

.chat-incognito-btn.active { background: var(--ai-bg); border-color: var(--ai-border); color: var(--ai-fg); }
.chat-incognito-btn.active svg { stroke: currentColor; }


/* ══════════════════════════════════════════════════════════════════════════
   TABLES
   The densest surface in the product and the one that most repays discipline.
   ══════════════════════════════════════════════════════════════════════════ */

.table-wrap, #tractTableWrap, #ownersTableWrap {
  background: var(--bg-card); border: 1px solid var(--border);
  border-radius: var(--radius-card); box-shadow: var(--shadow-card);
  overflow-x: auto; overflow-y: hidden; -webkit-overflow-scrolling: touch;
}

/* .rt-table is NOT in these two groups, deliberately. run-title.js owns the runsheet table and
   sets `border-collapse:separate; border-spacing:0` — the companion `position:sticky` on <th>
   needs, since a collapsed border belongs to the table rather than the cell and the sticky header
   comes apart from it while scrolling. Grouping it here forced `collapse` back on (this file loads
   after the component's injected <style>), which is what made the column header float with rows
   passing above and behind it.

   The `white-space: nowrap` below is the second half of it: run-title.js switched to
   `table-layout:fixed` + a colgroup precisely BECAUSE nowrap on the headers made every label a hard
   minimum width and let the headers, not the data, size the columns. See the comment above
   `.rt-table` in run-title.js.

   `.rt-table td` stays in the tabular-nums rule at the top of this file — that one is wanted. */
.list-table, .td-doc-table, .sv-doc-table, .tc-table {
  width: 100%; border-collapse: collapse; font-size: var(--text-base);
}

.list-table th, .td-doc-table th, .sv-doc-table th, #tractTableWrap th {
  text-align: left; white-space: nowrap;
  padding: var(--space-4) var(--space-5);
  font-size: var(--text-2xs); font-weight: var(--weight-bold);
  text-transform: uppercase; letter-spacing: var(--tracking-wide);
  color: var(--text-muted); background: var(--bg-sunken);
  border-bottom: 1px solid var(--border);
}
.list-table td, .td-doc-table td, .sv-doc-table td, #tractTableWrap td {
  padding: var(--row-py) var(--space-5);
  border-bottom: 1px solid var(--border-subtle);
  color: var(--text-primary); vertical-align: middle;
}
.list-table tbody tr:last-child td, #tractTableWrap tr:last-child td { border-bottom: none; }
@media (hover: hover) {
  .list-table tbody tr:hover td, .td-doc-table tbody tr:hover,
  #tractTableWrap tr:hover td { background: var(--bg-hover); }
}
.row-clickable, .td-doc-table tbody tr, #tractTableWrap tbody tr, .clickable { cursor: pointer; }
.list-muted, .td-muted, .dc-muted, .sv-muted, .dcmp-muted { color: var(--text-muted); font-size: var(--text-sm); }
.list-title { font-weight: var(--weight-semibold); }
.list-actions { display: flex; gap: var(--space-2); justify-content: flex-end; }

/* Selected rows use a left rail rather than a full-row tint: a tinted row is
   easily confused with hover, and bulk delete is not a thing to be unsure about. */
tr.row-selected td, tr.selected td, .tract-selected {
  background: var(--info-bg);
  box-shadow: inset 3px 0 0 var(--blue-600);
}

/* Sorting. The arrow was a ::after glyph at 0.3 opacity — effectively invisible. */
.sortable-th, th[data-sort], th[data-me-sort] { cursor: pointer; user-select: none; white-space: nowrap; }
@media (hover: hover) { .sortable-th:hover, th[data-sort]:hover, th[data-me-sort]:hover { background: var(--bg-subtle); color: var(--text-primary); } }
th[data-sort]::after, th[data-me-sort]::after { content: ' \2195'; opacity: 0.4; font-weight: var(--weight-bold); }
th[data-sort].sort-asc::after,  th[data-me-sort].sort-asc::after  { content: ' \2191'; opacity: 1; }
th[data-sort].sort-desc::after, th[data-me-sort].sort-desc::after { content: ' \2193'; opacity: 1; }
th[data-sort].sort-asc, th[data-sort].sort-desc,
th[data-me-sort].sort-asc, th[data-me-sort].sort-desc,
.sortable-th.sort-asc, .sortable-th.sort-desc {
  background: var(--info-bg); color: var(--info-fg);
}
.sort-icon, .ow-sort-arrow, .dc-sort-arrow, .sv-sort-arrow { font-size: var(--text-xs); color: var(--text-muted); margin-left: var(--space-2); }
.sortable-th.sort-asc .sort-icon, .sortable-th.sort-desc .sort-icon { color: var(--info-fg); }

/* Column reorder by drag. */
th[data-col-key] { cursor: grab; user-select: none; }
th[data-col-key]:active { cursor: grabbing; }
th.col-drag-over, .col-drag-over { outline: 2px solid var(--blue-500); outline-offset: -2px; }
th.col-dragging, .col-dragging, .dragging, .kanban-dragging, .dcmp-dragging { opacity: 0.45; }
.drag-over { outline: 2px solid var(--blue-600); border-radius: var(--radius-sm); }

/* Inline cell editing. The !important is inherited: list renderers write a
   padding inline on the same cell. */
td[data-edit-key]:not(.cell-editing) { cursor: text; }
@media (hover: hover) { td[data-edit-key]:not(.cell-editing):hover { background: var(--bg-hover); box-shadow: inset 0 0 0 1px var(--border-strong); } }
td.cell-editing { padding: var(--space-1) !important; background: var(--info-bg); }
td.cell-editing input, td.cell-editing select {
  width: 100%; padding: var(--space-3) var(--space-4);
  border: 1px solid var(--border-focus); border-radius: var(--radius-xs);
  background: var(--bg-card); color: var(--text-primary);
  font-size: inherit; font-family: inherit; outline: none;
  box-shadow: var(--focus-ring);
}

/* Column picker */
.col-select-wrap { position: relative; }
.col-select-panel {
  display: none; position: absolute; right: 0; top: calc(100% + var(--space-2));
  min-width: 180px; max-height: 300px; overflow-y: auto;
  padding: var(--space-4) var(--space-5);
  background: var(--bg-card); border: 1px solid var(--border);
  border-radius: var(--radius-md); box-shadow: var(--shadow-popover);
  z-index: var(--z-panel);
}
.col-select-panel.open { display: flex; flex-direction: column; gap: var(--space-4); }
.col-select-item {
  display: flex; align-items: center; gap: var(--space-4);
  font-size: var(--text-sm); color: var(--text-primary);
  cursor: pointer; white-space: nowrap; min-height: 28px;
}
input[type="checkbox"], input[type="radio"] { accent-color: var(--blue-600); cursor: pointer; }
[data-theme="dark"] input[type="checkbox"], [data-theme="dark"] input[type="radio"] { color-scheme: dark; }

/* Pagination */
.lp-nav { display: flex; align-items: center; gap: var(--space-3); flex-wrap: wrap; padding: var(--space-5) 0; }
.lp-showing { font-size: var(--text-sm); color: var(--text-muted); margin-right: auto; }
.lp-btn { min-height: var(--target-min-dense); min-width: var(--target-min-dense); padding: var(--space-3) var(--space-4); }
.lp-btn.active { background: var(--blue-700); border-color: var(--blue-700); color: var(--text-on-accent); }
.lp-gap { color: var(--text-muted); padding: 0 var(--space-2); }


/* ══════════════════════════════════════════════════════════════════════════
   FORMS
   ══════════════════════════════════════════════════════════════════════════ */

.form-group { display: flex; flex-direction: column; gap: var(--space-3); }
.form-row { display: grid; grid-template-columns: 1fr 1fr; gap: var(--space-5); }

.form-label, .doc-lbl, .adv-section-label, .filter-label, .ow-section-title,
.sp-org-admin-nav-section, .pane-section-prefix, .collapse-title,
.task-detail-field-label, .detail-label, .atd-meta-label, .dtf-lbl, .plot-lbl, .dr-lbl {
  font-size: var(--text-2xs); font-weight: var(--weight-bold);
  color: var(--text-muted);
  text-transform: uppercase; letter-spacing: var(--tracking-wide);
  line-height: var(--leading-tight);
}

.form-input, .form-select, .form-textarea, .toolbar-search, .adv-date-input,
.ow-rel-input, .ow-note-textarea, .ow-notes-filter, .doc-party-input,
.doc-alias-add-input, .key-input, .cs-face, .plot-in, .plot-ta, .dr-in, .dc-ref-input {
  font-family: inherit; font-size: var(--text-md);
  color: var(--text-primary); background: var(--bg-input);
  padding: var(--space-4) var(--space-5);
  min-height: var(--target-min-dense);
  border: 1px solid var(--border); border-radius: var(--radius-input);
  outline: none; width: 100%;
  transition: border-color var(--dur-fast) var(--ease-out), box-shadow var(--dur-fast) var(--ease-out);
}
.form-input::placeholder, .form-textarea::placeholder, .toolbar-search::placeholder { color: var(--text-muted); }
.form-input:focus, .form-select:focus, .form-textarea:focus, .toolbar-search:focus,
.adv-date-input:focus, .doc-party-input:focus, .key-input:focus {
  border-color: var(--border-focus); box-shadow: var(--focus-ring);
}
.form-input:disabled, .form-input[readonly] {
  background: var(--bg-subtle); color: var(--text-muted); cursor: default;
}
.form-input.invalid, .form-input[aria-invalid="true"] {
  border-color: var(--danger); box-shadow: 0 0 0 3px var(--danger-bg);
}

/* The chevron is drawn with currentColor via a data URI, so it cannot go dark-on-
   dark the way the old hard-coded #7a90a1 arrow did. */
.form-select, .dc-filter-select, .sv-filter-select, .ow-filter-select {
  appearance: none; -webkit-appearance: none; cursor: pointer;
  padding-right: var(--space-9);
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 24 24' fill='none' stroke='currentColor' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='6 9 12 15 18 9'/%3E%3C/svg%3E");
  background-repeat: no-repeat; background-position: right var(--space-4) center;
}
.form-textarea { resize: vertical; min-height: 90px; line-height: var(--leading-normal); }
.form-textarea.auto-grow { resize: none; overflow: hidden; }
.form-hint, .form-error, .dtf-hint, .dr-hint, .plot-hint, .sp-toggle-desc, .obj-note {
  font-size: var(--text-xs); color: var(--text-muted); line-height: var(--leading-normal);
}
.form-error { color: var(--danger-fg); display: none; }
.form-error.visible { display: block; }

/* Search with a leading icon */
.toolbar-search-wrap, .banner-search-wrap { position: relative; }
.toolbar-search-wrap svg {
  position: absolute; left: var(--space-5); top: 50%; transform: translateY(-50%);
  width: 15px; height: 15px; pointer-events: none;
  fill: none; stroke: var(--text-muted); stroke-width: 2; stroke-linecap: round; stroke-linejoin: round;
}
.toolbar-search { padding-left: var(--space-9); }

/* Toggle switch */
.toggle-switch { position: relative; display: inline-block; width: 44px; height: 24px; flex-shrink: 0; }
.toggle-switch input { position: absolute; opacity: 0; width: 0; height: 0; }
.toggle-slider {
  position: absolute; inset: 0; cursor: pointer;
  background: var(--border-strong); border-radius: var(--radius-pill);
  transition: background var(--dur-base) var(--ease-out);
}
.toggle-slider::before {
  content: ''; position: absolute; width: 18px; height: 18px; left: 3px; bottom: 3px;
  background: var(--bg-card); border-radius: var(--radius-circle);
  box-shadow: 0 1px 3px rgba(0,0,0,0.25);
  transition: transform var(--dur-base) var(--ease-out);
}
input:checked + .toggle-slider { background: var(--blue-600); }
input:checked + .toggle-slider::before { transform: translateX(20px); }
.toggle-switch input:focus-visible + .toggle-slider { box-shadow: var(--focus-ring); }

/* Fractional interest entry — the numerator/denominator control. Its parts must
   read as one object, so the group carries the border and the members do not. */
.doc-pct-wrap { display: inline-flex; align-items: stretch; flex-shrink: 0; border: 1px solid var(--border); border-radius: var(--radius-sm); overflow: hidden; background: var(--bg-input); }
.doc-pct-wrap:focus-within { border-color: var(--border-focus); box-shadow: var(--focus-ring); }
.doc-pct-mode, .doc-royalty-mode {
  display: flex; align-items: center; padding: 0 var(--space-4);
  font-size: var(--text-2xs); font-weight: var(--weight-bold);
  background: var(--bg-subtle); color: var(--text-muted);
  border: none; border-right: 1px solid var(--border); cursor: pointer; white-space: nowrap;
}
.doc-pct-mode.frac-active, .doc-royalty-mode.frac-active { background: var(--warning-bg); color: var(--warning-fg); }
.doc-party-pct, .doc-party-royalty, .doc-pct-fnum, .doc-pct-fden,
.doc-royalty-fnum, .doc-royalty-fden {
  border: none; background: transparent; min-height: auto;
  padding: var(--space-3) var(--space-3); font-size: var(--text-base); text-align: center;
  -moz-appearance: textfield;
}
.doc-party-pct, .doc-party-royalty { width: 58px; text-align: right; }
.doc-pct-fnum, .doc-pct-fden, .doc-royalty-fnum, .doc-royalty-fden { width: 34px; }
.doc-pct-sep, .doc-royalty-sep { display: flex; align-items: center; padding: 0 var(--space-1); color: var(--text-muted); font-weight: var(--weight-bold); }
.doc-party-pct::-webkit-inner-spin-button, .doc-party-pct::-webkit-outer-spin-button,
.doc-pct-fnum::-webkit-inner-spin-button, .doc-pct-fnum::-webkit-outer-spin-button,
.doc-pct-fden::-webkit-inner-spin-button, .doc-pct-fden::-webkit-outer-spin-button,
.doc-party-royalty::-webkit-inner-spin-button, .doc-party-royalty::-webkit-outer-spin-button,
.doc-royalty-fnum::-webkit-inner-spin-button, .doc-royalty-fnum::-webkit-outer-spin-button,
.doc-royalty-fden::-webkit-inner-spin-button, .doc-royalty-fden::-webkit-outer-spin-button,
#docPercentage::-webkit-inner-spin-button, #docPercentage::-webkit-outer-spin-button { display: none; }
#docPercentage { -moz-appearance: textfield; }

/* Drop zones */
#docFileDrop, #caseFileDrop, #tractImportDropZone {
  padding: var(--space-7) var(--space-6); text-align: center; cursor: pointer;
  border: 2px dashed var(--border-strong); border-radius: var(--radius-md);
  background: var(--bg-input);
  transition: border-color var(--dur-fast) var(--ease-out), background var(--dur-fast) var(--ease-out);
}
@media (hover: hover) { #docFileDrop:hover, #caseFileDrop:hover, #tractImportDropZone:hover { border-color: var(--blue-500); background: var(--info-bg); } }

/* Custom select (cs-*) */
.cs-wrap { position: relative; display: inline-flex; align-items: center; }
.cs-face { min-width: 120px; max-width: 240px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; user-select: none; padding-right: var(--space-8); }
.cs-face::after {
  content: ''; position: absolute; right: var(--space-4); top: 50%; transform: translateY(-50%);
  border-left: 4px solid transparent; border-right: 4px solid transparent;
  border-top: 5px solid var(--text-muted); pointer-events: none;
}
.cs-menu {
  position: absolute; top: calc(100% + var(--space-2)); left: 0; min-width: 100%;
  max-height: 300px; overflow-y: auto;
  background: var(--bg-card); border: 1px solid var(--border);
  border-radius: var(--radius-md); box-shadow: var(--shadow-popover);
  z-index: var(--z-panel);
}
.cs-opt, .doc-owner-opt, .tract-picker-option, .unit-picker-option, .ow-rel-dd-opt {
  padding: var(--space-4) var(--space-5); font-size: var(--text-base);
  color: var(--text-primary); cursor: pointer; white-space: nowrap;
  min-height: var(--target-min-dense); display: flex; align-items: center; justify-content: space-between; gap: var(--space-4);
}
@media (hover: hover) { .cs-opt:hover, .doc-owner-opt:hover, .tract-picker-option:hover, .unit-picker-option:hover, .ow-rel-dd-opt:hover { background: var(--bg-hover); } }
.cs-opt.cs-cur { color: var(--info-fg); font-weight: var(--weight-semibold); background: var(--info-bg); }
.doc-owner-opt.new { color: var(--info-fg); font-weight: var(--weight-semibold); }
.tract-picker-empty, .unit-picker-empty, .dtf-none, .dr-none, .dr-empty {
  padding: var(--space-4) var(--space-5); font-size: var(--text-sm);
  color: var(--text-muted); font-style: italic;
}
.tract-picker, .unit-picker { display: flex; flex-direction: column; gap: var(--space-3); }
.tract-picker-search-row, .unit-picker-search-row { display: flex; gap: var(--space-3); }
.tract-picker-dropdown, .unit-picker-dropdown, .doc-owner-dd, .ow-rel-dd {
  max-height: 200px; overflow-y: auto;
  background: var(--bg-card); border: 1px solid var(--border);
  border-radius: var(--radius-md); box-shadow: var(--shadow-popover);
}
.doc-owner-dd, .ow-rel-dd { position: absolute; top: calc(100% + var(--space-1)); left: 0; right: 0; z-index: var(--z-drawer); display: none; }
.tract-picker-selected, .unit-picker-selected { display: flex; flex-wrap: wrap; gap: var(--space-3); min-height: 24px; }
.tract-picker-create { display: flex; flex-direction: column; gap: var(--space-3); padding: var(--space-5); background: var(--bg-subtle); border: 1px solid var(--border); border-radius: var(--radius-md); }


/* ══════════════════════════════════════════════════════════════════════════
   CARDS
   ══════════════════════════════════════════════════════════════════════════ */

.project-card, .task-card, .tract-card, .kanban-task-card, .sp-card,
.settings-card, .doc-party-card, .platform-card, .owner-card, .rqc-card, .dr-card {
  background: var(--bg-card); border: 1px solid var(--border);
  border-radius: var(--radius-card); box-shadow: var(--shadow-card);
  position: relative; overflow: hidden;
  transition: box-shadow var(--dur-base) var(--ease-out),
              transform var(--dur-base) var(--ease-out),
              border-color var(--dur-fast) var(--ease-out);
}
.project-card, .settings-card, .sp-card { padding: var(--space-6); display: flex; flex-direction: column; gap: var(--space-5); }
.task-card, .tract-card, .kanban-task-card, .doc-party-card { padding: var(--space-5); display: flex; flex-direction: column; gap: var(--space-4); }

/* The 4px accent stripe on every project card was the loudest colour on a screen
   that also has to show a Priority defect. Reduced to 2px so the hierarchy is
   status first, decoration second. */
.project-card::before {
  content: ''; position: absolute; top: 0; left: 0; right: 0; height: 2px;
  background: var(--card-accent, var(--blue-600));
}
.kanban-task-card::before {
  content: ''; position: absolute; top: 0; left: 0; right: 0; height: 2px;
  background: var(--kcard-accent, var(--blue-600));
}
@media (hover: hover) {
  .project-card.clickable:hover, .task-card.clickable:hover,
  .tract-card:hover, .kanban-task-card:hover, .platform-card.live:hover {
    box-shadow: var(--shadow-card-hover); transform: translateY(-2px); border-color: var(--border-strong);
  }
}
.card-header { display: flex; align-items: flex-start; justify-content: space-between; gap: var(--space-4); }
.card-title, .list-title, .sp-card-heading, .card-heading, .doc-section-title, .tract-num {
  font-size: var(--text-lg); font-weight: var(--weight-semibold);
  color: var(--text-primary); line-height: var(--leading-tight);
}
.sp-card-heading, .card-heading {
  font-size: var(--text-md);
  padding-bottom: var(--space-5); margin-bottom: var(--space-5);
  border-bottom: 1px solid var(--border-subtle);
}
.card-meta, .task-card-meta { display: flex; align-items: center; gap: var(--space-3); flex-wrap: wrap; }
.card-detail { display: flex; flex-direction: column; gap: var(--space-3); }
.card-detail-row { display: flex; align-items: baseline; gap: var(--space-4); font-size: var(--text-base); }
.detail-label { min-width: 76px; flex-shrink: 0; }
.detail-value { color: var(--text-secondary); line-height: var(--leading-snug); }
.card-scope { font-size: var(--text-base); color: var(--text-secondary); line-height: var(--leading-normal); display: -webkit-box; -webkit-line-clamp: 3; -webkit-box-orient: vertical; overflow: hidden; }
.card-amount { font-size: var(--text-xl); font-weight: var(--weight-bold); color: var(--text-primary); font-variant-numeric: tabular-nums; }
.card-cost, .ktc-expense { font-size: var(--text-md); font-weight: var(--weight-bold); color: var(--warning-fg); font-variant-numeric: tabular-nums; }
.card-actions { display: flex; gap: var(--space-2); flex-shrink: 0; }
.project-grid, .tasks-cards-grid, .tract-grid, .platform-grid {
  display: grid; grid-template-columns: repeat(auto-fill, minmax(300px, 1fr)); gap: var(--space-6);
}
.tasks-cards-grid, .tract-grid { grid-template-columns: repeat(auto-fill, minmax(260px, 1fr)); gap: var(--space-5); }


/* ══════════════════════════════════════════════════════════════════════════
   BANNER, USER MENU, ORG SWITCHER
   The banner is dark in every theme, so its internals use fixed alpha values on
   white rather than tokens — those are relationships to the banner, not to the
   page, and they must hold when --bg-banner changes from navy to green.
   ══════════════════════════════════════════════════════════════════════════ */

.banner {
  display: flex; align-items: center; gap: var(--space-5);
  height: 64px; padding: 0 var(--gutter);
  background: var(--bg-banner); box-shadow: var(--shadow-banner);
  position: sticky; top: 0;
  /* ABOVE the tract working view (200) so the user dropdown, which hangs below
     the banner, is never clipped by it. This relationship fixes a real bug. */
  z-index: var(--z-banner);
}
.banner-logo { display: flex; align-items: center; gap: var(--space-4); flex-shrink: 0; }
.banner-icon { width: 34px; height: 34px; border-radius: var(--radius-md); object-fit: cover; flex-shrink: 0; display: block; }
.banner-spacer { flex: 1; }
.banner-date { font-size: var(--text-xs); font-weight: var(--weight-medium); color: rgba(255,255,255,0.68); white-space: nowrap; }
.banner-actions { display: flex; align-items: center; gap: var(--space-4); flex-shrink: 0; }
.banner-actions-top { display: flex; align-items: center; gap: var(--space-3); }

/* The wordmark is fitted at runtime by _fitBannerBrand, which measures the
   rendered glyphs and overwrites font-size on load. So the size lives in
   --brand-scale on .banner-logo and NOT in a font-size here — a fixed size would
   be silently replaced. Only the treatment is set below. */
.banner-logo { --brand-scale: 0.667; }
.banner-brand {
  font-size: 50px;                /* pre-paint estimate only; the script wins */
  line-height: 1;
  font-weight: var(--weight-semibold);
  font-style: italic;
  text-transform: uppercase;
  letter-spacing: 0.5px;
  white-space: nowrap;
  /* Exactly the logo's stripe colour (Mike), sampled from logo.png rather than eyeballed: the
     stripes are #e6e9ed across 53,914px, not pure white — against the #0d2a44 field a true
     #fff wordmark reads brighter than the mark it sits beside. */
  color: #e6e9ed;
  text-box: trim-both cap alphabetic;
  display: block; align-self: center;
}
@supports not (text-box: trim-both cap alphabetic) {
  .banner-brand { display: flex; align-items: center; height: 34px; }
}

.user-menu, .org-switcher { position: relative; flex-shrink: 0; }
.user-menu-chip, .org-chip {
  display: flex; align-items: center; gap: var(--space-3);
  padding: var(--space-2) var(--space-5) var(--space-2) var(--space-2);
  min-height: var(--target-min-dense);
  background: rgba(255,255,255,0.10); border: 1px solid rgba(255,255,255,0.16);
  border-radius: var(--radius-pill); cursor: pointer; user-select: none;
  transition: background var(--dur-fast) var(--ease-out);
}
@media (hover: hover) { .user-menu-chip:hover, .org-chip:hover { background: rgba(255,255,255,0.18); } }
.user-avatar {
  display: flex; align-items: center; justify-content: center; flex-shrink: 0;
  width: 26px; height: 26px; border-radius: var(--radius-circle);
  background: var(--blue-500); color: var(--text-on-dark);
  font-size: var(--text-2xs); font-weight: var(--weight-bold);
}
.user-name, .org-chip-name {
  font-size: var(--text-sm); font-weight: var(--weight-medium);
  color: rgba(255,255,255,0.88);
  max-width: 130px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}

.user-dropdown, .org-dropdown, .mw-more-menu, #mwMoreMenu, #mwMoreMenuMobile,
#tractMorePanel, #ownersMorePanel {
  display: none; position: absolute; top: calc(100% + var(--space-3)); right: 0;
  min-width: 230px; overflow: hidden;
  background: var(--bg-card); border: 1px solid var(--border);
  border-radius: var(--radius-md); box-shadow: var(--shadow-popover);
  z-index: var(--z-popover);
}
.user-dropdown.open, .org-dropdown.open { display: block; }
.user-dropdown a, .user-dropdown button, .org-dropdown-item, .org-dropdown-link {
  display: flex; align-items: center; gap: var(--space-4); width: 100%;
  padding: var(--space-4) var(--space-6); min-height: var(--target-min);
  font-family: inherit; font-size: var(--text-base); font-weight: var(--weight-medium);
  color: var(--text-secondary); background: transparent; border: none;
  text-align: left; text-decoration: none; cursor: pointer;
  transition: background var(--dur-fast) var(--ease-out);
}
@media (hover: hover) { .user-dropdown a:hover, .user-dropdown button:hover, .org-dropdown-item:hover { background: var(--bg-hover); color: var(--text-primary); } }
.user-dropdown svg, .org-dropdown-link svg {
  width: 15px; height: 15px; flex-shrink: 0; color: var(--text-muted);
  fill: none; stroke: currentColor; stroke-width: 2; stroke-linecap: round; stroke-linejoin: round;
}
.user-dropdown hr, .org-dropdown-divider { height: 1px; margin: var(--space-2) 0; border: none; background: var(--border-subtle); }
.user-dropdown-section-header {
  padding: var(--space-5) var(--space-6) var(--space-2);
  font-size: var(--text-2xs); font-weight: var(--weight-bold); color: var(--text-muted);
  text-transform: uppercase; letter-spacing: var(--tracking-wide); pointer-events: none;
}
/* Which panel you are in, and which ORG you are acting in. Getting the second one
   wrong means reading the wrong firm's data, so it is marked with a rail and a
   fill rather than a font-weight change. */
.user-dropdown a.is-current, .org-dropdown-item.is-active {
  background: var(--info-bg); color: var(--info-fg);
  font-weight: var(--weight-semibold);
  box-shadow: inset 3px 0 0 var(--blue-600);
}
.user-dropdown a.is-current svg { color: var(--info-fg); }
.org-item-name { font-size: var(--text-base); font-weight: var(--weight-semibold); color: var(--text-primary); flex: 1; }
.org-item-role {
  font-size: var(--text-2xs); font-weight: var(--weight-bold);
  padding: var(--space-1) var(--space-3); border-radius: var(--radius-xs);
  background: var(--info-bg); color: var(--info-fg); border: 1px solid var(--info-border);
  white-space: nowrap;
}
.org-dropdown-link { color: var(--ai-fg); font-weight: var(--weight-semibold); }
.org-chip-chevron { width: 12px; height: 12px; flex-shrink: 0; fill: none; stroke: rgba(255,255,255,0.6); stroke-width: 2; stroke-linecap: round; transition: transform var(--dur-base) var(--ease-out); }
.org-chip.open .org-chip-chevron { transform: rotate(180deg); }

/* Banner search */
.banner-search-wrap { flex: 1; max-width: 420px; margin: 0 auto; }
.banner-search-wrap svg { position: absolute; left: var(--space-5); top: 50%; transform: translateY(-50%); width: 16px; height: 16px; pointer-events: none; fill: none; stroke: rgba(255,255,255,0.55); stroke-width: 2; stroke-linecap: round; }
#searchInput {
  width: 100%; padding: var(--space-4) var(--space-5) var(--space-4) var(--space-9);
  font-size: var(--text-md); font-family: inherit; color: var(--text-on-dark);
  background: rgba(255,255,255,0.10); border: 1px solid rgba(255,255,255,0.16);
  border-radius: var(--radius-pill); outline: none;
  transition: background var(--dur-fast) var(--ease-out), border-color var(--dur-fast) var(--ease-out);
}
#searchInput::placeholder { color: rgba(255,255,255,0.45); }
#searchInput:focus { background: rgba(255,255,255,0.16); border-color: rgba(255,255,255,0.4); }

/* Commodity ticker */
.og-item { display: inline-flex; align-items: baseline; gap: var(--space-2); font-size: var(--text-xs); color: rgba(255,255,255,0.7); white-space: nowrap; }
.og-sym { font-weight: var(--weight-bold); letter-spacing: var(--tracking-wide); text-transform: uppercase; }
.og-px { font-variant-numeric: tabular-nums; color: var(--text-on-dark); }
.og-chg { font-variant-numeric: tabular-nums; font-weight: var(--weight-semibold); }


/* ══════════════════════════════════════════════════════════════════════════
   TAB BARS
   sticky-list.js measures the banner, tab bar, sub-tab bar and filter bar at
   runtime and sets an explicit height on each list's scroll container. It reads
   the live DOM, so it adapts to the heights below automatically — but only while
   these elements still exist, are still in this order, and are still sticky.
   ══════════════════════════════════════════════════════════════════════════ */

#tabsWrapper { position: sticky; top: 64px; z-index: var(--z-dropdown); }

.tabs-bar-wrap { position: relative; overflow: hidden; background: var(--bg-card); border-bottom: 1px solid var(--border); }
.tabs-bar {
  display: flex; align-items: stretch; gap: 0;
  padding: 0 var(--gutter);
  background: var(--bg-card); border-bottom: 1px solid var(--border);
  overflow-x: auto; -webkit-overflow-scrolling: touch; scrollbar-width: none;
}
.tabs-bar::-webkit-scrollbar { display: none; }
.tabs-bar-wrap .tabs-bar { border-bottom: none; }

.tab-btn, .ow-tab-btn, .pmt-btn, .obj-tab, .plot-side-tab, .tab {
  display: inline-flex; align-items: center; gap: var(--space-3);
  padding: var(--space-5) var(--space-6);
  font-family: inherit; font-size: var(--text-base); font-weight: var(--weight-semibold);
  color: var(--text-muted); background: transparent;
  border: none; border-bottom: 2px solid transparent;
  cursor: pointer; white-space: nowrap;
  transition: color var(--dur-fast) var(--ease-out), border-color var(--dur-fast) var(--ease-out);
}
.tab-btn svg, .ow-tab-btn svg { width: 16px; height: 16px; flex-shrink: 0; fill: none; stroke: currentColor; stroke-width: 2; stroke-linecap: round; stroke-linejoin: round; }
@media (hover: hover) { .tab-btn:hover, .ow-tab-btn:hover, .pmt-btn:hover, .tab:hover { color: var(--text-primary); } }
.tab-btn.active, .ow-tab-btn.active, .pmt-btn.active, .obj-tab.active, .tab.active {
  color: var(--blue-700); border-bottom-color: var(--blue-700);
}
[data-theme="dark"] .tab-btn.active, [data-theme="dark"] .ow-tab-btn.active,
[data-theme="dark"] .pmt-btn.active, [data-theme="dark"] .tab.active {
  color: var(--blue-600); border-bottom-color: var(--blue-600);
}
.tabs-bar-sub .tab-btn { padding: var(--space-4) var(--space-6); font-size: var(--text-sm); }
/* .tab-subbar is deliberately NOT in this rule. The Public Data and Tools strips use it, and it
   gave them a --bg-subtle panel plus a second layer of padding on top of .tab-subbar-wrap's own
   (hence 78px tall against the Curative bar's 63px). Mike wants all three sub-tab strips to read
   the same, and the Curative one — flat on the page background — is the reference.
   db-tabs.js owns .tab-subbar's layout; leave it there. */
#projectSubTabsBar, #dashNavBtns {
  background: var(--bg-subtle); border-bottom: 1px solid var(--border);
  padding: var(--space-4) var(--gutter); display: flex; gap: var(--space-4); flex-wrap: wrap; align-items: center;
}
.tab-pt-count, .ow-tab-cnt, .kanban-col-count, .task-count-badge {
  display: inline-flex; align-items: center; justify-content: center;
  min-width: 18px; height: 18px; padding: 0 var(--space-3);
  border-radius: var(--radius-pill);
  background: var(--neutral-bg); color: var(--text-muted);
  font-size: var(--text-2xs); font-weight: var(--weight-bold); line-height: 1;
}
.ow-tab-btn.active .ow-tab-cnt { background: var(--info-bg); color: var(--info-fg); }
.ow-tab-cnt:empty { display: none; }

/* Horizontal-overflow affordance. Kept, because on a phone the six-item tab bar
   genuinely overflows and there is otherwise nothing telling you so. */
.tabs-scroll-arrow {
  position: absolute; top: 0; bottom: 0; width: 48px;
  display: flex; align-items: center; justify-content: center;
  pointer-events: none; opacity: 0; z-index: 5;
  transition: opacity var(--dur-slow) var(--ease-out);
}
.tabs-scroll-arrow.visible { opacity: 1; pointer-events: auto; }
.tabs-scroll-left  { left: 0;  background: linear-gradient(to right, var(--bg-card) 45%, transparent); }
.tabs-scroll-right { right: 0; background: linear-gradient(to left,  var(--bg-card) 45%, transparent); }
.tabs-scroll-arrow svg { width: 16px; height: 16px; fill: none; stroke: var(--blue-600); stroke-width: 2.5; stroke-linecap: round; stroke-linejoin: round; }
.tabs-scroll-arrow.visible svg { animation: sh-scrollhint 2.5s ease-in-out infinite; }
@keyframes sh-scrollhint {
  0%, 100% { opacity: 0.25; transform: translateX(0); }
  50%      { opacity: 0.85; transform: translateX(3px); }
}
.tabs-scroll-left.visible svg { animation-name: sh-scrollhint-l; }
@keyframes sh-scrollhint-l {
  0%, 100% { opacity: 0.25; transform: translateX(0); }
  50%      { opacity: 0.85; transform: translateX(-3px); }
}


/* ══════════════════════════════════════════════════════════════════════════
   FILTER BAR
   ══════════════════════════════════════════════════════════════════════════ */

.filter-bar {
  display: flex; align-items: center; gap: var(--space-4); flex-wrap: wrap;
  padding: var(--space-5) var(--gutter);
  background: var(--bg-filter); border-bottom: 1px solid var(--border);
  transition: background var(--transition), border-color var(--transition);
}
.filter-divider, .mw-stype-sep, .chat-model-bar-sep {
  width: 1px; align-self: stretch; min-height: 22px;
  margin: 0 var(--space-3); background: var(--border); flex-shrink: 0;
}
.filter-toggle-btn {
  display: inline-flex; align-items: center; gap: var(--space-3);
  margin-left: auto; flex-shrink: 0;
  padding: var(--space-3) var(--space-5); min-height: var(--target-min-dense);
  font-family: inherit; font-size: var(--text-sm); font-weight: var(--weight-medium);
  background: var(--bg-card); color: var(--text-secondary);
  border: 1px solid var(--border); border-radius: var(--radius-pill);
  cursor: pointer; white-space: nowrap;
  transition: all var(--dur-fast) var(--ease-out);
}
.filter-toggle-btn svg { width: 14px; height: 14px; fill: none; stroke: currentColor; stroke-width: 2; stroke-linecap: round; stroke-linejoin: round; }
.filter-toggle-btn .toggle-chevron { transition: transform var(--dur-base) var(--ease-out); }
.filter-toggle-btn.open .toggle-chevron { transform: rotate(180deg); }
/* "You have filters applied that you cannot currently see." Worth a fill, not a
   border tint — a hidden active filter is how you end up misreading a list. */
.filter-toggle-btn.has-active {
  background: var(--info-bg); color: var(--info-fg); border-color: var(--info-border);
  font-weight: var(--weight-semibold);
}
.filter-active-dot { display: none; width: 6px; height: 6px; flex-shrink: 0; border-radius: var(--radius-circle); background: currentColor; }
.filter-toggle-btn.has-active .filter-active-dot { display: block; }

.adv-filter-panel {
  overflow: hidden; max-height: 0;
  background: var(--bg-filter);
  transition: max-height var(--dur-slow) var(--ease-out), background var(--transition);
}
.adv-filter-panel.open { max-height: 420px; }
.adv-filter-inner {
  display: flex; flex-direction: column; gap: var(--space-5);
  padding: var(--space-5) var(--gutter) var(--space-6);
  border-top: 1px solid var(--border); border-bottom: 1px solid var(--border);
}
.adv-filter-row { display: flex; align-items: center; gap: var(--space-4); flex-wrap: wrap; }
.adv-filter-group { display: flex; align-items: center; gap: var(--space-3); }
.adv-section-label { min-width: 84px; }
.adv-filter-sep { font-size: var(--text-sm); color: var(--text-muted); }
.adv-date-input { width: auto; min-width: 130px; font-size: var(--text-sm); }
.adv-clear-btn {
  margin-left: auto; padding: var(--space-3) var(--space-5);
  font-family: inherit; font-size: var(--text-sm); font-weight: var(--weight-medium);
  background: transparent; color: var(--text-muted);
  border: 1px solid var(--border); border-radius: var(--radius-pill);
  cursor: pointer; white-space: nowrap;
}
@media (hover: hover) { .adv-clear-btn:hover { border-color: var(--danger-border); color: var(--danger-fg); background: var(--danger-bg); } }

.results-bar { display: flex; align-items: center; gap: var(--space-5); margin-bottom: var(--space-6); }
.results-count, .results-bar-count, .tasks-toolbar-count { font-size: var(--text-sm); color: var(--text-muted); }


/* ══════════════════════════════════════════════════════════════════════════
   MODALS AND OVERLAYS
   ══════════════════════════════════════════════════════════════════════════ */

.modal-overlay, .confirm-overlay, .tos-overlay, .owner-sheet-overlay {
  position: fixed; inset: 0;
  display: flex; align-items: center; justify-content: center;
  padding: var(--space-6);
  background: var(--bg-overlay);
  opacity: 0; pointer-events: none;
  transition: opacity var(--dur-base) var(--ease-out);
  z-index: var(--z-modal);
}
.confirm-overlay { z-index: calc(var(--z-modal) + 10); }   /* a confirm can open over a modal */
.modal-overlay.open, .confirm-overlay.open, .tos-overlay.open, .owner-sheet-overlay.open { opacity: 1; pointer-events: all; }

/* .rt-modal and .tc-modal are NOT in this rule. Both size themselves — the runsheet is
   width:min(1800px,100%) (run-title.js:137, and the comment there explains why: it is a
   13-column legal abstract, and at a narrower width the Land Description cell wraps to fourteen
   lines and rows stand 200px tall), the title chain is min(760px,100%). Sweeping them in here
   applied max-width:560px on top, which silently capped both — a `width` declaration cannot beat
   a `max-width`. Mike: "the pop out is squeezed and too narrow", and the timeline inside it the
   same. They keep their own background/radius/shadow too, so nothing here was doing them any
   good. Sizing belongs with the component that knows its own content. */
.modal, .confirm-box, .tos-modal, .tos-gate-modal {
  width: 100%; max-width: 560px; max-height: 90vh; overflow-y: auto;
  background: var(--bg-modal); border: 1px solid var(--border);
  border-radius: var(--radius-lg); box-shadow: var(--shadow-modal);
  transform: scale(0.97) translateY(8px);
  transition: transform var(--dur-base) var(--ease-out);
}
.modal-overlay.open .modal, .confirm-overlay.open .confirm-box, .tos-overlay.open .tos-modal { transform: scale(1) translateY(0); }

.modal-header, .tos-header, .tos-gate-header {
  display: flex; align-items: center; justify-content: space-between; gap: var(--space-5);
  padding: var(--space-6) var(--space-7) var(--space-5);
  border-bottom: 1px solid var(--border-subtle);
}
.modal-title, .tos-header h3, .tos-gate-header h2 {
  display: flex; align-items: center; gap: var(--space-4);
  font-size: var(--text-lg); font-weight: var(--weight-semibold); color: var(--text-primary);
}
.modal-title-icon, .modal-logo {
  display: flex; align-items: center; justify-content: center; flex-shrink: 0;
  width: 34px; height: 34px; border-radius: var(--radius-sm);
  background: var(--info-bg);
}
.modal-title-icon svg { width: 17px; height: 17px; fill: none; stroke: var(--info-fg); stroke-width: 2; stroke-linecap: round; stroke-linejoin: round; }
.modal-subtitle { font-size: var(--text-xs); color: var(--text-muted); margin-top: 1px; }
.modal-body { display: flex; flex-direction: column; gap: var(--space-6); padding: var(--space-6) var(--space-7); }
.modal-footer {
  display: flex; align-items: center; justify-content: flex-end; gap: var(--space-4);
  padding: var(--space-5) var(--space-7) var(--space-6);
  border-top: 1px solid var(--border-subtle);
}

.confirm-box { max-width: 380px; padding: var(--space-8); text-align: center; }
.confirm-icon {
  display: flex; align-items: center; justify-content: center;
  width: 52px; height: 52px; margin: 0 auto var(--space-5);
  border-radius: var(--radius-circle); background: var(--danger-bg);
}
.confirm-icon svg { width: 24px; height: 24px; fill: none; stroke: var(--danger-fg); stroke-width: 2; stroke-linecap: round; stroke-linejoin: round; }
.confirm-box h3 { font-size: var(--text-lg); font-weight: var(--weight-semibold); margin-bottom: var(--space-4); }
.confirm-box p { font-size: var(--text-base); color: var(--text-secondary); line-height: var(--leading-normal); margin-bottom: var(--space-7); }
.confirm-actions { display: flex; gap: var(--space-4); justify-content: center; }

/* First-login ToS / password gates */
.tos-gate { position: fixed; inset: 0; display: flex; align-items: center; justify-content: center; padding: var(--space-6); background: var(--bg-overlay); z-index: var(--z-modal); }
.tos-gate-modal { display: flex; flex-direction: column; max-height: 88vh; }
.tos-gate-body, .tos-body {
  flex: 1; overflow-y: auto;
  padding: var(--space-6) var(--space-7);
  font-size: var(--text-base); color: var(--text-secondary); line-height: var(--leading-normal);
}
.tos-gate-body h4, .tos-body h4 {
  font-size: var(--text-2xs); font-weight: var(--weight-bold); color: var(--text-primary);
  text-transform: uppercase; letter-spacing: var(--tracking-wide);
  margin: var(--space-6) 0 var(--space-3);
}
.tos-gate-body h4:first-child, .tos-body h4:first-child { margin-top: 0; }
.tos-gate-body p, .tos-body p { margin-bottom: var(--space-4); }
.tos-gate-footer, .tos-footer { flex-shrink: 0; padding: var(--space-5) var(--space-7) var(--space-6); border-top: 1px solid var(--border-subtle); }
.tos-gate-btn { width: 100%; }

/* Toasts */
.toast, .plot-toast {
  position: fixed; bottom: var(--space-8); left: 50%;
  transform: translateX(-50%) translateY(80px);
  padding: var(--space-4) var(--space-7);
  font-size: var(--text-base); font-weight: var(--weight-semibold);
  background: var(--success); color: var(--text-on-dark);
  border-radius: var(--radius-pill); box-shadow: var(--shadow-popover);
  opacity: 0; white-space: nowrap; z-index: var(--z-toast);
  transition: opacity var(--dur-base) var(--ease-out), transform var(--dur-base) var(--ease-out);
}
.toast.show { opacity: 1; transform: translateX(-50%) translateY(0); }
.toast.error { background: var(--danger); }

/* Org notification banner. pointer-events:none on the shell so the tabs behind it
   stay usable; :auto only on the close control. Priority now maps to tones. */
#orgNotifBanner {
  position: fixed; top: 150px; left: 50%; transform: translateX(-50%);
  width: calc(100% - 32px); max-width: 520px;
  padding: var(--space-5) var(--space-6);
  font-size: var(--text-base); font-weight: var(--weight-medium);
  border: 1px solid transparent; border-radius: var(--radius-card);
  box-shadow: var(--shadow-popover); pointer-events: none;
  z-index: var(--z-toast);
}
#orgNotifBanner #orgNotifBannerClose { pointer-events: auto; cursor: pointer; }
#orgNotifBanner[data-priority="urgent"]    { background: var(--danger-bg);  border-color: var(--danger-border);  color: var(--danger-fg); }
#orgNotifBanner[data-priority="important"] { background: var(--warning-bg); border-color: var(--warning-border); color: var(--warning-fg); }
#orgNotifBanner[data-priority="routine"]   { background: var(--info-bg);    border-color: var(--info-border);    color: var(--info-fg); }
#tzMismatchBanner {
  position: fixed; top: 150px; left: 50%; transform: translateX(-50%);
  width: calc(100% - 32px); max-width: 580px;
  align-items: center; gap: var(--space-5);
  padding: var(--space-5) var(--space-6);
  font-size: var(--text-base);
  background: var(--info-bg); color: var(--info-fg);
  border: 1px solid var(--info-border); border-radius: var(--radius-card);
  box-shadow: var(--shadow-popover); pointer-events: none;
  z-index: calc(var(--z-toast) - 1);
}
#tzMismatchBanner button { pointer-events: auto; }


/* ══════════════════════════════════════════════════════════════════════════
   KANBAN
   ══════════════════════════════════════════════════════════════════════════ */

.kanban-board { display: flex; gap: var(--space-5); align-items: stretch; overflow-x: auto; padding-bottom: var(--space-6); height: calc(100vh - 290px); min-height: 300px; }
.kanban-col { display: flex; flex-direction: column; flex: 0 0 272px; min-width: 240px; overflow: hidden; background: var(--bg-subtle); border: 1px solid var(--border); border-radius: var(--radius-card); }
.kanban-col-header {
  display: flex; align-items: center; justify-content: space-between; flex-shrink: 0;
  padding: var(--space-4) var(--space-5);
  font-size: var(--text-2xs); font-weight: var(--weight-bold);
  text-transform: uppercase; letter-spacing: var(--tracking-wide);
  color: var(--text-secondary);
  background: var(--bg-card); border-bottom: 1px solid var(--border);
}
.kanban-col-body { display: flex; flex-direction: column; gap: var(--space-4); flex: 1; min-height: 0; padding: var(--space-4); overflow-y: auto; }
.kanban-col-body > * { flex-shrink: 0; }
.kanban-col-body.drag-over { background: var(--info-bg); outline: 2px dashed var(--blue-600); outline-offset: -4px; }
.kanban-empty, .kanban-col-hint { padding: var(--space-6) var(--space-4); text-align: center; font-size: var(--text-sm); color: var(--text-muted); }
.kanban-col-hint { font-style: italic; }
.ktc-title { font-size: var(--text-base); font-weight: var(--weight-semibold); line-height: var(--leading-tight); }
.ktc-desc { font-size: var(--text-sm); color: var(--text-secondary); line-height: var(--leading-snug); }
.ktc-meta { display: flex; flex-direction: column; gap: var(--space-1); font-size: var(--text-xs); color: var(--text-muted); }
.ktc-footer { display: flex; align-items: center; justify-content: space-between; gap: var(--space-3); padding-top: var(--space-4); border-top: 1px solid var(--border-subtle); }


/* ══════════════════════════════════════════════════════════════════════════
   CHAT
   ══════════════════════════════════════════════════════════════════════════ */

.chat-panel { display: flex; flex-direction: column; height: calc(100vh - 64px - 52px); max-width: 820px; margin: 0 auto; padding: 0 var(--gutter); }
.chat-messages { flex: 1; display: flex; flex-direction: column; gap: var(--space-5); padding: var(--space-8) 0 var(--space-6); overflow-y: auto; scroll-behavior: smooth; }
.chat-msg { display: flex; flex-direction: column; gap: var(--space-2); max-width: 78%; }
.chat-msg.user { align-self: flex-end; align-items: flex-end; }
.chat-msg.ai { align-self: flex-start; align-items: flex-start; }
.chat-bubble { padding: var(--space-4) var(--space-5); font-size: var(--text-md); line-height: var(--leading-normal); border-radius: var(--radius-card); word-break: break-word; white-space: pre-wrap; }
.chat-msg.user .chat-bubble { background: var(--blue-700); color: var(--text-on-accent); border-bottom-right-radius: var(--radius-xs); }
.chat-msg.ai .chat-bubble { background: var(--bg-card); color: var(--text-primary); border: 1px solid var(--border); border-bottom-left-radius: var(--radius-xs); }
.chat-typing .chat-bubble { display: flex; align-items: center; gap: var(--space-3); }
.chat-typing .dot { width: 7px; height: 7px; border-radius: var(--radius-circle); background: var(--text-muted); animation: sh-typing 1.2s infinite ease-in-out; }
.chat-typing .dot:nth-child(2) { animation-delay: 0.2s; }
.chat-typing .dot:nth-child(3) { animation-delay: 0.4s; }
@keyframes sh-typing { 0%, 60%, 100% { transform: translateY(0); opacity: 0.4; } 30% { transform: translateY(-5px); opacity: 1; } }
.chat-empty { flex: 1; display: flex; flex-direction: column; align-items: center; justify-content: center; gap: var(--space-5); padding: var(--space-10) var(--space-6); text-align: center; color: var(--text-muted); }
.chat-empty svg { width: 48px; height: 48px; fill: none; stroke: var(--border-strong); stroke-width: 1.5; }
.chat-input-row { display: flex; align-items: flex-end; gap: var(--space-4); padding: var(--space-5) 0; border-top: 1px solid var(--border); }
.chat-input-row textarea { flex: 1; max-height: 120px; resize: none; overflow-y: auto; line-height: var(--leading-normal); }
.chat-send-btn, .chat-stop-btn {
  display: flex; align-items: center; justify-content: center; flex-shrink: 0;
  width: 42px; height: 42px; border: none; border-radius: var(--radius-md);
  color: var(--text-on-dark); cursor: pointer;
}
.chat-send-btn { background: var(--blue-700); }
.chat-stop-btn { background: var(--danger); display: none; }
.chat-stop-btn.visible { display: flex; }
.chat-send-btn.hidden { display: none; }
.chat-send-btn:disabled { background: var(--border-strong); cursor: default; }
.chat-send-btn svg, .chat-stop-btn svg { width: 18px; height: 18px; fill: none; stroke: currentColor; stroke-width: 2; stroke-linecap: round; stroke-linejoin: round; }
.chat-model-bar { display: flex; align-items: center; gap: var(--space-4); flex-wrap: wrap; padding-bottom: var(--space-6); }
.chat-model-lbl { display: flex; align-items: center; gap: var(--space-2); flex-shrink: 0; font-size: var(--text-xs); font-weight: var(--weight-semibold); color: var(--text-muted); white-space: nowrap; }
.chat-model-select { width: auto; min-height: var(--target-min-dense); font-size: var(--text-sm); border-radius: var(--radius-pill); padding: var(--space-3) var(--space-8) var(--space-3) var(--space-5); }
.chat-incognito-btn, .chat-conn-link, .chat-action-btn {
  display: inline-flex; align-items: center; gap: var(--space-2);
  padding: var(--space-3) var(--space-5); min-height: var(--target-min-dense);
  font-family: inherit; font-size: var(--text-sm); font-weight: var(--weight-semibold);
  background: var(--bg-card); color: var(--text-muted);
  border: 1px solid var(--border); border-radius: var(--radius-pill);
  cursor: pointer; white-space: nowrap;
  transition: all var(--dur-fast) var(--ease-out);
}
.chat-incognito-btn svg, .chat-conn-link svg { width: 12px; height: 12px; flex-shrink: 0; fill: none; stroke: currentColor; stroke-width: 2; stroke-linecap: round; stroke-linejoin: round; }
.chat-bubble-actions { display: flex; gap: var(--space-3); margin-top: var(--space-2); opacity: 0; transition: opacity var(--dur-fast) var(--ease-out); }
.chat-msg.ai:hover .chat-bubble-actions, .chat-msg.ai:focus-within .chat-bubble-actions { opacity: 1; }
.chat-provider-attr { font-size: var(--text-2xs); color: var(--text-muted); font-style: italic; }

/* Assistant replies are rendered from Markdown at runtime, so these descendants
   cannot be given classes — they must be styled generically. */
.chat-md { white-space: normal; }
.chat-md p { margin: 0 0 var(--space-4); }
.chat-md p:last-child { margin-bottom: 0; }
.chat-md strong { font-weight: var(--weight-bold); }
.chat-md em { font-style: italic; }
.chat-md ul, .chat-md ol { margin: var(--space-2) 0 var(--space-4) var(--space-7); }
.chat-md li { margin-bottom: var(--space-2); }
.chat-md h2, .chat-md h3 { font-size: var(--text-md); font-weight: var(--weight-bold); margin: var(--space-5) 0 var(--space-3); }
.chat-md a { color: var(--info-fg); text-decoration: underline; }
.chat-md code { font-family: var(--font-mono); font-size: 0.9em; padding: 1px var(--space-3); background: var(--bg-input); border: 1px solid var(--border); border-radius: var(--radius-xs); }
.chat-md pre { margin: var(--space-4) 0; padding: var(--space-5); overflow-x: auto; background: var(--bg-input); border: 1px solid var(--border); border-radius: var(--radius-sm); }
.chat-md pre code { padding: 0; background: none; border: none; }
.chat-md table { width: 100%; border-collapse: collapse; margin: var(--space-4) 0; font-size: var(--text-sm); }
.chat-md th, .chat-md td { padding: var(--space-3) var(--space-4); border: 1px solid var(--border); text-align: left; }
.chat-md th { background: var(--bg-sunken); font-weight: var(--weight-bold); }
.chat-md blockquote { margin: var(--space-4) 0; padding-left: var(--space-5); border-left: 3px solid var(--border-strong); color: var(--text-secondary); }


/* ══════════════════════════════════════════════════════════════════════════
   EMPTY AND LOADING STATES
   An empty screen is an invitation to act, not a shrug.
   ══════════════════════════════════════════════════════════════════════════ */

.empty-state, .tasks-empty, .doc-empty-state, .td-placeholder, .rqc-empty {
  grid-column: 1 / -1;
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: var(--space-4); padding: var(--space-11) var(--space-6);
  text-align: center; color: var(--text-muted);
}
.empty-state svg, .tasks-empty svg { width: 48px; height: 48px; margin-bottom: var(--space-2); fill: none; stroke: var(--border-strong); stroke-width: 1.5; }
.empty-state h3, .tasks-empty h3 { font-size: var(--text-lg); font-weight: var(--weight-semibold); color: var(--text-secondary); }
.empty-state p, .tasks-empty p { font-size: var(--text-base); }

.tract-loading, .pp-bar { display: flex; flex-direction: column; align-items: center; justify-content: center; gap: var(--space-5); padding: var(--space-11) var(--space-6); }
/* 32px is a PAGE-LOADING spinner. `.rt-spin` (13px) and `.tc-spin` (12px) are INLINE spinners that
   sit inside a line of text on `vertical-align:-2px`, and run-title.js / title-chain.js size them
   themselves — grouping them here inflated both to 32px mid-sentence, because this file loads after
   those components' injected <style>. `.doc-ai-spinner` is the tell: it is in this group and then
   immediately overridden back down to 16px below, which is the patch that should have been a
   removal. Inline spinners stay with their component. */
.tract-loading-spinner, .spinner, .pp-spin, .doc-ai-spinner {
  width: 32px; height: 32px; flex-shrink: 0;
  border: 3px solid var(--border); border-top-color: var(--blue-600);
  border-radius: var(--radius-circle);
  animation: sh-spin 0.75s linear infinite;
}
.doc-ai-spinner { width: 16px; height: 16px; border-width: 2px; border-top-color: var(--ai-rail); }
.tract-loading-label, .pp-busy-label { font-size: var(--text-md); color: var(--text-muted); font-weight: var(--weight-medium); }


/* ══════════════════════════════════════════════════════════════════════════
   THE PHONE LAYER
   Everything below 768px. The dual-DOM filter bar swap is preserved exactly —
   collapsing it into one responsive bar is recommended in NEEDS-JS-CHANGES.md
   but is a JS change, not a CSS one.

   The !important declarations here are the legitimate kind: each one beats an
   inline style attribute written by a JS module, named in the comment above it.
   ══════════════════════════════════════════════════════════════════════════ */

@media (max-width: 768px) {
  /* Dual-DOM swap. Both halves are populated by the same render function; the
     phone "New/Add" buttons proxy-click the hidden desktop ones, which works
     precisely because a scripted .click() ignores display:none. */
  #mwFilterBar { display: none !important; }
  #mwFilterMobile { display: flex !important; }
  #meFilterBar { display: none !important; }
  #meFilterMobile { display: flex !important; }

  /* index.html writes grid-template-columns inline on these. */
  #panelDashboardOverview > div { grid-template-columns: minmax(0, 1fr) !important; padding: var(--space-6) var(--gutter-phone) var(--space-10) !important; overflow-x: hidden; }
  #ovStatCards { grid-template-columns: repeat(2, 1fr) !important; }
  #ovStatusBreakdown { grid-template-columns: 1fr !important; }

  /* The reminders rail carries the only time-critical content on the Dashboard.
     On a phone it would otherwise sit below five charts. */
  #panelDashboardOverview > div > div:last-child { order: -1 !important; }

  .project-grid { grid-template-columns: repeat(auto-fill, minmax(min(280px, 100%), 1fr)) !important; }
  .tasks-cards-grid { grid-template-columns: repeat(auto-fill, minmax(min(240px, 100%), 1fr)) !important; }
  .modal { max-width: 94vw !important; width: 94vw !important; }
  .kanban-board { height: calc(100vh - 200px) !important; }
  #taskDetailView { padding: var(--space-6) var(--gutter-phone) var(--space-10) !important; }
  #taskDetailView .collapse-body > div[style] { grid-template-columns: 1fr !important; }
  #mwResults, #meResults { overflow-x: auto; }

  /* Four pills wrapping 3+1 reads as a broken row, so the sub-tabs become a 2×2
     grid. The pill STYLE is shared with desktop; only the layout adapts. */
  #mwTypeBar { display: grid !important; grid-template-columns: 1fr 1fr !important; gap: var(--space-4) !important; padding: var(--space-5) var(--gutter-phone) !important; }
  #mwTypeBar .mw-type-btn { width: 100% !important; text-align: center !important; }
  /* The scope switch spans both columns above the sub-tabs — it governs them. */
  #mwTypeBar #mwScopeToggle { grid-column: 1 / -1 !important; }
  #mwTypeBar #mwScopeSep { display: none !important; }

  /* user-init.js writes the select background inline. */
  #mwMobileWorkPanel select, #mePayPeriodSelectMobile { width: 100% !important; }

  .sp-org-admin-layout { flex-direction: column; }
  .sp-content { padding: var(--space-6); max-width: 100%; }
}

@media (max-width: 640px) {
  .banner { height: auto; flex-wrap: nowrap; align-items: flex-start; padding: var(--space-5) var(--gutter-phone); gap: var(--space-4); }
  /* relative, not static: the banner must keep its z-index so the user dropdown
     still paints over the tract working view. */
  .banner { position: relative; }
  #tabsWrapper { top: 0; }
  .banner-icon { width: 28px; height: 28px; border-radius: var(--radius-sm); }
  .banner-brand { font-size: calc(28px / var(--brand-cap-ratio, 0.72)); height: 28px; letter-spacing: 0; }
  .banner-search-wrap { order: 3; width: 100%; max-width: 100%; }
  .banner-actions { flex-direction: column; align-items: flex-end; gap: var(--space-3); margin-left: auto; }
  .banner-actions-top { order: 1; }
  #bannerDate { order: 2; }

  .tabs-bar { padding: 0 var(--space-4); }
  .tab-btn { padding: var(--space-5) var(--space-5); font-size: var(--text-sm); }
  .filter-bar { padding: var(--space-4) var(--gutter-phone); }
  .filter-label, .filter-divider { display: none; }
  .filter-toggle-btn span.filter-toggle-text { display: none; }
  .adv-filter-inner { padding: var(--space-5) var(--gutter-phone); }
  .adv-section-label { min-width: unset; }
  .chat-panel { padding: 0 var(--space-5); }
  .chat-model-lbl { display: none; }
  .chat-msg { max-width: 90%; }
  .form-row { grid-template-columns: 1fr; }

  /* Settings becomes a drawer. The !importants beat inline styles written by
     user-settings.js when it opens the pane. */
  .sp-org-admin-layout { flex-direction: column !important; position: relative; }
  .sp-org-admin-nav {
    position: fixed !important; left: 0; top: 0; bottom: 0;
    width: min(288px, 84vw) !important; z-index: var(--z-drawer);
    display: block !important; flex-wrap: unset !important; gap: unset !important;
    padding: var(--space-4) 0 var(--space-9) !important;
    background: var(--bg-card); border-right: 1px solid var(--border); border-bottom: none !important;
    overflow-y: auto; overflow-x: hidden;
    transform: translateX(-100%);
    transition: transform var(--dur-slow) var(--ease-out);
  }
  .sp-org-admin-nav.mob-open { transform: translateX(0); box-shadow: var(--shadow-modal); }
  .sp-org-admin-nav-section { display: block !important; }
  .sp-nav-item { width: 100% !important; border-left: 3px solid transparent !important; border-bottom: none !important; padding: var(--space-5) var(--space-6) !important; font-size: var(--text-md) !important; min-height: var(--target-min); }
  .sp-nav-item.active { border-left-color: var(--blue-600) !important; border-bottom-color: transparent !important; }
  .sp-drawer-backdrop { display: none; position: fixed; inset: 0; background: var(--bg-overlay); z-index: calc(var(--z-drawer) - 1); }
  .sp-drawer-backdrop.mob-open { display: block; }
  .sp-mobile-back-bar { padding: var(--space-4) var(--gutter-phone); background: var(--bg-card); border-bottom: 1px solid var(--border); }
  .sp-pane-heading { display: none; }
  .sp-content { padding: var(--space-6) var(--gutter-phone) var(--space-10) !important; max-width: 100% !important; }
}

@media (max-width: 480px) {
  .project-grid, .tasks-cards-grid { grid-template-columns: 1fr !important; }
  .modal { max-width: 96vw !important; border-radius: var(--radius-card) !important; }
  .modal-body, .modal-header, .modal-footer { padding-left: var(--space-6) !important; padding-right: var(--space-6) !important; }
  /* Cells tighten horizontally, never in type size — the phone is the hard case
     for legibility, so shrinking text here would be exactly backwards. */
  .list-table th, .list-table td { padding: var(--space-4) var(--space-4) !important; }
}

/* A phone turned sideways: vertical space is the scarce axis, not horizontal. */
@media (orientation: landscape) and (max-height: 500px) {
  #panelDashboardOverview > div { grid-template-columns: minmax(0, 1fr) !important; padding: var(--space-6) var(--gutter-phone) var(--space-10) !important; }
  #ovStatCards { grid-template-columns: repeat(3, 1fr) !important; }
  #panelDashboardOverview > div > div:last-child { order: -1 !important; }
  #ovStatusBreakdown { grid-template-columns: 1fr 1fr !important; }
  #mwResults, #meResults { overflow-x: auto; }
  .banner { height: 52px; }
  .chat-panel { height: calc(100vh - 52px); }
}

@media (min-width: 641px) { #settingsPanelClose { display: none; } }
