/* ===========================================================================
   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%;
}

/* ===========================================================================
   Scrollbars, and where a sideways swipe stops

   Two problems in one place, because both are properties of every scroll
   container rather than of any one component. There are a dozen of them across
   four stylesheets — tables, the rail, the date popover, the live feed, the
   market list, the analytics wraps — and doing this per class both missed
   several and would miss the next one added.

   **The bars.** Left to the platform, a panel scrolling inside the page gets
   the OS scrollbar in OS colours: on the dark theme a bright grey slab against
   the ink, the loudest thing on a page whose whole job is the numbers. Tokens
   instead, so they follow the theme, over a transparent track so nothing is
   drawn until there is something to drag.

   **The swipe.** A trackpad flick sideways in a table already at its last
   column chained to the document, where the browser reads it as the back
   gesture — so reading the right-hand end of a table navigated off the page.
   `contain` stops the chain at the container it started in.
   `overscroll-behavior` only applies to scroll containers, so on everything
   else this declaration does nothing.

   `html`/`body` used to be handed back to `auto` here, on the reasoning that
   the fix should be "the table swallows it" rather than "the app disables
   it", so a back-swipe still worked where nothing else was scrolling. That
   did not survive a laptop. A two-finger flick only lands inside `.table-wrap`
   if the pointer happens to be over the table; over the stat band, the header,
   a chart or the gutter either side — most of the page — it went straight to
   the document and navigated away, mid-read, with unsaved filter state. The
   gesture is indistinguishable from the one that scrolls a table, so "where
   nothing else is scrolling" turned out not to be a line a person can see.

   So the document refuses horizontal overscroll outright. This is a dashboard
   people scroll sideways through all day, and losing the page is a far worse
   failure than not having the gesture; back is still a keystroke, a button and
   a swipe on any page that is not this one. `-x` only, so vertical rubber-band
   is untouched, and the `*` rule above still keeps one scroller's overscroll
   out of another's.

   `*` throughout, which carries no specificity of its own, so any component
   that wants something else wins without `!important` or a load order —
   `.rail__nav` hides its own bar on mobile and still does.
   ======================================================================== */
* {
  scrollbar-width: thin;
  scrollbar-color: var(--line-strong) transparent;
  overscroll-behavior-x: contain;
}

html,
body {
  overscroll-behavior-x: none;
}

/* WebKit ignores the two standard properties above and needs its own parts. */
*::-webkit-scrollbar {
  width: 10px;
  height: 10px;
}

*::-webkit-scrollbar-track,
/* Where a horizontal and a vertical bar meet. Left alone it is an opaque
   square in the platform's grey — the one corner of a panel nothing else
   paints. */
*::-webkit-scrollbar-corner {
  background: transparent;
}

*::-webkit-scrollbar-thumb {
  background: var(--line-strong);
  border-radius: 6px;
  /* The inset that keeps the thumb off the content beside it. A transparent
     border plus `background-clip`, because a margin means nothing on a
     scrollbar part. */
  border: 2px solid transparent;
  background-clip: content-box;
}

*::-webkit-scrollbar-thumb:hover {
  background: var(--ink-faint);
  background-clip: content-box;
}

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;
}

/* Mobile only. The drawer it opens is defined in 01-launch.css inside the
   same media query, so above the breakpoint neither exists. */
.rail__menu,
.rail__scrim {
  display: none;
}

/* A section header is a button now, so it needs the affordances of one while
   still reading as the quiet divider it was.

   The rule runs the full width of the rail, which means escaping
   `.rail__nav`'s 12px padding with a negative margin and paying it back as
   padding — so the label still lines up with the page links above and below it
   (both sit 22px in) while the line itself reaches both edges. An inset rule
   read as a boxed-in heading rather than as the division between two sections.

   No rule on the first one: `.apps` already draws a border along its bottom,
   and the two sat a few pixels apart looking like a mistake. The top nav's own
   edge is the divider before the first section. */
.rail__section {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
  width: calc(100% + 24px);
  margin: 10px -12px 4px;
  padding: 10px 22px 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;
}

/* Only when it really is first: a group without a `key` renders no header, so
   if one leads the rail there is content between the top nav and this heading
   and it needs its own line after all. */
.rail__nav > .rail__section:first-child {
  margin-top: 0;
  border-top: 0;
}

.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);
}

/* Only where a pointer can actually leave again. A tap leaves `:hover` stuck
   on the last cell touched, and this rule outranks the range styling below it
   — so on a phone the day you just picked lost its end cap and read as an
   ordinary day inside the range, which looked like the pick had not taken.
   Days already in the range are excluded for the same reason on a desktop:
   hovering what you just chose should not un-choose it. */
@media (hover: hover) {
  .date-picker__day:hover:not(:disabled):not([data-in-range]) {
    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);
  }
}

/* The names behind a count. Small, soft and allowed to wrap — three game
   titles is a sentence, not a label, and truncating it would defeat the point
   of naming them. */
.stat__note {
  margin-top: 4px;
  font-size: 12px;
  line-height: 1.35;
  color: var(--ink-faint);
}

/* My Creative Pot composes its panels rather than declaring them, and with the
   by-game table gone the two that remain are dense tables sitting nose to
   tail. A little more than the shared 20px gap between them. */
.pot-section {
  margin-block: 6px;
}

.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);
}

/* ---------------------------------------------------------------------------
   The total row

   Sticky directly under the header, so the figure the page is actually about
   stays on screen while the rows scroll under it. `top` is the header row's
   own height — the two are coupled, which is why both live here — and the
   z-index sits one below the header so a scrolled total passes behind it
   rather than through it.
   ------------------------------------------------------------------------ */
table.data tbody.is-total td {
  position: sticky;
  top: 38px;
  z-index: 2;
  background: var(--accent-soft);
  color: var(--ink);
  font-weight: 650;
  /* Heavier than a row rule and in the accent, so the eye stops here on the
     way down rather than reading the sum as the first game. */
  border-bottom: 2px solid var(--accent);
}

/* The label cell. Sticky on both axes at once — under the header and against
   the left edge — and it has to restate the background, because the frozen
   rule below sets `--surface` on every frozen cell. */
table.data tbody.is-total td.is-frozen {
  z-index: 3;
  background: var(--accent-soft);
  color: var(--accent);
  font-weight: 650;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  font-size: 11px;
}

/* Not a record. Nothing here responds to a hover, because there is nothing
   underneath it to open or select. */
table.data tbody.is-total tr:hover td {
  background: var(--accent-soft);
}

/* 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);
}

/* A table showing fewer columns than it has says so on the button, in the
   accent rather than as an error — it is a setting someone chose, not a
   fault. Without it the state is invisible: the menu that holds it is behind
   this button, and a trimmed table is indistinguishable from a short one. */
.btn.is-trimmed {
  border-color: var(--accent);
  color: var(--accent);
}

.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);
}

/* Flat. Neither good nor bad, so neither green nor red. */
.cell-delta.is-flat {
  color: var(--ink-faint);
}


/* 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);
}
