/*
 * Holocron Rating Index — Design System
 * Dark theme, holocron crystal aesthetic.
 */

/* ============================================================
   DESIGN TOKENS
   ============================================================ */

:root {
  /* Colors — from Pencil design (design/hri-design.pen) */
  --bg:              #0B1120;
  --surface:         #111827;
  --card:            #1A2332;
  --card-hover:      #1E2A3D;
  --input:           #0F1A2A;
  --border:          #1E3A5F;
  --text-primary:    #F1F5F9;
  --text-secondary:  #CBD5E1;
  --text-tertiary:   #94A3B8;
  --accent:          #0EA5E9;
  --accent-hover:    #38BDF8;
  --accent-surface:  var(--accent-subtle);
  --accent-glow:     rgba(56, 189, 248, 0.2);

  /* Accent alpha scale. Use these instead of inline rgba() calls so
     hover/active/surface tints stay consistent across the stylesheet.
       wash    — almost-there tint (backgrounds, grid, hovers on dark cards)
       subtle  — chip bg, surface tint
       soft    — callout bg, active-filter bg, header underline
       mid     — focus ring halo, medium glow
       strong  — primary border, strong chip border
       hot     — reserved for #1 rank, peak states */
  --accent-wash:     rgba(14, 165, 233, 0.04);
  --accent-subtle:   rgba(14, 165, 233, 0.08);
  --accent-soft:     rgba(14, 165, 233, 0.18);
  --accent-mid:      rgba(14, 165, 233, 0.25);
  --accent-strong:   rgba(14, 165, 233, 0.4);
  --accent-hot:      rgba(14, 165, 233, 0.6);
  --positive:        #22C55E;
  --negative:        #EF4444;
  --warning:         #F59E0B;

  /* Tier colors — distinct hues for instant recognition */
  --tier-planetary:  #22C55E;
  --tier-sector:     #F59E0B;
  --tier-regional:   #0EA5E9;
  --tier-galactic:   #7C3AED;

  /* Rank medal colors */
  --rank-gold:       #F59E0B;
  --rank-silver:     #CBD5E1;
  --rank-bronze:     #D97706;

  /* Typography — Geist family from Pencil design.
     Inter Fallback is a metric-matched local Arial defined further down in
     the @font-face block; listing it here means the page lays out at near-
     final metrics on first paint, eliminating the layout shift when Inter
     finishes loading over the Google Fonts CDN. */
  --font-display: 'Geist', system-ui, sans-serif;
  --font-body:    'Inter', 'Inter Fallback', system-ui, sans-serif;
  --font-data:    'Geist Mono', 'JetBrains Mono', monospace;

  /* Type scale (rem-based; derived from DESIGN.md). Rem so user font-size
     preferences and browser zoom scale the whole UI proportionally — px
     values would freeze the scale at 16px regardless of accessibility
     settings. The 9 main steps cover everything from nav labels to the
     page hero; the two micro tokens cover sub-readable contexts (tier
     badges, deltas in tight tables) where dropping below body size is
     intentional, not accidental. */
  --text-2xs:    0.6875rem; /* 11px — column headers, tier tag labels */
  --text-xs:     0.75rem;   /* 12px — small caption, secondary metadata */
  --text-sm:     0.8125rem; /* 13px — secondary body, table small */
  --text-base:   0.875rem;  /* 14px — body default in app contexts */
  --text-md:     1rem;      /* 16px — rank numbers, large body */
  --text-lg:     1.125rem;  /* 18px — subheadings, callout body */
  --text-xl:     1.25rem;   /* 20px — section headings */
  --text-2xl:    1.375rem;  /* 22px — page subtitles, brand mark */
  --text-3xl:    1.75rem;   /* 28px — card stat values, page H1 */
  --text-4xl:    2rem;      /* 32px — wide hero secondary */
  --text-5xl:    2.25rem;   /* 36px — profile rating display */
  --text-6xl:    2.5rem;    /* 40px — page-level hero title */
  --text-7xl:    3rem;      /* 48px — oversize stat */
  --text-8xl:    4rem;      /* 64px — mega hero (rare) */
  /* Sub-readable micro tier — for tier badges, deltas in compact rows,
     other contextual labels. Never use for paragraphs. */
  --text-micro:  0.625rem;  /* 10px */
  --text-nano:   0.5625rem; /*  9px */

  /* Line heights — semantic names, not values. Pair with type-scale tokens:
       tight    — single-line display numbers, hero ratings
       snug     — headings (h1-h3)
       normal   — UI labels, table cells, dense secondary text
       relaxed  — body paragraphs
       loose    — long-form prose (/about, /how-it-works) */
  --leading-tight:   1;
  --leading-snug:    1.2;
  --leading-normal:  1.4;
  --leading-relaxed: 1.5;
  --leading-loose:   1.7;

  /* Letter spacing — semantic. Display headings tighten slightly so the big
     letterforms feel sculpted; uppercase mono labels (column headers, tier
     tags, microcopy in caps) open up so they don't look smushed. Normal
     flow text leaves it at the font's default. */
  --tracking-tight:  -0.02em;
  --tracking-normal: 0;
  --tracking-wide:   0.06em;
  --tracking-wider:  0.08em;

  /* Spacing (8px base) */
  --space-2xs:  4px;
  --space-xs:   8px;
  --space-sm:   12px;
  --space-md:   16px;
  --space-lg:   24px;
  --space-xl:   32px;
  --space-2xl:  48px;
  --space-3xl:  64px;

  /* Border radii */
  --radius-sm:   4px;
  --radius-md:   8px;
  --radius-lg:   12px;
  --radius-full: 20px;
}

/* ============================================================
   WEB FONT FALLBACKS
   ============================================================ */

/* Metric-matched fallback for Inter. Google Fonts loads Inter asynchronously
   (display=swap) — without this, the initial paint uses Arial at Arial's
   natural metrics, and the page reflows when Inter finishes loading. The
   overrides below tune local('Arial') to match Inter's x-height, cap-height,
   and advance widths, so the shift on swap is near-invisible.

   Values published by Vercel (next/font's built-in Inter fallback config);
   verified against Inter 4.x on macOS. Geist and Geist Mono don't get a
   matched fallback here because publishing tuned values for them would need
   empirical measurement this commit hasn't done — they keep the naive
   system-ui / monospace fallback, which means a small shift on first load
   for display and data text. Acceptable tradeoff: body prose is the main
   reading surface and it's the one most hurt by layout shift. */
@font-face {
  font-family: 'Inter Fallback';
  src: local('Arial');
  size-adjust: 107%;
  ascent-override: 90%;
  descent-override: 22%;
  line-gap-override: 0%;
}

/* ============================================================
   RESET / BASE
   ============================================================ */

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

/* tabular-nums is an inherited property and applies to whichever font
   actually renders the digits — so setting it on data-heavy page wrappers
   gives every number inside (rating, RD, win%, rank, deltas) tabular
   figures automatically, without re-declaring the rule per-cell. Wrappers
   listed are the leaderboard table, profile/audit/H2H pages, and both
   tournament views. New data-dense surfaces should join this list rather
   than re-declaring on individual cells. */
.table-container,
.leaderboards-show,
.profile-page,
.player-vs,
.player-audit,
.tournaments-index,
.tournament-show {
  font-variant-numeric: tabular-nums;
}

html {
  /* 100% (not 16px) so the whole rem-based type scale honors the user's
     browser font-size preference. Users who've bumped their default to
     20px in browser settings get the entire UI scaled up 25%; users on
     default 16px see the nominal sizes. Pure accessibility win with no
     visual cost for default-preference users. */
  font-size: 100%;
  -webkit-text-size-adjust: 100%;
}

body {
  font-family: var(--font-body);
  font-size: var(--text-md);
  line-height: var(--leading-relaxed);
  background-color: var(--bg);
  color: var(--text-primary);
  min-height: 100vh;
  display: flex;
  flex-direction: column;
  isolation: isolate;
  /* Inter OpenType features. cv11 swaps the default double-story 'a' for a
     single-story humanist form — tiny lift that takes Inter from
     generic-SaaS to a touch more distinctive. ss03 is Inter's "rounder
     quotes" set; leaving it off for now but easy to add. Cost: zero,
     since OTF features are encoded in the font itself. */
  font-feature-settings: 'cv11';
  font-kerning: normal;
}

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

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

img, svg {
  display: block;
  max-width: 100%;
}

table {
  border-collapse: collapse;
}

/* ============================================================
   BACKGROUND SURFACE — schematic grid
   Faint tactical-display grid fixed to the viewport, masked
   to fade from top-center outward. Sits behind all content.
   ============================================================ */

body::before {
  content: '';
  position: fixed;
  inset: 0;
  z-index: -1;
  pointer-events: none;
  background-image:
    linear-gradient(var(--accent-subtle) 1px, transparent 1px),
    linear-gradient(90deg, var(--accent-subtle) 1px, transparent 1px);
  background-size: 48px 48px;
  -webkit-mask-image: radial-gradient(ellipse at top, black 0%, transparent 70%);
          mask-image: radial-gradient(ellipse at top, black 0%, transparent 70%);
}

/* ============================================================
   LAYOUT
   ============================================================ */

.container {
  width: 100%;
  max-width: 1440px;
  margin-inline: auto;
  padding-inline: 32px;
}

main {
  flex: 1;
}

.content-area {
  padding: 32px 80px 48px;
}

/* ============================================================
   SITE HEADER (matches Pencil NavBar component)
   ============================================================ */

.site-header {
  background-color: var(--surface);
  position: sticky;
  top: 0;
  z-index: 100;
}

.site-nav {
  display: flex;
  align-items: center;
  justify-content: space-between;
  height: 56px;
  padding: 0 32px;
}

.nav-left { display: flex; align-items: center; }

.site-logo {
  display: flex;
  align-items: center;
  font-family: var(--font-display);
  font-size: var(--text-lg);
  font-weight: 700;
  letter-spacing: -0.01em;
  color: var(--text-primary);
  transition: color 0.15s ease;
}

.site-logo:hover {
  color: var(--accent);
}

.nav-logo-img {
  height: 36px;
  width: auto;
  transition: opacity 0.15s ease;
}

.site-logo:hover .nav-logo-img { opacity: 0.85; }

.nav-links {
  display: flex;
  align-items: center;
  gap: var(--space-lg);
}

.nav-link {
  font-family: var(--font-body);
  font-size: var(--text-sm);
  font-weight: 500;
  color: var(--text-secondary);
  transition: color 0.15s ease;
}

.nav-link:hover {
  color: var(--text-primary);
}

.nav-link--active {
  color: var(--accent);
  font-weight: 600;
}

/* ============================================================
   SITE FOOTER
   ============================================================ */

.site-footer {
  background-color: var(--surface);
  border-top: 1px solid var(--border);
  padding-block: var(--space-lg);
  margin-top: auto;
}

.footer-text {
  font-size: var(--text-sm);
  color: var(--text-tertiary);
  text-align: center;
  line-height: var(--leading-relaxed);
}

.footer-link {
  color: var(--text-secondary);
  text-decoration: underline;
  text-underline-offset: 2px;
}

.footer-link:hover {
  color: var(--text-primary);
}

/* ============================================================
   TYPOGRAPHY
   ============================================================ */

.heading-xl {
  font-family: var(--font-display);
  font-size: var(--text-3xl);
  font-weight: 700;
  color: var(--text-primary);
  line-height: var(--leading-snug);
  letter-spacing: -0.01em;
}

.heading-lg {
  font-family: var(--font-display);
  font-size: var(--text-xl);
  font-weight: 600;
  color: var(--text-primary);
  line-height: var(--leading-snug);
}

.label {
  font-family: var(--font-body);
  font-size: var(--text-2xs);
  font-weight: 600;
  color: var(--text-tertiary);
  letter-spacing: var(--tracking-wider);
  text-transform: uppercase;
}

/* ============================================================
   DATA TYPOGRAPHY
   ============================================================ */

.data {
  font-family: var(--font-data);
  font-size: var(--text-sm);
  font-weight: 400;
  color: var(--text-primary);
}

.data-lg {
  font-family: var(--font-data);
  font-size: var(--text-md);
  font-weight: 500;
  color: var(--text-primary);
}

/* ============================================================
   RATING NUMBERS
   Glow ONLY on hero and table rating — no glow elsewhere.
   ============================================================ */

.rating-number {
  font-family: var(--font-data);
  font-weight: 700;
  color: var(--accent);
  line-height: var(--leading-tight);
}

/* Hero: large glow (profile page, featured display) */
.rating-number--hero {
  font-size: var(--text-7xl);  /* 48px */
  text-shadow: 0 0 12px var(--accent-glow);
}

/* Table: subtle glow in the ratings column */
.rating-number--table {
  font-size: var(--text-sm);
  text-shadow: 0 0 8px var(--accent-glow);
}

/* ============================================================
   RATING CHANGE
   ============================================================ */

.rating-change {
  font-family: var(--font-data);
  font-size: var(--text-sm);
  font-weight: 500;
}

.rating-change--positive {
  color: var(--positive);
}

.rating-change--negative {
  color: var(--negative);
}

/* ============================================================
   RATING DEVIATION
   ============================================================ */

.rd {
  font-family: var(--font-data);
  font-size: var(--text-2xs);
  color: var(--text-tertiary);
}

/* ============================================================
   HERO SECTION (Rankings page top)
   ============================================================ */

/* Vertical stack: logo anchors top, title + subtitle as a lockup below,
   stats strip at the bottom. Three clear tiers with generous gap between
   them (--space-xl = 32px) so the hero reads as a composition, not a
   single crowded row. */
.hero-section {
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: var(--space-xl);
  padding: 48px 80px 40px;
}

/* Bottom divider inset to match the content-area's horizontal padding,
   so the line aligns with the visible edges of the table rows below
   instead of running edge-to-edge of the hero's own 1280px box. Media
   queries below track the responsive padding changes. */
.hero-section::after {
  content: '';
  position: absolute;
  left: 80px;
  right: 80px;
  bottom: 0;
  height: 1px;
  background: var(--border);
}

.hero-lockup {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 8px;
  min-width: 0;
  text-align: center;
}

.hero-logo {
  height: 128px;
  width: auto;
  flex-shrink: 0;
  display: block;
  filter: drop-shadow(0 0 20px var(--accent-soft));
}

.hero-title {
  font-family: var(--font-display);
  font-size: var(--text-5xl);
  font-weight: 700;
  letter-spacing: -0.03em;
  color: var(--text-primary);
  margin: 0;
  line-height: var(--leading-tight);
}

.hero-sub {
  font-family: var(--font-body);
  font-size: var(--text-base);
  font-weight: 400;
  color: var(--text-secondary);
  margin: 0;
  letter-spacing: 0.005em;
  line-height: var(--leading-normal);
}

.hero-sub strong {
  color: var(--accent);
  font-weight: 600;
}

/* Global stats banner at the top of the rankings page — three big numbers
   (Rated Players / Tournaments Tracked / Matches Analyzed). Distinct
   namespace from the profile's .hero-stat tree below, which was being
   silently shadowed by the later-declared profile rule. */
/* Force the stats strip onto its own row beneath the logo + title lockup,
   centered. Flex-basis: 100% flips it into a second flex line so it no
   longer competes with the hero title horizontally. Smaller numbers match
   the demoted position — still readable, but no longer shouting over the
   title. */
.global-stats {
  display: flex;
  align-items: stretch;
  justify-content: center;
  gap: 28px;
  flex-basis: 100%;
}

.global-stat {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 2px;
  min-width: 88px;
}

.global-stat__value {
  font-family: var(--font-data);
  font-size: var(--text-xl);
  font-weight: 700;
  color: var(--text-primary);
  letter-spacing: var(--tracking-tight);
  line-height: var(--leading-tight);
  font-variant-numeric: tabular-nums;
}

.global-stat__label {
  font-family: var(--font-body);
  font-size: var(--text-2xs);
  font-weight: 500;
  color: var(--text-tertiary);
  text-transform: uppercase;
  letter-spacing: var(--tracking-wide);
}

.hero-divider {
  width: 1px;
  align-self: stretch;
  background: var(--border);
}

/* ============================================================
   TOURNAMENTS INDEX — layout rhythm
   Scoped overrides to cap content width, restore hero hierarchy,
   and stack filter concerns vertically so the page has a proper
   heading / controls / table / footer rhythm instead of a single
   undifferentiated slab of rows.
   ============================================================ */

/* Cap the table column at a comfortable reading width on 1440+ screens.
   Below 1280px the rule is a no-op (max-width kicks in only above the
   cap); above it, the row text stops running the full monitor width. */
.tournaments-index {
  max-width: 1280px;
  margin-inline: auto;
}

/* Hero strip: the title earns real display-size weight; the count
   sits immediately next to it like a dateline ("Tournaments · 799
   rated events"), not stretched to the far edge where it floats
   disconnected. The shared .section-header default is space-between
   (leaderboard pairs title with a right-aligned control); on this
   page the count is a supporting stat for the title, so flush-start. */
.tournaments-index .section-header {
  justify-content: flex-start;
  align-items: baseline;
  gap: var(--space-md);
  margin-top: var(--space-xl);
  margin-bottom: var(--space-xl);
}

.section-header__title {
  font-family: var(--font-display);
  font-size: var(--text-3xl);
  font-weight: 700;
  color: var(--text-primary);
  line-height: var(--leading-tight);
  letter-spacing: -0.01em;
}

.section-header__meta {
  margin: 0;
  font-family: var(--font-body);
  font-size: var(--text-sm);
  color: var(--text-tertiary);
}

.section-header__count {
  font-family: var(--font-data);
  font-size: var(--text-base);
  font-weight: 600;
  color: var(--text-secondary);
  letter-spacing: 0.01em;
}

/* (filtered) is a link to clear every filter in one shot. The dotted
   underline appears only on hover so the default state reads as a quiet
   status marker, not a blue web link. Chrome becomes functionality. */
.section-header__filtered {
  font-family: var(--font-data);
  font-size: var(--text-2xs);
  color: var(--accent);
  text-transform: uppercase;
  letter-spacing: var(--tracking-wider);
  margin-left: var(--space-2xs);
  text-decoration: none;
  border-bottom: 1px dotted transparent;
  transition: border-color 150ms ease;
}

a.section-header__filtered:hover,
a.section-header__filtered:focus-visible {
  border-bottom-color: color-mix(in oklch, var(--accent) 60%, transparent);
  outline: none;
}

/* Search input wrap — holds the field and the `/` keyboard-shortcut
   badge. Position: relative so the badge can absolute-position at the
   right edge without knocking the input's own padding. The badge stays
   out of the way of typed input via right-side padding on the input. */
.search-input-wrap {
  position: relative;
  width: 100%;
  max-width: 480px;
}

.search-input-wrap .search-input {
  width: 100%;
  padding-right: 36px;
}

.search-input__hint {
  position: absolute;
  top: 50%;
  right: 10px;
  transform: translateY(-50%);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 20px;
  height: 20px;
  padding: 0 6px;
  font-family: var(--font-data);
  font-size: var(--text-2xs);
  font-weight: 600;
  color: var(--text-tertiary);
  background: color-mix(in oklch, var(--border) 60%, transparent);
  border: 1px solid var(--border);
  border-radius: 4px;
  pointer-events: none;
  opacity: 0.8;
  transition: opacity 150ms ease, color 150ms ease;
}

.search-input-wrap:focus-within .search-input__hint {
  opacity: 0;
}

/* Hide the hint on small screens — mobile users don't have a keyboard
   and the badge would just eat input padding. */
@media (max-width: 640px) {
  .search-input__hint { display: none; }
  .search-input-wrap .search-input { padding-right: 16px; }
}


/* Filter stack — search + two labeled groups stacked vertically.
   Related rows sit 12px apart; the whole stack separates from the
   table below by a generous 32px so the table reads as a distinct
   block with real whitespace above and below it. */
.filter-stack {
  display: flex;
  flex-direction: column;
  gap: var(--space-sm);
  margin-bottom: var(--space-xl);
}

.filter-stack__search .search-input {
  width: 100%;
  max-width: 480px;
}

.filter-stack__group {
  display: flex;
  align-items: center;
  gap: var(--space-md);
}

.filter-stack__label {
  font-family: var(--font-data);
  font-size: var(--text-2xs);
  font-weight: 600;
  color: var(--text-tertiary);
  text-transform: uppercase;
  letter-spacing: var(--tracking-wider);
  min-width: 56px;
}

/* Row height matches DESIGN.md spec for TournamentRow (52px).
   The shared .rank-row base is 56px for the leaderboard where the
   rating number wants more presence; tournaments are a denser list
   and earn the 4px back. Also adds an inter-column gap so adjacent
   cells (Players/Winner, Winner/Date, Tier/Format) don't collide —
   numeric and text values had been butting up against each other
   at the cell boundary with no visual breath. Header matches so
   column labels stay perfectly aligned with row cells. Mobile
   override below restores the tighter 2-line card gaps so the
   compact card layout keeps its density. */
.tournaments-index .tournament-row,
.tournaments-index .table-header {
  gap: var(--space-md);
}

.tournaments-index .tournament-row {
  height: 52px;
}

/* Mobile: release the fixed 52px desktop height so the 2-3 line wrap
   card can grow to fit its content. The base .tournament-row mobile
   rule (below, in the main responsive block) sets height: auto too,
   but this scoped selector has higher specificity and wins — without
   this override, long winner usernames overflow the row and collide
   with the next card's title. Gaps pulled in here so the whole mobile
   behavior for tournaments-index is visible in one place. */
@media (max-width: 640px) {
  .tournaments-index .tournament-row {
    height: auto;
    row-gap: 6px;
    column-gap: 10px;
  }
}

/* Pagination breathing room — reads as a page footer, not as row 51. */
.tournaments-index .pagination {
  padding-top: var(--space-xl);
  margin-top: var(--space-sm);
}

/* Small-screen stack: the filter-stack__group wraps its label above
   the chip row so chips still get a full horizontal scroll lane.
   Mobile table-header sticky offset is handled elsewhere. */
@media (max-width: 640px) {
  .filter-stack__group {
    flex-direction: column;
    align-items: flex-start;
    gap: var(--space-2xs);
  }
  .tournaments-index .section-header {
    flex-direction: column;
    align-items: flex-start;
    gap: var(--space-2xs);
    margin-top: var(--space-lg);
    margin-bottom: var(--space-lg);
  }
  .section-header__title {
    font-size: var(--text-2xl);
  }
}

/* ============================================================
   FILTER ROW (search + chips) — legacy, still used elsewhere
   ============================================================ */

.filter-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  margin-bottom: 20px;
}

.filter-row .search-input {
  width: 480px;
  max-width: 480px;
}

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

.chip {
  display: inline-flex;
  align-items: center;
  padding: 6px 16px;
  border-radius: 6px;
  border: 1px solid var(--border);
  background-color: var(--card);
  font-family: var(--font-body);
  font-size: var(--text-sm);
  font-weight: 500;
  color: var(--text-secondary);
  cursor: pointer;
  transition: all 0.15s ease;
  white-space: nowrap;
}

.chip:hover {
  border-color: var(--accent);
  color: var(--text-primary);
}

/* Tactile press — chip scales down a hair on click-and-hold and springs
   back. 60ms is under the perceptual threshold of "laggy" but long enough
   to feel the click. Respects prefers-reduced-motion. */
.chip:active {
  transform: scale(0.96);
  transition: transform 0.06s ease;
}

@media (prefers-reduced-motion: reduce) {
  .chip:active { transform: none; }
}

.chip--active {
  border-color: var(--accent);
  background-color: var(--accent);
  color: var(--bg);
  font-weight: 600;
}

/* Tier-colored chip variants. Inactive state uses the tier hue for text +
   hover border; active state fills with the tier color (matching the same
   tier's badge on the tournament rows below). */
.chip--tier-planetary { color: var(--tier-planetary); }
.chip--tier-planetary:hover { border-color: var(--tier-planetary); color: var(--tier-planetary); }
.chip--tier-planetary.chip--active { background-color: var(--tier-planetary); border-color: var(--tier-planetary); color: var(--bg); }

.chip--tier-sector { color: var(--tier-sector); }
.chip--tier-sector:hover { border-color: var(--tier-sector); color: var(--tier-sector); }
.chip--tier-sector.chip--active { background-color: var(--tier-sector); border-color: var(--tier-sector); color: var(--bg); }

.chip--tier-regional { color: var(--tier-regional); }
.chip--tier-regional:hover { border-color: var(--tier-regional); color: var(--tier-regional); }
.chip--tier-regional.chip--active { background-color: var(--tier-regional); border-color: var(--tier-regional); color: var(--bg); }

.chip--tier-galactic { color: var(--tier-galactic); }
.chip--tier-galactic:hover { border-color: var(--tier-galactic); color: var(--tier-galactic); }
.chip--tier-galactic.chip--active { background-color: var(--tier-galactic); border-color: var(--tier-galactic); color: var(--bg); }

/* Format chips are intentionally neutral — see leaderboards/show.html.erb
   for the rationale. The .chip--fmt-* color variants and the --fmt-*
   tokens were removed: tier chips do the colored-chip work, and a
   single-color-per-row policy keeps the page calm. */

/* ============================================================
   SEARCH INPUT
   ============================================================ */

.search-input {
  display: block;
  padding: 0 16px;
  background-color: var(--input);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  font-family: var(--font-body);
  font-size: var(--text-md);
  color: var(--text-primary);
  outline: none;
  transition: border-color 0.15s ease;
  height: 44px;
}

.search-input::placeholder { color: var(--text-tertiary); }
.search-input:focus { border-color: var(--accent); }

/* ============================================================
   TABLE CONTAINER (flex-based rows, not <table>)
   Matches Pencil RankRow / TournRow components
   ============================================================ */

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

.table-header {
  display: flex;
  align-items: center;
  /* Column breathing room. Data columns (Rating, Record, Winrate, Events,
     Confidence) sit flush against each other without this — the right edge
     of the table reads as one cramped data block. 16px gives each metric
     its own visual cell while staying tight enough for scanning down a
     column. The same gap is set on .rank-row below so headers and data
     align column-for-column. */
  gap: 16px;
  height: 40px;
  padding: 0 20px;
  font-family: var(--font-data);
  font-size: var(--text-2xs);
  font-weight: 500;
  color: var(--text-tertiary);
  letter-spacing: 0.5px;
  text-transform: uppercase;
  /* Sticks directly below the fixed site nav (56px desktop, 52px mobile) so
     column labels survive scrolls past rank ~15. Background matches --bg so
     the header doesn't render transparent when the sticky kicks in. Mobile
     top offset is handled in the <=640px breakpoint below. */
  position: sticky;
  top: 56px;
  z-index: 10;
  background: var(--bg);
}

@media (max-width: 640px) {
  .table-header { top: 52px; }
}

/* Profile pages already have their own sticky header (.profile-sticky) in
   the same vertical slot — stacking another sticky layer under it just
   obscures the column labels. Drop the sticky behavior here; profile
   tables are paginated to 10 rows so the column labels are rarely
   scrolled out of view anyway. */
.profile-page .table-header {
  position: static;
  top: auto;
}

/* Content-width cap — same 1280px envelope as .tournaments-index and
   .tournament-show. Keeps every top-level user-facing view optically
   aligned on wide monitors: rankings, profile, per-event audit, and both
   tournament pages all center on the same vertical column. Below 1280px
   the rule is a no-op (max-width only kicks in when the viewport is
   wider). Tables inside (all on the shared .table-container primitive)
   inherit the constraint without any per-component width changes.

   The leaderboard's .hero-section sits OUTSIDE .content-area in the DOM,
   so it gets its own matching cap — otherwise the banner would run
   full-bleed while the filter/table below it sat in the middle, with
   mismatched left edges. Capping it here keeps the whole page on one
   centerline. */
.leaderboards-show,
.profile-page,
.player-audit,
.player-vs {
  max-width: 1280px;
  margin-inline: auto;
}

.hero-section {
  max-width: 1280px;
  margin-inline: auto;
}

/* Rank row (leaderboard) */
.rank-row {
  display: flex;
  align-items: center;
  /* Match the header's column gap so cells line up vertically — see
     .table-header for the rationale. */
  gap: 16px;
  height: 56px;
  padding: 0 20px;
  background: var(--card);
  border-radius: 6px;
  text-decoration: none;
  transition: background 0.1s ease;
  color: inherit;
}

.rank-row:hover { background: var(--card-hover); }

.rank-row--empty {
  flex-direction: column;
  justify-content: center;
  align-items: center;
  gap: 4px;
  height: auto;
  min-height: 72px;
  padding: 16px 20px;
  color: var(--text-tertiary);
  font-size: var(--text-sm);
  text-align: center;
}

.rank-row--empty:hover { background: var(--card); }

.rank-row--empty__headline {
  margin: 0;
  color: var(--text-secondary);
  font-weight: 500;
}

.rank-row--empty__recovery {
  margin: 0;
  font-size: var(--text-xs);
  color: var(--text-tertiary);
}

.rank-row--empty__link {
  color: var(--accent);
  text-decoration: none;
  border-bottom: 1px dotted color-mix(in oklch, var(--accent) 40%, transparent);
  transition: border-color 150ms ease;
}

.rank-row--empty__link:hover,
.rank-row--empty__link:focus-visible {
  border-bottom-color: var(--accent);
  outline: none;
}

/* On desktop, .rank-meta and .tourn-meta are invisible to layout — their
   children participate in the row's flex container directly, matching the
   table-header column widths. On mobile they collapse into a second row. */
.rank-meta, .tourn-meta { display: contents; }

/* Tournament row (profile) */
.tourn-row {
  display: flex;
  align-items: center;
  height: 52px;
  padding: 0 20px;
  background: var(--card);
  border-radius: 6px;
}

/* Column widths — rank table.
   Rank column sized to fit 5 digits at 16px Geist Mono Bold (~48px content)
   plus right-side breathing room before the player name starts. Right-aligned
   so numbers stay visually flush as digit count changes. */
/* Every numeric column right-aligns on desktop so digits line up vertically
   down the table. tabular-nums keeps same-digit counts at the same pixel
   width — critical for the leaderboard where small differences between
   rating numbers should read at a glance. */
.th-rank, .rank-num    { width: 72px; padding-right: 16px; text-align: right; font-variant-numeric: tabular-nums; }
/* Player flexes but caps at 360px so it doesn't gobble all the slack on a
   1280px viewport (handles + usernames rarely exceed 40 chars). The leftover
   space is absorbed by margin-left: auto on .th-rating / .rank-rating below,
   which keeps the data block anchored to the row's right edge instead of
   stranding empty space mid-row. */
.th-player, .rank-player { flex: 1 1 0; min-width: 0; max-width: 360px; }
.th-rating, .rank-rating { width: 80px; margin-left: auto; text-align: right; font-variant-numeric: tabular-nums; }
/* W-L is centered because the hyphen is the natural visual anchor for both
   the header label ("W-L") and the data ("12-5", "100-50") — the digit count
   varies but the dash sits roughly mid-cell either way. Right-align gave the
   cell a hard right edge that visually merged into the next column. */
.th-record, .rank-record { width: 70px; text-align: center; font-variant-numeric: tabular-nums; }
.th-winrate, .rank-winrate { width: 60px; text-align: right; font-variant-numeric: tabular-nums; }
.th-events, .rank-events { width: 60px; text-align: right; font-variant-numeric: tabular-nums; }
.th-confidence, .rank-confidence { width: 90px; text-align: right; font-variant-numeric: tabular-nums; }

/* Sortable header links. Column widths above still govern layout — these
   rules add interactivity (hover, active state, sort indicator) without
   changing dimensions. Headers render as <a> so every sort is bookmarkable,
   keyboard-focusable, and announced as a column header by screen readers. */
.th-link {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  color: inherit;
  text-decoration: none;
  cursor: pointer;
  transition: color 120ms ease;
}
.th-link .th-link__label { display: inline-block; }
/* Indicator is always rendered so the column width doesn't shift when the
   active column changes. Empty text + reserved width keeps alignment. */
.th-link .th-link__indicator {
  display: inline-block;
  min-width: 10px;
  font-size: var(--text-nano);
  line-height: var(--leading-tight);
  color: var(--accent);
  opacity: 0;
  transition: opacity 120ms ease;
}
.th-link--active .th-link__indicator { opacity: 1; }
.th-link:hover,
.th-link:focus-visible { color: var(--text-primary); }
.th-link--active { color: var(--text-primary); }
.th-link:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; border-radius: 2px; }
/* Right-aligned numeric columns put the indicator to the LEFT of the label
   because the column itself is right-aligned — keeps the number/label
   visually flush with the row's number beneath it. */
.th-link.th-rating,
.th-link.th-winrate,
.th-link.th-events,
.th-link.th-confidence { justify-content: flex-end; flex-direction: row-reverse; }
/* Record (W-L) is centered, so indicator sits to the RIGHT of the label
   (natural reading order). Centering the flex container keeps "W-L ▼" as
   one balanced lockup over the centered "12-5" data below. */
.th-link.th-record { justify-content: center; flex-direction: row; }
/* Rank column is right-aligned like the numeric columns — the existing
   16px right-padding on .th-rank still applies; this only reverses the
   flex so the ▲/▼ sits to the LEFT of "Rank". */
.th-link.th-rank { justify-content: flex-end; flex-direction: row-reverse; }

/* Column widths — tournament table */
.th-event, .tourn-event   { flex: 1; }
.th-tier, .tourn-tier     { width: 100px; text-align: center; }
.th-delta, .tourn-delta   { width: 80px; text-align: right; }
.th-date, .tourn-date     { width: 90px; text-align: right; }
.tourn-record             { width: 80px; text-align: center; }
.tourn-rating             { width: 80px; text-align: right; }

/* Global tournaments index columns — reuses .rank-row for the row chrome.
   Winner cell is left-aligned so the gold ★ markers stack into a clean
   podium line down the column instead of drifting around a center point
   as usernames change length. Tier/Format stay center-aligned because
   the badge and tag read as centered chips; Players/Date stay right
   because they're numeric/temporal. */
.th-format, .tourn-format   { width: 80px; text-align: center; font-family: var(--font-data); font-size: var(--text-2xs); color: var(--text-tertiary); text-transform: uppercase; letter-spacing: 0.4px; }
.th-players, .tourn-players { width: 72px; text-align: right; font-family: var(--font-data); font-size: var(--text-sm); color: var(--text-secondary); }
/* Override: the header span inherits .th-players above for layout but
   the brighter text-secondary made it the loudest header on the row,
   competing with the active sort indicator. Header cells across the
   table all use text-tertiary; force this one back into line. */
.table-header .th-players { color: var(--text-tertiary); font-size: var(--text-2xs); }
.th-winner, .tourn-winner   { width: 140px; text-align: left; }

/* Winner cell repeats on every row — glow here would debase the "accent glow
   means rating hero / #1 rank / Galactic tier badge" signal (see DESIGN.md
   glow-restraint principle). Color alone carries the link affordance; row
   hover raises the row background for the interactive feedback. A dotted
   underline fills in on precision-hover, matching the inline-link idiom
   used by .rank-row--empty__link — rewards the user who aimed at the name. */
.tourn-winner__name {
  font-family: var(--font-body);
  font-size: var(--text-sm);
  font-weight: 600;
  color: var(--accent);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  display: inline-block;
  max-width: calc(100% - 20px);
  vertical-align: middle;
  border-bottom: 1px dotted transparent;
  transition: border-color 200ms ease, color 150ms ease;
}

.tourn-winner__name:hover,
.tourn-winner__name:focus-visible {
  border-bottom-color: color-mix(in oklch, var(--accent) 60%, transparent);
  outline: none;
}

/* Gold star before the winner name. Scope is the single event, so the
   winner IS the #1 of that event — same rank-gold token used for the
   overall leaderboard's gold medal. One glyph, screen-reader hidden,
   so the density stays tight and the cell still reads as a player link. */
.tourn-winner__medal {
  display: inline-block;
  margin-right: 5px;
  font-size: var(--text-2xs);
  color: var(--rank-gold);
  vertical-align: baseline;
  line-height: var(--leading-tight);
  pointer-events: none;
  user-select: none;
}

.tourn-winner__empty {
  font-family: var(--font-data);
  font-size: var(--text-sm);
  color: var(--text-tertiary);
}

/* Stretched-link pattern: the tournament name is the primary link for the
   row, but its ::before overlay covers the whole card so clicking any
   empty cell still navigates. Other row links (like the winner name) use
   .stretched-link-escape to sit above the overlay and keep their own href. */
.tournament-row {
  position: relative;
  cursor: pointer;
}

.tournament-row .stretched-link::before {
  content: '';
  position: absolute;
  inset: 0;
  z-index: 0;
  pointer-events: auto;
}

.tournament-row .stretched-link-escape {
  position: relative;
  z-index: 1;
}

.tournament-row .tourn-event-name {
  font-family: var(--font-body);
  font-size: var(--text-base);
  font-weight: 500;
  color: var(--text-primary);
}

/* Rank number medals */
.rank-num {
  font-family: var(--font-data);
  font-size: var(--text-md);
  font-weight: 700;
  color: var(--text-secondary);
}
/* Podium rank numbers in the leaderboard table.
   #1 promotes to the site accent + a soft glow so it reads as "champion"
   coherently with the new profile rank-badge chip and the
   .rank-rating__number--champion treatment one column over. Amber-gold
   was retired here for the same reason as the profile chip: it doubled
   the Sector tier hue and competed with the sky-blue rating sitting
   immediately to its right. Silver and bronze keep their metal tones
   as a quieter podium tier — only #1 earns the glow.
   The --rank-gold token itself stays available (still used elsewhere
   for tier dots and the audit accent), only this rank number moved. */
.rank--gold   {
  color: var(--accent);
  text-shadow: 0 0 10px color-mix(in oklch, var(--accent) 50%, transparent);
}
.rank--silver { color: var(--rank-silver); }
.rank--bronze { color: var(--rank-bronze); }

/* Player cell with avatar */
.rank-player {
  display: flex;
  align-items: center;
  gap: 12px;
}

.player-avatar {
  width: 34px;
  height: 34px;
  border-radius: 17px;
  background: var(--border);
  flex-shrink: 0;
}

.player-name {
  font-family: var(--font-body);
  font-size: var(--text-base);
  font-weight: 600;
  color: var(--text-primary);
}

.player-username {
  font-size: var(--text-2xs);
  font-weight: 400;
  color: var(--text-tertiary);
  margin-left: 4px;
}

/* Rating column */
.rank-rating {
  font-family: var(--font-data);
  font-size: var(--text-sm);
  font-weight: 700;
  color: var(--accent);
}

/* Record, win rate, events */
.rank-record, .tourn-record {
  font-family: var(--font-data);
  font-size: var(--text-sm);
  color: var(--text-secondary);
}

.rank-winrate {
  font-family: var(--font-data);
  font-size: var(--text-sm);
}

/* Win-rate sign tints — pulled out of an inline style so the view stays
   CSP-friendly and the color semantic lives with the rest of the type.
   The neutral variant renders on zero-match ranked players (rare data
   edge) where "0.0%" in red would falsely imply a losing record. */
.rank-winrate--positive { color: var(--positive); }
.rank-winrate--negative { color: var(--negative); }
.rank-winrate--neutral  { color: var(--text-tertiary); }

/* Sparkbars on the Win% and Confidence columns — thin 2px bar at the
   bottom of each cell, width scaled by the row's --magnitude custom
   property. Same data-density pattern as the tournament-show rating
   shift table: a visual layer on top of the number so the whole column
   is scannable at a glance. Gradient fades from transparent to the
   full signal color, reading as "lit" from the right edge. */
.rank-winrate,
.rank-confidence {
  position: relative;
  overflow: hidden;
}

.rank-winrate::after,
.rank-confidence::after {
  content: '';
  position: absolute;
  right: 0;
  bottom: 0;
  height: 2px;
  width: calc(var(--magnitude, 0) * 100%);
  border-radius: 1px;
  pointer-events: none;
}

.rank-winrate--positive::after {
  background: linear-gradient(to left,
    rgba(34, 197, 94, 0.85),
    rgba(34, 197, 94, 0.15));
}

.rank-winrate--negative::after {
  background: linear-gradient(to left,
    rgba(239, 68, 68, 0.85),
    rgba(239, 68, 68, 0.15));
}

.rank-confidence--established::after {
  background: linear-gradient(to left,
    rgba(34, 197, 94, 0.75),
    rgba(34, 197, 94, 0.15));
}

.rank-confidence--developing::after {
  background: linear-gradient(to left,
    rgba(245, 158, 11, 0.75),
    rgba(245, 158, 11, 0.15));
}

.rank-confidence--provisional::after {
  background: linear-gradient(to left,
    rgba(148, 163, 184, 0.5),
    rgba(148, 163, 184, 0.1));
}

.rank-events {
  font-family: var(--font-data);
  font-size: var(--text-sm);
  color: var(--text-secondary);
}


/* Confidence badge */
.confidence-badge {
  font-family: var(--font-data);
  font-size: var(--text-sm);
  color: var(--text-secondary);
  cursor: help;
  border-bottom: 1px dotted var(--text-tertiary);
}

/* Sortable column header on the tournaments index (Date). Inherits the
   table-header typography so it reads as part of the row; the arrow is
   the only visual signal that the column is interactive.
   Arrow placement matches the leaderboard convention: arrow LEFT of the
   label for right-aligned columns, so "↓ Date" reads as one lockup
   anchored to the right edge of the column above the right-aligned date
   values below. (Was "Date ↓" — header drifted off-axis from data.) */
.th-date__sort {
  color: inherit;
  text-decoration: none;
  display: inline-flex;
  align-items: baseline;
  gap: 4px;
  flex-direction: row-reverse;
}

.th-date__sort:hover .th-date__arrow,
.th-date__sort:focus-visible .th-date__arrow {
  color: var(--accent);
}

.th-date__sort:focus-visible {
  outline: 1px solid color-mix(in oklch, var(--accent) 50%, transparent);
  outline-offset: 2px;
  border-radius: 3px;
}

.th-date__arrow {
  color: var(--text-tertiary);
  font-size: var(--text-xs);
  transition: color 150ms ease;
}

/* Tournament row specifics */
.tourn-event-name {
  font-family: var(--font-body);
  font-size: var(--text-sm);
  font-weight: 600;
  color: var(--text-primary);
}

/* Tournament name in a collapsed history row, made into a link to the
   per-tournament rating-math audit. Inherits .tourn-event-name typography
   so the row looks visually unchanged at rest, then takes the accent
   color + dashed-bottom on hover and a focus ring on keyboard focus.
   Mirrors .h2h-row__name-link in shape so the two "click the entity name
   to drill in" affordances feel like the same idea. */
.tourn-event-name-link {
  text-decoration: none;
  color: inherit;
  border-bottom: 1px dashed transparent;
  transition: color 120ms ease, border-color 120ms ease;
}

.tourn-event-name-link:hover {
  color: var(--accent);
  border-bottom-color: var(--accent);
}

.tourn-event-name-link:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
  border-radius: 2px;
}

.tourn-event-external {
  font-family: var(--font-data);
  font-size: var(--text-micro);
  color: var(--text-tertiary);
  text-decoration: none;
  margin-left: 8px;
  letter-spacing: 0.4px;
  text-transform: uppercase;
  transition: color 150ms ease;
  white-space: nowrap;
}

/* Event location as a dim second line under the event name. Search matches
   location server-side, so surfacing it here is how the row tells the user
   "that 'Milwaukee' you typed? matched this." Inter body font keeps it as
   narrative context; Geist Mono would misread as a data field. */
.tourn-event__location {
  display: block;
  margin-top: 2px;
  font-family: var(--font-body);
  font-size: var(--text-xs);
  color: var(--text-tertiary);
  line-height: var(--leading-snug);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* Same location string in the tournament show-page hero meta strip —
   sits inline with tier/format/date/player-count. Uses the meta row's
   existing typography scale; no new tokens introduced. Mirrors the index
   ellipsis cap so an unusually long upstream location string can't blow
   out the meta row on mobile; full string still accessible via Melee. */
.tournament-hero__location {
  font-family: var(--font-body);
  font-size: var(--text-sm);
  color: var(--text-tertiary);
  max-width: 32ch;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.tourn-event-external:hover {
  color: var(--accent);
}

.tourn-event-external::after {
  content: " \2192";
}

/* On the index row, the "→" glyph is hidden at rest (50 rows × an arrow
   would read as chrome) and fades in only on hover or keyboard focus.
   The link still opens Melee in a new tab; the arrow just surfaces as
   an "external" affordance once the user's cursor declares intent. */
.tournament-row .tourn-event-external::after {
  opacity: 0;
  display: inline-block;
  transition: opacity 150ms ease;
}

.tournament-row .tourn-event-external:hover::after,
.tournament-row .tourn-event-external:focus-visible::after {
  opacity: 1;
}

/* Keyboard focus ring — the winner-name link next door gets implicit
   focus via the browser default; this link deserves the same visibility
   so keyboard users can see where Tab landed above the stretched-link
   overlay. Matches the focus treatment used on other accent links. */
.tournament-row .tourn-event-external:focus-visible {
  outline: 1px solid color-mix(in oklch, var(--accent) 60%, transparent);
  outline-offset: 2px;
  border-radius: 3px;
  color: var(--accent);
}

.tourn-date {
  font-family: var(--font-data);
  font-size: var(--text-2xs);
  color: var(--text-tertiary);
}

.format-label {
  font-family: var(--font-data);
  font-size: var(--text-2xs);
  color: var(--text-tertiary);
  margin-left: 4px;
}

.tourn-rating {
  font-family: var(--font-data);
  font-size: var(--text-sm);
  font-weight: 700;
  color: var(--accent);
}

/* ============================================================
   BADGES (tier indicators — matches Pencil Tag components)
   ============================================================ */

.badge {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  padding: 4px 10px;
  border-radius: 4px;
  font-family: var(--font-data);
  font-size: var(--text-2xs);
  font-weight: 600;
  white-space: nowrap;
  line-height: var(--leading-tight);
}

.badge--planetary      { color: var(--tier-planetary); background: rgba(34, 197, 94, 0.2); }
.badge--store_showdown { color: var(--text-secondary); background: rgba(148, 163, 184, 0.15); }
.badge--sector         { color: var(--tier-sector);    background: rgba(245, 158, 11, 0.2); }
.badge--regional       { color: var(--tier-regional);  background: var(--accent-mid); }
.badge--galactic       { color: var(--tier-galactic);  background: rgba(124, 58, 237, 0.2); }

/* ============================================================
   BREADCRUMB (profile page)
   ============================================================ */

.breadcrumb {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-bottom: 24px;
}

.breadcrumb-link {
  font-size: var(--text-sm);
  color: var(--accent);
}

.breadcrumb-sep {
  font-size: var(--text-sm);
  color: var(--text-tertiary);
}

.breadcrumb-current {
  font-size: var(--text-sm);
  color: var(--text-secondary);
}

/* ============================================================
   PROFILE NAME
   ============================================================ */

.profile-name {
  font-family: var(--font-display);
  font-size: clamp(32px, 3.5vw, 44px);
  font-weight: 800;
  line-height: var(--leading-tight);
  letter-spacing: -0.025em;
  color: var(--text-primary);
  margin: 0;
}

.profile-name a {
  color: inherit;
}

.profile-name a:hover {
  color: var(--accent);
}

.profile-username {
  font-family: var(--font-data);
  font-size: var(--text-lg);
  font-weight: 400;
  color: var(--text-tertiary);
  letter-spacing: 0;
}

.profile-meta {
  display: flex;
  align-items: center;
  gap: 12px;
}

.rank-badge {
  font-family: var(--font-data);
  font-size: var(--text-2xs);
  font-weight: 600;
  color: var(--text-secondary);
  background: var(--card);
  border: 1px solid var(--border);
  padding: 2px 8px;
  border-radius: 4px;
  letter-spacing: 0.02em;
}

.rank-badge--unranked {
  color: var(--text-tertiary);
}

/* Format qualifier appended to the rank (e.g. "Rank #1 Premier"). Slightly
   softer than the rank number itself so "#1" stays the headline and
   "Premier" reads as context. Uses currentColor inheritance so it tints
   through on gold/silver/bronze medal badges. */
.rank-badge__qualifier {
  font-weight: 400;
  opacity: 0.72;
}

/* Podium chips for ranks 1-3 on the player profile.
   #1 (gold class, accent treatment): the chip earns the site's primary
   accent so it reads with the rating number it celebrates instead of
   fighting it. Amber-gold was retired from this chip because it doubled
   the Sector tier hue and competed with the sky-blue rating in the same
   hero zone. The amber token (--rank-gold) is still available for tier
   dots and the leaderboard row number cell — only the badge moved.
   #2/#3 (silver/bronze) get the matching chip frame so the three ranks
   read as a coherent tier system, not a one-off accent + bare text.
   Only #1 earns motion and a leading rank glyph (DESIGN.md "earn every
   glow"). */
.rank-badge--gold {
  position: relative;
  color: var(--accent);
  background: color-mix(in oklch, var(--accent) 14%, transparent);
  border-color: color-mix(in oklch, var(--accent) 50%, var(--border));
  box-shadow: 0 0 12px color-mix(in oklch, var(--accent) 22%, transparent),
              inset 0 0 0 1px color-mix(in oklch, var(--accent) 14%, transparent);
  letter-spacing: 0.04em;
  animation:
    hri-rank-stamp 520ms cubic-bezier(0.34, 1.56, 0.64, 1) 80ms 1 both,
    hri-rank-pulse 3.5s ease-in-out 600ms infinite;
}

/* Geometric rank glyph — sci-fi command-UI tag, deliberately not a star
   or trophy emoji (DESIGN.md anti-reference: no Star Wars pastiche, no
   gaming chrome). The diamond is a neutral geometric mark that reads
   "tier" without genre baggage. Decorative; aria-hidden via pseudo. */
.rank-badge--gold::before {
  content: "◆";
  display: inline-block;
  margin-right: 6px;
  font-size: 0.82em;
  line-height: 1;
  transform: translateY(-1px);
  opacity: 0.9;
}

.rank-badge--silver {
  color: var(--rank-silver);
  background: color-mix(in oklch, var(--rank-silver) 8%, transparent);
  border-color: color-mix(in oklch, var(--rank-silver) 30%, var(--border));
}

.rank-badge--bronze {
  color: var(--rank-bronze);
  background: color-mix(in oklch, var(--rank-bronze) 10%, transparent);
  border-color: color-mix(in oklch, var(--rank-bronze) 34%, var(--border));
}

/* Stamp: one-shot bounce on first paint, like a service medal being
   pinned on. Reads as "you earned this" because it fires once and
   stops, never ambient. Opacity stays at 1 throughout so a screenshot
   or a slow render never catches the chip half-faded — the bounce IS
   the reveal. */
@keyframes hri-rank-stamp {
  0%   { transform: scale(0.92); }
  55%  { transform: scale(1.08); }
  100% { transform: scale(1); }
}

/* Ambient breathing glow on #1 only. Same cadence as the LIVE changelog
   tag (3.5s ease-in-out infinite) so the design system stays coherent
   across "this is alive" signals. */
@keyframes hri-rank-pulse {
  0%, 100% { box-shadow: 0 0 12px color-mix(in oklch, var(--accent) 18%, transparent),
                         inset 0 0 0 1px color-mix(in oklch, var(--accent) 12%, transparent); }
  50%      { box-shadow: 0 0 18px 1px color-mix(in oklch, var(--accent) 32%, transparent),
                         inset 0 0 0 1px color-mix(in oklch, var(--accent) 22%, transparent); }
}

@media (prefers-reduced-motion: reduce) {
  .rank-badge--gold { animation: none; }
}

/* Compact variant for the per-format ranks inside the hero stat strip.
   Sits as its own line below the ±RD note so the format's headline rating
   stays the dominant element, but the rank gets the same badge treatment
   as the Overall rank chip above the hero. */
.rank-badge--format {
  align-self: flex-start;
  margin-top: 4px;
  font-size: var(--text-micro);
  padding: 2px 6px;
}


/* ============================================================
   PROFILE HERO — name + rank + dominant rating + stat-strip,
   all in one card so a player's vitals read as a single unit.
   ============================================================ */

.profile-hero {
  display: grid;
  grid-template-columns: 1fr;
  gap: 18px;
  padding: 18px 22px 20px;
  margin-bottom: 14px;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 8px;
}

/* Anchor name + big rating number on a shared first baseline so the two
   hero elements read as one horizontal line. Subordinate info (rank-badge
   under name, caption + delta under number) stacks below each without
   competing with the headline row. */
.profile-hero__top {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 24px;
  flex-wrap: wrap;
}

.profile-hero__id {
  flex: 1 1 auto;
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: var(--space-sm);
}

.profile-hero__rank { display: inline-flex; }

.profile-hero__rating {
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  gap: var(--space-2xs);
  text-align: right;
  flex-shrink: 0;
}

/* Caption row — format label · ±CI · confidence %, all inline. Reads as
   a scoreboard subtitle under the big rating number rather than three
   separate stacked stats competing for vertical real estate. */
.profile-hero__caption {
  display: flex;
  align-items: center;
  gap: var(--space-2xs);
  flex-wrap: wrap;
  justify-content: flex-end;
  margin-top: var(--space-2xs);
  font-family: var(--font-data);
  font-size: var(--text-xs);
  color: var(--text-tertiary);
}

.profile-hero__caption-sep {
  color: var(--text-tertiary);
  opacity: 0.5;
}

.profile-hero__label {
  font-family: var(--font-data);
  font-size: var(--text-2xs);
  font-weight: 500;
  color: var(--text-tertiary);
  letter-spacing: var(--tracking-wider);
  text-transform: uppercase;
}

.profile-hero__number {
  font-family: var(--font-data);
  font-size: var(--text-7xl);
  font-weight: 700;
  color: var(--accent);
  line-height: var(--leading-tight);
  text-shadow: 0 0 16px var(--accent-glow);
  letter-spacing: var(--tracking-tight);
}

/* 95% credible interval bound range, sits below the hero number. Reads
   as a stat-line annotation: "95% CI 1,771 – 2,079". Mono numerals,
   muted color so it doesn't compete with the headline rating, but
   visible enough to communicate "the range is part of the rating, not
   an afterthought." */
.profile-hero__range {
  display: inline-flex;
  align-items: baseline;
  gap: 6px;
  margin-top: 6px;
  font-family: var(--font-data);
  font-size: var(--text-xs);
  color: var(--text-tertiary);
  align-self: flex-end;
  cursor: help;
}

.profile-hero__range-label {
  font-size: var(--text-micro);
  font-weight: 600;
  letter-spacing: var(--tracking-wider);
  text-transform: uppercase;
  color: var(--text-tertiary);
  opacity: 0.75;
}

.profile-hero__range-value {
  font-feature-settings: "tnum";
  color: var(--text-secondary);
}

.profile-hero__number--muted {
  font-family: var(--font-data);
  font-size: var(--text-5xl);
  color: var(--text-secondary);
  text-shadow: none;
  letter-spacing: normal;
}

.profile-hero__conf {
  font-family: var(--font-data);
  font-size: var(--text-xs);
  color: var(--text-tertiary);
}

.profile-hero__conf.confidence-badge {
  border-bottom: 1px dotted var(--text-tertiary);
}

/* Last-event pill. Reads like a stock ticker: sign + magnitude carries the
   whole message. Subordinate to the rating number but louder than the
   confidence % that sits above it — 13px vs 11px — because "what just moved"
   is the primary persona's job-to-be-done. Clicking deep-links to the
   per-tournament audit view. */
.profile-hero__delta {
  display: inline-flex;
  align-items: baseline;
  gap: 0;
  margin-top: 6px;
  font-family: var(--font-data);
  font-size: var(--text-sm);
  font-weight: 600;
  font-feature-settings: "tnum";
  line-height: var(--leading-tight);
  text-decoration: none;
  letter-spacing: 0.01em;
}

.profile-hero__delta:hover { filter: brightness(1.15); }

.profile-hero__delta--up    { color: var(--positive); }
.profile-hero__delta--down  { color: var(--negative); }
.profile-hero__delta--flat  { color: var(--text-tertiary); }

.profile-hero__delta-format {
  color: var(--text-secondary);
  font-weight: 500;
}

.profile-hero__delta-sep {
  color: var(--text-tertiary);
  font-weight: 400;
}

/* Context line — tournament name + date. Truncates long event names so the
   hero never wraps past two lines. The full name is available on hover via
   the link's title attribute. */
.profile-hero__delta-meta {
  display: block;
  max-width: 240px;
  margin-top: 2px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  font-family: var(--font-body);
  font-size: var(--text-2xs);
  color: var(--text-tertiary);
  text-align: right;
}

.profile-hero__delta-meta-link {
  color: var(--text-secondary);
  text-decoration: none;
}

.profile-hero__delta-meta-link:hover { color: var(--accent); }

/* Stat-strip lives inside the hero card under the big rating. Tabular
   mono numerals so columns of digits line up between players. Top divider
   reads as a horizontal rule between the hero and the stat row. */
.profile-hero__stats {
  display: grid;
  grid-template-columns: repeat(5, minmax(0, 1fr));
  gap: 12px 18px;
  padding-top: 14px;
  border-top: 1px solid var(--border);
}

.hero-stat {
  display: flex;
  flex-direction: column;
  gap: 2px;
  min-width: 0;
}

.hero-stat__label {
  font-family: var(--font-data);
  font-size: var(--text-micro);
  font-weight: 500;
  color: var(--text-tertiary);
  letter-spacing: var(--tracking-wide);
  text-transform: uppercase;
}

.hero-stat__value {
  font-family: var(--font-data);
  font-size: var(--text-base);
  font-weight: 600;
  color: var(--text-primary);
  font-feature-settings: "tnum";
  line-height: var(--leading-tight);
}

.hero-stat__value--muted {
  color: var(--text-tertiary);
  font-weight: 400;
}

.hero-stat__note {
  font-family: var(--font-data);
  font-size: var(--text-micro);
  color: var(--text-tertiary);
}

@media (max-width: 768px) {
  .profile-hero { padding: 14px 16px 16px; }
  .profile-hero__number { font-size: var(--text-6xl); }
  .profile-hero__stats { grid-template-columns: repeat(3, minmax(0, 1fr)); }
}

/* ============================================================
   PER-PLAYER PER-TOURNAMENT AUDIT HERO
   ============================================================ */

.audit-hero {
  display: flex;
  align-items: flex-end;
  justify-content: space-between;
  gap: 24px;
  padding-bottom: 20px;
  margin-bottom: 20px;
  border-bottom: 1px solid var(--border);
  flex-wrap: wrap;
}

.audit-hero__title { flex: 1; min-width: 0; }

.audit-hero__meta {
  display: flex;
  align-items: center;
  gap: 12px;
  flex-wrap: wrap;
  margin-top: 8px;
  font-family: var(--font-body);
  font-size: var(--text-sm);
  color: var(--text-tertiary);
}

.audit-hero__date { color: var(--text-secondary); }
.audit-hero__record .data,
.audit-hero__field .data { color: var(--text-primary); font-weight: 600; }

.audit-hero__delta {
  display: flex;
  align-items: center;
  gap: 16px;
  flex-shrink: 0;
}

.audit-hero__rating {
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  gap: 2px;
}

.audit-hero__label {
  font-family: var(--font-data);
  font-size: var(--text-micro);
  font-weight: 500;
  color: var(--text-tertiary);
  letter-spacing: var(--tracking-wider);
  text-transform: uppercase;
}

.audit-hero__number {
  font-family: var(--font-data);
  font-size: var(--text-4xl);
  font-weight: 700;
  color: var(--accent);
  line-height: var(--leading-tight);
  letter-spacing: var(--tracking-tight);
}

/* ============================================================
   METRIC STRIP — demoted format ratings + stats
   ============================================================ */

.metric-strip {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 12px 20px;
  margin-bottom: 32px;
  padding: 4px 0;
}

.metric-strip__item {
  display: flex;
  align-items: baseline;
  gap: 8px;
  white-space: nowrap;
}

.metric-strip__label {
  font-family: var(--font-data);
  font-size: var(--text-2xs);
  font-weight: 500;
  color: var(--text-tertiary);
  letter-spacing: var(--tracking-wider);
  text-transform: uppercase;
}

.metric-strip__value {
  font-family: var(--font-data);
  font-size: var(--text-md);
  font-weight: 600;
  color: var(--text-primary);
}

.metric-strip__value--warning { color: var(--warning); }

.metric-strip__note {
  font-family: var(--font-data);
  font-size: var(--text-2xs);
  color: var(--text-tertiary);
}

.metric-strip__sep {
  width: 1px;
  height: 16px;
  background: var(--border);
  align-self: center;
}

/* ============================================================
   SECTION HEADER
   ============================================================ */

.section-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin-bottom: 16px;
  margin-top: 32px;
}

.heading-lg {
  font-family: var(--font-display);
  font-size: var(--text-xl);
  font-weight: 600;
  color: var(--text-primary);
}

/* ============================================================
   PAGINATION
   ============================================================ */

.pagination {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  padding-top: 16px;
}

.page-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 36px;
  height: 36px;
  border-radius: 6px;
  background: var(--card);
  border: 1px solid var(--border);
  font-family: var(--font-data);
  font-size: var(--text-sm);
  color: var(--text-secondary);
  text-decoration: none;
  transition: all 0.15s ease;
}

.page-btn:hover {
  border-color: var(--accent);
  color: var(--text-primary);
}

.page-btn--active {
  background: var(--accent);
  border-color: var(--accent);
  color: var(--bg);
  font-weight: 600;
}

.page-dots {
  color: var(--text-tertiary);
  font-size: var(--text-sm);
}

/* ============================================================
   FOOTER NOTE
   ============================================================ */

.footer-note {
  font-family: var(--font-data);
  font-size: var(--text-2xs);
  color: var(--text-tertiary);
  text-align: center;
  padding-top: 12px;
  margin-top: 20px;
}

/* ============================================================
   ABOUT / HOW IT WORKS PAGE
   ============================================================ */

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

.about-title {
  font-family: var(--font-display);
  font-size: var(--text-3xl);
  font-weight: 700;
  color: var(--text-primary);
  /* Page title: generous bottom margin so the intro paragraph reads as a
     separate thought, not a descender on the title. 24px > line-height of
     title itself, which is the threshold where "single block" breaks into
     "title + intro." */
  margin-bottom: 24px;
  letter-spacing: -0.01em;
}

/* System Health callout — the clamp-rate receipt, pulled up to the top of
   the page so the system's self-check is visible before the reader commits
   to 2,400 words of methodology. Mono numerals, tertiary framing, one
   dominant percentage with a tooltip for the raw counts. */
.about-health {
  display: grid;
  grid-template-columns: auto auto 1fr;
  gap: 16px 20px;
  align-items: baseline;
  padding: 14px 18px;
  margin-bottom: 40px;
  background: color-mix(in oklch, var(--accent) 6%, var(--card));
  border: 1px solid color-mix(in oklch, var(--accent) 22%, var(--border));
  border-radius: 8px;
  max-width: 68ch;
}

.about-health__label {
  font-family: var(--font-data);
  font-size: var(--text-micro);
  color: var(--text-tertiary);
  text-transform: uppercase;
  letter-spacing: var(--tracking-wider);
}

.about-health__value {
  font-family: var(--font-data);
  font-size: var(--text-xl);
  font-weight: 700;
  color: var(--accent);
  font-feature-settings: "tnum";
  cursor: help;
}

.about-health__desc {
  font-size: var(--text-xs);
  color: var(--text-secondary);
  line-height: var(--leading-relaxed);
  grid-column: 1 / -1;
}

.about-health__desc strong { color: var(--text-primary); }

@media (max-width: 768px) {
  .about-health {
    grid-template-columns: auto 1fr;
  }
  .about-health__label { grid-column: 1; }
  .about-health__value { grid-column: 2; justify-self: end; }
}

/* Two-column layout on desktop: sticky TOC left, main content right. Mobile
   drops back to single column and the TOC is hidden — deep-linking from
   tooltips still works via the anchor IDs on each section. */
.about-layout {
  display: grid;
  grid-template-columns: 1fr;
  gap: 32px;
}

.about-content { min-width: 0; max-width: 800px; }

.about-toc { display: none; }

@media (min-width: 1024px) {
  .about-layout {
    grid-template-columns: 220px minmax(0, 1fr);
    gap: 48px;
  }
  .about-toc {
    display: block;
    position: sticky;
    top: 80px;
    align-self: start;
    max-height: calc(100vh - 100px);
    overflow-y: auto;
  }
}

.about-toc-heading {
  font-family: var(--font-data);
  font-size: var(--text-micro);
  color: var(--text-tertiary);
  text-transform: uppercase;
  letter-spacing: var(--tracking-wider);
  margin: 0 0 12px;
}

.about-toc-list {
  list-style: none;
  padding: 0;
  margin: 0;
  display: flex;
  flex-direction: column;
  gap: 2px;
}

.about-toc-list a {
  display: block;
  padding: 5px 10px;
  font-family: var(--font-data);
  font-size: var(--text-xs);
  color: var(--text-secondary);
  text-decoration: none;
  border-radius: 4px;
  transition: color 150ms ease, background-color 150ms ease;
}

.about-toc-list a:hover {
  color: var(--accent);
  background: color-mix(in oklch, var(--accent) 8%, transparent);
}

/* Active-section state — set by active_section_controller.js as the user
   scrolls. The small left border is the key signal: color + background
   alone read as hover; the 2px accent rule gives the currently-active
   section an obvious "you are here" marker in a dense 15-item list.
   Padding nudges right by the rule width so the label doesn't shift. */
.about-toc-list a.toc-active {
  color: var(--accent);
  background: color-mix(in oklch, var(--accent) 10%, transparent);
  box-shadow: inset 2px 0 0 var(--accent);
  padding-left: 12px;
}

.about-toc-list a:focus-visible {
  outline: 1px solid color-mix(in oklch, var(--accent) 50%, transparent);
  outline-offset: 2px;
}

/* Anchor-jump offset so clicked section headings land below the sticky
   site nav (56px desktop, 52px mobile) instead of hiding behind it.
   Vertical rhythm between sections lives here too. */
.about-section {
  scroll-margin-top: 80px;
  margin-bottom: 48px;
}

/* H3 subheading used when an H2 section has internal structure — e.g. the
   three components of Event-Weighted K-Factor. Smaller than .about-heading,
   same display font, visible hierarchy without the extra padding of a new
   section break. Asymmetric margins: generous top (40px) separates it
   from the previous block; tight bottom (16px) binds it to the first
   paragraph below. */
.about-subheading {
  font-family: var(--font-display);
  font-size: var(--text-lg);
  font-weight: 600;
  color: var(--text-primary);
  margin: 40px 0 16px;
  letter-spacing: -0.01em;
}

/* Smaller sub-heading used to group v5.3 sub-topics inside the
   Calibration section. Slightly less prominent than the full
   .about-subheading so the v5.1 bootstrap narrative stays the
   dominant break within Calibration. Same asymmetric-margin idea,
   scaled down. */
.about-subheading--muted {
  font-size: var(--text-base);
  color: var(--text-secondary);
  margin: 28px 0 10px;
  font-weight: 600;
  letter-spacing: 0;
}

.about-intro {
  font-size: var(--text-md);
  color: var(--text-secondary);
  line-height: var(--leading-loose);
  /* 24px matches the rhythm used for body paragraphs (.about-text) —
     consistent block gap down the whole page. The last intro gets an
     extra 16px before the System Health callout / first section to
     create a page-level break. */
  margin-bottom: 24px;
  max-width: 68ch;
}

.about-intro:last-of-type { margin-bottom: 40px; }

.about-intro strong { color: var(--text-primary); }

/* Second intro beat — the technical sentence for the analyst persona who
   skipped the first-timer welcome. Slightly quieter so the welcoming
   first beat stays dominant. */
.about-intro--technical {
  font-size: var(--text-base);
  color: var(--text-tertiary);
}

.about-intro--technical strong { color: var(--text-secondary); }

.about-heading {
  font-family: var(--font-display);
  font-size: var(--text-xl);
  font-weight: 600;
  color: var(--text-primary);
  /* 16px below the H2 — tight enough that the first paragraph feels bound
     to the heading, loose enough that the heading reads as a separate
     line. The top of each H2 is anchored by the previous section's
     margin-bottom: 48px (see .about-section). */
  margin-bottom: 16px;
}

.about-text {
  font-size: var(--text-md);
  color: var(--text-secondary);
  line-height: var(--leading-loose);
  /* 24px between top-level blocks (paragraph → paragraph, paragraph →
     card, paragraph → table). Matches the margin on .about-card /
     .about-example / .about-table below so the whole section reads as a
     consistent rhythm regardless of block type. */
  margin-bottom: 24px;
  max-width: 68ch;
}

.about-text strong { color: var(--text-primary); }
.about-text a { color: var(--accent); }
/* Muted caption beneath a preceding block. 16px top margin sits tighter
   against its anchor than a fresh paragraph would, signalling
   "footnote to the above" rather than "new idea." */
.about-text--muted { color: var(--text-tertiary); font-size: var(--text-sm); margin-top: 16px; margin-bottom: 24px; }

/* Inline accent link used in prose paragraphs (changelog, about-intro). */
.inline-link {
  color: var(--accent);
  border-bottom: 1px solid transparent;
  transition: border-color 150ms ease, color 150ms ease;
}
.inline-link:hover {
  color: var(--accent-hover);
  border-bottom-color: var(--accent-hover);
}

/* Info cards */
.about-card {
  background: var(--card);
  border: 1px solid var(--border);
  border-radius: 12px;
  overflow: hidden;
  /* Same 24px block rhythm as .about-text / .about-intro / .about-example
     — consistent gap to the next block regardless of what type it is. */
  margin-bottom: 24px;
}

.about-card-row {
  padding: 16px 20px;
  border-bottom: 1px solid var(--border);
}

.about-card-row:last-child { border-bottom: none; }

.about-card-label {
  font-family: var(--font-data);
  font-size: var(--text-2xs);
  font-weight: 500;
  color: var(--text-tertiary);
  letter-spacing: var(--tracking-wider);
  text-transform: uppercase;
  margin-bottom: 6px;
}

.about-card-value {
  font-family: var(--font-display);
  font-size: var(--text-3xl);
  font-weight: 700;
  color: var(--text-primary);
  margin-bottom: 4px;
  letter-spacing: -0.01em;
}

.about-card-desc {
  font-size: var(--text-sm);
  color: var(--text-secondary);
  line-height: var(--leading-relaxed);
}

/* Numbered factors */
.about-factors {
  display: flex;
  flex-direction: column;
  gap: 16px;
  /* Consistent 24px block rhythm after the list, same as cards/paragraphs. */
  margin-bottom: 24px;
}

.about-factor {
  display: flex;
  gap: 16px;
  align-items: flex-start;
}

.about-factor-num {
  width: 36px;
  height: 36px;
  border-radius: 8px;
  background: var(--card);
  border: 1px solid var(--border);
  display: flex;
  align-items: center;
  justify-content: center;
  font-family: var(--font-data);
  font-size: var(--text-sm);
  font-weight: 700;
  color: var(--text-primary);
  flex-shrink: 0;
}

/* Format-code tints for the P/L/E factor chips in the Formats section.
   Each uses the tier color as background with white text for contrast. */
.about-factor-num--sector   { background: var(--tier-sector); border-color: var(--tier-sector); color: var(--text-primary); }
.about-factor-num--regional { background: var(--tier-regional); border-color: var(--tier-regional); color: var(--text-primary); }
.about-factor-num--galactic { background: var(--tier-galactic); border-color: var(--tier-galactic); color: var(--text-primary); }

.about-factor-content { flex: 1; }

.about-factor-content h3 {
  font-family: var(--font-display);
  font-size: var(--text-md);
  font-weight: 600;
  color: var(--text-primary);
  margin-bottom: 4px;
}

.about-factor-content p {
  font-size: var(--text-sm);
  color: var(--text-secondary);
  line-height: var(--leading-relaxed);
  max-width: 68ch;
}

/* Tier/round tables */
.about-table {
  background: var(--card);
  border: 1px solid var(--border);
  border-radius: 12px;
  overflow: hidden;
  /* Consistent 24px block rhythm after the table. */
  margin-bottom: 24px;
}

.about-table-header {
  display: flex;
  padding: 10px 20px;
  font-family: var(--font-data);
  font-size: var(--text-2xs);
  font-weight: 500;
  color: var(--text-tertiary);
  letter-spacing: 0.5px;
  text-transform: uppercase;
  border-bottom: 1px solid var(--border);
}

.about-table-row {
  display: flex;
  padding: 12px 20px;
  align-items: center;
  border-bottom: 1px solid var(--border);
}

.about-table-row:last-child { border-bottom: none; }

.about-table-col-tier { width: 140px; flex-shrink: 0; }
.about-table-col-val { width: 100px; flex-shrink: 0; font-weight: 600; color: var(--accent); }

/* Highlighted multiplier values — replaces inline style="color: var(--warning);"
   on the Putting-It-All-Together rows that call out finals-winner totals. */
.about-table-col-val--highlight { color: var(--warning); }
.about-table-col-val--champion  { color: var(--warning); font-weight: 700; }

/* The single "Galactic Finals (champion)" callout row — the one match worth
   the most in the system. Warm tint + slightly heavier border to match its
   semantic weight. Replaces an inline background style. */
.about-table-row--champion {
  background: color-mix(in oklch, var(--warning) 8%, transparent);
}

/* Labeled-list modifier tints for the Qualifying Events card. Replaces
   inline color styles on the Included / Excluded labels. */
.about-card-label--positive { color: var(--positive); }
.about-card-label--muted    { color: var(--text-tertiary); }
.about-table-col-desc { flex: 1; font-size: var(--text-sm); color: var(--text-secondary); }

/* Inline SVG calibration chart. Replaces the old bucket table on
   /how-it-works. The SVG is fully self-contained (no JS); layout and
   colors are hard-coded in the helper to match DESIGN.md tokens. The
   figure wrapper holds the caption and provides the surface/border
   continuity with adjacent .about-card / .about-text blocks. */
.calibration-chart-figure {
  /* 24px block rhythm — matches .about-card / .about-table / .about-example. */
  margin: 0 0 24px;
  padding: 20px 24px 16px;
  background: var(--card);
  border: 1px solid var(--border);
  border-radius: 12px;
}

.calibration-chart {
  display: block;
  width: 100%;
  height: auto;
  max-width: 100%;
}

.calibration-chart-caption {
  font-family: var(--font-body);
  font-size: var(--text-xs);
  line-height: var(--leading-relaxed);
  color: var(--text-secondary);
  margin: 12px 0 0;
  max-width: 64ch;
}

/* Screen-reader-only: keeps the per-bucket numeric table accessible to
   assistive tech after the visual chart replaced it. Sighted users see the
   SVG + hover tooltips; screen-reader users get a proper <table>. */
.sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}

/* Worked example — the one place on the page where the math is walked
   end-to-end instead of stated as a formula. Stepped numbered blocks
   so the reader can follow the order of operations visually. */
.about-example {
  background: var(--card);
  border: 1px solid var(--border);
  border-radius: 8px;
  padding: 24px;
  /* Consistent 24px block rhythm after the example. */
  margin-bottom: 24px;
}

.about-example__setup {
  font-size: var(--text-base);
  color: var(--text-secondary);
  line-height: var(--leading-loose);
  margin: 0 0 24px;
  padding-bottom: 20px;
  border-bottom: 1px solid var(--border);
}

.about-example__steps {
  display: flex;
  flex-direction: column;
  gap: 20px;
}

.about-example__step {
  display: flex;
  gap: 16px;
  align-items: flex-start;
}

.about-example__step-num {
  width: 28px;
  height: 28px;
  border-radius: 14px;
  background: color-mix(in oklch, var(--accent) 18%, var(--card));
  border: 1px solid color-mix(in oklch, var(--accent) 35%, var(--border));
  display: flex;
  align-items: center;
  justify-content: center;
  font-family: var(--font-data);
  font-size: var(--text-xs);
  font-weight: 700;
  color: var(--accent);
  flex-shrink: 0;
  margin-top: 1px;
}

.about-example__step-body { flex: 1; min-width: 0; }

.about-example__step-heading {
  font-family: var(--font-display);
  font-size: var(--text-base);
  font-weight: 600;
  color: var(--text-primary);
  margin: 0 0 4px;
  letter-spacing: -0.005em;
}

.about-example__step-body p {
  font-size: var(--text-sm);
  color: var(--text-secondary);
  line-height: var(--leading-loose);
  margin: 0;
}

.about-example__step-body code {
  font-family: var(--font-data);
  font-size: var(--text-xs);
  background: color-mix(in oklch, var(--accent) 10%, transparent);
  color: var(--text-primary);
  padding: 1px 5px;
  border-radius: 3px;
}

/* ============================================================
   CHANGELOG — reverse-chronological entry list with sticky version
   sidebar. Shares the .content-area.about-page wrapper with how-it-works
   and inherits .about-title / .about-intro typography.
   ============================================================ */

.changelog-layout {
  display: grid;
  grid-template-columns: 1fr;
  gap: 32px;
}

@media (min-width: 1024px) {
  .changelog-layout {
    grid-template-columns: 180px minmax(0, 1fr);
    gap: 48px;
  }
  .changelog-toc {
    position: sticky;
    top: 80px;
    align-self: start;
    max-height: calc(100vh - 100px);
    overflow-y: auto;
  }
}

.changelog-toc { display: none; }
@media (min-width: 1024px) { .changelog-toc { display: block; } }

.changelog-toc__heading {
  font-family: var(--font-data);
  font-size: var(--text-micro);
  color: var(--text-tertiary);
  text-transform: uppercase;
  letter-spacing: var(--tracking-wider);
  margin: 0 0 12px;
}

.changelog-toc__list {
  list-style: none;
  padding: 0;
  margin: 0;
  display: flex;
  flex-direction: column;
  gap: 2px;
}

.changelog-toc__link {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 8px;
  padding: 6px 10px;
  border-radius: 4px;
  text-decoration: none;
  color: var(--text-secondary);
  transition: color 150ms ease, background-color 150ms ease;
}

.changelog-toc__link:hover {
  color: var(--accent);
  background: color-mix(in oklch, var(--accent) 8%, transparent);
}

/* Active-section state — same 2px left-rule marker as the how-it-works
   TOC. The link stays compact (version + date on one row) but the rule
   gives it a clear "you are here" anchor during scroll. */
.changelog-toc__link.toc-active {
  background: color-mix(in oklch, var(--accent) 10%, transparent);
  box-shadow: inset 2px 0 0 var(--accent);
  padding-left: 12px;
}

.changelog-toc__link.toc-active .changelog-toc__version { color: var(--accent); }

.changelog-toc__link:focus-visible {
  outline: 1px solid color-mix(in oklch, var(--accent) 50%, transparent);
  outline-offset: 2px;
}

.changelog-toc__version {
  font-family: var(--font-data);
  font-size: var(--text-sm);
  font-weight: 600;
}

.changelog-toc__date {
  font-family: var(--font-data);
  font-size: var(--text-micro);
  color: var(--text-tertiary);
}

.changelog-toc__link--current .changelog-toc__version {
  color: var(--accent);
}

.changelog-content { min-width: 0; max-width: 800px; }

.changelog-entries {
  display: flex;
  flex-direction: column;
  /* 24px gap between version entries — they're major boundaries (each
     is a full release) and 16px made them feel like one continuous log
     rather than a list of distinct shipments. */
  gap: 24px;
}

.changelog-entry {
  background: var(--card);
  border: 1px solid var(--border);
  border-radius: 8px;
  /* Slightly more generous padding — 24px on top matches bottom, and the
     extra 4px over the previous 20/24 asymmetric value reads as "this is
     a substantial release note" rather than "compact status update."
     Horizontal 28px gives the prose more breathing room off the border. */
  padding: 24px 28px;
  scroll-margin-top: 80px;
  transition: border-color 150ms ease;
}

.changelog-entry:hover {
  border-color: color-mix(in oklch, var(--accent) 20%, var(--border));
}

.changelog-entry:target,
.changelog-entry--current {
  border-color: color-mix(in oklch, var(--accent) 40%, var(--border));
}

.changelog-entry:target .changelog-entry__permalink,
.changelog-entry:hover .changelog-entry__permalink {
  opacity: 1;
}

.changelog-entry__header {
  display: flex;
  align-items: baseline;
  flex-wrap: wrap;
  gap: 12px;
  /* Header/body separator: 16px + 16px = 32px total visual gap across the
     hairline. Was 24px (12+12). The extra breathing room lets the
     release body start as a clean reading surface instead of running
     up against the version + date + tags strip. */
  margin-bottom: 16px;
  padding-bottom: 16px;
  border-bottom: 1px solid var(--border);
}

.changelog-entry__meta {
  display: flex;
  align-items: baseline;
  gap: 10px;
}

.changelog-entry__version {
  font-family: var(--font-data);
  font-size: var(--text-lg);
  font-weight: 700;
  color: var(--text-primary);
  letter-spacing: -0.01em;
}

/* Current version gets the accent color as the signal. No glow — glow is
   reserved for the rating hero, #1 rank, table rating cells, and the
   Galactic tier badge (see DESIGN.md glow-restraint principle). The
   paired "Live" tag on the same row carries the freshness signal. */
.changelog-entry--current .changelog-entry__version {
  color: var(--accent);
}

.changelog-entry__date {
  font-family: var(--font-data);
  font-size: var(--text-2xs);
  color: var(--text-tertiary);
  letter-spacing: 0.04em;
}

.changelog-entry__badges {
  display: flex;
  align-items: center;
  gap: 6px;
  margin-left: auto;
}

/* Small tags categorizing each change. "Rating change" is positive-tinted
   because that's the signal a player cares about when scanning the list.
   Engine / Display are neutral. "Live" is the top-version marker. */
.changelog-entry__tag {
  font-family: var(--font-data);
  font-size: var(--text-micro);
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: var(--tracking-wide);
  padding: 3px 8px;
  border-radius: 3px;
  border: 1px solid currentColor;
  white-space: nowrap;
}

.changelog-entry__tag--rating {
  color: var(--positive);
  background: color-mix(in oklch, var(--positive) 10%, transparent);
}

.changelog-entry__tag--engine {
  color: var(--text-secondary);
  background: transparent;
}

/* Overall composite is a specific scope within Engine — same color tier,
   but its own class so the taxonomy reads (engine / overall / display /
   admin / rating / current) rather than overloading --engine to mean
   three different things like the pre-v5 taxonomy did. */
.changelog-entry__tag--overall {
  color: var(--text-secondary);
  background: transparent;
}

/* Admin-tooling tag — muted tint so it doesn't compete for attention
   with rating/engine tags that actually affect player-facing numbers. */
.changelog-entry__tag--admin {
  color: var(--text-tertiary);
  background: transparent;
}

.changelog-entry__tag--display {
  color: var(--text-tertiary);
  background: transparent;
}

.changelog-entry__tag--current {
  color: var(--accent);
  background: color-mix(in oklch, var(--accent) 12%, transparent);
  border-color: color-mix(in oklch, var(--accent) 40%, var(--border));
  /* The one element on the page whose job is "this version is running
     in production right now." 3.5s breathing pulse is slow enough that
     it never reads as chrome noise but unmistakable once you notice —
     earned glow for a signal that actually earns it (see DESIGN.md
     "Earn every glow"). Disabled under prefers-reduced-motion below. */
  animation: hri-live-pulse 3.5s ease-in-out infinite;
}

@keyframes hri-live-pulse {
  0%, 100% { box-shadow: 0 0 0 0 color-mix(in oklch, var(--accent) 0%, transparent); }
  50%      { box-shadow: 0 0 0 3px color-mix(in oklch, var(--accent) 22%, transparent); }
}

@media (prefers-reduced-motion: reduce) {
  .changelog-entry__tag--current { animation: none; }
}

.changelog-entry__permalink {
  font-family: var(--font-data);
  font-size: var(--text-md);
  font-weight: 400;
  color: var(--text-tertiary);
  text-decoration: none;
  opacity: 0;
  transition: opacity 150ms ease, color 150ms ease;
  padding: 0 4px;
}

.changelog-entry__permalink:hover {
  opacity: 1;
  color: var(--accent);
}

/* Keyboard focus: reveal the glyph AND show a visible ring so the tab
   stop is discoverable. Matches the :focus-visible pattern used by
   `.th-date__sort` and `.about-faq-q` elsewhere in this file. */
.changelog-entry__permalink:focus-visible {
  opacity: 1;
  color: var(--accent);
  outline: 1px solid color-mix(in oklch, var(--accent) 60%, transparent);
  outline-offset: 2px;
  border-radius: 3px;
}

.changelog-entry__body {
  font-size: var(--text-base);
  line-height: var(--leading-loose);
  color: var(--text-secondary);
}

.changelog-entry__headline {
  font-family: var(--font-display);
  font-size: var(--text-md);
  font-weight: 600;
  color: var(--text-primary);
  /* 14px — gives the one-sentence headline breathing room before the
     prose starts. 10px ran the headline into the first paragraph. */
  margin: 0 0 14px;
  letter-spacing: -0.005em;
}

.changelog-entry__prose {
  margin: 0;
}

.changelog-entry__prose code {
  font-family: var(--font-data);
  font-size: var(--text-xs);
  background: color-mix(in oklch, var(--accent) 8%, transparent);
  color: var(--text-primary);
  padding: 1px 5px;
  border-radius: 3px;
}

.changelog-entry__list {
  /* Slightly more air between a parent paragraph and the bullet list it
     introduces. 4px read as the paragraph and list being one continuous
     block; 8px separates them while still keeping the list visually
     bound to its parent. */
  margin: 8px 0 0;
  padding-left: 20px;
  color: var(--text-secondary);
  font-size: var(--text-base);
  line-height: var(--leading-relaxed);
}

.changelog-entry__list li {
  /* 6px between bullets. Enough to let each point breathe without
     making the list feel like a stack of detached sentences. */
  margin-bottom: 6px;
}

/* Mobile: tags stack below the version+date row when screen is narrow */
@media (max-width: 640px) {
  .changelog-entry__badges { margin-left: 0; }
  .changelog-entry__permalink { display: none; }
}

/* FAQ — native <details> disclosures. Collapsed by default so the 8-item
   block reads as a scannable index of questions; clicking any one opens
   just that answer. No JS, no ARIA, no state management. */
.about-faq {
  background: var(--card);
  border: 1px solid var(--border);
  border-radius: 8px;
  padding: 0;
  margin-bottom: 8px;
  transition: border-color 150ms ease;
}

.about-faq[open] {
  border-color: color-mix(in oklch, var(--accent) 35%, var(--border));
}

.about-faq-q {
  display: flex;
  align-items: baseline;
  gap: 12px;
  padding: 16px 20px;
  font-family: var(--font-display);
  font-size: var(--text-base);
  font-weight: 600;
  color: var(--text-primary);
  cursor: pointer;
  list-style: none;
  transition: color 150ms ease;
}

.about-faq-q::-webkit-details-marker { display: none; }

.about-faq-q::after {
  content: "+";
  margin-left: auto;
  font-family: var(--font-data);
  font-size: var(--text-lg);
  font-weight: 400;
  color: var(--text-tertiary);
  transition: transform 200ms ease, color 150ms ease;
}

.about-faq[open] .about-faq-q::after {
  content: "\2212"; /* minus sign */
  color: var(--accent);
}

.about-faq-q:hover { color: var(--accent); }

.about-faq-q:focus-visible {
  outline: 1px solid color-mix(in oklch, var(--accent) 50%, transparent);
  outline-offset: -1px;
  border-radius: 8px;
}

.about-faq-a {
  padding: 0 20px 18px;
  font-size: var(--text-sm);
  color: var(--text-secondary);
  line-height: var(--leading-loose);
  margin: 0;
}

.about-faq-a a { color: var(--accent); }

/* ============================================================
   RESPONSIVE
   ============================================================ */

@media (max-width: 1024px) {
  .content-area { padding: 24px 32px 32px; }
  .hero-section { padding: 36px 32px 28px; gap: 20px 28px; }
  .hero-section::after { left: 32px; right: 32px; }
  .hero-logo { height: 112px; }
  .hero-title { font-size: var(--text-4xl); }
  .hero-sub { font-size: var(--text-base); }
  .global-stats { gap: 20px; margin-left: 0; }
  .global-stat__value { font-size: var(--text-xl); }
  .global-stat { min-width: 80px; }
  .rank-confidence, .th-confidence { display: none; }
  .filter-row .search-input { width: 320px; max-width: 320px; }
}

@media (max-width: 640px) {
  .content-area { padding: 20px 16px 24px; }
  .hero-section { padding: 28px 16px 22px; gap: 18px 20px; flex-direction: column; text-align: center; align-items: center; }
  .hero-section::after { left: 16px; right: 16px; }
  .hero-logo { height: 96px; }
  .hero-lockup { align-items: center; }
  .hero-title { font-size: var(--text-3xl); text-align: center; }
  .hero-sub { font-size: var(--text-sm); text-align: center; }
  .global-stats { flex-wrap: wrap; justify-content: center; gap: 14px 20px; margin-left: 0; }
  .global-stat { min-width: auto; align-items: center; }
  .global-stat__value { font-size: var(--text-lg); }
  .hero-divider { display: none; }

  .filter-row {
    flex-direction: column;
    align-items: stretch;
  }
  .filter-row .search-input { width: 100%; max-width: none; }
  .filter-chips { flex-wrap: wrap; }

  /* Hide desktop column headers on mobile — rows use stacked layout instead */
  .table-header .th-record,
  .table-header .th-winrate,
  .table-header .th-events,
  .table-header .th-confidence,
  .table-header .th-tier,
  .table-header .th-date,
  .table-header .th-format,
  .table-header .th-players,
  .table-header .th-winner,
  .table-header .th-delta { display: none; }

  /* Leaderboard row becomes a 2-line card */
  .rank-row {
    flex-wrap: wrap;
    height: auto;
    min-height: 64px;
    padding: 10px 16px;
    row-gap: 6px;
    column-gap: 12px;
  }
  /* Mobile: left-align so rank hugs the left edge and sits naturally before
     the player name. Wider than desktop's 72px isn't needed here since the
     row-gap of the wrapping flex keeps rank and name separated. 56px fits
     5 digits at 16px Geist Mono Bold with a touch of room. */
  .rank-num { width: 56px; padding-right: 8px; text-align: left; }
  .rank-player { flex: 1; min-width: 0; }
  .rank-rating { width: auto; text-align: right; }

  .rank-meta {
    display: flex;
    flex-basis: 100%;
    align-items: baseline;
    gap: 12px;
    font-size: var(--text-2xs);
    color: var(--text-tertiary);
  }
  .rank-meta > span {
    width: auto;
    text-align: left;
    font-size: var(--text-2xs);
    color: var(--text-tertiary);
  }
  .rank-meta .rank-winrate { font-weight: 600; }
  .rank-meta .rank-record::before { content: "W-L "; color: var(--text-tertiary); opacity: 0.7; }
  .rank-meta .rank-events::before { content: "Events "; color: var(--text-tertiary); opacity: 0.7; }
  .rank-meta .confidence-badge { font-size: var(--text-2xs); color: var(--text-tertiary); }

  /* Tournament row becomes a 2-line card */
  .tourn-row {
    flex-wrap: wrap;
    height: auto;
    min-height: 60px;
    padding: 10px 16px;
    row-gap: 6px;
    column-gap: 12px;
    align-items: baseline;
  }
  .tourn-event { flex: 1; min-width: 0; }

  .tourn-meta {
    display: flex;
    flex-basis: 100%;
    align-items: baseline;
    flex-wrap: wrap;
    gap: 6px 12px;
    font-size: var(--text-2xs);
    color: var(--text-tertiary);
  }
  .tourn-meta > * {
    width: auto;
    text-align: left;
  }
  .tourn-meta .tourn-rating { font-size: var(--text-sm); }
  .tourn-meta .tourn-delta { margin-left: auto; }

  /* Tournaments index row (.rank-row.tournament-row): on mobile the event
     name takes a full-width first line, then tier/format/players/winner/
     date wrap beneath as a compact meta strip. Without this, the fixed-
     width columns flow awkwardly and the tier badge jams into the middle
     of a wrapping event name. */
  .tournament-row {
    flex-wrap: wrap;
    height: auto;
    min-height: 68px;
    padding: 10px 16px;
    row-gap: 6px;
    column-gap: 10px;
    align-items: baseline;
  }
  .tournament-row .tourn-event {
    flex-basis: 100%;
    width: 100%;
  }
  .tournament-row .tourn-tier,
  .tournament-row .tourn-format,
  .tournament-row .tourn-players,
  .tournament-row .tourn-winner,
  .tournament-row .tourn-date {
    width: auto;
    text-align: left;
    font-size: var(--text-xs);
  }
  .tournament-row .tourn-format,
  .tournament-row .tourn-players,
  .tournament-row .tourn-date {
    color: var(--text-tertiary);
  }
  .tournament-row .tourn-winner { margin-left: auto; }
  .tournament-row .tourn-players::after { content: " players"; opacity: 0.7; }

  .profile-hero {
    flex-direction: column;
    align-items: flex-start;
    gap: 16px;
  }
  .profile-hero__rating {
    align-items: flex-start;
    text-align: left;
  }
  .profile-hero__number { font-size: var(--text-6xl); }
  .profile-hero__number--muted { font-size: var(--text-4xl); }
  .profile-hero__delta-meta { text-align: left; }

  .metric-strip { gap: 8px 16px; }
  .metric-strip__sep { display: none; }

  .site-nav { padding: 0 16px; height: 52px; }
  .site-logo { font-size: var(--text-md); }
  .nav-logo-img { height: 32px; }
}

/* ============================================================
   v3 — Trust layer: cohort toggle, RD bands, credible interval
   ============================================================ */

/* Secondary filter row — sits below the format chips and search */
.filter-row--secondary {
  margin-top: 12px;
  margin-bottom: 8px;
}

/* Overall format is experimental — visually muted so players prefer per-format */
.chip--experimental {
  opacity: 0.75;
  font-style: italic;
}

.chip--experimental.chip--active { opacity: 1; font-style: normal; }

/* Ranked / Provisional cohort toggle */
.cohort-toggle {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  border: 1px solid var(--border);
  border-radius: 6px;
  background: var(--card);
  padding: 4px;
}

.cohort-chip {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  padding: 6px 14px;
  border-radius: 4px;
  font-family: var(--font-body);
  font-size: var(--text-xs);
  font-weight: 500;
  color: var(--text-tertiary);
  cursor: pointer;
  transition: all 0.15s ease;
}

.cohort-chip:hover { color: var(--text-primary); }

.cohort-chip--active {
  background: var(--accent-surface);
  color: var(--accent);
  font-weight: 600;
}

.cohort-count {
  font-family: var(--font-data);
  font-size: var(--text-2xs);
  color: var(--text-tertiary);
  background: var(--bg);
  padding: 1px 6px;
  border-radius: 10px;
  min-width: 18px;
  text-align: center;
}

.cohort-chip--active .cohort-count {
  background: var(--accent-soft);
  color: var(--accent);
}

/* Rating cell — number inherits typography from .rank-rating parent;
   only .rank-rating__ci below needs its own override for the smaller,
   dimmer ± credible-interval span. The .rank-row--provisional override
   further down still targets .rank-rating__number by class name to
   desaturate the number in provisional rows. */
.rank-rating__ci {
  font-family: var(--font-data);
  font-size: var(--text-2xs);
  font-weight: 400;
  color: var(--text-tertiary);
  margin-left: 6px;
  cursor: help;
}

/* Champion rating — earned glow for the #1 player on any format's
   leaderboard. All rank ratings default to accent blue, so color alone
   isn't the differentiator; the champion gets a two-layer glow + bumped
   weight so the #1 number reads as "THE number." One row per page earns
   this. Matches the tournament-show champion pedestal treatment. */
.rank-rating__number--champion {
  font-weight: 800;
  text-shadow: 0 0 14px var(--accent-hot), 0 0 4px var(--accent-strong);
}

/* Provisional row — visually quieter on the ranked leaderboard if one slips
   in, and always on the Provisional tab. 0.82 was almost imperceptible
   against the already-dim palette; 0.65 actually reads as "still stabilizing"
   at a glance. Accent color loses its glow treatment. Hover restores to full
   so the row is still scannable on intent. */
.rank-row--provisional { opacity: 0.65; }
.rank-row--provisional .rank-rating__number { color: var(--text-secondary); }
.rank-row--provisional:hover { opacity: 1; }

/* ============================================================
   STYLED TOOLTIPS — replaces native title="" on data/help elements.
   Usage: <span class="has-tooltip" data-tooltip="Full sentence here">78%</span>
   Supports an optional position hint via data-tooltip-pos="left|right|bottom".
   Default anchor: above the element, center-aligned.
   ============================================================ */

.has-tooltip {
  position: relative;
  cursor: help;
}

.has-tooltip::before,
.has-tooltip::after {
  position: absolute;
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.15s ease, transform 0.15s ease;
  z-index: 300;
}

/* Tooltip body */
.has-tooltip::after {
  content: attr(data-tooltip);
  bottom: calc(100% + 10px);
  left: 50%;
  transform: translateX(-50%) translateY(4px);
  background: var(--surface);
  border: 1px solid var(--border);
  color: var(--text-primary);
  padding: 10px 14px;
  border-radius: 6px;
  font-family: var(--font-body);
  font-size: var(--text-xs);
  font-weight: 400;
  line-height: var(--leading-relaxed);
  letter-spacing: 0.005em;
  text-align: left;
  text-transform: none;
  width: max-content;
  max-width: 320px;
  white-space: normal;
  box-shadow: 0 10px 28px rgba(0, 0, 0, 0.55);
}

/* Caret */
.has-tooltip::before {
  content: "";
  bottom: calc(100% + 4px);
  left: 50%;
  transform: translateX(-50%) translateY(4px);
  border: 6px solid transparent;
  border-top-color: var(--border);
  width: 0;
  height: 0;
}

.has-tooltip:hover::before,
.has-tooltip:hover::after,
.has-tooltip:focus-visible::before,
.has-tooltip:focus-visible::after {
  opacity: 1;
  transform: translateX(-50%) translateY(0);
}

/* Right-anchor variant — for tooltips near the left edge */
.has-tooltip[data-tooltip-pos="right"]::after,
.has-tooltip[data-tooltip-pos="right"]::before {
  left: auto;
  right: 0;
  transform: translateX(0) translateY(4px);
}
.has-tooltip[data-tooltip-pos="right"]::before { right: 10px; }
.has-tooltip[data-tooltip-pos="right"]:hover::after,
.has-tooltip[data-tooltip-pos="right"]:hover::before { transform: translateX(0) translateY(0); }

/* Left-anchor variant — for tooltips near the right edge (leaderboard Confidence column) */
.has-tooltip[data-tooltip-pos="left"]::after,
.has-tooltip[data-tooltip-pos="left"]::before {
  left: 0;
  right: auto;
  transform: translateX(0) translateY(4px);
}
.has-tooltip[data-tooltip-pos="left"]::before { left: 10px; }
.has-tooltip[data-tooltip-pos="left"]:hover::after,
.has-tooltip[data-tooltip-pos="left"]:hover::before { transform: translateX(0) translateY(0); }

/* Bottom-anchor — for tooltips that would clip off the top of the viewport */
.has-tooltip[data-tooltip-pos="bottom"]::after {
  bottom: auto;
  top: calc(100% + 10px);
  transform: translateX(-50%) translateY(-4px);
}
.has-tooltip[data-tooltip-pos="bottom"]::before {
  bottom: auto;
  top: calc(100% + 4px);
  border-top-color: transparent;
  border-bottom-color: var(--border);
  transform: translateX(-50%) translateY(-4px);
}
.has-tooltip[data-tooltip-pos="bottom"]:hover::after,
.has-tooltip[data-tooltip-pos="bottom"]:hover::before { transform: translateX(-50%) translateY(0); }

/* Confidence percentage — leaderboard + profile. Hover reveals raw RD.
   Tier colors come from rd_band so a glance still tells you if a rating
   is established/developing/provisional, but the number is the data. */
.confidence-pct {
  display: inline-flex;
  align-items: baseline;
  font-family: var(--font-data);
  font-size: var(--text-xs);
  font-weight: 600;
  letter-spacing: 0.02em;
  color: var(--text-secondary);
  cursor: help;
}

.confidence-pct--established { color: var(--positive); }
.confidence-pct--developing  { color: var(--warning); }
.confidence-pct--provisional { color: var(--text-tertiary); }

/* RD-band badge — pairs with the confidence pill on profile hero. Shown only
   when rating is not yet Established (RD > 80), so the player sees "Provisional"
   or "Developing" spelled out next to their rating instead of learning from
   color + tooltip alone. Brand tone: honest about uncertainty, not apologetic. */
.band-badge {
  display: inline-flex;
  align-items: center;
  font-family: var(--font-body);
  font-size: var(--text-2xs);
  font-weight: 600;
  letter-spacing: 0.4px;
  text-transform: uppercase;
  padding: 3px 8px;
  border-radius: 10px;
  border: 1px solid transparent;
  cursor: help;
}

.band-badge--established {
  color: var(--positive);
  background: rgba(34, 197, 94, 0.08);
  border-color: rgba(34, 197, 94, 0.25);
}

.band-badge--developing {
  color: var(--warning);
  background: rgba(245, 158, 11, 0.08);
  border-color: rgba(245, 158, 11, 0.25);
}

.band-badge--provisional {
  color: var(--text-tertiary);
  background: rgba(148, 163, 184, 0.08);
  border-color: rgba(148, 163, 184, 0.2);
}

/* Provisional banner on profile pages */
.provisional-banner {
  display: flex;
  align-items: flex-start;
  gap: 12px;
  padding: 12px 16px;
  margin-bottom: 20px;
  background: rgba(245, 158, 11, 0.06);
  border: 1px solid rgba(245, 158, 11, 0.25);
  border-radius: var(--radius-md);
  color: var(--text-secondary);
  font-size: var(--text-sm);
  line-height: var(--leading-relaxed);
}

.provisional-banner strong { color: var(--warning); font-weight: 600; }

/* Calm informational banner — tiny data, separate-format context, etc.
   Accent-blue tint instead of the warning-amber provisional banner. */
.format-notice {
  display: flex;
  align-items: baseline;
  flex-wrap: wrap;
  gap: 4px 8px;
  padding: 10px 16px;
  margin-bottom: 20px;
  background: var(--accent-wash);
  border: 1px solid var(--accent-mid);
  border-radius: var(--radius-md);
  color: var(--text-secondary);
  font-size: var(--text-sm);
  line-height: var(--leading-relaxed);
}

.format-notice strong {
  color: var(--accent);
  font-weight: 600;
}

/* Plain-English explainer inside each tournament dropdown on the profile page.
   Answers the community question "why did I get 0 points / so few points at
   a big tournament?" The label sits above the sentence in Geist Mono caps
   so it reads as a data row header, not a generic blurb. */
/* "Why Your Rating Moved" callout — warm amber instead of accent blue so
   it reads as an *insight* layer rather than competing with the site's
   primary accent. Amber = "pay attention, this is annotation," not
   "earned signal." Keeps the brand's accent budget reserved for ratings,
   rankings, and glow treatments. */
.tourn-explainer {
  margin: 0;
  padding: var(--space-sm) var(--space-md);
  background: rgba(245, 158, 11, 0.07);
  border-left: 2px solid var(--warning);
  border-radius: 0 var(--radius-sm) var(--radius-sm) 0;
}

.tourn-explainer__label {
  display: block;
  font-family: var(--font-data);
  font-size: var(--text-micro);
  font-weight: 700;
  color: var(--warning);
  letter-spacing: var(--tracking-wider);
  text-transform: uppercase;
  margin-bottom: var(--space-2xs);
}

.tourn-explainer__text {
  margin: 0;
  font-size: var(--text-sm);
  line-height: var(--leading-relaxed);
  color: var(--text-secondary);
}

.tourn-explainer__cta {
  margin: var(--space-xs) 0 0;
  font-size: var(--text-xs);
  font-family: var(--font-data);
}

/* Small persistent caption explaining the leaderboard sort order. Community
   feedback: players kept asking why a lower-raw-rating player sorts above a
   higher-raw-rating player — because we sort by the lower bound of the 95%
   CI, not the raw number. Caption lives right above the table so it's in
   scope whenever someone is scanning ranks. */
.sort-caption {
  font-size: var(--text-xs);
  color: var(--text-tertiary);
  margin: 6px 0 14px;
  line-height: var(--leading-relaxed);
}

.sort-caption code {
  font-family: var(--font-data);
  font-size: var(--text-xs);
  color: var(--text-secondary);
  background: none;
  padding: 0;
}

.sort-caption a {
  color: var(--text-secondary);
  border-bottom: 1px dotted currentColor;
  text-decoration: none;
}

.sort-caption a:hover { color: var(--accent); }

/* Admin surfaces (anomaly review, etc). Intentionally understated — these
   aren't end-user pages, so we lean on the same table/form primitives as
   the main app rather than building a second visual language. */
.admin-count {
  font-family: var(--font-data);
  font-size: var(--text-xs);
  color: var(--text-tertiary);
}

.admin-filter-row {
  display: flex;
  flex-wrap: wrap;
  gap: 8px 16px;
  align-items: flex-end;
  margin: 12px 0 20px;
  padding: 12px 16px;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
}

.admin-filter-row label {
  display: flex;
  flex-direction: column;
  gap: 4px;
  font-size: var(--text-micro);
  text-transform: uppercase;
  letter-spacing: 0.4px;
  color: var(--text-tertiary);
}

.admin-filter-row input,
.admin-filter-row select {
  font-family: var(--font-data);
  font-size: var(--text-sm);
  color: var(--text-primary);
  background: var(--bg);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  padding: 6px 8px;
  min-width: 110px;
}

.admin-filter-row button {
  border: none;
  cursor: pointer;
  padding: 7px 14px;
}

/* Tournament detail page ─────────────────────────────────────────────── */

/* Cap show-page content width to match /tournaments index (1280px). Keeps
   both tournament views optically aligned on wide monitors — bracket and
   shift table stop running to the far edge where rows become tiring to
   scan. Below 1280px the rule is a no-op, same as the index's rule. */
.tournament-show {
  max-width: 1280px;
  margin-inline: auto;
}

/* Tournament hero — three tiers: title row (name + tier badge), meta row
   (format · date · location · melee link), and a two-pedestal data block
   (Champion on the left, Field Size on the right). The pedestal gives the
   page a dominant data point that matches the site's "rating is the hero"
   principle without inventing a fake number: champion is the narrative of
   any tournament, field size is the scale. Everything else subordinates. */
.tournament-hero {
  display: flex;
  flex-direction: column;
  gap: var(--space-sm);
  padding: var(--space-lg) 0;
  border-bottom: 1px solid var(--border);
  margin-bottom: var(--space-2xl);
}

.tournament-hero__title-row {
  display: flex;
  align-items: baseline;
  gap: var(--space-md);
  flex-wrap: wrap;
}

.tournament-hero__name {
  font-family: var(--font-display);
  font-size: clamp(28px, 4vw, 36px);
  font-weight: 700;
  line-height: var(--leading-tight);
  color: var(--text-primary);
  margin: 0;
  flex: 1;
  min-width: 0;
}

.tournament-hero__meta {
  display: flex;
  align-items: center;
  gap: var(--space-xs);
  flex-wrap: wrap;
  font-family: var(--font-data);
  font-size: var(--text-xs);
  color: var(--text-tertiary);
}

.tournament-hero__meta-item {
  color: var(--text-secondary);
}

.tournament-hero__meta-sep {
  color: var(--text-tertiary);
  opacity: 0.6;
}

.tournament-hero__meta-link {
  color: var(--text-tertiary);
  text-decoration: none;
  border-bottom: 1px dashed transparent;
  transition: border-color 0.15s ease, color 0.15s ease;
}

.tournament-hero__meta-link:hover,
.tournament-hero__meta-link:focus-visible {
  color: var(--accent);
  border-bottom-color: var(--accent);
}

/* Two-pedestal data block: champion (left) + field size (right). Separator
   above pushes it visually away from the name/meta. Grid with 1fr auto lets
   the champion take available width while field size hugs the right edge. */
.tournament-hero__pedestal {
  display: grid;
  grid-template-columns: 1fr auto;
  gap: var(--space-xl) var(--space-2xl);
  align-items: end;
  padding-top: var(--space-md);
  margin-top: var(--space-xs);
  border-top: 1px solid var(--border);
}

.tournament-hero__pedestal-label {
  display: block;
  font-family: var(--font-data);
  font-size: var(--text-micro);
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 1px;
  color: var(--text-tertiary);
  margin-bottom: var(--space-2xs);
}

.tournament-hero__champion {
  min-width: 0;
}

.tournament-hero__champion-name {
  font-family: var(--font-data);
  font-size: clamp(22px, 3.2vw, 28px);
  font-weight: 700;
  line-height: var(--leading-snug);
  color: var(--accent);
  text-shadow: 0 0 14px var(--accent-strong);
  text-decoration: none;
  transition: text-shadow 0.15s ease;
}

.tournament-hero__champion-name:hover,
.tournament-hero__champion-name:focus-visible {
  text-shadow: 0 0 22px var(--accent-hot);
}

.tournament-hero__champion-name--empty {
  color: var(--text-tertiary);
  text-shadow: none;
}

.tournament-hero__champion-meta {
  margin-top: var(--space-2xs);
  font-family: var(--font-body);
  font-size: var(--text-sm);
  color: var(--text-tertiary);
}

.tournament-hero__runner-up {
  color: var(--text-secondary);
  text-decoration: none;
  border-bottom: 1px dashed transparent;
  transition: border-color 0.15s ease, color 0.15s ease;
}

.tournament-hero__runner-up:hover,
.tournament-hero__runner-up:focus-visible {
  color: var(--text-primary);
  border-bottom-color: var(--text-secondary);
}

.tournament-hero__finals-record {
  font-family: var(--font-data);
  color: var(--text-secondary);
  font-weight: 600;
}

.tournament-hero__field {
  text-align: right;
  line-height: var(--leading-tight);
}

.tournament-hero__field-number {
  display: block;
  font-family: var(--font-data);
  font-size: clamp(32px, 4.8vw, 44px);
  font-weight: 700;
  color: var(--text-primary);
  font-variant-numeric: tabular-nums;
  letter-spacing: var(--tracking-tight);
}

.tournament-hero__field-unit {
  display: block;
  font-family: var(--font-data);
  font-size: var(--text-micro);
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 1px;
  color: var(--text-tertiary);
  margin-top: var(--space-2xs);
}

/* Narrow viewports: stack the two pedestals so neither gets crushed. */
@media (max-width: 560px) {
  .tournament-hero__pedestal {
    grid-template-columns: 1fr;
    gap: var(--space-lg);
    align-items: start;
  }
  .tournament-hero__field {
    text-align: left;
  }
}

/* Actual bracket: Quarters → Semis → Finals, left to right. Each round is
   its own flexbox column with justify-content: space-around so matches
   stay vertically centered relative to what feeds them. Horizontal
   connector lines extend from every non-final match into the next column. */

.tournament-bracket {
  display: flex;
  align-items: stretch;
  gap: 40px;
  margin-bottom: 32px;
  padding: 4px 12px 12px;
}

.bracket-column {
  display: flex;
  flex-direction: column;
  min-width: 0;
  flex: 1;
}

.bracket-column__label {
  font-family: var(--font-data);
  font-size: var(--text-micro);
  text-transform: uppercase;
  letter-spacing: 0.5px;
  color: var(--text-tertiary);
  text-align: center;
  padding-bottom: 8px;
  border-bottom: 1px solid var(--border);
  margin-bottom: 16px;
}

.bracket-column__matches {
  flex: 1;
  display: flex;
  flex-direction: column;
  justify-content: space-around;
  gap: 8px;
}

.bracket-match {
  position: relative;
  display: flex;
  flex-direction: column;
  gap: 2px;
  padding: 8px 12px;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
}

/* Horizontal connector extending rightward from every match in all
   columns except the last. Puts a tick-mark that reads as a bracket
   feed without requiring pairing metadata we don't have. */
.bracket-column:not(:last-child) .bracket-match::after {
  content: '';
  position: absolute;
  left: 100%;
  top: 50%;
  width: 40px;
  border-top: 1px solid var(--border);
}

/* Horizontal connector extending leftward into every match after the
   first column, meeting the outgoing line from the previous round. */
.bracket-column:not(:first-child) .bracket-match::before {
  content: '';
  position: absolute;
  right: 100%;
  top: 50%;
  width: 40px;
  border-top: 1px solid var(--border);
}

.bracket-match__player {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 4px 8px;
  border-radius: var(--radius-sm);
  color: var(--text-secondary);
  font-family: var(--font-body);
  font-size: var(--text-sm);
}

.bracket-match__player a {
  color: inherit;
}

.bracket-match__player--winner {
  background: var(--accent-subtle);
  color: var(--text-primary);
  font-weight: 600;
}

.bracket-match__score {
  font-family: var(--font-data);
  font-size: var(--text-xs);
  color: var(--text-tertiary);
}

.bracket-match__player--winner .bracket-match__score {
  color: var(--accent);
  font-weight: 700;
}

/* Bracket crescendo: Quarters are the widest, most muted column (many matches,
   less narrative weight); Semis sit between; Finals gets the heaviest label,
   a slightly tinted surface, and a deeper glow on the winner. The entire
   bracket reads left-to-right as a drumbeat toward the champion. */
.bracket-column--quarterfinals .bracket-column__label {
  color: var(--text-tertiary);
  opacity: 0.75;
}

.bracket-column--semifinals .bracket-column__label {
  color: var(--text-tertiary);
}

.bracket-column--finals .bracket-column__label {
  color: var(--accent);
  font-weight: 700;
  letter-spacing: 1px;
  border-bottom-color: var(--accent);
}

.bracket-column--finals .bracket-match {
  background: var(--card);
  border-color: var(--accent-strong);
}

.bracket-column--finals .bracket-match__player--winner {
  background: var(--accent-soft);
}

.bracket-column--finals .bracket-match__player--winner a {
  color: var(--text-primary);
  text-shadow: 0 0 10px var(--accent-strong);
}

.bracket-column--finals .bracket-match__player--winner .bracket-match__score {
  text-shadow: 0 0 8px var(--accent-strong);
}

/* Responsive fallback: stack columns on narrow viewports. Connectors hide
   since they only make sense horizontally. */
@media (max-width: 768px) {
  .tournament-bracket {
    flex-direction: column;
    gap: var(--space-lg);
    padding: var(--space-xs) 0 var(--space-sm);
  }
  .bracket-column:not(:last-child) .bracket-match::after,
  .bracket-column:not(:first-child) .bracket-match::before {
    display: none;
  }
}

/* Section-header caption — small mono label sitting next to the heading
   in the right slot. Used on /tournaments/:id under "Rating Shift" to
   state the sort dimension. Mirrors .section-header__count/__filtered in
   weight and tone without carrying their specific semantics. */
.section-header__caption {
  font-family: var(--font-data);
  font-size: var(--text-2xs);
  font-weight: 500;
  text-transform: uppercase;
  letter-spacing: 0.5px;
  color: var(--text-tertiary);
}

/* Tournament rating-shift table — reuses .rank-row and the table-header
   column classes. Scoped to .tournament-show so the 52px compact row (same
   as /tournaments index) doesn't leak to the leaderboard which wants 56px
   to give the big rating number presence. Also adds two scoped column
   widths: 'Before' (pre-tournament rating) and a variant RD cell that
   drops the confidence-pill styling. */
.tournament-show .shift-row,
.tournament-show .table-header {
  height: 52px;
  gap: var(--space-md);
}

.tournament-show .table-header {
  height: 40px;
}

.th-shift-before, .shift-before {
  width: 80px;
  text-align: right;
  font-family: var(--font-data);
  font-size: var(--text-sm);
  color: var(--text-tertiary);
}

.shift-row .shift-rd {
  font-family: var(--font-data);
  font-size: var(--text-sm);
  color: var(--text-tertiary);
  font-weight: 500;
}

/* Delta sparkbar — a 2px bar at the bottom edge of every rating-change
   cell, width scaled to that row's |change| as a fraction of the page's
   biggest swing (--magnitude from the view). Green for gains, red for
   drops. Data-density move, not decoration: at a glance, a player can
   see "how big is this row's swing relative to the biggest on the page"
   without reading every number.

   The cell itself stays tight — the bar sits inside its padding box at
   the absolute bottom so it doesn't push layout or clip the number.
   When --magnitude is 0 (or unset on non-show pages), width is 0 and
   the bar is invisible. */
.tournament-show .tourn-delta {
  position: relative;
  overflow: hidden;
}

.tournament-show .tourn-delta::after {
  content: '';
  position: absolute;
  right: 0;
  bottom: 0;
  height: 2px;
  width: calc(var(--magnitude, 0) * 100%);
  border-radius: 1px;
  transition: width 0.3s ease-out;
  pointer-events: none;
}

.tournament-show .tourn-delta--up::after {
  background: linear-gradient(to left,
    rgba(34, 197, 94, 0.85),
    rgba(34, 197, 94, 0.15));
}

.tournament-show .tourn-delta--down::after {
  background: linear-gradient(to left,
    rgba(239, 68, 68, 0.85),
    rgba(239, 68, 68, 0.15));
}

/* Top Mover chip — awarded to the single player with the biggest |rating
   change| across the whole field (not the page). Lives inline next to the
   player name in the rating-shift table, small and tight. Accent glow is
   reserved — only one row on the page earns it. */
.top-mover-badge {
  display: inline-flex;
  align-items: center;
  margin-left: var(--space-xs);
  padding: 2px 8px;
  font-family: var(--font-data);
  font-size: var(--text-micro);
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.8px;
  color: var(--accent);
  background: var(--accent-subtle);
  border: 1px solid var(--accent-mid);
  border-radius: var(--radius-full);
  text-shadow: 0 0 8px var(--accent-strong);
  line-height: var(--leading-normal);
  white-space: nowrap;
}

/* Reduced-motion: count-up controller already bails to the final value,
   but kill the sparkbar width-transition so it doesn't flash in from 0
   on navigation either. Both flourishes are data-decoration and MUST
   not impair the core read for users who've opted out of motion. */
@media (prefers-reduced-motion: reduce) {
  .tournament-show .tourn-delta::after {
    transition: none;
  }
}

/* Rating history chart — dark panel with a fixed height so the Chart.js
   canvas has somewhere to draw. No border beyond the container frame,
   the grid lines carry the structure. */
.rating-chart-wrap {
  height: 220px;
  padding: 12px 12px 6px;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  margin-bottom: 24px;
}

.rating-chart { display: block; width: 100% !important; height: 100% !important; }

/* Head-to-head table — uses shared .table-container + .rank-row primitives,
   with a per-row left border vibe tint and a compact win/draw/loss split
   bar that carries the record visually. */
.th-h2h-record, .h2h-record   { width: 80px; text-align: center; font-family: var(--font-data); font-size: var(--text-sm); color: var(--text-secondary); }
.th-h2h-viz, .h2h-viz         { width: 110px; text-align: center; }
.th-h2h-winrate, .h2h-winrate { width: 70px; text-align: right; font-family: var(--font-data); font-size: var(--text-sm); }
.th-h2h-matches, .h2h-matches { width: 72px; text-align: right; font-family: var(--font-data); font-size: var(--text-sm); color: var(--text-secondary); }

.h2h-rating-fmt {
  font-family: var(--font-data);
  font-size: var(--text-micro);
  color: var(--text-tertiary);
  text-transform: uppercase;
  letter-spacing: 0.4px;
  margin-left: 6px;
}

/* Left accent border vibes — green for favored matchups, red for nemesis.
   4px strip so the aesthetic stays restrained (no full-row tinting). */
.h2h-row { border-left: 3px solid transparent; }
.h2h-row--favorable { border-left-color: var(--positive); }
.h2h-row--nemesis   { border-left-color: var(--negative); }
.h2h-row--even      { border-left-color: var(--border); }

.h2h-row--favorable .h2h-winrate { color: var(--positive); font-weight: 600; }
.h2h-row--nemesis   .h2h-winrate { color: var(--negative); font-weight: 600; }

/* Split bar: one flex row showing wins/draws/losses as proportional
   segments. Colored by outcome. Total width fixed by the column. */
.h2h-viz__bar {
  display: flex;
  align-items: stretch;
  width: 88px;
  height: 8px;
  border-radius: 4px;
  overflow: hidden;
  background: rgba(148, 163, 184, 0.12);
  margin: 0 auto;
}
.h2h-viz__win  { background: var(--positive); }
.h2h-viz__draw { background: var(--warning); opacity: 0.7; }
.h2h-viz__loss { background: var(--negative); }

/* ─── H2H inline expansion (details panel under each rival row) ─── */

/* Panel shell — matches .tourn-expand so the H2H expansion and the
   Tournament History expansion read as the same surface grammar. */
.h2h-expand {
  background: var(--surface);
  border: 1px solid var(--border);
  border-top: none;
  border-bottom-left-radius: 6px;
  border-bottom-right-radius: 6px;
  overflow: hidden;
}

/* Hero row inside the expansion: horizontal split bar on the left
   (the rivalry at a glance), big win rate on the right (the headline
   number). Mirrors the hero density pattern from the profile. */
.h2h-expand__hero {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 24px;
  padding: 16px 20px 12px;
  border-bottom: 1px solid rgba(148, 163, 184, 0.08);
  flex-wrap: wrap;
}
.h2h-expand__hero-main { flex: 1; min-width: 240px; }

.h2h-expand__hero-winrate {
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  gap: 4px;
}
.h2h-expand__hero-winrate .h2h-vibe { margin-left: 0; margin-bottom: 2px; }
.h2h-expand__hero-winrate .audit-hero__label {
  font-size: var(--text-micro);
  letter-spacing: 1.2px;
}
.h2h-expand__hero-winrate .audit-hero__number {
  font-family: var(--font-data);
  font-size: var(--text-3xl);
  color: var(--accent);
  font-feature-settings: "tnum";
  line-height: var(--leading-tight);
}

.h2h-vibe {
  display: inline-block;
  font-family: var(--font-mono);
  font-size: var(--text-2xs);
  font-weight: 600;
  letter-spacing: 1.2px;
  padding: 2px 8px;
  margin-left: 10px;
  border-radius: 3px;
  border: 1px solid currentColor;
  text-transform: uppercase;
}
.h2h-vibe--favorable { color: var(--positive); }
.h2h-vibe--nemesis   { color: var(--negative); }
.h2h-vibe--even      { color: var(--text-tertiary); }

/* Summary-row vibe chip — smaller, tinted variant of .h2h-vibe that sits
   inline next to the opponent name on the collapsed H2H row. A softer
   background tint (not just an outline) so the chip reads as a tag the
   eye can lock onto while scanning down the list. */
.h2h-row-chip {
  display: inline-flex;
  align-items: center;
  margin-left: var(--space-xs);
  padding: 1px 7px;
  font-family: var(--font-data);
  font-size: var(--text-micro);
  font-weight: 700;
  letter-spacing: 0.8px;
  text-transform: uppercase;
  border-radius: var(--radius-full);
  border: 1px solid transparent;
  line-height: var(--leading-relaxed);
  white-space: nowrap;
}

.h2h-row-chip--favorable {
  color: var(--positive);
  background: rgba(34, 197, 94, 0.1);
  border-color: rgba(34, 197, 94, 0.3);
}

.h2h-row-chip--nemesis {
  color: var(--negative);
  background: rgba(239, 68, 68, 0.1);
  border-color: rgba(239, 68, 68, 0.3);
}

/* Horizontal split bar under the hero — proportional win/draw/loss
   breakdown visible at a glance. Reuses the tokens from the profile
   H2H aggregate row's viz for consistency. */
.h2h-hero__split {
  margin-top: 14px;
  max-width: 520px;
}
.h2h-hero__split-bar {
  display: flex;
  height: 10px;
  border-radius: 5px;
  overflow: hidden;
  background: rgba(148, 163, 184, 0.1);
}
.h2h-hero__split-bar > span { display: block; transition: width 200ms ease; }
.h2h-hero__split-bar .split--win  { background: var(--positive); }
.h2h-hero__split-bar .split--draw { background: var(--warning); opacity: 0.7; }
.h2h-hero__split-bar .split--loss { background: var(--negative); }

.h2h-hero__split-legend {
  display: flex;
  gap: 18px;
  margin-top: 8px;
  font-family: var(--font-data);
  font-size: var(--text-2xs);
  color: var(--text-tertiary);
  letter-spacing: 0.3px;
  text-transform: uppercase;
}
.h2h-hero__split-legend > span::before {
  content: "";
  display: inline-block;
  width: 8px;
  height: 8px;
  border-radius: 2px;
  margin-right: 6px;
  vertical-align: middle;
}
.h2h-hero__split-legend .legend--win::before  { background: var(--positive); }
.h2h-hero__split-legend .legend--draw::before { background: var(--warning); }
.h2h-hero__split-legend .legend--loss::before { background: var(--negative); }

/* H2H match table: overrides .tourn-matches grid (which is sized for the
   7-column audit view) with column priorities tuned for this page:
   tournament wide (it's the point), rating pills compact. Selector doubles
   up to beat .tourn-matches source-order specificity. */
.tourn-matches.h2h-match-table {
  display: grid;
  width: 100%;
  grid-template-columns:
    96px     /* Date */
    minmax(240px, 1fr)  /* Tournament — the scan anchor */
    56px     /* Round */
    72px     /* Format */
    86px     /* Player rating */
    86px     /* Opponent rating */
    72px;    /* Result */
  padding: 0 20px 16px;
}

.tourn-matches.h2h-match-table .tourn-matches__header > span {
  font-size: var(--text-micro);
  padding: 12px 0 10px;
}

/* Header "Date" aligns with row date values — rows carry 12px left
   padding for the outcome stripe, so the header's first cell has to
   match or the column labels look off. */
.tourn-matches.h2h-match-table .tourn-matches__header > span:first-child {
  padding-left: 15px;
}

/* Player name headers — mono + primary color so the two competitors
   read as column subjects, not dimmed labels. No truncation: when the
   name is longer than the column, it ellipsizes inline. */
.h2h-match-table__name-col {
  font-family: var(--font-mono);
  font-size: var(--text-2xs);
  color: var(--text-primary) !important;
  letter-spacing: 0.3px;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* Per-row outcome accent — 3px left stripe on the first cell of each
   match row. display:contents parent means we can't box-shadow the
   whole row, so we stripe the first span and pad it. Color = outcome,
   matches site-wide grammar. */
.tourn-matches.h2h-match-table .h2h-match-row > span {
  padding: 12px 0;
  border-bottom: 1px solid rgba(148, 163, 184, 0.08);
  align-self: center;
}
.tourn-matches.h2h-match-table .h2h-match-row:last-child > span { border-bottom: none; }

.tourn-matches.h2h-match-table .h2h-match-row > span:first-child {
  padding-left: 12px;
  border-left: 3px solid transparent;
}
.h2h-match-row--win  > span:first-child { border-left-color: var(--positive) !important; }
.h2h-match-row--loss > span:first-child { border-left-color: var(--negative) !important; }
.h2h-match-row--draw > span:first-child { border-left-color: var(--warning)  !important; }

/* Tournament name cell — unconstrained line wrapping off by default,
   so long names truncate gracefully instead of exploding the row. */
.h2h-match-row__tourn {
  display: flex;
  align-items: center;
  gap: 8px;
  min-width: 0;
}
.h2h-match-row__tourn a {
  color: var(--text-primary);
  font-family: var(--font-body);
  font-size: var(--text-sm);
  font-weight: 500;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  flex: 1;
  min-width: 0;
}
.h2h-match-row__tourn a:hover { color: var(--accent); }

/* Tier chip next to the tournament name — compact, data-dense
   classification without taking a separate column. */
.h2h-match-row__tier {
  display: inline-flex;
  align-items: center;
  flex: 0 0 auto;
  font-family: var(--font-mono);
  font-size: var(--text-nano);
  font-weight: 600;
  letter-spacing: 0.6px;
  text-transform: uppercase;
  padding: 2px 6px;
  border-radius: 2px;
}
.h2h-match-row__tier--planetary { color: var(--positive); border: 1px solid var(--positive); }
.h2h-match-row__tier--sector    { color: var(--warning);  border: 1px solid var(--warning);  }
.h2h-match-row__tier--regional  { color: var(--accent);   border: 1px solid var(--accent);   }
.h2h-match-row__tier--galactic  { color: #A78BFA;         border: 1px solid #A78BFA;          }

/* Date cell — mono, tertiary, single line, right-aligned so numbers
   line up visually across rows. */
.tourn-matches.h2h-match-table .h2h-match-row .h2h-match-row__date {
  font-family: var(--font-data);
  font-size: var(--text-2xs);
  color: var(--text-tertiary);
  white-space: nowrap;
}

/* Rating pill cells — center the pill so the numbers align across
   the two player rating columns. */
.tourn-matches.h2h-match-table .h2h-match-row > span:nth-child(5),
.tourn-matches.h2h-match-table .h2h-match-row > span:nth-child(6) {
  display: flex;
  justify-content: center;
  align-items: center;
}

@media (max-width: 768px) {
  .tourn-matches.h2h-match-table {
    grid-template-columns: auto 1fr 72px;
    padding: 0 12px;
  }
  .tourn-matches.h2h-match-table .h2h-match-row > span:nth-child(n+3):nth-child(-n+6) {
    display: none;
  }
  .tourn-matches.h2h-match-table .tourn-matches__header > span:nth-child(n+3):nth-child(-n+6) {
    display: none;
  }
}

/* ─── Profile sticky header + section nav ─── */
.profile-sticky {
  position: sticky;
  top: 0;
  z-index: 20;
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 16px;
  flex-wrap: wrap;
  padding: 10px 0;
  margin-bottom: 20px;
  background: var(--bg);
  border-bottom: 1px solid var(--border);
  backdrop-filter: blur(6px);
}

/* Kill the breadcrumb's own bottom margin when it's inside the sticky row —
   the sticky row already handles vertical rhythm. */
.profile-sticky .breadcrumb { margin-bottom: 0; }

.profile-section-nav {
  display: flex;
  gap: 4px;
  flex-wrap: wrap;
}

.profile-section-nav__link {
  font-family: var(--font-data);
  font-size: var(--text-2xs);
  font-weight: 500;
  text-transform: uppercase;
  letter-spacing: 0.4px;
  color: var(--text-tertiary);
  padding: 5px 10px;
  border-radius: 5px;
  border: 1px solid transparent;
  text-decoration: none;
  transition: border-color 150ms ease, color 150ms ease;
}

.profile-section-nav__link:hover {
  color: var(--accent);
  border-color: var(--accent);
}

/* Anchored section targets get scroll-margin so clicks land below the
   sticky bar instead of having the heading tucked behind it. */
.section-anchor {
  scroll-margin-top: 72px;
}

/* Smooth scroll for in-page anchor jumps */
html { scroll-behavior: smooth; }

/* Styled horizontal rule between major profile sections (Rating History,
   Tournament History, Head-to-Head). Subtle accent highlight in the middle
   so it reads as a data-terminal section break, not a default gray line. */
.profile-section-divider {
  height: 0;
  margin: 36px 0;
  border: 0;
  border-top: 1px solid var(--border);
  background: transparent;
  position: relative;
}

.profile-section-divider::after {
  content: "";
  position: absolute;
  top: -1px;
  left: 50%;
  transform: translateX(-50%);
  width: 72px;
  height: 1px;
  background: var(--accent);
  opacity: 0.6;
}

/* Rating history section — chart wrapper is a block so the canvas height
   behaves. Format chips moved into the unified .profile-format-tabs. */
.rating-history-block { display: block; }
.rating-history-block .section-header { align-items: center; }

/* Empty-state overlay sits dead-center over the canvas when there's no
   data for the selected format. Chart still draws empty axes underneath
   so the layout doesn't reflow on tab switches. */
.rating-chart-wrap { position: relative; }
.rating-chart-empty {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  font-family: var(--font-data);
  font-size: var(--text-xs);
  letter-spacing: var(--tracking-wider);
  text-transform: uppercase;
  color: var(--text-tertiary);
  pointer-events: none;
  background: linear-gradient(180deg, rgba(11, 17, 32, 0) 0%, rgba(11, 17, 32, 0.55) 50%, rgba(11, 17, 32, 0) 100%);
}

/* ============================================================
   PROFILE FORMAT TABS — single switch driving Rating History,
   Tournament History, and Head-to-Head together. Designed to
   read clearly as a navigation control, not subtle filter chips.
   ============================================================ */

.profile-format-tabs {
  display: flex;
  align-items: stretch;
  gap: 0;
  margin: 0 0 28px;
  padding: 0;
  border-bottom: 1px solid var(--border);
  position: relative;
  flex-wrap: wrap;
}

.profile-format-tab {
  position: relative;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 6px;
  padding: 12px 22px;
  margin-bottom: -1px; /* overlap the bottom border so active tab's underline replaces it */
  font-family: var(--font-data);
  font-size: var(--text-sm);
  font-weight: 600;
  letter-spacing: var(--tracking-wide);
  text-transform: uppercase;
  color: var(--text-tertiary);
  text-decoration: none;
  border: 1px solid transparent;
  border-bottom: 2px solid transparent;
  background: transparent;
  cursor: pointer;
  transition: color 150ms ease, border-color 150ms ease, background 150ms ease;
}

.profile-format-tab:hover {
  color: var(--text-primary);
  background: var(--accent-wash);
  border-bottom-color: var(--text-tertiary);
}

.profile-format-tab--active {
  color: var(--accent);
  background: linear-gradient(180deg, var(--accent-subtle) 0%, var(--accent-wash) 100%);
  border-bottom-color: var(--accent);
  text-shadow: 0 0 12px var(--accent-glow);
}

.profile-format-tab--active::after {
  content: "";
  position: absolute;
  bottom: -2px;
  left: 0;
  right: 0;
  height: 2px;
  background: var(--accent);
  box-shadow: 0 0 8px var(--accent-glow);
}

/* Right-side hint that explains the tabs drive every section below.
   Pushed to the right with margin-left:auto so it stays out of the
   way of the tab row but stays informative. */
.profile-format-tabs__hint {
  margin-left: auto;
  align-self: center;
  padding: 0 6px 8px;
  font-family: var(--font-data);
  font-size: var(--text-micro);
  color: var(--text-tertiary);
  letter-spacing: var(--tracking-wide);
  text-transform: uppercase;
}

@media (max-width: 768px) {
  .profile-format-tab { padding: 10px 14px; font-size: var(--text-xs); }
  .profile-format-tabs__hint { display: none; }
}

/* ============================================================
   PAGINATION NAV — custom CSS, no Tailwind.
   ============================================================ */

.pagy-nav {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: center;
  gap: 6px;
  margin: 20px 0 8px;
  padding: 12px 0 0;
  font-family: var(--font-data);
}

.pagy-nav__link {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 32px;
  height: 32px;
  padding: 0 10px;
  font-size: var(--text-xs);
  font-weight: 600;
  color: var(--text-secondary);
  background: var(--card);
  border: 1px solid var(--border);
  border-radius: 5px;
  text-decoration: none;
  transition: border-color 150ms ease, color 150ms ease, background 150ms ease;
}

.pagy-nav__link:hover {
  border-color: var(--accent);
  color: var(--accent);
}

.pagy-nav__link--current {
  background: var(--accent);
  border-color: var(--accent);
  color: var(--bg);
  box-shadow: 0 0 10px var(--accent-glow);
}

.pagy-nav__link--arrow { padding: 0 12px; }

.pagy-nav__link--disabled,
.pagy-nav__link--disabled:hover {
  color: var(--text-tertiary);
  border-color: var(--border);
  background: transparent;
  cursor: not-allowed;
  box-shadow: none;
}

.pagy-nav__gap {
  color: var(--text-tertiary);
  padding: 0 4px;
}

/* "Showing X–Y of Z" sits centered on its own line below the page links.
   `flex-basis: 100%` forces a wrap so the hint never competes with the
   numbered controls for horizontal space. */
.pagy-nav__hint {
  flex-basis: 100%;
  text-align: center;
  margin-top: 8px;
  font-size: var(--text-2xs);
  color: var(--text-tertiary);
  letter-spacing: 0.04em;
}

/* ============================================================
   H2H EMPTY STATE — terminal "no signal" blank slate.
   Direct copy, no emojis, sci-fi data terminal vibe.
   ============================================================ */

.h2h-empty {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 14px;
  padding: 36px 24px 40px;
  background: var(--surface);
  border: 1px dashed var(--border);
  border-radius: 8px;
  text-align: center;
}

.h2h-empty__reticle {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  font-family: var(--font-data);
  color: var(--text-tertiary);
  font-size: var(--text-sm);
  letter-spacing: 0.4em;
  text-transform: uppercase;
}

.h2h-empty__bracket {
  color: var(--accent);
  font-size: var(--text-lg);
  text-shadow: 0 0 6px var(--accent-glow);
}

.h2h-empty__core {
  position: relative;
}

.h2h-empty__core::after {
  content: "";
  display: inline-block;
  width: 8px;
  height: 8px;
  margin-left: 8px;
  border-radius: 50%;
  background: var(--accent);
  box-shadow: 0 0 8px var(--accent-glow);
  vertical-align: middle;
  animation: h2h-pulse 1.8s ease-in-out infinite;
}

@keyframes h2h-pulse {
  0%, 100% { opacity: 0.4; }
  50% { opacity: 1; }
}

.h2h-empty__copy {
  display: flex;
  flex-direction: column;
  gap: 6px;
  max-width: 480px;
}

.h2h-empty__headline {
  font-family: var(--font-display);
  font-size: var(--text-md);
  font-weight: 600;
  color: var(--text-primary);
}

.h2h-empty__sub {
  font-family: var(--font-body);
  font-size: var(--text-sm);
  color: var(--text-tertiary);
  line-height: var(--leading-relaxed);
}

/* Profile hero: credible-interval ± width. Sits inline inside the caption
   row alongside the format label and confidence % — flex gap in the parent
   handles spacing, so no local margin needed. 12px matches .profile-hero__conf
   so neither signal dominates; confidence % is the more interpretable of
   the two and leads the caption via document order. */
.profile-hero__ci {
  font-family: var(--font-data);
  font-size: var(--text-xs);
  color: var(--text-tertiary);
  font-weight: 400;
}

/* Tournament history: expandable per-match audit trail */
.tourn-row-wrapper { display: block; }

.tourn-row { cursor: pointer; }
.tourn-row[aria-expanded="true"] { border-bottom-left-radius: 0; border-bottom-right-radius: 0; }

/* Affordance chevron — sits to the left of every row, points right when
   collapsed and rotates to point down when the <details> is open. The
   native disclosure marker is suppressed so this is the only indicator. */
.tourn-row { list-style: none; }
.tourn-row::-webkit-details-marker { display: none; }

.tourn-row__chevron {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 20px;
  flex-shrink: 0;
  margin-right: 8px;
  color: var(--text-tertiary);
  transition: transform 150ms ease, color 150ms ease;
}

.tourn-row:hover .tourn-row__chevron { color: var(--accent); }

details[open] > .tourn-row > .tourn-row__chevron,
details[open] > .h2h-row > .tourn-row__chevron {
  transform: rotate(90deg);
  color: var(--accent);
}

/* H2H rows reuse the tourn-row disclosure pattern so the chevron and
   expansion behave identically. The marker is suppressed, the summary
   takes the flex row layout from .rank-row, and the bottom radius
   opens up when the panel expands so the two read as one unit. */
.h2h-row { cursor: pointer; list-style: none; }
.h2h-row::-webkit-details-marker { display: none; }
.h2h-row:hover .tourn-row__chevron { color: var(--accent); }
details[open] > .h2h-row { border-bottom-left-radius: 0; border-bottom-right-radius: 0; }

/* Header alignment column for the chevron — same width + margin as the
   chevron column so the Event header lines up with the event names. */
.th-chevron {
  width: 20px;
  flex-shrink: 0;
  margin-right: 8px;
}

/* Expansion panel — wraps the explainer + metric strip + matches grid so
   the audit info lives on the profile directly. Shares surface + border
   with the summary row so the two read as a single expanded unit. Flex
   column with a uniform gap between children controls vertical rhythm.
   All children sit at 20px inset matching the summary row's padding so
   columns and content align vertically from summary through expand. */
.tourn-expand {
  display: flex;
  flex-direction: column;
  gap: var(--space-sm);
  padding: var(--space-sm) 20px;
  background: var(--surface);
  border: 1px solid var(--border);
  border-top: none;
  border-bottom-left-radius: 6px;
  border-bottom-right-radius: 6px;
  overflow: hidden;
}

/* Navigation row at the top of an expanded tournament card. These links
   used to live inside the <summary> as <a> tags, which triggered Chrome's
   "interactive element inside <summary>" warning (the summary itself is
   the toggle — nested links make click intent ambiguous). Moving them
   into the expand container keeps both destinations available without
   breaking the details/summary contract. */
.tourn-expand__links {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-sm);
  font-size: var(--text-xs);
  font-family: var(--font-data);
  letter-spacing: 0.2px;
  text-transform: uppercase;
  padding-bottom: var(--space-xs);
  border-bottom: 1px solid var(--border);
}

.tourn-expand__link-primary {
  color: var(--accent);
}

.tourn-expand__link-external {
  color: var(--text-tertiary);
  text-decoration: none;
  transition: color 150ms ease;
}

.tourn-expand__link-external:hover,
.tourn-expand__link-external:focus-visible {
  color: var(--accent);
}

.tourn-expand__meta {
  padding: 0;
}

/* Deep-link at the bottom of an expanded tournament row to the per-event
   audit page. Small, right-aligned, arrow cues "keep going" without
   fighting the section-header hierarchy above. */
.tourn-expand__cta {
  margin: 0;
  text-align: right;
  font-size: var(--text-xs);
  font-family: var(--font-data);
}

.tourn-expand__cta-link {
  color: var(--accent);
  letter-spacing: 0.2px;
  text-transform: uppercase;
}

/* Strip chrome off the matches grid and break it out to the panel's
   full width so the separator line runs edge-to-edge. The grid's own
   internal padding re-creates the 20px inset for its cells. */
.tourn-expand .tourn-matches {
  background: transparent;
  border: none;
  border-top: 1px solid var(--border);
  border-radius: 0;
  margin: 0 -20px;
}

/* Inline metric-strip modifier: kill the section-level bottom margin
   and tighten gaps since we're already inside a padded panel. */
.metric-strip--inline {
  margin: 0;
  padding: 0;
  gap: var(--space-xs) var(--space-xl);
}

/* After an open details row, give the next sibling a beat of space so
   the expand panel doesn't visually merge into the following row. The
   base table-container gap of 2px is right for collapsed rows but too
   tight when an expand panel sits above the next row. */
.tourn-row-wrapper[open],
.h2h-row-wrapper[open] {
  margin-bottom: var(--space-sm);
}

.tourn-matches {
  background: var(--surface);
  border: 1px solid var(--border);
  border-top: none;
  border-bottom-left-radius: 6px;
  border-bottom-right-radius: 6px;
  padding: var(--space-sm) 20px;
  font-family: var(--font-data);
  font-size: var(--text-xs);
  color: var(--text-secondary);
  display: grid;
  /* Columns: Round | HRI | Opponent | Result | Game W-L | Multiplier | Δ | Type
     The Δ column (v5.3) sits between Multiplier and Type so players can see the
     per-match rating change approximation right next to the multiplier that
     produced it. See PlayersHelper#per_match_rating_delta for the caveat. */
  grid-template-columns: 50px 64px 1fr 70px 60px 60px 60px 70px;
  gap: 6px var(--space-sm);
  align-items: center;
}

.tourn-matches__header {
  display: contents;
  font-size: var(--text-micro);
  color: var(--text-tertiary);
  text-transform: uppercase;
  letter-spacing: 0.4px;
}

.tourn-matches__header > span { padding-bottom: 6px; border-bottom: 1px solid var(--border); }

.tourn-match { display: contents; }
.tourn-match > span { padding: 4px 0; }

.tourn-match__result--win { color: var(--positive); }
.tourn-match__result--loss { color: var(--negative); }
.tourn-match__result--draw { color: var(--warning); }

.tourn-match__mult {
  color: var(--text-tertiary);
  font-size: var(--text-2xs);
}

/* Per-match Δ pill (v5.3). Approximate per-match rating change; column
   header carries the tooltip that names the approximation. Color follows
   positive/negative/neutral semantics the rest of the profile uses for
   rating movement so the eye reads the direction immediately. */
.tourn-match__delta-cell { text-align: right; }

.tourn-match__delta {
  display: inline-block;
  min-width: 36px;
  padding: 1px 6px;
  border-radius: 4px;
  font-family: var(--font-data);
  font-size: var(--text-2xs);
  font-feature-settings: "tnum";
  text-align: right;
}

.tourn-match__delta--positive { color: var(--positive); background: color-mix(in oklch, var(--positive) 10%, transparent); }
.tourn-match__delta--negative { color: var(--negative); background: color-mix(in oklch, var(--negative) 10%, transparent); }
.tourn-match__delta--neutral  { color: var(--text-tertiary); }
.tourn-match__delta--none     { color: var(--text-tertiary); }

.tourn-match__opponent a {
  color: var(--text-primary);
  font-family: var(--font-body);
  font-size: var(--text-xs);
}

.tourn-match__hri {
  display: flex;
  align-items: center;
  justify-content: center;
}

/* Column alignment — shared by profile inline expansion and tournament breakdown.
   Columns: 1 Round (left) | 2 HRI (center) | 3 Opponent (left) |
            4 Result (center) | 5 Game W-L (right) | 6 Multiplier (right) | 7 Type (right) */
.tourn-matches__header > span:nth-child(2),
.tourn-matches__header > span:nth-child(4) {
  text-align: center;
}

.tourn-matches__header > span:nth-child(n+5) {
  text-align: right;
}

.tourn-match > span:nth-child(4) {
  text-align: center;
}

.tourn-match > span:nth-child(n+5) {
  text-align: right;
}

.tourn-match__hri-pill {
  display: inline-flex;
  align-items: center;
  padding: 2px 8px;
  font-family: var(--font-data);
  font-size: var(--text-2xs);
  font-feature-settings: "tnum";
  color: var(--text-secondary);
  background: rgba(148, 163, 184, 0.06);
  border: 1px solid var(--border);
  border-radius: 10px;
  cursor: help;
  transition: border-color 150ms ease, color 150ms ease;
  white-space: nowrap;
}

.tourn-match__hri-pill:hover {
  color: var(--text-primary);
  border-color: var(--text-tertiary);
}

.tourn-match__hri--none {
  color: var(--text-tertiary);
  font-family: var(--font-data);
  font-size: var(--text-2xs);
}

/* ============================================================
   RATING MATH PANEL — the framed "How this rating change was
   computed" section. Distinct from the Matches table below it
   so the two don't visually collapse into one long table. The
   left accent rail marks this as the insight zone; the mono
   kicker and tabular grid carry the data-terminal voice.
   ============================================================ */

.rating-math-panel {
  margin: 32px 0 28px;
  padding: 20px 24px 20px 28px;
  background: var(--accent-wash);
  border-left: 2px solid var(--accent);
  border-radius: 0 var(--radius-md) var(--radius-md) 0;
}

.rating-math-panel__kicker {
  margin: 0 0 2px;
  font-family: var(--font-data);
  font-size: var(--text-micro);
  letter-spacing: 0.8px;
  text-transform: uppercase;
  color: var(--accent-hover);
  font-feature-settings: "tnum";
}

.rating-math-panel__heading {
  margin: 0 0 12px;
  font-family: var(--font-display);
  font-size: var(--text-2xl);
  font-weight: 700;
  color: var(--text-primary);
  letter-spacing: -0.01em;
}

.rating-math-panel__lede {
  margin: 0 0 18px;
  max-width: 72ch;
  font-family: var(--font-body);
  font-size: var(--text-base);
  line-height: var(--leading-relaxed);
  color: var(--text-secondary);
}

.rating-math-panel__lede strong { color: var(--text-primary); }
.rating-math-panel__lede em { color: var(--text-primary); font-style: italic; }

.rating-math-panel__inline-data {
  display: inline-block;
  padding: 0 6px;
  font-family: var(--font-data);
  font-size: var(--text-xs);
  font-feature-settings: "tnum";
  color: var(--accent-hover);
  background: var(--accent-subtle);
  border-radius: 4px;
}

/* v5.3 size-band callout. Sits between the heading and the lede, amber
   annotation style so it reads as "context for what you're about to see"
   rather than competing with the data below. Only shown for Planetary
   events that actually took a size-band reduction (Micro or Small). */
.rating-math-panel__size-band-alert {
  margin: 0 0 18px;
  max-width: 72ch;
}

/* "Full rivalry history →" link inside the H2H expansion on the profile.
   Sits below the win-rate number, accent-colored to read as the call-to-
   action it is, but at small text size so it doesn't compete with the
   data above it. */
.h2h-expand__full-link {
  display: block;
  margin-top: 8px;
  font-family: var(--font-data);
  font-size: var(--text-2xs);
  font-weight: 500;
  letter-spacing: 0.04em;
  color: var(--accent);
  text-decoration: none;
}

.h2h-expand__full-link:hover { text-decoration: underline; }

/* Opponent name in a collapsed H2H row, made into a link to the
   /players/A/vs/B drill-in view. Inherits .player-name typography +
   color so the row looks visually unchanged at rest, but takes the
   accent on hover and underlines on focus to signal interactivity.
   Sits inside <summary>; browsers route the click to the anchor href
   instead of toggling the <details>, so clicking the name navigates
   without expanding the row. */
.h2h-row__name-link {
  text-decoration: none;
  color: inherit;
  border-bottom: 1px dashed transparent;
  transition: color 120ms ease, border-color 120ms ease;
}

.h2h-row__name-link:hover {
  color: var(--accent);
  border-bottom-color: var(--accent);
}

.h2h-row__name-link:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
  border-radius: 2px;
}

/* ─── Player vs. Player drill-in (/players/:slug/vs/:opponent_slug) ─── */

/* HERO STRIP — the rivalry's identity in one look. Three-column matchup
   row (left player | center vibe + win-rate | right player), then a
   full-width split bar, then a meta row with count + flip-perspective. */
.player-vs .vs-hero {
  display: flex;
  flex-direction: column;
  gap: var(--space-md);
  padding: var(--space-lg) 0;
  border-bottom: 1px solid var(--border);
  margin-bottom: var(--space-lg);
}

.vs-hero__matchup {
  display: grid;
  grid-template-columns: 1fr auto 1fr;
  align-items: center;
  gap: var(--space-md);
}

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

.vs-hero__player--left  { align-items: flex-start; text-align: left;  }
.vs-hero__player--right { align-items: flex-end;   text-align: right; }

.vs-hero__player-name {
  font-family: var(--font-display);
  font-size: var(--text-2xl);
  font-weight: 700;
  color: var(--text-primary);
  text-decoration: none;
  letter-spacing: -0.01em;
}

.vs-hero__player-name:hover { color: var(--accent); }

.vs-hero__player-rating {
  font-family: var(--font-data);
  font-size: var(--text-base);
  font-weight: 600;
  color: var(--accent);
  font-feature-settings: "tnum";
}

.vs-hero__player-rd {
  margin-left: 6px;
  font-size: var(--text-2xs);
  font-weight: 400;
  color: var(--text-tertiary);
}

.vs-hero__center {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 6px;
  padding: 0 var(--space-lg);
}

/* Vibe badge. Same vocabulary as the H2H section on the profile —
   FAVORABLE / NEMESIS / EVEN — but rendered larger and as the visual
   anchor of the page since this is a single-rivalry deep dive. */
.vs-hero__vibe {
  font-family: var(--font-data);
  font-size: var(--text-2xs);
  font-weight: 700;
  letter-spacing: 0.14em;
  padding: 4px 12px;
  border-radius: 999px;
  border: 1px solid currentColor;
  text-transform: uppercase;
}

.vs-hero__vibe--favorable {
  color: var(--positive);
  background: color-mix(in srgb, var(--positive) 10%, transparent);
}
.vs-hero__vibe--nemesis {
  color: var(--negative);
  background: color-mix(in srgb, var(--negative) 10%, transparent);
}
.vs-hero__vibe--even {
  color: var(--text-tertiary);
}

.vs-hero__win-pct {
  font-family: var(--font-data);
  font-size: var(--text-8xl);
  font-weight: 800;
  line-height: var(--leading-tight);
  color: var(--accent);
  text-shadow: 0 0 16px var(--accent-glow);
  letter-spacing: var(--tracking-tight);
  font-feature-settings: "tnum";
}

.vs-hero__win-pct-symbol {
  font-size: var(--text-5xl);
  font-weight: 600;
  margin-left: 2px;
  color: var(--accent-hover);
}

.vs-hero__win-pct-label {
  font-family: var(--font-data);
  font-size: var(--text-micro);
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--text-tertiary);
}

/* The full-width split bar — taller and more prominent than the slim
   version on the profile expansion. Shows W/D/L counts overlaid when
   each segment is wide enough to fit them legibly. */
.vs-hero__split-row { padding: 0; }

.vs-hero__split {
  display: flex;
  width: 100%;
  height: 36px;
  border-radius: var(--radius-sm);
  overflow: hidden;
  background: var(--border);
  font-family: var(--font-data);
  font-feature-settings: "tnum";
}

.vs-hero__split-segment {
  display: flex;
  align-items: center;
  justify-content: center;
  height: 100%;
  font-size: var(--text-xs);
  font-weight: 700;
  color: white;
  letter-spacing: 0.04em;
  transition: width 200ms ease;
}

.vs-hero__split-segment--win  { background: var(--positive); }
.vs-hero__split-segment--draw { background: var(--text-tertiary); }
.vs-hero__split-segment--loss { background: var(--negative); }

.vs-hero__meta {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2xs);
  align-items: baseline;
  font-family: var(--font-data);
  font-size: var(--text-xs);
  color: var(--text-tertiary);
}

.vs-hero__sep { opacity: 0.5; }
.vs-hero__count       { font-feature-settings: "tnum"; color: var(--text-secondary); }
.vs-hero__date-range  { font-feature-settings: "tnum"; }

.vs-hero__flip {
  color: var(--accent);
  text-decoration: none;
  font-weight: 500;
}

.vs-hero__flip:hover { text-decoration: underline; }

.vs-empty {
  padding: var(--space-lg) var(--space-md);
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  text-align: center;
  color: var(--text-secondary);
}

/* INSIGHT CARDS — two-column on wide viewports, stacked on narrow.
   Houses the next-meeting prediction and the storybook-moments cards. */
.vs-insight-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(360px, 1fr));
  gap: var(--space-md);
  margin-bottom: var(--space-lg);
}

.vs-card {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  padding: var(--space-md);
}

.vs-card__head { margin-bottom: var(--space-sm); }

.vs-card__kicker {
  display: block;
  font-family: var(--font-data);
  font-size: var(--text-micro);
  font-weight: 600;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--text-tertiary);
  margin-bottom: 4px;
}

.vs-card__heading {
  margin: 0;
  font-family: var(--font-display);
  font-size: var(--text-lg);
  font-weight: 600;
  color: var(--text-primary);
  letter-spacing: -0.01em;
}

/* PREDICTION CARD — Glicko expected score for the next meeting. The
   horizontal bar is the dominant visual; the percentages flank it and
   the player names sit above so the reader gets a quick "X favorite at
   Y%" without parsing labels. */
.vs-prediction__body {
  display: flex;
  flex-direction: column;
  gap: var(--space-sm);
}

.vs-prediction__row {
  display: grid;
  grid-template-columns: 1fr auto auto 1fr;
  align-items: center;
  gap: var(--space-sm);
}

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

.vs-prediction__side--player { align-items: flex-start; }
.vs-prediction__side--opp    { align-items: flex-end; text-align: right; }

.vs-prediction__name {
  font-family: var(--font-body);
  font-size: var(--text-sm);
  font-weight: 600;
  color: var(--text-primary);
}

.vs-prediction__rating {
  font-family: var(--font-data);
  font-size: var(--text-xs);
  color: var(--text-secondary);
  font-feature-settings: "tnum";
}

.vs-prediction__rd {
  margin-left: 4px;
  font-size: var(--text-micro);
  color: var(--text-tertiary);
}

.vs-prediction__pct {
  display: flex;
  flex-direction: column;
  align-items: center;
}

.vs-prediction__pct-num {
  font-family: var(--font-data);
  font-size: var(--text-4xl);
  font-weight: 800;
  line-height: var(--leading-tight);
  font-feature-settings: "tnum";
  letter-spacing: var(--tracking-tight);
}

.vs-prediction__pct--player .vs-prediction__pct-num { color: var(--accent); }
.vs-prediction__pct--opp    .vs-prediction__pct-num { color: var(--text-secondary); }

.vs-prediction__bar {
  display: flex;
  width: 100%;
  height: 8px;
  border-radius: 999px;
  overflow: hidden;
  background: var(--border);
}

.vs-prediction__bar-segment--player { background: var(--accent); }
.vs-prediction__bar-segment--opp    { background: var(--text-tertiary); }

.vs-prediction__caveat {
  margin: 0;
  font-family: var(--font-body);
  font-size: var(--text-xs);
  line-height: var(--leading-relaxed);
  color: var(--text-tertiary);
}

/* STORY CARD — list of derived "moments" from the rivalry. Each row is
   one stat: kicker label up top, big value, descriptive line beneath
   that links into the corresponding tournament audit. */
.vs-story__list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  gap: var(--space-sm);
}

.vs-story__entry {
  display: grid;
  grid-template-columns: 1fr;
  gap: 2px;
  padding: var(--space-2xs) 0;
  border-bottom: 1px dashed var(--border);
}

.vs-story__entry:last-child { border-bottom: none; }

.vs-story__kicker {
  font-family: var(--font-data);
  font-size: var(--text-nano);
  font-weight: 600;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--text-tertiary);
}

.vs-story__value {
  font-family: var(--font-data);
  font-size: var(--text-md);
  font-weight: 700;
  color: var(--accent);
  font-feature-settings: "tnum";
}

.vs-story__label {
  font-family: var(--font-body);
  font-size: var(--text-sm);
  color: var(--text-secondary);
  text-decoration: none;
}

a.vs-story__label:hover {
  color: var(--accent);
  text-decoration: underline;
}

.vs-per-format {
  margin: var(--space-md) 0 var(--space-lg);
}

.vs-per-format__grid {
  display: grid;
  gap: var(--space-xs);
  grid-template-columns: repeat(auto-fit, minmax(160px, 1fr));
  margin-top: var(--space-sm);
}

.vs-per-format__row {
  display: grid;
  grid-template-columns: minmax(70px, auto) 1fr auto auto;
  align-items: baseline;
  gap: var(--space-sm);
  padding: var(--space-xs) var(--space-sm);
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
}

.vs-per-format__pct {
  font-family: var(--font-data);
  font-size: var(--text-xs);
  font-weight: 600;
  color: var(--accent);
  font-feature-settings: "tnum";
}

.vs-per-format__count {
  font-family: var(--font-data);
  font-size: var(--text-2xs);
  color: var(--text-tertiary);
}

.vs-per-format__label {
  font-family: var(--font-data);
  font-size: var(--text-2xs);
  letter-spacing: var(--tracking-wider);
  text-transform: uppercase;
  color: var(--text-tertiary);
}

.vs-per-format__record {
  font-family: var(--font-data);
  font-size: var(--text-base);
  font-feature-settings: "tnum";
  color: var(--text-secondary);
}

.vs-per-format__win  { color: var(--positive); font-weight: 600; }
.vs-per-format__loss { color: var(--negative); font-weight: 600; }
.vs-per-format__draw { color: var(--text-secondary); font-weight: 600; }

.vs-matches__tourn a {
  color: var(--text-primary);
  text-decoration: none;
}

.vs-matches__tourn a:hover { color: var(--accent); }

/* Override the inherited .tourn-matches__header nth-child rules — those
   were calibrated for a different table layout (col 2 was a centered
   numeric HRI cell). On the vs-matches grid, col 2 is the Tournament
   name (text, left-aligned data) so the header needs to start-align too,
   otherwise header and data drift visually. */
.tourn-matches.vs-matches .tourn-matches__header > span:nth-child(2) {
  text-align: start;
}

.vs-matches__format,
.vs-matches__round {
  font-family: var(--font-data);
  font-size: var(--text-xs);
  color: var(--text-tertiary);
  font-feature-settings: "tnum";
}

.vs-matches__pre-match {
  display: inline-flex;
  align-items: baseline;
  gap: 6px;
  font-family: var(--font-data);
  font-size: var(--text-xs);
  color: var(--text-secondary);
  font-feature-settings: "tnum";
}

.vs-matches__rating-pill {
  display: inline-block;
  padding: 1px 6px;
  background: var(--accent-subtle);
  color: var(--accent-hover);
  border-radius: 3px;
  font-weight: 500;
}

.vs-matches__rating-sep {
  font-size: var(--text-micro);
  letter-spacing: var(--tracking-wider);
  color: var(--text-tertiary);
  text-transform: uppercase;
}

.vs-matches__rating-empty {
  color: var(--text-tertiary);
  opacity: 0.6;
}

.vs-matches__result {
  display: inline-flex;
  align-items: baseline;
  gap: 6px;
  font-weight: 600;
}

.vs-matches__game-score {
  font-family: var(--font-data);
  font-size: var(--text-xs);
  color: var(--text-tertiary);
  font-weight: 500;
  font-feature-settings: "tnum";
}

/* Bulleted lists inside How-It-Works card descriptions. Default UL styles
   are oddly indented in this theme; tighten vertical rhythm and pull the
   bullet flush with the description column so it composes with the
   surrounding prose. */
.about-list {
  margin: 8px 0 0;
  padding-left: 20px;
  color: var(--text-secondary);
}

.about-list li {
  margin: 6px 0;
  line-height: var(--leading-relaxed);
}

.about-list li::marker {
  color: var(--text-tertiary);
}

/* The phase table sits inside the panel — override the outer table chrome
   so it blends into the panel surface instead of looking like a second
   card inside the frame. */
.rating-math-panel__table {
  background: transparent;
  border: none;
  border-top: 1px solid var(--accent-soft);
  border-radius: 0;
  padding: 8px 0 2px;
  margin: 0;
}

/* Right-align all numeric columns (Record, Raw Glicko-2, Multiplier,
   Applied, Running). The default .tourn-matches rule only right-aligns
   columns 5+, but the phase breakdown has numeric data in columns 2-6
   so every column except Phase (col 1) should be tabular-right. */
.rating-math-panel__table .tourn-matches__header > span:nth-child(n+2),
.rating-math-panel__table .tourn-match > span:nth-child(n+2) {
  text-align: right;
}

.rating-math-panel__phase {
  display: inline-flex;
  align-items: center;
  gap: 8px;
}

.rating-math-panel__phase strong {
  font-family: var(--font-body);
  font-size: var(--text-sm);
  color: var(--text-primary);
}

.rating-math-panel__phase-count {
  color: var(--text-tertiary);
  font-size: var(--text-2xs);
  font-family: var(--font-data);
  font-feature-settings: "tnum";
}

.rating-math-panel__mult {
  color: var(--text-secondary);
  font-feature-settings: "tnum";
}

/* Multiplier-intensity phase dots. Swiss at base tier is the faintest;
   a Finals-winner match (highest multiplier) is the brightest. Visual
   encoding of "how much this phase amplified every point of surprise." */
.phase-dot {
  display: inline-block;
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: var(--accent);
  flex-shrink: 0;
}
.phase-dot--faint { background: var(--accent-soft); }
.phase-dot--soft  { background: var(--accent-mid); }
.phase-dot--mid   { background: var(--accent-strong); }
.phase-dot--hot   { background: var(--accent-hot); box-shadow: 0 0 6px var(--accent-glow); }

/* Full-width separator rule above the Total row — spans every grid column
   as one continuous line instead of per-cell borders with column-gap breaks. */
.rating-math-panel__separator {
  grid-column: 1 / -1;
  border-top: 1px solid var(--accent-soft);
  margin: 8px 0 6px;
}

.rating-math-panel__total > span {
  font-family: var(--font-data);
  font-feature-settings: "tnum";
}

.rating-math-panel__total > span:first-child strong {
  font-family: var(--font-body);
  font-size: var(--text-sm);
  color: var(--text-primary);
  text-transform: uppercase;
  letter-spacing: 0.6px;
}

.rating-math-panel__caveat {
  margin: 16px 0 0;
  padding-top: 14px;
  border-top: 1px dashed var(--border);
  font-family: var(--font-body);
  font-size: var(--text-xs);
  line-height: var(--leading-relaxed);
  color: var(--text-tertiary);
}

/* ============================================================
   MATCH HIGHLIGHTS — compact "biggest upset / costliest loss"
   callouts above the matches table. Two events of the tournament
   that moved the rating the most, given first-class surface above
   the row-level scan. Earned accent color — these are the rating's
   two most consequential moments.
   ============================================================ */

.match-highlights {
  display: flex;
  flex-wrap: wrap;
  gap: 10px;
  margin: 0 0 14px;
}

.match-highlight {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  padding: 8px 14px;
  background: var(--surface);
  border: 1px solid var(--border);
  border-left: 2px solid;
  border-radius: var(--radius-sm);
  font-family: var(--font-body);
  font-size: var(--text-sm);
  color: var(--text-secondary);
  flex: 1 1 auto;
  min-width: 280px;
}

.match-highlight--upset  { border-left-color: var(--positive); }
.match-highlight--fumble { border-left-color: var(--negative); }

.match-highlight__label {
  font-family: var(--font-data);
  font-size: var(--text-micro);
  letter-spacing: 0.8px;
  text-transform: uppercase;
  color: var(--text-tertiary);
  white-space: nowrap;
}

.match-highlight--upset  .match-highlight__label { color: var(--positive); }
.match-highlight--fumble .match-highlight__label { color: var(--negative); }

.match-highlight__detail {
  color: var(--text-secondary);
}

.match-highlight__detail strong { color: var(--text-primary); }

.match-highlight__rating {
  display: inline-block;
  padding: 1px 6px;
  margin-left: 4px;
  font-family: var(--font-data);
  font-size: var(--text-2xs);
  font-feature-settings: "tnum";
  color: var(--text-tertiary);
  background: rgba(148, 163, 184, 0.06);
  border: 1px solid var(--border);
  border-radius: 10px;
}

.match-highlight__data {
  font-family: var(--font-data);
  font-size: var(--text-xs);
  font-feature-settings: "tnum";
  color: var(--text-primary);
}

.match-highlight__sep {
  color: var(--text-tertiary);
  padding: 0 2px;
}

/* Inline row marker that points out the upset / costliest-loss rows in
   the match table. Tiny triangles carry the same semantics as rating
   deltas — up = positive surprise, down = negative. */
.match-flag {
  display: inline-block;
  font-size: var(--text-nano);
  margin-right: 4px;
  line-height: var(--leading-tight);
  vertical-align: baseline;
  cursor: help;
}

.match-flag--upset  { color: var(--positive); }
.match-flag--fumble { color: var(--negative); }

@media (max-width: 768px) {
  .tourn-matches {
    grid-template-columns: auto auto 1fr;
    gap: 4px 10px;
  }
  .tourn-matches__header { display: none; }
  .tourn-match > span { font-size: var(--text-2xs); }
}

/* ============================================================
   FEEDBACK — modal + form + nav button + flash
   ============================================================ */

/* Nav-bar Feedback button: same visual weight as a nav link but styled
   as a button so a11y reads it as an action, not a destination. */
.nav-link--feedback {
  background: transparent;
  border: 1px solid var(--border);
  padding: 4px 12px;
  border-radius: 5px;
  cursor: pointer;
  transition: border-color 150ms ease, color 150ms ease, background 150ms ease;
}
.nav-link--feedback:hover {
  color: var(--accent);
  border-color: var(--accent);
  background: var(--accent-wash);
}

/* Right-edge cluster in the sticky header: the section-jump nav plus the
   standalone Report Issue action. The action is NOT inside the <nav>, so
   screen-readers don't announce a modal trigger as a profile section. */
.profile-sticky__tail {
  display: flex;
  align-items: center;
  gap: 12px;
  flex-wrap: wrap;
}

/* Report Issue — deliberately different from the section-jump anchors so
   a keyboard tabber can tell an action from a jump. Ghost-button styling
   with a hairline border that activates on hover. */
.profile-sticky__report {
  font-family: var(--font-data);
  font-size: var(--text-2xs);
  font-weight: 500;
  text-transform: uppercase;
  letter-spacing: 0.4px;
  color: var(--text-tertiary);
  background: transparent;
  padding: 5px 10px;
  border: 1px solid var(--border);
  border-radius: 5px;
  cursor: pointer;
  transition: border-color 150ms ease, color 150ms ease;
}

.profile-sticky__report:hover,
.profile-sticky__report:focus-visible {
  color: var(--accent);
  border-color: var(--accent);
}

/* Modal — native <dialog> element. Uses the data-terminal aesthetic to
   match the rest of the site. ::backdrop blurs the page underneath.
   `inset: 0; margin: auto` centers horizontally + vertically (Chrome/Edge
   defaults handle this for un-styled <dialog> but we override `display`,
   so we set it explicitly). */
.feedback-modal[open] {
  display: block;
  position: fixed;
  inset: 0;
  margin: auto;
  padding: 0;
  width: min(540px, calc(100vw - 24px));
  max-height: calc(100vh - 48px);
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 8px;
  color: var(--text-primary);
  box-shadow: 0 20px 60px rgba(0, 0, 0, 0.5), 0 0 0 1px var(--accent-soft);
  overflow: hidden;
}

.feedback-modal::backdrop {
  background: rgba(11, 17, 32, 0.72);
  backdrop-filter: blur(4px);
}

.feedback-modal__inner {
  display: flex;
  flex-direction: column;
  gap: 14px;
  padding: 22px 24px 24px;
  max-height: calc(100vh - 48px);
  overflow-y: auto;
}

.feedback-modal__header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
}

.feedback-modal__close {
  width: 32px;
  height: 32px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  background: transparent;
  border: 1px solid var(--border);
  border-radius: 5px;
  font-size: var(--text-2xl);
  line-height: var(--leading-tight);
  color: var(--text-tertiary);
  cursor: pointer;
  transition: border-color 150ms ease, color 150ms ease;
}
.feedback-modal__close:hover {
  color: var(--accent);
  border-color: var(--accent);
}

.feedback-modal__intro {
  margin: 0;
}

/* Form — also used standalone on /feedbacks/new. Tabular, dense, no fluff. */
.feedback-form {
  display: flex;
  flex-direction: column;
  gap: 14px;
}

.feedback-form__row {
  display: flex;
  flex-direction: column;
  gap: 6px;
}

.feedback-form__label {
  font-family: var(--font-data);
  font-size: var(--text-2xs);
  font-weight: 500;
  letter-spacing: var(--tracking-wide);
  text-transform: uppercase;
  color: var(--text-tertiary);
}

.feedback-form__hint {
  margin: 6px 0 0;
  font-size: var(--text-xs);
  line-height: var(--leading-relaxed);
  color: var(--text-tertiary);
}

.feedback-form__hint strong {
  color: var(--text-secondary);
  font-weight: 600;
}

.feedback-form__input,
.feedback-form__textarea {
  font-family: var(--font-body);
  font-size: var(--text-base);
  color: var(--text-primary);
  background: var(--input);
  border: 1px solid var(--border);
  border-radius: 5px;
  padding: 9px 12px;
  width: 100%;
  transition: border-color 150ms ease, box-shadow 150ms ease;
}

/* Strip the native <select> appearance and draw our own chevron so the
   caret sits at consistent padding, matches the dark theme, and doesn't
   look squashed against the right edge. The SVG inlines a chevron-down. */
select.feedback-form__input {
  appearance: none;
  -webkit-appearance: none;
  padding-right: 36px;
  background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='12' height='8' viewBox='0 0 12 8' fill='none'><path d='M1 1.5L6 6.5L11 1.5' stroke='%2394A3B8' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'/></svg>");
  background-repeat: no-repeat;
  background-position: right 14px center;
  cursor: pointer;
}

select.feedback-form__input:focus {
  background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='12' height='8' viewBox='0 0 12 8' fill='none'><path d='M1 1.5L6 6.5L11 1.5' stroke='%230EA5E9' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'/></svg>");
}

.feedback-form__textarea {
  resize: vertical;
  min-height: 120px;
  line-height: var(--leading-relaxed);
}

.feedback-form__input:focus,
.feedback-form__textarea:focus {
  outline: none;
  border-color: var(--accent);
  box-shadow: 0 0 0 3px var(--accent-soft);
}

.feedback-form__actions {
  display: flex;
  justify-content: flex-end;
}

.feedback-form__errors {
  background: rgba(239, 68, 68, 0.08);
  border: 1px solid rgba(239, 68, 68, 0.4);
  color: var(--negative);
  border-radius: 5px;
  padding: 10px 12px;
  font-size: var(--text-sm);
}
.feedback-form__errors ul {
  list-style: disc;
  margin: 6px 0 0 18px;
  padding: 0;
}

/* Generic primary button used by the form submit + future actions. */
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 6px;
  padding: 9px 18px;
  font-family: var(--font-body);
  font-size: var(--text-base);
  font-weight: 600;
  border-radius: 5px;
  border: 1px solid transparent;
  cursor: pointer;
  text-decoration: none;
  transition: background 150ms ease, border-color 150ms ease, color 150ms ease;
}

.btn--primary {
  background: var(--accent);
  color: var(--bg);
  border-color: var(--accent);
}
.btn--primary:hover {
  background: var(--accent-hover);
  border-color: var(--accent-hover);
  color: var(--bg);
}

/* Toast-style flash anchored bottom-center. Auto-fades after 4s. */
.flash {
  position: fixed;
  left: 50%;
  bottom: 24px;
  transform: translateX(-50%);
  z-index: 100;
  padding: 10px 18px;
  background: var(--surface);
  border: 1px solid var(--accent);
  border-radius: 5px;
  font-family: var(--font-body);
  font-size: var(--text-sm);
  color: var(--text-primary);
  box-shadow: 0 6px 24px rgba(0, 0, 0, 0.4), 0 0 0 1px var(--accent-soft);
  animation: flash-fade 4s ease forwards;
}
.flash--notice { color: var(--text-primary); }

@keyframes flash-fade {
  0%, 80% { opacity: 1; transform: translateX(-50%) translateY(0); }
  100%    { opacity: 0; transform: translateX(-50%) translateY(8px); pointer-events: none; }
}

/* ============================================================
   AUTH PAGES — Devise sign in / password reset.
   Centered card, dark theme, brand chrome. Reuses the existing
   .feedback-form__input + .btn primitives.
   ============================================================ */

.auth-page {
  display: flex;
  align-items: center;
  justify-content: center;
  min-height: calc(100vh - 120px);
  padding: 32px 16px;
}

.auth-card {
  position: relative;
  width: 100%;
  max-width: 420px;
  padding: 28px 32px 32px;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 8px;
  box-shadow: 0 20px 60px rgba(0, 0, 0, 0.45), 0 0 0 1px var(--accent-soft);
}

/* Same accent edge-glow as the profile hero card. */
.auth-card::before {
  content: "";
  position: absolute;
  inset: -1px;
  border-radius: 8px;
  border: 1px solid transparent;
  background: linear-gradient(180deg, var(--accent-mid), transparent 50%) border-box;
  -webkit-mask: linear-gradient(#000 0 0) padding-box, linear-gradient(#000 0 0);
  -webkit-mask-composite: xor;
          mask-composite: exclude;
  pointer-events: none;
}

.auth-card__brand {
  display: flex;
  align-items: center;
  gap: 12px;
  margin-bottom: 24px;
}

.auth-logo {
  height: 28px;
  width: auto;
}

.auth-card__chip {
  font-family: var(--font-data);
  font-size: var(--text-micro);
  font-weight: 600;
  letter-spacing: 0.2em;
  color: var(--accent);
  border: 1px solid var(--accent);
  border-radius: 4px;
  padding: 2px 8px;
  background: var(--accent-subtle);
  text-shadow: 0 0 8px var(--accent-glow);
}

.auth-card__title {
  font-family: var(--font-display);
  font-size: var(--text-2xl);
  font-weight: 700;
  color: var(--text-primary);
  margin: 0 0 6px;
}

.auth-card__sub {
  font-family: var(--font-body);
  font-size: var(--text-sm);
  color: var(--text-tertiary);
  margin: 0 0 20px;
}

.auth-form {
  /* Tighter than the feedback modal — shorter form, more breathing room. */
  gap: 16px;
}

.auth-actions {
  margin-top: 4px;
}

.auth-actions .btn {
  width: 100%;
  justify-content: center;
}

.auth-remember {
  display: flex;
  align-items: center;
  gap: 8px;
  font-family: var(--font-body);
  font-size: var(--text-sm);
  color: var(--text-tertiary);
  cursor: pointer;
}
.auth-remember input[type="checkbox"] {
  accent-color: var(--accent);
  width: 14px;
  height: 14px;
}

.auth-card__footer {
  margin: 18px 0 0;
  text-align: center;
  font-family: var(--font-body);
  font-size: var(--text-sm);
  color: var(--text-tertiary);
}

/* Inline alert for Devise's flash messages (alert/notice). */
.auth-flash {
  margin: 0 0 14px;
  padding: 10px 12px;
  font-family: var(--font-body);
  font-size: var(--text-sm);
  border-radius: 5px;
  border: 1px solid var(--border);
  background: var(--input);
  color: var(--text-secondary);
}
.auth-flash--alert {
  color: var(--negative);
  border-color: rgba(239, 68, 68, 0.4);
  background: rgba(239, 68, 68, 0.08);
}
.auth-flash--notice {
  color: var(--accent);
  border-color: var(--accent-strong);
  background: var(--accent-subtle);
}


