/* ==========================================================================
   SCAPE — page + component styles
   Consumes tokens.css only. Raw values appear solely where Figma stores the
   value as an effect (gradients, inner shadows) rather than as a variable.
   ========================================================================== */

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

html {
  -webkit-text-size-adjust: 100%;
  scroll-behavior: smooth;
  scroll-padding-top: calc(var(--header-height) + var(--space-7));
}

body {
  margin: 0;
  background: var(--color-background-default);
  color: var(--color-text-primary);
  font-family: var(--font-family-body);
  font-size: var(--font-size-md);
  line-height: var(--line-height-normal);
  -webkit-font-smoothing: antialiased;
  overflow-x: hidden;
}

h1, h2, h3 {
  margin: 0;
  font-family: var(--font-family-heading);
  font-weight: var(--font-weight-semibold);
  line-height: var(--line-height-tight);
  letter-spacing: var(--letter-spacing-tight);
  text-transform: uppercase;
  text-wrap: balance;
}
p { margin: 0; text-wrap: pretty; }
a { color: inherit; }
img, svg, canvas { display: block; max-width: 100%; }

:focus-visible {
  outline: 3px solid var(--color-brand-default);
  outline-offset: 2px;
  border-radius: var(--radius-sm);
}
::selection { background: var(--color-brand-default); color: var(--color-text-on-brand); }

/* --------------------------------------------------------------- utilities */

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

.section { padding-block: var(--spacing-layout-lg); }
.section--rule { border-top: 1px solid var(--color-border-subtle); }
.section--flush-top { padding-top: 0; }
/* the page is centre-aligned throughout; blocks stay full-width and centre
   their own contents rather than hugging a left column */
main { text-align: center; }

.section-head {
  margin: 0 auto var(--spacing-layout-xs);
  max-width: 900px;
}
.section-head h2 { font-size: var(--font-size-5xl); }

/* the opening block of an inner page: title, one line, nothing else */
.page-head { padding-block: var(--spacing-layout-md) var(--spacing-layout-sm); }
.page-head h1 {
  font-size: var(--font-size-5xl);
  font-weight: var(--font-weight-bold);
  letter-spacing: var(--letter-spacing-tighter);
  text-wrap: balance;
}
.page-head p {
  margin: var(--spacing-component-md) auto 0;
  max-width: 60ch;
  color: var(--color-text-secondary);
  font-size: var(--font-size-lg);
  line-height: var(--line-height-relaxed);
}

/* a section head can carry a label and a line of copy as well as the title */
.section-head .label { margin-bottom: var(--spacing-component-sm); }
.section-head p {
  margin: var(--spacing-component-md) auto 0;
  max-width: 60ch;
  color: var(--color-text-secondary);
  font-size: var(--font-size-lg);
  line-height: var(--line-height-relaxed);
}

.skip-link {
  position: absolute;
  left: var(--space-5); top: var(--space-5);
  z-index: 100;
  padding: var(--spacing-component-sm) var(--spacing-component-md);
  background: var(--color-brand-default);
  color: var(--color-text-on-brand);
  border-radius: var(--radius-lg);
  font-weight: var(--font-weight-bold);
  transform: translateY(-250%);
  transition: transform var(--duration-base) var(--ease-out);
}
.skip-link:focus-visible { transform: translateY(0); }

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

/* ==========================================================================
   BUTTON  — Figma: Buttons / Type × State × Size × Icon
   Gradient surface, inner top highlight, inner bottom shade, 2px "depth"
   plate and the "glare-glint" sheen, all taken from the component.
   ========================================================================== */

.btn {
  position: relative;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--button-gap-md);
  height: var(--button-height-md);
  padding-inline: var(--button-padding-x-md);
  border: 0;
  border-radius: var(--button-radius);
  font-family: var(--font-family-heading);
  font-size: var(--button-font-size-md);
  font-weight: var(--font-weight-semibold);
  line-height: var(--line-height-none);
  letter-spacing: 0.16px;
  text-transform: uppercase;
  text-decoration: none;
  white-space: nowrap;
  cursor: pointer;
  isolation: isolate;
  transition:
    background var(--duration-fast) var(--ease-out),
    box-shadow var(--duration-fast) var(--ease-out),
    transform var(--duration-fast) var(--ease-out),
    color var(--duration-fast) var(--ease-out);
}

.btn--primary::before,
.btn--secondary::before {
  content: "";
  position: absolute;
  left: 8px; right: 8px; top: 3px;
  height: 30%;
  border-radius: var(--radius-full);
  background: linear-gradient(180deg, rgba(255, 255, 255, 0.6), rgba(255, 255, 255, 0));
  pointer-events: none;
  z-index: -1;
}

.btn--primary {
  background: linear-gradient(180deg,
    var(--button-primary-default-gradient-start),
    var(--button-primary-default-gradient-end));
  color: var(--button-primary-default-text);
  box-shadow:
    inset 0 2px 4px rgba(255, 255, 255, 0.30),
    inset 0 -2px 2px rgba(0, 0, 0, 0.25),
    0 var(--button-depth) 0 var(--button-primary-default-drop),
    0 8px 12px rgba(0, 0, 0, 0.20);
}
.btn--primary:hover:not([disabled]) {
  background: linear-gradient(180deg,
    var(--button-primary-hover-gradient-start),
    var(--button-primary-hover-gradient-end));
}
.btn--primary:active:not([disabled]) {
  background: linear-gradient(180deg,
    var(--button-primary-clicked-gradient-start),
    var(--button-primary-clicked-gradient-end));
  transform: translateY(var(--button-depth));
  box-shadow:
    inset 0 2px 4px rgba(255, 255, 255, 0.30),
    inset 0 -2px 2px rgba(0, 0, 0, 0.25),
    0 0 0 var(--button-primary-default-drop),
    0 2px 6px rgba(0, 0, 0, 0.20);
}
.btn--primary[disabled] {
  background: linear-gradient(180deg,
    var(--button-primary-disabled-gradient-start),
    var(--button-primary-disabled-gradient-end));
  color: var(--button-primary-disabled-text);
  cursor: default;
  box-shadow: 0 var(--button-depth) 0 var(--button-primary-default-drop);
}
.btn--primary[disabled]::before { opacity: 0.25; }

/* Figma: Buttons / Type=Secondary. Flat surface rather than a gradient —
   the secondary tokens ship one surface per state, not a start/end pair. */
.btn--secondary {
  background: var(--button-secondary-default-surface);
  color: var(--button-secondary-default-text);
  box-shadow:
    inset 0 2px 4px rgba(255, 255, 255, 0.55),
    inset 0 -2px 2px rgba(0, 0, 0, 0.12),
    0 var(--button-depth) 0 var(--button-secondary-default-drop),
    0 8px 12px rgba(0, 0, 0, 0.20);
}
.btn--secondary:hover:not([disabled]) { background: var(--button-secondary-hover-surface); }
.btn--secondary:active:not([disabled]) {
  background: var(--button-secondary-clicked-surface);
  transform: translateY(var(--button-depth));
  box-shadow:
    inset 0 2px 4px rgba(255, 255, 255, 0.55),
    inset 0 -2px 2px rgba(0, 0, 0, 0.12),
    0 0 0 var(--button-secondary-default-drop),
    0 2px 6px rgba(0, 0, 0, 0.20);
}
/* the white sheen does nothing on a white surface */
.btn--secondary::before { opacity: 0.35; }

.btn--tertiary {
  background: var(--button-tertiary-default-surface);
  color: var(--button-tertiary-default-text);
  box-shadow:
    inset 0 0 0 1px var(--button-tertiary-default-stroke),
    0 var(--button-depth) 0 var(--button-tertiary-default-drop);
  backdrop-filter: blur(12px);
  -webkit-backdrop-filter: blur(12px);
}
.btn--tertiary:hover { background: var(--button-tertiary-hover-surface); }
.btn--tertiary:active {
  background: var(--button-tertiary-clicked-surface);
  transform: translateY(var(--button-depth));
  box-shadow: inset 0 0 0 1px var(--button-tertiary-default-stroke), 0 0 0 var(--button-tertiary-default-drop);
}

.btn--sm {
  height: var(--button-height-sm);
  padding-inline: var(--button-padding-x-sm);
  gap: var(--button-gap-sm);
  font-size: var(--button-font-size-sm);
}
.btn--lg { height: var(--button-height-lg); padding-inline: var(--button-padding-x-lg); }
.btn--icon-only { width: var(--button-height-md); padding-inline: 0; }
.btn--icon-only.btn--sm { width: var(--button-height-sm); }
.btn svg { width: 18px; height: 18px; flex: none; }

/* ==========================================================================
   LABEL  — Figma: Labels
   ========================================================================== */

.label {
  display: inline-flex;
  align-items: center;
  gap: var(--spacing-component-2xs);
  height: 24px;
  padding-inline: var(--spacing-component-xs);
  border-radius: var(--radius-full);
  font-family: var(--font-family-mono);
  font-size: var(--font-size-2xs);
  letter-spacing: var(--letter-spacing-wider);
  text-transform: uppercase;
  white-space: nowrap;
}
.label--brand   { background: var(--label-brand-subtle-surface); color: var(--label-brand-subtle-text); }
.label--neutral { background: var(--color-background-muted); color: var(--color-text-secondary); }
.label .dot { width: 6px; height: 6px; border-radius: var(--radius-full); background: currentColor; }

/* ==========================================================================
   HEADER  — Figma: Header / Top Navigation
   ========================================================================== */

/* Bare over the hero, and only takes on a surface once the page moves under
   it. State comes from data-scrolled on the element, set in site.js. */
.site-header {
  position: sticky;
  top: 0;
  z-index: 60;
  height: var(--header-height);
  display: flex;
  align-items: center;
  background: transparent;
  border-bottom: 1px solid transparent;
  transition:
    background var(--duration-base) var(--ease-out),
    border-color var(--duration-base) var(--ease-out);
}

/* Inner pages have no hero to sit over, so the header carries its surface
   from the top rather than waiting for a scroll. */
body.page .site-header,
.site-header[data-scrolled="true"] {
  background: color-mix(in srgb, var(--top-navigation-default-surface) 88%, transparent);
  backdrop-filter: blur(16px) saturate(140%);
  -webkit-backdrop-filter: blur(16px) saturate(140%);
  border-bottom-color: var(--top-navigation-default-stroke);
}
/* Three tracks with equal sides, so the nav lands on the centre of the page
   rather than on the centre of whatever the mark and the CTA leave over.
   Placement is explicit because the burger sits between the mark and the nav
   in the source, and auto-flow would push the actions onto a second row. */
.site-header .wrap {
  display: grid;
  grid-template-columns: 1fr auto 1fr;
  align-items: center;
  gap: var(--spacing-component-lg);
}
.site-header .brand          { grid-column: 1; justify-self: start; }
.site-header .site-nav       { grid-column: 2; justify-self: center; }
.site-header .header-actions { grid-column: 3; justify-self: end; }
.site-header .burger         { grid-column: 3; justify-self: end; }

/* brand lockup — exported from Figma: Logo / Type=Full, Color=Yes */
.brand { display: inline-flex; align-items: center; text-decoration: none; flex: none; }
.brand__logo {
  height: 46px;
  width: auto;
  filter: drop-shadow(0 4px 10px rgba(0, 0, 0, 0.55));
}

/* one-page nav — centred on the page by the header grid above */
.site-nav {
  display: flex;
  align-items: center;
  gap: var(--spacing-component-lg);
}
.site-nav a {
  position: relative;
  font-family: var(--font-family-heading);
  font-size: var(--font-size-sm);   /* desktop stays at 14px */
  font-weight: var(--font-weight-medium);
  letter-spacing: 0.4px;
  text-transform: uppercase;
  text-decoration: none;
  white-space: nowrap;
  color: var(--color-text-secondary);
  padding-block: var(--spacing-component-2xs);
  transition: color var(--duration-fast) var(--ease-out);
}
.site-nav a:hover,
.site-nav a[aria-current] { color: var(--color-text-primary); }

.site-nav a::after {
  content: "";
  position: absolute;
  left: 0; right: 0; bottom: -3px;
  height: 2px;
  border-radius: var(--radius-full);
  background: var(--color-brand-default);
  transform: scaleX(0);
  transition: transform var(--duration-base) var(--ease-out);
}
.site-nav a:hover::after,
.site-nav a[aria-current]::after { transform: scaleX(1); }

/* --- the games menu ------------------------------------------------------
   Games is a destination and a menu at once: the link keeps the whole
   library, the toggle beside it opens the categories. */
.site-nav__group {
  position: relative;
  display: flex;
  align-items: center;
  /* no gap here — see the toggle. The space you read between the word and the
     chevron is mostly the chevron's own inset inside a 44px-friendly box, not
     a gap, so closing the gap is where the room actually comes from. */
  gap: 0;
}
.site-nav__toggle {
  width: 22px;
  height: 22px;
  flex: none;
  /* the glyph sits 6.75px inside this box, so with the gap closed the visible
     space is still 6.75px; one more pixel takes it to half what it was
     (10.8px) without shrinking the target the box provides */
  margin-inline-start: -1px;
  display: grid;
  place-items: center;
  padding: 0;
  border: 0;
  background: none;
  color: var(--color-text-secondary);
  cursor: pointer;
  transition: color var(--duration-fast) var(--ease-out),
              transform var(--duration-fast) var(--ease-out);
}
/* a chevron drawn from a rotated square corner rather than a glyph, so it
   lines up with the word beside it at any size */
.site-nav__toggle span {
  width: 6px;
  height: 6px;
  border-right: 1.5px solid currentColor;
  border-bottom: 1.5px solid currentColor;
  transform: translateY(-2px) rotate(45deg);
}
.site-nav__group:hover .site-nav__toggle,
.site-nav__toggle:hover { color: var(--color-text-primary); }
.site-nav__toggle:focus-visible {
  outline: none;
  color: var(--color-brand-default);
  box-shadow: 0 0 0 3px var(--color-state-focus-brand);
  border-radius: var(--radius-sm);
}
.site-nav__toggle[aria-expanded="true"] { transform: rotate(180deg); }

.site-nav__menu {
  list-style: none;
  margin: 0;
  padding: 0;
  display: none;
}
/* on a pointer the word itself is the way to the whole library, so the list
   does not need to carry it */
.site-nav__all { display: none; }
.site-nav__toggle[aria-expanded="true"] + .site-nav__menu { display: block; }

/* the underline the top-level items carry would sit under every category */
.site-nav .site-nav__menu a::after { display: none; }

/* the same marks the landing page gives crash and slots, at nav size */
.site-nav__icon {
  display: block;
  width: 22px;
  height: 22px;
  flex: none;
  filter: drop-shadow(0 4px 8px rgba(0, 0, 0, 0.55));
}
.site-nav__icon img { display: block; width: 100%; height: 100%; object-fit: contain; }

@media (min-width: 901px) {
  .site-nav__menu {
    position: absolute;
    top: 100%;
    /* Pulled left by exactly the panel's own inset, so the category text
       starts on the same vertical as the word "Games" above it rather than
       20px inside it: the panel's padding, the items' padding, and the 1px
       border that sits outside both. */
    left: calc((var(--spacing-component-xs) + var(--spacing-component-sm) + 1px) * -1);
    z-index: 5;
    min-width: 180px;
    margin-top: 14px;
    padding: var(--spacing-component-xs);
    border: 1px solid var(--color-border-subtle);
    border-radius: var(--radius-xl);
    /* opaque, not the header's translucent treatment. The menu opens over the
       page title, and at 94% the six-times-larger word behind it read straight
       through the panel. */
    background: var(--color-surface-default);
    box-shadow: 0 24px 48px -24px rgba(0, 0, 0, 0.9);
  }
  /* the 14px gap under the trigger is dead space the pointer has to cross;
     this bridges it so the menu does not close on the way down */
  .site-nav__menu::before {
    content: "";
    position: absolute;
    left: 0; right: 0; top: -14px;
    height: 14px;
  }
  /* data-dismissed is set for the moment after a category is chosen. Without
     it the pointer is still sitting on the group when the page updates, so
     :hover puts the panel straight back over the heading that just changed —
     which is the one thing on screen saying the choice landed. It clears when
     the pointer leaves. */
  .site-nav__group:not([data-dismissed]):hover .site-nav__menu,
  .site-nav__group:not([data-dismissed]):focus-within .site-nav__menu { display: block; }

  /* the chevron turns over with the panel, on the same conditions, so it
     never points down at an open menu — the click state has its own rule
     above for the widths where there is no hover to go on */
  .site-nav__group:not([data-dismissed]):hover .site-nav__toggle,
  .site-nav__group:not([data-dismissed]):focus-within .site-nav__toggle { transform: rotate(180deg); }

  .site-nav .site-nav__menu a {
    display: flex;
    align-items: center;
    gap: var(--spacing-component-sm);
    padding: var(--spacing-component-xs) var(--spacing-component-sm);
    border-radius: var(--radius-lg);
    font-size: var(--font-size-sm);
  }
  .site-nav .site-nav__menu a:hover {
    background: var(--color-surface-raised);
    color: var(--color-text-primary);
  }
}

.header-actions {
  display: flex;
  align-items: center;
  gap: var(--spacing-component-xs);
  flex: none;
}

/* ---------------------------------------------------------------- burger
   Hidden on desktop. Under 900px it toggles the header itself into a
   full-height panel — the header carries backdrop-filter, which makes it a
   containing block for position:fixed, so a fixed drawer inside it would size
   to the header rather than the viewport. Growing the header sidesteps that
   and keeps one copy of the nav links. */
.burger {
  display: none;
  width: 44px;                    /* 44px touch target */
  height: 44px;
  align-items: center;
  justify-content: center;
  flex: none;
  padding: 0;
  border: 0;
  border-radius: var(--radius-lg);
  background: none;
  color: var(--color-text-primary);
  cursor: pointer;
}
.burger__box { position: relative; display: block; width: 22px; height: 16px; }
.burger__box i {
  position: absolute;
  left: 0;
  width: 100%;
  height: 2px;
  border-radius: var(--radius-full);
  background: currentColor;
  transition:
    top var(--duration-base) var(--ease-out),
    transform var(--duration-base) var(--ease-out),
    opacity var(--duration-fast) var(--ease-out);
}
.burger__box i:nth-child(1) { top: 0; }
.burger__box i:nth-child(2) { top: 7px; }
.burger__box i:nth-child(3) { top: 14px; }

[data-nav="open"] .burger { color: var(--color-brand-default); }
[data-nav="open"] .burger__box i:nth-child(1) { top: 7px; transform: rotate(45deg); }
[data-nav="open"] .burger__box i:nth-child(2) { opacity: 0; }
[data-nav="open"] .burger__box i:nth-child(3) { top: 7px; transform: rotate(-45deg); }

@media (max-width: 900px) {
  .brand__logo { height: 38px; }
  .burger { display: inline-flex; }

  /* collapsed */
  .site-nav,
  .header-actions { display: none; }

  /* open — the header becomes the panel */
  .site-header[data-nav="open"] {
    height: 100dvh;
    background: var(--color-background-default);
    overflow-y: auto;
    overscroll-behavior: contain;
  }
  .site-header[data-nav="open"] .wrap {
    display: grid;
    grid-template-columns: 1fr auto;
    grid-template-rows: var(--header-height) 1fr auto;
    min-height: 100dvh;
    align-items: center;
  }
  .site-header[data-nav="open"] .brand  { grid-area: 1 / 1; }
  .site-header[data-nav="open"] .burger { grid-area: 1 / 2; }

  .site-header[data-nav="open"] .site-nav {
    grid-area: 2 / 1 / 3 / 3;
    display: flex;
    flex-direction: column;
    justify-content: center;
    gap: var(--spacing-component-lg);
    margin: 0;
  }
  /* The wrap centres what it holds; this one fills the row instead, and its
     own items fill it in turn, so every word starts on the same left edge. */
  .site-header[data-nav="open"] .site-nav {
    justify-self: stretch;
    align-items: stretch;
    text-align: left;
  }
  .site-header[data-nav="open"] .site-nav > a,
  .site-header[data-nav="open"] .site-nav__group > a { font-size: calc(var(--font-size-4xl) * 1.5); }
  .site-header[data-nav="open"] .site-nav a::after { bottom: -6px; }
  /* the underline is drawn edge to edge on a full-width block; hold it to the
     word it belongs to */
  .site-header[data-nav="open"] .site-nav > a,
  .site-header[data-nav="open"] .site-nav__group > a { width: fit-content; }

  /* Games holds the row: the word on the left, the way in on the right, and
     the list wrapping onto its own line beneath the pair rather than sharing
     the row with them. */
  .site-header[data-nav="open"] .site-nav__group {
    flex-wrap: wrap;
    justify-content: space-between;
    gap: var(--spacing-component-md);
  }
  .site-header[data-nav="open"] .site-nav__toggle {
    width: 44px;
    height: 44px;
    margin-inline-start: 0;
  }
  /* the chevron keeps the meaning it has everywhere else: down at a list,
     over when that list is open. The base rule below the group handles the
     turn; this only sizes it for the panel. */
  .site-header[data-nav="open"] .site-nav__toggle span {
    width: 12px;
    height: 12px;
    border-width: 2px;
  }

  /* --- the categories, under the word ------------------------------------
     A branch, not a level of its own: Home, About and Contact stay where they
     are and the list opens between Games and About. Set well down from the
     54px items above it, indented behind a hairline, so it reads as belonging
     to the word rather than as four more of them. */
  .site-header[data-nav="open"] .site-nav__menu {
    width: 100%;
    margin-top: var(--spacing-component-lg);
    padding-left: var(--spacing-component-lg);
    border-left: 1px solid var(--color-border-subtle);
  }
  .site-header[data-nav="open"] .site-nav__menu li + li { margin-top: var(--spacing-component-md); }
  .site-header[data-nav="open"] .site-nav__menu a {
    display: flex;
    align-items: center;
    gap: var(--spacing-component-md);
    font-size: var(--font-size-3xl);
    width: fit-content;
  }
  .site-header[data-nav="open"] .site-nav__icon { width: 32px; height: 32px; }
  .site-header[data-nav="open"] .site-nav__all { display: block; }

  .site-header[data-nav="open"] .header-actions {
    grid-area: 3 / 1 / 4 / 3;
    display: flex;
    /* the wrap centres its items and this one was sized to its own label;
       stretch, then let the button take the row */
    justify-self: stretch;
    padding-bottom: var(--spacing-layout-sm);
  }
  .site-header[data-nav="open"] .header-actions .btn {
    flex: 1;
    height: var(--button-height-lg);
  }
}

/* the panel is a full-screen takeover; no reason to animate it for anyone who
   has asked for less motion */
@media (prefers-reduced-motion: reduce) {
  .burger__box i { transition: none; }
}

/* ==========================================================================
   1 · HERO
   ========================================================================== */

.hero {
  position: relative;
  overflow: hidden;
  /* Pulled up under the transparent header and padded back by the same
     amount, so the plate runs behind the nav instead of starting below it.
     With the negative margin the hero box starts at the very top of the
     viewport, so this really is 80% of one screen.
     svh, not vh: on a phone vh measures the viewport with the browser chrome
     retracted, so 80vh would be taller than 80% of what is actually on
     screen when the page loads. */
  margin-top: calc(var(--header-height) * -1);
  min-height: 80svh;
  display: flex;
  flex-direction: column;
  justify-content: center;
  padding-block: calc(var(--spacing-layout-lg) + var(--header-height)) var(--spacing-layout-lg);
  background: var(--color-background-default);
}
.hero .wrap { position: relative; z-index: 2; }

/* --- background video ---------------------------------------------------
   The clip fills the section and is cropped, never letterboxed. Everything
   readable sits above it, so the glows that used to be the hero's background
   move onto the overlay here — under the video they would be invisible. */
.hero__bg {
  position: absolute;
  inset: 0;
  z-index: 0;
  overflow: hidden;
}
.hero__video {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
}
.hero__bg::after {
  content: "";
  position: absolute;
  inset: 0;
  background:
    radial-gradient(75% 55% at 72% 8%, var(--color-brand-alpha-a20) 0%, transparent 62%),
    radial-gradient(60% 45% at 8% 4%, var(--color-accent-purple-alpha-a20) 0%, transparent 62%),
    /* The weak point of a scrim like this is its mid band, and that is exactly
       where the headline sits. 0.74 was solved for against the brightest frame
       of the loop; 0.62 left the type at 4.74:1, passing with no margin. */
    linear-gradient(180deg,
      rgba(3, 7, 18, 0.82) 0%,
      rgba(3, 7, 18, 0.74) 42%,
      rgba(3, 7, 18, 0.94) 100%);
}

@media (prefers-reduced-motion: reduce) {
  /* the cue still reads as a mouse without the wheel travelling */
  .hero__mouse::after { animation: none; opacity: 1; }
}

.hero h1 {
  /* +12px on the preferred term as well as both bounds, so it is that much
     larger at every width rather than only at the ends where the clamp bites */
  font-size: clamp(50px, 6.4vw + 12px, 94px);
  font-weight: var(--font-weight-bold);
  letter-spacing: var(--letter-spacing-tighter);
  line-height: 1;
  text-wrap: balance;
  max-width: 1120px;   /* wide enough that "the first game is a storm." holds one line */
  margin-inline: auto;
}
/* the brand yellow, as a gradient down the letterforms rather than a flat
   fill — the same treatment the hero has always given its accent word */
.hero h1 .gold {
  background: linear-gradient(180deg, var(--yellow-200) 5%, var(--yellow-500) 95%);
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
}

/* One line under the name, not two. What the studio makes and how you get it
   are the same thought, so they are set in one voice and left to read as a
   sentence — the size step from the heading is what carries the order. The
   measure is wide enough for two even lines: at 30ch, sized for four words,
   this sentence broke into three. */
.hero__sub {
  margin: var(--spacing-component-lg) auto 0;
  max-width: 34ch;
  font-family: var(--font-family-heading);
  /* one step lighter than the rest of the heading family, and set as written
     rather than in caps: this is a sentence to be read, not a label */
  font-weight: var(--font-weight-regular);
  font-size: clamp(22px, 2.2vw + 4px, 34px);
  line-height: var(--line-height-tight);
  letter-spacing: var(--letter-spacing-tight);
  text-wrap: balance;
  color: var(--color-text-primary);
}
.hero__sub span { white-space: nowrap; }

.hero__actions {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: var(--spacing-component-sm);
  /* xl, not 2xl: there is a paragraph between these and the heading now, and
     the old gap was sized for them sitting straight underneath it */
  margin-top: var(--spacing-component-xl);
}

/* --- scroll cue ---------------------------------------------------------
   Sits at the foot of the full-height hero and says "there is more". The
   box is 44px wide so it clears the minimum touch target even though the
   drawn mouse is narrower. */
.hero__scroll {
  position: absolute;
  left: 50%;
  bottom: var(--spacing-layout-xs);
  translate: -50% 0;
  z-index: 2;
  display: grid;
  place-items: center;
  width: 44px;
  height: 52px;
  border-radius: var(--radius-full);
  color: var(--color-overlay-light-a50);
  transition: color var(--duration-base) var(--ease-out);
}
.hero__scroll:hover { color: var(--color-brand-default); }

/* the mouse body, with the wheel drawn as its ::after */
.hero__mouse {
  position: relative;
  display: block;
  width: 24px;
  height: 38px;
  border: 2px solid currentColor;
  border-radius: 12px;
}
.hero__mouse::after {
  content: "";
  position: absolute;
  left: 50%;
  top: 7px;
  width: 3px;
  height: 7px;
  border-radius: var(--radius-full);
  background: currentColor;
  translate: -50% 0;
  animation: scrollWheel 1.9s ease-in-out infinite;
}

@keyframes scrollWheel {
  0%       { translate: -50% 0;    opacity: 0; }
  22%      { translate: -50% 0;    opacity: 1; }
  72%      { translate: -50% 13px; opacity: 0; }
  100%     { translate: -50% 13px; opacity: 0; }
}

/* ==========================================================================
   2 · THE GAME
   ========================================================================== */

/* Game slot — replaces the in-page build.
   Footprint pinned to the panel it superseded: 1272 × 692.8 at full width,
   so the section keeps its exact proportions. Narrower viewports fall back to
   ratios a game can actually be played at rather than a letterbox slit. */
.game-frame {
  position: relative;        /* anchors the edge overlay */
  display: block;
  width: 100%;
  max-width: 100%;
  min-width: 0;              /* never let a grid/flex parent shrink-wrap it */
  aspect-ratio: 1272 / 693;
  margin-inline: auto;

  /* No border: with border-box it would eat 2px of the container width and
     leave the iframe 1270 inside a 1272 box. The edge is drawn by ::after. */
  border: 0;
  border-radius: var(--radius-3xl);

  /* A surface one step lighter than the page, so the slot reads as a panel
     even before the game loads or wherever the build letterboxes. */
  background: var(--color-surface-default);

  --frame-ring: var(--color-border-strong);
  --frame-glow: 0 0 0 0 transparent;
  box-shadow: 0 40px 90px -40px rgba(0, 0, 0, 0.95), var(--frame-glow);

  overflow: hidden;
  transition: box-shadow var(--duration-base) var(--ease-out);
}

/* The edge, drawn ABOVE the iframe.
   An inset box-shadow on .game-frame itself paints in the background layer,
   which sits *below* child content — the iframe would cover it completely and
   the panel would have no visible boundary at all. This overlay sits on top,
   costs no layout space, and never intercepts clicks. */
/* The control under the panel. Right-aligned against the frame's own edge —
   the page is centred, so this is the one block that is not. */
.game-frame__controls {
  display: flex;
  justify-content: flex-end;
  margin-top: var(--spacing-component-sm);
}
.game-frame__controls .btn {
  display: inline-flex;
  align-items: center;
  gap: var(--spacing-component-xs);
}
.game-frame__controls svg { width: 16px; height: 16px; }

/* Full screen drops the slot's fixed shape: the ratio is there to hold a place
   in the page, and there is no page left to hold it in. Two rules, not one
   list — an unknown pseudo-class invalidates the whole selector, and the
   prefixed spelling is unknown to the browsers that take the plain one. */
.game-frame:fullscreen {
  aspect-ratio: auto;
  height: 100%;
  border-radius: 0;
}
.game-frame:fullscreen::after { border-radius: 0; }
.game-frame:-webkit-full-screen {
  aspect-ratio: auto;
  height: 100%;
  border-radius: 0;
}
.game-frame:-webkit-full-screen::after { border-radius: 0; }

.game-frame::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 1;
  border-radius: inherit;
  pointer-events: none;
  box-shadow:
    inset 0 0 0 1px var(--frame-ring),
    inset 0 1px 0 var(--color-overlay-light-a10);
  transition: box-shadow var(--duration-base) var(--ease-out);
}

.game-frame iframe {
  display: block;
  width: 100%;
  height: 100%;
  border: 0;
}

/* status glow — Figma: Border Game Status Indicator Glows.
   Driven by the build via postMessage({type:'scape:status', status}).
   Only the ring colour and the glow change; the footprint never moves. */
.game-frame[data-status="spin"] {
  --frame-ring: var(--color-brand-border);
  --frame-glow: 0 0 60px -18px var(--color-brand-alpha-a50);
}
.game-frame[data-status="win"] {
  --frame-ring: var(--color-brand-default);
  --frame-glow: 0 0 70px -12px var(--color-brand-alpha-a50);
}
.game-frame[data-status="lose"] {
  --frame-ring: var(--color-feedback-error-border);
  --frame-glow: 0 0 60px -18px rgba(239, 68, 68, 0.45);
}

@media (max-width: 900px) { .game-frame { aspect-ratio: 4 / 3; } }
@media (max-width: 640px) { .game-frame { aspect-ratio: 3 / 4; } }

/* ==========================================================================
   WHAT WE CREATE
   Lives on About, between the opening and the rules. The pitch —
   heading, one line, the way out to the games — holds the left; the four
   layers fill a quadrant grid on the right, divided by a single cross of
   hairlines rather than boxed into cards. Asymmetric on purpose: the row of
   four key points sits directly above this beat, and a second four-across row
   would read as one long list rather than a new thought.
   ========================================================================== */

.creates {
  display: grid;
  grid-template-columns: minmax(0, 0.8fr) minmax(0, 1.2fr);
  text-align: left;
}

.creates__lead h2 {
  font-size: clamp(32px, 3.6vw, 56px);
  line-height: 1;
  text-align: left;
}
.creates__lead p {
  margin: var(--spacing-component-lg) 0 var(--spacing-layout-xs);
  max-width: 34ch;
  color: var(--color-text-secondary);
  font-size: var(--font-size-md);
  line-height: var(--line-height-relaxed);
}

.creates__list {
  list-style: none;
  margin: 0;
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  padding: 0 0 0 var(--spacing-layout-sm);
  /* the rule that separates the pitch from the layers */
  border-left: 1px solid var(--color-border-subtle);
}
.create { padding: var(--spacing-component-xl); }
/* one cross of hairlines, not four boxes: the second column carries the
   vertical, the second row carries the horizontal, and the outer edges of the
   quadrant stay open */
.creates__list > :nth-child(odd)  { padding-left: 0; }
.creates__list > :nth-child(even) {
  padding-right: 0;
  border-left: 1px solid var(--color-border-subtle);
}
.creates__list > :nth-child(-n+2) { padding-top: 0; }
.creates__list > :nth-child(n+3)  {
  padding-bottom: 0;
  border-top: 1px solid var(--color-border-subtle);
}

/* picture is display:inline by default, which leaves the img no height to
   resolve against */
.create__mark {
  display: block;
  width: 48px;
  height: 48px;
  margin-bottom: var(--spacing-component-md);
  filter: drop-shadow(0 8px 14px rgba(0, 0, 0, 0.55));
}
.create__mark img { display: block; width: 100%; height: 100%; object-fit: contain; }
.create h3 {
  font-family: var(--font-family-heading);
  font-size: var(--font-size-2xl);
  text-transform: uppercase;
  letter-spacing: var(--letter-spacing-tight);
  color: var(--color-text-primary);
}
.create p {
  margin: var(--spacing-component-xs) 0 0;
  max-width: 40ch;
  color: var(--color-text-secondary);
  font-size: var(--font-size-sm);
  line-height: var(--line-height-relaxed);
}

@media (max-width: 1000px) {
  /* the pitch sits above the quadrant, and the rule lies down with it */
  .creates { grid-template-columns: minmax(0, 1fr); }
  /* once it is a full-width band rather than the left half of a split, the
     pitch centres — left-aligned it would sit against a lot of empty room */
  .creates__lead { text-align: center; }
  .creates__lead h2 { text-align: center; }
  .creates__lead p { margin-inline: auto; }
  .creates__list {
    padding: var(--spacing-layout-xs) 0 0;
    margin-top: var(--spacing-layout-xs);
    border-left: 0;
    border-top: 1px solid var(--color-border-subtle);
  }
}

@media (max-width: 600px) {
  .creates__list { grid-template-columns: minmax(0, 1fr); }
  /* same two-class shape as the nth-child rules above, and declared after
     them, so source order settles it without an !important */
  .creates__list > .create {
    padding: var(--spacing-component-lg) 0;
    border-left: 0;
    border-top: 1px solid var(--color-border-subtle);
  }
  .creates__list > :first-child { padding-top: 0; border-top: 0; }
  .creates__list > :last-child  { padding-bottom: 0; }
}

/* ==========================================================================
   THREE FACTS
   Each card carries a small "receipt" of spec rows under a dashed rule. The
   claims are the point of this section, so every card ends in something that
   looks like data rather than more prose.
   ========================================================================== */

/* Bento: the first card takes the whole left column and both rows, the other
   two stack down the right. grid-auto-rows keeps the two on the right an equal
   half each, so their combined height matches the tall one exactly. */
.facts {
  list-style: none;
  margin: 0 auto;
  padding: 0;
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  grid-auto-rows: minmax(0, 1fr);
  gap: var(--spacing-component-md);
}
.facts > :first-child { grid-column: 1; grid-row: 1 / span 2; }
.facts > :nth-child(2) { grid-column: 2; grid-row: 1; }
.facts > :nth-child(3) { grid-column: 2; grid-row: 2; }

.fact {
  position: relative;
  overflow: hidden;
  display: flex;
  flex-direction: column;
  padding: 0;                       /* the artwork runs to the card edges */
  border: 1px solid var(--color-border-subtle);
  border-radius: var(--radius-2xl);
  /* The card carries its own artwork's colour so the image and the card read
     as one surface. --fact-blend is sampled per card from the artwork at the
     crop edge (see the modifiers below); the foot darkens that same hue rather
     than cutting to a neutral, which would reintroduce a seam on the warm one. */
  --fact-blend: #04070f;
  background: linear-gradient(
    180deg,
    var(--fact-blend) 0%,
    var(--fact-blend) 58%,
    color-mix(in srgb, var(--fact-blend) 45%, #05080f) 100%);
  transition:
    border-color var(--duration-base) var(--ease-out),
    transform var(--duration-base) var(--ease-out),
    box-shadow var(--duration-base) var(--ease-out);
}

/* a light catching the top edge, widening on hover */
.fact::before {
  content: "";
  position: absolute;
  top: 0;
  left: 15%;
  right: 15%;
  height: 1px;
  z-index: 2;
  background: linear-gradient(90deg, transparent, var(--color-brand-default), transparent);
  opacity: 0.5;
  transition: opacity var(--duration-base) var(--ease-out),
              left var(--duration-base) var(--ease-out),
              right var(--duration-base) var(--ease-out);
}
.fact:hover::before { left: 0; right: 0; opacity: 1; }
.fact:hover {
  border-color: var(--color-brand-border);
  transform: translateY(-4px);
  box-shadow: 0 24px 48px -28px rgba(0, 0, 0, 0.9);
}

/* Sampled from each artwork's own dark surround — the average of its four
   corner patches — so the card continues the void the subject sits in.
   Sampling the band at the crop edge instead pulled the wallet warm, because
   its subject fills the frame and that edge lands on coins rather than on
   background. The ::after overlay fades the artwork down to this colour. */
.fact--rtp    { --fact-blend: #01030a; }
.fact--commit { --fact-blend: #110f15; }
.fact--wallet { --fact-blend: #01030a; }

/* Key art, full bleed across the top and dissolved into the card so there is
   no hard cut between image and surface. */
.fact__art {
  position: relative;
  width: 100%;
  aspect-ratio: 4 / 3;
  flex: none;
  min-height: 0;      /* flex items default to min-height:auto and would grow */
  overflow: hidden;
}

/* --- the two on the right ------------------------------------------------
   Half the height of the tall one, so stacking art over copy would leave no
   room for either. They turn on their side: art left, copy right. */
.facts > :nth-child(2),
.facts > :nth-child(3) {
  flex-direction: row;
  align-items: stretch;
}
.facts > :nth-child(2) .fact__art,
.facts > :nth-child(3) .fact__art {
  width: 42%;
  aspect-ratio: auto;
  height: auto;
}
.facts > :nth-child(2) .fact__body,
.facts > :nth-child(3) .fact__body {
  align-items: flex-start;
  justify-content: center;
  text-align: left;
  padding: var(--spacing-component-lg);
}
/* <picture> is display:inline by default, so height:100% on the img resolved
   against nothing and the artwork's intrinsic 1:1 won over the 4/3 box */
.fact__art picture {
  display: block;
  width: 100%;
  height: 100%;
}
.fact__art img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
  /* The square art scaled to the card width overflows the 4:3 box, and centring
     took ~51px off the top, clipping the padlock shackle. A negative length
     shifts the image up by a fixed amount, so the top cut stays 32px at every
     card width — a percentage would shrink with the card. */
  object-position: center -32px;
}
/* Fades to --fact-blend, not to the card surface. Measured across the three
   artworks, the bottom of the visible crop sits at rgb(0,1,7) / rgb(0,3,17) /
   rgb(38,25,9) — all darker than #111827, so fading to the surface would
   brighten into the seam. */
.fact__art::after {
  content: "";
  position: absolute;
  inset: 0;
  background: linear-gradient(
    180deg,
    transparent 42%,
    color-mix(in srgb, var(--fact-blend) 65%, transparent) 70%,
    var(--fact-blend) 96%);
}

.fact__body {
  position: relative;
  z-index: 1;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--spacing-component-sm);
  /* clear of the artwork rather than pulled up into it */
  margin-top: 0;
  padding: var(--spacing-component-xl) var(--spacing-component-lg) var(--spacing-component-xl);
  flex: 1;
}

.fact strong {
  font-family: var(--font-family-heading);
  font-size: var(--font-size-2xl);   /* 36px / 1.5 = 24px */
  font-weight: var(--font-weight-semibold);
  line-height: var(--line-height-tight);
  text-transform: uppercase;
  letter-spacing: var(--letter-spacing-tight);
  color: var(--color-brand-default);
  text-wrap: balance;
  /* No reserved block here. The artwork aligns the tops and margin-top:auto on
     the receipt aligns the bottoms, so forcing three lines of title height only
     punched a hole under the one-line "97% RTP". */
}
.fact__body > span {
  color: var(--color-text-secondary);
  font-size: var(--font-size-sm);
}

@media (max-width: 900px) {
  /* the bento needs two columns to be a bento; below that everything stacks
     and the side-by-side pair goes back to art over copy */
  .facts { grid-template-columns: minmax(0, 1fr); grid-auto-rows: auto; }
  .facts > :first-child,
  .facts > :nth-child(2),
  .facts > :nth-child(3) { grid-column: 1; grid-row: auto; }
  .facts > :nth-child(2),
  .facts > :nth-child(3) { flex-direction: column; }
  .facts > :nth-child(2) .fact__art,
  .facts > :nth-child(3) .fact__art { width: 100%; aspect-ratio: 4 / 3; }
  .facts > :nth-child(2) .fact__body,
  .facts > :nth-child(3) .fact__body {
    align-items: center;
    text-align: center;
    padding: var(--spacing-component-xl) var(--spacing-component-lg);
  }
}

@media (max-width: 780px) {
  /* stacked: there is no row to align across, so the reserved title block
     would just be a hole under the short title */
  .fact strong { min-height: 0; }
}

/* ==========================================================================
   3 · THE HOUSE
   ========================================================================== */

/* Alternating rows, no card: no border, no surface, no hover lift. The row is
   just content sitting on the page, with the middle one mirrored. */
.tiles {
  display: grid;
  gap: var(--spacing-layout-sm);
}

.tile {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  align-items: center;
  gap: var(--spacing-layout-xs);
  padding: 0;
  border: 0;
  background: none;
  text-align: left;
}

.tile__text {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: var(--spacing-component-sm);
}

/* the small 3D mark — the banner row only; the other two show theirs full
   size in the media slot instead */
.tile__mark {
  display: block;
  width: 48px;
  height: 48px;
  filter: drop-shadow(0 8px 14px rgba(0, 0, 0, 0.55));
}
.tile__mark img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: contain;
}

.tile h3 { font-size: var(--font-size-4xl); }
.tile p {
  color: var(--color-text-secondary);
  font-size: var(--font-size-md);
  max-width: 52ch;
}

/* The section mark IS the artwork here — the same 3D icon, blown up into the
   slot rather than repeated small above the title. picture is display:inline
   by default, so it is made a grid to centre the image in the box. */
.tile__media {
  display: grid;
  place-items: center;
  aspect-ratio: 4 / 3;
  min-width: 0;
  background: radial-gradient(
    52% 52% at 50% 50%,
    var(--color-brand-alpha-a10) 0%,
    transparent 70%);
}
.tile__media img {
  display: block;
  width: min(64%, 320px);
  height: auto;
  object-fit: contain;
  filter: drop-shadow(0 30px 50px rgba(0, 0, 0, 0.7));
}

/* Middle row mirrored: artwork left, text right. Source order stays
   text-then-image so the reading order is unchanged. */
.tile--flip .tile__text  { grid-column: 2; grid-row: 1; }
.tile--flip .tile__media { grid-column: 1; grid-row: 1; }

/* ---------------------------------------------------------------- BANNER ---
   03 is not a third product row — it is the floor the other two stand on, so
   it runs as a single strip: mark, then copy. This is the one place in the
   section that keeps a surface and an edge, which is what makes it read as a
   banner rather than another row. */
.tile--banner {
  grid-template-columns: auto minmax(0, 1fr);
  align-items: center;
  gap: var(--spacing-component-lg);
  padding: var(--spacing-component-lg) var(--spacing-component-xl);
  border: 1px solid var(--color-border-subtle);
  border-left: 3px solid var(--color-brand-default);
  border-radius: var(--radius-2xl);
  background:
    linear-gradient(90deg, var(--color-brand-alpha-a5) 0%, transparent 42%),
    var(--color-surface-default);
}
.tile--banner .tile__mark { width: 56px; height: 56px; }
.tile--banner .tile__text { gap: var(--spacing-component-2xs); }
.tile--banner h3 { font-size: var(--font-size-2xl); }
.tile--banner p  { font-size: var(--font-size-sm); max-width: 76ch; }

@media (max-width: 780px) {
  .tile,
  .tile--flip { grid-template-columns: 1fr; justify-items: center; text-align: center; }
  .tile__text { align-items: center; }

  /* Artwork above the copy on every row once stacked. The flip placements
     have to be unset at matching specificity — .tile--flip .tile__text beats
     a bare .tile__text and would otherwise keep forcing a second column. */
  .tile .tile__media,
  .tile--flip .tile__media { grid-row: 1; grid-column: 1; width: 100%; }
  .tile .tile__text,
  .tile--flip .tile__text { grid-row: 2; grid-column: 1; }

  .tile h3 { font-size: var(--font-size-3xl); }

  /* The banner keeps its three-across shape here — stacked it stopped reading
     as an alert. It only tightens: smaller mark, smaller gaps, less padding,
     and the copy wraps in whatever is left beside the mark. */
  .tile--banner {
    grid-template-columns: auto minmax(0, 1fr);
    justify-items: start;
    text-align: left;
    gap: var(--spacing-component-sm);
    padding: var(--spacing-component-md) var(--spacing-component-md);
  }
  .tile--banner .tile__mark { width: 40px; height: 40px; }
  .tile--banner .tile__text {
    grid-row: auto;
    grid-column: auto;
    align-items: flex-start;
  }
  .tile--banner h3 { font-size: var(--font-size-xl); }
  .tile--banner p  { font-size: var(--font-size-sm); }
}

/* ==========================================================================
   4 · FOR OPERATORS
   ========================================================================== */

/* A roadmap, not a bento. The four items are genuinely sequential — sandbox
   key on day one through to the engineer still there at launch — so they run
   along a rail.

   Written narrow-first on purpose. The wide layout has to place each stop in
   its own column, and those selectors carry more specificity than any plain
   .stop rule could; putting the vertical spine in the base and the rail in a
   min-width query means the two never compete.

   Narrow — spine down the left, every stop to its right:

        o  [01]
        |
        o  [02]

   Wide — the spine lies down and the stops alternate across it:

        [01]        [03]
          |           |
     =====o=====o=====o=====o=====
                |           |
              [02]        [04]                                              */

.roadmap {
  --node: 38px;                    /* the numbered marker on the rail */
  --spine-gap: var(--spacing-component-md);
  position: relative;
  margin: 0;
  display: grid;
  grid-template-columns: var(--node) minmax(0, 1fr);
  column-gap: var(--spine-gap);
}

/* The spine is positioned, not placed on the grid: `grid-row: 1 / -1` counts
   lines in the EXPLICIT grid, and the narrow layout declares no rows, so -1
   collapsed to the first line and the rail only reached stop 01. The wide
   layout does declare rows, so it puts this back on the grid below. */
.roadmap::before {
  content: "";
  position: absolute;
  top: 0;
  bottom: 0;
  left: calc(var(--node) / 2 - 1px);
  width: 2px;
  border-radius: var(--radius-full);
  background: linear-gradient(180deg,
    transparent 0%,
    var(--color-overlay-light-a50) 5%,
    var(--color-overlay-light-a50) 95%,
    transparent 100%);
}

.stop {
  /* The flex gap is the heading-to-copy space; the mark buys its own room
     back below, so tightening one does not close up the other. */
  --stop-gap: calc(var(--spacing-component-sm) / 4);
  position: relative;
  grid-column: 2;
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: var(--stop-gap);
  text-align: left;
  padding-bottom: var(--spacing-layout-md);
}
.stop:last-of-type { padding-bottom: 0; }

.stop__no {
  position: absolute;
  top: 0;
  left: calc((var(--node) + var(--spine-gap)) * -1);
  z-index: 1;
  display: grid;
  place-items: center;
  width: var(--node);
  height: var(--node);
  border-radius: var(--radius-full);
  /* deliberately recessive: the rail is wayfinding, not a headline */
  border: 2px solid var(--color-overlay-light-a50);
  background: var(--color-background-default);
  font-family: var(--font-family-mono);
  font-size: var(--font-size-xs);
  color: var(--color-overlay-light-a50);
  letter-spacing: var(--letter-spacing-wide);
}

/* picture is display:inline by default, which leaves the img no height to
   resolve against */
.stop__mark {
  display: block;
  width: 44px;
  height: 44px;
  margin-bottom: calc(var(--spacing-component-sm) - var(--stop-gap));
  filter: drop-shadow(0 8px 14px rgba(0, 0, 0, .55));
}
.stop__mark img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: contain;
}

.stop dt {
  font-family: var(--font-family-heading);
  font-size: var(--font-size-xl);
  text-transform: uppercase;
  letter-spacing: var(--letter-spacing-tight);
  color: var(--color-brand-default);
}
.stop dd {
  margin: 0;
  color: var(--color-text-secondary);
  font-size: var(--font-size-sm);
  line-height: var(--line-height-relaxed);
  max-width: 60ch;
}

/* 02 — the language set carried as chips instead of a slash-separated string */
.stop--langs dt {
  display: flex;
  flex-wrap: wrap;
  gap: var(--spacing-component-2xs);
}
.stop--langs dt span {
  display: inline-grid;
  place-items: center;
  min-width: 44px;
  height: 32px;
  padding-inline: var(--spacing-component-xs);
  border-radius: var(--radius-lg);
  border: 1px solid var(--color-brand-border);
  background: var(--color-brand-alpha-a10);
  font-size: var(--font-size-sm);
  letter-spacing: var(--letter-spacing-wide);
}

@media (max-width: 560px) {
  .roadmap { --spine-gap: var(--spacing-component-sm); }
}

/* ---------------------------------------------- the rail lies down ------- */

@media (min-width: 1025px) {
  .roadmap {
    --rail-band: 104px;            /* the clear strip the rail runs through */
    grid-template-columns: repeat(4, minmax(0, 1fr));
    grid-template-rows: auto var(--rail-band) auto;
    column-gap: var(--spacing-component-md);
  }

  .roadmap::before {
    position: static;              /* rows are explicit here, so 1/-1 resolves */
    grid-column: 1 / -1;
    grid-row: 2;
    align-self: center;
    justify-self: stretch;
    width: auto;
    height: 2px;
    background: linear-gradient(90deg,
      transparent 0%,
      var(--color-overlay-light-a50) 8%,
      var(--color-overlay-light-a50) 92%,
      transparent 100%);
  }

  .roadmap .stop {
    align-items: center;
    text-align: center;
    padding: 0;
  }
  .roadmap .stop dd { max-width: 34ch; }
  .roadmap .stop--langs dt { justify-content: center; }

  /* stem — joins the stop to the rail. Absolute so it can cross the row edge
     without adding height to the stop itself. */
  .stop::after {
    content: "";
    position: absolute;
    left: 50%;
    width: 2px;
    height: calc((var(--rail-band) - var(--node)) / 2);
    background: var(--color-overlay-light-a50);
    translate: -50% 0;
  }

  .roadmap .stop--up {
    grid-row: 1;
    align-self: end;
    padding-bottom: var(--spacing-component-sm);
  }
  .roadmap .stop--down {
    grid-row: 3;
    align-self: start;
    padding-top: var(--spacing-component-sm);
  }
  .stop:nth-child(1) { grid-column: 1; }
  .stop:nth-child(2) { grid-column: 2; }
  .stop:nth-child(3) { grid-column: 3; }
  .stop:nth-child(4) { grid-column: 4; }

  .stop--up::after   { top: 100%; }
  .stop--down::after { bottom: 100%; }

  .stop--up .stop__no {
    top: calc(100% + (var(--rail-band) - var(--node)) / 2);
    bottom: auto;
    left: 50%;
    translate: -50% 0;
  }
  .stop--down .stop__no {
    top: auto;
    bottom: calc(100% + (var(--rail-band) - var(--node)) / 2);
    left: 50%;
    translate: -50% 0;
  }
}

/* ==========================================================================
   5 · STUDIO
   ========================================================================== */

/* A diptych, both halves image-led: the myth on the left, the machine on the
   right. Equal columns — they are the same kind of thing. */
.studio {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: var(--spacing-component-md);
  text-align: left;
}

.studio__panel {
  position: relative;
  overflow: hidden;
  display: flex;
  border: 1px solid var(--color-border-subtle);
  border-radius: var(--radius-2xl);
  background: var(--color-surface-default);
  transition:
    border-color var(--duration-base) var(--ease-out),
    transform var(--duration-base) var(--ease-out),
    box-shadow var(--duration-base) var(--ease-out);
}
.studio__panel:hover {
  border-color: var(--color-brand-border);
  transform: translateY(-4px);
  box-shadow: 0 24px 48px -28px rgba(0, 0, 0, 0.9);
}

/* --- 01, the shrine: artwork fills the panel, copy sits in the storm --- */
.studio__panel--shrine {
  min-height: 520px;
  align-items: flex-end;
}
.studio__art {
  position: absolute;
  inset: 0;
  display: block;
}
.studio__art img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: center 30%;
}
/* Scrim, so the copy has something to sit on without dimming the whole image.
   It has to be near-opaque by 58% — that is where the text block starts, and
   the lightning bolt behind it is almost pure white. At a gentler ramp the
   eyebrow measured 1.82:1 and the first line of copy 4.41:1. */
.studio__art::after {
  content: "";
  position: absolute;
  inset: 0;
  background: linear-gradient(
    180deg,
    transparent 22%,
    rgba(3, 7, 18, 0.62) 44%,
    rgba(3, 7, 18, 0.94) 58%,
    rgba(3, 7, 18, 0.98) 100%);
}
.studio__panel--shrine .studio__body {
  position: relative;
  z-index: 1;
}

.studio__body {
  display: flex;
  flex-direction: column;
  gap: var(--spacing-component-md);
  padding: var(--spacing-layout-xs) var(--spacing-layout-sm);
}
.studio__panel h3 {
  font-size: var(--font-size-4xl);
  text-wrap: balance;
}
.studio__panel p {
  position: relative;
  z-index: 1;
  color: var(--color-text-secondary);
  font-size: var(--font-size-lg);
  line-height: var(--line-height-relaxed);
  max-width: 46ch;                 /* keeps lines inside the readable range */
}

@media (max-width: 900px) {
  .studio { grid-template-columns: 1fr; }
  .studio__panel h3 { font-size: var(--font-size-3xl); }

  /* Stop overlaying the copy on the artwork. Stacked, the panel is short
     enough that the text block starts around 40% — where the scrim is barely
     half strength — putting the eyebrow straight over the lit crossbeam.
     Below this width the image becomes a band and the copy sits on the card. */
  .studio__panel--shrine { display: block; min-height: 0; }
  .studio__art {
    position: relative;
    inset: auto;
    height: 240px;
  }
  .studio__art::after {
    background: linear-gradient(
      180deg,
      transparent 50%,
      color-mix(in srgb, var(--color-surface-default) 75%, transparent) 82%,
      var(--color-surface-default) 100%);
  }
}
@media (max-width: 560px) {
  .studio__art { height: 200px; }
  .studio__body { padding: var(--spacing-component-xl) var(--spacing-component-lg); }
}

/* the heading takes focus when the header menu filters the library, and a
   focus ring around a 48px word is a box round the whole page */
#gamesTitle:focus { outline: none; }

/* ==========================================================================
   GAMES — the library and the demo overlay
   ========================================================================== */

/* Three across, fixed rather than auto-fit: the count is the layout here, not
   whatever happens to fit. Two up on tablets, one on phones. */
.game-list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  /* 16 at every width — the component scale has no mobile remap. Each column
     is a fraction of the row, so the cards take whatever the gap gives back,
     and the 4/3 on the thumb holds their shape as they widen. */
  gap: var(--spacing-component-md);
}
@media (max-width: 900px) {
  .game-list { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
@media (max-width: 560px) {
  .game-list { grid-template-columns: minmax(0, 1fr); }
}

/* No card: the artwork is the only surface, and the name sits on the page
   under it. Matches the house rows, which lost their chrome for the same
   reason — a border round the whole thing was doing nothing the image was not
   already doing. */
.game-card {
  transition: transform var(--duration-base) var(--ease-out);
}
.game-card:hover,
.game-card:focus-within { transform: translateY(-3px); }

.game-card__thumb {
  position: relative;
  aspect-ratio: 4 / 3;
  overflow: hidden;
  border: 1px solid var(--color-border-subtle);
  border-radius: var(--radius-2xl);
  background: var(--color-surface-default);
  transition: border-color var(--duration-base) var(--ease-out);
}
.game-card:hover .game-card__thumb,
.game-card:focus-within .game-card__thumb { border-color: var(--color-brand-border); }
/* Child combinators: the lock mark is a picture/img nested in this same box,
   and a descendant selector would outrank its own rule and blur it too. */
.game-card__thumb > picture { display: block; width: 100%; height: 100%; }
.game-card__thumb > picture > img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
  transition: transform var(--duration-slow) var(--ease-out);
}

/* Unreleased titles are held back behind a blur: the colour and the shape of
   the art come through, the logo does not. The upscale is what keeps it
   clean — a blur samples past the edges of its own box, so without it the
   frame would fade out into nothing at all four sides. */
.game-card__thumb:not(.game-card__thumb--live) > picture > img {
  filter: blur(12px);
  transform: scale(1.12);
}
.game-card__thumb::after {
  content: "";
  position: absolute;
  inset: 0;
  /* Lightened now the blur has gone: with art worth looking at underneath,
     the scrim only has to hold "Coming soon" legible, not hide the picture.
     Measured against all twenty-three — this is the lightest stop set that
     still clears 4.5:1 on the worst of them, at 5.98. One step further down
     (0.62/0.48/0.28) lands at 4.45 and fails. */
  background: radial-gradient(
    62% 62% at 50% 50%,
    rgba(3, 7, 18, 0.70) 0%,
    rgba(3, 7, 18, 0.56) 55%,
    rgba(3, 7, 18, 0.34) 100%);
}
/* the finished game is not hidden — it only dims enough to carry the button */
.game-card__thumb--live::after {
  background: linear-gradient(180deg, rgba(3, 7, 18, 0.30) 0%, rgba(3, 7, 18, 0.62) 100%);
  opacity: 0.55;
  transition: opacity var(--duration-base) var(--ease-out);
}
.game-card:hover .game-card__thumb--live::after,
.game-card:focus-within .game-card__thumb--live::after { opacity: 1; }
.game-card:hover .game-card__thumb--live > picture > img { transform: scale(1.04); }

/* A stack, not a single centred control. align-content rather than
   align-items: with two auto rows in a box this tall, centring the items
   would centre each one inside its own stretched row and split the pair
   across the artwork. justify-items stays at its stretch default on purpose,
   so both take the width of the wider label and read as a matched pair. */
@media (prefers-reduced-motion: reduce) {
  .game-card,
  .game-card__thumb > picture > img { transition: none; }
  .game-card:hover { transform: none; }
}

/* the lock stack — no plate behind it, so the scrim on the thumb does the
   work of making the words readable */
.game-card__lock {
  position: absolute;
  top: 50%;
  left: 50%;
  z-index: 1;
  translate: -50% -50%;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--spacing-component-sm);
  font-family: var(--font-family-heading);
  font-size: var(--font-size-lg);
  letter-spacing: var(--letter-spacing-wider);
  text-transform: uppercase;
  color: var(--color-text-primary);
  white-space: nowrap;
  text-shadow: 0 2px 14px rgba(3, 7, 18, 0.9);
}
.game-card__lock-mark {
  display: block;
  width: 44px;
  height: 44px;
  filter: drop-shadow(0 8px 16px rgba(0, 0, 0, 0.7));
}
.game-card__lock-mark img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: contain;
}

/* --- the rail ------------------------------------------------------------
   Same list, laid on its side: four across on a wide screen, scrolling by a
   screenful at a time. grid-auto-columns rather than a track list, because a
   carousel's width is "a quarter of what you can see", not "a quarter of the
   content". */
/* flow-root so the rail's negative margins stay inside and actually pull the
   height back — left to collapse, they move this box up instead of shrinking
   it, and the row keeps the 8px it was meant to give back */
.carousel { position: relative; display: flow-root; }
.game-list--rail {
  /* the width of a card is a quarter of what you can see, less the three gaps
     between the four of them — so the track follows the gap on its own */
  --rail-gap: var(--spacing-component-xs);
  --rail-fade: 48px;
  grid-template-columns: none;
  grid-auto-flow: column;
  grid-auto-columns: calc((100% - 3 * var(--rail-gap)) / 4);
  gap: var(--rail-gap);

  /* The edges fade the cards themselves rather than laying a coloured panel
     over them, so this holds on any background the section is ever given.
     Each side only fades when there is something that way to scroll to —
     the JS sets the attributes, and a fade of 0 leaves the mask fully
     opaque, so at rest the row has hard edges like any other. */
  --fade-l: 0px;
  --fade-r: 0px;
  -webkit-mask-image: linear-gradient(90deg,
    transparent 0, #000 var(--fade-l),
    #000 calc(100% - var(--fade-r)), transparent 100%);
  mask-image: linear-gradient(90deg,
    transparent 0, #000 var(--fade-l),
    #000 calc(100% - var(--fade-r)), transparent 100%);
  overflow-x: auto;
  overscroll-behavior-inline: contain;
  scroll-snap-type: x mandatory;
  scroll-behavior: smooth;
  scrollbar-width: none;
  /* A horizontal scroller cannot have a visible vertical axis — the pair is
     forced to auto — so the box has to clip, and it was cutting the 3px a
     card rises by on hover. The padding gives that lift somewhere to go and
     the matching negative margin takes the height straight back out of the
     layout, so the rail still sits exactly on its cards. */
  overflow-y: hidden;
  padding-block: var(--spacing-component-xs);
  margin-block: calc(var(--spacing-component-xs) * -1);
}
.game-list--rail::-webkit-scrollbar { display: none; }
.game-list--rail > .game-card { scroll-snap-align: start; }

.carousel:not([data-at-start]) .game-list--rail { --fade-l: var(--rail-fade); }
.carousel:not([data-at-end])   .game-list--rail { --fade-r: var(--rail-fade); }

.carousel__nav {
  position: absolute;
  top: 50%;
  translate: 0 -50%;
  z-index: 3;
  width: 44px;
  height: 44px;
  display: grid;
  place-items: center;
  padding: 0;
  border: 1px solid var(--color-border-strong);
  border-radius: var(--radius-full);
  background: color-mix(in srgb, var(--color-surface-default) 88%, transparent);
  backdrop-filter: blur(12px);
  -webkit-backdrop-filter: blur(12px);
  color: var(--color-text-primary);
  font-size: 30px;
  line-height: 1;
  cursor: pointer;
  transition: border-color var(--duration-fast) var(--ease-out),
              color var(--duration-fast) var(--ease-out),
              opacity var(--duration-fast) var(--ease-out);
}
.carousel__nav:hover { border-color: var(--color-brand-border); color: var(--color-brand-default); }
.carousel__nav:focus-visible { outline: none; box-shadow: 0 0 0 3px var(--color-state-focus-brand); }
/* at an end there is nothing to go to: it stays in place rather than moving,
   so the row does not shift under the pointer */
.carousel__nav[disabled] { opacity: 0.25; cursor: default; }
.carousel__nav--prev { left: var(--spacing-component-md); }
.carousel__nav--next { right: var(--spacing-component-md); }

@media (max-width: 900px) {
  .game-list--rail {
    grid-auto-columns: 46%;
    scroll-padding-inline: var(--page-gutter);
    margin-inline: calc(var(--page-gutter) * -1);
    padding-inline: var(--page-gutter);
  }
  /* a thumb does the scrolling here, and a 44px button over a 46% card is in
     the way of the thing it is meant to help you see */
  .carousel__nav { display: none; }
}
@media (max-width: 560px) {
  .game-list--rail { grid-auto-columns: 78%; }
}

/* the heading over a list on the landing page */
.game-list__title {
  margin: var(--spacing-layout-sm) 0 var(--spacing-component-lg);
  font-family: var(--font-family-heading);
  font-size: var(--font-size-xl);
  text-transform: uppercase;
  letter-spacing: var(--letter-spacing-tight);
  color: var(--color-text-secondary);
}

/* --- the card as a way in -------------------------------------------------
   The card is its artwork and nothing else, so the link is the card: an empty
   box over the whole of it, carrying the game's name for anyone who cannot
   see the picture. The page it opens is where the demo lives. */
.game-card { position: relative; }
.game-card__link {
  position: absolute;
  inset: 0;
  z-index: 2;
  border-radius: var(--radius-2xl);
}
/* the ring goes on the thumb's own border, which is the edge you can see —
   an outline on the link would trace the same box twice */
.game-card__link:focus-visible { outline: none; }
.game-card:focus-within .game-card__thumb { border-color: var(--color-brand-border); }
.game-card__link:focus-visible ~ .game-card__thumb,
.game-card:has(.game-card__link:focus-visible) .game-card__thumb {
  box-shadow: 0 0 0 3px var(--color-state-focus-brand);
}

/* ==========================================================================
   GAME DETAIL — one page per finished title
   Poster, spec sheet, the read, the round, the art. Everything on it is left
   aligned against the centred rest of the site, because it is a document
   about one thing rather than a pitch.
   ========================================================================== */

.gamepage { text-align: left; }

/* --- the poster ---------------------------------------------------------- */
/* No crumb bar above it — the artwork starts under the header the way About's
   opening does, and the way back is a control on the picture instead. */
.gamehead__banner {
  position: relative;
  display: flex;
  align-items: flex-end;
  min-height: min(74svh, 640px);
  overflow: hidden;
  border-block: 1px solid var(--color-border-subtle);
}

/* Carries .wrap for its measure and gutter, and is laid over the top of the
   banner. It gets its own name rather than being reached through
   `.gamehead__banner .wrap`, so it and the intro below never compete for the
   same rule — that pair would tie on specificity and be decided by whichever
   was written last. */
.gamehead__nav {
  position: absolute;
  top: var(--spacing-component-lg);
  left: 0;
  right: 0;
  z-index: 2;                     /* over the scrim, which is ::after at auto */
}
.gamehead__art { position: absolute; inset: 0; display: block; }
.gamehead__art img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
  /* the bolt runs up the middle; hold the top of it in frame on wide crops */
  object-position: 50% 38%;
}
/* Two gradients, not one. The vertical pass sits the read on a dark bottom
   edge and closes on the page colour at 100% so the banner has no seam where
   it meets the section under it. The horizontal pass darkens the side the
   words are on and leaves the side the artwork is on alone.

   The split is measured, not styled by eye. A single vertical scrim heavy
   enough for the lede at 375 — where the cover crop drags the bolt in behind
   the text — put the whole picture behind a flat grey. Sampling the real crop
   and compositing candidates gave this pair: the lede's worst glyph line goes
   from 3.88:1 (a fail) to 4.87:1, and the subject keeps its light. */
.gamehead__banner::after {
  content: "";
  position: absolute;
  inset: 0;
  background:
    linear-gradient(
      180deg,
      rgba(3, 7, 18, 0.70) 0%,
      rgba(3, 7, 18, 0.28) 30%,
      rgba(3, 7, 18, 0.88) 78%,
      #030712 100%),
    linear-gradient(
      90deg,
      rgba(3, 7, 18, 0.42) 0%,
      rgba(3, 7, 18, 0.10) 70%,
      rgba(3, 7, 18, 0) 100%);
}
.gamehead__intro {
  position: relative;
  z-index: 1;
  padding-block: var(--spacing-layout-sm);
}
.gamehead h1 {
  margin: var(--spacing-component-sm) 0 0;
  font-size: clamp(48px, 7.4vw, 96px);
  font-weight: var(--font-weight-bold);
  line-height: 0.92;
  letter-spacing: var(--letter-spacing-tighter);
}
.gamehead__type {
  margin: var(--spacing-component-sm) 0 0;
  font-family: var(--font-family-mono);
  font-size: var(--font-size-xs);
  letter-spacing: var(--letter-spacing-widest);
  text-transform: uppercase;
  color: var(--color-brand-default);
}
.gamehead__lede {
  margin: var(--spacing-component-lg) 0 0;
  max-width: 48ch;
  color: var(--color-text-primary);
  font-size: var(--font-size-lg);
  line-height: var(--line-height-relaxed);
}
.gamehead__actions {
  display: flex;
  flex-wrap: wrap;
  gap: var(--spacing-component-md);
  margin-top: var(--spacing-component-xl);
}

/* --- key highlights ------------------------------------------------------
   One slab split by hairlines rather than six cards with six borders: the
   figures belong together, and a gap between them would read as six separate
   claims instead of one spec sheet. */
.highlights {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 1px;
  border: 1px solid var(--color-border-subtle);
  border-radius: var(--radius-2xl);
  background: var(--color-border-subtle);   /* shows through the 1px gap */
  overflow: hidden;
}
.highlight {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr);
  align-items: start;
  gap: var(--spacing-component-md);
  padding: var(--spacing-component-xl);
  background: var(--color-background-default);
}
.highlight__mark {
  display: block;
  width: 40px;
  height: 40px;
  filter: drop-shadow(0 8px 14px rgba(0, 0, 0, 0.55));
}
.highlight__mark img { display: block; width: 100%; height: 100%; object-fit: contain; }
.highlight strong {
  display: block;
  font-family: var(--font-family-heading);
  font-size: var(--font-size-2xl);
  font-weight: var(--font-weight-medium);
  line-height: var(--line-height-tight);
  text-transform: uppercase;
  letter-spacing: var(--letter-spacing-tight);
  color: var(--color-text-primary);
}
.highlight span {
  display: block;
  margin-top: var(--spacing-component-2xs);
  color: var(--color-text-secondary);
  font-size: var(--font-size-sm);
  line-height: var(--line-height-normal);
}

/* the heading over a block on this page — left, like everything else here */
.blockhead { margin-bottom: var(--spacing-layout-xs); }
.blockhead h2 { font-size: var(--font-size-4xl); text-align: left; }
.blockhead p {
  margin: var(--spacing-component-md) 0 0;
  max-width: 62ch;
  color: var(--color-text-secondary);
  font-size: var(--font-size-lg);
  line-height: var(--line-height-relaxed);
}

/* --- the read, with the sheet beside it ---------------------------------- */
.gamebody {
  display: grid;
  grid-template-columns: minmax(0, 1.3fr) minmax(0, 0.7fr);
  gap: var(--spacing-layout-sm);
  align-items: start;
}
.gamebody__copy h2 { font-size: var(--font-size-4xl); text-align: left; }
.gamebody__copy p {
  margin: var(--spacing-component-lg) 0 0;
  max-width: 62ch;
  color: var(--color-text-secondary);
  font-size: var(--font-size-md);
  line-height: var(--line-height-relaxed);
}

.spec {
  padding: var(--spacing-component-xl);
  border: 1px solid var(--color-border-subtle);
  border-radius: var(--radius-2xl);
  background: color-mix(in srgb,
    var(--color-surface-default) 50%, var(--color-background-default));
}
.spec h3 {
  margin: 0 0 var(--spacing-component-sm);
  font-size: var(--font-size-lg);
  text-transform: uppercase;
  letter-spacing: var(--letter-spacing-tight);
}
.spec dl { margin: 0; }
.spec__row {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--spacing-component-md);
  padding-block: var(--spacing-component-sm);
  border-bottom: 1px solid var(--color-border-subtle);
}
.spec__row:last-of-type { border-bottom: 0; padding-bottom: 0; }
.spec dt {
  flex: none;
  color: var(--color-text-secondary);
  font-size: var(--font-size-sm);
}
/* The values carry the heading face, not the mono one. Mono is the system's
   voice for data you are meant to copy — an address, a domain — and these are
   answers, so they take the same Oswald treatment as every other figure on the
   page. The labels opposite stay in the body face, which is what separates the
   two columns now that the fonts do the work the alignment used to. */
.spec dd {
  margin: 0;
  text-align: right;
  color: var(--color-text-primary);
  font-family: var(--font-family-heading);
  font-size: var(--font-size-md);
  font-weight: var(--font-weight-medium);
  line-height: var(--line-height-tight);
  text-transform: uppercase;
  letter-spacing: var(--letter-spacing-tight);
}
/* PLACEHOLDER — volatility, max win and bet ranges are deliberately not on
   this sheet. They are real regulated figures and none of them exist yet;
   this line says where they live instead of inventing them. */
.spec__note {
  margin: var(--spacing-component-lg) 0 0;
  padding-top: var(--spacing-component-lg);
  border-top: 1px solid var(--color-border-subtle);
  color: var(--color-text-secondary);
  font-size: var(--font-size-xs);
  line-height: var(--line-height-normal);
}
.spec__note a { color: var(--color-text-link); }

/* --- how a round works --------------------------------------------------- */
.round {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: var(--spacing-component-2xl);
}
.round li { padding-top: var(--spacing-component-lg); border-top: 1px solid var(--color-border-subtle); }
.round__mark {
  display: block;
  width: 44px;
  height: 44px;
  margin-bottom: var(--spacing-component-md);
  filter: drop-shadow(0 8px 14px rgba(0, 0, 0, 0.55));
}
.round__mark img { display: block; width: 100%; height: 100%; object-fit: contain; }
.round h3 {
  margin: 0;
  font-size: var(--font-size-xl);
  text-transform: uppercase;
  letter-spacing: var(--letter-spacing-tight);
}
.round p {
  margin: var(--spacing-component-sm) 0 0;
  color: var(--color-text-secondary);
  font-size: var(--font-size-md);
  line-height: var(--line-height-relaxed);
}

/* --- the art ------------------------------------------------------------- */
/* One row, not a block of four. It sits directly under the poster, where it
   reads as the strip you look at before you read anything — so it stays on a
   single line and the eye crosses it once. */
.gallery {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  grid-auto-flow: column;
  grid-auto-columns: minmax(0, 1fr);
  gap: var(--spacing-component-lg);
}
.shot { margin: 0; }
.shot__btn {
  display: block;
  width: 100%;
  padding: 0;
  overflow: hidden;
  border: 1px solid var(--color-border-subtle);
  border-radius: var(--radius-2xl);
  background: var(--color-surface-default);
  cursor: zoom-in;
  transition: border-color var(--duration-base) var(--ease-out);
}
.shot__btn img {
  display: block;
  width: 100%;
  /* height:auto is load-bearing, not tidiness. The width and height attributes
     on the tag land as presentational hints, and a hint beats `auto` — so
     without this the height stays at the file's own 896px and aspect-ratio is
     never consulted, because aspect-ratio only fills in a dimension that is
     actually auto. */
  height: auto;
  aspect-ratio: 4 / 3;
  object-fit: cover;
  transition: transform var(--duration-slow) var(--ease-out);
}
.shot__btn:hover { border-color: var(--color-brand-border); }
.shot__btn:hover img { transform: scale(1.03); }
.shot__btn:focus-visible {
  outline: none;
  border-color: var(--color-brand-border);
  box-shadow: 0 0 0 3px var(--color-state-focus-brand);
}
@media (prefers-reduced-motion: reduce) {
  .shot__btn img { transition: none; }
  .shot__btn:hover img { transform: none; }
}

/* --- the sticky bar -------------------------------------------------------
   The poster carries the two things you can do with this game, and once it
   has scrolled away there is nothing to act on until the footer. This rides
   in at the foot of the screen to cover that stretch, and rides out again at
   the footer, where the signup is the thing in front of you.

   visibility, not just opacity: a bar sitting invisibly over the page would
   still take clicks and still take tab focus. The 0s visibility transition
   delayed by the animation's own length is what lets it fade out and only
   then leave. */
.gamebar {
  position: fixed;
  inset-inline: 0;
  bottom: 0;
  z-index: 70;
  padding-block: var(--spacing-component-md);
  padding-bottom: calc(var(--spacing-component-md) + env(safe-area-inset-bottom));
  border-top: 1px solid var(--color-border-subtle);
  background: color-mix(in srgb, var(--color-background-subtle) 92%, transparent);
  backdrop-filter: blur(16px) saturate(140%);
  -webkit-backdrop-filter: blur(16px) saturate(140%);
  visibility: hidden;
  opacity: 0;
  transform: translateY(100%);
  transition:
    transform var(--duration-base) var(--ease-out),
    opacity var(--duration-base) var(--ease-out),
    visibility 0s linear var(--duration-base);
}
.gamebar[data-shown="true"] {
  visibility: visible;
  opacity: 1;
  transform: none;
  transition:
    transform var(--duration-base) var(--ease-out),
    opacity var(--duration-base) var(--ease-out),
    visibility 0s;
}
.gamebar .wrap {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--spacing-component-lg);
}
.gamebar__id { min-width: 0; }
/* The heading treatment h1-h3 carry, spelled out: this is a <p> because it
   heads nothing, so it inherits none of it — and it was rendering at the body
   weight of 400 against the system's 600. */
.gamebar__name {
  margin: 0;
  font-family: var(--font-family-heading);
  font-weight: var(--font-weight-semibold);
  font-size: var(--font-size-xl);
  line-height: var(--line-height-tight);
  letter-spacing: var(--letter-spacing-tight);
  text-transform: uppercase;
  color: var(--color-text-primary);
}
/* Body face, not mono. Mono is this system's voice for strings you are meant
   to copy — an address, a domain — and this is a line about the game. Sized
   and spaced from the scale rather than a 2px nudge. */
.gamebar__meta {
  margin-top: var(--spacing-component-3xs);
  font-size: var(--font-size-sm);
  line-height: var(--line-height-normal);
  color: var(--color-text-secondary);
  white-space: nowrap;
}
.gamebar__actions {
  flex: none;
  display: flex;
  align-items: center;
  gap: var(--spacing-component-md);
}

@media (prefers-reduced-motion: reduce) {
  .gamebar { transition: visibility 0s linear var(--duration-base); transform: none; }
  .gamebar[data-shown="true"] { transition: visibility 0s; }
}

@media (max-width: 700px) {
  /* the name alone: the meta line and a pair of buttons will not share 343px,
     and the buttons are the reason the bar is there */
  .gamebar__meta { display: none; }
  .gamebar .wrap { gap: var(--spacing-component-md); }
  .gamebar__actions { gap: var(--spacing-component-xs); }
}
@media (max-width: 480px) {
  /* below this the name loses too, rather than the buttons wrapping */
  .gamebar__id { display: none; }
  .gamebar__actions { flex: 1; }
  .gamebar__actions .btn { flex: 1; }
}

/* --- narrower ------------------------------------------------------------ */
@media (max-width: 1000px) {
  .gamebody { grid-template-columns: minmax(0, 1fr); gap: var(--spacing-layout-xs); }
  .highlights { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .round { grid-template-columns: minmax(0, 1fr); gap: var(--spacing-component-xl); }
}
@media (max-width: 700px) {
  .gamehead__banner { min-height: min(80svh, 560px); }
  .highlights { grid-template-columns: minmax(0, 1fr); }
  .highlight { padding: var(--spacing-component-lg); }
  .gamehead__actions .btn { width: 100%; }
}

/* Four across stops being a row and starts being four slivers somewhere
   around here, so below it the row scrolls instead of shrinking. The negative
   margin cancels the wrap's gutter so the rail runs to both screen edges, and
   the padding puts it back on the first and last card — the strip bleeds off
   the side, the pictures stay on the grid. */
@media (max-width: 1000px) {
  .gallery {
    grid-auto-columns: 78%;
    overflow-x: auto;
    overscroll-behavior-inline: contain;
    scroll-snap-type: x mandatory;
    scroll-padding-inline: var(--page-gutter);
    margin-inline: calc(var(--page-gutter) * -1);
    padding-inline: var(--page-gutter);
    /* the focus ring is drawn outside the card and would be clipped flat */
    padding-block: 3px;
    scrollbar-width: none;
  }
  .gallery::-webkit-scrollbar { display: none; }
  .shot { scroll-snap-align: start; }
}

/* --- modal ----------------------------------------------------------------
   The hero's plug-and-test form. Same shell language as the lightbox — dimmed
   blurred backdrop, a bar with a title and a 44px close — but the stage is a
   scrolling panel rather than a fixed-ratio frame. */
.modal {
  position: fixed;
  inset: 0;
  z-index: 90;
  display: grid;
  place-items: center;
  padding: var(--spacing-component-lg);
}
.modal[hidden] { display: none; }

.modal__backdrop {
  position: absolute;
  inset: 0;
  background: rgba(3, 7, 18, 0.88);
  backdrop-filter: blur(6px);
  -webkit-backdrop-filter: blur(6px);
}

.modal__panel {
  position: relative;
  z-index: 1;
  width: min(100%, 880px);
  /* the form is taller than a short laptop screen, so the panel caps out and
     scrolls its own body rather than pushing the close button off-screen */
  max-height: calc(100dvh - var(--spacing-layout-xs) * 2);
  display: flex;
  flex-direction: column;
  border: 1px solid var(--color-border-subtle);
  border-radius: var(--radius-2xl);
  background: var(--color-background-default);
  box-shadow: 0 40px 90px -40px rgba(0, 0, 0, 0.95);
  overflow: hidden;
}

.modal__bar {
  flex: none;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--spacing-component-md);
  padding: var(--spacing-component-lg) var(--spacing-component-xl);
  border-bottom: 1px solid var(--color-border-subtle);
}
.modal__title {
  margin: 0;
  font-family: var(--font-family-heading);
  font-size: var(--font-size-2xl);
  text-transform: uppercase;
  letter-spacing: var(--letter-spacing-tight);
  color: var(--color-text-primary);
  text-align: left;
}
.modal__close {
  width: 44px;                    /* 44px touch target */
  height: 44px;
  flex: none;
  display: grid;
  place-items: center;
  padding: 0;
  border: 1px solid var(--color-border-strong);
  border-radius: var(--radius-full);
  background: var(--color-surface-default);
  color: var(--color-text-primary);
  font-size: 24px;
  line-height: 1;
  cursor: pointer;
  transition:
    border-color var(--duration-fast) var(--ease-out),
    color var(--duration-fast) var(--ease-out);
}
.modal__close:hover { border-color: var(--color-brand-border); color: var(--color-brand-default); }
.modal__close:focus-visible {
  outline: none;
  box-shadow: 0 0 0 3px var(--color-state-focus-brand);
}

.modal__body {
  padding: var(--spacing-component-xl);
  overflow-y: auto;
  text-align: left;
}
.modal__lede {
  margin: 0 0 var(--spacing-layout-xs);
  max-width: 52ch;
  color: var(--color-text-secondary);
  font-size: var(--font-size-md);
  line-height: var(--line-height-relaxed);
}
/* inside the modal the form is the whole width of the body, not a centred
   block on a page */
.modal__body .enquiry { max-width: none; margin-inline: 0; }
/* and the submit fills that width — it is the only action in the dialog, so
   there is nothing for it to sit beside. The status line drops under it. */
.modal__body .enquiry__foot {
  flex-direction: column;
  align-items: stretch;
  gap: var(--spacing-component-sm);
}
.modal__body .enquiry__foot .btn { width: 100%; }

/* On a phone this stops being a dialog floating on the page and becomes a
   full-screen overlay: edge to edge, top to bottom, no corners. 700px is where
   the form itself drops to one column, so both switch together. */
@media (max-width: 700px) {
  .modal { padding: 0; place-items: stretch; }
  .modal__panel {
    width: 100%;
    max-width: none;
    height: 100dvh;
    max-height: none;
    border: 0;
    border-radius: 0;
    box-shadow: none;
  }
  .modal__bar  { padding: var(--spacing-component-md) var(--page-gutter); }
  .modal__body {
    padding: var(--spacing-component-lg) var(--page-gutter);
    /* clear the home indicator, or the send button sits under it */
    padding-bottom: calc(var(--spacing-component-lg) + env(safe-area-inset-bottom));
  }
}

/* --- the demo overlay -----------------------------------------------------
   Full screen at every width, not a dialog that happened to go full screen on
   a phone. A bar across the top carries the title, the way in and the close;
   everything under it is the build. The letterboxed 1272x693 stage it used to
   sit in wasted most of a desktop screen and landed about 200px tall on a
   375. */
.lightbox {
  position: fixed;
  inset: 0;
  z-index: 90;
  display: grid;
  place-items: stretch;
}
.lightbox[hidden] { display: none; }

/* still here: it covers the page for the frame between unhiding the overlay
   and the panel painting, and the image viewer sits on it properly */
.lightbox__backdrop {
  position: absolute;
  inset: 0;
  background: rgba(3, 7, 18, 0.88);
  backdrop-filter: blur(6px);
  -webkit-backdrop-filter: blur(6px);
}

.lightbox__panel {
  position: relative;
  z-index: 1;
  width: 100%;
  height: 100dvh;
  display: flex;
  flex-direction: column;
  background: var(--color-background-default);
}

.lightbox__bar {
  flex: none;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--spacing-component-md);
  padding: var(--spacing-component-md) var(--page-gutter);
  border-bottom: 1px solid var(--color-border-subtle);
}
.lightbox__title {
  margin: 0;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  font-family: var(--font-family-heading);
  font-size: var(--font-size-2xl);
  text-transform: uppercase;
  letter-spacing: var(--letter-spacing-tight);
  color: var(--color-text-primary);
}
/* the way in and the way out, together on the right */
.lightbox__actions {
  flex: none;
  display: flex;
  align-items: center;
  gap: var(--spacing-component-md);
}
/* on a wide screen the pair just sits in that row like anything else */
.lightbox__steps {
  display: flex;
  align-items: center;
  gap: var(--spacing-component-md);
}
.lightbox__close,
.lightbox__nav,
.lightbox__full {
  width: 44px;                    /* 44px touch target */
  height: 44px;
  flex: none;
  display: grid;
  place-items: center;
  padding: 0;
  border: 1px solid var(--color-border-strong);
  border-radius: var(--radius-full);
  background: var(--color-surface-default);
  color: var(--color-text-primary);
  font-size: 24px;
  line-height: 1;
  cursor: pointer;
  transition:
    border-color var(--duration-fast) var(--ease-out),
    color var(--duration-fast) var(--ease-out);
}
.lightbox__close:hover,
.lightbox__nav:hover,
.lightbox__full:hover { border-color: var(--color-brand-border); color: var(--color-brand-default); }
.lightbox__close:focus-visible,
.lightbox__nav:focus-visible,
.lightbox__full:focus-visible {
  outline: none;
  box-shadow: 0 0 0 3px var(--color-state-focus-brand);
}

/* Drawn rather than typed: the corner glyphs (⤢, ⛶) are missing from enough
   system fonts to land as a box, and this button sits next to a close that
   never does. One icon per state, the pressed one carrying the way back.
   Keyed on the attribute the script binds, so the overlay's button and the
   landing page's share it and only differ in size. */
[data-fullscreen] svg {
  fill: none;
  stroke: currentColor;
  stroke-width: 2;
  stroke-linecap: round;
  stroke-linejoin: round;
}
[data-fullscreen] .icon-collapse { display: none; }
[data-fullscreen][aria-pressed="true"] .icon-expand { display: none; }
[data-fullscreen][aria-pressed="true"] .icon-collapse { display: block; }
.lightbox__full svg { width: 20px; height: 20px; }
/* the chevrons are lighter glyphs than the multiplication sign and sit small
   at the close button's 24px */
.lightbox__nav { font-size: 30px; }
.lightbox__count {
  margin: 0;
  font-family: var(--font-family-mono);
  font-size: var(--font-size-sm);
  letter-spacing: var(--letter-spacing-wide);
  color: var(--color-text-secondary);
  font-variant-numeric: tabular-nums;
}
.lightbox__count:empty { display: none; }

/* everything the bar leaves over, edge to edge */
.lightbox__stage {
  position: relative;
  flex: 1;
  min-height: 0;
  overflow: hidden;
  background: var(--color-surface-default);
}
.lightbox__stage iframe {
  display: block;
  width: 100%;
  height: 100%;
  border: 0;
}

@media (max-width: 700px) {
  .lightbox__title { font-size: var(--font-size-lg); }
  .lightbox__actions { gap: var(--spacing-component-xs); }
  /* the label loses its opening words rather than the button losing its place
     in the bar — see the markup, where the two halves sit with no whitespace
     between them so nothing is left behind when this hides one */
  .label-more { display: none; }
}

/* --- the image viewer -----------------------------------------------------
   The gallery on a game page borrows the overlay's shell but not its job: a
   picture is something you look at, so it stays a centred dialog while the
   demo behind this rule is full screen. It has to sit after the overlay's own
   rules — .lightbox--media and .lightbox are both a single class, so the two
   tie on specificity and whichever is written last wins. Placement, not
   !important. */
.lightbox--media {
  place-items: center;
  padding: var(--spacing-component-lg) var(--page-gutter);
}
.lightbox--media .lightbox__panel {
  /* solved for a 4:3 picture: any wider and a tall image is capped by its own
     height and floats in the middle of an over-wide box */
  width: min(100%, calc((100dvh - 160px) * 4 / 3));
  max-width: 1100px;
  height: auto;
  gap: var(--spacing-component-sm);
  background: transparent;
}
.lightbox--media .lightbox__bar { padding: 0; border-bottom: 0; }
.lightbox--media .lightbox__stage {
  flex: none;
  border-radius: var(--radius-2xl);
  background: transparent;
  box-shadow: none;
}
.lightbox--media .lightbox__stage img {
  display: block;
  width: 100%;
  height: auto;
  max-height: calc(100dvh - 160px);
  object-fit: contain;
}

@media (max-width: 700px) {
  .lightbox--media .lightbox__panel { width: 100%; max-width: none; }
  /* the picture gives back the room the arrows now take at the foot */
  .lightbox--media .lightbox__stage img { max-height: calc(100dvh - 220px); }

  /* Out of the bar and down to the bottom of the screen, where a thumb is.
     fixed, not absolute: the panel is centred and only as tall as its picture,
     so its own bottom edge is nowhere near the screen's.

     max() rather than a plain 24px — that is the 24 asked for wherever there
     is nothing to clear, and the inset instead on a phone whose home
     indicator would otherwise sit on top of them. */
  .lightbox--media .lightbox__steps {
    position: fixed;
    left: 0;
    right: 0;
    bottom: max(var(--spacing-component-lg), env(safe-area-inset-bottom));
    justify-content: center;
    gap: var(--spacing-component-lg);
  }
}

/* ==========================================================================
   ABOUT — four beats, each a different shape
   The page reads top to bottom as a sequence rather than a stack of identical
   grids: an editorial opening with the artwork bleeding off the right, the
   asymmetric "what we create" quadrant, the staircase of rules, then three
   compliance cards under one heading.
   ========================================================================== */

/* 1 — the opening ---------------------------------------------------------
   Two beats in one section. The artwork runs the full width of the screen
   with the statement centred on it, and nothing is asked of the reader until
   they scroll past it: body on the left, the four key points on the right. */
.who {
  /* no top padding — the banner starts directly under the header */
  padding-block: 0 var(--spacing-layout-lg);
  text-align: left;
}

.who__banner {
  position: relative;
  display: grid;
  place-items: center;
  min-height: min(52svh, 400px);
  overflow: hidden;
}
.who__art { display: block; position: absolute; inset: 0; }
.who__art img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
  /* the subject is bottom-heavy in a wide crop; bias up so the roof and the
     lightning stay in frame */
  object-position: 50% 42%;
}
/* the landing page's hero clip, filling the same box. Centred rather than
   biased up like the still was — the clip is framed for a full-bleed crop
   already, and that is how the hero carries it. */
.who__video {
  display: block;
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
}
/* the statement sits dead centre, which is also where the art is brightest,
   so the scrim is heaviest there. Solved for rather than guessed: sampling
   the cover-fit crop under the heading and compositing this stack gives a
   worst case of 5.4:1, against a 3:1 bar for text this size. */
.who__banner::after {
  content: "";
  position: absolute;
  inset: 0;
  background: radial-gradient(
    82% 82% at 50% 50%,
    rgba(3, 7, 18, 0.66) 0%,
    rgba(3, 7, 18, 0.55) 48%,
    rgba(3, 7, 18, 0.22) 100%);
}
.who__banner .wrap {
  position: relative;
  z-index: 1;
  text-align: center;
}
.who h1 {
  margin-inline: auto;
  max-width: 22ch;
  font-size: clamp(34px, 4.4vw, 64px);
  font-weight: var(--font-weight-bold);
  line-height: 1.02;
  letter-spacing: var(--letter-spacing-tighter);
  text-wrap: balance;
}

/* the read stacks under the banner: body first, then the key points across */
.who__grid {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: var(--spacing-layout-sm);
  padding-top: var(--spacing-layout-lg);
}
/* two paragraphs, two columns — full width under the heading would run the
   lines past a comfortable measure */
.who__body {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: var(--spacing-layout-sm);
  align-items: start;
}
.who__body p {
  margin: 0;
  max-width: 56ch;
  color: var(--color-text-secondary);
  font-size: var(--font-size-md);
  line-height: var(--line-height-relaxed);
}

/* the four key points run across under the body, one row ------------------ */
.claims {
  list-style: none;
  margin: 0;
  padding: var(--spacing-layout-xs) 0 0;
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: var(--spacing-component-xl);
  align-items: start;
  border-top: 1px solid var(--color-border-subtle);
}
.claim {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr);
  /* start, not center: the labels run to different line counts, and this keeps
     all four marks on one line across the row */
  align-items: start;
  gap: var(--spacing-component-md);
}
/* picture is display:inline by default, which leaves the img no height to
   resolve against */
.claim__mark {
  display: block;
  width: 44px;
  height: 44px;
  filter: drop-shadow(0 8px 14px rgba(0, 0, 0, 0.55));
}
.claim__mark img { display: block; width: 100%; height: 100%; object-fit: contain; }
.claim > span {
  color: var(--color-text-primary);
  font-size: var(--font-size-md);
  line-height: var(--line-height-normal);
}

/* 2 — the rules as a staircase -------------------------------------------
   Written narrow-first on purpose. The wide layout lives in a min-width query
   below, so the two never compete on specificity: at phone width the steps
   are a single rail, and only above 1000px do they step across the page. */
.how { text-align: left; }
.how__head { margin-bottom: var(--spacing-layout-sm); }
.how__head h2 {
  font-size: clamp(32px, 3.6vw, 56px);
  line-height: 1;
  text-align: left;
}
.how__head p {
  margin: var(--spacing-component-md) 0 0;
  max-width: 40ch;
  color: var(--color-text-secondary);
  font-size: var(--font-size-md);
  line-height: var(--line-height-relaxed);
}

.steps {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  grid-template-columns: minmax(0, 1fr);
}
.step {
  position: relative;
  padding: var(--spacing-component-lg) 0 var(--spacing-layout-xs)
           var(--spacing-component-xl);
  /* the riser. Consecutive steps share it, so it reads as one continuous run */
  border-left: 1px solid var(--color-border-subtle);
  /* weight at the riser, gone by the middle of the tread — enough to read as a
     surface without becoming a card */
  background: linear-gradient(90deg,
    rgba(31, 41, 55, 0.42) 0%,
    rgba(31, 41, 55, 0) 62%);
}
/* the node sits where the riser meets the tread */
.step::before {
  content: "";
  position: absolute;
  left: -5px;
  top: -5px;
  width: 9px;
  height: 9px;
  border-radius: var(--radius-full);
  background: var(--color-brand-default);
}
.step__no {
  display: block;
  margin-bottom: var(--spacing-component-sm);
  font-family: var(--font-family-mono);
  font-size: var(--font-size-3xl);
  line-height: 1;
  color: var(--color-brand-default);
  opacity: 0.55;
}
.step h3 {
  font-family: var(--font-family-heading);
  font-size: var(--font-size-2xl);
  text-transform: uppercase;
  letter-spacing: var(--letter-spacing-tight);
  color: var(--color-text-primary);
}
.step p {
  margin: var(--spacing-component-xs) 0 0;
  max-width: 46ch;
  color: var(--color-text-secondary);
  font-size: var(--font-size-md);
  line-height: var(--line-height-relaxed);
}

/* the staircase is a single rail below this, so its heading centres with it */
@media (max-width: 1000px) {
  .how__head { text-align: center; }
  .how__head h2 { text-align: center; }
  .how__head p { margin-inline: auto; }
}

@media (min-width: 1001px) {
  /* seven columns so four steps of four columns each can start one column
     further right than the last: 1-5, 2-6, 3-7, 4-8 */
  .steps { grid-template-columns: repeat(7, minmax(0, 1fr)); }
  .step {
    border-top: 1px solid var(--color-border-subtle);
    padding-right: var(--spacing-component-xl);
  }
  .step:nth-child(1) { grid-column: 1 / span 4; grid-row: 1; }
  .step:nth-child(2) { grid-column: 2 / span 4; grid-row: 2; }
  .step:nth-child(3) { grid-column: 3 / span 4; grid-row: 3; }
  .step:nth-child(4) { grid-column: 4 / span 4; grid-row: 4; }
}

/* 3 — the heading sits over three cards rather than beside them ----------- */
.compliance { text-align: left; }
.compliance__head { margin-bottom: var(--spacing-layout-sm); }
.compliance__head h2 {
  font-size: clamp(32px, 3.6vw, 56px);
  line-height: 1;
  text-align: left;
}
.compliance__head p {
  margin: var(--spacing-component-md) 0 0;
  max-width: 44ch;
  color: var(--color-text-secondary);
  font-size: var(--font-size-md);
  line-height: var(--line-height-relaxed);
}

/* doubled class: the base .details is declared later in the sheet, and at
   equal specificity source order would hand it the columns */
/* Same treatment as the other two heads, but declared here rather than in the
   shared query above: .compliance__head h2 sets text-align:left and the p uses
   a margin shorthand, both of which come later in the sheet and would win the
   tie on source order. */
@media (max-width: 1000px) {
  .compliance__head { text-align: center; }
  .compliance__head h2 { text-align: center; }
  .compliance__head p { margin-inline: auto; }
}

.details.details--trio {
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: var(--spacing-component-lg);
}
.details--trio .detail {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr);
  grid-template-rows: auto auto;
  column-gap: var(--spacing-component-md);
  row-gap: var(--spacing-component-2xs);
  align-items: start;
  align-content: start;
  text-align: left;
}
.details--trio .detail__mark {
  grid-row: 1 / span 2;
  width: 40px;
  height: 40px;
  margin-bottom: 0;
}
/* In this variant the label is the card's title and the value is the sentence
   under it — the inverse of the contact cards, where the value carries the
   heading and the label is only an eyebrow. */
.details--trio .detail__label {
  font-family: var(--font-family-heading);
  font-size: var(--font-size-xl);
  font-weight: var(--font-weight-semibold);
  line-height: var(--line-height-tight);
  letter-spacing: var(--letter-spacing-tight);
  text-transform: uppercase;
  color: var(--color-text-primary);
}
.details--trio .detail__value {
  font-family: var(--font-family-body);
  font-size: var(--font-size-sm);
  line-height: var(--line-height-relaxed);
  color: var(--color-text-secondary);
}

@media (max-width: 900px) {
  .who__banner { min-height: min(52svh, 340px); }
  .who__grid {
    gap: var(--spacing-layout-xs);
    padding-top: var(--spacing-layout-sm);
  }
  /* one measure of prose, and the row of four folds to a pair of pairs */
  .who__body { grid-template-columns: minmax(0, 1fr); gap: var(--spacing-component-md); }
  .claims { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  /* three across leaves each card about four words wide */
  .details.details--trio { grid-template-columns: minmax(0, 1fr); }
}

/* after the 900 block, not before it: both rules are single-class, so source
   order is what decides which one a 375px viewport gets */
@media (max-width: 560px) {
  /* two across leaves each label about three words wide */
  .claims { grid-template-columns: minmax(0, 1fr); }
}

/* ==========================================================================
   CONTACT — details and enquiry form
   .details is shared: contact uses it for email/phone/location, about for the
   certification trio.
   ========================================================================== */

.details {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
  gap: var(--spacing-component-md);
}
.detail {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--spacing-component-2xs);
  padding: var(--spacing-component-xl) var(--spacing-component-lg);
  border: 1px solid var(--color-border-subtle);
  border-radius: var(--radius-2xl);
  background: var(--color-surface-default);
}
/* picture is display:inline by default, which leaves the img no height to
   resolve against */
.detail__mark {
  display: block;
  width: 48px;
  height: 48px;
  margin-bottom: var(--spacing-component-sm);
  filter: drop-shadow(0 8px 14px rgba(0, 0, 0, 0.55));
}
.detail__mark img { display: block; width: 100%; height: 100%; object-fit: contain; }

.detail__label {
  font-family: var(--font-family-mono);
  font-size: var(--font-size-2xs);
  letter-spacing: var(--letter-spacing-widest);
  text-transform: uppercase;
  color: var(--color-text-secondary);
}
.detail__value {
  font-family: var(--font-family-heading);
  font-size: var(--font-size-xl);
  color: var(--color-text-primary);
}

.detail__value a { color: inherit; text-decoration: none; }
.detail__value a:hover { color: var(--color-brand-default); }

/* --- the form -------------------------------------------------------------
   Fields are the Figma "Fields" component (Size=Large): a pill with the label
   living inside it, small along the top once the field is focused or filled
   and centred as a placeholder while it is empty. The float is pure CSS via
   :placeholder-shown, so it works with JS off. */
/* the details and the form share one section: how to reach us on the left,
   what to tell us on the right --------------------------------------------- */
.contact-grid {
  display: grid;
  grid-template-columns: minmax(0, 0.75fr) minmax(0, 1.25fr);
  gap: var(--spacing-layout-sm);
  align-items: start;
  text-align: left;
}

.details.details--stack {
  grid-template-columns: minmax(0, 1fr);
  gap: var(--spacing-component-md);
}
.details--stack .detail {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr);
  grid-template-rows: auto auto;
  column-gap: var(--spacing-component-lg);
  row-gap: var(--spacing-component-2xs);
  align-items: center;
  align-content: center;
  text-align: left;
  padding: var(--spacing-component-lg);
}
.details--stack .detail__mark {
  grid-row: 1 / span 2;
  width: 40px;
  height: 40px;
  margin-bottom: 0;
}

/* the whole card is the hit area, not just the line of text: the link inside
   the value stretches an invisible layer over the card, so email and phone are
   459x99 targets instead of 200x24 ones. One link in the DOM either way, so
   the accessible name is still the address itself. */
.details--stack .detail { position: relative; }
.details--stack .detail__value a::after {
  content: "";
  position: absolute;
  inset: 0;
}
/* hover and focus belong to the card, since that is what the pointer is over.
   :has() is the only way to reach the parent; where it is unsupported the card
   simply keeps its resting border and the link still works. */
.details--stack .detail:has(a:hover) {
  border-color: var(--color-brand-border);
}
.details--stack .detail:has(a:focus-visible) {
  outline: 2px solid var(--color-brand-default);
  outline-offset: 2px;
}
/* the location card carries no link, so it must not look like it takes a tap */
.details--stack .detail:has(a) { cursor: pointer; }

/* the form sits in a panel of its own, so it reads as one group beside the
   three detail cards rather than as loose controls on the page */
.contact-form {
  padding: var(--spacing-component-xl);
  border: 1px solid var(--color-border-subtle);
  border-radius: var(--radius-2xl);
  /* deliberately not the cards' full --color-surface-default: the field pills
     are #111827 as well, and they would sink into a panel of the same fill */
  background: color-mix(in srgb,
    var(--color-surface-default) 50%, var(--color-background-default));
}

.contact-form__head { margin-bottom: var(--spacing-layout-xs); }
.contact-form__head h2 {
  font-size: clamp(28px, 3vw, 44px);
  line-height: 1;
  text-align: left;
}
.contact-form__head p {
  margin: var(--spacing-component-md) 0 0;
  max-width: 46ch;
  color: var(--color-text-secondary);
  font-size: var(--font-size-md);
  line-height: var(--line-height-relaxed);
}
/* the form is a column now, not a centred block on its own */
.contact-grid .enquiry { max-width: none; margin-inline: 0; }

.enquiry {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: var(--spacing-component-lg);
  max-width: 820px;
  margin-inline: auto;
  text-align: left;
}
.field--wide { grid-column: 1 / -1; }
.field--trap { position: absolute; left: -9999px; width: 1px; height: 1px; overflow: hidden; }

.field { display: flex; flex-direction: column; gap: var(--spacing-component-2xs); }
.field__req { color: var(--color-brand-default); }

/* the pill itself — the control owns the border and the fill, the input inside
   it is transparent so the label can sit over the same surface */
.field__control {
  position: relative;
  display: block;
  height: var(--input-lg-height);
  border: var(--input-border-width-default) solid var(--input-border-default);
  border-radius: var(--input-radius);
  background: var(--input-container-bg-default);
  transition: border-color var(--duration-fast) var(--ease-out);
}
.field__control:hover { border-color: var(--input-border-hover); }
.field__control:focus-within { border-color: var(--input-border-focus); }

.field__control--area {
  height: auto;
  border-radius: var(--radius-2xl);   /* a pill cannot hold five lines */
}

.field__control input,
.field__control select,
.field__control textarea {
  width: 100%;
  height: 100%;
  margin: 0;
  padding: 26px var(--input-lg-padding-x) 7px;
  border: 0;
  border-radius: inherit;
  background: none;
  color: var(--input-text-default);
  font-family: var(--font-family-body);
  font-size: var(--input-value-size);
  line-height: var(--line-height-normal);
}
.field__control textarea {
  min-height: 148px;
  padding-bottom: var(--input-lg-padding-x);
  resize: vertical;
}
.field__control input:focus,
.field__control select:focus,
.field__control textarea:focus { outline: none; }

/* the ring goes on the pill, not the control, so it follows the rounded edge */
.field__control:focus-within {
  box-shadow: 0 0 0 var(--input-focus-ring-width) var(--input-focus-ring-color);
}

/* the label: centred over an empty field, small along the top once there is
   something to sit above */
.field__control label {
  position: absolute;
  left: var(--input-lg-padding-x);
  right: var(--input-lg-padding-x);
  top: 50%;
  translate: 0 -50%;
  font-family: var(--font-family-heading);
  font-size: var(--input-placeholder-size);
  font-weight: var(--font-weight-regular);
  line-height: 1.5;
  letter-spacing: var(--letter-spacing-wide);
  text-transform: uppercase;
  color: var(--input-placeholder);
  pointer-events: none;
  transition:
    top var(--duration-fast) var(--ease-out),
    translate var(--duration-fast) var(--ease-out),
    font-size var(--duration-fast) var(--ease-out),
    color var(--duration-fast) var(--ease-out);
}

.field__control input:focus + label,
.field__control input:not(:placeholder-shown) + label,
.field__control textarea:focus + label,
.field__control textarea:not(:placeholder-shown) + label,
.field__control--select label,
.field__control--area label {
  top: 9px;
  translate: 0 0;
  font-size: var(--input-label-size);
  font-weight: var(--font-weight-medium);
  line-height: 1.25;
  color: var(--input-label-default);
}
/* select and textarea never rest: a select always shows a value, and a
   centred label in a 148px box would sit on top of the first line typed */
.field__control--area label { top: 14px; }

.field__control:focus-within label { color: var(--input-label-focus); }

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

/* the native arrow is unstyleable across browsers, so it is drawn here */
.field__control--select select {
  appearance: none;
  padding-right: var(--spacing-layout-xs);
  background-image:
    linear-gradient(45deg, transparent 50%, var(--select-chevron) 50%),
    linear-gradient(135deg, var(--select-chevron) 50%, transparent 50%);
  background-position: right 26px top 58%, right 20px top 58%;
  background-size: 6px 6px, 6px 6px;
  background-repeat: no-repeat;
}
.field__control--select select option { background: var(--input-container-bg-default); }

/* error takes the border and the label together, and the message under the
   field is the primary signal — the colour only reinforces it */
.field__control:has([aria-invalid="true"]) { border-color: var(--input-border-error); }
.field__control:has([aria-invalid="true"]) label { color: var(--input-label-error); }

.field__hint,
.field__err { margin: 0; font-size: var(--font-size-sm); }
.field__hint { color: var(--color-text-secondary); }
.field__err  { color: var(--color-feedback-error-text); }

/* checkbox — Figma: Checkbox. The native control is kept and only restyled,
   so it stays keyboard operable and announces its own state. */
.check {
  display: flex;
  align-items: flex-start;
  gap: var(--checkbox-gap);
  font-family: var(--font-family-heading);
  font-size: var(--checkbox-label-size);
  font-weight: var(--font-weight-medium);
  line-height: 1.25;
  letter-spacing: normal;
  /* The component sets this label in uppercase, which is right for the one
     word it was drawn with. This one is a sentence, and a 60-character line
     of caps is measurably harder to read, so the case is left alone. */
  text-transform: none;
  color: var(--checkbox-label-text);
  cursor: pointer;
}
.check input {
  appearance: none;
  flex: none;
  width: var(--checkbox-size-lg);
  height: var(--checkbox-size-lg);
  margin: 0;
  border: var(--input-border-width-default) solid var(--checkbox-border-unchecked);
  border-radius: var(--checkbox-radius);
  background: var(--checkbox-bg-unchecked);
  box-shadow: var(--checkbox-depth);
  cursor: pointer;
  transition:
    background-color var(--duration-fast) var(--ease-out),
    border-color var(--duration-fast) var(--ease-out);
}
.check input:hover { border-color: var(--checkbox-border-hover); }

/* the tick is a real checkmark rather than two crossed gradients, which is
   what made the checked state look bent */
.check input:checked {
  border-color: var(--checkbox-border-checked);
  background-color: var(--checkbox-bg-checked);
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20'%3E%3Cpath d='M4.8 10.4l3.4 3.4L15.2 6.8' fill='none' stroke='%23030712' stroke-width='2.6' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: center;
  background-size: 100% 100%;
}

.check input[aria-invalid="true"] { border-color: var(--checkbox-border-error); }
.field--check:has([aria-invalid="true"]) .check { color: var(--checkbox-label-error); }

.check input:focus-visible {
  outline: none;
  box-shadow: var(--checkbox-depth), 0 0 0 var(--input-focus-ring-width) var(--input-focus-ring-color);
}

.check input:disabled {
  border-color: var(--checkbox-border-disabled);
  background-color: var(--checkbox-bg-disabled);
  cursor: not-allowed;
}

.enquiry__foot {
  flex-direction: row;
  align-items: center;
  flex-wrap: wrap;
  gap: var(--spacing-component-md);
}
.enquiry__status,
.signup__status { margin: 0; font-size: var(--font-size-md); }
.enquiry__status[data-state="error"],
.signup__status[data-state="error"]   { color: var(--color-feedback-error-text); }
.enquiry__status[data-state="success"],
.signup__status[data-state="success"] { color: var(--color-feedback-success-text); }
.enquiry__status[data-state="working"],
.signup__status[data-state="working"] { color: var(--color-text-secondary); }

@media (max-width: 980px) {
  /* the details stop being a column and sit above the form */
  .contact-grid { grid-template-columns: minmax(0, 1fr); gap: var(--spacing-layout-xs); }
}

@media (max-width: 560px) {
  /* 32px of panel on top of 24px of wrap leaves the fields under 265px */
  .contact-form { padding: var(--spacing-component-lg); }
}

@media (max-width: 700px) {
  .enquiry { grid-template-columns: 1fr; }
}

/* ==========================================================================
   6 · CLOSE
   ========================================================================== */

/* ==========================================================================
   FOOTER
   Three rows on the reference's shape: the studio and the way in, the small
   print, then the play line. Its own band now rather than a strip nested in a
   closing call-to-action section — the call to action lives in the top row.
   ========================================================================== */

.site-footer {
  border-top: 1px solid var(--color-brand-border);
  background:
    radial-gradient(70% 130% at 50% 0%, var(--color-brand-alpha-a20) 0%, transparent 62%),
    var(--color-background-subtle);
  padding-block: var(--spacing-layout-lg) var(--spacing-layout-sm);
  text-align: left;
  color: var(--color-text-secondary);
  font-size: var(--font-size-sm);
}
.site-footer p { margin: 0; }
.site-footer a { color: inherit; }

/* 1 — the studio on the left, the way in on the right ---------------------- */
.site-footer__top {
  display: grid;
  grid-template-columns: minmax(0, 1.35fr) minmax(0, 0.65fr);
  gap: var(--spacing-layout-sm);
  align-items: start;
  padding-bottom: var(--spacing-layout-sm);
}
/* the header's logo, a little larger — it is the only brand mark down here */
.site-footer__mark {
  display: block;
  height: 58px;
  width: auto;
  margin-bottom: var(--spacing-component-lg);
  filter: drop-shadow(0 4px 10px rgba(0, 0, 0, 0.55));
}
.site-footer__legal {
  max-width: 68ch;
  line-height: var(--line-height-relaxed);
}

.site-footer__cta h2 {
  font-family: var(--font-family-heading);
  font-size: var(--font-size-2xl);
  text-transform: uppercase;
  letter-spacing: var(--letter-spacing-tight);
  color: var(--color-text-primary);
  text-align: left;
}
.site-footer__pitch {
  margin: var(--spacing-component-sm) 0 var(--spacing-component-lg) !important;
  max-width: 42ch;
  line-height: var(--line-height-relaxed);
}

/* one field and one button, stacked — the column is about 385px wide, and an
   inline row would leave the input too short to read an address in */
.signup {
  display: grid;
  gap: var(--spacing-component-md);
  max-width: 420px;
}
.signup .btn { width: 100%; }
.signup__status:empty { display: none; }
.signup__note {
  margin-top: var(--spacing-component-md) !important;
  font-size: var(--font-size-xs);
  color: var(--color-text-tertiary);
}

/* 2 and 3 — the small print, each row divided by a hairline ---------------- */
.site-footer__row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: var(--spacing-component-md) var(--spacing-component-xl);
  padding-block: var(--spacing-component-xl);
  border-top: 1px solid var(--color-border-subtle);
}
/* the last row runs out on the footer's own bottom padding rather than
   stacking its own on top of it */
.site-footer__row:last-of-type { padding-bottom: 0; }

.site-footer__links {
  display: flex;
  flex-wrap: wrap;
  gap: var(--spacing-component-xl);
}
.site-footer__links a { text-decoration: underline; text-underline-offset: 3px; }
.site-footer__links a:hover { color: var(--color-brand-default); }

/* Sits under the studio paragraph now, on the same 68ch measure, so the two
   read as one statement. Two classes, not one: `.site-footer p { margin: 0 }`
   is a class plus a type and would otherwise flatten the gap — this outranks
   it without reaching for !important. */
.site-footer .site-footer__play {
  display: flex;
  align-items: center;
  gap: var(--spacing-component-sm);
  margin-top: var(--spacing-component-lg);
  max-width: 68ch;
}

@media (max-width: 900px) {
  .site-footer__top { grid-template-columns: minmax(0, 1fr); }
  .site-footer__row { align-items: flex-start; flex-direction: column; }
}

.age-badge {
  display: inline-grid;
  place-items: center;
  width: 34px; height: 34px;
  flex: none;
  border-radius: var(--radius-full);
  border: 2px solid var(--color-text-secondary);
  font-family: var(--font-family-heading);
  font-size: var(--font-size-xs);
}


/* ==========================================================================
   MOTION
   ========================================================================== */

[data-reveal] {
  opacity: 0;
  transform: translateY(20px);
  transition: opacity var(--duration-slow) var(--ease-out),
              transform var(--duration-slow) var(--ease-out);
}
[data-reveal="in"] { opacity: 1; transform: none; }

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  *, *::before, *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
  }
  [data-reveal] { opacity: 1; transform: none; }
}
