/* Index Operations Control Center — tokens follow the validated reference palette. */
:root {
  color-scheme: light;
  --page: #f9f9f7;
  --surface: #fcfcfb;
  --ink: #0b0b0b;
  --ink-2: #52514e;
  --muted: #898781;
  --grid: #e1e0d9;
  --baseline: #c3c2b7;
  --border: rgba(11, 11, 11, 0.10);
  --series-1: #2a78d6;
  --series-2: #eb6834;
  --series-3: #1baf7a;
  --good: #0ca30c;
  --good-text: #006300;
  --warning: #fab219;
  --serious: #ec835a;
  --critical: #d03b3b;
  --accent-wash: rgba(42, 120, 214, 0.08);
  /* Opaque equivalent of accent-wash over the surface — sticky cells cannot
     use a translucent hover or the columns beneath show through. */
  --row-hover: #f0f4fb;
  /* Opaque tints, so text keeps full contrast against them. */
  --ex-today: #dbe8fa;
  --ex-today-hover: #cbdef7;
  --ex-today-link: #104281;
}
@media (prefers-color-scheme: dark) {
  :root:where(:not([data-theme="light"])) {
    color-scheme: dark;
    --page: #0d0d0d;
    --surface: #1a1a19;
    --ink: #ffffff;
    --ink-2: #c3c2b7;
    --muted: #898781;
    --grid: #2c2c2a;
    --baseline: #383835;
    --border: rgba(255, 255, 255, 0.10);
    --series-1: #3987e5;
    --series-2: #d95926;
    --series-3: #199e70;
    --good-text: #0ca30c;
    --accent-wash: rgba(57, 135, 229, 0.12);
    --row-hover: #22262d;
    --ex-today: #16304d;
    --ex-today-hover: #1d3d61;
    --ex-today-link: #9ec5f4;
  }
}
:root[data-theme="dark"] {
  color-scheme: dark;
  --page: #0d0d0d;
  --surface: #1a1a19;
  --ink: #ffffff;
  --ink-2: #c3c2b7;
  --muted: #898781;
  --grid: #2c2c2a;
  --baseline: #383835;
  --border: rgba(255, 255, 255, 0.10);
  --series-1: #3987e5;
  --series-2: #d95926;
  --series-3: #199e70;
  --good-text: #0ca30c;
  --accent-wash: rgba(57, 135, 229, 0.12);
  --row-hover: #22262d;
  --ex-today: #16304d;
  --ex-today-hover: #1d3d61;
  --ex-today-link: #9ec5f4;
}

* { box-sizing: border-box; }
html, body { margin: 0; }
body {
  font-family: system-ui, -apple-system, "Segoe UI", sans-serif;
  background: var(--page);
  color: var(--ink);
  font-size: 14px;
}
a { color: var(--series-1); text-decoration: none; }
a:hover { text-decoration: underline; }

/* ---- top bar ---- */
.topbar {
  display: flex;
  align-items: center;
  gap: 20px;
  padding: 0 20px;
  height: 52px;
  background: var(--surface);
  border-bottom: 1px solid var(--border);
  position: sticky;
  top: 0;
  z-index: 30;
}
/* Brand lockup. The full name is set on two lines so it fits the 52px bar
   without taking the width the tab strip needs; below 1100px, where the tabs
   start to scroll, it collapses to the initials. */
.brand { flex: none; font-weight: 700; letter-spacing: 0.04em; text-transform: uppercase; white-space: nowrap; }
.brand-lines { display: grid; gap: 1px; line-height: 1.1; }
.brand-lines b { font-size: 11.5px; }
.brand-lines > span { font-size: 11.5px; color: var(--series-1); }
.brand-abbr { display: none; font-size: 16px; letter-spacing: 0.06em; color: var(--series-1); }
@media (max-width: 1100px) {
  .brand-lines { display: none; }
  .brand-abbr { display: block; }
}
.tabs { display: flex; gap: 2px; overflow-x: auto; flex: 1; }
.tabs a {
  padding: 6px 12px;
  border-radius: 6px;
  color: var(--ink-2);
  white-space: nowrap;
  font-weight: 500;
}
.tabs a:hover { background: var(--accent-wash); text-decoration: none; }
.tabs a.active { background: var(--accent-wash); color: var(--series-1); font-weight: 600; }
.meta { color: var(--muted); font-size: 12px; white-space: nowrap; }

/* Pre-advice generator selectors + dual download row. */
.dl-row { display: flex; gap: 10px; flex-wrap: wrap; margin: 6px 0; }
.commit-bar { display: flex; align-items: center; gap: 14px; flex-wrap: wrap; margin: 10px 0; padding: 12px; border: 1px solid var(--critical); border-radius: 8px; background: rgba(208,59,59,0.06); }
.dlbtn.danger { background: var(--critical); color: #fff; border-color: var(--critical); }
.dlbtn.danger:disabled { opacity: 0.45; cursor: not-allowed; }
.commit-msg .pos { color: var(--good-text); }
.preadvice-controls { display: flex; gap: 20px; flex-wrap: wrap; margin-bottom: 10px; }
.preadvice-controls label { display: flex; flex-direction: column; gap: 4px; font-size: 12px; color: var(--ink-2); font-weight: 600; }
.preadvice-controls select { padding: 6px 10px; border: 1px solid var(--border); border-radius: 6px; background: var(--surface); color: var(--ink); font: inherit; min-width: 260px; }
#pa-cols code { background: var(--accent-wash); padding: 1px 5px; border-radius: 4px; font-size: 11px; }

/* Theme toggle — cycles System / Light / Dark (see js/theme.js). */
.theme-toggle {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  flex: none;
  padding: 5px 10px;
  border: 1px solid var(--border);
  border-radius: 6px;
  background: var(--surface);
  color: var(--ink-2);
  font: inherit;
  font-size: 12px;
  font-weight: 500;
  cursor: pointer;
  white-space: nowrap;
}
.theme-toggle:hover { background: var(--accent-wash); color: var(--series-1); border-color: var(--series-1); }
.theme-toggle .ti { font-size: 13px; line-height: 1; }
@media (max-width: 720px) { .theme-toggle .tl { display: none; } }

/* ---- layout ---- */
/* Full-width content — use the whole viewport (wide tables get more room, so
   fewer columns hide behind a horizontal scroll). Capped only on huge monitors
   so text lines don't become unreadably long. */
.view { padding: 20px 24px; max-width: 2600px; margin: 0 auto; }
.loading { color: var(--muted); padding: 40px; text-align: center; }
.error { color: var(--critical); padding: 24px; }
h1 { font-size: 20px; margin: 0 0 4px; }
h2 { font-size: 15px; margin: 24px 0 8px; }
.sub { color: var(--ink-2); margin: 0 0 16px; font-size: 13px; }
.crumb { font-size: 12px; color: var(--muted); margin-bottom: 10px; }

.cards { display: grid; grid-template-columns: repeat(auto-fill, minmax(180px, 1fr)); gap: 10px; margin: 12px 0; }
.card {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 8px;
  padding: 10px 12px;
}
.card .k { font-size: 11px; color: var(--muted); text-transform: uppercase; letter-spacing: 0.05em; }
.card .v { font-size: 16px; font-weight: 600; margin-top: 2px; word-break: break-word; }
.card .v.small { font-size: 13px; font-weight: 500; }

.panel {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 8px;
  padding: 14px;
  margin: 12px 0;
}

/* ---- tables ---- */
.tablewrap {
  overflow-x: auto;
  overscroll-behavior-x: contain;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 8px;
  /* Always show the horizontal bar when the table overflows, so it is never
     a hidden overlay you have to hunt for. */
  scrollbar-width: thin;
  scrollbar-color: var(--baseline) transparent;
}
.tablewrap::-webkit-scrollbar { height: 12px; }
.tablewrap::-webkit-scrollbar-track { background: var(--grid); border-radius: 6px; }
.tablewrap::-webkit-scrollbar-thumb {
  background: var(--baseline); border-radius: 6px;
  border: 3px solid var(--surface);
}
.tablewrap::-webkit-scrollbar-thumb:hover { background: var(--muted); }

/* A section's table capped to a few rows and scrolled in place, so one long block
   cannot push everything below it off the page. `th` is already sticky, so the
   header stays put while the body scrolls. */
.tablewrap.scrollrows { max-height: calc(var(--rows-visible, 10) * 29px + 34px); overflow-y: auto; }
.tablewrap.scrollrows::-webkit-scrollbar { width: 12px; }

/* An anchor styled as a button — links out to a full-page table. */
.linkout { margin: 12px 0 4px; }
a.dlbtn { text-decoration: none; display: inline-block; }

/* Size columns to their content and let .tablewrap scroll horizontally, rather
   than crushing every column to fit the viewport. On a narrow screen a forced
   width:100% squeezed columns until headers broke apart mid-word; min-width
   keeps the table filling the pane when it is narrower than the wrapper. */
table { border-collapse: collapse; width: max-content; min-width: 100%; font-size: 13px; }
th, td { padding: 6px 8px; text-align: left; white-space: nowrap; }
th {
  position: sticky; top: 0;
  background: var(--surface);
  color: var(--muted);
  font-size: 11px;
  text-transform: uppercase;
  letter-spacing: 0.03em;
  border-bottom: 1px solid var(--baseline);
  cursor: pointer;
  user-select: none;
  /* Let headers stack instead of dictating the column width — "No. of
     Components" was forcing 144px for 3-character data. break-word still wraps
     a single over-long word (e.g. UNVSCLOSEPRICE) so it is never clipped, but
     unlike `anywhere` it does not let the browser shrink the column to one
     character — which is what turned "Indices affected" into a vertical stack. */
  white-space: normal;
  overflow-wrap: break-word;
  line-height: 1.25;
  vertical-align: bottom;
  max-width: 96px;
}
th.sorted::after { content: " ▾"; color: var(--series-1); }
th.sorted.asc::after { content: " ▴"; }
td { border-bottom: 1px solid var(--grid); }
tr:last-child td { border-bottom: none; }
tbody tr:hover { background: var(--accent-wash); }
td.num, th.num { text-align: right; font-variant-numeric: tabular-nums; }
td.dim { color: var(--muted); }

/* Truncate free-text columns rather than letting them set the table width.
   Full value stays available via the cell's title tooltip. */
td.trunc {
  max-width: 210px;
  overflow: hidden;
  text-overflow: ellipsis;
}
td.trunc-sm { max-width: 110px; overflow: hidden; text-overflow: ellipsis; }
/* The indices list carries two names in one cell -- the published name of the
   index and, under it, the engine's identifier. Published names run to 60-odd
   characters, so this column WRAPS instead of truncating: the name is the point
   of the column, and an ellipsis would hide the part that distinguishes one
   variant from the next. The identifier under it still truncates. */
td.trunc-name { max-width: 340px; white-space: normal; line-height: 1.35; }
td.trunc-name .small { display: block; font-size: 11px; overflow: hidden;
                       text-overflow: ellipsis; white-space: nowrap; }

/* Keep identity visible while scrolling right. Sticky cells need their own
   background — the row stripe cannot show through them. */
.tablewrap.sticky-id th:nth-child(1), .tablewrap.sticky-id td:nth-child(1) {
  position: sticky; left: 0; z-index: 2;
  background: var(--surface);
  min-width: 108px;
}
.tablewrap.sticky-id th:nth-child(2), .tablewrap.sticky-id td:nth-child(2) {
  position: sticky; left: 108px; z-index: 2;
  background: var(--surface);
  box-shadow: 1px 0 0 var(--grid);
}
.tablewrap.sticky-id th:nth-child(1), .tablewrap.sticky-id th:nth-child(2) { z-index: 3; }
.tablewrap.sticky-id tbody tr:hover td:nth-child(1),
.tablewrap.sticky-id tbody tr:hover td:nth-child(2) { background: var(--row-hover); }

/* ---- controls ---- */
.controls { display: flex; gap: 10px; align-items: center; flex-wrap: wrap; margin: 12px 0; }
.controls input[type="search"], .controls select {
  background: var(--surface);
  color: var(--ink);
  border: 1px solid var(--baseline);
  border-radius: 6px;
  padding: 6px 10px;
  font-size: 13px;
  min-width: 220px;
}
.controls select { min-width: 140px; }
.controls label.chk { display: inline-flex; align-items: center; gap: 6px; font-size: 13px; color: var(--ink-2); cursor: pointer; user-select: none; white-space: nowrap; }
.controls label.chk input[type="checkbox"] { cursor: pointer; margin: 0; }
.count { color: var(--muted); font-size: 12px; }
.dlbtn {
  background: var(--surface); color: var(--ink-2);
  border: 1px solid var(--baseline); border-radius: 6px;
  padding: 6px 12px; font-size: 13px; font-family: inherit; cursor: pointer;
  white-space: nowrap;
}
.dlbtn:hover:not(:disabled) { background: var(--accent-wash); color: var(--series-1); border-color: var(--series-1); }
.dlbtn:disabled { opacity: 0.6; cursor: default; }
.spacer { flex: 1; }
.chk { display: inline-flex; align-items: center; gap: 5px; font-size: 13px; color: var(--ink-2); cursor: pointer; }
.chk input { margin: 0; }
.seg button:disabled { opacity: 0.45; cursor: not-allowed; }

/* Corporate actions going ex on the real calendar day.
   The whole row is tinted, but the ink is left alone — the previous version
   coloured the text blue on a blue wash and became unreadable. */
tbody tr.ex-today-row > td { background: var(--ex-today); }
tbody tr.ex-today-row > td:first-child { box-shadow: inset 3px 0 0 var(--series-1); }
tbody tr.ex-today-row:hover > td { background: var(--ex-today-hover); }
/* The default link blue sits too close to the tint (3.7:1); step it away. */
tbody tr.ex-today-row a { color: var(--ex-today-link); }

/* Brief highlight when a link lands you on a specific section */
.tablewrap.flash { animation: flash 1.6s ease-out; }
@keyframes flash {
  0%, 40% { box-shadow: 0 0 0 2px var(--series-1); }
  100% { box-shadow: 0 0 0 2px transparent; }
}
.seg { display: inline-flex; border: 1px solid var(--baseline); border-radius: 6px; overflow: hidden; }
.seg button {
  background: var(--surface); color: var(--ink-2); border: none;
  padding: 6px 12px; font-size: 13px; cursor: pointer;
}
.seg button.active { background: var(--accent-wash); color: var(--series-1); font-weight: 600; }

/* ---- as-of date bar ---- */
.datebar {
  display: flex; align-items: center; gap: 8px; flex-wrap: wrap;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 8px;
  padding: 8px 12px;
  margin: 12px 0;
}
.datebar label { font-size: 11px; color: var(--muted); text-transform: uppercase; letter-spacing: 0.05em; }
.datebar input[type="date"] {
  background: var(--page); color: var(--ink);
  border: 1px solid var(--baseline); border-radius: 6px;
  padding: 5px 8px; font-size: 13px; font-family: inherit;
}
.datebar .asof { font-size: 12px; }
.linkbtn {
  background: none; border: none; color: var(--series-1);
  cursor: pointer; font-size: 12px; padding: 2px 4px; font-family: inherit;
}
.linkbtn:hover { text-decoration: underline; }
.linkbtn[hidden] { display: none; }

/* ---- badges ---- */
.badge {
  display: inline-block; padding: 1px 7px; border-radius: 999px;
  font-size: 11px; font-weight: 600; border: 1px solid var(--border);
}
.badge.DIV { color: var(--series-1); background: var(--accent-wash); }
.badge.CAP { color: var(--series-2); background: rgba(235, 104, 52, 0.10); }
.badge.MNA { color: var(--muted); }
.badge.live { color: var(--good-text); background: rgba(12, 163, 12, 0.10); }
.badge.ended { color: var(--muted); }
.badge.auto { color: var(--series-3); background: rgba(27, 175, 122, 0.10); }
.badge.warn { color: var(--ink); background: rgba(250, 178, 25, 0.22); border-color: var(--warning); }
.badge.variant { color: var(--ink-2); background: transparent; font-weight: 600; letter-spacing: 0.02em; cursor: help; }
.badge.esg { color: var(--good-text); background: rgba(27, 175, 122, 0.12); border-color: rgba(27, 175, 122, 0.45); letter-spacing: 0.02em; }
.badge.hist { color: var(--series-2); background: rgba(235, 104, 52, 0.10); }

/* ---- chart ---- */
.chart { position: relative; }
.chart svg { display: block; width: 100%; height: auto; }
.chart .tooltip {
  position: absolute;
  pointer-events: none;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 6px;
  padding: 5px 9px;
  font-size: 12px;
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.12);
  display: none;
  z-index: 5;
  white-space: nowrap;
}
.chart .tooltip .tv { font-weight: 600; font-variant-numeric: tabular-nums; }
.chart .tooltip .td2 { color: var(--muted); }

/* Event markers on the line */
.chart .mk { cursor: pointer; }
.chart .mk .mk-dot { transition: r 0.1s ease-out; }
.chart .mk.on .mk-dot { r: 7; }
.chart .tooltip { max-width: 340px; white-space: normal; }
.chart .tooltip .mk-title { font-weight: 600; margin-bottom: 2px; }
.chart .tooltip .mk-line { color: var(--ink-2); font-size: 11.5px; }
.chart .tooltip .mk-sep { border-top: 1px solid var(--grid); margin: 5px 0; }
.chart-legend { display: flex; gap: 14px; flex-wrap: wrap; font-size: 12px; color: var(--ink-2); margin-top: 8px; }
.chart-legend .mk-key { display: inline-block; width: 9px; height: 9px; border-radius: 50%; margin-right: 5px; vertical-align: -1px; }

.pos { color: var(--good-text); }
.neg { color: var(--critical); }

/* Move thresholds: |Δ| > 5% red, 2–5% amber. Colour is a highlight, so each
   cell also carries an icon + the signed number — never colour alone. */
td.move-high {
  background: rgba(208, 59, 59, 0.16);
  color: var(--critical);
  font-weight: 600;
  box-shadow: inset 2px 0 0 var(--critical);
}
td.move-mid {
  background: rgba(250, 178, 25, 0.20);
  color: var(--ink);
  font-weight: 600;
  box-shadow: inset 2px 0 0 var(--warning);
}
.move-icon { margin-right: 4px; font-size: 11px; }
.legend { display: flex; gap: 14px; align-items: center; flex-wrap: wrap; font-size: 12px; color: var(--ink-2); margin: 8px 0 0; }
.legend .sw { display: inline-block; width: 10px; height: 10px; border-radius: 2px; margin-right: 5px; vertical-align: -1px; }
.legend .sw.high { background: rgba(208, 59, 59, 0.16); box-shadow: inset 2px 0 0 var(--critical); }
.legend .sw.mid { background: rgba(250, 178, 25, 0.20); box-shadow: inset 2px 0 0 var(--warning); }
.legend .sw.ok { background: rgba(12, 163, 12, 0.18); box-shadow: inset 2px 0 0 var(--good); }
.badge.crit { color: var(--critical); background: rgba(208, 59, 59, 0.12); border-color: var(--critical); }

/* Rebalance execution: logic cards, uploader */
a.logic-card { text-decoration: none; color: inherit; display: block; transition: border-color 0.12s; }
a.logic-card:hover { border-color: var(--series-1); text-decoration: none; }
a.logic-card .go { margin-top: 10px; color: var(--series-1); font-size: 13px; font-weight: 600; }
.sheetname { font-size: 14px; padding: 4px 8px; }
a.dlbtn.tmpl {
  display: inline-block; text-decoration: none;
  background: var(--accent-wash); color: var(--series-1);
  border-color: var(--series-1); font-weight: 600;
}
a.dlbtn.tmpl:hover {
  text-decoration: none;
  background: var(--series-1); color: #fff; border-color: var(--series-1);
}
ul.rules { margin: 4px 0 0; padding-left: 20px; color: var(--ink-2); font-size: 13px; }
ul.rules li { margin: 3px 0; }

.uploader {
  border: 2px dashed var(--baseline);
  border-radius: 10px;
  padding: 22px;
  margin: 16px 0;
  display: flex; flex-direction: column; gap: 14px; align-items: center;
  background: var(--surface);
  transition: border-color 0.12s, background 0.12s;
}
.uploader.over { border-color: var(--series-1); background: var(--accent-wash); }
.drop-inner { text-align: center; }
.drop-title { font-size: 14px; margin-bottom: 4px; }
.dlbtn.primary { background: var(--series-1); color: #fff; border-color: var(--series-1); font-size: 14px; padding: 8px 16px; margin: 12px 0; }
.dlbtn.primary:hover { filter: brightness(1.05); }
#run { font-size: 14px; padding: 8px 16px; font-weight: 600; }
#run:not(:disabled) { background: var(--series-1); color: #fff; border-color: var(--series-1); }
#run:not(:disabled):hover { filter: brightness(1.05); background: var(--series-1); color: #fff; }
.sheet-field { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; justify-content: center; }
.sheet-field label { font-size: 12px; color: var(--muted); text-transform: uppercase; letter-spacing: 0.05em; }
.sheet-field input {
  background: var(--page); color: var(--ink);
  border: 1px solid var(--baseline); border-radius: 6px;
  padding: 5px 9px; font-size: 13px; font-family: inherit; min-width: 260px;
}
.hint { color: var(--muted); font-size: 12px; margin-top: 6px; }
.nowrap { white-space: nowrap; }
tr.cleanup-remove > td { background: rgba(214, 69, 69, 0.14); }

/* --- Operations Check --- */
.ops-toolbar { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; margin: 12px 0; }
.ops-search {
  background: var(--page); color: var(--ink); border: 1px solid var(--baseline);
  border-radius: 6px; padding: 6px 10px; font-size: 13px; font-family: inherit; min-width: 220px;
}
.ops-toolbar select {
  background: var(--page); color: var(--ink); border: 1px solid var(--baseline);
  border-radius: 6px; padding: 6px 8px; font-size: 13px; font-family: inherit; max-width: 240px;
}
.ops-tabs { display: inline-flex; gap: 4px; }
.ops-tabs button {
  background: var(--surface); color: var(--ink-2); border: 1px solid var(--baseline);
  border-radius: 6px; padding: 5px 12px; font-size: 13px; font-family: inherit; cursor: pointer;
}
.ops-tabs button.active { background: var(--accent-wash); color: var(--series-1); border-color: var(--series-1); font-weight: 600; }
.ops-selcount { font-size: 12px; }
.ops-addcmt.dlbtn:not(:disabled) { background: var(--series-1); color: #fff; border-color: var(--series-1); font-weight: 600; }

.ops-table th.ops-cb, .ops-table td.ops-cb,
.ops-table th.ops-exp, .ops-table td.ops-exp { width: 1%; text-align: center; padding-left: 6px; padding-right: 6px; }
.ops-table input[type="checkbox"] { cursor: pointer; margin: 0; }
.ops-row { cursor: pointer; }
.ops-row.sel > td { background: var(--accent-wash) !important; }
.ops-table th.ops-sortable { cursor: pointer; user-select: none; }
.ops-table th.ops-sortable:hover { color: var(--series-1); }

/* Severity row tints (light + dark aware via alpha over surface) */
.ops-row.sev-red > td { background: rgba(214, 69, 69, 0.14); }
.ops-row.sev-yellow > td { background: rgba(214, 168, 42, 0.16); }
.ops-row.sev-blue > td { background: rgba(42, 120, 214, 0.14); }
.ops-row.sev-none > td { background: transparent; }
.ops-row:hover > td { filter: brightness(0.98); }
.chk-yes { color: #2e9e5b; font-weight: 700; }
.chk-flag { color: #d6a82a; }

/* Expandable trail row */
.trail-row > td { background: var(--surface); padding: 0; }
.trail-wrap { padding: 12px 16px; border-left: 3px solid var(--series-1); }
.trail-top { display: flex; align-items: center; gap: 10px; font-size: 13px; margin-bottom: 8px; }
.trail-add { font-size: 12px; }
ul.trail { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 8px; }
ul.trail li { border-left: 2px solid var(--baseline); padding: 2px 0 2px 10px; }
.trail-head { font-size: 12px; display: flex; gap: 8px; align-items: center; }
.trail-body { font-size: 13px; color: var(--ink-2); white-space: pre-wrap; margin-top: 2px; }

/* Comment modal — centered overlay so the page never jumps */
.modal-overlay {
  position: fixed; inset: 0; background: rgba(0,0,0,0.45);
  display: flex; align-items: center; justify-content: center; z-index: 1000; padding: 20px;
}
.modal {
  background: var(--page); color: var(--ink); border: 1px solid var(--baseline);
  border-radius: 10px; padding: 20px; width: 460px; max-width: 100%;
  box-shadow: 0 12px 40px rgba(0,0,0,0.3); display: flex; flex-direction: column; gap: 10px;
}
.modal { max-height: 88vh; overflow-y: auto; }
.modal h2 { margin: 0; font-size: 16px; }
.modal-list { font-size: 12px; }
.modal-impact { font-size: 12px; line-height: 1.6; }
.modal-note { font-size: 12px; line-height: 1.5; color: var(--ink-2); background: var(--baseline-soft, rgba(127,127,127,0.08)); border: 1px solid var(--baseline); border-radius: 6px; padding: 8px 10px; max-height: 140px; overflow-y: auto; }
.modal-note-lbl { text-transform: uppercase; letter-spacing: 0.05em; font-size: 10px; color: var(--muted); margin-right: 6px; }
.modal-trail { border: 1px solid var(--baseline); border-radius: 6px; padding: 10px 12px; max-height: 220px; overflow-y: auto; }
.modal-trail-h { font-size: 12px; text-transform: uppercase; letter-spacing: 0.05em; color: var(--muted); margin-bottom: 8px; }
.modal label { display: flex; flex-direction: column; gap: 4px; font-size: 12px; color: var(--muted); }
.modal label.chk { flex-direction: row; align-items: center; gap: 6px; color: var(--ink-2); }
.modal input[type="text"], .modal textarea {
  background: var(--surface); color: var(--ink); border: 1px solid var(--baseline);
  border-radius: 6px; padding: 7px 9px; font-size: 13px; font-family: inherit; resize: vertical;
}
.modal-msg { font-size: 12px; min-height: 14px; }
.modal-actions { display: flex; justify-content: flex-end; gap: 10px; align-items: center; }
.modal-actions .dlbtn.primary { margin: 0; }

/* Index Launch */
.filebtn {
  display: inline-block; background: var(--surface); color: var(--ink-2);
  border: 1px solid var(--baseline); border-radius: 6px;
  padding: 6px 12px; font-size: 13px; cursor: pointer; white-space: nowrap;
}
.filebtn:hover { background: var(--accent-wash); color: var(--series-1); border-color: var(--series-1); }
.dlbtn.commit { background: var(--series-1); color: #fff; border-color: var(--series-1); }
.dlbtn.commit:hover:not(:disabled) { filter: brightness(1.08); color: #fff; }
.dlbtn.commit:disabled { opacity: 0.45; cursor: not-allowed; }
.sqlblock {
  background: var(--surface); border: 1px solid var(--baseline); border-radius: 6px;
  padding: 12px; font-family: ui-monospace, Menlo, Consolas, monospace; font-size: 12px;
  white-space: pre-wrap; word-break: break-all; max-height: 340px; overflow: auto; color: var(--ink-2);
}

/* --- global search (top bar) ------------------------------------------------ */
.gsearch { position: relative; margin-left: auto; }
.gsearch input {
  width: 210px; padding: 5px 9px; font-size: 12px; color: var(--ink);
  background: var(--surface); border: 1px solid var(--baseline); border-radius: 6px;
}
.gsearch input:focus { outline: none; border-color: var(--series-1); width: 280px; }
.gsearch-results {
  position: absolute; top: calc(100% + 5px); right: 0; width: 380px; max-height: 420px;
  overflow: auto; z-index: 60; background: var(--surface);
  border: 1px solid var(--baseline); border-radius: 8px; box-shadow: 0 8px 26px rgba(0,0,0,.28);
}
.gs-row {
  display: grid; grid-template-columns: 52px 1fr; gap: 2px 8px; align-items: center;
  padding: 7px 10px; text-decoration: none; color: var(--ink); border-bottom: 1px solid var(--grid);
}
.gs-row:last-child { border-bottom: 0; }
.gs-row:hover, .gs-row.on { background: var(--grid); }
.gs-kind {
  grid-row: span 2; font-size: 10px; text-align: center; padding: 2px 0; border-radius: 4px;
  background: var(--grid); color: var(--muted); border: 1px solid var(--baseline);
}
.gs-kind.index { color: var(--series-1); border-color: var(--series-1); }
.gs-label { font-size: 13px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.gs-sub { font-size: 11px; color: var(--muted); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.gs-empty { padding: 10px; font-size: 12px; color: var(--muted); }

/* Indices that did not calculate today */
.missing-box { border-color: var(--critical); }

/* --- grouped nav tabs (dropdown) -------------------------------------------- */
.tabgroup { position: relative; }
.tabgroup-btn {
  display: flex; align-items: center; gap: 5px;
  padding: 6px 12px; border-radius: 6px;
  background: none; border: 0; cursor: pointer;
  color: var(--ink-2); font: inherit; font-weight: 500; font-size: inherit;
  white-space: nowrap;
}
.tabgroup-btn:hover { background: var(--accent-wash); }
.tabgroup.active > .tabgroup-btn { background: var(--accent-wash); color: var(--series-1); font-weight: 600; }
.tabgroup-btn .caret { font-size: 9px; opacity: .7; }
/* `display` must be scoped to the visible state — an author `display: flex`
   beats the UA stylesheet's `[hidden] { display: none }`, which left both
   menus permanently open. */
.tabgroup-menu[hidden] { display: none; }
/* `position: fixed`, with coordinates set from the button when it opens. The
   .tabs strip is `overflow-x: auto`, which makes overflow-y compute to `auto`
   too — an absolutely-positioned menu was clipped at the bottom of the tab bar
   and never visible. Fixed escapes that clipping context; the top bar is sticky
   so the menu stays aligned while the page scrolls. */
.tabgroup-menu {
  position: fixed; z-index: 40; min-width: 190px;
  display: flex; flex-direction: column; padding: 4px;
  background: var(--surface); border: 1px solid var(--baseline);
  border-radius: 8px; box-shadow: 0 8px 26px rgba(0,0,0,.28);
}
.tabs .tabgroup-menu a { padding: 7px 10px; border-radius: 5px; font-weight: 500; }
.tabs .tabgroup-menu a.on { color: var(--series-1); background: var(--accent-wash); font-weight: 600; }

/* Note: content-visibility on these rows was tried and measured — it gave no
   scroll gain (auto table layout still measures every row), so it is not used. */

/* Large ops tables: with column widths pinned (JS adds <colgroup> and
   table-layout:fixed) it is safe to let rows outside the viewport skip layout
   and paint — without pinned widths the columns would jump while scrolling. */
.tablewrap.rows-contained tbody tr {
  content-visibility: auto;
  contain-intrinsic-size: auto var(--row-h, 29px);
}

/* Instrument detail: "flagged in today's checks" strip under the fact cards. */
.flagbar { display: flex; flex-wrap: wrap; align-items: center; gap: 6px; margin: 10px 0 4px; font-size: 12px; }
.flagbar a.badge { text-decoration: none; }
.flagbar a.badge:hover { filter: brightness(1.15); }

/* Section heading with an action (e.g. the index-level download) on the right. */
.sectionbar { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; }
.sectionbar h2 { margin-right: auto; }

/* ETF Simulator: the assumption bars wrap on narrow screens, and each control
   carries its own label so the numbers are never ambiguous. */
.etfbar { flex-wrap: wrap; gap: 10px 16px; align-items: flex-end; }
.etfbar label { display: inline-flex; align-items: center; gap: 6px; font-size: 12px; color: var(--muted); }
.etfbar input, .etfbar select { margin-left: 2px; }
.etfbar input[type="number"] { width: 108px; }

/* Health verdict: what would stop this index being tracked by a fund. */
.healthbox { padding: 12px 14px; }
.healthbox h3 { margin: 0 0 8px; font-size: 13px; }
.healthbox ul { margin: 0; padding-left: 18px; }
.healthbox li { margin: 6px 0; font-size: 12px; line-height: 1.5; }
.healthbox li .badge { margin-right: 6px; }

/* --- live-rebalance progress tunnel (public/js/tunnel.js) -------------------
   Motion is transform/opacity only, so it composites without layout. Nothing
   animates unless a stage is actually running, and .tun-paused freezes the lot
   when the tab is hidden. */
.tun { border: 1px solid var(--border); border-radius: 8px; background: var(--surface); padding: 14px; }
.tun-paused *, .tun-paused *::before, .tun-paused *::after { animation-play-state: paused !important; }

.tun-pipe {
  position: relative; height: 122px; overflow: hidden; border-radius: 6px;
  background: radial-gradient(120% 100% at 100% 50%, var(--accent-wash), transparent 62%);
  border: 1px solid var(--grid);
}
/* Perspective rings: one repeating gradient, drifting. Cheap and static-safe. */
.tun-rings {
  position: absolute; inset: 0;
  background-image: repeating-linear-gradient(90deg,
    var(--grid) 0 1px, transparent 1px 64px);
  opacity: 0.5;
  -webkit-mask-image: linear-gradient(90deg, transparent, #000 22%, #000 78%, transparent);
          mask-image: linear-gradient(90deg, transparent, #000 22%, #000 78%, transparent);
}
.tun-live .tun-rings { animation: tun-drift 2.4s linear infinite; }
@keyframes tun-drift { to { background-position-x: 64px; } }

.tun-end {
  position: absolute; right: 0; top: 0; bottom: 0; width: 92px;
  display: flex; align-items: center; justify-content: center;
  font-size: 10px; letter-spacing: 0.08em; text-transform: uppercase; color: var(--muted);
  border-left: 1px dashed var(--baseline);
  background: linear-gradient(90deg, transparent, var(--accent-wash));
}
.tun-live .tun-end { color: var(--series-1); }

.tun-flight { position: absolute; inset: 0; }
.tun-chip {
  position: absolute; left: 0;
  font: 500 10px/1.6 ui-monospace, SFMono-Regular, Consolas, monospace;
  padding: 1px 6px; border-radius: 999px; white-space: nowrap;
  border: 1px solid var(--border); background: var(--page); color: var(--ink-2);
  will-change: transform, opacity;
}
.tun-chip.out  { animation: tun-out 2.1s cubic-bezier(.4,0,.7,1) forwards; }
.tun-chip.back { animation: tun-back 2.1s cubic-bezier(.3,0,.6,1) forwards;
                 border-color: var(--series-1); color: var(--series-1); }
@keyframes tun-out {
  0%   { transform: translate3d(6px,0,0) scale(1);   opacity: 0; }
  12%  { opacity: 1; }
  85%  { opacity: 1; }
  100% { transform: translate3d(calc(100% + 320px),0,0) scale(.62); opacity: 0; }
}
@keyframes tun-back {
  0%   { transform: translate3d(calc(100% + 320px),0,0) scale(.62); opacity: 0; }
  15%  { opacity: 1; }
  85%  { opacity: 1; }
  100% { transform: translate3d(6px,0,0) scale(1); opacity: 0; }
}

.tun-stat {
  display: flex; align-items: baseline; gap: 6px; margin: 10px 2px 12px;
  font-size: 12px; color: var(--ink);
}
.tun-stat > span:nth-child(odd) { font-weight: 600; font-variant-numeric: tabular-nums; }
.tun-dim { color: var(--muted); margin-right: 10px; }
.tun-spacer { flex: 1; }
.tun-clock { color: var(--muted); font-variant-numeric: tabular-nums; }

.tun-stages { list-style: none; margin: 0; padding: 0; display: grid; gap: 5px; }
.tun-stages li { display: flex; align-items: center; gap: 8px; font-size: 12px; color: var(--muted); }
.tun-dot {
  width: 8px; height: 8px; border-radius: 50%; flex: none;
  border: 1px solid var(--baseline); background: transparent;
}
.tun-stages li.on   { color: var(--ink); }
.tun-stages li.on .tun-dot { border-color: var(--series-1); background: var(--series-1); animation: tun-pulse 1.3s ease-in-out infinite; }
.tun-stages li.done { color: var(--ink-2); }
.tun-stages li.done .tun-dot { border-color: var(--good); background: var(--good); }
.tun-stages li.failed { color: var(--critical); }
.tun-stages li.failed .tun-dot { border-color: var(--critical); background: var(--critical); }
.tun-note { color: var(--muted); font-size: 11px; }
@keyframes tun-pulse { 50% { opacity: .35; transform: scale(.82); } }

.tun-lanes { margin-top: 12px; display: grid; gap: 6px; }
.tun-lane { display: grid; grid-template-columns: 78px 1fr 168px; align-items: center; gap: 10px; font-size: 11px; }
.tun-lane-n { color: var(--ink-2); }
.tun-lane-s { color: var(--muted); text-align: right; font-variant-numeric: tabular-nums; }
.tun-bar { height: 4px; border-radius: 999px; background: var(--grid); overflow: hidden; display: block; }
.tun-bar > i { display: block; height: 100%; width: 0; background: var(--series-1); border-radius: inherit; }
/* Waiting is indeterminate on purpose — DataScope reports no percentage, so we
   must not draw one. A sweep says "working", the label says how long. */
.tun-lane.on .tun-bar > i { width: 34%; animation: tun-sweep 1.5s ease-in-out infinite; }
.tun-lane.done .tun-bar > i { width: 100%; background: var(--good); animation: none; }
.tun-lane.done .tun-lane-s { color: var(--good-text); }
@keyframes tun-sweep { 0% { transform: translateX(-110%); } 100% { transform: translateX(330%); } }

.tun-failed .tun-pipe { border-color: var(--critical); }

@media (prefers-reduced-motion: reduce) {
  .tun-pipe { display: none; }
  .tun-rings, .tun-chip, .tun-dot, .tun-bar > i { animation: none !important; }
  .tun-lane.on .tun-bar > i { width: 100%; opacity: .45; }
}

/* --- cell inspector (installCellInspector in public/js/util.js) -------------
   A cell whose text is cut off can be clicked to open it in full. The class is
   added by JS on hover, because only measurement can tell whether a given cell
   actually overflows. */

.cellinspect { width: 560px; }
.cellinspect .ci-val {
  width: 100%; min-height: 120px; max-height: 46vh; resize: vertical;
  font: 12px/1.6 ui-monospace, SFMono-Regular, Consolas, monospace;
  color: var(--ink); background: var(--surface);
  border: 1px solid var(--baseline); border-radius: 6px; padding: 10px 12px;
  white-space: pre-wrap; word-break: break-word;
}
.cellinspect .ci-actions { display: flex; align-items: center; gap: 10px; }
.cellinspect .ci-len { font-size: 11px; }
.cellinspect .ci-msg { font-size: 12px; color: var(--muted); margin-left: auto; }
.cellinspect .ci-msg.ok { color: var(--good-text); font-weight: 600; }
.cellinspect .ci-actions .dlbtn:first-of-type { margin-left: 0; }
.cellinspect h2 { display: flex; align-items: baseline; gap: 8px; }
/* Names the dialog, so it can never be mistaken for the comment box, which is
   the other thing a click in these tables can open. */
.cellinspect .ci-kind {
  font-size: 10px; font-weight: 600; letter-spacing: 0.06em; text-transform: uppercase;
  color: var(--series-1); background: var(--accent-wash);
  border: 1px solid var(--series-1); border-radius: 999px; padding: 1px 7px;
}
.cellinspect .ci-sub { margin: 0; font-size: 11px; color: var(--muted); }

/* --- cell chip: open this cell's value, in any table -----------------------
   Its own hit target at the cell's right edge, so it never competes with a
   link, a clickable row, or the operations-check comment box. Appears only
   while the cell is hovered, so a table of 19,000 cells shows none of them. */
/* Only cells that host a chip are positioned — a blanket rule would fight the
   sticky first column. */
td.ci-host { position: relative; }
.ci-chip {
  position: absolute; top: 50%; right: 2px; transform: translateY(-50%);
  display: none; align-items: center; justify-content: center;
  width: 18px; height: 18px; padding: 0; line-height: 1;
  font-size: 11px; cursor: zoom-in;
  color: var(--series-1); background: var(--surface);
  border: 1px solid var(--series-1); border-radius: 4px;
  box-shadow: 0 0 0 3px var(--surface);
}
td:hover > .ci-chip { display: inline-flex; }
.ci-chip:hover { background: var(--accent-wash); }

/* A cell whose text is cut off says so; one whose whole body opens the value
   (because its row has no click action of its own) also takes the zoom cursor. */
td.ci-cut { box-shadow: inset 0 -1px 0 0 var(--baseline); }
td.ci-body { cursor: zoom-in; }
td.ci-body.ci-cut:hover { box-shadow: inset 0 -2px 0 0 var(--series-1); }

/* --- flash on change -------------------------------------------------------
   One pass over background-color (paint only, no layout) on exactly the cells a
   save rewrote, so a bulk comment is visible where it landed. */
@keyframes flash-change {
  0%   { background-color: var(--accent-wash); box-shadow: inset 0 0 0 1px var(--series-1); }
  70%  { background-color: var(--accent-wash); box-shadow: inset 0 0 0 1px transparent; }
  100% { background-color: transparent; box-shadow: none; }
}
.flash-change { animation: flash-change 1.5s ease-out 1; }

@media (prefers-reduced-motion: reduce) {
  .flash-change { animation: none; }
}

/* --- Extra Ordinary CA: instrument type-ahead -------------------------------
   Reuses the global-search rows (.gs-*); only the popup's anchoring differs,
   since here it hangs off a field in a card rather than the top bar. */
.ca-field { position: relative; }
.ca-field .ca-pop {
  position: absolute; top: calc(100% + 4px); left: 0; right: auto;
  width: 420px; max-width: 78vw; max-height: 320px;
}
/* The rows are buttons here (a picker, not links), so reset the button chrome. */
.ca-field .gs-row {
  width: 100%; background: none; font: inherit; text-align: left; cursor: pointer;
}
.ca-field .gs-kind { font-variant-numeric: tabular-nums; white-space: nowrap; }
.ca-chosen { margin-top: 6px; line-height: 1.7; }

/* --- Extra Ordinary CA: pro-rata removal card ------------------------------ */
.pr-card { margin-top: 18px; }
.pr-warn {
  margin: 8px 0; padding: 9px 12px; line-height: 1.55; color: var(--ink);
  border-left: 3px solid var(--warning); border-radius: 4px; background: rgba(250, 178, 25, 0.12);
}
.pr-err { color: var(--critical); }
/* A leaver with no price is not a caution about this run — it reports value the
   index has ALREADY lost, so it reads critical and carries its own table. */
.pr-stale { border-left-color: var(--critical); background: rgba(208, 59, 59, 0.10); }
.pr-stale p { margin: 6px 0; }
.pr-stale .tablewrap { margin: 10px 0; }
.pr-stale code { padding: 0 3px; border-radius: 3px; background: rgba(128, 128, 128, 0.18); font-size: 11px; }
.pr-detail { margin: 10px 0; }
.pr-detail h4 { margin: 14px 0 6px; font-size: 13px; }
.pr-sqlbox { margin: 12px 0 8px; }
/* The generated SQL runs to thousands of lines. It had a max-height but no
   overflow, so the box stopped at 280px while the text kept going — the name
   field, the write buttons and the result message were laid out against the
   280px box and painted on top of ~1,700px of spilled SQL. It scrolls now. */
pre.ca-sql {
  max-height: 300px;
  overflow: auto;
  margin: 8px 0 0;
  padding: 10px 12px;
  border: 1px solid var(--border);
  border-radius: 6px;
  background: var(--surface);
  font-size: 11px;
  line-height: 1.5;
  white-space: pre;            /* keep each INSERT tuple on its own line */
}
pre.pr-sql { max-height: 280px; }


/* --- Instrument Clean Up: selecting instruments to unsubscribe ------------- */
/* The checkbox lives in an <a> only so the table's row-click navigation lets it
   through; it is not a link and must not look like one. */
td.pickcell { width: 34px; text-align: center; }
a.pick { display: inline-flex; align-items: center; justify-content: center;
         cursor: pointer; text-decoration: none; padding: 2px 4px; }
a.pick input { cursor: pointer; margin: 0; accent-color: var(--critical); }

.pickbar { display: flex; align-items: center; gap: 12px; flex-wrap: wrap;
           margin: 12px 0 0; padding: 10px 12px; border-radius: 8px;
           border: 1px solid var(--critical); background: rgba(208, 59, 59, 0.08); }
.pickbar .spacer { flex: 1; }

.ds-panel { margin-top: 14px; padding: 14px 16px; border: 1px solid var(--border);
            border-radius: 8px; background: var(--surface); }
.ds-panel h2 { margin-top: 0; }
.ds-panel .tablewrap { max-height: 320px; }
.ds-confirm { display: flex; align-items: center; gap: 14px; flex-wrap: wrap; margin-top: 12px; }
.ds-confirm input.confirm { width: 120px; text-transform: uppercase; letter-spacing: .08em; }

.dlbtn.danger { border-color: var(--critical); color: var(--critical); }
.dlbtn.danger:disabled { opacity: .45; cursor: not-allowed; }
.dlbtn.danger:not(:disabled):hover { background: var(--critical); color: #fff; }
.pr-ok { color: var(--good-text); }


/* An error that offers a way out, rather than just stating the problem. */
.errbtns { display: flex; gap: 10px; margin-top: 12px; flex-wrap: wrap; }
.errbtns .dlbtn { text-decoration: none; }


/* Ops-check instrument-type filter: one checkbox per type present, so any
   combination ("only cryptos", "equities + options") is a click. */
.ops-types { display: flex; align-items: center; gap: 4px; flex-wrap: wrap; }
.ops-typelabel { margin-right: 2px; font-size: 12px; }
.ops-type { display: inline-flex; align-items: center; gap: 5px; cursor: pointer;
            padding: 3px 8px; border: 1px solid var(--border); border-radius: 999px;
            font-size: 12px; white-space: nowrap; line-height: 1.5; }
.ops-type:hover { background: var(--accent-wash); }
.ops-type input { margin: 0; cursor: pointer; }
.ops-type:has(input:checked) { border-color: var(--series-1); background: var(--accent-wash); }


/* Queue a finished live rebalance into underlysecrebal. */
.queue-panel { margin-top: 22px; padding: 14px 16px; border: 1px solid var(--critical);
               border-radius: 8px; background: rgba(208, 59, 59, 0.05); }
.queue-panel h2 { margin-top: 0; }
.queue-panel .q-targets { display: flex; align-items: center; gap: 6px; flex-wrap: wrap; margin: 10px 0; }
.queue-panel .tablewrap { max-height: 260px; }
.q-sql { margin: 10px 0; }
.q-sql summary { cursor: pointer; font-size: 12px; color: var(--ink-2); }
.q-sql pre, .queue-panel pre.pr-sql { max-height: 220px; overflow: auto; font-size: 11px;
  padding: 10px; border: 1px solid var(--border); border-radius: 6px; background: var(--surface); }


/* German Treasury screen: the parameter bar above the run. */
.gt-bar { flex-wrap: wrap; gap: 14px; }
.gt-bar label { display: inline-flex; align-items: center; gap: 6px; font-size: 12px; color: var(--ink-2); }
.gt-out { margin-top: 6px; }
