/* ==========================================================================
   DUEL STONE — duel-stone.com
   --------------------------------------------------------------------------
   The palette and the display face are not chosen for the web; they are lifted
   out of the game so the two cannot drift:

     --ember  scripts/ui/wordmark.gd      EMBER  Color(1.0, 0.45, 0.13)
     --gold   scripts/ui/title_screen.gd  GOLD   Color("f4c542")
     --bone   scripts/ui/title_screen.gd  TXT    Color("eef1f6")
     Cinzel   assets/fonts/Cinzel.ttf — the actual wordmark face (OFL)

   The element, tier and chapter colours are NOT here: tools/build-content.py
   renders them from the game's scripts/ui/ui_kit.gd into the marked region below.

   Gold obeys the same discipline it does in-game: it marks the thing you press,
   and nothing else. Ember is for emphasis and heat; stone greys carry structure.

   THIS IS THE SOURCE, not what ships: tools/build-content.py fills the token
   region, resolves every image and font url() to its
   content-hashed file, and writes public/assets/css/site.<hash>.css. Ask for
   the plain name here; a name no build published fails the render.

   SCREENSHOTS: the game renders at 720x1280. On a 2x screen anything shown wider
   than 360 CSS px is upscaled, and that is what reads as grain. Every still on
   this page is shown well under that, and opens at full size on click.
   ========================================================================== */

/* --------------------------------------------------------------- typefaces */
/* Self-hosted, subset in tools/build-fonts.py. No font CDN: the privacy policy
   promises no third-party requests, and that has to be true of this page too. */
@font-face {
  font-family: "Cinzel";
  src: url("../fonts/cinzel-var.38e9d8737d.woff2") format("woff2-variations");
  font-weight: 400 900;
  font-display: swap;
}
@font-face {
  font-family: "Alegreya Sans";
  src: url("../fonts/alegreya-sans-400.39810a1e49.woff2") format("woff2");
  font-weight: 400;
  font-display: swap;
}
@font-face {
  font-family: "Alegreya Sans";
  src: url("../fonts/alegreya-sans-700.c647abfe5e.woff2") format("woff2");
  font-weight: 700;
  font-display: swap;
}

/* ------------------------------------------------------------------ tokens */
:root {
  --ground: #07070a;
  --pitch: #0b0d12;
  --slab: #101319;
  --slab-lit: #171b23;
  --edge: rgba(255, 255, 255, 0.075);
  --edge-strong: rgba(255, 255, 255, 0.14);
  --rule: rgba(255, 255, 255, 0.06);
  --shadow: rgba(0, 0, 0, 0.6);

  --bone: #eef1f6;
  --body: #c2c8d3;
  --dim: #878e9c;

  --ember: #ff7321;
  --ember-deep: #c2410c;
  --gold: #f4c542;

  --display: "Cinzel", "Times New Roman", serif;
  --text: "Alegreya Sans", ui-sans-serif, system-ui, -apple-system, "Segoe UI", sans-serif;

  --gutter: clamp(1rem, 4vw, 2rem);
  --shell: 76rem;
  --measure: 38rem;
  --band: clamp(2.25rem, 4.5vw, 3.5rem);
  --hair: 1px;
}

/* >>> GENERATED TOKENS (tools/build-content.py) >>> */
/* The game's colour tokens, from hero-forge-remake a8d2ed8. Rendered into site.css; do not edit there.
   The game's own colour tokens (scripts/ui/ui_kit.gd: ELEMENT, TIER, CHAPTER), so a colour on this
   site is the colour in the game. They live in a stylesheet rather than in style="" attributes
   because this site's CSP is style-src 'self', which (correctly) refuses inline styles. */
:root {
  --fire: #e8843c;
  --water: #3c8fe8;
  --earth: #5fae54;
  --tier-common: #aab2bd;
  --tier-rare: #4a90e2;
  --tier-epic: #b06ad0;
  --tier-legendary: #f4c542;
  --ch1: #e8843c;
  --ch2: #7fb2f0;
  --ch3: #5ac04a;
}
.chap--ch1 { --c: var(--ch1); }
.chap--ch2 { --c: var(--ch2); }
.chap--ch3 { --c: var(--ch3); }
.rung--common { --t: var(--tier-common); }
.rung--rare { --t: var(--tier-rare); }
.rung--epic { --t: var(--tier-epic); }
.rung--legendary { --t: var(--tier-legendary); }
.is-fire { --el: var(--fire); }
.is-water { --el: var(--water); }
.is-earth { --el: var(--earth); }
/* <<< GENERATED TOKENS <<< */

/* ------------------------------------------------------------------- reset */
*,
*::before,
*::after { box-sizing: border-box; }

html {
  -webkit-text-size-adjust: 100%;
  scroll-behavior: smooth;
}

body {
  margin: 0;
  background: var(--ground);
  color: var(--body);
  font-family: var(--text);
  font-size: clamp(1rem, 0.96rem + 0.2vw, 1.0625rem);
  line-height: 1.55;
  font-kerning: normal;
  -webkit-font-smoothing: antialiased;
  overflow-x: hidden;
}

img { max-width: 100%; height: auto; display: block; }

a { color: var(--ember); text-decoration-thickness: 1px; text-underline-offset: 0.22em; }
a:hover { color: #ff9350; }

:focus-visible {
  outline: 2px solid var(--gold);
  outline-offset: 3px;
  border-radius: 2px;
}

ul, ol { margin: 0; padding: 0; list-style: none; }
h2, h3, p { margin: 0; }

/* --------------------------------------------------------------- primitives */
.shell {
  width: 100%;
  max-width: var(--shell);
  margin-inline: auto;
  padding-inline: var(--gutter);
}

.skip {
  position: absolute;
  left: -9999px;
  top: 0;
  z-index: 99;
  background: var(--gold);
  color: #14100a;
  padding: 0.7rem 1.1rem;
  font-weight: 700;
  border-radius: 0 0 3px 0;
}
.skip:focus { left: 0; }

.sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  overflow: hidden;
  clip: rect(0 0 0 0);
  white-space: nowrap;
  border: 0;
}

/* An eyebrow that says what kind of thing follows, set the way the game sets a
   carved label, lit by one ember diamond — the bundle's own carve. */
.eyebrow {
  display: flex;
  align-items: center;
  gap: 0.6rem;
  margin: 0 0 0.55rem;
  font-family: var(--display);
  font-weight: 600;
  font-size: 0.72rem;
  letter-spacing: 0.28em;
  text-transform: uppercase;
  color: var(--dim);
}
.eyebrow::before {
  content: "";
  width: 6px;
  height: 6px;
  flex: none;
  background: var(--ember);
  transform: rotate(45deg);
  box-shadow: 0 0 8px rgba(255, 115, 33, 0.75);
}

.title {
  font-family: var(--display);
  font-weight: 700;
  line-height: 1.15;
  letter-spacing: 0.02em;
  color: var(--bone);
  margin: 0;
  font-size: clamp(1.4rem, 1.1rem + 1.3vw, 2rem);
  text-wrap: balance;
}
.title--sm { font-size: clamp(1.25rem, 1.05rem + 0.8vw, 1.6rem); }

.lede { margin: 0 0 1.4rem; max-width: var(--measure); color: var(--body); }

.fineprint {
  font-size: 0.85rem;
  line-height: 1.5;
  color: var(--dim);
}

.subhead {
  margin: 0;
  font-family: var(--display);
  font-weight: 700;
  font-size: 0.95rem;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--bone);
}

/* A slab: a panel of stone catching light on its top edge and sitting in its
   own shadow. Used instead of floating cards so the page reads as carved. */
.slab {
  position: relative;
  background: linear-gradient(180deg, var(--slab-lit), var(--slab) 62%);
  border: var(--hair) solid var(--edge);
  border-bottom-color: rgba(0, 0, 0, 0.8);
  border-radius: 3px;
  box-shadow: 0 1px 0 rgba(255, 255, 255, 0.045) inset, 0 12px 28px -18px var(--shadow);
}

/* A card: one object in a row of equals. */
.card {
  background: linear-gradient(180deg, var(--slab-lit), var(--slab) 55%);
  border: var(--hair) solid var(--edge);
  border-radius: 6px;
}

/* ------------------------------------------------------------------ header */
/* Sticky, so the in-page nav is always one tap away. */
.topbar {
  position: sticky;
  top: 0;
  z-index: 20;
  border-bottom: var(--hair) solid rgba(255, 255, 255, 0.05);
  background: rgba(7, 7, 10, 0.86);
  backdrop-filter: blur(6px);
}
.topbar__in {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  min-height: 3.25rem;
}
.topbar__mark {
  display: inline-flex;
  align-items: center;
  gap: 0.55rem;
  font-family: var(--display);
  font-weight: 700;
  font-size: 0.88rem;
  letter-spacing: 0.24em;
  color: var(--bone);
  text-decoration: none;
}
.topbar__mark:hover { color: var(--bone); }
.topbar__mark img { width: 22px; height: 22px; border-radius: 5px; }

.topbar__nav {
  display: flex;
  gap: clamp(0.9rem, 2.5vw, 1.5rem);
  font-size: 0.92rem;
}
.topbar__nav a {
  color: var(--dim);
  text-decoration: none;
}
.topbar__nav a:hover,
.topbar__nav a[aria-current="page"] { color: var(--bone); }
@media (max-width: 43.99rem) { .home .topbar__nav { display: none; } }
[id] { scroll-margin-top: 4rem; }

/* The bar's "Join the beta" is a link to the #join band, never a form. On the home page it
   fades in as the hero's own button row scrolls up under the bar, so one of the two is always on
   screen: a CSS scroll-driven animation, no script. The timeline is that ROW, not the hero: on a
   phone the hero runs a full screen past its button, and a hero-wide timeline left ~500px of
   scroll with neither button showing. Hidden means visibility:hidden, so while hidden it is out
   of the tab order and the accessibility tree, not merely transparent. Where scroll-driven
   animation is unsupported (Firefox, by default) the button is simply always there. */
.topbar__nav { margin-left: auto; }
.topbar__cta { flex: none; }
@supports (animation-timeline: view()) {
  .home { timeline-scope: --hero-cta; }
  .home .hero__cta { view-timeline: --hero-cta block; }
  .home .topbar__cta {
    animation: cta-in linear both;
    animation-timeline: --hero-cta;
    animation-range: cover 75% cover 100%;
  }
}
@keyframes cta-in {
  from { opacity: 0; visibility: hidden; }
  to   { opacity: 1; visibility: visible; }
}

/* ----------------------------------------------------------------- buttons */
/* Gold is for the thing you press: the beta's three buttons (the bar, the hero, and the band's
   "Join the Google Group"), and nothing else on the page. A second step that is not the first
   thing to press is a line button. */
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  height: 3.25rem;
  padding: 0 1.35rem;
  border: 0;
  border-radius: 3px;
  font-family: var(--display);
  font-weight: 700;
  font-size: 0.8rem;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  text-decoration: none;
  white-space: nowrap;
  cursor: pointer;
}
.btn--gold { background: var(--gold); color: #14100a; }
.btn--gold:hover { background: #ffd462; color: #14100a; }
.btn--line { background: transparent; color: var(--bone); box-shadow: inset 0 0 0 var(--hair) var(--edge-strong); }
.btn--line:hover { color: #fff; box-shadow: inset 0 0 0 var(--hair) rgba(255, 255, 255, 0.42); }
.btn--sm { height: 2.15rem; padding: 0 0.95rem; font-size: 0.7rem; letter-spacing: 0.12em; }

/* -------------------------------------------------------------------- hero */
.hero {
  position: relative;
  /* clip, not hidden: `hidden` makes the hero a scroll container, and then the header button's
     view timeline (on .hero__cta, inside it) tracks a box that never scrolls and never fires. */
  overflow: clip;
  padding-block: clamp(2rem, 5vw, 3.25rem) 0;
}

/* The one place with motion: embers drifting up out of the base of the mark,
   the same direction the in-game wordmark heats. No canvas, no script. */
.hero__heat {
  position: absolute;
  inset: -20% -10% -30%;
  pointer-events: none;
  background:
    radial-gradient(38vw 30vw at 22% 92%, rgba(255, 115, 33, 0.18), transparent 68%),
    radial-gradient(30vw 24vw at 78% 78%, rgba(194, 65, 12, 0.14), transparent 70%),
    radial-gradient(52vw 40vw at 50% 118%, rgba(255, 140, 60, 0.12), transparent 72%);
  animation: heat 19s ease-in-out infinite alternate;
}
@keyframes heat {
  from { transform: translate3d(0, 0, 0) scale(1); opacity: 0.85; }
  to   { transform: translate3d(0, -3.5%, 0) scale(1.07); opacity: 1; }
}

.hero__in {
  position: relative;
  display: grid;
  gap: 2rem;
  align-items: center;
}

/* THE SIGNATURE ------------------------------------------------------------
   The headline is made of the brand: letterforms filled with the arena's own
   stone and heated toward ember at the base — the same passes as the in-game
   shader (scripts/ui/wordmark.gd + assets/shaders/wordmark.gdshader). */
.wordmark {
  position: relative;
  margin: 0;
  font-family: var(--display);
  font-weight: 900;
  font-size: clamp(2.9rem, 11vw, 5.75rem);
  line-height: 0.94;
  letter-spacing: 0.055em;
  text-transform: uppercase;
  color: var(--bone);
}
.wordmark span { display: block; }

@supports ((-webkit-background-clip: text) or (background-clip: text)) {
  .wordmark span {
    color: transparent;
    background-image:
      linear-gradient(180deg, rgba(255, 115, 33, 0) 36%, rgba(255, 115, 33, 0.92) 97%),
      url("../img/stone-tile.69c58d9ad5.webp");
    background-size: auto, 46% auto;
    background-blend-mode: screen, normal;
    -webkit-background-clip: text;
    background-clip: text;
    filter: drop-shadow(0 2px 0 rgba(0, 0, 0, 0.85)) drop-shadow(0 0 26px rgba(255, 115, 33, 0.22));
  }
  .wordmark span + span {
    background-image:
      linear-gradient(180deg, rgba(255, 115, 33, 0.18) 14%, rgba(255, 100, 20, 1) 96%),
      url("../img/stone-tile.69c58d9ad5.webp");
  }
}

.hero__hook {
  margin: 1.1rem 0 0;
  font-family: var(--display);
  font-weight: 600;
  font-size: clamp(1.05rem, 0.95rem + 0.6vw, 1.3rem);
  letter-spacing: 0.12em;
  color: var(--bone);
}
.hero__blurb { margin: 0.7rem 0 0; max-width: 31rem; }
.hero__cta { display: flex; flex-wrap: wrap; align-items: center; gap: 0.6rem; margin-top: 1.5rem; }
.hero__cta .stores { margin-top: 0; }

.hero__shot { width: 13rem; justify-self: center; }
@media (min-width: 56rem) {
  .hero__in { grid-template-columns: minmax(0, 1fr) 15rem; column-gap: 4rem; }
  .hero__shot { width: 15rem; justify-self: end; }
}

/* How a round works, as one row under the hero. Numbered because a round
   really is this sequence; each number sits in the bundle's diamond. */
.steps {
  position: relative;
  display: grid;
  gap: 1.25rem 2.5rem;
  margin-top: clamp(2rem, 4vw, 2.75rem);
  padding-block: 1.5rem clamp(2rem, 4vw, 2.75rem);
  border-top: var(--hair) solid var(--rule);
}
@media (min-width: 48rem) { .steps { grid-template-columns: repeat(3, minmax(0, 1fr)); } }
.steps__item { display: grid; grid-template-columns: 2.1rem minmax(0, 1fr); gap: 0.85rem; align-items: start; }
.steps__k {
  width: 1.6rem;
  height: 1.6rem;
  margin: 0.2rem 0 0 0.25rem;
  display: grid;
  place-items: center;
  border: var(--hair) solid var(--edge-strong);
  background: var(--slab);
  transform: rotate(45deg);
}
.steps__k span {
  transform: rotate(-45deg);
  font-family: var(--display);
  font-weight: 700;
  font-size: 0.78rem;
  color: var(--bone);
}
.steps__t {
  margin: 0 0 0.15rem;
  font-family: var(--display);
  font-weight: 700;
  font-size: 0.95rem;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--bone);
}
.steps p { font-size: 0.94rem; }

/* ----------------------------------------------------------- store badges */
/* Two slots, laid out now at the height the official badges will take, so a
   launch is an asset swap plus a URL and never a relayout. Deliberately NOT
   lookalikes: Google's and Apple's brand rules allow only their own unmodified
   badge. Nothing here is a link yet, so nothing here is gold. See README,
   "Store badges". */
.stores { display: flex; flex-wrap: wrap; gap: 0.6rem; margin-top: 1.5rem; }
.store-slot {
  display: flex;
  align-items: center;
  gap: 0.7rem;
  height: 3.25rem;              /* 52px: the shared height both badges will be set at */
  padding: 0 1rem;
  border: var(--hair) solid var(--edge-strong);
  border-radius: 8px;
  background: linear-gradient(180deg, var(--slab-lit), var(--slab));
  text-align: left;
  line-height: 1.2;
}
.store-slot--play { min-width: 13rem; }
.store-slot--apple { min-width: 9.75rem; background: none; border-style: dashed; }
.store-slot__glyph { width: 26px; height: 26px; flex: none; border-radius: 7px; }
.store-slot__now {
  display: block;
  font-family: var(--display);
  font-weight: 700;
  font-size: 0.78rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--bone);
}
/* Cinzel has no lowercase, so a name like "iOS" is set in the text face. */
.store-slot__now--quiet { font-family: var(--text); font-size: 0.95rem; letter-spacing: 0.02em; text-transform: none; color: var(--body); }
.store-slot__then { display: block; font-size: 0.8rem; color: var(--dim); }

/* ------------------------------------------------------------ phone framing */
/* Portrait stills in a stone bezel rather than a glossy device mockup. */
.phone {
  position: relative;
  margin: 0;
  padding: 5px;
  background: linear-gradient(160deg, #2b2e37, #101116 70%);
  border-radius: 13px;
  box-shadow:
    0 0 0 1px rgba(0, 0, 0, 0.9),
    0 24px 44px -28px rgba(0, 0, 0, 0.95),
    0 1px 0 rgba(255, 255, 255, 0.1) inset;
}
.phone img { width: 100%; border-radius: 9px; background: var(--pitch); }

/* ----------------------------------------------------------------- section */
.band { padding-block: var(--band); }
.band--edged { border-top: var(--hair) solid var(--rule); }

/* A section header: the title on the left, one line of context on the right. */
.head {
  display: flex;
  flex-wrap: wrap;
  align-items: end;
  justify-content: space-between;
  gap: 0.6rem 2.5rem;
  margin-bottom: clamp(1.25rem, 3vw, 1.75rem);
}
.head__note { max-width: 30rem; font-size: 0.94rem; color: var(--dim); }

/* ----------------------------------------------------------------- heroes */
.classes { display: grid; gap: 1rem; }
/* Subgrid rows: the ability lists and unlock lines line up across the three
   cards however long each blurb runs. */
@media (min-width: 64rem) {
  .classes { grid-template-columns: repeat(3, minmax(0, 1fr)); grid-template-rows: repeat(3, auto); row-gap: 0; }
  .cls { grid-row: span 3; grid-template-rows: subgrid; }
}
@media (min-width: 44rem) and (max-width: 63.99rem) { .classes { grid-template-columns: repeat(2, minmax(0, 1fr)); } .cls:last-child { grid-column: 1 / -1; } }

.cls {
  display: grid;
  grid-template-columns: 6.25rem minmax(0, 1fr);
  grid-template-areas: "img body" "abil abil" "unlock unlock";
  gap: 0.9rem 1rem;
  align-content: start;
  padding: 1rem;
  background: linear-gradient(180deg, var(--slab-lit), var(--slab) 55%);
  border: var(--hair) solid var(--edge);
  border-radius: 6px;
  min-width: 0;
}
.cls__img {
  grid-area: img;
  width: 100%;
  aspect-ratio: 5 / 6;
  object-fit: cover;
  object-position: 50% 16%;
  border-radius: 4px;
  background: var(--pitch);
}
.cls__body { grid-area: body; display: grid; gap: 0.35rem; align-content: start; min-width: 0; }
.cls__name {
  margin: 0;
  font-family: var(--display);
  font-weight: 700;
  font-size: 1.12rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--bone);
}
.cls__blurb { font-style: italic; font-size: 0.9rem; line-height: 1.45; }
.cls__traits { font-size: 0.82rem; color: var(--dim); }
.cls__traits b { color: var(--bone); }

/* Six abilities as three element pairs. A filled dot is in the starting four;
   a ring unlocks later. The colour is the element, from the game's tokens. */
.abil { grid-area: abil; display: grid; grid-template-columns: 1fr 1fr; gap: 0.3rem 0.8rem; }
.abil__item { display: flex; align-items: center; gap: 0.5rem; font-size: 0.88rem; color: var(--dim); min-width: 0; cursor: help; }
.abil__item::before {
  content: "";
  width: 8px;
  height: 8px;
  flex: none;
  border-radius: 50%;
  border: 2px solid var(--el);
}
.abil__item.is-start { color: var(--bone); }
.abil__item.is-start::before { background: var(--el); box-shadow: 0 0 7px var(--el); }
.cls__unlock { grid-area: unlock; font-size: 0.8rem; color: var(--dim); }

/* The four enemies side by side, under the three heroes. */
.enemies {
  margin-top: 1rem;
  padding: 1rem;
  border: var(--hair) solid var(--edge);
  border-radius: 6px;
}
.enemies__head { display: flex; flex-wrap: wrap; align-items: baseline; justify-content: space-between; gap: 0.3rem 1.5rem; margin-bottom: 0.9rem; }
.enemies__note { font-size: 0.86rem; color: var(--dim); }
.beasts { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 0.8rem 1rem; }
@media (min-width: 52rem) { .beasts { grid-template-columns: repeat(4, minmax(0, 1fr)); } }
.beast { display: grid; grid-template-columns: 4.5rem minmax(0, 1fr); gap: 0.8rem; align-items: center; min-width: 0; }
.beast img {
  width: 100%;
  aspect-ratio: 1;
  object-fit: cover;
  object-position: 50% 18%;
  border-radius: 5px;
  background: radial-gradient(80% 70% at 50% 45%, #1b1f28, var(--pitch));
  border: var(--hair) solid var(--edge);
}
.beast__text { display: grid; gap: 0.1rem; min-width: 0; }
.beast__name { font-family: var(--display); font-weight: 700; font-size: 0.95rem; letter-spacing: 0.04em; color: var(--bone); }
.beast__meta { font-size: 0.82rem; color: var(--dim); }
#heroes > .shell > .fineprint { margin-top: 0.9rem; }

/* ---------------------------------------------------------------- campaign */
.chapters { display: grid; gap: 0.9rem; }
@media (min-width: 52rem) { .chapters { grid-template-columns: repeat(3, minmax(0, 1fr)); } }
.chap {
  display: grid;
  gap: 0.3rem;
  align-content: start;
  padding: 1rem 1.1rem 1.1rem;
  border-top: 3px solid var(--c);
  border-radius: 0 0 6px 6px;
  background: linear-gradient(180deg, color-mix(in srgb, var(--c) 9%, var(--slab)), var(--slab) 70%);
}
.chap__meta { font-size: 0.78rem; letter-spacing: 0.08em; text-transform: uppercase; color: var(--dim); }
.chap__name {
  font-family: var(--display);
  font-weight: 700;
  font-size: 1.08rem;
  letter-spacing: 0.04em;
  color: var(--bone);
}
.chap__boss { font-size: 0.92rem; font-weight: 700; color: var(--body); }
.chap__rule { margin: 0.15rem 0 0; }
.chap__rule p { font-style: italic; font-size: 0.88rem; line-height: 1.45; color: var(--dim); }

.achieve { display: flex; flex-wrap: wrap; align-items: center; gap: 0.7rem 1.25rem; margin-top: 1.5rem; }
.chips { display: flex; flex-wrap: wrap; gap: 0.45rem; }
.chips li {
  padding: 0.22rem 0.8rem;
  border: var(--hair) solid var(--edge-strong);
  border-radius: 999px;
  font-size: 0.86rem;
  color: var(--body);
}
.chips b { color: var(--bone); margin-left: 0.2rem; }

/* -------------------------------------------------------------------- gear */
.gear { display: grid; gap: 1.75rem; align-items: center; }
@media (min-width: 64rem) { .gear { grid-template-columns: minmax(0, 5fr) minmax(0, 7fr); column-gap: 3rem; } }
.gear__text { display: grid; gap: 0.8rem; align-content: start; }
.gear__text .eyebrow { margin: 0; }
.gear__text > p:not(.eyebrow) { max-width: var(--measure); }

.econ { display: grid; gap: 0.6rem; margin: 0.2rem 0 0; }
@media (min-width: 30rem) { .econ { grid-template-columns: 1fr 1fr; } }
.econ__item { border: var(--hair) solid var(--edge); border-radius: 4px; padding: 0.7rem 0.85rem; }
.econ__item dt {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  margin-bottom: 0.15rem;
  font-family: var(--display);
  font-weight: 700;
  font-size: 0.85rem;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--bone);
}
/* A coin of the currency's own colour: content, not a control. */
.econ__item dt::before { content: ""; width: 9px; height: 9px; border-radius: 50%; }
.econ__item--gold dt::before { background: var(--gold); }
.econ__item--ember dt::before { background: var(--ember); }
.econ__item dd { margin: 0; font-size: 0.88rem; }

/* The ladder. Tier is carried by the card SURFACE, the channel the game's own
   gear cards use (DESIGN_SYSTEM 7.2): no edge, then an edge, then a tier border,
   then a glow. That channel only goes up; a tier as a NAME COLOUR would run
   backwards on a dark ground, so the tier word stays bone. */
.ladder { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 0.7rem; }
@media (min-width: 40rem) { .ladder { grid-template-columns: repeat(4, minmax(0, 1fr)); } }
.rung {
  display: grid;
  gap: 0.1rem;
  padding-bottom: 0.65rem;
  background: var(--slab);
  border: var(--hair) solid var(--edge-strong);
  border-radius: 4px;
  overflow: hidden;
  min-width: 0;
}
.rung img { width: 100%; aspect-ratio: 107 / 72; object-fit: cover; margin-bottom: 0.5rem; }
.rung__tier,
.rung__name { padding-inline: 0.75rem; }
.rung__tier {
  font-family: var(--display);
  font-weight: 700;
  font-size: 0.78rem;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--bone);
}
.rung__name { font-size: 0.82rem; line-height: 1.35; color: var(--dim); }
.rung--rare,
.rung--epic,
.rung--legendary { box-shadow: inset 4px 0 0 var(--t); }
.rung--epic,
.rung--legendary { border-color: var(--t); }
.rung--legendary { box-shadow: inset 4px 0 0 var(--t), 0 0 22px -6px color-mix(in srgb, var(--t) 70%, transparent); }
.rung--rare .rung__tier, .rung--rare .rung__name,
.rung--epic .rung__tier, .rung--epic .rung__name,
.rung--legendary .rung__tier, .rung--legendary .rung__name { padding-left: 1rem; }

/* ------------------------------------------------------------- multiplayer */
.trio-cards { display: grid; gap: 0.9rem; }
@media (min-width: 48rem) { .trio-cards { grid-template-columns: repeat(3, minmax(0, 1fr)); } }
.trio-cards li {
  padding: 1rem 1.1rem;
  background: linear-gradient(180deg, var(--slab-lit), var(--slab) 55%);
  border: var(--hair) solid var(--edge);
  border-radius: 6px;
}
.trio-cards h3 {
  margin: 0 0 0.3rem;
  font-family: var(--display);
  font-weight: 700;
  font-size: 0.98rem;
  letter-spacing: 0.06em;
  color: var(--bone);
}
.trio-cards p { font-size: 0.92rem; }

/* ----------------------------------------------------------------- gallery */
/* Seven small stills in one row on a desktop (a sideways rail on a phone).
   Small on purpose: a 720px capture shown wider than 360 CSS px is upscaled on a
   2x screen. Each opens at full size in a native popover. */
.gallery {
  /* position: relative makes the rail the containing block for anything
     absolutely positioned inside it, so nothing escapes the rail's clipping and
     widens the page on a phone. */
  position: relative;
  display: grid;
  grid-auto-flow: column;
  grid-auto-columns: min(36vw, 9.5rem);
  gap: 0.8rem;
  overflow-x: auto;
  scroll-snap-type: x mandatory;
  margin-inline: calc(var(--gutter) * -1);
  padding: 0 var(--gutter) 0.75rem;
  scrollbar-width: thin;
  scrollbar-color: #2a2d36 transparent;
}
@media (min-width: 64rem) {
  .gallery {
    grid-auto-flow: row;
    grid-auto-columns: auto;
    grid-template-columns: repeat(7, minmax(0, 1fr));
    gap: 1rem;
    overflow: visible;
    margin-inline: 0;
    padding: 0;
  }
}
.gallery__item { scroll-snap-align: start; display: grid; gap: 0.45rem; align-content: start; min-width: 0; }
.gallery__open {
  display: block;
  width: 100%;
  padding: 4px;
  border: 0;
  border-radius: 11px;
  background: linear-gradient(160deg, #2b2e37, #101116 70%);
  box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.9), 0 18px 34px -24px rgba(0, 0, 0, 0.95);
  cursor: zoom-in;
}
.gallery__open img { width: 100%; border-radius: 8px; background: var(--pitch); transition: filter 0.2s; }
.gallery__open:hover img { filter: brightness(1.12); }
.gallery__cap { font-size: 0.84rem; text-align: center; color: var(--dim); }

/* The lightbox is a native popover: the browser gives it the top layer, Esc to
   close and click-outside dismiss, with no script. The image is capped at 640px
   tall so a 1280px capture is never shown above its own resolution on a 2x screen. */
.lightbox {
  margin: auto;
  padding: 0;
  border: 0;
  background: transparent;
  max-width: min(94vw, 30rem);
  max-height: 96vh;
  overflow: visible;
  color: var(--body);
}
.lightbox::backdrop { background: rgba(4, 4, 6, 0.9); }
.lightbox__fig { margin: 0; display: grid; gap: 0.6rem; justify-items: center; }
.lightbox__fig img {
  width: auto;
  max-width: 100%;
  max-height: min(calc(96vh - 6rem), 640px);
  border-radius: 12px;
  border: 5px solid #1d2029;
}
.lightbox__fig figcaption { max-width: 26rem; text-align: center; font-size: 0.92rem; }
.lightbox__close {
  display: block;
  margin: 0.7rem auto 0;
  padding: 0.45rem 1.3rem;
  border: var(--hair) solid var(--edge-strong);
  border-radius: 3px;
  background: var(--slab-lit);
  color: var(--bone);
  font: inherit;
  font-size: 0.9rem;
  cursor: pointer;
}
.lightbox__close:hover { background: #20252f; }

/* ------------------------------------------------------------------- get it */
.getit__in { display: grid; gap: 1rem 2.5rem; align-items: center; }
.getit__in p { margin-top: 0.3rem; color: var(--body); }
.getit .stores { margin-top: 0; }
@media (min-width: 52rem) { .getit__in { grid-template-columns: minmax(0, 1fr) auto; } }

/* -------------------------------------------------------------- the pledge */
/* The differentiator, so the plainest treatment on the page: no icons, one
   ember rule. The dated "Today" item spans the row because it is the one that
   will change. */
.pledge { display: grid; gap: 1rem 2.5rem; padding-left: clamp(0.9rem, 3vw, 1.5rem); border-left: 2px solid var(--ember); }
@media (min-width: 48rem) { .pledge { grid-template-columns: 1fr 1fr; } .pledge__today, .pledge__wide { grid-column: 1 / -1; } }
.pledge h3 {
  margin: 0 0 0.2rem;
  font-family: var(--display);
  font-weight: 600;
  font-size: 1.02rem;
  letter-spacing: 0.03em;
  color: var(--bone);
}
.pledge p { font-size: 0.92rem; color: var(--dim); }

/* --------------------------------------------------------------------- faq */
/* Questions name products ("iPhone"), and Cinzel's missing lowercase would set
   that as IPHONE, so the questions are in the text face. */
.faq { display: grid; column-gap: 2.5rem; }
@media (min-width: 52rem) { .faq { grid-template-columns: 1fr 1fr; } }
.faq details { border-top: var(--hair) solid rgba(255, 255, 255, 0.07); padding: 0.8rem 0; }
.faq summary {
  cursor: pointer;
  font-family: var(--text);
  font-weight: 700;
  font-size: 1.02rem;
  color: var(--bone);
}
.faq summary::marker { color: var(--ember); }
.faq details p { margin: 0.45rem 0 0.1rem; font-size: 0.94rem; }

/* ------------------------------------------------------------ join the beta */
/* No form (R1 reversed again, 2026-10-14): the beta is a Google Group, and the site collects
   nothing. Two steps, each a link out with its hint directly under it: join the group (the band's
   only gold), then opt in on Google Play. On a computer, a QR code hands both steps to the phone,
   where the Play Store account is. */
.join { --join-stone: min(21rem, 100vw); position: relative; overflow: clip; }
@media (min-width: 30rem) { .join { --join-stone: 16rem; } }
@media (min-width: 52rem) {
  .join { --join-stone: 27rem; display: grid; align-content: center; min-height: calc(var(--join-stone) + 3rem); }
}
.join__in { display: grid; gap: 1.5rem 3rem; align-items: start; }
@media (min-width: 52rem) { .join__in { grid-template-columns: minmax(0, 5fr) minmax(0, 6fr); } }

/* The backdrop: the game's loading-screen stone, the author's own art used as is (ruled
   2026-10-12; tools/build-images.py only resizes it). Two things make "as is" work here:
   * `lighten` blending. The art sits on a black card a few levels off the page's ground, which
     drew a visible rounded rectangle; under `lighten` anything darker than the ground changes
     nothing, so the card and its corners vanish without touching the image.
   * It sits behind the HEADING's column. Every line of text in the band is body grey or brighter,
     which holds 4.5:1 over the stone's white seam cores up to about 0.31 opacity (dim grey did not:
     3.7:1 at 0.22). In one column the stone hangs from the top of the band (21rem on a phone, 16rem
     from 30rem up) over whatever the flag puts there; in two, it is centred on the heading's
     column, in a band tall enough to hold all of it. Re-measure if the opacity or a colour moves. */
.join__text { position: relative; }
.join__text::before {
  content: "";
  position: absolute;
  z-index: -1;
  top: calc(0.75rem - var(--band));
  left: 50%;
  width: var(--join-stone);
  aspect-ratio: 1;
  translate: -50% 0;
  background: url("../img/join-stone-960.bf236a8f42.webp") center / contain no-repeat;
  background-image: image-set(url("../img/join-stone-480.74f20cf23c.webp") 1x, url("../img/join-stone-960.bf236a8f42.webp") 2x);
  mix-blend-mode: lighten;
  opacity: var(--join-stone-o, 0.26);
  pointer-events: none;
}
@media (min-width: 30rem) { .join__text::before { left: auto; right: 0; translate: 0 0; } }
@media (min-width: 52rem) {
  .join__text { align-self: stretch; }
  .join__text::before { top: 50%; left: 50%; right: auto; translate: -50% -50%; }
}
@media (forced-colors: active), (prefers-contrast: more) { .join__text::before { display: none; } }
.join__text p { margin-top: 0.45rem; max-width: 30rem; color: var(--body); }

/* Each step leads with its ruled sentence, then its button, which that sentence describes
   (aria-describedby). Step one's sentence is the account-match line, the most common way a tester
   fails. On a phone the buttons run full width (a thumb target, and room for the label at 320px);
   from 30rem up the steps are numbered in a gutter, unless there is only one: while the track is
   dark, step one is the whole list, and a lone "1" reads as a missing "2". */
.join__steps { display: grid; gap: 1.5rem; margin: 0; padding: 0; list-style: none; counter-reset: step; }
.join__step { display: grid; gap: 0.7rem; counter-increment: step; }
.join__step .btn { width: 100%; }
.join__say { margin: 0; max-width: 34rem; font-size: 0.95rem; line-height: 1.5; color: var(--body); }
.join__say strong { color: var(--bone); font-weight: 700; }
.join__hint { margin: 0; max-width: 34rem; font-size: 0.88rem; line-height: 1.45; color: var(--body); }
@media (min-width: 30rem) {
  .join__step .btn { width: auto; justify-self: start; }
  .join__step:not(:only-child) { grid-template-columns: 2rem minmax(0, 1fr); column-gap: 0.95rem; }
  .join__step:not(:only-child)::before {
    content: counter(step);
    grid-row: 1;
    display: grid;
    place-items: center;
    width: 2rem;
    height: 2rem;
    border: var(--hair) solid var(--edge-strong);
    border-radius: 50%;
    font-family: var(--display);
    font-weight: 700;
    font-size: 0.85rem;
    color: var(--bone);
  }
  .join__step:not(:only-child) > * { grid-column: 2; }
}

/* A computer cannot install an Android test, and its browser may be signed in to a different
   Google account than the phone, which is the failure the first hint warns about. So on a
   pointer-and-hover screen wide enough for two columns, the band shows a QR code to THIS band, and
   the phone gets both steps in the account its Play Store uses. Dark modules on a light square,
   quiet zone included (tools/build-images.py); 8rem holds a module at about four CSS pixels. Hidden
   elsewhere, and lazy, so a phone never downloads it. */
.join__qr { display: none; }
@media (min-width: 52rem) and (hover: hover) and (pointer: fine) {
  .join__qr { display: grid; grid-template-columns: auto minmax(0, 1fr); gap: 1.1rem; align-items: center; margin: 1.6rem 0 0; }
}
.join__qr img { width: 8rem; height: 8rem; border-radius: 3px; }
.join__qr figcaption { max-width: 20rem; font-size: 0.88rem; line-height: 1.45; color: var(--body); }
/* The disclosure is set in body grey, not the quieter dim grey, because its position over the stone
   depends on how tall the band is, and that changes with the track flag: in one column, the short
   dark-state band put it on the stone (dim measured 3.4:1 at 479px, under the 4.5:1 it needs). Body
   grey holds 4.5:1 anywhere on the stone at this opacity, so no layout can break it. */
.join__go .join__disclose { margin-top: 1.4rem; font-size: 0.85rem; line-height: 1.5; color: var(--body); }

/* ------------------------------------------------------------------ footer */
.foot {
  border-top: var(--hair) solid rgba(255, 255, 255, 0.055);
  padding-block: 2.75rem 3.25rem;
  font-size: 0.92rem;
  color: var(--dim);
}
.foot__in { display: grid; gap: 1.5rem; }
.foot__nav { display: flex; flex-wrap: wrap; gap: 0.6rem 1.4rem; }
.foot__nav a { color: var(--body); text-decoration: none; }
.foot__nav a:hover { color: var(--bone); text-decoration: underline; }
.foot__legal { margin: 0; }

@media (min-width: 44rem) {
  .foot__in { grid-template-columns: 1fr auto; align-items: center; }
  .foot__legal { text-align: right; }
}

/* ------------------------------------------------------------------- prose */
/* Long-form pages: privacy and credits. */
.prose { max-width: 44rem; }
.prose__h2 {
  margin: 2.75rem 0 0.85rem;
  font-family: var(--display);
  font-weight: 700;
  font-size: clamp(1.25rem, 1.1rem + 0.7vw, 1.6rem);
  letter-spacing: 0.02em;
  color: var(--bone);
}
.prose__h3 {
  margin: 2rem 0 0.6rem;
  font-family: var(--display);
  font-weight: 600;
  font-size: 1.08rem;
  letter-spacing: 0.04em;
  color: var(--bone);
}
.prose p { margin: 0 0 1.05rem; }
.prose ul { margin: 0 0 1.2rem; padding-left: 1.15rem; list-style: disc; }
.prose li { margin-bottom: 0.5rem; }
.prose li::marker { color: var(--ember); }

.stamp {
  display: inline-block;
  margin-bottom: 2rem;
  padding: 0.55rem 0.9rem;
  font-size: 0.85rem;
  letter-spacing: 0.04em;
  color: var(--body);
}
.stamp b { color: var(--bone); font-weight: 700; }

/* Credits: one row per asset, grouped by licence. */
.credit-group { margin-bottom: 2.5rem; }
.credit-list { margin: 0; padding: 0; list-style: none; display: grid; gap: 0.1rem; }
.credit-list li {
  display: grid;
  gap: 0.15rem;
  padding: 0.8rem 0;
  border-bottom: var(--hair) solid rgba(255, 255, 255, 0.05);
  margin: 0;
}
.credit-list b { color: var(--bone); font-weight: 700; }
.credit-list small { color: var(--dim); font-size: 0.87rem; }
.licence {
  display: inline-block;
  margin-left: 0.5rem;
  padding: 0.05rem 0.45rem;
  border: var(--hair) solid var(--edge-strong);
  border-radius: 2px;
  font-size: 0.72rem;
  letter-spacing: 0.09em;
  text-transform: uppercase;
  color: var(--dim);
  vertical-align: 0.12em;
}

/* --------------------------------------------------------------------- 404 */
.lost {
  min-height: 72vh;
  display: grid;
  place-content: center;
  text-align: center;
  gap: 1.1rem;
  padding-block: 4rem;
}
.lost__mark { width: 88px; height: 88px; margin-inline: auto; opacity: 0.9; }
.lost__code {
  margin: 0;
  font-family: var(--display);
  font-weight: 900;
  font-size: clamp(3rem, 14vw, 5rem);
  letter-spacing: 0.1em;
  color: var(--bone);
}
.lost__line { margin: 0; color: var(--body); }

/* ------------------------------------------------------------ reduced motion */
@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  .hero__heat { animation: none; }
  *,
  *::before,
  *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
  }
}
