/* ============================================================
   Paperserve ClientPulse — design system (M5)
   POC layout language (casm-client-pulse) re-tokened onto the
   official CASM palette (S10). No external requests: fonts and
   scripts are bundled under wwwroot (S3).
   ============================================================ */

/* ---- Open Sans, bundled locally (R2) ---- */
@font-face {
  font-family: "Open Sans";
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url("../fonts/open-sans-latin-400-normal.woff2") format("woff2");
}

@font-face {
  font-family: "Open Sans";
  font-style: italic;
  font-weight: 400;
  font-display: swap;
  src: url("../fonts/open-sans-latin-400-italic.woff2") format("woff2");
}

@font-face {
  font-family: "Open Sans";
  font-style: normal;
  font-weight: 700;
  font-display: swap;
  src: url("../fonts/open-sans-latin-700-normal.woff2") format("woff2");
}

/* ---- Brand tokens (S10) + derived system tokens ---- */
:root {
  --brand-primary: #1769a3;
  --brand-sky: #31aae1;
  --brand-teal: #6bcad5;
  --brand-coral: #ff6666;
  --brand-gray: #8b8d98;

  /* Shades of Primary Blue for chrome depth */
  --primary-700: #125a8d;
  --primary-800: #0e4a75;
  --primary-900: #0b3a5d;

  /* Wordmark navy sampled from the Paperserve letterhead — anchors the shell */
  --brand-navy: #205078;

  /* Vision-2031 rail navy — the one big dark mass that anchors every screen */
  --rail-top: #0c2c44;
  --rail-bottom: #0e3350;

  /* Neutrals (blue-tinted derivatives of Primary Blue — nothing pure grey) */
  --ink-strong: #0c2c44;     /* headings, KPI values */
  --ink: #1d3547;            /* body */
  --muted: #5f7789;          /* AA-safe secondary text, labels */
  --faint: #8fa3b2;          /* timestamps, tertiary */
  --line: #dfe8ee;
  --line-soft: #ecf2f5;      /* in-card dividers */
  --paper: #f2f6f8;
  --surface: #ffffff;
  --surface-tint: #fafcfd;

  /* Semantic (pill/alert) inks — dark enough for AA on their tints */
  --ok: #1a7a50;
  --warn: #915613;
  --danger-ink: #b03636;
  --danger-solid: #c23b3b;   /* deepened coral for solid destructive buttons */

  /* State tint pairs (pale fill + darkened ink) */
  --teal-bg: #e9f7f8;
  --teal-ink: #1d7f8c;       /* NON-STATUS chrome only — .tag and .tier-pill--3. Status teal is jade, below */
  --sky-bg: #e8f5fc;
  --sky-ink: #176e9e;
  --coral-bg: #ffefef;

  /* Jade pill tint (W2-21). Expansion opportunity's status colour is --seg-teal,
     so the PILL has to carry the same jade the bar segment does — one status is
     one colour whether pill or segment (DECISIONS thirty-second follow-up). The
     tint is a light step off that jade rather than a straight alpha of it: jade
     tops out at 4.88:1 on white, so a .13 alpha fill lands at 4.08:1 and misses
     AA for 12px bold. This value gives 4.54:1, level with --pill-green, and is
     an improvement on the cyan-teal it replaces (4.28:1, which never passed). */
  --jade-bg: #eef9f6;

  /* Solid data fills (distribution bars, meters). The pill vocabulary is the
     source of identity (S9) — these are the SOLID counterparts of the pill
     tones, so one status is one colour whether it appears as a pill or as a
     bar segment.

     VALIDATED, NOT CHOSEN BY EYE. The seven below are the ClientStatus
     categorical palette, checked with the dataviz skill's validate_palette.js
     (OKLab ΔE×100, Machado-Oliveira-Fernandes 2009 CVD sim at severity 1.0),
     light mode, surface #ffffff, in lifecycle adjacency order:
       violet · green · sky · jade · red · gray · amber
       PASS lightness band · PASS adjacent CVD (worst 9.1 deutan)
       PASS normal-vision floor (worst 15.1) · PASS contrast (all >= 3:1)
     Re-run it after ANY change here:
       node validate_palette.js \
         "#7250b5,#1a7a50,#1f95cc,#00806b,#c23b3b,#5f7789,#c8862a" \
         --mode light --surface "#ffffff"

     Per DECISIONS thirty-second follow-up, this vocabulary may leave the five
     CASM brand colours where distinguishability requires it; chrome and
     identity stay on brand. That is why --seg-sky exists alongside
     --brand-sky rather than reusing it.

     Two deliberate departures:
       - --seg-gray is intentionally below the chroma floor: Dormant is the
         de-emphasis case and is meant to read as grey.
       - Under --pairs all, jade/green fall to ΔE 3.8. Seven mutually distinct
         hues is not achievable once red, amber and grey are pinned by meaning
         (every alternative tested just moves the failure). The bar therefore
         keeps a FIXED lifecycle order so adjacency is the governing standard,
         and the legend carries labels and counts. Do not sort segments by
         count — that makes adjacency variable and no palette can satisfy it. */
  --seg-violet: #7250b5;     /* Prospect — re-stepped from #5d4787 (defined, not inherited) */
  --seg-sky: #1f95cc;        /* Implementation — darkened from --brand-sky, which failed contrast at 2.64:1 */
  --seg-teal: #00806b;       /* Expansion opportunity — re-stepped from #00728a, which failed the chroma floor */
  --seg-gray: #5f7789;       /* Dormant — deliberately neutral */
  --warn-solid: #c8862a;     /* Renewal due — amber solid; urgency meter, amber segment */
  --danger-deep: #8b1f1f;    /* deepest health band (not part of the seven) */

  /* Consolidation targets — one value per meaning (W1-33) */
  --ok-strong: #14603f;      /* pressed/hover step below --ok */
  --ok-bg: rgba(31, 138, 91, 0.13);
  --warn-bg: rgba(178, 106, 24, 0.14);
  --danger-strong: #a52f2f;  /* pressed step below --danger-solid */
  --ink-mid: #344456;        /* between --ink and --muted */
  --neutral-bg: #edf1f4;
  --neutral-ink: #4d6274;
  --rail-label: #6d94b0;     /* rail section labels only */
  --on-rail: #cfe1ee;        /* primary text on the dark rail */
  --on-rail-muted: #b9d2e4;  /* secondary text on the dark rail */

  --radius: 8px;
  --radius-lg: 10px;
  --shadow: 0 18px 45px rgba(11, 58, 93, 0.12);
  --shadow-card: 0 1px 2px rgba(12, 44, 68, 0.06), 0 4px 16px rgba(12, 44, 68, 0.05);
  --focus-ring: 0 0 0 3px rgba(49, 170, 225, 0.4);

  --sidebar-width: 264px;

  /* Content measures (W1-34) */
  --measure-page: 980px;
  --measure-wide: 860px;
  --measure-form: 760px;
  --measure-mid: 640px;
  --measure-narrow: 560px;

  /* Section rhythm (W1-34) */
  --space-section: 18px;

  /* Inline icon masks (no runtime requests, S3) */
  --icon-lock: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2.4' stroke-linecap='round' stroke-linejoin='round'%3E%3Crect x='4' y='10.5' width='16' height='11' rx='2'/%3E%3Cpath d='M8 10.5V7a4 4 0 0 1 8 0v3.5'/%3E%3C/svg%3E");
}

/* ---- Base ---- */
* {
  box-sizing: border-box;
}

html {
  height: 100%;
}

body {
  margin: 0;
  min-height: 100vh;
  background: var(--paper);
  color: var(--ink);
  font-family: "Open Sans", Arial, Helvetica, sans-serif;
  font-size: 14px;
  line-height: 1.45;
}

h1,
h2,
h3,
h4 {
  margin: 0;
  color: var(--ink-strong);
  font-weight: 700;
  line-height: 1.25;
}

p {
  margin: 0 0 10px;
}

a {
  color: var(--brand-primary);
  font-weight: 700;
  text-decoration: none;
}

a:hover {
  color: var(--primary-800);
  text-decoration: underline;
}

button,
input,
select,
textarea {
  font: inherit;
  color: inherit;
}

button {
  cursor: pointer;
}

code {
  font-family: Consolas, "Courier New", monospace;
  font-size: 13px;
}

hr {
  border: 0;
  border-top: 1px solid var(--line);
  margin: 20px 0;
}

:focus-visible {
  outline: none;
  box-shadow: var(--focus-ring);
  border-radius: 4px;
}

.sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}

.skip-link {
  position: absolute;
  left: -9999px;
  top: 0;
  z-index: 100;
  background: var(--surface);
  color: var(--brand-primary);
  padding: 10px 14px;
  border-radius: 0 0 var(--radius) 0;
}

.skip-link:focus {
  left: 0;
}

/* ============================================================
   App shell: CASM Primary Blue sidebar + paper canvas
   ============================================================ */
.app-shell {
  display: grid;
  grid-template-columns: var(--sidebar-width) minmax(0, 1fr);
  min-height: 100vh;
}

.sidebar {
  display: flex;
  flex-direction: column;
  gap: 18px;
  padding: 20px 18px 18px;
  background: linear-gradient(180deg, var(--rail-top) 0%, var(--rail-bottom) 100%);
  color: var(--on-rail);
}

.brand {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 9px;
  padding: 0 6px;
}

/* Blue-on-transparent wordmark sits on a light chip so it reads on the navy shell */
.brand-logo {
  display: block;
  padding: 10px 14px;
  border-radius: var(--radius);
  background: #ffffff;
  box-shadow: 0 1px 0 rgba(11, 58, 93, 0.25);
}

.brand-logo img {
  display: block;
  width: 176px;
  height: auto;
}

.brand-name {
  padding-left: 2px;
  font-size: 13px;
  font-weight: 700;
  letter-spacing: 0.02em;
  color: rgba(255, 255, 255, 0.82);
}

.nav-list {
  display: grid;
  gap: 2px;
  margin: 0;
  padding: 0;
  list-style: none;
}

.nav-section {
  margin: 12px 0 2px;
  padding: 0 12px;
  color: var(--rail-label);
  font-size: 10px;
  font-weight: 700;
  letter-spacing: 0.18em;
  text-transform: uppercase;
}

.nav-section:first-child {
  margin-top: 0;
}

.nav-item {
  position: relative;
  display: flex;
  align-items: center;
  gap: 12px;
  min-height: 36px;
  padding: 8px 12px;
  border-radius: var(--radius);
  color: var(--on-rail);
  font-weight: 700;
  text-decoration: none;
}

.nav-item svg {
  flex: 0 0 auto;
  width: 18px;
  height: 18px;
  stroke: var(--brand-teal);
}

.nav-item:hover {
  background: rgba(255, 255, 255, 0.07);
  color: #ffffff;
  text-decoration: none;
}

/* Active nav: sky-tinted fill + inset sky stripe (Vision-2031 rail language). */
.nav-item.active {
  background: rgba(49, 170, 225, 0.18);
  color: #ffffff;
  box-shadow: inset 3px 0 0 var(--brand-sky);
}

.nav-item.active svg,
.nav-item:hover svg {
  stroke: #ffffff;
}

.sidebar-foot {
  margin-top: auto;
  padding: 14px;
  border: 1px solid rgba(255, 255, 255, 0.12);
  border-radius: var(--radius);
  background: rgba(255, 255, 255, 0.05);
  color: var(--on-rail-muted);
  font-size: 12px;
  line-height: 1.5;
}

.sidebar-foot strong {
  display: block;
  color: #ffffff;
}

.sidebar-backdrop {
  display: none;
}

/* ---- Main column ---- */
.main-column {
  min-width: 0;
  display: flex;
  flex-direction: column;
}

.main {
  flex: 1;
  padding: 24px 26px 40px;
}

/* ---- Topbar ---- */
.topbar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 18px;
  padding: 18px 26px;
  border-bottom: 1px solid var(--line);
  background: var(--surface);
}

.topbar-title .eyebrow {
  margin-bottom: 3px;
}

.topbar-title h1 {
  font-size: 24px;
}

.topbar-actions {
  display: flex;
  align-items: center;
  justify-content: flex-end;
  gap: 12px;
  flex: 1;
  min-width: 0;
}

.search-wrap {
  flex: 0 1 380px;
  min-width: 160px;
}

.search-wrap input[type="search"] {
  background: var(--surface-tint);
}

.user-badge {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 6px 8px 6px 6px;
  border: 1px solid var(--line);
  border-radius: 999px;
  background: var(--surface);
  white-space: nowrap;
}

.user-avatar {
  display: grid;
  place-items: center;
  width: 30px;
  height: 30px;
  border-radius: 999px;
  background: var(--brand-primary);
  color: #ffffff;
  font-size: 12px;
  font-weight: 700;
  text-transform: uppercase;
}

.user-name {
  font-size: 13px;
  font-weight: 700;
  color: var(--ink-mid);
}

.signout-form {
  display: inline;
}

.nav-toggle {
  display: none;
  width: 44px;
  height: 44px;
  padding: 0;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: var(--surface);
}

.nav-toggle svg {
  width: 20px;
  height: 20px;
  stroke: var(--ink);
  vertical-align: middle;
}

/* ============================================================
   Typography helpers
   ============================================================ */
.eyebrow {
  margin: 0 0 5px;
  color: var(--muted);
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.16em;
  text-transform: uppercase;
}

.text-muted {
  color: var(--muted);
}

.text-danger {
  color: var(--danger-ink);
}

/* ============================================================
   Panels, cards, metrics
   ============================================================ */
.panel {
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  background: var(--surface);
  box-shadow: var(--shadow-card);
  padding: 18px;
  margin-bottom: 16px;
}

.panel-heading {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  padding-bottom: 12px;
  border-bottom: 1px solid var(--line-soft);
  margin-bottom: 14px;
}

.panel-heading h2,
.panel-heading h3 {
  font-size: 17px;
}

.panel-count {
  display: inline-flex;
  margin-top: 4px;
  color: var(--muted);
  font-size: 13px;
  font-weight: 700;
}

.metric-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(170px, 1fr));
  gap: 14px;
  margin-bottom: 16px;
}

.metric {
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  background: var(--surface);
  box-shadow: var(--shadow-card);
  padding: 16px;
}

.metric span {
  display: block;
  color: var(--muted);
  font-size: 12px;
  font-weight: 600;
}

/* Clickable KPI tiles: same card, plus an affordance on hover. */
a.metric {
  display: block;
  color: inherit;
  font-weight: inherit;
  text-decoration: none;
}

a.metric:hover {
  border-color: var(--brand-sky);
  text-decoration: none;
}

.metric strong {
  display: block;
  margin: 8px 0 6px;
  color: var(--ink-strong);
  font-size: 28px;
  font-variant-numeric: tabular-nums;
}

.metric small {
  color: var(--muted);
  line-height: 1.4;
}

/* Teal→sky kicker bar: the second brand hue, woven through the chrome so
   screens stop reading as blue-on-blue. Panel headings + page title only. */
.panel-heading .eyebrow::before,
.topbar-title .eyebrow::before {
  content: "";
  display: inline-block;
  vertical-align: 2px;
  width: 14px;
  height: 3px;
  margin-right: 7px;
  border-radius: 999px;
  background: linear-gradient(90deg, var(--brand-teal), var(--brand-sky));
}

/* Attention tiles: coral left stripe + coral value — applied only while the
   count is non-zero, so colour keeps meaning state, never decoration. */
.metric-alert {
  border-left: 3px solid var(--brand-coral);
}

.metric-alert strong {
  color: var(--danger-ink);
}

.card-grid {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 14px;
}

.stack-list {
  display: grid;
  gap: 10px;
}

.row-card {
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: var(--surface-tint);
  padding: 13px;
}

.row-top {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 10px;
}

.row-title {
  margin: 0 0 4px;
  font-weight: 700;
}

.row-meta {
  margin: 0;
  color: var(--muted);
  font-size: 13px;
  line-height: 1.45;
}

.empty-row {
  border: 1px dashed var(--line);
  border-radius: var(--radius);
  color: var(--muted);
  padding: 12px;
  font-size: 13px;
}

/* Hero band (client 360 header) */
.hero-band {
  display: flex;
  justify-content: space-between;
  align-items: flex-start;
  gap: 18px;
  border-radius: var(--radius-lg);
  background:
    radial-gradient(620px 220px at 92% -30%, rgba(107, 202, 213, 0.28), transparent 60%),
    linear-gradient(135deg, var(--brand-primary) 0%, var(--primary-900) 100%);
  color: #ffffff;
  padding: 24px;
  margin-bottom: 16px;
  box-shadow: var(--shadow);
}

.hero-band .eyebrow {
  color: var(--brand-teal);
}

.hero-band h2,
.hero-band h3 {
  color: #ffffff;
  font-size: 28px;
}

.hero-copy {
  max-width: 850px;
  margin: 12px 0 0;
  color: var(--on-rail);
  line-height: 1.55;
}

/* Pills sit on the dark band as solid light chips so their semantic inks
   stay AA-readable (the tinted defaults vanish on Primary Blue). */
.hero-pills {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 7px;
  margin-top: 10px;
}

.hero-pills .pill {
  background: rgba(255, 255, 255, 0.94);
}

.hero-pills small {
  color: var(--on-rail);
}

/* Client 360 hero: compact disclosure explaining what health / status /
   at risk mean, styled to sit quietly on the dark band. */
.hero-legend {
  max-width: 850px;
  margin-top: 10px;
  color: var(--on-rail);
  font-size: 12px;
}

.hero-legend summary {
  cursor: pointer;
  color: var(--brand-teal);
  font-weight: 700;
}

.hero-legend p {
  margin: 6px 0 0;
  color: var(--on-rail);
  line-height: 1.55;
}

.hero-actions {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  justify-content: flex-end;
  align-items: flex-start;
}

/* Derived metrics live BELOW the band as normal light cards. */
.client-hero-metrics {
  margin-bottom: 18px;
}

/* ---- Segmented tabs (client 360, pricing version) — Vision-2031
   segmented control: bordered white track, solid-blue active pill. ---- */
.tab-bar {
  display: inline-flex;
  max-width: 100%;
  gap: 2px;
  padding: 3px;
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  background: var(--surface);
  box-shadow: var(--shadow-card);
  margin-bottom: 18px;
  overflow-x: auto;
}

.tab-btn {
  border: 0;
  border-radius: 7px;
  background: transparent;
  padding: 8px 14px;
  color: var(--muted);
  font-weight: 700;
  white-space: nowrap;
}

.tab-btn:hover {
  background: var(--paper);
  color: var(--ink);
}

.tab-btn.active {
  background: var(--brand-primary);
  color: #ffffff;
}

.tab-btn.active:hover {
  background: var(--primary-700);
  color: #ffffff;
}

/* Phase sub-tabs (project workspace / template editor). Completed phases
   (Done == Total, Total > 0) go green — pill-green tones, readable whether
   the sub-tab is active or not. */
.tab-btn.tab-done {
  color: var(--ok);
}

.tab-btn.tab-done:hover {
  color: var(--ok-strong);
}

.tab-btn.tab-done.active {
  background: var(--ok);
  color: #ffffff;
}

.tab-btn.tab-done.active:hover {
  background: var(--ok-strong);
  color: #ffffff;
}

/* Out-of-scope phases stay reachable (their re-include button lives inside)
   but read as muted in the bar. */
.tab-btn.tab-off {
  color: var(--brand-gray);
  font-style: italic;
  font-weight: 400;
}

.tab-btn.tab-off:hover {
  color: var(--muted);
}

.tab-btn.tab-off.active {
  background: var(--brand-gray);
  color: #ffffff;
}

[x-cloak] {
  display: none !important;
}

/* ---- Dashboard work queue (POC daily-view feel) ---- */
.queue-list {
  display: grid;
  gap: 10px;
}

.queue-row {
  display: flex;
  justify-content: space-between;
  align-items: flex-start;
  gap: 14px;
  border: 1px solid var(--line);
  border-left: 6px solid var(--brand-gray);
  border-radius: var(--radius);
  background: var(--surface);
  box-shadow: var(--shadow-card);
  padding: 12px 14px;
}

.queue-row--green {
  border-left-color: var(--ok);
}

.queue-row--amber {
  border-left-color: var(--warn);
}

.queue-row--red {
  border-left-color: var(--brand-coral);
}

.queue-row-main {
  min-width: 0;
  display: grid;
  gap: 3px;
}

.queue-row-title {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 8px;
  margin: 0;
  font-size: 15px;
}

.queue-row-sub,
.queue-row-counts {
  margin: 0;
  color: var(--muted);
  font-size: 13px;
}

.queue-row-actions {
  display: flex;
  align-items: flex-start;
  flex-wrap: wrap;
  gap: 8px;
  justify-content: flex-end;
}

@media (max-width: 720px) {
  .queue-row {
    flex-direction: column;
  }

  .queue-row-actions {
    justify-content: flex-start;
  }
}

/* Inline quick-note capture on the client 360 */
.quick-note-form {
  display: flex;
  align-items: flex-start;
  gap: 10px;
  margin-bottom: 14px;
}

.quick-note-form textarea {
  flex: 1;
  resize: vertical;
}

/* CR-1: the box gained a subject line above the body, so its two inputs stack
   in a column and the button stays beside the pair. Without this wrapper the
   three children sat in one flex row and the textarea — the thing you actually
   type into — collapsed to a sliver beside the subject. */
.quick-note-fields {
  display: grid;
  gap: 8px;
  flex: 1;
}

/* ============================================================
   Pills & tags — classes emitted by Common/StatusPillMap (S9)
   ============================================================ */
.pill {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  min-height: 24px;
  border-radius: 999px;
  padding: 3px 10px;
  font-size: 12px;
  font-weight: 700;
  line-height: 1.25;
  white-space: nowrap;
}

/* Status dot inherits the pill's ink for free via currentColor. */
.pill::before {
  content: "";
  flex: 0 0 auto;
  width: 6px;
  height: 6px;
  border-radius: 999px;
  background: currentColor;
}

.pill-green {
  background: var(--ok-bg);
  color: var(--ok);
}

.pill-amber {
  background: var(--warn-bg);
  color: var(--warn);
}

.pill-red {
  background: var(--coral-bg);
  color: var(--danger-ink);
}

.pill-blue {
  background: rgba(23, 105, 163, 0.12);
  color: var(--brand-primary);
}

.pill-sky {
  background: var(--sky-bg);
  color: var(--sky-ink);
}

/* Jade, not the cyan-teal above: this pill is Expansion opportunity's status
   colour and must match --seg-teal in the distribution bar (W2-21). It points at
   the segment token directly, the way .pill-violet points at --seg-violet, so
   the two can never be re-stepped apart again. */
.pill-teal {
  background: var(--jade-bg);
  color: var(--seg-teal);
}

.pill-violet {
  background: rgba(114, 80, 181, 0.13);
  color: var(--seg-violet);
}

.pill-gray {
  background: var(--neutral-bg);
  color: var(--neutral-ink);
}

.tag-list {
  display: flex;
  flex-wrap: wrap;
  gap: 7px;
  margin-top: 10px;
}

.tag {
  display: inline-flex;
  align-items: center;
  min-height: 24px;
  border-radius: 999px;
  background: var(--teal-bg);
  color: var(--teal-ink);
  padding: 3px 9px;
  font-size: 12px;
  font-weight: 700;
}

/* ============================================================
   Buttons
   ============================================================ */
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  min-height: 38px;
  padding: 8px 14px;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: var(--surface);
  color: var(--ink);
  font-weight: 700;
  text-decoration: none;
}

.btn:hover {
  background: var(--surface-tint);
  border-color: var(--line);
  color: var(--ink);
  text-decoration: none;
}

.btn-primary {
  border-color: var(--brand-primary);
  background: var(--brand-primary);
  color: #ffffff;
}

.btn-primary:hover {
  border-color: var(--primary-700);
  background: var(--primary-700);
  color: #ffffff;
}

/* Destructive: deepened coral so white text keeps AA contrast;
   Coral Red proper carries the alert accents (PRD §4.2). */
.btn-danger {
  border-color: var(--danger-solid);
  background: var(--danger-solid);
  color: #ffffff;
}

.btn-danger:hover {
  border-color: var(--danger-strong);
  background: var(--danger-strong);
  color: #ffffff;
}

.btn-light {
  border-color: rgba(255, 255, 255, 0.45);
  background: rgba(255, 255, 255, 0.12);
  color: #ffffff;
}

.btn-light:hover {
  background: rgba(255, 255, 255, 0.22);
  color: #ffffff;
}

.icon-button {
  width: 38px;
  min-height: 38px;
  padding: 0;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: var(--surface);
  font-weight: 700;
}

.link-button {
  border: 0;
  background: transparent;
  padding: 0;
  min-height: 0;
  text-align: left;
  color: var(--brand-primary);
  font-weight: 700;
}

.link-button:hover {
  color: var(--primary-800);
  text-decoration: underline;
}

/* ============================================================
   Forms
   ============================================================ */
/* input:not([type]) — an <input> without a type attribute IS a text input,
   but attribute selectors miss it; without this the bare inputs in workspace
   tables and quick-add rows fall back to the browser-default look. */
input:not([type]),
input[type="text"],
input[type="search"],
input[type="email"],
input[type="password"],
input[type="tel"],
input[type="url"],
input[type="date"],
input[type="number"],
select,
textarea {
  width: 100%;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: var(--surface);
  color: var(--ink);
  padding: 10px 12px;
  outline: none;
}

input:focus,
select:focus,
textarea:focus {
  border-color: var(--brand-sky);
  box-shadow: var(--focus-ring);
}

input[type="checkbox"],
input[type="radio"] {
  width: 16px;
  height: 16px;
  accent-color: var(--brand-primary);
}

.field {
  display: grid;
  gap: 6px;
  /* Pack the label/control/error rows to the TOP of the field box.
     .field is a grid inside .form-grid, which stretches each column to the
     tallest field in the row. Without this, that extra height was shared out
     between the field's own rows, so the moment one column gained a validation
     message its neighbour's input drifted downwards and the two columns stopped
     lining up. The taller box is correct — its CONTENTS floating in it was not.
     Systemic: every two-column form in the app sat slightly out of true as soon
     as anything failed validation, and worse the longer the message. */
  align-content: start;
  margin-bottom: 14px;
  color: var(--ink-mid);
  /* NO font-weight here. It used to be 700 to make the label read as a label,
     but `font: inherit` on controls (above) carried it into the input too — so
     every note, Pain and scope note in the platform was TYPED in bold, which was
     never intended and was a standing complaint (DECISIONS thirty-seventh
     follow-up, ruling 5). The label sets its own weight below, which is where the
     intent belongs; .field-hint and .field-validation-error likewise. */
}

.field label {
  color: var(--ink-mid);
  font-weight: 700;
}

.field-hint {
  color: var(--muted);
  font-size: 12px;
  font-weight: 400;
}

.form-grid {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 13px;
}

.form-actions {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 10px;
  margin-top: 16px;
}
/* Buttons keep their intrinsic width; an explanatory sibling takes the slack and
   wraps below when there isn't any. Without this, a long note beside the buttons
   squeezed them until their labels broke over three lines — visible on the
   signed-off Reviews tab, where "Book a date only" wrapped. .capture-actions,
   the newer of the pair, already wrapped; this brings the older one into line. */
.form-actions > .btn,
.form-actions > button,
.form-actions > .link-button { flex: 0 0 auto; }
.form-actions > .text-muted { flex: 1 1 260px; }

.check-field {
  display: flex;
  align-items: center;
  gap: 9px;
  margin-bottom: 14px;
  color: var(--ink-mid);
  font-weight: 700;
}

/* ASP.NET validation hooks */
.field-validation-error {
  color: var(--danger-ink);
  font-size: 12px;
  font-weight: 700;
}

.input-validation-error {
  border-color: var(--brand-coral);
}

/* Assignee picker — multi-select of people rendered as toggleable pills.
   Native checkboxes (visually hidden, still focusable) inside pill labels:
   binds as a plain List<string> post, no JS. Selected = solid brand blue. */
.assignee-field {
  border: 0;
  margin: 0 0 14px;
  padding: 0;
  min-width: 0;
}

.assignee-field legend {
  margin-bottom: 6px;
  padding: 0;
  color: var(--ink-mid);
  font-weight: 700;
}

.assignee-field .field-hint {
  display: block;
  margin-top: 6px;
}

.assignee-picker {
  display: flex;
  flex-wrap: wrap;
  gap: 7px;
}

.assignee-option {
  position: relative;
  display: inline-flex;
  align-items: center;
  gap: 6px;
  min-height: 32px;
  border: 1px solid var(--line);
  border-radius: 999px;
  background: var(--surface);
  color: var(--neutral-ink);
  padding: 4px 13px 4px 11px;
  font-size: 13px;
  font-weight: 700;
  line-height: 1.25;
  cursor: pointer;
  user-select: none;
}

.assignee-option::before {
  content: "+";
  opacity: 0.5;
}

.assignee-option:hover {
  border-color: var(--brand-sky);
  color: var(--brand-primary);
}

.assignee-option input {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: 0;
  opacity: 0;
  pointer-events: none;
}

.assignee-option:has(input:checked) {
  border-color: var(--brand-primary);
  background: var(--brand-primary);
  color: #ffffff;
}

.assignee-option:has(input:checked)::before {
  content: "✓";
  opacity: 1;
}

.assignee-option:has(input:focus-visible) {
  border-color: var(--brand-sky);
  box-shadow: var(--focus-ring);
}

@media (prefers-reduced-motion: no-preference) {
  .assignee-option {
    transition: background-color 0.15s ease, border-color 0.15s ease, color 0.15s ease;
  }
}

/* Multi-action capture rows (note capture): each row = its own field block. */
.action-row {
  padding: 12px 0;
}

.action-row + .action-row {
  border-top: 1px dashed var(--line);
}

.validation-summary-errors {
  border: 1px solid var(--brand-coral);
  border-left: 5px solid var(--brand-coral);
  border-radius: var(--radius);
  background: rgba(255, 102, 102, 0.08);
  color: var(--danger-ink);
  padding: 11px 13px;
  margin-bottom: 14px;
  font-weight: 700;
}

.validation-summary-errors ul {
  margin: 0;
  padding-left: 18px;
}

.validation-summary-valid {
  display: none;
}

/* Alerts (coral = alert accent, never the sole signal) */
.alert {
  border: 1px solid var(--line);
  border-left: 5px solid var(--brand-coral);
  border-radius: var(--radius);
  background: rgba(255, 102, 102, 0.07);
  padding: 12px 14px;
  margin-bottom: 14px;
}

/* ============================================================
   Tables
   ============================================================ */
.table-wrap {
  overflow-x: auto;
}

.table {
  width: 100%;
  border-collapse: collapse;
}

.table th {
  text-align: left;
  padding: 10px 12px;
  border-bottom: 2px solid var(--line);
  background: var(--surface-tint);
  color: var(--muted);
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  white-space: nowrap;
}

.table td {
  padding: 12px;
  border-bottom: 1px solid var(--line-soft);
  vertical-align: middle;
  font-variant-numeric: tabular-nums;
}

.table tbody tr:hover {
  background: var(--surface-tint);
}

.table-actions {
  display: flex;
  align-items: center;
  gap: 12px;
  justify-content: flex-end;
  white-space: nowrap;
}

/* --- UIFIX sortable registers (wwwroot/js/site.js) ------------------------- */
table.sortable th:not([data-nosort]) {
  cursor: pointer;
  user-select: none;
  padding-right: 20px;
  position: relative;
}

table.sortable th:not([data-nosort]):hover {
  background: var(--surface-tint);
  color: var(--brand-primary);
}

table.sortable th[aria-sort]:not([data-nosort])::after {
  content: "";
  position: absolute;
  right: 7px;
  top: 50%;
  border: 4px solid transparent;
}

table.sortable th[aria-sort="ascending"]:not([data-nosort])::after {
  border-bottom-color: var(--brand-primary);
  margin-top: -6px;
}

table.sortable th[aria-sort="descending"]:not([data-nosort])::after {
  border-top-color: var(--brand-primary);
  margin-top: 2px;
}

/* ============================================================
   Toast
   ============================================================ */
.toast {
  position: fixed;
  right: 22px;
  bottom: 22px;
  z-index: 40;
  max-width: 360px;
  border-radius: var(--radius);
  border-left: 4px solid var(--brand-teal);
  background: var(--primary-900);
  color: #ffffff;
  padding: 13px 15px;
  font-weight: 700;
  box-shadow: var(--shadow);
  transform: translateY(18px);
  opacity: 0;
  pointer-events: none;
}

.toast.show {
  transform: translateY(0);
  opacity: 1;
}

@media (prefers-reduced-motion: no-preference) {
  .toast {
    transition: transform 180ms ease, opacity 180ms ease;
  }

  .nav-item,
  .btn,
  input,
  select,
  textarea {
    transition: background-color 120ms ease, border-color 120ms ease,
      box-shadow 120ms ease, color 120ms ease;
  }

  .sidebar {
    transition: transform 200ms ease;
  }
}

/* ============================================================
   Auth layout (sign in / access denied)
   ============================================================ */
.auth-shell {
  min-height: 100vh;
  display: grid;
  place-items: center;
  padding: 24px;
  background:
    radial-gradient(1200px 500px at 85% -10%, rgba(49, 170, 225, 0.12), transparent 60%),
    radial-gradient(900px 420px at -10% 110%, rgba(107, 202, 213, 0.14), transparent 55%),
    var(--paper);
}

.auth-card {
  width: min(420px, 100%);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: var(--surface);
  box-shadow: var(--shadow);
  overflow: hidden;
}

.auth-brand {
  display: flex;
  align-items: center;
  gap: 14px;
  background: linear-gradient(135deg, var(--brand-primary) 0%, var(--brand-navy) 100%);
  color: #ffffff;
  padding: 22px 24px;
}

.auth-body {
  padding: 24px;
}

.auth-body h1 {
  font-size: 20px;
  margin-bottom: 6px;
}

.auth-foot {
  margin-top: 16px;
  color: var(--muted);
  font-size: 12px;
}

/* ============================================================
   Responsive
   ============================================================ */
@media (max-width: 1024px) {
  .app-shell {
    grid-template-columns: 1fr;
  }

  .sidebar {
    position: fixed;
    inset: 0 auto 0 0;
    z-index: 30;
    width: var(--sidebar-width);
    transform: translateX(-105%);
    overflow-y: auto;
  }

  .app-shell.nav-open .sidebar {
    transform: translateX(0);
    box-shadow: var(--shadow);
  }

  .sidebar-backdrop {
    position: fixed;
    inset: 0;
    z-index: 25;
    border: 0;
    padding: 0;
    background: rgba(11, 58, 93, 0.5);
  }

  .app-shell:not(.nav-open) .sidebar-backdrop {
    display: none;
  }

  .app-shell.nav-open .sidebar-backdrop {
    display: block;
  }

  .nav-toggle {
    display: inline-grid;
    place-items: center;
  }

  .card-grid {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }
}

@media (max-width: 720px) {
  .main {
    padding: 16px;
  }

  .topbar {
    flex-wrap: wrap;
    padding: 12px 16px;
  }

  .topbar-actions {
    flex-wrap: wrap;
  }

  .search-wrap {
    flex-basis: 100%;
    order: 3;
  }

  .form-grid,
  .card-grid {
    grid-template-columns: 1fr;
  }

  .hero-band {
    flex-direction: column;
    align-items: stretch;
  }

  .toast {
    left: 16px;
    right: 16px;
    max-width: none;
  }
}

/* Print: content only, no chrome */
@media print {
  .sidebar,
  .topbar,
  .toast {
    display: none !important;
  }

  .app-shell {
    grid-template-columns: 1fr;
  }

  body {
    background: #ffffff;
  }
}

/* --- M6 layout utilities ------------------------------------------------ */
.panel-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(340px, 1fr));
  gap: 18px;
  align-items: start;
}

.metric-row {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(170px, 1fr));
  gap: 12px;
  margin-top: 14px;
}

.metric-row .metric strong {
  font-size: 17px;
}

.filter-row {
  display: flex;
  flex-wrap: wrap;
  gap: 10px;
  align-items: center;
  margin-bottom: 16px;
}

/* Inline filter/quick-add controls: consistent height and sane intrinsic
   widths (the global width:100% otherwise lets flex shrink them oddly —
   collapsing date inputs, ragged text boxes). */
.filter-row input,
.filter-row select {
  width: auto;
  min-height: 38px;
  padding: 8px 12px;
}

.filter-row input[type="text"],
.filter-row input[type="search"],
.filter-row input:not([type]) {
  min-width: 140px;
}

.filter-row input[type="date"] {
  min-width: 150px;
}

.filter-row input[type="number"] {
  min-width: 90px;
}

.filter-row select {
  min-width: 130px;
}

.filter-row .check-field {
  margin-bottom: 0;
}

.check-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
  gap: 6px 16px;
}

.panel h3 {
  margin: 18px 0 6px;
  font-size: 15px;
}

fieldset.field {
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: 12px 16px 16px;
}

fieldset.field legend {
  font-weight: 700;
  font-size: 14px;
  padding: 0 6px;
}

/* --- M7 contact cards ---------------------------------------------------- */
.card-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
  gap: 14px;
}

.contact-card {
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: var(--surface);
  padding: 16px;
  display: flex;
  flex-direction: column;
  gap: 6px;
}

.contact-card-head {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 8px;
}

.contact-card small {
  color: var(--muted);
}

.contact-card p {
  margin: 4px 0;
}

/* --- M8 kanban board ------------------------------------------------------ */
.kanban {
  display: grid;
  grid-template-columns: repeat(7, minmax(280px, 1fr));
  gap: 16px;
  overflow-x: auto;
  align-items: stretch;
  padding-bottom: 10px;
}

.kanban-col {
  display: flex;
  flex-direction: column;
  background: var(--paper);
  border: 1px solid var(--line);
  border-top: 3px solid var(--brand-gray);
  border-radius: var(--radius);
  padding: 12px;
  min-height: 420px;
}

.kanban-col-head {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 5px;
  margin-bottom: 12px;
}

.kanban-col-head small {
  color: var(--muted);
}

.kanban-cards {
  display: flex;
  flex-direction: column;
  gap: 10px;
  flex: 1;
}

/* Dashed placeholder shown only when a column has no cards (kept live
   during drag via :has so drops into empty columns stay obvious). */
.kanban-empty {
  border: 1px dashed var(--line);
  border-radius: var(--radius);
  color: var(--muted);
  font-size: 13px;
  text-align: center;
  padding: 26px 12px;
  margin-top: 10px;
}

.kanban-col:has(.kanban-card) .kanban-empty {
  display: none;
}

.kanban-card {
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  box-shadow: 0 1px 0 rgba(11, 58, 93, 0.04);
  overflow: hidden;
}

.kanban-drag-handle {
  display: grid;
  place-items: center;
  padding: 5px 0;
  background: var(--surface-tint);
  border-bottom: 1px solid var(--line);
  cursor: grab;
}

.kanban-drag-handle:active {
  cursor: grabbing;
}

.kanban-drag-handle span {
  width: 34px;
  height: 8px;
  background-image: radial-gradient(circle, var(--faint) 1.3px, transparent 1.4px);
  background-size: 7px 4.5px;
  background-position: center;
}

.kanban-card-body {
  display: flex;
  flex-direction: column;
  gap: 6px;
  padding: 11px 13px 12px;
}

.kanban-card small {
  color: var(--muted);
}

.kanban-card-client .tag {
  margin-left: 4px;
  min-height: 20px;
  padding: 1px 8px;
  font-size: 11px;
}

.kanban-card-pain {
  margin: 0;
  color: var(--muted);
  font-size: 12px;
  font-style: italic;
  line-height: 1.4;
}

.kanban-card-value {
  font-weight: 700;
}

.kanban-card-next {
  color: var(--sky-ink);
  font-size: 12px;
}

.kanban-card-meta {
  display: flex;
  gap: 8px;
  align-items: center;
  font-size: 13px;
}

.kanban-card-actions {
  display: flex;
  gap: 6px;
  align-items: center;
  margin-top: 2px;
}

.kanban-stage-select {
  font-size: 12px;
  padding: 4px 6px;
  flex: 1;
}

/* --- UIFIX dashboard companies-by-status board -----------------------------
   Read-only distribution view (no drag, no status change) — recessive
   styling per dataviz sensibilities, counts as plain text in a pill. */
.status-board {
  display: grid;
  grid-auto-flow: column;
  grid-auto-columns: minmax(200px, 1fr);
  gap: 12px;
  overflow-x: auto;
  padding-bottom: 6px;
}

.status-board-col {
  display: flex;
  flex-direction: column;
  gap: 8px;
  background: var(--paper);
  border: 1px solid var(--line);
  border-top: 3px solid var(--brand-gray);
  border-radius: var(--radius);
  padding: 12px;
  min-width: 200px;
}

/* Column accents ride the status pill already in the column head, so board
   colours can never drift from StatusPillMap (S9 single vocabulary). */
.status-board-col:has(.status-board-col-head .pill-green),
.kanban-col:has(.kanban-col-head .pill-green) {
  border-top-color: var(--ok);
}

.status-board-col:has(.status-board-col-head .pill-amber),
.kanban-col:has(.kanban-col-head .pill-amber) {
  border-top-color: var(--warn);
}

.status-board-col:has(.status-board-col-head .pill-red),
.kanban-col:has(.kanban-col-head .pill-red) {
  border-top-color: var(--brand-coral);
}

.status-board-col:has(.status-board-col-head .pill-sky),
.kanban-col:has(.kanban-col-head .pill-sky) {
  border-top-color: var(--brand-sky);
}

.status-board-col:has(.status-board-col-head .pill-teal),
.kanban-col:has(.kanban-col-head .pill-teal) {
  border-top-color: var(--brand-teal);
}

.status-board-col:has(.status-board-col-head .pill-blue),
.kanban-col:has(.kanban-col-head .pill-blue) {
  border-top-color: var(--brand-primary);
}

.status-board-col:has(.status-board-col-head .pill-violet),
.kanban-col:has(.kanban-col-head .pill-violet) {
  border-top-color: var(--seg-violet);
}

.status-board-col-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
}

.status-board-col-head strong {
  font-size: 13px;
}

.status-board-list {
  display: flex;
  flex-direction: column;
  gap: 6px;
  margin: 0;
  padding: 0;
  list-style: none;
}

.status-board-list li {
  font-size: 13px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.status-board-empty {
  margin: 0;
  color: var(--muted);
  font-size: 13px;
}

.status-board-more {
  font-size: 12px;
  font-weight: 700;
}

/* --- Snooze --------------------------------------------------------------- */
.snooze-details summary {
  cursor: pointer;
  color: var(--brand-sky);
}

/* --- M18 quote builder ---------------------------------------------------- */
.quote-layout {
  display: grid;
  grid-template-columns: minmax(0, 1fr) 380px;
  gap: 18px;
  align-items: start;
}

.quote-main {
  display: flex;
  flex-direction: column;
  gap: 18px;
  min-width: 0;
}

.quote-summary-wrap {
  position: sticky;
  top: 16px;
}

.quote-summary-panel {
  max-height: calc(100vh - 32px);
  overflow-y: auto;
}

.tb-group-row td {
  background: var(--paper);
}

@media (max-width: 1100px) {
  .quote-layout {
    grid-template-columns: 1fr;
  }
  .quote-summary-wrap {
    position: static;
  }
}


/* --- W1 client-proactive components ------------------------------------
   Lifted verbatim from docs/mockups/_mockup.css, whose header noted they were
   written to be moved here once the designs were approved (signed off
   2026-07-29). Wave-1 set only: tier pills, health-score meters, the
   engagement strip and the review-scoring components stay in the mockups
   until wave 2 gives them backing fields. */

/* Page scope bar — one scope control governing the whole screen */
.scope-bar {
  display: flex;
  flex-wrap: wrap;
  gap: 12px;
  align-items: center;
  justify-content: space-between;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  padding: 10px 14px;
  box-shadow: var(--shadow-card);
}
.scope-choice { display: flex; gap: 6px; align-items: center; }
.scope-choice .btn.is-active {
  background: var(--brand-primary);
  border-color: var(--brand-primary);
  color: #ffffff;
}

/* Main + rail: keeps team coverage above the fold beside the queue */
/* The rail was sized when it held short one-line rows. It now carries the
   agenda strip, whose rows are a date chip, a title, a meta line and an
   action — and at 1.9fr the strip came out around 310px on a 1366 laptop,
   which is where it read as squashed (Paul, 5 August). Widened enough to give
   the strip a comfortable measure without the queue beside it losing a
   meaningful amount: at 1366 the strip goes 313px → 353px and the queue
   666px → 626px, which changes no wrapping in a queue row. */
.rail-layout {
  display: grid;
  grid-template-columns: minmax(0, 1.6fr) minmax(320px, 1fr);
  gap: 18px;
  align-items: start;
}
@media (max-width: 1100px) {
  .rail-layout { grid-template-columns: 1fr; }
}

/* Urgency meter on a queue row */
.urgency {
  display: block;
  height: 4px;
  border-radius: 999px;
  background: var(--line-soft);
  overflow: hidden;
  margin: 8px 0 2px;
}
.urgency span { display: block; height: 100%; border-radius: 999px; }
.urgency--red span { background: var(--danger-solid); }
.urgency--amber span { background: var(--warn-solid); }
.urgency--green span { background: var(--ok); }

/* Next-step line — the forward-looking field, on every row */
.next-step {
  font-size: 12.5px;
  font-weight: 700;
  color: var(--ink);
  margin: 2px 0 0;
}
.next-step small { font-weight: 400; color: var(--muted); }
.next-step--none { color: var(--danger-ink); }

/* Distribution bar — counts, not a wall of client names */
.dist { margin: 4px 0 14px; }
.dist-label {
  font-size: 11.5px;
  font-weight: 700;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--muted);
  margin-bottom: 6px;
}
/* Segments carry NO inline text. An interior stacked segment has no free end
   to label, and a segment narrow enough to clip its own count is worse than
   no count at all — so identity and value live in .dist-key below the bar,
   which is always present. Each segment keeps an accessible name (.sr-only)
   and a title, so it stays a real link. Segments are separated by a 2px gap
   in the surface colour rather than a stroke: white does the separating, so
   no segment gains ink that isn't data. */
.dist-bar {
  display: flex;
  gap: 2px;
  height: 22px;
  border-radius: var(--radius);
  background: var(--surface);
  overflow: hidden;
}
.dist-seg {
  display: block;
  min-width: 3px;
  background: var(--seg-gray);
}
.dist-seg:first-child { border-radius: var(--radius) 0 0 var(--radius); }
.dist-seg:last-child { border-radius: 0 var(--radius) var(--radius) 0; }
.dist-seg:only-child { border-radius: var(--radius); }

/* Solid counterparts of the pill tones — the bar and the pill for one status
   are the same colour by construction. Add a tone here only by adding it to
   StatusPillMap first. */
.dist-seg--green { background: var(--ok); }
.dist-seg--amber { background: var(--warn-solid); }
.dist-seg--red { background: var(--danger-solid); }
.dist-seg--deep-red { background: var(--danger-deep); }
.dist-seg--blue { background: var(--brand-primary); }
.dist-seg--sky { background: var(--seg-sky); }
.dist-seg--teal { background: var(--seg-teal); }
.dist-seg--violet { background: var(--seg-violet); }
.dist-seg--gray { background: var(--seg-gray); }

/* The legend is the identity channel and the relief for the sky segment's
   sub-3:1 fill — it is never optional. Swatch colour rides a modifier, the
   text stays in a text token (a light fill is illegible as type). */
.dist-key {
  display: flex;
  flex-wrap: wrap;
  gap: 4px 14px;
  margin-top: 7px;
  font-size: 12px;
  color: var(--muted);
}
.dist-key a,
.dist-key span {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  color: var(--muted);
  font-weight: 400;
}
.dist-key a:hover { color: var(--ink-strong); }
.dist-key b {
  color: var(--ink-strong);
  font-variant-numeric: tabular-nums;
}
/* Unscoped so the tone modifiers below win on specificity — a descendant
   selector here (.dist-key .dist-swatch) silently greys every swatch. */
.dist-swatch {
  flex: 0 0 auto;
  width: 9px;
  height: 9px;
  border-radius: 2px;
  background: var(--seg-gray);
}
.dist-swatch--green { background: var(--ok); }
.dist-swatch--amber { background: var(--warn-solid); }
.dist-swatch--red { background: var(--danger-solid); }
.dist-swatch--deep-red { background: var(--danger-deep); }
.dist-swatch--blue { background: var(--brand-primary); }
.dist-swatch--sky { background: var(--seg-sky); }
.dist-swatch--teal { background: var(--seg-teal); }
.dist-swatch--violet { background: var(--seg-violet); }
.dist-swatch--gray { background: var(--seg-gray); }

/* Coverage table — per-owner book coverage */
.coverage { width: 100%; border-collapse: collapse; font-size: 12.5px; }
.coverage th {
  text-align: left;
  font-size: 11px;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--muted);
  padding: 0 6px 6px 0;
  font-weight: 700;
}
.coverage td { padding: 7px 6px 7px 0; border-top: 1px solid var(--line-soft); }
.coverage td:first-child { font-weight: 700; color: var(--ink-strong); }
.meter {
  display: block;
  height: 5px;
  width: 100%;
  min-width: 54px;
  background: var(--line-soft);
  border-radius: 999px;
  overflow: hidden;
}
/* Meter fill carries severity; the track is a lighter step of the same ramp,
   so state reads across the whole bar and not just the filled part. */
.meter span { display: block; height: 100%; background: var(--brand-primary); }
.meter--warn span { background: var(--warn-solid); }
.meter--low span { background: var(--danger-solid); }

/* Identity columns in a register (owner, person) should not wrap to three
   lines just because the row gained relationship columns beside it */
.table td.col-nowrap { white-space: nowrap; }

/* Pager — server-side paging for every register */
.pager {
  display: flex;
  gap: 12px;
  align-items: center;
  justify-content: space-between;
  padding-top: 12px;
  margin-top: 4px;
  border-top: 1px solid var(--line-soft);
  font-size: 12.5px;
  color: var(--muted);
}
.pager-pages { display: flex; gap: 4px; align-items: center; }

/* Empty state with an action, replacing the grey sentence in a <td> */
.empty-state {
  text-align: center;
  padding: 34px 20px;
  color: var(--muted);
}
.empty-state h3 { font-size: 15px; margin-bottom: 4px; }
.empty-state p { max-width: 380px; margin: 0 auto 12px; }

/* ============================================================
   Loading feedback for htmx round-trips (W1-30)
   htmx puts .htmx-request on whatever hx-indicator selects (or on the
   requesting element when it selects nothing), and takes it off when the
   response settles. Every rule here hangs off that, so no JS is needed.
   ============================================================ */

/* A region whose contents are now stale. Used on the quote summary and the
   tag-build figures, which is where a silent round-trip actually costs
   money: a price that looks live while it is being recalculated is worse
   than one that visibly says it is thinking. */
.hx-busy {
  transition: opacity 120ms ease;
}
.hx-busy.htmx-request {
  position: relative;
  opacity: 0.55;
}
.hx-busy.htmx-request::after {
  content: "Updating…";
  position: absolute;
  top: 8px;
  right: 12px;
  font-size: 11.5px;
  font-weight: 700;
  color: var(--sky-ink);
}

/* Inline spinner for cascading selects, which are too small to dim. */
.hx-spin {
  display: none;
  width: 13px;
  height: 13px;
  margin-left: 8px;
  vertical-align: -2px;
}
.hx-spin.htmx-request,
.htmx-request > .hx-spin {
  display: inline-block;
}

@media (prefers-reduced-motion: no-preference) {
  .hx-spin.htmx-request {
    border: 2px solid var(--line);
    border-top-color: var(--brand-primary);
    border-radius: 50%;
    animation: hx-rotate 640ms linear infinite;
  }
  @keyframes hx-rotate {
    to { transform: rotate(360deg); }
  }
}

/* Motion is not the only signal available: without it the spinner becomes
   a word, so the state is still reported rather than simply absent. */
@media (prefers-reduced-motion: reduce) {
  .hx-spin {
    width: auto;
    height: auto;
    font-size: 11.5px;
    font-weight: 700;
    color: var(--sky-ink);
  }
  .hx-spin.htmx-request::after {
    content: "loading…";
  }
}

/* Breadcrumb (W1-B) — a page inside a client names the client. Replaces the
   hand-typed eyebrow on those pages rather than sitting beside it. */
.crumb ol {
  list-style: none;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0 6px;
  margin: 0 0 2px;
  padding: 0;
  font-size: 11.5px;
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--muted);
}
.crumb li + li::before {
  content: "›";
  margin-right: 6px;
  color: var(--faint);
}
.crumb a { color: var(--brand-primary); }
.crumb a:hover { text-decoration: underline; }
.crumb [aria-current="page"] { color: var(--ink); }

/* ============================================================
   Utilities (W1-34)
   The recurring inline declarations, named once. Added only where
   a declaration repeated across views — a class with one caller is
   worse than the inline it replaces.
   ============================================================ */

/* Section rhythm. Replaces 22x margin-top:18px, the single most repeated
   inline style. A margin utility rather than a .page-stack parent, because
   restructuring every parent to own its children's spacing is a bigger
   change than the duplication it would remove. */
.mt-section { margin-top: var(--space-section); }
.mb-section { margin-bottom: var(--space-section); }

/* Content measures. Nine ad-hoc page widths (980/900/860/780/760/720/
   640/639/560) snap onto five named steps; 639px was a typo for 640. */
.measure-page { max-width: var(--measure-page); }
.measure-wide { max-width: var(--measure-wide); }
.measure-form { max-width: var(--measure-form); }
.measure-mid { max-width: var(--measure-mid); }
.measure-narrow { max-width: var(--measure-narrow); }

/* Text flow */
.prose-preserve { white-space: pre-line; }
.nowrap { white-space: nowrap; }

/* Table column width scale. Registers carried ~60 one-off min/max-widths
   at 12 near-identical sizes; these snap onto a five-step scale per axis.
   Truncation width shifting by <=10px is imperceptible; twelve magic
   numbers scattered across views is not. */
.col-90 { max-width: 90px; }
.col-110 { max-width: 110px; }
.col-160 { max-width: 160px; }
.col-190 { max-width: 190px; }
.col-220 { max-width: 220px; }
.col-min-130 { min-width: 130px; }
.col-min-180 { min-width: 180px; }
.col-min-200 { min-width: 200px; }
.col-min-220 { min-width: 220px; }
.col-min-240 { min-width: 240px; }

/* Progress bar — the Projects screens hand-built a track+fill pair three
   times. .meter already models exactly that, so this is a size modifier
   on the existing component rather than a second one. */
.meter--lg { height: 8px; flex: 1; background: var(--line); }

/* Spacing scale. Only the steps that actually recurred in the views are
   named — 4/6/8/10/12/14px above an element, plus the resets. The
   "stack-N" variants also zero the bottom margin, which is what
   `margin: 6px 0 0` was doing on paragraphs that already carry one. */
.mt-0 { margin-top: 0; }
.mt-4 { margin-top: 4px; }
.mt-6 { margin-top: 6px; }
.mt-8 { margin-top: 8px; }
.mt-10 { margin-top: 10px; }
.mt-12 { margin-top: 12px; }
.mt-14 { margin-top: 14px; }
.mb-0 { margin-bottom: 0; }
.m-0 { margin: 0; }
.my-14 { margin: 14px 0; }
.stack-4 { margin: 4px 0 0; }
.stack-6 { margin: 6px 0 0; }
.stack-8 { margin: 8px 0 0; }

/* Display and flow */
.inline { display: inline; }
.inline-block { display: inline-block; }
.row-flex { display: flex; align-items: center; }
.row-flex-wrap { display: flex; align-items: center; flex-wrap: wrap; }
.gap-6 { gap: 6px; }
.gap-8 { gap: 8px; }
.gap-10 { gap: 10px; }
.flex-1 { flex: 1; }
.overflow-hidden { overflow: hidden; }
.list-reset { list-style: none; padding: 0; }
.fw-400 { font-weight: 400; }

/* Transcript and brief panels. Seven sites hand-rolled the same two shapes:
   a scrollable pre-line block, and a bordered paper panel. The scroll cap
   was 240/320/420/480px across them for no reason anyone recorded. */
.transcript {
  white-space: pre-line;
  max-height: 320px;
  overflow-y: auto;
  margin: 6px 0 0;
}
.transcript--tall { max-height: 480px; }
.transcript-box {
  background: var(--paper);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: 14px;
  font-family: inherit;
}
.prose-wrap { white-space: pre-wrap; }
.mb-16 { margin-bottom: 16px; }
.mt-16 { margin-top: 16px; }
.mt-24 { margin-top: 24px; }
.pt-0 { padding-top: 0; }
.col-min-160 { min-width: 160px; }
.col-min-280 { min-width: 280px; }
.col-240 { max-width: 240px; }
.col-280 { max-width: 280px; }
.col-320 { max-width: 320px; }
.w-190 { width: 190px; }
.w-220 { width: 220px; }
.fs-24 { font-size: 24px; }
.cursor-pointer { cursor: pointer; }

/* ============================================================
   Disabled, read-only and locked states (W1-31)
   The stylesheet previously had no :disabled or [readonly] rule at
   all, so a dead control looked exactly like a live one.
   ============================================================ */

/* Inert: nothing will happen if you click it. */
.btn:disabled,
.btn[aria-disabled="true"],
button:disabled,
select:disabled,
input:disabled,
textarea:disabled {
  background: var(--neutral-bg);
  border-color: var(--line);
  color: var(--faint);
  cursor: not-allowed;
  box-shadow: none;
}

/* Read-only is not the same as disabled: the value is real and worth
   reading, you just cannot change it here. Keeps readable ink and marks
   the boundary with a dashed edge rather than greying the content out. */
input[readonly],
textarea[readonly] {
  background: var(--surface-tint);
  border-style: dashed;
  color: var(--muted);
  cursor: default;
}
input[readonly]:focus-visible,
textarea[readonly]:focus-visible {
  border-color: var(--brand-primary);
}

/* One banner for "this record is locked", replacing six uses of .alert.
   .alert carries a coral left border and reads as a problem; a closed
   project or an archived template is a state, not a fault. */
.locked-banner {
  display: flex;
  align-items: flex-start;
  gap: 10px;
  border: 1px solid var(--line);
  border-left: 5px solid var(--seg-gray);
  border-radius: var(--radius);
  background: var(--surface-tint);
  color: var(--muted);
  padding: 12px 14px;
  margin-bottom: 14px;
}
.locked-banner::before {
  content: "";
  flex: none;
  width: 14px;
  height: 14px;
  margin-top: 2px;
  background: currentColor;
  /* padlock */
  -webkit-mask: var(--icon-lock) center / contain no-repeat;
  mask: var(--icon-lock) center / contain no-repeat;
}
.locked-banner strong { color: var(--ink); }

/* --- W2 engagement components -------------------------------------------
   Lifted from docs/mockups/_mockup.css now that wave 2 has given them backing
   fields. Landed centrally because three workers need them; per-agent copies
   would diverge. The four tier tints borrow existing tokens rather than the
   mockup's one-off hexes — a pill is chrome, not a data fill, so it does not
   use the validated --seg-* palette. */

/* Client tier — structural, human-set, never derived from health. */
.tier-pill {
  display: inline-block;
  font-size: 11px;
  font-weight: 700;
  padding: 2px 9px;
  border-radius: 999px;
  white-space: nowrap;
  background: var(--surface-tint);
  border: 1px solid var(--line);
  color: var(--muted);
}
.tier-pill--1 { background: var(--paper); border-color: var(--line); color: var(--primary-700); }
.tier-pill--2 { background: var(--sky-bg); border-color: var(--line); color: var(--sky-ink); }
.tier-pill--3 { background: var(--teal-bg); border-color: var(--line); color: var(--teal-ink); }
.tier-pill--4 { background: var(--coral-bg); border-color: var(--line); color: var(--danger-ink); }

/* Explanatory card — the primitives and lifecycle blocks on the engagement
   screens, where a panel would be too heavy for one short definition. */
.model-card {
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: 14px;
  background: var(--surface-tint);
}
.model-card h4 { font-size: 14px; margin-bottom: 4px; }
.model-card p { font-size: 12.5px; color: var(--muted); margin-bottom: 8px; }
.model-card--now { border-style: dashed; }
.model-card--next { border-color: var(--brand-sky); background: var(--sky-bg); }

/* "This is configured elsewhere" note — a derived value telling you where it
   comes from, so a read-only field does not look like a broken editable one. */
.config-note {
  font-size: 12px;
  color: var(--muted);
  border-left: 3px solid var(--brand-teal);
  padding: 2px 0 2px 10px;
  margin: 10px 0 0;
}


/* A register header may wrap, so a long column name does not force the column
   wider than its data needs. */
.table th.col-wrap { white-space: normal; }

/* --- W2 engagement screens ----------------------------------------------
   Ported from docs/mockups/_mockup.css now that wave 2 gives these backing
   fields. Landed centrally: three workers need overlapping subsets, and a
   second stylesheet would mean a second request, a second MapStaticAssets
   staleness trap, and two files to keep in step. Raw mockup hexes are mapped
   onto existing tokens — the warning figure reuses the pill-amber tint rather
   than becoming a fourth amber, which W1-33 exists to prevent. */
/* Health score /50 — derived band, shown as value + meter */
.score {
  display: flex;
  align-items: center;
  gap: 8px;
  white-space: nowrap;
}
.score b { font-size: 13px; color: var(--ink-strong); }
.score .meter { width: 56px; min-width: 56px; }
.score-band {
  font-size: 11px;
  font-weight: 700;
  color: var(--muted);
}

/* Engagement strip — tier, score, next review, champion on the 360 */
.engagement-strip {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
  gap: 14px;
  align-items: center;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  padding: 14px 16px;
  margin-top: 14px;
  box-shadow: var(--shadow-card);
}
.engagement-strip > div span {
  display: block;
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--muted);
  margin-bottom: 3px;
}
.engagement-strip > div strong {
  font-size: 14px;
  color: var(--ink-strong);
}
.engagement-strip > div small { display: block; color: var(--muted); font-size: 12px; }

/* Health-score dimension rows (quarterly review tab) */
.dimension-row {
  display: grid;
  grid-template-columns: minmax(120px, 1.4fr) 1fr auto;
  gap: 12px;
  align-items: center;
  padding: 8px 0;
  border-top: 1px solid var(--line-soft);
}
.dimension-row:first-of-type { border-top: 0; }
.dimension-row label { font-weight: 700; font-size: 12.5px; color: var(--ink); }
.dimension-row input[type="range"] { width: 100%; }
.dimension-row b { font-size: 13px; color: var(--ink-strong); min-width: 22px; text-align: right; }

/* Weighted score groups — adoption 50 / capability 20 / data health 30 */
.dimension-group {
  margin-top: 14px;
  padding-top: 10px;
  border-top: 2px solid var(--line);
}
.dimension-group:first-of-type { margin-top: 4px; }
.dimension-group-head {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: 10px;
  margin-bottom: 2px;
}
.dimension-group-head strong {
  font-size: 12px;
  font-weight: 700;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--ink-strong);
}
.dimension-group-head span {
  font-size: 12.5px;
  font-weight: 700;
  color: var(--muted);
  white-space: nowrap;
}
.dimension-group-note {
  font-size: 12px;
  color: var(--muted);
  margin: 0 0 4px;
}

/* Score history — the trend the prototype stores and never shows */
.score-history {
  display: flex;
  gap: 10px;
  align-items: flex-end;
  padding: 10px 0 2px;
}
.score-history div { text-align: center; flex: 1; }
.score-history i {
  display: block;
  background: var(--brand-sky);
  border-radius: 3px 3px 0 0;
  margin-bottom: 5px;
}
.score-history span { font-size: 11px; color: var(--muted); display: block; }
.score-history b { font-size: 12px; color: var(--ink-strong); }

/* Theme chips — the rotating quarterly focus */
.chip-row { display: flex; flex-wrap: wrap; gap: 7px; }
.chip {
  font-size: 12px;
  font-weight: 700;
  padding: 5px 11px;
  border-radius: 999px;
  border: 1px solid var(--line);
  background: var(--surface);
  color: var(--muted);
  cursor: pointer;
}
.chip.is-active {
  background: var(--brand-primary);
  border-color: var(--brand-primary);
  color: #fff;
}

/* Engagement cycle reference rows */
.cycle-row {
  display: grid;
  grid-template-columns: 52px minmax(120px, 1fr) 2fr minmax(140px, 1fr);
  gap: 14px;
  align-items: center;
  padding: 11px 0;
  border-top: 1px solid var(--line-soft);
}
.cycle-row:first-of-type { border-top: 0; }
.cycle-row > b:first-child {
  font-size: 15px;
  color: var(--faint);
}
.cycle-row.is-current {
  background: var(--sky-bg);
  border-radius: var(--radius);
  padding-left: 12px;
  padding-right: 12px;
}
.cycle-row.is-current > b:first-child { color: var(--sky-ink); }

/* ---- Review pack: a printable document, not an app screen ----
   Client-facing, so it carries CASM branding rather than app chrome.
   Sized to A4 and stripped back for print. */
.doc-wrap {
  max-width: 900px;
  margin: 0 auto;
  padding: 24px 20px 48px;
}
.doc-sheet {
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-card);
  overflow: hidden;
  margin-bottom: 34px;
}
.doc-head {
  background: var(--brand-primary);
  color: #fff;
  padding: 20px 26px;
}
.doc-head .eyebrow { color: var(--brand-teal); }
.doc-head h2 { color: #fff; font-size: 21px; margin-bottom: 4px; }
.doc-head p { color: rgba(255, 255, 255, 0.82); margin: 0; font-size: 13px; }
.doc-head-meta {
  display: flex;
  flex-wrap: wrap;
  gap: 18px;
  margin-top: 12px;
  padding-top: 12px;
  border-top: 1px solid rgba(255, 255, 255, 0.22);
  font-size: 12px;
  color: rgba(255, 255, 255, 0.82);
}
.doc-head-meta strong { color: #fff; display: block; font-size: 13px; }

.doc-body { padding: 22px 26px 26px; }
.doc-section { margin-bottom: 22px; }
.doc-section > h3 {
  font-size: 12px;
  font-weight: 700;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  color: var(--brand-primary);
  padding-bottom: 5px;
  margin-bottom: 10px;
  border-bottom: 2px solid var(--brand-teal);
}
.doc-section p { font-size: 13px; }

/* Figure row — the numbers a review is argued from */
.figure-row {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(130px, 1fr));
  gap: 12px;
}
.figure {
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: 10px 12px;
  background: var(--surface-tint);
}
.figure span {
  display: block;
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.03em;
  text-transform: uppercase;
  color: var(--muted);
  margin-bottom: 3px;
}
.figure strong { font-size: 19px; color: var(--ink-strong); display: block; line-height: 1.15; }
.figure small { font-size: 11.5px; color: var(--muted); }
.figure--warn { border-color: rgba(178, 106, 24, 0.35); background: rgba(178, 106, 24, 0.10); }
.figure--warn strong { color: var(--warn); }

/* Internal-only band — never leaves the building */
.internal-band {
  border: 1px dashed var(--danger-solid);
  border-radius: var(--radius);
  background: var(--coral-bg);
  padding: 14px 16px;
  margin-bottom: 22px;
}
.internal-band > h3 {
  font-size: 12px;
  font-weight: 700;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  color: var(--danger-ink);
  margin-bottom: 8px;
  border: 0;
  padding: 0;
}
.internal-band p { font-size: 12.5px; color: var(--danger-ink); }

.doc-foot {
  border-top: 1px solid var(--line);
  padding-top: 12px;
  margin-top: 4px;
  font-size: 11.5px;
  color: var(--muted);
}

/* Model diagram (mockup 04) */
.model-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
  gap: 14px;
}

/* Capture rows — the five actions a review may raise, each becoming a reminder */
.capture-row {
  display: grid;
  grid-template-columns: minmax(0, 2.2fr) minmax(140px, 1fr) minmax(130px, 0.7fr) auto;
  gap: 10px;
  align-items: end;
  padding: 9px 0;
  border-top: 1px solid var(--line-soft);
}
.capture-row:first-of-type { border-top: 0; }
.capture-row .field { margin: 0; }
.capture-row-num {
  font-size: 12px;
  font-weight: 700;
  color: var(--faint);
  padding-bottom: 9px;
}
@media (max-width: 820px) {
  .capture-row { grid-template-columns: 1fr; align-items: stretch; }
}

/* Capture and document toolbars */

/* Sticky action bar on a capture screen — save/draft/cancel stay reachable
   without scrolling a seven-step form to the bottom. */
.capture-actions {
  position: sticky;
  bottom: 0;
  display: flex;
  flex-wrap: wrap;
  gap: 10px;
  align-items: center;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  padding: 12px 14px;
  margin-top: 16px;
  box-shadow: var(--shadow-card);
}
.capture-actions .text-muted { font-size: 12px; }

/* Screen-only toolbar above a printable document. */
.doc-toolbar {
  display: flex;
  flex-wrap: wrap;
  gap: 10px;
  align-items: center;
  justify-content: space-between;
  margin-bottom: 10px;
}
.doc-toolbar h2 { font-size: 16px; }

/* The prepared pack read back on the close screen: what you walked in with. */
.walked-in-list { list-style: none; padding: 0; margin: 0; font-size: 12.5px; }
.walked-in-list li + li { margin-top: 6px; }

@media print {
  .doc-toolbar, .capture-actions { display: none; }
  .doc-wrap { max-width: none; padding: 0; }
  .doc-sheet { border: 0; box-shadow: none; border-radius: 0; page-break-after: always; }
}

/* ============================================================
   Help — "How ClientPulse works" (mockup 13)
   The page explains derived logic, so its one genuinely new mark
   is .help-live: a value READ from configuration at render time
   rather than typed into the prose. It is deliberately quiet —
   a tinted chip, not a badge — because a page speckled with
   highlights reads as a warning rather than as documentation.
   ============================================================ */

.help-live {
  padding: 1px 7px;
  border-radius: var(--radius);
  background: rgba(23, 105, 163, 0.10);
  box-shadow: inset 0 0 0 1px rgba(23, 105, 163, 0.28);
  color: var(--ink-strong);
  font-weight: 700;
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}

.help-key {
  margin: 0 0 18px;
  padding: 11px 14px;
  border-radius: var(--radius);
  background: var(--surface-tint);
  box-shadow: inset 0 0 0 1px var(--line);
  font-size: 13px;
}

/* One at-risk arm: the condition, why it exists, and the exact wording the
   rule produces — so a user can match what they read here to what they saw
   on the queue, character for character. */
.help-arm {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1.15fr);
  gap: 4px 22px;
  padding: 14px 0;
  border-top: 1px solid var(--line-soft);
}

.help-arm:first-of-type {
  border-top: 0;
}

.help-arm-when {
  font-weight: 700;
  color: var(--ink-strong);
}

.help-arm-why {
  color: var(--muted);
  font-size: 13px;
}

.help-arm-says {
  grid-column: 1 / -1;
  margin-top: 6px;
  padding: 7px 11px;
  border-left: 3px solid var(--brand-sky);
  border-radius: 0 var(--radius) var(--radius) 0;
  background: var(--surface-tint);
  font-size: 13px;
}

/* Cadence precedence — numbered because "most specific wins" is the whole
   point, and an unordered list would lose it. */
.help-precedence {
  display: grid;
  gap: 8px;
  margin: 0;
  padding: 0;
  list-style: none;
  counter-reset: help-step;
}

.help-precedence li {
  display: grid;
  grid-template-columns: 24px minmax(0, 1fr);
  gap: 12px;
  align-items: start;
  padding: 11px 13px;
  border-radius: var(--radius);
  background: var(--surface-tint);
  box-shadow: inset 0 0 0 1px var(--line);
}

.help-precedence li::before {
  counter-increment: help-step;
  content: counter(help-step);
  display: grid;
  place-items: center;
  width: 24px;
  height: 24px;
  border-radius: 999px;
  background: var(--brand-primary);
  color: #ffffff;
  font-size: 12px;
  font-weight: 700;
}

.help-toc {
  columns: 2;
  column-gap: 32px;
  margin: 0;
  padding: 0;
  list-style: none;
}

.help-toc li {
  break-inside: avoid;
  margin-bottom: 7px;
}

@media (max-width: 720px) {
  .help-toc { columns: 1; }
  .help-arm { grid-template-columns: 1fr; }
}

/* ============================================================
   Actions dashboard (mockup 14) + tabbed client edit (15)
   Merged from the two build agents' scratch files. Reviewed
   here rather than written by them: site.css has one writer,
   which is how a stylesheet this heavily tokened stays
   coherent. Every custom property referenced was checked to
   exist, and no selector collides with an existing rule.
   ============================================================ */

/* --- Mockup 14: the Actions tab ----------------------------------------
   Three additions, all built from tokens already in site.css. Nothing here
   introduces a colour, a radius or a type scale that does not exist.

   Append after the existing .scope-bar / .scope-choice block (~line 2012),
   since .when-choice depends on .scope-choice .btn.is-active being defined
   first — it deliberately overrides it. */

/* 1. Two toggles on one bar, and they must not read as one four-way control.
      Each gets its own label, and a hairline rule separates them. */
.scope-group {
  display: flex;
  align-items: center;
  gap: 8px;
}
.scope-group > .eyebrow { margin: 0; }

.scope-split {
  align-self: stretch;
  width: 1px;
  margin: 0 4px;
  background: var(--line);
}

/* 2. The When toggle. Same button component as Who, deliberately a different
      KIND of emphasis — tinted and inset rather than a solid navy fill — so a
      glance separates the two choices without a second colour vocabulary.
      Teal is legitimate here because it is chrome, not status: --teal-ink is
      the token site.css already reserves for non-status use (.tag, .tier-pill--3).
      Status teal is jade and is untouched. */
.when-choice .btn.is-active {
  background: var(--teal-bg);
  border-color: var(--teal-ink);
  color: var(--teal-ink);
  box-shadow: inset 0 -2px 0 var(--teal-ink);
}

/* 3. Section labels inside a panel — "Acting now" / "Approaching",
      "Overdue and today" / "Upcoming". A divider plus a count, not a heading:
      the panel already has its h2, and these are divisions of one list. */
.sub-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 10px;
  margin: 0 0 8px;
  padding-top: 12px;
  border-top: 1px solid var(--line-soft);
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  color: var(--muted);
}
.sub-head:first-of-type {
  border-top: 0;
  padding-top: 0;
}
.sub-head b {
  color: var(--ink-strong);
  font-size: 12px;
}

.sub-note {
  margin: -4px 0 8px;
  font-size: 12px;
  color: var(--muted);
}

/* A section that follows a list needs air above its rule. */
.queue-list + .sub-head,
.stack-list + .sub-head,
.pager + .sub-head,
.empty-state + .sub-head {
  margin-top: 16px;
}

/* ============================================================
   Mockup 15 — the tabbed client form
   Three additions and two small tweaks. Everything else the form
   uses (.tab-bar, .tab-btn, .field, .form-grid, .check-grid,
   .validation-summary-errors, .form-actions) ships already.
   ============================================================ */

/* The error count on a tab header. Coral because it is the alert accent, and
   never the sole signal: the same count is in the summary above, and an
   sr-only sibling says what the digit means. */
.tab-badge {
  display: inline-grid;
  place-items: center;
  min-width: 18px;
  height: 18px;
  margin-left: 7px;
  padding: 0 5px;
  border-radius: 999px;
  background: var(--danger-solid);
  color: #ffffff;
  font-size: 11px;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
  vertical-align: 1px;
}

/* On the active (solid blue) tab the coral badge would fight the fill —
   invert it instead. */
.tab-btn.active .tab-badge {
  background: #ffffff;
  color: var(--danger-ink);
}

/* Which tab an error is on, in the summary. --danger-ink at 14%: the opaque
   --coral-bg disappears against the summary block's own coral tint. */
.err-tab {
  display: inline-block;
  margin-right: 7px;
  padding: 1px 7px;
  border-radius: 999px;
  background: rgba(176, 54, 54, 0.14);
  font-size: 11px;
  letter-spacing: 0.03em;
  text-transform: uppercase;
}

/* Summary lines are now links that switch tab and focus the field, so they
   have to read as links and need room to breathe between them. */
.validation-summary-errors .summary-lead {
  margin: 0 0 7px;
}

.validation-summary-errors li {
  margin-bottom: 5px;
}

.validation-summary-errors a {
  color: var(--danger-ink);
  text-decoration: underline;
}

/* A pane's opening sentence — was a <fieldset> hint, and the fieldset has gone
   now that the tab header carries the section name. */
.pane-lead {
  display: block;
  margin-bottom: 12px;
}

/* ============================================================
   Collapsible rail groups + note truncation.
   Merged here from the build agents' scratch files: site.css
   keeps one writer. Tokens checked to exist, selectors checked
   against every existing rule for collisions.
   ============================================================ */

/* ============================================================
   Collapsible rail groups (mockup 12, favourites half dropped)
   Merge target: site.css, immediately after the .nav-item rules
   (~line 365, before .sidebar-foot). Replaces nothing — .nav-section
   is now unused by _Layout.cshtml but is left in place.
   ============================================================ */

/* A group label becomes a button. Same type, colour and rhythm as
   .nav-section — only a chevron and a count are added, so a collapsed rail
   still reads as the rail people already know. Vertical padding is 6px
   rather than the label's 0 so the target clears 24px on a tablet. */
.nav-section-btn {
  display: flex;
  align-items: center;
  gap: 8px;
  width: 100%;
  margin: 12px 0 2px;
  padding: 6px 12px;
  border: 0;
  border-radius: var(--radius);
  background: none;
  color: var(--rail-label);
  font: inherit;
  font-size: 10px;
  font-weight: 700;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  text-align: left;
  cursor: pointer;
}

.nav-section-btn:hover {
  background: rgba(255, 255, 255, 0.07);
  color: #ffffff;
}

.nav-section-btn:focus-visible {
  outline: 2px solid var(--brand-sky);
  outline-offset: 2px;
}

.nav-section-name {
  flex: 1 1 auto;
}

/* The nested list carries the .nav-list rhythm so a grouped rail spaces
   exactly as the flat one did. */
.nav-group-items {
  display: grid;
  gap: 2px;
  margin: 0;
  padding: 0;
  list-style: none;
}

.nav-chev {
  flex: none;
  width: 12px;
  height: 12px;
  stroke: currentColor;
}

@media (prefers-reduced-motion: no-preference) {
  .nav-chev {
    transition: transform 120ms ease;
  }
}

/* A collapsed group still says how much is inside. Hidden while expanded,
   where the links speak for themselves — display:none also keeps the count
   out of the button's accessible name until it is the only thing left. */
.nav-count {
  flex: none;
  padding: 1px 6px;
  border-radius: 999px;
  background: rgba(255, 255, 255, 0.14);
  color: var(--on-rail);
  font-size: 10px;
  font-weight: 700;
  letter-spacing: 0.04em;
}

.nav-section-btn[aria-expanded="true"] .nav-count {
  display: none;
}

.nav-section-btn[aria-expanded="false"] .nav-chev {
  transform: rotate(-90deg);
}

/* Pre-paint state. The inline script in <head> writes the saved keys to
   <html data-nav-collapsed> before the rail is parsed, so a group saved as
   collapsed is never painted open first — applying it from site.js after
   paint made the sidebar jump on every navigation. site.js then sets
   aria-expanded to match and keeps this attribute in step on every toggle,
   so the two selector families never disagree once JS has run.
   Group keys are enumerated because CSS cannot match an ancestor's
   attribute against a descendant's; there are four, and they are static. */
:root[data-nav-collapsed~="relationship"] [data-nav-group="relationship"] > .nav-group-items,
:root[data-nav-collapsed~="commercial"] [data-nav-group="commercial"] > .nav-group-items,
:root[data-nav-collapsed~="delivery"] [data-nav-group="delivery"] > .nav-group-items,
:root[data-nav-collapsed~="admin"] [data-nav-group="admin"] > .nav-group-items {
  display: none;
}

:root[data-nav-collapsed~="relationship"] [data-nav-group="relationship"] > .nav-section-btn > .nav-chev,
:root[data-nav-collapsed~="commercial"] [data-nav-group="commercial"] > .nav-section-btn > .nav-chev,
:root[data-nav-collapsed~="delivery"] [data-nav-group="delivery"] > .nav-section-btn > .nav-chev,
:root[data-nav-collapsed~="admin"] [data-nav-group="admin"] > .nav-section-btn > .nav-chev {
  transform: rotate(-90deg);
}

:root[data-nav-collapsed~="relationship"] [data-nav-group="relationship"] > .nav-section-btn > .nav-count,
:root[data-nav-collapsed~="commercial"] [data-nav-group="commercial"] > .nav-section-btn > .nav-count,
:root[data-nav-collapsed~="delivery"] [data-nav-group="delivery"] > .nav-section-btn > .nav-count,
:root[data-nav-collapsed~="admin"] [data-nav-group="admin"] > .nav-section-btn > .nav-count {
  display: inline-block;
}

/* ============================================================
   Note previews and clamped tile text
   Paul, on the notes list, the register and the 360 tiles: a note is a
   paragraph of record, not a headline, and every surface carrying one was
   painting the whole thing — one meeting note filled the screen.

   These clamp what is PAINTED. Nothing is truncated server-side, so the full
   text stays in the DOM for find-in-page, for copy-and-paste, and for a screen
   reader reading straight through.
   ============================================================ */

/* The note body in a list: three lines, opened in place by .note-body-toggle.
   -webkit-line-clamp is still the recipe that works everywhere; the unprefixed
   line-clamp is beside it for the browsers that have caught up. */
.note-body-text {
  display: -webkit-box;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 3;
  line-clamp: 3;
  overflow: hidden;
  overflow-wrap: anywhere;
}

/* Open: back to a normal paragraph, so pre-line newlines lay out as they
   always did. The clamp only ever changed the closed state. */
.note-body-text.is-open {
  display: block;
  -webkit-line-clamp: none;
  line-clamp: none;
  overflow: visible;
}

.note-body-toggle {
  margin-top: 6px;
  font-size: 12.5px;
}

/* Two lines, for text that lands somewhere with no room to grow: the register's
   next-step cell and the 360's Last interaction / Next contact tiles. All three
   can carry a note body — a reminder back-filled from an action-note took the
   note's text as its reason. No toggle here; the 360 and the note hold the rest. */
.clamp-2 {
  display: -webkit-box;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 2;
  line-clamp: 2;
  overflow: hidden;
  overflow-wrap: anywhere;
}

/* .clamp-2 ends its own line, so the <br> that used to separate the tile's
   closing line went with it — this is that line, now a block of its own. */
.tile-line {
  display: block;
}

/* ============================================================
   Editor sections, inline row removal, and add dialogs
   Introduced for the review-template editor (admin → engagement →
   review templates), where three checklist columns at a fixed 200px
   field width squashed every row onto two lines and the add forms sat
   in a separate block at the foot of the page.
   ============================================================ */

/* An h3 with the action that belongs to it. .panel h3 carries a bottom
   margin for the prose that normally follows a heading; in a flex row
   that margin pushes the heading off the button's centre line. */
.editor-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  flex-wrap: wrap;
}

.editor-head h3 {
  margin-bottom: 0;
}

/* A .btn sized to sit beside a heading or inside a card rather than in a
   form's action row — same border, background and weight, less presence. */
.btn-compact {
  min-height: 30px;
  padding: 4px 11px;
  font-size: 12.5px;
}

/* Stage columns. Three of them plus the gaps need 3×290 + 28 = 898px, and the
   shell gives 911px inside the panel at a 1280px window — the narrowest screen
   these are read on — so Before/During/After stay side by side there, which is
   the point of showing a sequence. 290px is enough for the field and its remove
   control on ONE line (290 − 28 card padding − 32 button − 6 gap = 224px of
   field, and the field shrinks rather than wrapping). Narrower than that,
   auto-fit drops to two columns and then one instead of squashing. */
.checklist-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(290px, 1fr));
  gap: 14px;
  align-items: start;
}

.checklist-card {
  display: flex;
  flex-direction: column;
  gap: 6px;
}

.checklist-card-head {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 10px;
  margin-bottom: 4px;
}

.checklist-card-head h4,
.checklist-card-head p {
  margin: 0;
}

/* The field takes the column; min-width:0 is what lets it, since a flex item
   will not shrink below its intrinsic width without it — the failure that put
   the remove control on its own line in the first place. */
.checklist-row {
  display: flex;
  align-items: center;
  gap: 6px;
}

.checklist-text {
  flex: 1 1 auto;
  min-width: 0;
}

/* Inline removal: quiet until it is wanted, and unmistakable once it is.
   Muted rather than faint because it is an interactive control, so it owes
   3:1 against the surface it sits on. */
.row-remove {
  flex: 0 0 auto;
  display: inline-grid;
  place-items: center;
  width: 32px;
  height: 32px;
  min-height: 32px;
  padding: 0;
  border: 1px solid transparent;
  border-radius: var(--radius);
  background: transparent;
  color: var(--muted);
  font-size: 18px;
  line-height: 1;
}

.row-remove:hover,
.row-remove:focus-visible {
  color: var(--danger-ink);
  background: var(--coral-bg);
  border-color: var(--brand-coral);
}

/* Touch: a 32px target is fine for a mouse and too small for a thumb. */
@media (pointer: coarse) {
  .row-remove {
    width: 44px;
    height: 44px;
    min-height: 44px;
  }
}

@media (prefers-reduced-motion: no-preference) {
  .row-remove {
    transition: background-color 0.15s ease, border-color 0.15s ease, color 0.15s ease;
  }
}

/* ---- Add dialogs (native <dialog>, opened with showModal) ---------------- */
/* The element brings the focus trap, Escape, the top layer and the backdrop;
   all this does is make it look like the rest of the app. */
dialog.modal {
  width: min(520px, calc(100vw - 32px));
  max-height: calc(100vh - 48px);
  /* max-height without overflow is a trap: the admin add-dialogs are short
     enough never to reach it, but the Discovery gate carries a client name and
     type, the Prospect line, and four contact fields — on a laptop that spills
     past the cap and puts Confirm out of reach, with no way to scroll to it. */
  overflow: auto;
  padding: 0;
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  background: var(--surface);
  color: var(--ink);
  box-shadow: var(--shadow);
}

dialog.modal::backdrop {
  background: rgba(12, 44, 68, 0.45);
}

/* The form IS the card — a wrapper between the two would only be there to
   carry padding. Grid, so the fields space themselves. */
.modal-card {
  display: grid;
  gap: 14px;
  margin: 0;
  padding: 18px 20px 20px;
}

.modal-head {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 12px;
}

.modal-head h3 {
  margin: 0;
  font-size: 16px;
}

.modal-actions {
  display: flex;
  flex-wrap: wrap;
  justify-content: flex-end;
  gap: 10px;
}

@media (prefers-reduced-motion: no-preference) {
  dialog.modal[open] {
    animation: modal-in 0.14s ease-out;
  }

  @keyframes modal-in {
    from {
      opacity: 0;
      transform: translateY(6px);
    }
  }
}


/* Clamped text on 360 tiles (mockup follow-up). Merged from the tile
   agent's scratch file, minus rules whose selectors the notes merge had
   already defined. */

/* ============================================================
   Append to the "Note previews and clamped tile text" block in site.css,
   after the existing .note-body-toggle rule (~line 3274).
   ============================================================ */

/* The preview wrapper is a <span>, not a <div>: a 360 metric tile nests it
   inside a <small>, which may only hold phrasing content. Nothing else about
   the box changes. */
.note-body {
  display: block;
}

/* The same preview on a metric tile: two lines, not the list's three. It also
   has to re-assert display, because `.metric span` (line 557) sets block at
   (0,1,1) and outranks the plain .note-body-text class — that is why the Last
   interaction tile was still painting a whole meeting note despite the clamp. */
.note-body-text.note-body-text-tile {
  display: -webkit-box;
  -webkit-line-clamp: 2;
  line-clamp: 2;
}

/* Open: as .note-body-text.is-open, but it must outrank the tile rule above. */
.note-body-text.note-body-text-tile.is-open {
  display: block;
  -webkit-line-clamp: none;
  line-clamp: none;
  overflow: visible;
}

/* Show more / Show less, sized for 12px tile type. */
.note-body-toggle.note-body-toggle-tile {
  margin-top: 4px;
  font-size: 12px;
}

/* ============================================================
   Leads without a client (36th follow-up): the unworked-leads
   strand on Actions, and the deal capture / Discovery gate.
   Merged here from the build agents' scratch files — site.css
   keeps one writer. Every token verified to exist and every
   selector checked against the existing rules for collisions.
   ============================================================ */

/* ============================================================
   Actions → Unworked leads (third strand, thirty-sixth follow-up)
   ------------------------------------------------------------
   The panel, its rows and its empty state reuse .panel, .stack-list,
   .row-card, .row-top, .row-title, .row-meta, .pill-* and .empty-state
   unchanged — a lead row is a record card exactly like the reminder
   above it, and it must not look like a second species.

   The ONE thing with no existing class: a captured lead has no client
   record, so its phone and email ARE the record until Discovery. They
   sit on the row rather than a click away, and on a tablet they have to
   be dialable without a pinch.
   ============================================================ */

.lead-contacts {
  display: flex;
  flex-wrap: wrap;
  gap: 2px 16px;
  margin: 6px 0 10px;
  font-size: 13px;
}

/* Tall enough to hit with a thumb without stretching the row: the padding
   is vertical only, so the two links still read as one line of detail. */
.lead-contacts a {
  display: inline-block;
  padding: 5px 0;
  font-weight: 700;
  color: var(--brand-primary);
  word-break: break-word;
}

.lead-contacts a:hover,
.lead-contacts a:focus-visible {
  color: var(--primary-700);
}

/* Neither an error nor an action — just a fact about what was captured. */
.lead-contacts--none {
  color: var(--faint);
}

/* ============================================================
   Leads without a client — capture form + Lead → Discovery gate
   (DECISIONS thirty-sixth follow-up)

   Two screens share one pattern: an either/or choice made ONCE at
   the top, with only the chosen branch's fields shown beneath it.
   Used by the deal capture form (existing client vs new lead) and
   by the gate (create the client vs pick the one already in the book).

   No new tokens, no new colour vocabulary. The option card is the
   .assignee-option idiom (native input inside a label, :has() for
   the checked state) scaled up to a card, because a mutually
   exclusive choice earns a visible radio rather than a hidden one.

   Append after the .assignee-* block (~line 1271 of site.css), which
   is where the :has() pattern it follows is established.
   ============================================================ */

.party-block {
  margin-bottom: 4px;
}

/* Fieldset reset matched to .assignee-field, so the legend sits like
   a .field label rather than like a browser default. */
.party-choice {
  border: 0;
  margin: 0 0 12px;
  padding: 0;
  min-width: 0;
}

.party-choice legend {
  margin-bottom: 8px;
  padding: 0;
  color: var(--ink-mid);
  font-weight: 700;
}

/* Two cards, side by side while there is room and stacked when there
   is not — the choice must never be a scroll away from its fields. */
.party-options {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
  gap: 10px;
}

.party-option {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr);
  grid-template-areas:
    "radio label"
    ".     hint";
  gap: 3px 10px;
  align-items: start;
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  background: var(--surface);
  padding: 12px 14px;
  cursor: pointer;
  /* Body text, stated rather than inherited. (.field no longer sets 700 on its
     contents — see the note there — but this was always the intent.) */
  font-weight: 400;
}

.party-option input {
  grid-area: radio;
  margin: 2px 0 0;
}

.party-option-label {
  grid-area: label;
  color: var(--ink-strong);
  font-weight: 700;
}

.party-option-hint {
  grid-area: hint;
  color: var(--muted);
  font-size: 12px;
  line-height: 1.4;
}

.party-option:hover {
  border-color: var(--brand-sky);
}

.party-option:has(input:checked) {
  border-color: var(--brand-primary);
  background: var(--sky-bg);
  /* Inset rather than a 2px border: a thicker border would reflow the
     card by a pixel on every change of mind. */
  box-shadow: inset 0 0 0 1px var(--brand-primary);
}

.party-option:has(input:focus-visible) {
  outline: 2px solid var(--brand-sky);
  outline-offset: 2px;
}

/* Standing in for the fields until a choice is made, so an empty
   capture form reads as "answer this first" rather than as broken. */
.party-prompt {
  margin: 0 0 14px;
  font-size: 13px;
}

.party-block:has(.party-radio:checked) .party-prompt {
  display: none;
}

/* The chosen branch's fields, tied to the choice by a rule rather than
   by another box — the form already has enough boxes. */
.party-panel {
  border-left: 3px solid var(--brand-sky);
  padding-left: 14px;
  margin-bottom: 2px;
}

.party-panel > .form-grid + .field-hint,
.party-panel > .field-hint {
  display: block;
  margin-top: 2px;
}

.party-panel h3 {
  margin-top: 14px;
}

/* ---- The reveal ---------------------------------------------------------
   Value-keyed pairs rather than ids, so the same four rules serve both
   screens and the enum names in the markup are the only contract.

   Deliberately written as HIDE rules with a visible default: where :has()
   is unsupported the whole block is dropped, both branches show, and the
   form still works because the server is what enforces the either/or.
   A show-only scheme would leave that browser with no fields at all. */
.party-block:not(:has(.party-radio:checked)) .party-panel,
.party-block:has(.party-radio[value="Client"]:checked) .party-panel[data-party="Lead"],
.party-block:has(.party-radio[value="Lead"]:checked) .party-panel[data-party="Client"],
.party-block:has(.party-radio[value="Create"]:checked) .party-panel[data-party="Existing"],
.party-block:has(.party-radio[value="Existing"]:checked) .party-panel[data-party="Create"] {
  display: none;
}

/* ---- Deal detail: the captured lead ------------------------------------
   While there is no client these four fields ARE the record of who this
   is, so they sit on the page rather than behind Edit. */
.lead-card {
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  background: var(--surface-tint);
  padding: 14px 16px;
  margin-bottom: 16px;
}

.lead-card h3 {
  margin: 0 0 10px;
  font-size: 14px;
}

.lead-card > p {
  margin: 0;
  font-size: 13px;
}

.lead-facts {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(160px, 1fr));
  gap: 12px;
  margin: 0 0 10px;
}

.lead-facts dt {
  color: var(--muted);
  font-size: 12px;
}

.lead-facts dd {
  margin: 0;
  color: var(--ink-strong);
  font-weight: 700;
  overflow-wrap: anywhere;
}

/* ---- The gate ----------------------------------------------------------- */

/* What the phone call captured, shown above the form it pre-filled. */
.gate-lead {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 8px;
  margin: 0 0 16px;
}

/* Prospect is stated, not offered — a fixed value needs to look unlike a
   field, or somebody will hunt for the dropdown. */
.gate-fixed {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 8px;
  margin: 0 0 4px;
  color: var(--ink-mid);
  font-weight: 700;
}

.gate-fixed .text-muted {
  font-size: 12px;
  font-weight: 400;
}


/* A dialog carrying a full form rather than one or two fields. The gate renders
   the same form the full-page fallback shows at measure-wide (860px); at the
   base 520px its .form-grid collapses to two ~233px columns and the party
   options stack. Widening here rather than changing the base keeps the short
   admin add-dialogs exactly as they were. */
dialog.modal.modal-wide {
  width: min(760px, calc(100vw - 32px));
}

/* ============================================================
   Markdown capture fields (DECISIONS thirty-seventh follow-up)
   Two components: .md-editor, the chrome wrapped round a plain
   textarea; and .prose, rendered markdown wherever a narrative
   field is displayed in full.
   ============================================================ */

/* ---- The editor ---- */
.md-editor {
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: var(--surface);
  overflow: hidden;
  /* .field no longer sets a weight, but be explicit: long-form prose is read
     and written at 400 whatever a future parent decides. */
  font-weight: 400;
}

/* The wrapper takes the focus treatment the textarea normally gets, because
   .md-input's own ring is suppressed below — the frame is the control now. A
   border-colour change alone is not a focus affordance and fails WCAG 2.4.7. */
.md-editor:focus-within {
  border-color: var(--brand-sky);
  box-shadow: var(--focus-ring);
}

.md-toolbar {
  display: flex;
  flex-wrap: wrap;
  gap: 10px;
  align-items: center;
  justify-content: space-between;
  padding: 7px 8px;
  background: var(--surface-tint);
  border-bottom: 1px solid var(--line);
}

.md-tools {
  display: flex;
  flex-wrap: wrap;
  gap: 2px;
  align-items: center;
}

/* Sized well below .btn: a formatting button above a five-row Pain box must not
   out-weigh the Save button under it. */
.md-tool {
  min-width: 30px;
  min-height: 30px;
  padding: 4px 8px;
  border: 1px solid transparent;
  border-radius: 6px;
  background: transparent;
  color: var(--muted);
  font-size: 12.5px;
  font-weight: 700;
  line-height: 1;
  cursor: pointer;
}

.md-tool:hover:not(:disabled) {
  background: var(--surface);
  border-color: var(--line);
  color: var(--ink-strong);
}

.md-tool:disabled {
  opacity: 0.4;
  cursor: default;
}

/* Same reasoning, and the same 44px, as .row-remove above — a 30px target is
   fine for a mouse and too small for a thumb. */
@media (pointer: coarse) {
  .md-tool {
    min-width: 44px;
    min-height: 44px;
  }
}

/* Tidy is the odd one out: every other button inserts a mark at the cursor,
   this one rewrites the whole box. The border at rest is the warning. */
.md-tool--tidy {
  border-color: var(--line);
  color: var(--sky-ink);
}

.md-tool-sep {
  width: 1px;
  height: 18px;
  margin: 0 5px;
  background: var(--line);
}

/* The Write/Preview toggle reuses .tab-bar/.tab-btn, shrunk — it is the same
   control as the 360's segmented tabs and should read as the same idea, just
   not at page-tab weight inside a form field. */
.md-modes {
  margin-bottom: 0;
  box-shadow: none;
}

.md-modes .tab-btn {
  padding: 5px 12px;
  font-size: 12.5px;
}

/* Drop the shared border and radius from the textarea rule — the wrapper owns
   the frame now, and a second one inside it reads as a bug. */
.md-editor .md-input {
  border: 0;
  border-radius: 0;
  min-height: 150px;
  padding: 14px;
  resize: vertical;
}

.md-editor .md-input:focus {
  outline: 0;
  box-shadow: none;
}

.md-preview {
  min-height: 150px;
  padding: 14px;
}

/* ---- .prose — rendered markdown ---- */
/* The whole design problem is restraint. This sits INSIDE a .panel that already
   has an <h2>, so markdown headings must read as section labels within the note,
   never as page titles competing with the panel above them. Margins are tight
   for the same reason: a note is a dense record, not an article. */
.prose {
  font-size: 15px;
  line-height: 1.6;
  font-weight: 400;
  color: var(--ink);
}

.prose > :first-child { margin-top: 0; }
.prose > :last-child { margin-bottom: 0; }

/* Markdig maps # -> h1, ## -> h2, ### -> h3 with no demotion (proven in
   MarkdownServiceTests), so h1 has to be styled even though a note should not
   really open with one.

   The scale is bounded at BOTH ends and there are only 2px between them. Above:
   .panel-heading h2 is 17px, and a heading inside a note must not reach the
   heading of the panel containing it. Below: body here is 15px, and a heading
   smaller than the paragraph it introduces is not a heading. So 17 > 16 > 15,
   landing h3 level with body and separated by weight and ink alone, which is as
   far down as a heading can legitimately go. */
.prose h1,
.prose h2,
.prose h3,
.prose h4 {
  color: var(--ink-strong);
  font-weight: 700;
  line-height: 1.3;
  margin: 18px 0 6px;
}

.prose h1 { font-size: 17px; }
.prose h2 { font-size: 16px; }
.prose h3 { font-size: 15px; }

/* h4 deliberately leaves the scale rather than continuing below body size — it
   becomes a label, in the same vocabulary as .eyebrow. */
.prose h4 {
  font-size: 12.5px;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--muted);
}

.prose p { margin: 0 0 10px; }

.prose ul,
.prose ol {
  margin: 0 0 10px;
  padding-left: 22px;
}

.prose li { margin-bottom: 3px; }
.prose li > ul,
.prose li > ol { margin: 3px 0 0; }

.prose blockquote {
  margin: 10px 0;
  padding: 2px 0 2px 12px;
  border-left: 3px solid var(--brand-teal);
  color: var(--muted);
  font-style: italic;
}

.prose code {
  padding: 1px 5px;
  border-radius: 4px;
  background: var(--paper);
  font-size: 0.9em;
}

/* Bordered to match .transcript-box, the other bordered-paper block. */
.prose pre {
  margin: 0 0 10px;
  padding: 12px;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: var(--paper);
  overflow-x: auto;
}

.prose pre code { padding: 0; background: none; }
.prose hr { margin: 14px 0; border: 0; border-top: 1px solid var(--line); }

/* No `.prose a` colour override. A link inside a note is a link, and the global
   rule already says what one looks like. A second link blue for one context is
   exactly the drift this stylesheet spends its length avoiding. */

/* .prose and .prose-preserve are MUTUALLY EXCLUSIVE. .prose-preserve is
   `white-space: pre-line` for raw text that never went through a renderer;
   on rendered markdown it double-spaces every block, because the newlines
   between the source's paragraphs survive inside the emitted HTML. Rendered
   output gets .prose. Un-rendered text keeps .prose-preserve. */

/* Tables never appear: pipe tables are a Markdig extension and stay off. Images
   never appear either, but NOT because of .DisableHtml() — `![alt](url)` is core
   CommonMark, so MarkdownService neutralises it explicitly. This rule is the
   belt-and-braces so a miss cannot also burst the panel. */
.prose img { max-width: 100%; height: auto; }

/* The review pack sets its own body size (.doc-section p is 13px) and links this
   stylesheet, so an unqualified .prose came out mixed: paragraphs at 13px from
   that rule, list items and headings at the 15-17px above it, in one block. Scale
   the whole thing to the document it sits in — the same reasoning that made
   Quotes/Print.cshtml restate .prose at its own 12.5px. */
.doc-section .prose { font-size: 13px; }
.doc-section .prose p { font-size: inherit; }
.doc-section .prose h1 { font-size: 15px; }
.doc-section .prose h2 { font-size: 14px; }
.doc-section .prose h3 { font-size: 13px; }

/* ==========================================================================
   The date-list strip — a list led by its dates (mockup 17, thirty-ninth
   follow-up). Used twice, and only where the question the list answers is
   "when": the dashboard's reminders and to-dos, and the project pipeline.

   The chip's tint is the PILL rule (overdue red, today amber, later sky), not
   a second colour language — one vocabulary means a chip needs no learning.
   Lists that are worked with filters and sorting keep their tables; this is
   for short lists that are read.

   NAMED date-list, not agenda. It shipped as .agenda on 5 August and broke the
   review preparation screen the same day: Reviews/Prepare.cshtml has had
   <tr class="agenda-row"> as a JS hook since long before, so the grid rule
   below landed on those table rows and collapsed the agenda table. A component
   class has to be checked against the markup that already exists — see
   CssCollisionTests, which now fails the build for a layout rule on a table row.
   ========================================================================== */
.date-list {
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: var(--surface);
  overflow: hidden;
  /* Measured against the STRIP, not the viewport: the same component sits in
     the dashboard's ~340px rail and across the full width of the pipeline, and
     it is the rail that decides whether a row fits on one line. A viewport
     query would leave the rail cramped on a wide screen. */
  container-type: inline-size;
}

.date-list-row {
  display: grid;
  grid-template-columns: 54px minmax(0, 1fr) auto;
  gap: 12px;
  align-items: center;
  padding: 10px 12px;
}

.date-list-row + .date-list-row { border-top: 1px solid var(--line-soft); }

/* Banding, not decoration: it is what carries the eye from a row to its
   action across the width of the strip. */
.date-list-row:nth-child(even) { background: var(--surface-tint); }

.date-list-title {
  margin: 0 0 2px;
  font-weight: 700;
  color: var(--ink-strong);
  line-height: 1.35;
}

.date-list-meta {
  margin: 0;
  color: var(--muted);
  font-size: 13px;
  line-height: 1.45;
}

.date-list-actions {
  display: flex;
  align-items: center;
  gap: 8px;
  justify-self: end;
}

.date-list-value {
  font-weight: 700;
  color: var(--ink-strong);
  white-space: nowrap;
}

/* Fixed width so every date lines up down the list — the whole point of
   leading with it. */
.date-chip {
  width: 54px;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: var(--neutral-bg);
  padding: 5px 4px 6px;
  text-align: center;
  line-height: 1.1;
}

.date-chip .dow {
  display: block;
  font-size: 10.5px;
  font-weight: 700;
  letter-spacing: 0.03em;
  text-transform: uppercase;
  color: var(--muted);
}

.date-chip .day {
  display: block;
  font-size: 19px;
  font-weight: 800;
  letter-spacing: -0.02em;
  color: var(--ink-strong);
}

.date-chip .mon {
  display: block;
  font-size: 10.5px;
  font-weight: 700;
  color: var(--muted);
}

.date-chip--overdue { background: var(--coral-bg); border-color: #f3cccc; }
.date-chip--overdue .day,
.date-chip--overdue .dow,
.date-chip--overdue .mon { color: var(--danger-ink); }

.date-chip--today { background: var(--warn-bg); border-color: rgba(178, 106, 24, 0.3); }
.date-chip--today .day,
.date-chip--today .dow,
.date-chip--today .mon { color: var(--warn); }

.date-chip--upcoming { background: var(--sky-bg); border-color: #bfe0f2; }
.date-chip--upcoming .day,
.date-chip--upcoming .dow,
.date-chip--upcoming .mon { color: var(--sky-ink); }

/* Nothing was captured. Dashed, so an absent date reads as a gap to fill
   rather than as a date of its own. */
.date-chip--none { background: transparent; border-style: dashed; }
.date-chip--none .day { color: var(--faint); font-weight: 700; }

/* Narrow — the dashboard rail. The actions STAY in their column and stack
   inside it instead, pill above Done, both right-aligned (Paul, 5 August: the
   status and the tick belong at the end of the row, not underneath it). The
   wide case — the project pipeline — keeps them on one line. */
@container (max-width: 470px) {
  .date-list-actions {
    flex-direction: column;
    align-items: flex-end;
    gap: 5px;
  }
}

/* Genuinely narrow, i.e. a phone: three columns stop fitting at all. */
@container (max-width: 330px) {
  .date-list-row {
    grid-template-columns: 54px minmax(0, 1fr);
    row-gap: 8px;
  }
  .date-list-actions { grid-column: 2; justify-self: start; align-items: flex-start; }
}

/* Done is a link, not a bordered button: one per row down a narrow strip, a
   full button each is more furniture than the rows themselves. It stays a real
   form submit — this is styling, not a link that pretends to POST. */
.btn-quiet {
  border: 0;
  background: none;
  padding: 0;
  color: var(--brand-primary);
  font-family: inherit;
  font-size: 12.5px;
  font-weight: 700;
  cursor: pointer;
}

.btn-quiet:hover { color: var(--primary-700); text-decoration: underline; }
.btn-quiet:focus-visible { outline: 2px solid var(--brand-sky); outline-offset: 2px; border-radius: 3px; }

/* The agreed scope on the governance tab — quoted rather than merely printed,
   because it is the thing that was signed off rather than a field. */
.scope-quote {
  border-left: 3px solid var(--brand-sky);
  background: var(--sky-bg);
  border-radius: 0 var(--radius) var(--radius) 0;
  padding: 12px 14px;
}

/* ==========================================================================
   The Actions screen as cards (mockup 18 / design 1b, fortieth follow-up).

   Everything below is NEW. Nothing here redefines .queue-row, .metric or any
   other existing component, so no screen outside the Actions tab moves.
   ========================================================================== */

/* KPI tiles on their side: the number in a tinted square beside its label
   rather than stacked above it. Same four tiles, same links, about half the
   vertical space — which is what the queue gets back. */
.kpi-strip {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(210px, 1fr));
  gap: 12px;
  margin-top: 14px;
}

.kpi-tile {
  display: flex;
  align-items: center;
  gap: 13px;
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  background: var(--surface);
  box-shadow: var(--shadow-card);
  padding: 13px 15px;
  text-decoration: none;
  color: inherit;
}

.kpi-tile:hover { border-color: var(--brand-sky); text-decoration: none; }

.kpi-num {
  flex: 0 0 auto;
  display: grid;
  place-items: center;
  width: 42px;
  height: 42px;
  border-radius: 10px;
  background: var(--neutral-bg);
  color: var(--ink-strong);
  font-size: 17px;
  font-weight: 800;
  font-variant-numeric: tabular-nums;
}

.kpi-num--alert { background: var(--coral-bg); color: var(--danger-ink); }
.kpi-num--info { background: var(--sky-bg); color: var(--sky-ink); }

.kpi-label {
  min-width: 0;
  display: grid;
  gap: 1px;
  font-size: 12px;
}

.kpi-label b { color: var(--muted); }
.kpi-label small { color: var(--muted); }
.kpi-label small.text-danger { color: var(--danger-ink); }

/* A queue row as a card: who it is on the left, how urgent on the right, what
   is planned and what you can do along the foot. */
.client-card {
  border: 1px solid var(--line);
  border-radius: 12px;
  background: var(--surface);
  box-shadow: var(--shadow-card);
  padding: 18px;
}

.client-card + .client-card { margin-top: 12px; }

.client-card-head {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 12px;
}

.client-card-id {
  display: flex;
  align-items: center;
  gap: 12px;
  min-width: 0;
}

/* Owner initials — decorative, which is why it is aria-hidden. The owner's
   NAME stays in the detail line: under the all-owners scope two people share
   initials sooner than anyone expects, and this is the only owner marker on
   the card. */
.avatar {
  flex: 0 0 auto;
  display: grid;
  place-items: center;
  width: 38px;
  height: 38px;
  border-radius: 999px;
  background: var(--brand-primary);
  color: #ffffff;
  font-size: 13px;
  font-weight: 700;
  letter-spacing: 0.02em;
}

.avatar--none { background: var(--brand-gray); }

/* The urgency score. QueueUrgency already computes it 0–100 and the old row
   drew it as a bar; the ring is the same number in a fifth of the width, and
   can carry the digits. Banded on the same thresholds as the row. */
.urgency-ring {
  flex: 0 0 auto;
  text-align: center;
}

.urgency-ring i {
  display: grid;
  place-items: center;
  width: 44px;
  height: 44px;
  border-radius: 999px;
  border: 3px solid var(--ok);
  color: var(--ok);
  font-size: 13px;
  font-weight: 800;
  font-style: normal;
  font-variant-numeric: tabular-nums;
}

.urgency-ring b {
  display: block;
  margin-top: 3px;
  font-size: 9.5px;
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--muted);
}

.urgency-ring--amber i { border-color: var(--warn-solid); color: var(--warn); }
.urgency-ring--red i { border-color: var(--danger-solid); color: var(--danger-ink); }

.client-card-foot {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  flex-wrap: wrap;
  margin-top: 14px;
  padding-top: 13px;
  border-top: 1px solid var(--line-soft);
}

.client-card-actions {
  display: flex;
  align-items: center;
  gap: 8px;
  flex-wrap: wrap;
}

/* Secondary actions as squares. Never the only route to anything — Open is
   also the client's name at the top of the card — and every one carries a
   title and an aria-label, because a glyph on its own teaches nobody. */
.icon-btn {
  display: grid;
  place-items: center;
  width: 34px;
  height: 34px;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: var(--surface);
  color: var(--muted);
  font-size: 14px;
  font-weight: 700;
  text-decoration: none;
}

.icon-btn:hover { border-color: var(--brand-sky); color: var(--sky-ink); text-decoration: none; }

/* The scope toggles and the filter controls as pills. Shape only — the two
   scope controls keep the different treatments that stop them reading as one
   four-way choice. The filter bar is the ONE place inputs are round; every
   other form in the platform keeps the 8px rectangle. */
.scope-choice--pill .btn { border-radius: 999px; }

.filter-bar {
  display: flex;
  align-items: center;
  gap: 10px;
  flex-wrap: wrap;
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  background: var(--surface);
  box-shadow: var(--shadow-card);
  padding: 12px 16px;
}

.filter-bar input,
.filter-bar select { border-radius: 999px; }
.filter-bar .btn { border-radius: 999px; margin-left: auto; }

/* Four labels and four counts inside a ~410px rail. The standard .tab-bar
   overflows there and offers a scrollbar, which is a worse answer than
   smaller type: nobody scrolls a four-item control. */
.tab-bar--compact { margin-bottom: 14px; }
.tab-bar--compact .tab-btn { padding: 6px 10px; font-size: 12.5px; }
.tab-bar--compact .tab-badge {
  margin-left: 5px;
  min-width: 16px;
  height: 16px;
  font-size: 10.5px;
}

/* The scope row: page tabs on the left, both toggles on the right. */
.actions-bar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: 14px;
}

@media (max-width: 720px) {
  .client-card-head { flex-wrap: wrap; }
  .client-card-foot { align-items: flex-start; }
}

/* The snooze control is an icon now, so its <summary> has to stop drawing a
   disclosure triangle and start looking like the button beside it. */
.client-card-actions .snooze-details > summary { list-style: none; cursor: pointer; }
.client-card-actions .snooze-details > summary::-webkit-details-marker { display: none; }
.client-card-actions .snooze-details > summary::marker { content: ""; }

/* Open, the date form is a small popover under its icon rather than a row that
   reflows the card — the button you just clicked must not move. */
.client-card-actions .snooze-details { position: relative; }
.client-card-actions .snooze-details[open] > form {
  position: absolute;
  right: 0;
  top: calc(100% + 6px);
  z-index: 5;
  width: max-content;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: var(--surface);
  box-shadow: var(--shadow);
  padding: 10px;
}

/* A glyph quoted in the help page's own prose, drawn at the size it appears on
   the card so the page and the screen are recognisably the same mark. */
.help-glyph {
  display: inline-grid;
  place-items: center;
  width: 24px;
  height: 24px;
  margin-right: 6px;
  border: 1px solid var(--line);
  border-radius: 6px;
  background: var(--surface);
  color: var(--muted);
  font-size: 13px;
  vertical-align: -6px;
}

/* A count of nothing is not an alarm. The badge keeps its shape so the tabs
   stay aligned, and loses the red. */
.tab-badge--zero { background: var(--brand-gray); }
.tab-btn.active .tab-badge--zero { background: #ffffff; color: var(--muted); }

/* A person who cannot be put on a meeting invite because no email address is
   recorded (forty-first follow-up). Still tickable — being in the room and
   being invited are different facts — so this says why, rather than removing
   them from the list and leaving somebody to wonder where they went. */
.assignee-option.is-unavailable {
  border-style: dashed;
  color: var(--faint);
}
