/* ============================================================
   FORGE STUDIOS | Premium dark build

   Brand, from the company brand sheet:
     purple  #753a96      orange  #f59421
     fonts   Champagne & Limousines, Montserrat, Poppins

   The brand purple is specified against white, where it reads
   well. On this site's near-black it only reaches 2.67:1, below
   the 4.5:1 that body text needs, so it is kept as
   --purple-brand for fills, glows and gradients, and paired with
   --purple, the same hue lifted to 4.92:1 for anything that has
   to be read. Both are the brand purple, one is legible on black.

   Champagne & Limousines is the wordmark face. Its licence
   forbids web embedding without a paid commercial licence, so it
   is named first in --font-logo and renders for anyone who has it
   installed, with Poppins carrying the same geometric shape for
   everyone else. Add an @font-face here once the licence is
   bought and the wordmark switches over on its own.

   Fonts load from a <link> in the head, not @import, so the
   browser fetches them in parallel with this stylesheet.
   ============================================================ */

:root {
  --ink:          #08080a;
  --ink-2:        #0e0e12;
  --surface:      #131318;
  --line:         rgba(255,255,255,0.09);
  --line-2:       rgba(255,255,255,0.16);
  --white:        #ffffff;
  --muted:        rgba(255,255,255,0.52);
  --muted-2:      rgba(255,255,255,0.58);
  --orange:       #f59421;
  --purple-brand: #753a96;
  --purple:       #a165c3;
  --grad:         linear-gradient(120deg, #f59421 0%, #a165c3 100%);
  --font:         'Montserrat', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
  --font-display: 'Poppins', 'Montserrat', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
  --font-logo:    'Champagne & Limousines', 'Poppins', 'Montserrat', sans-serif;
  /* Elevation scale. The page had one surface and one border colour, so
     nothing read as an object sitting on a background. These are added
     alongside the originals, which stay in place for anything already
     using them. */
  --surface-1:    #101015;
  --surface-2:    #16161d;
  --surface-3:    #1c1c25;
  --line-soft:    rgba(255,255,255,0.06);
  --line-strong:  rgba(255,255,255,0.14);
  --edge-hi:      rgba(255,255,255,0.08);
  /* ── Motion tokens (B-1). Nothing animates longer than --dur-5.
     Only transform, opacity, clip-path, mask-image and
     filter: brightness/saturate may be animated. */
  --dur-1: 120ms;
  --dur-2: 200ms;
  --dur-3: 320ms;
  --dur-4: 560ms;
  --dur-5: 900ms;
  --ease-out: cubic-bezier(0.22, 1, 0.36, 1);
  --ease-in:  cubic-bezier(0.55, 0, 1, 0.45);
  --ease-io:  cubic-bezier(0.65, 0, 0.35, 1);
  --ease-spring: linear(0, 0.4, 0.9, 1.06, 1.02, 1);
  --lift-1: 6px;
  --lift-2: 18px;
  --lift-3: 40px;
  --ease:         cubic-bezier(0.22, 1, 0.36, 1);
  --pad:          clamp(20px, 5vw, 80px);
}

*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }

html {
  -webkit-text-size-adjust: 100%;
  /* Covers the paths Lenis does not intercept: arriving with a hash,
     browser find-on-page, and keyboard focus jumps. forge.js measures
     the header for its own scrolling. */
  scroll-padding-top: 124px;
}

/* Lenis needs the page to scroll natively; it hijacks the wheel only */
html.lenis, html.lenis body { height: auto; }
.lenis.lenis-smooth { scroll-behavior: auto !important; }

body {
  font-family: var(--font);
  background: var(--ink);
  color: var(--white);
  line-height: 1.6;
  overflow-x: hidden;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
}

/* height:auto is required now that every <img> declares its intrinsic
   width and height. max-width:100% shrinks the width on narrow
   screens, and without height:auto the declared height would stay
   fixed and squash the picture. */
img, video { max-width: 100%; height: auto; display: block; }
a { color: inherit; text-decoration: none; }
ul { list-style: none; }

/* ── Layout ─────────────────────────────────────────────── */
.wrap {
  width: 100%;
  max-width: 1440px;
  margin: 0 auto;
  padding-left: var(--pad);
  padding-right: var(--pad);
}

.sec { padding: clamp(56px, 6vw, 86px) 0; position: relative; }

/* ── Type ───────────────────────────────────────────────── */
.display {
  font-size: clamp(44px, 9.2vw, 148px);
  font-weight: 900;
  line-height: 0.92;
  letter-spacing: -0.045em;
  text-transform: none;
}

h2.big {
  font-size: clamp(34px, 6.2vw, 92px);
  font-weight: 900;
  line-height: 0.98;
  letter-spacing: -0.038em;
}

h3 {
  font-size: clamp(19px, 2vw, 26px);
  font-weight: 700;
  letter-spacing: -0.02em;
  line-height: 1.25;
}

p { font-size: clamp(14px, 1.1vw, 17px); font-weight: 300; color: var(--muted); line-height: 1.75; }

/* Poppins carries every display-scale element, Montserrat the running
   text and the small UI labels. Listing the display selectors in one
   place keeps the two faces from drifting apart. */
.display, h2.big, h3, .drawer a, .q-btn,
.hero-meta .v, .rail-foot .tot, .row .fig, .row .n,
.svc-t, .svc-n, .cs-sv, .cs-quote, .legal h2,
.vmeta blockquote, .pillar h3, .num-tag {
  font-family: var(--font-display);
}

.lede { font-size: clamp(16px, 1.4vw, 21px); font-weight: 300; color: rgba(255,255,255,0.7); line-height: 1.65; }

.grad { background: var(--grad); -webkit-background-clip: text; background-clip: text; -webkit-text-fill-color: transparent; }

/* When .grad sits inside a split heading the splitter moves the text
   down into .sp-wi or .sp-c, so .grad is left clipping a gradient to an
   empty text box while the child inherits a transparent fill and paints
   nothing. That is why "buys" in the statement line rendered as a gap.
   Push the fill down to whichever element actually holds the text.
   Standalone .grad has no split children, so the stat figures are
   untouched by this. */
/* Each word is its own inline-block, so each was painting a complete
   orange to purple ramp and a four word phrase read as a rainbow.
   forge.js measures the whole .grad phrase and gives every word a
   background sized to the phrase and offset to the word's position in
   it, so the ramp is continuous across the phrase and across wrapped
   lines. no-repeat matters: without it the ramp tiles. */
.grad .sp-wi,
.grad .sp-c {
  background-image: var(--grad);
  background-repeat: no-repeat;
  -webkit-background-clip: text;
          background-clip: text;
  -webkit-text-fill-color: transparent;
}
/* Gradient words fade rather than rise. A Y translate would carry the
   measured background with them and the ramp would visibly slide. */
.js-anim .grad .sp-wi { transform: none; opacity: 0; }

.eyebrow {
  display: inline-block;
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.28em;
  text-transform: uppercase;
  color: var(--orange);
  margin-bottom: clamp(18px, 2.4vw, 34px);
}

.num-tag {
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.2em;
  color: var(--muted-2);
  font-variant-numeric: tabular-nums;
}

/* ── Buttons ────────────────────────────────────────────── */
.btn {
  position: relative;
  display: inline-flex;
  align-items: center;
  gap: 12px;
  font-size: 12px;
  font-weight: 700;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  padding: 19px 36px;
  border-radius: 100px;
  overflow: hidden;
  isolation: isolate;
  transition: color 0.4s var(--ease), border-color 0.4s var(--ease);
  border: 1px solid transparent;
  cursor: pointer;
  font-family: var(--font);
}

.btn svg { width: 15px; height: 15px; transition: transform 0.45s var(--ease); }
.btn:hover svg { transform: translateX(5px); }

.btn-fill { background: var(--orange); color: #0a0a0a; }
.btn-fill::before {
  content: ''; position: absolute; inset: 0; z-index: -1;
  background: var(--grad);
  transform: translateY(101%);
  transition: transform 0.5s var(--ease);
}
.btn-fill:hover::before { transform: translateY(0); }
.btn-fill:hover { color: #fff; }

.btn-ghost { border-color: var(--line-2); color: #fff; }
.btn-ghost::before {
  content: ''; position: absolute; inset: 0; z-index: -1;
  background: #fff; transform: translateY(101%);
  transition: transform 0.5s var(--ease);
}
.btn-ghost:hover::before { transform: translateY(0); }
.btn-ghost:hover { color: #0a0a0a; border-color: #fff; }

/* ── Nav ────────────────────────────────────────────────── */
.nav {
  position: fixed; top: 0; left: 0; right: 0; z-index: 200;
  padding: 26px 0;
  transition: padding 0.5s var(--ease), background 0.5s var(--ease), backdrop-filter 0.5s var(--ease);
}
.nav.stuck {
  padding: 14px 0;
  background: rgba(8,8,10,0.7);
  /* A full width backdrop-filter repaints on every scroll frame. A
     near solid fill costs nothing and reads the same on this palette. */
  background: rgba(8,8,10,0.92);
  border-bottom: 1px solid var(--line);
}
.nav-in { display: flex; align-items: center; justify-content: space-between; gap: 24px; }

/* The wordmark is a stacked lockup on the brand sheet: FORGE on top
   with a purple O, STUDIOS underneath, smaller, letterspaced and
   ranged right to FORGE's edge. Rendering it as one inline word,
   FORGESTUDIOS, is what made it read as a placeholder rather than
   the real mark. This is still type standing in for artwork, so
   replace it with the logo file when that is available. */
.logo {
  font-family: var(--font-logo);
  display: inline-flex; flex-direction: column; align-items: flex-end;
  line-height: 0.92; text-align: right;
}
.logo b {
  color: var(--orange); font-weight: 800;
  font-size: 20px; letter-spacing: 0.06em;
}
.logo b i { color: var(--purple); font-style: normal; }
.logo span {
  color: var(--purple); font-weight: 500;
  font-size: 9.5px; letter-spacing: 0.30em;
  margin-top: 3px; margin-right: -0.30em;
}
.foot-brand .logo b { font-size: 23px; }
.foot-brand .logo span { font-size: 11px; }

.nav-links { display: flex; gap: 38px; }
.nav-links a {
  position: relative;
  font-size: 11.5px; font-weight: 600; letter-spacing: 0.14em;
  text-transform: uppercase; color: var(--muted);
  transition: color 0.35s ease;
}
.nav-links a::after {
  content: ''; position: absolute; left: 0; bottom: -6px;
  width: 100%; height: 1px; background: var(--orange);
  transform: scaleX(0); transform-origin: right;
  transition: transform 0.45s var(--ease);
}
.nav-links a:hover { color: #fff; }
.nav-links a:hover::after { transform: scaleX(1); transform-origin: left; }
.nav-links a.on { color: #fff; }
.nav-links a.on::after { transform: scaleX(1); }

.nav-cta { font-size: 11px; font-weight: 700; letter-spacing: 0.14em; text-transform: uppercase;
  padding: 13px 26px; border-radius: 100px; background: var(--orange); color: #0a0a0a;
  transition: background 0.4s ease, transform 0.4s var(--ease); }
.nav-cta:hover { background: #fff; transform: translateY(-2px); }

.burger { display: none; width: 26px; height: 16px; position: relative; background: none; border: 0; cursor: pointer; }
.burger i { position: absolute; left: 0; width: 100%; height: 1.5px; background: #fff; transition: 0.4s var(--ease); }
.burger i:nth-child(1) { top: 0; }
.burger i:nth-child(2) { bottom: 0; }
.burger.x i:nth-child(1) { top: 7px; transform: rotate(45deg); }
.burger.x i:nth-child(2) { bottom: 7px; transform: rotate(-45deg); }

.drawer {
  position: fixed; inset: 0; z-index: 190;
  background: var(--ink); padding: 120px var(--pad) 60px;
  display: flex; flex-direction: column; gap: 6px;
  clip-path: inset(0 0 100% 0);
  transition: clip-path 0.7s var(--ease);
  pointer-events: none;
}
.drawer.open { clip-path: inset(0 0 0 0); pointer-events: auto; }
.drawer a { font-size: clamp(30px, 8vw, 54px); font-weight: 900; letter-spacing: -0.03em; padding: 10px 0; }
.drawer .btn { margin-top: 28px; align-self: flex-start; }

/* ── Hero ───────────────────────────────────────────────── */
.hero { min-height: 100svh; display: flex; flex-direction: column; justify-content: center;
  padding-top: 140px; padding-bottom: 60px; position: relative; overflow: hidden; }

.hero-orb { position: absolute; border-radius: 50%; pointer-events: none; z-index: 0; }
.orb-a { width: 64vw; height: 64vw; top: -22vw; right: -16vw;
  background: radial-gradient(circle, rgba(245,148,33,0.20), rgba(8,8,10,0) 62%); }
.orb-b { width: 58vw; height: 58vw; bottom: -24vw; left: -18vw;
  background: radial-gradient(circle, rgba(117,58,150,0.34), rgba(8,8,10,0) 62%); }

.hero-in { position: relative; z-index: 1; }


@media (min-width: 1100px) {
  }
.hero h1 { margin-bottom: clamp(26px, 3vw, 44px); }
.line-mask { display: block; overflow: hidden; }
.hero .lede { max-width: 560px; margin-bottom: clamp(32px, 4vw, 52px); }
.hero-btns { display: flex; gap: 14px; flex-wrap: wrap; }

.hero-meta {
  margin-top: clamp(48px, 7vw, 96px);
  display: grid; grid-template-columns: repeat(3, auto);
  gap: clamp(28px, 6vw, 90px); justify-content: start;
  padding-top: 30px; border-top: 1px solid var(--line);
}
.hero-meta .v { font-size: clamp(26px, 3.4vw, 46px); font-weight: 900; letter-spacing: -0.03em; line-height: 1; }
.hero-meta .k { font-size: 10.5px; font-weight: 600; letter-spacing: 0.18em; text-transform: uppercase;
  color: var(--muted-2); margin-top: 9px;
  /* PEAK REACH GROWTH wraps to two lines while the others do not, which
     dropped the three figures off a shared baseline. */
  min-height: 2.6em; }

/* ── Scroll cue ─────────────────────────────────────────────
   A thin rule with a highlight travelling down it. No bouncing
   mouse, and it stops entirely under reduced motion. */
/* Sits on the hero itself, not inside the content grid. Anchored to
   the right gutter, which is the one part of the hero with nothing in
   it, so it cannot land on the stat row the way it did before. */
.scroll-cue {
  /* Anchored to the first viewport, not to the bottom of the hero. The
     hero runs taller than 100svh once the stat row is in, so a bottom
     anchor put the cue below the fold where nobody could see it. The
     hero starts at the top of the document, so this lands the cue a
     fixed distance above the fold with no script. */
  position: absolute; right: var(--pad); top: calc(100svh - 52px); z-index: 2;
  display: flex; align-items: center; gap: 10px;
  font-size: 9px; font-weight: 700; letter-spacing: 0.22em;
  text-transform: uppercase; color: var(--muted-2);
  pointer-events: none;
}
.scroll-cue i {
  display: block; width: 1px; height: 30px;
  background: var(--line-2); position: relative; overflow: hidden;
}
.scroll-cue i::after {
  content: ''; position: absolute; left: 0; top: 0;
  width: 100%; height: 42%;
  background: linear-gradient(180deg, rgba(245,148,33,0), var(--orange));
  animation: cue 2s var(--ease) infinite;
}
@keyframes cue {
  0% { transform: translateY(-100%); }
  60%, 100% { transform: translateY(240%); }
}
@media (prefers-reduced-motion: reduce) {
  .scroll-cue i::after { animation: none; transform: none; }
}
@media (max-width: 1099px), (max-height: 760px) { .scroll-cue { display: none; } }

/* ── Marquee ────────────────────────────────────────────── */
.strip { padding: 14px 0; border-top: 1px solid var(--line); border-bottom: 1px solid var(--line);
  overflow: hidden; white-space: nowrap; background: var(--ink-2); }
.strip-track { display: inline-flex; gap: 56px; animation: slide 34s linear infinite;
  will-change: transform; }
.strip-track:hover { animation-play-state: paused; }
.strip span { font-size: clamp(13px, 1.5vw, 20px); font-weight: 800; letter-spacing: -0.01em;
  color: rgba(255,255,255,0.46); display: inline-flex; align-items: center; gap: 56px; }
.strip span i { color: var(--orange); font-style: normal; font-size: 0.7em; }
@keyframes slide { to { transform: translateX(-50%); } }

/* ── Section heading ────────────────────────────────────── */
.sec-head { max-width: 900px; margin-bottom: clamp(26px, 2.8vw, 44px); }
.sec-head.mid { margin-left: auto; margin-right: auto; text-align: center; }
.sec-head p { margin-top: 22px; max-width: 560px; }
.sec-head.mid p { margin-left: auto; margin-right: auto; }

/* ── Problem rows ───────────────────────────────────────── */
.rows { border-top: 1px solid var(--line); }
.row {
  display: grid; grid-template-columns: 70px 1fr 320px;
  gap: clamp(20px, 4vw, 60px); align-items: center;
  padding: clamp(24px, 3vw, 40px) 0;
  border-bottom: 1px solid var(--line);
  transition: background 0.5s var(--ease), padding-left 0.5s var(--ease);
}
.row:hover { background: rgba(255,255,255,0.018); padding-left: 18px; }
.row .n { font-size: clamp(13px, 1.2vw, 15px); font-weight: 700; color: var(--muted-2); font-variant-numeric: tabular-nums; }
.row h3 { margin-bottom: 10px; }
.row p { max-width: 620px; }
.row .fig { font-size: clamp(22px, 2.6vw, 34px); font-weight: 900; letter-spacing: -0.03em; line-height: 1.05; }
.row .fig-k { font-size: 12px; color: var(--muted-2); margin-top: 8px; line-height: 1.55; font-weight: 300; }

/* ── Reel rail ──────────────────────────────────────────── */
.rail { display: flex; gap: 18px; overflow-x: auto; padding-bottom: 14px; scrollbar-width: none; }
.rail::-webkit-scrollbar { display: none; }
.reel {
  position: relative; flex: 0 0 clamp(180px, 20vw, 250px);
  aspect-ratio: 0.707; border-radius: 16px; overflow: hidden;
  border: 1px solid var(--line);
  transition: transform 0.6s var(--ease), border-color 0.5s ease;
}
.reel img { width: 100%; height: 100%; object-fit: cover;
  /* The six thumbnails were shot and graded differently. A single
     shared grade pulls them together without re-exporting anything. */
  filter: saturate(1.05) contrast(1.03) brightness(0.97);
  transition: filter 0.45s var(--ease), transform 0.55s var(--ease); }
.reel:hover { transform: translateY(-12px); border-color: rgba(245,148,33,0.5); }
.reel:hover img { filter: saturate(1.08) contrast(1.02) brightness(1.02); transform: scale(1.02); }
.reel .v { position: absolute; left: 12px; bottom: 12px; z-index: 2;
  display: inline-flex; align-items: center; gap: 7px;
  background: rgba(10,10,12,0.82);
  border-radius: 100px; padding: 7px 13px; font-size: 12px; font-weight: 800; letter-spacing: 0.02em; }
.reel .v svg { width: 13px; height: 13px; opacity: 0.85; }

.rail-foot { margin-top: clamp(26px, 3vw, 40px); display: flex; align-items: flex-end;
  justify-content: space-between; gap: 30px; flex-wrap: wrap; }
.rail-foot .tot { font-size: clamp(38px, 6vw, 76px); font-weight: 900; letter-spacing: -0.04em; line-height: 0.9; }
.rail-foot p { max-width: 380px; }

/* ── Loop figure ────────────────────────────────────────── */

/* ── Testimonials ───────────────────────────────────────── */
/* Two 9:16 players centred in a 1280px container left roughly 440px of
   dead gutter on each side. Four across fills the width and puts two
   real results next to the two testimonials. */
/* Two real testimonials, laid out as feature cards rather than padded
   out to four with view-count cards that said nothing. Player left,
   the quote at readable size on the right, so two of them still fill
   the row without stranding it in a narrow centred block. */
.vid-grid { display: grid; grid-template-columns: repeat(2, minmax(0,1fr));
  gap: clamp(16px, 2vw, 28px); max-width: none; margin: 0; align-items: stretch; }
@media (min-width: 861px) {
  .vid-grid .vcard { flex-direction: row; }
  .vid-grid .vwrap { flex: 0 0 46%; }
  .vid-grid .vwrap video { height: 100%; }
  .vid-grid .vmeta { justify-content: center; padding: clamp(20px,2vw,30px); }
  .vid-grid .vmeta blockquote { font-size: clamp(16px, 1.5vw, 21px); }
}


.vcard {
  display: flex; flex-direction: column;
  background: var(--surface); border: 1px solid var(--line); border-radius: 18px; overflow: hidden;
  transform-origin: center;
  transition: transform 0.55s var(--ease), border-color 0.5s ease, box-shadow 0.55s var(--ease);
}
.vcard:hover, .vcard.prev {
  transform: translateY(-14px) scale(1.035);
  border-color: rgba(245,148,33,0.45);
  box-shadow: 0 34px 80px rgba(0,0,0,0.55), 0 0 50px rgba(245,148,33,0.1);
  z-index: 3;
}
.vwrap { position: relative; line-height: 0; flex: 0 0 auto; }
.vwrap video { width: 100%; aspect-ratio: 9/16; object-fit: cover; background: #000;
  transition: filter 0.5s ease; filter: saturate(0.88) brightness(0.9); }
.vcard:hover video, .vcard.prev video { filter: saturate(1.04) brightness(1.02); }
.vplay { position: absolute; inset: 0; display: flex; align-items: center; justify-content: center;
  background: rgba(0,0,0,0.2); border: 0; cursor: pointer; transition: opacity 0.4s ease, background 0.4s ease; }
.vplay::before { content: ''; position: absolute; width: 70px; height: 70px; border-radius: 50%;
  background: var(--orange); transition: transform 0.5s var(--ease); }
.vcard:hover .vplay::before { transform: scale(1.1); }
.vplay svg { position: relative; width: 26px; height: 26px; color: #0a0a0a; margin-left: 4px; }
.vcard.playing .vplay { opacity: 0; pointer-events: none; }
.vhint { position: absolute; left: 50%; bottom: 16px; transform: translateX(-50%) translateY(8px);
  display: inline-flex; align-items: center; gap: 7px; background: rgba(0,0,0,0.7);
  border-radius: 100px; padding: 8px 15px;
  font-size: 10px; font-weight: 700; letter-spacing: 0.14em; text-transform: uppercase;
  opacity: 0; transition: 0.4s var(--ease); pointer-events: none; z-index: 3; white-space: nowrap; }
.vhint svg { width: 13px; height: 13px; }
.vcard.prev .vhint { opacity: 1; transform: translateX(-50%) translateY(0); }
.vcard.playing .vhint { opacity: 0; }
.vmeta { padding: 24px 26px 26px; display: flex; flex-direction: column; flex: 1; }
.vmeta blockquote { font-size: clamp(15px, 1.4vw, 18px); font-weight: 700; letter-spacing: -0.02em;
  line-height: 1.4; flex: 1; margin-bottom: 16px; }
.vwho { margin-top: auto; padding-top: 15px; border-top: 1px solid var(--line);
  display: flex; flex-direction: column; gap: 3px; }
.vwho b { font-size: 13px; font-weight: 800; }
.vwho a { font-size: 12.5px; color: var(--orange); width: fit-content; transition: opacity 0.3s; }
.vwho a:hover { opacity: 0.7; }

/* ── Written reviews ────────────────────────────────────── */
.rev-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(218px, 1fr));
  gap: 18px; margin-top: clamp(26px, 3vw, 40px); }
.rev {
  background: var(--surface); border: 1px solid var(--line); border-radius: 16px;
  padding: 24px 24px; display: flex; flex-direction: column;
  transition: transform 0.5s var(--ease), border-color 0.4s ease, background 0.4s ease;
}
.rev:hover { transform: translateY(-7px); border-color: var(--line-2); background: #17171d; }
.stars { display: flex; gap: 3px; margin-bottom: 16px; }
.stars svg { width: 14px; height: 14px; color: var(--orange); }
.rev q { font-size: 14.5px; font-weight: 400; color: rgba(255,255,255,0.82); line-height: 1.65;
  quotes: none; display: block; flex: 1; margin-bottom: 20px; }
.rev-by { display: flex; align-items: center; justify-content: space-between; gap: 12px;
  margin-top: auto; padding-top: 16px; border-top: 1px solid var(--line); }
.rev-by b { font-size: 12.5px; font-weight: 700; }
.rev-by span { font-size: 11px; color: var(--muted-2); letter-spacing: 0.04em; }

/* ── FAQ ────────────────────────────────────────────────── */
.faq { border-top: 1px solid var(--line); }
.q-item { border-bottom: 1px solid var(--line); }
.q-btn { width: 100%; background: none; border: 0; cursor: pointer; text-align: left;
  font-family: var(--font); color: #fff; padding: clamp(24px,3vw,34px) 60px clamp(24px,3vw,34px) 0;
  font-size: clamp(16px, 1.7vw, 22px); font-weight: 700; letter-spacing: -0.02em; line-height: 1.35;
  position: relative; transition: color 0.4s ease; }
.q-btn:hover { color: var(--orange); }
.q-btn::after, .q-btn::before { content: ''; position: absolute; right: 6px; top: 50%; background: var(--orange);
  transition: transform 0.45s var(--ease), opacity 0.45s ease; }
.q-btn::after { width: 15px; height: 1.5px; transform: translateY(-50%); }
.q-btn::before { width: 1.5px; height: 15px; right: 12.7px; transform: translateY(-50%); }
.q-item.open .q-btn::before { transform: translateY(-50%) rotate(90deg); opacity: 0; }
.q-item.open .q-btn { color: var(--orange); }
.q-a { max-height: 0; overflow: hidden; visibility: hidden;
  transition: max-height 0.55s var(--ease), visibility 0s linear 0.55s; }
.q-item.open .q-a { visibility: visible; transition: max-height 0.55s var(--ease), visibility 0s; }
.q-a-in { padding: 0 60px 30px 0; max-width: 780px; }
.q-a-in p + p { margin-top: 13px; }

/* ── CTA ────────────────────────────────────────────────── */
.cta { text-align: center; position: relative; overflow: hidden;
  padding: clamp(52px, 5.4vw, 84px) 0; }
.cta .wrap { position: relative; z-index: 1; }
.cta h2 { margin-bottom: 26px; }
.cta p { max-width: 520px; margin: 0 auto 42px; }
.cta-row { display: flex; gap: 14px; justify-content: center; flex-wrap: wrap; }

/* ── Footer ─────────────────────────────────────────────── */
.foot { border-top: 1px solid var(--line); padding: clamp(44px,4.6vw,64px) 0 20px; background: var(--ink-2); }
.foot-grid { display: grid; grid-template-columns: 2fr 1fr 1fr 1.2fr; gap: 46px; margin-bottom: 60px; }
.foot h2 { font-family: var(--font); font-size: 10px; font-weight: 700; letter-spacing: 0.2em; text-transform: uppercase;
  color: var(--muted-2); margin-bottom: 20px; }
.foot ul { display: flex; flex-direction: column; gap: 12px; }
.foot ul a { font-size: 13.5px; color: var(--muted); transition: color 0.3s ease; }
.foot ul a:hover { color: var(--orange); }
.foot-brand p { margin: 16px 0 24px; max-width: 260px; font-size: 13.5px; }
.socials { display: flex; gap: 10px; }
.socials a { width: 40px; height: 40px; border: 1px solid var(--line); border-radius: 50%;
  display: flex; align-items: center; justify-content: center; color: var(--muted);
  transition: 0.4s var(--ease); }
.socials a:hover { border-color: var(--orange); color: var(--orange); transform: translateY(-3px); }
.socials svg { width: 15px; height: 15px; }
.foot-base { padding-top: 28px; border-top: 1px solid var(--line);
  display: flex; justify-content: space-between; gap: 18px; flex-wrap: wrap; }
.foot-base p, .foot-base a { font-size: 12px; color: var(--muted-2); }
.foot-base a:hover { color: var(--orange); }
.foot-base div { display: flex; gap: 22px; }

/* ── Reveal primitives (GSAP drives these) ──────────────── */
.js-anim .rv { opacity: 0; transform: translateY(30px); }
.js-anim .rv-line { display: block; }

/* ── Focus ──────────────────────────────────────────────── */
a:focus-visible, button:focus-visible, [tabindex]:focus-visible,
summary:focus-visible, video:focus-visible {
  outline: 3px solid var(--orange);
  outline-offset: 3px;
  border-radius: 4px;
  transition: none;
  /* The custom cursor hides the native one, so the ring is the only
     affordance a keyboard user gets. Make it unmissable on dark. */
  box-shadow: 0 0 0 6px rgba(245,148,33,0.22);
}

/* ── Responsive ─────────────────────────────────────────── */
@media (max-width: 1024px) {
  .row { grid-template-columns: 44px 1fr; }
  .row .fig-wrap { grid-column: 2; padding-top: 6px; }
  .foot-grid { grid-template-columns: 1fr 1fr; gap: 36px; }
}

@media (max-width: 860px) {
  .nav-links, .nav-cta { display: none; }
  .burger { display: block; }
  .hero-meta { grid-template-columns: 1fr 1fr; gap: 26px 20px; }
  .vid-grid { grid-template-columns: 1fr; max-width: 340px; margin: 0 auto; }
}

@media (max-width: 560px) {
  .hero-meta { grid-template-columns: 1fr; }
  .foot-grid { grid-template-columns: 1fr; }
  .row { grid-template-columns: 1fr; gap: 12px; }
  .row .n { margin-bottom: -4px; }
  .row .fig-wrap { grid-column: 1; }
}

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after { animation-duration: 0.01ms !important; transition-duration: 0.01ms !important; }
  .strip-track { animation: none !important; }
  .js-anim .rv { opacity: 1; transform: none; }
  .vcard:hover, .vcard.prev { transform: none; }
}

/* ============================================================
   Inner pages. These share the same shell as the home page, so
   only the page-specific blocks are defined below.
   ============================================================ */

.pt0 { padding-top: 0; }

/* ── Shorter hero for inner pages ───────────────────────── */
.page-hero {
  position: relative; overflow: hidden;
  padding: clamp(150px, 19vw, 240px) 0 clamp(60px, 8vw, 110px);
}
.page-hero .wrap { position: relative; z-index: 1; }
.display.sm { font-size: clamp(38px, 6.6vw, 104px); }
.page-hero .lede { max-width: 620px; margin-top: clamp(22px, 2.6vw, 34px); }
.page-hero .hero-meta { margin-top: clamp(40px, 5vw, 70px); }

/* ── Services ───────────────────────────────────────────── */
.svc-list { border-top: 1px solid var(--line); }
.svc {
  display: grid; grid-template-columns: 70px 1fr 1fr;
  gap: clamp(22px, 4vw, 60px);
  padding: clamp(40px, 5.5vw, 78px) 0;
  border-bottom: 1px solid var(--line);
  transition: background 0.5s var(--ease), padding-left 0.5s var(--ease);
}
.svc:hover { background: rgba(255,255,255,0.018); padding-left: 18px; }
.svc-n { font-size: clamp(13px, 1.2vw, 15px); font-weight: 700; color: var(--muted-2);
  font-variant-numeric: tabular-nums; padding-top: 6px; }
.svc-t { font-size: clamp(28px, 3.6vw, 50px); font-weight: 900; line-height: 0.98;
  letter-spacing: -0.035em; margin-bottom: 18px; }
.svc-intro { max-width: 460px; }
.svc-side h3, .call-l h2 {
  font-family: var(--font);
  font-size: 10px; font-weight: 700; letter-spacing: 0.2em; text-transform: uppercase;
  color: var(--muted-2); margin-bottom: 16px;
}
.svc-res { margin-top: 28px; padding-top: 22px; border-top: 1px solid var(--line); }

.ticks { display: flex; flex-direction: column; gap: 11px; }
.ticks li {
  position: relative; padding-left: 26px;
  font-size: clamp(13.5px, 1.05vw, 15px); font-weight: 300;
  color: rgba(255,255,255,0.78); line-height: 1.55;
}
.ticks li::before {
  content: ''; position: absolute; left: 0; top: 0.55em;
  width: 11px; height: 6px; border-left: 1.6px solid var(--orange);
  border-bottom: 1.6px solid var(--orange); transform: rotate(-45deg);
}
.big-ticks li { font-size: clamp(14px, 1.15vw, 16.5px); padding-left: 30px; }

/* ── About ──────────────────────────────────────────────── */
.split { display: grid; grid-template-columns: 1fr 1fr; gap: clamp(30px, 6vw, 90px); align-items: start; }
.split-r p + p { margin-top: 18px; }
.split-btn { margin-top: 32px; }

.pillars { display: grid; grid-template-columns: repeat(auto-fit, minmax(260px, 1fr)); gap: 18px; }
.pillar {
  background: var(--surface); border: 1px solid var(--line); border-radius: 18px;
  padding: clamp(28px, 3vw, 40px);
  transition: transform 0.5s var(--ease), border-color 0.4s ease, background 0.4s ease;
}
.pillar:hover { transform: translateY(-8px); border-color: rgba(245,148,33,0.4); background: #17171d; }
.pillar-n { font-size: 12px; font-weight: 700; letter-spacing: 0.2em; color: var(--orange); margin-bottom: 22px; }
.pillar h3 { margin-bottom: 12px; }

/* ── Case studies ───────────────────────────────────────── */
.cs-list { display: flex; flex-direction: column; gap: clamp(44px, 5vw, 80px); }
.cs { border-top: 1px solid var(--line); padding-top: clamp(34px, 4vw, 56px); }
.cs-head { max-width: 860px; margin-bottom: clamp(34px, 4vw, 56px); }
.cs-head .num-tag { display: block; margin-bottom: 18px; }
.cs-head h2 { margin-bottom: 22px; }
.cs-head p { max-width: 640px; }
.cs-body { display: grid; grid-template-columns: repeat(3, 1fr); gap: clamp(22px, 3vw, 44px);
  padding-bottom: clamp(34px, 4vw, 52px); }
.cs-col h3 { font-family: var(--font); font-size: 10px; font-weight: 700; letter-spacing: 0.2em; text-transform: uppercase;
  color: var(--orange); margin-bottom: 15px; }
.cs-shots { display: grid; grid-template-columns: 1fr 1fr; gap: 16px; }
.cs-shots.one { grid-template-columns: 1fr; max-width: 620px; }
.cs-shots figure { border: 1px solid var(--line); border-radius: 14px; overflow: hidden; background: var(--surface); }
.cs-shots figcaption { font-size: 10px; font-weight: 700; letter-spacing: 0.2em; text-transform: uppercase;
  color: var(--muted-2); padding: 13px 16px; border-bottom: 1px solid var(--line); }
.cs-shots img { width: 100%; }
.cs-stats { display: grid; grid-template-columns: repeat(4, 1fr); gap: 16px;
  margin-top: clamp(26px, 3vw, 40px); padding: clamp(24px, 3vw, 34px) 0;
  border-top: 1px solid var(--line); border-bottom: 1px solid var(--line); }
.cs-sv { font-size: clamp(24px, 3vw, 40px); font-weight: 900; letter-spacing: -0.035em; line-height: 1; }
.cs-sk { font-size: 10px; font-weight: 600; letter-spacing: 0.16em; text-transform: uppercase;
  color: var(--muted-2); margin-top: 10px; }
.cs-quote { margin-top: clamp(24px, 3vw, 36px); font-size: clamp(17px, 1.9vw, 24px);
  font-weight: 700; letter-spacing: -0.02em; line-height: 1.4; max-width: 640px; }
.cs-quote cite { display: block; margin-top: 12px; font-size: 11px; font-weight: 600; font-style: normal;
  letter-spacing: 0.16em; text-transform: uppercase; color: var(--muted-2); }
.cs-tags { display: flex; flex-wrap: wrap; gap: 8px; margin-top: clamp(24px, 3vw, 34px); }
.cs-tags span { font-size: 10.5px; font-weight: 600; letter-spacing: 0.1em; text-transform: uppercase;
  color: var(--muted); border: 1px solid var(--line); border-radius: 100px; padding: 8px 15px; }

/* ── Portfolio grid ─────────────────────────────────────── */
.pgrid { display: grid; grid-template-columns: repeat(auto-fill, minmax(230px, 1fr)); gap: 18px; }
.pgrid .reel { flex: none; width: 100%; }

/* ── Book a call ────────────────────────────────────────── */
.call-grid { display: grid; grid-template-columns: 1.25fr 1fr; gap: clamp(30px, 5vw, 70px); align-items: start; }
.call-l h2.mt { margin-top: 44px; }
.call-card {
  background: var(--surface); border: 1px solid var(--line); border-radius: 20px;
  padding: clamp(30px, 3.4vw, 44px); position: sticky; top: 110px;
}
.call-card h3 { margin-bottom: 10px; }
.call-meta { font-size: 12px; font-weight: 600; letter-spacing: 0.12em; text-transform: uppercase;
  color: var(--muted-2); margin-bottom: 28px; }
.call-card .btn { width: 100%; justify-content: center; }
.call-alt { margin-top: 22px; padding-top: 20px; border-top: 1px solid var(--line);
  display: flex; flex-direction: column; gap: 5px; }
.call-alt span { font-size: 11px; font-weight: 600; letter-spacing: 0.14em;
  text-transform: uppercase; color: var(--muted-2); }
.call-alt a { font-size: 15px; font-weight: 700; color: var(--orange); width: fit-content; }
.call-alt a:hover { opacity: 0.75; }

/* ── Legal pages ────────────────────────────────────────── */
.legal { max-width: 760px; }
.legal h2 { font-size: clamp(19px, 1.9vw, 25px); font-weight: 800; letter-spacing: -0.02em;
  margin: 44px 0 14px; }
.legal h2:first-of-type { margin-top: 0; }
.legal p { margin-bottom: 14px; }
.legal ul { display: flex; flex-direction: column; gap: 10px; margin: 0 0 16px; }
.legal li { position: relative; padding-left: 22px; font-size: clamp(13.5px, 1.05vw, 15px);
  font-weight: 300; color: var(--muted); line-height: 1.7; }
.legal li::before { content: ''; position: absolute; left: 2px; top: 0.72em;
  width: 5px; height: 5px; border-radius: 50%; background: var(--orange); }
.legal a { color: var(--orange); }
.legal-updated { font-size: 11px; font-weight: 600; letter-spacing: 0.16em; text-transform: uppercase;
  color: var(--muted-2); margin-bottom: 40px; }
.legal-note { margin-top: 48px; padding: 26px 28px; background: var(--surface);
  border: 1px solid var(--line); border-radius: 16px; }
.legal-note p:last-child { margin-bottom: 0; }

/* ── Responsive additions ───────────────────────────────── */
@media (max-width: 1024px) {
  .svc { grid-template-columns: 44px 1fr; }
  .svc-side { grid-column: 2; }
  .cs-body { grid-template-columns: 1fr; }
  .cs-stats { grid-template-columns: 1fr 1fr; }
  .call-grid { grid-template-columns: 1fr; }
  .call-card { position: static; }
  .split { grid-template-columns: 1fr; gap: 26px; }
}

@media (max-width: 560px) {
  .svc { grid-template-columns: 1fr; gap: 16px; }
  .svc-side, .svc-main { grid-column: 1; }
  .cs-shots { grid-template-columns: 1fr; }
  .pgrid { grid-template-columns: 1fr 1fr; gap: 12px; }
}

/* ============================================================
   PREMIUM LAYER
   Added after the base so it lifts the existing components
   without rewriting them. Four things happen here: a grain
   overlay, frosted glass on every card, a lit gradient edge on
   hover, and a real share of the brand purple.
   ============================================================ */

/* Grain. A fixed, very low opacity noise field, so the large
   flat black areas stop reading as empty screen. Pointer events
   are off, it can never intercept a click. */
body::before {
  content: '';
  position: fixed; inset: 0;
  z-index: 1;
  pointer-events: none;
  opacity: 0.032;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.82' numOctaves='4' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E");
}

/* Ambient light. Each flagged section gets a faint brand wash so
   the page is never one flat charcoal from top to bottom. */
/* The glow is painted as a background on the section itself, not as
   an oversized pseudo-element that has to be clipped.

   The first version used a pseudo-element plus overflow:hidden, which
   silently broke the process section: .stack-item is position:sticky,
   and any ancestor with overflow:hidden becomes its scroll container,
   so the cards stopped sticking and the stack fell apart. A background
   is clipped to the box for free, needs no overflow, adds no layer and
   cannot break sticky. Backgrounds do not stack across rules, so the
   combinations with .sec-band are written out in full. */
.sec-glow { position: relative; }
.sec-glow > .wrap { position: relative; z-index: 1; }

.sec-glow.g-purple {
  background-image: radial-gradient(62% 58% at 10% 4%, rgba(117,58,150,0.30), rgba(8,8,10,0) 70%);
}
.sec-glow.g-orange {
  background-image: radial-gradient(62% 58% at 92% 96%, rgba(245,148,33,0.20), rgba(8,8,10,0) 70%);
}
.sec-band.g-purple {
  background-image:
    radial-gradient(62% 58% at 10% 4%, rgba(117,58,150,0.30), rgba(8,8,10,0) 70%),
    linear-gradient(180deg, rgba(255,255,255,0.022), rgba(255,255,255,0) 42%),
    linear-gradient(180deg, rgba(117,58,150,0.07), rgba(8,8,10,0) 60%);
}
.sec-band.g-orange {
  background-image:
    radial-gradient(62% 58% at 92% 96%, rgba(245,148,33,0.20), rgba(8,8,10,0) 70%),
    linear-gradient(180deg, rgba(255,255,255,0.022), rgba(255,255,255,0) 42%),
    linear-gradient(180deg, rgba(117,58,150,0.07), rgba(8,8,10,0) 60%);
}

/* Section rhythm, so the scroll reads as chapters. */
.sec-band {
  background-color: var(--surface-1);
  background-image:
    linear-gradient(180deg, rgba(255,255,255,0.022), rgba(255,255,255,0) 42%),
    linear-gradient(180deg, rgba(117,58,150,0.07), rgba(8,8,10,0) 60%);
  border-top: 1px solid var(--line);
  border-bottom: 1px solid var(--line);
}

/* Frosted glass. One surface treatment shared by every card, so
   they read as a set rather than six different boxes. */
.vcard, .rev, .pillar, .call-card, .cs-shots figure {
  position: relative;
  background:
    linear-gradient(158deg, rgba(255,255,255,0.05) 0%, rgba(255,255,255,0.012) 48%, rgba(255,255,255,0.028) 100%),
    var(--surface-2);
  border: 1px solid var(--line-strong);
  box-shadow:
    inset 0 1px 0 var(--edge-hi),
    0 18px 44px rgba(0,0,0,0.5);
  transition: transform 180ms var(--ease), border-color 180ms var(--ease),
              box-shadow 180ms var(--ease), background-color 180ms var(--ease);
}

/* The lit edge. A masked pseudo-element draws a one pixel
   gradient border that fades in on hover. */


.rev:hover, .pillar:hover {
  transform: translateY(-6px);
  border-color: rgba(255,255,255,0.22);
  box-shadow:
    inset 0 1px 0 rgba(255,255,255,0.12),
    0 26px 60px rgba(0,0,0,0.58),
    0 0 40px rgba(117,58,150,0.18);
  background:
    linear-gradient(158deg, rgba(255,255,255,0.07) 0%, rgba(255,255,255,0.024) 48%, rgba(255,255,255,0.042) 100%),
    var(--surface-3);
}
.vcard:hover, .vcard.prev {
  transform: translateY(-14px) scale(1.03);
  border-color: rgba(255,255,255,0.18);
  box-shadow:
    inset 0 1px 0 rgba(255,255,255,0.10),
    0 40px 90px rgba(0,0,0,0.6),
    0 0 60px rgba(245,148,33,0.16);
}

/* Reels. The rail is the piece people actually look at, so the
   cards get the same lit edge and a deeper lift. */
.reel {
  border-color: rgba(255,255,255,0.10);
  box-shadow: 0 18px 44px rgba(0,0,0,0.45);
  transition: transform 0.65s var(--ease), border-color 0.5s ease, box-shadow 0.6s var(--ease);
}
.reel::after {
  content: '';
  position: absolute; inset: 0; z-index: 1;
  border-radius: inherit;
  background: linear-gradient(180deg, rgba(117,58,150,0.10) 0%, rgba(8,8,10,0) 42%, rgba(8,8,10,0.55) 100%);
  pointer-events: none;
}
.reel .v { z-index: 2; }
.reel:hover {
  transform: translateY(-14px);
  border-color: rgba(245,148,33,0.55);
  box-shadow: 0 30px 70px rgba(0,0,0,0.6), 0 0 44px rgba(245,148,33,0.20);
}
.reel .v {
  border: 1px solid rgba(255,255,255,0.14);
  background: rgba(10,10,12,0.82);
}

/* Buttons get depth, and the ghost button a glass fill rather
   than a flat outline. */
.btn-fill {
  box-shadow: 0 12px 30px rgba(245,148,33,0.26);
  transition: color 0.4s var(--ease), box-shadow 0.5s var(--ease);
}
.btn-fill:hover { box-shadow: 0 16px 44px rgba(161,101,195,0.38); }
.btn-ghost {
  background: rgba(255,255,255,0.05);
  border-color: rgba(255,255,255,0.22);
}
.nav-cta { box-shadow: 0 10px 26px rgba(245,148,33,0.28); }

/* Nav. Stronger frost once it sticks, and a hairline drawn from
   the brand gradient instead of a flat grey rule. */
.nav.stuck {
  background: rgba(8,8,10,0.94);
  border-bottom-color: transparent;
  box-shadow: 0 1px 0 rgba(255,255,255,0.07), 0 18px 40px rgba(0,0,0,0.35);
}
.nav.stuck::after {
  content: '';
  position: absolute; left: 0; right: 0; bottom: 0; height: 1px;
  background: linear-gradient(90deg, rgba(8,8,10,0), rgba(245,148,33,0.55), rgba(161,101,195,0.55), rgba(8,8,10,0));
}

/* Purple's share. The brand is half purple, so give it surface
   area rather than only the logo. */
.pillar-n, .cs-col h3 { color: var(--purple); }
.hero-meta .v {
  background: var(--grad);
  -webkit-background-clip: text; background-clip: text;
  -webkit-text-fill-color: transparent;
}
.q-item.open .q-btn { color: var(--purple); }
.q-item.open .q-btn::after, .q-item.open .q-btn::before { background: var(--purple); }
.socials a:hover {
  border-color: var(--purple); color: var(--purple);
  box-shadow: 0 0 22px rgba(117,58,150,0.35);
}
.cs-tags span { border-color: rgba(161,101,195,0.30); }
.vplay::before { box-shadow: 0 0 40px rgba(245,148,33,0.5); }
.vhint { border: 1px solid rgba(255,255,255,0.14); }

/* Phones drop the backdrop filters, which are the expensive
   part, and keep a solid surface instead. */
@media (max-width: 860px) {
  .reel .v, .vhint { backdrop-filter: none; -webkit-backdrop-filter: none;
    background: rgba(10,10,12,0.86); }
}
@media (prefers-reduced-motion: reduce) {
  .rev:hover, .pillar:hover, .vcard:hover, .reel:hover { transform: none; }
  body::before { display: none; }
}

/* ── The Forge Loop ─────────────────────────────────────────
   Was a 1536px raster displayed at 1278px, soft on any retina
   screen, with baked-in type that did not match the site and
   stage names that contradicted the cards directly above it:
   the diagram said LEARN and MEASURE where the cards say
   Engage and Measure. It is native markup now, so the labels
   are real text, selectable, searchable, crisp at any zoom,
   and taken from the same six names as the cards. */
.loop {
  position: relative;
  width: min(100%, 620px);
  margin: 0 auto;
  aspect-ratio: 1;
}
.loop-svg { position: absolute; inset: 0; width: 100%; height: 100%; overflow: visible; }

.loop-track {
  fill: none; stroke: var(--line-strong); stroke-width: 1;
}
.loop-draw {
  fill: none; stroke: url(#loopGrad); stroke-width: 2; stroke-linecap: round;
  /* 2 * pi * 252 */
  stroke-dasharray: 1583;
  /* Drawn by default. Only the motion layer hides it to animate it in,
     so a page without JS still shows the full gradient ring. */
  stroke-dashoffset: 0;
  transform-origin: 50% 50%;
  transform: rotate(-90deg);
}
.loop-core-ring {
  fill: none; stroke: var(--line-soft); stroke-width: 1;
  stroke-dasharray: 6 10;
  transform-origin: 50% 50%;
}
.loop-pulse { fill: var(--orange); opacity: 0; }

.loop-core {
  position: absolute; left: 50%; top: 50%;
  transform: translate(-50%, -50%);
  width: 40%; text-align: center;
  display: flex; flex-direction: column; gap: 4px;
}
.loop-core-k {
  font-size: 9.5px; font-weight: 700; letter-spacing: 0.24em;
  text-transform: uppercase; color: var(--muted-2);
}
.loop-core-v {
  font-family: var(--font-display);
  font-size: clamp(13px, 1.5vw, 19px); font-weight: 700;
  letter-spacing: -0.01em; line-height: 1.15; color: #fff;
}

.loop-nodes { position: absolute; inset: 0; list-style: none; margin: 0; padding: 0; }
.loop-node {
  position: absolute; left: 50%; top: 50%;
  display: flex; flex-direction: column; align-items: center; gap: 3px;
  width: 128px; margin: 0;
  transform: translate(-50%, -50%);
  text-align: center;
}
.loop-n {
  font-family: var(--font-display);
  font-size: 10px; font-weight: 800; letter-spacing: 0.16em;
  color: var(--orange);
}
.loop-t {
  font-family: var(--font-display);
  font-size: clamp(13px, 1.35vw, 17px); font-weight: 700;
  letter-spacing: -0.01em; color: #fff;
}
/* Six nodes on the ring, starting at the top and running clockwise.
   translate places them on a 252/720 radius of the square box. */
/* Placed with explicit percentages, not rotate plus translateY. A
   percentage translate resolves against the element's own box, so
   translateY(-35%) moved each label 15px instead of onto the ring and
   all six stacked in the middle. The ring radius is 252 of a 720
   viewBox, which is 35% of the square. */
.loop-node.n1 { left: 50.0%; top: 15.0%; }
.loop-node.n2 { left: 80.3%; top: 32.5%; }
.loop-node.n3 { left: 80.3%; top: 67.5%; }
.loop-node.n4 { left: 50.0%; top: 85.0%; }
.loop-node.n5 { left: 19.7%; top: 67.5%; }
.loop-node.n6 { left: 19.7%; top: 32.5%; }

@media (max-width: 560px) {
  .loop { width: min(100%, 380px); }
  .loop-node { width: 96px; }
  .loop-t { font-size: 12px; }
}


/* ── B-3: scroll-driven reveals ─────────────────────────────
   The default reveal was a GSAP timeline per element. Where the
   browser supports a view timeline this runs on the compositor
   instead and costs nothing on the main thread. forge.js checks
   the same feature and skips its own .rv tween when it is
   present, so the two never both drive an element. GSAP still
   owns the pinned rail, the split headlines and the 3D binding.
   Anything older keeps the existing IntersectionObserver path. */
@supports (animation-timeline: view()) {
  /* Only elements that start below the fold get the scroll timeline.
     An element already on screen at load never crosses the start of
     its range, so it would sit at the from state, opacity 0, for good.
     That is the same trap the JS reveal fell into, and it is why
     forge.js tags the below-fold ones with .rv-lazy instead of the
     stylesheet claiming every .rv. */
  html.vt-reveal.js-anim .rv { opacity: 1; transform: none; }
  html.vt-reveal .rv.rv-lazy {
    animation: reveal linear both;
    animation-timeline: view();
    animation-range: entry 5% cover 28%;
  }
}
@keyframes reveal {
  from { opacity: 0; transform: translateY(var(--lift-2)); }
  to   { opacity: 1; transform: none; }
}

/* ── C-1: cross-document view transitions ───────────────────
   Four flat HTML pages with hard navigations, so every click
   flashed. This needs no router and no script, and browsers
   without it simply keep today's behaviour. */
@view-transition { navigation: auto; }
::view-transition-old(root) { animation: vt-out 220ms var(--ease-in)  both; }
::view-transition-new(root) { animation: vt-in  380ms var(--ease-out) both; }
@keyframes vt-out { to   { opacity: 0; transform: translateY(-8px); } }
@keyframes vt-in  { from { opacity: 0; transform: translateY(12px); } }

/* The header is common to every page, so it holds still instead of
   cross-fading with the rest of the document. */
.nav      { view-transition-name: site-nav; }
.nav .logo { view-transition-name: site-logo; }

/* ── B-2: reduced motion is a real kill switch ──────────────
   Durations collapse to a single frame, the marquees hold at
   their start, the cursor and the scroll cue go away. forge.js
   reads the same query and never starts Lenis, ScrollTrigger or
   the 3D scene, so this is belt and braces rather than the only
   line of defence. */
@media (prefers-reduced-motion: reduce) {
  :root {
    --dur-1: 1ms; --dur-2: 1ms; --dur-3: 1ms;
    --dur-4: 1ms; --dur-5: 1ms;
  }
  html.vt-reveal .rv.rv-lazy { animation: none; opacity: 1; transform: none; }
  .strip-track { animation: none !important; transform: none !important; }
  .loop-draw { stroke-dashoffset: 0; }
  .loop-pulse, .scroll-cue { display: none; }
  .cur { display: none !important; }
  @view-transition { navigation: none; }
}

/* ── The Forge Loop, running ────────────────────────────────
   Four things move, and all of them are transform or
   stroke-dashoffset, so nothing touches layout.

   Two classes, set by forge.js from an IntersectionObserver.
   .in fires the one-shot reveals and is never removed, so the
   diagram cannot un-draw itself when it scrolls away. .running
   gates only the continuous spin and the orbiting pulse, and is
   toggled, so nothing burns frames offscreen. The hidden start states are
   scoped to html.js-anim, so if the motion layer never loads
   the diagram is simply drawn and visible. */

/* The outer ring draws itself in loop order, once. */
html.js-anim .loop-draw { stroke-dashoffset: 1583; }
.loop.in .loop-draw {
  animation: loop-draw 1400ms var(--ease-out) forwards;
}
@keyframes loop-draw { to { stroke-dashoffset: 0; } }

/* The dashed core turns slowly and continuously, one revolution
   every 40 seconds, so the centre reads as something running
   rather than a label in a circle. */
.loop-core-ring {
  animation: loop-spin 40s linear infinite;
  animation-play-state: paused;
}
.loop.running .loop-core-ring { animation-play-state: running; }

/* A light travels the ring on a 6 second cycle. It is one small
   circle inside a rotating group, which is a single composited
   transform rather than a path calculation every frame. */
.loop-orbit {
  transform-origin: 360px 360px;
  animation: loop-spin 6s linear infinite;
  animation-play-state: paused;
}
.loop.running .loop-orbit { animation-play-state: running; }
.loop-pulse {
  filter: drop-shadow(0 0 6px rgba(245,148,33,0.9));
  transition: opacity var(--dur-4) var(--ease-out) 1200ms;
}
.loop.in .loop-pulse { opacity: 1; }

@keyframes loop-spin { to { transform: rotate(360deg); } }

/* The six nodes arrive on a stagger, in loop order, as the
   ring draws past them. */
html.js-anim .loop-node { opacity: 0; }
.loop.in .loop-node {
  animation: loop-node var(--dur-4) var(--ease-out) forwards;
}
.loop.in .loop-node.n1 { animation-delay: 120ms; }
.loop.in .loop-node.n2 { animation-delay: 300ms; }
.loop.in .loop-node.n3 { animation-delay: 480ms; }
.loop.in .loop-node.n4 { animation-delay: 660ms; }
.loop.in .loop-node.n5 { animation-delay: 840ms; }
.loop.in .loop-node.n6 { animation-delay: 1020ms; }
@keyframes loop-node {
  from { opacity: 0; transform: translate(-50%, -50%) scale(0.86); }
  to   { opacity: 1; transform: translate(-50%, -50%) scale(1); }
}

/* The centre label fades up with the ring. */
html.js-anim .loop-core { opacity: 0; }
.loop.in .loop-core {
  animation: loop-node var(--dur-4) var(--ease-out) 900ms forwards;
}

@media (prefers-reduced-motion: reduce) {
  html.js-anim .loop-draw { stroke-dashoffset: 0; }
  html.js-anim .loop-node,
  html.js-anim .loop-core { opacity: 1; }
  .loop-core-ring, .loop-orbit,
  .loop.in .loop-draw, .loop.in .loop-node, .loop.in .loop-core {
    animation: none !important;
  }
  .loop-pulse { display: none; }
}

/* ============================================================
   NEON SYSTEM
   Glow comes from layered box-shadow and radial-gradient only.
   No filter: blur() above 40px, no backdrop-filter on anything
   large or pinned, and the whole system runs on the existing
   orange to purple ramp. No cyan, teal, blue or green.
   ============================================================ */

:root {
  /* rgb channels of the brand colours, so rgb()/alpha maths works */
  --glow-orange: 245 148 33;
  --glow-purple: 161 101 195;
  --glow-deep:   117 58 150;

  --glass:      rgba(255 255 255 / 0.035);
  --glass-hi:   rgba(255 255 255 / 0.06);
  --glass-line: rgba(255 255 255 / 0.10);

  /* Every glow on the site comes from one of these three. */
  --halo-sm: 0 0 0 1px rgb(var(--glow-orange) / .22),
             0 0 18px -4px rgb(var(--glow-orange) / .35);
  --halo-md: 0 0 0 1px rgb(var(--glow-purple) / .28),
             0 0 34px -6px rgb(var(--glow-purple) / .45),
             0 0 90px -30px rgb(var(--glow-orange) / .35);
  --halo-lg: 0 0 60px -10px rgb(var(--glow-orange) / .40),
             0 0 140px -40px rgb(var(--glow-purple) / .45);
}

/* ── Part 3: the gradient border ────────────────────────────
   border-image ignores border-radius, so the edge is a masked
   pseudo-element instead. Applied to every card type, because a
   neon border on some cards and not others reads as a bug. */
.neon-edge { position: relative; }
.neon-edge::after {
  content: "";
  position: absolute; inset: 0;
  border-radius: inherit;
  padding: 1px;
  background: linear-gradient(140deg,
    rgb(var(--glow-orange) / .55),
    rgb(var(--glow-purple) / .55) 55%,
    rgb(255 255 255 / .06) 100%);
  -webkit-mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
          mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
  -webkit-mask-composite: xor;
          mask-composite: exclude;
  pointer-events: none;
  opacity: .5;
  transition: opacity var(--dur-3) var(--ease-out);
  z-index: 3;
}
.neon-edge:hover::after,
.neon-edge:focus-within::after { opacity: 1; }

/* ── Part 4: glass cards ────────────────────────────────────*/
.vcard, .rev, .pillar, .call-card, .cs-shots figure, .stack-in {
  background:
    linear-gradient(180deg, var(--glass-hi), var(--glass)),
    var(--surface-2);
}
.rev:hover, .pillar:hover, .stack-in:hover, .call-card:hover {
  box-shadow: inset 0 1px 0 var(--edge-hi), var(--halo-md);
}
.vcard:hover, .vcard.prev {
  box-shadow: inset 0 1px 0 var(--edge-hi), var(--halo-md);
}

/* ── Part 8: buttons ────────────────────────────────────────*/
.btn-fill { box-shadow: var(--halo-sm); }
.btn-fill:hover { box-shadow: var(--halo-md); }
.btn-ghost {
  background: transparent;
  border-color: transparent;
  box-shadow: none;
}
.btn-ghost::after {
  content: "";
  position: absolute; inset: 0;
  border-radius: inherit;
  padding: 1px;
  background: linear-gradient(140deg,
    rgb(var(--glow-orange) / .55),
    rgb(var(--glow-purple) / .55) 55%,
    rgb(255 255 255 / .10) 100%);
  -webkit-mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
          mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
  -webkit-mask-composite: xor;
          mask-composite: exclude;
  pointer-events: none;
  opacity: .55;
  transition: opacity var(--dur-2) var(--ease-out);
}
/* The glow is the hover state. No fill slides in. */
.btn-ghost::before { display: none; }
.btn-ghost:hover { color: #fff; box-shadow: var(--halo-sm); }
.btn-ghost:hover::after { opacity: 1; }

/* ── Part 5a: perspective grid floor ────────────────────────
   Only background-position drifts. The transform is static,
   because animating a perspective transform is expensive. */
.grid-floor {
  position: absolute; inset: auto 0 0 0; height: 46vh;
  pointer-events: none; z-index: 0;
  background-image:
    linear-gradient(rgb(var(--glow-purple) / .16) 1px, transparent 1px),
    linear-gradient(90deg, rgb(var(--glow-purple) / .16) 1px, transparent 1px);
  background-size: 64px 64px;
  transform: perspective(340px) rotateX(62deg);
  transform-origin: bottom center;
  -webkit-mask-image: linear-gradient(to top, #000 0%, transparent 85%);
          mask-image: linear-gradient(to top, #000 0%, transparent 85%);
  animation: grid-drift 30s linear infinite;
  animation-play-state: paused;
}
.hero.lit .grid-floor,
.sec.lit .grid-floor { animation-play-state: running; }
@keyframes grid-drift {
  from { background-position: 0 0, 0 0; }
  to   { background-position: 0 64px, 0 0; }
}

/* ── Part 5b: star and particle field ───────────────────────
   Tiled gradients, no canvas and no particle library. Varied
   tile sizes so the repeat does not read. */
.starfield {
  position: absolute; inset: 0; pointer-events: none; z-index: 0;
  opacity: .55;
  background-image:
    radial-gradient(1px 1px at 20% 30%, rgb(255 255 255 / .35), transparent),
    radial-gradient(1px 1px at 70% 60%, rgb(var(--glow-orange) / .40), transparent),
    radial-gradient(1px 1px at 45% 85%, rgb(var(--glow-purple) / .40), transparent),
    radial-gradient(1.5px 1.5px at 85% 20%, rgb(255 255 255 / .25), transparent),
    radial-gradient(1px 1px at 12% 68%, rgb(var(--glow-orange) / .30), transparent),
    radial-gradient(1.5px 1.5px at 58% 14%, rgb(255 255 255 / .22), transparent),
    radial-gradient(1px 1px at 92% 74%, rgb(var(--glow-purple) / .34), transparent);
  background-size:
    380px 380px, 520px 520px, 460px 460px, 610px 610px,
    340px 340px, 700px 700px, 430px 430px;
  animation: star-drift 90s linear infinite;
  animation-play-state: paused;
}
.hero.lit .starfield, .sec.lit .starfield { animation-play-state: running; }
/* Each layer drifts at its own rate, which gives parallax for free. */
@keyframes star-drift {
  from { background-position: 0 0, 0 0, 0 0, 0 0, 0 0, 0 0, 0 0; }
  to   { background-position:
           0 -380px, 0 -260px, 0 -460px, 0 -180px,
           0 -340px, 0 -140px, 0 -430px; }
}

/* ── Part 9: sparkle glyphs ─────────────────────────────────
   Four on a page reads as craft. Twelve reads as clip art. */
.spark {
  position: absolute; pointer-events: none; z-index: 0;
  width: 18px; height: 18px;
  color: rgb(var(--glow-orange) / .55);
  opacity: .0;
  animation: spark-pulse 4s var(--ease-io) infinite;
  animation-play-state: paused;
}
.hero.lit .spark, .sec.lit .spark { animation-play-state: running; }
.spark.s2 { color: rgb(var(--glow-purple) / .55); animation-delay: 1.1s; width: 13px; height: 13px; }
.spark.s3 { animation-delay: 2.3s; width: 15px; height: 15px; }
.spark.s4 { color: rgb(var(--glow-purple) / .45); animation-delay: 3.2s; width: 11px; height: 11px; }
@keyframes spark-pulse {
  0%, 100% { opacity: .12; transform: scale(.85); }
  50%      { opacity: .62; transform: scale(1); }
}

@media (prefers-reduced-motion: reduce) {
  .grid-floor, .starfield, .spark { animation: none !important; }
  .spark { opacity: .3; }
}

/* ── Hero background clip ───────────────────────────────────
   The clip is the whole hero background now, not a card in the
   right column, and the reel collage it replaced is gone.

   No poster, by request. The hero sits on --ink anyway, so the
   frame before the first decoded frame is the same near black
   the page already is, and the scrim is painted over the top
   regardless of whether the video has arrived.

   preload is none. forge.js only starts loading once the hero is
   actually on screen and the viewport is wide enough to show it,
   so a phone never pays for a 2.8MB download. */
.hero { position: relative; overflow: hidden; }

.hero-bg {
  position: absolute; inset: 0; z-index: 0;
  pointer-events: none; overflow: hidden;
}
.hero-bg video {
  width: 100%; height: 100%;
  object-fit: cover;
  /* The subject sits centre right in the clip, so bias the crop
     that way and the composition survives a narrow viewport. */
  object-position: 62% center;
  display: block;
}

/* What makes the headline readable. Heavy on the left where the
   copy sits, clearing to nothing on the right so the subject is
   not washed out. */
.hero-scrim {
  position: absolute; inset: 0; z-index: 1;
  pointer-events: none;
  background: linear-gradient(100deg,
    rgb(8 8 10 / .96) 0%,
    rgb(8 8 10 / .92) 34%,
    rgb(8 8 10 / .62) 52%,
    rgb(8 8 10 / .18) 74%,
    rgb(8 8 10 / .05) 100%);
}
/* A short lift off the bottom edge so the stat row keeps its
   contrast where the clip is brightest. */
.hero-scrim::after {
  content: '';
  position: absolute; inset: auto 0 0 0; height: 42%;
  background: linear-gradient(to top, rgb(8 8 10 / .92), rgb(8 8 10 / 0));
}

/* Decorative layers ride above the clip, content above those. */
.hero .starfield, .hero .grid-floor, .hero .spark { z-index: 2; }
.hero-in { position: relative; z-index: 3; }

@media (min-width: 1100px) {
  /* Single column again, because the right column is the clip now.
     The copy is capped so it never sprawls across the subject. */
  .hero-in { display: block; }
  .hero-copy { max-width: min(56%, 720px); }
  .hero .display { font-size: clamp(44px, 5.2vw, 96px); }
}

@media (max-width: 1099px) {
  /* Stacked layout, so the scrim runs bottom up instead of left
     to right or the copy loses its ground. */
  .hero-scrim {
    background: linear-gradient(to top,
      rgb(8 8 10 / .96) 0%,
      rgb(8 8 10 / .90) 38%,
      rgb(8 8 10 / .62) 64%,
      rgb(8 8 10 / .34) 100%);
  }
  .hero-bg video { object-position: 58% center; }
}

@media (prefers-reduced-motion: reduce) {
  /* forge.js also refuses to call play(), this is the belt. */
  .hero-bg video { animation: none !important; }
}
