/* ==========================================================================
   spatial-type.css · SPATIAL TYPE · v6.4 · precision scroll
   A reusable scroll-directed typographic system. One scene is one chapter:
   a scroll region (--st-dur vh) with a sticky viewport stage. The
   choreography is data: the scene's score (data-st-score) names every unit
   on the stage and gives it a path on the beat axis u, and spatial-type.js
   writes each unit's transform, opacity and colour as a pure function of
   the document's position, which the motion clock (motion-clock.js) moves
   from one requestAnimationFrame loop. This stylesheet
   owns everything that is not motion: the typography, the grid, the
   semantic ink, the ghost planes, the label gutter, and the resolved
   composition that stands without scripting, in print and under reduced
   motion.

   Nothing is written per frame but transform, opacity and colour: on the
   units, on the two planes (the carriage: the stage's contents decelerate
   onto the grid after the stage pins and accelerate back to page speed
   before it lets go), on the caption and on the sequence label's states,
   which crossfade. No custom property changes per frame. data-st-seq names
   the current sequential state for scripts and tests only.

   The field is flat Carbon. Nothing here tints it: no gradient, no glow,
   no bloom, no clipped fill, no shadow. Ghost words are white at a low
   level, which lifts Carbon neutrally. Readable type is flat matte in the
   semantic palette (Bone: the idea in reading position; Acid: resolved or
   leaving focus; latent: context; Silver: subordinate copy). The engine
   moves between these states in oklab; at rest every word is exactly one
   of them, and the static classes (.st__w--bone …) are the same ink
   without a script.

   Opacity is never authored for readable type. The engine derives it from
   the unit's position against the stage, so text is fully legible anywhere
   it can be read and fades only as it physically leaves the stage; a
   statement that rides out with the page keeps its ink until the page has
   taken it away.

   Labels: the scene's numeral and words hang in a gutter to the left of the
   principal statement column on desktop, sharing the baseline of the
   statement's last line (grid, last-baseline alignment); below that width
   they stand above the statement in flow. Never overlaid, never behind.

   Namespaced: every selector is .st-scoped. Tokens: Carbon #171717, Acid
   #DFFF00, Bone #F2EFE9, Silver #9FA4A5, latent #8A8A8A, white for ghosts.
   ========================================================================== */

.st{
  --st-top:calc(var(--nav-h,64px) + var(--v-bar-h,52px));
  --st-fgw:calc(var(--st-vw,100vw) - var(--st-left,0px) - var(--v-pad,24px));
  --st-ggap:clamp(28px,2.4vw,44px);                /* the gap between the label gutter and the statement */
  /* the foreground's vertical paddings plus the stacked label row: the
     stage's height bounds the type so a composition always fits it */
  --st-fgpad:calc(clamp(56px,11vh,120px) + clamp(56px,10vh,110px) + 44px);
  --st-cap:156px;
  position:relative;margin:0;
  padding:clamp(48px,9vh,112px) 0 clamp(40px,7vh,88px);
  color:#F2EFE9;
}
.st__stage{position:relative;min-height:0}
.st__bg{
  position:absolute;inset:0;pointer-events:none;overflow:hidden;z-index:0;
  user-select:none;-webkit-user-select:none;
}
.st__fg{position:relative;z-index:2}
.st__meta{
  position:absolute;right:var(--v-pad,24px);bottom:clamp(14px,3vh,30px);z-index:2;margin:0;
  font-weight:500;font-size:10.5px;letter-spacing:.22em;text-transform:uppercase;
  color:#9FA4A5;pointer-events:none;user-select:none;-webkit-user-select:none;
}
.st__meta__s{margin-left:1.2em;color:#DFFF00}

/* the scene label: the section's numeral and the scene's words. In the
   static composition it stands above the statement; on the live stage it
   hangs in the label gutter at the statement's last baseline (desktop) or
   stands above it (narrower). A sequence advances its label with its
   statements; the sequence's own name stays for assistive technology. */
.st__eyebrow{
  margin:0 0 clamp(20px,3.4vh,40px);
  font-weight:500;font-size:11px;letter-spacing:.26em;text-transform:uppercase;color:#DFFF00;
  font-variant-numeric:tabular-nums;white-space:nowrap;
}
.st__num{color:#DFFF00}
.st__lab{margin-left:.9em;color:#F2EFE9}
.st__lab__s{display:none;color:#F2EFE9}
.v-layout-js .st:not(.is-static) .st__lab__all{position:absolute;width:1px;height:1px;overflow:hidden;clip:rect(0 0 0 0);white-space:nowrap}
/* live: the states stack in one grid cell and crossfade (the engine writes
   each one's opacity); the inline grid keeps the label on the baseline */
.v-layout-js .st:not(.is-static) .st__lab--seq{display:inline-grid;vertical-align:baseline}
.v-layout-js .st:not(.is-static) .st__lab__s{display:block;grid-area:1/1;white-space:nowrap;opacity:0}

/* the statement: Radikal Light, uppercase, its lines governed exactly, sized
   so the widest line fills the reading column and the whole composition
   fits the stage; never clamped by wrapping, never rotated */
.st__statement{
  margin:0;
  font-weight:300;text-transform:uppercase;
  letter-spacing:-.03em;line-height:.94;
  font-size:min(
    calc(var(--st-fgw) / (var(--st-em,10) * 1.03)),
    var(--st-cap),
    calc((100vh - var(--st-top) - var(--st-fgpad)) / ((var(--st-lines,3) + var(--st-extra,0)) * .94)));
}
.st--grand{--st-cap:200px}
/* beside a count the statement keeps clear of the numerals: the widest
   numeral's own width, and a gap, come off the reading column */
.st--count{--st-numw:calc(min(var(--st-stw,100vw) * .38 / var(--nem,.83), (100vh - var(--st-top)) * .5) * .98)}
.st--count .st__statement{
  font-size:min(
    calc(var(--st-fgw) / (var(--st-em,10) * 1.03)),
    calc((var(--st-stw,100vw) - var(--st-left,0px) - var(--st-numw) - var(--v-pad,24px) - clamp(24px,3vw,56px)) / (var(--st-em,10) * 1.03)),
    var(--st-cap),
    calc((100vh - var(--st-top) - var(--st-fgpad)) / ((var(--st-lines,3) + var(--st-extra,0)) * .94)));
}
.st__l{display:block;white-space:nowrap;transform-origin:0 50%}
.st--center .st__statement{text-align:center}
.st--center .st__l{transform-origin:50% 50%}
.st__lp{display:inline-block}
.st__w,.st__s,.st__p{display:inline-block;transform-origin:50% 50%}
.st__p{white-space:nowrap}
/* the shared-suffix axis: every line but the last steps in by the
   difference of the two stems, so the two suffixes stack on one axis */
.st__l:not(:last-child){margin-left:var(--st-ind,0em)}
.st--center .st__l:not(:last-child){margin-left:0}
/* a sequence: its statements share one place and one last baseline; each
   is a real heading for assistive technology */
.st__statement--seq{display:grid;align-items:last baseline}
.st__b{grid-area:1/1;margin:0;font:inherit;letter-spacing:inherit;line-height:inherit;text-transform:inherit;transform-origin:0 50%}

/* the semantic ink at rest (and without a script) */
.st__w--bone,.st .st__w--bone{color:#F2EFE9}
.st__w--acid,.st .st__w--acid{color:#DFFF00}
.st__w--silver,.st .st__w--silver{color:#9FA4A5}
.st__w--latent,.st .st__w--latent{color:#8A8A8A}

/* the secondary: real copy, read once, subordinate to the statement */
.st__secondary{
  margin:clamp(18px,3vh,36px) 0 0;
  font-weight:300;text-transform:uppercase;letter-spacing:-.012em;line-height:1.05;color:#F2EFE9;
}
.st__secondary--words{
  display:flex;flex-wrap:wrap;gap:.2em 1.4em;
  font-size:min(clamp(18px,3.2vw,52px),calc(var(--st-fgw) / (var(--st-xem,20) * 1.12)));
}
.st__secondary--words .st__w{white-space:nowrap}
.st__secondary--lines{
  font-size:min(clamp(20px,3.4vw,56px),calc(var(--st-fgw) * .6 / var(--st-yem,10)));
}

/* the count: one numeral at a time in the reading position, at the right */
.st__count{
  position:absolute;inset:0 var(--v-pad,24px) 0 auto;z-index:1;
  display:grid;align-content:center;justify-items:end;
  pointer-events:none;user-select:none;-webkit-user-select:none;
}
.st__numeral{
  grid-area:1/1;
  font-weight:300;line-height:1;letter-spacing:-.06em;font-variant-numeric:tabular-nums;white-space:nowrap;
  font-size:min(calc(var(--st-stw,100vw) * .38 / var(--nem,.83)),calc((100vh - var(--st-top)) * .5));
  transform-origin:50% 50%;
}

/* the ghost words: at most two on a stage, each one of the scene's largest
   ideas, white at a low level (a neutral lift of Carbon), sized as a
   fraction of the stage's width, centred on their anchor, never read */
.st__ghost{
  position:absolute;left:calc(var(--gx,.5) * 100%);top:calc(var(--gy,.5) * 100%);
  translate:-50% -50%;
  font-weight:300;text-transform:uppercase;letter-spacing:-.04em;line-height:1;white-space:nowrap;
  font-size:calc(var(--st-stw,100vw) * var(--gs,.8) / var(--gem,4));
  color:#FFFFFF;opacity:var(--gl,.06);
  transform-origin:50% 50%;
}
.st__ghost--v{font-size:calc(var(--st-sth,100vh) * var(--gs,.8) / var(--gem,4))}
/* the one orthogonal exception: the vertical ghost's inner span is turned
   a deliberate 90°; the unit that moves is its upright parent */
.st__ghost__r{display:inline-block;transform:rotate(90deg)}

/* the rule beneath a statement: a unit whose scale is its drawn length;
   the engine draws it as the composition locks */
.st__rule{display:block;height:2px;margin-top:.32em;background:#DFFF00;transform-origin:0 50%}

/* a selection paints Carbon ink on the Acid highlighter */
.st__statement ::selection,.st__statement::selection,.st__secondary ::selection{color:#171717;background:#DFFF00}

/* ====================================================== THE LIVE STAGE */
/* Only once scripting has taken charge (.v-layout-js, and the scene no
   longer .is-static): the region is --st-dur viewport heights tall, the
   stage sticks beneath the site header and the sticky bar, and the scene
   bleeds to the viewport's edges: --st-left and --st-vw are measured once
   by the script, on resize, never per frame. */
.v-layout-js .st:not(.is-static){
  height:calc(var(--st-dur,280) * 1vh);
  padding:0;
  width:var(--st-vw,100vw);
  margin-left:calc(-1 * var(--st-left,0px));
}
.v-layout-js .st:not(.is-static) .st__stage{
  position:sticky;top:var(--st-top);
  height:calc(100vh - var(--st-top));
  overflow:hidden;
}
@supports (height:100svh){
  .v-layout-js .st:not(.is-static) .st__stage{height:calc(100svh - var(--st-top))}
}
/* The foreground is a grid: the label's gutter, then the statement column
   (which stays exactly the page's reading column). Rows are the statement
   (--st-srow), the secondary (--st-xrow) and the label (--st-lrow); every
   row aligns its items on their last baseline, so the label shares the
   baseline of the statement's last line when it hangs beside it. Stacked
   (narrow) is the default; hanging is declared at the desktop width below.
   The foreground itself never moves or fades: its padding box is the safe
   reading region, and every unit leaves on its own path. */
.v-layout-js .st:not(.is-static) .st__fg{
  --st-lrow:1;--st-srow:2;--st-xrow:3;
  position:absolute;inset:0;
  padding:clamp(56px,11vh,120px) var(--v-pad,24px) clamp(56px,10vh,110px) var(--st-left,0px);
  display:grid;grid-template-columns:minmax(0,1fr);grid-auto-rows:max-content;
  align-content:center;align-items:last baseline;justify-items:start;
}
.v-layout-js .st:not(.is-static).st--qualities .st__fg{--st-lrow:1;--st-xrow:2;--st-srow:3}
.v-layout-js .st:not(.is-static) .st__eyebrow{
  /* stacked: clear of the statement's glyph ascent (its ink overflows the tight line box upward);
     in front of the statement, so a sequence's departing block passes behind the label */
  grid-column:1;grid-row:var(--st-lrow);margin:0 0 clamp(40px,5vh,56px);
  align-self:end;align-self:last baseline;position:relative;z-index:2;
}
.v-layout-js .st:not(.is-static) .st__statement{grid-column:1;grid-row:var(--st-srow)}
.v-layout-js .st:not(.is-static) .st__secondary{grid-column:1;grid-row:var(--st-xrow);margin-top:0}
.v-layout-js .st:not(.is-static).st--qualities .st__secondary{margin:0 0 clamp(18px,3vh,36px)}
.v-layout-js .st:not(.is-static).st--after .st__secondary{margin-top:clamp(18px,3vh,36px)}
/* the count is a positioned plane across the stage, not a row of the grid:
   auto placement gives an absolutely positioned child the grid's own edges */
.v-layout-js .st:not(.is-static) .st__count{grid-row:auto;grid-column:auto}
.v-layout-js .st:not(.is-static).st--center .st__statement{justify-self:stretch}
@media (min-width:1280px){
  /* hanging: the gutter is the page's left margin (the rail lives at its
     top, the label hangs at the statement's last baseline, well below) */
  .st{--st-fgpad:calc(clamp(56px,11vh,120px) + clamp(56px,10vh,110px))}
  .v-layout-js .st:not(.is-static) .st__fg{
    --st-lrow:var(--st-srow);--st-srow:1;--st-xrow:2;
    padding-left:0;
    grid-template-columns:[gutter] calc(var(--st-left,0px) - var(--st-ggap)) [col] minmax(0,1fr);
    column-gap:var(--st-ggap);
  }
  .v-layout-js .st:not(.is-static).st--qualities .st__fg{--st-xrow:1;--st-srow:2;--st-lrow:2}
  .v-layout-js .st:not(.is-static) .st__eyebrow{
    grid-column:gutter;justify-self:end;margin:0;
    max-width:calc(var(--st-left,0px) - var(--st-ggap) - var(--v-pad,24px));
  }
  .v-layout-js .st:not(.is-static) .st__statement,
  .v-layout-js .st:not(.is-static) .st__secondary{grid-column:col}
  .v-layout-js .st:not(.is-static) .st__count{grid-row:auto;grid-column:auto}
}
/* will-change only on the scene that is actually moving */
.st.is-live [data-u]{will-change:transform,opacity}

/* ================================================= THE STATIC COMPOSITION */
/* The resolved reading, with nothing to scroll: before the script runs,
   without scripting, in print and under reduced motion. Every unit on its
   slot in its resting ink; a sequence as its statements, one after
   another; the count as its destination. */
.st.is-static .st__stage{position:static;height:auto;overflow:visible}
/* the static composition sits in the reading column, not on a full-bleed
   stage, so it is sized by its own container's width (the live stage is
   sized by --st-vw, which only the script measures) */
.st.is-static{container-type:inline-size}
@supports (width:1cqw){
  .st.is-static .st__stage{--st-fgw:100cqw;--st-stw:100cqw}
}
.st.is-static [data-u]{transform:none;opacity:1}
.st.is-static .st__bg{inset:-10% 0}
.st.is-static .st__ghost{opacity:var(--gl,.06)}
.st.is-static .st__meta{position:static;margin-top:clamp(18px,3vh,32px)}
.st.is-static .st__statement--seq{display:block}
.st.is-static .st__b{display:block;margin:0 0 .45em;font-size:.75em}
.st.is-static .st__b:last-child{margin-bottom:0}
.st.is-static .st__count{position:relative;inset:auto;display:block;margin-top:clamp(18px,3vh,36px);justify-items:start}
.st.is-static .st__numeral{display:none}
.st.is-static .st__numeral[data-u="n10"]{display:block;font-size:min(calc(var(--st-fgw) * .3 / var(--nem,.83)),200px)}
.st.is-static .st__rule{transform:scaleX(1)}

/* ================================================================ MOBILE */
/* The same message hierarchy: governed narrow breaks, the type sized to the
   widest of them, the count beneath the statement instead of beside it. */
@media (max-width:1279px){
  .st__meta{display:none}
}
@media (max-width:860px){
  .st{--st-fgpad:calc(clamp(64px,12vh,100px) + clamp(40px,8vh,80px) + 44px);--st-cap:64px}
  .v-layout-js .st:not(.is-static){height:calc(var(--st-dur-m,190) * 1vh)}
  .v-layout-js .st:not(.is-static) .st__fg{padding-top:clamp(64px,12vh,100px);padding-bottom:clamp(40px,8vh,80px)}
  .st__statement{
    font-size:min(
      calc(var(--st-fgw) / (var(--st-emm,var(--st-em,10)) * 1.03)),
      var(--st-cap),
      calc((100vh - var(--st-top) - var(--st-fgpad)) / ((var(--st-linesm,var(--st-lines,3)) + var(--st-extra,0)) * .94)));
    line-height:.98;
  }
  .st__lp{display:block}
  .st__secondary--words{font-size:min(clamp(15px,4.2vw,24px),calc(var(--st-fgw) / (var(--st-xem,20) * .62)));gap:.15em 1em}
  .st__secondary--lines{font-size:clamp(16px,4.6vw,24px)}
  .st__ghost{font-size:calc(var(--st-stw,100vw) * min(var(--gs,.8),1.1) / var(--gem,4))}
  .v-layout-js .st:not(.is-static).st--count .st__fg{align-content:start}
  .st__count{inset:auto var(--v-pad,20px) clamp(24px,6vh,48px) auto}
  .st__numeral{font-size:min(calc(var(--st-stw,100vw) * .34 / var(--nem,.83)),calc((100vh - var(--st-top)) * .3))}
  .st--count .st__statement{
    font-size:min(
      calc(var(--st-fgw) / (var(--st-emm,var(--st-em,10)) * 1.03)),
      var(--st-cap),
      calc((100vh - var(--st-top) - var(--st-fgpad)) / ((var(--st-linesm,var(--st-lines,3)) + var(--st-extra,0) + 2.2) * .94)));
  }
  .st__l:not(:last-child){margin-left:0}
}

/* ======================================================== REDUCED MOTION */
/* No choreography: every scene is its static editorial composition, every
   word visible at its intended scale in its resting ink, ordinary
   scrolling, nothing hidden, nothing off the stage. */
@media (prefers-reduced-motion:reduce){
  .v-layout-js .st,.v-layout-js .st:not(.is-static){height:auto;padding:clamp(48px,9vh,112px) 0 clamp(40px,7vh,88px);width:auto;margin-left:0}
  .st__stage,.v-layout-js .st:not(.is-static) .st__stage{position:static;height:auto;overflow:visible}
  .v-layout-js .st:not(.is-static) .st__fg{position:relative;inset:auto;padding:0;display:block}
  .v-layout-js .st:not(.is-static) .st__eyebrow{position:static;margin:0 0 clamp(20px,3.4vh,40px)}
  .st [data-u]{transform:none!important;transition:none!important;opacity:1!important;will-change:auto}
  .st .st__fg,.st .st__bg{transform:none!important}
  .st .st__ghost{opacity:var(--gl,.06)!important}
  .st .st__lab__s{display:none!important}
  .v-layout-js .st:not(.is-static) .st__lab__all{position:static;width:auto;height:auto;overflow:visible;clip:auto}
  .st__statement--seq{display:block}
  .st__b{display:block;margin:0 0 .45em;font-size:.75em}
  .st__count{position:relative;inset:auto;display:block;margin-top:clamp(18px,3vh,36px);justify-items:start}
  .st__numeral{display:none}
  .st__numeral[data-u="n10"]{display:block;font-size:min(calc(var(--st-fgw) * .3 / var(--nem,.83)),200px)}
  .st__rule{transform:scaleX(1)!important}
  .st__meta{position:static;margin-top:clamp(18px,3vh,32px);opacity:1!important}
  .st__statement{font-size:min(calc(var(--st-fgw) / (var(--st-em,10) * 1.03)),var(--st-cap))}
}

/* ================================================================== PRINT */
/* Ordinary printing: the scene is its statement, in ink, in the flow. */
@media print{
  .st,.v-layout-js .st:not(.is-static){height:auto;width:auto;margin:0;padding:12pt 0 6pt}
  .st__stage,.v-layout-js .st:not(.is-static) .st__stage{position:static;height:auto;overflow:visible}
  .v-layout-js .st:not(.is-static) .st__fg{position:static;inset:auto;padding:0;display:block}
  .v-layout-js .st:not(.is-static) .st__eyebrow{position:static;margin:0 0 6pt}
  .st [data-u]{transform:none!important;opacity:1!important;transition:none!important}
  .st .st__fg,.st .st__bg{transform:none!important}
  .st .st__fg,.st .st__fg *{color:#171717!important;background:none!important}
  .st__statement{font-size:22pt;line-height:1.05}
  .st__statement--seq{display:block}
  .st__b{display:block;margin:0 0 6pt;font-size:22pt}
  .st__secondary{font-size:12pt}
  .st__secondary--words{display:block}
  .st__bg,.st__count,.st__meta{display:none!important}
  .st__lab__s{display:none!important}
  .v-layout-js .st:not(.is-static) .st__lab__all{position:static;width:auto;height:auto;overflow:visible;clip:auto}
  .st__rule{display:none}
}
