/* ═══════════════════════════════════════════════════════════════════════
   INVENTORY MODULE — Cards, chips, runner, forms
   ─────────────────────────────────────────────────────────────────────
   Built to read identically to checklists/callouts/assets — same chip
   patterns, same card shape, same KPI cards, same modal shell. The
   only new primitives are: .inv-chip (lighter-weight chip than the
   list-card refs), .inv-chip-picker (multi-select chip rows), the
   per-location row in the item form, and the count-runner table.
   ═════════════════════════════════════════════════════════════════════ */

/* Card — clones .callout-card shape */
.inv-card {
  display: flex;
  align-items: stretch;
  background: var(--bg-surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  padding: 12px;
  cursor: pointer;
  transition: border-color 0.12s, box-shadow 0.12s, transform 0.06s;
  text-align: left;
  font-family: inherit;
  color: inherit;
  width: 100%;
}
.inv-card:hover { border-color: color-mix(in srgb, var(--accent) 35%, var(--border)); }
.inv-card:active { transform: scale(0.998); }
.inv-card-main {
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 6px;
}
.inv-card-top {
  display: flex;
  align-items: center;
  gap: 6px;
  flex-wrap: wrap;
}
.inv-card-title {
  font-size: var(--text-lg);
  font-weight: 600;
  color: var(--text-primary);
  line-height: 1.3;
  word-wrap: break-word;
}
.inv-card-sub {
  font-size: var(--text-sm);
  color: var(--text-muted);
  line-height: 1.4;
}
.inv-card-meta {
  display: flex;
  flex-wrap: wrap;
  gap: 10px;
  font-size: var(--text-xs);
  color: var(--text-muted);
}
.inv-card-meta-item {
  display: inline-flex;
  align-items: center;
  gap: 4px;
}
.inv-card-chip-row {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  align-items: center;
}

/* Generic chip used in inventory cards (different palette from
   .callout-card-ref — that's still used for ref IDs) */
.inv-chip {
  display: inline-flex;
  align-items: center;
  padding: 4px 9px;
  border: 1px solid transparent;
  border-radius: var(--radius-sm);
  font: 600 var(--text-xs) / 1.2 var(--body);
  letter-spacing: 0.01em;
  white-space: nowrap;
}
.inv-chip.is-more {
  background: var(--bg-elevated);
  color: var(--text-muted);
  border-color: var(--border);
}
/* Stock status — CALM (2026-06-29): neutral chip + colour on the dot. */
.inv-chip-status {
  gap: 5px;
  background: var(--bg-elevated);
  border-color: var(--border);
  color: var(--text-primary);
  font-weight: 500;
}
.inv-status-dot { width: 6px; height: 6px; border-radius: var(--radius-full); flex-shrink: 0; }
/* Type tag chip on a list card — neutral, so it's distinct from the
   colour-coded "List" chip and the orange "to order" chip. */
.inv-chip--type {
  background: var(--bg-elevated);
  color: var(--text-secondary);
  border-color: var(--border-strong);
}

/* ─── Supplier groups on the Lists tab ─────────────────────────────────
   Lists are grouped under a collapsible header per supplier so the pile
   stays organised and ordering stays per-supplier. */
.inv-supplier-groups {
  display: flex;
  flex-direction: column;
  gap: 16px;
}
.inv-supplier-group-head {
  display: flex;
  align-items: center;
  gap: 8px;
  width: 100%;
  padding: 6px 2px 8px;
  background: none;
  border: none;
  border-bottom: 1px solid var(--border);
  font-family: inherit;
  color: var(--text-primary);
  cursor: pointer;
  text-align: left;
  -webkit-tap-highlight-color: transparent;
}
.inv-group-chev {
  width: 16px; height: 16px;
  flex-shrink: 0;
  color: var(--text-muted);
  transition: transform 0.15s;
}
.inv-supplier-group.is-collapsed .inv-group-chev { transform: rotate(-90deg); }
.inv-supplier-group-name {
  font: 700 14px / 1.2 var(--display, var(--body));
  letter-spacing: -0.01em;
}
.inv-supplier-group-count {
  font: 500 var(--text-xs) / 1 var(--body);
  color: var(--text-muted);
}
.inv-supplier-group-order {
  margin-left: auto;
  padding: 2px 9px;
  border-radius: 999px;
  background: rgba(234,88,12,0.10);
  color: #ea580c;
  border: 1px solid rgba(234,88,12,0.35);
  font: 700 var(--text-xs) / 1.4 var(--body);
  white-space: nowrap;
}
.inv-supplier-group-body { padding-top: 12px; }
.inv-supplier-group.is-collapsed .inv-supplier-group-body { display: none; }
/* Lists stack full-width, one per row — the multi-column grid felt cluttered
   inside the supplier groups. (Scoped here so other cl-card-grid uses keep
   their multi-column layout.) */
.inv-supplier-group-body .cl-card-grid { grid-template-columns: 1fr; }

/* Two-tier card: the shared .card-loc-chip (css/04) leads the top-left; the
   classifier chips (category + dept) pin to the right and stay NEUTRAL — the
   colour lives only on the category dot / the dept icon. */
.inv-card-top .card-loc-chip { margin-right: auto; }
.inv-chip-cat,
.inv-chip-dept {
  gap: 5px;
  background: var(--bg-elevated);
  color: var(--text-secondary);
  border-color: var(--border);
}
.inv-cat-dot { width: 7px; height: 7px; border-radius: var(--radius-full); flex-shrink: 0; }
.inv-dept-ico { display: inline-grid; place-items: center; flex-shrink: 0; }
.inv-dept-ico svg { width: 13px; height: 13px; }

/* Counter chip on stock card — shows "5 / par 8 bottles" */
.inv-card-counter {
  display: inline-flex;
  align-items: center;
  padding: 3px 8px;
  background: var(--c-soft, var(--bg-elevated));
  border: 1px solid color-mix(in srgb, var(--c-color, var(--border)) 30%, transparent);
  border-radius: var(--radius-sm);
  color: var(--c-color, var(--text-primary));
  font: 600 var(--text-xs) / 1.2 var(--body);
  letter-spacing: 0.01em;
  white-space: nowrap;
}

/* Two-level tab bar (2026-07-17) — the SECONDARY "Stock register" sections row
   (Stock/Locations/Suppliers/Movements/Counts), shown only when inside the
   register. Reuses the .callout-view-toggle pill (same idiom as every tab bar)
   and therefore keeps its canonical solid-accent active state, matching
   Projects. This local rule only makes the secondary row smaller + more tightly
   grouped below the primary Lists | Stock register bar. */
/* .asset-list-wrap is a 12px-gap flex column, so a NEGATIVE top margin pulls
   the sub-bar up to ~8px under its primary "Stock register" tab — tighter than
   the 12px to the content below, so the two read as one grouped unit. */
.inv-subtab-fade {
  display: block;
  align-self: stretch;
  width: 100%;
  margin-top: -4px;
}
.inv-subtab-fade > .inv-subtab-bar { width: 100%; }

/* Tab toolbar — used in item/locations/suppliers/counts tabs */
.inv-tab-toolbar {
  display: flex;
  align-items: center;
  gap: 8px;
  margin: 8px 0;
}
.inv-tab-cta {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  /* Read on ONE line (like the "New project" app-bar button) and keep a
     consistent icon size — the inline + SVGs ship without width/height, so
     without this they render oversized and the label wraps. */
  white-space: nowrap;
  flex: none;
}
.inv-tab-cta svg { flex: none; width: 18px; height: 18px; }

/* Movement-list quick actions row */
.inv-mov-actions { flex-wrap: wrap; }

/* Section heading inside a tab body */
.inv-section-head {
  font: 600 var(--text-xs) / 1 var(--body);
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--text-muted);
  margin: 16px 0 8px;
}

/* ─── Chip picker (used everywhere multi/single chips need toggling) ── */
.inv-chip-picker {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
}
.inv-chip-pick {
  display: inline-flex;
  align-items: center;
  padding: 5px 11px;
  background: var(--bg-elevated);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  color: var(--text-secondary);
  font: 600 var(--text-xs) / 1 var(--body);
  letter-spacing: 0.01em;
  cursor: pointer;
  transition: background 0.12s, border-color 0.12s, color 0.12s, transform 0.06s;
  font-family: inherit;
}
.inv-chip-pick:hover { border-color: var(--text-muted); }
.inv-chip-pick:active { transform: scale(0.96); }
.inv-chip-pick.is-selected {
  background: var(--chip-soft, var(--accent-soft));
  border-color: color-mix(in srgb, var(--chip-color, var(--accent)) 45%, transparent);
  color: var(--chip-color, var(--accent));
}

/* ─── Supplier / location / item pick button (the trigger) ───────── */
.inv-supplier-pick-btn {
  display: inline-flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
  padding: 9px 12px;
  background: var(--bg-elevated);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  color: var(--text-primary);
  font: 500 var(--text-base) / 1.2 var(--body);
  cursor: pointer;
  width: 100%;
  font-family: inherit;
  text-align: left;
  transition: border-color 0.12s, background 0.12s;
}
.inv-supplier-pick-btn:hover { border-color: var(--text-muted); }
.inv-supplier-pick-btn .is-placeholder { color: var(--text-muted); }

/* Picker row (used inside the picker overlays) */
.inv-picker-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
  padding: 10px 12px;
  background: var(--bg-surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  color: var(--text-primary);
  cursor: pointer;
  font-family: inherit;
  text-align: left;
  transition: background 0.12s, border-color 0.12s;
}
.inv-picker-row:hover {
  background: color-mix(in srgb, var(--accent) 6%, var(--bg-surface));
  border-color: color-mix(in srgb, var(--accent) 30%, var(--border));
}
.inv-picker-row-name { font-weight: 600; font-size: var(--text-base); }
.inv-picker-row-meta { font-size: var(--text-xs); color: var(--text-muted); }

/* ─── Item form: per-location rows ───────────────────────────────── */
.inv-loc-rows {
  display: flex;
  flex-direction: column;
  gap: 6px;
  margin-bottom: 8px;
}
.inv-loc-empty {
  padding: 12px;
  background: var(--bg-elevated);
  border: 1px dashed var(--border);
  border-radius: var(--radius-md);
  color: var(--text-muted);
  text-align: center;
  font-size: var(--text-sm);
}
.inv-loc-row {
  display: grid;
  grid-template-columns: 1fr 1fr 32px;
  gap: 6px;
  align-items: stretch;
  padding: 8px;
  background: var(--bg-elevated);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
}
@media (max-width: 640px) {
  .inv-loc-row { grid-template-columns: 1fr 32px; }
  .inv-loc-row-fields { grid-column: 1 / -1; }
}
.inv-loc-row-pick {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  justify-content: center;
  gap: 2px;
  padding: 6px 9px;
  background: var(--bg-surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  color: var(--text-primary);
  cursor: pointer;
  font-family: inherit;
  text-align: left;
  min-width: 0;
  transition: border-color 0.12s;
}
.inv-loc-row-pick:hover { border-color: color-mix(in srgb, var(--accent) 30%, var(--border)); }
.inv-loc-row-pick > span:last-child {
  font: 600 var(--text-sm) / 1.2 var(--body);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  width: 100%;
}
.inv-loc-row-eyebrow {
  font: 600 10px / 1 var(--body);
  text-transform: uppercase;
  letter-spacing: 0.05em;
  color: var(--text-muted);
}
.inv-loc-row-fields {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 6px;
}
.inv-loc-row-field {
  display: flex;
  flex-direction: column;
  gap: 2px;
}
.inv-loc-row-field input {
  padding: 6px 8px;
  background: var(--bg-surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  font: 500 var(--text-sm) / 1 var(--body);
  color: var(--text-primary);
}
.inv-loc-row-field input:focus {
  outline: none;
  border-color: var(--accent);
  box-shadow: 0 0 0 2px var(--accent-soft);
}
.inv-loc-row-del {
  display: grid;
  place-items: center;
  width: 32px;
  height: 100%;
  background: transparent;
  border: 0;
  border-radius: var(--radius-sm);
  color: var(--danger);
  cursor: pointer;
}
.inv-loc-row-del:hover { background: var(--danger-soft); }
/* Read-only current-count on EDIT (Part 4) — an existing location's stock is
   movement-owned, so it shows as a static value, not an editable input. Box-
   matches the sibling par inputs; grid stretch keeps the field heights aligned. */
.inv-loc-row-field .inv-loc-row-locked {
  display: flex;
  align-items: center;
  flex: 1;                 /* fill the stretched grid cell → aligns with the par inputs */
  min-height: 30px;
  padding: 6px 8px;
  box-sizing: border-box;
  background: var(--bg-elevated);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  font: 600 var(--text-sm) / 1 var(--body);
  color: var(--text-secondary);
  cursor: default;
}
.inv-loc-note {
  margin-top: 8px;
  font: 500 var(--text-xs) / 1.45 var(--body);
  color: var(--text-muted);
}
.inv-loc-note strong { color: var(--text-secondary); font-weight: 600; }
/* Match the sibling par <input>s, which the global rule bumps to --text-xl on
   ≤1024px (css/01 iOS-zoom rule). Longhand font-size AFTER the base rule so the
   media override wins on source order (§4). */
@media (max-width: 1024px) {
  .inv-loc-row-field .inv-loc-row-locked { font-size: var(--text-xl); }
}

.inv-loc-add-btn {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  padding: 5px 11px;
  background: transparent;
  border: 1px dashed var(--accent);
  border-radius: var(--radius-sm);
  color: var(--accent);
  font: 600 var(--text-xs) / 1 var(--body);
  cursor: pointer;
  font-family: inherit;
}
.inv-loc-add-btn:hover { background: var(--accent-soft); }

/* ─── Count runner ─────────────────────────────────────────────── */
.inv-runner-items {
  display: flex;
  flex-direction: column;
  gap: 8px;
  padding: 12px;
}
.inv-runner-row {
  background: var(--bg-surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  padding: 10px;
  display: flex;
  flex-direction: column;
  gap: 8px;
}
.inv-runner-row-head {
  display: flex;
  align-items: center;
  gap: 10px;
}
.inv-runner-row-num {
  width: 26px;
  height: 26px;
  display: grid;
  place-items: center;
  background: var(--bg-elevated);
  border-radius: 50%;
  font: 700 var(--text-sm) / 1 var(--body);
  color: var(--text-muted);
  flex-shrink: 0;
}
.inv-runner-row-name {
  font: 600 var(--text-base) / 1.3 var(--body);
  color: var(--text-primary);
  flex: 1;
  min-width: 0;
  word-wrap: break-word;
}
.inv-runner-row-ref {
  margin-left: 8px;
  font: 500 var(--text-xs) / 1 var(--font-mono, monospace);
  color: var(--text-muted);
}
.inv-runner-row-body {
  display: grid;
  grid-template-columns: 1fr 1fr 1fr;
  gap: 6px;
}
.inv-runner-row-field {
  display: flex;
  flex-direction: column;
  gap: 2px;
}
.inv-runner-row-field input {
  padding: 8px 10px;
  background: var(--bg-elevated);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  font: 600 var(--text-base) / 1 var(--body);
  color: var(--text-primary);
  text-align: center;
}
.inv-runner-row-field input:focus {
  outline: none;
  border-color: var(--accent);
  box-shadow: 0 0 0 2px var(--accent-soft);
}
.inv-runner-row-value {
  padding: 8px 10px;
  background: var(--bg-elevated);
  border-radius: var(--radius-sm);
  font: 600 var(--text-base) / 1 var(--body);
  color: var(--text-primary);
  text-align: center;
}
.inv-runner-row-eyebrow {
  font: 600 10px / 1 var(--body);
  text-transform: uppercase;
  letter-spacing: 0.05em;
  color: var(--text-muted);
  text-align: center;
}
.inv-runner-var {
  padding: 8px 10px;
  background: var(--bg-elevated);
  border-radius: var(--radius-sm);
  font: 700 var(--text-base) / 1 var(--body);
  text-align: center;
  color: var(--text-muted);
}
.inv-runner-var.is-ok    { background: rgba(22,163,74,0.12);  color: #16a34a; }
.inv-runner-var.is-up    { background: rgba(59,130,246,0.12); color: #3b82f6; }
.inv-runner-var.is-down  { background: rgba(220,38,38,0.12);  color: #dc2626; }

/* Count runner — live progress + Next-uncounted + quiet save state (Prompt 10) */
.inv-runner-meta {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  margin-top: 10px;
  flex-wrap: wrap;
}
.inv-runner-progress {
  font: 700 var(--text-sm) / 1 var(--body);
  color: var(--text-secondary);
  background: var(--bg-elevated);
  border: 1px solid var(--border);
  border-radius: 999px;
  padding: 5px 11px;
}
.inv-runner-next {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 6px 12px;
  background: var(--bg-elevated);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  color: var(--text-primary);
  font: 600 var(--text-sm) / 1 var(--body);
  font-family: inherit;
  cursor: pointer;
}
.inv-runner-next svg { width: 15px; height: 15px; flex-shrink: 0; }
.inv-runner-next:hover { border-color: var(--accent); color: var(--accent); }
.inv-runner-next:disabled { opacity: 0.45; cursor: default; }
.inv-runner-next:focus-visible { outline: none; border-color: var(--accent); box-shadow: 0 0 0 2px var(--accent-soft); }

.cl-runner-footer-status {
  min-height: 16px;
  display: flex;
  align-items: center;
}
.inv-runner-savestate {
  font: 600 var(--text-sm) / 1 var(--body);
  color: var(--text-muted);
  transition: opacity 0.2s ease;
}
.inv-runner-savestate.is-saved { color: var(--rag-pass-fill); }
.inv-runner-savestate.is-error { color: var(--rag-fail-fill); }

/* ─── Movement list timeline (already shares .callout-event-list) ── */
.inv-movement-list { padding-top: 8px; }
.inv-movement-row .callout-event-sub { margin-top: 2px; }

/* ─── Inventory v1.1 additions: table, specs editor, categories admin ─── */

/* In-form "Manage categories" link */
.inv-form-link {
  margin-left: 8px;
  background: transparent;
  border: 0;
  color: var(--accent);
  font: 600 var(--text-xs) / 1 var(--body);
  cursor: pointer;
  padding: 0;
}
.inv-form-link:hover { text-decoration: underline; }

/* ─── Spec rows (free-form Label : Value) ────────────────────── */
.inv-specs-rows {
  display: flex;
  flex-direction: column;
  gap: 6px;
  margin-bottom: 8px;
}
.inv-spec-row {
  display: grid;
  grid-template-columns: 1fr 1fr 32px;
  gap: 6px;
  align-items: center;
}
.inv-spec-row input {
  padding: 8px 10px;
  background: var(--bg-surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  font: 500 var(--text-sm) / 1.2 var(--body);
  color: var(--text-primary);
  min-width: 0;
}
.inv-spec-row input:focus {
  outline: none;
  border-color: var(--accent);
  box-shadow: 0 0 0 2px var(--accent-soft);
}

/* ─── Table (desktop default for Stock + Items tabs) ─────────── */
.inv-table-wrap {
  margin-top: 8px;
  background: var(--bg-surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  overflow: hidden;
}
.inv-table {
  width: 100%;
  border-collapse: collapse;
  font-family: inherit;
}
.inv-table thead th {
  position: sticky;
  top: 0;
  z-index: 1;
  background: var(--bg-elevated);
  border-bottom: 1px solid var(--border);
  padding: 9px 12px;
  text-align: left;
  font: 600 var(--text-xs) / 1 var(--body);
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--text-muted);
  cursor: pointer;
  user-select: none;
  white-space: nowrap;
}
.inv-table thead th:hover { color: var(--text-primary); background: color-mix(in srgb, var(--accent) 4%, var(--bg-elevated)); }
.inv-table thead th svg { vertical-align: middle; margin-left: 2px; }
.inv-table tbody tr {
  border-bottom: 1px solid var(--border);
  cursor: pointer;
  transition: background 0.08s ease;
}
.inv-table tbody tr:last-child { border-bottom: 0; }
.inv-table tbody tr:hover { background: color-mix(in srgb, var(--accent) 5%, transparent); }
.inv-table tbody tr:nth-child(even) { background: color-mix(in srgb, var(--text-muted) 3%, transparent); }
.inv-table tbody tr:nth-child(even):hover { background: color-mix(in srgb, var(--accent) 6%, transparent); }
.inv-table td {
  padding: 10px 12px;
  font: 500 var(--text-sm) / 1.3 var(--body);
  color: var(--text-primary);
  vertical-align: middle;
}
.inv-table-ref {
  font-family: var(--font-mono, ui-monospace, SFMono-Regular, monospace);
  font-size: var(--text-xs);
  font-weight: 600;
  color: var(--text-secondary);
  background: var(--bg-elevated);
  border: 1px solid var(--border);
  padding: 2px 6px;
  border-radius: var(--radius-sm);
  letter-spacing: 0.04em;
  white-space: nowrap;
}
.inv-table-name {
  font-weight: 600;
  color: var(--text-primary);
}
.inv-table-sub {
  font-size: var(--text-xs);
  color: var(--text-muted);
  margin-top: 1px;
  font-weight: 500;
}
.inv-table-muted { color: var(--text-secondary); }
.inv-table-counter {
  display: inline-flex;
  align-items: baseline;
  gap: 4px;
  font: 700 var(--text-sm) / 1 var(--body);
  color: var(--c-color, var(--text-primary));
}
.inv-table-unit {
  font-size: var(--text-xs);
  font-weight: 500;
  color: var(--text-muted);
}

/* ─── Categories admin modal ─────────────────────────────────── */
.inv-category-list {
  display: flex;
  flex-direction: column;
  gap: 6px;
  margin-bottom: 8px;
}
.inv-category-row {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 8px 10px;
  background: var(--bg-elevated);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
}
.inv-category-swatch {
  width: 28px;
  height: 28px;
  border-radius: var(--radius-sm);
  border: 1.5px solid var(--border);
  cursor: pointer;
  padding: 0;
  flex-shrink: 0;
  transition: transform 0.06s ease, box-shadow 0.12s ease;
}
.inv-category-swatch:hover { transform: scale(1.05); box-shadow: 0 0 0 2px var(--accent-soft); }
.inv-category-info {
  flex: 1;
  min-width: 0;
}
.inv-category-label {
  font: 600 var(--text-sm) / 1.2 var(--body);
  color: var(--text-primary);
}
.inv-category-meta {
  font: 500 var(--text-xs) / 1 var(--body);
  color: var(--text-muted);
  text-transform: uppercase;
  letter-spacing: 0.04em;
  margin-top: 2px;
}
.inv-category-action {
  display: grid;
  place-items: center;
  width: 30px;
  height: 30px;
  background: transparent;
  border: 0;
  border-radius: var(--radius-sm);
  color: var(--text-muted);
  cursor: pointer;
  transition: background 0.12s ease, color 0.12s ease;
}
.inv-category-action:hover { background: var(--bg-surface); color: var(--text-primary); }
.inv-category-action.is-danger { color: var(--danger); }
.inv-category-action.is-danger:hover { background: var(--danger-soft); }

/* Colour swatches popover (inside categories admin) */
.inv-colour-popover {
  position: absolute;
  z-index: 9999;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  background: var(--bg-surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  box-shadow: 0 12px 32px rgba(0,0,0,0.18), 0 2px 6px rgba(0,0,0,0.08);
  padding: 14px;
  width: 280px;
}
.inv-colour-popover-head {
  font: 600 var(--text-sm) / 1.2 var(--body);
  color: var(--text-primary);
  margin-bottom: 10px;
}
.inv-colour-swatches {
  display: grid;
  grid-template-columns: repeat(8, 1fr);
  gap: 6px;
  margin-bottom: 12px;
}
.inv-colour-swatch {
  width: 100%;
  aspect-ratio: 1 / 1;
  border-radius: var(--radius-sm);
  border: 1.5px solid transparent;
  cursor: pointer;
  padding: 0;
  transition: transform 0.06s ease, box-shadow 0.12s ease;
}
.inv-colour-swatch:hover { transform: scale(1.10); }
.inv-colour-swatch.is-selected {
  border-color: var(--text-primary);
  box-shadow: 0 0 0 2px var(--bg-surface), 0 0 0 4px var(--accent);
}
.inv-colour-custom {
  display: flex;
  gap: 6px;
  align-items: center;
}
.inv-colour-custom input {
  flex: 1;
  padding: 8px 10px;
  background: var(--bg-elevated);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  font-family: var(--font-mono, monospace);
  font-size: var(--text-sm);
}
.inv-category-edit-pop .inv-category-edit-fields {
  display: flex;
  flex-direction: column;
  gap: 10px;
}
.inv-category-edit-fields > input[type="text"] {
  padding: 9px 12px;
  background: var(--bg-elevated);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  font: 500 var(--text-base) / 1.2 var(--body);
  color: var(--text-primary);
}
.inv-colour-custom-row {
  display: flex;
  gap: 6px;
  justify-content: flex-end;
}

/* ═══════════════════════════════════════════════════════════════════════
   INVENTORY LISTS (V2 model) — Editable table + info modal + order
   ─────────────────────────────────────────────────────────────────────
   Each list = its own spreadsheet. Built-in columns: # | Description |
   [custom] | Needed | Current | To order | Info. Inline editable on
   desktop; falls back to expandable cards on mobile (<768px).
   ═════════════════════════════════════════════════════════════════════ */

/* Colour stripe on list cards (re-use from V1 .callout-card-priority-bar pattern) */
.inv-card-bar {
  position: absolute;
  top: 0; left: 0;
  width: 3px; height: 100%;
  border-radius: var(--radius-md) 0 0 var(--radius-md);
}
.inv-card { position: relative; }

/* ─── Columns editor (in lists form) ─────────────────────────── */
.inv-list-cols-editor {
  display: flex;
  flex-direction: column;
  gap: 6px;
  margin-bottom: 8px;
}
.inv-list-col-row {
  display: grid;
  grid-template-columns: 1fr 130px auto;
  gap: 6px;
  align-items: center;
  padding: 6px 8px;
  background: var(--bg-elevated);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
}
.inv-list-col-row input,
.inv-list-col-row select {
  padding: 6px 9px;
  background: var(--bg-surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  font: 500 var(--text-sm) / 1.2 var(--body);
  color: var(--text-primary);
  min-width: 0;
}
.inv-list-col-row input:focus,
.inv-list-col-row select:focus {
  outline: none;
  border-color: var(--accent);
  box-shadow: 0 0 0 2px var(--accent-soft);
}
.inv-list-col-actions { display: inline-flex; gap: 2px; }
.inv-list-col-move {
  display: grid; place-items: center;
  width: 28px; height: 28px;
  background: transparent;
  border: 0;
  border-radius: var(--radius-sm);
  color: var(--text-muted);
  cursor: pointer;
}
.inv-list-col-move:hover:not(:disabled) { background: var(--bg-surface); color: var(--text-primary); }
.inv-list-col-move:disabled { opacity: 0.3; cursor: default; }

/* ─── Table overlay shell ────────────────────────────────────── */
.inv-list-table-overlay {
  position: fixed;
  inset: 0;
  z-index: 1300;
  background: var(--bg-base);
  opacity: 0;
  transition: opacity 0.2s ease;
  overflow: auto;
}
.inv-list-table-overlay.is-open { opacity: 1; }
.inv-list-table-shell {
  display: flex;
  flex-direction: column;
  min-height: 100%;
}

/* Header */
.inv-list-table-head {
  position: sticky;
  top: 0;
  z-index: 10;
  background: var(--bg-base);
  border-bottom: 1px solid var(--border);
  /* This overlay is fullscreen (inset:0) so on an iPhone its top sits behind
     the status-bar clock / notch. Pad the sticky header down by the safe-area
     inset (longhand — Safari drops a `padding` shorthand when env()/max() is
     in the first slot) so the title, edit + close buttons stay tappable.
     env() resolves to 0 on desktop, where max() keeps the original 14px. */
  padding-top: max(14px, calc(env(safe-area-inset-top, 0px) + 12px));
  padding-right: 16px;
  padding-bottom: 14px;
  padding-left: 16px;
  display: flex;
  flex-direction: column;
  gap: 8px;
}
.inv-list-table-head-top {
  display: flex;
  align-items: center;
  gap: 10px;
  flex-wrap: wrap;
}
.inv-list-table-title {
  margin: 0;
  font: 600 var(--text-2xl) / 1.2 var(--display, var(--body));
  color: var(--text-primary);
  flex: 1;
  min-width: 0;
}
.inv-list-table-head-actions {
  display: inline-flex;
  gap: 4px;
  margin-left: auto;
}
.inv-list-table-desc {
  font: 500 var(--text-sm) / 1.4 var(--body);
  color: var(--text-muted);
}
.inv-list-table-head-meta {
  display: flex;
  align-items: center;
  gap: 12px;
  flex-wrap: wrap;
  font: 500 var(--text-sm) / 1 var(--body);
  color: var(--text-muted);
}
.inv-list-table-head-meta strong { color: var(--text-primary); font-weight: 600; }
.inv-list-order-btn {
  margin-left: auto;
  display: inline-flex;
  align-items: center;
  gap: 6px;
}

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

/* Body */
.inv-list-table-body { padding: 14px 16px; }

/* ─── Editable table ─────────────────────────────────────────── */
.inv-list-table-wrap {
  background: var(--bg-surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  overflow-x: auto;
}
.inv-list-editable-table {
  width: 100%;
  border-collapse: collapse;
  font-family: inherit;
  /* Fixed layout so <colgroup> widths are authoritative — required for
     drag-to-resize columns to track the cursor exactly. */
  table-layout: fixed;
}
.inv-list-editable-table thead th {
  background: var(--bg-elevated);
  border-bottom: 1px solid var(--border);
  padding: 9px 10px;
  text-align: left;
  font: 600 var(--text-xs) / 1 var(--body);
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--text-muted);
  white-space: nowrap;
  position: relative;   /* anchors the resize handle */
  overflow: hidden;     /* clip header label when a column is dragged narrow */
}
.inv-list-editable-table tbody tr {
  border-bottom: 1px solid var(--border);
}
.inv-list-editable-table tbody tr:last-child { border-bottom: 0; }
.inv-list-editable-table tbody tr:hover { background: color-mix(in srgb, var(--accent) 3%, transparent); }
.inv-list-editable-table td {
  padding: 4px 6px;
  font: 500 var(--text-sm) / 1.3 var(--body);
  color: var(--text-primary);
  vertical-align: middle;
}
.inv-list-table-col-id {
  font-family: var(--font-mono, ui-monospace, SFMono-Regular, monospace);
  font-size: var(--text-xs);
  font-weight: 600;
  color: var(--text-muted);
  white-space: nowrap;
  width: 56px;
  padding-left: 12px !important;
}
.inv-list-table-col-num   { width: 92px; }
.inv-list-table-col-info  { width: 64px; text-align: center; }
.inv-list-table-col-del   { width: 40px; text-align: center; padding-right: 8px !important; }

/* ─── Drag-to-resize columns ───────────────────────────────────────────
   A thin grab strip on each header's right edge. The actual width lives on
   the <colgroup> <col> (set live while dragging, saved to localStorage). */
.inv-col-resize {
  position: absolute;
  top: 0;
  right: 0;
  width: 9px;
  height: 100%;
  cursor: col-resize;
  z-index: 3;
  touch-action: none;
  user-select: none;
}
.inv-col-resize::after {
  content: '';
  position: absolute;
  top: 18%;
  bottom: 18%;
  right: 3px;
  width: 2px;
  border-radius: 1px;
  background: transparent;
  transition: background 0.12s;
}
.inv-list-editable-table thead th:hover .inv-col-resize::after,
.inv-col-resize:hover::after {
  background: color-mix(in srgb, var(--accent) 55%, transparent);
}
/* Trailing flexible column soaks up slack so resized columns render at the
   exact px the user dragged (and the table still fills the panel width). */
.inv-col-filler { width: auto; padding: 0 !important; }
/* While dragging: force the resize cursor everywhere + suppress selection. */
body.inv-col-resizing, body.inv-col-resizing * {
  cursor: col-resize !important;
  user-select: none !important;
}

/* Cell inputs — they ARE the cell. No borders unless focused. */
.inv-list-cell {
  width: 100%;
  padding: 7px 8px;
  background: transparent;
  border: 1px solid transparent;
  border-radius: var(--radius-sm);
  font: 500 var(--text-sm) / 1.2 var(--body);
  color: var(--text-primary);
  min-width: 0;
}
.inv-list-cell:hover { background: var(--bg-elevated); }
.inv-list-cell:focus {
  outline: none;
  background: var(--bg-surface);
  border-color: var(--accent);
  box-shadow: 0 0 0 2px var(--accent-soft);
}
.inv-list-cell-num { text-align: right; font-variant-numeric: tabular-nums; }
/* Kill the up/down spinner arrows on number cells — they crowd the column
   and nobody uses them for stock counts (same treatment as the cost widget). */
.inv-list-cell[type="number"] {
  -moz-appearance: textfield;
  appearance: textfield;
}
.inv-list-cell::-webkit-outer-spin-button,
.inv-list-cell::-webkit-inner-spin-button {
  -webkit-appearance: none;
  margin: 0;
}
.inv-list-cell-current {
  background: color-mix(in srgb, var(--accent) 5%, transparent);
  font-weight: 600;
}
.inv-list-cell-order {
  text-align: right;
  padding: 7px 10px;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
  color: var(--text-muted);
  border-radius: var(--radius-sm);
}
.inv-list-cell-order.is-short {
  background: rgba(220,38,38,0.10);
  color: #dc2626;
}
.inv-list-cell-order.is-ok {
  background: rgba(22,163,74,0.10);
  color: #16a34a;
}

/* Info button */
.inv-list-info-btn {
  position: relative;
  display: grid;
  place-items: center;
  width: 32px; height: 32px;
  background: transparent;
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  color: var(--text-muted);
  cursor: pointer;
  margin: 0 auto;
  transition: background 0.12s, color 0.12s, border-color 0.12s;
}
.inv-list-info-btn:hover {
  background: var(--accent-soft);
  color: var(--accent);
  border-color: color-mix(in srgb, var(--accent) 40%, transparent);
}
.inv-list-info-btn.has-info {
  background: var(--accent-soft);
  color: var(--accent);
  border-color: color-mix(in srgb, var(--accent) 40%, transparent);
}
.inv-list-info-badge {
  position: absolute;
  top: -5px; right: -5px;
  min-width: 14px; height: 14px;
  padding: 0 3px;
  background: var(--accent);
  color: white;
  border-radius: 7px;
  font: 700 9px / 14px var(--body);
  text-align: center;
}

/* Footer (Add row) */
.inv-list-table-foot {
  padding: 12px 16px 20px;
  border-top: 1px solid var(--border);
  background: var(--bg-base);
  position: sticky;
  bottom: 0;
}
.inv-list-add-row-btn {
  display: inline-flex;
  align-items: center;
  gap: 6px;
}

/* ─── Mobile fallback cards (when <768px) ────────────────────── */
.inv-list-row-card {
  background: var(--bg-surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  padding: 14px;
  margin-bottom: 10px;
  display: flex;
  flex-direction: column;
  gap: 12px;
  box-shadow: var(--shadow-card);
}
.inv-list-row-card-head {
  display: flex;
  align-items: center;
  gap: 8px;
}
.inv-list-row-card-id {
  font: 700 11px / 1 var(--body);
  color: var(--text-muted);
  background: var(--bg-elevated);
  border: 1px solid var(--border);
  border-radius: 999px;
  padding: 4px 9px;
  flex-shrink: 0;
}
.inv-list-row-card-desc {
  flex: 1;
  font-weight: 700;
  font-size: var(--text-base);
}
.inv-list-row-card-cells {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(130px, 1fr));
  gap: 10px;
}
.inv-list-row-card-cell {
  display: flex;
  flex-direction: column;
  gap: 5px;
  min-width: 0;
}
.inv-list-row-card-eyebrow {
  font: 700 10px / 1 var(--body);
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--text-muted);
}

/* Every editable field on the card gets a real "tappable field" look — no
   hover on mobile, so they need a visible affordance at rest. */
.inv-list-row-card .inv-list-cell {
  background: var(--bg-elevated);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  padding: 9px 10px;
}
.inv-list-row-card .inv-list-cell:focus {
  background: var(--bg-surface);
  border-color: var(--accent);
  box-shadow: 0 0 0 3px var(--accent-soft);
}
/* Description is the card title — keep it borderless + bold, box only on focus. */
.inv-list-row-card-desc.inv-list-cell {
  background: transparent;
  border-color: transparent;
  padding: 4px 6px;
}
.inv-list-row-card-desc.inv-list-cell:focus { background: var(--bg-surface); }

/* Stock block — the hero of a stock-take: three clear boxed stats. */
.inv-list-row-card-stock {
  display: grid;
  grid-template-columns: 1fr 1fr 1fr;
  gap: 8px;
  padding-top: 12px;
  border-top: 1px solid var(--border);
}
.inv-list-row-card-stock .inv-list-row-card-eyebrow { text-align: center; }
.inv-list-row-card-stock .inv-list-cell {
  text-align: center;
  font-size: 17px;
  font-weight: 700;
  padding: 11px 6px;
}
/* Current = the count you're editing — make it the focal point. */
.inv-list-row-card-stock .inv-list-cell-current {
  background: var(--accent-soft);
  border-color: color-mix(in srgb, var(--accent) 40%, transparent);
  color: var(--accent);
}
/* To order — computed, shown as a boxed stat that colour-codes the state. */
.inv-list-row-card-toorder .inv-list-cell-order {
  display: flex;
  align-items: center;
  justify-content: center;
  min-height: 41px;
  border-radius: var(--radius-sm);
  background: var(--bg-elevated);
  border: 1px solid var(--border);
  font-size: 17px;
  font-weight: 700;
  color: var(--text-muted);
}
.inv-list-row-card-toorder .inv-list-cell-order.is-short {
  background: rgba(234,88,12,0.10);
  border-color: rgba(234,88,12,0.35);
  color: #ea580c;
}
.inv-list-row-card-toorder .inv-list-cell-order.is-ok {
  background: var(--success-soft);
  border-color: color-mix(in srgb, var(--success) 35%, transparent);
  color: var(--success);
}
/* Info + Delete sit top-right of the card as borderless icon buttons,
   matching the icon-button style used across the other modules. */
.inv-list-row-card-actions {
  display: flex;
  align-items: center;
  gap: 2px;
  flex-shrink: 0;
  margin-left: auto;
}
.inv-list-row-card-actbtn {
  position: relative;
  display: grid;
  place-items: center;
  width: 36px;
  height: 36px;
  background: transparent;
  border: 0;
  border-radius: 50%;
  color: var(--text-muted);
  cursor: pointer;
  transition: background 0.12s, color 0.12s;
  -webkit-tap-highlight-color: transparent;
}
.inv-list-row-card-actbtn:hover,
.inv-list-row-card-actbtn.has-info {
  background: var(--accent-soft);
  color: var(--accent);
}
.inv-list-row-card-actbtn--del:hover {
  background: var(--danger-soft, rgba(220,38,38,0.10));
  color: var(--danger, #dc2626);
}

/* ─── Info modal photos ──────────────────────────────────────── */
.inv-list-info-photos {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(90px, 1fr));
  gap: 6px;
  margin-bottom: 8px;
}
.inv-list-info-photo {
  position: relative;
  aspect-ratio: 1 / 1;
  background: var(--bg-elevated);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  overflow: hidden;
  cursor: pointer;
}
.inv-list-info-photo-thumb { width: 100%; height: 100%; }
.inv-list-info-photo-thumb img {
  width: 100%; height: 100%;
  object-fit: cover;
  display: block;
}
.inv-list-info-photo-del {
  position: absolute;
  top: 4px; right: 4px;
  display: grid; place-items: center;
  width: 22px; height: 22px;
  background: rgba(0,0,0,0.55);
  color: white;
  border: 0;
  border-radius: 50%;
  cursor: pointer;
}
.inv-list-info-photo-del:hover { background: var(--danger); }
/* Add-photo tile — same square footprint as a thumbnail, sits in the grid. */
.inv-list-info-photo-add {
  aspect-ratio: 1 / 1;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 4px;
  background: var(--bg-elevated);
  border: 1.5px dashed var(--border-strong);
  border-radius: var(--radius-sm);
  color: var(--text-muted);
  font: 600 var(--text-xs) / 1 var(--body);
  cursor: pointer;
  transition: border-color 0.12s, color 0.12s, background 0.12s;
}
.inv-list-info-photo-add:hover {
  border-color: var(--accent);
  color: var(--accent);
  background: color-mix(in srgb, var(--accent) 6%, transparent);
}
.inv-list-info-photo-add svg { width: 20px; height: 20px; }
.inv-list-info-photos.is-uploading { opacity: 0.55; pointer-events: none; }

/* ─── Generate-order textarea ────────────────────────────────── */
.inv-list-order-preview {
  width: 100%;
  padding: 12px;
  background: var(--bg-elevated);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  font-family: var(--font-mono, ui-monospace, SFMono-Regular, monospace);
  font-size: var(--text-sm);
  line-height: 1.4;
  color: var(--text-primary);
  resize: vertical;
  white-space: pre;
}

/* ─── Inline column management (table view) ──────────────────── */
/* Custom column header — needs space for the inline action buttons
   that appear on hover. */
.inv-list-editable-table thead th.inv-list-table-col-custom {
  padding: 6px 8px;
  position: relative;
  cursor: pointer;   /* the whole header opens the column editor */
}
.inv-list-editable-table thead th.inv-list-table-col-custom:hover {
  background: color-mix(in srgb, var(--accent) 7%, var(--bg-elevated));
}
.inv-list-col-header-label {
  display: inline-block;
  font: 600 var(--text-xs) / 1 var(--body);
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--text-muted);
}
.inv-list-col-header-actions {
  display: inline-flex;
  gap: 1px;
  margin-left: 6px;
  opacity: 0;
  transition: opacity 0.12s ease;
  vertical-align: middle;
}
.inv-list-editable-table thead th.inv-list-table-col-custom:hover .inv-list-col-header-actions {
  opacity: 1;
}
.inv-list-col-header-act {
  display: grid;
  place-items: center;
  width: 22px;
  height: 22px;
  background: var(--bg-surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  color: var(--text-muted);
  cursor: pointer;
  transition: background 0.12s, color 0.12s, border-color 0.12s;
}
.inv-list-col-header-act:hover:not(:disabled) {
  background: var(--accent-soft);
  color: var(--accent);
  border-color: color-mix(in srgb, var(--accent) 40%, transparent);
}
.inv-list-col-header-act:disabled {
  opacity: 0.25;
  cursor: default;
}

/* Footer row holds both "Add row" + "Add column" side-by-side.
   Single obvious place for "add something to this list", matching
   how the user mentally groups the actions. */
.inv-list-table-foot {
  display: flex;
  gap: 8px;
  align-items: center;
  flex-wrap: wrap;
}
.inv-list-add-col-foot-btn {
  display: inline-flex;
  align-items: center;
  gap: 6px;
}

/* ─── Inventory Lists v2.2: SKU + Price columns, mobile toggles, dividers ── */

/* SKU column — compact, monospaced (matches the rest of "code-like"
   values in the app). Sits between # and Description, narrow because
   most SKUs are <20 chars. */
.inv-list-table-col-sku {
  width: 110px;
  min-width: 90px;
}
.inv-list-cell-sku {
  font-family: var(--font-mono, ui-monospace, SFMono-Regular, monospace);
  font-size: var(--text-xs);
  letter-spacing: 0.02em;
}

/* Price column — sits just before Needed. Numeric, right-aligned. */
.inv-list-table-col-price {
  width: 96px;
  min-width: 84px;
}

/* Column dividers — subtle 1px right-borders between every header
   and data cell. Reads as gentle structure, not a heavy spreadsheet
   grid. Drop the divider on the very last cell so it doesn't paint
   over the table's outer border. */
.inv-list-editable-table thead th,
.inv-list-editable-table tbody td {
  border-right: 1px solid color-mix(in srgb, var(--border) 65%, transparent);
}
.inv-list-editable-table thead th:last-child,
.inv-list-editable-table tbody td:last-child {
  border-right: none;
}

/* Mobile-visibility toggle button on each column header. Subtle until
   hovered or activated. "On" state (visible on mobile) is accent-tinted;
   "off" state is muted — matches a physical toggle's affordance.
   Tooltip explains the state on hover. */
.inv-list-mobile-toggle {
  display: inline-grid;
  place-items: center;
  width: 20px;
  height: 20px;
  margin-left: 4px;
  background: transparent;
  border: 0;
  border-radius: var(--radius-sm);
  color: var(--text-muted);
  cursor: pointer;
  opacity: 0;
  transition: opacity 0.12s ease, background 0.12s ease, color 0.12s ease;
  vertical-align: middle;
}
/* Always show "off" state — communicates that the column won't appear
   on mobile so the user doesn't wonder why it's missing. */
.inv-list-mobile-toggle:not(.is-on) {
  opacity: 0.85;
  color: var(--text-muted);
  background: color-mix(in srgb, var(--text-muted) 12%, transparent);
}
.inv-list-editable-table thead th:hover .inv-list-mobile-toggle.is-on {
  opacity: 1;
  color: var(--accent);
}
.inv-list-mobile-toggle:hover {
  background: var(--accent-soft);
  color: var(--accent);
  opacity: 1;
}

/* Mobile media query — hide any cell with .inv-list-mobile-hide
   when the viewport is narrow. The toggles only show on desktop
   (the table view), so users won't be able to flip the state on
   mobile — they'll just see the consequences.

   Note: .inv-list-row-card (mobile card) already filters in JS
   based on the same boolean, so the cards reflect the toggles too. */
@media (max-width: 767px) {
  .inv-list-editable-table .inv-list-mobile-hide { display: none !important; }
}

/* ═══════════════════════════════════════════════════════════════════════
   AUDITS MODULE
   ─────────────────────────────────────────────────────────────────────
   Layout follows the same rectangular-card / Syne-headline aesthetic
   as the rest of Angel Hub. Three primary surfaces:
     1. Templates list + builder modal
     2. Audit runner (full-screen overlay, mobile-optimised)
     3. Submission detail (read-only, with linked-task overlay)

   All overlays use .audit-modal-overlay backdrop + .audit-modal panel.
   Buttons follow .audit-btn / .audit-btn-primary / -secondary / -ghost
   / -ghost-danger conventions.
   ═══════════════════════════════════════════════════════════════════════ */

/* ─── Page shell ─────────────────────────────────────────────────────
   The Audits front page now renders inside the SHARED Checklist list shell
   (.asset-list-wrap — see modules/audits/index.js), so it inherits the exact
   same width and vertical rhythm as Checklists on every breakpoint: it fills
   .main (up to ~1700px on large monitors) with no audit-specific 1280px cap or
   extra side padding, and the KPI row / tab bar / card grid are spaced by the
   wrapper's own flex gap. The old forked .audit-page (1280px cap + redundant
   inner padding) made Audits render ~32px narrower than Checklists and stop
   widening past 1280px on big screens — removed. The tab bar
   (.callout-view-toggle.cl-tab-bar.view-toggle-fit, css/12), KPI cards
   (.asset-stats-row + .callout-kpi-card.cl-kpi-card, css/12) and card grid
   (.cl-card-grid, css/14) are all the shared Checklist styles. */
.audit-tab-body { min-height: 200px; }

.audit-loading {
  text-align: center;
  color: var(--text-muted);
  padding: var(--space-6);
  font-size: var(--text-sm);
}

/* ─── Empty state ─────────────────────────────────────────────────── */
.audit-empty {
  display: flex;
  flex-direction: column;
  align-items: center;
  padding: 56px 24px;
  text-align: center;
  background: var(--bg-surface);
  border: 1px dashed var(--border);
  border-radius: var(--radius-md);
}
/* ── Reports tab — finalised-audit summary (KPIs + per-template table) ── */
.audit-report { display: flex; flex-direction: column; gap: 14px; }
.audit-report-kpis { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 10px; }
.audit-report-section-head { font: 700 var(--text-sm) / 1 var(--body); text-transform: uppercase; letter-spacing: 0.05em; color: var(--text-muted); }
.audit-report-table { display: flex; flex-direction: column; border: 1px solid var(--border); border-radius: var(--radius-md); overflow: hidden; background: var(--bg-surface); }
.audit-report-row { display: grid; grid-template-columns: 2fr 0.7fr 0.8fr 0.7fr 0.7fr; align-items: center; gap: 8px; padding: 10px 12px; border-top: 1px solid var(--border); }
.audit-report-row:first-child { border-top: 0; }
.audit-report-head { background: var(--bg-elevated); }
.audit-report-head .audit-report-cell { font: 700 var(--text-xs) / 1 var(--body); text-transform: uppercase; letter-spacing: 0.04em; color: var(--text-muted); }
.audit-report-cell { font: 600 13px / 1.3 var(--body); color: var(--text-primary); text-align: center; }
.audit-report-tpl { text-align: left; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.audit-report-open { color: var(--rag-fail); font-weight: 700; }
/* RAG colour on the avg-score cells + KPI (scoped, so no effect elsewhere).
   .audit-stat-tone-* only colours a nested .audit-stat-num, so map them here. */
.audit-report-cell.audit-stat-tone-success { color: var(--rag-pass); }
.audit-report-cell.audit-stat-tone-warning { color: var(--rag-warn); }
.audit-report-cell.audit-stat-tone-danger  { color: var(--rag-fail); }
.audit-report-kpis .callout-kpi-value.audit-stat-tone-success { color: var(--rag-pass); }
.audit-report-kpis .callout-kpi-value.audit-stat-tone-warning { color: var(--rag-warn); }
.audit-report-kpis .callout-kpi-value.audit-stat-tone-danger  { color: var(--rag-fail); }
@media (max-width: 640px) { .audit-report-kpis { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
.audit-empty-icon {
  width: 56px; height: 56px;
  border-radius: 50%;
  background: var(--accent-soft);
  display: flex; align-items: center; justify-content: center;
  color: var(--accent);
  margin-bottom: var(--space-3);
}
.audit-empty-icon svg { width: 28px; height: 28px; }
.audit-empty-title {
  font-family: var(--display);
  font-size: 20px;
  font-weight: 700;
  margin-bottom: 6px;
}
.audit-empty-sub {
  font-size: var(--text-sm);
  color: var(--text-muted);
  max-width: 460px;
  margin-bottom: var(--space-4);
}

/* ─── Buttons ──────────────────────────────────────────────────────
   Aligned to the modal-btn pattern used by tasks/callouts:
   - Padding 9px 18px (was 9px 14px)
   - Radius-md (was -sm)
   - Primary uses var(--bg-base) for text (canonical inverse on accent)
   - Hover via opacity:0.88 to match elsewhere in the app
*/
.audit-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 6px;
  padding: 9px 18px;
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  background: var(--bg-elevated);
  color: var(--text-secondary);
  font-family: var(--body);
  font-size: var(--text-sm);
  font-weight: 600;
  cursor: pointer;
  transition: opacity 120ms, background 120ms, color 120ms, border-color 120ms;
}
.audit-btn:hover  { background: var(--bg-hover); color: var(--text-primary); }
.audit-btn svg    { width: 14px; height: 14px; }
.audit-btn:disabled { opacity: 0.5; cursor: not-allowed; }

.audit-btn-primary {
  background: var(--accent);
  border-color: var(--accent);
  color: var(--bg-base);
}
.audit-btn-primary:hover {
  opacity: 0.88;
  background: var(--accent);
  color: var(--bg-base);
}

.audit-btn-secondary {
  background: var(--bg-elevated);
  border: 1.5px dashed var(--border-strong);
  color: var(--text-secondary);
}
.audit-btn-secondary:hover {
  color: var(--accent);
  border-color: var(--accent);
  background: var(--bg-elevated);
}

.audit-btn-ghost {
  background: transparent;
  border-color: var(--border);
  color: var(--text-secondary);
}
.audit-btn-ghost:hover { background: var(--bg-elevated); color: var(--text-primary); }

.audit-btn-ghost-danger {
  background: transparent;
  border-color: color-mix(in srgb, #ef4444 35%, var(--border));
  color: #ef4444;
}
.audit-btn-ghost-danger:hover {
  background: color-mix(in srgb, #ef4444 8%, transparent);
}

/* ─── Template cards (grid is now the SHARED .cl-card-grid, css/14) ──── */

.audit-tpl-card {
  background: var(--bg-surface);
  border: 1px solid var(--border);
  /* Canonical card identity bar (matches submission cards + the two-tier rule:
     the template's colour lives on a 4px edge bar, not an 8×38 swatch). */
  border-left: 4px solid var(--tpl-colour, var(--accent));
  border-radius: var(--radius-lg);
  padding: var(--space-4);
  display: flex;
  flex-direction: column;
  gap: 10px;
  position: relative;
  transition: border-color 120ms, transform 120ms;
}
.audit-tpl-card:hover {
  border-color: color-mix(in srgb, var(--tpl-colour, var(--accent)) 45%, var(--border));
  border-left-color: var(--tpl-colour, var(--accent));
  transform: translateY(-1px);
}

.audit-tpl-card-head {
  display: flex;
  align-items: center;
  gap: 10px;
}
.audit-tpl-card-title-wrap { flex: 1; min-width: 0; }

/* ── Audit settings panel (gear → tabbed: Categories / Types / Question packs).
   Reuses the shared tab bar + the asset taxonomy-manager rows; only the tab-body
   frame + the "coming next" placeholder are local. ── */
.audit-set-tabs { margin-bottom: 14px; }
.audit-set-tabbody { min-height: 240px; }
.audit-set-tabbody .asset-cat-list { margin: 0 0 12px; }
.audit-set-add { width: 100%; }
.audit-set-placeholder {
  display: flex; flex-direction: column; gap: 8px;
  padding: 22px 16px; text-align: center;
  border: 1px dashed var(--border-strong); border-radius: var(--radius-lg, 14px);
  background: var(--bg-elevated);
}
.audit-set-placeholder-title { font: 600 var(--text-base) / 1.2 var(--display, var(--body)); color: var(--text-primary); }
.audit-set-placeholder-sub { font: 500 var(--text-sm) / 1.5 var(--body); color: var(--text-secondary); max-width: 460px; margin: 0 auto; }
/* Saved-pack list row: a question-count sits between the name chip and the delete
   action (the actions block already pushes right via margin-left:auto). */
.audit-set-pack-count { margin-left: 10px; font: 500 var(--text-xs) / 1 var(--body); color: var(--text-muted); white-space: nowrap; }

/* Builder: "New pack" + "Use a saved pack" side by side; "Save to library" under
   a pack's questions. */
.audit-pack-add-row { display: flex; gap: 8px; flex-wrap: wrap; }
.audit-pack-add-row > .audit-btn { flex: 1 1 140px; }
.audit-pack-savelib { margin-top: 8px; }

/* Category chip — neutral chip, colour only on the dot (two-tier card rule). */
.audit-tpl-cat-chip {
  display: inline-flex; align-items: center; gap: 6px; flex-shrink: 0;
  padding: 4px 9px;
  font: 600 var(--text-xs) / 1 var(--body);
  color: var(--text-secondary);
  background: var(--bg-elevated);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  white-space: nowrap;
}
.audit-tpl-cat-dot { width: 8px; height: 8px; border-radius: 50%; flex-shrink: 0; }
.audit-tpl-card-title {
  font-family: var(--display);
  font-weight: 700;
  font-size: 16px;
  line-height: 1.2;
}
.audit-tpl-card-meta {
  font-size: var(--text-xs);
  color: var(--text-muted);
  margin-top: 2px;
}
.audit-tpl-card-desc {
  font-size: var(--text-sm);
  color: var(--text-muted);
  line-height: 1.4;
}
.audit-tpl-card-sections {
  display: flex;
  flex-wrap: wrap;
  gap: 4px;
}
.audit-section-pill {
  display: inline-block;
  padding: 4px 9px;
  background: var(--bg-elevated);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  font-size: var(--text-xs);
  color: var(--text-muted);
  font-weight: 600;
}
.audit-section-pill-more { color: var(--accent); }

.audit-tpl-card-actions {
  display: flex;
  gap: 6px;
  margin-top: auto;
  padding-top: 4px;
}
.audit-tpl-card-actions .audit-btn-primary { flex: 1; }

/* ─── Modal overlay ─────────────────────────────────────────────── */
.audit-modal-overlay {
  position: fixed; inset: 0;
  background: rgba(0, 0, 0, 0.5);
  z-index: 9100;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: var(--space-3);
  overflow-y: auto;
}
body.audit-modal-open { overflow: hidden; }

/* Modal frame — radius-lg per canonical modal pattern (callouts uses
   the same 10px radius). Border-strong for the outline to match the
   tasks-modal frame. */
.audit-modal {
  background: var(--bg-surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  width: 100%;
  max-width: 720px;
  max-height: 90vh;
  display: flex;
  flex-direction: column;
  overflow: hidden;
  box-shadow: var(--shadow-lg);
}

.audit-modal-head {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  padding: 16px var(--space-4) 14px;
  border-bottom: 1px solid var(--border);
}
.audit-modal-title {
  font-family: var(--display);
  font-weight: 600;
  font-size: var(--text-xl);
  flex: 1;
}
.audit-modal-close {
  width: 36px; height: 36px;
  display: grid; place-items: center;
  background: transparent;
  border: 0;
  border-radius: var(--radius-md);
  color: var(--text-muted);
  cursor: pointer;
  transition: all 0.12s;
}
.audit-modal-close:hover { background: var(--bg-elevated); color: var(--text-primary); }
.audit-modal-close svg { width: 18px; height: 18px; }

.audit-modal-body {
  flex: 1;
  /* min-height:0 lets this flex child shrink BELOW its content height so its OWN
     overflow-y scroll engages. Without it the body grows to fit content and pushes
     the modal past the viewport, so the whole thing (top bar + footer buttons)
     scrolls instead of just the body — the bug Radek hit (2026-06-29). Same fix
     Tasks + chat use. */
  min-height: 0;
  overflow-y: auto;
  padding: 16px var(--space-4);
}

.audit-modal-foot {
  display: flex;
  align-items: center;
  flex-shrink: 0;
  gap: var(--space-2);
  padding: 14px var(--space-4) 18px;
  border-top: 1px solid var(--border);
  background: var(--bg-surface);
}

/* Form rows — labels match the tasks/callouts pattern (text-sm,
   text-secondary, no uppercase). Inputs match canonical .tasks-field
   (bg-elevated background, radius-md, 10px padding, text-base). */
.audit-form-row { margin-bottom: var(--space-3); }
.audit-form-label {
  display: block;
  font-size: var(--text-sm);
  font-weight: 600;
  color: var(--text-secondary);
  margin-bottom: 6px;
}
.audit-required { color: #ef4444; }
.audit-input {
  width: 100%;
  padding: 10px 12px;
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  background: var(--bg-elevated);
  color: var(--text-primary);
  font-family: var(--body);
  font-size: var(--text-base);
  transition: border-color 120ms, background 120ms;
}
.audit-input:focus {
  outline: none;
  border-color: var(--accent);
  background: var(--bg-elevated);
}
.audit-textarea {
  resize: vertical;
  min-height: 70px;
  line-height: 1.45;
}
.audit-select {
  appearance: none;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 24 24' fill='none' stroke='%2364748b' stroke-width='2'%3E%3Cpolyline points='6 9 12 15 18 9'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right 10px center;
  padding-right: 30px;
}

/* Colour picker */
.audit-colour-picker {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
}
.audit-colour-swatch {
  width: 28px; height: 28px;
  border-radius: 50%;
  border: 2px solid transparent;
  cursor: pointer;
  transition: border-color 120ms, transform 120ms;
}
.audit-colour-swatch:hover { transform: scale(1.1); }
.audit-colour-swatch.is-selected {
  border-color: var(--text-primary);
  outline: 2px solid var(--bg-surface);
}

/* Builder divider + section head */
.audit-builder-divider {
  height: 1px;
  background: var(--border);
  margin: var(--space-4) 0 var(--space-3);
}
.audit-builder-section-head {
  margin-bottom: var(--space-3);
}
.audit-builder-section-title {
  font-family: var(--display);
  font-size: 16px;
  font-weight: 700;
  margin: 0;
}
.audit-builder-section-sub {
  font-size: var(--text-xs);
  color: var(--text-muted);
  margin-top: 2px;
}
.audit-builder-questions {
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
  margin-bottom: var(--space-3);
}
.audit-add-question {
  width: 100%;
  padding: 12px;
}

/* Builder question card */
.audit-q-card {
  background: var(--bg-elevated);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  padding: 10px var(--space-3) var(--space-3);
}
.audit-q-head {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-bottom: 8px;
}
.audit-q-num {
  font-family: var(--display);
  font-weight: 700;
  font-size: 13px;
  color: var(--accent);
  padding: 2px 8px;
  background: var(--accent-soft);
  border-radius: 4px;
  letter-spacing: 0.04em;
}
.audit-q-handle-actions {
  margin-left: auto;
  display: flex;
  gap: 4px;
}
.audit-q-move, .audit-q-delete {
  width: 28px; height: 28px;
  border: 1px solid transparent;
  background: transparent;
  border-radius: var(--radius-sm);
  color: var(--text-muted);
  cursor: pointer;
  display: inline-flex; align-items: center; justify-content: center;
}
.audit-q-move:hover { background: var(--bg-surface); color: var(--accent); border-color: var(--border); }
.audit-q-delete:hover { background: color-mix(in srgb, #ef4444 10%, transparent); color: #ef4444; }
.audit-q-move svg, .audit-q-delete svg { width: 14px; height: 14px; }

.audit-q-body { display: flex; flex-direction: column; gap: 8px; }
/* The photo/Info zone now sits inside the shared `.cl-item-action-row` next to
   the "On fail" chip — exactly like a Checklist check — so css/14's
   `.cl-item-v2 .cl-item-action-row .kb-doc-block-bottom-row { margin-left:auto }`
   right-pushes it. No audit-specific override needed (Radek 2026-06-26). */
.audit-q-field-row { display: flex; flex-direction: column; gap: 4px; }
.audit-q-field-label {
  font-size: 11px;
  font-weight: 600;
  color: var(--text-muted);
  text-transform: uppercase;
  letter-spacing: 0.04em;
}
.audit-q-section { font-size: var(--text-sm); }
.audit-q-label { font-weight: 500; }
.audit-q-grid {
  display: grid;
  grid-template-columns: 1fr 1fr 1fr;
  gap: 8px;
}
.audit-q-options-row { margin-top: 4px; }
.audit-q-toggles {
  display: flex;
  flex-wrap: wrap;
  gap: 12px;
  margin-top: 4px;
}
.audit-q-toggle {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font-size: var(--text-sm);
  color: var(--text-muted);
  cursor: pointer;
}
.audit-q-toggle input { accent-color: var(--accent); }

/* ── Fail-card field designer (Radek 2026-06-26) ──────────────────────
   One shared Off / Optional / Required segmented control + a labelled row,
   reused for every configurable fail-card field across all question types. */
.audit-fail-fields {
  display: flex;
  flex-direction: column;
  gap: 8px;
  padding: 10px 12px;
  background: var(--bg-surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
}
.audit-fail-fields-intro {
  font-size: var(--text-xs);
  color: var(--text-muted);
  line-height: 1.4;
}
.audit-fail-vis-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  flex-wrap: wrap;
}
.audit-fail-vis-text { display: flex; flex-direction: column; gap: 1px; min-width: 0; }
.audit-fail-vis-name { font-size: var(--text-sm); font-weight: 600; color: var(--text-primary); }
.audit-fail-vis-help { font-size: var(--text-xs); color: var(--text-muted); }
/* The segmented control — three buttons, the active one tinted accent. Mirrors
   the calm chip language used elsewhere (neutral base, accent on-state). */
.audit-fail-vis {
  display: inline-flex;
  flex-shrink: 0;
  background: var(--bg-elevated);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  padding: 2px;
  gap: 2px;
}
.audit-fail-vis-btn {
  appearance: none;
  border: 0;
  background: transparent;
  color: var(--text-secondary);
  font: 600 var(--text-xs) / 1 var(--body);
  padding: 6px 10px;
  border-radius: calc(var(--radius-sm) - 2px);
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
  transition: background 0.12s, color 0.12s;
}
.audit-fail-vis-btn:hover { color: var(--text-primary); }
.audit-fail-vis-btn.is-on {
  background: var(--accent);
  color: #fff;
}
/* "Required" pill on a run-time fail-card label */
.audit-fail-req {
  display: inline-block;
  margin-left: 6px;
  padding: 1px 7px;
  border-radius: 999px;
  font-size: 10px;
  font-weight: 700;
  letter-spacing: 0.03em;
  text-transform: uppercase;
  color: var(--rag-warn);
  background: color-mix(in srgb, var(--rag-warn-fill) 16%, transparent);
}

@media (max-width: 600px) {
  .audit-q-grid { grid-template-columns: 1fr; }
}

/* ═══ RUNNER ════════════════════════════════════════════════════════ */
.audit-runner-overlay { padding: 0; align-items: stretch; overflow: hidden; }
.audit-modal-runner {
  max-width: 800px;
  max-height: 100vh;
  max-height: 100dvh;
  height: 100vh;
  height: 100dvh;
  border-radius: 0;
}
@media (min-width: 800px) {
  .audit-runner-overlay { padding: var(--space-3); align-items: center; }
  .audit-modal-runner { max-height: 96vh; height: auto; border-radius: var(--radius-md); }
}

/* Audit runner top bar — kept COMPACT (Radek 2026-06-28): site/category/due chips
   + title pack tight, with the smallest smart gap down to the progress line. The
   runner reuses the CHECKLIST header classes, so scope the trim to .audit-modal-runner
   (+ the audit-only .audit-runner-overall-band) so the checklist header is untouched. */
.audit-modal-runner .callout-detail-header { padding-top: 10px; padding-bottom: 4px; }
.audit-modal-runner .cl-runner-title-row   { padding-top: 6px; padding-bottom: 0; }
.audit-runner-overall-band > .cl-runner-progress { padding-top: 8px; padding-bottom: 10px; }
.audit-runner-overall-band > .cl-runner-progress .cl-runner-progress-stats { margin-bottom: 6px; }
/* Runner footer. .audit-modal-foot is a NON-WRAPPING flex row, and a flex item
   cannot shrink below its min-content width — so when the actions don't fit, the
   row doesn't overflow and it doesn't scroll: every label wraps internally and
   the buttons grow into tall multi-line stacks. Measured at 390px: "Save &
   continue later" rendered 74px wide × 70px tall over three lines, which is why
   it read as missing rather than cramped (Radek 2026-08-01). Wrapping keeps each
   label on one line and pushes the overflow DOWN, where there is room.
   The 44px floor lives in the BASE rule, not behind a breakpoint: these stay
   tappable at every width (§4 — the floor follows the control).
   Right-align rather than leaning on a flex:1 spacer: a growing spacer only
   right-aligns the row it sits on, so a wrapped button would start hard left.
   The zones + preview stages still carry their spacer (they need a LEFT item),
   and it keeps working — it simply absorbs the slack ahead of the right group. */
.audit-runner-foot { flex-wrap: wrap; row-gap: 8px; justify-content: flex-end; }
.audit-runner-foot .audit-btn { min-height: 44px; }
.audit-detail-foot .audit-btn { min-height: 44px; }
/* On a phone the runner is a full-screen takeover at the physical
   top. Clear the status bar / notch at the OVERLAY level — push the whole modal
   down by --safe-top and subtract it from the modal height — the robust pattern
   the detail modal + the Tasks detail use. Padding the header alone proved
   unreliable on iOS PWA (Radek 2026-06-30). The dvh pair follows Safari's
   changing toolbar height; the preceding vh pair is the legacy fallback. Header
   keeps its compact 10px. Both action footers clear the home indicator. */
@media (max-width: 600px) {
  .audit-runner-overlay { align-items: flex-start; padding-top: var(--safe-top); }
  .audit-modal-runner {
    height: calc(100vh - var(--safe-top));
    max-height: calc(100vh - var(--safe-top));
    height: calc(100dvh - var(--safe-top));
    max-height: calc(100dvh - var(--safe-top));
  }
  .audit-modal-runner .audit-runner-foot,
  .audit-modal-detail .audit-detail-foot {
    padding-bottom: calc(14px + env(safe-area-inset-bottom));
  }
}

.audit-runner-head {
  display: grid;
  grid-template-columns: 36px 1fr auto;
  align-items: center;
  gap: var(--space-2);
  padding: var(--space-3);
  border-bottom: 1px solid var(--border);
  background: var(--bg-surface);
  position: relative;
}
.audit-runner-head::before {
  content: '';
  position: absolute;
  top: 0; left: 0; right: 0;
  height: 3px;
  background: var(--tpl-colour, var(--accent));
}
.audit-runner-close {
  width: 36px; height: 36px;
  border: 0; background: transparent;
  color: var(--text-muted);
  border-radius: var(--radius-sm);
  cursor: pointer;
  display: inline-flex; align-items: center; justify-content: center;
}
.audit-runner-close:hover { background: var(--bg-elevated); color: var(--text-primary); }
.audit-runner-close svg { width: 20px; height: 20px; }
.audit-runner-head-text { min-width: 0; }
.audit-runner-title {
  font-family: var(--display);
  font-weight: 700;
  font-size: 16px;
  line-height: 1.2;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.audit-runner-sub {
  font-size: var(--text-xs);
  color: var(--text-muted);
}
.audit-runner-progress {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 3px;
  flex-shrink: 0;
}
.audit-progress-ring { width: 44px; height: 44px; }
.audit-progress-ring-bg {
  fill: none;
  stroke: rgba(255,255,255,0.25);
  stroke-width: 3;
}
.audit-progress-ring-fill {
  fill: none;
  stroke: white;
  stroke-width: 3;
  stroke-linecap: round;
  transition: stroke-dashoffset 300ms ease-out;
}
.audit-runner-progress-text {
  font-size: 11px;
  font-weight: 700;
  color: rgba(255,255,255,0.9);
  white-space: nowrap;
}

.audit-runner-body { padding: var(--space-3); background: var(--bg-elevated); }
.audit-runner-section { margin-bottom: var(--space-4); }
.audit-runner-section-head {
  font-family: var(--display);
  font-weight: 700;
  font-size: 14px;
  color: var(--text-primary);
  padding: 10px 14px;
  background: var(--bg-surface);
  border-radius: var(--radius-md);
  border-left: 3px solid var(--accent);
  display: block;
  margin-bottom: var(--space-2);
}
.audit-runner-section-body {
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
}

/* ─── Phase 4: live RAG status (whole audit + per section) ──────────── */
/* Per-section cards reuse the checklist .cl-run-section* + .cl-runner-progress*
   styling wholesale; only spacing + the whole-audit summary card are audit-local. */
.audit-runner-body .cl-run-section { margin-bottom: var(--space-3); }
.audit-runner-overall {
  border: 1px solid var(--border);
  border-left: 4px solid #94a3b8;
  border-radius: var(--radius-md);
  background: var(--bg-elevated);
  padding: 10px 14px;
  margin-bottom: 14px;
  display: flex;
  flex-direction: column;
  gap: 8px;
}
.audit-runner-overall.is-green   { border-left-color: var(--rag-pass-fill); }
.audit-runner-overall.is-amber   { border-left-color: var(--rag-warn-fill); }
.audit-runner-overall.is-red     { border-left-color: var(--rag-fail-fill); }
.audit-runner-overall.is-neutral { border-left-color: var(--text-muted); }
.audit-runner-overall-label {
  font: 700 var(--text-xs) / 1 var(--body);
  text-transform: uppercase;
  letter-spacing: 0.05em;
  color: var(--text-muted);
}

.audit-q-run-card {
  background: var(--bg-surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  padding: var(--space-3);
  transition: border-color 120ms;
}
.audit-q-run-card.audit-q-missing-flash {
  border-color: var(--rag-warn-fill);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--rag-warn-fill) 25%, transparent);
}
/* Calm cards — no red/green flood (Radek 2026-06-26). The Yes/No button +
   the ✓/✕ status badge already say pass or fail; a coloured wash on every
   answered card was visual noise. A passed card stays neutral. The card the
   auditor is actively working (a fail, with its action panel open) gets a
   quiet accent left-edge so it reads as "needs follow-up" — not a red clang. */
.audit-q-run-card.is-passed {
  border-color: var(--border);
}
.audit-q-run-card.is-failed {
  /* Red spine, not cyan (Radek 2026-07-01): "fail" now reads red everywhere —
     runner, report, chips — agreeing with the red ✕ "No" badge. Still calm: a
     3px left spine, no red flood. (Supersedes the 2026-06-26 accent-edge.) */
  border-color: color-mix(in srgb, var(--rag-fail-fill) 35%, var(--border));
  border-left: 3px solid var(--rag-fail-fill);
}
/* Yes/No + Pass/Fail audit questions reuse the Checklist run card (.cl-run-item,
   css/14) for an exact match — re-assert the checklist grid here so it beats the
   base .audit-q-run-card above (css/15 loads after css/14). The round number badge
   (.cl-run-item-num), head, right-side ✓/✕ status badge and Yes/No buttons all come
   from css/14. (Radek 2026-06-26 — step 1 of the audit-question rebuild; the on-fail
   extras keep their audit styling until the next step.) */
.audit-q-run-card.cl-run-item {
  display: grid;
  grid-template-columns: 32px 1fr;
  align-items: start;
  gap: 8px 12px;
  padding: 14px;
}
/* .audit-q-run-num is still used by the detail view's answer card; its old
   sibling head/question rules were dropped when every question type moved to the
   shared .cl-run-item card (Phase 5, dead-CSS sweep 2026-06-26). */
.audit-q-run-num {
  font-family: var(--display);
  font-weight: 700;
  font-size: 12px;
  color: var(--accent);
  padding: 2px 8px;
  background: var(--accent-soft);
  border-radius: 4px;
  flex-shrink: 0;
  margin-top: 2px;
  letter-spacing: 0.04em;
}
.audit-q-run-badges {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  margin-bottom: 10px;
}
/* Canonical small-badge shape: radius-pill, 3px×9px padding, text-xs
   weight 600. Matches .role-badge / .inv-chip-pill / etc. */
.audit-q-badge {
  display: inline-block;
  padding: 3px 9px;
  font-size: var(--text-xs);
  font-weight: 600;
  border-radius: var(--radius-sm);
  background: var(--bg-elevated);
  border: 1px solid var(--border);
  color: var(--text-muted);
}
.audit-q-badge-required {
  color: #ef4444;
  border-color: color-mix(in srgb, #ef4444 35%, transparent);
  background: color-mix(in srgb, #ef4444 8%, transparent);
}
.audit-q-badge-sev { /* inline-styled */ }
.audit-q-badge-dept {
  color: var(--accent);
  border-color: color-mix(in srgb, var(--accent) 45%, transparent);
  background: var(--accent-soft);
}
/* Phase 3: the dept routing chip is a BUTTON — tap to re-route this audit's
   follow-up to another department. Reuses the accent badge look + a chevron. */
.audit-dept-chip {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  cursor: pointer;
  font-family: inherit;
  line-height: 1.1;
  transition: background 0.12s, border-color 0.12s, color 0.12s;
}
.audit-dept-chip:hover { background: color-mix(in srgb, var(--accent) 16%, transparent); }
.audit-dept-chip .audit-dept-chip-chev { width: 12px; height: 12px; opacity: 0.7; }
/* Unset = no dept routed yet: calm neutral so it reads as "optional, tap to set". */
.audit-dept-chip.is-unset {
  color: var(--text-secondary);
  border-color: var(--border);
  background: var(--bg-elevated);
}
.audit-dept-chip.is-unset:hover { color: var(--accent); border-color: color-mix(in srgb, var(--accent) 45%, transparent); }

/* Phase 3b: off-script findings ("Raise a task" during the audit). */
.audit-extra-findings {
  /* A calm, FINISHED card (solid hairline — the old dashed box read as a
     placeholder). The single dashed affordance is now the "Raise a task" button
     itself, so the action is obvious and the panel matches the runner's solid
     surfaces (Radek 2026-06-29). */
  margin-top: 14px;
  padding: 14px;
  background: var(--bg-surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  display: flex;
  flex-direction: column;
  gap: 12px;
}
.audit-extra-findings-head { display: flex; align-items: flex-start; gap: 10px; }
.audit-extra-findings-icon {
  display: grid;
  place-items: center;
  width: 32px;
  height: 32px;
  flex-shrink: 0;
  border-radius: var(--radius-md);
  background: color-mix(in srgb, var(--accent) 12%, transparent);
  color: var(--accent);
}
.audit-extra-findings-icon svg { width: 18px; height: 18px; }
.audit-extra-findings-headtext { min-width: 0; }
.audit-extra-findings-title { font: 600 var(--text-base) / 1.25 var(--display, var(--body)); color: var(--text-primary); }
.audit-extra-findings-sub { font-size: var(--text-xs); color: var(--text-muted); margin-top: 2px; }
.audit-findings-list { display: flex; flex-direction: column; gap: 10px; }
.audit-findings-list:empty { display: none; }
.audit-finding-card {
  background: var(--bg-surface);
  border: 1px solid var(--border);
  border-left: 4px solid var(--accent);
  border-radius: var(--radius-md);
  padding: 10px;
  display: flex;
  flex-direction: column;
  gap: 10px;
}
.audit-finding-head { display: flex; align-items: center; gap: 8px; }
.audit-finding-num { display: grid; place-items: center; width: 28px; height: 28px; flex-shrink: 0; color: var(--accent); }
.audit-finding-num svg { width: 18px; height: 18px; }
.audit-finding-title { flex: 1; min-width: 0; }
.audit-finding-controls { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; }
/* Full-width dashed "add" affordance — the one dashed element in the panel, matching
   the app's add-tile language (.cl-add-item-btn / .audit-photo-add-btn). */
.audit-add-finding {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  width: 100%;
  padding: 11px;
  background: transparent;
  border: 1.5px dashed var(--border-strong);
  border-radius: var(--radius-md);
  color: var(--text-secondary);
  font: 600 var(--text-base) / 1 var(--body);
  cursor: pointer;
  transition: border-color 0.12s, color 0.12s, background 0.12s;
}
.audit-add-finding:hover {
  border-color: var(--accent);
  color: var(--accent);
  background: color-mix(in srgb, var(--accent) 7%, transparent);
}
.audit-add-finding svg { width: 18px; height: 18px; }

/* Pass/Fail buttons */
.audit-pf-buttons {
  display: flex;
  gap: 8px;
  margin-bottom: 10px;
}
.audit-pf-btn {
  flex: 1;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  padding: 16px 20px;
  min-height: 52px;
  border: 2px solid var(--border);
  border-radius: var(--radius-md);
  background: var(--bg-elevated);
  color: var(--text-secondary);
  font-family: var(--body);
  font-size: 15px;
  font-weight: 600;
  cursor: pointer;
  transition: all 120ms;
}
.audit-pf-btn svg { width: 18px; height: 18px; }
.audit-pf-btn:hover { background: var(--bg-elevated); }

.audit-pf-pass.is-on {
  background: color-mix(in srgb, var(--rag-pass-fill) 12%, transparent);
  border-color: var(--rag-pass-fill);
  color: var(--rag-pass);
}
.audit-pf-fail.is-on {
  background: color-mix(in srgb, var(--rag-fail-fill) 12%, transparent);
  border-color: var(--rag-fail-fill);
  color: var(--rag-fail);
}
.audit-pf-na.is-on {
  background: var(--bg-elevated);
  border-color: var(--text-muted);
  color: var(--text-muted);
}
.audit-pf-small .audit-pf-btn { padding: 8px 12px; flex: 0 0 auto; }
.audit-pf-small { margin-bottom: 8px; }

.audit-numeric-row { display: flex; flex-direction: column; gap: 8px; margin-bottom: 8px; }
.audit-numeric-input { max-width: 200px; }

.audit-mc-options {
  display: flex;
  flex-direction: column;
  gap: 6px;
  margin-bottom: 8px;
}
.audit-mc-option {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 10px 12px;
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  background: var(--bg-elevated);
  cursor: pointer;
  transition: all 120ms;
}
.audit-mc-option input { accent-color: var(--accent); }
.audit-mc-option-label { flex: 1; }
.audit-mc-option-fail-flag {
  font-size: 10px;
  font-weight: 700;
  letter-spacing: 0.06em;
  padding: 2px 6px;
  border-radius: var(--radius-sm);
  background: color-mix(in srgb, var(--rag-fail-fill) 14%, transparent);
  color: var(--rag-fail);
  border: 1px solid color-mix(in srgb, var(--rag-fail-fill) 35%, transparent);
}

/* Non-fail option selected — accent highlight */
.audit-mc-option.is-selected:not(.is-fail-option) {
  border-color: color-mix(in srgb, var(--accent) 50%, transparent);
  background: color-mix(in srgb, var(--accent) 6%, var(--bg-elevated));
}
/* Fail option selected — red highlight (visually confirms auto-fail) */
.audit-mc-option.is-selected.is-fail-option {
  border-color: color-mix(in srgb, #ef4444 50%, transparent);
  background: color-mix(in srgb, #ef4444 8%, var(--bg-elevated));
}

/* Builder: multi-choice option row editor */
.audit-q-mc-help {
  font-size: var(--text-xs);
  color: var(--text-muted);
  margin-bottom: 8px;
  line-height: 1.4;
}
.audit-q-mc-list {
  display: flex;
  flex-direction: column;
  gap: 6px;
  margin-bottom: 8px;
}
.audit-q-mc-row {
  display: grid;
  grid-template-columns: 1fr auto auto;
  gap: 8px;
  align-items: center;
  padding: 6px 8px;
  background: var(--bg-elevated);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  transition: border-color 120ms, background 120ms;
}
.audit-q-mc-row.is-fail {
  border-color: color-mix(in srgb, #ef4444 35%, transparent);
  background: color-mix(in srgb, #ef4444 6%, var(--bg-elevated));
}
.audit-q-mc-input { font-size: var(--text-sm); padding: 6px 10px; }
.audit-q-mc-fail-toggle {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 5px 10px;
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  background: var(--bg-surface);
  color: var(--text-muted);
  font-size: var(--text-xs);
  font-weight: 600;
  cursor: pointer;
  user-select: none;
  transition: all 120ms;
}
.audit-q-mc-fail-toggle input { display: none; }
.audit-q-mc-fail-toggle:hover {
  border-color: color-mix(in srgb, var(--rag-fail-fill) 50%, var(--border));
  color: var(--rag-fail);
}
.audit-q-mc-fail-toggle.is-on {
  background: color-mix(in srgb, var(--rag-fail-fill) 14%, transparent);
  border-color: color-mix(in srgb, var(--rag-fail-fill) 45%, transparent);
  color: var(--rag-fail);
}
.audit-q-mc-add { width: auto; padding: 8px 12px; font-size: var(--text-xs); }

/* Detail view: multi-choice fail-option indicator */
.audit-detail-value-fail {
  color: var(--rag-fail);
}
.audit-detail-mc-fail-flag {
  display: inline-block;
  margin-left: 6px;
  font-size: 10px;
  font-weight: 700;
  letter-spacing: 0.06em;
  padding: 2px 6px;
  border-radius: var(--radius-sm);
  background: color-mix(in srgb, var(--rag-fail-fill) 14%, transparent);
  color: var(--rag-fail);
  border: 1px solid color-mix(in srgb, var(--rag-fail-fill) 35%, transparent);
}

/* Photo strip — matches the canonical .callout-form-photo-tile pattern
   (86×86 tiles, radius-md, 1.5px dashed border-strong on the add-button,
   delete affordance in the corner). */
.audit-photo-row {
  display: flex;
  align-items: flex-start;
  gap: 10px;
  /* No own margin — the parent .cl-run-item-content's flex gap is the single
     vertical rhythm so spacing doesn't compound (Radek 2026-06-26). */
  margin: 0;
  flex-wrap: wrap;
}
.audit-photo-add-btn {
  display: inline-flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 4px;
  width: 86px; height: 86px;
  border: 1.5px dashed var(--border-strong);
  border-radius: var(--radius-md);
  background: var(--bg-elevated);
  color: var(--text-secondary);
  font-family: var(--body);
  font-size: var(--text-xs);
  font-weight: 600;
  cursor: pointer;
  flex-shrink: 0;
  transition: all 0.12s;
}
.audit-photo-add-btn:hover { color: var(--accent); border-color: var(--accent); }
.audit-photo-add-btn svg { width: 20px; height: 20px; }
.audit-photo-strip {
  display: flex;
  gap: 8px;
  flex-wrap: wrap;
  flex: 1;
}
.audit-photo-thumb {
  position: relative;
  width: 86px; height: 86px;
  border-radius: var(--radius-md);
  overflow: hidden;
  background: var(--bg-elevated);
  border: 1px solid var(--border);
}
.audit-photo-thumb img {
  width: 100%; height: 100%;
  object-fit: cover;
  display: block;
  cursor: pointer;   /* tap to open the shared lightbox + annotate */
}
.audit-photo-thumb img:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }
.audit-photo-thumb-del {
  position: absolute;
  top: 4px; right: 4px;
  width: 22px; height: 22px;
  border: 0;
  background: rgba(0, 0, 0, 0.65);
  color: white;
  border-radius: var(--radius-full);
  cursor: pointer;
  display: grid;
  place-items: center;
  opacity: 0;
  transition: opacity 120ms;
}
.audit-photo-thumb:hover .audit-photo-thumb-del { opacity: 1; }
/* Touch: show the remove button always (a hover-reveal traps the first tap). */
@media (hover: none) { .audit-photo-thumb:not(.audit-photo-thumb-readonly) .audit-photo-thumb-del { opacity: 1; } }
.audit-photo-thumb-del:hover { background: rgba(0, 0, 0, 0.8); }
.audit-photo-thumb-del svg { width: 12px; height: 12px; }
.audit-photo-thumb-loading {
  display: grid;
  place-items: center;
  font-size: var(--text-xs);
  color: var(--text-muted);
  background: var(--bg-elevated);
  font-weight: 600;
}
.audit-photo-thumb-readonly:hover .audit-photo-thumb-del { opacity: 0; }

/* Spacing owned by the parent .cl-run-item-content flex gap (the note row is a
   sibling flex child now, not nested) — no own margin so it doesn't compound. */
.audit-q-note-row { margin-top: 0; }
.audit-q-note-input { font-size: var(--text-sm); }

/* Compact per-question evidence row (audit runner, Radek 2026-06-27) — replaces
   the old 86px "Add photo" tile that sat on every question. Photo + Note are small
   pills; thumbnails shrink to match. The asset DETAIL view keeps its own larger
   .audit-photo-row / .audit-photo-add-btn — these classes are runner-only. */
.audit-evidence-row {
  display: flex;
  align-items: flex-start;
  gap: 8px;
  flex-wrap: wrap;
  margin: 0;
}
/* Square evidence tiles — the SAME 64×64 footprint as the photo thumbnails in
   this row, matching the question's reference thumbnail size, so "Photo / Note"
   and any added photos line up as one clean grid of equal squares. Bumped from
   56→64 (Radek 2026-06-28) so the tiles aren't cramped and the corner ✕ is less
   easy to mis-tap. Icon on top + label below, like the Tasks quick-action tiles
   (.callout-detail-quick-chip). */
.audit-evidence-btn {
  display: inline-flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 5px;
  /* PASS view = compact 64×64 (the 2026-06-27 declutter — Photo/Note are light
     optional actions on a pass). A FAIL bumps to 86 via `.is-fail` below, matching
     the detail after-photos (Radek 2026-06-29). */
  width: 64px;
  height: 64px;
  padding: 6px 4px;
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-md);
  background: var(--bg-elevated);
  color: var(--text-secondary);
  font: 600 11px / 1 var(--body);
  cursor: pointer;
  flex-shrink: 0;
  transition: color 0.12s, border-color 0.12s, background 0.12s;
}
.audit-evidence-btn:hover { color: var(--accent); border-color: var(--accent); }
/* Icon carries the accent (label stays neutral) — matches the Tasks quick-action
   tiles (.callout-detail-quick-chip), so "Photo / Note" read the same everywhere. */
.audit-evidence-btn svg { width: 20px; height: 20px; color: var(--accent); }
/* PASS view thumbnails stay compact 64×64 to match the compact Photo/Note tiles. */
.audit-evidence-row .audit-photo-thumb { width: 64px; height: 64px; }
/* FAIL view (Radek 2026-06-29): the Photo/Note tiles AND the added-photo thumbnails
   grow to 86×86 — identical to the detail after-photos — because a fail's photo is
   the evidence. Driven by `is-fail` on the row (set in audit-form.js). */
.audit-evidence-row.is-fail .audit-evidence-btn,
.audit-evidence-row.is-fail .audit-photo-thumb { width: 86px; height: 86px; }

/* ── Section nav strip (Radek 2026-06-29) — a sticky, swipeable row of "jump to
   section" chips under the whole-audit progress bar. Colour = RAG COMPLETION
   (neutral → green when done → red on a fail); hierarchy is structural (nested
   chips read smaller with a ›, location-bound ones carry a pin). ───────────── */
.audit-section-nav {
  display: flex;
  align-items: center;
  gap: 8px;
  flex-shrink: 0;                     /* stays put while the body scrolls */
  overflow-x: auto;
  -webkit-overflow-scrolling: touch;
  scrollbar-width: none;
  padding: 9px 14px;
  background: var(--bg-surface);
  border-bottom: 1px solid var(--border);
}
.audit-section-nav::-webkit-scrollbar { display: none; }
.audit-section-nav-chip {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  flex-shrink: 0;
  padding: 6px 11px;
  border-radius: 999px;
  border: 1px solid var(--border);
  background: var(--bg-elevated);
  color: var(--text-secondary);
  font: 600 var(--text-sm) / 1 var(--body);
  white-space: nowrap;
  cursor: pointer;
  transition: border-color 0.12s, color 0.12s, background 0.12s;
}
.audit-section-nav-chip:hover { border-color: var(--accent); color: var(--text-primary); }
.audit-section-nav-pin { width: 13px; height: 13px; flex-shrink: 0; }
.audit-section-nav-chev { color: var(--text-muted); font-weight: 700; margin-right: -1px; }
/* nested sub-sections — smaller + calmer (the structural hierarchy cue) */
.audit-section-nav-chip.is-nested { padding: 5px 9px; font-size: var(--text-xs); color: var(--text-muted); }
/* RAG completion = the loud signal (defined AFTER .is-nested so colour wins) */
.audit-section-nav-chip.is-green {
  border-color: color-mix(in srgb, var(--rag-pass-fill) 45%, transparent);
  background: color-mix(in srgb, var(--rag-pass-fill) 12%, transparent);
  color: var(--rag-pass);
}
.audit-section-nav-chip.is-red {
  border-color: color-mix(in srgb, var(--rag-fail-fill) 50%, transparent);
  background: color-mix(in srgb, var(--rag-fail-fill) 12%, transparent);
  color: var(--rag-fail);
}

.audit-runner-notes-row {
  background: var(--bg-surface);
  padding: var(--space-3);
  border-radius: var(--radius-md);
  border: 1px solid var(--border);
}

/* ═══ DETAIL VIEW ═══════════════════════════════════════════════════ */
.audit-detail-overlay { padding: 0; align-items: stretch; overflow: hidden; }
.audit-modal-detail {
  max-width: 820px;
  max-height: 100vh;
  max-height: 100dvh;
  height: 100vh;
  height: 100dvh;
  border-radius: 0;
}
@media (min-width: 800px) {
  .audit-detail-overlay { padding: var(--space-3); align-items: center; }
  .audit-modal-detail { max-height: 96vh; height: auto; border-radius: var(--radius-md); }
}

/* Detail header mirrors the RUNNER's top bar — it reuses the same
   `callout-detail-header cl-runner-header` classes (site LOCATION chip +
   share/close actions on the RIGHT, title below, then a small ref·who·when
   meta line), so it inherits the runner's look + spacing. Scope the compact
   trim to .audit-modal-detail so the checklist/callout header is untouched. */
.audit-modal-detail .callout-detail-header {
  padding-top: 10px;
  padding-bottom: 4px;
  /* The shared header defaults to --bg-base (grey), which reads as an unfinished
     grey bar on the white audit-detail modal. Paint it --bg-surface (white) so the
     header sits seamlessly on the white modal shell (its border-bottom keeps the
     divider). Scoped to .audit-modal-detail — the RUNNER (.audit-modal-runner)
     keeps its grey header. The grey body (--bg-elevated) stays, so the white
     question-cards still stand out. */
  background: var(--bg-surface);
}
.audit-modal-detail .cl-runner-title-row   { padding-top: 6px; padding-bottom: 2px; }
.audit-detail-submeta {
  /* Leads the scroll head now (moved out of the pinned bar 2026-07-05) — a little
     top room so it doesn't butt against the pinned divider. */
  padding: 9px 16px 10px;
  font-size: var(--text-xs);
  color: var(--text-muted);
}
/* The detail modal is a full-screen takeover below 800px. On phones,
   push the WHOLE modal below the status bar / notch at the OVERLAY level, then subtract
   the inset from the modal height — the robust pattern the Tasks detail uses
   (.tasks-modal-overlay.is-fullscreen-overlay, css/05). Padding the header alone proved
   UNRELIABLE on iOS PWA — the top bar still slid under the status bar (Radek 2026-06-30).
   The header keeps its compact 10px; the overlay now owns the notch clearance.
   dvh keeps the history footer reachable while Safari's bottom toolbar is visible. */
@media (max-width: 600px) {
  .audit-detail-overlay { align-items: flex-start; padding-top: var(--safe-top); }
  .audit-modal-detail {
    height: calc(100vh - var(--safe-top));
    max-height: calc(100vh - var(--safe-top));
    height: calc(100dvh - var(--safe-top));
    max-height: calc(100dvh - var(--safe-top));
  }
}

.audit-detail-stats {
  display: grid;
  /* THREE cells (2026-07-05): Score · Passed · Failed, or Score · Failed ·
     Resolved once follow-up tasks exist — the filler Sections cell is gone,
     so the strip breathes on a phone with no squeeze block. */
  grid-template-columns: 1.4fr 1fr 1fr;
  gap: 1px;
  background: var(--border);
  padding: 1px;
}
.audit-stat {
  background: var(--bg-surface);
  /* Compacted 2026-07-05 — the strip now scrolls with the questions, so it reads
     as a tidy summary rather than a billboard. */
  padding: 9px 6px;
  text-align: center;
}
.audit-stat-num {
  font-family: var(--display);
  font-weight: 700;
  font-size: 18px;
  line-height: 1.1;
}
.audit-stat-label {
  font-size: 11px;
  color: var(--text-muted);
  text-transform: uppercase;
  letter-spacing: 0.06em;
  font-weight: 600;
  margin-top: 3px;
}
.audit-stat-tone-success .audit-stat-num { color: var(--rag-pass); }
.audit-stat-tone-ok      .audit-stat-num { color: var(--text-primary); }
.audit-stat-tone-warning .audit-stat-num { color: var(--rag-warn); }
.audit-stat-tone-danger  .audit-stat-num { color: var(--rag-fail); }
.audit-stat-score .audit-stat-num { font-size: 22px; }
/* "Failed" carries the audit size as a muted denominator (2 /5) — how many were
   failed AND how many checks there were, in one cell (Radek 2026-07-05). */
.audit-stat-denom { font-size: 0.6em; color: var(--text-muted); font-weight: 600; margin-left: 1px; }

.audit-detail-tasks-overlay {
  padding: var(--space-3);
  background: color-mix(in srgb, var(--accent) 6%, var(--bg-surface));
  border-bottom: 1px solid var(--border);
}
.audit-detail-tasks-overlay-text {
  font-size: var(--text-sm);
  margin-bottom: 6px;
}
.audit-detail-tasks-bar {
  height: 6px;
  background: var(--bg-elevated);
  border-radius: 3px;
  overflow: hidden;
}
.audit-detail-tasks-bar-fill {
  height: 100%;
  background: var(--accent);
  transition: width 400ms ease-out;
}

.audit-detail-overall-notes {
  padding: var(--space-3);
  background: var(--bg-elevated);
  border-bottom: 1px solid var(--border);
}
.audit-detail-overall-notes-label {
  font-size: 11px;
  font-weight: 600;
  color: var(--text-muted);
  text-transform: uppercase;
  letter-spacing: 0.04em;
  margin-bottom: 4px;
}
.audit-detail-overall-notes-body {
  font-size: var(--text-sm);
  line-height: 1.4;
}

/* The detail body is now the ONLY scroll region: the pinned top bar stays put
   while the summary head (.audit-detail-scrollhead) + questions scroll together
   (Radek 2026-07-05). Padding moves onto .audit-detail-questions so the summary
   strip can stay full-bleed. */
.audit-detail-body { background: var(--bg-elevated); padding: 0; }
/* White summary head that scrolls away with the questions — keeps the app's
   white-chrome → grey-body → white-cards polarity while reclaiming the pinned
   height. Its children own their own padding (submeta / overlay / notes /
   filterbar) or go edge-to-edge (the stat strip). */
.audit-detail-scrollhead {
  background: var(--bg-surface);
  border-bottom: 1px solid var(--border);
}
.audit-detail-questions { padding: var(--space-3); }
.audit-detail-filterbar { padding: 10px var(--space-3) 12px; }
.audit-detail-section { margin-bottom: var(--space-4); }
.audit-detail-section-head {
  font-family: var(--display);
  font-weight: 700;
  font-size: 13px;
  color: var(--text-muted);
  text-transform: uppercase;
  letter-spacing: 0.06em;
  padding: 4px 12px;
  background: color-mix(in srgb, var(--accent) 8%, transparent);
  border-radius: 4px;
  display: inline-block;
  margin-bottom: var(--space-2);
}
.audit-detail-section-body {
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
}

.audit-detail-q-card {
  background: var(--bg-surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  padding: var(--space-3);
}
.audit-detail-q-card.is-failed {
  /* Calm card, ONE red signal (Radek 2026-07-01): no red WASH on the card — the
     body stays neutral like a passed card; the fail reads from a 3px red left
     spine + the red "No" status badge (the two-tier card rule, same as a Tasks
     priority bar). Replaces the old faint-red-tint fill that muddied on dark. */
  border-color: var(--border);
  border-left: 3px solid var(--rag-fail-fill);
}
.audit-detail-q-head {
  display: flex;
  align-items: flex-start;
  gap: 8px;
  margin-bottom: 8px;
}
.audit-detail-q-question {
  flex: 1;
  font-weight: 500;
  font-size: var(--text-base);
  line-height: 1.35;
}
/* Answer result badge — CALM (neutral chip + colour on the ✓/✕ icon). A reviewed
   FAIL reads calm here; the failed-question card + its follow-up task still flag
   it. Matches the Tasks/Call-out detail standard. (Radek 2026-06-29) */
.audit-detail-status-badge {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  padding: 4px 9px;
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  background: var(--bg-elevated);
  color: var(--text-primary);
  font-size: var(--text-xs);
  font-weight: 500;
  flex-shrink: 0;
}
.audit-detail-status-badge svg { width: 12px; height: 12px; }
.audit-status-pass svg    { color: var(--rag-pass); }
.audit-status-fail svg    { color: var(--rag-fail); }
.audit-status-neutral svg { color: var(--text-muted); }

.audit-detail-value {
  font-size: var(--text-sm);
  color: var(--text-muted);
  margin: 6px 0;
}
.audit-detail-value-text {
  background: var(--bg-elevated);
  padding: 8px 12px;
  border-radius: var(--radius-sm);
  color: var(--text-primary);
  line-height: 1.4;
}
.audit-detail-value-na { font-style: italic; }

.audit-detail-photos {
  display: flex;
  gap: 6px;
  flex-wrap: wrap;
  margin-top: 8px;
}
/* Small "Reported" / "After fix" caption that separates the auditor's fail
   evidence from the team's after-fix photos on the answer card. */
.audit-detail-photos-label {
  margin-top: 10px;
  font-size: var(--text-xs);
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--text-muted);
}
/* Card photo thumbs are tappable → the shared lightbox. */
.audit-detail-photos [data-view-photo] { cursor: pointer; }
/* Off-script finding card — a flag glyph (not a number) sits in the round badge to
   mark a spotted finding vs a numbered checklist item. */
.audit-detail-offscript-num svg { width: 15px; height: 15px; }

/* Detail card top chip-row (Radek 2026-06-29): clickable task chip + location +
   department, left-packed, same height. Calm "where + who" context strip; the task
   chip carries the green resolved state (so the old "Resolved by team" pill is gone). */
.audit-detail-chiprow {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 6px;
  margin-bottom: 10px;
}
/* The task chip normally has a top margin for footer use — zero it inside the row. */
.audit-detail-chiprow .audit-linked-task-chip { margin-top: 0; }
/* Single cyan "Action" button (same look as the modal Close button), pushed to the
   bottom-right of the card footer. */
.audit-detail-action-btn { margin-left: auto; }

/* Embedded Tasks detail — the Action button unfolds the REAL Tasks detail inline
   (AngelHubTasks.mountDetail). The .tasks-detail* styles come from css/05 (loaded
   globally), so it looks identical to the Tasks module; this only spaces it from
   the card above and frames it as a distinct working area. */
.audit-detail-task-embed {
  margin-top: 12px;
  padding-top: 4px;
  border-top: 1px dashed var(--border);
}
.audit-detail-task-embed-loading {
  padding: 16px;
  text-align: center;
  color: var(--text-muted);
  font-size: var(--text-sm);
}

.audit-detail-note {
  display: flex;
  align-items: flex-start;
  gap: 6px;
  margin-top: 8px;
  padding: 8px 12px;
  background: var(--bg-elevated);
  border-radius: var(--radius-sm);
  font-size: var(--text-sm);
  color: var(--text-primary);
  line-height: 1.4;
}
.audit-detail-note-icon {
  width: 14px; height: 14px;
  margin-top: 2px;
  flex-shrink: 0;
  color: var(--text-muted);
}

/* Plain, clickable task-ref pill — matches the task-number pill in the Tasks
   detail top banner (.kb-instr-ref-pill): neutral bg-elevated shell, mono ref,
   no icon. The task's progress moved OUT of this chip to a calm status chip in
   the card footer next to the priority (Radek 2026-06-30). */
.audit-linked-task-chip {
  display: inline-flex;
  align-items: center;
  margin-top: 10px;
  padding: 3px 9px;
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  background: var(--bg-elevated);
  font-size: var(--text-xs);
  font-weight: 600;
  letter-spacing: 0.04em;
  cursor: pointer;
  color: var(--text-secondary);
  transition: border-color 120ms, background 120ms;
  font-family: var(--body);
  position: relative;
}
/* §4 touch floor — the same transparent ::before the linked-ASSET chip carries
   (see .audit-asset-chip-clickable below), for the same reason: measured 20.5px
   tall at 375px, under HALF the 44px floor, and this is the control that opens
   the follow-up task. Height, not min-height, and absolute, so the visible chip
   keeps the one app-wide chip size (§4) and still matches the location and
   department chips sitting beside it in the same row.
   Measured difference from the asset chip, and the reason this was measured
   rather than copied: this chip lives in .audit-detail-chiprow, which WRAPS on a
   phone (gap 6px). A centred 44px box therefore reaches ~6px into the top of
   whatever wrapped onto the line below. That is deliberate and safe HERE because
   both neighbours are inert: locChipHtml and deptChipHtml in modules/audits/
   utils.js return a <span> in every branch, with no handler and cursor:auto — so
   the overlap can only ever claim a tap that would otherwise do nothing. Put an
   interactive control in this row and that stops being true. */
.audit-linked-task-chip::before {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  top: 50%;
  transform: translateY(-50%);
  height: 44px;
}
.audit-linked-task-chip:hover { border-color: var(--accent); }
.audit-linked-task-ref { font-family: var(--mono, ui-monospace, SFMono-Regular, monospace); }

/* ═══ SUBMISSION CARDS (history/active/reports) — grid is shared .cl-card-grid ══ */
.audit-sub-card {
  background: var(--bg-surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  padding: var(--space-4);
  cursor: pointer;
  transition: border-color 120ms, transform 120ms;
  display: flex;
  flex-direction: column;
  gap: 8px;
}
.audit-sub-card:hover {
  border-color: color-mix(in srgb, var(--accent) 50%, transparent);
  background: color-mix(in srgb, var(--accent) 6%, var(--bg-surface));
  transform: translateY(-1px);
}
.audit-sub-tone-success { border-left: 4px solid var(--rag-pass-fill); }
.audit-sub-tone-ok      { border-left: 4px solid var(--text-muted); }
.audit-sub-tone-warning { border-left: 4px solid var(--rag-warn-fill); }
.audit-sub-tone-danger  { border-left: 4px solid var(--rag-fail-fill); }

.audit-sub-card-head {
  display: flex;
  align-items: center;
  gap: 10px;
}
.audit-sub-score {
  font-family: var(--display);
  font-weight: 700;
  font-size: 22px;
  padding: 4px 10px;
  border-radius: var(--radius-sm);
  background: var(--bg-elevated);
  color: var(--text-primary);
  flex-shrink: 0;
}
/* RAG tone on the score pill — the loud, act-now headline metric. These
   rules never existed, so the badge was always neutral regardless of score. */
.audit-sub-score.audit-stat-tone-success { color: var(--rag-pass); background: color-mix(in srgb, var(--rag-pass-fill) 14%, transparent); }
.audit-sub-score.audit-stat-tone-warning { color: var(--rag-warn); background: color-mix(in srgb, var(--rag-warn-fill) 16%, transparent); }
.audit-sub-score.audit-stat-tone-danger  { color: var(--rag-fail); background: color-mix(in srgb, var(--rag-fail-fill) 14%, transparent); }
.audit-sub-score.audit-stat-tone-ok      { color: var(--text-secondary); }
.audit-sub-card-titles { flex: 1; min-width: 0; }
.audit-sub-ref {
  font-family: var(--mono, ui-monospace, SFMono-Regular, monospace);
  font-size: var(--text-xs);
  color: var(--text-muted);
  letter-spacing: 0.05em;
}
.audit-sub-title {
  font-family: var(--display);
  font-weight: 700;
  font-size: 15px;
  line-height: 1.2;
  margin-top: 2px;
}
.audit-sub-meta {
  display: flex;
  gap: 8px;
  font-size: var(--text-xs);
  color: var(--text-muted);
}
.audit-sub-meta-item:not(:last-child)::after {
  content: '·';
  margin-left: 8px;
  color: var(--border);
}
.audit-sub-stats {
  display: flex;
  flex-wrap: wrap;
  gap: 4px;
}
.audit-sub-stat-chip {
  display: inline-block;
  padding: 4px 9px;
  font-size: var(--text-xs);
  font-weight: 600;
  border-radius: var(--radius-sm);
  background: var(--bg-elevated);
  border: 1px solid var(--border);
  color: var(--text-muted);
}
.audit-sub-stat-pass {
  color: var(--rag-pass);
  background: color-mix(in srgb, var(--rag-pass-fill) 10%, transparent);
  border-color: color-mix(in srgb, var(--rag-pass-fill) 35%, transparent);
}
.audit-sub-stat-fail {
  color: var(--rag-fail);
  background: color-mix(in srgb, var(--rag-fail-fill) 10%, transparent);
  border-color: color-mix(in srgb, var(--rag-fail-fill) 35%, transparent);
}
.audit-sub-stat-tasks {
  color: var(--accent);
  background: var(--accent-soft);
  border-color: color-mix(in srgb, var(--accent) 35%, transparent);
}

/* ── To-do (scheduled audit) cards — RAG "due" state ──────────────────── */
/* Reuse the .audit-sub-card base; add a coloured left edge + a loud due chip,
   following the two-tier card rule (calm context, loud act-now signal). */
.audit-todo-card { cursor: pointer; border-left: 3px solid var(--border-strong); }
.audit-todo-card.audit-due-edge-red     { border-left-color: var(--rag-fail-fill); }
.audit-todo-card.audit-due-edge-amber   { border-left-color: var(--rag-warn-fill); }
.audit-todo-card.audit-due-edge-green   { border-left-color: var(--rag-pass-fill); }
.audit-todo-card.audit-due-edge-neutral { border-left-color: var(--border-strong); }
.audit-todo-icon {
  width: 38px; height: 38px; border-radius: var(--radius-md);
  display: inline-flex; align-items: center; justify-content: center;
  background: var(--bg-elevated); color: var(--text-secondary); flex-shrink: 0;
}
.audit-todo-icon svg { width: 20px; height: 20px; }
.audit-due-ink-red    { color: var(--rag-fail); }
.audit-due-ink-amber  { color: var(--rag-warn); }
.audit-due-ink-green  { color: var(--rag-pass); }
.audit-due-red {
  color: var(--rag-fail);
  background: color-mix(in srgb, var(--rag-fail-fill) 10%, transparent);
  border-color: color-mix(in srgb, var(--rag-fail-fill) 35%, transparent);
}
.audit-due-amber {
  color: var(--rag-warn);
  background: color-mix(in srgb, var(--rag-warn-fill) 10%, transparent);
  border-color: color-mix(in srgb, var(--rag-warn-fill) 35%, transparent);
}
.audit-due-green {
  color: var(--rag-pass);
  background: color-mix(in srgb, var(--rag-pass-fill) 10%, transparent);
  border-color: color-mix(in srgb, var(--rag-pass-fill) 35%, transparent);
}

/* Preview footer flag (read-only runner preview from the builder) */
.audit-preview-flag {
  display: inline-flex; align-items: center;
  font: 600 var(--text-xs) / 1 var(--body);
  color: var(--text-muted);
}

/* (The old @media (prefers-color-scheme: dark) red/green-brightening block was
   removed 2026-07-01 — it keyed off the OS preference, not the app's own
   [data-theme]. The --rag-* tokens now carry the dark-brightened values and
   flip with the app theme, so pass/fail read cleanly in both modes.) */

/* ═══════════════════════════════════════════════════════════════════════
   AUDITS — Phase 2a (multi-zone + asset linkage + recommendations)
   ═══════════════════════════════════════════════════════════════════════ */

/* ─── Template builder: multi-zone toggle + zone-types picker ──── */
.audit-builder-zone-block {
  background: var(--bg-elevated);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  padding: var(--space-3);
  margin-bottom: var(--space-3);
}
.audit-zone-toggle {
  display: flex;
  gap: 12px;
  align-items: flex-start;
  cursor: pointer;
}
.audit-zone-toggle input {
  width: 18px; height: 18px;
  margin-top: 2px;
  accent-color: var(--accent);
  flex-shrink: 0;
}
.audit-zone-toggle-title {
  font-family: var(--display);
  font-weight: 700;
  font-size: 15px;
  margin-bottom: 2px;
}
.audit-zone-toggle-sub {
  font-size: var(--text-xs);
  color: var(--text-muted);
  line-height: 1.4;
}
.audit-zone-config {
  margin-top: var(--space-3);
  padding-top: var(--space-3);
  border-top: 1px dashed var(--border);
}
.audit-form-help {
  font-size: 11px;
  color: var(--text-muted);
  margin-top: 4px;
  line-height: 1.4;
}

.audit-zone-types-picker {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
}
.audit-zone-type-chip {
  padding: 5px 10px;
  border: 1px solid var(--border);
  border-radius: 14px;
  background: var(--bg-surface);
  color: var(--text-muted);
  font-size: var(--text-xs);
  font-weight: 600;
  cursor: pointer;
  transition: all 120ms;
}
.audit-zone-type-chip:hover {
  border-color: var(--accent);
  color: var(--accent);
}
.audit-zone-type-chip.is-on {
  background: var(--accent-soft);
  border-color: var(--accent);
  color: var(--accent);
}

/* Per-question "applies to" chip row */
.audit-q-zones-row { margin-top: 4px; }
.audit-q-zone-chips {
  display: flex;
  flex-wrap: wrap;
  gap: 4px;
}
.audit-q-zone-chip {
  padding: 3px 8px;
  border: 1px solid var(--border);
  border-radius: 10px;
  background: var(--bg-surface);
  color: var(--text-muted);
  font-size: 11px;
  font-weight: 600;
  cursor: pointer;
  transition: all 120ms;
}
.audit-q-zone-chip:hover {
  border-color: var(--accent);
  color: var(--accent);
}
.audit-q-zone-chip.is-on {
  background: var(--accent-soft);
  border-color: var(--accent);
  color: var(--accent);
}

/* ─── Runner: zone setup screen ──────────────────────────────── */
.audit-zone-setup {
  max-width: 560px;
  margin: 0 auto;
}
.audit-zone-setup-head { margin-bottom: var(--space-4); text-align: center; }
.audit-zone-setup-title {
  font-family: var(--display);
  font-weight: 700;
  font-size: 22px;
  margin: 0 0 8px;
}
.audit-zone-setup-sub {
  font-size: var(--text-sm);
  color: var(--text-muted);
  margin: 0;
  line-height: 1.4;
}

.audit-zone-list {
  display: flex;
  flex-direction: column;
  gap: 8px;
  margin-bottom: var(--space-3);
}
.audit-zone-row {
  display: grid;
  grid-template-columns: auto 1fr 36px;
  align-items: center;
  gap: 10px;
  background: var(--bg-surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  padding: 10px 12px;
}
.audit-zone-row-type-chip {
  font-size: 11px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  padding: 4px 10px;
  background: var(--accent-soft);
  color: var(--accent);
  border-radius: 4px;
}
.audit-zone-row-name { font-size: var(--text-base); font-weight: 500; }
.audit-zone-row-del {
  width: 32px; height: 32px;
  border: 0;
  background: transparent;
  color: var(--text-muted);
  border-radius: var(--radius-sm);
  cursor: pointer;
  display: inline-flex; align-items: center; justify-content: center;
}
.audit-zone-row-del:hover {
  background: color-mix(in srgb, #ef4444 10%, transparent);
  color: #ef4444;
}
.audit-zone-row-del svg { width: 16px; height: 16px; }

.audit-zone-add-block {
  margin-top: var(--space-3);
}
.audit-zone-add-types {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
}
.audit-zone-add-btn { padding: 8px 12px; }

/* ─── Runner: zone nav + zone headers ────────────────────────── */
.audit-runner-zone-nav {
  display: flex;
  gap: 6px;
  flex-wrap: wrap;
  padding: var(--space-2) var(--space-3);
  background: var(--bg-surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  margin-bottom: var(--space-3);
  position: sticky;
  top: 0;
  z-index: 5;
}
.audit-runner-zone-nav-chip {
  padding: 4px 10px;
  border: 1px solid var(--border);
  border-radius: 12px;
  background: var(--bg-elevated);
  color: var(--text-primary);
  font-size: 11px;
  font-weight: 600;
  cursor: pointer;
}
.audit-runner-zone-nav-chip:hover {
  background: var(--accent-soft);
  border-color: var(--accent);
  color: var(--accent);
}

.audit-runner-zone-block {
  margin-bottom: var(--space-4);
}
.audit-runner-zone-head {
  display: flex;
  align-items: baseline;
  gap: 10px;
  padding: var(--space-2) 0;
  margin-bottom: var(--space-2);
  border-bottom: 2px solid var(--accent);
}
.audit-runner-zone-head-label {
  font-size: 11px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--accent);
}
.audit-runner-zone-head-name {
  font-family: var(--display);
  font-weight: 700;
  font-size: 18px;
}

/* ─── Failed-only extras: asset link ────────── */
.audit-fail-extras {
  /* No top border/margin — the dashed red divider was removed and spacing is
     owned by the parent's flex gap so it stays consistent (Radek 2026-06-26). */
  margin-top: 0;
  padding-top: 0;
}
.audit-fail-extras-label {
  font-size: 11px;
  font-weight: 600;
  color: var(--text-muted);
  text-transform: uppercase;
  letter-spacing: 0.04em;
  margin-bottom: 6px;
}

/* Collapsible "more" on a fail — Recommendation / asset / how-to-fix tuck behind
   one tap so the essentials (photos, urgency, complete-by) read clean
   (Radek 2026-06-26). Closed = a calm dashed invite; open = a solid header with
   a flipped chevron. */
.audit-fail-more {
  display: flex;
  align-items: center;
  gap: 8px;
  width: 100%;
  margin-top: 10px;
  padding: 8px 10px;
  border: 1px dashed var(--border-strong);
  border-radius: var(--radius-md);
  background: transparent;
  color: var(--text-secondary);
  font: 600 var(--text-xs) / 1.2 var(--body);
  cursor: pointer;
  transition: border-color 120ms, color 120ms;
}
.audit-fail-more:hover { border-color: var(--accent); color: var(--accent); }
.audit-fail-more.is-open { border-style: solid; }
.audit-fail-more-label { flex: 1; text-align: left; }
.audit-fail-more-chev {
  width: 16px;
  height: 16px;
  flex: none;
  transition: transform 160ms ease;
}
.audit-fail-more.is-open .audit-fail-more-chev { transform: rotate(180deg); }
.audit-fail-more-body { margin-top: 10px; }
.audit-fail-more-body[hidden] { display: none; }
/* Surfaced department routing dropdown (Phase 3b) — breathing room before the note
   disclosure below it. */
.audit-dept-surfaced { margin-bottom: 10px; }
/* Exact-spot free-text on a fail — its caption was removed (Radek 2026-06-29), so it
   carries its own breathing room below to keep the same rhythm as the fields around
   it (Location above, How-urgent below). */
.audit-spot-input { margin-bottom: 10px; }
/* (Removed 2026-08-01, with the retired run-time asset picker — see
   audit-form.js: .audit-rec-buttons (its row), .audit-rec-btn + its svg/:hover/
   .is-on states, and .audit-asset-link-btn (the "Link asset" button). At HEAD
   the ONLY markup carrying .audit-rec-btn / .audit-rec-buttons was that asset
   section, so deleting it orphaned them outright.
   .audit-asset-chip below is NOT part of that: it is the audit DETAIL card's
   linked-asset chip and is very much alive.) */
.audit-asset-chip {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 4px 10px;
  border: 1px solid color-mix(in srgb, var(--accent) 45%, transparent);
  background: var(--accent-soft);
  color: var(--accent);
  border-radius: var(--radius-sm);
  font-size: var(--text-xs);
  font-weight: 600;
  font-family: var(--body);
}
.audit-asset-chip svg { width: 12px; height: 12px; }
.audit-asset-chip-clickable {
  cursor: pointer;
  border: 1px solid color-mix(in srgb, var(--accent) 35%, transparent);
  background: var(--accent-soft);
  position: relative;
}
/* §4 touch floor. The chip computes 20-23px tall — a chip is 4px 10px at
   --text-xs by law, and that is half the 44px floor. Grow the HIT area with a
   transparent ::before (the .nb-seen-dismiss / nb-detail2 close pattern) rather
   than min-height, so the VISIBLE chip stays exactly the size every other chip
   in the app is (§4 "one chip size") and still matches the severity badge
   sitting beside it in the same row.
   The box is a CENTRED 44px, never a negative inset: the chip is not one fixed
   height (measured 23px at 375px, 20px at 1280px), so any hardcoded inset is a
   guess that silently drops under the floor the moment padding, --text-xs or
   the viewport changes -- -11px already gave 42px on desktop. Declaring the
   height states the requirement itself, so it cannot rot.
   Vertical only: the box is already far past 44px wide, and a horizontal bleed
   is what would let a chip steal taps from its row neighbour. Base rule, not a
   breakpoint, because this chip stays tappable at every width (§4). Only the
   -clickable variant gets it — the plain chip renders as a <div>, not a
   control. */
.audit-asset-chip-clickable::before {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  top: 50%;
  transform: translateY(-50%);
  height: 44px;
}
.audit-asset-chip-clickable:hover {
  background: color-mix(in srgb, var(--accent) 15%, transparent);
}
/* (Removed: the bespoke audit asset-picker modal styles — audits now use the
   shared AngelHubUI.openAssetPicker. Dead-CSS sweep 2026-06-26.)
   (Removed 2026-08-01: .audit-asset-chip-del — the runner chip's unlink ✕ —
   with the retired run-time asset picker. It had been unreachable since
   2026-06-29 AND was broken: a <span> handler nested inside the chip's own
   <button>, so the delegated handler resolved every tap on it to the parent
   and opened the picker instead of unlinking.) */

/* ─── Detail: per-zone summary cards ─────────────────────────── */
.audit-detail-zones-summary {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
  gap: 8px;
  padding: var(--space-3);
  background: var(--bg-elevated);
  border-bottom: 1px solid var(--border);
}
.audit-detail-zone-card {
  display: flex;
  flex-direction: column;
  gap: 6px;
  padding: 10px 12px;
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  background: var(--bg-surface);
  cursor: pointer;
  text-align: left;
  font-family: var(--body);
  transition: border-color 120ms, transform 120ms;
}
.audit-detail-zone-card:hover {
  border-color: var(--accent);
  transform: translateY(-1px);
}
.audit-detail-zone-card-head {
  display: flex;
  align-items: center;
  gap: 10px;
}
.audit-detail-zone-card-score {
  font-family: var(--display);
  font-weight: 700;
  font-size: 18px;
  padding: 2px 8px;
  background: var(--bg-elevated);
  border-radius: 4px;
  flex-shrink: 0;
}
.audit-detail-zone-card.audit-stat-tone-success .audit-detail-zone-card-score { color: var(--rag-pass); }
.audit-detail-zone-card.audit-stat-tone-warning .audit-detail-zone-card-score { color: var(--rag-warn); }
.audit-detail-zone-card.audit-stat-tone-danger  .audit-detail-zone-card-score { color: var(--rag-fail); }
.audit-detail-zone-card-name {
  font-family: var(--display);
  font-weight: 700;
  font-size: 14px;
  line-height: 1.2;
}
.audit-detail-zone-card-type {
  font-size: 11px;
  color: var(--text-muted);
  text-transform: uppercase;
  letter-spacing: 0.05em;
  font-weight: 600;
}
.audit-detail-zone-card-stats {
  display: flex;
  gap: 4px;
  flex-wrap: wrap;
}

/* ─── Detail: zone headers ───────────────────────────────────── */
.audit-detail-zone-block {
  margin-bottom: var(--space-4);
}
.audit-detail-zone-head {
  display: flex;
  align-items: baseline;
  gap: 10px;
  padding: var(--space-2) 0;
  margin-bottom: var(--space-2);
  border-bottom: 2px solid var(--accent);
}
.audit-detail-zone-head-type {
  font-size: 11px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--accent);
}
.audit-detail-zone-head-name {
  font-family: var(--display);
  font-weight: 700;
  font-size: 18px;
}

/* ─── Detail: q-card footer (rec + asset + task chips) ─────── */
.audit-detail-q-footer {
  display: flex;
  gap: 6px;
  flex-wrap: wrap;
  margin-top: 10px;
  padding-top: 10px;
  border-top: 1px dashed var(--border);
  align-items: center;
}

/* ─── Detail: per-dept status chips ──────────────────────────── */
.audit-detail-dept-chips {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
}
.audit-detail-dept-chip {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 3px 9px;
  background: var(--bg-surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  font-size: var(--text-xs);
  font-weight: 600;
  font-family: var(--body);
  color: var(--text-primary);
  cursor: pointer;
  transition: border-color 120ms, background 120ms;
}
.audit-detail-dept-chip:hover { border-color: var(--accent); }
.audit-detail-dept-chip-dot { width: 7px; height: 7px; border-radius: 50%; flex-shrink: 0; }
.audit-detail-dept-chip-name { font-weight: 600; }
.audit-detail-dept-chip-count {
  font-family: var(--mono, ui-monospace, monospace);
  color: var(--text-muted);
}

/* (The 5-up "has-resolved" grid variant retired 2026-07-05 — the strip is a
   fixed 3-up: the Resolved tile REPLACES Passed when follow-up tasks exist.) */

/* ═══ "By dept" collapsible department cards (Radek 2026-06-30) ═══════════════════
   One card per team: tap the header (a progress bar + %) to reveal only that
   department's follow-ups. Complete teams read green so you can scan who's done. */
.audit-detail-deptcard {
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  background: var(--bg-surface);
  margin-bottom: 10px;
  overflow: hidden;
}
.audit-detail-deptcard-head {
  display: flex;
  flex-direction: column;
  gap: 8px;
  width: 100%;
  padding: 12px var(--space-3);
  background: transparent;
  border: 0;
  cursor: pointer;
  text-align: left;
  font-family: var(--body);
  color: var(--text-primary);
}
.audit-detail-deptcard-top {
  display: flex;
  align-items: center;
  gap: 8px;
}
.audit-detail-deptcard-dot {
  width: 9px; height: 9px; border-radius: 50%;
  flex-shrink: 0;
}
.audit-detail-deptcard-name {
  font-weight: 700;
  font-size: var(--text-base);
  flex: 1;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.audit-detail-deptcard-meta {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  flex-shrink: 0;
}
.audit-detail-deptcard-count {
  font-family: var(--mono, ui-monospace, monospace);
  font-size: var(--text-xs);
  color: var(--text-muted);
}
.audit-detail-deptcard-pct {
  font-weight: 700;
  font-size: var(--text-sm);
  min-width: 38px;
  text-align: right;
}
.audit-detail-deptcard-chev {
  display: inline-flex;
  color: var(--text-muted);
  transition: transform 180ms ease;
  flex-shrink: 0;
}
.audit-detail-deptcard-chev svg { width: 18px; height: 18px; }
.audit-detail-deptcard.is-open .audit-detail-deptcard-chev { transform: rotate(180deg); }
.audit-detail-deptcard-bar {
  display: block;
  height: 6px;
  background: var(--bg-elevated);
  border-radius: 3px;
  overflow: hidden;
}
.audit-detail-deptcard-bar-fill {
  display: block;
  height: 100%;
  background: var(--accent);
  transition: width 400ms ease-out;
}
/* Body collapsed by default; revealed when the card carries .is-open. */
.audit-detail-deptcard-body {
  display: none;
  padding: 0 var(--space-3) 12px;
  border-top: 1px solid var(--border);
}
.audit-detail-deptcard.is-open .audit-detail-deptcard-body { display: block; }
/* The first answer/off-script card inside sits flush under the divider. */
.audit-detail-deptcard-body > .audit-detail-q-card:first-child { margin-top: 12px; }

/* ─── Deadline (Complete by) buttons ───────────────────────────── */
/* Sits between the severity row and the recommendation row. Same
   visual weight as severity buttons but neutral colour (severity
   carries the urgency colour; deadline is informational). The
   custom date input slots into the same flex row so the auditor
   can pick a date without leaving the keyboard. */
.audit-due-buttons {
  display: flex;
  gap: 6px;
  flex-wrap: wrap;
  margin-bottom: 10px;
  align-items: center;
}
.audit-due-btn {
  padding: 6px 10px;
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  background: var(--bg-elevated);
  color: var(--text-secondary);
  font-family: var(--body);
  font-size: var(--text-xs);
  font-weight: 600;
  cursor: pointer;
  transition: all 120ms;
}
.audit-due-btn:hover {
  background: var(--bg-hover);
  border-color: var(--border-strong);
  color: var(--text-primary);
}
.audit-due-btn.is-on {
  background: var(--accent-soft);
  border-color: color-mix(in srgb, var(--accent) 50%, transparent);
  color: var(--accent);
}
.audit-due-date-input {
  padding: 5px 10px;
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  background: var(--bg-surface);
  color: var(--text-primary);
  font-family: var(--body);
  /* ≥16px so iOS Safari never auto-zooms on focus (§4 mobile law). */
  font-size: var(--text-base);
  font-weight: 600;
  cursor: pointer;
  color-scheme: dark light;
}
.audit-due-date-input:hover { border-color: var(--accent); }
.audit-due-date-input:focus {
  outline: none;
  border-color: var(--accent);
  background: var(--bg-elevated);
}

/* "Complete by" inline hint — small relative-time after the label */
.audit-fail-extras-hint {
  font-weight: 600;
  color: var(--accent);
  text-transform: none;
  letter-spacing: 0;
  margin-left: 6px;
  font-size: var(--text-xs);
}

/* (Removed 2026-08-01, with the retired run-time asset picker — see
   audit-form.js: .audit-asset-chip-linked and its ✕ overrides, plus the SECOND
   .audit-asset-link-btn rule that lived here — the ghost/dashed override, far
   from the base rule it modified. Worth remembering: deleting the base rule
   left this one behind, and a grep for the selector is what found it.) */

/* ─── Severity buttons (auditor-set at audit time) ───────────────── */
.audit-sev-buttons {
  display: flex;
  gap: 6px;
  flex-wrap: wrap;
  margin-bottom: 10px;
}
/* Severity chips — one size with the Complete-by chips
   (Radek 2026-06-26: "How urgent" was much bigger than the others). Dropped the
   flex:1 stretch, the uppercase + 700 weight, and the chunky 8px padding so all
   three fail-panel chip rows share the same 6px 10px / 600 text-xs shape. */
.audit-sev-btn {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 6px 10px;
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  background: var(--bg-elevated);
  color: var(--text-secondary);
  font-family: var(--body);
  font-size: var(--text-xs);
  font-weight: 600;
  cursor: pointer;
  transition: all 120ms;
}
.audit-sev-btn:hover {
  background: color-mix(in srgb, var(--sev-colour, var(--accent)) 8%, transparent);
  border-color: var(--sev-colour, var(--accent));
  color: var(--sev-colour, var(--accent));
}
.audit-sev-btn.is-on {
  background: color-mix(in srgb, var(--sev-colour, var(--accent)) 18%, transparent);
  border-color: var(--sev-colour, var(--accent));
  color: var(--sev-colour, var(--accent));
}

/* Detail view: severity badge — calm context chip (Radek 2026-06-29). The
   severity colour reads from a small leading dot only; the chip shell itself
   is neutral at the shared chip size, so it never competes with the red "No". */
.audit-detail-sev-badge {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 4px 9px;
  border: 1px solid var(--border);
  background: var(--bg-elevated);
  color: var(--text-secondary);
  border-radius: var(--radius-sm);
  font-size: var(--text-xs);
  font-weight: 600;
  letter-spacing: 0.02em;
}
.audit-detail-sev-badge::before {
  content: '';
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: var(--sev-colour, var(--text-muted));
  flex-shrink: 0;
}

/* ═══════════════════════════════════════════════════════════════════════
   AUDITS — Phase 2b (question packs + property tree)
   ═══════════════════════════════════════════════════════════════════════ */

/* ─── Builder: question pack cards ──────────────────────────────── */
.audit-pack-list {
  display: flex;
  flex-direction: column;
  gap: 8px;
  margin-bottom: var(--space-3);
}
.audit-pack-card {
  background: var(--bg-surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  overflow: hidden;
  transition: border-color 120ms;
}
.audit-pack-card.is-open {
  border-color: color-mix(in srgb, var(--pack-colour, var(--accent)) 50%, var(--border));
  box-shadow: 0 0 0 1px color-mix(in srgb, var(--pack-colour, var(--accent)) 30%, transparent);
}
.audit-pack-card-head {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 10px var(--space-3);
  cursor: pointer;
  background: var(--bg-elevated);
}
.audit-pack-card-head:hover { background: var(--bg-hover); }
.audit-pack-card-swatch {
  width: 8px;
  height: 32px;
  border-radius: 4px;
  flex-shrink: 0;
}
.audit-pack-card-text { flex: 1; min-width: 0; }
.audit-pack-card-name {
  font-family: var(--display);
  font-weight: 700;
  font-size: 14px;
  line-height: 1.2;
}
.audit-pack-card-meta {
  font-size: var(--text-xs);
  color: var(--text-muted);
  margin-top: 2px;
}
.audit-pack-card-actions {
  display: flex;
  gap: 4px;
  align-items: center;
}
.audit-pack-card-chev {
  display: inline-flex;
  align-items: center;
  width: 24px;
  color: var(--text-muted);
}
.audit-pack-card-chev svg { width: 14px; height: 14px; }
.audit-pack-card-body {
  padding: var(--space-3);
  border-top: 1px solid var(--border);
}

/* ─── Builder: zone group cards ─────────────────────────────────── */
.audit-group-list {
  display: flex;
  flex-direction: column;
  gap: 8px;
  margin-bottom: var(--space-3);
}
.audit-group-card {
  background: var(--bg-surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  overflow: hidden;
}
.audit-group-card.is-open {
  border-color: color-mix(in srgb, var(--accent) 45%, var(--border));
}
.audit-group-card-head {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 10px var(--space-3);
  cursor: pointer;
  background: var(--bg-elevated);
}
.audit-group-card-head:hover { background: var(--bg-hover); }
.audit-group-card-icon {
  font-size: 18px;
  flex-shrink: 0;
}
.audit-group-card-text { flex: 1; min-width: 0; }
.audit-group-card-name {
  font-family: var(--display);
  font-weight: 700;
  font-size: 14px;
}
.audit-group-card-meta {
  font-size: var(--text-xs);
  color: var(--text-muted);
  margin-top: 2px;
}
.audit-group-card-body {
  padding: var(--space-3);
  border-top: 1px solid var(--border);
}

/* ─── Builder: location rows ─────────────────────────────────────── */
.audit-loc-list {
  display: flex;
  flex-direction: column;
  gap: 6px;
  margin-bottom: var(--space-2);
}
.audit-loc-row {
  display: grid;
  grid-template-columns: 1.5fr 1fr 1.5fr auto;
  gap: 6px;
  align-items: center;
  padding: 6px 8px;
  background: var(--bg-elevated);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
}
.audit-loc-row-actions {
  display: flex;
  gap: 4px;
}
.audit-loc-name, .audit-loc-type, .audit-loc-pack {
  font-size: var(--text-sm);
  padding: 6px 8px;
}
@media (max-width: 700px) {
  .audit-loc-row {
    grid-template-columns: 1fr auto;
    grid-template-rows: auto auto auto;
  }
  .audit-loc-name { grid-column: 1 / 2; grid-row: 1; }
  .audit-loc-row-actions { grid-column: 2 / 3; grid-row: 1; }
  .audit-loc-type { grid-column: 1 / -1; grid-row: 2; }
  .audit-loc-pack { grid-column: 1 / -1; grid-row: 3; }
}

/* ─── Runner: template-defined zone selector (grouped checklist) ─── */
.audit-zone-group-section {
  background: var(--bg-surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  margin-bottom: var(--space-3);
  overflow: hidden;
}
.audit-zone-group-section-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  padding: 10px var(--space-3);
  background: var(--bg-elevated);
  border-bottom: 1px solid var(--border);
}
.audit-zone-group-section-title {
  font-family: var(--display);
  font-weight: 700;
  font-size: 14px;
}
.audit-zone-group-section-actions {
  display: flex;
  align-items: center;
  gap: 10px;
}
.audit-zone-group-section-count {
  font-size: 11px;
  color: var(--text-muted);
  font-weight: 600;
}
.audit-zone-group-select-all {
  font-size: var(--text-xs);
  font-weight: 600;
  color: var(--accent);
  background: transparent;
  border: 0;
  cursor: pointer;
  padding: 4px 8px;
  border-radius: var(--radius-sm);
}
.audit-zone-group-select-all:hover { background: var(--accent-soft); }
.audit-zone-group-section-body {
  display: flex;
  flex-direction: column;
}

.audit-zone-tpl-row {
  display: grid;
  grid-template-columns: 24px auto 1fr;
  gap: 10px;
  align-items: center;
  padding: 10px var(--space-3);
  cursor: pointer;
  border-bottom: 1px solid var(--border);
  transition: background 120ms;
}
.audit-zone-tpl-row:last-child { border-bottom: 0; }
.audit-zone-tpl-row:hover { background: var(--bg-elevated); }
.audit-zone-tpl-row.is-unchecked {
  opacity: 0.55;
}
.audit-zone-tpl-row input[type="checkbox"] {
  width: 18px;
  height: 18px;
  accent-color: var(--accent);
}
.audit-zone-tpl-row-type-chip {
  font-size: 10px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  padding: 3px 8px;
  background: var(--accent-soft);
  color: var(--accent);
  border-radius: 4px;
  flex-shrink: 0;
}
.audit-zone-tpl-row-name {
  font-weight: 500;
  font-size: var(--text-base);
}

.audit-zone-adhoc-section {
  margin-top: var(--space-3);
  padding: var(--space-3);
  background: var(--bg-elevated);
  border: 1px dashed var(--border);
  border-radius: var(--radius-md);
}
.audit-zone-row-pack {
  font-size: var(--text-sm);
  padding: 4px 8px;
}

/* Make ad-hoc row 4-column to accommodate pack picker */
.audit-zone-row {
  grid-template-columns: auto 1fr 1fr 36px;
}
@media (max-width: 600px) {
  .audit-zone-row {
    grid-template-columns: 1fr 36px;
    grid-template-rows: auto auto;
  }
  .audit-zone-row-type-chip { grid-column: 1 / -1; grid-row: 1; }
  .audit-zone-row-name      { grid-column: 1 / 2;  grid-row: 2; }
  .audit-zone-row-pack      { grid-column: 1 / -1; grid-row: 3; }
  .audit-zone-row-del       { grid-column: 2 / 3;  grid-row: 2; }
}

/* ─── Runner: zone header with pack name ─────────────────────────── */
.audit-runner-zone-head {
  display: flex;
  align-items: baseline;
  gap: 10px;
}
.audit-runner-zone-head-text { flex: 1; min-width: 0; }
.audit-runner-zone-head-pack {
  font-size: 11px;
  font-weight: 600;
  color: var(--text-muted);
  padding: 3px 8px;
  background: var(--bg-elevated);
  border: 1px solid var(--border);
  border-radius: 12px;
  white-space: nowrap;
  flex-shrink: 0;
}

/* ─── Detail: grouped tree summary ───────────────────────────────── */
.audit-detail-tree-summary {
  background: var(--bg-elevated);
  border-bottom: 1px solid var(--border);
  padding: var(--space-3);
}
.audit-detail-tree-group { margin-bottom: var(--space-3); }
.audit-detail-tree-group:last-child { margin-bottom: 0; }
.audit-detail-tree-group-head {
  font-family: var(--display);
  font-weight: 700;
  font-size: 13px;
  color: var(--text-muted);
  text-transform: uppercase;
  letter-spacing: 0.06em;
  padding: 4px 12px;
  background: color-mix(in srgb, var(--accent) 8%, transparent);
  border-radius: 4px;
  display: inline-block;
  margin-bottom: 8px;
}
.audit-detail-tree-group .audit-detail-zones-summary {
  padding: 0;
  background: transparent;
  border: 0;
}

/* ═══════════════════════════════════════════════════════════════════════
   AUDITS — Phase 2e (inline follow-up update panel)
   ═══════════════════════════════════════════════════════════════════════ */

/* Resolved overlay — pinned to the top-right of a failed item card
   once its linked task transitions to completed. Audit answer stays
   "Fail" in the immutable record, but visually the card is marked
   green so the auditor sees the work was done. */
.audit-detail-q-card.is-task-resolved {
  /* Same treatment, green: no green wash — a green left spine + the "Resolved"
     pill carry it. Keeps fail + resolved cards visually neutral (Radek 2026-07-01). */
  border-color: var(--border);
  border-left: 3px solid var(--rag-pass-fill);
}
.audit-detail-resolved-overlay {
  /* Sits as a pill ABOVE the question head (normal flow) — it used to be an absolute
     top-right corner overlay that collided with the answer badge (Radek 2026-06-29).
     Now both the "Resolved by team" state and the original answer are visible. */
  display: inline-flex;
  align-items: center;
  gap: 4px;
  margin-bottom: 8px;
  padding: 4px 9px;
  background: color-mix(in srgb, var(--rag-pass-fill) 14%, transparent);
  color: var(--rag-pass);
  border: 1px solid color-mix(in srgb, var(--rag-pass-fill) 45%, transparent);
  border-radius: var(--radius-sm);
  font-size: var(--text-xs);
  font-weight: 700;
  letter-spacing: 0.04em;
  text-transform: uppercase;
}
.audit-detail-resolved-overlay svg { width: 12px; height: 12px; }
.audit-detail-q-card { position: relative; }

/* The "Update" button on the linked-task chip row */
.audit-followup-update-btn {
  padding: 4px 10px;
  font-size: var(--text-xs);
}

/* Inline panel — expands below the question card */
.audit-followup-panel {
  margin-top: 12px;
  padding: var(--space-3);
  background: var(--bg-elevated);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
}
.audit-followup-panel-head {
  display: flex;
  align-items: center;
  gap: var(--space-2);
}
.audit-followup-panel-title {
  font-family: var(--display);
  font-weight: 600;
  font-size: var(--text-md);
  flex: 1;
  color: var(--text-primary);
}
.audit-followup-section {
  display: flex;
  flex-direction: column;
  gap: 6px;
}
.audit-followup-label {
  font-size: var(--text-sm);
  font-weight: 600;
  color: var(--text-secondary);
}
.audit-followup-label-hint {
  font-weight: 400;
  color: var(--text-muted);
  font-size: var(--text-xs);
}

/* Status pills row */
.audit-followup-status-pills {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
}
.audit-followup-status-pill {
  padding: 6px 12px;
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  background: var(--bg-surface);
  color: var(--text-secondary);
  font-family: var(--body);
  font-size: var(--text-xs);
  font-weight: 600;
  cursor: pointer;
  transition: all 120ms;
}
.audit-followup-status-pill:hover {
  border-color: var(--pill-colour, var(--accent));
  color: var(--pill-colour, var(--accent));
}
.audit-followup-status-pill.is-on {
  background: color-mix(in srgb, var(--pill-colour, var(--accent)) 14%, transparent);
  border-color: var(--pill-colour, var(--accent));
  color: var(--pill-colour, var(--accent));
}

/* Thread of recent comments + status changes */
.audit-followup-thread {
  display: flex;
  flex-direction: column;
  gap: 8px;
  padding: var(--space-2) var(--space-3);
  background: var(--bg-surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  max-height: 240px;
  overflow-y: auto;
}
.audit-followup-thread-loading,
.audit-followup-thread-empty {
  text-align: center;
  color: var(--text-muted);
  font-size: var(--text-xs);
  padding: 8px;
  font-style: italic;
}
.audit-followup-thread-item {
  padding: 6px 0;
  border-bottom: 1px dashed var(--border);
}
.audit-followup-thread-item:last-child { border-bottom: 0; padding-bottom: 0; }
.audit-followup-thread-item.is-status {
  background: var(--bg-elevated);
  padding: 4px 8px;
  margin: 0 -8px;
  border-radius: var(--radius-sm);
  border: 0;
}
.audit-followup-thread-meta {
  display: flex;
  gap: 8px;
  align-items: center;
  font-size: var(--text-xs);
  margin-bottom: 2px;
}
.audit-followup-thread-meta strong { color: var(--text-primary); }
.audit-followup-thread-time { color: var(--text-muted); }
.audit-followup-thread-kind {
  margin-left: auto;
  padding: 1px 6px;
  font-size: 10px;
  font-weight: 700;
  letter-spacing: 0.06em;
  color: var(--text-muted);
  background: var(--bg-elevated);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  text-transform: uppercase;
}
.audit-followup-thread-text {
  font-size: var(--text-sm);
  color: var(--text-primary);
  line-height: 1.4;
}

.audit-followup-foot {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  padding-top: var(--space-2);
  border-top: 1px solid var(--border);
}

/* ─── Phase 2d: Active follow-up cards ──────────────────────────── */
/* Finalised audits with open corrective-action tasks land in the
   Active tab. Accent-tinted bar distinguishes from Drafts (amber)
   and History (no bar). */
.audit-sub-card-followup {
  border-left: 4px solid var(--accent);
}
.audit-sub-card-followup:hover {
  border-color: color-mix(in srgb, var(--accent) 50%, transparent);
  background: color-mix(in srgb, var(--accent) 6%, var(--bg-surface));
}
.audit-followup-progress {
  display: flex;
  align-items: center;
  gap: 10px;
  margin-top: 4px;
}
.audit-followup-progress-bar {
  flex: 1;
  height: 6px;
  background: var(--bg-elevated);
  border-radius: 3px;
  overflow: hidden;
}
.audit-followup-progress-fill {
  height: 100%;
  background: var(--accent);
  transition: width 400ms ease-out;
}
.audit-followup-progress-text {
  font-size: var(--text-xs);
  font-weight: 600;
  color: var(--text-muted);
  white-space: nowrap;
}
.audit-sub-stat-followup {
  color: var(--accent);
  background: var(--accent-soft);
  border-color: color-mix(in srgb, var(--accent) 45%, transparent);
}

/* ─── Phase 2d: Draft audits ─────────────────────────────────────── */
.audit-sub-card-draft {
  border-left: 4px solid #f59e0b;
}
.audit-sub-card-draft:hover {
  border-color: color-mix(in srgb, #f59e0b 50%, transparent);
  background: color-mix(in srgb, #f59e0b 6%, var(--bg-surface));
}
.audit-draft-tag {
  display: inline-block;
  padding: 1px 6px;
  font-size: 10px;
  font-weight: 700;
  letter-spacing: 0.06em;
  background: color-mix(in srgb, #f59e0b 14%, transparent);
  color: #f59e0b;
  border: 1px solid color-mix(in srgb, #f59e0b 35%, transparent);
  border-radius: var(--radius-sm);
}
.audit-draft-progress {
  display: flex;
  align-items: center;
  gap: 10px;
  margin-top: 4px;
}
.audit-draft-progress-bar {
  flex: 1;
  height: 6px;
  background: var(--bg-elevated);
  border-radius: 3px;
  overflow: hidden;
}
.audit-draft-progress-fill {
  height: 100%;
  background: #f59e0b;
  transition: width 400ms ease-out;
}
.audit-draft-progress-text {
  font-size: var(--text-xs);
  font-weight: 600;
  color: var(--text-muted);
  white-space: nowrap;
}

/* "Save progress" button — softer than primary so the
   primary "Submit" CTA stays the visual anchor */
.audit-btn-save-draft {
  border-color: color-mix(in srgb, #f59e0b 45%, var(--border));
  color: #f59e0b;
  background: color-mix(in srgb, #f59e0b 6%, var(--bg-elevated));
}
.audit-btn-save-draft:hover {
  background: color-mix(in srgb, #f59e0b 12%, var(--bg-elevated));
  color: #f59e0b;
  border-color: #f59e0b;
}
/* NO mobile shrink here. This used to drop the save control to --text-xs and
   8px/12px padding at ≤560px — making the hardest-to-hit button on the smallest
   screens smaller still, and pushing it under the 44px floor. The footer now
   WRAPS instead (.audit-runner-foot, in the RUNNER block above), so the label
   no longer has to be starved to fit. */

/* ─── Builder: inline validation banner ──────────────────────────── */
/* Pinned right above the modal footer so the user CAN'T miss errors.
   Toast is too easy to overlook — this banner replaces blind submits. */
.audit-builder-validation {
  background: color-mix(in srgb, #ef4444 12%, var(--bg-surface));
  border-top: 1px solid color-mix(in srgb, #ef4444 35%, var(--border));
  color: #dc2626;
  padding: 10px var(--space-3);
  font-size: var(--text-sm);
  font-weight: 600;
  line-height: 1.4;
}
.audit-builder-validation-flash {
  animation: audit-validation-flash 600ms ease-out;
}
@keyframes audit-validation-flash {
  0%   { background: color-mix(in srgb, #ef4444 30%, var(--bg-surface)); }
  100% { background: color-mix(in srgb, #ef4444 12%, var(--bg-surface)); }
}

/* ═══════════════════════════════════════════════════════════════════════
   ORDERS MODULE — Phase 1
   ─────────────────────────────────────────────────────────────────────
   Canonical Angel Hub patterns throughout:
   - var(--bg-surface) cards, var(--bg-elevated) inputs
   - var(--radius-md) inputs/buttons, var(--radius-sm) chips
   - var(--accent) for primary action, soft+colour-mix for chips
   - var(--body) DM Sans family
   ═══════════════════════════════════════════════════════════════════════ */

/* ─── Page shell ─────────────────────────────────────────────────── */
.order-page {
  padding: 0 var(--space-4) var(--space-6);
  max-width: 1280px;
  margin: 0 auto;
}
/* Orders module tabs — adopt canonical segmented look. Stretched
   to fill the page width — matches Audits + Callouts detail. */
.order-tabs {
  display: flex;
  align-items: center;
  gap: 2px;
  padding: 3px;
  margin: var(--space-3) 0 var(--space-4);
  background: var(--bg-elevated);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
}
.order-tab {
  flex: 1;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 6px;
  padding: 8px 14px;
  background: transparent;
  border: 0;
  border-radius: var(--radius-sm);
  color: var(--text-secondary);
  font-family: var(--body);
  font-size: var(--text-sm);
  font-weight: 600;
  cursor: pointer;
  transition: background 0.15s, color 0.15s;
  white-space: nowrap;
}
.order-tab svg { width: 14px; height: 14px; }
.order-tab:hover { color: var(--text-primary); }
.order-tab.is-on {
  background: var(--accent);
  color: var(--bg-base);
}

.order-tab-body { min-height: 200px; }
.order-loading {
  text-align: center;
  color: var(--text-muted);
  padding: var(--space-6);
  font-size: var(--text-sm);
}

/* Empty states */
.order-empty {
  display: flex;
  flex-direction: column;
  align-items: center;
  padding: 56px 24px;
  text-align: center;
  background: var(--bg-surface);
  border: 1px dashed var(--border);
  border-radius: var(--radius-md);
}
.order-empty-icon {
  width: 56px; height: 56px;
  border-radius: 50%;
  background: var(--accent-soft);
  display: grid; place-items: center;
  color: var(--accent);
  margin-bottom: var(--space-3);
}
.order-empty-icon svg { width: 28px; height: 28px; }
.order-empty-title {
  font-family: var(--display);
  font-size: 20px;
  font-weight: 700;
  margin-bottom: 6px;
}
.order-empty-sub {
  font-size: var(--text-sm);
  color: var(--text-muted);
  max-width: 480px;
  margin-bottom: var(--space-4);
  line-height: 1.5;
}
.order-empty-inline {
  text-align: center;
  color: var(--text-muted);
  padding: var(--space-4);
  font-size: var(--text-sm);
  font-style: italic;
}

/* ─── KPI strip ─────────────────────────────────────────────────── */
.order-kpi-strip {
  margin-top: 0;
  margin-bottom: var(--space-3);
}
.order-tab-body > .hscroll-fade {
  margin-bottom: var(--space-3);
}
.order-kpi-card {
  font-family: var(--body);
  text-align: left;
  cursor: default;
}
button.order-kpi-card { cursor: pointer; }
.order-kpi-amber .dashboard-stat-value,
.order-kpi-amber .dashboard-stat-icon { color: var(--warning); }
.order-kpi-danger .dashboard-stat-value,
.order-kpi-danger .dashboard-stat-icon { color: var(--danger); }

/* ─── Filter chips ──────────────────────────────────────────────── */
.order-filter-chips {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  margin-bottom: var(--space-3);
}
.order-filter-chip {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 6px 12px;
  background: var(--bg-elevated);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  font-family: var(--body);
  font-size: var(--text-xs);
  font-weight: 600;
  color: var(--text-secondary);
  cursor: pointer;
  transition: all 120ms;
}
.order-filter-chip:hover {
  background: var(--bg-hover);
  color: var(--text-primary);
}
.order-filter-chip.is-on {
  background: var(--accent-soft);
  border-color: color-mix(in srgb, var(--accent) 45%, transparent);
  color: var(--accent);
}
.order-filter-chip-count {
  font-size: 10px;
  padding: 1px 6px;
  background: var(--bg-surface);
  border-radius: var(--radius-sm);
  color: inherit;
}

/* ─── Order cards ───────────────────────────────────────────────── */
.order-card-list {
  display: flex;
  flex-direction: column;
  gap: 10px;
}
/* History month folders reuse the Tasks .tasks-month-* group/header/chevron/
   label/count classes (the Budgets + Projects drill-in convention — see the
   budget-detail-months note below). Inter-group spacing comes from the shared
   `.tasks-month-group + .tasks-month-group` margin and each group's own flex
   gap (header ↔ card list); the wrapper adds none of its own. The only
   orders-specific addition is the per-folder realised-spend total (closed
   orders, inc VAT) sitting after the count pill in the header. */
.order-month-groups {
  display: flex;
  flex-direction: column;
}
.order-month-total {
  font: 600 var(--text-sm) / 1 var(--body);
  color: var(--text-secondary);
  white-space: nowrap;
  font-variant-numeric: tabular-nums;
}
.order-card {
  display: flex;
  align-items: stretch;
  width: 100%;
  background: var(--bg-surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  padding: 0;
  cursor: pointer;
  text-align: left;
  overflow: hidden;
  transition: background 0.16s, border-color 0.16s, transform 0.12s, box-shadow 0.16s;
}
.order-card:hover {
  background: var(--bg-hover);
  border-color: var(--border-strong);
  transform: translateY(-1px);
  box-shadow: 0 4px 14px color-mix(in srgb, var(--accent) 8%, transparent);
}
.order-card:active { transform: translateY(0); box-shadow: none; }

.order-card-bar {
  width: 5px;
  flex-shrink: 0;
  background: var(--text-muted);
}
.order-card-tone-success .order-card-bar { background: #22c55e; }
.order-card-tone-accent  .order-card-bar { background: var(--accent); }
.order-card-tone-info    .order-card-bar { background: var(--accent); }
.order-card-tone-amber   .order-card-bar { background: #f59e0b; }
.order-card-tone-danger  .order-card-bar { background: #ef4444; }
.order-card-tone-muted   .order-card-bar { background: #94a3b8; }
.order-card-tone-neutral .order-card-bar { background: var(--text-muted); }

.order-card-main {
  flex: 1;
  min-width: 0;
  padding: 12px 14px;
  display: flex;
  flex-direction: column;
  gap: 5px;
}
.order-card-top {
  display: flex;
  align-items: center;
  gap: 8px;
  flex-wrap: wrap;
}
.order-card-ref {
  font-family: var(--mono, ui-monospace, monospace);
  font-size: var(--text-xs);
  font-weight: 700;
  color: var(--text-secondary);
  background: var(--bg-elevated);
  border: 1px solid var(--border);
  padding: 3px 8px;
  border-radius: var(--radius-sm);
  letter-spacing: 0.04em;
}
/* CALM (2026-06-29) — neutral chip + colour on the dot. The detail twin
   .order-detail-status now matches (its marker rules below are redundant; fold
   away in a later dead-CSS sweep). */
.order-card-status {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font-size: var(--text-xs);
  font-weight: 500;
  padding: 4px 9px;
  border-radius: var(--radius-sm);
  background: var(--bg-elevated);
  border: 1px solid var(--border);
  color: var(--text-primary);
}
.order-card-status::before {
  content: ''; width: 6px; height: 6px; border-radius: 50%;
  background: var(--text-muted); flex-shrink: 0;
}
.order-card-status-tone-success::before { background: #16a34a; }
.order-card-status-tone-accent::before,
.order-card-status-tone-info::before    { background: var(--accent); }
.order-card-status-tone-amber::before   { background: #d97706; }
.order-card-status-tone-danger::before  { background: #dc2626; }
.order-card-status-tone-muted::before,
.order-card-status-tone-neutral::before { background: var(--text-muted); }
/* Order DETAIL header status — CALM (neutral chip + colour on the dot). The order
   LIST card keeps the loud tinted status for scanning (two-tier card rule).
   (Radek 2026-06-29) */
.order-detail-status {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  background: var(--bg-elevated);
  border-color: var(--border);
  color: var(--text-primary);
  font-weight: 500;
}
.order-detail-status::before {
  content: ''; width: 7px; height: 7px; border-radius: 50%;
  background: var(--text-muted); flex-shrink: 0;
}
.order-detail-status.order-card-status-tone-success::before { background: #16a34a; }
.order-detail-status.order-card-status-tone-accent::before,
.order-detail-status.order-card-status-tone-info::before    { background: var(--accent); }
.order-detail-status.order-card-status-tone-amber::before   { background: #d97706; }
.order-detail-status.order-card-status-tone-danger::before  { background: #dc2626; }
.order-detail-status.order-card-status-tone-muted::before,
.order-detail-status.order-card-status-tone-neutral::before { background: var(--text-muted); }
/* Source = a NEUTRAL calm-context chip (two-tier rule: colour reserved for the
   loud status signal + the left tone bar). */
.order-card-source-chip {
  font-size: var(--text-xs);
  font-weight: 600;
  padding: 4px 9px;
  border-radius: var(--radius-sm);
  background: var(--bg-elevated);
  border: 1px solid var(--border);
  color: var(--text-secondary);
}
/* Loud signal row under the title — status (act-now) + the total. */
.order-card-signals {
  display: flex;
  align-items: center;
  gap: 8px;
  flex-wrap: wrap;
}
.order-card-total {
  margin-left: auto;
  font-family: var(--display);
  font-weight: 700;
  font-size: var(--text-base);
  color: var(--text-primary);
}
.order-card-supplier {
  font-weight: 600;
  font-size: var(--text-lg);
  line-height: 1.3;
  color: var(--text-primary);
}
.order-card-meta {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  font-size: var(--text-xs);
  color: var(--text-muted);
}
.order-card-meta-item:not(:last-child)::after {
  content: '·';
  margin-left: 8px;
  color: var(--border);
}
/* P85 — an overdue delivery is an act-now "chase it up" signal: amber, not the
   calm muted meta colour. Only set on sent/partially_received past-due cards. */
.order-card-due.is-overdue { color: var(--warning); font-weight: 700; }

/* ─── Modal overlay + frame ─────────────────────────────────────── */
.order-modal-overlay {
  position: fixed; inset: 0;
  background: rgba(0, 0, 0, 0.5);
  z-index: 9100;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: var(--space-3);
  overflow-y: auto;
}
body.order-modal-open { overflow: hidden; }

.order-modal {
  background: var(--bg-surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  width: 100%;
  max-width: 820px;
  max-height: 92vh;
  display: flex;
  flex-direction: column;
  overflow: hidden;
  box-shadow: var(--shadow-lg);
}
.order-modal-head {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  padding: 16px var(--space-4) 14px;
  border-bottom: 1px solid var(--border);
}
.order-modal-title {
  font-family: var(--display);
  font-weight: 600;
  font-size: var(--text-xl);
  flex: 1;
}
.order-modal-close {
  width: 36px; height: 36px;
  display: grid; place-items: center;
  background: transparent;
  border: 0;
  border-radius: var(--radius-md);
  color: var(--text-muted);
  cursor: pointer;
  transition: all 0.12s;
}
.order-modal-close:hover { background: var(--bg-elevated); color: var(--text-primary); }
.order-modal-close svg { width: 18px; height: 18px; }
.order-modal-body {
  flex: 1;
  overflow-y: auto;
  padding: 16px var(--space-4);
}
.order-modal-foot {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  padding: 14px var(--space-4) 18px;
  border-top: 1px solid var(--border);
  background: var(--bg-surface);
}
.order-modal-detail { max-width: 880px; }
.order-modal-form { max-width: 920px; }

.order-form-source-badge {
  font-size: var(--text-xs);
  padding: 4px 10px;
  background: var(--accent-soft);
  border: 1px solid color-mix(in srgb, var(--accent) 45%, transparent);
  border-radius: var(--radius-sm);
  color: var(--accent);
  font-weight: 600;
  display: inline-flex;
  align-items: center;
  gap: 6px;
}
.order-form-source-badge svg { width: 12px; height: 12px; }

/* ─── Form fields ───────────────────────────────────────────────── */
.order-form-grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: var(--space-3);
  margin-bottom: var(--space-3);
}
.order-form-row { margin-bottom: var(--space-3); }
.order-form-row-span-2 { grid-column: 1 / -1; }
.order-form-label {
  display: block;
  font-size: var(--text-sm);
  font-weight: 600;
  color: var(--text-secondary);
  margin-bottom: 6px;
}
.order-form-label-hint {
  font-weight: 400;
  color: var(--text-muted);
  font-size: var(--text-xs);
}
.order-required { color: #ef4444; }
.order-input {
  width: 100%;
  padding: 10px 12px;
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  background: var(--bg-elevated);
  color: var(--text-primary);
  font-family: var(--body);
  font-size: var(--text-base);
  transition: border-color 120ms, background 120ms;
}
.order-input:focus {
  outline: none;
  border-color: var(--accent);
}
.order-textarea {
  resize: vertical;
  min-height: 60px;
  line-height: 1.45;
}
.order-form-section-head {
  margin: var(--space-4) 0 var(--space-2);
}
.order-form-section-title {
  font-family: var(--display);
  font-size: 16px;
  font-weight: 700;
  margin: 0;
}
.order-form-section-sub {
  font-size: var(--text-xs);
  color: var(--text-muted);
  margin-top: 2px;
}

/* ─── Line editor rows ──────────────────────────────────────────── */
.order-line-list {
  display: flex;
  flex-direction: column;
  gap: 6px;
  margin-bottom: var(--space-2);
}
.order-line-row {
  display: grid;
  grid-template-columns: 24px 90px 1fr 70px 110px 80px 90px 32px;
  gap: 6px;
  align-items: center;
  padding: 6px 8px;
  background: var(--bg-elevated);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
}
.order-line-row-num {
  font-size: var(--text-xs);
  color: var(--text-muted);
  font-weight: 600;
  text-align: center;
}
.order-line-row .order-input {
  padding: 6px 10px;
  font-size: var(--text-sm);
}
.order-line-sku {
  font-family: var(--mono, ui-monospace, monospace);
  font-size: var(--text-xs);
}
.order-line-qty,
.order-line-price {
  text-align: right;
}
.order-line-price-group {
  display: flex;
  align-items: center;
  gap: 4px;
  position: relative;
}
.order-line-currency {
  color: var(--text-muted);
  font-size: var(--text-sm);
  font-weight: 600;
  padding-left: 4px;
}
.order-line-total {
  text-align: right;
  font-weight: 700;
  font-size: var(--text-sm);
  font-family: var(--display);
}
.order-line-del {
  width: 28px; height: 28px;
  border: 1px solid transparent;
  background: transparent;
  border-radius: var(--radius-sm);
  color: var(--text-muted);
  cursor: pointer;
  display: grid; place-items: center;
}
.order-line-del:hover {
  background: color-mix(in srgb, #ef4444 10%, transparent);
  color: #ef4444;
}
.order-line-del:disabled { opacity: 0.3; cursor: not-allowed; }
.order-line-del svg { width: 14px; height: 14px; }

.order-add-line { margin-bottom: var(--space-3); }

@media (max-width: 700px) {
  .order-line-row {
    grid-template-columns: 24px 1fr 70px 32px;
    grid-template-rows: auto auto auto;
    row-gap: 4px;
  }
  .order-line-row-num { grid-column: 1; grid-row: 1; }
  .order-line-sku { grid-column: 2 / 5; grid-row: 1; }
  .order-line-desc { grid-column: 1 / 5; grid-row: 2; }
  .order-line-qty { grid-column: 1 / 2; grid-row: 3; }
  .order-line-price-group { grid-column: 2 / 3; grid-row: 3; }
  .order-line-vat { grid-column: 3 / 4; grid-row: 3; }
  .order-line-total { grid-column: 4 / 5; grid-row: 3; }
  .order-line-del { grid-column: 4 / 5; grid-row: 1; }
}

/* ─── Inventory suggestions ─────────────────────────────────────── */
.order-suggest-block {
  margin: var(--space-3) 0;
  padding: var(--space-3);
  background: color-mix(in srgb, var(--accent) 4%, var(--bg-surface));
  border: 1px solid color-mix(in srgb, var(--accent) 30%, var(--border));
  border-radius: var(--radius-md);
}
.order-suggest-head {
  font-weight: 600;
  font-size: var(--text-sm);
  color: var(--accent);
  display: inline-flex;
  align-items: center;
  gap: 6px;
  margin-bottom: 8px;
}
.order-suggest-head svg { width: 14px; height: 14px; }
.order-suggest-chips {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
}
.order-suggest-chip {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 5px 10px;
  background: var(--bg-elevated);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  font-size: var(--text-xs);
  font-weight: 600;
  color: var(--text-primary);
  cursor: pointer;
  transition: all 120ms;
}
.order-suggest-chip:hover {
  background: var(--accent-soft);
  border-color: var(--accent);
  color: var(--accent);
}
.order-suggest-chip svg { width: 12px; height: 12px; }
.order-suggest-chip-stock { color: var(--text-muted); font-weight: 400; }

/* ─── Totals block ──────────────────────────────────────────────── */
.order-totals-block {
  margin: var(--space-3) 0 var(--space-4);
  padding: var(--space-3);
  background: var(--bg-elevated);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  max-width: 360px;
  margin-left: auto;
}
.order-totals-row {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  padding: 4px 0;
  font-size: var(--text-sm);
  color: var(--text-secondary);
}
.order-totals-row-grand {
  border-top: 2px solid var(--border-strong);
  padding-top: 8px;
  margin-top: 4px;
  font-family: var(--display);
  font-weight: 700;
  font-size: var(--text-lg);
  color: var(--text-primary);
}
.order-totals-num { font-variant-numeric: tabular-nums; }

/* ─── Buttons ───────────────────────────────────────────────────── */
.order-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 6px;
  padding: 9px 18px;
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  background: var(--bg-elevated);
  color: var(--text-secondary);
  font-family: var(--body);
  font-size: var(--text-sm);
  font-weight: 600;
  cursor: pointer;
  transition: opacity 120ms, background 120ms, color 120ms, border-color 120ms;
}
.order-btn:hover { background: var(--bg-hover); color: var(--text-primary); }
.order-btn svg { width: 14px; height: 14px; }
.order-btn:disabled { opacity: 0.5; cursor: not-allowed; }
.order-btn-primary {
  background: var(--accent);
  border-color: var(--accent);
  color: var(--bg-base);
}
.order-btn-primary:hover { opacity: 0.88; background: var(--accent); color: var(--bg-base); }
.order-btn-secondary {
  background: var(--bg-elevated);
  border: 1.5px dashed var(--border-strong);
  color: var(--text-secondary);
}
.order-btn-secondary:hover { color: var(--accent); border-color: var(--accent); }
.order-btn-ghost { background: transparent; color: var(--text-secondary); }
.order-btn-ghost:hover { background: var(--bg-elevated); color: var(--text-primary); }
.order-btn-ghost-danger {
  background: transparent;
  border-color: color-mix(in srgb, #ef4444 35%, var(--border));
  color: #ef4444;
}
.order-btn-ghost-danger:hover { background: color-mix(in srgb, #ef4444 8%, transparent); }

/* ─── DETAIL VIEW ─────────────────────────────────────────────── */
.order-detail-overlay { padding: 0; align-items: stretch; }
.order-modal-detail {
  max-height: 100vh;
  height: 100vh;
  border-radius: 0;
}
@media (min-width: 800px) {
  .order-detail-overlay { padding: var(--space-3); align-items: center; }
  .order-modal-detail { max-height: 96vh; height: auto; border-radius: var(--radius-lg); }
}
/* Below 800px the detail is a full-screen takeover (height:100vh above). Push the
   WHOLE modal below the iPhone status bar / notch at the OVERLAY level and subtract
   the inset from the modal height — the robust pattern the Tasks fullscreen detail
   and .audit-detail-overlay use (padding the header alone slides back under the
   status bar on iOS PWA). Overlay painted --bg-surface so the safe-area strip reads
   as one continuous header bar, not a dark scrim slice (the Tasks-approved look).
   The dvh pair self-corrects for the Safari toolbar; in the installed PWA dvh == vh. */
@media (max-width: 799px) {
  .order-detail-overlay {
    align-items: flex-start;
    padding-top: var(--safe-top);
    background: var(--bg-surface);
  }
  .order-modal-detail {
    height: calc(100vh - var(--safe-top));
    max-height: calc(100vh - var(--safe-top));
    height: calc(100dvh - var(--safe-top));
    max-height: calc(100dvh - var(--safe-top));
  }
  /* Send to supplier / Print PO / Share must ALL be reachable: wrap instead of
     overflowing off the right edge, and clear the iPhone home indicator. */
  .order-detail-overlay .order-modal-foot {
    flex-wrap: wrap;
    padding-bottom: calc(env(safe-area-inset-bottom) + 14px);
  }
}
.order-detail-head {
  display: grid;
  /* text · total · close — ✕ sits on the RIGHT like the form modal, the receive
     panel, and every other detail header app-wide (Radek 2026-07-17). */
  grid-template-columns: 1fr auto 36px;
  gap: var(--space-2);
  padding: var(--space-3) var(--space-4);
  border-bottom: 1px solid var(--border);
  align-items: center;
}
.order-detail-head-text { min-width: 0; }
.order-detail-ref {
  font-family: var(--mono, ui-monospace, monospace);
  font-size: var(--text-xs);
  font-weight: 700;
  color: var(--text-secondary);
  margin-bottom: 2px;
}
.order-detail-supplier {
  font-family: var(--display);
  font-weight: 700;
  font-size: var(--text-lg);
  line-height: 1.2;
}
.order-detail-meta-row {
  display: flex;
  align-items: center;
  gap: 10px;
  margin-top: 4px;
  flex-wrap: wrap;
}
.order-detail-meta-item {
  font-size: var(--text-xs);
  color: var(--text-muted);
  display: inline-flex;
  align-items: center;
  gap: 4px;
}
.order-detail-meta-item svg { width: 12px; height: 12px; }
.order-detail-head-total {
  text-align: right;
}
.order-detail-head-total-label {
  font-size: var(--text-xs);
  color: var(--text-muted);
  text-transform: uppercase;
  letter-spacing: 0.06em;
  font-weight: 600;
}
.order-detail-head-total-num {
  font-family: var(--display);
  font-weight: 700;
  font-size: 22px;
  color: var(--text-primary);
}

.order-detail-body { padding: var(--space-3) var(--space-4); }

.order-detail-notes {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: var(--space-3);
  margin-bottom: var(--space-3);
}
.order-detail-notes-block {
  padding: var(--space-3);
  background: var(--bg-elevated);
  border-radius: var(--radius-md);
  border: 1px solid var(--border);
}
.order-detail-notes-label {
  font-size: var(--text-xs);
  font-weight: 600;
  color: var(--text-muted);
  text-transform: uppercase;
  letter-spacing: 0.04em;
  margin-bottom: 4px;
}
.order-detail-notes-body {
  font-size: var(--text-sm);
  color: var(--text-primary);
  line-height: 1.4;
}
@media (max-width: 700px) {
  .order-detail-notes { grid-template-columns: 1fr; }
}

/* Lines table — read-only */
.order-detail-line-table {
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  overflow: hidden;
  margin-bottom: var(--space-3);
}
.order-detail-line-head,
.order-detail-line-row {
  display: grid;
  grid-template-columns: 32px 1fr 60px 80px 50px 80px;
  align-items: center;
  padding: 8px 12px;
  font-size: var(--text-sm);
}
.order-detail-line-row:has(.order-detail-line-col-recv),
.order-detail-line-head:has(.order-detail-line-col-recv) {
  grid-template-columns: 32px 1fr 60px 80px 50px 80px 80px;
}
.order-detail-line-head {
  background: var(--bg-elevated);
  font-size: var(--text-xs);
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--text-muted);
  border-bottom: 1px solid var(--border);
}
.order-detail-line-row { border-bottom: 1px solid var(--border); }
.order-detail-line-row:last-child { border-bottom: 0; }
.order-detail-line-col-num   { color: var(--text-muted); }
.order-detail-line-col-qty,
.order-detail-line-col-price,
.order-detail-line-col-vat,
.order-detail-line-col-total { text-align: right; }
.order-detail-line-col-total { font-weight: 700; }
.order-detail-line-col-recv  {
  text-align: right;
  font-weight: 600;
}
.order-detail-line-col-recv.is-full { color: #16a34a; }
.order-detail-line-col-recv.is-partial { color: #f59e0b; }
.order-detail-line-col-recv.is-none { color: var(--text-muted); }

.order-detail-line-sku {
  display: inline-block;
  font-family: var(--mono, ui-monospace, monospace);
  font-size: var(--text-xs);
  color: var(--text-muted);
  margin-right: 8px;
}
.order-detail-line-desc-text { font-weight: 500; }

@media (max-width: 700px) {
  .order-detail-line-head { display: none; }
  .order-detail-line-row {
    grid-template-columns: 1fr auto;
    grid-template-rows: auto auto;
    gap: 4px;
  }
}

/* ─── Receive panel ─────────────────────────────────────────────── */
.order-receive-panel {
  margin: var(--space-3) 0;
  padding: var(--space-3);
  background: color-mix(in srgb, var(--accent) 4%, var(--bg-surface));
  border: 1px solid color-mix(in srgb, var(--accent) 35%, var(--border));
  border-radius: var(--radius-md);
}
.order-receive-panel-head {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  margin-bottom: 4px;
}
.order-receive-panel-title {
  font-family: var(--display);
  font-weight: 600;
  font-size: var(--text-md);
  flex: 1;
  display: inline-flex;
  align-items: center;
  gap: 6px;
}
.order-receive-panel-title svg { width: 16px; height: 16px; }
.order-receive-instructions {
  font-size: var(--text-xs);
  color: var(--text-muted);
  margin-bottom: var(--space-3);
}
/* P85 — the global "Receive everything" bar sits between the instructions and
   the lines; keep the panel's vertical rhythm so the ghost button never abuts
   the first line. */
.order-receive-allbar { margin-bottom: var(--space-3); }
.order-receive-lines {
  display: flex;
  flex-direction: column;
  gap: 6px;
  margin-bottom: var(--space-3);
}
.order-receive-line {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  padding: 8px 10px;
  background: var(--bg-elevated);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  flex-wrap: wrap;
}
.order-receive-line-info {
  flex: 1;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 6px;
}
.order-receive-line-desc { font-weight: 500; }
.order-receive-line-ordered {
  font-size: var(--text-xs);
  color: var(--text-muted);
}
.order-receive-line-input-wrap {
  display: flex;
  align-items: center;
  gap: 6px;
}
.order-receive-line-input {
  width: 80px;
  text-align: right;
  padding: 6px 8px;
  font-size: var(--text-sm);
}
.order-receive-line-of {
  font-size: var(--text-xs);
  color: var(--text-muted);
}
.order-receive-line-full {
  padding: 5px 10px;
  background: var(--bg-surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  font-size: var(--text-xs);
  font-weight: 600;
  color: var(--text-secondary);
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  gap: 4px;
}
.order-receive-line-full:hover {
  color: #16a34a;
  border-color: #22c55e;
}
.order-receive-line-full svg { width: 12px; height: 12px; }
/* Optional "invoice total ex VAT (if different)" captured at receipt — feeds
   Budgets (orderBudgetExVat_) in preference to the PO total. One calm field;
   the £ prefix sits beside the input. The input is 16px (not .order-input's
   13px var(--text-base)) — the iOS no-zoom floor (§4); this .order-receive-
   invoice-input rule follows .order-input in source order so it wins. */
.order-receive-invoice {
  margin-bottom: var(--space-3);
}
.order-receive-invoice-label {
  display: block;
  font-size: var(--text-xs);
  font-weight: 600;
  color: var(--text-muted);
  text-transform: uppercase;
  letter-spacing: 0.04em;
  margin-bottom: 6px;
}
.order-receive-invoice-hint {
  text-transform: none;
  letter-spacing: 0;
  font-weight: 500;
}
.order-receive-invoice-field {
  display: flex;
  align-items: center;
  gap: 6px;
  max-width: 220px;
}
.order-receive-invoice-prefix {
  font-size: var(--text-base);
  font-weight: 600;
  color: var(--text-secondary);
}
.order-receive-invoice-input {
  text-align: right;
  font-variant-numeric: tabular-nums;
  font-size: 16px; /* iOS no-zoom floor — .order-input's var(--text-base) is 13px */
}
.order-receive-invoice-sub {
  font-size: var(--text-xs);
  color: var(--text-muted);
  margin-top: 6px;
}
.order-receive-photos {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: var(--space-3);
}
.order-receive-photo-label {
  font-size: var(--text-xs);
  font-weight: 600;
  color: var(--text-muted);
  text-transform: uppercase;
  letter-spacing: 0.04em;
  margin-bottom: 6px;
}
@media (max-width: 700px) {
  .order-receive-photos { grid-template-columns: 1fr; }
}

/* ─── Photo strip ───────────────────────────────────────────────── */
.order-photo-row {
  display: flex;
  align-items: flex-start;
  gap: 10px;
  flex-wrap: wrap;
}
.order-photo-add-btn {
  display: inline-flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 4px;
  width: 86px; height: 86px;
  border: 1.5px dashed var(--border-strong);
  border-radius: var(--radius-md);
  background: var(--bg-elevated);
  color: var(--text-secondary);
  font-family: var(--body);
  font-size: var(--text-xs);
  font-weight: 600;
  cursor: pointer;
  flex-shrink: 0;
  transition: all 0.12s;
}
.order-photo-add-btn:hover { color: var(--accent); border-color: var(--accent); }
.order-photo-add-btn svg { width: 20px; height: 20px; }
.order-photo-strip {
  display: flex;
  gap: 10px;
  flex-wrap: wrap;
  flex: 1;
}
.order-photo-strip-display {
  margin-top: var(--space-2);
  margin-bottom: var(--space-3);
}
.order-photo-thumb {
  position: relative;
  width: 86px; height: 86px;
  border-radius: var(--radius-md);
  overflow: hidden;
  background: var(--bg-elevated);
  border: 1px solid var(--border);
}
.order-photo-thumb img {
  width: 100%; height: 100%;
  object-fit: cover;
  display: block;
}
.order-photo-thumb-delivery { border-color: color-mix(in srgb, var(--accent) 40%, var(--border)); }
.order-photo-thumb-invoice  { border-color: color-mix(in srgb, #f59e0b 40%, var(--border)); }

/* ─── History feed ──────────────────────────────────────────────── */
.order-history-feed {
  display: flex;
  flex-direction: column;
  gap: 8px;
  padding: var(--space-2) var(--space-3);
  background: var(--bg-elevated);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  max-height: 320px;
  overflow-y: auto;
}
.order-history-item {
  padding: 6px 0;
  border-bottom: 1px dashed var(--border);
}
.order-history-item:last-child { border-bottom: 0; padding-bottom: 0; }
.order-history-meta {
  display: flex;
  gap: 8px;
  align-items: baseline;
  font-size: var(--text-xs);
  margin-bottom: 2px;
}
.order-history-meta strong { color: var(--text-primary); }
.order-history-time { color: var(--text-muted); }
.order-history-text {
  font-size: var(--text-sm);
  color: var(--text-secondary);
  line-height: 1.4;
}

/* ══════════════════════════════════════════════════════════════════════
   UI POLISH  (appended last so these win by source order over the base
   component rules — no specificity hacks needed)
   ══════════════════════════════════════════════════════════════════════ */

/* ── Primary-action sheen ──────────────────────────────────────────────
   A subtle top-down gloss on accent CTAs, layered as background-image so it
   sits over the accent background-colour and works with ANY accent the user
   picks. Gives primary buttons a premium, slightly convex feel. Both themes.
   Hover states on these buttons use filter/opacity (not background), so the
   sheen persists on hover. */
.callout-btn-primary,
.tasks-btn-primary,
.modal-btn-primary,
.kb-modal-btn-primary,
.ce-add-line-btn,
.ce-save-btn {
  background-image: linear-gradient(180deg,
    rgba(255,255,255,0.17),
    rgba(255,255,255,0) 45%,
    rgba(0,0,0,0.07));
}

/* ── Confident heading ─────────────────────────────────────────────────
   Tighter tracking on the app-bar title (seen on every screen) reads more
   modern and deliberate than the previous 0 letter-spacing. */
.app-bar-title { letter-spacing: -0.02em; }

/* "Choose from contractors" trigger — Callouts form + Asset contractor modal */
.callout-form-pick-contractor,
.asset-contractor-choose {
  display: inline-flex; align-items: center; gap: 8px; width: 100%; justify-content: center;
  height: 42px; padding: 0 14px; margin-bottom: 2px;
  background: var(--accent-soft);
  border: 1px solid color-mix(in srgb, var(--accent) 35%, transparent);
  border-radius: 10px; color: var(--accent); font: 600 13.5px/1 var(--body); cursor: pointer;
  transition: background 0.12s, border-color 0.12s;
  -webkit-tap-highlight-color: transparent;
}
.callout-form-pick-contractor:hover,
.asset-contractor-choose:hover { background: color-mix(in srgb, var(--accent) 16%, transparent); }
.callout-form-pick-contractor svg,
.asset-contractor-choose svg { width: 16px; height: 16px; }

/* ═══════════════════════════════════════════════════════════════════════
   COMPLIANCE — dashboard (Phase 1). Reuses RAG colours + KPI/card patterns
   from the rest of the app so it feels native.
   ═══════════════════════════════════════════════════════════════════════ */
.compliance-page { display: flex; flex-direction: column; gap: 18px; }

/* Phase 3 — actionable alert banner (the in-app reminder) */
.compliance-alert {
  display: flex; align-items: center; gap: 11px; width: 100%; text-align: left;
  padding: 12px 14px; border-radius: var(--radius-md); border: 1px solid;
  font: 500 13.5px / 1.4 var(--body); font-family: inherit; cursor: pointer;
  transition: filter 0.12s;
}
.compliance-alert:hover { filter: brightness(0.98); }
.compliance-alert svg { width: 20px; height: 20px; flex-shrink: 0; }
.compliance-alert strong { font-weight: 700; }
.compliance-alert.is-breach  { background: color-mix(in srgb, var(--rag-fail-fill) 8%, transparent); border-color: color-mix(in srgb, var(--rag-fail-fill) 30%, transparent); color: var(--rag-fail); }
.compliance-alert.is-warning { background: color-mix(in srgb, var(--rag-warn-fill) 8%, transparent); border-color: color-mix(in srgb, var(--rag-warn-fill) 30%, transparent); color: var(--rag-warn); }
.compliance-alert.is-ok      { background: color-mix(in srgb, var(--rag-pass-fill) 8%, transparent); border-color: color-mix(in srgb, var(--rag-pass-fill) 25%, transparent); color: var(--rag-pass); cursor: default; }

/* Score donut + headline */
.compliance-score-card {
  display: flex; align-items: center; gap: 18px;
  background: var(--bg-surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg, 14px);
  padding: 18px 20px;
  box-shadow: var(--shadow-card);
}
.compliance-score-ring {
  position: relative;
  width: 92px; height: 92px; flex-shrink: 0;
  border-radius: 50%;
  display: grid; place-items: center;
  background: conic-gradient(var(--ring, var(--rag-pass)) calc(var(--pct, 0) * 1%), var(--bg-elevated) 0);
}
.compliance-score-ring::before {
  content: ''; position: absolute; inset: 9px;
  border-radius: 50%; background: var(--bg-surface);
}
.compliance-score-num {
  position: relative; z-index: 1;
  font: 800 24px / 1 var(--display, var(--body));
  color: var(--text-primary);
}
.compliance-score-num small { font-size: 13px; font-weight: 700; color: var(--text-muted); }
.compliance-score-meta  { min-width: 0; }
.compliance-score-title { font: 700 var(--text-base) / 1.2 var(--display, var(--body)); color: var(--text-primary); }
.compliance-score-sub   { font: 500 13px / 1.4 var(--body); color: var(--text-secondary); margin-top: 3px; }
/* The health verdict (Excellent / Action needed …) sits on its OWN line under
   the count line — keeps the ring card compact so the KPI strip beside it (the
   collection-browser desktop layout) is never pushed off-screen. Shared by
   contractors, compliance & safety. */
.compliance-score-verdict { display: block; font-weight: 700; margin-top: 1px; }

/* KPI strip — 4 clickable cards (reuses .callout-kpi-card). The .asset-stats-row
   conjunct lifts specificity to (0,2,0) so this BEATS css/12's
   .asset-stats-row:has(.cl-kpi-card) — without it the minmax(0) below was dead
   (cards could not shrink and sliced off-screen when the row got tight). */
.asset-stats-row.compliance-kpis { grid-template-columns: repeat(4, minmax(0, 1fr)); }

/* Section titles */
.compliance-section-title {
  font: 700 var(--text-sm) / 1 var(--body);
  text-transform: uppercase; letter-spacing: 0.05em;
  color: var(--text-muted); margin: 2px 0 -6px;
}
.compliance-filter-note { font-weight: 500; text-transform: none; letter-spacing: 0; color: var(--text-muted); }

/* Category cards */
.compliance-cat-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(220px, 1fr)); gap: 10px; }
.compliance-cat {
  text-align: left; background: var(--bg-surface); border: 1px solid var(--border);
  border-radius: var(--radius-md); padding: 12px 14px; cursor: pointer; font-family: inherit;
  display: flex; flex-direction: column; gap: 9px;
  transition: border-color 0.12s, background 0.12s;
}
.compliance-cat:hover { border-color: var(--accent); }
.compliance-cat.is-active { border-color: var(--accent); background: color-mix(in srgb, var(--accent) 6%, var(--bg-surface)); }
.compliance-cat-head { display: flex; align-items: baseline; justify-content: space-between; gap: 8px; }
.compliance-cat-name { font: 700 14px / 1.2 var(--display, var(--body)); color: var(--text-primary); }
.compliance-cat-count { font: 600 12px / 1 var(--body); color: var(--text-muted); }
.compliance-cat-bar { display: flex; height: 7px; border-radius: 4px; overflow: hidden; background: var(--bg-elevated); }
.compliance-cat-bar span { display: block; min-width: 2px; }
.compliance-cat-foot { display: flex; align-items: center; justify-content: space-between; gap: 8px; }
.compliance-cat-flag { font: 700 11px / 1 var(--body); }
.compliance-cat-flag.is-ok { color: var(--rag-pass); }
.compliance-cat-flag.is-warning { color: var(--rag-warn); }
.compliance-cat-flag.is-breach { color: var(--rag-fail); }
.compliance-cat-next { font: 500 11px / 1 var(--body); color: var(--text-muted); white-space: nowrap; }

/* Item list / timeline */
.compliance-list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 6px; }
/* Desktop: the checks / register / certificates lists fan into a responsive grid
   at a GENEROUS min width so the info-dense rows stay readable; the Tree tab keeps
   its single-column .ctree. Auto-collapses to 1 col on mobile. */
.compliance-page .compliance-list { display: grid; grid-template-columns: repeat(auto-fill, minmax(460px, 1fr)); gap: 8px; align-items: start; }
.compliance-row {
  display: flex; align-items: center; gap: 12px;
  background: var(--bg-surface); border: 1px solid var(--border);
  border-radius: var(--radius-md); padding: 12px;
  width: 100%; text-align: left; font-family: inherit; color: inherit;
}
.compliance-row.is-clickable { cursor: pointer; transition: border-color 0.12s, background 0.12s; }
.compliance-row.is-clickable:hover { border-color: var(--accent); background: color-mix(in srgb, var(--accent) 4%, var(--bg-surface)); }
/* Two-tier card: calm category chip on top, title hero, loud status + due below. */
.compliance-row-body { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 5px; }
.compliance-row-top { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.compliance-row-cat {
  display: inline-flex; align-items: center; white-space: nowrap;
  padding: 4px 9px; border-radius: var(--radius-sm);
  background: var(--bg-elevated); border: 1px solid var(--border);
  color: var(--text-secondary); font: 600 var(--text-xs) / 1 var(--body);
}
.compliance-row-cat-dot { width: 8px; height: 8px; border-radius: 50%; margin-right: 6px; flex-shrink: 0; }
.compliance-row-owner { font: 500 12px / 1.3 var(--body); color: var(--text-secondary); }
.compliance-row-title { font: 600 var(--text-lg) / 1.3 var(--body); color: var(--text-primary); }
.compliance-row-signals { display: flex; align-items: center; gap: 6px; flex-wrap: wrap; }
.compliance-row-status {
  flex-shrink: 0; white-space: nowrap;
  padding: 4px 9px; border-radius: var(--radius-sm); border: 1px solid transparent;
  font: 600 var(--text-xs) / 1 var(--body);
}
/* CALM 'In date' (2026-06-29) — neutral chip + green dot. The due_soon / overdue /
   missing states stay LOUD (deadline alarms). */
.compliance-row-status--calm {
  display: inline-flex; align-items: center; gap: 6px;
  background: var(--bg-elevated); border-color: var(--border);
  color: var(--text-primary); font-weight: 500;
}
.compliance-status-dot { width: 6px; height: 6px; border-radius: var(--radius-full); flex-shrink: 0; }
.compliance-row-due {
  display: inline-flex; align-items: center; white-space: nowrap;
  padding: 4px 9px; border-radius: var(--radius-sm);
  background: var(--bg-elevated); border: 1px solid var(--border);
  color: var(--text-secondary); font: 600 var(--text-xs) / 1 var(--body);
}
.compliance-row-meta { display: flex; gap: 12px; flex-wrap: wrap; margin-top: 2px; }
.compliance-row-arrow { width: 16px; height: 16px; flex-shrink: 0; color: var(--text-muted); }
.compliance-empty { color: var(--text-muted); font-size: 13px; padding: 16px; text-align: center; }

/* Phase 2 — tabs shell + register document chip */
.compliance-wrap { display: flex; flex-direction: column; gap: 14px; }
.compliance-tabs { align-self: flex-start; }
.compliance-row-cert svg { vertical-align: -1px; }
.compliance-doc-chip {
  display: inline-flex; align-items: center; gap: 6px;
  padding: 5px 5px 5px 10px;
  background: var(--bg-elevated); border: 1px solid var(--border);
  border-radius: var(--radius-sm);
}
.compliance-doc-chip a {
  display: inline-flex; align-items: center; gap: 6px;
  color: var(--accent); font: 600 12px / 1.3 var(--body); text-decoration: none;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap; max-width: 300px;
}
.compliance-doc-chip a svg { flex-shrink: 0; }
.compliance-doc-chip button {
  display: grid; place-items: center; width: 24px; height: 24px; flex-shrink: 0;
  background: transparent; border: 0; border-radius: 50%; cursor: pointer; color: var(--text-muted);
}
.compliance-doc-chip button:hover { background: var(--bg-hover); color: var(--text-primary); }
.compliance-doc-chip button svg { width: 12px; height: 12px; }
.compliance-links-row { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.compliance-links-row .compliance-doc-chip > span { display: inline-flex; align-items: center; gap: 6px; }
.compliance-links-row .compliance-doc-chip button, .compliance-links-row > .callout-btn { min-width: 44px; min-height: 44px; }

@media (max-width: 640px) {
  /* (0,2,0) matches the desktop rule above so 2-across still wins on phones. */
  .asset-stats-row.compliance-kpis { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .compliance-cat-grid { grid-template-columns: 1fr; }
}

/* ═══════════════════════════════════════════════════════════════════════
   COMPLIANCE — tree (Phase 4a). Smart collapsible RAG tree.
   Top categories are full-width cards; branches nest with a connector line.
   Colour comes live from the linked item; parents show their worst child.
   ═══════════════════════════════════════════════════════════════════════ */
.ctree { display: flex; flex-direction: column; gap: 8px; }
.ctree-row { display: flex; flex-direction: column; }

.ctree-card {
  position: relative;
  display: flex; align-items: center; gap: 11px;
  background: var(--bg-surface); border: 1px solid var(--border);
  border-radius: var(--radius-md); padding: 12px 12px 12px 14px;
  cursor: pointer; font-family: inherit; text-align: left;
  transition: border-color 0.12s, background 0.12s, box-shadow 0.12s;
}
.ctree-card:hover { border-color: var(--accent); background: color-mix(in srgb, var(--accent) 4%, var(--bg-surface)); }
.ctree-card.is-static { cursor: default; }
.ctree-card.is-static:hover { border-color: var(--border); background: var(--bg-surface); }
.ctree-row[data-depth="0"] > .ctree-card { box-shadow: var(--shadow-card); padding-top: 14px; padding-bottom: 14px; }

/* RAG status dot (colour set inline) */
.ctree-dot { flex-shrink: 0; width: 11px; height: 11px; border-radius: 50%; }
.ctree-row[data-depth="0"] > .ctree-card .ctree-dot { width: 13px; height: 13px; }

/* Expand / collapse chevron */
.ctree-chev {
  flex-shrink: 0; display: grid; place-items: center;
  width: 24px; height: 24px; padding: 0;
  background: transparent; border: 0; border-radius: 6px;
  color: var(--text-muted); cursor: pointer;
  transition: transform 0.15s ease, background 0.12s, color 0.12s;
}
.ctree-chev:hover { background: var(--bg-hover); color: var(--text-primary); }
.ctree-chev.is-open { transform: rotate(90deg); }
.ctree-chev svg { width: 16px; height: 16px; }
.ctree-chev-spacer { flex-shrink: 0; width: 24px; height: 24px; }

/* Title + subtext */
.ctree-body { flex: 1; min-width: 0; }
.ctree-title {
  display: flex; align-items: center; gap: 8px; flex-wrap: wrap;
  font: 700 14px / 1.25 var(--display, var(--body)); color: var(--text-primary);
}
.ctree-row[data-depth="0"] > .ctree-card .ctree-title { font-size: 15.5px; }
.ctree-row[data-depth="2"] > .ctree-card .ctree-title { font-size: 13px; }
.ctree-link {
  font: 600 11px / 1 var(--body); color: var(--accent);
  background: var(--accent-soft); padding: 3px 7px; border-radius: 999px;
  max-width: 200px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.ctree-sub { font: 500 12px / 1.4 var(--body); color: var(--text-secondary); margin-top: 3px; }
.ctree-sub em { font-style: normal; }

/* Per-card actions */
.ctree-actions { flex-shrink: 0; display: flex; align-items: center; gap: 2px; }
.ctree-act {
  display: grid; place-items: center; width: 30px; height: 30px;
  background: transparent; border: 0; border-radius: 7px;
  color: var(--text-muted); cursor: pointer; transition: background 0.12s, color 0.12s;
}
.ctree-act:hover { background: var(--bg-hover); color: var(--text-primary); }
.ctree-act svg { width: 15px; height: 15px; }

/* ── Plan tab phase cards (Plan-only — scoped to .plan-folder so the SHARED
   Compliance .ctree tree is untouched). A phase leads with a RAG left-accent
   bar (colour = its worst-child status, via inline --rag) instead of a dot, and
   an "Add" pill instead of a cryptic + icon. Task leaves keep the dot + chips. */
.plan-folder .ctree-card.is-phase {
  border-left: 3px solid var(--rag, var(--border));
  padding-left: 13px;
}
/* The "Add" pill — soft accent, used on both the folder phase card and the board
   phase column. A clearer affordance than the old icon-only button. */
.ctree-add-pill {
  flex-shrink: 0; display: inline-flex; align-items: center; gap: 5px;
  height: 30px; padding: 0 11px; border-radius: var(--radius-md);
  background: var(--accent-soft);
  border: 1px solid color-mix(in srgb, var(--accent) 38%, transparent);
  color: var(--accent); font: 600 12.5px/1 var(--body); cursor: pointer;
  -webkit-tap-highlight-color: transparent; touch-action: manipulation;
  transition: background 0.12s, border-color 0.12s, transform 0.06s;
}
.ctree-add-pill:hover { background: color-mix(in srgb, var(--accent) 16%, transparent); border-color: var(--accent); }
.ctree-add-pill:active { transform: scale(0.97); }
.ctree-add-pill svg { width: 15px; height: 15px; flex: none; }
/* Plan-tab nesting connector — a touch stronger than the shared Compliance tree
   so sub-phases read as distinct levels on a phone (Plan-scoped). */
.plan-folder .ctree-children { border-left-color: var(--border-strong); }

/* Nested children — indent + vertical connector */
.ctree-children {
  display: flex; flex-direction: column; gap: 8px;
  margin: 8px 0 2px 12px; padding-left: 16px;
  border-left: 2px solid var(--border);
}

/* Link-type picker in the builder modal */
/* "What are you adding?" type picker — an even 5-up segmented tile control
   (icon over a short label). Replaces the old ragged auto-width button row so
   it reads as one app-consistent control. */
.ctree-lt-row { display: grid; grid-template-columns: repeat(5, 1fr); gap: 8px; }
.ctree-lt {
  display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 6px;
  min-width: 0; padding: 11px 4px; border-radius: var(--radius-md);
  background: var(--bg-elevated); border: 1px solid var(--border);
  color: var(--text-secondary); font: 600 11.5px / 1 var(--body); cursor: pointer;
  -webkit-tap-highlight-color: transparent; touch-action: manipulation;
  transition: background 0.12s, border-color 0.12s, color 0.12s, transform 0.06s;
}
.ctree-lt svg { width: 20px; height: 20px; flex: none; }
.ctree-lt > span { max-width: 100%; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.ctree-lt:hover { border-color: var(--accent); color: var(--text-primary); }
.ctree-lt:active { transform: scale(0.97); }
.ctree-lt.is-on { background: var(--accent-soft); border-color: var(--accent); color: var(--accent); }

@media (max-width: 640px) {
  .ctree-children { margin-left: 8px; padding-left: 12px; }
  .ctree-act { width: 34px; height: 34px; }
  .ctree-act svg { width: 16px; height: 16px; }
}

/* ═══════════════════════════════════════════════════════════════════════
   BUDGETS — budget vs actual. Period switcher → KPI strip → budget lines
   (RAG progress bars, realised solid + committed faded) → unbudgeted spend.
   Reuses the app's card / chip / KPI language so it feels native.
   ═══════════════════════════════════════════════════════════════════════ */
.budget-page { display: flex; flex-direction: column; gap: 16px; }

/* Toolbar: period type · prev/next · set-budget */
.budget-toolbar { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; }
.budget-period-nav { display: flex; align-items: center; gap: 6px; }
.budget-period-arrow {
  display: grid; place-items: center; width: 32px; height: 32px; flex-shrink: 0;
  background: var(--bg-surface); border: 1px solid var(--border); border-radius: 9px;
  color: var(--text-secondary); cursor: pointer; transition: background 0.12s, border-color 0.12s, color 0.12s;
}
.budget-period-arrow:hover { border-color: var(--accent); color: var(--text-primary); }
.budget-period-arrow svg { width: 16px; height: 16px; }
.budget-period-label {
  min-width: 132px; text-align: center;
  font: 700 var(--text-base) / 1 var(--display, var(--body)); color: var(--text-primary);
}
/* KPI strip */
.budget-kpis { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 10px; }
.budget-kpi {
  background: var(--bg-surface); border: 1px solid var(--border);
  border-radius: var(--radius-md); padding: 12px 14px; box-shadow: var(--shadow-card);
}
.budget-kpi-label { font: 700 var(--text-xs) / 1 var(--body); text-transform: uppercase; letter-spacing: 0.05em; color: var(--text-muted); }
.budget-kpi-value { margin-top: 7px; font: 800 20px / 1 var(--display, var(--body)); color: var(--text-primary); }
.budget-kpi.is-over .budget-kpi-value  { color: #dc2626; }
.budget-kpi.is-good .budget-kpi-value  { color: #16a34a; }

/* Department groups */
.budget-group { display: flex; flex-direction: column; gap: 10px; }
.budget-group-head {
  display: flex; align-items: baseline; gap: 8px;
  font: 700 var(--text-sm) / 1 var(--body); text-transform: uppercase; letter-spacing: 0.05em;
  color: var(--text-muted); margin: 4px 0 -2px;
}
.budget-group-note { font-weight: 500; text-transform: none; letter-spacing: 0; color: var(--text-muted); }
.budget-group-lines { display: grid; grid-template-columns: repeat(auto-fill, minmax(290px, 1fr)); gap: 10px; }

/* A budget line card */
.budget-line {
  background: var(--bg-surface); border: 1px solid var(--border);
  border-radius: var(--radius-md); padding: 13px 14px; cursor: pointer;
  transition: border-color 0.12s, background 0.12s; text-align: left;
}
.budget-line:hover { border-color: var(--accent); background: color-mix(in srgb, var(--accent) 4%, var(--bg-surface)); }
.budget-line-head { display: flex; align-items: center; justify-content: space-between; gap: 8px; }
.budget-line-title { display: flex; align-items: center; gap: 6px; flex-wrap: wrap; min-width: 0; }
.budget-line-edit {
  display: grid; place-items: center; width: 28px; height: 28px; flex-shrink: 0;
  background: transparent; border: 0; border-radius: 7px; color: var(--text-muted); cursor: pointer;
  transition: background 0.12s, color 0.12s;
}
.budget-line-edit:hover { background: var(--bg-hover); color: var(--text-primary); }
.budget-line-edit svg { width: 15px; height: 15px; }

.budget-line-chip {
  font: 600 11px / 1 var(--body); padding: 4px 8px; border-radius: 999px;
  color: var(--text-muted); background: var(--bg-elevated); border: 1px solid var(--border);
}

.budget-line-amounts { display: flex; align-items: baseline; justify-content: space-between; gap: 8px; margin-top: 11px; }
.budget-line-budget { font: 800 17px / 1 var(--display, var(--body)); color: var(--text-primary); }
.budget-line-pct { font: 700 13px / 1 var(--body); }

.budget-bar { position: relative; height: 9px; border-radius: 5px; background: var(--bg-elevated); overflow: hidden; margin: 7px 0 8px; }
.budget-bar-fill, .budget-bar-committed { position: absolute; top: 0; bottom: 0; }
.budget-bar-fill { left: 0; border-radius: 5px; }

.budget-line-foot { display: flex; align-items: baseline; justify-content: space-between; gap: 8px; font: 500 12px / 1.3 var(--body); color: var(--text-secondary); }
.budget-line-left { font-weight: 700; color: #16a34a; white-space: nowrap; }
.budget-line-left.is-over { color: #dc2626; }

/* Amount input in the form */
.budget-amount-input { display: flex; align-items: center; gap: 2px; background: var(--bg-elevated); border: 1px solid var(--border); border-radius: var(--radius-sm); padding: 0 12px; }
.budget-amount-input:focus-within { border-color: var(--accent); }
.budget-amount-prefix { font: 700 15px / 1 var(--body); color: var(--text-muted); }
.budget-amount-input input { flex: 1; min-width: 0; border: 0; background: transparent; padding: 11px 6px; font: 600 15px / 1 var(--body); color: var(--text-primary); }
.budget-amount-input input:focus { outline: none; }

@media (max-width: 640px) {
  .budget-kpis { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .budget-group-lines { grid-template-columns: 1fr; }
  .budget-toolbar { gap: 10px; }
  .budget-period-nav { flex: 1; justify-content: center; }
}

/* Budget node editor — the optional monthly-plan split (the one bespoke
   Budgets surface; reuses .budget-amount-* + .asset-cat-swatch* atoms). */
.budget-split-section { padding-top: 2px; }
.budget-split-head {
  display: flex; align-items: center; gap: 8px; width: 100%;
  background: transparent; border: 0; padding: 4px 0; cursor: pointer; text-align: left;
  font: 700 var(--text-sm) / 1 var(--body); color: var(--text-primary);
}
.budget-split-chev { width: 16px; height: 16px; flex-shrink: 0; color: var(--text-muted); transform: rotate(-90deg); transition: transform 0.15s; }
.budget-split-section.is-open .budget-split-chev { transform: rotate(0deg); }
.budget-split-head-opt { font-weight: 500; color: var(--text-muted); margin-left: 4px; }
.budget-split-body { margin-top: 10px; }
.budget-split-toolbar { display: flex; align-items: center; justify-content: space-between; gap: 10px; margin-bottom: 10px; flex-wrap: wrap; }
.budget-split-even { padding: 7px 12px; font-size: var(--text-xs); }
.budget-split-sum { font: 600 var(--text-xs) / 1.3 var(--body); color: var(--text-secondary); }
.budget-split-sum.is-ok    { color: #16a34a; }
.budget-split-sum.is-over  { color: #dc2626; }
.budget-split-sum.is-under { color: var(--text-secondary); }
.budget-split-grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 8px 10px; }
.budget-split-row { display: flex; align-items: center; gap: 8px; }
.budget-split-month { width: 30px; flex-shrink: 0; font: 600 var(--text-xs) / 1 var(--body); color: var(--text-muted); }
.budget-split-input { flex: 1; min-width: 0; padding: 0 10px; }
.budget-split-input input { padding: 8px 4px; font-size: var(--text-sm); }
@media (max-width: 599px) {
  .budget-split-grid { grid-template-columns: 1fr; }
}

/* Budget pot TREE (the dashboard monitor view) — a collapsible tree of
   bar-cards (.budget-line reused); folders indent their children. */
.budget-tree { display: flex; flex-direction: column; gap: 10px; }
.budget-node { display: flex; flex-direction: column; gap: 10px; }
.budget-node-kids {
  display: flex; flex-direction: column; gap: 10px;
  margin-left: 16px; padding-left: 12px; border-left: 2px solid var(--border);
}
.budget-pot-title { gap: 8px; }
.budget-pot-toggle {
  display: grid; place-items: center; width: 22px; height: 22px; flex-shrink: 0;
  background: transparent; border: 0; padding: 0; color: var(--text-muted); cursor: pointer;
}
.budget-pot-chev { width: 16px; height: 16px; transform: rotate(-90deg); transition: transform 0.15s; }
.budget-pot-toggle.is-open .budget-pot-chev { transform: rotate(0deg); }
.budget-pot-dot { width: 9px; height: 9px; border-radius: 50%; background: var(--c, var(--text-muted)); flex-shrink: 0; }
.budget-pot-name {
  font: 700 var(--text-base) / 1.2 var(--display, var(--body)); color: var(--text-primary);
  min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.budget-pot-count {
  font: 600 11px / 1 var(--body); color: var(--text-muted);
  background: var(--bg-elevated); border: 1px solid var(--border); border-radius: 999px;
  padding: 2px 7px; flex-shrink: 0;
}
.budget-manage-btn { margin-left: auto; gap: 6px; }
/* The toolbar + empty-state action icons have no intrinsic size — constrain them
   (the period arrows + edit pencil have their own sizing and aren't .callout-btn). */
.budget-page .callout-btn svg { width: 17px; height: 17px; flex-shrink: 0; }
@media (max-width: 599px) {
  .budget-node-kids { margin-left: 8px; padding-left: 8px; }
}

/* Budget pot DRILL-IN — status bar + month folders (.tasks-month-* reused) +
   task rows. */
.budget-detail-modal { max-width: 560px; }
.budget-detail-modal .callout-modal-head h2 { display: flex; align-items: center; gap: 8px; min-width: 0; }
.budget-detail-year { font: 600 var(--text-xs) / 1 var(--body); color: var(--text-muted); }
.budget-detail-status { margin-bottom: 14px; }
.budget-detail-kpis { display: flex; flex-wrap: wrap; gap: 4px 14px; margin-top: 8px; font: 500 var(--text-sm) / 1.3 var(--body); color: var(--text-secondary); }
.budget-detail-kpis b { color: var(--text-primary); font-weight: 700; }
.budget-detail-kpis .is-good b { color: #16a34a; }
.budget-detail-kpis .is-over b { color: #dc2626; }
.budget-detail-months { display: flex; flex-direction: column; gap: 8px; }
.budget-month-sum { font: 600 var(--text-xs) / 1 var(--body); color: var(--text-secondary); white-space: nowrap; }
.budget-month-sum.is-over { color: #dc2626; }
.budget-month-list { display: flex; flex-direction: column; gap: 6px; margin-top: 6px; }
.budget-detail-month-empty, .budget-detail-empty { font: 500 var(--text-sm) / 1.4 var(--body); color: var(--text-muted); padding: 8px 2px; }
.budget-task-row {
  display: flex; align-items: center; gap: 10px; width: 100%; text-align: left;
  background: var(--bg-surface); border: 1px solid var(--border); border-radius: var(--radius-md);
  padding: 10px 12px; cursor: pointer; font-family: inherit;
  transition: border-color 0.12s, background 0.12s;
}
.budget-task-row:hover { border-color: var(--accent); background: color-mix(in srgb, var(--accent) 4%, var(--bg-surface)); }
.budget-task-dot { width: 8px; height: 8px; border-radius: 50%; background: var(--c, var(--text-muted)); flex-shrink: 0; }
.budget-task-title { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font: 600 var(--text-sm) / 1.3 var(--body); color: var(--text-primary); }
.budget-task-cost { font: 700 var(--text-sm) / 1 var(--body); color: var(--text-primary); white-space: nowrap; }
.budget-task-move {
  display: grid; place-items: center; width: 30px; height: 30px; flex-shrink: 0;
  background: transparent; border: 0; border-radius: 7px; color: var(--text-muted); cursor: pointer;
  transition: background 0.12s, color 0.12s;
}
.budget-task-move:hover { background: var(--bg-hover); color: var(--text-primary); }
.budget-task-move svg { width: 16px; height: 16px; }
.budget-task-kind { display: inline-grid; place-items: center; color: var(--text-muted); flex-shrink: 0; }
.budget-task-kind svg { width: 13px; height: 13px; }

/* Expand caret on a budget row — reveals that job's cost ledger inline. */
.budget-task-expand {
  display: grid; place-items: center; width: 28px; height: 28px; flex-shrink: 0;
  background: transparent; border: 0; border-radius: 7px; color: var(--text-muted); cursor: pointer;
  transition: background 0.12s, color 0.12s, transform 0.18s;
}
.budget-task-expand:hover { background: var(--bg-hover); color: var(--text-primary); }
.budget-task-expand svg { width: 16px; height: 16px; }
.budget-task-expand.is-open { transform: rotate(180deg); color: var(--accent); }

/* Inline cost ledger under a budget row (the shared AngelHubCostLedger, compact). */
.budget-task-costs {
  margin: -2px 0 8px; padding: 8px 10px 4px;
  background: var(--bg-elevated);
  border: 1px solid var(--border-subtle, rgba(255,255,255,0.05));
  border-radius: 10px; overflow-x: auto;
}
.budget-costs-loading { color: var(--text-muted); font: 400 var(--text-xs) / 1.4 var(--body); padding: 6px 2px; }

/* Desktop drag-to-pot rail in the drill-in (hidden on mobile — the Move button +
   picker cover touch). Drag a job row onto a pot chip to reassign it. */
.budget-move-rail {
  position: sticky; top: 0; z-index: 1;
  display: flex; flex-direction: column; gap: 7px;
  margin: 0 0 12px; padding: 10px 12px;
  border: 1px dashed var(--border-strong, var(--border)); border-radius: var(--radius-md);
  background: var(--bg-surface);
}
.budget-move-rail-label { font: 600 var(--text-xs) / 1.3 var(--body); color: var(--text-muted); }
.budget-move-rail-pots { display: flex; flex-wrap: wrap; gap: 6px; }
.budget-move-pot {
  display: inline-flex; align-items: center; gap: 6px;
  padding: 5px 10px; border-radius: var(--radius-pill);
  border: 1px solid var(--border); background: var(--bg-elevated);
  color: var(--text-secondary); font: 600 var(--text-xs) / 1 var(--body); cursor: default;
  transition: border-color 0.12s, background 0.12s, color 0.12s;
}
.budget-move-pot.is-drop-target { border-color: var(--accent); background: color-mix(in srgb, var(--accent) 14%, var(--bg-elevated)); color: var(--text-primary); }
.budget-task-row[draggable="true"] { cursor: grab; }
.budget-task-row.is-dragging { opacity: 0.45; }
@media (max-width: 599px) { .budget-move-rail { display: none; } }

/* Project pot chip on the dashboard Projects lens — a calm neutral tag naming
   the pot jobs in this project default to (two-tier card rule: neutral = elevated
   bg, subtle border, secondary text). */
.budget-proj-pot {
  display: inline-flex; align-items: center; flex-shrink: 0;
  padding: 2px 8px; border-radius: var(--radius-pill);
  background: var(--bg-elevated); border: 1px solid var(--border);
  color: var(--text-secondary); font: 600 var(--text-xs) / 1 var(--body);
  white-space: nowrap; max-width: 150px; overflow: hidden; text-overflow: ellipsis;
}

/* "Unassigned / other years" safety line — spend tagged to a pot that isn't in
   this year's active list, surfaced so money is never silently lost (§5). */
.budget-orphan { margin-top: 4px; }
.budget-orphan-line {
  display: flex; align-items: baseline; justify-content: space-between; gap: 10px;
  padding: 10px 12px; border: 1px dashed var(--border); border-radius: var(--radius-md);
  background: var(--bg-surface);
}
.budget-orphan-label { font: 600 var(--text-sm) / 1.3 var(--body); color: var(--text-secondary); }
.budget-orphan-val { font: 500 var(--text-xs) / 1.3 var(--body); color: var(--text-muted); white-space: nowrap; }

/* Task+call-out pair chip in the drill-in — a quiet amber nudge to check the
   same invoice wasn't entered on both sides of one job. Calm tint, not loud. */
.budget-task-pair {
  display: inline-flex; align-items: center; flex-shrink: 0;
  padding: 2px 7px; border-radius: var(--radius-pill);
  background: color-mix(in srgb, var(--rag-warn) 14%, transparent);
  color: var(--rag-warn); font: 600 var(--text-xs) / 1 var(--body); white-space: nowrap;
}
/* Audit-raised marker on a drill-in task row — calm neutral context chip
   (§4 two-tier rule): elevated bg + border, muted text, colour only on nothing
   loud. Marks the task wherever it's allocated, without competing with the
   act-now status dot/cost. */
.budget-task-audit {
  display: inline-flex; align-items: center; gap: 4px; flex-shrink: 0;
  padding: 2px 7px; border-radius: var(--radius-pill);
  background: var(--bg-elevated); border: 1px solid var(--border);
  color: var(--text-secondary); font: 600 var(--text-xs) / 1 var(--body); white-space: nowrap;
}
.budget-task-audit svg { width: 12px; height: 12px; flex-shrink: 0; color: var(--text-muted); }

/* Pot name in the drill-in title (modal head + workspace heading) — clips long
   names so the year chip + close button stay put. */
.budget-detail-potname { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.budget-detail-head-actions { display: flex; align-items: center; gap: 8px; flex-shrink: 0; }
.budget-detail-addtask { padding: 7px 12px; font-size: var(--text-xs); gap: 5px; }
.budget-detail-addtask svg { width: 15px; height: 15px; flex-shrink: 0; }

/* ── DESKTOP in-page workspace (≥1024px; kills the 560px modal on desktop) ── */
.budget-detail-page { display: flex; flex-direction: column; gap: 14px; }
.budget-detail-topbar { display: flex; align-items: center; gap: 12px; }
.budget-detail-back {
  display: inline-flex; align-items: center; gap: 6px; flex-shrink: 0;
  background: var(--bg-surface); border: 1px solid var(--border); border-radius: var(--radius-md);
  padding: 8px 12px 8px 9px; color: var(--text-secondary); font: 600 var(--text-sm) / 1 var(--body); cursor: pointer;
  transition: border-color 0.12s, color 0.12s;
}
.budget-detail-back:hover { border-color: var(--accent); color: var(--text-primary); }
.budget-detail-back svg { width: 16px; height: 16px; }
.budget-detail-heading {
  flex: 1; min-width: 0; display: flex; align-items: center; gap: 8px;
  font: 800 var(--text-lg) / 1.2 var(--display, var(--body)); color: var(--text-primary);
}
.budget-detail-topbar-actions { display: flex; align-items: center; gap: 8px; margin-left: auto; flex-shrink: 0; }
.budget-workspace { display: flex; flex-direction: column; gap: 14px; }
.budget-workspace .budget-detail-status { margin-bottom: 0; }
.budget-workspace .budget-detail-months { gap: 0; }

/* Months HERO TABLE — rows = months, cols = Plan | Spent | Committed | Variance.
   A row expands that month's jobs inline. Numbers tabular + right-aligned. */
.budget-months-table {
  display: flex; flex-direction: column;
  border: 1px solid var(--border); border-radius: var(--radius-md); overflow: hidden;
  background: var(--bg-surface);
}
.budget-mt-head, .budget-mt-row {
  display: grid; grid-template-columns: minmax(150px, 2fr) repeat(4, minmax(78px, 1fr));
  align-items: center; gap: 10px;
}
.budget-mt-head {
  padding: 9px 16px; background: var(--bg-elevated); border-bottom: 1px solid var(--border);
  font: 700 var(--text-xs) / 1 var(--body); text-transform: uppercase; letter-spacing: 0.04em; color: var(--text-muted);
}
.budget-mt-head .budget-mt-num { justify-self: end; }
.budget-mt-row-group { border-bottom: 1px solid var(--border); }
.budget-mt-row-group:last-child { border-bottom: 0; }
.budget-mt-row {
  width: 100%; padding: 12px 16px; background: transparent; border: 0; text-align: left;
  font-family: inherit; color: var(--text-primary); cursor: pointer; transition: background 0.12s;
}
.budget-mt-row:hover:not(:disabled) { background: color-mix(in srgb, var(--accent) 5%, var(--bg-surface)); }
.budget-mt-row:disabled { cursor: default; }
.budget-mt-cell { min-width: 0; }
.budget-mt-month { display: flex; align-items: center; gap: 8px; font: 700 var(--text-sm) / 1.2 var(--body); }
.budget-mt-month-name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.budget-mt-chev { width: 15px; height: 15px; flex-shrink: 0; color: var(--text-muted); transform: rotate(-90deg); transition: transform 0.15s; }
.budget-mt-row[aria-expanded="true"] .budget-mt-chev { transform: rotate(0deg); }
.budget-mt-chev-spacer { width: 15px; flex-shrink: 0; }
.budget-mt-count {
  font: 600 11px / 1 var(--body); color: var(--text-muted);
  background: var(--bg-elevated); border: 1px solid var(--border); border-radius: 999px; padding: 2px 7px; flex-shrink: 0;
}
.budget-mt-num { justify-self: end; text-align: right; font: 700 var(--text-sm) / 1 var(--body); color: var(--text-primary); white-space: nowrap; font-variant-numeric: tabular-nums; }
.budget-mt-dim { color: var(--text-muted); font-weight: 500; }
.budget-mt-var { font-weight: 700; color: var(--text-secondary); }
.budget-mt-var.is-over { color: var(--rag-fail); }
.budget-mt-var.is-none { color: var(--text-muted); font-weight: 500; }
.budget-mt-jobs {
  display: flex; flex-direction: column; gap: 6px;
  padding: 4px 16px 14px; background: color-mix(in srgb, var(--bg-elevated) 45%, var(--bg-surface));
}
/* These containers set display:flex, which TIES the UA `[hidden]` rule on
   specificity and (author > UA) wins — so `hidden` alone wouldn't collapse them.
   Explicit rules make the surgical collapse actually hide (accordion months, the
   folder-tree kids, and the table's month jobs). */
.budget-mt-jobs[hidden],
.budget-month-list[hidden],
.budget-node-kids[hidden] { display: none; }

/* Toolbar right cluster (fold-all + Manage) + the Expand/Collapse-all button. */
.budget-toolbar-right { margin-left: auto; display: flex; align-items: center; gap: 8px; }
.budget-toolbar-right .budget-manage-btn { margin-left: 0; }
.budget-foldall { padding: 7px 12px; font-size: var(--text-xs); }

/* Bigger tap target on the tree folder chevron (Radek 2026-07-05) — grows the
   hit area without shifting the row (negative margins soak up the extra size). */
.budget-pot-toggle { width: 30px; height: 30px; margin: -5px 2px -5px -6px; }

/* "Show N more" chunker under a big month's job list (§5). */
.budget-more {
  width: 100%; margin-top: 2px; padding: 8px;
  background: var(--bg-elevated); border: 1px dashed var(--border); border-radius: var(--radius-md);
  color: var(--text-secondary); font: 600 var(--text-xs) / 1 var(--body); cursor: pointer;
  transition: border-color 0.12s, color 0.12s;
}
.budget-more:hover { border-color: var(--accent); color: var(--accent); }

/* Visible focus rings on every interactive budget element (§4). */
.budget-line:focus-visible,
.budget-pot-toggle:focus-visible,
.budget-line-edit:focus-visible,
.budget-period-arrow:focus-visible,
.budget-foldall:focus-visible,
.budget-task-row:focus-visible,
.budget-task-move:focus-visible,
.budget-task-expand:focus-visible,
.budget-move-pot:focus-visible,
.budget-more:focus-visible,
.budget-detail-back:focus-visible,
.budget-detail-addtask:focus-visible,
.tasks-month-header:focus-visible {
  outline: 2px solid var(--accent); outline-offset: 2px;
}
/* .budget-mt-row lives inside the table's overflow:hidden — inset the ring. */
.budget-mt-row:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }

/* ── Budget drill-in analytics: "where it went" (Prompt 24) ──────────────
   A calm supplementary card under the status bar — category stacked bar +
   legend, a by-source split (+ project-linked lens), and top contractors,
   all reflecting the selected month chip. Variables only → both themes free. */
.budget-analytics {
  background: var(--bg-surface); border: 1px solid var(--border);
  border-radius: var(--radius-md); padding: 12px 14px;
  display: flex; flex-direction: column; gap: 14px;
}
.budget-an-head { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
.budget-an-title { font: 700 var(--text-sm) / 1 var(--display, var(--body)); color: var(--text-primary); }
.budget-an-months {
  margin-left: auto; display: flex; gap: 6px; overflow-x: auto;
  max-width: 100%; scrollbar-width: none; -webkit-overflow-scrolling: touch;
}
.budget-an-months::-webkit-scrollbar { display: none; }
.budget-an-mchip {
  flex-shrink: 0; padding: 5px 11px; border-radius: 999px; cursor: pointer;
  font: 600 var(--text-xs) / 1 var(--body); white-space: nowrap;
  color: var(--text-secondary); background: var(--bg-elevated); border: 1px solid var(--border);
  transition: color 0.12s, background 0.12s, border-color 0.12s;
}
.budget-an-mchip:hover { border-color: var(--accent); color: var(--accent); }
.budget-an-mchip.is-on { color: #fff; background: var(--accent); border-color: var(--accent); }
.budget-an-body { display: flex; flex-direction: column; gap: 16px; }
.budget-an-block { display: flex; flex-direction: column; gap: 9px; }
.budget-an-sub {
  font: 700 var(--text-xs) / 1 var(--body); letter-spacing: 0.04em; text-transform: uppercase;
  color: var(--text-muted);
}
.budget-an-empty { color: var(--text-muted); font: 500 var(--text-sm) / 1.4 var(--body); padding: 4px 0; }

/* Category stacked bar + legend */
.budget-an-bar {
  display: flex; height: 12px; border-radius: 6px; overflow: hidden;
  background: var(--bg-elevated); gap: 1px;
}
.budget-an-seg { min-width: 2px; }
.budget-an-seg-unc { background: repeating-linear-gradient(45deg, color-mix(in srgb, var(--text-muted) 40%, transparent) 0 4px, transparent 4px 8px); }
.budget-an-legend { display: flex; flex-direction: column; gap: 6px; }
.budget-an-leg {
  display: grid; grid-template-columns: auto 1fr auto auto; align-items: baseline; gap: 8px;
  font: 500 var(--text-sm) / 1.2 var(--body);
}
.budget-an-leg-dot { width: 9px; height: 9px; border-radius: 3px; align-self: center; flex-shrink: 0; }
.budget-an-leg-dot.is-unc { background: repeating-linear-gradient(45deg, color-mix(in srgb, var(--text-muted) 55%, transparent) 0 3px, transparent 3px 6px); border: 1px solid var(--border); }
.budget-an-leg-label { color: var(--text-primary); min-width: 0; }
.budget-an-leg-note { color: var(--text-muted); font-size: var(--text-xs); margin-left: 6px; }
.budget-an-leg-amt { color: var(--text-primary); font-weight: 700; font-variant-numeric: tabular-nums; white-space: nowrap; }
.budget-an-leg-pct { color: var(--text-muted); font-size: var(--text-xs); font-variant-numeric: tabular-nums; min-width: 30px; text-align: right; }

/* By-source split + project-linked lens */
.budget-an-srcs { display: flex; flex-wrap: wrap; gap: 8px; }
.budget-an-src {
  flex: 1 1 120px; display: flex; flex-direction: column; gap: 2px;
  padding: 9px 11px; border-radius: var(--radius-sm);
  background: var(--bg-elevated); border: 1px solid var(--border);
}
.budget-an-src-val { font: 800 var(--text-md, 15px) / 1 var(--display, var(--body)); color: var(--text-primary); font-variant-numeric: tabular-nums; }
.budget-an-src-lbl { font: 600 var(--text-xs) / 1 var(--body); color: var(--text-secondary); }
.budget-an-src-sub { font: 500 var(--text-xs) / 1.3 var(--body); color: var(--text-muted); }
.budget-an-lens {
  display: flex; align-items: center; gap: 6px; margin-top: 2px;
  font: 500 var(--text-xs) / 1.4 var(--body); color: var(--text-secondary);
}
.budget-an-lens svg { width: 14px; height: 14px; flex-shrink: 0; color: var(--text-muted); }
.budget-an-lens b { color: var(--text-primary); font-weight: 700; }
.budget-an-lens-note { color: var(--text-muted); }

/* Top contractors / suppliers */
.budget-an-cons { display: flex; flex-direction: column; gap: 6px; }
.budget-an-con {
  display: grid; grid-template-columns: 1fr auto auto; align-items: baseline; gap: 10px;
  padding: 7px 10px; border-radius: var(--radius-sm);
  background: var(--bg-elevated); border: 1px solid var(--border);
  font: 500 var(--text-sm) / 1.2 var(--body);
}
.budget-an-con-name { color: var(--text-primary); font-weight: 600; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.budget-an-con-jobs { color: var(--text-muted); font-size: var(--text-xs); white-space: nowrap; }
.budget-an-con-amt { color: var(--text-primary); font-weight: 700; font-variant-numeric: tabular-nums; white-space: nowrap; }
.budget-an-mchip:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

/* ═══════════════════════════════════════════════════════════════════════
   CONTRACTORS — site-scoping polish. Cap content width so the list doesn't
   sprawl on wide monitors (matches the constrained feel of other modules),
   plus the detail site chip + the copy-to-site picker.
   ═══════════════════════════════════════════════════════════════════════ */
#module-view-contractors .asset-list-wrap { max-width: 1040px; }

.ctr-detail-site-chip {
  display: inline-flex; align-items: center; gap: 5px;
  font: 600 var(--text-xs, 11px) / 1 var(--body);
  padding: 4px 9px; border-radius: var(--radius-sm);
  color: var(--text-secondary);
  background: var(--bg-elevated);
  border: 1px solid var(--border);
  white-space: nowrap;
}
.ctr-detail-site-chip svg {
  width: 12px; height: 12px; flex-shrink: 0;
  color: var(--text-muted);
}

.ctr-copy-site-list { display: flex; flex-direction: column; gap: 8px; }
.ctr-copy-site-btn {
  display: flex; align-items: center; gap: 8px; width: 100%; text-align: left;
  padding: 12px 14px; border-radius: var(--radius-md);
  background: var(--bg-elevated); border: 1px solid var(--border);
  font: 600 var(--text-sm) / 1.2 var(--body); color: var(--text-primary); cursor: pointer;
  transition: background 0.12s, border-color 0.12s;
}
.ctr-copy-site-btn:hover { border-color: var(--accent); background: color-mix(in srgb, var(--accent) 6%, var(--bg-surface)); }
.ctr-copy-site-btn:disabled { opacity: 0.6; cursor: default; }

/* Contractor detail overlay — tighten to a contained, single-width column so
   the header, tabs and body all line up (previously the body capped at 760px
   under a 980px header, which read as a sprawling full-width page). Also give
   the tab row breathing room so it isn't jammed against the header divider. */
.ctr-overlay-inner { max-width: 880px; }
.ctr-panel-body { max-width: none; }
.ctr-overlay-inner .callout-detail-tab-bar { padding: 12px 18px 4px; }

/* ═══════════════════════════════════════════════════════════════════════
   SHARED — upcoming-events calendar (Dashboard + Compliance). Week strip on
   desktop, day agenda on mobile, with type-toggle chips + RAG event blocks.
   ═══════════════════════════════════════════════════════════════════════ */
.cal-wrap {
  background: var(--bg-surface); border: 1px solid var(--border);
  border-radius: var(--radius-lg, 14px); box-shadow: var(--shadow-card); overflow: hidden;
}
.cal-loading { padding: 32px; text-align: center; color: var(--text-muted); font-size: var(--text-sm); }

.cal-head { display: flex; align-items: center; justify-content: space-between; gap: 12px; padding: 14px 16px; border-bottom: 1px solid var(--border); }
.cal-head-left { display: flex; align-items: center; gap: 10px; min-width: 0; }
.cal-title { font: 700 var(--text-base) / 1.2 var(--display, var(--body)); color: var(--text-primary); }
.cal-due-pill { font: 700 11px / 1 var(--body); letter-spacing: 0.04em; text-transform: uppercase; color: var(--text-secondary); background: var(--bg-elevated); border: 1px solid var(--border); border-radius: var(--radius-sm); padding: 4px 9px; white-space: nowrap; }
.cal-nav { display: flex; align-items: center; gap: 6px; flex-shrink: 0; }
.cal-nav-btn { display: grid; place-items: center; width: 32px; height: 32px; background: transparent; border: 1px solid transparent; border-radius: 9px; color: var(--text-secondary); cursor: pointer; transition: background .12s, color .12s; }
.cal-nav-btn:hover { background: var(--bg-elevated); color: var(--text-primary); }
.cal-nav-btn:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.cal-nav-btn svg { width: 16px; height: 16px; }
.cal-today-btn { height: 32px; padding: 0 14px; background: var(--bg-surface); border: 1px solid var(--border); border-radius: 9px; font: 600 var(--text-sm) / 1 var(--body); color: var(--text-primary); cursor: pointer; transition: border-color .12s; }
.cal-today-btn:hover { border-color: var(--accent); }

.cal-controls { display: flex; align-items: center; justify-content: space-between; gap: 12px; flex-wrap: wrap; padding: 12px 16px; border-bottom: 1px solid var(--border); }
.cal-toggle { display: flex; flex-wrap: wrap; gap: 6px; }
.cal-chip { display: inline-flex; align-items: center; gap: 6px; padding: 6px 11px; border-radius: var(--radius-sm); background: var(--bg-elevated); border: 1px solid var(--border); font: 600 12px / 1 var(--body); color: var(--text-secondary); cursor: pointer; transition: background .12s, border-color .12s, color .12s; }
.cal-chip-dot { width: 8px; height: 8px; border-radius: 50%; background: var(--c, var(--text-muted)); opacity: .4; }
.cal-chip.is-on { color: var(--c, var(--accent)); border-color: color-mix(in srgb, var(--c, var(--accent)) 45%, transparent); background: color-mix(in srgb, var(--c, var(--accent)) 10%, transparent); }
.cal-chip.is-on .cal-chip-dot { opacity: 1; }
.cal-chip-all { --c: var(--accent); }
.cal-legend { display: flex; gap: 12px; flex-wrap: wrap; }
.cal-legend-item { display: inline-flex; align-items: center; gap: 5px; font: 500 11px / 1 var(--body); color: var(--text-muted); }
.cal-legend-dot { width: 8px; height: 8px; border-radius: 50%; }

.cal-strip-scroll { overflow-x: auto; -webkit-overflow-scrolling: touch; }
.cal-strip { display: flex; min-height: 220px; }
.cal-col { flex: 0 0 130px; border-right: 1px solid var(--border); display: flex; flex-direction: column; }
.cal-col:last-child { border-right: 0; }
.cal-col.is-today { background: color-mix(in srgb, var(--accent) 4%, transparent); }
.cal-col-overdue { flex-basis: 150px; background: color-mix(in srgb, var(--rag-fail-fill) 4%, transparent); }
.cal-col-head { padding: 10px 12px 8px; }
.cal-col-dow { display: block; font: 700 10px / 1 var(--body); letter-spacing: 0.06em; color: var(--text-muted); }
.cal-col-overdue .cal-col-dow { color: var(--rag-fail); }
.cal-col-num { display: block; margin-top: 3px; font: 800 18px / 1 var(--display, var(--body)); color: var(--text-primary); }
.cal-col.is-today .cal-col-num { color: var(--accent); }
.cal-col-body { padding: 4px 8px 12px; display: flex; flex-direction: column; gap: 6px; }

/* CALM (calm pass 2026-06-30): the event card is a NEUTRAL tile (like a Tasks
   card) — the RAG status rides ONLY on the 3px left edge (the at-a-glance scan)
   and the loud .cal-ev-pill (the single act-now signal). The whole-card --ss/--sb
   status tint was the brightness; removed. */
.cal-ev { display: flex; flex-direction: column; align-items: flex-start; gap: 6px; width: 100%; text-align: left; padding: 8px 9px; border-radius: 10px; cursor: pointer; font-family: inherit; background: var(--bg-elevated); border: 1px solid var(--border); border-left: 3px solid var(--s); transition: filter .12s, transform .1s; }
.cal-ev:hover { filter: brightness(0.98); }
.cal-ev:active { transform: scale(0.99); }
/* Title (hero) over a calm context line; the due-pill is the only loud signal. */
.cal-ev-body { display: flex; flex-direction: column; gap: 2px; min-width: 0; max-width: 100%; }
.cal-ev-title { font: 600 12.5px / 1.25 var(--body); color: var(--text-primary); overflow: hidden; text-overflow: ellipsis; display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; }
/* Calm context line — colour lives on the dept DOT only (two-tier card rule);
   "Dept · Type · SITE · Scheduled" stays neutral so it never competes. */
.cal-ev-sub { display: flex; align-items: center; gap: 5px; max-width: 100%; font: 500 11px / 1.2 var(--body); color: var(--text-muted); }
.cal-ev-sub-txt { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.cal-ev-dot { width: 7px; height: 7px; border-radius: 50%; flex: none; }
/* The RAG due-pill — the act-now signal. Tinted from the row's status colour so
   it pops off the fainter row background. */
.cal-ev-pill { flex: none; align-self: flex-start; font: 600 11px / 1 var(--body); padding: 4px 9px; border-radius: 999px; white-space: nowrap; color: var(--s); background: color-mix(in srgb, var(--s) 15%, transparent); border: 1px solid color-mix(in srgb, var(--s) 40%, transparent); }
/* Upcoming-from-template preview — a checklist not yet active. NO dashed coloured
   side edge (Radek 2026-06-30): a calm NEUTRAL grey left edge + a recessed
   (transparent) background + a muted grey pill, so it reads as "scheduled, not
   started" against the live cards without the loud dashed colour. */
.cal-ev.is-scheduled { border-left-color: var(--border); background: transparent; opacity: 0.95; }
.cal-ev.is-scheduled .cal-ev-pill { color: var(--text-muted); background: var(--bg-elevated); border-color: var(--border); }

.cal-agenda { padding: 10px 12px 14px; display: flex; flex-direction: column; gap: 14px; }
.cal-agenda-group { display: flex; flex-direction: column; gap: 7px; }
.cal-agenda-day-head { display: flex; align-items: center; gap: 8px; font: 700 11px / 1 var(--body); letter-spacing: 0.05em; text-transform: uppercase; color: var(--text-muted); padding: 4px 2px 8px; border-bottom: 1px solid var(--border); }
.cal-agenda-day-head.is-today { color: var(--accent); }
.cal-agenda-overdue-label { color: var(--rag-fail); }
.cal-agenda-count { margin-left: auto; color: var(--rag-fail); }
.cal-agenda-list { display: flex; flex-direction: column; gap: 7px; }
/* Mobile list: row — title + context on the left, due-pill hard right. */
.cal-agenda .cal-ev { flex-direction: row; align-items: center; gap: 10px; }
.cal-agenda .cal-ev-body { flex: 1; }
.cal-agenda .cal-ev-title { -webkit-line-clamp: 1; }
.cal-agenda .cal-ev-pill { align-self: center; margin-left: auto; }
.cal-empty { padding: 28px 16px; text-align: center; color: var(--text-muted); font-size: var(--text-sm); }
/* Mobile agenda overflow — first 15 rows show, the rest collapse behind this
   toggle (desktop column strip is never capped). */
.cal-agenda-more { display: flex; flex-direction: column; gap: 14px; }
.cal-agenda-more[hidden] { display: none; }
.cal-agenda-more-btn {
  align-self: stretch; margin-top: 2px; padding: 10px 12px;
  border: 1px solid var(--border); border-radius: var(--radius-md, 10px);
  background: var(--bg-elevated); color: var(--text-secondary);
  font: 600 var(--text-sm) / 1 var(--body); cursor: pointer;
  transition: border-color .12s, color .12s;
}
.cal-agenda-more-btn:hover { border-color: var(--accent); color: var(--accent); }
.cal-agenda-more-btn:active { transform: scale(0.99); }

@media (max-width: 640px) {
  .cal-legend { display: none; }
  .cal-controls { padding: 10px 12px; }
  .cal-head { padding: 12px; }
}

/* ═══════════════════════════════════════════════════════════════════════
   PROJECTS — list cards + detail (overview / plan / budget / documents).
   The plan tree reuses the .ctree-* styles.
   ═══════════════════════════════════════════════════════════════════════ */
#module-view-projects .asset-list-wrap { max-width: 1040px; }

/* Projects has THREE KPI cards (Projects / Active / Total budget) — keep them
   three-across at every width. Two are now clickable filter cards (.cl-kpi-card),
   which would otherwise pull in the shared four-card grid
   (.asset-stats-row:has(.cl-kpi-card)); this higher-specificity rule overrides
   it back to three. */
.project-wrap .asset-stats-row:has(.cl-kpi-card) { grid-template-columns: repeat(3, 1fr); }

.project-list { display: flex; flex-direction: column; gap: 10px; }
.project-card-row {
  display: flex; align-items: stretch; width: 100%; text-align: left; cursor: pointer;
  background: var(--bg-surface); border: 1px solid var(--border); border-radius: var(--radius-md);
  overflow: hidden; font-family: inherit; transition: border-color .12s, background .12s;
}
.project-card-row:hover { border-color: var(--accent); background: color-mix(in srgb, var(--accent) 4%, var(--bg-surface)); }
.project-card-bar { width: 5px; flex-shrink: 0; }
.project-card-main { flex: 1; min-width: 0; padding: 13px 14px; display: flex; flex-direction: column; gap: 6px; }
.project-card-top { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.project-card-name { font: 700 15px / 1.25 var(--display, var(--body)); color: var(--text-primary); }
.project-card-meta { display: flex; gap: 14px; flex-wrap: wrap; }
.project-card-meta-item { font: 500 12px / 1.3 var(--body); color: var(--text-secondary); }
.project-card-arrow { width: 18px; height: 18px; align-self: center; margin-right: 12px; color: var(--text-muted); flex-shrink: 0; }

.project-ref {
  display: inline-flex; align-items: center; font-family: var(--font-mono, ui-monospace, SFMono-Regular, Menlo, monospace);
  font-size: var(--text-xs); font-weight: 600; line-height: 1; letter-spacing: 0.04em; padding: 4px 9px;
  border-radius: var(--radius-sm); color: var(--text-secondary); background: var(--bg-elevated);
  border: 1px solid var(--border);
}
/* CALM (2026-06-29) — neutral chip + colour on the dot; BOTH the card and the
   detail header use this now (the -calm marker below is redundant — later sweep). */
.project-status-chip {
  display: inline-flex; align-items: center; gap: 6px; font: 500 11px / 1 var(--body); padding: 4px 9px;
  border-radius: var(--radius-sm); color: var(--text-primary);
  background: var(--bg-elevated);
  border: 1px solid var(--border);
}
.project-status-chip-calm {
  gap: 6px;
  font-weight: 500;
  color: var(--text-primary);
  background: var(--bg-elevated);
  border-color: var(--border);
}
.project-status-cdot {
  width: 7px; height: 7px; border-radius: 50%;
  background: var(--s, var(--text-muted)); flex-shrink: 0;
}

/* Detail */
/* CONSISTENCY: every project tab shares ONE content width (fills .main, capped
   ~1700 app-wide) — no per-tab max-width, so switching Overview / Plan / Calendar
   / Budget / Documents never jumps wider/narrower. Content that needs a readable
   line length caps ITSELF (e.g. .project-desc p, .project-cards), never the frame.
   (App-wide rule — see CLAUDE.md §3 + MEMORY.) */
.project-detail { display: flex; flex-direction: column; gap: 14px; }
.project-detail-head { display: flex; flex-direction: column; gap: 10px; }
.project-detail-navrow { display: flex; align-items: center; justify-content: space-between; gap: 8px; }
.project-detail-navrow-actions { display: flex; align-items: center; gap: 6px; }
.project-back {
  display: inline-flex; align-items: center; gap: 4px; padding: 6px 10px 6px 6px; flex-shrink: 0;
  background: var(--bg-surface); border: 1px solid var(--border); border-radius: var(--radius-md);
  font: 600 13px / 1 var(--body); color: var(--text-secondary); cursor: pointer; transition: border-color .12s, color .12s;
}
.project-back:hover { border-color: var(--accent); color: var(--text-primary); }
.project-back svg { width: 16px; height: 16px; }
.project-detail-toprow { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.project-detail-name { margin: 0; font: 800 22px / 1.2 var(--display, var(--body)); color: var(--text-primary); }
.project-tabs { align-self: flex-start; }

.project-overview { display: flex; flex-direction: column; gap: 16px; }
.project-cards { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 12px; max-width: 760px; }
.project-card { background: var(--bg-surface); border: 1px solid var(--border); border-radius: var(--radius-md); padding: 14px 16px; box-shadow: var(--shadow-card); }
.project-card-head { font: 700 var(--text-xs) / 1 var(--body); text-transform: uppercase; letter-spacing: 0.05em; color: var(--text-muted); }
.project-card-big { margin-top: 8px; font: 800 24px / 1 var(--display, var(--body)); color: var(--text-primary); }
.project-card-sub { margin-top: 8px; font: 500 12.5px / 1.4 var(--body); color: var(--text-secondary); }
.project-bar { height: 8px; border-radius: 5px; background: var(--bg-elevated); overflow: hidden; margin-top: 10px; }
.project-bar-fill { display: block; height: 100%; border-radius: 5px; }

/* List-card plan progress — "is it on track?" at a glance: a mini bar (RAG
   colour set inline from plan_worst) + "9/20 tasks" label. Rendered only when
   the denormalised counters exist (see list.js progressHtml_). */
.project-card-progress { display: flex; align-items: center; gap: 10px; margin-top: 2px; }
/* Bar fills the row up to the "N/M tasks" count (which pins the right end) —
   no fixed cap, so it never strands dead space on a wide card. No-op on mobile
   (the card is already narrow, so the bar was near-full there anyway). */
.project-card-progress .project-bar { flex: 1; margin-top: 0; }
.project-card-progress-label { flex-shrink: 0; white-space: nowrap; font: 600 11.5px / 1 var(--body); }

/* Completed/cancelled projects fold (Tasks month-group header reused) — a small
   gap separates it from the active list above. */
.project-completed-group { margin-top: 12px; }
.project-completed-group .project-list { margin-top: 10px; }

/* Project Files (Documents tab) — small cards in a responsive grid (NOT full
   width). Reuses the shared .tasks-detail-file-chip card; auto-fill keeps a lone
   file the same compact size instead of stretching across the row. The acts
   wrapper + neutral edit (rename) button are the only project-specific bits. */
.project-files-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(340px, 1fr)); gap: 10px; }
.tasks-detail-file-edit {
  flex-shrink: 0; width: 30px; height: 30px; display: grid; place-items: center;
  background: transparent; border: none; border-radius: var(--radius-sm, 8px);
  color: var(--text-muted); cursor: pointer; transition: color .15s ease, background .15s ease;
}
.tasks-detail-file-edit:hover { color: var(--accent); background: color-mix(in srgb, var(--accent) 10%, transparent); }
.tasks-detail-file-edit svg { width: 16px; height: 16px; }

/* Project team — staff + external contractors as member tiles */
.project-team { margin-top: 18px; }
.project-team-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(180px, 1fr)); gap: 8px; margin-top: 10px; }
.project-member { position: relative; display: flex; align-items: center; gap: 11px; padding: 10px 12px; background: var(--bg-surface); border: 1px solid var(--border); border-radius: var(--radius-md); }
.project-member-av { flex-shrink: 0; width: 38px; height: 38px; border-radius: 50%; display: grid; place-items: center; color: #fff; font: 700 13px/1 var(--body); }
.project-member-av.is-staff { background: var(--accent); }
.project-member-av.is-contractor { background: #ea580c; }
.project-member-av.is-asset { background: #0ea5e9; }
.project-member-av.is-folder { background: var(--kb-folder); }
.project-member-av.is-doc { background: var(--kb-doc); }
/* Clickable tile variant — used for the inline documents browser (open in place) */
.project-member.is-link { cursor: pointer; width: 100%; text-align: left; font: inherit; transition: border-color 0.12s, background 0.12s; }
.project-member.is-link:hover { border-color: var(--accent); background: color-mix(in srgb, var(--accent) 4%, var(--bg-surface)); }
.project-member-go { flex-shrink: 0; color: var(--text-muted); display: grid; place-items: center; }
.project-member-go svg { width: 16px; height: 16px; }
.project-docs-head { display: flex; align-items: center; gap: 10px; margin-bottom: 12px; }
.project-docs-back { display: inline-flex; align-items: center; gap: 5px; background: none; border: none; color: var(--text-secondary); font: 600 13px/1 var(--body); cursor: pointer; padding: 4px; }
.project-docs-back:hover { color: var(--text-primary); }
.project-docs-back svg { width: 16px; height: 16px; }

/* Plan "card tree" — desktop board of phase columns (folder⇄tree toggle) */
.ptree-board { display: grid; grid-template-columns: repeat(auto-fill, minmax(250px, 1fr)); gap: 14px; align-items: start; }
.ptree-col { background: var(--bg-surface); border: 1px solid var(--border); border-left: 3px solid var(--rag, var(--border)); border-radius: var(--radius-md); padding: 12px 12px 14px; box-shadow: var(--shadow-card); }
.ptree-col-head { display: flex; align-items: center; gap: 8px; }
.ptree-col-title { flex: 1; min-width: 0; font: 700 14.5px/1.3 var(--display, var(--body)); color: var(--text-primary); }
.ptree-col-sub { font: 600 11.5px/1.3 var(--body); margin-top: 4px; }
.ptree-col-body { display: flex; flex-direction: column; gap: 8px; margin-top: 12px; }
.ptree-card { display: flex; align-items: center; gap: 9px; width: 100%; text-align: left; background: var(--bg-elevated); border: 1px solid var(--border); border-radius: var(--radius-md); padding: 9px 10px; cursor: pointer; font: inherit; transition: border-color 0.12s, background 0.12s; }
.ptree-card:hover { border-color: var(--accent); background: color-mix(in srgb, var(--accent) 4%, var(--bg-elevated)); }
.ptree-card-body { flex: 1; min-width: 0; }
.ptree-card-title { font: 600 13px/1.3 var(--body); color: var(--text-primary); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.ptree-card-sub { font: 500 11px/1.3 var(--body); color: var(--text-muted); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
/* Plan board — drag a card into a phase column / sub-phase to reparent (desktop).
   Native HTML5 DnD is pointer-only, so this is inert on touch (cards stay tappable). */
.ptree-card[draggable="true"] { cursor: grab; }
.ptree-card.is-dragging { opacity: 0.45; }
/* Per-card Manage / remove pencil — same affordance the Folder view has, so a
   linked task / asset / document can be unlinked (or edited) straight from the
   board. A sibling button (not nested in the card button) tucked top-right; the
   card reserves right padding so its text never runs under it. */
.ptree-card-wrap { position: relative; }
.ptree-card-wrap .ptree-card { padding-right: 40px; }
.ptree-card-manage { position: absolute; top: 7px; right: 6px; }
@media (max-width: 640px) { .ptree-card-manage { width: 34px; height: 34px; } }
/* ▲▼ reorder pair (Prompt 74 — reuses .ctree-act, never hover-revealed §4).
   On board task cards the pair sits LEFT of the manage pencil; the wrap gets a
   --qor modifier so only arrow-carrying cards reserve the wider padding. The
   pair renders only when the run has ≥2 members; the edge button is disabled. */
.ptree-qor:disabled { opacity: 0.35; cursor: default; }
.ptree-qor:disabled:hover { background: transparent; color: var(--text-muted); }
.ptree-card-qor { position: absolute; top: 7px; right: 40px; display: flex; gap: 2px; }
.ptree-card-wrap--qor .ptree-card { padding-right: 104px; }
@media (max-width: 640px) {
  .ptree-card-qor { right: 46px; }
  .ptree-card-wrap--qor .ptree-card { padding-right: 118px; }
}
.ptree-col.is-drop-target,
.ptree-sub.is-drop-target { box-shadow: inset 0 0 0 2px var(--accent), var(--shadow-card); background: color-mix(in srgb, var(--accent) 9%, var(--bg-surface)); }
/* "Not in a phase" tray — top-level leaf cards (task / asset / doc) not yet
   filed under a phase. A full-width holding area at the top of the board: plain
   draggable cards (no +, no sections, no children) — drag one into a phase to
   file it, or back here to un-file. Dashed so it reads as a tray, not a phase. */
/* ── Prompt 72: inline quick-add (plan tree) ────────────────────────────────
   Ghost affordances that swap to a title-only input (type + Enter). Rendered
   at the folder-view root + inside every phase (both views) + as the board's
   trailing "Add phase" stub. Quiet by design — dashed, muted, tokens only. */
.ptree-qa { display: flex; align-items: center; gap: 8px; min-width: 0; }
.ptree-qa-btn {
  display: inline-flex; align-items: center; gap: 6px;
  padding: 8px 12px; border-radius: var(--radius-md);
  background: transparent; border: 1px dashed var(--border-strong);
  color: var(--text-secondary); font: 600 12.5px/1 var(--body); cursor: pointer;
  -webkit-tap-highlight-color: transparent; touch-action: manipulation;
  transition: border-color 0.12s, color 0.12s, background 0.12s;
}
.ptree-qa-btn:hover { border-color: var(--accent); color: var(--accent); background: var(--accent-soft); }
.ptree-qa-btn:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.ptree-qa-btn:active { transform: scale(0.98); }
.ptree-qa-btn svg { width: 14px; height: 14px; flex: none; }
.ptree-qa-input {
  flex: 1; min-width: 0; padding: 9px 12px; border-radius: var(--radius-md);
  background: var(--bg-surface); border: 1px solid var(--accent);
  color: var(--text-primary); font: 500 14px/1.3 var(--body);
}
.ptree-qa-input::placeholder { color: var(--text-muted); }
.ptree-qa-input:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
/* Board tail: the Add-phase stub is its own slim grid "column". */
.ptree-qa-col { align-self: start; }
.ptree-qa-col .ptree-qa-btn { width: 100%; justify-content: center; padding: 14px 12px; }
/* Empty state: centred runway under the "Build your project plan" card. */
.ptree-qa-empty { margin-top: 12px; display: flex; justify-content: center; }
.ptree-qa-empty .ptree-qa { width: 100%; max-width: 420px; justify-content: center; }
.ptree-qa-empty .ptree-qa-input { flex: 1; }

.ptree-loose { grid-column: 1 / -1; border: 1px dashed var(--border); border-radius: var(--radius-md); padding: 10px 12px 12px; background: color-mix(in srgb, var(--bg-surface) 55%, transparent); }
.ptree-loose-head { font: 600 11.5px/1.3 var(--body); color: var(--text-muted); margin-bottom: 9px; }
.ptree-loose-cards { display: flex; flex-wrap: wrap; gap: 10px; }
.ptree-loose-cards .ptree-card-wrap { width: 256px; max-width: 100%; }
.ptree-loose.is-drop-target { border-color: var(--accent); border-style: solid; background: color-mix(in srgb, var(--accent) 9%, var(--bg-surface)); }
@media (max-width: 640px) { .ptree-loose-cards .ptree-card-wrap { width: 100%; } }
/* Plan task-card meta chips (department / site / location) — reuse the Tasks
   card's .tasks-card-dept + .tasks-card-location; the row just needs breathing
   room in the compact plan card, and the site chip is a neutral variant. */
.ptree-chips { margin-top: 6px; gap: 4px; }
/* Plan-card chips match the app's ONE chip size (4px 9px / 600 var(--text-xs)) —
   they used to be shrunk here, which made the Plan tab read differently from the
   Tasks list. Standardised so a plan task card looks like a real task card. */
.ptree-chips .tasks-card-dept,
.ptree-chips .tasks-card-location,
.ptree-chips .ptree-site-chip { font-size: var(--text-xs); padding: 4px 9px; gap: 5px; }
.ptree-chips svg { width: 13px; height: 13px; }
.ptree-site-chip { display: inline-flex; align-items: center; gap: 5px; max-width: 100%; padding: 4px 9px; background: var(--bg-surface); border: 1px solid var(--border); border-radius: var(--radius-sm); color: var(--text-secondary); font: 600 var(--text-xs)/1 var(--body); }
.ptree-site-chip svg { width: 13px; height: 13px; flex-shrink: 0; }
/* Per-task budget line (allocated vs actual + variance). Used on task leaves
   in both the folder tree (.ctree-body) and the desktop card-tree (.ptree-card). */
.ptree-budget { font: 600 11.5px/1.3 var(--body); margin-top: 4px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.ctree-body .ptree-budget { font-size: 12px; margin-top: 5px; }

/* ── Asset leaves in the plan (added inside a phase; shown as photo cards) ──
   Folder view = compact thumbnail row; board view = photo-on-top tile.
   Placeholder reuses the register's .asset-gallery-ph (css/13). */
.ctree-card.is-asset { gap: 12px; }
.ctree-asset-thumb {
  flex: none; width: 44px; height: 44px; border-radius: var(--radius-sm);
  overflow: hidden; background: var(--bg-elevated);
  display: flex; align-items: center; justify-content: center;
}
.ctree-asset-thumb img { width: 100%; height: 100%; object-fit: cover; display: block; }
.ctree-asset-thumb.is-ph { color: var(--text-muted); }
.ctree-asset-thumb.is-ph svg { width: 20px; height: 20px; opacity: 0.6; }
/* Compact asset card (board view): small cover thumbnail + name + price, so the
   plan stays scannable. The full photo is one tap away on the asset's own page.
   Uses the default .ptree-card row layout (no column override). */
.ptree-asset-thumb { flex: none; width: 56px; height: 56px; border-radius: var(--radius-sm); overflow: hidden; background: var(--bg-elevated); display: flex; align-items: center; justify-content: center; }
.ptree-asset-thumb img { width: 100%; height: 100%; object-fit: cover; display: block; }
.ptree-asset-thumb.is-ph { color: var(--text-muted); }
.ptree-asset-thumb.is-ph svg { width: 24px; height: 24px; opacity: 0.6; }
/* Chosen-asset row in the add / edit node form */
.ctree-asset-chosen { display: flex; align-items: center; gap: 10px; }
.ctree-asset-chosen-name { flex: 1; min-width: 0; font: 600 14px/1.3 var(--body); color: var(--text-primary); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.ctree-kb-chosen-icon { flex: none; width: 30px; height: 30px; border-radius: var(--radius-sm); display: flex; align-items: center; justify-content: center; color: #fff; }
.ctree-kb-chosen-icon svg { width: 17px; height: 17px; }
.ctree-kb-chosen-icon.is-folder { background: var(--kb-folder); }
.ctree-kb-chosen-icon.is-doc { background: var(--kb-doc); }

/* ── KB leaves in the plan (a linked Knowledge Base document or folder) ──
   Folder view = icon-avatar row; board view = compact icon card. Avatar
   colours match the detail Documents tab (folder = amber, doc = slate). */
.ctree-card.is-kb { gap: 12px; }
.ctree-kb-icon { flex: none; width: 44px; height: 44px; border-radius: var(--radius-sm); display: flex; align-items: center; justify-content: center; color: #fff; }
.ctree-kb-icon svg { width: 22px; height: 22px; }
.ctree-kb-icon.is-folder { background: var(--kb-folder); }
.ctree-kb-icon.is-doc { background: var(--kb-doc); }
.ptree-card.ptree-kb .ptree-kb-icon { flex: none; width: 32px; height: 32px; border-radius: var(--radius-sm); display: flex; align-items: center; justify-content: center; color: #fff; }
.ptree-card.ptree-kb .ptree-kb-icon svg { width: 18px; height: 18px; }
.ptree-card.ptree-kb .ptree-kb-icon.is-folder { background: var(--kb-folder); }
.ptree-card.ptree-kb .ptree-kb-icon.is-doc { background: var(--kb-doc); }
.ptree-sub { margin-top: 2px; padding-left: 10px; border-left: 3px solid var(--rag, var(--border)); }
.ptree-sub-head { display: flex; align-items: center; gap: 7px; }
.ptree-sub-title { flex: 1; min-width: 0; font: 600 12.5px/1.3 var(--body); color: var(--text-secondary); }
.ptree-sub-body { display: flex; flex-direction: column; gap: 8px; margin-top: 8px; }
.ptree-empty { font: 500 12px/1.4 var(--body); color: var(--text-muted); padding: 10px 12px; text-align: center; border: 1px dashed var(--border); border-radius: var(--radius-sm); }

/* Collapsible phase sections (Tasks / Assets / Documents / Sub-phases) — shared
   by the board columns AND the folder outline. Collapsed by default; the body
   only renders when open. Reuses the plan's chevron-rotate collapse pattern. */
.plan-sec + .plan-sec { margin-top: 5px; }
/* The whole header is the tap target — a defined, finger-friendly pressable row
   (sits on the column's --bg-surface, so --bg-elevated reads as raised). */
.plan-sec-head {
  display: flex; align-items: center; gap: 9px; width: 100%;
  min-height: 42px;
  background: var(--bg-elevated); border: 1px solid var(--border); padding: 10px 12px; cursor: pointer;
  font-family: inherit; color: var(--text-secondary); text-align: left;
  border-radius: var(--radius-sm); transition: background .12s, color .12s;
}
.plan-sec-head:hover { background: var(--bg-hover); color: var(--text-primary); }
.plan-sec-head:active { background: var(--bg-hover); }
.plan-sec-head:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
.plan-sec-chev { width: 15px; height: 15px; flex: none; color: var(--text-muted); transition: transform .15s ease; }
.plan-sec-chev.is-open { transform: rotate(90deg); }
.plan-sec-label { flex: 1; min-width: 0; font: 700 11.5px/1 var(--body); text-transform: uppercase; letter-spacing: .04em; }
.plan-sec-count { flex: none; font: 700 10.5px/1 var(--body); color: var(--text-muted); background: var(--bg-surface); border: 1px solid var(--border); border-radius: 999px; padding: 3px 9px; }
.plan-sec-body { display: flex; flex-direction: column; gap: 8px; margin: 8px 0 6px; }
.plan-sec-note-row { display: flex; align-items: flex-start; gap: 6px; }
.plan-sec-note { flex: 1; min-width: 0; font: 500 12.5px/1.5 var(--body); color: var(--text-secondary); white-space: pre-wrap; padding: 4px 5px; }
.plan-sec-note-edit { flex: none; margin-top: 1px; }   /* the pencil icon-button (reuses .ctree-act) */

/* Bigger, finger-friendly section toggles on touch / small screens */
@media (max-width: 640px) {
  .plan-sec-head { min-height: 48px; padding: 12px 13px; gap: 11px; }
  .plan-sec-chev { width: 17px; height: 17px; }
  .plan-sec-label { font-size: 12.5px; }
}
.project-member-av svg { width: 18px; height: 18px; }
.project-member-info { flex: 1; min-width: 0; }
.project-member-name { font: 600 13.5px/1.3 var(--body); color: var(--text-primary); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.project-member-x { flex-shrink: 0; width: 26px; height: 26px; border-radius: 8px; border: none; background: transparent; color: var(--text-muted); display: grid; place-items: center; cursor: pointer; transition: background 0.12s, color 0.12s; }
.project-member-x:hover { background: var(--bg-hover); color: #dc2626; }
.project-member-x svg { width: 15px; height: 15px; }
.project-member-add { display: flex; align-items: center; justify-content: center; gap: 7px; min-height: 60px; padding: 10px 12px; background: transparent; border: 1px dashed var(--border); border-radius: var(--radius-md); color: var(--text-secondary); font: 600 13px/1 var(--body); cursor: pointer; transition: border-color 0.12s, color 0.12s, background 0.12s; }
.project-member-add:hover { border-color: var(--accent); color: var(--accent); background: color-mix(in srgb, var(--accent) 5%, transparent); }
.project-member-add svg { width: 16px; height: 16px; }

/* Prompt 71 — TEAM rows are compact (a name is one line, not a 60px card).
   Scoped to .project-team-grid: the Documents browser reuses .project-member
   at its original full size, so these overrides must never leak there. */
.project-team-grid .project-member { padding: 6px 9px; gap: 8px; }
.project-team-grid .project-member-av { width: 26px; height: 26px; font-size: 10px; }
.project-team-grid .project-member-av svg { width: 14px; height: 14px; }
.project-team-grid .project-member-name { font-size: 13px; }
.project-team-grid .project-member-x { width: 30px; height: 30px; }   /* denser row → slightly BIGGER remove target */
.project-team-grid .project-member-add { min-height: 40px; padding: 6px 12px; }

/* Prompt 71 — Overview "at a glance": ONE calm card under the KPI strip with
   the slim progress/budget bars + one-line stories (replaces the two big
   number cards that repeated the strip). Reuses .project-card + .project-bar. */
.project-glance { max-width: 760px; display: flex; flex-direction: column; gap: 13px; }
.project-glance-top { display: flex; align-items: baseline; justify-content: space-between; gap: 10px; min-width: 0; }
.project-glance-label { font: 700 var(--text-xs) / 1 var(--body); text-transform: uppercase; letter-spacing: 0.05em; color: var(--text-muted); white-space: nowrap; }
.project-glance-sub { font: 500 12.5px / 1.3 var(--body); color: var(--text-secondary); text-align: right; min-width: 0; }
.project-glance-line .project-bar { margin-top: 7px; }

/* Prompt 71 — Plan snapshot on Overview: phases at a glance (zero-read rows
   into the Plan tab). Real <button> rows → keyboard + focus ring for free. */
.project-plansnap { margin-top: 18px; max-width: 760px; }
.project-plansnap-list { display: flex; flex-direction: column; gap: 6px; margin-top: 10px; }
.project-plansnap-row { display: flex; align-items: center; gap: 10px; width: 100%; text-align: left; padding: 9px 12px; background: var(--bg-surface); border: 1px solid var(--border); border-radius: var(--radius-md); cursor: pointer; font: inherit; color: inherit; transition: border-color 0.12s, background 0.12s; }
.project-plansnap-row:hover { border-color: var(--accent); background: color-mix(in srgb, var(--accent) 4%, var(--bg-surface)); }
.project-plansnap-dot { flex-shrink: 0; width: 9px; height: 9px; border-radius: 50%; }
.project-plansnap-name { flex: 1; min-width: 0; font: 600 13.5px / 1.3 var(--body); color: var(--text-primary); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.project-plansnap-count { flex-shrink: 0; font: 600 12px / 1 var(--body); color: var(--text-secondary); }
.project-plansnap-more { margin-top: 8px; font: 500 12px / 1.3 var(--body); color: var(--text-muted); }

.project-meta-grid { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 1px; background: var(--border); border: 1px solid var(--border); border-radius: var(--radius-md); overflow: hidden; }
.project-meta-item { background: var(--bg-surface); padding: 12px 14px; display: flex; flex-direction: column; gap: 4px; }
.project-meta-label { font: 600 var(--text-xs) / 1 var(--body); text-transform: uppercase; letter-spacing: 0.05em; color: var(--text-muted); }
.project-meta-val { font: 600 13.5px / 1.3 var(--body); color: var(--text-primary); }
.project-section-label { font: 700 var(--text-sm) / 1 var(--body); text-transform: uppercase; letter-spacing: 0.05em; color: var(--text-muted); margin-bottom: 8px; }
.project-desc p { margin: 0; max-width: 72ch; font: 500 14px / 1.6 var(--body); color: var(--text-secondary); white-space: pre-wrap; }

/* The "Who can see this" audience chooser styles (.pvis-*) moved to
   css/19-audience-picker.css when the chooser became a shared component. */

/* Visibility chip in the project detail header — calm/neutral context chip
   (two-tier card rule): who can see this project. Display-only. */
.project-vis-chip { display: inline-flex; align-items: center; gap: 5px; padding: 4px 9px; background: var(--bg-elevated); border: 1px solid var(--border); border-radius: var(--radius-sm); font: 600 var(--text-xs) / 1 var(--body); color: var(--text-secondary); white-space: nowrap; }
.project-vis-chip svg { width: 13px; height: 13px; flex-shrink: 0; color: var(--text-muted); }
/* Ownership chip dept dot — colour lives ONLY on the dot (two-tier card rule). */
.project-home-dot { width: 8px; height: 8px; border-radius: 50%; flex-shrink: 0; }

.project-budget-kpis { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 10px; }
.project-kpi { background: var(--bg-surface); border: 1px solid var(--border); border-radius: var(--radius-md); padding: 12px 14px; box-shadow: var(--shadow-card); }
.project-kpi-label { font: 700 var(--text-xs) / 1 var(--body); text-transform: uppercase; letter-spacing: 0.05em; color: var(--text-muted); }
.project-kpi-val { margin-top: 7px; font: 800 19px / 1 var(--display, var(--body)); color: var(--text-primary); }
.project-kpi.is-over .project-kpi-val { color: #dc2626; }
.project-kpi.is-good .project-kpi-val { color: #16a34a; }
/* Per-task budget table on the Budget tab — task title + allocated/spent +
   variance, RAG-coded. Reuses the surface/border tokens; mirrors the plan
   card budget line so the two readouts match. */
.project-budget-rows { display: flex; flex-direction: column; gap: 8px; }
.project-budget-row { display: flex; align-items: center; gap: 10px; background: var(--bg-surface); border: 1px solid var(--border); border-radius: var(--radius-md); padding: 10px 12px; box-shadow: var(--shadow-card); }
.project-budget-row-main { flex: 1; min-width: 0; }
.project-budget-row-title { font: 600 13px / 1.3 var(--body); color: var(--text-primary); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.project-budget-row-sub { font: 600 12px / 1.3 var(--body); margin-top: 3px; }

@media (max-width: 640px) {
  #module-view-projects .asset-list-wrap { max-width: 100%; }
  .project-cards { grid-template-columns: 1fr; }
  .project-meta-grid { grid-template-columns: 1fr 1fr; }
  .project-budget-kpis { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

/* ═══════════════════════════════════════════════════════════════════════
   SAFETY — RA / COSHH / SSoW / H&S library. Uniform chips, hazard cards,
   5×5 risk ratings, sign-off. Reuses .project-* detail + .callout-* form.
   ═══════════════════════════════════════════════════════════════════════ */
/* Safety fills the content width (the list below is a responsive grid). */
#module-view-safety .asset-list-wrap { max-width: none; }

/* Two-tier card rule: the TYPE classifier is a calm NEUTRAL chip — the
   doc-type colour lives only on its small dot (the compliance-register
   pattern). Review / risk chips below the title stay loud: they're the
   act-now signals. One chip size everywhere: 4px 9px, 600 var(--text-xs). */
.safety-type-chip {
  display: inline-flex; align-items: center; gap: 6px;
  font: 600 var(--text-xs) / 1 var(--body); padding: 4px 9px;
  border-radius: var(--radius-sm); white-space: nowrap;
  color: var(--text-secondary); background: var(--bg-elevated);
  border: 1px solid var(--border);
}
.safety-type-dot { width: 7px; height: 7px; border-radius: var(--radius-full); background: var(--s, var(--text-muted)); flex-shrink: 0; }
.safety-type-chip svg { width: 12px; height: 12px; flex-shrink: 0; } /* site/globe context chips */
.safety-other-sites-label { margin-top: 18px; }
/* Signal chips: --s = readable text token, --sf = the fill hue for tints. */
.safety-review-chip, .safety-risk-chip {
  display: inline-flex; align-items: center; font: 600 var(--text-xs) / 1 var(--body); padding: 4px 9px;
  border-radius: var(--radius-sm); white-space: nowrap;
  color: var(--s, var(--text-secondary));
  background: color-mix(in srgb, var(--sf, var(--s, var(--border))) 12%, transparent);
  border: 1px solid color-mix(in srgb, var(--sf, var(--s, var(--border))) 38%, transparent);
}

/* List cards */
/* Desktop: safety items fan into a responsive card grid (cards are already
   full-width, so they just flow into columns); auto-collapses to 1 col on mobile. */
.safety-list { display: grid; grid-template-columns: repeat(auto-fill, minmax(320px, 1fr)); gap: 10px; align-items: start; }
.safety-card { display: flex; align-items: stretch; width: 100%; text-align: left; cursor: pointer; background: var(--bg-surface); border: 1px solid var(--border); border-radius: var(--radius-md); overflow: hidden; font-family: inherit; transition: border-color .12s, background .12s; }
.safety-card:hover { border-color: var(--accent); background: color-mix(in srgb, var(--accent) 4%, var(--bg-surface)); }
/* (The old full-height coloured left bar was removed — it doubled the type
   colour the chip dot already carries; two-tier rule keeps context calm.) */
.safety-card-main { flex: 1; min-width: 0; padding: 13px 14px; display: flex; flex-direction: column; gap: 6px; }
.safety-card-top { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
/* Classifier group (category + type) pinned top-right as ONE cluster —
   a single auto margin, so two chips never spread across the free space. */
.safety-card-top .safety-card-chips { margin-left: auto; display: flex; gap: 6px; flex-wrap: wrap; justify-content: flex-end; flex-shrink: 0; }
.safety-cat-toggle { margin-top: 6px; }
/* The one-line "shared with Compliance/Safety" note at the top of BOTH
   category managers — the two modules edit the same taxonomy. */
.sc-cat-shared-note { margin: 0 0 12px; font: 500 12.5px / 1.4 var(--body); color: var(--text-secondary); }
.safety-card-signals { display: flex; gap: 8px; flex-wrap: wrap; } /* loud signal chips below the title */
.safety-card-name { font: 700 15px / 1.25 var(--display, var(--body)); color: var(--text-primary); }
.safety-card-meta { display: flex; gap: 14px; flex-wrap: wrap; }
.safety-card-meta-item { font: 500 12px / 1.3 var(--body); color: var(--text-secondary); }

/* Builder — matrix legend + hazard cards */
.safety-matrix-legend { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; margin-bottom: 12px; font: 500 12px / 1.3 var(--body); color: var(--text-secondary); }
/* Hazard card pops OFF the grey section (surface fill + strong edge — it was
   --bg-elevated on --bg-elevated, an invisible box); its inputs flip to the
   elevated fill so they read as boxes ON the card (the app-wide grey-on-white
   field law, inverted with the card). */
.safety-haz { background: var(--bg-surface); border: 1px solid var(--border-strong); border-radius: var(--radius-md); padding: 12px; display: flex; flex-direction: column; gap: 8px; margin-bottom: 10px; }
.safety-haz-head { display: flex; align-items: center; justify-content: space-between; }
.safety-haz-num { font: 700 12px / 1 var(--body); text-transform: uppercase; letter-spacing: .04em; color: var(--text-secondary); }
.safety-haz-del { display: grid; place-items: center; width: 26px; height: 26px; background: transparent; border: 0; border-radius: 6px; color: var(--text-muted); cursor: pointer; }
.safety-haz-del:hover { background: var(--bg-hover); color: var(--rag-fail); }
.safety-haz-del svg { width: 14px; height: 14px; }
.safety-haz input[type="text"], .safety-haz textarea, .safety-haz select {
  width: 100%; padding: 9px 11px; background: var(--bg-elevated); border: 1px solid var(--border-input);
  border-radius: var(--radius-sm); font-family: inherit; font-size: var(--text-sm); color: var(--text-primary);
}
.safety-haz input:focus, .safety-haz textarea:focus, .safety-haz select:focus { outline: none; border-color: var(--accent); box-shadow: 0 0 0 3px var(--accent-soft); }
.safety-haz-score { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; }
.safety-haz-lvl { display: flex; align-items: center; gap: 6px; font: 600 12px / 1 var(--body); color: var(--text-secondary); }
.safety-haz-lvl select { width: auto; padding: 6px 8px; }
.safety-add-haz { width: 100%; }
/* Add hazard / Add step / + Add file — the transparent .callout-btn-secondary
   melted into the grey section panel. Surface fill + input border makes them
   read as real tappable affordances; hover takes the checklist builder's
   .cl-add-item-btn accent tint (the reference add-affordance). */
.safety-add-haz,
.safety-file-row .callout-btn-secondary {
  background: var(--bg-surface);
  border-color: var(--border-input);
  color: var(--text-secondary);
}
.safety-add-haz:hover,
.safety-file-row .callout-btn-secondary:hover {
  border-color: var(--accent);
  background: color-mix(in srgb, var(--accent) 8%, var(--bg-surface));
  color: var(--text-primary);
}
.safety-file-row { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
.safety-file-name { font: 500 12px / 1.3 var(--body); color: var(--text-muted); }
/* P4: hazard action owner + due (HSE template columns) */
.safety-haz-action-edit { display: flex; gap: 8px; }
.safety-haz-action-edit input[type="text"] { flex: 1; min-width: 0; }
.safety-haz-action-edit input[type="date"] { width: auto; flex-shrink: 0; }
/* P4: quick multi-select pills (persons at risk / exposure routes) */
.safety-chip-row { margin-bottom: 8px; }
/* 10 doc-type pills: natural-width wrap — the checklist 5-col grid crushed
   long labels ("Fire risk assessment") into ~66px columns on phones. Row
   direction (base .ctree-lt is a COLUMN tile) so the type's identity colour
   dot (.safety-type-dot, the register-chip pattern) sits beside the label. */
.callout-modal [data-type-row].ctree-lt-row,
.callout-modal .safety-chip-row.ctree-lt-row { display: flex; flex-wrap: wrap; gap: 6px; }
.callout-modal [data-type-row] .ctree-lt,
.callout-modal .safety-chip-row .ctree-lt { flex: 0 1 auto; width: auto; flex-direction: row; }
/* P4: multi-attachment list */
.safety-docs-list { display: flex; flex-direction: column; gap: 6px; margin-bottom: 8px; }
.safety-doc-row { display: flex; align-items: center; justify-content: space-between; gap: 8px; padding: 7px 10px; background: var(--bg-surface); border: 1px solid var(--border-strong); border-radius: var(--radius-sm); }
.safety-doc-row.is-pending { border-style: dashed; }
.safety-doc-row .safety-file-name { color: var(--text-secondary); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.safety-docs-view { display: flex; flex-wrap: wrap; gap: 8px; }
/* SSoW ordered method cards (Prompt 115). */
[data-ss-list] { display: grid; gap: 14px; }
.safety-step-card {
  min-width: 0;
  padding: 16px;
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  background: var(--bg-elevated);
}
.safety-step-card-head,
.safety-step-media-foot {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
}
.safety-step-card-head { margin-bottom: 10px; }
.safety-step-number { display: flex; align-items: baseline; gap: 7px; color: var(--text-secondary); font: 650 var(--text-md) / 1 var(--body); }
.safety-step-number strong { color: var(--text-primary); font-size: 26px; line-height: 1; }
.safety-step-remove,
.safety-step-add-photo { min-height: 44px; gap: 7px; }
.safety-step-remove svg,
.safety-step-add-photo svg { width: 18px; height: 18px; flex: 0 0 18px; }
.safety-step-instruction,
.safety-step-photo-caption > span {
  display: block;
  margin-bottom: 7px;
  color: var(--text-secondary);
  font: 650 var(--text-md) / 1.3 var(--body);
}
.safety-step-card > textarea,
.safety-step-photo-caption textarea {
  display: block;
  width: 100%;
  box-sizing: border-box;
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-sm);
  background: var(--bg-surface);
  color: var(--text-primary);
  font: 500 16px / 1.55 var(--body);
}
.safety-step-card > textarea {
  min-height: 96px;
  padding: 12px 13px;
  overflow: hidden;
  resize: none;
}
.safety-step-photo-caption textarea { min-height: 84px; padding: 10px 11px; resize: vertical; }
/* Prompt 123's control box needs NO styling of its own — it is a direct child
   of .safety-step-card, so the four rules above already give it the border,
   surface, 16px font, focus ring and 70ch measure. Only the FLOOR differs: a
   control is typically one sentence, so it starts ~2 lines where the
   instruction starts ~3. Prompt 118 reads this per-element min-height as the
   floor and grows from it, which is exactly why a second value is safe here. */
.safety-step-card > textarea.safety-step-control { min-height: 76px; }
/* .safety-step-instruction carries margin-bottom but no margin-top, which was
   invisible while a step card held ONE label. Prompt 123 added a second one
   ("Control measures") directly after the instruction textarea, where it sat
   flush against it — the two fields read as one. Scoped to a label that
   FOLLOWS a textarea, so the first label is untouched and any future field
   inherits the gap. Higher specificity (0,2,1) than the base rule, and no
   .safety-inline-editor label rule sets a margin, so ONE rule serves both
   hosts — unlike the min-height above, which needs its twin at :7486. */
.safety-step-card > textarea + .safety-step-instruction { margin-top: 14px; }
.safety-step-optional { color: var(--text-muted); font-weight: 500; }
.safety-step-card > textarea:focus,
.safety-step-photo-caption textarea:focus {
  outline: 2px solid var(--accent);
  outline-offset: 1px;
  border-color: var(--accent);
}
.safety-step-photo-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(180px, 220px));
  gap: 12px;
  margin-top: 14px;
}
.safety-step-photo-card {
  min-width: 0;
  padding: 9px;
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  background: var(--bg-surface);
}
.safety-step-photo-card.is-pending { border-style: dashed; }
.safety-step-photo-media {
  position: relative;
  display: grid;
  place-items: center;
  width: 100%;
  min-height: 170px;
  aspect-ratio: 4 / 3;
  padding: 0;
  overflow: hidden;
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  background: var(--bg-base);
  color: var(--text-secondary);
  cursor: zoom-in;
}
.safety-step-photo-media:focus-visible { outline: 3px solid var(--accent); outline-offset: 2px; }
.safety-step-photo-media img { display: block; width: 100%; height: 100%; object-fit: contain; }
.safety-step-photo-media:disabled { cursor: default; opacity: 1; }
.safety-step-photo-missing { display: grid; gap: 5px; max-width: 180px; padding: 14px; text-align: center; }
.safety-step-photo-missing strong { color: var(--text-primary); font: 650 var(--text-md) / 1.35 var(--body); }
.safety-step-photo-missing span,
.safety-step-photo-state,
.safety-step-photo-help,
.safety-step-limit { color: var(--text-secondary); font: 500 var(--text-sm) / 1.45 var(--body); }
.safety-step-photo-state { min-height: 20px; margin: 7px 1px 0; }
.safety-step-photo-caption { display: block; margin-top: 9px; }
.safety-step-photo-caption > span { margin-bottom: 5px; font-size: var(--text-sm); }
.safety-step-photo-actions {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 6px;
  margin-top: 8px;
}
.safety-step-photo-actions .callout-btn { min-width: 0; min-height: 44px; padding: 8px 9px; }
.safety-step-media-foot { justify-content: flex-start; flex-wrap: wrap; margin-top: 12px; }
.safety-step-limit { color: var(--text-muted); }
/* (.safety-ssow-steps — the old flat 14px <ol> — retired by Prompt 116; the
   reader now renders .safety-ssow-method step cards, see the tail of this file.) */
.safety-haz-action-meta { font: 600 12px / 1.4 var(--body); color: var(--text-muted); margin-top: 3px; }

/* Detail — RA hazard view + sign-off */
.safety-ra-intro { display: flex; flex-wrap: wrap; gap: 18px; }
.safety-ra-intro > div { flex: 1; min-width: 160px; font: 500 14px / 1.5 var(--body); color: var(--text-primary); }
.safety-haz-view-list { display: flex; flex-direction: column; gap: 10px; }
.safety-haz-view { background: var(--bg-surface); border: 1px solid var(--border); border-radius: var(--radius-md); padding: 12px 14px; }
.safety-haz-view-top { display: flex; align-items: center; justify-content: space-between; gap: 8px; }
.safety-haz-view-title { font: 700 14px / 1.3 var(--body); color: var(--text-primary); }
/* Prompt 119: the reader has no width cap either, so a hazard/COSHH value on a
   1920px monitor ran to ~155 characters. Capped so the VALUE gets the 72ch
   measure: the label column is min-width 110px plus the 8px gap, and capping
   the row itself at a flat 72ch would leave the value only ~55. */
.safety-haz-view-row { display: flex; gap: 8px; margin-top: 6px; max-width: calc(72ch + 118px); font: 500 13px / 1.4 var(--body); color: var(--text-secondary); }
.safety-haz-view-row span { color: var(--text-muted); min-width: 110px; flex-shrink: 0; font-weight: 600; font-size: 12px; }
.safety-haz-action { margin-top: 10px; padding-top: 10px; border-top: 1px dashed var(--border); display: flex; align-items: center; justify-content: space-between; gap: 10px; flex-wrap: wrap; }
.safety-haz-action-text { font: 500 13px / 1.4 var(--body); color: var(--text-secondary); flex: 1; min-width: 180px; }
.safety-haz-task { flex-shrink: 0; padding: 7px 12px !important; font-size: 12px !important; }

.safety-signoff { background: var(--bg-surface); border: 1px solid var(--border); border-radius: var(--radius-md); padding: 14px 16px; }
.safety-signoff-head { display: flex; align-items: center; justify-content: space-between; gap: 10px; }
.safety-signoff-sub { font: 500 12px / 1 var(--body); color: var(--text-muted); margin-top: 3px; }
.safety-signed-badge { font: 700 12px / 1 var(--body); color: var(--rag-pass); }
.safety-signoff-list { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 10px; }
.safety-sign-chip { font: 600 11px / 1 var(--body); padding: 4px 9px; border-radius: var(--radius-sm); background: var(--bg-elevated); border: 1px solid var(--border); color: var(--text-secondary); }
/* P5 signature register states: superseded = signed an earlier revision
   (amber, needs re-confirming); missing = in the audience, never signed. */
.safety-sign-chip.is-superseded { color: var(--rag-warn); border-color: color-mix(in srgb, var(--rag-warn-fill) 45%, transparent); }
.safety-sign-chip.is-missing { border-style: dashed; color: var(--text-muted); }
/* signed the CURRENT version = the "done" state — green so a reader can see at
   a glance who has read it (matches the --rag-pass token used app-wide). */
.safety-sign-chip.is-signed { color: var(--rag-pass); border-color: color-mix(in srgb, var(--rag-pass-fill) 50%, transparent); background: color-mix(in srgb, var(--rag-pass-fill) 14%, var(--bg-elevated)); }
/* Department groups — used when the register is long enough to organise the
   chips by team (see safety/detail.js signoffHtml_). Each header is a button
   that collapses/expands its chips; a fully-read team's header is green and
   starts collapsed, so a manager only opens the teams that still owe a read. */
.safety-signoff-group { margin-top: 12px; }
.safety-signoff-group + .safety-signoff-group { margin-top: 14px; }
.safety-signoff-group-head { display: flex; align-items: center; gap: 8px; width: 100%; padding: 5px 0; background: none; border: 0; cursor: pointer; text-align: left; font: 700 11px / 1 var(--body); text-transform: uppercase; letter-spacing: .04em; color: var(--text-muted); }
.safety-signoff-group-head:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; border-radius: 4px; }
.safety-signoff-group-chev { width: 13px; height: 13px; flex: 0 0 auto; transition: transform .15s ease; }
.safety-signoff-group:not(.is-collapsed) .safety-signoff-group-chev { transform: rotate(90deg); }
.safety-signoff-group-name { flex: 0 1 auto; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.safety-signoff-group-count { margin-left: auto; flex: 0 0 auto; font: 600 11px / 1 var(--body); text-transform: none; letter-spacing: 0; color: var(--text-secondary); background: var(--bg-elevated); border: 1px solid var(--border); border-radius: 999px; padding: 2px 8px; }
.safety-signoff-group .safety-signoff-list { margin-top: 7px; }
.safety-signoff-group.is-collapsed .safety-signoff-list { display: none; }
/* a whole team on the current version = green header (no need to open it). */
.safety-signoff-group.is-done .safety-signoff-group-head { color: var(--rag-pass); }
.safety-signoff-group.is-done .safety-signoff-group-count { color: var(--rag-pass); border-color: color-mix(in srgb, var(--rag-pass-fill) 50%, transparent); background: color-mix(in srgb, var(--rag-pass-fill) 14%, var(--bg-elevated)); }
/* Light "paper" theme: the default green (#16a34a) is only ~2.6:1 on the pale
   elevated surface — below AA. Darken the "read/done" green locally (scoped to
   light so dark mode is untouched; the shared --rag-pass token is left alone). */
[data-theme="light"] .safety-sign-chip.is-signed,
[data-theme="light"] .safety-signoff-group.is-done .safety-signoff-group-head,
[data-theme="light"] .safety-signoff-group.is-done .safety-signoff-group-count { color: #166534; }
.safety-reviews { margin-bottom: 12px; }

/* Prompt 114: existing Safety documents edit in the reader's in-page surface.
   This is composition only: shared project-detail, callout form, button,
   taxonomy and audience components remain the controls inside the workspace. */
/* Prompt 119: NO self-cap. The editor fills .main exactly as the reader it
   edits does — the app-wide rule at the top of the project-detail block above
   ("Content that needs a readable line length caps ITSELF … never the frame",
   CLAUDE.md §3). The old 1120px cap cost 120px at 1440 and 484px at 1920
   against the reader, so tapping Edit reflowed the document being read.
   margin: 0 auto went with it — an uncapped block has nothing to centre. */
.safety-inline-editor {
  width: 100%;
  min-width: 0;
  padding-bottom: 20px;
  display: flex;
  flex-direction: column;
  gap: 18px;
}
.safety-editor-head { gap: 9px; }
.safety-editor-intro {
  max-width: 70ch;
  margin: -2px 0 0;
  color: var(--text-secondary);
  font: 500 var(--text-md) / 1.55 var(--body);
  text-wrap: pretty;
}
/* The settings RAIL is gone (Prompt 119). It held Site, Category, Assessed by
   and both dates — every one of them content that prints in the reader's meta
   grid, not editor state — and it was already failing on its own terms at 274px
   usable width (the audience mode pills need 288px and wrapped). They are now
   ONE collapsed fold above the canvas, so the document gets the whole width. */
.safety-editor-main,
.safety-editor-main > [data-type-body] {
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 14px;
}
/* The fold reuses .settings-fold wholesale (css/02-shell.css) — head, chevron,
   title, meta and the §6-correct [hidden] body rule. Only the four things that
   differ from the Settings screen's own use are scoped here. */
.safety-editor-settings {
  margin-top: 0;                    /* the flex column already supplies the gap */
  box-shadow: var(--shadow-card);   /* one card family with the form panels */
  /* .settings-fold sets overflow:hidden to keep the head's fill inside its
     rounded corners. That cannot stand here: the Department control is an INLINE
     position:absolute menu (AngelHubAssets.taxonomyDropdown →
     AngelHubUI.placeDropdownMenu), so the clip box SLICED its last row —
     measured at 16px, half an option, with "Department 14" cut through the
     middle. The rail this fold replaced never clipped anything. Let the menu
     escape, and round the head itself so the corners still read as one card. */
  overflow: visible;
}
.safety-editor-settings > .settings-fold-head {
  border-radius: calc(var(--radius-lg) - 1px) calc(var(--radius-lg) - 1px) 0 0;
}
.safety-editor-settings > .settings-fold-head[aria-expanded="false"] {
  border-radius: calc(var(--radius-lg) - 1px);
}
/* Settings now sit on the full canvas, so the CONTROLS keep a form-sized
   measure while their labels and help text keep the panel's width. Without
   this, a 1600px canvas renders a 790px-wide Status select and date box —
   not a considered control. Same principle as the prose caps below: the
   content caps itself, never the frame. */
.safety-editor-settings .settings-fold-body > .callout-form-section { max-width: 760px; }
.safety-editor-settings .callout-form-field input[type="date"] { max-width: 240px; }
/* The head summary is a sentence (type · status · review date), not the
   Settings screen's one-word "On" / "12 chips", so it wraps instead of
   squeezing the title off the head. */
.safety-editor-settings .settings-fold-meta { white-space: normal; text-align: right; }
.safety-inline-editor .callout-form-section {
  border: 1px solid var(--border);
  box-shadow: var(--shadow-card);
}
.safety-inline-editor .callout-form-section-head {
  font-size: var(--text-md);
  font-weight: 700;
}
.safety-inline-editor .callout-form-field > label,
.safety-inline-editor .safety-track-toggle {
  font-size: var(--text-base);
  line-height: 1.35;
}
.safety-inline-editor .safety-track-toggle { min-height: 44px; }
.safety-inline-editor .callout-form-help {
  font-size: var(--text-sm);
  line-height: 1.5;
  color: var(--text-secondary);
}
.safety-inline-editor .callout-form-field input:not([type="checkbox"]):not([type="file"]),
.safety-inline-editor .callout-form-field select,
.safety-inline-editor .callout-form-field textarea,
.safety-inline-editor .safety-haz input,
.safety-inline-editor .safety-haz select,
.safety-inline-editor .safety-haz textarea,
.safety-inline-editor .safety-step-card > textarea,
.safety-inline-editor .safety-step-photo-caption textarea {
  min-height: 48px;
  font-size: var(--text-xl);
  line-height: 1.5;
}
.safety-inline-editor .callout-form-field textarea,
.safety-inline-editor .safety-haz textarea,
.safety-inline-editor .safety-step-card > textarea { min-height: 96px; }
/* The inline editor re-floors every textarea at 96px, and that rule ties this
   one on specificity (0,2,1) while sitting LATER in the file — so the control's
   shorter floor has to be restated here or it silently reverts to 96px on the
   editor's primary surface. Do not delete as a duplicate. */
.safety-inline-editor .safety-step-card > textarea.safety-step-control { min-height: 76px; }
/* Prose keeps a readable measure now the canvas is uncapped: 1920px uncapped
   renders ~155-character lines, past WCAG 1.4.8's 80-character guidance. 72ch
   is the app's own value (.project-desc p), which the READER already applies to
   this same text — so a paragraph occupies about the same number of lines in
   both. Pixel-identical reflow is not achievable and is not the goal: the
   editor deliberately runs a larger field font (the 16px iOS floor, §4), so it
   is the CHARACTER measure that is matched. Cards, hazard rows, step photo
   grids and tables keep the FULL canvas — only the running text is capped. */
.safety-inline-editor .callout-form-field textarea,
.safety-inline-editor .safety-haz textarea { max-width: 72ch; }
/* The step instruction matches the reader's own .safety-ssow-step-text (70ch, a
   measured Prompt 116 value) rather than 72 — the two render the same words. */
.safety-inline-editor .safety-step-card > textarea { max-width: 70ch; }
.safety-inline-editor .safety-editor-title-field input {
  min-height: 54px;
  font-size: var(--text-2xl);
  font-weight: 650;
}
.safety-inline-editor .ctree-lt,
.safety-inline-editor .project-back,
.safety-inline-editor .callout-btn { min-height: 44px; }
.safety-inline-editor [data-read-mode-row],
.safety-inline-editor .safety-chip-row {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
}
.safety-inline-editor [data-read-mode-row] .ctree-lt,
.safety-inline-editor .safety-chip-row .ctree-lt {
  flex: 1 1 132px;
  width: auto;
  flex-direction: row;
}
.safety-inline-editor .safety-haz-del {
  width: 44px;
  height: 44px;
  flex: 0 0 44px;
}
.safety-editor-draft-state {
  color: var(--text-secondary);
  font: 600 var(--text-sm) / 1.3 var(--body);
}
/* The head no longer carries a draft-state twin (Prompt 120). Two elements
   rendering the same string is how they drift, and the surviving one is the
   action bar's — which from 1024px up is the one that is always on screen. */
.safety-editor-actions {
  /* STATIC below 1024px on purpose (Prompt 120). At ≤640px this bar already
     wraps to two rows, and two sticky rows plus the iOS keyboard would leave
     almost no document; phone stickiness is a separate decision after the
     on-device pass. The sticky state is the min-width block below. */
  position: static;
  display: flex;
  align-items: center;
  gap: 10px;
  margin-top: 2px;
  padding: 11px 12px calc(11px + env(safe-area-inset-bottom));
  background: var(--bg-surface);
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-md);
}
.safety-editor-actions > .safety-editor-draft-state {
  flex: 1 1 auto;
  min-width: 110px;
}
.safety-editor-action-buttons {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-left: auto;
}
.safety-editor-action-buttons .callout-btn { min-width: 104px; }
/* Delete is the LAST section of the form, not a passenger on the permanent bar
   (Prompt 120). The panel inherits the editor's own .callout-form-section look,
   so the only thing to say is that the button keeps its own size — a column
   flex would otherwise stretch a danger button across the whole canvas, which
   is exactly the loud-by-default this change exists to remove (§4). */
.safety-editor-danger > .callout-btn-danger { align-self: flex-start; }

/* Save follows you (Prompt 120). Below the fold, "Save changes" was reachable
   only by scrolling to the end of a document that Prompt 118 made taller — the
   same defect css/02-shell.css fixed for the department editor, where closing
   before scrolling silently discarded edits. Sticky, never fixed (§4). The base
   rule above already supplies the opaque background, the full border and the
   safe-area-aware padding, so this block only does the sticking.
   The bar is the LAST flex child of a containing block that spans the whole
   document, so a bottom offset floats it at the scrollport edge for the entire
   scroll and lets it settle into flow at the end — nothing to clamp, and no
   overflow:hidden between it and .main (the scroller). z-index sits above form
   content and deliberately BELOW the taxonomy dropdown's 60 and every
   *-overlay, so an open Department menu still wins. */
@media (min-width: 1024px) {
  /* A sticky bottom offset stops at the SCROLLPORT's padding edge, so .main's
     own 40px bottom padding showed as a strip of the document sliding UNDER the
     parked bar and being clipped by the window — measured, not theorised. The
     surface that supplies the end-of-document breathing room therefore changes
     while the editor is open: .main gives none, and the bar docks flush with
     content scrolling cleanly behind it. Scoping a .main rule to one module's
     surface is the pattern css/03 already uses (.main:has(>
     #module-view-kb.kb-hosting-reader)). Nothing is lost to the chat bubble:
     the parked bar already occupies the zone that padding existed to clear.
     TWO THINGS ABOUT THIS SELECTOR ARE LOAD-BEARING — do not shorten it to
     `.main:has(.safety-inline-editor)`, which is what it was until both were
     measured:
     (a) `.module-view.active` is REQUIRED because :has() matches on DOM
         PRESENCE, not visibility. navigate() (core.js) only strips the `active`
         class — it never empties a module view, and `.module-view` is merely
         display:none — so leaving this editor open and navigating away left
         `.safety-inline-editor` sitting in .main and stripped the bottom
         padding from EVERY other module, taking the chat-bubble clearance with
         it. Scoping to the visible surface is what makes the rule mean what it
         says.
     (b) the resulting specificity (0,4,0) has to OUTRANK
         `body.has-chat-nudge .main` (css/02, the rule that lets the last card
         clear the floating chat bubble), which is (0,2,1) — element selectors
         count in the third slot. A bare `.main:has(…)` is only (0,2,0) and LOST
         90px whenever anyone had an unread chat, i.e. the clipped strip came
         straight back in the commonest real state. Source order does not enter
         into it, so no link reordering can break this. */
  .main:has(.module-view.active .safety-inline-editor) { padding-bottom: 0; }
  .safety-inline-editor { padding-bottom: 0; }
  .safety-editor-actions {
    position: sticky;
    bottom: 0;
    z-index: 3;
    /* Docked, not floating: square the two corners that now meet the window and
       drop the edge that sits off-screen, so it reads as this surface's footer
       exactly as the shared sticky bars do (.inv-count-bar, .site-edit-actions
       in css/02). */
    border-bottom: 0;
    border-bottom-left-radius: 0;
    border-bottom-right-radius: 0;
  }
  /* Keyboard honesty: without this, tabbing into a field near the end of the
     document scrolls it to the very bottom of .main — underneath the bar that
     is now parked there. Scroll padding is the scroll CONTAINER's to give.
     Clears the bar (≈67px) plus a line of breathing. Same `.module-view.active`
     scoping as above, and for the same reason (a) — a lingering editor in a
     hidden view must not leave every other module scroll-padded. */
  .main:has(.module-view.active .safety-inline-editor) { scroll-padding-bottom: 88px; }

  /* Docking the bar claims the bottom edge — which this app already reserves for
     floating chrome. MEASURED, and the reason this block exists: the guest-request
     pill is fixed bottom-RIGHT at z-index 46 against the bar's 3, so
     document.elementFromPoint() at the centre of "Save changes" returned
     .guest-nudge, not the button — Save was literally unclickable whenever a site
     had an open guest request (109x29px overlap at 1440). The chat nudge does the
     same to the draft-state label bottom-LEFT. Both were already latent at the end
     of a long document before the bar docked; docking made them permanent, so they
     are this change's to fix.
     Lift them instead of raising the bar above them: these are an operational alert
     and a notification, and a Save bar that covers them would be a worse trade. This
     is the app's OWN idiom — .guest-nudge.above-duty already stacks a pill by +64px
     to clear the minimised duty pill. The lift is deliberately LARGER than that 64px
     so the guest pill still clears a duty pill it is also avoiding, and .nudge-fan
     keeps its own +64px so it still opens ABOVE the pill it belongs to.
     KB's .float-ai-fab and the Tasks FAB are module-scoped and never render here.
     NOT addressed (pre-existing, app-wide, transient): .sync-indicator sits
     bottom-right at z-index 9400 and covers this corner in every module already. */
  body:has(.module-view.active .safety-inline-editor) { --safety-docked-bar: 76px; }
  body:has(.module-view.active .safety-inline-editor) .chat-nudge,
  body:has(.module-view.active .safety-inline-editor) .guest-nudge {
    bottom: calc(max(22px, env(safe-area-inset-bottom) - 12px) + var(--safety-docked-bar));
  }
  body:has(.module-view.active .safety-inline-editor) .nudge-fan {
    bottom: calc(max(22px, env(safe-area-inset-bottom) - 12px) + var(--safety-docked-bar) + 64px);
  }
}

/* The ≤1023px block that collapsed the two-column workspace and un-stuck the
   rail is gone with them (Prompt 119): one column is now the ONLY layout, at
   every breakpoint, in document order. */

@media (max-width: 640px) {
  #module-view-safety .asset-list-wrap { max-width: 100%; }
  .safety-editor-actions {
    flex-wrap: wrap;
    min-height: 0;
  }
  .safety-editor-actions > .safety-editor-draft-state {
    order: -1;
    flex-basis: 100%;
  }
  .safety-editor-action-buttons { flex: 1 1 auto; }
  .safety-editor-action-buttons .callout-btn { flex: 1 1 0; min-width: 86px; }
  .safety-step-photo-grid { grid-template-columns: minmax(0, 1fr); }
  .safety-step-photo-media { min-height: 0; }
}

/* Full-width phone trim. This block deliberately sits after every base rule:
   media queries add no specificity, and longhand keeps the footer's
   safe-area-aware vertical padding intact. */
@media (max-width: 599px) {
  .safety-inline-editor { gap: 14px; padding-bottom: 8px; }
  .safety-editor-main,
  .safety-editor-main > [data-type-body] { gap: 12px; }
  /* The head summary drops to its own line rather than fighting the title for a
     375px head (§4). The fold stays collapsed by default here too. */
  .safety-editor-settings .settings-fold-head { flex-wrap: wrap; }
  .safety-editor-settings .settings-fold-meta { flex-basis: 100%; margin-left: 0; text-align: left; }
  .safety-inline-editor .callout-form-section {
    padding-left: 10px;
    padding-right: 10px;
  }
  .safety-editor-actions {
    padding-left: 10px;
    padding-right: 10px;
  }
  .safety-step-card {
    padding-left: 10px;
    padding-right: 10px;
  }
  .safety-step-card-head { align-items: flex-start; }
  .safety-step-remove { padding-left: 10px; padding-right: 10px; }
  .safety-step-media-foot { align-items: flex-start; flex-direction: column; }
  .safety-step-add-photo { width: 100%; justify-content: center; }
}

/* ═══════════════════════════════════════════════════════════════════════
   STATISTICS — operations dashboard
   ─────────────────────────────────────────────────────────────────────
   Live KPI tiles, a per-site league table and a task-throughput trend.
   Reuses the app's card / KPI / chip language so it reads as native.
   ═══════════════════════════════════════════════════════════════════════ */
/* Dashboard fills the content area (no self-cap) — the .main ceiling bounds it.
   KPI grid + league table + charts all use the width; chart density is tuned
   further in the Statistics desktop pass. */
.stat-page { display: flex; flex-direction: column; gap: 16px; margin: 0 auto; width: 100%; }

/* Toolbar: period type · prev/next · scope */
.stat-toolbar { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; }
.stat-period-nav { display: flex; align-items: center; gap: 6px; }
.stat-period-arrow {
  display: grid; place-items: center; width: 32px; height: 32px; flex-shrink: 0;
  background: var(--bg-surface); border: 1px solid var(--border); border-radius: 9px;
  color: var(--text-secondary); cursor: pointer; transition: background .12s, border-color .12s, color .12s;
}
.stat-period-arrow:hover { border-color: var(--accent); color: var(--text-primary); }
.stat-period-arrow svg { width: 16px; height: 16px; }
.stat-period-label { min-width: 132px; text-align: center; font: 700 var(--text-base) / 1 var(--display, var(--body)); color: var(--text-primary); }
.stat-scope { margin-left: auto; }
.stat-scope-label { font: 600 var(--text-sm) / 1 var(--body); color: var(--text-muted); }
.stat-scope-reset {
  display: inline-flex; align-items: center; gap: 7px; padding: 7px 12px;
  background: color-mix(in srgb, var(--accent) 10%, var(--bg-surface));
  border: 1px solid color-mix(in srgb, var(--accent) 40%, var(--border));
  border-radius: var(--radius-sm); color: var(--text-primary); font: 600 var(--text-sm) / 1 var(--body); cursor: pointer;
}
.stat-scope-reset span { color: var(--text-muted); font-weight: 700; }
.stat-scope-reset:hover { border-color: var(--accent); }

/* KPI strip — the SHARED .callout-kpi-card tiles (css/12) in a responsive grid.
   The bespoke .stat-kpi tile shell was retired 2026-07-06 (Prompt 13): the cards
   now reuse .callout-kpi-card / .cl-kpi-card, so only the grid wrapper and the
   in-card content the shared card doesn't model — the sub-line and the prior-period
   delta badge — remain here. No parallel tile styles left behind. */
.stat-kpis { display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); gap: 10px; }
.stat-kpi-sub { margin-top: 4px; font: 600 var(--text-xs) / 1.3 var(--body); color: var(--text-muted); }
.stat-kpi-sub.is-bad  { color: #dc2626; }
.stat-kpi-sub.is-good { color: #16a34a; }
/* Prior-period delta — ▲ up / ▼ down / → level, toned by metric direction
   (more completed = good/green; more spend = bad/red). */
.stat-kpi-delta { display: inline-flex; align-items: baseline; gap: 4px; margin-top: 5px; font: 700 var(--text-xs) / 1 var(--body); }
.stat-kpi-delta small { font-weight: 600; color: var(--text-muted); }
.stat-kpi-delta.is-good { color: #16a34a; }
.stat-kpi-delta.is-bad  { color: #dc2626; }
.stat-kpi-delta.is-flat { color: var(--text-muted); }

/* Labelled report sections (2026-07-06) — group the cards under an eyebrow icon +
   title + one-line description so the page reads like a report, not one long scroll.
   Sections are direct children of the flex-column .stat-page (16px gap); .stat-slot
   gives the async adoption/platform sections the same rhythm. */
.stat-section { display: flex; flex-direction: column; gap: 12px; }
.stat-slot { display: flex; flex-direction: column; gap: 16px; }
.stat-section-head { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.stat-section-icon { display: inline-flex; color: var(--text-secondary); }
.stat-section-icon svg { width: 18px; height: 18px; }
.stat-section-title { font: 700 var(--text-xl) / 1 var(--display, var(--body)); color: var(--text-primary); }
.stat-section-tag { font: 600 var(--text-xs) / 1 var(--body); padding: 3px 9px; border-radius: 999px; background: var(--bg-elevated); border: 1px solid var(--border); color: var(--text-secondary); }
.stat-section-sub { flex-basis: 100%; margin: 2px 0 0 26px; font: 500 var(--text-xs) / 1.4 var(--body); color: var(--text-muted); }

/* Card shell (league + chart) */
.stat-card { background: var(--bg-surface); border: 1px solid var(--border); border-radius: var(--radius-md); padding: 14px 16px; box-shadow: var(--shadow-card); }
.stat-card-head { display: flex; align-items: baseline; justify-content: space-between; gap: 10px; margin-bottom: 12px; }
.stat-card-title { font: 700 var(--text-base) / 1 var(--display, var(--body)); color: var(--text-primary); }
.stat-card-note { font: 500 var(--text-xs) / 1 var(--body); color: var(--text-muted); }

/* League table — div grid so it collapses cleanly on mobile */
.stat-league { display: flex; flex-direction: column; }
.stat-league-row { display: grid; grid-template-columns: minmax(0, 1fr) 90px 90px 96px; align-items: center; gap: 8px; padding: 6px 8px; border-bottom: 1px solid var(--border); }
/* Prompt 113: with the Checks column the league is 5 columns (Site · Tasks ·
   Call-outs · Checks · Spend). Scoped to .stat-league--checks so the base 4-col
   layout (and the annual/old-payload league) is untouched. Hidden at phone
   widths (the @640 block below) where a 5-col league can't fit. */
.stat-league--checks .stat-league-row { grid-template-columns: minmax(0, 1fr) 90px 90px 90px 96px; }
/* Problem-areas table reuses the league row but has 3 columns (Area · Open · Total). */
.stat-league--areas .stat-league-row { grid-template-columns: minmax(0, 1fr) 96px 84px; padding: 11px 8px; }
.stat-league-row:last-child { border-bottom: 0; }
.stat-league-head { font: 700 var(--text-xs) / 1 var(--body); text-transform: uppercase; letter-spacing: .05em; color: var(--text-muted); padding: 11px 8px; }
.stat-league-head .stat-league-site { color: var(--text-muted); }
/* Body metric buttons carry padding:5px 4px; match it on the header cells so each
   caption's right edge stays flush over its numbers (right-aligned numeric table). */
.stat-league-head .stat-league-cell { padding-right: 4px; }
.stat-league-row.is-focus { background: color-mix(in srgb, var(--accent) 6%, transparent); box-shadow: inset 2px 0 0 var(--accent); border-radius: var(--radius-sm); }
.stat-league-site { display: flex; align-items: center; gap: 9px; font: 600 var(--text-base) / 1.2 var(--body); color: var(--text-primary); min-width: 0; }
/* Site name is a button (focus this site); metric cells are buttons (jump to the
   filtered list). Reset the chrome, keep the grid alignment, add hover + focus. */
.stat-league-site-btn { border: 0; background: none; margin: 0; padding: 5px 6px 5px 0; text-align: left; cursor: pointer; border-radius: var(--radius-sm); transition: background .12s; }
.stat-league-site-btn:hover { background: var(--bg-hover); }
.stat-league-site-btn:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
.stat-league-name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.stat-dot { width: 8px; height: 8px; border-radius: 50%; flex-shrink: 0; }
.stat-league-cell { display: flex; align-items: center; justify-content: flex-end; gap: 6px; font: 700 var(--text-base) / 1 var(--body); color: var(--text-primary); }
.stat-league-metric { flex-direction: column; align-items: flex-end; gap: 3px; border: 0; background: none; margin: 0; padding: 5px 4px; font: inherit; color: var(--text-primary); cursor: pointer; border-radius: var(--radius-sm); transition: background .12s; }
.stat-league-metric:hover { background: var(--bg-hover); }
.stat-league-metric:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
.stat-league-metric-top { display: flex; align-items: center; justify-content: flex-end; gap: 6px; font: 700 var(--text-base) / 1 var(--body); }
.stat-league-pips { display: flex; align-items: center; gap: 5px; }
.stat-pip { font: 700 12px / 1 var(--body); padding: 3px 7px; border-radius: 999px; font-variant-numeric: tabular-nums; white-space: nowrap; }
.stat-pip--urgent { color: #dc2626; background: rgba(220, 38, 38, 0.12); }
.stat-pip--high { color: #b45309; background: rgba(234, 88, 12, 0.14); }
.stat-cell-num { font-weight: 700; font-size: 18px; line-height: 1; font-variant-numeric: tabular-nums; }
.stat-od { font: 700 12px / 1 var(--body); color: #fff; background: #dc2626; padding: 3px 7px; border-radius: 999px; }
.stat-league-spend { justify-content: flex-end; font-size: 15px; font-variant-numeric: tabular-nums; color: var(--text-secondary); }
.stat-league-spend.stat-league-metric { color: var(--text-secondary); }
.stat-league-legend { margin-top: 10px; font: 500 var(--text-xs) / 1.4 var(--body); color: var(--text-muted); display: flex; align-items: center; gap: 6px; flex-wrap: wrap; }
.stat-league-legend .stat-pip { padding: 1px 6px; }

/* Trend chart — built-in grouped bars, no library */
.stat-chart-legend { display: flex; gap: 12px; }
.stat-leg { display: inline-flex; align-items: center; gap: 5px; font: 600 var(--text-xs) / 1 var(--body); color: var(--text-secondary); }
.stat-leg-sw { width: 10px; height: 10px; border-radius: 2px; }
.stat-sw-created { background: #6366f1; }
.stat-sw-done { background: #16a34a; }
.stat-chart-bars { display: flex; align-items: flex-end; gap: 10px; height: 130px; padding-top: 6px; }
.stat-col { flex: 1; display: flex; flex-direction: column; align-items: center; gap: 6px; min-width: 0; height: 100%; }
.stat-col-bars { flex: 1; display: flex; align-items: flex-end; justify-content: center; gap: 3px; width: 100%; }
.stat-bar { width: 42%; max-width: 18px; min-height: 2px; border-radius: 2px 2px 0 0; }
.stat-bar-created { background: #6366f1; }
.stat-bar-done { background: #16a34a; }
/* Daily variant: 30 columns instead of ~5, so tighter gaps + thinner bars, and no
   2px floor (a quiet day is a real gap on the continuous axis, not a tick). */
.stat-chart-bars--daily { gap: 2px; }
.stat-chart-bars--daily .stat-bar { width: 68%; max-width: 11px; min-height: 0; }
.stat-col-label { font: 600 10px / 1 var(--body); color: var(--text-muted); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; max-width: 100%; }
/* Daily axis: only every ~5th column is labelled, so let a 2-digit day (16, 21…)
   spill into the empty neighbour columns instead of ellipsising to "1.." in a 3px cell. */
.stat-chart-bars--daily .stat-col-label { overflow: visible; max-width: none; }
.stat-chart-empty { padding: 28px 0; text-align: center; font: 500 var(--text-sm) / 1.4 var(--body); color: var(--text-muted); }

@media (max-width: 640px) {
  .stat-kpis { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .stat-scope { margin-left: 0; width: 100%; }
  .stat-league-row { grid-template-columns: minmax(0, 1fr) 66px 66px 70px; gap: 5px; padding: 4px; }
  .stat-league-head { padding: 8px 4px; }
  /* Prompt 113: the Checks column is desktop/tablet only — at phone widths a
     5-col league can't fit, so hide it and revert --checks rows to 4 columns. */
  .stat-league-checks { display: none; }
  .stat-league--checks .stat-league-row { grid-template-columns: minmax(0, 1fr) 66px 66px 70px; }
  .stat-league--areas .stat-league-row { grid-template-columns: minmax(0, 1fr) 70px 58px; padding: 10px 4px; }
  .stat-league-metric { padding: 4px 2px; }
  .stat-league-head .stat-league-cell { padding-right: 2px; }
  .stat-league-site-btn { padding-left: 0; padding-right: 4px; }
  .stat-od { padding: 2px 5px; }
  .stat-pip { padding: 2px 5px; }
  .stat-cell-num { font-size: 15px; }
  .stat-league-spend { font-size: 14px; }
  .stat-col-label { font-size: 9px; }
}

/* ═══════════════════════════════════════════════════════════════════════
   CHAT — real-time team messaging (Phase 1)
   Two-pane app layout (conversation list + thread), WhatsApp-style bubbles,
   composer, and the new-chat modals. Reuses app tokens + .callout-modal.
   ═══════════════════════════════════════════════════════════════════════ */
.chat-layout {
  /* --banner-h: the impersonation banner pushes .app-shell down — without it
     the composer/last rows sit below the fold while viewing-as (§6). */
  display: flex; height: calc(100dvh - 104px - var(--banner-h, 0px)); min-height: 460px;
  border: 1px solid var(--border); border-radius: var(--radius-lg);
  overflow: hidden; background: var(--bg-surface); box-shadow: var(--shadow-card);
}
.chat-sidebar { width: 340px; flex-shrink: 0; display: flex; flex-direction: column; border-right: 1px solid var(--border); min-width: 0; }
.chat-sidebar-head { display: flex; gap: 8px; padding: 12px; border-bottom: 1px solid var(--border); }
.chat-list-search { flex: 1; min-width: 0; padding: 9px 12px; background: var(--bg-base); border: 1px solid var(--border-input); border-radius: var(--radius-pill); font: 500 var(--text-sm)/1 var(--body); color: var(--text-primary); }
.chat-list-search:focus { outline: none; border-color: var(--accent); }
.chat-new-btn { display: grid; place-items: center; width: 38px; height: 38px; flex-shrink: 0; background: var(--accent); color: #fff; border: 0; border-radius: 50%; cursor: pointer; box-shadow: 0 0 12px var(--accent-glow); }
.chat-new-btn svg { width: 18px; height: 18px; }
.chat-list { flex: 1; min-height: 0; overflow-y: auto; }
.chat-loading { padding: 20px; text-align: center; color: var(--text-muted); font: 500 var(--text-sm)/1 var(--body); }
/* "Loading earlier messages…" — shown at the top while paging older history in. */
.chat-older { display: flex; align-items: center; justify-content: center; gap: 8px; padding: 12px; color: var(--text-muted); font: 600 var(--text-xs)/1 var(--body); }
.chat-older-spin { width: 14px; height: 14px; border: 2px solid color-mix(in srgb, var(--text-muted) 35%, transparent); border-top-color: var(--accent); border-radius: 50%; animation: chatOlderSpin .7s linear infinite; }
@keyframes chatOlderSpin { to { transform: rotate(360deg); } }
@media (prefers-reduced-motion: reduce) { .chat-older-spin { animation: none; } }
.chat-list-empty { padding: 28px 18px; text-align: center; }
.chat-list-empty-title { font: 700 var(--text-base)/1.2 var(--display, var(--body)); color: var(--text-primary); }
.chat-list-empty-sub { margin-top: 6px; font: 500 var(--text-sm)/1.45 var(--body); color: var(--text-muted); }

/* ── "On duty now" strip — pinned above the conversation list. Shows who's on
   duty (Deputy / Maintenance / Housekeeping) so you can see + message them
   without leaving chat. Calm/neutral; the dept colour lives only on the icon. ── */
.chat-duty-strip { flex-shrink: 0; border-bottom: 1px solid var(--border); background: var(--bg-base); }
.chat-duty-strip[hidden] { display: none; }
.chat-duty-head { width: 100%; display: flex; align-items: center; gap: 8px; padding: 9px 13px; background: transparent; border: 0; cursor: pointer; text-align: left; }
.chat-duty-head-l { display: flex; align-items: baseline; gap: 8px; min-width: 0; flex: 1 1 auto; }
.chat-duty-title { font: 800 11px/1 var(--body); letter-spacing: .06em; text-transform: uppercase; color: var(--text-muted); flex-shrink: 0; }
.chat-duty-site { font: 600 11px/1 var(--body); color: var(--text-muted); opacity: .85; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.chat-duty-sum { font: 600 var(--text-xs)/1 var(--body); color: var(--text-secondary); margin-left: auto; flex-shrink: 0; }
.chat-duty-strip:not(.is-collapsed) .chat-duty-sum { display: none; }
.chat-duty-caret { display: grid; place-items: center; color: var(--text-muted); transition: transform .2s ease; }
.chat-duty-caret svg { width: 16px; height: 16px; }
.chat-duty-strip.is-collapsed .chat-duty-caret { transform: rotate(-90deg); }
.chat-duty-strip.is-collapsed .chat-duty-rows { display: none; }
@media (prefers-reduced-motion: reduce) { .chat-duty-caret { transition: none; } }
.chat-duty-rows { display: flex; flex-direction: column; gap: 6px; padding: 0 9px 10px; }
.chat-duty-row { display: flex; align-items: stretch; background: var(--bg-elevated); border: 1px solid var(--border); border-radius: var(--radius-md); overflow: hidden; }
/* Left = message zone (tap → 1:1 when someone else is on); right = "raise a job". */
.chat-duty-msg { flex: 1 1 auto; min-width: 0; display: flex; align-items: center; gap: 10px; padding: 8px 10px; background: transparent; border: 0; text-align: left; color: inherit; cursor: default; transition: background .12s; }
button.chat-duty-msg { cursor: pointer; }
button.chat-duty-msg:hover { background: var(--bg-hover); }
button.chat-duty-msg:focus-visible { outline: none; box-shadow: inset 0 0 0 2px var(--accent); }
.chat-duty-ico { display: grid; place-items: center; width: 30px; height: 30px; flex-shrink: 0; border-radius: 9px; color: var(--c, var(--text-secondary)); background: color-mix(in srgb, var(--c, var(--text-muted)) 14%, transparent); }
.chat-duty-ico svg { width: 17px; height: 17px; }
.chat-duty-main { min-width: 0; flex: 1 1 auto; display: flex; flex-direction: column; gap: 2px; }
.chat-duty-label { font: 700 10px/1 var(--body); letter-spacing: .04em; text-transform: uppercase; color: var(--text-muted); }
.chat-duty-who { font: 600 var(--text-sm)/1.2 var(--body); color: var(--text-primary); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.chat-duty-who.chat-duty-none { color: var(--text-muted); font-weight: 500; }
.chat-duty-until { color: var(--text-muted); font-weight: 500; }
.chat-duty-avs { display: flex; align-items: center; flex-shrink: 0; }
.chat-duty-avs .chat-avatar-sm { width: 28px; height: 28px; font-size: 11px; box-shadow: 0 0 0 2px var(--bg-elevated); }
.chat-duty-avs .chat-avatar-sm + .chat-avatar-sm { margin-left: -8px; }
.chat-duty-more { margin-left: 5px; font: 700 var(--text-xs)/1 var(--body); color: var(--text-muted); }
.chat-duty-chev { flex-shrink: 0; width: 38px; display: grid; place-items: center; color: var(--text-muted); }
.chat-duty-chev svg { width: 16px; height: 16px; }

.chat-list-row { width: 100%; display: flex; align-items: center; gap: 11px; padding: 11px 13px; background: transparent; border: 0; border-bottom: 1px solid var(--border); cursor: pointer; text-align: left; transition: background .12s; }
.chat-list-row:hover { background: var(--bg-hover); }
.chat-list-row.is-active { background: color-mix(in srgb, var(--accent) 8%, transparent); box-shadow: inset 3px 0 0 var(--accent); }
.chat-list-row-main { flex: 1; min-width: 0; }
.chat-list-row-top { display: flex; align-items: baseline; justify-content: space-between; gap: 8px; }
.chat-list-name { font: 600 var(--text-sm)/1.3 var(--body); color: var(--text-primary); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.chat-list-row.is-unread .chat-list-name { font-weight: 800; }
/* Name + a small muted-bell badge group on the left; time stays pinned right. */
.chat-list-name-wrap { display: flex; align-items: center; gap: 5px; min-width: 0; flex: 1 1 auto; }
.chat-row-muted { display: inline-grid; place-items: center; flex-shrink: 0; color: var(--text-muted); }
.chat-row-muted svg { width: 14px; height: 14px; }
.chat-list-time { font: 500 11px/1 var(--body); color: var(--text-muted); flex-shrink: 0; }
.chat-list-row-bot { display: flex; align-items: center; justify-content: space-between; gap: 8px; margin-top: 3px; }
.chat-list-preview { font: 500 var(--text-xs)/1.35 var(--body); color: var(--text-muted); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.chat-list-row.is-unread .chat-list-preview { color: var(--text-secondary); font-weight: 600; }
.chat-unread-dot { width: 9px; height: 9px; border-radius: 50%; background: var(--accent); flex-shrink: 0; }

/* Swipe-to-delete (mobile) + hover trash (desktop) — "remove from my list" */
.chat-row { position: relative; overflow: hidden; }
.chat-row .chat-list-row { position: relative; z-index: 1; transition: transform .22s cubic-bezier(.4, 0, .2, 1); touch-action: pan-y; }
.chat-row.is-swiping .chat-list-row { transition: none; }
.chat-row.is-open .chat-list-row { transform: translateX(-88px); }
.chat-row-del {
  position: absolute; top: 0; right: 0; bottom: 0; width: 88px; z-index: 0;
  display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 4px;
  border: 0; background: var(--danger); color: #fff; cursor: pointer;
  font: 700 var(--text-xs)/1 var(--body); opacity: 0; pointer-events: none; transition: opacity .15s ease;
}
.chat-row-del svg { width: 19px; height: 19px; }
.chat-row.is-open .chat-row-del, .chat-row.is-swiping .chat-row-del { opacity: 1; pointer-events: auto; }
.chat-row-trash {
  position: absolute; top: 50%; right: 12px; transform: translateY(-50%); z-index: 2;
  width: 32px; height: 32px; border-radius: var(--radius-md); border: 0;
  display: grid; place-items: center; background: var(--bg-elevated); color: var(--text-muted);
  opacity: 0; pointer-events: none; cursor: pointer; transition: opacity .12s ease, color .12s ease, background .12s ease;
}
.chat-row-trash svg { width: 16px; height: 16px; }
@media (hover: hover) {
  .chat-row:hover .chat-row-trash { opacity: 1; pointer-events: auto; }
  .chat-row-trash:hover { color: var(--danger); background: color-mix(in srgb, var(--danger) 16%, var(--bg-elevated)); }
}

/* Avatars */
.chat-avatar { width: 42px; height: 42px; flex-shrink: 0; border-radius: 50%; display: grid; place-items: center; color: #fff; font: 700 14px/1 var(--body); background: var(--av, #64748b); }
.chat-avatar-group svg { width: 20px; height: 20px; }
.chat-avatar-sm { width: 34px; height: 34px; font-size: 12px; }

/* Main pane */
.chat-main { flex: 1; min-width: 0; display: flex; flex-direction: column; }
.chat-placeholder { flex: 1; display: flex; flex-direction: column; align-items: center; justify-content: center; text-align: center; padding: 30px; }
.chat-placeholder-icon { width: 64px; height: 64px; border-radius: 50%; display: grid; place-items: center; background: var(--bg-elevated); color: var(--text-muted); margin-bottom: 14px; }
.chat-placeholder-icon svg { width: 30px; height: 30px; }
.chat-placeholder-title { font: 700 var(--text-lg)/1.2 var(--display, var(--body)); color: var(--text-primary); }
.chat-placeholder-sub { margin-top: 6px; font: 500 var(--text-sm)/1.5 var(--body); color: var(--text-muted); max-width: 280px; }

/* Thread */
.chat-thread { display: flex; flex-direction: column; height: 100%; min-height: 0; }
.chat-thread-head { display: flex; align-items: center; gap: 11px; padding: 11px 14px; border-bottom: 1px solid var(--border); background: var(--bg-surface); }
.chat-back { display: none; place-items: center; width: 34px; height: 34px; background: transparent; border: 0; border-radius: 8px; color: var(--text-secondary); cursor: pointer; }
.chat-back svg { width: 20px; height: 20px; }
.chat-thread-head-text { min-width: 0; flex: 1 1 auto; }
/* Per-chat mute toggle, pinned to the far right of the thread header. */
.chat-head-mute { margin-left: auto; flex-shrink: 0; display: grid; place-items: center; width: 34px; height: 34px; background: transparent; border: 0; border-radius: 8px; color: var(--text-secondary); cursor: pointer; transition: color .12s, background .12s; }
.chat-head-mute:hover { background: var(--bg-elevated); color: var(--text-primary); }
.chat-head-mute svg { width: 19px; height: 19px; }
.chat-head-mute.is-muted { color: var(--accent); }
.chat-thread-title { font: 700 var(--text-base)/1.2 var(--display, var(--body)); color: var(--text-primary); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.chat-thread-sub { font: 500 var(--text-xs)/1 var(--body); color: var(--text-muted); margin-top: 2px; }

/* Desktop two-pane: the left list header (search) and the right thread header
   sit side by side, so their bottom borders must land on ONE line across both
   panes. The thread header is ~4px taller (its 42px avatar vs the 38px search
   controls), so nudge the search header's vertical padding 12→14px to match.
   Two-pane only (≥761px, above the max-width:760px single-pane collapse) — the
   mobile list header keeps its 12px. */
@media (min-width: 761px) {
  .chat-sidebar-head { padding-top: 14px; padding-bottom: 14px; }
}

.chat-messages { flex: 1; min-height: 0; overflow-y: auto; padding: 16px; display: flex; flex-direction: column; gap: 0; background: var(--bg-base); }
/* Anchor messages to the BOTTOM (just above the composer) like every real
   messenger — a short conversation no longer floats at the top with dead space
   below it. The auto-margin spacer collapses to 0 once messages overflow, so
   scrolling still works. Only when there ARE messages, so the empty "Say hello"
   state stays centred. */
.chat-messages:has(.chat-bubble-row)::before { content: ''; margin-top: auto; }
.chat-empty-thread { margin: auto; text-align: center; color: var(--text-muted); }
.chat-empty-thread-title { font: 700 var(--text-base)/1.2 var(--body); color: var(--text-secondary); }
.chat-empty-thread-sub { margin-top: 4px; font-size: var(--text-sm); }
/* Date divider — floats while you scroll its day (sticky), translucent + blur
   so messages glide beneath it. pointer-events:none: the full-width row must
   never steal taps from bubbles under its line. */
.chat-day { text-align: center; margin: 14px 0 10px; position: sticky; top: 6px; z-index: 3; pointer-events: none; }
.chat-day span { display: inline-block; padding: 3px 12px; border-radius: var(--radius-pill); background: color-mix(in srgb, var(--bg-elevated) 78%, transparent); -webkit-backdrop-filter: blur(8px); backdrop-filter: blur(8px); border: 1px solid var(--border); font: 600 11px/1 var(--body); color: var(--text-muted); }
/* "New messages" divider — a full-width accent line shown above the first unread
   message when you open a chat (lands you here instead of the very bottom). */
.chat-unread-line { display: flex; align-items: center; gap: 10px; margin: 12px 4px 8px; color: var(--accent); }
.chat-unread-line::before, .chat-unread-line::after { content: ''; flex: 1; height: 1px; background: color-mix(in srgb, var(--accent) 40%, transparent); }
.chat-unread-line span { font: 700 10px/1 var(--body); letter-spacing: .06em; text-transform: uppercase; }
/* Rows: avatar gutter (incoming group/dept) + sender+time run grouping. */
.chat-bubble-row { display: flex; align-items: flex-end; gap: 8px; margin-top: 2px; -webkit-touch-callout: none; -webkit-user-select: none; user-select: none; }
.chat-bubble-row.is-mine { justify-content: flex-end; }
.chat-bubble-row.chat-run-start { margin-top: 12px; }   /* breathing room between speakers/runs */
.chat-row-av { width: 34px; flex-shrink: 0; align-self: flex-end; }   /* gutter; avatar fills it on the first row of a run */
/* Stack = bubble + its reaction pills, so reactions sit just under the bubble,
   aligned to the speaker's side. Width cap lives here now (was on the bubble). */
.chat-bubble-stack { display: flex; flex-direction: column; align-items: flex-start; max-width: min(78%, 560px); min-width: 0; }
.chat-bubble-row.is-mine .chat-bubble-stack { align-items: flex-end; }
/* Incoming bubbles: a real tonal step off the --bg-base canvas (surface-on-base
   was ≈1.07:1 — invisible; the WhatsApp/Telegram pattern is a perceptible
   incoming fill). Own messages are the FILLED accent side (below). */
.chat-bubble { max-width: 100%; padding: 8px 11px 5px; border-radius: 16px; background: var(--bg-elevated); border: 1px solid var(--border); position: relative; }
/* Reaction pills under a bubble */
.chat-reactions { display: flex; flex-wrap: wrap; gap: 4px; margin-top: 4px; }
.chat-reaction { display: inline-flex; align-items: center; gap: 3px; padding: 2px 8px; border-radius: var(--radius-pill); background: var(--bg-elevated); border: 1px solid var(--border); font: 600 11px/1.5 var(--body); color: var(--text-secondary); cursor: pointer; }
.chat-reaction.is-mine { background: color-mix(in srgb, var(--accent) 18%, var(--bg-elevated)); border-color: color-mix(in srgb, var(--accent) 40%, var(--border)); color: var(--accent); }
/* Optimistic send — a faint "sending" clock until the server confirms; a tappable
   "Not sent" chip if it fails (the deterministic per-sender id makes retry safe). */
.chat-bubble-row.is-pending .chat-bubble { opacity: .68; }
.chat-sending { display: inline-flex; vertical-align: middle; margin-left: 3px; color: var(--text-muted); }
.chat-bubble-row.is-mine .chat-sending { color: color-mix(in srgb, var(--text-secondary) 80%, var(--text-primary)); }
.chat-sending svg { width: 11px; height: 11px; }
.chat-msg-failed { display: inline-flex; align-items: center; gap: 6px; margin-top: 4px; padding: 5px 10px; background: color-mix(in srgb, var(--danger) 12%, var(--bg-surface)); border: 1px solid color-mix(in srgb, var(--danger) 38%, var(--border)); border-radius: var(--radius-pill); color: var(--danger); font: 600 var(--text-xs)/1 var(--body); cursor: pointer; }
.chat-msg-failed svg { width: 13px; height: 13px; flex-shrink: 0; }
.chat-msg-failed:active { transform: scale(.97); }
/* WhatsApp-style "tail": the first bubble of a run loses its outer-top corner. */
.chat-bubble-row.chat-run-start:not(.is-mine) .chat-bubble { border-top-left-radius: 5px; }
.chat-bubble-row.chat-run-start.is-mine .chat-bubble { border-top-right-radius: 5px; }
/* Mine = FILLED, others = outlined — the instant own-vs-team read every current
   messenger uses (iMessage blue / WhatsApp green). Border dropped: the fill IS
   the edge. */
.chat-bubble-row.is-mine .chat-bubble { background: color-mix(in srgb, var(--accent) 30%, var(--bg-surface)); border-color: transparent; }
.chat-bubble-sender { font: 700 11px/1 var(--body); margin-bottom: 3px; }
.chat-bubble-text { font: 500 var(--text-sm)/1.4 var(--body); color: var(--text-primary); white-space: pre-wrap; word-break: break-word; }
.chat-bubble-time { float: right; margin: 4px 0 0 10px; font: 500 10px/1 var(--body); color: var(--text-muted); }

/* ── Reply-to-a-specific-message (quoted reply) ─────────────────────────── */
/* In-bubble quoted strip (the message this one replies to). Neutral translucent
   panel so it reads on BOTH incoming and mine bubbles; accent rail + name. */
.chat-reply-quote {
  display: block; width: 100%; box-sizing: border-box;
  margin: 0 0 5px; padding: 5px 9px; text-align: left; cursor: pointer;
  background: color-mix(in srgb, var(--text-primary) 7%, transparent);
  border: 0; border-left: 3px solid var(--accent); border-radius: 7px;
  font: inherit; color: inherit; -webkit-tap-highlight-color: transparent;
}
.chat-bubble-row.is-mine .chat-reply-quote {
  background: color-mix(in srgb, var(--text-primary) 12%, transparent);
  border-left-color: color-mix(in srgb, var(--accent) 65%, var(--text-primary));
}
.chat-reply-quote:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
/* "Forwarded" marker — subtle, sits at the top of the bubble like WhatsApp. */
.chat-fwd-label { display: flex; align-items: center; gap: 4px; margin-bottom: 3px; font: 500 11.5px/1.2 var(--body); font-style: italic; color: var(--text-muted); }
.chat-fwd-label svg { width: 13px; height: 13px; }
.chat-reply-quote-name { display: block; font: 700 11.5px/1.35 var(--body); color: var(--accent); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.chat-bubble-row.is-mine .chat-reply-quote-name { color: var(--text-primary); }
.chat-reply-quote-text { display: block; font: 500 12px/1.35 var(--body); color: var(--text-secondary); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }

/* Composer reply bar — "Replying to … · preview" above the input. */
.chat-reply-bar { display: flex; align-items: center; gap: 9px; margin: 0 8px 6px; padding: 7px 6px 7px 10px; background: var(--bg-elevated); border: 1px solid var(--border); border-left: 3px solid var(--accent); border-radius: 10px; }
.chat-reply-bar[hidden] { display: none; }
.chat-reply-bar-icon { flex: none; display: grid; place-items: center; color: var(--accent); }
.chat-reply-bar-icon svg { width: 18px; height: 18px; }
.chat-reply-bar-body { flex: 1 1 auto; min-width: 0; }
.chat-reply-bar-name { font: 700 12px/1.3 var(--body); color: var(--accent); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.chat-reply-bar-text { font: 500 12.5px/1.35 var(--body); color: var(--text-secondary); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.chat-reply-bar-x { flex: none; width: 28px; height: 28px; border: 0; background: transparent; color: var(--text-muted); border-radius: 50%; cursor: pointer; font-size: 15px; line-height: 1; display: grid; place-items: center; }
.chat-reply-bar-x:hover { background: var(--bg-base); color: var(--text-primary); }

/* Swipe-right-to-reply: a reply glyph revealed behind the bubble as it slides. */
.chat-bubble-row { position: relative; }
.chat-swipe-hint { position: absolute; left: 6px; top: 50%; width: 30px; height: 30px; border-radius: 50%; display: grid; place-items: center; color: var(--text-secondary); background: var(--bg-elevated); opacity: 0; transform: translateY(-50%) scale(0.7); transition: opacity .12s ease, transform .12s ease; pointer-events: none; }
/* Desktop-only "⋯" actions affordance (touch uses long-press/swipe). Hidden on
   touch entirely; on a fine pointer it reserves space and reveals on row hover. */
.chat-more-btn { display: none; flex-shrink: 0; align-self: center; width: 28px; height: 28px; border: 0; border-radius: 50%; background: transparent; color: var(--text-muted); cursor: pointer; place-items: center; }
.chat-more-btn svg { width: 18px; height: 18px; }
.chat-bubble-row.is-mine .chat-more-btn { order: -1; }
@media (hover: hover) and (pointer: fine) {
  .chat-more-btn { display: grid; opacity: 0; transition: opacity .12s ease; }
  .chat-bubble-row:hover .chat-more-btn { opacity: 1; }
  .chat-more-btn:hover { background: var(--bg-elevated); color: var(--text-primary); }
}
.chat-swipe-hint svg { width: 16px; height: 16px; }
.chat-bubble-row.chat-swiping .chat-swipe-hint { opacity: 0.9; }
.chat-bubble-row.chat-swipe-armed .chat-swipe-hint { color: var(--accent); transform: translateY(-50%) scale(1); }
/* Fade the incoming avatar out while swiping so the reply glyph sits in its place. */
.chat-row-av { transition: opacity .12s ease; }
.chat-bubble-row.chat-swiping .chat-row-av { opacity: 0; }

/* Jump-to-original flash when you tap a quoted strip. */
@keyframes chatJumpFlash { 0%, 100% { background: transparent; } 25% { background: color-mix(in srgb, var(--accent) 16%, transparent); } }
.chat-bubble-row.chat-jump-flash .chat-bubble { animation: chatJumpFlash 1.4s ease; border-radius: 16px; }
@media (prefers-reduced-motion: reduce) { .chat-bubble-row.chat-jump-flash .chat-bubble { animation: none; } .chat-swipe-hint { transition: none; } }
/* Sent/read ticks (1:1) + typing line */
.chat-tick { font-size: 11px; color: var(--text-muted); }
.chat-tick.is-read { color: var(--accent); }
/* On MY bubbles the accent tint drops --text-muted to ~2.2:1 (fails AA). Lift the
   incidental labels toward --text-primary (~5.3:1 dark / ~7:1 light); the accent
   read-tick / seen states are already high-contrast, so :not() leaves them alone. */
.chat-bubble-row.is-mine .chat-bubble-time,
.chat-bubble-row.is-mine .chat-tick:not(.is-read),
.chat-bubble-row.is-mine .chat-seen:not(.is-seen),
.chat-bubble-row.is-mine .chat-bubble-deleted {
  color: color-mix(in srgb, var(--text-secondary) 80%, var(--text-primary));
}
.chat-thread-sub.is-typing { color: var(--accent); font-style: italic; }
/* Quick-reply chips — a one-line scrollable row above the composer (one-tap sends). */
.chat-quick { display: flex; gap: 6px; align-items: center; padding: 7px 12px; overflow-x: auto; flex-shrink: 0; background: var(--bg-base); border-top: 1px solid var(--border); -webkit-overflow-scrolling: touch; scrollbar-width: none; }
.chat-quick::-webkit-scrollbar { display: none; }
.chat-quick-chip { flex-shrink: 0; padding: 7px 13px; border-radius: var(--radius-pill); background: var(--bg-elevated); border: 1px solid var(--border); font: 600 var(--text-sm)/1 var(--body); color: var(--text-secondary); white-space: nowrap; cursor: pointer; transition: background .12s, border-color .12s; }
.chat-quick-chip:hover { background: var(--bg-surface); border-color: color-mix(in srgb, var(--accent) 40%, var(--border)); color: var(--text-primary); }
.chat-quick-chip:active { transform: scale(.97); }

.chat-composer { display: flex; align-items: flex-end; gap: 7px; padding: 5px 8px; border-top: 1px solid var(--border); background: var(--bg-surface); }
.chat-input { flex: 1; min-width: 0; resize: none; padding: 6px 12px; background: var(--bg-base); border: 1px solid var(--border-input); border-radius: 16px; font: 500 var(--text-sm)/1.35 var(--body); color: var(--text-primary); max-height: 130px; }
.chat-input:focus { outline: none; border-color: var(--accent); }
.chat-send { display: grid; place-items: center; width: 34px; height: 34px; flex-shrink: 0; background: var(--accent); color: #fff; border: 0; border-radius: 50%; cursor: pointer; box-shadow: 0 0 12px var(--accent-glow); }
.chat-send:disabled { opacity: .5; cursor: default; }
.chat-send svg { width: 18px; height: 18px; }

/* New-chat modals */
/* .callout-modal gives no padding of its own (callout screens pad via their head/
   body); the chat modal puts content straight in, so it needs its own inset or the
   title + items sit flush against the edge. */
.chat-modal { max-width: 460px; width: 92vw; max-height: 80vh; display: flex; flex-direction: column; padding: 18px 20px 20px; }
.chat-modal-head { margin-bottom: 14px; }
.chat-modal-title { font: 700 var(--text-lg)/1.2 var(--display, var(--body)); color: var(--text-primary); }
.chat-modal-sub { margin-top: 3px; font: 500 var(--text-sm)/1.4 var(--body); color: var(--text-muted); }
.chat-modal-empty { padding: 22px; text-align: center; color: var(--text-muted); font-size: var(--text-sm); }
.chat-menu { display: flex; flex-direction: column; gap: 8px; }
.chat-menu-item { display: flex; align-items: center; gap: 13px; padding: 13px 14px; background: var(--bg-base); border: 1px solid var(--border); border-radius: var(--radius-md); cursor: pointer; text-align: left; transition: border-color .12s, background .12s; }
.chat-menu-item:hover { border-color: var(--accent); background: var(--bg-hover); }
.chat-menu-icon { display: grid; place-items: center; width: 38px; height: 38px; border-radius: 50%; background: var(--bg-elevated); color: var(--accent); flex-shrink: 0; }
.chat-menu-icon svg { width: 19px; height: 19px; }
.chat-menu-t { display: block; font: 700 var(--text-sm)/1.2 var(--body); color: var(--text-primary); }
.chat-menu-s { display: block; font: 500 var(--text-xs)/1.3 var(--body); color: var(--text-muted); margin-top: 2px; }
.chat-search-input, .chat-name-input { width: 100%; padding: 10px 13px; margin-bottom: 10px; background: var(--bg-base); border: 1px solid var(--border-input); border-radius: var(--radius-md); font: 500 var(--text-sm)/1 var(--body); color: var(--text-primary); }
.chat-search-input:focus, .chat-name-input:focus { outline: none; border-color: var(--accent); }
.chat-people-list { flex: 1; overflow-y: auto; min-height: 120px; max-height: 46vh; display: flex; flex-direction: column; gap: 2px; }
.chat-person { display: flex; align-items: center; gap: 11px; padding: 8px 8px; border-radius: var(--radius-sm); cursor: pointer; }
.chat-person:hover { background: var(--bg-hover); }
.chat-person-cb { width: 18px; height: 18px; accent-color: var(--accent); flex-shrink: 0; }
.chat-person-name { flex: 1; min-width: 0; font: 600 var(--text-sm)/1.2 var(--body); color: var(--text-primary); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.chat-person-go { flex-shrink: 0; padding: 6px 14px; background: var(--accent); color: #fff; border: 0; border-radius: var(--radius-pill); font: 600 var(--text-xs)/1 var(--body); cursor: pointer; }

/* New-group task routing — pick the department tasks raised in this group go to
   (a "Create task" button then appears in the group, delegating there). */
.chat-route { margin-top: 14px; }
.chat-route-label { font: 700 var(--text-sm)/1.2 var(--body); color: var(--text-primary); }
.chat-route-sub { margin-top: 3px; font: 500 var(--text-xs)/1.45 var(--body); color: var(--text-muted); }
.chat-route-types { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 11px; }
.chat-route-chip { display: inline-flex; align-items: center; gap: 7px; padding: 8px 12px; background: var(--bg-base); border: 1px solid var(--border); border-radius: var(--radius-pill); font: 600 var(--text-xs)/1 var(--body); color: var(--text-secondary); cursor: pointer; transition: border-color .12s, background .12s, color .12s; }
.chat-route-chip:hover { border-color: var(--accent); }
.chat-route-chip.is-active { border-color: var(--accent); background: color-mix(in srgb, var(--accent) 12%, var(--bg-surface)); color: var(--text-primary); }
.chat-route-dot { width: 10px; height: 10px; border-radius: 50%; background: var(--c, var(--text-muted)); flex-shrink: 0; }

/* New-group task routing via Site + Department dropdowns (the shared taxonomy
   dropdown — same widget as the staff picker / Projects form). */
.chat-group-routing { margin-top: 14px; }
.chat-route-dds { display: grid; grid-template-columns: 1fr 1fr; gap: 10px; margin-top: 11px; }
.chat-route-dd { min-width: 0; display: flex; flex-direction: column; gap: 5px; }
.chat-route-dd > label { font: 600 var(--text-xs)/1 var(--body); color: var(--text-secondary); }
.chat-route-dd .tasks-priority-dropdown { width: 100%; }
.chat-route-dd .tasks-priority-trigger { width: 100%; }
@media (max-width: 460px) { .chat-route-dds { grid-template-columns: 1fr; } }
/* Group settings sheet (creator-only): name + access field + task routing.
   Scrolls within the modal when it outgrows the viewport. */
.chat-group-edit { max-height: min(70vh, 560px); overflow-y: auto; margin-top: 2px; }
.chat-group-name { width: 100%; box-sizing: border-box; margin-top: 6px; padding: 10px 12px;
  background: var(--bg-elevated); border: 1px solid var(--border); border-radius: var(--radius-md);
  color: var(--text-primary); font: 600 var(--text-base)/1.3 var(--body); }
.chat-group-name:focus { outline: none; border-color: var(--accent); }
.chat-group-edit .cax-help, .chat-group-edit .cax-depts { margin-top: 8px; }
.chat-modal-actions { display: flex; justify-content: flex-end; gap: 10px; margin-top: 14px; }

/* ── Task-group "Banner alerts" control (compose.alertBannerControl_) — a toggle
   (reuses .ah-switch) + a fixed colour palette. Shown in New Group + Group
   settings. When on, tasks become claimable cards + a corner banner. ───────── */
.chat-alert-ctrl { margin-top: 12px; padding-top: 12px; border-top: 1px solid var(--border); }
.chat-alert-row { display: flex; align-items: center; gap: 12px; cursor: pointer; }
.chat-alert-text { flex: 1 1 auto; min-width: 0; }
.chat-alert-t { display: block; font: 600 var(--text-base)/1.3 var(--body); color: var(--text-primary); }
.chat-alert-s { display: block; font: 500 var(--text-sm)/1.4 var(--body); color: var(--text-secondary); margin-top: 2px; }
.chat-alert-colours { margin-top: 12px; }
.chat-alert-colours[hidden] { display: none; }
.chat-alert-colours-label { display: block; font: 600 var(--text-sm)/1.3 var(--body); color: var(--text-secondary); margin-bottom: 8px; }
.chat-alert-swatches { display: flex; flex-wrap: wrap; gap: 10px; }
.chat-alert-swatch { width: 30px; height: 30px; border-radius: 50%; background: var(--sw); border: 2px solid var(--border);
  cursor: pointer; padding: 0; transition: transform .12s, box-shadow .12s; }
.chat-alert-swatch:active { transform: scale(0.92); }
.chat-alert-swatch.is-sel { border-color: var(--text-primary); box-shadow: 0 0 0 2px var(--bg-surface), 0 0 0 4px var(--sw); }

/* ── Manual guest request (compose.openManualRequest) — reception logs a phone-in
   / walk-up request into the Guest requests thread. Service buttons + −/+ steppers
   mirror the public guest page, in app styling. ─────────────────────────────── */
.creq-body { max-height: min(72vh, 600px); overflow-y: auto; margin-top: 2px; }
.creq-flabel { display: block; font: 600 var(--text-sm)/1.3 var(--body); color: var(--text-secondary); margin: 14px 0 6px; }
.creq-flabel:first-child { margin-top: 4px; }
.creq-room { display: flex; gap: 8px; align-items: stretch; }
.creq-room-input { flex: 1 1 auto; margin: 0; }
.creq-room-pick { flex: 0 0 auto; white-space: nowrap; }
.creq-note { width: 100%; box-sizing: border-box; margin: 0; resize: vertical; min-height: 44px;
  padding: 10px 12px; background: var(--bg-elevated); border: 1px solid var(--border);
  border-radius: var(--radius-md); color: var(--text-primary); font: 500 var(--text-base)/1.4 var(--body); }
.creq-note:focus { outline: none; border-color: var(--accent); }
.creq-svcs { display: grid; grid-template-columns: repeat(auto-fill, minmax(120px, 1fr)); gap: 8px; }
.creq-svc { display: flex; align-items: center; gap: 8px; padding: 10px 11px; text-align: left;
  background: var(--bg-elevated); border: 1px solid var(--border); border-radius: var(--radius-md);
  color: var(--text-primary); cursor: pointer; transition: border-color .12s, background .12s; }
.creq-svc:active { transform: scale(0.98); }
.creq-svc.is-active { border-color: var(--accent); background: color-mix(in srgb, var(--accent) 12%, var(--bg-elevated)); }
.creq-svc-emoji { font-size: 18px; line-height: 1; flex: 0 0 auto; }
.creq-svc-label { font: 600 var(--text-sm)/1.2 var(--body); min-width: 0; }
.creq-note-hint { margin-top: 10px; padding: 9px 11px; border-radius: var(--radius-md);
  background: color-mix(in srgb, #16a34a 10%, var(--bg-base)); border: 1px solid color-mix(in srgb, #16a34a 35%, var(--border));
  color: var(--text-secondary); font: 500 var(--text-sm)/1.4 var(--body); }
.creq-opts { margin-top: 10px; display: flex; flex-direction: column; gap: 2px; }
.creq-opt { display: flex; align-items: center; justify-content: space-between; gap: 12px; padding: 7px 2px;
  border-bottom: 1px solid var(--border-subtle, var(--border)); }
.creq-opt:last-child { border-bottom: none; }
.creq-opt-label { font: 600 var(--text-sm)/1.3 var(--body); color: var(--text-primary); }
.creq-step { display: inline-flex; align-items: center; gap: 12px; }
.creq-step-btn { width: 30px; height: 30px; border-radius: 50%; border: 1px solid var(--border);
  background: var(--bg-elevated); color: var(--text-primary); font: 700 18px/1 var(--body);
  display: inline-flex; align-items: center; justify-content: center; cursor: pointer; }
.creq-step-btn:disabled { opacity: .35; cursor: default; }
.creq-step-btn:not(:disabled):active { background: var(--accent); border-color: var(--accent); color: #fff; }
.creq-step-qty { min-width: 18px; text-align: center; font: 700 var(--text-base)/1 var(--body); }
.creq-step-qty.is-zero { color: var(--text-muted, var(--text-secondary)); }

.chat-scope { margin-top: 16px; }
.chat-scope-label { font: 500 var(--text-sm)/1.4 var(--body); color: var(--text-secondary); margin-bottom: 8px; }
.chat-scope-site { margin-top: 10px; }

@media (max-width: 760px) {
  /* Chat is a full-height app pane (scrolling message list + pinned composer),
     not a scrolling page like Tasks. Size it with a pure FLEX chain — exactly
     the .fs-settings-body pattern — so the pane is always EXACTLY the height
     .main gives it (viewport minus the app-bar incl. its safe-area top pad) and
     can never be taller. A percentage height:100% chain (the old approach) let
     the pane spill past the visible viewport on iOS, so the WINDOW itself
     scrolled — dragging the app-bar up under the status bar and leaving the
     composer short of the bottom. Flex can't exceed its container, so the
     app-bar stays put exactly like every other module. (min-height:0 each step
     lets the message list, not the pane, be the thing that scrolls; height:auto
     + flex override the base desktop calc()/min-height:460 so a small phone
     can't be forced past the viewport.) */
  .main:has(#module-view-chat.active) { padding: 0; max-width: none; overflow: hidden; display: flex; flex-direction: column; }
  #module-view-chat.active { flex: 1 1 auto; min-height: 0; display: flex; }
  .chat-layout { flex: 1 1 auto; min-height: 0; height: auto; border: 0; border-radius: 0; box-shadow: none; }
  .chat-sidebar { width: 100%; border-right: 0; }
  .chat-main { display: none; }
  .chat-layout.is-thread-open .chat-sidebar { display: none; }
  .chat-layout.is-thread-open .chat-main { display: flex; }
  .chat-back { display: grid; }
  /* ── Immersive conversation (thread open) on mobile ──
     Hide the global app bar; the conversation header becomes the only top bar. */
  .app-shell:has(.chat-layout.is-thread-open) .app-bar { display: none; }
  /* DO NOT position:fixed the chat pane. On iOS a position:fixed element whose
     containing block is a transformed ancestor (our .app-shell has a permanent
     transform, css/02:202) is rendered env(safe-area-inset-top) px too HIGH under
     viewport-fit=cover — confirmed on-device: mainTop:-59 with a 59px inset, which
     slid the header up behind the status bar. Instead .main stays in NORMAL FLOW,
     exactly like every other module's top bar (which is why theirs never breaks):
     the shell is locked (html/body overflow:hidden) so nothing scrolls, the header
     sits at the top with its safe-area padding, and we only SHRINK the pane to the
     visible-viewport height when the keyboard is up so the composer rests on it. */
  body.kb-open .main:has(.chat-layout.is-thread-open) {
    flex: none;
    height: var(--vvh, 100%);
  }
  /* OFFLINE + thread open: the global #offlineBanner normally sits in NORMAL
     FLOW at the very top of <body>, stealing ~its height and pushing the whole
     (locked, full-height) chat pane down. That shoved the composer below the
     keyboard AND stacked a second status-bar gap under the banner (the header
     adds its own safe-area inset on top). Subtracting the banner height from the
     pane (the earlier attempt) was fragile against iOS keyboard panning and left
     the composer floating above the keyboard. Instead, INSIDE an open thread we
     OVERLAY the banner (position:fixed → out of flow) so the chat pane keeps the
     EXACT proven ONLINE geometry (.main = --vvh, composer rests on the keyboard),
     and pad the header down by the banner's measured height so its content clears
     it. Scoped to the thread only — every other offline screen keeps the in-flow
     banner unchanged. */
  body.is-offline:has(.chat-layout.is-thread-open) #offlineBanner {
    position: fixed;
    top: 0; left: 0; right: 0;
  }
  /* HEADER = the app bar's own safe-area recipe (.app-bar, css/09:1187) so its
     content sits below the status bar / notch and its solid bg bleeds up behind.
     flex-shrink:0 so the scrolling message list can never squeeze it. */
  .chat-layout.is-thread-open .chat-thread-head {
    flex-shrink: 0;
    /* Floor of 54px so the header ALWAYS clears the status bar — even when iOS
       reports env(safe-area-inset-top) as 0, which it can do (stubbornly, not
       just briefly) on a standalone-PWA cold launch until a layout pass. In the
       normal case env+11 (≈58–70px on notch/Dynamic-Island phones) is larger
       than the floor, so those devices are unchanged; the floor only catches the
       0-inset failure that was putting the header back behind the clock. */
    padding-top: max(54px, calc(env(safe-area-inset-top) + 11px));
  }
  /* When offline, the OVERLAID banner already clears the status bar, so the
     header only needs to clear the BANNER itself (its measured height + a small
     gap) — NOT add a second safe-area inset on top of it. That second inset was
     the big empty gap below the banner. --offline-h is measured live in
     index.html (updates as the banner text wraps to 1/2 lines). */
  body.is-offline .chat-layout.is-thread-open .chat-thread-head {
    padding-top: max(54px, calc(var(--offline-h, 44px) + 8px));
  }
  /* Composer rests at the bottom of the pinned pane — the SAME snug bar whether the
     keyboard is up or down (Radek's ask: keyboard-up felt perfect, match it when
     down). A flat 4px keeps the box tight to the bottom; the bar's surface bg fills
     down to the screen edge so the home indicator floats over it (no floating gap).
     Was a ~16px home-indicator inset when down, which read as a taller bar. */
  .chat-composer-wrap {
    padding-bottom: 4px;
    background: var(--bg-surface);
  }
}

/* ═══════════════════════════════════════════════════════════════════════
   CHAT — Phase 2 (photos, task cards, emergency alarm)
   ═══════════════════════════════════════════════════════════════════════ */
/* Composer tool buttons (photo / task) */
.chat-tool { display: grid; place-items: center; width: 34px; height: 34px; flex-shrink: 0; background: var(--bg-base); border: 1px solid var(--border); border-radius: 50%; color: var(--text-secondary); cursor: pointer; transition: border-color .12s, color .12s; }
.chat-tool:hover { border-color: var(--accent); color: var(--accent); }
.chat-tool svg { width: 18px; height: 18px; }
.chat-send.is-busy { opacity: .6; }

/* ── Composer: [+] [message] [camera] [mic/send] + the "+" popover ── */
.chat-composer-wrap { position: relative; flex-shrink: 0; }
/* These set display, which would otherwise defeat the [hidden] attribute. */
.chat-send[hidden], .chat-mic[hidden], .chat-plus-menu[hidden] { display: none; }
.chat-plus.is-open { border-color: var(--accent); color: var(--accent); }
.chat-mic { display: grid; place-items: center; width: 34px; height: 34px; flex-shrink: 0; background: var(--bg-base); border: 1px solid var(--border); border-radius: 50%; color: var(--text-secondary); cursor: pointer; touch-action: none; -webkit-user-select: none; user-select: none; transition: transform .12s, background .12s, color .12s, border-color .12s; }
.chat-mic svg { width: 19px; height: 19px; }
.chat-mic:active,
.chat-composer-wrap.is-recording .chat-mic { background: var(--danger); color: #fff; border-color: var(--danger); transform: scale(1.12); position: relative; z-index: 2; }
.chat-plus-menu { position: absolute; bottom: calc(100% + 8px); left: 10px; right: 10px; max-width: 380px; z-index: 6; display: flex; flex-direction: column; gap: 9px; padding: 12px; background: var(--bg-surface); border: 1px solid var(--border); border-radius: var(--radius-lg, 16px); box-shadow: 0 14px 36px rgba(0, 0, 0, .30); }
.chat-plus-quick { display: flex; flex-wrap: wrap; gap: 7px; }
.chat-plus-item { display: flex; align-items: center; gap: 10px; width: 100%; padding: 11px 12px; border: 0; background: var(--bg-base); border-radius: var(--radius-md); color: var(--text-primary); font: 600 var(--text-sm)/1 var(--body); cursor: pointer; transition: background .12s; }
.chat-plus-item:hover { background: var(--bg-elevated); }
.chat-plus-item svg { width: 18px; height: 18px; color: var(--accent); flex-shrink: 0; }
/* Jump-to-latest — floats just above the composer when scrolled up; badged with the
   count of messages that arrived while reading up-thread (matches the nudge accent). */
.chat-jump-latest { position: absolute; bottom: calc(100% + 10px); right: 12px; z-index: 5; display: grid; place-items: center; width: 40px; height: 40px; border-radius: 50%; border: 1px solid color-mix(in srgb, var(--accent) 42%, var(--border)); background: color-mix(in srgb, var(--accent) 14%, var(--bg-elevated)); color: var(--accent); box-shadow: 0 8px 22px rgba(0, 0, 0, .22); cursor: pointer; transition: transform .12s ease, background .12s ease; }
.chat-jump-latest[hidden] { display: none; }
.chat-jump-latest:hover { background: color-mix(in srgb, var(--accent) 22%, var(--bg-elevated)); }
.chat-jump-latest:active { transform: scale(.92); }
.chat-jump-latest svg { width: 22px; height: 22px; }
.chat-jump-count { position: absolute; top: -5px; right: -4px; min-width: 18px; height: 18px; padding: 0 5px; border-radius: 9px; background: var(--accent); color: #fff; font: 700 11px/18px var(--body); text-align: center; box-shadow: 0 0 0 2px var(--bg-surface); }
.chat-jump-count[hidden] { display: none; }
@media (prefers-reduced-motion: reduce) { .chat-jump-latest { transition: none; } }
.chat-recbar { position: absolute; top: 0; bottom: 0; left: 0; right: 58px; display: none; align-items: center; gap: 10px; padding: 11px 16px; background: var(--bg-surface); }
.chat-composer-wrap.is-recording .chat-recbar { display: flex; }
.chat-composer-wrap.is-recording .chat-composer > :not(.chat-mic) { opacity: 0; pointer-events: none; }
.chat-rec-dot { width: 11px; height: 11px; border-radius: 50%; background: var(--danger); flex-shrink: 0; animation: chatRecPulse 1s ease-in-out infinite; }
@keyframes chatRecPulse { 0%, 100% { opacity: 1; } 50% { opacity: .2; } }
.chat-rec-time { font: 700 var(--text-sm)/1 var(--body); color: var(--text-primary); min-width: 40px; flex-shrink: 0; }
.chat-rec-hint { font: 500 var(--text-xs)/1.2 var(--body); color: var(--text-muted); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.chat-rec-hint.is-cancel { color: var(--danger); font-weight: 700; }
/* Voice-note player — play/pause + waveform progress + tap-seek + speed */
.chat-voice { display: flex; align-items: center; gap: 10px; min-width: 210px; max-width: 270px; padding: 2px 0; }
.chat-voice-play { flex-shrink: 0; width: 38px; height: 38px; border-radius: 50%; border: 0; background: var(--accent); color: #fff; display: grid; place-items: center; cursor: pointer; transition: transform .1s; }
.chat-voice-play:active { transform: scale(.92); }
.chat-voice-play svg { width: 18px; height: 18px; }
.chat-voice .vico { display: inline-flex; }
.chat-voice .vico-pause { display: none; }
.chat-voice.is-playing .vico-play { display: none; }
.chat-voice.is-playing .vico-pause { display: inline-flex; }
.chat-voice-main { flex: 1; min-width: 0; }
.chat-voice-wave { display: flex; align-items: center; gap: 2px; height: 30px; cursor: pointer; }
.chat-voice-bar { flex: 1; min-width: 2px; border-radius: 2px; background: color-mix(in srgb, var(--text-muted) 50%, transparent); transition: background .08s; }
.chat-voice-bar.is-played { background: var(--accent); }
.chat-voice-row { display: flex; align-items: center; justify-content: space-between; margin-top: 4px; }
.chat-voice-time { font: 600 11px/1 var(--body); color: var(--text-muted); }
.chat-voice-speed { appearance: none; -webkit-appearance: none; background: var(--bg-elevated); border: 1px solid var(--border); border-radius: var(--radius-pill); padding: 2px 8px; font: 700 10px/1 var(--body); color: var(--text-secondary); cursor: pointer; transition: transform .1s; }
.chat-voice-speed:active { transform: scale(.93); }

/* Motion + depth polish */
/* (dark theme: NO bubble shadow — tonal steps carry the depth; a black shadow
   on a near-black canvas only muddies edges. Light mode re-adds a crisp print
   shadow in css/11.) */
@keyframes chatMsgIn { from { opacity: 0; transform: translateY(9px); } to { opacity: 1; transform: none; } }
.chat-bubble-row.is-new { animation: chatMsgIn .22s cubic-bezier(.4, 0, .2, 1); }
.chat-reaction:active { transform: scale(.93); }
@media (prefers-reduced-motion: reduce) { .chat-bubble-row.is-new { animation: none; } }

/* Group/department read receipts — inline "Seen N" + the seen-by sheet */
.chat-seen { appearance: none; -webkit-appearance: none; background: none; border: 0; margin: 0; padding: 0 0 0 5px; font: 600 11px/1 var(--body); color: var(--text-muted); cursor: pointer; vertical-align: baseline; }
.chat-seen.is-seen { color: var(--accent); }
.chat-seen-sheet .chat-seen-head { font: 700 var(--text-base)/1.2 var(--display, var(--body)); color: var(--text-primary); padding: 2px 2px 10px; }
.chat-seen-list { max-height: 48vh; overflow-y: auto; }
.chat-seen-group { font: 700 var(--text-xs)/1 var(--body); color: var(--text-muted); text-transform: uppercase; letter-spacing: .04em; padding: 10px 2px 6px; }
.chat-seen-row { display: flex; align-items: center; gap: 10px; padding: 7px 2px; }
.chat-seen-name { flex: 1; min-width: 0; font: 600 var(--text-sm)/1.2 var(--body); color: var(--text-primary); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.chat-seen-at { font: 500 11px/1 var(--body); color: var(--text-muted); flex-shrink: 0; }
.chat-seen-empty { padding: 16px 2px; color: var(--text-muted); font: 500 var(--text-sm)/1 var(--body); text-align: center; }

/* Delete-message: danger menu item + "deleted" tombstone bubble */
.chat-msg-menu-item.is-danger { color: var(--danger); }
.chat-msg-menu-item.is-danger svg { color: var(--danger); }
.chat-bubble-deleted { display: inline-flex; align-items: center; gap: 6px; font: 500 var(--text-sm)/1.3 var(--body); font-style: italic; color: var(--text-muted); }
.chat-bubble-deleted svg { width: 14px; height: 14px; flex-shrink: 0; }

/* Photo attachments in bubbles */
.chat-imgs { display: grid; gap: 4px; margin-bottom: 4px; }
.chat-imgs-1 { grid-template-columns: 1fr; }
.chat-imgs-multi { grid-template-columns: 1fr 1fr; max-width: 260px; }
.chat-img { width: 100%; max-height: 260px; object-fit: cover; border-radius: 10px; cursor: pointer; display: block; background: var(--bg-elevated); }
.chat-imgs-1 .chat-img { max-width: 260px; }
/* Multi-photo grid: capped at 4 SQUARE tiles (space reserved → no reflow on decode),
   the last carrying a "+N" overlay when there are more. */
.chat-img-tile { position: relative; display: block; min-width: 0; }
.chat-imgs-multi .chat-img { aspect-ratio: 1 / 1; max-height: none; }
.chat-img-more { position: absolute; inset: 0; display: grid; place-items: center; border-radius: 10px; background: rgba(0, 0, 0, .5); color: #fff; font: 800 22px/1 var(--display, var(--body)); pointer-events: none; }
.chat-bubble-img { padding: 4px 4px 5px; }

/* Full-screen photo view now reuses the shared AngelHubPhotoViewer
   (.photo-viewer-overlay, css/04) — the bespoke .chat-lightbox was removed. */

/* Long-press message menu — sharp, slim, professional (WhatsApp/iOS-style).
   One crisp card: a quiet reaction row (bare emoji glyphs, no toy bordered
   pills) over hairline-separated action rows with neutral icons. No native
   text-selection / "Copy" callout (we own long-press) — Copy is a menu action. */
.chat-msg-menu-overlay {
  position: fixed; inset: 0; z-index: 4100; background: rgba(0,0,0,.5);
  display: flex; align-items: flex-end; justify-content: center;
  padding: 12px; padding-bottom: max(12px, env(safe-area-inset-bottom));
  -webkit-touch-callout: none; -webkit-user-select: none; user-select: none;
  /* iOS WebKit: self-composite so the flat fixed overlay can never paint
     behind the composited .app-shell (§11e latent-bug family). */
  transform: translateZ(0); will-change: transform;
}
.chat-msg-menu {
  width: 100%; max-width: 360px; background: var(--bg-surface);
  border: 1px solid var(--border); border-radius: 16px; overflow: hidden;
  box-shadow: 0 14px 40px rgba(0,0,0,.34);
  -webkit-touch-callout: none; -webkit-user-select: none; user-select: none;
}
/* reaction row: bare glyphs, evenly spread, quiet hover — not toy pills */
.chat-msg-menu-emojis { display: flex; justify-content: space-between; gap: 2px; padding: 6px; border-bottom: 1px solid var(--border); }
.chat-emoji-btn { flex: 1; height: 46px; display: grid; place-items: center; background: transparent; border: 0; border-radius: 12px; font-size: 27px; line-height: 1; cursor: pointer; -webkit-touch-callout: none; -webkit-user-select: none; user-select: none; transition: transform .12s ease, background .12s ease; }
.chat-emoji-btn:hover { background: var(--bg-elevated); }
.chat-emoji-btn:active { transform: scale(1.22); }
/* "+" = open the full picker — neutral glyph, not a reaction; no scale-pop */
.chat-emoji-more { color: var(--text-secondary); }
.chat-emoji-more svg { width: 22px; height: 22px; }
.chat-emoji-more:active { transform: none; }
/* quoted message preview (2 lines max) */
.chat-msg-menu-quote { font: 500 var(--text-xs)/1.45 var(--body); color: var(--text-secondary); padding: 9px 14px; border-bottom: 1px solid var(--border); display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; }
/* action rows: slim, neutral icons, hairline separators */
.chat-msg-menu-item { display: flex; align-items: center; gap: 13px; width: 100%; padding: 13px 14px; background: transparent; border: 0; border-top: 1px solid var(--border); font: 600 var(--text-base)/1 var(--body); color: var(--text-primary); cursor: pointer; text-align: left; }
.chat-msg-menu-item:first-child,
.chat-msg-menu-quote + .chat-msg-menu-item,
.chat-msg-menu-emojis + .chat-msg-menu-item { border-top: 0; }
.chat-msg-menu-item:hover, .chat-msg-menu-item:active { background: var(--bg-elevated); }
.chat-msg-menu-item svg { width: 19px; height: 19px; color: var(--text-secondary); flex-shrink: 0; }
.chat-msg-menu-cancel { display: block; width: 100%; padding: 13px; background: transparent; border: 0; border-top: 1px solid var(--border); font: 700 var(--text-sm)/1 var(--body); color: var(--text-secondary); cursor: pointer; text-align: center; }
.chat-msg-menu-cancel:hover { background: var(--bg-elevated); }
/* "Can't do now" reason sheet — reuses the chat action-sheet shell. Reason chips
   are single-select (reuse .chat-req-btn is-ghost↔is-primary) + an optional note. */
.chat-hold-body { padding: 12px 14px 14px; }
.chat-hold-reasons { display: flex; flex-wrap: wrap; gap: 8px; margin-bottom: 10px; }
.chat-hold-reasons .chat-req-btn { flex: 0 1 auto; min-height: 40px; padding: 9px 14px; font-size: var(--text-sm); }
.chat-hold-note { width: 100%; box-sizing: border-box; min-height: 56px; padding: 9px 11px; resize: vertical;
  border: 1px solid var(--border); border-radius: 12px; background: var(--bg-elevated); color: var(--text-primary);
  font: 500 16px/1.4 var(--body); -webkit-user-select: text; user-select: text; }
.chat-hold-note:focus { outline: none; border-color: var(--accent); }
.chat-hold-actions { display: flex; gap: 10px; margin-top: 12px; }

/* Task card in thread — the TOP elevation step. The canvas is --bg-base, plain
   bubbles one step up; an action card must unmistakably read "interactive
   object": elevated fill + full-strength border + a soft lift shadow.
   (Was --bg-base — literally the canvas colour, the "cards blend into the
   background" bug.) !important: defeats the .is-mine accent tint on own-sent
   cards, deliberately. */
.chat-bubble-task {
  background: var(--bg-elevated) !important;
  border-color: var(--border-strong) !important;
  border-radius: 14px;
  box-shadow: 0 4px 14px rgba(0, 0, 0, .25);
}
.chat-task-card { display: flex; align-items: center; gap: 10px; }
.chat-task-card[data-open-task], .chat-task-card[data-open-doc], .chat-task-card[data-open-ref] { cursor: pointer; }
.chat-task-ico { display: grid; place-items: center; width: 34px; height: 34px; flex-shrink: 0; border-radius: 9px; background: color-mix(in srgb, var(--accent) 14%, transparent); color: var(--accent); }
.chat-doc-ico { background: color-mix(in srgb, #7c3aed 16%, transparent); color: #7c3aed; }
/* Generic shared-item ref card — icon tint per type (task=accent; rest distinct hues) */
.chat-ref-card[data-ref-type="task"]       .chat-ref-ico { background: color-mix(in srgb, var(--accent) 14%, transparent); color: var(--accent); }
.chat-ref-card[data-ref-type="asset"]      .chat-ref-ico { background: color-mix(in srgb, #0891b2 16%, transparent); color: #0891b2; }
.chat-ref-card[data-ref-type="callout"]    .chat-ref-ico { background: color-mix(in srgb, #dc2626 14%, transparent); color: #dc2626; }
.chat-ref-card[data-ref-type="contractor"] .chat-ref-ico { background: color-mix(in srgb, #2563eb 14%, transparent); color: #2563eb; }
.chat-ref-card[data-ref-type="project"]    .chat-ref-ico { background: color-mix(in srgb, #7c3aed 16%, transparent); color: #7c3aed; }
.chat-ref-card[data-ref-type="order"]      .chat-ref-ico { background: color-mix(in srgb, #16a34a 16%, transparent); color: #16a34a; }
.chat-ref-card[data-ref-type="doc"]        .chat-ref-ico { background: color-mix(in srgb, #7c3aed 16%, transparent); color: #7c3aed; }
.chat-ref-card[data-ref-type="checklist"]  .chat-ref-ico { background: color-mix(in srgb, #4f46e5 16%, transparent); color: #4f46e5; }
.chat-ref-card[data-ref-type="compliance"] .chat-ref-ico { background: color-mix(in srgb, #0d9488 16%, transparent); color: #0d9488; }
.chat-ref-card[data-ref-type="audit"]      .chat-ref-ico { background: color-mix(in srgb, #ca8a04 16%, transparent); color: #ca8a04; }
.chat-ref-card[data-ref-type="supplier"]   .chat-ref-ico { background: color-mix(in srgb, #d97706 16%, transparent); color: #d97706; }
.chat-ref-card[data-ref-type="inventory"]  .chat-ref-ico { background: color-mix(in srgb, #0891b2 16%, transparent); color: #0891b2; }
.chat-ref-card[data-ref-type="safety"]     .chat-ref-ico { background: color-mix(in srgb, #e11d48 16%, transparent); color: #e11d48; }
.chat-task-ico svg { width: 18px; height: 18px; }
.chat-task-body { display: flex; flex-direction: column; min-width: 0; flex: 1; }
.chat-task-label { font: 700 10px/1.2 var(--body); text-transform: uppercase; letter-spacing: .05em; color: var(--text-muted); }
.chat-task-title { font: 600 var(--text-sm)/1.3 var(--body); color: var(--text-primary); }
.chat-task-go { flex-shrink: 0; font: 700 var(--text-xs)/1 var(--body); color: var(--accent); padding: 5px 11px; border: 1px solid color-mix(in srgb, var(--accent) 40%, var(--border)); border-radius: var(--radius-pill); }

/* ── Claimable request card (guest / room service) ─────────────────────────
   Same card shell as task/doc/ref, but it stacks (label · title · note · eco ·
   state) and carries an action row of Claim / Done / Hand-back buttons below.
   The bell tint is a warm "service" amber; the card fades once it's done. */
.chat-request-card { align-items: flex-start; gap: 11px; }
.chat-req-ico { background: color-mix(in srgb, #d97706 16%, transparent); color: #d97706; }
.chat-request-card.is-done,
.chat-request-card.is-accepted,
.chat-request-card.is-cancelled,
.chat-request-card.is-declined { opacity: .72; }
.chat-req-note { font: 500 var(--text-sm)/1.35 var(--body); color: var(--text-secondary); margin-top: 2px; }
.chat-req-eco { display: inline-flex; align-items: center; gap: 5px; margin-top: 5px; padding: 4px 9px;
  font: 600 var(--text-xs)/1.25 var(--body); color: #15803d; align-self: flex-start;
  background: color-mix(in srgb, #16a34a 12%, transparent);
  border: 1px solid color-mix(in srgb, #16a34a 30%, var(--border)); border-radius: var(--radius-pill); }
.chat-req-eco svg { width: 13px; height: 13px; flex-shrink: 0; }
.chat-req-state { display: inline-flex; align-items: center; margin-top: 6px; font: 700 var(--text-xs)/1.2 var(--body); }
.chat-req-state.is-open    { color: var(--text-muted); }
.chat-req-state.is-claimed { color: var(--accent); }
.chat-req-state.is-done    { color: #15803d; }
.chat-req-state.is-declined { color: #dc2626; }
.chat-req-state.is-hold     { color: #a16207; }
.chat-req-state.is-cancelled { color: var(--text-muted); }
.chat-req-state svg { width: 14px; height: 14px; margin-right: 5px; flex-shrink: 0; }
/* A request is the one card you ACT on from the thread, so it runs wider than a
   normal chat bubble — near the full chat column — to give its claim buttons room
   for an easy thumb-tap. Overrides the 78%/560px bubble cap (left-aligned, system
   sender). :has() is already used elsewhere in chat. */
.chat-bubble-row:has(.chat-request-card) .chat-bubble-stack { max-width: min(94%, 640px); }
.chat-request-card .chat-task-title { font-size: var(--text-base); font-weight: 700; }
/* Action row — sits under the card, inside the same bubble (so a button tap
   never bubbles up to the card's "open task"). Buttons stretch to fill the wider
   card: one button (Claim) goes full-width; two (Done + Hand-back) split evenly —
   both large, ≥44px tall for a comfortable tap. */
.chat-req-actions { display: flex; flex-wrap: wrap; gap: 10px; margin-top: 12px; }
.chat-req-btn { flex: 1 1 auto; justify-content: center; min-height: 44px;
  display: inline-flex; align-items: center; gap: 7px; padding: 11px 18px; cursor: pointer;
  font: 700 var(--text-md)/1 var(--body); border-radius: var(--radius-pill); border: 1px solid transparent;
  transition: background .12s, border-color .12s, opacity .12s; }
.chat-req-btn svg { width: 17px; height: 17px; }
.chat-req-btn.is-primary { background: var(--accent); color: #fff; }
.chat-req-btn.is-primary:hover { background: color-mix(in srgb, var(--accent) 88%, #000); }
.chat-req-btn.is-ghost { background: transparent; color: var(--text-secondary); border-color: var(--border); }
.chat-req-btn.is-ghost:hover { background: var(--bg-elevated); color: var(--text-primary); }
.chat-req-btn.is-busy, .chat-req-btn:disabled { opacity: .55; pointer-events: none; }
/* State read at a glance across the whole thread — a coloured left spine + a faint
   matching tint on the card (mirrors the delivery-row state shade). So you can scan
   a stack of requests and instantly tell which still needs picking up, which someone
   is on, and which is finished: open = amber (act now), claimed = teal (in hand),
   done = green. The state class lives on the inner .chat-request-card; :has lifts it
   to the bubble shell (already used elsewhere in chat). */
.chat-bubble-task:has(.chat-request-card) { border-left-width: 3px; }
.chat-bubble-task:has(.chat-request-card.is-open) {
  border-left-color: #d97706 !important;
  background: color-mix(in srgb, #d97706 8%, var(--bg-elevated)) !important;
}
.chat-bubble-task:has(.chat-request-card.is-claimed) {
  border-left-color: var(--accent) !important;
  background: color-mix(in srgb, var(--accent) 9%, var(--bg-elevated)) !important;
}
.chat-bubble-task:has(.chat-request-card.is-done),
.chat-bubble-task:has(.chat-request-card.is-accepted) {
  border-left-color: #16a34a !important;
  background: color-mix(in srgb, #16a34a 6%, var(--bg-elevated)) !important;
}
/* A declined decision card — red spine + faint red tint, so a "no" reads at a glance. */
.chat-bubble-task:has(.chat-request-card.is-declined) {
  border-left-color: #dc2626 !important;
  background: color-mix(in srgb, #dc2626 7%, var(--bg-elevated)) !important;
}
/* On hold — a known job parked for the team (amber, matching the On-hold status
   badge), NOT faded: it still needs doing, just not right now. */
.chat-bubble-task:has(.chat-request-card.is-on_hold) {
  border-left-color: #a16207 !important;
  background: color-mix(in srgb, #a16207 8%, var(--bg-elevated)) !important;
}
/* Cancelled — muted grey spine + faded card: the job's gone, don't act on it. */
.chat-bubble-task:has(.chat-request-card.is-cancelled) {
  border-left-color: var(--text-muted) !important;
  background: var(--bg-elevated) !important;
}
/* Plain task card, completed — same green done-state as the claimable cards, so a
   "do later" job that's been finished reads at a glance in the chat. */
.chat-task-card.is-done { opacity: .8; }
.chat-bubble-task:has(.chat-task-card.is-done) {
  border-left-color: #16a34a !important;
  background: color-mix(in srgb, #16a34a 6%, var(--bg-elevated)) !important;
}

/* Task-create modal grid */
.chat-task-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 10px; margin-bottom: 10px; }
.chat-task-f { display: flex; flex-direction: column; gap: 4px; }
.chat-task-f > span { font: 600 var(--text-xs)/1 var(--body); color: var(--text-muted); }
.chat-task-f .chat-name-input { margin-bottom: 0; }
.chat-task-desc { resize: vertical; }

/* Alarm trigger button (sidebar) */
.chat-alarm-btn { display: grid; place-items: center; width: 38px; height: 38px; flex-shrink: 0; background: color-mix(in srgb, #dc2626 12%, var(--bg-surface)); border: 1px solid color-mix(in srgb, #dc2626 45%, var(--border)); border-radius: 50%; color: #dc2626; cursor: pointer; transition: background .12s; }
.chat-alarm-btn:hover { background: #dc2626; color: #fff; }
.chat-alarm-btn svg { width: 18px; height: 18px; }

/* Alarm compose modal */
.chat-alarm-types { display: grid; grid-template-columns: repeat(3, 1fr); gap: 8px; margin-bottom: 12px; }
.chat-alarm-type { display: flex; flex-direction: column; align-items: center; gap: 4px; padding: 12px 6px; background: var(--bg-base); border: 1px solid var(--border); border-radius: var(--radius-md); font: 600 var(--text-xs)/1 var(--body); color: var(--text-secondary); cursor: pointer; }
.chat-alarm-type span { font-size: 22px; }
.chat-alarm-type:hover { border-color: #dc2626; }
.chat-alarm-type.is-active { border-color: #dc2626; background: color-mix(in srgb, #dc2626 10%, var(--bg-surface)); color: var(--text-primary); }
.chat-alarm-send { padding: 10px 20px; background: #dc2626; color: #fff; border: 0; border-radius: var(--radius-md); font: 800 var(--text-sm)/1 var(--body); cursor: pointer; }
.chat-alarm-send:hover { background: #b91c1c; }
.chat-alarm-send.is-armed { background: #b91c1c; box-shadow: 0 0 0 3px color-mix(in srgb, #dc2626 35%, transparent); animation: alarmPulseBtn 1s ease-in-out infinite; }
@keyframes alarmPulseBtn { 50% { box-shadow: 0 0 0 7px color-mix(in srgb, #dc2626 12%, transparent); } }

/* ── GLOBAL emergency alarm banner (app-wide) ── */
.alarm-banner { position: fixed; inset: 0; z-index: 100000; display: grid; place-items: center; padding: 24px;
  background: color-mix(in srgb, var(--alarm, #dc2626) 88%, #000); animation: alarmFlash 1.1s steps(1) infinite; }
@keyframes alarmFlash { 50% { background: color-mix(in srgb, var(--alarm, #dc2626) 64%, #000); } }
.alarm-card { width: min(440px, 92vw); text-align: center; color: #fff; }
.alarm-ico { font-size: 72px; line-height: 1; animation: alarmShake .6s ease-in-out infinite; }
@keyframes alarmShake { 25% { transform: rotate(-9deg); } 75% { transform: rotate(9deg); } }
.alarm-type { font: 900 40px/1 var(--display, var(--body)); letter-spacing: .04em; margin-top: 14px; text-transform: uppercase; }
.alarm-msg { font: 600 18px/1.4 var(--body); margin-top: 12px; }
.alarm-from { font: 500 13px/1.4 var(--body); opacity: .85; margin-top: 10px; }
.alarm-ack { margin-top: 26px; padding: 15px 28px; background: #fff; color: #b91c1c; border: 0; border-radius: var(--radius-pill); font: 800 16px/1 var(--body); cursor: pointer; box-shadow: 0 8px 24px rgba(0,0,0,.3); }
.alarm-ack:hover { transform: translateY(-1px); }

@media (max-width: 760px) {
  .chat-task-grid { grid-template-columns: 1fr; }
  .alarm-type { font-size: 32px; }
}

/* Tasks — desktop detail-pane header (ref + dept + edit/delete), parity with
   the mobile modal title bar so edit + delete are reachable on desktop too. */
.tasks-detail-pane-head { display: flex; align-items: center; gap: 8px; flex-wrap: nowrap; padding: 16px 20px 12px; margin-bottom: 8px; border-bottom: 1px solid var(--border); }
.tasks-detail-pane-head .tasks-modal-head-spacer { flex: 1 1 auto; }
/* Keep the row intact on narrow panes: ref + icons stay put, dept pill
   shrinks/ellipsises rather than wrapping the edit/delete icons to a 2nd line. */
.tasks-detail-pane-head .kb-instr-ref-pill,
.tasks-detail-pane-head .kb-reader-action { flex-shrink: 0; }
.tasks-detail-pane-head .tasks-detail-dept-pill { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

/* Tasks — KB link chooser buttons (folder / document / remove) */
.tasks-kb-choice { display: flex; align-items: center; gap: 12px; padding: 13px 14px; background: var(--bg-base); border: 1px solid var(--border); border-radius: var(--radius-md); cursor: pointer; text-align: left; transition: border-color .12s, background .12s; }
.tasks-kb-choice:hover { border-color: var(--accent); background: var(--bg-hover); }
.tasks-kb-choice.danger:hover { border-color: #dc2626; }
.tasks-kb-choice-ico { font-size: 22px; line-height: 1; flex-shrink: 0; }
.tasks-kb-choice-txt { display: flex; flex-direction: column; min-width: 0; }
.tasks-kb-choice-txt strong { font: 700 var(--text-sm) / 1.2 var(--body); color: var(--text-primary); }
.tasks-kb-choice-txt small { font: 500 var(--text-xs) / 1.3 var(--body); color: var(--text-muted); margin-top: 2px; }

/* ═══════════════════════════════════════════════════════════════════════
   ENERGY EFFICIENCY
   Reuses the Statistics stat-* components (toolbar, KPI tiles, cards,
   chart bars, league legend). Only the energy-specific bits live here.
   ═══════════════════════════════════════════════════════════════════════ */

/* KPI strip — one tile per utility, accent dot + unit suffix */
.energy-kpi { cursor: pointer; }
.energy-kpi-dot { display: inline-block; width: 9px; height: 9px; border-radius: 50%; margin-right: 7px; vertical-align: middle; }
.energy-kpi-unit { font: 600 var(--text-sm) / 1 var(--body); color: var(--text-muted); margin-left: 5px; }

/* Utility selector tabs (drive the trend + comparison) */
.energy-util-tabs {
  display: flex; gap: 8px; margin: 4px 0 12px;
  overflow-x: auto; -webkit-overflow-scrolling: touch; scrollbar-width: none;
}
.energy-util-tabs::-webkit-scrollbar { display: none; }
.energy-util-tab {
  display: inline-flex; align-items: center; gap: 7px; flex: 0 0 auto;
  padding: 8px 14px; border-radius: 999px;
  background: var(--bg-elevated); border: 1.5px solid var(--border);
  color: var(--text-secondary); font: 600 var(--text-sm) / 1 var(--body);
  cursor: pointer; font-family: inherit; transition: border-color .12s, color .12s, background .12s;
}
.energy-util-tab-icon { width: 15px; height: 15px; }
.energy-util-tab:hover { color: var(--text-primary); }
.energy-util-tab.is-active { background: color-mix(in srgb, var(--u) 14%, transparent); border-color: var(--u); color: var(--u); }
.energy-util-tab.is-empty { opacity: .5; }

/* Trend bars: single series, coloured per utility (height + bg set inline) */
.energy-trend-bars .stat-col-bars { justify-content: center; }
.energy-bar { border-radius: 6px 6px 0 0; min-height: 0; }

/* Site comparison — horizontal bars, highest first */
.energy-cmp { display: flex; flex-direction: column; gap: 8px; }
.energy-cmp-row {
  display: grid; grid-template-columns: minmax(0, 1.6fr) minmax(40px, 1fr) auto auto;
  align-items: center; gap: 8px; width: 100%;
  padding: 8px 10px; border-radius: var(--radius-md);
  background: var(--bg-elevated); border: 1px solid var(--border);
  cursor: pointer; text-align: left; font-family: inherit;
  transition: border-color .12s, background .12s;
}
.energy-cmp-row:hover { border-color: var(--accent); }
.energy-cmp-row.is-focus { border-color: var(--accent); background: color-mix(in srgb, var(--accent) 7%, transparent); }
.energy-cmp-name { font: 600 var(--text-sm) / 1.2 var(--body); color: var(--text-primary); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.energy-cmp-track { height: 10px; border-radius: 999px; background: var(--bg-surface); overflow: hidden; }
.energy-cmp-fill { display: block; height: 100%; border-radius: 999px; min-width: 4px; }
.energy-cmp-val { font: 700 var(--text-sm) / 1 var(--body); color: var(--text-primary); white-space: nowrap; font-variant-numeric: tabular-nums; }
.energy-cmp-unit { font-weight: 600; color: var(--text-muted); font-size: var(--text-xs); }
.energy-cmp-cost { font: 600 var(--text-xs) / 1 var(--body); color: var(--text-secondary); white-space: nowrap; min-width: 42px; text-align: right; }

/* ── Forms (reading entry / meters / records) ─────────────────────────── */
.energy-form { display: flex; flex-direction: column; gap: 14px; }
.energy-select {
  width: 100%; font-family: inherit; font-size: var(--text-base); color: var(--text-primary);
  padding: 10px 12px; background: var(--bg-elevated); border: 1px solid var(--border);
  border-radius: var(--radius-md); transition: border-color .12s, background .12s;
}
.energy-select:focus { outline: none; border-color: var(--accent); background: var(--bg-surface); }
.energy-value-row { display: flex; align-items: stretch; gap: 8px; }
.energy-value-row .energy-select { flex: 1; font: 700 var(--text-lg, 18px) / 1 var(--body); font-variant-numeric: tabular-nums; }
.energy-value-unit {
  display: inline-flex; align-items: center; padding: 0 14px; white-space: nowrap;
  background: var(--bg-elevated); border: 1px solid var(--border); border-radius: var(--radius-md);
  font: 600 var(--text-base) / 1 var(--body); color: var(--text-secondary);
}
.energy-opt { font-weight: 500; color: var(--text-muted); font-size: var(--text-xs); }

/* Meter list rows */
.energy-meter-group { margin-bottom: 14px; }
.energy-meter-group-head {
  display: flex; align-items: center; gap: 8px;
  font: 700 var(--text-xs) / 1 var(--body); text-transform: uppercase; letter-spacing: .05em;
  color: var(--text-muted); margin-bottom: 8px;
}
.energy-meter-row {
  display: flex; align-items: center; gap: 10px;
  padding: 10px 12px; border-radius: var(--radius-md);
  background: var(--bg-elevated); border: 1px solid var(--border); margin-bottom: 6px;
}
.energy-meter-dot { width: 12px; height: 12px; border-radius: 50%; flex-shrink: 0; }
.energy-meter-info { display: flex; flex-direction: column; gap: 2px; flex: 1; min-width: 0; }
.energy-meter-name { font: 600 var(--text-sm) / 1.2 var(--body); color: var(--text-primary); }
.energy-meter-meta { font: 500 var(--text-xs) / 1.3 var(--body); color: var(--text-muted); }
.energy-icon-btn {
  flex-shrink: 0; width: 32px; height: 32px; display: inline-flex; align-items: center; justify-content: center;
  background: transparent; border: 1px solid transparent; border-radius: var(--radius-sm);
  color: var(--text-secondary); cursor: pointer; transition: border-color .12s, color .12s, background .12s;
}
.energy-icon-btn svg { width: 16px; height: 16px; }
.energy-icon-btn:hover { color: var(--text-primary); background: var(--bg-elevated); }
.energy-icon-btn:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.energy-icon-btn.is-danger:hover { color: #dc2626; border-color: color-mix(in srgb, #dc2626 50%, transparent); background: color-mix(in srgb, #dc2626 8%, transparent); }

/* Records table */
.energy-rec-row {
  display: grid; grid-template-columns: 1.3fr 1fr 1.1fr .9fr 32px;
  align-items: center; gap: 8px; padding: 7px 4px; border-bottom: 1px solid var(--border);
  font: 500 var(--text-sm) / 1.2 var(--body); color: var(--text-primary); font-variant-numeric: tabular-nums;
}
.energy-rec-head { font: 700 var(--text-xs) / 1 var(--body); text-transform: uppercase; letter-spacing: .04em; color: var(--text-muted); border-bottom: 1.5px solid var(--border); }
.energy-rec-cons { color: var(--text-secondary); }
.energy-rec-cost { color: var(--text-secondary); }
.energy-rec-row .energy-icon-btn { width: 28px; height: 28px; border-color: transparent; }

/* ═══════════════════════════════════════════════════════════════════════
   CHAT — floating unread nudge (modules/chat/nudge.js)
   App-wide marker so a message is never missed once the toast is gone.
   Bottom-LEFT (create-FABs own bottom-right). Rests as a quiet avatar dot;
   expands to a full pill with one pulse when a new message lands.
   ═══════════════════════════════════════════════════════════════════════ */
.chat-nudge {
  position: fixed;
  left: 22px;
  /* Equal visual gap to the left & bottom edges so it sits naturally in the
     corner (not jammed against it). Lands at 22px on notch phones; grows only
     if a device reports an unusually large bottom inset. The button is on the
     LEFT, the home indicator is centred, so they never overlap. */
  bottom: max(22px, calc(env(safe-area-inset-bottom) - 12px));
  z-index: 45;                                  /* above content, below modals (9000+) */
  display: flex;
  align-items: center;
  max-width: min(80vw, 320px);
  padding: 7px;                                 /* dot state: tight ring around the avatar */
  border-radius: 999px;
  background: var(--bg-elevated);
  border: 1px solid var(--border);
  color: var(--text-primary);
  box-shadow: 0 8px 24px rgba(0, 0, 0, 0.20);
  cursor: pointer;
  font-family: var(--body);
  text-align: left;
  -webkit-tap-highlight-color: transparent;
  opacity: 0;
  transform: translateY(10px) scale(0.92);
  pointer-events: none;
  transition: opacity .22s ease, transform .22s cubic-bezier(.2, .8, .3, 1), padding .22s ease;
}
.chat-nudge.is-show { opacity: 1; transform: translateY(0) scale(1); pointer-events: auto; }
.chat-nudge.is-show:hover { box-shadow: 0 12px 30px rgba(0, 0, 0, 0.26); }
.chat-nudge.is-show:active { transform: translateY(0) scale(0.96); }

/* Watched-jobs sub-marker (Radek 2026-07-04) — a small amber wrench+count badge on
   the chat pill's top-right corner. A SEPARATE, calm signal from messages: it means
   "activity on a call-out you watch", pulses once on new activity, tap → the watched
   fan (one row per updated call-out → the job). Amber (--warning) so it never reads
   as a message; dark ink stays legible on both the bright (dark-mode) and deeper
   (light-mode) amber. Absolute → rides the pill's corner in every render state. */
.chat-nudge-watch {
  position: absolute;
  top: -7px; right: -7px;
  display: inline-flex; align-items: center; gap: 2px;
  padding: 2px 6px 2px 5px;
  border-radius: 999px;
  background: var(--warning);
  color: #231a05;
  border: 2px solid var(--bg-elevated);
  font: 800 11px/1 var(--body);
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.28);
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
}
.chat-nudge-watch svg { width: 11px; height: 11px; }
.chat-nudge-watch:active { transform: scale(0.94); }
.chat-nudge-watch.is-pulse { animation: chatNudgeWatchPulse 0.6s ease 2; }
@keyframes chatNudgeWatchPulse {
  0%, 100% { transform: scale(1); box-shadow: 0 2px 8px rgba(0, 0, 0, 0.28); }
  50%      { transform: scale(1.2); box-shadow: 0 0 0 5px var(--warning-soft), 0 2px 8px rgba(0, 0, 0, 0.28); }
}
/* the wrench glyph inside a watched-jobs fan row */
.nudge-fan-ic-watch { color: var(--warning); }

/* Desktop: the drawer is a permanent part of the layout (not a slide-in), so a
   viewport-anchored left:22px sits ON TOP of the side menu. Clear the drawer so
   the FAB floats over the main content — like the mobile view, where the drawer
   is hidden and 22px is already on the main screen. Snaps back to the edge when
   the rail is collapsed, and slides in step with the rail (matching transition). */
@media (min-width: 1024px) {
  .chat-nudge { left: calc(var(--drawer-width) + 22px); transition: opacity .22s ease, transform .22s cubic-bezier(.2, .8, .3, 1), padding .22s ease, left 0.28s cubic-bezier(0.32,0.72,0,1); }
  body.rail-collapsed .chat-nudge { left: 22px; }
}

.chat-nudge-av {
  position: relative;
  width: 40px; height: 40px; flex: none;
  border-radius: 50%;
  display: grid; place-items: center;
  background: var(--av, var(--accent));
  color: #fff;
  font: 700 14px/1 var(--body);
  letter-spacing: .02em;
}

/* Notification badge anchored to the avatar — visible in BOTH the dot and the
   expanded states, so the unread count is always legible. */
.chat-nudge-count {
  position: absolute; top: -3px; right: -3px;
  min-width: 18px; height: 18px; padding: 0 5px;
  box-sizing: border-box;
  border-radius: 999px;
  background: var(--accent); color: #04181c;
  font: 800 11px/18px var(--body); text-align: center;
  border: 2px solid var(--bg-elevated);
}
.chat-nudge-count.is-dot { min-width: 13px; width: 13px; height: 13px; padding: 0; }

/* Body (who + preview) — collapsed to zero width in the resting dot state, slides
   open when expanded. */
.chat-nudge-body {
  display: flex; flex-direction: column; gap: 2px;
  min-width: 0; max-width: 0;
  margin-left: 0; opacity: 0; overflow: hidden;
  transition: max-width .22s ease, margin-left .22s ease, opacity .18s ease;
}
.chat-nudge.is-expanded { padding-right: 16px; }
.chat-nudge.is-expanded .chat-nudge-body { max-width: 230px; margin-left: 10px; opacity: 1; }
.chat-nudge-title { font: 700 13px/1.2 var(--body); color: var(--text-primary); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.chat-nudge-sub { font: 500 12px/1.25 var(--body); color: var(--text-secondary); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }

/* One-shot arrival pulse — a soft expanding ring in the avatar's own colour. */
@keyframes chatNudgePulse {
  0%   { box-shadow: 0 0 0 0 color-mix(in srgb, var(--av, var(--accent)) 55%, transparent); }
  70%  { box-shadow: 0 0 0 13px color-mix(in srgb, var(--av, var(--accent)) 0%, transparent); }
  100% { box-shadow: 0 0 0 0 color-mix(in srgb, var(--av, var(--accent)) 0%, transparent); }
}
.chat-nudge.is-arrive .chat-nudge-av { animation: chatNudgePulse .9s ease-out; }

@media (prefers-reduced-motion: reduce) {
  .chat-nudge { transition: opacity .2s ease; transform: none; }
  .chat-nudge.is-show { transform: none; }
  .chat-nudge.is-show:active { transform: scale(0.97); }
  .chat-nudge.is-arrive .chat-nudge-av { animation: none; }
}

/* Idle "go to chat" state — calm / recessed (shown when there's NO unread). Quieter
   than the bold accent create-FABs so the two floating buttons don't compete; morphs
   into the loud avatar+count nudge above the moment a message lands. */
.chat-nudge.is-idle {
  padding: 0;
  width: 52px; height: 52px;
  justify-content: center;
  /* A touch of accent so it reads as a clear, tappable button instead of
     blending into the dark background — accent glyph + tinted fill + accent
     border. Still a tint (not a solid accent blob) so it doesn't masquerade as
     a primary "create" FAB. */
  color: var(--accent);
  background: color-mix(in srgb, var(--accent) 14%, var(--bg-elevated));
  border-color: color-mix(in srgb, var(--accent) 48%, var(--border));
  box-shadow: 0 6px 18px rgba(0, 0, 0, 0.22);
}
.chat-nudge.is-idle:hover { color: var(--accent); background: color-mix(in srgb, var(--accent) 22%, var(--bg-elevated)); box-shadow: 0 9px 24px rgba(0, 0, 0, 0.28); }
.chat-nudge-fab { display: grid; place-items: center; }
.chat-nudge-fab svg { width: 23px; height: 23px; }

/* ═══════════════════════════════════════════════════════════════════════
   GUEST REQUESTS — floating "waiting" banner (modules/chat/guest-nudge.js)
   Bottom-RIGHT, mirroring the maintenance duty pill so an OPEN guest request
   can never be missed. Amber bell; pulses on arrival; shown to everyone in a
   "Guest requests" thread. Hidden (opacity 0) until there's an open request.
   ═══════════════════════════════════════════════════════════════════════ */
.guest-nudge {
  position: fixed; right: 22px;
  bottom: max(22px, calc(env(safe-area-inset-bottom) - 12px));
  z-index: 46;                                  /* above content + the chat nudge; below modals (9000+) */
  display: flex; align-items: center; gap: 10px;
  max-width: min(76vw, 330px);
  padding: 8px 14px 8px 10px; border-radius: 999px;
  background: var(--bg-elevated); border: 1px solid var(--gn-accent, #d97706);
  color: var(--text-primary); box-shadow: 0 10px 28px rgba(0, 0, 0, 0.28);
  cursor: pointer; pointer-events: none; text-align: left;
  font-family: var(--body); -webkit-tap-highlight-color: transparent;
  opacity: 0; transform: translateY(10px) scale(0.92);
  transition: opacity .22s ease, transform .22s cubic-bezier(.2, .8, .3, 1), bottom .22s ease;
}
.guest-nudge.is-show { opacity: 1; transform: translateY(0) scale(1); pointer-events: auto; }
.guest-nudge.is-show:active { transform: translateY(0) scale(0.97); }
/* When the maintenance duty pill is also minimised in the corner, stack above it. */
.guest-nudge.above-duty { bottom: calc(max(22px, env(safe-area-inset-bottom) - 12px) + 64px); }
/* Desktop: clear the side rail so the pill floats over content, not the menu. */
@media (min-width: 1024px) {
  .guest-nudge { transition: opacity .22s ease, transform .22s cubic-bezier(.2, .8, .3, 1), bottom .22s ease, right 0s; }
}
.guest-nudge-ico {
  width: 34px; height: 34px; flex: none; border-radius: 50%;
  display: grid; place-items: center; color: var(--gn-accent, #d97706);
  background: color-mix(in srgb, var(--gn-accent, #d97706) 16%, transparent);
}
.guest-nudge-ico svg { width: 19px; height: 19px; }
.guest-nudge-body { display: flex; flex-direction: column; gap: 1px; min-width: 0; }
.guest-nudge-title { font: 700 13px/1.2 var(--body); color: var(--text-primary); white-space: nowrap; }
.guest-nudge-sub { font: 500 12px/1.25 var(--body); color: var(--text-secondary); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; max-width: 230px; }
.guest-nudge-count {
  flex: none; min-width: 20px; height: 20px; padding: 0 6px; box-sizing: border-box;
  border-radius: 999px; background: var(--gn-accent, #d97706); color: #fff;
  font: 800 11px/20px var(--body); text-align: center;
}
.guest-nudge.is-arrive .guest-nudge-ico { animation: guestNudgePulse 1s ease-out; }
@keyframes guestNudgePulse {
  0%   { box-shadow: 0 0 0 0 color-mix(in srgb, var(--gn-accent, #d97706) 55%, transparent); }
  70%  { box-shadow: 0 0 0 12px color-mix(in srgb, var(--gn-accent, #d97706) 0%, transparent); }
  100% { box-shadow: 0 0 0 0 color-mix(in srgb, var(--gn-accent, #d97706) 0%, transparent); }
}
@media (prefers-reduced-motion: reduce) {
  .guest-nudge { transition: opacity .2s ease; transform: none; }
  .guest-nudge.is-show { transform: none; }
  .guest-nudge.is-show:active { transform: scale(0.97); }
  .guest-nudge.is-arrive .guest-nudge-ico { animation: none; }
}

/* ═══════════════════════════════════════════════════════════════════════
   NUDGE FAN-OUT — shared stack for both corner pills (modules/chat/nudge-fan.js)
   When the FAB (left) or the requests banner (right) collapses several items
   into one count, tapping UNFOLDS them as a small stacked list so you choose
   which to open first. column-reverse → row 0 sits nearest the pill (easiest
   reach); each row eases up with a staggered delay for a calm "fan" motion.
   ═══════════════════════════════════════════════════════════════════════ */
.nudge-fan {
  position: fixed;
  z-index: 47;                                  /* above both pills (45/46); below modals (9000+) */
  bottom: calc(max(22px, env(safe-area-inset-bottom) - 12px) + 64px);
  display: flex; flex-direction: column-reverse; gap: 8px;
  width: min(82vw, 320px);
  pointer-events: none;                         /* inert until open */
}
.nudge-fan-left  { left: 22px; }
.nudge-fan-right { right: 22px; }
@media (min-width: 1024px) {
  .nudge-fan-left { left: calc(var(--drawer-width) + 22px); }
}
.nudge-fan.is-open { pointer-events: auto; }
.nudge-fan-item {
  display: flex; align-items: center; gap: 10px; width: 100%;
  padding: 10px 12px; border-radius: 14px;
  background: var(--bg-elevated); border: 1px solid var(--border);
  box-shadow: 0 8px 22px rgba(0, 0, 0, 0.24);
  color: var(--text-primary); font-family: var(--body);
  text-align: left; cursor: pointer; -webkit-tap-highlight-color: transparent;
  opacity: 0; transform: translateY(12px) scale(0.96);
  transition: opacity .2s ease, transform .24s cubic-bezier(.2, .8, .3, 1), box-shadow .18s ease;
}
.nudge-fan.is-open .nudge-fan-item {
  opacity: 1; transform: translateY(0) scale(1);
  transition-delay: calc(var(--i, 0) * 42ms);   /* staggered "fan" entrance, bottom-up */
}
.nudge-fan-item:hover  { box-shadow: 0 12px 28px rgba(0, 0, 0, 0.30); }
.nudge-fan-item:active { transform: scale(0.98); }
.nudge-fan-av {
  width: 34px; height: 34px; flex: none; border-radius: 50%;
  display: grid; place-items: center;
  background: var(--fav, var(--accent)); color: #fff;
  font: 700 13px/1 var(--body); letter-spacing: .02em;
}
.nudge-fan-ic {
  width: 34px; height: 34px; flex: none; border-radius: 50%;
  display: grid; place-items: center; color: var(--fav, #d97706);
  background: color-mix(in srgb, var(--fav, #d97706) 16%, transparent);
}
.nudge-fan-ic svg { width: 18px; height: 18px; }
.nudge-fan-main { display: flex; flex-direction: column; gap: 1px; min-width: 0; flex: 1; }
.nudge-fan-name { font: 700 13px/1.2 var(--body); color: var(--text-primary); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.nudge-fan-prev { font: 500 12px/1.25 var(--body); color: var(--text-secondary); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.nudge-fan-badge {
  flex: none; min-width: 20px; height: 20px; padding: 0 6px; box-sizing: border-box;
  border-radius: 999px; background: var(--fav, var(--accent)); color: #fff;
  font: 800 11px/20px var(--body); text-align: center;
}
.nudge-fan-more { justify-content: center; color: var(--text-secondary); font: 600 12px/1.1 var(--body); }
@media (prefers-reduced-motion: reduce) {
  .nudge-fan-item { transition: opacity .15s ease; transform: none; }
  .nudge-fan.is-open .nudge-fan-item { transform: none; transition-delay: 0s; }
}

/* ════════════════════════════════════════════════════════════════════
   AUDITS — Phase 2 (redesign): single-zone checklist-style builder.
   Reuses the checklist `.cl-section-*` / `.cl-add-item-*` / `.cl-item-v2`
   styling wholesale (no forked CSS); only these audit-specific glue rules
   are needed: the 6-type picker grid, a neutral type pill, and body spacing
   for the audit fields living inside the .cl-item-v2 shell.
   ════════════════════════════════════════════════════════════════════ */
/* Audits has 6 question types (checklist has 5) → 3-up grid (2 even rows). The
   unified picker adds Structure (Section/Location) + Pack (Load/Save) tiles → 4-up
   so it stays compact (≤3 rows). Scoped by .aq-picker so checklists are untouched. */
.cl-add-item-buttons.aq-picker { grid-template-columns: repeat(3, minmax(0, 1fr)); }
.cl-add-item-buttons.aq-picker.has-structure { grid-template-columns: repeat(4, minmax(0, 1fr)); }

/* ── Three tile FAMILIES, set apart ONLY by icon colour (same shape/border/hover):
   question types (accent, the base) · structure Section/Location (violet) · packs
   Load/Save (amber). Tinting only the icon keeps the row calm while grouping them. */
.cl-add-item-btn.is-structure .cl-add-item-btn-icon svg { color: var(--audit-structure, #8b5cf6); }
.cl-add-item-btn.is-pack .cl-add-item-btn-icon svg { color: var(--audit-pack, #d97706); }
.cl-add-item-btn.is-structure:hover { border-color: var(--audit-structure, #8b5cf6); background: color-mix(in srgb, var(--audit-structure, #8b5cf6) 8%, var(--bg-elevated)); }
.cl-add-item-btn.is-pack:hover { border-color: var(--audit-pack, #d97706); background: color-mix(in srgb, var(--audit-pack, #d97706) 8%, var(--bg-elevated)); }

/* ── Nested section cards (audit-only, scoped by .ah-asec). The shared
   .cl-section-card already has a 3px left rail; here we (a) inset each nested level
   via the depth var, and (b) make a plain Section's rail NEUTRAL while a Location-
   bound section keeps the ACCENT rail + a tinted header, so "this is a real place"
   reads at a glance vs a plain organisational container. */
.cl-section-card.ah-asec { margin-left: calc(var(--asec-depth, 0) * 14px); border-left-color: var(--border); }
.cl-section-card.ah-asec[data-depth="0"] { margin-left: 0; }
.cl-section-card.ah-asec.is-location { border-left-color: var(--accent); }
.cl-section-card.ah-asec.is-location > .cl-section-head { background: color-mix(in srgb, var(--accent) 12%, var(--bg-surface)); }

/* ── Active section — the one the author is working in. A full accent border + a
   1px accent ring (no layout shift) + a stronger header tint so it's unmistakable
   which section new checks land in. Placed after .is-location so it wins the header
   tint. Calm siblings keep the neutral treatment (no dimming — the contrast is enough). */
.cl-section-card.ah-asec.is-active { border-color: var(--accent); box-shadow: 0 0 0 1px var(--accent); }
.cl-section-card.ah-asec.is-active > .cl-section-head { background: color-mix(in srgb, var(--accent) 16%, var(--bg-surface)); }
/* Neutral type pill for audit question types that don't map to a checklist colour
   (Note / Photo). The base .cl-item-type-pill is borderless, so give it a chip look. */
.cl-item-type-pill.is-audit {
  background: var(--bg-elevated);
  color: var(--text-secondary);
  border-color: var(--border);
}
/* The audit field body sitting inside the reused .cl-item-v2 card shell. */
.audit-q-card-v2 .audit-q-body { width: 100%; }
.audit-q-card-v2 .audit-q-options-row { margin-top: 2px; }

/* ── Section interiors (2026-07-05 polarity flip): the section CARD is white
   now (see the "BUILDERS → runner polarity" block at file end), so its body
   simply inherits the white well and the GREY question cards of the 2026-07-01
   era are gone — cards are white with grey controls, popping against the grey
   modal canvas at the top level and separated by firm borders inside a well.
   The old ".ah-asec white body" + add-row/tile compensation rules that existed
   for grey-cards-on-white are superseded by the shared .cl-form-modal rules
   (css/14 legibility block + the end-of-file block here). */

/* Mobile width pass (2026-06-25): tighten horizontal padding on mobile so content
   uses the screen width. Appended at end-of-file so it beats the base rules AND any
   wider-breakpoint (600/640/767px) blocks at equal specificity (later source wins).
   Desktop (>=600px) untouched. */
@media (max-width: 599px) {
  .inv-list-table-head { padding-left: 12px; padding-right: 12px; }
  .inv-list-table-body { padding: 14px 12px; }
  .audit-empty { padding: 48px 16px; }
  .audit-modal-body { padding: 16px 12px; }
  /* Detail body is padding-0 (scroll region); its questions wrapper carries the
     mobile inset instead so the summary head can stay full-bleed. */
  .audit-detail-questions { padding: 16px 12px; }
  .audit-modal-foot { padding: 14px 12px 18px; }
  .order-empty { padding: 48px 16px; }
  .order-detail-body { padding: 12px 12px; }
  .order-modal-body { padding: 16px 12px; }
  /* .main already carries the mobile inset — the extra --space-4 here made Orders
     cards visibly NARROWER than Tasks/Call-outs cards. Longhand: the base rule's
     bottom --space-6 must survive (2026-07-17). Tasks (.tasks-page) is the reference. */
  .order-page { padding-left: 0; padding-right: 0; }
  .order-detail-head { padding-left: 12px; padding-right: 12px; }
  .order-modal-foot { padding-left: 12px; padding-right: 12px; }
  .compliance-empty { padding: 16px 12px; }
  .cal-empty { padding: 24px 12px; }
  .project-card { padding: 14px 12px; }
  .project-card-main { padding: 13px 12px; }
  .project-meta-item { padding: 12px 12px; }
  .ptree-col { padding: 12px 10px 14px; }
  .safety-card-main { padding: 13px 12px; }
  .budget-kpi { padding: 12px 12px; }
  .budget-line { padding: 13px 12px; }
  .inv-colour-popover { padding: 12px; }
  .project-member { padding: 10px 10px; }
  .project-member-add { padding: 10px 10px; }
  .project-budget-row { padding: 10px 10px; }
  /* Nested audit sections: gentler per-level indent + a hard cap so deep nesting
     never strands a card in a narrow column on a phone (content keeps the width). */
  .cl-section-card.ah-asec { margin-left: min(calc(var(--asec-depth, 0) * 8px), 24px); }
  .cl-section-card.ah-asec[data-depth="0"] { margin-left: 0; }
  /* Chat (2026-07-02): thread canvas + the compose modals (New group, Group
     settings, guest request, alarm) adopt the standard 12px mobile side inset —
     they predate the mobile-width standard and stranded ~20px per side. */
  .chat-messages { padding-left: 12px; padding-right: 12px; }
  .chat-modal { padding-left: 12px; padding-right: 12px; }
}

/* (The ≤430px five-tile squeeze block retired 2026-07-05 — three tiles fit a
   narrow phone at full size.) */

/* ════════════════════════════════════════════════════════════════════
   AUDIT RUNNER + FOLLOW-UP → match the TEMPLATE builder's polarity
   → ONE polarity across the audit system (Radek 2026-07-03, completed
   2026-07-05): WHITE cards + GREY (--bg-elevated) controls on a GREY body —
   the checklist-runner look. The runner + follow-up/detail flipped
   2026-07-03; the TEMPLATE BUILDERS (audit + checklist, shared
   .cl-form-modal shell) adopted the SAME polarity 2026-07-05 — see the
   "BUILDERS → runner polarity" block at the end of this file plus the
   re-based css/14 legibility block. Building a template, running it and
   reading its report now share one visual language.

   Hard constraint handled here: SHARED-CLASS LEAK. The runner reuses the
   CHECKLIST runner classes (.cl-run-section / .cl-run-item /
   .cl-run-yesno-btn …). EVERY override below is nested under an audit-only
   ancestor (.audit-runner-body / .audit-detail-body / .audit-q-run-card) so
   the live Checklist runner inherits only its own shared base.
   Dark-mode safe: there --bg-elevated (body/controls) is LIGHTER than
   --bg-surface (cards), so the same "card ≠ its surroundings" contrast
   holds, just inverted in lightness.
   ════════════════════════════════════════════════════════════════════ */

/* ── RUNNER ─────────────────────────────────────────────────────────── */
/* Surface + section container → white; question card → grey + firm border. */
/* Grey scroll body so the WHITE question cards pop — identical to the checklist
   runner (Radek 2026-07-03). The white top band (header + title + status bar) then
   reads as chrome, and the grey body below is clearly the content area. */
.audit-runner-body { background: var(--bg-elevated); }
.audit-runner-body .cl-run-section { background: var(--bg-surface); }
/* Checklist polarity (Radek 2026-07-03): WHITE question card + GREY answer
   controls — matching the Checklist runner, which shares these .cl-run-* classes.
   The answer buttons keep their grey (--bg-elevated) base (no white bump) so they
   read as grey pills on the white card. Reverts the 2026-07-01 grey-card flip.
   Photo tiles + the unset dept chip stay light so they read inside the grey
   fail-extras panel. */
.audit-q-run-card { background: var(--bg-surface); border-color: var(--border-input); }
.audit-runner-body .audit-photo-add-btn,
.audit-runner-body .audit-photo-thumb,
.audit-runner-body .audit-photo-thumb-loading,
.audit-runner-body .audit-dept-chip.is-unset {
  background: var(--bg-surface);
}
/* Fail-panel controls (severity / due / location / priority / asset / reading
   input) sit on the WHITE card via the transparent .audit-fail-extras, so they
   keep their grey (--bg-elevated) base — grey pills on a white card, exactly like
   the answer buttons and the checklist runner. (The old white bump only existed
   because the 2026-07-01 card was grey.) Multi-choice selected tints and the
   "what was wrong" note keep their shared base styling (which tints over grey). */
/* The section "well" was flipped to the surface colour (same as the body it sits
   on) so it read as no-card; a firmer divider makes it group its question cards. */
.audit-runner-body .cl-run-section { border-color: var(--border-input); }

/* ── FOLLOW-UP / DETAIL ─────────────────────────────────────────────── */
/* Grey scroll body so the WHITE report cards pop — the runner got this on
   2026-07-03; the report caught up 2026-07-05, completing the ONE polarity
   (white chrome → grey body → white cards) across builder/runner/report. */
/* padding:0 here (base rule, wins over the mobile .audit-modal-body 16px/12px via
   source order) — the scroll body stays edge-to-edge; .audit-detail-questions owns
   the inset (see its ≤599px override in the mobile block above). */
.audit-detail-body { background: var(--bg-elevated); padding: 0; }
/* Checklist polarity, same as the runner (Radek 2026-07-03): WHITE report card,
   colour only on the 3px left spine + status/sev badge. */
.audit-detail-q-card { background: var(--bg-surface); border-color: var(--border-input); }
.audit-detail-q-card.is-failed {
  border-color: var(--border-input);
  border-left: 3px solid var(--rag-fail-fill);
}
.audit-detail-q-card.is-task-resolved {
  border-color: var(--border-input);
  border-left: 3px solid var(--rag-pass-fill);
}
/* Detail controls (status / severity badge, value, note, linked-task chip) keep
   their grey (--bg-elevated) base — grey pills on the white card, matching the
   runner. (The old white bump only existed because the 2026-07-01 card was grey.) */

/* ── BUILDERS → runner polarity (Radek 2026-07-05) ──────────────────────
   The template builders (audit + checklist, shared .cl-form-modal shell)
   join the runner/report polarity: GREY modal canvas, WHITE panels /
   section wells / check cards, GREY controls (the css/14 legibility block
   was re-based to --bg-elevated the same day). White modal chrome (head +
   foot) stays — only the scrolling body greys, exactly like the runner's
   white top band over its grey body. Rails and tinted section heads are
   untouched here (backgrounds only; the head tints re-based over
   --bg-surface live in the .ah-asec block above). Dark-mode safe: the
   tokens invert lightness, matching the runner. */
.cl-form-modal .callout-modal-body { background: var(--bg-elevated); }
.cl-form-modal .callout-form-section { background: var(--bg-surface); }
.cl-form-modal .cl-section-card { background-color: var(--bg-surface); }
.cl-form-modal .cl-section-head { background: color-mix(in srgb, var(--accent) 7%, var(--bg-surface)); }
.cl-form-modal .cl-item { background-color: var(--bg-surface); }
/* Guarded so the click-to-select accent border + :focus-within accent from
   css/14 keep winning — this only firms the RESTING edge. */
.cl-form-modal .cl-item:not(.is-selected):not(:focus-within) { border-color: var(--border-input); }
/* The dashed add-zone reads as a white "add here" strip on the grey canvas
   and blends seamlessly inside the white section wells; its tiles are grey
   pills (css/14). */
.cl-form-modal .cl-add-item-row { background: var(--bg-surface); }

/* ── Statistics · Adoption panel (self-hosted usage analytics) ─────────────
   AGGREGATE ONLY — DAU/MAU tiles + module popularity + actions/day + the
   read-heavy actions that drive the Firestore bill. Reuses .stat-card /
   .stat-kpis / .stat-chart-* wholesale; adds only a single-series trend bar
   and a compact ranked "usage bar" list. Both themes via tokens. */
.stat-bar-usage { background: var(--accent); }
.stat-uselist { display: flex; flex-direction: column; gap: 8px; }
.stat-use-row { display: grid; grid-template-columns: minmax(92px, 1.1fr) 2fr auto; align-items: center; gap: 10px; }
.stat-use-name { font: 600 var(--text-sm) / 1.2 var(--body); color: var(--text-primary); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.stat-use-name.is-mono { font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-weight: 500; font-size: var(--text-xs); color: var(--text-secondary); }
.stat-use-track { height: 8px; border-radius: 999px; background: var(--bg-elevated); border: 1px solid var(--border); overflow: hidden; }
.stat-use-fill { display: block; height: 100%; background: var(--accent); border-radius: 999px; }
.stat-use-count { font: 700 var(--text-sm) / 1 var(--body); color: var(--text-primary); font-variant-numeric: tabular-nums; text-align: right; min-width: 46px; }
.stat-use-count small { font-weight: 500; color: var(--text-muted); }
.stat-checks-sites { margin-top: 16px; padding-top: 14px; border-top: 1px solid var(--border); }
.stat-checks-sites .stat-card-head { margin-bottom: 10px; }
button.stat-use-row.is-nav { width: 100%; min-height: 44px; padding: 8px 6px; text-align: left; border-radius: var(--radius-sm); transition: background .12s; }
button.stat-use-row.is-nav:hover { background: var(--bg-hover); }
button.stat-use-row.is-nav:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
@media (max-width: 599px) {
  .stat-use-row { grid-template-columns: minmax(72px, 1fr) 1.4fr auto; gap: 8px; }
}

/* ═══════════════════════════════════════════════════════════════════════
   MONTHLY OPERATIONS REVIEW — the group-report composer (modules/statistics/
   report.js). The full-screen shell reuses the Tasks modal spine
   (.tasks-modal.is-fullscreen); everything below is the report-specific
   editable body. Both themes via tokens (never --bg-card). The PDF itself is
   a separate self-contained print window (styles inlined in report.js), so
   these rules only dress the on-screen composer.
   ═══════════════════════════════════════════════════════════════════════ */
.statrep-head { gap: 12px; }
.statrep-head-title { font: 700 var(--text-lg) / 1.1 var(--display); color: var(--text-primary); }
.statrep-head-tools { margin-left: auto; display: flex; align-items: center; gap: 12px; }
.statrep-toggle { display: inline-flex; align-items: center; gap: 6px; font: 500 var(--text-xs) / 1 var(--body); color: var(--text-secondary); cursor: pointer; user-select: none; }
.statrep-toggle input { width: 15px; height: 15px; accent-color: var(--accent); }

/* Centre the report to a readable measure; full-bleed on phones (§4). */
.statrep-body { display: block; }
.statrep-body > .statrep-cover,
.statrep-body > .statrep-section,
.statrep-body > .statrep-signoff { max-width: 860px; margin-left: auto; margin-right: auto; }

/* 0 · cover masthead */
.statrep-cover { margin-bottom: 22px; }
.statrep-cover-band { display: flex; align-items: center; gap: 16px; background: linear-gradient(135deg, var(--accent), var(--accent-soft, var(--accent))); color: #fff; border-radius: var(--radius-lg, 16px); padding: 20px 22px; }
.statrep-logo { width: 46px; height: 46px; border-radius: 10px; background: rgba(255,255,255,0.15); object-fit: cover; flex-shrink: 0; }
.statrep-eyebrow { font: 600 var(--text-2xs, 11px) / 1 var(--body); letter-spacing: 0.14em; text-transform: uppercase; opacity: 0.92; }
.statrep-h1 { font: 800 clamp(28px, 5vw, 40px) / 1.05 var(--display); letter-spacing: -0.01em; margin: 3px 0 8px; }
.statrep-periodpill { display: inline-block; background: rgba(255,255,255,0.2); border-radius: 999px; padding: 4px 14px; font: 600 var(--text-sm) / 1 var(--body); }
.statrep-prepline { margin: 14px 2px 10px; font: 500 var(--text-sm) / 1.5 var(--body); color: var(--text-secondary); }
.statrep-summary { font: 500 var(--text-md, 16px) / 1.55 var(--body); color: var(--text-primary); border-left: 3px solid var(--accent); padding: 4px 0 4px 14px; }

/* contenteditable affordances — dashed underline on screen, clean in print */
.statrep-inline { border-bottom: 1px dashed var(--border-strong, var(--border)); padding: 0 3px; min-width: 42px; display: inline-block; color: var(--text-primary); outline: none; }
.statrep-inline:focus { border-bottom-color: var(--accent); }
.statrep-inline:empty::before,
.statrep-summary:empty::before,
.statrep-comment::before,
.statrep-score-note:empty::before { content: attr(data-placeholder); color: var(--text-muted); pointer-events: none; }
.statrep-summary:focus, .statrep-insight:focus, .statrep-comment:focus, .statrep-score-note:focus { outline: 2px solid var(--accent); outline-offset: 2px; border-radius: var(--radius-sm); }

/* sections */
.statrep-section { padding: 18px 0 4px; border-top: 1px solid var(--border); margin-top: 14px; }
.statrep-sec-head { display: flex; align-items: baseline; gap: 10px; border-bottom: 2px solid var(--accent); padding-bottom: 6px; margin-bottom: 12px; }
.statrep-sec-n { font: 700 var(--text-md, 16px) / 1 var(--display); color: var(--accent); }
.statrep-sec-title { font: 700 clamp(20px, 3vw, 26px) / 1.1 var(--display); color: var(--text-primary); }
.statrep-sec-sub { margin-left: auto; font: 500 var(--text-xs) / 1 var(--body); text-transform: uppercase; letter-spacing: 0.06em; color: var(--text-muted); }

/* the section "voice" — computed insight, editable */
.statrep-insight { font: 500 var(--text-sm) / 1.5 var(--body); color: var(--accent); margin: 0 0 12px; padding: 2px 4px; }

/* headline tiles reuse .callout-kpi-card; grid + sub/delta are ours */
.statrep-kpis { display: grid; grid-template-columns: repeat(6, 1fr); gap: 10px; }
.statrep-kpis--tight { grid-template-columns: repeat(4, 1fr); }
/* Prompt 113: 7 headline tiles when the "Checks on time" tile is present. Placed
   before the ≤ mobile rule below so that rule (repeat(2)) still wins on phones. */
.statrep-kpis--7 { grid-template-columns: repeat(7, 1fr); }
.statrep-kpi-sub { font: 500 var(--text-xs) / 1.3 var(--body); color: var(--text-muted); margin-top: 4px; }
.statrep-delta { font: 600 var(--text-xs) / 1 var(--body); color: var(--text-secondary); margin-top: 5px; }
.statrep-delta small { color: var(--text-muted); font-weight: 500; }

.statrep-card { background: var(--bg-surface); border: 1px solid var(--border); border-radius: var(--radius-md, 12px); padding: 14px 16px; }
.statrep-legend { display: flex; gap: 16px; margin-bottom: 8px; }
.statrep-chart-empty { color: var(--text-muted); font: italic 500 var(--text-sm) / 1.4 var(--body); padding: 10px 2px; }

/* editable comment: collapses in print (and hidden on screen unless the
   composer's "show empty slots" toggle is on) */
.statrep-comment { background: var(--bg-elevated); border: 1px solid var(--border); border-radius: var(--radius-md, 12px); padding: 10px 13px; margin: 10px 0 2px; font: 500 var(--text-sm) / 1.5 var(--body); color: var(--text-primary); min-height: 20px; outline: none; }
.statrep-comment::before { display: none; }
.statrep-comment.is-empty { border-style: dashed; color: var(--text-muted); }
.statrep-comment.is-empty::before { display: block; }
.statrep-body:not(.statrep-show-empty) .statrep-comment.is-empty { display: none; }

/* 4 · outstanding jobs */
.statrep-jobs { display: flex; flex-direction: column; gap: 14px; }
.statrep-jobgroup { background: var(--bg-surface); border: 1px solid var(--border); border-radius: var(--radius-md, 12px); overflow: hidden; }
.statrep-jobgroup-head { display: flex; align-items: center; gap: 8px; padding: 9px 13px; font: 700 var(--text-sm) / 1 var(--body); color: var(--text-primary); background: var(--bg-elevated); border-bottom: 1px solid var(--border); }
.statrep-jobgroup-n { margin-left: auto; font-weight: 600; color: var(--text-muted); }
.statrep-job { display: flex; align-items: center; gap: 8px; padding: 9px 13px; border-bottom: 1px solid var(--border); }
.statrep-job:last-child { border-bottom: 0; }
.statrep-job-main { display: flex; align-items: center; gap: 7px; flex: 1; min-width: 0; flex-wrap: wrap; }
.statrep-job-title { font: 500 var(--text-sm) / 1.35 var(--body); color: var(--text-primary); }
.statrep-job-age { flex-shrink: 0; font: 500 var(--text-xs) / 1 var(--body); color: var(--text-muted); white-space: nowrap; }
.statrep-overdue { color: var(--rag-high, #fb923c); font-weight: 700; }

/* 5 · site scorecard */
.statrep-scoretable { display: flex; flex-direction: column; border: 1px solid var(--border); border-radius: var(--radius-md, 12px); overflow: hidden; }
.statrep-score-hrow, .statrep-score-row { display: grid; grid-template-columns: 1.4fr repeat(5, 0.7fr) 1fr 1.6fr; align-items: center; gap: 8px; padding: 9px 12px; }
.statrep-scoretable--checks .statrep-score-hrow,
.statrep-scoretable--checks .statrep-score-row { grid-template-columns: 1.35fr repeat(5, 0.62fr) 0.95fr 1.05fr 1.4fr; }
.statrep-score-hrow { background: var(--bg-elevated); font: 600 var(--text-2xs, 11px) / 1 var(--body); text-transform: uppercase; letter-spacing: 0.04em; color: var(--text-muted); border-bottom: 1px solid var(--border); }
.statrep-score-row { border-bottom: 1px solid var(--border); font: 500 var(--text-sm) / 1 var(--body); color: var(--text-secondary); font-variant-numeric: tabular-nums; }
.statrep-score-row:last-child { border-bottom: 0; }
.statrep-score-site { font-weight: 700; color: var(--text-primary); }
.statrep-hot { color: var(--rag-fail, #f87171); }
.statrep-warm { color: var(--rag-high, #fb923c); }
.statrep-score-checks { display: flex; align-items: baseline; gap: 4px; flex-wrap: wrap; }
.statrep-score-checks .statrep-warm { font-size: var(--text-2xs, 11px); white-space: nowrap; }
.statrep-score-note { font: italic 500 var(--text-xs) / 1.4 var(--body); color: var(--text-secondary); border-bottom: 1px dashed var(--border-strong, var(--border)); padding: 2px 4px; outline: none; min-height: 16px; }
.statrep-verdict { display: inline-block; border: 1px solid; border-radius: 999px; padding: 3px 9px; font: 600 var(--text-2xs, 11px) / 1 var(--body); white-space: nowrap; }
.statrep-verdict--green { color: var(--rag-pass, #4ade80); border-color: color-mix(in srgb, var(--rag-pass, #4ade80) 45%, transparent); background: color-mix(in srgb, var(--rag-pass, #4ade80) 12%, transparent); }
.statrep-verdict--amber { color: var(--rag-warn, #fbbf24); border-color: color-mix(in srgb, var(--rag-warn, #fbbf24) 45%, transparent); background: color-mix(in srgb, var(--rag-warn, #fbbf24) 12%, transparent); }
.statrep-verdict--red { color: var(--rag-fail, #f87171); border-color: color-mix(in srgb, var(--rag-fail, #f87171) 45%, transparent); background: color-mix(in srgb, var(--rag-fail, #f87171) 12%, transparent); }

/* 6 · spend bar */
.statrep-spendbar { position: relative; display: flex; height: 18px; background: var(--bg-elevated); border-radius: 999px; overflow: hidden; }
.statrep-spend-fill { display: block; height: 100%; }
.statrep-spend-realised { background: var(--rag-pass-fill, #22c55e); }
.statrep-spend-committed { background: var(--rag-warn-fill, #f59e0b); }
.statrep-spend-over { position: absolute; right: 8px; top: 50%; transform: translateY(-50%); font: 700 var(--text-2xs, 11px) / 1 var(--body); color: #fff; }
.statrep-spend-legend { display: flex; flex-wrap: wrap; gap: 14px; margin-top: 8px; font: 500 var(--text-xs) / 1 var(--body); color: var(--text-secondary); }
.statrep-sw { display: inline-block; width: 10px; height: 10px; border-radius: 3px; margin-right: 5px; vertical-align: middle; }
.statrep-sw-realised { background: var(--rag-pass-fill, #22c55e); }
.statrep-sw-committed { background: var(--rag-warn-fill, #f59e0b); }

/* 7 · sign-off */
.statrep-signoff { margin-top: 24px; padding-top: 12px; border-top: 1px solid var(--border); font: 500 var(--text-xs) / 1.5 var(--body); color: var(--text-muted); }

/* footer buttons */
.statrep-foot-spacer { flex: 1; }

/* mobile — full width, single column (§4) */
@media (max-width: 599px) {
  .statrep-kpis, .statrep-kpis--tight { grid-template-columns: repeat(2, 1fr); }
  .statrep-cover-band { flex-direction: column; align-items: flex-start; text-align: left; }
  .statrep-score-hrow { display: none; }
  .statrep-score-row { grid-template-columns: 1fr auto; gap: 4px 10px; padding: 10px 12px; }
  .statrep-scoretable--checks .statrep-score-row { grid-template-columns: 1fr auto; }
  .statrep-score-row > span:nth-child(2)::before { content: 'Reported '; color: var(--text-muted); }
  .statrep-score-row > span:nth-child(3)::before { content: 'Completed '; color: var(--text-muted); }
  .statrep-score-row > span:nth-child(4)::before { content: 'Open '; color: var(--text-muted); }
  .statrep-score-row > span:nth-child(5)::before { content: 'Urgent '; color: var(--text-muted); }
  .statrep-score-row > span:nth-child(6)::before { content: 'Overdue '; color: var(--text-muted); }
  .statrep-scoretable--checks .statrep-score-row > span:nth-child(7)::before { content: 'Checks '; color: var(--text-muted); }
  .statrep-scoretable--checks .statrep-score-row > span:nth-child(8) { grid-column: 1 / -1; margin-top: 4px; }
  .statrep-score-site { grid-column: 1 / -1; }
  .statrep-score-note { grid-column: 1 / -1; }
  .statrep-score-checks { justify-content: flex-end; }
}

/* The monthly-only seventh KPI needs the primary layout breakpoint too;
   otherwise its seven fixed columns clip between 600px and 640px. */
@media (max-width: 640px) {
  .statrep-kpis--7 { grid-template-columns: repeat(2, 1fr); }
}

/* an accidental Cmd-P of the APP (not the export window) shouldn't dump the
   raw composer — the real PDF is the self-contained print window. */
@media print { .statrep-overlay { display: none !important; } }

/* ═══ P52 — per-row supplier override cell + supplier-split order blocks ═══ */
/* The row supplier cell: a quiet pick-trigger. Calm dim "List default"
   when inheriting; the supplier name (normal weight) when overridden. */
.inv-list-cell-supplier {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  max-width: 100%;
  padding: 6px 9px;
  background: var(--bg-elevated);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  color: var(--text-primary);
  font: 600 var(--text-xs) / 1.2 var(--body);
  font-family: inherit;
  text-align: left;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
button.inv-list-cell-supplier { cursor: pointer; transition: border-color 0.12s, background 0.12s; }
button.inv-list-cell-supplier:hover { border-color: var(--text-muted); }
button.inv-list-cell-supplier:focus-visible {
  outline: none;
  border-color: var(--accent);
  box-shadow: 0 0 0 2px var(--accent-soft);
}
.inv-list-cell-supplier.is-default { color: var(--text-muted); font-weight: 500; }

/* Generate-order modal — one block per supplier */
.inv-order-modal-sub {
  margin: 0 0 12px;
  font-size: var(--text-sm);
  color: var(--text-secondary);
}
.inv-order-block-meta {
  font: 500 var(--text-xs) / 1.2 var(--body);
  color: var(--text-muted);
}
.inv-order-block-actions {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-top: 8px;
  flex-wrap: wrap;
}
.inv-order-block-actions a.callout-btn { text-decoration: none; gap: 6px; }
.inv-order-block-hint { font-size: var(--text-xs); color: var(--text-muted); }

/* Mobile: the supplier cell is a real tap target in the card grid */
@media (max-width: 767px) {
  .inv-list-cell-supplier { padding: 10px 12px; font-size: var(--text-sm); }
}

/* ═══ P53 — site-wide Order sheet (what needs ordering, by supplier) ═══ */
/* The "To order" KPI card doubles as the Order-sheet entry when > 0. */
button.inv-kpi-btn {
  font-family: inherit;
  cursor: pointer;
  text-align: left;
  width: 100%;
  appearance: none;
  transition: border-color 0.12s, transform 0.06s;
}
button.inv-kpi-btn:hover { border-color: color-mix(in srgb, var(--accent) 40%, var(--border)); }
button.inv-kpi-btn:active { transform: scale(0.99); }
button.inv-kpi-btn:focus-visible { outline: none; box-shadow: 0 0 0 2px var(--accent-soft); }

.inv-order-sheet-loading {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 10px;
  padding: 48px 16px;
  color: var(--text-secondary);
  font-size: var(--text-sm);
}
.inv-order-sheet-sub {
  margin: 0 0 14px;
  font-size: var(--text-sm);
  color: var(--text-secondary);
}
.inv-order-sheet-block {
  background: var(--bg-surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  padding: 14px;
  margin-bottom: 12px;
}
.inv-order-sheet-block-head {
  display: flex;
  align-items: baseline;
  gap: 8px;
  flex-wrap: wrap;
  margin-bottom: 10px;
}
.inv-order-sheet-block-name {
  font: 600 var(--text-base) / 1.2 var(--display);
  color: var(--text-primary);
}
.inv-order-src-chips {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  margin-bottom: 8px;
}
.inv-order-src-chip {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 3px 9px;
  background: var(--bg-elevated);
  border: 1px solid var(--border);
  border-radius: 999px;
  font: 600 var(--text-xs) / 1.2 var(--body);
  color: var(--text-secondary);
}
.inv-order-src-dot {
  width: 8px;
  height: 8px;
  border-radius: 50%;
  flex: none;
}
.inv-order-sheet-foot {
  display: flex;
  justify-content: flex-end;
  padding-top: 4px;
}

/* ═══ P83 — On-order awareness (order sheet + count-mode Generate order) ═════
   Calm neutral surfaces (§4): stock already on an open PO is CONTEXT, not an
   act-now signal — the identifying colour lands on the truck icon only. Both
   themes inherit via the vars. */
.inv-onorder-banner {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 10px 12px;
  margin-bottom: 12px;
  background: var(--bg-elevated);
  border: 1px solid var(--border);
  border-radius: 10px;
  color: var(--text-secondary);
  font: 500 var(--text-sm) / 1.35 var(--body);
}
.inv-onorder-banner svg { width: 16px; height: 16px; color: var(--accent); flex: none; }
/* P85 — the order-sheet "N awaiting delivery" line reuses this calm banner AS A
   BUTTON: give the interactive variant the §4 44px tap-target floor (the static
   <div> uses of the class keep their content height). */
button.inv-onorder-banner { min-height: 44px; }
.inv-onorder-sec { margin-top: 6px; }
.inv-onorder-sec > summary {
  cursor: pointer;
  list-style: none;
  display: flex;
  align-items: center;
  gap: 6px;
  min-height: 44px;              /* §4 — the disclosure is a real tap target */
  padding: 6px 0;
  font: 600 var(--text-xs) / 1.2 var(--body);
  color: var(--text-secondary);
}
.inv-onorder-sec > summary::-webkit-details-marker { display: none; }
.inv-onorder-sec > summary svg { width: 13px; height: 13px; color: var(--text-secondary); flex: none; }
.inv-onorder-rows { display: flex; flex-direction: column; gap: 6px; padding: 2px 0 6px; }
.inv-onorder-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  width: 100%;
  min-height: 44px;             /* §4 — the row taps through to the PO */
  text-align: left;
  padding: 8px 10px;
  background: var(--bg-elevated);
  border: 1px solid var(--border);
  border-radius: 8px;
  cursor: pointer;
  color: var(--text-primary);
  font: 500 var(--text-sm) / 1.3 var(--body);
}
.inv-onorder-row:hover { border-color: var(--border-strong); }
.inv-onorder-desc { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.inv-onorder-chip {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  flex: none;
  color: var(--text-secondary);
  font: 600 var(--text-xs) / 1.2 var(--body);
}
.inv-onorder-chip svg { width: 12px; height: 12px; color: var(--accent); flex: none; }

/* Roomier reading width on desktop; the fullscreen body already pads. */
@media (min-width: 768px) {
  .inv-order-sheet-overlay .inv-list-table-body { max-width: 720px; margin: 0 auto; width: 100%; }
}

/* ═══ P54 — Count mode: thumb-first stock-take walk ═════════════════════════ */
/* The list-table body swaps to one big stepper row per item. The table chrome
   (supplier/Generate-order meta row + add-row/column footer) hides so the
   count is the only thing on screen. */
.inv-list-table-overlay.is-counting .inv-list-table-head-meta { display: none; }
.inv-list-table-overlay.is-counting .inv-list-table-foot { display: none; }

/* Count + Generate-order grouped at the right of the header meta row. */
.inv-list-head-meta-actions {
  margin-left: auto;
  display: inline-flex;
  gap: 8px;
  align-items: center;
  flex-wrap: wrap;
}
.inv-list-head-meta-actions .inv-list-order-btn { margin-left: 0; }

.inv-count { display: flex; flex-direction: column; }
.inv-count-list { display: flex; flex-direction: column; gap: 10px; }

.inv-count-row {
  display: flex;
  align-items: center;
  gap: 12px;
  flex-wrap: wrap;
  padding: 12px 14px;
  background: var(--bg-surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  transition: border-color 0.15s, box-shadow 0.15s, background 0.15s;
}
.inv-count-row.is-counted { border-color: color-mix(in srgb, var(--accent) 34%, var(--border)); }
.inv-count-row.is-focus {
  border-color: var(--accent);
  box-shadow: 0 0 0 3px var(--accent-soft);
}
.inv-count-row-main { flex: 1; min-width: 150px; display: flex; flex-direction: column; gap: 4px; }
.inv-count-row-name {
  font: 600 var(--text-md) / 1.25 var(--body);
  color: var(--text-primary);
}
/* Per-row Info button in count mode — the SAME affordance the table row
   carries (REUSES .inv-list-info-btn: badge anchor + .has-info accent). It now
   sits as a direct sibling of the stepper (not on the name line) so the row's
   align-items:center vertically centres it with the +/- buttons, and it is
   sized/styled to MATCH .inv-count-step (46px, elevated fill, same radius) for
   one consistent control cluster. Leaving the flex:1 main block, its width is
   ~unchanged vs the old name-line placement — no new phone-wrap risk. This
   block sits AFTER .inv-list-info-btn in source order, so it wins at equal
   specificity. */
.inv-count-info-btn {
  width: 46px; height: 46px;
  margin: 0;
  flex: none;
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  background: var(--bg-elevated);
}
.inv-count-info-btn:hover { background: var(--bg-surface); border-color: var(--text-muted); }
.inv-count-info-btn.has-info { border-color: color-mix(in srgb, var(--accent) 40%, var(--border)); }
.inv-count-info-btn:focus-visible { outline: none; box-shadow: 0 0 0 2px var(--accent-soft); }
.inv-count-row-meta {
  display: flex;
  align-items: center;
  gap: 8px;
  flex-wrap: wrap;
  font: 500 var(--text-xs) / 1.2 var(--body);
  color: var(--text-muted);
}
.inv-count-row-sku {
  font-family: var(--font-mono, ui-monospace, SFMono-Regular, monospace);
  font-size: var(--text-xs);
  color: var(--text-secondary);
}
.inv-count-row-needed { color: var(--text-secondary); font-weight: 600; }

/* Stepper — big thumb targets, direct entry in the middle. */
.inv-count-stepper {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  flex: none;
}
.inv-count-step {
  width: 46px;
  height: 46px;
  flex: none;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  background: var(--bg-elevated);
  color: var(--text-primary);
  cursor: pointer;
  transition: background 0.12s, border-color 0.12s, transform 0.06s;
}
.inv-count-step:hover { background: var(--bg-surface); border-color: var(--text-muted); }
.inv-count-step:active { transform: scale(0.94); }
.inv-count-step:focus-visible { outline: none; box-shadow: 0 0 0 2px var(--accent-soft); }
.inv-count-input {
  width: 76px;
  height: 46px;
  text-align: center;
  font: 700 18px / 1 var(--body);          /* ≥16px — no iOS zoom-on-focus */
  color: var(--text-primary);
  background: var(--bg-base);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  -moz-appearance: textfield;
  appearance: textfield;
}
.inv-count-input:focus {
  outline: none;
  border-color: var(--accent);
  box-shadow: 0 0 0 2px var(--accent-soft);
}
.inv-count-input::-webkit-outer-spin-button,
.inv-count-input::-webkit-inner-spin-button { -webkit-appearance: none; margin: 0; }

.inv-count-row-foot {
  width: 100%;
  display: flex;
  gap: 12px;
  align-items: center;
}
.inv-count-row-foot:empty { display: none; }
.inv-count-was { font: 500 var(--text-xs) / 1.2 var(--body); color: var(--text-muted); }
.inv-count-order { font: 700 var(--text-xs) / 1.2 var(--body); color: #dc2626; }

/* Sticky bottom action bar — progress + next + done, thumb-reachable. */
.inv-count-bar {
  position: sticky;
  bottom: 0;
  z-index: 5;
  display: flex;
  align-items: center;
  gap: 10px;
  flex-wrap: wrap;
  margin-top: 12px;
  padding: 12px 14px;
  padding-bottom: max(12px, env(safe-area-inset-bottom, 0px));
  background: color-mix(in srgb, var(--bg-base) 90%, transparent);
  -webkit-backdrop-filter: blur(8px);
  backdrop-filter: blur(8px);
  border-top: 1px solid var(--border);
}
.inv-count-progress { font: 600 var(--text-sm) / 1 var(--body); color: var(--text-secondary); }
.inv-count-bar-actions { margin-left: auto; display: inline-flex; gap: 8px; }
.inv-count-done { min-width: 92px; justify-content: center; }
.inv-count-next[disabled] { opacity: 0.5; cursor: default; }

/* Summary panel */
.inv-count-summary {
  max-width: 420px;
  margin: 24px auto;
  text-align: center;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 10px;
  padding: 26px 20px;
  background: var(--bg-surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
}
.inv-count-summary-icon {
  width: 52px; height: 52px;
  display: inline-flex; align-items: center; justify-content: center;
  color: var(--accent);
  background: var(--accent-soft);
  border-radius: 50%;
}
.inv-count-summary-icon svg { width: 26px; height: 26px; }
.inv-count-summary-title {
  font: 600 var(--text-lg) / 1.2 var(--display, var(--body));
  color: var(--text-primary);
}
.inv-count-summary-stats { font: 500 var(--text-md) / 1.4 var(--body); color: var(--text-secondary); }
.inv-count-summary-stats strong { color: var(--text-primary); font-weight: 700; }
.inv-count-summary-actions {
  display: flex;
  flex-direction: column;
  gap: 8px;
  width: 100%;
  margin-top: 8px;
}
.inv-count-summary-actions .callout-btn { justify-content: center; gap: 6px; }

/* ── Table input hygiene: kill iOS zoom-on-focus (≤599px) ─────────────────── */
/* .inv-list-cell rides --text-sm (12px); Safari zooms the viewport when a
   focused input is under 16px. Raise to 16px on phones only — longhand
   font-size, AFTER the base rules (source-order law). The count input has its
   own ≥16px size above, so it's unaffected. */
@media (max-width: 599px) {
  .inv-list-cell { font-size: 16px; }
  .inv-list-row-card .inv-list-cell { font-size: 16px; }
}

/* ── First-open teaching hint (Prompt 55) ────────────────────────────────── */
/* A calm one-liner above the table teaching "Needed = your par level".
   Dismiss = gone forever (localStorage). Shows on ALL breakpoints (unlike the
   KB desktop-only drag hint) — the phone is the primary stock-take device.
   Hidden in count mode, which owns the screen. Both themes via tokens. */
.inv-list-hint {
  display: flex;
  align-items: flex-start;
  gap: 10px;
  margin: 12px 16px 0;
  padding: 10px 12px;
  background: var(--accent-soft);
  border: 1px solid color-mix(in srgb, var(--accent) 30%, transparent);
  border-radius: var(--radius-md);
  color: var(--text-secondary);
  font: 500 var(--text-sm) / 1.45 var(--body);
}
.inv-list-hint > svg { width: 17px; height: 17px; flex-shrink: 0; margin-top: 1px; color: var(--accent); }
.inv-list-hint span { flex: 1; min-width: 0; }
.inv-list-hint strong { color: var(--text-primary); font-weight: 700; }
.inv-list-hint-close {
  flex-shrink: 0;
  width: 40px; height: 40px;
  display: grid; place-items: center;
  background: transparent;
  border: 1px solid transparent;
  border-radius: var(--radius-md);
  color: var(--text-muted);
  cursor: pointer;
  transition: background 0.1s, color 0.1s, border-color 0.1s;
  -webkit-tap-highlight-color: transparent;
}
.inv-list-hint-close:hover { background: var(--bg-surface); color: var(--text-primary); border-color: var(--border); }
.inv-list-hint-close:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.inv-list-hint-close svg { width: 15px; height: 15px; }
.inv-list-table-overlay.is-counting .inv-list-hint { display: none; }
@media (max-width: 599px) {
  .inv-list-hint { margin-left: 12px; margin-right: 12px; }
}

/* ── Prompt 14: at-a-glance stock signals ───────────────────────────────────
   Loud-ONLY-when-firing left accent bar on the desktop stock/items table rows
   (the §4 act-now signal; reuses the --rag-* tokens, NOT the .colb-row shell).
   Rows that are fine stay transparent. Mobile cards already carry the status
   chip, so they're untouched. box-shadow inset renders reliably on a table
   cell regardless of border-collapse. */
.inv-table tbody tr[data-status="out_of_stock"] td:first-child { box-shadow: inset 4px 0 0 var(--rag-fail); }
.inv-table tbody tr[data-status="below_par"]    td:first-child { box-shadow: inset 4px 0 0 var(--rag-warn); }

/* "N unpriced" caveat under the Stock-value KPI — items holding stock with no
   last_purchase_price, so the £ total honestly signals it under-counts (§5).
   Stacks below the value via the card's own flex-column gap. */
.inv-kpi-note { font: 500 var(--text-xs) / 1.2 var(--body); color: var(--text-muted); }

/* Prompt 71 — mobile tap heights for the compacted Overview pieces. The scoped
   .project-team-grid rules above out-specific the generic ≤599px .project-member
   trim, so the ≥44px floor must be restated at the same scoped specificity
   (and, per the §4 source-order law, appended at the END of the file). */
@media (max-width: 599px) {
  .project-team-grid .project-member { padding: 9px 10px; }      /* 26px avatar + 2×9 = 44px row */
  .project-team-grid .project-member-add { min-height: 44px; }
  .project-plansnap-row { padding: 13px 12px; }                  /* ≥44px tap row */
}

/* Prompt 72 — plan quick-add on mobile: 16px input (iOS zoom law §4) + ≥44px
   tap targets. Appended at END of file per the §4 source-order law. */
@media (max-width: 599px) {
  .ptree-qa-btn { min-height: 44px; }
  .ptree-qa-input { font-size: 16px; min-height: 44px; }
}

/* ═══ INVENTORY ITEM FORM — collapsed folds (2026-07-17) ═════════════════
   Progressive disclosure for the New/Edit item modal: essentials stay open
   (name/category · locations+par · supplier); secondary sections are
   .inv-form-fold — a .callout-form-section whose head is a ≥44px toggle
   button. Collapsed by default on NEW; a fold holding data auto-expands on
   EDIT (§5 — filled fields are never hidden behind a fold). Mechanics
   follow the roles-editor P45 pattern: hidden body + an explicit [hidden]
   rule (§6 — .inv-fold-body sets display:flex, so the UA [hidden] rule
   alone would silently lose). */
.inv-form-fold { padding: 0; gap: 0; }
.inv-fold-head {
  display: flex; align-items: center; gap: 8px;
  width: 100%; min-height: 44px; padding: 12px 14px;
  background: none; border: 0; border-radius: var(--radius-md);
  cursor: pointer; text-align: left;
}
.inv-fold-head:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }
.inv-fold-title {
  font-family: var(--display); font-size: var(--text-base);
  font-weight: 600; color: var(--text-primary); flex-shrink: 0;
}
.inv-fold-hint {
  margin-left: auto; min-width: 0;
  font-size: var(--text-xs); font-weight: 500; color: var(--text-muted);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.inv-fold-chev { flex-shrink: 0; color: var(--text-secondary); transition: transform 0.15s ease; }
.inv-form-fold.is-open .inv-fold-hint { display: none; }
.inv-form-fold.is-open .inv-fold-chev { margin-left: auto; transform: rotate(180deg); }
.inv-fold-body {
  display: flex; flex-direction: column; gap: 10px;
  padding: 0 14px 14px;
}
.inv-fold-body[hidden] { display: none !important; }
/* Mobile matches the .callout-form-section 10px trim (§4). END of file per
   the source-order law. */
@media (max-width: 599px) {
  .inv-fold-head { padding: 12px 10px; }
  .inv-fold-body { padding: 0 10px 12px; }
}

/* ─── Desktop: inventory browses as LISTS, not card grids (2026-07-17) ────
   Radek: on wide screens the multi-column .inv-card grid is hard to scan —
   every card-based inventory collection (Lists / Locations / Suppliers /
   Counts + the Stock <768 card fallback) should read as a single-column list
   of dense horizontal rows, matching the Stock register's .colb-row look on
   desktop. Phones (≤720px, where .cl-card-grid single-columns anyway — css/14)
   keep the tap-friendly stacked cards. 721px = exactly where the grid goes
   multi-column, so there's no dead band between "stacked card" and "row".
   SCOPED to .inv-card via :has() so the SHARED .cl-card-grid (checklists /
   audits submissions) is untouched. Base .inv-card rules live at the top of
   this file, so this min-width override must sit AFTER them (source order,
   §4) — hence the file tail. Row layout: title first (order:-1) as the hero,
   context chips after it, meta / stock counter pushed to the far right. */
@media (min-width: 721px) {
  .cl-card-grid:has(> .inv-card) { grid-template-columns: 1fr; gap: 8px; }
  .inv-card { align-items: center; padding: 10px 14px 10px 16px; }
  .inv-card-main {
    flex-direction: row;
    align-items: center;
    gap: 8px 14px;
    flex-wrap: wrap;
  }
  .inv-card-title { order: -1; font-size: var(--text-md); flex: 0 1 auto; }
  .inv-card-top { flex: 0 0 auto; gap: 6px; }
  .inv-card-top .card-loc-chip { margin-right: 0; }
  .inv-card-sub {
    flex: 0 1 auto;
    min-width: 0;
    max-width: 46ch;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
  }
  .inv-card-meta,
  .inv-card-chip-row { margin-left: auto; flex: 0 0 auto; }
}

/* ─── Card-with-inline-actions variant (Counts templates, 2026-07-23) ─────
   A card that OPENS a record and also carries a CTA splits into a clickable
   HIT button + an action cluster — the .colb-row--has-actions shape from
   css/21, reused verbatim in spirit so both row families behave the same.
   (The CTA used to be nested inside the card's own <button>: invalid HTML,
   so the parser closed the card first and the CTA rendered as an orphan bar
   between cards.) The frame, hover and focus stay on the card; the padding
   moves onto the hit button so its whole area is the tap target.
   Must sit AFTER the min-width:721px block above (source order, §4); the
   doubled class also out-specifies the base .inv-card padding either way. */
.inv-card.inv-card--has-actions {
  padding: 0;
  cursor: default;
  flex-direction: column;   /* phones: CTA sits UNDER the stacked card body */
  align-items: stretch;
}
.inv-card-hit {
  flex: 1;
  min-width: 0;
  width: 100%;
  display: flex;
  align-items: stretch;
  padding: 12px;
  background: transparent;
  border: 0;
  border-radius: inherit;
  font-family: inherit;
  color: inherit;
  text-align: left;
  cursor: pointer;
}
.inv-card-actions {
  display: flex;
  align-items: center;
  gap: 6px;
  flex-shrink: 0;
  padding: 0 12px 12px;
}
/* The hit button is the focusable open-affordance here (the outer .inv-card is a
   non-focusable <div> in this variant) — keep the accent ring (§4). */
.inv-card-hit:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: -2px;
  border-radius: var(--radius-md);
}
/* §4 44px tap floor — .callout-btn's own padding lands short of it. Scoped to
   this cluster so the shared button is untouched. */
.inv-card-actions .callout-btn { min-height: 44px; }

@media (min-width: 721px) {
  /* Desktop rows read left-to-right: body, then the CTA at the far end. */
  .inv-card.inv-card--has-actions { flex-direction: row; align-items: center; }
  .inv-card-hit { align-items: center; padding: 10px 4px 10px 16px; }
  .inv-card-actions { padding: 0 14px 0 4px; }
}
/* Compact the CTA so it sits calmly in a dense row (the .colb-row-actions
   .audit-btn precedent) — but ONLY on a real pointer. A wide TOUCH device (an
   iPad in landscape is >=721px) is still a thumb, so it keeps the 44px floor. */
@media (min-width: 721px) and (hover: hover) and (pointer: fine) {
  .inv-card-actions .callout-btn { min-height: 0; padding: 8px 14px; font-size: var(--text-sm); }
}

/* ═══════════════════════════════════════════════════════════════════════
   SAFETY — SSoW READ MODE (Prompt 116)
   A method statement is read while the job is being done, so Read mode is a
   document, not a record card: generous line length, a numbered sequence, and
   each photo at a size where the thing being pointed at is actually visible.
   Every colour is a css/01 token, so both themes come free (§4).
   ═══════════════════════════════════════════════════════════════════════ */
/* Long-form fields keep the line breaks the manager typed — a method step or
   an emergency arrangement is written as lines, and collapsing them turns a
   procedure into a paragraph. SSoW-only: shared .project-desc is untouched. */
.safety-ssow-prose { white-space: pre-line; }
/* Prompt 119: "If something goes wrong" is the one SSoW prose block that no cap
   reached — Scope rides .project-desc p (72ch) and the step text carries its own
   70ch, but the emergency block is a bare <div> inside .safety-haz-view, so at
   1920px it ran the full canvas. Scoped to .safety-haz-view so the step text
   above keeps its own measured value. */
.safety-haz-view > .safety-ssow-prose { max-width: 72ch; }
/* ONE calm intro panel (PPE + who may do the job), never two loud cards. */
.safety-ssow-intro {
  display: grid;
  gap: 12px 26px;
  grid-template-columns: repeat(auto-fit, minmax(210px, 1fr));
  margin: 0 0 16px;
  padding: 13px 15px;
  background: var(--bg-surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
}
.safety-ssow-intro-item { display: flex; flex-direction: column; gap: 3px; min-width: 0; }
.safety-ssow-intro-label { color: var(--text-muted); font: 600 var(--text-xs) / 1.3 var(--body); }
.safety-ssow-intro-val { color: var(--text-primary); font: 500 var(--text-md) / 1.45 var(--body); }
/* The numbered method. list-style is dropped because the number is its own
   element (aria-hidden — the <ol>/<li> already carries the order for a screen
   reader, so a visible duplicate would be read twice). */
.safety-ssow-method { list-style: none; margin: 0 0 16px; padding: 0; display: grid; gap: 12px; }
.safety-ssow-step {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr);
  gap: 12px;
  align-items: start;
  padding: 14px 15px;
  background: var(--bg-elevated);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
}
.safety-ssow-step-num {
  display: flex; align-items: center; justify-content: center;
  width: 30px; height: 30px; flex: 0 0 30px;
  background: var(--bg-surface);
  border: 1px solid var(--border-strong);
  border-radius: 999px;
  color: var(--text-primary);
  font: 700 var(--text-md) / 1 var(--body);
}
.safety-ssow-step-body { min-width: 0; display: grid; gap: 11px; }
/* The instruction and its control measure are ONE prose column, and this
   wrapper is what makes that true (Prompt 123). Leaving the control as a third
   child of the body was MEASURED and rejected: the body becomes two columns at
   1024px, so the control had to be pinned to row 2 — and a tall photo in row 1
   then stretched that row, stranding the control at the BOTTOM of the card with
   a ~250px gap between an instruction and the control that qualifies it. A
   spanning photo distributes its surplus height across the rows it spans, so no
   placement rule fixes that; the two have to share one cell.
   align-content:start keeps them at the top when the photo is the taller side. */
.safety-ssow-step-main { min-width: 0; display: grid; gap: 11px; align-content: start; }
.safety-ssow-step-text {
  margin: 0;
  /* ~70ch is the readable measure for instructions people follow under
     pressure; the step card itself is free to be wider. */
  max-width: 70ch;
  color: var(--text-primary);
  font: 500 var(--text-md) / 1.6 var(--body);
}
/* The step's own control measure (Prompt 123). An inset panel on the elevated
   step card — the same --bg-surface-inside-a-card treatment .safety-ssow-step-num
   already uses, so both themes come free. Capped at the step text's own 70ch so
   the two paragraphs share one measure. */
.safety-ssow-step-control {
  display: grid;
  gap: 3px;
  max-width: 70ch;
  padding: 9px 11px;
  background: var(--bg-surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
}
.safety-ssow-step-control-label { color: var(--text-muted); font: 600 var(--text-xs) / 1.3 var(--body); }
.safety-ssow-step-control p { margin: 0; color: var(--text-primary); font: 500 var(--text-md) / 1.55 var(--body); }
.safety-ssow-step-media { display: flex; flex-wrap: wrap; gap: 12px; min-width: 0; }
.safety-ssow-figure { margin: 0; min-width: 0; max-width: 320px; flex: 1 1 200px; }
/* The photo is a real <button>, so Enter/Space open the viewer and the focus
   ring is free. position:relative hosts BOTH the annotation badge and the SVG
   mark overlay the reader attaches on load. */
.safety-ssow-photo {
  position: relative;
  display: block;
  width: 100%;
  padding: 0;
  background: var(--bg-surface);
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-sm);
  cursor: pointer;
  overflow: hidden;
}
.safety-ssow-photo:focus-visible { outline: 3px solid var(--accent); outline-offset: 2px; }
/* height:auto keeps the element box AT the image's aspect ratio, which is what
   lets the shared overlay's default 'contain'/meet fit land the marks exactly —
   and means nothing the manager circled is ever cropped away. */
.safety-ssow-photo img { display: block; width: 100%; height: auto; }
.safety-ssow-caption {
  margin: 7px 1px 0;
  color: var(--text-secondary);
  font: 500 var(--text-sm) / 1.5 var(--body);
  white-space: pre-line;
}
/* Media sits BENEATH its text by default — that is the required behaviour below
   768px, and it stays that way up to the app's own desktop breakpoint (1024px,
   §4). Side-by-side is switched on there, not at 768px, because 768 was
   MEASURED and rejected: the split left a 373px text measure (cramped for
   instructions someone follows under pressure) and, with the media column down
   to 276px, a 2-photo step stacked back to 519px tall. At 1024px the same step
   reads 529px of text beside a side-by-side photo row, 238px tall. Below the
   switch the step still gets the full width for text and a wrapping photo row. */
@media (min-width: 1024px) {
  .safety-ssow-step-body.has-media {
    grid-template-columns: minmax(0, 1.35fr) minmax(0, 1fr);
    gap: 16px;
    align-items: start;
  }
  /* The base wrapping row + the 320px cap carry over deliberately. Two earlier
     drafts were measured and rejected: an uncapped figure rendered a 4:3 photo
     ~465px tall, and a single-column media stack made a 2-photo step 513px tall
     with a mostly-empty text column beside it. Both turn the photo into the
     document hero — the one thing a step photo must never be. It illustrates
     its instruction; it never outranks it. */
}

/* ── PPE pictograms (Prompt 121) ──────────────────────────────────────────
   The CHIP PICKER itself needs no CSS at all: chipRow_ emits
   .ctree-lt-row.safety-chip-row, both hosts already flex-wrap it, .ctree-lt
   svg is already 20px, .is-on already styles selection and the inline editor
   already floors .ctree-lt at 44px. What follows is only the two places a
   picked item is rendered with its own spec line — the editor's spec rows and
   the reader's strip. Tokens only, so both themes come out right by
   construction.

   NEVER use <span> inside .safety-haz-view-row: its `span` descendant rule
   (above) forces min-width:110px and the muted label colour onto anything it
   wraps, which would push every PPE name into a second column. */
.safety-ppe-specs:not(:empty) { display: flex; flex-direction: column; gap: 6px; margin: 8px 0; }
.safety-ppe-spec-row { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.safety-ppe-spec-label { display: flex; align-items: center; gap: 8px; min-width: 0; color: var(--text-secondary); }
.safety-ppe-spec-label svg { width: 20px; height: 20px; flex: none; }
.safety-ppe-spec-name { font: 600 12px / 1.3 var(--body); }
/* flex-basis 200px: on a wide canvas every name and its input share one line
   (measured 14/14 at 1440px). The input never shrinks below 200px — a slot
   too narrow to read "Half-mask, A1P2 filters, face-fit tested" back is worse
   than a second line. Phones stack uniformly instead — see the ≤599px block
   at the end of this file. */
.safety-ppe-spec-row input[type="text"] { flex: 1 1 200px; min-width: 0; }

.safety-ppe-list { display: flex; flex-direction: column; gap: 6px; }
.safety-ppe-item { display: flex; align-items: flex-start; gap: 8px; }
.safety-ppe-item > svg { width: 18px; height: 18px; flex: none; margin-top: 1px; color: var(--text-secondary); }
.safety-ppe-text { min-width: 0; }
.safety-ppe-name { color: var(--text-primary); font: 600 13px / 1.4 var(--body); }
.safety-ppe-spec { color: var(--text-secondary); font: 500 13px / 1.4 var(--body); }
/* The free-text PPE line under the strip in the SSoW intro panel. */
.safety-ppe-detail { margin-top: 6px; }
@media (max-width: 599px) {
  /* Uniform stacking beats flex-wrap's raggedness here. With the basis rule
     above, "Gloves" keeps its input alongside while "Respiratory protection
     (RPE)" pushes its own below — so a list of picks alternates between two
     layouts down the screen (measured: 6 of 14 rows one way, 8 the other).
     On a phone every spec line stacks, and every input gets the full width.
     Appended AFTER the base rule: a media query adds no specificity (§4). */
  .safety-ppe-spec-row { align-items: flex-start; }
  .safety-ppe-spec-row input[type="text"] { flex: 1 0 100%; }
}

/* Prompt 122: the 5×5 score selects are a phone control and were 38px — under
   the §4 44px floor. Raising all FOUR together (initial AND residual): the two
   pairs sit in matching rows, so a 44px residual beside a 38px likelihood would
   read as a mistake, and the residual pair is new in this change. The base rule
   is .safety-haz-lvl select up the file; appended at the END because a media
   query adds NO specificity, so it must come after in source order (§4). */
@media (max-width: 599px) {
  .safety-haz-lvl select { min-height: 44px; padding: 10px 8px; }
}

/* Prompt 125: the action button inside an alert banner is a phone control and
   was 37px — under the §4 44px floor. Exactly TWO buttons live inside a
   .compliance-alert (the P50 "Ask readers to confirm" nudge and the P125
   sample-honesty "Edit document"), both in the same banner shell, so they are
   raised together: a 44px Edit beside a 37px Ask would read as a mistake, and
   the Edit button is new in this change. Every OTHER .compliance-alert in the
   app IS itself the button and already clears the floor on its own padding.
   The base rule is .callout-btn in css/12; appended at the END because a media
   query adds NO specificity, so it must come after in source order (§4). */
@media (max-width: 599px) {
  .compliance-alert .callout-btn { min-height: 44px; }
}

/* Prompt 164: compliance-tree controls are repeated phone actions, so their
   hit boxes meet the §4 floor. The existing 2px flex gap remains between all
   adjacent controls: no overlap is traded onto Add or Edit. Appended after the
   30px base / 34px ≤640px rule because a media query adds no specificity. */
@media (max-width: 599px) {
  .ctree-act { width: 44px; height: 44px; }
}
