/* =========================================================
   GIMS — Greenlight Internal Management System
   Cockpit stylesheet. SYSTEM 1 skin (Kare, 1-bit).

   Single theme by design. GIMS is a one-bit world: black ink on
   white paper, texture from dither, hierarchy from weight and
   structure. There is no dark mode and no theme toggle, because a
   1-bit language has nothing to invert into. The Reminiscences skin
   this replaced is kept whole at gims.css.reminiscences.bak.

   ONE spot colour: green. It is the green light, and it marks going --
   the active nav rail, the live pulse, the Shopfront's smile, and the
   states that mean a thing is clear (verified, clean, passing). It is
   never chrome: no green button, no green panel, no green heading.
   Severity is carried by dither density and weight, never by hue, which
   is why --red is black and --amber is grey. A SECOND colour arriving
   on this page is a bug.
   ========================================================= */

/* Chicago is self-hosted. No CDN, no Google Fonts, no external font
   request of any kind — the cockpit renders identically on a box with
   no route to the internet. */
@font-face {
  font-family: 'ChicagoFLF';
  src: url('/static/fonts/ChicagoFLF.ttf') format('truetype');
  font-display: swap;
}

/* ---------- tokens ---------- */
:root {
  color-scheme: light;

  /* Ground & surfaces */
  --ground: #FFFFFF;
  --panel: #FFFFFF;
  --panel-raised: #FFFFFF;
  --panel-inset: #F0F0F0;

  /* Structure — pure black lines */
  --bezel: #111111;
  --bezel-light: #999999;

  /* Sidebar — black bar, white text */
  --sidebar-bg: #111111;
  --sidebar-text: #FFFFFF;
  --sidebar-hover: #333333;
  --sidebar-active: #000000;

  /* Ink — pure black */
  --text-primary: #111111;
  --text-bright: #000000;
  --text-dim: #666666;
  --text-label: #888888;
  --ink: #111111;

  /* Primary accent — green (the ONE spot colour) */
  --accent: #12813E;
  --accent-glow: rgba(18, 129, 62, 0.30);
  --accent-soft: rgba(18, 129, 62, 0.09);

  /* Secondary accent — same green, for active state */
  --brass: #12813E;
  --brass-glow: rgba(18, 129, 62, 0.30);
  --brass-soft: rgba(18, 129, 62, 0.09);

  /* Semantics — 1-bit world: dither density and weight carry severity,
     not hue. --red is black on purpose: an alarm is the heaviest mark
     on the page, not the reddest one. */
  --green: #12813E;
  --green-glow: rgba(18, 129, 62, 0.30);
  --green-soft: rgba(18, 129, 62, 0.09);
  --amber: #666666;
  --amber-glow: rgba(102, 102, 102, 0.30);
  --amber-soft: rgba(102, 102, 102, 0.09);
  --red: #111111;
  --red-glow: rgba(17, 17, 17, 0.20);
  --red-soft: rgba(17, 17, 17, 0.11);
  --teal: #888888;
  --teal-glow: rgba(136, 136, 136, 0.30);
  --teal-soft: rgba(136, 136, 136, 0.09);
  --purple: #555555;
  --purple-glow: rgba(85, 85, 85, 0.30);
  --purple-soft: rgba(85, 85, 85, 0.09);

  /* Selection — inverse video */
  --selection-bg: #111111;
  --selection-text: #FFFFFF;

  /* Type. Chicago for anything that names a thing (headings, nav,
     buttons, table headers); Verdana for prose, because Chicago is a
     display face and a paragraph set in it is unreadable; Monaco for
     every measured number, with tabular figures so a column of them
     lines up. */
  --font-display: 'ChicagoFLF', 'Courier New', monospace;
  --font-mono: Monaco, Consolas, 'Courier New', monospace;
  --font-body: Verdana, Geneva, Tahoma, sans-serif;

  --sidebar-w: 200px;
  --topbar-h: 52px;
  /* Hard corners: a 1-bit window has no anti-aliased radius to draw
     one with. Buttons are the one exception and set their own. */
  --radius: 0px;
  --radius-lg: 0px;
  /* A drop shadow with no blur and no alpha — the Mac window shadow was
     one hard offset rectangle, and that is all this is. */
  --shadow-panel: 2px 2px 0 var(--ink);
  --overlay-scrim: rgba(17, 17, 17, 0.55);
  /* The CRT scanline belonged to the cockpit skin. System 1 carries its
     texture in the desktop dither instead, so this reads as nothing. */
  --scanline: transparent;
}

/* ---------- reset / base ---------- */
*, *::before, *::after { box-sizing: border-box; }

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

body {
  margin: 0;
  min-height: 100%;
  background: var(--ground);
  background-image: repeating-linear-gradient(
    0deg,
    transparent 0 3px,
    var(--scanline) 3px 4px
  );
  color: var(--text-primary);
  font-family: var(--font-body);
  font-size: 14px;
  line-height: 1.5;
  -webkit-font-smoothing: antialiased;
}

/* The user-agent rule for [hidden] is a bare `display: none` on the element type,
   so any class here that sets `display` outranks it and the element stays on
   screen with the attribute set. `hidden` is the one signal that has to win, so it
   is the one place this stylesheet carries !important. */
[hidden] { display: none !important; }

a { color: inherit; text-decoration: none; }
button { font: inherit; color: inherit; cursor: pointer; }
input, select, textarea { font: inherit; color: inherit; }
h1, h2, h3, h4 { margin: 0; font-family: var(--font-display); font-weight: 600; }
p { margin: 0 0 0.75em; }

.mono { font-family: var(--font-mono); font-size: 12.5px; }
.dim { color: var(--text-dim); }

::selection { background: var(--accent-soft); color: var(--text-bright); }

/* ---------- shell layout ---------- */
.shell {
  display: flex;
  min-height: 100vh;
}

/* ---------- sidebar ---------- */
.sidebar {
  position: fixed;
  top: 0;
  left: 0;
  bottom: 0;
  width: var(--sidebar-w);
  display: flex;
  flex-direction: column;
  background: var(--panel);
  border-right: 1px solid var(--bezel);
  z-index: 40;
}

.brand {
  padding: 18px 16px 14px;
  border-bottom: 1px solid var(--bezel);
}

.brand-mark {
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 22px;
  letter-spacing: 0.22em;
  color: var(--accent);
  text-shadow: 0 0 14px var(--accent-glow);
  line-height: 1;
}

.brand-sub {
  margin-top: 6px;
  font-family: var(--font-mono);
  font-size: 10px;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--text-label);
}

.nav {
  flex: 1;
  overflow-y: auto;
  padding: 10px 0;
}

.nav-group { padding: 8px 0; }

.nav-group-label {
  padding: 4px 16px 6px;
  font-family: var(--font-mono);
  font-size: 9.5px;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--text-label);
}

.nav-item {
  position: relative;
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 8px 16px 8px 18px;
  color: var(--text-dim);
  font-family: var(--font-display);
  font-size: 13.5px;
  letter-spacing: 0.02em;
  border-left: 3px solid transparent;
  transition: color 120ms ease, background 120ms ease, border-color 120ms ease;
}

.nav-item:hover {
  color: var(--text-primary);
  background: var(--panel-raised);
}

.nav-item.active {
  color: var(--text-bright);
  border-left-color: var(--accent);
  background: linear-gradient(90deg, var(--accent-soft), transparent 70%);
  box-shadow: inset 6px 0 14px -8px var(--accent-glow);
}

.nav-item.active .nav-icon {
  color: var(--accent);
  text-shadow: 0 0 8px var(--accent-glow);
}

.nav-icon {
  width: 16px;
  text-align: center;
  font-size: 12px;
  color: var(--text-label);
}

.sidebar-foot {
  padding: 12px 16px 14px;
  border-top: 1px solid var(--bezel);
  display: flex;
  flex-direction: column;
  gap: 10px;
}

.logout-form { margin: 0; }

.logout-btn {
  width: 100%;
  padding: 6px 8px;
  background: transparent;
  border: 1px solid var(--bezel);
  border-radius: var(--radius);
  color: var(--text-dim);
  font-family: var(--font-mono);
  font-size: 11px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
}

.logout-btn:hover { color: var(--text-primary); border-color: var(--bezel-light); }

/* ---------- heartbeat ---------- */
.heartbeat {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  font-family: var(--font-mono);
  font-size: 10px;
  letter-spacing: 0.14em;
  color: var(--text-label);
}

.heartbeat-dot {
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: var(--green);
  box-shadow: 0 0 8px var(--green-glow);
  animation: pulse 2.2s ease-in-out infinite;
}

/* ---------- main column ---------- */
.main {
  flex: 1;
  min-width: 0;
  margin-left: var(--sidebar-w);
  display: flex;
  flex-direction: column;
  min-height: 100vh;
}

/* ---------- topbar ---------- */
.topbar {
  position: sticky;
  top: 0;
  z-index: 30;
  height: var(--topbar-h);
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  padding: 0 22px;
  background: color-mix(in srgb, var(--panel) 92%, transparent);
  backdrop-filter: blur(8px);
  -webkit-backdrop-filter: blur(8px);
  border-bottom: 1px solid var(--bezel);
}

.topbar-left {
  display: flex;
  align-items: baseline;
  gap: 12px;
  min-width: 0;
}

.topbar-kicker {
  font-family: var(--font-mono);
  font-size: 9.5px;
  letter-spacing: 0.2em;
  color: var(--text-label);
}

.topbar-title {
  font-size: 17px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--text-bright);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.topbar-right {
  display: flex;
  align-items: center;
  gap: 18px;
}

.refresh-indicator {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  padding: 4px 10px;
  border: 1px solid var(--bezel);
  border-radius: 999px;
  background: var(--panel-inset);
  font-family: var(--font-mono);
  font-size: 10.5px;
  letter-spacing: 0.16em;
  color: var(--teal);
}

.refresh-dot {
  width: 9px;
  height: 9px;
  border-radius: 50%;
  border: 2px solid var(--teal);
  border-top-color: transparent;
  box-shadow: 0 0 6px var(--teal-glow);
  animation: spin 1.6s linear infinite;
}

.refresh-indicator.syncing { color: var(--amber); border-color: var(--amber-soft); }
.refresh-indicator.syncing .refresh-dot {
  border-color: var(--amber);
  border-top-color: transparent;
  box-shadow: 0 0 8px var(--amber-glow);
  animation-duration: 0.6s;
}

.clock {
  display: inline-flex;
  align-items: center;
  gap: 9px;
  font-family: var(--font-mono);
  font-size: 12px;
  letter-spacing: 0.08em;
  color: var(--text-primary);
  white-space: nowrap;
}

.clock-pulse {
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: var(--green);
  box-shadow: 0 0 10px var(--green-glow);
  animation: pulse 1.4s ease-in-out infinite;
}

/* ---------- content ---------- */
.content {
  flex: 1;
  padding: 22px;
  display: flex;
  flex-direction: column;
  gap: 18px;
}

.footer {
  display: flex;
  justify-content: space-between;
  padding: 12px 22px;
  border-top: 1px solid var(--bezel);
  font-family: var(--font-mono);
  font-size: 10px;
  letter-spacing: 0.18em;
  color: var(--text-label);
  text-transform: uppercase;
}

.grid-2 {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(340px, 1fr));
  gap: 18px;
}

.stack { display: flex; flex-direction: column; gap: 10px; }

/* ---------- panels ---------- */
.panel {
  background: var(--panel);
  border: 1px solid var(--bezel);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-panel);
  overflow: hidden;
}

.panel-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  padding: 10px 16px;
  background: var(--panel-raised);
  border-bottom: 1px solid var(--bezel);
}

.panel-title {
  font-size: 12.5px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--text-bright);
}

.panel-title::before {
  content: "";
  display: inline-block;
  width: 6px;
  height: 6px;
  margin-right: 10px;
  vertical-align: middle;
  background: var(--accent);
  box-shadow: 0 0 8px var(--accent-glow);
}

.panel-meta {
  font-family: var(--font-mono);
  font-size: 10.5px;
  letter-spacing: 0.1em;
  color: var(--text-dim);
}

.panel-body { padding: 14px 16px; }

.empty {
  padding: 26px 16px;
  text-align: center;
  color: var(--text-dim);
  font-family: var(--font-mono);
  font-size: 12px;
  letter-spacing: 0.04em;
  border: 1px dashed var(--bezel);
  border-radius: var(--radius);
  background: var(--panel-inset);
}

/* ---------- instruments row ---------- */
.instruments {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(160px, 1fr));
  gap: 14px;
}

.instrument {
  position: relative;
  padding: 14px 16px;
  background: var(--panel);
  border: 1px solid var(--bezel);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-panel);
  overflow: hidden;
}

.instrument::after {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  height: 2px;
  background: var(--tone, var(--accent));
  box-shadow: 0 0 12px var(--tone-glow, var(--accent-glow));
}

.instrument.tone-accent { --tone: var(--accent); --tone-glow: var(--accent-glow); }
.instrument.tone-teal   { --tone: var(--teal);   --tone-glow: var(--teal-glow); }
.instrument.tone-green  { --tone: var(--green);  --tone-glow: var(--green-glow); }
.instrument.tone-amber  { --tone: var(--amber);  --tone-glow: var(--amber-glow); }
.instrument.tone-red    { --tone: var(--red);    --tone-glow: var(--red-glow); }
.instrument.tone-purple { --tone: var(--purple); --tone-glow: var(--purple-glow); }

.instrument-label {
  font-family: var(--font-mono);
  font-size: 10px;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--text-label);
}

.instrument-value {
  margin-top: 6px;
  font-family: var(--font-display);
  font-size: 30px;
  font-weight: 600;
  line-height: 1;
  color: var(--text-bright);
}

.instrument-unit {
  margin-top: 6px;
  font-family: var(--font-mono);
  font-size: 11px;
  color: var(--text-dim);
}

/* ---------- command bar ---------- */
.command-bar {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
}

.cmd-btn {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 7px 14px;
  background: var(--panel-raised);
  border: 1px solid var(--bezel-light);
  border-radius: var(--radius);
  color: var(--text-primary);
  font-family: var(--font-display);
  font-size: 12.5px;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  transition: background 120ms ease, border-color 120ms ease, box-shadow 120ms ease, color 120ms ease;
}

.cmd-btn:hover:not(:disabled) {
  color: var(--text-bright);
  border-color: var(--accent);
  box-shadow: 0 0 0 1px var(--accent-soft), 0 0 12px var(--accent-soft);
}

.cmd-btn:active:not(:disabled) { transform: translateY(1px); }

.cmd-btn:disabled { opacity: 0.4; cursor: not-allowed; }

.cmd-btn.primary {
  background: var(--accent);
  border-color: var(--accent);
  color: #0c0906;
  font-weight: 600;
  box-shadow: 0 0 14px var(--accent-glow);
}

.cmd-btn.primary:hover:not(:disabled) {
  color: #0c0906;
  box-shadow: 0 0 20px var(--accent-glow);
  filter: brightness(1.08);
}

/* A command that reaches out to a menu rather than to a person. Teal is the
   collection lane throughout the cockpit, so the button wears it. */
.cmd-btn.teal { border-color: var(--teal); color: var(--teal); }

.cmd-btn.teal:hover:not(:disabled) {
  border-color: var(--teal);
  color: var(--text-bright);
  box-shadow: 0 0 0 1px var(--teal-soft), 0 0 12px var(--teal-soft);
}

/* A standing statement sitting beside the buttons rather than being one. It is not
   a control -- nothing about it is clickable except the date -- so it takes the bar's
   height and none of its chrome, and it drops to its own line before it squeezes the
   navigation. */
.cmd-note {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  margin-left: auto;
  padding: 7px 0;
  min-width: 0;
}

.cmd-note-text {
  font-size: 12px;
  color: var(--text-dim);
}

@media (max-width: 760px) {
  .cmd-note { margin-left: 0; flex-basis: 100%; }
}

/* ---------- filter pills ---------- */
.filter-row {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
}

.filter-pill {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 4px 11px;
  background: var(--panel-inset);
  border: 1px solid var(--bezel);
  border-radius: 999px;
  color: var(--text-dim);
  font-family: var(--font-mono);
  font-size: 11px;
  letter-spacing: 0.06em;
  transition: all 120ms ease;
}

.filter-pill:hover { color: var(--text-primary); border-color: var(--bezel-light); }

.filter-pill.active {
  color: var(--text-bright);
  border-color: var(--teal);
  background: var(--teal-soft);
  box-shadow: 0 0 10px var(--teal-soft);
}

.pill-count {
  padding: 0 6px;
  border-radius: 999px;
  background: var(--panel-raised);
  font-size: 10px;
  color: var(--text-dim);
}

.filter-pill.active .pill-count { color: var(--teal); }

/* ---------- tables ---------- */
.table-wrap { overflow-x: auto; }

.data-table {
  width: 100%;
  border-collapse: collapse;
  font-size: 13px;
}

.data-table thead th {
  position: sticky;
  top: 0;
  padding: 9px 14px;
  text-align: left;
  background: var(--panel-raised);
  border-bottom: 1px solid var(--bezel);
  font-family: var(--font-mono);
  font-size: 10px;
  font-weight: 500;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--text-label);
  white-space: nowrap;
}

.data-table tbody td {
  padding: 10px 14px;
  border-bottom: 1px solid var(--bezel);
  vertical-align: middle;
  white-space: nowrap;
}

.data-table tbody tr:last-child td { border-bottom: none; }
.data-table tbody tr:hover td { background: var(--panel-raised); }
.data-table tbody tr[hidden] { display: none; }

.cell-name { font-weight: 500; color: var(--text-bright); }
.cell-action { color: var(--accent); }

.data-table td.empty { border: none; white-space: normal; }

/* ---------- stage badges ---------- */
.stage-badge {
  display: inline-block;
  padding: 2px 9px;
  border-radius: 999px;
  border: 1px solid var(--bezel);
  background: var(--panel-inset);
  color: var(--text-dim);
  font-family: var(--font-mono);
  font-size: 10.5px;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  white-space: nowrap;
}

.stage-queued      { color: var(--amber);  background: var(--amber-soft);  border-color: var(--amber-soft); }
.stage-contacted   { color: var(--teal);   background: var(--teal-soft);   border-color: var(--teal-soft); }
.stage-converted   { color: var(--green);  background: var(--green-soft);  border-color: var(--green-soft); box-shadow: 0 0 8px var(--green-soft); }
.stage-not-started { color: var(--text-dim); background: var(--panel-inset); border-color: var(--bezel); }
.stage-parked      { color: var(--purple); background: var(--purple-soft); border-color: var(--purple-soft); }

/* ---------- gate indicators ---------- */
.gate {
  display: inline-block;
  width: 10px;
  height: 10px;
  border-radius: 50%;
  background: var(--text-label);
  vertical-align: middle;
}

.gate-open    { background: var(--amber); box-shadow: 0 0 8px var(--amber-glow); }
.gate-pass    { background: var(--green); box-shadow: 0 0 8px var(--green-glow); }
.gate-blocked { background: var(--red);   box-shadow: 0 0 8px var(--red-glow); }
.gate-none    { background: var(--bezel-light); }

/* ---------- audit indicators, and the labelled lamp they generalise to ----------
   `.audit` came first and named itself after its first column. `.lamp` is the same
   component -- a coloured dot with a word next to it -- wearing a name that fits
   any state a cell reports. They share these rules rather than being two copies of
   them; the only difference is the dot's shape, square for audit and round for a
   lamp, which is what tells the two columns apart at a glance. */
.audit,
.lamp {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font-family: var(--font-mono);
  font-size: 11px;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--text-dim);
  white-space: nowrap;
}

.audit::before,
.lamp::before {
  content: "";
  flex-shrink: 0;
  width: 6px;
  height: 6px;
  border-radius: 1px;
  background: var(--text-label);
}

.lamp::before { border-radius: 50%; }

.audit-done::before,
.lamp-pass::before    { background: var(--green); box-shadow: 0 0 6px var(--green-glow); }
.audit-pending::before,
.lamp-open::before    { background: var(--amber); box-shadow: 0 0 6px var(--amber-glow); }
.audit-flagged::before,
.lamp-blocked::before { background: var(--red);   box-shadow: 0 0 6px var(--red-glow); }
.audit-none::before,
.lamp-none::before    { background: var(--bezel-light); }

.audit-done,    .lamp-pass    { color: var(--green); }
.audit-pending, .lamp-open    { color: var(--amber); }
.audit-flagged, .lamp-blocked { color: var(--red); }

/* ---------- action items ---------- */
.action-item {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 14px;
  padding: 10px 12px;
  margin-bottom: 8px;
  background: var(--panel-inset);
  border: 1px solid var(--bezel);
  border-left: 3px solid var(--accent);
  border-radius: var(--radius);
}

.action-item:last-child { margin-bottom: 0; }
.action-item.priority-high { border-left-color: var(--red); }
.action-item.priority-low  { border-left-color: var(--text-label); }

.action-title { color: var(--text-bright); font-weight: 500; }
.action-sub { font-size: 12px; color: var(--text-dim); }
.action-due {
  font-family: var(--font-mono);
  font-size: 11px;
  letter-spacing: 0.08em;
  color: var(--amber);
  white-space: nowrap;
}

.activity-row {
  display: flex;
  gap: 14px;
  padding: 7px 0;
  border-bottom: 1px solid var(--bezel);
}

.activity-row:last-child { border-bottom: none; }
.activity-when { color: var(--text-dim); white-space: nowrap; }

/* ---------- build rail ----------
   One unit is a spine (node + line) beside a body (card). The line is drawn per
   unit rather than once down the rail, so the last unit can simply not draw one
   and the rail ends at the last node instead of trailing past it. */
.build-rail {
  display: flex;
  flex-direction: column;
}

.build-unit {
  display: flex;
  gap: 14px;
  align-items: stretch;
}

.build-spine {
  display: flex;
  flex-direction: column;
  align-items: center;
  flex-shrink: 0;
  width: 14px;
  padding-top: 14px;
}

.build-node {
  width: 12px;
  height: 12px;
  border-radius: 50%;
  flex-shrink: 0;
  background: var(--panel);
  border: 2px solid var(--bezel-light);
}

.build-line {
  width: 2px;
  flex: 1;
  min-height: 18px;
  margin: 4px 0 0;
  background: var(--bezel-light);
}

/* The last unit closes the rail: no line past the final node. */
.build-unit:last-child .build-line { display: none; }

.build-node.queued { border-color: var(--text-dim); }
.build-node.executing {
  border-color: var(--amber);
  background: var(--amber);
  box-shadow: 0 0 10px var(--amber-glow);
}
.build-node.done {
  border-color: var(--green);
  background: var(--green);
  box-shadow: 0 0 10px var(--green-glow);
}
.build-node.active {
  border-color: var(--amber);
  background: var(--amber);
  animation: build-node-pulse 1.8s ease-in-out infinite;
}

@keyframes build-node-pulse {
  0%, 100% { box-shadow: 0 0 0 0 var(--amber-glow); }
  50%      { box-shadow: 0 0 0 5px transparent; }
}

@media (prefers-reduced-motion: reduce) {
  .build-node.active {
    animation: none;
    box-shadow: 0 0 10px var(--amber-glow);
  }
}

.build-body {
  flex: 1;
  min-width: 0;
  display: flex;
  align-items: flex-start;
  gap: 12px;
  margin-bottom: 10px;
  padding: 12px 14px;
  background: var(--panel-inset);
  border: 1px solid var(--bezel);
  border-radius: var(--radius-lg);
  transition: border-color 120ms ease;
}

.build-body:hover { border-color: var(--bezel-light); }

.build-id {
  flex-shrink: 0;
  font-family: var(--font-mono);
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.08em;
  color: var(--text-label);
  padding-top: 1px;
}

.build-info { flex: 1; min-width: 0; }

.build-name {
  font-family: var(--font-display);
  font-size: 14px;
  font-weight: 600;
  color: var(--text-bright);
}

.build-meta {
  margin-top: 3px;
  font-size: 11px;
  color: var(--text-dim);
  letter-spacing: 0.08em;
}

.build-badge {
  flex-shrink: 0;
  font-family: var(--font-mono);
  font-size: 9px;
  font-weight: 600;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  padding: 3px 7px;
  border-radius: 3px;
  white-space: nowrap;
  color: var(--text-label);
  background: var(--panel-raised);
  border: 1px solid var(--bezel);
}

.build-badge.accepted  { color: var(--green); background: var(--green-soft); border-color: transparent; }
.build-badge.review    { color: var(--accent); background: var(--accent-soft); border-color: transparent; }
.build-badge.executing { color: var(--amber); background: var(--amber-soft); border-color: transparent; }
.build-badge.queued    { color: var(--text-label); }

.build-suite-tag {
  display: inline-block;
  margin-top: 6px;
  font-family: var(--font-mono);
  font-size: 10px;
  letter-spacing: 0.06em;
  color: var(--text-dim);
}

.build-suite-tag .pass { color: var(--green); }

/* ---------- cards ---------- */
.card-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(240px, 1fr));
  gap: 12px;
}

.customer-card,
.contact-card,
.offer-card,
.decision-card {
  padding: 14px;
  background: var(--panel-inset);
  border: 1px solid var(--bezel);
  border-radius: var(--radius-lg);
  transition: border-color 120ms ease, box-shadow 120ms ease;
}

.customer-card:hover, .contact-card:hover, .offer-card:hover, .decision-card:hover {
  border-color: var(--bezel-light);
  box-shadow: 0 0 0 1px var(--accent-soft);
}

.customer-card { border-top: 2px solid var(--green); }
.contact-card { border-top: 2px solid var(--teal); }
.offer-card { border-top: 2px solid var(--purple); }
.decision-card { border-left: 3px solid var(--amber); }

.customer-name, .contact-name, .offer-name, .decision-title {
  font-family: var(--font-display);
  font-size: 15px;
  font-weight: 600;
  color: var(--text-bright);
}

.customer-plan, .contact-role, .offer-desc { margin-top: 4px; font-size: 12.5px; color: var(--text-primary); }
.customer-meta, .contact-meta { margin-top: 6px; font-size: 11px; color: var(--text-dim); }
.contact-notes { margin-top: 8px; font-size: 12.5px; color: var(--text-dim); }
.offer-price { margin-top: 6px; font-size: 20px; color: var(--accent); }

/* ---------- decision card ---------- */
.decision-card { position: relative; }

.decision-id {
  font-family: var(--font-mono);
  font-size: 10px;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--text-label);
}

.decision-title {
  display: block;
  margin-top: 3px;
  padding-right: 84px;
  font-family: var(--font-display);
  font-size: 14px;
  font-weight: 600;
  color: var(--text-bright);
}

.decision-status {
  position: absolute;
  top: 12px;
  right: 12px;
  font-family: var(--font-mono);
  font-size: 9px;
  font-weight: 600;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  padding: 3px 7px;
  border-radius: 3px;
  color: var(--text-label);
  background: var(--panel-raised);
}

.decision-status.active  { color: var(--green); background: var(--green-soft); }
.decision-status.revisit { color: var(--amber); background: var(--amber-soft); }

.decision-fields {
  display: grid;
  grid-template-columns: 1fr;
  gap: 8px;
  margin-top: 12px;
}

.df { display: flex; flex-direction: column; gap: 2px; }

.df-label {
  font-family: var(--font-mono);
  font-size: 9px;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--text-label);
}

.df-val {
  font-size: 11px;
  color: var(--text-dim);
  white-space: pre-wrap;
}

.confidence {
  align-self: flex-start;
  font-family: var(--font-mono);
  font-size: 9px;
  font-weight: 600;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  padding: 2px 6px;
  border-radius: 3px;
  color: var(--text-label);
  background: var(--panel-raised);
}

.confidence.low    { color: var(--amber); background: var(--amber-soft); }
.confidence.medium { color: var(--accent); background: var(--accent-soft); }
.confidence.high   { color: var(--green); background: var(--green-soft); }

/* The edit control sits under the status badge, out of the title's way. */
.decision-edit { position: absolute; top: 38px; right: 12px; }

/* ---------- forms ---------- */
.form { display: flex; flex-direction: column; gap: 12px; padding: 16px; }

.field { display: flex; flex-direction: column; gap: 5px; }

.field-label {
  font-family: var(--font-mono);
  font-size: 10px;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--text-label);
}

.field-input {
  width: 100%;
  padding: 8px 10px;
  background: var(--panel-inset);
  border: 1px solid var(--bezel-light);
  border-radius: var(--radius);
  color: var(--text-bright);
  font-family: var(--font-body);
  font-size: 14px;
  outline: none;
  transition: border-color 120ms ease, box-shadow 120ms ease;
}

.field-input:focus {
  border-color: var(--accent);
  box-shadow: 0 0 0 3px var(--accent-soft);
}

textarea.field-input { resize: vertical; min-height: 72px; }

.form-actions {
  display: flex;
  justify-content: flex-end;
  gap: 8px;
  padding-top: 4px;
}

/* ---------- overlays ---------- */
.overlay {
  position: fixed;
  inset: 0;
  z-index: 100;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 20px;
  background: var(--overlay-scrim);
  backdrop-filter: blur(3px);
  -webkit-backdrop-filter: blur(3px);
}

.overlay[hidden] { display: none; }

.overlay-panel {
  width: min(520px, 100%);
  max-height: calc(100vh - 40px);
  overflow-y: auto;
  background: var(--panel);
  border: 1px solid var(--bezel-light);
  border-radius: var(--radius-lg);
  box-shadow: 0 20px 60px rgba(0, 0, 0, 0.5), 0 0 0 1px var(--accent-soft);
  animation: overlay-in 140ms ease-out;
}

.overlay-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 12px 16px;
  background: var(--panel-raised);
  border-bottom: 1px solid var(--bezel);
}

.overlay-title {
  font-size: 13px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--accent);
}

.overlay-close {
  width: 28px;
  height: 28px;
  background: transparent;
  border: 1px solid var(--bezel);
  border-radius: var(--radius);
  color: var(--text-dim);
  font-size: 18px;
  line-height: 1;
}

.overlay-close:hover { color: var(--text-bright); border-color: var(--red); }

body.overlay-open { overflow: hidden; }

/* ---------- login ---------- */
.login-body {
  display: flex;
  align-items: center;
  justify-content: center;
  min-height: 100vh;
  padding: 20px;
}

.login-wrap { width: 100%; max-width: 380px; }

.login-card {
  padding: 28px 26px 22px;
  background: var(--panel);
  border: 1px solid var(--bezel-light);
  border-top: 2px solid var(--accent);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-panel), 0 0 40px var(--accent-soft);
  display: flex;
  flex-direction: column;
  gap: 14px;
}

.login-brand { text-align: center; padding-bottom: 8px; border-bottom: 1px solid var(--bezel); }

.login-title {
  font-size: 18px;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--text-bright);
}

.login-hint {
  margin: -8px 0 0;
  font-family: var(--font-mono);
  font-size: 11px;
  color: var(--text-dim);
}

.login-error {
  padding: 8px 10px;
  background: var(--red-soft);
  border: 1px solid var(--red);
  border-radius: var(--radius);
  color: var(--red);
  font-family: var(--font-mono);
  font-size: 12px;
}

.login-btn { width: 100%; justify-content: center; margin-top: 4px; }

.login-foot {
  display: flex;
  justify-content: space-between;
  align-items: center;
  margin-top: 6px;
  padding-top: 12px;
  border-top: 1px solid var(--bezel);
}

.login-foot-right {
  font-family: var(--font-mono);
  font-size: 10px;
  letter-spacing: 0.18em;
  color: var(--text-label);
}

/* ---------- mobile nav ---------- */
.mobile-nav {
  display: none;
  position: fixed;
  left: 0;
  right: 0;
  bottom: 0;
  z-index: 50;
  background: var(--panel);
  border-top: 1px solid var(--bezel);
  padding-bottom: env(safe-area-inset-bottom);
}

.mobile-nav-scroll {
  display: flex;
  gap: 4px;
  padding: 8px 10px;
  overflow-x: auto;
  -webkit-overflow-scrolling: touch;
  scrollbar-width: none;
}

.mobile-nav-scroll::-webkit-scrollbar { display: none; }

.mobile-nav-btn {
  flex: 0 0 auto;
  padding: 7px 12px;
  border: 1px solid var(--bezel);
  border-radius: 999px;
  background: var(--panel-inset);
  color: var(--text-dim);
  font-family: var(--font-display);
  font-size: 12px;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  white-space: nowrap;
}

.mobile-nav-btn.active {
  color: var(--text-bright);
  border-color: var(--accent);
  background: var(--accent-soft);
  box-shadow: 0 0 10px var(--accent-soft);
}

/* Visible to a screen reader, to nothing else. Not a utility class habit -- there
   is exactly one control on this cockpit whose label has nowhere to render (the
   attention queue's snooze select), and a select with no label is a control a
   screen reader announces as "combo box". */
.sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  overflow: hidden;
  clip: rect(0 0 0 0);
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

/* ---------- flash banner ----------
   What the last write did, carried back on the redirect. It states an outcome, so
   it is a status region: a screen reader announces it without stealing focus. */
.flash {
  padding: 9px 14px;
  border: 1px solid var(--accent-soft);
  border-left: 3px solid var(--accent);
  border-radius: var(--radius);
  background: var(--accent-soft);
  color: var(--text-bright);
  font-size: 13px;
}

/* ---------- toast ----------
   For a button that has nothing behind it yet. It says so and disappears; it never
   claims the work happened. */
.toast {
  position: fixed;
  right: 18px;
  bottom: 18px;
  z-index: 200;
  max-width: min(360px, calc(100vw - 36px));
  padding: 10px 14px;
  background: var(--panel-raised);
  border: 1px solid var(--bezel-light);
  border-left: 3px solid var(--accent);
  border-radius: var(--radius);
  box-shadow: 0 12px 32px rgba(0, 0, 0, 0.45);
  color: var(--text-bright);
  font-size: 13px;
  animation: overlay-in 140ms ease-out;
}

/* ---------- clickable rows ---------- */
.data-table tbody tr.row-open { cursor: pointer; }

.data-table tbody tr.row-open:focus-visible {
  outline: none;
  box-shadow: inset 0 0 0 1px var(--accent);
}

.data-table tbody tr.row-open:focus-visible td { background: var(--panel-raised); }

/* ---------- overlay body + wide panel ---------- */
.overlay-panel.wide { width: min(720px, 100%); }

.overlay-body { padding: 14px 16px 18px; }

.detail-strip {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 8px 14px;
  padding-bottom: 12px;
  border-bottom: 1px solid var(--bezel);
}

.detail-head {
  margin: 18px 0 8px;
  font-size: 11px;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--text-label);
}

.detail-count {
  margin-left: 6px;
  font-family: var(--font-mono);
  color: var(--text-dim);
}

.detail-grid {
  display: grid;
  grid-template-columns: 150px minmax(0, 1fr);
  gap: 6px 14px;
  margin: 0;
}

.detail-grid dt {
  font-family: var(--font-mono);
  font-size: 10.5px;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--text-label);
  padding-top: 2px;
}

.detail-grid dd {
  margin: 0;
  min-width: 0;
  color: var(--text-primary);
  overflow-wrap: anywhere;
}

.detail-link { color: var(--teal); text-decoration: underline; text-underline-offset: 2px; }
.detail-link:hover { color: var(--text-bright); }

.detail-note {
  margin: 0;
  padding: 10px 12px;
  background: var(--panel-inset);
  border: 1px solid var(--bezel);
  border-radius: var(--radius);
  color: var(--text-primary);
}

/* ---------- a chronological log inside an overlay ---------- */
.log { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 8px; }

.log-entry {
  padding: 10px 12px;
  background: var(--panel-inset);
  border: 1px solid var(--bezel);
  border-left: 2px solid var(--bezel-light);
  border-radius: var(--radius);
}

.log-meta {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 12px;
  margin-bottom: 5px;
}

.log-kind {
  font-family: var(--font-mono);
  font-size: 10.5px;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--accent);
}

.log-when { font-size: 11px; color: var(--text-dim); }

.log-body { margin: 0; color: var(--text-bright); }

.log-sub { margin: 4px 0 0; font-size: 13px; color: var(--text-primary); }

.log-tag {
  font-family: var(--font-mono);
  font-size: 10px;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--text-label);
  margin-right: 6px;
}

/* ---------- radio sets + form hints ---------- */
.radio-set { display: flex; flex-wrap: wrap; gap: 8px; }

.radio {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  padding: 6px 10px;
  background: var(--panel-inset);
  border: 1px solid var(--bezel);
  border-radius: var(--radius);
  cursor: pointer;
}

.radio:hover { border-color: var(--bezel-light); }
.radio:has(input:checked) { border-color: var(--accent); box-shadow: 0 0 0 1px var(--accent-soft); }
.radio input { accent-color: var(--accent); margin: 0; }

fieldset.field { border: 0; margin: 0; padding: 0; min-width: 0; }
fieldset.field legend { padding: 0; }

.form-hint { margin: 0; font-size: 12px; color: var(--text-dim); }

/* ---------- call sheet ---------- */
.sheet-filter {
  margin: 0 0 14px;
  padding: 9px 12px;
  background: var(--panel-inset);
  border: 1px solid var(--bezel);
  border-radius: var(--radius);
  font-size: 12.5px;
  color: var(--text-dim);
}

.sheet-list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 12px; }

.sheet-card {
  padding: 12px 14px;
  background: var(--panel-inset);
  border: 1px solid var(--bezel);
  border-left: 3px solid var(--accent);
  border-radius: var(--radius);
  break-inside: avoid;
}

.sheet-head {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 10px;
  margin-bottom: 10px;
  padding-bottom: 8px;
  border-bottom: 1px solid var(--bezel);
}

.sheet-num { color: var(--text-label); font-size: 12px; }

.sheet-name {
  font-family: var(--font-display);
  font-size: 16px;
  font-weight: 600;
  color: var(--text-bright);
}

.sheet-line { display: grid; grid-template-columns: 110px minmax(0, 1fr); gap: 10px; padding: 3px 0; }

.sheet-label {
  font-family: var(--font-mono);
  font-size: 10px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--text-label);
  padding-top: 3px;
}

.sheet-value { min-width: 0; overflow-wrap: anywhere; }

.sheet-rule { display: grid; grid-template-columns: 110px minmax(0, 1fr); gap: 10px; margin-top: 10px; }

.sheet-blank { border-bottom: 1px dashed var(--bezel-light); min-height: 26px; }

/* =========================================================
   CONTACTS
   The card is a person plus what they are owed. Warmth colours the badge only --
   the pills carry their own meaning (pain is teal, a promise is accent) so the
   temperature of the relationship never recolours the work outstanding.
   ========================================================= */

/* A contact card carries four field rows, a quote and two pill runs, so it needs
   more width than the 240px directory card the grid defaults to. */
.card-grid.wide-cards { grid-template-columns: repeat(auto-fill, minmax(330px, 1fr)); }

.contact-card { display: flex; flex-direction: column; gap: 10px; }

.contact-top {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 10px;
}

.contact-ident { min-width: 0; }

/* .contact-name and .contact-role are already declared with the other card names
   further up this sheet; only the spacing changes here, because this card sets its
   own gaps in flex rather than with per-child margins. */
.contact-card .contact-role { margin-top: 2px; font-size: 11px; color: var(--text-dim); }

.contact-rel {
  flex-shrink: 0;
  padding: 2px 8px;
  border: 1px solid;
  border-radius: 999px;
  font-family: var(--font-mono);
  font-size: 9.5px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  white-space: nowrap;
}

.contact-rel.warm { background: var(--amber-soft); border-color: var(--amber); color: var(--amber); }
.contact-rel.new  { background: var(--teal-soft);  border-color: var(--teal);  color: var(--teal); }
.contact-rel.cold { background: var(--panel-inset); border-color: var(--bezel-light); color: var(--text-dim); }

.contact-fields {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 8px 12px;
  padding: 10px 0;
  border-top: 1px solid var(--bezel);
  border-bottom: 1px solid var(--bezel);
}

.cf { min-width: 0; }

.cf-label {
  display: block;
  font-family: var(--font-mono);
  font-size: 9.5px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--text-label);
}

.cf-val {
  display: block;
  margin-top: 2px;
  font-size: 12.5px;
  color: var(--text-primary);
  overflow-wrap: anywhere;
}

/* The operator's own words. Purple across this cockpit means "somebody said this",
   which is why the quote here and the discovery panel share the colour. */
.contact-quote {
  padding: 8px 10px;
  background: var(--purple-soft);
  border-left: 2px solid var(--purple);
  border-radius: 0 var(--radius) var(--radius) 0;
  font-size: 12.5px;
  font-style: italic;
  color: var(--text-primary);
}

.contact-pills { display: flex; flex-wrap: wrap; gap: 6px; }

.contact-pill {
  padding: 3px 9px;
  border: 1px solid transparent;
  border-radius: 999px;
  font-size: 11px;
  line-height: 1.4;
}

.contact-pill.pain { background: var(--teal-soft); border-color: var(--teal-soft); color: var(--teal); }
/* The brief calls this accent-soft; the token this sheet ships is --accent-soft,
   which is the same role at the same weight. One token, not a second name for it. */
.contact-pill.promise { background: var(--accent-soft); border-color: var(--accent-soft); color: var(--accent); }

.contact-next {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 6px;
  margin-top: auto;
  padding-top: 8px;
  border-top: 1px solid var(--bezel);
  font-family: var(--font-mono);
  font-size: 11.5px;
  color: var(--text-primary);
}

.contact-next-arrow { color: var(--accent); }
.contact-next-text { min-width: 0; overflow-wrap: anywhere; }

.contact-next-src {
  font-size: 9.5px;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--text-label);
}

/* Nothing outstanding is a fact, not an empty state, so it is dimmed rather than
   dropped -- a card with no next row would read as a card that failed to load. */
.contact-next.none, .contact-next.none .contact-next-arrow { color: var(--text-dim); }

.contact-actions { display: flex; flex-wrap: wrap; align-items: center; gap: 6px; }

.cmd-btn.small { padding: 3px 9px; font-size: 11px; }
.cmd-btn.purple { border-color: var(--purple); color: var(--purple); }
.cmd-btn.purple:hover { background: var(--purple-soft); box-shadow: 0 0 0 1px var(--purple-soft); }
.cmd-btn.danger { border-color: var(--red); color: var(--red); }
.cmd-btn.danger:hover:not(:disabled) { background: var(--red-soft); box-shadow: 0 0 0 1px var(--red-soft); }

/* A command-bar action that POSTs needs a form around its button. `display:
   contents` lets the button sit in the bar's flex row directly, so a posting
   action and a link-shaped one line up the same way. */
.inline-form { display: contents; }

/* Suite counts: three narrow fields on one line, stacking on a phone. */
.field-row {
  display: flex;
  flex-wrap: wrap;
  gap: 10px;
}
.field-row .field { flex: 1; min-width: 90px; }


/* =========================================================
   DISCOVERY
   A record is quotes plus workflows plus how it landed. The quote styling matches
   .contact-quote on purpose: the same purple means the same thing on both pages.
   ========================================================= */

.disc-link {
  color: var(--purple);
  text-decoration: underline;
  text-underline-offset: 2px;
  cursor: pointer;
}

.disc-link:hover { color: var(--text-bright); text-decoration-color: var(--purple); }
.disc-link:focus-visible { outline: 1px solid var(--purple); outline-offset: 2px; }

/* The panel a discovery record opens in. Purple top edge fading out to the right,
   so the overlay is identifiable as a discovery record before it is read. */
.overlay-panel.purple {
  border-top: 2px solid var(--purple);
  box-shadow: 0 20px 60px rgba(0, 0, 0, 0.5), 0 0 0 1px var(--purple-soft);
}

.overlay-panel.purple::before {
  content: "";
  display: block;
  height: 2px;
  margin-top: -2px;
  background: linear-gradient(90deg, var(--purple), transparent 70%);
}

.overlay-panel.purple .overlay-title { color: var(--purple); }

.disc-list { display: flex; flex-direction: column; gap: 14px; }

.disc-subject {
  font-family: var(--font-display);
  font-size: 13px;
  font-weight: 600;
  letter-spacing: 0.06em;
  color: var(--text-bright);
}

.disc-record {
  padding: 12px 14px;
  background: var(--panel-inset);
  border: 1px solid var(--bezel);
  border-left: 3px solid var(--purple);
  border-radius: var(--radius);
}

.disc-head {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 6px 12px;
  padding-bottom: 8px;
  border-bottom: 1px solid var(--bezel);
}

.disc-kind {
  font-family: var(--font-mono);
  font-size: 10px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--purple);
}

.disc-when { color: var(--text-dim); }
.disc-who { font-size: 12.5px; color: var(--text-primary); }

.disc-section { margin-top: 12px; }

.disc-section-head {
  margin: 0 0 8px;
  font-size: 10px;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--text-label);
}

.disc-quote {
  margin-bottom: 6px;
  padding: 8px 10px;
  background: var(--purple-soft);
  border-left: 2px solid var(--purple);
  border-radius: 0 var(--radius) var(--radius) 0;
  font-size: 12.5px;
  font-style: italic;
  color: var(--text-primary);
}

.disc-wf {
  margin-bottom: 10px;
  padding: 10px 12px;
  background: var(--panel);
  border: 1px solid var(--bezel);
  border-radius: var(--radius);
}

.disc-wf-name {
  margin-bottom: 6px;
  font-family: var(--font-display);
  font-size: 13px;
  font-weight: 700;
  color: var(--text-bright);
}

.disc-row {
  display: grid;
  grid-template-columns: 140px minmax(0, 1fr);
  gap: 10px;
  padding: 3px 0;
}

.disc-key {
  padding-top: 3px;
  font-family: var(--font-mono);
  font-size: 9.5px;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--text-label);
}

.disc-val { min-width: 0; font-size: 12.5px; color: var(--text-primary); overflow-wrap: anywhere; }

.overlap-line { display: flex; align-items: baseline; justify-content: space-between; gap: 10px; }
.overlap-desk { min-width: 0; }
.overlap-pct { color: var(--teal); }

.overlap-bar {
  display: block;
  height: 5px;
  margin-top: 5px;
  background: var(--panel-inset);
  border: 1px solid var(--bezel);
  border-radius: 999px;
  overflow: hidden;
}

.overlap-fill {
  display: block;
  height: 100%;
  background: var(--teal);
  box-shadow: 0 0 8px var(--teal-glow);
}

.disc-foot {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 10px;
  margin-top: 12px;
  padding-top: 10px;
  border-top: 1px solid var(--bezel);
}

.disc-foot-cell { min-width: 0; }

.disc-foot-label {
  display: block;
  font-family: var(--font-mono);
  font-size: 9.5px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--text-label);
}

.disc-foot-val {
  display: block;
  margin-top: 3px;
  font-size: 12.5px;
  color: var(--text-primary);
  overflow-wrap: anywhere;
}

/* Only the reaction recorded as Strong goes green. Colouring the others would be
   the page upgrading a judgement the founder made. */
.disc-foot-val.strong { color: var(--green); }
.disc-foot-val.next { color: var(--accent); }


/* ---------- the repeatable groups on the log form ---------- */
.repeat-group { display: flex; flex-direction: column; gap: 8px; }
.repeat-slots { display: flex; flex-direction: column; gap: 8px; }
.repeat-group > .cmd-btn { align-self: flex-start; }

.wf-slot {
  display: flex;
  flex-direction: column;
  gap: 6px;
  padding: 10px;
  background: var(--panel-inset);
  border: 1px solid var(--bezel);
  border-left: 2px solid var(--teal);
  border-radius: var(--radius);
}

.wf-pair { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 6px; }

.wf-slider { display: flex; flex-direction: column; gap: 4px; justify-content: center; }
.wf-slider input[type="range"] { width: 100%; accent-color: var(--teal); }
.wf-out { color: var(--teal); }

/* ---------- print ----------
   Paper carries the sheet and nothing else: no chrome, no scanlines, and a flat
   white ground so a full-bleed dark fill does not turn every page into a bitmap. */
@media print {
  .no-print, .sidebar, .topbar, .mobile-nav, .footer, .overlay { display: none !important; }

  body {
    background: #fff !important;
    background-image: none !important;
    color: #000;
  }

  .shell, .main { display: block; margin: 0; }
  .content { padding: 0; }

  .panel, .sheet-card, .sheet-filter {
    background: #fff !important;
    border-color: #999 !important;
    box-shadow: none !important;
  }

  .panel-head { background: #fff !important; border-bottom: 1px solid #000; }
  .panel-title, .sheet-name, .sheet-value, .log-body { color: #000 !important; }
  .panel-meta, .sheet-label, .sheet-num, .sheet-filter, .dim { color: #444 !important; }
  .cell-action { color: #000 !important; }
  .contact-card, .disc-record, .disc-wf, .disc-quote, .contact-quote {
    background: #fff !important;
    border-color: #999 !important;
  }
  .disc-val, .cf-val, .disc-foot-val, .contact-next { color: #000 !important; }
  .overlap-fill { background: #666 !important; box-shadow: none !important; }
  .stage-badge { border-color: #999 !important; background: #fff !important; color: #000 !important; }
  .sheet-card { border-left-width: 2px; }
}

/* ---------- responsive ---------- */
@media (max-width: 860px) {
  .sidebar { display: none; }
  .main { margin-left: 0; }
  .mobile-nav { display: block; }
  .content { padding: 16px 14px 86px; }
  .footer { padding-bottom: 72px; }
  .topbar { padding: 0 14px; }
  .topbar-kicker { display: none; }
  .clock-text { font-size: 11px; }
  .refresh-indicator { padding: 3px 8px; }
}

@media (max-width: 500px) {
  .instruments { grid-template-columns: 1fr; }
  .grid-2 { grid-template-columns: 1fr; }
  /* A 150px label column leaves nothing for the value on a phone. Stack instead. */
  .detail-grid { grid-template-columns: 1fr; gap: 2px 0; }
  .detail-grid dd { margin-bottom: 8px; }
  .sheet-line, .sheet-rule { grid-template-columns: 1fr; gap: 2px; }
  .contact-fields { grid-template-columns: 1fr; }
  .disc-row { grid-template-columns: 1fr; gap: 2px; }
  .disc-key { padding-top: 0; }
  .disc-foot { grid-template-columns: 1fr; }
  .wf-pair { grid-template-columns: 1fr; }
  .toast { right: 12px; left: 12px; bottom: 84px; max-width: none; }
  .topbar-right { gap: 10px; }
  .refresh-label { display: none; }
  .topbar-title { font-size: 15px; }
}

/* ---------- motion ---------- */
@keyframes spin { to { transform: rotate(360deg); } }

@keyframes pulse {
  0%, 100% { opacity: 1; transform: scale(1); }
  50% { opacity: 0.45; transform: scale(0.8); }
}

@keyframes overlay-in {
  from { opacity: 0; transform: translateY(8px) scale(0.98); }
  to { opacity: 1; transform: none; }
}

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

/* =========================================================
   DASHBOARD — the morning view
   -------------------------------------------------------
   Scoped under `.dash` on purpose. Two of these names are
   already taken elsewhere in this file and mean something
   else: `.heartbeat` is the sidebar's SYSTEM NOMINAL badge
   (line ~301) and `.action-item` is the calendar's row
   (line ~795). Scoping is what lets the dashboard use the
   names it wants without reaching into either.
   ========================================================= */

/* ---------- generic, reusable (not scoped) ---------- */
.panel-label {
  display: flex;
  align-items: center;
  gap: 8px;
  font-family: var(--font-mono);
  font-size: 10px;
  font-weight: 600;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--text-label);
  margin-bottom: 12px;
}

.badge {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 18px;
  height: 18px;
  padding: 0 6px;
  border-radius: 9px;
  background: var(--accent-soft);
  border: 1px solid var(--accent-soft);
  color: var(--accent);
  font-family: var(--font-mono);
  font-size: 10px;
  font-weight: 600;
  letter-spacing: 0.04em;
}

.badge-quiet {
  background: var(--panel-inset);
  border-color: var(--bezel);
  color: var(--text-dim);
}

/* ---------- morning greeting ---------- */
.dash { display: flex; flex-direction: column; gap: 18px; }

/* NOTHING ON THIS PAGE IS SQUASHED TO FIT.
   The shell is three nested flex columns -- .main, then .content, then .dash --
   and a flex item defaults to flex-shrink: 1. On a narrow viewport, where a
   section grows tall enough to matter, that default let the shell compress a
   panel below its content: the five pre-pilot gate rows collapsed on top of each
   other at 500px and read as four overlapping copies of the word QUEUED.

   It fails silently, and it fails worst exactly where the founder is least able to
   argue with it -- on the phone. Stated on the containers rather than per panel,
   so a section added later is upright by construction. */
.content > *,
.dash > * { flex-shrink: 0; }

.morning { margin-bottom: 2px; }

.morning-day {
  font-family: var(--font-display);
  font-size: 28px;
  font-weight: 700;
  line-height: 1.1;
  letter-spacing: 0.02em;
  text-transform: uppercase;
  color: var(--text-bright);
}

.morning-date {
  margin-top: 4px;
  font-family: var(--font-mono);
  font-size: 11px;
  letter-spacing: 0.1em;
  color: var(--text-dim);
}

/* ---------- needs you ---------- */
.dash .needs-you {
  background: var(--panel);
  border: 1px solid var(--bezel);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-panel);
  padding: 16px;
}

/* ---------- the attention queue's item ----------
   The queue's rows are the shared card (templates/macros/card.html, `.gims-card`),
   which is where the stripe, the truncation and the phone stack now live. What
   stood here was a private copy of all three -- correct, and the fourth surface in
   this cockpit to answer "title, chips, meta, buttons" its own way.

   What is left below is only what the queue adds to a card: the snooze control,
   which is a select inside a button row and belongs to nothing else. */

.dash .aq-snooze { gap: 4px; }

.dash .aq-days {
  padding: 3px 4px;
  border: 1px solid var(--bezel-light);
  border-radius: var(--radius);
  background: var(--panel);
  color: var(--text-dim);
  font-family: var(--font-mono);
  font-size: 10px;
}

.dash .aq-foot {
  margin-top: 10px;
  font-family: var(--font-mono);
  font-size: 10px;
  letter-spacing: 0.06em;
  color: var(--text-label);
}

/* On a phone the card stacks its own rows and makes its buttons full width -- that
   is `.gims-card`'s job now, at the one mobile boundary. The snooze form is the only
   thing left to say: it holds a select as well as a button, so it takes more of the
   row than the DONE beside it. */
@media (max-width: 860px) {
  .dash .aq-snooze { flex: 1.4; }
}

.dash .action-clear {
  padding: 10px;
  text-align: center;
  font-family: var(--font-mono);
  font-size: 11px;
  letter-spacing: 0.08em;
  color: var(--green);
  opacity: 0.7;
}

/* ---------- instruments ---------- */
.dash .instruments-row {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 12px;
}

.dash .inst {
  position: relative;
  overflow: hidden;
  padding: 14px 16px;
  background: var(--panel);
  border: 1px solid var(--bezel);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-panel);
}

.dash .inst::after {
  content: "";
  position: absolute;
  inset: 0 0 auto 0;
  height: 2px;
  background: var(--tone, var(--accent));
  box-shadow: 0 0 10px var(--tone-glow, var(--accent-glow));
}

.dash .inst.tone-accent { --tone: var(--accent); --tone-glow: var(--accent-glow); }
.dash .inst.tone-teal   { --tone: var(--teal);   --tone-glow: var(--teal-glow); }
.dash .inst.tone-green  { --tone: var(--green);  --tone-glow: var(--green-glow); }
.dash .inst.tone-amber  { --tone: var(--amber);  --tone-glow: var(--amber-glow); }
.dash .inst.tone-red    { --tone: var(--red);    --tone-glow: var(--red-glow); }
.dash .inst.tone-purple { --tone: var(--purple); --tone-glow: var(--purple-glow); }

.dash .inst-label {
  font-family: var(--font-mono);
  font-size: 10px;
  font-weight: 600;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--text-label);
}

.dash .inst-val {
  margin: 6px 0 2px;
  font-family: var(--font-display);
  font-size: 26px;
  font-weight: 700;
  line-height: 1;
  color: var(--text-bright);
}

.dash .inst-sub {
  font-family: var(--font-mono);
  font-size: 10.5px;
  letter-spacing: 0.04em;
  color: var(--tone, var(--text-dim));
  opacity: 0.85;
}

/* ---------- runtime heartbeat ---------- */
.dash .heartbeat {
  display: block;
  padding: 14px 16px;
  background: var(--panel);
  border: 1px solid var(--bezel);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-panel);
  font-family: inherit;
  font-size: inherit;
  letter-spacing: normal;
  color: var(--text-primary);
}

.dash .hb-row {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 5px 0;
  border-bottom: 1px solid var(--bezel);
}

.dash .hb-row:last-of-type { border-bottom: 0; }

.dash .hb-led {
  flex-shrink: 0;
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: var(--text-label);
}

.dash .hb-led.ok   { background: var(--green); box-shadow: 0 0 8px var(--green-glow); }
.dash .hb-led.warn { background: var(--amber); box-shadow: 0 0 8px var(--amber-glow); }
.dash .hb-led.err  { background: var(--red);   box-shadow: 0 0 8px var(--red-glow); }

.dash .hb-name { flex: 1; font-size: 12.5px; color: var(--text-primary); }

.dash .hb-status {
  font-family: var(--font-mono);
  font-size: 10.5px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--text-dim);
}

.dash .hb-row:has(.hb-led.warn) .hb-status { color: var(--amber); }
.dash .hb-row:has(.hb-led.err) .hb-status  { color: var(--red); }

.dash .hb-footer {
  margin-top: 10px;
  padding-top: 10px;
  border-top: 1px solid var(--bezel);
  font-family: var(--font-mono);
  font-size: 10px;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--text-label);
}

/* ---------- split panels ---------- */
.dash .split {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 12px;
  align-items: start;
}

.dash .split-panel {
  padding: 14px 16px;
  background: var(--panel);
  border: 1px solid var(--bezel);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-panel);
}

/* --- pipeline snapshot --- */
.dash .snap-row {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 5px 0;
}

.dash .snap-label {
  flex-shrink: 0;
  width: 96px;
  font-family: var(--font-mono);
  font-size: 10.5px;
  letter-spacing: 0.06em;
  color: var(--text-dim);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.dash .snap-track {
  flex: 1;
  height: 8px;
  border-radius: 4px;
  background: var(--panel-inset);
  border: 1px solid var(--bezel);
  overflow: hidden;
}

/* The base colour covers every stage in the enum, including the ones the stage
   palette has no entry for. A stage with a colour overrides it below. */
.dash .snap-bar {
  display: block;
  height: 100%;
  min-width: 2px;
  border-radius: 4px;
  background: var(--text-label);
}

.dash .snap-bar.stage-not-started { background: var(--text-dim); }
.dash .snap-bar.stage-queued      { background: var(--amber);  box-shadow: 0 0 8px var(--amber-glow); }
.dash .snap-bar.stage-contacted   { background: var(--teal);   box-shadow: 0 0 8px var(--teal-glow); }
.dash .snap-bar.stage-converted   { background: var(--green);  box-shadow: 0 0 8px var(--green-glow); }
.dash .snap-bar.stage-parked      { background: var(--purple); box-shadow: 0 0 8px var(--purple-glow); }

.dash .snap-count {
  flex-shrink: 0;
  width: 24px;
  text-align: right;
  font-family: var(--font-mono);
  font-size: 11px;
  font-weight: 600;
  color: var(--text-bright);
}

/* --- event ledger --- */
.dash .log-scroll {
  position: relative;
  max-height: 280px;
  overflow-y: auto;
  scrollbar-width: thin;
}

.dash .log-row {
  display: flex;
  gap: 10px;
  padding: 5px 0;
  border-bottom: 1px solid var(--bezel);
  font-size: 12px;
}

.dash .log-row:last-of-type { border-bottom: 0; }

.dash .log-ts {
  flex-shrink: 0;
  width: 48px;
  font-family: var(--font-mono);
  font-size: 10.5px;
  letter-spacing: 0.04em;
  color: var(--text-label);
  padding-top: 1px;
}

.dash .log-text { flex: 1; min-width: 0; color: var(--text-dim); }
.dash .log-text strong { color: var(--text-bright); font-weight: 600; }

/* Sticky rather than absolute so it pins to the bottom of the scroll port without
   a wrapper element; the negative margin keeps it from adding a row of height. */
.dash .log-fade {
  position: sticky;
  bottom: 0;
  height: 40px;
  margin-top: -40px;
  pointer-events: none;
  background: linear-gradient(to bottom, transparent, var(--panel));
}

/* ---------- narrow ---------- */
@media (max-width: 1000px) {
  .dash .instruments-row { grid-template-columns: repeat(2, 1fr); }
  .dash .split { grid-template-columns: 1fr; }
}

@media (max-width: 560px) {
  .morning-day { font-size: 22px; }
  .dash .instruments-row { grid-template-columns: 1fr; }
  /* The source chip is the first thing to go on the narrowest phone: the
     summary already names the subject, and the stripe already carries the
     rank. What is lost is which view the row lives in, which the link
     still answers on tap. */
  .dash .gims-card-chips { display: none; }
  .dash .snap-label { width: 76px; }
}

/* =========================================================
   MOVE ROWS — the controls that close a Needs You item
   -------------------------------------------------------
   One shape shared by four pages (build unit status, decision
   status, customer health, promise outcome). They are all the
   same gesture: here is where this stands, here is where it
   may go. Sharing the row means the gesture reads the same
   wherever the founder meets it.
   ========================================================= */
.move-row {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 6px;
  margin-top: 10px;
  padding-top: 10px;
  border-top: 1px solid var(--bezel);
}

/* Names which record this row acts on, where a card carries more than one of
   them (a person can hold several open promises). */
.move-label {
  flex: 1;
  min-width: 0;
  font-family: var(--font-mono);
  font-size: 10.5px;
  letter-spacing: 0.04em;
  color: var(--text-dim);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* The value the record already carries. Marked rather than hidden, so the row
   shows the current state and the alternatives in one line -- and it stays
   clickable, because the handler refuses a no-op write rather than the button
   pretending the state is unreachable. */
.cmd-btn.current {
  border-color: var(--accent);
  color: var(--accent);
  background: var(--accent-soft);
  cursor: default;
}

/* =========================================================
   CUSTOMERS · OFFER · FINANCIALS
   Three views, two shared shapes.

   .readout-grid is the flexible cousin of .instruments: the
   instrument row carries four short scalars on the Dashboard,
   a readout carries a value that may be a whole sentence
   ("Free Pilot — live data, real reports, no commitment").
   Same tone rail, different type scale and wrapping.

   .cust-detail is an inline panel rather than an overlay,
   because the question this page answers is "how is this one
   doing next to the others" and an overlay hides the others.
   ========================================================= */

/* ---------- readouts (offer + financials) ---------- */
.readout-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
  gap: 12px;
}

.readout {
  position: relative;
  padding: 14px 16px;
  background: var(--panel-inset);
  border: 1px solid var(--bezel);
  border-radius: var(--radius-lg);
  overflow: hidden;
}

.readout::after {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  height: 2px;
  background: var(--tone, var(--accent));
  box-shadow: 0 0 12px var(--tone-glow, var(--accent-glow));
}

.readout.tone-accent { --tone: var(--accent); --tone-glow: var(--accent-glow); }
.readout.tone-teal   { --tone: var(--teal);   --tone-glow: var(--teal-glow); }
.readout.tone-green  { --tone: var(--green);  --tone-glow: var(--green-glow); }
.readout.tone-amber  { --tone: var(--amber);  --tone-glow: var(--amber-glow); }
.readout.tone-red    { --tone: var(--red);    --tone-glow: var(--red-glow); }
.readout.tone-purple { --tone: var(--purple); --tone-glow: var(--purple-glow); }

.readout-label {
  font-family: var(--font-mono);
  font-size: 10px;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--text-label);
}

/* A readout value is a price on one card and a sentence on the next, so it wraps
   and its size is the size a sentence can hold, not the Dashboard's 30px. */
.readout-value {
  margin-top: 6px;
  font-family: var(--font-display);
  font-size: 19px;
  font-weight: 600;
  line-height: 1.25;
  color: var(--text-bright);
}

.readout-note {
  margin-top: 6px;
  font-family: var(--font-mono);
  font-size: 11px;
  line-height: 1.45;
  color: var(--text-dim);
}

/* ---------- workflow signals (offer) ---------- */
.wf-signal-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
  gap: 12px;
}

.wf-signal {
  padding: 12px 14px;
  background: var(--panel-inset);
  border: 1px solid var(--bezel);
  border-left: 3px solid var(--teal);
  border-radius: var(--radius-lg);
}

/* The amber edge is the whole message: operators named this and nothing here
   does it yet. One declaration, so served and unserved cannot drift apart. */
.wf-signal.unserved { border-left-color: var(--amber); }

.wf-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 10px;
}

.wf-name {
  font-family: var(--font-display);
  font-size: 13px;
  font-weight: 600;
  color: var(--text-bright);
}

.wf-count {
  flex-shrink: 0;
  font-size: 13px;
  font-weight: 600;
  color: var(--teal);
}

.wf-signal.unserved .wf-count { color: var(--amber); }

.wf-details {
  margin: 9px 0 0;
  padding: 0;
  list-style: none;
  display: flex;
  flex-direction: column;
  gap: 5px;
}

.wf-details li {
  display: flex;
  align-items: baseline;
  gap: 8px;
  font-size: 12px;
  line-height: 1.45;
  color: var(--text-dim);
}

.wf-dot {
  flex-shrink: 0;
  width: 4px;
  height: 4px;
  border-radius: 50%;
  background: var(--teal);
  box-shadow: 0 0 6px var(--teal-glow);
}

.wf-signal.unserved .wf-dot {
  background: var(--amber);
  box-shadow: 0 0 6px var(--amber-glow);
}

/* The unserved count wears the colour of the panel it heads, so the two
   `.panel-label` rows are told apart at a glance rather than by reading. */
.badge.amber {
  background: var(--amber-soft);
  border-color: var(--amber-soft);
  color: var(--amber);
}

/* ---------- customer grid ---------- */
.cust-grid {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: 12px;
}

@media (max-width: 1000px) {
  .cust-grid { grid-template-columns: 1fr; }
}

.cust-card {
  display: block;
  padding: 14px;
  background: var(--panel-inset);
  border: 1px solid var(--bezel);
  border-top: 2px solid var(--health, var(--text-label));
  border-radius: var(--radius-lg);
  color: inherit;
  transition: border-color 120ms ease, box-shadow 120ms ease;
}

/* The top edge is the card's largest patch of colour, so it carries the same
   fact as the badge. A green edge over a COLD badge is the card arguing with
   itself, and the edge is what gets read first from across the grid. */
.cust-card.good  { --health: var(--green); }
.cust-card.watch { --health: var(--amber); }
.cust-card.cold  { --health: var(--red); }

.cust-card:hover {
  border-color: var(--bezel-light);
  border-top-color: var(--health, var(--text-label));
  box-shadow: 0 0 0 1px var(--accent-soft);
}

/* The open card and the panel under it are one object, so the card keeps an
   accent ring for as long as its detail is showing. */
.cust-card.open {
  border-color: var(--accent);
  border-top-color: var(--health, var(--accent));
  box-shadow: 0 0 0 1px var(--accent-soft), 0 0 16px var(--accent-soft);
}

.cust-header {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 10px;
}

.cust-name {
  font-family: var(--font-display);
  font-size: 15px;
  font-weight: 600;
  color: var(--text-bright);
}

.cust-health {
  flex-shrink: 0;
  padding: 3px 7px;
  border-radius: 3px;
  font-family: var(--font-mono);
  font-size: 9px;
  font-weight: 600;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--text-label);
  background: var(--panel-raised);
}

.cust-health.good  { color: var(--green); background: var(--green-soft); }
.cust-health.watch { color: var(--amber); background: var(--amber-soft); }
.cust-health.cold  { color: var(--red);   background: var(--red-soft); }

.cust-stats {
  display: flex;
  flex-wrap: wrap;
  gap: 18px;
  margin-top: 11px;
}

.cust-stat { display: flex; flex-direction: column; gap: 2px; }

.cust-stat-label {
  font-family: var(--font-mono);
  font-size: 9px;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--text-label);
}

.cust-stat-val { font-size: 13px; color: var(--text-primary); }

.cust-desks {
  display: flex;
  flex-wrap: wrap;
  gap: 5px;
  margin-top: 12px;
}

.cust-desk-pill {
  padding: 3px 8px;
  border: 1px solid var(--teal-soft);
  border-radius: 10px;
  background: var(--teal-soft);
  font-family: var(--font-mono);
  font-size: 10px;
  letter-spacing: 0.04em;
  color: var(--teal);
}

/* A customer running no desk is not a customer with no pills; it is a customer
   nothing is being done for, and the card has to say which. */
.cust-desk-pill.none {
  border-color: var(--bezel);
  background: transparent;
  color: var(--text-label);
}

/* ---------- customer detail ---------- */
.cust-detail {
  margin-top: 14px;
  padding: 16px;
  background: var(--panel-raised);
  border: 1px solid var(--accent-soft);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-panel);
}

.cust-detail-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  padding-bottom: 12px;
  border-bottom: 1px solid var(--bezel);
}

.cust-detail-name {
  margin: 0;
  font-family: var(--font-display);
  font-size: 17px;
  font-weight: 600;
  color: var(--text-bright);
}

.cust-close {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 26px;
  height: 26px;
  flex-shrink: 0;
  border: 1px solid var(--bezel-light);
  border-radius: var(--radius);
  background: var(--panel-inset);
  font-size: 16px;
  line-height: 1;
  color: var(--text-dim);
  transition: color 120ms ease, border-color 120ms ease;
}

.cust-close:hover { color: var(--text-bright); border-color: var(--accent); }

.cust-detail-grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 12px;
  margin-top: 14px;
}

@media (max-width: 720px) {
  .cust-detail-grid { grid-template-columns: repeat(2, 1fr); }
}

.cust-detail-cell {
  display: flex;
  flex-direction: column;
  gap: 3px;
  padding: 9px 11px;
  background: var(--panel-inset);
  border: 1px solid var(--bezel);
  border-radius: var(--radius);
}

.cust-detail-label {
  font-family: var(--font-mono);
  font-size: 9px;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--text-label);
}

.cust-detail-val { font-size: 13px; color: var(--text-bright); }

/* ---------- customer tabs ---------- */
.cust-tabs {
  display: flex;
  flex-wrap: wrap;
  gap: 2px;
  margin-top: 16px;
  border-bottom: 1px solid var(--bezel);
}

.cust-tab {
  padding: 7px 13px;
  border: 1px solid transparent;
  border-bottom: 0;
  border-radius: var(--radius) var(--radius) 0 0;
  margin-bottom: -1px;
  font-family: var(--font-display);
  font-size: 12px;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  color: var(--text-dim);
  transition: color 120ms ease, background 120ms ease;
}

.cust-tab:hover { color: var(--text-bright); background: var(--panel-inset); }

.cust-tab.active {
  color: var(--accent);
  background: var(--panel-inset);
  border-color: var(--bezel);
  border-bottom-color: var(--panel-inset);
}

.cust-tab-body { padding-top: 14px; }

/* ---------- desk / store rows ---------- */
.desk-row {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 8px 11px;
  background: var(--panel-inset);
  border: 1px solid var(--bezel);
  border-radius: var(--radius);
  font-size: 12.5px;
}

.desk-led {
  flex-shrink: 0;
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: var(--text-label);
}

.desk-led.active {
  background: var(--green);
  box-shadow: 0 0 8px var(--green-glow);
}

/* Paused is dim and steady, not off. A desk that exists and is not running is a
   different fact from a desk that does not exist. */
.desk-led.paused { background: var(--amber); opacity: 0.55; }

.desk-name { flex: 1; min-width: 0; color: var(--text-bright); }

.desk-cadence {
  flex-shrink: 0;
  font-family: var(--font-mono);
  font-size: 11px;
  color: var(--text-dim);
}

.desk-last {
  flex-shrink: 0;
  min-width: 58px;
  text-align: right;
  font-size: 11px;
  color: var(--text-label);
}

@media (max-width: 620px) {
  .desk-row { flex-wrap: wrap; }
  .desk-name { flex-basis: 100%; }
}

/* ---------- customer log rows ----------
   Unscoped `.log-row`, unlike the Dashboard's `.dash .log-row`. They are
   different rows on different pages, and that scoping is what keeps the two
   from colliding -- see the note at the head of the dashboard block. */
.log-row {
  display: flex;
  gap: 12px;
  padding: 8px 11px;
  background: var(--panel-inset);
  border: 1px solid var(--bezel);
  border-radius: var(--radius);
  font-size: 12.5px;
}

.log-row-when {
  flex-shrink: 0;
  width: 52px;
  padding-top: 2px;
  font-size: 10.5px;
  letter-spacing: 0.04em;
  color: var(--text-label);
}

.log-row-text { flex: 1; min-width: 0; line-height: 1.5; color: var(--text-primary); }

.cust-log-form {
  display: flex;
  gap: 8px;
  margin-bottom: 12px;
}

.cust-log-form .field-input { flex: 1; min-width: 0; }

/* =========================================================
   MENU AUDIT
   The teal capability: the Pipeline column indicator, the
   audit panel, its class bar, and the finding table.

   Teal rather than accent because an audit is the one thing
   on this board that is about the OPERATOR'S data rather
   than about our relationship with them, and the colour is
   what says so before the panel is read.
   ========================================================= */

/* ---------- the Pipeline column indicator ----------
   `.audit-ind` is a button where an audit exists and a span where none does,
   because one of those is a thing you can open and the other is not. It carries
   its own colours rather than joining `.audit` above: that component is a lamp
   reporting a state, this one is a link to a panel. */
.audit-ind {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 0;
  background: none;
  border: none;
  font-family: var(--font-mono);
  font-size: 11px;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  white-space: nowrap;
  color: var(--text-dim);
}

button.audit-ind {
  cursor: pointer;
  border-bottom: 1px solid transparent;
}

.audit-ind::before {
  content: "";
  flex-shrink: 0;
  width: 6px;
  height: 6px;
  border-radius: 1px;
  background: var(--text-label);
}

.audit-ind.done {
  color: var(--teal);
}

.audit-ind.done::before {
  background: var(--teal);
  box-shadow: 0 0 6px var(--teal-glow);
}

/* An audit that ran and found nothing. A different fact from no audit, and the
   column would lie if the two shared a colour. */
.audit-ind.clean {
  color: var(--green);
}

.audit-ind.clean::before {
  background: var(--green);
  box-shadow: 0 0 6px var(--green-glow);
}

.audit-ind.none {
  color: var(--text-dim);
}

/* No audit, nothing to open, so nothing that looks openable. */
.audit-ind.none::before { display: none; }

button.audit-ind:hover { border-bottom-color: currentColor; }
button.audit-ind:focus-visible { outline: 1px solid var(--teal); outline-offset: 3px; }


/* ---------- the panel ----------
   Teal top edge fading right, the same shape the discovery panel wears in
   purple, so a panel is identifiable before it is read. */
.overlay-panel.teal {
  border-top: 2px solid var(--teal);
  box-shadow: 0 20px 60px rgba(0, 0, 0, 0.5), 0 0 0 1px var(--teal-soft);
}

.overlay-panel.teal::before {
  content: "";
  display: block;
  height: 2px;
  margin-top: -2px;
  background: linear-gradient(90deg, var(--teal), transparent 70%);
}

.overlay-panel.teal .overlay-title { color: var(--teal); }


/* ---------- the summary line ----------
   The two numbers the audit is accountable for, in a sentence rather than a
   pair of tiles. A tile invites comparison between the numbers; the sentence
   says what each one counts. */
.overlay-summary {
  margin: 0 0 8px;
  font-size: 14px;
  line-height: 1.6;
  color: var(--text-primary);
}

.overlay-summary strong {
  font-family: var(--font-mono);
  font-weight: 600;
  color: var(--text-bright);
}

.overlay-meta {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 8px;
  margin: 0 0 14px;
  font-family: var(--font-mono);
  font-size: 11px;
  letter-spacing: 0.04em;
  color: var(--text-dim);
}

.overlay-meta .mono {
  overflow: hidden;
  max-width: 100%;
  font-size: 11px;
  text-overflow: ellipsis;
  white-space: nowrap;
}


/* ---------- the class bar ----------
   One pill per class that turned something up. A class with nothing in it does
   not get a pill: a zero on a coloured pill reads as a category, and the
   summary line above already says how many listings were read. */
.audit-class-bar {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  margin-bottom: 14px;
}

.audit-class-pill {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 4px 9px;
  border: 1px solid transparent;
  border-radius: 999px;
  font-family: var(--font-mono);
  font-size: 11px;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  white-space: nowrap;
}

.audit-class-pill strong {
  font-weight: 600;
  color: var(--text-bright);
}

.ac-dot {
  width: 6px;
  height: 6px;
  flex-shrink: 0;
  border-radius: 50%;
  background: currentColor;
}

.audit-class-pill.red    { background: var(--red-soft);    color: var(--red); }
.audit-class-pill.amber  { background: var(--amber-soft);  color: var(--amber); }
.audit-class-pill.accent { background: var(--accent-soft); color: var(--accent); }

/* Empty and placeholder are the cheap defects -- a field nobody filled in. The
   border is what keeps the pill legible without claiming a severity it has not
   got. */
.audit-class-pill.dim {
  background: transparent;
  border-color: var(--bezel-light);
  color: var(--text-dim);
}


/* ---------- the class badge on a finding row ---------- */
.defect-tag {
  display: inline-block;
  padding: 2px 7px;
  border: 1px solid transparent;
  border-radius: var(--radius);
  font-family: var(--font-mono);
  font-size: 10.5px;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  white-space: nowrap;
}

.defect-tag.red    { background: var(--red-soft);    color: var(--red); }
.defect-tag.amber  { background: var(--amber-soft);  color: var(--amber); }
.defect-tag.accent { background: var(--accent-soft); color: var(--accent); }

.defect-tag.dim {
  background: transparent;
  border-color: var(--bezel-light);
  color: var(--text-dim);
}


/* ---------- the finding table ----------
   The description is quoted at one line with the rest under the title attribute.
   A finding is read by its class and its detail; the description is there to
   locate the listing on the page, and letting it wrap would push the two columns
   that carry the answer off the bottom of the panel. */
.audit-table { table-layout: fixed; width: 100%; }

/* The class column is sized to the longest label the vocabulary holds --
   RULE REACHING -- because the badge does not wrap and a column narrower than
   its widest badge pushes it over the detail beside it. */
.audit-table th:nth-child(1), .audit-table td:nth-child(1) { width: 20%; }
.audit-table th:nth-child(2), .audit-table td:nth-child(2) { width: 22%; }
.audit-table th:nth-child(3), .audit-table td:nth-child(3) { width: 118px; }

.desc-cell {
  max-width: 0;
  overflow: hidden;
  color: var(--text-dim);
  text-overflow: ellipsis;
  white-space: nowrap;
}

.audit-table .cell-name {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* The detail is the evidence line, so it is the one column allowed to wrap. */
.audit-table .detail-cell {
  line-height: 1.5;
  color: var(--text-primary);
  white-space: normal;
}

@media (max-width: 760px) {
  .audit-table { table-layout: auto; }
  .desc-cell { max-width: 160px; }
}


/* ---------- one-click audit, on the row ----------
   The result and the Run share a cell. The result is the loud half -- it is
   the thing being reported -- so the Run sits quiet beside it until hovered,
   the way a row action should: available without competing with the data. */
.audit-cell {
  display: inline-flex;
  align-items: center;
  gap: 10px;
}

/* A form inside a table cell or a command bar must not take a line of its own. */
.inline-form { display: inline-flex; margin: 0; }

.audit-go {
  padding: 2px 8px;
  background: transparent;
  border: 1px solid var(--bezel);
  border-radius: var(--radius);
  color: var(--text-label);
  cursor: pointer;
  font-family: var(--font-mono);
  font-size: 10px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  opacity: 0;
  transition: opacity 100ms ease-out, color 100ms ease-out, border-color 100ms ease-out;
}

/* Shown on the row the founder is actually on, and always shown to a keyboard
   -- an action that only exists under a mouse does not exist for half the ways
   this page is used. */
.data-table tbody tr:hover .audit-go,
.data-table tbody tr:focus-within .audit-go,
.audit-go:focus-visible {
  opacity: 1;
}

.audit-go:hover {
  border-color: var(--teal);
  color: var(--teal);
}

.audit-go:focus-visible {
  outline: 1px solid var(--teal);
  outline-offset: 2px;
}

/* A sweep runs a browser per door and takes seconds, not milliseconds. The
   button says so while it works rather than sitting there looking ignored. */
.cmd-btn[data-running="1"] {
  opacity: 0.65;
  pointer-events: none;
}

.field-note { font-weight: 400; color: var(--text-label); text-transform: none; letter-spacing: 0; }

/* ---------- calendar: the week grid ----------
   Seven columns, collapsing to two then one as the viewport narrows. A cell's left
   edge carries its priority the way .action-item does, so the grid and the list
   under it speak one colour language. Overdue paints the whole cell rather than a
   badge: a past day still holding items is the thing not to scroll past. */
.cal-week { display: grid; grid-template-columns: repeat(7, minmax(0, 1fr)); gap: 8px; }

.cal-day {
  display: flex; flex-direction: column; min-height: 132px; overflow: hidden;
  background: var(--panel-inset);
  border: 1px solid var(--bezel);
  border-left: 3px solid var(--bezel-light);
  border-radius: var(--radius);
}
.cal-day.priority-high   { border-left-color: var(--red); }
.cal-day.priority-normal { border-left-color: var(--accent); }
.cal-day.priority-low    { border-left-color: var(--text-label); }
.cal-day.overdue { background: var(--red-soft); }
.cal-day.today   { border-color: var(--teal); box-shadow: 0 0 0 1px var(--teal-soft); }
.cal-day.focused { box-shadow: 0 0 0 1px var(--accent-glow); }

.cal-day-head {
  display: flex; align-items: baseline; gap: 5px; padding: 6px 8px;
  background: var(--panel-raised);
  border-bottom: 1px solid var(--bezel);
  color: var(--text-dim); text-decoration: none;
}
.cal-day-head:hover { color: var(--accent); }
.cal-day.today .cal-day-head { color: var(--teal); }
.cal-dow { font-size: 10px; letter-spacing: 0.12em; text-transform: uppercase; }
.cal-dom { font-size: 15px; color: var(--text-bright); }
.cal-mon { font-size: 10px; letter-spacing: 0.1em; text-transform: uppercase; }

/* `1 0 auto`, not `1`: the shorthand `flex: 1` is `1 1 0%`, which lets the body
   shrink below its own content when the grid row is the shorter of the two -- and
   with overflow:hidden above, shrinking is clipping. Grow into a short day, never
   shrink out of a full one. */
.cal-day-body { flex: 1 0 auto; padding: 6px 6px 8px; }
.cal-group + .cal-group { margin-top: 6px; }
.cal-group-label { font-size: 9px; letter-spacing: 0.14em; text-transform: uppercase; color: var(--text-label); }

.cal-item {
  display: block; padding: 3px 5px; margin-top: 3px;
  background: var(--panel);
  border-left: 2px solid var(--accent);
  border-radius: 2px;
  color: var(--text-primary); text-decoration: none;
}
.cal-item:hover { background: var(--panel-raised); }
.cal-item.priority-high { border-left-color: var(--red); }
.cal-item.priority-low  { border-left-color: var(--text-label); }
.cal-item-title { display: block; font-size: 11.5px; color: var(--text-bright); }
.cal-item-sub   { display: block; font-size: 10px; color: var(--text-dim); }
.cal-clear { font-size: 10px; letter-spacing: 0.1em; color: var(--text-label); }

/* ---------- dated-item kinds and reasons ----------
   A promise carries a kind and a reason. The chip says what sort of thing this is,
   the reason says why the date matters, and between them the attention queue and the
   calendar read without opening the record behind them.

   THE KIND NEVER TAKES THE LEFT BORDER. On both `.action-item` and `.cal-item` that
   border is the priority read, and a red edge means something on the row slipped. A
   kind stripe painted over it would make an overdue promise look like every other
   promise, which is the one signal the calendar exists to carry. The kind rides as
   an inset stripe just inside it, so a row says how late AND what sort and neither
   answer is spent on the other.

   The tokens are the sheet's own: this skin is near-monochrome, `--red` is black and
   `--teal` is grey, and a kind that reached for a hex of its own would be a second
   palette that drifts the first time the skin moves. */
.kind-chip {
  display: inline-block;
  padding: 1px 7px;
  border: 1px solid transparent;
  border-radius: 999px;
  font-size: 10px;
  line-height: 1.5;
  letter-spacing: 0.04em;
  white-space: nowrap;
}
.kind-chip.kind-ink    { background: var(--red-soft);    border-color: var(--red-soft);    color: var(--ink); }
.kind-chip.kind-green  { background: var(--green-soft);  border-color: var(--green-soft);  color: var(--green); }
.kind-chip.kind-teal   { background: var(--teal-soft);   border-color: var(--teal-soft);   color: var(--teal); }
.kind-chip.kind-amber  { background: var(--amber-soft);  border-color: var(--amber-soft);  color: var(--amber); }
.kind-chip.kind-purple { background: var(--purple-soft); border-color: var(--purple-soft); color: var(--purple); }
/* Outlined rather than filled: `general` sits on `--panel-inset` in the queue, so a
   fill of the same token would be a chip nobody can see. */
.kind-chip.kind-muted  { background: transparent; border-color: var(--bezel-light); color: var(--text-label); }

.action-reason {
  display: block;
  margin-top: 2px;
  font-size: 11.5px;
  line-height: 1.45;
  color: var(--text-dim);
}
.cal-item-reason {
  display: block;
  margin-top: 1px;
  font-size: 9.5px;
  line-height: 1.35;
  color: var(--text-dim);
}

.cal-item.kind-ink,
.action-item.kind-ink    { box-shadow: inset 3px 0 0 var(--ink); }
.cal-item.kind-green,
.action-item.kind-green  { box-shadow: inset 3px 0 0 var(--green); }
.cal-item.kind-teal,
.action-item.kind-teal   { box-shadow: inset 3px 0 0 var(--teal); }
.cal-item.kind-amber,
.action-item.kind-amber  { box-shadow: inset 3px 0 0 var(--amber); }
.cal-item.kind-purple,
.action-item.kind-purple { box-shadow: inset 3px 0 0 var(--purple); }
.cal-item.kind-muted,
.action-item.kind-muted  { box-shadow: inset 3px 0 0 var(--bezel-light); }

@media (max-width: 1100px) { .cal-week { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (max-width: 560px)  { .cal-week { grid-template-columns: 1fr; } }

/* ---------- draft helpers ----------
   A draft button is a real form so it works without JavaScript. display:contents
   keeps that form out of the layout, so the button sits exactly where a bare button
   would in a .contact-actions or .form-actions row. */
.draft-inline { display: contents; }

.draft-row { display: flex; align-items: center; gap: 10px; margin-bottom: 12px; }
.draft-note { font-size: 11px; color: var(--text-dim); }

.draft-text {
  width: 100%;
  min-height: 200px;
  font-family: var(--font-body);
  font-size: 13px;
  line-height: 1.55;
  white-space: pre-wrap;
  resize: vertical;
}


/* =========================================================
   SYSTEM 1 CHROME
   The Kare layer. Everything above is structure inherited from the
   cockpit build; everything here is what makes it one-bit.

   This block is deliberately last so it wins on source order rather
   than on specificity — a rule here is the skin's final word, and it
   is the only place in this file allowed to override a section rule.

   It is written against the class names this app actually ships
   (.panel-head, .cmd-btn, .content, .topbar, .footer), with the
   generic names from the reskin plan (.card-header, .btn,
   .content-area, .header-bar, .app-footer) carried alongside them as
   aliases so a future template can use either and land on the skin.
   ========================================================= */

/* Every measured number is tabular. Inherited from the root rather than
   enumerated per component, so a number added tomorrow is already
   aligned; the proportional body face simply ignores it. */
body { font-variant-numeric: tabular-nums; }

/* ---------- the desktop ---------- */

/* The 50% dither -- the original desktop pattern, one black pixel next
   to one white one. A repeating conic gradient at a 2px tile draws four
   1px squares, which is exactly what the Mac drew and which optically
   reads as a calm grey field with the pixels visible in it.

   The reskin plan specified a 4px tile. Built at 4px it renders 2px
   squares, and a half-black chequerboard at that scale stops being
   ground and becomes the loudest thing on the page -- the panels have
   to fight it for attention. This is the same pattern at the size it
   was designed for; if the founder wants it coarser it is one number.

   It sits on the body and shows through .content, so panels read as
   windows lying on a desktop rather than as boxes on a page. Anything
   that carries text paints over it. */
body {
  background:
    repeating-conic-gradient(var(--ink) 0% 25%, var(--ground) 0% 50%) 0 0 / 2px 2px;
}

.main { background: transparent; }

/* Transparent so the desktop shows in the 22px margin and the 18px
   gutters between sections. The children below put the paper back. */
.content, .content-area, .main-content { background: transparent; }

/* Paper. Every direct child of the content column, and every section of
   the dashboard's one wrapper, is opaque -- stated as a child rule
   rather than a list of component names so a view added later is
   readable by construction and not by remembering to come back here. */
.content > *,
.dash > * { background: var(--ground); }

/* ---------- windows ---------- */

/* Cards and panels are Mac windows: a 2px black frame and one hard
   offset shadow. No blur, no radius, no gradient -- a 1-bit display
   could not draw any of the three. */
.content > section,
.dash > section,
.panel,
.card,
.inst,
.readout,
.split-panel,
.vital-card,
.contact-card,
.cust-card,
.decision-card,
.sheet-card,
.instrument,
.disc-record,
.login-card {
  border: 2px solid var(--ink);
  border-radius: 0;
  box-shadow: 2px 2px 0 var(--ink);
  background: var(--ground);
}

/* Title bars. The pinstripe is the Mac's active-window signal, and it
   is drawn the way the Mac drew it: alternating one-pixel rules. The
   label is knocked out of the stripe on a solid block, because a
   pinstripe running through letterforms is unreadable at 14px. */
.panel-head,
.card-header {
  background: repeating-linear-gradient(
    to bottom, var(--ink) 0px, var(--ink) 1px,
    var(--ground) 1px, var(--ground) 3px
  );
  color: var(--ground);
  font-family: var(--font-display);
  border-bottom: 2px solid var(--ink);
  padding: 4px 8px;
  font-size: 14px;
  letter-spacing: 0.04em;
}

.panel-head > *,
.card-header > *,
.panel-title,
.panel-meta {
  background: var(--ink);
  color: var(--ground);
  padding: 1px 6px;
}

/* The other title bar, and the one every inner section head wears.

   Each heading in this group carries its title as a BARE TEXT NODE --
   there is no element around the words -- so none of them can take the
   knocked-out plate the rule above gives `.panel-head`, and a pinstripe
   run straight through 14px letterforms is unreadable. So they are drawn
   the other way up: a solid bar with the title on it, and the stripes
   filling the width the title does not use. Same Mac title bar, same two
   ingredients, opposite order.

   It reads as a hierarchy too, which is why the split is worth keeping:
   the pinstriped bar is a window's title, the solid bar is a section
   inside that window. */
.panel-label,
.disc-section-head,
.cust-detail-head,
.detail-head {
  display: flex;
  align-items: center;
  gap: 8px;
  background: var(--ink);
  color: var(--ground);
  font-family: var(--font-display);
  border-bottom: 2px solid var(--ink);
  padding: 4px 8px;
  font-size: 14px;
  letter-spacing: 0.04em;
}
.panel-label::after,
.disc-section-head::after,
.cust-detail-head::after,
.detail-head::after {
  content: "";
  flex: 1 1 auto;
  align-self: stretch;
  min-width: 20px;
  background: repeating-linear-gradient(
    to bottom, var(--ground) 0px, var(--ground) 1px,
    var(--ink) 1px, var(--ink) 3px
  );
}
/* A badge sitting in that bar is knocked out of it, which is the same
   move as the plate above with the ink and the paper swapped. It needs
   no `order`: the title is an anonymous flex item, the badge and the
   ::after filler are both order 0 too, so all three fall in source
   order and the stripes land last where they belong. */
.panel-label > *,
.disc-section-head > *,
.cust-detail-head > *,
.detail-head > * {
  background: var(--ground);
  color: var(--ink);
  padding: 1px 6px;
}

/* The title bar's leading dot was a glowing lozenge in the old skin.
   Here it is nothing: the pinstripe already says "this is a title". */
.panel-title::before { display: none; }

/* ---------- controls ---------- */

/* Kare's buttons: a rounded rectangle, and the default action wearing a
   second ring outside the first. Hovering inverts, because on a 1-bit
   screen inversion is the only feedback there is. */
button,
.btn,
.cmd-btn,
.logout-btn,
.login-btn,
.audit-run,
.audit-go {
  border: 2px solid var(--ink);
  border-radius: 6px;
  background: var(--ground);
  color: var(--ink);
  font-family: var(--font-display);
  padding: 4px 16px;
  cursor: pointer;
  box-shadow: none;
  text-shadow: none;
}

button:hover:not(:disabled),
.btn:hover,
.cmd-btn:hover:not(:disabled),
.audit-run:hover:not(:disabled),
.audit-go:hover {
  background: var(--ink);
  color: var(--ground);
  border-color: var(--ink);
  box-shadow: none;
}

/* The default action wears a second ring outside the first, which is the
   whole of how the Mac marked it -- not a fill. The background reset is
   load-bearing: `.cmd-btn.primary` further up this file paints itself
   accent, and a solid green button becomes the loudest thing on every
   screen it lands on. Green is a state here, never a chrome colour. */
button.primary,
.btn-primary,
.cmd-btn.primary,
.login-btn {
  background: var(--ground);
  color: var(--ink);
  border-color: var(--ink);
  box-shadow: 0 0 0 2px var(--ground), 0 0 0 4px var(--ink);
}
.cmd-btn.primary:hover:not(:disabled),
.login-btn:hover {
  background: var(--ink);
  color: var(--ground);
  box-shadow: 0 0 0 2px var(--ground), 0 0 0 4px var(--ink);
}

/* The tone variants keep their names and lose their hues -- in a 1-bit
   world a destructive button is not redder, it is heavier. */
.cmd-btn.teal,
.cmd-btn.purple,
.cmd-btn.danger { border-color: var(--ink); color: var(--ink); }
.cmd-btn.danger { border-width: 3px; font-weight: bold; }
.cmd-btn.small { padding: 2px 10px; font-size: 12px; }

/* Fields: the same 2px frame, square, so a text field reads as a hole
   rather than as a raised surface. */
.field-input,
input[type="text"],
input[type="date"],
input[type="number"],
input[type="password"],
input[type="search"],
select,
textarea {
  border: 2px solid var(--ink);
  border-radius: 0;
  background: var(--ground);
  color: var(--ink);
  box-shadow: none;
}
/* Focus is the default-button ring again: a second black rule outside the
   first, with paper between them. Not green -- green is reserved for
   states that mean go, and "the cursor is here" is not one of them. */
.field-input:focus,
input:focus,
select:focus,
textarea:focus {
  outline: 2px solid var(--ink);
  outline-offset: 2px;
  border-color: var(--ink);
  box-shadow: none;
}

/* ---------- Finder list views ---------- */

.data-table { border-collapse: collapse; }

.data-table thead th {
  border-bottom: 2px solid var(--ink);
  text-decoration: underline;
  font-family: var(--font-display);
  font-size: 13px;
  letter-spacing: 0.5px;
  color: var(--ink);
  background: var(--ground);
}
/* The old skin's second hairline under the header. One rule is enough
   now that the rule itself is 2px. */
.data-table thead th::after { display: none; }

/* Inverse video on hover, the way the Finder selected a row. It has to
   land on the cells: a `td` background rule further up this file would
   otherwise paint straight over a `tr` background. The descendant reset
   is what makes it *inverse* rather than black-on-black -- badges,
   links and meta text inside the row all carry their own colour. */
.data-table tbody tr:hover td,
.data-table tbody tr:focus-within td,
.data-table tbody tr.row-open:focus-visible td {
  background: var(--ink);
  color: var(--ground);
}
.data-table tbody tr:hover td *,
.data-table tbody tr:focus-within td *,
.data-table tbody tr.row-open:focus-visible td * {
  color: var(--ground);
  border-color: var(--ground);
}
.data-table tbody tr:hover td .cmd-btn,
.data-table tbody tr:hover td .audit-go {
  background: var(--ink);
  color: var(--ground);
  border-color: var(--ground);
}
.data-table tbody td { border-bottom: 1px solid var(--ink); }

/* ---------- the black spine ---------- */

/* The sidebar is the one solid black mass on the page. The base rule
   paints it --panel, which is white in this skin, so this is not a
   flourish: without it the spine and its white brand mark vanish into
   the page. Everything inside it therefore has to be re-lit for a dark
   ground -- that is what the rest of this block is. */
.sidebar {
  background: var(--sidebar-bg);
  color: var(--sidebar-text);
  border-right: 2px solid var(--ink);
  box-shadow: none;
}
.sidebar .brand { border-bottom: 1px solid var(--sidebar-hover); }
.sidebar .brand-mark {
  font-family: var(--font-display);
  color: var(--ground);
  letter-spacing: 0.15em;
  text-shadow: none;
}
.sidebar .brand-sub,
.sidebar .nav-group-label { color: #AAAAAA; }
.sidebar .nav-item {
  color: #DDDDDD;
  border-left: 3px solid transparent;
  background: transparent;
}
.sidebar .nav-item:hover { background: var(--sidebar-hover); color: var(--ground); }
.sidebar .nav-icon { color: #888888; }

/* Active nav: the row inverts to paper and takes the one green rail.
   This is the same selector the plan names (`.sidebar nav a.active`),
   written against the class this app's nav actually uses. */
.sidebar nav a.active,
.sidebar .nav-item.active {
  background: var(--ground);
  color: var(--ink);
  border-left: 3px solid var(--accent);
  padding-left: calc(var(--nav-pad-x, 18px) - 3px);
  font-weight: bold;
  box-shadow: none;
}
.sidebar .nav-item.active .nav-icon { color: var(--accent); text-shadow: none; }

.sidebar-foot { border-top: 1px solid var(--sidebar-hover); }
.sidebar .heartbeat { color: #AAAAAA; background: transparent; }
.logout-btn { color: #DDDDDD; border-color: #666666; background: transparent; }
.logout-btn:hover { background: var(--ground); color: var(--ink); border-color: var(--ground); }

/* ---------- menu bar ---------- */

.topbar,
.header-bar {
  background: var(--ground);
  border-bottom: 2px solid var(--ink);
  font-family: var(--font-display);
  backdrop-filter: none;
  -webkit-backdrop-filter: none;
}
.topbar-title { color: var(--ink); }
.refresh-indicator {
  border: 2px solid var(--ink);
  border-radius: 999px;
  background: var(--ground);
  color: var(--ink);
}

/* ---------- lamps ---------- */

/* The green light. These three are the only places the spot colour is
   allowed to appear as a lit dot, and `.heartbeat` is deliberately NOT
   among them: it is a whole <section> on the dashboard and a text badge
   in the sidebar, so painting it accent fills a panel with green. The
   dot inside it is the thing that lights. */
.pulse-dot,
.heartbeat-dot,
.clock-pulse {
  background: var(--accent);
  box-shadow: 0 0 4px var(--accent-glow);
}

/* ---------- badges ---------- */

.stage-badge,
.badge,
.contact-pill,
.kind-chip,
.build-badge,
.cust-desk-pill,
.filter-pill,
.log-tag,
.defect-tag {
  border-radius: 0;
  border: 1px solid var(--ink);
  font-family: var(--font-display);
  font-size: 11px;
}

/* ---------- colophon ---------- */

.footer,
.app-footer {
  background: var(--ground);
  border-top: 2px solid var(--ink);
  font-family: var(--font-mono);
  font-size: 10px;
  letter-spacing: 0.15em;
  text-transform: uppercase;
  color: var(--text-dim);
  opacity: 1;
}

/* ---------- overlays ---------- */

.overlay-panel {
  border: 2px solid var(--ink);
  border-radius: 0;
  box-shadow: 4px 4px 0 var(--ink);
  background: var(--ground);
}
.overlay-head { border-bottom: 2px solid var(--ink); }

/* ---------- mobile nav ---------- */

.mobile-nav { background: var(--sidebar-bg); border-top: 2px solid var(--ink); }
.mobile-nav-btn { color: #DDDDDD; border-radius: 0; }
.mobile-nav-btn.active { background: var(--ground); color: var(--ink); font-weight: bold; }

/* ---------- login ---------- */

/* The login screen is one window on the bare desktop, so the page behind
   it stays dithered rather than being painted over. */
/* .login-body IS the <body>, so it must not be given a background here:
   anything set on it replaces the desktop dither and the login window
   ends up floating on blank white. Only the wrapper is cleared. */
.login-wrap { background: transparent; }
.login-card { box-shadow: 3px 3px 0 var(--ink); }
.login-brand, .login-title { font-family: var(--font-display); color: var(--ink); }
/* The wordmark is accent-coloured by the base rule, which is right in the
   black spine and wrong on paper: green is a state, and a brand mark is
   not a state. The sidebar keeps its white one; everywhere else it is ink. */
.brand-mark { color: var(--ink); text-shadow: none; }

/* ---------- scrollbars ---------- */

::-webkit-scrollbar { width: 6px; height: 6px; }
::-webkit-scrollbar-track { background: var(--ground); }
::-webkit-scrollbar-thumb { background: var(--ink); border-radius: 0; }
::-webkit-scrollbar-thumb:hover { background: var(--ink); }
* { scrollbar-width: thin; scrollbar-color: var(--ink) var(--ground); }

/* ---------- selection ---------- */

::selection { background: var(--selection-bg); color: var(--selection-text); }

/* ---------- the Shopfront ---------- */

/* The mascot draws itself from <symbol> definitions in base.html. Only
   the sizing lives here; the pixels live in the sprite. */
.shopfront {
  /* The sprite paints itself in currentColor, so this is the shop's ink
     and an inverted row hands it white without a filter. */
  color: var(--ink);
  display: inline-block;
  width: 32px;
  height: 32px;
  vertical-align: middle;
  flex: 0 0 auto;
  shape-rendering: crispEdges;
  image-rendering: pixelated;
}
.shopfront-sm { width: 22px; height: 22px; }
.shopfront-lg { width: 44px; height: 44px; }

/* ---------- the morning header ---------- */

/* The mascot leads, the date follows. Baseline-aligned rather than centred
   so the face sits on the same line the greeting reads from. */
.dash .morning {
  display: flex;
  align-items: center;
  gap: 14px;
}
.dash .morning-words { min-width: 0; }

/* ---------- the Founding Five ---------- */

.pilot {
  border: 2px solid var(--ink);
  box-shadow: 2px 2px 0 var(--ink);
  background: var(--ground);
}

.pilot-head {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 10px 12px;
  border-bottom: 2px solid var(--ink);
}

.pilot-headline {
  font-family: var(--font-display);
  font-size: 15px;
  letter-spacing: 0.06em;
  color: var(--ink);
}

.pilot-row {
  display: grid;
  grid-template-columns: 14px 200px 1fr auto;
  align-items: baseline;
  gap: 10px;
  padding: 7px 12px;
  border-bottom: 1px solid var(--bezel-light);
}
.pilot-row:last-child { border-bottom: none; }

/* An open item is a hollow square; a closed one is the green light filled
   in. Two shapes, not two colours -- the lamp still reads on a monochrome
   print, and green stays the one thing that means go. */
.pilot-lamp {
  width: 10px;
  height: 10px;
  border: 2px solid var(--ink);
  background: var(--ground);
}
.pilot-row.done .pilot-lamp {
  background: var(--accent);
  border-color: var(--accent);
}

.pilot-name {
  font-family: var(--font-display);
  font-size: 13px;
  color: var(--ink);
}
.pilot-row.done .pilot-name { color: var(--text-dim); }

.pilot-detail { color: var(--text-dim); font-size: 12.5px; }

.pilot-src {
  font-size: 10.5px;
  letter-spacing: 0.06em;
  color: var(--text-label);
  text-transform: uppercase;
  white-space: nowrap;
}

@media (max-width: 760px) {
  .pilot-row { grid-template-columns: 14px 1fr; }
  .pilot-detail, .pilot-src { grid-column: 2; }
}

/* ---------- the edition lamp ----------
   Same frame as the Founding Five above, because it is the same kind of thing: a
   panel that reports a measured state rather than a list of work. The dot itself is
   the shared `.lamp` component and nothing here restyles it -- one lamp, one meaning,
   wherever it appears. */

.edition {
  border: 2px solid var(--ink);
  box-shadow: 2px 2px 0 var(--ink);
  background: var(--ground);
}

.edition-head {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 10px 12px;
  border-bottom: 2px solid var(--ink);
}

.edition-headline {
  font-family: var(--font-display);
  font-size: 15px;
  letter-spacing: 0.06em;
  color: var(--ink);
}

/* A gap is the one state worth reading from across the room, and it is the one state
   that cannot be fixed later, so it gets more than a dot. In this skin --red IS black
   (see the token block: an alarm is the heaviest mark, not a second hue), so colouring
   the headline red would change nothing on screen. Weight is the signal instead: a
   deeper shadow, and the shaded band .cal-day.overdue already uses for the same fact. */
.edition:has(.lamp-blocked) { box-shadow: 4px 4px 0 var(--ink); }
.edition:has(.lamp-blocked) .edition-head { background: var(--red-soft); }

.edition-figures {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
}

.edition-fig {
  display: flex;
  flex-direction: column;
  gap: 4px;
  padding: 10px 12px;
  border-right: 1px solid var(--bezel-light);
}
.edition-fig:last-child { border-right: none; }

.edition-fig-label {
  font-size: 10.5px;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--text-label);
}

.edition-fig-val {
  font-size: 17px;
  color: var(--ink);
}

.edition-note {
  padding: 8px 12px;
  border-top: 1px solid var(--bezel-light);
  background: var(--panel-inset);
  font-size: 12.5px;
  color: var(--text-dim);
}

.edition-footer {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 6px;
  padding: 8px 12px;
  border-top: 2px solid var(--ink);
  font-size: 12px;
  color: var(--text-dim);
}

/* The path is evidence, not decoration: a lamp reading Empty because it is pointed
   at the wrong file has to be tellable from one reading an empty archive. */
.edition-src {
  margin-left: auto;
  font-size: 10.5px;
  color: var(--text-label);
  word-break: break-all;
}

@media (max-width: 760px) {
  .edition-figures { grid-template-columns: 1fr; }
  .edition-fig { border-right: none; border-bottom: 1px solid var(--bezel-light); }
  .edition-fig:last-child { border-bottom: none; }
  .edition-src { margin-left: 0; }
}

/* ---------- the Templates view ---------- */

.tpl-face { width: 28px; padding-right: 0; }
.tpl-face .shopfront { display: block; }

.tpl-open { text-decoration: underline; }

/* The open row keeps the frame that the detail below hangs off, so the two
   read as one record rather than as a row and a loose block under it. */
.data-table tbody tr.row-current td { background: var(--panel-inset); }

.tpl-detail-row td { background: var(--panel-inset); border-bottom: 2px solid var(--ink); }

.tpl-detail {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
  gap: 12px 20px;
  padding: 12px 4px;
}
.tpl-detail-block.wide { grid-column: 1 / -1; }

.tpl-detail-label {
  font-family: var(--font-display);
  font-size: 11px;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--text-label);
  margin-bottom: 3px;
}

.tpl-detail-val { color: var(--text-primary); font-size: 12.5px; }

.tpl-version {
  display: flex;
  gap: 12px;
  padding: 3px 0;
  border-bottom: 1px solid var(--bezel-light);
  font-size: 12.5px;
}
.tpl-version:last-child { border-bottom: none; }

/* The skin badge. Two skins, two weights: the internal one is filled,
   because inside this cockpit S1 is the default and I is the exception. */
.stage-badge.skin-system1 { background: var(--ink); color: var(--ground); }
.stage-badge.skin-instrument { background: var(--ground); color: var(--ink); }

/* The registry is nine columns wide and every one of them is part of the
   reading. Wrapping beats the horizontal scroll the base table rule would
   otherwise give it: a column you have to scroll sideways to find is a
   column that does not get read. */
/* Selector weight is load-bearing here. `.data-table tbody td` further up
   this file sets `white-space: nowrap`, and it outweighs a bare
   `.tpl-table td` -- which is how three of these nine columns ended up
   scrolled off the right edge with the detail block's grid cells drawn on
   top of each other. Matching its shape wins it back. */
.data-table.tpl-table { width: 100%; table-layout: auto; }
.data-table.tpl-table thead th,
.data-table.tpl-table tbody td { white-space: normal; }
.data-table.tpl-table tbody td.mono { white-space: nowrap; }
/* The face column and the ID never wrap and never grow. */
.data-table.tpl-table tbody td.tpl-face { width: 30px; padding-left: 8px; padding-right: 0; }

/* The fault page's Closed face, beside the sentence that says the same
   thing in words. Both, not one: the mascot is a glance and the sentence
   is the record. */
.fault-face {
  display: flex;
  align-items: center;
  gap: 14px;
  margin-bottom: 14px;
  font-family: var(--font-display);
  font-size: 14px;
  line-height: 1.4;
  color: var(--ink);
}

/* ---------- the copy gate: the stamp, and the alert box under it ----------
   The System 1 alert: a plain box with a hard black rule around it, and the
   weight of that rule is the severity. `hard` gets the 2px border and the
   offset shadow the window chrome uses -- it is the heaviest mark on the
   page. `style` gets the single hairline. A finding the machine cannot
   judge gets the same box dashed, because a thing handed back for a read
   is not a thing that failed.

   Severity is carried by weight, never by hue: there is no second colour
   here. Green appears in one place only, on the stamp of a text that came
   through clear, which is the one meaning green has in this cockpit. */
.lint-form { display: flex; flex-direction: column; gap: 12px; margin-bottom: 12px; }

.lint-text {
  font-family: var(--font-mono);
  font-size: 12.5px;
  line-height: 1.6;
}

.stamp {
  display: flex;
  align-items: baseline;
  flex-wrap: wrap;
  gap: 12px;
  padding: 10px 14px;
  margin-bottom: 12px;
  background: var(--panel);
  border: 2px solid var(--ink);
  box-shadow: var(--shadow-panel);
}

/* Clear, and nothing else on this page, wears the green light. */
.stamp-green { border-color: var(--accent); }

/* Nothing was linted yet. A hairline, no shadow: this is not a verdict. */
.stamp-empty { border-width: 1px; box-shadow: none; }

.stamp-headline { font-size: 13px; color: var(--text-bright); }

.stamp-note {
  margin: -4px 0 12px;
  font-size: 12px;
  color: var(--text-dim);
}

.alert-stack { display: flex; flex-direction: column; }

.alert {
  padding: 10px 12px;
  margin-bottom: 10px;
  background: var(--panel);
  border: 1px solid var(--bezel);
  border-radius: var(--radius);
}

.alert-hard {
  border-width: 2px;
  box-shadow: var(--shadow-panel);
}

.alert-read { border-style: dashed; }

.alert-top {
  display: flex;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: 12px;
  margin-bottom: 6px;
}

.alert-kind {
  font-family: var(--font-display);
  font-size: 12px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--text-bright);
}

.alert-where {
  font-family: var(--font-mono);
  font-size: 11px;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--text-dim);
  white-space: nowrap;
}

/* The offending line, printed as it was typed. pre-wrap so leading spaces
   survive: the column number has to point at something. */
.alert-line {
  margin: 0 0 6px;
  padding: 6px 8px;
  background: var(--panel-inset);
  font-family: var(--font-mono);
  font-size: 12.5px;
  line-height: 1.6;
  white-space: pre-wrap;
  overflow-wrap: anywhere;
}

.alert-dim { color: var(--text-dim); }

/* Inverse video on the match itself, which is how this world highlights. */
.alert-line mark {
  padding: 0 2px;
  background: var(--ink);
  color: var(--ground);
}

.alert-msg { margin: 0; font-size: 12.5px; color: var(--text-primary); }
.alert-msg + .alert-msg { margin-top: 6px; }

.alert-divider {
  display: flex;
  align-items: center;
  gap: 10px;
  margin: 16px 0 10px;
  font-family: var(--font-mono);
  font-size: 11px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--text-dim);
}

.alert-divider::before,
.alert-divider::after {
  content: "";
  flex: 1;
  border-top: 1px dashed var(--bezel-light);
}

/* ---------- the gate strip ----------
   Same frame as the Founding Five and the edition lamp, because it is the same
   kind of object: a panel reporting a measured state. The dot is the shared
   `.lamp` component and nothing here restyles it -- one lamp, one meaning.

   Three states, and they are drawn as three weights rather than three hues,
   because in this skin --red IS black and a second colour would be a lie about
   the palette. Green is go. Not-shipped is the heavy state the edition lamp's gap
   wears: deeper shadow, shaded band. Not-queued is the lightest thing on the
   page, because nothing has been promised about it yet. */

/* Scoped under `.dash` for the same reason the Needs You block is: `.gate`,
   `.gate-open` and `.gate-none` are already taken in this stylesheet by the gate
   indicators up at "gate indicators" -- a 10px round dot. No template renders that
   dot today (the pipeline moved to the shared `.lamp`), but the rules are still
   here, and redefining a live selector on the strength of nothing currently using
   it is how the next template to reach for `.gate` gets a flex column.

   AND SCOPING WAS NOT ENOUGH. This section used to call its rows `.gate` and rely
   on `.dash .gate` to win. It won on the properties it restated -- display,
   padding, border -- and lost on the three it did not: the dot's width, height and
   border-radius. Every row rendered ten pixels tall with five lines of text
   spilling over the row below it, at EVERY width, and the panel had been shipping
   that way. The rows are `.gate-row` now. An override that has to remember every
   property the rule it is fighting happens to set is not a fix, it is the same bug
   with a delay on it. */

.dash .gates {
  border: 2px solid var(--ink);
  box-shadow: 2px 2px 0 var(--ink);
  background: var(--ground);
}

.dash .gates-head {
  padding: 10px 12px;
  border-bottom: 2px solid var(--ink);
}

.dash .gates-headline {
  font-family: var(--font-display);
  font-size: 15px;
  letter-spacing: 0.06em;
  color: var(--ink);
}

.dash .gates-strip {
  display: grid;
  grid-template-columns: repeat(5, 1fr);
}

.dash .gate-row {
  display: flex;
  flex-direction: column;
  gap: 6px;
  padding: 10px 12px;
  border-right: 1px solid var(--bezel-light);
  color: inherit;
  text-decoration: none;
}
.dash .gate-row:last-child { border-right: none; }
.dash a.gate-row:hover { background: var(--panel-inset); }

.dash .gate-no {
  font-size: 10.5px;
  letter-spacing: 0.06em;
  color: var(--text-label);
}

.dash .gate-name {
  font-family: var(--font-display);
  font-size: 13px;
  color: var(--ink);
}

.dash .gate-unit {
  font-size: 10.5px;
  color: var(--text-label);
  overflow-wrap: anywhere;
}

/* The one line that appears only when the board and the Founding Five disagree.
   It is a sentence rather than a badge on purpose: the panel is not resolving the
   disagreement, it is reporting one. */
.dash .gate-note {
  font-size: 11.5px;
  color: var(--text-dim);
  border-top: 1px solid var(--bezel-light);
  padding-top: 6px;
}

/* Weight, not colour. Same treatment as `.edition:has(.lamp-blocked)`. */
.dash .gate-row-open { background: var(--red-soft); }
.dash .gates:has(.gate-row-open) { box-shadow: 4px 4px 0 var(--ink); }

/* Nothing queued for this gate. Dimmed rather than shaded: no work order is a
   lighter fact than an unfinished one, and it must not read as the alarm. */
.dash .gate-row-none .gate-name { color: var(--text-dim); }

.dash .gates-note {
  padding: 8px 12px;
  border-top: 2px solid var(--ink);
  background: var(--panel-inset);
  font-size: 12px;
  color: var(--text-dim);
}

/* 860, not 760. This is where the layout actually switches -- the sidebar leaves
   and the mobile nav arrives -- and a five-column strip left standing for another
   hundred pixels after that is five columns in the width of three. One mobile
   boundary, the same one the card family keeps. */
@media (max-width: 860px) {
  .dash .gates-strip { grid-template-columns: 1fr; }
  .dash .gate-row { border-right: none; border-bottom: 1px solid var(--bezel-light); }
  .dash .gate-row:last-child { border-bottom: none; }
}

/* ---------- the stall detectors ----------
   Two lists under the strip. Deliberately quieter than Needs You: these rows are
   not urgent, they are overlooked, and a panel that shouted would compete with the
   one thing on this page that is actually due today. */

.stalls {
  border: 2px solid var(--ink);
  box-shadow: 2px 2px 0 var(--ink);
  background: var(--ground);
}

.stall-group { border-bottom: 2px solid var(--ink); }
.stall-group:last-child { border-bottom: none; }

.stall-head {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 6px 12px;
  border-bottom: 1px solid var(--bezel-light);
}

.stall-name {
  font-family: var(--font-display);
  font-size: 12px;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--ink);
}

.stall-rule {
  flex: 1 1 auto;
  height: 2px;
  background: var(--bezel-light);
}

.stall-count {
  font-family: var(--font-mono);
  font-size: 12px;
  color: var(--text-label);
}

.stall-row {
  display: flex;
  align-items: baseline;
  gap: 10px;
  padding: 7px 12px;
  border-bottom: 1px solid var(--bezel-light);
  color: inherit;
  text-decoration: none;
}
.stall-row:last-child { border-bottom: none; }
.stall-row:hover { background: var(--panel-inset); }

.stall-text { min-width: 0; }
.stall-text strong { font-family: var(--font-display); font-size: 13px; font-weight: normal; }

.stall-detail {
  display: block;
  font-size: 12px;
  color: var(--text-dim);
}

.stall-days {
  margin-left: auto;
  white-space: nowrap;
  font-size: 12.5px;
  color: var(--ink);
}

/* An empty list is a result, not an absence of one, so it says so in the data
   face rather than leaving the group blank. */
.stall-clear {
  padding: 8px 12px;
  font-size: 12.5px;
  color: var(--text-dim);
}

/* ---------- Quote Bank ----------
   One rule, and it is the reason this block exists: `.data-table tbody td` sets
   `white-space: nowrap` and `.tpl-table` walks it back for every cell EXCEPT a
   mono one, which is exactly the cell a verbatim answer sits in. A prospect's
   sentence scrolled off the right edge is a quote nobody reads, and the whole
   value of this page is that the words are read as words.

   Nothing here introduces a colour, a face or a border weight: the skin is the
   same 1-bit board with the same variables, and this is the wrapping the content
   needs. */
.data-table.quote-table { table-layout: auto; }
.data-table.quote-table tbody td.quote-verbatim {
  white-space: pre-wrap;
  min-width: 22ch;
  color: var(--text-bright);
}
/* The signals behind the count, under it rather than beside it, so a long list
   of labels does not push the quote column off the page. */
.data-table.quote-table tbody td.quote-fire { white-space: normal; min-width: 12ch; }

/* =========================================================
   THE PILOT SCOREBOARD — five signals per design-partner door
   -------------------------------------------------------
   It is NOT the Founding Five panel on the Dashboard. That one counts the five
   pre-pilot GATES; this one counts five signals per enrolled door. Two panels,
   two denominators, and deliberately two different headings — see
   gims/scoreboard.py on why the name matters.

   Same frame as every other instrument on this cockpit: the shared `.lamp`
   component, unrestyled, so a green lamp means the same thing here as it does on
   the gate strip and the edition lamp. One lamp, one meaning.
   ========================================================= */

/* The green sibling of .teal and .purple. The enrol button opens a program, which
   is the one green act on this bar. */
.cmd-btn.green { border-color: var(--green); color: var(--green); }
.cmd-btn.green:hover:not(:disabled) {
  background: var(--green-soft);
  box-shadow: 0 0 0 1px var(--green-soft);
}

/* Wider than the default card so five lamps and their detail lines fit without
   any of them truncating. A lamp whose detail is cut off is a lamp that has to be
   hovered, and the point of the row is that it reads at a glance. */
.pilot-grid { grid-template-columns: repeat(auto-fill, minmax(300px, 1fr)); }

.pilot-card {
  display: flex;
  flex-direction: column;
  gap: 12px;
  padding: 14px;
  background: var(--panel-inset);
  border: 1px solid var(--bezel);
  border-top: 2px solid var(--green);
  border-radius: var(--radius-lg);
}

.pilot-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 10px;
}

.pilot-name {
  margin: 0;
  font-family: var(--font-display);
  font-size: 14px;
  color: var(--text-bright);
}

.pilot-stage {
  flex-shrink: 0;
  font-family: var(--font-mono);
  font-size: 9px;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--text-label);
}

.pilot-clock { display: flex; flex-direction: column; gap: 4px; }

.pilot-bar {
  height: 6px;
  background: var(--panel);
  border: 1px solid var(--bezel-light);
}

/* No transition. The bar is a day count, not an animation, and a width that
   slides in on load reads as something happening rather than as a fact. */
.pilot-fill {
  display: block;
  height: 100%;
  background: var(--green);
}

.pilot-days {
  font-family: var(--font-mono);
  font-size: 10px;
  letter-spacing: 0.06em;
  color: var(--text-dim);
}

.pilot-lamps {
  margin: 0;
  padding: 0;
  list-style: none;
  display: flex;
  flex-direction: column;
  gap: 6px;
}

.pilot-signal {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: 1px;
}

/* The detail and the note sit under the lamp's own label, indented to the width
   the lamp's dot takes, so the column of dots stays a column. */
.pilot-detail,
.pilot-note {
  padding-left: 16px;
  font-size: 11px;
  overflow: hidden;
  text-overflow: ellipsis;
}

.pilot-detail { color: var(--text-dim); }

/* Where the number was measured FROM. It is evidence, not decoration: a
   first-value lamp with no compliance day on record says so here rather than
   implying the clock started at enrolment. */
.pilot-note {
  font-family: var(--font-mono);
  font-size: 9px;
  letter-spacing: 0.04em;
  color: var(--text-label);
}

.pilot-foot {
  display: flex;
  justify-content: space-between;
  gap: 10px;
  padding-top: 8px;
  border-top: 1px solid var(--bezel-light);
  font-family: var(--font-mono);
  font-size: 10px;
  letter-spacing: 0.06em;
  color: var(--text-label);
}

.pilot-actions { display: flex; justify-content: flex-end; }

/* One enrol form per qualifying door, stacked in the overlay. A rule between them
   so two adjacent forms do not read as one form with six fields. */
.pilot-enrol { padding-bottom: 14px; }
.pilot-enrol + .pilot-enrol { border-top: 1px solid var(--bezel-light); padding-top: 14px; }

.pilot-enrol-name {
  margin-bottom: 8px;
  font-family: var(--font-display);
  font-size: 13px;
  color: var(--text-bright);
}

/* One column on a phone. The cards already flow through .card-grid, so this is
   only the inside of a card: the footer's two halves stop competing for a line. */
@media (max-width: 860px) {
  .pilot-grid { grid-template-columns: 1fr; }
  .pilot-foot { flex-direction: column; gap: 2px; }
}

/* =========================================================
   QUICK CAPTURE — the omnibox, and the page it lands on when it cannot route
   -------------------------------------------------------
   The box sits between the view title and the clock, which is where the eye
   already goes on this layout. It is a real form: with JavaScript off it still
   posts and still lands somewhere useful, and the `/` shortcut is the enhancement
   rather than the mechanism.
   ========================================================= */

.omni {
  flex: 1;
  display: flex;
  align-items: center;
  gap: 6px;
  max-width: 420px;
  margin: 0 16px;
  padding: 0 8px;
  background: var(--panel-inset);
  border: 1px solid var(--bezel);
  border-radius: var(--radius);
}

.omni:focus-within {
  border-color: var(--accent);
  box-shadow: 0 0 0 1px var(--accent-soft);
}

.omni-input {
  flex: 1;
  min-width: 0;
  padding: 6px 0;
  border: 0;
  background: transparent;
  color: var(--text-primary);
  font-family: var(--font-body);
  font-size: 12px;
}

.omni-input:focus { outline: none; }
.omni-input::placeholder { color: var(--text-label); }

/* The shortcut, printed on the box. A keyboard affordance nobody is told about is
   a keyboard affordance nobody uses. */
.omni-key {
  flex-shrink: 0;
  padding: 1px 5px;
  border: 1px solid var(--bezel-light);
  border-radius: var(--radius);
  color: var(--text-label);
  font-family: var(--font-mono);
  font-size: 10px;
}

.omni:focus-within .omni-key { opacity: 0; }

.capture-page { max-width: 640px; }

.capture-echo {
  margin: 0 0 14px;
  font-size: 12px;
  color: var(--text-dim);
}

.capture-said {
  padding: 1px 6px;
  background: var(--panel-inset);
  border: 1px solid var(--bezel-light);
  font-family: var(--font-mono);
  color: var(--text-primary);
}

.capture-lead {
  margin: 0 0 14px;
  font-size: 13px;
  color: var(--text-primary);
}

.capture-choices { display: flex; flex-wrap: wrap; gap: 8px; margin-bottom: 14px; }

.capture-examples {
  margin: 0 0 14px;
  padding: 0;
  list-style: none;
  display: flex;
  flex-direction: column;
  gap: 10px;
}

.capture-example-label {
  display: block;
  margin-bottom: 2px;
  font-family: var(--font-mono);
  font-size: 9px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--text-label);
}

/* The example is a button that runs itself. Reading the shape teaches it; pressing
   it proves it, on the founder's own board, without anything being written. */
.capture-example {
  padding: 6px 9px;
  width: 100%;
  text-align: left;
  background: var(--panel-inset);
  border: 1px solid var(--bezel-light);
  border-radius: var(--radius);
  color: var(--text-primary);
  cursor: pointer;
  font-family: var(--font-mono);
  font-size: 11px;
}

.capture-example:hover { border-color: var(--accent); background: var(--panel-raised); }

/* The omnibox is the first thing to go when the bar gets tight: the topbar already
   has to hold a view title and a clock, and a 90px search box helps nobody. It stays
   reachable at /capture, which the thumb row does not carry either — the box is a
   desk convenience, and a phone has a keyboard the founder is holding anyway. */
@media (max-width: 860px) {
  .omni { max-width: 220px; margin: 0 10px; }
  .omni-key { display: none; }
}

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

/* =========================================================
   THE CARD — one responsive layout, used wherever this cockpit lists things
   -------------------------------------------------------
   Rendered by templates/macros/card.html. Card markup used to be written per view
   as each view was built, so four surfaces each had their own answer to "title,
   chips, meta, buttons" and only some of them stacked at the breakpoint. The
   founder reads this on a phone, and a card that needs a sideways scroll is a card
   he skips.

   It is deliberately NOT applied to the Pipeline board or the Build Tracker. Those
   are a table and a rail: a table's columns line up down the page and a rail draws
   a dependency, and converting either into cards would delete the reading rather
   than improve it. Both already sit inside their own scroll container.

   COLOURS COME FROM THE TOKENS. The macro takes a WEIGHT (hot / warm / cool), never
   a colour, because in this skin --red is black and --amber is grey. A caller
   passing a hex would put a second palette in a template.
   ========================================================= */

.gims-card {
  display: flex;
  align-items: center;
  gap: 10px;
  /* A touch target, not a text height. 44px is the floor a thumb needs, and the
     padding is what gets a one-line card there without a min-height that would
     stretch an empty one. */
  min-height: 44px;
  padding: 8px 10px;
  margin-bottom: 4px;
  background: var(--panel-inset);
  border: 1px solid transparent;
  border-radius: var(--radius);
  transition: border-color 0.12s ease, background 0.12s ease;
}

.gims-card:last-of-type { margin-bottom: 0; }

.gims-card:hover,
.gims-card:focus-within {
  border-color: var(--bezel-light);
  background: var(--panel-raised);
}

/* Three weights and no fourth. See gims/attention.py, BANDS: the founder reads a
   stripe, not a number, and a fourth shade is a distinction nobody can see at
   arm's length on a phone. */
.gims-card.band-hot  { box-shadow: inset 4px 0 0 var(--red);         background: var(--panel-raised); }
.gims-card.band-warm { box-shadow: inset 3px 0 0 var(--amber); }
.gims-card.band-cool { box-shadow: inset 2px 0 0 var(--bezel-light); }

.gims-card-body {
  flex: 1;
  min-width: 0;
  display: flex;
  align-items: center;
  gap: 10px;
  color: inherit;
  text-decoration: none;
}

.gims-card-text {
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 1px;
}

/* Both lines truncate rather than wrap. A list whose rows are different heights
   cannot be scanned down its left edge, which is the only way a list like this is
   ever read. `min-width: 0` on every flex ancestor is what makes the ellipsis
   actually appear -- without it a flex item refuses to shrink below its content
   and the row overflows instead. */
.gims-card-title,
.gims-card-sub {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.gims-card-title {
  font-size: 13px;
  font-weight: 600;
  color: var(--text-primary);
}

.band-hot .gims-card-title { color: var(--text-bright); }

.gims-card-sub { font-size: 11px; color: var(--text-dim); }

.gims-card-chips {
  flex-shrink: 0;
  display: flex;
  flex-wrap: wrap;
  gap: 4px;
}

.gims-card-chip {
  padding: 2px 7px;
  border: 1px solid var(--bezel-light);
  border-radius: var(--radius);
  font-family: var(--font-mono);
  font-size: 9px;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--text-label);
  white-space: nowrap;
}

.band-hot  .gims-card-chip { border-color: var(--ink);   color: var(--text-bright); }
.band-warm .gims-card-chip { border-color: var(--amber); }

.gims-card-meta {
  flex-shrink: 0;
  display: flex;
  align-items: baseline;
  gap: 10px;
  font-family: var(--font-mono);
  font-size: 10px;
  letter-spacing: 0.08em;
  color: var(--text-label);
}

.band-hot .gims-card-meta { color: var(--text-primary); }

.gims-card-actions {
  flex-shrink: 0;
  display: flex;
  align-items: center;
  gap: 6px;
}

/* A grid of cards rather than a stack, for the views that show many at once. The
   360px floor is what stops a two-column layout from producing a card too narrow
   to read a title in. */
.gims-card-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(360px, 1fr));
  gap: 8px;
}

/* THE PHONE. Everything stacks, the buttons go full width, and nothing scrolls
   sideways.

   860, not the 760 the work order named. 860 is where this layout actually
   switches -- the sidebar leaves, the mobile nav arrives, the content padding
   changes -- and a card family with its own breakpoint 100px inside that would
   restack while the page was still in its desktop shell. One mobile boundary. */
@media (max-width: 860px) {
  .gims-card {
    flex-wrap: wrap;
    align-items: stretch;
  }

  .gims-card-body {
    flex: 1 1 100%;
    flex-direction: column;
    align-items: flex-start;
    gap: 4px;
  }

  .gims-card-text,
  .gims-card-chips,
  .gims-card-meta { width: 100%; }

  /* The meta's two halves push apart rather than stacking: they are short, and
     side by side they read as one line rather than as two facts. */
  .gims-card-meta { justify-content: space-between; }

  .gims-card-actions { flex: 1 1 100%; }
  .gims-card-actions > * { flex: 1; }
  .gims-card-actions .cmd-btn { width: 100%; }

  .gims-card-grid { grid-template-columns: 1fr; }
}

/* =========================================================
   THE THUMB ROW — the five most-used views, where a thumb can reach them
   -------------------------------------------------------
   Phone only. On a desktop the sidebar is the navigation and this is hidden.

   IT IS NOT THE FIRST BOTTOM BAR. `.mobile-nav` above is already fixed to the
   bottom at 860px -- the work order describes the current navigation as a row of
   links at the TOP, which it is not. So this does not replace that scroller, it
   sits under it and takes over the five views it carries: those pills hide at this
   breakpoint (`[data-thumb]`) and the other eight stay, so all thirteen views are
   still one tap away and none is listed twice.

   760, AND THE SEAM IT LEAVES. The card family and the gate strip switch at 860,
   where the layout actually changes. This row switches at 760, because CS5 says so
   in both its spec and its acceptance ("resize to 800px and confirm the thumb row
   is hidden"). Between 761 and 860 the scroller is the whole navigation, which is
   what it was before this row existed -- nothing breaks in the band, but the two
   numbers are a seam and this comment is where it is written down.
   ========================================================= */

.thumb-row {
  display: none;
  position: fixed;
  left: 0;
  right: 0;
  bottom: 0;
  z-index: 60;
  background: var(--panel);
  border-top: 2px solid var(--ink);
  padding-bottom: env(safe-area-inset-bottom);
}

@media (max-width: 760px) {
  .thumb-row {
    display: grid;
    grid-template-columns: repeat(5, 1fr);
  }

  /* The scroller lifts above this row and drops the five it carries. */
  .mobile-nav { bottom: 60px; border-bottom: 1px solid var(--bezel-light); }
  .mobile-nav-btn[data-thumb] { display: none; }

  /* Room for both bars. The 86px already there cleared the scroller alone; the
     thumb row is 60 more. Content hidden behind a fixed bar is the one bug a
     bottom bar reliably introduces. */
  .content { padding-bottom: 152px; }
  .footer { padding-bottom: 138px; }
}

.thumb-btn {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 3px;
  /* 60 minus the border. A thumb target, not a text height. */
  min-height: 58px;
  padding: 6px 2px;
  color: var(--text-dim);
  text-decoration: none;
  border-right: 1px solid var(--bezel-light);
}

.thumb-btn:last-child { border-right: none; }

/* Green on the view you are looking at, and nowhere else on this bar. It is the
   one spot colour in the skin, and a bar with two greens says nothing. */
.thumb-btn.active { color: var(--green); background: var(--green-soft); }

.thumb-icon {
  width: 24px;
  height: 24px;
  /* Stroked, not filled: the glyphs are pixel-aligned paths on a 24 grid, and a
     fill would turn an outline of a house into a black lozenge. */
  fill: none;
  stroke: currentColor;
  stroke-width: 2;
  stroke-linecap: square;
  stroke-linejoin: miter;
  shape-rendering: crispEdges;
}

.thumb-label {
  font-family: var(--font-display);
  font-size: 9px;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  white-space: nowrap;
}

/* The bars are chrome, not content. They must not appear on a printed call sheet. */
@media print {
  .thumb-row { display: none !important; }
}
