/* ===========================================================================
   Track Board — one stylesheet, mobile-first.
   Every min-width query adds to the small-screen layout; there is no max-width
   query in this file and none should be added.

   The palette is not chosen by eye. The categorical trio and the ordinal ramp
   were run through the data-viz validator against the LIGHTEST card surface
   (#151d2a — the worst case, since the card is a gradient) and every text pair
   was measured. The numbers are in README.md. Green and red mean direction
   only, never decoration, and never alone: an arrow or a word goes with them.
   =========================================================================== */

:root {
  /* The page is dark-only, and the browser has to be TOLD so. Without this the
     native parts it draws itself — the <select> popup, scrollbars, the focus
     ring on form controls — stay light, and a <select> whose options inherit
     this page's near-white ink becomes white text on a white list. Measured on
     Windows/Chrome: the option rows were invisible except the highlighted one. */
  color-scheme: dark;

  /* surfaces — a card is lit from the top, not a flat fill */
  --page:     #090d15;
  --page-2:   #0c111b;
  --card-hi:  #151d2a;
  --card-lo:  #101724;
  --card-2:   #18202d;          /* nested: table heads, chips, tail rows */
  --line:     rgba(255, 255, 255, .085);
  --line-2:   rgba(255, 255, 255, .05);
  --edge:     rgba(255, 255, 255, .055);   /* the 1px lit top edge */
  --grid:     rgba(255, 255, 255, .055);

  /* ink — all three clear 4.5:1 on the lightest card edge and on the page */
  --ink:   #e8edf5;
  --ink-2: #9aa9bd;
  --ink-3: #7d8b9f;

  /* one accent for interactive elements and the primary series */
  --accent:     #4c8dff;
  --accent-2:   #a78bfa;        /* the far end of a gradient, never an identity */
  --accent-dim: rgba(76, 141, 255, .15);
  --on-accent:  #08111f;        /* white on the accent is 3.2:1 — this is 5.9:1 */

  /* direction, never decoration */
  --up:   #3fb950;
  --down: #f85149;
  --warn: #e3b341;

  /* ordinal ramp: funnel stages and the stacked series, in stage order */
  --s-view:  #3a74d4;
  --s-click: #4c8dff;
  --s-lead:  #74a5ff;
  --s-conv:  #a8c6ff;
  --s-test:  #717d8e;

  /* categorical, for the one donut */
  --c-1: #4c8dff;
  --c-2: #cf7a2e;
  --c-3: #d55181;

  --r:      10px;
  --r-sm:   6px;
  --gut:    16px;
  --gap:    14px;
  --rail-w:   68px;
  --rail-btn: 42px;
  --sans: system-ui, -apple-system, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;

  --lift: 0 1px 2px rgba(0, 0, 0, .45), 0 12px 28px -18px rgba(0, 0, 0, .9);
}

* { box-sizing: border-box; }

/* An author `display` beats the UA's [hidden] rule, and several components
   below set one. Without this, `el.hidden = true` does nothing to them. */
[hidden] { display: none !important; }

html { -webkit-text-size-adjust: 100%; }
@media (prefers-reduced-motion: no-preference) { html { scroll-behavior: smooth; } }

/* The sticky header must not land on top of the heading a rail link jumped to. */
.wrap > [id^="sec-"] { scroll-margin-top: 112px; }

body {
  margin: 0;
  background: var(--page);
  color: var(--ink);
  font: 400 15px/1.5 var(--sans);
  overflow-x: hidden;            /* nothing may be wider than the viewport (§6) */
}

h1, h2, h3, p, figure, ul { margin: 0; }
ul { padding: 0; list-style: none; }
button, select { font: inherit; color: inherit; }

svg.ico {
  width: 16px; height: 16px; flex: none;
  fill: none; stroke: currentColor; stroke-width: 1.8;
  stroke-linecap: round; stroke-linejoin: round;
}
.ico--sm { width: 14px; height: 14px; color: var(--ink-3); }

/* --- the Live events card's own controls ------------------------------- */

/* These had no rules at all for a while and held together by accident: .btn is
   inline-flex, so two buttons flowed side by side on their own. A third child
   made the accident visible. */
.demo { display: flex; flex-wrap: wrap; gap: 8px; }
.demo__b { background: var(--card-2); text-decoration: none; color: inherit; }
.demo__b:disabled { opacity: .6; cursor: default; }
.demo__l { white-space: nowrap; }

/* --- the event log page ------------------------------------------------ */

/* NOT `.wrap--log { display: block }`: this block sits above `.wrap` in the
   file, so at equal specificity `.wrap { display: grid }` wins and the log
   card took one column of the twelve — a 90px-wide table that still passed
   every functional check, because it had rows and did not overflow. The card
   spans the grid instead, which does not depend on source order. */
.card--log { display: flex; flex-direction: column; }
.tbl--log td { vertical-align: middle; }
.tbl--log tbody tr:first-child td { padding-top: 11px; }

.more {
  display: flex; flex-direction: column; align-items: center; gap: 6px;
  padding-top: 14px;
}
.more .btn { min-width: 160px; justify-content: center; }
.more .note { padding: 4px 0 0; text-align: center; }

.card__sub b { color: var(--ink); font-weight: 650; font-variant-numeric: tabular-nums; }

.vh {
  position: absolute; width: 1px; height: 1px; overflow: hidden;
  clip-path: inset(50%); white-space: nowrap;
}

.skip {
  position: absolute; left: -9999px; top: 8px; z-index: 100;
  padding: 10px 14px;
  border-radius: var(--r-sm);
  background: var(--accent); color: var(--on-accent);
  font-size: 13px; font-weight: 600; text-decoration: none;
}
.skip:focus { left: 8px; }

:where(button, select, a, [tabindex]):focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
  border-radius: var(--r-sm);
}

/* --- shell + rail ------------------------------------------------------ */

.rail { display: none; }
.main { min-width: 0; }

/* --- header ------------------------------------------------------------ */

.top {
  position: sticky; top: 0; z-index: 30;
  border-bottom: 1px solid var(--line);
  background: linear-gradient(180deg, #121a27, var(--page));
  transition: box-shadow .25s ease, background-color .25s ease;
}

@supports (backdrop-filter: blur(1px)) {
  .is-scrolled .top {
    background: rgba(9, 13, 21, .84);
    backdrop-filter: blur(16px) saturate(150%);
  }
}
.is-scrolled .top { box-shadow: 0 10px 30px rgba(0, 0, 0, .45); }

.top__in {
  max-width: 1360px;
  margin: 0 auto;
  padding: 18px var(--gut) 0;
  display: flex; flex-direction: column; gap: 16px;
}

.brand { display: flex; gap: 11px; align-items: flex-start; }

.brand__mark {
  width: 36px; height: 36px; flex: none;
  display: grid; place-items: center;
  border-radius: var(--r-sm);
  background: linear-gradient(145deg, rgba(76, 141, 255, .26), rgba(167, 139, 250, .16));
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, .12);
  color: #8fb6ff;
}
.brand__mark svg { width: 20px; height: 20px; }

.brand__txt { display: block; min-width: 0; }
.brand__name { display: block; font-size: 19px; font-weight: 650; letter-spacing: -.015em; }

.brand__sub {
  display: block; margin-top: 2px; max-width: 64ch;
  color: var(--ink-2); font-size: 13px; line-height: 1.45;
  transition: opacity .22s ease, max-height .28s ease, margin-top .28s ease;
  max-height: 60px;
}
.is-scrolled .brand__sub { opacity: 0; max-height: 0; margin-top: 0; }
.is-scrolled .top__in:first-child { padding-top: 12px; }
.is-scrolled .top__live { padding-top: 8px; padding-bottom: 10px; }

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

.seg {
  display: flex; padding: 3px;
  border: 1px solid var(--line); border-radius: var(--r-sm);
  background: linear-gradient(180deg, var(--card-hi), var(--card-lo));
}
.seg__b {
  min-height: 34px; padding: 0 13px;
  border: 0; border-radius: 4px; background: none;
  color: var(--ink-2); font-size: 13px; font-weight: 550; cursor: pointer;
  transition: background-color .16s ease, color .16s ease;
}
.seg__b:hover { color: var(--ink); background: var(--line-2); }
.seg__b.is-on {
  background: linear-gradient(180deg, rgba(76, 141, 255, .26), rgba(76, 141, 255, .13));
  color: #a8c6ff;
  box-shadow: inset 0 0 0 1px rgba(76, 141, 255, .3);
}

.sitesel { display: block; }
.sitesel__i {
  min-height: 40px; padding: 0 30px 0 11px;
  border: 1px solid var(--line); border-radius: var(--r-sm);
  background: linear-gradient(180deg, var(--card-hi), var(--card-lo))
              url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%239aa9bd' stroke-width='2' stroke-linecap='round'%3E%3Cpath d='m6 9 6 6 6-6'/%3E%3C/svg%3E") no-repeat right 8px center / 15px;
  font-size: 13px; appearance: none; cursor: pointer;
}
/* Belt and braces: some platforms draw the popup from the OS and honour these
   rather than color-scheme. Both are cheap; only one has to work. */
.sitesel__i option {
  background: var(--card-2);
  color: var(--ink);
}

.btn {
  display: inline-flex; align-items: center; gap: 7px;
  min-height: 40px; padding: 0 14px;          /* touch target (§10) */
  border: 1px solid var(--line); border-radius: var(--r-sm);
  background: linear-gradient(180deg, var(--card-hi), var(--card-lo));
  font-size: 13px; font-weight: 550; cursor: pointer;
  transition: border-color .16s ease, background-color .16s ease, filter .16s ease;
}
.btn:hover { border-color: rgba(255, 255, 255, .2); filter: brightness(1.18); }

.btn--accent {
  border-color: transparent;
  background: linear-gradient(140deg, #5d97ff, var(--accent));
  color: var(--on-accent);
  box-shadow: 0 6px 18px -8px rgba(76, 141, 255, .9), inset 0 1px 0 rgba(255, 255, 255, .28);
}
.btn--ghost { min-height: 32px; padding: 0 10px; }
.btn--icon {
  min-width: 40px; min-height: 40px; padding: 0; justify-content: center;
  border-color: transparent; background: none; color: var(--ink-2);
}
.btn--icon:hover { background: var(--card-2); color: var(--ink); }

.top__live {
  padding-top: 12px; padding-bottom: 14px;
  flex-direction: row; flex-wrap: wrap; gap: 10px; align-items: center;
}

.live {
  display: inline-flex; align-items: center; gap: 7px;
  color: var(--ink-3); font-size: 12px; font-variant-numeric: tabular-nums;
}
.live__dot {
  width: 7px; height: 7px; border-radius: 50%;
  background: var(--up); box-shadow: 0 0 0 3px rgba(63, 185, 80, .16);
}
.live.is-paused .live__dot { background: var(--ink-3); box-shadow: none; }
.live.is-bad .live__dot { background: var(--down); box-shadow: 0 0 0 3px rgba(248, 81, 73, .16); }

@media (prefers-reduced-motion: no-preference) {
  .live:not(.is-paused):not(.is-bad) .live__dot { animation: pulse 2.6s ease-in-out infinite; }
  @keyframes pulse { 50% { box-shadow: 0 0 0 6px rgba(63, 185, 80, 0); } }
}

.err {
  display: inline-flex; align-items: center; gap: 10px;
  padding: 5px 5px 5px 11px;
  border: 1px solid rgba(248, 81, 73, .35); border-radius: var(--r-sm);
  background: rgba(248, 81, 73, .1);
  color: var(--down); font-size: 12px;
}
.err .btn { color: var(--ink); }

/* --- board grid -------------------------------------------------------- */

.wrap {
  max-width: 1360px; margin: 0 auto;
  padding: var(--gut) var(--gut) 40px;
  display: grid; gap: var(--gap);
}

.kpis { display: grid; gap: var(--gap); }

/* A card is a lit surface: a top-to-bottom gradient, a bright hairline on the
   top edge, and a shadow that sets it off the page. Flat fills are what made
   the first pass read as a document rather than as a product. */
.card {
  position: relative; min-width: 0;
  padding: 15px;
  border: 1px solid var(--line); border-radius: var(--r);
  background: linear-gradient(180deg, var(--card-hi), var(--card-lo));
  box-shadow: var(--lift), inset 0 1px 0 var(--edge);
}

.card__head { margin-bottom: 13px; }
.card__head h2 {
  font-size: 13px; font-weight: 600; letter-spacing: .04em; text-transform: uppercase;
  color: var(--ink-2);
}
.card__sub { margin-top: 4px; color: var(--ink-3); font-size: 12px; line-height: 1.45; }
.card__sub em { color: var(--ink-2); font-style: normal; font-weight: 600; }

.card__head--row {
  display: flex; flex-wrap: wrap; gap: 12px;
  justify-content: space-between; align-items: flex-start;
}
.card__head--row > div:first-child { min-width: 0; flex: 1 1 260px; }

.sub {
  margin-bottom: 8px; color: var(--ink-3);
  font-size: 11px; font-weight: 600; letter-spacing: .05em; text-transform: uppercase;
}

/* --- KPI tiles --------------------------------------------------------- */

/* Two columns, as the reference has them: the figure on the left, the
   sparkline holding the right. The spark is coloured by the direction the
   figure is actually going — which is what made the reference's top row read
   at a glance, and what a single muted blue for all four did not. */
/* No `overflow: hidden` here: the end-of-line dot sits half its width past the
   sparkline's own box, and the card's padding is what keeps it inside. */
.kpi {
  display: grid; grid-template-columns: minmax(0, 1fr) 38%;
  gap: 10px; align-items: center;
}
.kpi__l { min-width: 0; }

.kpi__label {
  display: flex; align-items: center; gap: 6px;
  font-size: 12px; font-weight: 600; letter-spacing: .05em; text-transform: uppercase;
  color: var(--ink-3);
}
.kpi__label .ico { width: 13px; height: 13px; }

.kpi__val { margin-top: 7px; }

/* Tabular figures on purpose, against the usual advice for display numbers:
   this one is replaced every fifteen seconds, and proportional digits make a
   live figure twitch sideways on each refresh. */
.kpi__val .num {
  font-size: 30px; font-weight: 640; letter-spacing: -.02em; line-height: 1.05;
  font-variant-numeric: tabular-nums;
}

.kpi__meta {
  margin-top: 8px; min-height: 21px;
  display: flex; flex-wrap: wrap; gap: 8px; align-items: center;
}
.kpi__note { color: var(--ink-3); font-size: 12px; }

.kpi__spark { height: 66px; align-self: center; }
.spark { position: relative; display: block; height: 100%; }
.spark svg { display: block; width: 100%; height: 100%; }
/* `left: 100%` plus a half-size translate puts the CENTRE of the dot on the
   last data point. Anchoring by an edge instead (`right: 1px`) left it 4.5px
   adrift of the line end — measured, not guessed. */
.spark__dot {
  position: absolute; left: 100%; top: 0;
  width: 7px; height: 7px;
  transform: translate(-50%, -50%);
  border-radius: 50%; background: var(--sc);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--sc) 22%, transparent);
}

/* The arrow, not the colour, is what carries the direction (§10). */
.delta {
  display: inline-flex; align-items: center; gap: 3px;
  padding: 2px 7px; border-radius: 100px;
  font-size: 12px; font-weight: 600; font-variant-numeric: tabular-nums;
}
.delta--up   { background: rgba(63, 185, 80, .1);  color: var(--up); }
.delta--down { background: rgba(248, 81, 73, .1);  color: var(--down); }
.delta--flat { background: var(--card-2); color: var(--ink-3); }
.delta--new  { background: var(--accent-dim); color: #8fb6ff; }

/* --- funnel ------------------------------------------------------------ */

/* A real tapered funnel, and still an honest one: each band's TOP edge is the
   linear share of its own stage and its BOTTOM edge the share of the next, so
   the taper is the join between two true widths rather than an invented
   shape. Nothing is read off area. */
.card--funnel { display: flex; flex-direction: column; }

/* One continuous silhouette in the middle column, the labels either side of
   it: four separate shapes with captions between them read as four trapezoids,
   not as a funnel. The SVG spans every row, so the bands and the labels line
   up without a single magic number. */
.funnel {
  flex: 1;
  display: grid;
  grid-template-columns: minmax(62px, 1fr) minmax(96px, 46%) minmax(78px, 1fr);
  grid-template-rows: repeat(4, minmax(58px, 1fr));
  column-gap: 14px;
  align-content: center;
}

.funnel__art { grid-column: 2; grid-row: 1 / -1; min-height: 232px; }
.funnel__art svg { display: block; width: 100%; height: 100%; }
.funnel__art polygon { transition: opacity .16s ease; }
.funnel__art:hover polygon { opacity: .85; }

.fname {
  grid-column: 1; align-self: center; text-align: right;
  color: var(--ink-2); font-size: 12.5px; line-height: 1.3;
}
.fname b {
  display: block; margin-top: 3px;
  color: var(--ink-3); font-size: 10.5px; font-weight: 500; line-height: 1.35;
}

.fval { grid-column: 3; align-self: center; }
.fval__n { display: block; font-size: 19px; font-weight: 650; font-variant-numeric: tabular-nums; }
.fval__p { display: block; color: var(--ink-3); font-size: 10.5px; font-variant-numeric: tabular-nums; }

.fdrop {
  display: inline-flex; align-items: baseline; gap: 3px;
  margin-top: 4px;
  color: var(--ink-3); font-size: 11px; font-variant-numeric: tabular-nums;
}
.fdrop--up { color: var(--warn); }

/* Only when the funnel is answering a new question — a poll that changed a
   number must not replay this. */
@media (prefers-reduced-motion: no-preference) {
  .funnel.is-reveal .funnel__art polygon {
    animation: fgrow .55s cubic-bezier(.22, 1, .36, 1) backwards;
    animation-delay: calc(var(--fi) * .08s);
    transform-origin: 50% 0;
    transform-box: fill-box;
  }
  .funnel.is-reveal .fname,
  .funnel.is-reveal .fval { animation: fade .6s ease backwards; animation-delay: .2s; }
  @keyframes fgrow { from { opacity: 0; transform: scaleY(.05); } }
  @keyframes fade  { from { opacity: 0; } }
}

/* --- plots ------------------------------------------------------------- */

/* Fixed heights so the skeleton and the rendered chart occupy the same box and
   nothing jumps when data lands — tall enough to include the axis band. */
.plot { position: relative; }
.plot--series { height: 240px; }
.plot--donut  { height: 176px; }
.plot canvas { display: block; }

.legend { display: flex; flex-wrap: wrap; gap: 6px 14px; margin-bottom: 11px; }
.legend--col { flex-direction: column; gap: 9px; margin: 0; }

.lg { display: inline-flex; align-items: center; gap: 7px; color: var(--ink-2); font-size: 12px; }
.lg__sw { width: 10px; height: 10px; border-radius: 3px; flex: none; }
.lg__n { color: var(--ink); font-variant-numeric: tabular-nums; }
.lg__pc { color: var(--ink-3); font-variant-numeric: tabular-nums; }

.donut { display: grid; gap: 14px; align-items: center; }

/* Built in HTML rather than on the canvas: it needs the page's own type,
   tabular figures and a swatch per series. */
.tip {
  position: absolute; top: 0; left: 0; z-index: 5;
  min-width: 156px; padding: 9px 11px;
  border: 1px solid var(--line); border-radius: var(--r-sm);
  background: rgba(20, 27, 39, .97);
  box-shadow: 0 14px 38px rgba(0, 0, 0, .55);
  font-size: 12px; font-variant-numeric: tabular-nums;
  pointer-events: none; opacity: 0; will-change: transform;
  transition: opacity .14s ease, transform .1s cubic-bezier(.22, 1, .36, 1);
}
@media (prefers-reduced-motion: reduce) { .tip { transition: opacity .14s ease; } }

.tip__h {
  margin-bottom: 7px; padding-bottom: 6px;
  border-bottom: 1px solid var(--line);
  color: var(--ink-2); font-size: 11.5px;
}
.tip__r, .tip__t { display: flex; align-items: center; gap: 7px; padding: 2px 0; }
.tip__t { margin-top: 5px; padding-top: 6px; border-top: 1px solid var(--line); }
.tip__sw { width: 8px; height: 8px; border-radius: 2px; flex: none; }
.tip__l { color: var(--ink-2); margin-right: auto; }
.tip__v { color: var(--ink); font-weight: 600; }

/* --- tables ------------------------------------------------------------ */

/* A wide table scrolls inside its own box rather than pushing the page (§6). */
.tbl-scroll { overflow-x: auto; overscroll-behavior-x: contain; }

.tbl {
  width: 100%; border-collapse: collapse;
  font-size: 13px; font-variant-numeric: tabular-nums;
}
.tbl th {
  padding: 0 10px 7px;
  border-bottom: 1px solid var(--line);
  color: var(--ink-3);
  font-size: 11px; font-weight: 600; letter-spacing: .04em; text-transform: uppercase;
  text-align: left; white-space: nowrap;
}
.tbl td { padding: 9px 10px; border-bottom: 1px solid var(--line-2); white-space: nowrap; }
.tbl tr:last-child td { border-bottom: 0; }
.tbl th:first-child, .tbl td:first-child { padding-left: 0; }
.tbl th:last-child,  .tbl td:last-child  { padding-right: 0; }
.tbl .r { text-align: right; }
.tbl .mut { color: var(--ink-3); }

/* A row responds to the pointer by changing colour, never by moving. */
.tbl tbody tr { transition: background-color .15s ease; }
.tbl tbody tr:hover { background: var(--card-2); }

.cell { display: inline-flex; align-items: center; gap: 7px; min-width: 0; }

/* A term that carries an explanation in its title. A dotted underline is the
   convention for "there is more here" and, unlike a solid one, does not read
   as a link — nothing on these tables is clickable. Without it the tooltip
   exists and nobody ever learns it is there. */
.hint {
  text-decoration: underline dotted;
  text-decoration-color: var(--ink-3);
  text-underline-offset: 3px;
  cursor: help;
}
.flag {
  width: 18px; height: 13px; flex: none;
  border-radius: 2px; background: var(--card-2); object-fit: cover;
}

/* A track plus a fill: at one event out of ninety-eight a lone 1%-wide rule
   reads as a speck of dirt under the number rather than as the smallest bar. */
.share {
  display: block; width: 62px; max-width: 100%;
  height: 3px; margin-top: 5px; margin-left: auto;
  border-radius: 2px; background: rgba(255, 255, 255, .07); overflow: hidden;
}
.share__f {
  display: block; height: 100%; border-radius: 2px;
  background: linear-gradient(90deg, var(--s-view), var(--s-lead));
  transition: width .55s cubic-bezier(.22, 1, .36, 1);
}
@media (prefers-reduced-motion: reduce) { .share__f { transition: none; } }

/* What the network has NOT settled as approved, named under the count rather
   than folded into it. */
.prs { display: block; margin-top: 2px; font-size: 10.5px; white-space: nowrap; }
.prs .pr + .pr { margin-left: 6px; }
.pr--pend { color: var(--warn); }
.pr--rej  { color: var(--down); }

/* The conversion that could not be credited to an ad. Two lines, because the
   first is a fact and the second is the reason for it, and a table cell is the
   one place a reader will not go looking for a tooltip. */
/* The folded tail is a real button, so it looks like one can be pressed —
   a row that reads as text and behaves as a control is worse than either. */
.fold {
  display: inline-flex; align-items: center; gap: 7px;
  min-height: 32px; padding: 3px 9px 3px 6px;
  border: 1px solid var(--line); border-radius: var(--r-sm);
  background: var(--card-2);
  color: var(--ink-2); font-size: 12.5px; cursor: pointer;
  transition: color .15s ease, border-color .15s ease;
}
.fold:hover { color: var(--ink); border-color: rgba(255, 255, 255, .2); }
.fold:disabled { opacity: .6; cursor: default; }
.fold__i { color: inherit; }

.orph { display: block; min-width: 0; }
.orph b { display: block; font-weight: 600; color: var(--ink-2); }
.orph i {
  display: block; margin-top: 1px;
  color: var(--ink-3); font-size: 11px; font-style: normal;
  white-space: normal;
}
.is-orphan td { vertical-align: top; }

/* The geography table's direction column (§7). */
.gd { display: inline-flex; align-items: center; gap: 2px; font-weight: 600; }
.gd--up   { color: var(--up); }
.gd--down { color: var(--down); }
.gd--flat { color: var(--ink-3); }
.gd--new  { color: #8fb6ff; font-weight: 550; }

.tag {
  display: inline-flex; align-items: center; gap: 5px;
  padding: 2px 8px; border-radius: 100px;
  background: var(--card-2); font-size: 11.5px; font-weight: 550;
}
.tag__dot { width: 6px; height: 6px; border-radius: 50%; flex: none; }

.tbl--tail tbody tr { transition: background-color .5s ease; }
.tbl--tail tbody tr.is-new { background: var(--accent-dim); }

/* --- monetisation / health --------------------------------------------- */

.money { display: grid; gap: 20px; }
.money__col { min-width: 0; }

.health { display: flex; flex-wrap: wrap; gap: 8px; }
.hs {
  display: flex; align-items: center; gap: 8px;
  padding: 8px 12px;
  border: 1px solid var(--line); border-radius: var(--r-sm);
  background: var(--card-2); font-size: 12.5px;
}
.hs__n { font-weight: 640; font-variant-numeric: tabular-nums; }
.hs__l { color: var(--ink-3); }
.hs__dot { width: 7px; height: 7px; border-radius: 50%; flex: none; }

/* --- states ------------------------------------------------------------ */

/* First load only. A refetch holds the previous render instead of flashing a
   skeleton back over it. */
.is-loading .num,
.is-loading .fband__name, .is-loading .fband__v { color: transparent; background: var(--card-2); border-radius: 4px; }
.is-loading .plot { background: var(--card-2); border-radius: var(--r-sm); }

@media (prefers-reduced-motion: no-preference) {
  .is-loading .num, .is-loading .plot { animation: shimmer 1.4s ease-in-out infinite; }
  @keyframes shimmer { 50% { opacity: .55; } }
}

.is-stale { opacity: .55; transition: opacity .2s; }
.note { padding: 10px 0 2px; color: var(--ink-3); font-size: 12.5px; }
.note--dest { padding-top: 12px; font-size: 12px; display: grid; gap: 5px; }
.note__l { display: block; }
.note__l b { color: var(--ink-2); font-weight: 650; }
.note__l .ok  { color: var(--up); }
.note__l .bad { color: var(--warn); }

/* The edge-filter note. Ruled off above rather than boxed: it is a footnote to
   the table, not a competing panel, and a second surface here would read as a
   card inside a card. */
.note--edge {
  margin-top: 14px; padding-top: 12px;
  border-top: 1px solid var(--line);
  display: grid; gap: 4px;
}
.note--edge .note__l { color: var(--ink-2); font-weight: 600; }
.note--edge .note__r { display: block; color: var(--ink-3); font-size: 12px; line-height: 1.5; }
.note--edge em { font-style: normal; color: var(--ink-2); }

/* --- footer ------------------------------------------------------------ */

.foot {
  max-width: 1360px; margin: 0 auto;
  padding: 22px var(--gut) 38px;
  display: flex; flex-wrap: wrap; gap: 8px 20px; justify-content: center;
  border-top: 1px solid var(--line);
}
.foot a {
  display: inline-flex; align-items: center; gap: 7px;
  min-height: 40px; padding: 0 4px;
  color: var(--ink-2); font-size: 13px;
  text-decoration: underline;
  text-decoration-color: rgba(154, 169, 189, .35);
  text-underline-offset: 4px; text-decoration-thickness: 1px;
}
.foot a:hover { color: var(--ink); text-decoration-color: currentColor; }
.foot .ico.solid { fill: currentColor; stroke: none; }

/* --- modal -------------------------------------------------------------- */

/* On phones it is a full-screen sheet, not a floating window (§5). */
.modal { position: fixed; inset: 0; z-index: 50; display: flex; }
.modal__scrim { position: absolute; inset: 0; background: rgba(2, 4, 9, .72); }
.modal__box {
  position: relative; display: flex; flex-direction: column;
  width: 100%; max-height: 100%; overflow: hidden;
  border: 1px solid var(--line);
  background: linear-gradient(180deg, var(--card-hi), var(--card-lo));
}
.modal__head {
  flex: none;
  display: flex; align-items: center; justify-content: space-between; gap: 12px;
  padding: 14px 10px 14px var(--gut);
  border-bottom: 1px solid var(--line);
}
.modal__head h2 { font-size: 16px; font-weight: 640; }
.modal__body {
  padding: 16px var(--gut) 28px; overflow-y: auto;
  display: grid; gap: 14px;
  color: var(--ink-2); font-size: 14px; line-height: 1.62;
}
.modal__body strong { color: var(--ink); font-weight: 620; }
.modal__body em { color: var(--ink); font-style: normal; font-weight: 550; }

/* --- entrance ----------------------------------------------------------- */

/* Once, on the first paint. A refresh never replays it. */
@media (prefers-reduced-motion: no-preference) {
  .wrap > * { animation: rise .5s cubic-bezier(.22, 1, .36, 1) backwards; }
  .wrap > :nth-child(1) { animation-delay: .02s; }
  .wrap > :nth-child(2) { animation-delay: .07s; }
  .wrap > :nth-child(3) { animation-delay: .11s; }
  .wrap > :nth-child(4) { animation-delay: .15s; }
  .wrap > :nth-child(5) { animation-delay: .19s; }
  .wrap > :nth-child(6) { animation-delay: .22s; }
  .wrap > :nth-child(7) { animation-delay: .25s; }
  .wrap > :nth-child(8) { animation-delay: .28s; }
  @keyframes rise { from { opacity: 0; transform: translateY(10px); } }
}

/* =========================================================================
   ≥560px — the KPI figure and its sparkline stop competing for width
   ========================================================================= */
@media (min-width: 560px) {
  .kpi__val .num { font-size: 33px; }
}

/* =========================================================================
   ≥768px — the KPI row becomes 2×2 (§6)
   ========================================================================= */
@media (min-width: 768px) {
  :root { --gut: 24px; --gap: 16px; }

  .top__in { flex-direction: row; align-items: flex-start; justify-content: space-between; gap: 24px; }
  .top__live { align-items: center; }
  .controls { flex: none; }

  .kpis { grid-template-columns: repeat(2, 1fr); }
  .card { padding: 17px; }

  .money { grid-template-columns: 1fr 1fr; gap: 26px; }
  .donut { grid-template-columns: 176px 1fr; gap: 22px; }

  .modal { align-items: center; justify-content: center; padding: 24px; }
  .modal__box { max-width: 620px; border-radius: var(--r); max-height: calc(100vh - 48px); }
}

/* =========================================================================
   ≥1100px — the rail appears, four tiles across, two heavy blocks share a row
   ========================================================================= */
@media (min-width: 1100px) {
  .shell { display: grid; grid-template-columns: var(--rail-w) minmax(0, 1fr); }

  /* Every entry is a real destination on this page — there is nothing here
     that does not go somewhere. */
  .rail {
    position: sticky; top: 0; z-index: 40;
    height: 100vh; padding: 16px 0 20px;
    display: flex; flex-direction: column; align-items: center; gap: 18px;
    border-right: 1px solid var(--line);
    background: linear-gradient(180deg, #121a27, var(--page-2) 40%, var(--page));
  }

  .rail__mark {
    width: 38px; height: 38px; flex: none;
    display: grid; place-items: center;
    border-radius: var(--r-sm);
    background: linear-gradient(145deg, rgba(76, 141, 255, .28), rgba(167, 139, 250, .16));
    box-shadow: inset 0 1px 0 rgba(255, 255, 255, .14);
    color: #8fb6ff;
  }
  .rail__mark svg { width: 21px; height: 21px; }

  .rail__list { display: flex; flex-direction: column; gap: 4px; }

  .rail__b {
    position: relative;
    width: var(--rail-btn); height: var(--rail-btn);
    display: grid; place-items: center;
    border-radius: 9px;
    color: var(--ink-3);
    transition: color .16s ease, background-color .16s ease;
  }
  .rail__b svg { width: 18px; height: 18px; fill: none; stroke: currentColor; stroke-width: 1.7; stroke-linecap: round; stroke-linejoin: round; }
  .rail__b:hover { color: var(--ink); background: rgba(255, 255, 255, .055); }

  .rail__b.is-on { color: #8fb6ff; background: var(--accent-dim); }

  /* The marker is a bar on the rail's OWN left edge, not a moving card.
     Its offset is derived, not eyeballed: the button is centred in the rail's
     content box, which is the rail width less its 1px right border — so the
     rail's edge sits (button − (rail − 1px)) / 2 away. Guessing it at −14px
     put half the 3px bar off-screen at x = −1.5. */
  .rail__b.is-on::before {
    content: ""; position: absolute; top: 50%;
    left: calc((var(--rail-btn) - (var(--rail-w) - 1px)) / 2);
    width: 3px; height: 20px; margin-top: -10px;
    border-radius: 0 3px 3px 0; background: var(--accent);
  }

  /* The label arrives beside the icon rather than as a browser tooltip, so it
     shows on keyboard focus too. */
  .rail__b::after {
    content: attr(data-label);
    position: absolute; left: calc(100% + 10px); top: 50%;
    transform: translateY(-50%) translateX(-4px);
    padding: 5px 9px; border-radius: var(--r-sm);
    border: 1px solid var(--line);
    background: var(--card-2); color: var(--ink);
    font-size: 12px; white-space: nowrap;
    box-shadow: var(--lift);
    opacity: 0; pointer-events: none;
    transition: opacity .14s ease, transform .14s ease;
  }
  .rail__b:hover::after, .rail__b:focus-visible::after { opacity: 1; transform: translateY(-50%) translateX(0); }

  .wrap { grid-template-columns: repeat(12, 1fr); }

  .kpis { grid-column: 1 / -1; grid-template-columns: repeat(4, 1fr); }

  .card--funnel { grid-column: span 5; }
  .card--series { grid-column: span 7; }
  .card--geo    { grid-column: span 7; }
  .card--dev    { grid-column: span 5; }
  .card--tail   { grid-column: 1 / -1; }
  .card--log    { grid-column: 1 / -1; }
  .card--money  { grid-column: span 8; }
  .card--health { grid-column: span 4; }

  .plot--series { height: 280px; }
  .kpi__val .num { font-size: 34px; }

  /* Stretched down a tall column, a row of chips reads as a gap; laid out as a
     label-and-figure list it reads as a report. */
  .card--health .health { flex-direction: column; align-items: stretch; gap: 7px; }
  .card--health .hs { justify-content: space-between; }
  .card--health .hs__n { order: 3; margin-left: auto; font-size: 14px; }
  .card--health .hs__l { color: var(--ink-2); }
}

@media (min-width: 1100px) and (prefers-reduced-motion: reduce) {
  .rail__b, .rail__b::after { transition: none; }
}
