/* ===========================================================================
   Tokens
   A cool porcelain surface with a single pine accent. Dense numeric UI reads
   better on light ground; the dark variant reuses the same variable names so
   nothing else in the sheet has to know which is active.

   The series steps are validated, not chosen by eye: the previous light green
   and blue fell below the OKLCH chroma floor and read as grey on white, which
   is what made light-mode charts look muddy, and every dark step sat above the
   dark lightness band. Both sets now clear the lightness band, the chroma
   floor, CVD separation and contrast. Re-run before changing one:
     node scripts/validate_palette.js "<hexes>" --mode light --surface "#ffffff"

   --accent stays its own darker pine deliberately. It dresses UI chrome —
   pressed states, focus rings, links — where a darker green reads better; a
   data mark has the opposite requirement, which is exactly why the shared
   value failed as a series colour.

   Three states, not two: an explicit choice stamps data-theme on <html>, and
   the default — nothing stamped — follows the OS. That is why the dark
   aliases appear twice, once under the media query and once under
   [data-theme="dark"]. Only the *aliases* are duplicated; the palettes below
   are declared once each, so a colour cannot drift between the two paths.
   ======================================================================== */
:root {
  /* Palettes, single-sourced. */
  --l-paper: #f1f4f6;
  --l-surface: #ffffff;
  --l-surface-sunk: #f7f9fa;
  --l-line: #dde3e8;
  --l-line-strong: #c5ced6;
  --l-ink: #131a21;
  --l-ink-soft: #4c5b68;
  --l-ink-faint: #7b8894;
  --l-accent: #1c6b57;
  --l-accent-soft: #e4efeb;
  --l-positive: #1c6b57;
  --l-negative: #a8402f;
  /* Per-game hues are generated from the game's name (there are more games
     than the five categorical slots), so only their saturation and lightness
     are themed. Deeper and more saturated on white; lighter on the dark
     surface, where a deep fill disappears. */
  --l-game-sat: 52%;
  --l-game-lum: 40%;
  --d-game-sat: 45%;
  --d-game-lum: 56%;

  --l-series-1: #118a69;
  --l-series-2: #6b4fbb;
  --l-series-3: #b1652a;
  --l-series-4: #2b7ec4;
  --l-series-5: #97324c;
  --l-shadow-pop: 0 6px 24px rgba(19, 26, 33, 0.12);

  --d-paper: #10151a;
  --d-surface: #171d24;
  --d-surface-sunk: #131920;
  --d-line: #262f38;
  --d-line-strong: #38434e;
  --d-ink: #e7ecf0;
  --d-ink-soft: #a3b0bc;
  --d-ink-faint: #74828e;
  --d-accent: #4fb99a;
  --d-accent-soft: #17302a;
  --d-positive: #4fb99a;
  --d-negative: #e08472;
  --d-series-1: #2f9d7d;
  --d-series-2: #8b6fe0;
  --d-series-3: #c2833c;
  --d-series-4: #4a8fd0;
  --d-series-5: #c55a75;
  --d-shadow-pop: 0 6px 24px rgba(0, 0, 0, 0.5);

  /* Light is the default: no data-theme, no dark OS preference. */
  --paper: #f1f4f6;
  --surface: #ffffff;
  --surface-sunk: #f7f9fa;
  --line: #dde3e8;
  --line-strong: #c5ced6;
  --ink: #131a21;
  --ink-soft: #4c5b68;
  --ink-faint: #7b8894;
  --accent: #1c6b57;
  --accent-soft: #e4efeb;
  --positive: #1c6b57;
  --negative: #a8402f;
  --game-sat: var(--l-game-sat);
  --game-lum: var(--l-game-lum);
  --series-1: #118a69;
  --series-2: #6b4fbb;
  --series-3: #b1652a;
  --series-4: #2b7ec4;
  --series-5: #97324c;
  --shadow-pop: 0 6px 24px rgba(19, 26, 33, 0.12);

  --rail-width: 224px;
  --radius: 10px;
  --radius-sm: 6px;
  --ease: cubic-bezier(0.22, 0.61, 0.36, 1);
}

/* Follow the OS, unless the reader has explicitly asked for light. */
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    --paper: var(--d-paper);
    --surface: var(--d-surface);
    --surface-sunk: var(--d-surface-sunk);
    --line: var(--d-line);
    --line-strong: var(--d-line-strong);
    --ink: var(--d-ink);
    --ink-soft: var(--d-ink-soft);
    --ink-faint: var(--d-ink-faint);
    --accent: var(--d-accent);
    --accent-soft: var(--d-accent-soft);
    --positive: var(--d-positive);
    --negative: var(--d-negative);
    --game-sat: var(--d-game-sat);
    --game-lum: var(--d-game-lum);
    --game-sat: var(--d-game-sat);
  --game-lum: var(--d-game-lum);
  --series-1: var(--d-series-1);
    --series-2: var(--d-series-2);
    --series-3: var(--d-series-3);
    --series-4: var(--d-series-4);
    --series-5: var(--d-series-5);
    --shadow-pop: var(--d-shadow-pop);
  }
}

/* An explicit dark choice wins over a light OS. */
:root[data-theme="dark"] {
  --paper: var(--d-paper);
  --surface: var(--d-surface);
  --surface-sunk: var(--d-surface-sunk);
  --line: var(--d-line);
  --line-strong: var(--d-line-strong);
  --ink: var(--d-ink);
  --ink-soft: var(--d-ink-soft);
  --ink-faint: var(--d-ink-faint);
  --accent: var(--d-accent);
  --accent-soft: var(--d-accent-soft);
  --positive: var(--d-positive);
  --negative: var(--d-negative);
  --game-sat: var(--d-game-sat);
  --game-lum: var(--d-game-lum);
  --series-1: var(--d-series-1);
  --series-2: var(--d-series-2);
  --series-3: var(--d-series-3);
  --series-4: var(--d-series-4);
  --series-5: var(--d-series-5);
  --shadow-pop: var(--d-shadow-pop);
}

*,
*::before,
*::after {
  box-sizing: border-box;
}

html,
body {
  height: 100%;
}

body {
  margin: 0;
  background: var(--paper);
  color: var(--ink);
  font-family: 'Instrument Sans', ui-sans-serif, system-ui, -apple-system, 'Segoe UI', sans-serif;
  font-size: 14px;
  line-height: 1.5;
  -webkit-font-smoothing: antialiased;
}

button {
  font: inherit;
  color: inherit;
  cursor: pointer;
}

:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
  border-radius: 3px;
}

.numeric {
  font-variant-numeric: tabular-nums;
  font-feature-settings: 'tnum' 1;
}

/* ===========================================================================
   Shell
   ======================================================================== */
.shell {
  display: grid;
  grid-template-columns: var(--rail-width) minmax(0, 1fr);
  min-height: 100vh;
}

.rail {
  background: var(--surface);
  border-right: 1px solid var(--line);
  display: flex;
  flex-direction: column;
  position: sticky;
  top: 0;
  height: 100vh;
}

/* The single-column mobile layout has no rail beside the content, so there is
   nothing to resize and the handle would sit over the nav. */
@media (max-width: 760px) {
  .rail__resize { display: none; }
}

.rail__brand {
  padding: 22px 20px 18px;
  display: flex;
  align-items: baseline;
  gap: 8px;
}

.rail__mark {
  font-size: 17px;
  font-weight: 600;
  letter-spacing: -0.015em;
}

.rail__model {
  font-size: 11.5px;
  color: var(--ink-faint);
}

.rail__nav {
  padding: 4px 12px;
  display: flex;
  flex-direction: column;
  gap: 2px;
  flex: 1;
  overflow-y: auto;
}

.rail__link {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 8px 10px;
  border: 0;
  background: none;
  border-radius: var(--radius-sm);
  color: var(--ink-soft);
  text-align: left;
  width: 100%;
  transition: background 0.12s var(--ease), color 0.12s var(--ease);
}

.rail__link:hover {
  background: var(--surface-sunk);
  color: var(--ink);
}

.rail__link[aria-current='page'] {
  background: var(--accent-soft);
  color: var(--accent);
  font-weight: 500;
}

.rail__link svg {
  width: 16px;
  height: 16px;
  flex: none;
}

/* Admin: a page hidden from everyone else stays in the rail, dimmed, with an
   eye to flip it. The link and the eye are siblings, never nested buttons. */
.rail__row {
  display: flex;
  align-items: center;
  gap: 2px;
}

.rail__row .rail__link {
  min-width: 0;
  flex: 1;
}

/* The drag handle. Hidden until the row is hovered or the handle is focused,
   so an admin's rail reads the same as everyone else's until they reach for
   it — but it keeps its width either way, or every row would shift sideways
   on hover. */
.rail__grip {
  flex: none;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 18px;
  height: 26px;
  padding: 0;
  border: 0;
  background: none;
  color: var(--ink-faint);
  cursor: grab;
  opacity: 0;
  transition: opacity 0.12s var(--ease);
}

.rail__grip svg {
  width: 16px;
  height: 16px;
  fill: currentColor;
  stroke: none;
}

.rail__row:hover .rail__grip,
.rail__grip:focus-visible {
  opacity: 1;
}

.rail__grip:active {
  cursor: grabbing;
}

.rail__row.is-dragging {
  opacity: 0.4;
}

/* Where it will land. A line rather than a gap: a gap moves every row below it
   and the whole rail jitters as the pointer crosses each one. */
.rail__row.is-drop-before,
.rail__row.is-drop-after {
  position: relative;
}

.rail__row.is-drop-before::before,
.rail__row.is-drop-after::after {
  content: '';
  position: absolute;
  left: 4px;
  right: 4px;
  height: 2px;
  border-radius: 2px;
  background: var(--accent);
}

.rail__row.is-drop-before::before {
  top: -1px;
}

.rail__row.is-drop-after::after {
  bottom: -1px;
}

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

.rail__link.is-hidden {
  color: var(--ink-faint);
}

.rail__link.is-hidden span {
  text-decoration: line-through;
  text-decoration-color: var(--line-strong);
}

.rail__eye {
  flex: none;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 26px;
  height: 26px;
  padding: 0;
  border: 0;
  border-radius: var(--radius-sm);
  background: none;
  color: var(--ink-faint);
  opacity: 0.55;
  transition: opacity 0.12s var(--ease), color 0.12s var(--ease), background 0.12s var(--ease);
}

.rail__row:hover .rail__eye,
.rail__eye:focus-visible,
.rail__eye[aria-pressed='true'] {
  opacity: 1;
}

.rail__eye:hover {
  background: var(--surface-sunk);
  color: var(--ink);
}

.rail__eye[aria-pressed='true'] {
  color: var(--negative);
}

.rail__eye:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 1px;
}

.rail__eye svg {
  width: 14px;
  height: 14px;
}

/* A section header is a button now, so it needs the affordances of one while
   still reading as the quiet divider it was. */
.rail__section {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
  width: calc(100% - 20px);
  margin: 10px 10px 4px;
  padding: 10px 0 4px;
  border: 0;
  border-top: 1px solid var(--line);
  background: none;
  color: var(--ink-faint);
  font: inherit;
  font-size: 9px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  text-align: left;
  cursor: pointer;
}

.rail__section:hover {
  color: var(--ink-soft);
}

.rail__section:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
  border-radius: 3px;
}

.rail__chevron {
  width: 12px;
  height: 12px;
  flex: none;
  transition: transform 0.15s var(--ease);
}

.rail__section[aria-expanded='false'] .rail__chevron {
  transform: rotate(-90deg);
}

.rail__group {
  display: flex;
  flex-direction: column;
  gap: 2px;
}

.rail__group[hidden] {
  display: none;
}

.rail__foot {
  border-top: 1px solid var(--line);
  padding: 14px 20px;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
}

.rail__who {
  min-width: 0;
}

.rail__name {
  font-size: 13px;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.rail__upn {
  font-size: 11.5px;
  color: var(--ink-faint);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.linkish {
  background: none;
  border: 0;
  padding: 0;
  color: var(--ink-faint);
  font-size: 12.5px;
  text-decoration: underline;
  text-underline-offset: 2px;
}

.linkish:hover {
  color: var(--ink);
}

/* ===========================================================================
   Topbar and filters
   ======================================================================== */
.main {
  min-width: 0;
  display: flex;
  flex-direction: column;
}

.topbar {
  position: sticky;
  top: 0;
  z-index: 20;
  background: color-mix(in srgb, var(--paper) 88%, transparent);
  backdrop-filter: blur(10px);
  border-bottom: 1px solid var(--line);
  padding: 16px 28px;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 20px;
  flex-wrap: wrap;
}

.topbar__title {
  margin: 0;
  font-size: 19px;
  font-weight: 600;
  letter-spacing: -0.02em;
}

.topbar__meta {
  font-size: 12px;
  color: var(--ink-faint);
  margin-top: 1px;
}

.filters {
  display: flex;
  align-items: center;
  gap: 8px;
}

.range {
  display: inline-flex;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  /* Not `hidden`: the exact-date popover is a descendant now, and clipping the
     group to its own rounded corners would cut the popover off too. The
     corners are rounded on the end segments instead. */
  overflow: visible;
}

.range > :first-child,
.range > :first-child > .date-picker__toggle {
  border-top-left-radius: calc(var(--radius-sm) - 1px);
  border-bottom-left-radius: calc(var(--radius-sm) - 1px);
}

.range > :last-child,
.range > :last-child > .date-picker__toggle {
  border-top-right-radius: calc(var(--radius-sm) - 1px);
  border-bottom-right-radius: calc(var(--radius-sm) - 1px);
}

.range > button,
.range > .date-picker {
  border-right: 1px solid var(--line);
}

.range > :last-child {
  border-right: 0;
}

/* `>` throughout: the exact-date popover lives inside this group, and a
   descendant selector here would reach into its buttons and its fields. */
.range > button {
  border: 0;
  background: none;
  padding: 6px 11px;
  font-size: 12.5px;
  color: var(--ink-soft);
}

/* Two labels in the DOM, one shown. The swap is a media query rather than a
   resize listener, so it happens with the layout instead of after it. */
.range__short {
  display: none;
}

.range > button[aria-pressed='true'] {
  background: var(--accent);
  color: #fff;
}

/* ---------------------------------------------------------------------------
   Exact date range
   The last segment of the preset row, so choosing "the last N days" and
   choosing exact dates are visibly the same control. That means the group can
   no longer clip its overflow, or the popover would be cut off at the first
   hairline — hence `overflow: visible` on `.range` above and the hairline
   drawn per button instead.
   ------------------------------------------------------------------------ */
.date-picker {
  position: relative;
  display: inline-flex;
}

.date-picker__toggle {
  border: 0;
  background: none;
  padding: 6px 9px;
  color: var(--ink-soft);
  display: inline-flex;
  align-items: center;
}

.date-picker__toggle svg {
  width: 14px;
  height: 14px;
}

.date-picker__toggle:hover {
  color: var(--ink);
}

.date-picker__toggle[aria-pressed='true'] {
  background: var(--accent);
  color: #fff;
}

.date-picker__toggle[aria-expanded='true'] {
  background: var(--accent-soft);
  color: var(--ink);
}

.date-picker__toggle[aria-pressed='true'][aria-expanded='true'] {
  background: var(--accent);
  color: #fff;
}

.date-picker__pop {
  position: absolute;
  top: calc(100% + 6px);
  right: 0;
  z-index: 40;
  width: max-content;
  /* A calendar is tall, and a landscape phone or a short desktop window is not.
     Without a ceiling the Apply button ends up below the fold with no way to
     reach it — the popover is anchored, so the page cannot scroll to it. */
  max-height: calc(100vh - 72px);
  overflow-y: auto;
  padding: 12px;
  display: grid;
  gap: 10px;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  box-shadow: var(--shadow-pop);
}

/* Relative builder: a direction, then a period. Two segmented rows rather than
   twelve buttons, because "Last" and "This" apply to all four periods and a
   grid of every combination is twelve labels saying four things. */
.date-picker__relative {
  display: grid;
  gap: 6px;
}

.date-picker__dirs,
.date-picker__periods {
  display: grid;
  gap: 4px;
}

.date-picker__dirs {
  grid-template-columns: repeat(2, 1fr);
}

.date-picker__periods {
  grid-template-columns: repeat(4, 1fr);
}

.date-picker__dirs button,
.date-picker__periods button {
  padding: 5px 4px;
  font-size: 11.5px;
  color: var(--ink-soft);
  background: var(--surface-sunk);
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
}

.date-picker__dirs button:hover,
.date-picker__periods button:hover {
  border-color: var(--line-strong);
  color: var(--ink);
}

.date-picker__dirs button[aria-pressed='true'] {
  background: var(--accent);
  border-color: var(--accent);
  color: #fff;
}

/* Reports, never accepts. A range is two dates that have to agree, and the
   calendar cannot express an invalid one — so this is a readout, styled as a
   field because that is where someone looks for the answer. */
.date-picker__range {
  width: 100%;
  padding: 6px 9px;
  font: inherit;
  font-size: 12.5px;
  font-variant-numeric: tabular-nums;
  text-align: center;
  color: var(--ink);
  background: var(--surface-sunk);
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  cursor: default;
}

.date-picker__range:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 1px;
}

.date-picker__cal {
  display: grid;
  gap: 4px;
}

.date-picker__calhead {
  display: grid;
  grid-template-columns: 26px 1fr 26px;
  align-items: center;
}

.date-picker__month {
  text-align: center;
  font-size: 12.5px;
  font-weight: 500;
  color: var(--ink);
}

.date-picker__nav {
  width: 26px;
  height: 26px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border: 0;
  background: none;
  border-radius: var(--radius-sm);
  color: var(--ink-soft);
}

.date-picker__nav:hover {
  background: var(--surface-sunk);
  color: var(--ink);
}

.date-picker__nav svg {
  width: 15px;
  height: 15px;
}

.date-picker__dow,
.date-picker__grid {
  display: grid;
  grid-template-columns: repeat(7, 30px);
  gap: 1px;
}

.date-picker__dow span {
  text-align: center;
  font-size: 10.5px;
  color: var(--ink-faint);
  padding-bottom: 2px;
}

.date-picker__day {
  height: 28px;
  border: 0;
  background: none;
  border-radius: var(--radius-sm);
  font-size: 12px;
  font-variant-numeric: tabular-nums;
  color: var(--ink-soft);
}

.date-picker__day:hover:not(:disabled) {
  background: var(--accent-soft);
  color: var(--ink);
}

.date-picker__day[data-outside] {
  color: var(--ink-faint);
  opacity: 0.55;
}

.date-picker__day:disabled {
  opacity: 0.25;
  cursor: not-allowed;
}

/* Today is marked, not selected — it is where the data stops, which matters
   when the calendar will happily let you pick past it. */
.date-picker__day[data-today] {
  box-shadow: inset 0 0 0 1px var(--line-strong);
}

/* Square-ended in the middle of the run so the days read as one block, rounded
   at the two ends so the block has visible edges. */
.date-picker__day[data-in-range] {
  background: var(--accent-soft);
  color: var(--ink);
  border-radius: 0;
}

.date-picker__day[data-start] {
  border-top-left-radius: var(--radius-sm);
  border-bottom-left-radius: var(--radius-sm);
}

.date-picker__day[data-end] {
  border-top-right-radius: var(--radius-sm);
  border-bottom-right-radius: var(--radius-sm);
}

.date-picker__day[data-start],
.date-picker__day[data-end] {
  background: var(--accent);
  color: #fff;
  font-weight: 600;
}

.date-picker__note {
  margin: 0;
  min-height: 15px;
  font-size: 11.5px;
  color: var(--ink-faint);
}

.date-picker__actions {
  display: flex;
  justify-content: flex-end;
  gap: 6px;
}

.date-picker__actions .btn--primary {
  background: var(--accent);
  border-color: var(--accent);
  color: #fff;
}

.date-picker__actions .btn--primary:hover {
  color: #fff;
}

.date-picker__actions .btn[disabled] {
  opacity: 0.5;
  cursor: not-allowed;
}

.btn {
  border: 1px solid var(--line);
  background: var(--surface);
  border-radius: var(--radius-sm);
  padding: 6px 11px;
  font-size: 12.5px;
  color: var(--ink-soft);
  display: inline-flex;
  align-items: center;
  gap: 6px;
}

.btn:hover {
  border-color: var(--line-strong);
  color: var(--ink);
}

.btn svg {
  width: 14px;
  height: 14px;
}

.content {
  padding: 22px 28px 60px;
  display: flex;
  flex-direction: column;
  gap: 20px;
}

/* ===========================================================================
   Stat band
   One continuous surface split by hairlines rather than a row of identical
   cards — the figures are one thought, not four separate objects.
   ======================================================================== */
.stats {
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
  overflow: hidden;
}

.stat {
  padding: 16px 20px;
  border-right: 1px solid var(--line);
  min-width: 0;
}

.stat:last-child {
  border-right: 0;
}

.stat__label {
  font-size: 12.5px;
  color: var(--ink-faint);
}

.stat__value {
  font-size: 27px;
  font-weight: 600;
  letter-spacing: -0.025em;
  line-height: 1.2;
  margin-top: 3px;
}

.stat__delta {
  font-size: 12.5px;
  margin-top: 3px;
  display: flex;
  align-items: center;
  gap: 5px;
  color: var(--ink-faint);
}

.stat__delta b {
  font-weight: 500;
}

.stat__delta.is-up b {
  color: var(--positive);
}

.stat__delta.is-down b {
  color: var(--negative);
}

/* ===========================================================================
   Panels and charts
   ======================================================================== */
.grid {
  display: grid;
  gap: 20px;
  grid-template-columns: minmax(0, 1.75fr) minmax(0, 1fr);
}

@media (max-width: 1080px) {
  .grid {
    grid-template-columns: minmax(0, 1fr);
  }
}

.panel {
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  display: flex;
  flex-direction: column;
  min-width: 0;
}

.panel__head {
  padding: 14px 18px 8px;
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 12px;
}

/* The brand is a link now, so it must not inherit link colour or underline. */
.rail__brand { color: inherit; text-decoration: none; }
.rail__logo { display: block; width: 100%; max-width: 172px; height: auto; }
/* The wordmark ships as two artworks — dark type for a light ground, pale for
   a dark one — because a single flat version would go muddy on one of them.
   Only the alt text on the light one is read out; the other is decorative. */
.rail__logo--dark { display: none; }
/* The same three states the tokens use: follow the OS unless the reader chose,
   and let an explicit choice win either way. Keying this off the media query
   alone would show dark-on-dark type the moment someone overrode the theme. */
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .rail__logo--light { display: none; }
  :root:not([data-theme="light"]) .rail__logo--dark { display: block; }
}
:root[data-theme="dark"] .rail__logo--light { display: none; }
:root[data-theme="dark"] .rail__logo--dark { display: block; }
:root[data-theme="light"] .rail__logo--light { display: block; }
:root[data-theme="light"] .rail__logo--dark { display: none; }
/* The text is only for when the logo asset is missing; once it loads the
   wordmark is in the image and repeating it would read as a stutter. */
.rail__brand:not(.is-textonly) .rail__mark, .rail__brand:not(.is-textonly) .rail__model { display: none; }

/* A single highlighted column, tinted rather than bolded: bold competes with
   the sort indicator and the totals row, a tint does not. */
/* A comparison column whose value has drifted past its tolerance. Not a
   direction signal — see tintBeyond in table.js. */
.data .cell-metric__value.is-off { color: var(--negative); }

.data td.is-emphasis, .data th.is-emphasis { background: color-mix(in srgb, var(--accent) 8%, transparent); }
.data td.is-emphasis { color: var(--ink); font-weight: 500; }

.figures { display: grid; grid-template-columns: repeat(auto-fit, minmax(180px, 1fr)); gap: 22px 36px; padding: 4px 0 6px; }
.figures__item { display: grid; grid-template-rows: auto auto 1fr; gap: 6px; align-content: start; min-width: 0; }
.figures__label { font-size: 10px; font-weight: 500; letter-spacing: .05em; text-transform: uppercase; color: var(--ink-faint); }
.figures__value { font-size: 26px; font-weight: 500; color: var(--ink); line-height: 1.1; letter-spacing: -0.01em; font-variant-numeric: tabular-nums; overflow-wrap: anywhere; }
.figures__hint { font-size: 11px; color: var(--ink-soft); line-height: 1.45; max-width: 34ch; }
.figures__item.is-good .figures__value { color: var(--accent); }
.figures__item.is-bad .figures__value { color: var(--negative); }

/* Sits on the rail's own border so the border becomes the affordance. Wider
   than it looks: 4px is visible, 9px is grabbable, and the difference is the
   whole usability of a splitter. */
.rail__resize { position: absolute; top: 0; right: -4px; width: 9px; height: 100%; z-index: 5; cursor: col-resize; background: none; border: 0; }
.rail__resize::after { content: ''; position: absolute; top: 0; right: 4px; width: 1px; height: 100%; background: var(--accent); opacity: 0; transition: opacity .12s var(--ease); }
.rail__resize:hover::after, .rail__resize:focus-visible::after, body.is-resizing-rail .rail__resize::after { opacity: 1; }
.rail__resize:focus-visible { outline: none; }
.rail__resize:focus-visible::after { width: 2px; }
/* While dragging: no text selection anywhere, and the resize cursor holds even
   when the pointer runs ahead of the handle. */
body.is-resizing-rail { cursor: col-resize; user-select: none; }

.rail__gear { display: grid; place-items: center; width: 26px; height: 26px; padding: 0; border: 0; border-radius: var(--radius-sm); background: none; color: var(--ink-faint); cursor: pointer; }
.rail__gear svg { width: 16px; height: 16px; }
.rail__gear:hover { color: var(--ink); background: var(--surface-sunk); }
.rail__gear:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

/* Backdrop covers the rail as well: the panel is about the whole app, not the
   page behind it, and a half-covered layout reads as a dropdown gone wrong. */
.settings { position: fixed; inset: 0; z-index: 200; display: grid; place-items: center; padding: 24px; background: rgb(0 0 0 / .45); }
.settings__sheet { width: 100%; max-width: 420px; background: var(--surface); border: 1px solid var(--line); border-radius: var(--radius); box-shadow: var(--shadow-pop); }
.settings__head { display: flex; align-items: center; justify-content: space-between; padding: 16px 18px; border-bottom: 1px solid var(--line); }
.settings__head h2 { margin: 0; font-size: 14px; font-weight: 600; }
.settings__close { border: 0; background: none; color: var(--ink-faint); font-size: 22px; line-height: 1; cursor: pointer; padding: 0 4px; }
.settings__close:hover { color: var(--ink); }
.settings__group { padding: 18px; border-bottom: 1px solid var(--line); }
.settings__group:last-child { border-bottom: 0; }
.settings__group h3 { margin: 0 0 12px; font-size: 10px; font-weight: 500; letter-spacing: .04em; text-transform: uppercase; color: var(--ink-faint); }
.settings__themes { display: inline-flex; }
.settings__hint { margin: 10px 0 0; font-size: 11px; color: var(--ink-soft); min-height: 1em; }
.settings__admin { width: 100%; justify-content: center; }

.info { position: relative; display: inline-flex; margin-left: 7px; vertical-align: middle; }
.info__btn { width: 15px; height: 15px; border-radius: 50%; border: 1px solid var(--line-strong, var(--line)); background: none; color: var(--ink-soft); font: inherit; font-size: 9px; font-style: italic; font-weight: 600; line-height: 1; display: grid; place-items: center; cursor: pointer; padding: 0; }
.info__btn:hover, .info__btn[aria-expanded="true"] { border-color: var(--accent); color: var(--accent); }
.info__btn:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.info__pop { position: absolute; top: 22px; left: -8px; z-index: 30; width: max-content; max-width: 320px; padding: 10px 12px; border: 1px solid var(--line); border-radius: var(--radius-sm); background: var(--surface); color: var(--ink-soft); box-shadow: 0 10px 30px rgb(0 0 0 / .28); font-size: 11.5px; font-weight: 400; line-height: 1.5; white-space: normal; text-transform: none; letter-spacing: 0; }
@media (max-width: 760px) { .info__pop { max-width: 240px; left: auto; right: -8px; } }

.matrix__table { width: 100%; min-width: 620px; border-collapse: collapse; font-variant-numeric: tabular-nums; }
.matrix__table th, .matrix__table td { text-align: right; padding: 8px 14px; border-bottom: 1px solid var(--line); vertical-align: middle; white-space: nowrap; }
.matrix__table thead th { font-size: 10px; font-weight: 500; color: var(--ink-soft); white-space: nowrap; }
.matrix__table th:first-child { text-align: left; padding-left: 0; }
.matrix__table b { font-size: 13px; font-weight: 500; }
.matrix__table small { margin-left: 8px; font-size: 10px; font-weight: 400; color: var(--ink-faint); text-transform: none; letter-spacing: 0; }
.matrix__table .is-total { background: var(--surface-sunk); }
/* A wide matrix scrolls sideways, and the two columns you navigate by — which
   month, and the row's total — are the first to leave the screen. Pinning them
   keeps every cell readable against something. Opaque backgrounds are required
   or the scrolling cells show through. */
.matrix__table th:first-child { position: sticky; left: 0; z-index: 2; background: var(--surface); }
.matrix__table tfoot th:first-child { background: var(--surface-sunk); }
.matrix__table th.is-total, .matrix__table td.is-total { position: sticky; right: 0; z-index: 1; }
.matrix__table thead th.is-total { z-index: 3; }
.matrix__table tfoot th, .matrix__table tfoot td { border-top: 2px solid var(--line-strong); border-bottom: 0; background: var(--surface-sunk); font-weight: 500; padding-top: 11px; padding-bottom: 11px; }
.matrix__table tfoot th:first-child { font-size: 12px; }
.matrix__table tfoot b { font-size: 14px; }

.segments { display: flex; gap: 2px; margin-left: auto; border: 1px solid var(--line); padding: 3px; border-radius: var(--radius-sm); }
.segments button { border: 0; border-radius: 3px; padding: 6px 10px; color: var(--ink-soft); background: transparent; font: inherit; font-size: 11px; cursor: pointer; }
.segments button[aria-pressed="true"] { background: var(--accent-soft); color: var(--accent); }
.segments button:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

.panel__title {
  display: flex;
  align-items: center;
  gap: 7px;
  margin: 0;
  font-size: 14px;
  font-weight: 600;
}

.panel__title svg {
  width: 15px;
  height: 15px;
  flex: none;
  color: var(--ink-faint);
}

.panel__note {
  font-size: 12px;
  color: var(--ink-faint);
}

.panel__body {
  padding: 4px 10px 14px;
  flex: 1;
  min-height: 0;
}

/* Analytics page sections: Device (Device/Orientation/Browser/OS) and
   Behaviour (play-setting splits, engagement, bet mode by game) — plain CSS
   proportion bars, not SVG. Equal-width cards within a section (unlike the
   asymmetric trend+rank .grid), since cards in one section carry equal
   visual weight; 2x2 on desktop, single column once narrow. */

.analytics-section {
  display: flex;
  flex-direction: column;
  gap: 14px;
}

.analytics-section__title {
  margin: 0;
  color: var(--ink);
  font-size: 15px;
  font-weight: 600;
}

.split-grid {
  display: grid;
  gap: 20px;
  grid-template-columns: 1fr 1fr;
}

@media (max-width: 1080px) {
  .split-grid {
    grid-template-columns: 1fr;
  }
}

.split-figures {
  display: flex;
  flex-wrap: wrap;
  gap: 18px 24px;
  padding: 6px 8px 16px;
}

.split-figure {
  display: flex;
  align-items: center;
  gap: 9px;
}

.split-figure__icon {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 30px;
  height: 30px;
  flex: none;
  border-radius: 8px;
  background: var(--surface-sunk);
  color: var(--series-color);
}

.split-figure__icon svg {
  width: 17px;
  height: 17px;
}

.split-figure__text {
  display: flex;
  flex-direction: column;
  gap: 2px;
}

.split-figure__text strong {
  color: var(--series-color);
  font-size: 23px;
  font-weight: 600;
  font-variant-numeric: tabular-nums;
  line-height: 1.1;
}

.split-figure__text span {
  color: var(--ink-soft);
  font-size: 11px;
}

.split-bar {
  display: flex;
  gap: 2px;
  height: 14px;
  margin: 0 8px;
  border-radius: 7px;
  background: var(--surface-sunk);
  overflow: hidden;
}

.split-segment {
  height: 100%;
  min-width: 2px;
}

.split-empty {
  padding: 24px 8px;
  color: var(--ink-faint);
  font-size: 12px;
  text-align: center;
}

/* Metric row: independent single-value figures (splash/paytable reach,
   median read time) — not shares of a whole, so neutral-inked and with no
   proportion bar or legend, unlike split-figure. */
.metric-row {
  display: flex;
  flex-wrap: wrap;
  gap: 20px 32px;
  padding: 10px 8px 20px;
}

.metric-figure {
  display: flex;
  align-items: center;
  gap: 10px;
}

.metric-figure__icon {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 30px;
  height: 30px;
  flex: none;
  border-radius: 8px;
  background: var(--surface-sunk);
  color: var(--ink-faint);
}

.metric-figure__icon svg {
  width: 17px;
  height: 17px;
}

.metric-figure__text {
  display: flex;
  flex-direction: column;
  gap: 2px;
}

.metric-figure__text strong {
  color: var(--ink);
  font-size: 23px;
  font-weight: 600;
  font-variant-numeric: tabular-nums;
  line-height: 1.1;
}

.metric-figure__text span {
  color: var(--ink-soft);
  font-size: 11px;
}

/* Bet mode by game: small multiples, one stacked proportion bar per game,
   sharing the section's single legend. Label + bar side by side rather than
   stacked, so many games read as a scannable table, not a tall gallery. */
.game-breakdown {
  display: flex;
  flex-direction: column;
  gap: 12px;
  padding: 8px 8px 4px;
}

.game-breakdown__row {
  display: flex;
  align-items: center;
  gap: 16px;
}

.game-breakdown__label {
  display: flex;
  flex-direction: column;
  gap: 1px;
  width: 190px;
  flex: none;
  min-width: 0;
}

.game-breakdown__label span {
  color: var(--ink);
  font-size: 12.5px;
  font-weight: 500;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.game-breakdown__label b {
  color: var(--ink-faint);
  font-size: 11px;
  font-weight: 500;
  font-variant-numeric: tabular-nums;
}

.game-breakdown__bar {
  flex: 1;
  height: 12px;
  margin: 0;
}

@media (max-width: 720px) {
  .game-breakdown__row {
    flex-direction: column;
    align-items: stretch;
    gap: 6px;
  }

  .game-breakdown__label {
    width: auto;
    flex-direction: row;
    justify-content: space-between;
  }
}

.legend {
  display: flex;
  gap: 14px;
  padding: 0 18px 10px;
  font-size: 12px;
  color: var(--ink-soft);
}

.legend span {
  display: inline-flex;
  align-items: center;
  gap: 6px;
}

.legend i {
  width: 9px;
  height: 9px;
  border-radius: 2px;
  display: inline-block;
}

.chart {
  width: 100%;
  display: block;
  overflow: visible;
}

.chart .axis-label {
  font-size: 11px;
  fill: var(--ink-faint);
}

.chart .gridline {
  stroke: var(--line);
  stroke-width: 1;
}

.chart .series-line {
  opacity: 0.96;
  fill: none;
  stroke-width: 2.25;
  stroke-linecap: round;
  stroke-linejoin: round;
}

.chart .marker {
  stroke: var(--surface);
  stroke-width: 2;
}

.chart .bar {
  transition: opacity 0.12s var(--ease);
}

.chart:hover .bar:not(:hover) {
  opacity: 0.62;
}

.chart .hit {
  fill: transparent;
}

.chart-tip {
  position: fixed;
  z-index: 60;
  pointer-events: none;
  background: var(--surface);
  border: 1px solid var(--line-strong);
  border-radius: var(--radius-sm);
  box-shadow: var(--shadow-pop);
  padding: 8px 10px;
  font-size: 12.5px;
  min-width: 130px;
  opacity: 0;
  transform: translateY(3px);
  transition: opacity 0.1s var(--ease), transform 0.1s var(--ease);
}

.chart-tip.is-open {
  opacity: 1;
  transform: translateY(0);
}

.chart-tip__label {
  color: var(--ink-faint);
  margin-bottom: 4px;
}

.chart-tip__row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 14px;
}

.chart-tip__row i {
  width: 8px;
  height: 8px;
  border-radius: 2px;
  margin-right: 6px;
  display: inline-block;
}

/* ===========================================================================
   Table
   ======================================================================== */
.table-wrap {
  overflow: auto;
  max-height: 620px;
  border-top: 1px solid var(--line);
}

table.data {
  border-collapse: separate;
  border-spacing: 0;
  width: 100%;
  table-layout: fixed;
}

table.data th,
table.data td {
  padding: 0 12px;
  height: 38px;
  text-align: left;
  border-bottom: 1px solid var(--line);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

table.data thead th {
  position: sticky;
  top: 0;
  z-index: 3;
  background: var(--surface-sunk);
  font-size: 12px;
  font-weight: 500;
  color: var(--ink-soft);
  user-select: none;
  border-bottom: 1px solid var(--line-strong);
}

table.data th.is-frozen,
table.data td.is-frozen {
  position: sticky;
  left: 0;
  z-index: 2;
  background: var(--surface);
}

table.data thead th.is-frozen {
  z-index: 4;
  background: var(--surface-sunk);
}

table.data tbody tr:hover td {
  background: var(--surface-sunk);
}

/* Row disclosure. The detail cell has to undo the three things every other
   data cell wants — fixed height, nowrap and overflow:hidden — because it is
   the one cell whose content is meant to wrap and grow. */
table.data td.has-expand {
  padding-left: 4px;
}

.cell-expand {
  width: 18px;
  height: 18px;
  margin-right: 2px;
  padding: 0;
  border: 0;
  border-radius: 4px;
  background: none;
  color: var(--ink-faint);
  font-size: 10px;
  line-height: 1;
  cursor: pointer;
  transition: transform 120ms var(--ease), color 120ms ease;
}

.cell-expand:hover {
  color: var(--ink);
  background: var(--surface-sunk);
}

.cell-expand[aria-expanded="true"] {
  transform: rotate(90deg);
  color: var(--ink);
}

table.data tbody tr.is-open td {
  border-bottom-color: transparent;
}

table.data tbody tr.is-detail td {
  height: auto;
  padding: 0;
  white-space: normal;
  overflow: visible;
  background: var(--surface-sunk);
}

table.data tbody tr.is-detail:hover td {
  background: var(--surface-sunk);
}

table.data td.align-right,
table.data th.align-right {
  text-align: right;
}

.th-inner {
  display: flex;
  align-items: center;
  gap: 5px;
  height: 100%;
  cursor: grab;
}

.th-inner.align-right {
  justify-content: flex-end;
}

th.is-dragging {
  opacity: 0.4;
}

th.is-drop-before::after,
th.is-drop-after::after {
  content: '';
  position: absolute;
  top: 0;
  bottom: 0;
  width: 2px;
  background: var(--accent);
}

th.is-drop-before::after {
  left: -1px;
}

th.is-drop-after::after {
  right: -1px;
}

.sort-mark {
  color: var(--accent);
  font-size: 10px;
  line-height: 1;
}

.resizer {
  position: absolute;
  top: 0;
  right: -3px;
  width: 7px;
  height: 100%;
  cursor: col-resize;
  z-index: 5;
}

.resizer:hover::after,
.resizer.is-active::after {
  content: '';
  position: absolute;
  top: 0;
  bottom: 0;
  left: 3px;
  width: 1px;
  background: var(--accent);
}

table.data th {
  position: relative;
}

.cell-bar {
  position: absolute;
  left: 0;
  top: 50%;
  transform: translateY(-50%) scaleX(0);
  transform-origin: left center;
  height: 22px;
  background: var(--accent-soft);
  border-radius: 3px;
  z-index: 0;
}

td.has-bar {
  position: relative;
}

td.has-bar span {
  position: relative;
  z-index: 1;
}

.tag {
  display: inline-block;
  padding: 1px 7px;
  border-radius: 999px;
  background: var(--surface-sunk);
  border: 1px solid var(--line);
  font-size: 12px;
  color: var(--ink-soft);
}

.table-tools {
  position: relative;
}

.col-menu {
  position: absolute;
  right: 0;
  top: calc(100% + 6px);
  background: var(--surface);
  border: 1px solid var(--line-strong);
  border-radius: var(--radius-sm);
  box-shadow: var(--shadow-pop);
  padding: 6px;
  min-width: 200px;
  z-index: 30;
  max-height: 320px;
  overflow: auto;
}

.col-menu label {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 6px 8px;
  border-radius: 4px;
  font-size: 13px;
  cursor: pointer;
}

.col-menu label:hover {
  background: var(--surface-sunk);
}

.col-menu hr {
  border: 0;
  border-top: 1px solid var(--line);
  margin: 5px 0;
}

/* ===========================================================================
   States
   ======================================================================== */
.skeleton {
  background: linear-gradient(90deg, var(--surface-sunk) 25%, var(--line) 37%, var(--surface-sunk) 63%);
  background-size: 400% 100%;
  border-radius: 4px;
  animation: shimmer 1.3s ease-in-out infinite;
}

@keyframes shimmer {
  0% { background-position: 100% 50%; }
  100% { background-position: 0 50%; }
}

.notice {
  border: 1px solid var(--line);
  border-left: 3px solid var(--negative);
  border-radius: 0 var(--radius-sm) var(--radius-sm) 0;
  background: var(--surface);
  padding: 14px 16px;
}

.notice h3 {
  margin: 0 0 4px;
  font-size: 14px;
  font-weight: 600;
}

.notice p {
  margin: 0;
  color: var(--ink-soft);
}

.empty {
  padding: 44px 20px;
  text-align: center;
  color: var(--ink-faint);
}

/* ===========================================================================
   Load sequence
   One orchestrated entrance on page load: figures count up, the trend line
   draws itself, bars grow from the baseline, rows settle in. Nothing animates
   on hover or scroll — motion here means "this just arrived".
   ======================================================================== */
.rise {
  animation: rise 0.42s var(--ease) both;
  animation-delay: var(--delay, 0ms);
}

@keyframes rise {
  from {
    opacity: 0;
    transform: translateY(7px);
  }
  to {
    opacity: 1;
    transform: none;
  }
}

.draw {
  stroke-dasharray: var(--len);
  stroke-dashoffset: var(--len);
  animation: draw 0.95s var(--ease) forwards;
  animation-delay: var(--delay, 120ms);
}

@keyframes draw {
  to {
    stroke-dashoffset: 0;
  }
}

.fade-in {
  animation: fade 0.5s var(--ease) both;
  animation-delay: var(--delay, 0ms);
}

@keyframes fade {
  from { opacity: 0; }
  to { opacity: 1; }
}

.grow {
  transform: scaleY(0);
  transform-origin: bottom;
  animation: grow 0.5s var(--ease) forwards;
  animation-delay: var(--delay, 0ms);
}

@keyframes grow {
  to {
    transform: scaleY(1);
  }
}

.bar-fill {
  animation: barfill 0.6s var(--ease) forwards;
  animation-delay: var(--delay, 0ms);
}

@keyframes barfill {
  to {
    transform: translateY(-50%) scaleX(var(--w));
  }
}

@media (prefers-reduced-motion: reduce) {
  .rise,
  .draw,
  .fade-in,
  .grow,
  .bar-fill,
  .skeleton {
    animation: none !important;
  }
  .draw {
    stroke-dashoffset: 0;
  }
  .grow {
    transform: none;
  }
  .cell-bar {
    transform: translateY(-50%) scaleX(var(--w));
  }
  * {
    transition-duration: 0.01ms !important;
  }
}

@media (max-width: 760px) {
  .shell {
    grid-template-columns: minmax(0, 1fr);
  }
  .rail {
    position: static;
    height: auto;
    flex-direction: row;
    align-items: center;
    overflow-x: auto;
  }
  .rail__nav {
    flex-direction: row;
  }
  .rail__foot {
    border-top: 0;
    border-left: 1px solid var(--line);
  }
  .content {
    padding: 16px 14px 40px;
  }
  .topbar {
    padding: 14px 14px;
  }
}


/* Table percentage movement */

.cell-metric {
  display: inline-flex;
  align-items: center;
  justify-content: flex-end;
  gap: 7px;
  width: 100%;
}

.cell-metric__value {
  min-width: 0;
  /* The value shrinks but its text does not, so without this a number wider
     than its column simply drew on top of the delta beside it — "26,508,792"
     under "↓7%" reads as neither. Columns are resizable, so a too-narrow one
     has to degrade to something that says "widen me" rather than to a figure
     that looks complete and is not. */
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.cell-delta {
  flex: 0 0 auto;
  font-size: 10.5px;
  font-weight: 500;
}

/* The UA's `[hidden] { display: none }` loses to the author `display` above, so
   `trimDeltas` in table.js could set `hidden` without the delta giving its
   space back. Declared here and in 03-game-stats.css, which redefines
   `.cell-delta` and loads later. */
.cell-delta[hidden] {
  display: none;
}

.cell-delta.is-up {
  color: var(--positive);
}

.cell-delta.is-down {
  color: var(--negative);
}


/* Top operators */

.rank-list__row {
  display: flex;
  align-items: center;
  gap: 10px;
  min-height: 31px;
}

.rank-list__position {
  width: 20px;
  flex: 0 0 20px;
  color: var(--ink-faint);
  font-size: 11px;
  text-align: right;
}

.rank-list__main {
  min-width: 0;
  flex: 1;
}

.rank-list__top {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 10px;
  font-size: 12px;
}

.rank-list__label {
  color: var(--ink-soft);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.rank-list__top b {
  color: var(--ink);
  font-weight: 500;
  flex: 0 0 auto;
}

.rank-list__track {
  height: 4px;
  margin-top: 4px;
  border-radius: 999px;
  background: var(--surface-sunk);
  overflow: hidden;
}

.rank-list__track i {
  display: block;
  width: var(--w);
  height: 100%;
  border-radius: inherit;
  background: var(--accent);
}
