/* ============================================================
   THUNDERSTRIKE — scroll-scrubbed robot stage
   Paired with robot-scrub.js. Loaded only by the robot page.

   The render's own backdrop is a flat #282828 with a vignette baked in,
   so the stage wears that colour and lays its own vignette over the top.
   The seam between drawn frame and stage plate then has nothing to give
   it away, and the section can run full-bleed without a visible box.
   ============================================================ */

/* One outer gutter for everything on this page — hero copy, section lead-ins,
   the annotation column and the game rows all start on the same vertical.
   Only the pinned render is allowed to reach the viewport edge. */
:root {
  --rp-gutter: max(6vw, 26px);

  /* The rung the page was missing. Every editorial block used to cut straight
     from display caps to 18px body — an 80px headline with a 17px sentence
     under it reads as a billboard with a caption, not as a thought with a
     second line. This is the size a supporting sentence gets. */
  --rp-lede: clamp(20px, 1.05rem + 0.75vw, 27px);
}

.scrub {
  position: relative;
  height: calc(var(--scrub-vh, 14) * 100vh);
  background: #282828;
}

.scrub-pin {
  position: sticky;
  top: 0;
  height: 100vh;
  height: 100svh;
  overflow: hidden;
  background: #282828;
}

.scrub-pin canvas {
  position: absolute;
  inset: 0;
  display: block;
}

/* Our vignette, over the frame. Doubles as the blend into the black page
   above and below, so the section has no hard edge anywhere. */
.scrub-pin::after {
  content: "";
  position: absolute;
  inset: 0;
  pointer-events: none;
  background:
    linear-gradient(to bottom, #000 0%, rgba(0, 0, 0, 0) 11%, rgba(0, 0, 0, 0) 87%, #000 100%),
    radial-gradient(82% 68% at 50% 46%, rgba(0, 0, 0, 0) 48%, rgba(0, 0, 0, 0.42) 100%);
}

.scrub-pin svg[data-scrub-lines] {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  overflow: visible;
  pointer-events: none;
  z-index: 5;
}

/* ---- the callout ----
   The line has to cross whatever the render happens to put underneath it —
   brushed aluminium one moment, black anodise the next — so it carries its
   own soft shadow rather than relying on the frame to stay dark. */
.sn-leader {
  fill: none;
  stroke: rgba(255, 255, 255, 0.82);
  stroke-width: 1.25;
  stroke-linejoin: round;
  stroke-linecap: round;
  vector-effect: non-scaling-stroke;
  filter: drop-shadow(0 0 3px rgba(0, 0, 0, 0.9));
}

.sn-dot {
  fill: var(--thunder-orange);
  filter: drop-shadow(0 0 6px rgba(0, 0, 0, 0.85));
}

.sn-ring {
  fill: none;
  stroke: var(--thunder-orange);
  stroke-width: 1.25;
}

[data-scrub-notes] {
  position: absolute;
  inset: 0;
  z-index: 4;
  pointer-events: none;
}

.scrub-note {
  position: absolute;
  z-index: 2;
  width: min(700px, 48vw);
  margin: 0;
  opacity: 0;
  visibility: hidden;
  will-change: opacity, transform;
}

/* A soft pool of shade, not a card. The text has to stay legible when the
   frame behind it is polished aluminium, but a hard panel would look pasted
   onto the render — this reads as depth of field instead. */
/* Shade under the text.

   A pool centred on the block reads as a slab floating in the middle of the
   render — it starts and stops in mid-air with nothing anchoring it. The
   shade instead runs off the same viewport edge the text is anchored to and
   covers the full height of the stage, so it reads as the frame falling away
   into shadow rather than as a panel laid on top of it. It is flipped to
   whichever side the live annotation landed on. */
[data-scrub-notes]::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -1;
  pointer-events: none;
  opacity: 0;
  transition: opacity 200ms ease;
  background: linear-gradient(to right,
    rgba(0, 0, 0, 0.94) 0%,
    rgba(0, 0, 0, 0.92) calc(var(--shade-solid, 34%) * 0.5),
    rgba(0, 0, 0, 0.88) var(--shade-solid, 34%),
    rgba(0, 0, 0, 0.5) calc((var(--shade-solid, 34%) + var(--shade-end, 62%)) * 0.5),
    rgba(0, 0, 0, 0) var(--shade-end, 62%));
}

[data-scrub-notes].notes-right::before { opacity: 1; }
[data-scrub-notes].notes-left::before { opacity: 1; transform: scaleX(-1); }

/* A little extra local shade right under the words, for the case where the
   render happens to put something bright at the far end of a long headline. */
.scrub-note::before {
  content: "";
  position: absolute;
  inset: -64px -120px -58px -120px;
  z-index: -1;
  pointer-events: none;
  background: radial-gradient(56% 60% at 50% 50%,
    rgba(0, 0, 0, 0.5) 0%,
    rgba(0, 0, 0, 0.4) 52%,
    rgba(0, 0, 0, 0) 100%);
}

@media (max-width: 760px) {
  [data-scrub-notes]::before { display: none; }
  .scrub-note::before {
    inset: -34px -30px -60px -30px;
    background: linear-gradient(to bottom,
      rgba(0, 0, 0, 0) 0%,
      rgba(0, 0, 0, 0.72) 20%,
      rgba(0, 0, 0, 0.84) 55%,
      rgba(0, 0, 0, 0.84) 100%);
  }
}

.scrub-note.side-right { left: var(--rp-gutter); }
.scrub-note.side-left { right: var(--rp-gutter); }

.sn-label {
  display: block;
  font-family: var(--font-mono);
  font-size: 17px;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--thunder-orange);
  margin-bottom: 16px;
}

.scrub-note h3 {
  font-family: var(--font-display);
  font-weight: 800;
  text-transform: uppercase;
  font-size: clamp(34px, 5.6vw, 80px);
  line-height: 1.02;
  letter-spacing: 0.004em;
  color: #fff;
  margin: 0 0 18px;
  text-wrap: balance;
}

.scrub-note p {
  font-family: var(--font-body);
  font-size: 18px;
  line-height: 1.22;
  color: rgba(244, 242, 238, 0.92);
  margin: 0;
  max-width: 48ch;
}


.scrub-note p b { color: #fff; font-weight: 400; }

[data-scrub-notes]::after {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  height: 46%;
  pointer-events: none;
  opacity: 0;
  transition: opacity 260ms ease;
  background: linear-gradient(to top, rgba(0, 0, 0, 0.92) 0%, rgba(0, 0, 0, 0.6) 34%, rgba(0, 0, 0, 0) 100%);
}

.scrub.is-beat [data-scrub-notes]::after { opacity: 1; }

/* The breather between subsystems. Anchored low so it never lands on the
   robot — the whole point of these two stops is that you finally see the
   machine whole. */
.scrub-beat {
  position: absolute;
  z-index: 2;
  left: 50%;
  bottom: clamp(34px, 6vh, 74px);
  transform: translateX(-50%);
  width: min(880px, 84vw);
  margin: 0;
  text-align: center;
  font-family: var(--font-display);
  font-weight: 800;
  text-transform: uppercase;
  font-size: clamp(26px, 3.9vw, 56px);
  line-height: 1.04;
  letter-spacing: 0.005em;
  color: #fff;
  text-wrap: balance;
  opacity: 0;
  visibility: hidden;
  will-change: opacity, transform;
}

.scrub-beat::after {
  content: "";
  position: absolute;
  left: 50%;
  bottom: -26px;
  width: 54px;
  height: 3px;
  margin-left: -27px;
  background: var(--thunder-orange);
}

/* ---- phone: no sideways room, so the text sits under the robot ---- */
@media (max-width: 760px) {
  /* The phone frame is a band from ~8% to ~50% of the stage; below it the
     #282828 render backdrop showed as a grey "chin". Keep the frame body
     clear, then fade hard to black by ~58% so the chin reads as page, and
     thin the top band so the frame's top isn't clipped. */
  .scrub-pin::after {
    background:
      linear-gradient(to bottom, #000 0%, rgba(0, 0, 0, 0) 7%, rgba(0, 0, 0, 0) 46%, #000 58%);
  }

  .scrub-note,
  .scrub-note.side-left,
  .scrub-note.side-right {
    left: var(--rp-gutter);
    right: var(--rp-gutter);
    width: auto;
    text-align: left;
  }


  .scrub-note h3 { font-size: 46px; }
  .scrub-note p { font-size: 18px; line-height: 1.24; max-width: none; }
}

/* ---- reduced motion: the stops become a plain list ---- */
.scrub.scrub-static { height: auto; background: #000; }
.scrub.scrub-static .scrub-pin { display: none; }

.scrub-fallback {
  display: grid;
  gap: clamp(40px, 7vw, 78px);
  max-width: 1160px;
  margin: 0 auto;
  padding: clamp(48px, 8vh, 90px) 34px;
}

.scrub-fallback figure { margin: 0; display: grid; gap: 20px; }
.scrub-fallback img { width: 100%; border-radius: 10px; }
.scrub-fallback h3 {
  font-family: var(--font-display);
  font-weight: 800;
  text-transform: uppercase;
  font-size: clamp(20px, 3vw, 30px);
  color: #fff;
  margin: 8px 0 10px;
}
.scrub-fallback p {
  font-family: var(--font-body);
  font-size: 16px;
  line-height: 1.6;
  color: var(--fg-on-dark-muted);
  margin: 0;
  max-width: 56ch;
}

@media (min-width: 860px) {
  .scrub-fallback figure { grid-template-columns: 1.25fr 1fr; align-items: center; gap: 44px; }
}

/* If the manifest never arrives there is nothing to scrub — collapse the
   section rather than leaving a tall empty grey band. */
.scrub.scrub-dead { height: auto; }
.scrub.scrub-dead .scrub-pin { display: none; }

/* ============================================================
   Page furniture around the stage.

   Sizes are set against measurements taken off apple.com/macbook-pro
   and apple.com/iphone-17-pro at 1440 and 390: the largest headline on
   the page is a section headline, the hero sits at ~0.8x of it, and body
   copy holds a >=3.5:1 gap below the peak. Headline leading stays under
   1.13 so a two-line headline reads as one object.
   ============================================================ */

.rp-hero {
  position: relative;
  min-height: 100vh;
  min-height: 100svh;
  display: flex;
  flex-direction: column;
  justify-content: flex-end;
  overflow: hidden;
  background: #000;
}

.rp-hero-media {
  position: absolute;
  inset: 0;
  z-index: 0;
}

.rp-hero-media img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: center 46%;
}

/* The render's own floor fades to near-black at the bottom left, so the
   scrim only has to carry the last stretch rather than dim the machine. */
.rp-hero-media::after {
  content: "";
  position: absolute;
  inset: 0;
  background:
    linear-gradient(to top, #000 2%, rgba(0, 0, 0, 0.72) 22%, rgba(0, 0, 0, 0) 58%),
    linear-gradient(to right, rgba(0, 0, 0, 0.55) 0%, rgba(0, 0, 0, 0.28) 18%, rgba(0, 0, 0, 0) 34%);
}

.rp-hero-copy {
  position: relative;
  z-index: 1;
  width: 100%;
  max-width: 1340px;
  margin: 0 auto;
  padding: 0 var(--rp-gutter) clamp(64px, 11vh, 118px);
}

.rp-eyebrow {
  font-family: var(--font-mono);
  font-size: 17px;
  font-weight: 500;
  letter-spacing: 0.26em;
  text-transform: uppercase;
  color: var(--thunder-orange);
  margin: 0 0 24px;
}

.rp-hero h1 {
  font-family: var(--font-display);
  font-weight: 800;
  text-transform: uppercase;
  font-size: clamp(38px, 4.6vw, 66px);
  line-height: 0.98;
  letter-spacing: 0.004em;
  color: #fff;
  margin: 0 0 22px;
  max-width: 15ch;
  text-wrap: balance;
}

.rp-hero-sub {
  font-family: var(--font-body);
  font-size: var(--rp-lede);
  line-height: 1.2;
  color: rgba(244, 242, 238, 0.82);
  margin: 0;
  max-width: 32ch;
}

/* The programme's name should not break across lines. */
.nb { white-space: nowrap; }

.rp-hero-sub b { color: #fff; font-weight: 500; }

/* ---- section lead-ins ---- */
.rp-lead {
  padding: clamp(110px, 20vh, 240px) var(--rp-gutter) clamp(96px, 17vh, 200px);
  text-align: center;
  background: #000;
}

.rp-lead .rp-eyebrow { margin-bottom: 34px; }

.rp-peak {
  font-family: var(--font-display);
  font-weight: 800;
  text-transform: uppercase;
  font-size: clamp(42px, 5.6vw, 80px);
  line-height: 1.0;
  letter-spacing: 0.004em;
  color: #fff;
  margin: 0 auto;
  max-width: 15ch;
  text-wrap: balance;
}

.rp-body {
  font-family: var(--font-body);
  font-size: var(--rp-lede);
  line-height: 1.22;
  color: rgba(244, 242, 238, 0.7);
  max-width: 46ch;
  margin: clamp(40px, 6vh, 74px) auto 0;
}

/* One clause carries the weight and the rest of the sentence steps back —
   the emphasis stays inside the paragraph instead of being pulled out into
   a callout that would need its own space. */
.rp-body b { color: #fff; font-weight: 400; }

/* Names the game the paragraph is describing, without becoming a heading.
   Orange rather than another white <b>, so it reads as a label and does not
   compete with the emphasised clause further into the sentence. Named
   .rp-gamelabel, not .rp-game — that one is the DECODE card layout below. */
.rp-body .rp-gamelabel { color: var(--thunder-orange); font-weight: 600; }

.rp-lead.rp-tight { padding-bottom: clamp(56px, 9vh, 110px); }

@media (max-width: 760px) {
  .rp-eyebrow { font-size: 15px; letter-spacing: 0.2em; }
  .rp-hero h1 { max-width: none; }
  .rp-hero-sub { max-width: none; }
  .rp-hero-media img { object-position: center 42%; }
  .rp-peak { max-width: none; }
}

/* ---- the game, explained ----
   Three ideas, one per screen, each with the picture doing the explaining.
   The earlier version put these in a 3-up thumbnail row; at that size a
   reader could not tell a goal from a referee, which is the whole job. */
.rp-game {
  display: grid;
  grid-template-columns: 1fr;
  gap: clamp(16px, 3vw, 34px);
  align-items: center;
  max-width: 1340px;
  margin: 0 auto;
  padding: clamp(46px, 8vh, 96px) var(--rp-gutter);
}

.rp-game figure { margin: 0; }

.rp-game img {
  width: 100%;
  height: clamp(300px, 58vh, 620px);
  object-fit: cover;
  border-radius: 20px;
  background: #111;
}

.rp-game-copy h3 {
  font-family: var(--font-display);
  font-weight: 800;
  text-transform: uppercase;
  font-size: clamp(24px, 2.9vw, 40px);
  line-height: 1.02;
  letter-spacing: 0.004em;
  color: #fff;
  margin: 0 0 18px;
  text-wrap: balance;
}

.rp-game-copy p {
  font-family: var(--font-body);
  font-size: clamp(18px, 1.4vw, 20px);
  line-height: 1.28;
  color: rgba(244, 242, 238, 0.62);
  margin: 0;
  max-width: 40ch;
}

.rp-game-copy p b { color: #fff; font-weight: 400; }

@media (min-width: 900px) {
  .rp-game { grid-template-columns: 1.15fr 0.85fr; gap: clamp(40px, 5vw, 84px); }
  .rp-game.rev figure { order: 2; }
  .rp-game.rev .rp-game-copy { order: 1; }
}

/* The specification is a reference block, not a read — two columns on a wide
   screen so it stops being a single column taller than the viewport, and its
   labels come up off the floor. */
@media (min-width: 900px) {
  .spectable {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: clamp(26px, 3vw, 54px);
    align-items: start;
  }
}

.spectable .sp-head { font-size: 15px; }
.spectable .sp-line dt { font-size: 17px; }
.spectable .sp-line dd { font-size: 17px; }

/* ---- the hero on a phone ----
   A 16:9 render cropped to fill a 390x844 window keeps about a quarter of the
   robot and reads as a detail of something, not as a machine. So the phone
   stops overlaying: the render takes a 4:3 band across the top — wide enough
   to hold the whole robot, since it occupies the middle 60% of the frame —
   and the copy sits below it on black, where it needs no scrim at all. */
@media (max-width: 760px) {
  .rp-hero {
    min-height: 100svh;
    display: flex;
    flex-direction: column;
    justify-content: center;
    gap: clamp(26px, 5vh, 52px);
    padding-bottom: clamp(46px, 8vh, 72px);
  }

  /* The source is 16:9 and the robot fills its middle sixty per cent, so a
     band tall enough to own a portrait screen would have to cut the machine in
     half. The hero takes the whole viewport instead by giving the copy the
     room the crop can't have. */
  .rp-hero-media { flex: 0 0 auto; }

  .rp-hero-media {
    position: relative;
    inset: auto;
    aspect-ratio: 7 / 6;
  }

  .rp-hero-media img { object-position: center 48%; }

  .rp-hero-media::after {
    background: linear-gradient(to top, #000 1%, rgba(0, 0, 0, 0.45) 16%, rgba(0, 0, 0, 0) 44%);
  }

  .rp-hero-copy { padding: 0 var(--rp-gutter); }
}

/* A second headline tier, for sections that set the scene rather than make a
   claim about the machine. The page's largest type belongs on the robot. */
.rp-peak.rp-scene { font-size: clamp(42px, 5.6vw, 80px); }

/* The closing claim comes from site.css at 66px, which put it level with the
   annotations; step it into the scene tier so the ladder keeps its rungs. */
.claim h2 { font-size: clamp(46px, 5.6vw, 80px); }

/* In the spec table the label and the value were the same size, so the densest
   block on the page carried no hierarchy at all — only a tint difference. */
.spectable .sp-line dt { font-size: 17px; line-height: 1.32; }
.spectable .sp-line dd { font-size: 18px; line-height: 1.32; color: #fff; }

/* The line under the hero subtitle — where the robot was made, and by whom.
   Quieter than the subtitle it sits under, but still above the editorial
   floor, because it carries the part of the claim people remember. */
.rp-hero-fine {
  font-family: var(--font-body);
  font-size: 18px;
  line-height: 1.3;
  color: rgba(244, 242, 238, 0.66);
  margin: 18px 0 0;
  max-width: 46ch;
}

/* A portrait source in a landscape well loses the faces. SITE-FACTS §5.3 is
   explicit about that, so portrait cards get a taller, narrower picture and
   the copy takes the rest of the row. */
.rp-game.portrait img {
  aspect-ratio: 4 / 5;
  height: auto;
  max-height: min(74vh, 720px);
  object-position: center 32%;
}

@media (min-width: 900px) {
  .rp-game.portrait { grid-template-columns: 0.78fr 1fr; }
}

@media (max-width: 760px) {
  .rp-hero-fine { font-size: 17px; max-width: none; }
}

/* Supporting sentences that follow a display headline, wherever they appear on
   this page. Colour comes up with the size — the closing line under the
   biggest headline on the page was measuring #545454 and barely registering. */
.claim p,
.rp-lead .lede,
section.block .lede {
  font-size: var(--rp-lede);
  line-height: 1.24;
  color: rgba(244, 242, 238, 0.7);
  max-width: 44ch;
}

.claim p { margin-top: 28px; }

/* The same label was 17px inside the pinned section and 12px below it. */
.eyebrow-lg {
  font-size: 17px;
  letter-spacing: 0.18em;
  margin-bottom: 18px;
}
