/*
 * Home page. Loaded by index.html and by nothing else.
 *
 * It loads immediately after assets/css/site-system.css, so the system owns
 * everything this page is made of: .ds-card for the step and tune tiles,
 * .ds-tag for the marquee chips, .ds-section-title / .ds-lede for the section
 * heads, .ds-container for the column, .ds-section for the rhythm and the
 * hairline between bands, and the shared disclosure for the FAQ. This file
 * declares no colour, type step, radius or elevation of its own.
 *
 * What is left here is what the system has no opinion about: the hero crop, the
 * marquee mechanics, and where each tile sits on the twelve-column track. Plus
 * one more job — turning off the older, page-level treatments in style.css that
 * the system does not reach, because they were written before it existed.
 *
 * On specificity. site-system.css writes `.desktop-web .thing`; this file loads
 * after it, so a rule here needs the same `.desktop-web` prefix to win a tie,
 * and that prefix is why it appears on rules that otherwise would not need it.
 * The one id, on the hero padding, is aimed at desktop-shell.css, which loads
 * last of all.
 */

/* -- Page shell ------------------------------------------------------------
 *
 * The shell height, the scroll-reveal switch-off and the page entrance are the
 * system's now (site-system.css §3), one definition for all six pages that have
 * an #app. Only the tile gap this page's two bentos share is left here.
 */
#app { --tile-gap: var(--ds-5); }

/* -- Hero -------------------------------------------------------------------
 *
 * Its own stacking context. The decorative wave is absolutely positioned and
 * deliberately larger than the viewport, so it has to be clipped here rather
 * than allowed to widen the page; overflow:hidden on the section is what stops
 * the whole document scrolling sideways on a narrow screen.
 *
 * The frame and the wave itself are set in effects.css, which owns the shared
 * hero surface. Only the crop and the copy are decided here.
 *
 * No glass card sits over this artwork, although §1 would allow one. The scrim
 * below stops the wave at the sixth column line, which means the copy stands on
 * flat --surface-base and a card there would be frosting nothing; moving the
 * copy right, onto the bright band, is the centred-hero-over-artwork the brief
 * bans and drops --text-muted under 4.5:1 on the blown-out stops. The glass on
 * this page is the sticky header.
 */
.home-hero {
  position: relative;
  isolation: isolate;
  overflow: hidden;
  display: grid;
  align-content: center;
  min-height: min(640px, 78vh);
}

/*
 * desktop-shell.css states .desktop-web .home-hero padding and loads after this
 * file, so an id is what takes it back. The horizontal half belongs to
 * .ds-container on .opening: 40px of section padding on top of a centred column
 * put the hero's text on a different line from every other band.
 */
#app .home-hero {
  padding: clamp(72px, 9vw, 132px) 0 clamp(56px, 7vw, 96px);
}

/*
 * A crop, not a vignette.
 *
 * The artwork is left showing in a band on the right and cut off near the
 * sixth column line. A soft light source spread behind the whole hero is the
 * thing that reads as a blur filter switched on for its own sake; the same
 * artwork with an edge reads as a photograph placed on a grid.
 *
 * It also fixes the floor for the copy rather than leaving it to chance. Every
 * word of the hero sits left of the opaque stop, on flat --surface-base, which
 * is why the paragraph measures 7.6:1 instead of whatever the gradient happened
 * to be doing behind it.
 */
.hero-scrim {
  position: absolute;
  inset: 0;
  z-index: -1;
  pointer-events: none;
  background:
    linear-gradient(to right, var(--surface-base) 0 52%, rgba(10, 10, 10, 0) 64%),
    linear-gradient(to bottom, var(--surface-base) 0 4%, rgba(10, 10, 10, 0) 24%),
    linear-gradient(to top, var(--surface-base) 0 6%, rgba(10, 10, 10, 0) 30%);
}

/* Eyebrow, title, lede, one action — the page hierarchy in §3, left aligned.
   Spacing is stated per element rather than as a grid gap, because an eyebrow
   belongs closer to its title than a paragraph does. */
.opening {
  display: grid;
  justify-items: start;
}

.desktop-web .opening .ds-eyebrow { margin: 0 0 var(--ds-3); }

/*
 * A measure, not a size.
 *
 * Left to the full column the headline broke into four short ragged lines;
 * capped near fifteen characters it settles into two or three even ones at
 * every width above a phone, and stays well clear of the artwork's edge. The
 * size is the system's display step.
 */
.desktop-web .opening .hero-title {
  margin: 0;
  max-width: 15ch;
}

/* Short measure on purpose: a wide grey paragraph under a large headline is the
   shape of a template, and it would also run past the crop. */
.desktop-web .opening .opening-body {
  margin: var(--ds-4) 0 0;
  max-width: 46ch;
}

.desktop-web .opening .hero-button {
  margin-top: clamp(20px, 2.6vw, 30px);
}

/* -- Supported titles -------------------------------------------------------
 *
 * A continuous run of game names in a band of its own, so the label lines up
 * with the column while the run itself bleeds off both edges of the window —
 * which is the whole point of a run: it has to look like it continues past the
 * page.
 *
 * The track holds the list twice and the animation moves it left by exactly
 * half its own width, so the moment the first copy leaves the frame the second
 * is sitting where it started and the seam never lands on screen. Translating a
 * transform keeps this off the layout and paint path; animating a position or a
 * margin would relayout the band on every frame.
 */
/* `--ds-5`, not a clamp of its own.
 *
 * Every other section head on this page stands 48px (`--ds-7`) clear of its
 * content; this one stood 16 to 22 depending on the viewport, which was both
 * off the 4px scale (DESIGN §5) and the one heading-to-content distance on the
 * site that changed as the window was dragged. The band is `.ds-section--tight`
 * and its content is a running ribbon rather than a block, so it keeps the
 * shorter step, but it is a step: 24px at every width. */
.supported-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--ds-4);
  margin-bottom: var(--ds-5);
}

/* The pause control (WCAG 2.2.2). It is the system's quiet text control, not a
   new button variant: no fill, no border, the muted step, and the one focus
   ring everything else uses. 44px of touch target under a coarse pointer
   (§12) comes from the padding rather than a min-height, so it stays on the
   heading's baseline at every width. */
.desktop-web .supported-toggle {
  flex: 0 0 auto;
  margin: 0;
  border: 0;
  border-radius: var(--ds-r-xs);
  padding: var(--ds-2) var(--ds-3);
  background: none;
  color: var(--text-muted);
  font: inherit;
  font-size: var(--ds-text-small);
  font-weight: 500;
  line-height: 1;
  cursor: pointer;
  transition: color var(--ds-motion-fast) var(--ds-ease);
}

.desktop-web .supported-toggle:hover {
  color: var(--text);
}

.desktop-web .supported-toggle:focus-visible {
  outline: 2px solid var(--ds-focus);
  outline-offset: 2px;
}

@media (pointer: coarse) {
  .desktop-web .supported-toggle {
    min-height: 44px;
    padding-inline: var(--ds-3);
  }
}

/* What the control does. Both tracks, because the duplicate is half of what is
   on screen at any moment and stopping only the first would leave the run
   still moving. */
.lt-marquee-paused .supported-track {
  animation-play-state: paused;
}

/* A section head, at the section step, like the three bands below it.
   It was set at the eyebrow step, which made it a second eyebrow on a page that
   already has one in the hero (DESIGN.md §3: one per page, at the top) and the
   only h2 on the site rendered in small caps. The .desktop-web prefix is
   load-bearing — site-system.css sets weight, tracking and colour on every
   heading, and a bare class loses to it. */
.desktop-web .supported-title {
  margin: 0;
  font-size: var(--ds-text-h2);
  font-weight: 600;
  line-height: 1.18;
  letter-spacing: -0.024em;
  text-transform: none;
}

/* The frame. Names are cut off at both edges rather than stopping short of
   them, which is what tells the eye the run continues past the page. */
.supported-viewport {
  display: flex;
  gap: var(--ds-2);
  overflow: hidden;
  /* A flex item will not shrink below its own content unless told it may, and
     the content here is two full copies of the list. Without this the frame
     grows to about fifteen thousand pixels, the run is never clipped, and the
     animation slides a track that had nothing to hide behind. */
  min-width: 0;
  padding: 2px 0;
  mask-image: linear-gradient(90deg, transparent, #000 6%, #000 94%, transparent);
  -webkit-mask-image: linear-gradient(90deg, transparent, #000 6%, #000 94%, transparent);
}

/* `reverse` reads the same keyframe back to front, so the track still starts
   at 0 and unwinds to the exact point the duplicate copy picks up from: the
   join stays invisible, only the direction on screen flips from right-to-left
   to left-to-right. 45s is retimed for the shortened list below: 22 chips
   make one copy about 2135px wide, so the loop travels roughly 2143px
   (the copy plus the gap into the next one) every 45s, near 48px/s, the same
   calm middle of the 40-60px/s range the full 60-title list used to read at
   over its own, much longer, 90s. */
.supported-track {
  display: flex;
  flex: 0 0 auto;
  gap: var(--ds-2);
  margin: 0;
  padding: 0;
  list-style: none;
  animation: supported-run 45s linear infinite reverse;
}

/* The chips are .ds-tag and nothing else; the only thing stated here is that a
   name in a moving run must not wrap. */
.desktop-web .supported-track > li {
  flex: 0 0 auto;
  min-height: 28px;
  white-space: nowrap;
}

/* Half the distance, because the track is duplicated: the gap between the two
   copies is part of the width, so the shift includes it. */
@keyframes supported-run {
  from { transform: translateX(0); }
  to   { transform: translateX(calc(-100% - var(--ds-2))); }
}

/* Stopped while it is being read. */
.supported-viewport:hover .supported-track,
.supported-viewport:focus-within .supported-track {
  animation-play-state: paused;
}

/* A chip is not a control, and §8 leaves exactly one hover transform in the
   system. style.css still lifts these, so it is turned off here; the colour
   change it also makes is fine and is left alone. */
@media (hover: hover) {
  .desktop-web .supported-track > li:hover {
    transform: none;
  }
}

/* Movement across the whole width of the page is exactly what a vestibular
   trigger looks like. Asked not to, it holds still and simply wraps.
   `flex-wrap` alone was never enough: `.supported-track` is `flex: 0 0 auto`
   in `.supported-viewport`'s row, so its own preferred width is measured as
   if it never wrapped (the flexbox auto-minimum-size algorithm uses the
   max-content size either way), and it rendered as one long unwrapped row
   clipped at the edge of the frame instead. Filling the row it is given lets
   its own children actually wrap inside that width. */
@media (prefers-reduced-motion: reduce) {
  .supported-track {
    width: 100%;
    animation: none;
    flex-wrap: wrap;
  }

  .supported-viewport {
    flex-wrap: wrap;
    mask-image: none;
    -webkit-mask-image: none;
  }

  .supported-viewport .supported-track[aria-hidden="true"] {
    display: none;
  }

  /* Nothing is moving here, so a control offering to stop it would be a button
     that does nothing. */
  .desktop-web .supported-toggle {
    display: none;
  }
}

/* -- Section heads ----------------------------------------------------------
 *
 * .ds-section-head carries the measure and the space beneath; .band-head and
 * .faq-head survive only as hooks. The id-weighted heading overrides that used
 * to live here are gone: they set their own size, weight and tracking, which is
 * what put these two headings a step out of the scale on every other page.
 */
.desktop-web :is(.band-head, .faq-head) p {
  margin: var(--ds-3) 0 0;
}

/* -- The bentos -------------------------------------------------------------
 *
 * Two grids on one twelve-column track, with no two tiles beside each other
 * the same width: 8 and 4 in the first, then 7 and 5 pinwheeled in the second
 * so the wide tile changes sides between rows. The three-up and four-up rows of
 * identical boxes this replaces were the single thing that made the page look
 * assembled rather than designed — equal tiles in equal rows is what you get
 * when nobody has decided which of them matters more.
 *
 * The tiles themselves are .ds-card: surface, radius, padding and elevation all
 * come from the system, and nothing about them is restated here.
 */
.steps,
.tune-list {
  display: grid;
  gap: var(--tile-gap);
  margin: 0;
  padding: 0;
  list-style: none;
}

.steps { counter-reset: step; }

.desktop-web :is(.steps > li, .tune-list > div) {
  display: grid;
  align-content: start;
  gap: var(--ds-2);
}

.steps > li { counter-increment: step; }

/*
 * The one numbered sequence on the site, and the exemption DESIGN.md §14
 * records: a session runs in this order, so the figures are information, not a
 * section-number eyebrow.
 *
 * Which is why they are no longer set as one. At the eyebrow step (11px/600
 * with 0.14em of tracking) "01" read as the `01 / OVERVIEW` device §14 bans,
 * and read it twice over, because about.html was numbering three unordered
 * cards the same way. That copy is deleted; this one is restated at the
 * treatment tos.html already gives a clause number (content.css,
 * `.tos-section-num`): mono, 12px, no tracking, weight 400. A figure people
 * count along, set like a figure. A pseudo-element cannot carry a class, which
 * is why the treatment is restated here rather than applied.
 */
.steps > li::before {
  content: counter(step, decimal-leading-zero);
  margin-bottom: var(--ds-1);
  color: var(--text-faint);
  font-family: var(--font-mono);
  font-size: var(--ds-text-tag);
  font-weight: 400;
  letter-spacing: 0;
  line-height: 1.4;
  font-variant-numeric: tabular-nums;
}

/* style.css still applies its own 16px "title" and 14px "body" roles to these
   tiles through `.session .steps h3` and `.tune-list dd`, which outrank a plain
   .ds-card-title. Restating the system's two steps here is what keeps one card
   title size on the page rather than two. */
.desktop-web :is(.steps, .tune-list) .ds-card-title {
  font-size: var(--ds-text-h3);
  line-height: 1.35;
  font-weight: 600;
  letter-spacing: -0.015em;
}

.desktop-web :is(.steps, .tune-list) .ds-card-lede {
  max-width: 58ch;
  font-size: var(--ds-text-small);
  line-height: 1.6;
}

/* A static card does not move (§8). style.css lifts all of these on hover. */
@media (hover: hover) {
  .desktop-web :is(.steps > li, .tune-list > div):hover {
    transform: none;
    border-color: transparent;
    box-shadow: var(--ds-e2);
  }
}

/*
 * What earns the lead tile its size.
 *
 * A tile twice the area of its neighbours with the same three lines in it is
 * just a bigger box. These are the details a reader actually wants at that
 * point — what connects, to what, and for how long — set as the system's
 * label/value rows rather than folded into the paragraph.
 */
.tile-spec {
  display: grid;
  margin: var(--ds-3) 0 0;
}

.tile-spec > div {
  display: flex;
  flex-wrap: wrap;
  justify-content: space-between;
  gap: 2px var(--ds-4);
  padding: 9px 0;
  border-top: 1px solid var(--ds-hairline);
  font-size: var(--ds-text-small);
  line-height: 1.6;
}

.tile-spec dd {
  margin: 0;
  color: var(--text);
}

/*
 * A text action, not a third button competing with the one action the page
 * wants. Colour and decoration are the system's .quiet-link; the box is 44px
 * tall for a thumb, and style.css's animated ::after underline is turned off
 * because at that height it lands twelve pixels below the text as a second line.
 */
.quiet-link {
  justify-self: start;
  display: inline-flex;
  align-items: center;
  min-height: 44px;
  margin-top: var(--ds-5);
}

.quiet-link::after {
  display: none;
}

/* -- Frequently asked -------------------------------------------------------
 *
 * A shell holding five rounded cards holding five more rectangles was three
 * boxes deep. It is the system's disclosure now: hairline rows, a chevron that
 * turns, no box per question. Everything below only undoes the older card
 * treatment in style.css, which the system does not reach.
 */
/* This band cannot carry .ds-section: a test pins `class="home-faq" id="faq"`
   as a literal string, so the section rhythm and the hairline that
   `.ds-section + .ds-section` would have drawn are stated here instead. The
   values are the system's, not new ones. */
.desktop-web .home-faq {
  width: auto;
  max-width: none;
  margin: 0;
  padding-block: var(--ds-section-y);
  padding-inline: 0;
  border-top: 1px solid var(--ds-hairline);
}

.desktop-web .home-faq-list {
  display: grid;
  gap: 0;
}

.desktop-web .home-faq-item summary {
  min-height: 0;
  padding-inline: 0;
  background: transparent;
}

.desktop-web .home-faq-item summary:hover {
  background: transparent;
}

/* The open row is already marked by its content; a second rule under the
   summary only crowds the one above it. */
.desktop-web .home-faq-item[open] summary {
  border-bottom: 0;
}

/* A row does not lift, and it has no box to catch a shadow. */
@media (hover: hover) {
  .desktop-web .home-faq-item:hover {
    transform: none;
    border-color: transparent;
    box-shadow: none;
  }
}

/* -- Entrance ---------------------------------------------------------------
 *
 * A short lift as each tile crosses into view, scrubbed by the scroll position
 * rather than run off a timer — so there is no script on the page, nothing to
 * observe, and nothing for the strict script-src to have to allow.
 *
 * A browser without scroll-driven animations never enters this block and paints
 * the finished state, which is also exactly what the reduced-motion query asks
 * for. The movement is written as `translate` rather than `transform` so it
 * composes with anything else on the tile instead of cancelling it.
 */
@supports (animation-timeline: view()) {
  @media (prefers-reduced-motion: no-preference) {
    .steps > li,
    .tune-list > div {
      animation: tile-in linear both;
      animation-timeline: view();
      animation-range: entry 6% entry 46%;
    }

    .steps > li:nth-child(2),
    .tune-list > div:nth-child(2) {
      animation-range: entry 10% entry 50%;
    }

    .steps > li:nth-child(3),
    .tune-list > div:nth-child(3) {
      animation-range: entry 14% entry 54%;
    }

    .tune-list > div:nth-child(4) {
      animation-range: entry 18% entry 58%;
    }

    @keyframes tile-in {
      from { opacity: 0; translate: 0 16px; }
      to   { opacity: 1; translate: 0 0; }
    }
  }
}

/* -- The grid, once there is room for one -----------------------------------
 *
 * Below this the page is a single column and every tile is full width, because
 * a twelve-column track on a phone is twelve columns of about twenty pixels.
 */
@media (min-width: 768px) {
  .steps,
  .tune-list {
    grid-template-columns: repeat(12, minmax(0, 1fr));
    column-gap: var(--tile-gap);
  }

  /* A grid item with no span of its own takes a single column, which at this
     width is 47px. Everything that is not placed here has to say so. */
  .steps > li:first-child { grid-column: 1 / -1; }

  .steps > li:nth-child(2),
  .steps > li:nth-child(3) { grid-column: span 6; }

  .tune-list > div:nth-child(1) { grid-column: span 7; }
  .tune-list > div:nth-child(2) { grid-column: span 5; }
  .tune-list > div:nth-child(3) { grid-column: span 5; }
  .tune-list > div:nth-child(4) { grid-column: span 7; }
}

@media (min-width: 1024px) {
  /* The lead tile: eight columns wide and both rows tall, with the two short
     steps stacked in the remaining four.

     Its height is set by the column beside it, not by its own text, so roughly
     160px of slack has to go somewhere. Left at the end it was a card whose
     bottom edge sat 160px below its last line, which reads as a hole rather
     than as room. It goes between the paragraph and the spec instead: the spec
     is a hairline-topped strip of facts, not a continuation of the sentence
     above it, so space over it reads as the foot of the tile, and its last row
     lands level with the bottom of the tile beside it. */
  .steps > li:first-child {
    grid-column: span 8;
    grid-row: span 2;
    /* Four children — the counter, the h3, the lede and the spec strip — and
       the third takes the slack. Naming the tracks is what lets the tile stay
       the grid every other step is (`align-content: start` above packs tracks
       and leaves free space after the last one, where nothing can reach it);
       a `1fr` row absorbs that space during track sizing instead, before any
       alignment runs, so the spec lands on the tile's bottom edge. */
    grid-template-rows: auto auto 1fr auto;
  }

  .steps > li:nth-child(2),
  .steps > li:nth-child(3) { grid-column: span 4; }
}

/* -- Narrow ----------------------------------------------------------------- */

@media (max-width: 560px) {
  .desktop-web .opening .hero-title { max-width: none; }

  /* The hero's floor is set for a wide window, where the artwork needs room to
     read. On a phone the wave is already dimmed to half (desktop-shell.css) and
     the copy runs the full width, so the same floor only parks the marquee a
     screen and a half down. */
  .home-hero { min-height: min(520px, 68vh); }

  #app .home-hero { padding: clamp(48px, 13vw, 72px) 0 clamp(40px, 10vw, 56px); }
}
