/* ==========================================================================
   sections.css — the page's own components, one block per Figma section.

   NO NESTED COMMENTS ANYWHERE IN THIS FILE. CSS comments do not nest: the
   first "close" inside a block comment ends it, and everything after is
   parsed as a selector until the next brace, which silently swallows the rule
   that follows. Annotate derivations with "--", never with a second comment.

   HOW EVERY NUMBER IN HERE IS DERIVED:
   X IS PROPORTIONAL, Y IS PIXELS FROM THE NEAREST EDGE.
       left: 60.2083%;             -- 867 / 1440
       top:  60px;                 -- upper half: the file's number verbatim
       top:  calc(100% - 32px);    -- lower half: 868 on a 900 frame
   WHICH HALF? y > frame_h / 2 belongs to the bottom edge.
   NEVER `bottom` FOR A BLOCK THAT CAN GAIN A LINE.

   CAP HEIGHT. Figma's y is the top of the CAPITALS (the file uses text-box
   trim = cap / alphabetic on every text node). CSS does the same thing with
   `text-box: trim-both cap alphabetic`, so a label's box IS its caps and the
   file's y can be used verbatim. Browsers without text-box (Firefox, as of
   writing) fall back to the plain line box, which at 12px on a .85 leading
   sits within about a pixel of the same place.
   ========================================================================== */

.section{ position:relative; }
.section--full{ min-height:100dvh; }


/* ==========================================================================
   SECTION 01 — HERO ("Index", node 15197:341)
   Frame 1440 x 900. The composition is two parts:
     LEFT  a HUD panel, x 5 -> 862, drawn in brackets, rules and labels
     RIGHT a column, x 867 -> 1435, stacked top to bottom:
           nav bar 20 -- gap 5 -- viewer (flexes) -- gap 4 -- headline -- 9 to the frame's bottom
   ========================================================================== */

.hero{
  height:100dvh;
  min-height:620px;
  overflow:hidden;
  background:var(--c-ground);

  /* R is the left panel's right edge, 862 / 1440. C is the panel's centre
     line, where the vertical rule and both tees sit. */
  --R:59.8611%;
  --C:calc((5px + var(--R)) / 2);

  /* THE LOOK DIALS. Every value here is the file's own; js/look.js
     overwrites them live from the tuning panel. They are written here too so
     the page looks right with JS off. When a tuned value is baked in, it
     changes in BOTH places: here and in CONFIG in js/look.js. */
  --tex-opacity:.4;
  --tex-blend:luminosity;
  --stripe-alpha:.06;
  --stripe-gap:4px;          /* half the file's width (4 -> 2) and as much space between: denser */
  --stripe-w:2px;
  --stripe-angle:112.587deg;
  --stripe-phase:1px;
  --rule-opacity:.2;
  --hud-opacity:1;
  --label-opacity:1;
}

.hero :where(p){ margin:0; }

/* ---- THE LABEL: the only type style in the frame ------------------------ */
.t{
  position:absolute;
  margin:0;
  font-size:var(--ui-12);
  line-height:var(--ui-lh);
  letter-spacing:var(--ui-track);
  font-kerning:none;
  text-transform:uppercase;
  white-space:nowrap;
  color:var(--c-ink);
  text-box:trim-both cap alphabetic;
}

/* ---- NAV (node 15197:449) ----------------------------------------------- */
.nav__logo{
  position:absolute; left:var(--gut); top:var(--gut);
  width:167px; height:33px;
}
.nav__logo img{ width:100%; height:100%; }

.nav__links{
  position:absolute;
  top:var(--gut); height:20px; align-items:center;   /* on the middle of the Get in touch bar (5 + 20) */
  right:calc(100% - var(--R));
  display:flex; gap:5px;
  margin:0; padding:0; list-style:none;
}
.nav__links .t{ position:static; display:block; }
.nav__links a:hover,
.nav__links a:focus-visible{ opacity:.6; }

/* ---- LEFT HUD PANEL ----------------------------------------------------- */
.hud > *{ position:absolute; }
.hud img{ display:block; }

.brk--tl{ left:var(--gut); top:43px; }
.brk--tr{ left:calc(var(--R) - 17px); top:43px; }
.brk--bl{ left:var(--gut); top:calc(100% - 22px); }
.brk--br{ left:calc(var(--R) - 17px); top:calc(100% - 22px); }

.brk, .tee, .tick, .thumb__frame{ opacity:var(--hud-opacity); }
.hud .t{ opacity:var(--label-opacity); }

/* EFFECT SWITCHES. .no-tex / .no-stripes are the console's per-effect
   toggles; html.fx-off is its A/B button, which drops every effect at once
   so the flat composition can be compared against the finished one. */
.hero.no-tex .title__tex,
.hero.no-tex .title__dot-tex,
html.fx-off .hero .title__tex,
html.fx-off .hero .title__dot-tex,
html.fx-off .hero .noise{ display:none; }
.hero.no-stripes .viewer,
html.fx-off .hero .viewer{ background:none; }

.tee--top{    left:calc(var(--C) - 8.5px); top:86px; }
.tee--bottom{ left:calc(var(--C) - 8.5px); top:calc(100% - 14px); }
.tee--left{   left:37px;                   top:calc(50% - 9px); }
.tee--right{  left:calc(var(--R) - 41px);  top:calc(50% - 9px); }

/* Each rule fades to nothing at --fade-at and is brightest at its ends. The
   file puts the fade at 45%, which is where the two rules cross; js/hero.js
   moves it with the cursor, so the dark gap always sits at the crossing. */
.rule{
  opacity:var(--rule-opacity);
  -webkit-mask:var(--dash);
          mask:var(--dash);
}
.rule--v{
  left:calc(var(--C) - .25px);
  top:95px;
  width:.5px;
  height:calc(100% - 109px);
  background:linear-gradient(to bottom, var(--c-ink) 0%, rgba(253,244,223,0) var(--fade-at, 45%), var(--c-ink) 100%);
  --dash:repeating-linear-gradient(to bottom, #000 0 1px, transparent 1px 2px);
}
.rule--h{
  left:46px;
  top:calc(50% - .5px);
  width:calc(var(--R) - 87px);
  height:.5px;
  background:linear-gradient(to right, var(--c-ink) 0%, rgba(253,244,223,0) var(--fade-at, 45%), var(--c-ink) 100%);
  --dash:repeating-linear-gradient(to right, #000 0 1px, transparent 1px 2px);
}

.lbl--tl{ left:22px; top:60px; }
.lbl--tr{ right:calc(100% - var(--R) + 17px); top:60px; text-align:right; }
.lbl--tc{
  left:var(--C); top:60px;
  width:89px; transform:translateX(-50%);
  text-align:center; white-space:normal;
  line-height:.9;
}
.lbl--bl{ left:22px; top:calc(100% - 32px); }
.lbl--br{ right:calc(100% - var(--R) + 17px); top:calc(100% - 32px); text-align:right; }

.coord{
  display:flex; gap:5px;
  top:calc(50% - 5px);
}
.coord .t{ position:static; }
.coord--y{ left:var(--gut); }
.coord--x{ right:calc(100% - var(--R)); }

/* ---- RIGHT COLUMN --------------------------------------------------------- */
.col{
  position:absolute;
  left:60.2083%;
  right:var(--gut);
  top:var(--gut);
  bottom:9px;
  display:flex;
  flex-direction:column;
}

.cta{
  flex:0 0 20px;
  display:flex; align-items:center; justify-content:center;
  background:var(--c-ink);
  color:var(--c-ground);
  transition:opacity .2s;
}
.cta .t{ position:static; color:inherit; }
.cta:hover,
.cta:focus-visible{ opacity:.85; }

/* ---- VIEWER (node 15197:363 "Interactive Area", 568 x 566) ---------------
   The stripes are one Figma vector: vertical lines 9.5438px apart, a 4px
   cream stroke at 6%, rotated 22.587deg. In CSS that is a repeating
   gradient. Its axis is the lines' normal, (0.9233, 0.3841), which is
   112.587deg in CSS terms; for that angle the gradient's zero sits exactly on
   the box's top-left corner at ANY box size, so the phase is fixed: line
   centres at 2.9816 + n * 9.5438. The stroke is 4px centred, so each band
   runs 0.9816 to 4.9816.
   Written with variables so the panel can change spacing, width, angle and
   strength; a band is clamped inside its period so a wide stroke on a tight
   spacing cannot break the pattern. */
.viewer{
  position:relative;
  flex:1 1 auto;
  min-height:0;
  margin-top:5px;
  overflow:hidden;
  --s-a:max(0px, calc(var(--stripe-phase) - var(--stripe-w) / 2));
  --s-b:min(var(--stripe-gap), calc(var(--s-a) + var(--stripe-w)));
  --s-c:color-mix(in srgb, var(--c-ink) calc(var(--stripe-alpha) * 100%), transparent);
  /* one stripe period along the x axis (the stripes are slanted) */
  --s-per:calc(var(--stripe-gap) / max(sin(var(--stripe-angle)), -1 * sin(var(--stripe-angle))));
}
/* THE STRIPES DRIFT left to right inside the box, slowly and evenly: a layer
   one period wider than the box, moved by transform only (the compositor;
   no repaint, no shimmer of re-drawn lines) and looped on its period */
.viewer::before{
  content:''; position:absolute; top:0; bottom:0; left:calc(-1 * var(--s-per)); right:0; z-index:0; pointer-events:none;
  background:repeating-linear-gradient(var(--stripe-angle),
    transparent 0 var(--s-a),
    var(--s-c) var(--s-a) var(--s-b),
    transparent var(--s-b) var(--stripe-gap));
  will-change:transform;
  animation:viewer-stripes .234s linear infinite;   /* 5x the first pace (2.5x, then 2x again): ~18 px/s */
}
/* the way in (js/intro.js): the stripes come in one by one, a hard edge stepping
   a whole stripe at a time across them (--rv: how far, along their own axis) */
.viewer.stripes-in::before{ -webkit-mask-image:linear-gradient(var(--stripe-angle), #000 var(--rv, 0px), transparent var(--rv, 0px)); mask-image:linear-gradient(var(--stripe-angle), #000 var(--rv, 0px), transparent var(--rv, 0px)); }
@keyframes viewer-stripes{ from{ transform:translate3d(0, 0, 0); } to{ transform:translate3d(var(--s-per), 0, 0); } }
@media (prefers-reduced-motion: reduce){ .viewer::before{ animation:none; } }
.hero.no-stripes .viewer::before, html.fx-off .hero .viewer::before{ display:none; }
.viewer > *{ position:absolute; }

.tick{ width:6px; height:6px; }
.tick--tl{ left:0; top:0; }
.tick--bl{ left:0; top:calc(100% - 6px); }
.tick--br{ right:0; top:calc(100% - 6px); }

.v-lbl--1{ left:5px; top:5px; }
.v-lbl--2{ left:5px; top:17px; }

.v-lbl--range{ left:10px; top:calc(100% - 20px); }
.v-lbl--mid{   right:56px; top:calc(100% - 20px); transform:translateX(50%); }
.v-lbl--max{   right:10px; top:calc(100% - 20px); }

.scan{
  left:50%; top:calc(100% - 39px);
  width:34px; height:34px;
  transform:translateX(-50%);
  display:flex; align-items:center; justify-content:center;
  padding:0; border:0;
  background:var(--c-ink);
  cursor:pointer;
}
.scan img{ width:24px; height:24px; }

/* ---- THUMBNAILS (node 15197:374) ---------------------------------------- */
.thumbs{
  right:var(--gut); top:var(--gut);
  display:flex; gap:4.077px;
  margin:0; padding:0; list-style:none;
}
.thumbs li{ display:flex; }
.thumb{
  position:relative;
  width:78px; height:78px;
  padding:0; border:0;
  background:transparent;
  cursor:pointer;
  overflow:hidden;
}
.thumb__frame{ position:absolute; inset:0; width:78px; height:78px; }
.thumb__img{
  position:absolute; left:50%; top:50%;
  transform:translate(calc(-50% + var(--tx, 0px)), calc(-50% + var(--ty, 0px)));
  background-size:cover;
  background-position:center;
  background-repeat:no-repeat;
}
.thumb--1 .thumb__img{ width:44px; height:32px; --tx:.15px; background-image:url('../assets/img/mask-1.png'), url('../assets/img/mask-1-active.png'); }
.thumb--2 .thumb__img{ width:55px; height:53px; --tx:.58px; --ty:-2.5px; background-image:url('../assets/img/mask-2.png'), url('../assets/img/mask-2-active.png'); }
.thumb--3 .thumb__img{ width:65px; height:30px; --tx:.5px; background-image:url('../assets/img/mask-3.png'), url('../assets/img/mask-3-active.png'); }

/* TWO STATES, TWO PICTURES. The file's pictures at 4x: cream on the
   dark cards, black on the selected (cream) card. Each card carries both
   as layers, the unused one drawn at 0x0, so switching never flashes. */
.thumb--1.is-active .thumb__img{ background-image:url('../assets/img/mask-1-active.png'), url('../assets/img/mask-1.png'); }
.thumb--2.is-active .thumb__img{ background-image:url('../assets/img/mask-2-active.png'), url('../assets/img/mask-2.png'); }
.thumb--3.is-active .thumb__img{ background-image:url('../assets/img/mask-3-active.png'), url('../assets/img/mask-3.png'); }
.thumb__img{ background-size:cover, 0 0; }

.thumb .thumb__frame--on{ display:none; }
.thumb.is-active{ background:var(--c-ink-soft); }
.thumb.is-active .thumb__frame--off{ display:none; }
.thumb.is-active .thumb__frame--on{ display:block; }
/* THE PROGRESS LINE. In the file it is 23px of the card's 78 — a snapshot
   of a line that fills the card while the model is on show (js/hero.js
   drives --progress 0..1 over 3s, then the next model takes over). Drawn
   full width and scaled, so the fill is a transform and costs no layout;
   23/78.92 is the no-JS value, which is the file's picture. */
.thumb.is-active::after{
  content:'';
  position:absolute; left:-.92px; bottom:0;
  width:calc(100% + .92px); height:1px;
  background:var(--c-accent);
  transform-origin:0 50%;
  transform:scaleX(var(--progress, .2914));
}

/* ---- HEADLINE BLOCK (nodes 15197:353-362, 413, 421) ----------------------
   The headline is a vector in the file (outlined type), so it ships as an
   SVG and scales with the column. The block's box is the COLUMN's width
   (568) by the headline's height (291.12); every child is a percentage of
   that box so the whole group scales as one.
     headline   565.49 wide      -- 99.5586% of 568
     slash      at (510, 99)     -- 89.7887%, 34.0066%;  58 x 93 -> 10.2113% x 31.9456%
     circle     at (182, 197)    -- 32.0423%, 67.6697%;  93 -> 16.3732%
     icons      at (0 / 30, 197) -- 24px, fixed, they are UI glyphs not type

   THE TEXTURE. In the file a 1082x722 photo is masked by the headline and by
   the circle, laid on in LUMINOSITY at 40%. On the circle the clip (its
   border-radius) and the blend sit on the SAME element on purpose: a blend on
   the child of a clipped/masked parent is isolated and blends with nothing.
   Background offset as a percentage is offset / (box - image):
     headline  (-141, -215) -> 27.298%, 49.898%; size 1082/565.49, 722/291.12
     circle    (-503, -367) -> 50.859%, 58.347%; size 1082/93, 722/93 */
.title{
  position:relative;
  flex:0 0 auto;
  margin-top:4px;
  width:100%;
  aspect-ratio:568 / 291.12;
  /* The column is the unit: cqw = 1% of the column's width, so the type,
     its placement and the texture all scale together with it. */
  container-type:inline-size;
}
.title > *{ position:absolute; }

/* THE HEADLINE AS TEXT. Measured against the file's outline at the
   design size: 116px, tracking -2% (-0.02em), normal kerning — each line's
   ink width matched the outline to within 0.35px.
     font-size 116 / 568                                 -- 20.4225cqw
     line tops  = cap tops 1.16 / 99.16 / 197.16         -- trimmed to cap height
     line x     = outline ink x minus the glyph's own left bearing
                  N: 0 - 2 = -2, I: 0.109 - 2 = -1.891, S: 281.538 - 1 = 280.538
                  then -1px x on every line and -1px y on line 3, found by
                  pixel-matching the render against the outline (the canvas
                  bearings are whole pixels, so the derivation was 1px off)
   Each line is a full-size box (inset 0) placed by padding-top and
   text-indent, so all three share the block's box — which is what lets
   the texture below use ONE background size and position for every line. */
.title__type,
.title__tex{
  left:0; top:0;
  width:100%; height:100%;
  margin:0;
  font-size:20.4225cqw;
  font-weight:900;
  line-height:1;
  letter-spacing:-.02em;
  text-transform:uppercase;
  white-space:nowrap;
  color:var(--c-ink);
}
.ln{
  position:absolute; inset:0;
  display:block;
  text-box:trim-both cap alphabetic;
}
.ln--1{ padding-top:.2042cqw;  text-indent:-.5282cqw; }
.ln--2{ padding-top:17.4577cqw; text-indent:-.5090cqw; }
.ln--3{ padding-top:34.5352cqw; text-indent:49.2144cqw; }

/* THE TEXTURE, now on live text: every line is the block's size, so the
   file's offsets apply unchanged (offset / (box - image), see below), and
   background-clip:text cuts the photo to the letters. Blend and opacity sit
   on the wrapper so the three lines blend as one layer. */
.title__tex{
  width:99.5586%;                    /* the headline's own box: the texture offsets are measured against it */
  pointer-events:none;
}
.title__tex .ln{
  color:transparent;
  /* the concrete's specks (js/texture.js) on the ink itself: the headline keeps
     the site's one cream; moved by --tpx/--tpy for the parallax (js/story.js) */
  background-image:var(--tex-specks, none), linear-gradient(var(--c-ink), var(--c-ink));
  background-repeat:repeat, no-repeat;
  background-size:var(--tex-size, 768px), 100% 100%;
  background-position:var(--tpx, 0px) var(--tpy, 0px), 0 0;
  -webkit-background-clip:text;
          background-clip:text;
}

/* THE LIQUID REVEAL (js/titlefx.js) — blur + contrast, the "gooey" trick,
   done with compositor filters so it runs on the GPU:
     the h1 gets an opaque black ground and filter:contrast(k);
     each line (and the slash, the circle) gets filter:blur(b).
   Contrast on a blurred WHITE edge over black is a threshold at half
   coverage: the soft falloff snaps to solid ink or nothing, so blurred
   letters fuse into blobs and separate again as b shrinks. The ink is white
   while this runs and .title__tint, the ink colour multiplied on top, turns
   white back into cream and leaves black black.
   html.title-pending hides the title from the first paint until the reveal
   has started; the head script lifts it after 4s whatever happens. The
   texture sits the reveal out and fades in after it. */
/* THE WAY IN (js/intro.js): these wait for their turn from the first paint */
html.intro-pending .hero .nav__logo, html.intro-pending .hero .nav__links li, html.intro-pending .hero .nav__menu,
html.intro-pending .hero .cta, html.intro-pending .hud .brk, html.intro-pending .hud .tee, html.intro-pending .hud .rule,
html.intro-pending .hero .viewer, html.intro-pending .viewer .tick, html.intro-pending .viewer .thumbs li, html.intro-pending .viewer .scan,
html.intro-pending .title__icon, html.intro-pending .hud .coord, html.intro-pending .hud .lbl, html.intro-pending #gl{ visibility:hidden; }
html.title-pending .title > *,
.title.is-revealing .title__tex,
.title.is-revealing .title__dot-tex{ visibility:hidden; }
.title.is-revealing .title__type{
  background:var(--c-ground);
  color:#fff;
  filter:contrast(var(--goo-k, 1));
  /* THE INK STAYS IN ITS OWN BOX. The blur and the sideways stretch spread
     the lines past the headline's box — over the left panel and the viewer
     above. clip-path is applied after the filter, so the finished blobs are
     cut exactly at the headline's edges and touch nothing around them. */
  clip-path:inset(0);
  will-change:filter;
}
.title__tint{
  left:0; top:0; width:100%; height:100%;
  background:var(--c-ink);
  mix-blend-mode:multiply;
  pointer-events:none;
  display:none;
}
.title.is-revealing .title__tint{ display:block; }
.title.is-revealing .title__type > *{ will-change:filter, transform; }

.title__type > .title__slash,
.title__type > .title__dot{ position:absolute; display:block; }
.title__slash{ left:89.7887%; top:34.0066%; width:10.2113%; height:31.9456%; }

.title__dot,
.title__dot-tex{
  left:32.0423%; top:67.6697%;
  width:16.3732%; aspect-ratio:1;
  border-radius:50%;
}
.title__dot{ background:currentColor; }
.title__dot-tex{
  background-image:var(--tex-specks, none);
  background-repeat:repeat;
  background-size:var(--tex-size, 768px);
  background-position:var(--tpx, 0px) var(--tpy, 0px);
}

.title__icon{ top:67.6697%; width:24px; height:24px; }
.title__icon--1{ left:0; }
.title__icon--2{ left:30px; }

/* ---- NOISE (replaces node 15197:458) --------------------------------------
   The file lays a 1440x900 noise photo over EVERYTHING in DIFFERENCE at 30%.
   Here it is a live canvas drawn by js/noise.js in --c-ink, same blend, same
   opacity, so it moves instead of sitting still. Opacity and blend are set
   from JS so the tuning panel can drive them; these are the no-JS values. */
.noise{
  position:fixed; inset:0;
  width:100%; height:100%;
  pointer-events:none;
  opacity:.3;
  z-index:60;                 /* over everything, on every screen of the site */
  image-rendering:pixelated;
}

.sr-only{
  position:absolute !important;
  width:1px; height:1px; margin:-1px; padding:0; border:0;
  overflow:hidden; clip:rect(0 0 0 0); white-space:nowrap;
}


/* ---- TABLET: the HUD needs room ------------------------------------------
   Below ~1000px the left panel's two top labels (EXTREME ALTITUDE and the
   centred block) start to touch, because 12px type does not shrink with the
   panel. The centred block goes first. The viewer is then under ~395px
   wide, where the three 78px thumbnails reach the two labels at its top
   left, so they drop below them. */
@media (max-width:1000px){
  .lbl--tc{ display:none; }
  .thumbs{ top:34px; }
}

/* ---- PHONE: see the PHONE block at the end of this file ------------------ */
.nav__menu, .mmenu{ display:none; }


/* ==========================================================================
   LOADER (Figma "Loader" storyboard, 12180:24426) — built by js/loader.js.
   A fixed black sheet over the page. Positions that depend on the viewport
   (ticks, circles, squares) are set from JS; the rest is the file's numbers:
   labels 5px from the sides, phrases' cap top at the vertical centre - 5
   (445 on 900), the percentage's at 848 (bottom - 52), the bars' foot at 895.
   ========================================================================== */
html.is-loading{ overflow:hidden; }
.ldr{
  position:fixed; inset:0; z-index:1000;
  background:var(--c-ground);
  overflow:hidden;
  cursor:default;
  -webkit-user-select:none; user-select:none;
}
.ldr__a{ position:absolute; inset:0; }
.ldr .t{ color:var(--c-ink); }

/* corner ticks: 6px, the HUD's line colour */
.ldr__tick{
  position:absolute; left:0; top:0; width:6px; height:6px;
  border:0 solid var(--c-line);
  opacity:0; will-change:transform, opacity;
}
.ldr__tick--tl{ border-left-width:1px; border-top-width:1px; }
.ldr__tick--tr{ border-right-width:1px; border-top-width:1px; }
.ldr__tick--bl{ border-left-width:1px; border-bottom-width:1px; }
.ldr__tick--br{ border-right-width:1px; border-bottom-width:1px; }

.ldr__svg{ position:absolute; left:0; top:0; overflow:visible; pointer-events:none; }
.ldr__c{ fill:none; stroke:var(--c-ink); stroke-opacity:.2; stroke-width:1; stroke-dasharray:1 2; }
.ldr__arc{ fill:none; stroke-width:8; stroke-dasharray:2 1; }

/* the two phrases */
.ldr__txt{ top:calc(50% - 5px); will-change:transform; }
.ldr__txt--l{ left:var(--gut); }
.ldr__txt--r{ right:var(--gut); }

/* red squares with their numbers */
.ldr__sq{ position:absolute; width:10px; height:10px; opacity:0; }
.ldr__sq i{
  position:absolute; inset:0;
  background:var(--c-ground); border:1px solid var(--c-accent);
}
.ldr__sq .t{ left:15px; top:0; }
.ldr__sq--before .t{ left:auto; right:15px; }

/* percentage + bars */
.ldr__pct{ left:var(--gut); top:var(--pct-top, calc(100% - 52px)); opacity:0; }
.ldr__bars{ position:absolute; left:var(--gut); right:0; bottom:5px; height:var(--bar-h, 32px); }
.ldr__bars i{ position:absolute; left:0; bottom:0; width:0; }
.ldr__bars-off{ height:calc(var(--bar-h, 32px) / 2); background:repeating-linear-gradient(90deg, rgba(86,82,74,.4) 0 2px, transparent 2px 7px); }
.ldr__bars-on{ height:var(--bar-h, 32px); background:repeating-linear-gradient(90deg, var(--c-ink) 0 2px, transparent 2px 7px); }

/* ---- CLICK TO START: placed and faded by JS ---- */
.ldr__tag{
  position:absolute; left:0; top:0;
  width:62px; height:12px; padding-top:2px; box-sizing:border-box;
  text-align:center; text-box:none; line-height:10px;
  background:var(--c-accent); color:var(--c-ground) !important;
  opacity:0;
  pointer-events:none;
  will-change:transform, opacity;
}
.ldr__enter{
  position:absolute; width:1px; height:1px; padding:0; margin:-1px; overflow:hidden;
  clip:rect(0 0 0 0); white-space:nowrap; border:0;
}
.ldr:not(.is-idle) .ldr__enter{ visibility:hidden; }


/* ==========================================================================
   3D (js/scene3d.js + js/vendor/neuroform3d.js)
   One fixed canvas behind the whole page; the canvas draws its own ground,
   grain and vignette. The hero's ground is transparent, so its skull and
   mask show in their panels; as the hero scrolls away the 3D goes on
   behind everything after it (html.has-3d once the engine is up).
   ========================================================================== */
#gl{
  position:fixed; inset:0;
  width:100vw; height:100vh;
  display:block;
  z-index:0;
  touch-action:none;
}
html.has-3d body.hover{ cursor:pointer; }
main{ position:relative; z-index:1; }
/* past the hero its black ground would cover the big type's layer, which
   sits under <main>: the page's own ground (body) takes over */
html.is-deep .hero{ background:transparent; }

.hero .hud, .hero .viewer, .hero .title, .hero .noise{
  transition:opacity .6s ease;
}
html.is-deep .hero .hud,
html.is-deep .hero .viewer,
html.is-deep .hero .title{
  opacity:0;
  pointer-events:none;
}
/* the hero scrolls away — its panels move up a screen as p goes 0 -> 1
   (js/scroll.js sets --deep), the nav stays; the 3D rides with them */
html.page-scroll .hero{ background:transparent; }
/* a transformed .hud becomes its children's box: give it the hero's size */
html.page-scroll .hero .hud{ position:absolute; inset:0; }
html.page-scroll .hero .hud, html.page-scroll .hero .viewer, html.page-scroll .hero .title{
  transition:none; opacity:1;
  transform:translate3d(0, calc(var(--deep, 0) * -100vh), 0);
}
html.page-scroll.is-deep .hero .hud, html.page-scroll.is-deep .hero .viewer, html.page-scroll.is-deep .hero .title{ visibility:hidden; }
/* the grain's opacity is set inline by js/noise.js, so hiding it needs
   !important. It stays over the big type (html.is-bigtype), as in the file */

/* over the red section the grain sits above the story layer (z 10) */



/* ==========================================================================
   STORY — the UI after the hero (js/story.js), from the storyboard.
   Two fixed layers, each holding a stage the size of the window (phone: a
   375-wide stage scaled to the width). The Figma frames are designed
   against the viewport, so placement is the hero's rule:
     X  in % of the width (n / 1440), or centred, or from the right edge
     Y  px from the nearest edge: top half from the top, bottom half with
        calc(100% - n), things on the centre line from 50%
   Sizes are the file's px; the big type, the giant word and the footer
   word span the width, so they are in vw / %.
     .st-blend  the huge type behind the mask. OUTSIDE <main>, directly over
                the 3D canvas, in LIGHTEN: where the mask is brighter than
                the type the mask wins, so the type reads as behind it.
     .st        everything else, inside <main>, above the hero, below the nav.
   Every number is the Figma frame's. Opacity/transform come from JS.
   ========================================================================== */
main{ z-index:2; }
.st-blend{ position:fixed; inset:0; z-index:1; pointer-events:none; overflow:hidden; }
/* over the 3D the big type is drawn in LIGHTEN: where the mask is brighter
   than the type the mask wins, so the type reads as behind it */
html.has-3d .st-blend{ mix-blend-mode:lighten; }
/* the big type UNDER the 3D, which sees through where it is dark (js/story.js CHECKER):
   the mask over the type, the type seen through its checker */
html.has-3d.type-under .st-blend{ z-index:-1; mix-blend-mode:normal; }
/* the type under the mask, soft (js/story.js underBlur): over the type, under the 3D */
.st-under{ position:fixed; inset:0; z-index:-1; pointer-events:none; display:none; }
html.has-3d.type-under .st-under{ display:block; }
.st{ position:fixed; inset:0; z-index:10; pointer-events:none; overflow:hidden; }
.st-stage{ position:absolute; left:0; top:0; width:100%; height:100%; transform-origin:0 0; }
.st-stage > *, .st-blend .st-stage > *{ position:absolute; opacity:0; visibility:hidden; }
.st .t, .st-blend .t{ position:static; }
.hero .nav__logo, .hero .nav__links, .hero .cta{ z-index:30; }
.hero .cta{ position:relative; }
/* the header leaves on the scroll before the footer comes (js sets --nav-o) */
.hero .nav__logo, .hero .nav__links, .hero .cta{ opacity:var(--nav-o, 1); }
html.is-finale .hero .nav__logo, html.is-finale .hero .nav__links, html.is-finale .hero .cta{ pointer-events:none; }

/* 1. the huge type — 184 of 1440 (12.778vw), 85% leading, -2% tracking,
   centred across the width */
.st-big{
  left:.139%; top:-14px; width:99.722%;
  display:flex; flex-direction:column; align-items:center;
  font-size:12.778vw; line-height:.85; letter-spacing:-.02em; text-transform:uppercase;
  color:#fff; text-align:center; white-space:nowrap;
  will-change:transform, opacity;
}
/* THE LIQUID REVEAL, as on the hero headline: white ink on a black ground
   with a contrast (the goo threshold, js sets --goo-k); a cream layer
   multiplied on top turns white into the type's colour. In LIGHTEN over
   the page, the black ground is invisible. */
.st-big:not(.st-big--tex){ background:#000; }   /* the goo is per line now (js) */
.st-tint{ left:0; top:0; width:100%; height:100%; background:var(--c-ink); mix-blend-mode:multiply; }
/* the multiply needs a ground inside the layer: black, which LIGHTEN then drops */
.st-blend .st-stage{ background:#000; }
.st-big span{ display:block; }
/* the texture: the hero headline's photo clipped to the letters, 40% in
   luminosity (Figma: 1526x1017 at -45,-45 of the type's box) */
.st-big--tex span{
  color:transparent;
  /* the specks alone, over the type (its colour stays the site's one cream) */
  background-image:var(--tex-specks, none);
  background-repeat:repeat;
  background-size:var(--tex-size, 768px);
  /* each line shows its own band of one continuous texture */
  background-position:var(--tpx, 0px) calc(var(--tpy, 0px) - var(--i) * .85em);
  -webkit-background-clip:text; background-clip:text;
}
.st-side{ position:absolute !important; top:calc(50% - 5px); }   /* on the centre line, level with the side tees */
.st-side--l{ left:20px; }
.st-side--r{ right:20px; }

/* 2. fabric and skeleton: SCROLL. position repeated: ".st .t" is static */
.st .st-scroll{ position:absolute; }
.st-scroll--l{ left:5px; top:calc(100% - 15px); }
.st-scroll--c{ left:calc(50% - 14px); top:calc(100% - 15px); }
/* the frame round the big type: ticks at 5,43 / 5,889 / 1429,889, tees on
   the sides at y 441 (Figma 12072:5317) */
.st-mframe{ left:0; top:0; width:100%; height:100%; }
.st-mframe > *{ position:absolute; }
.st-mframe__tee{ width:9px; height:17px; top:calc(50% - 9px); }
.st-mframe__tee--l{ left:5px; } .st-mframe__tee--r{ right:5px; }
.st-mframe__tick{ width:6px; height:6px; }
.st-mframe__tick--tl{ left:5px; top:43px; }
.st-mframe__tick--tr{ display:none; }
.st-mframe__tick--bl{ left:5px; top:calc(100% - 11px); }
.st-mframe__tick--br{ right:5px; top:calc(100% - 11px); }

/* the skeleton frames' three thin rings (12173:21154), nested as in the
   file: two big ones from the left and right edges (906 of 1440 = 62.92vw),
   and the small one (372 = 25.83vw) exactly between their inner edges —
   its left edge is the right ring's, its right edge the left ring's. All
   on the centre line, all from the width, so the fit holds on any window
   (like the file, the big ones run past the top and bottom). */
.st-rings{ left:0; top:0; overflow:visible; }
.st-rings circle{ fill:none; stroke:rgba(253,244,223,.2); stroke-width:1; stroke-dasharray:1 1; stroke-dashoffset:1; }

/* 3. the skeleton card: 284x141, black, picture left, text + bar right */
.st-body{ left:calc(50% - 142px); top:calc(50% - 70px); width:284px; height:141px; background:#000; transform-origin:50% 50%; }
.st-body__pic{ position:absolute; left:5px; top:4px; width:131px; height:134px; overflow:hidden; background:#efe6d2; }
.st-body__pic i{ position:absolute; inset:0; background:url('../assets/img/body-pose-1.webp') 50% 50% / cover no-repeat #efe6d2; will-change:transform, clip-path; }
.st-brk{ position:absolute; top:5px; width:17px; height:17px; opacity:1; visibility:visible; }
.st-body .st-brk--l{ left:144px; } .st-body .st-brk--r{ right:5px; }
.st-body__txt{ position:absolute !important; left:141px; right:0; top:50%; transform:translateY(-50%); text-align:center; white-space:normal !important; line-height:.9; padding:0 30px; }
.st-body__bar{ position:absolute; left:144px; bottom:5px; width:135px; height:10px; display:flex; align-items:flex-end; gap:1px; }   /* 65 x 1px + 64 x 1px gaps: every gap exactly the same, no rounding */
.st-body__bar i{ width:1px; height:5px; background:rgba(86,82,74,.4); }
.st-body__bar i.on{ height:10px; background:#01be5a; }

/* 4. the red section */
.st-red{ position:absolute; inset:0; background:linear-gradient(180deg, #000 0%, #6a0000 100%); visibility:hidden; will-change:transform; }
/* the section's concrete texture: the hero's photo, its specks darkening
   the red (multiply) */
.st-red::after{ content:''; position:absolute; inset:0;
  background-image:var(--tex-img, url('../assets/img/texture.jpg')); background-repeat:repeat;
  background-size:var(--tex-size, 768px); background-position:var(--tpx, 0px) var(--tpy, 0px);
  mix-blend-mode:multiply; opacity:calc(.55 * var(--tex, 0)); }
.st-giant{
  left:0; top:0; width:100%; text-align:center; white-space:nowrap;
  font-size:var(--giant-fs, 20.833vw); line-height:.8; letter-spacing:-.02em; text-transform:uppercase; color:#4a0303;
  transform-origin:50% 0; will-change:transform; filter:blur(10px);
}
.st-div{ top:0; width:1px; height:100%;
  /* as the file: dotted, fading in from nothing at the top to its full
     strength at the bottom */
  background:linear-gradient(to bottom, rgba(253,244,223,0) 0%, rgba(253,244,223,.06) 35%, rgba(253,244,223,.32) 100%);
  -webkit-mask:repeating-linear-gradient(to bottom, #000 0 1px, transparent 1px 2px);
          mask:repeating-linear-gradient(to bottom, #000 0 1px, transparent 1px 2px); }
.st-div--l{ left:16.944%; } .st-div--r{ left:83.056%; } .st-div--c{ left:50%; }
.st-lockup{ left:50%; top:0; width:167px; height:116px; transform-origin:50% 0; }
.st-lockup--tex{
  /* the lockup's cream with the site's spots in it: cream x the concrete tile
     (as the Edition card's paper), drawn over the lockup through its own shape */
  background-color:var(--c-ink); background-image:var(--tex-specks, none);
  background-size:calc(var(--tex-size, 768px) / var(--lk-s, 1));
  -webkit-mask:url('../assets/svg/oxygen-lockup-ink.svg') center / 100% 100% no-repeat; mask:url('../assets/svg/oxygen-lockup-ink.svg') center / 100% 100% no-repeat; }
.st-lede{ position:absolute !important; left:50%; top:0; width:277px; text-align:center; white-space:normal !important; line-height:.95; letter-spacing:-.02em; }
.st-labels{ left:0; top:0; width:100%; height:100%; }
.st-lab{ position:absolute; top:0; display:flex; flex-direction:column; gap:6px; }
.st-lab b{ font-weight:inherit; opacity:.5; }
/* the file's "//": a 6x11 vector, 5px from the word, its cap-height box
   (the label is trimmed to the caps, so the slashes stand on the baseline
   and reach just above the caps, as drawn) */
.st-lab b.sl, .st-foot b.sl{
  display:inline-block; width:6px; height:11px; opacity:1;
  background:url('../assets/svg/slashes.svg') no-repeat 0 0 / 6px 11px;
  vertical-align:-1px;
}
.st-lab b.sl:first-child, .st-foot b.sl:first-child{ margin-right:5px; }
.st-lab b.sl:last-child, .st-foot b.sl:last-child{ margin-left:5px; }
/* the file mirrors the slashes that close a label on the right */
.st-lab b.flip{ display:inline-block; transform:scaleX(-1); }
.st-lab[data-k="class"]{ left:5px; }
.st-lab[data-k="layer"]{ left:17.292%; }
.st-lab[data-k="status"]{ left:calc(50% - 100px); width:200px; align-items:center; }
.st-lab[data-k="terrain"]{ right:17.292%; align-items:flex-end; }
.st-lab[data-k="state"]{ right:5px; align-items:flex-end; }
.st-cols{ left:0; top:0; width:100%; height:100%; }
.st-cband{ position:absolute; inset:0; overflow:hidden; }
.st-cmove{ position:absolute; inset:0; will-change:transform; }
/* PROGRESSIVE BLUR, the same at the top and the bottom: the word columns are
   never sharp (the labels over them must read): soft through the middle,
   softer still toward both edges, fading out at both */
.st-cband--sharp{ opacity:.45; filter:blur(9px);
  -webkit-mask-image:linear-gradient(180deg, #000 0, transparent 26%, transparent 74%, #000 100%);
          mask-image:linear-gradient(180deg, #000 0, transparent 26%, transparent 74%, #000 100%); }
.st-cband--soft{ opacity:.5; filter:blur(4.5px);
  -webkit-mask-image:linear-gradient(180deg, transparent 4%, #000 26%, #000 74%, transparent 96%);
          mask-image:linear-gradient(180deg, transparent 4%, #000 26%, #000 74%, transparent 96%); }
.st-col{ position:absolute; top:0; width:140px; }
.st-col--1{ left:5px; } .st-col--2{ left:17.292%; }
.st-col--3{ right:17.292%; text-align:right; } .st-col--4{ right:5px; text-align:right; }
.st-col p{ margin:0 0 5px; font-size:12px; line-height:.85; text-transform:uppercase; color:var(--c-ink); white-space:nowrap; }
.st-col--r{ text-align:right; }
.st-foot{ left:calc(50% - 100px); top:calc(100% - 31px); width:200px; display:flex; flex-direction:column; align-items:center; gap:6px; }
.st-foot b{ font-weight:inherit; opacity:.5; }

.st-card{
  left:calc(50% - 138px); top:0; width:275px; height:420px;
  background:var(--c-ink); color:#000;
  transform-origin:137px 210px;      /* no will-change: scaled up 9.6x it must re-draw sharp, not stretch a bitmap */
}
.st-card .st-brk--l{ left:5px; } .st-card .st-brk--r{ right:5px; }
/* the card's concrete texture (the hero's photo, multiplied into the cream);
   it fades with the cream when the card opens (--card-o, js) */
/* the card's concrete: js/story.js draws it procedurally at screen pixels
   (.st-conc) and clips it to the card, so it never scales or pixelates */
.st-conc{ position:fixed; left:0; top:0; width:100vw; height:100vh; pointer-events:none; mix-blend-mode:multiply; z-index:3; display:none; }
.st-card .st-brk{ filter:none; }
.st-card__title{
  position:absolute; left:0; right:0; top:10px; margin:0;
  font-size:32px; line-height:.85; letter-spacing:-.02em; text-transform:uppercase; text-align:center;
  color:#3e0101; font-weight:900; padding:0 40px;
}
.st-card__panel{ position:absolute; left:5px; top:143px; width:265px; height:134px; background:#000; }
.st-card__slash, .st-card__deco, .st-card__dots{ position:absolute; inset:0; width:100%; height:100%; display:block; }
/* the panel's corner squares blink, as the loader's ticks (2 a second) */
.st-card__dots g{ animation:nf-blink .5s steps(1, end) infinite; }
@keyframes nf-blink{ 0%{ opacity:1; } 50%{ opacity:.15; } }
.st-card__list{ position:absolute; left:5px; right:5px; top:345px; margin:0; padding:0; list-style:none; }
.st-card__list li{ display:flex; justify-content:space-between; font-size:12px; line-height:.85; text-transform:uppercase; height:15px; }

.st-beam{ left:calc(50% - 245px); top:-3px; width:483px; height:calc(100% - 57px); background:url('../assets/svg/slash-beam.svg') center/100% 100% no-repeat; will-change:filter, opacity; }

/* the finale */
.st-fin{ left:0; top:0; width:100%; height:100%; }
.st-fin > *{ position:absolute; }
.st-fin a{ pointer-events:auto; color:inherit; text-decoration:none; }
/* page links: the header's hover (dim), plus a rule sliding in under the word */

/* the cursor trail — in screen space, not on the stage */
.st-trail{ position:absolute; inset:0; }
.st-trail img{ position:absolute; left:0; top:0; width:80px; height:auto; transform-origin:0 0; will-change:transform; }


/* ==========================================================================
   PHONE — Figma "Mobile" (12153:18179), 375 wide.
     12156:19063  the page: hero (0-812), skull HUD (812-1624), the type
                  behind the mask (1624-2436), the skeleton card (2436-3248)
     12271:16338  the red section       12277:17055  the footer
   The hero is two screens that slide (--step 0..1, set by js/scene3d.js).
   Y in px from the nearest edge of an 812 screen, as on desktop.
   ========================================================================== */
@media (max-width:720px){

  /* ---- nav: 125 logo, MENU on the right --------------------------------- */
  .hero{ min-height:0; --step:0; }
  .nav__logo{ width:125px; height:25px; }
  .nav__links{ display:none; }
  .nav__menu{
    display:block; position:absolute; right:0; top:0; z-index:30;
    padding:13px 5px 8px 20px; margin:0; border:0; background:none; cursor:pointer;
  }
  .nav__menu .t{ position:static; display:block; }
  .hero .nav__logo, .hero .nav__menu{ opacity:calc(1 - var(--step)); transition:opacity .3s; }
  html.is-deep .hero .nav__logo, html.is-deep .hero .nav__menu{ opacity:1; }
  html.is-finale .hero .nav__logo, html.is-finale .hero .nav__menu, html.is-finale .hero .cta{ opacity:var(--nav-o, 1); pointer-events:none; }

  .mmenu:not([hidden]){ display:block; position:fixed; inset:0; z-index:29; background:#000; }
  .mmenu ul{ position:absolute; left:5px; right:5px; top:35px; margin:0; padding:0; list-style:none; }
  .mmenu li{ display:flex; justify-content:space-between; height:20px; }
  .mmenu .t{ position:static; }

  /* ---- screen 1: viewer 5,35 365 x (812-35-199), headline 164, CTA -------- */
  .col{ left:var(--gut); right:var(--gut); top:35px; bottom:5px; }
  .viewer{ order:1; margin-top:0; }
  .title{ order:2; margin-top:5px; aspect-ratio:365 / 164; }
  .cta{ order:3; margin-top:5px; }
  .viewer, .title{ transform:translateY(calc(var(--step) * -100dvh)); }
  html.page-scroll .hero .viewer, html.page-scroll .hero .title{ transform:translate3d(0, calc(var(--step) * -100dvh), 0); }

  .tick--tl{ display:none; }
  .thumbs{ top:var(--gut); }
  .scan{ left:5px; top:5px; transform:none; }
  .v-lbl--1{ left:calc(100% - 247px); top:93px; }
  .v-lbl--2{ left:calc(100% - 247px); top:105px; }
  .v-lbl--range{ left:5px; top:calc(100% - 15px); }
  .v-lbl--mid{ right:51px; top:calc(100% - 15px); }
  .v-lbl--max{ right:5px; top:calc(100% - 15px); }

  /* the headline on the 365 column: 66.3px type (line pitch 56 = 98 x
     66.3/116), lines at 0 / 55 / 111, SYSTEM at 209; slash 332,57 32x51;
     circle 152,113 51; icons 16px at 0 / 20, y 112 */
  .title__type, .title__tex{ font-size:18.16cqw; letter-spacing:-.034em; }   /* the phone outline is set ~1px/letter tighter */
  .title__tex{ width:100%; }
  .ln--1{ padding-top:0;       text-indent:-.47cqw; }
  .ln--2{ padding-top:15.07cqw; text-indent:-.45cqw; }
  .ln--3{ padding-top:30.41cqw; text-indent:56.95cqw; }
  .title__slash{ left:90.96%; top:34.76%; width:8.77%; height:31.1%; }
  .title__dot, .title__dot-tex{ left:41.64%; top:68.9%; width:13.97%; }
  .title__icon{ top:68.3%; width:16px; height:16px; }
  .title__icon--2{ left:20px; }

  /* ---- screen 2: the HUD, full width ------------------------------------ */
  .hud{
    position:absolute; inset:0;
    --R:calc(100% - 5px);
    --C:calc((5px + var(--R)) / 2);   /* re-declared: a var() resolves where it is declared */
    transform:translateY(calc((1 - var(--step)) * 100dvh));
  }
  html.page-scroll .hero .hud{ transform:translate3d(0, calc((1 - var(--step)) * 100dvh - var(--deep, 0) * 100dvh), 0); }
  .brk--tl, .brk--tr{ top:35px; }
  .brk--bl, .brk--br{ top:calc(100% - 47px); }
  .tee--top{ top:78px; }
  .tee--bottom{ top:calc(100% - 39px); }
  .rule--v{ top:87px; height:calc(100% - 126px); }
  .lbl--tl{ left:21px; top:52px; }
  .lbl--tc{ display:block; top:52px; }
  .lbl--tr{ top:52px; }
  .lbl--bl, .lbl--br{ top:calc(100% - 57px); }
  .coord--x{ flex-direction:row-reverse; }

  /* ---- the story stage, 375 wide (js/story.js sets its height) ----------- */
  .st-stage{ width:375px; }

  /* type behind the mask: 86px, wrapped to 365, cap box at 5,35 */
  .st-big{ left:5px; top:35px; width:365px; font-size:86px; white-space:normal; }
  .st-mframe__tee{ top:calc(50% - 9px); }
  .st-mframe__tee--r{ left:auto; right:5px; }
  .st-mframe__tick--tl{ left:5px; top:35px; }
  .st-mframe__tick--tr{ display:block; left:auto; right:5px; top:35px; transform:scaleX(-1); }
  .st-mframe__tick--br{ left:auto; right:5px; }
  .st-mframe__tick--bl{ left:5px; top:calc(100% - 36px); }
  .st-mframe__tick--br{ right:5px; top:calc(100% - 36px); }

  .st-scroll--l{ left:5px; top:calc(100% - 40px); }
  .st-scroll--c{ left:173px; top:calc(100% - 40px); }

  /* skeleton card 45,336 = centred; rings 210 round it, 511 above/below */
  .st-body{ left:calc(50% - 142px); top:calc(50% - 70px); }
  .st-body .st-body__lung{ display:none; }

  /* the red section */
  .st-div{ height:100%; }
  .st-div--l{ left:5px; } .st-div--c{ display:block; left:187px; } .st-div--r{ left:370px; }
  .st-giant{ width:375px; }
  .st-lockup{ left:187.5px; width:136px; height:94.4px; }
  .st-lede{ left:187.5px; }
  .st-labels{ width:100%; height:100%; }
  .st-lab[data-k="layer"]{ left:5px; }
  .st-lab[data-k="status"]{ left:87.5px; }
  .st-lab[data-k="terrain"]{ right:5px; }
  /* two word columns, right-aligned against the dividers (187 / 370) */
  .st-col--1, .st-col--2{ display:none; }
  .st-col--3{ right:auto; left:44px; }
  .st-col--4{ right:auto; left:227px; }
  .st-foot{ display:none; }   /* not in the phone frame: CLASS / STATE hold the bottom */
  .st-card{ left:50px; }
  .st-card__title{ font-size:24px; padding:0 60px; }
  .st-lab b.flip-m{ display:inline-block; transform:scaleX(-1); }
  html.is-red .hero .nav__logo, html.is-red .hero .nav__menu, html.is-red .hero .cta{ opacity:0; pointer-events:none; }
  .st-beam{ left:-77px; top:0; width:354px; height:100%; }

  /* the footer (12277:17055) */
  .st-fin{ width:100%; height:100%; }
}

/* body text, line-by-line masked reveal (js/story.js): each line clips its
   own box; a little room below so descenders are not cut */
.lm{ display:block; overflow:hidden; padding:.2em 0 .15em; margin:-.2em 0 -.15em; }   /* room above and below: tight leading must not cut the caps */
.li{ display:block; }
.lw{ display:inline; }
.st-body__txt .lm, .st-lede .lm{ text-align:center; }

/* THE FILTER's own labels (the 3D build's: legend, tags, intake / outflow,
   card): hidden — the site designs its own text for this block */
#fcard, #flegend, #fline, .fport, .ftag{ display:none !important; }

/* THE WASH (js/story.js): the way from the filter to the skeleton — light,
   then dissolving away. Over the 3D, under the page. */
.st-wash{ position:fixed; inset:0; width:100vw; height:100vh; z-index:0; pointer-events:none; display:none; image-rendering:auto; }

/* THE PRELOADER'S SKULL (js/preskull.js): the real skull, light, drawn where the big 3D
   will draw it, until the big one takes over (js/intro.js) */
.pre-skull{ position:fixed; inset:0; width:100%; height:100%; z-index:2; pointer-events:none; display:none;
  image-rendering:pixelated; filter:drop-shadow(0 0 22px rgba(255,244,214,.3)); }   /* in blocks; the big one's glow */
html.intro-pending .pre-skull{ display:block; }
@media (max-width:720px){ html.intro-pending .pre-skull{ display:none; } }

/* THE FILTER'S NAMES at the cursor (js/ftip.js) */
.ftip{ position:fixed; left:0; top:0; z-index:70; pointer-events:none;
  /* the "Get in touch" button's height and type (its .t), 10px each side */
  height:20px; padding:0 10px; display:flex; align-items:center; justify-content:center;
  background:var(--c-ink); color:var(--c-ground); white-space:nowrap;
  opacity:0; transition:opacity .15s ease; will-change:transform; }
.ftip .t{ position:static; color:inherit; }
.ftip.on{ opacity:1; }

/* INTO THE FILTER (js/story.js INTO): the dark we pass through, over the 3D, under the page */
.st-into{ position:fixed; inset:0; z-index:0; background:#000; opacity:0; pointer-events:none; }

/* THE FILTER'S LEGEND (js/story.js, Figma 15947:606): four tiles, 10 from the edges
   and apart, 26 tall; a 4px bar of the tile's colour, its diamond, the two labels;
   stripes drifting slowly left to right behind */
.st-legend{ position:fixed; left:10px; right:10px; bottom:10px; height:26px; z-index:6; display:flex; gap:10px; pointer-events:none; visibility:hidden; opacity:0; }
.st-legend__t{ position:relative; flex:1 1 0; overflow:hidden; background:none; display:flex; align-items:center; color:var(--c-ink); border-left:4px solid var(--lc); }
.st-legend__t::before{ content:''; position:absolute; top:0; bottom:0; left:-6.6px; right:0;
  /* no ground of its own: only the grey stripes, the 3D seen between them */
  left:-12.13px; background:repeating-linear-gradient(112.587deg, transparent 0 3.6px, rgba(253,244,223,.16) 3.6px 5.2px, transparent 5.2px 5.6px);
  animation:legend-stripes 1.8s linear infinite; will-change:transform; backface-visibility:hidden; }
/* two periods per loop (12.13px along x): a steady drift, left to right */
@keyframes legend-stripes{ from{ transform:translate3d(0, 0, 0); } to{ transform:translate3d(12.13px, 0, 0); } }
@media (prefers-reduced-motion: reduce){ .st-legend__t::before{ animation:none; } }
.st-legend__d{ position:relative; width:20px; height:20px; margin-left:10px; flex:none; display:grid; place-items:center; }
.st-legend__d::after{ content:''; width:10px; height:10px; background:var(--lc); transform:rotate(45deg); }
.st-legend .t{ position:relative; margin-left:5px; }
.st-legend__r{ margin-left:auto !important; margin-right:10px; }
@media (max-width:720px){ .st-legend{ display:none; } }

/* the mask flying over the hero's content (js/story.js gl-over): the 3D above the page, seen through where it is dark */
html.has-3d.gl-over #gl{ z-index:3; pointer-events:none; }

/* THE SKELETON'S VIGNETTE (js/story.js): over the 3D in its poses, under the page —
   the sides dark, a cool blue gradient from the top left and a warm red one from
   the bottom right, the middle clear */
.st-skelvig{ position:fixed; inset:0; z-index:0; pointer-events:none; opacity:0;
  /* the dark wins (Figma): only the middle is lit, everything round it falls off
     softly to near black; blue on the left, warm red at the bottom right */
  background:
    radial-gradient(ellipse 48% 62% at 52% 48%, transparent 0%, rgba(0,0,0,.18) 30%, rgba(0,0,0,.62) 62%, rgba(0,0,0,.9) 85%, #000 100%),
    radial-gradient(ellipse 60% 90% at 0% 30%, rgba(14,48,92,.85) 0%, rgba(14,48,92,.4) 45%, transparent 80%),
    radial-gradient(ellipse 60% 80% at 100% 95%, rgba(140,48,14,.8) 0%, rgba(140,48,14,.35) 45%, transparent 80%); }

/* THE HERO'S RED (screenshot 2026-11-06): the About page's gradient, brighter at the
   top, under the 3D (which sees through where it is dark there), with the site's
   concrete multiplied in; it goes as the hero scrolls away */
.hero-grad{ position:fixed; inset:0; z-index:-1; pointer-events:none;
  background-image:var(--tex-img, url('../assets/img/texture.jpg')), linear-gradient(180deg, #b30000 0%, #7a0000 32%, #300000 68%, #000 100%);
  background-size:var(--tex-size, 768px), 100% 100%; background-repeat:repeat, no-repeat;
  background-blend-mode:multiply, normal;
  opacity:calc(1 - min(1, var(--deep, 0) * 1.4)); }
/* not behind the preloader's skull: it comes up with the page (js/intro.js) */
html.intro-pending:not(.grad-in) .hero-grad{ opacity:0; }
