/* ============================================================
   Mash Partners — "Det går videre uten deg" · THE JOURNEY.

   One fixed canvas is the world and the world is a SEQUENCE OF PLACES. The
   document over it is a transparent sheet of BEATS — one beat per place, no
   beat paints a ground, ever. The only colour the page itself contributes is
   --field, and the page does not choose it: it is the current place's own
   colorField, handed over by Journey.place() and written on <body>.

   Because the ground changes when the place changes, THE INK HAS TO CHANGE
   WITH IT. Two tables, switched on the field's measured luminance:
     on-dark   #EDEBE6 / #B8B4AC / #9A968E   (near-black and the print blue)
     on-light  #0C0E12 / #3A3F47 / #4B5058   (the dusk field outside)
   and two FIELD CORRECTIONS where a base table misses 4.5:1 on its own field —
   the print blue lifts its two secondaries and the dusk field deepens its mute.
   Every number is in the build report; none of them is picked.
   ============================================================ */
:root{
  /* the two ink tables, declared once and selected by [data-ground] below */
  --dark-ink:   #EDEBE6;
  --dark-ink-2: #B8B4AC;
  --dark-mute:  #9A968E;
  --lite-ink:   #0C0E12;
  --lite-ink-2: #3A3F47;
  --lite-mute:  #4B5058;

  --field: #0B0C0E;                       /* the place's colorField; JS writes it */

  --t-mono: 0.6875rem;
  --t-sm:   0.8125rem;
  --t-body: 1.0625rem;                    /* 17px */
  --t-lede: 1.3125rem;
  --t-h3:   1.5rem;
  --t-h2:   clamp(1.9rem, 4.4vw, 3.6rem);
  --t-big:  clamp(2.6rem, 8.4vw, 7.5rem);
  --t-h1:   clamp(2.75rem, 11.2vw, 10rem);

  --gut:  clamp(20px, 5vw, 84px);
  --ease: cubic-bezier(.16,1,.3,1);
}

/* ---- the tables, chosen by the field's luminance. They are scoped to
   [data-ground], which every BEAT carries as well as <body>: at a cut the
   outgoing caption and the incoming one are both in frame, and a table hung
   only on <body> would hand one of them the other place's ink. The beat's
   value is authored beside its --field and re-derived from that field's
   measured luminance at boot, so the markup is only the pre-paint hint. ---- */
[data-ground="dark"]{
  --ink:var(--dark-ink); --ink-2:var(--dark-ink-2); --ink-mute:var(--dark-mute);
  --rule:rgba(237,235,230,.22);          /* 1.80:1 — decorative hairline */
  --rule-strong:rgba(237,235,230,.46);   /* 4.12:1 — structural */
}
[data-ground="light"]{
  --ink:var(--lite-ink); --ink-2:var(--lite-ink-2); --ink-mute:var(--lite-mute);
  --rule:rgba(12,14,18,.22);
  --rule-strong:rgba(12,14,18,.46);
}
/* FIELD CORRECTION 1 — the print blue. #1F4F9E measures L 0.0835, so it takes the
   on-dark table by luminance, and on it #B8B4AC is 3.81:1 and #9A968E is 2.67:1:
   both fail 1.4.3. A two-colour screen-print has one ink and the paper, so the
   secondaries are the same paper at a lower tint rather than a grey — 5.01:1 and
   4.57:1. The rule alphas go up with them (a .46 hairline is 2.65:1 here). */
[data-place="door"]{
  --ink-2:#D2CEC5; --ink-mute:#C9C5BC;
  --rule:rgba(237,235,230,.30);          /* 1.91:1 */
  --rule-strong:rgba(237,235,230,.60);   /* 3.45:1 */
}
/* FIELD CORRECTION 2 — the dusk field. #E8A0B4 is L 0.4559 and takes the on-light
   table; #4B5058 lands at 3.91:1 on it, so the mute deepens to 4.72:1. */
[data-place="outside"]{
  --ink-mute:#3F444C;
  --rule:rgba(12,14,18,.30);             /* 1.86:1 */
  --rule-strong:rgba(12,14,18,.60);      /* 3.90:1 */
}

*{ box-sizing:border-box }
html{ scroll-behavior:smooth; background:#0B0C0E }
@media (prefers-reduced-motion:reduce){ html{ scroll-behavior:auto } }
body{
  margin:0; background:var(--field); color:var(--ink);
  font-family:Newsreader,Georgia,serif; font-size:var(--t-body); line-height:1.5;
  overflow-x:clip;                         /* NEVER hidden — that makes body a scroll
                                              container and kills every held caption */
  -webkit-font-smoothing:antialiased;
}
/* the ground and the ink change AT A CUT, so they change by cut: no transition.
   A field that fades between two places is a dissolve, and this page has none. */
::selection{ background:var(--ink); color:var(--field) }
a{ color:inherit }
.skip{ position:absolute; left:-9999px; top:0; z-index:99; background:var(--ink);
  color:var(--field); padding:12px 18px; font-family:"Martian Mono",monospace;
  font-size:var(--t-mono); text-decoration:none }
.skip:focus{ left:16px; top:16px }
:focus-visible{ outline:2px solid var(--ink); outline-offset:3px }

/* ---------------- THE WORLD ---------------- */
#journey{
  position:fixed; inset:0; width:100%; height:100%; z-index:0;
  pointer-events:none; display:block;
  background:var(--field);                 /* the fallback before the first GL frame */
}
/* no-GL / no-JS tier: the page is complete as a DOCUMENT over one still of the
   first place. TOP-ALIGNED AND BANDED, never full-bleed — the still is a night
   interior, so covering the viewport with it would put the caption's ink on the
   image's own black. The band keeps the lower frame clean field, which is the
   same rule the live camera follows. */
.poster{ display:none; position:fixed; inset:52px 0 auto 0; width:100%; height:54vh;
  object-fit:cover; object-position:center top; z-index:0 }
/* the still belongs to the FIRST place and leaves with it — a night interior
   pinned over the dusk field would be a frontispiece for the wrong chapter */
html.no-gl body[data-place="desk"] .poster{ display:block }
html.no-gl #journey{ display:none }

/* ordered dither (4x4 Bayer cell) — the plane has a surface, not a texture pasted
   on it. It has to change operator with the field: multiply over near-black is
   invisible and multiply over the dusk field is the only place it would show. */
.grain{
  position:fixed; inset:0; z-index:1; pointer-events:none;
  background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='4' height='4'%3E%3Crect width='1' height='1'/%3E%3Crect width='1' height='1' x='2' y='2'/%3E%3Crect width='1' height='1' x='1' y='3'/%3E%3Crect width='1' height='1' x='3' y='1'/%3E%3C/svg%3E");
  background-size:4px 4px;
}
body[data-ground="dark"] .grain{ mix-blend-mode:screen; opacity:.05 }
body[data-ground="light"] .grain{ mix-blend-mode:multiply; opacity:.06 }
main, .foot, .bar{ position:relative; z-index:2 }

/* ---------------- the bar ---------------- */
.bar{
  background:var(--field, #0B0C0E);   /* the chrome paints the place's own ground: nothing passing under it can collide with it */
  position:fixed; inset:0 0 auto 0; z-index:20;
  display:flex; align-items:center; gap:28px;
  padding:16px var(--gut) 14px;
}
.bar__mark{ display:flex; align-items:center; gap:9px; color:var(--ink);
  font-family:"Bricolage Grotesque",system-ui,sans-serif; text-decoration:none;
  font-weight:800; font-size:var(--t-sm); font-variation-settings:"wdth" 80,"opsz" 14;
  letter-spacing:-.01em }
.bar__dot{ width:9px; height:9px; border-radius:50%; background:var(--ink); flex:0 0 auto }
.amp{ font-weight:400; margin:0 1px }
.bar__read{ margin:0 0 0 auto; font-family:"Martian Mono",monospace; font-size:var(--t-mono);
  letter-spacing:.08em; color:var(--ink-mute); font-variant-numeric:tabular-nums }
.bar__slash{ margin:0 3px }
.bar__act{ font-family:"Martian Mono",monospace; font-size:var(--t-mono); letter-spacing:.06em;
  color:var(--ink); text-decoration:none; border-bottom:1px solid var(--rule-strong);
  padding-bottom:2px }
.bar__act:hover{ border-bottom-color:var(--ink) }
.bar__rule{ position:absolute; inset:auto 0 0 0; height:1px; background:var(--rule) }
.bar__rule span{ display:block; height:100%; background:var(--ink);
  transform-origin:left; transform:scaleX(var(--read,0)) }

/* ---------------- BEATS ----------------
   A beat is a PLACE's runway, not a panel: it has a height and no ground. It is
   made of HOLDS — one per caption block — and each hold is the box its caption
   is held inside, so two blocks in one place release at different moments
   instead of stacking. Heights are deliberately unequal, and they are the
   storyboard's own pacing: 120 / 170 / 90 / 150 / 110vh. */
.beat{ position:relative; padding:0 var(--gut); --j:0;
  /* RE-RESOLVE THE INK HERE. `color` inherits as a computed COLOUR, not as the
     var() that produced it, so a beat that only re-scoped the tokens would still
     have every element without its own `color` wearing whatever <body> resolved
     --ink to when the page loaded. Measured before this line: the phase names on
     the dusk field inherited the near-black places' cream at 1.74:1. */
  color:var(--ink) }
.hold{ position:relative; min-height:var(--hh,100vh) }
/* HELD BY ITS TOP EDGE, IN THE LOWER HALF OF THE FRAME. The camera keeps the
   portal and the dense image in the upper half of every place, so a caption
   held above the midline is type on the picture. --cap is therefore never below
   50vh, and it is set per block against that block's own measured height so the
   bottom lands clear of the fold. */
.cap{
  /* FIXED, not sticky: the caption never travels. It is revealed in place by its
     beat's entry (--j) and masked out in place by its exit (--x). Scroll is time. */
  position:fixed; top:var(--cap,54vh); left:var(--gut); right:var(--gut); z-index:3;
  clip-path:inset(0 0 calc((1 - min(var(--j,0), var(--x,1))) * 100%) 0);
  pointer-events:none;
}
/* THE BEAT IS THE PLACE, SO ITS HEIGHT IS THE PLACE'S SHARE OF THE SHEET.
   For a whole build `--hh` on `.beat--*` was only ever an inherited FALLBACK
   for holds that set none — every hold sets its own, so the beat's declared
   height was dead and a beat measured exactly the sum of its holds. It has to
   be real, or the beat cannot carry the camera-only tail after its last
   caption, and the DOM and scenes.json shares drift apart. */
.beat{ min-height:var(--hh,100vh) }
.beat{ --j:initial; --x:initial }   /* the hold owns the gate; the beat must not shadow it */
.cap a, .cap button{ pointer-events:auto }
.hold{ min-height:var(--hh,100vh) }

/* THE RUNWAY IS FIXED FIRST, AND IT IS THE PLACE'S OWN SHARE OF IT.
   730vh put the whole film inside ~2,500 wheel units and left the doorway
   dolly-through 185px of scroll to happen in — a jump cut with correct
   geometry. Every window in this build (hold dwell, exit leg, the air in the
   void) is a FRACTION of this sheet, so it is set before anything is tuned.
   These five are scenes.json's shares x 1422vh, to the pixel; keep them in
   step or the caption and the place drift apart. */
.beat--desk   { --hh:270vh }   /* share .19 */
.beat--stage  { --hh:313vh }   /* share .22 */
.beat--door   { --hh:270vh }   /* share .19 */
.beat--outside{ --hh:299vh }   /* share .21 */
.beat--room   { --hh:270vh }   /* share .19 */

/* --cap ≈ 90 − (block height as % of the 1440x900 frame), measured, not picked */
/* The holds carry the READ; the tail of each beat is the camera's APPROACH to
   that place's portal, with no copy on it. The tail is sized to the exit leg
   (0.30 of the place) so the last line finishes about where the dolly-through
   starts: read, then the camera goes. Dwell is now ~1.9 screens where it was
   ~1.2 — the caption gates are absolute vh off the hold box, so the hold
   height IS the dwell. */
.hold--open  { --hh:190vh } .hold--open   .cap{ --cap:50vh }  /* 42vh block */
.hold--claim { --hh:165vh } .hold--claim  .cap{ --cap:55vh }  /* 32vh */
.hold--ledger{ --hh:145vh } .hold--ledger .cap{ --cap:56vh }  /* 29vh */
.hold--print { --hh:185vh } .hold--print  .cap{ --cap:50vh }  /* 39vh */
.hold--void  { --hh:95vh  } .hold--void   .cap{ --cap:62vh }  /* 13vh — the empty beat keeps its air */
.hold--phases{ --hh:165vh } .hold--phases .cap{ --cap:58vh }  /* 26vh */
.hold--ask   { --hh:175vh } .hold--ask    .cap{ --cap:50vh }  /* 40vh */

/* THE OUTGOING BLOCK LEAVES BEFORE THE NEXT ONE IS READ. A held caption exits
   through the upper band — the band the camera fills — so a block still hanging
   in frame when the next arrives is type on the picture. The number is derived:
   a successor arrives when its own top reaches 0.15vh, holds abut, so a held
   block has to finish 0.15vh above its hold's bottom edge; +24px covers the line
   box a 120px display face hangs below its own box. It costs nothing — these are
   sticky, so it only shortens the tail of the hold, never the held position.
   Not applied to --void, whose block is already clamped short by its own box. */
.hold--open, .hold--claim, .hold--ledger, .hold--print,
.hold--open, .hold--claim, .hold--ledger, .hold--print, .hold--void, .hold--phases, .hold--ask{ padding-bottom:calc(15vh + 24px) }

/* ---------------- type ---------------- */
.eyebrow{
  font-family:"Martian Mono",monospace; font-size:var(--t-mono);
  letter-spacing:.18em; text-transform:uppercase; color:var(--ink-mute); margin:0 0 18px;
}
.h1{
  margin:0 0 20px; max-width:11ch;
  font-family:"Bricolage Grotesque",system-ui,sans-serif; font-weight:800;
  font-size:var(--t-h1); line-height:.9; letter-spacing:-.04em;
  font-variation-settings:"wdth" 76,"opsz" 96;
}
.h1 em{ font-style:normal; color:var(--ink-mute) }
.h1__sub{ font-size:var(--t-lede); color:var(--ink-2); max-width:38ch; margin:0 }
/* the cue belongs to the FIRST viewport, not to the foot of a 120vh place */
.cue{
  position:absolute; right:var(--gut); bottom:calc(20vh + clamp(40px,8vh,92px)); margin:0;
  font-family:"Martian Mono",monospace; font-size:var(--t-mono);
  letter-spacing:.16em; text-transform:uppercase; color:var(--ink-mute);
  display:flex; align-items:center; gap:10px; writing-mode:vertical-rl;
}
.cue span{ width:1px; height:44px; background:var(--rule-strong); display:block }

/* the type-dominant claim. It RISES into its held position on its beat's own
   progress (--j), and its width axis rides scroll velocity. */
.big{
  /* 22ch, and it is measured rather than picked: at 14ch the claim's authored
     line breaks were being SUPPLEMENTED by wrapping — "De fleste har systemene.
     / De snakker bare ikke sammen." ran four lines instead of two, a 509px block
     that could not be held below the midline of a 900px frame. Swept at 1440,
     1728, 1024 and 390, 22ch is the first measure at which every claim lands on
     the lines it was written with at every width above the phone, and it still
     stops 123px short of the column at 1440. */
  margin:0; max-width:22ch;
  font-family:"Bricolage Grotesque",system-ui,sans-serif; font-weight:800;
  font-size:var(--t-big); line-height:.94; letter-spacing:-.034em;
  font-variation-settings:"wdth" var(--wd,78),"opsz" 96;
  text-wrap:balance;
  transform:translate3d(0,calc((1 - var(--j)) * 42px),0);
  clip-path:inset(0 0 calc((1 - var(--j)) * 100%) 0);
}
/* the ask carries a sub, a control and the terms under it — four elements in one
   held block — so its display line is set one step down. At the full --t-big the
   block measures 44.9vh and its bottom lands at 94.9vh, 45px off the fold; one
   step down puts it at 41.2vh and 91.2vh. */
.big--ask{ font-size:clamp(2.2rem,7.4vw,6.4rem) }
.h2{
  font-family:"Bricolage Grotesque",system-ui,sans-serif; font-weight:700;
  font-size:var(--t-h2); line-height:1.02; letter-spacing:-.024em;
  font-variation-settings:"wdth" 82,"opsz" 48;
  margin:0 0 clamp(18px,3vh,32px); max-width:20ch; text-wrap:balance;
}
.sub{ font-size:var(--t-lede); color:var(--ink-2); max-width:46ch; margin:26px 0 0; text-wrap:pretty }
.hold--void .cap{ text-align:center }
.void__line{
  /* the measure goes on the element carrying the DISPLAY size: `ch` resolves
     against the element's own font-size, so 22ch on a 17px wrapper would give a
     204px column and wrap this line to six. */
  margin:0 auto; max-width:22ch;
  font-family:Newsreader,Georgia,serif; font-weight:300;
  font-size:clamp(1.5rem,3.4vw,2.9rem); line-height:1.22; color:var(--ink-2); text-wrap:balance;
}

.spec{ margin:0; max-width:760px }
.spec > div{
  display:grid; grid-template-columns:minmax(120px,15ch) 1fr; gap:clamp(14px,3vw,48px);
  padding:20px 0; border-top:1px solid var(--rule); align-items:baseline;
}
.spec > div:last-child{ border-bottom:1px solid var(--rule) }
.spec dt{ font-family:"Martian Mono",monospace; font-size:var(--t-mono);
  letter-spacing:.09em; text-transform:uppercase; color:var(--ink) }
.spec dd{ margin:0; color:var(--ink-2); text-wrap:pretty }

.ph{ list-style:none; margin:0; padding:0; max-width:800px }
.ph li{
  display:grid; grid-template-columns:minmax(130px,14ch) 1fr; gap:clamp(14px,3vw,48px);
  padding:14px 0; border-top:1px solid var(--rule); align-items:baseline;
}
.ph li:last-child{ border-bottom:1px solid var(--rule) }
.ph__k{ font-family:"Bricolage Grotesque",system-ui,sans-serif; font-weight:700;
  font-size:var(--t-h3); font-variation-settings:"wdth" 84,"opsz" 28; letter-spacing:-.014em }
.ph__v{ color:var(--ink-2); text-wrap:pretty }

/* the control takes the FIELD's ink as its fill and the field itself as its
   label, so it is legible in every place without a second colour: 16.43:1 on
   the near-black places, 6.60:1 on the print blue, 9.31:1 on the dusk field. */
.btn{
  display:inline-flex; align-items:center; min-height:52px; padding:14px 30px; margin-top:30px;
  background:var(--ink); color:var(--field); text-decoration:none; border-radius:999px;
  font-family:"Bricolage Grotesque",system-ui,sans-serif; font-weight:700;
  font-variation-settings:"wdth" 86,"opsz" 18; font-size:var(--t-body);
  transition:transform .3s var(--ease);
}
.btn:hover{ transform:translateY(-2px) }
.terms{ font-family:"Martian Mono",monospace; font-size:var(--t-mono); letter-spacing:.05em;
  color:var(--ink-mute); margin:22px 0 0 }

/* ---------------- foot ---------------- */
.foot{ padding:clamp(56px,9vh,110px) var(--gut) clamp(40px,6vh,70px);
  border-top:1px solid var(--rule); color:var(--ink) }
.foot__sig{ font-family:"Bricolage Grotesque",system-ui,sans-serif; font-weight:800;
  font-size:clamp(2rem,8vw,6rem); line-height:1; letter-spacing:-.042em;
  font-variation-settings:"wdth" 74,"opsz" 72; margin:0 0 30px }
.foot__meta{ display:flex; flex-wrap:wrap; gap:10px 26px; margin:0 0 22px; padding-top:12px;
  font-family:"Martian Mono",monospace; font-size:var(--t-mono); letter-spacing:.05em;
  color:var(--ink-mute) }
.foot__meta a{ color:var(--ink); text-decoration:none; border-bottom:1px solid var(--rule-strong) }
.foot__demo{ font-family:"Martian Mono",monospace; font-size:var(--t-mono); line-height:1.85;
  letter-spacing:.03em; color:var(--ink-mute); max-width:64ch; margin:0; padding-top:18px }
.foot__demo code{ color:var(--ink-2) }

/* ---------------- arrivals ----------------
   THREE mechanisms, assigned by what the element IS, never by where it sits.
   Nothing arrives by opacity alone.
     clip  — display type and lists (data-wipe, data-stagger)
     rise  — subs and controls (data-rise)
     draw  — labels and registers (data-draw)
   14 arriving blocks: clip 4, rise 5, draw 5. None over 40%. */
[data-wipe]{ clip-path:inset(0 0 100% 0); transition:clip-path .95s var(--ease) }
[data-wipe].seen{ clip-path:inset(0 0 -2% 0) }

[data-rise]{ transform:translate3d(0,34px,0);
  transition:transform 1s var(--ease), opacity .55s linear .05s; opacity:.001 }
[data-rise].seen{ transform:none; opacity:1 }
.cue[data-rise]{ transition-delay:.2s }         /* the cue arrives after the hold */

[data-draw]{ position:relative }
[data-draw]::before{ content:""; position:absolute; left:0; top:-10px; height:1px; width:100%;
  background:currentColor; opacity:.55; transform:scaleX(0); transform-origin:left;
  transition:transform .8s var(--ease) }
[data-draw].seen::before{ transform:scaleX(1) }
.foot__demo[data-draw]::before, .foot__meta[data-draw]::before{ top:0 }

[data-stagger] > *{ clip-path:inset(0 0 100% 0); transition:clip-path .95s var(--ease) }
[data-stagger] > .seen{ clip-path:inset(0 0 -2% 0) }

/* ---------------- S1: the page owns the first 1.1s ----------------
   Base state is FINISHED; only html.intro-run hides the hero. So a returning
   visitor, a no-JS reader and reduced motion all get the type immediately. */
html.intro-run .beat--desk .h1,
html.intro-run .beat--desk .h1__sub,
html.intro-run .beat--desk .eyebrow{ clip-path:inset(0 0 100% 0) }
/* the finished state is an explicit inset, never `none`: clip-path is DISCRETE
   between a shape and none, so a base of `none` would jump instead of wiping */
.beat--desk .h1     { clip-path:inset(0 0 -2% 0); transition:clip-path 1.1s var(--ease) }
.beat--desk .eyebrow{ clip-path:inset(0 0 -2% 0); transition:clip-path .7s var(--ease) }
.beat--desk .h1__sub{ clip-path:inset(0 0 -2% 0); transition:clip-path .9s .25s var(--ease) }
html.intro-run .cue[data-rise]{ transition-delay:2s }   /* collapses to .2s when the hold ends */

@media (prefers-reduced-motion:reduce){
  [data-wipe],[data-rise],[data-draw],[data-stagger] > *{
    clip-path:none !important; transform:none !important; opacity:1 !important; transition:none !important }
  [data-draw]::before{ transform:scaleX(1) !important }
  .big{ transform:none !important; clip-path:none !important }
  .beat--desk .h1,.beat--desk .h1__sub,.beat--desk .eyebrow{ clip-path:none !important; transition:none !important }
  .btn:hover{ transform:none }
}

/* short frames: a held caption is anchored below the midline, so the only way it
   can reach the top of the frame is by being too TALL. Take the display down. */
@media (max-height:700px){
  :root{ --t-big:clamp(2.2rem,5.6vw,4.4rem); --t-h1:clamp(2.4rem,7.4vw,5.6rem);
         --t-lede:1.1875rem }
  .big--ask{ font-size:clamp(2rem,5.4vw,4rem) }
  .hold--void .cap{ --cap:56vh }
}
@media (max-width:700px){
  .spec > div, .ph li{ grid-template-columns:1fr; gap:8px }
  .cue,.bar__read{ display:none }
  .btn{ width:100%; justify-content:center }
  /* the prose runs narrower on a phone, so every block is taller as a fraction
     of a 390x844 frame; the caps are re-measured there rather than scaled. */
  .spec dd, .ph__v{ font-size:1rem }
  /* the four phases are the tallest block on a phone; at the desktop's row
     rhythm they measured 46.1vh and their bottom landed 33px off an 844px fold */
  .ph li{ padding:11px 0 }
  .ph__k{ font-size:1.25rem }
  .hold--open   .cap{ --cap:52vh }
  .hold--claim  .cap{ --cap:54vh }
  .hold--ledger .cap{ --cap:50vh }
  .hold--print  .cap{ --cap:50vh }
  .hold--void   .cap{ --cap:60vh }
  .hold--phases .cap{ --cap:50vh }
  .hold--ask    .cap{ --cap:50vh }
}

/* inside the screen the luminous plume stands centre-right: the copy keeps the left third */
.beat--stage .big{ max-width:none }
.beat--stage .spec{ max-width:min(38vw, 520px) }
.beat--stage .spec > div{ grid-template-columns:minmax(100px,12ch) 1fr }

/* the page ends when the footer fills the frame — the ask has masked out by then */
.foot{ min-height:100vh; display:flex; flex-direction:column; justify-content:flex-end }
