/* ============================================================
   WordPlayLeague — marketing site
   Palette inherited from the app (html_generator_v2.py, v6) so the
   site and the product read as one thing.
   System fonts only: page speed was half the reason for leaving Wix,
   and a font CDN hands some of it straight back.
   ============================================================ */

:root {
  --bg:        #1a1a1b;
  --bg-2:      #202022;
  --surface:   #26262a;
  --line:      #35353a;
  --line-soft: #2b2b2f;

  --text:      #d7dadc;
  --text-soft: #a8acae;
  --text-mute: #82858a;

  --cyan:      #00e8da;
  --cyan-deep: #00c4b8;
  --cyan-dim:  rgba(0, 232, 218, 0.12);
  --orange:    #ffa64d;
  --red:       #ff5c5c;

  --body:  "Segoe UI", system-ui, -apple-system, "Helvetica Neue", Arial, sans-serif;
  --data:  ui-monospace, "Cascadia Mono", "SF Mono", Menlo, Consolas, monospace;

  /* Type scale. Deliberately steeper at the top than the first pass: the gap
     between our page and the references was mostly headline size and air. */
  --step--1: clamp(0.8rem, 0.78rem + 0.1vw, 0.87rem);
  --step-0:  clamp(1rem, 0.97rem + 0.16vw, 1.1rem);
  --step-1:  clamp(1.2rem, 1.12rem + 0.38vw, 1.45rem);
  --step-2:  clamp(1.6rem, 1.38rem + 1.05vw, 2.3rem);
  --step-3:  clamp(2.2rem, 1.72rem + 2.3vw, 3.6rem);
  --step-4:  clamp(2.7rem, 1.9rem + 3.8vw, 5rem);

  /* Spacing scale — one ladder, used everywhere, so rhythm is consistent. */
  --sp-1: 0.5rem;
  --sp-2: 0.85rem;
  --sp-3: 1.35rem;
  --sp-4: 2.1rem;
  --sp-5: 3.25rem;
  --sp-6: clamp(4rem, 2.6rem + 6vw, 8rem);

  --radius:    14px;
  --radius-lg: 20px;
  --elev: 0 24px 60px -34px rgba(0, 0, 0, 0.92);

  --ease: cubic-bezier(0.22, 0.68, 0.32, 1);

  --wrap: 1280px;
  --gut:  clamp(1.1rem, 0.7rem + 1.9vw, 2.25rem);
}

* { box-sizing: border-box; }

html { scroll-behavior: smooth; }
@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  *, *::before, *::after { animation-duration: 0.01ms !important; transition-duration: 0.01ms !important; }
}

body {
  margin: 0;
  background: var(--bg);
  color: var(--text);
  font-family: var(--body);
  font-size: var(--step-0);
  line-height: 1.65;
  -webkit-font-smoothing: antialiased;
}

h1, h2, h3 {
  margin: 0;
  line-height: 1.12;
  letter-spacing: -0.025em;
  text-wrap: balance;
  font-weight: 750;
}
h1 { font-size: var(--step-3); }
h2 { font-size: var(--step-2); }
h3 { font-size: var(--step-1); }
p, ul, ol { margin: 0; }

a { color: var(--cyan); text-decoration-thickness: 1px; text-underline-offset: 0.18em; }
a:hover { color: var(--cyan-deep); }

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

.wrap {
  width: 100%;
  max-width: var(--wrap);
  margin-inline: auto;
  padding-inline: var(--gut);
}

/* Skip link — keyboard users shouldn't tab the whole nav on every page */
.skip {
  position: absolute;
  left: -9999px;
  top: 0;
  background: var(--cyan);
  color: #10201f;
  padding: 0.6rem 1rem;
  font-weight: 700;
  z-index: 100;
}
.skip:focus { left: 0; }

/* ---------------- header ---------------- */
.site-head {
  position: sticky;
  top: 0;
  z-index: 50;
  background: rgba(26, 26, 27, 0.88);
  backdrop-filter: blur(12px);
  -webkit-backdrop-filter: blur(12px);
  border-bottom: 1px solid var(--line-soft);
}
.site-head .wrap {
  display: flex;
  align-items: center;
  gap: 1rem;
  padding-block: 0.65rem;
  flex-wrap: wrap;
}
.brand {
  font-weight: 800;
  letter-spacing: -0.02em;
  font-size: 1.05rem;
  text-decoration: none;
  color: var(--text);
  margin-right: auto;
}
.brand span { color: var(--cyan); }
.nav {
  display: flex;
  gap: 0.25rem 1.15rem;
  flex-wrap: wrap;
  align-items: center;
  font-size: var(--step--1);
}
.nav a {
  color: var(--text-soft);
  text-decoration: none;
  padding: 0.3rem 0;
}
.nav a:hover, .nav a[aria-current="page"] { color: var(--cyan); }

/* ---------------- buttons ---------------- */
.btn {
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
  font-family: var(--data);
  font-size: var(--step--1);
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  text-decoration: none;
  padding: 0.7rem 1.15rem;
  border-radius: 5px;
  border: 2px solid var(--cyan);
  background: var(--cyan);
  color: #10201f;
  cursor: pointer;
  transition: transform 0.16s ease, background 0.16s ease, box-shadow 0.16s ease;
}
.btn:hover {
  color: #10201f;
  background: var(--cyan-deep);
  transform: translateY(-2px);
  box-shadow: 0 10px 24px -12px rgba(0, 232, 218, 0.65);
}
.btn--ghost {
  background: transparent;
  border-color: var(--line);
  color: var(--text);
}
.btn--ghost:hover {
  background: transparent;
  border-color: var(--cyan);
  color: var(--cyan);
  box-shadow: none;
}
.btn--sm { padding: 0.45rem 0.85rem; }

/* ---------------- prose pages (legal, rules) ---------------- */
.prose {
  padding-block: clamp(2.5rem, 1.6rem + 3.4vw, 4.5rem);
}
.prose .wrap { max-width: 50rem; }

.prose-head { margin-bottom: 2.25rem; }
.prose-head .eyebrow {
  font-family: var(--data);
  font-size: var(--step--1);
  letter-spacing: 0.17em;
  text-transform: uppercase;
  color: var(--text-mute);
  margin-bottom: 0.6rem;
}
.prose-head .updated {
  color: var(--text-mute);
  font-size: var(--step--1);
  margin-top: 0.7rem;
}

.prose h2 {
  font-size: var(--step-1);
  margin-top: 2.4rem;
  margin-bottom: 0.65rem;
  padding-top: 1.4rem;
  border-top: 1px solid var(--line-soft);
}
.prose h2:first-of-type { border-top: 0; padding-top: 0; }
.prose h3 {
  font-size: var(--step-0);
  margin-top: 1.5rem;
  margin-bottom: 0.4rem;
  color: var(--text);
}
.prose p { margin-bottom: 0.95rem; color: var(--text-soft); }
.prose ul { margin: 0 0 1rem 1.15rem; padding: 0; color: var(--text-soft); }
.prose li { margin-bottom: 0.35rem; }
.prose strong { color: var(--text); font-weight: 650; }

/* A call-out for the things carriers and auditors look for */
.keyline {
  border-left: 3px solid var(--cyan);
  background: var(--cyan-dim);
  border-radius: 0 6px 6px 0;
  padding: 1rem 1.15rem;
  margin: 1.4rem 0;
}
.keyline p { color: var(--text); margin-bottom: 0.5rem; }
.keyline p:last-child { margin-bottom: 0; }

code, .mono {
  font-family: var(--data);
  font-size: 0.92em;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: 3px;
  padding: 0.08em 0.36em;
  word-break: break-word;
}

/* ---------------- footer ---------------- */
.site-foot {
  border-top: 1px solid var(--line-soft);
  background: var(--bg-2);
  padding-block: 2.5rem;
  margin-top: 4rem;
  font-size: var(--step--1);
  color: var(--text-mute);
}
.foot-grid {
  display: flex;
  flex-wrap: wrap;
  gap: 1.5rem 2.5rem;
  justify-content: space-between;
  align-items: flex-start;
}
.foot-links {
  display: flex;
  flex-wrap: wrap;
  gap: 0.4rem 1.25rem;
}
.foot-links a { color: var(--text-soft); text-decoration: none; }
.foot-links a:hover { color: var(--cyan); }
.foot-note {
  margin-top: 1.75rem;
  padding-top: 1.25rem;
  border-top: 1px solid var(--line-soft);
  max-width: 70ch;
  line-height: 1.6;
}

/* ============================================================
   HOMEPAGE
   ============================================================ */

.sec { padding-block: clamp(3rem, 1.8rem + 5vw, 6rem); }
.sec--alt { background: var(--bg-2); border-block: 1px solid var(--line-soft); }
.sec-head { max-width: 46rem; margin-bottom: 2.5rem; }
.sec-head .eyebrow {
  font-family: var(--data);
  font-size: var(--step--1);
  letter-spacing: 0.17em;
  text-transform: uppercase;
  color: var(--cyan);
  margin-bottom: 0.7rem;
}
.sec-head p { color: var(--text-soft); margin-top: 0.9rem; font-size: var(--step-1); line-height: 1.55; }
.lede { color: var(--text-soft); max-width: 62ch; }
.lede + .lede { margin-top: 0.9rem; }

/* ---------- hero ---------- */
.hero { position: relative; overflow: hidden; padding-block: clamp(2.75rem, 1.5rem + 5vw, 5.5rem); }
.hero::before {
  content: "";
  position: absolute; inset: 0; z-index: 0; pointer-events: none;
  background:
    radial-gradient(56% 42% at 80% 6%, rgba(0,232,218,0.13), transparent 62%),
    radial-gradient(44% 38% at 6% 84%, rgba(255,166,77,0.09), transparent 64%);
}
.hero > .wrap { position: relative; z-index: 1; }
.hero-grid { display: grid; gap: clamp(2rem, 1rem + 4vw, 3.5rem); align-items: center; }
@media (min-width: 900px) { .hero-grid { grid-template-columns: 1.05fr 0.95fr; } }
.hero h1 { font-size: clamp(2.2rem, 1.6rem + 2.9vw, 3.9rem); }
.hero h1 em { font-style: normal; color: var(--cyan); display: block; }
.hero-sub { font-size: var(--step-1); color: var(--text-soft); max-width: 40ch; margin-top: 1.2rem; line-height: 1.5; }
.hero-actions { display: flex; flex-wrap: wrap; gap: 0.7rem; margin-top: 1.9rem; }
.hero-badge { display: flex; align-items: center; gap: 0.7rem; margin-top: 1.5rem; color: var(--text-mute); font-size: var(--step--1); }
.hero-badge img { width: 74px; height: auto; flex: none; }
.shot { width: 100%; height: auto; border-radius: 10px; display: block; }
.shot-frame { border: 1px solid var(--line); border-radius: 12px; overflow: hidden; background: var(--bg-2); box-shadow: 0 24px 54px -30px rgba(0,0,0,0.95); }

/* ---------- proof strip ---------- */
.proof { display: grid; gap: 1px; background: var(--line-soft); border-block: 1px solid var(--line-soft); grid-template-columns: repeat(2, 1fr); }
@media (min-width: 760px) { .proof { grid-template-columns: repeat(4, 1fr); } }
.proof div { background: var(--bg); padding: 1.4rem 1.2rem; text-align: center; }
.proof .n { font-size: var(--step-2); font-weight: 760; letter-spacing: -0.03em; color: var(--cyan); font-variant-numeric: tabular-nums; }
.proof .k { font-family: var(--data); font-size: 0.72rem; letter-spacing: 0.13em; text-transform: uppercase; color: var(--text-mute); margin-top: 0.3rem; }

/* ---------- generic card grids ---------- */
.grid { display: grid; gap: 1.1rem; }
@media (min-width: 700px) { .grid--3 { grid-template-columns: repeat(3, 1fr); } .grid--2 { grid-template-columns: repeat(2, 1fr); } }
.card { background: var(--surface); border: 1px solid var(--line); border-radius: 9px; padding: 1.4rem 1.45rem; }
.card h3 { margin-bottom: 0.5rem; }
.card p { color: var(--text-soft); font-size: calc(var(--step-0) * 0.97); }
.card .num { font-family: var(--data); font-size: 0.74rem; letter-spacing: 0.14em; color: var(--cyan); display: block; margin-bottom: 0.55rem; }
.card--free { border-color: rgba(0,232,218,0.4); }
.tag { display: inline-block; font-family: var(--data); font-size: 0.68rem; letter-spacing: 0.12em; text-transform: uppercase; color: #10201f; background: var(--cyan); border-radius: 3px; padding: 0.12rem 0.45rem; margin-left: 0.5rem; vertical-align: 0.14em; }

/* ---------- split rows ---------- */
.split { display: grid; gap: clamp(1.75rem, 1rem + 3vw, 3rem); align-items: center; }
@media (min-width: 880px) { .split { grid-template-columns: 1fr 1fr; } .split--flip > :first-child { order: 2; } }

/* ---------- pricing ---------- */
.price-grid { display: grid; gap: 1.1rem; }
@media (min-width: 760px) { .price-grid { grid-template-columns: repeat(3, 1fr); } }
.price { background: var(--surface); border: 1px solid var(--line); border-radius: 9px; padding: 1.5rem; display: flex; flex-direction: column; gap: 0.55rem; }
.price .amt { font-size: var(--step-2); font-weight: 760; letter-spacing: -0.03em; }
.price .amt small { font-size: var(--step--1); font-weight: 400; color: var(--text-mute); letter-spacing: 0; }
.price .who { font-family: var(--data); font-size: 0.74rem; letter-spacing: 0.14em; text-transform: uppercase; color: var(--cyan); }
.price p { color: var(--text-soft); font-size: calc(var(--step-0) * 0.95); }

/* ---------- testimonials ---------- */
.quotes { display: grid; gap: 1.1rem; }
@media (min-width: 820px) { .quotes { grid-template-columns: repeat(3, 1fr); } }
.quote { background: var(--surface); border: 1px solid var(--line); border-radius: 9px; padding: 1.5rem; display: flex; flex-direction: column; gap: 0.9rem; }
.quote blockquote { margin: 0; color: var(--text); font-size: calc(var(--step-0) * 1.02); line-height: 1.55; }
.quote .who { font-family: var(--data); font-size: 0.74rem; letter-spacing: 0.1em; color: var(--text-mute); text-transform: uppercase; }

/* ---------- faq ---------- */
.faq { display: grid; gap: 0; border: 1px solid var(--line); border-radius: 9px; overflow: hidden; }
.faq details { border-bottom: 1px solid var(--line-soft); background: var(--surface); }
.faq details:last-child { border-bottom: 0; }
.faq summary {
  cursor: pointer; list-style: none; padding: 1.05rem 1.3rem;
  font-weight: 650; display: flex; justify-content: space-between; gap: 1rem; align-items: center;
}
.faq summary::-webkit-details-marker { display: none; }
.faq summary::after { content: "+"; font-family: var(--data); color: var(--cyan); font-size: 1.2rem; line-height: 1; flex: none; }
.faq details[open] summary::after { content: "\2212"; }
.faq summary:hover { color: var(--cyan); }
.faq .answer { padding: 0 1.3rem 1.2rem; color: var(--text-soft); }
.faq .answer p + p { margin-top: 0.7rem; }

/* ---------- closing CTA ---------- */
.close-cta { text-align: center; }
.close-cta h2 { max-width: 20ch; margin-inline: auto; }
.close-cta p { color: var(--text-soft); max-width: 52ch; margin: 1rem auto 0; }
.close-cta .hero-actions { justify-content: center; }

/* Screenshots inside prose: cap at their natural width so tall phone captures
   don't blow up to the full measure, and give them an edge against the page. */
.prose .shot {
  max-width: min(100%, 420px);
  width: auto;
  margin: 0.4rem 0 0.2rem;
  border: 1px solid var(--line);
  border-radius: 8px;
  background: var(--bg-2);
}
.prose .shot[width="903"], .prose .shot[width="1100"], .prose .shot[width="330"] {
  max-width: min(100%, 620px);
}
.prose p:has(> .shot) { margin-bottom: 1.4rem; }

/* ---------------- signup form (/register) ---------------- */
.signup { max-width: 30rem; display: flex; flex-direction: column; gap: 1rem; margin-top: 0.5rem; }
.signup .field { display: flex; flex-direction: column; gap: 0.35rem; }
.signup label { font-size: var(--step--1); color: var(--text-soft); font-weight: 600; }
.signup label span { color: var(--cyan); }
.signup input {
  background: var(--bg-2);
  border: 1px solid var(--line);
  border-radius: 6px;
  padding: 0.65rem 0.8rem;
  color: var(--text);
  font: inherit;
  font-size: var(--step-0);
}
.signup input:focus-visible { border-color: var(--cyan); outline: 2px solid var(--cyan); outline-offset: 1px; }
.signup .consent {
  font-size: var(--step--1);
  color: var(--text-mute);
  line-height: 1.6;
  background: var(--bg-2);
  border: 1px solid var(--line-soft);
  border-radius: 6px;
  padding: 0.9rem 1rem;
  margin: 0;
}
.signup button { align-self: flex-start; }

/* ---------------- league index ---------------- */
.league-grid { display: grid; gap: 0.9rem; margin: 1.6rem 0 0.5rem; }
@media (min-width: 640px) { .league-grid { grid-template-columns: repeat(2, 1fr); } }
.league-card {
  display: flex; flex-direction: column; gap: 0.3rem;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: 9px;
  padding: 1.15rem 1.25rem;
  text-decoration: none;
  color: var(--text);
  transition: transform 0.16s ease, border-color 0.16s ease;
}
.league-card:hover { transform: translateY(-2px); border-color: rgba(0,232,218,0.45); color: var(--text); }
.league-card h2 { font-size: var(--step-1); margin: 0; border: 0; padding: 0; }
.league-card .meta { font-family: var(--data); font-size: 0.74rem; letter-spacing: 0.07em; text-transform: uppercase; color: var(--text-mute); margin: 0; }
.league-card .go { font-family: var(--data); font-size: var(--step--1); color: var(--cyan); margin-top: 0.5rem; }

/* ---------------- embedded app views ---------------- */
.embed-frame {
  margin: 1.6rem 0 0.6rem;
  border: 1px solid var(--line);
  border-radius: 10px;
  overflow: hidden;
  background: var(--bg-2);
}
.embed-frame iframe { display: block; width: 100%; height: 760px; border: 0; }
@media (max-width: 600px) { .embed-frame iframe { height: 620px; } }
.embed-fallback { font-size: var(--step--1); color: var(--text-mute); }

/* ============================================================
   FOUNDATIONS PASS — section formula, air, motion
   Appended last so it wins over the first-pass rules above.
   ============================================================ */

/* ---- air ---- */
.sec { padding-block: var(--sp-6); }
.prose { padding-block: clamp(3rem, 2rem + 4vw, 5.5rem); }
.sec-head { max-width: 50rem; margin-bottom: var(--sp-5); }
.grid, .price-grid, .quotes, .league-grid { gap: var(--sp-3); }
.split { gap: clamp(2.25rem, 1.2rem + 4vw, 4.5rem); }

/* ---- the section formula: eyebrow pill, big two-line headline, quiet subhead
        Both references converge on this, and it is what our first pass lacked. ---- */
.eyebrow {
  display: inline-block;
  font-family: var(--data);
  font-size: 0.72rem;
  font-weight: 700;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--cyan);
  background: var(--cyan-dim);
  border: 1px solid rgba(0, 232, 218, 0.28);
  border-radius: 999px;
  padding: 0.3rem 0.8rem;
  margin-bottom: var(--sp-3);
}
.sec-head .eyebrow, .prose-head .eyebrow { margin-bottom: var(--sp-3); }
.prose-head .eyebrow { color: var(--cyan); background: var(--cyan-dim); }

.sec-head h2, .close-cta h2 {
  font-size: var(--step-3);
  letter-spacing: -0.032em;
  line-height: 1.05;
  max-width: 18ch;
}
.close-cta h2 { max-width: 22ch; }
.sec-head p {
  font-size: var(--step-1);
  color: var(--text-soft);
  margin-top: var(--sp-3);
  max-width: 54ch;
  line-height: 1.5;
}
.split h2 { font-size: var(--step-3); letter-spacing: -0.032em; line-height: 1.05; max-width: 16ch; }
.prose-head h1, .hero h1 { letter-spacing: -0.035em; }
.hero h1 { font-size: var(--step-4); line-height: 1.0; }

/* ---- cards: bigger radius, softer elevation, quieter borders ---- */
.card, .price, .quote, .league-card, .faq, .shot-frame, .embed-frame {
  border-radius: var(--radius);
  border-color: var(--line-soft);
}
.shot-frame { border-radius: var(--radius-lg); box-shadow: var(--elev); }
.card, .price, .quote { padding: var(--sp-4); }
.card:hover, .league-card:hover, .quote:hover {
  transform: translateY(-4px);
  border-color: rgba(0, 232, 218, 0.4);
  box-shadow: var(--elev);
}
.card, .league-card, .quote {
  transition: transform 0.28s var(--ease), border-color 0.28s var(--ease),
              box-shadow 0.28s var(--ease);
}

/* ---- proof strip gets room to breathe ---- */
.proof div { padding: var(--sp-4) var(--sp-3); }
.proof .n { font-size: var(--step-2); }

/* ============================================================
   MOTION
   Elements are VISIBLE by default. The reveal styles only apply once the
   inline head script has confirmed JS is running and motion is wanted, so a
   crawler, a JS failure, or reduced-motion all get the finished page with no
   blank space. Apple's page shows what the other way round looks like when
   the observer does not fire.
   ============================================================ */
.js-motion [data-reveal] {
  opacity: 0;
  transform: translateY(18px);
  transition: opacity 0.62s var(--ease), transform 0.62s var(--ease);
  transition-delay: var(--reveal-delay, 0ms);
  will-change: opacity, transform;
}
.js-motion [data-reveal].is-in {
  opacity: 1;
  transform: none;
}
.js-motion [data-reveal].is-in { will-change: auto; }

@media (prefers-reduced-motion: reduce) {
  .js-motion [data-reveal] { opacity: 1 !important; transform: none !important; transition: none !important; }
  .card:hover, .league-card:hover, .quote:hover { transform: none; }
  .btn:hover { transform: none; }
}

/* ============================================================
   HERO DEMO — the thread filling up, the table filling in beside it
   ============================================================ */
.demo {
  display: grid;
  gap: var(--sp-3);
  grid-template-columns: 1fr;
  align-items: start;
}
@media (min-width: 540px) { .demo { grid-template-columns: 1.15fr 0.85fr; } }

/* phone */
.demo-phone {
  background: #0e0e12;
  border: 1px solid var(--line);
  border-radius: 26px;
  padding: 0.75rem 0.6rem 0.6rem;
  box-shadow: var(--elev), 0 0 0 1px rgba(0,232,218,0.1), 0 0 40px -16px rgba(0,232,218,0.3);
  position: relative;
}
.demo-phone::before {
  content: "";
  display: block;
  width: 60px; height: 4px;
  border-radius: 999px;
  background: #2a2a31;
  margin: 0 auto 0.6rem;
}
.demo-head {
  font-family: var(--data);
  font-size: 0.64rem;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--text-mute);
  text-align: center;
  padding-bottom: 0.5rem;
  margin-bottom: 0.5rem;
  border-bottom: 1px solid var(--line-soft);
}
.demo-thread {
  height: 310px;
  overflow: hidden;
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
  padding: 0 0.25rem;
  scroll-behavior: smooth;
  -webkit-mask-image: linear-gradient(180deg, transparent 0, #000 22px);
  mask-image: linear-gradient(180deg, transparent 0, #000 22px);
}

/* chat bubbles */
.bub {
  align-self: flex-start;
  max-width: 85%;
  background: #1c1c22;
  border: 1px solid var(--line-soft);
  border-radius: 13px 13px 13px 4px;
  padding: 0.45rem 0.6rem 0.5rem;
  position: relative;
  animation: bubIn 0.34s var(--ease) both;
}
.bub--mine {
  align-self: flex-end;
  background: rgba(0, 232, 218, 0.13);
  border-color: rgba(0, 232, 218, 0.3);
  border-radius: 13px 13px 4px 13px;
}
@keyframes bubIn {
  from { opacity: 0; transform: translateY(9px) scale(0.97); }
  to   { opacity: 1; transform: none; }
}
.bub-who {
  display: block;
  font-family: var(--data);
  font-size: 0.6rem;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--text-mute);
  margin-bottom: 0.15rem;
}
.bub--mine .bub-who { color: var(--cyan); }
.bub-txt { font-size: 0.8rem; color: var(--text); line-height: 1.35; }
.bub-react {
  position: absolute;
  right: -6px; bottom: -9px;
  font-size: 0.78rem;
  background: #16161b;
  border: 1px solid var(--line);
  border-radius: 999px;
  padding: 0.05rem 0.28rem;
  animation: pop 0.3s var(--ease) both;
}
@keyframes pop { from { opacity: 0; transform: scale(0.5); } to { opacity: 1; transform: none; } }

/* the wordle grid, 5 wide */
.wgrid {
  display: grid;
  grid-template-columns: repeat(5, 9px);
  gap: 2px;
  margin-top: 0.35rem;
}
.wt { width: 9px; height: 9px; border-radius: 2px; display: block; }
.wt--correct { background: var(--cyan); }
.wt--present { background: var(--orange); }
.wt--absent  { background: #34343c; }

/* the table beside it */
.demo-table {
  background: var(--surface);
  border: 1px solid var(--line-soft);
  border-radius: var(--radius);
  overflow: hidden;
}
.demo-table-head {
  font-family: var(--data);
  font-size: 0.62rem;
  letter-spacing: 0.11em;
  text-transform: uppercase;
  color: var(--cyan);
  padding: 0.6rem 0.8rem;
  border-bottom: 1px solid var(--line-soft);
  background: rgba(0, 232, 218, 0.06);
  display: flex;
  align-items: center;
  gap: 0.4rem;
}
.demo-table-head i {
  width: 6px; height: 6px; border-radius: 50%;
  background: var(--cyan);
  animation: blip 2s ease-in-out infinite;
}
@keyframes blip { 0%,100% { opacity: 1; } 50% { opacity: 0.25; } }
.demo-table ul { list-style: none; margin: 0; padding: 0.3rem 0; min-height: 268px; }
.drow {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: 0.8rem;
  padding: 0.42rem 0.8rem;
  font-size: 0.82rem;
  opacity: 0;
  transform: translateX(10px);
  transition: opacity 0.4s var(--ease), transform 0.4s var(--ease);
}
.drow.in, .js-nomotion .drow { opacity: 1; transform: none; }
.drow-name { color: var(--text-soft); }
.drow-score {
  font-family: var(--data);
  font-weight: 700;
  color: var(--cyan);
  font-variant-numeric: tabular-nums;
}

@media (prefers-reduced-motion: reduce) {
  .bub, .bub-react { animation: none; }
  .drow { opacity: 1; transform: none; transition: none; }
  .demo-table-head i { animation: none; }
}

/* ---- proof strip, tightened ----------------------------------------------
   Was four tall cards with dim labels. Now one slim line: numbers in the
   accent, labels in readable body colour rather than the muted grey. */
.proof {
  display: block;
  background: var(--bg-2);
  border-block: 1px solid var(--line-soft);
  padding-block: 0.85rem;
  grid-template-columns: none;
  gap: 0;
}
.proof-list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  align-items: baseline;
  gap: 0.35rem 1.9rem;
  font-size: var(--step--1);
  color: var(--text-soft);
}
.proof-list li { display: inline-flex; align-items: baseline; gap: 0.42rem; white-space: nowrap; }
.proof-list li + li::before {
  content: "";
  width: 3px; height: 3px;
  border-radius: 50%;
  background: var(--line);
  margin-right: 1.5rem;
  align-self: center;
}
.proof-list b {
  font-size: 1.12rem;
  font-weight: 760;
  color: var(--cyan);
  letter-spacing: -0.02em;
  font-variant-numeric: tabular-nums;
}
@media (max-width: 560px) {
  .proof-list { gap: 0.3rem 1.1rem; justify-content: flex-start; }
  .proof-list li + li::before { margin-right: 0.8rem; }
}

/* ---- demo proportions: make the phone actually phone-shaped ----
   First pass was squat — a 270x310 thread reads as a tablet. Real handsets are
   about 2.1:1, so the thread grows and the phone column is pinned narrow. */
.demo { grid-template-columns: 1fr; }
@media (min-width: 560px) {
  .demo {
    grid-template-columns: minmax(0, 248px) 1fr;
    gap: var(--sp-3);
    align-items: stretch;
  }
}
.demo-phone { border-radius: 30px; padding: 0.8rem 0.55rem 0.7rem; }
.demo-thread { height: 468px; }
.demo-table { display: flex; flex-direction: column; }
.demo-table ul { min-height: 0; flex: 1; }
@media (max-width: 559px) {
  .demo-thread { height: 330px; }
}

/* Longer fade at the top of the thread so older messages dissolve rather than
   getting a hard cut, and roomier table rows so the column fills its height. */
.demo-thread {
  -webkit-mask-image: linear-gradient(180deg, transparent 0, rgba(0,0,0,0.35) 34px, #000 76px);
  mask-image: linear-gradient(180deg, transparent 0, rgba(0,0,0,0.35) 34px, #000 76px);
  padding-top: 0.2rem;
}
.demo-table ul { display: flex; flex-direction: column; justify-content: flex-start; padding: 0.45rem 0; }
.drow { padding: 0.62rem 0.9rem; font-size: 0.86rem; }
.drow + .drow { border-top: 1px solid rgba(255,255,255,0.04); }
.drow-score { font-size: 0.95rem; }

/* ============================================================
   HERO DEMO v2 — loose bubbles left, the real Latest Scores tab right
   The phone UI mirrors the app's own view (see
   image_artifacts/update screenshots with new ui/latest_scores_2026.png):
   cyan league title, four pills, the Wordle # line, then score cards.
   ============================================================ */
.demo { display: grid; grid-template-columns: 1fr; gap: var(--sp-3); align-items: center; }
@media (min-width: 620px) { .demo { grid-template-columns: 1fr minmax(0, 252px); } }

/* --- loose bubbles, no phone frame --- */
.demo-bubbles {
  display: flex;
  flex-direction: column;
  gap: 0.55rem;
  align-items: flex-start;
  justify-content: flex-end;
  min-height: 300px;
}
.demo-bubbles .bub {
  max-width: 240px;
  background: #1e1e25;
  border: 1px solid var(--line-soft);
  border-radius: 14px 14px 14px 5px;
  padding: 0.45rem 0.65rem 0.5rem;
  box-shadow: 0 10px 26px -18px rgba(0,0,0,0.9);
}
.demo-bubbles .bub--mine {
  align-self: flex-end;
  background: rgba(0,232,218,0.12);
  border-color: rgba(0,232,218,0.3);
  border-radius: 14px 14px 5px 14px;
}

/* --- the phone --- */
.lphone {
  background: #07070b;
  border: 1px solid #2a2a33;
  border-radius: 30px;
  padding: 0.55rem 0.45rem;
  box-shadow: var(--elev), 0 0 0 1px rgba(0,232,218,0.09), 0 0 46px -18px rgba(0,232,218,0.3);
}
.lscreen {
  background: #0b0b10;
  border-radius: 24px;
  padding: 0.7rem 0.5rem 0.6rem;
  height: 470px;
  overflow: hidden;
  display: flex;
  flex-direction: column;
}
.lhead { text-align: center; margin-bottom: 0.5rem; }
.lhead .emoji { font-size: 0.9rem; display: block; line-height: 1; }
.lhead .lleague {
  font-size: 1.05rem;
  font-weight: 800;
  color: var(--cyan);
  letter-spacing: -0.02em;
  display: block;
  margin-top: 0.1rem;
}
.lhead .lbrand {
  font-size: 0.56rem;
  font-weight: 700;
  background: linear-gradient(90deg, var(--orange), #8ddc6b);
  -webkit-background-clip: text; background-clip: text; color: transparent;
  display: block;
}
.lpills { display: grid; grid-template-columns: 1fr 1fr; gap: 4px; margin-bottom: 0.5rem; }
.lpill {
  font-size: 0.54rem;
  font-weight: 700;
  text-align: center;
  padding: 0.3rem 0.1rem;
  border-radius: 999px;
  border: 1px solid #23232c;
  color: #6f737a;
  background: #111118;
}
.lpill.on {
  color: var(--cyan);
  border-color: rgba(0,232,218,0.55);
  box-shadow: 0 0 12px -4px rgba(0,232,218,0.6) inset;
}
.lwordle {
  text-align: center;
  font-size: 0.56rem;
  font-weight: 700;
  color: var(--cyan);
  margin-bottom: 0.45rem;
}
.lcards { display: flex; flex-direction: column; gap: 0.4rem; overflow: hidden; flex: 1; }

.lcard {
  background: #12121a;
  border: 1px solid #1e1e28;
  border-radius: 11px;
  padding: 0.45rem 0.5rem;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.4rem;
  opacity: 0;
  transform: translateY(-8px);
  transition: opacity 0.42s var(--ease), transform 0.42s var(--ease);
}
.lcard.in { opacity: 1; transform: none; }
.lcard-who { display: flex; flex-direction: column; gap: 0.18rem; }
.lname { font-size: 0.78rem; font-weight: 750; color: #e9ecee; letter-spacing: -0.01em; }
.lscore {
  align-self: flex-start;
  font-size: 0.6rem;
  font-weight: 800;
  color: var(--orange);
  background: rgba(255,166,77,0.14);
  border-radius: 4px;
  padding: 0.05rem 0.3rem;
}
.lgrid { display: grid; grid-template-columns: repeat(5, 8px); gap: 2px; flex: none; }
.lt { width: 8px; height: 8px; border-radius: 2px; display: block; }
.lt--g { background: #19d2c4; }
.lt--y { background: #f7a24b; }
.lt--x { background: #2f2f37; }
.lt--w { background: #e7e9ea; }

@media (prefers-reduced-motion: reduce) {
  .lcard { opacity: 1; transform: none; transition: none; }
}

/* Running annotation under the demo — explains the animation as it plays. */
.demo-caption {
  grid-column: 1 / -1;
  margin-top: var(--sp-3);
  min-height: 2.6em;
  font-size: var(--step--1);
  color: var(--text-mute);
  text-align: center;
  opacity: 0;
  transition: opacity 0.28s var(--ease);
  max-width: 44ch;
  margin-inline: auto;
  line-height: 1.5;
}
.demo-caption.on { opacity: 1; }
.demo-caption::before {
  content: "";
  display: inline-block;
  width: 5px; height: 5px;
  border-radius: 50%;
  background: var(--cyan);
  margin-right: 0.5rem;
  vertical-align: 0.18em;
}

/* ---- demo v3: phone left, bubbles right; taller screen; roomier bubbles ---- */
@media (min-width: 620px) {
  .demo { grid-template-columns: minmax(0, 262px) 1fr; }
}
.lscreen { height: 560px; }
.lcards { gap: 0.32rem; }
.lcard { padding: 0.4rem 0.5rem; min-height: 0; }
.lcard--none { background: #0e0e15; border-color: #191922; }
.lnone {
  align-self: flex-start;
  font-size: 0.58rem;
  font-weight: 600;
  color: #5d6167;
}
.lscore.is-good { color: var(--cyan);   background: rgba(0,232,218,0.15); }
.lscore.is-mid  { color: var(--orange); background: rgba(255,166,77,0.15); }
.lscore.is-fail { color: #9aa0a6;       background: rgba(154,160,166,0.15); }
.lcard.flash { animation: cardFlash 1.1s var(--ease); }
@keyframes cardFlash {
  0%   { background: rgba(0,232,218,0.2); border-color: rgba(0,232,218,0.5); }
  100% { background: #12121a; border-color: #1e1e28; }
}

/* bubbles: room for a real Wordle post, grid included */
.demo-bubbles { min-height: 420px; justify-content: center; gap: 0.7rem; }
.demo-bubbles .bub { max-width: 300px; padding: 0.6rem 0.8rem 0.7rem; border-radius: 16px 16px 16px 6px; }
.demo-bubbles .bub--mine { border-radius: 16px 16px 6px 16px; }
.demo-bubbles .bub-txt { font-size: 0.88rem; }
.demo-bubbles .bub-who { font-size: 0.64rem; margin-bottom: 0.25rem; }
.demo-bubbles .lgrid { grid-template-columns: repeat(5, 11px); gap: 2.5px; margin-top: 0.45rem; }
.demo-bubbles .lt { width: 11px; height: 11px; border-radius: 2.5px; }

@media (prefers-reduced-motion: reduce) { .lcard.flash { animation: none; } }

/* Cards persist now and change state in place, so they must not start hidden —
   the old opacity:0-until-.in rule left the whole screen blank. */
.lcard { opacity: 1; transform: none; }

/* ---- explainer callouts (right of the phone) ----------------------------
   Brighter and larger than the old chat bubbles: these are the point being
   made, not background texture. They arrive one beat at a time. */
.demo-bubbles { justify-content: center; gap: 1.1rem; min-height: 420px; }
.callout {
  display: flex;
  gap: 0.7rem;
  align-items: flex-start;
  max-width: 340px;
  background: linear-gradient(180deg, rgba(0,232,218,0.13), rgba(0,232,218,0.06));
  border: 1px solid rgba(0,232,218,0.32);
  border-radius: 16px;
  padding: 0.95rem 1.1rem;
  box-shadow: 0 18px 40px -26px rgba(0,232,218,0.55);
  opacity: 0;
  transform: translateY(14px);
  transition: opacity 0.5s var(--ease), transform 0.5s var(--ease);
}
.callout.in { opacity: 1; transform: none; }
.callout p {
  margin: 0;
  font-size: 0.97rem;
  line-height: 1.45;
  color: #eaf4f3;
  font-weight: 500;
}
.callout p b { color: var(--cyan); font-weight: 750; }
.callout-dot {
  flex: none;
  width: 9px; height: 9px;
  border-radius: 50%;
  background: var(--cyan);
  margin-top: 0.42rem;
  box-shadow: 0 0 0 4px rgba(0,232,218,0.16);
}
@media (prefers-reduced-motion: reduce) {
  .callout { opacity: 1; transform: none; transition: none; }
}

/* More of the hero's vertical real estate given to the phone. */
.lscreen { height: 648px; }
.demo-bubbles { min-height: 500px; }

/* ============================================================
   HERO v4 — headline across the top, demo full width beneath it
   ============================================================ */
.hero-top { display: grid; gap: var(--sp-3) var(--sp-5); align-items: end; margin-bottom: var(--sp-5); }
@media (min-width: 900px) { .hero-top { grid-template-columns: 1.05fr 0.95fr; } }
.hero-top h1 { font-size: clamp(2.4rem, 1.5rem + 4.2vw, 4.6rem); }
.hero-side .hero-sub { margin-top: 0; max-width: 46ch; }

/* three columns: phone, the thread, what it means */
.demo { grid-template-columns: 1fr; gap: var(--sp-4); align-items: start; }
@media (min-width: 760px)  { .demo { grid-template-columns: minmax(0, 300px) 1fr; } }
@media (min-width: 1040px) { .demo { grid-template-columns: minmax(0, 300px) minmax(0, 1fr) minmax(0, 1fr); } }

/* phone: wider and a bit shorter — was 2.55:1, which overshot a real handset */
.lscreen { height: 592px; }
.lphone { border-radius: 34px; padding: 0.6rem 0.5rem; }
.lname { font-size: 0.85rem; }
.lscore, .lnone { font-size: 0.65rem; }
.lgrid { grid-template-columns: repeat(5, 9px); }
.lt { width: 9px; height: 9px; }
.lhead .lleague { font-size: 1.2rem; }
.lpill { font-size: 0.6rem; padding: 0.34rem 0.1rem; }
.lwordle { font-size: 0.62rem; }

/* chat bubbles: the cyan glow treatment, no border */
.demo-bubbles { min-height: 0; gap: 0.75rem; justify-content: flex-start; }
.demo-bubbles .bub {
  max-width: 320px;
  border: 0;
  background: linear-gradient(180deg, rgba(0,232,218,0.15), rgba(0,232,218,0.05));
  box-shadow: 0 18px 44px -26px rgba(0,232,218,0.75);
  border-radius: 18px 18px 18px 6px;
  padding: 0.7rem 0.95rem 0.8rem;
  opacity: 0;
  transform: translateY(12px);
  transition: opacity 0.42s var(--ease), transform 0.42s var(--ease);
}
.demo-bubbles .bub.in { opacity: 1; transform: none; }
.demo-bubbles .bub--mine { align-self: flex-end; border-radius: 18px 18px 6px 18px; }
.demo-bubbles .bub-who { color: var(--cyan); font-size: 0.66rem; }
.demo-bubbles .bub-txt { font-size: 0.92rem; color: #eaf4f3; }

/* the explanation column sits alongside */
.demo-notes { display: flex; flex-direction: column; gap: 1.1rem; justify-content: flex-start; }
.demo-notes .callout { max-width: none; }

@media (prefers-reduced-motion: reduce) {
  .demo-bubbles .bub { opacity: 1; transform: none; transition: none; }
}

/* ============================================================
   HERO DEMO v5 — two matching phones, realistic card size, typed notes
   ============================================================ */
.demo { grid-template-columns: 1fr; gap: var(--sp-4); align-items: stretch; }
@media (min-width: 760px)  { .demo { grid-template-columns: repeat(2, minmax(0, 288px)); justify-content: center; } }
@media (min-width: 1100px) { .demo { grid-template-columns: minmax(0,288px) minmax(0,288px) minmax(0,1fr); justify-content: start; } }

.lphone { border-radius: 34px; padding: 0.55rem 0.45rem; }
.lscreen { height: 600px; }

/* --- thread phone --- */
.lscreen--thread { padding: 0.55rem 0.4rem 0.4rem; }
.tbar {
  display: flex; align-items: center; gap: 0.4rem;
  padding: 0 0.35rem 0.5rem;
  border-bottom: 1px solid #1a1a22;
  margin-bottom: 0.45rem;
}
.tbar-dot { width: 20px; height: 20px; border-radius: 50%; background: linear-gradient(140deg, var(--cyan), #0a7f78); flex: none; }
.tbar-name { font-size: 0.78rem; font-weight: 700; color: #e9ecee; }
.tbar-sub { font-size: 0.6rem; color: var(--text-mute); margin-left: auto; }
.tscroll {
  flex: 1; min-height: 0;
  overflow: hidden;
  display: flex; flex-direction: column; gap: 0.5rem;
  padding: 0.2rem 0.3rem 0;
  scroll-behavior: smooth;
}
.tscroll .bub {
  max-width: 88%;
  align-self: flex-start;
  border: 0;
  background: linear-gradient(180deg, rgba(0,232,218,0.16), rgba(0,232,218,0.06));
  box-shadow: 0 14px 34px -24px rgba(0,232,218,0.8);
  border-radius: 15px 15px 15px 5px;
  padding: 0.5rem 0.7rem 0.6rem;
  opacity: 0; transform: translateY(10px);
  transition: opacity 0.4s var(--ease), transform 0.4s var(--ease);
  flex: none;
}
.tscroll .bub.in { opacity: 1; transform: none; }
.tscroll .bub--mine {
  align-self: flex-end;
  border-radius: 15px 15px 5px 15px;
  background: linear-gradient(180deg, rgba(0,232,218,0.3), rgba(0,232,218,0.14));
}
.tscroll .bub-who { font-size: 0.6rem; color: var(--cyan); margin-bottom: 0.15rem; }
.tscroll .bub-txt { font-size: 0.82rem; color: #eef6f5; }
.tscroll .lgrid { grid-template-columns: repeat(5, 10px); gap: 2px; margin-top: 0.4rem; }
.tscroll .lt { width: 10px; height: 10px; border-radius: 2px; }

/* --- latest scores phone: real card size, ~4.5 visible, scrolls --- */
.lscroll { flex: 1; min-height: 0; overflow: hidden; }
.lcards { gap: 0.45rem; }
.lcard {
  padding: 0.65rem 0.6rem;
  border-radius: 13px;
  align-items: center;
  min-height: 74px;
}
.lcard-who { gap: 0.3rem; }
.lname { font-size: 1.02rem; font-weight: 780; }
.lscore { font-size: 0.8rem; padding: 0.1rem 0.42rem; border-radius: 5px; }
.lnone { font-size: 0.78rem; }
.lgrid { grid-template-columns: repeat(5, 13px); gap: 2.5px; }
.lt { width: 13px; height: 13px; border-radius: 3px; }
.lcard { transition: background 0.4s var(--ease), border-color 0.4s var(--ease); }

/* --- notes: no box, typed out, spread top / middle / bottom --- */
.demo-notes {
  display: flex;
  flex-direction: column;
  justify-content: space-between;
  gap: var(--sp-4);
  padding: 0.5rem 0 0.5rem var(--sp-2);
  min-height: 600px;
}
.note {
  margin: 0;
  font-size: var(--step-1);
  line-height: 1.45;
  color: #e7f3f2;
  font-weight: 500;
  max-width: 30ch;
  opacity: 0;
  transition: opacity 0.3s var(--ease);
  text-wrap: pretty;
}
.note.on { opacity: 1; }
.note[data-slot="1"] { align-self: center; }
.note[data-slot="2"] { align-self: flex-start; }
.note span::after {
  content: "";
  display: inline-block;
  width: 2px; height: 1.05em;
  background: var(--cyan);
  margin-left: 2px;
  vertical-align: -0.18em;
  animation: caret 1s step-end infinite;
}
.note.done span::after { display: none; }
@keyframes caret { 50% { opacity: 0; } }

@media (prefers-reduced-motion: reduce) {
  .tscroll .bub { opacity: 1; transform: none; transition: none; }
  .note span::after { animation: none; }
}

/* ============================================================
   HERO v5 — a real photo up top, the live demo in its own section below
   Container widened to 1280 (was 1120). Text measures stay capped so line
   length does not grow with it — wider layout, same readability.
   ============================================================ */
.hero { padding-block: clamp(2.5rem, 1.4rem + 4.5vw, 5rem); }
.hero-grid { display: grid; gap: clamp(2rem, 1rem + 4vw, 4rem); align-items: center; }
@media (min-width: 900px) { .hero-grid { grid-template-columns: 1.06fr 0.94fr; } }
.hero-copy h1 { font-size: clamp(2.4rem, 1.5rem + 4.2vw, 4.6rem); }
.hero-copy .hero-sub { max-width: 44ch; }
.hero-shot img {
  width: 100%;
  height: auto;
  display: block;
  border-radius: var(--radius-lg);
  box-shadow: var(--elev), 0 0 0 1px rgba(0,232,218,0.08), 0 0 70px -30px rgba(0,232,218,0.4);
}

/* the demo now has its own section, so it sits below the fold */
.sec--demo { background: var(--bg-2); border-block: 1px solid var(--line-soft); }
.sec--demo .sec-head h2 { max-width: 24ch; }

/* notes: start at the top with even spacing, not spread over the full height */
.demo-notes {
  justify-content: flex-start;
  gap: var(--sp-5);
  padding-top: 0.25rem;
  min-height: 0;
}
.note[data-slot="1"], .note[data-slot="2"] { align-self: flex-start; }
.note { max-width: 32ch; }

/* The headline is two sentences now, and longer than the old one — ease the
   size and let it run wider so it stays two lines rather than four. */
.hero-copy h1 { font-size: clamp(2.1rem, 1.3rem + 3.4vw, 3.9rem); max-width: 17ch; }

/* ============================================================
   STAT TILES — the numbers set in Wordle tiles that flip to reveal
   ============================================================ */
.proof { padding-block: var(--sp-5); }
.stats {
  list-style: none; margin: 0; padding: 0;
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: var(--sp-4) var(--sp-3);
}
@media (min-width: 820px) { .stats { grid-template-columns: repeat(4, 1fr); } }
.stat { display: flex; flex-direction: column; align-items: center; gap: 0.7rem; text-align: center; }
.stat-tiles { display: flex; gap: 5px; }
.stile {
  --t: clamp(34px, 2.1rem + 1vw, 48px);
  width: var(--t); height: var(--t);
  display: grid; place-items: center;
  font-family: var(--data);
  font-size: calc(var(--t) * 0.52);
  font-weight: 800;
  font-style: normal;
  color: #0c1f1e;
  background: var(--cyan);
  border-radius: 7px;
  box-shadow: 0 10px 26px -14px rgba(0, 232, 218, 0.8);
  transform-style: preserve-3d;
}
/* every fourth tile picks up the orange, so a row is not a solid cyan block */
.stat:nth-child(2) .stile:nth-child(2),
.stat:nth-child(3) .stile:nth-child(1),
.stat:nth-child(4) .stile:nth-child(3),
.stat:nth-child(1) .stile:nth-child(3) {
  background: var(--orange);
  box-shadow: 0 10px 26px -14px rgba(255, 166, 77, 0.75);
}
.stile.flip { animation: tileFlip 0.62s var(--ease) both; }
@keyframes tileFlip {
  0%   { transform: rotateX(-90deg); opacity: 0; }
  55%  { transform: rotateX(12deg);  opacity: 1; }
  100% { transform: rotateX(0);      opacity: 1; }
}
.stat-label {
  font-family: var(--data);
  font-size: 0.72rem;
  letter-spacing: 0.15em;
  text-transform: uppercase;
  color: var(--text-soft);
}
@media (prefers-reduced-motion: reduce) { .stile.flip { animation: none; } }

/* ---- stats as individual cards ------------------------------------------
   Not a slim strip and not the old dull boxes: each number gets its own card
   with an accent rail, so four separate facts read as four separate facts. */
.proof { padding-block: var(--sp-6); background: var(--bg); }
.stats { gap: var(--sp-3); }
.stat {
  position: relative;
  background: var(--surface);
  border: 1px solid var(--line-soft);
  border-radius: var(--radius);
  padding: var(--sp-4) var(--sp-3) var(--sp-3);
  gap: 0.9rem;
  overflow: hidden;
  transition: transform 0.28s var(--ease), border-color 0.28s var(--ease),
              box-shadow 0.28s var(--ease);
}
.stat::before {
  content: "";
  position: absolute;
  inset: 0 0 auto 0;
  height: 3px;
  background: linear-gradient(90deg, var(--cyan), rgba(0,232,218,0));
}
.stat:nth-child(2)::before { background: linear-gradient(90deg, var(--orange), rgba(255,166,77,0)); }
.stat:nth-child(4)::before { background: linear-gradient(90deg, var(--orange), rgba(255,166,77,0)); }
.stat::after {
  content: "";
  position: absolute;
  inset: auto -30% -60% -30%;
  height: 120px;
  background: radial-gradient(60% 100% at 50% 100%, rgba(0,232,218,0.14), transparent 70%);
  pointer-events: none;
}
.stat:hover {
  transform: translateY(-4px);
  border-color: rgba(0,232,218,0.4);
  box-shadow: var(--elev);
}
.stat-label { color: var(--text-soft); font-size: 0.7rem; }
.stile {
  --t: clamp(32px, 1.6rem + 1.4vw, 46px);
  border-radius: 8px;
  box-shadow: 0 12px 24px -14px rgba(0,0,0,0.9), inset 0 -2px 0 rgba(0,0,0,0.18);
}

/* ---- notes back in the cyan panel, popping in rather than typing --------- */
.note {
  display: flex;
  gap: 0.75rem;
  align-items: flex-start;
  max-width: 360px;
  background: linear-gradient(180deg, rgba(0,232,218,0.14), rgba(0,232,218,0.05));
  border: 1px solid rgba(0,232,218,0.3);
  border-radius: 16px;
  padding: 1rem 1.15rem;
  box-shadow: 0 20px 46px -28px rgba(0,232,218,0.75);
  font-size: var(--step-0);
  line-height: 1.5;
  color: #eaf4f3;
  opacity: 0;
  transform: translateY(12px) scale(0.97);
  transition: opacity 0.42s var(--ease), transform 0.42s var(--ease);
}
.note.on { opacity: 1; transform: none; }
.note::before {
  content: "";
  flex: none;
  width: 9px; height: 9px;
  border-radius: 50%;
  background: var(--cyan);
  margin-top: 0.46rem;
  box-shadow: 0 0 0 4px rgba(0,232,218,0.16);
}
.note span::after { display: none; }   /* caret retired with the typewriter */
@media (prefers-reduced-motion: reduce) {
  .note { opacity: 1; transform: none; transition: none; }
}

/* All digits cyan. The mixed orange made the numbers harder to read — the
   colour was decorating at the expense of the thing being decorated. */
.stat:nth-child(1) .stile,
.stat:nth-child(2) .stile,
.stat:nth-child(3) .stile,
.stat:nth-child(4) .stile {
  background: var(--cyan);
  box-shadow: 0 12px 24px -14px rgba(0,0,0,0.9), inset 0 -2px 0 rgba(0,0,0,0.18);
}

/* Same specificity as the rule that introduced the orange, or it does not win. */
.stat:nth-child(1) .stile:nth-child(3),
.stat:nth-child(2) .stile:nth-child(2),
.stat:nth-child(3) .stile:nth-child(1),
.stat:nth-child(4) .stile:nth-child(3) {
  background: var(--cyan);
  box-shadow: 0 12px 24px -14px rgba(0,0,0,0.9), inset 0 -2px 0 rgba(0,0,0,0.18);
}

/* ---- the phone's two tab views ------------------------------------------ */
.lview { display: flex; flex-direction: column; flex: 1; min-height: 0;
         transition: opacity 0.45s var(--ease); }
.lview--hidden { display: none; }
.lnote { font-size: 0.6rem; color: var(--text-soft); margin: 0 0 0.5rem; line-height: 1.4; }
.lnote b { color: var(--cyan); }
.ldiv { font-size: 0.72rem; font-weight: 800; letter-spacing: 0.02em; margin: 0 0 0.35rem; }
.ldiv--one { color: var(--cyan); }
.ldiv--two { color: var(--orange); margin-top: 0.8rem; }
.ltable { width: 100%; border-collapse: collapse; background: #12121a;
          border: 1px solid #1e1e28; border-radius: 10px; overflow: hidden; }
.ltable th { font-size: 0.52rem; letter-spacing: 0.06em; text-transform: uppercase;
             color: #8f949b; font-weight: 700; padding: 0.4rem 0.4rem; text-align: left;
             border-bottom: 1px solid #1e1e28; line-height: 1.15; }
.ltable td { font-size: 0.78rem; color: #e9ecee; padding: 0.42rem 0.4rem;
             border-bottom: 1px solid rgba(255,255,255,0.045); }
.ltable tbody tr:last-child td { border-bottom: 0; }
.ltable td.n { font-family: var(--data); font-weight: 700; font-variant-numeric: tabular-nums;
               text-align: center; width: 27%; }
.ltable tbody tr:first-child td { background: rgba(0,232,218,0.07); }
.ltable tr.is-new td:first-child { color: var(--orange); }
.lrel { font-size: 0.58rem; color: var(--cyan); font-style: italic; margin: 0.45rem 0 0; }

/* ============================================================
   BACKGROUND — a flared grid behind the dark sections
   A fine line grid masked by a radial gradient, so it reads as structure
   catching the light in one corner rather than wallpaper. Carried over from
   the earlier homepage concept, tinted to our cyan.
   ============================================================ */
.hero, .sec--demo { position: relative; isolation: isolate; }
.hero > .wrap, .sec--demo > .wrap { position: relative; z-index: 1; }

.hero::after, .sec--demo::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 0;
  pointer-events: none;
  background-image:
    linear-gradient(rgba(0, 232, 218, 0.07) 1px, transparent 1px),
    linear-gradient(90deg, rgba(0, 232, 218, 0.07) 1px, transparent 1px);
  background-size: 64px 64px;
  -webkit-mask-image: radial-gradient(118% 88% at 78% 4%, #000 14%, transparent 68%);
  mask-image: radial-gradient(118% 88% at 78% 4%, #000 14%, transparent 68%);
}
/* the demo section flares from the other side, so the two do not look stamped */
.sec--demo::after {
  -webkit-mask-image: radial-gradient(100% 80% at 12% 10%, #000 12%, transparent 66%);
  mask-image: radial-gradient(100% 80% at 12% 10%, #000 12%, transparent 66%);
}

/* the colour wash sits under the grid */
.hero::before { z-index: -1; }
.sec--demo::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -1;
  pointer-events: none;
  background:
    radial-gradient(50% 40% at 8% 6%, rgba(0, 232, 218, 0.10), transparent 64%),
    radial-gradient(44% 38% at 92% 92%, rgba(255, 166, 77, 0.07), transparent 66%);
}

/* Grid was too faint to register. Stronger lines, and the mask pulled wider
   so the flare covers more of the section before it dissolves. */
.hero::after, .sec--demo::after {
  background-image:
    linear-gradient(rgba(0, 232, 218, 0.13) 1px, transparent 1px),
    linear-gradient(90deg, rgba(0, 232, 218, 0.13) 1px, transparent 1px);
  background-size: 72px 72px;
}
.hero::after {
  -webkit-mask-image: radial-gradient(125% 100% at 72% 0%, #000 22%, transparent 76%);
  mask-image: radial-gradient(125% 100% at 72% 0%, #000 22%, transparent 76%);
}
.sec--demo::after {
  -webkit-mask-image: radial-gradient(115% 95% at 14% 6%, #000 18%, transparent 74%);
  mask-image: radial-gradient(115% 95% at 14% 6%, #000 18%, transparent 74%);
}

/* ---- free trial: an offer, not a footnote -------------------------------
   Was a 74px image beside muted grey text, which read as small print. */
.hero-badge {
  display: inline-flex;
  align-items: center;
  gap: 0.95rem;
  margin-top: var(--sp-4);
  padding: 0.8rem 1.25rem 0.8rem 0.9rem;
  background: linear-gradient(180deg, rgba(0,232,218,0.12), rgba(0,232,218,0.04));
  border: 1px solid rgba(0,232,218,0.3);
  border-radius: 14px;
  box-shadow: 0 18px 40px -28px rgba(0,232,218,0.7);
}
.hero-badge img {
  width: 104px;
  height: auto;
  flex: none;
  filter: drop-shadow(0 4px 10px rgba(0,0,0,0.5));
}
.hero-badge p {
  margin: 0;
  font-size: var(--step-0);
  line-height: 1.4;
  color: #eaf4f3;
}
.hero-badge strong {
  color: var(--cyan);
  font-weight: 780;
  font-size: 1.1em;
  letter-spacing: -0.01em;
}
.hero-badge span { color: var(--text-mute); font-size: var(--step--1); }
@media (max-width: 420px) {
  .hero-badge { gap: 0.7rem; padding: 0.7rem 0.9rem; }
  .hero-badge img { width: 78px; }
}

/* Pages whose content is a panel rather than prose — the reading measure is
   the wrong constraint there, the board should use the room it has. */
.prose--wide .wrap { max-width: var(--wrap); }
.prose--wide .prose-head { max-width: 50rem; }
.prose--wide .embed-frame { margin-top: var(--sp-4); }
.prose--wide .embed-frame iframe { height: 900px; }
@media (max-width: 700px) { .prose--wide .embed-frame iframe { height: 680px; } }

/* ============================================================
   ATMOSPHERE — drifting particles over a faint grid, site-wide
   Same field the league pages use, so the site and the product share an
   atmosphere. Both layers are decoration and sit behind everything.
   ============================================================ */
body {
  background-color: var(--bg);
  background-image:
    linear-gradient(rgba(0, 232, 218, 0.035) 1px, transparent 1px),
    linear-gradient(90deg, rgba(0, 232, 218, 0.035) 1px, transparent 1px);
  background-size: 72px 72px;
  background-attachment: fixed;
}
#wpl-particles {
  position: fixed;
  inset: 0;
  width: 100%;
  height: 100%;
  z-index: 0;
  pointer-events: none;
}
/* content rides above the field; sections keep their own backgrounds opaque
   enough to read against it */
.site-head, main, .site-foot { position: relative; z-index: 1; }
.sec--alt, .proof { background: rgba(32, 32, 34, 0.82); }
.sec--demo { background: rgba(32, 32, 34, 0.72); }

/* ============================================================
   RULES — the app's glass cards
   ============================================================ */
.prose--cards .wrap { max-width: 58rem; }
.rcard {
  background: rgba(16, 16, 36, 0.7);
  backdrop-filter: blur(16px);
  -webkit-backdrop-filter: blur(16px);
  border: 1px solid rgba(255, 255, 255, 0.08);
  border-radius: 14px;
  box-shadow: 0 6px 32px rgba(0, 0, 0, 0.45);
  padding: 1.4rem 1.5rem;
  margin-bottom: 1.1rem;
}
.rcard h2 {
  display: flex;
  align-items: center;
  gap: 0.55rem;
  font-size: var(--step-1);
  color: var(--cyan);
  margin: 0 0 0.7rem;
  padding: 0;
  border: 0;
  letter-spacing: -0.015em;
}
.rcard h2 span { font-size: 1.05em; line-height: 1; }
.rcard h3 {
  font-size: calc(var(--step-0) * 1.02);
  color: var(--orange);
  margin: 1.3rem 0 0.45rem;
}
.rcard p { color: var(--text); margin-bottom: 0.8rem; line-height: 1.6; }
.rcard p:last-child { margin-bottom: 0; }
.rcard b { color: var(--cyan); font-weight: 700; }
.rcard .rnote { color: var(--text-mute); font-size: var(--step--1); line-height: 1.55; }
.rcard .rnote b { color: var(--text-soft); }
.rlist, .rrank { margin: 0 0 0.8rem 1.1rem; padding: 0; color: var(--text); }
.rlist li, .rrank li { margin-bottom: 0.4rem; line-height: 1.55; }
.rrank li b { color: var(--cyan); }

/* the 3-7 selector, shown as a range rather than one league's setting */
.rseg { display: flex; gap: 8px; margin: 0.9rem 0 1rem; }
.rseg div {
  flex: 1;
  text-align: center;
  padding: 0.6rem 0.3rem;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: 9px;
}
.rseg em { display: block; font-style: normal; font-size: 1.3em; font-weight: 800; color: var(--text); }
.rseg span { display: block; font-size: 0.7rem; color: var(--text-mute); line-height: 1.25; margin-top: 0.15rem; }
@media (max-width: 560px) { .rseg { gap: 5px; } .rseg div { padding: 0.5rem 0.15rem; } .rseg span { font-size: 0.6rem; } }

/* ============================================================
   ATMOSPHERE, turned up — stronger grid, stronger colour flares
   ============================================================ */
body {
  background-image:
    linear-gradient(rgba(0, 232, 218, 0.085) 1px, transparent 1px),
    linear-gradient(90deg, rgba(0, 232, 218, 0.085) 1px, transparent 1px);
  background-size: 76px 76px;
}

/* A fixed flare layer behind everything, so the colour follows you down the
   page rather than only living in the hero. */
body::before {
  content: "";
  position: fixed;
  inset: 0;
  z-index: -1;
  pointer-events: none;
  background:
    radial-gradient(58% 46% at 82% -4%, rgba(0, 232, 218, 0.22), transparent 62%),
    radial-gradient(48% 42% at 2% 36%, rgba(255, 166, 77, 0.14), transparent 64%),
    radial-gradient(52% 44% at 96% 82%, rgba(139, 124, 246, 0.12), transparent 66%);
}

/* Section flares get stronger to match. */
.hero::before {
  background:
    radial-gradient(60% 48% at 78% 2%, rgba(0, 232, 218, 0.24), transparent 62%),
    radial-gradient(48% 42% at 4% 86%, rgba(255, 166, 77, 0.16), transparent 64%);
}
.sec--demo::before {
  background:
    radial-gradient(54% 44% at 10% 2%, rgba(0, 232, 218, 0.2), transparent 64%),
    radial-gradient(48% 42% at 94% 94%, rgba(255, 166, 77, 0.13), transparent 66%);
}
.hero::after, .sec--demo::after {
  background-image:
    linear-gradient(rgba(0, 232, 218, 0.2) 1px, transparent 1px),
    linear-gradient(90deg, rgba(0, 232, 218, 0.2) 1px, transparent 1px);
  background-size: 76px 76px;
}

/* Let the field show through the panelled sections instead of sealing it off. */
.sec--alt, .proof { background: rgba(32, 32, 34, 0.55); }
.sec--demo { background: rgba(32, 32, 34, 0.42); }
.embed-frame { background: rgba(20, 20, 26, 0.55); }

/* One grid, not two.
   The body grid starts at the top of the page; .hero::after and
   .sec--demo::after each started a second copy at the top of their own
   section, so on the homepage two identical grids sat about a nav-height
   apart and beat against each other. The section grids go; the body grid
   covers the whole page on its own, and the sections keep their colour
   flares (::before), which is what was doing the real work anyway.
   Strength pulled back from 0.085 — it only needed to be legible. */
.hero::after, .sec--demo::after { display: none; }

body {
  background-image:
    linear-gradient(rgba(0, 232, 218, 0.05) 1px, transparent 1px),
    linear-gradient(90deg, rgba(0, 232, 218, 0.05) 1px, transparent 1px);
  background-size: 72px 72px;
}

/* ============================================================
   WIDE PAGE SHELL — leagues and rules match the message board
   Same outer width and the same heading measure, so the three
   content pages share one silhouette. The content goes into
   columns rather than stretching: a 1280px-wide paragraph is
   unreadable, a 1280px-wide pair of columns is not.
   ============================================================ */

/* .prose--cards came later in the file and was winning on 58rem. */
.prose--cards.prose--wide .wrap { max-width: var(--wrap); }

/* Rules: two columns of cards, with the intro and the closing line
   spanning the full width so the page still reads top to bottom. */
@media (min-width: 900px) {
  /* Balanced columns rather than a grid: the cards are very different
     lengths, and a grid row sized to the taller one left a block of dead
     space under the shorter. Columns let each card hug its own content. */
  .prose--cards.prose--wide .wrap {
    columns: 2;
    column-gap: var(--sp-3);
  }
  .prose--cards.prose--wide .prose-head,
  .prose--cards.prose--wide .wrap > p { column-span: all; }
  .prose--cards.prose--wide .rcard {
    break-inside: avoid;
    margin: 0 0 var(--sp-3);
  }
}

/* Live leagues: three across once there is room, instead of two
   stretched cards. */
@media (min-width: 980px) {
  .prose--wide .league-grid { grid-template-columns: repeat(3, minmax(0, 1fr)); }
}

/* ============================================================
   LIVE LEAGUES — cards beside the network records
   Two equal halves at the same column width as the Rules cards, so the
   three content pages share one silhouette. League cards go two-up in
   the left half; the Best Average and Most Perfect Games widgets stack
   in the right. Both are the app's own /embed routes, so the figures
   are always the live ones and can never drift from the product.
   ============================================================ */
.league-cols { display: block; }
.league-rail { display: grid; gap: var(--sp-3); margin-top: var(--sp-4); }
.rail-card {
  border: 1px solid var(--line);
  border-radius: 12px;
  overflow: hidden;
  background: rgba(20, 20, 26, 0.55);
}
.rail-card iframe { display: block; width: 100%; height: 542px; border: 0; }

@media (min-width: 1040px) {
  .league-cols {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: var(--sp-3);
    align-items: start;
  }
  .league-rail { margin-top: 0; }
  /* two narrow cards per half, matching the Rules rhythm */
  .prose--wide .league-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  /* the card's single line of stats will not fit at this width */
  .league-card .meta { white-space: normal; line-height: 1.5; }
}

/* Side by side once there is room but before the halves kick in, so a
   tablet does not get one very tall stacked pair. */
@media (min-width: 700px) and (max-width: 1039px) {
  .league-rail { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
/* At half-width the stats line was wrapping "SCORES" onto its own row. */
@media (min-width: 1040px) {
  .prose--wide .league-card .meta { letter-spacing: 0.035em; font-size: 0.71rem; }
}
/* ...and once it is that narrow, the channel reads better on its own line
   than wrapping mid-phrase. */
@media (min-width: 1040px) {
  .prose--wide .league-card .chan { display: block; }
  .prose--wide .league-card .dot { display: none; }
}

/* ============================================================
   STAR PANELS — the message board's look, on our own pages
   The app's embeds sit on #06060e with two faint radial washes and the
   drifting particle field over the top. Our pages already run that field
   site-wide, so a panel only has to be dark and translucent enough to
   let it through. The old .rcard had backdrop-filter: blur(16px), which
   blurred the stars behind it into nothing — that is why the cards read
   as flat slabs. Dropped.
   ============================================================ */
.rcard, .starpanel {
  position: relative;
  isolation: isolate;
  background: rgba(6, 6, 14, 0.58);
  backdrop-filter: none;
  -webkit-backdrop-filter: none;
  border: 1px solid rgba(255, 255, 255, 0.08);
  border-radius: 14px;
  box-shadow: 0 6px 32px rgba(0, 0, 0, 0.45);
}
/* the two washes the embeds use: sky blue in, violet out */
.rcard::before, .starpanel::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -1;
  border-radius: inherit;
  pointer-events: none;
  background:
    radial-gradient(ellipse 60% 40% at 10% -10%, rgba(56, 189, 248, 0.10), transparent 70%),
    radial-gradient(ellipse 60% 40% at 90% 110%, rgba(168, 85, 247, 0.10), transparent 70%);
}

.starpanel {
  padding: clamp(1.4rem, 1rem + 2vw, 2.4rem);
  margin-top: var(--sp-4);
}
.starpanel > :first-child { margin-top: 0; }
.starpanel > :last-child  { margin-bottom: 0; }
/* the keyline rules read as double borders inside a panel */
.starpanel .keyline { opacity: 0.5; }

/* Leagues: the app's directory embed is the full left half. Fixed height so
   the two halves end together; it scrolls itself once the list outgrows it. */
.rail-card--dir iframe { height: 1126px; }
@media (max-width: 1039px) { .rail-card--dir iframe { height: 760px; } }

/* Panels carry their own field (starfield.js), so they can be solid again —
   same #06060e as the embeds, with the stars over the top rather than dimly
   behind. Translucency was letting the page grid through as well. */
.rcard, .starpanel {
  background-color: #06060e;
  background-image:
    radial-gradient(ellipse 60% 40% at 10% -10%, rgba(56, 189, 248, 0.07), transparent 70%),
    radial-gradient(ellipse 60% 40% at 90% 110%, rgba(168, 85, 247, 0.07), transparent 70%);
  overflow: hidden;   /* keep the canvas inside the rounded corners */
}
.rcard::before, .starpanel::before { display: none; }
.panel-stars {
  position: absolute;
  inset: 0;
  z-index: 0;
  pointer-events: none;
}
.rcard > *:not(.panel-stars),
.starpanel > *:not(.panel-stars) { position: relative; z-index: 1; }

/* One reading width for the three content pages. Rules already sat at 58rem
   and reads well; App for Slack was narrower and the message board was the
   full 1280, which left a wide black band either side of the board's own
   700px posts. */
.prose--read .wrap { max-width: 58rem; }
.prose--read .prose-head { max-width: 50rem; }
.prose--read .embed-frame { margin-top: var(--sp-4); }
.prose--read .embed-frame iframe { height: 900px; }

/* The default minimum. The app highlights whatever the league is set to;
   here there is no league, so it marks the value a new one starts on. */
.rseg .is-on {
  border-color: rgba(0, 232, 218, 0.5);
  background: rgba(0, 232, 218, 0.08);
}
.rseg .is-on em { color: var(--cyan); }
.rseg .is-on span { color: var(--text-soft); }

/* Leagues directory: tall enough for the whole list, so the panel never
   grows its own scrollbar. The embed paginates at 20, and 20 cards need
   about 1950px — bump this if the directory outgrows it. */
.rail-card--dir iframe { height: 1160px; }
@media (max-width: 1039px) { .rail-card--dir iframe { height: 1160px; } }
/* The board is two posts long right now and a fixed 900px frame left most of
   it empty. Sized to the content with room for a few more; like the leagues
   directory this is a fixed number and needs raising as the board grows. */
.prose--read .embed-frame iframe { height: 620px; }

/* ============================================================
   LIVE LEAGUES — built here, not iframed
   The app's embeds are sized for a fixed frame, which meant either an
   inner scrollbar inside the page scroll or a block of dead space.
   Rendering the same data ourselves fixes both, and lets the records
   lead on a phone where a nine-card list is a poor opener.
   ============================================================ */
.vh {
  position: absolute; width: 1px; height: 1px; overflow: hidden;
  clip: rect(0 0 0 0); clip-path: inset(50%); white-space: nowrap;
}

.league-cols { display: flex; flex-direction: column; gap: var(--sp-4); }
.rail { display: grid; gap: var(--sp-3); align-content: start; }

@media (min-width: 1040px) {
  .league-cols {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: var(--sp-3);
    align-items: start;
  }
  /* list leads on desktop, records lead on a phone */
  .league-main { order: 1; }
  .rail { order: 2; }
}

/* ---- league cards ---- */
.lg-grid { display: grid; gap: 0.9rem; }
@media (min-width: 560px) { .lg-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); } }

.lg {
  display: flex; flex-direction: column; gap: 0.4rem;
  padding: 1.05rem 1.15rem;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: 11px;
  text-decoration: none; color: var(--text);
  transition: transform 0.16s ease, border-color 0.16s ease, background 0.16s ease;
}
.lg:hover {
  transform: translateY(-2px);
  border-color: rgba(0, 232, 218, 0.45);
  background: rgba(0, 232, 218, 0.03);
  color: var(--text);
}
.lg-top { display: flex; align-items: center; gap: 0.5rem; flex-wrap: wrap; }
.lg-emoji { font-size: 1.25rem; line-height: 1; }
.lg-name { font-size: var(--step-1); font-weight: 700; letter-spacing: -0.015em; }
.lg-badge {
  font-family: var(--data); font-size: 0.64rem; letter-spacing: 0.06em;
  text-transform: uppercase; font-weight: 700;
  color: var(--orange); background: rgba(255, 166, 77, 0.14);
  border: 1px solid rgba(255, 166, 77, 0.3);
  padding: 0.15rem 0.45rem; border-radius: 5px;
}
.lg-meta {
  display: flex; flex-wrap: wrap; gap: 0.3rem 0.55rem;
  font-family: var(--data); font-size: 0.7rem; letter-spacing: 0.035em;
  text-transform: uppercase; color: var(--text-mute);
}
.lg-meta > span + span::before { content: "·"; margin-right: 0.55rem; }
.lg-chan { color: var(--text-soft); }
.lg-chan::before {
  content: ""; display: inline-block; width: 7px; height: 7px;
  border-radius: 50%; margin-right: 0.4rem; vertical-align: 0.05em;
}
.lg-chan--sms::before     { background: var(--cyan); }
.lg-chan--slack::before   { background: #a855f7; }
.lg-chan--discord::before { background: #5865f2; }
.lg-go { font-family: var(--data); font-size: var(--step--1); color: var(--cyan); margin-top: 0.25rem; }

/* ---- leaderboards ---- */
.bd {
  background: rgba(16, 16, 36, 0.62);
  border: 1px solid rgba(255, 255, 255, 0.08);
  border-radius: 14px;
  padding: 1.2rem 1.1rem 1rem;
  box-shadow: 0 6px 32px rgba(0, 0, 0, 0.45);
}
.bd h3 {
  margin: 0; text-align: center; font-size: var(--step-1);
  letter-spacing: -0.015em; border: 0; padding: 0;
}
.bd-sub {
  margin: 0.15rem 0 0.9rem; text-align: center;
  font-size: 0.76rem; color: var(--text-mute);
}
.bd table { width: 100%; border-collapse: collapse; font-variant-numeric: tabular-nums; }
.bd th {
  padding: 0.5rem; font-family: var(--data); font-size: 0.66rem;
  letter-spacing: 0.06em; text-transform: uppercase; color: var(--text-mute);
  font-weight: 600; text-align: left;
  border-bottom: 1px solid rgba(255, 255, 255, 0.12);
}
.bd td { padding: 0.7rem 0.5rem; }
.bd tr + tr td { border-top: 1px solid rgba(255, 255, 255, 0.05); }
.bd .bd-pos { text-align: center; width: 44px; }
.bd .bd-num { text-align: right; white-space: nowrap; color: var(--text-soft); }
.bd-medal { font-size: 1.15em; }
.bd-rank { color: var(--text-mute); font-weight: 700; }
.bd-who { display: block; font-weight: 700; color: var(--text); }
.bd-lg  { display: block; font-size: 0.76em; color: var(--text-mute); }
.bd td.is-cyan   { font-weight: 800; color: var(--cyan); }
.bd td.is-orange { font-weight: 800; color: var(--orange); }

/* page two is in the markup, hidden — the pager is a class toggle, no data */
.bd tr.p2 { display: none; }
.bd.on-2 tr.p2 { display: table-row; }
.bd.on-2 tbody tr:not(.p2) { display: none; }

.bd-pager { display: flex; align-items: center; justify-content: center; gap: 0.85rem; margin-top: 0.9rem; }
.bd-pager button {
  width: 34px; height: 34px; border-radius: 9px; cursor: pointer;
  display: flex; align-items: center; justify-content: center;
  font-size: 1.1rem; line-height: 1;
  background: rgba(255, 255, 255, 0.06);
  border: 1px solid rgba(255, 255, 255, 0.14);
  color: var(--text-soft);
  transition: background 0.15s, border-color 0.15s, opacity 0.15s;
}
.bd-pager button:hover:not(:disabled) {
  background: rgba(0, 232, 218, 0.12); border-color: rgba(0, 232, 218, 0.4); color: var(--cyan);
}
.bd-pager button:disabled { opacity: 0.3; cursor: default; }
.bd-pager .bd-pg { font-family: var(--data); font-size: 0.76rem; color: var(--text-mute); min-width: 48px; text-align: center; }

/* ============================================================
   MOBILE NAV — five links behind a toggle
   Only collapses when nav.js has run (html.js-nav). Without it the
   links stay visible and wrap, which is ugly but works; a toggle with
   nothing behind it does not.
   ============================================================ */
.nav-toggle { display: none; }

@media (max-width: 859px) {
  .js-nav .nav-toggle {
    display: flex; align-items: center; justify-content: center;
    width: 42px; height: 38px; padding: 0;
    margin-left: auto; margin-right: 0.6rem;
    background: transparent;
    border: 1px solid var(--line);
    border-radius: 9px;
    color: var(--text);
    cursor: pointer;
    transition: border-color 0.15s ease, background 0.15s ease;
  }
  .js-nav .nav-toggle:hover { border-color: rgba(0, 232, 218, 0.45); }
  .js-nav .nav-toggle:focus-visible { outline: 2px solid var(--cyan); outline-offset: 2px; }

  /* three bars, becoming a cross when open */
  .nav-bars, .nav-bars::before, .nav-bars::after {
    display: block; width: 18px; height: 2px; border-radius: 2px;
    background: currentColor;
    transition: transform 0.22s var(--ease), opacity 0.15s linear;
  }
  .nav-bars { position: relative; }
  .nav-bars::before, .nav-bars::after { content: ""; position: absolute; left: 0; }
  .nav-bars::before { top: -6px; }
  .nav-bars::after  { top: 6px; }
  .nav-open .nav-bars { background: transparent; }
  .nav-open .nav-bars::before { transform: translateY(6px) rotate(45deg); }
  .nav-open .nav-bars::after  { transform: translateY(-6px) rotate(-45deg); }

  .js-nav .site-head .wrap { flex-wrap: nowrap; }

  /* An overlay rather than a collapsing row: the panel is taken out of
     flow, so it can animate opacity and position without the header
     resizing, and a 0fr grid would not have collapsed anyway with five
     separate children in it. */
  .js-nav .nav {
    position: absolute;
    top: 100%; left: 0; right: 0;
    display: block;
    background: #0b0b12;   /* opaque: the header's backdrop blur does not extend to it */
    border-bottom: 1px solid var(--line);
    box-shadow: 0 18px 40px rgba(0, 0, 0, 0.5);
    opacity: 0;
    visibility: hidden;
    transform: translateY(-8px);
    pointer-events: none;
    transition: opacity 0.2s var(--ease), transform 0.2s var(--ease),
                visibility 0s linear 0.2s;
  }
  .js-nav .site-head.nav-open .nav {
    opacity: 1;
    visibility: visible;
    transform: none;
    pointer-events: auto;
    transition-delay: 0s, 0s, 0s;
  }

  .js-nav .nav a {
    display: block;
    padding: 0.9rem clamp(1.1rem, 4vw, 1.6rem);
    font-size: 1rem;
    border-top: 1px solid rgba(255, 255, 255, 0.06);
  }
  .js-nav .nav a:first-child { border-top: 0; }
  .js-nav .nav a[aria-current="page"] { color: var(--cyan); background: rgba(0, 232, 218, 0.06); }

  /* the panel hangs below the header, so the header has to outrank main
     (both were z-index 1, and main comes later in the document) */
  .js-nav .site-head { position: relative; z-index: 60; }
}

@media (prefers-reduced-motion: reduce) {
  .js-nav .nav { transition: none; }
  .nav-bars, .nav-bars::before, .nav-bars::after { transition: none; }
}

/* the emoji and a long league name should not split across lines */
.lg-name { flex: 1 1 auto; min-width: 0; }

/* ---- AI message examples ----------------------------------------------
   The app's rules page shows each message type with a sample, which lands
   the feature far better than a bullet list. Its version also stamps every
   row "ON", because it is rendering one league's settings — here they are
   off until a manager turns them on, so no state is shown. */
.aimsg { display: grid; gap: 0.9rem; margin: 1rem 0 1.2rem; }
.aimsg-row {
  display: grid;
  grid-template-columns: 2rem minmax(0, 1fr);
  gap: 0.2rem 0.75rem;
  padding: 0.9rem 1rem;
  background: rgba(255, 255, 255, 0.03);
  border: 1px solid rgba(255, 255, 255, 0.07);
  border-radius: 10px;
}
.aimsg-ico { font-size: 1.3rem; line-height: 1.3; }
.aimsg-row h3 {
  margin: 0; padding: 0; border: 0;
  font-size: 1rem; font-weight: 700; color: var(--text);
}
.aimsg-row p { margin: 0.15rem 0 0; font-size: var(--step--1); color: var(--text-soft); }
.aimsg-eg {
  margin-top: 0.55rem !important;
  padding-left: 0.8rem;
  border-left: 2px solid rgba(0, 232, 218, 0.4);
  color: var(--text-mute) !important;
  font-style: italic;
  line-height: 1.6;
}
@media (max-width: 480px) {
  .aimsg-row { grid-template-columns: 1fr; }
  .aimsg-ico { font-size: 1.15rem; }
}

/* the tone row is the same control as the 3-7 selector, with words */
.rseg--tone em { font-size: 0.95em; }

/* ============================================================
   PHOTO HERO — the image rolls up behind the page as you scroll
   The photo is pinned while the hero's own height scrolls past it,
   fading and drifting as it goes, so the content appears to roll up
   over it rather than push it off screen.
   Done with position:sticky and a scroll-linked opacity rather than
   background-attachment:fixed, which iOS Safari silently ignores.
   ============================================================ */
.photohero {
  position: relative;
  margin-bottom: var(--sp-4);
  padding-block: clamp(3rem, 2rem + 5vw, 6rem) clamp(2rem, 1.5rem + 3vw, 4rem);
  min-height: clamp(380px, 46vw, 560px);
  display: flex;
  align-items: center;
  overflow: clip;        /* clip, not hidden: does not create a scroll container */
}
.photohero-img {
  position: absolute;
  inset: 0 0 0 auto;
  width: min(54%, 680px);
  pointer-events: none;
  z-index: 0;
}
.photohero-img img {
  width: 100%; height: 100%;
  object-fit: cover; object-position: 50% 22%;
  display: block;
  /* the text column sits over the left of the image, so it fades out there
     as well as at the top and bottom */
  -webkit-mask-image:
    linear-gradient(to right, transparent, #000 38%),
    linear-gradient(to bottom, transparent, #000 14%, #000 72%, transparent);
          mask-image:
    linear-gradient(to right, transparent, #000 38%),
    linear-gradient(to bottom, transparent, #000 14%, #000 72%, transparent);
  -webkit-mask-composite: source-in;
          mask-composite: intersect;
  opacity: 0.82;
}
.photohero-body { position: relative; z-index: 1; max-width: 100%; }
.photohero-body .updated { max-width: 44ch; }
.photohero .eyebrow { color: var(--cyan); background: var(--cyan-dim); }
.photohero h1 { letter-spacing: -0.035em; max-width: 16ch; }

/* the roll-up itself, applied by photohero.js */
.js-motion .photohero-img {
  will-change: transform, opacity;
  transform: translate3d(0, calc(var(--ph, 0) * -28%), 0) scale(calc(1 + var(--ph, 0) * 0.06));
  opacity: calc(0.82 - var(--ph, 0) * 0.82);
}

@media (max-width: 760px) {
  .photohero-img { width: 100%; }
  .photohero-img img {
    object-position: 62% 18%;
    -webkit-mask-image: linear-gradient(to bottom, transparent, #000 10%, rgba(0,0,0,0.35) 70%, transparent);
            mask-image: linear-gradient(to bottom, transparent, #000 10%, rgba(0,0,0,0.35) 70%, transparent);
    -webkit-mask-composite: source-over;
            mask-composite: add;
    opacity: 0.4;
  }
  .photohero h1 { max-width: none; }
}

@media (prefers-reduced-motion: reduce) {
  .js-motion .photohero-img { transform: none; opacity: 0.82; }
}

/* ---- how it works: the three channels ---- */
.chan { display: grid; gap: 1rem; margin: 1.1rem 0 0.4rem; }
@media (min-width: 620px) { .chan { grid-template-columns: repeat(3, minmax(0, 1fr)); } }
.chan > div {
  padding: 0.9rem 1rem;
  background: rgba(255, 255, 255, 0.03);
  border: 1px solid rgba(255, 255, 255, 0.07);
  border-radius: 10px;
}
.chan h3 {
  display: flex; align-items: center; gap: 0.45rem;
  margin: 0 0 0.35rem; padding: 0; border: 0;
  font-size: 0.95rem; font-weight: 700; color: var(--text);
}
.chan p { margin: 0; font-size: var(--step--1); color: var(--text-soft); line-height: 1.6; }
.dot { width: 8px; height: 8px; border-radius: 50%; flex: none; }
.dot--sms { background: var(--cyan); }
.dot--slack { background: #a855f7; }
.dot--discord { background: #5865f2; }

/* numbered step headings read better a touch larger than an emoji */
.rcard h2 span { font-variant-numeric: normal; }

/* ---- pricing tables ---- */
.tiers {
  width: 100%; border-collapse: collapse; margin: 1rem 0 0.9rem;
  font-variant-numeric: tabular-nums;
}
.tiers th {
  text-align: left; padding: 0.5rem 0.6rem;
  font-family: var(--data); font-size: 0.66rem; font-weight: 600;
  letter-spacing: 0.06em; text-transform: uppercase; color: var(--text-mute);
  border-bottom: 1px solid rgba(255, 255, 255, 0.12);
}
.tiers td { padding: 0.62rem 0.6rem; color: var(--text); }
.tiers tr + tr td { border-top: 1px solid rgba(255, 255, 255, 0.05); }
.tiers .num { text-align: right; white-space: nowrap; }
.tiers tbody tr:hover td { background: rgba(255, 255, 255, 0.025); }
/* Price is the number that matters, so it is the only cyan one, and it is
   keyed on a class rather than a column index - the price sits in a different
   column in each of the two tables. Orange is reserved for the AI tag, since
   an orange price next to a cyan one reads as a warning. */
/* .tprice, not .price - there is already a .price card component from the
   old homepage pricing block, and td.price was picking up its surface,
   border, radius and padding. */
.tiers td.tprice { font-weight: 800; color: var(--cyan); }
.tiers .tier-no { color: var(--text-mute); font-size: var(--step--1); }
.tier-tag {
  font-family: var(--data); font-size: 0.6rem; font-weight: 700;
  letter-spacing: 0.06em; text-transform: uppercase;
  color: var(--orange); background: rgba(255, 166, 77, 0.14);
  border: 1px solid rgba(255, 166, 77, 0.3);
  padding: 0.1rem 0.35rem; border-radius: 4px; margin-left: 0.35rem;
}

/* ---- footer stat tiles -------------------------------------------------
   The counters moved out of a homepage section and into the footer, so they
   land on every page and the homepage loses 460px it did not need. Same
   flip-and-count-up as before; motion.js reads the targets from the markup,
   so the finished numbers are there without JS. */
.foot-stats {
  margin: 0 0 var(--sp-4);
  padding: 0 0 var(--sp-4);
  border-bottom: 1px solid var(--line-soft);
}
@media (max-width: 720px) {
  .foot-stats { gap: 1.2rem 1.6rem; }
  .foot-stats .stat-label { font-size: 0.64rem; }
}

/* ============================================================
   FULL-WIDTH PRICING — varied rhythm instead of one centred column
   Narrow intro, then a wide band for the tables (they are data and
   want the room), then an asymmetric split, then a three-up, then a
   two-up. The paragraphs still cap at a readable measure; it is the
   canvas that goes wide, not the prose.
   ============================================================ */

/* at a glance */
.atglance {
  list-style: none; margin: 0; padding: 0;
  display: grid; gap: 1px;
  grid-template-columns: repeat(2, 1fr);
  background: var(--line-soft);
  border: 1px solid var(--line-soft);
  border-radius: 12px;
  overflow: hidden;
}
@media (min-width: 760px) { .atglance { grid-template-columns: repeat(4, 1fr); } }
.atglance li {
  background: var(--bg);
  padding: 1.5rem 1.3rem;
  display: flex; flex-direction: column; gap: 0.35rem;
}
.ag-n {
  font-size: var(--step-3); font-weight: 780; letter-spacing: -0.035em;
  color: var(--cyan); line-height: 1; font-variant-numeric: tabular-nums;
}
.ag-k {
  font-family: var(--data); font-size: 0.7rem; letter-spacing: 0.09em;
  text-transform: uppercase; color: var(--text-mute); line-height: 1.5;
}

/* the two plan panels, side by side across the band */
.plans { display: grid; gap: var(--sp-3); align-items: start; }
@media (min-width: 900px) { .plans { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
.plan {
  position: relative;
  background: rgba(16, 16, 36, 0.62);
  border: 1px solid rgba(255, 255, 255, 0.08);
  border-radius: 14px;
  padding: clamp(1.3rem, 1rem + 1vw, 2rem);
  box-shadow: 0 6px 32px rgba(0, 0, 0, 0.35);
}
.plan--featured { border-color: rgba(0, 232, 218, 0.35); }
.plan-flag {
  position: absolute; top: -0.7rem; right: 1.2rem;
  font-family: var(--data); font-size: 0.64rem; font-weight: 700;
  letter-spacing: 0.08em; text-transform: uppercase;
  color: #000; background: var(--cyan);
  padding: 0.25rem 0.6rem; border-radius: 999px;
}
.plan-chan {
  display: inline-flex; align-items: center; gap: 0.4rem;
  font-family: var(--data); font-size: 0.72rem; letter-spacing: 0.12em;
  text-transform: uppercase; color: var(--text-soft);
}
.plan h3 {
  margin: 0.6rem 0 0; padding: 0; border: 0;
  font-size: var(--step-3); font-weight: 780; letter-spacing: -0.035em;
  line-height: 1; color: var(--text);
}
.plan h3 small {
  display: block; margin-top: 0.4rem;
  font-size: var(--step--1); font-weight: 400; letter-spacing: 0;
  color: var(--text-mute);
}
.plan header p { margin: 0.8rem 0 0; color: var(--text-soft); font-size: var(--step--1); line-height: 1.6; max-width: 46ch; }
.plan .tiers { margin-top: 1.2rem; }
.plan-foot { margin: 0.9rem 0 0; font-size: var(--step--1); color: var(--text-mute); line-height: 1.6; }

/* asymmetric split with the heading pinned while the prose scrolls */
.split--sticky { align-items: start; }
@media (min-width: 880px) {
  .split--sticky { grid-template-columns: minmax(0, 0.8fr) minmax(0, 1.2fr); }
  .split--sticky .split-side { position: sticky; top: calc(62px + var(--sp-4)); }
}
.split-side h2 { margin: 0; }
.split-main .lede + .lede { margin-top: 1rem; }
.lede--note { color: var(--text-mute); font-size: var(--step--1); line-height: 1.7; }

/* three-up and two-up bands */
.free3, .qa { display: grid; gap: var(--sp-3); }
@media (min-width: 820px) { .free3 { grid-template-columns: repeat(3, minmax(0, 1fr)); } }
@media (min-width: 760px) { .qa { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
.free3 > div, .qa > div {
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: 12px;
  padding: 1.3rem 1.4rem;
}
.free3 h3, .qa h3 {
  margin: 0 0 0.5rem; padding: 0; border: 0;
  font-size: var(--step-1); letter-spacing: -0.015em;
  display: flex; align-items: center; gap: 0.5rem;
}
.free3 p, .qa p { margin: 0; color: var(--text-soft); font-size: var(--step--1); line-height: 1.65; }

/* ---- footer tile mark ---------------------------------------------------
   The league pages' loading tiles, reused as a signature. Same geometry and
   timing as html_generator_v2.py so the two surfaces match: 2s rotateX, 0.2s
   stagger, colour re-rolled on each iteration by tiles.js. Smaller here —
   36px is right for a full-screen overlay, not for a footer. */
.tilemark {
  display: flex; gap: 7px; justify-content: center;
  perspective: 600px;
  margin: 0 0 var(--sp-3);
}
.tilemark .lt {
  width: 24px; height: 24px; border-radius: 5px;
  background: #37374a;
  animation: ltFlip 2s ease-in-out infinite;
  animation-play-state: paused;
  transition: background-color 0.3s ease;
}
.tilemark.is-running .lt { animation-play-state: running; }
.tilemark .lt.t-cyan { background: var(--cyan); }
.tilemark .lt.t-orange { background: var(--orange); }
.tilemark .lt.t-dark { background: #37374a; }
@keyframes ltFlip { 0%, 100% { transform: rotateX(0); } 50% { transform: rotateX(180deg); } }

@media (prefers-reduced-motion: reduce) {
  .tilemark { display: none; }
}

/* ---- how it works: the numbered steps ----------------------------------
   The number is a rule down the left rather than a circle badge, so the
   three steps read as one sequence instead of three unrelated cards. */
.steps { list-style: none; margin: 0; padding: 0; display: grid; gap: var(--sp-5); }
.steps > li {
  position: relative;
  padding-left: clamp(3.2rem, 2rem + 4vw, 6rem);
}
/* The rule starts BELOW the number and runs into the next step, so it needs
   no opaque backing to punch a hole in — an opaque block over a starfield
   reads as a slab, which is what the first attempt did. */
.steps > li::before {
  content: "";
  position: absolute;
  left: clamp(1.15rem, 0.65rem + 1.6vw, 2.25rem);
  top: 2.1rem;
  bottom: calc(-1 * var(--sp-5));
  width: 1px;
  background: linear-gradient(to bottom, rgba(0,232,218,0.45), rgba(0,232,218,0.04));
}
.steps > li:last-child::before { display: none; }
.step-n {
  position: absolute; left: 0; top: 0.25rem;
  font-family: var(--data); font-size: 0.8rem; font-weight: 700;
  letter-spacing: 0.1em; color: var(--cyan);
  width: clamp(2.3rem, 1.3rem + 3.2vw, 4.5rem);
}
.steps h2 { margin: 0 0 0.7rem; font-size: var(--step-2); letter-spacing: -0.03em; }
.steps .lede + .lede { margin-top: 0.9rem; }
.steps .chan { margin-top: 1.4rem; }
@media (max-width: 560px) {
  .steps > li { padding-left: 0; padding-top: 2rem; }
  .steps > li::before { display: none; }
  .step-n { width: auto; }
}

/* ---- a screenshot beside prose ----------------------------------------
   The wide split: text capped at a readable measure on one side, the shot
   taking the rest. Lets the page use the full width without a 1280px line
   of body copy. */
@media (min-width: 900px) {
  .split--wide { grid-template-columns: minmax(0, 1fr) minmax(0, 1.05fr); align-items: center; }
  .split--wide > div > .lede { max-width: 48ch; }
}
.shotfig { margin: 0; }
.shotfig img {
  display: block; width: 100%; height: auto;
  border-radius: 14px;
  border: 1px solid rgba(255, 255, 255, 0.08);
  box-shadow: 0 24px 60px rgba(0, 0, 0, 0.55);
}
/* the angled shot already has perspective of its own; a slight counter-tilt
   and a cyan glow sit it into the page rather than on top of it */
.shotfig--tilt img {
  box-shadow: 0 24px 70px rgba(0, 0, 0, 0.6), 0 0 90px rgba(0, 232, 218, 0.07);
}
@media (prefers-reduced-motion: no-preference) {
  .shotfig img { transition: transform 0.5s var(--ease); }
  .shotfig.is-in img { transform: none; }
  .js-motion .shotfig:not(.is-in) img { transform: translateY(14px) scale(0.985); }
}
/* The homepage runs the three steps as a summary, so they sit side by side
   rather than stacked down a rule. */
@media (min-width: 820px) {
  .steps--tight { grid-template-columns: repeat(3, minmax(0, 1fr)); gap: var(--sp-3); }
  .steps--tight > li { padding-left: 0; padding-top: 2.1rem; }
  .steps--tight > li::before { display: none; }
}
.steps--tight h3 {
  margin: 0 0 0.5rem; padding: 0; border: 0;
  font-size: var(--step-1); letter-spacing: -0.02em;
}
.steps--tight p { margin: 0; color: var(--text-soft); font-size: var(--step--1); line-height: 1.65; }
.steps--tight .atglance { margin-top: 0; }

/* ---- centred photo hero -------------------------------------------------
   The copy sits in the middle and the photo fills the whole band behind it
   rather than hiding in the right corner. It was already faded there, so
   making it bigger costs nothing and gives the page a proper opening.
   Legibility comes from a scrim under the text, not from shrinking the
   image. */
.photohero--centre { text-align: center; min-height: clamp(500px, 54vw, 720px); }
/* Anchored bottom-right in a column of her own aspect ratio. object-fit:cover
   across the full band was scaling an 825x1100 portrait to 1440 wide, i.e.
   1925px tall, so all you saw was an enormous cropped face behind the
   headline. contain in a narrow column shows the whole figure, and she ends
   up far larger than the original corner crop. */
/* She is sized to the viewport rather than to the band, and the band stops
   clipping her, so she runs on past it and fades out over the section below.
   object-fit:contain scales to the shorter axis, so the only way to get her
   near half the page wide is to give her the height to match - 940px tall
   renders about 705px wide, which the 530px band could never do.
   z-index -1 keeps her behind the cards she overlaps; they are nearly
   transparent, so she still reads through them. */
.photohero--centre { overflow: visible; }
.photohero--centre .photohero-img {
  inset: 0 0 auto auto;
  width: min(54%, 780px);
  height: clamp(520px, 66vw, 960px);
  z-index: -1;
}
/* She sits off to the right of the frame rather than dead centre: a face
   directly behind a centred headline competes with it, and the first attempt
   read as a dark smudge. Pushed right, the copy lands on clean ground and
   she can be much larger than the old corner crop allowed. */
.photohero--centre .photohero-img img {
  object-fit: contain;
  object-position: top right;
  opacity: 0.88;
  -webkit-mask-image:
    linear-gradient(to right, transparent 2%, #000 40%),
    linear-gradient(to bottom, transparent, #000 9%, #000 56%, transparent 94%);
          mask-image:
    linear-gradient(to right, transparent 2%, #000 40%),
    linear-gradient(to bottom, transparent, #000 9%, #000 56%, transparent 94%);
  -webkit-mask-composite: source-in;
          mask-composite: intersect;
}
/* scrim under the copy only, so the right side keeps its contrast */
.photohero--centre::after {
  content: "";
  position: absolute; inset: 0;
  pointer-events: none; z-index: 0;
  background: radial-gradient(58% 62% at 36% 48%, rgba(10, 10, 16, 0.9), transparent 72%);
}
.photohero--centre .photohero-body { margin-inline: auto; }
.photohero--centre .photohero-body h1 { max-width: 18ch; margin-inline: auto; }
.photohero--centre .photohero-body .updated { max-width: 56ch; margin-inline: auto; }

@media (max-width: 760px) {
  .photohero--centre .photohero-img img { opacity: 0.32; object-position: 50% 12%; }
}

/* The centred hero was leaving 388px between the end of its copy and the
   first heading: ~145px of empty band below the centred text, a 34px margin
   and 128px of section padding stacked on top of each other. The band keeps
   enough height for the figure, but the copy sits above centre rather than
   in it, the margin goes, and the section that follows gets a much smaller
   top pad than a standalone one needs. */
.photohero--centre {
  min-height: clamp(400px, 36vw, 530px);
  align-items: center;
  padding-block: clamp(2.2rem, 1.4rem + 3vw, 4rem) clamp(0.8rem, 0.5rem + 1vw, 1.6rem);
  margin-bottom: 0;
}
.photohero + .sec { padding-top: clamp(1.4rem, 0.9rem + 1.8vw, 2.8rem); }

/* ============================================================
   FOOTER — labelled columns instead of a wrapping link soup
   The old .foot-links was one flex row of eight links. On a phone that
   wrapped into a ragged two-line block with no grouping, which read as
   clutter. Three named columns stack cleanly at any width.
   ============================================================ */
.foot-grid {
  display: grid;
  gap: var(--sp-4) var(--sp-5);
  grid-template-columns: 1fr;
  align-items: start;
  justify-content: initial;   /* was space-between on the old flex row */
}
@media (min-width: 760px) {
  .foot-grid { grid-template-columns: minmax(0, 1fr) minmax(0, 1.5fr); }
}

.foot-brand p { margin: 0.6rem 0 0; max-width: 34ch; line-height: 1.6; }
.foot-brand .tilemark { margin: var(--sp-3) 0 0; justify-content: flex-start; }

.foot-nav { display: grid; gap: var(--sp-4) var(--sp-3); grid-template-columns: repeat(2, minmax(0, 1fr)); }
@media (min-width: 560px) { .foot-nav { grid-template-columns: repeat(3, minmax(0, 1fr)); } }
.foot-nav > div { display: flex; flex-direction: column; gap: 0.55rem; align-items: flex-start; }
.foot-nav h2 {
  margin: 0 0 0.2rem; padding: 0; border: 0;
  font-family: var(--data); font-size: 0.66rem; font-weight: 700;
  letter-spacing: 0.14em; text-transform: uppercase; color: var(--text-mute);
}
.foot-nav a { color: var(--text-soft); text-decoration: none; line-height: 1.3; }
.foot-nav a:hover { color: var(--cyan); }

.foot-base {
  display: flex; flex-wrap: wrap; align-items: flex-start;
  gap: 1rem var(--sp-4); justify-content: space-between;
  margin-top: var(--sp-4);
  padding-top: 1.25rem;
  border-top: 1px solid var(--line-soft);
}
.foot-base .foot-note { margin: 0; padding: 0; border: 0; max-width: 62ch; }

.to-top {
  flex: none;
  display: inline-flex; align-items: center; gap: 0.4rem;
  font-family: var(--data); font-size: 0.72rem; font-weight: 700;
  letter-spacing: 0.1em; text-transform: uppercase;
  color: var(--text-soft); text-decoration: none;
  border: 1px solid var(--line);
  border-radius: 999px;
  padding: 0.45rem 0.9rem;
  transition: color 0.15s ease, border-color 0.15s ease, background 0.15s ease;
}
.to-top:hover { color: var(--cyan); border-color: rgba(0, 232, 218, 0.45); background: rgba(0, 232, 218, 0.06); }
.to-top span { transition: transform 0.18s var(--ease); }
.to-top:hover span { transform: translateY(-2px); }

/* Back to top is an anchor to the header, so it works with JS off. Smooth
   only when motion is welcome; the header is sticky so the landing point
   needs no extra offset. */
@media (prefers-reduced-motion: no-preference) {
  html { scroll-behavior: smooth; }
}
/* Two buttons in a paragraph were relying on an inline margin-left, which
   left them touching and the second indented once they wrapped on a phone. */
.btn-row, .hero-actions {
  display: flex; flex-wrap: wrap; gap: 0.6rem;
}

/* ---- player reviews ----------------------------------------------------
   Hidden until the generator finds at least one approved review. Three empty
   slots read as an unfinished site, which is worse than no section at all. */
.reviews.is-empty { display: none; }
.q-stars { font-size: 0.95rem; letter-spacing: 0.08em; color: var(--orange); line-height: 1; }
.q-stars .q-dim { color: rgba(255, 255, 255, 0.14); }
.quote .who { display: flex; flex-wrap: wrap; gap: 0.5rem; align-items: baseline; }
.q-lg { color: var(--text-mute); text-transform: none; letter-spacing: 0.04em; }
.q-lg::before { content: "·"; margin-right: 0.5rem; }

/* ---- review carousel ---------------------------------------------------
   A scroll-snap row, so it works as a swipeable list with no JS at all.
   carousel.js only adds the arrows, dots, keyboard and auto-advance — the
   controls are built there so they can never show without the behaviour. */
.rev-wrap { position: relative; }
.rev-track {
  display: flex;
  gap: var(--sp-3);
  overflow-x: auto;
  scroll-snap-type: x mandatory;
  scroll-padding-inline: 0;
  padding-bottom: 0.4rem;
  scrollbar-width: none;            /* the dots are the affordance */
  -ms-overflow-style: none;
}
.rev-track::-webkit-scrollbar { display: none; }
.rev-track:focus-visible { outline: 2px solid var(--cyan); outline-offset: 6px; border-radius: 14px; }

.rev-track > .quote {
  scroll-snap-align: start;
  flex: 0 0 100%;
  min-width: 0;
}
@media (min-width: 680px)  { .rev-track > .quote { flex-basis: calc((100% - var(--sp-3)) / 2); } }
@media (min-width: 1000px) { .rev-track > .quote { flex-basis: calc((100% - 2 * var(--sp-3)) / 3); } }

.rev-nav {
  display: flex; align-items: center; gap: 0.9rem;
  margin-top: var(--sp-3);
}
.rev-nav--hidden { display: none; }   /* one page: nothing to navigate */
.rev-arrow {
  width: 36px; height: 36px; flex: none;
  display: flex; align-items: center; justify-content: center;
  font-size: 1.15rem; line-height: 1; cursor: pointer;
  background: rgba(255, 255, 255, 0.05);
  border: 1px solid var(--line);
  border-radius: 999px;
  color: var(--text-soft);
  transition: background 0.15s, border-color 0.15s, color 0.15s, opacity 0.15s;
}
.rev-arrow:hover:not(:disabled) {
  background: rgba(0, 232, 218, 0.12); border-color: rgba(0, 232, 218, 0.45); color: var(--cyan);
}
.rev-arrow:disabled { opacity: 0.28; cursor: default; }
.rev-dots { display: flex; gap: 0.45rem; margin-right: auto; align-items: center; }
.rev-nav .rev-arrow + .rev-arrow { margin-left: -0.25rem; }
.rev-dot {
  width: 8px; height: 8px; padding: 0; border: 0; border-radius: 999px;
  background: rgba(255, 255, 255, 0.2); cursor: pointer;
  transition: background 0.18s var(--ease), width 0.18s var(--ease);
}
.rev-dot.on { background: var(--cyan); width: 22px; }
.rev-dot:hover { background: rgba(0, 232, 218, 0.55); }

@media (prefers-reduced-motion: reduce) {
  .rev-track { scroll-behavior: auto; }
  .rev-dot { transition: none; }
}
/* One or two reviews: fill the row properly instead of leaving a three-column
   track two-thirds empty. A single review becomes a feature quote. */
.reviews--single .rev-track > .quote,
.reviews--pair .rev-track > .quote { flex-basis: 100%; }
@media (min-width: 680px) {
  .reviews--pair .rev-track > .quote { flex-basis: calc((100% - var(--sp-3)) / 2); }
  .reviews--single .rev-track > .quote { flex-basis: 100%; max-width: 46rem; }
  .reviews--single blockquote { font-size: var(--step-1); line-height: 1.55; }
  .reviews--single .quote { padding: 1.9rem 2rem; }
}
