/* AlgoSync dashboard — shared stylesheet for all pages (strategies / backtest / logs).
   Extracted verbatim from the former single-page index.html so the three pages
   cannot drift apart visually. */
:root {
  color-scheme: light;
  --surface-1: #fcfcfb;
  --plane: #f9f9f7;
  --ink: #0b0b0b;
  --ink-2: #52514e;
  --muted: #898781;
  --grid: #e1e0d9;
  --axis: #c3c2b7;
  --border: rgba(11,11,11,0.10);
  --series-1: #2a78d6;
  --good: #0ca30c;
  --good-text: #006300;
  --warning: #fab219;
  /* Text-contrast variant, same split as --good / --good-text: the amber that
     reads on a dark dot is unreadable as body text on a light surface. */
  --warning-text: #8a5a00;
  --serious: #ec835a;
  --critical: #d03b3b;
}
@media (prefers-color-scheme: dark) {
  :root:where(:not([data-theme="light"])) {
    color-scheme: dark;
    --surface-1: #1a1a19; --plane: #0d0d0d; --ink: #fff; --ink-2: #c3c2b7;
    --muted: #898781; --grid: #2c2c2a; --axis: #383835;
    --border: rgba(255,255,255,0.10); --series-1: #3987e5; --good-text: #0ca30c;
    --warning-text: #fab219;
  }
}
:root[data-theme="dark"] {
  color-scheme: dark;
  --surface-1: #1a1a19; --plane: #0d0d0d; --ink: #fff; --ink-2: #c3c2b7;
  --muted: #898781; --grid: #2c2c2a; --axis: #383835;
  --border: rgba(255,255,255,0.10); --series-1: #3987e5; --good-text: #0ca30c;
  --warning-text: #fab219;
}
* { box-sizing: border-box; }
body { margin: 0; background: var(--plane); color: var(--ink);
       font: 14px/1.5 system-ui, -apple-system, "Segoe UI", sans-serif; }
header { display: flex; align-items: center; gap: 14px; flex-wrap: wrap;
         padding: 12px 20px; background: var(--surface-1);
         border-bottom: 1px solid var(--border); position: sticky; top: 0; z-index: 20; }
h1 { font-size: 15px; margin: 0; font-weight: 650; letter-spacing: -0.01em; }
h2 { font-size: 13px; margin: 0 0 12px; font-weight: 650; color: var(--ink-2);
     text-transform: uppercase; letter-spacing: 0.04em; }
main { padding: 18px 20px 40px; max-width: 1600px; }
.cards { display: grid; gap: 16px; grid-template-columns: repeat(auto-fit, minmax(340px, 1fr)); }
.card { background: var(--surface-1); border: 1px solid var(--border);
        border-radius: 10px; padding: 16px; min-width: 0; }
.card.wide { grid-column: 1 / -1; }
.row { display: flex; gap: 10px; flex-wrap: wrap; align-items: center; }
.stats { display: grid; gap: 14px; grid-template-columns: repeat(auto-fit, minmax(118px, 1fr)); }
.stat-label { font-size: 11px; color: var(--muted); text-transform: uppercase;
              letter-spacing: 0.04em; margin-bottom: 3px; }
.stat-value { font-size: 21px; font-weight: 620; letter-spacing: -0.02em; }
.stat-value.sm { font-size: 15px; font-weight: 560; }
.pill { display: inline-flex; align-items: center; gap: 6px; padding: 3px 10px;
        border-radius: 999px; font-size: 12px; font-weight: 600; border: 1px solid var(--border); }
.dot { width: 8px; height: 8px; border-radius: 50%; background: var(--muted); flex: none; }
.dot.good { background: var(--good); } .dot.warn { background: var(--warning); }
.dot.crit { background: var(--critical); } .dot.idle { background: var(--muted); }
button { font: inherit; font-weight: 560; padding: 7px 14px; border-radius: 7px;
         border: 1px solid var(--border); background: var(--surface-1); color: var(--ink);
         cursor: pointer; }
button:hover:not(:disabled) { background: var(--plane); }
button:disabled { opacity: 0.4; cursor: not-allowed; }
button.primary { background: var(--series-1); border-color: var(--series-1); color: #fff; }
button.danger { background: var(--critical); border-color: var(--critical); color: #fff; }
input, select { font: inherit; padding: 6px 9px; border-radius: 6px;
                border: 1px solid var(--border); background: var(--plane);
                color: var(--ink); min-width: 0; }
label.field { display: flex; flex-direction: column; gap: 4px; font-size: 12px; color: var(--ink-2); }
label.field input, label.field select { width: 100%; }
label.field[title] { cursor: help; }
.fld-label { display: inline-flex; align-items: center; gap: 4px; }
.hint { cursor: help; color: var(--ink-3, var(--muted)); font-size: 11px; line-height: 1; }
.hint:hover { color: var(--accent, var(--ink-2)); }
button[disabled], button:disabled { opacity: 0.45; cursor: not-allowed; }
/* Chart hover tooltip (equity crosshair) */
.chart-tip { position: absolute; pointer-events: none; z-index: 6; min-width: 150px;
  background: var(--surface-1); border: 1px solid var(--border); border-radius: 8px;
  padding: 8px 10px; font-size: 12px; box-shadow: 0 6px 22px rgba(0,0,0,.18);
  font-variant-numeric: tabular-nums; }
.chart-tip .tip-d { color: var(--muted); font-weight: 600; margin-bottom: 5px; }
.chart-tip .tip-row { display: flex; align-items: center; gap: 7px; padding: 1px 0; }
.chart-tip .tip-l { color: var(--ink-2); flex: 1; }
.chart-tip .tip-v { color: var(--ink); }
.chart-tip .dot { width: 9px; height: 9px; }
#bt-eq-svg, #bt-monthly-chart svg { cursor: crosshair; }
/* Segmented Log/Linear toggle */
.seg { display: inline-flex; border: 1px solid var(--border); border-radius: 8px; overflow: hidden; }
.seg button { font-size: 12px; letter-spacing: .02em; padding: 5px 13px; background: transparent;
  color: var(--ink-2); border: 0; cursor: pointer; }
.seg button.on { background: var(--plane, var(--surface-1)); color: var(--ink); font-weight: 600; }
/* Clickable legend chips (toggle series) */
.pill.clickable { cursor: pointer; background: transparent; border: 0; font: inherit; padding: 3px 8px; border-radius: 7px; }
.pill.clickable:hover { background: var(--plane, var(--surface-1)); }
.pill.off { opacity: .4; }
.pill.off .dot { opacity: .4; }
/* Per-strategy risk table (blend) */
#b-multi-risk-table th, #b-multi-risk-table td { padding: 4px 8px; white-space: nowrap; }
#b-multi-risk-table td.name { font-weight: 600; }
#b-multi-risk-table input { width: 76px; }
.grid-fields { display: grid; gap: 10px; grid-template-columns: repeat(auto-fit, minmax(120px, 1fr)); }
table { width: 100%; border-collapse: collapse; font-size: 13px; font-variant-numeric: tabular-nums; }
th { text-align: left; font-size: 11px; text-transform: uppercase; letter-spacing: 0.04em;
     color: var(--muted); font-weight: 600; padding: 6px 8px;
     border-bottom: 1px solid var(--grid); white-space: nowrap; }
td { padding: 6px 8px; border-bottom: 1px solid var(--grid); white-space: nowrap; }
tr:last-child td { border-bottom: none; }
.scroll { overflow-x: auto; } .tall { max-height: 320px; overflow-y: auto; }
.pos { color: var(--good-text); } .neg { color: var(--critical); } .muted { color: var(--muted); }
/* Text-level warn. `.dot.warn` and `.banner.warn` already existed, but neither
   colours TEXT — a stat tile carrying class="warn" rendered in the default ink,
   silently losing the warning. Used by the day-drawdown tile as it approaches
   the kill-switch limit. */
.warn { color: var(--warning-text); }
pre { background: var(--plane); border: 1px solid var(--border); border-radius: 7px;
      padding: 10px; overflow: auto; max-height: 260px; font-size: 12px; margin: 0;
      white-space: pre-wrap; word-break: break-word; }
.note { font-size: 12px; color: var(--muted); margin-top: 8px; }
.banner { padding: 10px 14px; border-radius: 8px; font-size: 13px; font-weight: 560;
          margin-bottom: 14px; border: 1px solid var(--border); display: none; }
.banner.show { display: block; }
.banner.crit { background: color-mix(in srgb, var(--critical) 12%, transparent); border-color: var(--critical); }
.banner.warn { background: color-mix(in srgb, var(--warning) 16%, transparent); border-color: var(--warning); }

/* ---- keyboard focus ----
   There were NO focus rules at all, so tabbing through the page moved an
   invisible cursor — including across "Kill & flatten" and "Force-terminate",
   where the cost of pressing Enter on the wrong control is a closed book or an
   unmanaged position. :focus-visible (not :focus) so a mouse click does not
   leave a ring behind.

   `:where()` keeps specificity at 0, so a component that wants its own focus
   treatment can override this without !important. */
:where(button, a, input, select, textarea, [tabindex]):focus-visible {
  outline: 2px solid var(--series-1);
  outline-offset: 2px;
  border-radius: 6px;
}
/* Destructive controls get the danger colour, so the ring itself says which
   kind of button you have landed on before you read the label. */
.ctl-group.danger-zone :where(button):focus-visible,
:where(button.danger):focus-visible { outline-color: var(--critical); }
/* The tab strip is a real tablist — the ring goes on the whole card. */
.tab:focus-visible { outline: 2px solid var(--series-1); outline-offset: 2px; }

/* ---- page nav (strategies / backtest / logs) ----
   `flex-wrap` is load-bearing, not cosmetic: six links plus the title and the
   theme toggle measured 497px against a 375px viewport, which made the DOCUMENT
   scroll horizontally on every page on a phone. Without wrapping, a nav row
   cannot shrink below its content. */
.nav { display: flex; gap: 4px; flex-wrap: wrap; }
.nav a { display: inline-block; padding: 6px 13px; border-radius: 7px; font-size: 13px;
         font-weight: 560; color: var(--ink-2); text-decoration: none;
         border: 1px solid transparent; }
.nav a:hover { background: var(--plane); }
.nav a[aria-current="page"] { background: var(--plane); color: var(--ink);
                              border-color: var(--border); }

/* ---- sleeve tabs ---- */
.tabs { display: flex; gap: 8px; flex-wrap: wrap; margin-bottom: 16px; }
.tab { display: flex; flex-direction: column; gap: 5px; align-items: flex-start;
       padding: 10px 14px; border-radius: 9px; border: 1px solid var(--border);
       background: var(--surface-1); cursor: pointer; min-width: 176px; text-align: left; }
.tab[aria-selected="true"] { border-color: var(--series-1); box-shadow: inset 0 0 0 1px var(--series-1); }
.tab .t-name { font-weight: 650; font-size: 13px; display: flex; align-items: center; gap: 7px; }
.tab .t-meta { font-size: 11px; color: var(--muted); font-variant-numeric: tabular-nums; }
.agg { display: flex; gap: 18px; flex-wrap: wrap; padding: 11px 14px; margin-bottom: 16px;
       background: var(--surface-1); border: 1px solid var(--border); border-radius: 9px;
       font-size: 13px; align-items: center; }
.agg b { font-variant-numeric: tabular-nums; }

/* ---- controls ---- */
.ctl-group { border: 1px solid var(--border); border-radius: 9px; padding: 12px 14px;
             margin-bottom: 12px; }
.ctl-head { font-size: 11px; text-transform: uppercase; letter-spacing: 0.04em;
            color: var(--muted); font-weight: 650; margin-bottom: 4px; }
.ctl-group.danger-zone { border-color: color-mix(in srgb, var(--critical) 45%, var(--border)); }
.ctl-desc { font-size: 12px; color: var(--muted); margin: 0 0 10px; }
.ctl-btn { display: flex; flex-direction: column; align-items: flex-start; gap: 2px;
           padding: 9px 14px; text-align: left; min-width: 150px; }
.ctl-btn .b-title { font-weight: 620; font-size: 13px; }
.ctl-btn .b-sub { font-size: 11px; opacity: 0.75; font-weight: 450; }
.state-strip { display: flex; align-items: center; gap: 10px; padding: 10px 12px;
               border-radius: 8px; background: var(--plane); margin-bottom: 12px; font-size: 13px; }
#toast { position: fixed; bottom: 20px; right: 20px; z-index: 50; display: grid; gap: 8px; }
.toast { background: var(--surface-1); border: 1px solid var(--border); border-left-width: 3px;
         border-radius: 8px; padding: 10px 14px; font-size: 13px; max-width: 380px;
         box-shadow: 0 4px 16px rgba(0,0,0,0.12); }
.toast.err { border-left-color: var(--critical); } .toast.ok { border-left-color: var(--good); }

/* ---- logs page ---- */
#logbox.full { max-height: none; height: calc(100vh - 300px); min-height: 340px; }

/* ---- full-height page (whitelist) ----
   `.tall` caps at 320px, which is right for a table sharing a card with six
   others but wastes most of the screen on a page whose whole job is one 45-row
   table.

   Done with a flex column rather than #logbox.full's `calc(100vh - Npx)`: that
   constant has to encode the height of the header, filter row and footnotes, so
   it silently goes wrong whenever any of them wraps or changes. Here the table
   simply takes whatever is left over, at any viewport size. `min-height: 0` on
   each level is load-bearing — a flex item defaults to min-height:auto, which
   refuses to shrink below its content and would push the table off-screen
   instead of scrolling it internally. */
/* A DEFINITE height (not min-height) is what makes the flex children divide a
   fixed space — with min-height the card just grew to its content and the table
   never became a scroller.
   `overflow-y: auto`, NOT `hidden`: on a short window the fixed chrome plus the
   table's min-height can exceed 100vh, and `hidden` then CLIPS the bottom of the
   page with no way to reach it. `auto` keeps the normal case (table scrolls
   internally, page does not move) while still letting a genuinely cramped window
   scroll rather than hide content. */
body.fill-page { height: 100vh; overflow-y: auto; display: flex; flex-direction: column; }
body.fill-page main { flex: 1; min-height: 0; display: flex; flex-direction: column; }
/* `.cards` is normally a GRID with auto-sized rows, and `height:100%` on a grid
   item resolves against that auto row — i.e. against its own content, which
   constrains nothing. Switching to a flex column on this page gives the card a
   parent with a definite height to divide. */
body.fill-page .cards { display: flex; flex-direction: column; flex: 1; min-height: 0; }
body.fill-page .card.fill { display: flex; flex-direction: column; flex: 1; min-height: 0; }
body.fill-page .card.fill > .fill-body { flex: 1; min-height: 0; display: flex; }
/* Low floor: on a short window this is what decides whether the table shrinks
   politely or forces the whole page to scroll. ~4 rows is still usable. */
.scroll.full { flex: 1; max-height: none; min-height: 130px; overflow-y: auto; }
/* Sticky column headers: at full height you scroll far enough that "which column
   is this?" becomes a real question, which never arose in a 320px box. */
.scroll.full thead th { position: sticky; top: 0; z-index: 1;
                        background: var(--surface-1); }

/* Indeterminate progress stripe — used while a job has started but has not yet
   reported a countable unit. Deliberately distinct from the determinate bar so
   "we don't know yet" never reads as "0% done". */
@keyframes bt-indet {
  0%   { transform: translateX(-100%); }
  100% { transform: translateX(333%); }
}

/* ---- small screens ----
   This dashboard gets opened on a phone to answer one question — is the book
   OK — so the failure mode that matters is having to pan sideways to read a
   number. Tables keep `white-space: nowrap` and scroll INSIDE their own
   `.scroll` container; what must never happen is the page itself scrolling. */
@media (max-width: 640px) {
  main { padding: 12px 12px 32px; }
  header { padding: 10px 12px; gap: 10px; }
  /* Fixed min-width forced the strip wider than the viewport. `flex: 1 1 148px`
     lets tabs share the row and reflow instead. */
  .tab { min-width: 0; flex: 1 1 148px; }
  .tabs { gap: 6px; }
  .agg { gap: 10px 14px; font-size: 12px; padding: 10px 12px; }
  .stats { grid-template-columns: repeat(auto-fit, minmax(96px, 1fr)); gap: 10px; }
  .stat-value { font-size: 18px; }
  /* Full-width control buttons: a 150px min-width put two per row with a ragged
     edge, and "Kill & flatten" is not a control to mis-tap. */
  .ctl-btn { min-width: 0; width: 100%; }
  .grid-fields { grid-template-columns: 1fr 1fr; }
  #toast { left: 12px; right: 12px; bottom: 12px; }
  .toast { max-width: none; }
}

/* Respect the OS "reduce motion" setting. The indeterminate stripe is the only
   continuous animation here, and it runs for the whole duration of a backtest —
   long enough to matter to anyone who asked for less movement. It stays visually
   distinct from the determinate bar by pulsing opacity instead of travelling. */
@media (prefers-reduced-motion: reduce) {
  @keyframes bt-indet {
    0%, 100% { transform: translateX(0); opacity: 0.35; }
    50%      { transform: translateX(0); opacity: 0.9; }
  }
  *, *::before, *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
  }
  /* Exempt: this one must keep animating or "working…" looks like "stalled". */
  .bt-indet, [class*="indet"] {
    animation-duration: 1.6s !important;
    animation-iteration-count: infinite !important;
  }
}
