/* ===========================================================
   SYDRTH — portfolio v2
   Faithful to mock: cream pill nav, frosted glass card,
   "Human-first / AI-second" → "Hello, I am Siddharth" arc.
   Fonts: Canela (display) + Google Sans (UI/body)
   =========================================================== */

/* ---------- Local fonts (commercial — keep in assets/fonts/) ---------- */
@font-face {
  font-family: 'Canela';
  font-style: normal;
  font-weight: 300;
  font-display: swap;
  src: url('assets/fonts/Canela-Light.woff2') format('woff2');
}
@font-face {
  font-family: 'Canela';
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url('assets/fonts/Canela-Regular.woff2') format('woff2');
}
@font-face {
  font-family: 'Canela';
  font-style: normal;
  font-weight: 500;
  font-display: swap;
  src: url('assets/fonts/Canela-Medium.woff2') format('woff2');
}
@font-face {
  font-family: 'Google Sans';
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url('assets/fonts/GoogleSans-Regular.woff2') format('woff2');
}
@font-face {
  font-family: 'Google Sans';
  font-style: normal;
  font-weight: 500;
  font-display: swap;
  src: url('assets/fonts/GoogleSans-Medium.woff2') format('woff2');
}
@font-face {
  font-family: 'Google Sans';
  font-style: normal;
  font-weight: 700;
  font-display: swap;
  src: url('assets/fonts/GoogleSans-Bold.woff2') format('woff2');
}

:root {
  /* Palette pulled from the video footage */
  --bg-deep:    #0a1220;     /* fallback when video isn't rendered */
  --blue-deep:  #1a4769;     /* dominant blue from mock */
  --blue-mid:   #2f678c;
  --steel:      #d4d9df;
  --white:      #ffffff;
  --pale-blue:  #b8d3e6;     /* "AI-second" tint */
  --steel-dim:  #8a96a6;
  --steel-faint:#3b475b;

  /* Pill nav cream */
  --cream:      #f0e6d4;
  --cream-soft: #e8dbc4;
  --ink:        #14253a;     /* dark text on cream */

  /* Type — wordmark uses Helvetica per spec, body uses Google Sans, display uses Canela */
  --display:  'Canela', 'Times New Roman', serif;
  --sans:     'Google Sans', system-ui, sans-serif;
  --wordmark: 'Helvetica Neue', Helvetica, 'Arial Black', sans-serif;

  --ease-out-soft: cubic-bezier(0.22, 1, 0.36, 1);
  --ease-cinema:   cubic-bezier(0.65, 0, 0.35, 1);
}

* { box-sizing: border-box; margin: 0; padding: 0; }

html {
  scroll-behavior: smooth;
  /* Proximity snap (not mandatory). Mandatory hijacks scrolls inside
     the stage's 500vh scrub and jumps straight to My Work. Proximity
     only kicks in when the user pauses near a snap target — inside
     the stage there are no snap targets so scrubbing works free, but
     at section boundaries (work-intro / each chapter) the browser
     gently glides into place. */
  scroll-snap-type: y proximity;
}

/* Snap targets — one stop per content "page" after the intro stage. */
.work-intro-panel,
.chapter {
  scroll-snap-align: start;
}

body {
  background: var(--bg-deep);
  color: var(--white);
  font-family: var(--sans);
  font-weight: 400;
  font-size: 16px;
  line-height: 1.5;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  overflow-x: hidden;
  cursor: default;
}

img, video { display: block; max-width: 100%; }
a { color: inherit; text-decoration: none; }
em { font-style: italic; }

/* ========= WORDMARK (top-left, scales on scroll) =========
   Now an image asset (assets/sydrth-logo.png). The <a> remains the
   transform target — JS still writes --wordmark-scale on this element
   during phase 4, and transform-origin: top left keeps the mark
   anchored to the corner as it shrinks. The <img> inside is sized by
   height; width follows from the asset's intrinsic aspect ratio. */
.wordmark {
  position: fixed;
  top: 36px;
  left: 48px;
  z-index: 60;
  display: inline-block;
  transform-origin: top left;
  transform: scale(var(--wordmark-scale, 1));
  will-change: transform, opacity;
  /* Keep the link sized exactly to its image — no inherited line-box quirks. */
  line-height: 0;
  /* Smoothly fade out when user enters work section (is-hidden added by JS) */
  transition: opacity 0.5s ease;
}

.wordmark.is-hidden {
  opacity: 0;
  pointer-events: none;
}

.wordmark__img {
  display: block;
  /* Height-driven sizing to roughly match the previous text wordmark's
     visual weight. Width follows from aspect ratio (~2.94:1). */
  height: clamp(60px, 9.5vw, 140px);
  width: auto;
  /* Ensure crisp scaling on retina; no smoothing artefacts when the
     transform shrinks it during phase 4. */
  user-select: none;
  -webkit-user-drag: none;
}

/* ========= PILL NAV (top-right) — dark glass with cream CTA inside ========= */
.pill-nav {
  position: fixed;
  top: 36px;
  right: 48px;
  z-index: 60;
  display: flex;
  align-items: center;
  gap: 4px;
  /* Frosted glass — a near-neutral tint (slight cool cast) at higher
     opacity so the pill reads as luminous against navy. The previous
     navy-tinted fill made it read as "dark blue translucent panel"
     instead of "frosted glass". Reference: white-leaning frost. */
  background: rgba(255, 255, 255, 0.10);
  backdrop-filter: blur(20px) saturate(1.4);
  -webkit-backdrop-filter: blur(20px) saturate(1.4);
  border-radius: 999px;
  padding: 6px;
  /* Glass edge: brighter top highlight + soft hairline border */
  border: 1px solid rgba(255, 255, 255, 0.22);
  box-shadow:
    inset 0 1px 0 rgba(255, 255, 255, 0.35),
    inset 0 -1px 0 rgba(0, 0, 0, 0.10),
    0 8px 28px rgba(0, 0, 0, 0.20);
  font-family: var(--sans);
  /* Smoothly fade in/out when phase 1 ends — see .is-hidden below */
  transition: opacity 0.5s ease, transform 0.5s ease;
}

/* Zero-state: wordmark only. JS removes this class when stage exits phase 1. */
.pill-nav.is-hidden {
  opacity: 0;
  pointer-events: none;
  transform: translateY(-8px);
}

.pill-nav__items {
  display: flex;
  list-style: none;
  align-items: center;
  gap: 0;
}

.pill-nav__link {
  position: relative;
  display: inline-block;
  padding: 10px 22px;
  font-size: 15px;
  font-weight: 400;
  color: rgba(255, 255, 255, 0.82);
  border-radius: 999px;
  transition: color 0.3s var(--ease-out-soft);
}

.pill-nav__link:hover { color: var(--white); }

/* Active state — short underline beneath the label, white */
.pill-nav__link.is-active {
  color: var(--white);
  font-weight: 500;
}
.pill-nav__link.is-active::after {
  content: '';
  position: absolute;
  left: 50%;
  bottom: 4px;
  width: 18px;
  height: 1.5px;
  background: var(--white);
  transform: translateX(-50%);
  border-radius: 2px;
}

/* The CTA — cream pill nested inside the dark glass pill */
.pill-nav__cta {
  display: inline-flex;
  align-items: center;
  /* Tighter spacing between text and arrow */
  gap: 8px;
  /* Asymmetric padding: more on the left (where text starts), less on
     the right (where arrow sits closer to the edge). */
  padding: 10px 18px 10px 24px;
  margin-left: 6px;
  background: var(--cream);
  color: var(--ink);
  border-radius: 999px;
  font-size: 15px;
  font-weight: 500;
  box-shadow:
    inset 0 1px 0 rgba(255, 255, 255, 0.6),
    0 1px 2px rgba(0, 0, 0, 0.08);
  transition: background 0.3s var(--ease-out-soft), transform 0.3s var(--ease-out-soft);
}

.pill-nav__cta:hover {
  background: #f5e9d0;
  transform: translateY(-1px);
}

.pill-nav__arrow { display: inline-block; }

/* ========= STAGE (scroll-pinned hero) ========= */
.stage {
  position: relative;
  /* z-index 1 so the stage renders ABOVE the .work__glow fixed
     gradient (z-index 0). Without this explicit z-index, the fixed
     gradient at z-index 0 wins over the stage's "auto" z-index, and
     paints on top of the video. */
  z-index: 1;
  /* Was 500vh — Sid flagged that both held frames ("Human-first.
     AI-second." Hold A and "Hello, I am Siddharth" Hold B) felt too
     fast on scroll. Bumped to 650vh to give every phase more pixels
     of scroll distance; the new phase boundaries in script.js skew
     the extra budget toward the two holds (P3 and P5). */
  height: 650vh;
  width: 100%;
  /* Opaque navy — covers the .work__glow gradient layer (z-index 0)
     during intro phases. Phase 6 fades this stage's opacity to 0,
     revealing the gradient underneath. */
  background: var(--bg-deep);
}

.stage__sticky {
  position: sticky;
  top: 0;
  height: 100vh;
  width: 100%;
  overflow: hidden;
}

/* ===== SCROLL HINT =====
   Subtle scroll affordance shown during the stage's zero-state.
   Sits bottom-center of the sticky viewport so it travels with the
   video. Fades to 0 the moment the user begins scrolling — script.js
   toggles `.is-faded` based on stage scroll progress. Once faded,
   stays hidden for the rest of the visit. */
.stage__scroll-hint {
  position: absolute;
  /* Bottom-center of the viewport. The 56px clearance keeps it above
     the bottom edge so it doesn't kiss anything; large enough to feel
     intentional without crowding the wordmark or pill nav. */
  left: 50%;
  bottom: 56px;
  transform: translateX(-50%);
  /* Stack: pill mouse glyph above, label below. */
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 16px;
  /* Quiet on the dark navy — desaturated steel-teal that reads as a
     UI hint rather than competing with the wordmark or any layer text. */
  color: rgba(184, 211, 230, 0.55);
  /* Above the video and vignette but below the layer text. */
  z-index: 4;
  pointer-events: none;
  /* Initial fade-IN so it doesn't pop on page load — eases up to full
     opacity over the first ~600ms of viewing. */
  opacity: 0;
  animation: scroll-hint-fade-in 0.8s 0.4s var(--ease-out-soft) forwards;
  transition: opacity 0.5s var(--ease-out-soft),
              transform 0.5s var(--ease-out-soft);
}

/* Fade-out state: triggered by JS the moment the user starts scrolling.
   Once applied, the hint never returns — even if the user scrolls back
   to the top, this class stays on. */
.stage__scroll-hint.is-faded {
  opacity: 0 !important;
  /* Tiny downward drift on fade-out — feels like the hint is "going
     away" because the user did the thing it asked for. */
  transform: translateX(-50%) translateY(8px);
  /* Disable the dot animation once hidden so it's not running invisibly. */
  animation: none;
}

@keyframes scroll-hint-fade-in {
  to { opacity: 1; }
}

/* The pill outline ("mouse" body) — slim 1px stroke, rounded ends. */
.stage__scroll-hint-mouse {
  position: relative;
  width: 26px;
  height: 42px;
  border: 1.5px solid currentColor;
  border-radius: 14px;
}

/* The animated dot inside the pill. Drifts down on a 1.6s loop to
   suggest "scroll downward". Pure CSS, no JS. */
.stage__scroll-hint-dot {
  position: absolute;
  left: 50%;
  top: 8px;
  width: 4px;
  height: 4px;
  border-radius: 50%;
  background: currentColor;
  transform: translateX(-50%);
  animation: scroll-hint-dot 1.6s var(--ease-out-soft) infinite;
}

@keyframes scroll-hint-dot {
  /* Start near the top, drift down, fade out near the bottom, reset. */
  0%   { transform: translate(-50%, 0); opacity: 0; }
  20%  { opacity: 1; }
  70%  { transform: translate(-50%, 16px); opacity: 1; }
  100% { transform: translate(-50%, 22px); opacity: 0; }
}

.stage__scroll-hint-label {
  font-family: var(--sans);
  font-size: 11px;
  font-weight: 500;
  letter-spacing: 0.32em;
  /* Optical balance — wide letter-spacing pushes mass right of center. */
  padding-left: 0.32em;
}

/* Reduced motion: keep the hint visible but stop animating the dot. */
@media (prefers-reduced-motion: reduce) {
  .stage__scroll-hint {
    animation: none;
    opacity: 1;
  }
  .stage__scroll-hint-dot { animation: none; }
}

.stage__video {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  /* CSS-level poster fallback. If the video file fails to decode (which
     can happen on file:// protocol in some browsers), this poster image
     is still shown as a background-image. When the video DOES load,
     its frames paint on top. */
  background-image: url('assets/hero-poster.jpg');
  background-size: cover;
  background-position: center;
  filter: blur(40px) saturate(0.7) brightness(0.75);
  transform: scale(1.1);
  will-change: filter, transform;
}

.stage__vignette {
  position: absolute;
  inset: 0;
  pointer-events: none;
  background:
    linear-gradient(to bottom, rgba(10, 18, 32, 0.25) 0%, transparent 30%, transparent 70%, rgba(5, 12, 24, 0.6) 100%);
}

/* ========= STAGE LAYERS ========= */
.stage__layer {
  position: absolute;
  inset: 0;
  z-index: 3;
  opacity: 0;
  pointer-events: none;
  will-change: opacity;
}

/* ----- LAYER A: Human-first / AI-second + glass card ----- */
.stage__layer--a {
  display: grid;
  grid-template-columns: 1fr auto;
  align-items: end;
  /* Equal padding on left, right, and bottom so both children
     read as equidistant from the viewport edges. */
  padding: 0 48px 48px;
  gap: 40px;
}

.hero-a__title {
  font-family: var(--display);
  font-weight: 300;
  /* Reduced 20% from previous clamp(2.8rem, 8.6vw, 8rem). */
  font-size: clamp(2.24rem, 6.88vw, 6.4rem);
  line-height: 1.02;             /* slightly looser to give the f-tail room visually */
  letter-spacing: -0.025em;
  color: var(--white);
  max-width: 16ch;
  font-style: normal;            /* "Human-first." stays upright */
}

.hero-a__title em {
  font-style: normal;            /* both lines upright per mock */
  color: var(--pale-blue);
}

/* Frosted glass card — matches mock spec.
   Visible 1px hairline border on all four sides (mock shows it equally
   bright on every edge — no top-heavy inset highlight). Card body lifts
   the backdrop slightly via low-opacity white + saturation bump from
   backdrop-filter. */
.glass-card {
  position: relative;
  align-self: end;
  /* Vertical padding reduced 33% (48 → 32) to shrink card height ~20%
     overall while keeping horizontal padding the same so word-wrap is
     unchanged. Edge distance unchanged (stays anchored to layer's
     padding 48px from bottom and right). */
  padding: 32px 44px;
  min-width: 320px;
  border-radius: 0;                            /* sharp rectangle per mock */
  background: rgba(255, 255, 255, 0.06);
  backdrop-filter: blur(16px) saturate(1.2);
  -webkit-backdrop-filter: blur(16px) saturate(1.2);
  /* Single visible hairline on all sides — matches mock exactly */
  border: 1px solid rgba(255, 255, 255, 0.32);
  /* Subtle outer shadow only; no inset top-only highlight that would
     make the top edge look brighter than the others. */
  box-shadow: 0 6px 22px rgba(0, 0, 0, 0.10);
  color: var(--white);
}

.glass-card__title {
  font-family: var(--sans);
  font-weight: 700;                            /* Bold — heaviest weight loaded */
  font-size: clamp(1.7rem, 2.4vw, 2.3rem);
  line-height: 1.05;
  letter-spacing: -0.01em;                     /* tighter — adds visual heft to compensate */
  text-transform: uppercase;
  color: var(--white);
}

.glass-card__sub {
  margin-top: 12px;
  font-family: var(--sans);
  font-weight: 700;                            /* bold per latest spec */
  /* Sized to ~0.54x of title — matches mock proportion */
  font-size: clamp(1rem, 1.3vw, 1.25rem);
  letter-spacing: 0.04em;
  text-transform: uppercase;
  /* Desaturated pale-blue-gray, sits quietly inside the card */
  color: rgba(216, 226, 236, 0.55);
}

/* ----- LAYER B: "Hello, I am Siddharth." + intro paragraph
        Right-side stacked. Title and body both pinned to right column,
        body sits directly under title. Figure occupies left half. */
.stage__layer--b {
  display: flex;
  align-items: center;
  /* Position the column equidistant between the subject's right edge
     (~58% across) and the viewport right edge (100%). The midpoint of
     that 42% gap sits at ~79% across. We place the column there using
     a calculated right padding that keeps the gaps symmetric: with a
     400px column and 1440px viewport, padding-right ~100px lands the
     left edge at ~940 and the right edge at ~1340 — gap to subject ~110,
     gap to viewport edge ~100. Visually equidistant. */
  justify-content: flex-end;
  padding: 0 100px 0 0;
}

.stage__layer--b .hero-b {
  /* Override the wider default so this column doesn't overflow toward
     the subject. Smaller width keeps the column tight on the right. */
  width: min(28%, 400px);
}

.hero-b {
  /* Wrapper that holds title above body, both right-aligned to the column. */
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 16px;                                 /* tighter title→body gap per mock */
  width: min(34%, 480px);
}

.hero-b__title {
  font-family: var(--display);
  font-weight: 300;
  font-size: clamp(2.5rem, 5.6vw, 5.4rem);
  line-height: 1.0;
  letter-spacing: -0.025em;
  color: var(--pale-blue);
  font-style: normal;          /* upright per mock */
}

.hero-b__title em {
  font-style: normal;          /* keep upright, no italic */
  color: var(--white);         /* "Siddharth." — white per latest mock */
}

.hero-b__intro {
  font-family: var(--sans);
  font-weight: 400;
  font-size: clamp(1rem, 1.2vw, 1.2rem);
  line-height: 1.5;
  color: rgba(255, 255, 255, 0.82);
  max-width: 32ch;
}

/* Split text — words start hidden, JS reveals.
   Wrapper uses padding + clip-path instead of overflow:hidden so that
   descenders, italic serif tails (the "f" in "first"), and any glyph
   that excurses outside the em-box don't get cropped. The clip-path
   masks during reveal but releases at rest so glyphs render in full. */
[data-split="words"] .word {
  display: inline-block;
  vertical-align: top;
  /* Generous vertical padding so glyph excursions never touch the box edges */
  padding: 0.18em 0 0.22em;
  margin: -0.18em 0 -0.22em;
  /* clip-path masks the inner during the reveal; once the inner has
     translated to y:0%, this still sits beyond the visible glyph and
     no longer crops anything. */
  clip-path: inset(0);
  -webkit-clip-path: inset(0);
}
[data-split="words"] .word-inner {
  display: inline-block;
  transform: translateY(110%);
  opacity: 0;
}

/* ========= WORK SECTION
     Backdrop architecture:
     - .work__grid is the faint grid overlay.
     - .work__glow is the bottom gradient — pale-blue light rising from
       both bottom corners, with a darker valley running down the
       center. Sampled directly from the 34.png reference.
     - Both are POSITION: FIXED so they stay locked to viewport.
     - Cards live in normal scroll flow.
   ========================================================= */
.work {
  position: relative;
  /* No background — the .work__glow fixed layer (z-index 0) is what
     paints behind the work section. Setting a background here would
     override the gradient. */
  background: transparent;
  padding: 0;
  /* NOTE: perspective lives on each .chapter, not here, so position:
     fixed children stay anchored to the viewport instead of being
     trapped inside this element's transform context. */
}

/* Grid is baked into work-bg.jpg — separate overlay disabled. */
.work__grid { display: none; }

/* The 34.png gradient — POSITION FIXED, ALWAYS PAINTED at opacity 1
   from page load. During intro phases the stage's solid background
   covers it (we never see it). During phase 6 the stage fades to
   opacity 0, revealing the gradient underneath instantly — no fade-in
   delay, no navy strip. After phase 6, the gradient is the only thing
   between viewport and body, so it shows through across My Work and
   all chapters. */
.work__glow {
  position: fixed;
  inset: 0;
  pointer-events: none;
  z-index: 0;
  background-image: url('assets/work-bg.jpg');
  background-size: cover;
  background-position: center bottom;
  background-repeat: no-repeat;
  opacity: 1;
}

/* ===== "MY WORK" INTRO PANEL ===== */
.work-intro-panel {
  position: relative;
  z-index: 1;
  height: 100vh;
  min-height: 720px;
  display: flex;
  align-items: center;
  justify-content: center;
  /* Clip the curve animations to the panel's bounds — without this, a
     curve drifting near the panel's bottom edge can peek into the
     chapter 1 section during scroll transitions. */
  overflow: hidden;
}

/* ===== Math curves layer =====
   Five trail-follower curves at scattered fixed positions around the
   title. Each .curve hosts an SVG (built by curves.js) running its
   own particle-trail animation. Containers fade in/out + drift in the
   anticlockwise tangent direction over each cycle, with motion blur
   peaking on the fade edges (suggests inertia — the curve is "moving"
   even though it's only translating slightly). Hover snaps to white. */
/* Curves are POSITION FIXED — they don't scroll with the page. They
   appear after a brief delay when user lands on My Work, and fade out
   (fizzle) before chapter 1 emerges. JS toggles `.is-curves-on` on
   body to drive the master enter/leave fade. */
.curves {
  position: fixed;
  inset: 0;
  pointer-events: none;
  z-index: 1;
  /* Pale-blue color flows through to <path stroke="currentColor"> and
     particle <circle fill="currentColor"> inside each curve's SVG. */
  color: rgb(184, 211, 230);
  /* Master visibility — JS toggles body.is-curves-on to fade in/out */
  opacity: 0;
  transition: opacity 0.6s ease-out;
}

body.is-curves-on .curves {
  opacity: 1;
}

/* Collective motion — the whole group of 5 curves rotates anticlockwise
   around the panel's center as a unit. Slow (90s/revolution) so the
   eye reads it as ambient drift rather than active spinning. */
.curves__group {
  position: absolute;
  inset: 0;
  animation: curves-group-rotate 90s linear infinite;
  transform-origin: 50% 50%;
}

@keyframes curves-group-rotate {
  from { transform: rotate(0deg); }
  to   { transform: rotate(-360deg); }   /* negative = anticlockwise */
}

.curve {
  position: absolute;
  /* --x and --y are positions in % of the panel, set inline per curve */
  left: var(--x);
  top: var(--y);
  width: var(--size, 200px);
  height: var(--size, 200px);
  /* Cycle handles BOTH centering translate AND in-place wobble together.
     Total cycle = 20s. Visible window ~25% (5s) so most of the time the
     curve is hidden — matches reel where 1-2 max are lit at any moment. */
  animation: curve-cycle 20s ease-in-out var(--fade-delay, 0s) infinite;
  opacity: 0;
  /* pointer-events DEFAULT off — only enabled while curve is visible
     enough for a user to actually see it. JS animates a CSS variable
     that flips this on near peak opacity. Even simpler trick: set
     `pointer-events: auto` on a visible-only state (.is-visible) we
     toggle via JS based on the cycle phase. We do that below. */
  pointer-events: none;
  /* Smooth transition for the hover snap */
  transition: filter 0.25s ease, color 0.25s ease;
}

/* Single keyframe combining: opacity fade + centering + in-place wobble.
   Wobble is a tiny rotation that runs throughout the visible window —
   creates the "alive" feel from the reel where each sketch shimmers
   subtly even while standing still. Most of the cycle is invisible.
   pointer-events animates as discrete steps so hover only works while
   the curve is actually visible. */
@keyframes curve-cycle {
  0% {
    opacity: 0;
    transform: translate(-50%, -50%) rotate(-3deg) scale(0.96);
    filter: blur(4px);
    pointer-events: none;
  }
  8% {
    pointer-events: none;
  }
  10% {
    opacity: 0.9;
    transform: translate(-50%, -50%) rotate(-1deg) scale(0.99);
    filter: blur(0.8px);
    pointer-events: auto;
  }
  20% {
    opacity: 0.9;
    transform: translate(-50%, -50%) rotate(0deg) scale(1);
    filter: blur(0);
    pointer-events: auto;
  }
  30% {
    opacity: 0.9;
    transform: translate(-50%, -50%) rotate(2deg) scale(1.01);
    filter: blur(0);
    pointer-events: auto;
  }
  35% {
    pointer-events: none;
  }
  40% {
    opacity: 0;
    transform: translate(-50%, -50%) rotate(3deg) scale(0.98);
    filter: blur(4px);
  }
  100% {
    opacity: 0;
    transform: translate(-50%, -50%) rotate(0deg) scale(0.96);
    filter: blur(4px);
  }
}

/* Hover: snap stroke + particles to white, add a soft glow halo,
   pause both the cycle and the group rotation so the hover target
   stays still. */
.curve:hover {
  color: rgb(255, 255, 255);
  filter: drop-shadow(0 0 8px rgba(255, 255, 255, 0.55))
          drop-shadow(0 0 18px rgba(255, 255, 255, 0.35)) !important;
  animation-play-state: paused;
  opacity: 1 !important;
}

/* When any curve is hovered, also pause the parent group rotation */
.curves__group:has(.curve:hover) {
  animation-play-state: paused;
}

.work-intro-panel__inner {
  position: relative;
  /* Above curves */
  z-index: 2;
  text-align: center;
  max-width: 720px;
  padding: 0 40px;
}

.work-intro-panel__title {
  font-family: var(--display);
  font-weight: 300;
  font-size: clamp(4.5rem, 11vw, 11rem);
  line-height: 1;
  letter-spacing: -0.025em;
  color: var(--white);
  margin-bottom: 28px;
}

.work-intro-panel__note {
  font-family: var(--sans);
  /* Bigger & brighter for proper visibility against the gradient */
  font-size: clamp(1.05rem, 1.25vw, 1.18rem);
  line-height: 1.55;
  font-weight: 400;
  color: rgba(216, 226, 236, 0.85);    /* ~85% pale-blue-white */
  max-width: 64ch;
  margin: 24px auto 0;
}

/* ===== CHAPTER CARDS =====
   Each card enters with a 3D tilt that flattens as it comes into view —
   like the card is being pulled forward through space. */
.chapter {
  position: relative;
  z-index: 1;
  /* Single viewport per chapter. Each chapter card now strictly fits
     inside one viewfold so snap-scroll feels precise and consistent
     across all four chapters regardless of media-vs-empty state.
     Using `dvh` so mobile browser-chrome doesn't push content out of
     view; falling back to `vh` for older browsers. */
  height: 100vh;
  height: 100dvh;
  /* Floor so the card has room on very short viewports (rare, but
     covers things like split-screen on Surface tablets). */
  min-height: 640px;
  padding: 60px 64px;
  display: flex;
  align-items: center;
  justify-content: center;
  /* Perspective on each chapter so we can tilt the inner card */
  perspective: 2000px;
}

.chapter__card {
  position: relative;
  width: 100%;
  max-width: 1480px;
  /* Asymmetric padding: top + sides for the header, ZERO bottom so
     the media zone bleeds flush to the card's bottom border. The
     media's negative side-margins already swallow the 56px side
     padding; with bottom padding gone, the image hits all three of
     left, right, bottom edges and only the top of the card holds
     header content. Per Sid's spec: "extend the image to the bottom
     of the card, don't change header." */
  padding: 40px 56px 0;
  /* Card now uses flex-column so the bottom media zone can absorb
     all leftover height, ensuring the entire card fits within one
     viewfold no matter what's inside (image vs "Under construction"). */
  display: flex;
  flex-direction: column;
  /* Card fills its parent .chapter exactly. Section padding (60+60)
     is the only thing sitting outside it. */
  height: 100%;
  background: rgba(255, 255, 255, 0.04);
  backdrop-filter: blur(14px) saturate(1.15);
  -webkit-backdrop-filter: blur(14px) saturate(1.15);
  border: 1px solid rgba(255, 255, 255, 0.14);
  border-radius: 0;
  box-shadow:
    inset 1.5px 1.5px 0 rgba(255, 255, 255, 0.16),
    inset 0 -1px 0 rgba(0, 0, 0, 0.08),
    0 12px 40px rgba(0, 0, 0, 0.18);
  overflow: hidden;
  transform-style: preserve-3d;
  /* Cursor-tracking glow lives here */
}

/* Cursor-following spotlight inside the card — coords driven by JS */
.chapter__card::before {
  content: '';
  position: absolute;
  inset: 0;
  background: radial-gradient(
    420px circle at var(--mx, 50%) var(--my, 50%),
    rgba(184, 211, 230, 0.10),
    transparent 60%
  );
  opacity: 0;
  transition: opacity 0.5s var(--ease-out-soft);
  pointer-events: none;
  z-index: 2;
}
.chapter__card:hover::before {
  opacity: 1;
}

/* A second highlight that catches the upper-left edge — like rim light */
.chapter__card::after {
  content: '';
  position: absolute;
  inset: 0;
  background: linear-gradient(
    135deg,
    rgba(255, 255, 255, 0.06) 0%,
    transparent 30%
  );
  pointer-events: none;
  z-index: 1;
}

.chapter__head {
  display: grid;
  grid-template-columns: 1.1fr 0.9fr;
  gap: 80px;
  margin-bottom: 56px;
  align-items: start;
  position: relative;
  z-index: 3;
  /* Natural height — header doesn't compete with media for the leftover
     space in the flex column. The media zone below uses flex: 1 to
     absorb whatever remains. */
  flex: 0 0 auto;
}

.chapter__col--copy {
  padding-top: 38px;
  display: flex;
  flex-direction: column;
  gap: 36px;
  align-items: flex-start;
}

.chapter__eyebrow {
  font-family: var(--sans);
  font-size: 13px;
  font-weight: 500;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--steel-dim);
  margin-bottom: 24px;
}

.chapter__title {
  font-family: var(--display);
  font-weight: 300;
  font-size: clamp(2.2rem, 4.6vw, 4.4rem);
  line-height: 1.02;
  letter-spacing: -0.02em;
  color: var(--white);
  max-width: 14ch;
}

.chapter__body {
  font-family: var(--sans);
  font-size: clamp(1rem, 1.15vw, 1.15rem);
  line-height: 1.55;
  color: rgba(255, 255, 255, 0.78);
  max-width: 50ch;
}

/* CTA group: button on the LEFT, lock + microcopy aside on the RIGHT.
   Per Sid's directive: don't increase the vertical height of the CTA
   row, so the aside sits next to the button rather than beneath it.
   Wraps to a new line only at narrow widths (mobile). */
.chapter__cta-group {
  display: flex;
  flex-direction: row;
  flex-wrap: wrap;
  align-items: center;
  gap: 16px 20px; /* row-gap (only matters on wrap) | column-gap */
}

/* The lock + microcopy unit, sitting to the right of the button. Lock
   icon is a small inline glyph; the text is a short reassurance line.
   Both align centered with the button's vertical center. */
.chapter__cta-aside {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  /* Cap width so the microcopy doesn't push the row absurdly wide on
     huge viewports; on narrow ones it'll wrap to its own line via
     flex-wrap above. */
  max-width: 32ch;
}

.chapter__cta {
  display: inline-flex;
  align-items: center;
  /* Slightly tighter gap (label | arrow) now that the lock icon is
     no longer part of the CTA per the new mocks. */
  gap: 8px;
  /* Asymmetric: more left, less right so arrow sits close to the edge */
  padding: 14px 22px 14px 24px;
  border-radius: 999px;
  background: rgba(20, 37, 58, 0.5);
  border: 1px solid rgba(255, 255, 255, 0.14);
  color: var(--white);
  font-family: var(--sans);
  font-size: 15px;
  font-weight: 500;
  text-decoration: none;
  box-shadow:
    inset 0 1px 0 rgba(255, 255, 255, 0.12),
    0 2px 8px rgba(0, 0, 0, 0.16);
  transition: background 0.3s var(--ease-out-soft), transform 0.3s var(--ease-out-soft);
}
.chapter__cta:hover {
  background: rgba(35, 60, 90, 0.65);
  transform: translateY(-1px);
}

.chapter__cta-arrow {
  display: inline-block;
  transition: transform 0.3s var(--ease-out-soft);
}
.chapter__cta:hover .chapter__cta-arrow {
  transform: translate(2px, -2px);
}

/* Lock icon — sits inside the aside, to the left of the microcopy.
   Slightly dim so it reads as auxiliary info ("this is gated") rather
   than as a button. Color matches the microcopy text for visual unity. */
.chapter__cta-lock {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  /* Match the microcopy's tone — both register as the same visual
     unit (icon + label) rather than competing weights. */
  color: rgba(216, 226, 236, 0.65);
  flex: 0 0 auto;
}

/* Microcopy text — sits to the right of the lock. Light, small,
   single line. White-space allowed to wrap inside the aside if the
   container is tight, but max-width on the parent caps it. */
.chapter__cta-note {
  font-family: var(--sans);
  font-size: 12px;
  font-weight: 400;
  letter-spacing: 0.01em;
  /* Same tone as the lock — they read as one quiet group next to the
     prominent button. */
  color: rgba(216, 226, 236, 0.65);
  line-height: 1.35;
  margin: 0;
}

.chapter__media {
  position: relative;
  z-index: 3;
  margin: 0 -72px;
  width: calc(100% + 144px);
  overflow: hidden;
  /* Absorb remaining height in the flex column so the card always
     fits exactly one viewfold. The image inside uses object-fit:cover
     to crop gracefully when this slot is shorter than the image's
     natural aspect would render. */
  flex: 1 1 0;
  /* `min-height: 0` is required in flex children to allow them to
     shrink below their natural size — without it, the image content
     would force the chapter to grow past its container. */
  min-height: 0;
}
.chapter__media img,
.chapter__media video {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
  /* Center the image so cropping eats top/bottom evenly when the slot
     is shorter than the image's natural rendered height — keeps the
     central subject (AI Mode badge / silhouettes) framed. */
  object-position: center;
}

/* "Under construction" state — replaces the old hatched placeholder.
   Used on chapter__media when no real visual exists yet (chapters 3 & 4).
   Per the mock: italic, dim, centered in the leftover flex slot. No
   min-height here — the slot already gets whatever space remains
   inside the viewport-bound card. */
.chapter__media--empty {
  display: flex;
  align-items: center;
  justify-content: center;
}

.chapter__construction {
  /* Italic dim text per mock — reads as "this slot is reserved" rather
     than as content. */
  font-family: var(--display);
  font-style: italic;
  font-weight: 300;
  font-size: clamp(1.4rem, 2.2vw, 2rem);
  letter-spacing: -0.005em;
  color: rgba(255, 255, 255, 0.55);
  margin: 0;
}

.chapter [data-split="words"] .word-inner {
  display: inline-block;
  opacity: 0;
  transform: translateY(0.6em);
}

@media (max-width: 900px) {
  .work { padding: 0 0 120px; }
  .work-intro-panel { min-height: 560px; }
  .chapter { padding: 40px 20px; perspective: none; }
  .chapter__card { padding: 40px 24px 0; }
  .chapter__head {
    grid-template-columns: 1fr;
    gap: 32px;
    margin-bottom: 40px;
  }
  .chapter__col--copy { padding-top: 0; gap: 24px; }
  .chapter__title { max-width: none; }
  .chapter__media {
    margin: 0 -24px;
    width: calc(100% + 48px);
  }
  .chapter__media--empty { min-height: 280px; }
}

@media (prefers-reduced-motion: reduce) {
  .chapter [data-split="words"] .word-inner {
    opacity: 1 !important;
    transform: none !important;
  }
  .chapter__card { transform: none !important; }
}

/* ========= CONTACT SECTION =========
   Single 100vh snap target. Layout per Sid's "Hit me up" mock:
     - Hero: portrait card on the left, content stack on the right
       (title, bio paragraph, links row).
     - Wordmark "sydrth.*" centered horizontally below, sized smaller
       than before per mock — about 80% of viewport width, bleeding
       off the bottom edge but not the sides. */
.contact {
  position: relative;
  height: 100vh;
  min-height: 720px;
  scroll-snap-align: start;
  /* Two-region grid: hero (intro+portrait), wordmark below */
  display: grid;
  grid-template-rows: 1fr auto;
  padding: 80px 64px 0;
  overflow: hidden;
  z-index: 1;
}

/* --- Hero row: portrait card on LEFT, copy on RIGHT --- */
.contact__hero {
  display: grid;
  /* Both columns now have intrinsic, bounded widths. Previously the
     copy column was minmax(0, 1fr) which expanded to consume all
     available space, pushing the visual center off to the left because
     the portrait stayed pinned to its 360px width while the copy
     stretched to the right viewport edge. With both columns bounded,
     the grid forms a real block that auto-centers via margin: 0 auto. */
  grid-template-columns: minmax(280px, 360px) minmax(420px, 640px);
  gap: clamp(48px, 6vw, 96px);
  align-items: center;
  /* justify-content centers the grid track within .contact__hero's
     own width — belt-and-braces alongside the auto margins below. */
  justify-content: center;
  position: relative;
  z-index: 2;
  align-self: center;
  width: 100%;
  /* Width auto-derives from the columns + gap; max-width keeps it
     sane on ultrawide screens. */
  max-width: 1180px;
  margin: 0 auto;
}

/* --- Portrait card --- the new PNG ships with its own rounded mask and
   ambient glow already baked in, so the CSS frame is intentionally
   stripped: no border, no inset highlight, no shadow, no background
   tint, no padding, no border-radius, no edge feathering. Container is
   a pure transparent slot — the image renders exactly as exported. */
.contact__portrait {
  position: relative;
  /* 3:4 aspect (matches the source image) */
  aspect-ratio: 3 / 4;
  background: transparent;
  border: 0;
  box-shadow: none;
  padding: 0;
  border-radius: 0;
  overflow: visible;
}

.contact__portrait img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: contain;
  border-radius: 0;
  -webkit-mask-image: none;
          mask-image: none;
}

/* --- Right column: title, paragraph, links --- */
.contact__copy {
  display: flex;
  flex-direction: column;
  gap: 24px;
  /* Tight max-width so paragraph wraps similar to mock */
  max-width: 64ch;
}

.contact__title {
  font-family: var(--display);
  font-weight: 300;
  font-size: clamp(2.4rem, 4vw, 3.6rem);
  line-height: 1;
  letter-spacing: -0.015em;
  color: var(--white);
  margin: 0;
}

.contact__paragraph {
  font-family: var(--sans);
  font-size: clamp(0.95rem, 1.05vw, 1.05rem);
  line-height: 1.55;
  font-weight: 400;
  color: rgba(216, 226, 236, 0.78);
  margin: 0;
}

/* Links: row, evenly distributed across the column */
.contact__links {
  display: flex;
  align-items: center;
  gap: clamp(28px, 4vw, 64px);
  margin-top: 8px;
}

.contact__link {
  font-family: var(--sans);
  font-size: clamp(0.95rem, 1.1vw, 1.1rem);
  font-weight: 600;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--white);
  text-decoration: none;
  position: relative;
  padding: 4px 0;
  transition: color 0.25s var(--ease-out-soft);
  white-space: nowrap;
}

.contact__link::after {
  content: '';
  position: absolute;
  left: 0; right: 0; bottom: 0;
  height: 1px;
  background: currentColor;
  transform: scaleX(0);
  transform-origin: left center;
  transition: transform 0.35s var(--ease-out-soft);
}

.contact__link:hover { color: rgb(184, 211, 230); }
.contact__link:hover::after { transform: scaleX(1); }

/* --- Wordmark: centered, sized per mock (NOT a giant bleed) --- */
.contact__wordmark {
  position: relative;
  display: flex;
  justify-content: center;
  align-items: flex-end;
  /* Pull slightly past the bottom edge so the period is clipped a touch,
     matching the mock where the descenders just kiss the edge. */
  margin-bottom: -4vh;
  pointer-events: none;
  z-index: 0;
}

.contact__wordmark img {
  display: block;
  /* Per Sid: keep at high quality; don't down-scale aggressively.
     Width capped so the wordmark sits as a centered block in the
     viewport (bounded + auto margins via the flex parent). The cap
     is large enough that on most screens the wordmark visually
     reads as "fills the bottom of the page" while still leaving
     small margins of gradient at the left and right edges. */
  width: min(94vw, 1600px);
  max-width: 100%;
  height: auto;
  margin: 0 auto;
  user-select: none;
  -webkit-user-drag: none;
}

/* ========= RESPONSIVE ========= */
@media (max-width: 900px) {
  .wordmark { left: 20px; top: 20px; }
  .pill-nav { right: 20px; top: 20px; padding: 4px; }
  .pill-nav__link, .pill-nav__cta { padding: 8px 12px; font-size: 12px; }
  .pill-nav__items { gap: 0; }

  .stage__layer--a {
    grid-template-columns: 1fr;
    padding: 0 20px 40px;
    align-items: end;
  }
  .glass-card { min-width: auto; padding: 20px 24px; align-self: start; justify-self: end; }
  .stage__layer--b {
    grid-template-columns: 1fr;
    gap: 32px;
    padding: 0 20px;
  }

  .work-intro { padding: 140px 20px 80px; }

  .case { min-height: auto; }
  .case__preview, .case--reverse .case__preview { padding: 0; min-height: 60vh; }
  .case__preview > video, .case__preview > img, .case__preview > .placeholder { height: 60vh; }
  .case__panel, .case--reverse .case__panel {
    position: relative; width: 100%; left: auto; right: auto;
    padding: 60px 20px;
    border: none;
    border-bottom: 1px solid rgba(212, 217, 223, 0.06);
  }
  .case__panel-inner { max-width: none; }

  .footer { padding: 100px 20px 30px; }
  .footer__row--wide { flex-direction: column; align-items: flex-start; }
  .footer__fine { flex-direction: column; gap: 8px; }
}

/* ========= REDUCED MOTION =========
   Always last — these rules need highest specificity for accessibility. */

/* ===== MOBILE — iPhone 17 / phones ≤ 480px =====
   This block targets phones only. The earlier @media (max-width: 900px)
   block above catches tablets but is mostly stale code; this block
   overrides it where the mocks specify mobile-specific layout. Desktop
   (above 900px) is unaffected.

   Per Sid's mocks (iPhone 17 = 402×874pt) and explicit decisions:
     - Hide pill nav entirely on mobile.
     - Wordmark bigger top-left.
     - Stage Layer A: full-bleed video, title left-aligned mid-viewport,
       glass card pinned bottom-right (smaller footprint).
     - Stage Layer B: title + body left-aligned, single column, no figure.
     - Scroll hint: keep but smaller.
     - My Work: copy already matches mock; curves stay (no change needed).
     - Chapter cards: single-column (already in 900px block); CTA aside
       wraps below button at narrow widths via existing flex-wrap rule.
     - Contact: portrait stacks ABOVE the copy, everything centered,
       RESUME link hidden via .contact__link--desktop-only. */
@media (max-width: 480px) {

  /* ----- Hide pill nav on mobile ----- */
  .pill-nav { display: none !important; }

  /* ----- Wordmark: scale up significantly per mock 1/2 ----- */
  .wordmark {
    left: 20px;
    top: 20px;
    /* Override the desktop scale variable so it reads as a strong
       brand mark in the corner rather than a small badge. */
    --wordmark-scale: 1;
  }
  .wordmark__svg {
    /* Mock shows "sydrth.*" at roughly 200px wide on a 402px viewport.
       Scaling up the SVG container does this without touching the
       desktop sizing logic. */
    width: clamp(180px, 52vw, 220px);
    height: auto;
  }

  /* ----- Stage section: keep full-bleed video, no padding tweaks
           needed at the section level. The layers handle layout. ----- */

  /* ----- Layer A: "Human-first. AI-second." + glass card -----
     Mock 1: title left-aligned in lower-middle, glass card bottom-right
     as a compact tile. */
  .stage__layer--a {
    /* Was grid 1fr auto bottom-aligned. Now: full-viewport relative
       container so we can absolutely position the title and card. */
    display: block;
    padding: 0;
  }
  .hero-a__title {
    position: absolute;
    left: 20px;
    /* Roughly mid-viewport vertically per mock. */
    top: 48%;
    transform: translateY(-50%);
    font-size: clamp(2.4rem, 11vw, 3.4rem);
    line-height: 1.0;
    max-width: 86vw;
  }
  .glass-card {
    position: absolute;
    right: 20px;
    bottom: 32px;
    /* Compact tile per mock 1 — much smaller than desktop's 320px min. */
    min-width: 0;
    padding: 18px 22px;
    /* Constrain so title wraps to two lines like the mock: "LEAD UX /
       DESIGNER" stacked. */
    max-width: 200px;
  }
  .glass-card__title {
    font-size: 1.15rem;
    line-height: 1.1;
  }
  .glass-card__sub {
    margin-top: 8px;
    font-size: 0.78rem;
  }

  /* ----- Layer B: "Hello, I am Siddharth." + bio -----
     Mock 2: title + body both left-aligned, vertically centered-ish,
     single column. The desktop figure column is empty here anyway. */
  .stage__layer--b {
    display: block;
    padding: 0;
  }
  .stage__layer--b .hero-b {
    position: absolute;
    left: 20px;
    right: 20px;
    /* Vertically a bit above center per mock — title takes upper half,
       body sits directly under it. */
    top: 50%;
    transform: translateY(-50%);
    max-width: none;
  }
  .hero-b__title {
    font-size: clamp(2.4rem, 11vw, 3.4rem);
    line-height: 1.0;
    max-width: 86vw;
  }
  .hero-b__intro {
    font-size: 0.95rem;
    line-height: 1.45;
    max-width: 86vw;
    margin-top: 20px;
  }

  /* ----- Scroll hint: keep, smaller per Sid's spec ----- */
  .stage__scroll-hint {
    bottom: 28px;
    gap: 10px;
  }
  .stage__scroll-hint-mouse {
    width: 22px;
    height: 36px;
    border-width: 1.25px;
  }
  .stage__scroll-hint-dot {
    width: 3px;
    height: 3px;
    top: 7px;
  }
  .stage__scroll-hint-label {
    font-size: 9px;
    letter-spacing: 0.28em;
  }

  /* ----- Chapter cards: refine the existing 900px single-column
           layout for iPhone-narrow widths. The flex-wrap on
           .chapter__cta-group already handles the lock+microcopy
           wrapping below the button when there's no horizontal room. ----- */
  .chapter { padding: 32px 16px; }
  .chapter__card { padding: 32px 20px 0; }
  .chapter__head { gap: 20px; margin-bottom: 28px; }
  .chapter__title { font-size: clamp(2rem, 8vw, 2.6rem); }
  .chapter__body { font-size: 0.95rem; line-height: 1.5; }
  /* Media zone inset matches the new 20px card side padding so it
     bleeds edge-to-edge on the card. */
  .chapter__media {
    margin: 0 -20px;
    width: calc(100% + 40px);
  }

  /* ----- Contact section -----
     Mock 5: portrait stacked ABOVE the copy, everything centered,
     wordmark bleeds off bottom edges. */
  .contact {
    padding: 40px 20px 0;
    /* Allow it to grow naturally with stacked content rather than
       fighting fixed 100vh on a phone. */
    height: auto;
    min-height: 100vh;
  }
  .contact__hero {
    /* Stack: portrait first, then copy. */
    grid-template-columns: 1fr;
    gap: 36px;
    max-width: none;
    justify-content: stretch;
  }
  .contact__portrait {
    /* Slightly narrower than full-width so it has visual margin on
       the sides like the mock. */
    width: 80%;
    max-width: 320px;
    aspect-ratio: 3 / 4;
    justify-self: center;
  }
  .contact__copy {
    /* Mock shows everything centered on mobile. */
    align-items: center;
    text-align: center;
  }
  .contact__title {
    font-size: clamp(2rem, 9vw, 2.6rem);
    text-align: center;
  }
  .contact__paragraph {
    text-align: center;
    font-size: 0.95rem;
  }
  .contact__links {
    /* Three links spread evenly with smaller gap than desktop. */
    justify-content: center;
    gap: clamp(20px, 5vw, 36px);
    flex-wrap: wrap;
  }
  /* Hide RESUME link on mobile per Sid: only EMAIL · INSTAGRAM ·
     LINKEDIN show, no resume CTA. */
  .contact__link--desktop-only {
    display: none;
  }
  /* Mock 5 link order on mobile: EMAIL · INSTAGRAM · LINKEDIN.
     Markup keeps desktop order (EMAIL · LINKEDIN · RESUME · INSTAGRAM);
     using flex `order` here just shuffles Instagram to slot 2 on mobile
     without rearranging the HTML, so desktop stays untouched. */
  .contact__links { display: flex; }
  .contact__link--instagram { order: -1; }   /* before LinkedIn */
  /* Email needs to stay first — give it a smaller order than Instagram. */
  .contact__link[href^="mailto"] { order: -2; }

  /* ----- Wordmark at footer: per mock, bleeds off the bottom edges
           (i.e. larger than viewport width so left/right glyphs are
           clipped slightly by the screen edges). ----- */
  .contact__wordmark img {
    /* Larger than viewport so it bleeds off edges per mock 5. */
    width: 110%;
    max-width: 110%;
    margin-left: -5%;
  }
}

/* ========= REDUCED MOTION ========= */
@media (prefers-reduced-motion: reduce) {
  .stage { height: auto; }
  .stage__sticky { position: relative; height: auto; min-height: 100vh; }
  .stage__video { filter: none !important; transform: none !important; }
  .stage__layer { position: relative; opacity: 1 !important; padding: 80px 40px; }
  [data-split="words"] .word-inner { transform: none !important; opacity: 1 !important; }
  .wordmark { position: relative; top: auto; left: auto; }
}
