/* =========================================================================
   RedPlum Games - Home only
   The fixed WebGL stage, the four scroll acts, the hero, the scroll spine
   and the exploded spec sheet. Load after site.css.
   ========================================================================= */

:root {
  /* --stage-bg / --stage-bg-2 (the studio sweep) live in site.css, shared
     with .section--hero on the inner pages. */
  --spine-w: 148px;
}

/* On the stage acts the outline button sits over live WebGL - the ticket
   passes under this corner of act 2 - so it gets an opaque fill there
   rather than letting the scene show through its middle. */
.act .btn--outline { background: var(--surface-page); }

/* =========================================================================
   THE STAGE - one fixed WebGL canvas behind the first four acts
   ========================================================================= */
.stage {
  position: fixed; inset: 0; z-index: 0;
  background:
    radial-gradient(125% 95% at 50% 8%, var(--stage-bg-2) 0%, var(--stage-bg) 74%);
  opacity: 0;
  transition: opacity var(--dur-4) var(--ease-out);
  pointer-events: none;
}
.stage[data-active="true"] { opacity: 1; }
/* Above the dust, so the product occludes it rather than wearing it. */
.stage__canvas { width: 100%; height: 100%; display: block; position: relative; z-index: 2; }



/* Halftone motif dusted over the stage floor - decoration, never behind copy.
   Two departures from the motif token, both because this surface is nearly
   white: the dot is drawn a few stops deeper than the token's brand amber,
   which is tuned for darker ground and does not resolve here at all; and the
   dust sits under the canvas (z-index 1 against the canvas's 2) so the
   product occludes it instead of wearing it. The mask hugs the floor line so
   the dust stops short of the copy above it. */
.stage::after {
  content: ""; position: absolute; inset: 0; pointer-events: none; z-index: 1;
  background-image: radial-gradient(rgba(168,113,0,.42) 1.5px, transparent 1.6px);
  background-size: var(--motif-halftone-size);
  opacity: .80;
  -webkit-mask-image: radial-gradient(70% 42% at 50% 94%, #000 0%, transparent 76%);
  mask-image: radial-gradient(70% 42% at 50% 94%, #000 0%, transparent 76%);
}

/* ---------- Game field ---------------------------------------------------
   The floating key-art banners (game-field.css, game-field.js), here behind
   the hero's cabinet: above the dust (z-index 1) and under the canvas (2).
   --vx/--vy starts where the cabinet stands, and home.js moves it onto the
   cabinet's real centre once the stage has drawn it. */
.stage .game-field { --vx: 70%; --vy: 46%; --card-w: clamp(140px, 13vw, 230px); z-index: 1; }
/* The studio sweep, laid back over the field behind the copy, so a banner
   passing under the headline fades out instead of fighting it. */
.stage .game-field::after {
  content: ""; position: absolute; inset: 0; pointer-events: none;
  background: linear-gradient(90deg, var(--stage-bg-2) 0%, rgba(255,255,255,.94) 40%, rgba(255,255,255,0) 62%);
}

.stage__loader {
  position: absolute; inset-inline: 0; inset-block-end: 12%;
  display: grid; justify-items: center; gap: var(--sp-4);
  color: var(--text-tertiary); transition: opacity var(--dur-3) var(--ease-out);
}
.stage__loader[hidden] { display: none; }
.stage__bar { width: 168px; height: 2px; background: var(--border-default); border-radius: 2px; overflow: hidden; }
.stage__bar i { display: block; height: 100%; width: 0%; background: var(--motif-rule-accent); transition: width var(--dur-3) var(--ease-out); }
.stage__loader span { font: var(--text-overline); letter-spacing: var(--ls-overline); text-transform: uppercase; }

/* The acts: full-height scroll panels that sit above the fixed canvas */
.acts { position: relative; z-index: 1; color: var(--text-primary); }
.act {
  min-height: 100svh; display: grid; align-items: center;
  /* the extra bottom padding keeps copy clear of the scroll spine */
  padding-block: var(--sp-10) var(--sp-13); position: relative;
}
.act__inner { width: 100%; max-width: var(--container-wide); margin-inline: auto; padding-inline: var(--gutter); }

/* Act copy is pinned to one side so the product keeps the other - the
   canvas camera is offset to match. */
.act__copy {
  max-width: 620px;
  /* opacity/transform are driven per-frame by focusCopy() in home.js */
  will-change: opacity, transform;
}
/* Act 2 keeps the centre: the cabinet is leaving frame-left and the kiosk
   entering frame-right, so the sides are spoken for. */
.act__copy--centre { margin-inline: auto; text-align: center; max-width: 560px; }
.act__copy--centre .eyebrow { justify-content: center; }
.act__copy--centre .hero__actions { justify-content: center; }
.act__copy--centre .lead { margin-inline: auto; }
/* Acts 1 and 3 share the row with the product and its spec column. */
.act--annotated .act__copy { max-width: 460px; }
.hero .act__copy { max-width: 780px; }

/* ---------- Act 1: hero ------------------------------------------------ */
.hero { min-height: 100svh; display: grid; align-content: center; }
.hero__title {
  font: var(--text-display-2xl); letter-spacing: -.042em;
  text-wrap: balance;
}
/* The H1 is the eyebrow line (the SEO team's heading, 13 Oct 2026); the two display
   lines under it sit outside the H1, in the same wrapper. */
.hero__title .eyebrow { display: flex; margin-bottom: var(--sp-6); }
/* Amber is a surface colour in this system, never display text on white
   (1.9:1). The accent word takes wheel red instead - 6.0:1. */
.hero__title em { font-style: normal; color: var(--red-500); }
.hero__lead { margin-top: var(--sp-6); max-width: 46ch; font: var(--text-lead); color: var(--text-secondary); }
.hero__actions { display: flex; flex-wrap: wrap; gap: var(--sp-3); margin-top: var(--sp-9); }
.hero__note {
  margin-top: var(--sp-6); display: flex; flex-wrap: wrap; gap: var(--sp-2) var(--sp-5);
  font: var(--text-body-sm); color: var(--text-secondary);
}
.hero__note b { font-weight: var(--fw-semibold); color: var(--text-primary); }

.scroll-cue {
  position: absolute; inset-block-end: var(--sp-7); inset-inline-start: var(--gutter);
  display: inline-flex; align-items: center; gap: 10px;
  font: var(--text-overline); letter-spacing: var(--ls-overline);
  text-transform: uppercase; color: var(--text-tertiary);
}
.scroll-cue i {
  display: block; width: 1px; height: 34px; background: linear-gradient(180deg, var(--red-500), transparent);
}
@media (prefers-reduced-motion: no-preference) {
  .scroll-cue i { animation: cue 1.9s var(--ease-in-out) infinite; transform-origin: top; }
  @keyframes cue { 0%,100% { transform: scaleY(.4); opacity: .5 } 50% { transform: scaleY(1); opacity: 1 } }
}

/* ---------- The scroll spine -------------------------------------------
   Bottom-centre so it never competes with the copy column on the left or
   the spec column on the right. */
.spine {
  position: fixed; z-index: 40; inset-block-end: var(--sp-7); inset-inline: 0;
  display: flex; justify-content: center; align-items: flex-end; gap: var(--sp-5);
  opacity: 0; pointer-events: none; transition: opacity var(--dur-1) var(--ease-out);
}
.spine[data-active="true"] { opacity: 1; pointer-events: auto; transition-duration: var(--dur-3); }
/* Leaving is instant - never a transition the compositor can stall on. */
.spine[data-active="false"] { opacity: 0 !important; pointer-events: none !important; transition: none !important; }
.spine a {
  display: grid; justify-items: center; gap: 10px; text-decoration: none;
  font: var(--text-overline); letter-spacing: var(--ls-overline); text-transform: uppercase;
  color: var(--n-300); transition: color var(--dur-2) var(--ease-out);
  padding-block: 6px;
}
.spine a:hover { color: var(--text-primary); text-decoration: none; }
.spine a span {
  opacity: 0; transform: translateY(4px);
  transition: opacity var(--dur-2) var(--ease-out), transform var(--dur-2) var(--ease-out);
}
.spine a:hover span, .spine a[aria-current="true"] span { opacity: 1; transform: none; }
.spine a i {
  display: block; width: 42px; height: 2px; border-radius: 2px;
  background: currentColor; transition: width var(--dur-2) var(--ease-out), background-color var(--dur-2) var(--ease-out);
}
.spine a[aria-current="true"] { color: var(--red-500); }
.spine a[aria-current="true"] i { width: 72px; }

/* ---------- Exploded spec sheet ----------------------------------------
   Dots are projected from real 3D points each frame; cards sit in a fixed
   column so they can never collide; stage.js draws the leader lines. */
.pins { position: fixed; inset: 0; z-index: 2; pointer-events: none; }
.pins__lines { position: absolute; inset: 0; width: 100%; height: 100%; overflow: visible; }
.pins__lines path {
  fill: none; stroke: var(--red-500); stroke-width: 1;
  opacity: 0; transition: opacity var(--dur-3) var(--ease-out);
}
.pins__lines path[data-on="true"] { opacity: .45; }

.pin-dot {
  position: absolute; inset-block-start: 0; inset-inline-start: 0;
  width: 9px; height: 9px; margin: -4.5px 0 0 -4.5px; border-radius: 50%;
  background: var(--red-500); box-shadow: 0 0 0 4px rgba(192,32,48,.16);
  opacity: 0; transition: opacity var(--dur-3) var(--ease-out);
  will-change: transform;
}
.pin-dot[data-on="true"] { opacity: 1; }

.pin-cards {
  position: absolute; inset-block-start: 50%; inset-inline-end: var(--gutter);
  transform: translateY(-50%);
  width: 268px; display: grid; gap: var(--sp-4);
  opacity: 0; transition: opacity var(--dur-3) var(--ease-out);
}
.pin-cards[data-on="false"] { visibility: hidden; }
.pin-cards[data-on="true"] { opacity: 1; }

.pin-card {
  padding: var(--sp-4);
  transform: translateX(14px);
  transition: transform var(--dur-4) var(--ease-out);
}
.pin-cards[data-on="true"] .pin-card { transform: none; }
.pin-cards[data-on="true"] .pin-card:nth-child(2) { transition-delay: 60ms; }
.pin-cards[data-on="true"] .pin-card:nth-child(3) { transition-delay: 120ms; }
.pin-cards[data-on="true"] .pin-card:nth-child(4) { transition-delay: 180ms; }
.pin-card b { display: block; font: var(--text-h4); color: var(--text-primary); }
.pin-card i { display: block; font-style: normal; margin-top: 4px; font: var(--text-spec); font-size: var(--fs-caption); color: var(--text-accent); }
.pin-card p { margin-top: 8px; font: var(--text-body-sm); color: var(--text-secondary); }
@media (prefers-reduced-motion: reduce) {
  .pin-card { transform: none; }
}

/* ---------- Responsive ------------------------------------------------- */
@media (max-width: 760px) {
  .spine { display: none; }
}
@media (max-width: 900px) {
  .act__copy--right { margin-left: 0; }
  .act--annotated .act__copy, .hero .act__copy { max-width: none; }
  .hero__title .eyebrow { margin-bottom: var(--sp-5); }
  .hero__actions { flex-direction: column; align-items: stretch; }
  .hero__actions .btn { width: 100%; }
  .hero__note { display: grid; gap: var(--sp-2); }
  .scroll-cue { display: none; }
}

/* Portrait - matches the renderer's own `narrow` threshold (aspect < 1.05),
   so the CSS and the camera never disagree about the layout. The product
   holds the top of the viewport and the copy the bottom. */
@media (max-aspect-ratio: 21/20) {
  .act { align-items: end; padding-block: 44svh var(--sp-10); }
  /* …with a scrim behind each copy block, edge to edge, so the copy reads
     over the product as it scrolls past it. It travels and fades with its
     copy (focusCopy() in home.js) and is feathered top and bottom, so the
     product is never cut by a hard edge - it shows through the feather. */
  .act__copy { position: relative; isolation: isolate; }
  /* The copy column is narrower than the screen (and centred in act 2), so
     the scrim overshoots it on both sides and .acts clips it to the
     viewport's edges. */
  .acts { overflow-x: clip; }
  .act__copy::before {
    content: ""; position: absolute; z-index: -1; pointer-events: none;
    inset-block: -64px -56px; inset-inline: -100vw;
    background: linear-gradient(180deg,
      rgba(239,235,229,0) 0,
      rgba(239,235,229,.94) 64px,
      rgba(239,235,229,.94) calc(100% - 56px),
      rgba(239,235,229,0) 100%);
  }
  .hero__lead { font: var(--text-body); color: var(--text-secondary); }
}
/* Landscape below the spec column's width (tablets, landscape phones): the
   copy keeps the left half and the product the right, so the copy never
   runs across it. */
@media (min-aspect-ratio: 21/20) and (max-width: 1179px) {
  .act--annotated .act__copy { max-width: min(560px, 52%); }
  .hero .act__copy { max-width: min(780px, 60%); }
  .hero__actions { flex-direction: row; }
  .hero__actions .btn { width: auto; }
}
/* Short landscape (phones on their side): clear the floating header, and
   size the hero to the height there is. The spine has no room. */
@media (min-aspect-ratio: 21/20) and (max-height: 560px) {
  .act { padding-block: calc(var(--header-h) + 36px) var(--sp-8); }
  .hero__title { font-size: clamp(30px, 10vh, 52px); }
  .hero__title .eyebrow { margin-bottom: var(--sp-3); }
  .hero__lead { margin-top: var(--sp-4); font: var(--text-body-sm); }
  .hero__actions { margin-top: var(--sp-5); }
  .hero__actions .btn--lg { padding-block: 10px; }
  .act .display-lg { font-size: clamp(28px, 9vh, 44px); }
  .act .lead { font: var(--text-body-sm); margin-top: var(--sp-4) !important; }
  .spine, .scroll-cue { display: none; }
}
/* Portrait: the cabinet holds the top of the viewport, so the field plays
   there and fades out above the copy. */
@media (max-aspect-ratio: 21/20) {
  .stage .game-field { --vx: 50%; --vy: 28%; --card-w: clamp(110px, 26vw, 200px); }
  .stage .game-field::after { background: linear-gradient(0deg, var(--stage-bg) 0%, var(--stage-bg) 50%, rgba(239,235,229,0) 68%); }
}
@media (max-width: 620px) {
  .pins { display: none; }
}

/* ---------- Motion ------------------------------------------------------ */
@media (prefers-reduced-motion: no-preference) {
  .pin-cards[data-on="true"] .pin-card::before { animation: rule-in var(--dur-4) var(--ease-out) both; }
  @keyframes rule-in { from { transform: scaleX(0) } to { transform: scaleX(1) } }

  /* Hero entrance: the two headline lines rise, then lead, actions, note. */
  .hero__title .line { display: block; overflow: hidden; }
  .hero__title .line > span { display: block; transform: translateY(110%); animation: rise 720ms var(--ease-out) forwards; }
  .hero__title .line + .line > span { animation-delay: 110ms; }
  .hero .rise { opacity: 0; transform: translateY(14px); animation: rise-soft var(--dur-4) var(--ease-out) forwards; animation-delay: calc(240ms + var(--i, 0) * 90ms); }
  @keyframes rise { to { transform: none } }
  @keyframes rise-soft { to { opacity: 1; transform: none } }

  /* Spec pin cards slide in from the column edge, staggered. */
  .pin-cards[data-on="true"] .pin-card { animation: card-in var(--dur-4) var(--ease-out) both; }
  .pin-cards[data-on="true"] .pin-card:nth-child(2) { animation-delay: 70ms } .pin-cards[data-on="true"] .pin-card:nth-child(3) { animation-delay: 140ms } .pin-cards[data-on="true"] .pin-card:nth-child(4) { animation-delay: 210ms }
  @keyframes card-in { from { opacity: 0; transform: translateX(18px) } to { opacity: 1; transform: none } }
  .pin-dot[data-on="true"] { animation: pulse 2.4s var(--ease-in-out) infinite; }
  @keyframes pulse { 0%,100% { box-shadow: 0 0 0 4px rgba(192,32,48,.16) } 50% { box-shadow: 0 0 0 9px rgba(192,32,48,.06) } }
}

/* =========================================================================
   GAME SHOWCASE - every title's key art as a poster, in two rows that
   drift in opposite directions. The rows and posters are generated by
   .claude/games/sync_games.py. Each track holds its set twice and slides
   by exactly one set, so the loop has no seam; the second set is inert and
   hidden from screen readers. Hover or keyboard focus stops the drift.
   ========================================================================= */
.showcase { overflow: hidden; }
.showcase__rows {
  --poster-w: clamp(150px, 15vw, 232px);
  --poster-gap: clamp(12px, 1.2vw, 20px);
  display: grid; gap: var(--poster-gap); margin-top: var(--sp-9);
  /* The rows run off both edges of the page and fade out there. */
  -webkit-mask-image: linear-gradient(90deg, transparent 0, #000 7%, #000 93%, transparent 100%);
  mask-image: linear-gradient(90deg, transparent 0, #000 7%, #000 93%, transparent 100%);
}
/* Room above for a lifted poster's focus ring; scroll padding keeps a focused
   poster clear of the faded edges. */
.showcase__row { overflow: hidden; padding-block: 12px 14px; scroll-padding-inline: 8vw; }
.showcase__track { display: flex; width: max-content; }
.showcase__set { display: flex; gap: var(--poster-gap); padding-inline-end: var(--poster-gap); margin: 0; list-style: none; }

.poster {
  position: relative; display: block; overflow: hidden; isolation: isolate;
  width: var(--poster-w); aspect-ratio: 4 / 5;
  border-radius: var(--radius-md); background: var(--surface-subtle);
  box-shadow: var(--shadow-sm); color: var(--text-on-dark); text-decoration: none;
  transition: transform var(--dur-3) var(--ease-out), box-shadow var(--dur-3) var(--ease-out);
}
.poster img { display: block; width: 100%; height: 100%; object-fit: cover; transition: transform var(--dur-4) var(--ease-out); }
/* A scrim at the foot so the name reads on any artwork. */
.poster::after {
  content: ""; position: absolute; inset: 45% 0 0; z-index: 0; pointer-events: none;
  background: linear-gradient(180deg, rgba(20,8,8,0) 0%, rgba(20,8,8,.82) 100%);
}
.poster__name {
  position: absolute; inset-inline: var(--sp-4); inset-block-end: var(--sp-4); z-index: 1;
  font: var(--text-h4); text-wrap: balance; text-shadow: 0 1px 8px rgba(0,0,0,.45);
}
.poster__tag {
  position: absolute; inset-block-start: var(--sp-3); inset-inline-end: var(--sp-3); z-index: 1;
  font: var(--text-overline); letter-spacing: var(--ls-overline); text-transform: uppercase;
  padding: 3px 9px; border-radius: var(--radius-pill); background: var(--gradient-mark); color: var(--text-on-brand);
}
.poster:hover, .poster:focus-visible { transform: translateY(-4px); box-shadow: var(--shadow-lg); text-decoration: none; }
.poster:hover img, .poster:focus-visible img { transform: scale(1.05); }
.poster:focus-visible { outline: 3px solid var(--text-brand); outline-offset: 3px; }
@media (max-width: 760px) {
  .poster__name { inset-inline: var(--sp-3); inset-block-end: var(--sp-3); font: var(--text-label); }
}

@media (prefers-reduced-motion: no-preference) {
  .showcase__track { animation: showcase-drift 90s linear infinite; }
  .showcase__row[data-reverse] .showcase__track { animation-direction: reverse; animation-duration: 100s; }
  .showcase__row:hover .showcase__track { animation-play-state: paused; }
  /* Keyboard focus (home.js): the drift stops and the row scrolls instead. */
  .showcase__row[data-held] .showcase__track { animation: none; }
  @keyframes showcase-drift { to { transform: translateX(-50%); } }
}
/* No drift: each row is a plain strip the reader scrolls sideways. */
@media (prefers-reduced-motion: reduce) {
  .showcase__row { overflow-x: auto; scroll-snap-type: x mandatory; padding-inline: var(--gutter); }
  .showcase__set[aria-hidden] { display: none; }
  .showcase__set li { scroll-snap-align: start; }
}
