: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);
  --accent: #2a78d6;       /* sequential blue 450 family */
  --accent-strong: #1c5cab;
  --good-text: #006300;
  --warn: #fab219;
  --tint-max: rgba(42, 120, 214, 0.28); /* deepest yield tint on light */
  --series-2: #eb6834;   /* second chart series (200-day average) */
  --candle-up: #0ca30c;
  --candle-down: #d03b3b;
}
@media (prefers-color-scheme: dark) {
  :root: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);
    --accent: #3987e5;
    --accent-strong: #6da7ec;
    --good-text: #0ca30c;
    --tint-max: rgba(57, 135, 229, 0.32);
    --series-2: #d95926;
    --candle-up: #0ca30c;
    --candle-down: #e66767;
  }
}
: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);
  --accent: #3987e5;
  --accent-strong: #6da7ec;
  --good-text: #0ca30c;
  --tint-max: rgba(57, 135, 229, 0.32);
  --series-2: #d95926;
  --candle-up: #0ca30c;
  --candle-down: #e66767;
}

* { box-sizing: border-box; }
[hidden] { display: none !important; }
/* Never let the browser re-anchor scroll when content changes size — a
   table re-render must not yank the page out from under the cursor. */
html { overflow-anchor: none; }
body {
  margin: 0;
  background: var(--page);
  color: var(--ink);
  font: 14px/1.45 system-ui, -apple-system, "Segoe UI", sans-serif;
}
.wrap { max-width: 1280px; margin: 0 auto; padding: 16px 20px 40px; }

header { display: flex; align-items: baseline; gap: 12px; flex-wrap: wrap; margin-bottom: 4px; }
header h1 { font-size: 20px; margin: 0; }
.badge {
  font-size: 12px; color: var(--ink-2); border: 1px solid var(--border);
  border-radius: 999px; padding: 2px 10px; background: var(--surface);
}
.subtitle { color: var(--muted); font-size: 13px; margin: 0 0 14px; }

.panel {
  background: var(--surface); border: 1px solid var(--border);
  border-radius: 10px; padding: 14px 16px; margin-bottom: 14px;
}

/* Spreadsheet-style tab strip: sticks to the top of the viewport so every
   tab is reachable from any scroll position, and the active tab merges
   into the panel below it. */
.tabs {
  position: sticky; top: 0; z-index: 60;
  display: flex; gap: 4px; align-items: flex-end;
  padding: 8px 0 0;
  background: var(--page);
  border-bottom: 1px solid var(--border);
  margin-bottom: -1px;
  overflow-x: auto;
}
.tabs .tab {
  flex: 0 0 auto;
  background: var(--page); color: var(--ink-2);
  border: 1px solid var(--border); border-bottom-color: transparent;
  border-radius: 8px 8px 0 0;
  padding: 8px 16px; font: inherit; font-size: 13px; cursor: pointer;
  white-space: nowrap;
  position: relative; top: 1px;
}
.tabs .tab:hover { color: var(--ink); background: var(--surface); }
.tabs .tab.on {
  background: var(--surface); color: var(--ink); font-weight: 650;
  border-color: var(--border); border-bottom-color: var(--surface);
  box-shadow: inset 0 2px 0 var(--accent);
}
.tabpanel { border-top-left-radius: 0; }

.controls { display: flex; flex-wrap: wrap; gap: 14px 22px; align-items: flex-end; }
.field { display: flex; flex-direction: column; gap: 4px; position: relative; }

/* The calculators' top row: labels are the section headings for the control
   under them, so they read larger, bolder and centred. align-items: start
   (rather than the default flex-end) is what makes them share one baseline
   even though a button, an input, a readout and a segmented control are all
   different heights. */
.controls.headrow { align-items: flex-start; gap: 12px 26px; }
/* A primary and a ghost differ in padding and border, so side by side they
   sat at different heights. Pin head-row buttons to one box instead. */
.headrow > .field > button {
  height: 34px; display: inline-flex; align-items: center; justify-content: center;
  padding: 0 16px; font-size: 13px;
}
.headrow > .field { align-items: center; }
.headrow > .field > label {
  font-size: 13px; font-weight: 650; color: var(--ink);
  text-align: center; min-height: 18px; white-space: nowrap;
}

/* The slider row gets room to breathe: taller tracks and a bigger gap, so
   thumbs are easy to grab and adjacent rows never feel crowded. */
.controls.sliderrow { align-items: flex-start; gap: 18px 26px; margin-top: 14px; }
/* Four fixed 300px fields overflow the 1280px content width and wrap. Equal
   flex shares instead: the width now depends only on the container, never on
   the label text, so switching emphasis modes still cannot re-wrap the row
   or move a control out from under the cursor — the same guarantee the fixed
   width was there to give, held more tightly. */
/* max-width matters when a row holds an odd count (the finder has five):
   without it the one that wraps would stretch to the full content width. */
.sliderrow .sliderfield { flex: 1 1 240px; width: auto; min-width: 240px; max-width: 320px; }
.sliderrow .sliderfield > label { min-height: 18px; margin-bottom: 2px; }
.sliderrow .sliderfield input[type="range"] { height: 22px; }
.sliderrow .dualslider { height: 34px; }
.sliderrow .dualslider::before { top: 15px; }
.sliderrow .dualslider .fillbar { top: 15px; }
.sliderrow .dualslider input[type="range"] { height: 34px; }

/* Hover explainers on parameter boxes */
.field[data-tip]::after {
  content: attr(data-tip);
  position: absolute;
  top: calc(100% + 8px);
  left: 0;
  z-index: 30;
  width: max-content;
  max-width: 300px;
  background: var(--surface);
  color: var(--ink);
  border: 1px solid var(--baseline);
  border-radius: 8px;
  box-shadow: 0 4px 14px rgba(0, 0, 0, 0.18);
  padding: 8px 10px;
  font-size: 12px;
  font-weight: 400;
  line-height: 1.45;
  white-space: normal;
  text-align: left;
  opacity: 0;
  visibility: hidden;
  pointer-events: none;
  transition: opacity 0.12s ease 0.25s, visibility 0s linear 0.37s;
}
.field[data-tip]:hover::after {
  opacity: 1;
  visibility: visible;
  transition-delay: 0.35s;
}
.field > label { font-size: 12px; color: var(--ink-2); }
.field input[type="text"], .field input[type="number"] {
  background: var(--page); color: var(--ink); border: 1px solid var(--baseline);
  border-radius: 6px; padding: 6px 8px; font: inherit; width: 110px;
  font-variant-numeric: tabular-nums;
}
.seg { display: inline-flex; border: 1px solid var(--baseline); border-radius: 8px; overflow: hidden; }
.seg button {
  background: var(--surface); color: var(--ink-2); border: 0; padding: 7px 14px;
  font: inherit; cursor: pointer;
}
.seg button + button { border-left: 1px solid var(--baseline); }
.seg button.on { background: var(--accent); color: #fff; }

button.primary {
  background: var(--accent); color: #fff; border: 0; border-radius: 8px;
  padding: 8px 18px; font: inherit; font-weight: 600; cursor: pointer;
}
button.primary:disabled { opacity: 0.5; cursor: default; }

.poshead { display: flex; justify-content: space-between; align-items: center; gap: 12px; font-size: 12px; color: var(--ink-2); margin-bottom: 8px; }
.postable th, .postable td { padding: 4px 10px; font-size: 13px; }

/* Fixed width so "–" -> "$400.00" can't reflow the control row either. */
.stepper { display: flex; align-items: center; gap: 4px; }
.stepper button {
  background: transparent; color: var(--ink); border: 1px solid var(--baseline);
  border-radius: 6px; width: 24px; height: 24px; line-height: 1;
  font: inherit; font-size: 14px; cursor: pointer; padding: 0;
}
.stepper button:hover:not(:disabled) { background: var(--surface); border-color: var(--accent); }
.stepper button:disabled { opacity: 0.4; cursor: default; }
.stepper .readout { width: 46px; text-align: center; padding: 0; }

.readout { font-weight: 650; color: var(--accent-strong); padding: 6px 0 7px; font-variant-numeric: tabular-nums; width: 90px; }
/* Slider fields are FIXED size so the flex row can't re-wrap when a label
   or value changes length (e.g. switching emphasis modes). Keeps every
   control physically anchored under the cursor. */
.sliderfield { flex: 0 0 300px; width: 300px; min-width: 300px; }
.sliderfield > label {
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
  height: 18px; line-height: 18px;
  font-size: 13px; font-weight: 650; color: var(--ink);
}
.sliderfield input[type="range"] { width: 100%; accent-color: var(--accent); }
.sliderval { font-weight: 650; color: var(--accent-strong); font-variant-numeric: tabular-nums; }
/* Range labels that cycle their span on click. The dotted underline is the
   affordance - without it nothing suggests the label is interactive. */
.sliderval.cyclable {
  cursor: pointer; text-decoration: underline dotted;
  text-underline-offset: 3px;
}
.sliderval.cyclable:hover { color: var(--accent); }

/* Emphasis-mode row highlights: the mode's best-$/day row, and (lighter)
   the best average $/day across both outcomes */
.calcresults tbody tr.hl-primary { background: color-mix(in srgb, var(--accent) 22%, transparent); }
.calcresults tbody tr.hl-avg { background: color-mix(in srgb, var(--accent) 9%, transparent); }

/* Dual-thumb range: two overlaid native sliders sharing one painted track */
.dualslider { position: relative; height: 26px; }
.dualslider::before {
  content: ""; position: absolute; top: 11px; left: 0; right: 0;
  height: 4px; border-radius: 2px; background: var(--grid);
}
.dualslider .snapline {
  position: absolute; left: 50%; top: 4px; bottom: 4px; width: 2px;
  transform: translateX(-1px); background: var(--baseline);
  pointer-events: none; z-index: 0;
}
.dualslider .fillbar {
  position: absolute; top: 11px; height: 4px; border-radius: 2px;
  background: var(--accent); left: 0; width: 100%;
}
.dualslider input[type="range"] {
  position: absolute; left: 0; top: 0; width: 100%; height: 26px; margin: 0;
  background: transparent; pointer-events: none;
  -webkit-appearance: none; appearance: none;
}
/* pointer-events: none above exists so a two-thumb slider lets you grab the
   RIGHT thumb. A shell holding a single range needs no such protection, and
   without this you could not click the track to jump - you had to hit the
   thumb exactly. */
.dualslider > input[type="range"]:only-of-type { pointer-events: auto; }
.dualslider input[type="range"]::-webkit-slider-runnable-track { height: 4px; background: transparent; }
.dualslider input[type="range"]::-moz-range-track { height: 4px; background: transparent; }
.dualslider input[type="range"]::-webkit-slider-thumb {
  -webkit-appearance: none; appearance: none; pointer-events: auto;
  width: 14px; height: 14px; border-radius: 50%; background: var(--accent);
  border: none; margin-top: -5px; cursor: pointer;
}
.dualslider input[type="range"]::-moz-range-thumb {
  pointer-events: auto; width: 14px; height: 14px; border-radius: 50%;
  background: var(--accent); border: none; cursor: pointer;
}
.pnl-pos { color: var(--good-text); font-weight: 600; }
.pnl-neg { color: #d03b3b; font-weight: 600; }

.tablewrap { overflow-x: auto; }
table { border-collapse: collapse; width: 100%; font-variant-numeric: tabular-nums; }
th, td { padding: 6px 10px; text-align: right; white-space: nowrap; }
th:first-child, td:first-child, th.txt, td.txt { text-align: left; }
thead th { color: var(--ink-2); font-size: 12px; font-weight: 600; border-bottom: 1px solid var(--baseline); }
tbody tr { border-bottom: 1px solid var(--grid); }
tbody tr:hover { background: color-mix(in srgb, var(--accent) 7%, transparent); }
td .flag { color: var(--warn); cursor: help; }
.dim { color: var(--muted); }

/* The results table scrolls inside its own box rather than growing the
   page without limit. It sizes to its content up to a cap, so a short
   result set doesn't leave a void above the chart; the sliders sit ABOVE
   this box, so a height change never moves them under the cursor. */
.calcresults { min-height: 120px; max-height: 430px; overflow: auto; }
.calcresults thead th { position: sticky; top: 0; background: var(--surface); z-index: 2; }

.finderresults { max-height: 300px; overflow: auto; }
.finderresults thead th { position: sticky; top: 0; background: var(--surface); z-index: 2; }
.pickrow { cursor: pointer; }

.progress { display: none; align-items: center; gap: 10px; margin: 10px 0; }
.progress.show { display: flex; }
.bar { flex: 1; height: 8px; background: var(--grid); border-radius: 4px; overflow: hidden; }
.bar > div { height: 100%; width: 0; background: var(--accent); transition: width 0.3s; }
.progress span { font-size: 12px; color: var(--ink-2); font-variant-numeric: tabular-nums; }

button.ghost {
  background: transparent; color: var(--ink-2); border: 1px solid var(--baseline);
  border-radius: 8px; padding: 7px 14px; font: inherit; cursor: pointer;
}

/* Candle chart (inline SVG — no charting dependency) */
.charthead { margin-top: 10px; border-top: 1px solid var(--grid); padding-top: 8px; align-items: flex-start; }
.legend { display: flex; gap: 12px; flex-wrap: wrap; font-size: 12px; color: var(--ink-2); }
.legend .key { display: inline-flex; align-items: center; gap: 5px; white-space: nowrap; }
.sw { width: 10px; height: 10px; border-radius: 2px; display: inline-block; }
.sw-up { background: var(--candle-up); }
.sw-down { background: var(--candle-down); }
.sw-50 { background: var(--accent); height: 3px; border-radius: 1px; }
.sw-200 { background: var(--series-2); height: 3px; border-radius: 1px; }
.chartwrap { position: relative; }
.chartwrap svg { display: block; width: 100%; height: auto; }
.chartrange { display: flex; align-items: center; gap: 12px; margin-top: 4px; }
.chartrange label { font-size: 12px; color: var(--ink-2); white-space: nowrap; }
/* The range input now sits inside a .dualslider shell, and that shell is
   what has to grow. Targeting the input directly (as this rule used to)
   left the wrapper at zero width, which made the slider unusable - it
   looked "stuck" because every click landed on its left edge. */
.chartrange .dualslider { flex: 1; }
.charttip {
  position: absolute; z-index: 20; pointer-events: none;
  background: var(--surface); color: var(--ink);
  border: 1px solid var(--baseline); border-radius: 8px;
  box-shadow: 0 4px 14px rgba(0, 0, 0, 0.18);
  padding: 6px 9px; font-size: 12px; line-height: 1.5;
  font-variant-numeric: tabular-nums; white-space: nowrap;
}

.status { font-size: 13px; color: var(--ink-2); margin: 6px 0; min-height: 18px; }

footer { margin-top: 22px; font-size: 12px; color: var(--muted); border-top: 1px solid var(--grid); padding-top: 10px; }

/* Company description under the chart */
.profile {
  margin-top: 12px; padding-top: 10px;
  border-top: 1px solid var(--grid);
}
.profilehead {
  display: flex; align-items: baseline; gap: 10px;
  flex-wrap: wrap; margin-bottom: 4px;
}
.profilehead strong { font-size: 14px; }
.profilehead .dim { font-size: 12px; }
.profile p {
  /* Full width, matching everything else on the page. The wrap's own
     1280px cap already holds the line length to roughly 95 characters,
     which the generous line-height keeps comfortable - so an extra
     max-width here only made the block look oddly narrow. */
  margin: 0; font-size: 13px; line-height: 1.6; color: var(--ink-2);
}

/* Trade confirmation dialog. A native confirm() cannot hold a slider, and
   picking the fill IS the point of this dialog. */
.modal {
  position: fixed; inset: 0; z-index: 100;
  background: rgba(0, 0, 0, 0.45);
  display: flex; align-items: center; justify-content: center; padding: 16px;
}
.modalbox {
  background: var(--surface); border: 1px solid var(--baseline);
  border-radius: 12px; padding: 16px 18px 14px;
  width: min(440px, 100%); box-shadow: 0 12px 40px rgba(0, 0, 0, 0.35);
}
.modalbox > strong { font-size: 15px; }
.modalbox p { font-size: 13px; line-height: 1.5; color: var(--ink-2); margin: 8px 0 12px; }
.modalbox .sliderfield { flex: none; width: auto; min-width: 0; margin-bottom: 4px; }
.modalbox .sliderfield input[type="range"] { width: 100%; }
.fillends {
  display: flex; justify-content: space-between;
  font-size: 11.5px; color: var(--muted); margin-top: 2px;
}
#tradeSpreadNote:empty, #tradeTotals:empty { display: none; }
#tradeSpreadNote { color: var(--warn); }
#tradeTotals { font-weight: 600; color: var(--ink); }
.modalactions { display: flex; gap: 8px; margin-top: 12px; }

/* Settings menu (hamburger, top-right of the header) */
header { position: relative; }
.hamburger {
  margin-left: auto; background: transparent; color: var(--ink);
  border: 1px solid var(--baseline); border-radius: 8px; cursor: pointer;
  font-size: 15px; line-height: 1; padding: 6px 10px;
}
.hamburger:hover { background: var(--surface); }
.menu {
  position: absolute; right: 0; top: calc(100% + 6px); z-index: 50;
  min-width: 236px; background: var(--surface);
  border: 1px solid var(--baseline); border-radius: 10px;
  padding: 10px 12px 12px; box-shadow: 0 8px 24px var(--border);
}
.menuhead {
  font-size: 12px; color: var(--ink-2);
  padding-bottom: 8px; border-bottom: 1px solid var(--grid);
}
.menusection { padding-top: 10px; }
.menulabel {
  font-size: 11px; text-transform: uppercase; letter-spacing: 0.05em;
  color: var(--muted); margin-bottom: 5px;
}
.menuitem {
  display: block; padding: 6px 8px; border-radius: 6px;
  font-size: 13px; color: var(--ink); text-decoration: none;
}
.menuitem:hover { background: color-mix(in srgb, var(--accent) 14%, transparent); }

.menucheck, .menusel {
  display: flex; align-items: center; gap: 7px;
  font-size: 13px; color: var(--ink); padding: 4px 8px; cursor: pointer;
}
.menusel { justify-content: space-between; }
.menusel select { font-size: 12px; }
.menubtn {
  width: 100%; text-align: left; background: transparent;
  border: 0; cursor: pointer; font: inherit; font-size: 13px;
}
#setColumns label {
  display: flex; align-items: center; gap: 7px;
  font-size: 12.5px; color: var(--ink); padding: 3px 8px; cursor: pointer;
}
#pwPanel { padding: 6px 8px 2px; display: flex; flex-direction: column; gap: 6px; }
#pwPanel input {
  font: inherit; font-size: 12.5px; padding: 5px 7px;
  border: 1px solid var(--baseline); border-radius: 6px;
  background: var(--page); color: var(--ink);
}
#pwPanel button { align-self: flex-start; }
#pwNote { font-size: 12px; margin: 0; min-height: 0; }

/* Simulation list: the whole row selects the active sim, marked by a tint
   plus a rail down the left edge. The rail lives on td:first-child because
   box-shadow on a <tr> is unreliable under border-collapse: collapse. */
#simList tr { cursor: pointer; }
/* Both lists, not just the live one: the time machine reuses the same
   class and lost its highlight entirely by being scoped out of these. */
#simList tr.simactive, #gmList tr.simactive { background: color-mix(in srgb, var(--accent) 20%, transparent); }
#simList tr.simactive:hover, #gmList tr.simactive:hover { background: color-mix(in srgb, var(--accent) 26%, transparent); }
#simList tr.simactive td:first-child, #gmList tr.simactive td:first-child { box-shadow: inset 3px 0 0 0 var(--accent); }

/* Simulation performance tiles */
.stattiles { display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); gap: 10px; margin: 10px 0 12px; }
.stattile { background: var(--surface); border: 1px solid var(--grid); border-radius: 10px; padding: 9px 14px 10px; }
.stattile .stlabel { font-size: 11px; text-transform: uppercase; letter-spacing: 0.05em; color: var(--muted); margin-bottom: 2px; white-space: nowrap; }
.stattile .stvalue { font-size: 20px; font-weight: 650; font-variant-numeric: tabular-nums; white-space: nowrap; }
.stattile .stvalue .pnl-pos, .stattile .stvalue .pnl-neg { font-weight: 650; }
.stattile .stvalue.dim { font-weight: 600; }
.stattile .stsub { font-size: 11.5px; color: var(--ink-2); margin-top: 2px; }

/* Simulation tab + trade buttons */
.tradebtn {
  background: var(--accent); color: #fff; border: 0; border-radius: 6px;
  padding: 3px 12px; font: inherit; font-size: 12px; font-weight: 600;
  cursor: pointer;
}
.tradebtn:hover { background: var(--accent-strong); }
/* No active simulation to trade into. aria-disabled rather than the
   `disabled` attribute, so the button still fires hover and can explain
   itself in a tooltip. */
.tradebtn[aria-disabled="true"],
.tradebtn[aria-disabled="true"]:hover {
  background: var(--grid); color: var(--muted); cursor: not-allowed;
}
.simlog { max-height: 260px; overflow: auto; }
.simlog td { font-size: 12px; }
/* Compact ghosts belong to the sim TABLES (Roll/Close/Undo/Delete), not to
   every ghost button in the app - this used to be unscoped and quietly
   shrank the finder's "Refresh candidates" next to a full-size primary. */
#simList button.ghost, #simPositions button.ghost { font-size: 12px; padding: 4px 10px; }

/* Simulation row actions: keep Copy and Delete on one line so the row
   height matches every other row in the table. */
.rowacts {
  display: flex;
  gap: 6px;
  white-space: nowrap;
}
.rowacts button { flex: 0 0 auto; }

/* Long-press to rename: suppress the text selection and the iOS callout
   that a press-and-hold would otherwise trigger over the name. */
td.simname {
  cursor: text;
  -webkit-user-select: none;
  user-select: none;
  -webkit-touch-callout: none;
}
td.simname input.renameinput {
  width: 100%;
  box-sizing: border-box;
  font: inherit;
  font-weight: 600;
  padding: 2px 4px;
}

/* Company-name lookup under the ticker boxes. */
.field.hassuggest { position: relative; }
.suggestbox {
  position: absolute;
  top: 100%;
  left: 0;
  z-index: 40;
  min-width: 300px;
  max-height: 260px;
  overflow-y: auto;
  background: var(--surface);
  color: var(--ink);
  border: 1px solid var(--grid);
  border-radius: 4px;
  box-shadow: 0 6px 18px rgba(0, 0, 0, .18);
}
.suggestrow {
  display: grid;
  grid-template-columns: 62px 1fr auto;
  gap: 8px;
  align-items: baseline;
  padding: 6px 9px;
  cursor: pointer;
  font-size: 13px;
  white-space: nowrap;
}
.suggestrow + .suggestrow { border-top: 1px solid var(--grid); }
.suggestrow.on, .suggestrow:hover { background: var(--grid); }
.suggestrow .sym { font-weight: 700; }
.suggestrow .nm { overflow: hidden; text-overflow: ellipsis; color: var(--ink-2); }
.suggestrow .ex { font-size: 11px; color: var(--muted); }

/* Data-source badge in its warning state: the feed degraded, or the
   Schwab authorisation is about to lapse. Deliberately loud - the whole
   point is that this failure is otherwise invisible. */
.badge.badgewarn {
  color: var(--warn);
  border-color: var(--warn);
  font-weight: 600;
}

/* ----------------------------------------------------------- time machine */

.gamebar { align-items: center; gap: 8px; flex-wrap: wrap; }
.gamebar label { display: inline-flex; align-items: center; gap: 4px; }
.sep { display: inline-block; width: 1px; height: 20px;
       background: var(--line, #d0d0d0); margin: 0 6px; }

.gamestatus { flex-wrap: wrap; gap: 6px 14px; margin: 10px 0;
              padding: 8px 10px; border: 1px solid var(--line, #d0d0d0);
              border-radius: 6px; }
.gmstat { white-space: nowrap; font-variant-numeric: tabular-nums; }
.gmstat b { font-weight: 600; opacity: 0.65; margin-right: 4px;
            font-size: 0.85em; text-transform: uppercase;
            letter-spacing: 0.03em; }

.gameturn { gap: 8px; flex-wrap: wrap; margin-bottom: 12px;
            align-items: center; }

.gamegrid { display: grid; grid-template-columns: minmax(0, 1.15fr) minmax(0, 1fr);
            gap: 20px; align-items: start; }
.gamegrid h3 { margin: 14px 0 6px; font-size: 0.95rem; }
.gamegrid > div > h3:first-child { margin-top: 0; }
@media (max-width: 1100px) { .gamegrid { grid-template-columns: 1fr; } }

.gamechain, .gamelog { max-height: 340px; overflow-y: auto; }
.gamelog td { padding-top: 3px; padding-bottom: 3px; }
.gamelog td:nth-child(3) { white-space: normal; }

.gmchart { width: 100%; height: 160px; display: block; margin: 8px 0 14px; }
.gmkey { display: inline-block; width: 10px; height: 10px; border-radius: 2px;
         margin-right: 6px; vertical-align: baseline; }

.gmmodal { position: fixed; inset: 0; background: rgba(0, 0, 0, 0.45);
           display: flex; align-items: center; justify-content: center;
           z-index: 50; padding: 20px; }
.gmmodal[hidden] { display: none; }
.gmmodalbox { background: var(--panel, #fff); color: inherit;
              border-radius: 8px; padding: 18px 22px; max-width: 640px;
              width: 100%; max-height: 85vh; overflow-y: auto;
              box-shadow: 0 12px 40px rgba(0, 0, 0, 0.35); }
.gmmodalbox h2 { margin: 0 0 10px; font-size: 1.1rem; }

.simmode { display: flex; flex-direction: column; align-items: flex-start;
           gap: 6px; margin-bottom: 16px; }

/* One control, two segments - joined so it reads as a single either/or
   rather than two independent buttons that happen to sit together. */
.modeseg { display: inline-flex; border: 1px solid var(--border);
           border-radius: 10px; overflow: hidden; background: var(--surface); }
.modebtn { display: flex; flex-direction: column; align-items: flex-start;
           gap: 1px; padding: 8px 18px; line-height: 1.25; cursor: pointer;
           border: 0; border-radius: 0; background: transparent;
           color: var(--ink-2); text-align: left; min-width: 190px;
           transition: background 0.12s ease, color 0.12s ease; }
.modeseg .modebtn + .modebtn { border-left: 1px solid var(--border); }
.modebtn:hover { background: var(--grid); color: var(--ink); }
.modettl { font-weight: 600; font-size: 0.98em; }
.modesub { font-size: 0.78em; opacity: 0.7; font-weight: 400; }
.modebtn.on { background: var(--accent); color: #fff; }
.modebtn.on:hover { background: var(--accent-strong); }
.modebtn.on .modesub { opacity: 0.85; }
.modebtn:focus-visible { outline: 2px solid var(--accent-strong);
                         outline-offset: -2px; }
@media (max-width: 620px) {
  .modeseg { flex-direction: column; width: 100%; }
  .modeseg .modebtn + .modebtn { border-left: 0;
                                 border-top: 1px solid var(--border); }
}
.gmslotpick { min-width: 130px; }

/* Free cash beside the CSP mode buttons. Deliberately larger than the
   surrounding controls: it is the number that decides whether a trade is
   possible at all, and it was previously only visible on another tab. */
.freecashfield { margin-left: auto; align-items: flex-end; }
.freecashfield label { white-space: nowrap; }
.freecash { font-size: 22px; font-weight: 650; line-height: 1.1;
            font-variant-numeric: tabular-nums; color: var(--ink); }
.freecash.low { color: var(--candle-down); }

/* ITM flag and strike highlight, shared by both position tables. */
.itmcol { width: 42px; padding-left: 6px !important; padding-right: 0 !important; }
.itmbadge { display: inline-block; font-size: 10px; font-weight: 700;
            letter-spacing: 0.04em; padding: 1px 5px; border-radius: 4px;
            background: var(--warn); color: #1a1a19; vertical-align: middle; }
tr.itmrow td:first-child { box-shadow: inset 3px 0 0 0 var(--warn); }
.strikehl { font-weight: 650; color: var(--ink);
            background: color-mix(in srgb, var(--accent) 16%, transparent);
            border-radius: 4px; padding: 0 4px; }

/* "held" marker in the candidate finder, and the import button's note. */
.heldmark { display: inline-block; margin-left: 6px; font-size: 10px;
            font-weight: 700; letter-spacing: 0.04em; padding: 1px 5px;
            border-radius: 4px; background: var(--accent); color: #fff;
            vertical-align: middle; cursor: help; }
/* Sits under the Import button. The width is CAPPED and the text wraps:
   the note is as long as a ticker plus a basis plus a position counter, and
   left to size itself it widened its .field enough to push the Mode control
   onto the next line every time you imported. */
.importnote { display: block; margin-top: 4px; font-size: 12px;
              color: var(--ink-2); max-width: 168px; white-space: normal;
              line-height: 1.3; }

/* Equity curve at the foot of the time machine. */
#gmCurveWrap { border: 1px solid var(--grid); border-radius: 8px;
               background: var(--surface); padding: 6px 8px 2px; }
.growthchart { width: 100%; height: 220px; display: block; }
.growthstats { display: inline-flex; gap: 18px; align-items: baseline; }
.growthstat { display: inline-flex; flex-direction: column; align-items: flex-end; }
.growthstat .glabel { font-size: 10px; text-transform: uppercase;
                      letter-spacing: 0.05em; color: var(--muted); }
.growthstat .gvalue { font-size: 20px; font-weight: 650;
                      font-variant-numeric: tabular-nums; line-height: 1.1; }
.growthaxis { justify-content: space-between; font-size: 11.5px;
              color: var(--muted); margin-top: 4px; }

/* The play-mode segment sits beside the data-source one: same furniture,
   separate control, because they answer different questions - WHICH data,
   and WHO trades. */
.simmode { flex-direction: row; flex-wrap: wrap; align-items: center; gap: 10px; }
.ruleslink { align-self: center; font-size: 12px; padding: 5px 10px; }
.ruleslink.on { background: var(--accent); color: #fff;
                border-color: var(--accent); font-weight: 600; }
.ruleslink.on:hover { background: var(--accent-strong);
                      border-color: var(--accent-strong); }
.stratpicks { display: inline-flex; align-items: center; gap: 6px;
              flex-wrap: wrap; }
/* A rank badge, not decoration: these are ordered by measured performance
   and the order is the point. */
.stratbtn { align-self: center; font-size: 12px; padding: 5px 10px 5px 7px;
            display: inline-flex; align-items: center; gap: 6px; }
.stratbtn .rk { display: inline-grid; place-items: center;
                width: 16px; height: 16px; border-radius: 4px;
                background: var(--accent); color: #fff;
                font-size: 10px; font-weight: 700; line-height: 1; }
.stratbtn[data-rank="2"] .rk { background: var(--muted, #7b8a8e); }
/* A strategy that cannot execute as described here says so at the point of
   use - see data/strategies.json. */
.stratbtn.caveated .rk::after { content: "!"; }
.stratbtn.caveated { border-style: dashed; }

.simmode #modeNote { flex-basis: 100%; }

.timebar { align-items: center; gap: 8px; flex-wrap: wrap; margin-bottom: 12px; }
.timelabel { font-size: 10px; text-transform: uppercase; letter-spacing: 0.05em;
             color: var(--muted); }

.rulesform { display: grid; grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
             gap: 10px 20px; margin-bottom: 14px; }
.rulerow { display: flex; flex-direction: column; gap: 3px; padding: 8px 10px;
           border: 1px solid var(--grid); border-radius: 8px;
           background: var(--surface); }
.rulerow.gated { opacity: 0.55; }
.ruletop { display: flex; align-items: center; gap: 10px; }
.rulename { font-weight: 600; font-size: 13px; flex: 1; }
/* The whole row carries the long explanation, so cue that it is hoverable
   rather than leaving it to be discovered by accident. */
.rulerow { cursor: help; }
.rulerow input, .rulerow select { cursor: auto; }
.rulerow:hover { border-color: var(--baseline); }
.rulerow input[type=number], .rulerow select { width: 110px; }
.ruledesc { font-size: 11.5px; color: var(--ink-2); line-height: 1.35; }
.rulenote { font-size: 11px; color: var(--muted); line-height: 1.35;
            border-left: 2px solid var(--grid); padding-left: 6px; }
.ruledirty { border-color: var(--accent); }
.ruledirty .rulename::after { content: " (changed)"; color: var(--accent);
                              font-weight: 600; font-size: 11px; }
.rulebase { display: flex; align-items: center; gap: 8px; font-size: 11px;
            color: var(--muted); }
.ruledefault { font-variant-numeric: tabular-nums; }
.rulebase .ruleundo { font-size: 10.5px; padding: 1px 7px; }
.ruleschanged { margin: 6px 0 12px; font-size: 12.5px; display: flex;
                flex-wrap: wrap; gap: 6px; align-items: center; }
.ruleschanged .rulejump { font-size: 11.5px; padding: 2px 8px;
                          font-variant-numeric: tabular-nums; }
.ruleflash { animation: ruleflash 1.2s ease-out; }
@keyframes ruleflash {
  from { background: color-mix(in srgb, var(--accent) 30%, transparent); }
  to   { background: var(--surface); }
}

/* Which saved rule set the form came from, and whether it still matches. */
.rulesloaded { margin: 8px 0 2px; font-size: 12.5px; color: var(--ink-2); }
.rulesmod { color: var(--warn-text, var(--ink)); background:
            color-mix(in srgb, var(--warn) 22%, transparent);
            padding: 3px 8px; border-radius: 6px; }
#rulesetList tr.simactive td:first-child { box-shadow: inset 3px 0 0 0 var(--accent); }
#rulesetList button[disabled] { opacity: 0.45; cursor: not-allowed; }

/* Always its own line: these are the page's actions, not another item in
   the list of what differs from default, and they must not end up sitting
   beside that text when it happens to be short. */
.rulesactions { display: flex; width: 100%; flex-basis: 100%; gap: 8px;
                align-items: center; clear: both;
                margin: 10px 0 14px; padding-top: 10px;
                border-top: 1px solid var(--grid); }
.rulesactions button { font-weight: 650; }
/* The loaded rule set stays marked for as long as it is loaded, which is
   what replaced the "Apply to this sim" button. */
#rulesetList tr.simactive { background: color-mix(in srgb, var(--accent) 14%, transparent); }
#rulesetList tr.simactive .rsname strong::after {
  content: " · loaded"; color: var(--accent); font-weight: 600; font-size: 11px;
}
.rsname { cursor: pointer; }

/* Trend overlay legend under the equity curve. */
.trendlegend { display: flex; flex-wrap: wrap; gap: 4px 14px; margin: 8px 0 2px;
               font-size: 11.5px; color: var(--ink-2); }
.trendpick { display: inline-flex; align-items: center; gap: 5px;
             cursor: pointer; white-space: nowrap; }
.trendswatch { width: 16px; height: 3px; border-radius: 2px;
               display: inline-block; }
.trendnote { flex-basis: 100%; font-size: 11px; }

/* Sector allow-list. Wraps to as many columns as fit rather than making
   an 11-tall column that pushes every rule below it off the screen. */
.ruletop { flex-wrap: wrap; }        /* the sector grid needs its own line */
.rulemulti { display: flex; flex-wrap: wrap; gap: 2px 14px;
             flex-basis: 100%; margin-top: 2px; }
.multipick { display: inline-flex; align-items: center; gap: 5px;
             font-size: 12px; white-space: nowrap; cursor: pointer; }
.multiacts { display: inline-flex; gap: 6px; margin-left: auto; }
.multiacts button { font-size: 11px; padding: 1px 7px; }

/* Starting balance -> estimated value, every term in between. */
.reconwrap { margin: 8px 0 4px; font-size: 12px; }
.reconwrap summary { cursor: pointer; color: var(--ink-2); }
.recon { max-width: 460px; margin-top: 6px; border: 1px solid var(--grid);
         border-radius: 8px; padding: 6px 10px; background: var(--surface); }
.rrow { display: flex; justify-content: space-between; gap: 16px;
        padding: 3px 0; cursor: help; }
.rrow + .rrow { border-top: 1px dotted var(--grid); }
.rval { font-variant-numeric: tabular-nums; }
.rrowtot { font-weight: 600; }
.rrowsum { border-top: 1px solid var(--baseline); }

/* ------------------------------------------------ strategy guide tab */
.guideblurb { max-width: 72ch; color: var(--ink-2); line-height: 1.5;
              font-size: 13.5px; margin: 6px 0 10px; }
.guidescroll { overflow-x: auto; border: 1px solid var(--grid);
               border-radius: 10px; background: var(--surface); }
.guidesvg { display: block; }
.g-axis { font-size: 11px; fill: var(--muted); }
.guidewrap { position: relative; }
.guiderail { position: absolute; left: 0; top: 0; width: 122px;
             pointer-events: none; z-index: 5; overflow: hidden;
             border-radius: 10px 0 0 10px;
             background: linear-gradient(90deg, var(--surface) 72%,
                                         transparent); }
.guiderail span { position: absolute; left: 8px; transform: translateY(-50%);
                  white-space: nowrap; }
.g-rail-lane { font-size: 11px; color: var(--muted); font-weight: 600;
               text-transform: uppercase; letter-spacing: 0.06em; }
.g-rail-strike { font-size: 11px; color: var(--baseline); font-weight: 600;
                 pointer-events: auto; cursor: help; }
.g-rail-axis { font-size: 11px; color: var(--muted); }
.g-strike { stroke: var(--baseline); stroke-width: 1.5;
            stroke-dasharray: 7 5; opacity: 0.8; cursor: help; }
.g-strikelbl { font-size: 11px; fill: var(--baseline); font-weight: 600; }
.g-bar { cursor: help; }
.g-bar:hover rect { fill-opacity: 0.45; }
.g-barlbl { font-size: 11.5px; fill: var(--ink); }
.g-out { font-size: 13px; font-weight: 700; }
.g-good { fill: var(--good-text); }
.g-warn { fill: var(--candle-down); }
.g-roll { fill: var(--accent); }
.g-dim { fill: var(--muted); }
.g-flag { cursor: help; }
.g-flagnum { font-size: 11px; font-weight: 700; fill: var(--bg); }
.guidetip { position: fixed; z-index: 300; display: none;
            background: var(--surface); color: var(--ink);
            border: 1px solid var(--baseline); border-radius: 8px;
            padding: 10px 12px; font-size: 12.5px; line-height: 1.5;
            box-shadow: 0 6px 24px rgba(0,0,0,0.25); pointer-events: none; }
#guidePick button.on { border-color: var(--accent); color: var(--accent);
                       font-weight: 600; }

/* Strategy family selector on the rules page - same shape as the play-
   mode buttons so the page reads as one system. */
#rulesStrategies { gap: 8px; flex-wrap: wrap; margin: 4px 0 10px; }
.stratbtn { display: flex; flex-direction: column; align-items: flex-start;
            gap: 2px; padding: 8px 14px; border: 1px solid var(--grid);
            border-radius: 10px; background: var(--surface); cursor: pointer; }
.stratbtn:hover { border-color: var(--baseline); }
.stratbtn.on { border-color: var(--accent);
               background: color-mix(in srgb, var(--accent) 10%, var(--surface)); }
.stratbtn .modettl { font-weight: 600; font-size: 13px; color: var(--ink); }
.stratbtn .modesub { font-size: 11px; color: var(--muted); }

/* GA evolution player */
.gaprow { display: flex; gap: 14px; flex-wrap: wrap; align-items: flex-start; }
.gapsvg { display: block; max-width: 100%; background: var(--surface);
          border: 1px solid var(--grid); border-radius: 8px; margin-bottom: 8px; }
.gaplegendrow { display: flex; gap: 12px; font-size: 11.5px; color: var(--ink-2); }
.gaplegend { display: inline-flex; gap: 5px; align-items: center; }
.gapcard { border: 1px solid var(--accent); border-radius: 10px;
           padding: 12px 14px; margin-top: 10px; background: var(--surface); }
.gapcard h3 { margin: 0 0 8px; }
.gapgenes { display: flex; flex-wrap: wrap; gap: 6px 14px; margin-bottom: 8px; }
.gapgene { font-size: 12.5px; }
.gapgene em { color: var(--muted); font-style: normal; margin-right: 4px; }

/* ---- growth chart + explore overlay -------------------------------------
   The chart and its legend sit side by side so a run of twenty curves has
   somewhere to be named. Below 900px the legend drops underneath rather
   than squeezing the chart into a strip. */
.gmchartrow { display: flex; gap: 14px; align-items: flex-start; }
.gmchartrow #gmCurveWrap { flex: 1 1 auto; min-width: 0; }
#gmExplorePanel { flex: 0 0 214px; display: flex; flex-direction: column;
                  gap: 8px; }
.gmexprow { gap: 6px; align-items: center; }
.gmexpn { width: 62px; }
.gmrunlegend { display: flex; flex-direction: column; gap: 1px;
               max-height: 232px; overflow-y: auto; overflow-x: hidden;
               border: 1px solid var(--rule, #ddd); border-radius: 6px;
               padding: 4px; }
.gmrunlegend:empty { display: none; }
.gmrunrow { display: flex; align-items: center; gap: 6px; padding: 2px 4px;
            border-radius: 4px; cursor: pointer; font-size: 0.82rem;
            white-space: nowrap; }
.gmrunrow:hover { background: rgba(127, 127, 127, 0.12); }
/* The highlighted row and its curve are the same state, reachable from
   either side - click a line, its label lights; click a label, its line does. */
.gmrunhot { background: rgba(127, 127, 127, 0.20); font-weight: 600; }
.gmrunlbl { overflow: hidden; text-overflow: ellipsis; flex: 1 1 auto;
            min-width: 0; }
.gmrunret { font-variant-numeric: tabular-nums; font-size: 0.78rem; }
.gmaxis { font-size: 10px; fill: currentColor; opacity: 0.65; }
.gmbench { pointer-events: none; }
.gmrun { transition: stroke-opacity 0.12s, stroke-width 0.12s; }
@media (max-width: 900px) {
  .gmchartrow { flex-direction: column; }
  #gmExplorePanel { flex: 1 1 auto; width: 100%; }
  .gmrunlegend { max-height: 180px; }
}
/* The two fixed rows (this sim, the index) sit above the random runs and
   are always present - hiding a curve is useful before any comparison. */
.gmfixedrow { border-bottom: 1px solid var(--rule, #ddd); padding-bottom: 4px;
              margin-bottom: 2px; font-weight: 600; }
.gmfixedrow + .gmfixedrow { border-bottom-width: 2px; margin-bottom: 4px; }
/* Champion equity vs the index, under the GA scatter. A fitness of "3.20"
   is unreadable on sight; the same claim as a curve against SPY is not. */
.gapgrowth { margin-top: 8px; }
.gapgrowth .gapsvg { width: 100%; height: auto; }
.gapnote { margin: 8px 0 0; font-size: 0.85rem; }

/* GA run description (Evolution tab). Sits above the player and says what
   the run WAS, plus how far its numbers can be trusted - the caveat text
   is the point, so it is styled to be read, not skipped. */
.gaabout { margin: 4px 0 14px; max-width: 78ch; }
.gaabout p { margin: 0 0 8px; }
.gatable { border-collapse: collapse; margin: 10px 0; font-size: 0.9rem; }
.gatable th, .gatable td {
  text-align: right; padding: 4px 10px 4px 0;
  font-variant-numeric: tabular-nums;
}
.gatable th:first-child, .gatable td:first-child { text-align: left; }
.gatable thead th {
  border-bottom: 1px solid var(--grid); font-weight: 600; color: var(--ink-2);
}
.gaabout .gapnote { border-left: 2px solid var(--warn); padding-left: 10px; }

/* Transport sits above the scatter it drives. A little breathing room
   under it so the controls read as a header for the charts, not as a
   stray row floating between the description and the plot. */
.gaptransport { margin: 0 0 10px; }

/* Step buttons flanking play. Fixed width so the transport does not
   reflow as the labels change, and a clearly dead look at the ends of the
   run rather than a button that silently does nothing. */
.gapstep { min-width: 34px; padding-left: 8px; padding-right: 8px; }
.gapstep[disabled] { opacity: 0.35; cursor: default; }

/* The best-so-far and family-share strips, now full width below the
   charts rather than squeezed into a 260px side column. Their SVGs are a
   560-unit viewBox, so widening them stretches the fitness line out and
   makes generation-to-generation movement readable. */
.gapstrips { margin-top: 14px; }
.gapstrips .gapsvg { width: 100%; height: auto; }
.gapstrips .gaplegendrow { margin-top: 6px; }

/* Colour key sits under the scatter it explains. Only the families the
   run actually contains are listed - a key naming five when the run has
   one is noise pretending to be information. */
.gapscatterkey { margin: 6px 0 2px; flex-wrap: wrap; }

/* Growth-chart scale toggle. Sits above the chart it changes, right
   aligned so it reads as a control on that panel rather than a heading. */
.gapgrowthhead { display: flex; justify-content: flex-end; margin: 10px 0 -4px; }
.gaplogbtn { font-size: 11.5px; padding: 3px 10px; }

/* Flagged generations: the picker button, and the panel that drops under
   it. A native <select> cannot hold checkboxes, so the panel sits in the
   same place and answers the same gesture without imitating one. */
.gapflagbtn.on { border-color: var(--warn); color: var(--ink); }
.gapflagpanel {
  border: 1px solid var(--grid); border-radius: 6px;
  padding: 8px 10px; margin: 6px 0 10px; max-width: 460px;
  background: var(--surface);
}
.gapflaglist { display: flex; flex-direction: column; gap: 3px; max-height: 220px;
  overflow-y: auto; }
.gapflagitem { display: flex; align-items: center; gap: 7px; font-size: 12.5px; }
.gapflagitem input { margin: 0; }

/* Overlay legend, to the right of the growth chart like the simulation
   tab's run legend. Each row identifies one curve by colour; clicking a
   row makes that generation the active one. */
.gapgrowth { display: flex; gap: 12px; align-items: flex-start; }
.gapgrowth .gapsvg { flex: 1 1 auto; min-width: 0; }
.gapcurvelegend {
  flex: 0 0 auto; max-width: 230px; display: flex; flex-direction: column;
  gap: 4px; font-size: 12px; padding-top: 4px;
}
.gapcurvepick { cursor: pointer; white-space: normal; align-items: flex-start; }
.gapcurvepick.on { font-weight: 600; }
.gapcurvepick .trendswatch { margin-top: 6px; flex: 0 0 auto; }
.gapcurveret { font-variant-numeric: tabular-nums; margin-left: 4px; }

/* Episode counts sit beside the drawdown they qualify, left-aligned and
   quiet - they are a caveat on the number to their left, not a metric. */
.gatable td.gapeps { text-align: left; color: var(--ink-2); font-size: 0.85rem;
                     padding-left: 6px; white-space: nowrap; }
