/* Trading control surface — dark, dense, mobile-first.
   No webfonts, no external assets: this must render under a strict CSP. */

:root {
  --bg:        #0a0d12;
  --surface:   #131922;
  --surface-2: #1a212c;
  --border:    #26303e;
  --border-hi: #3a4759;

  --text:      #e8edf4;
  --text-dim:  #9aa7b8;
  /* The floor for every one of these is 4.5:1 against the DARKEST panel it can
     land on, which is --surface-2 and not --bg. The faint step is the one that
     gets close: 4.67:1 there, 5.10:1 on --surface. It carries labels, stamps
     and the totals exclusion note, so it is body text and gets no large-text
     allowance. Darkening it again re-breaks a lot of small copy at once. */
  --text-faint:#7e8b9d;

  --gain:      #3ddc97;
  --gain-bg:   #0e2a20;
  --loss:      #ff6b6b;
  --loss-bg:   #331417;
  --flat:      #9aa7b8;

  --paper:     #4cc2ff;
  --paper-bg:  #0d2733;
  --live:      #ffb020;
  --live-bg:   #3a2606;

  --danger:    #ff4d4d;
  --danger-bg: #400f0f;
  --warn:      #ffb020;
  --warn-bg:   #33240a;

  /* Text that sits ON a tinted panel. The status colour itself is too
     saturated to read a paragraph in, so each one has a legible partner. */
  --on-gain:   #b8f5db;
  --on-loss:   #ffc9c9;
  --on-warn:   #ffe4b5;
  --on-danger: #ffdede;
  --on-danger-strong: #ff9a9a;
  --text-hi:   #ffffff;

  --accent:    #6ea8fe;
  --accent-bg: #17324a;
  --focus:     #6aa9ff;

  /* Tints drawn over a panel rather than beside it, so they carry alpha. */
  --live-stripe:  rgba(255, 176, 32, 0.10);
  --stale-stripe: rgba(154, 167, 184, 0.10);
  --gain-glow:    rgba(61, 220, 151, 0.22);
  --killbar-bg:   rgba(10, 13, 18, 0.97);
  --bg-live:      #100c05;
  --bg-paper:     #070d12;

  /* Heat shades for a calendar or matrix cell: four steps either side of zero
     plus the no-result step. They live in the token table rather than in the
     sheet that draws them because a colour literal is a value that silently
     cannot be re-themed, and these are the only shades on the console that a
     second theme would have to redefine wholesale. The shade is always a
     SECOND signal — the amount and its sign are in the cell regardless — which
     is why four steps is enough. The darkest steps do NOT hold ordinary text:
     measured, the faint grey falls to 2.3:1 on the strongest positive shade
     and the status colours to 4.09:1 on the strongest negative one, so the
     cells that use them get a legible partner below. */
  --heat-zero:       var(--surface);
  --heat-1-positive: #0d2119;
  --heat-2-positive: #113022;
  --heat-3-positive: #16452e;
  --heat-4-positive: #1b5c3a;
  --heat-1-negative: #2a1215;
  --heat-2-negative: #3b171b;
  --heat-3-negative: #4f1d22;
  --heat-4-negative: #66242a;

  --radius: 10px;
  --tap: 48px;
  /* The floor for anything a thumb has to hit. Nothing may go under it. */
  --tap-phone: 44px;

  /* The measure the page column is allowed to reach, widened by the ladder
     at the bottom of this file rather than pinned at one value. */
  --wrap-max: 1100px;

  --mono: ui-monospace, "SF Mono", "Cascadia Mono", "Segoe UI Mono", Menlo,
          Consolas, "Liberation Mono", monospace;
  --sans: system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue",
          Arial, sans-serif;
}

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

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

body {
  margin: 0;
  background: var(--bg);
  color: var(--text);
  font-family: var(--sans);
  font-size: 15px;
  line-height: 1.45;
  padding-bottom: 96px; /* room for the sticky kill bar */
}

a { color: var(--paper); }
/* Everything focusable, not a list of element types that a new control can
   fall outside of. */
a:focus-visible,
button:focus-visible,
input:focus-visible,
select:focus-visible,
textarea:focus-visible,
summary:focus-visible,
[tabindex]:focus-visible {
  outline: 3px solid var(--focus);
  outline-offset: 2px;
}
/* The kill bar is fixed to the bottom edge: without this the browser can
   scroll a focused control to exactly where the bar covers it. */
:focus-visible { scroll-margin-bottom: 130px; scroll-margin-top: 12px; }
/* #main is focusable only so the skip link can land on it; it is not a
   control and must not draw a ring when it receives that focus. */
#main:focus { outline: none; }

h1, h2, h3 { margin: 0; font-weight: 650; letter-spacing: -0.01em; }
h1 { font-size: 1.15rem; }
h2 { font-size: 1rem; }
h3 { font-size: 0.9rem; }

.wrap { max-width: var(--wrap-max); margin: 0 auto; padding: 0 clamp(10px, 1.2vw, 22px); }

/* ---------- which account is on screen ----------
   Two agents report into this dashboard and only one of them spends real
   money. Confusing them is the worst mistake this interface can invite, so the
   instance is stated in words in the bar below, tagged next to every figure it
   could belong to, and — because both of those scroll away — drawn as a frame
   around the whole viewport that never leaves. Colour is never the only
   signal: every one of those carries the word LIVE or PAPER as well. */

.instbar {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
  padding: 8px 12px;
  border-bottom: 1px solid var(--border);
  background: var(--surface-2);
}
.instbar__now {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 4px 8px;
  font-size: 0.82rem;
  color: var(--text-dim);
}
.instbar__now strong {
  font-weight: 800;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  font-size: 0.84rem;
}
.instbar__eyebrow {
  font-size: 0.68rem;
  text-transform: uppercase;
  letter-spacing: 0.12em;
  color: var(--text-faint);
}
.instbar--live { background: var(--live-bg); border-bottom-color: var(--live); }
.instbar--live .instbar__now strong { color: var(--live); }
.instbar--paper { background: var(--paper-bg); border-bottom-color: var(--paper); }
.instbar--paper .instbar__now strong { color: var(--paper); }

.instbar__pick { display: flex; gap: 6px; flex: 0 0 auto; }
.instbar__opt {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  min-height: var(--tap-phone);
  padding: 6px 14px;
  border-radius: 999px;
  border: 1px solid var(--border-hi);
  background: var(--bg);
  color: var(--text-dim);
  font-size: 0.78rem;
  font-weight: 800;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  text-decoration: none;
}
.instbar__opt:hover { color: var(--text); border-color: var(--text-faint); }
.instbar__opt--live.is-active {
  background: var(--live-bg);
  border-color: var(--live);
  color: var(--live);
  box-shadow: inset 0 0 0 1px var(--live);
}
.instbar__opt--paper.is-active {
  background: var(--paper-bg);
  border-color: var(--paper);
  color: var(--paper);
  box-shadow: inset 0 0 0 1px var(--paper);
}
.instbar__never {
  font-size: 0.66rem;
  font-weight: 700;
  letter-spacing: 0.06em;
  color: var(--text-faint);
  text-transform: none;
}

/* The tag that rides beside a figure. Small, but it never says only a colour. */
.itag {
  display: inline-block;
  padding: 1px 7px;
  border-radius: 999px;
  border: 1px solid var(--border-hi);
  font-family: var(--sans);
  font-size: 0.64rem;
  font-weight: 800;
  font-style: normal;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  white-space: nowrap;
  vertical-align: middle;
}
.itag--live  { color: var(--live);  border-color: var(--live);  background: var(--live-bg); }
.itag--paper { color: var(--paper); border-color: var(--paper); background: var(--paper-bg); }
.itag--top { font-size: 0.7rem; }
/* Dimming stale figures must not swallow the label saying whose they are. */
body.is-stale .itag { opacity: 1; }

/* A frame on the viewport itself: visible mid-scroll, visible in a screenshot,
   visible from across the room. Inert, so it cannot swallow a tap. */
body.inst::before {
  content: '';
  position: fixed;
  inset: 0;
  z-index: 60;
  pointer-events: none;
  border: 4px solid transparent;
}
body.inst--live::before { border-color: var(--live); }
body.inst--paper::before { border-color: var(--paper); }
body.inst--live  { background-color: var(--bg-live); }
body.inst--paper { background-color: var(--bg-paper); }

/* ---------- an instance that has never reported ----------
   Not an empty account: an unknown one. It must not be able to be mistaken for
   a calm, flat, fully-funded portfolio. */

/* Wins over .card, which is defined later and would otherwise repaint it. */
.card.nodata {
  border: 2px solid var(--warn);
  background: var(--warn-bg);
}
.nodata__head {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 13px;
  font-weight: 800;
  font-size: 0.95rem;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--warn);
  border-bottom: 1px solid var(--warn);
}
.nodata__body { padding: 13px; font-size: 0.9rem; line-height: 1.55; color: var(--on-warn); }

.alert--flush { margin: 0; border-radius: 0; border-width: 0 0 3px; }

/* ---------- mode banner: PAPER vs LIVE, on every page ---------- */

.modebar {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: center;
  gap: 6px 10px;
  padding: 9px 12px;
  font-weight: 800;
  font-size: 0.82rem;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  border-bottom: 1px solid var(--border);
}
.modebar--paper {
  background: var(--paper-bg);
  color: var(--paper);
  border-bottom-color: var(--paper);
}
.modebar--live {
  background: var(--live-bg);
  color: var(--live);
  border-bottom-color: var(--live);
  background-image: repeating-linear-gradient(
    135deg, transparent 0 14px, var(--live-stripe) 14px 28px);
}
.modebar__glyph { font-size: 1rem; line-height: 1; }
.modebar__note {
  font-weight: 600;
  letter-spacing: 0.02em;
  text-transform: none;
  color: var(--text-dim);
}
/* On a stale snapshot the mode is a memory, not a reading. */
.modebar--stale {
  background: var(--surface-2);
  color: var(--text-dim);
  border-bottom-color: var(--border-hi);
  background-image: repeating-linear-gradient(
    135deg, transparent 0 10px, var(--stale-stripe) 10px 20px);
}

/* ---------- the agent's own health ----------
   A halt is not a staleness problem: the agent is checking in perfectly well
   and refusing to trade, so the staleness banner cannot describe it and the
   mode bar has no room for a reason. The slot is always in the markup and
   usually empty — the tile appearing at all is the signal, which is why a
   healthy day's chrome is exactly what it was. */

.healthslot:empty { display: none; }

.health {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 3px 10px;
  padding: 8px 12px;
  border-bottom: 1px solid var(--border);
  font-size: 0.82rem;
}
.health__glyph {
  align-self: center;
  font-size: 1rem;
  line-height: 1;
}
.health__state {
  font-weight: 800;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  font-size: 0.78rem;
}
/* The reason is the whole point of the tile, so it is the one part in the
   agent's own words and at full contrast. */
.health__reason { font-family: var(--mono); font-size: 0.8rem; }
.health__note {
  flex: 1 1 100%;
  font-size: 0.76rem;
  line-height: 1.45;
  color: var(--text-dim);
  overflow-wrap: anywhere;
}

.health--halted {
  background: var(--danger-bg);
  color: var(--on-danger);
  border-bottom-color: var(--danger);
  border-top: 1px solid var(--danger);
}
.health--halted .health__state { color: var(--on-danger-strong); }
.health--degraded {
  background: var(--warn-bg);
  color: var(--on-warn);
  border-bottom-color: var(--warn);
}
.health--degraded .health__state { color: var(--warn); }
/* Dimming is for figures. This is a warning, and it stays at full strength. */
body.is-stale .health { opacity: 1; }

/* ---------- is the venue even open ----------
   Rides inside the mode bar so it is on every page. Three states, and each one
   differs in colour, in dot SHAPE and in words, so it survives both a
   colour-blind reader and a screen reader: filled disc = open, hollow ring =
   closed, tilted square = unknown. Unknown is styled as a warning, not as a
   quiet default, because "closed" is the guess that would make a silent agent
   look expected. */

.market {
  display: inline-flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 2px 7px;
  max-width: 100%;
  padding: 3px 10px 3px 8px;
  border-radius: 999px;
  border: 1px solid var(--border-hi);
  background: var(--bg);
  font-size: 0.74rem;
  font-weight: 800;
  letter-spacing: 0.06em;
  text-transform: uppercase;
}
/* The state itself never breaks across lines; the qualifiers may. */
.market__label { white-space: nowrap; }
.market__dot {
  width: 10px;
  height: 10px;
  border-radius: 50%;
  flex: 0 0 auto;
}
.market__note,
.market__gap {
  font-weight: 600;
  letter-spacing: 0.02em;
  text-transform: none;
  color: var(--text-dim);
}
.market__gap { font-family: var(--mono); }

.market--open {
  color: var(--gain);
  border-color: var(--gain);
  background: var(--gain-bg);
}
.market--open .market__dot {
  background: var(--gain);
  box-shadow: 0 0 0 3px var(--gain-glow);
}
.market--closed {
  color: var(--text-dim);
  border-color: var(--border-hi);
  background: var(--surface-2);
}
.market--closed .market__dot {
  background: transparent;
  border: 2px solid var(--text-faint);
}
.market--unknown {
  color: var(--warn);
  border-color: var(--warn);
  background: var(--warn-bg);
}
.market--unknown .market__dot {
  background: var(--warn);
  border-radius: 2px;
  transform: rotate(45deg);
}
/* The dim-on-stale rule must not mute the light that says data is unconfirmed. */
body.is-stale .market { opacity: 1; }

/* ---------- staleness banner: the loudest thing on the page ---------- */

.stale {
  margin: 0;
  padding: 14px 12px;
  background: var(--danger-bg);
  color: var(--on-danger);
  border-bottom: 3px solid var(--danger);
  border-top: 3px solid var(--danger);
}
.stale--warn {
  background: var(--warn-bg);
  color: var(--on-warn);
  border-color: var(--warn);
}
.stale__head {
  display: flex;
  align-items: center;
  gap: 10px;
  font-weight: 800;
  font-size: 1rem;
  text-transform: uppercase;
  letter-spacing: 0.06em;
}
.stale__glyph { font-size: 1.3rem; line-height: 1; }
.stale__body { margin-top: 6px; font-size: 0.88rem; line-height: 1.5; }
.stale__body strong { font-family: var(--mono); }

@media (prefers-reduced-motion: no-preference) {
  .stale:not(.stale--warn) .stale__glyph { animation: pulse 1.6s ease-in-out infinite; }
}
@keyframes pulse { 0%, 100% { opacity: 1; } 50% { opacity: 0.35; } }

.fresh {
  padding: 7px 12px;
  font-size: 0.78rem;
  color: var(--text-faint);
  border-bottom: 1px solid var(--border);
  background: var(--surface);
}

/* ---------- header + nav ---------- */

.top {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  padding: 12px;
  background: var(--surface);
  border-bottom: 1px solid var(--border);
}
.top__title { display: flex; align-items: baseline; gap: 8px; flex-wrap: wrap; }
.top__sub { font-size: 0.75rem; color: var(--text-faint); }
.top__actions { display: flex; align-items: center; gap: 8px; flex-shrink: 0; }
.top__actions .btn { text-decoration: none; }

.nav {
  display: flex;
  gap: 2px;
  overflow-x: auto;
  background: var(--surface);
  border-bottom: 1px solid var(--border);
  padding: 0 6px;
  scrollbar-width: thin;
}
.nav a {
  flex: 0 0 auto;
  padding: 12px 13px;
  color: var(--text-dim);
  text-decoration: none;
  font-size: 0.85rem;
  font-weight: 600;
  white-space: nowrap;
  border-bottom: 3px solid transparent;
  min-height: var(--tap);
  display: flex;
  align-items: center;
}
.nav a:hover { color: var(--text); }
.nav a.is-active { color: var(--text); border-bottom-color: var(--paper); }
.nav .badge { margin-left: 6px; }

.badge {
  display: inline-block;
  min-width: 20px;
  padding: 1px 6px;
  border-radius: 999px;
  background: var(--danger);
  color: var(--text-hi);
  font-size: 0.72rem;
  font-weight: 800;
  text-align: center;
  font-family: var(--mono);
}
.badge--quiet { background: var(--border-hi); color: var(--text); }

/* ---------- cards, sections ---------- */

.card {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  margin: 12px 0;
  overflow: hidden;
}
.card__head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  padding: 11px 13px;
  border-bottom: 1px solid var(--border);
  background: var(--surface-2);
}
.card__body { padding: 13px; }
.card__body--flush { padding: 0; }

.section-title {
  margin: 20px 0 8px;
  font-size: 0.78rem;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--text-faint);
}

.empty {
  padding: 26px 14px;
  text-align: center;
  color: var(--text-faint);
  font-size: 0.9rem;
}

/* ---------- numbers ---------- */

/* Reflows on its own rather than stepping at one breakpoint: two tiles on a
   phone, and as many as the column can hold above it. The floor is the width
   a formatted currency figure needs before it starts wrapping. */
.stats {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
  gap: 1px;
  background: var(--border);
}

.stat { background: var(--surface); padding: 12px 13px; }
.stat__label {
  font-size: 0.7rem;
  text-transform: uppercase;
  letter-spacing: 0.09em;
  color: var(--text-faint);
}
.stat__value {
  margin-top: 3px;
  font-family: var(--mono);
  font-size: 1.32rem;
  font-weight: 650;
  font-variant-numeric: tabular-nums;
  letter-spacing: -0.02em;
  word-break: break-all;
}
.stat__sub { font-size: 0.76rem; color: var(--text-dim); font-family: var(--mono); }
.stat__stamp {
  margin-top: 5px;
  font-family: var(--mono);
  font-size: 0.7rem;
  letter-spacing: 0.03em;
  color: var(--warn);
  border-top: 1px dashed var(--warn);
  padding-top: 4px;
}

.num { font-family: var(--mono); font-variant-numeric: tabular-nums; }
.gain { color: var(--gain); }
.loss { color: var(--loss); }
.flat { color: var(--flat); }
/* Colour is never the only signal — a sign and an arrow glyph ride along. */
.delta__arrow { font-family: var(--sans); padding-right: 2px; }

/* ---------- stale data: the warning rides on the numbers ----------
   The banner is above the fold, but a reader who scrolls past it must not see
   three-day-old figures rendered exactly like fresh ones.

   De-emphasised, never illegible: the drop from --text to --text-faint is the
   whole signal, and it is a drop from 15:1 to 5.1:1 rather than out of the
   readable range. It is a colour and not an opacity because opacity composites
   against whatever panel is behind — the same rule measured 2.6:1 on a tile
   and 2.5:1 in the totals row, two different failures from one declaration.
   A stale figure the operator cannot read is not a warning, it is a redaction. */

body.is-stale .stat__value,
body.is-stale .stat__sub,
body.is-stale table.data td.num,
body.is-stale table.data td .num {
  color: var(--text-faint);
}
body.is-stale .stat__value .gain,
body.is-stale .stat__value .loss,
body.is-stale .stat__sub .gain,
body.is-stale .stat__sub .loss,
body.is-stale table.data td .gain,
body.is-stale table.data td .loss {
  color: var(--text-faint);
}
/* The stamp is the warning itself and must stay at full strength. */
body.is-stale .stat__stamp { color: var(--warn); opacity: 1; }
body.is-stale table.data td.num { font-style: italic; }

/* ---------- a position we have no price for ----------
   "Flat" is reassuring, and a substituted entry price renders as flat. This
   has to be readable as "we cannot see this position" from across the room. */

.nopx {
  display: inline-block;
  font-family: var(--sans);
  font-size: 0.74rem;
  font-weight: 700;
  font-style: normal;
  letter-spacing: 0.04em;
  color: var(--warn);
  text-transform: uppercase;
}
.nopx--flag {
  padding: 1px 7px;
  border-radius: 999px;
  background: var(--warn-bg);
  border: 1px solid var(--warn);
  color: var(--warn);
}
.nopx__sub {
  display: block;
  margin-top: 3px;
  font-family: var(--mono);
  font-size: 0.72rem;
  font-style: normal;
  color: var(--text-faint);
  text-decoration: line-through;
}
.pill--nopx {
  color: var(--warn);
  border-color: var(--warn);
  background: var(--warn-bg);
}
tr.row--nopx td {
  background: var(--warn-bg);
  border-bottom-color: var(--warn);
}
tr.row--nopx td:first-child { box-shadow: inset 3px 0 0 var(--warn); }
/* The dim-on-stale rule must not quietly mute this louder warning. */
body.is-stale tr.row--nopx td.num .nopx,
body.is-stale tr.row--nopx td .nopx,
body.is-stale tr.row--nopx td .nopx--flag { color: var(--warn); }

.approval__amt--unknown {
  color: var(--warn);
  font-size: 0.86rem;
  text-transform: uppercase;
  letter-spacing: 0.05em;
}

/* ---------- tables that become cards on a phone ---------- */

.table-scroll { overflow-x: auto; }
table.data {
  width: 100%;
  border-collapse: collapse;
  font-size: 0.87rem;
}
table.data th {
  text-align: left;
  padding: 9px 11px;
  font-size: 0.7rem;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: var(--text-faint);
  border-bottom: 1px solid var(--border);
  white-space: nowrap;
  background: var(--surface-2);
}
table.data td {
  padding: 10px 11px;
  border-bottom: 1px solid var(--border);
  vertical-align: top;
}
table.data tr:last-child td { border-bottom: 0; }
table.data td.r, table.data th.r { text-align: right; }
.sym { font-family: var(--mono); font-weight: 700; letter-spacing: 0.02em; }

@media (max-width: 640px) {
  table.data, table.data tbody, table.data tfoot,
  table.data tr, table.data td { display: block; width: 100%; }
  table.data thead { display: none; }
  /* The totals row is a card too, but it keeps the rule that separates it
     from the positions it sums. */
  table.data tfoot tr { border-top: 2px solid var(--border-hi); padding-top: 6px; }
  table.data tfoot td { border-top: 0; }
  /* The label cell carries a caption, not a figure: it reads left, on its own
     line, rather than as the left half of a label/value pair.
     A footer label written as <th scope="row"> is the same caption and must
     lay out the same way. It is NOT in the display:block list above — <th> is
     left out of it deliberately so a header cell keeps its own styling — so
     without this it stays a table-cell inside a block row, the browser wraps
     it in an anonymous table box, and the caption renders as a stray
     part-width strip detached from the figures it labels. */
  table.data tfoot td:first-child,
  table.data tfoot th:first-child { display: block; text-align: left; }
  table.data tfoot th {
    width: 100%;
    padding: 4px 13px;
    border-bottom: 0;
    white-space: normal;
  }
  table.data tr {
    border-bottom: 1px solid var(--border);
    padding: 6px 0;
  }
  table.data tr:last-child { border-bottom: 0; }
  table.data td {
    display: flex;
    justify-content: space-between;
    align-items: baseline;
    gap: 14px;
    border-bottom: 0;
    padding: 4px 13px;
    text-align: right;
  }
  table.data td::before {
    content: attr(data-label);
    flex: 0 0 auto;
    font-size: 0.7rem;
    text-transform: uppercase;
    letter-spacing: 0.07em;
    color: var(--text-faint);
    text-align: left;
  }
  table.data td.r { text-align: right; }
}

/* ---------- pills ---------- */

.pill {
  display: inline-block;
  padding: 2px 8px;
  border-radius: 999px;
  font-size: 0.72rem;
  font-weight: 700;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  border: 1px solid var(--border-hi);
  color: var(--text-dim);
  white-space: nowrap;
}
.pill--open       { color: var(--gain); border-color: var(--gain); }
.pill--weakened   { color: var(--warn); border-color: var(--warn); }
.pill--broken     { color: var(--loss); border-color: var(--loss); }
.pill--target_hit { color: var(--paper); border-color: var(--paper); }
.pill--closed     { color: var(--text-faint); }
.pill--filled     { color: var(--gain); border-color: var(--gain); }
.pill--partially_filled { color: var(--gain); border-color: var(--border-hi); }
.pill--pending_approval { color: var(--warn); border-color: var(--warn); }
.pill--approved   { color: var(--paper); border-color: var(--paper); }
.pill--submitted  { color: var(--paper); border-color: var(--border-hi); }
.pill--rejected, .pill--failed, .pill--expired, .pill--canceled { color: var(--loss); border-color: var(--loss); }
.pill--buy  { color: var(--gain); border-color: var(--gain); }
.pill--sell { color: var(--loss); border-color: var(--loss); }

/* ---------- headroom against a ceiling ----------
   The read carries used, ceiling and percent in words; the bar repeats it for
   the eye and is hidden from assistive technology. The fill is a class per 5%
   rather than a width in a style attribute, because style-src has no
   'unsafe-inline' and never will. */

.headrooms {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(210px, 1fr));
  gap: 1px;
  background: var(--border);
}
.headroom { background: var(--surface); padding: 11px 13px; }
/* The gauges are a flush grid, so a card-level stamp has to bring its own
   gutter or it renders hard against both edges. */
.headrooms__stamp { padding: 0 13px 11px; }
/* Label over figure, exactly as a stat tile stacks: the read is a sentence,
   and sharing a line with the label breaks it in the middle on a narrow
   column. */
.headroom__head { display: flex; flex-direction: column; gap: 2px; }
.headroom__label {
  font-size: 0.7rem;
  text-transform: uppercase;
  letter-spacing: 0.09em;
  color: var(--text-faint);
}
.headroom__read { font-size: 0.82rem; color: var(--text); }
.headroom__read--absent {
  font-family: var(--sans);
  font-size: 0.78rem;
  color: var(--text-faint);
  font-style: italic;
}
.headroom__track {
  margin-top: 7px;
  height: 8px;
  border-radius: 999px;
  background: var(--surface-2);
  border: 1px solid var(--border);
  overflow: hidden;
}
.headroom__fill {
  display: block;
  height: 100%;
  background: var(--accent);
}
.headroom--warn .headroom__fill { background: var(--warn); }
.headroom--warn .headroom__read { color: var(--on-warn); }
.headroom--over .headroom__fill { background: var(--danger); }
.headroom--over .headroom__read { color: var(--on-danger-strong); }
.headroom--over .headroom__track { border-color: var(--danger); }

.headroom__fill--0   { width: 0; }
.headroom__fill--5   { width: 5%; }
.headroom__fill--10  { width: 10%; }
.headroom__fill--15  { width: 15%; }
.headroom__fill--20  { width: 20%; }
.headroom__fill--25  { width: 25%; }
.headroom__fill--30  { width: 30%; }
.headroom__fill--35  { width: 35%; }
.headroom__fill--40  { width: 40%; }
.headroom__fill--45  { width: 45%; }
.headroom__fill--50  { width: 50%; }
.headroom__fill--55  { width: 55%; }
.headroom__fill--60  { width: 60%; }
.headroom__fill--65  { width: 65%; }
.headroom__fill--70  { width: 70%; }
.headroom__fill--75  { width: 75%; }
.headroom__fill--80  { width: 80%; }
.headroom__fill--85  { width: 85%; }
.headroom__fill--90  { width: 90%; }
.headroom__fill--95  { width: 95%; }
.headroom__fill--100 { width: 100%; }

/* Stale figures dim exactly as the tiles do; the gauge is a figure too. */
body.is-stale .headroom__read { color: var(--text-faint); }
/* The bar is a graphical object and needs 3:1 of its own against the track:
   at half strength the danger fill measured 2.1:1, so the one gauge that has
   to be seen across the room was the one the dimming erased. */
body.is-stale .headroom__fill { opacity: 0.75; }
/* A breached ceiling is not de-emphasised by anything. The stale rule above
   outranks .headroom--over on specificity, so without this the loudest read on
   the page loses its colour at precisely the moment it matters most. */
body.is-stale .headroom--warn .headroom__read { color: var(--on-warn); }
body.is-stale .headroom--over .headroom__read { color: var(--on-danger-strong); }

/* ---------- status chips ----------
   A pill says what a row is; a chip says where an order stands. It carries a
   glyph as well as a colour, because "filled" and "rejected" differing only in
   hue is the one distinction on the Trades page that must never rely on it. */

.chip {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  padding: 2px 9px;
  border-radius: 999px;
  border: 1px solid var(--border-hi);
  color: var(--text-dim);
  font-size: 0.72rem;
  font-weight: 700;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  white-space: nowrap;
}
.chip__glyph { font-size: 0.78rem; line-height: 1; }
.chip__note {
  margin-left: 7px;
  font-family: var(--mono);
  font-size: 0.74rem;
  color: var(--text-dim);
}
.chip--ok      { color: var(--gain); border-color: var(--gain); }
.chip--partial { color: var(--gain); border-color: var(--border-hi); }
.chip--info    { color: var(--paper); border-color: var(--paper); }
.chip--warn    { color: var(--warn); border-color: var(--warn); }
.chip--bad     { color: var(--loss); border-color: var(--loss); }
.chip--dim     { color: var(--text-faint); }
.chip--neutral { color: var(--text-dim); }

/* ---------- sortable headers and range links ---------- */

.sortlink {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  padding: 2px 0;
  color: inherit;
  text-decoration: none;
}
.sortlink:hover { color: var(--text); }
.sortlink__arrow { font-size: 0.62rem; color: var(--text-faint); }
.sortlink.is-active { color: var(--text); }
.sortlink.is-active .sortlink__arrow { color: var(--accent); }

.ranges { display: flex; flex-wrap: wrap; gap: 6px; }
.ranges__opt {
  display: inline-flex;
  align-items: center;
  min-height: var(--tap-phone);
  padding: 4px 13px;
  border-radius: 999px;
  border: 1px solid var(--border-hi);
  background: var(--bg);
  color: var(--text-dim);
  font-size: 0.78rem;
  font-weight: 700;
  letter-spacing: 0.04em;
  text-decoration: none;
}
.ranges__opt:hover { color: var(--text); border-color: var(--text-faint); }
.ranges__opt.is-active {
  background: var(--accent-bg);
  border-color: var(--accent);
  color: var(--text-hi);
}
.ranges__arrow { font-size: 0.62rem; padding-left: 5px; }

/* The sort control for a table that has stopped being a table. Below 640px
   the header row is hidden and the rows render as cards, which takes every
   sort link off the page at exactly the width where ten stacked positions are
   least readable. Same links, pill-shaped, and only there. */
.sortbar { display: none; }
.sortbar__label {
  flex: 1 1 100%;
  font-size: 0.66rem;
  text-transform: uppercase;
  letter-spacing: 0.09em;
  color: var(--text-faint);
}

@media (max-width: 640px) {
  .sortbar {
    display: flex;
    gap: 5px 6px;
    padding: 9px 11px 3px;
  }
  .sortbar .ranges__opt { padding: 4px 11px; font-size: 0.74rem; }
}

/* ---------- a totals row ----------
   Set apart from the rows it sums rather than reading as one more position:
   heavier rule above, no bottom border, and the exclusion count beside the
   label so a total that leaves a holding out never looks complete. */

table.data tfoot td {
  border-top: 2px solid var(--border-hi);
  border-bottom: 0;
  background: var(--surface-2);
  font-weight: 700;
}
.totals__label {
  font-size: 0.72rem;
  text-transform: uppercase;
  letter-spacing: 0.09em;
}
.totals__note {
  display: block;
  margin-top: 2px;
  font-family: var(--sans);
  font-size: 0.7rem;
  font-weight: 400;
  letter-spacing: 0.02em;
  color: var(--text-faint);
}
/* Stale figures dim in the totals exactly as they do in the rows above. */
body.is-stale table.data tfoot td .num,
body.is-stale table.data tfoot td.num { color: var(--text-faint); }

/* ---------- text sitting ON a heat cell ----------
   These live here rather than beside the calendar that draws them because the
   shades are tokens here, and the readable pairing is a property of the token,
   not of the one grid that happens to use it.

   A shaded cell is not a panel: the faint grey measures 2.30:1 on the strongest
   positive shade, and the status colours 4.09:1 on the strongest negative one.
   The day number takes the full text colour on any shaded cell (6.77:1 at the
   worst step), and the strongest step takes the same legible partner that any
   other tinted panel gets. The unshaded cells keep the faint grey — the ramp
   still reads, it just stops taking the figures with it. */

.cal__d:not(.cal__d--z):not(.cal__d--none):not(.cal__d--out) .cal__n,
.cal__d:not(.cal__d--z):not(.cal__d--none):not(.cal__d--out) .cal__more {
  color: var(--text);
}
/* The rule above outranks the calendar's own hover colour; hand it back. */
.cal__d:not(.cal__d--z):not(.cal__d--none):not(.cal__d--out) .cal__more:hover {
  color: var(--paper);
}
.cal__d--g4 .cal__v.gain { color: var(--on-gain); }
.cal__d--l4 .cal__v.loss { color: var(--on-loss); }

/* These carry their meaning in a border and a fill the forced palette flattens. */
@media (forced-colors: active) {
  .chip, .ranges__opt { border: 1px solid currentColor; }
  .headroom__fill { background: Highlight; }
  .headroom__track { border: 1px solid CanvasText; }
}

/* ---------- buttons ---------- */

.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  min-height: var(--tap);
  padding: 12px 18px;
  border-radius: var(--radius);
  border: 1px solid var(--border-hi);
  background: var(--surface-2);
  color: var(--text);
  font: inherit;
  font-weight: 700;
  cursor: pointer;
  text-decoration: none;
  -webkit-tap-highlight-color: transparent;
}
.btn:hover { border-color: var(--text-faint); }
.btn:active { transform: translateY(1px); }
.btn--sm { min-height: var(--tap-phone); padding: 7px 12px; font-size: 0.82rem; }
.btn--block { width: 100%; }
.btn--approve {
  background: var(--gain-bg);
  border-color: var(--gain);
  color: var(--gain);
  font-size: 1.02rem;
}
.btn--reject {
  background: var(--loss-bg);
  border-color: var(--loss);
  color: var(--loss);
  font-size: 1.02rem;
}
.btn--danger { background: var(--danger-bg); border-color: var(--danger); color: var(--on-danger-strong); }
.btn--primary { background: var(--accent-bg); border-color: var(--paper); color: var(--paper); }
.btn[disabled] { opacity: 0.45; cursor: not-allowed; }

/* ---------- confirm gate ----------
   The two-step lives in markup, not script, so the safety survives with
   JavaScript off. Opening the gate is the first tap; the button inside it
   names what it does and is the second. */

details.gate > summary {
  list-style: none;
  cursor: pointer;
}
details.gate > summary::-webkit-details-marker { display: none; }
details.gate > summary::marker { content: ''; }

.gate__open {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  width: 100%;
  min-height: var(--tap);
  padding: 12px 18px;
  border-radius: var(--radius);
  border: 1px solid var(--border-hi);
  background: var(--surface-2);
  color: var(--text);
  font-weight: 700;
  text-align: center;
  -webkit-tap-highlight-color: transparent;
}
.gate__open:hover { border-color: var(--text-faint); }

/* A gate opener carrying btn--danger has to look dangerous. .gate__open is
   defined after .btn--danger at equal specificity, so its neutral surface was
   winning and the kill switch rendered as an ordinary grey button — the one
   control on this console that must never be mistaken for an ordinary one. */
.gate__open.btn--danger {
  background: var(--danger-bg);
  border-color: var(--danger);
  color: var(--on-danger-strong);
}
.gate__open.btn--danger:hover { border-color: var(--danger); }
details.gate[open] > .gate__open {
  border-bottom-left-radius: 0;
  border-bottom-right-radius: 0;
  border-bottom-color: transparent;
}

.gate__body {
  padding: 12px 13px 13px;
  border: 1px solid var(--border-hi);
  border-top: 0;
  border-bottom-left-radius: var(--radius);
  border-bottom-right-radius: var(--radius);
  background: var(--bg);
}
.gate__q {
  margin: 0 0 10px;
  font-size: 0.86rem;
  line-height: 1.45;
  color: var(--text-dim);
}

/* Approve and reject are far apart and full-width so neither is hit by
   accident on a phone. */
.decide {
  display: grid;
  grid-template-columns: 1fr;
  gap: 26px;
  padding: 16px 13px;
  border-top: 1px solid var(--border);
  background: var(--surface-2);
}
@media (min-width: 620px) {
  .decide { grid-template-columns: 1fr 1fr; gap: 28px; }
  .decide .btn { min-height: 56px; }
}
.decide .btn { min-height: 58px; }

/* ---------- forms ---------- */

.field { margin-bottom: 14px; }
.field label {
  display: block;
  font-size: 0.76rem;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: var(--text-dim);
  margin-bottom: 5px;
}
.field .hint { font-size: 0.78rem; color: var(--text-faint); margin-top: 4px; text-transform: none; letter-spacing: 0; }
input[type="text"], input[type="password"], input[type="number"],
input[type="search"], select, textarea {
  width: 100%;
  min-height: var(--tap);
  padding: 10px 12px;
  border-radius: var(--radius);
  border: 1px solid var(--border-hi);
  background: var(--bg);
  color: var(--text);
  font: inherit;
  font-family: var(--mono);
}
select { font-family: var(--sans); }
textarea { min-height: 84px; font-family: var(--sans); resize: vertical; }

.filters {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  align-items: flex-end;
}
.filters .field { margin-bottom: 0; flex: 1 1 150px; }

/* ---------- alerts ---------- */

.alert {
  padding: 11px 13px;
  border-radius: var(--radius);
  border: 1px solid var(--border-hi);
  margin: 12px 0;
  font-size: 0.9rem;
}
.alert--ok    { border-color: var(--gain); background: var(--gain-bg); color: var(--on-gain); }
.alert--error { border-color: var(--loss); background: var(--loss-bg); color: var(--on-loss); }
.alert--warn  { border-color: var(--warn); background: var(--warn-bg); color: var(--on-warn); }

/* ---------- approvals ---------- */

.approval { border-color: var(--border-hi); }
.approval__head {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 9px;
  padding: 13px;
  border-bottom: 1px solid var(--border);
  background: var(--surface-2);
}
.approval__sym { font-family: var(--mono); font-size: 1.24rem; font-weight: 800; }
.approval__amt { margin-left: auto; font-family: var(--mono); font-size: 1.16rem; font-weight: 700; }

.expiry {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 9px 13px;
  font-family: var(--mono);
  font-size: 0.88rem;
  font-weight: 700;
  border-bottom: 1px solid var(--border);
  background: var(--bg);
}
.expiry--ok   { color: var(--text-dim); }
.expiry--soon { color: var(--warn); background: var(--warn-bg); }
.expiry--gone { color: var(--loss); background: var(--loss-bg); }

.kv {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(190px, 1fr));
  gap: 1px;
  background: var(--border);
}
.kv > div { background: var(--surface); padding: 10px 13px; }
.kv dt {
  font-size: 0.7rem;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: var(--text-faint);
}
.kv dd { margin: 3px 0 0; font-family: var(--mono); font-size: 1rem; font-weight: 650; }

.prose {
  font-size: 0.92rem;
  line-height: 1.6;
  color: var(--text);
  overflow-wrap: anywhere;
}
.prose--dim { color: var(--text-dim); }

/* Only text the agent wrote keeps its own line breaks. Static copy must not,
   or the template's own source indentation renders as leading whitespace. */
.prose--pre { white-space: pre-wrap; }

ul.conds, ul.links { margin: 6px 0 0; padding-left: 20px; }
ul.conds li, ul.links li { margin-bottom: 5px; line-height: 1.5; overflow-wrap: anywhere; }
ul.links a { color: var(--paper); overflow-wrap: anywhere; }

.evidence-host { color: var(--text-faint); font-size: 0.78rem; font-family: var(--mono); }

details.more { border-top: 1px solid var(--border); }
details.more > summary {
  padding: 12px 13px;
  cursor: pointer;
  font-size: 0.82rem;
  font-weight: 650;
  color: var(--text-dim);
  min-height: var(--tap);
  display: flex;
  align-items: center;
}
details.more > summary:hover { color: var(--text); }
details.more > div { padding: 0 13px 13px; }

/* ---------- review log ---------- */

.review {
  border-left: 3px solid var(--border-hi);
  padding: 2px 0 2px 12px;
  margin: 0 0 14px;
}
.review--broken { border-left-color: var(--loss); }
.review--weakened { border-left-color: var(--warn); }
.review--open { border-left-color: var(--gain); }
.review__meta {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  align-items: center;
  font-size: 0.76rem;
  color: var(--text-faint);
  margin-bottom: 5px;
  font-family: var(--mono);
}

/* ---------- kill switch bar ---------- */

.killbar {
  position: fixed;
  left: 0; right: 0; bottom: 0;
  z-index: 50;
  padding: 9px 12px calc(9px + env(safe-area-inset-bottom, 0px));
  background: var(--killbar-bg);
  border-top: 1px solid var(--border-hi);
  backdrop-filter: blur(6px);
}
.killbar__inner {
  max-width: var(--wrap-max);
  margin: 0 auto;
  display: flex;
  align-items: center;
  gap: 10px;
}

/* The sheet is a <details> so it collapses with no script, but on anything
   wider than a phone it must not collapse at all — this overrides the UA rule
   that hides a closed details' children, and hides the tab that would open it.
   Both live here rather than in the mobile query so that a browser which
   never evaluates the media query still shows the switch. */
.killbar__sheet > .killbar__inner { display: flex; }
/* Chrome now wraps a details' children in ::details-content and skips their
   layout with content-visibility while it is closed, so the display override
   above stopped being enough on its own: the bar measured zero high and the
   switch was laid out below the bottom of the viewport, unreachable. */
.killbar__sheet::details-content { content-visibility: visible; block-size: auto; }
.killbar__tab { display: none; }
.killbar__state {
  flex: 1 1 auto;
  font-size: 0.78rem;
  color: var(--text-dim);
  line-height: 1.3;
}
.killbar__state b { display: block; font-size: 0.86rem; color: var(--text); }
.killbar .btn { flex: 0 0 auto; }
.killbar--engaged {
  background: var(--danger-bg);
  border-top: 3px solid var(--danger);
}
.killbar--engaged .killbar__state { color: var(--on-danger); }
.killbar--engaged .killbar__state b { color: var(--text-hi); }

/* The bar never scrolls away, so it is the last line of defence against
   halting the wrong agent: it names the account in its own colour. */
.killbar--live:not(.killbar--engaged) { border-top: 3px solid var(--live); }
.killbar--live:not(.killbar--engaged) .killbar__state b { color: var(--live); }
.killbar--paper:not(.killbar--engaged) { border-top: 3px solid var(--paper); }
.killbar--paper:not(.killbar--engaged) .killbar__state b { color: var(--paper); }

/* The confirm step is one extra tap, never a typed phrase: in an emergency
   speed matters more than ceremony. */
.killconfirm {
  display: flex;
  gap: 10px;
  align-items: center;
  width: 100%;
}
.killconfirm .btn { flex: 1 1 auto; }
.killconfirm > details.gate { flex: 1 1 auto; }

/* The bar is pinned to the bottom edge, so its gate has to open upwards or the
   confirm button lands off-screen. */
.killbar details.gate { display: flex; flex-direction: column-reverse; }
.killbar .gate__body {
  border: 1px solid var(--border-hi);
  border-bottom: 0;
  border-radius: var(--radius) var(--radius) 0 0;
}
.killbar details.gate[open] > .gate__open {
  border-radius: 0 0 var(--radius) var(--radius);
  border-top-color: transparent;
  border-bottom-color: var(--border-hi);
}
[hidden] { display: none !important; }

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

.login-wrap {
  max-width: 380px;
  margin: 8vh auto;
  padding: 0 16px;
}
.login-brand { text-align: center; margin-bottom: 18px; }
.login-brand .mark {
  font-family: var(--mono);
  font-size: 1.5rem;
  font-weight: 800;
  letter-spacing: 0.16em;
}
.login-brand p { color: var(--text-faint); font-size: 0.82rem; margin: 6px 0 0; }

/* ---------- misc ---------- */

.foot {
  padding: 18px 12px 8px;
  color: var(--text-faint);
  font-size: 0.75rem;
  text-align: center;
}
.noscript {
  padding: 9px 12px;
  background: var(--surface-2);
  border-bottom: 1px solid var(--border);
  font-size: 0.8rem;
  color: var(--text-dim);
  text-align: center;
}
.sr-only {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}
.skip-link {
  position: absolute; left: -9999px; top: 0; z-index: 100;
  background: var(--surface); color: var(--text); padding: 12px 16px;
}
.skip-link:focus { left: 0; }

/* ---------- phone: buy back the vertical ----------
   Everything above is sized for a desktop reader. On a 390px screen the four
   chrome bars alone ate a third of the viewport before a single figure
   appeared, so this block tightens padding, gaps and display type on the phone
   only — the breakpoint is the same 640px the tables already switch at.

   Two things are deliberately NOT shrunk: body copy stays at 15px, and every
   tappable target stays at or above --tap-phone, which the base rules now
   hold to at every width. */

@media (max-width: 640px) {
  h1 { font-size: 1.05rem; }

  .instbar { gap: 6px; padding: 6px 10px; }
  .instbar__now { gap: 2px 6px; font-size: 0.78rem; }
  .instbar__now strong { font-size: 0.8rem; }
  /* The account title is on the switch itself two lines down; the eyebrow
     repeating it is the cheapest line on the screen to give up. */
  .instbar__now .instbar__eyebrow:last-child { display: none; }
  .instbar__opt { padding: 5px 12px; }

  .modebar { gap: 5px 8px; padding: 7px 10px; font-size: 0.78rem; }
  /* "Market open" already says regular session. The unknown state keeps its
     note, because there the qualifier IS the information. */
  .market--open .market__note,
  .market--closed .market__note { display: none; }
  .fresh { padding: 5px 10px; font-size: 0.74rem; }

  .top { padding: 9px 10px; gap: 8px; }
  .top__sub { display: none; }

  .nav { padding: 0 4px; }
  .nav a { min-height: var(--tap-phone); padding: 9px 11px; }

  .wrap { padding: 0 10px; }
  .card { margin: 9px 0; }
  .card__head { padding: 8px 11px; }
  .card__body { padding: 11px; }
  .section-title { margin: 14px 0 6px; }
  .empty { padding: 18px 12px; }

  .stat { padding: 9px 11px; }
  .stat__value { margin-top: 1px; font-size: 1.12rem; }

  /* Seven stacked label/value lines per position: a pixel off each one is
     worth most of a row. */
  table.data tr { padding: 3px 0; }
  table.data td { padding: 2px 11px; line-height: 1.35; }

  .kv > div { padding: 8px 11px; }
  .kv dd { font-size: 0.94rem; }

  .approval__head { gap: 7px; padding: 10px 11px; }
  .approval__sym { font-size: 1.12rem; }
  .approval__amt { font-size: 1.06rem; }
  .expiry { padding: 7px 11px; }

  .decide { gap: 18px; padding: 12px 11px; }
  .decide .btn { min-height: 50px; }

  details.more > summary { padding: 10px 11px; min-height: var(--tap-phone); }
  details.more > div { padding: 0 11px 11px; }

  .alert { padding: 9px 11px; margin: 9px 0; }

  /* The sticky bar is what body's bottom padding reserves room for, so the
     two shrink together or the last card ends up underneath it. On a phone it
     collapses to a single 44px tab: the switch is one tap away instead of
     zero, and the ~50px it gives back is permanent, on every screen. The tab
     still carries the state in words and colour, so a halted agent is as
     obvious collapsed as it was expanded. */
  .killbar { padding: 0 0 env(safe-area-inset-bottom, 0px); }
  .killbar__sheet:not([open]) > .killbar__inner { display: none; }
  /* The collapse is wanted here, so the rule above is handed back. */
  .killbar__sheet:not([open])::details-content { content-visibility: hidden; block-size: 0; }
  .killbar__tab {
    display: flex;
    align-items: center;
    gap: 8px;
    min-height: var(--tap-phone);
    padding: 0 12px;
    font-size: 0.78rem;
    font-weight: 600;
    color: var(--text);
    cursor: pointer;
    list-style: none;
  }
  .killbar__tab::-webkit-details-marker { display: none; }
  .killbar__pip {
    width: 9px;
    height: 9px;
    flex: 0 0 auto;
    border-radius: 50%;
    background: var(--paper);
  }
  .killbar--live .killbar__pip { background: var(--live); }
  /* A self-halt stops trading exactly as the switch does, so the collapsed tab
     has to read the same way from across the room. */
  .killbar--halted .killbar__pip { background: var(--warn); }
  .killbar--engaged .killbar__pip { background: var(--danger); }
  .killbar__tabtext { flex: 1 1 auto; }
  .killbar__chev {
    flex: 0 0 auto;
    font-size: 0.6rem;
    color: var(--text-faint);
  }
  /* The rotation is the only transition in the chrome; it is decoration, and
     it is opted into rather than suppressed after the fact. */
  @media (prefers-reduced-motion: no-preference) {
    .killbar__chev { transition: transform 0.15s ease; }
  }
  .killbar__sheet[open] .killbar__chev { transform: rotate(180deg); }
  .killbar__sheet[open] > .killbar__inner {
    padding: 0 10px 9px;
    border-top: 1px solid var(--border);
    padding-top: 9px;
  }
  .killbar__state { font-size: 0.74rem; }
  .killbar__state b { font-size: 0.82rem; }
  /* Reserves the collapsed tab only. The expanded sheet overlays the last
     card, which is correct: it is a deliberate, dismissable action panel. */
  body { padding-bottom: 52px; }

  /* ---- density: more rows per screen ---- */
  .card__head { font-size: 0.82rem; }
  .card__body { padding: 9px 10px; }
  .card { margin: 7px 0; }
  .stat { padding: 8px 10px; }
  .stat__label { font-size: 0.66rem; }
  .stat__value { font-size: 1.04rem; }
  .stat__sub { font-size: 0.72rem; }
  table.data td { padding: 1px 10px; font-size: 0.82rem; line-height: 1.3; }
  table.data tr { padding: 2px 0; }
  table.data td:first-child { font-size: 0.7rem; }
  .kv > div { padding: 6px 10px; }
  .kv dt { font-size: 0.66rem; }
  .kv dd { font-size: 0.88rem; }
  .section-title { margin: 11px 0 5px; font-size: 0.72rem; }
  .foot { font-size: 0.68rem; }

  .foot { padding: 12px 10px 6px; }
}

/* ---------- the ladder above the phone ----------
   Five named rungs, and only the page measure and a little breathing room
   move between them:

     phone   ≤ 640   the density block above
     tablet  ≥ 768   chrome relaxes, tables stay tables
     laptop  ≥ 1024
     desk    ≥ 1280
     wide    ≥ 1600

   The grids do the rest on their own. .stats and .kv are auto-fit, so a wider
   screen fits more tiles per row rather than four stretched ones with the
   phone's information architecture at three times the width. */

@media (min-width: 768px) {
  .card { margin: 14px 0; }
  .card__body { padding: 15px; }
  .instbar { padding: 9px 16px; }
  .top { padding: 14px 16px; }
}

@media (min-width: 1024px) {
  :root { --wrap-max: 1120px; }
  .card__body { padding: 16px 18px; }
  .card__head { padding: 12px 18px; }
  .stat { padding: 14px 18px; }
}

@media (min-width: 1280px) {
  :root { --wrap-max: 1280px; }
  h1 { font-size: 1.3rem; }
  .stat__value { font-size: 1.44rem; }
}

@media (min-width: 1600px) {
  :root { --wrap-max: 1480px; }
}

/* ---------- rendering modes we do not control ----------
   The kill bar is the one element on this page that must survive every one of
   them: a translucent panel that dissolves is a switch the operator cannot
   find. */

@media (prefers-reduced-transparency: reduce) {
  .killbar { background: var(--bg); backdrop-filter: none; }
}

@media (forced-colors: active) {
  .killbar {
    background: Canvas;
    backdrop-filter: none;
    border-top: 3px solid CanvasText;
  }
  /* These carry their meaning in a border the forced palette would flatten. */
  .badge, .itag, .pill, .nopx--flag, .market { border: 1px solid currentColor; }
  /* The health tile and the totals row are told apart by a tinted panel the
     forced palette removes entirely; a rule keeps them separate. */
  .health { border: 2px solid CanvasText; }
  table.data tfoot td { border-top: 2px solid CanvasText; }
  a:focus-visible,
  button:focus-visible,
  summary:focus-visible,
  [tabindex]:focus-visible { outline: 3px solid Highlight; }
}

/* ---------- utilities ----------
   These exist so no element needs a style attribute: the pages then render
   correctly even under a CSP with no 'unsafe-inline' in style-src. */

.u-sm { font-size: 0.86rem; }
.u-xs { font-size: 0.8rem; }
.u-mt { margin-top: 12px; }
.u-mb { margin-bottom: 10px; }
.u-top { border-top: 1px solid var(--border); }
.u-bot { border-bottom: 1px solid var(--border); }
.u-dial { border-top: 1px solid var(--border); padding-top: 14px; }
.field--auto { flex: 0 0 auto; }
.stat__value--sm { font-size: 0.95rem; }
.approval__head--plain { background: transparent; border-bottom: 0; }

/* ---------------------------------------------------------------- reveal --
   Show-password checkbox. Plain control, quiet by design: it must read as a
   convenience, not as a security control. */
.reveal {
  display: inline-flex; align-items: center; gap: 8px;
  margin: -4px 0 14px; font-size: 0.82rem; color: var(--text-dim);
  cursor: pointer; user-select: none;
}
.reveal input { accent-color: var(--accent); }
