/*
 * Marketing site — the whole stylesheet.
 *
 * One file, no build step, no framework. It is deployed by copying it to
 * nginx, so everything it needs is either here or in tokens.css.
 *
 * Two rules govern every line below:
 *
 *  1. No raw colour, size, radius or duration. Every value is a token from
 *     tokens.css. The only literals in this file are the two breakpoints
 *     (a media query cannot read a custom property) and structural constants
 *     with no design meaning — 0, 1fr, 100%, 50%.
 *
 *     The one exception is game art, which arrives from web/art/ carrying its
 *     own colour. That is the only place on the site that is not the palette.
 *
 *  2. Mobile first. Every rule is written for a 390px phone; the two
 *     min-width blocks add what a larger viewport can afford. The audience
 *     arrives on the phone they are about to install on, often on 3G, so the
 *     phone layout is the default path and the desktop layout is the
 *     exception.
 *
 * ---------------------------------------------------------------------------
 * WHAT THIS LOOKS LIKE, AND WHY IT CHANGED
 *
 * The first version of this page was a printed spec sheet: hairlines, muted
 * surfaces, one small accent, no artwork. It was honest and it was
 * disciplined and it read as a company rather than as a casino. Nobody wants
 * to install a spec sheet.
 *
 * So the register moved, and it moved in five specific ways:
 *
 *   - The ground became the plum night (--stage), so lantern-lit things read
 *     as lit. Game surfaces alone wear the emerald baize (--felt).
 *   - Gold has exactly one job: money and the brand. Marigold (--accent) keeps
 *     exactly one job: things you press. Two hues, two jobs, no overlap.
 *   - Glow and depth exist here (--glow-*, --shadow-*). The client has neither.
 *   - The type scale got a top end it did not have: --text-mega for the live
 *     multiplier, --text-hero for the headline.
 *   - Game art carries the catalogue. A tile is a picture with a name under
 *     it, not a paragraph in a box.
 *
 * What did NOT change is the arithmetic. Every house edge is still printed on
 * the tile it belongs to, the expected-loss table on Responsible play is still
 * plain, and no figure anywhere is dressed up. The page got louder about the
 * product; it did not get quieter about the odds.
 */

/* ========================================================== reset & base */

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

html {
  -webkit-text-size-adjust: 100%;
  scroll-behavior: smooth;
  background: var(--stage);
}

@media (prefers-reduced-motion: reduce) {
  html {
    scroll-behavior: auto;
  }
}

/* The two ambient washes are fixed to the viewport rather than painted on the
 * page, so a 6000px document does not stretch one radial gradient over its
 * whole height and lose it. They are off-centre pools of light on a dark room.
 * Deliberately not a linear two-hue gradient: that is the one gesture that
 * makes a page look machine-made. */
body {
  margin: 0;
  min-height: 100vh;
  background-color: var(--stage);
  background-image: var(--wash-accent), var(--wash-gold);
  background-repeat: no-repeat;
  background-attachment: fixed;
  background-size: 100% 720px, 100% 560px;
  color: var(--text-primary);
  font-family: var(--font-body);
  font-size: var(--text-body);
  line-height: var(--leading-normal);
  -webkit-font-smoothing: antialiased;
  overflow-x: hidden;
}

h1,
h2,
h3,
h4 {
  font-family: var(--font-display);
  font-weight: var(--weight-display);
  line-height: var(--leading-tight);
  letter-spacing: var(--tracking-display);
  margin: 0;
  text-wrap: balance;
}

h3,
h4 {
  font-weight: var(--weight-display-sub);
}

p {
  margin: 0;
}

ul,
ol {
  margin: 0;
  padding: 0;
}

a {
  color: var(--accent);
  text-decoration-color: var(--accent-border);
  text-underline-offset: var(--space-1);
}

a:hover {
  color: var(--accent-hover);
  text-decoration-color: var(--accent-hover);
}

img,
svg {
  display: block;
  max-width: 100%;
}

/* Focus is the accent and nothing else — identical on every focusable thing
 * on the site, exactly as --focus-ring is identical across all 38 games. */
:focus-visible {
  outline: var(--border-thick) solid var(--focus-ring);
  outline-offset: var(--space-1);
  border-radius: var(--radius-sm);
}

::selection {
  background: var(--accent-subtle);
  color: var(--text-primary);
}

/* Skip link. Present on every page; the site is a distribution channel and
 * has to be operable without a mouse. */
.skip {
  position: absolute;
  left: var(--space-3);
  top: calc(var(--space-3) * -1 - var(--tap-min));
  z-index: 4;
  padding: var(--space-3) var(--space-4);
  background: var(--accent);
  color: var(--text-on-accent);
  border-radius: var(--radius-md);
  font-weight: 600;
  text-decoration: none;
  transition: top var(--dur-fast) var(--ease-out);
}

.skip:focus {
  top: var(--space-3);
}

/* ================================================================== type */

.eyebrow {
  font-size: var(--text-label);
  letter-spacing: var(--tracking-label);
  text-transform: uppercase;
  color: var(--accent);
  font-weight: 600;
  margin: 0;
}

/* The quieter eyebrow, for a section that is information rather than an
 * invitation — the legal pages, the tables. */
.eyebrow--muted {
  color: var(--text-secondary);
}

.lead {
  font-size: var(--text-lead);
  color: var(--text-secondary);
  max-width: var(--measure-tight);
}

.prose {
  max-width: var(--measure);
  color: var(--text-secondary);
}

.prose > * + * {
  margin-top: var(--space-4);
}

.prose h2 {
  font-size: var(--text-subhead);
  color: var(--text-primary);
  margin-top: var(--space-7);
}

.prose h3 {
  font-size: var(--text-heading);
  color: var(--text-primary);
  margin-top: var(--space-6);
}

.prose strong {
  color: var(--text-primary);
  font-weight: 600;
}

.prose ul,
.prose ol {
  padding-left: var(--space-5);
}

.prose li + li {
  margin-top: var(--space-2);
}

.prose code {
  font-family: var(--font-numeric);
  font-size: var(--text-label);
  background: var(--stage-deep);
  border: var(--border-hairline) solid var(--border-subtle);
  border-radius: var(--radius-sm);
  padding: 0 var(--space-1);
  color: var(--text-primary);
  word-break: break-all;
}

/* Present for a screen reader, absent for everyone else. Used on table
 * captions, where the heading above already says it out loud. */
.visually-hidden {
  position: absolute;
  width: var(--rule);
  height: var(--rule);
  padding: 0;
  margin: 0;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

.num {
  font-family: var(--font-numeric);
  font-variant-numeric: var(--numeric);
}

/* An unfilled fact. Deliberately loud: nothing on this site should quietly
 * read as true when nobody has confirmed it.
 *
 * It wraps. Several of these are whole sentences describing what a policy has
 * to say, and a nowrap pill containing a sentence pushed the page 800px wider
 * than a 390px phone — invisible on a desktop, and on a phone it silently
 * shoved the entire layout sideways. */
.todo {
  font-family: var(--font-numeric);
  font-size: var(--text-caption);
  letter-spacing: var(--tracking-label);
  text-transform: uppercase;
  font-weight: 600;
  color: var(--pending);
  background: var(--pending-subtle);
  border: var(--border-hairline) solid var(--pending);
  border-radius: var(--radius-sm);
  padding: 0 var(--space-2);
  display: inline;
  max-width: 100%;
  overflow-wrap: anywhere;
  /* Each wrapped line gets its own border and padding rather than one box
   * torn across two lines. */
  -webkit-box-decoration-break: clone;
  box-decoration-break: clone;
}

.note {
  border-top: var(--border-thick) solid var(--pending);
  background: var(--panel);
  padding: var(--space-4);
  border-radius: 0 0 var(--radius-md) var(--radius-md);
  color: var(--text-secondary);
  font-size: var(--text-body);
  box-shadow: var(--shadow-card);
}

.note--info {
  border-top-color: var(--info);
}

.note > * + * {
  margin-top: var(--space-3);
}

.note strong {
  color: var(--text-primary);
}

/* A small honesty label. Used on anything on this page that LOOKS like live
 * data and is not — the wins ticker and the hero round. It is not decoration
 * and it does not get to be subtle: a visitor must never be able to mistake an
 * illustration for a feed of real players' money. */
.tag {
  display: inline-flex;
  align-items: center;
  gap: var(--space-1);
  padding: 0 var(--space-2);
  min-height: var(--space-5);
  border-radius: var(--radius-sm);
  font-family: var(--font-numeric);
  font-size: var(--text-caption);
  letter-spacing: var(--tracking-label);
  text-transform: uppercase;
  font-weight: 600;
  color: var(--text-secondary);
  background: var(--surface-wash);
  border: var(--border-hairline) solid var(--border-default);
  white-space: nowrap;
}

/* ================================================================ layout */

.wrap {
  width: 100%;
  max-width: var(--site-max);
  margin: 0 auto;
  padding-left: var(--site-gutter);
  padding-right: var(--site-gutter);
}

.section {
  padding-top: var(--space-7);
  padding-bottom: var(--space-7);
  border-top: var(--rule) solid var(--border-subtle);
  position: relative;
}

.section--flush {
  border-top: 0;
}

/* A band rather than a section — the ticker brings its own rules top and
 * bottom and does not need the full section rhythm around it as well. */
.section--band {
  padding-top: var(--space-5);
  padding-bottom: var(--space-5);
}

.section--sunken {
  background: var(--stage-deep);
  border-top-color: var(--border-default);
}

.stack > * + * {
  margin-top: var(--space-4);
}

.stack-lg > * + * {
  margin-top: var(--space-6);
}

/* The section head: a tracked label, a hairline, then the heading. This is the
 * spec-sheet motif that the whole first version was built on. It survives —
 * it is genuinely good structure — but the rule is now lit at its left end,
 * so it reads as part of a room with the lights on rather than a page in a
 * ring binder. */
.head {
  margin-bottom: var(--space-5);
}

.head__rule {
  height: var(--border-thick);
  background: linear-gradient(
    to right,
    var(--accent) 0,
    var(--border-default) var(--space-8),
    var(--transparent) 100%
  );
  border-radius: var(--radius-pill);
  margin: var(--space-3) 0 var(--space-4);
}

.head h2,
.head h1 {
  font-size: var(--text-section);
}

.head p {
  margin-top: var(--space-3);
  color: var(--text-secondary);
  max-width: var(--measure);
}

/* ================================================================ header */

.site-header {
  position: sticky;
  top: 0;
  z-index: 3;
  background: var(--surface-scrim);
  backdrop-filter: blur(var(--blur-header));
  -webkit-backdrop-filter: blur(var(--blur-header));
  border-bottom: var(--rule) solid var(--border-subtle);
  box-shadow: var(--shadow-sticky);
}

/* The dotted bazaar divider: dashes of lantern light with alternating gold
 * and rani beads, riding just under the header. */
.site-header::after {
  content: '';
  position: absolute;
  left: 0;
  right: 0;
  top: 100%;
  height: var(--divider-h);
  background-image: var(--bazaar-divider);
  background-size: var(--divider-sizes);
  background-position: var(--divider-pos);
  background-repeat: repeat-x;
  pointer-events: none;
}

/* One flex row that is allowed to wrap. The brand and the toggle sit on the
 * first line; the nav is the wrapped third child, full width, hidden until the
 * toggle opens it. At 1000px it stops wrapping and becomes a normal row — the
 * same DOM, no duplicate markup, no overlay to get trapped behind. */
.site-header__bar {
  min-height: var(--header-height);
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-4);
}

/* The wordmark: Kismat in Rozha One, led by the diya mark — the lantern
 * flame from the identity boards. The markup carries "Kismat" as plain text
 * so the accessible name reads naturally. */
.brand {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  color: var(--text-primary);
  text-decoration: none;
  font-family: var(--font-display);
  font-weight: var(--weight-display);
  font-size: var(--text-title);
  letter-spacing: var(--tracking-display);
  line-height: var(--leading-tight);
}

/* The diya is gold — the money colour — and it is the one non-figure on the
 * site allowed to carry it. It is a glyph, never a control. Drawn inline in
 * the markup; coloured here so the SVG carries no raw value. */
.brand__diya {
  width: var(--diya-w);
  height: var(--diya-h);
  flex: none;
}
.brand__diya path {
  stroke: var(--gold);
  stroke-width: var(--icon-stroke);
  fill: var(--accent-subtle);
}
.brand__diya circle { fill: var(--gold); }

/* Nav collapses to a disclosure on the phone. One button, no overlay, no
 * scroll lock — it pushes the page down, which costs nothing and cannot trap
 * anyone behind a broken close button on a slow connection. */
.nav-toggle {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-2);
  min-height: var(--tap-min);
  min-width: var(--tap-min);
  padding: 0 var(--space-3);
  background: var(--surface-wash);
  color: var(--text-primary);
  border: var(--border-hairline) solid var(--border-default);
  border-radius: var(--radius-md);
  font: inherit;
  font-size: var(--text-label);
  font-weight: 600;
  cursor: pointer;
}

.nav-toggle:hover {
  background: var(--surface-wash-strong);
  border-color: var(--border-strong);
}

.nav-toggle svg {
  width: var(--icon-lg);
  height: var(--icon-lg);
}

.nav-toggle .nav-toggle__close,
.nav-toggle[aria-expanded='true'] .nav-toggle__open {
  display: none;
}

.nav-toggle[aria-expanded='true'] .nav-toggle__close {
  display: block;
}

.site-nav {
  display: none;
  flex-basis: 100%;
  padding-bottom: var(--space-4);
  border-top: var(--rule) solid var(--border-subtle);
}

.site-nav.is-open {
  display: block;
}

.site-nav ul {
  list-style: none;
  display: flex;
  flex-direction: column;
}

/* The plain nav links carry their own class rather than being selected as
 * `.site-nav a`. The download CTA is also an anchor inside this nav, and a
 * descendant selector would out-specify .btn and strip its padding — which is
 * exactly what it did before this class existed. */
.site-nav__link {
  display: flex;
  align-items: center;
  min-height: var(--tap-min);
  padding: var(--space-2) 0;
  color: var(--text-secondary);
  text-decoration: none;
  font-weight: 500;
  border-bottom: var(--rule) solid var(--border-subtle);
}

.site-nav__link:hover {
  color: var(--text-primary);
}

.site-nav__link[aria-current='page'] {
  color: var(--accent);
}

.site-nav .site-nav__cta {
  margin-top: var(--space-4);
  border-bottom: 0;
}

/* ================================================================ button */

.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-2);
  /* Was `--tap-bet` — a token this file referenced and tokens.css never
   * defined, so the declaration was invalid at computed-value time and every
   * button on the site had no minimum height whatsoever. Nothing errored and
   * nothing looked obviously broken. HANDOFF §5.3, on the web side. */
  min-height: var(--tap-cta);
  padding: var(--space-3) var(--space-5);
  border-radius: var(--radius-md);
  border: var(--border-hairline) solid var(--transparent);
  font-family: var(--font-body-medium);
  font-size: var(--text-body);
  font-weight: 600;
  text-decoration: none;
  cursor: pointer;
  transition: transform var(--dur-fast) var(--ease-spring),
    box-shadow var(--dur-normal) var(--ease-out),
    background-color var(--dur-fast) var(--ease-out),
    border-color var(--dur-fast) var(--ease-out);
}

.btn svg {
  width: var(--icon-lg);
  height: var(--icon-lg);
  flex: none;
}

.btn:active {
  transform: scale(var(--press-scale));
}

.btn--primary {
  background: var(--cta-grad);
  color: var(--text-on-accent);
  box-shadow: var(--glow-accent);
}

.btn--primary:hover {
  background: var(--cta-grad-hover);
  color: var(--text-on-accent);
  box-shadow: var(--glow-accent-lg);
}

.btn--primary:active {
  background: var(--accent-pressed);
}

.btn--secondary {
  background: var(--surface-wash);
  color: var(--text-primary);
  border-color: var(--border-strong);
}

.btn--secondary:hover {
  background: var(--surface-wash-strong);
  color: var(--text-primary);
  border-color: var(--accent-border);
}

.btn--block {
  width: 100%;
}

/* The one unmissable button. Used once per page, on the download. It is
 * bigger than a normal button, it glows, and the second line under the label
 * answers the question a first-time visitor actually has — how big, and from
 * where — so the button does not need a paragraph next to it. */
.btn--cta {
  min-height: var(--space-8);
  padding: var(--space-4) var(--space-6);
  font-size: var(--text-subhead);
  /* Buttons stay in Mukta — Rozha One is display only, never a control. */
  font-family: var(--font-body);
  font-weight: 700;
  letter-spacing: var(--tracking-display);
  border-radius: var(--radius-lg);
  gap: var(--space-3);
}

.btn--cta svg {
  width: var(--icon-xl);
  height: var(--icon-xl);
}

.btn__sub {
  display: block;
  font-family: var(--font-body);
  font-size: var(--text-caption);
  font-weight: 500;
  letter-spacing: var(--tracking-label);
  text-transform: uppercase;
  opacity: var(--opacity-pressed);
}

/* A quiet secondary route. Deliberately NOT a second button — the download is
 * the only call to action on this site and a competing button would dilute it. */
.textlink {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  min-height: var(--tap-min);
  font-weight: 600;
  text-decoration: none;
  border-bottom: var(--border-hairline) solid var(--accent-border);
  align-self: flex-start;
}

.textlink svg {
  width: var(--icon-md);
  height: var(--icon-md);
  transition: transform var(--dur-fast) var(--ease-out);
}

.textlink:hover svg {
  transform: translateX(var(--space-1));
}

/* ================================================================== hero */

.hero {
  padding-top: var(--space-6);
  padding-bottom: var(--space-7);
  position: relative;
}

.hero h1 {
  font-size: var(--text-hero);
  max-width: var(--measure-tight);
  letter-spacing: var(--tracking-mega);
}

.hero .lead {
  margin-top: var(--space-4);
}

.hero .eyebrow {
  margin-bottom: var(--space-3);
}

.hero__actions {
  margin-top: var(--space-6);
  display: flex;
  flex-direction: column;
  align-items: stretch;
  gap: var(--space-4);
}

/* --text-secondary, not --text-muted: muted clears 4.5:1 on this surface by
 * 0.01, which is not a margin worth having on a line that tells someone their
 * phone is about to show them a security warning. */
.hero__meta {
  margin-top: var(--space-4);
  color: var(--text-secondary);
  font-size: var(--text-label);
  max-width: var(--measure-tight);
}

/* The home hero. Tighter than the others: the headline, the live round and the
 * button have to be inside the first screenful of a 390px phone, so the lead
 * paragraph is one sentence and everything else waits. */
.hero--home {
  padding-top: var(--space-5);
  padding-bottom: var(--space-6);
}

/* Everything above the live panel is compressed on purpose. On a 700px-tall
 * phone the headline, the button and the first artwork all have to be inside
 * the first screenful, and the only way to buy that room is to stop the lead
 * paragraph and the action block from breathing as much as they do elsewhere. */
.hero--home .lead {
  margin-top: var(--space-3);
}

.hero--home .hero__actions {
  margin-top: var(--space-5);
  gap: var(--space-3);
}

.hero--home .strip {
  margin-top: var(--space-4);
}

/* The meta paragraph is the last thing in the hero and the ticker band that
 * follows brings its own rule, so the hero does not also need a full section's
 * worth of air under it. */
.hero--home .hero__meta {
  margin-top: var(--space-4);
  margin-bottom: calc(var(--space-4) * -1);
}

.hero--home h1 {
  max-width: none;
}

/* The money phrase in the headline — "Real money." — in the money colour,
 * glowing. This is the only place on the site where display type is coloured,
 * and it is the one non-figure phrase gold is allowed on because it literally
 * names money. */
.hero em {
  font-style: normal;
  color: var(--gold);
  text-shadow: var(--glow-ink-gold);
}

/* ========================================================== live round == */

/* The showpiece. A Crash round that actually runs: the multiplier climbs, the
 * curve draws under it, and at a point fixed before the animation started it
 * stops and goes red. It is the product's central mechanic, shown rather than
 * described, and it is the reason a visitor understands this page in two
 * seconds instead of reading four paragraphs.
 *
 * It is labelled ILLUSTRATIVE, twice, because it is not a feed. */
.live {
  margin-top: var(--space-5);
  position: relative;
  overflow: hidden;
  border: var(--border-hairline) solid var(--accent-border);
  border-radius: var(--radius-lg);
  background-color: var(--felt);
  background-image: var(--wash-tile), var(--wash-felt);
  box-shadow: var(--shadow-panel), var(--inset-top);
  padding: var(--space-4) var(--space-4) 0;
}

.live__head {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  flex-wrap: wrap;
}

.live__label {
  font-size: var(--text-caption);
  letter-spacing: var(--tracking-label);
  text-transform: uppercase;
  font-weight: 600;
  color: var(--text-on-felt);
  margin-right: auto;
}

/* The pulse. Small, and the only thing on the page that moves on its own
 * without being asked to. */
.live__dot {
  width: var(--space-2);
  height: var(--space-2);
  border-radius: var(--radius-pill);
  background: var(--live);
  box-shadow: 0 0 0 0 var(--win-subtle);
  flex: none;
  animation: pulse var(--dur-live) var(--ease-out) infinite;
}

@keyframes pulse {
  0% {
    box-shadow: 0 0 0 0 var(--win-subtle);
  }
  70% {
    box-shadow: 0 0 0 var(--space-3) var(--transparent);
  }
  100% {
    box-shadow: 0 0 0 0 var(--transparent);
  }
}

/* The number. This is --text-mega and nothing else on the site uses it. */
.live__mult {
  font-family: var(--font-numeric);
  font-variant-numeric: var(--numeric);
  font-size: var(--text-mega);
  font-weight: 600;
  line-height: 1;
  letter-spacing: var(--tracking-mega);
  color: var(--accent);
  text-shadow: var(--glow-ink-accent);
  margin: var(--space-2) 0 0;
  transition: color var(--dur-fast) var(--ease-out);
}

.live__mult .live__x {
  font-size: 0.44em;
  letter-spacing: 0;
  opacity: var(--opacity-pressed);
}

/* The crash. The number turns to the loss colour and the caption underneath
 * swaps — the same two-state treatment the game screen uses. */
.live.is-bust .live__mult {
  color: var(--loss);
  text-shadow: none;
}

.live.is-bust .live__curve {
  stroke: var(--loss);
}

.live.is-bust .live__area {
  fill: var(--loss-subtle);
}

.live.is-bust .live__dot {
  background: var(--loss);
  animation: none;
}

.live__state {
  margin-top: var(--space-1);
  font-size: var(--text-label);
  font-weight: 600;
  letter-spacing: var(--tracking-label);
  text-transform: uppercase;
  color: var(--text-on-felt-dim);
  min-height: var(--space-5);
}

.live.is-bust .live__state {
  color: var(--loss);
}

.live__plot {
  width: 100%;
  height: var(--hero-plot-h);
  margin-top: var(--space-3);
  display: block;
}

.live__grid {
  stroke: var(--border-subtle);
  stroke-width: var(--border-hairline);
  fill: none;
}

.live__curve {
  fill: none;
  stroke: var(--accent);
  stroke-width: var(--border-thick);
  stroke-linecap: round;
  stroke-linejoin: round;
}

.live__area {
  fill: var(--accent-subtle);
  stroke: none;
}

.live__nose {
  fill: var(--accent);
}

.live.is-bust .live__nose {
  fill: var(--loss);
}

.live__foot {
  display: flex;
  flex-wrap: wrap;
  justify-content: space-between;
  gap: var(--space-2);
  padding: var(--space-3) 0;
  margin-top: var(--space-1);
  border-top: var(--rule) solid var(--border-subtle);
  font-size: var(--text-caption);
  letter-spacing: var(--tracking-label);
  text-transform: uppercase;
  color: var(--text-on-felt-dim);
}

/* ============================================================ art strip = */

/* A row of game art that runs off the right edge of the phone. The overflow is
 * the point: it says "there are more of these" without a word, and it puts
 * artwork on screen inside the first viewport, which is the single thing the
 * old page never did.
 *
 * It is full-bleed on the phone — negative margins to the gutter — so the
 * first tile lines up with the text and the last one is cut by the screen edge
 * rather than by a container. */
.strip {
  margin-top: var(--space-5);
}

.strip__label {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--space-3);
  font-size: var(--text-caption);
  letter-spacing: var(--tracking-label);
  text-transform: uppercase;
  font-weight: 600;
  color: var(--text-secondary);
  margin-bottom: var(--space-3);
}

.strip__rail {
  display: grid;
  grid-auto-flow: column;
  grid-auto-columns: var(--art-hero-min);
  gap: var(--space-3);
  overflow-x: auto;
  overscroll-behavior-x: contain;
  scroll-snap-type: x proximity;
  -webkit-overflow-scrolling: touch;
  scrollbar-width: none;
  margin-left: calc(var(--site-gutter) * -1);
  margin-right: calc(var(--site-gutter) * -1);
  padding: var(--space-1) var(--site-gutter);
}

.strip__rail::-webkit-scrollbar {
  display: none;
}

.strip__rail > * {
  scroll-snap-align: start;
}

/* ============================================================== fact row */

.facts {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(var(--fact-min), 1fr));
  gap: var(--space-5);
}

.fact dt {
  font-size: var(--text-label);
  letter-spacing: var(--tracking-label);
  text-transform: uppercase;
  color: var(--text-secondary);
  font-weight: 600;
}

/* The figures are gold, because gold is the money colour on this page and
 * every one of these is a number about money, a count or an age. */
.fact dd {
  margin: var(--space-2) 0 0;
  font-family: var(--font-numeric);
  font-variant-numeric: var(--numeric);
  font-size: var(--text-numeral);
  font-weight: 600;
  line-height: var(--leading-tight);
  color: var(--gold);
  letter-spacing: var(--tracking-mega);
}

.fact p {
  margin-top: var(--space-2);
  color: var(--text-secondary);
  font-size: var(--text-label);
}

/* ================================================================== card */

.card {
  background-color: var(--panel);
  background-image: var(--wash-tile);
  border: var(--border-hairline) solid var(--border-subtle);
  border-radius: var(--radius-lg);
  padding: var(--space-5);
  box-shadow: var(--shadow-card), var(--inset-top);
}

.card > * + * {
  margin-top: var(--space-3);
}

.card h3 {
  font-size: var(--text-heading);
}

.card p {
  color: var(--text-secondary);
}

.grid {
  display: grid;
  gap: var(--space-4);
  grid-template-columns: repeat(auto-fit, minmax(min(var(--tile-min), 100%), 1fr));
}

.grid--steps {
  grid-template-columns: repeat(auto-fit, minmax(min(var(--step-min), 100%), 1fr));
}

/* ============================================================= game tile */

/* THE CATALOGUE IS THE MARKETING.
 *
 * A tile is a picture with a name under it. The picture is the whole top of
 * the tile at a fixed 4:3, it is the largest thing in the component, and it
 * comes from web/art/ — the one part of this page whose colour the design
 * system does not own.
 *
 * The art is loaded as <img src="/art/games/<slug>.svg">, not as a <use> into
 * the sprite, for three reasons that all matter to the actual audience:
 *   - nginx sends `default-src 'none'; img-src 'self' data:`. An <img> is
 *     unambiguously allowed by img-src. An external <use> reference is not
 *     covered by any directive and its handling is not consistent.
 *   - a per-file <img> can be loading="lazy", so a 390px phone on 3G fetches
 *     the four tiles it can see and not the other sixteen.
 *   - each file caches and revalidates on its own; one sprite invalidates
 *     every tile whenever any single game's art is redrawn.
 * The sprite at web/art/games.svg remains the right choice for anything that
 * needs to recolour the art with currentColor. This page does not.
 *
 * If the art has not landed yet the <img> resolves to nothing and the frame it
 * sits in still reads as a deliberate panel rather than a hole, because the
 * frame carries its own ground and inner light. */
.tile {
  display: flex;
  flex-direction: column;
  position: relative;
  background-color: var(--felt);
  background-image: var(--wash-tile);
  border: var(--border-hairline) solid var(--border-default);
  border-radius: var(--radius-lg);
  overflow: hidden;
  text-decoration: none;
  color: inherit;
  box-shadow: var(--shadow-card), var(--inset-top);
  transition: transform var(--dur-normal) var(--ease-spring),
    box-shadow var(--dur-normal) var(--ease-out),
    border-color var(--dur-fast) var(--ease-out);
}

a.tile:hover,
a.tile:focus-visible {
  color: inherit;
  transform: translateY(var(--lift-tile));
  border-color: var(--accent-border);
  box-shadow: var(--shadow-card-hover), var(--glow-accent), var(--inset-top-strong);
}

.tile__art {
  display: block;
  position: relative;
  aspect-ratio: var(--art-ratio);
  background-color: var(--felt-deep);
  background-image: radial-gradient(
    80% 70% at 50% 28%,
    var(--felt-raised) 0%,
    var(--felt-deep) 78%
  );
  border-bottom: var(--rule) solid var(--border-subtle);
  overflow: hidden;
}

.tile__art img,
.tile__art svg {
  width: 100%;
  height: 100%;
  object-fit: cover;
  transition: transform var(--dur-slow) var(--ease-out);
}

a.tile:hover .tile__art img,
a.tile:hover .tile__art svg {
  transform: scale(1.04);
}

/* The kind badge rides on the art rather than taking a line of its own —
 * eighteen tiles on a phone cannot each afford a third line. */
.tile__kind {
  position: absolute;
  left: var(--space-2);
  top: var(--space-2);
  padding: 0 var(--space-2);
  border-radius: var(--radius-sm);
  background: var(--surface-scrim);
  backdrop-filter: blur(var(--blur-header));
  -webkit-backdrop-filter: blur(var(--blur-header));
  border: var(--border-hairline) solid var(--border-subtle);
  font-size: var(--text-caption);
  letter-spacing: var(--tracking-label);
  text-transform: uppercase;
  font-weight: 600;
  color: var(--text-secondary);
  line-height: var(--space-5);
}

.tile__body {
  display: flex;
  flex-direction: column;
  gap: var(--space-1);
  padding: var(--space-3);
  flex: 1;
}

.tile__name {
  font-family: var(--font-display);
  font-weight: var(--weight-display-sub);
  font-size: var(--text-body);
  line-height: var(--leading-tight);
  letter-spacing: var(--tracking-display);
  color: var(--text-primary);
}

/* A game's house edge is a fact about it, so it is printed on the tile rather
 * than buried in a footnote. It stayed when everything else got louder. */
.tile__edge {
  font-family: var(--font-numeric);
  font-variant-numeric: var(--numeric);
  font-size: var(--text-caption);
  letter-spacing: var(--tracking-label);
  text-transform: uppercase;
  font-weight: 600;
  color: var(--text-on-felt-dim);
  margin-top: auto;
}

.tile__edge b {
  color: var(--gold);
  font-weight: 600;
}

/* The catalogue tile on games.html, which has room for the sentence that says
 * what the game actually is. */
.tile--detail .tile__body {
  gap: var(--space-2);
  padding: var(--space-4);
}

.tile--detail .tile__name {
  font-size: var(--text-heading);
}

.tile__desc {
  color: var(--text-on-felt);
  font-size: var(--text-label);
  margin: 0;
}

.tile--detail .tile__edge {
  padding-top: var(--space-3);
  border-top: var(--rule) solid var(--border-subtle);
}

/* The art grid. Two columns on the smallest phone we support, and it grows by
 * itself — no rule anywhere in this file names a column count. */
.grid--art {
  display: grid;
  gap: var(--space-3);
  grid-template-columns: repeat(auto-fill, minmax(min(var(--art-min), 100%), 1fr));
}

.grid--catalogue {
  display: grid;
  gap: var(--space-4);
  grid-template-columns: repeat(auto-fill, minmax(min(var(--tile-min), 100%), 1fr));
}

/* =============================================================== ticker = */

/* The wins ticker.
 *
 * READ THIS BEFORE CHANGING IT. These rows are WRITTEN INTO THE HTML as fixed
 * example values and they are labelled, in visible text, as illustrative. They
 * are not a feed, they are not sampled from the ledger, and no number here is
 * presented as a real person's money. A casino page that fakes a live win feed
 * is lying to the exact people this product is asking to trust its RNG, and
 * this one does not.
 *
 * What it IS: an honest demonstration of the shape of a result — game, stake,
 * multiplier, return — so a visitor can see what winning looks like here
 * before installing anything. */
.ticker {
  position: relative;
  overflow: hidden;
  border-top: var(--rule) solid var(--border-subtle);
  border-bottom: var(--rule) solid var(--border-subtle);
  background: var(--stage-deep);
  padding: var(--space-3) 0;
  /* Both edges fade out, so the row reads as a band passing through the page
   * rather than a list that has been cut off. */
  -webkit-mask-image: linear-gradient(
    to right,
    var(--transparent),
    var(--text-primary) var(--space-8),
    var(--text-primary) calc(100% - var(--space-8)),
    var(--transparent)
  );
  mask-image: linear-gradient(
    to right,
    var(--transparent),
    var(--text-primary) var(--space-8),
    var(--text-primary) calc(100% - var(--space-8)),
    var(--transparent)
  );
}

.ticker__rail {
  display: flex;
  width: max-content;
  gap: var(--space-3);
  animation: marquee var(--dur-marquee) linear infinite;
}

.ticker:hover .ticker__rail {
  animation-play-state: paused;
}

/* Two identical copies sit in the rail and it translates by exactly half its
 * own width, so the seam never appears. The second copy is aria-hidden in the
 * markup — a screen reader reads the list once. */
@keyframes marquee {
  from {
    transform: translateX(0);
  }
  to {
    transform: translateX(-50%);
  }
}

.win {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  flex: none;
  padding: var(--space-2) var(--space-4);
  border-radius: var(--radius-pill);
  background: var(--panel);
  border: var(--border-hairline) dashed var(--border-default);
  white-space: nowrap;
  font-size: var(--text-label);
}

.win__who {
  color: var(--text-secondary);
  font-weight: 500;
}

.win__game {
  color: var(--text-muted);
}

.win__mult {
  font-family: var(--font-numeric);
  font-variant-numeric: var(--numeric);
  font-weight: 600;
  color: var(--accent);
}

.win__amt {
  font-family: var(--font-numeric);
  font-variant-numeric: var(--numeric);
  font-weight: 600;
  color: var(--gold);
}

/* A big multiplier gets the hot colour. Same ramp as the client's multiplier
 * heat, used here for the same reason: magnitude should read before value. */
.win--big .win__mult {
  color: var(--gold);
}

.win--huge .win__mult {
  color: var(--ember);
}

.ticker__note {
  margin-top: var(--space-3);
  font-size: var(--text-caption);
  color: var(--text-muted);
  letter-spacing: var(--tracking-label);
  text-transform: uppercase;
  display: flex;
  align-items: center;
  gap: var(--space-2);
  flex-wrap: wrap;
}

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

  .ticker {
    overflow-x: auto;
  }

  .live__dot {
    animation: none;
  }
}

/* A trailing link under a grid or a list. Named rather than set with an
 * inline style attribute: nginx sends `style-src 'self'` with no
 * 'unsafe-inline', so a style="" attribute on this site is not applied at all
 * in production while looking perfectly fine on a local file:// preview. */
.more {
  margin-top: var(--space-5);
}

/* =============================================================== ordered */

.steps {
  list-style: none;
  counter-reset: step;
}

.steps > li {
  counter-increment: step;
  padding: var(--space-5) 0;
  border-top: var(--rule) solid var(--border-default);
  display: grid;
  gap: var(--space-3);
}

.steps > li::before {
  content: counter(step, decimal-leading-zero);
  font-family: var(--font-numeric);
  font-variant-numeric: var(--numeric);
  font-size: var(--text-figure);
  font-weight: 600;
  color: var(--accent);
  text-shadow: var(--glow-ink-accent);
  letter-spacing: var(--tracking-mega);
  line-height: 1;
}

/* The text column of a step. The <li> grid gaps the number away from the
 * text; this gaps the heading away from its first paragraph, which otherwise
 * sat flush against it because the two are siblings inside one child. */
.steps > li > div {
  display: grid;
  gap: var(--space-3);
  align-content: start;
}

.steps h3 {
  font-size: var(--text-subhead);
}

.steps p {
  color: var(--text-secondary);
  max-width: var(--measure);
}

.steps p + p {
  margin-top: var(--space-3);
}

/* ================================================================= table */

.table-wrap {
  overflow-x: auto;
  border: var(--border-hairline) solid var(--border-subtle);
  border-radius: var(--radius-md);
  background: var(--panel);
  box-shadow: var(--shadow-card);
}

table {
  border-collapse: collapse;
  width: 100%;
  font-size: var(--text-label);
}

/* Tight horizontal padding by default so a four-column table fits inside a
 * 390px phone without scrolling. It grows at 720px, where there is room. */
th,
td {
  text-align: left;
  padding: var(--space-3) var(--space-2);
  border-bottom: var(--rule) solid var(--border-subtle);
  white-space: nowrap;
  vertical-align: top;
}

/* Headers wrap, cells do not. The headers are the widest thing in every table
 * on this site ("Expected loss per hour"), and letting them wrap is what keeps
 * the column that actually matters on screen instead of behind a scroll. */
th {
  font-size: var(--text-caption);
  letter-spacing: var(--tracking-label);
  text-transform: uppercase;
  color: var(--text-secondary);
  font-weight: 600;
  white-space: normal;
}

td {
  color: var(--text-primary);
}

td.is-num {
  font-family: var(--font-numeric);
  font-variant-numeric: var(--numeric);
}

tr:last-child td {
  border-bottom: 0;
}

/* ============================================================== release */

/* The download facts. A checksum is only useful if it can be read and copied
 * without error, so it gets its own well, wraps on any character, and carries
 * a copy button that falls back to plain selectable text without JS. */
.release {
  background-color: var(--panel);
  background-image: var(--wash-tile);
  border: var(--border-hairline) solid var(--border-default);
  border-radius: var(--radius-lg);
  padding: var(--space-5);
  box-shadow: var(--shadow-card), var(--inset-top);
}

.release dl {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--space-4);
  margin: 0;
}

.release dt {
  font-size: var(--text-caption);
  letter-spacing: var(--tracking-label);
  text-transform: uppercase;
  color: var(--text-secondary);
  font-weight: 600;
}

.release dd {
  margin: var(--space-1) 0 0;
  font-family: var(--font-numeric);
  font-variant-numeric: var(--numeric);
  font-size: var(--text-heading);
  color: var(--text-primary);
}

.checksum {
  margin-top: var(--space-5);
}

.checksum__value {
  display: block;
  margin-top: var(--space-2);
  padding: var(--space-3);
  background: var(--stage-deep);
  border: var(--border-hairline) solid var(--border-subtle);
  border-radius: var(--radius-md);
  font-family: var(--font-numeric);
  font-variant-numeric: var(--numeric);
  font-size: var(--text-label);
  color: var(--text-primary);
  word-break: break-all;
  line-height: var(--leading-normal);
}

.copy {
  margin-top: var(--space-3);
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  min-height: var(--tap-min);
  padding: 0 var(--space-4);
  background: var(--surface-wash);
  color: var(--text-primary);
  border: var(--border-hairline) solid var(--border-default);
  border-radius: var(--radius-md);
  font: inherit;
  font-size: var(--text-label);
  font-weight: 600;
  cursor: pointer;
}

.copy:hover {
  background: var(--surface-wash-strong);
  border-color: var(--border-strong);
}

.copy svg {
  width: var(--icon-md);
  height: var(--icon-md);
}

.copy[data-copied='true'] {
  color: var(--accent);
  border-color: var(--accent-border);
}

pre.cmd {
  margin: var(--space-3) 0 0;
  padding: var(--space-3) var(--space-4);
  background: var(--stage-deep);
  border: var(--border-hairline) solid var(--border-subtle);
  border-radius: var(--radius-md);
  overflow-x: auto;
  font-family: var(--font-numeric);
  font-size: var(--text-label);
  color: var(--text-primary);
}

/* ============================================================== closer == */

/* The last block on every page: one line, one button. A visitor who has read
 * to the bottom should not have to scroll back up to find the download. */
.closer {
  text-align: center;
  background-color: var(--panel);
  background-image: var(--wash-tile);
  border: var(--border-hairline) solid var(--border-default);
  border-radius: var(--radius-xl);
  padding: var(--space-6) var(--space-5);
  box-shadow: var(--shadow-panel), var(--inset-top);
  display: grid;
  justify-items: center;
  gap: var(--space-4);
}

.closer h2 {
  font-size: var(--text-section);
  max-width: var(--measure-tight);
}

.closer p {
  color: var(--text-secondary);
  max-width: var(--measure-tight);
}

/* ================================================================ footer */

.site-footer {
  border-top: var(--rule) solid var(--border-default);
  background: var(--stage-deep);
  padding-top: var(--space-7);
  padding-bottom: var(--space-7);
  margin-top: var(--space-7);
  position: relative;
}

.site-footer__cols {
  display: grid;
  gap: var(--space-6);
  grid-template-columns: repeat(auto-fit, minmax(min(var(--fact-min), 100%), 1fr));
}

.site-footer h2 {
  font-family: var(--font-body-medium);
  font-size: var(--text-caption);
  letter-spacing: var(--tracking-label);
  text-transform: uppercase;
  color: var(--text-secondary);
  font-weight: 600;
  margin-bottom: var(--space-3);
}

.site-footer ul {
  list-style: none;
}

.site-footer li + li {
  margin-top: var(--space-1);
}

/* Only the column links get the 44px target treatment. Scoped to `li a`
 * rather than `.site-footer a`, because the legal paragraph below also
 * contains links, and turning an inline link inside a sentence into a 44px
 * flex box tears a hole in the line it sits in. */
.site-footer li a {
  display: inline-flex;
  align-items: center;
  min-height: var(--tap-min);
  color: var(--text-secondary);
  text-decoration: none;
}

.site-footer li a:hover {
  color: var(--text-primary);
}

.site-footer__legal {
  margin-top: var(--space-6);
  padding-top: var(--space-5);
  border-top: var(--rule) solid var(--border-subtle);
  color: var(--text-muted);
  font-size: var(--text-label);
  display: grid;
  gap: var(--space-3);
}

/* The 18+ mark. Not decoration — it is the one thing on this page that has to
 * be visible on every single view. */
.age {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: var(--tap-min);
  min-height: var(--tap-min);
  border: var(--border-thick) solid var(--loss);
  color: var(--loss);
  border-radius: var(--radius-pill);
  font-family: var(--font-numeric);
  font-variant-numeric: var(--numeric);
  font-weight: 600;
  font-size: var(--text-label);
  flex: none;
}

/* Two flex children and no bare text nodes: the badge, and one span holding
 * the whole sentence. A bare text node beside an inline link would become two
 * separate anonymous flex items and the sentence would lay out in columns. */
.age-line {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  color: var(--text-secondary);
}

/* ============================================================ 720px + == */

@media (min-width: 720px) {
  .section {
    padding-top: var(--space-8);
    padding-bottom: var(--space-8);
  }

  .hero__actions {
    flex-direction: row;
    align-items: center;
    flex-wrap: wrap;
  }

  .btn--block {
    width: auto;
  }

  .release dl {
    grid-template-columns: repeat(auto-fit, minmax(min(var(--fact-min), 100%), 1fr));
  }

  th,
  td {
    padding: var(--space-3) var(--space-4);
  }

  .steps > li {
    grid-template-columns: auto 1fr;
    column-gap: var(--space-5);
  }

  .live {
    padding: var(--space-5) var(--space-5) 0;
  }

  .closer {
    padding: var(--space-8) var(--space-6);
  }
}

/* =========================================================== 1000px + == */

@media (min-width: 1000px) {
  /* The nav stops collapsing. The toggle is removed from the accessibility
   * tree as well as from view, so a keyboard user is never sent to a control
   * that does nothing. */
  .nav-toggle {
    display: none;
  }

  .site-nav {
    display: block;
    flex-basis: auto;
    margin-left: auto;
    border-top: 0;
    padding-bottom: 0;
  }

  .site-nav ul {
    flex-direction: row;
    align-items: center;
    gap: var(--space-5);
  }

  .site-nav__link {
    min-height: var(--header-height);
    border-bottom: 0;
    font-size: var(--text-label);
  }

  .site-nav .site-nav__cta {
    margin-top: 0;
  }

  /* In the header the CTA drops to the standard 44px target. --tap-cta is
   * sized for the one button the whole site is pointing at; a nav button is
   * pressed once, and at 56px it fills the entire 58px bar. */
  .site-nav__cta .btn {
    min-height: var(--tap-min);
    padding: var(--space-2) var(--space-4);
    font-size: var(--text-label);
  }

  .site-header__bar {
    gap: var(--space-6);
  }

  /* The hero splits: copy left, the live round right, sharing one baseline. */
  .hero__split {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: var(--space-7);
    align-items: center;
  }

  .live {
    margin-top: 0;
  }

  /* The strip stops being a scroller and becomes a row, because at this width
   * the whole row fits and a horizontal scrollbar on a desktop is a nuisance
   * rather than an affordance. */
  .strip__rail {
    grid-auto-flow: unset;
    grid-template-columns: repeat(auto-fit, minmax(min(var(--art-hero-min), 100%), 1fr));
    overflow-x: visible;
    margin-left: 0;
    margin-right: 0;
    padding-left: 0;
    padding-right: 0;
  }
}
