:root {
  color-scheme: dark;
  --surface-1: #16202d;
  --surface-2: #1b2838;
  --page-plane: #0e141c;
  --text-primary: #e9edf1;
  --text-secondary: #8f98a0;
  --text-muted: #6b7480;
  --gridline: rgba(255, 255, 255, 0.08);
  --baseline: rgba(255, 255, 255, 0.2);
  --border: rgba(255, 255, 255, 0.1);
  /* series-1/2/3 are validated as an ordered set (dataviz skill: OKLCH
     lightness band, adjacent-pair CVD ΔE, contrast — re-run that check on
     any hand-tune). Valve red sits in decor-4, not series-3, specifically
     because it's a near-miss on CVD separation from series-2's orange when
     adjacent; violet clears that easily in both directions. */
  --series-1: #1e7fe0; /* aperture blue */
  --series-2: #d9600e; /* aperture orange */
  --series-3: #9b6bff; /* violet */
  --series-1-rgb: 30, 127, 224;
  --series-2-rgb: 217, 96, 14;
  --series-3-rgb: 155, 107, 255;
  --decor-4: #f74843; /* valve red, from valvesoftware.com's own stylesheet — decorative accent only, never used to encode chart data */
  --decor-4-rgb: 247, 72, 67;
  /* Boot-intro particle accent — kept independent of decor-4 so reordering
     the chart palette's rotation doesn't silently recolor the intro. */
  --decor-violet-rgb: 155, 107, 255;
  --success-text: #a4d007;
  --accent: #66c0f4;
  --accent-rgb: 102, 192, 244;
  --accent-strong: #1a9fff;
  --radius: 10px;
  --radius-sm: 6px;
  --font-sans: "Segoe UI", Arial, "Helvetica Neue", sans-serif;
  --font-display: "Oswald", var(--font-sans);
  --font-mono: "Share Tech Mono", "SFMono-Regular", Consolas, monospace;
}

* {
  box-sizing: border-box;
}

body {
  margin: 0;
  font-family: var(--font-sans);
  background: linear-gradient(180deg, #1b2838 0%, #0e141c 480px, #0e141c 100%);
  background-attachment: fixed;
  color: var(--text-primary);
  line-height: 1.5;
}

::selection {
  background: var(--accent-strong);
  color: #0e141c;
}

::-webkit-scrollbar {
  width: 12px;
  height: 12px;
}

::-webkit-scrollbar-track {
  background: var(--page-plane);
}

::-webkit-scrollbar-thumb {
  background: #2a3a4a;
  border-radius: 6px;
  border: 2px solid var(--page-plane);
}

::-webkit-scrollbar-thumb:hover {
  background: #3a5068;
}

a {
  color: var(--accent);
  text-decoration: none;
}

a:hover {
  color: #ffffff;
  text-decoration: underline;
}

/* Motion (progressive enhancement — see js/motion.js) */
.reveal {
  opacity: 0;
  transform: translateY(18px);
  transition: opacity 0.55s ease, transform 0.55s ease;
}

.reveal.is-visible {
  opacity: 1;
  transform: none;
}

/* Nav */
.site-nav {
  position: sticky;
  top: 0;
  z-index: 10;
  display: flex;
  align-items: center;
  gap: 6px;
  padding: 0 24px;
  height: 56px;
  background: #171a21;
  border-bottom: 1px solid #000;
  box-shadow: 0 3px 6px rgba(0, 0, 0, 0.4);
}

.site-nav .brand {
  display: inline-flex;
  align-items: center;
  font-family: var(--font-display);
  font-weight: 700;
  margin-right: 28px;
  color: #ffffff;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  font-size: 1.15rem;
  white-space: nowrap;
}

.site-nav .brand-icon {
  width: 22px;
  height: 22px;
  margin-right: 10px;
  flex-shrink: 0;
}

.site-nav a {
  position: relative;
  text-decoration: none;
  color: #b8b6b4;
  font-weight: 600;
  font-size: 0.92rem;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  padding: 18px 14px;
  transition: color 0.15s ease, background 0.15s ease;
}

/* Pushes Report/Dashboard (and, following them, the "..." menu) to the
   right as one group, leaving the brand alone on the left. Targets the
   first nav link rather than .nav-popup so it works regardless of which
   page's link is .active. */
.site-nav a:first-of-type {
  margin-left: auto;
}

.site-nav a::after {
  content: "";
  position: absolute;
  left: 14px;
  right: 14px;
  bottom: 0;
  height: 2px;
  background: linear-gradient(90deg, var(--series-1), var(--series-2));
  transform: scaleX(0);
  transform-origin: left;
  transition: transform 0.2s ease;
}

.site-nav a:hover {
  color: #ffffff;
  background: rgba(255, 255, 255, 0.04);
  text-decoration: none;
}

.site-nav a:hover::after {
  transform: scaleX(1);
}

.site-nav a.active {
  color: #ffffff;
}

.site-nav a.active::after {
  transform: scaleX(1);
  background: var(--accent);
}

/* Layout */
main {
  max-width: 980px;
  margin: 0 auto;
  padding: 32px 20px 80px;
  counter-reset: finding;
}

/* Hero header — bleeds full viewport width like a showcase banner */
.page-header {
  position: relative;
  width: 100vw;
  left: 50%;
  right: 50%;
  margin-left: -50vw;
  margin-right: -50vw;
  overflow: hidden;
  padding: 64px 24px 52px;
  --hx: 0px;
  --hy: 0px;
  /* js/cursor-fx.js nudges --hx/--hy from pointer position; the three
     radial-gradient anchors drift a few px off their base position for a
     slow parallax feel. --hx/--hy are registered as animatable <length>s
     below so the drift eases instead of snapping to each pointer sample. */
  background:
    radial-gradient(ellipse 900px 480px at calc(12% + var(--hx)) calc(-15% + var(--hy)), rgba(var(--series-1-rgb), 0.35), transparent 60%),
    radial-gradient(ellipse 700px 480px at calc(92% + var(--hx)) calc(5% + var(--hy)), rgba(var(--decor-4-rgb), 0.3), transparent 60%),
    radial-gradient(ellipse 600px 380px at calc(55% + var(--hx)) calc(115% + var(--hy)), rgba(var(--series-2-rgb), 0.2), transparent 60%),
    linear-gradient(160deg, #1e3450 0%, #101823 72%);
  border-bottom: 1px solid var(--border);
  box-shadow: 0 24px 48px -20px rgba(0, 0, 0, 0.6);
  transition: --hx 0.5s ease, --hy 0.5s ease;
}

main > .page-header:first-child {
  margin-top: -32px;
}

.page-header::before {
  content: "";
  position: absolute;
  top: -60%;
  right: -4%;
  width: 360px;
  height: 220%;
  background: linear-gradient(135deg, rgba(255, 255, 255, 0.06), rgba(255, 255, 255, 0) 60%);
  transform: skewX(-12deg);
  pointer-events: none;
  z-index: 0;
}

.page-header-inner {
  position: relative;
  z-index: 1;
  max-width: 980px;
  margin: 0 auto;
}

.kicker {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  margin: 0 0 16px;
  font-family: var(--font-display);
  font-weight: 600;
  font-size: 0.78rem;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--accent);
}

.page-header h1 {
  font-family: var(--font-display);
  font-weight: 700;
  font-size: clamp(2.1rem, 4.5vw, 3.25rem);
  line-height: 1.3;
  letter-spacing: -0.01em;
  margin: 0 0 14px;
  padding-bottom: 0.1em;
  max-width: none;
  color: #ffffff;
  background: linear-gradient(180deg, #ffffff 0%, #cfe9fb 100%);
  -webkit-background-clip: text;
  background-clip: text;
  -webkit-text-fill-color: transparent;
}

.byline {
  color: var(--text-secondary);
  margin: 0 0 18px;
  font-weight: 500;
  font-size: 0.95rem;
}

.summary-paragraph {
  font-size: 1.05rem;
  color: var(--text-secondary);
  max-width: 82ch;
  text-align: justify;
  hyphens: none;
}

.stat {
  color: #ffffff;
  font-weight: 700;
}

/* Stat tiles — asymmetric bento grid */
.stat-grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  grid-template-areas:
    "a a b"
    "c d d";
  grid-auto-rows: minmax(118px, auto);
  gap: 12px;
  margin: 28px 0 44px;
}

.stat-tile:nth-child(1) {
  grid-area: a;
  --tile-accent: var(--series-1);
  --tile-accent-rgb: var(--series-1-rgb);
}

.stat-tile:nth-child(2) {
  grid-area: b;
  --tile-accent: var(--series-2);
  --tile-accent-rgb: var(--series-2-rgb);
}

.stat-tile:nth-child(3) {
  grid-area: c;
  --tile-accent: var(--series-3);
  --tile-accent-rgb: var(--series-3-rgb);
}

.stat-tile:nth-child(4) {
  grid-area: d;
  --tile-accent: var(--decor-4);
  --tile-accent-rgb: var(--decor-4-rgb);
}

@media (max-width: 640px) {
  .stat-grid {
    grid-template-columns: 1fr;
    grid-template-areas: "a" "b" "c" "d";
  }
}

.stat-tile {
  position: relative;
  overflow: hidden;
  display: flex;
  flex-direction: column;
  justify-content: center;
  background: linear-gradient(160deg, var(--surface-2) 0%, var(--surface-1) 100%);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 18px 20px 16px;
  transition: transform 0.18s ease, box-shadow 0.18s ease, border-color 0.18s ease;
}

.stat-tile::before {
  content: "";
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  height: 3px;
  background: var(--tile-accent);
  opacity: 0.9;
}

.stat-tile:hover {
  transform: translateY(-3px);
  border-color: rgba(var(--tile-accent-rgb), 0.5);
  box-shadow: 0 16px 36px -12px rgba(var(--tile-accent-rgb), 0.45), 0 4px 12px rgba(0, 0, 0, 0.35);
}

.stat-tile .value {
  font-family: var(--font-display);
  font-size: 1.9rem;
  font-weight: 700;
  display: block;
  color: #ffffff;
}

.stat-tile:nth-child(1) .value,
.stat-tile:nth-child(4) .value {
  font-size: clamp(2.1rem, 3vw, 2.7rem);
}

.stat-tile .label {
  color: var(--text-muted);
  font-size: 0.8rem;
  text-transform: uppercase;
  letter-spacing: 0.04em;
}

/* A short ring-pulse on the tile whenever its number actually changes value
   (js/motion.js's animateNumber triggers it) — a beat of feedback for
   peripheral vision to catch on a dashboard filter change, not just the
   digits ticking. Never triggered under reduced motion. */
@keyframes stat-pulse {
  0% {
    box-shadow: 0 0 0 0 rgba(var(--tile-accent-rgb), 0.55);
  }
  70%,
  100% {
    box-shadow: 0 0 0 16px rgba(var(--tile-accent-rgb), 0);
  }
}

.stat-tile.pulse {
  animation: stat-pulse 0.6s ease-out;
}

/* Sections */
.finding {
  position: relative;
  margin: 60px 0;
  padding-top: 24px;
  border-top: 1px solid var(--gridline);
}

/* 4-color rotation (was 3) so all four palette colors — including decor-4 —
   show up across the report's 8 findings, two apiece, instead of cycling
   through just series-1/2/3 and leaving decor-4 unused here. */
.finding:nth-of-type(4n+1) {
  --finding-accent: var(--series-1);
  --finding-accent-rgb: var(--series-1-rgb);
}

.finding:nth-of-type(4n+2) {
  --finding-accent: var(--series-2);
  --finding-accent-rgb: var(--series-2-rgb);
}

.finding:nth-of-type(4n+3) {
  --finding-accent: var(--series-3);
  --finding-accent-rgb: var(--series-3-rgb);
}

.finding:nth-of-type(4n) {
  --finding-accent: var(--decor-4);
  --finding-accent-rgb: var(--decor-4-rgb);
}

.finding:not(.closing):not(#finding-10) {
  counter-increment: finding;
}

.finding h2 {
  display: flex;
  align-items: center;
  gap: 16px;
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 1.45rem;
  letter-spacing: -0.005em;
  margin: 10px 0 12px;
  color: #ffffff;
}

.finding:not(.closing) h2::before {
  content: counter(finding, decimal-leading-zero);
  flex: 0 0 auto;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 42px;
  height: 42px;
  border-radius: var(--radius-sm);
  font-size: 1.02rem;
  font-weight: 700;
  color: #0e141c;
  background: linear-gradient(135deg, var(--finding-accent), rgba(var(--finding-accent-rgb), 0.65));
  box-shadow: 0 8px 18px -4px rgba(var(--finding-accent-rgb), 0.55);
}

.closing h2 {
  padding-left: 14px;
  border-left: 4px solid var(--accent);
}

.finding p {
  color: var(--text-secondary);
  max-width: 70ch;
}

/* Findings with a chart below them read the text at the chart's own full
   width instead of the 70ch reading-line cap, so the write-up doesn't leave
   a block of empty space beside it before the chart-card starts. The
   closing section's methodology prose (no chart) keeps the narrower cap. */
.finding:not(.closing) p {
  max-width: none;
}

.chart-card {
  position: relative;
  overflow: hidden;
  --bracket-color: var(--tile-accent, var(--finding-accent, var(--accent)));
  /* Still read by js/cursor-fx.js to color the particle trail spawned while
     hovering this card, even though nothing here draws a glow with it. */
  --bracket-color-rgb: var(--tile-accent-rgb, var(--finding-accent-rgb, var(--accent-rgb)));
  background-image: linear-gradient(160deg, var(--surface-2) 0%, var(--surface-1) 100%);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 16px;
  margin-top: 14px;
  box-shadow: 0 4px 14px rgba(0, 0, 0, 0.3);
  transition: transform 0.18s ease, box-shadow 0.18s ease, border-color 0.18s ease;
}

.chart-card::before {
  content: "";
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  height: 3px;
  background: var(--bracket-color);
  opacity: 0.9;
}

/* Four corner brackets, like a targeting reticle — eight thin solid-fill
   background layers (an H bar + V bar per corner), positioned with the
   `right/bottom <offset>` background-position syntax so each pair shares
   one anchor point instead of needing four separate calc()s. */
.chart-card::after {
  content: "";
  position: absolute;
  inset: 0;
  pointer-events: none;
  background-image: linear-gradient(var(--bracket-color), var(--bracket-color)),
    linear-gradient(var(--bracket-color), var(--bracket-color)),
    linear-gradient(var(--bracket-color), var(--bracket-color)),
    linear-gradient(var(--bracket-color), var(--bracket-color)),
    linear-gradient(var(--bracket-color), var(--bracket-color)),
    linear-gradient(var(--bracket-color), var(--bracket-color)),
    linear-gradient(var(--bracket-color), var(--bracket-color)),
    linear-gradient(var(--bracket-color), var(--bracket-color));
  background-repeat: no-repeat;
  background-size: 14px 2px, 2px 14px, 14px 2px, 2px 14px, 14px 2px, 2px 14px, 14px 2px, 2px 14px;
  background-position: left 8px top 8px, left 8px top 8px, right 8px top 8px, right 8px top 8px,
    right 8px bottom 8px, right 8px bottom 8px, left 8px bottom 8px, left 8px bottom 8px;
  opacity: 0.5;
  transition: opacity 0.18s ease;
}

.finding .chart-card:hover {
  transform: translateY(-2px);
  border-color: rgba(var(--finding-accent-rgb), 0.45);
  box-shadow: 0 18px 40px -16px rgba(var(--finding-accent-rgb), 0.4), 0 4px 14px rgba(0, 0, 0, 0.3);
}

.chart-card:hover::after {
  opacity: 1;
}

/* p.chart-fig (not just .chart-fig): needs to match .finding p's own
   specificity so max-width/color here win over that rule's 70ch clamp and
   secondary-text color — otherwise, inside a .finding, the flex row is
   squeezed to 70ch and "Fig." lands well short of the card's right edge. */
p.chart-fig {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
  margin: 0 0 6px;
  max-width: none;
  font-family: var(--font-mono);
  font-size: 0.68rem;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--text-muted);
}

/* Aperture Science's own logo (img/aperture-logo.png, recolored/cropped
   from the source file), stamped in front of the figure number like a
   lab-equipment badge. */
.chart-fig-logo {
  height: 14px;
  width: auto;
  flex: 0 0 auto;
  opacity: 0.85;
}

.chart-card canvas {
  max-height: 320px;
}

.closing h2 {
  font-size: 1.35rem;
}

.closing p,
.closing li {
  color: var(--text-secondary);
  max-width: 74ch;
}

.finding p.closing-cta {
  max-width: none;
}

/* Dashboard */
.filter-bar {
  display: flex;
  flex-wrap: wrap;
  align-items: end;
  gap: 16px;
  background: linear-gradient(160deg, var(--surface-2) 0%, var(--surface-1) 100%);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 16px;
  margin: 24px 0;
}

.filter-field {
  display: flex;
  flex-direction: column;
  gap: 4px;
  font-size: 0.85rem;
  color: var(--text-secondary);
}

.filter-field label {
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.03em;
  font-size: 0.75rem;
  color: var(--text-muted);
}

.filter-field select,
.filter-field input {
  font-family: var(--font-sans);
  font-size: 0.9rem;
  padding: 7px 8px;
  border: 1px solid var(--baseline);
  border-radius: var(--radius-sm);
  background: #0e141c;
  color: var(--text-primary);
  min-width: 150px;
}

.filter-field select:focus,
.filter-field input:focus {
  outline: none;
  border-color: var(--accent);
  box-shadow: 0 0 0 1px var(--accent);
}

.reset-filters-btn {
  font-family: var(--font-sans);
  font-weight: 700;
  font-size: 0.85rem;
  text-transform: uppercase;
  letter-spacing: 0.03em;
  padding: 9px 18px;
  border-radius: var(--radius-sm);
  border: none;
  background: linear-gradient(to right, #588a1b 0%, #8bc53f 100%);
  color: #e6f5d0;
  cursor: pointer;
  height: fit-content;
  box-shadow: 0 2px 6px rgba(0, 0, 0, 0.35);
  transition: transform 0.15s ease, box-shadow 0.15s ease, background 0.15s ease, color 0.15s ease;
}

.reset-filters-btn:hover {
  background: linear-gradient(to right, #6ba829 0%, #a0dc4c 100%);
  color: #ffffff;
  transform: translateY(-1px);
  /* Own green glow, not --series-2-rgb — that token now means chart-series
     orange, which would clash with this button's green fill. */
  box-shadow: 0 10px 22px -8px rgba(139, 197, 63, 0.55);
}

/* Smaller twin of the filter-bar's reset button, sized to sit inline with
   the compact selects in .chart-controls rather than the filter bar's own
   larger fields. */
.chart-controls .reset-filters-btn {
  font-size: 0.78rem;
  padding: 6px 14px;
}

.chart-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(380px, 1fr));
  gap: 16px;
  margin: 24px 0;
}

.chart-grid .chart-card:nth-child(4n+1) {
  --tile-accent: var(--series-1);
  --tile-accent-rgb: var(--series-1-rgb);
}

.chart-grid .chart-card:nth-child(4n+2) {
  --tile-accent: var(--series-2);
  --tile-accent-rgb: var(--series-2-rgb);
}

.chart-grid .chart-card:nth-child(4n+3) {
  --tile-accent: var(--series-3);
  --tile-accent-rgb: var(--series-3-rgb);
}

.chart-grid .chart-card:nth-child(4n) {
  --tile-accent: var(--decor-4);
  --tile-accent-rgb: var(--decor-4-rgb);
}

.chart-grid .chart-card:hover {
  transform: translateY(-2px);
  border-color: rgba(var(--tile-accent-rgb), 0.45);
  box-shadow: 0 18px 40px -16px rgba(var(--tile-accent-rgb), 0.4), 0 4px 14px rgba(0, 0, 0, 0.3);
}

.chart-controls {
  display: flex;
  gap: 10px;
  margin-bottom: 10px;
  flex-wrap: wrap;
}

.chart-controls select {
  font-family: var(--font-sans);
  font-size: 0.82rem;
  padding: 5px 6px;
  border: 1px solid var(--baseline);
  border-radius: var(--radius-sm);
  background: #0e141c;
  color: var(--text-primary);
}

.chart-card h3 {
  margin: 0 0 8px;
  font-family: var(--font-display);
  font-weight: 600;
  letter-spacing: 0.01em;
  font-size: 1.05rem;
  color: var(--tile-accent, var(--accent));
}

/* Table */
.table-wrap {
  overflow-x: auto;
  background: var(--surface-1);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 4px 16px;
  margin: 16px 0 40px;
  box-shadow: 0 4px 14px rgba(0, 0, 0, 0.3);
}

table {
  width: 100%;
  border-collapse: collapse;
  font-size: 0.9rem;
}

th,
td {
  text-align: left;
  padding: 10px 12px;
  border-bottom: 1px solid var(--gridline);
  font-variant-numeric: tabular-nums;
}

tbody tr:nth-child(odd) {
  background: rgba(255, 255, 255, 0.02);
}

tbody tr:hover {
  background: rgba(102, 192, 244, 0.08);
}

/* Rows fade/rise in on a fresh render (js/dashboard.js adds "row-enter" on
   insert, then removes it a frame later — never added at all under reduced
   motion, so the rule below is simply inert there). */
tbody tr {
  transition: opacity 0.25s ease, transform 0.25s ease;
}

tbody tr.row-enter {
  opacity: 0;
  transform: translateY(6px);
}

th {
  color: var(--text-muted);
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.03em;
  font-size: 0.78rem;
  cursor: pointer;
  user-select: none;
  white-space: nowrap;
}

th:hover {
  color: var(--accent);
}

th[data-sort]:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: -2px;
}

th[data-sort].sort-asc,
th[data-sort].sort-desc {
  color: var(--accent);
}

th[data-sort].sort-asc::after,
th[data-sort].sort-desc::after {
  display: inline-block;
  margin-left: 6px;
  font-size: 0.7em;
}

th[data-sort].sort-asc::after {
  content: "▲";
}

th[data-sort].sort-desc::after {
  content: "▼";
}

td:first-child,
th:first-child {
  font-variant-numeric: normal;
}

/* Numeric columns read as numbers, not text — right-aligned so digits stack
   for comparison, matching the header they sit under. */
th[data-sort]:not(:first-child) {
  text-align: right;
}

td.num-cell {
  text-align: right;
}

tr.row-drillable {
  cursor: pointer;
}

tr.row-drillable:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: -2px;
}

tr.row-active {
  box-shadow: inset 3px 0 0 0 var(--accent);
}

tr.row-active,
tr.row-active:hover {
  background: rgba(var(--accent-rgb), 0.16);
}

.section-title {
  margin: 48px 0 6px;
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 1.3rem;
  color: #ffffff;
  padding-left: 14px;
  border-left: 4px solid var(--accent);
}

.hint {
  color: var(--text-muted);
  font-size: 0.85rem;
  margin: 0 0 12px;
}

/* A live readout of what the table is currently showing — breakdown, sort,
   and any row-drilldown filter — so that state is always spelled out
   in words instead of left for hover/click states alone to imply. */
.table-status {
  font-family: var(--font-mono);
  font-size: 0.8rem;
  color: var(--text-secondary);
  margin: 0 0 10px;
}

.table-status strong {
  color: var(--accent);
  font-weight: 700;
}

footer.site-footer {
  text-align: center;
  color: var(--text-muted);
  font-size: 0.85rem;
  padding: 0 20px 40px;
  margin-top: 40px;
}

footer.site-footer::before {
  content: "";
  display: block;
  height: 3px;
  width: 100%;
  margin-bottom: 32px;
  background: linear-gradient(90deg, var(--series-1), var(--series-2), var(--series-3), var(--decor-4));
  opacity: 0.7;
}

/* Nav overflow popup (replay-intro lives inside it), docked to the right
   side of the nav. The menu is right-anchored to the trigger and animates
   in from that fixed right edge, so it visibly "opens to the left" instead
   of risking overflow past the viewport's right edge. */
.nav-popup {
  position: relative;
}

.nav-popup-trigger {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 34px;
  height: 34px;
  padding: 0;
  color: var(--text-muted);
  background: transparent;
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  cursor: pointer;
  transition: color 0.15s ease, border-color 0.15s ease;
}

.nav-popup-trigger svg {
  fill: currentColor;
}

.nav-popup-trigger:hover,
.nav-popup.open .nav-popup-trigger {
  color: #ffffff;
  border-color: var(--accent);
}

.nav-popup-menu {
  position: absolute;
  top: calc(100% + 8px);
  right: 0;
  min-width: 160px;
  background: linear-gradient(160deg, var(--surface-2) 0%, var(--surface-1) 100%);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  box-shadow: 0 12px 28px -10px rgba(0, 0, 0, 0.55);
  padding: 6px;
  opacity: 0;
  transform: translateX(60px);
  pointer-events: none;
  transition: opacity 0.22s ease, transform 0.22s cubic-bezier(0.22, 0.7, 0.3, 1);
  z-index: 20;
}

.nav-popup.open .nav-popup-menu {
  opacity: 1;
  transform: translateX(0);
  pointer-events: auto;
}

.nav-popup-item {
  display: block;
  width: 100%;
  text-align: left;
  font-family: var(--font-sans);
  font-size: 0.85rem;
  font-weight: 600;
  letter-spacing: 0.02em;
  color: var(--text-secondary);
  background: transparent;
  border: none;
  border-radius: var(--radius-sm);
  padding: 9px 10px;
  cursor: pointer;
  transition: color 0.15s ease, background 0.15s ease;
}

.nav-popup-item:hover {
  color: #ffffff;
  background: rgba(255, 255, 255, 0.06);
}

/* Muted state: the label reads "Sound" either way — a diagonal strike
   across just the word (sized to its own text width, not the row) signals
   "off" instead of swapping in an "on"/"off" suffix. */
.sound-toggle-label {
  position: relative;
  display: inline-block;
}

[data-sound-toggle][aria-pressed="false"] {
  color: var(--text-muted);
}

[data-sound-toggle][aria-pressed="false"] .sound-toggle-label::after {
  content: "";
  position: absolute;
  left: -3px;
  right: -3px;
  top: 50%;
  height: 1.5px;
  background: currentColor;
  transform: translateY(-50%) rotate(-12deg);
}

/* Boot intro — mimics the Steam Deck startup animation: particles converge
   into the site's brand mark, then fade into the page. See js/boot-intro.js. */
html.boot-lock,
html.boot-lock body {
  overflow: hidden;
  height: 100%;
}

#boot-intro {
  position: fixed;
  inset: 0;
  z-index: 999;
  background: #06090d;
  cursor: pointer;
  opacity: 1;
  transition: opacity 1.1s ease;
}

#boot-intro.boot-fade {
  opacity: 0;
  pointer-events: none;
}

#boot-intro canvas {
  position: absolute;
  inset: 0;
  display: block;
  opacity: 1;
  transition: opacity 0.35s ease;
}

#boot-intro canvas.boot-canvas-hide {
  opacity: 0;
}

/* Steam icon build-up: a blue "power" pupil inside a ring that closes,
   the arm/ball fading in, then an outer badge ring drawing itself and
   crossfading into Valve's exact icon geometry (.lg-final). */
.boot-logo-wrap {
  position: absolute;
  left: 50%;
  top: 50%;
  transform: translate(-50%, calc(-50% - 10px));
  width: var(--boot-logo-size, 300px);
  height: var(--boot-logo-size, 300px);
  opacity: 0;
  pointer-events: none;
  filter: drop-shadow(0 0 24px rgba(102, 192, 244, 0.45));
  transition: opacity 0.4s ease;
}

#boot-intro.boot-logo-active .boot-logo-wrap {
  opacity: 1;
}

.boot-logo-wrap svg {
  width: 100%;
  height: 100%;
  overflow: visible;
}

.lg-eye-ring {
  fill: none;
  stroke: #ffffff;
  opacity: 0;
  transition: opacity 0.3s ease 0.3s, stroke-dashoffset 0.75s cubic-bezier(0.22, 0.7, 0.3, 1) 0.35s;
}

#boot-intro.boot-logo-active .lg-eye-ring {
  opacity: 1;
  stroke-dashoffset: 0;
}

.lg-arm {
  fill: #ffffff;
  opacity: 0;
  transition: opacity 0.65s ease 0.4s;
}

#boot-intro.boot-logo-active .lg-arm {
  opacity: 1;
}

.lg-build {
  transition: opacity 0.6s ease 1.5s;
}

#boot-intro.boot-logo-active .lg-build {
  opacity: 0;
}

.lg-final {
  fill: #ffffff;
  opacity: 0;
  transition: opacity 0.6s ease 1.5s;
}

#boot-intro.boot-logo-active .lg-final {
  opacity: 1;
}

.lg-badge {
  fill: none;
  stroke: #ffffff;
  stroke-width: 0.9;
  opacity: 0.85;
  transition: stroke-dashoffset 0.85s cubic-bezier(0.22, 0.7, 0.3, 1) 0.9s;
}

#boot-intro.boot-logo-active .lg-badge {
  stroke-dashoffset: 0;
}

.boot-wordmark {
  position: absolute;
  left: 0;
  right: 0;
  top: calc(50% + 185px);
  text-align: center;
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 1.1rem;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: #ffffff;
  opacity: 0;
  transform: translateY(6px);
  transition: opacity 0.8s ease, transform 0.8s ease;
  pointer-events: none;
}

#boot-intro.boot-assembled .boot-wordmark {
  opacity: 0.92;
  transform: none;
}

#boot-intro.boot-reduced canvas,
#boot-intro.boot-reduced .boot-logo-wrap {
  display: none;
}

#boot-intro.boot-reduced .boot-wordmark {
  top: 50%;
  transform: translateY(-50%);
}

/* Registering these as typed custom properties is what makes them
   transition smoothly through the var()/calc() expressions that consume
   them above (.page-header's parallax) — an unregistered custom property
   change never animates, it just snaps. */
@property --hx {
  syntax: "<length>";
  inherits: true;
  initial-value: 0px;
}

@property --hy {
  syntax: "<length>";
  inherits: true;
  initial-value: 0px;
}

/* Reading-progress bar (report page only; js/motion.js no-ops if the
   element isn't present). Fixed just under the sticky nav so it's visible
   at any scroll position. Reflects scroll position directly — not gated by
   prefers-reduced-motion, same as a native scrollbar. */
.scroll-progress {
  position: fixed;
  top: 56px;
  left: 0;
  width: 100%;
  height: 3px;
  background: rgba(255, 255, 255, 0.06);
  z-index: 9;
}

.scroll-progress-bar {
  height: 100%;
  width: 100%;
  /* Same 4-color sweep as the footer's own top line (footer.site-footer::before). */
  background: linear-gradient(90deg, var(--series-1), var(--series-2), var(--series-3), var(--decor-4));
  transform: scaleX(0);
  transform-origin: left;
}

/* Skeleton shimmer on the headline stat tiles while the CSV is still
   loading (report.js/dashboard.js remove body.is-loading once data lands).
   The shimmer keyframe itself is reduced-motion-gated; the transparent
   placeholder box still shows either way. */
body.is-loading .stat-tile .value {
  color: transparent;
  display: inline-block;
  min-width: 3.5em;
  border-radius: 4px;
  background: linear-gradient(90deg, var(--surface-2) 25%, var(--surface-1) 37%, var(--surface-2) 63%);
  background-size: 400% 100%;
}

@media (prefers-reduced-motion: no-preference) {
  body.is-loading .stat-tile .value {
    animation: skeleton-shimmer 1.4s ease infinite;
  }
}

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


/* Release timeline (finding 10) */
/* The year strip is a store-style bar (same dark-blue gradient and shadow as
   the featured panel below it) instead of a chart-card, so the whole timeline
   reads as one piece of the store page. */
.timeline {
  margin-top: 14px;
}

.timeline-track {
  position: relative;
  display: flex;
  align-items: flex-end;
  overflow-x: auto;
  padding: 10px 8px 10px;
  scrollbar-width: thin;
  background: linear-gradient(135deg, #1b2b3c 0%, #16202d 60%, #121a24 100%);
  box-shadow: 0 0 12px rgba(0, 0, 0, 0.55);
  border-radius: 3px;
}

.timeline-node {
  position: relative;
  flex: 0 0 auto;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: flex-end;
  gap: 8px;
  min-width: 52px;
  padding: 0;
  background: none;
  border: 0;
  color: var(--text-secondary);
  font: inherit;
  cursor: pointer;
}

/* Fixed-height slot centers every dot on one shared line, drawn per slot so
   it spans the whole track without depending on the dots' varying sizes. */
.timeline-slot {
  position: relative;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 100%;
  height: 40px;
}

.timeline-slot::before {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  top: 50%;
  height: 2px;
  margin-top: -1px;
  background: rgba(103, 193, 245, 0.18);
}

.timeline-dot {
  position: relative;
  display: block;
  border-radius: 50%;
  background: rgba(103, 193, 245, 0.25);
  border: 2px solid rgba(103, 193, 245, 0.7);
  transition: transform 0.15s ease, background 0.15s ease, box-shadow 0.15s ease;
}

.timeline-year {
  font-family: var(--font-mono);
  font-size: 0.82rem;
}

.timeline-node:hover .timeline-dot {
  transform: scale(1.15);
}

.timeline-node:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
  border-radius: var(--radius-sm);
}

.timeline-node.is-active {
  color: #ffffff;
}

.timeline-node.is-active .timeline-dot {
  background: #67c1f5;
  border-color: #c6d4df;
  box-shadow: 0 0 0 5px rgba(103, 193, 245, 0.28);
}

.timeline-panel {
  margin-top: 14px;
}

/* The picks panel is styled after Steam's store "Featured & Recommended"
   carousel: wide capsule art on the left with a dark info column, a rail of
   small portrait thumbnails on the right, store-blue tag pills, a green/red
   review line, and a dark price box. Colors are the store's own palette,
   kept local to this block rather than added to the site's tokens. */
.timeline-head {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 4px 14px;
}

.timeline-title {
  margin: 0;
  font-family: var(--font-sans);
  font-size: 0.82rem;
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: #ffffff;
}

.timeline-sub {
  font-size: 0.8rem;
  color: #8f98a0;
}

.store-feature {
  display: grid;
  grid-template-columns: minmax(0, 1fr) 232px;
  gap: 4px;
  margin-top: 10px;
  padding: 4px;
  background: linear-gradient(135deg, #1b2b3c 0%, #16202d 60%, #121a24 100%);
  box-shadow: 0 0 12px rgba(0, 0, 0, 0.55);
  border-radius: 3px;
}

.store-feature[hidden] {
  display: none;
}

.store-hero {
  display: grid;
  grid-template-columns: minmax(0, 1.5fr) minmax(0, 1fr);
  background: rgba(0, 0, 0, 0.25);
  min-height: 300px;
}

.store-stage {
  background: #0b1118;
  display: flex;
  align-items: center;
}

/* Natural aspect ratio, never cropped: the stage's dark background fills any
   leftover height. contain also keeps the portrait fallback intact. */
.store-capsule {
  display: block;
  width: 100%;
  height: auto;
  max-height: 100%;
  object-fit: contain;
}

.store-capsule.is-missing,
.timeline-art.is-missing {
  visibility: hidden;
}

.store-info {
  display: flex;
  flex-direction: column;
  gap: 10px;
  padding: 16px 16px 14px;
  background: linear-gradient(180deg, rgba(42, 71, 94, 0.55) 0%, rgba(22, 32, 45, 0.9) 100%);
}

.store-name {
  margin: 0;
  font-family: var(--font-sans);
  font-size: 1.35rem;
  font-weight: 400;
  line-height: 1.2;
  color: #ffffff;
  overflow-wrap: anywhere;
}

.store-tags {
  display: flex;
  flex-wrap: wrap;
  gap: 4px;
}

.store-tag {
  padding: 2px 7px;
  font-size: 0.74rem;
  border-radius: 2px;
  color: #67c1f5;
  background: rgba(103, 193, 245, 0.2);
}

.store-review,
.store-owners {
  font-size: 0.82rem;
  color: #8f98a0;
}

.store-review-label.is-pos {
  color: #66c0f4;
}

.store-review-label.is-mixed {
  color: #b9a074;
}

.store-review-label.is-neg {
  color: #a8926a;
}

.store-price {
  margin-top: auto;
  align-self: flex-start;
  padding: 6px 12px;
  font-size: 0.95rem;
  color: #c6d4df;
  background: rgba(0, 0, 0, 0.4);
  border-radius: 2px;
}

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

.store-rail-item {
  flex: 1 1 0;
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 4px 8px 4px 4px;
  text-align: left;
  font: inherit;
  color: #c6d4df;
  background: rgba(103, 193, 245, 0.1);
  border: 0;
  cursor: pointer;
  transition: background 0.15s ease;
}

.store-rail-item:hover,
.store-rail-item:focus-visible {
  background: rgba(103, 193, 245, 0.22);
  outline: none;
}

.store-rail-item.is-active {
  background: rgba(103, 193, 245, 0.35);
  color: #ffffff;
}

.store-rail-text {
  display: flex;
  flex-direction: column;
  min-width: 0;
}

.store-rail-name {
  font-size: 0.86rem;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.store-rail-price {
  font-size: 0.76rem;
  color: #8f98a0;
}

.timeline-art {
  flex: 0 0 auto;
  width: 40px;
  height: 60px;
  object-fit: cover;
  border-radius: 2px;
  background: #0b1118;
}

@media (max-width: 760px) {
  .store-feature,
  .store-hero {
    grid-template-columns: 1fr;
  }

  .store-rail-item {
    padding-block: 6px;
  }
}

.timeline-explore {
  margin-top: 14px;
  padding: 0;
  background: none;
  border: 0;
  color: var(--accent);
  font: inherit;
  cursor: pointer;
}

.timeline-explore:hover {
  text-decoration: underline;
}

/* The timeline's slot in the report's 4-color rotation lands on orange;
   it's pinned to the Steam logo's gray (#C5C3C0, from img/steam-icon.svg)
   instead. The id selector outranks the nth-of-type rules. */
#finding-10 {
  --finding-accent: #c5c3c0;
  --finding-accent-rgb: 197, 195, 192;
}

/* No "10" number badge on the timeline heading. */
#finding-10 h2::before {
  display: none;
}

/* Dashboard milestones strip: one box-art tile per year. */
.milestones-strip {
  display: flex;
  gap: 10px;
  overflow-x: auto;
  padding: 6px 4px 10px;
  scrollbar-width: thin;
}

.milestone {
  flex: 0 0 auto;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 6px;
  padding: 0;
  background: none;
  border: 0;
  font: inherit;
  color: var(--text-secondary);
  cursor: pointer;
}

.milestone-art {
  width: 80px;
  height: 120px;
  object-fit: cover;
  border-radius: 3px;
  background: #0b1118;
  border: 1px solid var(--border);
  box-shadow: 0 4px 10px rgba(0, 0, 0, 0.4);
  transition: transform 0.15s ease, border-color 0.15s ease;
}

.milestone-art.is-missing {
  visibility: hidden;
}

.milestone-year {
  font-family: var(--font-mono);
  font-size: 0.82rem;
}

.milestone:hover .milestone-art,
.milestone:focus-visible .milestone-art,
.milestone.is-active .milestone-art {
  transform: translateY(-3px);
  border-color: var(--accent);
}

.milestone:focus-visible {
  outline: none;
}

.milestone.is-active {
  color: #ffffff;
}

.milestones-footer {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  justify-content: space-between;
  gap: 4px 16px;
}

.milestones-all {
  margin-left: auto;
  padding: 0;
  background: none;
  border: 0;
  font: inherit;
  font-size: 0.88rem;
  color: var(--accent);
  cursor: pointer;
}

.milestones-all[hidden] {
  display: none;
}

.milestones-all:hover {
  text-decoration: underline;
}

.milestones-caption {
  min-height: 1.4em;
  margin: 0;
  font-size: 0.88rem;
  color: var(--text-secondary);
}

/* Table: expandable "top games" row under a group */
.row-expand {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 22px;
  height: 22px;
  margin-right: 8px;
  padding: 0;
  font-size: 0.8rem;
  color: var(--accent);
  background: rgba(var(--accent-rgb), 0.12);
  border: 0;
  border-radius: 3px;
  cursor: pointer;
  vertical-align: middle;
}

.row-expand:hover,
.row-expand:focus-visible {
  background: rgba(var(--accent-rgb), 0.28);
  outline: none;
}

tr.detail-row > td {
  padding: 4px 12px 14px 42px;
  background: rgba(var(--accent-rgb), 0.05);
}

.detail-title {
  margin: 6px 0 6px;
  font-size: 0.8rem;
  color: var(--text-muted);
}

.detail-table {
  width: 100%;
  border-collapse: collapse;
  font-size: 0.86rem;
}

.detail-table th {
  cursor: default;
  padding: 6px 10px;
  font-size: 0.7rem;
}

.detail-table td {
  padding: 6px 10px;
}

.detail-table th:hover {
  color: var(--text-muted);
}

.detail-name {
  font-weight: 600;
  color: #ffffff;
}

.detail-table th.num-cell {
  text-align: right;
}

.charts-title .reset-filters-btn {
  margin-left: 14px;
  font-size: 0.78rem;
  padding: 6px 14px;
  vertical-align: middle;
}
