/* ============================================================
   AUGMENT — master one-pager styles
   Section order: 1 Hero · 2 Globe · 3 Logos · 4 About ·
   5 Portfolio · [grad 6 Capabilities · 7 Separator · 8 Team] ·
   9 Footer.  tokens.css holds colour + type.
   ============================================================ */

/* ---------- section scaffolding / rhythm ---------- */
.sec{position:relative;width:100%;}
.sec--ink{background:var(--c-ink);color:var(--c-white);}
.sec__inner{max-width:var(--maxw);margin-inline:auto;padding-inline:var(--gutter);}

/* §6–§8 : ONE seamless gradient, top #727272 → bottom #1D1E1D */
.grad-zone{background:linear-gradient(180deg,var(--grad-top) 0%,var(--grad-bot) 100%);color:var(--c-white);
  /* dark buffer below the team carousel so the footer stays off-screen while you're on §8 */
  padding-bottom:clamp(9rem,22vh,15rem);}

/* ---------- scroll-reveal engine ---------- */
[data-reveal]{opacity:0;will-change:opacity,transform;transition:opacity .9s var(--ease),transform 1s var(--ease);}
[data-reveal].in{opacity:1;transform:none;}
[data-reveal="rise"]{transform:translateY(34px);}
[data-reveal="fade"]{transform:none;}
[data-reveal="left"]{transform:translateX(-40px);}
[data-reveal="right"]{transform:translateX(40px);}
[data-reveal] [data-stagger]{opacity:0;transform:translateY(24px);transition:opacity .8s var(--ease),transform .9s var(--ease);}
[data-reveal].in [data-stagger]{opacity:1;transform:none;}

@media (prefers-reduced-motion:reduce){
  [data-reveal],[data-reveal] [data-stagger]{opacity:1!important;transform:none!important;}
}

/* ============================================================
   §1 · HEADER / HERO  — centred mark + "A FORCE…" on grey
   ============================================================ */
.hero{
  /* less the ticker bar above it, so hero + ticker together fill exactly one
     viewport (--ticker-h is published by main.js; 0 fallback if JS is off) */
  position:relative;min-height:calc(100svh - var(--ticker-h,0px));
  /* Ink base, not grey: the arrival montage settles into a loop behind this type,
     and dark text over footage is unreadable at any usable scrim strength — the
     video's own dark shots swallow it (measured 1.6:1). Light type over darkened
     footage measures 5.7:1 instead, and it hands into the dark globe section below
     more naturally than the old grey did. The ink also shows through wherever the
     video is absent (no JS, reduced motion, decode refused), so the hero is never
     unstyled. */
  background:var(--c-ink);color:var(--c-paper);
  display:flex;flex-direction:column;align-items:center;text-align:center;
  padding:clamp(1.5rem,3.4vh,2.8rem) var(--gutter) clamp(2rem,5vh,3.5rem);
  overflow:hidden;
}
/* ---------- hero background loop ----------
   This lives INSIDE #hero deliberately. .intro is 230svh of scroll runway, so an
   inset:0 child of .intro would span the whole runway rather than the pinned
   viewport; #hero is position:relative + overflow:hidden and pins as one piece, so
   an absolutely positioned child travels with it for free.
   Three non-obvious requirements, each load-bearing:
   - pointer-events:none. cursor.js counts <video> as invertible imagery, so a
     hit-testable video makes elementFromPoint return it everywhere over the hero
     and the cyan dot is replaced by a hero-sized invert lens. #globeCanvas was
     excluded from that same list for the same reason.
   - max-width:none. tokens.css sets a global video{max-width:100%}, which would
     clamp the deliberate oversize below and expose the blur's soft fringe.
   - NO filter here. The blur is baked into the file: any CSS filter on a <video>
     drops it out of the hardware overlay path and roughly doubles playback power,
     which is not a price worth paying on something that runs forever. */
.hero__bg{
  position:absolute;top:0;left:0;z-index:0;pointer-events:none;
  /* oversized so the blurred edges bleed past the frame instead of vignetting in */
  width:calc(100% + 80px);height:calc(100% + 80px);margin:-40px;max-width:none;
  object-fit:cover;opacity:0;transition:opacity .9s var(--ease);
}
.hero__bg.is-on{opacity:1;}
/* The scrim is what makes the type legible, so it is not optional decoration.
   Measured against the shipped loop frame by frame: the full montage swings from
   Y=22 to Y=234 in the band the headline occupies, which is far wider than any
   single shot, so the midpoint has to sit at .62 to hold 4.5:1. Lighter than that
   (.52 was the first attempt) still clears 3:1 large text but drops the body-text
   bar, and the headline is the one element that must never be marginal. */
.hero__scrim{position:absolute;inset:0;z-index:1;pointer-events:none;
  background:
    linear-gradient(to bottom, rgba(29,30,29,.80) 0%, rgba(29,30,29,.62) 38%, rgba(29,30,29,.80) 100%);
}
/* the hero's own content has to sit above both layers — without this the
   absolutely positioned video paints over the in-flow h1 and logo */
.hero__mark,.hero__title,.hero__cue{position:relative;z-index:2;}

.hero__mark{
  margin-top:clamp(.4rem,2.4vh,1.8rem);color:var(--c-paper);line-height:0;
  opacity:0;transform:translateY(-10px);
  transition:opacity .8s ease .05s,transform .9s var(--ease) .05s;
}
.hero__mark img{height:clamp(30px,4vh,48px);width:auto;}
.hero.in .hero__mark{opacity:1;transform:none;}

.hero__title{
  margin:auto 0;
  font-family:var(--font-display);font-weight:800;
  font-size:var(--fs-display);line-height:.9;letter-spacing:-.022em;
  color:var(--c-paper);text-transform:uppercase;
}
.hero__title .ln{display:block;}
.hero__title .w{
  display:inline-block;
  transform:translateY(1.05em);opacity:0;filter:blur(9px);
  transition:transform .95s var(--ease),opacity .8s ease,filter .9s ease;
  will-change:transform,opacity,filter;
}
.hero.in .hero__title .w{transform:none;opacity:1;filter:blur(0);}

/* Scroll cue at the base of the hero: the word plus the hairline beneath it.
   Grouping them in one wrapper means a single opacity drives both — the word,
   the line and its pulse fade out together as the globe starts to climb. */
.hero__cue{
  position:absolute;left:50%;bottom:clamp(1.2rem,3vh,2.2rem);
  transform:translateX(-50%);
  display:flex;flex-direction:column;align-items:center;gap:clamp(.5rem,1.4vh,.85rem);
  opacity:0;transition:opacity 1s ease 1.1s;
}
.hero.in .hero__cue{opacity:1;}
/* during the intro the cue is driven by scroll, so it needs to track the
   variable promptly rather than run the slow entrance transition */
html.intro-live .hero.in .hero__cue{opacity:var(--cue,1);transition:opacity .25s linear;}
.hero__cueword{
  font-family:var(--font-mono);font-size:var(--fs-label);
  letter-spacing:.26em;text-transform:uppercase;color:var(--c-paper);opacity:.6;
}
.hero__scroll{
  width:1px;height:clamp(34px,6vh,58px);
  background:linear-gradient(var(--c-paper),transparent);
  opacity:.55;
}
.hero.in .hero__scroll{animation:heroPulse 2.6s var(--ease) 1.4s infinite;}
/* the line is a flex child now, not absolutely centred, so the pulse is a
   plain scaleY — the old keyframes carried the translateX(-50%) centring */
@keyframes heroPulse{0%,100%{transform:scaleY(.7);opacity:.18;}50%{transform:scaleY(1);opacity:.5;}}

@media (max-width:640px){
  .hero__title{line-height:.92;letter-spacing:-.015em;}
}

/* ============================================================
   §2 · GLOBE  — tagline + interactive shaded globe · bg #1D1E1D
   (ported from sections/globe, scoped + section-relative)
   ============================================================ */
/* ---------- INTRO STAGE (§1 + §2 pinned together) ----------------------
   The hero is not scrolled away — it is COVERED. Hero and globe are both
   stuck to the top of the viewport inside .intro, whose extra height is the
   scroll runway. Two layers then move in opposite directions as you scroll:
   the globe climbs in from the bottom, then the dark panel drops from above
   and swallows the header. Scroll back up and it all reverses, because both
   layers are pure functions of scroll position — there is no timeline, no
   playback state, nothing to get out of sync.

   Everything is gated on html.intro-live, which main.js adds only when it is
   actually driving the layers. Without JS (or with reduced motion) .intro is
   an inert wrapper and the two sections stack normally. */
.intro{position:relative;background:var(--c-ink);}   /* matches the hero base so the pin has no seam */
html.intro-live .intro{height:230svh;}
/* Both pin BELOW the sticky ticker, not under it — otherwise the bar covers
   the top of the hero (and the AUGMENT mark) once they are pinned. */
html.intro-live #hero{position:sticky;top:var(--ticker-h,0px);z-index:0;}
html.intro-live #globe{position:sticky;top:var(--ticker-h,0px);z-index:1;
  /* pull the globe up onto the hero so both pin to the same place */
  margin-top:calc(-1 * (100svh - var(--ticker-h,0px)));}
/* --rise / --away are 1 when fully displaced, 0 when seated. main.js writes
   them; CSS owns the geometry so the .g-copy centring translate survives. */
html.intro-live #globe .g-stage{transform:translate3d(0,calc(var(--rise,0) * 100%),0);}
html.intro-live #globe .g-bg   {transform:translate3d(0,calc(var(--away,0) * -100%),0);}
html.intro-live #globe .g-copy {transform:translate3d(var(--copy-x,-50%),calc(var(--away,0) * -100svh),0);}
/* (.g-tagline / .g-hint entrance animations are switched off further down —
    they have to be, since animations beat normal declarations) */

/* the section itself is transparent — .g-bg carries the colour so it can move */
/* height excludes the sticky ticker so the section fills exactly the space
   left under the bar rather than overhanging the viewport by its height */
#globe{position:relative;height:calc(100svh - var(--ticker-h,0px));min-height:560px;background:transparent;overflow:hidden;isolation:isolate;}
#globe .g-bg{position:absolute;inset:0;z-index:1;background:var(--c-ink);will-change:transform;}

/* --copy-x is the horizontal offset this layout needs. It is published as a
   variable so the intro can add its vertical motion without having to know
   which layout is active — the mobile rule below centres with left/right
   instead and sets it to 0. */
#globe .g-copy{position:absolute;z-index:5;left:50%;--copy-x:-50%;transform:translateX(var(--copy-x));top:clamp(3.4rem,8.5vh,6.4rem);width:min(92vw,950px);text-align:center;pointer-events:none;}
#globe .g-tagline{font-family:var(--font-sans);font-weight:500;font-size:clamp(1.45rem,3.55vw,3.05rem);line-height:1.22;letter-spacing:-.01em;color:#727272;text-wrap:balance;}
#globe .g-tagline .em{font-style:italic;color:var(--c-g2);}
#globe .g-tagline b{font-weight:600;color:var(--c-g2);}

#globe .g-stage{position:absolute;inset:0;z-index:2;}
#globe #globeCanvas{position:absolute;inset:0;width:100%;height:100%;display:block;touch-action:none;cursor:grab;}
#globe #globeCanvas.drag{cursor:grabbing;}
/* Touch devices: the canvas spans the WHOLE section, so touch-action on it is
   all-or-nothing — none traps the page scroll, pan-y gives vertical away and
   the globe can't be dragged up/down. Instead globe.js lays a hit surface over
   the stage and clip-paths it to the sphere (clip-path clips hit-testing too).
   Only that circle gets touch-action:none, so a drag ON the globe is fully
   captured and the page holds still, while a swipe anywhere else in the
   section passes straight through the canvas and scrolls as normal. */
#globe .g-hit{position:absolute;inset:0;z-index:2;display:none;}
@media (pointer:coarse){
  /* On a phone the sphere is ~2.3x the viewport WIDTH, so below its top edge
     the whole screen is globe — there is no gutter to scroll in. That rules
     out both naive options: touch-action:none traps the page (you cannot
     scroll past), and pan-y makes the globe undraggable vertically AND lets a
     vertical drag rewind the reveal, which is worse.

     So we take the gesture and route it ourselves (see bindDrag in globe.js):
     horizontal drags spin the sphere, vertical drags scroll the page 1:1 with
     the finger. touch-action:none is required for that — it is what stops the
     browser stealing the gesture before we can classify it. */
  #globe .g-hit{display:block;touch-action:none;}
  #globe #globeCanvas{touch-action:auto;pointer-events:none;}
  /* While the reveal is still carrying the globe up, scrolling is the ONLY
     thing the user can be doing — hand the touch straight back to the page. */
  html.globe-hold #globe .g-hit{pointer-events:none;}
}
#globe .g-pins{position:absolute;inset:0;z-index:3;pointer-events:none;}

#globe .pin{position:absolute;left:0;top:0;width:32px;height:40px;margin:-40px 0 0 -16px;pointer-events:auto;cursor:pointer;will-change:transform,opacity;transform-origin:50% 100%;-webkit-tap-highlight-color:transparent;}
/* Each pin permanently wears its event's brand colour (globe.js sets
   --pin-brand, already legibility-checked against the dark sphere). The dark
   drop-shadow doubles as a separator so pale brands don't melt into the land. */
#globe .pin .tri{position:absolute;left:50%;bottom:0;transform:translateX(-50%);transform-origin:50% 100%;
  width:20px;height:26px;background:var(--pin-brand,var(--c-g3));
  clip-path:polygon(50% 100%,0 0,100% 0);
  filter:drop-shadow(0 1px 2px rgba(0,0,0,.85)) drop-shadow(0 3px 7px rgba(0,0,0,.45));
  transition:background .2s var(--ease),transform .24s var(--ease),filter .24s var(--ease);}
/* small bright core so every pin reads as a marker, whatever its brand hue */
#globe .pin .tri::after{content:"";position:absolute;left:50%;top:5px;width:6px;height:6px;
  margin-left:-3px;border-radius:50%;background:rgba(255,255,255,.92);
  transition:opacity .2s var(--ease);}
#globe .pin .ring{position:absolute;left:50%;bottom:1px;margin-left:-11px;width:22px;height:22px;border:1.5px solid var(--pin-brand,var(--c-g2));border-radius:50%;opacity:0;pointer-events:none;}
#globe .pin:hover .tri,#globe .pin.active .tri{transform:translateX(-50%) scale(1.34) translateY(-2px);
  filter:drop-shadow(0 2px 3px rgba(0,0,0,.9)) drop-shadow(0 6px 16px rgba(0,0,0,.55));}
#globe .pin:hover .tri::after,#globe .pin.active .tri::after{opacity:0;}
#globe .pin:hover .ring,#globe .pin.active .ring{opacity:.7;animation:ringPulse 1.5s var(--ease) infinite;}
@keyframes ringPulse{0%{transform:scale(.5);opacity:.85}70%{transform:scale(2.3);opacity:0}100%{opacity:0}}

#globe .g-card{position:absolute;z-index:8;left:0;top:0;width:min(88vw,372px);background:rgba(14,15,15,.93);border:1px solid rgba(140,140,140,.2);border-radius:0;padding:4px;backdrop-filter:blur(16px) saturate(1.05);-webkit-backdrop-filter:blur(16px) saturate(1.05);box-shadow:0 18px 48px -14px rgba(0,0,0,.7);opacity:0;transform:translateY(6px) scale(.98);transform-origin:50% 100%;transition:opacity .2s var(--ease),transform .2s var(--ease);pointer-events:none;}
#globe .g-card.show{opacity:1;transform:none;pointer-events:auto;}
#globe .g-card .chead{display:flex;align-items:baseline;justify-content:space-between;gap:.8em;padding:14px 16px 11px;}
#globe .g-card .chead b{font-family:var(--font-display);font-weight:600;font-size:1.16rem;line-height:1.1;letter-spacing:-.012em;color:var(--bl,#fff);}
#globe .g-card .chead span{flex:none;font-family:var(--font-mono);font-size:.56rem;letter-spacing:.14em;text-transform:uppercase;color:#8f8f8f;}
#globe .g-card .evt{display:block;padding:12px 16px 14px;border-radius:0;transition:background .18s var(--ease);}
#globe .g-card .evt:hover{background:rgba(255,255,255,.05);}
/* Optional event photo, directly under the title and above the logo. Renders
   only when the event carries an `image` in events.json, so events without one
   look exactly as they did before. 16:9 is reserved up front via aspect-ratio,
   so the card doesn't jump height as the photo decodes. */
#globe .g-card .ephoto{display:block;margin:.6rem 0 .15rem;border-radius:0;overflow:hidden;
  background:var(--c-ink-2);aspect-ratio:16/9;}
#globe .g-card .ephoto img{display:block;width:100%;height:100%;object-fit:cover;}
/* Photos make cards much taller — London stacks three events. Cap the BODY
   (not the card) so tall cards scroll internally; the caret is a sibling of
   the body, so it stays unclipped. */
#globe #gCardBody{max-height:min(68vh,560px);overflow-y:auto;overscroll-behavior:contain;
  scrollbar-width:thin;scrollbar-color:rgba(255,255,255,.22) transparent;}
#globe #gCardBody::-webkit-scrollbar{width:6px;}
#globe #gCardBody::-webkit-scrollbar-thumb{background:rgba(255,255,255,.22);border-radius:0;}

/* logo sits under the event title, rendered large. The wrapper reserves the
   band so a missing/failed logo collapses cleanly instead of leaving a hole. */
#globe .g-card .elogowrap{display:block;margin:.55rem 0 .5rem;}
#globe .g-card .elogo{height:46px;width:auto;max-width:100%;object-fit:contain;object-position:left center;opacity:.95;display:block;}
#globe .g-card .ename{display:block;font-family:var(--font-sans);font-weight:600;font-size:.98rem;line-height:1.28;letter-spacing:-.004em;color:var(--bl,#fff);}
#globe .g-card .ename em{font-style:italic;}
#globe .g-card .evt:hover .ename{text-decoration:underline;text-underline-offset:3px;}
#globe .g-card .dist{display:block;font-family:var(--font-mono);font-size:.55rem;letter-spacing:.13em;text-transform:uppercase;color:#8f8f8f;margin:.5rem 0 .35rem;}
#globe .g-card .blurb{display:block;font-family:var(--font-sans);font-size:.8rem;line-height:1.5;color:#9a9a9a;}
#globe .g-card .g-caret{position:absolute;width:10px;height:10px;background:rgba(17,18,17,.84);border:1px solid rgba(122,122,122,.26);border-top:none;border-left:none;transform:rotate(45deg);left:calc(50% - 5px);bottom:-6px;}
#globe .g-card.below .g-caret{top:-6px;bottom:auto;border:1px solid rgba(122,122,122,.26);border-bottom:none;border-right:none;}

#globe .g-hint{position:absolute;right:clamp(1.2rem,4vw,3.4rem);bottom:clamp(1.1rem,3vh,2rem);z-index:5;font-family:var(--font-mono);font-size:var(--fs-label);letter-spacing:.16em;text-transform:uppercase;color:var(--c-g1);}

/* intro (fires when the section scrolls into view) */
#globe .g-tagline{opacity:0;transform:translateY(18px);}
#globe .g-hint{opacity:0;}
#globe.ready .g-tagline{animation:gRise 1.05s var(--ease) .15s forwards;}
#globe.ready .g-hint{animation:gFade 1s ease .7s forwards;}
@keyframes gRise{to{opacity:1;transform:none;}}
@keyframes gFade{to{opacity:1;}}
@media (prefers-reduced-motion:reduce){#globe .g-tagline,#globe .g-hint{opacity:1!important;transform:none!important;animation:none!important;}}

/* Under the scroll-driven intro these two are placed by scroll position, not by
   their own entrance animations, so the animations are switched OFF rather than
   out-specified — a running animation wins over any normal declaration however
   specific. The .ready variants are listed too: they outrank #globe.ready on
   their own, and being later in the file settles any tie. */
html.intro-live #globe .g-tagline,
html.intro-live #globe.ready .g-tagline{animation:none;opacity:1;transform:none;}
html.intro-live #globe .g-hint,
html.intro-live #globe.ready .g-hint{animation:none;opacity:var(--hint,1);}

@media (max-width:720px){
  #globe .g-copy{top:clamp(3.8rem,8vh,5.5rem);left:4.5vw;right:4.5vw;width:auto;--copy-x:0px;transform:none;}
  #globe .g-tagline{font-size:clamp(1.05rem,4.7vw,1.5rem);line-height:1.26;}
  #globe .g-hint{left:50%;right:auto;transform:translateX(-50%);}
}

/* ============================================================
   MARQUEE (shared by §3 logos + §7 separator) — constant px/s
   ============================================================ */
/* ============================================================
   §0a · INTRO VIDEO — a short montage that covers the page on
   arrival, then blasts out of frame to hand over to the header.

   Hidden by default so a visitor without JS (or one who has
   already seen it this session) never gets a black rectangle.
   The inline script in <head> opts in by putting .introv-on on
   <html> before first paint, which is what stops the hero
   flashing up for a frame first. intro-video.js then adds
   .is-live to the overlay itself, so the class that locks the
   scroll can be dropped independently of the one that shows it.
   ============================================================ */
.intro-v{display:none;}
html.introv-on{overflow:hidden;}
html.introv-on body{overflow:hidden;}
html.introv-on .intro-v,
.intro-v.is-live{display:block;position:fixed;inset:0;
  /* above the trail (8000), below the custom cursor (9000) so the
     cursor keeps working over the video */
  z-index:8500;background:var(--c-ink);
  transform-origin:50% 50%;will-change:transform,opacity,filter;}
.intro-v__media{width:100%;height:100%;object-fit:cover;display:block;}
/* the source is 16:9 — on a portrait phone `cover` would crop away three
   quarters of every frame, so letterbox it on the ink instead */
@media (max-aspect-ratio:1/1){ .intro-v__media{object-fit:contain;} }
/* The hand-off. This used to blast the frame out to white, which made sense when
   the montage simply left. It now settles INTO the looping hero background
   instead: a gentle push forward while the blur converges on the loop's own baked
   blur and the frame dissolves, revealing the loop already running underneath.
   No brightness blowout — the hero behind it is ink, so a flash to white would
   read as a glitch rather than a flourish. */
.intro-v.is-out{pointer-events:none;
  animation:introSettle 900ms cubic-bezier(.22,1,.36,1) forwards;}
@keyframes introSettle{
  0%  {transform:scale(1);    opacity:1; filter:blur(0px);}
  100%{transform:scale(1.08); opacity:0; filter:blur(16px);}
}
.intro-v__skip{position:absolute;right:clamp(1rem,3vw,2rem);bottom:clamp(1rem,3vw,2rem);
  z-index:2;appearance:none;border:1px solid rgba(255,255,255,.45);background:transparent;
  color:#fff;font-family:var(--font-mono);font-size:.66rem;letter-spacing:.18em;
  text-transform:uppercase;padding:.6em 1.1em;cursor:pointer;border-radius:0;
  transition:background .25s var(--ease),border-color .25s var(--ease);}
.intro-v__skip:hover,.intro-v__skip:focus-visible{background:rgba(255,255,255,.14);border-color:#fff;}
/* belt and braces — the head script already declines to opt in under
   reduced motion, but if the preference flips mid-session, release it */
@media (prefers-reduced-motion:reduce){
  html.introv-on{overflow:auto;}
  html.introv-on body{overflow:auto;}
  .intro-v,html.introv-on .intro-v,.intro-v.is-live{display:none;}
}

/* ============================================================
   §0 · TICKER — thin one-liner bar above the hero. Reuses the
   shared marquee engine (buildMarquee in main.js clones the items
   until the track overflows, then duplicates for a seamless loop).
   ============================================================ */
/* Sticky, so the one-liners ride along for the whole site. sticky (not fixed)
   keeps the bar in normal flow, so it still occupies its own strip at the top
   and nothing below needs padding to compensate. z-index sits above the page
   but below the custom cursor (9000). */
.ticker{position:sticky;top:0;z-index:900;background:var(--c-ink);color:var(--c-g2);}
.ticker .marquee{
  /* the shared marquee fades its edges; here the bar runs full-bleed
     edge to edge, so drop the mask */
  -webkit-mask-image:none;mask-image:none;}
.ticker__track{gap:clamp(1.6rem,3.4vw,3.2rem);
  padding-block:clamp(.42rem,.85vh,.62rem);}
.ticker__item{font-family:var(--font-mono);font-size:clamp(.6rem,.72vw,.74rem);
  letter-spacing:.19em;text-transform:uppercase;white-space:nowrap;line-height:1;
  color:var(--c-g2);}
.ticker__sep{font-size:clamp(.5rem,.6vw,.62rem);line-height:1;
  color:color-mix(in srgb,var(--c-g1) 85%,transparent);}

.marquee{position:relative;overflow:hidden;
  -webkit-mask-image:linear-gradient(90deg,transparent 0,#000 7%,#000 93%,transparent 100%);
          mask-image:linear-gradient(90deg,transparent 0,#000 7%,#000 93%,transparent 100%);}
.marquee__track{display:flex;align-items:center;width:max-content;list-style:none;will-change:transform;animation:marqueeX linear infinite;}
.marquee[data-dir="right"] .marquee__track{animation-direction:reverse;}
@keyframes marqueeX{from{transform:translateX(0)}to{transform:translateX(-50%)}}
@media (prefers-reduced-motion:reduce){.marquee__track{animation:none;}}

/* ============================================================
   §3 · LOGO CAROUSEL — three opposed rows of mono client logos
   ============================================================ */
/* logo carousel — a comfortably-spaced band (not a full-screen panel) */
#logos{padding-block:clamp(3.5rem,8vh,7rem);}
#logos .logos-inner{display:flex;flex-direction:column;gap:clamp(1.6rem,4.2vh,3.4rem);}
#logos .logo{flex:0 0 auto;display:grid;place-items:center;padding-inline:clamp(1.5rem,3.4vw,3.4rem);}
#logos .logo img{height:clamp(28px,3vw,2.75rem);width:auto;max-width:13.1rem;object-fit:contain;
  opacity:.72;transition:opacity .35s var(--ease);}
#logos .logo:hover img{opacity:1;}

/* ============================================================
   §4 · DESCRIPTION — Figma frame 12: full-bleed grid composition.
   The 1512×2123 design canvas is a fixed-aspect container; every
   line / cell / caption sits at its exact Figma coordinate in %,
   type sizes in cqw so the whole composition scales as one piece.
   about-grid.js drives entrance + cursor parallax via --tx/--ty,
   so entrance transforms (on inner elements) stay independent.
   ============================================================ */
#about{overflow:hidden;padding-top:clamp(1.5rem,4.5vh,4.5rem);padding-bottom:var(--sec-pad);}
.a12{position:relative;aspect-ratio:1512/2123;width:100%;container-type:inline-size;}

/* parallax layers — JS writes --tx/--ty on [data-px] wrappers */
.a12 [data-px]{transform:translate3d(var(--tx,0px),var(--ty,0px),0);will-change:transform;}

/* hairline grid (page-level vectors in Figma render above content) */
/* fade the grid softly at the section's top & bottom edges so the lines that run
   off the canvas don't end on a hard cut */
.a12__lines{position:absolute;inset:0;z-index:3;pointer-events:none;
  -webkit-mask-image:linear-gradient(to bottom,transparent 0,#000 3%,#000 97%,transparent 100%);
          mask-image:linear-gradient(to bottom,transparent 0,#000 3%,#000 97%,transparent 100%);}
.a12__lines i{position:absolute;display:block;background:var(--c-g1);}
.a12__lines .lh{top:var(--p);left:var(--a);width:calc(var(--b) - var(--a));height:1px;
  transform:scaleX(0);transform-origin:left center;transition:transform 1.2s var(--ease) var(--d,0s);}
.a12__lines .lv{left:var(--p);top:var(--a);height:calc(var(--b) - var(--a));width:1px;
  transform:scaleY(0);transform-origin:center top;transition:transform 1.2s var(--ease) var(--d,0s);}
.a12.in .a12__lines .lh{transform:scaleX(1);}
.a12.in .a12__lines .lv{transform:scaleY(1);}

/* image cells (exact Figma boxes). bg = section ink so a not-yet-revealed cell
   blends into §4 instead of flashing a darker black on refresh */
.a12__cell{position:absolute;z-index:2;margin:0;overflow:hidden;background:var(--c-ink);}
.a12__cell img,.a12__cell video{width:100%;height:100%;object-fit:cover;display:block;
  opacity:0;transform:translateY(8%) scale(1.06);
  transition:opacity 1s ease var(--d,0s),transform 1.35s var(--ease) var(--d,0s);}
.a12.in .a12__cell img,.a12.in .a12__cell video{opacity:1;transform:none;}
@media (hover:hover){
  .a12__cell:hover img,.a12__cell:hover video{transform:scale(1.045);transition:transform .8s var(--ease);}
}
.a12__street{left:5.5556%;top:3.0146%;width:36.7063%;height:30.4757%;--d:.15s;}
.a12__coastal{left:59.0608%;top:19.0297%;width:33.7302%;height:32.9251%;--d:.3s;}
.a12__portrait{left:16.6667%;top:49.0815%;width:36.5741%;height:37.0231%;--d:.45s;}

/* caption blocks — sized so the longer copy fits each slot without overlapping
   the neighbouring image (the --r block above the coastal image is the tightest) */
/* The box edges stay locked to the grid lines (see the positions below), while
   padding insets the TEXT so it never touches a rule. box-sizing:border-box
   keeps the box width at 31.5476%, so alignment is unaffected; em units keep the
   inset proportional to the fluid caption size. */
.a12__cap{position:absolute;z-index:2;width:31.5476%;margin:0;
  padding:.85em 1em;
  font-family:var(--font-sans);font-weight:400;font-size:max(9px,1.04cqw);line-height:1.26;
  color:var(--c-g1);opacity:0;transition:opacity 1s ease var(--d,.5s);}
.a12.in .a12__cap{opacity:1;}
/* left edges flush to the column grid lines (same as the images above/below).
   --l and --b sit BELOW their images (top flush to the image bottom); --r sits
   ABOVE the coastal image, so it's bottom-anchored to hug the coastal's top edge
   (bottom:80.9703% == top:19.0297%, the coastal top / grid line) regardless of
   how long the copy is. */
.a12__cap--r{left:59.0608%;bottom:80.9703%;--d:.35s;}
.a12__cap--l{left:5.5556%;top:33.4904%;--d:.45s;}
.a12__cap--b{left:16.6667%;top:86.1046%;--d:.55s;}

/* giant A — Neue Power Heavy 901.78px @1512, cut by the x=1403 line */
.a12__aclip{position:absolute;z-index:1;overflow:hidden;pointer-events:none;
  left:59.0608%;top:51.9548%;width:33.7302%;height:34.1498%;}
.a12__A{position:absolute;left:-1.1905cqw;top:-8.1217cqw;
  font-family:var(--font-display);font-weight:700;font-size:59.6419cqw;line-height:1.3;
  color:var(--c-g1);
  opacity:0;transform:translateY(6%);
  transition:opacity 1.1s ease .5s,transform 1.4s var(--ease) .5s;}
.a12.in .a12__A{opacity:1;transform:none;}

@media (prefers-reduced-motion:reduce){
  .a12__lines .lh,.a12__lines .lv{transform:none!important;transition:none!important;}
  .a12__cell img,.a12__cell video,.a12__cap,.a12__A{opacity:1!important;transform:none!important;transition:none!important;}
}

/* mobile: the canvas proportions collapse — stack image → caption pairs, no A */
@media (max-width:760px){
  .a12{aspect-ratio:auto;display:flex;flex-direction:column;gap:1.4rem;
    padding:2.2rem var(--gutter) 0;}
  .a12__lines,.a12__aclip{display:none;}
  .a12__cell{position:static;width:100%;}
  .a12__street{order:1;aspect-ratio:555/646;}
  .a12__cap--l{order:2;}
  .a12__coastal{order:3;aspect-ratio:510/699;}
  .a12__cap--r{order:4;}
  .a12__portrait{order:5;aspect-ratio:553/786;}
  .a12__cap--b{order:6;}
  /* pull each caption toward its image, push the next image away */
  /* stacked flow, and the grid rules are hidden here — so no inset needed */
  .a12__cap{position:static;width:auto;max-width:42ch;font-size:.95rem;color:var(--c-g2);
    padding:0;margin:-.3rem 0 1.2rem;}
  .a12__cap--b{margin-bottom:0;}
}

/* ============================================================
   §5 · PORTFOLIO — horizontal gallery, paging arrows, count
   ============================================================ */
/* Full-viewport panel: the whole section (PORTFOLIOS heading → bottom of the
   cards → arrows) fits in one screen height. The card row flexes to fill the
   leftover vertical space and shows EXACTLY 3 cards, none cut off; the rest
   scroll in horizontally. --pf-gap is shared by the track gap and the card width
   math so 3 cards + 2 gaps span the viewport precisely. */
#portfolio{--pf-gap:clamp(.8rem,1.3vw,1.4rem);overflow:hidden;}
/* tight chrome (padding/heading/foot) so the card row claims most of the height
   → cards stand tall as vertical rectangles rather than near-squares */
#portfolio .pf{max-width:var(--maxw);margin-inline:auto;
  padding:clamp(1.6rem,3.5vh,3.4rem) var(--gutter) clamp(1.2rem,2.6vh,2.4rem);
  min-height:100svh;display:flex;flex-direction:column;}
/* heading: number + PORTFOLIOS sit ABOVE the carousel */
#portfolio .pf-head{flex:none;display:flex;align-items:flex-end;justify-content:space-between;gap:1rem;
  margin-bottom:clamp(.7rem,1.5vh,1.2rem);}
#portfolio .pf-nav{display:flex;gap:.6rem;}
#portfolio .pf-arrow{width:clamp(42px,3.1vw,54px);aspect-ratio:1;border:1px solid color-mix(in srgb,var(--c-g1) 70%,transparent);border-radius:0;color:var(--c-g2);display:grid;place-items:center;transition:background .3s var(--ease),color .3s,border-color .3s,transform .3s;}
#portfolio .pf-arrow svg{width:42%;height:42%;}
#portfolio .pf-arrow:hover{background:var(--c-white);color:var(--c-ink);border-color:var(--c-white);}
#portfolio .pf-arrow:disabled{opacity:.3;pointer-events:none;}

/* divider lines: above the carousel, and above the PORTFOLIOS text */
#portfolio .pf-rule{height:1px;width:100%;background:color-mix(in srgb,var(--c-white) 15%,transparent);}
#portfolio .pf-rule--top{flex:none;margin-bottom:clamp(.9rem,1.8vh,1.6rem);}

/* Cards are a FIXED portrait aspect ratio and fill the row's height; their
   width is derived from that height. So however many fit across the viewport,
   fit — ~3 on a normal screen, more on wide/ultrawide monitors — and every card
   stays the same tall rectangle instead of stretching toward a square as the
   screen widens. flex:1 makes the row claim the leftover column height. */
#portfolio .pf-viewport{flex:1 1 0;min-height:0;
  overflow-x:auto;overflow-y:hidden;scroll-snap-type:x proximity;scrollbar-width:none;-ms-overflow-style:none;}
#portfolio .pf-viewport::-webkit-scrollbar{display:none;}
#portfolio .pf-track{display:flex;gap:var(--pf-gap);list-style:none;width:max-content;height:100%;}
#portfolio .pf-card{position:relative;flex:0 0 auto;width:auto;height:100%;aspect-ratio:.62;
  overflow:hidden;border-radius:0;background:#141514;scroll-snap-align:start;}
#portfolio .pf-card a{display:block;width:100%;height:100%;}
/* absolute so the image can't inflate the card's width — the aspect-ratio alone
   drives width off the definite height */
#portfolio .pf-card img{position:absolute;inset:0;width:100%;height:100%;object-fit:cover;object-position:center;
  filter:grayscale(1);transition:transform .7s var(--ease),filter .6s var(--ease);}
/* preview covers sit in black & white, then bloom to full colour + zoom on hover;
   a grey "view" badge pops in the corner */
#portfolio .pf-card:hover img,#portfolio .pf-card:focus-within img{transform:scale(1.06);filter:grayscale(0);}
#portfolio .pf-card::after{content:"↗";position:absolute;z-index:3;right:16px;bottom:16px;
  width:2.5em;height:2.5em;display:grid;place-items:center;line-height:1;font-weight:700;
  font-size:clamp(.9rem,1.15vw,1.1rem);color:var(--c-ink);background:var(--c-g3);border-radius:0;
  opacity:0;transform:translateY(12px) scale(.6);pointer-events:none;
  transition:opacity .4s var(--ease),transform .55s var(--ease);}
#portfolio .pf-card:hover::after,#portfolio .pf-card:focus-within::after{opacity:1;transform:none;}
/* project name over the foot of each cover, so the carousel reads as an index */
#portfolio .pf-card__cap{position:absolute;z-index:2;left:0;right:0;bottom:0;
  padding:clamp(2.6rem,5vw,3.4rem) clamp(.9rem,1.4vw,1.3rem) clamp(.85rem,1.3vw,1.15rem);
  font-family:var(--font-mono);font-size:var(--fs-label);letter-spacing:.14em;
  text-transform:uppercase;color:var(--c-white);line-height:1.3;
  background:linear-gradient(to top,rgba(0,0,0,.78),rgba(0,0,0,.42) 46%,transparent);
  pointer-events:none;}

/* foot: paging arrows sit BENEATH the carousel, right-aligned */
#portfolio .pf-foot{flex:none;margin-top:clamp(.9rem,1.8vh,1.6rem);}
#portfolio .pf-foot .pf-rule{margin-bottom:clamp(.7rem,1.3vh,1.1rem);}
#portfolio .pf-foot .pf-nav{justify-content:flex-end;}
#portfolio .pf-count__num{font-family:var(--font-display);font-weight:700;font-size:clamp(2.4rem,5vw,4.4rem);line-height:.9;color:var(--c-g2);}
#portfolio .pf-title{font-family:var(--font-display);font-weight:700;font-size:clamp(2.2rem,6.4vw,5.4rem);line-height:.9;letter-spacing:-.02em;color:var(--c-g1);}

@media (max-width:640px){
  /* phones: don't force a full-height panel — auto height, ~1.4 portrait cards */
  #portfolio .pf{min-height:0;padding-top:var(--sec-pad);padding-bottom:clamp(2.5rem,8vw,4rem);}
  /* the desktop rule is flex:1 1 0, which fills the leftover height of the
     100svh panel. Here .pf is auto-height, so a 0 flex-basis leaves no free
     space to grow into and the row collapses to 0px — taking the cards with it.
     Size it from its content instead. */
  #portfolio .pf-viewport{flex:0 0 auto;container-type:normal;}
  #portfolio .pf-track{height:auto;}
  #portfolio .pf-card{flex:0 0 auto;width:68vw;height:auto;aspect-ratio:0.64;}
  /* 11vw made "CASE STUDIES" wider than the space left beside the 04 counter,
     so it wrapped to two lines and the counter floated oddly against it.
     9vw keeps it on one line down to ~320px. */
  #portfolio .pf-title{font-size:clamp(1.6rem,9vw,3.2rem);}
}
/* ultrawide: the portfolio stretches edge-to-edge so the wider viewport fits
   MORE of the fixed-ratio cards (instead of capping at --maxw and showing 3) */
@media (min-width:1600px){
  #portfolio .pf{max-width:none;}
}

/* ---------- touch targets ----------
   On touch devices several controls render smaller than the ~44px minimum
   comfortable tap size. Grow the HIT AREA with a centred pseudo-element so the
   visual design is untouched — nothing moves, the targets just get easier to
   hit (the globe pins especially, since tapping them is the whole interaction). */
@media (pointer:coarse){
  #globe .pin::after,
  .hero__mark::after,
  .footer__y11mark::after,
  .footer__augmark::after,
  #portfolio .pf-arrow::after{
    content:"";position:absolute;left:50%;top:50%;transform:translate(-50%,-50%);
    width:max(100%,44px);height:max(100%,44px);}
  .hero__mark,.footer__y11mark,.footer__augmark,#portfolio .pf-arrow{position:relative;}
}

/* ============================================================
   §6 · CAPABILITIES — accordion + cyan hover-reveal box
   (lives in .grad-zone: bg is the gradient top ≈ #727272)
   ============================================================ */
#capabilities{color:var(--c-ink);}
#capabilities .cap{display:grid;grid-template-columns:1.05fr .95fr;gap:clamp(2rem,5vw,5.5rem);
  max-width:var(--maxw);margin-inline:auto;padding:clamp(9rem,18vh,15rem) var(--gutter) clamp(6.5rem,13vh,11rem);align-items:stretch;
  /* 3D stage for the cyan plane — children tilt toward the cursor */
  perspective:1200px;perspective-origin:50% 42%;}
#capabilities .cap__left{display:flex;flex-direction:column;}
#capabilities .cap__title{font-family:var(--font-display);font-weight:700;text-transform:uppercase;
  font-size:var(--fs-h2);line-height:.98;letter-spacing:-.015em;color:var(--c-ink);}
#capabilities .cap__list{list-style:none;margin-top:clamp(2rem,4.5vw,3.6rem);
  border-bottom:1px solid color-mix(in srgb,var(--c-ink) 26%,transparent);}
#capabilities .cap__item{border-top:1px solid color-mix(in srgb,var(--c-ink) 26%,transparent);}
#capabilities .cap__row{position:relative;display:flex;align-items:center;justify-content:space-between;gap:1rem;
  width:100%;text-align:left;padding:clamp(.85rem,1.7vw,1.35rem) 0;cursor:pointer;outline:none;
  background:none;border:0;font:inherit;color:inherit;}
/* the thin dark line that wipes in L→R on hover */
#capabilities .cap__row::after{content:"";position:absolute;top:-1px;left:0;right:0;height:2px;
  background:var(--c-ink);transform:scaleX(0);transform-origin:left;transition:transform .5s var(--ease);}
#capabilities .cap__row:hover::after,#capabilities .cap__row:focus-visible::after,#capabilities .cap__row.is-active::after{transform:scaleX(1);}
#capabilities .cap__rowtitle{font-family:var(--font-sans);font-weight:500;
  font-size:clamp(1.05rem,1.5vw,1.5rem);letter-spacing:-.005em;color:var(--c-ink);transition:opacity .3s;}
#capabilities .cap__chev{display:grid;place-items:center;color:var(--c-ink);opacity:.85;transition:transform .35s var(--ease);}
#capabilities .cap__chev svg{width:clamp(20px,1.5vw,26px);height:auto;}
#capabilities .cap__row:hover .cap__chev,#capabilities .cap__row.is-active .cap__chev{transform:translateX(5px);}
#capabilities .cap__row:not(:hover):not(.is-active) .cap__rowtitle{opacity:.78;}
#capabilities .cap__row.is-active .cap__chev{transform:rotate(90deg);}
/* the intro now sits directly under the title, above the questions */
#capabilities .cap__intro{margin-top:clamp(1.1rem,2.4vw,1.7rem);max-width:52ch;
  font-family:var(--font-sans);font-size:clamp(.9rem,1.08vw,1.12rem);line-height:1.55;
  color:color-mix(in srgb,var(--c-ink) 80%,transparent);}

/* accordion panel — answer drops down beneath its own question.
   grid-template-rows 0fr→1fr animates to the content's natural height
   without needing a JS-measured max-height. */
#capabilities .cap__panel{display:grid;grid-template-rows:0fr;
  transition:grid-template-rows .5s var(--ease);}
/* the clipped grid item must carry NO padding of its own — padding is an
   incompressible minimum contribution and would hold the row open (it kept
   the closed panels 24px tall). All spacing lives on the <p> inside. */
#capabilities .cap__panelinner{overflow:hidden;min-height:0;padding:0;}
#capabilities .cap__item.is-open .cap__panel{grid-template-rows:1fr;}
#capabilities .cap__answer{margin:0;
  padding:0 0 clamp(1rem,2vw,1.5rem);max-width:56ch;
  font-family:var(--font-sans);font-weight:400;
  font-size:clamp(.92rem,1.12vw,1.15rem);line-height:1.55;
  color:color-mix(in srgb,var(--c-ink) 82%,transparent);
  opacity:0;transform:translateY(-6px);
  transition:opacity .45s var(--ease) .06s,transform .45s var(--ease) .06s;}
#capabilities .cap__item.is-open .cap__answer{opacity:1;transform:none;}

/* cyan box — a brushed-metallic 3D plane. On desktop it's a SQUARE that faces
   the cursor and pitches as you scroll past it (rx/ry/pz driven from JS). On
   mobile it becomes a rectangle with a static upward tilt + idle float. */
#capabilities .cap__box{position:relative;overflow:hidden;border-radius:0;isolation:isolate;
  align-self:center;width:100%;max-width:28rem;margin-inline:auto;
  aspect-ratio:1;padding:clamp(1.4rem,2.4vw,2.4rem);
  container-type:inline-size;
  /* anodized cyan with faint brushed striations = metallic surface */
  background:
    repeating-linear-gradient(116deg, rgba(255,255,255,.05) 0 1px, rgba(0,0,0,0) 1px 7px),
    linear-gradient(135deg,
      color-mix(in srgb,var(--c-cyan) 62%,#fff) 0%,
      var(--c-cyan) 42%,
      color-mix(in srgb,var(--c-cyan) 90%,#06303f) 100%);
  transform:rotateX(var(--rx,0deg)) rotateY(var(--ry,0deg)) translateZ(var(--pz,0px));
  transform-origin:50% 50%;backface-visibility:hidden;will-change:transform;
  /* beveled metal edge (bright top rim, subtle inner rim, deep base) + cast shadow */
  box-shadow:
    inset 0 1px 0 rgba(255,255,255,.5),
    inset 0 0 0 1px rgba(255,255,255,.08),
    inset 0 -12px 28px -16px rgba(0,30,32,.5),
    0 24px 55px -34px rgba(0,0,0,.5);
  transition:box-shadow .45s var(--ease);}
#capabilities .cap__box.tilting{box-shadow:
    inset 0 1px 0 rgba(255,255,255,.6),
    inset 0 0 0 1px rgba(255,255,255,.1),
    inset 0 -12px 28px -16px rgba(0,30,32,.55),
    0 42px 82px -38px rgba(0,0,0,.58);}
/* static metallic top glare — fixed highlight (no cursor tracking) */
#capabilities .cap__sheen{position:absolute;inset:0;z-index:0;pointer-events:none;border-radius:inherit;
  opacity:.55;mix-blend-mode:screen;
  background:radial-gradient(75% 58% at 50% -12%, rgba(255,255,255,.5), rgba(255,255,255,0) 62%);}
/* top line wipes in L→R while a row is hovered (box mirrors the row hover) */
#capabilities .cap__box::before{content:"";position:absolute;top:0;left:0;right:0;height:2px;z-index:4;
  background:var(--c-ink);transform:scaleX(0);transform-origin:left;transition:transform .5s var(--ease);}
#capabilities .cap__box.lit::before{transform:scaleX(1);}
/* ---------- plaque: cross-fading image stack ----------
   Default is the AUGMENT mark; opening a question swaps in that question's
   artwork. All layers are stacked and cross-faded so there is no reflow. */
#capabilities .cap__plaque{position:absolute;inset:0;z-index:2;border-radius:inherit;overflow:hidden;}
#capabilities .cap__img{position:absolute;inset:0;width:100%;height:100%;
  opacity:0;transition:opacity .55s var(--ease);will-change:opacity;}
#capabilities .cap__img.is-on{opacity:1;}
/* artwork fills the plaque; the default mark is a centred logo, not a fill */
#capabilities .cap__img--art{object-fit:cover;object-position:center;}
/* The plaque is a square on desktop but a wide band between 641-820px, so a
   plain centre crop decapitates the finisher and clips STUDIO off the tee.
   Bias each one toward its own subject instead. */
#capabilities .cap__img--art[data-a="0"]{object-position:center 56%;}  /* keep AUGMENT YOUR RACE lockup */
#capabilities .cap__img--art[data-a="1"]{object-position:center 30%;}  /* keep her face and raised arms */
#capabilities .cap__img--art[data-a="2"]{object-position:center 36%;}  /* keep STUDIO + the script mark */
#capabilities .cap__img[data-default]{object-fit:contain;padding:26%;
  /* the mark ships white — it sits on the bright plaque, so knock it to ink */
  filter:brightness(0) saturate(100%);opacity:0;}
#capabilities .cap__img[data-default].is-on{opacity:.92;}
/* corner badge while the stand-in artwork is still in place */
#capabilities .cap__box[data-placeholder] .cap__plaque::after{
  content:"PLACEHOLDER ART";position:absolute;right:0;bottom:0;z-index:3;
  padding:.5em .7em;font-family:var(--font-mono);font-size:.58rem;letter-spacing:.16em;
  color:var(--c-ink);background:color-mix(in srgb,var(--c-white) 82%,transparent);
  pointer-events:none;}
#capabilities .cap__box[data-placeholder]:not(.lit) .cap__plaque::after{display:none;}

@media (max-width:820px){
  #capabilities .cap{grid-template-columns:1fr;gap:clamp(1.8rem,5vw,2.6rem);}
  /* single column: box is a full-width rectangle again (not the desktop square) */
  #capabilities .cap__box{aspect-ratio:auto;min-height:clamp(220px,52vw,300px);max-width:none;}
}
/* mobile — frame 16 layout: title → rows → intro → full-width cyan box that
   shows the tapped answer with the giant AUGMT bleeding off both sides below. */
@media (max-width:640px){
  #capabilities{overflow:hidden;}   /* contain the tilted box on narrow screens */
  #capabilities .cap__title{font-size:clamp(2rem,8.4vw,2.7rem);}
  /* full-width rectangle with a static 3D upward tilt + gentle idle float
     (no cursor on mobile, so the depth + movement comes from the animation) */
  #capabilities .cap__box{aspect-ratio:auto;min-height:clamp(300px,84vw,380px);
    padding:clamp(1.5rem,5.5vw,2.1rem);
    transform:rotateX(9deg) rotateY(-6deg);transform-origin:50% 58%;
    animation:capFloat 7s var(--ease) infinite;}
  /* answers now live in the accordion under each question, not in the plaque */
  #capabilities .cap__answer{font-size:clamp(.95rem,3.9vw,1.1rem);line-height:1.5;}
}
/* mobile idle float — slight 3D upward tilt that breathes; disabled under
   reduced-motion (which leaves the static upward tilt above in place) */
@keyframes capFloat{
  0%   {transform:rotateX(10deg) rotateY(-7deg) translateZ(0);}
  50%  {transform:rotateX(6.5deg) rotateY(6deg) translateZ(12px);}
  100% {transform:rotateX(10deg) rotateY(-7deg) translateZ(0);}
}

/* ============================================================
   TEAM TITLE — the scrolling TEAM strip (was the §7 separator)
   now titles §8, sitting directly above the 3D carousel. Light
   on the dark lower gradient; compact so strip + carousel share
   the viewport on every screen.
   ============================================================ */
/* extra breathing room below the TEAM words so the bobbing cards never reach
   them; the space below the cards (scene→subline) is left untouched */
#team .team__title{flex:none;width:100%;margin-bottom:clamp(4rem,9.5vh,7.5rem);}
#team .sep-hatch{height:clamp(11px,1.7vh,18px);
  background:repeating-linear-gradient(125deg,
    color-mix(in srgb,var(--c-g2) 68%,transparent) 0 2px,
    transparent 2px 11px);}
#team .team__title .sep-band{padding-block:clamp(.5rem,1.6vh,1.25rem);overflow:hidden;}
#team .team__title .marquee{-webkit-mask-image:none;mask-image:none;}
#team .sep-track{align-items:center;gap:clamp(2rem,5vw,5rem);}
#team .sep-item{font-family:var(--font-display);font-weight:700;text-transform:uppercase;
  font-size:clamp(2.6rem,10vw,8rem);line-height:1;letter-spacing:-.01em;color:var(--c-g1);}
#team .sep-star{display:flex;align-items:center;color:var(--c-g1);line-height:1;
  font-family:var(--font-display);font-size:clamp(1.4rem,3.6vw,3.4rem);}

/* ============================================================
   §9 · FOOTER — big cyan contact moment + giant AUGMT wordmark
   ============================================================ */
.footer{position:relative;background:var(--c-cyan);color:var(--c-ink);overflow:hidden;
  padding-top:clamp(2.8rem,7vh,5.5rem);}
.footer__inner{max-width:var(--maxw);margin-inline:auto;padding-inline:var(--gutter);}
/* only the brand lockup lives here now — the CTA line moved into the form */
.footer__top{display:flex;justify-content:flex-start;align-items:flex-start;gap:2rem;}
/* footer brand: horizontal AUGMENT | Y11 lockup with the tagline left-aligned
   beneath it; the whole block sits at the right of the footer */
.footer__brand{flex:0 0 auto;display:flex;flex-direction:column;align-items:flex-start;
  gap:clamp(.8rem,1.6vw,1.3rem);}
.footer__lockup{display:flex;align-items:center;gap:clamp(.9rem,1.9vw,1.7rem);}
.footer__augmark img,.footer__y11mark img{height:clamp(50px,6vw,90px);width:auto;display:block;}
.footer__rule{flex:none;align-self:stretch;width:1.5px;background:var(--c-ink);}
.footer__augmark,.footer__y11mark{transition:opacity .25s var(--ease);}
.footer__augmark:hover,.footer__y11mark:hover{opacity:.6;}
.footer__tagline{font-family:var(--font-display);font-weight:700;text-transform:uppercase;
  text-align:left;line-height:1.07;letter-spacing:.004em;color:var(--c-ink);
  font-size:clamp(.85rem,1.35vw,1.2rem);}
/* Just the address now — the Instagram/LinkedIn links are gone. It was a
   3-column grid with the links left and the address pushed right; a lone
   right-aligned item reads as a layout bug, so it sits left on its own row. */
.footer__meta{display:block;
  margin-top:clamp(3rem,9vh,6.5rem);
  font-family:var(--font-sans);font-size:clamp(.85rem,1vw,1.05rem);color:var(--c-ink);}
.footer__meta span{text-align:left;}
.footer__wordwrap{overflow:hidden;width:100%;margin-top:clamp(1rem,2.5vh,2rem);display:flex;justify-content:center;}
.footer__word{flex:none;font-family:var(--font-display);font-weight:800;white-space:nowrap;
  font-size:max(6.5rem,30vw);line-height:.76;letter-spacing:-.04em;color:var(--c-ink);
  transform:translateY(14%);}
[data-reveal="rise"].footer__word{transform:translateY(46%);}
[data-reveal="rise"].footer__word.in{transform:translateY(14%);}

@media (max-width:680px){
  /* stack the CTA and the brand lockup. Side by side, the CTA takes the full
     width while .footer__brand is flex:0 0 auto (it cannot shrink), so the
     lockup + tagline were pushed off the right edge and clipped by the
     footer's overflow:hidden. */
  .footer__top{flex-direction:column;gap:clamp(1.9rem,6vw,2.6rem);}
    .footer__brand{align-self:flex-start;}
  /* nothing to re-flow now that the address is the only child */
  .footer__meta span{text-align:left;}
}

/* ============================================================
   §8 · MEET THE TEAM — 3D carousel (ported) on the gradient
   ============================================================ */
#team{position:relative;min-height:100svh;display:flex;flex-direction:column;align-items:center;
  justify-content:center;gap:clamp(1rem,3.2vh,2.6rem);text-align:center;color:var(--c-white);
  /* clip sideways sway only; let cards bob/tilt DOWN into the dark buffer below
     instead of being cut off at the section's bottom edge */
  overflow-x:clip;overflow-y:visible;
  padding:clamp(2rem,5vh,4rem) 0 clamp(5.5rem,13vh,10rem);}

#team .team__scene{position:relative;z-index:3;width:100%;height:clamp(340px,47vh,560px);
  perspective:1500px;perspective-origin:50% 44%;touch-action:pan-y;}
#team .team__ring{position:absolute;left:50%;top:50%;width:0;height:0;transform-style:preserve-3d;
  will-change:transform;--cw:170px;--ch:240px;}
#team .card{position:absolute;top:50%;left:50%;width:var(--cw);height:var(--ch);
  margin-left:calc(var(--cw) / -2);margin-top:calc(var(--ch) / -2);
  transform-style:preserve-3d;backface-visibility:hidden;border-radius:0;
  box-shadow:0 30px 55px -22px rgba(0,0,0,.82);cursor:pointer;will-change:transform;}
#team .card__inner{position:absolute;inset:0;border-radius:0;overflow:hidden;
  box-shadow:0 0 0 1px rgba(255,255,255,.07) inset;
  transition:transform .45s var(--ease),filter .45s var(--ease);}
#team .card img{width:100%;height:100%;object-fit:cover;object-position:50% 24%;}
#team .card.is-hot .card__inner{transform:scale(1.05);filter:brightness(1.06);}
#team .card.is-hot{box-shadow:0 42px 80px -18px rgba(0,0,0,.9),0 0 0 1px rgba(255,255,255,.22) inset;}
#team .card__cap{position:absolute;left:0;right:0;bottom:0;z-index:2;padding:2.4rem .75rem .7rem;
  background:linear-gradient(to top,rgba(10,11,10,.95),rgba(10,11,10,.55) 48%,transparent);
  text-align:left;opacity:0;transform:translateY(10px);
  transition:opacity .42s var(--ease),transform .42s var(--ease);pointer-events:none;}
#team .card.is-hot .card__cap{opacity:1;transform:none;}
#team .c-name{display:block;font-family:var(--font-display);font-weight:600;line-height:1.02;
  font-size:1.05rem;letter-spacing:-.01em;color:#fff;}
#team .c-role{display:block;margin-top:.18rem;font-family:var(--font-mono);font-size:.6rem;
  letter-spacing:.13em;text-transform:uppercase;color:var(--c-g2);}
#team .team__subline{flex:none;position:relative;z-index:6;font-size:var(--fs-h3);color:var(--c-g2);padding-inline:1rem;}
#team .team__subline b{color:var(--c-white);font-weight:600;}
.u-sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0;}

@media (max-width:640px){#team .team__scene{height:clamp(340px,48vh,520px);}}
@media (max-width:640px),(hover:none){#team .card__cap{opacity:1;transform:none;}}

/* ============================================================
   CUSTOM CURSOR — precise dot + springy, velocity-aware ring
   (cursor.js). Native cursor is hidden only while it's active.
   ============================================================ */
@media (hover:hover) and (pointer:fine){
  html.has-cursor, html.has-cursor *{ cursor:none !important; }
}
/* Flat brand-cyan dot — no ring, no glow. Two full-viewport layers clipped to
   mutually exclusive regions (see cursor.js): cyan everywhere EXCEPT the image
   under the pointer, and an invert+grayscale lens exactly ON it. A cursor
   straddling a photo edge is therefore blue on the page side and monochrome on
   the picture side, split on the boundary. They must never overlap —
   backdrop-filter samples what is painted beneath it, so an overlapping cyan
   layer would get inverted instead of the photograph. */
.cur{position:fixed;inset:0;pointer-events:none;opacity:0;}
.cur-base{z-index:9000;}
/* the circle itself; JS sets clip-path each frame. Start collapsed so there is
   no full-screen cyan flash on the first paint. */
.cur-base__in{position:absolute;inset:0;clip-path:circle(0);background:var(--c-cyan);}

/* The lens is a DIRECT CHILD OF <body> and deliberately does not carry the
   .cur class. backdrop-filter is killed by a "backdrop root": any ancestor
   with clip-path, opacity < 1, filter, mask or a transform makes the element
   filter an empty backdrop and vanish. So it gets no clipped wrapper and is
   never faded by opacity — JS positions it, sizes it to the clipped region,
   and hands it a single polygon() for circle-intersect-image. */
.cur-lens{position:fixed;z-index:9001;pointer-events:none;display:none;
  backdrop-filter:invert(1) grayscale(1);
  -webkit-backdrop-filter:invert(1) grayscale(1);}
@media (prefers-reduced-motion:reduce){ .cur,.cur-lens{display:none;} }

/* minimal cursor trail (trail.js) — a thin white hairline that inverts
   against the backdrop via difference (no glow, no colour) */
#fxTrail{position:fixed;inset:0;z-index:8000;pointer-events:none;}
@media (prefers-reduced-motion:reduce){ #fxTrail{display:none;} }

/* ============================================================
   §9 · CONTACT FORM — replaces the exposed mailto address
   ============================================================ */
.cform{background:var(--c-ink);color:var(--c-white);}
.cform__inner{max-width:var(--maxw);margin-inline:auto;padding:clamp(4rem,10vh,8rem) var(--gutter) clamp(4rem,9vh,7rem);
  display:grid;grid-template-columns:1fr 1fr;gap:clamp(2rem,6vw,6rem);align-items:start;}
.cform__title{font-family:var(--font-display);font-weight:700;text-transform:uppercase;
  font-size:var(--fs-h2);line-height:.98;letter-spacing:-.015em;color:var(--c-white);}
.cform__sub{margin-top:clamp(.8rem,1.6vw,1.2rem);max-width:46ch;
  font-family:var(--font-sans);font-size:clamp(.9rem,1.05vw,1.1rem);line-height:1.55;color:var(--c-g2);}
.cform__form{display:flex;flex-direction:column;gap:clamp(1rem,2vw,1.4rem);}
.cform__row{display:grid;grid-template-columns:1fr 1fr;gap:clamp(1rem,2vw,1.4rem);}
.cform__field{display:flex;flex-direction:column;gap:.45rem;min-width:0;}
.cform__field label{font-family:var(--font-mono);font-size:var(--fs-label);letter-spacing:.16em;
  text-transform:uppercase;color:var(--c-g1);}
.cform__field input,.cform__field textarea{
  font-family:var(--font-sans);font-size:1rem;color:var(--c-white);
  background:transparent;border:0;border-bottom:1px solid color-mix(in srgb,var(--c-g1) 70%,transparent);
  padding:.65rem 0;outline:none;resize:vertical;
  transition:border-color .3s var(--ease);}
.cform__field input:focus,.cform__field textarea:focus{border-bottom-color:var(--c-cyan);}
.cform__field input:focus-visible,.cform__field textarea:focus-visible{outline:2px solid var(--c-cyan);outline-offset:3px;}
.cform__field .is-invalid{border-bottom-color:#ff6b6b;}
.cform__err{font-family:var(--font-mono);font-size:.66rem;letter-spacing:.1em;text-transform:uppercase;color:#ff6b6b;}
/* honeypot: off-screen for people, still reachable to a naive bot */
.cform__hp{position:absolute;left:-9999px;width:1px;height:1px;overflow:hidden;}
.cform__actions{display:flex;align-items:center;gap:1.2rem;flex-wrap:wrap;margin-top:.4rem;}
.cform__submit{font-family:var(--font-display);font-weight:600;font-size:clamp(1rem,1.5vw,1.3rem);
  color:var(--c-ink);background:var(--c-cyan);border:0;border-radius:0;
  padding:.75em 1.5em;cursor:pointer;transition:transform .3s var(--ease),opacity .3s;}
.cform__submit span{display:inline-block;transition:transform .3s var(--ease);}
.cform__submit:hover span{transform:translateX(5px);}
.cform__submit:focus-visible{outline:2px solid var(--c-white);outline-offset:3px;}
.form.is-sending .cform__submit,.cform__form.is-sending .cform__submit{opacity:.55;pointer-events:none;}
.cform__status{font-family:var(--font-mono);font-size:.72rem;letter-spacing:.1em;text-transform:uppercase;color:var(--c-g2);}
.cform__status[data-state="ok"]{color:var(--c-cyan);}
.cform__status[data-state="error"]{color:#ff6b6b;}
/* Fallback shown a beat after submit, for visitors whose browser has no mail
   handler registered and where the mailto: therefore did nothing. */
.cform__fallback{display:block;margin-top:.55rem;color:var(--c-g2);text-transform:none;letter-spacing:.02em;line-height:1.6;}
.cform__fallback a{color:var(--c-cyan);text-decoration:underline;text-underline-offset:3px;}
.cform__copy{font:inherit;color:var(--c-cyan);background:none;border:0;padding:0;
  text-decoration:underline;text-underline-offset:3px;cursor:pointer;}
.cform__copy:hover{color:var(--c-white);}
@media (max-width:820px){
  .cform__inner{grid-template-columns:1fr;gap:clamp(1.8rem,6vw,2.6rem);}
}
@media (max-width:560px){
  .cform__row{grid-template-columns:1fr;}
}


/* ---- globe card: brand cue kept to a single hairline ----
   Text colour comes from --bl (contrast-checked in globe.js); everything
   supporting stays light grey. Deliberately no tinted borders, glows or
   top bars — they made the card noisy and fought the logo. */
#globe .g-card[data-brand] .chead{
  border-bottom:1px solid color-mix(in srgb,var(--bl) 26%,transparent);}
#globe .g-card .elogowrap{margin:.6rem 0 .1rem;}
#globe .g-card .elogo{height:40px;opacity:.9;}
