/* ============================================================
   Ascend Media - core stylesheet
   Palette: Rich Black + Anti-Flash White + Picton Blue
   ============================================================ */

/* ---------- Tokens ---------- */
:root {
  --ink:        #081121;   /* Rich Black */
  --ink-2:      #0B1626;
  --ink-3:      #153056;   /* Rich Black tint */
  --ink-line:   rgba(255,255,255,.10);

  --paper:      #F0F4F7;   /* Anti-Flash White */
  --paper-2:    #DFE4E8;   /* Anti-Flash White tint */
  --paper-line: rgba(8,17,33,.12);

  --hi:         #FFFFFF;
  --mid:        rgba(255,255,255,.64);
  --low:        rgba(255,255,255,.42);

  --on-paper:      #081121;
  --on-paper-mid:  rgba(8,17,33,.62);
  --on-paper-low:  rgba(8,17,33,.45);

  --blue:    #00B2FF;  /* Picton Blue */
  --blue-2:  #36CBFF;
  --blue-3:  #80E4FF;
  --blue-4:  #BBEDFF;
  --navy-2:  #2962A5;
  --navy-3:  #4A9CED;

  --spectrum: linear-gradient(100deg, var(--navy-3) 0%, var(--blue) 45%, var(--blue-2) 75%, var(--blue-3) 100%);

  --wrap: 100%;
  --gut: clamp(1.25rem, 4vw, 5rem);
  --head-h: 76px;

  --ease: cubic-bezier(.16, 1, .3, 1);

  --f-display: "Montserrat", "Segoe UI", system-ui, sans-serif;
  --f-hero: "Helvetica Now Display", "Helvetica Neue", Helvetica, "Montserrat", Arial, sans-serif;
  --f-body: "Inter", system-ui, -apple-system, sans-serif;
  --f-mono: "Inter", ui-monospace, "SFMono-Regular", monospace;
}

/* ---------- Fonts ---------- */
/* WOFF2 throughout. The raw .ttf sources these came from are three times the
   size on the wire for identical glyphs — 673KB of Montserrat alone, ahead of
   first paint, on whatever connection the phone happens to have. */
@font-face {
  font-family: "Montserrat";
  src: url("../fonts/montserrat/Montserrat-VariableFont_wght.woff2") format("woff2-variations"),
       url("../fonts/montserrat/Montserrat-VariableFont_wght.woff2") format("woff2");
  font-weight: 100 900;
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: "Montserrat";
  src: url("../fonts/montserrat/Montserrat-Italic-VariableFont_wght.woff2") format("woff2-variations"),
       url("../fonts/montserrat/Montserrat-Italic-VariableFont_wght.woff2") format("woff2");
  font-weight: 100 900;
  font-style: italic;
  font-display: swap;
}
/* Body and small-label text. Montserrat is a geometric display face: it has
   a large x-height and wide, circular letterforms that read well at headline
   sizes and turn muddy below about 16px, which is where nearly all the copy
   on this site sits. Inter is drawn for screens at exactly those sizes, and
   it is what the project README specified for body in the first place. */
@font-face {
  font-family: "Inter";
  src: url("../fonts/inter/inter-variable.woff2") format("woff2-variations"),
       url("../fonts/inter/inter-variable.woff2") format("woff2");
  font-weight: 100 900;
  font-style: normal;
  font-display: swap;
}
/* Hero headlines only. One cut, Bold, so nothing else should reach for it:
   any other weight would come out of the browser's synthetic bolding. */
@font-face {
  font-family: "Helvetica Now Display";
  src: url("../fonts/helvetica-now/helvetica-now-display-bold.woff2") format("woff2");
  font-weight: 700;
  font-style: normal;
  font-display: swap;
}

/* ---------- Reset ---------- */
*, *::before, *::after { box-sizing: border-box; }
html { -webkit-text-size-adjust: 100%; scroll-behavior: smooth; }
body {
  margin: 0;
  background: var(--ink);
  color: var(--hi);
  font-family: var(--f-body);
  font-size: 16px;
  line-height: 1.6;
  -webkit-font-smoothing: antialiased;
  overflow-x: hidden;
  opacity: 1;
  transition: opacity .4s var(--ease), transform .4s var(--ease);
}
body.page-out { opacity: 0; transform: scale(.985); }

/* ---------- Page transitions ----------
   Cross-document view transitions. The browser snapshots the outgoing page,
   loads the next one, then animates between the two. Because it is native
   there is no stall in front of the navigation: the old JS approach had to
   fade out, wait 380ms, and only then start loading, so the visitor paid for
   the animation twice. Browsers without support simply navigate as before. */
@view-transition { navigation: auto; }

/* The new snapshot paints ON TOP of the old one, so only the incoming page
   is animated. Fading the OLD one out punches a hole straight through to the
   page background before the new one has finished arriving, and that hole is
   the flash of blue. Holding the old at full opacity underneath means there
   is always something solid behind the fade. */
::view-transition-old(root) { animation: none; }
::view-transition-new(root) { animation: page-enter .5s var(--ease) both; }

@keyframes page-enter { from { opacity: 0; transform: translateY(14px); } }

/* The header is identical on every page, so it sits still while the body
   changes underneath it. Animating it too makes the whole window read as a
   reload, which is the exact thing this is meant to avoid. */
.header { view-transition-name: site-header; }
::view-transition-old(site-header) { animation: none; }
::view-transition-new(site-header) { animation: none; }

@media (prefers-reduced-motion: reduce) {
  ::view-transition-new(root) { animation: none; }
}
img, svg, video { display: block; max-width: 100%; }
a { color: inherit; text-decoration: none; }
button { font: inherit; color: inherit; background: none; border: 0; cursor: pointer; }
h1, h2, h3, h4 { margin: 0; font-family: var(--f-display); font-weight: 600; letter-spacing: -.03em; line-height: 1.02; }
p { margin: 0; }
ul { margin: 0; padding: 0; list-style: none; }
:focus-visible { outline: 2px solid var(--blue-2); outline-offset: 3px; border-radius: 4px; }
::selection { background: var(--blue); color: #fff; }

/* ---------- Layout ---------- */
.wrap { width: 100%; max-width: var(--wrap); margin-inline: auto; padding-inline: var(--gut); }
.section { position: relative; padding-block: clamp(5rem, 11vw, 10rem); }
.section--paper { background: var(--paper); color: var(--on-paper); }
.section--ink { background: var(--ink); }
.section--ink2 { background: var(--ink-2); }

.skip-link {
  position: absolute; left: 1rem; top: -100px; z-index: 200;
  background: var(--hi); color: var(--ink); padding: .7rem 1.1rem; border-radius: 8px;
  font-family: var(--f-mono); font-size: .7rem; letter-spacing: .14em; text-transform: uppercase;
  transition: top .3s var(--ease);
}
.skip-link:focus { top: 1rem; }

/* ---------- Type utilities ---------- */
.eyebrow {
  font-family: var(--f-mono);
  font-size: .68rem;
  font-weight: 500;
  letter-spacing: .24em;
  text-transform: uppercase;
  /* Was --low, which measures 2.99:1 against ink at this size and read as
     grey noise rather than a label. --mid clears the 4.5:1 floor and still
     sits well back from the heading it introduces. */
  color: var(--mid);
  display: inline-flex;
  align-items: center;
  gap: .6rem;
}
.eyebrow::before {
  content: "";
  width: 18px; height: 2px;
  background: var(--spectrum);
}
.section--paper .eyebrow { color: var(--on-paper-mid); }

.display   { font-size: clamp(2.9rem, 8.2vw, 7rem); line-height: .94; font-weight: 800; letter-spacing: -.02em; }
.h2        { font-size: clamp(2.1rem, 4.6vw, 3.9rem); line-height: 1.02; font-weight: 700; }
.h3        { font-size: clamp(1.3rem, 2.1vw, 1.75rem); line-height: 1.16; letter-spacing: -.02em; }
.lede      { font-size: clamp(1rem, 1.35vw, 1.2rem); color: var(--mid); max-width: 46ch; }
.section--paper .lede { color: var(--on-paper-mid); }
.muted     { color: var(--mid); }
.section--paper .muted { color: var(--on-paper-mid); }
.spectrum-text {
  background: var(--spectrum);
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
}

/* ---------- Buttons ---------- */
.btn {
  --btn-bg: var(--hi);
  --btn-fg: var(--ink);
  display: inline-flex;
  align-items: center;
  gap: .85rem;
  padding: .55rem .55rem .55rem 1.5rem;
  min-height: 48px;
  border-radius: 100px;
  background: var(--btn-bg);
  color: var(--btn-fg);
  font-family: var(--f-hero);
  font-size: .72rem;
  /* Helvetica Now Display ships as a single Bold cut here, so 500 would be
     synthesised. Take the real 700 and pull the tracking in slightly, since
     a heavier face at this size needs less of it. */
  font-weight: 700;
  letter-spacing: .13em;
  text-transform: uppercase;
  white-space: nowrap;
  transition: transform .35s var(--ease), background-color .3s var(--ease), color .3s var(--ease);
}
.btn:hover { transform: translateY(-2px); }
.btn__dot {
  width: 34px; height: 34px;
  border-radius: 50%;
  display: grid; place-items: center;
  background: var(--ink);
  color: var(--hi);
  transition: transform .45s var(--ease);
}
.btn:hover .btn__dot { transform: rotate(45deg); }
.btn__dot svg { width: 13px; height: 13px; }
.btn--ghost {
  --btn-bg: transparent;
  --btn-fg: var(--hi);
  border: 1px solid var(--ink-line);
}
.btn--ghost .btn__dot { background: var(--hi); color: var(--ink); }
.btn--ghost:hover { background: rgba(255,255,255,.06); }
.btn--dark { --btn-bg: var(--ink); --btn-fg: var(--hi); }
.btn--dark .btn__dot { background: var(--hi); color: var(--ink); }

.link-arrow {
  display: inline-flex; align-items: center; gap: .5rem;
  font-family: var(--f-mono); font-size: .72rem;
  letter-spacing: .16em; text-transform: uppercase;
  padding-bottom: 4px;
  border-bottom: 1px solid currentColor;
  transition: gap .3s var(--ease), opacity .3s var(--ease);
}
.link-arrow:hover { gap: .95rem; }

/* ---------- Prism / atmosphere ---------- */
.atmos { position: absolute; inset: 0; overflow: hidden; pointer-events: none; z-index: 0; }
.shard {
  position: absolute;
  border-radius: 50%;
  filter: blur(64px);
  opacity: .72;
  mix-blend-mode: screen;
  will-change: transform;
}
.shard--a { width: 46vw; height: 46vw; left: -12vw; top: -14vw; background: radial-gradient(circle at 35% 35%, var(--navy-3), transparent 62%); animation: drift 26s var(--ease) infinite alternate; }
.shard--b { width: 40vw; height: 40vw; right: -10vw; top: 4vw;   background: radial-gradient(circle at 60% 40%, var(--blue), transparent 60%); animation: drift 32s var(--ease) infinite alternate-reverse; }
.shard--c { width: 34vw; height: 34vw; left: 28vw; bottom: -16vw; background: radial-gradient(circle at 50% 50%, var(--blue-2), transparent 62%); animation: drift 38s var(--ease) infinite alternate; opacity: .58; }
.shard--d { width: 28vw; height: 28vw; right: 18vw; bottom: -8vw; background: radial-gradient(circle at 50% 50%, var(--navy-2), transparent 60%); animation: drift 30s var(--ease) infinite alternate-reverse; opacity: .5; }
@keyframes drift {
  from { transform: translate3d(0,0,0) scale(1); }
  to   { transform: translate3d(6vw, 5vh, 0) scale(1.18); }
}

/* Phones: hold the ambient glows still. Four 30-46vw blurred, screen-blended
   layers animating transform every frame is a real GPU cost on mobile for a
   background flourish. Static, they cost nothing per frame. */
@media (hover: none), (max-width: 760px) {
  .shard { animation: none !important; will-change: auto; }
  .marquee--lg .marquee__track,
  .marquee__track { animation-duration: 60s; }

  /* Holding them still was only half of it. A 64px blur across a 46vw circle
     is a large raster in its own right, and `screen` keeps every one of these
     out of the compositor's opaque fast path, so all four re-blend on every
     frame the hero video paints underneath them. The gradients are soft
     enough to carry the glow without the blur, and a plain composite at a
     lower opacity lands in the same place on a screen this size. */
  .shard { filter: none; mix-blend-mode: normal; }
  .shard--a, .shard--b { opacity: .5; }
  .shard--c { opacity: .4; }
  .shard--d { opacity: .34; }
  .hero .shard { opacity: .17; }
  .hero .shard--c { opacity: .13; }
  .hero .shard--d { opacity: .11; }

  /* Overlay-blended noise across a whole section, one of which sits over a
     playing video: a blend of the full section every frame, to deliver a
     texture that does not survive this pixel density anyway. */
  .grain::after { display: none; }

  /* Each drop-shadow is an alpha blur of the entire bitmap, and there are two
     of them stacked on a pair of 1200px logos. The screen blend is cheap by
     comparison and carries most of the look, so only the filter goes. */
  .cta-mini__logo { filter: none; }

  /* backdrop-filter makes the compositor re-read whatever is behind the
     element, which on the hero is a video mid-playback. */
  .card__play { backdrop-filter: none; background: rgba(255,255,255,.2); }
}

/* Over a video hero these screen-blended glows sit on top of the footage and
   cast the whole frame blue. The flat CTA panels have nothing to wash out, so
   they keep the full strength; heroes get it pulled right back and the film
   reads the way it was graded. */
.hero .shard    { opacity: .24; }
.hero .shard--c { opacity: .18; }
.hero .shard--d { opacity: .16; }

/* crisp glass blade */
.blade { position: absolute; pointer-events: none; opacity: .85; }
.blade--tl { width: clamp(220px, 30vw, 560px); left: -5%; top: -10%; transform: rotate(-14deg); }
.blade--br { width: clamp(240px, 34vw, 620px); right: -7%; bottom: -16%; transform: rotate(8deg); }

.grain::after {
  content: "";
  position: absolute; inset: 0;
  pointer-events: none;
  z-index: 4;
  opacity: .15;
  mix-blend-mode: overlay;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='160' height='160'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.9' numOctaves='3' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E");
}

/* `.grain` sections are always the last one before the footer — fade them
   into the footer's background instead of ending on a hard edge. */
.grain::before {
  content: "";
  position: absolute; left: 0; right: 0; bottom: 0;
  height: clamp(6rem, 16vw, 12rem);
  background: linear-gradient(to bottom, transparent, var(--ink));
  pointer-events: none;
  z-index: 2;
}

/* ---------- Header ---------- */
.header {
  position: fixed; inset: 0 0 auto 0; z-index: 100;
  height: var(--head-h);
  display: flex; align-items: center;
  background: transparent;
  border-bottom: 1px solid transparent;
}
/* Grid (not flex + margin-inline:auto) so the nav links stay centered on
   the header itself regardless of how wide the logo vs. the CTA button
   are — a flex "auto margins" center only balances against whatever's on
   either side, so it drifted whenever the two didn't match (e.g. the
   short logo vs. a long "Request Information" button). */
.header__inner {
  width: 100%; max-width: var(--wrap); margin-inline: auto; padding-inline: var(--gut);
  display: grid; grid-template-columns: 1fr auto 1fr; align-items: center; gap: 2rem;
}
.brand { display: inline-flex; align-items: center; justify-self: start; }
.brand__wordmark { height: 26px; width: auto; display: block; }
.brand__wordmark--black { display: none; }
.nav { display: flex; gap: 2rem; justify-self: center; }
.header__inner .btn, .header__inner .burger { grid-column: 3; justify-self: end; }
.nav a {
  font-family: var(--f-mono); font-size: .95rem; font-weight: 700;
  letter-spacing: .08em; text-transform: uppercase;
  color: var(--hi); position: relative; padding-block: .4rem;
  transition: color .3s var(--ease);
}
.nav a:hover, .nav a[aria-current="page"] { color: var(--blue-2); }
.nav a::after {
  content: ""; position: absolute; left: 0; bottom: 0;
  width: 100%; height: 1px; background: var(--spectrum);
  transform: scaleX(0); transform-origin: left; transition: transform .4s var(--ease);
}
.nav a:hover::after, .nav a[aria-current="page"]::after { transform: scaleX(1); }
.header .btn {
  padding: .65rem 1.6rem; min-height: 46px;
  font-size: .95rem; font-weight: 800; letter-spacing: .02em;
  justify-content: center; text-indent: .02em;
  transition: background-color .35s var(--ease), color .35s var(--ease);
}
.header .btn__dot { display: none; }

.burger { display: none; width: 44px; height: 44px; place-items: center; }
.burger span { display: block; width: 22px; height: 2px; background: var(--hi); transition: transform .4s var(--ease), opacity .3s var(--ease), background-color .35s var(--ease); }
.burger span + span { margin-top: 6px; }
body.nav-open .burger span:first-child { transform: translateY(4px) rotate(45deg); }
body.nav-open .burger span:last-child  { transform: translateY(-4px) rotate(-45deg); }

/* Header tone flips to dark text/logo when the section behind it is light */
.header--on-light .brand__wordmark--white { display: none; }
.header--on-light .brand__wordmark--black { display: block; }
.header--on-light .nav a { color: var(--on-paper); }
.header--on-light .nav a:hover, .header--on-light .nav a[aria-current="page"] { color: var(--blue); }
.header--on-light .burger span { background: var(--on-paper); }
.header--on-light .btn { --btn-bg: var(--ink); --btn-fg: var(--hi); }
.header--on-light .btn .btn__dot { background: var(--hi); color: var(--ink); }
.header--on-light .btn--ghost { --btn-bg: transparent; --btn-fg: var(--on-paper); border-color: var(--paper-line); }
.header--on-light .btn--ghost .btn__dot { background: var(--ink); color: var(--hi); }

.mobile-nav {
  position: fixed; inset: 0; z-index: 99;
  background: var(--ink);
  display: grid; align-content: center; gap: .6rem;
  padding: 6rem var(--gut) 3rem;
  clip-path: inset(0 0 100% 0);
  transition: clip-path .6s var(--ease);
}
body.nav-open .mobile-nav { clip-path: inset(0 0 0 0); }
.mobile-nav a { font-family: var(--f-display); font-weight: 600; font-size: clamp(2rem, 9vw, 3.2rem); letter-spacing: -.03em; color: var(--low); }
.mobile-nav a:hover, .mobile-nav a[aria-current="page"] { color: var(--hi); }
.mobile-nav .btn { margin-top: 2rem; justify-self: start; }
body.nav-open { overflow: hidden; }

/* ---------- Hero ---------- */
.hero {
  position: relative;
  min-height: 100svh;
  display: flex; align-items: flex-end;
  padding-block: calc(var(--head-h) + 3rem) clamp(2.5rem, 6vw, 5rem);
  background: var(--ink);
  overflow: hidden;
  isolation: isolate;
}
.hero__bg {
  position: absolute; inset: 0; z-index: 0;
  width: 100%; height: 100%; object-fit: cover; object-position: center 30%;
  opacity: .55;
}
.hero__bg--video { object-position: center; opacity: .9; }

.hero__volume {
  position: absolute; z-index: 4; right: var(--gut); bottom: clamp(1.5rem, 4vw, 2.5rem);
  display: flex; align-items: center; gap: 0;
}
.hero__mute {
  flex: 0 0 auto; width: 44px; height: 44px; border-radius: 50%;
  display: grid; place-items: center;
  background: rgba(255,255,255,.12); backdrop-filter: blur(8px);
  border: 1px solid rgba(255,255,255,.25); color: var(--hi);
  transition: background-color .3s var(--ease), transform .3s var(--ease), border-color .3s var(--ease);
}
.hero__mute:hover { background: rgba(255,255,255,.24); transform: scale(1.06); }
.hero__mute-icon { width: 19px; height: 19px; }
.hero__mute-icon--on { display: none; }
.hero__mute[aria-pressed="false"] .hero__mute-icon--off { display: none; }
.hero__mute[aria-pressed="false"] .hero__mute-icon--on { display: block; }
.hero__mute[aria-pressed="false"] { border-color: var(--blue); color: var(--blue-2); }

/* Volume slider: hidden until the control is hovered/focused, then slides
   out to the left of the mute button. Applies to every hero video that
   uses this .hero__volume + [data-volume-for] pattern. */
.hero__volume-slider {
  width: 0; min-width: 0; opacity: 0; margin-right: 0;
  -webkit-appearance: none; appearance: none;
  height: 4px; border-radius: 100px; outline: none;
  background: rgba(255,255,255,.28);
  cursor: pointer;
  transition: width .35s var(--ease), opacity .3s var(--ease), margin-right .35s var(--ease);
}
.hero__volume:hover .hero__volume-slider,
.hero__volume:focus-within .hero__volume-slider {
  width: 84px; opacity: 1; margin-right: .7rem;
}
.hero__volume-slider::-webkit-slider-thumb {
  -webkit-appearance: none; width: 13px; height: 13px; border-radius: 50%;
  background: var(--hi); border: 2px solid var(--blue); cursor: pointer;
}
.hero__volume-slider::-moz-range-thumb {
  width: 13px; height: 13px; border-radius: 50%;
  background: var(--hi); border: 2px solid var(--blue); cursor: pointer;
}
.hero__volume-slider::-moz-range-track { background: rgba(255,255,255,.28); height: 4px; border-radius: 100px; }
.hero__scrim {
  position: absolute; inset: 0; z-index: 1;
  background:
    /* Corner vignette, pulled right back so the footage is not ringed in
       navy. This one is decorative and the frame reads better without it. */
    radial-gradient(115% 85% at 50% 30%, rgba(8,17,33,0), rgba(8,17,33,.22) 74%),
    /* The vertical wash is not decorative: it is what keeps the white
       headline legible over a bright frame and blends the hero into the
       section below, so the lower half stays close to where it was. */
    linear-gradient(to top, var(--ink) 0%, rgba(8,17,33,.12) 48%, rgba(8,17,33,.26));
}
.hero .wrap { position: relative; z-index: 3; width: 100%; }
.hero__title {
  max-width: 15ch;
  font-size: clamp(2.4rem, 5.6vw, 4.6rem);
  font-family: var(--f-hero);
  /* The only cut we have. The base h1 asks for 600, which against a lone
     700 face would leave the browser faking the weight. */
  font-weight: 700;
  /* Helvetica Now Display is drawn for large sizes and is already tightly
     spaced, so it needs less negative tracking than Montserrat does. */
  letter-spacing: -.022em;
}

/* The homepage headline breaks where the <br> says it does, so the width cap
   would only add a second, unwanted break inside the longer line. */
.hero--center .hero__title { max-width: none; }
.hero__sub { margin-top: 1.6rem; max-width: 42ch; color: var(--mid); font-size: clamp(1rem, 1.4vw, 1.15rem); }
.hero__actions { margin-top: 2.4rem; display: flex; flex-wrap: wrap; gap: .8rem; }
.hero__meta {
  margin-top: clamp(2.2rem, 5vw, 4rem);
  padding-top: 1.4rem;
  border-top: 1px solid var(--ink-line);
  display: flex; flex-wrap: wrap; gap: 1rem 3rem;
  font-family: var(--f-mono); font-size: .66rem;
  letter-spacing: .18em; text-transform: uppercase; color: var(--low);
}

/* Centered hero variant — horizontally centered, anchored toward the lower
   third so a looping background video has room to breathe above the copy */
.hero--center { align-items: flex-end; text-align: center; padding-bottom: clamp(4rem, 10vw, 7rem); }
.hero--center .wrap { display: flex; flex-direction: column; align-items: center; }
.hero--center .hero__title { max-width: 25ch; margin-inline: auto; }
.hero--center .hero__actions { justify-content: center; }

/* ---------- Scale / stats section ---------- */
/* Capped and centred. The rest of the site runs full bleed, but a two-item
   row is the one layout that gets worse with width: past this the label and
   its number drift so far apart the eye stops pairing them. */
.scale {
  display: grid;
  grid-template-columns: minmax(0,1fr) minmax(0,1.3fr);
  gap: clamp(2rem, 5vw, 5rem);
  align-items: stretch;
}
/* The phone is framed to this box's height, so a flat 560px left it small in
   a column that keeps getting wider. Scale the box with the viewport. */
.scale__art { position: relative; background: transparent; min-height: clamp(560px, 38vw, 680px); }
.scale__art img { width: 100%; height: 100%; object-fit: cover; position: absolute; inset: 0; }
.phone-viewer { position: absolute; inset: 0; cursor: grab; touch-action: none; }
.phone-viewer:active { cursor: grabbing; }
.phone-viewer canvas { display: block; width: 100% !important; height: 100% !important; }
/* Stat list — simple bordered rows, label left / number right */
/* Centred against the art column, which is taller than the list. Left at the
   top the rows read as a fragment with a hole beneath them. */
.scale-grid { display: flex; flex-direction: column; justify-content: center; }
/* Same row rhythm as .acc__item, so the two list sections on the site read as
   one component rather than two unrelated ones.

   Every row is sized for the largest state, including the featured one. The
   value grows on hover and the lead value is permanently large, so without a
   common floor the list would jump every time the cursor crossed it. */
.scale-row {
  display: flex; align-items: center; justify-content: space-between; gap: 2rem;
  padding-block: 1.5rem;
  min-height: clamp(84px, 9.5vw, 116px);
  border-top: 1px solid var(--ink-line);
  cursor: default;
}
.section--paper .scale-row { border-color: var(--paper-line); }
.scale-row:last-child { border-bottom: 1px solid var(--ink-line); }
.section--paper .scale-row:last-child { border-color: var(--paper-line); }
/* Matches .acc__btn exactly: same face, weight, size and tracking, dim at
   rest and brightening on hover. The old 11px uppercase mono was a different
   component in the same visual slot, and it left the label so small that the
   number opposite it looked stranded across the row.

   --low / --on-paper-low sit near 3:1, which fails for small text but is
   fine here: at 22px and weight 600 this clears the large-text threshold,
   and it is the same value the accordion already uses. */
.scale-row__k {
  font-family: var(--f-display); font-weight: 600;
  font-size: clamp(1.05rem, 1.7vw, 1.4rem);
  letter-spacing: -.02em;
  color: var(--low);
  line-height: 1.3;
  transition: color .35s var(--ease);
}
.section--paper .scale-row__k { color: var(--on-paper-low); }
.scale-row:hover .scale-row__k { color: var(--hi); }
.section--paper .scale-row:hover .scale-row__k { color: var(--on-paper); }
.scale-row__v {
  font-family: var(--f-hero); font-weight: 700; letter-spacing: -.02em;
  font-size: clamp(1.3rem, 2.6vw, 1.9rem);
  /* Tight. Inheriting the body's 1.6 gave the featured value a 92px line box
     and made its row 25px taller than every other one. */
  line-height: 1.05;
  /* No tabular-nums here. This face gives the comma and the hyphen a full
     digit advance under that feature, which set "1,200+" as "1 , 200+" and
     "In-house" as "In - house". The values are right-aligned anyway, so
     there is no column to keep in step. */
  white-space: nowrap;
  /* Grows to the featured value's size on hover. transform rather than
     font-size: scaling is a compositor job, so it stays smooth, and it costs
     no layout, so the row underneath never moves. Anchored right because the
     column is right-aligned; it should grow leftward into the empty run
     between the label and the number, not push past the edge. */
  transform-origin: 100% 50%;
  transition: transform .4s var(--ease), color .3s var(--ease);
}

.scale-row:hover .scale-row__v { transform: scale(1.9); }
/* The featured value is already at that size, so it holds still. */
.scale-row--lead:hover .scale-row__v { transform: none; }
/* Steps up with the row, staying in proportion to the larger number. */
.scale-row--lead .scale-row__k { font-size: clamp(1.15rem, 1.9vw, 1.55rem); }
.scale-row--lead .scale-row__v { font-size: clamp(2.2rem, 5.4vw, 3.6rem); color: var(--blue); }

/* Hover-to-count: numbers re-animate from zero each time the row is hovered */
.scale-row:hover .scale-row__v[data-count] { color: var(--blue); }

/* ---------- Asymmetric content grid (editorial collage) ---------- */
.content-grid {
  display: grid;
  grid-template-columns: 1.15fr 1fr;
  grid-template-rows: repeat(2, minmax(0,1fr));
  gap: clamp(.8rem, 1.6vw, 1.4rem);
  aspect-ratio: 15 / 13;
}
.content-grid__tile { position: relative; border-radius: 16px; overflow: hidden; border: 1px solid var(--ink-line); }
.content-grid__tile--a { grid-column: 1; grid-row: 1 / 3; }
.content-grid__tile--b { grid-column: 2; grid-row: 1; }
.content-grid__tile--c { grid-column: 2; grid-row: 2; }
/* Sits in the frame rather than 20% outside it. The oversize cropped a fifth
   off every tile before it was seen, which is what cut the heads off these
   three, and it was redundant: the hover zoom below is a transform. */
.content-grid__mesh { position: absolute; inset: 0; transition: transform .8s var(--ease); }
.content-grid__mesh img { width: 100%; height: 100%; object-fit: cover; }
.content-grid__tile::after {
  content: ""; position: absolute; inset: 0; z-index: 1;
  background: linear-gradient(to top, rgba(8,17,33,.75), transparent 45%);
  opacity: 0; transition: opacity .4s var(--ease);
  pointer-events: none;
}
.content-grid__tile:hover::after { opacity: 1; }
.content-grid__handle {
  position: absolute; z-index: 2; left: 1rem; right: 1rem; bottom: .9rem;
  font-family: var(--f-mono); font-size: .68rem; font-weight: 600;
  letter-spacing: .04em; color: var(--hi);
  opacity: 0; transform: translateY(6px);
  transition: opacity .4s var(--ease), transform .4s var(--ease);
  pointer-events: none;
}
.content-grid__tile:hover .content-grid__handle { opacity: 1; transform: translateY(0); }
.content-grid__mesh--benson { object-position: 46% 6%; }
.content-grid__mesh--jiggy { object-position: 42% 14%; }
.content-grid__tile:hover .content-grid__mesh { transform: scale(1.06); }

/* ---------- Minimal CTA ----------
   Four different closing-CTA structures grew up across the site: a section
   that owns the CTA (.cta-mini), a block bolted onto the foot of a section
   carrying other content (brands, work), and the case-study version sitting
   left-aligned in a bare .wrap. They ended up with 0px, 38px and 48px between
   headline and button, and two of the three alignments. `.cta-block` gives
   the bolted-on ones the same treatment .cta-mini already had. */
.cta-block { text-align: center; }
.cta-block > .display, .cta-block > .h2 { margin-inline: auto; }
.cta-mini .btn,
.cta-block .btn,
.cta-block .hero__actions { margin-top: clamp(2rem, 4vw, 3rem); }
.cta-block .hero__actions { justify-content: center; }

/* Even out wrapped headings. Left alone, the browser fills each line to the
   maximum and dumps whatever is left on the last one, which set "Want a
   project like this one?" as a 718px line above a 346px one. */
.display, .h2, .cta-mini__title { text-wrap: balance; }

.cta-mini { text-align: center; }
.cta-mini .wrap { display: flex; flex-direction: column; align-items: center; }
.cta-mini__title { max-width: 16ch; }
.cta-mini .btn { margin-top: clamp(2rem, 4vw, 3rem); }
.cta-mini__logo {
  position: absolute; z-index: 1;
  width: clamp(400px, 50vw, 700px); height: auto;
  opacity: .3;
  mix-blend-mode: screen;
  filter: drop-shadow(0 40px 70px rgba(0,178,255,.3)) drop-shadow(0 0 50px rgba(0,178,255,.22)) brightness(.94) saturate(1.05);
  pointer-events: none;
}
/* A matched pair. These were drifting apart on every axis at once: different
   widths (820 vs 600), different opacities (.38 vs .22), one anchored to the
   middle and the other to the bottom, and rotations that were not mirrors of
   each other. Same size, same weight, same vertical anchor, and --b is a true
   horizontal mirror of --a, so the two read as one deliberate frame. */
.cta-mini__logo--a {
  top: 50%; right: clamp(-26rem, -19vw, -15rem);
  transform: translateY(-50%) rotate(-14deg);
}
.cta-mini__logo--b {
  top: 50%; left: clamp(-26rem, -19vw, -15rem);
  transform: translateY(-50%) scaleX(-1) rotate(-14deg);
}
@media (max-width: 900px) {
  .cta-mini__logo { width: clamp(260px, 68vw, 420px); opacity: .26; }
  .cta-mini__logo--a { right: -30vw; }
  .cta-mini__logo--b { left: -30vw; }
}

/* ---------- Marquee ---------- */
.marquee { overflow: hidden; padding-block: 1.7rem; border-block: 1px solid var(--paper-line); }
.marquee__track { display: flex; align-items: center; gap: 4rem; width: max-content; animation: slide 40s linear infinite; }
.marquee--lg { padding-block: clamp(2.5rem, 5vw, 4rem); border-block-width: 0; }
.marquee--lg .marquee__track { gap: clamp(3.5rem, 7vw, 7rem); animation-duration: 52s; }
.marquee--lg .marquee__item img { height: clamp(54px, 7vw, 94px); max-width: 270px; }
/* Compact placement above "Why it works" — same logos, noticeably smaller.
   Sizing by height alone doesn't work here: roughly half these files are
   square canvases with the wordmark padded inside (a16z, carpe, Strega,
   PrizePicks...), while others are tight 3.5:1 crops (Replit, Sports
   Illustrated). A shared height makes the square ones occupy a third of
   the area of the wide ones. Give every item the same box instead and
   let each logo scale to fit inside it. */
.marquee--sm { padding-block: clamp(1rem, 2vw, 1.5rem); }
.marquee--sm .marquee__track { gap: clamp(1.5rem, 3vw, 2.75rem); animation-duration: 38s; }
/* Items shrink-wrap their logo instead of sitting in a fixed-width box. The
   box kept the BOX gaps identical, but a narrow mark like the FIFA badge then
   floated in dead space inside its slot while a wide one like Carpe filled
   its own, so the gaps you actually see were uneven. Sizing to the artwork
   makes the whitespace between marks constant. Height still sets the common
   ceiling and max-width still stops a wide logo running away. */
.marquee--sm .marquee__item {
  flex: 0 0 auto; justify-content: center;
  height: clamp(56px, 6.2vw, 78px);
}
.marquee--sm .marquee__item img {
  width: auto; height: auto;
  max-height: 100%;
  max-width: clamp(120px, 13vw, 168px);
}
.marquee:hover .marquee__track { animation-play-state: paused; }
.marquee__item {
  display: flex; align-items: center;
  font-family: var(--f-display); font-weight: 600;
  font-size: clamp(1rem, 1.6vw, 1.35rem);
  letter-spacing: -.015em; color: var(--on-paper-low);
  white-space: nowrap; transition: color .3s var(--ease);
}
.marquee__item:hover { color: var(--on-paper); }
.section--ink .marquee, .section--ink2 .marquee { border-color: var(--ink-line); }
.section--ink .marquee__item, .section--ink2 .marquee__item { color: var(--low); }
.section--ink .marquee__item:hover, .section--ink2 .marquee__item:hover { color: var(--hi); }

/* Logo-cloud variant: uniform grayscale so wildly different brand colors
   don't clash, full color revealed on hover. */
.marquee__item img {
  height: clamp(22px, 2.6vw, 32px); width: auto; max-width: 140px;
  object-fit: contain;
  filter: grayscale(1) brightness(.7) contrast(.9);
  opacity: .68;
  transition: filter .35s var(--ease), opacity .35s var(--ease);
}
.marquee__item:hover img { filter: none; opacity: 1; }
.section--ink .marquee__item img, .section--ink2 .marquee__item img { filter: grayscale(1) brightness(2.2) contrast(.85); }

/* .marquee--lg (brands.html carousel): full colour always. These are the
   client logos and the brand wants them shown as they are. */
.marquee--lg .marquee__item img,
.marquee--lg .marquee__item:hover img,
.section--ink .marquee--lg .marquee__item img,
.section--ink2 .marquee--lg .marquee__item img {
  filter: none;
  opacity: 1;
}
.marquee--lg:hover .marquee__track { animation-play-state: running; }
.section--ink .marquee__item:hover img, .section--ink2 .marquee__item:hover img { filter: none; }
@keyframes slide { to { transform: translateX(-50%); } }

/* ---------- Stats ---------- */
.stats { display: grid; grid-template-columns: repeat(auto-fit, minmax(140px, 1fr)); gap: clamp(1.5rem, 2.6vw, 2.6rem); }
.stat__num {
  font-family: var(--f-display); font-weight: 600;
  font-size: clamp(2.4rem, 5.2vw, 4.2rem);
  line-height: 1; letter-spacing: -.045em;
}
.stat__label {
  margin-top: .9rem; padding-top: .9rem;
  border-top: 1px solid var(--ink-line);
  font-family: var(--f-mono); font-size: .64rem;
  letter-spacing: .2em; text-transform: uppercase; color: var(--low);
}
.section--paper .stat__label { border-color: var(--paper-line); color: var(--on-paper-low); }

/* ---------- Split layout ---------- */
.split { display: grid; grid-template-columns: 1fr 1fr; gap: clamp(2.5rem, 5vw, 7rem); align-items: start; }
/* Heading and accordion share a top edge. Centring the shorter column split
   the leftover space in two and left the heading floating with a gap above
   it as well as below; one clean edge with the whitespace collected
   underneath reads as deliberate instead. */
.split--sticky > :first-child { position: sticky; top: calc(var(--head-h) + 3rem); }
/* Decorative, like .cta-mini__logo: large, rotated, and left to bleed
   off the column rather than sitting inline. z-index:1 (not a
   negative value) — this section has no stacking context of its own,
   so a negative z-index here fell through to compare against
   page-level siblings and rendered completely hidden behind them. */
.split__icon {
  position: absolute; z-index: 1; pointer-events: none;
  width: clamp(460px, 48vw, 680px); height: auto;
  bottom: clamp(-15rem, -13vw, -10rem); left: clamp(-19rem, -17vw, -12rem);
}
/* Mirror image for the right-side placement (e.g. brands page, where
   the accordion sits on the left and the heading/icon flip to the
   right) — bleeds off the right edge instead of the left. */
.split__icon--right { left: auto; right: clamp(-19rem, -17vw, -12rem); }
/* Higher specificity than the generic [data-reveal].is-in rule, which
   would otherwise reset opacity/transform to their "revealed" values
   (1 / none) and wipe out the decorative fade. */
.split__icon.is-in { opacity: .55 !important; transform: none !important; }
.split__icon--right.is-in { transform: scaleX(-1) !important; }

/* ---------- Accordion ---------- */
.acc__item { border-top: 1px solid var(--paper-line); }
.section--ink .acc__item, .section--ink2 .acc__item { border-color: var(--ink-line); }
.acc__item:last-child { border-bottom: 1px solid var(--paper-line); }
.section--ink .acc__item:last-child, .section--ink2 .acc__item:last-child { border-bottom-color: var(--ink-line); }
.acc__btn {
  width: 100%; display: flex; align-items: center; justify-content: space-between; gap: 1.5rem;
  padding-block: 1.5rem; text-align: left;
  font-family: var(--f-display); font-weight: 600;
  font-size: clamp(1.05rem, 1.7vw, 1.4rem); letter-spacing: -.02em;
  color: var(--on-paper-low);
  transition: color .35s var(--ease);
}
.section--ink .acc__btn, .section--ink2 .acc__btn { color: var(--low); }
.acc__btn:hover, .acc__btn[aria-expanded="true"] { color: var(--on-paper); }
.section--ink .acc__btn:hover, .section--ink .acc__btn[aria-expanded="true"],
.section--ink2 .acc__btn:hover, .section--ink2 .acc__btn[aria-expanded="true"] { color: var(--hi); }
.acc__sign { position: relative; flex: 0 0 auto; width: 14px; height: 14px; }
.acc__sign::before, .acc__sign::after {
  content: ""; position: absolute; top: 50%; left: 0; width: 100%; height: 2px; background: currentColor;
  transition: transform .4s var(--ease);
}
.acc__sign::after { transform: rotate(90deg); }
.acc__btn[aria-expanded="true"] .acc__sign::after { transform: rotate(0deg); }
.acc__panel { overflow: hidden; height: 0; transition: height .5s var(--ease); }
.acc__panel-inner { padding-bottom: 1.7rem; max-width: 58ch; color: var(--on-paper-mid); font-size: .98rem; }
.section--ink .acc__panel-inner, .section--ink2 .acc__panel-inner { color: var(--mid); }
.acc__tags { display: flex; flex-wrap: wrap; gap: .5rem; margin-top: 1.1rem; }
.tag {
  font-family: var(--f-mono); font-size: .6rem; letter-spacing: .14em; text-transform: uppercase;
  padding: .38rem .7rem; border-radius: 100px; border: 1px solid var(--paper-line); color: var(--on-paper-low);
}
.section--ink .tag, .section--ink2 .tag { border-color: var(--ink-line); color: var(--low); }

/* ---------- Work cards ---------- */
.work-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(320px, 1fr)); gap: clamp(1rem, 1.6vw, 1.6rem); }
.card {
  position: relative; display: block; overflow: hidden;
  border-radius: 18px; border: 1px solid var(--ink-line);
  background: var(--ink-2);
  transition: transform .5s var(--ease), border-color .4s var(--ease);
}
.card:hover { transform: translateY(-6px); border-color: rgba(255,255,255,.26); }
.card__media { position: relative; aspect-ratio: 4 / 3; overflow: hidden; }
.card__media::after { content: ""; position: absolute; inset: 0; background: linear-gradient(to top, rgba(8,9,13,.92), transparent 58%); }
.card__mesh { position: absolute; inset: -18%; transition: transform .8s var(--ease); }
.card__mesh img { width: 100%; height: 100%; object-fit: cover; display: block; }
/* When .card__mesh is the <img> itself (not a wrapper div around one),
   the rule above never matches it — it's a descendant selector. Without
   an explicit width/height/object-fit, a positioned replaced element
   like this doesn't reliably expand to the inset-derived box, so it
   was rendering near its container's size with the CSS-default
   object-fit:fill, stretching/distorting the photo. Fill and cover it
   explicitly. Gradient meshes can take the full -18% oversize
   invisibly, but a real photo loses too much of the frame at that
   zoom — ease it back. */
/* Sits exactly in the frame. The old -4%/108% oversize cropped roughly eight
   percent off every thumbnail before it was even seen, and it was redundant:
   the hover zoom below is a transform, which does not need the image to be
   pre-enlarged. `max-width:100%` from the global img rule also clamped the
   width but not the height, so the picture came out taller than its box. */
img.card__mesh { inset: 0; width: 100%; height: 100%; object-fit: cover; }
.card:hover .card__mesh { transform: scale(1.08); }
.card__body { position: absolute; left: 0; right: 0; bottom: 0; z-index: 2; padding: 1.4rem 1.5rem; }
.card__kicker { font-family: var(--f-mono); font-size: .6rem; letter-spacing: .2em; text-transform: uppercase; color: var(--low); }
.card__title { margin-top: .5rem; font-family: var(--f-display); font-weight: 600; font-size: 1.15rem; letter-spacing: -.02em; }
.card__stat { margin-top: .55rem; font-family: var(--f-mono); font-size: .66rem; letter-spacing: .1em; color: var(--mid); }
.card__play {
  position: absolute; z-index: 3; right: 1.1rem; top: 1.1rem;
  width: 38px; height: 38px; border-radius: 50%;
  display: grid; place-items: center;
  background: rgba(255,255,255,.14); backdrop-filter: blur(8px);
  border: 1px solid rgba(255,255,255,.22);
  transition: background-color .3s var(--ease);
}
.card:hover .card__play { background: rgba(255,255,255,.3); }

/* Inline-playing video cards (Content Engine) */
.card--video { cursor: pointer; }
.card--video:focus-visible { outline: 2px solid var(--blue-2); outline-offset: 3px; }
.card__iframe { position: absolute; inset: 0; width: 100%; height: 100%; border: 0; z-index: 4; }
.card__iframe > div, .card__iframe iframe { width: 100%; height: 100%; display: block; border: 0; }
/* Video cards: 16:9 to match the actual footage — the 4:3 default
   left YouTube's letterboxed hqdefault thumbnails showing black bars,
   and would've done the same to the video itself once playing. */
.card--video .card__media { aspect-ratio: 16 / 9; }
.card--video.is-playing { cursor: default; }
.card--video.is-playing .card__media::after,
.card--video.is-playing .card__play,
.card--video.is-playing .card__body { display: none; }

/* mesh variants (stand-ins for photography) */
.mesh-1 { background: radial-gradient(60% 70% at 25% 25%, #00B2FF, transparent 60%), radial-gradient(55% 60% at 78% 30%, #153056, transparent 62%), radial-gradient(70% 70% at 50% 92%, #0B1626, transparent 70%), #0B1626; }
.mesh-2 { background: radial-gradient(55% 65% at 70% 22%, #80E4FF, transparent 60%), radial-gradient(60% 60% at 22% 60%, #2962A5, transparent 62%), #0B1626; }
.mesh-3 { background: radial-gradient(60% 60% at 30% 70%, #4A9CED, transparent 60%), radial-gradient(55% 55% at 76% 34%, #00B2FF, transparent 60%), #081121; }
.mesh-4 { background: radial-gradient(65% 65% at 50% 30%, #153056, transparent 58%), radial-gradient(50% 55% at 18% 82%, #36CBFF, transparent 60%), #081121; }
.mesh-5 { background: radial-gradient(60% 70% at 78% 70%, #2962A5, transparent 60%), radial-gradient(50% 50% at 30% 24%, #00B2FF, transparent 58%), #0B1626; }
.mesh-6 { background: radial-gradient(60% 60% at 40% 40%, #36CBFF, transparent 58%), radial-gradient(60% 60% at 80% 86%, #4A9CED, transparent 60%), #081121; }

/* ---------- Path cards ---------- */
.paths { display: grid; grid-template-columns: repeat(auto-fit, minmax(320px, 1fr)); gap: clamp(1rem, 2vw, 1.6rem); }
.path {
  position: relative; overflow: hidden; border-radius: 20px;
  min-height: clamp(320px, 34vw, 440px);
  display: flex; align-items: flex-end;
  padding: clamp(1.5rem, 3vw, 2.5rem);
  border: 1px solid var(--ink-line);
  transition: transform .55s var(--ease), box-shadow .55s var(--ease), border-color .45s var(--ease);
  --px: .5; --py: .5;
}
.path:hover {
  /* Lift only. The old scale(1.015) stretched the 1px border along with the
     card, which softened the edge on the one element defined by its edge. */
  transform: translateY(-6px);
  border-color: rgba(255,255,255,.26);
  /* Neutral depth instead of a blue glow. The glow implied a light source
     that exists nowhere else on the page and lit the card from underneath,
     which is not how anything is lit. A dark shadow just reads as height. */
  box-shadow: 0 30px 64px -30px rgba(0,0,0,.8);
}
.path__mesh {
  position: absolute; inset: -14%;
  transition: transform .75s var(--ease), filter .55s var(--ease);
  transform: translate3d(calc((var(--px) - .5) * -22px), calc((var(--py) - .5) * -22px), 0) scale(1);
}
.path__mesh img { width: 100%; height: 100%; object-fit: cover; }
.path__mesh--talent img { object-position: 62% 18%; }
/* Slower and shallower than the old 1.1. A big card zooming hard reads as a
   glitch; a slow drift reads as the image waking up. */
.path:hover .path__mesh {
  transform: translate3d(calc((var(--px) - .5) * -22px), calc((var(--py) - .5) * -22px), 0) scale(1.06);
  filter: brightness(1.05);
}
.path::after { content: ""; position: absolute; inset: 0; background: linear-gradient(to top, rgba(8,9,13,.94), rgba(8,9,13,.2) 64%); }
.path__body {
  position: relative; z-index: 2; max-width: 34ch;
  display: flex; align-items: center; gap: clamp(.7rem, 1.4vw, 1.1rem);
}
.path__title { font-family: var(--f-display); font-weight: 600; font-size: clamp(1.8rem, 3.4vw, 2.8rem); letter-spacing: -.03em; }
.path__text { margin-top: .7rem; color: var(--mid); font-size: .97rem; }
/* Arrives with the hover rather than sitting there. Gives the card an
   affordance at the moment you need one, and gives the interaction something
   to say beyond "it got bigger". */
.path__go {
  flex: 0 0 auto;
  width: clamp(34px, 3.4vw, 44px); aspect-ratio: 1;
  display: grid; place-items: center;
  border-radius: 100px;
  border: 1px solid rgba(255,255,255,.34);
  background: rgba(255,255,255,.08);
  color: var(--hi);
  opacity: 0; transform: translateX(-12px);
  transition: opacity .45s var(--ease), transform .45s var(--ease), background-color .35s var(--ease);
}
.path__go svg { width: 42%; height: 42%; }
.path:hover .path__go { opacity: 1; transform: translateX(0); }
.path:hover .path__go { background: rgba(255,255,255,.16); }

/* ---------- Steps ---------- */
.steps { display: grid; grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); gap: clamp(1.6rem, 3vw, 2.6rem); }
.step { padding-top: 1.4rem; border-top: 1px solid var(--ink-line); }
.section--paper .step { border-color: var(--paper-line); }
.step__n { font-family: var(--f-mono); font-size: .64rem; letter-spacing: .2em; color: var(--low); }
.section--paper .step__n { color: var(--on-paper-low); }
.step__t { margin-top: 1.1rem; font-family: var(--f-display); font-weight: 600; font-size: 1.18rem; letter-spacing: -.02em; }
.step__d { margin-top: .7rem; font-size: .94rem; color: var(--mid); }
.section--paper .step__d { color: var(--on-paper-mid); }

/* ---------- Timeline (animated step process) ---------- */
.timeline { position: relative; padding-top: clamp(1.5rem, 3vw, 2.5rem); }
.timeline__track {
  position: absolute; left: 0; right: 0; top: calc(clamp(1.5rem, 3vw, 2.5rem) + 27px);
  height: 2px; background: var(--paper-line); z-index: 0;
  overflow: hidden;
}
.section--ink .timeline__track, .section--ink2 .timeline__track { background: var(--ink-line); }
.timeline__track::after {
  content: ""; position: absolute; inset: 0; background: var(--spectrum);
  transform: scaleX(0); transform-origin: left;
  transition: transform 1.3s var(--ease) .15s;
}
.timeline__track.is-in::after { transform: scaleX(1); }
.timeline__steps {
  position: relative; z-index: 1;
  display: grid; grid-template-columns: repeat(4, 1fr);
  gap: clamp(1.4rem, 3vw, 2.4rem);
}
.timeline__dot {
  display: grid; place-items: center;
  width: 54px; height: 54px; border-radius: 50%;
  background: var(--paper); border: 2px solid var(--paper-line);
  font-family: var(--f-mono); font-weight: 700; font-size: .82rem;
  color: var(--on-paper-low);
  transition: transform .45s var(--ease), background-color .35s var(--ease), color .35s var(--ease), border-color .35s var(--ease), box-shadow .35s var(--ease);
}
.section--ink .timeline__dot, .section--ink2 .timeline__dot { background: var(--ink-2); border-color: var(--ink-line); color: var(--low); }
.timeline__step:hover .timeline__dot {
  transform: scale(1.18) rotate(-8deg);
  background: var(--blue); border-color: var(--blue); color: #fff;
  box-shadow: 0 10px 30px -8px rgba(0,178,255,.55);
}
.timeline__t {
  margin-top: 1.3rem; font-family: var(--f-display); font-weight: 700;
  font-size: clamp(1.1rem, 1.9vw, 1.35rem); letter-spacing: -.02em;
  transition: color .3s var(--ease);
}
.timeline__step:hover .timeline__t { color: var(--blue); }
.timeline__d { margin-top: .6rem; font-size: .92rem; color: var(--mid); }
.section--paper .timeline__d { color: var(--on-paper-mid); }
/* Touch targets. A pointer that cannot hover is a finger, and these links
   were sitting at 18-26px tall, well under the 44px minimum, stacked close
   enough together to make a mis-tap likely. Height only, so nothing moves
   horizontally and the desktop layout is untouched. */
@media (max-width: 760px) {
  .footer__col a,
  .link-arrow,
  .mobile-nav a,
  .skip-link,
  .brand { display: inline-flex; align-items: center; min-height: 44px; }
}

@media (max-width: 760px) {
  .timeline__steps { grid-template-columns: 1fr 1fr; row-gap: 2.4rem; }
  .timeline__track { display: none; }
}

/* ---------- Quote ---------- */
.quote__body { font-family: var(--f-display); font-weight: 500; font-size: clamp(1.4rem, 2.6vw, 2.1rem); line-height: 1.2; letter-spacing: -.025em; max-width: 22ch; }
.quote__by { margin-top: 1.6rem; font-family: var(--f-mono); font-size: .66rem; letter-spacing: .18em; text-transform: uppercase; color: var(--low); }
.section--paper .quote__by { color: var(--on-paper-low); }

/* ---------- Roster ---------- */
.roster { display: grid; grid-template-columns: repeat(auto-fit, minmax(155px, 1fr)); gap: 1rem; }
.roster__card { position: relative; border-radius: 16px; overflow: hidden; aspect-ratio: 3/4; border: 1px solid var(--ink-line); }
.roster__mesh { position: absolute; inset: 0; transition: transform .7s var(--ease); }
.roster__card:hover .roster__mesh { transform: scale(1.07); }
.roster__card::after { content: ""; position: absolute; inset: 0; background: linear-gradient(to top, rgba(8,9,13,.92), transparent 55%); }
.roster__meta { position: absolute; z-index: 2; left: 1rem; right: 1rem; bottom: 1rem; }
.roster__name { font-family: var(--f-display); font-weight: 600; font-size: 1rem; letter-spacing: -.01em; }
.roster__tag { margin-top: .3rem; font-family: var(--f-mono); font-size: .58rem; letter-spacing: .16em; text-transform: uppercase; color: var(--low); }

/* ---------- Pricing / tiers ---------- */
.tiers { display: grid; grid-template-columns: repeat(auto-fit, minmax(260px, 1fr)); gap: 1.2rem; }
.tier { padding: clamp(1.5rem, 2.5vw, 2.2rem); border-radius: 18px; border: 1px solid var(--ink-line); background: var(--ink-2); }
.tier--lead { background: linear-gradient(var(--ink-2), var(--ink-2)) padding-box, var(--spectrum) border-box; border-color: transparent; }
.tier__name { font-family: var(--f-mono); font-size: .64rem; letter-spacing: .2em; text-transform: uppercase; color: var(--low); }
.tier__h { margin-top: 1.1rem; font-family: var(--f-display); font-weight: 600; font-size: 1.45rem; letter-spacing: -.025em; }
.tier__d { margin-top: .8rem; color: var(--mid); font-size: .94rem; }
.tier__list { margin-top: 1.4rem; display: grid; gap: .7rem; }
.tier__list li { display: flex; gap: .7rem; font-size: .92rem; color: var(--mid); }
.tier__list li::before { content: ""; flex: 0 0 auto; width: 6px; height: 6px; margin-top: .55rem; border-radius: 50%; background: var(--spectrum); }

/* ---------- Contact / CTA ---------- */
.cta { position: relative; overflow: hidden; padding-block: clamp(6rem, 14vw, 12rem); background: var(--ink-2); isolation: isolate; }
.cta .wrap { position: relative; z-index: 3; }
.cta__title { font-size: clamp(3rem, 11vw, 9rem); line-height: .92; }
.cta__sub { margin-top: 1.4rem; font-family: var(--f-mono); font-size: .66rem; letter-spacing: .28em; text-transform: uppercase; color: var(--low); }
.cta__actions { margin-top: 2.6rem; display: flex; flex-wrap: wrap; gap: .8rem; }

/* ---------- Form ---------- */
.form { display: grid; gap: 1.1rem; }
.field { display: grid; gap: .5rem; }
.field label { font-family: var(--f-mono); font-size: .62rem; letter-spacing: .18em; text-transform: uppercase; color: var(--low); }
.field input, .field select, .field textarea {
  width: 100%; padding: .85rem 1rem; border-radius: 10px;
  background: rgba(255,255,255,.04); border: 1px solid var(--ink-line);
  color: var(--hi); font-family: var(--f-body); font-size: .95rem;
  transition: border-color .3s var(--ease), background-color .3s var(--ease);
}
.field textarea { min-height: 130px; resize: vertical; }
.field input::placeholder, .field textarea::placeholder { color: rgba(255,255,255,.28); }
.field input:focus, .field select:focus, .field textarea:focus { border-color: rgba(255,255,255,.36); background: rgba(255,255,255,.07); }
.field select option { background: var(--ink-2); color: var(--hi); }
.form__row { display: grid; grid-template-columns: 1fr 1fr; gap: 1.1rem; }
.form__note { font-size: .8rem; color: var(--low); }
.form__status { font-family: var(--f-mono); font-size: .68rem; letter-spacing: .14em; text-transform: uppercase; color: var(--blue-2); min-height: 1.2em; }

/* ---------- Footer ---------- */
.footer { background: var(--ink); padding-block: clamp(3rem, 6vw, 5rem) 2rem; }
.footer__top { display: grid; grid-template-columns: 1.5fr 1fr 1fr 1fr; gap: 2.5rem; }
.footer__col h4 { font-family: var(--f-mono); font-weight: 500; font-size: .62rem; letter-spacing: .2em; text-transform: uppercase; color: var(--low); }
.footer__col ul { margin-top: 1.1rem; display: grid; gap: .7rem; }
.footer__col a { color: var(--mid); font-size: .92rem; transition: color .3s var(--ease); }
.footer__col a:hover { color: var(--hi); }
.footer__brand p { margin-top: 1rem; max-width: 32ch; color: var(--low); font-size: .9rem; }
.footer__bottom {
  margin-top: clamp(2.5rem, 5vw, 3.5rem); padding-top: 1.6rem;
  border-top: 1px solid var(--ink-line);
  display: flex; flex-wrap: wrap; gap: 1rem;
  align-items: center; justify-content: space-between;
}
.footer__bottom p { color: var(--low); font-size: .82rem; }
.footer__legal { display: flex; flex-wrap: wrap; gap: 1.6rem; }
.footer__legal a { color: var(--low); font-size: .82rem; transition: color .3s var(--ease); }
.footer__legal a:hover { color: var(--hi); }

/* ---------- Legal pages (privacy / terms) ---------- */
.legal-layout { display: grid; grid-template-columns: 260px 1fr; gap: clamp(2.5rem, 5vw, 5rem); align-items: start; }
[data-no-copy] { -webkit-user-select: none; -moz-user-select: none; -ms-user-select: none; user-select: none; }
.legal-nav {
  /* Sticks near the top while there's room, then just scrolls along with
     the page once you pass it (no nested scrollbar to fight with). */
  position: sticky; top: calc(var(--head-h) + 1.5rem); align-self: start;
  display: flex; flex-direction: column;
}
.legal-nav a {
  display: block; padding: 0 0 1.3rem 1.1rem; margin-bottom: 1.3rem;
  border-bottom: 1px solid var(--paper-line);
  border-left: 2px solid transparent;
  font-family: var(--f-mono); font-size: .72rem; font-weight: 700;
  letter-spacing: .03em; line-height: 1.5; text-transform: uppercase;
  color: var(--on-paper-low);
  transition: color .25s var(--ease), border-color .25s var(--ease);
}
.legal-nav a:last-child { border-bottom: 0; margin-bottom: 0; }
.legal-nav a:hover { color: var(--on-paper); }
.legal-nav a.is-active { color: var(--on-paper); border-left-color: var(--on-paper); }
.legal-nav__n { color: var(--on-paper-low); margin-right: .5em; }
.legal-nav a.is-active .legal-nav__n, .legal-nav a:hover .legal-nav__n { color: inherit; }

.legal { max-width: 74ch; }
.legal__updated { color: var(--on-paper-low); font-size: .82rem; font-family: var(--f-mono); letter-spacing: .04em; text-transform: uppercase; }
.legal__badge {
  display: table;
  padding: .6rem 1.1rem; margin-bottom: 1.6rem;
  border: 1px solid var(--ink-line); border-radius: 100px;
  font-family: var(--f-mono); font-size: .68rem; font-weight: 700;
  letter-spacing: .08em; text-transform: uppercase; color: var(--mid);
}
.legal h2 { margin-top: clamp(2.6rem, 5vw, 3.4rem); scroll-margin-top: calc(var(--head-h) + 1.5rem); font-size: clamp(1.25rem, 2.4vw, 1.55rem); }
.legal h2:first-of-type { margin-top: 0; }
.legal h2 .legal-nav__n { display: block; font-family: var(--f-mono); font-size: .68rem; font-weight: 700; letter-spacing: .1em; color: var(--blue); margin-bottom: .5rem; }
.legal h3 {
  margin-top: clamp(1.6rem, 3vw, 2.1rem);
  font-family: var(--f-mono); font-size: .74rem; font-weight: 700;
  letter-spacing: .06em; text-transform: uppercase; color: var(--on-paper);
}
.legal h3 + p { margin-top: .7rem; }
.legal__box {
  margin-top: 1rem; padding: 1.2rem 1.4rem;
  border: 1px solid var(--paper-line); border-radius: 12px; background: var(--paper-2);
}
.legal__box p { margin: 0; }
.legal__box p + p { margin-top: .2rem; }
.legal p, .legal li { color: var(--on-paper-mid); font-size: .98rem; line-height: 1.7; }
.legal p + p, .legal ul + p, .legal p + ul { margin-top: 1rem; }
.legal ul { margin-top: .8rem; padding-left: 1.3rem; display: grid; gap: .5rem; list-style: disc; }
.legal a { color: var(--blue); text-decoration: underline; text-underline-offset: 2px; }
.legal strong { color: var(--on-paper); }
.legal__callout {
  margin-top: clamp(1.6rem, 3vw, 2.2rem); padding: 1.3rem 1.5rem;
  border: 1px solid var(--paper-line); border-radius: 12px; background: var(--paper-2);
}
.legal__callout p { color: var(--on-paper); font-weight: 600; font-size: .92rem; margin: 0; }
@media (max-width: 900px) {
  .legal-layout { grid-template-columns: 1fr; }
  .legal-nav { position: static; flex-direction: row; flex-wrap: wrap; gap: .6rem; }
  .legal-nav a {
    flex: 0 0 auto; padding: .6rem .9rem; margin-bottom: 0;
    border: 1px solid var(--paper-line); border-radius: 100px;
  }
  .legal-nav a.is-active { border-color: var(--on-paper); }
}

/* ---------- Reveal ---------- */
[data-reveal] { opacity: 0; transform: translateY(14px); }
[data-reveal].is-in {
  opacity: 1; transform: none;
  transition: opacity .45s var(--ease), transform .45s var(--ease);
  transition-delay: var(--d, 0ms);
}

/* ---------- Responsive ---------- */
@media (max-width: 980px) {
  .split { grid-template-columns: 1fr; }
  .split--sticky > :first-child { position: static; }
  .footer__top { grid-template-columns: 1fr 1fr; }
  /* Narrow desktop still runs the 3D viewer; just stack it under the stats.
     Portrait box for a tall phone (a 4:3 box rendered it tiny). */
  .scale { grid-template-columns: 1fr; }
  .scale__art { aspect-ratio: 4 / 5; min-height: 0; order: 2; }
}

/* Coarse pointer / phone width: the viewer script does not load here
   (see index.html), so drop the art column and let the numbers run the
   full width. Matches the JS gate exactly. */
@media (hover: none), (max-width: 760px) {
  .scale { grid-template-columns: 1fr; }
  .scale__art { display: none; }
}

/* Hero sound control: drop it on touch devices only. There is no hover to
   reveal the slider and the homepage hero has no audio track, so the whole
   control goes. Gated on (pointer: coarse) rather than a width so a narrow
   desktop window keeps it. After the base .hero__volume rule so it wins the
   cascade at equal specificity. */
@media (hover: none) and (pointer: coarse) {
  .hero__volume { display: none; }
}
@media (max-width: 640px) {
  .content-grid { grid-template-columns: 1fr 1fr; aspect-ratio: 4 / 5; }
}
@media (max-width: 1060px) {
  .nav { display: none; }
  .burger { display: grid; margin-left: auto; }
  .header .btn { display: none; }
  .form__row { grid-template-columns: 1fr; }
}
@media (max-width: 560px) {
  .footer__top { grid-template-columns: 1fr; }
  .hero__meta { gap: .6rem 2rem; }
}

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: .01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: .01ms !important;
    scroll-behavior: auto !important;
  }
  [data-reveal] { opacity: 1; transform: none; }
}


/* ---------- Case study pages ---------- */
.case-hero { position: relative; min-height: 100svh; display: flex; align-items: flex-end; padding-block: calc(var(--head-h) + 4rem) clamp(2.5rem, 5vw, 4rem); overflow: hidden; isolation: isolate; }
.case-hero__mesh { position: absolute; inset: -8%; z-index: 0; }
.case-hero__mesh img { width: 100%; height: 100%; object-fit: cover; display: block; }
.case-hero__scrim { position: absolute; inset: 0; z-index: 1; background: linear-gradient(to top, var(--ink) 2%, rgba(8,9,13,.55) 55%, rgba(8,9,13,.7)); }
.case-hero .wrap { position: relative; z-index: 3; }
.case-hero__title { max-width: 18ch; }

.factsheet { display: grid; grid-template-columns: repeat(auto-fit, minmax(160px, 1fr)); gap: clamp(1.5rem, 3vw, 3rem); }
.fact { padding-top: 1.2rem; border-top: 1px solid var(--ink-line); }
.section--paper .fact { border-color: var(--paper-line); }
.fact__k { font-family: var(--f-mono); font-size: .6rem; letter-spacing: .2em; text-transform: uppercase; color: var(--low); }
.section--paper .fact__k { color: var(--on-paper-low); }
.fact__v { margin-top: .7rem; font-family: var(--f-display); font-weight: 600; font-size: 1.05rem; letter-spacing: -.02em; }

.gallery { display: grid; grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); gap: clamp(1rem, 1.6vw, 1.6rem); }
.gallery__tile { position: relative; aspect-ratio: 4/3; border-radius: 16px; overflow: hidden; border: 1px solid var(--ink-line); }
.gallery__tile > div { position: absolute; inset: 0; }
.gallery__tile img { width: 100%; height: 100%; object-fit: cover; display: block; }
.gallery__tile--wide { grid-column: span 2; aspect-ratio: 16/9; }
@media (max-width: 760px) { .gallery__tile--wide { grid-column: span 1; aspect-ratio: 4/3; } }

/* Three phone mockups fanned out — one centered up front, two peeking
   from behind on either side. All absolutely centered on the same
   point so the layout holds at any width. They animate in on scroll:
   the center settles first, then the two side phones flare out from
   behind it (transform is driven by .phone-trio.is-in, set by the
   shared [data-reveal] IntersectionObserver in main.js). */
.phone-trio { position: relative; height: clamp(340px, 44vw, 460px); max-width: 780px; margin-inline: auto; }
.phone-mock {
  position: absolute; top: 50%; left: 50%;
  width: clamp(148px, 19vw, 208px);
  aspect-ratio: 9 / 19.5;
  background: #0b0f1a;
  border-radius: clamp(20px, 3vw, 28px);
  padding: 7px;
  box-shadow: 0 30px 60px -20px rgba(0,0,0,.55), 0 0 0 1px rgba(255,255,255,.08);
  opacity: 0;
  transition: transform 1s var(--ease), opacity 1s var(--ease);
}
.phone-mock__screen { width: 100%; height: 100%; border-radius: clamp(14px, 2.2vw, 20px); overflow: hidden; background: #000; }
.phone-mock__screen img,
.phone-mock__screen video { width: 100%; height: 100%; object-fit: cover; display: block; }

.phone-mock--left { transform: translate(calc(-50% - clamp(84px, 11vw, 132px) - 36px), calc(-50% + 26px)) rotate(-22deg); z-index: 1; transition-delay: 140ms; }
.phone-mock--right { transform: translate(calc(-50% + clamp(84px, 11vw, 132px) + 36px), calc(-50% + 26px)) rotate(22deg); z-index: 1; transition-delay: 240ms; }
.phone-mock--center { width: clamp(172px, 22vw, 240px); transform: translate(-50%, calc(-50% + 34px)) scale(.94); z-index: 2; }

.phone-trio.is-in .phone-mock--left { transform: translate(calc(-50% - clamp(84px, 11vw, 132px)), -50%) rotate(-11deg); opacity: 1; }
.phone-trio.is-in .phone-mock--right { transform: translate(calc(-50% + clamp(84px, 11vw, 132px)), -50%) rotate(11deg); opacity: 1; }
.phone-trio.is-in .phone-mock--center { transform: translate(-50%, -50%) scale(1); opacity: 1; }

/* Two-up layout: phones held to one side, an orbiting stat graphic on
   the other, so neither sits alone in a sea of empty space. Both are
   grid items with absolutely-positioned-only content, so they need an
   explicit width — otherwise auto-margin centering collapses them to
   zero width (visible before as a stray vertical line: a percentage-
   sized ring with 0 width but real height). */
.phone-showcase { display: grid; grid-template-columns: 1fr 1fr; align-items: center; gap: clamp(2rem, 5vw, 4rem); }
@media (max-width: 860px) { .phone-showcase { grid-template-columns: 1fr; } }

/* Phone side: the fanned phones plus a little "views" bar chart and a
   scatter of floating hearts behind them, so that side reads as a
   result, not just a photo prop. */
.phone-panel { position: relative; display: flex; align-items: center; justify-content: center; gap: clamp(1rem, 2.4vw, 1.8rem); flex-wrap: wrap; }
.phone-hearts { position: absolute; inset: 0; z-index: 0; pointer-events: none; overflow: visible; }
.phone-heart {
  position: absolute; bottom: 30%; z-index: 3;
  width: 24px; height: 24px;
  opacity: 0;
  animation: heart-float 4.5s ease-in infinite;
}
.phone-heart svg { width: 100%; height: 100%; filter: drop-shadow(0 0 10px rgba(255,45,110,.85)); }
.phone-heart--1 { left: 8%; animation-delay: 0s; }
.phone-heart--2 { left: 40%; width: 18px; height: 18px; animation-delay: 1.1s; }
.phone-heart--3 { left: 72%; animation-delay: 2.2s; }
.phone-heart--4 { left: 24%; width: 19px; height: 19px; animation-delay: 3.1s; }
.phone-heart--5 { left: 88%; width: 18px; height: 18px; animation-delay: .6s; }
@keyframes heart-float {
  0% { transform: translateY(0) scale(.5) rotate(-6deg); opacity: 0; }
  12% { opacity: 1; }
  85% { opacity: .85; }
  100% { transform: translateY(-220px) scale(1.15) rotate(8deg); opacity: 0; }
}

.reach-stat { position: relative; z-index: 1; flex: 0 0 auto; text-align: center; }
/* Fixed width so the count-up animation's changing digit widths don't
   reflow the flex row it shares with the phones (was visibly jostling
   them sideways every frame). */
.reach-stat__num { min-width: 4.4ch; font-family: var(--f-display); font-weight: 700; font-size: clamp(1.6rem, 3vw, 2.2rem); letter-spacing: -.02em; color: var(--hi); line-height: 1; }
.reach-stat__label { margin: .4rem auto 0; font-family: var(--f-mono); font-size: .58rem; letter-spacing: .1em; text-transform: uppercase; color: var(--mid); max-width: 9ch; text-align: center; }
.reach-stat__bars { margin-top: 1.1rem; display: flex; align-items: flex-end; justify-content: center; gap: 5px; height: 64px; }
.reach-stat__bars span {
  width: 7px; border-radius: 3px 3px 0 0;
  background: linear-gradient(to top, var(--blue-3), var(--hi));
  transform: scaleY(0); transform-origin: bottom;
  transition: transform .8s var(--ease);
  box-shadow: 0 0 10px rgba(0,178,255,.4);
}
.reach-stat.is-in .reach-stat__bars span { transform: scaleY(1); }
.reach-stat__bars span:nth-child(1) { height: 26%; transition-delay: .05s; }
.reach-stat__bars span:nth-child(2) { height: 44%; transition-delay: .15s; }
.reach-stat__bars span:nth-child(3) { height: 62%; transition-delay: .25s; }
.reach-stat__bars span:nth-child(4) { height: 82%; transition-delay: .35s; }
.reach-stat__bars span:nth-child(5) { height: 100%; transition-delay: .45s; }

.phone-trio--side { width: 100%; height: clamp(380px, 38vw, 480px); max-width: 600px; }
.phone-trio--side .phone-mock--left,
.phone-trio--side .phone-mock--right { width: clamp(128px, 15vw, 176px); }
.phone-trio--side .phone-mock--center { width: clamp(150px, 17vw, 202px); }
.phone-trio--side .phone-mock--left { transition-delay: 140ms; }
.phone-trio--side.is-in .phone-mock--left { transform: translate(calc(-50% - clamp(112px, 13vw, 168px)), -50%) rotate(-11deg); }
.phone-trio--side.is-in .phone-mock--right { transform: translate(calc(-50% + clamp(112px, 13vw, 168px)), -50%) rotate(11deg); }
.phone-trio--side .phone-mock--left { transform: translate(calc(-50% - clamp(112px, 13vw, 168px) - 34px), calc(-50% + 24px)) rotate(-22deg); }
.phone-trio--side .phone-mock--right { transform: translate(calc(-50% + clamp(112px, 13vw, 168px) + 34px), calc(-50% + 24px)) rotate(22deg); }

/* Small screens: pull the fan in so the right-hand phone stays inside the
   viewport instead of spilling and forcing a horizontal scroll. The trio
   also gets a hard clip as a backstop. */
@media (max-width: 560px) {
  .phone-showcase { overflow-x: clip; }
  .phone-trio--side { height: clamp(340px, 92vw, 400px); }
  .phone-trio--side .phone-mock--left,
  .phone-trio--side .phone-mock--right { width: clamp(104px, 30vw, 132px); }
  .phone-trio--side .phone-mock--center { width: clamp(124px, 36vw, 158px); }
  .phone-trio--side.is-in .phone-mock--left  { transform: translate(calc(-50% - clamp(78px, 24vw, 104px)), -50%) rotate(-10deg); }
  .phone-trio--side.is-in .phone-mock--right { transform: translate(calc(-50% + clamp(78px, 24vw, 104px)), -50%) rotate(10deg); }
  .phone-trio--side .phone-mock--left  { transform: translate(calc(-50% - clamp(78px, 24vw, 104px) - 22px), calc(-50% + 20px)) rotate(-20deg); }
  .phone-trio--side .phone-mock--right { transform: translate(calc(-50% + clamp(78px, 24vw, 104px) + 22px), calc(-50% + 20px)) rotate(20deg); }
}

/* Orbit graphic: a soft pulsing glow behind a floating, slowly
   turning 3D logo, with platform badges that continuously revolve
   around it in a real circular orbit — the badges sit inside a
   rotating layer (.orbit__satellites) while each icon counter-rotates
   at the same speed so it stays upright as it travels. Hovering a
   badge lifts it and pops a little sparkle burst. */
.orbit { position: relative; width: 100%; height: clamp(420px, 42vw, 560px); max-width: 640px; margin-inline: auto; }

.orbit__glow {
  position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%);
  width: clamp(180px, 22vw, 260px); height: clamp(180px, 22vw, 260px);
  border-radius: 50%;
  background: radial-gradient(circle, rgba(0,178,255,.4), rgba(0,178,255,0) 70%);
  filter: blur(4px);
  animation: orbit-glow-pulse 5s ease-in-out infinite;
  pointer-events: none;
}
@keyframes orbit-glow-pulse {
  0%, 100% { opacity: .65; transform: translate(-50%, -50%) scale(1); }
  50% { opacity: 1; transform: translate(-50%, -50%) scale(1.15); }
}

.orbit__accents { position: absolute; inset: 0; animation: orbit-spin 20s linear infinite; pointer-events: none; }
.orbit__spark { position: absolute; width: 6px; height: 6px; border-radius: 50%; background: var(--hi); box-shadow: 0 0 14px 3px rgba(0,178,255,.7); }
.orbit__spark--a { top: 6%; left: 50%; }
.orbit__spark--b { top: 50%; right: 4%; }
.orbit__spark--c { bottom: 10%; left: 16%; }
@keyframes orbit-spin { to { transform: rotate(360deg); } }

.orbit__core {
  position: absolute; top: 50%; left: 50%;
  width: clamp(130px, 15vw, 190px); height: clamp(130px, 15vw, 190px);
  display: flex; align-items: center; justify-content: center;
  animation: orbit-core-float 6s ease-in-out infinite;
}
.orbit__core img { width: 100%; height: 100%; object-fit: contain; filter: drop-shadow(0 0 40px rgba(0,178,255,.5)); }
@keyframes orbit-core-float {
  0%, 100% { transform: translate(-50%, -50%) translateY(0) rotate(-2deg); }
  50% { transform: translate(-50%, -50%) translateY(-10px) rotate(2deg); }
}

/* Rotating carrier for the badges. Full 360deg every 46s. */
.orbit__satellites { position: absolute; inset: 0; animation: orbit-spin 46s linear infinite; }
.orbit__badge {
  position: absolute; top: 50%; left: 50%;
  width: clamp(92px, 10vw, 128px);
  opacity: 0; transition: transform .9s var(--ease), opacity .9s var(--ease);
}
/* Counter-spins at the same speed so the icon and label stay upright
   while the badge itself travels around the ring. */
.orbit__badge-spin {
  display: flex; flex-direction: column; align-items: center; gap: .6rem;
  animation: orbit-spin-reverse 46s linear infinite;
}
@keyframes orbit-spin-reverse { to { transform: rotate(-360deg); } }

.orbit__badge-icon {
  position: relative;
  width: 100%; aspect-ratio: 1;
  display: flex; align-items: center; justify-content: center;
  cursor: pointer;
  transition: transform .4s var(--ease), filter .4s var(--ease);
}
.orbit__badge-icon img { width: 100%; height: 100%; object-fit: contain; filter: drop-shadow(0 10px 22px rgba(0,0,0,.5)); }
.orbit__badge:hover .orbit__badge-icon { transform: scale(1.16) translateY(-6px) rotate(-4deg); }
.orbit__badge--tiktok .orbit__badge-icon img { filter: drop-shadow(0 10px 22px rgba(0,0,0,.5)) drop-shadow(0 0 22px rgba(37,244,238,.55)); }
.orbit__badge--instagram .orbit__badge-icon img { filter: drop-shadow(0 10px 22px rgba(0,0,0,.5)) drop-shadow(0 0 22px rgba(219,39,119,.5)); }
.orbit__badge--shorts .orbit__badge-icon img { filter: drop-shadow(0 10px 22px rgba(0,0,0,.5)) drop-shadow(0 0 22px rgba(255,26,26,.45)); }
.orbit__badge--twitch .orbit__badge-icon img { filter: drop-shadow(0 10px 22px rgba(0,0,0,.5)) drop-shadow(0 0 22px rgba(145,70,255,.55)); }
.orbit__badge:hover .orbit__badge-icon img { filter: drop-shadow(0 14px 28px rgba(0,0,0,.55)) drop-shadow(0 0 34px rgba(0,178,255,.5)); }
.orbit__badge-label { font-family: var(--f-mono); font-size: .64rem; letter-spacing: .1em; text-transform: uppercase; color: var(--mid); text-align: center; }

.orbit__sparkle {
  position: absolute; width: 11px; height: 11px; background: #fff;
  clip-path: polygon(50% 0%, 61% 39%, 100% 50%, 61% 61%, 50% 100%, 39% 61%, 0% 50%, 39% 39%);
  opacity: 0; transform: scale(.3) rotate(0deg);
  transition: opacity .35s ease, transform .45s ease;
  pointer-events: none;
}
.orbit__sparkle--1 { top: -8%; right: 2%; }
.orbit__sparkle--2 { bottom: -2%; left: -10%; transition-delay: .08s; }
.orbit__sparkle--3 { top: 32%; right: -14%; width: 8px; height: 8px; transition-delay: .16s; }
.orbit__badge:hover .orbit__sparkle { opacity: 1; transform: scale(1) rotate(90deg); }

/* Four satellites on a diamond: top / right / bottom / left, all one radius
   from the core so the ring reads as even with the fourth logo added. */
.orbit__badge--tiktok { transform: translate(-50%, -50%); transition-delay: 120ms; }
.orbit__badge--twitch { transform: translate(-50%, -50%); transition-delay: 200ms; }
.orbit__badge--instagram { transform: translate(-50%, -50%); transition-delay: 280ms; }
.orbit__badge--shorts { transform: translate(-50%, -50%); transition-delay: 360ms; }

.orbit.is-in .orbit__badge--tiktok    { transform: translate(-50%, calc(-50% - clamp(140px, 16vw, 200px))); opacity: 1; }
.orbit.is-in .orbit__badge--twitch    { transform: translate(calc(-50% + clamp(140px, 16vw, 200px)), -50%); opacity: 1; }
.orbit.is-in .orbit__badge--instagram { transform: translate(-50%, calc(-50% + clamp(140px, 16vw, 200px))); opacity: 1; }
.orbit.is-in .orbit__badge--shorts    { transform: translate(calc(-50% - clamp(140px, 16vw, 200px)), -50%); opacity: 1; }

/* Phones: the 140px minimum travel pushed the left/right badges (Shorts,
   Twitch) half off a 375px screen, and the section's overflow clip sliced
   the icon and its coloured glow into a hard-edged rectangle. Pull the
   diamond in and size the badges down so the whole thing sits inside the
   frame with margin to spare. Desktop is untouched. */
@media (max-width: 560px) {
  .orbit { height: clamp(340px, 96vw, 400px); }
  .orbit__badge { width: clamp(58px, 17vw, 74px); }
  .orbit__badge-spin { gap: .5rem; }
  .orbit__core { width: clamp(104px, 30vw, 132px); height: clamp(104px, 30vw, 132px); }

  .orbit.is-in .orbit__badge--tiktok    { transform: translate(-50%, calc(-50% - clamp(104px, 30vw, 126px))); }
  .orbit.is-in .orbit__badge--twitch    { transform: translate(calc(-50% + clamp(104px, 30vw, 126px)), -50%); }
  .orbit.is-in .orbit__badge--instagram { transform: translate(-50%, calc(-50% + clamp(104px, 30vw, 126px))); }
  .orbit.is-in .orbit__badge--shorts    { transform: translate(calc(-50% - clamp(104px, 30vw, 126px)), -50%); }
}

.deliverables { display: grid; gap: 0; }
.deliverables li {
  display: flex; gap: clamp(1rem, 3vw, 3rem); align-items: baseline;
  padding-block: 1.3rem; border-top: 1px solid var(--paper-line);
  font-family: var(--f-display); font-weight: 600;
  font-size: clamp(1.05rem, 1.7vw, 1.45rem); letter-spacing: -.02em;
}
.deliverables li:last-child { border-bottom: 1px solid var(--paper-line); }
.deliverables span {
  flex: 0 0 auto; width: 3ch;
  font-family: var(--f-mono); font-weight: 400; font-size: .64rem;
  letter-spacing: .18em; color: var(--on-paper-low);
}

.next-case { display: block; position: relative; overflow: hidden; border-radius: 20px; min-height: clamp(260px, 26vw, 380px); display: flex; align-items: flex-end; padding: clamp(1.5rem, 3vw, 2.5rem); border: 1px solid var(--ink-line); transition: transform .5s var(--ease); }
.next-case:hover { transform: translateY(-6px); }
.next-case__mesh { position: absolute; inset: -10%; transition: transform .9s var(--ease); }
.next-case:hover .next-case__mesh { transform: scale(1.06); }
.next-case::after { content: ""; position: absolute; inset: 0; background: linear-gradient(to top, rgba(8,9,13,.94), rgba(8,9,13,.2) 66%); }
.next-case__body { position: relative; z-index: 2; }

@media (min-width: 1500px) {
  .work-grid { grid-template-columns: repeat(3, 1fr); }
}

/* ============ PRELOADER ============
   Covers the first paint while the webfont, the hero video, and the
   above-the-fold art settle, so visitors land on a finished page instead
   of a half-built one.

   Only ever visible while <html> carries .is-loading, which a one-line
   inline script in each page's head adds. No JS means no class means no
   overlay, so the page renders normally rather than hiding behind
   something nothing is left to remove. */
.preloader {
  position: fixed;
  inset: 0;
  z-index: 9999;
  display: grid;
  place-items: center;
  background: var(--ink);
  /* Parked above the fold: the panel travels up to reveal the page, so its
     resting state is where it ends, not where it starts. */
  transform: translateY(-100%);
  visibility: hidden;
  /* The site's --ease is a hard ease-out that spends most of its movement in
     the first third. Across a full viewport that reads as a snap followed by
     a crawl, so the curtain gets a longer, evenly weighted curve of its own. */
  transition: transform 1.05s cubic-bezier(.65, 0, .35, 1), visibility 0s linear 1.05s;
  /* Promote before it moves, so the travel runs on the compositor rather than
     competing with video decode and the 3D viewer booting on the main thread. */
  will-change: transform;
  /* Belt and braces. Going non-interactive otherwise depends on the exit
     transition actually finishing, and a tab loaded in the background can sit
     on a frozen transition for as long as it stays unfocused. Without this a
     full-screen panel you cannot see still swallows every click. */
  pointer-events: none;
}

.is-loading .preloader {
  transform: translateY(0);
  visibility: visible;
  transition: none;
  /* While it genuinely covers the page, it should absorb input. */
  pointer-events: auto;
  /* Last-resort exit. If main.js 404s or throws before it can clear the
     class, the panel still lifts instead of stranding the visitor. */
  animation: preloader-failsafe 1.05s cubic-bezier(.65, 0, .35, 1) 8s forwards;
}

.preloader__inner {
  display: grid;
  justify-items: center;
  padding: var(--gut);
}

/* Animated wordmark. Blades slide in from the lower left, the letters
   rise, the cyan bolt lands last. Lifted from the standalone intro the
   client supplied, trimmed to the mark itself. */
.preloader__mark {
  width: clamp(200px, 42vw, 520px);
  height: auto;
  overflow: visible;
}
.preloader__glow {
  filter:
    drop-shadow(0 0 1px  rgba(255,255,255,.95))
    drop-shadow(0 0 6px  rgba(255,255,255,.7))
    drop-shadow(0 0 24px rgba(240,247,255,.4));
}
.preloader__blade,
.preloader__letter { fill: #fff; opacity: 0; }
.preloader__bolt   { fill: var(--blue); opacity: 0; }

.preloader__blade {
  transform: translate(-26px, 14px);
  animation: preloader-blade .78s cubic-bezier(.22, 1, .36, 1) forwards;
  animation-delay: calc(.24s + var(--i, 0) * .08s);
}
.preloader__letter {
  transform: translateY(30px);
  animation: preloader-rise .8s cubic-bezier(.22, 1, .36, 1) forwards;
  animation-delay: calc(.5s + var(--i, 0) * .07s);
}
.preloader__bolt {
  transform: translate(-18px, 10px);
  animation: preloader-blade .78s cubic-bezier(.22, 1, .36, 1) .56s forwards;
}

@keyframes preloader-blade { to { opacity: 1; transform: translate(0, 0); } }
@keyframes preloader-rise  { to { opacity: 1; transform: translateY(0); } }

@keyframes preloader-failsafe {
  to { transform: translateY(-100%); visibility: hidden; }
}

@keyframes preloader-failsafe-fade {
  to { opacity: 0; visibility: hidden; }
}

@media (prefers-reduced-motion: reduce) {
  /* A full-height panel travelling up the screen is exactly the motion
     this setting asks us to drop. Cross-fade instead, which carries no
     movement, and hold the loading bar still. */
  .preloader {
    transform: none;
    opacity: 0;
    transition: opacity .3s linear, visibility 0s linear .3s;
  }
  .is-loading .preloader {
    transform: none;
    opacity: 1;
    animation: preloader-failsafe-fade .3s linear 8s forwards;
  }
  .preloader__inner { transition: none; }
  /* No blade slide, no letter rise, no colour climb: the mark simply
     shows, and the cross-fade above carries the curtain. */
  .preloader__blade,
  .preloader__letter,
  .preloader__bolt { animation: none; opacity: 1; transform: none; }
}

/* Announced to screen readers, invisible to everyone else. */
.u-sr-only {
  position: absolute;
  width: 1px; height: 1px;
  margin: -1px; padding: 0;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}
