/* Edda — Ranking surface (page + landing teaser) styles.
   Consumes edda.css tokens only; zero new tokens, zero hex literals,
   zero body.X scope. Single source of truth — same .ranking-card recipe
   serves /ranking page AND the index.php landing teaser.
   Phase 07.1.1.1 strips podium/skeleton/featured/cluster (D-04, D-05, D-06, D-07).
   Phase 8 deletes the bespoke ranking-hero subtree + race-state header rules
   (markup migrated to shared editorial primitives — see ranking.php).
   Phase 09.1 adds .ranking-page page-shell + .race-state banner namespace
   (foundation for the 09.2 per-class drill-down). */

/* 0. PAGE SHELL (Phase 09.1 D-01, D-03, D-04) ---------------------
   .ranking-page is the page-tier identity hook on <main>. Overrides
   the inherited landing .wrap max-width (1200px → 1100px) for a more
   focused editorial column. Vertical rhythm uses existing tokens —
   no new --space-page-* needed. */
.ranking-page {
    max-width: 1100px;
    margin: 0 auto;
    padding: 0 var(--space-xl) var(--space-3xl);  /* D11-09 — 96px bottom before .wash-ink footer */
}
.ranking-page > .wrap {
    /* .ranking-page already centers + frames; inner .wrap (kept for
       markup compatibility with header/footer includes) becomes a
       transparent passthrough so its own 1200px max-width doesn't fight us. */
    max-width: none;
    padding: 0;
}
.ranking-page-intro {
    padding-top: var(--space-xl);   /* P1-θ — was 2xl (64px), now xl (~48px). 16px reclaim. */
}

/* 10.1 D-05 (P1-ι) — footer bridge. Today: bare cream → wash-ink footer cliff with no transition.
   Add a 1px sage-soft hairline rule + small bottom margin INSIDE the .ranking-page padding box, so the
   eye sees a deliberate close-mark before the dark footer block.
   NOT a .wash-ink extension — sub-page invariant per Phase 04a.2. */
.ranking-page::after {
    content: '';
    display: block;
    height: 1px;
    background: var(--color-accent-soft);
    margin-top: var(--space-2xl);   /* breathing space between last panel and the bridge */
    opacity: 0.6;                    /* halftone presence — not a divider, a fade-mark */
}

@media (max-width: 639px) {
    .ranking-page { padding: 0 var(--space-md) var(--space-2xl); }
    .ranking-page-intro { padding-top: var(--space-xl); }
}

/* 2. CARD GRID (D-21) — same responsive recipe for page + teaser - */
.ranking-grid {
    display: grid;
    grid-template-columns: repeat(3, 1fr);
    gap: var(--space-3);
    margin: 0 auto;
    padding: var(--space-sm) 0;
}
@media (max-width: 1024px) {
    .ranking-grid { grid-template-columns: repeat(2, 1fr); }
}
@media (max-width: 639px) {
    .ranking-grid { grid-template-columns: 1fr; }
}

/* 3. RANKING CARD (D-23) — hairline, no shadow, no transform ---- */
.ranking-card {
    background: var(--color-surface-raised);
    border: 1px solid var(--color-border);
    border-radius: var(--radius-lg);
    padding: var(--space-md);
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: var(--space-sm);
    transition: background-color var(--dur-fast) var(--ease-out),
                border-color var(--dur-fast) var(--ease-out);
}
.ranking-card:hover { border-color: var(--color-border-strong); }

.ranking-position {
    width: 40px; height: 40px;
    background: var(--color-surface-sunken);
    border: 1px solid var(--color-border);
    border-radius: var(--radius-pill);
    display: flex; align-items: center; justify-content: center;
    font-family: var(--font-display); font-size: var(--fs-subheading);
    color: var(--color-accent-strong);
    flex-shrink: 0; font-variant-numeric: tabular-nums;
}

.ranking-player {
    display: flex;
    align-items: center;
    gap: var(--space-sm);
    flex: 1 1 auto;
    min-width: 0;
}

/* Avatar slot inside a card. Combines with .sprite-box primitive
   (edda.css:568) which ships hatched-fallback chrome. */
.ranking-card__sprite {
    width: 56px; height: 56px; flex-shrink: 0;
    border-radius: var(--radius-md); overflow: hidden;
}
.ranking-card__sprite img {
    width: 100%; height: 100%; object-fit: contain;
    image-rendering: pixelated; image-rendering: crisp-edges; display: block;
}

.player-info { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.player-name {
    font: 600 var(--fs-body-lg)/1.25 var(--font-body);
    color: var(--color-ink);
    margin: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
.player-class {
    font: 400 var(--fs-caption)/var(--lh-caption) var(--font-body);
    color: var(--color-ink-muted);
    margin: 0;
}
.class-badge {
    display: inline-block;
    padding: 2px var(--space-xs);
    border: 1px solid var(--color-border);
    border-radius: var(--radius-sm);
    font: 600 var(--fs-11)/1.4 var(--font-body);
    letter-spacing: 0.04em;
    text-transform: uppercase;
    color: var(--color-ink-muted);
    background: var(--color-surface);
}

/* "Primeiro da classe" star pill — rendered by js/ranking.js:162-164.
   Mirrors .class-badge chrome but accent-colored (sage) to read as an
   honor mark, not a category tag. Inline-flex so the inline SVG aligns
   with the cap line of the label text. */
.ranking-card__badge {
    display: inline-flex;
    align-items: center;
    gap: var(--space-xs);
    padding: 2px var(--space-xs);
    border: 1px solid var(--color-accent);
    border-radius: var(--radius-pill);
    font: 600 var(--fs-11)/1.4 var(--font-body);
    letter-spacing: 0.04em;
    text-transform: uppercase;
    color: var(--color-accent-strong);
    background: var(--color-accent-soft);
}
.ranking-card__badge svg {
    width: 12px;
    height: 12px;
    flex-shrink: 0;
}

.ranking-details {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--space-md);
    width: 100%;
    margin-top: var(--space-sm);
}
.detail-row { display: flex; flex-direction: column; align-items: flex-end; gap: 2px; min-width: 60px; }
.detail-label {
    font: 600 var(--fs-11)/1.4 var(--font-body);
    letter-spacing: 0.08em;
    text-transform: uppercase;
    color: var(--color-ink-muted);
}
.detail-value {
    font: 600 var(--fs-body)/1.2 var(--font-body);
    color: var(--color-ink);
    font-variant-numeric: tabular-nums;
}

/* 4. STATS SUMMARY ----------------------------------------------- */
.ranking-stats {
    display: flex;
    justify-content: center;
    gap: var(--space-md);
    margin-top: var(--space-xl);
    flex-wrap: wrap;
}
.stat-box {
    background: var(--color-surface-raised);
    border: 1px solid var(--color-border);
    border-radius: var(--radius-md);
    padding: var(--space-md) var(--space-lg);
    text-align: center; min-width: 180px;
}
.stat-value {
    font-family: var(--font-display); font-size: var(--fs-heading);
    color: var(--color-accent-strong); display: block;
    margin-bottom: var(--space-1);
    font-variant-numeric: tabular-nums; line-height: 1;
}
.stat-label {
    font: 400 var(--fs-micro)/var(--lh-micro) var(--font-body);
    letter-spacing: 0.06em;
    text-transform: uppercase;
    color: var(--color-ink-muted);
}

/* 5. LOADING + ERROR --------------------------------------------- */
.ranking-loading {
    text-align: center;
    padding: var(--space-xl);
    color: var(--color-ink-muted);
    font-size: var(--fs-body);
}
/* Retry button reuses .btn .btn-ghost .btn-sm. */
.ranking-error {
    text-align: center;
    padding: var(--space-lg) var(--space-md);
    border: 1px solid var(--color-border);
    border-radius: var(--radius-md);
    background: var(--color-surface-raised);
    max-width: 640px; margin: var(--space-lg) auto;
}
.ranking-error__body {
    font: 400 var(--fs-body)/var(--lh-body) var(--font-body);
    color: var(--color-ink-muted);
    margin: 0 0 var(--space-md);
}

/* 6. RANKING INTRO BLOCKS (Phase 8 P0-2 + P0-3) -------------------
   Page hero (.ranking-page-intro) and landing teaser (.ranking-teaser-intro)
   share editorial rhythm with .sistemas-intro / .download-head / .faq-left
   (max-width + space-xl bottom margin + scoped eyebrow gap). The base
   .display rule in edda.css carries family/weight/letter-spacing only —
   per-section selectors set the font-size ramp. Mirror landing's existing
   ramp: page hero matches .download-head/.faq-left (48-80px); teaser h2
   sits one tier below (40-64px) since it's a section, not a page hero. */
.ranking-page-intro {
    /* 10.1-fu — editorial 880px column constraint dropped. Was for the legacy lede paragraph
       (which got removed in Phase 10.1). With masthead-only intro (H1 + rule), the row needs
       to span the full .wrap content width (1200px max) so the hairline rule reaches across. */
    margin-bottom: var(--space-xl);
}

/* 10.1-fu — editorial masthead. H1 (page title, fixed) ─── hairline rule.
   Two flex children: H1 auto-width left, rule flex-grow filler.
   Magazine-masthead register: page identity + connecting rule. */
.ranking-page-intro__hero {
    display: flex;
    align-items: center;
    gap: var(--space-md);
}

.ranking-page-intro h1.display {
    flex: 0 0 auto;
    font-size: clamp(48px, 5.6vw, 80px);
    line-height: 1;
    margin: 0;  /* margin handled by hero lockup gap */
}

.ranking-page-intro__rule {
    flex: 1 1 auto;
    height: 1px;
    background: var(--color-border);
    align-self: center;
}

@media (max-width: 768px) {
    .ranking-page-intro__hero {
        flex-direction: column;
        align-items: flex-start;
        gap: var(--space-sm);
    }
    .ranking-page-intro__rule {
        width: 100%;
        flex: 0 0 auto;
    }
}

/* 10.1-fu — H1 lockup-mark dropped. Was leftover from D-06 editorial-tier eyebrow.
   Senior critique: short hairline floating above the heading without functional anchor =
   stub, not lockup. Hub h1 'Rankings.' carries enough display weight on its own. */
.ranking-intro {
    margin-top: var(--space-md);
    color: var(--color-ink-muted);
    /* .section-lede primitive provides fs-17 + balanced wrap; this scope adds page-tier color/spacing */
}

/* Paper-safe lede primitive (Phase 8 P0-1). The legacy .classes-intro rule
   (edda.css:1198) is cream-on-ink — only valid inside .wash-ink. On the
   paper-bg /ranking surface it rendered ~1.05:1 contrast (cream-on-cream).
   .section-lede is the editorial-tier paper variant: full ink-soft on cream,
   60ch line-length, balanced wrap. Kept here (not edda.css) to limit
   blast radius — promote to edda.css if a third surface needs it. */
.section-lede {
    font-size: var(--fs-17);
    line-height: 1.6;
    color: var(--ink-soft);
    max-width: 60ch;
    margin: 0;
    text-wrap: balance;
}

.ranking-teaser-intro {
    max-width: 720px;
    margin-bottom: var(--space-xl);
}
.ranking-teaser-intro .eyebrow { margin-bottom: var(--space-eyebrow-gap); }
.ranking-teaser-intro h2.display {
    font-size: clamp(40px, 4.4vw, 64px);
    line-height: 1;
    margin-bottom: var(--space-md);
}

/* 6b. EDITORIAL EMPTY-STATE PRIMITIVE (Phase 9 D-01) ----------------
   Sibling to .empty-state in css/forms.css (which keeps its dashed
   form-state aesthetic for actual form contexts). On editorial surfaces
   (/ranking + landing teaser), data-absence reads as poetic waiting,
   not as a form error. Mirrors landing's intro-block rhythm and reuses
   the .eyebrow recipe (var(--fs-11) / 0.18em / uppercase / sage). */
.section-empty {
    text-align: center;
    padding: var(--space-lg) var(--space-md);
    max-width: 56ch;
    margin: 0 auto;
}
.section-empty__chip {
    display: inline-flex;
    align-items: center;
    gap: var(--space-xs);
    /* Mirrors .eyebrow recipe at css/edda.css:548-563 — token-only,
       no font-mono token (no such token exists in edda.css). */
    font-family: var(--font-body);
    font-size: var(--fs-11);
    letter-spacing: 0.18em;
    text-transform: uppercase;
    color: var(--color-accent-strong);
    font-weight: 500;
    margin: 0 0 var(--space-md);
}
.section-empty__chip .dot {
    width: 16px;
    height: 1px;
    background: var(--color-accent-strong);
    flex-shrink: 0;
    /* Hairline rule reads less decorative than a filled dot — chip prefix as
       editorial mark, not bullet. */
}
.section-empty__title {
    font-size: var(--fs-17);
    color: var(--ink-soft);
    margin: 0 0 var(--space-md);
    line-height: 1.3;
    text-wrap: balance;
}
.section-empty p {
    font-size: var(--fs-17);
    line-height: 1.6;
    color: var(--ink-soft);
    margin: 0;
    text-wrap: balance;
}
.section-empty p + p { margin-top: var(--space-sm); }
.section-empty a {
    color: var(--color-accent-strong);
    text-decoration: underline;
    text-underline-offset: 2px;
}
.section-empty a:hover { color: var(--color-accent); }

/* 10.1-fu — recovery action button on error-path empty cards.
   Looks like an inline link; behavior is a real <button> so screen readers announce it correctly
   and the click handler fires without a navigation. */
.section-empty__actions {
    margin: var(--space-md) 0 0;
}
.section-empty__action {
    background: none;
    border: 0;
    padding: 0;
    margin: 0;
    font: inherit;
    font-size: var(--fs-13);
    color: var(--color-accent-strong);
    text-decoration: underline;
    text-underline-offset: 2px;
    cursor: pointer;
}
.section-empty__action:hover,
.section-empty__action:focus-visible {
    color: var(--color-accent);
}

/* CTA-hide on empty teaser (Phase 9 D-05). :has() is supported in all
   current evergreens (Chrome 105+, Safari 15.4+, Firefox 121+).
   The .is-empty fallback is toggled by js/ranking.js for browsers that
   don't yet support :has() — graceful degradation, not a duplicate. */
#rankingTeaserGrid:has(.section-empty) + .ranking-teaser-cta { display: none; }
.ranking-teaser.is-empty .ranking-teaser-cta { display: none; }

/* 7. RANKING TEASER LEDE (D-24) ---------------------------------- */
/* Teaser-specific lede on the landing ranking teaser. Decoupled from
   the (deleted) page-hero lede so cross-surface coupling stays clean. */
.ranking-teaser__lede {
    font: 400 var(--fs-body-lg)/var(--lh-body-lg) var(--font-body);
    color: var(--ink-soft); max-width: 640px;
    margin: var(--space-md) 0 var(--space-lg);
}

/* 8. MOBILE BREAKPOINTS ------------------------------------------ */
@media (max-width: 1024px) {
    .ranking-card { padding: var(--space-sm); }
    .ranking-card__sprite { width: 48px; height: 48px; }
    .ranking-details {
        gap: var(--space-sm);
        padding-top: var(--space-3);
        border-top: 1px solid var(--color-border);
    }
    .ranking-stats { gap: var(--space-sm); }
    .stat-box { min-width: 140px; padding: var(--space-sm) var(--space-md); }
}

@media (max-width: 639px) {
    .ranking-card { padding: var(--space-3); }
    .ranking-position { width: 36px; height: 36px; font-size: var(--fs-body); }
    .ranking-card__sprite { width: 48px; height: 48px; }
    .player-name { font-size: var(--fs-body); }
    .stat-box { min-width: 100%; }
}

/* 7. RANKING TABLE (Phase 09.5 — page surface mercado-pattern) -----
   Page-surface (#rankingTable) replaces Skal's .ranking-card 3-col
   grid. Mirrors mercado-table-edda recipe (border-collapse, hairline
   rows, mono-caps thead, hover) but uses edda.css tokens. Sibling to
   .ranking-card which still serves landing teaser surface. */
/* 09.11 D11-08 — table wrap + outer hairline border (mercado precedent + landing .class-panel parity). */
.ranking-table-wrap {
    border: 1px solid var(--color-border);
    background: var(--color-surface-raised);  /* P1-β — paper-tier lift off cream body (normalize lens #3 root fix). */
    margin-top: var(--space-xl);
}

/* 09.11 D11-05 — class-selected caption above thead. */
.ranking-table__caption {
    caption-side: top;
    text-align: left;
    padding: var(--space-md);
    border-bottom: 1px solid var(--color-border);
    background: var(--color-surface-raised);  /* match table-wrap lift. */
}
/* 10.1 D-05 (P2-γ polish lens #1) — caption reveal/hide animates via max-height instead of toggling
   [hidden] (which slams display:none/block + causes layout shift inside the table-wrap).
   Approach: keep [hidden] for SR semantics, BUT also use a CSS-driven max-height animation when the
   caption transitions from hidden→visible. JS (04b) toggles caption.hidden = false AND
   adds caption.classList.add('ranking-table__caption--in') to trigger the animation. */
.ranking-table__caption {
    max-height: 200px;     /* generous cap; real caption is ~50-60px */
    overflow: hidden;
    transition: max-height var(--dur-fast) var(--ease-out), padding var(--dur-fast) var(--ease-out);
}
.ranking-table__caption[hidden] {
    display: block;        /* override browser default — keep in flow so transition can run */
    max-height: 0;
    padding-top: 0;
    padding-bottom: 0;
    border-bottom: 0;
    /* visibility: hidden + aria-hidden semantics preserved via the [hidden] attribute,
       which screen readers honor regardless of CSS display. */
}
.ranking-table__caption-name {
    font-family: var(--font-display);
    font-size: var(--fs-17);
    color: var(--color-ink);
    margin-right: var(--space-md);
}
.ranking-table__caption-sub {
    font-family: var(--font-body);
    font-size: var(--fs-11);
    letter-spacing: 0.18em;
    text-transform: uppercase;
    color: var(--color-ink-muted);
}

.ranking-table {
    width: 100%;
    border-collapse: collapse;
    font-family: var(--font-body);
    font-size: var(--fs-body);
    border: 0;  /* wrap provides outer hairline */
}
.ranking-table thead th {
    text-align: left;
    padding: var(--space-sm) var(--space-md);
    font-family: var(--font-body);
    font-size: var(--fs-12);                       /* P1-ν / P2-ξ — bump 11→12 for scannability */
    font-weight: 600;                              /* P1-ν — bump 500→600 for grip on tracking */
    text-transform: uppercase;
    letter-spacing: 0.18em;
    color: var(--color-ink);                       /* P1-ν — full ink, not muted */
    border-bottom: 1px solid var(--color-border);
    background: var(--color-surface-secondary);    /* P1-β — subtle thead tint over raised wrap */
    white-space: nowrap;
}
.ranking-table tbody tr {
    border-bottom: 1px solid var(--color-border);
    transition: background-color var(--dur-fast) var(--ease-out);
}
.ranking-table tbody tr:last-child { border-bottom: 0; }
.ranking-table tbody tr:hover { background: var(--color-surface-secondary); }
.ranking-table td {
    padding: var(--space-sm) var(--space-md);
    color: var(--color-ink);
    vertical-align: middle;
}
.ranking-table tbody tr.is-empty td {
    color: var(--color-ink-muted);
    text-align: center;
    padding: var(--space-lg) var(--space-md);
    font-style: italic;
}
.ranking-table tbody tr.is-empty:hover { background: transparent; }

/* 10.1 D-04 (P0-δ full) — skeleton-row rules removed; SSR + JS now both render the
   .section-empty primitive on pre-data / empty / error. Legacy .is-skeleton class is gone
   from ranking.php (Task 1) and js/ranking.js (Task 3). __empty-msg rule retained as defensive
   fallback for any inline classEmptyMessage() composition path. */
.ranking-table__empty-msg {
    color: var(--color-ink-muted);
    font-style: italic;
    text-align: center;
}

/* 10.1 D-04 (P0-δ) — empty-row holder for the .section-empty SSR fallback (and JS renderSectionEmpty
   target). The <td colspan> spans the full table width; .section-empty centers itself within. */
.ranking-table tbody tr.ranking-table__empty-row td {
    padding: var(--space-lg) var(--space-md);
    background: transparent;
    border: 0;
}
.ranking-table tbody tr.ranking-table__empty-row:hover {
    background: transparent;
}

/* Per-column typography (using nth-child since columns aren't
   per-cell classed in JS-emitted rows). */
.ranking-table tbody td:first-child {
    width: 3em;
    font-variant-numeric: tabular-nums;
    font-weight: 600;
    color: var(--color-accent-strong);
}
.ranking-table tbody td:nth-child(2) {
    font-size: var(--fs-body-lg);
    font-weight: 500;
    /* P2-θ (harden lens #1) — wrap long names inside the cell instead of breaking the layout.
       10.1-fu — promoted from MVP/Zeny-scoped to all-panels. Corrida 99 also benefits since
       col 3 (Classe) is also text. */
    min-width: 0;
    overflow-wrap: anywhere;
    word-break: break-word;
}
/* P0-α — value-column muting was a cross-tab regression. Corrida 99 col 3 = "Classe" (secondary text);
   MVPs col 3 = "MVPs Mortos" (HEADLINE NUMBER); Zeny col 3 = "Total Zeny" (HEADLINE NUMBER).
   Scope the muting to Corrida-99-only via [data-panel] attribute already shipped on every panel
   (ranking.php:111, 180, 218). MVP/Zeny col 3 then inherits the col-2 weight by default — and gains
   tabular-nums + ink-strong + bumped size below for the headline-number treatment. */
.ranking-panel[data-panel="corrida99"] .ranking-table tbody td:nth-child(3),
.ranking-panel[data-panel="corrida99"] .ranking-table thead th:nth-child(3) {
    font-size: var(--fs-caption);
    color: var(--color-ink-muted);
    /* Class col content varies; let it size to content. Name col (2) absorbs slack. */
    width: 1%;
    white-space: nowrap;
}

/* 10.1-fu — Chegada (col 4) styling. Caption-tier muted right-aligned; nowrap so the
   formatted date (e.g. '14/04/2026 21:30:00') stays on one line. */
.ranking-panel[data-panel="corrida99"] .ranking-table tbody td:nth-child(4),
.ranking-panel[data-panel="corrida99"] .ranking-table thead th:nth-child(4) {
    font-size: var(--fs-caption);
    color: var(--color-ink-muted);
    width: 10em;
    white-space: nowrap;
    text-align: right;
}

/* 10.1-fu — racer view: col 3 = Nível (numeric). Override Classe styling with
   headline-number treatment matching MVP/Zeny value cells. */
.ranking-panel[data-panel="corrida99"].ranking-panel--racers-mode .ranking-table tbody td:nth-child(3) {
    font-size: var(--fs-body-lg);
    color: var(--color-ink);
    font-weight: 500;
    font-variant-numeric: tabular-nums;
    text-align: right;
    width: 1%;
    white-space: nowrap;
}

/* 10.1-fu — racer view: col 4 (Chegada) hidden. Racer rows only have 3 cells; thead col 4
   stays in the source for monument mode but disappears here. */
.ranking-panel[data-panel="corrida99"].ranking-panel--racers-mode .ranking-table thead th:nth-child(4),
.ranking-panel[data-panel="corrida99"].ranking-panel--racers-mode .ranking-table tbody td:nth-child(4) {
    display: none;
}

/* MVPs/Zeny col 3 = the headline number. Tabular figures + bump for legibility.
   Right-align to align decimal/thousand separators across rows. */
.ranking-panel[data-panel="mvp"] .ranking-table tbody td:nth-child(3),
.ranking-panel[data-panel="zeny"] .ranking-table tbody td:nth-child(3) {
    font-size: var(--fs-body-lg);
    font-weight: 500;
    font-variant-numeric: tabular-nums;
    text-align: right;
    color: var(--color-ink);
}
.ranking-panel[data-panel="mvp"] .ranking-table thead th:nth-child(3),
.ranking-panel[data-panel="zeny"] .ranking-table thead th:nth-child(3) {
    text-align: right;
}
.ranking-table tbody td:nth-child(4) {
    width: 5em;
    font-variant-numeric: tabular-nums;
    text-align: right;
}
.ranking-table tbody td:nth-child(5) {
    width: 10em;
    font-size: var(--fs-caption);
    color: var(--color-ink-muted);
    text-align: right;
    white-space: nowrap;
}


/* P2-ν / P2-θ partial — .ranking-panel-header__copy ships unstyled in markup (ranking.php:182, 220).
   Without min-width:0 the grid track refuses to shrink below content-width on long names in the
   header copy column, blowing out the Template B header layout. Plan 03 relocates the MVP toggle
   out of __copy (structurally closing P2-ν — no additional __copy styling needed once the toggle moves). */
.ranking-panel-header__copy {
    min-width: 0;
}

/* 10.1-fu — .ranking-table__primeiro removed. Badge dropped per impeccable distill lens
   (every row in the dataset IS first-of-class by construction; badge on every row repeated
   the panel kicker's promise). See js/ranking.js buildTableRow for full rationale. */

/* 10.1 D-04 D-11 (P1-γ — bolder lens #1, arrange lens #3, delight lens #3) — podium row treatment.
   Rank 1 = sage-soft bg + leading ★ + value-cell font-size bump.
   Rank 2/3 = sage-subtle bg, no glyph, no font bump.
   Rank 4-10 = plain (no rule).

   Scoped per [data-panel] so the value-cell bump applies to the correct column on each table:
   - Corrida 99 col 4 = "Nível" (the headline number for first-to-99).
   - MVPs / Zeny col 3 = the headline number (already bumped to body-lg by Plan 02).

   Plan 05 may scope the podium tint to per-panel accent (purple for MVP, sage for Corrida 99,
   gold-or-sage for Zeny per D-12). This rule ships sage-default; Plan 05 attribute-scopes overrides. */

/* 10.1-fu Pattern A — purple = rank-1 tier semantic across ALL panels.
   Drops the Plan 05 MVP-only scoping. First place reads the same color anywhere on /ranking;
   sage is the shared chrome (tabs, dashes, chip, retry, ranks 2-10). Purple becomes a single
   semantic role: "rank 1 / apex" — not a per-panel narrative tag. */
.ranking-table tbody tr.ranking-row--podium-1 {
    background: var(--color-accent-alt-soft);
}
.ranking-table tbody tr.ranking-row--podium-1:hover {
    background: var(--color-accent-alt-soft);  /* no hover shift on podium rows — already lifted */
}
.ranking-table tbody tr.ranking-row--podium-1 td:first-child {
    font-weight: 700;
    color: var(--color-accent-alt-strong);  /* rank "1" gets purple to match the tier */
}
.ranking-table tbody tr.ranking-row--podium-1 .ranking-row__podium-glyph {
    color: var(--color-accent-alt-strong);
    margin-right: var(--space-xs);
    font-size: 0.9em;
    line-height: 1;
}

.ranking-table tbody tr.ranking-row--podium-2,
.ranking-table tbody tr.ranking-row--podium-3 {
    background: var(--color-accent-subtle);
}
.ranking-table tbody tr.ranking-row--podium-2:hover,
.ranking-table tbody tr.ranking-row--podium-3:hover {
    background: var(--color-accent-subtle);
}

/* Per-panel value-cell bump for podium-1 (data-panel scopes the column index). */
.ranking-panel[data-panel="corrida99"] .ranking-table tbody tr.ranking-row--podium-1 td:nth-child(4) {
    font-size: var(--fs-body-lg);
    font-weight: 600;
}
.ranking-panel[data-panel="mvp"] .ranking-table tbody tr.ranking-row--podium-1 td:nth-child(3),
.ranking-panel[data-panel="zeny"] .ranking-table tbody tr.ranking-row--podium-1 td:nth-child(3) {
    /* Plan 02 already set font-size: var(--fs-body-lg) on MVP/Zeny col 3.
       Podium-1 bumps weight from 500 to 600 for one extra grade of emphasis. */
    font-weight: 600;
}

/* Mobile (≤639px) — table collapses to stacked rows. */
@media (max-width: 639px) {
    .ranking-table { font-size: var(--fs-caption); margin-top: var(--space-lg); }
    .ranking-table thead { display: none; }
    .ranking-table tbody, .ranking-table tr, .ranking-table td { display: block; }
    .ranking-table tbody tr {
        display: grid;
        grid-template-columns: auto 1fr auto;
        gap: var(--space-xs) var(--space-md);
        padding: var(--space-sm) 0;
        border-bottom: 1px solid var(--color-border);
    }
    .ranking-table tbody tr:last-child { border-bottom: 0; }
    .ranking-table tbody td { padding: 0; border: 0; width: auto; }
    .ranking-table tbody td:first-child {
        grid-row: 1 / span 2;
        align-self: center;
    }
    .ranking-table tbody td:nth-child(2) { grid-column: 2; }
    .ranking-table tbody td:nth-child(3) { grid-column: 2; }
    .ranking-table tbody td:nth-child(4) { grid-column: 3; align-self: center; text-align: right; }
    .ranking-table tbody td:nth-child(5) { grid-column: 2 / span 2; font-size: var(--fs-12); text-align: left; }
    .ranking-table tbody tr.is-empty {
        display: block;
        text-align: center;
    }
    .ranking-table tbody tr.is-empty td {
        text-align: center;
    }
}

/* 10.1 D-04 (P1-μ, adapt lens #3) — wrap collapses class filter behind <details> at ≤600px.
   Desktop (>600px) hides the <summary> entirely so the grid renders identically to legacy.
   Mobile (≤600px) shows the summary as a tap target; closed-by-default if user taps it.
   <details open> in markup ensures desktop default-open + mobile-toggleable. */
.ranking-class-filters-wrap {
    margin: 0 auto var(--space-md);
}

.ranking-class-filters-summary {
    display: none;  /* hidden on desktop — class-filters render directly */
    cursor: pointer;
    list-style: none;
    padding: var(--space-sm) var(--space-md);
    border: 1px solid var(--color-border);
    background: var(--color-surface-raised);
    margin-bottom: var(--space-sm);
    color: var(--color-ink);
    user-select: none;
    align-items: center;
    justify-content: space-between;
    gap: var(--space-sm);
}

.ranking-class-filters-summary::-webkit-details-marker { display: none; }

.ranking-class-filters-summary__hint {
    color: var(--color-ink-muted);
    font-size: var(--fs-11);
    letter-spacing: 0.18em;
}

@media (max-width: 600px) {
    .ranking-class-filters-summary {
        display: flex;
    }
    /* Override the existing margin on .ranking-class-filters at css/ranking.css:544
       so the wrap takes ownership of vertical rhythm at this breakpoint. */
    .ranking-class-filters-wrap[open] .ranking-class-filters {
        margin-top: 0;
    }
}

/* 10.1 WR-01 — Force the class-filter grid visible at desktop regardless of the
   user-agent <details> collapsed state. If a user collapses on mobile and then
   resizes to desktop, the <summary> is hidden (display: none above) but <details>
   stays closed; without this rule the entire grid disappears with no recovery
   affordance. Summary is unreachable at this breakpoint, so the collapsed state
   is unreachable too — safe to force-open. */
@media (min-width: 601px) {
    .ranking-class-filters-wrap > .ranking-class-filters {
        display: grid !important;
    }
}

/* 8. RANKING CLASS FILTERS (Phase 09.11 — Balanceamento BONES, cream tones) ----
   Lifts landing III's tile-grid composition (tight 6×3 + 1px seams + sharp
   corners + display-font names) to cream context. Tile bg matches page surface.
   Active = sage-subtle bg + ink text.
   09.11.2 — seams shipped as per-tile outlines (overlap cleanly, no doubling).
   Container drops background+border so trailing empty grid cells render as
   transparent space rather than gray rectangles when tile count is non-multiple
   of column count (e.g. 19 tiles in 6-col grid). */
.ranking-class-filters {
    display: grid;
    grid-template-columns: repeat(6, 1fr);
    gap: 0;
    background: transparent;
    border: none;
    margin: 0 auto var(--space-md);      /* D11-06 — tight to table below */
    padding: 0;
}
@media (max-width: 1080px) { .ranking-class-filters { grid-template-columns: repeat(4, 1fr); } }
@media (max-width: 760px)  { .ranking-class-filters { grid-template-columns: repeat(3, 1fr); } }
@media (max-width: 600px)  { .ranking-class-filters { grid-template-columns: repeat(2, 1fr); } }

.ranking-class-filter {
    appearance: none;
    background: var(--color-surface);    /* CREAM — same as page */
    border: none;
    border-radius: 0;                     /* sharp corners (Balanceamento precedent) */
    /* 09.11.2 — outline draws seam without taking layout space; offset -1 keeps
       seam inside the tile so adjacent tile outlines overlap cleanly into a
       single 1px hairline. Empty grid cells render transparent (no outline). */
    outline: 1px solid var(--color-border);
    outline-offset: -1px;
    color: var(--color-ink);
    padding: var(--space-md) var(--space-sm);
    min-height: 92px;                    /* 09.11.2 — match iconed-tile height for "Todas Classes" no-icon tile */
    cursor: pointer;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;             /* 09.11.2 — vertically center content (icon+name OR name-only) */
    gap: var(--space-3);          /* P2-μ — was hardcoded 10px; snap to token (12px). +2px nudge below perception threshold. */
    text-align: center;
    min-width: 0;
    transition: background-color .15s, color .15s;
}
.ranking-class-filter:hover {
    background: var(--color-accent-soft);
}
.ranking-class-filter:focus-visible {
    outline: 2px solid var(--color-accent);
    outline-offset: -2px;
}
.ranking-class-filter.active {
    background: var(--color-accent-subtle);
    color: var(--color-ink);
}
.ranking-class-filter.active:hover {
    background: var(--color-accent-subtle);  /* no hover shift once active */
}
.ranking-class-filter__icon {
    width: 22px;
    height: 22px;
    flex-shrink: 0;
    image-rendering: pixelated;
    opacity: 0.9;
}
.ranking-class-filter.active .ranking-class-filter__icon { opacity: 1; }
.ranking-class-filter__name {
    /* Landing .class-tile__name recipe verbatim. */
    font-family: var(--font-display);
    font-size: var(--fs-17);
    font-weight: 500;
    letter-spacing: -0.01em;
    line-height: 1.1;
    overflow-wrap: anywhere;
    color: inherit;
}
@media (max-width: 600px) {
    .ranking-class-filter { padding: var(--space-sm) var(--space-xs); gap: var(--space-xs); }   /* P2-μ — was hardcoded 6px; snap to token (8px). */
    .ranking-class-filter__name { font-size: var(--fs-15); }
}

/* 09.11.2 — "Todas Classes" tile (text-only, no icon). Inherits all base
   .ranking-class-filter tones (cream-on-cream + outline seam). justify-content
   from base rule centers the lone name span; min-height matches iconed siblings. */

/* ═══════════════════════════════════════════════════════════════════════════
   Phase 10 — Rankings hub: tab nav + panel templates
   Hub identity = Rankings page; three tabs (Corrida 99, MVPs, Zeny).
   Tab nav = display-headlines + sage underline indicator (D-05).
   Panel templates: A = Corrida 99 (untouched internals); B = MVPs + Zeny
   (two-column header [copy-left | sprite-right] + 3-col table).
   ═══════════════════════════════════════════════════════════════════════════ */

/* 10.1 D-05 D-13 (P1-α typeset lens #1, P1-δ — anchor-nav semantics committed).
   Tab strip = single bottom hairline. Drop the top border (P1-α — border-sandwich = ToC frame; nav doesn't need a frame).
   Anchor-nav (not tablist ARIA): aria-current="page" already shipped per ranking.php:98, 102, 106. SSR ?tab= routing
   already supports deep links + works without JS. Tablist ARIA upgrade deferred per D-13 — no in-phase escalation
   path; if a screen-reader regression surfaces post-ship, file as new finding. */
.ranking-tabs {
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-lg);
    align-items: baseline;
    border-bottom: 1px solid var(--color-border);
    padding: 0 0 var(--space-md);
    margin: var(--space-md) 0 var(--space-lg);   /* 10.1-fu — bottom xl→lg, tighter tab→content rhythm. */
}

.ranking-tab {
    font-family: var(--font-display);
    font-size: clamp(18px, 1.8vw, 22px);  /* P1-α — demoted from 20-28 so tab→h2 ratio reorders correctly (h1:h2:tab ≈ 72:44:22) */
    font-weight: 400;
    line-height: 1;
    text-decoration: none;
    color: var(--color-ink-muted);
    position: relative;
    padding: var(--space-xs) 0;
    transition: color 120ms ease, opacity 120ms ease;
    opacity: 0.7;
}

.ranking-tab:hover,
.ranking-tab:focus-visible {
    color: var(--color-ink);
    opacity: 1;
}

.ranking-tab--active {
    color: var(--color-accent-strong);
    opacity: 1;
}

/* P2-α (polish lens #2) — single ::after rule for both hover+focus AND active states.
   Base provides positioning + content; per-state selectors override height + color.
   Source-order ensures active wins on active+hover (active selector specificity == hover selector;
   active rule listed AFTER so it cascades). */
.ranking-tab:hover::after,
.ranking-tab:focus-visible::after,
.ranking-tab--active::after {
    content: '';
    position: absolute;
    left: 0;
    right: 0;
    bottom: -1px;
    height: 1px;
    background: var(--color-ink);
}

.ranking-tab--active::after {
    height: 2px;
    background: var(--color-accent-strong);
}

/* P1-κ (adapt lens #1) — at ≤960px, 3 tabs at 22px wrap to 2 lines and read as a list, not nav.
   Switch to vertical-stack: each tab full-width row, indicator moved from bottom to LEFT (vertical strip). */
@media (max-width: 960px) {
    .ranking-tabs {
        flex-direction: column;
        gap: 0;
        border-bottom: 0;            /* the per-tab bottom-rule below replaces the strip-level rule */
        padding: 0;
    }
    .ranking-tab {
        font-size: clamp(18px, 4vw, 22px);
        padding: var(--space-sm) var(--space-md);
        border-bottom: 1px solid var(--color-border);
        width: 100%;
    }
    .ranking-tabs > .ranking-tab:last-child {
        border-bottom: 0;
    }
    /* Move indicator from bottom (1px/2px horizontal) to left (vertical strip). */
    .ranking-tab:hover::after,
    .ranking-tab:focus-visible::after,
    .ranking-tab--active::after {
        bottom: auto;
        left: 0;
        right: auto;
        top: 0;
        bottom: 0;
        width: 1px;
        height: auto;
    }
    .ranking-tab--active::after {
        width: 2px;
    }
}

/* 10.1 D-05 (P2-β polish lens #3) — panel cross-fade. Browser default toggles display:none/block
   synchronously when [hidden] flips. We can't transition display, but we CAN fade-in newly-visible
   panels by triggering an opacity transition on the :not([hidden]) state.
   Reduced-motion users get instant transition via the global edda.css:814-820 umbrella. */
.ranking-panel {
    margin-bottom: var(--space-2xl);
    opacity: 1;
    transition: opacity var(--dur-fast) var(--ease-out);
}
.ranking-panel[hidden] {
    /* browser default display:none stays; opacity moot when hidden */
}
/* Animation hook: js/ranking.js may add a momentary `.ranking-panel--just-shown` class on the panel
   that just became visible to trigger a 0→1 fade. If JS doesn't add this class, the rule above
   keeps panels at opacity:1 with no flicker — graceful degradation. */
.ranking-panel--just-shown {
    animation: rankingPanelFadeIn var(--dur-fast) var(--ease-out);
}
@keyframes rankingPanelFadeIn {
    from { opacity: 0; }
    to   { opacity: 1; }
}

/* 10.1 D-04 D-14 — Template B header is now the inline-lockup form (option C from UI-REVIEW).
   Single column: sprite + H2 read as one unit. Drop the right-column 120px billboard. */
.ranking-panel-header {
    margin-bottom: var(--space-sm);  /* 10.1-fu — tightened from md → sm (~12px). With H2 dropped on MVP/Zeny, sprite/toggle group sits closer to table. */
}

.ranking-panel-header h2.display {
    font-size: clamp(32px, 3.6vw, 52px);
    line-height: 1;
    margin: 0 0 var(--space-md);
}

/* 10.1 P1-η (typeset lens #2) — panel kicker demoted from mono-caps to body italic muted-ink.
   Markup drops the `.mono` class (Task 1 in this plan); CSS rule becomes self-contained:
   body font + italic + muted-ink. Reserves mono-caps for true eyebrow tier (e.g., section-empty
   chip, MVP scope toggle). Matches landing `.section-lede` register per Phase 10.1 voice direction. */
.ranking-panel-kicker {
    margin: 0;
    font-family: var(--font-body);
    font-style: italic;
    font-weight: 400;
    text-transform: none;            /* override any inherited uppercase if `.mono` defined it */
    letter-spacing: normal;          /* override any inherited tracking */
    color: var(--color-ink-muted);
}

/* 10.1-fu — masthead-in-panel-header system removed. Sprite moved to hero lockup at page level
   (.ranking-page-intro__hero). Panel headers now collapse to minimal chrome: MVP keeps the scope
   toggle only; Zeny has no panel header at all; Corrida 99 keeps its own template (kicker +
   class-filter row). */

.ranking-panel-header__copy {
    /* min-width:0 already set in Plan 02 — keeps the column shrinkable inside the flex lockup. */
    flex: 1 1 auto;
}

/* Template A (Corrida 99) — no sprite. Keep the minimal-margin treatment for the class-filter
   row that follows immediately. */
.ranking-panel-header--corrida99 {
    margin-bottom: var(--space-lg);
}

/* 10.1 D-04 (P1-ξ) — MVP toggle moved out of __copy. Standalone row variant uses block-flow
   layout below the lockup; baseline alignment between Geral and Mensal preserved.
   D-04 (P1-λ, adapt lens #2) — 44px min tap target on coarse-pointer (touch) devices. */
.ranking-mvp-toggle {
    display: inline-flex;
    align-items: baseline;
    gap: var(--space-sm);
}

/* 10.1-fu — masthead lockup puts toggle on the right of the H2 row via parent flex
   space-between. Drop the margin-top hack from D-04 (was for stacked-row variant). */
.ranking-mvp-toggle--standalone {
    flex: 0 0 auto;
}

@media (max-width: 768px) {
    .ranking-mvp-toggle--standalone {
        margin-top: var(--space-md);  /* re-add gap when stacked on mobile */
    }
}

.ranking-mvp-scope {
    background: none;
    border: 0;
    padding: 0 0 4px;
    margin: 0;
    font: inherit;
    /* 10.1-fu second-pass — promoted again. Toggle was reading as afterthought next to the
       144px sprite; bumped to fs-17 + tighter letter-spacing for display-tier register. */
    font-size: var(--fs-17);
    letter-spacing: 0.08em;
    font-weight: 500;
    color: var(--color-ink-muted);
    cursor: pointer;
    position: relative;
    transition: color 120ms ease;
}

.ranking-mvp-scope:hover,
.ranking-mvp-scope:focus-visible {
    color: var(--color-ink);
}

.ranking-mvp-scope--active {
    color: var(--color-ink);
    font-weight: 600;
}

.ranking-mvp-scope--active::after {
    content: '';
    position: absolute;
    left: 0;
    right: 0;
    bottom: 0;
    height: 1px;
    background: var(--color-accent-strong);
}

.ranking-mvp-toggle__sep {
    color: var(--color-ink-muted);
    user-select: none;
}

/* P1-λ — touch-pointer 44px min height (Apple HIG; Material is 48). Visual density preserved
   via padding-block; the underline rule above stays hugged to the text baseline. */
@media (pointer: coarse) {
    .ranking-mvp-scope {
        min-height: 44px;
        padding: var(--space-xs) var(--space-xs) calc(var(--space-xs) + 2px);
        display: inline-flex;
        align-items: center;
    }
}

/* ═══════════════════════════════════════════════════════════════════════════
   10.1-fu Pattern A — purple = rank-1 tier semantic. Sage = shared chrome.

   Plan 05 originally scoped purple to the MVP panel as a "Dragão Púrpura / boss-killer"
   narrative tag. Cross-tab review surfaced a real cost: first place looked purple on MVP,
   sage on Corrida 99, sage on Zeny — the same achievement read three colors. Pattern A
   collapses panel identity into a single rank-tier semantic:

     - rank 1 (podium-1) → purple-soft bg + purple "1" + purple ★ glyph (universal — see line 620)
     - rank 2-3 (podium-2/3) → sage-subtle bg, no other tier marks
     - rank 4-10 → plain
     - All chrome (tab active states, scope toggle, H2 dash, ERRO chip, retry action) → sage

   Purple = winner everywhere. One semantic role, three panels, no drift.
   Token-only — uses --color-accent-alt-* shipped at css/edda.css:99-101.
   ═══════════════════════════════════════════════════════════════════════════ */

/* 10.1-fu — H2 em-dash dropped. The dash was leftover from the inline-baseline lockup
   (D-04 option C) where it served as an eyebrow anchoring the H2 next to a small sprite.
   In the masthead redesign, the BIG sprite IS the anchor for MVP/Zeny panels, and the
   class-filter row anchors Corrida 99. Dash became redundant decoration that didn't speak
   the new design language. Cleaner without. */
