:root {
  --bg: #08090c;
  --panel: rgba(18, 20, 28, 0.9);
  --panel-2: #1a1d28;
  --line: rgba(255, 70, 85, 0.16);
  --line-strong: rgba(255, 70, 85, 0.3);
  --grid: rgba(255, 60, 75, 0.1);
  --text: #f2f4f8;
  --muted: #7a8499;
  --accent: #ff3b4a;
  --ok: #00e676;
  --warn: #ffb020;
  --err: #ff3b4a;
  --font: "DM Sans", system-ui, sans-serif;
  --mono: "JetBrains Mono", ui-monospace, monospace;
  --radius: 14px;
}

* { box-sizing: border-box; }
html, body {
  margin: 0;
  width: 100%;
  height: 100%;
  max-height: 100dvh;
  overflow: hidden;
}
body {
  font: 15px/1.5 var(--font);
  color: var(--text);
  background-color: var(--bg);
  background-image:
    linear-gradient(var(--grid) 1px, transparent 1px),
    linear-gradient(90deg, var(--grid) 1px, transparent 1px),
    radial-gradient(ellipse 80% 50% at 50% -10%, rgba(255, 59, 74, 0.12), transparent 55%);
  background-size: 48px 48px, 48px 48px, 100% 100%;
  background-attachment: fixed;
  -webkit-font-smoothing: antialiased;
}

/* Explicit two-row canvas: topbar + fill — avoids broken flex/% height chains */
#app {
  width: 100%;
  max-width: 100%;
  height: 100dvh;
  max-height: 100dvh;
  display: grid;
  /* minmax(0,1fr) is required: default auto column uses min-content and a
     wide ticker/table blew the whole shell to ~75k px (only SYM column visible). */
  grid-template-columns: minmax(0, 1fr);
  grid-template-rows: auto minmax(0, 1fr);
  min-height: 0;
  min-width: 0;
  overflow: hidden;
}
#app > .topbar {
  grid-row: 1;
  min-width: 0;
  max-width: 100%;
}
#app > .main {
  grid-row: 2;
  min-height: 0;
  min-width: 0;
  max-width: 100%;
}

.login-wrap {
  min-height: 100%;
  height: 100%;
  display: grid;
  place-items: center;
  padding: 24px;
  overflow: auto;
}
.login-card {
  width: min(400px, 100%);
  padding: 28px 26px;
  border: 1px solid var(--line-strong);
  border-radius: var(--radius);
  background: var(--panel);
  box-shadow: 0 16px 48px rgba(0,0,0,.45);
  display: flex;
  flex-direction: column;
  gap: 10px;
}
.login-card h1 { margin: 0; font-size: 20px; }
.login-card .sub { margin: 0 0 8px; color: var(--muted); font-size: 13px; }
.login-card label { font-size: 12px; color: var(--muted); font-weight: 600; }
.login-card input {
  width: 100%;
  padding: 11px 12px;
  border-radius: 10px;
  border: 1px solid var(--line);
  background: var(--panel-2);
  color: var(--text);
  font: inherit;
}
.login-card input:focus {
  outline: none;
  border-color: rgba(255,59,74,.5);
  box-shadow: 0 0 0 3px rgba(255,59,74,.12);
}

.brand-mark {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 48px;
  height: 48px;
  border-radius: 12px;
  /* Matches the app icon: dark field, red L (down) + green F (up). */
  background: linear-gradient(145deg, #141820, #0b0e14);
  box-shadow: inset 0 0 0 1px rgba(255,255,255,.06);
  font-weight: 800;
  font-size: 12px;
  letter-spacing: .04em;
  margin-bottom: 6px;
}
.brand-mark b,
.brand-mark i {
  font-style: normal;
  font-weight: 800;
}
.brand-mark b { color: #ff4d5a; }
.brand-mark i { color: #00e676; }

.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  border: 1px solid var(--line);
  background: var(--panel-2);
  color: var(--text);
  border-radius: 10px;
  padding: 10px 14px;
  font: inherit;
  font-weight: 600;
  cursor: pointer;
}
.btn:hover { border-color: var(--line-strong); }
.btn-primary {
  background: linear-gradient(135deg, #ff4d5a, #d4142c);
  border-color: transparent;
  color: #fff;
  margin-top: 6px;
}
.btn-primary:disabled { opacity: .55; cursor: wait; }
.btn-ghost { background: transparent; }

.alert-error {
  padding: 10px 12px;
  border-radius: 10px;
  background: rgba(255,59,74,.12);
  border: 1px solid rgba(255,59,74,.35);
  color: #ffb0b6;
  font-size: 13px;
}

.topbar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  padding: 8px 12px;
  border-bottom: 1px solid var(--line-strong);
  background: linear-gradient(180deg, rgba(18,20,28,.96), rgba(12,13,18,.92));
  flex-shrink: 0;
  z-index: 20;
  backdrop-filter: blur(14px);
  min-height: 52px;
  max-height: 56px;
  width: 100%;
  max-width: 100%;
  min-width: 0;
  /* x-clip keeps wide children from expanding the shell; y stays visible for alert panel */
  overflow-x: hidden;
  overflow-y: visible;
}
.brand {
  display: flex;
  align-items: center;
  gap: 8px;
  flex: 0 1 auto;
  min-width: 0;
}
.brand .brand-mark {
  width: 32px !important;
  height: 32px !important;
  font-size: 11px;
  margin: 0 !important;
  flex-shrink: 0;
}
.brand h1 { margin: 0; font-size: 14px; font-weight: 700; line-height: 1.15; white-space: nowrap; }
.brand .sub {
  margin: 0;
  font-size: 11px;
  color: var(--muted);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  max-width: 200px;
}
@media (max-width: 1100px) {
  .brand .sub { display: none; }
}
.topbar-right {
  display: flex;
  align-items: center;
  gap: 6px;
  flex: 1 1 auto;
  justify-content: flex-end;
  min-width: 0;
  flex-wrap: nowrap;
  /* visible so alert-panel dropdown is not clipped */
  overflow: visible;
}
.topbar-right .btn {
  padding: 6px 10px;
  font-size: 12px;
  flex-shrink: 0;
  white-space: nowrap;
}
.topbar-right .btn-ghost { margin-top: 0; }
.pill {
  font-family: var(--mono);
  font-size: 11px;
  padding: 4px 8px;
  border-radius: 999px;
  border: 1px solid var(--line);
  background: var(--panel-2);
  color: var(--muted);
  white-space: nowrap;
  flex-shrink: 0;
}
.pill.user-pill {
  max-width: 140px;
  overflow: hidden;
  text-overflow: ellipsis;
  flex-shrink: 1;
  min-width: 0;
}
.pill.ok { color: var(--ok); border-color: rgba(0,230,118,.35); background: rgba(0,230,118,.1); }
.pill.warn { color: var(--warn); border-color: rgba(255,176,32,.35); }
.pill.err { color: var(--err); border-color: rgba(255,59,74,.4); }
@media (max-width: 900px) {
  .pill.user-pill { display: none; }
  #session-pill { display: none; }
}

.main {
  max-width: 1280px;
  margin: 0 auto;
  padding: 18px 18px 40px;
  display: flex;
  flex-direction: column;
  gap: 14px;
  width: 100%;
}

/* Dashboard: natural page scroll so every section (incl. bottom tables) is reachable */
.main.main-dashboard {
  display: flex !important;
  flex-direction: column;
  width: 100%;
  max-width: 100%;
  min-width: 0;
  height: auto;
  min-height: 100%;
  margin: 0;
  padding: 0;
  gap: 0;
  overflow: visible;
}
/* When dashboard is active, let the app shell scroll (not a locked viewport) */
#app:has(> .main.main-dashboard) {
  height: auto;
  max-height: none;
  min-height: 100dvh;
  width: 100%;
  max-width: 100%;
  overflow-x: hidden;
  overflow-y: auto;
  grid-template-columns: minmax(0, 1fr);
  grid-template-rows: auto auto;
  /* REQUIRED, not cosmetic. This grid has min-height:100dvh but only two
     `auto` rows, and the default align-content (`normal` = stretch) shares the
     leftover viewport height BETWEEN the rows. When the page is shorter than
     the viewport, that inflates row 1 — which holds only the 56px topbar — to
     ~204px, pushing the first card down behind a band of empty space that
     contains nothing. Measured 2026-08-09 on /#/clients: row 1 = 204px, main
     starting at y=204 instead of y=56.

     Only shows up when content does NOT fill the screen, which is why it reads
     as "the card sits in the middle" on a short page and looks fine on a long
     one. `start` keeps both rows at their content height and lets the leftover
     space fall below the last card, where it belongs. */
  align-content: start;
}
html:has(#app > .main.main-dashboard),
body:has(#app > .main.main-dashboard) {
  height: auto;
  max-height: none;
  width: 100%;
  max-width: 100%;
  overflow-x: hidden;
  overflow-y: auto;
}

/* Horizontal scrolling ticker — always visible on dashboard */
.ticker-bar {
  grid-row: 1;
  display: flex;
  align-items: stretch;
  gap: 0;
  width: 100%;
  max-width: 100%;
  min-width: 0;
  height: 32px;
  min-height: 32px;
  max-height: 32px;
  border-bottom: 1px solid var(--line-strong);
  background: rgba(12, 14, 20, 0.95);
  overflow: hidden;
  flex-shrink: 0;
}
.ticker-label {
  flex-shrink: 0;
  padding: 0 10px;
  font-size: 10px;
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--accent);
  border-right: 1px solid var(--line);
  background: rgba(255, 59, 74, 0.08);
  display: flex;
  align-items: center;
}
.ticker-track {
  flex: 1;
  min-width: 0;
  overflow: hidden;
  display: flex;
  align-items: center;
  mask-image: linear-gradient(90deg, transparent, #000 3%, #000 97%, transparent);
}
.ticker-inner {
  display: inline-flex;
  align-items: center;
  gap: 22px;
  padding: 0 14px;
  white-space: nowrap;
  /* Slow horizontal marquee — duration scales with content length via JS if set */
  animation: ticker-scroll var(--ticker-duration, 180s) linear infinite;
  will-change: transform;
}
.ticker-inner:hover { animation-play-state: paused; }
.ticker-item {
  display: inline-flex;
  align-items: baseline;
  gap: 6px;
  font-family: var(--mono);
  font-size: 11px;
  line-height: 1;
}
.ticker-item .sym { font-weight: 700; color: var(--text); }
.ticker-item .px { color: var(--muted); }
.ticker-item .pct {
  font-weight: 600;
  font-size: 10px;
  color: var(--muted);
  min-width: 3.8em;
}
.ticker-item.up .px,
.ticker-item.up .pct { color: var(--ok); }
.ticker-item.down .px,
.ticker-item.down .pct { color: var(--err); }
.ticker-item .dot {
  width: 4px;
  height: 4px;
  border-radius: 50%;
  background: var(--muted);
  display: inline-block;
  align-self: center;
}
.ticker-item.up .dot { background: var(--ok); }
.ticker-item.down .dot { background: var(--err); }
@keyframes ticker-scroll {
  from { transform: translateX(0); }
  to { transform: translateX(-50%); }
}

.dash-layout {
  flex: 1 1 auto;
  width: 100%;
  max-width: 100%;
  min-width: 0;
  min-height: 0;
  height: auto;
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(260px, 280px);
  grid-template-rows: auto;
  gap: 10px;
  padding: 10px 12px 32px;
  overflow: visible;
  align-items: start;
}
.dash-primary {
  min-width: 0;
  max-width: 100%;
  min-height: 0;
  height: auto;
  overflow: visible;
  display: flex;
  flex-direction: column;
  gap: 10px;
  padding-right: 2px;
  /* Extra room so Metrics / Candle freshness aren't clipped at the fold */
  padding-bottom: 48px;
}
.dash-primary > .card {
  min-width: 0;
  max-width: 100%;
  overflow: hidden; /* contain table min-content so grid column can shrink */
}
.dash-feed {
  min-width: 0;
  /* Sticky live feed while the main column scrolls with the page */
  position: sticky;
  top: 10px;
  height: calc(100dvh - 72px);
  max-height: calc(100dvh - 72px);
  display: flex;
  flex-direction: column;
  padding: 10px 10px 8px;
  overflow: hidden;
  align-self: start;
}
.dash-feed-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 6px;
  margin-bottom: 4px;
  flex-shrink: 0;
}
.dash-feed-head h3 { margin: 0; font-size: 13px; }
.dash-feed-head .sub { font-size: 11px; margin: 2px 0 0; }
.live-feed-sort {
  display: flex;
  flex-wrap: wrap;
  gap: 4px;
  margin: 0 0 6px;
  flex-shrink: 0;
}
.feed-sort-btn {
  border: 1px solid var(--line);
  background: var(--panel-2);
  color: var(--muted);
  border-radius: 999px;
  padding: 3px 8px;
  font-size: 10px;
  font-weight: 700;
  letter-spacing: 0.03em;
  text-transform: uppercase;
  cursor: pointer;
  font-family: var(--font);
}
.feed-sort-btn:hover {
  color: var(--text);
  border-color: var(--line-strong);
}
.feed-sort-btn.on {
  color: #fff;
  background: rgba(255, 59, 74, 0.2);
  border-color: rgba(255, 59, 74, 0.45);
}
.live-feed-list {
  flex: 1 1 auto;
  min-height: 0;
  overflow-y: auto;
  overflow-x: hidden;
  display: flex;
  flex-direction: column;
  gap: 1px;
  font-family: var(--mono);
  font-size: 11px;
  scrollbar-gutter: stable;
}
.live-feed-row {
  display: grid;
  grid-template-columns: 42px minmax(0, 1fr) auto auto;
  gap: 6px;
  align-items: baseline;
  padding: 5px 6px;
  border-radius: 6px;
  border: 1px solid transparent;
  /* No enter animation on every rebuild — that caused constant flicker on live ticks */
  flex-shrink: 0;
  background: transparent;
  /* Smooth price/dir color changes without full repaint flash */
  transition: background 0.25s ease, border-color 0.25s ease, color 0.15s ease;
}
.live-feed-row:nth-child(odd of .live-feed-row) {
  background: rgba(255, 255, 255, 0.02);
}
.live-feed-row.up {
  border-color: rgba(0, 230, 118, 0.18);
  background: rgba(0, 230, 118, 0.06);
}
.live-feed-row.down {
  border-color: rgba(255, 59, 74, 0.2);
  background: rgba(255, 59, 74, 0.06);
}
/* Only brand-new symbols animate in once */
.live-feed-row.feed-new {
  animation: feed-in 0.28s ease-out;
}
.live-feed-row .t { color: var(--muted); font-size: 10px; }
.live-feed-row .s {
  font-weight: 600;
  color: var(--text);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.live-feed-row .s a {
  color: inherit;
  text-decoration: none;
  border-bottom: 1px dashed rgba(255, 59, 74, 0.35);
}
.live-feed-row .p { font-weight: 600; font-size: 11px; }
.live-feed-row .p.up { color: var(--ok); }
.live-feed-row .p.down { color: var(--err); }
.live-feed-row .pct {
  font-weight: 600;
  font-size: 10px;
  min-width: 4em;
  text-align: right;
  color: var(--muted);
}
.live-feed-row .pct.up { color: var(--ok); }
.live-feed-row .pct.down { color: var(--err); }
/* Source column hidden — cramped; still available via title on price */
.live-feed-row .src { display: none; }
.live-feed-empty {
  padding: 12px 6px;
  color: var(--muted);
  font-size: 11px;
  text-align: center;
}
.live-feed-group {
  position: sticky;
  top: 0;
  z-index: 1;
  padding: 6px 8px 4px;
  font-size: 10px;
  font-weight: 700;
  letter-spacing: 0.08em;
  color: var(--accent);
  background: rgba(12, 14, 20, 0.95);
  border-bottom: 1px solid var(--line);
}
@keyframes feed-in {
  from { opacity: 0; transform: translateY(-4px); }
  to { opacity: 1; transform: none; }
}
/* Narrow: feed on top (not sticky); primary content scrolls with page */
@media (max-width: 960px) {
  .dash-layout {
    grid-template-columns: 1fr;
    grid-template-rows: auto auto;
    gap: 8px;
    padding: 8px 8px 32px;
  }
  .dash-feed {
    order: 0;
    position: relative;
    top: auto;
    height: min(40vh, 320px);
    max-height: min(40vh, 320px);
  }
  .dash-primary { order: 1; padding-bottom: 56px; }
}

.grid {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: 8px;
  flex-shrink: 0;
}
@media (max-width: 1000px) {
  .grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
@media (max-width: 560px) {
  .grid { grid-template-columns: 1fr; }
}

.card {
  border: 1px solid var(--line);
  border-radius: 12px;
  background: var(--panel);
  padding: 10px 12px;
  overflow: hidden;
}
.card h2, .card h3 {
  margin: 0 0 4px;
  font-size: 13px;
  font-weight: 700;
  letter-spacing: .02em;
}
.card .sub { color: var(--muted); font-size: 11px; margin: 0 0 8px; line-height: 1.35; }

.stat-num {
  font-family: var(--mono);
  font-size: 18px;
  font-weight: 700;
  line-height: 1.1;
}
.stat-num.ok { color: var(--ok); }
.stat-num.warn { color: var(--warn); }
.stat-num.err { color: var(--err); }
.stat-label {
  display: block;
  margin-top: 3px;
  font-size: 10px;
  text-transform: uppercase;
  letter-spacing: .05em;
  color: var(--muted);
}

.row2 {
  display: grid;
  grid-template-columns: 1.2fr .8fr;
  gap: 10px;
}
@media (max-width: 900px) {
  .row2 { grid-template-columns: 1fr; }
}

/* KPI tiles denser on dashboard */
.main-dashboard .grid > .card {
  padding: 10px 12px;
}

.table-scroll {
  overflow-x: auto;
  /* Keep tables as real tables — never set display:block on <table> */
  max-height: none;
  /* Critical: scroll port must size to the card, NOT expand with table min-content.
     Without this, table-layout:auto blew MTF/quote tables to ~75k px wide and only
     the SYM column was visible in the viewport. */
  max-width: 100%;
  min-width: 0;
  width: 100%;
}
.main-dashboard .card .table-scroll {
  max-height: none;
}
.main-dashboard #metrics-panel {
  max-height: none;
  overflow: visible;
  padding-bottom: 8px;
  display: block; /* pre/mono panel is a div, not a table */
}
table.cmp {
  width: 100%;
  max-width: 100%;
  border-collapse: collapse;
  font-size: 13px;
  /* fixed so columns share container width; auto + nowrap was expanding to 10k+ px/col */
  table-layout: fixed;
  color: var(--text);
}
table.cmp th {
  text-align: left;
  font-size: 11px;
  text-transform: uppercase;
  letter-spacing: .04em;
  color: var(--muted);
  padding: 8px 8px;
  border-bottom: 1px solid var(--line);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
table.cmp td {
  padding: 8px;
  border-bottom: 1px solid rgba(255,255,255,.04);
  font-family: var(--mono);
  font-size: 12.5px;
  white-space: nowrap;
  color: var(--text);
  vertical-align: middle;
  overflow: hidden;
  text-overflow: ellipsis;
}
table.cmp tr:hover td { background: rgba(255,59,74,.04); }
table.cmp .path { font-family: var(--font); font-weight: 600; }
table.cmp .num { text-align: right; font-variant-numeric: tabular-nums; }

/* ── Nav tabs ─────────────────────────────────────────────────────────── */
.nav-tabs {
  display: flex;
  gap: 4px;
  align-items: center;
}
.nav-link {
  color: var(--muted);
  text-decoration: none;
  font-weight: 600;
  font-size: 12px;
  padding: 5px 10px;
  border-radius: 999px;
  border: 1px solid transparent;
  white-space: nowrap;
}
.nav-link:hover { color: var(--text); border-color: var(--line); }
.nav-link.on {
  color: #fff;
  background: rgba(255, 59, 74, 0.18);
  border-color: rgba(255, 59, 74, 0.4);
}

/* ── Alert bell (channel alerts) ──────────────────────────────────────── */
.alert-bell-wrap {
  position: relative;
  flex-shrink: 0;
  z-index: 40;
}
.alert-bell-btn {
  position: relative;
  border: 1px solid var(--line);
  background: var(--panel-2);
  color: var(--text);
  border-radius: 10px;
  width: 38px;
  height: 34px;
  cursor: pointer;
  display: grid;
  place-items: center;
  padding: 0;
}
.alert-bell-btn:hover,
.alert-bell-btn[aria-expanded="true"] {
  border-color: var(--line-strong);
  background: rgba(255, 59, 74, 0.1);
}
.alert-bell-icon { font-size: 16px; line-height: 1; }
.alert-bell-badge {
  position: absolute;
  top: -4px;
  right: -4px;
  min-width: 16px;
  height: 16px;
  padding: 0 4px;
  border-radius: 999px;
  background: var(--accent);
  color: #fff;
  font: 700 10px var(--mono);
  display: grid;
  place-items: center;
}
/* fixed: survives any ancestor overflow; position set in JS on open */
.alert-panel {
  position: fixed;
  top: 56px;
  right: 12px;
  width: min(360px, calc(100vw - 24px));
  max-height: min(70vh, 480px);
  display: flex;
  flex-direction: column;
  background: rgba(14, 16, 22, 0.98);
  border: 1px solid var(--line-strong);
  border-radius: 12px;
  box-shadow: 0 16px 40px rgba(0, 0, 0, 0.5);
  z-index: 600;
  overflow: hidden;
}
.alert-panel[hidden] {
  display: none !important;
}
.alert-panel-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
  padding: 10px 12px;
  border-bottom: 1px solid var(--line);
}
.alert-panel-sub {
  margin: 0;
  padding: 6px 12px 0;
  font-size: 10px;
  color: var(--muted);
}
.alert-list {
  list-style: none;
  margin: 0;
  padding: 6px 0;
  overflow-y: auto;
  max-height: min(55vh, 400px);
}
.alert-item {
  display: flex;
  gap: 10px;
  align-items: flex-start;
  padding: 8px 12px;
  cursor: pointer;
  border-bottom: 1px solid rgba(255, 255, 255, 0.04);
}
.alert-item:hover { background: rgba(255, 59, 74, 0.08); }
.alert-item-body {
  display: flex;
  flex-direction: column;
  gap: 2px;
  min-width: 0;
  font-size: 12px;
}
.alert-item-body .tiny { font-size: 10px; }
.alert-item-link {
  justify-content: center;
  cursor: default;
  border-bottom: none;
}
.alert-item-link a {
  color: var(--accent, #ff3b4a);
  font-weight: 600;
  font-size: 12px;
  text-decoration: none;
}
.alert-item-link a:hover { text-decoration: underline; }
.alert-item-link:hover { background: transparent; }

/* ── Alerts page ──────────────────────────────────────────────────────── */
.alerts-page-card {
  width: 100%;
}
.alerts-table-scroll {
  margin-top: 10px;
  max-height: none;
  overflow: visible;
}
.alerts-page-table {
  width: 100%;
}
.alerts-page-table th {
  position: sticky;
  top: 0;
  z-index: 1;
  background: var(--panel);
}
.alerts-row:hover {
  background: rgba(255, 59, 74, 0.08);
}
.alerts-empty {
  text-align: center;
  padding: 28px 12px !important;
}
.alerts-asset-tabs {
  flex-wrap: wrap;
}

/* ── Full-canvas pages (Sim / Alerts): top-aligned, edge-to-edge ──────── */
.main.main-dashboard .page-fill {
  display: flex;
  flex-direction: column;
  align-items: stretch;
  justify-content: flex-start; /* top, not center */
  width: 100%;
  max-width: none;
  margin: 0;
  padding: 10px 12px 16px;
  min-height: calc(100dvh - 56px);
  box-sizing: border-box;
  gap: 0;
}
.main.main-dashboard .page-fill-card {
  flex: 1 1 auto;
  width: 100%;
  max-width: none;
  margin: 0;
  display: flex;
  flex-direction: column;
  align-items: stretch;
  justify-content: flex-start;
  min-height: 0;
  padding: 12px 14px 14px;
}
.main.main-dashboard .page-fill-card .table-scroll {
  flex: 1 1 auto;
  min-height: 180px;
  max-height: none;
  overflow: auto;
  width: 100%;
}
.main.main-dashboard .page-fill-card .grid {
  width: 100%;
}
.main.main-dashboard .page-fill-card .mtf-consensus-head {
  align-items: flex-start;
  width: 100%;
}
.alerts-page-card,
.sim-page .page-fill-card {
  width: 100%;
}

/* ── Sim page ─────────────────────────────────────────────────────────── */
.sim-page .kpi-card {
  padding: 10px 12px;
  min-height: 0;
}
.sim-page .kpi-val.ok { color: var(--ok, #00e676); }
.sim-page .kpi-val.err { color: var(--err, #ff3b4a); }
.sim-trade-row:hover {
  background: rgba(255, 59, 74, 0.08);
}
.sim-page .page-fill-card #sim-trades-table,
.alerts-page .page-fill-card #alerts-page-table {
  width: 100%;
}

/* Equity books grid — every sim tab */
/* _GRID_FULL_WIDTH: auto-FIT, not auto-fill. auto-fill keeps empty 260px
   tracks, so on a wide screen the cards bunch to the left and their values
   ellipsise while half the row sits blank. auto-fit drops the empty tracks and
   stretches the real ones across the full width. */
.sim-equity-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
  gap: 10px;
  width: 100%;
  margin-bottom: 4px;
}
.sim-eq-card {
  border: 1px solid var(--line);
  border-radius: 12px;
  background: var(--panel-2);
  padding: 10px 12px;
  display: flex;
  flex-direction: column;
  gap: 8px;
  transition: border-color 0.15s ease, background 0.15s ease;
}
.sim-eq-card:hover {
  border-color: var(--line-strong);
  background: rgba(255, 59, 74, 0.06);
}
.sim-eq-card-wide {
  grid-column: 1 / -1;
}
.sim-eq-head {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 8px;
}
.sim-eq-title {
  font-weight: 700;
  font-size: 14px;
  line-height: 1.2;
}
.sim-eq-sub {
  font-size: 11px;
  margin-top: 2px;
}
.sim-eq-grid {
  display: grid;
  /* auto-fit so a wide card spreads its cells instead of squeezing four into
     a narrow column; 84px is where the mono digits stop clipping. */
  grid-template-columns: repeat(auto-fit, minmax(84px, 1fr));
  gap: 6px 10px;
}
@media (max-width: 720px) {
  .sim-eq-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
.sim-eq-cell {
  min-width: 0;
}
.sim-eq-val {
  font-family: var(--mono);
  font-size: 13px;
  font-weight: 600;
  line-height: 1.25;
  /* Wrap instead of ellipsising: "$100,…" hides the number the card exists
     to show. overflow-wrap keeps a long figure inside its cell. */
  white-space: normal;
  overflow-wrap: anywhere;
}
.sim-eq-val.ok { color: var(--ok); }
.sim-eq-val.err { color: var(--err); }
.sim-eq-session {
  font-size: 12px;
}

/* ── Chart popup (fullscreen page) ────────────────────────────────────── */
.chart-popup {
  position: fixed;
  inset: 0;
  z-index: 500;
  display: flex;
  align-items: stretch;
  justify-content: stretch;
  padding: 0;
  margin: 0;
  box-sizing: border-box;
  width: 100%;
  height: 100%;
  height: 100dvh;
  max-height: 100dvh;
}
.chart-popup-backdrop {
  position: absolute;
  inset: 0;
  background: var(--bg);
  /* Full page — no dimmed card; solid fill */
  opacity: 1;
}
.chart-popup-panel {
  position: relative;
  z-index: 1;
  display: flex;
  flex-direction: column;
  width: 100%;
  height: 100%;
  max-width: none;
  max-height: none;
  margin: 0;
  background: var(--bg);
  border: 0;
  border-radius: 0;
  box-shadow: none;
  overflow: hidden;
}
.chart-popup-head {
  flex-shrink: 0;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 10px;
  padding: 10px 14px;
  border-bottom: 1px solid var(--line);
  background: rgba(14, 16, 22, 0.98);
}
.chart-popup-head .sym {
  font: 700 15px var(--mono);
  letter-spacing: 0.04em;
  color: var(--text);
}
.chart-popup-head .tf-group { margin-left: 4px; }
.chart-popup-actions {
  margin-left: auto;
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  align-items: center;
}
.chart-popup-host {
  flex: 1 1 auto;
  min-height: 0;
  width: 100%;
  height: 100%;
  background: transparent;
}
.chart-popup-status {
  flex-shrink: 0;
  padding: 6px 14px;
  font: 11px/1.4 var(--mono);
  color: var(--muted);
  border-top: 1px solid var(--line);
}
.chart-popup-status.ok { color: var(--ok); }
.chart-popup-status.err { color: var(--err); }
body.chart-popup-open { overflow: hidden; }

/* ── Chart page (full-canvas) ─────────────────────────────────────────── */
.main.main-chart {
  max-width: none;
  width: 100%;
  height: 100%;
  min-height: 0;
  display: flex;
  flex-direction: column;
  gap: 0;
  padding: 0;
  margin: 0;
  /* Breaker panels + MTF table above chart; chart host flexes. Typeahead is fixed. */
  overflow: auto;
}
.chart-toolbar-card {
  padding: 10px 14px;
  flex-shrink: 0;
  border-radius: 0;
  border-left: 0;
  border-right: 0;
  border-top: 0;
}
.chart-toolbar {
  display: flex;
  flex-wrap: wrap;
  gap: 12px;
  align-items: center;
}
.symbol-search {
  position: relative;
  min-width: 220px;
  flex: 0 1 280px;
}
.symbol-search input {
  width: 100%;
  padding: 10px 12px;
  border-radius: 10px;
  border: 1px solid var(--line);
  background: var(--panel-2);
  color: var(--text);
  font: 600 14px var(--mono);
  letter-spacing: 0.04em;
  text-transform: uppercase;
}
.symbol-search input:focus {
  outline: none;
  border-color: rgba(255, 59, 74, 0.5);
  box-shadow: 0 0 0 3px rgba(255, 59, 74, 0.12);
}
.typeahead {
  /* fixed, not absolute: .main-chart clips overflow, so an absolutely
     positioned dropdown gets cut off. Coords are set in positionTypeahead(). */
  position: fixed;
  z-index: 200;
  margin: 0;
  padding: 4px 0;
  list-style: none;
  max-height: 320px;
  overflow-y: auto;
  border: 1px solid var(--line-strong);
  border-radius: 12px;
  background: rgba(14, 16, 22, 0.98);
  box-shadow: 0 12px 32px rgba(0, 0, 0, 0.45);
}
.typeahead li button {
  width: 100%;
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 10px;
  padding: 8px 12px;
  border: 0;
  background: transparent;
  color: var(--text);
  font: inherit;
  cursor: pointer;
  text-align: left;
}
.typeahead li button:hover {
  background: rgba(255, 59, 74, 0.1);
}
.ta-sym {
  font-family: var(--mono);
  font-weight: 600;
  font-size: 13px;
}
.ta-meta {
  display: flex;
  align-items: center;
  gap: 6px;
}
.ta-cat {
  font-size: 11px;
  color: var(--muted);
  text-transform: uppercase;
  letter-spacing: 0.04em;
}
.tf-group {
  display: flex;
  flex-wrap: wrap;
  gap: 4px;
}
.tf-btn {
  border: 1px solid var(--line);
  background: var(--panel-2);
  color: var(--muted);
  border-radius: 8px;
  padding: 7px 10px;
  font: 600 12px var(--mono);
  cursor: pointer;
}
.tf-btn:hover { color: var(--text); border-color: var(--line-strong); }
.tf-btn.on {
  color: #fff;
  background: rgba(255, 59, 74, 0.22);
  border-color: rgba(255, 59, 74, 0.45);
}
.chart-meta {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  align-items: center;
  margin-left: auto;
}
.chart-card {
  padding: 0;
  /* Fill leftover viewport between fixed breaker chrome (above) and MTF (below).
     Height is stable because breaker/MTF/legend/status use locked sizes — not
     because we doubled the chart (that was a red herring for layout jump). */
  flex: 1 1 auto;
  min-height: 0;
  display: flex;
  flex-direction: column;
  border-radius: 0;
  border-left: 0;
  border-right: 0;
  border-bottom: 0;
  overflow: hidden;
  contain: layout style;
}
.chart-host {
  flex: 1 1 auto;
  width: 100%;
  min-height: 280px;
  height: 100%;
  /* Host box itself does not grow from content; autoSize only tracks this box */
  contain: size layout style;
}
.chart-status {
  flex-shrink: 0;
  padding: 6px 14px;
  font-size: 12px;
  font-family: var(--mono);
  border-top: 1px solid var(--line);
  height: 32px;
  min-height: 32px;
  max-height: 32px;
  box-sizing: border-box;
  overflow: hidden;
  white-space: nowrap;
  text-overflow: ellipsis;
}
.chart-legend {
  flex-shrink: 0;
  display: flex;
  flex-wrap: nowrap;
  gap: 12px;
  padding: 6px 14px 0;
  font-size: 11px;
  font-family: var(--mono);
  color: var(--muted);
  height: 28px;
  min-height: 28px;
  max-height: 28px;
  box-sizing: border-box;
  overflow: hidden;
  align-items: center;
}
.chart-legend b { color: var(--text); font-weight: 600; }
.chart-legend .leg-ema b { color: #22d3ee; }
.chart-legend .leg-lo b { color: #34d399; }
.chart-legend .leg-hi b { color: #fb7185; }
.chart-legend .leg-pivots { font-size: 10px; flex-shrink: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

/* Breaker panel (parity with iOS / trade.sgatrader.com)
   Fixed height = skeleton = loaded, so chart never jumps when data arrives. */
.breaker-card {
  flex-shrink: 0;
  padding: 12px 14px;
  border-radius: 0;
  border-left: 0;
  border-right: 0;
  /* Head + optional 2 MTF-why lines + SELLS/BUYS grid */
  height: 200px;
  min-height: 200px;
  max-height: 200px;
  box-sizing: border-box;
  overflow: hidden;
  contain: layout style;
}
.breaker-mtf-why-slot {
  min-height: 2.6em;
  max-height: 2.6em;
  overflow: hidden;
  margin-top: 4px;
}
.breaker-head {
  display: flex;
  flex-wrap: wrap;
  justify-content: space-between;
  gap: 10px;
  align-items: flex-start;
}
.breaker-title {
  font-size: 13px;
  font-weight: 600;
  color: var(--text);
}
.breaker-status {
  font-size: 13px;
  font-weight: 700;
  margin-top: 2px;
}
.breaker-status.ok { color: var(--ok); }
.breaker-status.warn { color: var(--warn); }
.breaker-sub { font-size: 11px; margin-top: 2px; }
.breaker-pills {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  align-items: center;
}
.breaker-mtf-why {
  font-size: 11px;
  margin-top: 6px;
  color: var(--warn);
}
.breaker-sides {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 10px;
  margin-top: 10px;
}
@media (max-width: 640px) {
  .breaker-sides { grid-template-columns: 1fr; }
}
.breaker-side {
  border: 1px solid var(--line);
  border-radius: 10px;
  padding: 10px 12px;
  background: var(--panel-2);
}
.breaker-side.on {
  border-color: rgba(0, 230, 118, 0.35);
  background: rgba(0, 230, 118, 0.06);
}
.breaker-side-top {
  display: flex;
  justify-content: space-between;
  font-size: 11px;
  font-weight: 600;
  color: var(--muted);
  margin-bottom: 4px;
}
.breaker-side-pct {
  font-size: 20px;
  font-weight: 700;
  font-family: var(--mono);
  color: var(--text);
}
.breaker-side .tiny { font-size: 11px; margin-top: 2px; }
.breaker-side .ok { color: var(--ok); }
.tf-btn.mtf-toggle.on {
  color: var(--ok);
  background: rgba(0, 230, 118, 0.12);
  border-color: rgba(0, 230, 118, 0.4);
}
/* Regression channel toggle — sgaSMR red family */
.tf-btn.reg-toggle.on {
  color: #ff8a94;
  background: rgba(255, 59, 74, 0.14);
  border-color: rgba(255, 107, 120, 0.5);
}
.chart-legend .leg-reg b { color: #ff8a94; }
.chart-legend .leg-alerts b.ok { color: var(--ok); }

.mtf-card {
  flex-shrink: 0;
  padding: 8px 14px 10px;
  border-radius: 0;
  border-left: 0;
  border-right: 0;
  /* Fit head + 6 TF rows (5m–1d) fully — no inner scroll */
  height: auto;
  min-height: 0;
  max-height: none;
  display: flex;
  flex-direction: column;
  box-sizing: border-box;
  overflow: visible;
}
.mtf-card[hidden] {
  /* Override UA [hidden] so layout slot is never removed */
  display: flex !important;
  visibility: visible;
}
.mtf-head {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  align-items: baseline;
  margin-bottom: 4px;
  flex-shrink: 0;
}
.mtf-head strong { font-size: 12px; }
.mtf-head .tiny { font-size: 10px; }
.mtf-scroll {
  /* No scroll — entire MTF table always visible */
  max-height: none;
  overflow: visible;
  flex: 0 0 auto;
}
.mtf-table {
  width: 100%;
  font-size: 11px;
  border-collapse: collapse;
  table-layout: fixed;
}
.mtf-table th {
  text-align: left;
  color: var(--muted);
  font-size: 9px;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  padding: 3px 8px;
  border-bottom: 1px solid var(--line);
  white-space: nowrap;
}
.mtf-table td {
  padding: 3px 8px;
  border-bottom: 1px solid rgba(255, 255, 255, 0.04);
  white-space: nowrap;
  line-height: 1.25;
  color: var(--text);
}
.mtf-table td.num { font-family: var(--mono); text-align: right; font-size: 11px; }
.mtf-table tr.primary-tf { background: rgba(255, 59, 74, 0.06); }
.mtf-table .ok { color: var(--ok); font-weight: 700; }
.mtf-table .err { color: var(--err); font-weight: 600; }
/* 6 voting frames + header ≈ compact strip under chart */
.mtf-table tbody tr { height: 22px; }
.breaker-footnote {
  flex-shrink: 0;
  margin: 0;
  padding: 6px 14px 10px;
  font-size: 11px;
  line-height: 1.4;
  height: 3.4em;
  min-height: 3.4em;
  max-height: 3.4em;
  box-sizing: border-box;
  overflow: hidden;
}

/* Dashboard MTF consensus (iOS parity) */
.stale-banner {
  margin: 0 0 8px;
  padding: 10px 12px;
  border-radius: 10px;
  border: 1px solid var(--line);
  background: var(--panel);
  font-size: 13px;
}
.stale-banner.warn {
  border-color: rgba(255, 176, 32, 0.4);
  background: rgba(255, 176, 32, 0.08);
  color: var(--warn);
}
.mtf-consensus-card {
  margin-bottom: 0;
  /* Don't let flex/overflow clip rows — full list viewport stays usable */
  flex: 0 0 auto;
  overflow: visible;
  min-height: 0;
}
.mtf-consensus-head {
  display: flex;
  flex-wrap: wrap;
  justify-content: space-between;
  gap: 10px;
  align-items: flex-start;
  margin-bottom: 8px;
}
.mtf-consensus-head h3 { margin: 0 0 2px; }
.mtf-filter-row {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  margin-bottom: 8px;
}
.mtf-filter-btn {
  border: 1px solid var(--line);
  background: var(--panel-2);
  color: var(--muted);
  border-radius: 6px;
  padding: 5px 10px;
  font: 700 10px var(--mono);
  letter-spacing: 0.04em;
  cursor: pointer;
}
.mtf-filter-btn:hover { color: var(--text); }
.mtf-filter-btn.on {
  color: #fff;
  background: rgba(255, 59, 74, 0.22);
  border-color: rgba(255, 59, 74, 0.45);
}
/* MTF consensus table — full 6 columns visible (SYM CLS SELL BUY TFs PX) */
.mtf-agree-scroll {
  min-height: 220px;
  max-height: min(40vh, 360px);
  height: auto;
  width: 100%;
  max-width: 100%;
  min-width: 0;
  overflow: auto;
  overflow-anchor: none;
  border: 1px solid var(--line);
  border-radius: 10px;
  background: rgba(0, 0, 0, 0.22);
  -webkit-overflow-scrolling: touch;
}
table.cmp.mtf-agree-table,
.mtf-agree-table {
  width: 100% !important;
  max-width: 100% !important;
  /* Allow a sensible floor for horizontal scroll on narrow cards, but never
     grow past the scrollport (parent max-width:100% + overflow:auto). */
  min-width: 0 !important;
  border-collapse: collapse;
  table-layout: fixed !important;
  color: var(--text);
}
.mtf-agree-table col.col-sym { width: 22%; }
.mtf-agree-table col.col-cls { width: 10%; }
.mtf-agree-table col.col-side { width: 14%; }
.mtf-agree-table col.col-tfs { width: 10%; }
.mtf-agree-table col.col-px { width: 16%; }
.mtf-agree-table thead th {
  position: sticky;
  top: 0;
  z-index: 2;
  background: #141820;
  box-shadow: 0 1px 0 var(--line);
  text-align: left;
  font-size: 11px;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--muted);
  padding: 8px 10px;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.mtf-agree-table tbody td {
  color: var(--text) !important;
  vertical-align: middle;
  padding: 8px 10px;
  border-bottom: 1px solid rgba(255, 255, 255, 0.04);
  font-family: var(--mono);
  font-size: 12.5px;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  opacity: 1 !important;
  visibility: visible !important;
}
.mtf-agree-table td.num {
  font-family: var(--mono);
  text-align: right;
  font-variant-numeric: tabular-nums;
}
.mtf-agree-table td.mtf-cls,
.mtf-agree-table td.mtf-side,
.mtf-agree-table td.mtf-tfs,
.mtf-agree-table td.mtf-px {
  color: var(--text);
}
.mtf-agree-table .path {
  font-family: var(--font);
  font-weight: 600;
}
.mtf-agree-table .path a {
  color: var(--text);
  font-weight: 600;
  text-decoration: none;
  border-bottom: 1px dashed rgba(255, 59, 74, 0.35);
}
.mtf-agree-table .path a:hover { color: #ff8a94; }
.mtf-agree-table .chip {
  display: inline-flex !important;
  align-items: center;
  padding: 2px 8px;
  border-radius: 999px;
  font-size: 11px;
  font-weight: 700;
  border: 1px solid var(--line);
  background: rgba(255, 255, 255, 0.06);
  color: var(--muted);
}
.mtf-agree-table .chip.ok {
  background: rgba(0, 230, 118, 0.14);
  color: var(--ok);
  border-color: rgba(0, 230, 118, 0.35);
}
.mtf-agree-table .chip.chip-off {
  background: rgba(255, 255, 255, 0.05);
  color: var(--muted);
}
.mtf-agree-row:hover td {
  background: rgba(255, 59, 74, 0.06);
}
.mtf-agree-empty {
  padding: 24px 12px;
  text-align: center;
  color: var(--muted);
  font-size: 13px;
}

table.cmp a {
  color: var(--text);
  text-decoration: none;
  border-bottom: 1px dashed rgba(255, 59, 74, 0.35);
}
table.cmp a:hover { color: #ff8a94; }

.toolbar {
  display: flex;
  gap: 8px;
  flex-wrap: wrap;
  align-items: center;
  margin-bottom: 10px;
}
.toolbar input {
  flex: 1;
  min-width: 160px;
  padding: 9px 12px;
  border-radius: 10px;
  border: 1px solid var(--line);
  background: var(--panel-2);
  color: var(--text);
  font: inherit;
}
.toolbar input:focus {
  outline: none;
  border-color: rgba(255,59,74,.45);
}

.chip {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 3px 8px;
  border-radius: 999px;
  font-size: 11px;
  font-weight: 700;
  background: rgba(255,59,74,.1);
  color: var(--accent);
  border: 1px solid transparent;
}
.chip.ok { background: rgba(0,230,118,.12); color: var(--ok); border-color: rgba(0,230,118,.25); }
.chip.warn { background: rgba(255,176,32,.12); color: var(--warn); border-color: rgba(255,176,32,.25); }
.chip.chip-off {
  background: rgba(255, 255, 255, 0.06);
  color: var(--muted);
  border-color: var(--line);
}

/* Dashboard tables: share container width so all columns stay in view */
#quote-table,
#stale-table,
#proxy-table {
  width: 100% !important;
  max-width: 100% !important;
  min-width: 0 !important;
  table-layout: fixed !important;
}
#quote-table th,
#quote-table td,
#stale-table th,
#stale-table td,
#proxy-table th,
#proxy-table td {
  color: var(--text);
  overflow: hidden;
  text-overflow: ellipsis;
}
/* Explicit column weights so Price/Class/etc. don't collapse to 0 */
#quote-table th:nth-child(1),
#quote-table td:nth-child(1) { width: 18%; }
#quote-table th:nth-child(2),
#quote-table td:nth-child(2) { width: 16%; }
#quote-table th:nth-child(3),
#quote-table td:nth-child(3) { width: 12%; }
#quote-table th:nth-child(4),
#quote-table td:nth-child(4) { width: 14%; }
#quote-table th:nth-child(5),
#quote-table td:nth-child(5) { width: 20%; }
#quote-table th:nth-child(6),
#quote-table td:nth-child(6) { width: 20%; }

.muted { color: var(--muted); }
.mono { font-family: var(--mono); }
.pos { color: var(--ok); }
.neg { color: var(--err); }
.warn-text { color: var(--warn); }

/* Clients page: keep candle freshness visible while the client list scrolls.
   sticky (not fixed) so it still participates in layout and cannot overlap the
   card below.

   WHICH ELEMENT SCROLLS MATTERS, and on this route it is NOT #app. An earlier
   version of this rule assumed #app was the scroll container (body
   overflow:hidden) and used top:0. But `#app:has(> .main.main-dashboard)`
   above re-declares #app as `height:auto; overflow-y:auto` and sets
   html/body to `overflow-y:auto` too — so on /#/clients the DOCUMENT scrolls,
   while the card's sticky containing block is still #app. Sticking to the top
   of a container that is itself scrolling out of view does nothing: measured
   2026-08-09, scrolling 400px moved the card's top to -348px — it scrolled
   away completely rather than pinning, which read as "sits in the middle".

   Fix: let the DOCUMENT be the only scroller on this route by dropping #app's
   own overflow (see the #app override below), so the card's sticky container
   is the one that actually scrolls. top:0 is right because .topbar is static
   (position:static, it scrolls away) — offsetting by its height would leave a
   dead band once it is gone. */
.card.clients-pinned {
  position: sticky;
  top: 0;
  z-index: 30;
  /* .card is translucent by default; opaque here so list rows scrolling
     underneath do not show through the pinned panel. */
  background: var(--bg);
  box-shadow: 0 6px 18px rgba(0, 0, 0, 0.45);
  /* Six timeframe rows plus a header can fill a phone screen. Cap the pinned
     panel so the client list below always has room; the table scrolls inside. */
  max-height: 55vh;
  overflow-y: auto;
}

/* #app declares overflow-y:auto for dashboard routes, which makes it a scroll
   container and therefore the sticky containing block for anything inside it.
   Since html/body already scroll on these routes, #app's own scrollport is
   redundant AND it is what stopped .clients-pinned from ever sticking. Scoped
   to the Clients route so the dashboard/alerts/sim routes keep their existing
   behaviour untouched. */
#app:has(.card.clients-pinned) {
  overflow-y: visible;
  overflow-x: clip;
}

/* ...and body must not be a scroll container either. The dashboard rules set
   html/body to `overflow-x:hidden`, and `hidden` (unlike `clip`) creates a
   scrollport — so BODY, not HTML, became the sticky containing block for the
   card. body is as tall as the content and never scrolls itself, so sticking
   to it pinned the card to a box that never moves: the card slid straight off
   screen (top: 0 -> -1148px at 1200px scroll). `clip` suppresses the same
   horizontal overflow WITHOUT creating a scrollport, so the sticky container
   becomes the viewport, which is what actually scrolls.

   Both axes must be declared together: `overflow-x:clip` beside an
   `overflow-y:auto` is not a usable pair, and the browser coerces the clip
   back to `hidden` — which recreates the scrollport and the bug. Setting
   overflow-y:visible alongside it keeps the clip honest, and the page still
   scrolls because HTML remains the scroller. */
html:has(#app .card.clients-pinned),
body:has(#app .card.clients-pinned) {
  overflow-x: clip;
  overflow-y: visible;
}

/* Candle-freshness bars (Clients page). A proportion reads faster than the
   number alone when scanning six timeframe rows. */
.fresh-track {
  width: 100%;
  height: 6px;
  border-radius: 999px;
  background: var(--panel-2);
  overflow: hidden;
}
.fresh-fill {
  height: 100%;
  border-radius: 999px;
  transition: width 0.3s ease;
}

/* _TD_TONE: simTone() emits .ok / .err on table cells (PnL, ROE, uPnL,
   REALIZED, RET%, group nets) but every existing .ok/.err rule was scoped to
   .pill / .stat-num / .kpi-val / .sim-eq-val — never a td. The classes were
   landing on the markup and doing nothing, so profits and losses rendered
   identically. Scoped to td/th so components that style .ok themselves are
   untouched.
   BOOK $ is intentionally left neutral: it is starting capital, not a result. */
td.ok, th.ok, td .ok { color: var(--ok); }
td.err, th.err, td .err { color: var(--err); }

/* _TD_TONE_SPECIFICITY: `table.cmp td { color: var(--text) }` above is
   (0,1,2) and beat the (0,1,1) rules just above, so simTone's classes landed
   on the markup and rendered plain text anyway. These are (0,2,2), so they
   win on specificity rather than with !important. */
table.cmp td.ok, table.cmp th.ok, table.cmp td .ok { color: var(--ok); }
table.cmp td.err, table.cmp th.err, table.cmp td .err { color: var(--err); }

/* ── Double / triple pattern cells ────────────────────────────────────── */
.pat-toggle-on {
  background: rgba(0, 200, 83, 0.18);
  border-color: rgba(0, 200, 83, 0.55);
  color: #69F0AE;
}
.pat-toggle-off {
  background: rgba(55, 71, 79, 0.5);
  border-color: var(--line);
  color: var(--muted);
}
.pat-search {
  background: var(--panel-2);
  border: 1px solid var(--line);
  color: var(--text);
  border-radius: 8px;
  padding: 6px 10px;
  font: 500 12px var(--mono);
  width: 140px;
}
.pat-cell {
  display: inline-flex;
  align-items: baseline;
  gap: 3px;
  min-width: 46px;
  justify-content: center;
  padding: 2px 6px;
  border-radius: 6px;
  font: 700 11px var(--mono);
  letter-spacing: .02em;
}
.pat-cell small {
  font-weight: 600;
  font-size: 9px;
  opacity: .85;
  text-transform: lowercase;
}
.pat-none { color: var(--muted); background: transparent; font-weight: 500; }
.pat-bot-f { background: #1B5E20; color: #C8E6C9; }
.pat-bot-c { background: #00C853; color: #003300; }
.pat-top-f { background: #7B1414; color: #FFCDD2; }
.pat-top-c { background: #D50000; color: #fff; }
.pat-inv { background: #37474F; color: #B0BEC5; }
.pat-conf-b { color: #69F0AE; font: 700 11px var(--mono); margin-right: 6px; }
.pat-conf-t { color: #FF8A80; font: 700 11px var(--mono); }
