/* ---------------------------------------------------------------------
   THEME TRACKER — styles
   Dark dashboard styled after a trading-desk "theme momentum" tool.
--------------------------------------------------------------------- */
:root{
  --bg:            #12151a;
  --panel:         #181c22;
  --panel-2:       #1e232b;
  --panel-border:  #272e37;
  --row-hover:     #222831;
  --input-bg:      #15191f;
  --text:          #eef0f3;
  --text-dim:      #97a0ac;
  --text-faint:    #666f7c;
  --accent:        #22c55e;
  --accent-dim:    #15402b;
  --green:         #35d07f;
  --green-bg:      #113322;
  --red:           #ff5a5a;
  --red-bg:        #3a1418;
  --accent-line:   #2b323c;
  --shadow-sm:     0 1px 2px rgba(0,0,0,.35);
  --shadow-md:     0 10px 30px rgba(0,0,0,.35), 0 2px 8px rgba(0,0,0,.22);
  --shadow-lg:     0 24px 70px rgba(0,0,0,.5), 0 6px 20px rgba(0,0,0,.3);
  --ease:          cubic-bezier(.4,0,.2,1);
  --ease-spring:   cubic-bezier(.16,1,.3,1);
  --t-fast:        160ms var(--ease);
  --t-med:         220ms var(--ease);
  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Inter, Roboto, Helvetica, Arial, sans-serif;
  font-variant-numeric: tabular-nums;
}
*{ box-sizing: border-box; }

html,body{ margin:0; padding:0; background:var(--bg); color:var(--text); }
body{ padding: 28px 42px 60px; }

/* Custom scrollbars — thin, quiet, brighten slightly on hover */
::-webkit-scrollbar{ height:9px; width:9px; }
::-webkit-scrollbar-track{ background:transparent; }
::-webkit-scrollbar-thumb{
  background:var(--panel-border); border-radius:999px; border:2px solid transparent; background-clip:padding-box;
  transition: background-color var(--t-fast);
}
::-webkit-scrollbar-thumb:hover{ background-color:#3a4149; background-clip:padding-box; }
*{ scrollbar-width: thin; scrollbar-color: var(--panel-border) transparent; }

a{ color:inherit; }

/* ---------- Header ---------- */
.header-row{
  display:flex; justify-content:space-between; align-items:flex-start;
  margin-bottom: 10px; flex-wrap: wrap; gap:12px;
}
.brand-row{ display:flex; align-items:center; gap:13px; }
.brand-logo{
  width:52px; height:52px; border-radius:12px; display:block; object-fit:cover;
  box-shadow: var(--shadow-sm), 0 0 0 1px var(--panel-border);
}
h1{
  font-size: 26px; font-weight: 700; margin: 0 0 4px 0; letter-spacing: -0.02em;
}
.subtitle{
  color: var(--text-dim); font-size: 13px; margin:0;
}

/* Header right-side cluster: ticker count + status on the left, icon
   buttons on the right, all one row — kept visually secondary so it
   never competes with the <h1>. Wraps onto its own line under the brand
   row (via .header-row's own flex-wrap) rather than overlapping when
   the window gets narrow or the status text is at its longest. */
.header-actions{
  display:flex; flex-direction:row; align-items:center; gap:14px;
  flex-wrap:wrap; justify-content:flex-end;
}
.header-status{
  display:flex; align-items:center; gap:6px; font-size:12px; color:var(--text-dim);
  white-space:nowrap;
}
.status-sep{ color:var(--text-faint); }
.update-dot{ color:var(--text-faint); transition: color var(--t-fast); white-space:nowrap; }
.update-dot.ok{ color:var(--accent); }
.update-dot.err{ color:var(--red); }
.header-icons{ display:flex; gap:6px; flex:0 0 auto; }

.icon-btn{
  background: var(--panel); border:1px solid var(--panel-border); color: var(--text);
  padding: 7px 14px; border-radius: 7px; font-size: 13px; cursor: pointer;
  transition: background var(--t-fast), border-color var(--t-fast), color var(--t-fast), transform var(--t-fast);
}
.icon-btn:hover{ background:#171b20; border-color:#333a44; }
.icon-btn:active{ transform: scale(.97); }
.icon-btn:disabled{ opacity:.5; cursor: default; }
.icon-btn.small{ padding:5px 11px; font-size:12px; }
.icon-btn.round{
  width:34px; height:34px; padding:0; border-radius:50%; font-size:15px;
  display:inline-flex; align-items:center; justify-content:center;
}

/* Slim top progress bar — sits right under the header, above the controls bar */
.progress-track{
  height:2.5px; width:100%; background:var(--panel-border); border-radius:999px; overflow:hidden;
  margin: 6px 0 18px; opacity:0; transition:opacity .2s;
}
.progress-track.active{ opacity:1; }
.progress-bar{
  height:100%; width:0%; background:var(--accent); transition:width .2s ease;
}

/* ---------- Controls bar ---------- */
.controls-bar{
  display:flex; align-items:center; gap: 18px; margin: 4px 0 14px;
  flex-wrap: wrap;
}
.rank-label{ color: var(--text-dim); font-size: 13px; margin-right: 2px; }
/* Rank-by group and the Expand/collapse action button share one visual
   language: a pill-shaped "track" (panel-2 fill, fully rounded, small inner
   padding) holding fully-rounded buttons — see .rank-btn below. The track
   itself carries no border; its background alone reads as a grouping. */
.rank-buttons, .utility-buttons, .chart-range-buttons, .segmented{
  display:flex; gap:3px; background: var(--panel-2); border-radius:999px; padding:4px;
}
.rank-btn{
  background: transparent; border:none; color: var(--text-dim);
  padding: 6px 13px; border-radius: 999px; font-size: 13px; cursor:pointer; font-weight:600;
  transition: background var(--t-fast), color var(--t-fast), transform var(--t-fast);
}
.rank-btn:hover{ color: var(--text); }
.rank-btn:active{ transform: scale(.97); }
.rank-btn.active{
  background: var(--accent); color: #06210f;
}
.spacer{ flex:1; }
/* Same floating pill-track language as .rank-buttons/.utility-buttons above:
   one continuous var(--panel-2) capsule holds both inputs and the round "+"
   button. Inputs go transparent/borderless (the capsule itself reads as the
   field boundary) with a thin vertical divider between them, and focus is
   shown on the CAPSULE as a whole via :focus-within rather than on whichever
   input happens to be focused — so the two fields + button always read as
   one element, not three. */
.add-theme-form{
  display:flex; align-items:center; gap:0;
  background: var(--panel-2); border-radius:999px; padding:4px;
  transition: box-shadow var(--t-fast);
}
.add-theme-form:focus-within{
  box-shadow: 0 0 0 3px rgba(34,197,94,.18);
}
.add-theme-form input{
  background: transparent; border:none; color: var(--text);
  padding: 7px 12px; font-size: 13px; outline:none;
}
.add-theme-form input#newThemeName{ width: 150px; }
.add-theme-form input#newThemeTickers{ width: 190px; }
.add-theme-form input::placeholder{ color: var(--text-faint); }
.add-theme-divider{
  width:1px; height:18px; background:var(--panel-border); flex:0 0 auto; margin:0 2px;
}
/* Overrides just the size of the shared .icon-btn.round (34px, sized for the
   header) down to fit this shorter 4px-padded pill snugly — otherwise
   identical: same background/border/hover/active behavior as every other
   round icon button in the app. */
#addThemeBtn{
  width:30px; height:30px; font-size:16px; flex:0 0 auto; margin-left:4px;
}
.btn-primary{
  background: var(--accent); color:#06210f; border:none; padding:7px 16px;
  border-radius:7px; font-size:13px; font-weight:700; cursor:pointer;
  transition: filter var(--t-fast), transform var(--t-fast);
}
.btn-primary:hover{ filter:brightness(1.08); }
.btn-primary:active{ transform: scale(.97); }

/* ---------- Settings drawer ---------- */
.drawer-backdrop{
  display:flex; position: fixed; inset:0; background: rgba(4,5,7,0.55);
  -webkit-backdrop-filter: blur(6px) saturate(120%); backdrop-filter: blur(6px) saturate(120%);
  z-index: 110; justify-content:flex-end;
  visibility:hidden; opacity:0; pointer-events:none;
  transition: opacity var(--t-med), visibility 0s linear var(--t-med);
}
.drawer-backdrop.open{
  visibility:visible; opacity:1; pointer-events:auto;
  transition: opacity var(--t-med), visibility 0s linear 0s;
}
.settings-drawer{
  width: 360px; max-width:92vw; height:100%; background: var(--panel);
  border-left:1px solid var(--panel-border); box-shadow: var(--shadow-lg);
  display:flex; flex-direction:column;
  transform: translateX(100%); transition: transform var(--t-med) var(--ease-spring);
}
.drawer-backdrop.open .settings-drawer{ transform: translateX(0); }
.drawer-header{
  display:flex; align-items:center; justify-content:space-between;
  padding:16px 18px; border-bottom:1px solid var(--panel-border); flex:0 0 auto;
}
.drawer-header h2{ margin:0; font-size:15px; font-weight:700; }
.drawer-header-actions{ display:flex; align-items:center; gap:6px; }
.drawer-body{
  flex:1 1 auto; overflow-y:auto; padding:16px 18px; display:flex; flex-direction:column; gap:20px;
}
.drawer-footer{
  flex:0 0 auto; padding:14px 18px; border-top:1px solid var(--panel-border);
  display:flex; flex-direction:column; gap:8px;
}
.settings-group{ display:flex; flex-direction:column; gap:10px; }
.settings-group h3{
  margin:0; font-size:10.5px; letter-spacing:.07em; text-transform:uppercase;
  color: var(--text-faint); font-weight:700;
}
.settings-field{ display:flex; flex-direction:column; gap:5px; min-width:0; }
.settings-field label{ font-size:11.5px; color:var(--text-dim); }
.settings-field input,.settings-field select{
  background:var(--input-bg); border:1px solid var(--panel-border); color:var(--text);
  padding:7px 10px; border-radius:6px; font-size:13px; min-width:0; outline:none;
  transition: border-color var(--t-fast), box-shadow var(--t-fast);
}
.settings-field input:focus,.settings-field select:focus{ border-color:#4a5563; box-shadow: 0 0 0 3px rgba(34,197,94,.10); }
.settings-field input[type="number"]{ width:100%; }
.settings-note{ color:var(--text-faint); font-size:11.5px; line-height:1.5; }
.settings-note code{ background:var(--panel-2); padding:1px 4px; border-radius:3px; }
.danger-btn{
  background:transparent; color:var(--red); border:1px solid rgba(255,90,90,.3);
  padding:8px 12px; border-radius:7px; font-size:12.5px; font-weight:700; cursor:pointer;
  transition: background var(--t-fast), border-color var(--t-fast), transform var(--t-fast);
}
.danger-btn:hover{ background:var(--red-bg); border-color:var(--red); }
.danger-btn:active{ transform: scale(.97); }
.toggle-field{
  display:flex; align-items:center; gap:8px; min-height:26px; color:var(--text);
  font-size:12.5px;
}
.toggle-field input{ accent-color:var(--accent); }

/* Requests/min: quick presets + a compact custom field for anything else.
   flex-wrap:wrap on the row is a defensive fallback — if the drawer/preset
   labels ever genuinely can't fit on one line, the number input drops to
   its own line below the presets (with the same 8px gap applied as a
   row-gap) instead of clipping or overlapping anything. */
.rate-row{ display:flex; align-items:center; gap:8px; flex-wrap:wrap; }
/* flex:0 0 auto — without this, .segmented was the only shrinkable flex
   item in the row (the number input below already had flex:0 0 auto, but
   this div never did), so whenever the row ran short on space — a narrow
   drawer (max-width:92vw), or just a browser zoomed in, which shrinks the
   CSS pixels available exactly like a narrower viewport would — .segmented
   was the one thing squeezed. Combined with its own overflow:hidden (there
   to clip its rounded corners), squeezing it below its buttons' natural
   combined width didn't reflow anything, it just hard-clipped button text
   (e.g. "60" rendering as "6"), leaving the segmented control and the
   input crammed edge-to-edge. Both children are now fixed to their natural
   size, so the row above wraps instead of either one ever shrinking. */
.segmented{ flex:0 0 auto; }
.seg-btn{
  background:transparent; border:none; color:var(--text-dim);
  padding:7px 12px; border-radius:999px; font-size:12.5px; font-weight:600; cursor:pointer;
  transition: background var(--t-fast), color var(--t-fast);
}
.seg-btn:hover{ color:var(--text); }
.seg-btn.active{ background:var(--accent); color:#06210f; }
/* Chrome/Edge/Safari render their own native up/down spin-button control
   inside a number input, with its own light, OS-drawn background that
   ignores both this input's dark theme and its border-radius — it pokes
   out past the input's rounded corner as a separate-looking grey box
   overlapping the border. Hiding it (still fully functional as a text
   field for typing a value, and the 5/30/60 presets already cover the
   common cases a spinner would be for) is the standard fix; the two
   -webkit-/-moz- rules plus the standard `appearance` property cover every
   browser this app needs to support. */
.rate-row input[type="number"]{ width:70px; flex:0 0 auto; appearance:textfield; -moz-appearance:textfield; }
.rate-row input[type="number"]::-webkit-outer-spin-button,
.rate-row input[type="number"]::-webkit-inner-spin-button{
  -webkit-appearance:none; margin:0;
}

/* ---------- Popups (Add ticker / API key setup) ---------- */
.popup-backdrop{
  display:flex; position: fixed; inset:0; background: rgba(4,5,7,0.6);
  -webkit-backdrop-filter: blur(6px) saturate(120%); backdrop-filter: blur(6px) saturate(120%);
  z-index: 120; align-items:center; justify-content:center; padding:20px;
  visibility:hidden; opacity:0; pointer-events:none;
  transition: opacity var(--t-med), visibility 0s linear var(--t-med);
}
.popup-backdrop.open{
  visibility:visible; opacity:1; pointer-events:auto;
  transition: opacity var(--t-med), visibility 0s linear 0s;
}
.popup-card{
  background: var(--panel); border:1px solid var(--panel-border); border-radius:12px;
  padding:18px 20px; width:100%; max-width:320px; box-shadow: var(--shadow-lg);
  transform: scale(.95) translateY(8px); opacity:0;
  transition: transform .24s var(--ease-spring), opacity .2s var(--ease);
}
.popup-backdrop.open .popup-card{ transform: scale(1) translateY(0); opacity:1; }
.popup-card h3{ margin:0 0 4px; font-size:15px; font-weight:700; }
.popup-sub{ color:var(--text-faint); font-size:11.5px; margin:0 0 14px; line-height:1.5; }
.popup-row{ display:flex; gap:8px; }
.popup-input{
  flex:1; min-width:0; background:var(--input-bg); border:1px solid var(--panel-border); color:var(--text);
  padding:8px 12px; border-radius:7px; font-size:13.5px; outline:none;
  transition: border-color var(--t-fast), box-shadow var(--t-fast);
}
.popup-input::placeholder{ color:var(--text-faint); }
.popup-input:focus{ border-color:#4a5563; box-shadow:0 0 0 3px rgba(34,197,94,.10); }
.popup-round-btn{
  background:var(--accent); color:#06210f; border:none; width:36px; height:36px; border-radius:50%;
  font-size:18px; font-weight:700; cursor:pointer; flex:0 0 auto; line-height:1;
  transition: filter var(--t-fast), transform var(--t-fast);
}
.popup-round-btn:hover{ filter:brightness(1.08); }
.popup-round-btn:active{ transform: scale(.94); }

/* First-run API key setup + the same guide reused inside the Help modal's
   "Getting your API key" section — a tight 3-step numbered list plus a
   button-styled link, kept deliberately short so it doesn't make the
   already-compact popup feel crowded. */
.popup-steps{
  margin: 0 0 12px; padding-left: 18px; color: var(--text-dim);
  font-size: 12px; line-height: 1.6;
}
.popup-steps li{ margin-bottom: 2px; }
.popup-link-btn{
  display:block; text-align:center; text-decoration:none;
  background: var(--accent); color:#06210f; font-weight:700; font-size:12.5px;
  padding:8px 12px; border-radius:7px; margin-bottom:14px;
  transition: filter var(--t-fast);
}
.popup-link-btn:hover{ filter:brightness(1.08); }

/* ---------- Help modal ----------
   Reuses .popup-backdrop for the fade/scale/click-outside behavior every
   other popup here already has, but .help-modal overrides .popup-card's
   compact sizing since it holds several sections of text — wider, and
   capped at a viewport-relative max-height with its own scrolling body so
   long content never gets clipped or forces the page itself to scroll. */
.popup-card.help-modal{
  max-width: 520px; max-height: 82vh; padding:0;
  display:flex; flex-direction:column;
}
.help-modal-header{
  display:flex; align-items:center; justify-content:space-between;
  padding: 14px 20px; border-bottom:1px solid var(--panel-border); flex:0 0 auto;
}
.help-modal-header h3{ margin:0; }
.help-modal-body{ overflow-y:auto; padding: 16px 20px 20px; flex:1 1 auto; }
.help-section{ margin-bottom:18px; }
.help-section:last-child{ margin-bottom:0; }
.help-section h4{
  margin:0 0 5px; font-size:11px; letter-spacing:.05em; text-transform:uppercase;
  color: var(--accent); font-weight:700;
}
.help-section p{ margin:0; color:var(--text-dim); font-size:12.5px; line-height:1.6; }
.help-disclaimer p{ color: var(--text-faint); font-size:11px; }
.help-section .popup-steps{ margin-top:6px; }
.help-section .popup-link-btn{ margin-top:8px; margin-bottom:0; max-width:220px; }

/* ---------- Table Layout & Fixed Sizing (Alignment Fix) ---------- */
.table-card{
  background: var(--panel); border:1px solid var(--panel-border); border-radius:12px;
  overflow:hidden; box-shadow: var(--shadow-md);
}
.table-scroll{ overflow-x:auto; }
table{ width:100%; border-collapse: collapse; table-layout: fixed; }
.table-scroll table{ min-width: 760px; }

/* Main table column widths configuration */
thead th.col-theme, tbody td.col-theme { width: 24%; text-align: left; }
thead th.col-n, tbody td.col-n { width: 7%; text-align: center; }
thead th[data-period], tbody td.num { width: 9%; text-align: right; }
/* The header and body cell are split (rather than sharing one selector)
   because they no longer share a text-align: the "TREND" label is centered
   to line up with the sparkline below it (.trend-inner uses
   justify-content: center — see that rule further down), while
   td.col-trend keeps text-align: right, which is vestigial today (the
   sparkline is an absolutely-positioned flex child, not aligned text) but
   harmless to leave as-is. */
thead th.col-trend { width: 14%; text-align: center; }
tbody td.col-trend { width: 14%; text-align: right; }

thead th{
  font-size: 11px; letter-spacing:.04em; color: var(--text-dim);
  font-weight:600; padding: 11px 14px; border-bottom: 1px solid var(--accent-line);
  background: var(--panel-2);
  cursor:pointer; user-select:none; white-space:nowrap;
  transition: color var(--t-fast), background-color var(--t-fast);
}
thead th.col-n{ color:var(--text-faint); cursor:default; }
thead th.col-trend{ cursor:default; }
/* Theme is the third non-sortable header (alongside Members/col-n and
   TREND/col-trend above) — it has no data-period and no click-to-sort
   handler (see the `thead th[data-period]` binding in bindUI, js/ui.js),
   so like those two it should never look clickable either. This was
   missed in the round that fixed Members/TREND (that round was scoped to
   a *different* bug — the nested per-theme ticker-list header — and never
   touched the main table's Theme column at all), which is why the hover
   rule below still caught it: :not(.col-n):not(.col-trend) never excluded
   .col-theme, so it fell through to the generic sortable-header hover. */
thead th.col-theme{ cursor:default; }
/* Hover highlight for the main table's clickable sort column headers only
   (1D/1W/.../YTD) — the child combinator (#themeTable > thead) is load-
   bearing here, not just tidiness: the nested per-theme ticker-list table
   (built in buildDetailRow) has no id of its own, but it lives INSIDE
   #themeTable's own tbody (a detail row's <td> holds it), so it is still a
   CSS descendant of #themeTable regardless of the extra <table> boundary in
   between — descendant selectors don't know or care about table-nesting
   semantics. A prior round scoped this rule to plain "#themeTable thead
   th:hover" believing the id alone would keep it out of the nested table;
   it does not — that selector still matched the nested header's Ticker/
   Last/period cells the whole time, which is exactly the bug reported
   again here. "#themeTable > thead" only matches the OUTER table's own
   <thead> (a genuine direct child of #themeTable), so it can never reach
   into a table nested arbitrarily deep inside its tbody, regardless of what
   classes that nested header's cells do or don't have — no growing list of
   :not() exceptions required to keep it out.
   :not(.col-n):not(.col-trend):not(.col-theme) — Theme, Members and TREND
   are the table's three non-sortable headers; only the data-period columns
   (1D/1W/1M/3M/6M/1Y/YTD) are meant to light up on hover. The nested
   ticker-list header (Ticker/Last/its own 1D-YTD/+) gets NONE of this
   hover — not even on its own period columns — just the plain
   cursor:pointer every thead th already has; see the "+Ticker" comment
   below for why sorting a theme's own member list intentionally reads as a
   much lower-emphasis action than sorting the whole table. */
#themeTable > thead th:hover:not(.col-n):not(.col-trend):not(.col-theme){
  color: var(--text); background: var(--row-hover);
}
thead th.sorted{ color: var(--accent); }
.sort-arrow{ font-size:10px; margin-left:3px; }

tbody tr.theme-row{
  border-bottom: 1px solid var(--accent-line); cursor:pointer;
}
tbody tr.theme-row:last-child{ border-bottom:none; }
/* Row hover/expanded tint is painted per-cell, not on the <tr> itself, and
   NEVER uses border-radius anywhere at the <tr>/<td> level — a plain
   rectangular tint needs no rounding to look right, and this file has
   already spent several rounds finding out how unreliable border-radius is
   on table cells under border-collapse:collapse.
   EVERY td gets this same plain grey, with NO exclusions for .sorted-col or
   TREND (td.col-trend) — earlier rounds tried excluding one or the other
   and both were wrong:
   - Excluding .sorted-col meant its <td> had no background of its own to
     show through .num-bg's rounded corners while hovering, so those
     corners revealed the plain (un-tinted) table background instead of the
     row's hover grey — a hard, dark-looking seam exactly at the corners of
     the highlight, right where neighboring hover-tinted cells expected to
     see the same grey. Including .sorted-col here fixes that: the td's own
     background is now the same grey as every other cell, and .num-bg's
     rounded, colored overlay simply sits on top of it — since .num-bg is a
     later-painted child of the td, it still fully covers its own area, so
     nothing about the sorted-column highlight's own look changes; only the
     small rounded-corner slivers around it now match the row instead of
     looking like a black notch.
   - Excluding TREND (to stop a *different*, now-fixed bug where its small
     fixed-size sparkline left visible empty tinted space around it) went
     too far the other way: TREND stopped getting any hover tint at all, so
     it stayed a flat, un-tinted block while the rest of a hovered row
     lightened — its own kind of visual mismatch. td.col-trend has no
     background of its own in any state (see below), so all it ever shows
     is this same shared grey, exactly like the Members column. */
tbody tr.theme-row:hover td,
tbody tr.theme-row.expanded td{
  background-color: var(--row-hover);
  transition: background-color var(--t-fast);
}

td{ padding: 11px 14px; font-size: 13.5px; font-weight:500; vertical-align: middle; white-space:nowrap; }
td.col-theme{ white-space:normal; }
td.col-n{ color: var(--text-faint); font-size:12.5px; font-weight:500; white-space:normal; }
td.num{ position:relative; font-variant-numeric: tabular-nums; font-weight:600; }
/* Not scoped to `td` — applyReturnStyle adds this class to whatever element
   it's given, which for every numeric cell now is .num-main/.num-bg (a
   <div>), never the <td> itself. */
.na{ color: var(--text-faint); font-weight:400; }

/* ---------- Sorted-column highlight: structural fix ----------
   Three earlier rounds (padding, z-index, hover-selector scoping) tried to
   fix "leaf tip" artifacts at the highlight's rounded corners, plus glitches
   at row boundaries near TREND, by tuning values on td.num.sorted-col
   itself. None of it held up, because the actual cause is that
   border-radius on a <td> inside a border-collapse:collapse table doesn't
   render reliably — browsers draw collapsed cell borders through/under the
   radius inconsistently across zoom levels and hover states. No amount of
   padding or z-index changes the fact that a <td> is the wrong element to
   carry that visual. td.num/td.num.sorted-col NEVER get a border-radius of
   their own, and the only background they ever carry is the same plain,
   un-rounded grey every other cell gets on row hover/expanded (see that
   rule above) — never a dedicated highlight color of their own.
   The highlight itself is split across two <div>s inside td.num (built in
   buildThemeRow/buildDetailRow):
   - .num-bg carries the background + border-radius. It's position:absolute
     with inset:0 against td.num (position:relative above), so it always
     fills the cell's full content box exactly, however tall the row ends
     up — this is what the earlier .num-inner-fills-via-height:100% attempt
     was trying (and unreliably failing) to achieve: percentage heights on a
     plain block descendant of a table cell resolve inconsistently, since
     the cell's own height is itself content-derived. Absolute positioning
     sidesteps that entirely — .num-bg reads the FINAL used size of its
     positioned ancestor, it doesn't need to participate in computing it.
   - .num-inner holds the actual text (.num-main + optional .alpha-tag) in
     normal flow, exactly as before, so it still drives the cell/row's
     natural height (needed so turning on the alpha two-line layout still
     grows the row) and keeps its own small padding for breathing room
     between the text and .num-bg's edges.
   Both remain ordinary <div>s — never part of the table's border-collapse
   model — so border-radius still always renders as a clean, simple rounded
   rect no matter the zoom level or row state. */
.num-bg{
  position:absolute; inset:0;
  border-radius:8px;
  pointer-events:none;
  transition: background-color var(--t-fast);
}
.num-inner{
  position:relative; /* paints above the earlier (also positioned) .num-bg */
  display:flex; flex-direction:column; align-items:flex-end; justify-content:center;
  width:100%;
}
/* Breathing room between the text and .num-bg's edges, only at the main
   table's scale — the compact member-row cells inside an expanded theme
   share the same markup but never show a background (member rows always
   pass isSortedCol=false), so they keep their existing tight rhythm. */
.theme-row td.num .num-inner{ padding: 6px 10px; }
.num-main{ transition: color var(--t-fast); }

/* Breadth tag ("· 75% up") embedded in the Members cell — secondary info,
   so it stays visually quiet next to the n/total count (spec 2b/2c): small,
   dim by default, and only lightly tinted green/red past the 50% mark. Never
   its own column, so it can't disturb the fixed Members column width. */
.breadth-tag{ font-size: 10.5px; font-weight:500; }
.breadth-tag.pos{ color: rgba(53,208,127,.8); }
.breadth-tag.neg{ color: rgba(255,90,90,.8); }

/* Relative Strength vs SPY (state.showAlpha, opt-in — spec 3d): when the
   toggle is on, this "α ±x.x" line is appended into .num-inner right after
   .num-main, deliberately smaller/dimmer so it never competes with the
   primary number above it. Off by default — the element is simply never
   created, so nothing here applies. */
.alpha-tag{ font-size: 10px; font-weight:500; opacity:.85; margin-top:1px; }

.theme-name-cell{ display:flex; align-items:center; gap:8px; }
.expand-caret{
  color: var(--text-faint); font-size:10px; width:10px; display:inline-block;
  transition: transform var(--t-med), color var(--t-fast);
}
.theme-row.expanded .expand-caret{ transform: rotate(90deg); color: var(--accent); }
.theme-name{ font-weight:600; letter-spacing:-0.005em; }
.theme-name-input{
  background: var(--input-bg); border:1px solid var(--accent-dim); color: var(--text);
  font-size:13.5px; font-weight:600; font-family:inherit; padding:2px 7px; border-radius:5px;
  outline:none; min-width:140px; box-shadow: 0 0 0 3px rgba(34,197,94,.12);
}
.badge-gaining{
  display:inline-flex; align-items:center; gap:4px; font-size:10.5px; font-weight:700;
  color: var(--green); border:1px solid rgba(53,208,127,.38); background: rgba(53,208,127,0.13);
  padding: 2.5px 9px 2.5px 7px; border-radius: 999px; letter-spacing:.03em; white-space:nowrap;
  box-shadow: 0 0 0 1px rgba(53,208,127,.07) inset;
}
.badge-custom{
  font-size:10px; color: var(--text-faint); border:1px solid var(--panel-border);
  background: rgba(255,255,255,0.02);
  padding:2.5px 8px; border-radius:999px; margin-left:2px;
}
.edit-theme, .del-theme{
  background:none; border:none; color: var(--text-faint); cursor:pointer; font-size:13px;
  padding: 2px 4px; border-radius:4px; visibility:hidden;
  transition: color var(--t-fast), background-color var(--t-fast), visibility var(--t-fast);
}
.theme-row:hover .edit-theme, .theme-row:hover .del-theme{ visibility:visible; }
.edit-theme:hover{ color: var(--accent); background: rgba(34,197,94,.10); }
.del-theme:hover{ color: var(--red); background: var(--red-bg); }

/* TREND column: this exact area has broken three times from unrelated
   fixes elsewhere (alpha's taller rows, the sorted-col hover rework, etc.)
   — every time because the old .trend-cell rule put `display:flex` directly
   on the <td>, which doesn't reliably stretch to the table row's actual
   computed height the way an ordinary table-cell does, leaving a gap where
   whatever's behind it (at the time, the row's un-tinted base) shows
   through as a stray dark stripe. td.col-trend now carries NO visual
   properties of its own at all — no background, border, or box-shadow, in
   any state — so all it can ever show is the row's shared hover grey
   (painted on it by the same rule every other td uses — see above). The
   sparkline is centered using .trend-inner instead, a plain <div>
   absolutely positioned to fill td.col-trend's exact rendered box (the
   same proven technique as .num-bg below): since it reads the cell's FINAL
   size rather than needing to compute/stretch to it, there is no longer
   any row-height mismatch left for a future change to reopen. */
td.col-trend{ position:relative; }
.trend-inner{
  position:absolute; inset:0;
  display:flex; justify-content:center; align-items:center;
}

/* ---------- Detail (members) panel alignment ---------- */
@keyframes detailIn{
  from{ opacity:0; transform: translateY(-5px); }
  to{ opacity:1; transform: translateY(0); }
}
/* Direct-child combinator (>) is deliberate: this only zeroes the OUTER
   wrapping <td colspan="10"> that holds .detail-wrap, so .detail-wrap's
   own full-bleed background/edges work. Without the ">", this descendant
   selector's specificity (0,1,2) — one class + two elements — beats every
   ".detail-wrap td" rule (0,1,1) below, which was silently zeroing the
   padding on every ticker/price/period cell inside the nested table too,
   collapsing the whole ticker-column indent back to 0. That was the real
   cause of "ticker text isn't aligned with the theme name" — not a value
   that needed increasing, but this selector leaking into cells it was
   never meant to touch. */
tr.detail-row > td{ padding: 0; }
.detail-wrap{
  padding: 4px 0 10px 0;
  background: var(--input-bg);
  box-shadow: inset 0 1px 0 var(--panel-border), inset 0 -1px 0 var(--panel-border);
}
.detail-wrap.detail-enter{ animation: detailIn .22s var(--ease); }

.detail-wrap table{
  width: 100%;
  table-layout: fixed;
}

/* Child table reuses the SAME percentage widths as the parent table's
   matching columns (theme→ticker, members→last, periods, trend→filler)
   so column boundaries line up pixel-for-pixel regardless of how the
   browser normalizes table-layout:fixed percentages — both tables are
   rendered at the same width and use the same per-column shares. */
.detail-wrap thead th.ticker-col, .detail-wrap td.ticker { width: 24%; text-align: left; }
.detail-wrap thead th.price-col, .detail-wrap td.price { width: 7%; text-align: right; color: var(--text-dim); }
.detail-wrap thead th[data-period], .detail-wrap td.num { width: 9%; text-align: right; }
.detail-wrap thead th.filler-col, .detail-wrap td.filler { width: 14%; }

.detail-wrap thead th{
  font-size: 10.5px;
  border-bottom: 1px solid var(--accent-line);
  padding: 6px 10px;
  background: transparent;
  transition: color var(--t-fast), background-color var(--t-fast);
}
/* Only the clickable period headers (1D–YTD, each carrying data-period —
   see buildDetailRow in js/ui.js) get the same grey hover the main table's
   sortable headers use (#themeTable > thead th:hover above). Ticker/Last/
   the trailing filler column stay hover-free since they aren't sortable —
   scoping to [data-period] achieves that with no :not() list to maintain. */
.detail-wrap thead th[data-period]:hover{
  color: var(--text); background: var(--row-hover);
}

.detail-wrap td{
  padding: 3.5px 10px;
  font-size: 12.5px;
  color: var(--text-dim);
}

/* Ticker cell: a fixed-width remove-button slot on the LEFT, then the
   symbol + dimmed company name. The slot is 22px and the cell keeps the
   base 10px left padding from ".detail-wrap td" above — 10 + 22 = 32px,
   which is exactly the theme-row name text's own indent (14px cell
   padding + 10px caret width + 8px caret-to-name gap — see
   .theme-name-cell below). So it's the SYMBOL, not the button, that lines
   up with the theme name above it. The button always occupies its slot
   (opacity toggles on row hover, never added/removed from the DOM), so
   nothing shifts when it appears, and it can never be mistaken for part
   of the ticker text since it sits to the left of it, not the right. */
td.ticker{ cursor: pointer; }
.ticker-cell-inner{ display:flex; align-items:center; }
.ticker-remove-slot{
  flex:0 0 22px; display:inline-flex; align-items:center; justify-content:center;
}
.ticker-symbol{
  font-weight: 600; color: var(--text); transition: color .12s; flex:0 0 auto; margin-right:8px;
}
td.ticker:hover .ticker-symbol{ color: var(--accent); text-decoration: underline; }
.ticker-company{
  flex:1 1 auto; min-width:0; overflow:hidden; text-overflow:ellipsis; white-space:nowrap;
  color: var(--text-faint); font-size:11px; font-weight:400; margin-right:8px;
}
.ticker-remove{
  background:none; border:none; color: var(--text-faint); cursor:pointer; font-size:12px;
  width:15px; height:15px; flex:0 0 auto; line-height:1; padding:0; border-radius:3px;
  opacity:0; transition: opacity var(--t-fast), color var(--t-fast), background-color var(--t-fast);
}
.detail-wrap tbody tr:hover .ticker-remove{ opacity:1; }
.ticker-remove:hover{ color: var(--red); background: var(--red-bg); }

.member-error{ color: var(--text-faint); font-style: italic; font-weight:400; }
/* "fetching…" placeholder for a ticker just added via fetchNewTickers
   (js/ui.js, state.fetchingTickers) — same faint/italic base as
   .member-error above, plus a slow opacity pulse so it visibly reads as
   in-progress rather than as the inert "no cached data" state it briefly
   replaces. */
@keyframes memberLoadingPulse{
  0%, 100%{ opacity:.4; }
  50%{ opacity:1; }
}
.member-loading{ animation: memberLoadingPulse 1.1s ease-in-out infinite; }

/* "+ Ticker" header: an icon immediately before the "Ticker" label, inside
   the ticker list's header row itself (not as a separate element under the
   list, which used to render at the wrong width in the wrong place).
   .ticker-header-inner is a flex row so the icon and label sit on one line
   without disturbing the <th>'s own table-cell layout (don't set
   display:flex directly on a th/td — that pulls it out of the table's
   column-sizing algorithm). The icon lives in a fixed 22px slot — the same
   width as .ticker-remove-slot on the rows below — so the SLOT's left edge
   (where the whole "+ Ticker" cluster starts) lines up exactly with the
   remove-button slot's left edge on the ticker rows underneath it,
   independent of the icon being there. A small gap after the slot then
   separates the icon from the "Ticker" label so they don't visually touch
   ("+ Ticker", not "+ticker") — this shifts the label a few px right of
   where the ticker symbol below it starts, which is fine: it's the
   cluster's left edge that has to stay aligned, not the label text itself.
   Only the icon itself gets a hover state; "Ticker" isn't a sort control.
   It pops in with a quick scale+fade whenever its panel is (re)built, i.e.
   whenever the theme is opened. */
@keyframes addBtnIn{
  from{ opacity:0; transform: scale(.7); }
  to{ opacity:1; transform: scale(1); }
}
.ticker-header-inner{ display:flex; align-items:center; gap:6px; }
.ticker-header-icon-slot{
  flex:0 0 22px; display:inline-flex; align-items:center; justify-content:center;
}
.add-ticker-btn{
  background:var(--panel-2); border:1px solid var(--panel-border); color:var(--text-dim);
  width:18px; height:18px; border-radius:50%; font-size:12px; cursor:pointer; line-height:1;
  display:inline-flex; align-items:center; justify-content:center; flex:0 0 auto;
  transition: background var(--t-fast), color var(--t-fast), border-color var(--t-fast), transform var(--t-fast);
  animation: addBtnIn .2s var(--ease-spring);
}
.add-ticker-btn:hover{ background:var(--row-hover); color:var(--accent); border-color:var(--accent-dim); transform: scale(1.1); }
.add-ticker-btn:active{ transform: scale(.92); }

/* ---------- Misc ---------- */
.empty-state{ padding: 60px 20px; text-align:center; color: var(--text-dim); }
.empty-state code{ background:var(--panel-2); padding:2px 6px; border-radius:4px; }
footer{ margin-top: 26px; color: var(--text-faint); font-size:11.5px; text-align:center; }
.footer-disclaimer{ margin-top:4px; font-size:11px; color: var(--text-faint); opacity:.75; }

/* ---------- Ticker chart modal ---------- */
.chart-modal-backdrop{
  display:flex; position: fixed; inset:0; background: rgba(4,5,7,0.72);
  -webkit-backdrop-filter: blur(7px) saturate(120%); backdrop-filter: blur(7px) saturate(120%);
  z-index: 100; align-items: center; justify-content: center; padding: 24px;
  visibility:hidden; opacity:0; pointer-events:none;
  transition: opacity var(--t-med), visibility 0s linear var(--t-med);
}
.chart-modal-backdrop.open{
  visibility:visible; opacity:1; pointer-events:auto;
  transition: opacity var(--t-med), visibility 0s linear 0s;
}
.chart-modal{
  background: var(--panel); border:1px solid var(--panel-border); border-radius: 14px;
  width: 100%; max-width: 760px; padding: 20px 22px 16px; box-shadow: var(--shadow-lg);
  transform: scale(.96) translateY(10px); opacity:0;
  transition: transform .28s var(--ease-spring), opacity .22s var(--ease);
}
.chart-modal-backdrop.open .chart-modal{ transform: scale(1) translateY(0); opacity:1; }
.chart-modal-header{ display:flex; justify-content:space-between; align-items:flex-start; margin-bottom: 10px; }
.chart-ticker-row{ display:flex; align-items:baseline; gap:10px; }
.chart-ticker{ font-size: 20px; font-weight:800; letter-spacing:-0.01em; }
.chart-price{ font-size: 16px; color: var(--text-dim); font-variant-numeric: tabular-nums; }
.chart-change{ font-size: 14px; font-weight:700; font-variant-numeric: tabular-nums; transition: color var(--t-fast); }
.chart-sub{ font-size: 12px; color: var(--text-faint); margin-top:2px; }
.chart-range-buttons{ width: max-content; margin-bottom: 12px; }
.chart-range-buttons .rank-btn{ padding: 5px 11px; font-size:12px; }
.chart-canvas-wrap{ width:100%; }
.chart-canvas-wrap svg{ width:100%; height:auto; display:block; }
.chart-empty{ padding: 50px 10px; text-align:center; color: var(--text-dim); font-size:13px; }
.chart-axis-label{ fill: var(--text-faint); font-size: 10px; font-family: inherit; }
.chart-gridline{ stroke: var(--accent-line); stroke-width: 1; }
.chart-crosshair-label{ fill: var(--text); font-size: 10.5px; font-family: inherit; }

/* ---------- Status toast (theme/ticker CRUD + settings feedback only —
   NOT used for cache/refresh status, which lives in the header). Fixed
   position so it floats above the page and never reserves layout space,
   even while visible; fades + slides in on show, then fades back out on
   its own (see setStatus() in js/ui.js). ---------- */
.status-line{
  position: fixed; top: 84px; right: 32px; z-index: 200; max-width: 340px;
  background: var(--panel); border:1px solid var(--panel-border); border-radius:8px;
  box-shadow: var(--shadow-md); padding: 9px 14px; font-size:12.5px; color:var(--text-dim);
  opacity:0; visibility:hidden; transform: translateY(-8px) scale(.98); pointer-events:none;
  transition: opacity var(--t-med), transform var(--t-med), visibility 0s linear var(--t-med), color var(--t-fast);
}
.status-line.show{
  opacity:1; visibility:visible; transform: translateY(0) scale(1); pointer-events:auto;
  transition: opacity var(--t-med), transform var(--t-med), visibility 0s linear 0s, color var(--t-fast);
}
.status-line.err{ color:var(--red); border-color:rgba(255,90,90,.35); }
