/* Syncera — the design system.
 *
 * Every colour here came out of dataviz/scripts/validate_palette.js and the
 * WCAG contrast check, not out of anyone's eye. Do not adjust a hex value
 * without re-running it: two contrast bugs in the first mockups were caught
 * this way, and the three-hue ceiling is computed rather than stylistic.
 *
 * The two themes are designed independently. Dark is not an inversion of
 * light, so both token blocks are written out in full.
 */

/* ---- tokens ------------------------------------------------------------ */

:root {
  --bg: #F7F8FA;
  --p1: #FFFFFF;
  --p2: #F1F3F6;
  --line: #E2E6EC;
  --ink: #10151C;
  --ink2: #59636F;
  --ink3: #67717E;

  /* Sport identity. The one genuine categorical dimension in the data. */
  --foot: #12905E;
  --mach: #2A6CC4;
  --str: #8F5410;
  --oth: #6E7A88;
  /* Marks need 3:1, text needs 4.5:1 — so text gets its own values. */
  --footT: #0B7048;
  --machT: #2A6CC4;
  --strT: #8F5410;

  --empty: #E7EAEF;
  --sprd: #DDE2E9;
  --ghost: #C3CBD5;

  --z1: #2060C8;
  --z2: #0E9CB8;
  --z3: #358F30;
  --z4: #D2541A;
  --z5: #96122F;
  --hr: #D2541A;

  /* Provenance: an ordinal confidence ramp, reserved, never a series. */
  --prov: #3E4A58;
  --provInk: #FFFFFF;
  --crit: #C02A30;
  --critT: #B3252B;

  color-scheme: light;
}

@media (prefers-color-scheme: dark) {
  :root {
    --bg: #0C0F14;
    --p1: #151A22;
    --p2: #1C222C;
    --line: #252D39;
    --ink: #EDF1F6;
    --ink2: #93A0B0;
    --ink3: #7C8794;

    --foot: #1C9C6E;
    --mach: #4285D8;
    --str: #C98420;
    --oth: #6B7A8C;
    --footT: #2FB783;
    --machT: #5AA0E8;
    --strT: #D99A3C;

    --empty: #1B222C;
    --sprd: #232B36;
    --ghost: #3A4A57;

    --z1: #0A84FF;
    --z2: #3FC9DE;
    --z3: #34C759;
    --z4: #F59300;
    --z5: #FF3B5C;
    --hr: #F59300;

    --prov: #B9CBD9;
    --provInk: #0C0F14;
    --crit: #E5484D;
    --critT: #FF8A8A;

    color-scheme: dark;
  }
}

/* The explicit override has to win in both directions, so each theme is
 * declared again against the attribute rather than only as a media default. */
:root[data-theme='light'] {
  --bg: #F7F8FA; --p1: #FFFFFF; --p2: #F1F3F6; --line: #E2E6EC;
  --ink: #10151C; --ink2: #59636F; --ink3: #67717E;
  --foot: #12905E; --mach: #2A6CC4; --str: #8F5410; --oth: #6E7A88;
  --footT: #0B7048; --machT: #2A6CC4; --strT: #8F5410;
  --empty: #E7EAEF; --sprd: #DDE2E9; --ghost: #C3CBD5;
  --z1: #2060C8; --z2: #0E9CB8; --z3: #358F30; --z4: #D2541A; --z5: #96122F;
  --hr: #D2541A;
  --prov: #3E4A58; --provInk: #FFFFFF; --crit: #C02A30; --critT: #B3252B;
  color-scheme: light;
}

:root[data-theme='dark'] {
  --bg: #0C0F14; --p1: #151A22; --p2: #1C222C; --line: #252D39;
  --ink: #EDF1F6; --ink2: #93A0B0; --ink3: #7C8794;
  --foot: #1C9C6E; --mach: #4285D8; --str: #C98420; --oth: #6B7A8C;
  --footT: #2FB783; --machT: #5AA0E8; --strT: #D99A3C;
  --empty: #1B222C; --sprd: #232B36; --ghost: #3A4A57;
  --z1: #0A84FF; --z2: #3FC9DE; --z3: #34C759; --z4: #F59300; --z5: #FF3B5C;
  --hr: #F59300;
  --prov: #B9CBD9; --provInk: #0C0F14; --crit: #E5484D; --critT: #FF8A8A;
  color-scheme: dark;
}

/* ---- motion ------------------------------------------------------------
 * Four tokens, declared once. Tuning the feel means changing these numbers,
 * not hunting literals through the stylesheet.
 *
 * The rule: chrome never animates on its own; data always does.
 */
:root {
  --draw: 1.7s;   /* a route or trace drawing itself */
  --rise: .52s;   /* one tile arriving in the opening sequence */
  --step: .07s;   /* the gap between consecutive tiles */
  --grow: .3s;    /* bars re-growing when the range changes */
  --unfold: .18s; /* the provenance panel opening — see the note below */
}

/* ---- base -------------------------------------------------------------- */

* { box-sizing: border-box; margin: 0; padding: 0 }

body {
  background: var(--bg);
  color: var(--ink);
  font-family: Archivo, system-ui, -apple-system, sans-serif;
  line-height: 1.45;
  padding: 20px 24px 60px;
  -webkit-font-smoothing: antialiased;
}

.page { max-width: 1180px; margin: 0 auto }

/* ---- type -------------------------------------------------------------- */

.lbl {
  font-size: 9.5px; letter-spacing: .16em; text-transform: uppercase;
  color: var(--ink2); font-weight: 800;
}
.dim { color: var(--ink2) }
.tnum { font-variant-numeric: tabular-nums }
.big {
  font-weight: 800; font-variation-settings: 'wdth' 120;
  letter-spacing: -.03em; line-height: .88;
}
.unit {
  font-variation-settings: 'wdth' 100; font-weight: 700; font-size: .3em;
  letter-spacing: .07em; text-transform: uppercase; color: var(--ink2);
  margin-left: .28em;
}
.brand {
  font-weight: 800; font-variation-settings: 'wdth' 116;
  letter-spacing: .03em; text-transform: uppercase; font-size: 17px;
}
.rowhead {
  font-weight: 800; font-variation-settings: 'wdth' 110; font-size: 13px;
  letter-spacing: .02em; text-transform: uppercase;
}

.axis { display: flex; justify-content: space-between }
.axis span {
  font-size: 9.5px; color: var(--ink3); letter-spacing: .09em; font-weight: 700;
}

.tile {
  background: var(--p1); border-radius: 13px; padding: 17px 18px;
  border: 1px solid var(--line);
}

/* ---- masthead ---------------------------------------------------------- */

.mast { display: flex; align-items: center; gap: 18px; flex-wrap: wrap }
.navlinks { display: flex; gap: 14px }
.navlinks a {
  font: 800 11px Archivo; letter-spacing: .1em; text-transform: uppercase;
  color: var(--ink2); text-decoration: none; padding: 4px 0;
  border-bottom: 2px solid transparent; cursor: pointer;
}
.navlinks a.on { color: var(--ink); border-bottom-color: var(--foot) }
.navlinks a:focus-visible { outline: 2px solid var(--foot); outline-offset: 3px }

.search {
  flex: 1; display: flex; align-items: center; gap: 8px;
  background: var(--p2); border: 1px solid var(--line); border-radius: 9px;
  padding: 8px 12px; max-width: 420px; min-width: 200px;
}
.search input {
  flex: 1; background: none; border: 0; color: var(--ink);
  font: 600 12.5px Archivo; outline: none;
}
.search input::placeholder { color: var(--ink3) }
.search:focus-within { border-color: var(--foot) }

/* ---- controls ---------------------------------------------------------- */

.pills {
  display: flex; gap: 4px; background: var(--bg); padding: 3px;
  border-radius: 9px; border: 1px solid var(--line); flex-wrap: wrap;
}
.pills button {
  font: 800 10.5px/1 Archivo; letter-spacing: .09em; text-transform: uppercase;
  color: var(--ink2); background: none; border: 0; padding: 7px 11px;
  border-radius: 6px; cursor: pointer;
}
.pills button[aria-pressed='true'] {
  background: var(--p2); color: var(--ink); box-shadow: inset 0 0 0 1px var(--line);
}
.pills button:focus-visible { outline: 2px solid var(--foot); outline-offset: 2px }

.custom {
  display: flex; gap: 8px; align-items: center; margin-top: 10px;
  flex-wrap: wrap; font-size: 11.5px;
}
.custom input {
  background: var(--p2); border: 1px solid var(--line); border-radius: 7px;
  color: var(--ink); font: 600 11.5px Archivo; padding: 6px 8px;
}
.custom input:focus-visible { outline: 2px solid var(--foot); outline-offset: 1px }

/* ---- charts ------------------------------------------------------------ */

/* The spread. A 24-bucket histogram of every activity in that sport, with the
 * current one lit. The signature element — see §5. */
.spread { display: flex; align-items: flex-end; gap: 1px; height: 22px }
.spread i {
  flex: 1; min-height: 1px; background: var(--sprd); border-radius: .5px;
  position: relative;
}
.spread i.on { background: currentColor }
.spread i.ghost { background: var(--ghost) }
.spread.lg { height: 44px }

/* A one-pixel bucket is still a thing you should be able to point at, so each
 * bar carries a full-height invisible hit area over it. */
.spread i::after {
  content: ''; position: absolute; left: -1px; right: -1px; bottom: 0;
  height: 44px; max-height: 300%;
}
.spread:hover i { background: var(--sprd) }
.spread i:hover { background: var(--ink3) }
.spread i.on, .spread:hover i.on, .spread i.on:hover { background: currentColor }
.spread i.ghost:hover { background: var(--ghost) }

/* The range summary — the four figures for the selected window. Deliberately
 * a typographic strip rather than four bordered cards: uniform stat tiles four
 * across, one colour per metric, is the generic dashboard this design exists
 * to beat (§8). The numbers are the same; the packaging is not. */
.summary {
  display: grid; grid-template-columns: repeat(4, 1fr); gap: 0;
  margin-top: 14px; border-top: 1px solid var(--line); padding-top: 13px;
}
.summary > div { padding: 0 16px; border-left: 1px solid var(--line) }
.summary > div:first-child { padding-left: 0; border-left: 0 }
.summary .n { font-size: 27px; margin-top: 5px }
.summary .sub { font-size: 10.5px; margin-top: 5px; color: var(--ink2) }
.trend { font-weight: 800; font-size: 10.5px; letter-spacing: .02em }
.trend.up { color: var(--footT) }
.trend.down { color: var(--critT) }
.trend.steady { color: var(--ink3) }

@media (max-width: 760px) {
  .summary { grid-template-columns: 1fr 1fr; row-gap: 14px }
  .summary > div:nth-child(3) { padding-left: 0; border-left: 0 }
}

.chart { display: flex; align-items: flex-end; gap: 1px; overflow: hidden }
.chart .b {
  flex: 1; display: flex; flex-direction: column; justify-content: flex-end;
  min-height: 1px; border-radius: 1px 1px 0 0; overflow: hidden;
  transition: height var(--grow) cubic-bezier(.2, .7, .3, 1);
}
.chart .b i { display: block }

/* Zone bands. Every band carries its number: identity is never carried by hue
 * alone, because green-orange collapses under red-green colour blindness. */
.zb { display: flex; height: 22px; border-radius: 6px; overflow: hidden; gap: 2px }
.zb i {
  display: flex; align-items: center; justify-content: center;
  font: 800 9.5px Archivo; letter-spacing: .03em;
}

.lv {
  font-size: 9px; letter-spacing: .1em; text-transform: uppercase;
  font-weight: 800; padding: 2px 6px; border-radius: 4px;
  background: var(--prov); color: var(--provInk);
}

/* ---- rows -------------------------------------------------------------- */

.row {
  display: grid;
  grid-template-columns: 8px 126px 1fr 128px 164px 100px;
  gap: 14px; align-items: center; padding: 13px 0;
  border-top: 1px solid var(--line);
  position: relative;
}
.cap { width: 4px; height: 32px; border-radius: 99px }

/* The whole row opens the activity.
 *
 * Only the sport is a real anchor — the numbers are buttons that open the
 * provenance panel, and nesting those inside an anchor would make them
 * unreachable by keyboard and invalid besides. So the anchor is stretched over
 * the entire row with a pseudo-element, and the few things that must stay
 * independently clickable are lifted above it. Everything else — the figures,
 * the spread, the sources — is click-through and opens the activity.
 */
.rowlink { color: inherit; text-decoration: none }
/* Scoped to `.row` deliberately. An `inset: 0` overlay attaches to the nearest
 * *positioned* ancestor, so outside a `.row` it escapes and covers the whole
 * panel — in a table that stacked one full-panel overlay per row and the last
 * one swallowed every click, opening the wrong activity. Table rows navigate
 * through a handler instead. */
.row .rowlink::after { content: ''; position: absolute; inset: 0; z-index: 1 }
.rowlink:hover { color: var(--ink) }
.rowlink:focus-visible { outline: 2px solid var(--foot); outline-offset: 3px }
.row:hover { background: var(--p1) }
.row:hover .rowhead { color: var(--ink) }
/* Above the stretched anchor, so they keep their own behaviour. */
.row .prov-btn, .row .unfold, .row .spread { position: relative; z-index: 2 }

/* ---- provenance unfold -------------------------------------------------
 * A number is quietly touchable and opens in place into what measured it.
 *
 * NOTE — this is an explicit, agreed exception to the motion rule that
 * "tiles, panels and text never slide or fade in response to interaction".
 * Richard chose the animated unfold over an instant one so it reads as the
 * number opening rather than a panel appearing. Recorded in the spec rather
 * than slipped in.
 */
.prov-btn {
  background: none; border: 0; padding: 0; margin: 0; font: inherit;
  color: inherit; cursor: pointer; text-align: left;
  border-bottom: 1px dotted var(--ink3);
}
.prov-btn:focus-visible { outline: 2px solid var(--foot); outline-offset: 3px }

.unfold {
  display: grid; grid-template-rows: 0fr;
  transition: grid-template-rows var(--unfold) cubic-bezier(.2, .7, .3, 1);
}
.unfold[data-open='true'] { grid-template-rows: 1fr }
.unfold > div { overflow: hidden }

.prov-panel {
  background: var(--p2); border: 1px solid var(--line); border-radius: 10px;
  padding: 13px 14px; margin-top: 10px; font-size: 11.5px;
}
.prov-panel .strike { text-decoration: line-through; color: var(--critT) }

/* ---- opening sequence -------------------------------------------------- */

.rise { opacity: 0; transform: translateY(9px) }
.play .rise { animation: rise var(--rise) cubic-bezier(.2, .7, .3, 1) forwards }
@keyframes rise { to { opacity: 1; transform: none } }

/* The opening sequence runs once, on first open. Changing the range re-renders
 * the same screen, and replaying a staggered fade every time you press a pill
 * would be chrome animating in response to interaction — which the motion rule
 * forbids, and which would also get old fast. */
.noseq .rise { animation: none !important; opacity: 1; transform: none }

/* dasharray and dashoffset are set from each path's measured length in JS:
 * never via pathLength, which repeats the dash and draws the line with gaps. */
.play .drawline { animation: draw var(--draw) cubic-bezier(.33, .5, .25, 1) forwards }
.play .drawline.slow { animation-duration: calc(var(--draw) * 1.18) }
@keyframes draw { to { stroke-dashoffset: 0 } }

.fadein { opacity: 0 }
.play .fadein { animation: fade calc(var(--rise) * 1.3) ease calc(var(--draw) * .5) forwards }
@keyframes fade { to { opacity: 1 } }

@media (prefers-reduced-motion: reduce) {
  .rise, .play .rise { animation: none !important; opacity: 1; transform: none }
  .drawline, .play .drawline { animation: none !important; stroke-dashoffset: 0 !important }
  .fadein, .play .fadein { animation: none !important; opacity: 1 }
  .chart .b { transition: none }
  .unfold { transition: none }
}

/* ---- activity detail --------------------------------------------------- */

.back {
  margin-left: auto; font: 800 11px Archivo; letter-spacing: .1em;
  text-transform: uppercase; color: var(--ink2); text-decoration: none;
}
.back:hover { color: var(--ink) }
.back:focus-visible { outline: 2px solid var(--foot); outline-offset: 3px }

.hero { margin-top: 16px }
.hero-map { height: 420px; padding: 0 12px 12px; display: flex; flex-direction: column }
.map-holder { width: 100%; height: 100%; border-radius: 10px; overflow: hidden }
.maprow {
  display: flex; align-items: center; gap: 10px; padding: 7px 4px 0; flex: none;
}
/* MapLibre's own chrome, brought into the palette. */
.maplibregl-ctrl-attrib {
  font: 500 10px Archivo !important;
  background: color-mix(in srgb, var(--p1) 88%, transparent) !important;
  color: var(--ink3) !important;
}
.maplibregl-ctrl-attrib a { color: var(--ink2) }
.maplibregl-ctrl-group {
  background: var(--p1) !important; border: 1px solid var(--line);
  box-shadow: none !important;
}
.maplibregl-ctrl-group button { background: transparent }
.hero-hr { padding: 0 0 4px }
.hero-spread { padding: 4px 20px 20px }

.statrow {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(120px, 1fr));
  gap: 18px 8px;
}

.prov-table { margin-top: 12px }
.prov-row {
  display: grid; grid-template-columns: 160px 1fr; gap: 18px;
  padding: 13px 0; border-top: 1px solid var(--line); align-items: start;
}
/* A genuine disagreement is the one thing on this page worth interrupting for. */
.prov-row.contested { border-left: 2px solid var(--crit); padding-left: 12px }
.contested-tag {
  font-size: 9px; letter-spacing: .1em; text-transform: uppercase; font-weight: 800;
  color: var(--critT); border: 1px solid var(--crit); border-radius: 4px; padding: 2px 5px;
}

@media (max-width: 640px) {
  .hero-map { height: 260px }
  .prov-row { grid-template-columns: 1fr; gap: 8px }
}

/* ---- browse ------------------------------------------------------------ */

.browse { display: grid; grid-template-columns: 232px 1fr; gap: 12px; margin-top: 10px }
.browse-main { min-width: 0 }

.facet {
  display: flex; align-items: center; gap: 8px; width: 100%;
  background: none; border: 0; border-radius: 7px; padding: 6px 8px;
  color: var(--ink2); font: 600 12px Archivo; cursor: pointer; text-align: left;
}
.facet i { width: 8px; height: 8px; border-radius: 2px; flex: none }
.facet span { flex: 1 }
.facet b { color: var(--ink2); font-variant-numeric: tabular-nums }
.facet:hover { background: var(--p2); color: var(--ink) }
.facet.on { background: var(--p2); color: var(--ink) }
.facet.on b { color: var(--ink) }
.facet:focus-visible { outline: 2px solid var(--foot); outline-offset: 1px }

.cal-head { display: flex; justify-content: space-between; align-items: center; gap: 12px; flex-wrap: wrap }
.step {
  background: var(--p2); border: 1px solid var(--line); border-radius: 7px;
  color: var(--ink); font: 800 13px Archivo; width: 28px; height: 28px; cursor: pointer;
}
.step:hover { border-color: var(--foot) }
.step:focus-visible { outline: 2px solid var(--foot); outline-offset: 2px }

.cal-dow {
  display: grid; grid-template-columns: repeat(7, 1fr); gap: 6px; margin: 14px 0 6px;
}
.cal-dow span {
  font-size: 9.5px; letter-spacing: .12em; text-transform: uppercase;
  color: var(--ink3); font-weight: 800;
}
.cal { display: grid; grid-template-columns: repeat(7, 1fr); gap: 6px }
.cell {
  background: var(--p2); border: 1px solid var(--line); border-radius: 9px;
  padding: 6px; min-height: 96px; display: flex; flex-direction: column; gap: 3px;
}
.cell.outside { opacity: .38 }
.cell.quiet { background: none }
.cell-h { display: flex; justify-content: space-between; font-size: 10.5px; font-weight: 700 }

.chip {
  display: flex; justify-content: space-between; gap: 5px; align-items: baseline;
  background: var(--p1); border-left: 3px solid var(--c, var(--oth)); border-radius: 4px;
  padding: 3px 5px; font-size: 10px; color: var(--ink); text-decoration: none;
  overflow: hidden; white-space: nowrap; min-width: 0;
}
.chip span { overflow: hidden; text-overflow: ellipsis }
.chip b { font-variant-numeric: tabular-nums; flex: none }
.chip:hover { background: var(--bg) }
.chip:focus-visible { outline: 2px solid var(--foot); outline-offset: 1px }

.more {
  background: none; border: 0; color: var(--ink2); font: 800 9.5px Archivo;
  letter-spacing: .08em; text-transform: uppercase; cursor: pointer;
  padding: 3px 4px; text-align: left; border-radius: 4px;
}
.more:hover { color: var(--ink); background: var(--p2) }
.more:focus-visible { outline: 2px solid var(--foot); outline-offset: 1px }
.quiet-more { opacity: 0 }
.cell:hover .quiet-more, .quiet-more:focus-visible { opacity: 1 }

/* the when strip */
.strip {
  position: relative; height: 46px; margin-top: 10px;
  background: var(--p2); border-radius: 8px; overflow: hidden;
}
.block {
  position: absolute; top: 6px; bottom: 6px; border-radius: 4px;
  display: block; text-decoration: none;
}
.block:hover { outline: 2px solid var(--ink); outline-offset: 1px }
.block:focus-visible { outline: 2px solid var(--foot); outline-offset: 2px }
.strip-axis { position: relative; height: 14px; margin-top: 4px }
.strip-axis span {
  position: absolute; transform: translateX(-50%); font-size: 9.5px;
  color: var(--ink3); font-weight: 700; white-space: nowrap;
}

/* the table */
.tablewrap { overflow-x: auto }
.tbl { width: 100%; border-collapse: collapse; font-size: 12px }
.tbl th { padding: 0 0 8px }
.tbl th button {
  background: none; border: 0; color: var(--ink2); cursor: pointer;
  font: 800 9.5px Archivo; letter-spacing: .12em; text-transform: uppercase;
  padding: 2px 4px; border-radius: 4px;
}
.tbl th button:hover { color: var(--ink) }
.tbl th button:focus-visible { outline: 2px solid var(--foot); outline-offset: 1px }
.tbl td { padding: 9px 6px; border-top: 1px solid var(--line); white-space: nowrap }
.tbl tbody tr { cursor: pointer }
.tbl tbody tr:hover td { background: var(--p2) }
.dot { display: inline-block; width: 8px; height: 8px; border-radius: 2px; margin-right: 7px }

/* ---- browse on a small screen ------------------------------------------
 *
 * Measured on a phone, where three failures showed up that no desktop width
 * reveals: the filter rail pushed the calendar most of a screen down, the
 * seven-day grid could not shrink below its chips' text and scrolled the whole
 * page sideways, and the table ran its last four columns off the edge.
 */

/* The rail becomes one disclosure. `<details>` gives the whole behaviour with
 * no script and the right semantics; on a wide screen the summary is hidden
 * and JS renders it open, so the desktop rail is unchanged. */
.rail > summary { display: none }

@media (max-width: 900px) {
  .browse { grid-template-columns: 1fr }

  .rail > summary {
    display: flex; align-items: center; gap: 8px; cursor: pointer;
    list-style: none; padding: 11px 14px; border-radius: 10px;
    background: var(--p1); border: 1px solid var(--line);
    font: 800 10.5px Archivo; letter-spacing: .12em; text-transform: uppercase;
    color: var(--ink);
  }
  .rail > summary::-webkit-details-marker { display: none }
  .rail > summary::after { content: '▾'; margin-left: auto; font-size: 12px; color: var(--ink2) }
  .rail[open] > summary::after { content: '▴' }
  .rail > summary:focus-visible { outline: 2px solid var(--foot); outline-offset: 2px }
  .rail .count { color: var(--ink2); letter-spacing: .04em }
  .rail > .tile:first-of-type { margin-top: 12px }

  /* The grid keeps all seven days — a month you cannot see whole is not a
   * calendar. The chips lose their text and become ticks in the sport colour;
   * the day page behind each cell carries the detail. */
  .cell { min-height: 58px; padding: 4px 3px; gap: 2px }
  .cell-h { font-size: 10px; padding: 0 2px }
  .cal, .cal-dow { gap: 3px }
  .cal-dow span { font-size: 8.5px; letter-spacing: .04em; text-align: center }
  .chip {
    height: 5px; padding: 0; border-left: 0; border-radius: 3px;
    background: var(--c, var(--oth));
  }
  .chip span, .chip b { display: none }
  .more { font-size: 8.5px; padding: 2px }
  .quiet-more { display: none }
}

/* Below this the columns the table can afford run out before the data does. */
@media (max-width: 700px) {
  .tbl th:nth-child(n+5), .tbl td:nth-child(n+5) { display: none }
  .tbl td { padding: 10px 4px }
  .tablewrap { overflow-x: visible }
}

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

.msg {
  border: 1px solid var(--line); background: var(--p1); border-radius: 13px;
  padding: 22px; font-size: 13px; color: var(--ink2);
}
.msg b { color: var(--ink); font-weight: 700 }
.msg button {
  margin-top: 12px; font: 800 10.5px Archivo; letter-spacing: .09em;
  text-transform: uppercase; background: var(--p2); color: var(--ink);
  border: 1px solid var(--line); border-radius: 7px; padding: 8px 12px;
  cursor: pointer;
}

.skel { background: var(--p2); border-radius: 6px; animation: pulse 1.4s ease-in-out infinite }
@keyframes pulse { 0%, 100% { opacity: 1 } 50% { opacity: .55 } }
@media (prefers-reduced-motion: reduce) { .skel { animation: none } }

/* The loading state is the page's own shape, not a sentence.
 *
 * It is in the HTML rather than drawn by script, so it is on screen during the
 * request that fetches the history — the moment it exists to cover. Nothing
 * here shows a number: an outline promises a layout, whereas a placeholder
 * figure would be inventing data before any has arrived. */
.boot { animation: fadein .3s ease }
@keyframes fadein { from { opacity: 0 } }
.boot .bar { height: 17px; width: 108px; background: var(--p2); border-radius: 5px }
.boot .tile { display: block }
.boot .line { height: 10px; background: var(--p2); border-radius: 5px; margin-bottom: 9px }
@media (prefers-reduced-motion: reduce) { .boot { animation: none } }

/* ---- layout ------------------------------------------------------------ */

.hero-grid { display: grid; grid-template-columns: 1.55fr 1fr; gap: 12px; margin-top: 12px }
.three-grid { display: grid; grid-template-columns: 1fr 1fr 1.3fr; gap: 12px; margin-top: 12px }

/* The mosaic is specified at 1180px. Below that the unequal tiles stop
 * earning their sizes, so they stack rather than compress. */
@media (max-width: 900px) {
  .hero-grid, .three-grid { grid-template-columns: 1fr }
  .row { grid-template-columns: 8px 1fr 1fr; row-gap: 8px }
  .row .row-rate, .row .row-spread { display: none }
  .row .row-src { text-align: left !important; grid-column: 2 / -1 }
}

@media (max-width: 560px) {
  body { padding: 14px 12px 40px }
  .mast { gap: 10px }
  .search { order: 3; max-width: none; width: 100% }
  .pills { width: 100% }
  .pills button { flex: 1 }
}
