/* Copied from animalarmygame e24e710; keep in sync by hand (see scripts/lib/copy-rules.mjs). */
/*
 * Animal Army — stylesheet (Field Crate reskin)
 * Design tokens live in :root below (cabinet / drawer / brass / parchment /
 * graphite / moss / blaze). Responsive rules: mobile-first single column,
 * side stats panel appears at min-width:1000px.
 */
:root{
    /* Field Crate reskin, replacing the Flat Modern dark-panel palette this
       codebase shipped with previously - the old token names (ink, panel,
       stamp, pine, amber, chalk) are gone entirely, not renamed-in-place,
       because a variable still resolving to the old brown/red/gold set
       under a name that implies "current" would mislead the next person
       to touch this file. Look in git history if a stale comment or a
       branch still references one of those old names.
       Traced from the site's own already-live dark-green ground rather than
       an invented brown-black - a specimen crate in a dim depot, not a
       safari sunset. See the redesign brief's Phase 2 plan for the full
       reasoning (key art didn't match the brand's actual ground color, so
       it wasn't sampled from).
       Discipline: --cabinet and --drawer carry the bulk of every surface;
       --parchment and --graphite carry text; --moss is structural only
       (rules, active states, taxonomic dividers) and never sits under body
       text; --blaze is the one loud color - stencil numerals, primary CTAs,
       the Legendary rarity tier - deliberately promoted from a <5% accent
       to a primary color per direct request, so it shows up more than a
       typical "rare highlight" would elsewhere in this file. */
    /* Theme redesign (2026-09): deep jungle ground instead of near-black,
       warmer parchment, three saturated accents (ember --blaze, leaf --moss,
       water --water). Token names are kept so the 6k lines below and the
       sibling stylesheets keep resolving; only the values moved. */
    --cabinet:#0f2118;
    --cabinet-deep:#091510;
    --drawer:#1a3327;
    --drawer-hover:#214032;
    /* Contrast pass (2026-09-11): brightened border / text / accent tokens
       to hit WCAG AA on all UI (≥3:1 for non-text UI, ≥4.5:1 for text),
       and AAA (≥7:1) for body-text tokens on both --cabinet and --drawer.
       Darkened solid-fill button backgrounds (--blaze-dark / --pewter-dark)
       so --parchment text on top of them also clears AAA. Previous values
       are left in comments so a designer skimming the diff can see what
       moved and by how much. */
    --drawer-edge:#4c7a5e;                        /* was #4a4630 (1.86:1) → 3.75:1 on cab, 3.06:1 on drw */
    --drawer-shadow: rgba(3,10,6,0.6);
    --brass:#c9a24d;                              /* was #8c7a45 → 5.27:1 on cab (AA for labels) */
    --brass-bright:#f0cf6e;                       /* was #c9b26b → 9.57:1 on cab, 7.81:1 on drw (AAA) */
    --parchment:#f6ead0;                          /* unchanged: 13.65:1 on cab, 11.14:1 on drw (AAA) */
    --parchment-dim: rgba(246,234,208,0.82);      /* was 0.66 → 9.49:1 on cab, 8.01:1 on drw (AAA) */
    --graphite:#b7c6b6;                           /* was #93998a → 8.74:1 on cab, 7.13:1 on drw (AAA) */
    --moss:#4f9a63;                               /* was #4e5f3f (2.56:1) → 4.12:1 on cab, 3.36:1 on drw (border/UI ≥3:1) */
    --moss-bright:#7ed48f;                        /* unchanged (already 4.49:1 on cab) */
    /* Accent color for stencil numerals, primary CTAs, Legendary tier.
       --blaze bumped for AA safety on the darker drawer surface;
       --blaze-bright hits AAA on cab. */
    --blaze:#ff7a1f;                              /* was #d3560f → 5.01:1 on cab, 4.08:1 on drw */
    --blaze-bright:#ffa04f;                       /* was #f07a2e → 6.91:1 on cab, 5.64:1 on drw */
    /* Solid-fill button/badge backgrounds with --parchment text on top.
       Darkened so parchment-on-top clears AAA (7:1). */
    --blaze-dark:#8f3a06;                         /* was #8f3405 → parchment-on-top now 7.34:1 (was 6.08) */
    --moss-dark:#245236;                          /* unchanged: parchment-on-top 8.52:1 (AAA) */
    --pewter:#c3c9cc;                             /* was #9aa0a6 → 9.68:1 on cab, 7.90:1 on drw (AAA) */
    --pewter-dark:#3d4a46;
    --water:#2ec4d6;
    --water-bright:#7fe3ee;
    --water-dark:#0f5560;
    /* One accent per mode (tiles, setup, result stamps). */
    --mode-daily:var(--blaze);
    --mode-puzzle:#d9a52a;
    --mode-fieldid:#9b6cff;
    --mode-intel:#5ab0ff;
    --mode-auction:var(--water);
    --mode-roll:var(--moss-bright);                        /* was #5d6266 → parchment-on-top now 7.63:1 (was 4.75) */
    /* --font-display is Black Ops One - an actual military stencil face,
       not a font chosen to evoke one. One weight, works at both hero and
       small-caps-label sizes (unlike the old Bungee Inline/Bungee split,
       which needed two variants because the inline stripe broke down
       below ~28px) - so display and display-small collapse into the same
       face now. */
    --font-display: "Black Ops One", Impact, "Oswald", ui-sans-serif, sans-serif;
    --font-display-small: "Black Ops One", Impact, "Oswald", ui-sans-serif, sans-serif;
    /* --font-stamp carries every small caps-locked meta label (case
       numbers, bonus tags, wallet labels, trait chips) same as before -
       still Special Elite (the body face) rather than a genuine monospace,
       because Special Elite simulates a real manual typewriter, which
       already renders numerals at near-uniform width. Tabular alignment
       where digits actually need to line up in a column (Fight Power,
       wallet balances, population counts) comes from
       font-variant-numeric:tabular-nums on the specific rule, not from
       carrying a whole separate monospace face for 109 call sites that
       were never about alignment in the first place. */
    --font-stamp: "Nunito", ui-sans-serif, system-ui, sans-serif;
    /* --font-body is Special Elite - a worn manual-typewriter face, real
       field-manual/requisition-form texture rather than a generic
       humanist sans. Line-height is generous (see body{} below) since a
       typewriter face reads slightly denser than Nunito did at the same
       size. */
    --font-body: "Nunito", ui-sans-serif, system-ui, sans-serif;
    /* --font-legible is the one deliberate exception to the two-font system
       above. Special Elite's typewriter texture works at display sizes and
       for short labels, but Daily Intel is a dense-reading widget -
       a full sentence of question text, four answer choices, then a fact
       to read - where that same texture measurably costs legibility at
       body-copy sizes. Inter is a UI face built for exactly that job; used
       ONLY for the trivia panel's actual reading content (question/choices/
       fact), not its short caps-locked labels, which stay on --font-stamp
       for visual continuity with the rest of the tile. */
    --font-legible: "Nunito", ui-sans-serif, system-ui, sans-serif;
  }
  /* --------------------------------------------------------------------
     Chipped-tile shape
     --------------------------------------------------------------------
     Every animal-tile in the game (draft candidate, auction card, roster
     row slot, home mode CTA, trophy square, avatar tile) used to sit on
     the same rounded 6-14px border-radius. That reads as generic "web
     card" chrome; the animal-army vibe wants a torn field-guide look
     instead.

     Approach: replace border-radius with a clip-path polygon that shaves
     a small triangular notch out of two opposite corners (top-right and
     bottom-left). Asymmetric on purpose - a symmetric bite reads as a
     styled octagon and drifts back toward "neat rounded thing", while
     the diagonal notch reads as hand-cut. --chip-size defaults to 10px;
     small tiles override to 6-7px so the notch stays proportional.

     Two costs to know about:

     1) clip-path clips box-shadow too. Any tile that uses box-shadow for
        a hover ring or a pulse (auction-card-enemy-taken, mode-cta.active)
        must switch to filter: drop-shadow on its container OR accept the
        clipped shadow. We handle each site inline below.

     2) On iOS Safari (Capacitor target) clip-path is fully supported and
        anti-aliased. On Android WebView it is supported from API 74+;
        the app already targets modern Chromium so this is a non-issue.
        prefers-reduced-motion is unaffected - clip-path is a static
        shape, not an animation. */
  :root{ --chip-size: 10px; }

  *{box-sizing:border-box;}
  html,body{margin:0;padding:0;}
  button{ touch-action:manipulation; }
  /* Field-guide-marginalia icon set, replacing the emoji nav/UI icons
     (📖🏆👤📚🔎🦴🔮 and the trophy-case padlock) - one ink weight, no
     fill, sized off the surrounding text so it never needs its own
     font-size rule at each call site. See animalGlyph()/uiIcon() in
     01-setup-and-pools.js for how these strings get built. */
  .ui-icon{
    width:1em; height:1em; vertical-align:-0.15em;
    fill:none; stroke:currentColor; stroke-width:1.7;
    stroke-linecap:round; stroke-linejoin:round;
    flex-shrink:0;
  }
  body{
    min-height:100vh;
    background:var(--cabinet);
    color:var(--parchment);
    font-family:var(--font-body);
    /* Special Elite reads denser than Nunito did at the same size - a
       little extra line-height keeps body copy (rules panel, encyclopedia
       entries) from feeling cramped. */
    line-height:1.45;
    display:flex;
    flex-direction:column;
    align-items:center;
    justify-content:center;
    /* The top padding is a reservation for the fixed .top-strip, not styling.
       It has to live in the base rule rather than in a max-width query,
       because justify-content:center only clears the strip while the page is
       shorter than the window - and the home screen is not: it is ~1200px tall
       at desktop widths, so it overflows every realistic viewport height and
       the first child lands on this padding every time. That is what made the
       masthead sit under the strip at 920px and up.

       --top-strip-h is measured from the real strip and written by
       observeTopStrip() in js/game.js; the literals here are the fallback for
       before that first measurement lands (and if ResizeObserver is missing).
       72px is the single-row strip (12px padding + a 38px control + 12px) plus
       10px of air. The phone breakpoints raise it where the strip stacks.

       A static reservation is what broke: #syncNotice and #authNotice are
       children of the strip that appear only in the states that need
       explaining (conflict / error / offline / stale), and the conflict copy
       runs to two or three lines on a phone. The strip grew, 72px did not, and
       the notice was painted straight over the masthead. No fixed number can
       be right here, because the height depends on how the sentence wraps at
       the current width - so the number is measured instead of guessed. */
    padding:var(--top-strip-h, 72px) 16px 32px;
    overflow-x:hidden;
    position:relative;
  }

  /* Tonal camo wash, replacing the old flat safari-green fill (#0e4b29) -
     built from the same cabinet/drawer/moss tokens as the rest of the
     page rather than a separate hardcoded color, so it reads as a
     texture on the existing ground instead of a second palette. Kept
     low-contrast and full-bleed behind everything (per the Phase 2
     camo note: texture, not costume) - never placed on a small surface
     at full strength where it would compete with dense UI. */
  .bg-sky{
    position:fixed;
    inset:0;
    z-index:-4;
    background-color:var(--cabinet);
    /* Clean gradient plus a faint topographic contour texture; the blurry
       blobs are gone. Arena colors still override this inline (render()). */
    background-image:
      repeating-radial-gradient(circle at 18% 28%, transparent 0 46px, rgba(255,255,255,0.028) 46px 47px),
      repeating-radial-gradient(circle at 82% 72%, transparent 0 58px, rgba(255,255,255,0.022) 58px 59px),
      radial-gradient(120% 70% at 50% -10%, color-mix(in srgb, var(--moss) 30%, var(--cabinet)) 0%, transparent 60%),
      linear-gradient(180deg, var(--cabinet) 0%, var(--cabinet-deep) 100%);
    transition: background 0.7s ease;
  }
  .bg-scrim{
    position:fixed;
    inset:0;
    z-index:-1;
    pointer-events:none;
    background: linear-gradient(180deg,
      rgba(10,4,6,0.45) 0%,
      rgba(10,4,6,0.08) 22%,
      rgba(10,4,6,0.04) 62%,
      rgba(10,4,6,0.32) 100%);
  }
  .masthead{
    text-align:center;
    margin-bottom:22px;
  }
  .masthead .kicker{
    font-family:var(--font-stamp);
    font-size:11px;
    letter-spacing:0.28em;
    text-transform:uppercase;
    color:var(--parchment-dim);
    text-shadow:0 2px 10px rgba(0,0,0,0.6);
  }
  /* Masthead H1 is the biggest text on any screen (home wordmark, mode
     title) - the ONE place Bungee Inline lives at full size. Letter-spacing
     drops from 0.06em to 0.02em because the inline outline already carries
     its own visual breathing room; the old tracking made it read as a
     stretched banner instead of a stamp. */
  .masthead h1{
    /* Bungee Inline needs ~28px+ for its inline stripe to read. clamp
       floor bumped from 22px to 30px so the display personality survives
       on the 320px iPhone SE viewport - at 22px the stripes disappeared
       and the H1 read as a generic monospace bold. */
    font-family:var(--font-display);
    font-weight:400;
    font-size:clamp(30px, 6vw, 40px);
    letter-spacing:0.02em;
    text-transform:uppercase;
    text-shadow:0 3px 16px rgba(0,0,0,0.65);
    margin:6px 0 0;
    text-wrap:balance;
  }

  .fight-layout{
    display:flex;
    flex-direction:column;
    align-items:center;
    width:100%;
    gap:14px;
  }
  .fight-side{ display:none; }
  @media (min-width:1000px){
    .fight-layout.in-fight{
      flex-direction:row;
      align-items:flex-start;
      justify-content:center;
      gap:20px;
    }
    .fight-layout.in-fight .card{ flex-shrink:0; }

    /* The rail drove the height of every desktop screen, and none of it was
       the thing the player was looking at.

       .trophy-grid is auto-fill minmax(104px, 1fr), written when the case
       lived full-width under the card - two columns on a phone, three or four
       on a wide screen. Inside this 230px rail the content box is 192px, and
       two 104px tracks plus the 8px gap need 216px, so auto-fill resolved to a
       single track: grid-template-columns computed to literally "192px". That
       stacked all 21 trophies vertically at 77px each - a 1773px grid in a
       1894px panel, making the rail 2385px tall beside a 463px draft card.
       Measured on a 1440x900 laptop the document came to 2562px against a
       900px viewport, so ~1660px of every screen - home, setup, draft, fight -
       was empty rail below the fold. The trophy case is reference material;
       it should not set the scroll length of the screen you play on.

       Fixed by bounding the grid instead of the page: ~4 rows are visible and
       the rest scroll inside the panel, so all 21 stay reachable while the
       document height goes back to being driven by the card. The cap is on
       .trophy-grid rather than .fight-side because the panel title, the
       .trophy-explain expander and the Animals Discovered rows underneath it
       must stay visible - a tapped trophy writes its explanation into
       .trophy-explain directly below the grid, and putting the scroll on the
       whole rail would let that explanation open outside the scrollport.

       Deliberately not position:sticky: body sets overflow-x:hidden, which
       computes its overflow-y to auto and makes body a scroll container of
       auto height, so a sticky rail inside it has no scrollport that moves and
       would silently never stick. */
    .progress-drawer .trophy-grid{
      max-height:340px;
      overflow-y:auto;
      overscroll-behavior:contain;
      padding-right:6px;
      scrollbar-width:thin;
      scrollbar-color:rgba(255,255,255,0.24) transparent;
    }
    .progress-drawer .trophy-grid::-webkit-scrollbar{ width:8px; }
    .progress-drawer .trophy-grid::-webkit-scrollbar-thumb{
      background:rgba(255,255,255,0.24); border-radius:999px;
    }
  }

  /* Past 1000px the card (460px) and the rail (230px) stop growing at all -
     on a 1920px monitor that's ~690px of content adrift in well over a
     thousand pixels of bare background, the same fixed width whether the
     screen is a small laptop or a wide desktop monitor. Two more steps,
     each widening both columns together so the two-column proportion
     stays intact rather than one side ballooning past the other.
     .card.draft-layout and its own .auction-extreme-layout widening
     (declared separately, at higher specificity) are untouched by the
     plain .card rule here - the draft screens already tune their own
     width for exactly this real estate.
     The trophy grid needs no extra rule to benefit: it's already
     auto-fill minmax(104px,1fr) (see .trophy-grid below), so a wider
     rail resolves to more columns on its own - 2 once the rail's content
     box clears ~216px, 3 once it clears ~328px. */
  @media (min-width:1400px){
    .fight-layout.in-fight{ gap:28px; }
    .card:not(.draft-layout){ width:min(600px, 100%); }
  }
  @media (min-width:1800px){
    .fight-layout.in-fight{ gap:32px; }
    .card:not(.draft-layout){ width:min(680px, 100%); }
  }

  .fight-panel-box{
    background:rgba(0,0,0,0.28);
    border:1px solid rgba(255,255,255,0.12);
    border-radius:8px;
    padding:16px 18px;
    color:rgba(244,240,232,0.9);
    /* The stats panel column is narrow (~200px on desktop, less on some
       phones), and it hosts long numeric strings like the level-meta line
       ("1,234 / 1,567 XP · 333 XP to Level 100") that can otherwise push
       past the panel's right border. min-width:0 lets the flex/grid child
       shrink below its content width instead of overflowing its column. */
    min-width:0;
  }
  .fight-panel-title{
    margin:0;
    font-family:var(--font-stamp);
    font-weight:800;
    font-size:12px;
    letter-spacing:0.1em;
    text-transform:uppercase;
    color:rgba(244,240,232,0.6);
    margin-bottom:10px;
  }
  .fight-stat-row{
    display:flex; justify-content:space-between; align-items:baseline;
    font-size:13px; padding:5px 0; border-bottom:1px solid rgba(255,255,255,0.08);
  }
  .fight-stat-row:last-of-type{ border-bottom:none; }
  .fight-stat-row .val{ font-weight:700; font-variant-numeric:tabular-nums; }
  .fight-history-list{ margin-top:10px; display:flex; flex-direction:column; gap:5px; }
  .fight-history-item{
    display:flex; justify-content:space-between; font-size:12px;
    color:rgba(244,240,232,0.7);
  }
  .fight-history-item .win{ color:#8fd66a; }
  .fight-history-item .lose{ color:#ff8a76; }
  .fight-empty-note{ font-size:12.5px; color:rgba(244,240,232,0.55); }
  .fight-collection-link{
    display:block; width:100%; margin-top:6px; min-height:24px;
    background:transparent; border:none; padding:5px 0;
    font-family:var(--font-stamp); font-size:12px; font-weight:700;
    text-align:left; color:var(--blaze-bright); cursor:pointer;
  }
  .fight-collection-link:hover{ text-decoration:underline; }
  /* auto-fill rather than a fixed two columns: the case doubled to 20 entries,
     and a fixed pair made it a ten-row scroll on every width. Narrow phones
     still land on two columns; anything wider packs three or four. */
  .trophy-grid{
    display:grid;
    grid-template-columns:repeat(auto-fill, minmax(104px, 1fr));
    gap:8px;
  }
  /* Trophy tile: --chip-size:6px because these are 104px minimum-width
     squares in an auto-fill grid, small enough that a 10px chip would
     start cutting the emoji at grayscale/unlocked color transitions. */
  .trophy-item{
    display:flex; flex-direction:column; align-items:center; text-align:center; gap:3px;
    background:rgba(255,255,255,0.04); padding:8px 4px;
    --chip-size:6px;
    clip-path: polygon(
      0 0,
      calc(100% - var(--chip-size)) 0,
      100% var(--chip-size),
      100% 100%,
      var(--chip-size) 100%,
      0 calc(100% - var(--chip-size))
    );
    font-size:10.5px; color:rgba(244,240,232,0.4);
  }
  .trophy-item.unlocked{ color:rgba(244,240,232,0.85); background:rgba(255,196,64,0.1); }
  .trophy-emoji{ font-size:20px; filter:grayscale(1); opacity:0.5; }
  .trophy-item.unlocked .trophy-emoji{ filter:none; opacity:1; }
  /* Locked tiles inherit .trophy-item's dim colour, which sat at 3.51:1 on the
     tile background - under AA, and fine while the word was only ever "Locked"
     next to a padlock. Now that the tile carries the trophy's actual name it is
     text somebody reads, so the name alone is lifted to 6.70:1. Everything else
     on a locked tile stays dim, and the padlock, the greyed emoji and the amber
     unlocked background still carry the state without relying on this. */
  .trophy-name{ line-height:1.3; color:rgba(244,240,232,0.62); }
  .trophy-item.unlocked .trophy-name{ color:inherit; }
  .trophy-xp{
    font-family:var(--font-stamp);
    font-size:9px; letter-spacing:0.06em;
    color:rgba(244,240,232,0.4);
  }
  .trophy-item.unlocked .trophy-xp{ color:#ffcf4d; }
  /* Tiles are now buttons - strip the browser defaults so they still read as
     tiles, and add a pressed/selected state so the player can see which one
     the explanation panel is describing. */
  button.trophy-item{
    border:none; font:inherit; cursor:pointer;
    width:100%;
  }
  button.trophy-item:hover{ background:rgba(255,255,255,0.08); }
  button.trophy-item.unlocked:hover{ background:rgba(255,196,64,0.16); }
  /* outline is clipped by the parent's clip-path polygon; render the
     focus ring as an inset shadow so it follows the chipped edges. */
  button.trophy-item:focus-visible{ outline:none; box-shadow: inset 0 0 0 2px var(--brass-bright); }
  .trophy-item.selected{ box-shadow:inset 0 0 0 2px var(--brass-bright); }
  /* The explanation panel that appears when a trophy is tapped. Sits right
     under the grid so it never scrolls out of view on a phone. */
  .trophy-explain{
    display:flex; align-items:flex-start; gap:10px;
    margin-top:10px; padding:10px 12px;
    background:rgba(255,255,255,0.05); border-radius:6px;
    border:1px solid rgba(255,255,255,0.08);
  }
  .trophy-explain[hidden]{ display:none; }
  .trophy-explain.unlocked{
    background:rgba(255,196,64,0.12);
    border-color:rgba(255,196,64,0.35);
  }
  .trophy-explain-emoji{
    font-size:26px; line-height:1;
    filter:grayscale(1); opacity:0.55;
  }
  .trophy-explain.unlocked .trophy-explain-emoji{ filter:none; opacity:1; }
  .trophy-explain-body{ flex:1; min-width:0; }
  .trophy-explain-name{
    font-weight:800; font-size:13px; letter-spacing:0.02em;
    color:rgba(244,240,232,0.92); margin-bottom:2px;
  }
  .trophy-explain-desc{
    margin:0 0 4px; font-size:12px; line-height:1.4;
    color:rgba(244,240,232,0.78);
  }
  .trophy-explain-meta{
    font-family:var(--font-stamp);
    font-size:9.5px; letter-spacing:0.08em;
    color:rgba(244,240,232,0.5);
  }
  .trophy-explain.unlocked .trophy-explain-meta{ color:#ffcf4d; }

  /* Player level ladder - the same bar is reused by the stats panel and the
     result overlay, so the sizing lives here rather than in either caller. */
  .level-headline{
    font-family:var(--font-stamp);
    font-size:26px; font-weight:800; line-height:1;
    color:#f4f0e8; margin-bottom:10px;
    /* "Level 100 / 100" at 26px is on the edge of what fits in a ~164px
       content box after padding. min-width:0 lets the text span shrink
       when the container is narrower, and overflow:hidden with
       text-overflow:ellipsis is the final safety net if a future rename
       or a translated string ever pushes it over. */
    min-width:0; overflow:hidden; text-overflow:ellipsis;
  }
  .level-of{
    font-size:13px; font-weight:700; letter-spacing:0.06em;
    color:rgba(244,240,232,0.45); margin-left:6px;
  }
  .level-bar{
    height:8px; border-radius:999px; overflow:hidden;
    background:rgba(255,255,255,0.1);
    border:1px solid rgba(255,255,255,0.08);
  }
  .level-bar-fill{
    height:100%; border-radius:999px;
    /* Was a literal orange-to-gold gradient (#ffb03a/#ffd977) - flat
       --blaze-bright fill instead, matching how every other progress-
       style indicator in the app reads (solid, not a decorative wash). */
    background:var(--blaze-bright);
    transition:width 420ms ease-out;
    min-width:2px;
  }
  .level-meta{
    margin-top:6px; font-size:11.5px;
    color:rgba(244,240,232,0.6);
    font-variant-numeric:tabular-nums;
    /* The meta line at high levels reads "1,234 / 1,567 XP · 333 XP to
       Level 100" - roughly 40 chars at 11.5px, or ~220px wide, which is
       wider than the ~164px content box on the desktop stats panel. Let
       the line wrap onto two rows instead of spilling past the right
       border. overflow-wrap:anywhere covers the long-XP-total edge case
       where the natural break points are far apart. */
    overflow-wrap:anywhere; word-break:break-word;
  }
  .fight-streak-banner{
    font-family:var(--font-stamp);
    font-weight:800;
    font-size:13px;
    text-align:center;
    color:#ffcf4d;
    background:rgba(255,196,64,0.14);
    border-radius:6px;
    padding:6px 8px;
    margin-bottom:8px;
  }

  /* Flat Modern: cards sit flat, no tilt. Used to carry transform:rotate(-1.1deg)
     (straightening on hover/focus) plus a matching rotation in the settle
     keyframe below - the "paper card set down askew" effect. Both removed;
     the settle animation keeps its fade/translateY entrance, just without
     the tilt. */
  .card{
    position:relative;
    width:min(460px, 100%);
    background:
      linear-gradient(180deg, rgba(255,255,255,0.06), transparent 40%),
      var(--drawer);
    border:1px solid var(--drawer-edge);
    border-radius:14px;
    box-shadow: 0 22px 45px var(--drawer-shadow), 0 2px 0 var(--drawer-edge) inset;
    color:var(--parchment);
    padding:28px 26px 24px;
    transition: transform 0.35s ease;
    animation: settle 0.5s ease;
  }
  .card.gauntlet-theme{
    border-color:var(--moss-dark);
    box-shadow: 0 22px 45px var(--drawer-shadow), 0 0 0 2px var(--moss-dark), 0 2px 0 var(--drawer-edge) inset;
  }

  @keyframes settle{
    from{ opacity:0; transform:translateY(10px); }
    to{ opacity:1; transform:translateY(0); }
  }

  .case-no{
    font-family:var(--font-stamp);
    font-size:11px;
    color:var(--parchment-dim);
    letter-spacing:0.08em;
    font-variant-numeric: tabular-nums;
  }
  /* The auction/mythical draft round header - a brass rule underneath so
     it reads as a crate-manifest line item rather than a floating label. */
  .draft-ledger-header{
    padding-bottom:8px; margin-bottom:2px;
    border-bottom:1px solid var(--drawer-edge);
  }
  .draft-ledger-sep{ color:var(--brass-bright); }
  /* The one decorative case-no use ("Choose Your Battle") needs real
     heading weight now that the stamp-badge it used to sit beneath is
     gone; the two informational uses (draft round/arena header, Gauntlet
     fighter count) keep the small dim label above unchanged. */
  .case-no-title{
    font-size:clamp(19px, 4vw, 23px);
    font-weight:800;
    letter-spacing:0.06em;
    color:var(--parchment);
  }

  .tally{
    display:flex;
    gap:6px;
    margin:14px 0 20px;
    flex-wrap:wrap;
  }
  .tally i{
    width:14px;height:14px;
    border:1.5px solid var(--parchment-dim);
    border-radius:2px;
    display:inline-block;
  }
  .tally i.done{ background:var(--moss); border-color:var(--moss-dark); }
  .tally i.now{ border-color:var(--blaze); }

  .prompt{
    font-family:var(--font-body);
    font-size:clamp(19px, 3.4vw, 23px);
    line-height:1.35;
    font-weight:600;
    margin:0 0 22px;
    text-wrap:balance;
    min-height:64px;
  }

  .lede{
    font-size:15px;
    line-height:1.55;
    color:var(--parchment-dim);
    margin:0 0 20px;
  }

  /* .btn-yes / .btn-no / .fight-assign-row went with Reroll & Swap. They only
     ever styled that style's Keep It / Re-Spin pair, which was the one place in
     the game that asked a yes-or-no question about a single dealt animal. Every
     surviving draft style puts the candidates themselves on screen as buttons,
     so there is nothing left to accept or reject. */

  /* The candidates are the whole screen now that they sit above the rosters
     rather than below them, so they are sized like it: the emoji is the
     largest thing on the draft screen and the card is a comfortable thumb
     target rather than a label with a picture on it. */
  .double-spin-grid{ display:grid; grid-template-columns:1fr 1fr; gap:12px; margin:14px 0 4px; }
  /* --chip-size:8px on this tile (smaller than the default 10px): a
     draft/auction card is one of the smallest surfaces on the screen -
     the two-up grid still puts these at ~140px wide - and a 10px chip
     ate visibly into the sprite on the 5-up auction board. 8px reads as
     a notch without stealing sprite real estate. clip-path replaces the
     old 6px border-radius: the two together would render an inconsistent
     shape (clip-path wins visually, border-radius only affects hit-test
     and outline), and the border still renders correctly because
     clip-path clips it uniformly - a 2px border becomes a 2px band on
     every edge of the polygon, chipped corners included. */
  .double-spin-card{
    display:flex; flex-direction:column; align-items:center; justify-content:center; gap:8px;
    padding:22px 10px; min-height:132px;
    --chip-size:8px;
    clip-path: polygon(
      0 0,
      calc(100% - var(--chip-size)) 0,
      100% var(--chip-size),
      100% 100%,
      var(--chip-size) 100%,
      0 calc(100% - var(--chip-size))
    );
    /* Same clip-path-vs-border story as .mode-cta - the real border
       gets sheared away on the diagonal chip corners, leaving only
       top/bottom edges visible. Inset box-shadow paints inside the
       clipped shape so the outline follows every edge. */
    border:0;
    box-shadow: inset 0 0 0 2px var(--moss);
    background:rgba(92,143,58,0.06); cursor:pointer; color:var(--parchment);
    transition:transform 0.12s ease, background 0.12s ease, box-shadow 0.12s ease;
  }
  .double-spin-card:hover{ background:rgba(92,143,58,0.16); transform:translateY(-1px); }
  /* Focus ring converted to inset shadow so it survives the parent's
     clip-path (outline paints outside the polygon and is sheared away).
     Stacks over the default pine border-shadow: 2px ink-text ring inset,
     then the standing 2px pine border underneath it. */
  .double-spin-card:focus-visible{
    outline:none;
    box-shadow: inset 0 0 0 2px var(--parchment), inset 0 0 0 4px var(--moss);
  }
  /* 68px rather than the original 54px: the draft card is the highest-
     traffic screen in the game (every round, every draft) and had real
     padding to spare - .double-spin-card is 22px 10px on a card that never
     exceeds 460px wide (see .card), so a bigger sprite here costs nothing
     in either the two-up or the (narrower) three-up layout below. */
  .double-spin-card .emoji{ font-size:68px; line-height:1; }
  /* Animal names on draft cards read as animal-poster nameplates now: the
     display face plus the sprite is the whole identity of a card. Weight
     drops to 400 - Bungee Inline is single-weight anyway - and tracking
     goes to 0 because the inline outline reads as extra spacing already.
     Nudged one step (13->14px) because the outline eats visible weight
     and the old size read as underweight next to a 68px sprite. */
  .double-spin-name{ font-family:var(--font-display-small); font-weight:400; font-size:14px; text-transform:uppercase; letter-spacing:0; text-align:center; }
  /* Each candidate is a card plus its rulebook link in a column. It stretches
     rather than centers because a centering wrapper let every card shrink to the
     width of the animal's name - two picks of equal weight were rendering as
     boxes of visibly different size, and the one with the longer name wrapped to
     two lines and grew taller as well. The height:100% pair makes both cards
     match even when one name wraps. */
  .double-spin-slot{ display:flex; flex-direction:column; align-items:stretch; gap:6px; height:100%; position:relative; }
  .double-spin-slot .double-spin-card{ flex:1 1 auto; }

  /* Small "learn about this animal" affordance sitting on the card's own
     corner, not inside its <button> (a button can't nest a button) - an
     absolutely-positioned sibling anchored to the slot reads as part of the
     card without being one. Sits above the card visually via z-index since
     the card itself has no stacking context of its own. */
  .card-info-btn{
    position:absolute; left:6px; bottom:6px; z-index:2;
    width:22px; height:22px; border-radius:999px;
    background:rgba(20,16,8,0.72); border:1px solid rgba(255,255,255,0.35);
    color:#f4ead2; font-family:var(--font-stamp); font-size:12px; font-weight:700;
    line-height:1; display:flex; align-items:center; justify-content:center;
    cursor:pointer; padding:0; transition:background 0.15s ease, transform 0.15s ease;
  }
  .card-info-btn:hover, .card-info-btn:focus-visible{
    background:rgba(20,16,8,0.92); transform:scale(1.08);
    outline:2px solid rgba(255,255,255,0.6); outline-offset:1px;
  }

  /* Both rerolls read as escape hatches rather than as the point of the round:
     underlined text, no fill, no border, at the size of the notes around them.
     .draft-pair-reroll spans the full grid width so it still reads as acting on
     every card rather than belonging to one of them. */
  .draft-pair-reroll{
    display:block; width:100%; margin:8px 0 0; padding:6px 4px;
    border:0; background:transparent; cursor:pointer;
    font-family:inherit; font-size:12px; line-height:1.4; color:var(--parchment-dim);
    text-decoration:underline; text-decoration-style:dotted; text-underline-offset:3px;
  }
  .draft-pair-reroll:hover{ color:var(--parchment); }
  .draft-pair-reroll:focus-visible{ outline:2px solid var(--parchment); outline-offset:2px; border-radius:3px; }
  .draft-pair-reroll-note{
    margin:6px 0 0; text-align:center; font-size:12px; color:var(--parchment-dim);
    font-style:italic;
  }

  /* The one-use rerolls (opponent, arena, enemy's last pick, spin-choice
     slots) all share this token look - a held object, not a plain menu
     button - so spending one reads as a real decision rather than a stray
     link. See fireRerollBurn (02-draft-and-combat.js): the button burns in
     place for ~300ms before the reroll actually fires and the next render
     removes it, so the click visibly costs something instead of the button
     just vanishing. */
  .reroll-btn{
    display:inline-flex; align-items:center; gap:7px;
    background:linear-gradient(180deg, rgba(211,86,15,0.22), rgba(211,86,15,0.10));
    border:1.5px solid var(--blaze);
    border-radius:6px;
    padding:9px 14px;
    font-family:var(--font-stamp);
    font-weight:700;
    font-size:12.5px;
    color:var(--parchment);
    cursor:pointer;
    transition:background 0.15s ease, border-color 0.15s ease, transform 0.12s ease;
  }
  .reroll-btn .ui-icon{ color:var(--blaze-bright); }
  .reroll-btn:hover{ background:linear-gradient(180deg, rgba(211,86,15,0.32), rgba(211,86,15,0.16)); border-color:var(--blaze-bright); transform:translateY(-1px); }
  .reroll-btn:focus-visible{ outline:2px solid var(--blaze-bright); outline-offset:2px; }
  .reroll-btn-uses{
    font-size:9.5px; font-weight:600; letter-spacing:0.04em; text-transform:uppercase;
    color:var(--blaze-bright); opacity:0.85;
  }
  .reroll-btn-compact{ padding:5px 10px; font-size:10.5px; margin:0; align-self:center; }
  .reroll-btn-compact .reroll-btn-uses{ font-size:8.5px; }
  .reroll-btn-burning{
    animation: rerollBurn 0.3s ease-out forwards;
    pointer-events:none;
  }
  @keyframes rerollBurn{
    0%{ filter:brightness(1) saturate(1); transform:scale(1); opacity:1; }
    45%{ filter:brightness(2.6) saturate(1.8); transform:scale(1.06); }
    100%{ filter:brightness(0.5) saturate(0.6); transform:scale(0.9); opacity:0.1; }
  }
  @media (prefers-reduced-motion:reduce){
    .reroll-btn-burning{ animation:none; opacity:0.4; }
  }

  .start-btn, .primary-btn{
    /* Big action buttons pick up the display-small (solid Bungee) so the
     top-level CTA reads as poster-lettering, not as an interface stamp.
     Font size nudged 13->15 and tracking pulled in from 0.1em to 0.03em
     because Bungee is already chunky - the old wide tracking made it
     look like a spaced monospace instead of a display face. */
    font-family:var(--font-display-small);
    font-weight:400;
    text-transform:uppercase;
    letter-spacing:0.03em;
    font-size:15px;
    padding:13px 18px;
    width:100%;
    border-radius:4px;
    border:2px solid var(--parchment);
    background:var(--parchment);
    color:var(--drawer);
    cursor:pointer;
    transition:transform 0.12s ease;
  }
  .start-btn:hover, .primary-btn:hover{ transform:translateY(-1px); }
  .start-btn:focus-visible, .primary-btn:focus-visible{ outline:2px solid var(--blaze); outline-offset:2px; }

  /* Deliberately much quieter than .primary-btn - it's a real option (some
     players genuinely want to skip ahead), but the draft itself is the
     point of the game, so this shouldn't compete with Start Draft/Keep It
     for attention or look like the "correct" next step. */
  /* A quiet text link under the rosters: a way out of the draft, not the
     way in. It used to be a pill beside Spin, and read as the main action. */
  .auto-finish-btn{
    display:block;
    font-family:var(--font-legible);
    font-weight:500;
    font-size:12.5px;
    text-transform:none;
    letter-spacing:0;
    padding:6px 4px;
    margin:12px auto 0;
    border:0; background:transparent; box-shadow:none;
    color:var(--parchment-dim);
    text-decoration:underline; text-decoration-style:dotted; text-underline-offset:3px;
    border-radius:20px;
    border:1.5px solid var(--parchment-dim);
    background:transparent;
    color:var(--parchment-dim);
    cursor:pointer;
  }
  .auto-finish-btn:hover{ color:var(--parchment); border-color:var(--parchment); }
  .auto-finish-btn:focus-visible{ outline:2px solid var(--blaze); outline-offset:2px; }

  /* Auction auto-skip notice: appears the moment the player is stranded
     (see scheduleAuctionAutoSkipIfStranded). Not a button - the skip fires
     automatically after ~1400ms. Sized/styled to read like a hint rather
     than a call to action; the coin balance and locked cards on the board
     already tell the story, this just names what's happening. */
  .auction-auto-skip-notice{
    display:block;
    font-family:var(--font-stamp);
    font-weight:700;
    font-size:10.5px;
    text-transform:uppercase;
    letter-spacing:0.05em;
    padding:6px 12px;
    margin-left:auto; margin-right:auto;
    border-radius:20px;
    border:1.5px dashed var(--parchment-dim);
    background:transparent;
    color:var(--parchment-dim);
    text-align:center;
    max-width:280px;
  }

  .toggle-row{ display:grid; grid-template-columns:1fr 1fr; gap:8px; margin-bottom:10px; }
  /* Base vertical padding is sized so the button clears the ~44px touch
     target minimum on both iOS and Android; anything less and mobile
     testing showed users mis-tapping between adjacent habitat pills.
     Applies uniformly - every mode/habitat/pool toggle uses this class -
     so bumping it here is a one-line change that fixes the ergonomics
     everywhere the setup screen ever surfaces a segmented control. */
  .toggle-btn{
    font-family:var(--font-stamp);
    font-size:11.5px;
    font-weight:700;
    letter-spacing:0.04em;
    text-transform:uppercase;
    padding:14px 6px;
    min-height:44px;
    border-radius:4px;
    border:1.5px solid var(--parchment-dim);
    background:transparent;
    color:var(--parchment-dim);
    cursor:pointer;
  }
  .toggle-btn.active{
    border-color:var(--parchment);
    background:var(--parchment);
    color:var(--drawer);
  }
  /* A habitat with too few animals behind it: still readable, clearly not
     pressable, and carrying the count that ruled it out. Used to be a
     dashed border; Flat Modern drops dashing app-wide, so "not pressable"
     is now carried by the opacity dim plus a thinner border (1px vs the
     enabled 1.5px) rather than by the line style. */
  .toggle-btn.unavailable,
  .toggle-btn[disabled]{
    opacity:0.45;
    cursor:default;
    border-width:1px;
  }
  .toggle-btn .toggle-btn-count{
    font-variant-numeric:tabular-nums;
    opacity:0.85;
  }
  .toggle-btn .toggle-btn-count::before{ content:'\00b7\00a0'; }

  .setup-section-label{
    font-family:var(--font-stamp);
    font-weight:800;
    font-size:11px;
    letter-spacing:0.08em;
    text-transform:uppercase;
    color:var(--parchment-dim);
    margin:16px 0 8px;
  }
  .setup-section-label:first-of-type{ margin-top:6px; }
  .setup-toggle-check{
    display:flex; align-items:center; gap:8px;
    font-size:13px; color:var(--parchment);
    margin-bottom:6px; cursor:pointer;
  }
  .setup-toggle-check input{ width:16px; height:16px; flex-shrink:0; }
  /* Daily Challenge's twist is fixed for the day - shown checked but inert,
     matching how its opponent/arena/pool read as locked rather than absent. */
  .setup-toggle-check.locked,
  .setup-toggle-check.locked input{ cursor:default; }
  .setup-lock-note{ color:var(--parchment-dim); }
  /* Small yellow "+2 coins" badge that sits inline next to a filter
     label whenever it grants an Auction coin bonus. Same visual weight
     as the wallet coin count so the number reads as coin-flavored
     without dominating the label. */
  .setup-toggle-bonus{
    display:inline-block; margin-left:4px; padding:0 6px;
    background:rgba(255,203,88,0.18);
    border:1px solid rgba(255,203,88,0.45);
    border-radius:6px; font-size:11px; font-weight:600;
    color:var(--cabinet-accent, #d4a03e);
    white-space:nowrap;
  }
  /* Inline coin-bonus note in the auction lede ("20 coins (+2 from ...)"):
     lower-contrast than the primary coin count, but still legible - it
     explains a number the player will immediately see change on the wallet. */
  .auction-coin-bonus-note{
    color:var(--parchment-dim); font-weight:500;
    font-size:0.9em;
  }
  .opponent-grid{ display:grid; grid-template-columns:1fr 1fr; gap:8px; }
  .opponent-btn{
    display:flex; flex-direction:column; align-items:flex-start; gap:3px;
    text-align:left; padding:10px 10px; border-radius:4px;
    border:1.5px solid var(--parchment-dim); background:transparent; cursor:pointer;
  }
  .opponent-btn.active{ border-color:var(--parchment); background:rgba(0,0,0,0.06); }
  .opponent-btn.bonus-slot{ border-color:var(--brass-bright); background:rgba(217,150,46,0.1); }

  /* One rule for all five mode buttons. They used to be four near-identical
     blocks that differed only in colour, each filled edge-to-edge with a
     different saturated brand colour - amber, alarm red, near-black, green -
     which meant the home screen asked the player to choose between four things
     all shouting at the same volume, and nothing on it read as a starting
     point. A left accent stripe over a manila-tinted wash used to carry each
     mode's identity; Flat Modern drops the stripe (and the paper) entirely -
     the whole card is now a solid flat panel tinted with its own mode's
     accent color at low opacity, so hue still distinguishes the five cards
     without needing an edge treatment to do it. */
  /* Home-screen mode cards get the largest chip (--chip-size defaults to
     10px here): they are the widest tiles in the game at ~360px on
     phone and the notch has room to read as a clear shape from arm's
     length. clip-path replaces the old 14px border-radius. */
  .mode-cta{
    display:flex; flex-direction:column; align-items:flex-start; gap:5px;
    width:100%; padding:12px 13px; margin:0 0 9px;
    clip-path: polygon(
      0 0,
      calc(100% - var(--chip-size)) 0,
      100% var(--chip-size),
      100% 100%,
      var(--chip-size) 100%,
      0 calc(100% - var(--chip-size))
    );
    /* Inset box-shadow instead of border: clip-path clips a real border
       to the polygon edges, which reads as broken (only the top and
       bottom horizontals visible on the chipped tile). An inset shadow
       paints inside the padding box, follows the clipped outline, and
       reads as a proper edge on every viewport. --drawer-edge is what
       the old border used. */
    border:0;
    box-shadow: inset 0 0 0 1px var(--drawer-edge);
    /* WCAG luminance contrast is the wrong tool to tune this by - it's
       blind to hue, and every mode-accent is a dark color mixed into an
       already-dark panel, so the ratio stays near 1.0 regardless of mix %
       even when the hues are clearly distinct to the eye. Tuned visually
       (screenshot comparison of all five cards side by side), not by the
       contrast formula. */
    background: color-mix(in srgb, var(--mode-accent) 26%, var(--drawer));
    color:var(--parchment);
    text-align:left; cursor:pointer;
    transition:background 0.12s ease, box-shadow 0.12s ease;
  }
  .mode-cta:hover{
    background: color-mix(in srgb, var(--mode-accent) 38%, var(--drawer));
  }
  .mode-cta.active{
    background: color-mix(in srgb, var(--mode-accent) 50%, var(--drawer));
    box-shadow: inset 0 0 0 1.5px var(--mode-accent);
  }

  /* "NEW" (or any future flag) stamped in front of the mode's descriptor
     badge. Flat Modern: plain rust-red pill, no rotation - reads as an
     ordinary new-feature tag rather than a rubber stamp. Hidden entirely
     when the mode is locked (see the render in 05-screens-fight-and-boot.js). */
  .mode-cta-flag{
    display:inline-block;
    background:var(--blaze); color:var(--parchment);
    padding:0 5px; margin-right:4px;
    border-radius:3px;
    font-size:9px; letter-spacing:0.08em;
  }

  .beginner-cta{ --mode-accent:#8a5510; }
  .trivia-cta{ --mode-accent:#1c5c6b; }
  .gauntlet-cta{ --mode-accent:var(--blaze-dark); }
  /* Used to be var(--cabinet) - a fitting "hardest mode" near-black when it only
     had to work as a stripe/badge fill against light manila. As a panel
     tint it mixed into an already-dark --drawer and stopped reading as a
     distinct color at all (dark mixed into dark stays dark, regardless of
     hue). This is a dedicated deep brick-red instead - keeps the "harder,
     more intense" feeling through saturation rather than sheer darkness,
     and is a different enough hue from Gauntlet's rust to stay
     distinguishable once tinted. */
  .hardcore-cta{ --mode-accent:#5c1c1c; }
  .daily-cta{ --mode-accent:var(--moss-dark); }
  /* Amber-fossil, distinct from the other four (brown, rust, brick-red,
     pine green): reads as tarpit/amber, a warm fit next to the other four
     warm accents. Chalk badge text clears 4.5:1 against it comfortably. */
  .prehistoric-cta{ --mode-accent:#8a6a1c; }
  /* Auction mode's accent: a deep gold that reads as coin/bidding without
     colliding with Prehistoric's amber-fossil next door in FIGHT_MODE_ORDER
     (Prehistoric is warmer and more brown, this one is cooler and more
     yellow-gold). Chalk on this tint measures 5.7:1 - well past the 4.5:1
     minimum badge text needs. */
  .auction-cta{ --mode-accent:#7a5a12; }
  /* Mythical's accent breaks the palette on purpose: every other mode
     (brown, rust, brick-red, pine, amber-fossil, gold) is a warm earth
     tone, and Mythical is the one roster that isn't real - a violet
     reads as "magic" the way none of those could, and is unmistakable
     next to its neighbors at a glance. Chalk on this tint measures
     9.15:1, well past the 4.5:1 badge-text minimum every other accent
     here is held to. */
  .mythical-cta{ --mode-accent:#5a2a7a; }
  /* Roll tile bundles Beginner + Gauntlet + Hardcore into one home card,
     so it doesn't inherit any single mode's accent. It gets its own -
     Gauntlet's rust (the same value .gauntlet-cta uses) - because Gauntlet
     is the default sub-mode the tile opens on, and the sub-mode buttons
     inside re-tint using this variable. */
  .roll-cta{ --mode-accent:var(--blaze-dark); }

  /* Cancel .mode-cta's default cursor:pointer for the div-based Roll and
     Auction tiles: the head owns the click, not the whole panel. Daily
     stays a <button> and keeps its original cursor. */
  .mode-cta[data-tile]{ cursor:default; }

  /* Era selector: three pills on one row. Sits below the sub-mode row on
     the Roll tile and above the Play button on the Auction tile - either
     way it's the second thing a player looks at, so its label is spelled
     out ("Era") rather than left implicit. */
  .home-era-row{
    display:flex; align-items:center; gap:6px;
    flex-wrap:wrap;
  }
  .home-era-label{
    font-family:var(--font-body); font-size:11px; font-weight:600;
    text-transform:uppercase; letter-spacing:0.08em;
    color:rgba(243, 236, 224,0.78);
    margin-right:2px;
  }
  .home-era-btn{
    display:inline-block; padding:6px 10px; min-height:24px; border:0;
    border-radius:12px;
    background:rgba(255,255,255,0.10); color:var(--parchment);
    font-family:var(--font-body); font-size:12px; letter-spacing:0.02em;
    cursor:pointer;
    transition:background 0.12s ease;
  }
  .home-era-btn:hover{
    background:rgba(255,255,255,0.18);
  }
  .home-era-btn.active{
    background: var(--mode-accent);
    color:var(--parchment);
  }
  /* Locked pill (Prehistoric before the unlock, or the whole row while
     Beginner is the active sub-mode): dimmed and inert. Same not-hidden
     principle .mode-cta-locked uses - the pill still says "Prehistoric"
     rather than vanishing, and the tile-level sub-copy spells out what
     it takes to open it. */
  .home-era-btn.locked{
    opacity:0.45; cursor:default;
  }
  .home-era-btn.locked:hover{
    background:rgba(255,255,255,0.10);
  }

  /* Auction's single-mode Play button: sits at the bottom of the expanded
     panel and matches the accent-heavy "active" fill sub-mode buttons
     use, since it's the same commit action - one tap starts the fight. */
  .home-play-btn{
    display:block; width:100%;
    padding:9px 12px; border:0;
    clip-path: polygon(
      0 0, calc(100% - 6px) 0, 100% 6px,
      100% 100%, 6px 100%, 0 calc(100% - 6px)
    );
    box-shadow: inset 0 0 0 1.5px var(--mode-accent);
    background: color-mix(in srgb, var(--mode-accent) 46%, var(--drawer));
    color:var(--parchment);
    font-family:var(--font-display-small); font-weight:400; font-size:14px;
    text-transform:uppercase; letter-spacing:0.03em;
    text-align:center; cursor:pointer;
    transition:background 0.12s ease;
  }
  .home-play-btn:hover{
    background: color-mix(in srgb, var(--mode-accent) 60%, var(--drawer));
  }

  /* Daily Intel fullscreen view: the trivia panel is painted directly
     into #card in place of the home deck (see the dailyIntelOpen branch
     in render()). The header floats a Back button on the left and the
     mode title in the middle, sitting above the panel with the same
     vertical rhythm the top of the home deck uses. The panel itself
     reuses .trivia-panel (below) so the questions, results, share row,
     bonus round and countdown all lay out identically to the way they
     used to inside the expandable tile. */
  .trivia-fullscreen{ display:flex; flex-direction:column; gap:14px; }
  .trivia-fullscreen-header{
    display:flex; align-items:center; gap:10px;
    flex-wrap:wrap; row-gap:8px;
    padding-bottom:10px;
    border-bottom:1px solid rgba(255,255,255,0.12);
  }
  .trivia-fullscreen-back{
    appearance:none; border:1px solid rgba(255,255,255,0.18);
    background:rgba(255,255,255,0.06); color:var(--parchment);
    font-family:var(--font-stamp); font-size:12px; font-weight:800;
    letter-spacing:0.08em; text-transform:uppercase;
    padding:8px 12px; border-radius:8px; cursor:pointer;
    transition:background 120ms ease, border-color 120ms ease;
  }
  .trivia-fullscreen-back:hover{
    background:rgba(255,255,255,0.12); border-color:rgba(255,255,255,0.3);
  }
  .trivia-fullscreen-title{
    font-family:var(--font-stamp); font-size:14px; font-weight:800;
    letter-spacing:0.08em; text-transform:uppercase;
    color:var(--parchment);
    display:inline-flex; align-items:center; gap:6px;
    white-space:nowrap;
  }
  /* The streak badge lives in the same row as the Back button and title;
     prevent its own copy ("12-day streak") from ever breaking mid-phrase
     so the header reads as three clean chips even when the panel column
     is narrow. If the three don't fit together, the header's flex-wrap
     lets the badge drop to its own line instead of mangling the words. */
  .trivia-fullscreen-header .daily-streak-badge{ white-space:nowrap; }
  /* At very narrow widths (phone / narrow panel), the icon-plus-title +
     streak badge combination can't fit on one line at 14px. Shrink both
     so the header stays a single row for as long as possible before the
     flex-wrap fallback kicks in. */
  @media (max-width:420px){
    .trivia-fullscreen-title{ font-size:12px; letter-spacing:0.06em; }
    .trivia-fullscreen-header .daily-streak-badge{ font-size:11px; padding:4px 8px; }
  }
  /* Panel content inside the fullscreen wrapper: no top border/padding
     because the header above already provides the visual separator that
     .home-tile-panel used to draw against the tile head. */
  .trivia-fullscreen-panel{
    margin-top:0; padding-top:0; border-top:0;
  }
  /* Field ID's reset control (see resetFieldIdToday(), 01-setup-and-
     pools.js, and fieldIdResetMenuHtml()/wireFieldIdResetMenu() in
     05-screens-fight-and-boot.js) - a small anchored menu pushed to the
     far right of the header. The trigger itself stays deliberately
     low-key (matches the old single-purpose button's footprint); the
     wrapper is what actually claims the header's margin-left:auto slot
     and the position:relative anchor for the dropdown panel below it. */
  .fieldid-reset-menu{
    position:relative; margin-left:auto; flex:0 0 auto;
    display:inline-flex;
  }
  .fieldid-reset-btn{
    appearance:none; border:1px solid rgba(255,255,255,0.14);
    background:transparent; color:var(--parchment-dim);
    font-family:var(--font-legible); font-size:11px;
    display:inline-flex; align-items:center; gap:4px;
    padding:4px 8px; border-radius:6px; cursor:pointer;
    transition:color 120ms ease, border-color 120ms ease;
  }
  .fieldid-reset-btn:hover{
    color:var(--parchment); border-color:rgba(255,255,255,0.3);
  }
  .fieldid-reset-caret{
    font-size:9px; line-height:1; transform:translateY(1px);
  }
  /* The dropdown panel: a small chipped tile anchored under the trigger
     on desktop, matching the purple-accent/inset-shadow language the
     rest of the Field ID panel uses (.fieldid-panel sets --fieldid-
     purple/--fieldid-purple-bright/--fieldid-display/--fieldid-body
     above). At mobile widths it switches to the same fixed bottom-sheet
     shell as .home-today-sheet so tap targets stay comfortable instead
     of a cramped anchored popover. */
  .fieldid-reset-panel{
    position:absolute; top:calc(100% + 6px); right:0; z-index:40;
    display:flex; flex-direction:column; gap:4px;
    min-width:220px; padding:6px;
    background:var(--drawer,#231913);
    /* contrast pass: border was --fieldid-purple #7c3aed (2.54:1 on drawer, FAIL for UI). Use the
       -bright variant (6.51:1 on cab, 5.31:1 on drw) so the panel edge is visible. */
    border:1px solid var(--fieldid-purple-bright, #a78bfa);
    box-shadow: inset 0 0 0 1px rgba(167,139,250,0.28), 0 10px 26px rgba(0,0,0,0.4);
    border-radius:10px;
    animation:fieldIdResetPanelIn 0.16s ease;
  }
  @keyframes fieldIdResetPanelIn{
    from{ transform:translateY(-4px); opacity:0; }
    to{ transform:translateY(0); opacity:1; }
  }
  @media (prefers-reduced-motion: reduce){
    .fieldid-reset-panel{ animation:none; }
  }
  .fieldid-reset-option{
    appearance:none; border:0; text-align:left; cursor:pointer;
    display:flex; flex-direction:column; gap:2px;
    padding:8px 10px; border-radius:7px;
    background:transparent; color:var(--parchment,#eadfc9);
    box-shadow: inset 0 0 0 1px transparent;
    transition:background 120ms ease, box-shadow 120ms ease;
  }
  .fieldid-reset-option:hover, .fieldid-reset-option:focus-visible{
    background: color-mix(in srgb, var(--fieldid-purple, #7c3aed) 18%, transparent);
    box-shadow: inset 0 0 0 1px var(--fieldid-purple-bright, #a78bfa);
    outline:none;
  }
  .fieldid-reset-option-label{
    font-family:var(--fieldid-display, var(--font-stamp));
    font-weight:400; font-size:13px; letter-spacing:0.01em;
    color:var(--parchment,#eadfc9);
  }
  .fieldid-reset-option-sub{
    font-family:var(--fieldid-body, var(--font-legible));
    font-size:11.5px; line-height:1.35;
    color:var(--parchment-dim,#a99a80);
  }
  /* Mobile: the same bottom-sheet shell as the Today-chip popover
     (.home-today-sheet, see below) rather than a cramped anchored
     dropdown - the established pattern for "this menu needs comfortable
     tap targets on a phone" elsewhere in the app. The wrapper itself
     doesn't need position:relative to matter here since the panel
     switches to position:fixed and centers itself. */
  @media (max-width:560px){
    .fieldid-reset-panel{
      position:fixed; inset:auto 0 0 0; top:auto; right:auto;
      z-index:1200; min-width:0; width:100%;
      border-radius:14px 14px 0 0; border-bottom:none;
      padding:14px 14px calc(14px + env(safe-area-inset-bottom, 0px));
      box-shadow: inset 0 0 0 1px rgba(167,139,250,0.18), 0 -10px 34px rgba(0,0,0,0.45);
      animation:fieldIdResetSheetUp 0.22s ease;
    }
    @keyframes fieldIdResetSheetUp{
      from{ transform:translateY(24px); opacity:0; }
      to{ transform:translateY(0); opacity:1; }
    }
    @media (prefers-reduced-motion: reduce){
      .fieldid-reset-panel{ animation:none; }
    }
    .fieldid-reset-option{ padding:12px 12px; }
    .fieldid-reset-option-label{ font-size:14px; }
    .fieldid-reset-option-sub{ font-size:12.5px; }
  }

  /* Daily Intel: question blocks inside the same .home-tile-panel shell
     every other expanded tile uses. Own gap since a question block
     (prompt + 4 choices + fact) is taller and more internally-structured
     than an era/variant row. */
  .trivia-panel{
    display:flex; flex-direction:column; gap:16px;
    width:100%;
  }
  .trivia-question-block{ display:flex; flex-direction:column; gap:7px; }
  .trivia-question-block + .trivia-question-block{
    padding-top:14px; border-top:1px solid rgba(255,255,255,0.08);
  }
  .trivia-question-num{
    font-family:var(--font-stamp); font-size:10.5px; font-weight:800;
    letter-spacing:0.08em; text-transform:uppercase;
    color:rgba(243,236,224,0.6);
  }
  .trivia-question-text{
    font-family:var(--font-legible); font-weight:500; font-size:15px; line-height:1.45;
    color:var(--parchment);
  }
  .trivia-choice-grid{
    display:grid; grid-template-columns:1fr 1fr; gap:6px;
  }
  @media (max-width:460px){
    .trivia-choice-grid{ grid-template-columns:1fr; }
  }
  .trivia-choice-btn{
    display:flex; align-items:center; justify-content:space-between; gap:6px;
    padding:8px 10px; border:0; border-radius:6px;
    box-shadow: inset 0 0 0 1px var(--drawer-edge);
    background: color-mix(in srgb, var(--mode-accent) 14%, var(--drawer));
    color:var(--parchment);
    text-align:left; font-family:var(--font-legible); font-weight:500; font-size:13.5px; line-height:1.35;
    cursor:pointer;
    transition:background 0.12s ease;
  }
  .trivia-choice-btn:hover:not(:disabled){
    background: color-mix(in srgb, var(--mode-accent) 28%, var(--drawer));
  }
  .trivia-choice-btn:disabled{ cursor:default; }
  .trivia-choice-correct{
    box-shadow: inset 0 0 0 1.5px #7bd88f;
    background: rgba(123,216,143,0.16);
    color:#c9f2d2;
  }
  .trivia-choice-wrong{
    box-shadow: inset 0 0 0 1.5px #ff8a76;
    background: rgba(255,138,118,0.14);
    color:#ffc7ba;
  }
  .trivia-choice-dim{ opacity:0.5; }
  .trivia-mark{ font-weight:800; }
  .trivia-fact{
    margin:2px 0 0; padding:9px 11px;
    background:rgba(0,0,0,0.2); border-radius:6px;
    border-left:2px solid var(--mode-accent);
    font-family:var(--font-legible); font-size:13px; line-height:1.5;
    color:rgba(244,240,232,0.92);
  }
  .trivia-summary{
    margin-top:2px; padding:9px 10px;
    background: color-mix(in srgb, var(--mode-accent) 30%, var(--drawer));
    box-shadow: inset 0 0 0 1.5px var(--mode-accent);
    border-radius:6px;
    font-family:var(--font-stamp); font-weight:700; font-size:12.5px;
    color:var(--parchment); text-align:center;
  }
  /* 5/5 gets a real moment: a burst-in on arrival (paired with a confetti
     launch fired once from the click handler that completes it) plus an
     ongoing glow pulse so it keeps reading as a win, not just a status
     line, for as long as the tile stays open. Named "Flawless" rather than
     "Perfect" to match the lifetime counter shown on the pre-play tile. */
  .trivia-summary-flawless{
    padding:11px 12px;
    font-size:14px;
    background: linear-gradient(135deg, rgba(211,86,15,0.38), rgba(28,92,107,0.38));
    box-shadow: inset 0 0 0 2px var(--blaze-bright), 0 0 16px rgba(211,86,15,0.35);
    animation:
      triviaPerfectBurst 0.55s cubic-bezier(0.2,0.85,0.3,1.4) both,
      triviaPerfectGlow 1.7s ease-in-out 0.55s infinite;
  }
  @keyframes triviaPerfectBurst{
    0%{ transform:scale(0.7); opacity:0; }
    60%{ transform:scale(1.08); opacity:1; }
    80%{ transform:scale(0.97); }
    100%{ transform:scale(1); }
  }
  @keyframes triviaPerfectGlow{
    0%,100%{ box-shadow: inset 0 0 0 2px var(--blaze-bright), 0 0 14px rgba(211,86,15,0.3); }
    50%{ box-shadow: inset 0 0 0 2px var(--blaze-bright), 0 0 28px rgba(211,86,15,0.6); }
  }
  @media (prefers-reduced-motion:reduce){
    .trivia-summary-flawless{ animation:none; }
  }
  /* Lifetime Flawless-days count shown on the pre-play tile head, once the
     player has hit 5/5 at least once - an aspirational number to chase
     back, sitting next to the streak badge rather than replacing it. */
  .trivia-flawless-count{
    display:inline-flex; align-items:center; gap:5px;
    margin:6px 0 0 8px; padding:4px 9px;
    font-family:var(--font-stamp); font-weight:800; font-size:11.5px;
    color:var(--parchment);
    background:rgba(211,86,15,0.16);
    border-radius:99px;
  }
  /* Deliberately link-sized, not a filled CTA button - toggling the answer
     review is a quiet secondary action, not the thing the tile is pushing
     you toward. */
  .trivia-keep-going{
    display:block; margin:4px auto 0; padding:6px 4px;
    border:0; background:transparent; cursor:pointer;
    font-family:var(--font-legible); font-size:12.5px; color:var(--parchment-dim);
    text-decoration:underline; text-decoration-style:dotted; text-underline-offset:3px;
  }
  .trivia-keep-going:hover{ color:var(--parchment); }
  /* The bonus round CTA reuses .home-play-btn's filled-button shell (see
     above) rather than the link-sized .trivia-keep-going treatment it used
     to share with the review toggle - it's a real next action worth a real
     button, not a quiet aside, so it should look like one. */
  .trivia-bonus-cta{ margin-top:6px; }

  /* Daily Intel: one question at a time. The progress row is five dots
     that fill green/red as they are answered; the current one carries the
     tile's accent ring. Each question block slides in on mount - render()
     rebuilds the DOM, so a mount animation plays on every advance for free. */
  .trivia-progress{ display:flex; gap:7px; align-items:center; }
  .trivia-dot{
    width:11px; height:11px; border-radius:50%;
    background:rgba(255,255,255,0.14);
    box-shadow: inset 0 0 0 1px rgba(255,255,255,0.18);
  }
  .trivia-dot-current{ box-shadow: 0 0 0 2px var(--mode-accent), inset 0 0 0 1px rgba(255,255,255,0.18); }
  .trivia-dot-correct{ background:#7bd88f; box-shadow:none; }
  .trivia-dot-wrong{ background:#ff8a76; box-shadow:none; }
  .trivia-dot-correct.trivia-dot-current, .trivia-dot-wrong.trivia-dot-current{ box-shadow: 0 0 0 2px var(--mode-accent); }
  .trivia-question-block{ animation: triviaSlideIn 0.28s ease-out both; }
  @keyframes triviaSlideIn{
    from{ opacity:0; transform:translateX(18px); }
    to{ opacity:1; transform:none; }
  }
  .trivia-difficulty{ color:var(--parchment-dim); }
  .trivia-difficulty-hard{ color:var(--blaze-bright); }
  .trivia-next{ margin-top:4px; }
  .trivia-encyclopedia-link{
    align-self:flex-start; margin:0; padding:2px 0;
    border:0; background:transparent; cursor:pointer;
    font-family:var(--font-legible); font-size:12.5px; color:var(--parchment-dim);
    text-decoration:underline; text-decoration-style:dotted; text-underline-offset:3px;
  }
  .trivia-encyclopedia-link:hover{ color:var(--parchment); }
  .trivia-shield-note, .trivia-bonus-done, .trivia-countdown{
    font-family:var(--font-legible); font-size:12.5px; line-height:1.45;
    color:var(--parchment-dim); text-align:center;
  }
  .trivia-shield-note{ color:var(--parchment); }
  .trivia-achievements{ display:flex; flex-direction:column; gap:6px; }
  .trivia-share-row{ display:flex; flex-direction:column; gap:6px; }
  .trivia-share-row .share-score-text[hidden]{ display:none; }
  .trivia-share-btn{
    display:block; width:100%;
    padding:9px 12px; border:0; border-radius:6px;
    box-shadow: inset 0 0 0 1.5px var(--mode-accent);
    background: color-mix(in srgb, var(--mode-accent) 26%, var(--drawer));
    color:var(--parchment); cursor:pointer;
    font-family:var(--font-display-small); font-size:14px;
    text-transform:uppercase; letter-spacing:0.03em;
    transition:background 0.12s ease;
  }
  .trivia-share-btn:hover{ background: color-mix(in srgb, var(--mode-accent) 40%, var(--drawer)); }
  .trivia-record{
    padding:8px 10px; border-radius:6px;
    background:rgba(0,0,0,0.2);
    font-family:var(--font-stamp); font-size:11.5px; letter-spacing:0.04em; text-transform:uppercase;
    color:var(--parchment-dim); text-align:center;
  }
  .trivia-record b{ color:var(--parchment); font-weight:800; }
  .trivia-review-block{ animation:none; }
  @media (prefers-reduced-motion:reduce){
    .trivia-question-block{ animation:none; }
  }

  /* Field ID: reuses most of Daily Intel's visual language directly
     (.trivia-progress/.trivia-dot, .trivia-summary, .trivia-record,
     .trivia-share-btn, .trivia-keep-going, .trivia-countdown,
     .daily-streak-badge, .home-play-btn) - only what's genuinely
     different about a clue-list-plus-timer-plus-text-box format gets its
     own rule below. Its own --mode-accent (a field-notebook slate-blue,
     distinct from every other mode's accent) is what tints all of those
     shared classes for this mode without touching their definitions. Set
     on .fieldid-panel - the wrapper the fullscreen view actually renders -
     rather than a home-tile class, since Field ID's home entry is a
     Today-row chip with no tile of its own for the fullscreen markup to
     inherit the variable from. */
  .fieldid-panel{
    --mode-accent:#7c3aed;
    --fieldid-purple:#7c3aed;
    --fieldid-purple-bright:#a78bfa;
    --fieldid-display:"Bungee Inline", "Bungee", var(--font-display);
    --fieldid-body:"Nunito", var(--font-legible);
  }
  .fieldid-panel .trivia-question-num,
  .fieldid-panel .fieldid-session-label,
  .fieldid-panel .fieldid-hints-heading > span,
  .fieldid-panel .fieldid-mc-kicker,
  .fieldid-panel .fieldid-mc-head strong,
  .fieldid-panel .fieldid-reveal-name{ font-family:var(--fieldid-display); font-weight:400; }
  .fieldid-panel .fieldid-hint-value,
  .fieldid-panel .fieldid-hint-reveal,
  .fieldid-panel .fieldid-guess-input,
  .fieldid-panel .fieldid-mc-option,
  .fieldid-panel .fieldid-reveal-verdict{ font-family:var(--fieldid-body); }
  .fieldid-session-label{
    align-self:center; padding:4px 10px; border-radius:999px;
    color:#5b21b6; background:rgba(124,58,237,0.12);
    font-size:10px; letter-spacing:0.06em; text-transform:uppercase;
  }
  .fieldid-question-block{
    display:flex; flex-direction:column; gap:10px;
    animation: triviaSlideIn 0.28s ease-out both;
  }
  .fieldid-question-block + .fieldid-question-block{
    padding-top:14px; border-top:1px solid rgba(255,255,255,0.08);
  }
  .fieldid-hints-card{
    padding:14px; border-radius:14px;
    background:linear-gradient(145deg, rgba(124,58,237,0.13), rgba(255,255,255,0.035));
    box-shadow:inset 0 0 0 1px rgba(124,58,237,0.32), 0 10px 24px rgba(33,20,55,0.12);
  }
  .fieldid-hints-heading{
    display:flex; align-items:baseline; justify-content:space-between; gap:10px;
    margin-bottom:10px; color:var(--parchment);
  }
  .fieldid-hints-heading > span{ font-size:13px; letter-spacing:0.04em; text-transform:uppercase; }
  .fieldid-hints-heading small{ font-family:var(--fieldid-body); font-size:11px; color:var(--parchment-dim); }
  .fieldid-hint-list{ display:flex; flex-direction:column; gap:8px; }
  .fieldid-hint-row{
    display:grid; grid-template-columns:28px minmax(0,1fr) auto; align-items:center; gap:10px;
    min-height:52px; padding:9px 10px; border-radius:10px;
    background:#241f14; box-shadow:inset 0 0 0 1px rgba(167,139,250,0.18);
  }
  .fieldid-hint-row.is-revealed{ animation:fieldIdHintReveal 260ms ease-out both; }
  .fieldid-hint-row.is-locked{ opacity:0.78; }
  .fieldid-hint-icon{
    width:28px; height:28px; display:flex; align-items:center; justify-content:center;
    border-radius:8px; background:rgba(124,58,237,0.2); color:var(--fieldid-purple-bright); font-size:14px;
  }
  .fieldid-hint-copy{ display:flex; min-width:0; flex-direction:column; gap:2px; }
  .fieldid-hint-label{
    font-family:var(--fieldid-display); font-size:9px; letter-spacing:0.05em;
    text-transform:uppercase; color:var(--fieldid-purple-bright);
  }
  .fieldid-hint-value{ font-size:13.5px; line-height:1.4; color:var(--parchment); }
  .fieldid-hint-state{ font-family:var(--fieldid-body); font-size:10px; color:rgba(233,226,200,0.75); }   /* contrast pass: was 0.55 (4.46:1) → 6.9:1 on drawer */
  .fieldid-hint-reveal{
    appearance:none; border:1px solid var(--fieldid-purple-bright); border-radius:8px;
    background:rgba(124,58,237,0.16); color:#ddd0ff; padding:7px 9px;
    font-size:11px; font-weight:800; cursor:pointer; white-space:nowrap;
  }
  .fieldid-hint-reveal:hover{ background:rgba(124,58,237,0.3); }
  @keyframes fieldIdHintReveal{
    from{ opacity:0.25; transform:translateY(4px); }
    to{ opacity:1; transform:translateY(0); }
  }
  .fieldid-clue-list{
    margin:0; padding:0; list-style:none;
    display:flex; flex-direction:column; gap:6px;
  }
  .fieldid-clue-list li{
    padding:9px 11px; background:rgba(0,0,0,0.2); border-radius:6px;
    border-left:2px solid var(--mode-accent);
    font-family:var(--fieldid-body); font-size:14px; line-height:1.45; color:var(--parchment);
  }
  .fieldid-clue-list-review li{ opacity:0.85; }
  .fieldid-timer{ display:flex; align-items:center; gap:8px; }
  .fieldid-timer-track{
    flex:1 1 auto; height:6px; border-radius:999px;
    background:rgba(255,255,255,0.1);
    box-shadow: inset 0 0 0 1px rgba(255,255,255,0.08);
    overflow:hidden;
  }
  .fieldid-timer-bar{
    height:100%; border-radius:999px;
    background:var(--mode-accent);
    /* No CSS transition on width - the ticking interval patches this
       every 250ms itself (see wireFieldIdCard), which already reads as
       smooth motion at that rate; a transition on top would just add lag
       between what the interval sets and what paints. */
  }
  .fieldid-timer-text{
    flex:0 0 auto; min-width:28px; text-align:right;
    font-family:var(--font-stamp); font-weight:800; font-size:12px;
    font-variant-numeric:tabular-nums;
    color:var(--parchment-dim);
  }
  .fieldid-mc{
    padding:14px; border-radius:14px;
    background:linear-gradient(145deg, rgba(124,58,237,0.22), rgba(72,35,120,0.12));
    box-shadow:inset 0 0 0 1.5px var(--fieldid-purple), 0 12px 28px rgba(57,25,95,0.18);
    animation:fieldIdHintReveal 220ms ease-out both;
  }
  .fieldid-mc[hidden]{ display:none; }
  .fieldid-mc-head{ display:flex; justify-content:space-between; gap:12px; align-items:center; margin-bottom:10px; }
  .fieldid-mc-head > div{ display:flex; flex-direction:column; gap:2px; }
  .fieldid-mc-kicker{ color:var(--fieldid-purple-bright); font-size:9px; letter-spacing:0.08em; text-transform:uppercase; }
  .fieldid-mc-head strong{ color:var(--parchment); font-size:12px; letter-spacing:0.02em; }
  .fieldid-mc-countdown{
    min-width:40px; padding:5px 8px; border-radius:999px; text-align:center;
    background:var(--fieldid-purple); color:#fff; font-family:var(--fieldid-display); font-size:11px;
    font-variant-numeric:tabular-nums;
  }
  .fieldid-mc-options{ display:grid; grid-template-columns:repeat(2,minmax(0,1fr)); gap:8px; }
  .fieldid-mc-option{
    appearance:none; min-height:44px; padding:9px 10px; border:1px solid rgba(167,139,250,0.5);
    border-radius:9px; background:rgba(20,14,28,0.72); color:var(--parchment);
    font-size:13px; font-weight:800; cursor:pointer;
  }
  .fieldid-mc-option:hover{ border-color:var(--fieldid-purple-bright); background:rgba(124,58,237,0.3); }
  .fieldid-new-questions{ width:100%; margin-top:4px; background:var(--fieldid-purple); border-color:var(--fieldid-purple); }
  /* Stacked, not side-by-side: the guess box is the single most important
     control on this screen, so it gets its own full-width row well above
     thumb/pointer-precision territory, with Submit as its own full-width
     row underneath rather than squeezed into the same line. A cramped
     input next to a button is an easy target to miss-click on a
     mouse/trackpad, landing focus nowhere and reading as "typing doesn't
     work" even though nothing is actually broken. */
  .fieldid-guess-row{ display:flex; flex-direction:column; gap:10px; }
  .fieldid-guess-input{
    width:100%; min-width:0; box-sizing:border-box;
    padding:16px 16px; border-radius:10px;
    background:rgba(0,0,0,0.3);
    box-shadow: inset 0 0 0 1.5px rgba(255,255,255,0.2);
    color:var(--parchment);
    font-family:var(--font-legible); font-size:18px;
    min-height:54px;
  }
  .fieldid-guess-input::placeholder{ color:var(--parchment-dim); }
  .fieldid-guess-input:focus{
    outline:none;
    background:rgba(0,0,0,0.4);
    box-shadow: inset 0 0 0 2.5px var(--mode-accent), 0 0 0 4px color-mix(in srgb, var(--mode-accent) 30%, transparent);
  }
  .fieldid-submit{ width:100%; padding:14px; font-size:15px; }
  /* Attempt-counter pill and the running list of wrong guesses so the
     player can see what they've already tried. The counter tints toward
     amber then red as attempts run out. */
  .fieldid-attempts{
    align-self:flex-start;
    padding:4px 10px; border-radius:999px;
    font-family:var(--font-legible); font-size:12px; letter-spacing:0.05em;
    background:rgba(255,255,255,0.08); color:var(--parchment-dim);
    box-shadow: inset 0 0 0 1px rgba(255,255,255,0.14);
  }
  .fieldid-attempts[data-attempts-left="2"]{ color:#f0c26b; box-shadow: inset 0 0 0 1px rgba(240,194,107,0.35); }
  .fieldid-attempts[data-attempts-left="1"]{ color:#ef8b6a; box-shadow: inset 0 0 0 1px rgba(239,139,106,0.45); }
  .fieldid-wrong-list{
    font-family:var(--font-legible); font-size:13px; line-height:1.4;
    color:var(--parchment-dim);
  }
  .fieldid-reveal{
    display:flex; align-items:center; gap:12px;
    padding:10px 12px; border-radius:8px;
    background:rgba(0,0,0,0.2);
    animation: triviaPerfectBurst 0.4s cubic-bezier(0.2,0.85,0.3,1.4) both;
  }
  .fieldid-reveal-correct{ box-shadow: inset 0 0 0 1.5px #7bd88f; }
  .fieldid-reveal-wrong{ box-shadow: inset 0 0 0 1.5px #ff8a76; }
  .fieldid-reveal-glyph{ font-size:44px; line-height:1; flex:0 0 auto; }
  .fieldid-reveal-glyph .animal-art{ width:44px; height:44px; object-fit:contain; }
  .fieldid-reveal-text{ display:flex; flex-direction:column; gap:2px; min-width:0; }
  .fieldid-reveal-name{
    font-family:var(--font-stamp); font-weight:800; font-size:15px;
    color:var(--parchment);
  }
  .fieldid-reveal-verdict{
    font-family:var(--font-legible); font-size:12.5px;
    color:var(--parchment-dim);
  }
  .fieldid-reveal-correct .fieldid-reveal-verdict{ color:#9ee8ac; }
  .fieldid-reveal-wrong .fieldid-reveal-verdict{ color:#ffb3a3; }
  @media (prefers-reduced-motion:reduce){
    .fieldid-question-block{ animation:none; }
    .fieldid-reveal, .fieldid-hint-row.is-revealed, .fieldid-mc{ animation:none; }
  }

  /* Field ID's own field-notebook khaki background - .card.fieldid-theme
     is toggled in render() whenever fieldIdOpen is true (see the fieldIdOpen
     branch in 05-screens-fight-and-boot.js). Deliberately NOT done by
     redefining --drawer/--parchment for the whole subtree: the clue list,
     guess box, reveal boxes and the results pills (.trivia-summary,
     .trivia-share-btn, .trivia-record, .daily-streak-badge) all sit inside
     their own dark boxes and already read fine with light text on a dark
     background regardless of what the page around them looks like -
     recoloring --drawer would lighten those boxes along with the card and
     break exactly the contrast they rely on. Only the handful of elements
     whose text sits directly on the bare card background (the header, the
     progress dots, the timer track/label, the question counter, the review
     toggle, the countdown/shield lines) need their own explicit dark-ink
     override here. */
  .card.fieldid-theme{
    background:
      linear-gradient(180deg, rgba(255,255,255,0.14), transparent 40%),
      #c4b88d;
    border-color:#8f7f56;
    color:#2e2717;
  }
  .card.fieldid-theme .trivia-fullscreen-header{
    border-bottom-color: rgba(46,39,23,0.18);
  }
  .card.fieldid-theme .trivia-fullscreen-back{
    border-color: rgba(46,39,23,0.35);
    background: rgba(46,39,23,0.07);
    color:#2e2717;
  }
  .card.fieldid-theme .trivia-fullscreen-back:hover{
    background: rgba(46,39,23,0.14);
    border-color: rgba(46,39,23,0.5);
  }
  .card.fieldid-theme .trivia-fullscreen-title{ color:#2e2717; }
  .card.fieldid-theme .fieldid-reset-btn{
    border-color: rgba(46,39,23,0.25); color: rgba(46,39,23,0.6);
  }
  .card.fieldid-theme .fieldid-reset-btn:hover{
    color:#2e2717; border-color: rgba(46,39,23,0.45);
  }
  .card.fieldid-theme .fieldid-session-label{ color:#4c1d95; background:rgba(124,58,237,0.14); }
  .card.fieldid-theme .fieldid-hints-card{ background:rgba(255,255,255,0.2); }
  .card.fieldid-theme .fieldid-hints-heading > span{ color:#2e2717; }
  .card.fieldid-theme .fieldid-hints-heading small{ color:rgba(46,39,23,0.68); }
  .card.fieldid-theme .trivia-question-num{ color: rgba(46,39,23,0.65); }
  .card.fieldid-theme .fieldid-question-block + .fieldid-question-block{
    border-top-color: rgba(46,39,23,0.14);
  }
  .card.fieldid-theme .trivia-dot{
    background: rgba(46,39,23,0.18);
    box-shadow: inset 0 0 0 1px rgba(46,39,23,0.22);
  }
  .card.fieldid-theme .fieldid-timer-track{
    background: rgba(46,39,23,0.14);
    box-shadow: inset 0 0 0 1px rgba(46,39,23,0.18);
  }
  .card.fieldid-theme .fieldid-timer-text{ color: rgba(46,39,23,0.65); }
  .card.fieldid-theme .trivia-keep-going{ color: rgba(46,39,23,0.65); }
  .card.fieldid-theme .trivia-keep-going:hover{ color:#2e2717; }
  .card.fieldid-theme .trivia-countdown,
  .card.fieldid-theme .trivia-bonus-done{ color: rgba(46,39,23,0.65); }
  .card.fieldid-theme .trivia-shield-note{ color:#2e2717; }
  /* .daily-streak-badge's background is a low-opacity orange wash rather
     than a solid fill, so it tints toward whatever sits behind it - over
     khaki that composites much lighter than over the standard dark drawer,
     and the shared --blaze-bright text color loses most of its contrast
     against that lighter result. Darkened only in this scope; the shared
     rule (used by every other mode's streak badge) is untouched. */
  .card.fieldid-theme .daily-streak-badge{ color:#8a410c; }
  /* The clue rows, guess box, reveal boxes and lifetime-record strip are
     all rgba(0,0,0,X) overlays, not solid fills - that only reads as a
     dark "ink well" box because it used to sit on the standard dark card.
     On khaki the same alpha-black just muddies down to a washed-out tan,
     which is worse for the guess box's legibility than the plain dark
     theme was ("can't see what you're typing"), not better. Solid dark
     fills here so these stay real dark boxes regardless of what the page
     around them looks like. */
  .card.fieldid-theme .fieldid-clue-list li,
  .card.fieldid-theme .fieldid-reveal,
  .card.fieldid-theme .trivia-record{
    background:#241f14;
  }
  .card.fieldid-theme .fieldid-guess-input{
    background:#1c1810;
    box-shadow: inset 0 0 0 1.5px rgba(46,39,23,0.4);
  }
  .card.fieldid-theme .fieldid-guess-input:focus{
    background:#14110b;
  }

  /* Only rendered once there's a real streak (2+ consecutive daily wins) to
     protect - see currentDailyStreak(). Sits between the blurb and the
     seed-locked preview so it reads as this run's stakes, not decoration. */
  .daily-streak-badge{
    display:inline-flex; align-items:center; gap:5px;
    margin:6px 0 0; padding:4px 9px;
    font-family:var(--font-stamp); font-weight:800; font-size:11.5px;
    color:var(--blaze-bright);
    background:rgba(211,86,15,0.16);
    border-radius:99px;
  }

  /* Today's Daily fixture, rendered on the card before the player commits.
     Monospace and tabular so the four values line up as a ledger - it should
     look like a posted result, which is the claim being made about it. */
  .daily-preview{
    display:block; width:100%;
    margin:4px 0 0; padding:7px 8px;
    border:1px solid var(--mode-accent);
    border-radius:8px;
    background:rgba(62,99,38,0.08);
  }
  /* ink-text, not pine-dark: this is the line the whole mode rests on, and
     pine-dark (a mid-dark green) is nearly invisible against this dark
     preview box - 1.6:1. ink-text is light and clears 9.5:1. */
  .daily-preview-seal{
    display:block; margin-bottom:5px;
    font-family:var(--font-stamp); font-weight:800; font-size:10px;
    text-transform:uppercase; letter-spacing:0.07em;
    color:var(--parchment);
  }
  .daily-preview-row{
    display:flex; justify-content:space-between; gap:10px;
    font-family:var(--font-stamp); font-size:11px; line-height:1.5;
  }
  .daily-preview-key{
    text-transform:uppercase; letter-spacing:0.05em;
    color:rgba(243, 236, 224,0.78);
  }
  .daily-preview-val{
    font-weight:700; text-align:right; color:var(--parchment);
  }

  .opponent-name{
    font-family:var(--font-stamp); font-weight:800; font-size:12px;
    text-transform:uppercase; letter-spacing:0.03em; color:var(--parchment);
  }
  .opponent-desc{ font-size:11px; color:var(--parchment-dim); line-height:1.35; }
  .opponent-spin-reveal{
    text-align:center; padding:14px 10px; border-radius:8px;
    border:1.5px solid var(--parchment-dim); margin-bottom:8px;
  }
  .opponent-spin-name{
    font-family:var(--font-stamp); font-weight:800; font-size:15px;
    text-transform:uppercase; letter-spacing:0.03em; color:var(--parchment);
  }
  .opponent-spin-note{ font-size:11.5px; color:var(--parchment-dim); margin-top:4px; }

  @media (prefers-reduced-motion: reduce){
    .card{ animation:none; }
    .start-btn, .primary-btn{ transition:none; }
    .bg-sky{ transition:none; }
  }

  /* Below the candidates now, so the top margin is what separates "what you are
     choosing" from "what you have chosen". */
  .fight-teams{ display:flex; flex-direction:column; gap:14px; margin:18px 0 4px; position:relative; }
  .fight-team-name{
    font-family:var(--font-stamp);
    font-weight:800;
    text-transform:uppercase;
    font-size:11.5px;
    letter-spacing:0.05em;
    text-align:center;
    margin-bottom:8px;
  }
  /* -light variants, not -dark: these are text color on the (now dark)
     --drawer, not a stripe on light manila - see the --blaze-bright/
     --moss-bright note in :root. */
  .fight-team.ai .fight-team-name{ color:var(--blaze-bright); }
  .fight-team.player .fight-team-name{ color:var(--moss-bright); }
  .fight-slots{ display:flex; flex-direction:row; flex-wrap:wrap; gap:6px; align-items:center; }
  /* Empty slot: outline only, no fill - .fight-slot.filled below adds a
     solid border plus a tinted background. That empty-vs-filled contrast is
     what makes the roster row scannable as "progress so far" at a glance;
     it used to be carried by dashed-vs-solid, Flat Modern carries it by
     outline-vs-filled instead. */
  /* Roster row slot: --chip-size:6px because these are the smallest
     tiles in the game (half-width on a 320px phone = ~150px wide, and
     empty variants shrink to 26px minimum). A larger chip would cut the
     tile's own number/emoji. Replaces the old 8px border-radius. */
  .fight-slot{
    flex:1 1 calc(50% - 6px);
    min-width:0;
    --chip-size:6px;
    clip-path: polygon(
      0 0,
      calc(100% - var(--chip-size)) 0,
      100% var(--chip-size),
      100% 100%,
      var(--chip-size) 100%,
      0 calc(100% - var(--chip-size))
    );
    /* Inset shadow instead of border - clip-path shears real borders on
       the diagonal chip corners. States below (filled/bonus) drive the
       outline color via --slot-edge instead of border-color. */
    --slot-edge: rgba(243,236,224,0.5);            /* contrast pass: was 0.22 (1.90:1) → 4.6:1 border on cab */
    border:0;
    box-shadow: inset 0 0 0 1.5px var(--slot-edge);
    padding:7px 8px;
    text-align:center;
    font-size:12.5px;
    color:var(--parchment-dim);
    min-height:18px;
    transition: box-shadow 0.12s ease, background 0.12s ease;
  }
  /* An empty slot shrinks to its number so a five- or ten-slot roster reads as
     one quiet row of what is still to come, instead of ten half-width boxes
     each announcing that it is empty. Because drafted slots fill in order, the
     row is always filled boxes followed by a tail of these - which is a
     progress bar you can count, at no extra markup. */
  .fight-slot.empty{
    flex:0 0 auto;
    min-width:26px;
    padding:4px 7px;
    font-size:11px;
    line-height:1.3;
    min-height:0;
    /* var(--parchment-dim) (0.66 alpha) now clears 5.4:1 against --drawer with
       room to spare - the old 0.72 override existed only to clear AA on the
       previous light-manila surface and isn't needed any more. */
    color:var(--parchment-dim);
    font-variant-numeric:tabular-nums;
  }
  .fight-slot.empty .bonus-slot-tag{ margin-left:2px; font-size:9px; padding:1px 4px; }
  .fight-slot.filled{
    --slot-edge: rgba(243,236,224,0.65);           /* contrast pass: was 0.4 → stronger "selected" outline vs the empty state */
    background:rgba(243, 236, 224,0.1);
    color:var(--parchment);
    font-weight:600;
  }
  .fight-slot .emoji{ font-size:16px; margin-right:5px; }
  .fight-slot.bonus-slot{ --slot-edge: var(--brass-bright); }
  .fight-slot.filled.bonus-slot{ background:rgba(217,150,46,0.12); }
  .bonus-slot-tag{
    display:inline-block; font-family:var(--font-stamp); font-size:10px; font-weight:800;
    /* ink-text, not stamp-dark: this amber tint over --drawer measures a dark
       background either way (7.8:1 for ink-text vs. 1.0:1 for stamp-dark -
       even --blaze-bright only reaches 5.0:1 here, and ink-text's own 7.8:1
       is still the better read against an amber tint). */
    color:var(--parchment); background:rgba(217,150,46,0.22);
    padding:1px 6px; border-radius:8px; letter-spacing:0.02em; vertical-align:middle;
  }
  /* Inline ★ glyph carried by the bonus-slot chip. Slightly brighter than
     the surrounding text so the star pops without shouting -
     the same symbol appears on the roster slot pill and on the mid-fight
     matchup card, so a player can trace both back to one mechanic
     (Gauntlet's first-pick / last-two-picks stamina bonus). */
  .bonus-slot-tag-star{
    color:var(--brass-bright, #f4c363); margin-right:2px; font-weight:800;
  }

  /* ---- Muster Line: the roster row as an assembling army --------------- */

  /* An empty slot you still own reads as a place waiting to be filled, not
     just a blank number - a faint dashed inset distinguishes "not yet yours"
     from the solid outline .fight-slot.filled uses once it is. */
  .roster-slot-vacant{
    box-shadow: inset 0 0 0 1.5px rgba(243,236,224,0.16);
    border-style:dashed;
  }
  @keyframes rosterSlotSettle{
    0%{ transform:scale(1.16); box-shadow: inset 0 0 0 2px var(--brass-bright, #f4c363), 0 0 0 6px rgba(244,195,99,0.18); }
    60%{ transform:scale(1.04); }
    100%{ transform:scale(1); box-shadow: inset 0 0 0 1.5px var(--slot-edge); }
  }
  .roster-slot-just-drafted{
    animation: rosterSlotSettle 0.5s cubic-bezier(0.2,0.9,0.3,1.2) both;
    position:relative;
  }
  @keyframes rosterSlotRipple{
    0%{ transform:translateY(0); }
    35%{ transform:translateY(-2px); }
    100%{ transform:translateY(0); }
  }
  .roster-slot-ripple{ animation: rosterSlotRipple 0.32s ease-out 0.14s both; }
  @keyframes rosterSlotStampIn{
    0%{ opacity:0; transform:scale(1.6) rotate(-10deg); }
    55%{ opacity:1; transform:scale(0.92) rotate(-4deg); }
    100%{ opacity:1; transform:scale(1) rotate(-4deg); }
  }
  .roster-slot-stamp{
    display:inline-block; margin-left:4px; font-family:var(--font-stamp);
    font-size:8.5px; font-weight:800; letter-spacing:0.05em;
    color:var(--brass-bright, #f4c363); border:1px solid var(--brass-bright, #f4c363);
    border-radius:3px; padding:0 3px; vertical-align:middle;
    animation: rosterSlotStampIn 0.4s cubic-bezier(0.2,0.9,0.3,1.2) 0.1s both;
  }
  @keyframes armyAssembledFlash{
    0%{ opacity:0; transform:translateY(-4px) scale(0.97); }
    12%{ opacity:1; transform:translateY(0) scale(1); }
    88%{ opacity:1; }
    100%{ opacity:0; }
  }
  .army-assembled-banner{
    text-align:center; font-family:var(--font-stamp); font-weight:800;
    font-size:12px; letter-spacing:0.06em; color:var(--ink);
    background:var(--brass-bright, #f4c363); border-radius:8px;
    padding:6px 10px; margin:8px 0; animation: armyAssembledFlash 2.4s ease-in-out both;
  }
  @media (prefers-reduced-motion: reduce){
    .roster-slot-just-drafted, .roster-slot-ripple, .roster-slot-stamp, .army-assembled-banner{
      animation:none !important;
    }
    .roster-slot-stamp{ opacity:1; transform:none; }
    .army-assembled-banner{ opacity:1; transform:none; }
  }

  /* Fight-screen coin-cost chip for Auction. Same size and shape as
     .bonus-slot-tag above so a row that stacks "pop x N | 1.2x | cost"
     reads as one row of chips, not three unrelated pills. The gold
     background matches the .auction-cost-badge on the draft board and
     the .auction-cta home accent - one currency color across every
     surface. Different enough from .bonus-slot-tag's amber tint to
     read as its own thing when both are on the same line. */
  .auction-cost-tag{
    display:inline-block; font-family:var(--font-stamp); font-size:10px; font-weight:800;
    color:var(--parchment); background:#7a5a12;
    padding:1px 6px; border-radius:8px; letter-spacing:0.02em; vertical-align:middle;
    border:1px solid rgba(0, 0, 0, 0.28);
  }

  /* Custom animal sprites (see ANIMAL_ART in game.js). Deliberately sized in em
   * and nothing else: every one of these sits exactly where a text emoji used
   * to, in containers that already set a font-size for that emoji (16px in a
   * draft slot, 30px in the roster sheet, 60px in the battle reveal), so one
   * rule tracks all of them and a new call site needs no new CSS.
   *
   * 1.15em rather than 1em because a text emoji glyph is drawn taller than the
   * em box it is measured in; at a flat 1em these read noticeably smaller than
   * the emoji they replaced, next to the ones they did not. The negative
   * vertical-align sits them on the text baseline for the inline cases. */
  .animal-art{
    display:inline-block; width:1.15em; height:1.15em;
    vertical-align:-0.2em; object-fit:contain;
    -webkit-user-select:none; user-select:none;
  }
  /* In the reveal and the spin cards the sprite is the only thing in its box,
   * so it centers as a block and drops the baseline offset it does not need. */
  .fight-reveal .animal-art,
  .double-spin-card .emoji .animal-art{ vertical-align:middle; }

  .fight-spin-area{ text-align:center; }
  /* 84px, up from 60px: already the single largest sprite in the app before
     this change, and it sits alone in open vertical space with nothing else
     in the row to crowd - the safest possible place to go bigger. */
  .fight-reveal{ position:relative; font-size:84px; line-height:1; margin:6px 0; animation:fighterPop 0.4s ease; }
  @keyframes fighterPop{
    from{ opacity:0; transform:scale(0.6); }
    to{ opacity:1; transform:scale(1); }
  }
  /* --------------------------------------------------------------------
     Battle choreography (Charge-and-Collide, Camp Shake, the Ordnance
     Stamp, momentum, last stand). Every animation below is keyed off a
     class renderGauntlet toggles per beat (gauntlet-beat-clash/-impact on
     .fight-teams) or per-render conditional markup - never off a CSS
     transition watching for a DOM node to persist across renders, because
     it never does: every render() rewrites the whole screen from scratch,
     so only animation (which plays in full on a freshly-mounted node) can
     drive this, not transition (which needs a node to survive a style
     change). The one exception - the stamina bar's own drain - fakes a
     transition on purpose; see staminaBarHtml's comment for how.
     -------------------------------------------------------------------- */
  /* The lunge: both cards coil in, then close the gap toward the VS circle
     between them. .fight-teams stacks vertically (AI on top, player on the
     bottom), so "toward each other" is a translateY convergence, not a
     horizontal one. */
  .gauntlet-beat-clash .fight-team.ai .fight-reveal{
    animation: fighterPop 0.4s ease, gauntletChargeDown 0.62s cubic-bezier(0.5,0,0.7,1) 0.05s both;
  }
  .gauntlet-beat-clash .fight-team.player .fight-reveal{
    animation: fighterPop 0.4s ease, gauntletChargeUp 0.62s cubic-bezier(0.5,0,0.7,1) 0.05s both;
  }
  @keyframes gauntletChargeDown{
    0%{ transform:translateY(0) rotate(0deg); }
    35%{ transform:translateY(-4px) rotate(-2deg); }
    100%{ transform:translateY(10px) rotate(2deg); }
  }
  @keyframes gauntletChargeUp{
    0%{ transform:translateY(0) rotate(0deg); }
    35%{ transform:translateY(4px) rotate(2deg); }
    100%{ transform:translateY(-10px) rotate(-2deg); }
  }
  /* Impact: the fighter that just lost recoils hard away from center, the
     winner snaps back to rest. Both play from wherever the lunge left off,
     so there is no pop back to a neutral position before impact registers.

     The flash and shake used to be one shared "something collided" cue,
     identical whether the player won or lost - a stark white flash plus a
     multi-direction rattle every single bout. That read as violence done
     TO you regardless of which side actually went down, which is exactly
     the "feels like your animal died" report even on a round the player
     won. Split by g.lastBoutWinnerIsPlayer (gauntlet-impact-win/-loss,
     set in renderGauntlet) so a win reads as a hit landed - a warm
     golden flash and a single decisive forward jolt - and a loss keeps the
     harsher white flash and the disorienting multi-axis rattle. */
  .gauntlet-impact-win::before,
  .gauntlet-impact-loss::before{
    content:''; position:absolute; inset:0; z-index:2; pointer-events:none;
    border-radius:10px;
  }
  .gauntlet-impact-loss::before{
    background:#fff;
    animation: gauntletFlash 0.32s ease-out both;
  }
  .gauntlet-impact-win::before{
    background:var(--brass-bright, #f4c363);
    animation: gauntletFlashWin 0.32s ease-out both;
  }
  @keyframes gauntletFlash{
    0%{ opacity:0.85; }
    100%{ opacity:0; }
  }
  @keyframes gauntletFlashWin{
    0%{ opacity:0.4; }
    100%{ opacity:0; }
  }
  .gauntlet-beat-impact .fight-team.ai .fight-reveal.defeated{
    animation: gauntletRecoilUp 0.5s cubic-bezier(0.3,0.6,0.4,1) both;
  }
  .gauntlet-beat-impact .fight-team.player .fight-reveal.defeated{
    animation: gauntletRecoilDown 0.5s cubic-bezier(0.3,0.6,0.4,1) both;
  }
  @keyframes gauntletRecoilUp{
    0%{ transform:translateY(10px) rotate(2deg); opacity:1; }
    100%{ transform:translateY(-18px) rotate(-14deg) scale(0.85); opacity:0.4; filter:grayscale(1); }
  }
  @keyframes gauntletRecoilDown{
    0%{ transform:translateY(-10px) rotate(-2deg); opacity:1; }
    100%{ transform:translateY(18px) rotate(14deg) scale(0.85); opacity:0.4; filter:grayscale(1); }
  }
  .gauntlet-beat-impact .fight-team.ai .fight-reveal:not(.defeated){
    animation: gauntletSettleDown 0.4s cubic-bezier(0.3,1.4,0.4,1) both;
  }
  .gauntlet-beat-impact .fight-team.player .fight-reveal:not(.defeated){
    animation: gauntletSettleUp 0.4s cubic-bezier(0.3,1.4,0.4,1) both;
  }
  @keyframes gauntletSettleDown{
    0%{ transform:translateY(10px) rotate(2deg); }
    100%{ transform:translateY(0) rotate(0deg); }
  }
  @keyframes gauntletSettleUp{
    0%{ transform:translateY(-10px) rotate(-2deg); }
    100%{ transform:translateY(0) rotate(0deg); }
  }
  /* Camp Shake, split the same way as the flash above. A loss keeps the
     disorienting multi-axis rattle - it should feel like the ground shook
     under your own fighter. A win gets a single decisive forward-and-back
     jolt instead: one clean hit landed, not a chaotic scramble. Both are
     transform-only (no layout properties), so they stay cheap on a phone. */
  .gauntlet-shake-loss{ animation: gauntletShake 0.4s cubic-bezier(0.3,0.1,0.3,1) both; }
  @keyframes gauntletShake{
    0%{ transform:translate(0,0); }
    15%{ transform:translate(-4px,2px); }
    30%{ transform:translate(4px,-2px); }
    45%{ transform:translate(-3px,1px); }
    60%{ transform:translate(3px,-1px); }
    75%{ transform:translate(-1px,1px); }
    100%{ transform:translate(0,0); }
  }
  .gauntlet-shake-win{ animation: gauntletPunch 0.32s cubic-bezier(0.2,0.7,0.3,1) both; }
  @keyframes gauntletPunch{
    0%{ transform:translate(0,0) scale(1); }
    35%{ transform:translate(0,3px) scale(1.015); }
    100%{ transform:translate(0,0) scale(1); }
  }
  /* Dust: a handful of motes kicked up from the impact point (the VS
     circle), each with its own drift so they don't read as a repeated
     sprite. Pure CSS, no art. */
  .gauntlet-dust{ position:absolute; left:50%; top:50%; z-index:1; pointer-events:none; }
  .gauntlet-dust-mote{
    position:absolute; left:0; top:0; width:4px; height:4px; border-radius:50%;
    background:var(--parchment-dim);
    animation: gauntletDustRise 0.65s ease-out both;
  }
  .gauntlet-dust-mote:nth-child(1){ animation-delay:0.02s; --dx:-18px; --dy:-22px; }
  .gauntlet-dust-mote:nth-child(2){ animation-delay:0.06s; --dx:14px; --dy:-26px; }
  .gauntlet-dust-mote:nth-child(3){ animation-delay:0.00s; --dx:-8px; --dy:-32px; }
  .gauntlet-dust-mote:nth-child(4){ animation-delay:0.09s; --dx:22px; --dy:-14px; }
  .gauntlet-dust-mote:nth-child(5){ animation-delay:0.04s; --dx:-24px; --dy:-10px; }
  .gauntlet-dust-mote:nth-child(6){ animation-delay:0.11s; --dx:6px; --dy:-30px; }
  @keyframes gauntletDustRise{
    0%{ transform:translate(0,0) scale(1); opacity:0.9; }
    100%{ transform:translate(var(--dx,0),var(--dy,-20px)) scale(0.4); opacity:0; }
  }
  /* The Ordnance Stamp: a rotated stencil slamming onto the card - "-N%"
     for the winner's own cost, a full K.O. band for whoever just fell. */
  .gauntlet-stamp{
    position:absolute; top:36%; left:50%; z-index:3;
    font-family:var(--font-stamp); font-weight:800; letter-spacing:0.05em;
    color:var(--blaze-bright); font-size:22px;
    text-shadow:0 1px 0 rgba(0,0,0,0.5);
    padding:2px 10px; border:2px solid var(--blaze-bright); border-radius:4px;
    background:rgba(20,26,16,0.55);
    transform:translate(-50%,-50%) rotate(-14deg) scale(2.4);
    opacity:0;
    animation: gauntletStamp 0.45s cubic-bezier(0.2,0.9,0.3,1.2) 0.08s both;
  }
  .gauntlet-stamp-ko{
    font-size:26px; color:var(--parchment); border-color:var(--parchment);
    background:rgba(143,52,5,0.75);
  }
  @keyframes gauntletStamp{
    0%{ transform:translate(-50%,-50%) rotate(-14deg) scale(2.4); opacity:0; }
    55%{ opacity:1; }
    100%{ transform:translate(-50%,-50%) rotate(-8deg) scale(1); opacity:1; }
  }
  @media (prefers-reduced-motion: reduce){
    .fight-reveal{ animation:none; }
    .gauntlet-beat-clash .fight-team.ai .fight-reveal,
    .gauntlet-beat-clash .fight-team.player .fight-reveal,
    .gauntlet-beat-impact .fight-team.ai .fight-reveal.defeated,
    .gauntlet-beat-impact .fight-team.player .fight-reveal.defeated,
    .gauntlet-beat-impact .fight-team.ai .fight-reveal:not(.defeated),
    .gauntlet-beat-impact .fight-team.player .fight-reveal:not(.defeated){
      animation:none;
    }
    .gauntlet-impact-win::before,
    .gauntlet-impact-loss::before{ display:none; }
    .gauntlet-shake-loss, .gauntlet-shake-win{ animation:none; }
    .gauntlet-dust{ display:none; }
    .gauntlet-stamp{ animation:none; opacity:1; transform:translate(-50%,-50%) rotate(-8deg) scale(1); }
  }
  .fight-reveal-name{
    font-family:var(--font-stamp);
    font-weight:800;
    text-transform:uppercase;
    letter-spacing:0.04em;
    margin-bottom:14px;
  }
  /* The Veteran's Wear: a fighter that has survived multiple bouts in a row
     earns visible mileage - a scratch overlay first, a bandage once it's
     really been through it. Purely cosmetic, driven by survivedBouts. */
  .gauntlet-veteran-scratched{
    background-image:
      linear-gradient(105deg, transparent 42%, rgba(0,0,0,0.35) 44%, transparent 46%),
      linear-gradient(100deg, transparent 58%, rgba(0,0,0,0.28) 60%, transparent 62%);
  }
  .gauntlet-veteran-bandaged::after{
    content:''; position:absolute; right:8%; bottom:6%; width:26%; height:10px;
    background:var(--parchment); opacity:0.85; border-radius:2px;
    transform:rotate(-18deg);
    box-shadow:0 0 0 1px rgba(0,0,0,0.25);
  }
  .gauntlet-veteran-tally{
    display:inline-block; font-family:var(--font-stamp); font-weight:800;
    color:var(--brass-bright); letter-spacing:0.06em; font-size:11px;
    margin-left:3px;
  }
  .stamina-bar{
    position:relative;
    height:8px; border-radius:4px; background:rgba(243, 236, 224,0.28);
    overflow:hidden; margin:2px auto 0; max-width:160px;
  }
  /* The ghost: a static, paler bar left at the OLD width, sitting behind
     the real fill. It never animates itself - the real fill (drawn on top,
     nudged to its new width a frame after paint, see the requestAnimation-
     Frame callback in renderGauntlet) recedes from under it, so the ghost's
     exposed sliver reads as an afterimage of what was just lost. */
  .stamina-ghost{
    position:absolute; top:0; left:0; height:100%;
    background:rgba(217,150,46,0.4);
  }
  .stamina-fill{ position:relative; height:100%; background:#4a7c2a; transition:width 0.4s ease, background-color 0.4s ease; }
  .stamina-fill-animating{ transition:width 0.6s cubic-bezier(0.3,0.7,0.35,1), background-color 0.4s ease; }
  .stamina-fill.ko{ background:#a8291c; }
  .stamina-bar-low .stamina-fill{ animation:staminaLowPulse 0.9s ease-in-out infinite; }
  @keyframes staminaLowPulse{
    0%,100%{ filter:brightness(1); }
    50%{ filter:brightness(1.6); }
  }
  /* The crack: a defeated fighter's bar splits into two shards and drops
     apart instead of just updating to an empty state. */
  .stamina-bar.cracked{ background:transparent; display:flex; gap:2px; }
  .stamina-shard{ flex:1; height:100%; background:#a8291c; opacity:1; }
  .stamina-shard-a{ border-radius:4px 0 0 4px; animation:gauntletShardLeft 0.5s ease-in both; }
  .stamina-shard-b{ border-radius:0 4px 4px 0; animation:gauntletShardRight 0.5s ease-in both; }
  @keyframes gauntletShardLeft{
    0%{ transform:translate(0,0) rotate(0deg); opacity:1; }
    100%{ transform:translate(-3px,6px) rotate(-10deg); opacity:0.4; }
  }
  @keyframes gauntletShardRight{
    0%{ transform:translate(0,0) rotate(0deg); opacity:1; }
    100%{ transform:translate(3px,6px) rotate(10deg); opacity:0.4; }
  }
  @media (prefers-reduced-motion: reduce){
    .stamina-fill, .stamina-fill-animating{ transition:none; }
    .stamina-bar-low .stamina-fill{ animation:none; }
    .stamina-shard-a, .stamina-shard-b{ animation:none; opacity:0.4; }
  }
  /* background is --cabinet (not --parchment) on purpose: this circle pairs with
     --brass-bright text, and needs to stay a dark fill regardless of which way
     --parchment points. Flat Modern also drops the rotation - sits flat like
     everything else now. The Momentum Divider: its resting tilt tracks which
     side is ahead on KOs across the whole gauntlet (--momentum-tilt, set
     inline per render from koDiff), and it whips through an exaggerated
     version of that tilt on every impact - a persistent visual throughline
     for a 10-round Extreme match, not just a static badge. */
  .gauntlet-vs{
    align-self:center; z-index:3;
    width:36px; height:36px; margin:-21px auto;
    border-radius:50%; background:var(--cabinet);
    border:3px solid var(--brass-bright);
    display:flex; align-items:center; justify-content:center;
    font-family:var(--font-stamp); font-weight:800; font-size:12px;
    color:var(--brass-bright); box-shadow:0 2px 6px rgba(0,0,0,0.3);
    transform:rotate(var(--momentum-tilt, 0deg));
  }
  .gauntlet-vs-whip{ animation:gauntletVsWhip 0.5s cubic-bezier(0.3,1.6,0.4,1) both; }
  @keyframes gauntletVsWhip{
    0%{ transform:rotate(0deg) scale(1.25); }
    60%{ transform:rotate(calc(var(--momentum-tilt, 0deg) * 1.6)) scale(1.1); }
    100%{ transform:rotate(var(--momentum-tilt, 0deg)) scale(1); }
  }
  @media (prefers-reduced-motion: reduce){
    .gauntlet-vs-whip{ animation:none; }
  }
  /* Artillery-Sight Lock-On: a brass reticle sweeps between the two cards
     and locks during the pre-clash pause, aiming the player's attention at
     where the collision is about to land. */
  .gauntlet-reticle{
    position:absolute; left:50%; top:50%; z-index:2; pointer-events:none;
    transform:translate(-50%,-50%);
  }
  .gauntlet-reticle-ring{
    position:absolute; left:50%; top:50%; border-radius:50%;
    border:1.5px solid var(--brass-bright);
    width:70px; height:70px; margin:-35px 0 0 -35px;
    animation:gauntletReticleLock 0.7s cubic-bezier(0.2,0.8,0.3,1) both;
  }
  .gauntlet-reticle-ring-2{
    width:44px; height:44px; margin:-22px 0 0 -22px;
    animation-delay:0.05s;
  }
  @keyframes gauntletReticleLock{
    0%{ opacity:0; transform:scale(2.2) rotate(-30deg); }
    70%{ opacity:1; }
    100%{ opacity:0.85; transform:scale(1) rotate(0deg); }
  }
  .gauntlet-reticle-text{
    position:absolute; left:50%; top:100%; transform:translateX(-50%);
    margin-top:6px; white-space:nowrap;
    font-family:var(--font-stamp); font-size:9.5px; font-weight:800;
    letter-spacing:0.1em; color:var(--brass-bright);
    opacity:0; animation:gauntletReticleTextIn 0.4s ease 0.35s both;
  }
  @keyframes gauntletReticleTextIn{
    from{ opacity:0; }
    to{ opacity:0.9; }
  }
  @media (prefers-reduced-motion: reduce){
    .gauntlet-reticle-ring{ animation:none; opacity:0.85; transform:scale(1) rotate(0deg); }
    .gauntlet-reticle-text{ animation:none; opacity:0.9; }
  }
  /* Last Stand: either side down to its final two fighters. The screen
     leans into it - a darker vignette, red-alert stamina bars, and the
     card's own settle animation runs a touch slower everywhere inside it,
     purely via CSS duration overrides (no change to the actual setTimeout
     pacing in 03-bouts-and-progression.js). */
  .gauntlet-last-stand{
    background:radial-gradient(ellipse at center, transparent 45%, rgba(0,0,0,0.35) 100%);
  }
  .gauntlet-last-stand .stamina-bar{ box-shadow:0 0 0 1px rgba(168,41,28,0.5); }
  /* The final stencil: one KO ends the whole match. Slammed across the
     strip before the screen hands off to the results overlay a beat later. */
  .gauntlet-final-stencil{
    position:absolute; left:50%; top:50%; z-index:4;
    transform:translate(-50%,-50%) rotate(-6deg) scale(0.6);
    font-family:var(--font-display-small); font-weight:400;
    text-transform:uppercase; letter-spacing:0.03em;
    font-size:22px; text-align:center; white-space:nowrap;
    padding:10px 18px; border-radius:4px;
    opacity:0;
    animation:gauntletFinalStencil 0.5s cubic-bezier(0.2,0.85,0.3,1.3) 0.12s both;
  }
  .gauntlet-final-win{ background:rgba(78,124,42,0.85); color:var(--parchment); border:2px solid var(--moss-bright); }
  .gauntlet-final-loss{ background:rgba(143,52,5,0.85); color:var(--parchment); border:2px solid var(--blaze-bright); }
  @keyframes gauntletFinalStencil{
    0%{ opacity:0; transform:translate(-50%,-50%) rotate(-6deg) scale(2.6); }
    60%{ opacity:1; }
    100%{ opacity:1; transform:translate(-50%,-50%) rotate(-6deg) scale(1); }
  }
  @media (prefers-reduced-motion: reduce){
    .gauntlet-final-stencil{ animation:none; opacity:1; transform:translate(-50%,-50%) rotate(-6deg) scale(1); }
  }
  .fight-reveal.defeated{ filter:grayscale(1); opacity:0.45; }
  .ko-tag{
    display:inline-block; font-family:var(--font-stamp); font-size:10.5px; font-weight:800;
    color:var(--parchment); background:var(--blaze-dark); letter-spacing:0.04em;
    margin-left:3px; padding:2px 6px; border-radius:9px; vertical-align:middle;
  }
  .gauntlet-log{ margin:10px 0 16px; display:flex; flex-direction:column; gap:5px; }
  /* line-height is explicit because these rows now carry sprites. A 1.15em
   * inline-block image sitting on a -0.2em baseline offset needs about 1.35em
   * of line box; at the default ~1.2 the rows touched and the log read as one
   * smudged block. */
  .gauntlet-log-item{ font-size:12px; line-height:1.6; color:var(--parchment-dim); text-align:center; }
  /* The post-bout breakdown. Boxed off from the battle screen above it
     because it is a different kind of thing - a record of what just happened
     rather than the live state of the fight - and without an edge it read as
     more fight UI. */
  .bout-why{
    margin:0 0 16px; padding:10px 11px;
    background:rgba(255,255,255,0.26);
    border:1px solid var(--drawer-edge);
    border-radius:10px;
  }
  .bout-why-head{
    display:flex; align-items:baseline; justify-content:space-between;
    gap:8px; margin-bottom:8px;
  }
  .bout-why-verdict{ font-size:13px; font-weight:700; color:var(--parchment); flex:1 1 auto; min-width:0; }
  /* The margin is the part players actually argue with, so it gets its own
     weight rather than trailing the winner's name in the same voice.
     -light, not -dark: sits directly on the (now dark) .bout-why panel with
     no chip of its own, so it needs the light sibling - see --blaze-bright in
     :root. */
  .bout-why-margin{ font-family:var(--font-stamp); font-size:11px; font-weight:700; color:var(--blaze-bright); white-space:nowrap; }
  .bout-why-toggle{
    flex:0 0 auto; background:none; border:0; padding:0;
    font:inherit; font-size:11px; font-weight:600;
    color:var(--moss-bright); text-decoration:underline; cursor:pointer;
  }
  .bout-why-rules{ display:flex; flex-direction:column; gap:4px; }
  /* Good/bad is carried on a left edge over a near-neutral chip rather than as
     a tinted fill - deliberately kept light even though the panel around it
     is now dark, so the original dark-accent-on-light-chip pairing
     (pine-dark/stamp-dark, tuned against a light surface) keeps working
     without needing yet another color variant. 0.88 alpha on --parchment over
     --drawer composites to a light neutral chip regardless of how dark the
     surrounding panel is; pine-dark reads 4.8:1 there and stamp-dark 7.6:1. */
  .bout-rule-item{
    display:flex; align-items:baseline; justify-content:space-between; gap:8px;
    font-size:12px; font-weight:600; text-align:left;
    padding:4px 8px; border-radius:0 8px 8px 0;
    background:rgba(243, 236, 224, 0.88); border-left:3px solid currentColor;
  }
  .bout-rule-item.good{ color:var(--moss-dark); }
  .bout-rule-item.bad{ color:var(--blaze-dark); }
  /* A rule both animals got is neither good news nor bad, so it takes a
     muted dark ink rather than borrowing green or red for something that
     changed nothing. Not --parchment (that's the light color for text on a
     dark panel - this chip is deliberately light, so it needs --drawer
     itself as a muted dark instead, 9.3:1 here). currentColor carries it to
     the left edge too. */
  .bout-rule-item.even{ color:rgba(58,44,30,0.74); }
  .bout-rule-name{ flex:1 1 auto; min-width:0; }
  /* No opacity here on purpose - dimming this to 0.85 drops the green variant
     to 4.12:1 at 10.5px. The monospace face and the smaller size already
     separate it from the rule name. */
  .bout-rule-mult{
    flex:0 0 auto; font-family:var(--font-stamp); font-size:10.5px; font-weight:700;
    white-space:nowrap; font-variant-numeric:tabular-nums lining-nums;
  }
  .bout-why-more{ font-size:11px; color:rgba(243, 236, 224,0.74); padding:1px 8px; }
  .bout-why-math{ margin-top:9px; border-top:1px solid var(--drawer-edge); padding-top:8px; }
  .bout-why-table{ width:100%; border-collapse:collapse; }
  /* Tabular figures so the two columns of powers and multipliers line up
     digit for digit - the whole point of the layout is comparing them. */
  .bout-why-table td, .bout-why-table th{
    font-size:11.5px; padding:3px 0; text-align:right;
    font-variant-numeric:tabular-nums lining-nums;
  }
  /* 0.74 rather than the --parchment-dim token: at 10px this is small text and
     needs 4.5:1, which the token's 0.62 alpha misses at 3.88:1. */
  .bout-why-table thead th{ font-size:10px; font-weight:700; color:rgba(243, 236, 224,0.74); letter-spacing:0.04em; text-transform:uppercase; }
  .bout-why-table th[scope="row"]{
    text-align:left; font-weight:500; color:rgba(243, 236, 224,0.74);
    padding-right:8px; font-variant-numeric:normal;
  }
  .bout-why-table td{ font-family:var(--font-stamp); color:var(--parchment); width:25%; }
  .bout-why-none{ color:var(--parchment-dim); }
  .bout-why-total th[scope="row"], .bout-why-total td{
    font-weight:800; color:var(--parchment);
    border-top:1px solid var(--drawer-edge); padding-top:5px;
  }
  /* Compact chain hint on the effective-power cell when Gauntlet's
     first-pick / last-two-picks stamina bonus fired - dimmer weight and
     slightly smaller than the total so the eye still lands on the big
     number first. */
  .bout-why-chain{
    font-weight:600; font-size:11px; color:var(--parchment-dim);
    margin-left:4px; white-space:nowrap;
  }

  /* Extreme Auction first-time explainer. Reuses .welcome-overlay /
     .welcome-sheet base styling wholesale (scrim, sheet chrome, mobile
     bottom-sheet behavior). */
  .extreme-explainer-steps li{ margin-bottom:6px; }
  .extreme-explainer-steps li:last-child{ margin-bottom:0; }

  /* z-index has to clear .top-strip (1100). The strip is fixed at the top of
     the viewport, so at 1000 the How To Play / Leaderboard / account pills
     were painted on top of the result screen and sat directly over the score
     and the "... Complete" kicker - the first two things the screen exists to
     show. Reserving top padding instead was the other option, but the strip is
     one row on desktop and two on phones, so any fixed inset is wrong at one
     of those sizes and the pills would still float over the battle log while
     scrolling. The result screen is a full-screen takeover with its own
     navigation (Fight Again / Back to Menu), so it covers the strip outright.
     Still below the rules (1200), roster sheet and leaderboard (1250),
     welcome (1300) and auth (1400) layers, which are meant to open over it. */
  .fight-overlay{
    position:fixed;
    inset:0;
    z-index:1150;
    background:#0a1a2e;
    color:#f4f0e8;
    display:flex;
    flex-direction:column;
    align-items:center;
    padding:28px 20px;
    overflow-y:auto;
  }
  .fight-overlay[hidden]{ display:none; }
  /* margin:auto (not justify-content:center) vertically centers this when
     it fits the viewport, but collapses to 0 - never negative - once the
     results screen grows taller than the screen, so the top (the score)
     stays scrollable into view instead of being clipped above an
     unreachable scroll position. */
  .fight-overlay-content{ max-width:440px; width:100%; text-align:center; margin:auto 0; }
  .ov-kicker{
    font-family:var(--font-stamp);
    font-size:11px;
    letter-spacing:0.25em;
    text-transform:uppercase;
    color:rgba(244,240,232,0.55);
    margin-bottom:6px;
  }
  .ov-score{
    font-family:var(--font-stamp);
    font-weight:800;
    font-size:64px;
    line-height:1;
    margin:6px 0;
    font-variant-numeric: tabular-nums;
  }
  .ov-score .of1000{ font-size:20px; color:rgba(244,240,232,0.55); }
  .ov-banner{
    font-family:var(--font-stamp);
    font-weight:800;
    text-transform:uppercase;
    letter-spacing:0.06em;
    font-size:20px;
    margin:8px 0 18px;
    opacity:0;
    animation: ovBannerIn 0.35s ease 0.85s forwards;
  }
  .ov-banner.win{ color:#8fd66a; }
  .ov-banner.lose{ color:#ff6b52; }

  .ov-percentile{
    font-size:12.5px;
    color:rgba(244,240,232,0.6);
    margin:-10px 0 16px;
  }
  .ov-combo-best{
    text-align:center; font-size:12.5px;
    color:rgba(244,240,232,0.65);
    margin:-8px 0 16px;
  }
  .ov-combo-best b{ color:rgba(244,240,232,0.9); }
  .ov-combo-best-new{
    font-size:13.5px; font-weight:700;
    color:var(--blaze-bright);
    background:rgba(211,86,15,0.14);
    border-radius:6px; padding:7px 10px;
  }
  .ov-combo-best-new b{ color:var(--blaze-bright); }
  /* "Beat it?" nudge - shown when the player finished within 10% of a
     combo PB. Deliberately warm-amber (brass tint, not the blaze that
     the new-PB combo box uses) so it reads as an invitation rather than
     a celebration. Sits right under the combo-best line the player just
     read ("Personal best for X: Y"), inheriting its width and rhythm. */
  .ov-beat-it{
    margin:0 0 16px;
    padding:12px 14px 14px;
    border-radius:10px;
    background:linear-gradient(180deg, rgba(200,152,63,0.18), rgba(200,152,63,0.08));
    border:1px solid rgba(200,152,63,0.35);
    text-align:center;
  }
  .ov-beat-it-headline{
    font-size:14px; font-weight:700;
    color:#f0c67a;
    letter-spacing:0.02em;
    margin-bottom:4px;
  }
  .ov-beat-it-body{
    margin:0 0 10px;
    font-size:12.5px; line-height:1.45;
    color:rgba(244,240,232,0.85);
  }
  .ov-beat-it-body b{ color:#f4f0e8; }
  .ov-beat-it-btn{
    /* Slightly narrower than the full-width primary CTA below - the retry
       is offered as a shortcut, not as competition for the main flow. */
    width:auto; min-width:220px; padding:9px 18px;
    font-size:13.5px;
  }
  /* 7-day-best pill on the Personal Bests panel - small dim value next to
     the highlighted all-time number, so "here's where you've been lately"
     reads as context for the number a player is trying to beat. */
  .fight-stat-recent{
    display:inline-block;
    margin-left:6px;
    padding:1px 6px;
    border-radius:6px;
    background:rgba(244,240,232,0.08);
    font-size:10.5px; font-weight:600;
    color:rgba(244,240,232,0.6);
    letter-spacing:0.02em;
    vertical-align:middle;
  }
  @keyframes ovBannerIn{
    from{ opacity:0; transform:translateY(4px) scale(0.96); }
    to{ opacity:1; transform:translateY(0) scale(1); }
  }
  @media (prefers-reduced-motion: reduce){
    .ov-banner{ animation:none; opacity:1; }
  }
  /* One above .fight-overlay so a win still throws confetti over the result
     screen rather than behind it. */
  .confetti-canvas{
    position:fixed;
    inset:0;
    z-index:1151;
    pointer-events:none;
  }
  .ov-explain{
    text-align:left;
    font-size:14.5px;
    line-height:1.6;
    color:rgba(244,240,232,0.85);
    background:rgba(255,255,255,0.06);
    border-radius:8px;
    padding:14px 16px;
    margin-bottom:20px;
  }
  .ov-explain p{ margin:0 0 10px; }
  .ov-explain p:last-child{ margin-bottom:0; }
  .ov-battle-log{ max-height:280px; overflow-y:auto; }
  /* On phones the battle log's internal scroll (280px max-height) reads
     as a rubber-banding trap inside the overlay's own overflow-y - two
     nested scroll surfaces on iOS Safari where one is enough. Flatten
     the log into document flow so the overlay itself scrolls the entire
     result screen, which pairs cleanly with the sticky Fight Again CTA
     added below. */
  @media (max-width:720px){
    .ov-battle-log{ max-height:none; overflow-y:visible; }
  }
  .ov-log-item{ margin-bottom:12px; }
  .ov-log-item:last-child{ margin-bottom:0; }
  .ov-log-item p{ margin:0 0 4px; line-height:1.6; }
  .ov-log-rules{ display:flex; flex-wrap:wrap; gap:4px; }
  .ov-log-rule{
    font-size:10.5px; font-weight:600;
    padding:2px 7px; border-radius:8px;
  }
  .ov-log-rule.good{ color:#a8d98a; background:rgba(92,143,58,0.22); }
  .ov-log-rule.bad{ color:#ff9b85; background:rgba(196,69,31,0.2); }
  /* Both animals triggered it, so it decided nothing - deliberately the
     quietest of the three, in the sheet's own muted text colour. */
  .ov-log-rule.even{ color:rgba(244,240,232,0.55); background:rgba(244,240,232,0.08); }
  .ov-daily-seed{
    font-size:11.5px;
    color:rgba(244,240,232,0.5);
    text-align:center;
    margin:-10px 0 16px;
    font-variant-numeric:tabular-nums;
  }
  .ov-first-recap{
    text-align:left;
    background:rgba(217,150,46,0.12);
    border:1px solid rgba(217,150,46,0.35);
    border-radius:8px;
    padding:14px 16px;
    margin-bottom:20px;
  }
  .ov-first-recap .ov-record-team-name{ text-align:left; color:#ffcf4d; }
  .ov-first-recap p{ margin:0 0 8px; font-size:13.5px; line-height:1.5; color:rgba(244,240,232,0.9); }

  .fight-pop{ opacity:0.65; font-weight:600; }
  /* The one orchestrated moment in the app: Fight Power is concealed for
     the entire draft (see .stat-hint-chip - one axis of three shows as a
     coarse tier, never the number), so the instant it
     finally prints in the Battle Log is the emotional payoff the rest of
     the game is built around - it earns a real animation, which is why
     nothing else in this file gets an entrance effect of its own. Staggered
     per log entry via nth-child so the numbers land one bout at a time
     even though the log itself renders as one block - the closest this
     pass gets to true sequential pacing without changing when the battle
     log's own HTML is built. */
  .fight-power{
    display:inline-block;
    font-weight:700; color:var(--blaze-bright);
    font-variant-numeric:tabular-nums;
    animation: powerReveal 0.42s cubic-bezier(0.2, 0.9, 0.3, 1.3) backwards;
  }
  @keyframes powerReveal{
    from{ opacity:0; transform:scale(0.55); filter:brightness(2.2); }
    60%{ opacity:1; }
    to{ opacity:1; transform:scale(1); filter:brightness(1); }
  }
  /* The Battle Log is a static block of HTML (every bout is already in the
     DOM), but it's meant to read as a sequential replay, not a dumped recap
     - each bout's own entrance is staggered so the eye is walked through them
     bout-by-bout instead of everything landing at once. Covers up to a
     10-animal team (Hardcore/Extreme); anything past that shares the last
     item's timing rather than growing the wait further. */
  .ov-log-item{
    animation: logItemReveal 0.36s cubic-bezier(0.2, 0.8, 0.3, 1) backwards;
  }
  @keyframes logItemReveal{
    from{ opacity:0; transform:translateY(6px); }
    to{ opacity:1; transform:translateY(0); }
  }
  .ov-log-item:nth-child(1){ animation-delay:0s; }
  .ov-log-item:nth-child(1) .fight-power{ animation-delay:0.12s; }
  .ov-log-item:nth-child(2){ animation-delay:0.15s; }
  .ov-log-item:nth-child(2) .fight-power{ animation-delay:0.27s; }
  .ov-log-item:nth-child(3){ animation-delay:0.3s; }
  .ov-log-item:nth-child(3) .fight-power{ animation-delay:0.42s; }
  .ov-log-item:nth-child(4){ animation-delay:0.45s; }
  .ov-log-item:nth-child(4) .fight-power{ animation-delay:0.57s; }
  .ov-log-item:nth-child(5){ animation-delay:0.6s; }
  .ov-log-item:nth-child(5) .fight-power{ animation-delay:0.72s; }
  .ov-log-item:nth-child(6){ animation-delay:0.75s; }
  .ov-log-item:nth-child(6) .fight-power{ animation-delay:0.87s; }
  .ov-log-item:nth-child(7){ animation-delay:0.9s; }
  .ov-log-item:nth-child(7) .fight-power{ animation-delay:1.02s; }
  .ov-log-item:nth-child(8){ animation-delay:1.05s; }
  .ov-log-item:nth-child(8) .fight-power{ animation-delay:1.17s; }
  .ov-log-item:nth-child(9){ animation-delay:1.2s; }
  .ov-log-item:nth-child(9) .fight-power{ animation-delay:1.32s; }
  .ov-log-item:nth-child(n+10){ animation-delay:1.35s; }
  .ov-log-item:nth-child(n+10) .fight-power{ animation-delay:1.47s; }
  @media (prefers-reduced-motion: reduce){
    .fight-power{ animation:none; }
    .ov-log-item{ animation:none; }
  }

  .ov-star{
    font-family:var(--font-stamp);
    font-size:12.5px;
    letter-spacing:0.02em;
    text-align:center;
    color:#ffcf4d;
    background:rgba(255,196,64,0.12);
    border-radius:6px;
    padding:7px 10px;
    margin-bottom:8px;
  }
  /* Two rows on purpose - a heading and the animal it names. On one row this
     line ran out of space around 320px and broke wherever it landed, most
     often between the em dash and the win count. */
  .ov-star-label{
    display:block;
    font-size:10px;
    letter-spacing:0.18em;
    text-transform:uppercase;
    color:rgba(255,207,77,0.75);
  }
  .ov-star-pick{ display:block; margin-top:3px; text-wrap:balance; }
  .ov-star-wins{ white-space:nowrap; }
  .fun-fact{
    font-size:13px;
    line-height:1.5;
    text-align:center;
    color:rgba(244,240,232,0.75);
    font-style:italic;
    margin:0 0 16px;
  }
  .ov-achievements{ margin:0 0 16px; }
  .ov-achievement-unlock{
    font-size:13px;
    line-height:1.5;
    text-align:center;
    color:#ffcf4d;
    background:rgba(255,196,64,0.14);
    border-radius:8px;
    padding:10px 12px;
    margin-bottom:8px;
  }
  .ov-achievement-unlock b{ font-family:var(--font-stamp); }
  .ov-level{
    margin:0 0 16px; padding:12px 14px;
    text-align:left;
    background:rgba(255,255,255,0.05);
    border:1px solid rgba(255,255,255,0.1);
    border-radius:8px;
  }
  .ov-level-up{
    font-family:var(--font-stamp);
    font-size:13px; letter-spacing:0.04em;
    text-align:center; color:#ffcf4d;
    margin-bottom:10px;
  }
  .ov-level-row{
    display:flex; justify-content:space-between; align-items:baseline;
    margin-bottom:8px;
  }
  .ov-level-name{ font-family:var(--font-stamp); font-size:15px; color:#f4f0e8; }
  .ov-level-gain{
    font-weight:700; font-size:13px; color:#8fd66a;
    font-variant-numeric:tabular-nums;
  }

  .ov-record-team-name{
    font-family:var(--font-stamp);
    font-size:10.5px;
    letter-spacing:0.05em;
    text-transform:uppercase;
    color:rgba(244,240,232,0.55);
    margin-bottom:6px;
    text-align:center;
  }

  .ov-btn{
    font-family:var(--font-stamp);
    font-weight:800;
    text-transform:uppercase;
    letter-spacing:0.08em;
    font-size:13px;
    padding:14px 24px;
    border-radius:6px;
    cursor:pointer;
    margin:5px;
  }
  .ov-btn.primary{ background:#f4f0e8; color:#0a0a0a; border:2px solid #f4f0e8; }
  .ov-btn.secondary{ background:transparent; color:#f4f0e8; border:2px solid rgba(244,240,232,0.4); }
  .ov-btn-sub{
    display:block; margin-top:2px;
    font-size:9.5px; font-weight:600; letter-spacing:0.04em;
    text-transform:none; opacity:0.65;
  }
  .ov-sticky-cta .ov-btn-sub{ display:none; }

  /* The inline CTA row is the one the overlay has always shown, kept in
     place so a desktop player sees the two big buttons after they scroll
     through the battle log. */
  .ov-cta-inline{ display:flex; flex-direction:column; align-items:center; gap:2px; }
  /* Sticky bottom CTA bar (mobile only). Fixed to the viewport so "Fight
     Again" is always one tap away regardless of how far the player has
     scrolled through battle log, achievements, and level-up cards. The
     fight-overlay uses z-index:1150; the sticky bar has to sit above that
     to remain on top of the overlay's own scroll surface, but stay below
     rulebook/leaderboard/welcome (all >=1200) so those still open on top.
     Padding-bottom of the overlay is extended with safe-area-aware inset
     so the last inline content isn't clipped behind the fixed bar. */
  .ov-sticky-cta{
    position:fixed;
    left:0; right:0; bottom:0;
    z-index:1155;
    display:none;
    gap:8px;
    padding:10px 14px calc(10px + env(safe-area-inset-bottom, 0px));
    background:linear-gradient(to top, rgba(10,26,46,0.98) 60%, rgba(10,26,46,0.85) 100%);
    border-top:1px solid rgba(244,240,232,0.15);
    box-shadow:0 -6px 18px rgba(0,0,0,0.35);
  }
  .ov-sticky-cta .ov-btn{
    flex:1;
    margin:0;
    padding:12px 10px;
    font-size:12.5px;
  }
  /* The sticky bar is a mobile-first affordance: on desktop the inline
     buttons at natural page flow are the primary path and a floating bar
     would just sit over the battle log. Wide viewports hide it entirely
     and rely on the inline pair; narrow viewports hide the inline pair
     is NOT done - both exist, so a tester can tap whichever appears first
     in their scroll position. */
  @media (max-width:720px){
    .ov-sticky-cta{ display:flex; }
    /* Enough room under the last inline block (the first game's "Why you
       won" box among them) that the sticky bar never sits on it once the
       overlay is scrolled to the end; the safe-area term matches the bar's. */
    .fight-overlay{ padding-bottom:calc(150px + env(safe-area-inset-bottom, 0px)); }
  }
  /* Level-up and new trophies, directly under the verdict. */
  .ov-unlocks{ margin:0 0 14px; }
  .ov-unlocks .ov-level-up{ margin-bottom:8px; }
  .ov-unlocks .ov-achievements{ margin:0; }

  /* Two share buttons stack vertically on narrow phones and sit inline on
     wider layouts - flex-wrap gives that for free without a media query,
     and the small gap keeps the primary CTA below still visually distinct. */
  .share-score-box{ margin:6px 0; display:flex; flex-wrap:wrap; gap:8px; }
  .share-score-box .ov-btn{ flex:1 1 200px; }
  .share-score-box .share-score-text{ flex:1 1 100%; }
  .share-score-text{
    display:block;
    width:100%;
    max-width:400px;
    margin:8px auto 0;
    font-family:var(--font-body);
    font-size:12.5px;
    line-height:1.4;
    color:#f4f0e8;
    background:rgba(255,255,255,0.08);
    border:1.5px solid rgba(244,240,232,0.35);
    border-radius:6px;
    padding:8px 10px;
    resize:none;
  }

  .trait-rules-list{
    display:flex; flex-direction:column; gap:8px; margin-top:8px;
    max-height:280px; overflow-y:auto; padding-right:6px;
  }
  .trait-rule-item{ line-height:1.45; }
  .trait-rule-mult{ font-family:var(--font-stamp); font-weight:800; opacity:0.75; }
  .rulebook-btn{
    display:inline-flex; align-items:center; gap:6px;
    font-family:var(--font-stamp); font-size:11.5px; font-weight:700;
    letter-spacing:0.03em; text-transform:uppercase;
    color:var(--moss-bright); background:rgba(92,143,58,0.12);
    border:1.5px solid var(--moss); border-radius:20px;
    padding:7px 15px; margin:2px 0 14px; cursor:pointer;
  }
  .rulebook-btn:hover{ background:rgba(92,143,58,0.22); }
  .fight-overlay .rulebook-btn{
    color:var(--brass-bright); background:rgba(217,150,46,0.14); border-color:var(--brass-bright);
  }
  .fight-overlay .rulebook-btn:hover{ background:rgba(217,150,46,0.24); }

  .how-to-play-fab{
    position:fixed;
    top:14px;
    left:14px;
    z-index:1100;
    display:inline-flex; align-items:center; gap:6px;
    font-family:var(--font-stamp); font-size:11px; font-weight:700;
    letter-spacing:0.03em; text-transform:uppercase;
    color:var(--parchment); background:rgba(20,14,8,0.55);
    border:1.5px solid rgba(244,240,232,0.4); border-radius:20px;
    padding:7px 14px; cursor:pointer;
    white-space:nowrap; overflow:hidden; text-overflow:ellipsis;
    /* Same reasoning as .leaderboard-btn min-height below: keeps assistive
       overlays from painting on top of the label text. */
    min-height:34px;
    backdrop-filter:blur(2px);
  }
  .how-to-play-fab:hover{ background:rgba(20,14,8,0.75); }

  .rules-overlay{
    position:fixed;
    inset:0;
    z-index:1200;
    background:rgba(10,6,3,0.55);
    display:flex;
    align-items:flex-end;
    justify-content:center;
    padding:0;
  }
  .rules-overlay[hidden]{ display:none; }
  .rules-sheet{
    background:
      linear-gradient(180deg, rgba(255,255,255,0.06), transparent 40%),
      var(--drawer);
    border:1px solid var(--drawer-edge);
    border-bottom:none;
    color:var(--parchment);
    width:100%;
    max-width:520px;
    max-height:85vh;
    border-radius:14px 14px 0 0;
    padding:20px 22px 26px;
    overflow-y:auto;
    box-shadow:0 -10px 34px var(--drawer-shadow);
    animation: rulesSheetUp 0.28s ease;
  }
  @keyframes rulesSheetUp{
    from{ transform:translateY(28px); opacity:0; }
    to{ transform:translateY(0); opacity:1; }
  }
  @media (prefers-reduced-motion: reduce){
    .rules-sheet{ animation:none; }
  }
  .rules-sheet-head{
    display:flex; align-items:center; justify-content:space-between;
    margin-bottom:8px;
  }
  .rules-sheet-title{
    font-family:var(--font-stamp); font-weight:800; text-transform:uppercase;
    letter-spacing:0.06em; font-size:15px; color:var(--parchment);
  }
  .rules-sheet-close{
    background:none; border:none; font-size:22px; line-height:1; cursor:pointer;
    color:var(--parchment-dim); padding:2px 6px;
  }
  .rules-sheet-close:hover{ color:var(--parchment); }

  /* ------------------------------------------------------------------
     Private-beta fixes (appended 2026-07 during standalone extraction).
     Keep new rules in this block so the original prototype styles above
     remain byte-for-byte diffable against the source artifact.
     ------------------------------------------------------------------ */

  /* The 58px phone reservation that used to live here is gone. It dated from
     when the only fixed thing up there was a single "How To Play" button; the
     strip that replaced it stacks into two rows at this same breakpoint and
     reserves 104px further down the file, which won on source order anyway. It
     is deleted rather than left as a no-op because it was the smaller number of
     the two, so anything that disturbed the ordering would have quietly put the
     buttons back on top of the kicker. */

  /* First-login How To Play (js/welcome.js). Centred on desktop, a bottom
     sheet on phones so the actions sit under the thumb — above the rules
     sheet, below the auth gate, which is never up at the same time. */
  .welcome-overlay{
    position:fixed;
    inset:0;
    z-index:1300;
    background:rgba(10,6,3,0.68);
    display:flex;
    align-items:center;
    justify-content:center;
    padding:16px;
    overflow-y:auto;
  }
  .welcome-overlay[hidden]{ display:none; }
  .welcome-sheet{
    background:
      linear-gradient(180deg, rgba(255,255,255,0.06), transparent 40%),
      var(--drawer);
    border:1px solid var(--drawer-edge);
    color:var(--parchment);
    width:100%;
    max-width:480px;
    max-height:calc(100vh - 32px);
    overflow-y:auto;
    border-radius:14px;
    padding:20px 22px 22px;
    box-shadow:0 18px 44px var(--drawer-shadow);
    animation: welcomeSheetIn 0.24s ease;
  }
  @keyframes welcomeSheetIn{
    from{ transform:translateY(14px); opacity:0; }
    to{ transform:translateY(0); opacity:1; }
  }
  @media (prefers-reduced-motion: reduce){
    .welcome-sheet{ animation:none; }
  }
  .welcome-head{
    display:flex; align-items:center; justify-content:space-between; gap:10px;
  }
  .welcome-title{
    font-family:var(--font-stamp); font-weight:800; text-transform:uppercase;
    letter-spacing:0.06em; font-size:15px; color:var(--parchment);
    margin:0;
  }
  .welcome-close{
    background:none; border:none; font-size:20px; line-height:1; cursor:pointer;
    color:var(--parchment-dim); padding:2px 6px;
  }
  .welcome-close:hover{ color:var(--parchment); }
  .welcome-lede{
    margin:6px 0 12px; font-size:13px; color:var(--parchment-dim);
  }
  .welcome-steps{
    margin:0; padding-left:20px;
    font-size:13.5px; line-height:1.5;
  }
  .welcome-steps li{ margin-bottom:9px; }
  .welcome-steps li:last-child{ margin-bottom:0; }
  .welcome-foot-note{
    margin:12px 0 0; font-size:12px; color:var(--parchment-dim);
  }
  .welcome-status{
    margin:12px 0 0; padding:9px 11px;
    border:1.5px solid var(--blaze); border-radius:8px;
    background:rgba(196,69,31,0.1);
    font-size:12.5px; line-height:1.45; color:var(--parchment);
  }
  .welcome-status[hidden]{ display:none; }
  .welcome-actions{
    display:flex; flex-wrap:wrap; align-items:center; justify-content:space-between;
    gap:12px;
    margin-top:16px; padding-top:14px;
    border-top:1.5px solid var(--parchment-dim);
  }
  .welcome-optout{
    display:flex; align-items:center; gap:8px;
    font-size:12.5px; color:var(--parchment); cursor:pointer;
    /* A 44px-tall hit area on touch without changing how it reads. */
    min-height:44px;
  }
  .welcome-optout input{
    width:17px; height:17px; margin:0; flex-shrink:0;
    accent-color:var(--moss-dark); cursor:pointer;
  }
  .welcome-buttons{ display:flex; align-items:center; gap:8px; margin-left:auto; }
  .welcome-start, .welcome-retry{
    font-family:var(--font-stamp); font-weight:800; text-transform:uppercase;
    letter-spacing:0.05em; font-size:12px;
    border-radius:8px; padding:11px 18px; min-height:44px; cursor:pointer;
  }
  .welcome-start{
    border:2px solid var(--moss-dark);
    background:var(--moss); color:var(--parchment);
  }
  .welcome-start:hover{ background:var(--moss-dark); }
  .welcome-retry{
    border:1.5px solid var(--parchment-dim);
    background:transparent; color:var(--parchment);
  }
  .welcome-retry:hover{ border-color:var(--parchment); }
  .welcome-retry[hidden]{ display:none; }
  .welcome-start[disabled], .welcome-retry[disabled]{ opacity:0.6; cursor:default; }
  .welcome-sheet :focus-visible{ outline:2px solid var(--parchment); outline-offset:2px; }

  @media (max-width:560px){
    .welcome-overlay{ align-items:flex-end; padding:0; }
    .welcome-sheet{
      max-width:none;
      max-height:88vh;
      border-bottom:none;
      border-radius:14px 14px 0 0;
      padding:18px 18px calc(18px + env(safe-area-inset-bottom));
    }
    .welcome-buttons{ width:100%; }
    .welcome-start{ flex:1; }
  }

  /* ------------------------------------------------------------------
     Mobile draft aids (appended 2026-07).
     A sticky draft status strip and the post-pick roster detail sheet.
     Everything here is scoped to max-width:720px except the sheet, which
     is shared with desktop, so the desktop layout above is untouched.
     ------------------------------------------------------------------ */

  /* Hidden by default: the strip is a phone affordance and must not paint,
     measure or take space on desktop. */
  .draft-status{ display:none; }

  /* Drafted roster slots become buttons. They must stay visually identical
     to the non-interactive slots so the card does not change on desktop —
     only the affordances (cursor, focus ring, press state) are added. */
  button.fight-slot{
    font-family:inherit;
    line-height:inherit;
    cursor:pointer;
    display:block;
    width:100%;
    appearance:none;
    -webkit-appearance:none;
  }
  button.fight-slot:hover{ --slot-edge: var(--parchment); }
  button.fight-slot:active{ transform:translateY(1px); }
  /* Outline is clipped by clip-path - stack a second inset shadow for
     the focus ring instead. First shadow (2px ink-text) reads as the
     focus outline; second shadow (existing --slot-edge) keeps the
     border in place under it. */
  button.fight-slot:focus-visible{
    box-shadow: inset 0 0 0 2px var(--parchment), inset 0 0 0 4px var(--slot-edge);
  }

  /* The blind-draft reminder. Deliberately quiet: it is a standing rule of
     the game, not a warning. */
  .draft-blind-note{
    margin:8px 0 0;
    font-size:11.5px;
    line-height:1.45;
    color:var(--parchment-dim);
    font-style:italic;
  }
  .pop-badge-note{
    margin:10px 0 0;
    font-size:11px;
    line-height:1.4;
    color:var(--parchment-dim);
  }

  /* Text that exists only for screen readers. Clipped to a 1px box rather than
     display:none, which would take it out of the accessibility tree too. */
  .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;
  }

  /* ------------------------------------------ post-pick roster detail sheet */

  /* Sits above the rules sheet (1200) and below the first-login guide (1300),
     neither of which can be open at the same time as a draft. */
  .roster-sheet-overlay{
    position:fixed;
    inset:0;
    z-index:1250;
    background:rgba(10,6,3,0.68);
    display:flex;
    align-items:center;
    justify-content:center;
    padding:16px;
    overflow-y:auto;
  }
  .roster-sheet-overlay[hidden]{ display:none; }
  .roster-sheet{
    background:
      linear-gradient(180deg, rgba(255,255,255,0.06), transparent 40%),
      var(--drawer);
    border:1px solid var(--drawer-edge);
    border-radius:14px;
    box-shadow:0 18px 44px var(--drawer-shadow);
    color:var(--parchment);
    width:100%;
    max-width:420px;
    max-height:calc(100vh - 32px);
    overflow-y:auto;
    -webkit-overflow-scrolling:touch;
    padding:18px 20px 20px;
    margin:auto;
  }
  .roster-sheet:focus{ outline:none; }
  .roster-sheet :focus-visible{ outline:2px solid var(--parchment); outline-offset:2px; }
  /* Drag handle affordance. Desktop shows a centred dialog, so it is only
     meaningful on the phone layout. */
  .roster-sheet-grabber{ display:none; }
  .roster-sheet-head{
    display:flex;
    align-items:center;
    gap:10px;
    margin-bottom:4px;
  }
  /* 48px, up from 30px: this header sits in a 420px-max sheet whose whole
     purpose is "look at just this one animal" - it was smaller than the
     battle reveal despite being the more deliberate look. */
  .roster-sheet-emoji{ font-size:48px; line-height:1; flex-shrink:0; }
  .roster-sheet-title{
    flex:1;
    min-width:0;
    margin:0;
    font-family:var(--font-stamp);
    font-weight:800;
    font-size:17px;
    letter-spacing:0.01em;
    color:var(--parchment);
    overflow-wrap:anywhere;
  }
  .roster-sheet-close{
    flex-shrink:0;
    background:none; border:none; cursor:pointer;
    color:var(--parchment-dim);
    font-size:18px; line-height:1;
    /* Touch target, without growing the visual weight of the glyph. */
    min-width:44px; min-height:44px;
    display:flex; align-items:center; justify-content:center;
  }
  .roster-sheet-close:hover{ color:var(--parchment); }
  .roster-sheet-slot{
    margin:0 0 12px;
    font-family:var(--font-stamp);
    font-size:11px;
    font-weight:800;
    letter-spacing:0.08em;
    text-transform:uppercase;
    color:var(--moss-bright);
  }
  .roster-sheet-facts{
    list-style:none;
    margin:0;
    padding:0;
    border-top:1.5px solid var(--parchment-dim);
  }
  .roster-sheet-fact{
    padding:10px 0;
    font-size:13.5px;
    line-height:1.5;
    border-bottom:1px solid rgba(243, 236, 224,0.22);
    overflow-wrap:anywhere;
  }
  .roster-sheet-fact[hidden]{ display:none; }
  .roster-sheet-fact:last-child{ border-bottom:none; }
  .roster-sheet-facts:empty{ display:none; }
  /* Dash used to carry "this is an alert" alongside the amber tint - now
     solid, since the tinted background plus the amber border already say
     that on their own (.welcome-status a few screens over already uses a
     solid border for the same kind of box). */
  .roster-sheet-blind{
    margin:12px 0 0;
    padding:10px 11px;
    border:1.5px solid var(--brass-bright);
    border-radius:8px;
    background:rgba(217,150,46,0.12);
    font-size:12.5px;
    line-height:1.45;
    color:var(--parchment);
  }
  .roster-sheet-done{
    display:block;
    width:100%;
    margin-top:16px;
    font-family:var(--font-stamp);
    font-weight:800;
    text-transform:uppercase;
    letter-spacing:0.05em;
    font-size:12px;
    border:2px solid var(--moss-dark);
    border-radius:8px;
    background:var(--moss);
    color:var(--parchment);
    padding:12px 18px;
    min-height:44px;
    cursor:pointer;
  }
  .roster-sheet-done:hover{ background:var(--moss-dark); }

  /* While a modal sheet is up, the page behind it must not scroll under the
     finger on iOS. Paired with focus trapping in js/game.js. */
  body.aa-sheet-open{ overflow:hidden; }

  @media (max-width:560px){
    .roster-sheet-overlay{ align-items:flex-end; padding:0; }
    .roster-sheet{
      max-width:none;
      max-height:86vh;
      border-bottom:none;
      border-radius:14px 14px 0 0;
      padding:10px 18px calc(18px + env(safe-area-inset-bottom, 0px));
    }
    .roster-sheet-grabber{
      display:block;
      width:38px; height:4px;
      margin:0 auto 10px;
      border-radius:999px;
      background:rgba(243, 236, 224,0.28);
    }
  }

  /* -------------------------------------- draft status strip (phones only) */

  @media (max-width:720px){
    /* Below the rules sheet (1200), the roster sheet (1250) and the first-login
       guide (1300) so any of them can cover it, and above the page itself.
       There is no results-screen special case to make: the strip only exists
       during the draft phase, so it is already gone by the time the results
       takeover paints its Play Again / Back to Menu buttons. */
    .draft-status{
      display:flex;
      align-items:center;
      gap:10px;
      position:fixed;
      left:0; right:0; bottom:0;
      z-index:1100;
      background:rgba(14,9,5,0.94);
      border-top:1px solid rgba(251,241,222,0.18);
      backdrop-filter:blur(6px);
      padding:7px 12px calc(7px + env(safe-area-inset-bottom, 0px));
    }
    /* Reserve exactly the strip's height, and only while it is up, so nothing
       is covered during a draft and no dead space is left on any other screen. */
    body.aa-draft-active{ padding-bottom:calc(64px + env(safe-area-inset-bottom, 0px)); }
    .draft-status[hidden]{ display:none; }
    .draft-status-metrics{
      flex:1;
      min-width:0;
      display:flex;
      align-items:baseline;
      gap:14px;
    }
    .draft-status-metric{
      display:flex;
      flex-direction:column;
      min-width:0;
    }
    .draft-status-metric b{
      font-family:var(--font-stamp);
      font-size:14px;
      font-weight:800;
      line-height:1.15;
      color:var(--parchment);
      white-space:nowrap;
    }
    .draft-status-metric-label{
      font-family:var(--font-stamp);
      font-size:8.5px;
      letter-spacing:0.09em;
      text-transform:uppercase;
      color:var(--parchment-dim);
      white-space:nowrap;
      overflow:hidden;
      text-overflow:ellipsis;
    }
    .draft-status-action{
      flex-shrink:0;
      font-family:var(--font-stamp);
      font-weight:800;
      text-transform:uppercase;
      letter-spacing:0.04em;
      font-size:11px;
      border:2px solid var(--moss-dark);
      border-radius:8px;
      background:var(--moss);
      color:var(--parchment);
      padding:9px 14px;
      min-height:40px;
      max-width:44vw;
      overflow:hidden;
      text-overflow:ellipsis;
      white-space:nowrap;
      cursor:pointer;
    }
    .draft-status-action:hover{ background:var(--moss-dark); }
    .draft-status-action[hidden]{ display:none; }
    .draft-status-action[disabled]{ opacity:0.55; cursor:default; }
    .draft-status-action:focus-visible{ outline:2px solid var(--parchment); outline-offset:2px; }
  }

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

  /* ------------------------------------------------------------------
     Global leaderboard + top strip (appended 2026-08).
     ------------------------------------------------------------------ */

  /* The fixed element is now the strip, not the buttons inside it. Only the
     controls take pointer events, so the empty middle of the bar does not
     swallow clicks meant for the page underneath. */
  .top-strip{
    position:fixed;
    top:0; left:0; right:0;
    z-index:1100;
    display:flex;
    align-items:flex-start;
    justify-content:space-between;
    /* Never wrap. Wrapping was how the strip got its second row between about
       580px and 620px, where the buttons and the account bar stopped fitting
       side by side - the same staggered shape the phone breakpoint exists to
       avoid, but with only 72px reserved beneath it, so it crowded the kicker.
       Above the breakpoint the account bar can shrink and the display name
       ellipses, which is a better answer at that width than a new row. */
    flex-wrap:nowrap;
    gap:8px;
    padding:12px 14px;
    pointer-events:none;
  }
  /* The buttons keep their natural width and the account bar absorbs every bit
     of the squeeze, because the bar has a graceful way to give ground - the
     display name ellipses - and the buttons do not. When both were allowed to
     shrink, "How To Play" broke onto two lines around 600px and made the strip
     taller instead of narrower. */
  .top-actions{
    display:flex;
    align-items:center;
    gap:8px;
    flex:0 0 auto;
    min-width:0;
    pointer-events:auto;
  }
  .top-strip .account-bar{
    flex:0 1 auto;
    min-width:0;
    margin-left:auto;
    pointer-events:auto;
  }
  /* Was `position:fixed; top:14px; left:14px` — .top-strip places it now. */
  .how-to-play-fab{ position:static; top:auto; left:auto; }

  .leaderboard-btn{
    display:inline-flex; align-items:center; gap:6px;
    font-family:var(--font-stamp); font-size:11px; font-weight:700;
    letter-spacing:0.03em; text-transform:uppercase;
    color:var(--parchment); background:rgba(20,14,8,0.55);
    border:1.5px solid rgba(244,240,232,0.4); border-radius:20px;
    padding:7px 14px; cursor:pointer;
    white-space:nowrap; overflow:hidden; text-overflow:ellipsis;
    /* iOS Safari occasionally paints Voice-Control/AssistiveTouch numeric
       badges directly on top of the button's textContent when the button is
       one contiguous text node. min-height gives the overlay chip room to
       sit above the label rather than through it; the two internal spans
       (see index.html and .top-action-icon/.top-action-label below) give
       assistive-tech a stable anchor per element so the overlay attaches to
       the icon rather than mid-word on the label. */
    min-height:34px;
    backdrop-filter:blur(2px);
  }
  .leaderboard-btn:hover{ background:rgba(20,14,8,0.75); }
  .leaderboard-btn:focus-visible{ outline:2px solid var(--parchment); outline-offset:2px; }

  /* Every top-action pill (How To Play / Leaderboard / Avatars) is split into
     an icon span and a label span so accessibility overlays anchor to the
     icon end of the pill instead of painting over the label. The label is
     the accessible name via aria-label on the button; the icon is
     aria-hidden. Kept side-effect-free (no positioning, no z-index) so this
     is purely a defensive markup change. */
  .top-action-icon{
    display:inline-flex; align-items:center; justify-content:center;
    flex:0 0 auto;
    /* A stamp-friendly em size that reads as an icon rather than a glyph
       inside the label; also gives the overlay attachment point a stable
       width even when the emoji font swaps in mid-load. */
    font-size:14px; line-height:1;
  }
  .top-action-label{
    display:inline-block;
    /* white-space and text-overflow live on the button already; keeping the
       label span transparent to them so nothing changes at the wrap layer. */
    white-space:inherit;
  }

  /* The 72px reservation this breakpoint used to add is now unconditional, in
     the base body rule. It was scoped to max-width:900px on the theory that
     wider windows had enough vertical slack to centre the content clear of the
     strip on their own; that stopped being true as the home screen grew past a
     viewport in height, and above 900px there was nothing holding the masthead
     down at all. The strip is the same one row at every width above the phone
     breakpoint, so one reservation covers all of them. */
  /* The strip breakpoints are deliberately split in two:

     * ≤820  — stacked layout. The original 560 covered the two-pill era, was
       raised to 680 for the four-pill row, and had to move again once the
       account bar grew a third line (streak). With .top-actions at ~520px and
       the account bar at ~290px of minimum content (name + Save progress or
       sync chip), the single-row layout needs the strip to be at least ~830px
       wide — below that, .account-id gets flex-squeezed to zero width and the
       name/level/streak text renders on top of itself at the left of the bar,
       leaving only the buttons visible on the right. The strip now stacks —
       pills on top, account row below — anywhere below 821. Above 820 nothing
       changes.

     * ≤560  — phone treatment for the pill row. At iPhone widths four full
       text labels ellipsis-clip ("HOW TO PLA...", "LEADERBOAR...",
       "ENCYCLOPEDI..."); we drop the text and keep just the emoji icon.
       Each button still has an explicit aria-label so assistive tech reads
       the full name unchanged. Small tablets in portrait (561–820) get the
       stacked layout with full-width label pills — the four pills fit
       comfortably in that row width without compression.

     Keeping the phone-only pill rules in a separate ≤560 block (instead of
     folding them into the ≤820 block) is what stops the icon-only treatment
     from leaking up into the 561–820 range where the labels still fit. */
  @media (max-width:820px){
    /* Deterministic two rows rather than "whenever it happens to wrap":
       buttons on top, account details beneath them. At 320px the two buttons
       alone already fill the row.

       Both rows are stretched to the same margins on purpose. They used to be
       hug-width and pulled to opposite ends — buttons left, account bar right
       — which left a gap under the buttons and a matching gap beside the
       account bar, and put the bar's left edge underneath the middle of the
       Leaderboard button. Nothing overlapped, but two pills ending nowhere
       near each other read as a collision. Making each row fill the strip
       turns three floating pills into a header with edges. */
    .top-strip{ flex-direction:column; align-items:stretch; gap:7px; padding:10px 10px 0; }
    /* Identity on the left, the controls you can act on against the right
       edge, so Sign out lines up with the end of the row above it. The auto
       margin lives on the name rather than being `justify-content:space-
       between` on the row, because that spread the save chip and Sign out to
       opposite ends of the leftover space and left the chip stranded in the
       middle of the bar. Pushing from the name keeps the two controls
       together as one cluster, and it behaves the same when the save chip is
       hidden and Sign out is the only thing on the right. */
    .top-strip .account-bar{ align-self:stretch; margin-left:0; }
    .top-strip .account-bar > .account-id{ margin-right:auto; }
    body{ padding-top:var(--top-strip-h, 104px); }
  }
  @media (max-width:560px){
    /* Icon-only pill row for phone widths (see block header above). */
    .top-actions{ flex-wrap:nowrap; gap:6px; }
    .top-actions > .how-to-play-fab,
    .top-actions > .leaderboard-btn{
      flex:1 1 0; min-width:0; justify-content:center;
      padding:7px 6px; gap:0;
    }
    .top-actions .top-action-label{
      /* Visually hidden but still exposed to assistive tech in case the
         aria-label ever gets stripped or overridden. */
      position:absolute; width:1px; height:1px; padding:0; margin:-1px;
      overflow:hidden; clip:rect(0 0 0 0); clip-path:inset(50%);
      white-space:nowrap; border:0;
    }
    .top-actions .top-action-icon{ font-size:18px; }
  }
  @media (max-width:360px){
    .top-actions{ gap:5px; }
    .top-actions > .how-to-play-fab,
    .top-actions > .leaderboard-btn{ padding:6px 4px; }
  }

  /* ------------------------------------------- compact home-screen panel --- */

  .week-leaders{
    margin:14px 0 4px;
    padding:12px 13px 11px;
    border:1.5px solid var(--drawer-edge);
    border-radius:10px;
    background:rgba(255,255,255,0.18);
    text-align:left;
  }
  .week-leaders-head{
    display:flex; align-items:center; justify-content:space-between;
    gap:8px; flex-wrap:wrap; margin-bottom:8px;
  }
  .week-leaders-title{
    font-family:var(--font-stamp); font-weight:800; text-transform:uppercase;
    letter-spacing:0.06em; font-size:12px; color:var(--parchment); margin:0;
  }
  .week-leaders-more{
    font-family:var(--font-stamp); font-size:10px; font-weight:800;
    letter-spacing:0.05em; text-transform:uppercase;
    color:var(--parchment); background:var(--moss-dark);
    border:1.5px solid var(--moss); border-radius:14px;
    padding:5px 11px; cursor:pointer; white-space:nowrap;
  }
  .week-leaders-more:hover{ background:color-mix(in srgb, var(--moss-dark) 80%, black); }
  .week-leaders-more:focus-visible{ outline:2px solid var(--moss-bright); outline-offset:2px; }
  .week-leaders-foot{
    margin:7px 0 0;
    font-family:var(--font-stamp); font-size:9.5px; letter-spacing:0.05em;
    text-transform:uppercase; color:var(--parchment-dim);
  }

  /* -------------------------------------------------- shared board rows --- */

  .lb-list{ list-style:none; margin:0; padding:0; display:flex; flex-direction:column; gap:5px; }
  /* Grid, not a table: the same markup is three columns on a laptop and a
     stacked card on a phone, so nothing ever scrolls sideways. */
  .lb-row{
    display:grid;
    grid-template-columns:auto 1fr auto;
    align-items:center;
    gap:4px 10px;
    padding:6px 9px;
    border:1px solid rgba(154, 124, 77,0.5);
    border-radius:8px;
    background:rgba(255,255,255,0.08);
    color:var(--parchment);
  }
  .lb-row.is-you{
    border-color:var(--moss-dark);
    background:rgba(92,143,58,0.18);
  }
  .lb-rank{
    font-family:var(--font-stamp); font-size:11px; font-weight:800;
    letter-spacing:0.04em; color:var(--parchment-dim); min-width:26px;
  }
  .lb-row.is-you .lb-rank{ color:var(--moss-bright); }
  .lb-who{ display:flex; flex-direction:column; gap:1px; min-width:0; }
  .lb-alias{
    font-size:13px; font-weight:700; line-height:1.2;
    overflow:hidden; text-overflow:ellipsis; white-space:nowrap;
  }
  .lb-you{
    font-family:var(--font-stamp); font-size:8.5px; font-weight:800;
    letter-spacing:0.1em; text-transform:uppercase; color:var(--moss-bright);
  }
  .lb-meta{
    font-family:var(--font-stamp); font-size:9px; letter-spacing:0.05em;
    text-transform:uppercase; color:var(--parchment-dim);
    overflow:hidden; text-overflow:ellipsis; white-space:nowrap;
  }
  /* Level chip lives on its own line under the alias. Same voice as .lb-meta
     (stamp font, uppercased) but a touch bolder because it is the identity
     of the player's standing, not throwaway metadata. Skipped entirely by
     leaderRow() when total_xp is missing so no phantom "Level 1" appears. */
  .lb-level{
    font-family:var(--font-stamp); font-size:9.5px; font-weight:700;
    letter-spacing:0.06em; text-transform:uppercase; color:var(--parchment);
    overflow:hidden; text-overflow:ellipsis; white-space:nowrap;
  }
  .lb-value{
    font-family:var(--font-stamp); font-size:15px; font-weight:800;
    letter-spacing:0.02em; color:var(--parchment); justify-self:end;
  }
  .lb-empty{
    margin:0; font-size:12px; line-height:1.45; color:var(--parchment-dim);
  }

  .lb-status{
    display:flex; flex-direction:column; align-items:flex-start; gap:8px;
    padding:4px 0;
  }
  .lb-status-text{ margin:0; font-size:12.5px; line-height:1.5; color:var(--parchment-dim); }
  .lb-status[data-state='error'] .lb-status-text{ color:var(--blaze-bright); }
  /* Offline is a condition, not a fault: stated plainly rather than in red. */
  .lb-status[data-state='offline'] .lb-status-text{ color:var(--parchment); }
  .lb-status-note{
    margin:6px 0 0; font-size:11.5px; line-height:1.45; color:var(--parchment-dim);
  }
  .lb-retry{
    font-family:var(--font-stamp); font-size:10px; font-weight:800;
    letter-spacing:0.06em; text-transform:uppercase;
    color:var(--parchment); background:var(--moss-dark); border:2px solid var(--moss);
    border-radius:8px; padding:6px 12px; cursor:pointer;
  }
  .lb-retry:hover{ background:color-mix(in srgb, var(--moss-dark) 80%, black); }
  .lb-retry:focus-visible{ outline:2px solid var(--parchment); outline-offset:2px; }
  .lb-spinner{
    width:16px; height:16px; border-radius:50%;
    border:2px solid rgba(243, 236, 224,0.25);
    border-top-color:var(--moss-dark);
    animation:lbSpin 0.8s linear infinite;
  }
  @keyframes lbSpin{ to{ transform:rotate(360deg); } }
  @media (prefers-reduced-motion: reduce){
    .lb-spinner{ animation-duration:2.4s; }
  }

  /* ------------------------------------------------------- board overlay --- */

  .leaderboard-overlay{
    position:fixed;
    inset:0;
    z-index:1250;
    background:rgba(10,6,3,0.6);
    display:flex;
    align-items:center;
    justify-content:center;
    padding:16px;
    overflow-y:auto;
  }
  .leaderboard-overlay[hidden]{ display:none; }
  .leaderboard-sheet{
    background:
      linear-gradient(180deg, rgba(255,255,255,0.06), transparent 40%),
      var(--drawer);
    border:1px solid var(--drawer-edge);
    color:var(--parchment);
    width:100%;
    max-width:560px;
    max-height:calc(100vh - 32px);
    border-radius:14px;
    padding:18px 20px 22px;
    overflow-y:auto;
    box-shadow:0 18px 44px var(--drawer-shadow);
  }
  .leaderboard-sheet:focus{ outline:none; }
  .leaderboard-head{
    display:flex; align-items:center; justify-content:space-between; gap:10px;
  }
  .leaderboard-title{
    font-family:var(--font-stamp); font-weight:800; text-transform:uppercase;
    letter-spacing:0.06em; font-size:15px; color:var(--parchment); margin:0;
  }
  .leaderboard-close{
    background:none; border:none; font-size:20px; line-height:1; cursor:pointer;
    color:var(--parchment-dim); padding:2px 6px;
  }
  .leaderboard-close:hover{ color:var(--parchment); }
  .leaderboard-tabs{
    display:flex; gap:6px; margin:12px 0 8px; flex-wrap:wrap;
  }
  .leaderboard-tab{
    flex:1 1 auto;
    font-family:var(--font-stamp); font-size:10.5px; font-weight:800;
    letter-spacing:0.06em; text-transform:uppercase;
    color:var(--parchment-dim); background:rgba(255,255,255,0.08);
    border:1.5px solid var(--drawer-edge); border-radius:8px;
    padding:8px 10px; min-height:38px; cursor:pointer; white-space:nowrap;
  }
  .leaderboard-tab:hover{ background:rgba(255,255,255,0.18); }
  .leaderboard-tab:focus-visible{ outline:2px solid var(--parchment); outline-offset:2px; }
  .leaderboard-tab[data-active='true']{
    color:var(--parchment); background:var(--moss-dark); border-color:var(--moss);
  }
  .leaderboard-note{
    margin:0 0 12px;
    font-family:var(--font-stamp); font-size:9.5px; letter-spacing:0.05em;
    text-transform:uppercase; color:var(--parchment-dim);
  }
  .leaderboard-body{ display:flex; flex-direction:column; gap:16px; }
  .lb-section-title{
    font-family:var(--font-stamp); font-weight:800; text-transform:uppercase;
    letter-spacing:0.05em; font-size:11.5px; color:var(--parchment);
    margin:0 0 2px;
  }
  .lb-section-note{
    margin:0 0 7px;
    font-size:11px; line-height:1.4; color:var(--parchment-dim);
  }
  .lb-section-title + .lb-list{ margin-top:6px; }

  @media (max-width:560px){
    .leaderboard-overlay{ align-items:flex-end; padding:0; }
    .leaderboard-sheet{
      max-width:none; max-height:88vh;
      border-radius:14px 14px 0 0; border-bottom:none;
      padding:16px 14px calc(20px + env(safe-area-inset-bottom, 0px));
    }
    /* Two rows instead of three columns: at 320px a rank, a 40-character alias
       and a score cannot share a line without one of them scrolling. */
    .lb-row{ grid-template-columns:auto 1fr; }
    .lb-row.has-avatar{ grid-template-columns:auto auto 1fr; }
    .lb-value{ grid-column:2; justify-self:start; font-size:14px; }
    .lb-row.has-avatar .lb-value{ grid-column:3; }
    .lb-rank{ grid-row:1 / span 2; align-self:start; padding-top:2px; }
    .lb-row.has-avatar .lb-avatar{ grid-row:1 / span 2; align-self:start; }
  }

  /* ============================================================ AVATARS === */
  /* Small avatar sprite shown next to a leaderboard alias when a player has
     one equipped. Non-avatar rows keep the original three-column layout via
     the .has-avatar class gate on .lb-row. */
  .lb-avatar{
    width:26px; height:26px; border-radius:6px;
    background:rgba(255,255,255,0.55);
    border:1px solid rgba(154, 124, 77,0.55);
    object-fit:contain;
  }
  .lb-row.has-avatar{ grid-template-columns:auto auto 1fr auto; }

  /* Level-panel headline: sprite + "Level N / 100" row. Sizes up on
     purpose - this is the shop reward, so it earns real real-estate on the
     home stats panel.

     The right-hand stats panel is narrow (~200px on desktop, less on some
     phones) - too narrow, it turns out, for "Level 100 / 100" plus a
     32px sprite to actually fit on one line at this size: the old
     white-space:nowrap "safety net" just forced the overflow instead of
     preventing it, and text-overflow:ellipsis on .level-headline then
     silently ate the "/ 100" a max-level player most wants to see.
     "/ 100" is now a block of its own under the level number instead of
     sharing its line - always fits regardless of sprite or panel width,
     so there is nothing left to truncate. */
  .level-headline{ display:flex; align-items:center; gap:8px; flex-wrap:wrap; }
  .level-headline-text{ min-width:0; }
  .level-of{ display:block; margin-left:0; margin-top:1px; }
  .level-avatar{ display:inline-flex; align-items:center; flex-shrink:0; }
  .level-avatar-img{
    width:32px; height:32px; border-radius:8px;
    background:rgba(255,255,255,0.55);
    border:1px solid rgba(154, 124, 77,0.55);
    object-fit:contain;
  }
  /* AAG Cash reads as its own currency, not a second XP bar - a small
     pill (like the Auction wallet chips) rather than plain text in the
     level-meta list, with its own banknote icon (see uiIcon('cash')). */
  .level-spendable{
    display:inline-flex; align-items:center; gap:4px;
    margin-top:6px; padding:3px 9px; border-radius:999px;
    font-family:var(--font-stamp);
    font-size:10px; font-weight:800; letter-spacing:0.06em; text-transform:uppercase;
    color:var(--moss-bright);
    background:rgba(92,143,58,0.14); border:1px solid rgba(92,143,58,0.4);
  }

  /* Fight header sprite - smaller, sits inline with the You:/Them: line. */
  .header-avatar-img{
    width:22px; height:22px; border-radius:5px;
    background:rgba(255,255,255,0.55);
    border:1px solid rgba(154, 124, 77,0.45);
    vertical-align:-6px; margin-right:6px;
    object-fit:contain;
  }

  /* ---------------------------------------------------- avatar shop tabs --- */
  .leaderboard-tab.is-active{
    background:var(--moss-dark); color:var(--parchment);
    border-color:var(--moss-dark);
  }

  /* ---------------------------------------------------- avatar shop grid --- */
  .avatar-shop-body{
    max-height:60vh; overflow-y:auto; padding-right:2px;
  }
  .avatar-shop-grid{
    display:grid;
    grid-template-columns:repeat(auto-fill, minmax(140px, 1fr));
    gap:10px;
  }
  /* Avatar shop tile: default --chip-size:10px because minmax(140px, 1fr)
     makes these medium tiles (140-180px on phone). Replaces old 12px
     border-radius. Inner .avatar-tile-art keeps its own rounded corners -
     the sprite plate is a photo frame, not a tile, so it stays rounded. */
  .avatar-tile{
    display:flex; flex-direction:column; align-items:stretch;
    gap:6px; padding:10px 8px;
    /* Border converted to inset shadow so it follows the chipped
       polygon on every edge (see .mode-cta / .double-spin-card notes). */
    --tile-edge: rgba(154, 124, 77, 0.9);          /* contrast pass: was 0.55 (2.25:1) → 3.9:1 (≥3:1 UI) */
    border:0;
    box-shadow: inset 0 0 0 1px var(--tile-edge);
    clip-path: polygon(
      0 0,
      calc(100% - var(--chip-size)) 0,
      100% var(--chip-size),
      100% 100%,
      var(--chip-size) 100%,
      0 calc(100% - var(--chip-size))
    );
    background:var(--drawer);
    transition: box-shadow 0.12s ease, background 0.12s ease;
  }
  .avatar-tile-equipped{
    --tile-edge: var(--moss-dark);
    background:rgba(92,143,58,0.1);
  }
  .avatar-tile-locked{ opacity:0.94; }
  .avatar-tile-art{
    position:relative; display:flex; align-items:center; justify-content:center;
    aspect-ratio:1 / 1; background:rgba(255,255,255,0.55);
    border-radius:10px; border:1px solid rgba(154, 124, 77,0.75);   /* contrast pass: was 0.35 → photo-frame border readable on drawer */
  }
  .avatar-shop-sprite{
    width:82%; height:82%; object-fit:contain;
  }
  .avatar-shop-sprite.is-locked{
    filter:grayscale(1) brightness(0.35) contrast(1.2) opacity(0.85);
  }
  .avatar-tile-star{
    position:absolute; top:4px; right:6px; font-size:16px;
  }
  .avatar-tile-caption{ display:flex; flex-direction:column; gap:2px; min-width:0; }
  .avatar-tile-rarity{
    font-family:var(--font-stamp); font-size:9px; letter-spacing:0.08em;
    text-transform:uppercase; color:var(--parchment-dim);
  }
  /* Rarity reads from material and border weight, not just a text-color
     ramp (see the Phase 2 reference) - Common stays the plain default tile,
     each tier up gets a heavier edge and its own tint, so the four are
     still distinguishable at thumbnail size or in greyscale, not only by
     hue. Text color is still set per tier below (5:1+ against --drawer in
     every case) as the secondary, not the only, signal. */
  .avatar-tile-rarity-legendary .avatar-tile-rarity{ color:var(--blaze-bright); }
  .avatar-tile-rarity-rare .avatar-tile-rarity{ color:var(--pewter); }
  .avatar-tile-rarity-uncommon .avatar-tile-rarity{ color:var(--moss-bright); }
  .avatar-tile-rarity-uncommon{
    --tile-edge: var(--moss-bright);
    box-shadow: inset 0 0 0 1.5px var(--tile-edge);
  }
  .avatar-tile-rarity-rare{
    --tile-edge: var(--pewter);
    box-shadow: inset 0 0 0 2px var(--tile-edge);
    background:linear-gradient(155deg, rgba(154,160,166,0.14), transparent 60%), var(--drawer);
  }
  .avatar-tile-rarity-legendary{
    --tile-edge: var(--blaze-bright);
    box-shadow: inset 0 0 0 2.5px var(--tile-edge);
    background:linear-gradient(155deg, rgba(240,122,46,0.16), transparent 65%), var(--drawer);
  }
  /* Four corner rivets on Legendary only - the loudest tier gets the most
     ornate frame, same "riveted brass plate" language as the Phase 2
     rarity reference. Skipped on Rare/Uncommon so it stays a genuine top-
     tier signal rather than decoration repeated at every level. */
  .avatar-tile-rarity-legendary{ position:relative; }
  .avatar-tile-rarity-legendary::before, .avatar-tile-rarity-legendary::after{
    content:''; position:absolute; width:5px; height:5px; border-radius:50%;
    background:radial-gradient(circle at 35% 30%, rgba(255,255,255,0.85), rgba(0,0,0,0.25) 70%);
    box-shadow:0 0 0 1px rgba(0,0,0,0.35); z-index:1;
  }
  .avatar-tile-rarity-legendary::before{ top:6px; left:6px; }
  .avatar-tile-rarity-legendary::after{ top:6px; right:6px; }
  .avatar-tile-name{
    font-size:13px; font-weight:700; line-height:1.2;
    overflow:hidden; text-overflow:ellipsis; white-space:nowrap;
  }
  .avatar-tile-actions{ margin-top:auto; }
  .avatar-tile-btn{
    width:100%; padding:7px 8px;
    font-family:var(--font-stamp); font-size:10.5px; font-weight:800;
    letter-spacing:0.05em; text-transform:uppercase;
    border:1px solid rgba(154, 124, 77,0.6);
    border-radius:8px; cursor:pointer;
    background:rgba(255,255,255,0.5); color:var(--cabinet);
  }
  .avatar-tile-btn.is-buy{ background:var(--moss-dark); color:var(--parchment); border-color:var(--moss-dark); }
  .avatar-tile-btn.is-buy.is-disabled{
    background:rgba(120,120,120,0.35); color:rgba(255,255,255,0.85);
    border-color:rgba(120,120,120,0.35); cursor:not-allowed;
  }
  .avatar-tile-btn.is-owned{ background:rgba(255,255,255,0.75); }
  .avatar-tile-btn.is-equipped{
    background:rgba(92,143,58,0.25); border-color:var(--moss-dark);
    color:var(--moss-bright);
  }
  .avatar-tile-btn:hover:not(:disabled){ filter:brightness(1.06); }
  .avatar-tile-btn:focus-visible{ outline:2px solid var(--parchment); outline-offset:2px; }

  /* ------------------------------------------------ triple spin draft grid */

  /* Double Spin's two-column grid is the default (see .double-spin-grid
     above). Triple Spin reuses every part of it and only re-columns, so the
     two styles stay visually the same mechanic at different widths. */
  .double-spin-grid[data-choices="3"]{ grid-template-columns:1fr 1fr 1fr; gap:8px; }
  .double-spin-grid[data-choices="3"] .double-spin-card{ padding:14px 6px; min-height:112px; }
  /* 50px, up from 40px: same reasoning as the two-up card above, scaled to
     this layout's narrower ~119px column. */
  .double-spin-grid[data-choices="3"] .double-spin-card .emoji{ font-size:50px; }
  .double-spin-grid[data-choices="3"] .double-spin-name{ font-size:10.5px; letter-spacing:0; }
  .double-spin-grid[data-choices="3"] .double-spin-slot .rulebook-btn{ font-size:9.5px; padding:5px 7px; }

  /* Three cards abreast stop being tappable well before 320px, so below this
     they stack. Vertical space is cheap here - the card already scrolls. */
  @media (max-width:400px){
    .double-spin-grid[data-choices="3"]{ grid-template-columns:1fr; gap:8px; }
    .double-spin-grid[data-choices="3"] .double-spin-card{
      flex-direction:row; justify-content:flex-start; gap:12px; padding:12px 14px; min-height:0;
    }
    /* Row layout has the full card width to work with (icon and name sit
       side by side, height grows with content), so this can go bigger than
       the plain 40px->50px bump above without any risk of clipping. */
    .double-spin-grid[data-choices="3"] .double-spin-card .emoji{ font-size:44px; }
    .double-spin-grid[data-choices="3"] .double-spin-name{ font-size:11.5px; text-align:left; }
  }

  /* ------------------------------------------------------------ Auction mode
     A five-card grid with visible cost badges, plus a two-row wallet HUD
     sitting between the header and the board. Deliberately reuses the
     .double-spin-card size/typography so the two draft flows read as the
     same physical control at different widths - the only new visual
     language is the coin badge and the locked-card treatment. */
  /* Wallet HUD - two-row strip that always sits between the header and
     the board. The player's wallet is the ONLY number that gates their
     next click (the enemy's is a read-only signal about how much room the
     AI has to buy things later), so the two panels render at deliberately
     different visual weights: player is wider (1.35fr vs 1fr), taller,
     with a brighter coin color; enemy is narrower, dimmer, and doesn't
     compete for attention. Fixes the flat-hierarchy playtest note without
     losing the enemy readout entirely. */
  .auction-hud{
    display:grid; grid-template-columns:1.35fr 1fr; gap:8px; margin:10px 0 12px;
    align-items:stretch;
  }
  .auction-wallet{
    display:flex; flex-direction:column; align-items:center; gap:2px;
    padding:8px 10px;
    background:rgba(122, 90, 18, 0.14);
    border:1px solid rgba(122, 90, 18, 0.5);
    border-radius:6px;
  }
  .auction-wallet-player{
    padding:10px 10px 11px;
    background:rgba(122, 90, 18, 0.22);
    border:2px solid rgba(217, 176, 74, 0.9);
    box-shadow:0 0 0 1px rgba(217, 176, 74, 0.25) inset;
  }
  .auction-wallet-enemy{
    background:rgba(122, 90, 18, 0.09);
    border:1px solid rgba(122, 90, 18, 0.35);
    opacity:0.9;
  }
  .auction-wallet-label{
    font-family:var(--font-stamp); font-size:10px; font-weight:700;
    letter-spacing:0.06em; text-transform:uppercase;
    color:var(--parchment-dim);
    text-align:center;
  }
  .auction-wallet-player .auction-wallet-label{
    color:var(--parchment);
  }
  .auction-wallet-coins{
    font-family:var(--font-stamp); font-size:18px; font-weight:800;
    color:var(--parchment); letter-spacing:0.02em;
  }
  .auction-wallet-player .auction-wallet-coins{
    font-size:24px; color:#f5d97a;
  }
  .auction-wallet-enemy .auction-wallet-coins{
    font-size:15px; color:rgba(244, 240, 232, 0.72);
  }

  /* ---- Coin Weight / OUTBID sting: the wallets react, not just the number */
  .auction-wallet{ position:relative; }
  @keyframes auctionWalletRattle{
    0%, 100%{ transform:translateX(0); }
    20%{ transform:translateX(-3px); }
    40%{ transform:translateX(3px); }
    60%{ transform:translateX(-2px); }
    80%{ transform:translateX(2px); }
  }
  .auction-wallet-rattle{ animation: auctionWalletRattle 0.4s ease; }
  @keyframes auctionWalletFlash{
    0%{ box-shadow:0 0 0 1px rgba(122,90,18,0.35) inset; }
    30%{ box-shadow:0 0 0 3px rgba(244,195,99,0.9) inset; }
    100%{ box-shadow:0 0 0 1px rgba(122,90,18,0.35) inset; }
  }
  .auction-wallet-flash{ animation: auctionWalletFlash 0.6s ease; }
  .auction-wallet-lowfunds{
    border-color:rgba(200,70,50,0.75) !important;
    box-shadow:0 0 0 1px rgba(200,70,50,0.3) inset !important;
  }
  .auction-wallet-lowfunds .auction-wallet-coins{ color:#e28a72; }
  /* Names the gap between the raw wallet number and what the reserve floor
     actually lets you spend this round - see playerReserved in
     renderAuctionDraft. Only rendered when that gap is non-zero, so a
     round-1 wallet with nothing reserved yet says nothing extra. */
  .auction-wallet-spendable{
    display:block; margin-top:1px;
    font-family:var(--font-stamp); font-size:9.5px; font-weight:700;
    letter-spacing:0.02em; color:var(--parchment-dim);
    cursor:help;
  }
  @keyframes auctionSpendFloat{
    0%{ opacity:0; transform:translate(-50%, 4px) scale(0.9); }
    25%{ opacity:1; transform:translate(-50%, -6px) scale(1.05); }
    100%{ opacity:0; transform:translate(-50%, -22px) scale(1); }
  }
  .auction-wallet-spend{
    position:absolute; left:50%; top:-6px; z-index:2;
    font-family:var(--font-stamp); font-weight:800; font-size:13px;
    color:#e28a72; pointer-events:none;
    animation: auctionSpendFloat 0.9s ease-out both;
  }
  @media (prefers-reduced-motion: reduce){
    .auction-wallet-rattle, .auction-wallet-flash, .auction-wallet-spend{ animation:none !important; }
    .auction-wallet-spend{ opacity:0; }
  }
  /* Auction grid is a five-column layout, using the same .double-spin-grid
     card shape - the data-choices="5" attribute is what selects this rather
     than the base two-up. On narrow phones it collapses to a two-column
     grid so cards stay tappable. */
  .double-spin-grid[data-choices="5"]{ grid-template-columns:repeat(5, 1fr); gap:6px; }
  .double-spin-grid[data-choices="5"] .double-spin-card{ padding:20px 4px 12px; min-height:120px; position:relative; }
  .double-spin-grid[data-choices="5"] .double-spin-card .emoji{ font-size:42px; }
  /* Fall back to the monospace stamp face on the tightest auction layout:
     Bungee Inline's inline outline is illegible below ~11px, but the 5-up
     auction board deliberately shrinks the animal name to 9.5px to fit
     "Great White Shark" on a narrow phone. Keep the display face only
     where it can breathe. */
  .double-spin-grid[data-choices="5"] .double-spin-name{
    font-family:var(--font-stamp); font-weight:800;
    font-size:9.5px; letter-spacing:0;
  }
  /* Long animal names ("Funnel-Web Spider", "Saber-Toothed Salmon") were
     letting the browser's auto-hyphenation kick in on the narrow 2-col
     mobile board, producing mid-word breaks like "FUNNE-/WEB SPIDER"
     that read as typos. Disable hyphenation on auction card names and
     break only at existing word/hyphen boundaries so the word survives
     intact - if it still doesn't fit, the card grows a hair taller
     rather than butchering the word. */
  .auction-card .double-spin-name{
    hyphens:none; -webkit-hyphens:none;
    word-break:normal; overflow-wrap:normal;
    line-height:1.15;
  }
  /* The 4-card variant is the board AFTER the enemy has taken one (their
     pick is spliced out server-side, so the render sees data-choices="4"),
     laid out on a wider four-column grid at the same card size so the
     player never sees the layout jump when the round reveals. */
  .double-spin-grid[data-choices="4"]{ grid-template-columns:repeat(4, 1fr); gap:6px; }
  .double-spin-grid[data-choices="4"] .double-spin-card{ padding:20px 4px 12px; min-height:120px; position:relative; }
  .double-spin-grid[data-choices="4"] .double-spin-card .emoji{ font-size:44px; }
  /* Same as data-choices="5": too small for the outlined display face. */
  .double-spin-grid[data-choices="4"] .double-spin-name{
    font-family:var(--font-stamp); font-weight:800;
    font-size:10px; letter-spacing:0;
  }
  /* Extreme Auction lays 9 animals down in a 3-per-row grid. Cards are
     smaller than the 5-up Standard board (30px emoji, tighter padding)
     so all nine fit above the fold on a normal-height desktop and stay
     tappable on tablet. Same .double-spin-card shape as the other
     auction layouts; only the sizing changes. The 8-up variant below is
     what the grid becomes after the enemy takes one card - kept on the
     SAME 3-column layout so the board doesn't visually snap to a new
     shape mid-round. */
  .double-spin-grid[data-choices="9"],
  .double-spin-grid[data-choices="8"]{ grid-template-columns:repeat(3, 1fr); gap:6px; }
  .double-spin-grid[data-choices="9"] .double-spin-card,
  .double-spin-grid[data-choices="8"] .double-spin-card{
    padding:16px 4px 10px; min-height:100px; position:relative;
  }
  .double-spin-grid[data-choices="9"] .double-spin-card .emoji,
  .double-spin-grid[data-choices="8"] .double-spin-card .emoji{ font-size:34px; }
  /* Same reason as the 5-up: names on cards this small need the stamp
     face; Bungee's inline outline is illegible below ~11px. */
  .double-spin-grid[data-choices="9"] .double-spin-name,
  .double-spin-grid[data-choices="8"] .double-spin-name{
    font-family:var(--font-stamp); font-weight:800;
    font-size:10px; letter-spacing:0;
  }
  /* Coin cost badge - top-right corner of every auction card. The gold
     mirrors the .auction-cta home accent so a player who read the home
     card sees the same currency color here. */
  .auction-cost-badge{
    position:absolute; top:5px; right:5px;
    display:inline-flex; align-items:center; gap:2px;
    padding:2px 6px;
    background:#7a5a12; color:var(--parchment);
    font-family:var(--font-stamp); font-size:11px; font-weight:800;
    letter-spacing:0.02em;
    border-radius:999px;
    border:1px solid rgba(0, 0, 0, 0.28);
    line-height:1;
  }
  /* The card's real second stat - Fight Power - never renders during a
     draft. This chip is the one piece of it a blind draft is allowed to
     show: a single named axis (Ferocity/Resilience/Instinct - see
     statHintFor) at a coarse tier, never the number. Static in normal flow
     rather than corner-absolute like the cost/lock badges, since it renders
     on every draft card in the game (both the plain blind draft and
     Auction's), and those two templates don't share a positioning context.
     Never rendered on an enemy-taken/mystery auction slot - those already
     carry their own badge in that spot. */
  .stat-hint-chip{
    display:inline-flex; align-items:center; gap:4px;
    padding:2px 7px;
    background:rgba(0,0,0,0.22);
    font-family:var(--font-stamp); font-size:9.5px; font-weight:700;
    letter-spacing:0.06em; text-transform:uppercase;
    border-radius:3px;
    border:1px solid var(--drawer-edge);
    line-height:1.4;
  }
  .stat-hint-label{ color:var(--graphite); }
  .stat-hint-tier{ font-weight:800; }
  .stat-hint-mult{ font-weight:800; font-variant-numeric:tabular-nums; letter-spacing:0.02em; }
  .stat-hint-low .stat-hint-tier{ color:var(--pewter-dark); }
  .stat-hint-average .stat-hint-tier{ color:var(--parchment-dim); }
  .stat-hint-high .stat-hint-tier{ color:var(--blaze-bright); }
  /* Favorable-multiplier chip (matchup rule): 'x1.30 UNDERDOG'. Warmer   */
  /* than the plain High-tier color to signal 'this is a concrete edge,   */
  /* not just a stat percentile'. */
  .stat-hint-rule .stat-hint-mult{ color:var(--blaze-bright); }
  .stat-hint-rule .stat-hint-label{ color:var(--ink); }
  /* Stat-percent fallback: '+22% FEROCITY'. Same coloring as the         */
  /* legacy High-tier chip since it carries the same information -        */
  /* the '+N%' just makes the tier concrete. */
  .stat-hint-pct .stat-hint-mult{ color:var(--blaze-bright); }
  .stat-hint-pct .stat-hint-label{ color:var(--ink); }
  /* Cards priced above the wallet floor. Kept visible (the player has to
     see the whole market) but read as clearly out of reach: dimmed, no
     hover state, and the cursor stays default. */
  .auction-card-locked{
    opacity:0.42;
    cursor:not-allowed;
  }
  .auction-card-locked:hover{
    background:rgba(92,143,58,0.16); transform:none;
  }
  /* The enemy's just-taken slot renders in place instead of being spliced
     out, so the player sees the full 5-card market (9-card in Extreme).
     It reads as "gone" rather than "unaffordable" - crossed out with a
     diagonal stripe and a red-tinted opponent-name stamp - so the two
     locked states never get confused.

     Deliberately STATIC, no pulse. Enemy-opens rounds get the central
     .auction-enemy-reveal (bounce-in + name slide + cost, ~1200ms) as
     the "AI just moved" event; by the time this class renders, the
     player has already seen the pick land and a ghost of it fly into
     the AI roster slot. A second animated reveal on the small board
     card here would be showing the same information twice back to back.
     Player-opens rounds don't hit this class at all - auctionBoard is
     nulled by pickAuctionChoice before the enemy's follow-up reveal
     renders, so the board never repaints for that round.

     Previously carried a 1.1s red drop-shadow pulse (auctionEnemyTakenPulse)
     back when this class WAS the enemy's only reveal - removed once the
     central .auction-enemy-reveal took over that job. See PR audit. */
  .auction-card-enemy-taken{
    opacity:0.55;
    background:repeating-linear-gradient(
      -35deg,
      rgba(122, 90, 18, 0.08) 0 6px,
      rgba(122, 90, 18, 0.02) 6px 12px
    );
  }

  .auction-mystery-glyph{
    font-family:var(--font-stamp); font-weight:800;
    color:rgba(122, 90, 18, 0.6);
  }

  /* Snake-draft opener chip - one line above the wallets. Two visual
     variants (player vs. enemy) so a glance is enough to read who has to
     pick first this round. */
  .auction-opener-chip{
    display:inline-block; margin:6px auto 10px;
    padding:5px 12px; border-radius:999px;
    font-family:var(--font-stamp); font-size:11px; font-weight:800;
    letter-spacing:0.05em; text-transform:uppercase;
    border:2px solid #4a3826;
  }
  .auction-opener-chip[data-opener="player"]{
    background:#e6f2d5; color:#2f4a12;
  }
  .auction-opener-chip[data-opener="enemy"]{
    background:#f4d7d0; color:#5a1f10;
  }
  /* Arena banner at the top of every draft screen. The tint layer uses the
     arena's own bg gradient at low opacity (see arenaBannerHtml in
     04-screens-start-and-draft.js for the inline style hookup), sitting
     under a solid panel edge so the name still reads on any gradient.
     Fixed height and tabular font so a long arena name ("The Midnight
     Grounds") doesn't reflow the whole draft header. */
  .arena-banner{
    position:relative; margin:6px auto 10px;
    padding:12px 16px; border-radius:12px;
    border:1px solid var(--drawer-edge);
    background:var(--drawer);
    overflow:hidden;
    text-align:center;
    max-width:520px;
  }
  .arena-banner-tint{
    position:absolute; inset:0;
    opacity:0.35;
    pointer-events:none;
  }
  .arena-banner-inner{
    position:relative; z-index:1;
  }
  .arena-banner-eyebrow{
    font-family:var(--font-stamp); font-size:10px; font-weight:800;
    letter-spacing:0.14em; text-transform:uppercase;
    color:var(--parchment); opacity:0.85;
    margin-bottom:2px;
  }
  .arena-banner-name{
    /* This is the main affordance - the arena name has to be the first
       thing the eye lands on when the draft screen paints. clamp() so
       it scales down on narrow mobile without wrapping mid-word. */
    font-family:var(--font-stamp); font-weight:800;
    font-size:clamp(18px, 4.6vw, 24px);
    letter-spacing:0.05em; color:var(--parchment);
    text-shadow:0 1px 2px rgba(0,0,0,0.4);
    line-height:1.15;
  }
  .arena-banner-desc{
    margin-top:4px;
    font-size:12px; color:var(--parchment); opacity:0.85;
    line-height:1.3;
  }
  /* Auction Recap - side-by-side player/enemy columns, same fight-stat-row
     styling the stats panel already uses so the numbers read consistently
     with everywhere else they appear in the game. */
  .auction-recap-grid{
    display:grid; grid-template-columns:1fr 1fr; gap:12px;
    margin-top:6px;
  }
  .auction-recap-col{
    background:var(--drawer); border:1px solid var(--drawer-edge);
    border-radius:10px; padding:12px 14px;
  }
  .auction-recap-name{
    font-family:var(--font-stamp); font-size:12px; font-weight:800;
    letter-spacing:0.05em; text-transform:uppercase;
    color:var(--parchment-dim); margin-bottom:6px;
  }
  @media (max-width:460px){
    .auction-recap-grid{ grid-template-columns:1fr; }
  }
  /* Draft Grade block on the recap: A-F letter + archetype tag beneath
     the roster name. Grade letter is oversized and color-coded; the
     archetype is a small chalk chip so it reads as a descriptor, not a
     second grade. */
  .auction-recap-grade{
    display:flex; align-items:center; gap:10px;
    margin:2px 0 10px;
  }
  .auction-recap-grade-letter{
    font-family:var(--font-stamp); font-size:34px; font-weight:900;
    line-height:1; letter-spacing:0.02em;
    padding:2px 10px 4px; border-radius:8px;
    background:rgba(0,0,0,0.18);
    border:1px solid var(--drawer-edge);
  }
  .auction-recap-grade-letter.grade-a{ color:#7bd88f; text-shadow:0 0 8px rgba(123,216,143,0.35); }
  .auction-recap-grade-letter.grade-b{ color:#c9e07a; }
  .auction-recap-grade-letter.grade-c{ color:#f0d97a; }
  .auction-recap-grade-letter.grade-d{ color:#f0a97a; }
  .auction-recap-grade-letter.grade-f{ color:#e96a5c; text-shadow:0 0 8px rgba(233,106,92,0.35); }
  .auction-recap-grade-letter.grade--{ color:var(--parchment-dim); font-size:26px; padding-top:4px; }
  .auction-recap-archetype{
    font-family:var(--font-stamp); font-size:11px; font-weight:800;
    letter-spacing:0.06em; text-transform:uppercase;
    padding:4px 8px; border-radius:999px;
    background:rgba(255,255,255,0.06); color:var(--parchment);
    border:1px solid var(--drawer-edge);
  }
  /* Priced-out stinger: fires exactly once per fight the first time the
     player is stranded. Same auto-skip notice with a red pulse and a
     PRICED OUT chip prepended - subsequent stranded rounds render the
     notice without the animation. */
  @keyframes pricedOutPulse{
    0%   { transform:scale(1);    box-shadow:0 0 0 0 rgba(233,106,92,0.55); }
    35%  { transform:scale(1.06); box-shadow:0 0 0 12px rgba(233,106,92,0); }
    70%  { transform:scale(0.99); }
    100% { transform:scale(1);    box-shadow:0 0 0 0 rgba(233,106,92,0); }
  }
  .auction-auto-skip-notice.auction-priced-out-stinger{
    animation:pricedOutPulse 0.9s ease-out 1;
    border-color:rgba(233,106,92,0.6);
  }
  .auction-priced-out-chip{
    display:inline-block; margin-right:6px;
    padding:2px 7px; border-radius:999px;
    background:#e96a5c; color:#fff;
    font-family:var(--font-stamp); font-size:10px; font-weight:900;
    letter-spacing:0.08em; text-transform:uppercase;
    vertical-align:1px;
  }
  /* Scoreboard-direct results hero: two big KO counts flanking a "vs",
     with the score formula underneath. Sits between the score/banner
     and the star / battle log so the numbers land before the prose. */
  .ov-scoreboard{
    display:grid; grid-template-columns:1fr auto 1fr; align-items:center;
    gap:12px; margin:14px auto 6px; max-width:520px;
  }
  .ov-scoreboard-cell{
    background:rgba(0,0,0,0.20); border:1px solid rgba(255,255,255,0.10);
    border-radius:12px; padding:10px 8px; text-align:center;
  }
  .ov-scoreboard-cell.ov-scoreboard-winner{
    background:linear-gradient(180deg, rgba(123,216,143,0.18) 0%, rgba(0,0,0,0.15) 100%);
    border-color:rgba(123,216,143,0.45);
  }
  .ov-scoreboard-num{
    font-family:var(--font-stamp); font-size:42px; font-weight:900;
    line-height:1; color:var(--parchment);
    text-shadow:0 2px 0 rgba(0,0,0,0.35);
  }
  .ov-scoreboard-label{
    margin-top:6px;
    font-family:var(--font-stamp); font-size:11px; font-weight:800;
    letter-spacing:0.06em; text-transform:uppercase;
    color:var(--parchment-dim);
    overflow:hidden; text-overflow:ellipsis; white-space:nowrap;
  }
  .ov-scoreboard-sublabel{
    font-size:10px; color:var(--parchment-dim); opacity:0.75;
    letter-spacing:0.08em; text-transform:uppercase;
  }
  .ov-scoreboard-sep{
    font-family:var(--font-stamp); font-size:16px; font-weight:900;
    color:var(--parchment-dim); letter-spacing:0.1em; text-transform:uppercase;
  }

  /* Small sentence-form caption under the score formula: same info the
     scoreboard already shows, kept for screen readers and quick scanners. */
  .ov-scoreboard-caption{
    text-align:center; font-size:12px; color:var(--parchment-dim);
    opacity:0.7; margin:0 auto 12px; max-width:520px;
  }
  @media (max-width:460px){
    .ov-scoreboard{ gap:8px; }
    .ov-scoreboard-num{ font-size:34px; }
    .ov-scoreboard-label{ font-size:10px; }
  }
  /* Both teams' aggregate Fight Power, shown every result screen (not just
     the first game) - the totals that actually decided the odds. */
  .ov-power-compare{
    margin:2px auto 14px; max-width:520px;
    background:rgba(0,0,0,0.15); border:1px solid rgba(255,255,255,0.08);
    border-radius:12px; padding:12px 14px;
  }
  .ov-power-compare-title{
    font-family:var(--font-stamp); font-size:11px; font-weight:800;
    letter-spacing:0.08em; text-transform:uppercase;
    color:var(--parchment-dim); margin-bottom:8px;
  }
  .ov-power-compare-row{
    display:grid; grid-template-columns:auto 1fr auto; align-items:center;
    gap:10px; margin-top:6px;
  }
  .ov-power-compare-row:first-of-type{ margin-top:0; }
  .ov-power-compare-name{
    font-family:var(--font-stamp); font-size:12px; color:var(--parchment);
    overflow:hidden; text-overflow:ellipsis; white-space:nowrap; max-width:120px;
  }
  .ov-power-compare-bar{
    height:8px; border-radius:99px; background:rgba(255,255,255,0.08);
    overflow:hidden;
  }
  .ov-power-compare-fill{
    height:100%; border-radius:99px;
    transition:width 0.5s cubic-bezier(0.2,0.8,0.3,1);
  }
  .ov-power-compare-fill.player{ background:var(--blaze-bright); }
  .ov-power-compare-fill.enemy{ background:var(--pewter); }
  .ov-power-compare-num{
    font-family:var(--font-stamp); font-size:13px; font-weight:800;
    color:var(--blaze-bright); text-align:right; min-width:44px;
  }
  @media (prefers-reduced-motion:reduce){
    .ov-power-compare-fill{ transition:none; }
  }
  .auction-taken-badge{
    position:absolute; top:5px; left:5px;
    display:inline-flex; align-items:center;
    padding:2px 6px; max-width:calc(100% - 60px);
    background:#8a2f1c; color:var(--parchment);
    font-family:var(--font-stamp); font-size:9px; font-weight:800;
    letter-spacing:0.04em; text-transform:uppercase;
    border-radius:999px;
    border:1px solid rgba(0, 0, 0, 0.28);
    line-height:1;
    white-space:nowrap; overflow:hidden; text-overflow:ellipsis;
  }
  /* One-word explanation of why a card is dimmed. Only unaffordable cards
     carry this chip - enemy-taken cards already have the opponent-name
     stamp in the same corner. Sits bottom-left so it never overlaps the
     cost badge (top-right) or the emoji (center). Small, dim, and
     non-clickable - it doesn't compete with real controls, it just
     answers the "why is this faded" question the playtest report flagged. */
  .auction-lock-chip{
    position:absolute; bottom:5px; left:5px;
    padding:2px 6px;
    background:rgba(20, 20, 20, 0.55); color:rgba(244, 240, 232, 0.85);
    font-family:var(--font-stamp); font-size:8.5px; font-weight:700;
    letter-spacing:0.04em; text-transform:uppercase;
    border-radius:999px;
    border:1px solid rgba(0, 0, 0, 0.25);
    line-height:1;
    pointer-events:none;
  }
  /* Unaffordable cards get a slightly less aggressive dim than
     enemy-taken so the two locked reasons read as distinct at a glance -
     one is a market state the player can't change, the other is a
     budget signal that might unlock next round. */
  .auction-card-unaffordable{
    opacity:0.5;
  }

  /* Enemy pick reveal - the moment the AI has taken its card. The glyph
     scales in with a coin flash and the name slides up under it. Timed
     to fit inside the advanceAfterAiPick setTimeout window so the
     animation completes before the next round's board renders.

     .auction-enemy-reveal is the outer wrapper (so the reveal can be
     targeted by tests via data-testid); the glyph and name each get their
     own keyframe so they don't animate in lockstep. The gold coin badge
     shows what the enemy PAID, which is the one number a player couldn't
     see on the board and now has to reason against their own wallet. */
  .auction-enemy-reveal{
    display:flex; flex-direction:column; align-items:center; gap:2px;
    padding:8px 0 4px;
    position:relative;
  }
  /* OUTBID vs. SOLD: only rendered when the enemy opened the round (see
     renderAuctionDraft's enemyOpenedThisRound) - distinguishes "you could
     have had this" from "this was never in your range" at a glance. */
  @keyframes auctionStingSlam{
    0%{ opacity:0; transform:translate(-50%,-50%) rotate(-18deg) scale(1.8); }
    60%{ opacity:1; transform:translate(-50%,-50%) rotate(-14deg) scale(0.94); }
    100%{ opacity:1; transform:translate(-50%,-50%) rotate(-14deg) scale(1); }
  }
  .auction-sting{
    position:absolute; left:50%; top:38%; z-index:3;
    font-family:var(--font-stamp); font-weight:800; letter-spacing:0.08em;
    padding:4px 14px; border-radius:4px; pointer-events:none;
    animation: auctionStingSlam 0.5s cubic-bezier(0.2,0.9,0.3,1.2) 0.35s both;
  }
  .auction-sting-outbid{
    font-size:20px; color:#fff2e6;
    background:rgba(191,58,34,0.92); border:2px solid #fff2e6;
    box-shadow:0 0 18px rgba(191,58,34,0.6);
  }

  @media (prefers-reduced-motion:reduce){
    .auction-sting{ animation:none; opacity:1; transform:translate(-50%,-50%) rotate(-14deg); }
  }
  .auction-enemy-reveal-glyph{
    animation:auctionEnemyGlyphIn 0.5s cubic-bezier(0.2, 0.9, 0.3, 1.2);
    filter:drop-shadow(0 0 12px rgba(122, 90, 18, 0.55));
  }
  .auction-enemy-reveal-name{
    animation:auctionEnemyNameIn 0.5s ease-out 0.15s both;
  }
  .auction-enemy-reveal-cost{
    display:inline-block; margin-left:4px;
    padding:2px 7px; border-radius:999px;
    background:#7a5a12; color:var(--parchment);
    font-family:var(--font-stamp); font-size:11px; font-weight:800;
    letter-spacing:0.02em; line-height:1;
    border:1px solid rgba(0, 0, 0, 0.28);
  }
  @keyframes auctionEnemyGlyphIn{
    0%{ transform:scale(0.4) rotate(-8deg); opacity:0; }
    55%{ transform:scale(1.15) rotate(2deg); opacity:1; }
    100%{ transform:scale(1) rotate(0); opacity:1; }
  }
  @keyframes auctionEnemyNameIn{
    0%{ transform:translateY(10px); opacity:0; }
    100%{ transform:translateY(0); opacity:1; }
  }
  @media (prefers-reduced-motion:reduce){
    .auction-enemy-reveal-glyph,
    .auction-enemy-reveal-name{ animation:none; }
    .auction-enemy-reveal-glyph{ filter:none; }
  }

  /* The flying ghost itself - see flyAuctionPickToRoster() in
     05-screens-fight-and-boot.js. Fixed and appended to <body> (not a
     descendant of .card) so it is never clipped by a card's own
     overflow, and disabled outright under prefers-reduced-motion there
     rather than here, since JS decides whether to create it at all. */
  .auction-fly-ghost{
    position:fixed;
    display:flex; align-items:center; justify-content:center;
    font-size:28px; line-height:1;
    pointer-events:none;
    z-index:9999;
    transition:transform 0.4s cubic-bezier(0.3, 0.6, 0.35, 1), opacity 0.4s ease-in 0.05s;
    will-change:transform, opacity;
  }
  .auction-fly-ghost img{ width:100%; height:100%; object-fit:contain; }

  /* Phone breakpoint: five across is too tight below ~460px, so the auction
     board collapses to a two-column grid and reflows to a row layout inside
     each card. Same treatment Triple Spin already uses at 400px.

     The 9/8-up Extreme boards stay in a 3-column grid at this width -
     showing them as rows on phone would push the last few cards well
     below the fold and add real scroll to every round. The cards
     tighten instead: smaller emoji, no font shrink (10px is already the
     floor for the stamp face). */
  @media (max-width:460px){
    .double-spin-grid[data-choices="5"]{ grid-template-columns:1fr 1fr; gap:8px; }
    .double-spin-grid[data-choices="4"]{ grid-template-columns:1fr 1fr; gap:8px; }
    .double-spin-grid[data-choices="9"],
    .double-spin-grid[data-choices="8"]{ grid-template-columns:repeat(3, 1fr); gap:5px; }
    .double-spin-grid[data-choices="9"] .double-spin-card,
    .double-spin-grid[data-choices="8"] .double-spin-card{
      padding:14px 3px 8px; min-height:92px;
    }
    .double-spin-grid[data-choices="9"] .double-spin-card .emoji,
    .double-spin-grid[data-choices="8"] .double-spin-card .emoji{ font-size:28px; }
    .double-spin-grid[data-choices="5"] .double-spin-card,
    .double-spin-grid[data-choices="4"] .double-spin-card{
      flex-direction:row; justify-content:flex-start; gap:12px; padding:14px 14px 14px 12px; min-height:0;
    }
    .double-spin-grid[data-choices="5"] .double-spin-card .emoji,
    .double-spin-grid[data-choices="4"] .double-spin-card .emoji{ font-size:40px; }
    .double-spin-grid[data-choices="5"] .double-spin-name,
    .double-spin-grid[data-choices="4"] .double-spin-name{ font-size:11px; text-align:left; }
  }

  /* ----------------------------------------------- draft desktop 2-col layout
     Above 1280px, a draft card widens from the base 460px cap to up to
     ~980px and re-flows into a left column (header, chips, wallets, board)
     and a right column (both team rosters). The playtest report called out
     ~1000px of unused horizontal space on desktop with the roster panels
     stacked below a narrow board; this converts that dead zone into an
     at-a-glance roster read without changing the card's own scroll flow.

     This shipped first as .auction-layout, auction-only, on the reasoning
     that the auction board was the widest. That was the wrong axis: the
     dead space is a property of the viewport, not of the mode. Measured on
     a 1440px laptop the auction draft used 1210px of width (84%) while
     every other mode - beginner, gauntlet, hardcore, daily, prehistoric -
     was still a 460px card beside a 230px rail: 710px, 49% of the screen,
     with ~365px of empty gutter down each side. The modes are the same
     shape (header, board, two rosters, round action) and this layout never
     depended on the one thing that differs, the number of cards on the
     board. So the class is now .draft-layout and every mode's draft phase
     gets it. The auction-only children (habitat / opener / market chips)
     are simply absent in the other modes, so their rules go inert there
     rather than needing to be split out.

     Only activated at a viewport that clearly benefits - tablets in
     landscape stay on the single-column path so the board stays big enough
     to tap. Below 1280 the .draft-layout class is a no-op and the card
     renders exactly as it did before. */
  @media (min-width:1000px){
    .card.draft-layout{
      width:min(980px, 96vw);
      max-width:none;
      padding:28px 30px 24px;
      display:grid;
      grid-template-columns:minmax(0, 1.15fr) minmax(0, 1fr);
      grid-column-gap:26px;
      grid-row-gap:0;
      align-items:start;
    }
    /* Everything the auction render emits belongs to the LEFT column
       (round header aside) unless it is explicitly named below. The
       original version opted individual selectors IN, which meant any
       child no rule claimed - the "Pricey round" market chip, the
       Auto-Finish button, the auto-skip notice - auto-placed into
       whatever cell was free, i.e. the roster column on the right,
       detached from the board it acts on. Defaulting to column 1 means
       a new element added to this screen lands under the board rather
       than on top of the rosters. */
    .card.draft-layout > *{
      grid-column:1 / 2;
    }
    /* The main-menu chip and the round header span both columns and are
       PINNED to rows 1 and 2.

       They have to be pinned. A full-width item cannot be placed inside
       the row range the roster reserves in column 2, so with an auto row
       the header was shoved below that whole reserved block: the round
       header ended up orphaned in the middle of the card, the entire
       left column (chips, wallets, board) started below the rosters, and
       the reserved-but-empty rows collapsed into a ~250px dead band
       across the top-left of a MacBook viewport. */
    .card.draft-layout > .card-menu-btn{
      grid-row:1;
      grid-column:1 / -1;
      /* A grid item stretches by default, which blew the pill out to the
         full 918px card width. Keep it chip-sized, as it is on mobile. */
      justify-self:start;
    }
    /* The round header stays pinned to row 2 for the reason above, but it
       sits in column 1 only. Spanning both columns centered it on the card,
       which put "ROUND 1 OF 8 - Triple Spin" at x=560 on a 1440px screen -
       floating over the 26px gutter between the board (136..613) and the
       rosters (639..1054), labelling neither. In column 1 it centers over
       the board it is actually describing. */
    .card.draft-layout > .case-no{
      grid-row:2;
      grid-column:1 / 2;
    }
    /* Right column: the two rosters, stacked, starting level with the
       first row of left-column content (row 3, directly under the pinned
       header) and spanning well past it so the left column can grow
       round over round without the roster wrapping underneath it. */
    .card.draft-layout > .fight-teams{
      grid-column:2 / 3;
      grid-row:3 / span 40;
      margin:6px 0 0;
      padding:14px 14px 12px;
      border:1px solid rgba(244, 240, 232, 0.10);
      background:rgba(0, 0, 0, 0.10);
      border-radius:10px;
      align-self:start;
      position:sticky; top:14px;
    }
    /* Roster panels breathe a bit more in the wide column - they used to
       have to compete with the board directly above them. */
    .card.draft-layout .fight-teams{
      display:flex; flex-direction:column; gap:12px;
    }
    .card.draft-layout .fight-team{
      padding:8px 10px 10px;
      background:rgba(244, 240, 232, 0.04);
      border-radius:8px;
    }
    /* At the wider width, keep the 5-card board a comfortable size so
       cards are readable rather than stretching to fill 550px of column. */
    .card.draft-layout .double-spin-grid[data-choices="5"],
    .card.draft-layout .double-spin-grid[data-choices="4"]{
      max-width:520px; margin:0 auto;
    }
    /* Chips center inside the left column so they don't drift to a wide
       edge and lose their visual grouping. The market chip is listed here
       too: it only renders from round 2 onward, which is why it escaped
       the original placement rules and was easy to miss in a round-1
       screenshot. */
    .card.draft-layout > .auction-opener-chip{
      justify-self:center;
    }
    /* Both round-level actions belong under the board they act on, not
       stretched across the left column. */
    .card.draft-layout > .auto-finish-btn,
    .card.draft-layout > .auction-auto-skip-notice{
      justify-self:center;
    }

    /* Auction Extreme's 9-card board (repeat(3,1fr), same as the 8-card
       mid-round state once a slot locks) was inheriting the same ~460px
       left column every other draft screen uses - a column sized for the
       5-card Standard board and the 2-up spin screens, not the widest
       board in the game. On a real laptop there is plenty of unclaimed
       width past that column (see .auction-extreme-layout toggle in
       render(), 05-screens-fight-and-boot.js): widen the whole card and
       give the board column more of the split so 9 cards read as a market
       you can actually scan, not a cramped grid of tiny tiles. */
    .card.draft-layout.auction-extreme-layout{
      width:min(1180px, 96vw);
      grid-template-columns:minmax(0, 1.55fr) minmax(0, 1fr);
    }
    .card.draft-layout.auction-extreme-layout .double-spin-grid[data-choices="9"],
    .card.draft-layout.auction-extreme-layout .double-spin-grid[data-choices="8"]{
      gap:10px;
    }
    .card.draft-layout.auction-extreme-layout .double-spin-grid[data-choices="9"] .double-spin-card,
    .card.draft-layout.auction-extreme-layout .double-spin-grid[data-choices="8"] .double-spin-card{
      padding:20px 8px 14px; min-height:132px;
    }
    .card.draft-layout.auction-extreme-layout .double-spin-grid[data-choices="9"] .double-spin-card .emoji,
    .card.draft-layout.auction-extreme-layout .double-spin-grid[data-choices="8"] .double-spin-card .emoji{
      font-size:52px;
    }
    .card.draft-layout.auction-extreme-layout .double-spin-grid[data-choices="9"] .double-spin-name,
    .card.draft-layout.auction-extreme-layout .double-spin-grid[data-choices="8"] .double-spin-name{
      font-size:12.5px;
    }
  }

  /* ----------------------------------------------------- in-card main menu */

  /* Present on the setup, draft and battle screens. Sits at the top of the
     card, left of the "Animal Army" stamp, so it reads as "leave this" rather
     than as one of the actions the screen is offering. */
  .card-menu-btn{
    display:inline-block; margin:0 0 6px;
    font-family:var(--font-stamp); font-size:10.5px; font-weight:700;
    letter-spacing:0.08em; text-transform:uppercase;
    color:var(--parchment-dim); background:transparent;
    border:1px solid rgba(243, 236, 224,0.32); border-radius:999px;
    padding:5px 11px; cursor:pointer;
    transition:color 0.12s ease, border-color 0.12s ease, background 0.12s ease;
  }
  .card-menu-btn:hover{ color:var(--parchment); border-color:var(--parchment); background:rgba(243, 236, 224,0.06); }
  .card-menu-btn:focus-visible{ outline:2px solid var(--blaze); outline-offset:2px; }
  /* Comfortable thumb target on a phone; the stamp badge is absolutely
     positioned, so widening this can't collide with it. */
  @media (max-width:560px){
    .card-menu-btn{ padding:7px 13px; font-size:11px; }
  }

  /* ------------------------------------------------------------- guest ask
     The mid-trial invitation for guests (js/auth.js, mounted into the home
     screen by game.js). Deliberately an inline card and not an overlay: it sits
     in the page under the play buttons, so it can be read, ignored or scrolled
     past, and nothing about it stops a battle from starting. */
  .guest-ask{
    margin:14px 0 4px;
    padding:12px 13px 11px;
    border:1.5px solid var(--moss);
    border-radius:10px;
    background:rgba(92,143,58,0.10);
    text-align:left;
  }
  .guest-ask-title{
    display:block;
    font-family:var(--font-stamp); font-weight:800; font-size:12px;
    text-transform:uppercase; letter-spacing:0.05em; color:var(--parchment);
  }
  .guest-ask-note{
    margin:6px 0 0; font-size:12px; line-height:1.45; color:var(--parchment-dim);
  }
  .guest-ask-actions{
    display:flex; align-items:center; gap:8px; flex-wrap:wrap; margin-top:10px;
  }
  .guest-ask-go{
    font-family:var(--font-stamp); font-size:11px; font-weight:800;
    letter-spacing:0.05em; text-transform:uppercase;
    color:#fff; background:var(--moss-dark);
    border:1.5px solid var(--moss-dark); border-radius:14px;
    padding:7px 13px; cursor:pointer;
  }
  .guest-ask-go:hover{ filter:brightness(1.12); }
  .guest-ask-go:focus-visible{ outline:2px solid var(--moss-dark); outline-offset:2px; }
  .guest-ask-later{
    appearance:none; -webkit-appearance:none;
    background:none; border:0; padding:4px 2px; margin:0;
    font-family:inherit; font-size:12px; color:var(--parchment-dim);
    text-decoration:underline; cursor:pointer;
  }
  .guest-ask-later:hover{ color:var(--parchment); }
  .guest-ask-later:focus-visible{ outline:2px solid var(--moss-dark); outline-offset:2px; }

  /* ------------------------------------------------------ add to home screen
     The one-time install offer (js/install.js) and its permanent, quiet
     counterpart in the home-screen footer.

     The banner is pinned to the bottom edge rather than dropped into the page
     so it cannot push the play buttons around when it appears, and it clears
     the iOS home indicator via safe-area insets — on an iPhone that is exactly
     where the Share button the copy points at lives, and a banner sitting on
     top of it would be telling players to tap something it is covering. */
  .install-link{
    appearance:none; -webkit-appearance:none;
    background:none; border:0; padding:0; margin:0;
    font:inherit; color:var(--parchment-dim);
    text-decoration:underline; cursor:pointer;
  }
  .install-link:hover{ color:var(--parchment); }

  .install-banner{
    position:fixed; left:0; right:0; bottom:0; z-index:70;
    display:flex; align-items:center; gap:12px;
    padding:12px 14px;
    padding-bottom:calc(12px + env(safe-area-inset-bottom, 0px));
    background:var(--drawer); border-top:2px solid var(--drawer-edge);
    box-shadow:0 -6px 22px rgba(0,0,0,0.32);
  }
  .install-banner[hidden]{ display:none; }
  .install-banner-icon{
    flex:0 0 auto; width:44px; height:44px; border-radius:9px;
    border:1.5px solid var(--drawer-edge);
  }
  .install-banner-copy{ flex:1 1 auto; min-width:0; display:flex; flex-direction:column; gap:2px; }
  .install-banner-title{
    font-family:var(--font-stamp); font-weight:800; font-size:13px;
    text-transform:uppercase; letter-spacing:0.03em; color:var(--parchment);
    line-height:1.2;
  }
  .install-banner-note{ font-size:11px; color:var(--parchment-dim); line-height:1.35; }
  .install-banner-actions{ flex:0 0 auto; display:flex; align-items:center; gap:8px; }
  .install-banner-dismiss{
    appearance:none; -webkit-appearance:none;
    background:none; border:0; padding:8px 6px;
    font-family:var(--font-stamp); font-weight:700; font-size:11px;
    text-transform:uppercase; letter-spacing:0.04em;
    color:var(--parchment-dim); cursor:pointer;
  }
  .install-banner-dismiss:hover{ color:var(--parchment); }
  /* --blaze-dark clears the 4.5:1 floor the rest of the game holds to
     (8.0:1 against chalk, comfortably above --blaze's own 5.1:1), and
     hover brightens rather than swapping to the lighter purple so the
     hovered state stays above the line too. */
  .install-banner-go{
    appearance:none; -webkit-appearance:none;
    padding:9px 15px; border-radius:4px; cursor:pointer;
    background:var(--blaze-dark); border:1.5px solid var(--blaze-dark);
    font-family:var(--font-stamp); font-weight:800; font-size:12px;
    text-transform:uppercase; letter-spacing:0.04em; color:var(--parchment);
  }
  .install-banner-go:hover{ filter:brightness(1.15); }
  .install-banner-go[hidden]{ display:none; }

  /* On a narrow phone the two buttons and the copy will not sit on one line
     without the title wrapping mid-word, so the actions drop underneath and
     the icon comes out — the title already says what this is. */
  @media (max-width:460px){
    .install-banner{ flex-wrap:wrap; gap:8px 10px; }
    .install-banner-icon{ width:36px; height:36px; }
    .install-banner-actions{ width:100%; justify-content:flex-end; }
  }

  /* ======================================================= ENCYCLOPEDIA === */
  /* Reuses .leaderboard-overlay/-sheet/-head/-title/-close/-tabs/-tab/-note
     and .leaderboard-empty wholesale (same shell as the leaderboard and
     avatar shop) - only what's specific to a search-and-browse index lives
     here: the search box, the filter row's extra width, the letter-grouped
     list, and the detail view. */
  .encyclopedia-sheet{ max-width:480px; }
  .encyclopedia-search-row{ margin:12px 0 4px; display:flex; flex-direction:column; gap:8px; }
  .encyclopedia-search-box{
    display:flex; align-items:center; gap:8px;
    background:rgba(0,0,0,0.22); border:1.5px solid rgba(154,124,77,0.35);
    border-radius:10px; padding:9px 12px;
  }
  .encyclopedia-search-box input{
    flex:1; min-width:0; background:none; border:none; outline:none;
    color:var(--parchment); font-family:var(--font-body); font-size:13.5px;
  }
  .encyclopedia-search-box input::placeholder{ color:var(--parchment-dim); }
  /* -webkit-appearance:none strips Safari's default magnifying-glass icon and
     clear button styling so they don't fight with our own 🔎 and layout. */
  .encyclopedia-search-box input[type="search"]{ -webkit-appearance:none; appearance:none; }
  .encyclopedia-filters{
    flex-wrap:nowrap; overflow-x:auto; margin:0; padding-bottom:2px;
  }
  .encyclopedia-filters .leaderboard-tab{ flex:0 0 auto; }

  /* pan-y: a vertical drag still scrolls the list/detail body normally;
     only a horizontal drag is left free for encyclopedia.js's swipe
     page-flip to claim without the browser also trying to scroll or zoom. */
  .encyclopedia-body{ margin-top:10px; touch-action:pan-y; }
  .encyclopedia-list{ max-height:52vh; overflow-y:auto; margin:0 -20px; }
  .encyclopedia-letter{
    font-family:var(--font-stamp); font-size:10.5px; font-weight:800;
    letter-spacing:0.1em; color:var(--moss-bright);
    background:rgba(0,0,0,0.22); padding:5px 20px; position:sticky; top:0;
  }
  .encyclopedia-row{
    display:flex; align-items:center; gap:12px; cursor:pointer;
    padding:9px 20px; border-bottom:1px solid rgba(255,255,255,0.05);
  }
  .encyclopedia-row:hover, .encyclopedia-row:focus-visible{ background:rgba(255,255,255,0.05); }
  .encyclopedia-row:focus-visible{ outline:2px solid var(--parchment); outline-offset:-2px; }
  .encyclopedia-thumb{
    width:38px; height:38px; border-radius:8px; flex-shrink:0;
    background:rgba(255,255,255,0.07); border:1px solid rgba(154,124,77,0.3);
    display:flex; align-items:center; justify-content:center; overflow:hidden;
  }
  .encyclopedia-thumb-img{ width:100%; height:100%; object-fit:contain; }
  .encyclopedia-thumb-img-glyph{ font-size:19px; }
  .encyclopedia-row-main{ flex:1; min-width:0; }
  .encyclopedia-row-name{
    font-size:13.5px; font-weight:700; display:flex; align-items:center; gap:6px;
    overflow:hidden; text-overflow:ellipsis; white-space:nowrap;
  }
  .encyclopedia-row-group{
    font-family:var(--font-stamp); font-size:9.5px; letter-spacing:0.05em;
    text-transform:uppercase; color:var(--parchment-dim); margin-top:2px;
  }
  .encyclopedia-row-power{
    font-family:var(--font-stamp); font-size:12px; font-weight:800;
    color:var(--blaze-bright); text-align:right; flex-shrink:0;
  }
  .encyclopedia-row-power small{
    display:block; font-size:8px; font-weight:700; letter-spacing:0.06em;
    color:var(--parchment-dim); text-transform:uppercase; margin-top:1px;
  }
  /* A small badge, not a full second label: reused identically on a list row
     and inside the detail art frame, so one class covers both spots. */
  .prehistoric-tag{
    font-family:var(--font-stamp); font-size:8.5px; font-weight:800;
    letter-spacing:0.04em; color:var(--brass-bright); background:rgba(245,179,1,0.14);
    border:1px solid rgba(245,179,1,0.4); border-radius:4px; padding:1px 4px;
    text-transform:uppercase; white-space:nowrap;
  }

  .encyclopedia-back{
    appearance:none; -webkit-appearance:none; background:none; border:none;
    cursor:pointer; padding:0 0 12px;
    font-family:var(--font-stamp); font-size:11px; font-weight:800;
    letter-spacing:0.05em; text-transform:uppercase; color:var(--parchment-dim);
  }
  .encyclopedia-back:hover{ color:var(--parchment); }

  /* Prev/next page-flip header. Two roughly-equal buttons flanking the arrow
     glyph closest to the edge, so "Next" always reads left-to-right toward
     the direction it moves (arrow, then name, mirrored on the prev side). */
  .encyclopedia-nav{
    display:flex; align-items:stretch; gap:8px; margin:0 0 12px;
  }
  .encyclopedia-nav-btn{
    appearance:none; -webkit-appearance:none; cursor:pointer; flex:1 1 0; min-width:0;
    display:flex; align-items:center; gap:6px; padding:8px 10px;
    background:rgba(0,0,0,0.18); border:1px solid rgba(154,124,77,0.3); border-radius:10px;
    font-family:var(--font-stamp); color:var(--parchment-dim); text-align:left;
  }
  .encyclopedia-nav-next{ text-align:right; flex-direction:row-reverse; }
  .encyclopedia-nav-btn:hover, .encyclopedia-nav-btn:focus-visible{
    color:var(--parchment); border-color:rgba(154,124,77,0.55); background:rgba(0,0,0,0.28);
  }
  .encyclopedia-nav-btn:focus-visible{ outline:2px solid var(--parchment); outline-offset:-2px; }
  .encyclopedia-nav-arrow{
    font-size:15px; line-height:1; flex:0 0 auto; color:var(--brass-bright);
  }
  .encyclopedia-nav-name{
    font-size:10.5px; font-weight:800; letter-spacing:0.02em;
    overflow:hidden; text-overflow:ellipsis; white-space:nowrap;
  }

  /* Page-flip transition: content enters from the side it's paging toward
     (right-to-left for Next, left-to-right for Prev) with a quick fade,
     200ms total to stay snappy across a whole Mythical read-through. */
  .encyclopedia-enter-next{ animation:encyclopediaEnterNext 0.2s ease-out; }
  .encyclopedia-enter-prev{ animation:encyclopediaEnterPrev 0.2s ease-out; }
  @keyframes encyclopediaEnterNext{
    from{ opacity:0; transform:translateX(18px); }
    to{ opacity:1; transform:translateX(0); }
  }
  @keyframes encyclopediaEnterPrev{
    from{ opacity:0; transform:translateX(-18px); }
    to{ opacity:1; transform:translateX(0); }
  }
  @media (prefers-reduced-motion: reduce){
    .encyclopedia-enter-next, .encyclopedia-enter-prev{ animation:none; }
  }

  .encyclopedia-art{
    aspect-ratio:16/10; border-radius:12px; position:relative;
    background:radial-gradient(ellipse at 50% 30%, rgba(255,255,255,0.09), transparent 70%), rgba(0,0,0,0.2);
    border:1px solid rgba(154,124,77,0.3);
    display:flex; align-items:center; justify-content:center; overflow:hidden;
  }
  .encyclopedia-art-img{ width:70%; height:70%; object-fit:contain; }
  .encyclopedia-art-img-glyph{ font-size:76px; }
  .encyclopedia-art .prehistoric-tag{ position:absolute; top:10px; right:10px; }
  .encyclopedia-detail-title{ margin-top:14px; }
  .encyclopedia-detail-name{
    font-family:var(--font-stamp); font-size:19px; font-weight:800; margin:0;
  }
  .encyclopedia-detail-group{
    font-family:var(--font-stamp); font-size:10.5px; letter-spacing:0.08em;
    text-transform:uppercase; color:var(--moss-bright); margin-top:4px;
  }
  .encyclopedia-description{
    margin:10px 0 0; font-size:13.5px; line-height:1.55; color:var(--parchment-dim);
  }
  .encyclopedia-stats{
    display:grid; grid-template-columns:repeat(3,1fr); gap:8px; margin-top:14px;
  }
  .encyclopedia-stat-box{
    background:rgba(0,0,0,0.2); border:1px solid rgba(154,124,77,0.25);
    border-radius:10px; padding:10px 6px; text-align:center;
  }
  .encyclopedia-stat-v{
    font-family:var(--font-stamp); font-size:15px; font-weight:800;
    color:var(--blaze-bright); font-variant-numeric:tabular-nums;
  }
  .encyclopedia-stat-l{
    font-family:var(--font-stamp); font-size:8.5px; letter-spacing:0.06em;
    text-transform:uppercase; color:var(--parchment-dim); margin-top:3px;
  }
  .encyclopedia-traits{ display:flex; flex-wrap:wrap; gap:6px; margin-top:14px; }
  /* Kind maps straight to encyclopediaTraitsForAnimal()'s classification in
     game.js: bonus/resistant are a real edge this animal has (pine),
     weakness/vulnerable are a real edge something else has on it (stamp),
     situational fires both ways depending on who's across the ring (amber). */
  .trait-chip{
    font-family:var(--font-stamp); font-size:9.5px; font-weight:800;
    letter-spacing:0.03em; text-transform:uppercase; padding:5px 10px;
    border-radius:999px; cursor:default;
  }
  .trait-chip-bonus, .trait-chip-resistant{
    background:color-mix(in srgb, var(--moss) 22%, var(--drawer));
    border:1px solid var(--moss); color:var(--moss-bright);
  }
  .trait-chip-weakness, .trait-chip-vulnerable{
    background:color-mix(in srgb, var(--blaze) 22%, var(--drawer));
    border:1px solid var(--blaze); color:var(--blaze-bright);
  }
  .trait-chip-situational{
    background:color-mix(in srgb, var(--brass-bright) 18%, var(--drawer));
    border:1px solid var(--brass-bright); color:var(--brass-bright);
  }
  .encyclopedia-blurb{
    margin-top:14px; padding:12px 14px; border-radius:10px;
    background:rgba(0,0,0,0.16); border:1px solid rgba(154,124,77,0.2);
    font-size:13.5px; line-height:1.55; color:var(--parchment);
  }
  .encyclopedia-blurb-text{ margin:0; }
  .encyclopedia-blurb-label{
    font-family:var(--font-stamp); font-size:9.5px; letter-spacing:0.08em;
    text-transform:uppercase; color:var(--parchment-dim); display:block; margin-bottom:6px;
  }

  @media (max-width:560px){
    .encyclopedia-list{ margin:0 -14px; }
    .encyclopedia-letter, .encyclopedia-row{ padding-left:14px; padding-right:14px; }
  }

  /* ------------------------------------------------------------------
     Home-declutter block (Today chips, popover overlay, week-leaders
     chip + overlay, More menu, sidebar accordion). Appended in a single
     block so the diff against the prior file stays localized. Shares
     the .rules-overlay / .rules-sheet pattern already used elsewhere:
     dim backdrop, bottom-sheet, subtle slide-up on open.
     ------------------------------------------------------------------ */

  /* Today row: two chips on one line, sitting above the deck. */
  .home-today-row{
    display:flex;
    flex-direction:column;
    align-items:stretch;
    gap:10px;
    margin:8px 0 16px;
  }
  .home-today-row-label{
    margin:0; font-weight:inherit;
    font-family:var(--font-stamp,inherit);
    text-transform:uppercase;
    letter-spacing:0.08em;
    font-size:12px;
    color:var(--parchment-dim,#a99a80);
    padding:0 2px;
  }
  .home-today-row-chips{
    display:grid;
    /* Three big orange CTAs across on wide screens; wrap to one column
       on narrow phones so each tap target stays comfortably large. */
    grid-template-columns:repeat(3, minmax(0, 1fr));
    gap:10px;
    min-width:0;
  }
  @media (max-width: 640px){
    .home-today-row-chips{ grid-template-columns:1fr; }
  }
  /* Big orange Today buttons: the three daily-cadence entries are the
     first thing on the home screen, sized like a mode tile head and
     colored with the --blaze palette so they clearly outweigh the
     anytime-mode deck below. Kept as .home-today-chip so existing test
     selectors and popover wiring still resolve. */
  .home-today-chip{
    display:flex;
    flex-direction:column;
    align-items:flex-start;
    justify-content:center;
    gap:6px;
    padding:14px 18px;
    background:linear-gradient(180deg, var(--blaze-bright,#f07a2e) 0%, var(--blaze,#d3560f) 100%);
    color:#fff;
    border:1px solid var(--blaze-dark,#8f3405);
    border-radius:12px;
    box-shadow:0 2px 0 var(--blaze-dark,#8f3405), 0 4px 12px rgba(211,86,15,0.28);
    font-family:inherit;
    font-size:15px;
    text-align:left;
    cursor:pointer;
    transition:transform 0.06s ease, box-shadow 0.15s ease, filter 0.15s ease;
    min-height:64px;
    position:relative;
  }
  .home-today-chip:hover{ filter:brightness(1.06); }
  .home-today-chip:active{
    transform:translateY(1px);
    box-shadow:0 1px 0 var(--blaze-dark,#8f3405), 0 2px 6px rgba(211,86,15,0.28);
  }
  .home-today-chip:focus-visible{
    outline:2px solid var(--parchment,#eadfc9);
    outline-offset:2px;
  }
  .home-today-chip-label{
    font-family:var(--font-display,inherit);
    font-weight:400;
    font-size:19px;
    letter-spacing:0.02em;
    text-transform:uppercase;
    line-height:1.05;
    color:#fff;
    /* Faint stamp so the white label reads on the brighter top of the
       gradient. */
    text-shadow:0 1px 0 rgba(0,0,0,0.28);
  }
  .home-today-chip-status{
    background:rgba(0,0,0,0.22);
    padding:3px 10px;
    border-radius:999px;
    font-size:12px;
    color:#fff;
    font-weight:600;
  }
  .home-today-chip-risk{
    font-size:14px;
    animation:homeTodayChipPulse 1.8s ease-in-out infinite;
  }
  @keyframes homeTodayChipPulse{
    0%,100%{ transform:scale(1); }
    50%{ transform:scale(1.15); }
  }
  @media (prefers-reduced-motion: reduce){
    .home-today-chip-risk{ animation:none; }
  }

  /* Week Leaders chip + overlay: a native [popover] bottom sheet. */
  .week-leaders-chip{
    display:flex;
    align-items:center;
    justify-content:space-between;
    width:100%;
    padding:10px 14px;
    margin:8px 0;
    background:var(--drawer,#231913);
    color:var(--parchment,#eadfc9);
    border:1px solid var(--drawer-edge,#4a382c);
    border-radius:10px;
    font-family:inherit;
    font-size:14px;
    cursor:pointer;
    transition:background 0.15s ease;
  }
  .week-leaders-chip:hover{ background:var(--drawer-hover,#2c1f18); }
  .week-leaders-chip-label{ font-weight:600; }
  .week-leaders-chip-hint{
    font-size:12px;
    color:var(--parchment-dim,#a99a80);
  }
  /* Native [popover] element, same reasoning (and same transition +
     @starting-style entry/exit animation) as .home-today-sheet above -
     no wrapper div, dim wash via ::backdrop, light-dismiss/Esc/top-layer
     for free. */
  .week-leaders-sheet{
    position:fixed;
    inset:auto 0 0 0;
    margin:0 auto;
    background:
      linear-gradient(180deg, rgba(255,255,255,0.06), transparent 40%),
      var(--drawer,#231913);
    border:1px solid var(--drawer-edge,#4a382c);
    border-bottom:none;
    color:var(--parchment,#eadfc9);
    width:100%;
    max-width:520px;
    max-height:85vh;
    border-radius:14px 14px 0 0;
    padding:20px 22px 26px;
    overflow-y:auto;
    box-shadow:0 -10px 34px var(--drawer-shadow,rgba(0,0,0,0.4));
    opacity:0;
    transform:translateY(28px);
    transition:
      opacity 0.28s ease-out, transform 0.28s ease-out,
      display 0.28s allow-discrete, overlay 0.28s allow-discrete;
  }
  .week-leaders-sheet:popover-open{
    opacity:1;
    transform:translateY(0);
  }
  @starting-style{
    .week-leaders-sheet:popover-open{
      opacity:0;
      transform:translateY(28px);
    }
  }
  .week-leaders-sheet::backdrop{
    background:rgba(10,6,3,0);
    transition:
      background-color 0.28s ease-out,
      display 0.28s allow-discrete, overlay 0.28s allow-discrete;
  }
  .week-leaders-sheet:popover-open::backdrop{
    background:rgba(10,6,3,0.55);
  }
  @starting-style{
    .week-leaders-sheet:popover-open::backdrop{
      background:rgba(10,6,3,0);
    }
  }
  @media (prefers-reduced-motion: reduce){
    .week-leaders-sheet{ transform:none; transition-duration:0.1s; }
    @starting-style{
      .week-leaders-sheet:popover-open{ transform:none; }
    }
  }
  .week-leaders-sheet-head{
    display:flex;
    align-items:center;
    justify-content:space-between;
    margin-bottom:12px;
  }
  .week-leaders-sheet-title{
    font-family:var(--font-stamp,inherit);
    font-weight:800;
    text-transform:uppercase;
    letter-spacing:0.06em;
    font-size:15px;
    color:var(--parchment,#eadfc9);
  }
  .week-leaders-sheet-close{
    background:none;
    border:none;
    font-size:22px;
    line-height:1;
    cursor:pointer;
    color:var(--parchment-dim,#a99a80);
    padding:2px 6px;
  }
  .week-leaders-sheet-close:hover{ color:var(--parchment,#eadfc9); }

  /* Home "More" menu: tiny inline button + panel. */
  .home-more{
    position:relative;
    margin:12px 0 4px;
    text-align:center;
  }
  .home-more-btn{
    background:none;
    border:1px solid var(--drawer-edge,#4a382c);
    color:var(--parchment-dim,#a99a80);
    padding:8px 14px; min-height:28px;
    border-radius:999px;
    font-family:inherit;
    font-size:12px;
    cursor:pointer;
    letter-spacing:0.06em;
    text-transform:uppercase;
  }
  .home-more-btn:hover{ color:var(--parchment,#eadfc9); border-color:var(--parchment-dim,#a99a80); }
  .home-more-panel{
    position:absolute;
    left:50%;
    transform:translateX(-50%);
    bottom:calc(100% + 6px);
    display:flex;
    flex-direction:column;
    gap:8px;
    padding:12px 16px;
    min-width:180px;
    background:var(--drawer,#231913);
    border:1px solid var(--drawer-edge,#4a382c);
    border-radius:10px;
    box-shadow:0 4px 16px rgba(0,0,0,0.4);
    z-index:100;
  }
  .home-more-panel[hidden]{ display:none; }
  .home-more-panel a{
    color:var(--parchment,#eadfc9);
    text-decoration:none;
    font-size:13px;
    padding:2px 0;
  }
  .home-more-panel a:hover{ text-decoration:underline; }

  /* ================================================================
     Home: Today tiles that act directly, the big Roll card, portraits
     on every card, the masthead hero. Fight: the picker in the slot.
     Daily setup: the seal card.
     ================================================================ */
  .masthead{ position:relative; }
  .masthead-hero{
    position:absolute; right:calc(50% - 345px); top:-42px;
    width:160px; height:160px; pointer-events:none; z-index:-1;
    -webkit-mask-image: radial-gradient(circle at 50% 45%, #000 45%, transparent 72%);
            mask-image: radial-gradient(circle at 50% 45%, #000 45%, transparent 72%);
    opacity:0.92;
  }

  @media (max-width:760px){ .masthead-hero{ right:-8px; top:26px; width:84px; height:84px; } }
  @media (max-width:360px){ .masthead-hero{ display:none; } }

  .mode-cta{ position:relative; overflow:hidden; }

  .home-today-chip{ overflow:hidden; }
  .home-today-chip-art{
    position:absolute; right:-10px; bottom:-12px;
    width:96px; height:96px; object-fit:contain; object-position:right bottom;
    opacity:0.5; pointer-events:none;
    -webkit-mask-image: linear-gradient(90deg, transparent 0%, #000 40%);
            mask-image: linear-gradient(90deg, transparent 0%, #000 40%);
    filter: drop-shadow(0 6px 10px rgba(0,0,0,0.45));
  }
  .home-today-chip > .home-today-chip-label, .home-today-chip > .home-today-chip-meta, .home-today-chip > .home-today-chip-foot{ position:relative; z-index:1; }
  .home-today-chip-meta{
    font-family:var(--font-legible); font-size:12.5px; line-height:1.35; color:rgba(255,255,255,0.88);
    padding-right:60px;
  }
  .home-today-chip-foot{ display:flex; align-items:center; gap:8px; width:100%; margin-top:2px; }

  .home-today-chip.is-done{ filter:saturate(0.55) brightness(0.92); }
  .home-today-chip.is-done .home-today-chip-status{ background:rgba(0,0,0,0.35); }

  .daily-seal-lede{ text-align:center; margin:8px 0 14px; }
  .daily-seal{
    position:relative; overflow:hidden;
    padding:14px 16px; border-radius:10px;
    background:rgba(0,0,0,0.26); border:1px solid var(--drawer-edge);
  }
  .daily-seal-art{
    position:absolute; right:-10px; top:-6px; width:120px; height:120px; object-fit:contain;
    opacity:0.35; pointer-events:none;
    -webkit-mask-image: linear-gradient(90deg, transparent 0%, #000 45%);
            mask-image: linear-gradient(90deg, transparent 0%, #000 45%);
  }
  .daily-seal-row{
    position:relative; z-index:1;
    display:flex; justify-content:space-between; gap:12px; padding:6px 0;
    border-top:1px solid rgba(255,255,255,0.08);
    font-family:var(--font-body); font-size:13.5px;
  }
  .daily-seal-row:first-of-type{ border-top:0; }
  .daily-seal-key{ font-family:var(--font-stamp); font-size:11px; letter-spacing:0.08em; text-transform:uppercase; color:var(--parchment-dim); }
  .daily-seal-val{ color:var(--parchment); text-align:right; }
  .daily-seal-desc{ position:relative; z-index:1; margin:10px 0 0; font-size:12.5px; color:var(--parchment-dim); }

  .fight-reveal-ghost{ opacity:0.22; filter:grayscale(1); }
  .fight-picker-title{ margin-bottom:6px; }
  .fight-picker{ width:100%; text-align:left; }
  @media (max-width:640px){
    .fight-picker{
      display:flex; overflow-x:auto; gap:8px; padding:2px 2px 8px;
      scroll-snap-type:x mandatory; -webkit-overflow-scrolling:touch;
    }
    .fight-picker > .opponent-btn{ flex:0 0 72%; scroll-snap-align:start; }
  }

  /* Three-up on a laptop: the tiles were half the width of a phone's and
     twice as tall. Smaller label, a two-line cap on the meta, smaller art. */
  /* The card is 460px at every width, so three columns of Today tiles
     were ~200px each on a laptop - narrower than a phone's. One tile per
     row everywhere; the tile carries a meta line and a portrait now, and
     needs the width. */
  .home-today-row-chips{ grid-template-columns:1fr; }
  @media (min-width:641px){ .home-today-row-chips{ grid-template-columns:1fr; } }

  /* ================================================================
  /*      Fight loop: own Fight Power on roster and picker, matchup preview,
     enemy bench, banked swap, upset callouts, rival card, double duty.
     ================================================================ */
  .fight-power-tag{
    display:inline-block; margin-left:4px; padding:1px 6px; border-radius:99px;
    font-family:var(--font-stamp); font-size:10.5px; font-weight:800; letter-spacing:0.03em;
    color:var(--brass-bright); background:rgba(201,178,107,0.14);
  }
  .fight-matchup{ display:flex; flex-wrap:wrap; gap:4px; margin-top:6px; }
  .fight-matchup-edge, .fight-matchup-rule{
    display:inline-block; padding:2px 7px; border-radius:4px;
    font-family:var(--font-stamp); font-size:10px; font-weight:800; letter-spacing:0.04em; text-transform:uppercase;
    background:rgba(0,0,0,0.28); color:var(--parchment-dim);
  }
  .fight-matchup-edge.good, .fight-matchup-rule.good{ color:#c9f2d2; background:rgba(123,216,143,0.16); }
  .fight-matchup-edge.bad, .fight-matchup-rule.bad{ color:#ffc7ba; background:rgba(255,138,118,0.14); }
  .fight-matchup-edge{ box-shadow: inset 0 0 0 1px currentColor; }
  .fight-bench{
    margin-top:6px; font-family:var(--font-legible); font-size:12px; line-height:1.5;
    color:var(--parchment-dim); text-align:center;
  }
  .fight-bench .animal-art{ width:16px; height:16px; vertical-align:-3px; }
  .fight-swap-btn{
    display:block; margin:8px auto 0; padding:6px 12px; border-radius:6px;
    border:0; background:rgba(0,0,0,0.28); box-shadow: inset 0 0 0 1px var(--brass);
    color:var(--parchment); cursor:pointer;
    font-family:var(--font-stamp); font-size:11.5px; font-weight:800; letter-spacing:0.04em; text-transform:uppercase;
  }
  .fight-swap-note{ display:block; font-family:var(--font-legible); font-size:10.5px; font-weight:400; text-transform:none; letter-spacing:0; color:var(--parchment-dim); }
  .gauntlet-upset-stencil{
    position:absolute; left:50%; top:46%; transform:translate(-50%,-50%) rotate(-8deg);
    padding:4px 14px; z-index:5; pointer-events:none;
    font-family:var(--font-display); font-size:30px; letter-spacing:0.08em;
    color:var(--blaze-bright); border:3px solid var(--blaze-bright); border-radius:6px;
    background:rgba(20,26,16,0.7); text-shadow:0 2px 8px rgba(0,0,0,0.6);
  }
  .gauntlet-log-upset, .ov-log-upset{ color:var(--blaze-bright); }
  .ov-upset-tag{ font-family:var(--font-stamp); font-size:11px; letter-spacing:0.08em; color:var(--blaze-bright); }
  .ov-upsets, .ov-first-win{
    margin:6px 0 0; text-align:center;
    font-family:var(--font-stamp); font-size:12px; font-weight:800; letter-spacing:0.04em;
    color:var(--brass-bright);
  }
  .home-today-row-head{ display:flex; justify-content:space-between; align-items:center; gap:8px; padding:0 2px; }
  .home-today-duty{
    font-family:var(--font-stamp); font-size:11px; font-weight:800; letter-spacing:0.04em;
    color:var(--brass-bright);
  }
  .home-today-duty.is-done{ color:#7bd88f; }
  /* The rival card is a Today chip in the grudge colours. */
  .home-today-chip.home-rival{
    background:rgba(0,0,0,0.32); color:var(--parchment);
    border:1px solid var(--blaze-dark); box-shadow: inset 0 0 0 1px rgba(240,122,46,0.3);
  }
  .home-today-chip.home-rival .home-today-chip-label{ color:var(--blaze-bright); }
  .home-today-chip.home-rival .home-today-chip-meta{ color:var(--parchment-dim); }
  .home-today-chip.home-rival .home-today-chip-status{ background:rgba(211,86,15,0.22); color:var(--blaze-bright); }
  /*      Home declutter: the four dailies (Daily Challenge, Daily Intel,
     Field ID, Predator Puzzle) as compact chips - portrait as a
     thumbnail on the left, one meta line, status on the right - and a
     two-tile deck (Roll, Auction; Mythical is a roster pill in Auction).
     ================================================================ */
  .home-today-row{ gap:6px; margin:4px 0 12px; }
  .home-today-row-chips{ display:grid; grid-template-columns:1fr; gap:6px; }
  .home-today-chip{
    display:grid; grid-template-columns:36px minmax(0,1fr) auto; grid-template-rows:auto auto;
    align-items:center; column-gap:10px; row-gap:1px;
    padding:6px 10px 6px 6px; min-height:48px; border-radius:10px;
    box-shadow:0 1px 0 var(--blaze-dark,#8f3405);
  }
  .home-today-chip-art{
    position:static; grid-column:1; grid-row:1 / span 2; width:36px; height:36px; opacity:1;
    -webkit-mask-image:none; mask-image:none; filter:none; object-position:center;
    border-radius:8px; background:rgba(0,0,0,0.22);
  }
  .home-today-chip-label{ grid-column:2; grid-row:1; font-size:13px; line-height:1.1; }
  .home-today-chip-meta{
    grid-column:2; grid-row:2; padding-right:0; font-size:11.5px; line-height:1.25;
    white-space:nowrap; overflow:hidden; text-overflow:ellipsis;
  }
  .home-today-chip-foot{ grid-column:3; grid-row:1 / span 2; width:auto; margin:0; gap:6px; }
  .home-today-chip-status{ font-size:11px; padding:2px 8px; white-space:nowrap; }

  /* ================================================================
     Meta loops: NEW roster stamp, scout tokens, draft grade, level
     rewards, weekly missions.
     ================================================================ */
  .roster-new-tag{
    display:inline-block; margin-left:4px; padding:0 5px; border-radius:3px;
    font-family:var(--font-stamp); font-size:9px; font-weight:800; letter-spacing:0.08em;
    background:var(--blaze); color:var(--parchment);
  }
  .scout-bar{
    margin:6px 0 10px; padding:8px 10px; border-radius:8px;
    background:rgba(0,0,0,0.26); border:1px dashed var(--brass);
    display:flex; flex-direction:column; gap:4px; align-items:center; text-align:center;
  }
  .scout-bar.is-used{ border-style:solid; font-family:var(--font-stamp); font-size:11.5px; color:var(--brass-bright); }
  .scout-btn{
    padding:6px 12px; border:0; border-radius:6px; cursor:pointer;
    background:rgba(201,178,107,0.16); color:var(--brass-bright);
    font-family:var(--font-stamp); font-size:11.5px; font-weight:800; letter-spacing:0.04em; text-transform:uppercase;
  }
  .scout-count{ margin-left:6px; padding:0 6px; border-radius:99px; background:rgba(0,0,0,0.3); }
  .scout-note{ font-family:var(--font-legible); font-size:11px; color:var(--parchment-dim); }
  .draft-grade-line{
    margin:0 0 8px; text-align:center;
    font-family:var(--font-stamp); font-size:12px; font-weight:800; letter-spacing:0.04em; color:var(--parchment-dim);
  }

  .level-title{ color:var(--brass-bright); }
  .home-missions{
    margin:0 0 14px; padding:8px 12px 6px;
    background:rgba(0,0,0,0.22); border:1px solid var(--drawer-edge); border-radius:10px;
  }
  .home-missions-head{ display:flex; justify-content:space-between; align-items:center; gap:8px; margin-bottom:4px; }
  .home-missions-count{ font-family:var(--font-stamp); font-size:10.5px; letter-spacing:0.04em; color:var(--parchment-dim); }
  .home-mission{
    display:grid; grid-template-columns:18px 1fr auto; align-items:center; gap:8px;
    padding:6px 0; border-top:1px solid rgba(255,255,255,0.06);
    font-family:var(--font-legible); font-size:13px; color:var(--parchment);
  }
  .home-mission:first-of-type{ border-top:0; }
  .home-mission-check{
    width:18px; height:18px; border-radius:5px; box-shadow: inset 0 0 0 1.5px var(--drawer-edge);
    display:inline-flex; align-items:center; justify-content:center; font-size:12px; font-weight:800; color:#0f1a12;
  }
  .home-mission.is-done .home-mission-check{ background:#7bd88f; box-shadow:none; }
  .home-mission.is-done .home-mission-text{ color:var(--parchment-dim); text-decoration:line-through; }
  .home-mission-progress{ display:flex; align-items:center; gap:6px; font-family:var(--font-stamp); font-size:11px; color:var(--parchment-dim); white-space:nowrap; }
  .home-mission-bar{ display:inline-block; width:54px; height:6px; border-radius:99px; background:rgba(255,255,255,0.12); overflow:hidden; }
  .home-mission-fill{ display:block; height:100%; background:var(--brass-bright); }

  /* ================================================================
     Theme redesign (2026-09): vibrant one-window home, Progress drawer,
     sidebar-free game screens, one accent per mode, hover/press motion.
     Appended last on purpose so equal-specificity rules here win.
     ================================================================ */
  .masthead{ margin-bottom:14px; }
  .masthead .kicker{ font-family:var(--font-body); font-weight:700; font-size:11px; letter-spacing:0.18em; }
  .card{ border-radius:18px; box-shadow:0 18px 40px var(--drawer-shadow); animation:settle 0.28s ease; }
  .card.home-card{ width:min(640px, 100%); padding:22px 22px 20px; }
  @media (max-width:560px){ .card.home-card{ padding:16px 14px 14px; } }
  .start-btn, .primary-btn{ border-radius:12px; border-color:var(--blaze); background:var(--blaze); color:var(--cabinet); font-size:16px; }
  .start-btn:hover, .primary-btn:hover{ background:var(--blaze-bright); border-color:var(--blaze-bright); }

  /* ---- Header: pills read as icon buttons below laptop width ---- */
  .leaderboard-btn, .how-to-play-fab{ background:rgba(9,21,16,0.62); border-color:rgba(246,234,208,0.28); font-weight:800; }
  @media (max-width:900px){
    .top-actions > .how-to-play-fab, .top-actions > .leaderboard-btn{ padding:7px 10px; gap:0; }
    .top-actions .top-action-label{
      position:absolute; width:1px; height:1px; padding:0; margin:-1px;
      overflow:hidden; clip:rect(0 0 0 0); clip-path:inset(50%); white-space:nowrap; border:0;
    }
    .top-actions .top-action-icon{ font-size:18px; }
  }

  /* ---- Home ---- */
  .home{ display:flex; flex-direction:column; gap:12px; }
  .home-section-label{
    font-family:var(--font-body); font-weight:800; font-size:11px; letter-spacing:0.14em;
    text-transform:uppercase; color:var(--parchment-dim); padding:0 2px;
  }

  /* The other dailies: compact quiet tiles with their mode color on the edge. */
  .home-today-row{ margin:0; gap:6px; }
  .home-today-row-chips{ display:grid; grid-template-columns:repeat(auto-fit, minmax(150px, 1fr)); gap:6px; }

  .home-today-chip[data-today-chip="trivia"]{ --mode-accent:var(--mode-intel); }
  .home-today-chip[data-today-chip="fieldid"]{ --mode-accent:var(--mode-fieldid); }
  .home-today-chip[data-today-chip="puzzle"]{ --mode-accent:var(--mode-puzzle); }
  .home-today-chip.home-rival{ --mode-accent:var(--brass-bright); }
  .home-today-chip.home-rival .home-today-chip-label{ color:var(--brass-bright); }
  .home-today-chip.is-done{ filter:none; opacity:0.72; }

  /* ---- Two-door landing (2026-09) ----
     Home opens with two big poster buttons - Knowledge and Battle -
     instead of the full mode deck. Tapping a door sets homeDoor in JS,
     which paints .home-drawer + .home-drawer-scrim on top of the
     landing. Layout: side-by-side on wide screens, stacked on phones.
     The doors share a body-face stencil title, a soft glowing icon,
     a subtitle listing what lives inside, and an accent border that
     picks up each door's theme color. */
  .home-doors{
    display:grid; grid-template-columns:1fr 1fr; gap:14px;
    margin:4px 0 4px;
    position:relative;
  }
  @media (max-width:640px){
    .home-doors{ grid-template-columns:1fr; gap:12px; }
  }
  /* Poster doors: full-bleed illustrated plate as the background, a
     bottom-anchored dark scrim carrying the subtitle so the plate art
     stays legible while the sub-list stays readable at any size.
     The KNOWLEDGE / BATTLE title is baked into the plate art itself
     (see assets/home/door-*.webp), so the visible title is a
     screen-reader-only landmark - no duplicate text on top of the ink. */
  .home-door{
    position:relative; display:block;
    padding:0; min-height:200px;
    border-radius:16px; cursor:pointer; text-align:left;
    color:var(--parchment); font-family:inherit;
    /* Matches the deep-green field baked into the plate art so the
       object-fit:contain letterbox merges seamlessly with the tile. */
    background:#1a3327;
    /* Solid orange outline on both doors so the pair reads as one
       coordinated CTA family regardless of per-door accent color. */
    border:2px solid var(--blaze);
    box-shadow:
      0 10px 28px rgba(0,0,0,0.42);
    transition: translate 0.2s ease, scale 0.14s ease, box-shadow 0.2s ease, filter 0.2s ease;
    overflow:hidden;
    isolation:isolate;
    transform-origin:left center;
  }
  /* Accent glow behind the hero. The art is transparent now, so the
     tile colour shows through around it and the glow reads as light
     coming off the animal. */
  .home-door::before{
    content:""; position:absolute; left:50%; top:56%; width:78%; aspect-ratio:1;
    translate:-50% -50%; border-radius:50%; z-index:0; pointer-events:none;
    background:radial-gradient(circle, color-mix(in srgb, var(--door-accent) 42%, transparent) 0%, transparent 68%);
  }
  @media (min-width:641px){
    .home-door{ min-height:280px; }
  }
  .home-door-knowledge{ --door-accent:var(--mode-intel); }
  .home-door-battle{ --door-accent:var(--blaze); }

  @keyframes doorArtIdle {
    0%,100% { transform: translateY(2px); }
    50%     { transform: translateY(-4px) scale(1.015); }
  }

  @keyframes boltFlicker{
    0%, 100%{ opacity:0; }
    58%{ opacity:1; } 59%{ opacity:0; } 60.5%{ opacity:0.75; } 62%{ opacity:0; }
    66%{ opacity:1; } 66.8%{ opacity:0.2; } 67.5%{ opacity:1; } 69%{ opacity:0; }
  }

  .home-door:hover{
    transform:perspective(900px) rotateY(-6deg);
    border-color:var(--blaze-bright);
    box-shadow:
      14px 12px 30px rgba(0,0,0,0.5),
      0 0 24px color-mix(in srgb, var(--door-accent) 32%, transparent);
  }

  .home-door:active{ transform:perspective(900px) rotateY(-11deg); }
  .home-door:focus-visible{ outline:2px solid var(--parchment); outline-offset:3px; }
  @media (prefers-reduced-motion:reduce){
    .home-door{ transition-duration:0.08s; }
    .home-door:hover, .home-door:active{ transform:none; }

  }

  /* Slide-in drawer overlay + scrim. Not the popover-based Progress drawer
     just below - this one is state-driven from JS (homeDoor) so it can
     hold live tile content (Daily Intel, Field ID, Roll/Auction) that
     re-renders when the user makes a selection inside it. Slides in from
     the right on desktop, from the bottom on phones, matching how sheets
     already read on those form factors. */
  .home-drawer-scrim{
    position:fixed; inset:0; background:rgba(5,12,8,0.62);
    z-index:60; animation: aa-scrim-in 0.18s ease-out;
  }
  @keyframes aa-scrim-in{ from{ opacity:0; } to{ opacity:1; } }
  .home-drawer{
    position:fixed;
    inset:0 0 0 auto; width:min(520px, 100vw); height:100dvh;
    z-index:61;
    display:flex; flex-direction:column;
    background:var(--drawer); color:var(--parchment);
    border-left:1px solid var(--drawer-edge);
    box-shadow:-18px 0 44px rgba(0,0,0,0.45);
    animation: aa-drawer-in-right 0.22s ease-out;
  }
  @keyframes aa-drawer-in-right{ from{ translate:100% 0; } to{ translate:0 0; } }
  /* The drawer is rendered inside #card, and a transformed ancestor becomes
     the containing block of anything position:fixed. .card's settle entrance
     animates transform, so while it ran (a home load with a door already
     open, e.g. ?door=battle or a reload) the drawer was laid out against the
     card and hung ~120px off the right edge of a desktop viewport. A card
     hosting a drawer fades in without moving. */
  .card.home-card:has(.home-drawer){ animation-name:settle-fade; }
  @keyframes settle-fade{ from{ opacity:0; } to{ opacity:1; } }
  @media (max-width:640px){
    .home-drawer{
      inset:auto 0 0 0; width:100%; height:min(92dvh, 720px);
      border-left:0; border-top:1px solid var(--drawer-edge);
      border-radius:18px 18px 0 0;
      box-shadow:0 -18px 44px rgba(0,0,0,0.45);
      animation: aa-drawer-in-bottom 0.22s ease-out;
    }
    @keyframes aa-drawer-in-bottom{ from{ translate:0 100%; } to{ translate:0 0; } }
  }
  @media (prefers-reduced-motion:reduce){
    .home-drawer, .home-drawer-scrim{ animation-duration:0.08s; }
  }
  .home-drawer-head{
    display:grid; grid-template-columns:auto minmax(0,1fr) auto;
    align-items:center; gap:10px;
    padding:14px 16px calc(12px + max(env(safe-area-inset-top, 0px), 0px));
    padding-top:calc(14px + env(safe-area-inset-top, 0px));
    border-bottom:1px solid var(--drawer-edge);
    background:var(--cabinet-deep);
  }
  .home-drawer-back{
    background:none; border:0; color:var(--parchment); cursor:pointer;
    font-family:var(--font-body); font-weight:700; font-size:14px;
    padding:6px 10px; border-radius:8px;
  }
  .home-drawer-back:hover{ background:rgba(255,255,255,0.06); }
  .home-drawer-back:focus-visible{ outline:2px solid var(--parchment); outline-offset:2px; }
  .home-drawer-title{
    margin:0; text-align:center;
    font-family:var(--font-display); font-weight:400; font-size:18px; letter-spacing:0.05em;
    text-transform:uppercase;
  }
  .home-drawer-close{
    background:none; border:0; color:var(--parchment-dim);
    font-size:20px; line-height:1; cursor:pointer; padding:4px 8px; border-radius:8px;
  }
  .home-drawer-close:hover{ color:var(--parchment); background:rgba(255,255,255,0.06); }
  .home-drawer-close:focus-visible{ outline:2px solid var(--parchment); outline-offset:2px; }
  .home-drawer-panel{
    flex:1 1 auto; min-height:0; overflow-y:auto; overscroll-behavior:contain;
    padding:14px 16px calc(20px + env(safe-area-inset-bottom, 0px));
    display:flex; flex-direction:column; gap:14px;
  }
  .home-drawer-section{ display:flex; flex-direction:column; gap:8px; }
  .home-drawer-section > .home-section-label{ margin:0; }

  /* Encyclopedia entry inside the Knowledge drawer: shares the shell of a
     collapsed today-chip so it sits next to the Daily Intel / Field ID
     tiles without inventing a new visual language. */
  .home-drawer-link{
    display:grid; grid-template-columns:36px minmax(0,1fr) auto; align-items:center; gap:12px;
    padding:11px 14px; border-radius:12px;
    background:var(--drawer); color:var(--parchment); cursor:pointer;
    border:1px solid var(--drawer-edge); border-left:3px solid var(--mode-fieldid);
    font-family:inherit; text-align:left;
    transition: translate 0.14s ease, background 0.14s ease;
  }
  .home-drawer-link:hover{ translate:0 -1px; background:var(--drawer-hover); }
  .home-drawer-link:focus-visible{ outline:2px solid var(--parchment); outline-offset:2px; }
  .home-drawer-link-icon{
    display:inline-flex; align-items:center; justify-content:center;
    width:36px; height:36px; border-radius:8px;
    color:var(--mode-fieldid);
    background:color-mix(in srgb, var(--mode-fieldid) 18%, rgba(0,0,0,0.28));
  }
  .home-drawer-link-icon .ui-icon{ width:20px; height:20px; }
  .home-drawer-link-text{ display:flex; flex-direction:column; min-width:0; }
  .home-drawer-link-title{ font-family:var(--font-display); font-size:15px; letter-spacing:0.02em; }
  .home-drawer-link-sub{ font-size:12px; color:var(--parchment-dim); }
  .home-drawer-link-arrow{ font-family:var(--font-display); font-size:20px; color:var(--parchment-dim); }

  /* When a drawer is open, the two-door landing behind it stops accepting
     interaction and blurs slightly so the drawer clearly owns focus. */
  .home-two-door[data-home-door="knowledge"] .home-doors,
  .home-two-door[data-home-door="battle"] .home-doors,
  .home-two-door[data-home-door="knowledge"] .home-progress,
  .home-two-door[data-home-door="battle"] .home-progress{
    pointer-events:none; filter:blur(1.5px) brightness(0.72); transition:filter 0.18s ease;
  }

  /* Progress strip + drawer. */
  .home-progress{
    display:flex; align-items:center; gap:10px; width:100%;
    padding:9px 12px; border-radius:12px; border:1px solid var(--drawer-edge);
    background:rgba(0,0,0,0.22); color:var(--parchment);
    font-family:var(--font-body); font-size:12.5px; font-weight:700; text-align:left; cursor:pointer;
    transition:background 0.15s ease, scale 0.12s ease;
  }
  .home-progress:hover{ background:rgba(0,0,0,0.32); }
  .home-progress:focus-visible{ outline:2px solid var(--parchment); outline-offset:2px; }
  .home-progress-level{ font-family:var(--font-display); font-size:13px; letter-spacing:0.02em; white-space:nowrap; }
  .home-progress-bar{ flex:1 1 60px; min-width:40px; height:8px; border-radius:99px; background:rgba(255,255,255,0.12); overflow:hidden; }
  .home-progress-fill{ display:block; height:100%; border-radius:99px; background:linear-gradient(90deg, var(--moss-bright), var(--brass-bright)); }
  .home-progress-stat{ display:inline-flex; align-items:center; gap:4px; white-space:nowrap; color:var(--parchment-dim); }
  .home-progress-hint{ margin-left:auto; white-space:nowrap; color:var(--brass-bright); }
  .progress-drawer{
    position:fixed; inset:auto 0 0 0; margin:0 auto; width:100%; max-width:560px; max-height:88dvh;
    overflow-y:auto; overscroll-behavior:contain;
    padding:18px 20px calc(24px + env(safe-area-inset-bottom, 0px));
    border:1px solid var(--drawer-edge); border-bottom:0; border-radius:18px 18px 0 0;
    background:var(--drawer); color:var(--parchment);
    box-shadow:0 -12px 40px var(--drawer-shadow);
    opacity:0; translate:0 28px;
    transition: opacity 0.25s ease-out, translate 0.25s ease-out, display 0.25s allow-discrete, overlay 0.25s allow-discrete;
  }
  .progress-drawer:popover-open{ opacity:1; translate:0 0; }
  @starting-style{ .progress-drawer:popover-open{ opacity:0; translate:0 28px; } }
  .progress-drawer::backdrop{ background:rgba(5,12,8,0.6); }
  @media (min-width:900px){
    .progress-drawer{
      inset:0 0 0 auto; max-width:420px; max-height:none; height:100dvh;
      border-radius:18px 0 0 18px; border-bottom:1px solid var(--drawer-edge); border-right:0;
      translate:28px 0;
    }
    @starting-style{ .progress-drawer:popover-open{ translate:28px 0; } }
  }
  @media (prefers-reduced-motion:reduce){
    .progress-drawer{ translate:none; transition-duration:0.1s; }
    @starting-style{ .progress-drawer:popover-open{ translate:none; } }
  }
  .progress-drawer-head{ display:flex; align-items:center; justify-content:space-between; margin-bottom:12px; }
  .progress-drawer-title{ font-family:var(--font-display); font-size:16px; letter-spacing:0.03em; }
  .progress-drawer-close{ background:none; border:0; color:var(--parchment-dim); font-size:20px; line-height:1; cursor:pointer; padding:2px 6px; }
  .progress-drawer-close:hover{ color:var(--parchment); }
  .progress-drawer .fight-panel-box{ margin-top:12px; }
  .progress-drawer .home-missions{ margin-bottom:10px; }
  .progress-drawer .home-more{ margin-top:14px; }
  .progress-drawer .home-more-panel{
    position:static; transform:none; flex-direction:row; flex-wrap:wrap; justify-content:center; gap:14px;
    margin-top:8px; padding:8px 0 0; background:transparent; border:0; box-shadow:none; min-width:0;
  }

  /* ---- Setup: mode name, three chips, one line, options folded ---- */
  .setup-title{ margin-bottom:4px; }
  .setup-chips{ display:flex; flex-wrap:wrap; justify-content:center; gap:6px; margin:8px 0 10px; }
  .setup-chip{
    padding:4px 10px; border-radius:999px; font-size:12px; font-weight:800; white-space:nowrap;
    color:var(--brass-bright); background:color-mix(in srgb, var(--brass) 20%, transparent);
    box-shadow: inset 0 0 0 1px var(--brass);
  }
  .setup-summary{ text-align:center; font-size:13.5px; margin:0 0 14px; }
  .setup-more{ margin:4px 0 12px; border-radius:12px; background:rgba(0,0,0,0.2); box-shadow: inset 0 0 0 1px var(--drawer-edge); }
  .setup-more-summary{
    display:flex; align-items:center; gap:8px; padding:10px 12px; cursor:pointer; list-style:none;
    font-weight:800; font-size:13px;
  }
  .setup-more-summary::-webkit-details-marker{ display:none; }
  .setup-more-summary::before{
    content:''; width:7px; height:7px; flex:0 0 auto;
    border-right:2px solid var(--brass-bright); border-bottom:2px solid var(--brass-bright);
    transform:rotate(-45deg); transition:transform 0.15s ease;
  }
  .setup-more[open] > .setup-more-summary::before{ transform:rotate(45deg); }
  .setup-more-hint{ font-weight:600; font-size:11.5px; color:var(--parchment-dim); }
  .setup-more > :not(summary){ margin-left:12px; margin-right:12px; }
  .setup-more[open]{ padding-bottom:10px; }
  .setup-more > .setup-section-label:first-of-type{ margin-top:2px; }

  /* ---- Draft: round dots, roster strips, three-up candidates on phones ---- */
  .draft-dots{ display:inline-flex; gap:4px; margin-left:10px; vertical-align:middle; }
  .draft-dots i{ display:inline-block; width:7px; height:7px; border-radius:99px; background:rgba(255,255,255,0.18); }
  .draft-dots i.done{ background:var(--moss-bright); }
  .draft-dots i.now{ background:var(--blaze-bright); box-shadow:0 0 0 2px rgba(255,160,79,0.35); }
  .draft-blind-note, .pop-badge-note{ font-size:11.5px; margin:4px 0; }
  .fight-teams{ gap:10px; margin:12px 0 4px; }
  /* Roster rows as horizontal strips (id-anchored: the muster-line rules
     above are more specific than a bare class). The two-column desktop
     layout keeps its wrapped grid, where the column has the room. */
  #fightTeams .fight-slots{ flex-wrap:nowrap; overflow-x:auto; padding-bottom:4px; scrollbar-width:thin; }
  #fightTeams .fight-slot{ flex:0 0 auto; min-width:64px; max-width:150px; white-space:nowrap; overflow:hidden; text-overflow:ellipsis; }
  #fightTeams .fight-slot.empty{ min-width:30px; }
  .card.draft-layout #fightTeams .fight-slots{ flex-wrap:wrap; overflow:visible; }
  .card.draft-layout #fightTeams .fight-slot{ flex:1 1 calc(50% - 6px); max-width:none; white-space:normal; }
  .card.draft-layout #fightTeams .fight-slot.empty{ flex:0 0 auto; }
  @media (max-width:400px){
    .double-spin-grid[data-choices="3"]{ grid-template-columns:repeat(3, minmax(0,1fr)); gap:6px; }
    .double-spin-grid[data-choices="3"] .double-spin-card{
      flex-direction:column; justify-content:center; gap:6px; padding:10px 4px; min-height:104px;
    }
    .double-spin-grid[data-choices="3"] .double-spin-card .emoji{ font-size:40px; }
    .double-spin-grid[data-choices="3"] .double-spin-name{ font-size:10px; text-align:center; }
  }
  .reroll-btn{ border-radius:8px; background:color-mix(in srgb, var(--blaze) 22%, var(--drawer)); }
  .reroll-btn-uses{ padding:1px 6px; border-radius:99px; background:var(--blaze); color:var(--cabinet); opacity:1; font-weight:800; white-space:nowrap; }
  .reroll-btn{ white-space:nowrap; }

  .home-today-chip:hover{ translate:0 -2px; filter:none; }
  .home-today-chip:active,
  .home-progress:active,
  .start-btn:active,
  .primary-btn:active,
  .home-play-btn:active,
  .reroll-btn:active{ scale:0.98; translate:0 0; transform:none; }
  @media (prefers-reduced-motion:reduce){
    .card{ animation:none; }
    .home-today-chip:hover{ translate:none; }
  }
  /* Three-up candidates on a phone: the reroll token goes icon + badge only
     and right-aligns so the (i) button keeps its own corner. */
  @media (max-width:460px){
    .double-spin-grid[data-choices="3"] .reroll-btn-compact{ font-size:0; gap:4px; padding:5px 8px; align-self:flex-end; }
    .double-spin-grid[data-choices="3"] .reroll-btn-compact .ui-icon{ width:14px; height:14px; }
    .double-spin-grid[data-choices="3"] .reroll-btn-compact .reroll-btn-uses{ font-size:9px; }
  }

  /* ---- Motion pass ------------------------------------------------- */
  /* A. Title flicker wave: a masked ::after clone of the wordmark carries a
     narrow light band left-to-right every 6s with a stutter at the leading
     edge. Home only - .home-two-door exists in #card on the home screen and
     nowhere else, so the :has() scope needs no JS. */
  .masthead h1{ position:relative; }
  body:has(.home-two-door) .masthead h1::after{
    content:"Animal Army"; position:absolute; inset:0; pointer-events:none;
    color:#fff; text-shadow:0 0 14px rgba(255,255,255,0.6);
    -webkit-mask-image:linear-gradient(100deg, transparent 42%, #000 49%, #000 51%, transparent 58%);
    mask-image:linear-gradient(100deg, transparent 42%, #000 49%, #000 51%, transparent 58%);
    -webkit-mask-size:300% 100%; mask-size:300% 100%;
    -webkit-mask-repeat:no-repeat; mask-repeat:no-repeat;
    animation:titleWave 6s ease-in-out infinite;
  }
  @keyframes titleWave{
    0%{ -webkit-mask-position:150% 0; mask-position:150% 0; }
    9%{ -webkit-mask-position:70% 0; mask-position:70% 0; }
    11%{ -webkit-mask-position:76% 0; mask-position:76% 0; }
    17%,100%{ -webkit-mask-position:-50% 0; mask-position:-50% 0; }
  }
  /* B. Spin! spins: reels blur and jitter vertically while fight.spinning,
     then the dealt cards settle in one after another. */
  @keyframes reelSpin{
    from{ transform:translateY(-7px); filter:blur(1.5px); }
    to{ transform:translateY(7px); filter:blur(3px); }
  }
  .double-spin-card.is-spinning .emoji{
    animation:reelSpin 0.1s linear infinite alternate;
  }
  .double-spin-card.is-spinning:nth-child(2) .emoji{ animation-delay:-33ms; }
  .double-spin-card.is-spinning:nth-child(3) .emoji{ animation-delay:-66ms; }
  @keyframes reelSettle{
    0%{ transform:translateY(-16px); filter:blur(2px); opacity:0.5; }
    60%{ transform:translateY(3px); filter:blur(0); opacity:1; }
    100%{ transform:translateY(0); }
  }
  .double-spin-grid[data-testid="spin-choice-grid"] .double-spin-slot{
    animation:reelSettle 0.4s cubic-bezier(0.2,0.9,0.3,1.2) both;
  }
  .double-spin-grid[data-testid="spin-choice-grid"] .double-spin-slot:nth-child(2){ animation-delay:80ms; }
  .double-spin-grid[data-testid="spin-choice-grid"] .double-spin-slot:nth-child(3){ animation-delay:160ms; }
  /* D. Score count-up lands with a scale punch (class added by
     animateScoreCountUp when the count reaches the final score). */
  @keyframes scoreLand{ 0%{ transform:scale(1); } 40%{ transform:scale(1.22); } 100%{ transform:scale(1); } }
  .ov-score-land{ display:inline-block; animation:scoreLand 0.35s cubic-bezier(0.2,0.9,0.3,1.2) both; }
  /* G1. The VS reticle used to pull 21px into both team blocks and sat on
     the YOUR FIGHTER label (and the enemy bench line). Give the two blocks
     that flank it the room instead. */
  .fight-team.ai:has(~ .gauntlet-vs){ padding-bottom:12px; }
  .gauntlet-vs ~ .fight-team.player{ padding-top:14px; }
  .gauntlet-vs{ margin:-12px auto; }
  /* G2. The phone bottom sheet is min(92dvh, 720px) tall; on a short phone
     that rose under the fixed top strip and the guest SAVE PROGRESS pill sat
     on the drawer title. Cap it to the space below the strip. */
  @media (max-width:640px){
    .home-drawer{ height:min(92dvh, 720px, calc(100dvh - var(--top-strip-h, 104px) - 8px)); }
  }
  @media (prefers-reduced-motion: reduce){
    body:has(.home-two-door) .masthead h1::after{ display:none; }
    .double-spin-card.is-spinning .emoji,
    .double-spin-grid[data-testid="spin-choice-grid"] .double-spin-slot,
    .ov-score-land{ animation:none; }
  }

  /* ================================================================
     Field ID v2 (2026-09): picture reveal, fair timer, warmer/colder
     chips, paid assist, tiers, results card, week strip, expedition,
     phone layout. Appended last so ties resolve here.
     ================================================================ */
  .fieldid-photo{
    position:relative; display:flex; align-items:center; justify-content:center;
    height:150px; border-radius:14px; overflow:hidden;
    background:radial-gradient(60% 70% at 50% 60%, rgba(124,58,237,0.22), rgba(0,0,0,0.25));
    box-shadow: inset 0 0 0 1px rgba(46,39,23,0.25);
  }
  .fieldid-photo .animal-art, .fieldid-photo-glyph{ width:120px; height:120px; font-size:104px; line-height:1; object-fit:contain; transition: filter 0.6s ease, transform 0.6s ease; }
  .fieldid-photo[data-focus="0"] .animal-art, .fieldid-photo[data-focus="0"] .fieldid-photo-glyph{ filter:blur(22px) saturate(0.6); transform:scale(1.9); }
  .fieldid-photo[data-focus="1"] .animal-art, .fieldid-photo[data-focus="1"] .fieldid-photo-glyph{ filter:blur(14px) saturate(0.7); transform:scale(1.6); }
  .fieldid-photo[data-focus="2"] .animal-art, .fieldid-photo[data-focus="2"] .fieldid-photo-glyph{ filter:blur(8px) saturate(0.85); transform:scale(1.3); }
  .fieldid-photo[data-focus="3"] .animal-art, .fieldid-photo[data-focus="3"] .fieldid-photo-glyph{ filter:blur(4px); transform:scale(1.1); }
  .fieldid-photo.is-halfway .animal-art, .fieldid-photo.is-halfway .fieldid-photo-glyph{ filter:blur(calc(var(--fid-blur, 8px) * 0.6)); }
  .fieldid-photo[data-focus="clear"] .animal-art, .fieldid-photo[data-focus="clear"] .fieldid-photo-glyph{ filter:none; transform:scale(1); }
  .fieldid-photo-tier{
    position:absolute; top:8px; left:8px; padding:2px 8px; border-radius:999px;
    font-family:var(--font-stamp); font-size:9.5px; font-weight:800; letter-spacing:0.08em; text-transform:uppercase;
    background:rgba(46,39,23,0.85); color:var(--parchment);
  }
  .fieldid-photo-tier.tier-3{ background:var(--mode-fieldid); }
  .fieldid-photo-tier.tier-4{ background:var(--blaze); color:var(--cabinet); }
  .fieldid-photo-clock{
    position:absolute; right:8px; bottom:8px; padding:2px 8px; border-radius:999px;
    font-family:var(--font-body); font-size:10.5px; font-weight:700;
    background:rgba(46,39,23,0.8); color:var(--parchment);
  }
  .fieldid-timer-live{
    flex:0 0 auto; margin-left:6px; padding:1px 8px; border-radius:999px;
    font-family:var(--font-stamp); font-size:11px; font-weight:800; font-variant-numeric:tabular-nums;
    background:rgba(124,58,237,0.14); color:#4c1d95;
  }
  .fieldid-timer-armed-note{ font-family:var(--font-body); font-size:11px; color:rgba(46,39,23,0.65); }

  /* Compact clue rows: one line each, reveal as a small chip. */
  .fieldid-hint-row{ min-height:44px; padding:7px 10px; grid-template-columns:24px minmax(0,1fr) auto; gap:8px; }
  .fieldid-hint-row.is-locked .fieldid-hint-value{ display:none; }
  .fieldid-hint-reveal{ padding:5px 9px; font-size:11px; border-radius:999px; white-space:nowrap; }
  .fieldid-hint-row.is-assist{ box-shadow:inset 0 0 0 1px rgba(124,58,237,0.5); }

  /* Warmer / colder chips under a wrong guess. */
  .fieldid-compare{ display:flex; flex-wrap:wrap; gap:6px; align-items:center; }
  .fieldid-compare-name{ font-family:var(--font-body); font-size:12px; font-weight:700; color:rgba(46,39,23,0.8); margin-right:2px; }
  .fieldid-compare-chip{
    padding:2px 9px; border-radius:999px; font-family:var(--font-body); font-size:11.5px; font-weight:700;
    background:rgba(239,139,106,0.18); color:#7a2f1b; box-shadow: inset 0 0 0 1px rgba(239,139,106,0.5);
  }
  .fieldid-compare-chip.is-hit{ background:rgba(123,216,143,0.22); color:#1f5a2c; box-shadow: inset 0 0 0 1px rgba(123,216,143,0.6); }

  /* Assist panel without a countdown. */
  .fieldid-mc-head strong{ font-size:13px; }
  .fieldid-mc-options{ grid-template-columns:repeat(2, minmax(0,1fr)); gap:8px; }
  .fieldid-mc-option{ padding:12px 10px; border-radius:10px; font-weight:700; }

  /* Results card */
  .fieldid-result-tiles{ display:grid; grid-template-columns:repeat(5, minmax(0,1fr)); gap:6px; }
  .fieldid-result-tile{
    display:flex; flex-direction:column; align-items:center; gap:3px; padding:8px 4px 6px; border-radius:12px;
    background:rgba(255,255,255,0.28); box-shadow: inset 0 0 0 1px rgba(46,39,23,0.18); text-align:center; min-width:0;
  }
  .fieldid-result-tile.is-hit{ box-shadow: inset 0 0 0 2px #4fae62; }
  .fieldid-result-tile.is-miss{ box-shadow: inset 0 0 0 2px #ef8b6a; opacity:0.85; }
  .fieldid-result-tile .animal-art, .fieldid-result-tile-glyph{ width:40px; height:40px; font-size:36px; line-height:1; object-fit:contain; }
  .fieldid-result-tile-name{ font-family:var(--font-body); font-size:10.5px; font-weight:700; line-height:1.15; color:#2e2717; max-width:100%; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
  .fieldid-result-tile-meta{ font-family:var(--font-stamp); font-size:10px; font-weight:800; color:#1f5a2c; }
  .fieldid-result-tile.is-miss .fieldid-result-tile-meta{ color:#7a2f1b; }
  .fieldid-week{ display:flex; align-items:center; gap:6px; flex-wrap:wrap; }
  .fieldid-week-label{ font-family:var(--font-stamp); font-size:10px; font-weight:800; letter-spacing:0.08em; text-transform:uppercase; color:rgba(46,39,23,0.65); margin-right:4px; }
  .fieldid-week-dot{
    width:22px; height:22px; border-radius:99px; display:inline-flex; align-items:center; justify-content:center;
    font-size:11px; font-weight:800; background:rgba(46,39,23,0.12); color:rgba(46,39,23,0.55); box-shadow: inset 0 0 0 1px rgba(46,39,23,0.2);
  }
  .fieldid-week-dot.is-hit{ background:#4fae62; color:#fff; box-shadow:none; }
  .fieldid-week-dot.is-zero{ background:#ef8b6a; color:#fff; box-shadow:none; }
  .fieldid-week-dot.is-shield{ background:#f0c26b; color:#2e2717; box-shadow:none; }
  .fieldid-week-dot.is-today{ outline:2px solid var(--mode-fieldid); outline-offset:2px; }
  .fieldid-result-head{ display:flex; align-items:baseline; justify-content:space-between; gap:10px; flex-wrap:wrap; }
  .fieldid-result-score{ font-family:var(--font-display); font-size:26px; color:#2e2717; }
  .fieldid-result-xp{ font-family:var(--font-stamp); font-size:13px; font-weight:800; color:#4c1d95; }

  /* Expedition (freeplay) header + filters */
  .fieldid-expedition-bar{ display:flex; align-items:center; justify-content:space-between; gap:8px; flex-wrap:wrap; font-family:var(--font-body); font-size:12.5px; font-weight:700; color:#2e2717; }
  .fieldid-lives{ letter-spacing:2px; color:var(--blaze); }
  .fieldid-filter-row{ display:flex; gap:6px; flex-wrap:wrap; }
  .fieldid-filter-btn{
    padding:4px 10px; border-radius:999px; border:0; cursor:pointer; font-family:var(--font-body); font-size:11.5px; font-weight:700;
    background:rgba(46,39,23,0.1); color:#2e2717; box-shadow: inset 0 0 0 1px rgba(46,39,23,0.22);
  }
  .fieldid-filter-btn.active{ background:var(--mode-fieldid); color:#fff; box-shadow:none; }

  /* Phone: the answer row rides above the keyboard, the header stays lean. */
  @media (max-width:640px){
    .fieldid-photo{ height:130px; }
    .fieldid-guess-row{
      position:sticky; bottom:0; z-index:2; margin:0 -14px; padding:8px 14px calc(8px + env(safe-area-inset-bottom, 0px));
      background:linear-gradient(to top, #c4b88d 85%, transparent);
    }
    .fieldid-guess-input{ padding:12px 14px; font-size:16px; min-height:48px; }
    .fieldid-submit{ padding:11px; }
    .fieldid-result-tiles{ grid-template-columns:repeat(5, minmax(0,1fr)); gap:4px; }
    .fieldid-result-tile{ padding:6px 2px 5px; }
  }
  @media (prefers-reduced-motion:reduce){
    .fieldid-photo .animal-art, .fieldid-photo-glyph{ transition:none; }
  }

  /* ---- Loop fixes (2026-09) ---------------------------------------- */
  .coach-mark{
    margin:8px 0 4px; padding:8px 12px; border-radius:10px;
    background:color-mix(in srgb, var(--brass-bright,#f4c363) 16%, transparent);
    border:1px solid color-mix(in srgb, var(--brass-bright,#f4c363) 45%, transparent);
    color:var(--parchment); font-family:var(--font-body); font-size:13px; text-align:center;
  }
  .fight-pace-row{ display:flex; justify-content:center; gap:8px; margin:0 0 6px; min-height:0; }
  .fight-pace-row:empty{ display:none; }
  .fight-pace-btn{
    background:none; border:1px solid var(--drawer-edge,#4a382c); color:var(--parchment-dim,#a99a80);
    border-radius:999px; padding:4px 10px; min-height:24px; font-family:var(--font-body); font-size:11.5px;
    font-weight:800; letter-spacing:0.04em; cursor:pointer;
  }
  .fight-pace-btn.active{ color:var(--cabinet,#0b1a12); background:var(--brass-bright,#f4c363); border-color:var(--brass-bright,#f4c363); }
  .fight-pace-btn:hover{ color:var(--parchment,#eadfc9); border-color:var(--parchment-dim,#a99a80); }
  /* Continue, drawn for the impact beat only (renderGauntlet): a full-width,
     thumb-height button under the fighters, since the fighters block itself
     is also tappable and nothing tells a first-timer that. */
  .fight-continue-btn{
    display:block; width:100%; margin:10px 0 2px; padding:14px 16px; min-height:52px;
    border:0; border-radius:14px; cursor:pointer;
    background:linear-gradient(180deg, var(--blaze-bright,#ff9a3d), var(--blaze,#ff7a1f));
    color:var(--cabinet,#0b1a12); font-family:var(--font-display); font-size:20px; letter-spacing:0.1em; text-transform:uppercase; line-height:1;
    box-shadow:0 3px 0 var(--blaze-dark,#b84d0a), 0 8px 20px rgba(255,122,31,0.3);
    animation: fightContinueIn 0.25s ease both;
  }
  .fight-continue-btn:active{ translate:0 2px; box-shadow:0 1px 0 var(--blaze-dark,#b84d0a); }
  .fight-continue-btn:focus-visible{ outline:2px solid var(--parchment,#eadfc9); outline-offset:3px; }
  .fight-continue-sub{ display:block; margin-top:4px; font-family:var(--font-body); font-size:10.5px; font-weight:700; letter-spacing:0.06em; text-transform:none; opacity:0.75; }
  @keyframes fightContinueIn{ from{ opacity:0; translate:0 6px; } to{ opacity:1; translate:0 0; } }
  .gauntlet-beat-impact.fight-teams{ cursor:pointer; }
  /* One line when a Favoured pick lost - see resolveGauntletRound. */
  .bout-upset-line{
    margin:8px 0 2px; padding:8px 12px; border-radius:8px; text-align:center;
    background:rgba(255,138,118,0.14); color:#ffc7ba;
    font-family:var(--font-body); font-size:13px; font-weight:800;
  }
  @media (prefers-reduced-motion: reduce){
    .fight-continue-btn{ animation:none; }
  }
  .home-today-line{
    display:block; width:100%; margin:12px 0 0; padding:10px 14px; border-radius:12px; text-align:left;
    background:var(--drawer,#14261c); border:1px solid var(--drawer-edge,#2b4a38); color:var(--parchment,#eadfc9);
    font-family:var(--font-body); font-size:13px; font-weight:700; cursor:pointer;
  }
  .home-today-line:hover{ border-color:var(--brass-bright,#f4c363); }
  .ov-details{ margin:10px 0; }
  .ov-details-summary{
    cursor:pointer; list-style:none; padding:10px 12px; border-radius:10px; border:1px solid var(--drawer-edge,#2b4a38);
    font-family:var(--font-stamp); font-size:11px; font-weight:800; letter-spacing:0.08em; text-transform:uppercase; color:var(--parchment-dim,#a99a80);
  }
  .ov-details-summary::-webkit-details-marker{ display:none; }
  .ov-details-summary::before{ content:"\25B8"; margin-right:8px; }
  .ov-details[open] > .ov-details-summary::before{ content:"\25BE"; }
  @media (max-width:640px){ .ov-cta-inline{ display:none; } }
  /* ------------------------------------------------- auction v2 (A-L)
     Home star, set tags + progress, outbid tokens/buttons/banner, the
     star-slot chip, pass button and recap callouts. Palette reuses the
     cost badge gold, the taken-badge red and the opener chip greens. */
  .auction-home-badge{
    position:absolute; bottom:5px; right:5px; z-index:2;
    display:inline-flex; align-items:center;
    padding:2px 6px; border-radius:999px;
    background:#2f4a12; color:#d8efb8;
    font-family:var(--font-stamp); font-size:9px; font-weight:800;
    letter-spacing:0.04em; text-transform:uppercase; line-height:1;
    border:1px solid rgba(0,0,0,0.28);
  }
  .auction-card-home:not(.auction-card-locked){ box-shadow:inset 0 0 0 1px rgba(143,214,106,0.45); }
  .auction-set-tags{
    display:flex; flex-wrap:wrap; justify-content:center; gap:3px; margin-top:4px;
  }
  .auction-set-tag, .auction-set-pill{
    display:inline-block; padding:2px 6px; border-radius:999px;
    font-family:var(--font-stamp); font-size:8.5px; font-weight:800;
    letter-spacing:0.05em; text-transform:uppercase; line-height:1.2;
    border:1px solid rgba(244,240,232,0.25); color:var(--parchment-dim);
    background:rgba(0,0,0,0.18);
  }
  .set-pack{ color:#e2a06c; border-color:rgba(226,160,108,0.45); }
  .set-ocean{ color:#7cc4e8; border-color:rgba(124,196,232,0.45); }
  .set-night{ color:#c2a4f0; border-color:rgba(194,164,240,0.45); }
  .auction-sets{
    display:flex; justify-content:center; flex-wrap:wrap; gap:6px; margin:0 auto 8px;
  }
  .auction-set-pill{ font-size:10px; padding:4px 10px; }
  .auction-set-pill.is-done{ background:rgba(92,143,58,0.22); color:#b7f09a; border-color:#8fd66a; }
  .auction-outbid-token{
    display:inline-block; margin-left:6px; padding:2px 7px; border-radius:999px;
    font-family:var(--font-stamp); font-size:9px; font-weight:800;
    letter-spacing:0.05em; text-transform:uppercase; vertical-align:middle;
    background:rgba(191,58,34,0.18); color:#f0b3a3; border:1px solid rgba(191,58,34,0.55);
  }
  .auction-outbid-token.is-spent{ opacity:0.45; text-decoration:line-through; }
  .auction-outbid-btn{
    width:100%; margin-top:2px; padding:7px 6px; border-radius:8px; cursor:pointer;
    font-family:var(--font-stamp); font-size:11px; font-weight:800;
    letter-spacing:0.05em; text-transform:uppercase;
    background:#bf3a22; color:#fff2e6; border:2px solid #fff2e6;
    box-shadow:0 0 12px rgba(191,58,34,0.45);
  }
  .auction-outbid-btn:hover, .auction-outbid-btn:focus-visible{ background:#d9482c; }
  .auction-outbid-banner{
    margin:4px auto 8px; padding:8px 12px; border-radius:10px; max-width:520px;
    background:#f4d7d0; color:#5a1f10; border:2px solid #8a2f1c;
    font-size:13px; font-weight:700; text-align:center;
  }
  .auction-outbid-banner .auction-sting{
    position:static; animation:none; display:inline-block;
    font-size:11px; padding:2px 8px; margin-right:6px; vertical-align:middle;
  }
  .auction-slot-chip{
    display:inline-block; margin:2px auto 6px; padding:4px 11px; border-radius:999px;
    font-family:var(--font-stamp); font-size:10px; font-weight:800;
    letter-spacing:0.05em; text-transform:uppercase;
    border:1px solid rgba(217,176,74,0.6); background:rgba(122,90,18,0.16); color:#f0d97a;
  }
  .auction-actions{ display:flex; flex-direction:column; align-items:center; gap:2px; margin-top:8px; }
  .auction-actions .auto-finish-btn{ margin-top:8px; }
  .auction-recap-callouts{
    list-style:none; margin:0 0 10px; padding:8px 10px; border-radius:8px;
    background:rgba(0,0,0,0.14); border:1px solid var(--drawer-edge);
    font-size:12px; line-height:1.45; color:var(--parchment);
  }
  .auction-recap-callouts li + li{ margin-top:3px; }
  .auction-recap-callouts b{
    font-family:var(--font-stamp); font-size:9.5px; letter-spacing:0.06em;
    text-transform:uppercase; color:var(--blaze-bright); margin-right:4px;
  }
  @media (min-width:1000px){
    .card.draft-layout > .auction-slot-chip,
    .card.draft-layout > .auction-outbid-banner,
    .card.draft-layout > .auction-sets,
    .card.draft-layout > .auction-actions{ justify-self:center; }
  }
  /* K: a 9-card board on a phone was three columns of 100px tiles with the
     info button sitting on the name. Two columns give every card room. */
  @media (max-width:520px){
    .double-spin-grid[data-choices="9"],
    .double-spin-grid[data-choices="8"]{ grid-template-columns:1fr 1fr; gap:8px; }
    .double-spin-grid[data-choices="9"] .double-spin-card,
    .double-spin-grid[data-choices="8"] .double-spin-card{ padding:18px 6px 12px; min-height:112px; }
    .double-spin-grid[data-choices="9"] .double-spin-card .emoji,
    .double-spin-grid[data-choices="8"] .double-spin-card .emoji{ font-size:38px; }
    .double-spin-grid[data-choices="9"] .double-spin-name,
    .double-spin-grid[data-choices="8"] .double-spin-name{ font-size:11px; padding:0 22px; }
  }
  /* The info button sits bottom-left of the slot; lift it clear of the
     Outbid button that appears under a SOLD card. */
  .double-spin-slot:has(.auction-outbid-btn) .card-info-btn{ bottom:44px; }
  /* ================================================================
     Polish pass (2026-09): phone masthead, rules tabs, result chips,
     fight entrances, verdict letter pop, ambient particle layer.
     ================================================================ */
  /* A. In a game the wordmark shrinks on laptops and goes away on phones,
     together with the fixed top strip, so the stage starts at the top. */
  body.aa-in-game .masthead{ margin-bottom:10px; }
  body.aa-in-game .masthead .kicker{ display:none; }
  body.aa-in-game .masthead h1{ font-size:22px; margin:0; }
  @media (max-width:760px){
    body.aa-in-game .masthead, body.aa-in-game .top-strip{ display:none; }
    body.aa-in-game{ padding-top:10px !important; }
  }

  /* L. Ambient particles: above the arena gradient, under the scrim. */
  .bg-ambient{ position:fixed; inset:0; z-index:-3; pointer-events:none; width:100%; height:100%; }
  @media (prefers-reduced-motion: reduce){ .bg-ambient{ display:none; } }

  /* E. Rules sheet tabs: radio inputs + labels, no script. */
  .rules-tab-input{ position:absolute; opacity:0; pointer-events:none; width:1px; height:1px; }
  .rules-tabs{ display:flex; gap:6px; flex-wrap:wrap; margin:0 0 12px; }
  .rules-tab{
    flex:1 1 auto; text-align:center; cursor:pointer;
    padding:8px 12px; border-radius:99px;
    border:1px solid rgba(246,234,208,0.25); color:var(--parchment-dim);
    font-family:var(--font-body); font-weight:800; font-size:12px; letter-spacing:0.08em; text-transform:uppercase;
    transition:background 0.15s ease, color 0.15s ease, transform 0.12s ease;
  }
  .rules-tab:hover{ transform:translateY(-1px); }
  .rules-tab-input:focus-visible + .rules-tabs .rules-tab,
  .rules-tab-input:focus-visible ~ .rules-tabs .rules-tab{ outline:none; }
  #rulesTabBattle:checked ~ .rules-tabs label[for="rulesTabBattle"],
  #rulesTabDraft:checked ~ .rules-tabs label[for="rulesTabDraft"],
  #rulesTabModes:checked ~ .rules-tabs label[for="rulesTabModes"],
  #rulesTabTraits:checked ~ .rules-tabs label[for="rulesTabTraits"]{
    background:var(--blaze); border-color:var(--blaze); color:var(--cabinet);
  }
  .rules-pane{ display:none; animation:settle 0.22s ease; }
  #rulesTabBattle:checked ~ .rules-panes .rules-pane-Battle,
  #rulesTabDraft:checked ~ .rules-panes .rules-pane-Draft,
  #rulesTabModes:checked ~ .rules-panes .rules-pane-Modes,
  #rulesTabTraits:checked ~ .rules-panes .rules-pane-Traits{ display:block; }
  .rules-list{ margin:0; padding-left:18px; }
  .rules-list li{ margin:0 0 10px; line-height:1.45; }
  .rules-list li b{ color:var(--parchment); }

  /* F. Result overlay: bonus lines are chips in one row; the sentence
     caption stays for screen readers only. */
  .ov-bonus-row{ display:flex; flex-wrap:wrap; justify-content:center; gap:6px; margin:10px auto 6px; max-width:520px; }
  .ov-bonus-row:empty{ display:none; }
  .ov-bonus-row .ov-first-win, .ov-bonus-row .ov-upsets{
    margin:0; padding:5px 10px; border-radius:99px;
    background:rgba(255,196,64,0.12); border:1px solid rgba(255,196,64,0.25);
    font-size:11px; letter-spacing:0.03em;
  }
  .ov-scoreboard-caption.sr-summary{
    position:absolute; width:1px; height:1px; padding:0; margin:-1px;
    overflow:hidden; clip:rect(0 0 0 0); clip-path:inset(50%); white-space:nowrap; border:0;
  }

  /* N. Fight entrances: the foe steps in from the left, your fighter from
     the right; the VS badge breathes between beats; UPSET slams like the
     K.O. stamp; the stamina drain takes a beat longer to read. */
  .fight-team.ai .fight-reveal{ animation:fighterEnterLeft 0.5s cubic-bezier(0.2,0.9,0.3,1.08) both; }
  .fight-team.player .fight-reveal{ animation:fighterEnterRight 0.5s cubic-bezier(0.2,0.9,0.3,1.08) both; }
  @keyframes fighterEnterLeft{ from{ opacity:0; transform:translateX(-46px) scale(0.8); } to{ opacity:1; transform:none; } }
  @keyframes fighterEnterRight{ from{ opacity:0; transform:translateX(46px) scale(0.8); } to{ opacity:1; transform:none; } }
  .gauntlet-vs:not(.gauntlet-vs-whip){ animation:vsBreathe 2.4s ease-in-out infinite; }
  @keyframes vsBreathe{
    0%,100%{ transform:rotate(var(--momentum-tilt, 0deg)) scale(1); box-shadow:0 2px 6px rgba(0,0,0,0.3); }
    50%{ transform:rotate(var(--momentum-tilt, 0deg)) scale(1.12); box-shadow:0 0 14px rgba(244,195,99,0.55); }
  }
  .gauntlet-upset-stencil{ animation:upsetSlam 0.42s cubic-bezier(0.2,0.9,0.3,1.2) 0.1s both; }
  @keyframes upsetSlam{
    0%{ transform:translate(-50%,-50%) rotate(-14deg) scale(2.6); opacity:0; }
    60%{ opacity:1; }
    100%{ transform:translate(-50%,-50%) rotate(-8deg) scale(1); opacity:1; }
  }
  .stamina-fill-animating{ transition:width 0.95s cubic-bezier(0.2,0.7,0.3,1), background-color 0.4s ease; }

  /* P. Verdict pops in letter by letter; KO counts land with the same punch
     as the score; Star of the Fight gets one shine sweep. */
  .ov-banner:has(.ov-banner-ch){ opacity:1; animation:none; }
  .ov-banner-ch{
    display:inline-block; opacity:0; transform:translateY(12px) scale(0.6);
    animation:ovLetterPop 0.38s cubic-bezier(0.2,1.4,0.4,1) calc(0.85s + var(--i, 0) * 0.055s) forwards;
  }
  @keyframes ovLetterPop{ to{ opacity:1; transform:none; } }
  .ov-scoreboard-num.ov-score-land{ animation:scoreLand 0.35s cubic-bezier(0.2,1.4,0.4,1) both; }
  .ov-star{ position:relative; overflow:hidden; }
  .ov-star::after{
    content:''; position:absolute; top:-40%; bottom:-40%; left:0; width:38%;
    background:linear-gradient(100deg, transparent 0%, rgba(255,255,255,0.32) 50%, transparent 100%);
    transform:skewX(-20deg) translateX(-260%);
    animation:ovShine 1.1s ease-out 1.7s forwards;
  }
  @keyframes ovShine{ to{ transform:skewX(-20deg) translateX(420%); } }
  @media (prefers-reduced-motion: reduce){
    .fight-team.ai .fight-reveal, .fight-team.player .fight-reveal, .gauntlet-vs:not(.gauntlet-vs-whip),
    .gauntlet-upset-stencil, .ov-star::after, .rules-pane{ animation:none; }
    .ov-banner-ch{ animation:none; opacity:1; transform:none; }
  }

  /* Battle picker: the smart call (weakest fighter expected to win). */
  .opponent-btn.smart-call{ box-shadow:inset 0 0 0 2px rgba(143,214,106,0.6); position:relative; }
  .smart-call-tag{
    position:absolute; top:-8px; left:10px; padding:2px 8px; border-radius:999px;
    background:#2f4a12; color:#d8efb8; border:1px solid #8fd66a;
    font-family:var(--font-stamp); font-size:9px; font-weight:800; letter-spacing:0.06em; text-transform:uppercase; line-height:1.2;
  }
  /* ================================================================
     Polish pass 2 (2026-09): daily seal art, knowledge drawer, draft
     grade stamp, fight picker rules toggle, door title sweep, dice
     tumble, XP fill, trivia pops, Today tile pulse.
     ================================================================ */
  /* B. The persona portrait sits behind the key column, fading toward the
     values, instead of under the values themselves. */
  .daily-seal-art{
    right:auto; top:auto; left:-16px; bottom:-10px; width:110px; height:110px; opacity:0.26;
    -webkit-mask-image:linear-gradient(90deg, #000 30%, transparent 100%);
            mask-image:linear-gradient(90deg, #000 30%, transparent 100%);
  }

  /* C. Knowledge drawer: narrower on a laptop, its rows carry the width. */
  @media (min-width:641px){ .home-drawer-knowledge{ width:min(440px, 100vw); } }

  /* D. Draft grade: stamps in, holds two beats, then folds away so the
     fight header is not wearing it for the whole bout. */
  .draft-grade-line{ overflow:hidden; animation:draftGradeStamp 3.2s cubic-bezier(0.2,0.9,0.3,1.2) both; }
  @keyframes draftGradeStamp{
    0%{ opacity:0; transform:scale(1.8) rotate(-4deg); max-height:32px; }
    12%{ opacity:1; transform:scale(1) rotate(0deg); }
    78%{ opacity:1; max-height:32px; margin-bottom:8px; }
    100%{ opacity:0; max-height:0; margin-bottom:0; }
  }

  /* K. Fight picker: each candidate is a slot holding the card, a (?)
     toggle and the rules panel it reveals. */
  .fight-pick-slot{ position:relative; display:flex; flex-direction:column; min-width:0; }
  .fight-pick-slot > .opponent-btn{ width:100%; }
  /* The (?) rides the card's bottom-right corner (a negative margin pulls it
     up over the card); the rules panel opens beneath it. */
  .fight-matchup-info{ position:relative; z-index:2; align-self:flex-end; margin:-30px 6px 0 0; }
  .fight-matchup-rules{ margin:6px 2px 0; animation:settle 0.2s ease; }
  .fight-matchup-rules[hidden]{ display:none; }
  @media (max-width:640px){
    .fight-picker > .fight-pick-slot{ flex:0 0 72%; scroll-snap-align:start; }
  }

  /* O. Reroll dice tumble on press. */
  .reroll-btn:active .ui-icon, .draft-pair-reroll:active .ui-icon, .reroll-btn-compact:active .ui-icon{ animation:diceTumble 0.45s cubic-bezier(0.2,0.9,0.3,1.1); }
  @keyframes diceTumble{ 0%{ transform:rotate(0) translateY(0); } 50%{ transform:rotate(200deg) translateY(-4px); } 100%{ transform:rotate(360deg) translateY(0); } }
  .auction-wallet-num{ display:inline-block; }

  /* Q. XP and collection bars fill when the Progress drawer opens; the
     answered Daily Intel dot pops and a correct answer pulses. */
  .progress-drawer .level-bar-fill{ transition:width 0.8s cubic-bezier(0.2,0.7,0.3,1); }
  .trivia-dot-correct.trivia-dot-current, .trivia-dot-wrong.trivia-dot-current{ animation:dotPop 0.4s cubic-bezier(0.2,1.4,0.4,1) both; }
  @keyframes dotPop{ 0%{ transform:scale(0.4); } 60%{ transform:scale(1.5); } 100%{ transform:scale(1); } }
  .trivia-choice-correct{ animation:choicePulse 0.7s ease-out both; }
  @keyframes choicePulse{ 0%{ box-shadow:inset 0 0 0 1.5px #7bd88f, 0 0 0 0 rgba(123,216,143,0.55); } 100%{ box-shadow:inset 0 0 0 1.5px #7bd88f, 0 0 0 14px rgba(123,216,143,0); } }

  @keyframes emberPulse{
    0%,100%{ box-shadow:0 2px 0 var(--blaze-dark), 0 10px 26px rgba(255,122,31,0.32); }
    50%{ box-shadow:0 2px 0 var(--blaze-dark), 0 10px 34px rgba(255,122,31,0.6), 0 0 0 3px rgba(255,180,90,0.25); }
  }

  @keyframes todayBob{ 0%{ opacity:0; transform:translateX(-22px) scale(0.7); } 60%{ transform:translateX(3px) scale(1.06); } 100%{ opacity:1; transform:none; } }

  @media (prefers-reduced-motion: reduce){
    .draft-grade-line,
    .trivia-dot-correct.trivia-dot-current,
    .trivia-dot-wrong.trivia-dot-current,
    .trivia-choice-correct,
    .fight-matchup-rules,
    .reroll-btn:active .ui-icon,
    .draft-pair-reroll:active .ui-icon{ animation:none; }

    .progress-drawer .level-bar-fill{ transition:none; }
  }

  /* ================================================================
     Polish pass 3 (2026-09): drawer above the top strip, phone auction
     board fits, (i) clear of Outbid, slim coach mark.
     ================================================================ */
  /* A. The door drawers are modal, so they layer above the fixed top
     strip (z 1100) instead of under the account pill. */
  .home-drawer-scrim{ z-index:1101; }
  .home-drawer{ z-index:1102; }
  /* B. Grid items default to min-width:auto, so a card whose chip, art and
     name sit in one row forced its column past the phone's edge. */
  .double-spin-grid > .double-spin-slot{ min-width:0; }
  .double-spin-grid .double-spin-card{ min-width:0; max-width:100%; }
  @media (max-width:460px){
    .double-spin-grid[data-choices="5"] .double-spin-card,
    .double-spin-grid[data-choices="4"] .double-spin-card{ flex-wrap:wrap; }
  }
  /* C. A sold card has no chip in its top-left corner, so the (i) moves
     there and leaves the bottom to the Outbid button. */
  .double-spin-slot:has(.auction-outbid-btn) .card-info-btn{ bottom:auto; top:6px; }
  /* E. Coach marks are one quiet line, not a boxed paragraph. */
  .coach-mark{ padding:2px 0; background:none; border:0; font-size:12.5px; color:var(--brass-bright); }
  @media (max-width:460px){
    /* Room under the name for the (i) button once the card wraps. */
    .double-spin-grid[data-choices="5"] .double-spin-card,
    .double-spin-grid[data-choices="4"] .double-spin-card{ padding-bottom:36px; }
  }

  /* ================================================================
     Animal Expert Club + the What's New star (2026-09). One gold accent
     (--club, brass-bright) reserved for membership and for announcing
     new things, so a star anywhere on the page means one of two things:
     "this is new" or "this player is a member". Nothing here paints
     unless js/club.js says the club is visible (see CLUB_LAUNCHED /
     ?club= in that file). Appended last so equal-specificity rules win.
     ================================================================ */
  :root{ --club:var(--brass-bright); --club-dark:#8a6a1e; }

  /* ---- The NEW star: a small gold star with the word, pinned next to a
     label. Lives inline so it works in a pill, a drawer row or a tile. ---- */
  .new-star{
    display:inline-flex; align-items:center; gap:3px; vertical-align:middle;
    margin-left:6px; padding:1px 6px 1px 4px; border-radius:999px;
    background:var(--club); color:var(--cabinet);
    font-family:var(--font-body); font-weight:800; font-size:9.5px; letter-spacing:0.12em;
    line-height:1.4; text-transform:uppercase; white-space:nowrap;
    box-shadow:0 0 0 1px rgba(0,0,0,0.25), 0 0 12px rgba(240,207,110,0.35);
    animation: newStarPop 0.5s cubic-bezier(0.2,0.9,0.3,1.3) both;
  }
  .new-star-icon{ width:10px; height:10px; fill:currentColor; }
  @keyframes newStarPop{ 0%{ transform:scale(0.6); opacity:0; } 100%{ transform:scale(1); opacity:1; } }
  @media (prefers-reduced-motion:reduce){ .new-star{ animation:none; } }

  /* ---- Top-strip pill: same shell as the other pills, gold edge. The
     hidden attribute has to beat the pill's own display:inline-flex, or the
     dark club would still show its pill (the stub tests read the attribute,
     a browser reads the computed display - this is the browser's half). ---- */
  .top-actions > .club-btn[hidden]{ display:none; }
  .top-actions > .club-btn{
    border-color:color-mix(in srgb, var(--club) 70%, transparent);
    color:var(--club);
    background:linear-gradient(180deg, rgba(240,207,110,0.14), rgba(240,207,110,0.04)), rgba(20,14,8,0.55);
  }
  .top-actions > .club-btn:hover{ background:linear-gradient(180deg, rgba(240,207,110,0.24), rgba(240,207,110,0.08)), rgba(20,14,8,0.55); }
  .top-actions > .club-btn .ui-icon{ fill:currentColor; stroke:none; }
  .top-actions > .club-btn.is-member{ background:var(--club); color:var(--cabinet); border-color:var(--club); }
  .top-actions > .club-btn .top-action-new{ display:inline-flex; }
  .top-actions > .club-btn .top-action-new:empty{ display:none; }
  .top-actions > .club-btn .new-star{ margin-left:0; }
  @media (max-width:560px){
    /* Icon-only row on phones: the star badge collapses to a gold dot so
       the pill keeps its width; the word NEW returns on the drawer row. */
    .top-actions > .club-btn .new-star{ position:absolute; top:-4px; right:-2px; padding:0; width:10px; height:10px; gap:0; }
    .top-actions > .club-btn .new-star-text, .top-actions > .club-btn .new-star-icon{ display:none; }
    .top-actions > .club-btn{ position:relative; overflow:visible; }
  }

  /* ---- Member star on the name, and a gold frame on the avatar. ---- */
  .account-club{ display:inline-flex; align-items:center; margin-left:4px; vertical-align:middle; }
  .account-club[hidden]{ display:none; }
  .club-badge{ display:inline-flex; align-items:center; color:var(--club); filter:drop-shadow(0 0 4px rgba(240,207,110,0.55)); }
  .club-badge-icon{ width:13px; height:13px; fill:currentColor; }
  .account-bar.is-club .account-avatar{ box-shadow:0 0 0 2px var(--club), 0 0 10px rgba(240,207,110,0.45); }

  /* ---- Progress drawer row (home). ---- */
  .club-row{
    display:grid; grid-template-columns:36px minmax(0,1fr) auto; align-items:center; gap:12px;
    width:100%; margin:0; padding:10px 12px 10px 10px; border-radius:12px;
    background:linear-gradient(135deg, rgba(240,207,110,0.16), rgba(240,207,110,0.04) 60%), var(--drawer);
    border:1px solid color-mix(in srgb, var(--club) 55%, var(--drawer-edge));
    color:var(--parchment); font-family:inherit; text-align:left; cursor:pointer;
    transition: translate 0.14s ease, background 0.14s ease;
  }
  .club-row:hover{ translate:0 -1px; }
  .club-row:focus-visible{ outline:2px solid var(--parchment); outline-offset:2px; }
  .club-row-star{ display:inline-flex; align-items:center; justify-content:center; width:36px; height:36px; border-radius:10px; background:rgba(0,0,0,0.3); color:var(--club); box-shadow:inset 0 0 0 1px color-mix(in srgb, var(--club) 45%, transparent); }
  .club-row-star .club-badge-icon{ width:18px; height:18px; }
  .club-row-text{ display:flex; flex-direction:column; min-width:0; }
  .club-row-title{ display:flex; align-items:center; font-family:var(--font-display); font-size:14px; letter-spacing:0.03em; text-transform:uppercase; }
  .club-row-sub{ font-size:12px; color:var(--parchment-dim); line-height:1.3; white-space:nowrap; overflow:hidden; text-overflow:ellipsis; }
  .club-row-hint{ font-family:var(--font-body); font-weight:800; font-size:11px; letter-spacing:0.08em; text-transform:uppercase; color:var(--club); white-space:nowrap; }
  .club-row.is-member .club-row-star{ background:var(--club); color:var(--cabinet); }

  /* ---- The club sheet. ---- */
  .club-sheet{ max-width:520px; }
  .club-sheet .leaderboard-title .ui-icon{ fill:var(--club); stroke:none; }
  .club-body{ display:flex; flex-direction:column; gap:16px; margin-top:8px; }
  .club-hero{
    display:flex; flex-direction:column; align-items:center; text-align:center; gap:4px;
    padding:22px 16px 18px; border-radius:14px;
    background:radial-gradient(ellipse at 50% 0%, rgba(240,207,110,0.28), transparent 60%), rgba(0,0,0,0.22);
    box-shadow:inset 0 0 0 1px color-mix(in srgb, var(--club) 45%, transparent);
  }
  .club-hero-star{ color:var(--club); filter:drop-shadow(0 0 14px rgba(240,207,110,0.6)); margin-bottom:4px; }
  .club-hero-star .club-badge-icon{ width:44px; height:44px; }
  .club-hero-name{ font-family:var(--font-display); font-size:22px; letter-spacing:0.06em; text-transform:uppercase; color:var(--parchment); }
  .club-hero-tagline{ font-family:var(--font-body); font-size:13px; color:var(--parchment-dim); }
  .club-hero-price{ margin-top:6px; font-family:var(--font-body); font-weight:800; font-size:12px; letter-spacing:0.1em; text-transform:uppercase; color:var(--club); }
  .club-cta{ display:flex; flex-direction:column; align-items:center; gap:8px; }
  .club-cta-btn{
    width:100%; padding:13px 16px; border:0; border-radius:12px; cursor:pointer;
    background:var(--club); color:var(--cabinet);
    font-family:var(--font-display); font-size:16px; letter-spacing:0.04em; text-transform:uppercase;
    box-shadow:0 2px 0 var(--club-dark), 0 10px 26px rgba(240,207,110,0.28);
    transition: translate 0.12s ease, filter 0.12s ease;
  }
  .club-cta-btn:hover{ translate:0 -1px; filter:brightness(1.05); }
  .club-cta-btn:focus-visible{ outline:2px solid var(--parchment); outline-offset:2px; }
  .club-cta-btn.is-secondary{ background:transparent; color:var(--club); box-shadow:inset 0 0 0 1.5px var(--club); }
  .club-cta-copy{ margin:0; font-family:var(--font-body); font-size:12px; color:var(--parchment-dim); text-align:center; }
  /* The renewal terms under the Join / Manage buttons: small, readable,
     never hidden - the disclosure has to be seen before the button. */
  .club-cta-terms{ margin:6px 0 0; font-size:12px; line-height:1.4; color:var(--parchment-dim); }
  .club-perks{ display:flex; flex-direction:column; gap:8px; }
  .club-perk{
    display:grid; grid-template-columns:28px minmax(0,1fr); gap:10px; align-items:start;
    padding:10px 12px; border-radius:12px; background:var(--drawer); border:1px solid var(--drawer-edge);
  }
  .club-perk-star{ display:inline-flex; align-items:center; justify-content:center; width:28px; height:28px; border-radius:8px; background:rgba(0,0,0,0.3); color:var(--club); }
  .club-perk-star .club-badge-icon{ width:14px; height:14px; }
  .club-perk-title{ display:flex; align-items:center; gap:8px; flex-wrap:wrap; font-family:var(--font-body); font-weight:800; font-size:13.5px; color:var(--parchment); }
  .club-perk-tag{ font-size:9.5px; font-weight:800; letter-spacing:0.12em; text-transform:uppercase; padding:1px 6px; border-radius:999px; background:color-mix(in srgb, var(--moss-bright) 30%, transparent); color:var(--moss-bright); }
  .club-perk-desc{ margin-top:2px; font-family:var(--font-body); font-size:12.5px; line-height:1.35; color:var(--parchment-dim); }
  .club-perk.is-coming{ opacity:0.72; }
  .club-perk.is-coming .club-perk-tag{ background:rgba(255,255,255,0.1); color:var(--parchment-dim); }
  .club-perk.is-coming .club-perk-star{ color:var(--parchment-dim); }
  .club-foot{ margin:0; font-family:var(--font-body); font-size:11px; color:var(--parchment-dim); text-align:center; }

  /* ---- Encyclopedia: Expert notes (members) and the teaser (everyone else). ---- */
  .encyclopedia-expert{
    margin:12px 0 4px; padding:12px 14px; border-radius:12px;
    background:linear-gradient(135deg, rgba(240,207,110,0.12), transparent 60%), rgba(0,0,0,0.22);
    box-shadow:inset 0 0 0 1px color-mix(in srgb, var(--club) 40%, var(--drawer-edge));
    display:flex; flex-direction:column; gap:10px;
  }
  .encyclopedia-expert-head{ display:flex; align-items:center; gap:8px; }
  .encyclopedia-expert-star{ color:var(--club); font-size:14px; }
  .encyclopedia-expert-title{ font-family:var(--font-body); font-weight:800; font-size:11px; letter-spacing:0.14em; text-transform:uppercase; color:var(--club); }
  .encyclopedia-expert-class{ margin-left:auto; font-family:var(--font-display); font-size:12px; letter-spacing:0.04em; text-transform:uppercase; color:var(--parchment); }
  .encyclopedia-expert-row{ display:grid; grid-template-columns:minmax(0,1fr) 44px; grid-template-rows:auto auto auto; column-gap:10px; row-gap:3px; align-items:center; }
  .encyclopedia-expert-label{ grid-column:1; grid-row:1; display:flex; justify-content:space-between; gap:8px; font-family:var(--font-body); font-size:12.5px; font-weight:700; color:var(--parchment); }
  .encyclopedia-expert-tier{ font-size:10.5px; font-weight:800; letter-spacing:0.1em; text-transform:uppercase; color:var(--parchment-dim); }
  .encyclopedia-expert-row.is-high .encyclopedia-expert-tier{ color:var(--club); }
  .encyclopedia-expert-bar{ grid-column:1; grid-row:2; height:7px; border-radius:99px; background:rgba(255,255,255,0.1); overflow:hidden; }
  .encyclopedia-expert-fill{ display:block; height:100%; border-radius:99px; background:var(--club); }
  .encyclopedia-expert-row.is-low .encyclopedia-expert-fill{ background:color-mix(in srgb, var(--club) 45%, var(--drawer-edge)); }
  .encyclopedia-expert-value{ grid-column:2; grid-row:1 / span 2; text-align:right; font-family:var(--font-display); font-size:18px; color:var(--parchment); }
  .encyclopedia-expert-hint{ grid-column:1 / -1; grid-row:3; font-family:var(--font-body); font-size:11px; color:var(--parchment-dim); }
  .encyclopedia-expert-teaser{
    display:grid; grid-template-columns:auto minmax(0,1fr) auto; align-items:center; gap:10px;
    width:100%; margin:12px 0 4px; padding:10px 12px; border-radius:12px; cursor:pointer;
    background:rgba(0,0,0,0.22); color:var(--parchment); font-family:var(--font-body); text-align:left;
    border:1px dashed color-mix(in srgb, var(--club) 55%, var(--drawer-edge));
  }
  .encyclopedia-expert-teaser:hover{ background:rgba(0,0,0,0.3); }
  .encyclopedia-expert-teaser-star{ color:var(--club); font-size:15px; }
  .encyclopedia-expert-teaser-text{ font-size:12px; color:var(--parchment-dim); white-space:nowrap; overflow:hidden; text-overflow:ellipsis; }
  .encyclopedia-expert-teaser-cta{ font-weight:800; font-size:11px; letter-spacing:0.08em; text-transform:uppercase; color:var(--club); white-space:nowrap; }
  @media (max-width:420px){
    .encyclopedia-expert-teaser{ grid-template-columns:auto minmax(0,1fr); }
    .encyclopedia-expert-teaser-cta{ grid-column:2; }
  }

  /* ================================================================
     Drawer tidy (2026-09): the Knowledge and Battle drawers behind the
     two home doors. One surface system for everything inside them -
     a dark panel (--cabinet), raised tiles (--drawer), one hairline
     edge (--drawer-edge) - with each mode's accent confined to its XP
     or status pill and the active ring, so the drawer stops reading
     as a patchwork of purple, blue, teal and amber borders. Layout is
     two labeled sections per drawer, every row full width, the Roll
     difficulties and their Era pills grouped in one card, Auction and
     its panel grouped in another. Appended last so equal-specificity
     rules here win over the theme redesign block above.
     ================================================================ */
  .home-drawer{
    width:min(500px, 100vw);
    background:var(--cabinet);
    border-left:1px solid var(--drawer-edge);
  }
  @media (min-width:641px){ .home-drawer-knowledge{ width:min(500px, 100vw); } }
  /* The dialog takes focus programmatically on open (Escape handling);
     the browser's default focus ring drew a bright blue frame around
     the whole sheet. Keyboard focus on the controls inside still shows. */
  .home-drawer:focus{ outline:none; }
  .home-drawer-head{
    padding:12px 12px 12px 8px;
    padding-top:calc(12px + env(safe-area-inset-top, 0px));
    background:var(--cabinet-deep);
  }
  .home-drawer-title{ font-size:19px; letter-spacing:0.08em; }
  .home-drawer-close{ font-size:18px; padding:6px 10px; }
  .home-drawer-panel{
    padding:16px 16px calc(24px + env(safe-area-inset-bottom, 0px));
    gap:20px;
  }
  .home-drawer-section{ gap:8px; }
  .home-drawer .home-section-label{ font-size:11px; letter-spacing:0.16em; color:var(--parchment-dim); }
  /* Phone bottom sheet: hug the content instead of always standing
     92dvh tall with dead space under the last tile. The panel still
     scrolls once the content is taller than the cap. */
  @media (max-width:640px){
    .home-drawer{
      height:auto;
      max-height:min(92dvh, calc(100dvh - var(--top-strip-h, 104px) - 8px));
      border-top:1px solid var(--drawer-edge);
    }
    .home-drawer-panel{ padding:14px 14px calc(22px + env(safe-area-inset-bottom, 0px)); gap:18px; }
  }

  /* ---- Quiet rows: Predator Puzzle, rival rematch, Daily Intel, Field ID.
     One shape - 40px art, title over a one-line meta, status pill on the
     right - on the raised tile surface with a single hairline edge. The
     mode accent only tints the art frame and the pill. ---- */
  .home-drawer .home-today-row-chips{ display:grid; grid-template-columns:1fr; gap:8px; }

  .home-drawer .home-today-chip.home-rival .home-today-chip-label{ color:var(--parchment); }
  .home-drawer .home-today-chip.is-done{ opacity:0.7; }

  /* ---- Knowledge: Encyclopedia and Collection rows match the daily rows. ---- */
  .home-drawer-link{
    grid-template-columns:40px minmax(0,1fr) auto; gap:12px;
    min-height:62px; padding:10px 12px 10px 10px;
    border:1px solid var(--drawer-edge); background:var(--drawer);
  }
  .home-drawer-link-icon{
    width:40px; height:40px; border-radius:10px;
    color:var(--parchment); background:rgba(0,0,0,0.3);
    box-shadow: inset 0 0 0 1px rgba(246,234,208,0.14);
  }
  .home-drawer-link-title{ font-size:14px; text-transform:uppercase; }
  .home-drawer-link-sub{ font-size:12px; line-height:1.3; }
  .home-drawer-link-arrow{ font-size:22px; line-height:1; }
  /* Narrow phones: tighten the quiet rows so a two-word title like
     "Predator Puzzle" stays on one line beside its status pill. */
  @media (max-width:360px){

    .home-drawer-link{ grid-template-columns:36px minmax(0,1fr) auto; gap:10px; padding:9px 10px 9px 9px; }
    .home-drawer-link-icon{ width:36px; height:36px; }
    .home-drawer-link-title{ font-size:13px; }
  }

  /* ================================================================
     One-tap Play (2026-09): the home screen is anchored by one PLAY
     hero. The two doors become quiet rows under it; the drawers
     behind them are unchanged. Appended last so equal-specificity
     rules here win over the two-door landing block above.
     ================================================================ */
  .home-hero{
    position:relative; overflow:hidden; isolation:isolate;
    min-height:250px; border-radius:18px;
    background:#1a3327; border:2px solid var(--blaze);
    box-shadow:0 14px 34px rgba(0,0,0,0.45);
    display:flex; align-items:flex-end;
  }
  @media (min-width:641px){ .home-hero{ min-height:300px; } }
  .home-hero-art{
    position:absolute; inset:0; width:100%; height:100%; object-fit:cover; object-position:50% 30%;
    z-index:0; pointer-events:none; user-select:none; -webkit-user-drag:none;
  }
  .home-hero-scrim{
    position:absolute; inset:0; z-index:1; pointer-events:none;
    background:linear-gradient(180deg, rgba(10,22,15,0.05) 30%, rgba(10,22,15,0.82) 68%, rgba(10,22,15,0.96) 100%);
  }
  .home-hero-inner{
    position:relative; z-index:2; width:100%;
    display:flex; flex-direction:column; align-items:center; text-align:center; gap:6px;
    padding:0 16px 16px;
  }
  .home-hero-kicker{
    font-family:var(--font-body); font-weight:800; font-size:11px; letter-spacing:0.18em; text-transform:uppercase;
    color:var(--brass-bright);
  }
  .home-go-btn{
    width:100%; max-width:360px; padding:16px 20px; border:0; border-radius:14px; cursor:pointer;
    background:linear-gradient(180deg, var(--blaze-bright), var(--blaze));
    color:var(--cabinet); font-family:var(--font-display); font-weight:400; font-size:28px;
    letter-spacing:0.12em; text-transform:uppercase; line-height:1;
    box-shadow:0 3px 0 var(--blaze-dark), 0 12px 30px rgba(255,122,31,0.38);
    transition: translate 0.12s ease, filter 0.12s ease, box-shadow 0.12s ease;
  }
  .home-go-btn:hover{ translate:0 -1px; filter:brightness(1.05); }
  .home-go-btn:active{ translate:0 2px; box-shadow:0 1px 0 var(--blaze-dark), 0 6px 16px rgba(255,122,31,0.3); }
  .home-go-btn:focus-visible{ outline:2px solid var(--parchment); outline-offset:3px; }
  .home-go-sub{ font-family:var(--font-body); font-size:13px; color:var(--parchment-dim); }
  .home-hardcore-toggle{
    display:inline-flex; align-items:center; gap:8px; margin-top:4px; padding:5px 10px 5px 6px; border-radius:999px;
    background:rgba(0,0,0,0.35); cursor:pointer; user-select:none;
    font-family:var(--font-body); font-weight:700; font-size:12px; color:var(--parchment);
  }
  .home-hardcore-toggle input{ position:absolute; opacity:0; width:1px; height:1px; }
  .home-hardcore-track{
    position:relative; width:32px; height:18px; border-radius:999px; background:rgba(255,255,255,0.18);
    transition: background 0.15s ease;
  }
  .home-hardcore-track::after{
    content:""; position:absolute; top:2px; left:2px; width:14px; height:14px; border-radius:50%;
    background:var(--parchment); transition: translate 0.15s ease;
  }
  .home-hardcore-toggle input:checked + .home-hardcore-track{ background:var(--blaze); }
  .home-hardcore-toggle input:checked + .home-hardcore-track::after{ translate:14px 0; }
  .home-hardcore-toggle input:focus-visible + .home-hardcore-track{ outline:2px solid var(--parchment); outline-offset:2px; }
  .home-hardcore-toggle:has(input:checked) .home-hardcore-label{ color:var(--blaze-bright); }

  /* Two heroes of one size (2026-10): Gauntlet and Habitat side by side on
     anything wider than a phone, stacked below that. */
  .home-heroes{ display:grid; grid-template-columns:1fr; gap:10px; }
  @media (min-width:641px){ .home-heroes-two{ grid-template-columns:1fr 1fr; } }
  .home-hero-habitat{ background:radial-gradient(circle at 50% 35%, #1d5a6e, #0f3d4c 72%); border-color:var(--water); }
  .home-hero-habitat-art{ object-fit:contain; object-position:50% 14%; padding:16px 16px 100px; }
  .home-go-btn-habitat{
    background:linear-gradient(180deg, var(--water-bright), var(--water));
    box-shadow:0 3px 0 var(--water-dark), 0 12px 30px rgba(46,196,214,0.35);
  }
  .home-go-btn-habitat:active{ box-shadow:0 1px 0 var(--water-dark), 0 6px 16px rgba(46,196,214,0.3); }
  .home-hero-kicker .new-star{ margin-left:6px; }
  /* The one small way to everything else. */
  .home-other-modes{
    justify-self:center; display:inline-flex; align-items:center; gap:6px;
    padding:8px 14px; border-radius:999px; cursor:pointer;
    background:var(--drawer); color:var(--parchment); border:1px solid var(--drawer-edge);
    font-family:var(--font-body); font-weight:700; font-size:13px;
    transition: background 0.14s ease, border-color 0.14s ease;
  }
  .home-other-modes:hover{ background:var(--drawer-hover); border-color:color-mix(in srgb, var(--blaze) 55%, var(--drawer-edge)); }
  .home-other-modes:focus-visible{ outline:2px solid var(--parchment); outline-offset:2px; }
  .home-other-modes .home-door-mini-arrow{ font-size:18px; }
  .home-doors.home-doors-mini:has(.home-other-modes){ grid-template-columns:1fr; }

  /* Quiet rows to the drawers. */
  .home-doors.home-doors-mini{ display:grid; grid-template-columns:1fr; gap:8px; margin:0; }
  @media (min-width:641px){ .home-doors.home-doors-mini{ grid-template-columns:1fr 1fr; gap:10px; } }
  .home-door-mini{
    display:grid; grid-template-columns:44px minmax(0,1fr) auto; align-items:center; gap:12px;
    padding:8px 12px 8px 8px; border-radius:12px; cursor:pointer; text-align:left;
    background:var(--drawer); color:var(--parchment); border:1px solid var(--drawer-edge);
    font-family:inherit; transition: translate 0.14s ease, background 0.14s ease, border-color 0.14s ease;
  }
  .home-door-mini:hover{ translate:0 -1px; background:var(--drawer-hover); border-color:color-mix(in srgb, var(--blaze) 55%, var(--drawer-edge)); }
  .home-door-mini:focus-visible{ outline:2px solid var(--parchment); outline-offset:2px; }
  .home-door-mini-art{ width:44px; height:44px; border-radius:10px; object-fit:cover; object-position:50% 20%; background:#1a3327; }
  .home-door-mini-text{ display:flex; flex-direction:column; min-width:0; }
  .home-door-mini-title{ font-family:var(--font-display); font-size:14px; letter-spacing:0.03em; text-transform:uppercase; }
  .home-door-mini-sub{ font-family:var(--font-body); font-size:11.5px; color:var(--parchment-dim); white-space:nowrap; overflow:hidden; text-overflow:ellipsis; }
  .home-door-mini-arrow{ font-family:var(--font-display); font-size:22px; line-height:1; color:var(--parchment-dim); }
  .home .home-today-line{ margin:0; }
  /* The drawer-open blur used to target the poster doors; the hero joins it. */
  .home-two-door[data-home-door="knowledge"] .home-hero,
  .home-two-door[data-home-door="battle"] .home-hero{ filter:blur(2px) brightness(0.6); pointer-events:none; }

  /* ================================================================
     Battle drawer, slimmed (2026-09): one Play button, a chip row,
     Era pills only when they matter, a Customize link, then the Today
     rows. Surface system from the drawer-tidy pass: dark panel
     (--cabinet), raised rows (--drawer), one hairline edge, and the
     blaze accent belongs to Play alone. Appended last so equal-
     specificity rules here win over the theme blocks above.
     ================================================================ */
  .home-drawer{ width:min(500px, 100vw); background:var(--cabinet); }
  /* The dialog takes focus programmatically on open (Escape handling);
     the browser's default focus ring drew a frame around the whole sheet.
     Keyboard focus on the controls inside still shows. */
  .home-drawer:focus{ outline:none; }
  .home-drawer-title{ font-size:19px; letter-spacing:0.08em; }
  .home-drawer-panel{ padding:16px 16px calc(24px + env(safe-area-inset-bottom, 0px)); gap:18px; }
  @media (max-width:640px){
    /* Phone bottom sheet: hug the content instead of standing 92dvh tall
       with dead space under the last row. Still scrolls past the cap. */
    .home-drawer{ height:auto; max-height:min(92dvh, calc(100dvh - 8px)); }
    .home-drawer-panel{ padding:14px 14px calc(22px + env(safe-area-inset-bottom, 0px)); gap:16px; }
  }

  /* ---- Play: the one loud button. ---- */
  .home-play{ display:flex; flex-direction:column; gap:10px; }
  .home-play-hero{
    display:flex; flex-direction:column; align-items:center; justify-content:center; gap:3px;
    width:100%; min-height:76px; margin:0; padding:14px 18px; border:0; border-radius:16px;
    background:linear-gradient(135deg, var(--blaze-bright) 0%, var(--blaze) 55%, var(--blaze-dark) 140%);
    color:#fff; cursor:pointer; font-family:inherit; text-align:center;
    box-shadow:0 2px 0 var(--blaze-dark), 0 12px 28px rgba(255,122,31,0.32);
    transition: translate 0.12s ease, box-shadow 0.15s ease, filter 0.15s ease;
  }
  .home-play-hero:hover{ filter:brightness(1.06); translate:0 -1px; }
  .home-play-hero:active{ translate:0 1px; box-shadow:0 1px 0 var(--blaze-dark), 0 6px 14px rgba(255,122,31,0.28); }
  .home-play-hero:focus-visible{ outline:2px solid var(--parchment); outline-offset:3px; }
  .home-play-hero-label{
    font-family:var(--font-display); font-weight:400; font-size:24px; letter-spacing:0.04em;
    text-transform:uppercase; line-height:1.05; text-shadow:0 1px 0 rgba(0,0,0,0.28);
  }
  .home-play-hero-sub{ font-family:var(--font-body); font-size:13px; font-weight:700; color:rgba(255,255,255,0.92); }

  /* ---- Mode chips: pick what Play starts. Equal widths in one row on
     wide screens; wrap on narrow phones. ---- */
  .home-mode-chips{ display:flex; flex-wrap:wrap; gap:6px; }
  .home-mode-chip{
    flex:1 1 auto; min-height:40px; margin:0; padding:8px 12px; border-radius:999px;
    background:var(--drawer); color:var(--parchment); cursor:pointer;
    border:1px solid var(--drawer-edge); box-shadow:none;
    font-family:var(--font-body); font-weight:800; font-size:13.5px; letter-spacing:0.01em; white-space:nowrap;
    transition: background 0.14s ease, color 0.14s ease, border-color 0.14s ease;
  }
  .home-mode-chip:hover{ background:var(--drawer-hover); }
  .home-mode-chip.active{ background:var(--parchment); color:var(--cabinet-deep); border-color:var(--parchment); }
  .home-mode-chip:focus-visible{ outline:2px solid var(--parchment); outline-offset:2px; }

  /* ---- Era pills (only drawn when Prehistoric is unlocked and the mode
     honours the era). ---- */
  .home-play .home-era-row{ margin:0; }
  .home-play .home-era-label{ font-weight:800; }
  .home-play .home-era-btn{ font-weight:700; }
  .home-play .home-era-btn.active{ background:var(--mode-roll); color:var(--cabinet); }

  /* ---- Customize: a quiet text link to the full setup screen. ---- */
  .home-customize{
    align-self:flex-end; display:inline-flex; align-items:center; gap:6px;
    margin:0; padding:4px 2px; background:none; border:0; box-shadow:none; cursor:pointer;
    color:var(--parchment-dim); font-family:var(--font-body); font-weight:800; font-size:13px;
  }
  .home-customize:hover{ color:var(--parchment); }
  .home-customize:focus-visible{ outline:2px solid var(--parchment); outline-offset:2px; border-radius:6px; }

  /* ---- Today rows: one quiet shape - 40px art, title over a one-line
     meta, status pill on the right - on the raised surface with one
     hairline edge. The mode accent only tints the art frame and the
     pill; the Daily is no longer the orange primary. ---- */
  .home-drawer .home-today-row-chips{ display:grid; grid-template-columns:1fr; gap:8px; }
  .home-drawer .home-today-chip{
    --mode-accent:var(--blaze);
    display:grid; grid-template-columns:40px minmax(0,1fr) auto; grid-template-rows:auto auto;
    column-gap:12px; row-gap:2px; align-items:center;
    min-height:62px; padding:10px 12px 10px 10px; border-radius:12px;
    background:var(--drawer); color:var(--parchment);
    border:1px solid var(--drawer-edge); box-shadow:none; filter:none;
    transition: translate 0.14s ease, background 0.14s ease;
  }
  .home-drawer .home-today-chip:hover{ translate:0 -1px; background:var(--drawer-hover); filter:none; }
  .home-drawer .home-today-chip:active{ transform:none; translate:0 0; box-shadow:none; }
  .home-drawer .home-today-chip .home-today-chip-art{
    grid-column:1; grid-row:1 / span 2; width:40px; height:40px; border-radius:10px;
    background:color-mix(in srgb, var(--mode-accent) 16%, rgba(0,0,0,0.3));
    box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--mode-accent) 45%, transparent);
  }
  .home-drawer .home-today-chip .home-today-chip-label{
    grid-column:2; grid-row:1; font-size:14px; line-height:1.15; color:var(--parchment); text-shadow:none;
  }
  .home-drawer .home-today-chip .home-today-chip-meta{
    display:block; grid-column:2; grid-row:2; padding-right:0;
    font-size:12px; line-height:1.3; color:var(--parchment-dim);
    white-space:nowrap; overflow:hidden; text-overflow:ellipsis;
  }
  .home-drawer .home-today-chip .home-today-chip-foot{
    grid-column:3; grid-row:1 / span 2; width:auto; margin:0; gap:6px;
  }
  .home-drawer .home-today-chip .home-today-chip-status{
    font-size:11px; font-weight:700; padding:3px 9px; white-space:nowrap;
    background:color-mix(in srgb, var(--mode-accent) 24%, rgba(0,0,0,0.25)); color:var(--parchment);
  }
  .home-drawer .home-today-chip[data-today-chip="puzzle"]{ --mode-accent:var(--mode-puzzle); }
  .home-drawer .home-today-chip[data-today-chip="trivia"]{ --mode-accent:var(--mode-intel); }
  .home-drawer .home-today-chip[data-today-chip="fieldid"]{ --mode-accent:var(--mode-fieldid); }
  .home-drawer .home-today-chip.home-rival{ --mode-accent:var(--brass-bright); }
  .home-drawer .home-today-chip.home-rival .home-today-chip-label{ color:var(--parchment); }
  .home-drawer .home-today-chip.is-done{ opacity:0.7; }

  /* ================================================================
     Free tier (club launched): the day's game in a mode is played, so
     the Play buttons read as the way into the club - club gold instead
     of blaze - and a played chip carries a tick. Nothing here renders
     while the club is dark (see freePlayExhausted in 01-setup-and-pools).
     ================================================================ */
  .home-go-btn.is-spent,
  .home-play-hero.is-spent{
    background:linear-gradient(180deg, var(--brass-bright), color-mix(in srgb, var(--brass-bright) 70%, #8a6a12));
    color:var(--cabinet);
    box-shadow:0 3px 0 rgba(0,0,0,0.35), 0 12px 30px rgba(240,207,110,0.28);
  }
  .home-play-hero.is-spent .home-play-hero-label,
  .home-play-hero.is-spent .home-play-hero-sub{ color:var(--cabinet); text-shadow:none; }
  .home-mode-chip.is-played{ opacity:0.72; }
  .home-mode-chip.is-played.active{ opacity:1; }
  .home-mode-chip-tick{ margin-left:2px; font-size:12px; }
  .ov-btn.primary[data-free-plays="0"]{ background:var(--brass-bright); color:var(--cabinet); }

  /* ================================================================
     First-run round 2 (2026-09): fighter picker as a grid on phones,
     44px tap targets on the first screens, mode blurb, wrapped drawer
     rows and landing sub-list, plain Beginner draft header.
     ================================================================ */
  /* A. Fighter picker on a phone: every candidate on screen at once, two
     to a row, instead of a carousel that showed one and a half cards and
     cut the second name mid-word. Compact card: smaller label that wraps
     instead of truncating, the power tag inline, the edge chip on its own
     line with room kept for the (?) that rides the corner. */
  @media (max-width:640px){
    .fight-picker{
      display:grid; grid-template-columns:1fr 1fr; gap:8px;
      overflow:visible; scroll-snap-type:none; padding:2px 0 4px;
    }
    .fight-picker > .fight-pick-slot{ flex:none; scroll-snap-align:none; min-width:0; }
    .fight-picker > .fight-pick-slot > .opponent-btn{ padding:8px 8px 9px; gap:4px; min-height:44px; }
    .fight-picker .opponent-name{
      font-size:11px; line-height:1.3; letter-spacing:0.02em;
      white-space:normal; overflow-wrap:anywhere;
    }
    .fight-picker .fight-power-tag{ font-size:10px; padding:1px 5px; margin-left:2px; }
    .fight-picker .fight-matchup{ margin-top:4px; padding-right:26px; }
    .fight-picker .fight-matchup-edge{ font-size:9.5px; padding:2px 6px; }
    .fight-picker .smart-call-tag{ left:6px; }
  }

  /* B. Tap targets: 44px hit areas without larger visuals. An absolutely
     positioned ::before extends the area a finger can land on past the
     drawn box; the buttons carry no other ::before. Rows and chips that
     already fill their width simply grow to 44px tall. */
  .top-actions > .how-to-play-fab, .top-actions > .leaderboard-btn,
  .card-menu-btn, .home-drawer-back, .home-drawer-close,
  .install-banner-dismiss, .install-banner-go{ position:relative; }
  .top-actions > .how-to-play-fab::before, .top-actions > .leaderboard-btn::before,
  .card-menu-btn::before, .home-drawer-back::before, .home-drawer-close::before,
  .install-banner-dismiss::before, .install-banner-go::before,
  .card-info-btn::before{ content:""; position:absolute; }
  @media (max-width:560px){
    /* The pills clip their overflow for the desktop label's ellipsis; on a
       phone the label is already hidden by its own clip, so the pill can
       let the landing zone show through. */
    .top-actions > .how-to-play-fab, .top-actions > .leaderboard-btn{ overflow:visible; }
    .top-actions > .how-to-play-fab::before, .top-actions > .leaderboard-btn::before{ inset:-5px 0; }
  }
  .card-menu-btn::before{ inset:-9px -4px; }
  .home-drawer-back::before{ inset:-7px -4px; }
  .home-drawer-close::before{ inset:-7px -6px; }
  .install-banner-dismiss::before, .install-banner-go::before{ inset:-7px -4px; }
  /* The (i) on a draft card and the (?) beside a fighter: a 22px circle
     with a 44px landing zone around it. */
  .card-info-btn::before{ inset:-11px; border-radius:50%; }
  /* The (?) rides a fighter card's corner; a wider right margin keeps its
     zone inside the slot rather than past the grid's edge. */
  .fight-pick-slot > .fight-matchup-info{ margin-right:11px; }
  .home-progress{ min-height:44px; }
  .home-mode-chip{ min-height:44px; }

  /* C. The selected mode's one-line description under the chip row. */
  .home-mode-blurb{
    margin:-2px 0 0; padding:0 4px;
    font-family:var(--font-body); font-size:12.5px; line-height:1.35; color:var(--parchment-dim);
  }

  /* D. Drawer rows wrap to a second line and end on a word instead of
     cutting a teaser mid-word with an ellipsis; the landing's "More ways
     to play" sub-list, now the full drawer order, wraps the same way. */
  .home-drawer .home-today-chip .home-today-chip-meta{
    white-space:normal; display:-webkit-box; -webkit-line-clamp:2; -webkit-box-orient:vertical;
    overflow:hidden; overflow-wrap:anywhere;
  }
  .home-door-mini-sub.home-door-mini-sub-wrap{ white-space:normal; overflow:visible; text-overflow:clip; line-height:1.3; }

  /* E. Beginner's draft header is a sentence ("Round 1 of 5"), not a
     letter-spaced stamp. */
  .draft-header-plain{ letter-spacing:0.02em; font-size:12px; }

  /* The club sheet must sit above the Encyclopedia (same overlay class):
     its Expert-notes teaser opens the club, and both were z-index 1250, so
     the sheet opened underneath. */
  #clubOverlay{ z-index:1260; }
  /* A share textarea is hidden until Share is pressed on every surface,
     not only the trivia row (the fight result showed an empty box). */
  .share-score-text[hidden]{ display:none; }
