/* Fantasy God Mode — PWA prototype.
   Palette is locked to fantasymath-browser/content.css so the two surfaces
   read as one product: blue = actual/current, green = optimal, red = the
   actual-score marker. Navy+gold is reels-only; it does not appear here. */

:root {
  --blue: #1a73e8;
  --blue-fill: rgba(26, 115, 232, 0.25);
  --blue-stroke: rgba(26, 115, 232, 0.75);
  --green: #1e8e3e;
  --green-fill: rgba(30, 142, 62, 0.22);
  --green-stroke: rgba(30, 142, 62, 0.7);
  --red: #d93025;

  /* Other side: orange, matching the desktop extension's completed-scores
     overlay (.fm-spark-area-bot) so the two surfaces read as one product.
     Blue = you (or the away side in a neutral matchup), orange = the other
     lane. Blue/orange is the classic CVD-safe pair; the fill stays light so
     the red actual-line marker keeps its contrast on top of it. */
  --orange: #d97706;
  --orange-fill: rgba(234, 138, 0, 0.26);
  --orange-stroke: rgba(217, 119, 6, 0.7);

  --ink: #202124;
  --ink-body: #3c4043;
  --ink-2: #5f6368;
  --ink-3: #80868b;

  --surface: #ffffff;
  --page: #f1f3f4;
  --line: #e3e6e8;
  --line-strong: #dadce0;

  --lucky-bg: #e6f4ea;
  --unlucky-bg: #fce8e6;

  --pad: 16px;
  --radius: 12px;
  --shell: 430px;

  --sans: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
}

* { box-sizing: border-box; }

html { -webkit-text-size-adjust: 100%; }

body {
  margin: 0;
  background: var(--page);
  color: var(--ink-body);
  font-family: var(--sans);
  font-size: 14px;
  line-height: 1.45;
  -webkit-font-smoothing: antialiased;
}

/* Numbers are the content here, so they get the display role: tabular
   figures everywhere, so columns align down a 12-row list. */
.num { font-variant-numeric: tabular-nums; font-feature-settings: "tnum" 1; }

/* Box-score register: the vernacular this product borrows from. Used for
   every label, never for prose. */
.eyebrow {
  font-size: 10px;
  font-weight: 700;
  letter-spacing: 0.09em;
  text-transform: uppercase;
  color: var(--ink-3);
}

.shell {
  max-width: var(--shell);
  margin: 0 auto;
  min-height: 100vh;
  background: var(--page);
  position: relative;
}

/* Desktop: the phone column is the product; frame it rather than stretch it. */
@media (min-width: 600px) {
  body { background: #e8eaed; }
  .shell {
    box-shadow: 0 0 0 1px var(--line-strong), 0 8px 32px rgba(32, 33, 36, 0.12);
    min-height: 100vh;
  }
}

/* ── App bar ─────────────────────────────────────────────── */

.appbar {
  position: sticky;
  top: 0;
  z-index: 10;
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 12px var(--pad);
  background: rgba(255, 255, 255, 0.94);
  backdrop-filter: saturate(180%) blur(8px);
  border-bottom: 1px solid var(--line);
}

.appbar-title { font-size: 15px; font-weight: 700; color: var(--ink); letter-spacing: -0.01em; }
.appbar-sub { font-size: 11px; color: var(--ink-3); }
.appbar-main { flex: 1; min-width: 0; }
.appbar-main .appbar-title { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }

.iconbtn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 36px; height: 36px;
  flex: none;
  border: none;
  border-radius: 50%;
  background: transparent;
  color: var(--ink-2);
  font-size: 18px;
  cursor: pointer;
  text-decoration: none;
}
.iconbtn:hover { background: var(--page); }

.linkbtn {
  flex: none;
  font-size: 12px;
  font-weight: 600;
  color: var(--blue);
  text-decoration: none;
  padding: 6px 8px;
  border-radius: 6px;
}
.linkbtn:hover { background: #e8f0fe; }

.screen { padding: var(--pad) var(--pad) 84px; }

/* ── Hero: the thesis. A curve with the red line out on the tail is the
      most characteristic image this product owns, so it opens the app. ── */

.hero {
  background: var(--surface);
  border-radius: var(--radius);
  padding: 14px 14px 10px;
  box-shadow: 0 1px 2px rgba(60, 64, 67, 0.1), 0 1px 3px 1px rgba(60, 64, 67, 0.06);
}

.hero-head { display: flex; align-items: baseline; justify-content: space-between; gap: 8px; }
.hero-name {
  font-size: 19px;
  font-weight: 700;
  color: var(--ink);
  letter-spacing: -0.02em;
  line-height: 1.2;
  margin: 3px 0 2px;
}
.hero-ranks { font-size: 12px; color: var(--ink-2); }
.hero-ranks strong { color: var(--ink-body); font-weight: 600; }

.hero-luck {
  flex: none;
  font-size: 20px;
  font-weight: 700;
  letter-spacing: -0.02em;
  line-height: 1;
}

.hero-chartwrap { margin: 6px -4px 0; }

.hero-legend {
  display: flex;
  flex-wrap: wrap;
  gap: 4px 14px;
  padding: 2px 4px 0;
}
.hero-caption {
  font-size: 12.5px;
  line-height: 1.5;
  color: var(--ink-2);
  margin: 8px 2px 2px;
}
.hero-caption strong { color: var(--ink-body); font-weight: 600; }

/* Legend: identity is never color-alone — every swatch is labelled. */
.legend-item { display: inline-flex; align-items: center; gap: 5px; font-size: 11px; color: var(--ink-2); }
.legend-swatch { width: 12px; height: 8px; border-radius: 2px; flex: none; }
.legend-swatch.actual { background: var(--blue-fill); border: 1px solid var(--blue-stroke); }
.legend-swatch.optimal { background: var(--green-fill); border: 1px solid var(--green-stroke); }
.legend-swatch.opp { background: var(--orange-fill); border: 1px solid var(--orange-stroke); }
/* Best-lineup shows as a line on the matchup hero, so its legend swatch is a rule. */
.legend-swatch.optimal-line { height: 0; border-top: 2px solid var(--green-stroke); border-radius: 0; }
.legend-swatch.marker { height: 0; border-top: 2px dashed var(--red); border-radius: 0; }

/* ── Section headers ─────────────────────────────────────── */

.section-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  margin: 24px 2px 8px;
}

.segmented {
  display: inline-flex;
  background: #e4e7e9;
  border-radius: 8px;
  padding: 2px;
  gap: 2px;
}
.segmented button {
  border: none;
  background: transparent;
  font-family: inherit;
  font-size: 11px;
  font-weight: 600;
  color: var(--ink-2);
  padding: 5px 10px;
  border-radius: 6px;
  cursor: pointer;
}
.segmented button[aria-pressed="true"] {
  background: var(--surface);
  color: var(--ink);
  box-shadow: 0 1px 2px rgba(60, 64, 67, 0.2);
}

.fm-race-pick { cursor: pointer; }

/* League section switcher (Standings | Scores | Moves): the .segmented look as
   a nav bar — full width, equal thirds, a touch taller for the tap target.
   First element on each League-tab screen. */
.subnav { display: flex; margin-bottom: 14px; }
.subnav button { flex: 1; padding: 7px 10px; font-size: 12px; }

/* ── House scoring note (SCHEME_PLAN §3 A2) ────────────────
   The League hub's disclosure that this league's rules aren't exactly the ones
   its numbers were computed under. Deliberately quiet — the `.footnote` /
   `.fa-keepbox` register, not a `.banner`: it's a standing fact about the
   league, not something that just went wrong. */
.house-note { margin: -6px 2px 14px; }
.house-btn {
  display: block; width: 100%; border: none; background: none;
  font-family: inherit; font-size: 10.5px; color: var(--ink-3);
  line-height: 1.5; padding: 0; cursor: pointer; text-align: left;
}
.house-btn:hover { color: var(--ink-2); }
.house-btn b { font-weight: 700; }
.house-caret { display: inline-block; margin-left: 5px; font-size: 9.5px; line-height: 1; }
.house-btn.open .house-caret { transform: rotate(180deg); }

.house-list {
  margin: 6px 0 0;
  padding: 0 0 0 14px;
  font-size: 10.5px;
  color: var(--ink-3);
  line-height: 1.6;
}
/* A rule shape we have no words for prints its raw values, which can be long
   and unbreakable — wrap it rather than push the shell column sideways. */
.house-list li { overflow-wrap: anywhere; }
.house-list b { font-weight: 600; color: var(--ink-2); }

/* ── Standings list ──────────────────────────────────────── */

/* The luck axis legend. The rail under each row is a real chart, so it gets
   a real axis label — once, at the top, not per row. */
.rail-axis {
  display: grid;
  grid-template-columns: 1fr auto 1fr;
  align-items: center;
  gap: 6px;
  padding: 0 2px 6px;
  font-size: 9.5px;
  color: var(--ink-3);
  letter-spacing: 0.07em;
  text-transform: uppercase;
  font-weight: 700;
}
/* The rail spans the full row and is anchored at its centre, so the poles
   label the far edges and the zero label sits over the tick. */
.rail-axis span:first-child { text-align: left; }
.rail-axis span:last-child { text-align: right; }
.rail-axis em {
  font-style: normal;
  color: var(--ink-2);
  padding: 0 2px;
}

.rows {
  background: var(--surface);
  border-radius: var(--radius);
  overflow: hidden;
  box-shadow: 0 1px 2px rgba(60, 64, 67, 0.1), 0 1px 3px 1px rgba(60, 64, 67, 0.06);
}

.row {
  display: block;
  width: 100%;
  text-align: left;
  border: none;
  background: var(--surface);
  font-family: inherit;
  color: inherit;
  padding: 10px 12px 0;
  cursor: pointer;
  position: relative;
}
.row + .row { border-top: 1px solid var(--line); }
.row:hover { background: #f8f9fa; }
.row:active { background: var(--page); }

.row-top { display: flex; align-items: center; gap: 9px; }

.rank {
  flex: none;
  width: 20px;
  font-size: 12px;
  font-weight: 700;
  color: var(--ink-3);
  text-align: right;
}

.row-name {
  flex: 1;
  min-width: 0;
  font-size: 14px;
  font-weight: 600;
  color: var(--ink);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  letter-spacing: -0.01em;
}

.row-luck { flex: none; font-size: 13.5px; font-weight: 700; letter-spacing: -0.01em; }

.row-bottom {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
  padding-left: 26px;
  min-height: 30px;
}

/* Box-score setting: fixed columns line the stats up down the list (that's
   what separates them — no middots), and only the sorted-on stat is dark.
   Three stats plus a curve is tight at 390px, and this is what buys the room. */
.row-stats { display: flex; align-items: baseline; font-size: 11px; color: var(--ink-2); white-space: nowrap; overflow: hidden; }
.row-stats b { font-weight: 500; color: var(--ink-2); }
.row-stats i { font-style: normal; font-size: 9.5px; color: var(--ink-3); margin-left: 2px; }
.row-stats .st-rec { min-width: 44px; }
.row-stats .st-xw { min-width: 56px; }
.row-stats .st-on b { font-weight: 700; color: var(--ink); }

.row-mini { flex: none; line-height: 0; }

/* Signature: the luck rail is the row's bottom edge. Stacked down the list
   the zero ticks line up into a shared axis, so the standings list *is* the
   luck board — no second chart, no extra row. */
.row-rail { line-height: 0; margin: 6px -12px 0; }

.lucky { color: var(--green); }
.unlucky { color: var(--red); }
.neutral { color: var(--ink-3); }

/* ── Charts (shared) ─────────────────────────────────────── */

/* The svg viewBox is a coordinate system; CSS decides the pixels. The rail
   must span the full row or its zero tick won't line up with its neighbours. */
.fm-hero-chart { display: block; width: 100%; height: auto; }
.fm-rail { display: block; width: 100%; }
.fm-mini { display: block; }

/* Fill and stroke are separate paths so the curve is stroked but the closing
   baseline isn't — a stroked baseline reads as a data line to the panel edge. */
.fm-fill-actual { fill: var(--blue-fill); stroke: none; }
.fm-fill-optimal { fill: var(--green-fill); stroke: none; }
.fm-fill-opp { fill: var(--orange-fill); stroke: none; }
.fm-line-actual { fill: none; stroke: var(--blue-stroke); stroke-width: 1.25; stroke-linejoin: round; stroke-linecap: round; }
.fm-line-optimal { fill: none; stroke: var(--green-stroke); stroke-width: 1.5; stroke-linejoin: round; stroke-linecap: round; }
.fm-line-opp { fill: none; stroke: var(--orange-stroke); stroke-width: 1.25; stroke-linejoin: round; stroke-linecap: round; }
.fm-actual-line { stroke: var(--red); stroke-width: 1.5; stroke-dasharray: 2.5 1.5; }

/* The wordmark on the connect screen reuses the brand curve. */
.fm-area-actual { fill: var(--blue-fill); stroke: var(--blue-stroke); stroke-width: 1.25; }
.fm-hero-axis { stroke: var(--line-strong); stroke-width: 1; }
.fm-hero-tick { fill: var(--ink-3); font-size: 9.5px; font-weight: 600; font-variant-numeric: tabular-nums; }
/* Ridgeline lane labels: name above the curve, projected total big beside it. */
.fm-lane-name { fill: var(--ink-2); font-size: 10px; font-weight: 700; letter-spacing: 0.02em; text-transform: uppercase; }
.fm-lane-total-lg { fill: var(--ink); font-size: 17px; font-weight: 800; letter-spacing: -0.02em; font-variant-numeric: tabular-nums; }
.fm-lane-accent-lg { fill: var(--green); font-size: 12px; font-weight: 700; font-variant-numeric: tabular-nums; }
.fm-lane-total-lg.dim { fill: var(--ink-3); font-weight: 700; }
.fm-lane-accent-lg.dim { fill: var(--ink-3); font-weight: 600; }
.fm-lane-you { fill: var(--blue); font-weight: 800; }
.fm-hero-marker-label { fill: var(--red); font-size: 10px; font-weight: 700; font-variant-numeric: tabular-nums; }
.fm-rail-zero { stroke: var(--line-strong); stroke-width: 1; }
.fm-rail-lucky { fill: var(--green); opacity: 0.85; }
.fm-rail-unlucky { fill: var(--red); opacity: 0.85; }

/* ── Team detail ─────────────────────────────────────────── */

.card {
  background: var(--surface);
  border-radius: var(--radius);
  box-shadow: 0 1px 2px rgba(60, 64, 67, 0.1), 0 1px 3px 1px rgba(60, 64, 67, 0.06);
}

.chart-card { padding: 12px 12px 8px; }
.chart-title { font-size: 13px; font-weight: 700; color: var(--ink); margin-bottom: 2px; }
.chart-sub { font-size: 11.5px; color: var(--ink-2); line-height: 1.45; margin-bottom: 6px; }

/* The two gaps this chart exists to show, stated as numbers under it. */
.decomp { display: flex; border-top: 1px solid var(--line); margin-top: 6px; }
.decomp-item { flex: 1; padding: 9px 4px 4px; text-align: center; }
.decomp-item + .decomp-item { border-left: 1px solid var(--line); }
.decomp-val { font-size: 15px; font-weight: 700; letter-spacing: -0.02em; line-height: 1.2; }
.decomp-label { font-size: 10px; color: var(--ink-3); margin-top: 1px; }

.tiles { display: grid; grid-template-columns: 1fr 1fr; gap: 8px; margin-top: 10px; }
.tile { background: var(--surface); border-radius: 10px; padding: 10px 11px; box-shadow: 0 1px 2px rgba(60, 64, 67, 0.1); }
.tile-val { font-size: 20px; font-weight: 700; color: var(--ink); letter-spacing: -0.03em; line-height: 1.15; }
.tile-label { margin-top: 1px; }
.tile-rank { font-size: 10.5px; color: var(--ink-3); margin-top: 3px; font-weight: 600; }

/* ── Blunders ────────────────────────────────────────────── */

.blunder-lede { padding: 11px 12px; border-bottom: 1px solid var(--line); }
.blunder-lede-val { font-size: 20px; font-weight: 700; letter-spacing: -0.03em; color: var(--ink); }
.blunder-lede p { margin: 3px 0 0; font-size: 11.5px; color: var(--ink-2); line-height: 1.45; }

.blunder {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 9px 12px;
}
.blunder + .blunder { border-top: 1px solid var(--line); }

.blunder-wk {
  flex: none;
  width: 30px;
  text-align: center;
  font-size: 9.5px;
  font-weight: 700;
  color: var(--ink-3);
  letter-spacing: 0.04em;
}
.blunder-wk b { display: block; font-size: 13px; color: var(--ink-body); letter-spacing: -0.02em; }

.blunder-body { flex: 1; min-width: 0; font-size: 12.5px; line-height: 1.4; }
.blunder-line { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; color: var(--ink-2); }
.blunder-line .name { font-weight: 600; color: var(--ink); }

.blunder-pts {
  display: flex;
  align-items: center;
  gap: 6px;
  font-size: 11px;
  color: var(--ink-3);
  font-variant-numeric: tabular-nums;
}
/* Nearly half these calls outscored the alternative. Marking them is the
   model's credibility, not a caveat to hide. */
.chip-worked {
  font-size: 9px;
  font-weight: 700;
  letter-spacing: 0.04em;
  color: var(--ink-2);
  background: var(--page);
  border-radius: 3px;
  padding: 1px 5px;
}

.blunder-cost {
  flex: none;
  text-align: right;
  font-size: 13px;
  font-weight: 700;
  color: var(--red);
  letter-spacing: -0.01em;
}
.blunder-cost small { display: block; font-size: 9px; font-weight: 700; color: var(--ink-3); letter-spacing: 0.05em; }

.pos-chip {
  display: inline-block;
  font-size: 9px;
  font-weight: 700;
  letter-spacing: 0.05em;
  color: var(--ink-2);
  background: var(--page);
  border-radius: 3px;
  padding: 1px 4px;
  margin-right: 5px;
  vertical-align: 1px;
}

/* ── Connect (mock) ──────────────────────────────────────── */

.connect { padding: 28px var(--pad) 40px; }
.connect-mark { display: flex; justify-content: center; margin-bottom: 4px; }
.connect h1 {
  font-size: 26px;
  font-weight: 700;
  color: var(--ink);
  letter-spacing: -0.03em;
  line-height: 1.15;
  margin: 10px 0 6px;
  text-align: center;
}
.connect-lede { font-size: 13.5px; color: var(--ink-2); text-align: center; margin: 0 0 22px; line-height: 1.5; }

/* First-visit framing, under the lede: what you get and what it costs. Set on a
   card so it reads as the landing copy it is, not as more form chrome. */
.connect-intro {
  margin: -8px 0 22px;
  padding: 12px 13px;
  background: var(--surface);
  border-radius: 10px;
  font-size: 12.5px;
  color: var(--ink-2);
  line-height: 1.55;
  box-shadow: 0 1px 2px rgba(60, 64, 67, 0.1), 0 1px 3px 1px rgba(60, 64, 67, 0.06);
}

.field-label { display: block; margin-bottom: 7px; }

.platforms { display: grid; grid-template-columns: 1fr 1fr; gap: 8px; }
.platform {
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 2px;
  padding: 11px 12px;
  border: 1px solid var(--line-strong);
  border-radius: 10px;
  background: var(--surface);
  font-family: inherit;
  cursor: pointer;
  text-align: left;
}
.platform[aria-pressed="true"] { border-color: var(--blue); box-shadow: inset 0 0 0 1px var(--blue); background: #f6faff; }
.platform-name { font-size: 13.5px; font-weight: 600; color: var(--ink); }
.platform-note { font-size: 10px; color: var(--ink-3); }
.platform[data-mode="extension"] .platform-name { color: var(--ink-2); }

.input {
  width: 100%;
  padding: 11px 12px;
  border: 1px solid var(--line-strong);
  border-radius: 10px;
  font-family: inherit;
  font-size: 15px;
  color: var(--ink);
  background: var(--surface);
  font-variant-numeric: tabular-nums;
}
.input:focus { outline: none; border-color: var(--blue); box-shadow: inset 0 0 0 1px var(--blue); }

.btn {
  display: block;
  width: 100%;
  margin-top: 14px;
  padding: 13px;
  border: none;
  border-radius: 10px;
  background: var(--blue);
  color: #fff;
  font-family: inherit;
  font-size: 15px;
  font-weight: 600;
  cursor: pointer;
}
.btn:hover { background: #1765cc; }
.btn:disabled { background: #c5d5ea; cursor: default; }

.skip { display: block; text-align: center; margin-top: 16px; font-size: 12.5px; color: var(--blue); font-weight: 600; text-decoration: none; }
.skip:hover { text-decoration: underline; }

.field + .field { margin-top: 18px; }

/* Pulling state: the curve draws itself as weeks land — the brand form as
   the loading indicator, and the one motion moment in the app. */
.pulling { text-align: center; padding: 40px 8px; }
.pulling-chart { margin: 0 auto 18px; }
.pulling-week { font-size: 26px; font-weight: 700; color: var(--ink); letter-spacing: -0.03em; }
.pulling-note { font-size: 12.5px; color: var(--ink-2); margin-top: 4px; }
.pulling-track { height: 3px; background: var(--line); border-radius: 2px; margin: 16px auto 0; max-width: 220px; overflow: hidden; }
.pulling-fill { height: 100%; background: var(--blue); border-radius: 2px; transition: width 0.28s ease; }

.state { padding: 48px 20px; text-align: center; color: var(--ink-2); font-size: 13.5px; }

/* ── Onboarding progress (#/connect, ONBOARD_PLAN §6.3) ───
   One row per (season × artifact) job, in the list idiom the rest of the app
   already uses. The dot carries the state: grey = waiting, blue + pulse =
   working, green = done, red = failed — the same green/red the luck surfaces
   use, so "done" reads the same everywhere. */
.ob-lede { margin: 4px 2px 16px; font-size: 13px; color: var(--ink-2); line-height: 1.5; }
.ob-track { max-width: none; margin: 0 0 6px; }
.ob-status { font-size: 12px; color: var(--ink-2); margin-bottom: 14px; }
.ob-card { padding: 10px 12px 6px; }
.ob-row { display: flex; align-items: center; gap: 8px; padding: 6px 2px; }
.ob-row + .ob-row { border-top: 1px solid var(--line); }
.ob-dot { flex: none; width: 7px; height: 7px; border-radius: 50%; background: var(--line-strong); }
.ob-label { flex: 1; min-width: 0; font-size: 13px; color: var(--ink);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.ob-state { flex: none; font-size: 11px; color: var(--ink-3); text-align: right; }
.ob-running .ob-dot { background: var(--blue); animation: ob-pulse 1.4s ease-in-out infinite; }
.ob-running .ob-state { color: var(--blue); font-weight: 600; }
.ob-done .ob-dot { background: var(--green); }
.ob-skipped .ob-dot { background: var(--line-strong); }
.ob-error .ob-dot { background: var(--red); }
.ob-error .ob-state { color: var(--red); }
@keyframes ob-pulse { 0%, 100% { opacity: 1; } 50% { opacity: 0.35; } }
@media (prefers-reduced-motion: reduce) { .ob-running .ob-dot { animation: none; } }
.ob-err { background: var(--unlucky-bg); color: #9b1c14; }
.ob-ready { margin: 14px 2px 10px; font-size: 12.5px; color: var(--ink-2); line-height: 1.5; }
/* Owner name beside a team in the "which team is yours?" picker. */
.pick-owner { flex: none; font-size: 11.5px; color: var(--ink-3); text-align: right;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis; max-width: 45%; }

.banner {
  display: flex;
  gap: 8px;
  align-items: baseline;
  margin: 0 0 12px;
  padding: 9px 11px;
  background: #fef7e0;
  border-radius: 8px;
  font-size: 11.5px;
  color: #7a5900;
  line-height: 1.45;
}
.banner b { font-weight: 700; }

.footnote { margin: 14px 2px 0; font-size: 10.5px; color: var(--ink-3); line-height: 1.5; }

.fade-in { animation: fade-in 0.28s ease both; }
@keyframes fade-in { from { opacity: 0; transform: translateY(4px); } to { opacity: 1; transform: none; } }

:focus-visible { outline: 2px solid var(--blue); outline-offset: 2px; }

/* ── Bottom nav ───────────────────────────────────────────
   Four tabs. Fixed to the shell column, not the viewport, so it tracks the
   phone frame on desktop and spans the screen on mobile. */
.tabbar {
  position: fixed;
  bottom: 0;
  left: 50%;
  transform: translateX(-50%);
  width: 100%;
  max-width: var(--shell);
  z-index: 20;
  display: grid;
  grid-auto-flow: column;
  grid-auto-columns: 1fr;
  background: rgba(255, 255, 255, 0.94);
  backdrop-filter: saturate(180%) blur(8px);
  border-top: 1px solid var(--line);
  padding-bottom: env(safe-area-inset-bottom);
}
.tab {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 3px;
  padding: 8px 0 9px;
  text-decoration: none;
  color: var(--ink-3);
  font-size: 10px;
  font-weight: 700;
  letter-spacing: 0.03em;
}
.tab-ico {
  width: 22px; height: 22px;
  fill: none;
  stroke: currentColor;
  stroke-width: 2;
  stroke-linecap: round;
  stroke-linejoin: round;
}
.tab[aria-current="page"] { color: var(--blue); }

/* ── Sample chip ──────────────────────────────────────────
   Shown only when the session asked for the bundled league (`?league=sample`).
   Pinned just above the tab bar inside the shell column — the screen already
   reserves 84px of bottom padding for the nav, so it lands in that slack. */
.samplechip {
  position: fixed;
  bottom: calc(56px + env(safe-area-inset-bottom) + 8px);
  left: 50%;
  transform: translateX(-50%);
  z-index: 25;
  display: flex;
  align-items: baseline;
  gap: 6px;
  max-width: calc(var(--shell) - 2 * var(--pad));
  padding: 6px 12px;
  border: 1px solid var(--line-strong);
  border-radius: 999px;
  background: rgba(255, 255, 255, 0.96);
  backdrop-filter: saturate(180%) blur(8px);
  box-shadow: 0 2px 8px rgba(60, 64, 67, 0.16);
  font-size: 11.5px;
  color: var(--ink-2);
  text-decoration: none;
  white-space: nowrap;
}
.samplechip b { font-weight: 700; color: var(--ink); }
.samplechip span { color: var(--blue); font-weight: 600; }
.samplechip[hidden] { display: none; }

/* ── Lineup hero ──────────────────────────────────────────
   The win-probability number leads; the current-vs-optimal curve and the two
   projected totals sit under it. */
.lu-hero { padding: 14px 14px 4px; }
/* Eyebrow row: the "Win probability" label with a quiet cross-link to the full
   neutral matchup view sitting opposite it. */
.lu-hero-eyebrow { display: flex; align-items: baseline; justify-content: space-between; gap: 8px; }
.lu-fullmatchup { flex: none; font-size: 11px; font-weight: 600; color: var(--blue); text-decoration: none; }
.lu-fullmatchup:hover { text-decoration: underline; }
.lu-wp { display: flex; align-items: baseline; gap: 11px; margin: 3px 0 2px; }
.lu-wp-num {
  font-size: 46px;
  font-weight: 800;
  letter-spacing: -0.03em;
  color: var(--ink);
  line-height: 0.9;
}
.lu-wp-cap { font-size: 13px; color: var(--ink-2); line-height: 1.25; }
.lu-wp-cap strong { color: var(--ink); font-weight: 700; }

.decomp-val.optimal-val { color: var(--green); }

/* The product's one-liner: "Best lineup gets you to 43%", or the clean-lineup ✓. */
.lu-headline {
  margin-top: 8px;
  padding: 9px 4px 6px;
  border-top: 1px solid var(--line);
  text-align: center;
  font-size: 13.5px;
  font-weight: 600;
}
.lu-headline strong { font-weight: 800; }
.lu-headline.up { color: var(--green); }
.lu-headline.ok { color: var(--ink-2); }
.lu-headline.warn { color: #a1580a; }   /* a starter has no projection */

/* ── Lineup / bench / opponent rows ───────────────────────
   Slot eyebrow · name + NFL team + hint · per-player density mini · projection. */
.lu-card { overflow: hidden; }
.lu-row {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 9px 12px;
}
.lu-row + .lu-row { border-top: 1px solid var(--line); }
.lu-slot { flex: none; width: 30px; color: var(--ink-3); }
.lu-main { flex: 1; min-width: 0; }
.lu-name {
  font-size: 13.5px;
  font-weight: 600;
  color: var(--ink);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  letter-spacing: -0.01em;
}
.lu-team { font-size: 10px; font-weight: 600; color: var(--ink-3); margin-left: 5px; letter-spacing: 0.02em; }
.lu-subhint { font-size: 10.5px; color: var(--ink-2); margin-top: 1px; }
.lu-subhint.up { color: var(--green); font-weight: 700; }
.lu-subhint.sub { color: var(--red); }
.lu-subhint.muted { color: var(--ink-3); font-style: italic; }
.lu-mini { flex: none; width: 74px; line-height: 0; }
.lu-num { flex: none; width: 48px; text-align: right; font-size: 14px; font-weight: 700; color: var(--ink); }
.lu-num.lu-na { color: var(--ink-3); font-weight: 600; }
.lu-lock { font-size: 9px; margin-right: 2px; }

/* Swap tints: a starter to sit (red edge) and its bench upgrade (green edge).
   Colour is never the only cue — the edge, the sub-line text, and the
   recommendations card all carry the same pairing. */
.lu-tint-sub { box-shadow: inset 3px 0 0 var(--red); background: var(--unlucky-bg); }
.lu-tint-up { box-shadow: inset 3px 0 0 var(--green); background: var(--lucky-bg); }
.lu-dim { opacity: 0.7; }

/* ── Recommendations (inverted blunder rows) ─────────────── */
.rec-ico {
  flex: none;
  width: 30px;
  text-align: center;
  font-size: 17px;
  font-weight: 800;
  color: var(--green);
}
.blunder-cost.rec-gain { color: var(--green); }

/* ── Opponent (collapsed) ─────────────────────────────────── */
.opp {
  margin-top: 18px;
  background: var(--surface);
  border-radius: var(--radius);
  box-shadow: 0 1px 2px rgba(60, 64, 67, 0.1), 0 1px 3px 1px rgba(60, 64, 67, 0.06);
  overflow: hidden;
}
.opp-summary {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 12px 14px;
  cursor: pointer;
  list-style: none;
}
.opp-summary::-webkit-details-marker { display: none; }
.opp-name {
  flex: 1; min-width: 0;
  font-size: 13.5px; font-weight: 700; color: var(--ink);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.opp-meta { flex: none; font-size: 11px; color: var(--ink-2); }
.opp-chev { flex: none; color: var(--ink-3); transition: transform 0.2s ease; }
.opp[open] .opp-chev { transform: rotate(180deg); }
.opp-body { border-top: 1px solid var(--line); }

/* ── Appbar team switcher ─────────────────────────────────── */
.switcher {
  flex: none;
  display: inline-flex;
  align-items: center;
  gap: 3px;
  max-width: 132px;
  padding: 5px 9px;
  border-radius: 8px;
  background: var(--page);
  color: var(--ink-body);
  font-size: 11.5px;
  font-weight: 600;
  text-decoration: none;
}
.switcher:hover { background: #e8eaed; }
.switcher-name { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.switcher-chev { flex: none; color: var(--ink-3); font-size: 10px; }

/* ── League switcher (#/connect "Your leagues") ───────────── */
.league-row { display: flex; align-items: stretch; background: var(--surface); }
.league-row + .league-row { border-top: 1px solid var(--line); }
.league-row .pick-row { flex: 1; min-width: 0; }
.league-row .pick-row + .pick-row { border-top: none; }
.league-remove {
  flex: none;
  border: none; background: none; cursor: pointer;
  font-family: inherit; font-size: 13px; color: var(--ink-3);
  padding: 0 14px;
}
.league-remove:hover { color: var(--ink); background: #f8f9fa; }

/* ── Team picker ──────────────────────────────────────────── */
.pick-lede { font-size: 13.5px; color: var(--ink-2); margin: 4px 2px 14px; line-height: 1.5; }
.pick-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  width: 100%;
  text-align: left;
  border: none;
  background: var(--surface);
  font-family: inherit;
  color: inherit;
  padding: 13px 14px;
  cursor: pointer;
}
.pick-row + .pick-row { border-top: 1px solid var(--line); }
.pick-row:hover { background: #f8f9fa; }
.pick-row:active { background: var(--page); }
.pick-row.pick-current { background: #f6faff; box-shadow: inset 3px 0 0 var(--blue); }
.pick-name {
  min-width: 0;
  font-size: 14px; font-weight: 600; color: var(--ink);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.pick-you {
  margin-left: 8px;
  font-size: 9px; font-weight: 700; letter-spacing: 0.05em; text-transform: uppercase;
  color: var(--blue); background: #e8f0fe; border-radius: 3px; padding: 1px 5px;
  vertical-align: 1px;
}
.pick-wp { flex: none; font-size: 14px; font-weight: 700; color: var(--ink-2); text-align: right; }
.pick-wp small {
  display: block;
  font-size: 8.5px; font-weight: 700; color: var(--ink-3);
  letter-spacing: 0.05em; text-transform: uppercase;
}

/* ── Scores (pre-game scoreboard) ─────────────────────────
   One tappable card per matchup: two team lines (win prob leads, projected
   total quiet) over a compact two-lane ridgeline. */
.sb-list { display: flex; flex-direction: column; gap: 12px; }

.sb-card {
  display: block;
  width: 100%;
  text-align: left;
  border: none;
  background: var(--surface);
  border-radius: var(--radius);
  padding: 12px 14px 8px;
  font-family: inherit;
  color: inherit;
  cursor: pointer;
  box-shadow: 0 1px 2px rgba(60, 64, 67, 0.1), 0 1px 3px 1px rgba(60, 64, 67, 0.06);
}
.sb-card:hover { background: #f8f9fa; }
.sb-card:active { background: var(--page); }

.sb-teams { display: flex; flex-direction: column; gap: 3px; }
.sb-team { display: flex; align-items: baseline; gap: 8px; }
.sb-name-wrap { flex: 1; min-width: 0; display: flex; align-items: baseline; gap: 6px; }
.sb-name {
  min-width: 0;
  font-size: 14.5px; font-weight: 700; color: var(--ink);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
  letter-spacing: -0.01em;
}
.sb-proj { flex: none; font-size: 12px; font-weight: 600; color: var(--ink-3); }
/* Win prob is the number the row exists for. */
.sb-wp { flex: none; width: 46px; text-align: right; font-size: 19px; font-weight: 800; color: var(--ink); letter-spacing: -0.02em; }

/* Favorite full-weight, underdog quietly muted — type carries the odds. */
.sb-team.dog .sb-name { color: var(--ink-2); font-weight: 600; }
.sb-team.dog .sb-proj { color: var(--ink-3); }
.sb-team.dog .sb-wp { color: var(--ink-3); font-weight: 700; }

.sb-chart { margin: 4px -4px 0; line-height: 0; }

/* ── Matchup detail (both rosters) ────────────────────────
   Per-team section header: name (+ You) on the left, projected total on the
   right, above the plain lineup rows. Bench collapses under the .opp pattern. */
.mu-head { margin: 20px 2px 8px; }
.mu-head-left { flex: 1; min-width: 0; display: flex; align-items: baseline; gap: 6px; }
.mu-team-name {
  min-width: 0;
  font-size: 15px; font-weight: 700; color: var(--ink);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
  letter-spacing: -0.01em;
}
.mu-head-proj { flex: none; font-size: 15px; font-weight: 700; color: var(--ink); }
.mu-head-proj small {
  font-size: 9px; font-weight: 700; color: var(--ink-3);
  letter-spacing: 0.05em; text-transform: uppercase; margin-left: 3px;
}
.mu-bench { margin-top: 8px; }
.mu-head-proj.mu-lost { color: var(--ink-3); font-weight: 600; }

/* ── Week nav strip (Scores timeline) ─────────────────────
   One continuous timeline: ‹ steps to the older week, › to the newer, capped at
   the current live week on the right. Dumb prev/next over a flat list. */
.wknav {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
  margin: 0 0 12px;
  padding: 2px 2px 10px;
  border-bottom: 1px solid var(--line);
}
.wknav-arm {
  flex: none;
  display: inline-flex; align-items: center; justify-content: center;
  width: 34px; height: 34px;
  border-radius: 8px;
  background: var(--page);
  color: var(--ink-2);
  font-size: 19px; font-weight: 700;
  line-height: 1;
  text-decoration: none;
}
.wknav-arm:hover { background: #e8eaed; }
.wknav-arm.disabled { color: var(--line-strong); background: transparent; cursor: default; pointer-events: none; }
.wknav-mid { display: flex; align-items: center; gap: 6px; }

/* Native selects dressed as the strip's text — a chevron hints they open the
   OS picker. Arrows step, dropdowns jump. */
.wknav-sel {
  appearance: none;
  -webkit-appearance: none;
  border: none;
  font-family: inherit;
  cursor: pointer;
  background: var(--page) url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='8' height='5'%3E%3Cpath d='M0 0l4 5 4-5z' fill='%2380868b'/%3E%3C/svg%3E") no-repeat right 8px center;
  border-radius: 8px;
  padding: 6px 22px 6px 10px;
}
.wknav-sel-wk { font-size: 14px; font-weight: 700; color: var(--ink); letter-spacing: -0.01em; }
.wknav-sel-yr { font-size: 13px; font-weight: 600; color: var(--ink-2); }

/* ── Retro (completed-week) treatments ────────────────────
   The retro scoreboard card is the same ridgeline card; these style the pieces
   the card and matchup detail add: the pre-game hook line, the neutral
   percentile sub-numbers, and the no-chart fallback row. Red stays reserved for
   the actual-score line only; percentile is always neutral ink (CVD rule). */

/* Retro matchup hero: the quiet "you had 39% and won" hook under the ridgeline. */
.mu-pregame {
  margin-top: 6px;
  padding: 8px 4px 4px;
  border-top: 1px solid var(--line);
  text-align: center;
  font-size: 12.5px;
  color: var(--ink-2);
}
.mu-pregame strong { color: var(--ink); font-weight: 800; }

/* Retro player row: the percentile the actual landed at — small, neutral, under
   the score. The red mini line already carries lucky/unlucky spatially. */
.lu-num small.lu-pct {
  display: block;
  font-size: 9px; font-weight: 600; color: var(--ink-3);
  letter-spacing: 0.02em;
  font-variant-numeric: tabular-nums;
}

/* Retro scoreboard fallback (no chart): percentile quiet, actual prominent. */
.sb-pct { flex: none; width: 40px; text-align: right; font-size: 12px; font-weight: 600; color: var(--ink-3); }

/* ── Team-week retro + per-week luck strip (2026-07-21) ─────
   One team's story of a played week, and the season page's per-week luck strip.
   Red stays the actual-score line only; percentile stays neutral ink; luck reads
   by sign + position first, colour only reinforcing (the CVD rule). */

/* Matchup detail: the per-team header link into that team's week retro (V2). */
.mu-week-link {
  flex: none; align-self: baseline;
  font-size: 11px; font-weight: 600; color: var(--blue);
  text-decoration: none; white-space: nowrap;
}
.mu-week-link:hover { text-decoration: underline; }

/* Team-week hero */
.tw-hero { padding: 14px 14px 12px; }
/* Degraded (summary-only) hero: no curve, so the numbers carry it. */
.tw-bignum { display: flex; align-items: baseline; gap: 12px; margin: 10px 2px 2px; }
.tw-actual { font-size: 40px; font-weight: 800; color: var(--ink); letter-spacing: -0.03em; line-height: 1; }
.tw-pct { display: flex; flex-direction: column; line-height: 1.15; }
.tw-pct .num { font-size: 17px; font-weight: 700; color: var(--ink-2); }
.tw-pct-lab { font-size: 9px; font-weight: 700; letter-spacing: 0.08em; text-transform: uppercase; color: var(--ink-3); }
.tw-summary { margin: 9px 2px 0; font-size: 13px; color: var(--ink-body); line-height: 1.5; }
.tw-summary strong { color: var(--ink); font-weight: 800; }
.tw-luck { margin: 3px 2px 0; font-size: 17px; font-weight: 800; letter-spacing: -0.01em; }
.tw-luck-exp { font-size: 12px; font-weight: 600; color: var(--ink-3); letter-spacing: 0; }

/* Per-week luck strip: a column of centre-anchored diverging bars on one shared
   zero axis. W/L is a small letter under the week number (never the bar colour),
   so a lucky loss and an unlucky win are both visible. */
.wkstrip-card { padding: 12px 8px 10px; }
.wkstrip-chart { position: relative; display: flex; align-items: flex-start; gap: 2px; padding: 0 2px; }
/* The shared zero axis, drawn once at the centre of the 56px bar band so a row
   of centre-anchored bars reads against one continuous line. */
.wkstrip-zero { position: absolute; left: 2px; right: 2px; top: 28px; height: 1px; background: var(--line-strong); }
.wkstrip-div { flex: none; width: 1px; height: 56px; margin: 0 3px; background: var(--line-strong); }
.wkcol {
  position: relative; z-index: 1;
  flex: 1 1 0; min-width: 0;
  display: flex; flex-direction: column; align-items: center;
  text-decoration: none; border-radius: 4px; padding-bottom: 2px;
}
.wkcol:hover { background: #eef1f3; }
.wkcol-bar { width: 100%; height: 56px; line-height: 0; }
.fm-lbar { display: block; width: 100%; height: 100%; }
.wkcol-num { margin-top: 4px; font-size: 10px; font-weight: 700; color: var(--ink-2); }
.wkcol-res { font-size: 9px; font-weight: 800; letter-spacing: 0.03em; line-height: 1.2; }
.wkcol-res.win { color: var(--ink); }
.wkcol-res.loss { color: var(--ink-3); font-weight: 700; }
.wkstrip-note { margin-top: 12px; }
.wkstrip-note b { color: var(--ink-body); font-weight: 700; }

/* Rest-of-season card: the forward twin of the luck strip. Reuses the tile/num
   register; the strip is plain DOM (div bars), NOT the luck strip's SVG chart —
   no lucky/unlucky green/red, since nothing has been played. */
.roscard { padding: 12px 10px 10px; }
.roscard-sub { margin: 0 0 10px; font-size: 12px; color: var(--ink-2); }
.roscard-lede { margin-bottom: 14px; }
.roscard-lede-val { font-size: 26px; font-weight: 700; color: var(--ink); letter-spacing: -0.03em; line-height: 1.1; }
.roscard-lede .tile-label { margin-top: 2px; }
.roscard-lede-detail { margin-top: 5px; font-size: 12px; color: var(--ink-2); }
/* The strip: one column per remaining week, bars diverging from a 50% midline.
   Deterministic geometry so the shared zero line lands exactly on the bar band's
   centre: no chart padding-top, a FIXED 14px value row, a 56px band whose midline
   is 28px in — so the line sits at 14 + 28 = 42px (matches the JS fill top MID=28
   measured inside the 56px band). */
.roscol-chart { position: relative; display: flex; align-items: flex-start; gap: 2px; padding: 0 2px; }
.roscol-zero { position: absolute; left: 2px; right: 2px; top: 42px; height: 1px; background: var(--line-strong); }
.roscol {
  position: relative; z-index: 1;
  flex: 1 1 0; min-width: 0;
  display: flex; flex-direction: column; align-items: center;
}
.roscol-val { height: 14px; line-height: 14px; font-size: 10px; font-weight: 700; color: var(--ink-2); }
.roscol-bar { position: relative; width: 100%; height: 56px; }
.roscol-fill { position: absolute; left: 22%; right: 22%; border-radius: 1px; }
.roscol-fill.up { background: var(--blue); }
.roscol-fill.down { background: var(--line-strong); }
.roscol-num { margin-top: 4px; font-size: 10px; font-weight: 700; color: var(--ink-2); }
.roscol-note { margin-top: 12px; }
.roscol-note b { color: var(--ink-body); font-weight: 700; }

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after { animation-duration: 0.001ms !important; transition-duration: 0.001ms !important; }
}

/* ── Trade calculator (#/trade) ───────────────────────────
   One column, mobile-first. Blue = your side (A), orange = the other side (B),
   the same colour rule as the scoreboard/matchup surfaces. Extra bottom padding
   so the sticky evaluate bar never covers the result. */
.tr-screen { padding-bottom: 96px; }

.tr-selects { display: grid; grid-template-columns: 1fr 1fr; gap: 10px; margin-bottom: 12px; }
.tr-selwrap { display: flex; flex-direction: column; gap: 4px; }
.tr-sellbl { padding-left: 2px; }
.tr-side-a .tr-sellbl { color: var(--blue); }
.tr-side-b .tr-sellbl { color: var(--orange); }
.tr-sel { width: 100%; font-size: 13px; font-weight: 700; color: var(--ink); }

.tr-panels { display: grid; grid-template-columns: 1fr; gap: 12px; }
.tr-panel { padding: 12px 12px 10px; border-top: 3px solid transparent; }
.tr-panel.tr-side-a { border-top-color: var(--blue); }
.tr-panel.tr-side-b { border-top-color: var(--orange); }

.tr-panel-head { display: flex; align-items: baseline; justify-content: space-between; gap: 8px; margin-bottom: 6px; }
.tr-sidetag { font-size: 11px; font-weight: 700; letter-spacing: 0.04em; text-transform: uppercase; color: var(--ink-3); }
.tr-side-a .tr-sidetag { color: var(--blue); }
.tr-side-b .tr-sidetag { color: var(--orange); }
.tr-base { font-size: 12px; color: var(--ink-2); font-weight: 600; }

.tr-poshdr { margin: 8px 0 2px; }
.tr-row {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 4px 2px;
  border-radius: 6px;
  cursor: pointer;
}
.tr-row:hover { background: var(--page); }
.tr-row input { accent-color: var(--blue); width: 16px; height: 16px; flex: none; }
.tr-side-b .tr-row input { accent-color: var(--orange); }
.tr-nm { flex: 1; min-width: 0; font-size: 13px; color: var(--ink); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.tr-byechip { flex: none; font-size: 10px; color: var(--orange); white-space: nowrap; }
.tr-rk { flex: none; width: 44px; text-align: right; font-size: 11px; color: var(--ink-3); }
.tr-ps { flex: none; width: 56px; text-align: right; font-size: 11px; color: var(--ink-2); }
.tr-skip { margin-top: 8px; font-size: 10.5px; color: var(--ink-3); line-height: 1.4; }

/* Sticky evaluate bar, pinned above the bottom nav within the shell column. */
.tr-bar {
  position: sticky;
  bottom: calc(56px + env(safe-area-inset-bottom));
  z-index: 15;
  display: flex;
  align-items: center;
  gap: 12px;
  margin: 14px 0 4px;
  padding: 10px 12px;
  background: rgba(255, 255, 255, 0.96);
  backdrop-filter: saturate(180%) blur(8px);
  border: 1px solid var(--line);
  border-radius: 12px;
  box-shadow: 0 2px 8px rgba(60, 64, 67, 0.1);
}
.tr-eval {
  border: 0;
  border-radius: 8px;
  background: var(--blue);
  color: #fff;
  font-family: inherit;
  font-size: 14px;
  font-weight: 700;
  padding: 9px 18px;
  cursor: pointer;
}
.tr-eval:disabled { opacity: 0.4; cursor: default; }
.tr-picksum { flex: 1; font-size: 12px; color: var(--ink-2); font-variant-numeric: tabular-nums; }
.tr-clear { border: 0; background: transparent; color: var(--ink-3); font-family: inherit; font-size: 12px; font-weight: 700; cursor: pointer; padding: 6px; }

.tr-resultbox { margin-top: 14px; }
.tr-result { padding: 14px 12px 12px; }
.tr-verdict { font-size: 14px; font-weight: 700; color: var(--ink); line-height: 1.4; margin-bottom: 12px; }
.tr-joint { font-weight: 600; }
.tr-mut { color: var(--ink-3); font-weight: 400; }

/* minmax(0,·): a grid item's automatic minimum is its content's min-width, so
   a bare 1fr lets the 14-column week table punch through the scroll wrapper
   and widen the card. Zero the minimum and the wrapper actually scrolls. */
.tr-sides { display: grid; grid-template-columns: minmax(0, 1fr); gap: 12px; }
.tr-sidecard { border: 1px solid var(--line); border-radius: 10px; padding: 10px 11px; border-left-width: 3px; }
.tr-sidecard.tr-side-a { border-left-color: var(--blue); }
.tr-sidecard.tr-side-b { border-left-color: var(--orange); }
/* Each side closes with a rule in its colour — the same bookend the txn
   analyzer's expanded sides use: the left bar opens the block, this ends it. */
.tr-sidecard::after { content: ''; display: block; height: 3px; border-radius: 2px; margin-top: 10px; }
.tr-sidecard.tr-side-a::after { background: var(--blue-stroke); }
.tr-sidecard.tr-side-b::after { background: var(--orange-stroke); }
.tr-sc-name { font-size: 13px; font-weight: 700; color: var(--ink); margin-bottom: 2px; }
.tr-delta { font-size: 24px; font-weight: 800; letter-spacing: -0.02em; line-height: 1.1; font-variant-numeric: tabular-nums; }
.tr-delta.good { color: var(--green); }
.tr-delta.bad { color: var(--red); }
.tr-delta.flat { color: var(--ink-2); }
.tr-delta-unit { font-size: 12px; font-weight: 700; color: var(--ink-3); letter-spacing: 0; }
.tr-beforeafter { font-size: 12px; color: var(--ink-2); margin-top: 3px; }
.tr-beforeafter b { color: var(--ink); font-weight: 700; }

/* 14-week horizons overflow a phone: the scroll wrapper owns the width, the
   table keeps its natural (wider) size inside it. */
.tr-wk-scroll { overflow-x: auto; margin-top: 8px; }
.tr-wk { border-collapse: collapse; font-size: 10.5px; width: 100%; }
.tr-wk th { font-weight: 600; color: var(--ink-3); padding: 1px 3px; text-align: right; }
.tr-wk td { color: var(--ink-2); padding: 1px 3px; text-align: right; }
.tr-wk-lbl { color: var(--ink-3) !important; text-align: left !important; font-weight: 700; }
.tr-wk td.tr-byewk { background: var(--orange-fill); border-radius: 3px; }
.tr-byenote { margin-top: 5px; font-size: 10px; line-height: 1.4; }

.tr-inc { margin-top: 9px; font-size: 12px; color: var(--ink-2); }
.tr-inc-row { line-height: 1.5; }
.tr-inc-row b { color: var(--ink); font-weight: 700; }
.tr-caveat { margin-top: 12px; font-size: 10.5px; color: var(--ink-3); line-height: 1.5; }

/* The side cards always stack: the app lives in the ~430px --shell frame no
   matter the viewport, and two 8-column weekly tables can never share it. */

/* ── Team-page roster card + trade shortcut (TEAM_ROSTER_PLAN §2) ──────────
   Reuses the trade panel's list register (tr-list/tr-row/pos-chip/tr-nm/…). On a
   partner's page it's the orange "other side" theme with checkbox rows + a
   primary CTA into #/trade; on your own page (roster-own) the accent is dropped
   and the rows are plain read-only lines. */
.roster-card.tr-side-b { border-top-color: var(--orange); }
.roster-own { border-top-color: transparent; }
.tr-row-plain { cursor: default; }
.tr-row-plain:hover { background: transparent; }
.roster-cta { margin-top: 12px; }
.roster-cta-btn { width: 100%; padding-top: 11px; padding-bottom: 11px; }

/* Chart-title row with the tucked-away explainer toggle (team page). */
.chart-title-row { display: flex; align-items: center; justify-content: space-between; }
.info-btn {
  border: none; background: none; cursor: pointer;
  color: var(--ink-3); font-size: 14px; line-height: 1;
  padding: 2px 4px; border-radius: 50%;
}
.info-btn:hover { color: var(--ink-2); }

/* ── Free-agent adds (#/fa, FA_PLAN §7) ────────────────────────────────────
   One column, always stacked — the app lives in the ~430px --shell frame, so no
   viewport media queries. Reuses the trade view's register (card / pos-chip /
   tr-delta / tr-wk / good|bad|flat colours) with fa-* for the list + badges. */
.fa-screen { padding-bottom: 96px; }

.fa-teamsel { display: flex; flex-direction: column; gap: 4px; margin-bottom: 12px; }
.fa-sellbl { padding-left: 2px; color: var(--blue); }
.fa-teamsel select { width: 100%; font-size: 13px; font-weight: 700; color: var(--ink); }

.fa-resultbox { display: flex; flex-direction: column; gap: 12px; }
.fa-progress { padding: 40px 20px; }

/* "Open to dropping" — compact disclosure, deliberately low-key (no card). */
.fa-keepbox { margin: -6px 0 12px; }
.fa-droppool { padding: 0 2px; }
.fa-dp-row {
  display: block; width: 100%; border: none; background: none;
  font-family: inherit; font-size: 11px; color: var(--ink-2);
  padding: 2px 0; cursor: pointer; text-align: left;
}
.fa-dp-chev { color: var(--ink-3); margin-left: 4px; }
.fa-dp-list { display: flex; flex-direction: column; gap: 1px; padding: 4px 0 2px; }
.fa-dp-item {
  display: flex; align-items: center; gap: 8px;
  font-size: 12px; color: var(--ink); padding: 3px 0; cursor: pointer;
}
.fa-dp-nm { flex: 1; min-width: 0; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }

/* Hero: the single best add, blown up. */
.fa-hero { padding: 14px 14px 12px; border-top: 3px solid var(--blue); }
.fa-hero-top { display: flex; align-items: baseline; gap: 6px; margin-bottom: 4px; }
.fa-hero-name { font-size: 15px; font-weight: 700; color: var(--ink); flex: 1; min-width: 0;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.fa-hero-rk { font-size: 12px; color: var(--ink-3); flex: none; }
.fa-hero-meta { font-size: 12.5px; color: var(--ink-2); margin-top: 3px; }
.fa-hero-meta b { color: var(--ink); font-weight: 700; }
.fa-explain { margin-top: 10px; font-size: 10.5px; line-height: 1.5; }
.fa-mut { color: var(--ink-3); font-weight: 400; }

/* Per-position groups: a rank/Δ-sorted list of tappable rows. */
.fa-group { padding: 10px 12px 6px; }
.fa-group-hd { margin: 2px 0 4px; }
.fa-row {
  display: flex; align-items: center; gap: 6px; width: 100%;
  padding: 7px 4px; border: none; border-top: 1px solid var(--line);
  background: none; font-family: inherit; text-align: left; cursor: pointer;
}
.fa-group-hd + .fa-row { border-top: none; }
.fa-row:hover { background: var(--page); }
.fa-nm { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 1px; }
.fa-nm-name { font-size: 13px; color: var(--ink); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.fa-nm-sub { font-size: 10.5px; display: flex; align-items: center; gap: 6px; flex-wrap: wrap; }
.fa-rk { flex: none; width: 40px; text-align: right; font-size: 11px; color: var(--ink-3); }
.fa-d { flex: none; width: 60px; text-align: right; font-size: 12.5px; font-weight: 700; }
.fa-d.good { color: var(--green); }
.fa-d.bad { color: var(--red); }
.fa-d.flat { color: var(--ink-2); }
.fa-badge {
  font-size: 8.5px; font-weight: 700; letter-spacing: 0.05em;
  color: var(--orange); background: rgba(234, 138, 0, 0.12);
  border-radius: 3px; padding: 1px 4px;
}

/* Detail card: reuses .tr-sidecard chrome + the weekly win-prob table. */
.fa-detailbox { margin-top: 2px; }
.fa-detail { border-left-color: var(--blue); }
.fa-detail-head { display: flex; align-items: baseline; gap: 6px; margin-bottom: 4px; }
.fa-detail-name { font-size: 13px; font-weight: 700; color: var(--ink); flex: 1; min-width: 0;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.fa-detail-ps { margin-top: 8px; font-size: 11.5px; }
.fa-dropsel { display: flex; flex-direction: column; gap: 4px; margin-top: 10px; }
.fa-dropsel select { width: 100%; font-size: 12.5px; font-weight: 600; color: var(--ink); }

/* ── Leaguewinner: FA ceiling lens + trade ceiling line + team ceiling card
   (WINNER_PLAN.md) ────────────────────────────────────────────────────────
   The upside axis: server-anchored `lwq90` peak chips + the client's late-window
   top-decile deltas. In-shell only (~430px), orange/blue palette. */

/* FA [Now | Ceiling] sort toggle — reuses .segmented from the standings sort. */
.fa-sorthead { margin: 2px 2px 10px; }
/* Muted "peak N/wk" chip on FA rows / roster. */
.fa-peak { font-size: 10px; }

/* Trade side-card ceiling line: sits under the before→after wins line. */
.tr-ceiling { font-size: 12px; margin-top: 4px; color: var(--ink-2); }
.tr-ceiling b { font-weight: 800; }
.tr-ceiling.good b { color: var(--green); }
.tr-ceiling.bad b { color: var(--red); }
.tr-ceiling.flat b { color: var(--ink-2); }

/* Team-page "Late-season ceiling" card. Headline number in the roscard register;
   the curve is styled like the matchup curves (blue KDE); markers are neutral
   (median) + orange (P90 ceiling) — never red (reserved for actual scores). */
.ceil-card { padding: 12px 12px 10px; }
.ceil-lede { margin-bottom: 2px; }
.ceil-lede-val { font-size: 26px; font-weight: 700; color: var(--ink); letter-spacing: -0.03em; line-height: 1.1; }
.ceil-sub { margin: 8px 2px 4px; font-size: 12px; color: var(--ink-2); line-height: 1.5; }
.ceil-sub b { color: var(--ink); font-weight: 700; }
.ceil-contrib-hd { margin: 12px 2px 4px; }
.ceil-contribs { display: flex; flex-direction: column; }
.ceil-contrib { display: flex; align-items: center; gap: 4px; padding: 6px 2px; }
.ceil-contrib + .ceil-contrib { border-top: 1px solid var(--line); }
.ceil-c-nm { flex: 1; min-width: 0; font-size: 13px; color: var(--ink);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.ceil-c-nums { flex: none; display: flex; align-items: baseline; gap: 8px; font-size: 12px; color: var(--ink-2); }
.ceil-c-nums b { color: var(--ink); font-weight: 700; }
.ceil-c-gap { color: var(--orange); font-weight: 700; font-size: 11px; }
.ceil-note { margin-top: 12px; }

/* ceilingCurve p50/p90 markers + legend swatch. */
.fm-mark-p50 { stroke: var(--ink-3); stroke-width: 1.25; stroke-dasharray: 3 2; }
.fm-mark-p90 { stroke: var(--orange-stroke); stroke-width: 1.5; }
.fm-mark-label { font-size: 9.5px; font-weight: 700; font-variant-numeric: tabular-nums; }
.fm-mark-label-p50 { fill: var(--ink-3); }
.fm-mark-label-p90 { fill: var(--orange); }
.legend-swatch.ceil-p90 { height: 0; border-top: 2px solid var(--orange-stroke); border-radius: 0; }

/* ── Outcome densities (OUTCOME_PLAN) ──────────────────────────────────────────
   The move's own per-world Δ distribution: a stacked KDE shaded by how often the
   acquired player starts (gray = never → lightening to solid accent = starts most
   weeks), the total outline on top, p10/p50/p90 tick markers, a dotted zero line.
   Blue = your side / FA add; orange = the trade counterparty. Light-only palette,
   consistent with the rest of the app. */

/* Stacked-band fills: never-starts gray, then the accent lightening→solid. */
.fm-obin { stroke: none; }
.fm-obin-never { fill: rgba(128, 134, 139, 0.22); }
.fm-obin-blue-1 { fill: rgba(26, 115, 232, 0.13); }
.fm-obin-blue-2 { fill: rgba(26, 115, 232, 0.30); }
.fm-obin-blue-3 { fill: rgba(26, 115, 232, 0.52); }
.fm-obin-orange-1 { fill: rgba(234, 138, 0, 0.15); }
.fm-obin-orange-2 { fill: rgba(234, 138, 0, 0.34); }
.fm-obin-orange-3 { fill: rgba(217, 119, 6, 0.55); }
.fm-obin-line { fill: none; stroke-width: 1.4; stroke-linejoin: round; stroke-linecap: round; }
.fm-obin-line-blue { stroke: var(--blue-stroke); }
.fm-obin-line-orange { stroke: var(--orange-stroke); }
/* Zero pivot: SOLID hairline + a baseline "0" — distinct from the dashed stat ticks. */
/* The zero pivot is the chart's key reference (mass right of it = the move
   helped), so it is deliberately the heaviest vertical mark: full ink and
   thicker than both the baseline and the dashed stat ticks. */
.fm-obin-zero { stroke: var(--ink); stroke-width: 1.8; }
.fm-obin-zero-lbl { fill: var(--ink); font-size: 11px; font-weight: 700; font-variant-numeric: tabular-nums; }
/* p10/p50/p90 stat ticks: DASHED, colored to match their strip value so the chart
   marks read as bad (red) · typical (ink) · great (green), same order as the strip. */
.fm-obin-mark { stroke-width: 1; stroke-dasharray: 3 2; }
.fm-obin-mark-p10 { stroke: var(--red); }
.fm-obin-mark-p50 { stroke: var(--ink); stroke-width: 1.4; }
.fm-obin-mark-p90 { stroke: var(--green); }

/* The shared outcome-density block (chart + stat strip + start-bin legend).
   Mobile-legibility pass (OUTCOME_PLAN RESULT): taller chart, one-line caption,
   a full-width headline stat strip, and a tight chip-row legend. */
.oc-block { margin-top: 12px; }
.oc-axislbl { margin: 3px 2px 9px; font-size: 11px; line-height: 1.35; }
/* Slightly larger endpoint (−13/+13) numerals — scoped to the outcome chart so the
   other hero charts' ticks are unaffected. */
.fm-outcome .fm-hero-tick { font-size: 11px; }

/* Headline stat strip: equal columns spanning the full card width, big tabular
   values under small-caps labels. data-n keeps values legible when the FA
   accordion adds Δ-wins + sits (5 columns) vs. the trade side's 3. */
.oc-strip {
  display: grid; grid-auto-flow: column; grid-auto-columns: 1fr; gap: 6px;
  width: 100%; padding-top: 9px; margin-top: 4px; border-top: 1px solid var(--line);
}
.oc-stat { display: flex; flex-direction: column; align-items: center; gap: 2px;
  min-width: 0; line-height: 1.08; text-align: center; }
.oc-stat-lbl { font-size: 9px; font-weight: 700; letter-spacing: 0.06em; text-transform: uppercase; color: var(--ink-3); white-space: nowrap; }
.oc-stat-val { font-size: 21px; font-weight: 800; letter-spacing: -0.02em; font-variant-numeric: tabular-nums; }
.oc-strip[data-n="4"] .oc-stat-val { font-size: 17px; }
.oc-strip[data-n="5"] .oc-stat-val { font-size: 15.5px; }
.oc-stat-val.good { color: var(--green); }
.oc-stat-val.bad { color: var(--red); }
.oc-stat-val.flat { color: var(--ink-2); }
.oc-legend { margin-top: 12px; }
.oc-legend-hd { margin-bottom: 6px; font-size: 11px; font-weight: 700; color: var(--ink-2); }
.oc-legend-items { display: flex; flex-wrap: wrap; gap: 6px; }
.oc-leg { display: inline-flex; align-items: center; gap: 5px; font-size: 11px; color: var(--ink-2);
  background: var(--page); border-radius: 999px; padding: 3px 9px; }
.oc-swatch { width: 11px; height: 11px; border-radius: 3px; flex: none; }
.oc-swatch-never { background: rgba(128, 134, 139, 0.5); }
.oc-swatch-blue-1 { background: rgba(26, 115, 232, 0.22); }
.oc-swatch-blue-2 { background: rgba(26, 115, 232, 0.42); }
.oc-swatch-blue-3 { background: rgba(26, 115, 232, 0.72); }
.oc-swatch-orange-1 { background: rgba(234, 138, 0, 0.24); }
.oc-swatch-orange-2 { background: rgba(234, 138, 0, 0.46); }
.oc-swatch-orange-3 { background: rgba(217, 119, 6, 0.75); }
.oc-leg-lbl { white-space: nowrap; }
.oc-leg-share { font-weight: 700; color: var(--ink-body); }

/* ── FA interval-glyph list (OUTCOME_PLAN Build B) ─────────────────────────────
   [name + "POS · #rank · sits N%"] · [p10→p90 glyph on a shared axis] · [numeral].
   The glyph + ruler share a fixed 150px column so every bar reads on one axis. */
.fa-explain2 { margin: -4px 2px 10px; font-size: 10.5px; line-height: 1.5; }

/* Header ruler row: same flex skeleton as fa-row2 so the ruler sits over the glyph
   column (spacer mirrors the name, fa-ruler-num mirrors the numeral). */
.fa-ruler-row { display: flex; align-items: center; gap: 8px; padding: 0 12px 2px; }
.fa-ruler-spacer { flex: 1; min-width: 0; }
.fm-ruler { display: block; width: 150px; flex: none; }
.fa-ruler-num { width: 52px; flex: none; }
.fm-ruler-axis { stroke: var(--line-strong); stroke-width: 1; }
.fm-ruler-tick { stroke: var(--ink-3); stroke-width: 1; }
.fm-ruler-zero { stroke: var(--ink-2); stroke-width: 1; }
.fm-ruler-lbl { fill: var(--ink-3); font-size: 9px; font-weight: 700; font-variant-numeric: tabular-nums; }

.fa-list { padding: 0; overflow: hidden; }
/* Position-group eyebrow header (old FA grouping restored): inset to the row
   gutter, separated from the first row by a hairline. */
.fa-list > .fa-group-hd { padding: 9px 12px 7px; margin: 0; border-bottom: 1px solid var(--line); }
.fa-item + .fa-item { border-top: 1px solid var(--line); }
.fa-item.open { background: var(--page); }
.fa-row2 {
  display: flex; align-items: center; gap: 8px; width: 100%;
  padding: 9px 12px; border: none; background: none;
  font-family: inherit; text-align: left; cursor: pointer;
}
.fa-row2:hover { background: var(--page); }
.fa-row2 .fa-nm { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 1px; }
.fa-row2 .fm-glyph { flex: none; width: 150px; }
.fa-glyph-num { flex: none; width: 52px; text-align: right; font-size: 13px; font-weight: 800;
  letter-spacing: -0.01em; font-variant-numeric: tabular-nums; }
.fa-glyph-num.good { color: var(--green); }
.fa-glyph-num.bad { color: var(--red); }
.fa-glyph-num.flat { color: var(--ink-2); }

/* Glyph marks. */
.fm-glyph { display: block; }
.fm-glyph-zero { stroke: var(--ink-3); stroke-width: 1; stroke-dasharray: 2 2; }
.fm-glyph-neg { fill: rgba(128, 134, 139, 0.55); }
.fm-glyph-pos-blue { fill: var(--blue); }
.fm-glyph-pos-orange { fill: var(--orange); }
.fm-glyph-p50 { stroke: var(--ink); stroke-width: 1.6; }

/* Tap-to-expand accordion: height animates from a measured scrollHeight; prefers-
   reduced-motion sets max-height:none directly (no .anim class → no transition). */
.fa-acc { overflow: hidden; }
.fa-acc.anim { transition: max-height 0.28s ease; }
.fa-acc-inner { padding: 4px 12px 14px; }
.fa-acc-head { display: flex; align-items: center; gap: 8px; margin-bottom: 2px; }
.fa-acc-head .eyebrow { flex: none; }
.fa-acc-drop { flex: 1; min-width: 0; font-size: 12.5px; font-weight: 600; color: var(--ink); }
.fa-acc-empty { padding: 12px 2px; }

/* ── Transaction log (#/transactions, TXNLOG_PLAN §7) ──────────────────────────
   The league's graded move feed. Reuses the FA list's 3-column skeleton so every
   interval glyph lines up under the ONE shared header ruler
   ([players/team] · [150px glyph] · [Δ wins numeral]), the trade view's
   .tr-wk / .tr-beforeafter / .oc-strip register inside the accordion, and
   .fa-acc for the expand animation. Blue = the team in focus, orange = the trade
   counterparty; red only ever via .bad on a negative Δ. In-shell only (~430px),
   no media queries. */

.txn-screen { padding-bottom: 96px; }
/* Filter bar: type segmented over the team select — the two controls that
   change WHAT the whole screen shows (rollup + feed). No MOVES/SORT eyebrow
   captions: the controls are self-describing, and the old three stacked
   labelled rows read as chrome (Nathan, 2026-07-31). The sort lives down at
   the feed (.txn-feedhead) with the list it re-orders. */
.txn-filters { display: flex; flex-direction: column; gap: 8px; margin: 10px 0 10px; }
/* Full width, equal-ish shares — the .subnav treatment, so the two bars rhyme. */
.txn-typeseg { display: flex; }
.txn-typeseg button { flex: 1; padding: 6px 4px; }
.txn-teamsel { display: block; }
.txn-teamsel select { width: 100%; box-sizing: border-box;
  font-size: 12px; font-weight: 600; color: var(--ink);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
/* Feed head inside the body box: the 12px box gap does the spacing above, the
   negative bottom pulls the axis explainer up under it. */
.txn-feedhead { margin: 2px 2px -4px; }
.txn-feedhead-left { display: flex; align-items: center; gap: 2px; }
.txn-feedctl { display: flex; align-items: center; gap: 6px; }
.txn-sortseg button { padding: 5px 9px; }
/* "Hide washes" toggle: the segmented's register as a lone pill; pressed goes
   solid ink (the TRADE-chip treatment) so the on state is unmistakable. */
.txn-washtoggle {
  border: none; background: #e4e7e9; border-radius: 8px; padding: 5px 10px;
  font-family: inherit; font-size: 11px; font-weight: 600; color: var(--ink-2);
  cursor: pointer;
}
.txn-washtoggle[aria-pressed="true"] { background: var(--ink); color: #fff; }

.txn-bodybox { display: flex; flex-direction: column; gap: 12px; }
/* The shared-axis caption belongs to the ruler under it, so drop the FA list's
   own margins and let the box's 12px gap do the spacing. */
.txn-bodybox > .fa-explain2 { margin: 0 2px; }

/* Rollup: per-team Σ Δ wins over the filtered set. */
.txn-rollup { padding: 10px 12px 8px; }
.txn-rollup-hd { margin-bottom: 4px; }
/* Rollup rows are buttons: tapping one filters the whole view to that team. */
.txn-rollup-row {
  display: flex; align-items: center; gap: 8px; width: 100%;
  padding: 5px 0; border: none; border-top: 1px solid var(--line);
  background: none; font-family: inherit; text-align: left; cursor: pointer;
}
.txn-rollup-row:hover { background: var(--page); }
.txn-rollup-hd + .txn-rollup-row { border-top: none; }
.txn-rollup-back {
  margin-top: 6px; padding: 0; border: none; background: none;
  font-family: inherit; font-size: 11.5px; font-weight: 600;
  color: var(--blue); cursor: pointer;
}
.txn-rollup-nm { flex: 1; min-width: 0; font-size: 13px; color: var(--ink);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.txn-rollup-n { flex: none; width: 28px; text-align: right; font-size: 11px; color: var(--ink-3); }
.txn-rollup-val { flex: none; width: 60px; text-align: right; font-size: 13px; font-weight: 800;
  letter-spacing: -0.01em; }
.txn-rollup-one { display: flex; align-items: baseline; gap: 8px; padding: 6px 0 2px; }
.txn-rollup-one-nm { flex: 1; min-width: 0; font-size: 14px; font-weight: 700; color: var(--ink);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.txn-rollup-one-n { flex: none; font-size: 11.5px; color: var(--ink-3); }
.txn-rollup-one-val { flex: none; font-size: 22px; font-weight: 800; letter-spacing: -0.02em; }
.txn-rollup-val.good, .txn-rollup-one-val.good { color: var(--green); }
.txn-rollup-val.bad, .txn-rollup-one-val.bad { color: var(--red); }
.txn-rollup-val.flat, .txn-rollup-one-val.flat { color: var(--ink-2); }
.txn-rollup-empty { padding: 8px 0; font-size: 12px; }
.txn-rollup-note { margin-top: 8px; }

/* Feed: one card per origin week, one .txn-item per event. */
.txn-group { padding: 0; overflow: hidden; }
.txn-group > .fa-group-hd { padding: 9px 12px 7px; margin: 0; border-bottom: 1px solid var(--line); }
.txn-item + .txn-item { border-top: 1px solid var(--line); }
.txn-item.open { background: var(--page); }
.txn-row {
  display: flex; flex-direction: column; gap: 4px; width: 100%;
  padding: 9px 12px; border: none; background: none;
  font-family: inherit; text-align: left; cursor: pointer;
}
.txn-row:hover { background: var(--page); }

.txn-hd { display: flex; align-items: center; gap: 6px; }
/* The chip sits on .card white AND on the var(--page) grey an open/hovered item
   takes, so it carries its own slightly darker fill rather than .pos-chip's. */
.txn-chip { margin-right: 0; font-size: 8.5px; letter-spacing: 0.06em; background: #e8eaed; }
/* Family tints — the app palette's blue (you act) and orange (claims), well
   under the fills' chart weight so they read as tags, not data. DROP keeps the
   plain gray (a non-event); TRADE inverts to solid ink — the marquee, rare
   enough that a loud chip can't make the feed busy. */
.txn-chip-add { background: rgba(26, 115, 232, 0.12); color: #174ea6; }
.txn-chip-waiver { background: rgba(217, 119, 6, 0.14); color: #8a5200; }
.txn-chip-trade { background: var(--ink); color: #fff; }
/* Team names: sentence case, small, muted. The earlier small-caps treatment
   held its own against the player lines but stacked with the chip it made every
   header a wall of letterspaced uppercase (Nathan, 2026-07-31 — "way too busy").
   Size + colour alone now carry the hierarchy. */
.txn-team { font-size: 11px; font-weight: 600; color: var(--ink-3);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
/* Shrink-to-fit (not flex:1) so the dotted tap affordance hugs the name instead
   of ruling across the whole header; the date's margin-left:auto keeps it right. */
.txn-hd .txn-team { flex: 0 1 auto; min-width: 0; }
/* Tappable team name — the dotted underline on every row read as clutter, so
   the affordance is hover-only now; the rollup rows + header select stay the
   discoverable filter surfaces. */
.txn-teambtn { cursor: pointer;
  border-bottom: 1px dotted transparent; padding-bottom: 1px;
  align-self: flex-start; max-width: 100%; }
.txn-teambtn:hover { color: var(--ink-2); border-bottom-color: var(--ink-3); }
.txn-date { flex: none; margin-left: auto; font-size: 10.5px; color: var(--ink-3); }

/* The 3-column line: [players][glyph][Δ]. Column widths match .fa-row2 so the
   shared .fa-ruler-row above the feed sits exactly over the glyphs. */
.txn-line { display: flex; align-items: center; gap: 8px; }
.txn-main { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 1px; }
.txn-line .fm-glyph { flex: none; width: 150px; }
.txn-num { flex: none; width: 52px; text-align: right; font-size: 12.5px; font-weight: 800;
  letter-spacing: -0.01em; }
.txn-num.good { color: var(--green); }
.txn-num.bad { color: var(--red); }
/* A wash (inside the ±0.05 dead zone) recedes instead of shouting a number: most
   adds grade ~0 and a column of bold grey zeros was pure noise. */
.txn-num.flat { color: var(--ink-3); font-weight: 500; font-size: 11px; opacity: 0.75; }
/* Fixed-width stand-in for a suppressed wash glyph — keeps the [players][glyph]
   [Δ] columns aligned under the shared ruler. */
.txn-glyph-none { flex: none; width: 150px; }

/* One player per line. */
.txn-players { display: flex; flex-direction: column; gap: 1px;
  font-size: 12.5px; color: var(--ink-2); line-height: 1.4; }
/* Hanging indent: a long name ("New England Patriots DST") wraps under its own
   first letter, not under the +/− sign. */
.txn-mv { display: block; padding-left: 14px; text-indent: -14px; }
/* Trade sides list only what they GET, unsigned — no sign column, no indent;
   the "TeamName gets" line above carries the direction. */
.txn-players-gets .txn-mv { padding-left: 0; text-indent: 0; }
.txn-teamline { display: flex; align-items: baseline; gap: 4px; min-width: 0; }
/* Italic + lighter than the team name — it's a connective, not part of the
   name (they blended at the same weight/colour). */
.txn-gets { flex: none; font-size: 10px; font-style: italic; font-weight: 400;
  color: var(--ink-3); opacity: 0.75; }
.txn-sign { display: inline-block; width: 10px; text-indent: 0; color: var(--ink-3); font-weight: 700; }
.txn-players b { color: var(--ink); font-weight: 600; }
.txn-pos { margin-left: 4px; font-size: 9.5px; font-weight: 700; letter-spacing: 0.04em;
  color: var(--ink-3); }
.txn-pick { color: var(--ink-3); font-style: italic; }

/* Trade item: both sides stacked inside ONE event, each a COLUMN inside its
   accent bar — the expanded per-side detail injects here, so the bar (not a
   repeated team name) says whose numbers they are. */
.txn-tside { display: flex; flex-direction: column; gap: 5px;
  border-left: 3px solid transparent; padding-left: 7px; }
/* The seam between the two teams keeps the most air; the coloured side-bottom
   rules replaced the gray hairline here. */
.txn-tside + .txn-tside { margin-top: 12px; }
.txn-tside-a { border-left-color: var(--blue); }
.txn-tside-b { border-left-color: var(--orange); }
/* EXPANDED only: each side closes with a rule in its colour — a card-bottom-ish
   accent (a touch softer than a full card edge) that bookends the block the
   left bar opens. Collapsed rows stay clean (Nathan, 2026-07-31; replaced a
   name-underline attempt). */
.txn-item.open .txn-tside::after { content: ''; flex: none; height: 3px;
  border-radius: 2px; margin-top: 5px; }
.txn-item.open .txn-tside-a::after { background: var(--blue-stroke); }
.txn-item.open .txn-tside-b::after { background: var(--orange-stroke); }
.txn-tside .txn-team { display: inline-block; margin-bottom: 2px; }
/* Verdict = a quiet caption under the trade, not a second headline. */
.txn-verdict {
  padding: 0 12px 10px; margin-top: -2px;
  font-size: 11px; font-weight: 600; color: var(--ink-2); line-height: 1.45;
}

/* Expanded detail: injected inside each side (no bottom panel, no repeated
   names). Compact register — the strip's numerals shrink to feed scale — with
   enough air that the detail doesn't crowd the players (Nathan, 2026-07-31). */
.txn-sidedetail { margin: 6px 0 4px; }
.txn-sidedetail .tr-beforeafter { margin-top: 0; font-size: 11.5px; }
/* The strip's hairline carries its unit label, so the strip itself drops its
   own border. */
.txn-strip-hd { margin-top: 8px; padding-top: 8px; border-top: 1px solid var(--line);
  font-size: 9px; font-weight: 700; letter-spacing: 0.06em; text-transform: uppercase;
  color: var(--ink-3); }
.txn-sidedetail .oc-strip { border-top: none; padding-top: 4px; margin-top: 0; }
.txn-sidedetail .oc-strip .oc-stat-val { font-size: 14px; }
/* The verdict + units caption breathe when the details are open above them. */
.txn-item.open .txn-verdict { margin-top: 4px; }
.txn-acc-caption { margin: 0; padding: 2px 12px 12px; }

.txn-skipped { margin: 2px 0 0; }

/* ── League hub year switch + the title-race card ────────────────────────────
   RECAP_PLAN §4/§5 + addendum. Palette rule unchanged: blue = the highlighted
   team, orange = the champion, ink-3 gray = everyone else. No new colours, no
   media queries (in-shell only, ~430px). */

/* Year select above the section sub-nav on the three League hub screens.
   Hidden entirely when the league has a single season (the demo). */
.hubyear { display: flex; align-items: center; gap: 8px; margin: 0 0 10px; }
.hubyear .eyebrow { flex: none; }
.hubyear select { flex: 1; min-width: 0; }

/* The champion's trophy, after the name on the standings row. */
.row-trophy { margin-left: 5px; font-size: 11px; vertical-align: 1px; }

/* Title race — first card on a finished season's standings screen. */
.race-card { padding: 10px 12px 12px; margin-bottom: 14px; }
.race-head { margin: 0 0 6px; align-items: center; }
.race-teamsel { display: block; max-width: 62%; }
.race-teamsel .wknav-sel { width: 100%; }
.race-champline { font-size: 12px; color: var(--ink-2); line-height: 1.45; margin-bottom: 4px; }
.race-champline b { color: var(--ink); font-weight: 700; }
.race-mut { color: var(--ink-3); font-variant-numeric: tabular-nums; }
.race-cap { margin-top: 8px; }
.race-card .hero-legend { margin-top: 6px; }

.fm-race { display: block; width: 100%; height: auto; }
.fm-race-grid { stroke: var(--line); stroke-width: 1; }
.fm-race-final { stroke: var(--line-strong); stroke-width: 1; stroke-dasharray: 2 3; }
.fm-race-ylbl, .fm-race-xlbl {
  fill: var(--ink-3); font-size: 9px; font-weight: 600; font-variant-numeric: tabular-nums;
}
.fm-race-line { fill: none; stroke-linejoin: round; stroke-linecap: round; }
/* Twelve equally-weighted lines are soup, so the field is a light gray wash and
   only the two lines with a story carry weight. */
.fm-race-line.fm-race-base { stroke: var(--ink-3); stroke-width: 1; opacity: 0.42; }
.fm-race-line.fm-race-champ { stroke: var(--orange); stroke-width: 2.1; }
.fm-race-line.fm-race-sel { stroke: var(--blue); stroke-width: 2.1; }
.fm-race-dot.fm-race-champ { fill: var(--orange); }
.fm-race-dot.fm-race-sel { fill: var(--blue); }
.legend-swatch.race-champ { height: 0; border-top: 2px solid var(--orange); border-radius: 0; }
.legend-swatch.race-sel { height: 0; border-top: 2px solid var(--blue); border-radius: 0; }

/* ══ #/draft — the static draft cheat sheet ═══════════════════════════════
   The one DESKTOP-FIRST surface in the app. Every other screen is a phone
   column; this is a wide table you print, or park on a second monitor while
   the draft runs. It breaks the 430px shell via `body[data-wide="draft"]`,
   which render() sets on this route and clears on every other one.
   Mobile keeps the same rows as a scrollable list: position filter chips, the
   survival columns hidden, and a tap on a row opening them underneath. */

body[data-wide="draft"] .shell { max-width: 1180px; }
body[data-wide="draft"] .tabbar { max-width: 1180px; }
@media (min-width: 600px) {
  body[data-wide="draft"] .shell { box-shadow: 0 0 0 1px var(--line-strong), 0 8px 32px rgba(32, 33, 36, 0.10); }
}

.dr-screen { padding-bottom: 96px; }
.dr-lede { font-size: 13.5px; color: var(--ink-2); margin: 2px 0 14px; max-width: 62ch; }
.dr-hint { font-size: 11.5px; color: var(--ink-3); line-height: 1.5; margin: 8px 0 0; max-width: 78ch; }
.dr-sub { margin-bottom: 8px; }

/* ── setup ── */
.dr-toggle {
  display: block; margin: 0 0 12px; padding: 0;
  border: none; background: none; cursor: pointer;
  font: inherit; font-size: 12.5px; font-weight: 600; color: var(--blue);
}
.dr-toggle:hover { text-decoration: underline; }
.dr-setup { padding: 14px; margin-bottom: 14px; }
.dr-setup-head { display: flex; align-items: baseline; gap: 10px; margin-bottom: 12px; }
.dr-auto { font-size: 11px; color: var(--green); font-weight: 600; }
.dr-warn { margin: 0 0 12px; }
.dr-grid { display: flex; flex-direction: column; gap: 16px; }
.dr-line { display: flex; flex-wrap: wrap; gap: 14px 18px; align-items: flex-end; }
.dr-step { display: flex; flex-direction: column; gap: 5px; }
.dr-step-lbl { white-space: nowrap; }
.dr-step-box {
  display: inline-flex; align-items: center;
  background: var(--page); border-radius: 8px; padding: 2px;
}
.dr-step-btn {
  width: 26px; height: 26px; border: none; border-radius: 6px;
  background: transparent; color: var(--ink-2); cursor: pointer;
  font-size: 15px; line-height: 1; font-family: inherit;
}
.dr-step-btn:hover { background: var(--surface); color: var(--ink); }
.dr-step-val { min-width: 22px; text-align: center; font-weight: 700; color: var(--ink); }
.dr-sel { font-size: 13px; font-weight: 600; color: var(--ink); }
/* ADP <-> value anchor dial. The default is MEASURED (and sits at ADP), so
   "typical" is a badge on the default rather than a label on the midpoint. */
.dr-dialrow { display: flex; align-items: center; gap: 10px; max-width: 420px; }
.dr-dialend {
  font-size: 10px; font-weight: 700; letter-spacing: 0.07em;
  color: var(--ink-3); text-transform: uppercase; white-space: nowrap;
}
.dr-dialinput { flex: 1; min-width: 140px; accent-color: var(--blue); cursor: pointer; }
.dr-dialread { display: flex; align-items: center; gap: 8px; margin-top: 6px; }
.dr-dialpct { font-size: 12.5px; font-weight: 700; color: var(--ink); }
.dr-dialtag {
  font-size: 9.5px; font-weight: 700; letter-spacing: 0.06em;
  text-transform: uppercase; color: var(--green);
  background: var(--lucky-bg); border-radius: 999px; padding: 2px 7px;
}
.dr-dialback {
  border: none; background: none; padding: 0; cursor: pointer;
  font: inherit; font-size: 11.5px; font-weight: 600; color: var(--blue);
}
.dr-dialback:hover { text-decoration: underline; }
/* Keeper PICKER: search the board, get a chip. Free text is gone — a typo used
   to leave the real player priced as draftable. */
.dr-picker { margin-top: 10px; }
.dr-chiplist { display: flex; flex-wrap: wrap; gap: 6px; }
.dr-chiplist:not(:empty) { margin-bottom: 8px; }
.dr-keepchip {
  display: inline-flex; align-items: center; gap: 6px;
  background: var(--page); border-radius: 999px; padding: 3px 4px 3px 11px;
  font-size: 12.5px;
}
.dr-keepchip-mine { background: var(--lucky-bg); }
.dr-keepname { font-weight: 600; color: var(--ink); }
.dr-keeppos { font-size: 10px; font-weight: 700; color: var(--ink-3); letter-spacing: 0.06em; }
.dr-keepnum {
  width: 46px; border: 1px solid var(--line-strong); border-radius: 6px;
  padding: 2px 4px; font: inherit; font-size: 11.5px; text-align: center;
  background: var(--surface); color: var(--ink);
}
.dr-keepmine {
  border: none; border-radius: 999px; cursor: pointer; font: inherit;
  font-size: 9.5px; font-weight: 700; letter-spacing: 0.06em;
  padding: 2px 7px; background: var(--surface); color: var(--ink-3);
}
.dr-keepmine[aria-pressed="true"] { background: var(--green); color: #fff; }
.dr-keepx {
  border: none; background: none; cursor: pointer; font: inherit;
  font-size: 12px; color: var(--ink-3); padding: 2px 6px; border-radius: 50%;
}
.dr-keepx:hover { background: var(--surface); color: var(--red); }
.dr-keepclear {
  border: none; background: none; cursor: pointer; font: inherit;
  font-size: 11.5px; color: var(--ink-3); text-decoration: underline;
  padding: 2px 6px; align-self: center;
}
.dr-keepclear:hover { color: var(--red); }
/* Flex editor: one row per group — a stepper, the four eligibility toggles,
   remove. Superflex is not a special case; it is a flex with QB ticked. */
.dr-flexlist { display: flex; flex-direction: column; gap: 6px; }
.dr-flexrow { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
.dr-flexchips { display: inline-flex; gap: 4px; }
.dr-flexpos {
  border: 1px solid var(--line-strong); border-radius: 999px; cursor: pointer;
  font: inherit; font-size: 10.5px; font-weight: 700; letter-spacing: 0.05em;
  padding: 3px 9px; background: var(--surface); color: var(--ink-3);
}
.dr-flexpos[aria-pressed="true"] {
  background: var(--blue); border-color: var(--blue); color: #fff;
}
.dr-flexadd { padding: 4px 0; }
/* The picks editor: the chips are the picks the board believes are YOURS. */
.dr-pickchip { gap: 4px; }
.dr-pickchip > .num:first-child { font-weight: 700; color: var(--ink); }
.dr-picksub { font-size: 10px; color: var(--ink-3); }
.dr-pickaddrow { display: flex; align-items: center; gap: 8px; margin-top: 6px; }
.dr-pickadd { width: 86px; text-align: left; padding: 5px 8px; font-size: 12.5px; }
.dr-pickrow { position: relative; }
.dr-pickinput {
  width: 100%; min-width: 0; padding: 8px 10px;
  border: 1px solid var(--line-strong); border-radius: 8px;
  font-family: inherit; font-size: 13px; color: var(--ink); background: var(--surface);
}
.dr-pickinput:focus { outline: 2px solid var(--blue); outline-offset: -1px; }
/* The suggestion panel: open on focus, stays open across row toggles. */
.dr-pickpanel {
  position: absolute; top: calc(100% + 4px); left: 0; right: 0; z-index: 30;
  background: var(--surface); border: 1px solid var(--line-strong);
  border-radius: 10px; max-height: 300px; overflow-y: auto;
  box-shadow: 0 8px 24px rgba(0, 0, 0, 0.14);
}
.dr-pickitem {
  display: flex; align-items: center; gap: 8px; padding: 7px 10px;
  cursor: pointer; font-size: 13px;
}
.dr-pickitem:hover { background: var(--page); }
.dr-pickitem-hi { background: var(--page); box-shadow: inset 2px 0 0 var(--blue); }
.dr-pickcheck { flex: none; width: 14px; color: var(--green); font-weight: 700; }
.dr-pickitem-kept .dr-pickname { color: var(--green); }
.dr-pickname { font-weight: 600; color: var(--ink); }
.dr-pickmeta { margin-left: auto; font-size: 11px; color: var(--ink-3); white-space: nowrap; }
.dr-pickempty { padding: 10px 12px; font-size: 12.5px; color: var(--ink-3); }
/* An unmatched keeper name is a real problem, not fine print — it sits above
   the table in the loud variant. */
.dr-warn-loud { border-left: 3px solid var(--red); }
.dr-build { margin-top: 16px; }
.dr-error { color: var(--red); padding: 12px 0 0; text-align: left; }

/* ── replacement level + the gate ── */
.dr-baselines, .dr-gate, .dr-notes { padding: 12px 14px 14px; margin-bottom: 12px; }
.dr-basegrid { display: flex; flex-wrap: wrap; gap: 10px; }
.dr-basecell {
  flex: 1 1 150px; background: var(--page); border-radius: 8px; padding: 8px 10px;
}
.dr-basepos { font-size: 10px; font-weight: 700; letter-spacing: 0.09em; color: var(--ink-3); }
.dr-baseval { font-size: 18px; font-weight: 700; color: var(--ink); letter-spacing: -0.02em; }
.dr-basesub { font-size: 11px; color: var(--ink-3); }
/* D/ST has a replacement line but is not in the gate, and its line carries no
   availability inflation — the dashed, unfilled cell says "same idea, weaker
   claim" without a second paragraph of copy. */
.dr-basecell-soft { background: none; border: 1px dashed var(--line-strong); }
.dr-basecell-soft .dr-baseval { color: var(--ink-2); }
.dr-rule { font-size: 13px; color: var(--ink-body); line-height: 1.55; margin: 4px 0 0; max-width: 78ch; }
ul.dr-rule { padding-left: 20px; }
ul.dr-rule li { margin: 2px 0; }

/* ── the chips + print row ── */
.dr-toolrow {
  display: flex; align-items: center; justify-content: space-between;
  gap: 10px; flex-wrap: wrap; margin-bottom: 10px;
}
.dr-toolrow .dr-chips { margin-bottom: 0; }
.dr-printbtn {
  border: none; border-radius: 8px; background: var(--blue); color: #fff;
  font: inherit; font-size: 12.5px; font-weight: 700; padding: 7px 14px;
  cursor: pointer; white-space: nowrap;
}
.dr-printbtn:hover { filter: brightness(1.08); }
.dr-chips { display: flex; flex-wrap: wrap; gap: 6px; margin-bottom: 10px; }
.dr-chip {
  border: 1px solid var(--line-strong); background: var(--surface);
  border-radius: 999px; padding: 5px 12px; cursor: pointer;
  font: inherit; font-size: 12px; font-weight: 600; color: var(--ink-2);
}
.dr-chip:hover { border-color: var(--ink-3); }
.dr-chip[aria-pressed="true"] { background: var(--ink); border-color: var(--ink); color: #fff; }

/* ── the sheet ── */
.dr-tablewrap {
  background: var(--surface); border-radius: var(--radius);
  border: 1px solid var(--line); overflow-x: auto; margin-bottom: 12px;
}
.dr-table { width: 100%; border-collapse: collapse; font-size: 13px; }
.dr-table th {
  position: sticky; top: 0; z-index: 2;
  background: var(--surface); border-bottom: 1px solid var(--line-strong);
  padding: 8px 8px; text-align: right; white-space: nowrap;
  font-size: 10px; font-weight: 700; letter-spacing: 0.07em; text-transform: uppercase;
  color: var(--ink-3);
}
.dr-table td { padding: 6px 8px; text-align: right; border-bottom: 1px solid var(--line); }
.dr-table tbody tr:last-child td { border-bottom: none; }
.dr-row:hover td { background: #f8f9fa; }
.dr-table th.dr-c-name, .dr-table th.dr-c-pos, .dr-table th.dr-c-team,
.dr-table td.dr-c-name, .dr-table td.dr-c-pos, .dr-table td.dr-c-team { text-align: left; }
.dr-c-rank { color: var(--ink-3); width: 44px; }
/* The live draft slot ("7.04"). Second column on purpose: the board rank says
   how good he is, the pick says when he is your problem. */
.dr-c-pick { color: var(--ink-2); width: 52px; font-weight: 600; }
.dr-c-name { min-width: 170px; }
.dr-name { font-weight: 600; color: var(--ink); }
.dr-c-val { font-weight: 700; color: var(--ink); }
.dr-c-band, .dr-c-adp, .dr-c-bye, .dr-c-tier { color: var(--ink-3); }
.dr-c-surv { width: 56px; }
.dr-pos-QB { color: #b06000; } .dr-pos-RB { color: var(--green); }
.dr-pos-WR { color: var(--blue); } .dr-pos-TE { color: #9334e6; }
/* Defenses carry a value now, so they read as a real position rather than a
   greyed-out afterthought — dark grey, not a sixth hue, because they still are
   not part of the gate. Kickers have no season law at all and stay muted (and a
   DST row in a league with no D/ST slot has no value either, so .dr-nolaw below
   — one class more specific — greys it right back out). */
.dr-pos-DST { color: var(--ink-2); }
.dr-pos-PK { color: var(--ink-3); }
.dr-nolaw td { color: var(--ink-3); background: #fbfbfc; }
.dr-nolaw .dr-name { font-weight: 500; color: var(--ink-2); }
.dr-kept td { background: #f1f3f4; }
.dr-kept .dr-name { text-decoration: line-through; color: var(--ink-3); }
.dr-mine td { background: #e6f4ea; }
.dr-mine .dr-name { text-decoration: none; color: var(--ink); }
/* Keepers are rendered as a block at the top of the sheet; this is the line
   where the players you can actually draft begin. */
.dr-openboard td { border-top: 2px solid var(--line-strong); }
.dr-tag {
  margin-left: 6px; font-size: 9px; font-weight: 700; letter-spacing: 0.06em;
  border-radius: 3px; padding: 1px 4px; background: var(--ink-3); color: #fff;
  vertical-align: 1px;
}
.dr-tag-soft { background: transparent; color: var(--ink-3); border: 1px solid var(--line-strong); }
/* the same badge sitting alone in a right-aligned cell, not trailing a name */
.dr-tag-pick { margin-left: 0; }
/* Survival is a three-step read, not a gradient: he'll be there / coin flip /
   he won't. */
.dr-s-hi { color: var(--green); font-weight: 600; }
.dr-s-mid { color: #b06000; }
.dr-s-lo { color: var(--ink-3); }

.dr-detail { display: none; }
.dr-detail.dr-open { display: table-row; }
.dr-detail-in { padding: 4px 8px 10px; text-align: left; }
.dr-detail-list { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 4px; }
.dr-detail-chip {
  display: inline-flex; gap: 5px; align-items: baseline;
  background: var(--page); border-radius: 6px; padding: 3px 8px; font-size: 12px;
}
.dr-detail-chip b { font-size: 10px; color: var(--ink-3); }

.dr-note { font-size: 12px; color: var(--ink-2); line-height: 1.5; margin: 6px 0 0; max-width: 82ch; }
.dr-stamp {
  font-size: 10.5px; color: var(--ink-3); margin: 12px 0 0;
  font-variant-numeric: tabular-nums; word-break: break-word;
}
.dr-download { margin-top: 4px; border: none; background: none; cursor: pointer; font: inherit; }
/* the take-it-with-you row after the board: print left, JSON export right */
.dr-actions {
  display: flex; align-items: center; justify-content: space-between;
  gap: 10px; flex-wrap: wrap; margin-top: 4px;
}
.dr-actions .dr-download { margin-top: 0; }

/* ── the live draft (Tier 2) ──
   The static sheet is a thing you read; the live board is a thing you TAP, at a
   table, under time pressure. So: one loud "who's on the clock" card, controls
   that are thumb-sized, and exactly one visual claim on the board itself —
   the gate's pick. Everything else gets out of its way. */
.dr-livebar {
  background: var(--surface); border: 1px solid var(--line);
  border-radius: var(--radius); padding: 12px 14px 14px; margin-bottom: 12px;
}
.dr-livebar .dr-hint { margin-top: 8px; }
.dr-startbtn {
  border: none; border-radius: 10px; background: var(--ink); color: #fff;
  font: inherit; font-size: 14px; font-weight: 700; padding: 11px 20px;
  cursor: pointer; white-space: nowrap;
}
.dr-startbtn:hover { filter: brightness(1.25); }
.dr-startbtn-slim { font-size: 12.5px; padding: 7px 14px; border-radius: 8px; }
.dr-hint-warn { color: var(--ink-2); }
.dr-livelink {
  display: inline-block; margin-top: 8px; border: none; background: none;
  padding: 0; cursor: pointer; font: inherit; font-size: 11.5px;
  font-weight: 600; color: var(--blue);
}
.dr-livelink:hover { text-decoration: underline; }
.banner .dr-livelink { margin: 0 0 0 8px; }

.dr-livecard {
  padding: 12px 14px 14px;
  margin-bottom: 12px;
  /* Rides along with the scroll, tucked under the sticky appbar (z 10); above
     the table's own sticky header (z 2). --dr-appbar tracks the appbar's
     rendered height — .card's opaque --surface bg keeps rows from ghosting
     through. */
  position: sticky;
  top: var(--dr-appbar, 63px);
  z-index: 5;
}
/* the pinned-state detector (drWatchStuck) — 1px so IntersectionObserver has
   an area to lose */
.dr-livesentinel { height: 1px; margin-bottom: -1px; }
/* Pinned: shed the label/sub/hint furniture and tighten the chrome, so the
   riding bar costs a phone as little board as possible. The clock number,
   roster chips and controls are the parts a live draft actually needs. */
.dr-livecard.dr-stuck { padding: 8px 14px 9px; }
.dr-livecard.dr-stuck .dr-clock > .eyebrow,
.dr-livecard.dr-stuck .dr-clocksub,
.dr-livecard.dr-stuck .dr-hint { display: none; }
.dr-livecard.dr-stuck .dr-clockpick { margin-top: 3px; }
.dr-livecard.dr-stuck .dr-clocklabel { font-size: 18px; }
.dr-livecard.dr-stuck .dr-roster { margin-top: 7px; }
.dr-livecard.dr-stuck .dr-rchip { padding: 2px 8px; font-size: 11px; }
.dr-livecard.dr-stuck .dr-lbtn { padding: 5px 9px; font-size: 12px; }
.dr-liverow {
  display: flex; align-items: flex-start; justify-content: space-between;
  gap: 12px; flex-wrap: wrap;
}
.dr-clockpick { display: flex; align-items: baseline; gap: 8px; margin-top: 2px; }
.dr-clocklabel { font-size: 22px; font-weight: 800; color: var(--ink); letter-spacing: -0.02em; }
.dr-clockseat { font-size: 12.5px; font-weight: 600; color: var(--ink-2); }
.dr-clockmine .dr-clocklabel { color: var(--green); }
.dr-clockmine .dr-clockseat {
  background: var(--lucky-bg); color: var(--green); border-radius: 999px;
  padding: 2px 9px; font-size: 10px; font-weight: 800; letter-spacing: 0.08em;
}
.dr-clocksub { font-size: 11px; color: var(--ink-3); margin-top: 2px; }
.dr-livectl { display: flex; gap: 6px; flex-wrap: wrap; }
.dr-lbtn {
  border: 1px solid var(--line-strong); background: var(--surface);
  border-radius: 8px; padding: 7px 12px; cursor: pointer;
  font: inherit; font-size: 12.5px; font-weight: 600; color: var(--ink-2);
}
.dr-lbtn:hover:not(:disabled) { border-color: var(--ink-3); color: var(--ink); }
.dr-lbtn:disabled { opacity: 0.4; cursor: default; }
.dr-lbtn[aria-pressed="true"] { background: var(--ink); border-color: var(--ink); color: #fff; }
.dr-lbtn-danger { background: var(--red); border-color: var(--red); color: #fff; }
.dr-lbtn-danger:hover:not(:disabled) { border-color: var(--red); color: #fff; filter: brightness(1.08); }
/* what your lineup still needs, at a glance */
.dr-roster { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 12px; }
.dr-rchip {
  display: inline-flex; align-items: baseline; gap: 6px;
  background: var(--page); border-radius: 999px; padding: 3px 10px; font-size: 12px;
}
.dr-rchip-full { background: var(--lucky-bg); }
.dr-rpos { font-size: 9.5px; font-weight: 800; letter-spacing: 0.07em; color: var(--ink-3); }
.dr-rchip-full .dr-rpos { color: var(--green); }
.dr-rcount { font-weight: 700; color: var(--ink); }
/* the gate's live sentence, above the fine print but below the rule it enforces */
.dr-gateline {
  font-size: 13px; color: var(--ink-body); line-height: 1.5;
  margin: 10px 0 0; max-width: 78ch;
  border-left: 3px solid #f29900; padding-left: 10px;
}
.dr-gateline-hatch { border-left-color: var(--green); }

/* the board, live */
.dr-tappable { cursor: pointer; }
.dr-table.dr-live .dr-row { cursor: default; }
.dr-table.dr-live .dr-tappable { cursor: pointer; }
/* already gone. Not MINE, though — a player I took is on my roster, not off
   the board, so he keeps the keeper-green treatment and his name intact. */
.dr-table .dr-drafted:not(.dr-mine) td { background: #f1f3f4; color: var(--ink-3); }
.dr-table .dr-drafted:not(.dr-mine) .dr-name {
  text-decoration: line-through; color: var(--ink-3); font-weight: 500;
}
.dr-wentat { color: var(--ink-3); }
/* where our future picks LAND if the draft follows this board — a light wash,
   deliberately quieter than the gate's amber: it marks a place, not a player.
   BLUE, not green: green already means "you drafted/kept this guy". Shown on
   the STATIC sheet too (2026-08-07) — same class, same meaning — with a thin
   blue edge so the wash reads at a glance. */
.dr-myslot td { background: rgba(26, 115, 232, 0.09); }
.dr-table .dr-myslot td:first-child { box-shadow: inset 3px 0 0 rgba(26, 115, 232, 0.55); }
.dr-myslot-pick { color: var(--blue); font-weight: 700; }
/* THE recommendation. One row, one colour, unmissable at arm's length. */
.dr-table .dr-gatepick td { background: #fff6e0; }
.dr-table .dr-gatepick td:first-child { box-shadow: inset 3px 0 0 #f29900; }
.dr-table .dr-gatepick .dr-name { font-weight: 800; color: var(--ink); }
.dr-tag-gate { background: #f29900; color: #fff; }
/* the no-FLEX escape hatch's other half: the starter the exception skipped.
   Findable when you scroll to it, never competing with the recommendation. */
.dr-table .dr-gatealt td { background: #fffaf0; }
.dr-table .dr-gatealt td:first-child { box-shadow: inset 3px 0 0 #fbd08a; }
/* redundant at your roster state — still on the board, still tappable */
.dr-dim td { opacity: 0.42; }
/* recomputing: the numbers on screen describe the previous state, so they say
   so rather than pretending to be current */
@keyframes dr-pulse { 0%, 100% { opacity: 0.3; } 50% { opacity: 0.62; } }
.dr-shimmer { animation: dr-pulse 1.1s ease-in-out infinite; }
@media (prefers-reduced-motion: reduce) {
  .dr-shimmer { animation: none; opacity: 0.35; }
}
.dr-c-lsurv { width: 62px; }

/* ── mobile: same rows, fewer columns, survival behind a tap ── */
@media (max-width: 720px) {
  .dr-table { font-size: 12.5px; }
  .dr-c-band, .dr-c-adp, .dr-c-tier, .dr-c-proj { display: none; }
  .dr-c-surv { display: none; }
  .dr-c-name { min-width: 0; }
  /* the surviving columns must FIT — the wrap deliberately doesn't scroll
     (a scroll container would unstick the header), so tighten the cells */
  .dr-table th, .dr-table td { padding-left: 5px; padding-right: 5px; }
  .dr-c-rank { width: 34px; }
  .dr-c-pick { width: 46px; }
  .dr-row { cursor: pointer; }
  .dr-detail.dr-open td { display: table-cell; }
  /* clip (not auto): kills the bleed past the card edge without creating a
     scroll container, so the sticky header keeps tracking the viewport */
  .dr-tablewrap { overflow-x: clip; }
  /* Live: ONE survival column earns its way back on (the whole point of the
     live board), so the bye week — the least urgent thing on a draft clock —
     pays for it. 34 + 46 + pos + team + value + 52 leaves the name ~150px,
     inside the 396px the card gets at 430px. */
  .dr-table .dr-c-lsurv { display: none; }
  .dr-table .dr-c-lsurv.dr-lsurv-mob { display: table-cell; width: 52px; }
  .dr-table.dr-live .dr-c-bye { display: none; }
  .dr-clocklabel { font-size: 19px; }
  .dr-lbtn { padding: 8px 11px; }
  /* Roster chips stay on ONE line: tight metrics fit even the 7-chip FLEX
     shape in the 396px card (12px to spare at 430px); a still-wider config
     scrolls sideways rather than wrapping (scrollbar hidden — a cut-off
     chip makes the overflow obvious). The doubled selector matches the
     .dr-stuck chip rule's specificity so pinning can't re-widen the chips. */
  .dr-roster { flex-wrap: nowrap; gap: 3px; overflow-x: auto; scrollbar-width: none; }
  .dr-roster::-webkit-scrollbar { display: none; }
  .dr-rchip, .dr-livecard.dr-stuck .dr-rchip {
    padding: 2px 5px; font-size: 11px; gap: 3px; flex: none;
  }
  .dr-rpos { font-size: 9px; }
}

/* ── Manager profiles (#/managers, ATTRIBUTION_PLAN §7) ──
   The League tab's fourth section: every team's season split into where it came
   from, as one signed stacked bar per team against the league average.

   Palette, EIGHT sections. FIVE DECISIONS get five hues (violet / blue / teal /
   magenta / forest); the THREE that weren't choices — drift, schedule, luck —
   are an AMBER RUN, so "how much of this bar is amber" still reads as "how much
   of this season wasn't a choice". Keeping that split is the whole organising
   idea of the screen and it is what the palette is spent on.

   It costs two pairs, and the honest thing is to write them down rather than
   contort around them. Validator (dataviz six checks, light, ALL 28 pairs):
   lightness band PASS, chroma PASS, one contrast WARN (--mg-luck at 2.38:1).
   26 of 28 pairs clear both separation floors. The two that do not are both
   INSIDE the amber run:

       drift ↔ schedule    CVD ΔE 2.9    normal ΔE 8.0
       schedule ↔ luck     CVD ΔE 9.6    normal ΔE 10.5

   Three hues of one family cannot be mutually separable — that is arithmetic,
   not a bad choice of hex, and a search over the whole warm gamut got the worst
   pair to ΔE 5.2 only by bleaching the family into salmons. So colour stopped
   being the only answer to "which slab is which": every slab is hoverable and
   tappable and lights its own labelled row (see .fm-stack-hl / .mg-crow-hl
   below), on top of the unconditional legend, the hairline on every slab, and
   the rows restating every component with its number.

   --mg-lineup is a DARK forest green, deliberately far from the house .good
   green #1e8e3e (which appears on this same screen, on the numerals) in both
   lightness and hue. */
:root {
  /* The five you chose. */
  --mg-keepers: #7b4ea3;
  --mg-draft:   #1a73e8;
  --mg-trades:  #0097a7;
  --mg-fa:      #c2185b;
  --mg-lineup:  #375c07;
  /* The three that happened to you, darkest to lightest along the bar. */
  --mg-drift:    #a86a00;
  --mg-schedule: #d2691e;
  --mg-draw:     #e39443;
  --mg-luck:     var(--mg-draw);   /* the format-1 "Results + schedule" bucket */
}

.mg-screen { padding-bottom: 96px; }
.mg-hd { margin-bottom: 6px; }
.mg-league, .mg-head, .mg-onebar { padding: 10px 12px 10px; }
.mg-drift, .mg-luck { padding: 10px 12px 10px; margin-top: 12px; }
.mg-onebar { margin-top: 12px; }
/* .card carries no padding of its own — every managers card has to opt in, and
   the two on an owner's page (Seasons, Biggest moves) never did: they bled to
   the shell's edges and sat flush against the card above. This is that opt-in,
   for any managers card that is not one of the named ones above. */
.mg-panel { padding: 10px 12px 10px; margin-top: 12px; }
.mg-league + .mg-league { margin-top: 12px; }

/* Legend: unconditional (identity is never colour-alone) and it wraps to as many
   lines as the shell needs. */
.mg-legend {
  display: flex; flex-wrap: wrap; gap: 3px 10px;
  margin: 0 0 8px; font-size: 10px; color: var(--ink-2);
}
.mg-legend-item { display: inline-flex; align-items: center; gap: 4px; }
.mg-sw { width: 9px; height: 9px; border-radius: 2px; flex: none;
  box-shadow: inset 0 0 0 0.5px rgba(32, 33, 36, 0.28); }
.mg-sw-keepers { background: var(--mg-keepers); }
.mg-sw-draft   { background: var(--mg-draft); }
.mg-sw-trades  { background: var(--mg-trades); }
.mg-sw-fa      { background: var(--mg-fa); }
.mg-sw-lineup  { background: var(--mg-lineup); }
.mg-sw-drift   { background: var(--mg-drift); }
.mg-sw-luck    { background: var(--mg-luck); }
.mg-sw-schedule { background: var(--mg-schedule); }
.mg-sw-draw     { background: var(--mg-draw); }

/* The bar's own slabs (charts.js stackedDeltaBar). The hairline is what keeps the
   lightest slab legible on white — see the contrast WARN above. */
.fm-stack { display: block; }
.fm-stack-slab { stroke: rgba(32, 33, 36, 0.30); stroke-width: 0.5; }
.fm-stack-keepers { fill: var(--mg-keepers); }
.fm-stack-draft   { fill: var(--mg-draft); }
.fm-stack-trades  { fill: var(--mg-trades); }
.fm-stack-fa      { fill: var(--mg-fa); }
.fm-stack-lineup  { fill: var(--mg-lineup); }
.fm-stack-drift   { fill: var(--mg-drift); }
.fm-stack-luck    { fill: var(--mg-luck); }
.fm-stack-schedule { fill: var(--mg-schedule); }
.fm-stack-draw     { fill: var(--mg-draw); }
.fm-stack-zero { stroke: var(--ink-3); stroke-width: 1; stroke-dasharray: 2 2; }
.fm-stack-net  { stroke: var(--ink); stroke-width: 1.6; }

/* Slab ↔ row identification (the answer to "which slab is which" now that eight
   hues cannot all be told apart — see the palette note above). Point at a slab
   and everything else in the bar recedes while the slab's own row lights up; the
   same happens from the row side. Transitions are short: this fires on hover, so
   anything slower reads as lag rather than as feedback. */
.fm-stack-tap { cursor: pointer; }
.fm-stack-slab { transition: opacity 0.12s ease; }
.mg-hero-lit .fm-stack-slab { opacity: 0.3; }
.mg-hero-lit .fm-stack-slab.fm-stack-hl {
  opacity: 1; stroke: var(--ink); stroke-width: 1.5;
}
/* The lit row: the house "this one" treatment — page-tint fill plus a solid ink
   edge on the leading side, the same vocabulary .mg-dc-on uses on the week
   strips, so a reader who has used one strip already knows what this means. */
.mg-crow-wrap.mg-crow-hl .mg-crow {
  background: var(--page); box-shadow: inset 3px 0 0 var(--ink-2);
}

/* League table: name · signed wins vs average · the bar, on one shared axis. The
   ruler is inset by the name+value columns so it lines up with the bars. */
.mg-rulerrow { display: flex; align-items: flex-end; gap: 8px; }
.mg-rule-spacer { flex: 1; min-width: 0; }
.mg-row {
  display: flex; align-items: center; gap: 8px; width: 100%;
  padding: 3px 0; border: none; border-top: 1px solid var(--line);
  background: none; font-family: inherit; text-align: left; cursor: pointer;
}
.mg-row:first-of-type { border-top: none; }
.mg-row:hover { background: var(--page); }
.mg-row-nm {
  flex: 1; min-width: 0; font-size: 12.5px; color: var(--ink);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.mg-row-val { flex: none; width: 40px; text-align: right; font-size: 12px; font-weight: 800; }
.mg-row-val.good { color: var(--green); }
.mg-row-val.bad { color: var(--red); }
.mg-row-val.flat { color: var(--ink-3); }
.mg-row .fm-stack { flex: none; width: 150px; }
/* Under a lens the column carries 2 decimals (a component's career mean is a
   tenth-of-a-win quantity; +0.7 would round three managers into a tie), so it
   needs the extra character's worth of width. */
.mg-row-lens .mg-row-val { width: 46px; }
.mg-note { margin-top: 8px; font-size: 11px; line-height: 1.45; }

/* All time (§V5 S3). The career table IS .mg-row — same name / value / bar
   grammar as the season table, because it is the same quantity averaged. What is
   new is per-row sample size (three seasons is a small number and the row has to
   admit it) and, on an owner's card, the individual seasons printed under each
   bucket instead of a standard deviation nobody can read off n=3. */
/* Its OWN line, not a suffix: inline, the sample size ellipsised the name away
   ("Deising · 3 seasons · 3 sha…"). The name is the row's subject and always
   gets the full width; the counts sit under it. */
.mg-row-n {
  display: flex; gap: 5px; font-weight: 400; color: var(--ink-3); font-size: 11px;
  white-space: nowrap; overflow: hidden;
}
/* The count yields; the lens's overall figure does not (see the comment at its
   call site — as one string it was the overall that got ellipsised away). */
.mg-row-n-tx { min-width: 0; overflow: hidden; text-overflow: ellipsis; }
.mg-row-n-tot { flex: none; }
.mg-row-n-tot::before { content: '·'; margin-right: 5px; }
.mg-season-team {
  display: block; font-size: 11px; color: var(--ink-3); font-weight: 400;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
/* ── The lens (§V5 S4) ──
   Chips that pick which component the table is about. Same pill register as
   .dr-chip on the draft board, plus the component's own swatch — so the chip,
   the legend entry, the lit slab and the bold number are one colour saying one
   thing. Wraps to as many lines as the shell needs; on a phone that is three. */

/* The card head carries the Bars/Table switch, so the eyebrow shares its line. */
.mg-cardhd {
  display: flex; align-items: flex-start; gap: 8px; justify-content: space-between;
}
.mg-cardhd .mg-hd { flex: 1; min-width: 0; }
/* Segmented, not two loose pills: these are two states of ONE thing, and the
   shared border says so. Small, quiet, top-right — it is a view control, and it
   must not read as louder than the sort. */
.mg-seg {
  flex: none; display: inline-flex; border: 1px solid var(--line-strong);
  border-radius: 999px; overflow: hidden; margin-top: -2px;
}
.mg-segbtn {
  border: none; background: var(--surface); cursor: pointer; font: inherit;
  font-size: 10.5px; font-weight: 700; letter-spacing: 0.02em;
  color: var(--ink-3); padding: 3px 10px;
}
.mg-segbtn + .mg-segbtn { border-left: 1px solid var(--line-strong); }
.mg-segbtn:hover { background: var(--page); }
.mg-segbtn[aria-pressed="true"] { background: var(--ink); color: #fff; }

/* ── The sortable table ──
   Wider than the shell on purpose (nine columns of signed hundredths do not fit
   a phone, and squeezing them would mean 9px numerals). It scrolls sideways
   inside its own box — the page body never does — with the name column pinned so
   a reader out at Luck still knows whose row it is. */
/* No negative-margin bleed to the card edge, however much nicer that looks: a
   sticky `left: 0` sticks to the scrollport's BORDER edge, so the 12px of
   padding to its left stayed a window onto the scrolling columns and printed
   stray digits beside the pinned names. */
.mg-tablewrap {
  overflow-x: auto; -webkit-overflow-scrolling: touch; margin-top: 2px;
}
.mg-table { border-collapse: separate; border-spacing: 0; width: 100%; }
.mg-th {
  padding: 0; vertical-align: bottom; background: var(--surface);
  border-bottom: 1px solid var(--line-strong);
}
.mg-thbtn {
  display: flex; align-items: center; justify-content: flex-end; gap: 3px;
  width: 100%; border: none; background: none; font: inherit; cursor: pointer;
  padding: 0 4px 4px; color: var(--ink-3);
  font-size: 10px; font-weight: 700; letter-spacing: 0.02em; white-space: nowrap;
}
.mg-thbtn:hover { color: var(--ink); }
.mg-th-num { min-width: 52px; }
.mg-th-tot .mg-thbtn { color: var(--ink-2); }
.mg-th-on .mg-thbtn { color: var(--ink); }
.mg-th-on { border-bottom-color: var(--ink); }
.mg-th-ar { font-size: 9px; }
/* The pinned name column, in the head and the body. Its own background, because
   the cells it overlaps while scrolling would otherwise show through. */
.mg-th-nm, .mg-td-nm {
  position: sticky; left: 0; z-index: 1; background: var(--surface);
  text-align: left; padding-right: 8px;
}
.mg-th-nm { padding-bottom: 4px; }
.mg-th-nm .mg-th-tx {
  font-size: 10px; font-weight: 700; letter-spacing: 0.02em; color: var(--ink-3);
}
.mg-tr:hover .mg-td { background: var(--page); }
.mg-td {
  padding: 4px 4px; border-bottom: 1px solid var(--line);
  font-size: 11.5px; font-weight: 700; text-align: right; white-space: nowrap;
}
.mg-td.good { color: var(--green); }
.mg-td.bad { color: var(--red); }
.mg-td.flat { color: var(--ink-3); }
/* The sorted column: a tint down its whole length, so the eye can find it after
   scrolling sideways without re-reading the head. */
.mg-td-on { background: rgba(32, 33, 36, 0.045); }
.mg-tr:hover .mg-td-on { background: rgba(32, 33, 36, 0.075); }
.mg-td-tot { font-weight: 800; color: var(--ink); }
.mg-td-tot.good { color: var(--green); }
.mg-td-tot.bad { color: var(--red); }
.mg-tdname {
  display: flex; align-items: baseline; gap: 5px; max-width: 112px;
  border: none; background: none; padding: 0; cursor: pointer;
  font: inherit; font-size: 12px; font-weight: 600; color: var(--ink);
  text-align: left; white-space: nowrap;
}
/* The ellipsis lives on the text span, not on the flex button — text-overflow
   does nothing to a flex container's anonymous items, so "craigsoccer99" simply
   overran the pinned column and printed under the numbers. */
.mg-tdname-tx { min-width: 0; overflow: hidden; text-overflow: ellipsis; }
.mg-tdname:hover .mg-tdname-tx { text-decoration: underline; }
/* Sample size, as small as it can be and still be read: the bars spell out
   "3 seasons" and a column of those would be nine characters of noise. */
.mg-td-n { flex: none; font-size: 9.5px; font-weight: 400; color: var(--ink-3); }
.mg-td-n::before { content: 'n'; }
.mg-lensrow { display: flex; flex-wrap: wrap; gap: 5px; margin: 0 0 9px; }
.mg-lenschip {
  display: inline-flex; align-items: center; gap: 5px;
  border: 1px solid var(--line-strong); background: var(--surface);
  border-radius: 999px; padding: 4px 10px; cursor: pointer;
  font: inherit; font-size: 11.5px; font-weight: 600; color: var(--ink-2);
}
.mg-lenschip:hover { border-color: var(--ink-3); }
.mg-lenschip[aria-pressed="true"] {
  background: var(--ink); border-color: var(--ink); color: #fff;
}
/* The swatch is the chip's identity, so it keeps its hue when the chip inverts —
   but it needs a light edge to survive on ink. */
.mg-lenschip[aria-pressed="true"] .mg-sw {
  box-shadow: inset 0 0 0 0.5px rgba(255, 255, 255, 0.55);
}

/* Skill is not a bucket, so it gets no hue of its own — its swatch is the five
   decision hues in a row, which is exactly what the number is a sum of. (Drift
   is in that sum and in the gradient; schedule and the two draws are not, which
   is the whole claim the swatch is making.) */
.mg-sw-skill {
  background: linear-gradient(100deg,
    var(--mg-keepers) 0 20%, var(--mg-draft) 20% 40%, var(--mg-trades) 40% 60%,
    var(--mg-fa) 60% 80%, var(--mg-drift) 80% 100%);
}

/* Subtracted, not deleted: the columns skill takes out stay legible enough to
   check the arithmetic and quiet enough to read as "not counted". */
.mg-out .mg-thbtn { opacity: 0.4; }
.mg-td.mg-out { opacity: 0.32; font-weight: 400; }

/* Who's best at what: swatch · bucket · the name · the number. A readout that is
   also the lens control, so it takes the row-button register (hover tint, and an
   ink edge on the one currently driving the table below). */
.mg-best {
  display: flex; align-items: baseline; gap: 8px; width: 100%;
  padding: 4px 0; border: none; border-top: 1px solid var(--line);
  background: none; font-family: inherit; text-align: left; cursor: pointer;
}
.mg-best:first-of-type { border-top: none; }
.mg-best:hover { background: var(--page); }
/* The aggregate row: same grammar, ruled off from the buckets below it and set
   in the body ink, because it is a sum of them rather than one more of them. */
.mg-best-agg { border-bottom: 2px solid var(--line-strong); padding-bottom: 6px; }
.mg-best-agg .mg-best-cat { color: var(--ink); }
.mg-best-agg + .mg-best { border-top: none; padding-top: 6px; }
.mg-best-on { background: var(--page); box-shadow: inset 3px 0 0 var(--ink-2); }
.mg-best .mg-sw { align-self: center; }
.mg-best-cat {
  flex: none; width: 78px; font-size: 11px; font-weight: 700; color: var(--ink-2);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.mg-best-nm {
  flex: 1; min-width: 0; font-size: 12.5px; color: var(--ink);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.mg-best-val { flex: none; font-size: 12.5px; font-weight: 800; }
.mg-best-val.good { color: var(--green); }
.mg-best-val.bad { color: var(--red); }
.mg-best-val.flat { color: var(--ink-3); }

/* A component row that is a readout, not a control: no caret, no tap, no hover
   — the per-season detail behind the season view's rows does not exist here. */
.mg-crow-static { cursor: default; }
.mg-crow-static:hover { background: none; }
.mg-crow-each {
  flex: none; font-size: 10.5px; color: var(--ink-3); letter-spacing: -0.01em;
  white-space: nowrap;
}

/* Team detail. The hero bar + ruler render at a fixed 380-wide viewBox and
   scale to the card (the raceChart idiom) — full width, uniform scale. */
.mg-hero-ruler .fm-ruler, .mg-hero-row .fm-stack {
  display: block; width: 100%; height: auto;
}
.mg-head-nm { font-size: 17px; font-weight: 800; letter-spacing: -0.02em;
  color: var(--ink); margin: 4px 0 2px; }

/* Component rows under the hero: swatch · label · the vs-league number. They
   are the legend. Tap for the fine print (mg-crow-detail). */
.mg-crow {
  display: flex; align-items: center; gap: 8px; width: 100%;
  padding: 7px 0; border: none; border-top: 1px solid var(--line);
  background: none; font-family: inherit; text-align: left; cursor: pointer;
}
.mg-crow-wrap:first-of-type .mg-crow { margin-top: 4px; }
.mg-crow:hover { background: var(--page); }
.mg-crow-nm { flex: 1; min-width: 0; font-size: 13px; font-weight: 700; color: var(--ink); }
.mg-crow-val { flex: none; font-size: 16px; font-weight: 800; letter-spacing: -0.02em; }
.mg-crow-val.good { color: var(--green); }
.mg-crow-val.bad { color: var(--red); }
.mg-crow-val.flat { color: var(--ink-2); }
.mg-crow-caret { flex: none; width: 10px; font-size: 10px; color: var(--ink-3); }
.mg-crow-detail { padding: 0 0 9px 17px; }
.mg-crow-detail:empty { display: none; }
.mg-comp-sub { font-size: 11px; margin-top: 1px; }
.mg-comp-sub2 { font-size: 11px; margin-top: 4px; }
.mg-comp-cap { font-size: 11px; line-height: 1.45; margin-top: 6px; }
.mg-side { display: flex; align-items: baseline; gap: 8px; margin-top: 6px; }
.mg-side-nm { flex: 1; min-width: 0; font-size: 11.5px; font-weight: 700; color: var(--ink); }
.mg-side-val { flex: none; font-size: 12px; font-weight: 800; }
.mg-side-val.good { color: var(--green); }
.mg-side-val.bad { color: var(--red); }
.mg-side-val.flat { color: var(--ink-3); }
.mg-side-cap { font-size: 11px; line-height: 1.45; margin-top: 1px; }

/* The luck strip's third row: the card's own number, week by week. Same weight as
   the drift strip's Δ wins row — these two strips are the same object one card
   apart and must not read as different kinds of thing. */
.mg-lkc.good { color: var(--green); font-weight: 700; }
.mg-lkc.bad { color: var(--red); font-weight: 700; }
.mg-lkc.flat { color: var(--ink-3); }

/* One week of the luck strip, opened. */
.mg-luck-detail:empty { display: none; }
.mg-lkwk { margin-top: 8px; padding-top: 6px; border-top: 1px solid var(--line); }
.mg-lkwk-score { font-size: 17px; font-weight: 800; letter-spacing: -0.02em;
  margin-top: 2px; }
.mg-lkwk-score .good { color: var(--green); }
.mg-lkwk-score .bad { color: var(--red); }
.mg-lkwk-dash { color: var(--ink-3); margin: 0 4px; }
.mg-lkwk-sub { font-size: 11px; margin-top: 1px; }
.mg-lkwk-terms { display: flex; flex-wrap: wrap; gap: 3px 12px; margin-top: 7px; }
.mg-lkwk-term { display: inline-flex; align-items: baseline; gap: 4px; }
.mg-lkwk-term-nm { font-size: 11px; color: var(--ink-2); }
.mg-lkwk-term-val { font-size: 12px; font-weight: 800; }
.mg-lkwk-term-val.good { color: var(--green); }
.mg-lkwk-term-val.bad { color: var(--red); }
.mg-lkwk-term-val.flat { color: var(--ink-3); }

.mg-chips { margin-top: 7px; }
.mg-chip { font-size: 10px; padding: 2px 5px; margin-bottom: 3px; }
.mg-link {
  display: block; margin-top: 8px; padding: 0; border: none; background: none;
  font-family: inherit; font-size: 11.5px; font-weight: 600;
  color: var(--blue); cursor: pointer; text-align: left;
}
.mg-moves { margin-top: 6px; }
.mg-move { display: flex; align-items: baseline; gap: 8px; padding: 3px 0;
  border-top: 1px solid var(--line); }
.mg-move-who { flex: 1; min-width: 0; font-size: 11.5px; color: var(--ink-body);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.mg-move-wk { flex: none; font-size: 10px; color: var(--ink-3); }
.mg-move-val { flex: none; width: 42px; text-align: right; font-size: 11.5px; font-weight: 700; }
.mg-move-val.good { color: var(--green); }
.mg-move-val.bad { color: var(--red); }
.mg-move-val.flat { color: var(--ink-3); }

/* Drift timeline: the .tr-wk strip, with tappable week cells. */
.mg-drifttbl td.good { color: var(--green) !important; font-weight: 700; }
.mg-drifttbl td.bad { color: var(--red) !important; font-weight: 700; }
.mg-dc-tap { cursor: pointer; border-radius: 3px; }
.mg-dc-tap:hover { background: var(--page); }
.mg-dc-on { background: var(--page); box-shadow: inset 0 0 0 1px var(--line-strong); }
.mg-drift-detail:empty { display: none; }
.mg-movers { margin-top: 8px; padding-top: 6px; border-top: 1px solid var(--line); }
.mg-mover { display: flex; align-items: baseline; gap: 6px; padding: 2px 0; }
.mg-mover-wk { flex: none; width: 22px; font-size: 10px; color: var(--ink-3); }
.mg-mover-nm { flex: 1; min-width: 0; font-size: 12px; color: var(--ink);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.mg-mover-pos { flex: none; font-size: 9px; font-weight: 700; color: var(--ink-3); }
.mg-mover-val { flex: none; width: 46px; text-align: right; font-size: 12px; font-weight: 700; }
.mg-mover-val.good { color: var(--green); }
.mg-mover-val.bad { color: var(--red); }
.mg-mover-val.flat { color: var(--ink-3); }
/* The W/L letters. Scoped to the STRIP, not to the card that used to hold it —
   in split mode the strip lives inside the Luck row's detail instead. */
.mg-lkstrip td.good { color: var(--green) !important; font-weight: 700; }
.mg-lkstrip td.bad { color: var(--red) !important; font-weight: 700; }
.mg-lkstrip { margin-top: 8px; }
.mg-crow-detail .mg-lkstrip { margin-top: 10px; padding-top: 8px;
  border-top: 1px solid var(--line); }

/* ── Keepers: the flag question + the entry form (ATTRIBUTION_PLAN §V3) ──
   The only card on this screen that ASKS for something, so it is allowed one
   full-width button — and nothing else that competes with the table under it.
   The pickers reuse the draft board's .dr-* picker chrome verbatim; only the
   per-team wrapper is new. */
.mg-keep { padding: 12px 12px 14px; margin-bottom: 12px; }
.mg-keep-q { font-size: 13.5px; font-weight: 700; color: var(--ink); line-height: 1.35; }
.mg-keep-lede { font-size: 11.5px; line-height: 1.5; margin-top: 4px; }
.mg-keep-choices { display: flex; gap: 8px; margin-top: 10px; }
.mg-keep-choice {
  flex: 1; padding: 9px 10px; border: 1px solid var(--line-strong);
  border-radius: 8px; background: var(--surface); cursor: pointer;
  font-family: inherit; font-size: 13px; font-weight: 700; color: var(--ink);
}
.mg-keep-choice:hover { background: var(--page); }
.mg-keep-choice[aria-pressed="true"] {
  border-color: var(--blue); color: var(--blue);
  box-shadow: inset 0 0 0 1px var(--blue);
}
.mg-keep-n { display: block; margin-top: 12px; }
.mg-keep-nin { width: 88px; }
.mg-keep-btn { margin-top: 12px; }
.mg-keep .skip { margin-top: 10px; }
.mg-keep .banner { margin: 12px 0 0; }
/* The save confirmation sits above the whole screen, not inside the card that
   is now gone. */
.mg-keep-saved { margin-bottom: 12px; }

.mg-keepform .mg-kteam { padding-top: 10px; margin-top: 10px; border-top: 1px solid var(--line); }
.mg-kteam-hd { display: flex; align-items: baseline; gap: 6px; }
.mg-kteam-nm {
  flex: 1; min-width: 0; font-size: 12.5px; font-weight: 700; color: var(--ink);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.mg-kteam-you {
  flex: none; font-size: 9px; font-weight: 700; letter-spacing: 0.06em;
  color: var(--green); background: var(--lucky-bg); border-radius: 999px;
  padding: 2px 7px;
}
.mg-kpick { margin-top: 6px; }
/* Over the league's per-team norm (a traded keeper) is a NOTE, not an error: the
   muted body colour every other aside on this card uses, no icon, no red. The
   league total is the only thing that can stop a save, and it says so down by
   the button it disables. */
.mg-kover { font-size: 11px; line-height: 1.45; margin-top: 5px; }
.mg-keep-over {
  font-size: 11.5px; line-height: 1.5; margin-top: 12px; color: var(--ink-2);
}
/* Both notes live in the DOM permanently and are written by their sync, so an
   empty one must take no room at all. */
.mg-kover:empty, .mg-keep-over:empty { display: none; }

/* ── the print sheet ──
   The screen sheet is one overall-value table; the sheet you take into a
   draft room is per-position columns you cross names off of (draftPrintSheet
   in app.js). It exists in the DOM whenever a board is rendered but only
   @media print ever shows it — on paper it REPLACES everything else on the
   route. Landscape comes from a @page rule the node carries itself, so other
   routes still print portrait. */
.dr-printsheet { display: none; }

@media print {
  body { background: #fff; }
  .shell, body[data-wide="draft"] .shell { max-width: none; box-shadow: none; }
  .tabbar, .appbar, .samplechip, .banner { display: none !important; }
  .screen { padding: 0; }
  .card { border: none; border-radius: 0; box-shadow: none; }

  /* draft route: swap the screen for the cheat sheet */
  body[data-wide="draft"] .dr-screen > * { display: none !important; }
  body[data-wide="draft"] .dr-printsheet { display: block !important; }

  .dr-printsheet { color: #000; }
  .dr-ps-top {
    display: flex; justify-content: space-between; align-items: baseline;
    gap: 12pt; border-bottom: 1.5pt solid #000; padding-bottom: 2pt;
  }
  .dr-ps-title { font-size: 10.5pt; font-weight: 800; white-space: nowrap; }
  .dr-ps-cfg { font-size: 7.5pt; font-weight: 600; text-align: right; }
  .dr-ps-rule { font-size: 6.5pt; color: #333; line-height: 1.3; margin: 2pt 0 4pt; max-width: none; }
  .dr-ps-grid { display: grid; grid-template-columns: repeat(4, 1fr); gap: 0 8pt; align-items: start; }
  .dr-ps { margin-bottom: 5pt; }
  .dr-ps-head {
    font-size: 7pt; font-weight: 700; letter-spacing: 0.08em;
    text-transform: uppercase; background: #202124; color: #fff;
    padding: 1.5pt 5pt; margin: 0; line-height: 1.3;
    print-color-adjust: exact; -webkit-print-color-adjust: exact;
  }
  .dr-ps-table {
    width: 100%; border-collapse: collapse;
    font-size: 7pt; line-height: 1.08; font-variant-numeric: tabular-nums;
  }
  .dr-ps-table th {
    font-size: 6pt; font-weight: 700; letter-spacing: 0.06em;
    text-transform: uppercase; text-align: left; color: #000;
    padding: 1pt 3pt; border-bottom: 0.75pt solid #000; line-height: 1.2;
  }
  .dr-ps-table td { padding: 0.8pt 3pt; border-bottom: 0.25pt solid #ddd; }
  .dr-ps-table tr { break-inside: avoid; }
  .dr-ps-num { color: #666; }
  .dr-ps-name {
    font-weight: 600; max-width: 9.5em;
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  }
  .dr-ps-r { text-align: right; }
  /* the value cell carries an inline data-bar gradient — force it onto paper */
  .dr-ps-val { font-weight: 700; print-color-adjust: exact; -webkit-print-color-adjust: exact; }
  /* upside is a second read, not a second VAL — quieter ink */
  .dr-ps-p90 { color: #555; }
  /* a tier boundary is a heavier rule between rows */
  .dr-ps-tier td { border-top: 1pt solid #555; }
  .dr-ps-kept td { color: #999; }
  .dr-ps-kept .dr-ps-name { text-decoration: line-through; font-weight: 500; }
  .dr-ps-mine td { background: #e6f4ea; print-color-adjust: exact; -webkit-print-color-adjust: exact; }
  .dr-ps-foot {
    display: flex; justify-content: space-between; gap: 14pt;
    margin-top: 4pt; font-size: 6.3pt; color: #333;
  }
}

/* The pre-owners fallback note (§V5 S3): a league whose artifacts were built
   before owner identity gets one season at a time and is told why. */
.mg-partial { text-align: left; font-size: 12px; line-height: 1.5; }

/* ── Accounts: sign in / subscribe / account (Slice 3) ────
   The front-door screens reuse the connect layout wholesale (.connect, .field,
   .input, .btn, .banner, .skip, .card, .eyebrow) — everything here is the small
   remainder those classes don't already carry. */

/* Account door at the end of every app bar: the same 36px .iconbtn the back
   arrow uses, so the bar's rhythm is unchanged. */
.auth-acct { color: var(--ink-2); }
.auth-acct:hover { color: var(--ink); }

/* The 6-digit code: the field IS the number, so it gets the display treatment
   (wide tracking, centred) rather than reading as ordinary text entry. */
.auth-code {
  text-align: center;
  font-size: 24px;
  font-weight: 700;
  letter-spacing: 0.34em;
  text-indent: 0.34em;      /* recentre: tracking pads only the right side */
  padding: 13px 12px;
}
.auth-code::placeholder { letter-spacing: 0.28em; color: var(--line-strong); }

/* Success banner: the muted-amber .banner in the green the luck surfaces use,
   so "this worked" reads the same everywhere. */
.auth-ok { background: var(--lucky-bg); color: #14652c; }

/* A second, quieter skip link under the primary one (resend vs. change email,
   sign out under manage billing) — same link, lower billing. */
.auth-quiet { color: var(--ink-2); font-weight: 600; margin-top: 12px; }
.auth-quiet:hover { color: var(--ink); }

.auth-screen { padding-bottom: 32px; }

.auth-card { padding: 12px 14px; margin-top: 4px; }

/* What a subscription buys, as a plain list — no bullets glyph, the rule
   between rows is the separation. */
.auth-list { margin: 0; padding: 0; list-style: none; }
.auth-list li {
  padding: 8px 0 8px 20px;
  font-size: 13px;
  color: var(--ink-body);
  line-height: 1.45;
  position: relative;
}
.auth-list li + li { border-top: 1px solid var(--line); }
.auth-list li::before {
  content: "✓";
  position: absolute;
  left: 0;
  color: var(--green);
  font-weight: 700;
}

/* Label-over-value rows on #/account (email, subscription). */
.auth-row { padding: 8px 0; }
.auth-row + .auth-row { border-top: 1px solid var(--line); }
.auth-val { font-size: 14px; color: var(--ink); margin-top: 2px; word-break: break-word; }

/* An anchor wearing the primary button: same box, and the text has to be
   centred by hand because <a> has no button's implicit centring. */
.auth-btnlink { text-align: center; text-decoration: none; }
