/* ═══════════════════════════════════════════════════════════════════════
   DreamPlay Concise — live presentation stage.
   Target device: MacBook 14 inch, PRESENTED FULLSCREEN: the stage is
   1512x982. In a browser window with chrome it is ~1512x860; everything
   here is derived from the live viewport, so both are correct.

   ONE HEADING SIZE
   The deck used to run two competing title sizes: .stage-title at 40px and
   .stage-sub at 26px. Adjacent scenes jumped between them, which reads as
   inconsistency rather than as hierarchy. There is now exactly one heading
   size — 40px, weight 600 — and it belongs to the scene that OPENS a
   chapter. A scene that continues a chapter carries the 12px kicker and,
   ONE heading size. Every scene titles itself at 40px, the chapter-title
   size, whether the line is a statement or just a name. The 18px
   .stage-label role is retired: two heading sizes read as two hierarchies
   from across a room, and there is only one here.

   THE CENTRING MODEL
   Every scene is a THREE row grid:
       minmax(--band, 1fr)  ·  auto  ·  minmax(--gutter, 1fr)
   The content is row two. Rows one and three are equal flexible tracks with
   floors, so:
     · short content splits the free space evenly and lands on the true
       centre of the viewport, not on the centre of the space under the
       title band;
     · tall content bottoms out on row one's floor (the title band) and
       still cannot come closer than --gutter to the bottom edge.
   The old model was `--band 1fr`, which centred content inside row two only
   and therefore sat every scene (band/2) px low.

   --band is the FLOOR under the title, not padding. It is set per scene in
   the markup from that scene's own header stack:
     kicker only ................ 92
     kicker + 40px title ....... 169   (40px under the title's baseline box)
     cover (title + lede stack)  172
   --gutter is the minimum clearance to the bottom edge: 40px everywhere.

   CONTENT IMAGES vs BACKGROUND IMAGES
   A content image (.plate board, .frame-phone, showcase strip) carries its
   own information and often its own type. No deck text may cross one.
   A background image (.bleed cover art, .film__bg blurred backdrop) is a
   ground, and text over it is expected. Only those two carry a scrim.

   MEDIA IS HEIGHT-BOUND, NOT WIDTH-BOUND
   A board's --pw is derived from its own aspect ratio against the height
   left under its own band, so the picture plus its caption always clears
   the floor. Nothing in the deck is sized from width alone any more.
   ═══════════════════════════════════════════════════════════════════ */

:root{
  --ground-1:#000000;
  --ink-1:#F5F3F7; --ink-2:rgba(245,243,247,.72); --ink-3:rgba(245,243,247,.58);
  /* Retained: the gate and the closing slide reference these by name. The
     gate is the same ground the deck opens onto, so it tracks --ground-1. */
  --dark-ground:#000000;
  --dark-ink-1:#F5F3F7; --dark-ink-2:rgba(245,243,247,.72); --dark-ink-3:rgba(245,243,247,.58);
  --indigo:#6A00F4; --chartreuse:#ADFF2F; --payne:#34455A;
  --indigo-on-dark:#9B5CFF;    /* 5.37:1 here, where --indigo is 2.92:1 */
  --pumpkin:#C2410C;           /* a GROUND: white on it is 5.18:1 */
  --signal:var(--chartreuse);  /* an INK: 17.12:1 on black */

  --font-display:'Schibsted Grotesk',system-ui,sans-serif;

  /* Fixed desktop values. Production clamps are already pinned at 1440. */
  --stage-title:40px;          /* the deck's ONLY heading size */
  --stage-kicker:14px;         /* .ch-eyebrow; 12px uppercase at .1em does not carry a room */
  --mediacap-title:18px;       /* .cs-mediacap-title, .frame-cap-title */
  --mediacap:15px;             /* .cs-mediacap */
  --statement:19px;

  --safe-x:64px; --title-top:56px;
  --foot-clear:96px;           /* chrome band 927-958 plus the 3px rule */
  --band:92px;                 /* kicker-only fallback */
  --gutter:40px;               /* minimum clearance at the floor */
  --plate-x:64px;
  /* A title that fits on one line must BE on one line. The longest title in
     the deck, "A low-cost way into the Dream Sports ecosystem.", measures
     915px at 40/600, so the measure is set just above it. */
  --title-measure:944px;
  --row-h:600px;               /* frame + caption must fit the well */
  --hero-h:700px;              /* single phone, brand film */
  /* The header stack measures 56 to 129. 39px of air under a 40px title read
     as crowding at the top and emptiness in the middle, so the band is 55.
     The floor stops at 908 because #counter and #mute occupy 927 to 958. */
  --well-top:208px; --well-bottom:884px; --well-w:1384px;

  /* Rhythm ladder. Proximity has to encode grouping, so each step out is at
     least 1.4x the step inside it and the group step is more than 2x. */
  --gap-in:16px;      /* a label to its own body            */
  --gap-set:28px;     /* sibling to sibling inside one set  */
  --gap-group:64px;   /* one group to a different group     */
  --gap-col:72px;     /* column gutter in a .split          */
  --gap-media:24px;   /* a picture to its own caption       */

  --display:72px;     /* statement scenes only              */
  --lead:30px; --pair:26px; --lead-body:22px;

  --s2:8px; --s3:12px; --s4:16px; --s5:24px; --s6:32px; --s7:48px; --s8:64px;
  --r-capsule:11px; --r-tile:18px; --r-phone:20px;
  --r-card:22px;               /* the reference card radius; --r-tile stays 18 for .plate--edge */
  --card-fill:linear-gradient(180deg,#2E2240 0%,#261D35 100%);
  --border-1:rgba(245,243,247,.14);

  --ease-standard:cubic-bezier(.4,0,.2,1);
  --ease-in-out:cubic-bezier(.42,0,.58,1);
  --ease-decel:cubic-bezier(0,0,.2,1);
  --ease-land:cubic-bezier(.16,1,.3,1);   /* position settles, no overshoot */
}

*,*::before,*::after{box-sizing:border-box}
html,body{margin:0;height:100%;background:var(--ground-1);overscroll-behavior:none;
  scrollbar-width:none;-ms-overflow-style:none}
html::-webkit-scrollbar,body::-webkit-scrollbar{width:0;height:0;display:none}
body{font-family:var(--font-display);color:var(--ink-1);-webkit-font-smoothing:antialiased}
#stage{-webkit-user-select:none;user-select:none}

/* The three row grid described at the top of the file. The title, the cover
   art and the film backdrop are absolutely positioned, so the ONLY in-flow
   child is the content block. */
.scene{position:relative;width:100vw;height:100dvh;overflow:hidden;
  display:block;background:var(--ground-1)}
/* Content is placed in row 2 explicitly, and ONLY the content. The title
   must NOT be listed: giving an absolutely positioned grid child a definite
   grid area re-bases its top:56px onto that area. */
/* One well, every working scene, absolutely placed. 1384 x 734. */
.scene > .plate,.scene > .ranked,.scene > .board,.scene > .spec,
.scene > .split,.scene > .frame-row,.scene > .cover-brief,.scene > .flow,
.scene > .stack,.scene > .statement{
  position:absolute;left:var(--safe-x);top:var(--well-top);
  width:var(--well-w);height:calc(var(--well-bottom) - var(--well-top));
  margin:0;padding-left:0;padding-right:0}

/* Vertical centring, declared per container so it never rides on transform.
   .frame-row stays top-aligned on purpose: its cells carry captions of
   different depths, and centring each cell would step the phone tops out of
   line for the sake of ~30px. */
.scene > .plate,.scene > .stack,.scene > .ranked,.scene > .flow,
.scene > .spec,.scene > .statement{justify-content:flex-start}
.scene > .cover-brief{justify-content:center}
.scene > .split{align-content:start}
/* Two deliberate exceptions to the top line, both asked for by name.
   The opportunity's picture drops to the foot of the text beside it, and the
   fairplay scene centres its pair of columns. */
.plate--bottom{align-self:end}
.scene--vcentre > .split{align-content:center}
/* Text wells distribute: lead at the top, the demoted rank on the floor, so
   the space sits BETWEEN the ranks where it reads as hierarchy. */
.scene > .frame-row{align-items:flex-start;justify-content:flex-start}
.scene > .split{align-items:start}
/* Kept dark on purpose: the cover, whose artwork is dark, and slide 2. */
/* Pinned top. These scenes carry frames and rails that should all begin at
   the same height down the page, so row one is a fixed track rather than a
   flexible one with a floor: content starts at --band, full stop, instead of
   drifting with its own height. 212px is where slide 17 sat, and that is the
   reference the deck now holds. */
.scene--pin{grid-template-rows:var(--band) auto minmax(var(--gutter),1fr)}

/* .scene--dark used to remap the ink tokens for the two scenes that stayed
   dark while the rest of the deck was light. The base is dark now, so every
   one of those rules restated what it already inherits. The class stays on
   the markup, and on the cover it still pairs with .scene--cover, but it no
   longer needs to declare anything. */
/* The closing statement has no title band at all, so it centres on the whole
   screen. The cover is the opposite case: its lede and figures are the
   continuation of the title stack and must hang off the title, not float. */
.scene--payne{background:var(--indigo);
  display:flex;align-items:center;justify-content:center;padding:0 var(--safe-x)}
/* .closer takes no colour of its own, so it inherited body's --ink-1. On the
   payne ground that measured 2.59:1. White is 7.29:1. */
.scene--payne .closer{color:#FFFFFF}

.scene--cover{grid-template-rows:var(--band) 1fr}
.scene--cover > .cover-brief{grid-row:2;align-self:start}

/* ── The one title position ─────────────────────────────────────────── */
.anchor--tl{position:absolute;z-index:5;top:var(--title-top);left:var(--safe-x);
  max-width:var(--title-measure);text-align:left}
.anchor--center{position:relative;text-align:center;max-width:1200px;margin:0 auto;
  padding:0 var(--safe-x)}

.kicker{font-size:var(--stage-kicker);font-weight:600;letter-spacing:.12em;line-height:1.2;
  text-transform:uppercase;color:var(--signal,var(--ink-3));margin:0 0 var(--s3)}
.stage-title{font-size:var(--stage-title);font-weight:600;letter-spacing:-.02em;
  line-height:1.15;margin:0}
/* The closing statement is a statement, not a scene title, but it is still
   the deck's one heading size. */
.closer{font-size:var(--stage-title);font-weight:600;letter-spacing:-.02em;
  line-height:1.15;margin:0}
.stage-cap{font-size:var(--statement);line-height:1.5;color:var(--ink-2);margin:0}
.stage-cap b{font-weight:600;color:var(--ink-1)}
/* Chartreuse measures 14.66:1 on the card fill and indigo only 2.50:1, so on
   this ground the accent is chartreuse. .board dt sets a colour of its own and
   at (0,1,1) outranks a bare .tint-c at (0,1,0), so the accent is matched on
   the element to win where it is applied. */
.tint-c{color:var(--chartreuse)}
.board dt.tint-c{color:var(--chartreuse)}

/* ── Full-bleed still. Cover art only; every other board is a .plate. ── */
.bleed{position:absolute;inset:0;width:100%;height:100%;object-fit:cover}

/* ── Board plate: picture and caption as one block, so the caption wraps
      inside the picture's own left and right edges. --pw is set per scene
      from the asset's real aspect ratio against the height available under
      that scene's own band, so no board can push its caption into the
      chrome. --mh is the same number expressed as a hard ceiling. ────── */
.plate{--wellh:calc(var(--well-bottom) - var(--well-top));
  position:relative;z-index:2;margin:0;display:flex;flex-direction:column;
  align-items:flex-start;justify-content:flex-start;
  width:auto;max-width:100%;max-height:var(--wellh)}
.plate img{display:block;width:auto;height:auto;
  max-width:100%;max-height:calc(var(--wellh) - var(--cap,0px));object-fit:contain}
.plate figcaption{order:1;font-size:var(--mediacap);line-height:1.55;
  color:var(--ink-3);margin:14px 0 0;width:100%}

/* A board exported from Figma is itself white. On a white slide it therefore
   has no visible edge, and the caption underneath reads as floating rather
   than as belonging to the picture. The hairline is the portfolio's own
   .cs-flat treatment (border-1 at the tile radius), which gives the picture
   an edge for the caption to line up against. The cover-adjacent opportunity
   board is excluded: its artwork already ends on a dark ground. */
.plate--edge img{border:1px solid var(--border-1);border-radius:var(--r-tile)}

/* ── A board exported on white ─────────────────────────────────────────
   Four boards in this deck were exported light. Dropped straight onto
   #000000 the edge is a 19:1 luminance step, so the board reads as a lit
   rectangle rather than as a picture. The mount steps white to Payne's Gray
   to near-black to ground. It is a shadow ring: it costs no layout, nothing
   is cropped, padded or resized, and the asset renders at its exact exported
   aspect. .plate--edge's hairline is rgba(245,243,247,.14), invisible against
   white, so these four scenes drop it. */
.plate--light img{border:0;border-radius:var(--r-tile);
  box-shadow:0 0 0 8px var(--payne),
             0 0 0 20px #14161C,
             0 18px 44px rgba(0,0,0,.6)}

/* ── The statement scene ───────────────────────────────────────────────
   The deck ran ten type sizes inside a 3.3x range, all in one of three
   greys, so nothing could be a moment. This is the top end. Four scenes
   carry it and nothing else does: at 72px against a deck that otherwise
   stops at 40, the room knows without being told which four are the
   argument and which are the evidence. The 64x4 chartreuse rule is not new
   furniture, it is the mark .cover-shipped-head::before already draws,
   at presentation scale. */
.statement{position:relative;z-index:2;display:flex;flex-direction:column;
  align-items:flex-start;gap:var(--gap-group)}
.statement-lead{font-size:var(--display);font-weight:700;letter-spacing:-.03em;
  line-height:1.03;color:var(--ink-1);margin:0 0 var(--s5);max-width:22ch}
.split .statement-lead{font-size:44px;max-width:none}
.split .statement-sub{margin:0 0 var(--gap-group)}
.statement-lead::before{content:"";display:block;width:64px;height:4px;
  border-radius:2px;background:var(--chartreuse);margin:0 0 var(--s5)}
.statement-lead em{font-style:normal;color:var(--indigo-on-dark)}
.statement-sub{font-size:var(--lead-body);line-height:1.5;color:var(--ink-2);
  margin:0;max-width:56ch}
.statement-q{font-size:24px;font-weight:600;line-height:1.35;
  color:var(--signal,var(--chartreuse));margin:0 0 var(--s6);max-width:46ch}
.split .rank-rest{border-top:0;padding-top:0;max-width:none}
.scene--statement{--signal:var(--chartreuse);--well-top:140px}
.scene--statement > .statement{justify-content:flex-start}

/* The closer is the last thing the room sees, so it is the largest thing in
   the deck. White on Payne's Gray is 9.79:1; chartreuse on it is 7.99:1. */
.scene--payne .closer{font-size:64px;line-height:1.1;letter-spacing:-.03em}
.scene--payne .closer em{font-style:normal;color:var(--chartreuse)}

/* ── The chapter card ──────────────────────────────────────────────────
   Nine of these, one at each chapter turn. A deck of 27 dark scenes gives a
   room no way to feel the narrative move; a full-bleed chartreuse ground at
   128px does it in the two seconds it is on screen, without a word of
   explanation. Black on #ADFF2F is 17.12:1, so the boldest thing in the deck
   is also the most legible. It is the same chartreuse already used for the
   statement rule and the closer's number, so the deck gains a chapter signal
   rather than a new colour. */
.scene--chapter{background:var(--pumpkin);color:#fff}
.ch-card{position:absolute;left:var(--safe-x);top:var(--title-top);
  right:var(--safe-x);bottom:var(--foot-clear);
  display:flex;flex-direction:column;justify-content:center;align-items:flex-start}
.ch-num{font-size:var(--stage-kicker);font-weight:700;letter-spacing:.14em;
  text-transform:uppercase;color:#fff;margin:0 0 var(--s4)}
.ch-title{font-size:128px;font-weight:800;letter-spacing:-.005em;word-spacing:.08em;line-height:1.0;
  color:#fff;margin:0 0 var(--s6);max-width:1300px}
.ch-next{font-size:24px;font-weight:700;line-height:1.35;color:#fff;
  margin:0;max-width:900px}
/* The chrome is near-white by default and would vanish on chartreuse. */
.on-chapter #counter,.on-chapter #mute{color:rgba(255,255,255,.86);text-shadow:none}

/* ── Chapter progress ──────────────────────────────────────────────────
   A 36-scene deck gives a room no sense of how far through it is. A 3px rule
   at the very bottom answers that without taking a word or a pixel of the
   stage. It reads as the chapter signal because it is the same chartreuse. */
#progress{position:fixed;left:0;right:0;bottom:0;height:3px;z-index:45;
  background:rgba(245,243,247,.12);pointer-events:none}
#progress i{display:block;height:100%;width:0;background:var(--chartreuse);
  transition:width 320ms var(--ease-standard)}
.on-chapter #progress{background:rgba(255,255,255,.22)}
.on-chapter #progress i{background:#fff}
.on-payne #progress{background:rgba(255,255,255,.22)}
.on-payne #progress i{background:#fff}
/* Same job on a dark scene: the supplied brand frames are pure black on a
   near-black ground, so a dark hairline would be invisible and the caption
   would float again. Light at the same weight. */

/* The four flow boards size against the LIVE viewport rather than a number
   baked in at a 860px window. The deck is presented fullscreen, where a
   MacBook 14 gives 982px of stage, and a fixed max-height left roughly 120px
   of that unused under every one of them. --ar is the asset's own aspect, so
   the width still follows the picture and the caption still wraps inside the
   picture's edges. --cap is the caption's own cost: 14px of margin above a
   15/1.55 line. */
.plate:not(:has(figcaption)){--cap:0px}
.plate:has(figcaption){--cap:52px}
.plate--fill{--cap:52px;
  --mh:calc(var(--well-bottom) - var(--well-top) - var(--cap));
  --pw:calc(var(--mh) * var(--ar))}

/* A full-width board is centred by the scene grid, which leaves its left edge
   a few px inboard of the title above it. This pins it to the same 64px the
   title, the cards and every rail already start from. */
.plate--left{justify-self:start;margin-left:0}

/* ── A board that sits BESIDE its rail ─────────────────────────────────
   .plate--fill sizes a board from the height left under its own band. A
   board in the right hand track of a .split is the other case: the rail
   and the gap have already taken their width, so the TRACK binds and the
   height follows the asset's own aspect. The first term is 100% of the
   track itself, never a number derived from 100vw, so it stays correct
   whatever --col and --split-gap become. The second term only bites if an
   aspect is ever tall enough to push the caption past the floor, in which
   case this degrades into .plate--fill's behaviour rather than
   overflowing. */
.plate--rail{--cap:52px;
  --mh:calc(var(--well-bottom) - var(--well-top) - var(--cap));
  width:min(100%,calc(var(--mh) * var(--ar)))}

/* The identity spec as the right hand column. It carries its own 1160 cap
   and its own 64px padding for the full width case; inside a track both
   jobs are already done. */
.split > .spec{max-width:none;padding:0;justify-self:stretch}

/* ── The happy flow ───────────────────────────────────────────────────
   The board is Figma node 346:132871 exported at 2.5x and cropped to its
   content group, with two things painted out in the frame's own #000: the
   three rects that carry live video, and the nine connector labels. The
   arrows, their dots and the five static screens are therefore the design
   itself, pixel for pixel. Everything sits at the rects Figma reports,
   expressed as percentages of the 1611.33 x 812.76 board, so the whole
   thing scales as one piece.

   The clips run muted. A flow board is read, not listened to, and each of
   these recordings also appears full size with its sound elsewhere. */
.flow{position:relative;z-index:2;margin:0;display:flex;flex-direction:column;align-items:flex-start;
  width:min(calc(100vw - var(--safe-x)*2),
            calc((var(--well-bottom) - var(--well-top) - 52px) * 1.9244))}
/* The board is the Figma export, used whole. The screens, the dashed
   connectors and every label are already in it, so nothing is redrawn here
   and nothing can drift out of register. The only thing layered on top is
   three clips, each pinned to the rect Figma reports for the screen it
   replaces, as a percentage of the 1655 x 860 crop. No device chrome: the
   screens in the export carry their own, and adding rings on top of three of
   eight would have made those three read as a different kind of object.

   The export draws a bezel around every screen. Measured on the plate's own
   pixels, all three identical: 4px stroke, 35px outer radius. The clips are
   pinned to the INNER rect, inset by that bezel, so the export's own stroke
   survives instead of being painted over, and the radius is the inner one:
   35 - 4 = 31px, which is 0.937% of the board's width. Re-measure both if the
   frame is ever re-exported at a different internal size. */
.flow-board{position:relative;height:100%;width:auto;aspect-ratio:3605 / 1840;
  container-type:inline-size}
.flow-plate{display:block;width:100%;height:100%;object-fit:fill}
.flow-vid{position:absolute;display:block;object-fit:fill;border-radius:0.860cqi;
  background:#0a0a0c;pointer-events:none}

.flow:has(figcaption) .flow-board{height:calc(100% - 52px)}
.flow figcaption{font-size:var(--mediacap);line-height:1.55;color:var(--ink-3);
  margin:14px 0 0;width:100%}

/* ── Phone frame: the portfolio's device treatment, unchanged. ──────── */
.frame-phone{border:7px solid #221D2E;border-radius:var(--r-phone);background:#221D2E;
  overflow:hidden;display:block;
  box-shadow:0 0 0 2px #6f6d78, 0 0 0 3.5px #4c4a54, 0 22px 56px rgba(122,74,255,.16)}
/* Heights are definite in px, not percentages of an auto-height parent. */
.frame-phone--row{height:var(--row-h);width:auto}
.frame-phone--row > img,.frame-phone--row > video{height:calc(var(--row-h) - 14px)}
.frame-phone--hero{position:relative;z-index:2;height:var(--hero-h);width:auto}
.frame-phone--hero > img,.frame-phone--hero > video{height:calc(var(--hero-h) - 14px)}
/* One hero phone on an otherwise empty slide: the caption moves beside the
   device instead of under it, using width the slide is not otherwise using. */
.scene--hero .frame-cell{display:grid;grid-template-columns:auto 292px;
  grid-template-rows:1fr auto auto;column-gap:44px;align-items:end}
.scene--hero{--well-top:176px}
.scene--hero > .frame-row{justify-content:center}
.scene--hero .frame-cell > .frame-phone{grid-column:1;grid-row:1/-1}
.scene--hero .frame-cap-title{grid-column:2;grid-row:2;width:auto;min-width:0;margin:0}
.scene--hero .frame-cap{grid-column:2;grid-row:3;width:auto;min-width:0;
  margin:var(--s2) 0 0}
.frame-phone > img,.frame-phone > video{width:auto;object-fit:cover;display:block}
video.frame-phone{object-fit:cover}
.frame-phone video::-webkit-media-controls-overlay-play-button,
.frame-phone video::-webkit-media-controls-start-playback-button{display:none!important}

/* Phone rows keep ONE device height across the whole deck. That is the one
   place a shared value is right: a handset that changes size from scene to
   scene reads as drift. Only the gap flexes. 540px is the largest height at
   which the tallest cell in the deck — frame + title + a three line caption
   — still clears the floor by 40px under the deepest band, at 860. */
.frame-row{position:relative;z-index:2;display:flex;align-items:flex-start;
  justify-content:center;gap:var(--frame-gap,64px)}
/* The cell is exactly as wide as the frame. The captions take width:0 so they
   add nothing to that measurement, then min-width pulls them back out to the
   frame's edges once it is known. */
.frame-cell{display:flex;flex-direction:column;align-items:flex-start;width:fit-content}
.frame-cell > .frame-phone{align-self:flex-start;flex:none}
.frame-cap-title{font-size:var(--mediacap-title);font-weight:600;line-height:1.3;
  margin:var(--gap-media) 0 0;width:0;min-width:100%}
.frame-cap{font-size:var(--mediacap);line-height:1.55;color:var(--ink-3);
  margin:var(--s2) 0 0;width:0;min-width:100%}

.film__bg{position:absolute;inset:-60px;width:calc(100% + 120px);height:calc(100% + 120px);
  object-fit:cover;filter:blur(64px) saturate(140%) brightness(.45);transform:scale(1.1)}
.reduced-transparency .film__bg{display:none}

/* ── Left rail of high level points beside the media ──────────────────
   Both columns start at the top of row two, so the points head and the top
   edge of the media share one baseline. --col and --split-gap are per scene:
   a 560px product shot and a seven tile grid do not want the same rail. */
.split{position:relative;z-index:2;display:grid;
  grid-template-columns:var(--col,340px) 1fr;gap:var(--split-gap,var(--gap-col));
  align-items:start;justify-items:center;
  width:100%;padding:0 var(--safe-x)}
.split > div{justify-self:stretch}

/* ── A board that leads, with its points beneath ───────────────────────
   Three of the identity scenes ran a 320px rail beside a landscape board,
   which capped the picture at 1008px and left it unable to reach even half
   the well. On scenes whose whole job is to show the artwork, the board
   takes the full 1384 and the points read as a row under it. --stack-foot
   is what the row and its gap cost, so the board can still never push the
   points past the well floor. Nothing is cropped: the image stays width and
   height bound on its own aspect. */
.stack{--wellh:calc(var(--well-bottom) - var(--well-top));
  position:relative;z-index:2;display:flex;flex-direction:column;
  align-items:stretch;gap:var(--s6)}
.stack .plate{width:100%;max-width:100%;align-items:flex-start}
.stack .plate img{width:auto;max-width:100%;
  max-height:calc(var(--wellh) - var(--stack-foot,116px))}
/* A stack with no points row has no foot to reserve: its only companion is the
   figcaption, which .plate already prices into --cap. */
.stack:not(:has(.points)) .plate img{max-height:calc(var(--wellh) - var(--cap,0px))}
.stack .points{flex-direction:row;gap:var(--gap-group);align-items:stretch}
.stack .points li{flex:1 1 0;font-size:18px;line-height:1.5}
.points{list-style:none;margin:0;padding:0;display:flex;flex-direction:column;gap:var(--gap-set)}
.points li{font-size:var(--statement);line-height:1.55;color:var(--ink-2);
  position:relative;padding-left:30px}
.points li::before{content:"";position:absolute;left:0;top:.55em;
  width:9px;height:9px;border-radius:2px;background:var(--signal,var(--chartreuse))}
.points b{font-weight:600;color:var(--ink-1)}
.points-head{font-size:24px;font-weight:600;letter-spacing:-.012em;
  line-height:1.3;color:var(--ink-1);margin:0 0 var(--gap-in)}
.split-lede{font-size:19px;line-height:1.55;color:var(--ink-2);margin:0}

/* ── Ranked content ─────────────────────────────────────────────────────
   This replaced a card grid. Cards gave every item an identical container, so
   seven problems read as seven problems of equal weight, which is false and is
   the opposite of what the frame has to argue. The ranking is not invented:
   the two leads on the challenges scene are exactly what the next two scenes
   are built to solve. Type size and position carry it now, and the fill, the
   radius and the decorative rule are gone with the container. */
.ranked{position:relative;z-index:2;width:100%;padding:0 var(--safe-x);
  display:flex;flex-direction:column;gap:var(--gap-group)}
.split .ranked{padding:0;gap:var(--gap-group)}

/* The lead. One claim, at a size the back of the room reads first. */
.rank-lead h3{font-size:var(--lead);font-weight:700;letter-spacing:-.01em;
  line-height:1.2;margin:0 0 var(--gap-in);color:var(--indigo-on-dark)}
.rank-lead p{font-size:var(--lead-body);line-height:1.5;color:var(--ink-1);
  margin:0;max-width:52ch}

/* Two leads side by side, for the scene that has two of equal standing. */
.rank-pair{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));
  column-gap:40px}
.rank-pair h3{font-size:var(--pair);font-weight:700;letter-spacing:-.01em;
  line-height:1.25;margin:0 0 var(--s3);color:var(--indigo-on-dark)}
.rank-pair p{font-size:19px;line-height:1.55;color:var(--ink-2);margin:0}

/* The remainder. Demoted, not hidden: still readable at the back, but clearly
   the second tier. A hairline separates the ranks without drawing a box. */
.rank-rest{list-style:none;margin:0;padding:var(--s6) 0 0;
  border-top:1px solid rgba(245,243,247,.14);
  display:flex;flex-direction:column;gap:var(--gap-set);max-width:96ch}
.rank-rest li{font-size:18px;line-height:1.55;color:var(--ink-2);
  position:relative;padding-left:30px}
.rank-rest li::before{content:"";position:absolute;left:0;top:.55em;
  width:9px;height:9px;border-radius:2px;background:var(--signal,var(--chartreuse))}
.rank-rest b{font-weight:600;color:var(--ink-1)}


/* ── Stat boards. Bold: these are the numbers the room is meant to keep. ─
   Left-aligned to the title, not centred in the middle of the screen. */
.board{position:relative;z-index:2;display:grid;gap:64px;width:100%;
  padding:0 var(--safe-x)}
.board dt{font-size:56px;font-weight:700;letter-spacing:-.03em;line-height:1;
  color:var(--ink-1)}
.board dd{margin:var(--s3) 0 0;font-size:16px;font-weight:600;
  line-height:1.45;color:var(--ink-2)}

/* Cover: the 0 to 1 line and the team figures are the continuation of the
   title stack, so they hang off the band rather than centring. */
.scene--cover .bleed{z-index:0}
.scene--cover::after{content:"";position:absolute;inset:0;z-index:1;
  pointer-events:none;
  background:linear-gradient(102deg,rgba(0,0,0,.92) 0%,rgba(0,0,0,.78) 32%,
    rgba(0,0,0,.30) 54%,rgba(0,0,0,0) 72%)}
.cover-brief{position:relative;z-index:3;justify-self:start;
  padding-left:var(--safe-x);max-width:616px}
.board--cover{grid-template-columns:repeat(2,1fr);gap:26px 40px;padding:0;
  margin:var(--s6) 0 0;max-width:none}
.board--cover dt{font-size:48px}
/* The second set on the cover: same grid, stepped down, so the two read as
   two groups rather than one long list. 34px against the team set's 48px is
   the same ratio the deck uses between a stage title and a card title. */
/* The shipped figures are a different claim from the team figures, so they sit
   in the deck's own card: same fill, same 22px radius, same --s6 padding and
   the same chartreuse mark the cards on the other scenes carry. Numbers step
   down to 34 against the team set's 48 so the card reads as the second beat. */
.cover-shipped{background:var(--card-fill);border-radius:var(--r-card);
  padding:var(--s6);margin:var(--s6) 0 0;width:552px;max-width:100%}
.cover-shipped-head{font-size:20px;font-weight:600;letter-spacing:-.012em;
  line-height:1.3;color:var(--ink-1);margin:0}
.board--shipped{gap:20px 40px;margin:var(--s5) 0 0}
.board--shipped dt{font-size:34px}
.board--shipped dd{font-size:14px;margin-top:6px}

/* ── Identity spec. Mirrors dreamplay.html's own swatch and type grid. ── */
.spec{position:relative;z-index:2;display:flex;flex-direction:column;gap:var(--gap-group);
  max-width:1160px;width:100%;padding:0 var(--safe-x);justify-self:start}
.spec-type{display:grid;grid-template-columns:repeat(2,minmax(0,420px));gap:56px;
  justify-content:start;padding-top:24px;border-top:1px solid rgba(245,243,247,.16)}
.spec-aa{font-size:82px;line-height:.9;margin:0 0 14px;display:block}
.spec-aa--tomorrow{font-family:'Tomorrow',var(--font-display);font-weight:700}
.spec-aa--inter{font-family:'Inter',var(--font-display);font-weight:700}
.spec-name{font-size:20px;font-weight:600;margin:0 0 6px}
.spec-role{font-size:16px;line-height:1.5;color:var(--ink-2);margin:0}
.spec-row{display:flex;gap:14px}
.spec-row--accent{display:grid;grid-template-columns:repeat(4,1fr);gap:14px}
.swatch{flex:1;height:76px;border-radius:var(--r-capsule);display:flex;
  flex-direction:column;justify-content:center;gap:2px;padding:0 20px;
  box-shadow:inset 0 0 0 1px rgba(245,243,247,.18)}
.swatch b{font-size:15px;font-weight:600}
.swatch span{font-size:13px;opacity:.72}

/* ── The portfolio's navigation, inside the deck ─────────────────────────
   A talk should not be framed by chrome, so this is hidden until the
   presenter asks for it: any pointer move, any click, or a tab into it. It
   hides again after a few still seconds, unless the Work menu is open or
   focus is inside. Dark glass rather than the site's white bar, because it
   sits over the deck, and it is 56px tall so a scene's kicker at y=56 is the
   first thing clear of it once it goes.

   Links resolve one level up: the deck lives in dreamplay-concise/. */
#deckNav{position:fixed;top:0;left:0;right:0;z-index:60;
  transform:translateY(-100%);opacity:0;pointer-events:none;
  transition:transform 260ms var(--ease-decel),opacity 200ms linear}
#deckNav.on{transform:none;opacity:1;pointer-events:auto}
.dn-bar{display:flex;align-items:center;gap:var(--s5);height:56px;
  padding:0 var(--safe-x);
  background:rgba(11,6,18,.82);backdrop-filter:blur(18px);
  border-bottom:1px solid rgba(245,243,247,.12)}
.dn-logo{display:grid;place-items:center;width:30px;height:30px;flex:none;
  border-radius:7px;background:var(--chartreuse);color:#000;
  font-size:13px;font-weight:700;letter-spacing:.02em;text-decoration:none}
.dn-links{display:flex;align-items:center;gap:var(--s5);margin-left:auto}
.dn-link{position:relative;background:none;border:0;padding:6px 2px;cursor:pointer;
  display:inline-flex;align-items:center;gap:6px;
  font-family:var(--font-display);font-size:14px;font-weight:500;
  color:var(--ink-2);text-decoration:none;
  transition:color 160ms linear}
.dn-link:hover,.dn-link:focus-visible{color:var(--ink-1)}
.dn-link--back{color:var(--chartreuse)}
.dn-drop{position:relative}
.dn-menu{position:absolute;top:calc(100% + 12px);right:0;min-width:248px;
  display:flex;flex-direction:column;padding:8px;
  background:rgba(21,14,31,.98);backdrop-filter:blur(18px);
  border:1px solid rgba(245,243,247,.14);border-radius:12px;
  box-shadow:0 18px 44px rgba(0,0,0,.55)}
.dn-menu[hidden]{display:none}
.dn-menu a{padding:9px 12px;border-radius:8px;
  font-size:14px;color:var(--ink-2);text-decoration:none;white-space:nowrap;
  transition:background 140ms linear,color 140ms linear}
.dn-menu a:hover,.dn-menu a:focus-visible{background:rgba(245,243,247,.08);color:var(--ink-1)}
.dn-rule{height:1px;margin:6px 8px;background:rgba(245,243,247,.14)}

/* ── Reveals. Explicit enter and exit, so stepping back replays. ────── */
.rv{opacity:0;transform:translateY(10px)}
.is-in .rv{opacity:1;transform:none;
  transition:opacity 240ms var(--ease-standard),transform 240ms var(--ease-land)}
/* Stepping back re-hides a group. That is a correction, not a performance. */
.is-in .rv.held{opacity:0;transform:translateY(10px);transition:none}
/* The happy flow's container holds three videos positioned in percentages
   against the raster behind them. It fades, it never moves. */
.rv--noshift{transform:none!important}
.scene--payne .rv{transform:none}
.scene--payne.is-in .rv{transition:opacity 900ms var(--ease-in-out);transform:none}

/* ── Chrome ─────────────────────────────────────────────────────────── */
/* No plate behind either of these. The counter used to sit on a grey capsule,
   which read as a bar across the bottom of every slide; the numerals carry
   the same information without putting furniture on the page. */
#counter,#mute{position:fixed;bottom:var(--s5);z-index:40;padding:8px 14px;
  font:inherit;font-size:12px;letter-spacing:.1em;text-transform:uppercase;
  background:none;color:var(--dark-ink-3);border:0;
  text-shadow:0 1px 3px rgba(0,0,0,.65), 0 0 10px rgba(0,0,0,.5)}
#counter{right:var(--s5);font-variant-numeric:tabular-nums}
#mute{right:calc(var(--s5) + 92px);cursor:pointer}
.on-payne #counter,.on-payne #mute{color:rgba(255,255,255,.82)}

#rail{position:fixed;right:var(--s5);top:50%;transform:translateY(-50%);z-index:40;
  display:flex;flex-direction:column;gap:0;padding:0}
#rail button{width:24px;height:24px;padding:9.5px;border:0;border-radius:50%;cursor:pointer;
  background-color:rgba(245,243,247,.46);background-clip:content-box;
  transition:background-color 300ms var(--ease-standard),transform 300ms var(--ease-standard)}
#rail button.on{background-color:var(--chartreuse);transform:scale(1.6)}
#rail button.chapter-start{margin-top:10px}

#overview{position:fixed;inset:0;z-index:60;background:rgba(0,0,0,.94);
  display:grid;place-items:center;padding:var(--s7)}
#overview[hidden]{display:none}
#ovGrid{display:grid;grid-template-columns:repeat(8,1fr);gap:var(--s4);max-width:1280px;width:100%}
#ovGrid button{aspect-ratio:16/10;border:2px solid transparent;border-radius:8px;
  background-size:cover;background-position:center;background-color:#2E2240;
  cursor:pointer;color:var(--ink-2);font-size:10px;letter-spacing:.06em;display:grid;
  place-items:end center;padding:5px;text-transform:uppercase;overflow:hidden}
#ovGrid button[aria-current="true"]{border-color:var(--chartreuse)}

/* ── The gate ───────────────────────────────────────────────────────────
   The name hangs off the top of the content well and the instruction sits on
   its floor, both on the 64px margin, so the first thing a room sees is the
   frame the next 35 scenes use. The die fills the space between them: one
   object common to every game in the app, drawn as a line figure in the
   deck's own ink. Its face cross-fades every 4.5s and nothing translates or
   rotates, so from the back of a hall the screen reads as still. */
#gate{position:fixed;inset:0;z-index:80;display:block;
  background:var(--dark-ground);color:var(--dark-ink-1);cursor:pointer;
  transition:opacity 240ms var(--ease-in-out)}
#gate.gone{opacity:0;pointer-events:none}

#gate .gate-block{position:absolute;inset:0;padding-bottom:var(--foot-clear);
  display:flex;flex-direction:column;align-items:center;justify-content:center;
  gap:var(--s7)}
#gate .gate-name{font-family:var(--font-display);font-size:var(--display);
  font-weight:700;letter-spacing:-.03em;line-height:1;color:var(--ink-1);margin:0;
  text-align:center}

/* The chrome size is the deck's near-invisible register, where #counter and
   #mute live. A live instruction to a presenter belongs in the signal ink at a
   size the back row reads. Padding is reserved so the focus fill cannot reflow. */
#gate .gate-prompt{font-family:var(--font-display);font-size:14px;font-weight:600;
  letter-spacing:.14em;line-height:1.2;text-transform:uppercase;
  color:var(--chartreuse);margin:0;padding:9px 14px;text-align:center;
  border-radius:var(--r-capsule);background:transparent;
  transition:background 140ms var(--ease-standard),color 140ms var(--ease-standard)}

/* The gate's border box is the viewport, so the global 3px offset ring draws
   off screen and indicates nothing. The indicator moves onto the thing the key
   press actually operates. */
#gate:focus-visible{outline:none}
#gate:focus-visible .gate-prompt,
#gate:active .gate-prompt{background:var(--chartreuse);color:#000}
#gate:active .gate-prompt{transition:none}

.gate-die{position:relative;width:260px;height:260px;overflow:visible;flex:none;
  transition:transform 160ms var(--ease-standard)}
#gate:hover .gate-die,#gate:focus-visible .gate-die{transform:scale(1.04)}

/* The roll, built to the Ludo dice in the product. Measured off the capture
   at 30fps: the die gathers and shrinks to .92 for ~100ms, launches, swells to
   about 1.3x while it tumbles off-centre, decelerates over the last five
   frames and lands at 633ms. Rotation is front-loaded and eased out by the
   keyframe spacing, so the timing function stays linear and never eases twice.
   rotateY stays under 55deg: the artwork is a 2.5D isometric cube, and a wider
   turn would flatten it to nothing instead of revealing a face. The product's
   die resolves to a flat face at rest and so ends ~10% smaller than its idle
   pose; ours returns to the same isometric die it started from. */
#gate.rolling .die-cube{animation:gate-roll 633ms linear forwards}
#gate.rolling .die-cast{animation:gate-cast 633ms linear forwards}
@keyframes gate-roll{
  0%  {transform:translate(0,0)        rotate(0deg)    rotateY(0deg)   scale(1)}
  8%  {transform:translate(0,3px)      rotate(-14deg)  rotateY(-10deg) scale(.96)}
  16% {transform:translate(0,4px)      rotate(-24deg)  rotateY(-6deg)  scale(.92)}
  24% {transform:translate(2px,-8px)   rotate(60deg)   rotateY(30deg)  scale(1.06)}
  32% {transform:translate(4px,-14px)  rotate(240deg)  rotateY(8deg)   scale(1.16)}
  40% {transform:translate(6px,-18px)  rotate(420deg)  rotateY(-14deg) scale(1.28)}
  46% {transform:translate(3px,-19px)  rotate(560deg)  rotateY(-52deg) scale(1.30)}
  55% {transform:translate(-2px,-19px) rotate(700deg)  rotateY(-4deg)  scale(1.30)}
  62% {transform:translate(-4px,-17px) rotate(810deg)  rotateY(50deg)  scale(1.27)}
  72% {transform:translate(-5px,-13px) rotate(920deg)  rotateY(10deg)  scale(1.24)}
  80% {transform:translate(-1px,-9px)  rotate(985deg)  rotateY(-44deg) scale(1.19)}
  86% {transform:translate(3px,-5px)   rotate(1030deg) rotateY(-12deg) scale(1.12)}
  93% {transform:translate(2px,-1px)   rotate(1068deg) rotateY(20deg)  scale(1.04)}
  100%{transform:translate(0,0)        rotate(1080deg) rotateY(0deg)   scale(1)}}

/* The cast shadow reads the height: it tightens and fades as the die rises,
   and is back at full weight on the landing frame. */
@keyframes gate-cast{
  0%  {opacity:.7;transform:scale(1)}
  16% {opacity:.78;transform:scale(1.04)}
  40% {opacity:.34;transform:scale(.82)}
  55% {opacity:.3;transform:scale(.79)}
  86% {opacity:.5;transform:scale(.92)}
  100%{opacity:.7;transform:scale(1)}}

/* The body, lighting and pips are painted by the SVG's own gradients, so the
   only thing CSS owns here is the float, the ring and the throw. */
.die-cast{transform-box:fill-box;transform-origin:center;
  transition:opacity 160ms var(--ease-standard)}
.die-cube{transform-box:fill-box;transform-origin:center;
  animation:die-float 6s var(--ease-in-out) infinite}

@keyframes die-float{0%,100%{transform:translateY(0)}50%{transform:translateY(-5px)}}

:focus-visible{outline:3px solid var(--chartreuse);outline-offset:3px;border-radius:4px}
.sr{position:absolute;width:1px;height:1px;overflow:hidden;clip:rect(0 0 0 0)}
@media (prefers-reduced-motion:reduce){
  /* A 1ms transition still schedules a compositing pass and can still fire
     transitionend. none is the honest declaration. Scene changes are already
     cuts, so there is nothing else to disable there. */
  .rv{transform:none!important}
  .is-in .rv{transform:none!important;transition:none!important}
  .is-in .rv.held{transition:none!important}
  .scene--payne.is-in .rv{transition:none!important}
  #rail button{transition:none}
  #gate{transition:none}
  .die-cube{animation:none}
  .die-cast{animation:none}
}
@media (prefers-reduced-transparency:reduce){ .film__bg{display:none} }

/* Gone from this file, and why:
     .points--row, .points--row--even, --pcols
       - the horizontal rules that ran under a board. Rules now sit in a
         left rail beside the picture on every scene that has them, so the
         horizontal variant has no users left.
     .stage-sub{font-size:26px} and .stage-label{font-size:18px}
       - both were second heading sizes. A continuation scene now titles
         itself at 40px like every other scene: one heading size, read at
         one distance.
     .cards--3 .card{min-height:230px} and the 180 / 168 / 126 siblings
       - fixed floors borrowed from the longest copy in the deck. Cards are
         intrinsic now, equalised per row by the grid.
     .scene--board .plate{margin-bottom:44px}
       - a margin, not a cap, so it did nothing when a board rendered tall.
         Replaced by --gutter and by height-derived --pw / --mh.
     .split{padding-top:24px}
       - double counted with the band, which pushed split scenes low.
     .frame-row,.cards,.board,.spec,.split{max-height:600px}
       - one cap across five unrelated archetypes. */
