/* The player detail page.
 *
 * Three panes on desktop: the nav stays, the middle column scrolls, the right
 * rail scrolls on its own. That shape is the point -- the chart stays in view
 * while you read the model, the prices and the matchup beside it.
 *
 * On a phone it becomes something different rather than merely narrower. The
 * cards stack, but the chart is what you came for, so it stays pinned under the
 * hero while the supporting cards scroll past it. Naive stacking scrolls the
 * chart away the moment you read anything, which destroys exactly the thing
 * that makes the desktop layout work.
 */

/* ---- The two-column body ------------------------------------------------ */
.detail {
  display: grid;
  grid-template-columns: minmax(0, 1fr) var(--rail-w);
  gap: var(--s5);
  align-items: start;
  padding: var(--s4) var(--s5) var(--s6);
}
/* Both panes own their scrolling, so neither drags the other. The offset is
 * the sticky topbar's height: without it the rail's own top sits under it. */
.detail > * { min-width: 0; }
.rail {
  position: sticky;
  top: calc(var(--topbar-h) + var(--s4));
  max-height: calc(100vh - var(--topbar-h) - var(--s5));
  overflow-y: auto;
  overscroll-behavior: contain;
  display: grid;
  gap: var(--s3);
  padding-right: 2px;
  scrollbar-width: thin;
}
.rail::-webkit-scrollbar { width: 8px; }
.rail::-webkit-scrollbar-thumb {
  background: var(--line-2); border-radius: 4px;
}

/* ---- Hero --------------------------------------------------------------- */
.hero {
  display: flex; align-items: center; gap: var(--s4);
  padding-bottom: var(--s4);
  border-bottom: 1px solid var(--line);
  margin-bottom: var(--s4);
}
.hero-face {
  position: relative; flex: none;
  width: 68px; height: 68px;
  border-radius: 50%;
  background: var(--panel-2);
  border: 1px solid var(--line-2);
  overflow: hidden;
}
/* ESPN headshots are cut out on transparency and only 31-42% opaque, so
 * anything behind them shows THROUGH the player's face. Nothing goes behind. */
.hero-face img { width: 100%; height: 100%; object-fit: cover; display: block; }
.hero-id { min-width: 0; }
.hero-name {
  font-family: var(--font-display); text-transform: uppercase;
  font-weight: 700; font-size: 30px; line-height: 1.05;
  letter-spacing: var(--track-display);
  margin: 0 0 2px;
}
.hero-meta {
  display: flex; align-items: center; gap: var(--s2);
  flex-wrap: wrap;
  font-size: var(--fs-sm); color: var(--ink-2);
}
.hero-meta img { width: 18px; height: 18px; object-fit: contain; }
.hero-meta .sep { color: var(--faint); }
.hero-right { margin-left: auto; text-align: right; flex: none; }

.pill {
  display: inline-flex; align-items: center; gap: 6px;
  padding: 3px 9px; border-radius: 999px;
  font-size: var(--fs-micro); font-weight: 600;
  letter-spacing: var(--track-lab); text-transform: uppercase;
  border: 1px solid var(--line-2); background: var(--panel-2); color: var(--ink-2);
}
.pill-out   { background: color-mix(in srgb, var(--under) 16%, transparent);
              border-color: color-mix(in srgb, var(--under) 45%, transparent);
              color: var(--under); }
.pill-doubt { background: color-mix(in srgb, var(--neutral) 15%, transparent);
              border-color: color-mix(in srgb, var(--neutral) 42%, transparent);
              color: var(--neutral); }
.pill-ok    { background: color-mix(in srgb, var(--over) 14%, transparent);
              border-color: color-mix(in srgb, var(--over) 40%, transparent);
              color: var(--over); }

/* ---- Stat tabs ---------------------------------------------------------- */
/* Horizontally scrollable rather than wrapped: a wrapping row of tabs changes
 * the page height when you switch stat, which makes the chart jump. */
.stat-tabs {
  display: flex; gap: var(--s2);
  overflow-x: auto; scrollbar-width: none;
  padding-bottom: var(--s2);
  margin-bottom: var(--s3);
  border-bottom: 1px solid var(--line);
}
.stat-tabs::-webkit-scrollbar { display: none; }
.stat-tab {
  flex: none; cursor: pointer;
  display: grid; gap: 1px; justify-items: start;
  padding: 7px var(--s3);
  border: 1px solid var(--line); border-radius: var(--r-chip);
  background: var(--panel); color: var(--ink-2);
  transition: background var(--t-fast), border-color var(--t-fast),
              color var(--t-fast);
}
.stat-tab:hover { background: var(--panel-3); color: var(--ink); }
.stat-tab[aria-pressed="true"] {
  background: var(--accent-weak); border-color: var(--accent);
  color: var(--ink);
}
.stat-tab .t-label {
  font-size: var(--fs-micro); font-weight: 700;
  letter-spacing: var(--track-lab); text-transform: uppercase;
}
/* ---- The rung menu on a tab ---------------------------------------------
 *
 * The strip shows each market's main line; the caret beside it opens that
 * market's goblins and demons. Clicking the tab itself still just shows the
 * market, which is why the caret is a sibling rather than something inside
 * the tab -- a button inside a button is invalid and Chrome drops it.
 */
.stat-tab-wrap { position: relative; flex: none; display: flex; }
.stat-tab-wrap.has-rungs .stat-tab {
  border-top-right-radius: 0; border-bottom-right-radius: 0;
  border-right: 0;
}
.rung-caret {
  flex: none; cursor: pointer;
  width: 22px;
  display: grid; place-items: center;
  padding: 0; font-size: 10px; line-height: 1;
  color: var(--dim); background: var(--panel);
  border: 1px solid var(--line); border-left: 1px solid var(--line);
  border-radius: 0 var(--r-chip) var(--r-chip) 0;
}
.rung-caret:hover { color: var(--ink); background: var(--panel-3); }
.rung-caret[aria-expanded="true"] {
  color: var(--accent); background: var(--accent-weak);
  border-color: var(--accent);
}
.stat-tab[aria-pressed="true"] + .rung-caret {
  background: var(--accent-weak); border-color: var(--accent);
}

/* A tab showing a rung says so, instead of silently printing a number that
   is not the market's main line. */
.stat-tab.is-rung .t-line { color: var(--accent); }
.t-rung {
  font-size: 8px; font-weight: 700; letter-spacing: var(--track-lab);
  padding: 1px 4px; border-radius: 3px; vertical-align: 1px;
}
.t-rung.mod-goblin, .ri-mod.mod-goblin {
  color: var(--over); background: color-mix(in srgb, var(--over) 18%, transparent);
}
.t-rung.mod-demon, .ri-mod.mod-demon {
  color: var(--under); background: color-mix(in srgb, var(--under) 18%, transparent);
}
.ri-mod.mod-main {
  color: var(--ink-2); background: var(--panel-3);
}

/* FIXED, not absolute.
 *
 * .stat-tabs is a horizontal scroll container, and overflow-x: auto clips on
 * BOTH axes -- an absolutely positioned menu inside it was cut off at the
 * strip's bottom edge. A fixed element escapes an ancestor's overflow (no
 * ancestor here creates a containing block with transform or filter), so the
 * menu is placed from the caret's own rect when it opens. */
.rung-menu {
  position: fixed; z-index: 60;
  min-width: 190px;
  display: grid;
  padding: var(--s1);
  background: var(--panel-2);
  border: 1px solid var(--line-2); border-radius: var(--r-card);
  box-shadow: 0 10px 28px rgb(0 0 0 / 55%);
}
.rung-menu[hidden] { display: none; }
/* Scrollable, and it keeps the scroll to itself.
 *
 * A ladder runs to seven rungs and the menu had no height limit, so on a
 * phone it ran off the screen and a drag on it scrolled the PAGE -- which
 * fired the window scroll handler that closes the menu. Touching it made it
 * disappear. Paul: "as soon as you touch it it dissapears, it needs to let
 * you scroll up or down on the drop downs."
 *
 * overscroll-behavior: contain stops a drag that reaches the end of the list
 * from continuing into the page and closing it that way. */
.rung-menu {
  max-height: min(60vh, 340px);
  overflow-y: auto;
  overscroll-behavior: contain;
  -webkit-overflow-scrolling: touch;
}
.rung-item {
  display: flex; align-items: center; gap: var(--s2);
  padding: 6px var(--s2); min-height: 34px;
  cursor: pointer; text-align: left;
  background: none; border: 0; border-radius: var(--r-chip);
  color: var(--ink); font: inherit;
}
.rung-item:hover { background: var(--panel-3); }
.rung-item[aria-current="true"] {
  background: var(--accent-weak);
  box-shadow: inset 2px 0 0 var(--accent);
}
.ri-line {
  font-family: var(--font-num); font-variant-numeric: tabular-nums;
  font-size: var(--fs-sm); font-weight: 600; min-width: 34px;
}
.ri-mod {
  font-size: var(--fs-micro); font-weight: 700;
  text-transform: uppercase; letter-spacing: var(--track-lab);
  padding: 1px 5px; border-radius: 3px;
}
.ri-pay {
  margin-left: auto;
  font-size: var(--fs-micro); color: var(--faint);
  font-family: var(--font-num);
}

.stat-tab .t-line {
  font-family: var(--font-num); font-variant-numeric: tabular-nums;
  font-size: var(--fs-sm); font-weight: 600; color: var(--ink);
}

/* ---- Figure strip ------------------------------------------------------- */
.figs {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(96px, 1fr));
  gap: 1px;
  background: var(--line);
  border: 1px solid var(--line); border-radius: var(--r-card);
  overflow: hidden;
  margin-bottom: var(--s4);
}
.fig-cell { background: var(--panel); padding: var(--s3); }
.fig-cell .lab { display: block; margin-bottom: 3px; }
.fig-cell .v {
  font-family: var(--font-num); font-variant-numeric: tabular-nums;
  font-size: 19px; font-weight: 600; line-height: 1.1;
}
.fig-cell .v.pos { color: var(--over); }
.fig-cell .v.neg { color: var(--under); }
.fig-cell .sub { font-size: var(--fs-micro); color: var(--faint); }

/* ---- Cards -------------------------------------------------------------- */
.card {
  background: var(--panel); border: 1px solid var(--line);
  border-radius: var(--r-card);
  overflow: hidden;
}
.card-head {
  display: flex; align-items: baseline; gap: var(--s2);
  padding: var(--s3) var(--s3) 0;
}
.card-head h2, .card-head h3 {
  margin: 0; font-size: var(--fs-sm);
  letter-spacing: var(--track-lab);
}
.card-head .micro { margin-left: auto; }

/* The rung switcher sits where the line used to be printed, because it IS
 * the line -- it says which number the card is about and lets you change it.
 * Styled as a control rather than as text so it reads as one. */
.card-head .rung-pick { margin-left: auto; }
.rung-pick {
  appearance: none;
  padding: 4px 26px 4px 10px; min-height: 30px;
  font-family: var(--font-mono); font-size: var(--fs-sm);
  color: var(--ink); background: var(--panel-2);
  border: 1px solid var(--line); border-radius: var(--r-chip);
  cursor: pointer;
  /* The chevron, drawn rather than fetched: one more request for 9 bytes of
     triangle is not worth it. */
  background-image: linear-gradient(45deg, transparent 50%, var(--dim) 50%),
                    linear-gradient(135deg, var(--dim) 50%, transparent 50%);
  background-position: calc(100% - 14px) 13px, calc(100% - 9px) 13px;
  background-size: 5px 5px, 5px 5px;
  background-repeat: no-repeat;
}
.rung-pick:hover { border-color: var(--line-2); }
.rung-pick:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }

/* The ladder card's rows go to the same place, so they have to look like
   they go somewhere. */
.mini tr[data-rung-go] { cursor: pointer; }
.mini tr[data-rung-go]:hover td { background: var(--panel-2); }
.mini tr.is-current td { background: var(--panel-2); color: var(--ink); }
.mini tr.is-current td:first-child { box-shadow: inset 2px 0 0 var(--accent); }
.card-body { padding: var(--s3); }
.card-note {
  padding: var(--s2) var(--s3) var(--s3);
  font-size: var(--fs-micro); color: var(--faint); line-height: 1.45;
}

/* ---- Charts ------------------------------------------------------------- */
/* Hand-drawn SVG. There is no build step and no charting library, and for bars
 * against a threshold a library would be more code than the thing it draws. */
.chart-card { margin-bottom: var(--s4); }
.chart { display: block; width: 100%; height: auto; }
.chart .bar { transition: opacity var(--t-fast); }
.chart .bar:hover { opacity: 0.82; }
.chart .bar-over  { fill: var(--over); }
.chart .bar-under { fill: color-mix(in srgb, var(--under) 72%, var(--panel-2)); }
.chart .bar-push  { fill: var(--neutral); }

/* A bar with no line to beat.
 *
 * Supporting stats used to fall through to the push amber, which reads as a
 * caution state for what is only a number. Orange is the house colour for
 * "this is what he did", and it is the same orange that fills a paired bar --
 * so a Minutes chart and a Field Goals chart are obviously the same family.
 */
.chart .bar-plain { fill: color-mix(in srgb, var(--accent) 74%, var(--panel)); }
.chart .bar:hover .bar-plain { fill: var(--accent); }
.chart .bar-h2h   { stroke: var(--accent); stroke-width: 1.5; }
.chart .bar-val {
  font-family: var(--font-num); font-size: 10px; font-weight: 600;
  fill: var(--ink-2); text-anchor: middle;
  /* A halo in the panel colour, so the dashed line rule passes BEHIND the
   * digits of a bar sitting near the line rather than through them. */
  stroke: var(--panel); stroke-width: 2.5px; paint-order: stroke fill;
}
/* "4/6" is two numbers where one used to be, so it gets a shade less width
   per digit. The made half is the one being read, so it keeps full weight
   and the pair stays one node rather than two that could collide. */
.chart .bar-val-pair { font-size: 9.5px; letter-spacing: -0.2px; }
.chart .x-lab {
  font-family: var(--font); font-size: 9.5px; fill: var(--faint);
  text-anchor: middle;
}
.chart .x-lab.is-h2h { fill: var(--accent); font-weight: 600; }
.chart .line-rule {
  stroke: var(--ink); stroke-width: 1.25; stroke-dasharray: 4 3;
  opacity: 0.85;
}
.chart .line-tag {
  font-family: var(--font-num); font-size: 10px; font-weight: 700;
  fill: var(--bg);
}
.chart .grid-rule { stroke: var(--line); stroke-width: 1; }
.chart .y-lab {
  font-family: var(--font-num); font-size: 9.5px; fill: var(--faint);
  text-anchor: end;
}

/* Distribution: the simulated outcomes, not a fitted curve. */
.dist .d-bar-over  { fill: var(--over); }
.dist .d-bar-under { fill: color-mix(in srgb, var(--under) 60%, var(--panel-2)); }
.dist .d-line { stroke: var(--ink); stroke-width: 1.25; stroke-dasharray: 3 2; }
.dist .d-band { fill: color-mix(in srgb, var(--accent) 12%, transparent); }
.dist .d-lab {
  font-family: var(--font-num); font-size: 9px; fill: var(--faint);
  text-anchor: middle;
}

/* Line movement: a step chart, because append-on-change means every vertex is
 * a real change. Joining the points with slopes would draw a line drifting
 * between observations, which is not what happened. */
.move .m-step { stroke: var(--accent); stroke-width: 1.75; fill: none; }
.move .m-dot  { fill: var(--accent); }
.move .m-lab  {
  font-family: var(--font-num); font-size: 9px; fill: var(--faint);
}

/* ---- Model card --------------------------------------------------------- */
.sim-top {
  display: flex; align-items: center; gap: var(--s3);
  margin-bottom: var(--s3);
}
.sim-big {
  font-family: var(--font-num); font-variant-numeric: tabular-nums;
  font-size: 34px; font-weight: 600; line-height: 1;
}
.sim-big.over  { color: var(--over); }
.sim-big.under { color: var(--under); }
.sim-side {
  font-family: var(--font-display); text-transform: uppercase;
  font-size: var(--fs-sm); font-weight: 700; letter-spacing: var(--track-lab);
  color: var(--ink-2);
}
.sim-proj { margin-left: auto; text-align: right; }
.sim-proj .v {
  font-family: var(--font-num); font-variant-numeric: tabular-nums;
  font-size: 19px; font-weight: 600;
}

/* A held-back stat says so, rather than leaving a blank that reads as a
 * loading state or, worse, as a confident zero. */
.held {
  display: grid; gap: var(--s2);
  padding: var(--s3);
  border: 1px dashed var(--line-2); border-radius: var(--r-chip);
  background: var(--panel-2);
  font-size: var(--fs-micro); color: var(--ink-2); line-height: 1.5;
}
.held strong { color: var(--ink); }

/* ---- Small data tables (prices, ladder, log) ---------------------------- */
.mini { width: 100%; border-collapse: collapse; font-size: var(--fs-sm); }
.mini th {
  text-align: left; padding: 0 var(--s2) var(--s1);
  font-size: var(--fs-micro); letter-spacing: var(--track-lab);
  text-transform: uppercase; color: var(--dim); font-weight: 600;
  white-space: nowrap;
}
.mini td {
  padding: 5px var(--s2);
  border-top: 1px solid var(--line);
  white-space: nowrap;
}
.mini td.num, .mini th.num { text-align: right; }
.mini tbody tr:hover { background: var(--panel-2); }
.mini .best { color: var(--over); font-weight: 600; }
.mini .book { color: var(--ink); font-weight: 500; }
.mini .is-pp .book { color: var(--accent); }

.mod-tag {
  display: inline-block; min-width: 54px;
  font-size: var(--fs-micro); font-weight: 700;
  letter-spacing: var(--track-lab); text-transform: uppercase;
}
.mod-goblin { color: var(--over); }
.mod-demon  { color: var(--under); }
.mod-main   { color: var(--ink-2); }

/* Hit rate pills, matching the board. Centred rather than right-aligned: the
 * pill has a min-width with the text centred inside it, so right-aligning the
 * cell and centring the text left them off their headers. */
.rates-wrap { display: grid; gap: var(--s2); }
/* The toggle sits under the windows, not beside them: the strip already
   fills the row, and a control squeezed onto the end of it is the first
   thing to wrap badly on a phone. */
.rates-wrap .sup-mode { margin-left: 0; justify-content: flex-start; }
.rates { display: flex; gap: 6px; flex-wrap: wrap; }
/* These are the chart's controls, so they have to look like controls. They
 * were styled as read-outs and behaved as buttons, which is the worst of both
 * -- nothing invited a click, so nobody made one. */
.rate {
  flex: 1 1 0; min-width: 62px;
  display: grid; gap: 2px; justify-items: center;
  padding: 6px 4px;
  border: 1px solid var(--line); border-radius: var(--r-chip);
  background: var(--panel-2);
  text-align: center;
  font: inherit; color: inherit;
  cursor: pointer;
  position: relative;
  transition: background var(--t-fast), border-color var(--t-fast),
              transform var(--t-fast);
}
.rate:hover:not(:disabled) {
  background: var(--panel-3);
  border-color: var(--line-2);
  transform: translateY(-1px);
}
.rate:active:not(:disabled) { transform: none; }
.rate:disabled { cursor: default; opacity: 0.45; }

/* The selected window, marked the same way the filter chips are so the whole
 * page has one vocabulary for "this is on". */
.rate[aria-pressed="true"] {
  background: var(--accent-weak);
  border-color: var(--accent);
}
.rate[aria-pressed="true"]::after {
  content: ""; position: absolute; left: 50%; bottom: -1px;
  width: 22px; height: 2px; transform: translateX(-50%);
  background: var(--accent); border-radius: 2px;
}
.rate[aria-pressed="true"] .lab { color: var(--ink); }
.rate .lab { font-size: 9.5px; }
.rate .v {
  font-family: var(--font-num); font-variant-numeric: tabular-nums;
  font-size: var(--fs-body); font-weight: 600;
}
.rate .n { font-size: 9.5px; color: var(--faint); }
.rate.hot  { border-color: color-mix(in srgb, var(--over) 45%, transparent); }
.rate.hot  .v { color: var(--over); }
.rate.cold { border-color: color-mix(in srgb, var(--under) 45%, transparent); }
.rate.cold .v { color: var(--under); }

.log-wrap { max-height: 420px; overflow-y: auto; }
.log-wrap .mini thead th {
  position: sticky; top: 0; z-index: 1;
  background: var(--panel); padding-top: var(--s2);
}
.log-hit   { color: var(--over); font-weight: 600; }
.log-miss  { color: var(--under); }
.log-h2h td { background: color-mix(in srgb, var(--accent) 7%, transparent); }

.matchup-row {
  display: flex; align-items: baseline; gap: var(--s2);
  padding: 6px 0;
  border-top: 1px solid var(--line);
  font-size: var(--fs-sm);
}
.matchup-row:first-child { border-top: 0; }
.matchup-row .k { color: var(--ink-2); }
.matchup-row .v {
  margin-left: auto;
  font-family: var(--font-num); font-variant-numeric: tabular-nums;
  font-weight: 600;
}
.rank-tag {
  font-family: var(--font-num); font-size: var(--fs-micro); font-weight: 700;
  padding: 1px 5px; border-radius: 4px;
  background: var(--panel-3); color: var(--ink-2);
}
/* Top ten most allowed is a green light, bottom ten a red one. */
.rank-tag.soft  { background: color-mix(in srgb, var(--over) 18%, transparent);
                  color: var(--over); }
.rank-tag.tough { background: color-mix(in srgb, var(--under) 18%, transparent);
                  color: var(--under); }

.inj-row {
  display: flex; align-items: center; gap: var(--s2);
  padding: 5px 0; border-top: 1px solid var(--line);
  font-size: var(--fs-sm);
}
.inj-row:first-child { border-top: 0; }
.inj-row .who { min-width: 0; overflow: hidden; text-overflow: ellipsis;
                white-space: nowrap; }
.inj-row .st { margin-left: auto; flex: none; }

.back {
  display: inline-flex; align-items: center; gap: 6px;
  font-size: var(--fs-sm); color: var(--ink-2); font-weight: 500;
  margin-bottom: var(--s3);
}
.back:hover { color: var(--ink); }

.empty {
  padding: var(--s4) var(--s3); text-align: center;
  font-size: var(--fs-sm); color: var(--faint);
}

/* ---- Tablet: the rail drops under the chart ----------------------------- */
@media (max-width: 1180px) {
  .detail {
    grid-template-columns: minmax(0, 1fr);
    padding: var(--s4) var(--s4) var(--s6);
  }
  /* No longer a sibling column, so it must stop being sticky and stop owning
   * its own scrollport, or it becomes a short scrolling box inside the page. */
  .rail {
    position: static; max-height: none; overflow: visible;
    grid-template-columns: repeat(auto-fit, minmax(290px, 1fr));
  }
}

/* ---- Phone ------------------------------------------------------------- */
/* A card head is a row of controls, and on a phone it is a row too long.
 *
 * The chart card's head carries the stat, the rung switcher, and the model's
 * OVER and UNDER buttons. .card is overflow: hidden, so at 390px the UNDER
 * button -- half of the model's actual opinion -- was simply cut off the
 * right edge rather than wrapping. Nothing in the row is optional, so the
 * row wraps. */
@media (max-width: 720px) {
  .card-head { flex-wrap: wrap; row-gap: var(--s2); }
  /* margin-left:auto on the first wrapped item would push it to the far edge
     and leave a hole; on a wrapped row it should just follow the heading. */
  .card-head .rung-pick, .card-head .micro { margin-left: 0; }
  .chart-card .card-head h2 { flex-basis: 100%; }
}

@media (max-width: 720px) {
  .detail { padding: var(--s3) var(--s3) var(--s6); gap: var(--s4); }
  .rail { grid-template-columns: minmax(0, 1fr); }

  /* The status pill drops to its own line below the name. It is width:100%,
   * so without wrapping it stayed beside the headshot and pushed the page 74px
   * wider than the viewport -- a sideways-scrolling page on a phone. */
  .hero { gap: var(--s3); align-items: flex-start; flex-wrap: wrap; }
  .hero-face { width: 50px; height: 50px; }
  .hero-name { font-size: 22px; }
  .hero-right { width: 100%; margin: var(--s2) 0 0; text-align: left; }

  /* NOT pinned. It was, and the reasoning was that the chart is what the
   * page is for, so it should stay put while the cards below it scroll.
   *
   * In portrait that is the wrong trade: the chart takes most of a phone
   * screen, so pinning it leaves a few lines for everything else and the
   * supporting stats never get enough room to read. Paul: "it doesnt leave
   * enough room to see anything else like the supported stats".
   *
   * It keeps the full-bleed treatment, which is what makes it feel like the
   * subject of the page -- edge to edge, no side borders -- and simply
   * scrolls away like everything else. */
  .chart-card {
    margin: 0 calc(-1 * var(--s3)) var(--s3);
    border-radius: 0; border-left: 0; border-right: 0;
    background: var(--panel);
  }
  .figs { grid-template-columns: repeat(2, 1fr); }
  .fig-cell .v { font-size: 17px; }
  .rate { min-width: 52px; }
  .log-wrap { max-height: none; }
}

/* The short-screen unpin that used to live here is gone with the pinning
 * itself: the chart is static on every phone now, portrait and landscape. */

@media (prefers-reduced-motion: reduce) {
  .chart .bar, .stat-tab, .btn { transition: none; }
}

/* Add-to-slip, on the chart header. Both sides offered rather than one star:
 * you have just read the distribution, so the page should not assume a side. */
.add-sides { display: flex; align-items: center; gap: var(--s1);
             margin-left: var(--s2); }
/* One word, so the pair below the chart and the pair in the header are not
 * read as the same kind of number. Muted: it is a caption, not a value. */
.add-sides-lab {
  font-size: var(--fs-micro); font-weight: 700; color: var(--ink-3);
  letter-spacing: var(--track-lab); text-transform: uppercase;
  white-space: nowrap; cursor: help;
}
.add-side {
  display: inline-flex; align-items: center; gap: 5px;
  padding: 4px var(--s2);
  border: 1px solid var(--line-2); border-radius: var(--r-chip);
  background: var(--panel-2); color: var(--ink-2);
  font-size: var(--fs-micro); font-weight: 700;
  letter-spacing: var(--track-lab); text-transform: uppercase;
  cursor: pointer; white-space: nowrap;
  transition: background var(--t-fast), border-color var(--t-fast),
              color var(--t-fast);
}
.add-side:hover { background: var(--panel-3); color: var(--ink); }
.add-over[aria-pressed="true"] {
  background: color-mix(in srgb, var(--over) 20%, transparent);
  border-color: var(--over); color: var(--over);
}
.add-under[aria-pressed="true"] {
  background: color-mix(in srgb, var(--under) 20%, transparent);
  border-color: var(--under); color: var(--under);
}
@media (max-width: 560px) {
  .card-head .add-sides { margin-left: 0; width: 100%; }
  .add-side { flex: 1; justify-content: center; }
  /* The label keeps its width; only the buttons stretch. */
  .add-sides-lab { flex: 0 0 auto; }
}

/* Free square: a promotion, not this player's line. */
.free-tag {
  display: inline-flex; align-items: center; cursor: help;
  padding: 2px 7px; border-radius: var(--r-pill);
  font-size: var(--fs-micro); font-weight: 700;
  letter-spacing: var(--track-lab); text-transform: uppercase;
  color: var(--neutral);
  background: color-mix(in srgb, var(--neutral) 15%, transparent);
  border: 1px solid color-mix(in srgb, var(--neutral) 42%, transparent);
}
.stat-tab.is-free .t-label { color: var(--neutral); }
.stat-tab.is-free[aria-pressed="true"] {
  border-color: var(--neutral);
  background: color-mix(in srgb, var(--neutral) 14%, transparent);
}

/* ---- Filter panel -------------------------------------------------------
 * Collapsed to a single row until opened. The whole page re-cuts when it
 * changes, so it sits directly above the figures it governs rather than in the
 * rail -- a filter you cannot see while reading the number it produced is how
 * people misread a 2-of-6 as a 2-of-10.
 */
.filters {
  border: 1px solid var(--line); border-radius: var(--r-card);
  background: var(--panel);
  margin-bottom: var(--s4);
  overflow: hidden;
}
.filters-bar {
  display: flex; align-items: center; gap: var(--s2);
  padding: var(--s2) var(--s3);
  flex-wrap: wrap;
}
.f-toggle {
  display: inline-flex; align-items: center; gap: 6px;
  padding: 6px var(--s3);
  border: 1px solid var(--line-2); border-radius: var(--r-chip);
  background: var(--panel-2); color: var(--ink);
  font-size: var(--fs-micro); font-weight: 700;
  letter-spacing: var(--track-lab); text-transform: uppercase; cursor: pointer;
}
.f-toggle:hover { background: var(--panel-3); }
.f-count {
  min-width: 16px; padding: 0 4px; border-radius: var(--r-pill);
  background: var(--accent); color: var(--accent-ink); font-size: 9px;
}
.f-group { display: flex; gap: 2px; }
.f-chip {
  padding: 6px var(--s3);
  border: 1px solid var(--line); border-radius: var(--r-chip);
  background: transparent; color: var(--ink-2);
  font-size: var(--fs-micro); font-weight: 600; cursor: pointer;
}
.f-chip:hover { background: var(--panel-3); color: var(--ink); }
.f-chip[aria-pressed="true"] {
  background: var(--accent); border-color: var(--accent); color: var(--accent-ink);
}
.f-reset {
  border: 0; background: none; color: var(--accent);
  font-size: var(--fs-micro); font-weight: 600; cursor: pointer;
}
.f-games { margin-left: auto; color: var(--dim); }

/* Collapsed by default. display:none rather than height:0 so the checkboxes
 * inside are not tabbable while hidden. */
.filters-body { display: none; }
.filters[data-open="1"] .filters-body {
  display: grid;
  grid-template-columns: 1fr 1fr auto;
  gap: var(--s4);
  padding: var(--s3) var(--s3) var(--s4);
  border-top: 1px solid var(--line);
}
.f-col { min-width: 0; display: grid; gap: var(--s2); align-content: start; }
.f-narrow { min-width: 150px; }
.mates {
  display: grid; gap: 2px;
  max-height: 210px; overflow-y: auto;
  padding-right: 2px;
}
.mate {
  display: flex; align-items: center; gap: var(--s2);
  padding: 4px var(--s2);
  border: 1px solid transparent; border-radius: var(--r-chip);
  font-size: var(--fs-sm); cursor: pointer;
}
.mate:hover { background: var(--panel-2); }
.mate.is-on { border-color: var(--accent); background: var(--accent-weak); }
.mate input { accent-color: var(--accent); flex: none; }
.mate-name {
  min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.mate-min { margin-left: auto; color: var(--dim); font-size: var(--fs-micro); }
.mate-status {
  flex: none; font-size: 9px; font-weight: 700; text-transform: uppercase;
  padding: 1px 5px; border-radius: var(--r-pill);
  border: 1px solid currentColor;
}
.f-range { display: flex; align-items: center; gap: var(--s2); }
.f-col input[type="number"] {
  width: 100%; min-width: 0;
  padding: 6px var(--s2);
  background: var(--panel-2); color: var(--ink);
  border: 1px solid var(--line-2); border-radius: var(--r-chip);
  font: inherit; font-size: var(--fs-sm);
  font-family: var(--font-num);
}

/* ---- Supporting stats --------------------------------------------------- */
.sup-card { margin-top: var(--s4); }
/* A real tap target. These inherit .segb from app.css now, which is sized
   for a dense desktop filter bar; on a card on a phone they are a thumb's
   job. */
.sup-mode { margin-left: auto; display: flex; gap: 2px; }
.sup-mode .segb { min-height: 36px; padding-inline: var(--s3); }
@media (max-width: 860px) { .sup-mode .segb { min-height: 44px; } }
.sup-tabs {
  display: flex; gap: var(--s2); overflow-x: auto; scrollbar-width: none;
  padding: var(--s2) var(--s3) 0;
}
.sup-tabs::-webkit-scrollbar { display: none; }
.sup-tab {
  flex: none; display: grid; gap: 2px; justify-items: start;
  padding: var(--s2) var(--s3);
  border: 0; border-bottom: 2px solid transparent;
  background: none; color: var(--ink-2); cursor: pointer; text-align: left;
}
.sup-tab:hover { color: var(--ink); }
.sup-tab[aria-pressed="true"] {
  color: var(--ink); border-bottom-color: var(--accent);
}
.sup-label {
  font-size: var(--fs-micro); font-weight: 700;
  letter-spacing: var(--track-lab); text-transform: uppercase;
}
.sup-val {
  font-family: var(--font-num); font-variant-numeric: tabular-nums;
  font-size: var(--fs-sm); font-weight: 600; white-space: nowrap;
}
.sup-sub { display: block; font-size: var(--fs-micro); color: var(--faint); }

@media (max-width: 900px) {
  .filters[data-open="1"] .filters-body { grid-template-columns: 1fr; }
  .mates { max-height: 170px; }
}

/* ---- On court / off court ----------------------------------------------- */
.oo-wrap { overflow-x: auto; }
.oo { min-width: 560px; }
.oo .oo-group th {
  padding-bottom: 2px; color: var(--ink-2);
  font-size: 9.5px; text-align: center;
  border-bottom: 1px solid var(--line);
}
.oo .oo-sep { border-left: 1px solid var(--line-2); }
.oo-name {
  max-width: 150px; overflow: hidden; text-overflow: ellipsis;
  white-space: nowrap; color: var(--ink);
}
.oo-delta { font-weight: 700; }
.oo-delta.up { color: var(--over); }
.oo-delta.down { color: var(--under); }

/* The old shot-chart styles stood here. The card was rebuilt as a
 * twelve-zone matchup chart and its styles live at the bottom of this
 * file; the old ones were not merely unused, they outranked the new
 * ones -- `.sc-sw.sc-neutral` beats `.sc-neutral` on specificity, so a
 * dead rule was still painting a live swatch. */

/* ---- Model record -------------------------------------------------------- */
.model-grid { display: grid; gap: var(--s4); max-width: 1000px; }
.model-table td, .model-table th { white-space: nowrap; }
.model-table tr.mismatch { background: color-mix(in srgb, var(--neutral) 9%, transparent); }
.over-bar { color: var(--under); font-weight: 700; }
.ship-on  { color: var(--over); font-weight: 600; }
.ship-off { color: var(--dim); }
.mismatch-card { border-color: color-mix(in srgb, var(--neutral) 45%, transparent); }
.mismatch-list {
  margin: 0; padding: 0 0 0 var(--s4);
  font-size: var(--fs-sm); color: var(--ink-2); line-height: 1.6;
}
.mismatch-list strong { color: var(--ink); }
.mismatch-list em { color: var(--neutral); font-style: normal; font-weight: 600; }

/* ---- Opportunity: potential assists and rebound chances ------------------ */
/* Conversion is the number to read, so it is the one that gets the colour.
 * The raw figures sit beside it because the interesting cases are where the
 * two disagree: chances holding while conversion collapses is the shape of a
 * line that has drifted too far. */
.track-table th[colspan] {
  text-align: center;
  border-bottom: 1px solid var(--line-2);
  padding-bottom: 2px;
}
.track-table .w-label { white-space: nowrap; color: var(--ink-2); }
.track-table .conv { color: var(--ink); font-weight: 600; }

.track-log { margin-top: var(--s3); }
.track-log-head { margin-bottom: var(--s1); }
.track-bars {
  display: flex; align-items: flex-end; gap: 3px;
  height: 64px; margin: 0 0 var(--s1); padding: 0; list-style: none;
}
.track-bar { flex: 1 1 0; height: 100%; display: flex; align-items: flex-end; }
/* The outer bar is the opportunity, the inner is what he took. A tall outer
 * with a short inner is the whole point of the section. */
.track-outer {
  width: 100%;
  background: color-mix(in srgb, var(--accent) 22%, transparent);
  border-radius: 2px;
  display: flex; align-items: flex-end;
}
.track-inner {
  width: 100%;
  background: var(--accent);
  border-radius: 2px;
}
/* A short season should not produce slabs: bars stay bar-shaped whether
 * there are five nights or fifteen, and the group stays left-aligned rather
 * than stretching to fill. */
.track-bars { justify-content: flex-start; }
.track-bar { max-width: 34px; min-width: 6px; }

/* The window ("last 10", "all 82") beside a chart heading. It is a qualifier,
   not part of the stat's name, so it is set apart instead of joined on with a
   dash. */
.h2-sub {
  color: var(--dim); font-weight: 400;
  letter-spacing: 0; text-transform: none;
}

/* ---- Paired bars: opportunity, and what came of it ----------------------
 *
 * The outer bar is the chance and the filled bar inside it is the result. A
 * single stacked bar would say the same thing arithmetically and read
 * completely differently: the eye compares the heights of adjacent TOTALS, so
 * stacking makes a 10-chance 2-rebound night look like a small night, when the
 * point is that it was a big night with nothing to show for it.
 */
.chart .bar-outer {
  fill: color-mix(in srgb, var(--ink-2) 14%, transparent);
  stroke: color-mix(in srgb, var(--ink-2) 34%, transparent);
  stroke-width: 1;
}
.chart .bar-inner { fill: color-mix(in srgb, var(--accent) 74%, var(--panel)); }
.chart .bar-outer.bar-h2h { stroke: var(--accent); }
.chart .bar-conv {
  font-family: var(--font-num); font-size: 9px; font-weight: 600;
  fill: var(--accent-ink); text-anchor: middle;
}
/* The games where the feed credits more results than chances. Marked rather
   than hidden, because a reader who notices it should find it acknowledged. */
.chart .bar-conv.over { fill: var(--neutral); }
/* The hover target is the whole column, so a one-rebound night is not a 6px
   target. Sits above the bars and paints the column behind them on hover. */
.chart .bar-hit { cursor: default; }
.chart .bar:hover .bar-hit { fill: color-mix(in srgb, var(--ink) 6%, transparent); }
.chart .bar:hover .bar-outer { stroke: var(--ink-2); }

/* ---- Matchup card: defense, with a position toggle ---------------------- */
.pos-tabs {
  display: flex; gap: var(--s1); padding: 0 var(--s4) var(--s2);
  border-bottom: 1px solid var(--line);
}
.pos-tab {
  appearance: none; background: none; border: 0; cursor: pointer;
  padding: var(--s2) var(--s2) var(--s3);
  font-family: var(--font-display); text-transform: uppercase;
  letter-spacing: var(--track-lab); font-size: var(--fs-lab); font-weight: 600;
  color: var(--dim); border-bottom: 2px solid transparent;
  margin-bottom: -1px; transition: color var(--t-fast) var(--ease);
}
.pos-tab:hover { color: var(--ink-2); }
.pos-tab[aria-selected="true"] { color: var(--ink); border-bottom-color: var(--accent); }
/* The player's own position, so it is findable without reading every tab. */
.pos-tab[data-own] { color: var(--ink-2); }
.pos-tab[data-own][aria-selected="true"] { color: var(--ink); }


/* ======================================================================
 * Mobile
 * ====================================================================== */

/* The chart on a phone.
 *
 * The compact viewBox already puts every length close to its rendered size,
 * so these are real pixel sizes rather than numbers chosen to survive being
 * shrunk by half. The value above each bar is the thing people read, so it is
 * the one that gets bigger.
 */
.chart-compact .bar-val { font-size: 13px; font-weight: 700; }
.chart-compact .x-lab   { font-size: 9.5px; }
.chart-compact .y-lab   { font-size: 9px; }
.chart-compact .bar-conv { font-size: 10px; }
.chart-compact .line-tag { font-size: 11px; }

@media (max-width: 720px) {
  /* ---- The stat picker has to look like one ---------------------------
   * There is no hover on a phone, so an affordance that depends on it is no
   * affordance at all. Three things say "this is a row of controls you can
   * drag": the tabs are raised off the page rather than outlined on it, the
   * selected one is filled rather than tinted, and the strip fades at the
   * right edge so it is visible that something is cut off.
   */
  .stat-tabs {
    position: relative;
    gap: var(--s2); padding: var(--s1) 0 var(--s3);
    scroll-snap-type: x proximity;
    -webkit-overflow-scrolling: touch;
    /* The fade. A mask rather than an overlay, so it works on any background
       and never intercepts a tap. */
    -webkit-mask-image: linear-gradient(to right, #000 calc(100% - 28px), transparent);
            mask-image: linear-gradient(to right, #000 calc(100% - 28px), transparent);
  }
  .stat-tabs.at-end {
    -webkit-mask-image: none; mask-image: none;
  }
  .stat-tab {
    scroll-snap-align: start;
    padding: var(--s2) var(--s3);
    min-height: 44px;              /* a real touch target */
    background: var(--panel-2);
    border-color: var(--line-2);
    box-shadow: 0 1px 0 rgb(0 0 0 / 0.35);
  }
  .stat-tab:active { transform: translateY(1px); }
  .stat-tab[aria-pressed="true"] {
    background: var(--accent); border-color: var(--accent);
  }
  .stat-tab[aria-pressed="true"] .t-label,
  .stat-tab[aria-pressed="true"] .t-line { color: var(--accent-ink); }

  /* ---- Filters open as a sheet ---------------------------------------
   * A panel that pushes the page down moves the chart out from under the
   * thumb that opened it. A sheet covers instead of displacing.
   */
  .f-toggle {
    min-height: 40px; padding: 0 var(--s4);
    background: var(--panel-2); border-color: var(--line-2); color: var(--ink);
  }
  .filters[data-open="1"] .filters-body {
    position: fixed; inset: auto 0 0 0; z-index: 90;
    max-height: 78vh; overflow-y: auto;
    padding: var(--s4) var(--s4) var(--s6);
    background: var(--panel);
    border-top: 1px solid var(--line-2);
    border-radius: var(--r-card) var(--r-card) 0 0;
    box-shadow: 0 -18px 40px -20px rgb(0 0 0 / 0.9);
    display: grid; gap: var(--s4); grid-template-columns: minmax(0, 1fr);
  }
  .filters[data-open="1"]::before {
    content: ""; position: fixed; inset: 0; z-index: 89;
    background: var(--scrim);
  }
  .sheet-close {
    position: sticky; bottom: 0;
    width: 100%; min-height: 46px; margin-top: var(--s2);
    background: var(--panel-2); color: var(--ink);
    border: 1px solid var(--line-2); border-radius: var(--r-chip);
    font-family: var(--font-display); text-transform: uppercase;
    letter-spacing: var(--track-lab); font-size: var(--fs-lab);
    font-weight: 600; cursor: pointer;
  }

  /* ---- Long cards collapse ------------------------------------------
   * On/off court is a 15-row table that most people scroll straight past on
   * a phone. Behind a summary it costs one tap and saves a screen of scroll.
   */
  .collapse-card > summary {
    list-style: none; cursor: pointer;
    display: flex; align-items: center; justify-content: space-between;
    gap: var(--s2); padding: var(--s3) var(--s4); min-height: 48px;
  }
  .collapse-card > summary::-webkit-details-marker { display: none; }
  .collapse-card > summary::after {
    content: "\203A"; font-size: 20px; color: var(--dim);
    transform: rotate(90deg); transition: transform var(--t) var(--ease);
  }
  .collapse-card[open] > summary::after { transform: rotate(-90deg); }
  .collapse-card > summary .display { margin: 0; }
}

/* On a wide screen the card is simply always open and the summary is a
   normal heading, so the same markup serves both. */
@media (min-width: 721px) {
  .collapse-card > summary {
    list-style: none; display: flex; align-items: center; gap: var(--s3);
  }
  .collapse-card > summary::-webkit-details-marker { display: none; }
  .collapse-card > summary::after { content: none; }
}

/* Five position tabs instead of three. They have to fit the rail on a desktop
   and the full width of a phone, so they share the row evenly rather than
   sizing to their own text. */
.pos-tabs { gap: 0; }
.pos-tabs .pos-tab { flex: 1 1 0; text-align: center; padding-inline: 2px; }
@media (max-width: 720px) {
  .pos-tabs .pos-tab { min-height: 42px; font-size: var(--fs-micro); }
}

/* ---- Matchup: the opponent's defense, by position ----------------------
 *
 * An inset panel inside the card rather than more rows on it. The tabs change
 * what the panel says, and a visible edge around the part that changes is what
 * makes that relationship readable without a word of explanation.
 */
.matchup-card .card-head { align-items: flex-start; }
.mk-info {
  margin-left: auto; flex: none;
  width: 18px; height: 18px; border-radius: var(--r-pill);
  border: 1px solid var(--line-2); color: var(--dim);
  display: inline-flex; align-items: center; justify-content: center;
  font-size: 11px; font-weight: 700; font-style: italic; cursor: help;
}
.mk-info:hover, .mk-info:focus-visible { color: var(--ink); border-color: var(--ink-2); }

.mk-panel {
  margin: var(--s3) var(--s4) var(--s4);
  border: 1px solid var(--line); border-radius: var(--r-chip);
  background: var(--panel-2); overflow: hidden;
}
.mk-table { width: 100%; border-collapse: collapse; }
.mk-table th, .mk-table td {
  padding: var(--s2) var(--s3); text-align: left; font-weight: 400;
}
.mk-table thead th {
  font-family: var(--font-display); text-transform: uppercase;
  letter-spacing: var(--track-lab); font-size: var(--fs-lab);
  color: var(--dim); border-bottom: 1px solid var(--line);
}
.mk-table .num { text-align: right; }
/* A quiet band, not a heading: it groups the rows under it without competing
   with the card title above. */
.mk-table .mk-section td {
  font-family: var(--font-display); text-transform: uppercase;
  letter-spacing: var(--track-lab); font-size: var(--fs-lab);
  color: var(--ink-2); background: var(--panel-3);
  padding-block: 5px;
}
.mk-table tbody th {
  color: var(--ink-2); font-size: var(--fs-sm);
}
.mk-table tbody td {
  font-family: var(--font-num); font-variant-numeric: tabular-nums;
  color: var(--ink); font-size: var(--fs-sm);
}
.mk-table tbody tr + tr:not(.mk-section) th,
.mk-table tbody tr + tr:not(.mk-section) td { border-top: 1px solid var(--line); }

.mk-rank { display: inline-flex; align-items: center; gap: 5px; justify-content: flex-end; }
/* The verdict badge. A glyph as well as a colour, because roughly one man in
   twelve cannot separate the green from the red. */
.mk-dot {
  width: 14px; height: 14px; border-radius: 4px;
  display: inline-flex; align-items: center; justify-content: center;
  font-size: 9px; font-weight: 700; font-style: normal; line-height: 1;
}
/* The ranked pip: the rank itself, in a circle the colour of its verdict.
   Solid rather than tinted, because the number is now the only thing
   carrying the reading and a 20-percent wash will not hold it.

   DARK type on all three. This shipped with white type and an exception for
   yellow, on the assumption that only yellow was light enough to need one.
   Nobody measured it. White on our green is 1.66:1, which is not a contrast
   ratio so much as the absence of one -- below every bar there is, and Paul
   could not read it: "the light green circle that is beneficial shows white
   text and it cant be read". White on the red is 3.24:1, which scrapes the
   large-bold bar that 13px type does not qualify for anyway.

   Measured against #0B0D10 instead: green 11.73, yellow 10.94, red 6.00.
   All three clear 4.5:1 with room, so there is no exception to carve. */
.mk-pip {
  display: inline-grid; place-items: center;
  min-width: 26px; height: 26px; padding: 0 5px;
  border-radius: var(--r-pill);
  font-family: var(--font-num); font-variant-numeric: tabular-nums;
  font-size: var(--fs-sm); font-weight: 700;
  color: var(--accent-ink);
}
.mk-pip.mk-good { background: var(--over); }
.mk-pip.mk-mid  { background: var(--neutral); }
.mk-pip.mk-bad  { background: var(--under); }

.mk-dot.mk-good { background: color-mix(in srgb, var(--over) 22%, transparent); color: var(--over); }
.mk-dot.mk-mid  { background: color-mix(in srgb, var(--neutral) 20%, transparent); color: var(--neutral); }
.mk-dot.mk-bad  { background: color-mix(in srgb, var(--under) 20%, transparent); color: var(--under); }

.mk-legend {
  border-top: 1px solid var(--line); padding: var(--s2) var(--s3);
  display: grid; gap: var(--s1); justify-items: center; text-align: center;
}
.mk-legend-head {
  font-size: var(--fs-micro); color: var(--dim);
  text-transform: uppercase; letter-spacing: var(--track-lab);
}
.mk-key { display: flex; flex-wrap: wrap; gap: var(--s3); justify-content: center; }
.mk-key span { display: inline-flex; align-items: center; gap: 5px;
  font-size: var(--fs-micro); color: var(--ink-2); }

@media (max-width: 720px) {
  .mk-panel { margin: var(--s2) var(--s3) var(--s3); }
  .mk-key { gap: var(--s2); }
  .mk-table th, .mk-table td { padding: var(--s2); }
}

/* A market we offer that nobody has priced yet.
 *
 * It used to render as "--", which reads as missing data rather than as a
 * market that is open and simply unpriced -- and on fantasy score, which the
 * DFS books post late, that is most of the day. The words are smaller and
 * quieter than a number so the tab still scans as "nothing to bet here yet".
 */
.t-line.t-noline {
  font-family: var(--font); font-size: var(--fs-micro);
  color: var(--dim); font-weight: 500; letter-spacing: 0;
  text-transform: none;
}

/* The side the model favours.
 *
 * Both buttons used to be the same grey with a percentage on each, which on a
 * phone made the one piece of actual opinion on the card look like two blank
 * rectangles. The favoured side gets the colour of its direction and the
 * weight; the other recedes rather than disappearing, because you can still
 * take it and the number is still worth reading.
 *
 * Nothing is marked when the model is near a coin flip -- see LEAN_MIN_EDGE.
 */
.add-sides[data-lean="over"] .add-over,
.add-sides[data-lean="under"] .add-under {
  font-weight: 700;
}
.add-sides[data-lean="over"] .add-over {
  background: color-mix(in srgb, var(--over) 22%, transparent);
  border-color: var(--over); color: var(--over);
}
.add-sides[data-lean="under"] .add-under {
  background: color-mix(in srgb, var(--under) 22%, transparent);
  border-color: var(--under); color: var(--under);
}
.add-sides[data-lean] .add-side:not(.add-over):not(.add-under),
.add-sides[data-lean="over"] .add-under,
.add-sides[data-lean="under"] .add-over {
  opacity: 0.62;
}

/* On a phone the pair is the thing you came to read, so it gets the room. */
@media (max-width: 720px) {
  .add-sides { width: 100%; gap: var(--s2); margin-top: var(--s2); }
  .add-side { flex: 1; justify-content: center; min-height: 42px; font-size: var(--fs-sm); }
  .add-side .num { font-size: var(--fs-body); }
}

/* Which season the matchup numbers come from. It moves as the blend moves --
   "2025-26" on opening night, "42% 2026-27" by mid-November -- so it sits
   beside the heading rather than being baked into it. */
.mk-season { margin-left: var(--s2); color: var(--dim); white-space: nowrap; }
.matchup-card .mk-info { margin-left: var(--s2); }

/* PROMO on the tab itself, so the promotion is visible before you click it
 * rather than only once the 100% hit rate is already on screen. */
.t-promo {
  font-size: 8px; font-weight: 700; letter-spacing: var(--track-lab);
  color: var(--neutral); vertical-align: middle;
}

/* The sentence that makes the on/off table readable, between the heading and
 * the numbers rather than under them -- it is the thing you need BEFORE you
 * look at a column called Usg%, not a footnote after you have given up. */
.oo-lead {
  margin: 0 var(--sp-4) var(--sp-3);
  max-width: 68ch;
}

/* The "n hidden" note beside the book count. Quieter than the count itself:
 * it is a footnote about what is NOT in the table, and it carries a tooltip
 * saying why, so a shorter list than another site shows is explained rather
 * than mysterious. */
.card-head .dim { color: var(--dim); cursor: help; }

/* A night he barely featured in.
 *
 * Marked, never removed: the game counts in every average on the page, and
 * the point of the mark is that a reader can see WHY one bar is a stub rather
 * than reading it as a bad performance. Amber, which this product already
 * uses for "this is a different kind of thing" rather than "this is bad". */
.chart .bar-short { opacity: 0.55; }
.chart .bar-short-mark {
  font-size: 9px; fill: var(--neutral); text-anchor: middle;
}
.short-mark {
  margin-left: 4px; color: var(--neutral); cursor: help; font-size: 11px;
}
.short-mark:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.log-short td { color: var(--ink-2); }


/* ---- On court / off court: the focused split ----------------------------
 *
 * The eight-column table answers every teammate at once, which is the wrong
 * shape for the question anybody arrives with: one man is out tonight, what
 * does that do to this player. The picker and the panel below answer that
 * one, and the table moves behind a <details> for when the whole picture is
 * what you want.
 */
.oo-pick {
  display: block;
  padding: 0 var(--s3) var(--s2);
}
.oo-pick-lab {
  font-family: var(--font-display); text-transform: uppercase;
  letter-spacing: var(--track-lab); font-size: var(--fs-lab); color: var(--dim);
}
.oo-chips {
  display: flex; gap: var(--s1); flex-wrap: wrap;
  margin-top: var(--s1);
}
.oo-chip {
  cursor: pointer; flex: none;
  padding: 6px var(--s3); min-height: 34px; border-radius: var(--r-chip);
  font-size: var(--fs-sm); color: var(--ink-2);
  background: var(--panel-2); border: 1px solid var(--line);
}
.oo-chip.is-on {
  color: var(--ink); background: var(--accent-weak); border-color: var(--accent);
}
.oo-chip-flag {
  font-size: var(--fs-micro); font-weight: 700; color: var(--under);
  text-transform: uppercase; letter-spacing: var(--track-lab);
}
.oo-waiting {
  padding: var(--s4); text-align: center;
  color: var(--dim); font-size: var(--fs-sm);
}

.oo-focus {
  margin: 0 var(--s3) var(--s3);
  border: 1px solid var(--line); border-radius: var(--r-card);
  background: var(--panel-2); overflow: hidden;
}
.oo-headline {
  display: flex; align-items: baseline; flex-wrap: wrap; gap: var(--s2);
  padding: var(--s3);
  border-bottom: 1px solid var(--line);
}
.oo-h-lab { font-size: var(--fs-sm); color: var(--ink-2); }
.oo-h-val {
  margin-left: auto;
  font-family: var(--font-num); font-variant-numeric: tabular-nums;
  font-size: 26px; font-weight: 700; line-height: 1; color: var(--ink);
}
/* Up is the player doing MORE with the teammate gone, which is the case a
   reader is usually hunting for, so it takes the positive colour. */
.oo-h-val.up { color: var(--over); }
.oo-h-val.down { color: var(--under); }
.oo-h-unit {
  display: block; margin-top: 3px;
  font-family: var(--font); font-size: var(--fs-micro);
  font-weight: 400; color: var(--faint); letter-spacing: 0;
}
.oo-flag {
  flex: none;
  font-size: var(--fs-micro); font-weight: 700;
  text-transform: uppercase; letter-spacing: var(--track-lab);
  padding: 2px 6px; border-radius: var(--r-chip);
  color: var(--under);
  background: color-mix(in srgb, var(--under) 16%, transparent);
}
.oo-focus-table { width: 100%; }
.oo-focus-table th[scope="row"] {
  text-align: left; font-weight: 500; color: var(--ink-2);
  font-size: var(--fs-sm);
}
.oo-focus-table thead th { color: var(--dim); font-size: var(--fs-micro); }
.oo-focus-table td, .oo-focus-table th { padding: 6px var(--s3); }
.oo-focus-table tbody tr + tr { border-top: 1px solid var(--line); }
/* The sample size, not a result. */
.oo-sample th[scope="row"], .oo-sample td { color: var(--faint); }

.oo-all > summary {
  cursor: pointer; padding: var(--s2) var(--s3);
  font-family: var(--font-display); text-transform: uppercase;
  letter-spacing: var(--track-lab); font-size: var(--fs-lab);
  color: var(--ink-2);
}
.oo-all > summary:hover { color: var(--ink); }

@media (max-width: 720px) {
  .oo-headline { gap: var(--s1); }
  .oo-h-val { margin-left: 0; font-size: 24px; }
  .oo-pick { flex-wrap: wrap; }
  .oo-pick .seg-select { max-width: none; }
}


/* ---- The sticky head (phones only) --------------------------------------
 *
 * Outlier's shape, which Paul asked for: player, window row, market row, all
 * pinned while the page scrolls under them. It works there because the chrome
 * budget is spent ONCE -- so on this page the nav strip and the topbar are
 * hidden and this takes their place, rather than stacking on top of them.
 *
 * Desktop keeps the existing hero and side rail and never sees this.
 */
.sticky-head { display: none; }

@media (max-width: 860px) {
  /* The two bars this replaces. Their job -- identity and a way back -- is
     done by the X and the name above. */
  .app > div > .topbar { display: none; }
  .app > .nav { display: none; }

  .sticky-head {
    display: block;
    position: sticky; top: 0; z-index: 40;
    /* NO negative margin. It carried `margin: 0 calc(-1 * var(--s3))` to
       bleed past a page gutter, and `.main` has no horizontal padding at any
       width this media query covers -- measured at 360, 390, 480, 600, 760
       and 859, all 0. So there was nothing to cancel and the header simply
       hung 12px past each edge, which put a 12px horizontal scroll on every
       player page on a phone. The padding below is the header's own inner
       gutter and stays. */
    padding: var(--s2) var(--s3) 0;
    background: var(--bg);
    border-bottom: 1px solid var(--line);
  }
  .sh-top { display: flex; align-items: center; gap: var(--s2); }
  /* Compressed, because 194px of head and 105px of bar was 35 per cent of a
     390x844 screen before a single number appeared. The identity reads just
     as well smaller -- it is a label on a page you arrived at deliberately,
     not something you have to find. */
  .sticky-head { padding-top: var(--s1); }
  /* The spacer MATCHES the back button rather than taking the slack. With
     flex:1 it ate the remaining width and shoved the headshot against the X
     instead of leaving it in the middle. */
  .sh-spacer { flex: none; width: 44px; }
  .sh-back {
    display: grid; place-items: center; cursor: pointer;
    /* 44, not 34: this is the only way off the page on a phone, and it is
       the one control here that must stay a full thumb target. */
    width: 44px; height: 44px; border-radius: 50%;
    color: var(--ink); background: var(--panel-2);
    border: 1px solid var(--line-2); text-decoration: none; font-size: 18px;
    line-height: 1;
  }
  .sh-id { flex: none; margin: 0 auto; }
  .sh-face {
    display: block; width: 36px; height: 36px; border-radius: 50%;
    object-fit: cover; background: var(--panel-2);
    border: 1px solid var(--line-2);
  }
  .sh-noface {
    display: grid; place-items: center;
    font-family: var(--font-display); color: var(--dim);
  }
  .sh-name {
    margin: 2px 0 0; text-align: center;
    font-family: var(--font-display); text-transform: uppercase;
    font-size: 17px; line-height: 1.1; letter-spacing: var(--track-display);
  }
  .sh-pos { color: var(--dim); font-size: 12px; }
  .sh-sub {
    margin: 1px 0 var(--s1); text-align: center;
    font-size: var(--fs-micro); color: var(--ink-2);
  }

  /* Both rows slide sideways. The fade that says so is wired in shell.js. */
  .sh-row {
    display: flex; gap: var(--s1);
    overflow-x: auto; scrollbar-width: none;
    margin: 0 calc(-1 * var(--s3)); padding: 0 var(--s3);
  }
  .sh-row::-webkit-scrollbar { display: none; }

  /* Centred under the name, to line up with everything above it.
     `safe center` rather than `center`: when the row is wider than the
     screen, plain centring hides the first chip off the left edge where it
     cannot be scrolled back to. The market row below stays left-aligned --
     that is a tab strip and it reads from the start. */
  .sh-windows { padding-bottom: var(--s1); justify-content: safe center; }
  .sh-win {
    flex: none; cursor: pointer;
    /* 36 high: still a comfortable target, four pixels back from the head. */
    padding: 6px var(--s3); min-height: 36px; border-radius: var(--r-chip);
    font-family: var(--font-display); text-transform: uppercase;
    letter-spacing: var(--track-lab); font-size: var(--fs-lab);
    color: var(--ink-2); background: var(--panel-2);
    border: 1px solid transparent;
  }
  .sh-win[aria-pressed="true"] {
    color: var(--ink); background: var(--panel-3); border-color: var(--line-2);
  }

  .sh-markets { border-top: 1px solid var(--line); }
  .sh-mkt {
    flex: none; cursor: pointer;
    padding: 7px var(--s2) 7px; min-height: 40px;
    background: none; border: 0; border-bottom: 2px solid transparent;
    font-family: var(--font-display); text-transform: uppercase;
    letter-spacing: var(--track-lab); font-size: var(--fs-sm);
    color: var(--dim);
  }
  .sh-mkt[aria-pressed="true"] {
    color: var(--ink); border-bottom-color: var(--accent);
  }

  /* The desktop hero, the old stat strip and the old rate pills are all
     superseded by the head above. Removed rather than hidden would mean two
     render paths; hidden keeps one. */
  /* Superseded by the sticky head and the bottom bar. Hidden rather than
     removed so there is ONE render path, not a mobile one and a desktop one.
     The rung picker and the ladder caret both go: the sheet is the single
     place a line is changed now. */
  .hero, .stat-tabs, .rates { display: none; }
  .rung-pick, .rung-caret { display: none; }
  /* The ladder card is the sheet now. */
  .mini tr[data-rung-go] { pointer-events: none; }
}


/* ---- The bottom bar (phones only) --------------------------------------- */
.pbar { display: none; }

@media (max-width: 860px) {
  .pbar {
    display: block;
    position: fixed; left: 0; right: 0; bottom: 0; z-index: 45;
    padding: var(--s2) var(--s3) calc(var(--s2) + env(safe-area-inset-bottom));
    background: color-mix(in srgb, var(--bg) 94%, transparent);
    backdrop-filter: blur(10px);
    border-top: 1px solid var(--line);
  }
  .pbar-row { display: flex; gap: var(--s2); align-items: stretch; }
  .pbar-leg {
    flex: 1 1 auto; min-width: 0; cursor: pointer; text-align: left;
    display: grid; gap: 1px; align-content: center;
    padding: 6px var(--s3); min-height: 44px;
    border-radius: var(--r-chip); border: 0;
    background: var(--accent); color: var(--on-accent, #16120c);
  }
  .pbar-leg.pbar-noline {
    background: var(--panel-2); color: var(--dim); cursor: default;
    display: grid; place-items: center; font-size: var(--fs-sm);
  }
  .pbar-side {
    font-family: var(--font-num); font-variant-numeric: tabular-nums;
    font-size: var(--fs-md); font-weight: 700; line-height: 1.1;
  }
  .pbar-meta { font-size: var(--fs-micro); opacity: 0.78; }
  .pbar-alt {
    flex: none; cursor: pointer;
    padding: 6px var(--s3); min-height: 44px; border-radius: var(--r-chip);
    font-family: var(--font-display); text-transform: uppercase;
    letter-spacing: var(--track-lab); font-size: var(--fs-lab);
    color: var(--ink); background: var(--panel-2);
    border: 1px solid var(--line-2);
  }
  .pbar-jump { margin-top: var(--s1); padding: 0; }
  .pbar-pill {
    flex: none; cursor: pointer;
    padding: 5px var(--s3); min-height: 32px; border-radius: var(--r-pill);
    font-size: var(--fs-micro); color: var(--ink-2);
    background: var(--panel-2); border: 1px solid var(--line);
  }
  .pbar-pill:active { background: var(--panel-3); }

  /* Room for it. Without this the bar sits on top of the last card and the
     game log's final rows can never be read. */
  .main { padding-bottom: 150px; }
  /* A jump lands the section under the sticky head rather than behind it.
   *
   * MEASURED, not guessed. This was a flat 170px against a head that is
   * actually 194, so every landing tucked the title 24px underneath it. The
   * variable is written from JS after the head renders, so it cannot drift
   * again when the head changes height -- which it will, because the next
   * item on the list is making it shorter. */
  [id^="sec-"], #onoff, #shots {
    scroll-margin-top: calc(var(--sticky-h, 194px) + 12px);
  }
}

/* ---- A short viewport: landscape, or portrait at large text --------------
 *
 * The sticky treatment keyed off WIDTH alone, so a phone turned sideways kept
 * all of the portrait chrome in less than half the vertical room: 194px of
 * head and 105px of bar in a 390px viewport, 77 per cent, leaving 91px of
 * content. Paul: "when the phone is in landscape mode, it doesnt work, the
 * sticky part causes it not to be able to do anything else".
 *
 * HEIGHT is the right question, and this project knew that once -- the chart
 * used to unpin below 520px with the note that "sticky anything is a
 * liability for someone who needs large text: at 200 per cent zoom a pinned
 * chart can leave no room to read". Same failure, moved to the header.
 *
 * So: below 520px of height the head scrolls away like everything else, and
 * the bottom bar keeps the control you act on and drops the jump pills. That
 * turns 91px of content into about 330. It also covers the case nobody has
 * hit yet -- portrait at large text, where zoom shrinks the viewport the same
 * way turning the phone does.
 */
@media (max-width: 1100px) and (max-height: 520px) {
  .sticky-head { position: static; }
  /* Nothing is pinned above, so a jump needs no offset. */
  [id^="sec-"], #onoff, #shots { scroll-margin-top: var(--s3); }
  .pbar-jump { display: none; }
  .main { padding-bottom: 96px; }
}


/* ---- The ALT LINES sheet ------------------------------------------------ */
.alt-sheet { position: fixed; inset: 0; z-index: 60; }
.alt-sheet[hidden] { display: none; }
.alt-scrim { position: absolute; inset: 0; background: rgb(0 0 0 / 55%); }
.alt-panel {
  position: absolute; left: 0; right: 0; bottom: 0;
  max-height: 80vh; display: flex; flex-direction: column;
  background: var(--panel);
  border-top: 1px solid var(--line-2);
  border-radius: var(--r-card) var(--r-card) 0 0;
  padding-bottom: env(safe-area-inset-bottom);
}
.alt-head {
  display: flex; align-items: center; gap: var(--s2);
  padding: var(--s3); border-bottom: 1px solid var(--line);
}
.alt-title {
  font-family: var(--font-display); text-transform: uppercase;
  letter-spacing: var(--track-lab); font-size: var(--fs-md);
}
.alt-stat { color: var(--dim); }
.alt-x {
  margin-left: auto; flex: none; cursor: pointer;
  width: 44px; height: 44px; border-radius: 50%;
  display: grid; place-items: center;
  color: var(--ink-2); background: var(--panel-2);
  border: 1px solid var(--line-2);
}
.alt-cols, .alt-row {
  display: grid; grid-template-columns: 64px 78px 1fr 84px;
  align-items: center; gap: var(--s2);
  padding: 0 var(--s3);
}
.alt-cols {
  padding-top: var(--s2); padding-bottom: var(--s2);
  font-family: var(--font-display); text-transform: uppercase;
  letter-spacing: var(--track-lab); font-size: var(--fs-lab); color: var(--dim);
  border-bottom: 1px solid var(--line);
}
.alt-list {
  overflow-y: auto; overscroll-behavior: contain;
  -webkit-overflow-scrolling: touch;
  /* The rows are buttons; without this a vertical drag on one can be taken
     as a press rather than a scroll. */
  touch-action: pan-y;
}
/* Set while the sheet is open.
 *
 * `position: fixed`, not `overflow: hidden`. iOS Safari scrolls the document
 * regardless of overflow on the body, which is why the page behind the sheet
 * still moved after the first attempt -- the comment there said fixing the
 * body was what works and then the rule did the thing that does not.
 *
 * `top` is set from JS to the scroll offset, so the page does not jump to
 * the top when it becomes fixed, and is restored on close. */
body.sheet-open {
  position: fixed;
  left: 0; right: 0; width: 100%;
  overflow: hidden;
}
.alt-row {
  width: 100%; min-height: 52px; cursor: pointer; text-align: left;
  background: none; border: 0; border-bottom: 1px solid var(--line);
  color: var(--ink); font: inherit;
}
.alt-row:active { background: var(--panel-2); }
.alt-row.is-on { background: var(--accent-weak); box-shadow: inset 3px 0 0 var(--accent); }
.alt-line {
  font-family: var(--font-num); font-variant-numeric: tabular-nums;
  font-size: var(--fs-md); font-weight: 700;
}
.alt-rung {
  justify-self: start;
  font-size: var(--fs-micro); font-weight: 700;
  text-transform: uppercase; letter-spacing: var(--track-lab);
  padding: 2px 6px; border-radius: 3px;
}
.alt-pay { font-size: var(--fs-sm); color: var(--faint); }
.alt-pct {
  text-align: right;
  font-family: var(--font-num); font-variant-numeric: tabular-nums;
  font-size: var(--fs-md); font-weight: 600;
}
.alt-empty { padding: var(--s5); text-align: center; color: var(--dim); }


/* ---- Pace up / pace down ------------------------------------------------ */
.pace-spot {
  display: grid; grid-template-columns: auto auto 1fr;
  align-items: baseline; gap: var(--s2);
  margin: var(--s2) var(--s3) 0; padding: var(--s2) var(--s3);
  border-radius: var(--r-chip); border: 1px solid var(--line);
  background: var(--panel-2);
}
.ps-word {
  font-family: var(--font-display); text-transform: uppercase;
  letter-spacing: var(--track-lab); font-size: var(--fs-lab); font-weight: 700;
}
.ps-num {
  font-family: var(--font-num); font-variant-numeric: tabular-nums;
  font-size: var(--fs-md); font-weight: 700;
}
.ps-why {
  grid-column: 1 / -1;
  font-size: var(--fs-micro); color: var(--faint); line-height: 1.4;
}
/* Faster is not automatically good -- it is good for an over and bad for an
   under -- so these are the DIRECTION's colours, not a verdict's. */
/* GOOD and BAD for the side being read, not up and down.
   They were named for the direction of the game, which stopped being the
   same thing once the page could be read from the under: "PACE UP" in red is
   correct there, because the game speeds up and that is bad news for you. */
.pace-spot.ps-good { border-color: color-mix(in srgb, var(--over) 45%, transparent); }
.pace-spot.ps-good .ps-word, .pace-spot.ps-good .ps-num { color: var(--over); }
.pace-spot.ps-bad { border-color: color-mix(in srgb, var(--under) 45%, transparent); }
.pace-spot.ps-bad .ps-word, .pace-spot.ps-bad .ps-num { color: var(--under); }
.pace-spot.ps-mid .ps-word, .pace-spot.ps-mid .ps-num { color: var(--ink-2); }


/* ---- Tap a bar to read it ----------------------------------------------- */
.bar-tip {
  position: fixed; z-index: 55;
  max-width: min(260px, calc(100vw - 16px));
  display: grid; gap: 2px;
  padding: var(--s2) var(--s3);
  border-radius: var(--r-chip);
  background: var(--panel-3);
  border: 1px solid var(--line-2);
  box-shadow: 0 8px 22px rgb(0 0 0 / 55%);
  pointer-events: none;
}
.bar-tip[hidden] { display: none; }
.bt-main {
  font-family: var(--font-num); font-variant-numeric: tabular-nums;
  font-size: var(--fs-sm); color: var(--ink); line-height: 1.35;
}
.bt-note { font-size: var(--fs-micro); color: var(--neutral); line-height: 1.35; }

/* The rich card: both crests, the scoreline with the winner in bold, and the
   game's own numbers. Paul asked for Outlier's, which is a heading over a
   small table rather than a sentence. */
.bar-tip-card { max-width: min(300px, calc(100vw - 16px)); gap: var(--s2); }
.bt-head {
  display: flex; align-items: center; justify-content: center; gap: 6px;
  padding-bottom: var(--s2); border-bottom: 1px solid var(--line);
}
.bt-logo { width: 20px; height: 20px; object-fit: contain; flex: none; }
.bt-abbr {
  font-family: var(--font-display); font-weight: 700; font-size: var(--fs-sm);
  letter-spacing: var(--track-lab); color: var(--ink-2);
}
.bt-score {
  display: inline-flex; align-items: baseline; gap: 4px; margin: 0 4px;
  font-family: var(--font-num); font-variant-numeric: tabular-nums;
  font-size: var(--fs-sm);
}
/* The winner in full strength, the loser dimmed. Outlier bolds one side and
   it is the fastest way to read a result without parsing which number is
   whose. */
.bt-score b { color: var(--dim); font-weight: 600; }
.bt-score b.w { color: var(--ink); font-weight: 700; }
.bt-score i { color: var(--faint); font-style: normal; }
.bt-date {
  font-size: var(--fs-micro); color: var(--dim); text-align: center;
  text-transform: uppercase; letter-spacing: var(--track-lab);
}
.bt-rows { display: grid; gap: 3px; }
.bt-row {
  display: flex; align-items: baseline; justify-content: space-between;
  gap: var(--s4); font-size: var(--fs-micro);
}
.bt-row em { font-style: normal; color: var(--ink-2); }
.bt-row b {
  font-family: var(--font-num); font-variant-numeric: tabular-nums;
  font-size: var(--fs-sm); color: var(--ink);
}
.bt-row b.bt-over  { color: var(--over); }
.bt-row b.bt-under { color: var(--under); }
.bt-row b.bt-push  { color: var(--neutral); }
/* The tick sits with its number rather than in its own column: the two are
   one reading, and a column of ticks separated from the lines they grade
   invites pairing them with the wrong row. */
.bt-mark { font-size: 0.9em; }
/* Not a zero and not a blank. An un-captured line is unrecoverable, so the
   card says the number does not exist rather than implying it is nothing. */
.bt-none {
  font-family: var(--font-sans); font-size: var(--fs-micro);
  color: var(--faint); font-weight: 500;
}
/* The whole slot is already the hit target (see .bar-hit), so a thin bar is
   still a thumb-sized tap. */
.chart .bar { cursor: pointer; }
/* Zones are tappable for the same reason the bars are. */
svg [data-zone] { cursor: pointer; }

/* O / U, on the rung menu and the bottom bar.
 *
 * Not a filter and not a bet: it says which side the page is being read
 * from. Everything that carries a colour follows it -- the chart bars, the
 * hit rates, the matchup -- because all of them were written from the
 * over's point of view and were simply wrong on the other side. */
.ri-side {
  font-family: var(--font-display); font-weight: 700;
  font-size: var(--fs-lab); letter-spacing: var(--track-lab);
  width: 14px; flex: none; text-align: center;
}
.side-over  { color: var(--over); }
.side-under { color: var(--under); }

.pbar-ou { display: inline-flex; gap: 2px; flex: none; }
.pbar-ou-b {
  min-width: 52px; min-height: 44px; padding: 0 var(--s2);
  border: 1px solid var(--line-2); border-radius: var(--r-chip);
  background: var(--panel-2); color: var(--ink-2);
  font-family: var(--font-num); font-variant-numeric: tabular-nums;
  font-size: var(--fs-sm); font-weight: 600; cursor: pointer;
}
.pbar-ou-b[aria-pressed="true"].side-over {
  color: var(--over); border-color: var(--over);
  background: color-mix(in srgb, var(--over) 16%, transparent);
}
.pbar-ou-b[aria-pressed="true"].side-under {
  color: var(--under); border-color: var(--under);
  background: color-mix(in srgb, var(--under) 16%, transparent);
}

/* ---- Shot chart --------------------------------------------------------- */
/* A twelve-zone matchup card. Zone fill is the opponent's defence there,
 * bubble size is his scoring from there, and the panel on the right is
 * whichever zone is selected.
 *
 * The three grade colours are the house verdict colours at low alpha over
 * the panel, rather than the pastel fills a light theme would use: on
 * #12151A a pastel green reads as a highlight rather than a floor.
 */
.sc-card .sc-head { display: flex; align-items: center; gap: var(--s3); }
.sc-seasons {
  margin-left: auto; display: flex; gap: 2px;
  background: var(--panel-2); border: 1px solid var(--line);
  border-radius: var(--r-chip); padding: 2px;
}
.sc-pill {
  font: inherit; font-size: var(--fs-sm); font-weight: 600;
  font-family: var(--font-num);
  color: var(--ink-2); background: none; border: 0;
  border-radius: 4px; padding: 5px 12px; cursor: pointer; min-height: 32px;
}
.sc-pill[aria-pressed="true"] { background: var(--panel-3); color: var(--ink); }
.sc-pill:hover { color: var(--ink); }

.sc-summary {
  display: flex; align-items: baseline; gap: var(--s2);
  margin: 0 0 var(--s4); padding-left: var(--s3);
  font-size: var(--fs-body); color: var(--ink-2); line-height: 1.5;
  position: relative;
}
.sc-summary .sc-bar {
  position: absolute; left: 0; top: 2px; bottom: 2px;
  width: 3px; border-radius: 2px;
}
.sc-nogame { padding-left: 0; color: var(--faint); font-size: var(--fs-sm); }
.sc-summary .sc-good { background: var(--over); }
.sc-summary .sc-bad  { background: var(--under); }
.sc-summary strong {
  font-family: var(--font-num); font-size: var(--fs-xl); font-weight: 700;
  color: var(--over);
}
.sc-summary .sc-bad ~ strong { color: var(--under); }

.sc-body { display: grid; grid-template-columns: 1.1fr 0.9fr; gap: var(--s5); }
.sc-left { min-width: 0; }

/* The scoring-type control. 46px tall because it is the card's main switch
   and a 32px segmented control is a miss on a trackpad, never mind a thumb. */
.sc-groups {
  display: grid; grid-template-columns: repeat(4, 1fr); gap: 2px;
  background: var(--panel-2); border: 1px solid var(--line);
  border-radius: var(--r-chip); padding: 3px; margin-bottom: var(--s3);
}
.sc-seg {
  font: inherit; font-size: var(--fs-sm); font-weight: 600;
  color: var(--ink-2); background: none; border: 0; border-radius: 5px;
  padding: 0 var(--s2); min-height: 40px; cursor: pointer;
}
.sc-seg[aria-pressed="true"] { background: var(--panel-3); color: var(--ink); }
.sc-seg:hover { color: var(--ink); }

/* ---- The court ---------------------------------------------------------- */
.sc { display: block; width: 100%; height: auto; }
.sc-floor { fill: var(--panel-2); }
/* The painted lines have to read over EVERY zone fill, and the fills got
 * brighter. A dark grey line measured 1.98:1 against a neutral zone, which
 * is below the 3:1 floor for a non-text element and is why the court
 * stopped looking like a court. Light and part-transparent instead: it sits
 * on top of whatever colour is underneath rather than competing with it. */
.sc-lines {
  fill: none; stroke-width: 1.4;
  stroke: var(--ink);
}
.sc-lines .sc-hoop {
  stroke: var(--ink);
  stroke-width: 1.4; fill: none;
}

/* ONE DEFINITION OF EACH GRADE, as two custom properties.
 *
 * --g is the zone's resting fill and --gink is the saturated version: the
 * bubble's number, the swatch beside a figure, the brightened state on
 * hover. The classes carry the colour; what an element DOES with it is the
 * element's business.
 *
 * This replaced plain `fill:` rules, which are an SVG paint property and do
 * nothing whatever on an <i>. The legend's weak/neutral/tough swatches and
 * the little square next to Points/game were both in the markup, both
 * correctly classed, and both invisible -- which is why the card looked
 * like it was missing a colour key it had never failed to render.
 */
.sc-g-favorable {
  --g: color-mix(in srgb, var(--over) 40%, var(--panel-2));
  --g-lit: color-mix(in srgb, var(--over) 88%, var(--panel-2));
  --gink: var(--over);
}
.sc-g-neutral {
  --g: color-mix(in srgb, var(--neutral) 34%, var(--panel-2));
  --g-lit: color-mix(in srgb, var(--neutral) 84%, var(--panel-2));
  --gink: var(--neutral);
}
.sc-g-unfavorable {
  --g: color-mix(in srgb, var(--under) 38%, var(--panel-2));
  --g-lit: color-mix(in srgb, var(--under) 86%, var(--panel-2));
  --gink: var(--under);
}
.sc-g-limited {
  --g: var(--panel-3); --g-lit: var(--panel-3); --gink: var(--faint);
}
/* No opponent, so nothing to grade. A flat floor rather than the hatch:
   the hatch means HIS sample is thin, and saying that about a rim he has
   taken two hundred shots at is a lie the card used to tell every time a
   player was opened on an off day. */
.sc-g-ungraded {
  --g: var(--panel-3); --g-lit: color-mix(in srgb, var(--ink) 10%, var(--panel-3));
  --gink: var(--ink-2);
}

.sc-zone {
  fill: var(--g);
  stroke: var(--bg); stroke-width: 1.5;
  transition: fill var(--t-fast);
}
.sc-zone.sc-g-limited { fill: url(#scHatch); }
.sc-zone.is-on { cursor: pointer; }
.sc-off { fill: var(--panel-3); opacity: 0.4; }
/* The resting fills are muted on purpose -- twelve saturated regions is a
   stained-glass window, not a chart -- so the zone under the cursor lights
   up to full strength. That IS the hover feedback; there is no popup. */
.sc-zone.is-on:hover, .sc-zone.is-on.is-hot,
.sc-zone.is-on.is-sel { fill: var(--g-lit); }
.sc-zone.sc-g-limited:hover, .sc-zone.sc-g-limited.is-hot,
.sc-zone.sc-g-limited.is-sel { fill: url(#scHatch); opacity: 0.78; }
/* Selected: a white ring, drawn as a stroke on the region itself so it
   follows the shape rather than boxing it. */
.sc-zone.is-sel { stroke: var(--ink); stroke-width: 2.5; }
.sc-zone.is-on:focus-visible { outline: none; stroke: var(--accent); stroke-width: 3; }
.sc-zone.is-thin { stroke-dasharray: 4 3; }

/* Opaque, with a hairline.
 *
 * Translucent, the bubble took on the colour of whatever zone it sat over
 * and came out at 2.73:1 against a neutral fill -- under the 3:1 floor for
 * a shape carrying information. The ring is what guarantees it: a bubble
 * has to separate from twelve different backgrounds, and a border does
 * that without depending on any of them. */
.sc-bub circle {
  fill: var(--bg);
  stroke: color-mix(in srgb, var(--ink) 55%, transparent);
  stroke-width: 1;
}
.sc-bub .sc-val {
  font-family: var(--font-num); font-size: 13px; font-weight: 700;
  text-anchor: middle; fill: var(--ink);
}
.sc-bub .sc-val { fill: var(--gink); }
.sc-bub.is-sel circle { stroke: var(--ink); stroke-width: 2; }
/* The bubble is never the hit target -- the zone under it is -- but it has
   to react, so it grows with the zone it belongs to. transform-box keeps
   the scale centred on the circle rather than the whole SVG. */
.sc-bub { pointer-events: none; transition: transform var(--t-fast); }
.sc-bub circle, .sc-bub .sc-val { transition: none; }
.sc-bub.is-hot {
  transform: scale(1.14);
  transform-origin: center;
  transform-box: fill-box;
}

.sc-ft circle { fill: color-mix(in srgb, var(--accent) 24%, var(--panel-3));
                stroke: var(--line-2, #2A313C); stroke-width: 1.5; cursor: pointer; }
.sc-ft .sc-val { font-size: 22px; fill: var(--ink); }
.sc-ft.is-sel circle { stroke: var(--ink); stroke-width: 2.5; }

/* ---- Legend ------------------------------------------------------------- */
.sc-legend {
  display: flex; gap: var(--s5); align-items: flex-start;
  margin-top: var(--s3); padding-top: var(--s3);
  border-top: 1px solid var(--line);
}
.sc-leg { display: flex; flex-direction: column; gap: 4px; }
.sc-leg-t {
  font-size: var(--fs-lab); letter-spacing: 0.06em; text-transform: uppercase;
  color: var(--faint);
}
.sc-leg-b {
  display: flex; align-items: center; gap: 6px;
  font-size: var(--fs-lab); color: var(--ink-2);
}
.sc-leg-b svg circle { fill: var(--panel-3); stroke: var(--line-2, #2A313C); }
.sc-leg-b i { font-style: normal; color: var(--faint); }
.sc-sw {
  display: inline-block; width: 10px; height: 10px; border-radius: 3px;
  flex: none; vertical-align: middle;
  background: var(--gink);
}
/* In the key the swatch stands for the ZONE, so it uses the zone's own
   resting fill -- wide and short, because a 10px square of a deliberately
   muted colour is not readable on a near-black card. */
.sc-leg-grade { gap: 0; }
.sc-leg-grade .sc-sw {
  width: 36px; height: 5px; border-radius: 3px;
  background: var(--g); margin: 0 6px 0 var(--s3);
}
.sc-leg-grade .sc-sw:first-child { margin-left: 0; }

/* ---- Detail panel ------------------------------------------------------- */
.sc-panel {
  min-width: 0;
  border-left: 1px solid var(--line);
  padding-left: var(--s5);
}
.sc-p-head { text-align: center; margin-bottom: var(--s4); }
.sc-p-title { display: block; min-width: 0; }
.sc-only-narrow { display: none; }
.sc-p-head h3 {
  margin: 0; font-family: var(--font-display); font-size: var(--fs-xl);
  font-weight: 700; color: var(--ink);
}
.sc-p-sub { margin: 2px 0 0; font-size: var(--fs-sm); color: var(--faint); }

.sc-p-cols { display: grid; grid-template-columns: 1fr 1fr; gap: var(--s4); }
.sc-p-col { min-width: 0; display: flex; flex-direction: column; gap: var(--s3); }
.sc-p-col + .sc-p-col { border-left: 1px solid var(--line); padding-left: var(--s4); }
.sc-p-who { display: flex; align-items: center; gap: var(--s2); min-height: 28px; }
.sc-p-logo, .sc-p-face { flex: none; }
.sc-p-face { border-radius: 50%; background: var(--panel-2); object-fit: cover; }
.sc-p-name {
  font-size: var(--fs-sm); font-weight: 600; color: var(--ink-2);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.sc-thin-note {
  margin: 0; font-size: var(--fs-lab); font-weight: 600; color: var(--under);
  display: flex; align-items: center; gap: 5px;
}
.sc-p-stats { margin: 0; display: grid; gap: 2px; }
.sc-p-stats dt {
  font-size: var(--fs-sm); color: var(--ink-2); margin-top: var(--s2);
}
.sc-p-stats dt span { color: var(--faint); }
.sc-p-stats dd {
  margin: 0; display: flex; align-items: baseline; gap: 6px;
  font-family: var(--font-num); font-size: var(--fs-xl); font-weight: 700;
  color: var(--ink);
}
.sc-p-stats dd .sc-of {
  font-size: var(--fs-sm); font-weight: 400; color: var(--faint);
}

.sc-meter { margin-top: auto; padding-top: var(--s3); }
.sc-m-lab { font-size: var(--fs-sm); color: var(--ink-2); }
.sc-m-donut {
  display: flex; align-items: center; justify-content: space-between;
  gap: var(--s2);
}
.sc-m-num { display: flex; flex-direction: column; }
.sc-m-num b {
  font-family: var(--font-num); font-size: var(--fs-xl); font-weight: 700;
}
.sc-m-num small { font-size: var(--fs-lab); color: var(--faint); }

/* The phone's version of the same number. Hidden here, shown below. */
.sc-m-bar { display: none; }
.sc-m-top b {
  font-family: var(--font-num); font-size: var(--fs-xl); font-weight: 700;
}
.sc-m-track {
  display: block; height: 7px; border-radius: 4px;
  background: var(--panel-3); overflow: hidden; margin: 5px 0 3px;
}
.sc-m-track i { display: block; height: 100%; background: currentColor; }
.sc-m-bar small { font-size: var(--fs-lab); color: var(--faint); }
.sc-d-track { fill: none; stroke: var(--panel-3); stroke-width: 9; }
.sc-d-fill  { fill: none; stroke-width: 9; stroke-linecap: round; }
.sc-mine   { color: var(--accent); stroke: var(--accent); }
.sc-theirs { color: var(--under);  stroke: var(--under); }

@media (prefers-reduced-motion: reduce) {
  .sc-zone { transition: none; }
}

/* ---- Shot chart, narrow ------------------------------------------------- */
/* Below 640 the two columns stop being two columns.
 *
 * The court goes full width on top and the panel stacks under it -- not a
 * bottom sheet, not a tooltip. Tapping a zone updates the panel in place and
 * does NOT scroll to it: a page that jumps under your thumb every time you
 * tap the floor is unusable, and the panel is close enough to read without
 * being moved to.
 *
 * Measured in a real 390px viewport, not a resized desktop window.
 */
@media (max-width: 640px) {
  .sc-body { grid-template-columns: minmax(0, 1fr); gap: var(--s4); }

  /* The panel sits under the court, so its divider is a rule above it
     rather than a border down its left-hand side. */
  .sc-panel {
    border-left: 0; padding-left: 0;
    border-top: 1px solid var(--line); padding-top: var(--s4);
  }

  /* 44px is the floor for anything a thumb has to hit. */
  .sc-seg { min-height: 44px; }
  .sc-pill { min-height: 40px; padding: 8px 14px; }

  /* Four segments across a 320px screen with no wrap. */
  .sc-groups { gap: 1px; padding: 2px; }

  /* Header: face, zone name, badge. The copies inside the columns are
     hidden; see the note in panel(). */
  .sc-p-head {
    display: flex; align-items: center; gap: var(--s3);
    margin-bottom: var(--s3);
  }
  .sc-only-narrow { display: block; }
  .sc-only-wide { display: none; }
  .sc-p-title { flex: 1; min-width: 0; }
  .sc-p-head h3 { font-size: var(--fs-lg); }

  /* The two columns stay side by side -- him on the left, them on the
     right -- because the whole point of the panel is the comparison, and
     stacking them puts his number a scroll away from theirs. */
  .sc-p-cols { gap: var(--s3); }
  .sc-p-col + .sc-p-col { padding-left: var(--s3); }
  .sc-p-col { gap: var(--s2); }
  .sc-p-who { display: none; }
  .sc-p-stats dd { font-size: var(--fs-lg); }
  .sc-p-stats dt { font-size: var(--fs-lab); }
  .sc-p-stats dt span { display: none; }

  /* A ring beside its own label eats a third of a 390px row. */
  .sc-m-donut { display: none; }
  .sc-m-bar { display: block; }
  .sc-m-top b { font-size: var(--fs-lg); }

  /* One row, not two. Both keys fit across 390 once the type comes down;
     letting them wrap pushes the panel a whole line further from the court
     for no gain. */
  .sc-legend { gap: var(--s3); flex-wrap: nowrap; }
  .sc-leg-t { font-size: 9px; letter-spacing: 0.04em; }
  .sc-leg-b { font-size: 9px; gap: 4px; }
  .sc-leg-grade .sc-sw { width: 20px; margin-left: 6px; }
  .sc-leg-b svg { width: 46px; }

  /* TAP TARGETS. A corner zone is three feet wide, which is about fourteen
     pixels of a 390px court -- not something a thumb can hit. The bubble
     sitting in it is forty-odd and is already the thing the eye aims at, so
     on a touch screen it becomes the target too.
     Mouse keeps bubbles inert: on a pointer you can hit the region itself,
     and a bubble nudged in from a narrow zone overlaps its neighbour. */
  @media (pointer: coarse) {
    .sc-bub { pointer-events: auto; cursor: pointer; }
  }
}

/* Very narrow: the four segments still must not wrap. */
@media (max-width: 380px) {
  .sc-seg { font-size: var(--fs-lab); padding: 0 4px; }
  .sc-legend { gap: var(--s3); }
}

/* ---- Shot chart: how to read it ----------------------------------------- */
/* Closed by default. Someone who already knows the card should not have to
 * scroll past an explanation of it every time.
 */
.sc-how { border-top: 1px solid var(--line); margin-top: var(--s4); }
.sc-how summary {
  cursor: pointer; list-style: none;
  padding: var(--s3) 0 0;
  font-size: var(--fs-sm); font-weight: 600; color: var(--ink-2);
  display: flex; align-items: center; gap: 6px;
  min-height: 40px;
}
.sc-how summary::-webkit-details-marker { display: none; }
.sc-how summary::before {
  content: "\25B8"; color: var(--faint);
  transition: transform var(--t-fast); display: inline-block;
}
.sc-how[open] summary::before { transform: rotate(90deg); }
.sc-how summary:hover { color: var(--ink); }
.sc-how-list {
  margin: 0 0 var(--s3); display: grid; gap: var(--s2) var(--s4);
  grid-template-columns: max-content 1fr;
  font-size: var(--fs-sm); line-height: 1.5;
}
.sc-how-list dt { color: var(--ink); font-weight: 600; }
.sc-how-list dd { margin: 0; color: var(--ink-2); }

@media (max-width: 640px) {
  .sc-how-list { grid-template-columns: minmax(0, 1fr); gap: 2px; }
  .sc-how-list dd { margin-bottom: var(--s2); }
}
