/* ============================================================
   FORGE STUDIOS | Motion layer
   Everything here is presentation for the animation engine in
   forge.js. Each hidden start state is scoped to .js-anim, which
   an inline head script strips again if the engine never boots,
   so a failed script can never leave the page blank.
   ============================================================ */

/* ── Preloader ──────────────────────────────────────────────
   Sits above everything, counts up, then splits open. */
.pl {
  position: fixed; inset: 0; z-index: 9999;
  background: var(--ink);
  display: flex; align-items: flex-end; justify-content: flex-end;
  padding: var(--pad);
  pointer-events: none;
}
.pl-panels { position: absolute; inset: 0; display: flex; }
.pl-panels i { flex: 1; background: var(--ink-2); display: block; }
.pl-num {
  position: relative; z-index: 2;
  font-size: clamp(70px, 17vw, 230px); font-weight: 900;
  letter-spacing: -0.05em; line-height: 0.8;
  font-variant-numeric: tabular-nums;
  color: #fff;
}
.pl-num sup { font-size: 0.24em; vertical-align: super; color: var(--orange); font-weight: 800; }
.pl-line {
  position: absolute; left: var(--pad); right: var(--pad); bottom: calc(var(--pad) + 6px);
  height: 1px; background: rgba(255,255,255,0.14); z-index: 2;
}
.pl-line i { display: block; height: 100%; width: 0%; background: var(--grad); }
.pl-word {
  position: absolute; left: var(--pad); bottom: calc(var(--pad) + 26px); z-index: 2;
  font-size: 11px; font-weight: 700; letter-spacing: 0.3em; text-transform: uppercase;
  color: var(--muted-2);
}
html.loaded .pl { display: none; }

/* The body is held still until the loader clears. */
html.loading, html.loading body { overflow: hidden; height: 100%; }

/* ── Page transition curtain ────────────────────────────────
   Wipes across on internal navigation so pages do not flash. */
.curtain {
  position: fixed; inset: 0; z-index: 9000; display: flex;
  pointer-events: none; transform: translateY(101%);
}
.curtain i { flex: 1; background: var(--ink-2); }
.curtain.up { pointer-events: auto; }

/* ── Custom cursor (pointer devices only) ───────────────────
   Two elements: a small dot that tracks exactly, and a ring
   that lags behind and swells over interactive things. */
.cur {
  position: fixed; top: 0; left: 0; z-index: 8000;
  pointer-events: none; border-radius: 50%;
  will-change: transform;
  opacity: 0;
}
/* The ring used to be a 0.35 alpha hairline on a near-black page,
   which is close to invisible, and the native cursor is hidden under
   it. It now carries a bright border, a soft outer glow and a faint
   frosted fill, so it reads on every surface the site has. */
.cur {
  width: 40px; height: 40px; margin: -20px 0 0 -20px;
  border: 1.5px solid rgba(255,255,255,0.75);
  background: rgba(255,255,255,0.06);
  box-shadow: 0 0 0 1px rgba(0,0,0,0.35), 0 0 18px rgba(245,148,33,0.25);
  transition: width 0.4s var(--ease), height 0.4s var(--ease),
              margin 0.4s var(--ease), background 0.4s var(--ease),
              border-color 0.4s var(--ease), opacity 0.3s ease,
              box-shadow 0.4s var(--ease);
  display: flex; align-items: center; justify-content: center;
}
/* The centre dot rides on the ring, so it cannot be left behind. */
.cur::after {
  content: '';
  position: absolute;
  width: 6px; height: 6px; border-radius: 50%;
  background: var(--orange);
  box-shadow: 0 0 10px 2px rgba(245,148,33,0.7);
  transition: opacity 0.25s ease, transform 0.3s var(--ease);
}
.cur-txt {
  font-size: 9px; font-weight: 800; letter-spacing: 0.16em; text-transform: uppercase;
  color: #0a0a0a; opacity: 0; transform: scale(0.6);
  transition: opacity 0.3s ease, transform 0.4s var(--ease);
  white-space: nowrap;
}
html.has-cursor .cur { opacity: 1; }
/* Grow over links and buttons */
html.cur-link .cur {
  width: 72px; height: 72px; margin: -36px 0 0 -36px;
  background: rgba(255,255,255,0.14);
  border-color: rgba(255,255,255,0.5);
  box-shadow: 0 0 26px rgba(245,148,33,0.3);
}
html.cur-link .cur::after { opacity: 0; transform: scale(0.4); }
/* Fill and label over media */
html.cur-media .cur {
  width: 104px; height: 104px; margin: -52px 0 0 -52px;
  background: var(--orange); border-color: transparent;
}
html.cur-media .cur-txt { opacity: 1; transform: scale(1); }
html.cur-media .cur::after { opacity: 0; transform: scale(0.4); }

/* Hide the native cursor only where ours is actually drawn */
html.has-cursor,
html.has-cursor a,
html.has-cursor button,
html.has-cursor .reel,
html.has-cursor .vcard { cursor: none; }

/* ── Split text ─────────────────────────────────────────────
   forge.js wraps each line and word; the mask is the parent. */
.sp-line { display: block; overflow: hidden; }
/* The mask was exactly one line box tall, so Poppins descenders on y, g,
   p, j and q were sliced flat. The padding gives the mask room and the
   negative margin takes that room back out of the layout, so line
   spacing is unchanged. The reveal offset has to clear the taller mask
   as well, hence 130% rather than 105%: at 105% the word would now peek
   below the mask before it animates. Keep this in step with the
   yPercent value in forge.js. */
.sp-w {
  display: inline-block; overflow: hidden; vertical-align: top;
  padding-bottom: 0.18em; margin-bottom: -0.18em;
}
.sp-wi { display: inline-block; }
.js-anim .sp-wi { transform: translateY(130%); }

.sp-cw { display: inline-block; white-space: nowrap; }
.sp-c { display: inline-block; }
.js-anim .sp-c { opacity: 0; transform: translateY(48%) rotate(4deg); }

/* ── Masked media reveal ────────────────────────────────────
   The frame unmasks while the picture inside drifts back to
   scale 1, so the image appears to settle rather than pop. */
.mask { overflow: hidden; position: relative; }
.js-anim .mask > img,
.js-anim .mask > picture > img { transform: scale(1.32); }
.mask > img, .mask > picture > img { }
.js-anim .mask-c { clip-path: inset(0 0 100% 0); }

/* ── Parallax ───────────────────────────────────────────────*/
[data-par] { }

/* ── Pinned horizontal rail ─────────────────────────────────*/
.pin-sec { position: relative; overflow: hidden; }
.pin-head {
  display: flex; align-items: flex-end; justify-content: space-between;
  gap: 30px; flex-wrap: wrap; margin-bottom: clamp(30px, 4vw, 52px);
}
.pin-prog { display: flex; align-items: center; gap: 14px; }
.pin-prog-bar { width: clamp(90px, 14vw, 190px); height: 1px; background: rgba(255,255,255,0.16); }
.pin-prog-bar i { display: block; height: 100%; width: 0%; background: var(--grad); }
.pin-prog span { font-size: 11px; font-weight: 700; letter-spacing: 0.2em; color: var(--muted-2);
  font-variant-numeric: tabular-nums; }

.htrack { display: flex; gap: clamp(16px, 2vw, 28px); width: max-content; will-change: transform; }
.htrack .reel { flex: 0 0 clamp(210px, 24vw, 330px); }

/* While the section is pinned it is the whole screen, so everything in
   it has to fit the viewport. It did not: the heading block plus the
   section's own padding plus a reel sized from the viewport WIDTH added
   up to more than the viewport HEIGHT, so the reels were sliced off at
   the bottom for the entire pinned scroll and only came into view once
   the pin released. The section is now a flex column of a fixed height,
   the head takes what it needs, the rail takes the rest, and the reels
   are sized from that remaining height rather than from the width. */
@media (min-width: 861px) {
  .pin-sec {
    /* A definite height, not a minimum. With min-height the section was
       free to grow past the viewport whenever the reel's own aspect
       ratio wanted more room, which is how 16px of reel still ended up
       below the fold at 1280x700. A fixed height forces the rail to
       take only the space that is left, and the reel follows it. */
    height: 100svh;
    display: flex; flex-direction: column;
    padding-top: clamp(92px, 11vh, 140px);
    padding-bottom: clamp(34px, 5vh, 60px);
  }
  .pin-sec > .wrap:first-child { flex: 0 0 auto; }
  .pin-sec .pin-head { margin-bottom: clamp(20px, 2.6vh, 36px); }
  .pin-sec h2.big { font-size: clamp(30px, 4.4vw, 68px); }
  .htrack-wrap {
    flex: 1 1 auto; min-height: 0; display: flex; align-items: center;
    /* The last visible card was cut flat by the viewport edge with no
       signal the row continued. The right edge fades; the left edge
       only fades once the track has actually moved, which forge.js
       flags with .scrolled. */
    -webkit-mask-image: linear-gradient(to right, #000 0, #000 calc(100% - 120px), transparent 100%);
            mask-image: linear-gradient(to right, #000 0, #000 calc(100% - 120px), transparent 100%);
  }
  .htrack-wrap.scrolled {
    -webkit-mask-image: linear-gradient(to right, transparent 0, #000 110px, #000 calc(100% - 120px), transparent 100%);
            mask-image: linear-gradient(to right, transparent 0, #000 110px, #000 calc(100% - 120px), transparent 100%);
  }
  .htrack { height: 100%; align-items: center; }
  .htrack .reel { flex: 0 0 auto; height: 100%; max-height: 460px; width: auto; }
}

/* Without JS the rail still has to be usable, so it scrolls. */
html:not(.js-anim) .htrack-wrap,
html.no-pin .htrack-wrap { overflow-x: auto; }
html.no-pin .htrack { width: max-content; }

/* ── Process stages ─────────────────────────────────────────
   Was six sticky panels, each a full viewport tall, which made this
   one section 7069px of the page on its own. It is a grid of six
   cards now, about 1400px in total.

   The old card was three columns, 86px number, then the title, then
   the body, which left roughly 500px of empty space across the middle
   of a 1280px card. Number and title now sit together on one row with
   the body directly under them, so the text reads as a single block
   and there is no dead region. */
.stack {
  position: relative;
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: clamp(14px, 1.4vw, 20px);
}
.stack-item { position: static; min-height: 0; display: block; }
.stack-in {
  height: 100%;
  border: 1px solid var(--line-strong);
  border-radius: 18px;
  padding: clamp(22px, 2vw, 30px);
  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);
  box-shadow: inset 0 1px 0 var(--edge-hi), 0 18px 44px rgba(0,0,0,0.5);
  display: grid;
  /* auto for the header row, 1fr for the body, so the number and title
     sit at a fixed height at the top of every card regardless of how
     much copy follows. Cards with one paragraph were centring their
     content and pushing the title about 15px off its neighbours. */
  grid-template-columns: auto 1fr;
  grid-template-rows: auto 1fr;
  grid-template-areas: "num title" "body body";
  gap: 12px clamp(14px, 1.4vw, 20px);
  align-items: center;
  transition: transform 180ms var(--ease), border-color 180ms var(--ease),
              background-color 180ms var(--ease), box-shadow 180ms var(--ease);
}
.stack-in:hover {
  transform: translateY(-5px);
  border-color: rgba(255,255,255,0.22);
  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);
  box-shadow: inset 0 1px 0 rgba(255,255,255,0.12), 0 26px 60px rgba(0,0,0,0.58);
}
.stack-in > .stack-n { grid-area: num; }
.stack-in > div:not(.stack-b):not(.stack-n) { grid-area: title; }
.stack-in > .stack-b {
  grid-area: body; margin-top: 4px;
  align-self: stretch;
  display: flex; flex-direction: column;
}
/* Chips ride the bottom of the card so every tag row in a grid row
   lands on the same baseline. */
.stack-b .stack-tags { margin-top: auto; }

@media (max-width: 1199px) { .stack { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (max-width: 760px)  { .stack { grid-template-columns: 1fr; } }
.stack-n {
  font-size: clamp(30px, 2.8vw, 44px); font-weight: 900; letter-spacing: -0.05em;
  line-height: 0.85; color: transparent; -webkit-text-stroke: 1px rgba(255,255,255,0.28);
}
.stack-item.on .stack-n, .stack-in:hover .stack-n { -webkit-text-stroke: 1px rgba(245,148,33,0.85); }
.stack-t { font-size: clamp(21px, 1.9vw, 28px); font-weight: 900; line-height: 1; letter-spacing: -0.035em; }
.stack-b p + p { margin-top: 14px; }
.stack-tags { display: flex; flex-wrap: wrap; gap: 7px; margin-top: 22px; }
.stack-tags span {
  font-size: 10px; font-weight: 700; letter-spacing: 0.14em; text-transform: uppercase;
  color: var(--muted); border: 1px solid var(--line); border-radius: 100px; padding: 7px 13px;
}

/* ── Velocity skew ──────────────────────────────────────────*/
[data-skew] { }

/* ── Scroll progress line ───────────────────────────────────*/
.sprog { position: fixed; top: 0; left: 0; height: 2px; width: 100%; z-index: 300;
  transform: scaleX(0); transform-origin: 0 50%; background: var(--grad); }

/* ── Section counter rail ───────────────────────────────────*/
.tick-rail {
  position: fixed; right: 26px; top: 50%; transform: translateY(-50%);
  z-index: 150; display: flex; flex-direction: column; gap: 12px;
  opacity: 0; transition: opacity 0.5s ease;
}
.tick-rail.on { opacity: 1; }
.tick-rail a { width: 22px; height: 1px; background: rgba(255,255,255,0.22); transition: 0.4s var(--ease); }
.tick-rail a.on { background: var(--orange); width: 38px; }

/* ── Magnetic wrapper ───────────────────────────────────────*/
.mag { display: inline-flex; }


/* ── Marquee driven by scroll ───────────────────────────────
   The CSS keyframe is dropped once JS takes over so the two
   cannot fight over the same transform. */
html.js-drive .strip-track { animation: none; }

/* ── Big outline statement ──────────────────────────────────*/
.statement {
  font-size: clamp(46px, 11vw, 190px); font-weight: 900;
  letter-spacing: -0.05em; line-height: 0.85; text-transform: uppercase;
}
.statement .ol { color: transparent; -webkit-text-stroke: 1px rgba(255,255,255,0.3); }

/* ── Reduced motion and no-hover fallbacks ──────────────────*/
@media (hover: none), (pointer: coarse) {
  .cur { display: none; }
}

@media (max-width: 860px) {
  .tick-rail { display: none; }
  .stack-in { transform: none !important; opacity: 1 !important; }
}

@media (prefers-reduced-motion: reduce) {
  .pl { display: none; }
  .cur, .curtain, .sprog, .tick-rail { display: none; }
  .js-anim .sp-wi { transform: none; }
  .js-anim .sp-c { opacity: 1; transform: none; }
  .js-anim .mask > img, .js-anim .mask > picture > img { transform: none; }
  .js-anim .mask-c { clip-path: none; }
  .stack-item { position: static; min-height: auto; }
}
