/* Aethos Charts — v4 brand over TradingView ergonomics and Skylit chrome.
   Palette is locked: Ink/Deep grounds, Porcelain type, Steel muted,
   Signal for state only. No red, no pulsing, no urgency mechanics.

   Everything below reads from the :root tokens. Three sessions edit this
   file, so a colour written as a literal in a component rule is a colour
   that will drift — add a token instead. */
* { margin: 0; padding: 0; box-sizing: border-box; }

/* Self-hosted faces (#126): the Google Fonts fetch told a third party who
   was looking at the chart and from where, on every page load — the one
   remaining default leak after the ROADMAP made per-user isolation a design
   rule. Latin subsets, woff2 only, 118KB total, same-origin.
   Archivo is one VARIABLE file covering both weights the UI uses — the two
   per-weight files Google served were byte-identical. And Newsreader loads
   for the first time ever: .lock-msg and .cov-quote asked for it since the
   day they were written, and the two screens meant to be read slowly have
   been rendering in Georgia the whole time. */
@font-face {
  font-family: Archivo; font-style: normal; font-weight: 100 900;
  font-display: swap; src: url(/static/fonts/archivo.woff2) format("woff2");
}
@font-face {
  font-family: "IBM Plex Mono"; font-style: normal; font-weight: 400;
  font-display: swap; src: url(/static/fonts/plex-mono-400.woff2) format("woff2");
}
@font-face {
  font-family: "IBM Plex Mono"; font-style: normal; font-weight: 500;
  font-display: swap; src: url(/static/fonts/plex-mono-500.woff2) format("woff2");
}
@font-face {
  font-family: Newsreader; font-style: normal; font-weight: 400;
  font-display: swap; src: url(/static/fonts/newsreader-400.woff2) format("woff2");
}

:root {
  /* ---- brand, locked by the v4 identity spec (assets-v4) ---- */
  --ink: #101419; --deep: #080D11;
  --porcelain: #F4F2ED;
  /* Was #EAE8E2 under v3. v4 locks porcelain one step warmer and lighter.
     app.js keeps its own copy of this in `C.porcelain` — the two must move
     together or the canvas price scale sits a shade off the header beside
     it. Note the raw rgba(234,232,226,…) literals in app.js are NOT this
     token: they are byte-matched to the heat raster and must not follow. */

  /* Neutral muted grey for secondary type. Deliberately NOT the v4 "Steel"
     accent, which is a blue witness mark — see --witness. */
  --steel: #82878C;
  /* Recessive states, all derived from --steel so they move as one. Was
     three different hardcoded alphas (.30 / .35 / .40 / .45 / .65) across
     five rules, which is why disabled type read at a different weight in
     the calendar than in the context menu. */
  --steel-soft: rgba(130, 135, 140, .65);   /* de-emphasised, still content */
  --steel-dim:  rgba(130, 135, 140, .50);   /* disabled / unavailable */

  --signal: #325B72;
  --signal-halo: rgba(50, 91, 114, .30);
  /* v4 "Steel" — the witness accent, #7FA0B4 on a dark ground. The spec
     says use it once, small, and meaningfully; the focus ring is exactly
     that. It also fixes a real failure: --signal against --deep is 2.7:1,
     under the 3:1 WCAG 2.2 asks of a focus indicator, so keyboard focus
     was close to invisible on the chrome it mattered most on. This is
     7.1:1. */
  --witness: #7FA0B4;

  --rule: #1E242B; --rule-soft: #161C22;
  --up: #47907A; --down: #A05B52;

  /* Skylit-style floating surfaces */
  --float: rgba(16, 20, 25, .88);
  --float-border: rgba(244, 242, 237, .10);
  --hover: rgba(244, 242, 237, .06);
  --active-bg: rgba(50, 91, 114, .18);

  --r-sm: 3px; --r-md: 6px; --r-lg: 10px;
  --t: .12s ease;

  --head-h: 40px; --rail-w: 44px; --foot-h: 56px;
  --dom-w: 208px;
}
html, body { height: 100%; overflow: hidden; }
body {
  background: var(--deep); color: var(--porcelain);
  font: 12px/1.5 "IBM Plex Mono", ui-monospace, Menlo, monospace;
  letter-spacing: .05em;
  display: flex; flex-direction: column;
  -webkit-font-smoothing: antialiased;
}
.hidden { display: none !important; }
.num { font-variant-numeric: tabular-nums; }

/* ============ header ============ */
header {
  display: flex; align-items: center; gap: 20px;
  height: var(--head-h); padding: 0 14px; background: transparent;
  border-bottom: 1px solid var(--rule-soft); flex: 0 0 auto;
  position: relative; z-index: 6;
}
.lockup { display: flex; align-items: center; gap: 12px; }
.lockup .rule { width: 1px; height: 16px; background: var(--steel); opacity: .4; }
.wordmark {
  font-family: Archivo, sans-serif; font-weight: 400;
  letter-spacing: .30em; font-size: 12px; color: var(--porcelain);
  transform: translateX(.15em);
}
.badge {
  font-family: Archivo, sans-serif; font-size: 8.5px; font-weight: 500;
  letter-spacing: .16em; text-transform: uppercase;
  color: var(--steel); border: 1px solid var(--rule);
  padding: 2px 6px; border-radius: var(--r-sm); line-height: 1;
}
.badge.live { color: var(--porcelain); border-color: rgba(71,144,122,.5); }
/* Orders on this chart reach a real account. Filled rather than
   outlined, because the difference between "watching" and "trading" has
   to survive peripheral vision. No pulsing — Aethos does not do alarm. */
.badge.armed {
  color: var(--deep); background: var(--down);
  border-color: var(--down); font-weight: 600;
}
/* The chart is showing a PREVIOUS session. Filled, for the same reason the
   armed badge is filled: the difference between "now" and "a recording of
   another day" has to survive peripheral vision, and it did not — a stale
   display went unnoticed for 11.5 hours because replay rendered no badge at
   all and the account branch underneath it printed the live-coloured
   `mirror · read only` instead.
   `--signal` rather than a new hue: the palette reserves up/down for
   aggression and porcelain for liquidity, and signal is already the tone
   for a computed reference rather than a live fact, which is exactly what
   a replayed session is. Not red — nothing here is dangerous, arming is
   refused while the display is not live. It is just not now. */
.badge.replay {
  color: var(--porcelain); background: var(--signal);
  border-color: var(--signal); font-weight: 600;
}
.badge:empty { display: none; }

/* Execution toggle. Three visual states that must never be mistaken for
   each other at a glance:
     MIRROR          — quiet outline, the default
     ARM? (confirm)  — outlined danger, waiting for the second click
     ARMED           — filled. Simulator route keeps the calm teal;
                       a real route gets the same danger fill as the
                       armed badge, because they are the same claim. */
#btnArm.confirm { color: var(--porcelain); border-color: var(--down); }
#btnArm.armed-sim {
  color: var(--deep); background: var(--up);
  border-color: var(--up); font-weight: 600;
}
#btnArm.armed-real {
  color: var(--deep); background: var(--down);
  border-color: var(--down); font-weight: 600;
}

.right { margin-left: auto; display: flex; align-items: center; gap: 12px; }
/* account read-out: label above, number below, so it stays legible at a
   glance without taking a panel's worth of space */
.tally { display: flex; flex-direction: column; gap: 1px; line-height: 1.1; }
.tally span {
  font-family: Archivo, sans-serif; font-size: 8px; font-weight: 500;
  letter-spacing: .18em; color: var(--steel);
}
.tally b {
  font-weight: 500; font-size: 11.5px; letter-spacing: .03em;
  font-variant-numeric: tabular-nums; color: var(--porcelain);
  white-space: nowrap;
}
.tally b.up { color: var(--up); } .tally b.down { color: var(--down); }
/* The account is mid-swap and every number beside this is a held value
   (#102). Deliberately loud: the failure it replaces was a readout that
   looked exactly as confident when it was showing a different account
   entirely, so a subtle treatment would repeat the original mistake. */
.tally b.held {
  color: var(--down); border: 1px solid var(--down); border-radius: 2px;
  padding: 0 4px; animation: heldPulse 1.4s ease-in-out infinite;
}
@keyframes heldPulse { 50% { opacity: .45; } }
.hdiv { width: 1px; height: 17px; background: var(--rule); }
#btnLockOut:hover { border-color: var(--down); color: var(--porcelain); }
.clock {
  font-weight: 500; font-size: 13px; font-variant-numeric: tabular-nums;
  letter-spacing: .04em;
}
.clock span {
  color: var(--steel); font-weight: 400; font-size: 9px;
  font-family: Archivo, sans-serif; letter-spacing: .18em; margin-left: 5px;
}

/* ============ controls ============ */
button, select, input[type=range] {
  font-family: Archivo, sans-serif; font-size: 10px; font-weight: 500;
  letter-spacing: .14em;
  background: transparent; color: var(--steel);
  border: 1px solid transparent; border-radius: var(--r-sm);
  height: 25px; padding: 0 10px; cursor: pointer;
  transition: color var(--t), border-color var(--t), background var(--t);
}
button:hover, select:hover { color: var(--porcelain); background: var(--hover); }
button.ghost { border-color: var(--rule); }
button.ghost:hover { border-color: var(--steel); }
button.active, button.play {
  color: var(--porcelain); border-color: var(--signal); background: var(--active-bg);
}
button:disabled { color: var(--steel-dim); cursor: default; background: none; }
/* 2px, not 1px: at 1px an outline on a 25px control is a hairline that
   reads as a border rather than as focus. */
button:focus-visible, select:focus-visible, input:focus-visible,
.xsel-btn:focus-visible {
  outline: 2px solid var(--witness); outline-offset: 1px;
}
/* the real <select> stays in the DOM as the source of truth, but the OS
   menu is never shown — .xsel draws it in the palette instead */
.xsel select { display: none; }
.xsel { position: relative; display: inline-flex; }
.xsel-btn {
  display: inline-flex; align-items: center; justify-content: space-between;
  gap: 7px; width: 100%; height: 25px; padding: 0 8px;
  border: 1px solid var(--rule); border-radius: var(--r-sm);
  font-family: Archivo, sans-serif; font-size: 10px; font-weight: 500;
  letter-spacing: .12em; color: var(--porcelain); background: transparent;
}
.xsel-btn span { white-space: nowrap; }
.xsel-btn svg {
  width: 8px; height: 5px; flex: 0 0 auto; fill: none;
  stroke: var(--steel); stroke-width: 1.4; stroke-linecap: round;
  stroke-linejoin: round; transition: transform var(--t);
}
.xsel-btn:hover { background: var(--hover); border-color: var(--steel); }
.xsel-btn.open { border-color: var(--signal); background: var(--active-bg); }
.xsel-btn.open::after { display: none; }     /* tip must not sit under its own menu */
.xsel-btn.open svg { transform: rotate(180deg); }
.xsel-menu {
  position: fixed; z-index: 60; padding: 4px;
  background: var(--ink); border: 1px solid var(--rule);
  border-radius: var(--r-md); box-shadow: 0 12px 34px rgba(0,0,0,.6);
  display: flex; flex-direction: column; gap: 1px;
}
.xsel-menu button {
  height: 25px; padding: 0 11px; border: none; border-radius: var(--r-sm);
  text-align: left; font-family: Archivo, sans-serif; font-size: 10px;
  letter-spacing: .12em; color: var(--steel); white-space: nowrap;
}
.xsel-menu button:hover { color: var(--porcelain); background: var(--hover); }
.xsel-menu button.on {
  color: var(--porcelain); background: var(--active-bg);
  box-shadow: inset 2px 0 0 var(--signal);
}

/* segmented timeframe control (TradingView-ish, Aethos-flat) */
.seg { display: flex; align-items: center; gap: 1px; }
.seg button {
  min-width: 34px; padding: 0 8px; border-radius: 0; border-color: transparent;
}
.seg button:first-child { border-radius: var(--r-sm) 0 0 var(--r-sm); }
.seg button:last-child { border-radius: 0 var(--r-sm) var(--r-sm) 0; }
.seg button.active {
  color: var(--porcelain); background: var(--active-bg);
  border-color: transparent; box-shadow: inset 0 -1px 0 var(--signal);
}

/* ============ main layout ============ */
main { flex: 1 1 auto; display: flex; min-height: 0; position: relative; }

/* Rail and ladder FLOAT over a full-bleed chart — panels beside the
   chart can't be see-through, they just show the same ground. */
.rail {
  position: absolute; left: 0; top: 0; bottom: 0; z-index: 4;
  width: var(--rail-w);
  display: flex; flex-direction: column; align-items: center; gap: 1px;
  padding: 6px 0;
  background: rgba(8, 13, 17, .38);
  backdrop-filter: blur(9px); -webkit-backdrop-filter: blur(9px);
  border-right: 1px solid var(--rule-soft);
}
.railbtn {
  width: 32px; height: 32px; padding: 0; border-radius: var(--r-md);
  display: flex; align-items: center; justify-content: center;
  color: var(--steel); border: none;
}
.railbtn svg {
  width: 16px; height: 16px; fill: none; stroke: currentColor;
  stroke-width: 1.5; stroke-linecap: round; stroke-linejoin: round;
}
.railbtn:hover { color: var(--porcelain); background: var(--hover); }
.railbtn.active { color: var(--porcelain); background: var(--active-bg); }
.raildiv { width: 22px; height: 1px; background: var(--rule); margin: 5px auto; }

.stage { position: absolute; inset: 0; }
#chart { display: block; width: 100%; height: 100%; background: var(--deep); }
/* Pointer ephemera live on their own transparent layer so that moving
   the mouse repaints two dashed lines instead of the whole chart. It
   must never eat events — the chart canvas under it owns the pointer. */
#overlay {
  position: absolute; inset: 0; width: 100%; height: 100%;
  pointer-events: none;
}

/* resizable ladder — also floating */
.splitter {
  position: absolute; top: 0; bottom: 0; z-index: 5;
  right: var(--dom-w); width: 9px; margin-right: -4px;
  cursor: col-resize; background: transparent;
  transition: background var(--t);
}
/* A 5px fully transparent strip is a control nobody finds. A hairline at
   rest says "this edge moves" without becoming furniture. */
.splitter::before {
  content: ""; position: absolute; top: 0; bottom: 0; left: 4px;
  width: 1px; background: var(--rule-soft);
  transition: background var(--t);
}
.splitter:hover::before, .splitter.dragging::before {
  background: var(--signal); box-shadow: 0 0 0 1px var(--signal);
}
/* Right column: the ladder and nothing else. Everything about the
   current trade belongs on the chart, next to the price it concerns. */
#side {
  position: absolute; right: 0; top: 0; bottom: 0; z-index: 4;
  width: var(--dom-w);
  display: flex; flex-direction: column; min-height: 0;
  background: rgba(8, 13, 17, .42);
  backdrop-filter: blur(9px); -webkit-backdrop-filter: blur(9px);
  border-left: 1px solid var(--rule);
}
/* height must be explicit — a canvas otherwise collapses to its
   intrinsic width/height attributes instead of filling the flex track */
/* full height, and it must be explicit — a canvas otherwise collapses to
   its intrinsic height attribute. The ladder maps prices with the
   CHART's y(), so its top edge has to line up with the chart's or every
   row sits at the wrong price. */
#dom {
  flex: 1 1 auto; width: 100%; height: 100%; min-height: 0;
  display: block; background: transparent;
}

/* circuit breaker — deliberately calm: no red, no motion */
#lockout {
  position: absolute; inset: 0; z-index: 25;
  background: rgba(8, 13, 17, .96);
  display: flex; align-items: center; justify-content: center;
}
.lock-in { text-align: center; max-width: 430px; padding: 0 24px; }
.lock-h {
  font-family: "IBM Plex Mono", monospace; font-size: 11px;
  letter-spacing: .30em; color: var(--steel); margin-bottom: 18px;
}
.lock-clock {
  font-size: 46px; font-weight: 400; font-variant-numeric: tabular-nums;
  letter-spacing: .04em; margin-bottom: 18px;
}
.lock-msg {
  font-family: Newsreader, Georgia, serif; font-size: 16px; line-height: 1.7;
  color: var(--porcelain); margin-bottom: 26px; letter-spacing: .01em;
}
#btnEndLock {
  font-family: Archivo, sans-serif; font-size: 9.5px; letter-spacing: .14em;
  color: var(--steel); border-color: transparent; text-transform: none;
}
#btnEndLock:hover { color: var(--porcelain); }

/* ============ chart legend (persistent, TradingView) ============ */
.legend {
  /* clear of the floating rail */
  position: absolute; top: 9px; left: calc(var(--rail-w) + 11px); z-index: 3;
  pointer-events: none; display: flex; flex-direction: column; gap: 3px;
}
.legend-row { display: flex; align-items: center; gap: 9px; white-space: nowrap; }
.lg-sym {
  font-family: Archivo, sans-serif; font-size: 11px; font-weight: 500;
  letter-spacing: .18em; color: var(--porcelain);
}
.lg-tf {
  font-family: Archivo, sans-serif; font-size: 9px; letter-spacing: .16em;
  color: var(--steel);
}
.ohlc { font-size: 10.5px; font-variant-numeric: tabular-nums; color: var(--steel); }
.ohlc i { font-style: normal; color: var(--steel-soft); margin-right: 3px; }
.ohlc b { font-weight: 400; color: var(--porcelain); }
.ohlc b.up { color: var(--up); }
.ohlc b.down { color: var(--down); }

/* measure readout */
#measureBox {
  position: absolute; z-index: 4; pointer-events: none;
  background: var(--float); border: 1px solid var(--float-border);
  border-radius: var(--r-md); padding: 6px 10px;
  backdrop-filter: blur(6px); -webkit-backdrop-filter: blur(6px);
  font-size: 10.5px; font-variant-numeric: tabular-nums; line-height: 1.6;
  color: var(--porcelain); white-space: nowrap;
}
#measureBox i { font-style: normal; color: var(--steel); margin-right: 5px; }

/* ============ popovers ============ */
.pop {
  position: absolute; z-index: 12;
  background: var(--ink); border: 1px solid var(--rule); border-radius: var(--r-md);
  padding: 6px; min-width: 208px;
  box-shadow: 0 12px 34px rgba(0,0,0,.6);
  /* app.js clamps the popover's TOP into <main> but nothing clamps its
     height, so on a short window the tail of the list was simply cut off
     with no way to reach it — EXECUTION is the last section, which put the
     bracket-stop distance out of reach exactly when the window was too
     small to spare the room. Percentages resolve against <main>, and the
     12px matches the 4px top / 8px bottom inset app.js already leaves. */
  max-height: calc(100% - 12px); overflow-y: auto; overscroll-behavior: contain;
  scrollbar-width: thin; scrollbar-color: var(--rule) transparent;
}
.pop::-webkit-scrollbar { width: 8px; }
.pop::-webkit-scrollbar-track { background: transparent; }
.pop::-webkit-scrollbar-thumb {
  background: var(--rule); border-radius: 4px;
  border: 2px solid var(--ink);
}
.pop::-webkit-scrollbar-thumb:hover { background: var(--steel); }
.pop-head {
  font-family: Archivo, sans-serif; font-size: 8.5px; font-weight: 500;
  letter-spacing: .22em; color: var(--steel);
  padding: 7px 8px 5px;
}
/* min-height, not height. Rows carrying a sub-label are 31px of content
   in a 27px box, so the label overflowed 2px top and bottom: every
   sub-label collided with the title of the row beneath it, and the toggle
   stopped aligning with the title it belongs to. The padding keeps
   single-line rows on their original 27px rhythm. */
.pop .row {
  display: flex; align-items: center; justify-content: space-between;
  gap: 16px; min-height: 27px; padding: 3px 8px; border-radius: var(--r-sm);
  cursor: pointer; font-size: 11px; color: var(--porcelain);
}
.pop .row:hover { background: var(--hover); }
/* switch */
.pop input[type=checkbox] {
  appearance: none; -webkit-appearance: none;
  width: 30px; height: 17px; border-radius: 9px; flex: 0 0 auto;
  background: var(--rule); border: none; cursor: pointer; position: relative;
  transition: background var(--t);
}
.pop input[type=checkbox]:checked { background: var(--signal); }
.pop input[type=checkbox]::after {
  content: ""; position: absolute; top: 2px; left: 2px;
  width: 13px; height: 13px; border-radius: 50%;
  background: var(--porcelain); transition: transform .18s ease;
}
.pop input[type=checkbox]:checked::after { transform: translateX(13px); }

/* sub-label inside a settings row — says what the thing actually is.
   Direct child only: the dropdown button carries its own <span>. */
.pop .row > span { display: flex; flex-direction: column; gap: 1px; }
.pop .row .xsel-btn { min-width: 86px; }
.pop .row > span i {
  font-style: normal; font-size: 9px; color: var(--steel);
  letter-spacing: .02em;
}

/* ============ tooltips ============
   Native title= waits ~1s and renders as an OS box that ignores the
   palette. These appear quickly and match the floating surfaces. */
[data-tip] { position: relative; }
[data-tip]::after {
  content: attr(data-tip);
  position: absolute; z-index: 40;
  background: var(--float); border: 1px solid var(--float-border);
  color: var(--porcelain); border-radius: var(--r-sm);
  padding: 4px 8px; white-space: nowrap; pointer-events: none;
  font-family: Archivo, sans-serif; font-size: 9px; font-weight: 500;
  letter-spacing: .12em; text-transform: uppercase; line-height: 1.4;
  backdrop-filter: blur(6px); -webkit-backdrop-filter: blur(6px);
  box-shadow: 0 6px 18px rgba(0, 0, 0, .5);
  opacity: 0; visibility: hidden;
  transition: opacity .1s ease .3s, visibility 0s linear .4s;
}
[data-tip]:hover::after { opacity: 1; visibility: visible; transition-delay: .3s, 0s; }
/* the aside hugs the right edge, so its tips must not open rightwards */
#side [data-tip]::after { top: calc(100% + 6px); right: 0; }
/* rail sits at the left edge, so its tips open to the right */
.rail [data-tip]::after { left: calc(100% + 9px); top: 50%; transform: translateY(-50%); }
/* header controls drop downwards */
header [data-tip]::after { top: calc(100% + 7px); right: 0; }
/* cockpit controls rise, so they clear the window edge */
footer [data-tip]::after { bottom: calc(100% + 7px); left: 50%; transform: translateX(-50%); }
.splitter[data-tip]::after { display: none; }

/* context menu */
#ctxMenu {
  position: fixed; z-index: 20; min-width: 196px;
  background: var(--ink); border: 1px solid var(--rule); border-radius: var(--r-md);
  padding: 4px; box-shadow: 0 12px 34px rgba(0,0,0,.6);
}
#ctxMenu button {
  display: flex; justify-content: space-between; align-items: center;
  gap: 16px; width: 100%; height: 27px; padding: 0 9px;
  border: none; border-radius: var(--r-sm); text-align: left;
  font-family: Archivo, sans-serif; font-size: 10.5px; letter-spacing: .1em;
  color: var(--porcelain);
}
#ctxMenu button:hover:not(:disabled) { background: var(--hover); }
#ctxMenu button:disabled { color: var(--steel-dim); }
#ctxMenu button i {
  font-style: normal; font-family: "IBM Plex Mono", monospace;
  color: var(--steel); font-size: 10px;
}
#ctxMenu hr { border: none; border-top: 1px solid var(--rule); margin: 4px 2px; }

/* ============ loading overlay ============ */
#seek {
  position: absolute; inset: 0; z-index: 15;
  display: flex; align-items: center; justify-content: center;
  background: rgba(8, 13, 17, .58); backdrop-filter: blur(2px);
}
.seek-box {
  background: var(--ink); border: 1px solid var(--rule); border-radius: var(--r-md);
  padding: 18px 26px; width: 264px; text-align: center;
}
.seek-label {
  font-family: Archivo, sans-serif; font-size: 9px; font-weight: 500;
  letter-spacing: .22em; color: var(--steel); margin-bottom: 6px;
}
.seek-time { font-size: 15px; font-weight: 500; margin-bottom: 12px; font-variant-numeric: tabular-nums; }
.seek-track { height: 2px; background: var(--rule); border-radius: 1px; overflow: hidden; }
.seek-fill { height: 100%; width: 0%; background: var(--signal); transition: width .2s linear; }
.seek-fill.indet { animation: seekSlide 1.2s ease-in-out infinite; }
@keyframes seekSlide { 0% { margin-left: -40%; } 100% { margin-left: 100%; } }

/* ============ replay cockpit ============ */
footer {
  display: flex; align-items: center; gap: 16px;
  height: var(--foot-h); padding: 0 14px; background: transparent;
  border-top: 1px solid var(--rule-soft); flex: 0 0 auto;
  position: relative; z-index: 6;
}
.group { display: flex; gap: 3px; align-items: center; }
.cal { position: relative; }
button.icon {
  width: 27px; padding: 0; font-family: "IBM Plex Mono", monospace; font-size: 13px;
}
#dateBtn {
  font-family: "IBM Plex Mono", monospace; letter-spacing: .05em;
  color: var(--porcelain); min-width: 116px; border-color: var(--rule);
}
#btnPlay { min-width: 62px; }
.transport .xsel-btn { min-width: 66px; }

/* calendar */
#calPop {
  position: absolute; bottom: 36px; left: 0; z-index: 14;
  background: var(--ink); border: 1px solid var(--rule); border-radius: var(--r-md);
  padding: 11px; width: 236px; box-shadow: 0 12px 34px rgba(0,0,0,.6);
}
.cal-head { display: flex; align-items: center; justify-content: space-between; margin-bottom: 8px; }
.cal-head span {
  font-family: Archivo, sans-serif; font-size: 10.5px; font-weight: 500;
  letter-spacing: .18em; color: var(--porcelain);
}
.cal-head button { width: 25px; padding: 0; }
.cal-dow {
  display: grid; grid-template-columns: repeat(7, 1fr);
  font-family: Archivo, sans-serif; font-size: 8.5px; letter-spacing: .1em;
  color: var(--steel); text-align: center; margin-bottom: 4px;
}
.cal-grid { display: grid; grid-template-columns: repeat(7, 1fr); gap: 2px; }
.cal-grid button {
  height: 26px; padding: 0; border: none; border-radius: var(--r-sm);
  font-family: "IBM Plex Mono", monospace; font-size: 11px;
  color: var(--porcelain); letter-spacing: 0;
  font-variant-numeric: tabular-nums;
}
.cal-grid button:disabled { color: var(--steel-dim); background: none; }
.cal-grid button:not(:disabled):hover { background: var(--hover); }
.cal-grid button.sel { background: var(--signal); color: var(--porcelain); }
.cal-grid .pad { visibility: hidden; }

/* timeline */
/* The label pruner handles the narrow case, so this may shrink freely. */
.timeline { flex: 1 1 auto; min-width: 150px; position: relative; padding-top: 11px; }
.group { flex: 0 0 auto; }
.tl-labels {
  position: relative; height: 12px;
  font-family: Archivo, sans-serif; font-size: 8.5px; font-weight: 500;
  letter-spacing: .14em; color: var(--steel);
}
.tl-labels span {
  position: absolute; transform: translateX(-50%); top: 0;
  white-space: nowrap; padding: 0 3px; background: var(--deep);
  cursor: pointer; pointer-events: auto;
  transition: color var(--t);
}
.tl-labels span:hover { color: var(--porcelain); }
.tl-labels span.dim { color: var(--steel-dim); }
.tl-labels .tl-end { left: 0; transform: none; }
.tl-labels .tl-right { left: auto; right: 0; transform: none; }
/* stretch of the session with no data in this file */
.tl-nodata {
  position: absolute; left: 0; top: 21px; height: 2px;
  background: repeating-linear-gradient(90deg, var(--rule) 0 3px, transparent 3px 6px);
  pointer-events: none;
}
#scrub {
  -webkit-appearance: none; appearance: none;
  width: 100%; height: 20px; padding: 0; border: none; background: transparent;
  cursor: pointer; position: relative; z-index: 1;
}
#scrub::-webkit-slider-runnable-track { height: 2px; background: var(--rule); border-radius: 1px; }
#scrub::-webkit-slider-thumb {
  -webkit-appearance: none; appearance: none;
  width: 11px; height: 11px; border-radius: 50%;
  background: var(--porcelain); border: none; margin-top: -4.5px;
  transition: box-shadow var(--t);
}
#scrub:hover::-webkit-slider-thumb { box-shadow: 0 0 0 4px var(--signal-halo); }
.tl-time {
  position: absolute; top: -3px; right: 0;
  font-size: 10.5px; color: var(--porcelain); font-variant-numeric: tabular-nums;
}

#toast {
  position: fixed; bottom: 74px; left: 50%; transform: translateX(-50%);
  background: var(--ink); border: 1px solid var(--rule); border-radius: var(--r-sm);
  padding: 6px 16px; opacity: 0; transition: opacity .3s; pointer-events: none;
  color: var(--porcelain); z-index: 30; font-size: 11px;
}
@media (prefers-reduced-motion: reduce) { * { transition: none !important; animation: none !important; } }

/* ---------- viewer role (#126a) ----------
   A viewer's commands are refused server-side above the whole dispatch
   chain, so this is not the boundary — it is the page telling the truth
   about itself. Two separate wrongs are being fixed here.

   OFFERING what will be refused: order size, FLAT and LOCK rendered live
   to someone whose every press comes back "viewer connection is
   read-only". A dead control that looks alive is worse than no control,
   and FLAT looking alive is worse than most.

   READING OUT what was never sent: the server strips the `account` block
   from a viewer's frames, so TRADES and NET keep their markup defaults —
   0 and an em dash. That does not read as "withheld", it reads as a flat,
   breakeven session, which is a claim about his trading day made to a
   stranger. Removed from the layout rather than blanked, so the header
   closes up instead of leaving a gap where his P&L used to be. */
body.viewer #ordQty,
body.viewer #btnFlatten,
body.viewer #btnLockOut,
body.viewer #btnArm,
body.viewer #hdHeldWrap,
body.viewer .right .tally { display: none !important; }

/* The divider earns its space only when something sits on both sides of
   it; with the execution surface gone it is a rule against the edge. */
body.viewer .right .hdiv { display: none !important; }

/* ---------- the covenant (#107) ----------
   A pure renderer of the broadcast `covenant` block. It computes nothing
   and enforces nothing — the guard is the order route, and a protocol you
   can click past is not a protocol. Everything here only SAYS what the
   server has already decided.

   House rule, and it is the whole tone: no red. The covenant is not an
   alarm and it is not a punishment. Steel and porcelain, calm and final. */

/* COOLDOWN TAKEOVER. Full-viewport rather than chart-only: a cooldown you
   can see around is a cooldown you trade around, and the point is to make
   the next five minutes uninteresting.

   `pointer-events: none` is load-bearing, not a detail. Design law 2 says
   exits are always frictionless, so FLAT, LOCK and every cancel must stay
   clickable THROUGH the dim. By the time cooldown fires the position is
   flat, but a bracket remnant may still rest, and a covenant that trapped
   a resting order behind its own overlay would be the exact bug the law
   exists to forbid. */
#covOverlay {
  position: fixed; inset: 0; z-index: 22;
  background: rgba(8, 13, 17, .85);
  display: flex; align-items: center; justify-content: center;
  pointer-events: none;
  transition: opacity .5s ease;
}
/* Esc peeks. Not dismisses — it fades for five seconds and comes back on
   its own, because a cooldown you can close is a suggestion. */
#covOverlay.peek { opacity: .06; }
.cov-in { text-align: center; max-width: 460px; padding: 0 24px; }
.cov-h {
  font-family: "IBM Plex Mono", monospace; font-size: 11px;
  letter-spacing: .30em; text-transform: uppercase;
  color: var(--steel); margin-bottom: 20px;
}
.cov-clock {
  font-size: 54px; font-weight: 300; font-variant-numeric: tabular-nums;
  letter-spacing: .04em; color: var(--porcelain); margin-bottom: 22px;
}
.cov-quote {
  font-family: Newsreader, Georgia, serif; font-size: 17px; line-height: 1.75;
  color: var(--porcelain); margin: 0 0 30px; letter-spacing: .01em;
  text-wrap: balance;
}
.cov-peek {
  font-family: Archivo, sans-serif; font-size: 9.5px; letter-spacing: .16em;
  text-transform: uppercase; color: var(--steel-dim);
}

/* REDUCE-ONLY / CLOSED banner. Slim and persistent — this state can last
   the rest of the session, so it has to be liveable rather than loud. */
#covBanner {
  position: absolute; left: 50%; transform: translateX(-50%);
  top: 8px; z-index: 14;
  display: flex; align-items: baseline; gap: 10px;
  padding: 5px 12px; border-radius: 3px;
  background: rgba(16, 20, 25, .92); border: 1px solid var(--rule);
  font-family: Archivo, sans-serif; font-size: 10.5px;
  letter-spacing: .06em; white-space: nowrap;
  pointer-events: none;
}
.cov-b-why { color: var(--steel); text-transform: uppercase; letter-spacing: .13em; }
/* The reassurance is the brighter of the two. What you can still do
   should never be quieter than what you cannot. */
.cov-b-ok { color: var(--porcelain); }

/* STOOD ASIDE. Amber, and ABOVE the cooldown takeover (z 22) rather than
   under it: when the guard cannot rule, a takeover still showing its
   countdown is stating a restriction that is not in force, and the
   correction has to be visible on top of the thing it corrects.
   Amber is the same borrowed-time colour as a spent trip allowance —
   not red, because this is not an alarm and nothing here is broken for
   the operator. It is the guard reporting its own blindness. */
#covBanner.unguarded {
  z-index: 23;
  border-color: rgba(176, 138, 79, .45);
}
#covBanner.unguarded .cov-b-why { color: var(--amber); }
.cov-chip.unguarded { color: var(--amber); border-color: rgba(176,138,79,.4); }

/* HEADER CHIP — trips, window, floor. */
.cov-chip {
  font-family: "IBM Plex Mono", monospace; font-size: 10px;
  letter-spacing: .08em; color: var(--steel);
  background: none; border: 1px solid var(--rule); border-radius: 3px;
  padding: 4px 8px; cursor: pointer;
  font-variant-numeric: tabular-nums; white-space: nowrap;
}
.cov-chip:hover { color: var(--porcelain); border-color: var(--steel-dim); }
.cov-chip.spent { color: var(--amber); border-color: rgba(176,138,79,.4); }

/* Today's frozen rules, read-only. No editor here, v1 or ever-until-
   decided: the file, edited before the session, is the interface. */
#covRules {
  position: absolute; right: 12px; top: 44px; z-index: 21;
  min-width: 230px; padding: 14px 16px;
  background: var(--ink); border: 1px solid var(--rule); border-radius: 4px;
  font-family: "IBM Plex Mono", monospace; font-size: 11px;
  color: var(--steel); line-height: 1.9;
  font-variant-numeric: tabular-nums;
}
#covRules h4 {
  margin: 0 0 8px; font-size: 9.5px; letter-spacing: .22em;
  text-transform: uppercase; color: var(--signal-hi); font-weight: 600;
}
#covRules .r { display: flex; justify-content: space-between; gap: 20px; }
#covRules .r b { color: var(--porcelain); font-weight: 500; }
#covRules .frozen {
  margin-top: 10px; padding-top: 8px; border-top: 1px solid var(--rule);
  font-size: 9.5px; letter-spacing: .1em; color: var(--steel-dim);
}
/* The stood-aside explanation. Prose, not a row — it is a paragraph
   answering a question, and setting it in the tabular rhythm of the
   rules would make it read as another rule. */
#covRules .stood-aside {
  margin: 0 0 10px; padding-bottom: 9px;
  border-bottom: 1px solid var(--rule);
  font-family: Archivo, sans-serif; font-size: 10.5px;
  line-height: 1.55; letter-spacing: .02em; color: var(--amber);
}
/* A refusal toast. Same furniture, amber rule — it stands still for six
   seconds because it is meant to be read, not noticed. */
#toast.refusal {
  border-color: rgba(176, 138, 79, .5);
  max-width: 420px; text-align: center; line-height: 1.5;
}

/* OWNER TOKEN WALL (#166). Above everything, including the covenant
   takeover: those states describe a connection that HAS data, and this
   one describes a connection that has none. No peek, no dismiss — there
   is nothing behind it to look at. */
#authWall {
  position: fixed; inset: 0; z-index: 40;
  background: var(--ink);
  display: flex; align-items: center; justify-content: center;
}
.auth-in { width: 100%; max-width: 380px; padding: 0 24px; text-align: center; }
.auth-h {
  font-family: "IBM Plex Mono", monospace; font-size: 13px;
  letter-spacing: .42em; color: var(--porcelain); margin-bottom: 14px;
}
.auth-sub {
  font-family: Archivo, sans-serif; font-size: 11.5px; line-height: 1.6;
  color: var(--steel); margin: 0 0 18px;
}
/* BOOTING — on screen from the first paint until we know. Shows only the
   wordmark and a connecting line; the prompt and its explanation would be
   a question we have not yet established anyone needs to answer. */
.auth-boot {
  font-family: "IBM Plex Mono", monospace; font-size: 10px;
  letter-spacing: .18em; color: var(--steel-dim);
}
#authWall:not(.booting) .auth-boot { display: none; }
#authWall.booting .auth-sub,
#authWall.booting .auth-row,
#authWall.booting .auth-msg,
#authWall.booting .auth-foot { display: none; }

.auth-row { display: flex; gap: 8px; }
#authToken {
  flex: 1 1 auto; min-width: 0;
  background: rgba(16, 20, 25, .9); border: 1px solid var(--rule);
  border-radius: 3px; padding: 8px 10px;
  font-family: "IBM Plex Mono", monospace; font-size: 11px;
  color: var(--porcelain); letter-spacing: .06em;
}
#authToken:focus { outline: none; border-color: var(--steel-dim); }
#authGo {
  flex: 0 0 auto; cursor: pointer;
  background: none; border: 1px solid var(--rule); border-radius: 3px;
  padding: 8px 14px;
  font-family: "IBM Plex Mono", monospace; font-size: 10px;
  letter-spacing: .16em; color: var(--steel);
}
#authGo:hover { color: var(--porcelain); border-color: var(--steel-dim); }
/* Amber, matching every other "this is a state, not a crash" message. */
.auth-msg {
  min-height: 15px; margin-top: 12px;
  font-family: "IBM Plex Mono", monospace; font-size: 10px;
  letter-spacing: .05em; color: var(--amber);
}
.auth-foot {
  margin-top: 18px; font-family: Archivo, sans-serif; font-size: 9.5px;
  letter-spacing: .04em; color: var(--steel-dim); line-height: 1.6;
}

/* FRAME TIMING (#165). Dimmer than anything that carries market
   information — it is a tool for looking at the chart, not part of the
   chart, and it must lose every contest for attention with the tape. */
#perfReadout {
  position: fixed; left: 12px; bottom: 8px; z-index: 25;
  padding: 3px 8px; border-radius: 3px;
  background: rgba(16, 20, 25, .82); border: 1px solid var(--rule);
  font-family: "IBM Plex Mono", monospace; font-size: 9.5px;
  letter-spacing: .04em; color: var(--steel-dim);
  font-variant-numeric: tabular-nums; white-space: nowrap;
  pointer-events: none;
}
