/* Chimera console.

   Warm off-white page, white cards, near-black text. Buttons are black
   pills; yellow is the single highlight, used only where something is
   urgent. No purple, no gradients for their own sake.

   Type is Outfit, vendored locally -- a geometric sans in the same family as
   the reference work, with no external font request at runtime. */

@font-face {
  font-family: "Outfit";
  src: url("/static/fonts/outfit-400.woff2") format("woff2");
  font-weight: 400; font-style: normal; font-display: swap;
}
@font-face {
  font-family: "Outfit";
  src: url("/static/fonts/outfit-500.woff2") format("woff2");
  font-weight: 500; font-style: normal; font-display: swap;
}
@font-face {
  font-family: "Outfit";
  src: url("/static/fonts/outfit-600.woff2") format("woff2");
  font-weight: 600; font-style: normal; font-display: swap;
}
@font-face {
  font-family: "Outfit";
  src: url("/static/fonts/outfit-700.woff2") format("woff2");
  font-weight: 700; font-style: normal; font-display: swap;
}

:root {
  --bg:       #F7F6F3;
  --panel:    #FFFFFF;
  --panel-2:  #FAFAF8;
  --chip:     #F3F2EE;
  --chip-hi:  #E8E8E2;
  --line:     #EAE8E2;

  --text:     #0D0D0F;
  --text-2:   #6B6B72;
  --text-3:   #9A9AA0;

  --ink:      #0D0D0F;      /* black pills and the hero card */
  --yellow:   #F2E85C;      /* the one highlight */

  --red:      #D93A2B;
  --amber:    #B26A00;
  --green:    #12855A;

  --geo: "Outfit", "Gilroy", "Avenir Next", -apple-system,
         BlinkMacSystemFont, "Segoe UI", sans-serif;

  --r-lg: 24px;
  --r-md: 18px;
  --r-chip: 14px;
  --sidebar: 268px;
  --ease: cubic-bezier(0.22, 1, 0.36, 1);
}

* { box-sizing: border-box; }

body {
  margin: 0;
  background: var(--bg);
  color: var(--text);
  font-family: var(--geo);
  font-size: 16px;
  line-height: 1.5;
  letter-spacing: -0.008em;
  -webkit-font-smoothing: antialiased;
}

.num, td.num, .metric-value, .money, .figure-value {
  font-variant-numeric: tabular-nums;
  font-feature-settings: "tnum" 1;
}

.money { color: var(--green); }

:focus-visible {
  outline: 2px solid var(--ink);
  outline-offset: 3px;
  border-radius: 10px;
}

/* ---------- Shell ---------- */

.shell {
  display: grid;
  grid-template-columns: var(--sidebar) 1fr;
  min-height: 100vh;
  gap: 14px;
  padding: 14px;
}

.sidebar {
  background: var(--panel);
  border-radius: var(--r-lg);
  padding: 22px 16px;
  display: flex;
  flex-direction: column;
  gap: 26px;
  position: sticky;
  top: 14px;
  height: calc(100vh - 28px);
  overflow-y: auto;
}

.brand { display: flex; align-items: center; gap: 12px; padding: 4px 8px 0; }
.brand-mark { font-size: 20px; font-weight: 700; letter-spacing: -0.03em; }
.brand-sub { font-size: 15px; color: var(--text-3); }

.nav { display: flex; flex-direction: column; gap: 4px; }
.nav-label { font-size: 13px; color: var(--text-3); padding: 16px 10px 6px; font-weight: 500; }

.nav-item {
  display: flex;
  align-items: center;
  gap: 14px;
  padding: 7px 10px 7px 7px;
  border-radius: var(--r-chip);
  color: var(--text-2);
  border: none;
  background: none;
  font: inherit;
  font-size: 16px;
  cursor: pointer;
  text-align: left;
  width: 100%;
  transition: color 160ms var(--ease), background 160ms var(--ease);
}

.nav-ico {
  flex: none;
  width: 42px; height: 42px;
  border-radius: var(--r-chip);
  background: var(--chip);
  display: grid; place-items: center;
  transition: background 160ms var(--ease), color 160ms var(--ease);
}

.nav-ico svg { width: 19px; height: 19px; stroke: currentColor; fill: none;
  stroke-width: 1.7; stroke-linecap: round; stroke-linejoin: round; }

.nav-item:hover { color: var(--text); }
.nav-item:hover .nav-ico { background: var(--chip-hi); }

.nav-item[aria-current="true"] { color: var(--text); font-weight: 500; }
.nav-item[aria-current="true"] .nav-ico { background: var(--ink); color: #fff; }

.nav-badge {
  margin-left: auto;
  font-size: 14px;
  color: var(--text-3);
  background: var(--chip);
  border-radius: 999px;
  min-width: 26px; height: 26px;
  padding: 0 8px;
  display: grid; place-items: center;
  font-variant-numeric: tabular-nums;
}

/* Phone-only members of the navigation, inert at every other width.

   `.nav-more` is the fifth tab: eight sections do not fit a thumb, so four
   reach the tab bar and the rest live one tap away in the sheet the same
   `.sidebar` element becomes when `data-expanded` is set. One nav in the DOM,
   two skins -- so `show()` keeps working, the badges keep updating, and there
   is no second copy of the section list to drift. */
.nav-more, .nav-user, .sheet-grip, .nav-scrim { display: none; }

/* `.nav-item` strips the user-agent button chrome; `.nav-more` is a button too
   and needs the same, or the fifth tab renders inside a grey bevelled box. */
.nav-more {
  border: none;
  background: none;
  font: inherit;
  color: var(--text-2);
  border-radius: var(--r-chip);
  cursor: pointer;
  transition: color 160ms var(--ease);
}

.nav-more:hover { color: var(--text); }
.nav-more[aria-expanded="true"] { color: var(--text); font-weight: 500; }
.nav-more[aria-expanded="true"] .nav-ico { background: var(--ink); color: #fff; }

/* How many posts are waiting on a human decision, on the More tab.

   An alert, not a counter: `.nav-badge` reads "36,347" on the Roster and is
   correctly hidden on the bar, but this number is 0 to 3 and it is the one
   time-sensitive thing living behind More. Posting is sheet-only -- the bar
   holds five and the daily loop is Call queue to Outreach -- so without this
   the operator would have to remember to look.

   Yellow, which this stylesheet spends only where something is urgent and
   actionable. A person owing a decision on a post nobody else can approve is
   exactly that. Black on yellow, so it does not rest on colour alone. */
.nav-alert {
  position: absolute;
  top: -4px; right: -6px;
  min-width: 18px; height: 18px;
  padding: 0 5px;
  border-radius: 999px;
  background: var(--yellow);
  color: #0D0D0F;
  border: 2px solid var(--panel);
  font-size: 11px; font-weight: 700; line-height: 14px;
  text-align: center;
  font-variant-numeric: tabular-nums;
}

.nav-alert[hidden] { display: none; }

/* The badge is positioned against the icon tile, so the tile is the
   containing block. `.nav-ico` is shared with every nav item; giving it
   `position: relative` costs nothing where no badge is rendered. */
.nav-ico { position: relative; }

/* The tab bar has ~68px a tab and the sidebar has 268px. "Call queue" fits one
   of those. `.nav-short` carries the label the bar can show; `aria-label` on
   the button carries the real name at every width, so which span is visible
   never decides what a screen reader announces. */
.nav-short { display: none; }

.main { min-width: 0; }

.topbar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  padding: 6px 8px 20px;
}

.topbar h1 { margin: 0; font-size: 20px; font-weight: 500; letter-spacing: -0.02em; }
.topbar-actions { display: flex; align-items: center; gap: 10px; }

/* ---------- Buttons ---------- */

.btn {
  font: inherit;
  font-size: 15px;
  padding: 11px 20px;
  border-radius: 999px;
  border: 1px solid var(--line);
  background: var(--panel);
  color: var(--text);
  cursor: pointer;
  transition: background 160ms var(--ease), transform 160ms var(--ease);
}

.btn:hover { background: var(--chip); }
.btn:active { transform: scale(0.98); }

.btn-primary { background: var(--ink); border-color: var(--ink); color: #fff; font-weight: 500; }
.btn-primary:hover { background: #26262A; }
.btn-sm { padding: 8px 15px; font-size: 14px; }
.kbd { font-size: 13px; color: var(--text-3); margin-left: 4px; }

/* A topbar action carries both a word and a glyph and shows one of them: the
   word down to 900px, the glyph below it, where four labelled pills do not fit
   a phone's width and pushed `Sign out` off the right edge. The accessible
   name is on the button's `aria-label` at every width, so hiding the visible
   label never hides the name. */
.btn-ico { display: none; }
.btn-ico svg { width: 18px; height: 18px; stroke: currentColor; fill: none;
  stroke-width: 1.8; stroke-linecap: round; stroke-linejoin: round; }

.section { padding: 0 8px 80px; }
.section[hidden] { display: none; }

/* ---------- Hero ---------- */

/* Black card on a white page. The one moment of high contrast, so it reads
   as the thing to act on. */
.detail-head { padding: 40px 40px 36px; background: var(--ink); color: #fff; }

.eyebrow { font-size: 15px; color: rgba(255, 255, 255, 0.62); font-weight: 500; margin-bottom: 10px; }

.next-call-name {
  font-size: clamp(26px, 2.4vw, 34px);
  font-weight: 500;
  letter-spacing: -0.022em;
  line-height: 1.18;
  margin: 0 0 14px;
  max-width: 22ch;
}

.next-call-why {
  color: rgba(255, 255, 255, 0.7);
  margin: 0 0 28px;
  font-size: 17px;
  line-height: 1.45;
  max-width: 54ch;
}

.next-call-figures { display: flex; flex-wrap: wrap; gap: 40px; margin-bottom: 28px; }
.figure-label { font-size: 14px; color: rgba(255, 255, 255, 0.55); margin-bottom: 3px; }
.figure-value { font-size: 24px; font-weight: 600; letter-spacing: -0.022em; line-height: 1.1; }
.detail-head .money { color: #fff; }

/* Yellow earns its place only on the deadline. */
.detail-head .pill {
  background: var(--yellow);
  color: #0D0D0F;
  font-weight: 600;
  border-radius: 999px;
  padding: 4px 12px;
  font-size: 15px;
}

.next-call-actions { display: flex; flex-wrap: wrap; gap: 10px; }
.detail-head .btn { background: rgba(255, 255, 255, 0.12); border-color: transparent; color: #fff; }
.detail-head .btn:hover { background: rgba(255, 255, 255, 0.2); }
.detail-head .btn-primary { background: #fff; color: var(--ink); }
.detail-head .btn-primary:hover { background: rgba(255, 255, 255, 0.88); }

/* ---------- Stat cards ---------- */

.stats {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(268px, 1fr));
  gap: 14px;
  margin-bottom: 14px;
}

.stat {
  background: var(--panel);
  border-radius: var(--r-lg);
  padding: 20px 22px 18px;
  display: flex;
  flex-direction: column;
  gap: 14px;
  min-width: 0;
}

.stat-head { display: flex; align-items: flex-start; gap: 12px; }

.stat-ico {
  flex: none;
  width: 34px; height: 34px;
  border-radius: 11px;
  background: var(--chip);
  display: grid; place-items: center;
  color: var(--text-2);
}

.stat-ico svg { width: 16px; height: 16px; stroke: currentColor; fill: none;
  stroke-width: 1.8; stroke-linecap: round; stroke-linejoin: round; }

.stat-titles { min-width: 0; flex: 1; }
.stat-title { font-size: 15px; font-weight: 500; }
.stat-sub { font-size: 12.5px; color: var(--text-3); margin-top: 1px; }

.stat-expand {
  flex: none;
  width: 30px; height: 30px;
  border-radius: 999px;
  border: none;
  background: var(--chip);
  color: var(--text-3);
  cursor: pointer;
  display: grid; place-items: center;
  transition: background 150ms var(--ease), color 150ms var(--ease);
}

.stat-expand:hover { background: var(--ink); color: #fff; }
.stat-expand svg { width: 14px; height: 14px; stroke: currentColor; fill: none;
  stroke-width: 1.9; stroke-linecap: round; stroke-linejoin: round; }

.stat-figure { display: flex; align-items: center; gap: 10px; }
.stat-value { font-size: 32px; font-weight: 600; letter-spacing: -0.03em; line-height: 1;
  font-variant-numeric: tabular-nums; }

.dot { width: 9px; height: 9px; border-radius: 999px; flex: none; }
.dot.ok { background: var(--green); }
.dot.warn { background: var(--amber); }
.dot.bad { background: var(--red); }
.dot.idle { background: var(--text-3); }

.spark { display: flex; align-items: flex-end; gap: 2px; height: 44px; }
.spark span { flex: 1; min-width: 2px; border-radius: 2px 2px 0 0;
  background: var(--chip-hi); min-height: 2px; }
.spark span[data-heat="now"]  { background: var(--red); }
.spark span[data-heat="hot"]  { background: var(--amber); }
.spark span[data-heat="near"] { background: #B9B9B2; }
.spark span[data-heat="far"]  { background: #DCDCD5; }

.spark-foot { display: flex; justify-content: space-between; gap: 10px;
  font-size: 12px; color: var(--text-3); margin-top: -6px; }
.spark-foot b { display: block; color: var(--text); font-weight: 600;
  font-variant-numeric: tabular-nums; font-size: 13px; }
.spark-foot > span:last-child { text-align: right; }

/* ---------- Cliff banner + countdown ---------- */

.cliff {
  background: var(--ink);
  color: #fff;
  border-radius: var(--r-lg);
  padding: 26px 28px;
  margin-bottom: 14px;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 20px;
}

.cliff-title { font-size: 15px; color: rgba(255, 255, 255, 0.6); }
.cliff-h { font-size: 22px; font-weight: 500; letter-spacing: -0.022em; margin: 6px 0 0; }
.cliff-sub { font-size: 14px; color: rgba(255, 255, 255, 0.55); margin-top: 5px; }

.countdown { display: flex; align-items: flex-end; gap: 10px; }

.cd-unit {
  background: var(--yellow);
  color: #0D0D0F;
  border-radius: 16px;
  padding: 11px 15px;
  min-width: 66px;
  text-align: center;
}

.cd-num { font-size: 27px; font-weight: 700; letter-spacing: -0.04em; line-height: 1;
  font-variant-numeric: tabular-nums; }
.cd-cap { font-size: 11.5px; color: rgba(13, 13, 15, 0.6); margin-top: 3px; font-weight: 500; }
.cd-sep { font-size: 22px; color: rgba(255, 255, 255, 0.3); padding-bottom: 18px; }

/* ---------- Rings ---------- */

.rings { display: flex; flex-wrap: wrap; gap: 26px; }
.ring { display: flex; flex-direction: column; align-items: center; gap: 7px; }
.ring-svg { width: 62px; height: 62px; transform: rotate(-90deg); }
.ring-track { fill: none; stroke: var(--chip); stroke-width: 5; }
.ring-arc { fill: none; stroke-width: 5; stroke-linecap: round;
  transition: stroke-dashoffset 700ms var(--ease); }
.ring-wrap { position: relative; display: grid; place-items: center; }
.ring-label { position: absolute; font-size: 14px; font-weight: 600;
  font-variant-numeric: tabular-nums; }
.ring-cap { font-size: 12.5px; color: var(--text-3); }

.delta { font-size: 13px; font-weight: 600; border-radius: 999px; padding: 3px 9px;
  font-variant-numeric: tabular-nums; }
.delta.up { background: rgba(18, 133, 90, 0.12); color: var(--green); }
.delta.down { background: rgba(217, 58, 43, 0.12); color: var(--red); }
.delta.flat { background: var(--chip); color: var(--text-3); }

/* Channel permission chips (outreach gate).

   Whether voice, email and mail are open is the first thing an operator needs
   and the last thing that should require reading. These previously borrowed
   `.nav-badge` -- the sidebar counter -- which stretched them and rendered
   "permitted" and "refused" in identical grey. Same tinted-background,
   coloured-text pattern as `.delta`, plus a dot so the state survives a
   greyscale print and does not rest on colour alone (WCAG 1.4.1). */
.perm-chip {
  display: inline-flex; align-items: center; gap: 7px;
  font-size: 13px; font-weight: 600;
  border-radius: 999px; padding: 4px 11px;
  white-space: nowrap;
}
.perm-chip .perm-dot {
  width: 7px; height: 7px; border-radius: 50%;
  background: currentColor; flex: none;
}
.perm-chip.ok  { background: rgba(18, 133, 90, 0.12); color: var(--green); }
.perm-chip.bad { background: rgba(217, 58, 43, 0.12); color: var(--red); }

/* The chips were separated by a text space, which at phone width let a chip
   break onto a line with no gap above it. A flex row wraps them deliberately:
   this is the first thing an operator reads and the last that should require
   effort. */
.perm-row { display: flex; flex-wrap: wrap; gap: 8px; }

/* ---------- Workspace ---------- */

.workspace {
  display: grid;
  grid-template-columns: minmax(320px, 400px) 1fr;
  gap: 14px;
  align-items: start;
}

.pane { background: var(--panel); border-radius: var(--r-lg); min-width: 0; }
.pane-list { display: flex; flex-direction: column; max-height: calc(100vh - 132px); }
.pane-head { padding: 18px 18px 12px; display: flex; flex-direction: column; gap: 12px; }

.search { display: flex; align-items: center; gap: 10px; background: var(--chip);
  border-radius: 14px; padding: 11px 14px; }
.search svg { width: 17px; height: 17px; flex: none; stroke: var(--text-3);
  fill: none; stroke-width: 1.8; stroke-linecap: round; }
.search input { border: none; background: none; color: var(--text); font: inherit;
  font-size: 15px; width: 100%; outline: none; }
.search input::placeholder { color: var(--text-3); }

.filters { display: flex; gap: 7px; overflow-x: auto; padding-bottom: 2px; }

.filter {
  font: inherit;
  font-size: 14px;
  white-space: nowrap;
  border: none;
  background: var(--chip);
  color: var(--text-2);
  border-radius: 999px;
  padding: 7px 15px;
  cursor: pointer;
  transition: background 150ms var(--ease), color 150ms var(--ease);
}

.filter:hover { background: var(--chip-hi); color: var(--text); }
.filter[aria-selected="true"] { background: var(--ink); color: #fff; font-weight: 500; }

.pane-body { overflow-y: auto; padding: 4px 10px 12px; display: flex;
  flex-direction: column; gap: 6px; }

.org-card {
  text-align: left; border: none; background: none; font: inherit; color: inherit;
  width: 100%; padding: 13px 14px; border-radius: 16px; cursor: pointer;
  display: grid; grid-template-columns: 1fr auto; gap: 4px 12px;
  transition: background 140ms var(--ease);
}

.org-card:hover { background: var(--panel-2); }
.org-card[aria-selected="true"] { background: var(--chip); }
.org-card .t { font-size: 15.5px; font-weight: 500; letter-spacing: -0.015em; }
.org-card .s { font-size: 13px; color: var(--text-3); grid-column: 1 / -1; }
.org-card .v { font-size: 14px; color: var(--green); font-variant-numeric: tabular-nums; }

.pane-detail { padding: 0; overflow: hidden; }

/* Return to the list. Hidden above the phone tier, where both panes are on
   screen at once and there is nothing to go back to. */
.pane-back {
  display: none;
  align-items: center; gap: 8px;
  width: 100%; min-height: 44px; padding: 12px 14px;
  border: none; background: none; color: var(--text-2);
  font: inherit; font-size: 15px; font-weight: 500;
  text-align: left; cursor: pointer;
}
.pane-back svg { width: 16px; height: 16px; flex: none; stroke: currentColor;
  fill: none; stroke-width: 2; stroke-linecap: round; stroke-linejoin: round; }
.pane-back:hover { color: var(--text); }

.detail-empty { padding: 80px 32px; text-align: center; color: var(--text-3); }
.detail-body { padding: 26px 34px 32px; display: flex; flex-direction: column; gap: 22px; }
.detail-row { display: flex; flex-wrap: wrap; gap: 34px; }
.detail-block .k, .detail-body .k { font-size: 13.5px; color: var(--text-3); margin-bottom: 4px; }
.detail-block .v { font-size: 19px; font-weight: 600; letter-spacing: -0.02em; }
.assumptions { display: flex; flex-wrap: wrap; align-items: flex-end; gap: 14px; }

/* ---------- Tables ---------- */

.table-wrap { overflow-x: auto; background: var(--panel); border-radius: var(--r-lg);
  padding: 6px 8px; }

table { width: 100%; border-collapse: collapse; font-size: 15px; }

th {
  text-align: left; font-size: 13px; font-weight: 500; color: var(--text-3);
  padding: 16px 18px 12px; white-space: nowrap;
  position: sticky; top: 0; background: var(--panel);
}

td { padding: 15px 18px; border-top: 1px solid var(--line); }
tbody tr:first-child td { border-top: none; }
tbody tr { transition: background 140ms var(--ease); }
tbody tr:hover { background: var(--panel-2); }
td.num, th.num { text-align: right; }

.rank { color: var(--text-3); font-variant-numeric: tabular-nums; }
.org-name { font-weight: 500; }
.org-why { color: var(--text-3); font-size: 13.5px; margin-top: 3px; }

/* Group heading over a table or an accordion. Was an inline style repeated in
   two renderers, both colouring it `var(--faint)` -- a token this stylesheet
   has never defined, so the declaration was dropped and the heading rendered
   at full black instead of the intended grey. */
.group-head {
  font-size: 11px; text-transform: uppercase; letter-spacing: 0.18em;
  color: var(--text-3); font-weight: 500; margin: 22px 0 10px;
}

/* ---------- Accordion ----------

   Capabilities is a 36-subsystem reference table -- subsystem, state, setting
   count, and every key configuration value. Four columns of that at 375px is
   a horizontal scroll through a wall of tags. On a phone it becomes one row
   per subsystem with the configuration behind a native `<details>`
   disclosure: no JavaScript, keyboard operable, and open-by-find works. */

.acc { display: flex; flex-direction: column; gap: 6px; }

.acc-item { background: var(--panel); border-radius: var(--r-md); }

.acc-sum {
  display: flex; align-items: center; gap: 10px;
  padding: 12px 15px; min-height: 44px;
  cursor: pointer; list-style: none;
  font-size: 15px; font-weight: 500;
}

/* Both spellings: WebKit still ships the pseudo-element, everyone else honours
   `list-style: none`. */
.acc-sum::-webkit-details-marker { display: none; }
.acc-sum::marker { content: ""; }

.acc-name { flex: 1; min-width: 0; }
.acc-count { font-size: 13px; color: var(--text-3); font-variant-numeric: tabular-nums; }

.acc-chev { flex: none; width: 16px; height: 16px; color: var(--text-3);
  transition: transform 160ms var(--ease); }
.acc-chev svg { width: 16px; height: 16px; stroke: currentColor; fill: none;
  stroke-width: 1.9; stroke-linecap: round; stroke-linejoin: round; }
.acc-item[open] .acc-chev { transform: rotate(90deg); }

.acc-body { padding: 0 15px 13px; }
.acc-body .k { font-size: 12.5px; color: var(--text-3); margin-bottom: 6px; }

.pill {
  display: inline-block; font-size: 14px; padding: 4px 11px; border-radius: 999px;
  white-space: nowrap; font-variant-numeric: tabular-nums;
  background: var(--chip); color: var(--text-2);
}

.pill[data-heat="now"]  { background: var(--yellow); color: #0D0D0F; font-weight: 600; }
.pill[data-heat="hot"]  { background: rgba(178, 106, 0, 0.12); color: var(--amber); font-weight: 500; }
.pill[data-heat="near"] { background: var(--chip); color: var(--text-2); }
.pill[data-heat="far"]  { background: var(--chip); color: var(--text-3); }
.pill[data-heat="none"] { background: var(--chip); color: var(--text-3); }

.stage-select, .field input, .field select {
  font: inherit; font-size: 15px;
  background: var(--chip); color: var(--text);
  border: 1px solid transparent; border-radius: 12px; padding: 9px 13px;
  transition: border-color 160ms var(--ease);
}

.stage-select:focus, .field input:focus, .field select:focus {
  border-color: var(--ink); outline: none;
}

.controls {
  display: flex; flex-wrap: wrap; align-items: flex-end; gap: 16px;
  margin-bottom: 16px; padding: 22px 24px;
  background: var(--panel); border-radius: var(--r-lg);
}

.field { display: flex; flex-direction: column; gap: 7px; }
.field label { font-size: 14px; color: var(--text-3); }
.field input, .field select { min-width: 140px; }
.field-grow { flex: 1; min-width: 280px; }
.field-grow input { width: 100%; }

/* ---------- Config tags ---------- */

.tags { max-width: 620px; }

.tag {
  display: inline-flex; align-items: baseline; gap: 6px;
  background: var(--chip); border-radius: 8px; padding: 3px 9px;
  margin: 3px 5px 3px 0; font-size: 12.5px; color: var(--text);
  font-variant-numeric: tabular-nums;
}

.tag i { font-style: normal; color: var(--text-3); }
.muted { color: var(--text-3); }

.path {
  max-width: 520px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  color: var(--text-2); font-size: 13.5px; direction: rtl; text-align: left;
}

/* ---------- Empty & status ---------- */

.empty {
  background: var(--panel); border-radius: var(--r-lg);
  padding: 68px 32px; text-align: center; color: var(--text-2);
}

.empty h3 { margin: 0 0 10px; color: var(--text); font-size: 24px; font-weight: 700;
  letter-spacing: -0.03em; }
.empty p { margin: 0 auto 20px; max-width: 54ch; font-size: 16px; }

.toast {
  position: fixed; right: 24px; bottom: 24px;
  background: var(--ink); color: #fff;
  border-radius: 16px; padding: 15px 20px; max-width: 420px; z-index: 100;
  font-size: 15px; box-shadow: 0 16px 40px rgba(0, 0, 0, 0.18);
}

.toast[data-kind="error"] { background: var(--red); }
.toast[hidden] { display: none; }

/* ---------- Command palette ---------- */

.palette-backdrop {
  position: fixed; inset: 0;
  background: rgba(13, 13, 15, 0.3); backdrop-filter: blur(6px);
  display: flex; align-items: flex-start; justify-content: center;
  padding-top: 14vh; z-index: 200;
}

.palette-backdrop[hidden] { display: none; }

.palette {
  width: min(600px, 92vw); background: var(--panel); border-radius: 22px;
  overflow: hidden; box-shadow: 0 24px 70px rgba(0, 0, 0, 0.18);
}

.palette input {
  width: 100%; font: inherit; font-size: 18px; padding: 20px 24px;
  background: transparent; color: var(--text); border: none;
  border-bottom: 1px solid var(--line);
}

.palette input:focus { outline: none; }
.palette-list { list-style: none; margin: 0; padding: 10px; max-height: 340px; overflow-y: auto; }

.palette-item {
  padding: 12px 15px; border-radius: 13px; cursor: pointer;
  display: flex; justify-content: space-between; gap: 12px;
  font-size: 15px; color: var(--text-2);
}

.palette-item[aria-selected="true"] { background: var(--ink); color: #fff; font-weight: 500; }
.palette-hint { color: var(--text-3); font-size: 13.5px; }
.palette-item[aria-selected="true"] .palette-hint { color: rgba(255, 255, 255, 0.6); }

/* ---------- Responsive ----------

   Three tiers, and the one that carries the work is the last. This console is
   used standing up, phone in hand, about to dial, so the two screens that
   serve that -- Call queue and Outreach -- get a single-column list-to-detail
   flow rather than a two-pane workspace squeezed to nothing.

   1180  the workspace stops being two columns.
    900  the sidebar becomes ONE scrolling row and the topbar's actions become
         glyphs. The keyboard hint goes with them.
    640  the phone. Navigation leaves the scroll flow entirely for a bottom tab
         bar, every control clears 44px, and text inputs reach 16px so iOS
         stops zooming the page when one takes focus. */

@media (max-width: 1180px) {
  .workspace { grid-template-columns: 1fr; }
  .pane-list { max-height: 420px; }
}

@media (max-width: 900px) {
  /* One column means two auto rows, and `min-height: 100vh` leaves the grid
     taller than its content on a short section. The default `align-content:
     stretch` then splits the slack between the rows, so the navigation grew to
     367px on Outreach and 142px on the Call queue -- the same bar, a different
     height per section. Rows size to content and pack at the top. */
  .shell { grid-template-columns: 1fr; align-content: start; padding: 10px; gap: 10px; }
  .sidebar { position: static; height: auto; flex-direction: row; flex-wrap: wrap;
    align-items: center; gap: 8px; }

  /* One row that scrolls, not eight rows that wrap. `flex-wrap: wrap` with
     `.nav-item { width: auto }` still gave every item a line of its own once
     the viewport was narrow enough -- the same vertical sidebar, relocated to
     the top, 476px of it before any content began. */
  .nav { flex-direction: row; flex-wrap: nowrap; gap: 4px;
    overflow-x: auto; scrollbar-width: none; }
  .nav::-webkit-scrollbar { width: 0; height: 0; }
  .nav-label { display: none; }
  .nav-item { width: auto; flex: none; white-space: nowrap; }

  .topbar { padding: 6px 4px 14px; gap: 10px; }
  .topbar h1 { flex: 1; min-width: 0; }
  .topbar-actions { flex: none; gap: 6px; }

  /* A keyboard shortcut, advertised on a device with no keyboard. Scoped to
     the palette button: `.kbd` is also how the outreach gate renders an
     artifact path and how an empty call list renders the CLI command that
     fills it, and hiding the class outright left "Artifact" followed by
     nothing at all. */
  #palette-open .kbd { display: none; }

  .btn-label { display: none; }
  .btn-ico { display: grid; }
  .btn.btn-icon { width: 44px; height: 44px; padding: 0; place-items: center; }

  .detail-head { padding: 26px 22px; }
  .next-call-figures { gap: 26px; }
}

/* ---------- Phone ----------

   No declaration in this block may impose a px width above 252. That is the
   narrowest phone (320) less the shell's own gutters (2 x 10) less the deepest
   card padding a control sits inside (2 x 24) -- the budget within which
   anything must fit or the page scrolls sideways. It was `.field-grow`'s
   280px, among others, that made `scrollWidth` 385 against a 375 viewport.
   `tests/web/test_console_mobile.py` computes the effective value of every
   width declaration at this tier and fails the build over one that does not
   fit. */

@media (max-width: 640px) {
  :root { --tabbar: 62px; }

  .shell {
    gap: 0;
    padding-top: 0;
    padding-left: max(10px, env(safe-area-inset-left, 0px));
    padding-right: max(10px, env(safe-area-inset-right, 0px));
    padding-bottom: calc(var(--tabbar) + env(safe-area-inset-bottom, 0px) + 10px);
  }

  /* --- Navigation: a bottom tab bar --- */

  /* Out of the scroll flow entirely. The complaint this answers is not that
     the nav was ugly but that it was 69% of the first screen: content began at
     582px on an 844px-tall phone. */
  .sidebar {
    position: fixed;
    inset: auto 0 0 0;
    z-index: 60;
    height: auto;
    flex-direction: column;
    /* Reset, not inherit. The 900px tier styles a centred, wrapping ROW, and
       both of its declarations misbehave once the direction is column:
       `align-items: center` left `.nav` at its content width -- a 200px tab bar
       floating in the middle of a 375px phone -- and `flex-wrap: wrap` against
       the sheet's capped height wrapped the nav into a SECOND COLUMN beside
       the operator's name. */
    align-items: stretch;
    flex-wrap: nowrap;
    gap: 0;
    padding: 5px 4px calc(5px + env(safe-area-inset-bottom, 0px));
    border-radius: 0;
    border-top: 1px solid var(--line);
    overflow: visible;
  }

  .brand { display: none; }
  .nav { flex-direction: row; flex-wrap: nowrap; overflow: visible; gap: 2px; }

  /* Four sections reach the bar; `data-tab` marks them in the markup. The
     other four are one tap away in the sheet. */
  .nav-item { display: none; }
  .nav-item[data-tab], .nav-more {
    display: flex;
    flex: 1 1 0;
    min-width: 0;
    flex-direction: column;
    align-items: center;
    justify-content: flex-start;
    gap: 3px;
    padding: 5px 2px 4px;
    min-height: 52px;
    font-size: 11px;
    line-height: 1.15;
    letter-spacing: 0;
    text-align: center;
  }

  .nav-item[data-tab] > span:not(.nav-ico):not(.nav-badge),
  .nav-more > span:not(.nav-ico) {
    max-width: 100%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  }

  /* Short label on the bar, full label everywhere else. */
  .nav-item[data-tab] .nav-short { display: block; }
  .nav-item[data-tab] .nav-name { display: none; }

  .nav-ico { width: 32px; height: 32px; border-radius: 11px; }
  .nav-ico svg { width: 17px; height: 17px; }

  /* The counters read "36,347". Honest in a sidebar, absurd as a superscript
     on a 70px tab -- and every one of them is already stated on the screen it
     describes. They come back in the sheet, which has the room. */
  .nav-badge { display: none; }

  /* --- Navigation: the overflow sheet --- */

  .sidebar[data-expanded="true"] {
    border-radius: 20px 20px 0 0;
    padding: 8px 10px calc(10px + env(safe-area-inset-bottom, 0px));
    max-height: 82vh;
    overflow-y: auto;
    box-shadow: 0 -18px 50px rgba(0, 0, 0, 0.16);
  }

  .sidebar[data-expanded="true"] .nav { flex-direction: column; gap: 2px; }

  .sidebar[data-expanded="true"] .nav-item,
  .sidebar[data-expanded="true"] .nav-more {
    display: flex;
    flex: none;
    width: 100%;
    flex-direction: row;
    align-items: center;
    justify-content: flex-start;
    gap: 14px;
    min-height: 52px;
    padding: 6px 10px;
    font-size: 16px;
    text-align: left;
  }

  .sidebar[data-expanded="true"] .nav-item > span:not(.nav-ico):not(.nav-badge),
  .sidebar[data-expanded="true"] .nav-more > span:not(.nav-ico) {
    white-space: normal; overflow: visible;
  }

  /* The sheet has the width for the real names, so it uses them. */
  .sidebar[data-expanded="true"] .nav-item .nav-name { display: block; }
  .sidebar[data-expanded="true"] .nav-item .nav-short { display: none; }

  .sidebar[data-expanded="true"] .nav-label { display: block; padding: 12px 10px 4px; }
  .sidebar[data-expanded="true"] .nav-badge { display: grid; }
  .sidebar[data-expanded="true"] .nav-ico { width: 40px; height: 40px; }
  .sidebar[data-expanded="true"] .nav-ico svg { width: 19px; height: 19px; }
  .sidebar[data-expanded="true"] .nav-user { display: block; }
  .sidebar[data-expanded="true"] .sheet-grip { display: block; }

  .sheet-grip {
    width: 38px; height: 4px; border-radius: 999px;
    background: var(--chip-hi); margin: 2px auto 6px; flex: none;
  }

  .nav-user {
    font-size: 13px; color: var(--text-3);
    padding: 2px 10px 6px;
  }

  .nav-scrim[data-on="true"] {
    display: block; position: fixed; inset: 0; z-index: 55;
    background: rgba(13, 13, 15, 0.3);
  }

  /* --- Topbar --- */

  /* Sticky, because the queue is a long list and Refresh should not be two
     thousand pixels above where the thumb is. */
  .topbar {
    position: sticky; top: 0; z-index: 40;
    background: var(--bg);
    padding: 10px 6px 12px;
  }

  .topbar h1 { font-size: 19px; }
  .topbar-user { display: none; }

  /* --- Density --- */

  .section { padding: 0 0 32px; }

  /* Three stat cards cannot sit beside each other at 375px, and `auto-fit`
     over a 268px track was already collapsing them to one. Saying so directly
     keeps the phone tier free of any px track width. */
  .stats { grid-template-columns: 1fr; gap: 10px; margin-bottom: 10px; }
  .stat { padding: 16px 16px 14px; }

  /* A 30px control with an `aria-label` of "Details" and no handler behind it
     anywhere in console.js. Rather than grow a dead target to 44px, it goes
     away at the width where space is scarcest. */
  .stat-expand { display: none; }

  .cliff { padding: 20px 18px; gap: 14px; }
  .countdown { gap: 8px; }
  .detail-head { padding: 24px 18px 22px; }
  .next-call-figures { gap: 20px; margin-bottom: 22px; }
  .next-call-name { font-size: 25px; }
  .next-call-why { font-size: 16px; }
  .detail-body { padding: 20px 18px 24px; gap: 18px; }
  .detail-row { gap: 18px; }
  .rings { gap: 18px; justify-content: space-between; }
  .empty { padding: 40px 20px; }
  .empty h3 { font-size: 20px; }

  /* The outreach gate explains itself with config paths, and
     `config/compliance/florida_authorization.example.yaml` is one word to a
     line-breaking algorithm. Wrapping it mid-token beats running it off the
     card, on the message an operator has to act on to make calls at all. */
  .empty p, .empty h3 { overflow-wrap: anywhere; }
  .controls { padding: 16px; gap: 12px; margin-bottom: 10px; }
  .pane-head { padding: 14px 14px 10px; }
  .table-wrap { padding: 4px; }
  th { padding: 14px 12px 10px; }
  td { padding: 13px 12px; }

  /* --- Fields --- */

  /* 280px inside a card padded 24px a side does not fit a 320px phone. This is
     the declaration that produced the sideways scroll.

     Dropping the minimum is not enough on its own: `.field-grow` still carries
     `flex: 1`, so in a wrapping row it took whatever was left at the end of a
     line -- 38px, for the outcome form's Notes field at 320px. Every control
     row on a phone is a column, and every field in it is full width. */
  .controls, .assumptions { flex-direction: column; align-items: stretch; }
  .field, .field-grow { flex: none; width: 100%; min-width: 0; }
  .field input, .field select { min-width: 0; width: 100%; }

  /* --- Touch targets, and iOS's 16px rule --- */

  /* Under 16px, Safari zooms the page when the field takes focus and does not
     zoom back out. Every text control here clears it. */
  .btn { min-height: 44px; padding: 11px 18px; }
  .btn-sm { min-height: 44px; padding: 10px 16px; font-size: 15px; }
  .filter { min-height: 44px; padding: 10px 16px; font-size: 15px; }
  .field input, .field select, .stage-select { min-height: 44px; font-size: 16px; }
  /* The height goes on the input, not on its wrapper. Padding on the wrapper
     makes the CARD 44px while the field a finger actually has to hit stays a
     26px line box, which is the sort of 44px that only measures true from the
     outside. */
  .search { min-height: 44px; padding: 0 14px; }
  .search input { min-height: 44px; font-size: 16px; }
  .palette input { font-size: 16px; }
  .palette-item { min-height: 44px; align-items: center; }
  .org-card { min-height: 56px; }
  .filters { gap: 6px; }

  /* --- Call queue and Outreach: list, then detail --- */

  /* One pane at a time. Stacking them meant scrolling past a 420px list to
     reach the record you had just selected, on the one screen that has to work
     with a phone in one hand. */
  .section[data-mobile-view="list"] .pane-detail { display: none; }
  .section[data-mobile-view="detail"] .pane-list { display: none; }

  /* In detail, the workspace is the screen. Everything else the section stacks
     above it -- the cliff banner, the rate cards, the outreach gate -- is a
     full screen of context that belongs to choosing a record, not to working
     one, and it is still a tap away on Back. */
  .section[data-mobile-view="detail"] > *:not(.workspace) { display: none; }

  /* No nested scroll area on a phone: the page scrolls, the panes do not. */
  .pane-list { max-height: none; }
  .pane-body { overflow-y: visible; padding: 4px 8px 10px; }

  .pane-back { display: flex; }

  /* --- Overlays --- */

  .toast {
    left: 12px; right: 12px; max-width: none;
    bottom: calc(var(--tabbar) + env(safe-area-inset-bottom, 0px) + 12px);
  }

  .palette-backdrop { padding-top: 8vh; }
  .palette { width: 94vw; }
  .palette-list { max-height: 50vh; }

  /* The login page's phone tier is NOT here. The Login section below is the
     last thing in this stylesheet, so its declarations come after this block
     in source order and win at equal specificity -- an override written here
     is simply not applied. It lives at the end of the file instead. */
}

@media (prefers-reduced-motion: reduce) {
  * { transition: none !important; animation: none !important; }
}

/* ---------- Login ----------

   The sign-in page is the same design language as the console, not a second
   one: it reuses `.field`, `.btn`, `.btn-primary`, `.brand-mark`,
   `.brand-sub` and the global `:focus-visible` ring rather than redefining a
   black pill. Only the page frame is new. */

.login-page {
  display: grid;
  place-items: center;
  min-height: 100vh;
  padding: 24px;
}

.login-card {
  width: 100%;
  max-width: 380px;
  background: var(--panel);
  border: 1px solid var(--line);
  border-radius: var(--r-lg);
  padding: 36px 32px 32px;
}

.login-brand {
  display: flex;
  align-items: baseline;
  gap: 10px;
  margin-bottom: 28px;
  font-family: var(--geo);
}

.login-form { display: flex; flex-direction: column; gap: 18px; }

.login-form .field input {
  width: 100%;
  font: inherit;
  font-size: 15px;
  padding: 11px 14px;
  border: 1px solid var(--line);
  border-radius: 12px;
  background: var(--panel);
  color: var(--text);
}

.login-submit { width: 100%; margin-top: 4px; }

.login-error {
  margin-bottom: 20px;
  padding: 11px 14px;
  border-radius: 12px;
  border: 1px solid var(--red);
  color: var(--red);
  font-size: 14px;
}

/* The login page on a phone.

   Deliberately at the very end of the file. This section's own declarations
   are the last in source order, so the same overrides written up in the main
   phone block lost to them at equal specificity and the sign-in field stayed
   at 15px -- which is exactly the size at which iOS Safari zooms the page on
   focus, on the first field an operator ever touches. */
@media (max-width: 640px) {
  .login-card { padding: 28px 22px 26px; }
  .login-form .field input { font-size: 16px; min-height: 44px; }
  .login-submit { min-height: 46px; }
}

/* Federated sign-in, shown on /login only when a provider is configured. */
.login-divider {
  display: flex;
  align-items: center;
  gap: 12px;
  margin: 20px 0 16px;
  color: var(--ink-6, #6b6b78);
  font-size: 13px;
}
.login-divider::before,
.login-divider::after {
  content: "";
  flex: 1;
  height: 1px;
  background: var(--border-2, #e2e0da);
}
.login-federated {
  display: flex;
  flex-direction: column;
  gap: 10px;
}
.login-federated .btn {
  text-align: center;
  text-decoration: none;
}

/* The posting gate's reasoning, one tap away.
   Measured at 390x844: the "No platform can post today" card was 560px and
   pushed the post list to top:1471px -- two phone screens of explanation
   before anything an operator could act on. The action stays on the face of
   the card; this holds the count, the ordering rule and the artifact path. */
.gate-why { margin-top: 10px; }
.gate-why > summary {
  cursor: pointer;
  list-style: none;
  display: inline-flex;
  align-items: center;
  gap: 6px;
  min-height: 44px;              /* thumb-sized, same floor as the nav */
  font-size: 13px;
  font-weight: 600;
  color: var(--ink-6, #6b6b78);
}
.gate-why > summary::-webkit-details-marker { display: none; }
.gate-why > summary::before {
  content: "";
  width: 0; height: 0;
  border: 5px solid transparent;
  border-left-color: currentColor;
  transition: transform 120ms ease;
}
.gate-why[open] > summary::before { transform: rotate(90deg) translateX(-2px); }
.gate-why > summary:focus-visible { outline: 2px solid var(--focus, #3b5bfd); outline-offset: 2px; }
.gate-why > p { margin: 0 0 8px; }

@media (prefers-reduced-motion: reduce) {
  .gate-why > summary::before { transition: none; }
}

/* On a phone, the posts come before the diagnostics.
   Measured at 390x844: `#posting-gate` rendered 743px and pushed
   `#posting-list` to top:1471px -- the operator opened Posting and got nearly
   two screens of "everything is blocked" before the first post they were there
   to decide about. Collapsing the gate's reasoning recovered ~120px; the rest
   is legitimate content ("Start with X", "Do this") that should not be hidden.
   So the fix is order, not deletion.
   Approving a post does not depend on whether dispatch is unblocked -- that is
   the owner's problem, on another day, at a desk. The gate keeps its full text
   one scroll below rather than two screens above. */
@media (max-width: 640px) {
  #section-posting { display: flex; flex-direction: column; }
  #section-posting > .workspace   { order: 1; }
  #section-posting > #posting-stats { order: 2; }
  #section-posting > #posting-gate  { order: 3; margin-top: 18px; }
}
