/* ==================================================================
   BREATH OF ESTOVA, AAA DETAIL LAYER
   Loaded after styles.css. Additive only, nothing here renames or
   removes an existing class, so it can be pulled out by deleting one
   <link> if the client wants the flatter look back.

   Built against the BOE AAA Detail Standard (sword benchmark):
     1. no naked flat fill larger than ~24px
     2. every surface is a material, 3+ tone hue-shifted ramp + 1px shine
     3. depth chrome on every plate, button, slot and row
     4. dedicated ornament pass, corner ticks / spurs / pips / flourishes
     5. icons are solid silhouettes with interior tones and a glint
     6. staged subjects, glow + pedestal + shadow
     7. roughly one decorated element per 60x60 region
   ================================================================== */

:root {
  /* extra ramp stops so every material has 4+ tones */
  --wood-hi:      #c08d63;
  --wood-lo:      #2a1a0c;
  --gold-pale:    #fff0bd;
  --gold-sheen:   rgba(255, 240, 189, 0.85);
  --navy:         #141024;
  --navy-soft:    #241c38;
  --parch-fiber:  rgba(120, 88, 46, 0.10);
  --stone-speck:  rgba(61, 40, 17, 0.16);

  --chrome-out:   3px;
  --ease-pixel:   steps(6, end);
  --ease-soft:    cubic-bezier(0.22, 0.61, 0.36, 1);
}

/* ------------------------------------------------------------------
   0. MATERIALS
   Each is a stack: grain + ramp + shine. Never a single flat colour.
   ------------------------------------------------------------------ */

/* wood: vertical plank grain + knot speckle + horizontal ramp */
.section--wood,
.mat-wood {
  background-image:
    /* knots */
    radial-gradient(ellipse 3px 9px at 12% 30%, rgba(0,0,0,0.22), transparent 70%),
    radial-gradient(ellipse 4px 12px at 63% 68%, rgba(0,0,0,0.18), transparent 70%),
    radial-gradient(ellipse 3px 8px at 86% 22%, rgba(0,0,0,0.20), transparent 70%),
    /* plank grain */
    repeating-linear-gradient(90deg,
      rgba(0,0,0,0.10) 0 2px,
      transparent 2px 6px,
      rgba(255,255,255,0.035) 6px 7px,
      transparent 7px 13px),
    /* long grain streaks */
    repeating-linear-gradient(88deg,
      rgba(0,0,0,0.05) 0 1px,
      transparent 1px 24px),
    /* vertical ramp */
    linear-gradient(180deg,
      var(--wood-light) 0%,
      var(--wood) 14%,
      var(--wood-dark) 62%,
      var(--wood-deep) 100%);
}

/* parchment: paper fibre in two directions + edge vignette */
.section--parchment,
.mat-parch {
  background-image:
    repeating-linear-gradient(52deg,
      var(--parch-fiber) 0 1px, transparent 1px 5px),
    repeating-linear-gradient(-38deg,
      var(--parch-fiber) 0 1px, transparent 1px 7px),
    radial-gradient(ellipse 120% 90% at 50% 40%,
      var(--parchment-lt) 0%,
      var(--parchment) 58%,
      var(--parchment-dk) 100%);
}

/* stone: speckle field + hairline cracks */
.mat-stone {
  background-image:
    radial-gradient(1px 1px at 18% 22%, var(--stone-speck), transparent),
    radial-gradient(1px 1px at 71% 14%, var(--stone-speck), transparent),
    radial-gradient(1px 1px at 44% 61%, var(--stone-speck), transparent),
    radial-gradient(1px 1px at 88% 77%, var(--stone-speck), transparent),
    radial-gradient(1px 1px at 29% 84%, var(--stone-speck), transparent),
    linear-gradient(180deg, var(--stone) 0%, var(--stone-dark) 100%);
}

/* metal: 4 band gloss with a hard 1px shine line */
.mat-gold {
  background-image:
    linear-gradient(180deg,
      var(--gold-pale)   0%,
      var(--gold-bright) 18%,
      var(--gold)        34%,
      var(--gold)        46%,
      var(--gold-dark)   47%,
      var(--gold-dark)   78%,
      var(--gold-deep)   100%);
}

/* ------------------------------------------------------------------
   1. DEPTH CHROME
   outline + bevel top + shade bottom + corner treatment
   ------------------------------------------------------------------ */

/* corner ticks, 8 background slices, no extra markup */
.aaa-ticks { position: relative; }
.aaa-ticks::after {
  content: '';
  position: absolute;
  inset: 5px;
  pointer-events: none;
  z-index: 2;
  background-repeat: no-repeat;
  background-image:
    linear-gradient(var(--gold-dark), var(--gold-dark)),
    linear-gradient(var(--gold-dark), var(--gold-dark)),
    linear-gradient(var(--gold-dark), var(--gold-dark)),
    linear-gradient(var(--gold-dark), var(--gold-dark)),
    linear-gradient(var(--gold-dark), var(--gold-dark)),
    linear-gradient(var(--gold-dark), var(--gold-dark)),
    linear-gradient(var(--gold-dark), var(--gold-dark)),
    linear-gradient(var(--gold-dark), var(--gold-dark));
  background-size: 9px 2px, 2px 9px, 9px 2px, 2px 9px,
                   9px 2px, 2px 9px, 9px 2px, 2px 9px;
  background-position:
    left top, left top,
    right top, right top,
    left bottom, left bottom,
    right bottom, right bottom;
  opacity: 0.75;
}
.section--wood .aaa-ticks::after { opacity: 0.55; }

/* filigree corner spurs, the client's own motif, 4 detached pixels */
.aaa-spurs::before {
  content: '';
  position: absolute;
  inset: -5px;
  pointer-events: none;
  background-repeat: no-repeat;
  background-size: 3px 3px;
  background-image:
    linear-gradient(var(--gold), var(--gold)),
    linear-gradient(var(--gold), var(--gold)),
    linear-gradient(var(--gold), var(--gold)),
    linear-gradient(var(--gold), var(--gold));
  background-position: left top, right top, left bottom, right bottom;
}

/* recessed well, for anything that holds a value or an icon */
.aaa-well {
  background-image:
    repeating-linear-gradient(45deg,
      rgba(0,0,0,0.05) 0 3px, transparent 3px 6px),
    linear-gradient(180deg, rgba(0,0,0,0.30), rgba(0,0,0,0.10));
  box-shadow:
    inset 0 3px 0 rgba(0,0,0,0.42),
    inset 0 -2px 0 rgba(255,255,255,0.10),
    inset 2px 0 0 rgba(0,0,0,0.20),
    inset -2px 0 0 rgba(0,0,0,0.20);
}

/* ------------------------------------------------------------------
   2. BUTTONS, banded bevel + gloss sweep + press travel
   ------------------------------------------------------------------ */
.btn {
  position: relative;
  overflow: hidden;
  isolation: isolate;
  transition: transform 0.11s var(--ease-soft), filter 0.15s var(--ease-soft);
}
/* v1 gloss sweep removed, superseded by the v2 plate in section 21 */
.btn:hover  { transform: translateY(-2px); filter: brightness(1.06); }
.btn:active { transform: translateY(2px); filter: brightness(0.95); }

.btn--primary, .btn--copper, .btn--lg {
  background-image:
    linear-gradient(180deg,
      rgba(255,255,255,0.20) 0%,
      rgba(255,255,255,0.06) 34%,
      rgba(0,0,0,0.03) 36%,
      rgba(0,0,0,0.08) 70%,
      rgba(0,0,0,0.18) 100%);
}

/* filigree rule under the big CTA label only */
.btn--lg::before {
  content: '';
  position: absolute;
  left: 12px; right: 12px; bottom: 6px;
  height: 1px;
  pointer-events: none;
  background: linear-gradient(90deg, transparent, rgba(0,0,0,0.18), transparent);
}

/* ------------------------------------------------------------------
   3. TITLE PLATES, eyebrow ribbons get flourishes
   ------------------------------------------------------------------ */
.section__eyebrow {
  position: relative;
  background-image:
    linear-gradient(180deg,
      var(--gold-bright) 0%, var(--gold) 40%,
      var(--gold-dark) 41%, var(--gold-deep) 100%) !important;
}
.section__eyebrow::before,
.section__eyebrow::after {
  content: '';
  position: absolute;
  top: 50%;
  width: 16px; height: 3px;
  transform: translateY(-50%);
  background: linear-gradient(90deg, transparent, var(--gold-dark));
}
.section__eyebrow::before { right: 100%; margin-right: 8px; }
.section__eyebrow::after  { left: 100%;  margin-left: 8px;
                            background: linear-gradient(270deg, transparent, var(--gold-dark)); }

/* headings get a hard pixel drop, no soft blur */
.section h2, .pack-hero__title, h1 {
  text-shadow:
     2px 2px 0 rgba(0,0,0,0.28),
     0 1px 0 rgba(255,255,255,0.22);
}
.section--wood h2, .section--wood h1, .pack-hero__title {
  text-shadow:
     2px 2px 0 var(--wood-black),
     0 1px 0 rgba(255,255,255,0.18),
     0 0 18px rgba(244, 199, 94, 0.18);
}

/* ------------------------------------------------------------------
   4. NAV, brass rail with rivets
   ------------------------------------------------------------------ */
.nav__inner { position: relative; }
.nav__inner::after {
  content: '';
  position: absolute;
  left: 0; right: 0; bottom: -4px;
  height: 3px;
  pointer-events: none;
  background:
    repeating-linear-gradient(90deg,
      var(--gold-dark) 0 3px,
      transparent 3px 26px),
    linear-gradient(180deg, var(--gold-deep), transparent);
  opacity: 0.5;
}
.nav__link { position: relative; }
.nav__link:not(.is-active)::before {
  content: '';
  position: absolute;
  left: 50%; bottom: 4px;
  width: 0; height: 2px;
  background: var(--gold);
  transform: translateX(-50%);
  transition: width 0.2s var(--ease-soft);
}
.nav__link:not(.is-active):hover::before { width: 62%; }
.nav__social { position: relative; overflow: hidden; }
.nav__social::after {
  content: '';
  position: absolute; inset: 0;
  background: linear-gradient(180deg, rgba(255,255,255,0.18), transparent 55%);
  pointer-events: none;
}

/* ------------------------------------------------------------------
   5. TRADING CARDS, staged like the in-game cards
   ------------------------------------------------------------------ */
.tcard {
  position: relative;
  isolation: isolate;
  background-image:
    repeating-linear-gradient(52deg, var(--parch-fiber) 0 1px, transparent 1px 5px),
    linear-gradient(180deg, var(--parchment-lt) 0%, var(--parchment) 70%, var(--parchment-dk) 100%);
  transition: transform 0.18s var(--ease-soft), box-shadow 0.18s var(--ease-soft);
}
.tcard::before {                        /* inner double trim */
  content: '';
  position: absolute;
  inset: 4px;
  pointer-events: none;
  border: 1px solid var(--parchment-edge);
  box-shadow: inset 0 0 0 1px rgba(255,255,255,0.45);
}
.tcard:hover {
  transform: translateY(-6px);
  box-shadow:
    inset 0 4px 0 rgba(255,255,255,0.45),
    0 11px 0 var(--wood-deep),
    0 0 22px rgba(0,0,0,0.28);
  z-index: 3;
}

/* rarity glow burst behind the art window */
.tcard__art { position: relative; overflow: hidden; }
.tcard__art::before {
  content: '';
  position: absolute;
  inset: -25%;
  pointer-events: none;
  background: radial-gradient(circle at 50% 55%,
    var(--glow, rgba(255,255,255,0)) 0%, transparent 62%);
  opacity: 0.9;
}
.tcard--common   { --glow: rgba(160,155,140,0.35); }
.tcard--uncommon { --glow: rgba(79,184,158,0.40); }
.tcard--rare     { --glow: rgba(74,127,201,0.45); }
.tcard--legend   { --glow: rgba(244,199,94,0.55); }

/* rarity pips instead of a bare word */
.tcard__rar { position: relative; padding-left: 13px; }
.tcard__rar::before {
  content: '';
  position: absolute;
  left: 0; top: 50%;
  width: 7px; height: 7px;
  margin-top: -3.5px;
  transform: rotate(45deg);
  background: currentColor;
  box-shadow: inset -1px -1px 0 rgba(0,0,0,0.35), 0 0 0 1px rgba(0,0,0,0.25);
}

/* legendary gets the full staging: gold ring, dust, pulse */
.tcard--legend { position: relative; }
.tcard--legend::after {
  content: '';
  position: absolute;
  inset: -3px;
  pointer-events: none;
  border: 2px solid var(--gold);
  box-shadow: 0 0 14px rgba(244,199,94,0.55), inset 0 0 10px rgba(244,199,94,0.25);
  animation: aaa-ringpulse 2.6s ease-in-out infinite;
}
@keyframes aaa-ringpulse {
  0%, 100% { opacity: 0.45; }
  50%      { opacity: 1; }
}
.tcard--legend .tcard__art::after {     /* legendary dust */
  content: '';
  position: absolute;
  inset: 0;
  pointer-events: none;
  background-repeat: no-repeat;
  background-size: 2px 2px;
  background-image:
    linear-gradient(var(--gold-pale), var(--gold-pale)),
    linear-gradient(var(--gold-pale), var(--gold-pale)),
    linear-gradient(var(--gold-pale), var(--gold-pale)),
    linear-gradient(var(--gold-pale), var(--gold-pale));
  background-position: 18% 24%, 74% 18%, 61% 72%, 31% 81%;
  animation: aaa-twinkle 2.2s steps(4, end) infinite;
}
@keyframes aaa-twinkle {
  0%, 100% { opacity: 0.25; }
  40%      { opacity: 1; }
  70%      { opacity: 0.5; }
}

/* ------------------------------------------------------------------
   6. ART SLOTS, staged pedestal instead of a bare dashed box
   ------------------------------------------------------------------ */
.art-slot {
  position: relative;
  overflow: hidden;
  background-image:
    repeating-linear-gradient(45deg,
      rgba(0,0,0,0.055) 0 6px, transparent 6px 12px),
    repeating-linear-gradient(-45deg,
      rgba(255,255,255,0.05) 0 6px, transparent 6px 12px),
    radial-gradient(ellipse 70% 46% at 50% 92%, rgba(0,0,0,0.22), transparent 70%),
    linear-gradient(180deg, var(--parchment) 0%, var(--parchment-dk) 100%);
}
.section--wood .art-slot {
  background-image:
    repeating-linear-gradient(45deg,
      rgba(0,0,0,0.14) 0 6px, transparent 6px 12px),
    radial-gradient(ellipse 70% 46% at 50% 92%, rgba(0,0,0,0.34), transparent 70%),
    linear-gradient(180deg, rgba(255,255,255,0.06) 0%, rgba(0,0,0,0.26) 100%);
}

/* pack hero art floats and casts a lit pedestal */
.pack-hero__slot {
  animation: aaa-float 5.5s ease-in-out infinite;
}
.pack-hero__art { position: relative; }
.pack-hero__art::before {
  content: '';
  position: absolute;
  left: 50%; bottom: -18px;
  width: 62%; height: 26px;
  transform: translateX(-50%);
  pointer-events: none;
  background: radial-gradient(ellipse at center,
    rgba(244,199,94,0.30) 0%, rgba(0,0,0,0.30) 45%, transparent 72%);
  animation: aaa-pedestal 5.5s ease-in-out infinite;
}
@keyframes aaa-float {
  0%, 100% { transform: translateY(0); }
  50%      { transform: translateY(-10px); }
}
@keyframes aaa-pedestal {
  0%, 100% { transform: translateX(-50%) scaleX(1);    opacity: 0.85; }
  50%      { transform: translateX(-50%) scaleX(0.88); opacity: 0.6; }
}

/* ------------------------------------------------------------------
   7. COUNTDOWN, brass digits in recessed wells
   ------------------------------------------------------------------ */
.cd__unit {
  position: relative;
  background-image:
    repeating-linear-gradient(90deg,
      rgba(0,0,0,0.08) 0 2px, transparent 2px 7px),
    linear-gradient(180deg, var(--wood) 0%, var(--wood-deep) 55%, var(--wood-black) 100%);
}
.cd__unit::before {
  content: '';
  position: absolute;
  inset: 4px 5px 22px;
  pointer-events: none;
  box-shadow:
    inset 0 3px 0 rgba(0,0,0,0.45),
    inset 0 -2px 0 rgba(255,255,255,0.08);
  background: linear-gradient(180deg, rgba(0,0,0,0.28), transparent 60%);
}
.cd__unit b { text-shadow: 0 2px 0 rgba(0,0,0,0.6), 0 0 12px rgba(244,199,94,0.35); }
.cd__soon {
  background-image:
    repeating-linear-gradient(90deg,
      rgba(0,0,0,0.08) 0 2px, transparent 2px 7px),
    linear-gradient(180deg, var(--wood) 0%, var(--wood-deep) 100%);
}

/* ------------------------------------------------------------------
   8. TIERS, framed like reward cards
   ------------------------------------------------------------------ */
.tier {
  position: relative;
  background-image:
    repeating-linear-gradient(52deg, var(--parch-fiber) 0 1px, transparent 1px 5px),
    linear-gradient(180deg, var(--parchment-lt) 0%, var(--parchment) 62%, var(--parchment-dk) 100%);
  transition: transform 0.2s var(--ease-soft), box-shadow 0.2s var(--ease-soft);
}
.tier:hover { transform: translateY(-5px); }
.tier::after {                         /* inner gold hairline */
  content: '';
  position: absolute;
  inset: 6px;
  pointer-events: none;
  border: 1px solid var(--parchment-edge);
}
.tier--featured::after { border-color: var(--gold-dark); box-shadow: inset 0 0 16px rgba(244,199,94,0.20); }
.tier__flag {
  background-image: linear-gradient(180deg,
    var(--gold-bright) 0%, var(--gold) 42%, var(--gold-dark) 43%, var(--gold-deep) 100%) !important;
}
.tier__list li::before { text-shadow: 0 1px 0 rgba(255,255,255,0.5); }
.tier__price { text-shadow: 1px 1px 0 rgba(255,255,255,0.55); }

/* pay buttons, riveted metal plates */
.pay__btn {
  position: relative;
  background-image:
    linear-gradient(180deg, #c2bcab 0%, var(--stone-dark) 48%, #6d6857 100%);
}

/* ------------------------------------------------------------------
   9. SLOTS, rarity-tinted pinwheel wells
   ------------------------------------------------------------------ */
.slot {
  position: relative;
  background-image:
    repeating-linear-gradient(45deg,
      rgba(255,255,255,0.035) 0 3px, transparent 3px 6px),
    radial-gradient(ellipse 80% 60% at 50% 38%, rgba(255,255,255,0.05), transparent 70%),
    linear-gradient(180deg, rgba(0,0,0,0.32), rgba(0,0,0,0.14));
}
.slot--filled {
  background-image:
    repeating-linear-gradient(45deg,
      rgba(255,255,255,0.06) 0 3px, transparent 3px 6px),
    radial-gradient(circle at 50% 40%, rgba(253,224,138,0.38), transparent 66%),
    linear-gradient(180deg, var(--gold-dark) 0%, var(--gold-deep) 55%, #4a3a10 100%);
  box-shadow:
    inset 0 4px 0 rgba(255,255,255,0.16),
    inset 0 -4px 0 rgba(0,0,0,0.35),
    0 0 16px rgba(244,199,94,0.30);
}
.slot::after {                       /* corner notches */
  content: '';
  position: absolute;
  inset: 4px;
  pointer-events: none;
  background-repeat: no-repeat;
  background-size: 6px 2px, 2px 6px, 6px 2px, 2px 6px,
                   6px 2px, 2px 6px, 6px 2px, 2px 6px;
  background-image:
    linear-gradient(var(--gold), var(--gold)),
    linear-gradient(var(--gold), var(--gold)),
    linear-gradient(var(--gold), var(--gold)),
    linear-gradient(var(--gold), var(--gold)),
    linear-gradient(var(--gold), var(--gold)),
    linear-gradient(var(--gold), var(--gold)),
    linear-gradient(var(--gold), var(--gold)),
    linear-gradient(var(--gold), var(--gold));
  background-position:
    left top, left top, right top, right top,
    left bottom, left bottom, right bottom, right bottom;
  opacity: 0.55;
}

/* ------------------------------------------------------------------
   10. RARITY PLATES + STAT PLATES + STEPS
   ------------------------------------------------------------------ */
.rarity {
  position: relative;
  background-image:
    repeating-linear-gradient(52deg, var(--parch-fiber) 0 1px, transparent 1px 5px),
    linear-gradient(180deg, var(--parchment-lt), var(--parchment-dk));
  transition: transform 0.16s var(--ease-soft);
}
.rarity:hover { transform: translateY(-3px); }
.rarity__chip { background-image: linear-gradient(180deg, rgba(255,255,255,0.28), rgba(0,0,0,0.22)); }
.rarity__stars { text-shadow: 0 1px 0 rgba(255,255,255,0.6); }
.rarity--legend .rarity__stars { animation: aaa-twinkle 3s steps(4,end) infinite; }

.pack-stat {
  position: relative;
  background-image:
    repeating-linear-gradient(45deg, rgba(0,0,0,0.06) 0 4px, transparent 4px 8px),
    linear-gradient(180deg, rgba(0,0,0,0.30), rgba(0,0,0,0.12));
  box-shadow:
    inset 0 3px 0 rgba(0,0,0,0.35),
    inset 0 -2px 0 rgba(255,255,255,0.09);
}
.pack-stat b { text-shadow: 0 2px 0 rgba(0,0,0,0.55); }

.step {
  position: relative;
  background-image:
    repeating-linear-gradient(52deg, var(--parch-fiber) 0 1px, transparent 1px 5px),
    linear-gradient(180deg, var(--parchment-lt), var(--parchment-dk));
}
.step::after {
  content: '';
  position: absolute;
  inset: 5px;
  pointer-events: none;
  border: 1px solid var(--parchment-edge);
  opacity: 0.7;
}
.step:hover::before { animation: aaa-nudge 0.4s var(--ease-soft); }
@keyframes aaa-nudge {
  0%, 100% { transform: translateY(0); }
  50%      { transform: translateY(-3px); }
}

/* ------------------------------------------------------------------
   11. INCLUSION CARDS
   ------------------------------------------------------------------ */
.incl {
  position: relative;
  background-image:
    repeating-linear-gradient(90deg, rgba(0,0,0,0.07) 0 2px, transparent 2px 7px),
    linear-gradient(180deg, rgba(0,0,0,0.14), rgba(0,0,0,0.30));
  transition: transform 0.18s var(--ease-soft), box-shadow 0.18s var(--ease-soft);
}
.incl:hover {
  transform: translateY(-4px);
  box-shadow: inset 0 4px 0 var(--wood), 0 8px 0 rgba(0,0,0,0.30);
}
.incl__name { text-shadow: 0 2px 0 rgba(0,0,0,0.5); }

/* ------------------------------------------------------------------
   12. INPUTS
   ------------------------------------------------------------------ */
.capture__input {
  background-image:
    repeating-linear-gradient(52deg, var(--parch-fiber) 0 1px, transparent 1px 5px),
    linear-gradient(180deg, var(--parchment-dk) 0%, var(--parchment-lt) 22%);
  box-shadow:
    inset 0 4px 0 rgba(0,0,0,0.16),
    inset 0 -2px 0 rgba(255,255,255,0.55);
}

/* ------------------------------------------------------------------
   13. TABLES, alternating grain rows
   ------------------------------------------------------------------ */
.lb-table tbody tr:nth-child(odd)  { background-image: linear-gradient(180deg, rgba(0,0,0,0.03), transparent); }
.lb-table tbody tr:nth-child(even) { background-image: linear-gradient(180deg, rgba(255,255,255,0.20), transparent); }
.lb-table tbody tr { transition: background-color 0.12s var(--ease-soft); }
.lb-table tbody tr:hover { background-color: rgba(244,199,94,0.22); }

/* ------------------------------------------------------------------
   14. SCROLL REVEAL, driven by site.js
   ------------------------------------------------------------------ */
/* THE HIDING IS SCOPED TO `.js-reveal` ON PURPOSE.
   ----------------------------------------------------------------------------
   This used to hide every [data-reveal] element unconditionally and rely on
   JavaScript to bring it back. That means the default state of the page is
   invisible, and anything that stops the script, a blocked file, a stray error
   above it, an observer that never fires, leaves real content at opacity 0 with
   no sign that anything went wrong. On a page whose job is to sell something,
   invisible beats misaligned as the worst possible failure.

   site.js adds `.js-reveal` to <html> before it observes anything. So the
   animation is an enhancement layered on top of a page that is already
   readable, rather than the thing that makes it readable. */
.js-reveal [data-reveal] {
  opacity: 0;
  transform: translateY(18px);
  transition: opacity 0.5s var(--ease-soft), transform 0.5s var(--ease-soft);
  will-change: opacity, transform;
}
.js-reveal [data-reveal].is-in { opacity: 1; transform: none; }
.js-reveal [data-reveal][data-reveal-d="1"] { transition-delay: 0.07s; }
.js-reveal [data-reveal][data-reveal-d="2"] { transition-delay: 0.14s; }
.js-reveal [data-reveal][data-reveal-d="3"] { transition-delay: 0.21s; }
.js-reveal [data-reveal][data-reveal-d="4"] { transition-delay: 0.28s; }
.js-reveal [data-reveal][data-reveal-d="5"] { transition-delay: 0.35s; }

/* ------------------------------------------------------------------
   15. SECTION EDGES, carved rails between bands
   ------------------------------------------------------------------ */
.section { position: relative; }
.section--wood::before,
.section--parchment::before {
  content: '';
  position: absolute;
  top: 0; left: 0; right: 0;
  height: 6px;
  pointer-events: none;
  background:
    repeating-linear-gradient(90deg,
      rgba(0,0,0,0.20) 0 4px, transparent 4px 12px),
    linear-gradient(180deg, rgba(0,0,0,0.28), transparent);
}
.section--wood::after,
.section--parchment::after {
  content: '';
  position: absolute;
  bottom: 0; left: 0; right: 0;
  height: 5px;
  pointer-events: none;
  background: linear-gradient(0deg, rgba(0,0,0,0.22), transparent);
}

/* ------------------------------------------------------------------
   16. FINE PRINT + FOOTER
   ------------------------------------------------------------------ */
.fineprint {
  position: relative;
  background-image:
    repeating-linear-gradient(45deg, rgba(0,0,0,0.05) 0 5px, transparent 5px 10px),
    linear-gradient(180deg, rgba(0,0,0,0.18), rgba(0,0,0,0.08));
}
.footer {
  background-image:
    repeating-linear-gradient(90deg,
      rgba(0,0,0,0.10) 0 2px, transparent 2px 6px, rgba(255,255,255,0.03) 6px 7px, transparent 7px 13px),
    linear-gradient(180deg, var(--wood-dark) 0%, var(--wood-deep) 70%, var(--wood-black) 100%);
}

/* ------------------------------------------------------------------
   17. MOTION SAFETY, mandatory
   ------------------------------------------------------------------ */
@media (prefers-reduced-motion: reduce) {
  .pack-hero__slot,
  .pack-hero__art::before,
  .tcard--legend::after,
  .tcard--legend .tcard__art::after,
  .rarity--legend .rarity__stars { animation: none !important; }
  .js-reveal [data-reveal] { opacity: 1 !important; transform: none !important; transition: none !important; }
  .btn::after { transition: none; }
  .btn:hover, .tcard:hover, .tier:hover, .incl:hover, .rarity:hover { transform: none; }
}

/* ------------------------------------------------------------------
   18. MOBILE, drop the heaviest layers so paint stays cheap
   ------------------------------------------------------------------ */
@media (max-width: 620px) {
  .aaa-ticks::after,
  .aaa-spurs::before { display: none; }
  .pack-hero__slot { animation: none; }
  .tcard:hover, .tier:hover, .incl:hover { transform: none; }
}


/* ------------------------------------------------------------------
   19. SLATE BAND, third material so no section falls back to flat sky
   ------------------------------------------------------------------ */
.section--slate {
  position: relative;
  background-image:
    radial-gradient(1px 1px at 14% 26%, rgba(0,0,0,0.20), transparent),
    radial-gradient(1px 1px at 63% 12%, rgba(0,0,0,0.18), transparent),
    radial-gradient(1px 1px at 39% 68%, rgba(0,0,0,0.20), transparent),
    radial-gradient(1px 1px at 84% 74%, rgba(0,0,0,0.18), transparent),
    radial-gradient(1px 1px at 26% 88%, rgba(0,0,0,0.16), transparent),
    repeating-linear-gradient(90deg,
      rgba(0,0,0,0.05) 0 1px, transparent 1px 34px),
    repeating-linear-gradient(0deg,
      rgba(255,255,255,0.025) 0 1px, transparent 1px 46px),
    linear-gradient(180deg, #6d6250 0%, #554b3c 46%, #3f382c 100%);
  border-top: 4px solid var(--wood-deep);
  border-bottom: 4px solid var(--wood-deep);
  color: var(--cream);
}
.section--slate h2,
.section--slate h3 { color: var(--cream); text-shadow: 2px 2px 0 rgba(0,0,0,0.45); }
.section--slate .section__lead { color: var(--cream-soft); }
.section--slate .art-slot {
  background-image:
    repeating-linear-gradient(45deg, rgba(0,0,0,0.14) 0 6px, transparent 6px 12px),
    radial-gradient(ellipse 70% 46% at 50% 92%, rgba(0,0,0,0.30), transparent 70%),
    linear-gradient(180deg, rgba(255,255,255,0.07) 0%, rgba(0,0,0,0.24) 100%);
  border-color: rgba(0,0,0,0.35);
  color: var(--cream-soft);
}
.section--slate .fineprint { color: var(--cream-soft); border-left-color: var(--wood); }
.section--slate::before {
  content: '';
  position: absolute; top: 0; left: 0; right: 0; height: 6px;
  pointer-events: none;
  background:
    repeating-linear-gradient(90deg, rgba(0,0,0,0.20) 0 4px, transparent 4px 12px),
    linear-gradient(180deg, rgba(0,0,0,0.30), transparent);
}

/* ==================================================================
   20. REAL GAME ART
   Pulled from BOE Panel redesigns. Pixel art must never be smoothed.
   ================================================================== */

img, .shot, .mount-still, .mount-anim__f,
.showcase__item-icon img { image-rendering: pixelated; }

/* framed screenshots, staged like an in-game preview */
.shot {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: center;
}
.usp-card__image,
.showcase__image,
.gameplay-card__image,
.area-promo__image { position: relative; overflow: hidden; }

/* glass sheen + vignette over every screenshot so it reads as a framed plate */
.usp-card__image::after,
.showcase__image::after,
.gameplay-card__image::after {
  content: '';
  position: absolute; inset: 0;
  pointer-events: none;
  background:
    linear-gradient(190deg, rgba(255,255,255,0.16) 0%, transparent 26%),
    radial-gradient(ellipse 120% 90% at 50% 45%, transparent 55%, rgba(0,0,0,0.34) 100%);
}
.usp-card:hover .shot,
.gameplay-card:hover .shot { transform: scale(1.03); }
.shot { transition: transform 0.4s var(--ease-soft); }

/* ---------------- life skill icons, game medallions ---------------- */
.showcase__item-icon {
  position: relative;
  display: grid !important;
  place-items: center;
  width: 40px; height: 40px;
  flex: 0 0 40px;
  background-image:
    radial-gradient(circle at 50% 34%, rgba(253,224,138,0.28), transparent 68%),
    linear-gradient(180deg, var(--parchment-dk) 0%, var(--wood) 60%, var(--wood-dark) 100%);
  border: 3px solid var(--wood-deep);
  box-shadow:
    inset 0 3px 0 rgba(255,255,255,0.28),
    inset 0 -3px 0 rgba(0,0,0,0.35),
    0 2px 0 var(--wood-black);
}
.showcase__item-icon::after {            /* corner ticks on the medallion */
  content: '';
  position: absolute; inset: 2px;
  pointer-events: none;
  background-repeat: no-repeat;
  background-size: 5px 1px, 1px 5px, 5px 1px, 1px 5px;
  background-image:
    linear-gradient(var(--gold), var(--gold)), linear-gradient(var(--gold), var(--gold)),
    linear-gradient(var(--gold), var(--gold)), linear-gradient(var(--gold), var(--gold));
  background-position: left top, left top, right bottom, right bottom;
  opacity: 0.65;
}
.showcase__item-icon img {
  width: 30px; height: 30px;
  filter: drop-shadow(0 1px 0 rgba(0,0,0,0.5));
}
.showcase__item.is-active .showcase__item-icon {
  background-image:
    radial-gradient(circle at 50% 34%, rgba(255,243,196,0.55), transparent 70%),
    linear-gradient(180deg, var(--gold-bright) 0%, var(--gold) 45%, var(--gold-dark) 100%);
  box-shadow:
    inset 0 3px 0 rgba(255,255,255,0.55),
    inset 0 -3px 0 var(--gold-deep),
    0 2px 0 var(--wood-deep),
    0 0 14px rgba(244,199,94,0.5);
}
.showcase__item { transition: transform 0.15s var(--ease-soft); }
.showcase__item:hover { transform: translateX(3px); }

/* ---------------- mounts ---------------- */
.mount-card__image {
  position: relative;
  display: grid;
  place-items: center;
  overflow: visible;
  background: transparent;
}

/* P2-122. The mount cards used to be a CSS colour wash with a per-card glow
   tint, which is what Ketema asked to replace: five cards lit five different
   colours read as five decisions rather than as one set. Each card now carries
   a drawn scene behind its mount.

   The scene sits on the CARD, not inside .mount-card__image. That box is only
   as tall as the sprite it centres, roughly 300px of a 520px card, so a
   background placed inside it covered the top half and left the name plate
   sitting on bare wood.

   `cover` because the hero card and the four small ones are near enough the
   same aspect that cropping never loses the horizon. `pixelated` because
   everything here is drawn at 144x128 and scaled by 4: letting the browser
   smooth it would undo the whole point of drawing it as pixel art. */
.mount-card__bg {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  image-rendering: pixelated;
  z-index: 0;
  pointer-events: none;
}

/* The fallback lives on the card itself, so a background that fails to load
   leaves a dark wood card rather than a white hole. */
.mount-card {
  background-image:
    radial-gradient(ellipse 62% 40% at 50% 78%, rgba(0,0,0,0.45), transparent 72%),
    linear-gradient(180deg, var(--wood) 0%, var(--wood-dark) 55%, var(--wood-deep) 100%);
}

/* stone pedestal under every mount, per the AAA staging rule */
.mount-card__image::before {
  content: '';
  position: absolute;
  left: 50%; bottom: 12%;
  width: 46%; height: 12px;
  transform: translateX(-50%);
  pointer-events: none;
  background:
    radial-gradient(ellipse at center, rgba(0,0,0,0.55) 0%, transparent 72%);
}
.mount-still,
.mount-anim {
  position: relative;
  width: 62%;
  max-width: 190px;
  aspect-ratio: 1 / 1;
  z-index: 1;
  animation: aaa-mountbob 2.4s ease-in-out infinite;
}
.mount-card--hero .mount-still,
.mount-card--hero .mount-anim { width: 54%; max-width: 300px; }
.mount-still { object-fit: contain; }

.mount-anim__f {
  position: absolute;
  inset: 0;
  width: 100%; height: 100%;
  object-fit: contain;
  opacity: 0;
}
.mount-anim__f.is-on { opacity: 1; }

@keyframes aaa-mountbob {
  0%, 100% { transform: translateY(0); }
  50%      { transform: translateY(-6px); }
}
.mount-card { transition: transform 0.2s var(--ease-soft); }
.mount-card:hover { transform: translateY(-5px); }
.mount-card:hover .mount-still,
.mount-card:hover .mount-anim { animation-duration: 1.1s; }

/* sparkle dust on the legendary mount */
.mount-card--hero .mount-card__image::after {
  content: '';
  position: absolute; inset: 0;
  pointer-events: none;
  background-repeat: no-repeat;
  background-size: 3px 3px;
  background-image:
    linear-gradient(#e8c8ff, #e8c8ff), linear-gradient(#e8c8ff, #e8c8ff),
    linear-gradient(#e8c8ff, #e8c8ff), linear-gradient(#e8c8ff, #e8c8ff),
    linear-gradient(#e8c8ff, #e8c8ff);
  background-position: 22% 30%, 74% 22%, 63% 68%, 33% 74%, 50% 15%;
  animation: aaa-twinkle 2.6s steps(4, end) infinite;
}

/* ---------------- item icon strip ---------------- */
.icon-tile {
  position: relative;
  background-image:
    radial-gradient(circle at 50% 36%, rgba(253,224,138,0.20), transparent 66%),
    linear-gradient(180deg, var(--parchment-dk) 0%, var(--wood) 62%, var(--wood-dark) 100%);
  box-shadow:
    inset 0 3px 0 rgba(255,255,255,0.26),
    inset 0 -3px 0 rgba(0,0,0,0.34),
    0 3px 0 var(--wood-black);
  transition: transform 0.15s var(--ease-soft);
}
.icon-tile:hover { transform: translateY(-3px); }
.icon-tile img { image-rendering: pixelated; }

@media (prefers-reduced-motion: reduce) {
  .mount-still, .mount-anim,
  .mount-card--hero .mount-card__image::after { animation: none !important; }
  .shot { transition: none; }
}

/* ---------------- showcase panel: caption needs a scrim over the shot -------- */
.showcase__panel { position: relative; }
.showcase__body,
.showcase__panel-body {
  position: relative;
  z-index: 2;
}
.showcase__image::before {
  content: '';
  position: absolute;
  left: 0; right: 0; bottom: 0;
  height: 62%;
  z-index: 1;
  pointer-events: none;
  background: linear-gradient(0deg,
    rgba(20,10,4,0.94) 0%,
    rgba(20,10,4,0.80) 34%,
    rgba(20,10,4,0.38) 68%,
    transparent 100%);
}
.showcase__panel .showcase__tag,
.showcase__panel h3,
.showcase__panel p { text-shadow: 0 2px 0 rgba(0,0,0,0.75); }

/* hero backdrop: the real HUD shot, pushed back so it reads as atmosphere */
/* P2-118 / the home backdrop.

   This was 20% opacity behind a 1px blur, and that was the right setting for
   what used to be here: assets/screens/hud.png, a screenshot with the inventory
   panel and the chat bar still in it. A busy UI has to be knocked almost all
   the way out or it fights every word on the page.

   world.webp is a clean capture of the village with the HUD off, so it can
   carry far more presence. 50%, and the blur is gone: a background that is
   blurred is a background nobody can tell is a real place. */
.atmosphere__photo {
  opacity: 0.50;
  filter: saturate(0.92);
  object-fit: cover;
}

/* The hero copy sits over the top of that image, so the top of the frame is
   washed back toward the sky colour and the bottom is left alone. One gradient
   rather than dulling the whole picture, because the part of the village worth
   seeing is the part no text is standing on. */
.atmosphere::after {
  content: '';
  position: absolute;
  inset: 0;
  pointer-events: none;
  background: linear-gradient(180deg,
    rgba(42, 155, 242, 0.72) 0%,
    rgba(42, 155, 242, 0.42) 26%,
    rgba(42, 155, 242, 0.10) 55%,
    rgba(42, 155, 242, 0) 78%);
}

/* mount gallery frame */
.mount-gallery .mount-card { position: relative; overflow: hidden; }

/* ==================================================================
   21. BUTTONS v2, rebuilt against the in-game gold plate
   Reference: Life_Skills_Mockup_2x, the LV / MILESTONE / RIDE plates.
   Anatomy per plate: navy outline, gold ring, 4-band face with a hard
   shine seam, engraved label, corner spurs, bottom shade.
   ================================================================== */

.btn {
  position: relative;
  border-radius: 0;
  border: 3px solid var(--wood-black) !important;
  padding: 14px 26px;
  font-family: 'Press Start 2P', monospace;
  letter-spacing: 0.05em;
  text-shadow: 0 2px 0 rgba(0,0,0,0.45);
}

/* the 4-band metal face, shared by every variant */
.btn::before {
  content: '';
  position: absolute;
  inset: 0;
  z-index: 0;
  pointer-events: none;
  background:
    /* engraved rules top and bottom */
    linear-gradient(rgba(255,255,255,0.30), rgba(255,255,255,0.30)) 6px 4px / calc(100% - 12px) 1px no-repeat,
    linear-gradient(rgba(0,0,0,0.28), rgba(0,0,0,0.28)) 6px calc(100% - 5px) / calc(100% - 12px) 1px no-repeat,
    /* hard shine seam at 42% */
    linear-gradient(180deg,
      rgba(255,255,255,0.34) 0%,
      rgba(255,255,255,0.14) 40%,
      rgba(255,255,255,0.42) 41%,
      rgba(255,255,255,0.42) 42%,
      rgba(0,0,0,0.06)      43%,
      rgba(0,0,0,0.14)      74%,
      rgba(0,0,0,0.30)      100%);
}
.btn > * { position: relative; z-index: 2; }

/* corner spurs, the client's filigree motif */
.btn::after {
  content: '';
  position: absolute;
  inset: 3px;
  width: auto; height: auto;
  transform: none;
  z-index: 1;
  pointer-events: none;
  background-repeat: no-repeat;
  background-size: 5px 2px, 2px 5px, 5px 2px, 2px 5px,
                   5px 2px, 2px 5px, 5px 2px, 2px 5px;
  background-image:
    linear-gradient(var(--spur, rgba(255,255,255,0.55)), var(--spur, rgba(255,255,255,0.55))),
    linear-gradient(var(--spur, rgba(255,255,255,0.55)), var(--spur, rgba(255,255,255,0.55))),
    linear-gradient(var(--spur, rgba(255,255,255,0.55)), var(--spur, rgba(255,255,255,0.55))),
    linear-gradient(var(--spur, rgba(255,255,255,0.55)), var(--spur, rgba(255,255,255,0.55))),
    linear-gradient(var(--spurd, rgba(0,0,0,0.40)), var(--spurd, rgba(0,0,0,0.40))),
    linear-gradient(var(--spurd, rgba(0,0,0,0.40)), var(--spurd, rgba(0,0,0,0.40))),
    linear-gradient(var(--spurd, rgba(0,0,0,0.40)), var(--spurd, rgba(0,0,0,0.40))),
    linear-gradient(var(--spurd, rgba(0,0,0,0.40)), var(--spurd, rgba(0,0,0,0.40)));
  background-position:
    left top, left top, right top, right top,
    left bottom, left bottom, right bottom, right bottom;
  /* the gloss sweep rides on top of this on hover */
  transition: none;
}

/* gloss sweep moved onto its own element so it can sit above the face */
.btn { overflow: hidden; }
.btn:hover::before {
  animation: aaa-sweep 0.55s var(--ease-soft) 1;
}
@keyframes aaa-sweep {
  0%   { background-position-x: 6px, 6px, -160%; }
  100% { background-position-x: 6px, 6px, 260%; }
}

/* --- gold primary, the LV / MILESTONE plate --- */
.btn--primary {
  background-color: var(--gold) !important;
  color: var(--wood-black) !important;
  box-shadow:
    inset 0 0 0 2px var(--gold-bright),
    0 6px 0 var(--gold-deep),
    0 10px 0 var(--wood-black),
    0 14px 16px -4px rgba(0,0,0,0.42) !important;
  text-shadow: 0 1px 0 rgba(255,255,255,0.45);
}
.btn--primary:hover { box-shadow:
    inset 0 0 0 2px #fff6d6,
    0 6px 0 var(--gold-deep),
    0 10px 0 var(--wood-black),
    0 0 22px rgba(244,199,94,0.60) !important; }

/* --- copper CTA, the RIDE plate weight --- */
.btn--copper {
  background-color: var(--copper) !important;
  color: #fff3e2 !important;
  --spur: rgba(255,220,190,0.60);
  box-shadow:
    inset 0 0 0 2px var(--copper-light),
    0 6px 0 var(--copper-deep),
    0 10px 0 var(--wood-black),
    0 14px 16px -4px rgba(0,0,0,0.42) !important;
}
.btn--copper:hover { box-shadow:
    inset 0 0 0 2px #ffb591,
    0 6px 0 var(--copper-deep),
    0 10px 0 var(--wood-black),
    0 0 22px rgba(184,90,40,0.65) !important; }

/* --- ghost / secondary, recessed parchment plate --- */
.btn--ghost {
  background-color: var(--parchment) !important;
  color: var(--ink) !important;
  --spur: rgba(255,255,255,0.7);
  --spurd: rgba(120,88,46,0.45);
  box-shadow:
    inset 0 0 0 2px var(--parchment-lt),
    0 6px 0 var(--parchment-edge),
    0 10px 0 var(--wood-dark),
    0 14px 16px -4px rgba(0,0,0,0.32) !important;
  text-shadow: 0 1px 0 rgba(255,255,255,0.6);
}

.btn--small { padding: 10px 16px; border-width: 3px !important; }
.btn--lg    { padding: 18px 34px; font-size: 0.82rem; }

/* P2-152 button presence.

   Ketema: "Buttons can be more detailed, popping out more."

   The plate is a stack of hard shadows: a coloured shoulder, a black base and
   an ambient below both. Raising the shoulder from 4px to 6px and the base
   from 7px to 10px is what makes the button read as an object with a side,
   rather than a rectangle with a line under it.

   The travel has to move with it. A button lifts 4px on hover and sinks 4px on
   press, so the press lands the face where the shoulder was. If the travel and
   the lift disagree the button either floats above its own shadow or sinks
   through the page, which is worse than a flat button. */
.btn:hover  { transform: translateY(-4px); }
.btn:active { transform: translateY(4px); filter: brightness(0.94); }

/* Focus ring.

   This USED to be `outline: 3px solid`, which drew nothing at all: .btn takes
   its notched corners from `clip-path`, and clip-path clips the outline along
   with the rest of the box, so the ring was rendered and then cut away. The
   site had no visible keyboard focus on any button.

   An inset shadow is inside the clip region, so it survives. It needs
   !important because every variant sets box-shadow with !important a few rules
   above, and a focus state that loses to a colour variant is no focus state. */
.btn:focus-visible {
  outline: none;
  box-shadow:
    inset 0 0 0 3px var(--gold-bright),
    inset 0 0 0 6px var(--wood-black),
    0 6px 0 var(--wood-black),
    0 10px 0 rgba(0,0,0,0.45),
    0 14px 16px -4px rgba(0,0,0,0.42) !important;
}

/* the old lg filigree rule is redundant now */
.btn--lg::before { border: 0; }

/* pay buttons keep the disabled metal look but gain the same anatomy */
.pay__btn { position: relative; border-radius: 0; }

@media (prefers-reduced-motion: reduce) {
  .btn:hover::before { animation: none; }
  .btn:hover, .btn:active { transform: none; }
}

/* ==================================================================
   22. SCREENSHOT FRAMING v2
   The UI captures are native 16:9. Section 20 used object-fit: cover,
   which sliced the left and right edges off every panel. Frames are
   now reshaped to 16:9 and the art is shown whole.
   ================================================================== */

.shot {
  object-fit: contain !important;
  object-position: center !important;
}

/* frames reshaped to the source ratio, so any letterbox is a hairline */
.usp-card__image,
.gameplay-card__image { aspect-ratio: 16 / 9 !important; }

.area-promo__image {
  aspect-ratio: 16 / 9;
  min-height: 0;
}

.showcase__view {
  aspect-ratio: 16 / 9;
  min-height: 320px;
}

/* the life skill caption sits over the plate, not over the art */
.showcase__image .shot { object-position: center top !important; }

/* plate backing, so residual letterbox reads as deliberate framing */
.usp-card__image,
.showcase__image,
.showcase__view,
.gameplay-card__image,
.area-promo__image {
  background:
    repeating-linear-gradient(45deg,
      rgba(255,255,255,0.012) 0 6px, transparent 6px 12px),
    linear-gradient(180deg, #2a1809 0%, #1b0f05 58%, #120903 100%);
  box-shadow:
    inset 0 0 0 1px rgba(0,0,0,0.62),
    inset 0 1px 0 rgba(255,214,138,0.10),
    inset 0 -26px 34px -20px rgba(0,0,0,0.7);
}

@media (max-width: 960px) {
  .showcase__view { min-height: 260px; }
  .area-promo__image { min-height: 0; }
}

/* caption band: strong scrim under the text so the art stays readable */
.showcase__image::before { height: 46%; }

/* aspect-ratio + min-height creates an automatic minimum WIDTH, which
   pushed the showcase past the viewport on phones. Clamp it. */
.showcase__view { min-width: 0; }
@media (max-width: 960px) {
  .showcase__view { min-height: 0; }
  .showcase__list { min-width: 0; }
}

/* ==================================================================
   P2-155. THE MOBILE HEADER, and why the whole site was wider than
   the phone it was being read on.

   The brand logo is 948x124. `.nav__brand img` set `height: 44px` and
   `max-width: none`, so it drew 336px wide and refused to shrink. 336px
   is wider than a 320px phone on its own, before the sign-in chip, the
   Enter Estova button and the hamburger are counted at all.

   The result was not a broken header, it was a broken document. The
   header forced the page's scroll width past the viewport, so every
   `width: 100%` band on the page (the testnet banner, the nav plate,
   each section) painted to the *viewport* width while the page's
   background painted to the full *scroll* width. That mismatch is the
   bare strip of sky down the right-hand side of the phone screenshot.
   It looked like a background bug. It was the logo.

   Two decisions here worth keeping:

   1. The logo is scaled by breakpoint rather than clamped with
      max-width. `object-fit: contain` inside a shorter box would
      letterbox it, leaving the wordmark floating in dead space; a
      smaller height keeps the artwork's own proportions.

   2. The header's Enter Estova button is dropped below 620px. It is not
      lost: it is the hero's primary button on the same screen, at four
      times the size. Keeping it in the header buys nothing and costs
      about 110px, which is the difference between fitting and not.
   ================================================================== */

.nav__inner { gap: 10px; min-width: 0; }
.nav__brand  { flex: 0 0 auto; min-width: 0; }
.nav__right  { min-width: 0; flex: 0 1 auto; flex-wrap: nowrap; }
.nav__wallet { min-width: 0; }

@media (max-width: 960px) {
  .nav__brand img { height: 34px; }   /* 260px wide */
  .nav__right     { gap: 10px; }
}

@media (max-width: 620px) {
  .nav__inner     { height: 64px; padding: 0 12px; gap: 8px; }
  .nav__brand img { height: 26px; }   /* 199px wide */
  .nav__right     { gap: 8px; }
  /* Same call to action, four times the size, one screen down. */
  .nav__right > .btn--primary { display: none; }
  .nav__menu      { top: 64px; max-height: calc(100vh - 64px); }
}

@media (max-width: 400px) {
  .nav__brand img { height: 22px; }   /* 168px wide */
  .nav__inner     { padding: 0 10px; }
}

/* A table cannot be made narrow, so it is given its own scroller rather
   than being allowed to widen the page behind it. */
.table-scroll { overflow-x: auto; -webkit-overflow-scrolling: touch; }
@media (max-width: 700px) {
  .lb-table { min-width: 520px; }
}

/* The hero wordmark had `max-width: 520px` and no `width`, so an <img> whose
   intrinsic width is 948px drew at a flat 520px on every screen. `.hero` is
   `overflow: hidden`, so on a phone the right-hand third of the wordmark was
   simply sliced off: "BREATH OF ES". A percentage width lets it shrink. */
.hero__logo { width: 100%; max-width: 520px; height: auto; }

@media (max-width: 620px) {
  .hero__inner { padding: 0 18px; }
  .hero        { padding: 26px 0 40px; }
  .hero__logo  { margin-bottom: 16px; }
}

/* ==================================================================
   P2-157. THE GENESIS CAP LINE.

   "The first 500 Founder Packs come with Genesis Trading Cards. 500
   left." The two numbers are the whole point of the sentence, and both
   were being drawn as ordinary body text in --gold over a mid-brown
   wood gradient. Gold on brown is about 2:1. You could see that a
   sentence was there; you could not read the number in it, which is the
   one word a scarcity line exists to deliver.

   Both numbers are now set in the pixel face on a dark plate, so they
   read as a counter rather than as emphasis. The remaining count gets
   the brighter of the two, because it is the one that changes.
   ================================================================== */
.gen-cap {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: center;
  gap: 8px 12px;
  max-width: 680px;
  /* 3 Sep 2026: the line lives under the three tier cards now - clear
     breathing room so it doesn't crowd their bottom guarantee rows. */
  margin: 48px auto 0;
  padding: 14px 18px;
  color: var(--cream, #f4e9d0);
  background: rgba(20, 12, 6, 0.55);
  border: 2px solid rgba(232, 193, 105, 0.55);
  text-shadow: 0 2px 0 rgba(0, 0, 0, 0.5);
}
.gen-cap__text { font-size: 0.95rem; line-height: 1.55; }

.gen-cap__n,
.gen-cap__left {
  font-family: 'Press Start 2P', monospace;
  font-size: 0.66rem;
  letter-spacing: 0.04em;
  padding: 5px 9px;
  white-space: nowrap;
  border: 2px solid var(--wood-black, #1c1109);
  text-shadow: none;
}
.gen-cap .gen-cap__n {
  color: #2a1a06;
  background: var(--gold, #e8c169);
  box-shadow: inset 0 2px 0 var(--gold-bright, #ffe9a8),
              inset 0 -2px 0 var(--gold-dark, #b98f36);
}
.gen-cap .gen-cap__left {
  color: #12240f;
  background: #8ddc7a;
  box-shadow: inset 0 2px 0 #d8ffcc, inset 0 -2px 0 #5aa84c;
}
.gen-cap__left:empty { display: none; }

.gen-cap.is-gone { border-color: rgba(214, 106, 74, 0.7); }
.gen-cap.is-gone .gen-cap__left {
  color: #2a0f08;
  background: #e58a6a;
  box-shadow: inset 0 2px 0 #ffc4ac, inset 0 -2px 0 #b0563a;
}

/* ==================================================================
   P2-156. THE JOKER, presented as what it is.
   ================================================================== */
.joker-note {
  display: flex;
  align-items: center;
  gap: 20px;
  max-width: 720px;
  margin: 28px auto 0;
  padding: 18px 22px;
  text-align: left;
  background: linear-gradient(180deg, rgba(92, 62, 148, 0.16), rgba(92, 62, 148, 0.06));
  border: 3px solid #5c3e94;
  box-shadow: inset 0 3px 0 rgba(168, 140, 226, 0.35),
              inset 0 -3px 0 rgba(48, 30, 82, 0.45),
              0 5px 0 rgba(0, 0, 0, 0.18);
}
.joker-note__badge {
  flex: 0 0 auto;
  font-family: 'Press Start 2P', monospace;
  font-size: 0.62rem;
  letter-spacing: 0.05em;
  padding: 10px 12px;
  color: #f3ecff;
  background: #5c3e94;
  border: 2px solid var(--wood-black, #1c1109);
  box-shadow: inset 0 2px 0 rgba(190, 166, 240, 0.5),
              inset 0 -2px 0 rgba(40, 24, 72, 0.6);
}
.joker-note__body h3 { margin: 0 0 6px; font-size: 0.9rem; }
.joker-note__body p  { margin: 0; font-size: 0.95rem; line-height: 1.6; }

.odds__joker {
  max-width: 66ch;
  margin: 18px auto 0;
  padding: 12px 16px;
  font-size: 0.95rem;
  line-height: 1.6;
  border-left: 4px solid #5c3e94;
  background: rgba(92, 62, 148, 0.08);
}

.tcard--joker { --rar: #5c3e94; }

@media (max-width: 620px) {
  .joker-note { flex-direction: column; align-items: flex-start; gap: 12px; }
}

/* ==================================================================
   P2-158. THE PITY STRIP.

   Ten boxes and a sentence. The filled count is this wallet's local
   mirror of the on-chain counter; the empty strip is the default and
   is what a signed-out visitor sees, because the alternative is
   telling everybody they are part-way through a guarantee they have
   not started.
   ================================================================== */
.pitytrack {
  max-width: 720px;
  margin: 32px auto 0;
  padding: 18px 20px;
  text-align: left;
  background: rgba(28, 17, 9, 0.06);
  border: 3px solid var(--wood-deep, #3d2811);
  box-shadow: inset 0 3px 0 rgba(255, 255, 255, 0.35),
              inset 0 -3px 0 rgba(61, 40, 17, 0.14),
              0 5px 0 rgba(0, 0, 0, 0.14);
}
.pitytrack__head {
  display: flex; align-items: center; justify-content: space-between;
  gap: 12px; margin-bottom: 14px;
}
.pitytrack__title {
  font-family: 'Press Start 2P', monospace;
  font-size: 0.66rem; line-height: 1.5; letter-spacing: 0.03em;
  color: var(--wood-deep, #3d2811);
}
.pitytrack__count {
  flex: 0 0 auto;
  font-family: 'Press Start 2P', monospace;
  font-size: 0.58rem;
  padding: 6px 9px;
  color: var(--cream, #f4e9d0);
  background: var(--wood-deep, #3d2811);
  border: 2px solid var(--wood-black, #1c1109);
}
.pitytrack__bar { display: flex; gap: 6px; }
.pitytrack__seg {
  flex: 1 1 0;
  height: 26px;
  background: rgba(28, 17, 9, 0.10);
  border: 2px solid var(--wood-deep, #3d2811);
  box-shadow: inset 0 2px 0 rgba(255, 255, 255, 0.30);
}
.pitytrack__seg.is-filled {
  background: var(--gold, #e8c169);
  box-shadow: inset 0 2px 0 var(--gold-bright, #ffe9a8),
              inset 0 -2px 0 var(--gold-dark, #b98f36);
}
.pitytrack__line {
  margin: 14px 0 0;
  font-size: 1rem;
  line-height: 1.5;
  color: var(--wood-deep, #3d2811);
}
.pitytrack__line b {
  font-family: 'Press Start 2P', monospace;
  font-size: 0.72rem;
}
.pitytrack__note {
  margin: 8px 0 0;
  font-size: 0.82rem;
  line-height: 1.55;
  opacity: 0.72;
}
@media (max-width: 620px) {
  .pitytrack        { padding: 14px; }
  .pitytrack__head  { flex-direction: column; align-items: flex-start; gap: 8px; }
  .pitytrack__bar   { gap: 4px; }
  .pitytrack__seg   { height: 20px; }
}

/* ==================================================================
   P2-159. THE COMING SOON PANEL.
   Used where a feature is real but not live. It has to look deliberate
   rather than broken, so it is a full panel with its own frame, not a
   sentence where a table used to be.
   ================================================================== */
.soon {
  max-width: 760px;
  margin: 0 auto;
  padding: 40px 32px;
  text-align: center;
  background: var(--parchment, #f4e3c0);
  border: 4px solid var(--wood-deep, #3d2811);
  box-shadow: inset 0 4px 0 rgba(255, 255, 255, 0.45),
              inset 0 -4px 0 rgba(61, 40, 17, 0.16),
              0 8px 0 rgba(0, 0, 0, 0.2);
}
.soon__badge {
  display: inline-block;
  font-family: 'Press Start 2P', monospace;
  font-size: 0.62rem;
  letter-spacing: 0.08em;
  padding: 9px 14px;
  margin-bottom: 20px;
  color: #2a1a06;
  background: var(--gold, #e8c169);
  border: 3px solid var(--wood-deep, #3d2811);
  box-shadow: inset 0 3px 0 var(--gold-bright, #ffe9a8),
              inset 0 -3px 0 var(--gold-dark, #b98f36),
              0 3px 0 var(--wood-deep, #3d2811);
}
.soon__title { margin: 0 0 14px; }
.soon__copy {
  max-width: 58ch;
  margin: 0 auto;
  font-size: 1.02rem;
  line-height: 1.65;
}
.soon__grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 10px;
  margin: 30px 0 24px;
}
.soon__item {
  padding: 14px 10px;
  background: rgba(28, 17, 9, 0.06);
  border: 2px solid rgba(61, 40, 17, 0.35);
}
.soon__item b {
  display: block;
  font-family: 'Press Start 2P', monospace;
  font-size: 0.56rem;
  line-height: 1.5;
  margin-bottom: 7px;
  color: var(--wood-deep, #3d2811);
}
.soon__item span { font-size: 0.86rem; opacity: 0.75; }
.soon__foot { margin: 0; font-size: 0.92rem; opacity: 0.85; }
.soon__foot a { text-decoration: underline; text-underline-offset: 3px; }

@media (max-width: 700px) {
  .soon       { padding: 28px 18px; }
  .soon__grid { grid-template-columns: repeat(2, 1fr); }
}
@media (max-width: 420px) {
  .soon__grid { grid-template-columns: 1fr; }
}

/* ==================================================================
   P2-160. THE ARENA LADDER, six tiers.

   The grid was `repeat(7, 1fr)` for seven tiers, three of which were
   invented. Six now, and the medal disc colours follow the real tiers:
   Master takes the amethyst it is drawn in, Grandmaster keeps the glow
   that used to sit under the made-up Champion.
   ================================================================== */
.arena__ladder { grid-template-columns: repeat(6, 1fr); }

.arena__tier-medal[data-t="master"] {
  background: #7a52c0; color: #f3ecff;
  box-shadow: inset 0 2px 0 #b294ea, inset 0 -2px 0 #33205c;
}
.arena__tier-medal[data-t="grandmaster"] {
  background: #c44a3a; color: #fde08a;
  box-shadow: inset 0 2px 0 #e08a72, inset 0 -2px 0 #5a1818,
              0 0 10px rgba(196, 74, 58, 0.6);
}

/* The MMR floor is what a player actually navigates by, so it is on the
   tile rather than in prose underneath it. */
.arena__tier-mmr {
  margin-top: 5px;
  font-size: 0.74rem;
  font-family: 'Pixelify Sans', sans-serif;
  color: var(--ink-faint, #7a6a52);
  letter-spacing: 0.02em;
}

@media (max-width: 960px) { .arena__ladder { grid-template-columns: repeat(3, 1fr); } }
@media (max-width: 520px) { .arena__ladder { grid-template-columns: repeat(2, 1fr); } }

/* ==================================================================
   P2-161. TIER PERKS get their description.

   The list was six nouns: "1,000 gems", "Rare skin", "5 day head
   start". Each of those is a thing a buyer has to already know the
   value of. The web notes carry a one-line description for every one,
   and the "What it gets you in-game" section further down the page
   already uses them, so the tier card was the only place on the page
   still asking the reader to guess.

   Same words in both places, deliberately. A perk that means one thing
   in the price list and another in the explainer is worse than a perk
   with no explanation at all.
   ================================================================== */
.tier__list li { padding: 10px 0 10px 22px; }
.tier__li-t {
  display: block;
  font-size: 0.92rem;
  color: var(--ink, #2f2213);
}
.tier__li-d {
  display: block;
  margin-top: 3px;
  font-size: 0.8rem;
  line-height: 1.45;
  color: var(--ink-faint, #7a6a52);
}

/* The three tier cards hold different numbers of perks, so their Buy buttons
   were landing at three different heights. The card becomes a column and the
   pay block is pushed to the bottom of it, so the three buttons line up and
   the eye can compare prices without tracking up and down. */
.tiers .tier { display: flex; flex-direction: column; }
.tiers .tier .pay { margin-top: auto; padding-top: 20px; }

/* The explorer link in a receipt row was rendering as a default browser blue,
   the one colour on the site that belongs to no palette. It is a link and it
   should still look like one, so it keeps the underline and takes the wood
   ink instead of the blue. */
.ck__rows a,
.ck__row dd a {
  color: var(--wood-dark, #5a3d1c);
  text-decoration: underline;
  text-underline-offset: 3px;
}
.ck__rows a:hover { color: var(--wood-deep, #3d2811); }

/* ==================================================================
   P2-166. THE HEADER, made collision-proof.

   Enter Estova was landing on top of the wallet chip. The cause is
   that the header had no width budget: `.nav__inner` is a flex row of
   three children (brand, menu, right) and none of them could shrink,
   so once the real logo loaded at 260px the row simply overflowed and
   the items drew over each other.

   Three changes, in order of how much room each buys:

   1. THE SOCIALS ARE GONE FROM THE HEADER. Three 40px buttons plus
      gaps is about 100px, and they were already duplicated in the
      footer on four of six pages, so this removes a duplicate rather
      than a feature. leaderboard and wiki gained the footer row they
      were missing.

   2. THE MENU IS THE ONE THAT SHRINKS. It is given `min-width: 0` and
      the right-hand group is pinned `flex: 0 0 auto`. That is the
      right way round: a nav link that tightens is fine, a Buy button
      that slides under a chip is not.

   3. NOTHING OVERLAPS, because nothing is allowed to. The row wraps
      its gaps down rather than letting children paint over one
      another, and the two chips get a max-width with an ellipsis so a
      long wallet label cannot push the button off the end.
   ================================================================== */
.nav__inner {
  display: flex;
  align-items: center;
  gap: 16px;
  flex-wrap: nowrap;
}
.nav__brand { flex: 0 1 auto; min-width: 0; }
.nav__menu  { flex: 1 1 auto; min-width: 0; margin-left: 20px; }
.nav__right { flex: 0 0 auto; min-width: 0; gap: 10px; }

/* The links tighten before anything else gives. */
.nav__links { flex-wrap: nowrap; }
.nav__link  { white-space: nowrap; padding: 10px 12px; }
@media (max-width: 1180px) { .nav__link { padding: 10px 9px; font-size: 0.58rem; } }

/* A wallet label is user data and can be any length. It gets a ceiling. */
.nav__wallet { max-width: 190px; }
.nav__wallet > * {
  max-width: 100%;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.nav__right > .btn--primary { flex: 0 0 auto; white-space: nowrap; }

/* The footer row the two thin pages were missing. */
.footer__social-row {
  display: flex;
  justify-content: center;
  padding: 4px 0 22px;
}

/* ==================================================================
   P2-167. NUMBERS YOU CAN ACTUALLY READ.

   "The numbers are completely unreadable now." Correct, and it is a
   font problem rather than a size or colour one.

   Every number on this site was being set in one of two faces, and
   neither can carry digits:

     Press Start 2P   a 5x7 display face. At 0.66rem a digit is about
                      six pixels tall. 5, 6, 8 and 0 differ by one or
                      two lit pixels, so "500" is a gold smear and
                      "2,500" and "2,800" are the same picture.
     Pixelify Sans    better, but it is a soft pixel face with a
                      one-storey 1 and an open 4, and at 0.92rem in a
                      list it still reads as texture.

   Neither is wrong for headings. Both are wrong for a figure somebody
   is about to spend money against.

   So numbers get their own face: a real monospace, which every system
   has, at a slightly larger size with tabular figures and a touch of
   letter-spacing. It is the one place on the site that steps outside
   the pixel styling, and it does so because a price nobody can read is
   worse than a price that does not match the frame.

   Applied narrowly, to runs that ARE numbers: the Genesis cap chips,
   the perk counts on the tier cards, the head-start days, and the MMR
   floors on the arena ladder. Body copy is untouched.
   ================================================================== */
:root {
  --num: ui-monospace, 'Cascadia Mono', 'Segoe UI Mono', 'Roboto Mono',
         Menlo, Consolas, 'DejaVu Sans Mono', monospace;
}

.num,
.gen-cap__n,
.gen-cap__left,
.tier__list .tier__li-t b,
.arena__tier-mmr,
.pitytrack__count,
.pitytrack__line b {
  font-family: var(--num);
  font-variant-numeric: tabular-nums lining-nums;
  letter-spacing: 0.02em;
  font-weight: 700;
}

/* The two cap chips. Bigger, and the plate grows with the text rather
   than the text being squeezed into a fixed plate. */
.gen-cap__n,
.gen-cap__left {
  font-size: 0.95rem;
  padding: 6px 11px;
  line-height: 1.2;
}

/* Perk counts sit inline with their label, so they take the label's
   size and just change face. Tabular figures keep "1,000" and "2,500"
   the same width, which is what makes them comparable down a column. */
.tier__list .tier__li-t b {
  font-size: 1.02rem;
  color: var(--ink, #2f2213);
}

.arena__tier-mmr  { font-size: 0.78rem; letter-spacing: 0.04em; }
.pitytrack__count { font-size: 0.8rem; letter-spacing: 0.06em; }
.pitytrack__line b { font-size: 1.05rem; }

/* ==================================================================
   P2-171. [hidden] HAS TO WIN.

   "once the cards are revealed, reveal all button doesn't make sense."
   It did not make sense, and the code already knew that: revealCards
   sets `allBtn.hidden = true` the moment the last card turns over.

   The button stayed on screen anyway. `hidden` is not magic, it is a
   `display: none` rule in the BROWSER's stylesheet, and any author
   rule that sets `display` on the same element beats it on the
   cascade. `.btn { display: inline-flex }` does exactly that, so
   every button on this site was immune to being hidden.

   That is a whole class of silent bug rather than one button: any
   element given a display value can never be hidden by the attribute
   again, and the code doing the hiding looks correct while it happens.
   One rule closes the class.
   ================================================================== */
[hidden] { display: none !important; }

/* ==================================================================
   P2-171. THE DRAW REEL, made of real cards.
   The cell keeps its rarity tint as a frame, and the art fills it.
   ================================================================== */
.rv__cell { position: relative; overflow: hidden; padding-bottom: 0; }
.rv__cell-art {
  width: 100%;
  height: 100%;
  object-fit: cover;
  image-rendering: pixelated;
  display: block;
}
.rv__cell.is-joker { background: linear-gradient(180deg, #a88ce0, #5c3e94); }

/* ==================================================================
   P2-172. THE REAL ARENA BADGES.

   "Actual badges we made for the pvp are not on the website." They
   were not: the ladder was drawing a coloured CSS disc with the old
   placeholder medal on top of it. The real set is in
   Redesigned Panels / Arena Tier Badges, six medallions with shields,
   laurels and a glow on Grandmaster.

   The badge is now the whole thing, so the disc underneath has to go.
   It was `border-radius: 50%` with a per-tier background and an inset
   bevel, all of which drew a second, worse badge behind the real one.
   ================================================================== */
.arena__tier-medal {
  /* v2 badges, 101x118 after trimming to the union of all six. Drawn at
     native size: pixel art at a fraction of native turns the shield to mush,
     which is what the first pass did at 68px. The shields are taller than
     they are wide, so both dimensions are set to the real ones rather than
     forcing a square. */
  width: 88px;
  height: 118px;
  background: none !important;
  border: 0;
  border-radius: 0;
  box-shadow: none !important;
  image-rendering: pixelated;
  object-fit: contain;
  filter: drop-shadow(0 3px 0 rgba(0, 0, 0, 0.28));
}
.arena__tier { padding-top: 14px; }

/* ==================================================================
   P2-176. THE MOBILE HEADER, second pass, and why the first one was
   never actually tested.

   P2-155 (above) diagnosed this correctly: the 948x124 wordmark with
   `max-width: none` sets the page's minimum width, and once the page is
   wider than the phone, every `width: 100%` band paints to the viewport
   while the background paints to the scroll width. That is the bare
   strip down the side of the screenshot, and it is why the drawer looked
   like it was floating over a page that had shrunk.

   What P2-155 got wrong is the fix. It scaled the logo by breakpoint to
   a set of heights that happen to fit *some* phones:

       <=620px  height 26px  ->  199px wide
       <=400px  height 22px  ->  168px wide

   The right-hand cluster (sign-in chip + wallet chip + hamburger) is
   174px. 199 + 174 + 24 padding + 8 gap = 405. On a 375px iPhone that is
   a 30px overflow, on a 360px Android 45px. The breakpoint at 400px does
   not help, because 375 and 360 are both above it and get the 199px
   logo. Every phone between 361px and 400px was broken by a fix written
   to unbreak them.

   It survived the test suite because the wordmark is hosted on a CDN and
   the tests run offline. The image 404s, `onerror` swaps in a 40px SVG,
   and 40px fits anywhere. The suite was measuring the fallback. The test
   at the bottom of this ticket pins the real 948x124 aspect so that
   cannot happen twice.

   The fix is to stop guessing widths. `max-width: 100%` with
   `height: auto` lets the logo take whatever the header has left after
   the buttons, at any viewport, including ones nobody has thought of.

   P2-155 rejected max-width on the grounds that `object-fit: contain`
   would letterbox the wordmark in dead space. That is true only while
   the height is pinned. With `height: auto` there is no box to letterbox
   inside: the image simply scales, and `max-height` caps it on desktop
   where there is room to spare.
   ================================================================== */

.nav__brand {
  flex: 0 1 auto;        /* may shrink; .nav__right is 0 0 auto and may not */
  min-width: 0;          /* without this a flex item will not go below its content */
  overflow: hidden;
}
.nav__brand img {
  display: block;
  width: auto;
  height: auto;
  max-width: 100%;
  max-height: 44px;
  object-fit: contain;
}

@media (max-width: 960px) { .nav__brand img { max-height: 34px; } }
@media (max-width: 620px) { .nav__brand img { max-height: 28px; } }

/* The right-hand cluster is the fixed cost the logo has to fit around,
   so on the narrowest phones it is trimmed too, rather than letting the
   wordmark shrink to an unreadable smear to make room for a chip that is
   mostly padding. */
@media (max-width: 420px) {
  .nav__right  { gap: 6px; }
  .nav__wallet { max-width: 120px; }
  .nav__wallet > * { max-width: 120px; }
}
@media (max-width: 360px) {
  .nav__inner  { padding: 0 8px; gap: 6px; }
  .nav__wallet { max-width: 96px; }
  .nav__wallet > * { max-width: 96px; }
}

/* ---------------------------------------------------------------- the drawer
   The other half of the report: opening the menu on a phone covered the
   header with the drawer, so Home and Cards were hidden behind the very
   bar you tapped to reach them.

   `.nav__menu` was `position: fixed; top: 76px`. Fixed positions against
   the viewport, and 76px is the height of the nav bar, so that top only
   lands correctly if the nav starts at y=0. It does not. The testnet
   banner sits above it, and the nav is `position: sticky`, so at the top
   of the page the nav's bottom edge is banner + 76. The drawer opened
   roughly a banner's height too high, straight over the header.

   P2-155 made this worse by "fixing" it to `top: 64px` under 620px,
   moving the drawer 12px further up the thing it was already covering.

   No fixed number can be right here, because the banner is copy and its
   height depends on how that copy wraps. So the drawer stops guessing:
   it is positioned against `.nav` itself (sticky counts as positioned,
   so it is a containing block) and pinned to `top: 100%`, the header's
   own bottom edge. It now follows the header wherever the header goes,
   including once the banner scrolls away and the nav sticks to y=0.

   The height cap goes with it. `calc(100vh - 76px)` was measured from
   the same wrong origin; `70vh` needs no origin and leaves the tap
   target for closing the drawer visible below it. */
@media (max-width: 960px) {
  .nav__menu {
    position: absolute;
    /* The containing block is `.nav__inner`, which carries
       `position: relative` for the plate decoration and does not include
       `.nav`'s 4px bottom border. 100% lands the drawer on top of that
       border; the 4px clears it. */
    top: calc(100% + 4px);
    left: 0;
    right: 0;
    width: auto;
    max-width: 100%;
    /* .nav__menu is a flex child in the desktop row and carries a left
       margin for spacing there. Out of the row it is dead inset. */
    margin-left: 0;
    max-height: 70vh;
    overflow-y: auto;
  }
}

/* Belt and braces. Nothing on a phone should be able to set the page's
   scroll width, and if something does, it scrolls inside itself. */
html, body { max-width: 100%; }
@media (max-width: 900px) { body { overflow-x: hidden; } }

/* ==================================================================
   P2-177. THE LOCKED PLAY BUTTON.

   The lock itself is in the markup (no href) and in play-gate.js (which
   puts the href back on launch day). This is only how it reads.

   Two states worth keeping apart:

   The big buttons carry a second line, "Opens 20 September". They stay
   gold and legible rather than being greyed out, because a grey button
   reads as broken and this one is not broken, it is early. The cursor
   and the aria state carry the "you cannot press this" part; the date
   carries the reason.

   The header and footer links are small, and a second line there would
   be longer than the link. They dim, and the date is in the title
   attribute for anyone who hovers.
   ================================================================== */

a.is-locked {
  cursor: not-allowed;
  pointer-events: none;   /* belt to the markup's braces */
}

/* Large call-to-action buttons: dressed down, not switched off. */
.btn.is-locked {
  position: relative;
  /* 3 Sep 2026 notes: locked Enter Estova buttons keep the full yellow of
     the nav button - no desaturation. The lock speaks through the hover
     tip instead of a greyed face. */
}
/* hover tip on locked play buttons: "Opening 26 SEP" (client-specified
   copy; the 20 Sep badge elsewhere is unchanged - flagged upstream). */
.btn.is-locked[data-tip]:hover::after {
  content: attr(data-tip);
  position: absolute;
  left: 50%;
  bottom: calc(100% + 10px);
  transform: translateX(-50%);
  white-space: nowrap;
  padding: 7px 12px;
  font-size: 0.62em;
  letter-spacing: 0.05em;
  color: #fff;
  background: var(--wood-deep, #3d2811);
  border: 2px solid var(--gold, #E8C169);
  border-radius: 6px;
  pointer-events: none;
  z-index: 30;
}
.btn.is-locked .btn__lock {
  display: block;
  margin-top: 4px;
  font-size: 0.62em;
  letter-spacing: 0.04em;
  line-height: 1.2;
  opacity: 0.85;
  font-weight: 700;
  white-space: nowrap;
}
/* The hero button centres its label, so the second line has to centre too. */
.btn--lg.is-locked, .btn--hero.is-locked {
  flex-direction: column;
  line-height: 1.1;
}

/* Header and footer: dim, no second line. */
.btn--small.is-locked { filter: saturate(0.6) brightness(0.85); opacity: 0.7; }
.footer a.is-locked   { opacity: 0.5; text-decoration: none; }

/* ==================================================================
   7 Sep 2026. THE RANK LADDER ON PHONES.

   Two columns of `1fr` are only equal while nothing inside them is wider
   than its share. "GRANDMASTER" in Press Start 2P at 1.02rem is, so that
   column grew, the other shrank, and the whole grid spilled past the
   panel's right edge. `minmax(0, 1fr)` pins both columns to an equal
   share of the panel whatever the label wants, and the label is sized to
   fit that share on a 360px screen with room to spare.
   ================================================================== */
@media (max-width: 520px) {
  .arena__ladder {
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: 10px;
    width: 100%;
  }
  .arena__tier { padding-left: 4px; padding-right: 4px; min-width: 0; }
  .arena__tier-name {
    font-size: 0.62rem;
    letter-spacing: 0.04em;
    line-height: 1.4;
    overflow-wrap: anywhere;
  }
  .arena__tier-medal { width: 72px; height: 97px; }
}

/* ==================================================================
   8 Sep 2026. THE TRAILER, LIVE STATES. See site.js trailer module.
   ================================================================== */
.trailer__poster { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; display: block; }
.trailer__player { position: absolute; inset: 0; width: 100%; height: 100%; border: 0; display: block; background: #000; }
/* The thumb is inset 12px inside a 16:9 frame, so it is a touch wider than 16:9
   and a contained video pillarboxes. Cover fills it; the crop is ~2% top and bottom. */
video.trailer__player { object-fit: cover; }
.trailer--playing { cursor: default; }
.trailer--playing:hover { transform: none; }
.trailer--playing .trailer__thumb::after { display: none; }
.trailer--ready .trailer__play { box-shadow: inset 0 6px 0 var(--copper-light), inset 0 -6px 0 var(--copper-deep), 0 8px 0 var(--wood-deep), 0 0 0 10px rgba(0,0,0,.25); }

/* ==================================================================
   8 Sep 2026. SEVEN LINKS IN THE NAV (Raffle added).

   With Raffle in the row, "Docs" slid under the Sign in chip inside the
   1280px container. The menu is the flexible part, so it tightens first
   and the right-hand group is barred from overlapping: the menu gets a
   hard right edge and the links shrink in two steps by viewport width.
   ================================================================== */
.nav__menu { margin-left: 12px; overflow: hidden; }
.nav__link { padding: 10px 10px; letter-spacing: 0.03em; font-size: 0.58rem; }
.nav__right { gap: 8px; }
.nav__right .wchip { padding: 9px 10px; }
.nav__right .btn--small { padding: 10px 14px; }
@media (max-width: 1340px) {
  .nav__link { padding: 10px 8px; font-size: 0.54rem; letter-spacing: 0.02em; }
  .nav__menu { margin-left: 8px; }
}
@media (max-width: 1180px) {
  .nav__link { padding: 10px 6px; font-size: 0.5rem; }
  .nav__right .wchip { padding: 8px 8px; }
  .nav__right .btn--small { padding: 9px 10px; font-size: 0.52rem; }
}
