/* ==========================================================================
   Versus - reference design.

   A dark, cinematically lit hero carrying one battle, sitting on a light
   editorial ground that holds the panels and the explore rail. The two sides
   own the only colour on the page; everything else is ink on paper.

   Loaded after app.css, which keeps the battle and admin pages on the older
   dark treatment. Anything here that shares a class name with app.css is a
   deliberate replacement, not an accident.
   ========================================================================== */

:root {
  --paper: #f4f3f0;
  --paper-2: #eceae6;
  --ink: #14141a;
  --ink-2: #5a5a66;
  --ink-3: #8d8d99;
  --card: #ffffff;
  --card-line: rgba(20, 20, 26, 0.09);
  --card-shadow: 0 1px 2px rgba(20, 20, 26, 0.04), 0 8px 24px rgba(20, 20, 26, 0.05);
  --night: #0b0c11;
  --link: #4f6ef7;
  --header-h: 68px;
  --page-max: 1240px;
  --page-pad: 34px;
}

body.page-home,
body.page-light {
  background: var(--paper);
  color: var(--ink);
}

/* The reference sets its type in a geometric sans with a tall x-height and
   tight tracking - noticeably rounder than Inter's grotesque forms. */
body.page-home,
body.page-light {
  font-family: "Plus Jakarta Sans", "Inter", -apple-system, BlinkMacSystemFont,
    "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
  letter-spacing: -0.005em;
}


/* The hero paints its own dark ground, so the header floats over it. */
body.page-home { --header-fg: #ffffff; }
body.page-light { --header-fg: var(--ink); }

/* Shared page measure. */
.site-header__inner,
.hero__grid,
.panels,
.explore__head,
.explore__grid,
.explore__rail,
.page-head,
.empty-state,
.site-footer__grid {
  width: 100%;
  max-width: var(--page-max);
  margin-inline: auto;
  padding-inline: var(--page-pad);
}

/* ------------------------------------------------------------------ header */

.site-header {
  position: absolute;
  inset-inline: 0;
  top: 0;
  z-index: 40;
  color: var(--header-fg, var(--text));
  background: none;
  border-bottom: 0;
  backdrop-filter: none;
}

body.page-light .site-header {
  position: sticky;
  background: color-mix(in srgb, var(--paper) 88%, transparent);
  backdrop-filter: blur(14px);
  border-bottom: 1px solid var(--card-line);
}

.site-header__inner {
  height: var(--header-h);
  display: flex;
  align-items: center;
  gap: 28px;
}

.wordmark {
  display: inline-flex;
  align-items: center;
  gap: 9px;
  font-size: 19px;
  font-weight: 700;
  letter-spacing: -0.02em;
  text-transform: none;
  color: inherit;
  text-decoration: none;
  white-space: nowrap;
}

.wordmark__spark { width: 20px; height: 20px; flex: none; }

.site-nav {
  display: flex;
  align-items: center;
  gap: 30px;
  margin-left: 0;
}

.site-nav a {
  position: relative;
  font-size: 14.5px;
  font-weight: 500;
  color: inherit;
  opacity: 0.66;
  text-decoration: none;
  padding: 6px 0;
  border-radius: 0;
  background: none;
  transition: opacity 0.15s var(--ease);
}

.site-nav a:hover { opacity: 1; color: inherit; background: none; }
.site-nav a[aria-current="page"] { opacity: 1; font-weight: 600; color: inherit; }

/* The active underline sits tight under the word, as in the reference. */
.site-nav a[aria-current="page"]::after {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  bottom: -1px;
  height: 2px;
  border-radius: 2px;
  background: currentColor;
}

.site-header__tools { margin-left: auto; display: flex; align-items: center; gap: 2px; }

/* -------------------------------------------------------------- compact nav

   Below 900px the links do not fit beside the wordmark and the search, and
   until now they were simply hidden - which left Battles and Leaderboard
   reachable on a phone only by tapping a card on the homepage.

   A <details> rather than a scripted button, because the header has to work
   on every page and ships no script of its own: prose routes load
   /js/app.js, battle routes load /js/home.js, and the error and sign-in
   pages load nothing at all. The browser owns the open state, the keyboard
   behaviour and aria-expanded, so the menu is as dependable as the rest of
   this server-rendered shell. */

.site-menu { display: none; position: relative; }

.site-menu__btn {
  width: 42px;
  height: 42px;
  display: grid;
  place-items: center;
  border-radius: 50%;
  color: inherit;
  cursor: pointer;
  opacity: 0.85;
  list-style: none;
  transition: opacity 0.15s, background 0.15s;
}

/* Chrome and Safari draw their own disclosure triangle; `display: grid` above
   sheds it in Firefox, this sheds it in the WebKit family. */
.site-menu__btn::-webkit-details-marker { display: none; }

.site-menu__btn:hover { opacity: 1; background: color-mix(in srgb, currentColor 10%, transparent); }
.site-menu__btn:focus-visible { outline: 2px solid currentColor; outline-offset: 2px; }
.site-menu__btn svg { width: 21px; height: 21px; display: block; }

/* One button, two glyphs: the bars become a cross while the panel is open. */
.site-menu__x { display: none; }
.site-menu[open] .site-menu__bars { display: none; }
.site-menu[open] .site-menu__x { display: block; }

/* Anchored to the button rather than laid into the flow, so opening the menu
   over the homepage does not push the hero down the page. */
.site-menu__panel {
  position: absolute;
  z-index: 50;
  top: calc(100% + 10px);
  right: 0;
  min-width: 208px;
  display: grid;
  padding: 8px;
  border-radius: 14px;
  border: 1px solid rgba(255, 255, 255, 0.12);
  background: rgba(12, 13, 19, 0.97);
  box-shadow: 0 18px 44px rgba(5, 6, 10, 0.5);
}

/* Dark is the default, not the exception: the homepage header floats over the
   hero art, and the admin pages pass no body class at all, so they sit on
   app.css's dark ground. Only the light editorial pages invert it. */
body.page-light .site-menu__panel {
  border-color: var(--card-line);
  background: color-mix(in srgb, var(--card) 97%, transparent);
  box-shadow: 0 18px 44px rgba(20, 20, 26, 0.16);
}

.site-menu__panel a {
  display: flex;
  align-items: center;
  min-height: 44px;
  padding: 0 12px;
  border-radius: 9px;
  font-size: 15px;
  font-weight: 500;
  color: inherit;
  opacity: 0.72;
  text-decoration: none;
}

.site-menu__panel a:hover { opacity: 1; background: color-mix(in srgb, currentColor 8%, transparent); }

.site-menu__panel a[aria-current="page"] {
  opacity: 1;
  font-weight: 700;
  background: color-mix(in srgb, currentColor 10%, transparent);
}

/* ------------------------------------------------------------------ search */

.site-search {
  display: flex;
  align-items: center;
  border-radius: 999px;
  border: 1px solid transparent;
  transition: border-color 0.2s var(--ease), background 0.2s var(--ease);
}

.site-search.is-open {
  border-color: color-mix(in srgb, currentColor 26%, transparent);
  background: color-mix(in srgb, currentColor 8%, transparent);
}

.site-search__input {
  width: 0;
  padding: 0;
  border: 0;
  background: none;
  color: inherit;
  font: inherit;
  font-size: 14px;
  outline: none;
  transition: width 0.24s var(--ease-out), padding 0.24s var(--ease-out);
}

.site-search.is-open .site-search__input { width: 190px; padding: 0 4px 0 16px; }
.site-search__input::placeholder { color: currentColor; opacity: 0.45; }

/* Chrome draws its own clear affordance on a type=search; ours is Escape. */
.site-search__input::-webkit-search-cancel-button { display: none; }

.site-search__btn {
  flex: none;
  width: 38px;
  height: 38px;
  display: grid;
  place-items: center;
  border-radius: 50%;
  border: 0;
  background: none;
  color: inherit;
  cursor: pointer;
  opacity: 0.8;
  transition: opacity 0.15s, background 0.15s;
}

.site-search__btn svg { width: 19px; height: 19px; }
.site-search__btn:hover { opacity: 1; background: color-mix(in srgb, currentColor 10%, transparent); }

/* -------------------------------------------------------------------- hero */

.hero {
  position: relative;
  isolation: isolate;
  display: block;
  max-width: none;
  background: var(--night);
  color: #fff;
  padding: calc(var(--header-h) + 18px) 0 74px;
  overflow: hidden;
}

/* Two accent washes and a warm centre spot, so the objects sit in real light. */
.hero__bg {
  position: absolute;
  inset: 0;
  z-index: -1;
  background:
    radial-gradient(58% 68% at 18% 42%, color-mix(in srgb, var(--a) 26%, transparent), transparent 68%),
    radial-gradient(58% 68% at 82% 42%, color-mix(in srgb, var(--b) 26%, transparent), transparent 68%),
    radial-gradient(46% 52% at 50% 8%, rgba(255, 244, 224, 0.16), transparent 70%),
    linear-gradient(180deg, #101219 0%, #0b0c11 58%, #08090d 100%);
}

/* A soft floor, so the plinths read as standing on something. */
.hero::after {
  content: "";
  position: absolute;
  inset-inline: 0;
  bottom: 0;
  height: 220px;
  z-index: -1;
  background: linear-gradient(180deg, transparent, rgba(0, 0, 0, 0.55));
  pointer-events: none;
}

/*
 * Two halves, each holding one object with its name and total centred under
 * it. The centre block is overlaid rather than given a column of its own: in
 * the reference the objects are wide enough to pass behind it, and a third
 * column would push them apart.
 */
.hero__grid {
  position: relative;
  display: grid;
  grid-template-columns: 1fr 1fr;
  align-items: start;
  gap: 8px;
  min-height: 430px;
}

/* ------------------------------------------------------------- hero centre */

.hero__center {
  position: absolute;
  top: 0;
  left: 50%;
  transform: translateX(-50%);
  width: min(420px, 44%);
  z-index: 3;
  display: grid;
  justify-items: center;
  gap: 14px;
  padding-top: 8px;
}

.live-pill {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  padding: 6px 14px;
  border-radius: 999px;
  border: 1px solid rgba(74, 222, 128, 0.42);
  background: rgba(74, 222, 128, 0.1);
  color: #6ee7a0;
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.13em;
}

.live-pill i {
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: currentColor;
  animation: pulse 2.4s var(--ease) infinite;
}

.live-pill--off {
  border-color: rgba(20, 20, 26, 0.16);
  background: rgba(20, 20, 26, 0.04);
  color: var(--ink-3);
}

/*
 * A battle that is not open: same shape, no green, no pulse. Unlike
 * --off this sits on the dark hero art, so it stays a light-on-dark chip.
 */
.live-pill--quiet {
  border-color: rgba(255, 255, 255, 0.28);
  background: rgba(255, 255, 255, 0.1);
  color: rgba(255, 255, 255, 0.86);
}

.live-pill--quiet i { animation: none; opacity: 0.65; }

.hero__tagline {
  margin: 0;
  font-size: 15px;
  color: rgba(255, 255, 255, 0.72);
  text-align: center;
  max-width: 34ch;
}

/* The countdown: four numerals with small caps beneath. */
.cd {
  display: grid;
  grid-auto-flow: column;
  gap: 26px;
  margin-top: 2px;
}

.cd__block { display: grid; justify-items: center; gap: 2px; }

.cd__value {
  font-size: 46px;
  font-weight: 800;
  line-height: 1;
  letter-spacing: -0.035em;
  font-variant-numeric: tabular-nums;
}

.cd__label {
  font-size: 9.5px;
  font-weight: 600;
  letter-spacing: 0.16em;
  color: rgba(255, 255, 255, 0.5);
}

.cd--ended .cd__value { color: rgba(255, 255, 255, 0.4); }

.hero__vs {
  margin: 10px 0 2px;
  font-size: 30px;
  font-weight: 800;
  letter-spacing: -0.02em;
  color: rgba(255, 255, 255, 0.92);
}

.meter {
  width: min(300px, 100%);
  height: 7px;
  border-radius: 999px;
  overflow: hidden;
  display: flex;
  background: rgba(255, 255, 255, 0.14);
}

.meter__seg { display: block; height: 100%; transition: width 0.6s var(--ease-out); }
.meter__seg--a { background: var(--a); }
.meter__seg--b { background: var(--b); }

.leadchip {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  margin: 4px 0 0;
  padding: 8px 17px;
  border-radius: 999px;
  border: 1px solid rgba(255, 255, 255, 0.15);
  background: rgba(255, 255, 255, 0.06);
  font-size: 11.5px;
  font-weight: 700;
  letter-spacing: 0.07em;
  text-transform: uppercase;
  color: rgba(255, 255, 255, 0.9);
}

.leadchip .icon { width: 14px; height: 14px; color: #f0c674; }

/* --------------------------------------------------------------- hero side */

.hero__side {
  position: relative;
  z-index: 1;
  display: grid;
  justify-items: center;
  align-content: start;
  text-align: center;
  min-width: 0;
}

.hero__name {
  margin: 0;
  font-size: clamp(30px, 3.4vw, 44px);
  font-weight: 800;
  letter-spacing: -0.035em;
  line-height: 1.05;
}

.hero__total {
  margin: 4px 0 0;
  font-size: clamp(32px, 3.8vw, 48px);
  font-weight: 800;
  letter-spacing: -0.04em;
  line-height: 1;
  font-variant-numeric: tabular-nums;
}

.hero__share {
  margin: 8px 0 0;
  font-size: 13px;
  font-weight: 500;
  letter-spacing: 0.01em;
  color: rgba(255, 255, 255, 0.72);
}

/*
 * The buttons sit under their own side's object. `.hero__actions` - a centred
 * row of its own, narrower than the two halves - was the pre-scene layout and
 * has not been rendered since; its rules are gone rather than left to look
 * live. The one that hid the amount on a narrow screen never fired because of
 * it, which is why the amount was still padding the button out on a phone.
 */
.hero__act { display: grid; }

.side-error {
  margin: 8px 0 0;
  font-size: 12.5px;
  font-weight: 600;
  color: #fca5a5;
}

/* --------------------------------------------------------------- figurines */

.fig {
  position: relative;
  display: grid;
  place-items: end center;
  width: 100%;
}

/*
 * Only the two hero objects get a real blurred glow. The explore rail can hold
 * a dozen figurines at once, and a dozen blur layers is genuinely expensive to
 * composite - the card art already carries its own accent wash, so the glow
 * there is redundant as well as costly.
 */
.fig__glow { display: none; }

.fig--hero .fig__glow {
  display: block;
  position: absolute;
  inset: auto 8% 4%;
  height: 42%;
  border-radius: 50%;
  background: radial-gradient(closest-side, color-mix(in srgb, var(--side-accent) 34%, transparent), transparent);
  filter: blur(26px);
  pointer-events: none;
}

.fig__object { position: relative; width: 100%; display: grid; place-items: center; }
.fig__object svg,
.fig__img { display: block; width: 100%; height: auto; }

.fig--hero .fig__object { max-width: 352px; }
.fig--card .fig__object { max-width: 128px; }

.fig--hero .fig__img { filter: drop-shadow(0 26px 40px rgba(0, 0, 0, 0.55)); }

/* ------------------------------------------------------------- amount rail */

/* The rail straddles the boundary: half on the dark hero, half on the paper. */
.rail-wrap {
  position: relative;
  z-index: 5;
  margin-top: -22px;
  display: flex;
  justify-content: center;
  padding-inline: 20px;
}

.rail {
  display: flex;
  align-items: center;
  gap: 8px;
  flex-wrap: wrap;
  justify-content: center;
}

.rail__custom {
  width: 108px;
  padding: 10px 14px;
  border-radius: 999px;
  border: 1px solid var(--card-line);
  background: var(--card);
  color: var(--ink);
  font: inherit;
  font-size: 14px;
  font-weight: 600;
}

/* ------------------------------------------------------------------- chips */

body.page-home .chip,
body.page-light .chip {
  padding: 10px 20px;
  border-radius: 999px;
  border: 1px solid var(--card-line);
  background: var(--card);
  color: var(--ink-2);
  font-size: 14px;
  font-weight: 600;
  line-height: 1;
  cursor: pointer;
  transition: background 0.15s var(--ease), color 0.15s var(--ease), border-color 0.15s var(--ease);
}

body.page-home .chip:hover,
body.page-light .chip:hover { border-color: var(--ink-3); color: var(--ink); }

body.page-home .chip.is-active,
body.page-light .chip.is-active,
body.page-home .chip[aria-pressed="true"],
body.page-light .chip[aria-pressed="true"] {
  background: var(--night);
  border-color: var(--night);
  color: #fff;
}

body.page-home .chip--custom:not(.is-active),
body.page-light .chip--custom:not(.is-active) { color: var(--ink-3); }

/* ------------------------------------------------------------------ panels */

.panels {
  padding-block: 46px 8px;
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 20px;
  align-items: start;
}

.panel-card {
  background: var(--card);
  border: 1px solid var(--card-line);
  border-radius: 16px;
  padding: 20px 22px 18px;
  box-shadow: var(--card-shadow);
  display: grid;
  align-content: start;
  gap: 14px;
  min-width: 0;
}

/* `display: grid` above is an author rule, so it beats the UA stylesheet's
   [hidden] { display: none } and the attribute renders nothing at all. The
   homepage stacks one details card per hero slide and hides all but the active
   one, so without this every battle's card shows at once and the three-column
   grid spills into extra rows. Same reason .sheet[hidden] exists in app.css. */
.panel-card[hidden] { display: none; }

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

.panel-card__title {
  display: inline-flex;
  align-items: center;
  gap: 9px;
  margin: 0;
  font-size: 15.5px;
  font-weight: 700;
  letter-spacing: -0.01em;
  color: var(--ink);
}

.panel-card__title .icon { width: 17px; height: 17px; color: var(--ink); }
.panel-card__sub { font-weight: 500; color: var(--ink-3); }

.panel-card__link,
.panel-card__more {
  font-size: 12.5px;
  font-weight: 600;
  color: var(--link);
  text-decoration: none;
}

.panel-card__link:hover,
.panel-card__more:hover { text-decoration: underline; }

.icon { flex: none; }

/* --------------------------------------------------------------- live feed */

.feed { list-style: none; margin: 0; padding: 0; display: grid; gap: 2px; }

.feed__row {
  display: grid;
  grid-template-columns: 26px minmax(0, auto) 1fr auto;
  grid-template-areas:
    "avatar who  side   amount"
    "avatar who  side   when";
  align-items: center;
  column-gap: 8px;
  padding: 7px 0;
  font-size: 13px;
  min-width: 0;
}

/* One line at full width; the timestamp tucks under the amount when tight. */
@media (min-width: 460px) {
  .feed__row {
    grid-template-columns: 26px auto auto minmax(0, 1fr) auto auto;
    grid-template-areas: "avatar who verb side amount when";
  }
}

/* The side's own mark, tinted to its accent, rather than an anonymous disc. */
.feed__avatar {
  grid-area: avatar;
  width: 26px;
  height: 26px;
  display: grid;
  place-items: center;
  border-radius: 50%;
  color: var(--side-accent);
  background: color-mix(in srgb, var(--side-accent) 13%, var(--card));
  box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--side-accent) 30%, transparent);
}

.feed__who { grid-area: who; font-weight: 600; color: var(--ink); white-space: nowrap; }
.feed__verb { grid-area: verb; color: var(--ink-3); display: none; }
.feed__side {
  grid-area: side;
  color: var(--ink-2);
  font-weight: 500;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.feed__amount {
  grid-area: amount;
  font-weight: 700;
  color: var(--ink);
  font-variant-numeric: tabular-nums;
  text-align: right;
  white-space: nowrap;
}
.feed__when { grid-area: when; font-size: 11.5px; color: var(--ink-3); text-align: right; white-space: nowrap; }

@media (min-width: 460px) {
  .feed__verb { display: inline; }
  .feed__when { padding-left: 12px; }
}

/* ------------------------------------------------------------ battle stats */

.kpis { display: grid; grid-template-columns: repeat(3, 1fr); gap: 4px; }

.kpi { display: grid; gap: 5px; padding-right: 10px; min-width: 0; }
.kpi + .kpi { border-left: 1px solid var(--card-line); padding-left: 14px; }

.kpi__label {
  display: flex;
  align-items: center;
  gap: 6px;
  margin: 0;
  font-size: 11.5px;
  color: var(--ink-3);
  white-space: nowrap;
}

.kpi__label .icon { width: 14px; height: 14px; }

.kpi__value {
  margin: 0;
  font-size: 19px;
  font-weight: 800;
  letter-spacing: -0.03em;
  color: var(--ink);
  font-variant-numeric: tabular-nums;
  overflow: hidden;
  text-overflow: ellipsis;
}

.progress { display: grid; gap: 8px; padding-top: 14px; border-top: 1px solid var(--card-line); }

.progress__label {
  display: flex;
  align-items: center;
  gap: 7px;
  margin: 0;
  font-size: 12.5px;
  font-weight: 600;
  color: var(--ink-2);
}

.progress__label .icon { width: 15px; height: 15px; }

.progress__track { height: 5px; border-radius: 999px; background: var(--paper-2); overflow: hidden; }
.progress__track i { display: block; height: 100%; border-radius: 999px; background: var(--link); }

.progress__ends {
  display: flex;
  justify-content: space-between;
  margin: 0;
  font-size: 11.5px;
  color: var(--ink-3);
}

/* ------------------------------------------------------------- leaderboard */

.board { list-style: none; margin: 0; padding: 0; display: grid; }

.board__row {
  display: grid;
  grid-template-columns: 18px minmax(0, 1fr) auto 46px;
  align-items: center;
  gap: 10px;
  padding: 9px 0;
  font-size: 13.5px;
  border-top: 1px solid transparent;
}

.board__row + .board__row { border-top-color: var(--card-line); }

.board__rank { color: var(--ink-3); font-size: 12.5px; font-weight: 600; }

.board__name {
  display: inline-flex;
  align-items: center;
  gap: 9px;
  font-weight: 600;
  color: var(--ink);
  text-decoration: none;
  min-width: 0;
}

.board__name:hover { text-decoration: underline; }

.board__dot {
  width: 24px;
  height: 24px;
  flex: none;
  display: grid;
  place-items: center;
  border-radius: 50%;
  color: var(--side-accent);
  background: color-mix(in srgb, var(--side-accent) 13%, var(--card));
  box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--side-accent) 30%, transparent);
}

.board__total { font-weight: 700; color: var(--ink); font-variant-numeric: tabular-nums; }
.board__share { font-size: 11.5px; color: var(--ink-3); text-align: right; }

/* ----------------------------------------------------------------- explore */

.explore { background: var(--paper-2); padding-block: 34px 66px; margin-top: 46px; }
.explore--page { background: none; margin-top: 0; padding-block: 8px 66px; }


.explore__head {
  display: flex;
  align-items: flex-end;
  justify-content: space-between;
  gap: 20px;
  margin-bottom: 20px;
}

.explore__title {
  margin: 0;
  font-size: clamp(28px, 3.4vw, 36px);
  font-weight: 800;
  letter-spacing: -0.035em;
  color: var(--ink);
}

.explore__sub { margin: 5px 0 0; font-size: 14px; color: var(--ink-2); }
.explore__nav { display: flex; gap: 10px; flex: none; }

.round-btn {
  width: 42px;
  height: 42px;
  display: grid;
  place-items: center;
  border-radius: 50%;
  border: 1px solid var(--card-line);
  background: var(--card);
  color: var(--ink);
  cursor: pointer;
  transition: border-color 0.15s, opacity 0.15s;
}

.round-btn svg { width: 18px; height: 18px; }
.round-btn:hover:not(:disabled) { border-color: var(--ink-3); }
.round-btn:disabled { opacity: 0.35; cursor: default; }

/* The rail scrolls horizontally and snaps; the grid version wraps instead. */
.explore__rail {
  display: flex;
  gap: 16px;
  overflow-x: auto;
  scroll-snap-type: x mandatory;
  scroll-padding-left: var(--page-pad);
  padding-block: 4px 8px;
  scrollbar-width: none;
}

.explore__rail::-webkit-scrollbar { display: none; }
.explore__rail > .battle-card { flex: 0 0 clamp(210px, 21vw, 244px); scroll-snap-align: start; }

.explore__grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(232px, 1fr));
  gap: 16px;
}


/* ------------------------------------------------------------- battle card */

.battle-card {
  display: grid;
  align-content: start;
  border-radius: 16px;
  overflow: hidden;
  background: var(--night);
  color: #fff;
  text-decoration: none;
  border: 1px solid rgba(255, 255, 255, 0.07);
  transition: transform 0.24s var(--ease-out), box-shadow 0.24s var(--ease-out);
  min-width: 0;
}

.battle-card:hover { transform: translateY(-3px); box-shadow: 0 16px 34px rgba(20, 20, 26, 0.22); }

.battle-card__art {
  position: relative;
  padding: 14px 12px 6px;
  background:
    radial-gradient(52% 62% at 26% 46%, color-mix(in srgb, var(--a) 34%, transparent), transparent 70%),
    radial-gradient(52% 62% at 74% 46%, color-mix(in srgb, var(--b) 34%, transparent), transparent 70%),
    linear-gradient(180deg, #14161d, #0b0c11);
}

.battle-card__art .live-pill--sm {
  position: absolute;
  top: 12px;
  left: 12px;
  z-index: 2;
  padding: 4px 9px;
  font-size: 9px;
  letter-spacing: 0.11em;
}

.battle-card__art .live-pill--sm i { width: 5px; height: 5px; }

.battle-card__pair {
  display: grid;
  grid-template-columns: 1fr 1fr;
  align-items: end;
  gap: 4px;
  min-height: 122px;
}

.battle-card__body { padding: 12px 14px 14px; display: grid; gap: 7px; }

.battle-card__names {
  display: flex;
  align-items: baseline;
  gap: 7px;
  margin: 0;
  font-size: 14.5px;
  font-weight: 700;
  letter-spacing: -0.02em;
  min-width: 0;
}

.battle-card__names > span:not(.battle-card__vs) {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.battle-card__vs { font-size: 11px; font-weight: 600; color: rgba(255, 255, 255, 0.45); flex: none; }

.battle-card__totals {
  display: flex;
  align-items: center;
  gap: 9px;
  margin: 0;
  font-size: 13px;
  font-weight: 600;
  color: rgba(255, 255, 255, 0.86);
  font-variant-numeric: tabular-nums;
}

.battle-card__bar { width: 1px; height: 12px; background: rgba(255, 255, 255, 0.22); }

.battle-card__left {
  display: flex;
  align-items: center;
  gap: 6px;
  margin: 0;
  font-size: 11.5px;
  color: rgba(255, 255, 255, 0.5);
}

.battle-card__left .icon { width: 13px; height: 13px; }

/* ----------------------------------------------------------------- buttons */

.btn--light,
.btn--dark {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 7px;
  padding: 15px 26px;
  border-radius: 999px;
  border: 1px solid transparent;
  font-size: 15px;
  font-weight: 600;
  letter-spacing: -0.01em;
  cursor: pointer;
  transition: transform 0.16s var(--ease), background 0.16s var(--ease), box-shadow 0.16s var(--ease);
}

.btn--light { background: #f6f5f2; color: #14141a; }
.btn--light:hover:not(:disabled) { background: #fff; box-shadow: 0 8px 22px rgba(0, 0, 0, 0.28); }

.btn--dark { background: #14161d; color: #fff; border-color: rgba(255, 255, 255, 0.16); }
.btn--dark:hover:not(:disabled) { background: #1c1f28; box-shadow: 0 8px 22px rgba(0, 0, 0, 0.34); }

.btn--light:active:not(:disabled),
.btn--dark:active:not(:disabled) { transform: translateY(1px); }

.btn--light:disabled,
.btn--dark:disabled { opacity: 0.45; cursor: default; }

.btn__sep { opacity: 0.4; }

/* --------------------------------------------------------------- page head */

.page-head { padding-block: 46px 26px; }

.page-head__title {
  margin: 0;
  font-size: clamp(34px, 6vw, 54px);
  font-weight: 800;
  letter-spacing: -0.04em;
  color: var(--ink);
}

.page-head__sub { margin: 8px 0 0; font-size: 16px; color: var(--ink-2); max-width: 56ch; }

.board-page { width: 100%; max-width: 760px; margin-inline: auto; padding: 0 var(--page-pad) 60px; }

.hollow {
  width: 100%;
  max-width: 640px;
  margin-inline: auto;
  padding: 110px var(--page-pad) 90px;
  display: grid;
  justify-items: start;
  gap: 16px;
}

.hollow__title { margin: 0; font-size: clamp(30px, 6vw, 46px); font-weight: 800; letter-spacing: -0.04em; }
.hollow__sub { margin: 0; color: var(--ink-2); font-size: 16px; }

body.page-home .empty-note,
body.page-light .empty-note { color: var(--ink-3); }

/* ------------------------------------------------------------------ footer */

body.page-home .site-footer,
body.page-light .site-footer {
  margin-top: 0;
  background: var(--night);
  border-top: 0;
  color: rgba(255, 255, 255, 0.62);
}

body.page-home .site-footer a,
body.page-light .site-footer a { color: rgba(255, 255, 255, 0.72); }

body.page-home .site-footer a:hover,
body.page-light .site-footer a:hover { color: #fff; }

.site-footer__note { margin-top: 14px; font-size: 13px; }

/* -------------------------------------------------------------- responsive */

@media (max-width: 1080px) {
  .hero__grid { grid-template-columns: 1fr minmax(260px, 340px) 1fr; }
  .fig--hero .fig__object { max-width: 320px; }
  .cd { gap: 18px; }
  .cd__value { font-size: 38px; }
}

@media (max-width: 900px) {
  .panels { grid-template-columns: 1fr; gap: 16px; }
  /* The bar's links hand over to the disclosure beside the search. */
  .site-nav { display: none; }
  .site-menu { display: block; }
}

/* Below this the three-column hero stops working: the centre column becomes a
   band of its own and the two sides sit under it, side by side. */
@media (max-width: 860px) {
  .hero__grid {
    grid-template-columns: 1fr 1fr;
    gap: 18px 12px;
    min-height: 0;
  }
  /* Overlaying the centre stops working once the objects are small: put it
     back in the flow as a band above them. */
  .hero__center {
    position: static;
    transform: none;
    width: auto;
    grid-column: 1 / -1;
    grid-row: 1;
    padding-bottom: 10px;
  }
  .hero__side--a { grid-column: 1; }
  .hero__side--b { grid-column: 2; }
  .fig--hero .fig__object { max-width: 220px; }
  .btn__sep { display: none; }
}

@media (max-width: 620px) {
  :root { --page-pad: 18px; }
  .cd { gap: 12px; }
  .cd__value { font-size: 30px; }
  .cd__label { font-size: 8.5px; letter-spacing: 0.1em; }
  .hero__name { font-size: 22px; }
  .hero__total { font-size: 24px; }
  .kpis { grid-template-columns: 1fr; }
  .kpi + .kpi {
    border-left: 0;
    padding-left: 0;
    border-top: 1px solid var(--card-line);
    padding-top: 10px;
  }
  .explore__head { flex-direction: column; align-items: flex-start; }

  /*
   * The open search shares the row with the wordmark and the menu button now,
   * and a flat 130px was 7px too wide for a 360px phone once the menu joined
   * it - which the body's hidden overflow would have swallowed rather than
   * shown.
   *
   * So the field is sized against what the rest of the row costs rather than
   * against a guessed fraction: 245px is the wordmark, the page padding, the
   * gap, the field's own padding and border, the search button and the menu
   * button added up. It still opens to the full 130px on any screen from
   * about 375px up, and gives back exactly what it has to below that. The
   * clamp floor keeps the width valid if the sum ever goes negative.
   *
   * The tighter gap is a minimum, not a spacing change: `.site-header__tools`
   * is pushed right by `margin-left: auto`, so it only bites when the row is
   * already full.
   */
  .site-header__inner { gap: 12px; }
  .site-search.is-open .site-search__input { width: clamp(0px, calc(100vw - 245px), 130px); }
}

/* ==========================================================================
   Hero scene mode
   --------------------------------------------------------------------------
   When a battle ships a single wide artwork holding both objects, their light
   and their floor, the hero stops being a flow layout and becomes an overlay:
   the art is laid in at its native 1254x579 aspect and every live element is
   pinned to the coordinate the art was composed for.

   `--u` is one unit of that coordinate space, so the whole composition scales
   as a single piece and nothing can drift out of register - a long battle
   description cannot push the countdown down onto the plinths, because every
   block is positioned rather than stacked.
   ========================================================================== */

.hero--scene {
  /*
   * The landing view is the picture. The hero is exactly one screen tall, so
   * opening the site puts you inside the battle rather than at the top of a
   * page about one; everything else begins below the fold.
   *
   * svh rather than vh: on a phone, vh is the viewport with the browser's
   * chrome hidden, which leaves the bottom of the composition under the
   * address bar until the user scrolls.
   */
  /*
   * One screen tall, and the picture is scaled to that box rather than fitted
   * to its own aspect - so it reaches all four edges with nothing cropped and
   * nothing left blank. Sizing it any other way trades one for the other: fit
   * the aspect and a band of the next section shows underneath; cover the box
   * and the sides of the scene get cut off.
   *
   * The scaling is linear on each axis, so every overlay anchor still lands on
   * the same point of the picture it was measured against.
   *
   * The bounds only catch window shapes far enough from the artwork's own that
   * the stretch would start to show; on any ordinary desktop this is 100svh.
   */
  height: clamp(35vw, 100svh, 60vw);
  min-height: 480px;

  /* 1 / 1254 of the viewport, capped so the art stops growing on huge screens. */
  --u: min(0.92px, 0.079745vw);

  /*
   * One anchor per side, the same on every battle.
   *
   * These were once measured per plate and followed each artwork's own object
   * centres, which ranged from 17.7% to 26.5%. That put the type fractionally
   * under each object - but it also meant the names, totals and buttons
   * hopped sideways every time the deck slid, because the furniture moved
   * with the picture. Fixed values sit within every plate's object footprint
   * and hold still while the art changes behind them.
   */
  --anchor-a: 24%;
  --anchor-b: 76%;
  position: relative;
  display: block;
  padding: 0;
  background: var(--night);
  overflow: hidden;
}

/*
 * On a screen wider than the composition, the art is blown up and blurred
 * behind it so the margins read as the same room, out of focus - rather than
 * as two black bars.
 */
.hero__wash {
  position: absolute;
  inset: 0;
  z-index: 0;
  background:
    radial-gradient(60% 70% at 50% 38%, rgba(32, 34, 44, 0.9), transparent 72%),
    var(--night);
  pointer-events: none;
}

/*
 * The drawn hero: the same room, built in CSS for battles with no photograph.
 * It breaks out of the stage to the full width of the screen, so these battles
 * reach the edges exactly as the photographed one does.
 */
.hero--drawn .hero__floor {
  position: absolute;
  inset: 0;
  z-index: 0;
  background:
    radial-gradient(58% 62% at 23.7% 44%, color-mix(in srgb, var(--a) 30%, transparent), transparent 68%),
    radial-gradient(58% 62% at 76.3% 44%, color-mix(in srgb, var(--b) 30%, transparent), transparent 68%),
    radial-gradient(44% 46% at 50% 4%, rgba(255, 244, 224, 0.20), transparent 70%),
    linear-gradient(180deg, #121520 0%, #0c0e14 52%, #090a0f 100%);
}

/*
 * The floor. A hard-edged band reads as a seam across the picture, so the
 * horizon is a soft gradient and each object gets an elliptical pool of light
 * under it instead - which is what makes them look stood on something rather
 * than pasted onto a gradient.
 */
.hero--drawn .hero__floor::after {
  content: "";
  position: absolute;
  inset: 0;
  background:
    radial-gradient(34% 13% at 23.7% 66%, color-mix(in srgb, var(--a) 26%, transparent), transparent 70%),
    radial-gradient(34% 13% at 76.3% 66%, color-mix(in srgb, var(--b) 26%, transparent), transparent 70%),
    radial-gradient(26% 9% at 23.7% 65.5%, rgba(0, 0, 0, 0.55), transparent 72%),
    radial-gradient(26% 9% at 76.3% 65.5%, rgba(0, 0, 0, 0.55), transparent 72%),
    linear-gradient(180deg, transparent 52%, rgba(0, 0, 0, 0.22) 66%, rgba(0, 0, 0, 0.58) 100%);
}

/* Figurines stand where the photographed objects stand: centres 297 and 957. */
.hero--drawn .fig--hero {
  position: absolute;
  top: calc(62 * var(--u));
  left: 50%;
  transform: translateX(-50%);
  width: calc(310 * var(--u));
  height: calc(315 * var(--u));
  place-items: end center;
  z-index: 1;
}

.hero--drawn .fig--hero .fig__object { max-width: none; width: 100%; }

/*
 * Full width, always. The plates are 1866px across - wider than a typical
 * desktop viewport - so filling the screen downscales them rather than
 * stretching them. Capping the stage narrower than the window was what left a
 * dead panel down each side that no amount of feathering could disguise.
 *
 * Type size stays pinned to `--u`, which is capped, so the picture growing to
 * the screen edges does not drag the lettering up with it.
 */
/*
 * The stage keeps the artwork's own aspect and is sized from the height, so it
 * is as wide as that aspect demands and overflows the screen sideways rather
 * than letterboxing. That matters for more than looks: every overlay anchor is
 * a percentage of this box, so the box staying true to the picture is what
 * keeps the names and buttons sitting under the objects they belong to. The
 * overflow is cropped evenly by the section, and nothing in the overlay lives
 * outside the middle 22-78%, so no live element is ever clipped.
 */
.hero__stage {
  position: relative;
  z-index: 1;
  width: 100%;
  height: 100%;
}

/*
 * The artwork covers its stage and nothing more. Earlier this element was
 * stretched past the stage over invented filler, which smeared the blossom and
 * the Parliament windows into horizontal streaks and left a hard seam where
 * real photograph met fabricated edge. Now the picture simply ends - and its
 * outer few pixels are masked to transparent so it settles into the surround
 * instead of stopping on a line.
 */
.hero__art {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: fill;
  z-index: 0;
  user-select: none;
  pointer-events: none;
}

/* Every overlay block is pinned to the art, centred on its own anchor. */
.hero--scene .hero__center,
.hero--scene .hero__center > *,
.hero--scene .hero__side > * {
  position: absolute;
  margin: 0;
  z-index: 2;
  left: 50%;
  transform: translateX(-50%);
  white-space: nowrap;
}

.hero--scene .hero__center {
  inset: 0;
  left: 0;
  width: 100%;
  transform: none;
  display: block;
  padding: 0;
  pointer-events: none;
}

.hero--scene .hero__center > * { pointer-events: auto; }

.hero--scene .live-pill {
  top: 13.5%;
  padding: calc(6 * var(--u)) calc(14 * var(--u));
  font-size: calc(11 * var(--u));
  letter-spacing: calc(1.4 * var(--u));
}

/*
 * Anchored by its bottom edge, not its top: a battle whose description runs to
 * two lines grows upward into the empty band under the pill, instead of
 * pushing the countdown down onto the plinths.
 */
/*
 * Centred inside the band between the pill and the countdown rather than
 * anchored to either. A one-line description lands exactly where the artwork
 * expects it; a two-line one fills the band instead of pushing the countdown
 * down onto the plinths or riding up over the pill.
 */
.hero--scene .hero__tagline {
  top: 18.1%;
  height: 7.9%;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: calc(15 * var(--u));
  max-width: calc(470 * var(--u));
  white-space: normal;
  text-wrap: balance;
  line-height: 1.35;
  overflow: hidden;
}

.hero--scene .cd {
  top: 26.1%;
  gap: calc(30 * var(--u));
}

.hero--scene .cd__value  { font-size: calc(45 * var(--u)); line-height: 1; }
.hero--scene .cd__label  { font-size: calc(10 * var(--u)); letter-spacing: calc(1.6 * var(--u)); margin-top: calc(6 * var(--u)); }

.hero--scene .hero__vs {
  top: 44.4%;
  font-size: calc(31 * var(--u));
  line-height: 1;
}

.hero--scene .meter {
  top: 53.4%;
  width: calc(293 * var(--u));
  height: calc(11 * var(--u));
}

.hero--scene .leadchip {
  top: 58.4%;
  padding: calc(8 * var(--u)) calc(17 * var(--u));
  font-size: calc(11.5 * var(--u));
  letter-spacing: calc(0.8 * var(--u));
}

.hero--scene .leadchip .icon { width: calc(14 * var(--u)); height: calc(14 * var(--u)); }

/* The two objects in the art are centred at 297 and 957 of 1254. */
.hero--scene .hero__side {
  position: absolute;
  top: 0;
  bottom: 0;
  width: calc(430 * var(--u));
  display: block;
  text-align: center;
  z-index: 2;
}

.hero--scene .hero__side--a { left: var(--anchor-a); transform: translateX(-50%); }
.hero--scene .hero__side--b { left: var(--anchor-b); transform: translateX(-50%); }

.hero--scene .hero__name {
  top: 66.5%;
  font-size: calc(46 * var(--u));
  line-height: 1;
}

.hero--scene .hero__total {
  top: 74.1%;
  font-size: calc(37 * var(--u));
  line-height: 1;
}

.hero--scene .hero__share {
  top: 82.6%;
  font-size: calc(13 * var(--u));
  line-height: 1;
}

.hero--scene .hero__act {
  position: absolute;
  top: 88.4%;
  transform: translateX(-50%);
  z-index: 3;
  display: grid;
  white-space: nowrap;
}

/* The same anchor the name and total use. */
.hero--scene .hero__act--a { left: var(--anchor-a); }
.hero--scene .hero__act--b { left: var(--anchor-b); }

.hero--scene .hero__act .btn {
  min-width: calc(250 * var(--u));
  padding: calc(12 * var(--u)) calc(24 * var(--u));
  font-size: calc(13.5 * var(--u));
  white-space: nowrap;
}

/* The rail sits just below the art's bottom edge, as in the reference. */
body.page-home .rail-wrap { margin-top: calc(-9 * var(--u, 1px)); }

/*
 * Below this the fixed composition is too small to read: the art drops back to
 * a dimmed backdrop and the ordinary stacked hero takes over.
 */
@media (max-width: 860px) {
  .hero--scene {
    --u: 1px;
    height: auto;
    min-height: 0;
    display: block;
    padding: calc(var(--header-h) + 18px) 0 42px;
  }

  .hero__stage {
    aspect-ratio: auto;
    height: auto;
    width: 100%;
    max-width: none;
    display: grid;
    gap: 14px 12px;
    /* minmax(0, …) is load-bearing, as it is in the pay sheet: a bare `1fr`
       floors each track at its content's min-content width, and the support
       button used to carry a 250px min-width down here from the desktop rule.
       Two of those made a 512px grid inside a 390px phone, and since the body
       hides its overflow the right-hand side was cropped away rather than
       scrollable. The tracks must be allowed to shrink. */
    grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
    padding-inline: var(--page-pad);
  }

  /*
   * The plate is 2.21:1 and the stacked hero is close to square, so the
   * picture cannot be both undistorted and full-bleed here. Three things were
   * tried before this one and each was wrong:
   *
   *   `fill`   - inherited from the desktop rule, which stretches to the box
   *              on purpose. Here that is a 2.3x vertical smear.
   *   `cover`  - scales to the height and discards 57% of the width.
   *   width:180% - the same crop, just gentler, on the assumption that the
   *              objects sit at 24% and 76%. They do not. Those two numbers
   *              are where the *type* is anchored (see --anchor-a/b above);
   *              sampling column brightness across all ten plates puts the
   *              lit subject matter at roughly 1% to 100%. These are
   *              full-bleed compositions whose plinths run to the frame
   *              edges, so every horizontal crop cuts an object in half. On
   *              the basketball plate, whose middle is empty floor, a 22-78%
   *              window showed very nearly nothing at all.
   *
   * So: the whole width, always, at the picture's own aspect - a band the
   * hero is wider than, rather than a box the picture is cut to. Nothing is
   * cropped on any plate and nothing is squashed. It is centred because the
   * scrim crushes the lower third and a bottom-anchored band disappeared into
   * it; centred, each object stands directly above its own side's name.
   *
   * No `object-fit` here on purpose: `height: auto` already gives the box the
   * picture's own aspect, so there is nothing left to fit.
   */
  .hero__art {
    inset: auto;
    top: 50%;
    left: 0;
    width: 100%;
    height: auto;
    transform: translateY(-50%);
    opacity: 0.62;
    -webkit-mask-image: linear-gradient(180deg, transparent, #000 16%, #000 84%, transparent);
    mask-image: linear-gradient(180deg, transparent, #000 16%, #000 84%, transparent);
  }

  /*
   * The scrim is shaped for the desktop composition, where it darkens the
   * floor the names and buttons sit on. Against a band a third of that height
   * it simply swallowed the picture. Here it only has to hold text contrast:
   * a little under the header, a little at the foot, and daylight in between.
   */
  .hero__scrim {
    background: linear-gradient(
      180deg,
      rgba(5, 6, 10, 0.55) 0%,
      rgba(5, 6, 10, 0.15) 30%,
      rgba(5, 6, 10, 0.15) 62%,
      rgba(5, 6, 10, 0.72) 100%
    );
  }
  .hero__wash { display: none; }
  .hero--drawn .hero__floor { position: absolute; inset: 0; }
  .hero--drawn .hero__floor::after { inset: 58% 0 0; }

  .hero--drawn .fig--hero {
    position: static;
    transform: none;
    width: 100%;
    height: auto;
    margin-inline: auto;
  }
  .hero--drawn .fig--hero .fig__object { max-width: 200px; }

  .hero--scene .hero__center,
  .hero--scene .hero__center > *,
  .hero--scene .hero__side,
  .hero--scene .hero__side > * {
    position: static;
    transform: none;
    width: auto;
    max-width: none;
    white-space: normal;
  }

  .hero--scene .hero__center {
    grid-column: 1 / -1;
    display: grid;
    justify-items: center;
    gap: 10px;
    inset: auto;
  }

  /*
   * Two escapees from the blanket reset above, both of which were silently
   * broken on every phone.
   *
   * `width: auto` is meant for the type, which was pinned to the artwork's
   * coordinates. The meter is not type: it is a flex box whose only children
   * are percentage-width segments, so on auto it collapsed to zero and the
   * split bar - the one element that says who is ahead - did not render at
   * all. And the reset restores width but not height, so the tagline kept the
   * `height: 7.9%` it uses against the fixed-aspect stage; against an
   * auto-height column that resolved to about 3px and its own `overflow:
   * hidden` clipped the description away.
   */
  /*
   * `position` matters as much as the width here, and for the same reason.
   * The pin is absolutely positioned at `left: <share>%`, which resolves
   * against its nearest positioned ancestor - the meter, by way of
   * `.meter { position: relative }`. The reset above takes that away, so the
   * percentage was measured against `.hero__stage` instead: a 300px bar with
   * its divider placed along a 375px ruler, landing some 16px to the right of
   * the colour boundary it is supposed to mark. Desktop never showed it
   * because there the meter is absolutely positioned and is its own
   * containing block.
   */
  /*
   * `left`/`top` have to go with it. The scene rules place this bar at
   * `top: 53.4%; left: 50%`, and the reset above only neutralised those by
   * making the element static, which leaves offsets inert. Restoring
   * `relative` brings them back to life, so they are cleared explicitly -
   * otherwise the bar slides half the hero to the right and drops down it.
   */
  .hero--scene .meter {
    position: relative;
    inset: auto;
    width: min(300px, 100%);
    height: 8px;
  }
  .hero--scene .hero__tagline { height: auto; overflow: visible; }

  .hero--scene .hero__side--a { grid-column: 1; }
  .hero--scene .hero__side--b { grid-column: 2; }
  .hero--scene .hero__side { display: grid; justify-items: center; gap: 2px; }

  .hero--scene .hero__act {
    position: static;
    transform: none;
    margin-top: 12px;
  }
  .hero--scene .hero__act--a { grid-column: 1; }
  .hero--scene .hero__act--b { grid-column: 2; }
  /* The desktop button is a fixed 250px lozenge sized in `--u`; down here
     `--u` is 1px, so that width has to be released explicitly along with the
     nowrap that goes with it. A long side name wraps to a second line now,
     and both buttons share a grid row, so they still match height. */
  .hero--scene .hero__act .btn {
    min-width: 0;
    width: 100%;
    padding: 11px 12px;
    font-size: 13px;
    white-space: normal;
    text-wrap: balance;
  }

  /* The amount belongs on the sheet that asks for it, not in a button this
     narrow. (The old rule for this named `.hero__actions`, which the hero has
     not rendered since it moved to the scene layout, so it never applied.) */
  .hero--scene .hero__act .btn__amount { display: none; }

  .hero--scene .hero__name { font-size: 22px; }
  .hero--scene .hero__total { font-size: 24px; }
  .hero--scene .hero__share { font-size: 12px; }
  .hero--scene .cd { gap: 12px; }
  .hero--scene .cd__value { font-size: 30px; }
  .hero--scene .hero__tagline { font-size: 14px; }

}

/* ------------------------------------------------- battle page panels */

.rules {
  margin: 0;
  padding: 0;
  list-style: none;
  display: grid;
  gap: 10px;
}

.rules li {
  position: relative;
  padding-left: 20px;
  font-size: 13.5px;
  line-height: 1.5;
  color: var(--ink-2);
}

/* A small accent tick rather than a bullet, so the list matches the cards. */
.rules li::before {
  content: "";
  position: absolute;
  left: 2px;
  top: 7px;
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: var(--ink);
  opacity: 0.25;
}

.verdict-name {
  margin: 0;
  font-size: 30px;
  font-weight: 800;
  letter-spacing: -0.035em;
  color: var(--side-accent, var(--ink));
}

.verdict-by {
  margin: 0;
  font-size: 13.5px;
  line-height: 1.55;
  color: var(--ink-2);
}

.verdict-by strong { color: var(--ink); font-variant-numeric: tabular-nums; }

body.page-home .fine-print,
/* Prose is styled only in app.css, against the dark ground: its `strong` is
   var(--text), which is white and therefore invisible on paper, and its body
   copy is the dim grey meant for a dark background. The legal and how-it-works
   pages run on the light ground, so restate the palette here. */
body.page-light .prose h1,
body.page-light .prose h2,
body.page-light .prose h3 { color: var(--ink); }

body.page-light .prose p,
body.page-light .prose li { color: var(--ink-2); }

body.page-light .prose strong { color: var(--ink); font-weight: 700; }

body.page-light .prose a { color: var(--link); }

body.page-light .prose em { color: var(--ink-2); }

body.page-light .fine-print {
  margin: 4px 0 0;
  padding-top: 12px;
  border-top: 1px solid var(--card-line);
  font-size: 12px;
  line-height: 1.5;
  color: var(--ink-3);
}

body.page-home .fine-print a,
body.page-light .fine-print a { color: var(--ink-2); }

/* Notices sit between the rail and the panels. */
.notices {
  width: 100%;
  max-width: var(--page-max);
  margin: 26px auto 0;
  padding-inline: var(--page-pad);
  display: grid;
  gap: 10px;
}

body.page-home .notice,
body.page-light .notice {
  display: flex;
  gap: 10px;
  padding: 12px 16px;
  border-radius: 12px;
  border: 1px solid var(--card-line);
  background: var(--card);
  color: var(--ink-2);
  font-size: 13.5px;
  box-shadow: var(--card-shadow);
}

body.page-home .notice--warn,
body.page-light .notice--warn {
  border-color: rgba(180, 120, 20, 0.28);
  background: #fff8ea;
  color: #7a5310;
}

/* ------------------------------------------------------------- the divider */

/*
 * The boundary between the two sides is drawn as its own mark rather than left
 * as the seam where two colours meet. It rides the split, so a lead of a few
 * dollars is visible at a glance, and it slides when the score changes.
 */
.meter { position: relative; overflow: visible; border-radius: 999px; }
.meter > .meter__seg:first-of-type { border-radius: 999px 0 0 999px; }
.meter > .meter__seg + .meter__seg { border-radius: 0 999px 999px 0; }

.meter__pin {
  position: absolute;
  top: 50%;
  width: calc(3 * var(--u, 1px));
  height: calc(19 * var(--u, 1px));
  border-radius: 999px;
  background: #fff;
  box-shadow: 0 0 0 calc(2 * var(--u, 1px)) rgba(10, 10, 14, 0.55),
              0 calc(2 * var(--u, 1px)) calc(6 * var(--u, 1px)) rgba(0, 0, 0, 0.45);
  transform: translate(-50%, -50%);
  transition: left 0.6s var(--ease-out);
  pointer-events: none;
}

/* A whisker of colour on the leading side of the pin. */
.meter[data-lead="a"] .meter__pin { background: color-mix(in srgb, #fff 82%, var(--a)); }
.meter[data-lead="b"] .meter__pin { background: color-mix(in srgb, #fff 82%, var(--b)); }

/* ------------------------------------------------- scene art on the cards */

.battle-card__art { aspect-ratio: 1866 / 843; padding: 0; overflow: hidden; }

.battle-card__img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: center 42%;
  transition: transform 0.4s var(--ease-out);
}

.battle-card:hover .battle-card__img { transform: scale(1.04); }

/* The drawn cards keep the same box, so a rail of mixed art still lines up. */
.battle-card__pair {
  height: 100%;
  min-height: 0;
  padding: 10px 10px 4px;
}

.battle-card--art .live-pill--sm {
  background: rgba(6, 8, 12, 0.66);
  backdrop-filter: blur(6px);
}

/* ==========================================================================
   Hero deck
   --------------------------------------------------------------------------
   The hero is a track of complete heroes. Only the one in front is visible or
   reachable; the rest wait off-screen with their clocks still running, so a
   slide arriving is already correct rather than catching up.
   ========================================================================== */

.hero-deck {
  position: relative;
  overflow: hidden;
  background: var(--night);
}

.hero-deck__track {
  display: flex;
  will-change: transform;
  transition: transform 0.72s cubic-bezier(0.65, 0, 0.35, 1);
}

.hero-deck__slide {
  flex: 0 0 100%;
  min-width: 0;
  /* Each slide clips its own artwork, which overhangs its stage. */
  position: relative;
  overflow: hidden;
}

/* A single battle needs none of the machinery. */
.hero-deck--single { overflow: visible; }
.hero-deck--single .hero-deck__slide { overflow: hidden; }

/* --------------------------------------------------------------- controls */

.deck-arrow {
  position: absolute;
  top: 50%;
  z-index: 6;
  width: 46px;
  height: 46px;
  transform: translateY(-50%);
  display: grid;
  place-items: center;
  border-radius: 50%;
  border: 1px solid rgba(255, 255, 255, 0.18);
  background: rgba(10, 12, 18, 0.42);
  backdrop-filter: blur(10px);
  color: #fff;
  cursor: pointer;
  opacity: 0;
  transition: opacity 0.22s var(--ease), background 0.18s var(--ease);
}

.deck-arrow svg { width: 19px; height: 19px; }
.deck-arrow--prev { left: 18px; }
.deck-arrow--next { right: 18px; }

/* Revealed on approach, and always for keyboard users. */
.hero-deck:hover .deck-arrow,
.deck-arrow:focus-visible { opacity: 1; }
.deck-arrow:hover { background: rgba(10, 12, 18, 0.72); }

.hero-deck__dots {
  position: absolute;
  z-index: 6;
  left: 50%;
  bottom: 14px;
  transform: translateX(-50%);
  display: flex;
  gap: 8px;
}

.deck-dot {
  width: 7px;
  height: 7px;
  padding: 0;
  border: 0;
  border-radius: 999px;
  background: rgba(255, 255, 255, 0.32);
  cursor: pointer;
  transition: width 0.3s var(--ease-out), background 0.2s var(--ease);
}

.deck-dot:hover { background: rgba(255, 255, 255, 0.6); }
.deck-dot.is-active { width: 22px; background: #fff; }

@media (prefers-reduced-motion: reduce) {
  .hero-deck__track { transition: none; }
}

@media (max-width: 620px) {
  /* The arrows only ever appear on hover, which a touch screen has not got.
     Swiping the deck works, so the dots are the visible affordance - and a 7px
     dot is not a thumb-sized target.

     The dot keeps its size and an invisible pseudo-element carries the hit
     area. Padding plus `background-clip: content-box` is the usual trick and
     does not work here: `.deck-dot.is-active` sets `background` as a
     shorthand, which resets `background-clip` to border-box, so the active
     dot painted its whole padding box as a white slab. */
  .deck-arrow { display: none; }
  .hero-deck__dots { bottom: 8px; gap: 15px; }
  .deck-dot { position: relative; }
  .deck-dot::before { content: ""; position: absolute; inset: -14px -7px; }
}

/* ----------------------------------------------------------------- scrims
 *
 * The reference plate left an empty middle for the countdown and empty floor
 * for the names. Most generated plates do not - the objects fill the frame
 * edge to edge and run to the bottom. So the overlay brings its own ground: a
 * soft column behind the centre type and a base wash under the names, totals
 * and buttons. Both are shaped to the composition, not flat boxes, so on a
 * plate that does leave room they are barely perceptible.
 */

.hero__scrim {
  position: absolute;
  inset: 0;
  z-index: 1;
  pointer-events: none;
  background:
    /* behind the centre column - wide enough to carry the description line,
       which is the widest thing in it and the first to become unreadable */
    radial-gradient(34% 52% at 50% 30%, rgba(5, 6, 10, 0.90), rgba(5, 6, 10, 0.52) 52%, transparent 80%),
    /* under the names, totals and buttons, which on most plates land on the
       plinths - and on the lettering moulded into them - rather than on floor */
    linear-gradient(180deg,
      transparent 44%,
      rgba(5, 6, 10, 0.30) 56%,
      rgba(5, 6, 10, 0.72) 68%,
      rgba(5, 6, 10, 0.92) 82%,
      rgba(5, 6, 10, 0.97) 100%),
    /* settle the top edge under the header */
    linear-gradient(180deg, rgba(5, 6, 10, 0.62), transparent 20%);
}

/* Type sits above the scrim. */
.hero--scene .hero__center,
.hero--scene .hero__center > *,
.hero--scene .hero__side,
.hero--scene .hero__side > * { z-index: 3; }

/* A touch more weight on the type itself, for the busiest plates. */
.hero--art .hero__name,
.hero--art .hero__total { text-shadow: 0 2px 18px rgba(0, 0, 0, 0.6); }

.hero--art .cd__value,
.hero--art .hero__vs { text-shadow: 0 2px 22px rgba(0, 0, 0, 0.7); }

.hero--art .hero__tagline,
.hero--art .cd__label,
.hero--art .hero__share { text-shadow: 0 1px 10px rgba(0, 0, 0, 0.85); }

/* ==========================================================================
   Pay sheet
   --------------------------------------------------------------------------
   The amount step. A native <dialog>, so the browser handles the focus trap,
   the Escape key and the top layer rather than this file reimplementing them.
   ========================================================================== */

.pay {
  width: min(420px, calc(100vw - 32px));
  padding: 0;
  border: 0;
  border-radius: 18px;
  background: var(--card);
  color: var(--ink);
  box-shadow: 0 24px 70px rgba(10, 10, 16, 0.36);
}

.pay::backdrop {
  background: rgba(8, 9, 14, 0.62);
  backdrop-filter: blur(3px);
}

/* minmax(0, …) is load-bearing: grid items default to min-width:auto, and the
   amount input's intrinsic width once pushed the column past the dialog and
   scrolled the buttons out of reach. The column must be allowed to shrink. */
.pay__inner {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: 12px;
  padding: 24px;
}

.pay__title {
  margin: 0;
  font-size: 21px;
  font-weight: 800;
  letter-spacing: -0.03em;
}

.pay__note { margin: 0; font-size: 13.5px; line-height: 1.5; color: var(--ink-2); }

/* The field reads as one control: the symbol sits inside the border. */
.pay__field {
  display: flex;
  align-items: center;
  gap: 2px;
  margin-top: 4px;
  padding: 12px 16px;
  border: 1px solid var(--card-line);
  border-radius: 12px;
  background: var(--paper);
  transition: border-color 0.15s var(--ease), box-shadow 0.15s var(--ease);
}

.pay__field:focus-within {
  border-color: var(--ink);
  box-shadow: 0 0 0 3px rgba(20, 20, 26, 0.08);
}

.pay__symbol { font-size: 26px; font-weight: 800; letter-spacing: -0.03em; color: var(--ink-3); }

.pay__input {
  flex: 1;
  min-width: 0;
  border: 0;
  background: none;
  outline: none;
  font: inherit;
  font-size: 28px;
  font-weight: 800;
  letter-spacing: -0.03em;
  color: var(--ink);
  font-variant-numeric: tabular-nums;
}

/* The spinners fight a large numeral and are not worth the width. */
.pay__input::-webkit-outer-spin-button,
.pay__input::-webkit-inner-spin-button { -webkit-appearance: none; margin: 0; }
.pay__input { -moz-appearance: textfield; appearance: textfield; }

/* The name is a quiet second field: the amount stays the headline. */
.pay__field--text { margin-top: 0; padding: 11px 16px; }

/* 16px is a floor, not a taste: iOS zooms the page in on focus for anything
   smaller, and this is the last field before payment. */
.pay__input--text {
  font-size: 16px;
  font-weight: 600;
  letter-spacing: -0.01em;
}

.pay__input--text::placeholder { color: var(--ink-3); font-weight: 500; }

/* Quick amounts. They share the row evenly and wrap rather than overflow. */
.pay__presets { display: flex; flex-wrap: wrap; gap: 8px; margin-top: -2px; }

.pay__chip {
  flex: 1 1 auto;
  min-width: 0;
  padding: 9px 12px;
  border: 1px solid var(--card-line);
  border-radius: 999px;
  background: var(--paper);
  color: var(--ink-2);
  font: inherit;
  font-size: 13px;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
  cursor: pointer;
  transition: border-color 0.15s var(--ease), color 0.15s var(--ease),
              background 0.15s var(--ease);
}

.pay__chip:hover { border-color: var(--ink-3); color: var(--ink); }

.pay__chip[aria-pressed="true"] {
  background: #14161d;
  border-color: #14161d;
  color: #fff;
}

.pay__limit { margin: 0; font-size: 12px; color: var(--ink-3); }

.pay__error {
  margin: 0;
  font-size: 13px;
  font-weight: 600;
  color: #b4342c;
}

/* Stacked and full width: the primary action can never be pushed off the edge
   by a long label, and the tap target is the whole sheet width on a phone. */
.pay__actions {
  display: grid;
  gap: 8px;
  margin-top: 6px;
}

.pay__actions .btn { width: 100%; padding: 13px 20px; font-size: 14px; }

.btn--quiet {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border-radius: 999px;
  border: 1px solid var(--card-line);
  background: none;
  color: var(--ink-2);
  font-weight: 600;
  cursor: pointer;
}

.btn--quiet:hover { color: var(--ink); border-color: var(--ink-3); }

@media (max-width: 420px) {
  .pay__actions { flex-direction: column-reverse; }
  .pay__actions .btn { width: 100%; }
}

.side-icon { display: block; }

/* Explore now sits directly under the hero, so it carries the seam instead of
   the panels - it needs no top margin and a little more room above its chips. */
.explore { margin-top: 0; padding-block: 40px 58px; }
.panels { padding-block: 44px 60px; }

/* An empty listing still needs the page's gutters and max-width, which live on
   the shared rule above - a bare <p> inside .explore--page ran the full width of
   the viewport and put the button outside the container entirely. */
.empty-state__cta { margin: 20px 0 0; }

/* .empty-note centres itself, which suits a bare "No battles yet." but not a
   paragraph sitting under a left-aligned heading beside a left-aligned button.
   Inside .empty-state it reads as a column with them, on a comfortable measure. */
.empty-state .empty-note { text-align: left; padding-inline: 0; max-width: 64ch; }
