/* =========================================================
   ESG — Engineering Section Group
   Editorial Swiss-grid system (white canvas / black bands)
   ========================================================= */

:root{
  /* colours */
  --pure-white:#ffffff;
  --absolute-black:#000000;
  --hairline:#e5e7eb;
  --stone:#6b7280;
  --ash:#b3b3b3;
  --quartz:#bbbbbb;
  --signal:#2563eb;
  --alert:#b91c1c;

  /* type */
  --font-plain:'Inter','Plain',ui-sans-serif,system-ui,-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,sans-serif;
  --w-light:300; --w-regular:400; --w-medium:500; --w-semibold:600;

  --text-body:16px;         --leading-body:1.56;
  --text-subheading:18px;   --leading-subheading:1.5;
  --text-heading-sm:24px;   --leading-heading-sm:1.25; --tracking-heading-sm:-0.05px;
  --text-heading:30px;      --leading-heading:1.13;    --tracking-heading:-0.75px;
  --text-heading-lg:50px;   --leading-heading-lg:1.08; --tracking-heading-lg:-2px;
  --text-display:100px;     --leading-display:1;       --tracking-display:-5px;

  /* spacing */
  --s4:4px; --s8:8px; --s12:12px; --s16:16px; --s20:20px; --s24:24px;
  --s32:32px; --s36:36px; --s40:40px; --s48:48px; --s100:100px;

  /* layout */
  --page-max:1440px;
  --gut:clamp(20px,4vw,48px);
  --section-gap:clamp(48px,7vw,80px);

  /* radii — the system is square except inputs */
  --radius-input:2px;
}

*{box-sizing:border-box;margin:0;padding:0}
html{-webkit-text-size-adjust:100%;scroll-behavior:smooth}
/* the bar is fixed, so anchor targets must clear it or they land underneath */
[id]{scroll-margin-top:calc(var(--nav-h,82px) + 28px)}
body{
  background:var(--pure-white);
  color:var(--absolute-black);
  font-family:var(--font-plain);
  font-size:var(--text-body);
  line-height:var(--leading-body);
  font-weight:var(--w-regular);
  -webkit-font-smoothing:antialiased;
  overflow-x:hidden;
}
img,video{display:block;max-width:100%;border-radius:0}
button{font:inherit;color:inherit;background:none;border:0;cursor:pointer}
::selection{background:var(--absolute-black);color:var(--pure-white)}

/* ---------- type ---------- */
.display{
  font-size:clamp(40px,8.2vw,var(--text-display));
  line-height:var(--leading-display);
  letter-spacing:-0.05em;
  font-weight:var(--w-light);
}
.heading-lg{
  font-size:clamp(30px,4.6vw,var(--text-heading-lg));
  line-height:var(--leading-heading-lg);
  letter-spacing:-0.04em;
  font-weight:var(--w-light);
}
.heading{
  font-size:clamp(23px,2.6vw,var(--text-heading));
  line-height:var(--leading-heading);
  letter-spacing:-0.025em;
  font-weight:var(--w-regular);
}
.heading-sm{
  font-size:var(--text-heading-sm);
  line-height:var(--leading-heading-sm);
  letter-spacing:-0.002em;
  font-weight:var(--w-regular);
}
.subheading{font-size:var(--text-subheading);line-height:var(--leading-subheading);font-weight:var(--w-regular)}
.kicker{font-size:var(--text-subheading);line-height:1.3;font-weight:var(--w-medium);color:var(--absolute-black)}
.muted{color:var(--ash)}
.stone{color:var(--stone)}
.measure{max-width:800px}
.measure-sm{max-width:62ch}

/* ---------- layout ---------- */
.wrap{max-width:var(--page-max);margin:0 auto;padding-inline:var(--gut)}
.sec{padding-block:var(--section-gap)}
.sec-tight{padding-block:clamp(32px,4vw,48px)}
.rule{height:1px;background:var(--hairline);border:0;width:100%}
.grid{display:grid;gap:var(--s32)}
.g2{grid-template-columns:repeat(2,1fr)}
.g3{grid-template-columns:repeat(3,1fr)}
.g4{grid-template-columns:repeat(4,1fr)}
@media(max-width:900px){.g3,.g4{grid-template-columns:repeat(2,1fr)}}
@media(max-width:600px){.g2,.g3,.g4{grid-template-columns:1fr}}

/* ---------- inverted band — the only elevation ---------- */
.band{background:var(--absolute-black);color:var(--pure-white)}
.band .muted{color:var(--ash)}
.band .rule{background:rgba(255,255,255,.22)}
.band .link{color:var(--pure-white);border-bottom-color:var(--pure-white)}
.band .link:hover{color:var(--signal);border-bottom-color:var(--signal)}
.band .kicker{color:var(--pure-white)}

/* =========================================================
   Navigation — three-zone bar + mega panel
   ========================================================= */
.nav{
  position:fixed;top:0;left:0;right:0;z-index:60;
  background:transparent;border-bottom:1px solid transparent;
  transform:translateY(0);opacity:1;
  transition:transform .55s cubic-bezier(.22,1,.36,1),
             opacity .32s ease,
             background .4s ease,
             border-color .4s ease,
             backdrop-filter .4s ease;
  will-change:transform;
}
/* hidden when scrolling down, restored on any upward scroll */
.nav.is-hidden{
  transform:translateY(-101%);opacity:0;
  transition:transform .42s cubic-bezier(.55,0,.35,1),opacity .22s ease;
}

/* a ground once away from the top — but never while it floats over the hero,
   where it carries white type on the footage instead */
.nav.scrolled:not(.over-hero){
  background:rgba(255,255,255,.94);
  backdrop-filter:blur(14px) saturate(1.4);
  -webkit-backdrop-filter:blur(14px) saturate(1.4);
  border-bottom-color:var(--hairline);
}
/* the bar takes a white ground only while the panel is open, so the two read
   as one surface instead of a white sheet under a transparent strip */
.nav.mega-open{background:var(--pure-white);border-bottom-color:var(--hairline)}
/* Full-bleed: the wordmark sits hard left and the utilities hard right, with
   only a small edge margin — no 1440px cap and no page gutter. */
.nav > .inner{
  max-width:none;margin:0;padding:var(--s20) clamp(16px,2vw,32px);
  display:grid;grid-template-columns:1fr auto 1fr;align-items:center;gap:var(--s24);
}
.wordmark{
  font-size:var(--text-heading-sm);font-weight:var(--w-regular);
  letter-spacing:-0.02em;color:var(--absolute-black);text-decoration:none;justify-self:start;
}

/* centre links — no underlines, plain black */
.primary{list-style:none;display:flex;gap:var(--s32);align-items:center;justify-self:center}
.primary > li > a,.primary > li > button{
  font-size:var(--text-body);font-weight:var(--w-regular);color:var(--absolute-black);
  text-decoration:none;padding-block:var(--s8);border:0;background:none;cursor:pointer;
  transition:color .2s;
}
.primary > li > a:hover,.primary > li > button:hover,
.primary > li > a[aria-current="page"]{color:var(--signal)}

/* right utilities */
.utils{display:flex;align-items:center;gap:var(--s20);justify-self:end}
.utils a{font-size:var(--text-body);color:var(--absolute-black);text-decoration:none;transition:color .2s}
.utils a:hover{color:var(--signal)}
.utils .ico{display:flex;align-items:center;color:var(--absolute-black);transition:color .2s}
.utils .ico:hover{color:var(--signal)}
.utils .ico svg{width:16px;height:16px;stroke:currentColor;fill:none;stroke-width:1.4}
.utils .lang{display:flex;align-items:center;gap:var(--s4);font-size:var(--text-body)}
.utils .lang svg{width:9px;height:9px;stroke:currentColor;fill:none;stroke-width:1.6}

/* ---------- mega panel ---------- */
/* Overlays the page instead of pushing it: absolutely positioned under the bar,
   so opening it never shifts a single pixel of content. */
.mega{
  position:absolute;top:100%;left:0;right:0;
  background:var(--pure-white);border-top:1px solid var(--hairline);
  opacity:0;visibility:hidden;transform:translateY(-10px);
  transition:opacity .32s ease,transform .45s cubic-bezier(.16,1,.3,1),visibility 0s linear .45s;
}
.mega.open{
  opacity:1;visibility:visible;transform:translateY(0);
  transition:opacity .32s ease,transform .45s cubic-bezier(.16,1,.3,1),visibility 0s linear 0s;
}

/* contents stagger in behind the panel */
.mega .cat,.mega .sub li,.mega .promo > *{
  opacity:0;transform:translateY(12px);
  transition:opacity .45s ease,transform .55s cubic-bezier(.16,1,.3,1);
}
.mega.open .cat,.mega.open .sub li,.mega.open .promo > *{opacity:1;transform:none}
.mega.open .cat:nth-child(1){transition-delay:.05s}
.mega.open .cat:nth-child(2){transition-delay:.10s}
.mega.open .cat:nth-child(3){transition-delay:.15s}
.mega.open .cat:nth-child(4){transition-delay:.20s}
.mega.open .sub li:nth-child(1){transition-delay:.22s}
.mega.open .sub li:nth-child(2){transition-delay:.27s}
.mega.open .sub li:nth-child(3){transition-delay:.32s}
.mega.open .promo > *:nth-child(1){transition-delay:.26s}
.mega.open .promo > *:nth-child(2){transition-delay:.31s}
.mega.open .promo > *:nth-child(3){transition-delay:.36s}
.mega.open .promo > *:nth-child(4){transition-delay:.41s}
@media(prefers-reduced-motion:reduce){
  .nav,.mega,.mega .cat,.mega .sub li,.mega .promo > *{transition:none}
}
.mega > .inner{display:block;max-width:none;margin:0;padding:var(--s32) clamp(16px,2vw,32px) var(--s48)}

/* the category row — display type divided by full-height hairlines.
   Four display-size words need ~1500px to sit on one line, so below that they
   fold into a hairline grid rather than overflowing the viewport. */
.cats{display:flex;align-items:stretch;border-bottom:1px solid var(--hairline)}
.cat{
  position:relative;display:flex;align-items:flex-start;gap:var(--s8);
  padding-block:0 var(--s16);padding-inline:0 var(--s32);text-decoration:none;
  color:var(--ash);transition:color .2s;min-width:0;
}
.cat + .cat{border-inline-start:1px solid var(--hairline);padding-inline-start:var(--s32)}
.cat span{
  font-size:clamp(26px,3.4vw,72px);line-height:1;letter-spacing:-0.045em;
  font-weight:var(--w-light);white-space:nowrap;
}
.cat sup{font-size:var(--text-body);font-weight:var(--w-regular);color:var(--ash);line-height:1}
.cat:hover,.cat.is-active{color:var(--absolute-black)}
.cat.is-active sup{color:var(--ash)}

@media(max-width:1560px){
  .cats{display:grid;grid-template-columns:repeat(2,1fr);border-bottom:0}
  .cat{
    padding-block:var(--s16) var(--s20);padding-inline:0 var(--s24);
    border-bottom:1px solid var(--hairline);align-items:baseline;
  }
  .cat + .cat{border-inline-start:0;padding-inline-start:0}
  .cat:nth-child(odd){border-inline-end:1px solid var(--hairline);padding-inline-end:var(--s32)}
  .cat:nth-child(even){padding-inline-start:var(--s32)}
}
@media(max-width:620px){
  .cats{grid-template-columns:1fr}
  .cat:nth-child(odd){border-inline-end:0;padding-inline-end:0}
  .cat:nth-child(even){padding-inline-start:0}
}

/* body of the panel */
.megabody{display:grid;grid-template-columns:1fr 1fr;gap:var(--s48);padding-top:var(--s24)}
.sub{list-style:none;display:flex;flex-direction:column;gap:var(--s8)}
.sub a{
  font-size:var(--text-subheading);color:var(--absolute-black);text-decoration:none;
  transition:color .2s;
}
.sub a:hover{color:var(--signal)}
.promo p{font-size:var(--text-body);color:var(--ash)}
.promo .lead{color:var(--absolute-black);font-weight:var(--w-medium)}
.promo .new{color:var(--ash);font-weight:var(--w-regular);margin-inline-start:var(--s8)}
.promo .startnow{
  display:inline-block;margin-top:var(--s32);
  background:#f5f5f5;color:var(--absolute-black);text-decoration:none;
  padding:var(--s16) var(--s48);border-radius:0;font-size:var(--text-body);
  transition:background .2s,color .2s;
}
.promo .startnow:hover{background:var(--absolute-black);color:var(--pure-white)}

/* declared before the breakpoint that shows it — when this sat after the media
   query it won on source order and the mobile menu button was never visible */
.navtoggle{display:none;font-size:var(--text-body);font-weight:var(--w-medium);
  border-bottom:1px solid var(--absolute-black);padding-bottom:2px}

@media(max-width:1080px){
  /* The language switcher stays. On a bilingual site it is navigation, not a
     utility — hiding it below 1080px left no way to reach the other language
     between 821px and 1080px, because the mobile menu had not appeared yet. */
  .utils a:not(.keep){display:none}
}
@media(max-width:900px){
  .megabody{grid-template-columns:1fr;gap:var(--s32)}
  .cats{flex-wrap:wrap}
  .cat + .cat{border-inline-start:0;padding-inline-start:0}
}
@media(max-width:820px){
  .nav > .inner{grid-template-columns:1fr auto auto;gap:var(--s16)}
  .navtoggle{display:block;justify-self:end}
  /* Careers and the language switcher survive into the mobile bar; neither
     appears anywhere else in the collapsed nav. */
  .utils{display:flex;align-items:center;gap:var(--s16);justify-self:end}
  .utils > *:not(.keep):not(.lang){display:none}
      .primary{
    position:fixed;inset:var(--nav-h,69px) 0 auto 0;background:var(--pure-white);
    flex-direction:column;align-items:stretch;gap:0;
    border-bottom:1px solid var(--hairline);
    max-height:0;overflow:hidden;transition:max-height .4s ease;justify-self:stretch;
  }
  .primary > li{border-top:1px solid var(--hairline)}
  .primary > li > a,.primary > li > button{
    display:block;width:100%;text-align:start;padding:var(--s16) var(--gut);
    font-size:var(--text-subheading);
  }
  body.menu-open .primary{max-height:70vh}
  .mega > .inner{padding-block:var(--s24) var(--s32)}
}

/* The bar is fixed, so it no longer occupies flow space. The hero deliberately
   runs full-bleed beneath it; every other page needs clearance on its first band.
   `:first-child` alone was too literal: projects.html opens with the cursor-preview
   div and the six article pages open with <article>, so on seven pages this matched
   nothing and the first kicker sat 37px underneath the bar. The second selector
   covers a first child that is an invisible utility (preview / cursor) — it cannot
   match index.html, whose hero is .hero and not .sec, so the hero stays full-bleed. */
main > .sec:first-child,
main > :first-child:not(.sec) + .sec,
main > article > .sec:first-child{padding-top:calc(var(--nav-h,69px) + var(--s48))}

/* ---------- links: the system is buttonless ---------- */
.link{
  display:inline-block;
  font-size:var(--text-body);font-weight:var(--w-medium);
  color:var(--absolute-black);text-decoration:none;
  border-bottom:1px solid var(--absolute-black);
  padding-bottom:2px;
  transition:color .2s,border-color .2s;
}
.link:hover{color:var(--signal);border-bottom-color:var(--signal)}
.link-inline{
  color:var(--absolute-black);text-decoration:none;
  border-bottom:1px solid var(--hairline);
  transition:color .2s,border-color .2s;
}
.link-inline:hover{color:var(--signal);border-bottom-color:var(--signal)}

/* ---------- full-bleed plate ---------- */
.plate{width:100%;display:block}
.plate img,.plate video{width:100%;height:auto;display:block}
.plate-tall{height:clamp(320px,62vw,760px)}
.plate-tall img,.plate-tall video{width:100%;height:100%;object-fit:cover}

/* ---------- hero: scroll-scrubbed build ---------- */
/* 850vh of travel for 10s of footage — roughly double the previous weight, so
   the build advances slowly and deliberately under the scroll. */
.hero{height:850vh;position:relative}
.hero .sticky{position:sticky;top:0;height:100vh;overflow:hidden;background:#000}
.hero .frame{position:absolute;inset:0;overflow:hidden}
.hero .frame video,.hero .frame img.poster{
  position:absolute;inset:0;width:100%;height:100%;object-fit:cover;
}
.hero .frame img.poster{transition:opacity .4s}
.hero.ready .frame img.poster{opacity:0}

/* gradient veil so white type holds against any frame of the footage */
.hero .veil{
  position:absolute;inset:0;pointer-events:none;
  background:
    linear-gradient(to bottom,rgba(0,0,0,.55) 0%,rgba(0,0,0,.10) 26%,
                    rgba(0,0,0,.05) 52%,rgba(0,0,0,.72) 100%);
}

/* ---------- motion-graphic HUD ---------- */
.hud{position:absolute;inset:0;color:#fff;pointer-events:none}

.hud .meta{
  position:absolute;inset-inline-start:var(--gut);top:calc(var(--nav-h,69px) + 40px);
  display:flex;align-items:baseline;gap:var(--s16);
  font-size:var(--text-body);letter-spacing:.28em;text-transform:uppercase;
  opacity:.85;
}
.hud .meta .idx{font-variant-numeric:tabular-nums}

/* the big word — replaced per phase, animated in on change */
.hud .stage{
  position:absolute;inset-inline:var(--gut);
  top:50%;transform:translateY(-50%);
}
.hud .word{
  display:block;overflow:hidden;
}
.hud .word b{
  display:block;font-weight:var(--w-light);color:#fff;
  font-size:clamp(52px,13vw,190px);line-height:.92;letter-spacing:-0.055em;
  text-transform:uppercase;
  will-change:transform,opacity;
}
.hud .word.anim b{animation:wordIn .85s cubic-bezier(.16,1,.3,1) both}
@keyframes wordIn{
  0%{transform:translateY(108%) skewY(4deg);opacity:0}
  100%{transform:translateY(0) skewY(0);opacity:1}
}
.hud .sub{
  margin-top:var(--s16);overflow:hidden;
}
.hud .sub b{
  display:block;font-weight:var(--w-regular);font-size:clamp(14px,1.5vw,20px);
  letter-spacing:.2em;text-transform:uppercase;opacity:.8;
}
.hud .sub.anim b{animation:wordIn .9s .08s cubic-bezier(.16,1,.3,1) both}

/* phase ticks + progress rule along the bottom */
.hud .rail{
  position:absolute;inset-inline:var(--gut);bottom:56px;
  display:flex;gap:6px;
}
.hud .rail i{
  flex:1;height:2px;background:rgba(255,255,255,.28);position:relative;overflow:hidden;
}
.hud .rail i::after{
  content:"";position:absolute;inset:0;background:#fff;
  transform:scaleX(0);transform-origin:left;transition:transform .5s cubic-bezier(.16,1,.3,1);
}
.hud .rail i.done::after{transform:scaleX(1)}
.hud .rail i.active::after{transform:scaleX(var(--fill,0))}

.hud .cue{
  position:absolute;left:50%;transform:translateX(-50%);bottom:24px;
  font-size:11px;letter-spacing:.34em;text-transform:uppercase;opacity:.7;
  transition:opacity .4s;
}

/* nav turns white while it floats over the hero footage */
.nav.over-hero .wordmark,
.nav.over-hero .primary > li > a,
.nav.over-hero .primary > li > button,
.nav.over-hero .utils a,
.nav.over-hero .utils .ico,
.nav.over-hero .utils .lang,
.nav.over-hero .navtoggle{color:#fff}
.nav.over-hero .navtoggle{border-bottom-color:#fff}
.nav.over-hero.mega-open .wordmark,
.nav.over-hero.mega-open .primary > li > a,
.nav.over-hero.mega-open .primary > li > button,
.nav.over-hero.mega-open .utils a,
.nav.over-hero.mega-open .utils .ico,
.nav.over-hero.mega-open .utils .lang{color:var(--absolute-black)}

/* =========================================================
   Intro — book riffle
   A stack of pages hinged on their left edge. Each flips through
   180° in a staggered cascade, so the whole stack riffles past like
   a thumbed book, revealing the site beneath. Real 3D rotation on a
   shared perspective, not a cross-fade.
   ========================================================= */
/* No background of its own: the front leaf is opaque, and as each leaf turns
   away it reveals the live page beneath rather than a black ground. That black
   ground was the flash before the hero appeared. */
#intro{
  position:fixed;inset:0;z-index:200;background:transparent;
  perspective:3000px;perspective-origin:32% 50%;
}
#intro .book{position:absolute;inset:0;transform-style:preserve-3d}
#intro .page{
  position:absolute;inset:0;overflow:hidden;
  transform-origin:left center;backface-visibility:hidden;
  transform:rotateY(0deg);will-change:transform;background:#000;
}
#intro .page img{width:100%;height:100%;object-fit:cover;opacity:.82}
/* pages that land on the white canvas finish on a white leaf */
#intro .page.final-light{background:var(--pure-white)}
/* edge shading so the leaf reads as a turning surface, not a flat plane */
#intro .page::after{
  content:"";position:absolute;inset:0;pointer-events:none;opacity:0;
  background:linear-gradient(90deg,rgba(0,0,0,.75) 0%,rgba(0,0,0,.15) 42%,rgba(0,0,0,0) 70%);
}
body.riffle #intro .page{animation:riffle 0.95s cubic-bezier(.50,0,.22,1) forwards}
body.riffle #intro .page::after{animation:leafShade 0.95s linear forwards}
@keyframes riffle{
  0%{transform:rotateY(0deg)}
  100%{transform:rotateY(-170deg)}
}
@keyframes leafShade{0%{opacity:0}55%{opacity:1}100%{opacity:1}}

body.riffle #intro .page:nth-child(1),
body.riffle #intro .page:nth-child(1)::after{animation-delay:0.000s}
#intro .page:nth-child(1){z-index:8}
body.riffle #intro .page:nth-child(2),
body.riffle #intro .page:nth-child(2)::after{animation-delay:0.110s}
#intro .page:nth-child(2){z-index:7}
body.riffle #intro .page:nth-child(3),
body.riffle #intro .page:nth-child(3)::after{animation-delay:0.220s}
#intro .page:nth-child(3){z-index:6}
body.riffle #intro .page:nth-child(4),
body.riffle #intro .page:nth-child(4)::after{animation-delay:0.330s}
#intro .page:nth-child(4){z-index:5}
body.riffle #intro .page:nth-child(5),
body.riffle #intro .page:nth-child(5)::after{animation-delay:0.440s}
#intro .page:nth-child(5){z-index:4}
body.riffle #intro .page:nth-child(6),
body.riffle #intro .page:nth-child(6)::after{animation-delay:0.550s}
#intro .page:nth-child(6){z-index:3}
body.riffle #intro .page:nth-child(7),
body.riffle #intro .page:nth-child(7)::after{animation-delay:0.660s}
#intro .page:nth-child(7){z-index:2}
body.riffle #intro .page:nth-child(8),
body.riffle #intro .page:nth-child(8)::after{animation-delay:0.770s}
#intro .page:nth-child(8){z-index:1}

/* title sits on the front leaf */
#intro .title{
  position:absolute;inset:0;display:flex;flex-direction:column;
  align-items:center;justify-content:center;gap:var(--s16);color:#fff;text-align:center;
}
#intro .title .line{overflow:hidden;display:block}
#intro .title .line b{
  display:block;font-weight:var(--w-light);
  font-size:clamp(58px,14vw,230px);line-height:.92;letter-spacing:-0.06em;
  text-transform:uppercase;
  transform:translateY(112%);
  transition:transform 1.45s cubic-bezier(.16,1,.3,1);
}
#intro .title .sub{overflow:hidden;display:block}
#intro .title .sub b{
  display:block;font-weight:var(--w-regular);
  font-size:clamp(12px,1.5vw,18px);letter-spacing:.46em;text-transform:uppercase;opacity:.75;
  transform:translateY(130%);
  transition:transform 1.35s cubic-bezier(.16,1,.3,1) .22s;
}
body.intro-in #intro .title .line b,
body.intro-in #intro .title .sub b{transform:translateY(0)}
body.intro-out #intro .title .line b,
body.intro-out #intro .title .sub b{
  transform:translateY(-125%);
  transition:transform .85s cubic-bezier(.7,0,.24,1);
}
body.intro-done #intro{display:none}
body.intro-active{overflow:hidden}
body.intro-lock main,body.intro-lock header.nav{opacity:0}
/* Instant, not faded: the leaves are still covering the page at this point, so
   it must be fully painted the moment the first one turns away.
   NOTE: scoped to <main> only. `riffle` stays on the body for the rest of the
   session, so including header.nav here permanently killed the bar's own
   transitions and the hide/show animation stopped working. The bar simply
   returns to opacity 1 when intro-lock is dropped, using its own transition. */
body.riffle main{opacity:1;transition:none}

@media(prefers-reduced-motion:reduce){
  #intro{display:none}
  .hud .word.anim b,.hud .sub.anim b{animation:none}
  #intro .panels i,#intro .title .line b{transition:none}
}


/* =========================================================
   Hero cursor — a dot that becomes a down-arrow when idle
   Position is driven by transform (compositor-friendly); the ring handles
   size and the bob, so the two never fight over the same property.
   ========================================================= */
.cursor{
  position:fixed;top:0;left:0;z-index:120;pointer-events:none;
  opacity:0;transition:opacity .25s ease;
  will-change:transform;
}
.cursor.on{opacity:1}
.cursor .ring{
  display:grid;place-items:center;
  width:9px;height:9px;margin:-4.5px 0 0 -4.5px;
  border-radius:50%;background:#fff;border:1px solid transparent;
  transition:width .45s cubic-bezier(.16,1,.3,1),height .45s cubic-bezier(.16,1,.3,1),
             margin .45s cubic-bezier(.16,1,.3,1),background .35s ease,border-color .35s ease;
}
.cursor .ring svg{
  width:18px;height:18px;stroke:#fff;fill:none;stroke-width:1.4;
  stroke-linecap:round;stroke-linejoin:round;
  opacity:0;transform:translateY(-5px);
  transition:opacity .3s ease,transform .4s cubic-bezier(.16,1,.3,1);
}
.cursor.hint .ring{
  width:62px;height:62px;margin:-31px 0 0 -31px;
  background:transparent;border-color:rgba(255,255,255,.9);
  animation:cursorBob 1.7s ease-in-out infinite;
}
.cursor.hint .ring svg{opacity:1;transform:translateY(0)}
@keyframes cursorBob{
  0%,100%{transform:translateY(0)}
  50%{transform:translateY(9px)}
}
/* native pointer hidden only while the custom one is live */
body.cursor-live .hero .sticky,
body.cursor-live .hero .sticky *{cursor:none}

@media(prefers-reduced-motion:reduce){
  .cursor.hint .ring{animation:none}
}
@media(hover:none),(pointer:coarse){
  .cursor{display:none}
  body.cursor-live .hero .sticky,
  body.cursor-live .hero .sticky *{cursor:auto}
}


/* =========================================================
   Project preview — an image that rides the cursor over the
   sector rows. Position via transform so the follow stays on the
   compositor; the inner figure owns the reveal.
   ========================================================= */
.preview{
  position:fixed;top:0;left:0;z-index:110;pointer-events:none;
  will-change:transform;
}
.preview .shot{
  width:clamp(220px,22vw,340px);aspect-ratio:4/3;overflow:hidden;
  margin:-50% 0 0 24px;background:var(--hairline);
  opacity:0;transform:scale(.94);
  transition:opacity .32s ease,transform .5s cubic-bezier(.16,1,.3,1);
}
.preview.on .shot{opacity:1;transform:scale(1)}
.preview .shot img{
  width:100%;height:100%;object-fit:cover;display:block;
  opacity:0;transition:opacity .28s ease;
}
.preview .shot img.ready{opacity:1}

/* the rows that drive it */
.row[data-preview]{transition:color .3s ease}
.row[data-preview] h3{transition:opacity .3s ease}
body.previewing .row[data-preview] h3{opacity:.35}
body.previewing .row[data-preview]:hover h3{opacity:1}

@media(hover:none),(pointer:coarse){.preview{display:none}}
@media(prefers-reduced-motion:reduce){
  .preview .shot{transition:opacity .2s ease}
  .preview.on .shot{transform:none}
}


/* =========================================================
   Accessibility
   ========================================================= */
/* Hidden up the block axis, not out along the inline one: `left:-9999px`
   creates a 9999px horizontal scroll region once the document is RTL. */
.skip{
  position:absolute;inset-inline-start:var(--s16);top:0;z-index:300;
  transform:translateY(-250%);
  background:var(--absolute-black);color:var(--pure-white);
  padding:var(--s12) var(--s20);font-size:var(--text-body);
}
.skip:focus{transform:none;top:var(--s16)}

/* a visible focus ring everywhere — the system had none at all */
:focus-visible{
  outline:2px solid var(--signal);
  outline-offset:3px;
  border-radius:2px;
}
.band :focus-visible,.hero :focus-visible{outline-color:#fff}
/* never strip focus from keyboard users just because the mouse is in use */
:focus:not(:focus-visible){outline:none}

/* screen-reader-only text */
.sr{
  position:absolute;width:1px;height:1px;padding:0;margin:-1px;
  overflow:hidden;clip:rect(0 0 0 0);white-space:nowrap;border:0;
}


/* =========================================================
   Gallery + lightbox
   ========================================================= */
.gallery{display:grid;grid-template-columns:repeat(3,1fr);gap:var(--s16)}
@media(max-width:900px){.gallery{grid-template-columns:repeat(2,1fr)}}
@media(max-width:560px){.gallery{grid-template-columns:1fr}}
.gallery .shot{
  position:relative;display:block;padding:0;border:0;background:none;
  aspect-ratio:4/3;overflow:hidden;cursor:pointer;text-align:start;
}
.gallery .shot img{
  width:100%;height:100%;object-fit:cover;
  transition:transform .9s cubic-bezier(.16,1,.3,1),filter .5s ease;
  filter:grayscale(1) brightness(.92);
}
.gallery .shot:hover img,.gallery .shot:focus-visible img{transform:scale(1.04);filter:none}
.gallery .shot .cap{
  position:absolute;inset-inline-start:var(--s12);bottom:var(--s12);
  background:var(--pure-white);color:var(--absolute-black);
  padding:var(--s4) var(--s8);font-size:var(--text-body);
}

.lightbox{
  position:fixed;inset:0;z-index:200;background:rgba(0,0,0,.96);
  display:flex;align-items:center;justify-content:center;
  opacity:0;transition:opacity .3s ease;
}
.lightbox[hidden]{display:none}
.lightbox.on{opacity:1}
.lb-figure{max-width:min(1200px,92vw);max-height:82vh;display:flex;flex-direction:column;gap:var(--s16)}
.lb-figure img{max-width:100%;max-height:72vh;object-fit:contain;margin:0 auto;display:block}
.lb-figure figcaption{color:var(--ash);font-size:var(--text-body);text-align:center;max-width:70ch;margin:0 auto}
.lb-close{
  position:absolute;top:var(--s24);inset-inline-end:var(--s24);color:#fff;
  font-size:34px;line-height:1;width:44px;height:44px;
}
.lb-nav{
  position:absolute;top:50%;transform:translateY(-50%);color:#fff;
  font-size:44px;line-height:1;width:56px;height:56px;
}
.lb-prev{inset-inline-start:var(--s16)} .lb-next{inset-inline-end:var(--s16)}
.lb-close:hover,.lb-nav:hover{color:var(--signal)}
.lb-count{
  position:absolute;bottom:var(--s24);left:50%;transform:translateX(-50%);
  color:var(--ash);font-size:var(--text-body);font-variant-numeric:tabular-nums;
}
@media(max-width:700px){.lb-nav{font-size:32px;width:40px;height:40px}}


/* =========================================================
   Search overlay + language menu
   ========================================================= */
.searchbox{
  position:fixed;inset:0;z-index:190;background:rgba(255,255,255,.98);
  backdrop-filter:blur(12px);-webkit-backdrop-filter:blur(12px);
  padding-top:calc(var(--nav-h,82px) + 8vh);
  opacity:0;transition:opacity .3s ease;overflow-y:auto;
}
.searchbox[hidden]{display:none}
.searchbox.on{opacity:1}
.searchbox input{
  width:100%;font-family:inherit;font-weight:var(--w-light);
  font-size:clamp(28px,5vw,64px);letter-spacing:-0.035em;
  color:var(--absolute-black);background:none;
  border:0;border-bottom:1px solid var(--hairline);
  padding:0 0 var(--s20);outline:none;border-radius:0;
}
.searchbox input::placeholder{color:var(--hairline)}
.searchbox input:focus{border-bottom-color:var(--absolute-black)}
.searchbox .hint{margin-top:var(--s16);font-size:var(--text-body)}
.searchbox .results{list-style:none;margin-top:var(--s40);display:flex;flex-direction:column}
.searchbox .results li{border-top:1px solid var(--hairline)}
.searchbox .results a{
  display:grid;grid-template-columns:1fr auto;gap:var(--s16);align-items:baseline;
  padding:var(--s20) 0;text-decoration:none;color:var(--absolute-black);
}
.searchbox .results a:hover{color:var(--signal)}
.searchbox .results b{font-weight:var(--w-regular);font-size:var(--text-heading-sm)}
.searchbox .results span{color:var(--ash);font-size:var(--text-body)}
.searchbox .results p{grid-column:1/-1;color:var(--ash);font-size:var(--text-body);margin-top:var(--s4)}

.lang{position:relative}
.lang > button{
  display:flex;align-items:center;gap:var(--s4);
  font-size:var(--text-body);color:inherit;
}
.lang > button svg{width:9px;height:9px;stroke:currentColor;fill:none;stroke-width:1.6}
.langmenu{
  position:absolute;top:calc(100% + 12px);inset-inline-end:0;min-width:150px;
  list-style:none;background:var(--pure-white);border:1px solid var(--hairline);
  opacity:0;visibility:hidden;transform:translateY(-6px);
  transition:opacity .25s ease,transform .3s cubic-bezier(.16,1,.3,1),visibility 0s .3s;
}
.lang.open .langmenu{opacity:1;visibility:visible;transform:none;transition-delay:0s}
.langmenu button{
  display:block;width:100%;text-align:start;padding:var(--s12) var(--s16);
  font-size:var(--text-body);color:var(--absolute-black);
}
.langmenu button:hover{color:var(--signal)}
.langmenu button[aria-current="true"]{color:var(--ash)}

/* ---------- key values grid ---------- */
.kv{display:grid;grid-template-columns:repeat(4,1fr);border-top:1px solid var(--hairline);border-inline-start:1px solid var(--hairline)}
.kv > div{
  border-inline-end:1px solid var(--hairline);border-bottom:1px solid var(--hairline);
  position:relative;aspect-ratio:1;display:flex;align-items:center;justify-content:center;
  overflow:hidden;
}
.kv svg{position:absolute;width:76%;height:76%;stroke:var(--hairline);fill:none;stroke-width:1}
.kv span{
  position:relative;font-size:clamp(24px,2.9vw,var(--text-heading-lg));
  line-height:1.05;letter-spacing:-0.04em;font-weight:var(--w-light);
  text-align:center;padding-inline:var(--s12);
}
@media(max-width:900px){.kv{grid-template-columns:repeat(2,1fr)}}

/* ---------- key/value rows ---------- */
.rows{border-top:1px solid var(--hairline)}
.row{
  display:grid;grid-template-columns:80px 1fr 1fr;gap:var(--s24);
  padding-block:var(--s24);border-bottom:1px solid var(--hairline);align-items:start;
}
.row .idx{font-size:var(--text-body);font-variant-numeric:tabular-nums;color:var(--ash)}
.row .t p{margin-top:var(--s8);color:var(--ash)}
@media(max-width:820px){.row{grid-template-columns:56px 1fr}.row .fig{grid-column:1/-1}}

/* ---------- figure with caption ---------- */
figure figcaption{
  margin-top:var(--s12);padding-top:var(--s12);
  border-top:1px solid var(--hairline);
  font-size:var(--text-body);color:var(--ash);
}
.band figure figcaption{border-top-color:rgba(255,255,255,.22)}

/* ---------- catalogue band ---------- */
.cat{display:grid;grid-template-columns:1fr 1fr;gap:clamp(32px,5vw,64px);align-items:center}
@media(max-width:820px){.cat{grid-template-columns:1fr}}

/* ---------- stats ---------- */
.stats{display:grid;grid-template-columns:repeat(4,1fr);border-top:1px solid var(--hairline)}
.stats > div{border-bottom:1px solid var(--hairline);border-inline-end:1px solid var(--hairline);padding:var(--s32) var(--s24)}
.stats > div:last-child{border-inline-end:0}
.stats b{
  display:block;font-size:clamp(30px,4.2vw,var(--text-heading-lg));
  font-weight:var(--w-light);letter-spacing:-0.04em;line-height:1;
  font-variant-numeric:tabular-nums;
}
.stats span{display:block;margin-top:var(--s12);font-size:var(--text-body);color:var(--ash)}
.band .stats{border-top-color:rgba(255,255,255,.22)}
.band .stats > div{border-bottom-color:rgba(255,255,255,.22);border-inline-end-color:rgba(255,255,255,.22)}
@media(max-width:820px){
  .stats{grid-template-columns:repeat(2,1fr)}
  .stats > div:nth-child(2n){border-inline-end:0}
}

/* ---------- accordion ---------- */
.acc{border-bottom:1px solid var(--hairline)}
.acc:first-of-type{border-top:1px solid var(--hairline)}
.acc button{
  width:100%;display:flex;justify-content:space-between;align-items:center;gap:var(--s24);
  padding-block:var(--s24);text-align:start;
}
.acc button:hover{color:var(--signal)}
.acc .sym{width:12px;height:12px;position:relative;flex:none}
.acc .sym::before,.acc .sym::after{content:"";position:absolute;background:currentColor;transition:transform .35s ease}
.acc .sym::before{left:0;right:0;top:5.5px;height:1px}
.acc .sym::after{top:0;bottom:0;inset-inline-start:5.5px;width:1px}
.acc.open .sym::after{transform:scaleY(0)}
.acc .body{max-height:0;overflow:hidden;transition:max-height .45s ease}
.acc .body p{padding-bottom:var(--s24);color:var(--ash);max-width:62ch}

/* ---------- forms ---------- */
.field{margin-bottom:var(--s20)}
.field label{display:block;font-size:var(--text-body);font-weight:var(--w-medium);margin-bottom:var(--s8)}
.field input,.field textarea,.field select{
  width:100%;font:inherit;font-size:var(--text-body);
  color:var(--absolute-black);background:var(--pure-white);
  border:1px solid var(--stone);border-radius:var(--radius-input);
  padding:var(--s8) var(--s12);outline:none;resize:none;
  transition:border-color .2s;
}
.field input::placeholder,.field textarea::placeholder{color:var(--ash)}
.field input:focus,.field textarea:focus,.field select:focus{border-color:var(--absolute-black)}

/* ---------- reveal ---------- */
/* Never clip the observed element: IntersectionObserver honours clipping, so a
   clipped node reports ratio 0, never gets .in, and stays hidden forever. */
[data-rise]{opacity:0;transform:translateY(14px);transition:opacity .8s ease,transform .8s ease}
[data-rise].in{opacity:1;transform:none}
[data-rise][data-d="1"]{transition-delay:.06s}
[data-rise][data-d="2"]{transition-delay:.12s}
[data-rise][data-d="3"]{transition-delay:.18s}
[data-rise][data-d="4"]{transition-delay:.24s}
[data-mask]{overflow:hidden}
[data-mask]>img,[data-mask]>video{
  clip-path:inset(0 0 100% 0);transition:clip-path 1s cubic-bezier(.16,1,.3,1);
}
[data-mask].in>img,[data-mask].in>video{clip-path:inset(0 0 0 0)}
@media(prefers-reduced-motion:reduce){
  [data-rise],[data-mask],[data-mask]>img,[data-mask]>video{
    opacity:1;transform:none;clip-path:none;transition:none;
  }
}

/* ---------- footer ---------- */
/* ---------- mega footer ----------
   Four stacked bands separated by hairlines: masthead, the link grid that
   surfaces the whole site, a utility strip, and the legal base. No fills, no
   boxes — the rules do all the structural work, same as everywhere else. */
.ftr{border-top:1px solid var(--absolute-black);padding-block:var(--s48) var(--s24)}
.ftr h4{
  font-size:var(--text-body);font-weight:var(--w-medium);
  margin-bottom:var(--s20);padding-bottom:var(--s8);
  border-bottom:1px solid var(--hairline);
}
.ftr ul{list-style:none;display:flex;flex-direction:column;gap:var(--s12)}
.ftr a{font-size:var(--text-body);color:var(--absolute-black);text-decoration:none;
  border-bottom:1px solid transparent;transition:color .2s,border-color .2s}
.ftr a:hover{color:var(--signal);border-bottom-color:var(--signal)}
.ftr p{font-size:var(--text-body);line-height:var(--leading-body)}

/* band 1 — masthead */
.ftr-top{
  display:grid;grid-template-columns:2.2fr 1fr 1fr 1fr;gap:var(--s32);
  padding-bottom:var(--s48);
}
.ftr-top .wordmark{font-size:34px}
.ftr-lede{
  margin-top:var(--s20);max-width:34ch;
  font-size:var(--text-subheading);line-height:1.5;font-weight:var(--w-light);
}
.ftr-reg{margin-top:var(--s20);color:var(--stone);max-width:38ch}

/* band 2 — the mega grid */
.ftr-mega{
  display:grid;grid-template-columns:repeat(5,1fr);gap:var(--s32);
  padding-block:var(--s40);border-top:1px solid var(--hairline);
}

/* band 3 — utilities */
.ftr-utils{
  display:flex;justify-content:space-between;align-items:baseline;
  gap:var(--s24);flex-wrap:wrap;
  padding-block:var(--s24);border-top:1px solid var(--hairline);
}
/* `.ftr ul` (0,1,1) sets flex-direction:column and out-specifies a bare
   `.ftr-inline` (0,1,0), so this needs the extra class to win. */
.ftr .ftr-inline{flex-direction:row;gap:var(--s24);flex-wrap:wrap}
.ftr-lang{display:flex;gap:var(--s16);align-items:baseline}
.ftr-lang a[aria-current]{border-bottom-color:var(--absolute-black);font-weight:var(--w-medium)}
.ftr-up{white-space:nowrap}

/* band 4 — legal */
.ftr .base{
  padding-top:var(--s20);border-top:1px solid var(--hairline);
  display:flex;justify-content:space-between;gap:var(--s16);flex-wrap:wrap;
  font-size:var(--text-body);color:var(--ash);
}

@media(max-width:1080px){
  /* The brand block takes the full width and the three contact columns share a
     row beneath it — squeezing brand + 3 columns into 3 tracks left Hours
     spanning two of them with dead space under Talk to us. */
  .ftr-top{grid-template-columns:repeat(3,1fr)}
  .ftr-top > :first-child{grid-column:1/-1;margin-bottom:var(--s16)}
  .ftr-mega{grid-template-columns:repeat(3,1fr);row-gap:var(--s40)}
}
@media(max-width:820px){
  .ftr-top{grid-template-columns:repeat(2,1fr)}
  .ftr-mega{grid-template-columns:repeat(2,1fr)}
}
@media(max-width:520px){
  .ftr-top,.ftr-mega{grid-template-columns:1fr}
  .ftr-top > :first-child{grid-column:auto}
  .ftr-utils{flex-direction:column;align-items:flex-start;gap:var(--s16)}
}

/* ---------- journal article ---------- */
/* Prose is the one place the system allows a narrower measure than .measure —
   68ch at 18px is the readable band; everything else stays on the page grid. */
.artmeta{display:flex;flex-wrap:wrap;gap:var(--s16);align-items:baseline;
  font-size:var(--text-body);color:var(--stone)}
.artmeta .sep{color:var(--hairline)}
.artmeta a{color:var(--stone);border-bottom:1px solid var(--hairline)}
.artmeta a:hover{color:var(--absolute-black);border-bottom-color:var(--absolute-black)}

.prose{max-width:68ch;font-size:var(--text-subheading);line-height:1.66;font-weight:var(--w-light)}
.prose > * + *{margin-top:var(--s24)}
.prose .lede{font-size:clamp(21px,2.2vw,27px);line-height:1.42;font-weight:var(--w-light);
  letter-spacing:-.6px;color:var(--absolute-black)}
.prose h2{font-size:var(--text-heading);line-height:var(--leading-heading);
  letter-spacing:var(--tracking-heading);font-weight:var(--w-light);
  margin-top:var(--s48);padding-top:var(--s24);border-top:1px solid var(--hairline)}
.prose h3{font-size:var(--text-heading-sm);line-height:var(--leading-heading-sm);
  font-weight:var(--w-medium);margin-top:var(--s36)}
.prose strong{font-weight:var(--w-medium)}
.prose a{border-bottom:1px solid var(--absolute-black)}
.prose a:hover{color:var(--signal);border-bottom-color:var(--signal)}
.prose ul,.prose ol{padding-inline-start:0;list-style:none}
.prose li{position:relative;padding-inline-start:var(--s24);margin-top:var(--s12)}
.prose ul > li::before{content:"";position:absolute;inset-inline-start:0;top:.72em;
  width:10px;height:1px;background:var(--absolute-black)}
.prose ol{counter-reset:n}
.prose ol > li{counter-increment:n}
.prose ol > li::before{content:counter(n,decimal-leading-zero);position:absolute;inset-inline-start:0;top:.16em;
  font-size:var(--text-body);color:var(--stone)}

/* pull quote — hairline above and below, never a box */
.pull{margin-top:var(--s48);padding:var(--s32) 0;
  border-top:1px solid var(--absolute-black);border-bottom:1px solid var(--hairline)}
.pull p{font-size:clamp(24px,3vw,38px);line-height:1.16;letter-spacing:-1.2px;
  font-weight:var(--w-light);max-width:20ch}
.pull cite{display:block;margin-top:var(--s16);font-style:normal;
  font-size:var(--text-body);color:var(--stone)}

/* inline figure inside prose, breaking the measure */
.figure{margin-top:var(--s48)}
.figure img{width:100%;height:auto;display:block}
.figure figcaption{margin-top:var(--s12);font-size:var(--text-body);color:var(--stone);max-width:60ch}

/* numbers strip inside an article */
.artnums{display:grid;grid-template-columns:repeat(4,1fr);gap:0;
  border-top:1px solid var(--absolute-black);margin-top:var(--s48)}
.artnums > div{padding-block:var(--s24);padding-inline:0 var(--s20);border-inline-end:1px solid var(--hairline)}
.artnums > div:last-child{border-inline-end:0}
.artnums b{display:block;font-size:clamp(30px,3.6vw,50px);line-height:1;
  letter-spacing:-1.6px;font-weight:var(--w-light)}
.artnums span{display:block;margin-top:var(--s12);font-size:var(--text-body);color:var(--stone)}

/* prev / next */
.artnav{display:grid;grid-template-columns:1fr 1fr;border-top:1px solid var(--absolute-black)}
.artnav > *{padding:var(--s32) 0;display:block}
.artnav > * + *{border-inline-start:1px solid var(--hairline);padding-inline-start:var(--s32)}
.artnav .dir{display:block;font-size:var(--text-body);color:var(--stone);margin-bottom:var(--s12)}
.artnav .ttl{display:block;font-size:var(--text-heading-sm);line-height:1.2;
  font-weight:var(--w-light);letter-spacing:-.4px;max-width:26ch}
.artnav > a:hover .ttl{color:var(--signal)}
.artnav .empty{color:var(--quartz)}
.artnav .empty .ttl{color:var(--quartz)}

/* There is no global anchor reset in this system — every link was previously
   carried by a component class, so a bare <a> fell through to browser-default
   blue and underline. Journal titles and the prev/next cards are bare anchors. */
a{color:inherit;text-decoration:none}

.rows .t h2 a{color:var(--absolute-black)}
.rows .t h2 a:hover{color:var(--signal)}

@media(max-width:820px){
  .artnums{grid-template-columns:repeat(2,1fr)}
  .artnums > div:nth-child(2n){border-inline-end:0}
  .artnav{grid-template-columns:1fr}
  .artnav > * + *{border-inline-start:0;border-top:1px solid var(--hairline);padding-inline-start:0}
}


/* ---------- form validation & submission state ---------- */
.field .err{margin-top:var(--s8);font-size:var(--text-body);color:var(--alert)}
.field input[aria-invalid="true"],
.field textarea[aria-invalid="true"],
.field select[aria-invalid="true"]{border-color:var(--alert)}
.field input[aria-invalid="true"]:focus,
.field textarea[aria-invalid="true"]:focus,
.field select[aria-invalid="true"]:focus{border-color:var(--alert)}

/* Spam trap. Off-screen rather than display:none — some bots skip hidden inputs,
   and a real person never reaches it because it is aria-hidden and untabbable. */
.trap{position:absolute!important;width:1px;height:1px;overflow:hidden;
  clip:rect(0 0 0 0);clip-path:inset(50%);white-space:nowrap}

/* Status message: a hairline rule on the left, in keeping with the rest of the
   system — no filled panel, no box. */
.formstate{margin-top:var(--s16);font-size:var(--text-body);line-height:var(--leading-body);
  border-inline-start:2px solid var(--absolute-black);padding-inline-start:var(--s12);max-width:52ch}
.formstate.is-ok{border-inline-start-color:var(--signal)}
.formstate.is-bad{border-inline-start-color:var(--alert);color:var(--alert)}
.formstate a{border-bottom:1px solid currentColor}

button[aria-busy="true"]{opacity:.45;pointer-events:none}

/* =========================================================
   Arabic & RTL
   Layout mirroring is handled by logical properties throughout the sheet, so
   this block only covers what has no logical equivalent: typography, 3D
   transform origins, and a handful of glyph directions.
   ========================================================= */

/* Arabic letterforms join. The negative tracking that makes the Latin display
   type cohere pulls Arabic apart and breaks the joins outright, so every
   tracking value is reset and the leading is opened up — Arabic carries taller
   ascenders and lower descenders than Latin at the same size. */
[lang="ar"] {
  --tracking-heading-sm:0;
  --tracking-heading:0;
  --tracking-heading-lg:0;
  --tracking-display:0;
  --leading-heading-sm:1.5;
  --leading-body:1.85;
  --leading-subheading:1.8;
}
[lang="ar"] body {
  font-family:"IBM Plex Sans Arabic","Noto Sans Arabic",Inter,system-ui,sans-serif;
}
/* Arabic has no capitals; uppercasing a kicker does nothing to Arabic glyphs but
   the letter-spacing applied alongside it still damages the joins. */
[lang="ar"] .kicker { letter-spacing:0; text-transform:none; }
[lang="ar"] .wordmark { letter-spacing:0; }
/* LEADING. This is the other half of the tracking problem and it is what
   actually clips text. Latin sits comfortably in a line box at 0.92–1.05 because
   its ink barely leaves the x-height band. Arabic does not: ج ح خ ع غ م ي drop
   well below the baseline, أ ل ك ط rise well above it, and at those ratios the
   ink leaves the line box. Wherever an ancestor clips — the hero mask, the intro
   mask, the square .kv cells — the overflowing ink is simply cut off, which is
   exactly the "words not rendering fully" symptom. Every ratio below ~1.3 is
   raised here. These are typographic corrections, not padding hacks. */
[lang="ar"] {
  --leading-display:1.32;
  --leading-heading-lg:1.38;
  --leading-heading:1.45;
}
[lang="ar"] .hud .word b        { line-height:1.35 }
[lang="ar"] #intro .title .line b,
[lang="ar"] #intro .title .sub b{ line-height:1.35 }
[lang="ar"] .cat span           { line-height:1.4 }
[lang="ar"] .cat sup            { line-height:1.4 }
[lang="ar"] .kv span            { line-height:1.42 }
[lang="ar"] .stats b            { line-height:1.35 }
[lang="ar"] .artnums b          { line-height:1.35 }
[lang="ar"] .pull p             { line-height:1.5 }
[lang="ar"] .artnav .ttl        { line-height:1.55 }
[lang="ar"] .kicker             { line-height:1.6 }
/* Wide positive tracking on joined Arabic script does not letterspace it, it
   takes the word apart — the letters stop connecting. Every place the design
   letterspaces a Latin label (.2em to .46em) is neutralised for Arabic, along
   with the uppercasing, which does nothing to Arabic glyphs anyway. */
[lang="ar"] #intro .title .sub b,
[lang="ar"] .hud .meta,
[lang="ar"] .hud .sub b,
[lang="ar"] .hud .cue { letter-spacing:0; text-transform:none }
/* The meta row and cue sit on one line; Arabic needs the room. */
[lang="ar"] .hud .meta { line-height:1.6 }
[lang="ar"] .hud .cue  { line-height:1.7 }
[lang="ar"] .prose .lede        { line-height:1.65 }
[lang="ar"] .promo .lead,
[lang="ar"] .promo p            { line-height:1.7 }

/* TRACKING. The sheet sets letter-spacing in a dozen places — on tokens, on each
   heading class in em, and on individual components. Enumerating them by name was
   tried and missed .cat span, .kv span and .artnav .ttl, which then kept negative
   tracking and broke the joins in the mega menu on every page.
   Arabic must never be tracked in either direction: negative values pull the
   joined letters into each other, positive values take the word apart. So this is
   a blanket reset for the Arabic pages rather than a list to keep in sync.

   `!important` is deliberate and is the right tool here. This is a script-level
   override that has to beat every component rule in the sheet, and component
   selectors like `.hud .word b` (0,2,1) already out-specify `[lang="ar"] body *`
   (0,1,1). Raising specificity by hand would mean mirroring the whole sheet. */
[lang="ar"] body,
[lang="ar"] body * { letter-spacing:0 !important }
/* The display sizes are tuned to Latin. Arabic set at 100px reads considerably
   larger because the letterforms fill more of the em, so it is pulled back. */
[lang="ar"] { --text-display:78px; --text-heading-lg:42px; }

/* Latin runs inside Arabic text — figures, emails, model names — need their own
   direction or the punctuation lands on the wrong end. */
[dir="rtl"] .ltr { direction:ltr; unicode-bidi:isolate; display:inline-block; }
[dir="rtl"] a[href^="mailto:"],
[dir="rtl"] a[href^="tel:"] { direction:ltr; unicode-bidi:isolate; display:inline-block; }

/* transform-origin and perspective-origin have no logical form. The book must
   hinge on its trailing edge, which is the right in RTL, and the pages then turn
   the other way. */
[dir="rtl"] #intro { perspective-origin:68% 50%; }
[dir="rtl"] #intro .page { transform-origin:right center; }
[dir="rtl"] body.riffle #intro .page { animation-name:riffle-rtl; }
@keyframes riffle-rtl {
  0%   { transform:rotateY(0deg) }
  100% { transform:rotateY(170deg) }
}
/* The hero progress bar fills from the leading edge. */
[dir="rtl"] .hud .rail i::after { transform-origin:right; }

/* Chevrons are glyphs, not layout — they do not mirror themselves. Flipping the
   button reuses the existing centring transform rather than swapping characters. */
[dir="rtl"] .lb-nav { transform:translateY(-50%) scaleX(-1); }

/* =========================================================
   Loading screen — a tower crane raises a building.

   Drawn, not filled: hairline strokes on the white canvas, same idiom as the
   rest of the system. Parts are revealed with transforms rather than
   stroke-dashoffset so no path-length maths is needed and nothing depends on
   getTotalLength() being available.

   Timeline (2.9s of build, then it holds until the page is actually ready):
     0.05  ground line
     0.15  mast grows up from its base
     0.62  apex A-frame
     0.78  jib and counter-jib grow outward from the mast
     1.10  trolley and hook appear, and start tracking along the jib
     1.25  five floors rise into place, 0.34s apart, in step with the trolley
     3.10  crane recedes, the finished building holds
   ========================================================= */
#loader{
  position:fixed;inset:0;z-index:300;background:var(--pure-white);
  display:grid;grid-template-rows:1fr auto;
  opacity:1;transition:opacity .55s ease,transform .75s cubic-bezier(.7,0,.2,1);
}
body.loading{overflow:hidden}
#loader.is-done{opacity:0;transform:translateY(-1.5%);pointer-events:none}

.ld-stage{display:grid;place-items:center;padding:var(--gut);min-height:0}
.ld-svg{
  width:min(84vw,820px);height:auto;overflow:visible;
  stroke:var(--absolute-black);stroke-width:1.25;
  vector-effect:non-scaling-stroke;
}
.ld-svg rect,.ld-svg line,.ld-svg path{vector-effect:non-scaling-stroke}
.ld-svg path{fill:var(--absolute-black);stroke:none}

/* --- the crane assembles itself ---
   Every part is animated on ONE shared 1s timeline that is permanently paused
   and scrubbed by a negative delay: `animation-delay: calc(var(--p) * -1s)`.
   --p is the loading fraction, so a keyframe written at 47% happens exactly when
   loading reaches 47%. The percentage on screen and the state of the building
   are the same number by construction — they cannot drift apart, because there
   is no second timeline for them to drift against. */
#loader .ld-ground line,
#loader .ld-mast, #loader .ld-apex, #loader .ld-jib, #loader .ld-cjib,
#loader .ld-trolley, #loader .ld-cable, #loader .ld-hook,
#loader .ld-build .fl, #loader .ld-crane{
  animation-duration:1s;
  animation-timing-function:linear;
  animation-fill-mode:both;
  animation-play-state:paused;
  animation-delay:calc(var(--p,0) * -1s);
}

.ld-ground line{transform-origin:center;animation-name:ldGround}
.ld-mast    {transform-origin:167px 500px;animation-name:ldMast}
.ld-apex    {transform-origin:185px 124px;animation-name:ldApex}
.ld-jib     {transform-origin:200px 130px;animation-name:ldJib}
.ld-cjib    {transform-origin:170px 130px;animation-name:ldCjib}
.ld-trolley {animation-name:ldTrolley}
.ld-cable   {transform-origin:0 144px;animation-name:ldCable}
.ld-hook    {animation-name:ldHook}
.ld-crane   {animation-name:ldRecede}

/* Floors are fully placed exactly on the deciles — 20, 40, 60, 80, 95 per
   cent — so the frame is n fifths up when the readout says n fifths. The
   crane assembles itself inside the first 14% instead of eating a fifth. */
.ld-build .fl:nth-child(1){animation-name:ldFloor0}
.ld-build .fl:nth-child(2){animation-name:ldFloor1}
.ld-build .fl:nth-child(3){animation-name:ldFloor2}
.ld-build .fl:nth-child(4){animation-name:ldFloor3}
.ld-build .fl:nth-child(5){animation-name:ldFloor4}

@keyframes ldGround {0%{transform:scaleX(0)} 3%,100%{transform:scaleX(1)}}
@keyframes ldMast   {0%,1%{transform:scaleY(0)} 9%,100%{transform:scaleY(1)}}
@keyframes ldApex   {0%,7%{opacity:0;transform:scaleY(0)} 12%,100%{opacity:1;transform:scaleY(1)}}
@keyframes ldJib    {0%,9%{transform:scaleX(0)} 14%,100%{transform:scaleX(1)}}
@keyframes ldCjib   {0%,10%{transform:scaleX(0)} 14%,100%{transform:scaleX(1)}}

@keyframes ldFloor0 {0%,12%{opacity:0;transform:translateY(-46px)} 20%,100%{opacity:1;transform:translateY(0)}}
@keyframes ldFloor1 {0%,30%{opacity:0;transform:translateY(-46px)} 40%,100%{opacity:1;transform:translateY(0)}}
@keyframes ldFloor2 {0%,50%{opacity:0;transform:translateY(-46px)} 60%,100%{opacity:1;transform:translateY(0)}}
@keyframes ldFloor3 {0%,70%{opacity:0;transform:translateY(-46px)} 80%,100%{opacity:1;transform:translateY(0)}}
@keyframes ldFloor4 {0%,85%{opacity:0;transform:translateY(-46px)} 95%,100%{opacity:1;transform:translateY(0)}}

@keyframes ldRecede {0%,95%{opacity:1} 100%{opacity:.16}}

@keyframes ldTrolley{
  0%,9%  {opacity:0;transform:translateX(280px)}
  12%    {opacity:1;transform:translateX(330px)}
  20%    {opacity:1;transform:translateX(620px)}
  30%    {opacity:1;transform:translateX(470px)}
  40%    {opacity:1;transform:translateX(660px)}
  50%    {opacity:1;transform:translateX(480px)}
  60%    {opacity:1;transform:translateX(640px)}
  70%    {opacity:1;transform:translateX(500px)}
  80%    {opacity:1;transform:translateX(620px)}
  88%    {opacity:1;transform:translateX(520px)}
  95%    {opacity:1;transform:translateX(600px)}
  100%   {opacity:1;transform:translateX(560px)}
}
/* The cable pays out to the deck the next floor lands on, then reels in. It gets
   shorter as the frame rises, because the deck is coming up to meet the hook. */
@keyframes ldCable{
  0%,10% {transform:scaleY(1)}
  20%    {transform:scaleY(295)}
  24%    {transform:scaleY(295)}
  30%    {transform:scaleY(152)}
  40%    {transform:scaleY(233)}
  44%    {transform:scaleY(233)}
  50%    {transform:scaleY(112)}
  60%    {transform:scaleY(171)}
  64%    {transform:scaleY(171)}
  70%    {transform:scaleY(82)}
  80%    {transform:scaleY(109)}
  84%    {transform:scaleY(109)}
  88%    {transform:scaleY(52)}
  95%    {transform:scaleY(47)}
  100%   {transform:scaleY(92)}
}
@keyframes ldHook{
  0%,10% {transform:translateY(0)}
  20%    {transform:translateY(293px)}
  24%    {transform:translateY(293px)}
  30%    {transform:translateY(150px)}
  40%    {transform:translateY(231px)}
  44%    {transform:translateY(231px)}
  50%    {transform:translateY(110px)}
  60%    {transform:translateY(169px)}
  64%    {transform:translateY(169px)}
  70%    {transform:translateY(80px)}
  80%    {transform:translateY(107px)}
  84%    {transform:translateY(107px)}
  88%    {transform:translateY(50px)}
  95%    {transform:translateY(45px)}
  100%   {transform:translateY(90px)}
}

/* --- readout --- */
.ld-foot{padding-bottom:clamp(24px,5vh,56px)}
.ld-row{
  display:flex;justify-content:space-between;align-items:baseline;
  gap:var(--s16);margin-bottom:var(--s16);
}
.ld-label{font-size:var(--text-body);letter-spacing:.28em;text-transform:uppercase;color:var(--stone)}
.ld-pct b{font-size:clamp(28px,4vw,46px);font-weight:var(--w-light);
  font-variant-numeric:tabular-nums;letter-spacing:-.02em}
.ld-bar{height:1px;background:var(--hairline);position:relative;overflow:hidden}
.ld-bar i{
  display:block;height:100%;width:100%;background:var(--absolute-black);
  transform:scaleX(0);transform-origin:left center;
}
[dir="rtl"] .ld-bar i{transform-origin:right center}
[lang="ar"] .ld-label{letter-spacing:0;text-transform:none}

/* Reduced motion: no crane assembly, just the finished frame and the readout. */
@media(prefers-reduced-motion:reduce){
  #loader .ld-ground line,#loader .ld-mast,#loader .ld-apex,
  #loader .ld-jib,#loader .ld-cjib,#loader .ld-trolley,
  #loader .ld-build .fl,#loader .ld-crane{animation:none}
  #loader .ld-build .fl{opacity:1;transform:none}
  #loader .ld-ground line,#loader .ld-mast,#loader .ld-jib,#loader .ld-cjib{transform:none}
  #loader .ld-apex,#loader .ld-trolley{opacity:1;transform:none}
}
@media(max-width:640px){
  .ld-svg{width:92vw}
  .ld-label{letter-spacing:.18em}
}

/* =========================================================
   Method — pinned horizontal stage sequence.

   The section is tall; its inner sticks for the duration and a flex track is
   translated sideways by scroll progress, so seven full-bleed stages pass across
   the screen. Each panel is the stage photograph at full size with the numeral
   and title over it, instead of the seven small rows this replaced.

   Direction-aware: the track moves the other way under dir="rtl" (JS decides the
   sign — a transform cannot be expressed logically).

   Below 820px, and under prefers-reduced-motion, the whole mechanism is dropped
   and the panels become a plain vertical stack. Nothing is pinned, nothing is
   translated, and the content reads exactly the same.
   ========================================================= */
/* Every selector below is scoped under .stages on purpose. The hero HUD already
   owns a `.stage` element (the phase word), so a bare `.stage::after` would drop
   a gradient over the hero and a bare `.stage{overflow:hidden}` would clip it. */
.stages{position:relative;--per:72vh}
.stages-sticky{
  position:sticky;top:0;height:100vh;overflow:hidden;
  background:var(--absolute-black);color:var(--pure-white);
}
.stages-track{display:flex;height:100%;will-change:transform}

.stages .stage{position:relative;flex:0 0 100vw;height:100%;overflow:hidden}
.stages .stage-media{position:absolute;inset:0;overflow:hidden}
.stages .stage-media img{
  width:100%;height:100%;object-fit:cover;display:block;
  transform:scale(1.12);will-change:transform;
}
/* legible type over photography, without a flat scrim over the whole frame */
.stages .stage::after{
  content:"";position:absolute;inset:0;pointer-events:none;
  background:linear-gradient(to top,
    rgba(0,0,0,.80) 0%, rgba(0,0,0,.42) 34%, rgba(0,0,0,.14) 62%, rgba(0,0,0,.40) 100%);
}
.stages .stage-body{
  position:absolute;z-index:2;
  inset-inline-start:var(--gut);bottom:clamp(48px,9vh,110px);
  max-width:min(52ch,78vw);
}
.stages .stage-no{
  display:block;font-weight:var(--w-light);opacity:.42;
  font-size:clamp(72px,12vw,200px);line-height:.82;letter-spacing:-.05em;
  margin-bottom:var(--s12);
}
.stages .stage h3{
  font-size:clamp(30px,5vw,68px);line-height:1.04;letter-spacing:-.04em;
  font-weight:var(--w-light);
}
.stages .stage h3 a{color:var(--pure-white);border-bottom:0;text-decoration:none}
.stages .stage h3 a:hover{color:var(--pure-white);opacity:.7}
.stages .stage p{
  margin-top:var(--s20);max-width:44ch;
  font-size:var(--text-subheading);line-height:1.55;color:rgba(255,255,255,.82);
}

/* head: kicker, seven-segment rail, counter */
.stages-head{
  position:absolute;z-index:3;top:clamp(84px,12vh,132px);
  inset-inline:var(--gut);
  display:flex;align-items:center;gap:var(--s24);flex-wrap:wrap;
}
.stages-head .kicker{color:var(--pure-white);opacity:.75;margin:0}
.stages-rail{
  flex:1 1 160px;max-width:460px;
  display:grid;grid-template-columns:repeat(7,1fr);gap:6px;
}
.stages-rail i{display:block;height:1px;background:rgba(255,255,255,.28);transition:background .35s ease}
.stages-rail i.on{background:var(--pure-white)}
.stages-count{
  font-size:var(--text-body);font-variant-numeric:tabular-nums;
  letter-spacing:.22em;opacity:.8;white-space:nowrap;
}
[lang="ar"] .stages-count{letter-spacing:0}

/* the sequence is horizontal only where there is room for it to be */
@media(max-width:820px){
  .stages{height:auto!important;--per:0}
  .stages-sticky{position:static;height:auto}
  .stages-track{display:block;transform:none!important}
  .stages .stage{width:auto;flex:none;height:auto;min-height:74vh}
  .stages .stage-media img{transform:none!important}
  .stages-head{
    position:static;background:var(--absolute-black);
    padding:var(--s32) var(--gut) var(--s16);
  }
  .stages-rail{display:none}
}
@media(prefers-reduced-motion:reduce){
  .stages{height:auto!important}
  .stages-sticky{position:static;height:auto}
  .stages-track{display:block;transform:none!important}
  .stages .stage{width:auto;flex:none;height:auto;min-height:74vh}
  .stages .stage-media img{transform:none!important}
  .stages-head{position:static;padding:var(--s32) var(--gut) var(--s16)}
  .stages-rail{display:none}
}

/* =========================================================
   Touch targets.

   The system is buttonless: every action is a typographic link, so its box is
   only as tall as its own line — 21px in the footer columns, 22px in the mega
   panel. That is fine with a cursor and too small for a thumb (WCAG 2.5.8 asks
   for 24px minimum; a thumb wants nearer 40).

   The hit area is enlarged with a transparent ::after rather than with padding,
   because these links carry a bottom border: padding would push the underline
   away from the text and change the design. The pseudo-element expands what is
   tappable and moves nothing.

   Scoped to coarse pointers, so the desktop rendering is byte-identical.
   ========================================================= */
@media (hover:none),(pointer:coarse){
  /* space the stacked lists first, so enlarged areas cannot overlap and steal
     each other's taps */
  .ftr ul{gap:var(--s20)}
  .mega .sub li + li{margin-top:var(--s8)}

  .ftr a,
  .mega .sub a,
  .nav .utils a,
  .lang [data-lang],
  .lang [data-setlang],
  .navtoggle,
  .link,
  .link-inline,
  [data-submit]{position:relative}

  .ftr a::after,
  .mega .sub a::after,
  .nav .utils a::after,
  .lang [data-lang]::after,
  .lang [data-setlang]::after,
  .navtoggle::after,
  .link::after,
  [data-submit]::after{
    content:"";position:absolute;inset:-9px -8px;
  }
  /* inline links sit inside running prose, so they may only grow vertically —
     widening them would swallow taps on the words either side */
  .link-inline::after{content:"";position:absolute;inset:-8px 0}
}

/* The hero carries 850vh of scroll for its 10s of footage. On a phone that is
   8.5 screens of thumb travel before the page even begins; 480vh keeps the
   build deliberate without making the hero a chore to get past. */
@media(max-width:820px){
  .hero{height:480vh}
}
