/* Google Fonts are loaded from <head> in index.html, not @import'ed here — an
   @import serialises behind this stylesheet's own download and parse, putting
   the font files four round-trips into the critical path. */

/* ==========================================================================
   Stacking order
   ---------------------------------------------------------------------------
   One ordered scale for everything that participates in the *page-level* stack,
   replacing hand-escalated values that had reached 999999. Listed low to high;
   the gaps leave room to insert a layer without renumbering.

   Values are deliberately small: a z-index only competes within its stacking
   context, so component-internal values (0–10 on .nav-cta, .hero-cta, the
   contact-modal parts, etc.) are already scoped and are left alone.

   The header intentionally has no z-index — it stacks by document order, which
   is what puts the mobile menu and contact modal above it.

   Breakpoints, for reference, since they cannot be tokenised in media queries:
     575.98 / 767.98 / 991.98 / 1199.98  max-width tiers
     576 / 768 / 992 / 1025              matching min-width tiers
   Prefer these over introducing new ones.
   ========================================================================== */
:root {
  --z-behind: -1;   /* decorative layers painted under content */
  --z-content: 2;   /* main flow */
  --z-header: 100;  /* fixed site header */
  --z-menu: 200;    /* full-screen mobile menu */
  --z-ripple: 300;  /* click ripple particles */
  --z-grain: 400;   /* full-viewport texture overlay */
  --z-modal: 500;   /* contact modal shutter */
  --z-cursor: 600;  /* custom cursor — always on top */

  /* Scoped inside the header's own stacking context, so this number is NOT
     competing with --z-menu despite sharing its value. The header carries
     --z-header, which establishes the context; the dropdown only has to
     outrank the header's other children. Named separately so nobody
     "fixes" the apparent collision by inflating it. */
  --z-dropdown: 200;
}

/* Design Tokens & Variables */
:root {
  --bg-primary: #fff3eb;
  --bg: #fff3eb;
  --bg-secondary: #faece1;
  --accent: #f8853d;
  --accent-rgb: 248, 133, 61;
  --accent-dim: rgba(248, 133, 61, 0.08);
  --text-primary: #1d1c1c;
  --text-secondary: #555555;
  --text-muted: #888888;
  --border-color: rgba(29, 28, 28, 0.08);
  --font-sans: 'Onest', sans-serif;
  --font-serif: 'Instrument Serif', serif;
  
  /* ── Motion tokens ──────────────────────────────────────────────────────
     One vocabulary for the whole site. Before this the stylesheets carried
     16 distinct easing curves, 23 travel distances and 16 durations — every
     section spoke its own dialect, which is what read as "not uniform" far
     more than any missing animation did.

     The three curves kept are the three that were already most used, so the
     majority of the site is unchanged and only the outliers snap into line.
     Anything entering or leaving uses --ease-out; anything moving or wiping
     on screen uses --ease-in-out; hovers and colour use --ease-state.

     Do not introduce a fourth curve or a fifth duration without a reason you
     can state. Every value here should be defensible. */
  --ease-out: cubic-bezier(0.16, 1, 0.3, 1);
  --ease-in-out: cubic-bezier(0.65, 0, 0.35, 1);
  --ease-state: cubic-bezier(0.23, 1, 0.32, 1);

  --dur-micro: 160ms;   /* press feedback */
  --dur-fast: 250ms;    /* hover, small state change */
  --dur-base: 450ms;    /* standard entrance */
  --dur-slow: 700ms;    /* large panels, media wipes */

  --rise-sm: 8px;       /* inline text, list items */
  --rise-md: 16px;      /* headings, paragraphs, blocks */
  --rise-lg: 24px;      /* cards, tiles, panels — nothing travels further */

  --stagger: 70ms;      /* one step, everywhere. Capped at 6 (420ms total) */

  --cursor-size: 8px;
  --cursor-follower-size: 40px;
  --transition-smooth: color 0.5s var(--ease-out), background-color 0.5s var(--ease-out), border-color 0.5s var(--ease-out), opacity 0.5s var(--ease-out), transform 0.5s var(--ease-out);
  --transition-fast: color 0.25s var(--ease-out), background-color 0.25s var(--ease-out), border-color 0.25s var(--ease-out), opacity 0.25s var(--ease-out), transform 0.25s var(--ease-out);
  --ease: var(--ease-out);
}

/* Global Selection Styles */
::selection {
  background-color: var(--accent);
  color: #fff3eb;
}
::-moz-selection {
  background-color: var(--accent);
  color: #fff3eb;
}

/* Reset & Base Styles */
*, *::before, *::after {
  box-sizing: border-box;
  margin: 0;
  padding: 0;
}

html {
  scroll-behavior: auto; /* Managed by Lenis */
  overflow-x: hidden;
  background: #1d1c1c;
}

body {
  font-family: var(--font-sans);
  background: #1d1c1c;
  color: var(--text-primary);
  line-height: 1.6;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  overflow-x: hidden;
  cursor: none; /* Hide default cursor to use custom cursor */
}

/* NOTE for future work: `overflow-x: hidden` above makes html and body scroll
   containers, and a scroll container is what `position: sticky` resolves
   against — so sticky silently does nothing anywhere on this site. Switching
   both to `overflow-x: clip` fixes it (clips identically, creates no scroll
   container). Not applied here because nothing currently needs sticky, and it
   is a global change that deserves its own test run. */

/* Animated breathing background layer (using pseudo-element so body doesn't trap position: fixed in Chrome) */
body::after {
  content: "";
  position: fixed;
  inset: 0;
  z-index: var(--z-behind);
  pointer-events: none;
  background: linear-gradient(-45deg, #f8853d, #1d1c1c, #767369, #f8853d, #1d1c1c);
  background-size: 400% 400%;
  animation: appleGlow 12s ease infinite;
}

/* Ensure main content sections retain clean background over the animated body gradient */
main, .footer-section {
  background-color: var(--bg-primary);
  position: relative;
  z-index: var(--z-content);
}

/* Apple Intelligence Breathing Background Animation */
@keyframes appleGlow {
  0% {
    background-position: 0% 50%;
  }
  50% {
    background-position: 100% 50%;
  }
  100% {
    background-position: 0% 50%;
  }
}


/* Paper Grain Overlay via CSS */
body::before {
  content: "";
  position: fixed;
  top: 0;
  left: 0;
  width: 100vw;
  height: 100vh;  /* fallback for engines without dvh */
  height: 100dvh;
  z-index: var(--z-grain);
  pointer-events: none;
  opacity: 0.035; /* subtle paper grain overlay */
  background-image: url("data:image/svg+xml,%3Csvg viewBox='0 0 200 200' xmlns='http://www.w3.org/2000/svg'%3E%3Cfilter id='noiseFilter'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.8' numOctaves='3' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23noiseFilter)'/%3E%3C/svg%3E");
}

/* Hide default cursor on interactive items too */
body, a, button, select, input, textarea, [role="button"], [data-cursor], .chat-option-btn, .accordion-header {
  cursor: none !important;
}

/* Blanket hide wherever the custom cursor is actually rendered. The explicit
   list above misses anything carrying its own `cursor: pointer` (cards, the
   accordion, the chat widget), which is why the system arrow used to surface
   on top of the SVG one. Scoped to the same conditions the custom cursor is
   shown under — >=992px, fine pointer, and not the contact page (which opts
   back into the native cursor). */
@media (min-width: 992px) and (pointer: fine) {
  html:has(body:not(.contact-body)),
  body:not(.contact-body),
  body:not(.contact-body) *,
  body:not(.contact-body) *::before,
  body:not(.contact-body) *::after {
    cursor: none !important;
  }
}

/* Custom Scrollbar */
::-webkit-scrollbar {
  width: 6px;
}
::-webkit-scrollbar-track {
  background: var(--bg-primary);
}
::-webkit-scrollbar-thumb {
  background: var(--text-muted);
  border-radius: 3px;
}
::-webkit-scrollbar-thumb:hover {
  background: var(--text-secondary);
}

/* ==========================================================================
   Custom Cursor — High-resolution SVG arrow (single object, zero lag).
   The tip of the arrow (0,0 in SVG space) sits exactly at the mouse pointer.
   ========================================================================== */
.custom-cursor {
  width: 22px;
  height: 26px;
  position: fixed;
  top: 0;
  left: 0;
  pointer-events: none;
  z-index: var(--z-cursor);
  /* Position lives on `translate`, an independent property from `transform`.
     The hover/press states below own `transform` (scale) exclusively, so the
     transition can ease the scale without ever easing the pointer position —
     previously both shared `transform`, which ran every mouse move through a
     180ms curve and read as lag. */
  translate: 0 0;
  will-change: translate, transform;
  backface-visibility: hidden;
  /* High-res inline SVG (24×28 viewBox) — filled charcoal arrow.
     Rendered via mask so we can recolor via background-color for hover states. */
  background-color: #0d0d0d;
  -webkit-mask-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 28'><path d='M2 2 L2 22.5 L7.5 17.5 L11 25 L14.5 23.5 L11 16 L18.5 16 Z'/></svg>");
          mask-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 28'><path d='M2 2 L2 22.5 L7.5 17.5 L11 25 L14.5 23.5 L11 16 L18.5 16 Z'/></svg>");
  -webkit-mask-repeat: no-repeat;
          mask-repeat: no-repeat;
  -webkit-mask-size: contain;
          mask-size: contain;
  /* Soft shadow for lift on light backgrounds */
  filter: drop-shadow(0 2px 4px rgba(0, 0, 0, 0.18));
  /* Scale from the arrow tip, not the box centre, so hover states don't nudge
     the hot spot off the real pointer. */
  transform-origin: 0 0;
  transition: transform 0.18s var(--ease-out),
              background-color 0.25s ease;
}

/* Hover states: subtle bounce + accent color on interactives */
body.cursor-hovering .custom-cursor {
  transform: scale(1.25);
  background-color: var(--accent);
}

body.cursor-dragging .custom-cursor {
  transform: scale(0.85);
}

/* Hand cursor variant — pointing finger for CTAs.
   ═══════════════════════════════════════════════════════════════════════════
   REUSABLE PATTERN: To make ANY element trigger the orange hand cursor on
   hover, add the attribute:  data-cursor="hand"
   Example:  <a class="my-button" data-cursor="hand">Book a call</a>
   The `.hero-cta` and `.nav-cta` selectors are also included by default.
   ═══════════════════════════════════════════════════════════════════════════
   Larger footprint (30×34), tip of the index finger sits at the mouse position. */
body.cursor-hand .custom-cursor {
  width: 30px;
  height: 34px;
  background-color: var(--accent); /* Brand orange #f8853d */
  transform: scale(1);
  filter: drop-shadow(0 3px 8px rgba(248, 133, 61, 0.45));
  -webkit-mask-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 72 90'><path d='M34.48 45V4.73C34.48 2.23 32.27 0 29.29 0c-2.98 0-5.19 2.23-5.19 4.73V53.49c0 .54-.27 1.05-.72 1.35-.45.3-1.02.36-1.52.16L8.17 47.68C3.30 45.70-2.27 51.34.96 55.19c3.25 3.86 13 14.75 22.88 25.71 11.94 13.24 33.18 11.82 43.30-2.85 3.16-4.59 4.86-10.02 4.86-15.59V33.88c0-2.21-1.96-4.20-4.62-4.20-2.67 0-4.63 1.99-4.63 4.20v11.13c0 .90-.73 1.63-1.62 1.63-.90 0-1.63-.73-1.63-1.63V29.64c0-2.21-1.96-4.20-4.63-4.20-2.66 0-4.62 1.99-4.62 4.20v15.37c0 .90-.73 1.63-1.63 1.63-.90 0-1.63-.73-1.63-1.63V25.40c0-2.21-1.96-4.20-4.62-4.20-2.66 0-4.63 1.99-4.63 4.20V45c0 .90-.73 1.63-1.63 1.63-.90 0-1.63-.72-1.63-1.62z'/></svg>");
          mask-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 72 90'><path d='M34.48 45V4.73C34.48 2.23 32.27 0 29.29 0c-2.98 0-5.19 2.23-5.19 4.73V53.49c0 .54-.27 1.05-.72 1.35-.45.3-1.02.36-1.52.16L8.17 47.68C3.30 45.70-2.27 51.34.96 55.19c3.25 3.86 13 14.75 22.88 25.71 11.94 13.24 33.18 11.82 43.30-2.85 3.16-4.59 4.86-10.02 4.86-15.59V33.88c0-2.21-1.96-4.20-4.62-4.20-2.67 0-4.63 1.99-4.63 4.20v11.13c0 .90-.73 1.63-1.62 1.63-.90 0-1.63-.73-1.63-1.63V29.64c0-2.21-1.96-4.20-4.63-4.20-2.66 0-4.62 1.99-4.62 4.20v15.37c0 .90-.73 1.63-1.63 1.63-.90 0-1.63-.73-1.63-1.63V25.40c0-2.21-1.96-4.20-4.62-4.20-2.66 0-4.63 1.99-4.63 4.20V45c0 .90-.73 1.63-1.63 1.63-.90 0-1.63-.72-1.63-1.62z'/></svg>");
}

/* When hovering a CTA, the generic .cursor-hovering scale should not apply on top of hand */
body.cursor-hand.cursor-hovering .custom-cursor {
  transform: scale(1);
  background-color: var(--accent);
}

/* Dark hand variant — for orange backgrounds where the orange hand would be invisible */
body.cursor-hand.cursor-hand--dark .custom-cursor,
body.cursor-hand.cursor-hand--dark.cursor-hovering .custom-cursor {
  background-color: #0d0d0d;
  filter: drop-shadow(0 3px 8px rgba(0, 0, 0, 0.35));
}

/* Light hand variant — for orange elements on dark backgrounds (modal CTAs) */
body.cursor-hand.cursor-hand--light .custom-cursor,
body.cursor-hand.cursor-hand--light.cursor-hovering .custom-cursor {
  background-color: #fff3eb;
  filter: drop-shadow(0 3px 8px rgba(0, 0, 0, 0.35));
}

/* Outro context — while the pointer is anywhere inside the black outro section
   the default arrow shifts to orange for contrast against the dark bg. The
   hand variants (dark for the CTA, light where appropriate) still override. */
/* .cursor-ctx-dark is the generic form of the same idea — any charcoal surface
   that wants the orange arrow. Added for the filled process cards; the outro
   keeps its own class so its pointer-driven marquee logic stays untouched. */
body.outro-cursor-ctx .custom-cursor,
body.cursor-ctx-dark .custom-cursor {
  background-color: var(--accent);
  filter: drop-shadow(0 2px 6px rgba(248, 133, 61, 0.4));
}
body.outro-cursor-ctx.cursor-hand.cursor-hand--dark .custom-cursor,
body.cursor-ctx-dark.cursor-hand.cursor-hand--dark .custom-cursor {
  background-color: #0d0d0d;
  filter: drop-shadow(0 3px 8px rgba(0, 0, 0, 0.5));
}

/* Legacy follower ring & rotating stamp badges are retired */
.custom-cursor-follower,
#cur,
.cur {
  display: none !important;
}

/* Ripple effect container — uses scale instead of width/height to avoid layout reflow */
.ripple-effect {
  position: fixed;
  width: 80px;
  height: 80px;
  border: 1px solid var(--accent);
  border-radius: 50%;
  pointer-events: none;
  will-change: transform, opacity;
  animation: ripple-animation 0.6s var(--ease-out) forwards;
  z-index: var(--z-ripple);
}

@keyframes ripple-animation {
  0%   { transform: translate(-50%, -50%) scale(0); opacity: 0.8; }
  100% { transform: translate(-50%, -50%) scale(1);   opacity: 0; }
}

/* Global Grid System */
.grid-container {
  display: grid;
  grid-template-columns: repeat(12, 1fr);
  gap: 30px;
  max-width: 1400px;
  margin: 0 auto;
  padding: 0 60px;
  width: 100%;
  position: relative;
  z-index: 2;
}

@media (max-width: 1024px) {
  .grid-container {
    padding: 0 30px;
    gap: 20px;
  }
}

@media (max-width: 768px) {
  .grid-container {
    grid-template-columns: repeat(4, 1fr);
    padding: 0 20px;
  }
}

/* Typography elements */
h1, h2, h3, h4 {
  font-weight: 500;
  letter-spacing: -0.02em;
}

span.serif-italic {
  font-family: var(--font-serif);
  font-style: italic;
  font-weight: 400;
  text-transform: none;
}

/* Section Common Styles */
section {
  padding: 100px 0;
  position: relative;
  overflow: hidden;
}

.section-label {
  font-size: 11px;
  text-transform: uppercase;
  color: var(--accent);
  letter-spacing: 0.15em;
  font-weight: 600;
  margin-bottom: 30px;
  display: inline-flex;
  align-items: center;
}

.section-label::before {
  content: '';
  display: inline-block;
  width: 6px;
  height: 6px;
  background: var(--accent);
  border-radius: 50%;
  margin-right: 10px;
  box-shadow: 0 0 10px var(--accent);
}

.section-label-centered {
  font-size: 11px;
  text-transform: uppercase;
  color: var(--accent);
  letter-spacing: 0.15em;
  font-weight: 600;
  margin-bottom: 30px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 100%;
}

.section-label-centered::before {
  content: '';
  display: inline-block;
  width: 6px;
  height: 6px;
  background: var(--accent);
  border-radius: 50%;
  margin-right: 10px;
  box-shadow: 0 0 10px var(--accent);
}

/* ==========================================================================
   Header & Thread Navigation
   ========================================================================== */
.thread-nav-container,
.thread-host {
  position: fixed;
  top: 0;
  left: 0;
  width: 100%;
  height: 80px; /* mobile height (unchanged); desktop/tablet raised to 96px below */
  z-index: var(--z-header);
  display: flex;
  align-items: center;
  background: transparent;
  border-bottom: none;
  will-change: transform;
  transform: translateZ(0);
}

@media (min-width: 768px) {
  .thread-nav-container,
  .thread-host {
    height: 120px;           /* taller, airier header (was 96px) */
    padding-top: 20px;       /* breathing space above the nav content */
    align-items: flex-start; /* nav content sits just below that top gap */
  }
}

.thread-nav-container::before,
.thread-host::before {
  content: "";
  position: absolute;
  inset: 0;
  /* Fade from warm background color to transparent */
  background: linear-gradient(180deg, 
    var(--bg, #fff3eb) 0%, 
    rgba(255, 243, 235, 0.98) 40%, 
    rgba(255, 243, 235, 0.85) 70%, 
    rgba(255, 243, 235, 0) 100%
  );
  backdrop-filter: blur(10px);
  -webkit-backdrop-filter: blur(10px);
  pointer-events: none;
  z-index: -1;
}

.thread-nav-wrapper {
  width: 100%;
  max-width: 1400px;
  margin: 0 auto;
  padding: 0 60px;
  display: flex;
  align-items: center;
  gap: clamp(20px, 2.5vw, 40px);
  position: relative;
}

@media (max-width: 1024px) {
  .thread-nav-wrapper {
    padding: 0 30px;
  }
}

@media (max-width: 768px) {
  .thread-nav-wrapper {
    padding: 0 20px;
  }
}

.thread-nav-logo {
  flex-shrink: 0;
  z-index: 10;
  position: relative;
}

/* Desktop only: outdent the logo toward the page edge so it reads as the
   primary anchor, while the wrapper keeps its consistent 60px page padding
   (tablet/mobile keep their tighter padding untouched, so no edge collision). */
@media (min-width: 1025px) {
  .thread-nav-logo {
    margin-left: -28px;
  }
}

.nav-logo-link {
  font-family: var(--font-sans);
  font-weight: 700;
  font-size: clamp(16px, 1.5vw, 20px);
  text-transform: uppercase;
  color: var(--text-primary);
  text-decoration: none;
  letter-spacing: -0.01em;
  display: flex;
  align-items: center;
}

/* Text wordmark is no longer used — the SVG brand mark is the single logo
   at every breakpoint. Kept in the HTML for a11y fallback only. */
.nav-logo-text {
  display: none;
}

.nav-logo-img {
  display: block;
  /* Desktop/tablet header is 120px tall (20px top gap); ~92px logo is the
     primary brand anchor. Asset is cropped to its artwork bounds so this
     height is all logo, aspect ratio preserved. */
  height: 92px;
  width: auto;
  object-fit: contain;
}

/* Scale the SVG logo down on smaller breakpoints (Bootstrap 5 lg/sm) */
@media (max-width: 991.98px) {
  .nav-logo-img { height: 60px; }
}
@media (max-width: 575.98px) {
  .nav-logo-img { height: 52px; }
}

/* ── Primary nav links: single clean row, pushed to the right of the logo.
   Editorial serif-italic type is kept from the original identity; the
   numeric prefixes and thread wire are gone for a Clay-style airy bar. ── */
.thread-nav-links {
  display: none; /* mobile: hidden, hamburger takes over. Revealed >=768px. */
  align-items: center;
  gap: clamp(24px, 2.4vw, 46px); /* generous spacing between items */
  margin-left: auto; /* push the link group (and CTA) to the right */
}

.nav-link {
  font-family: var(--font-sans);
  font-style: normal;
  font-size: clamp(13px, 0.95vw, 15px);
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--text-primary);
  text-decoration: none;
  white-space: nowrap;
  position: relative;
  padding: 10px 18px;
  border-radius: 999px;
  background-color: transparent;
  transition: background-color 0.35s var(--ease), color 0.35s var(--ease), transform 0.35s var(--ease);
  cursor: none;
}

.nav-link:hover {
  background-color: #0d0d0d;
  color: #fff3eb;
}

/* ── Primary CTA (far right) — matches the hero pill button language ── */
.nav-cta {
  display: none; /* mobile: hidden. Revealed >=768px. */
  align-items: center;
  gap: 12px;
  flex-shrink: 0;
  background-color: #1d1c1c;
  color: #fff3eb;
  padding: 10px 12px 10px 22px;
  border-radius: 40px;
  font-family: var(--font-sans);
  font-size: 14px;
  font-weight: 600;
  text-decoration: none;
  white-space: nowrap;
  box-shadow: 0 4px 15px rgba(0, 0, 0, 0.1);
  transition: transform 0.3s var(--ease), background-color 0.3s;
  cursor: none;
  position: relative;
  overflow: hidden;
}

/* Mouse-tracking radial glow (same treatment as .hero-cta) */
.nav-cta::before {
  content: "";
  position: absolute;
  inset: 0;
  background: radial-gradient(circle 120px at var(--x, 50%) var(--y, 50%), rgba(255, 107, 0, 0.45), transparent 70%);
  opacity: 0;
  transition: opacity 0.4s var(--ease);
  pointer-events: none;
  z-index: 0;
}

.nav-cta:hover::before {
  opacity: 1;
}

.nav-cta:hover {
  transform: translateY(-2px);
  background-color: #0c0b0b;
}

.nav-cta > span {
  position: relative;
  z-index: 1;
}

/* Desktop only (≥992px, Bootstrap lg): reveal the full nav (links + CTA).
   Below lg we collapse everything into the hamburger overlay. */
@media (min-width: 992px) {
  .thread-nav-links {
    display: flex;
  }
  .nav-cta {
    display: inline-flex;
  }
}

/* ==========================================================================
   Mobile / Tablet Navigation (<992px) — desktop nav is replaced by a
   "Menu" toggle + full-screen overlay.
   ========================================================================== */
.mobile-nav-toggle {
  display: none;
  align-items: center;
  gap: 10px;
  background: transparent;
  border: none;
  padding: 0;
  cursor: none;
  color: var(--text-primary);
  z-index: 10;
}

.mobile-nav-toggle-text {
  font-family: var(--font-sans);
  font-size: 12px;
  font-weight: 600;
  letter-spacing: 0.1em;
  text-transform: uppercase;
}

.mobile-nav-toggle-icon {
  width: 22px;
  height: 14px;
  position: relative;
}

.mobile-nav-toggle-line {
  position: absolute;
  left: 0;
  width: 100%;
  height: 1.5px;
  background: currentColor;
  transition: var(--transition-fast);
}

.mobile-nav-toggle-line:nth-child(1) { top: 0; }
.mobile-nav-toggle-line:nth-child(2) { bottom: 0; }

.mobile-menu-overlay {
  position: fixed;
  inset: 0;
  z-index: var(--z-menu);
  background: var(--text-primary);
  color: #fff3eb;
  display: flex;
  flex-direction: column;
  justify-content: space-between;
  padding: 24px 24px 40px;
  transform: translateY(-100%);
  transition: transform 0.7s var(--ease-in-out);
  /* Promoted only while open — see .mobile-menu-overlay.is-open. Held
     permanently this was a full-viewport layer (~27 MB) for a closed menu. */
}

.mobile-menu-overlay.is-open {
  transform: translateY(0);
  will-change: transform;
}

.mobile-menu-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
}

.mobile-menu-logo {
  display: flex;
  align-items: center;
  color: #fff3eb;
  text-decoration: none;
}
.mobile-menu-logo-img {
  height: 52px;
  width: auto;
  display: block;
  object-fit: contain;
  /* Recolor the coloured brand SVG to pure white on the dark overlay */
  filter: brightness(0) invert(1);
}

.mobile-nav-close {
  display: flex;
  align-items: center;
  gap: 10px;
  background: transparent;
  border: none;
  padding: 0;
  cursor: none;
  color: #fff3eb;
}

.mobile-nav-close-icon {
  width: 16px;
  height: 16px;
  position: relative;
}

.mobile-nav-close-icon .mobile-nav-toggle-line {
  top: 50%;
}

.mobile-nav-close-icon .mobile-nav-toggle-line:nth-child(1) {
  transform: rotate(45deg);
}

.mobile-nav-close-icon .mobile-nav-toggle-line:nth-child(2) {
  transform: rotate(-45deg);
}

.mobile-menu-links {
  display: flex;
  flex-direction: column;
  gap: 8px;
}

.mobile-menu-link {
  display: flex;
  align-items: baseline;
  gap: 16px;
  padding: 14px 4px;
  text-decoration: none;
  color: rgba(255, 243, 235, 0.5);
  border-bottom: 1px solid rgba(255, 243, 235, 0.1);
  transition: var(--transition-fast);
}

/* Hover-only highlight — no scroll-driven "active" state per design.
   (The .active class may still be set by JS for potential future use, but
   it has no visual effect — matches the desktop nav treatment.) */
.mobile-menu-link:hover {
  color: #fff3eb;
  padding-left: 14px;
}

.mobile-menu-link-num {
  font-family: var(--font-sans);
  font-size: 11px;
  font-weight: 500;
  letter-spacing: 0.08em;
  color: var(--accent);
}

.mobile-menu-link-text {
  font-family: var(--font-sans);
  font-size: clamp(1.6rem, 8vw, 2.4rem);
  font-weight: 700;
  font-style: normal;
  text-transform: uppercase;
  letter-spacing: 0.01em;
}

/* Note: nth-child serif-italic rhythm from the original design has been
   retired — mobile menu now matches the desktop nav's Onest bold uppercase. */
.mobile-menu-link:nth-child(even) .mobile-menu-link-text {
  font-family: var(--font-sans);
  font-style: normal;
  font-weight: 700;
}

.mobile-menu-footer {
  display: flex;
  flex-direction: column;
  gap: 16px;
}

.mobile-menu-status {
  display: flex;
  align-items: center;
  gap: 8px;
  font-size: 12px;
  color: rgba(255, 243, 235, 0.6);
  letter-spacing: 0.03em;
}

.mobile-menu-email {
  font-family: var(--font-sans);
  font-size: 14px;
  color: #fff3eb;
  text-decoration: none;
  border-bottom: 1px solid rgba(255, 243, 235, 0.3);
  width: fit-content;
}

/* Below 992px (Bootstrap lg): desktop nav (links + CTA) is hidden by its own
   base rules; reveal the hamburger and push it to the far right of the flex
   wrapper so the logo sits far-left and the toggle sits far-right. */
@media (max-width: 991.98px) {
  .mobile-nav-toggle {
    display: flex;
    margin-left: auto;
  }
}

/* ==========================================================================
   Hero Section
   ========================================================================== */
.hero-section {
  min-height: 100vh;  /* fallback for engines without dvh */
  min-height: 100dvh;
  display: flex;
  align-items: center;
  padding-top: 84px; /* pulled up to sit closer to the nav (was 140px) */
  padding-bottom: 60px;
  position: relative;
  border-bottom: none;
}

/* Tablet & below (Bootstrap lg): tight fold — hero content sits DIRECTLY
   below the fixed header with no dead space. Height collapses to the natural
   size of the kinetic title stack + CTA so nothing is stranded in a giant
   centered flex box. */
@media (max-width: 991.98px) {
  .hero-section {
    min-height: 0;
    height: auto;
    align-items: flex-start;
    padding-top: 128px;   /* matches tablet header height (120px) + 8px gap */
    padding-bottom: 40px;
  }
}
@media (max-width: 767.98px) {
  .hero-section {
    padding-top: 96px;    /* mobile header is 80px + 16px gap */
    padding-bottom: 32px;
  }
}
@media (max-width: 575.98px) {
  .hero-section {
    padding-top: 92px;
    padding-bottom: 24px;
  }
}

#hero-canvas, .threads-stage {
  display: none;
}

/* Original WebGL layer styles retained for other threads-stage usages
   (case-study page) — the hero canvas is disabled in the kinetic hero. */
.threads-stage--legacy {
  position: absolute;
  left: 0;
  top: 0;
  width: 100%;
  height: 100%;
  pointer-events: none;
  z-index: 1;
  -webkit-mask-image: linear-gradient(180deg, rgba(0,0,0,0) 0%, rgba(0,0,0,1) 15%, rgba(0,0,0,1) 82%, rgba(0,0,0,0) 100%);
  mask-image: linear-gradient(180deg, rgba(0,0,0,0) 0%, rgba(0,0,0,1) 15%, rgba(0,0,0,1) 82%, rgba(0,0,0,0) 100%);
}

.hero-content {
  grid-column: 1 / -1;
  display: flex;
  flex-direction: column;
  justify-content: center;
  z-index: 2;
  text-align: center;
}

.hero-title {
  font-size: clamp(3.5rem, 8.5vw, 9.5rem);
  line-height: 1.05;
  font-weight: 500;
  text-transform: none;
  margin-bottom: 20px;
  letter-spacing: -0.03em;
  text-align: center;
}

.title-line {
  display: block;
  overflow: hidden;
  text-align: center;
}

.reveal-text {
  display: inline-block;
  transform: translateY(110%);
}

.hero-footer-centered {
  display: flex;
  flex-direction: column;
  align-items: center;
  margin-top: 5px;
}

.hero-desc-centered {
  max-width: 580px;
  text-align: center;
  margin-bottom: 40px;
}

.hero-desc-centered p {
  font-size: clamp(16px, 1.25vw, 19px);
  color: var(--text-primary);
  font-weight: 400;
  line-height: 1.6;
}

.hero-cta-accent-container {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 16px;
}

.btn-pill-black,
.hero-cta {
  display: inline-flex;
  align-items: center;
  gap: 16px;
  background-color: #1d1c1c;
  color: #fff3eb;
  padding: 12px 24px;
  border-radius: 40px;
  text-decoration: none;
  font-family: var(--font-sans);
  font-size: 14px;
  font-weight: 600;
  box-shadow: 0 4px 15px rgba(0, 0, 0, 0.1);
  transition: transform 0.3s var(--ease-out), background-color 0.3s;
  position: relative;
  overflow: hidden;
}

.hero-cta::before {
  content: "";
  position: absolute;
  inset: 0;
  background: radial-gradient(circle 120px at var(--x, 50%) var(--y, 50%), rgba(255, 107, 0, 0.45), transparent 70%);
  opacity: 0;
  transition: opacity 0.4s var(--ease);
  pointer-events: none;
  z-index: 0;
}

.hero-cta:hover::before {
  opacity: 1;
}

.btn-pill-black:hover,
.hero-cta:hover {
  transform: translateY(-2px);
  background-color: #0c0b0b;
}

.hero-cta span:not(.arrow):not(.arrow-badge) {
  position: relative;
  z-index: 1;
}

.arrow-badge,
.hero-cta .arrow {
  position: relative;
  z-index: 1;
  width: 26px;
  height: 26px;
  border-radius: 50%;
  background: #fff;
  color: #0d0d0d;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  transition: transform 0.5s var(--ease), background 0.35s var(--ease), color 0.35s var(--ease);
}

.arrow-badge svg,
.hero-cta .arrow svg {
  width: 14px;
  height: 14px;
  display: block;
  transition: transform 0.3s var(--ease-out);
}

.hero-cta:hover .arrow,
.hero-cta:hover .arrow-badge,
.nav-cta:hover .arrow-badge,
.btn-pill-black:hover .arrow-badge {
  transform: translate(2px, -2px) rotate(-6deg);
  background: #FF6B00; /* Active brand orange */
  color: #fff; /* White icon stroke */
}

.hero-cta:hover .arrow svg,
.nav-cta:hover .arrow-badge svg,
.btn-pill-black:hover .arrow-badge svg {
  transform: rotate(-39deg);
}

.pulse-indicator-container {
  display: flex;
  align-items: center;
  gap: 8px;
}

.pulse-dot {
  width: 8px;
  height: 8px;
  background-color: var(--accent);
  border-radius: 50%;
  position: relative;
}

.pulse-dot::after {
  content: '';
  position: absolute;
  top: -4px;
  left: -4px;
  right: -4px;
  bottom: -4px;
  border: 1px solid var(--accent);
  border-radius: 50%;
  animation: pulse-ring 1.5s var(--ease-in-out) infinite;
}

.pulse-text {
  font-size: 12px;
  color: var(--text-secondary);
  font-weight: 500;
  letter-spacing: 0.05em;
}

@keyframes pulse-ring {
  0% { transform: scale(0.5); opacity: 1; }
  100% { transform: scale(1.5); opacity: 0; }
}


/* ==========================================================================
   Services Grid — brutalist poster tiles with content-swap hover
   ========================================================================== */
.services-grid-section {
  padding: 0;
  border-bottom: none;
  overflow: hidden;
  background: var(--bg-primary);
}

.services-grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 0;
}

/* ── Card shell (base — variant classes control layout) ── */
.svc-card {
  position: relative;
  display: block;
  text-decoration: none;
  color: inherit;
  overflow: hidden;
  cursor: none;
  border-right: 1px solid rgba(29, 28, 28, 0.08);
  border-bottom: 1px solid rgba(29, 28, 28, 0.08);
  transition: background-color 0.5s var(--ease);
  isolation: isolate;
}

/* ── Poster variant — tall half-width tile (icon top-left, title bottom-left, footer) ── */
.svc-card--poster {
  display: grid;
  grid-template-columns: 1fr auto;
  grid-template-rows: auto 1fr auto;
  gap: 24px;
  padding: 44px 48px;
  min-height: 46vh;
}
.svc-card--poster .svc-card__icon {
  grid-column: 1;
  grid-row: 1;
  align-self: start;
  width: 56px;
  height: 56px;
  border-radius: 14px;
  display: flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
  transition: transform 0.55s var(--ease);
}
.svc-card--poster .svc-card__icon svg { width: 26px; height: 26px; }
.svc-card--poster:hover .svc-card__icon {
  transform: rotate(-6deg) scale(1.06);
}

.svc-card--poster .svc-card__topright {
  grid-column: 2;
  grid-row: 1;
  align-self: start;
  margin-top: 22px;
  position: relative;
  height: 16px;
  overflow: hidden;
  min-width: 200px;
  text-align: right;
}
.svc-card--poster .svc-card__topright .svc-label {
  display: block;
  position: absolute;
  top: 0;
  right: 0;
  white-space: nowrap;
  transition: transform 0.55s var(--ease-in-out);
}
.svc-card--poster .svc-card__topright .svc-label--idle  { transform: translateY(0); }
.svc-card--poster .svc-card__topright .svc-label--hover { transform: translateY(28px); }
.svc-card--poster:hover .svc-card__topright .svc-label--idle  { transform: translateY(-28px); }
.svc-card--poster:hover .svc-card__topright .svc-label--hover { transform: translateY(0); }

.svc-card--poster .svc-card__body {
  grid-column: 1 / -1;
  grid-row: 2;
  align-self: end;
  position: relative;
  min-height: 220px;
}
.svc-card--poster .svc-card__idle,
.svc-card--poster .svc-card__hover {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  transition: opacity 0.5s var(--ease), transform 0.6s var(--ease);
  will-change: transform, opacity;
}
.svc-card--poster .svc-card__idle { opacity: 1; transform: translateY(0); }
.svc-card--poster .svc-card__hover {
  opacity: 0;
  transform: translateY(24px);
  pointer-events: none;
}
.svc-card--poster:hover .svc-card__idle {
  opacity: 0;
  transform: translateY(-24px);
}
.svc-card--poster:hover .svc-card__hover {
  opacity: 1;
  transform: translateY(0);
}

.svc-card--poster .svc-card__title {
  font-family: 'Barlow Condensed', 'Onest', sans-serif;
  font-size: clamp(2.4rem, 4.4vw, 4.6rem);
  font-weight: 700;
  text-transform: uppercase;
  line-height: 0.92;
  letter-spacing: 0.005em;
  margin: 0;
}
.svc-card--poster .svc-card__subhead {
  font-family: 'Barlow Condensed', 'Onest', sans-serif;
  font-size: clamp(1.5rem, 2.3vw, 2.2rem);
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.005em;
  line-height: 1.05;
  margin: 0 0 14px;
}
.svc-card--poster .svc-card__desc {
  font-family: var(--font-sans);
  font-size: clamp(13px, 0.95vw, 15px);
  line-height: 1.55;
  max-width: 44ch;
  margin: 0;
  opacity: 0.88;
  text-transform: none;
}

.svc-card--poster .svc-card__footer {
  grid-column: 1;
  grid-row: 3;
  align-self: end;
  padding-top: 32px;
}

/* ── Icon color per palette ── */
.svc-card--cream .svc-card__icon,
.svc-card--orange .svc-card__icon {
  background: #1d1c1c;
  color: #fff3eb;
}
.svc-card--charcoal .svc-card__icon {
  background: #fff3eb;
  color: #1d1c1c;
}
.svc-card--black .svc-card__icon {
  background: #fff3eb;
  color: #ff914d; /* Orange accent — closes the loop with hero waves */
}

/* ── Compact variant — full-width horizontal chapter band (icon + title inline) ── */
.svc-card--compact {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 40px;
  padding: 36px 56px;
  min-height: 200px;
  grid-template-columns: none;
  grid-template-rows: none;
}
.svc-card--compact .svc-card__lhs {
  display: flex;
  align-items: center;
  gap: 28px;
  min-width: 0;
  flex: 1;
}
.svc-card--compact .svc-card__icon {
  width: 56px;
  height: 56px;
  border-radius: 14px;
  flex-shrink: 0;
}
.svc-card--compact .svc-card__icon svg {
  width: 26px;
  height: 26px;
}

/* Content column: title (idle) ↔ subhead + description (hover) SWAP */
.svc-card--compact .svc-card__content {
  flex: 1;
  min-width: 0;
  position: relative;
  min-height: 96px;
}
.svc-card--compact .svc-card__idle,
.svc-card--compact .svc-card__hover {
  position: absolute;
  left: 0;
  right: 0;
  top: 50%;
  transform: translateY(-50%);
  transition: opacity 0.5s var(--ease), transform 0.55s var(--ease);
  will-change: transform, opacity;
}
.svc-card--compact .svc-card__idle { opacity: 1; }
.svc-card--compact .svc-card__hover {
  opacity: 0;
  transform: translateY(calc(-50% + 20px));
  pointer-events: none;
}
.svc-card--compact:hover .svc-card__idle {
  opacity: 0;
  transform: translateY(calc(-50% - 20px));
}
.svc-card--compact:hover .svc-card__hover {
  opacity: 1;
  transform: translateY(-50%);
}

.svc-card--compact .svc-card__title {
  font-family: 'Barlow Condensed', 'Onest', sans-serif;
  font-size: clamp(1.6rem, 2.6vw, 2.8rem);
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.01em;
  line-height: 1.02;
  margin: 0;
}

.svc-card--compact .svc-card__subhead {
  font-family: 'Barlow Condensed', 'Onest', sans-serif;
  font-size: clamp(1.2rem, 1.8vw, 1.8rem);
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.01em;
  line-height: 1.05;
  margin: 0 0 6px;
}
.svc-card--compact .svc-card__desc {
  font-family: var(--font-sans);
  font-size: clamp(13px, 0.95vw, 15px);
  font-weight: 400;
  line-height: 1.5;
  max-width: 64ch;
  margin: 0;
  opacity: 0.88;
  text-transform: none;
}

.svc-card--compact .svc-card__rhs {
  position: relative;
  height: 16px;
  min-width: 260px;
  overflow: hidden;
  text-align: right;
  flex-shrink: 0;
  align-self: center;
}
.svc-card--compact .svc-card__rhs .svc-label {
  display: block;
  position: absolute;
  top: 0;
  right: 0;
  white-space: nowrap;
  transition: transform 0.55s var(--ease-in-out);
}
.svc-card--compact .svc-card__rhs .svc-label--idle  { transform: translateY(0); }
.svc-card--compact .svc-card__rhs .svc-label--hover { transform: translateY(28px); }
.svc-card--compact:hover .svc-card__rhs .svc-label--idle  { transform: translateY(-28px); }
.svc-card--compact:hover .svc-card__rhs .svc-label--hover { transform: translateY(0); }

.svc-card--full { grid-column: 1 / -1; }
.svc-card--half { grid-column: span 1; }

/* Palette variants */
.svc-card--cream    { background-color: #faece1; color: #1d1c1c; }
.svc-card--orange   { background-color: #ff914d; color: #1d1c1c; }
.svc-card--charcoal { background-color: #1d1c1c; color: #fff3eb; border-color: rgba(255, 243, 235, 0.08); }
.svc-card--black    { background-color: #0d0d0d; color: #fff3eb; border-color: rgba(255, 243, 235, 0.06); }

.svc-card:last-child { border-right: none; }

.svc-card:hover {
  background-color: color-mix(in oklab, currentColor 4%, transparent);
}
.svc-card--cream:hover    { background-color: #f5e3d0; }
.svc-card--orange:hover   { background-color: #f5843b; }
.svc-card--charcoal:hover { background-color: #161514; }
.svc-card--black:hover    { background-color: #050505; }

/* ── Micro-label style (shared) ── */
.svc-label {
  font-family: 'JetBrains Mono', monospace;
  font-size: 11px;
  font-weight: 500;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  opacity: 0.85;
}

/* ── Responsive ── */
@media (max-width: 1024px) {
  .svc-card { padding: 40px 40px; min-height: 42vh; }
  .svc-card__body { min-height: 180px; margin-top: 40px; }
}
@media (max-width: 768px) {
  .services-grid { grid-template-columns: 1fr; }
  .svc-card,
  .svc-card--full,
  .svc-card--half {
    grid-column: 1 / -1;
    min-height: auto;
    padding: clamp(36px, 6vw, 48px) clamp(20px, 4vw, 32px);
    border-right: none;
  }
  .svc-card__body { min-height: auto; margin-top: clamp(36px, 6vw, 60px); }
  .svc-card--compact {
    min-height: auto;
    padding: 24px 20px;
    gap: 16px;
  }
  .svc-card--compact .svc-card__rhs { display: none; }
  .svc-card--compact .svc-card__title { font-size: clamp(1.2rem, 5.5vw, 1.6rem); }
  .svc-card__topright { min-width: auto; }
  .svc-card__title { font-size: clamp(2.2rem, 9vw, 3.6rem); }
}


#works.works-section {
  padding: 0;
  overflow: visible;
}

.works-layout {
  display: grid;
  grid-template-columns: 340px 1fr;
  align-items: start;
  min-height: 100vh;  /* fallback for engines without dvh */
  min-height: 100dvh;
}

/* ── Left Sticky Sidebar ── */
.works-sidebar {
  position: sticky;
  top: 120px;
  padding: 100px 60px 100px 80px;
  display: flex;
  flex-direction: column;
  gap: 24px;
}

.works-lhs-container {
  position: relative;
  width: 100%;
  min-height: 480px;
}

.works-lhs-content {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  display: flex;
  flex-direction: column;
  gap: 20px;
  
  /* Inactive State */
  opacity: 0;
  transform: translateY(40px);
  filter: blur(8px);
  pointer-events: none;
}

.works-lhs-content.is-active {
  opacity: 1;
  transform: translateY(0);
  filter: blur(0);
  pointer-events: auto;
}

.works-lhs-meta {
  display: flex;
  align-items: center;
}

.works-lhs-category {
  font-family: var(--font-sans);
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--accent);
}

.works-counter-block {
  display: flex;
  align-items: baseline;
  gap: 4px;
  margin-bottom: 8px;
}

.works-counter-num {
  font-size: clamp(3.5rem, 5vw, 6rem);
  font-weight: 400;
  font-family: var(--font-serif);
  font-style: italic;
  line-height: 1;
  color: var(--text-primary);
}

.works-counter-total {
  font-size: 14px;
  color: var(--text-muted);
  font-family: var(--font-sans);
  font-weight: 400;
  letter-spacing: 0.04em;
}

.works-headline {
  font-size: clamp(1.8rem, 2.4vw, 3rem);
  font-weight: 400;
  line-height: 1.2;
  letter-spacing: -0.02em;
}

.works-sidebar-desc {
  font-size: 15px;
  color: var(--text-secondary);
  line-height: 1.6;
  font-weight: 300;
  max-width: 240px;
}

.works-sidebar-metrics {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 12px;
  margin-top: 16px;
  padding-top: 16px;
  border-top: 1px solid var(--border-color);
  max-width: 260px;
}

.works-metric-label {
  font-size: 10px;
  color: var(--text-muted);
  font-family: var(--font-sans);
  letter-spacing: 0.05em;
  text-transform: uppercase;
}

.works-metric-value {
  font-size: 14px;
  font-weight: 600;
  color: var(--text-primary);
  font-family: var(--font-sans);
  margin-top: 2px;
}

/* ── Right Cards Area ── */
.works-cards-area {
  padding: 100px 80px 100px 60px;
  display: flex;
  flex-direction: column;
  gap: 60px;
  position: relative;
  cursor: none;
}

/* ── Individual Card ── */
.works-card {
  background: #f7f6f4;
  border-radius: 20px;
  overflow: hidden;
  border: 1px solid var(--border-color);
  will-change: filter, opacity, transform;
  transition: box-shadow 0.4s ease;
}

.works-card:hover {
  box-shadow: 0 24px 60px rgba(0,0,0,0.10);
}

.works-card-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 28px 32px 20px;
}

.works-card-meta {
  display: flex;
  align-items: baseline;
  gap: 16px;
}

.works-card-num {
  font-size: 12px;
  color: var(--accent);
  font-weight: 600;
  letter-spacing: 0.08em;
  font-family: var(--font-sans);
}

.works-card-name {
  font-size: clamp(1.1rem, 1.6vw, 1.7rem);
  font-weight: 500;
  letter-spacing: -0.01em;
  color: var(--text-primary);
}

.works-card-cta {
  display: flex;
  align-items: center;
  gap: 8px;
  font-size: 13px;
  font-weight: 500;
  font-family: var(--font-sans);
  color: var(--text-primary);
  background: #fff;
  border: 1px solid var(--border-color);
  border-radius: 100px;
  padding: 10px 20px;
  cursor: pointer;
  transition: background 0.25s, color 0.25s, border-color 0.25s;
}

.works-card-cta:hover {
  background: var(--accent);
  color: #fff;
  border-color: var(--accent);
}

.works-card-tags {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 0 32px 20px;
  flex-wrap: wrap;
}

.works-tag {
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--text-muted);
  background: rgba(0,0,0,0.05);
  border-radius: 100px;
  padding: 5px 12px;
}

.works-tag-year {
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.08em;
  color: var(--text-muted);
  margin-left: auto;
}

.works-card-img-wrapper {
  width: 100%;
  aspect-ratio: 16 / 9;
  overflow: hidden;
  background: #e8e6e1;
  position: relative;
}

.works-card-img {
  width: 100%;
  height: 130%; /* Taller for parallax movement */
  object-fit: cover;
  display: block;
  position: absolute;
  top: 0;
  left: 0;
  will-change: transform;
}

/* ── Dynamic Root Custom Cursor View Badge (#cur & .cur-view) ── */
.cur {
  position: fixed;
  top: 0;
  left: 0;
  pointer-events: none;
  z-index: 999999;
  will-change: transform;
}

.cur-view {
  position: absolute;
  top: 0;
  left: 0;
  width: 90px;
  height: 90px;
  border-radius: 50%;
  background: #f8853d;
  box-shadow: 0 10px 30px -8px rgba(248, 133, 61, 0.55);
  display: flex;
  align-items: center;
  justify-content: center;
  opacity: 0;
  transform: translate(-50%, -50%) scale(0);
  transition: transform 0.35s var(--ease-out), opacity 0.3s ease;
  will-change: transform, opacity;
}

.cur.is-view .cur-view {
  opacity: 1;
  transform: translate(-50%, -50%) scale(1);
}

/* Default: hide hand icon */
.cur-hand {
  display: none !important;
}

/* Active state: show hand, hide default dot/ring/view */
.cur.is-hand .cur-hand {
  display: block !important;
}
.cur.is-hand .cur-ring,
.cur.is-hand .cur-text,
.cur.is-hand .cur-view {
  display: none !important;
}

.cur-view-ring {
  position: absolute;
  top: 0;
  left: 0;
  width: 90px;
  height: 90px;
  animation: cur-view-spin 8s linear infinite;
}

.cur-view-ring text {
  font-family: "Onest", sans-serif;
  font-size: 8px;
  font-weight: 600;
  fill: #1d1c1c;
  letter-spacing: normal; /* Let the SVG textLength handle the letter spacing */
  text-transform: uppercase;
}

@keyframes cur-view-spin {
  from { transform: rotate(0deg); }
  to   { transform: rotate(360deg); }
}

.cur-view-arrow {
  width: 22px;
  height: 22px;
  stroke: #1d1c1c;
  z-index: 2;
  transition: transform 0.3s ease;
}

/* Hide default cursor dot and follower when view or hand badge is active */
body.cursor-view-active .custom-cursor,
body.cursor-view-active .custom-cursor-follower,
body.cursor-hand-active .custom-cursor,
body.cursor-hand-active .custom-cursor-follower,
.cur.is-hand ~ .custom-cursor,
.cur.is-hand ~ .custom-cursor-follower,
.cur.is-view ~ .custom-cursor,
.cur.is-view ~ .custom-cursor-follower {
  opacity: 0 !important;
  transform: scale(0) !important;
  pointer-events: none !important;
  transition: opacity 0.2s ease, transform 0.2s ease;
}



/* ==========================================================================
   Services Accordion Section
   ========================================================================== */
.services-left {
  grid-column: 1 / 6;
  position: sticky;
  top: 150px;
  height: fit-content;
}

.services-title {
  font-size: clamp(2rem, 3.5vw, 4rem);
  text-transform: uppercase;
  font-weight: 400;
  margin-bottom: 30px;
}

.services-intro {
  color: var(--text-secondary);
  font-size: 16px;
  max-width: 380px;
}

.services-right {
  grid-column: 7 / -1;
}

.accordion-container {
  display: flex;
  flex-direction: column;
  width: 100%;
}

.accordion-item {
  border-bottom: 1px solid var(--border-color);
  padding: 30px 0;
}

.accordion-item:first-child {
  border-top: 1px solid var(--border-color);
}

.accordion-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  transition: color 0.3s;
}

.accordion-title-group {
  display: flex;
  align-items: center;
  gap: 16px;
}

.accordion-dot {
  width: 8px;
  height: 8px;
  background-color: var(--border-color);
  border-radius: 50%;
  transition: var(--transition-fast);
}

.accordion-item.active .accordion-dot {
  background-color: var(--accent);
  box-shadow: 0 0 10px var(--accent);
}

.accordion-heading {
  font-size: clamp(1.25rem, 1.8vw, 1.8rem);
  font-weight: 500;
  color: var(--text-secondary);
  transition: color 0.3s;
}

.accordion-item.active .accordion-heading,
.accordion-header:hover .accordion-heading {
  color: var(--text-primary);
}

.accordion-icon-toggle {
  position: relative;
  width: 18px;
  height: 18px;
}

.accordion-icon-toggle span {
  position: absolute;
  background-color: var(--text-muted);
  transition: transform 0.4s var(--ease-out), background-color 0.3s;
}

.line-h {
  width: 100%;
  height: 1.5px;
  top: 50%;
  transform: translateY(-50%);
}

.line-v {
  width: 1.5px;
  height: 100%;
  left: 50%;
  transform: translateX(-50%);
}

.accordion-item.active .line-v {
  transform: translateX(-50%) rotate(90deg);
}

.accordion-header:hover .accordion-icon-toggle span {
  background-color: var(--accent);
}

.accordion-body {
  max-height: 0;
  overflow: hidden;
  transition: max-height 0.6s var(--ease-out);
}

.accordion-inner {
  padding-top: 24px;
  padding-left: 24px;
}

.accordion-inner p {
  color: var(--text-secondary);
  font-size: 15px;
  margin-bottom: 24px;
  max-width: 520px;
}

.capabilities-list {
  display: flex;
  flex-wrap: wrap;
  gap: 12px;
  list-style: none;
}

.capabilities-list li {
  font-size: 12px;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  padding: 8px 16px;
  background: var(--bg-secondary);
  border: 1px solid var(--border-color);
  border-radius: 20px;
  color: var(--text-secondary);
  font-weight: 500;
}

@media (max-width: 768px) {
  .services-left {
    grid-column: span 4;
    position: static;
    margin-bottom: 40px;
  }
  .services-right {
    grid-column: span 4;
  }
  .accordion-inner {
    padding-left: 0;
  }
}

/* ==========================================================================
   Process Section
   ========================================================================== */
.process-header {
  grid-column: 1 / -1;
  margin-bottom: 80px;
}

.process-title {
  font-size: clamp(2rem, 3.5vw, 4rem);
  text-transform: uppercase;
  font-weight: 400;
}

.process-timeline-container {
  grid-column: 1 / -1;
  position: relative;
  padding-left: 40px;
}

.timeline-rail {
  position: absolute;
  left: 0;
  top: 10px;
  bottom: 10px;
  width: 2px;
  background-color: var(--border-color);
  overflow: hidden;
}

/* Uses scaleY instead of height — compositor-only, zero layout cost */
.timeline-rail-fill {
  position: absolute;
  left: 0;
  top: 0;
  width: 100%;
  height: 100%;
  background-color: var(--accent);
  box-shadow: 0 0 8px var(--accent);
  transform: scaleY(0);
  transform-origin: top center;
  will-change: transform;
  transition: transform 0.1s ease-out;
}

.timeline-steps {
  display: flex;
  flex-direction: column;
  gap: 90px;
}

.timeline-step {
  position: relative;
  display: grid;
  grid-template-columns: repeat(12, 1fr);
  gap: 30px;
  opacity: 0.35;
  will-change: opacity;
  transition: opacity 0.5s;
}

.timeline-step.active {
  opacity: 1;
}

.timeline-node-wrapper {
  position: absolute;
  left: -48px;
  top: 4px;
  width: 18px;
  height: 18px;
  display: flex;
  align-items: center;
  justify-content: center;
  z-index: 3;
}

.timeline-node {
  width: 10px;
  height: 10px;
  background-color: var(--bg-primary);
  border: 2px solid var(--border-color);
  border-radius: 50%;
  will-change: transform, opacity;
  transition: transform 0.5s var(--ease-out), background-color 0.5s var(--ease-out), border-color 0.5s var(--ease-out);
}

.timeline-step.active .timeline-node {
  border-color: var(--accent);
  background-color: var(--accent);
  box-shadow: 0 0 10px var(--accent);
  transform: scale(1.3);
}

.timeline-content {
  grid-column: 1 / 9;
}

.step-num {
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.1em;
  color: var(--text-muted);
  display: block;
  margin-bottom: 12px;
  transition: color 0.3s;
}

.timeline-step.active .step-num {
  color: var(--accent);
}

.step-title {
  font-size: clamp(1.4rem, 2.2vw, 2.2rem);
  text-transform: uppercase;
  font-weight: 500;
  margin-bottom: 16px;
}

.step-desc {
  color: var(--text-secondary);
  font-size: clamp(14px, 1.1vw, 17px);
  max-width: 600px;
  font-weight: 300;
}

@media (max-width: 768px) {
  .timeline-content {
    grid-column: span 4;
  }
}

/* ==========================================================================
   Testimonials — deck of six cards.

   Replaced the one-at-a-time auto-rotator. The scatter is an entrance only:
   cards arrive rotated and overlapping, then settle into a readable grid when
   the section scrolls into view. The GRID IS THE RESTING STATE — the reference
   this is modelled on tidied only on hover, which left a third of its quotes
   clipped behind other cards for anyone on touch or a keyboard.

   Rotation lives in --card-rot (set per card inline) so the settle is a single
   transform interpolation to none. Transform + opacity only.
   ========================================================================== */
.testimonials-wrapper {
  grid-column: 2 / 12;
  display: flex;
  flex-direction: column;
  align-items: center;
}

.ledger-deck {
  width: 100%;
  margin: clamp(36px, 4vw, 60px) 0 0;
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: clamp(14px, 1.4vw, 22px);
  align-items: start;
}

.ledger-slide {
  position: relative;
  display: flex;
  flex-direction: column;
  margin: 0;
  padding: clamp(24px, 2.2vw, 34px);
  border-radius: clamp(14px, 1.4vw, 20px);
  text-align: left;
  border: 1px solid rgba(13, 13, 13, 0.1);
  opacity: 0;
  /* Flat by default. The scatter is applied only at >=992px — see below. */
  transform: none;
  /* box-shadow is deliberately NOT in this transition. Animating a shadow
     repaints the entire card on every frame of a 700ms transform, which is
     what made the separation stutter; the hover shadow is cross-faded on a
     pseudo-element below instead. Only opacity and transform animate here. */
  transition: opacity var(--dur-base) var(--ease-out) var(--card-delay, 0ms),
              transform var(--dur-slow) var(--ease-out) var(--card-delay, 0ms);
  box-shadow: 0 18px 44px -20px rgba(13, 13, 13, 0.34);
  /* Promoted so the transform runs on the compositor and the rotated text is
     rasterised once rather than every frame. Six elements, so the layer cost
     is trivial — unlike the site-wide reveal system, where promoting 100+
     elements would cost more than it saved. */
  will-change: transform;
  backface-visibility: hidden;
}

/* Deeper hover shadow as a cross-faded overlay. Opacity is compositor-only,
   so the lift stays smooth where transitioning box-shadow directly would not. */
.ledger-slide::after {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: inherit;
  box-shadow: 0 30px 62px -24px rgba(13, 13, 13, 0.46);
  opacity: 0;
  transition: opacity var(--dur-fast) var(--ease-state);
  pointer-events: none;
}

/* Scroll only fades the pile in — it does NOT tidy it. */
.ledger-deck.is-settled .ledger-slide { opacity: 1; }

/* Tidy on hover. :focus-within is not optional garnish — it is the only way a
   keyboard user can reach the readable grid, since they have no pointer to
   hover with. Both selectors must stay together. */
/* The pile, and the hover that resolves it, exist only where there is a
   three-column grid to collapse from. Below 992px the deck is one or two
   columns and the offsets are absolute pixels: at 390px a +205px card sits
   half off-screen, where the global `section { overflow: hidden }` clips its
   text rather than letting it scroll. Caught by the tablet/mobile screenshot
   tests, which failed while desktop passed. */
@media (min-width: 992px) {
  .ledger-slide {
    /* Scattered is the RESTING state — a thrown pile. Cards are scaled past
       their grid cell so they genuinely overlap their neighbours. */
    transform: rotate(var(--card-rot, 0deg))
               translate(var(--card-x, 0px), var(--card-y, 0px))
               scale(1.06);
    z-index: var(--card-z, 1);
  }
}

.ledger-deck.is-settled:hover .ledger-slide,
.ledger-deck.is-settled:focus-within .ledger-slide {
  transform: none;
  /* z-index deliberately left alone. Changing it mid-transform forces the
     compositor to restack every card, which showed up as a hitch right at the
     start of the separation. The cards no longer overlap once tidied, so the
     stacking order stops mattering anyway. */
  /* No delay on the way out. --card-delay staggers the initial fade-in, but
     reusing it here would make the separation lag the pointer by up to 350ms,
     and feedback must be immediate. */
  transition-delay: 0ms;
}

/* Coarse pointers cannot hover at all, so the pile would simply never resolve
   and several quotes would be permanently unreadable. They get the grid. */
@media (hover: none), (pointer: coarse) {
  .ledger-slide { transform: none !important; z-index: auto !important; will-change: auto; }
}

/* Site-wide 70ms step. Six cards land over 350ms — the deck tidies as a
   sequence rather than snapping as one block. */
.ledger-slide:nth-child(2) { --card-delay: 70ms; }
.ledger-slide:nth-child(3) { --card-delay: 140ms; }
.ledger-slide:nth-child(4) { --card-delay: 210ms; }
.ledger-slide:nth-child(5) { --card-delay: 280ms; }
.ledger-slide:nth-child(6) { --card-delay: 350ms; }

/* Brand surfaces. Same logic as the process cards — neutrals carry the set,
   one charcoal and one orange punctuate it. Orange takes NEAR-BLACK text:
   #f8853d against cream is 2.5:1 and fails AA, against #0d0d0d it is 7.6:1. */
.ledger-slide--cream  { background: var(--bg-primary); color: var(--text-primary); }
.ledger-slide--sand   { background: #f2e6d6; color: var(--text-primary); }
.ledger-slide--dark   { background: #0d0d0d; color: var(--bg-primary); border-color: #0d0d0d; }
.ledger-slide--accent { background: var(--accent); color: #0d0d0d; border-color: var(--accent); }

.ledger-quote-mark {
  font-family: var(--font-serif);
  font-size: clamp(2.6rem, 3.4vw, 4rem);
  line-height: 0.6;
  height: 0.6em;
  color: currentColor;
  opacity: 0.32;
  margin-bottom: clamp(12px, 1.4vw, 20px);
}

.ledger-text {
  font-family: var(--font-sans);
  /* Onest, not the display serif. Instrument Serif italic is beautiful at the
     old 48px single-quote size and genuinely hard to read at card scale. */
  font-size: clamp(14px, 1vw, 16px);
  font-style: normal;
  line-height: 1.6;
  margin: 0 0 clamp(22px, 2.4vw, 32px);
  color: inherit;
  opacity: 0.88;
}

.ledger-meta {
  display: flex;
  align-items: center;
  gap: 12px;
  /* Pinned to the bottom so every attribution sits on one baseline across the
     row regardless of how long its quote ran. */
  margin-top: auto;
}

/* Initials rather than photographs: no stock-photo tell, no new assets, and
   nothing that implies a headshot we do not have. */
.ledger-avatar {
  flex-shrink: 0;
  width: 38px;
  height: 38px;
  border-radius: 50%;
  display: grid;
  place-items: center;
  font-family: 'JetBrains Mono', monospace;
  font-size: 11px;
  letter-spacing: 0.06em;
  border: 1px solid currentColor;
  border-color: color-mix(in srgb, currentColor 30%, transparent);
  background: color-mix(in srgb, currentColor 8%, transparent);
  color: inherit;
}

.ledger-meta__text { display: flex; flex-direction: column; gap: 2px; min-width: 0; }

.ledger-author {
  font-family: 'Barlow Condensed', 'Onest', sans-serif;
  font-size: clamp(15px, 1.05vw, 17px);
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.01em;
  line-height: 1.1;
  color: inherit;
}

.ledger-role {
  font-family: var(--font-sans);
  font-size: 12px;
  line-height: 1.3;
  color: inherit;
  opacity: 0.62;
}

/* Hover lifts one card clear of the set. Gated behind a fine pointer so touch
   devices, which fire hover on tap, never get a stuck raised card. */
/* Within the tidied grid, the card under the pointer still lifts clear. */
@media (hover: hover) and (pointer: fine) {
  .ledger-deck.is-settled:hover .ledger-slide:hover {
    transform: translate3d(0, -6px, 0);
  }
  .ledger-deck.is-settled:hover .ledger-slide:hover::after { opacity: 1; }
}

@media (max-width: 991.98px) {
  .ledger-deck { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

@media (max-width: 640px) {
  .testimonials-wrapper { grid-column: span 4; }
  .ledger-deck { grid-template-columns: 1fr; }
}

/* Reduced motion: the grid is the point, the scatter is not. Land directly in
   the readable state with a plain fade — no rotation, no travel. */
@media (prefers-reduced-motion: reduce) {
  .ledger-slide {
    transform: none !important;
    z-index: auto !important;
    transition: opacity 200ms ease !important;
  }
}


/* ==========================================================================
   Dialogue FAQ Section
   ========================================================================== */
.dialogue-left {
  grid-column: 1 / 5;
}

.dialogue-title {
  font-size: clamp(2rem, 3.5vw, 4rem);
  text-transform: uppercase;
  font-weight: 400;
  margin-bottom: 30px;
}

.dialogue-desc {
  color: var(--text-secondary);
  font-size: 16px;
}

.dialogue-right {
  grid-column: 6 / -1;
}

.chat-terminal {
  width: 100%;
  background: var(--bg-secondary);
  border: 1px solid var(--border-color);
  border-radius: 16px;
  overflow: hidden;
  box-shadow: 0 20px 50px rgba(0,0,0,0.08);
  display: flex;
  flex-direction: column;
  height: 480px;
}

.chat-header {
  padding: 16px 24px;
  background: rgba(0, 0, 0, 0.02);
  border-bottom: 1px solid var(--border-color);
  display: flex;
  align-items: center;
  gap: 10px;
}

.chat-status-dot {
  width: 8px;
  height: 8px;
  background-color: var(--accent);
  border-radius: 50%;
  box-shadow: 0 0 8px var(--accent);
}

.chat-status-text {
  font-family: monospace;
  font-size: 12px;
  color: var(--text-secondary);
}

.chat-history {
  flex-grow: 1;
  padding: 24px;
  overflow-y: auto;
  display: flex;
  flex-direction: column;
  gap: 16px;
}

/* Custom Scrollbar for Chat history */
.chat-history::-webkit-scrollbar {
  width: 4px;
}

.chat-bubble {
  max-width: 80%;
  padding: 14px 20px;
  border-radius: 18px;
  font-size: 14px;
  line-height: 1.5;
  will-change: transform, opacity;
  animation: bubble-fade 0.4s var(--ease-out) forwards;
  opacity: 0;
  transform: translateY(10px);
}

@keyframes bubble-fade {
  to {
    opacity: 1;
    transform: translateY(0);
  }
}

.bot-message {
  align-self: flex-start;
  background-color: rgba(0, 0, 0, 0.02);
  color: var(--text-primary);
  border-bottom-left-radius: 4px;
  border: 1px solid var(--border-color);
}

.user-message {
  align-self: flex-end;
  background-color: var(--accent-dim);
  color: var(--text-primary);
  border-bottom-right-radius: 4px;
  border: 1px solid rgba(var(--accent-rgb), 0.2);
}

/* Typing indicator */
.typing-indicator {
  display: flex;
  gap: 4px;
  padding: 6px 0;
}

.typing-indicator span {
  width: 6px;
  height: 6px;
  background: var(--text-secondary);
  border-radius: 50%;
  will-change: transform;
  animation: typing-animation 1.4s infinite ease-in-out;
}

.typing-indicator span:nth-child(2) { animation-delay: 0.2s; }
.typing-indicator span:nth-child(3) { animation-delay: 0.4s; }

@keyframes typing-animation {
  0%, 80%, 100% { transform: translateY(0) translateZ(0); }
  40%           { transform: translateY(-6px) translateZ(0); }
}

.chat-options-container {
  padding: 20px 24px;
  border-top: 1px solid var(--border-color);
  background: rgba(0,0,0,0.03);
  display: flex;
  flex-wrap: wrap;
  gap: 10px;
}

.chat-option-btn {
  padding: 10px 18px;
  font-family: var(--font-sans);
  font-size: 12px;
  font-weight: 500;
  background: transparent;
  color: var(--text-secondary);
  border: 1px solid var(--border-color);
  border-radius: 30px;
  transition: var(--transition-fast);
}

.chat-option-btn:hover {
  color: var(--text-primary);
  border-color: var(--text-secondary);
  background: rgba(0, 0, 0, 0.03);
}

.chat-option-btn:active {
  transform: scale(0.97);
}

@media (max-width: 768px) {
  .dialogue-left {
    grid-column: span 4;
    margin-bottom: 40px;
  }
  .dialogue-right {
    grid-column: span 4;
  }
  .chat-terminal {
    height: 420px;
  }
}

/* ==========================================================================
   Footer Section
   ========================================================================== */
.footer-section {
  background-color: var(--bg-secondary);
  padding: 80px 0 40px 0;
  border-top: 1px solid var(--border-color);
}

.marquee-container {
  overflow: hidden;
  white-space: nowrap;
  width: 100%;
  padding-bottom: 80px;
  border-bottom: 1px solid var(--border-color);
  margin-bottom: 80px;
}

.marquee-track {
  display: inline-block;
  will-change: transform;
  animation: marquee 25s linear infinite;
}

.marquee-item {
  font-size: clamp(3rem, 8vw, 8rem);
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: -0.01em;
  color: rgba(0, 0, 0, 0.04);
  padding-right: 60px;
}

.marquee-item span {
  color: var(--accent);
}

@keyframes marquee {
  0% { transform: translate3d(0, 0, 0); }
  100% { transform: translate3d(-50%, 0, 0); }
}

.footer-layout {
  grid-column: 1 / -1;
  display: grid;
  grid-template-columns: repeat(12, 1fr);
  gap: 30px;
  margin-bottom: 80px;
}

.footer-brand {
  grid-column: 1 / 6;
}

.footer-logo {
  font-size: 28px;
  text-transform: uppercase;
  font-weight: 600;
  margin-bottom: 20px;
}

.footer-credo {
  color: var(--text-secondary);
  max-width: 320px;
  font-size: 14px;
}

.footer-links-grid {
  grid-column: 7 / -1;
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: 30px;
}

.col-title {
  font-size: 11px;
  text-transform: uppercase;
  color: var(--text-muted);
  letter-spacing: 0.15em;
  font-weight: 600;
  margin-bottom: 24px;
}

.footer-link {
  display: block;
  font-size: 14px;
  color: var(--text-secondary);
  text-decoration: none;
  margin-bottom: 12px;
  transition: color 0.3s;
}

.footer-link:hover {
  color: var(--accent);
}

.footer-bottom {
  grid-column: 1 / -1;
  display: flex;
  justify-content: space-between;
  border-top: 1px solid var(--border-color);
  padding-top: 40px;
  font-size: 12px;
  color: var(--text-muted);
}

@media (max-width: 768px) {
  .footer-layout {
    display: flex;
    flex-direction: column;
    gap: 50px;
  }
  .footer-brand {
    grid-column: span 4;
  }
  .footer-links-grid {
    grid-column: span 4;
  }
  .footer-bottom {
    flex-direction: column;
    gap: 16px;
  }
}

/* ==========================================================================
   Case Study Sliding Modal Overlay
   ========================================================================== */
.case-study-overlay {
  position: fixed;
  top: 0;
  left: 0;
  width: 100vw;
  height: 100vh;  /* fallback for engines without dvh */
  height: 100dvh;
  background-color: var(--bg-primary);
  z-index: 2000;
  overflow-y: auto;
  transform: translateY(100%);
  display: none; /* Managed by JS display blocks */
}

/* Custom Scrollbar for case study modal */
.case-study-overlay::-webkit-scrollbar {
  width: 6px;
}
.case-study-overlay::-webkit-scrollbar-track {
  background: var(--bg-primary);
}
.case-study-overlay::-webkit-scrollbar-thumb {
  background: var(--text-muted);
}

.overlay-close-btn {
  position: fixed;
  top: 40px;
  right: 40px;
  width: 48px;
  height: 48px;
  border-radius: 50%;
  border: 1px solid var(--border-color);
  background: rgba(255, 255, 255, 0.7);
  backdrop-filter: blur(10px);
  color: var(--text-primary);
  z-index: 100;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 4px;
  transition: var(--transition-fast);
}

.overlay-close-btn:hover {
  background: var(--accent);
  color: #fff3eb;
  border-color: var(--accent);
}

.close-icon-line {
  width: 16px;
  height: 1.5px;
  background-color: currentColor;
  display: block;
}

.overlay-close-btn:hover .close-icon-line {
  background-color: #fff3eb;
}

.overlay-close-btn .close-icon-line:nth-child(1) {
  transform: translateY(2.75px) rotate(45deg);
}

.overlay-close-btn .close-icon-line:nth-child(2) {
  transform: translateY(-2.75px) rotate(-45deg);
}

.overlay-scroll-container {
  max-width: 1000px;
  margin: 0 auto;
  padding: 160px 40px 100px 40px;
}

.overlay-header {
  margin-bottom: 60px;
}

.overlay-category {
  font-size: 12px;
  text-transform: uppercase;
  color: var(--accent);
  letter-spacing: 0.15em;
  font-weight: 600;
  margin-bottom: 16px;
  display: inline-block;
}

.overlay-title {
  font-size: clamp(2.5rem, 5vw, 5.5rem);
  text-transform: uppercase;
  font-weight: 500;
  line-height: 1.05;
}

.overlay-image-wrapper {
  width: 100%;
  height: 500px;
  overflow: hidden;
  border-radius: 16px;
  border: 1px solid var(--border-color);
  margin-bottom: 60px;
}

.overlay-image-wrapper img {
  width: 100%;
  height: 100%;
  object-fit: cover;
}

.overlay-content-grid {
  display: grid;
  grid-template-columns: 3fr 7fr;
  gap: 60px;
}

.overlay-meta-col {
  display: flex;
  flex-direction: column;
  gap: 30px;
}

.meta-item {
  border-bottom: 1px solid var(--border-color);
  padding-bottom: 16px;
}

.meta-label {
  font-size: 11px;
  text-transform: uppercase;
  color: var(--text-muted);
  display: block;
  margin-bottom: 6px;
  letter-spacing: 0.05em;
}

.meta-val {
  font-size: 15px;
  color: var(--text-primary);
  font-weight: 500;
}

.overlay-body-col {
  display: flex;
  flex-direction: column;
  gap: 30px;
}

.overlay-lead {
  font-size: clamp(18px, 1.4vw, 24px);
  line-height: 1.5;
  color: var(--text-primary);
  font-weight: 300;
}

.overlay-rich-text {
  color: var(--text-secondary);
  font-size: 16px;
  line-height: 1.7;
}

.overlay-rich-text p {
  margin-bottom: 24px;
}

.overlay-rich-text h3 {
  color: var(--text-primary);
  font-size: 20px;
  margin: 40px 0 20px 0;
  text-transform: uppercase;
}

@media (max-width: 768px) {
  .overlay-close-btn {
    top: 20px;
    right: 20px;
  }
  .overlay-scroll-container {
    padding: 100px 20px 60px 20px;
  }
  .overlay-image-wrapper {
    height: 300px;
  }
  .overlay-content-grid {
    grid-template-columns: 1fr;
    gap: 40px;
  }
}

/* ==========================================================================
   Case Study Overlay (Phase 1) — Aura-only overlay, scoped to #csOverlay
   so it no longer collides with the shared .case-study-overlay class used
   by the generic Kinetic/Nordic/Synapse modal above.
   ========================================================================== */
#csOverlay.case-study-overlay {
  z-index: 99999; /* Ensure it is above all headers and side navigation */
  background: #0d0d0d;
  display: block; /* This overlay is shown/hidden via transform + .is-open, not display */
  will-change: transform; /* GPU-accelerate the slide transition */
  transition: transform 0.85s var(--ease-in-out);
  pointer-events: auto;
  /* iOS momentum scrolling */
  -webkit-overflow-scrolling: touch;
}
#csOverlay.case-study-overlay.is-open {
  transform: translateY(0);
}

/* Scrollable body inside overlay — smooth iOS scroll */
.cs-scroll-body {
  -webkit-overflow-scrolling: touch;
  overflow-y: auto;
}

/* STICKY HEADER */
.cs-header {
  position: sticky;
  top: 0;
  width: 100%;
  height: 70px;
  background: rgba(13, 13, 13, 0.95);
  -webkit-backdrop-filter: blur(10px); /* Safari support */
  backdrop-filter: blur(10px);
  border-bottom: 1px solid rgba(255, 255, 255, 0.08);
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 0 40px;
  z-index: 10;
  color: #fff;
  font-family: 'Onest', sans-serif;
}
.cs-back-btn {
  background: transparent;
  border: 1px solid rgba(255, 255, 255, 0.25);
  color: #fff;
  padding: 8px 18px;
  border-radius: 99px;
  font-size: 11px;
  font-weight: 500;
  letter-spacing: 0.05em;
  cursor: pointer;
  transition: all 0.3s ease;
}
.cs-back-btn:hover {
  border-color: #FF6B00;
  color: #FF6B00;
}

/* HERO LAYOUT (2 Columns) */
.cs-hero-section {
  display: grid;
  grid-template-columns: 45% 55%;
  gap: 40px;
  padding: 80px 80px 100px;
  background: #0d0d0d;
  color: #fff;
  align-items: center;
  min-height: 85vh;
}

/* ROTATING STAMP BADGE */
@keyframes curViewSpin {
  0% { transform: rotate(0deg); }
  100% { transform: rotate(360deg); }
}
.cs-stamp-wrap {
  position: relative;
  width: 96px;
  height: 96px;
  margin-bottom: 32px;
}
.cs-stamp-ring {
  animation: curViewSpin 10s linear infinite;
  transform-origin: center;
}
.cs-stamp-ring text {
  font-family: 'Onest', sans-serif;
  font-size: 7px;
  font-weight: 600;
  fill: rgba(255, 255, 255, 0.65);
  letter-spacing: 1px;
}
.cs-stamp-star {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  font-size: 20px;
  color: #FF6B00;
}

/* TYPOGRAPHY */
.cs-category-pill {
  display: inline-block;
  padding: 6px 14px;
  background: rgba(255, 107, 0, 0.1);
  border: 1px solid rgba(255, 107, 0, 0.3);
  color: #FF6B00;
  border-radius: 99px;
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.06em;
  margin-bottom: 24px;
}
.cs-title {
  font-size: clamp(48px, 6vw, 72px);
  font-weight: 700;
  margin-bottom: 16px;
  line-height: 1.1;
}
.cs-tagline {
  font-size: 20px;
  color: #b0b0b0;
  line-height: 1.5;
}
.cs-tagline i {
  font-family: 'Instrument Serif', serif;
  font-style: italic;
  color: #fff;
}

@media (max-width: 992px) {
  .cs-hero-section {
    grid-template-columns: 1fr;
    padding: 60px 30px 80px;
  }
  .cs-header {
    padding: 0 20px;
  }
}

/* ==========================================================================
   Contact Modal — full-screen shutter panel with brand-black bg + orange CTA
   ========================================================================== */
.contact-modal {
  position: fixed;
  inset: 0;
  z-index: var(--z-modal);
  pointer-events: none;
  visibility: hidden;
  /* On close, keep the modal visible for the full shutter-up animation, then hide */
  transition: visibility 0s linear 0.75s;
}
.contact-modal[aria-hidden="false"] {
  visibility: visible;
  pointer-events: auto;
  transition-delay: 0s;
}

.contact-modal__backdrop {
  position: absolute;
  inset: 0;
  background: rgba(13, 13, 13, 0.55);
  opacity: 0;
  transition: opacity 0.55s var(--ease-in-out);
  cursor: none;
}
/* backdrop-filter only while open — see the note on .svc-modal__backdrop.
   Declared unconditionally it held a permanent full-viewport blur layer that
   Safari kept sampling on every scroll frame, for a closed dialog. */
.contact-modal[aria-hidden="false"] .contact-modal__backdrop {
  opacity: 1;
  backdrop-filter: blur(6px);
  -webkit-backdrop-filter: blur(6px);
}

/* The panel drops from the top like a shutter, covering the full viewport */
.contact-modal__panel {
  position: absolute;
  top: 0;
  left: 0;
  width: 100vw;
  height: 100vh;  /* fallback for engines without dvh */
  height: 100dvh;
  background: #0d0d0d;
  color: #fff3eb;
  transform: translateY(-100%);
  transition: transform 0.75s var(--ease-in-out);
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 32px;
  overflow: hidden;
}
.contact-modal[aria-hidden="false"] .contact-modal__panel {
  transform: translateY(0);
  will-change: transform;
}

/* Subtle grain / vignette to give the black surface depth */
.contact-modal__panel::before {
  content: "";
  position: absolute;
  inset: 0;
  background:
    radial-gradient(ellipse at 50% 0%, rgba(255, 145, 77, 0.08), transparent 60%),
    radial-gradient(ellipse at 100% 100%, rgba(255, 145, 77, 0.05), transparent 60%);
  pointer-events: none;
}

.contact-modal__close {
  position: absolute;
  top: 32px;
  right: 32px;
  width: 52px;
  height: 52px;
  background: transparent;
  border: 1px solid rgba(255, 243, 235, 0.2);
  border-radius: 50%;
  color: #fff3eb;
  cursor: none;
  display: flex;
  align-items: center;
  justify-content: center;
  transition: background-color 0.35s var(--ease), transform 0.4s var(--ease), border-color 0.35s;
  z-index: 3;
}
.contact-modal__close:hover {
  background: rgba(255, 243, 235, 0.08);
  border-color: rgba(255, 243, 235, 0.4);
  transform: rotate(90deg);
}
.contact-modal__close svg {
  width: 22px;
  height: 22px;
}

/* Logo top-left — position, height & width are synced to the header logo
   at runtime by JS so the shutter drop reads as "the same logo just changed
   to white." Uses the same SVG source, recolored via CSS filter. */
.contact-modal__logo {
  position: absolute;
  display: block;
  cursor: none;
  z-index: 3;
  text-decoration: none;
  opacity: 0;
  transition: opacity 0.35s var(--ease-in-out) 0.55s;
  will-change: opacity;
}
.contact-modal[aria-hidden="false"] .contact-modal__logo {
  opacity: 1;
}
.contact-modal__logo-img {
  width: 100%;
  height: 100%;
  display: block;
  object-fit: contain;
  /* Recolor the coloured header SVG to pure white for the dark modal */
  filter: brightness(0) invert(1);
}
.contact-modal__logo:hover {
  opacity: 0.85;
}

/* Cursor is orange while modal is open (black arrow would be invisible on black bg) */
body.modal-open .custom-cursor {
  background-color: #ff914d;
  filter: drop-shadow(0 2px 5px rgba(255, 145, 77, 0.5));
}

.contact-modal__inner {
  position: relative;
  z-index: 1;
  width: min(560px, 90vw);
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
  gap: 24px;
  opacity: 0;
  transform: translateY(20px);
  transition: opacity 0.5s var(--ease) 0.25s, transform 0.6s var(--ease) 0.25s;
}
.contact-modal[aria-hidden="false"] .contact-modal__inner {
  opacity: 1;
  transform: translateY(0);
}

.contact-modal__title {
  font-family: 'Barlow Condensed', 'Onest', sans-serif;
  font-size: clamp(2.5rem, 5.5vw, 4.6rem);
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.005em;
  line-height: 0.95;
  margin: 0;
}
.contact-modal__subtitle {
  font-family: var(--font-sans);
  font-size: clamp(14px, 1vw, 16px);
  font-weight: 400;
  line-height: 1.5;
  margin: 0;
  opacity: 0.7;
  max-width: 42ch;
}

.contact-modal__actions {
  width: 100%;
  display: flex;
  flex-direction: column;
  gap: 14px;
  margin-top: 20px;
}

.contact-modal__btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 12px;
  padding: 20px 32px;
  border-radius: 999px;
  font-family: var(--font-sans);
  font-size: 15px;
  font-weight: 600;
  text-decoration: none;
  transition: transform 0.4s var(--ease), background-color 0.35s, color 0.35s, border-color 0.35s;
  cursor: none;
  border: 2px solid transparent;
  position: relative;
}
.contact-modal__btn svg {
  transition: transform 0.4s var(--ease);
}

/* Primary — orange filled */
.contact-modal__btn--filled {
  background: #ff914d;
  color: #0d0d0d;
  border-color: #ff914d;
}
.contact-modal__btn--filled:hover {
  background: #ff7f2e;
  border-color: #ff7f2e;
  transform: translateY(-2px);
}
.contact-modal__btn--filled:hover svg {
  transform: translateX(4px);
}

/* Secondary — cream outline */
.contact-modal__btn--outline {
  background: transparent;
  color: #fff3eb;
  border-color: rgba(255, 243, 235, 0.4);
}
.contact-modal__btn--outline:hover {
  background: #fff3eb;
  color: #0d0d0d;
  border-color: #fff3eb;
  transform: translateY(-2px);
}
.contact-modal__btn--outline:hover svg {
  transform: translateX(4px);
}

/* Lock body scroll while modal open */
body.modal-open {
  overflow: hidden;
}

@media (max-width: 640px) {
  .contact-modal__close {
    top: 20px;
    right: 20px;
    width: 44px;
    height: 44px;
  }
  .contact-modal__inner { gap: 20px; }
  .contact-modal__btn { padding: 18px 24px; font-size: 14px; }
}


/* ==========================================================================
   Projects — poster statement with scroll-driven horizontal reveal
   ========================================================================== */
.projects-section {
  background: #f2e6d6;
  padding: clamp(80px, 10vw, 140px) clamp(24px, 4vw, 72px);
  overflow: hidden;
  position: relative;
}

/* ScrollTrigger wraps a pinned section in a .pin-spacer that paints nothing.
   Any pinned section shorter than the viewport therefore leaves a strip
   inside that spacer where main's #fff3eb shows through — a pale band under
   the statement for the whole pin (59px at 1440x900, 359px at 1440x1200).
   Painting the spacer to match its section closes the gap without touching
   layout. Browsers without :has() simply get today's behaviour. */
/* .process-v2-section was dropped from this list when it stopped being pinned
   — it uses position:sticky now, so it never gets a pin spacer. */
.pin-spacer:has(> .projects-section) {
  background: #f2e6d6;
}

.projects-container {
  max-width: 1720px;
  margin: 0 auto;
  display: grid;
  grid-template-columns: 1fr 1fr;
  grid-template-areas:
    "eyebrow    ."
    "statement  statement"
    ".          description";
  gap: clamp(32px, 5vw, 72px) 24px;
  align-items: end;
}

.projects-eyebrow { grid-area: eyebrow; }
.projects-statement { grid-area: statement; }
.projects-desc { grid-area: description; justify-self: end; }

.projects-tag {
  font-family: 'JetBrains Mono', monospace;
  font-size: 11px;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  opacity: 0.55;
  color: #1d1c1c;
  display: block;
  margin-bottom: 6px;
}
.projects-headline {
  font-family: 'Barlow Condensed', 'Onest', sans-serif;
  font-size: clamp(1.05rem, 1.3vw, 1.4rem);
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.015em;
  margin: 0;
  color: #1d1c1c;
  line-height: 1.1;
}

.projects-statement {
  font-size: clamp(2.4rem, 7.5vw, 8.5rem);
  line-height: 0.88;
}

.ps-line {
  display: flex;
  align-items: baseline;
  flex-wrap: nowrap;
  gap: 0.16em;
  will-change: transform, opacity;
}

.ps-ghost,
.ps-solid {
  font-family: 'Barlow Condensed', 'Onest', sans-serif;
  font-size: inherit;
  font-weight: 800;
  text-transform: uppercase;
  line-height: 0.88;
  letter-spacing: -0.005em;
  white-space: nowrap;
}
/* Ghost text reads as a subtle "watermark" against the warm off-white bg */
.ps-ghost { color: rgba(29, 28, 28, 0.18); }
.ps-solid { color: #0d0d0d; }

/* Inline icon slot — sized proportionally to the poster type it lives beside */
.ps-icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  position: relative;
  width: 0.75em;
  height: 0.75em;
  vertical-align: middle;
  flex-shrink: 0;
  transform: translateY(-0.05em);
}
.ps-icon-variant {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  opacity: 0;
  transform: scale(0.7) rotate(-8deg);
  transition: opacity 0.5s var(--ease-in-out),
              transform 0.5s var(--ease-out);
}
.ps-icon-variant.is-active {
  opacity: 1;
  transform: scale(1) rotate(0deg);
}
.ps-icon-variant svg {
  width: 100%;
  height: 100%;
  display: block;
}

.projects-desc {
  font-family: var(--font-sans);
  font-size: clamp(13px, 0.95vw, 15px);
  line-height: 1.6;
  color: #0d0d0d;
  opacity: 1;
  margin: 0;
  max-width: 34ch;
}

/* Initial hidden states — GSAP overrides these on scroll. Fallback: content
   remains functional if JS fails to init (no display:none). */
.projects-statement .ps-line,
.projects-desc,
.projects-eyebrow {
  opacity: 1;
}

/* Responsive layout */
@media (max-width: 900px) {
  .projects-container {
    grid-template-columns: 1fr;
    grid-template-areas:
      "eyebrow"
      "statement"
      "description";
  }
  .projects-desc { justify-self: start; max-width: 100%; }
  .ps-line { flex-wrap: wrap; }
  .ps-ghost, .ps-solid { font-size: clamp(2.4rem, 12vw, 5rem); }
  .ps-icon { width: 0.85em; height: 0.85em; }
}


/* ==========================================================================
   Selected Work — masonry showcase gallery
   Continues the projects background so statement and gallery read as one
   block. Cards carry no resting caption: the title, tags and description
   live in an overlay that fades in on hover, so the artwork is the only
   thing competing for attention at rest. Namespaced `showcase-` to stay
   clear of the legacy `.works-*` rules further up this file.
   ========================================================================== */
.showcase-section {
  background: #f2e6d6;
  /* Bottom trimmed from 10vw/150px. This section and .process-v2-section share
     the same cream field by design (see the section-boundary note below), so
     their paddings stack into one visible void rather than two separations. */
  padding: 0 clamp(24px, 4vw, 72px) clamp(56px, 6.5vw, 100px);
  position: relative;
}

.showcase-container {
  max-width: 1720px;
  margin: 0 auto;
}

/* Two independent column rails. Grid rows would tie the columns together —
   a tall card in one would push a gap into the other — so each column is
   its own flex stack that packs from the top. */
.showcase-grid {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: clamp(28px, 3.6vw, 64px);
  align-items: start;
}

.showcase-col {
  display: flex;
  flex-direction: column;
  /* Vertical rhythm is ~2x the column gutter, matching the reference. */
  gap: clamp(40px, 7vw, 120px);
  min-width: 0;
}

.showcase-card {
  display: block;
  position: relative;
  text-decoration: none;
  color: inherit;
  border-radius: 12px;
}

.showcase-card__frame {
  display: block;
  position: relative;
  width: 100%;
  border-radius: 12px;
  overflow: hidden;
  background: #0d0d0d;
  /* Low and tight — enough to hold a light card off a light background
     without reading as a floating UI panel. */
  box-shadow: 0 10px 30px -18px rgba(29, 28, 28, 0.38);
  /* Scroll reveal: the artwork is wiped in from the bottom edge. `round`
     keeps the corner radius intact while clip-path drives the reveal. */
  clip-path: inset(0 0 100% 0 round 12px);
  transition: clip-path 0.65s var(--ease-out);
}
.showcase-card.is-revealed .showcase-card__frame {
  clip-path: inset(0 0 0 0 round 12px);
}

.showcase-card__art {
  display: block;
  width: 100%;
  height: 100%;
}

.showcase-card--wide   .showcase-card__frame { aspect-ratio: 16 / 10; }
.showcase-card--tall   .showcase-card__frame { aspect-ratio: 4 / 5; }
.showcase-card--square .showcase-card__frame { aspect-ratio: 1 / 1; }

/* ── Overlay ──────────────────────────────────────────────────────────────
   Head block sits at the top, description at the bottom. Never display:none
   or visibility:hidden — the text has to stay in the accessibility tree for
   screen readers, which never see the hover. */
.showcase-card__info {
  position: absolute;
  inset: 0;
  border-radius: 12px;
  display: flex;
  flex-direction: column;
  justify-content: space-between;
  padding: clamp(26px, 3vw, 48px);
  /* Banded rather than flat. A uniform 50% scrim reads fine over the dark
     cards but leaves the title and tags illegible over the two cream ones,
     so the gradient goes near-opaque exactly where text sits and opens back
     up across the middle so the artwork still shows through. */
  background: linear-gradient(
    to bottom,
    rgba(8, 8, 8, 0.94) 0%,
    rgba(8, 8, 8, 0.92) 38%,
    rgba(8, 8, 8, 0.44) 52%,
    rgba(8, 8, 8, 0.44) 62%,
    rgba(8, 8, 8, 0.92) 78%,
    rgba(8, 8, 8, 0.94) 100%
  );
  opacity: 0;
  /* Exit is quicker than enter: the user has already moved on. */
  transition: opacity 0.16s var(--ease-state);
}

.showcase-card__head {
  display: flex;
  flex-direction: column;
  gap: clamp(10px, 1vw, 16px);
}

.showcase-card__title {
  font-family: 'Barlow Condensed', 'Onest', sans-serif;
  font-size: clamp(1.6rem, 2.6vw, 2.9rem);
  font-weight: 800;
  text-transform: uppercase;
  letter-spacing: -0.005em;
  line-height: 0.95;
  color: #fdfaf6;
}

.showcase-card__tags {
  display: flex;
  flex-wrap: wrap;
  gap: 6px clamp(14px, 1.4vw, 24px);
}

.showcase-card__tag {
  font-family: 'JetBrains Mono', monospace;
  font-size: clamp(10px, 0.75vw, 12px);
  letter-spacing: 0.16em;
  text-transform: uppercase;
  /* A lift on --accent, not the token itself: #f8853d at 11px only reaches
     ~2.5:1 against the scrim, while this clears 4.5:1. Same hue, same role. */
  color: #ffab74;
  white-space: nowrap;
}

.showcase-card__desc {
  font-family: var(--font-sans);
  font-size: clamp(13px, 1.05vw, 17px);
  line-height: 1.5;
  color: #fdfaf6;
  max-width: 62ch;
}

/* The text rises a touch behind the scrim so the overlay resolves rather
   than simply switching on. Movement stays under 10px — any more and it
   reads as an animation instead of an appearance. */
.showcase-card__head,
.showcase-card__desc {
  transform: translateY(8px);
  transition: transform 0.16s var(--ease-state);
}

@media (hover: hover) and (pointer: fine) {
  .showcase-card:hover .showcase-card__info,
  .showcase-card:focus-visible .showcase-card__info {
    opacity: 1;
    transition-duration: 0.24s;
  }
  .showcase-card:hover .showcase-card__head,
  .showcase-card:hover .showcase-card__desc,
  .showcase-card:focus-visible .showcase-card__head,
  .showcase-card:focus-visible .showcase-card__desc {
    transform: translateY(0);
    transition-duration: 0.26s;
    transition-delay: 0.04s;
  }
}

/* Press feedback — the whole card acknowledges the click */
.showcase-card:active {
  transform: scale(0.995);
  transition: transform 0.14s var(--ease-state);
}

.showcase-card:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 6px;
}

/* ── Touch ────────────────────────────────────────────────────────────────
   No hover means no way to reach the overlay, so the same element becomes a
   caption printed under the card. Same markup, no duplicated copy. */
@media (hover: none), (pointer: coarse) {
  .showcase-card__info {
    position: static;
    background: none;
    padding: clamp(14px, 3vw, 20px) 0 0;
    opacity: 1;
    gap: clamp(10px, 2vw, 14px);
  }
  .showcase-card__head,
  .showcase-card__desc { transform: none; }
  .showcase-card__title { color: #0d0d0d; }
  /* The scrim's lifted orange is for dark backdrops only — printed on cream
     it drops to ~2:1, so the tags fall back to the site's muted mono label. */
  .showcase-card__tag {
    color: #1d1c1c;
    opacity: 0.5;
  }
  .showcase-card__desc {
    color: #1d1c1c;
    opacity: 0.62;
    font-size: clamp(13px, 3.4vw, 15px);
  }
}

@media (max-width: 900px) {
  .showcase-grid {
    grid-template-columns: minmax(0, 1fr);
    gap: 0;
  }
  /* Columns dissolve so every card returns to DOM order in one stack. */
  .showcase-col { display: contents; }
  .showcase-card { margin-bottom: clamp(48px, 10vw, 80px); }
  .showcase-col:last-child .showcase-card:last-child { margin-bottom: 0; }
  /* One column reads better with a consistent frame height than with three
     different aspect ratios stacked on top of each other. */
  .showcase-card--tall .showcase-card__frame,
  .showcase-card--square .showcase-card__frame { aspect-ratio: 4 / 3; }
}

@media (max-width: 575.98px) {
  .showcase-card--wide .showcase-card__frame,
  .showcase-card--tall .showcase-card__frame,
  .showcase-card--square .showcase-card__frame { aspect-ratio: 3 / 2; }
}

/* Motion-sensitive users still get the reveal, just without the wipe or the
   travel — the content simply fades in place. */
@media (prefers-reduced-motion: reduce) {
  .showcase-card__frame {
    clip-path: none;
    opacity: 0;
    transition: opacity 0.3s ease;
  }
  .showcase-card.is-revealed .showcase-card__frame {
    clip-path: none;
    opacity: 1;
  }
  .showcase-card__head,
  .showcase-card__desc { transform: none; transition: none; }
}


/* ==========================================================================
   Our Process (v2) — off-white section with a centered black box.
   Content revealed in stages via a pinned ScrollTrigger timeline.
   ========================================================================== */
.process-v2-section {
  background: var(--bg-secondary);
  /* Asymmetric on purpose. .showcase-section above already ends with 144px of
     padding, so a matching 158px top here stacked into ~300px of dead cream
     before the eyebrow. The incoming section does not need to re-establish
     breathing room the previous one has already given. */
  padding: clamp(36px, 4vw, 64px) clamp(20px, 4vw, 60px) clamp(72px, 8vw, 120px);
  position: relative;
}

/* Header on top, four stages across. A single row makes the sequence read as
   a pipeline left-to-right, which is what the four numbered steps are — the
   earlier vertical stack read as four unrelated panels. */
.process-box {
  max-width: 1400px;
  margin: 0 auto;
}

.process-box__head {
  display: grid;
  grid-template-columns: minmax(0, 1.35fr) minmax(0, 1fr);
  gap: clamp(24px, 4vw, 72px);
  align-items: end;
  margin-bottom: clamp(40px, 5vw, 72px);
}

.process-box__eyebrow {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  margin: 0 0 clamp(18px, 2vw, 28px);
  padding: 10px 18px 10px 14px;
  border: 1px solid rgba(13, 13, 13, 0.2);
  border-radius: 100px;
  font-family: 'JetBrains Mono', monospace;
  font-size: 11px;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--text-primary);
}
.process-box__eyebrow-mark {
  width: 9px;
  height: 9px;
  border-radius: 50%;
  background: var(--accent);
  box-shadow: 0 0 0 4px rgba(var(--accent-rgb), 0.16);
}

/* Sized to hold on one line at desktop. white-space: nowrap is deliberate —
   this headline breaking across two lines was the thing that made the old
   layout feel unbalanced. It is released at the tablet breakpoint. */
.process-box__title {
  font-family: 'Barlow Condensed', 'Onest', sans-serif;
  font-size: clamp(2rem, 4.1vw, 4.2rem);
  font-weight: 800;
  text-transform: uppercase;
  letter-spacing: -0.015em;
  line-height: 0.95;
  white-space: nowrap;
  margin: 0;
  color: var(--text-primary);
}
/* One italic serif accent, matching the hero treatment on about / work /
   services so the pages read as one system. */
.process-box__title em {
  font-family: var(--font-serif);
  font-style: italic;
  font-weight: 400;
  text-transform: none;
  letter-spacing: -0.01em;
  color: var(--accent);
}

.process-box__intro {
  font-family: var(--font-sans);
  font-size: clamp(14px, 1vw, 17px);
  line-height: 1.6;
  color: rgba(13, 13, 13, 0.62);
  margin: 0;
  padding-bottom: 0.4em;
}

.process-box__list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: clamp(12px, 1.2vw, 20px);
}

/* Progressive fill. A card arrives as a cream outline, then fills charcoal —
   the fourth fills brand orange. The row builds from empty to structured to
   live across Discover -> Optimize, which is the content's own argument.

   Orange carries NEAR-BLACK text, never white: #f8853d against #fff3eb is
   2.5:1 and fails WCAG AA, while against #0d0d0d it is 7.6:1. The reference
   this is modelled on used a dark purple, where white works; ours does not. */
.process-item {
  display: flex;
  flex-direction: column;
  padding: clamp(22px, 2vw, 32px);
  border: 1px solid rgba(13, 13, 13, 0.14);
  border-radius: clamp(14px, 1.6vw, 20px);
  background: transparent;
  color: var(--text-primary);
  opacity: 0;
  transform: translateY(var(--rise-lg));
  /* Fill trails arrival by 140ms so the card lands first and then commits,
     rather than both reading as one muddy change. --process-delay carries the
     70ms stagger, set per card in main.js. */
  transition: opacity var(--dur-base) var(--ease-out) var(--process-delay, 0ms),
              transform var(--dur-base) var(--ease-out) var(--process-delay, 0ms),
              background-color var(--dur-base) var(--ease-state) calc(var(--process-delay, 0ms) + 140ms),
              border-color var(--dur-base) var(--ease-state) calc(var(--process-delay, 0ms) + 140ms),
              color var(--dur-base) var(--ease-state) calc(var(--process-delay, 0ms) + 140ms);
}

.process-item.is-in {
  opacity: 1;
  transform: none;
  background: #0d0d0d;
  border-color: #0d0d0d;
  color: var(--bg-primary);
}

.process-item--accent.is-in {
  background: var(--accent);
  border-color: var(--accent);
  color: #0d0d0d;
}

.process-item__icon {
  width: clamp(34px, 2.6vw, 42px);
  height: clamp(34px, 2.6vw, 42px);
  display: block;
  margin-bottom: clamp(20px, 2.4vw, 34px);
}
.process-item__icon svg { width: 100%; height: 100%; display: block; }

.process-item__label {
  display: flex;
  align-items: baseline;
  gap: 10px;
  font-family: 'Barlow Condensed', 'Onest', sans-serif;
  font-size: clamp(1.3rem, 1.7vw, 1.9rem);
  font-weight: 800;
  text-transform: uppercase;
  letter-spacing: -0.01em;
  line-height: 1;
  margin: 0 0 12px;
  color: inherit;
}
.process-item__step {
  font-family: 'JetBrains Mono', monospace;
  font-size: 11px;
  font-weight: 400;
  letter-spacing: 0.16em;
  opacity: 0.5;
}

.process-item__desc {
  font-family: var(--font-sans);
  font-size: clamp(13px, 0.95vw, 15px);
  line-height: 1.55;
  margin: 0 0 clamp(20px, 2.2vw, 30px);
  color: inherit;
  opacity: 0.78;
}

/* Deliverables. Pinned to the bottom so every card's list starts on the same
   baseline regardless of how long its description ran. */
.process-item__points {
  list-style: none;
  margin: auto 0 0;
  padding: 0;
  font-family: var(--font-sans);
  font-size: clamp(12px, 0.85vw, 13.5px);
  line-height: 1.4;
}
.process-item__points li {
  padding: 10px 0;
  border-top: 1px solid currentColor;
  /* The rule is the same ink as the text at low alpha, so it inverts with the
     card fill instead of needing a value per surface. */
  border-top-color: color-mix(in srgb, currentColor 22%, transparent);
  opacity: 0.86;
}
.process-item__points li:last-child { padding-bottom: 0; }

@media (max-width: 1100px) {
  .process-box__list { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

@media (max-width: 860px) {
  .process-box__head {
    grid-template-columns: 1fr;
    align-items: start;
    gap: clamp(16px, 3vw, 24px);
  }
  /* Released here: below this width the headline cannot hold one line without
     becoming too small to read. */
  .process-box__title { white-space: normal; font-size: clamp(2rem, 7vw, 3rem); }
  .process-box__intro { padding-bottom: 0; }
}

@media (max-width: 620px) {
  .process-box__list { grid-template-columns: 1fr; }
}
}



/* ==========================================================================
   Services — Bento grid on tablet (576-991px), stacked on mobile (<576px).
   Play-once fade-up animation on scroll into viewport. All hover content
   promoted to always-visible per card (no hover swap on touch).
   ========================================================================== */
@media (max-width: 991.98px) {
  .services-grid-section {
    padding: 60px 20px 80px;
    background: var(--bg-primary);
  }
  .services-grid {
    display: grid;
    gap: 16px;
    border-top: none;
  }

  .svc-card,
  .svc-card--full,
  .svc-card--half,
  .svc-card--compact,
  .svc-card--poster {
    grid-column: auto !important;
    width: 100% !important;
    max-width: 100% !important;
    min-width: 0 !important;
    min-height: auto !important;
    padding: 32px 28px 36px !important;
    display: flex !important;
    flex-direction: column !important;
    gap: 16px !important;
    border-right: none !important;
    border-bottom: none !important;
    border-radius: 14px;
    /* Play-once reveal — starts hidden, GSAP fades in on scroll into view */
    opacity: 0;
    transform: translateY(28px);
  }
  .svc-card.svc-visible {
    opacity: 1;
    transform: translateY(0);
    transition: opacity 0.7s var(--ease-out),
                transform 0.7s var(--ease-out);
  }

  /* Icon + content flow vertically inside each card */
  .svc-card__lhs { display: contents !important; }
  .svc-card__icon {
    width: 52px !important;
    height: 52px !important;
    transform: none !important;
    align-self: flex-start;
  }
  .svc-card__content,
  .svc-card__body {
    position: static !important;
    min-height: 0 !important;
    display: block !important;
    align-self: auto !important;
  }
  .svc-card__idle,
  .svc-card__hover {
    position: static !important;
    opacity: 1 !important;
    transform: none !important;
    pointer-events: auto !important;
    inset: auto !important;
  }
  .svc-card__idle { margin-bottom: 12px; }
  .svc-card__title {
    font-size: clamp(1.6rem, 5vw, 2.2rem) !important;
    line-height: 1.02 !important;
  }
  .svc-card__subhead {
    font-size: clamp(1rem, 3.2vw, 1.35rem) !important;
    margin: 4px 0 10px !important;
  }
  .svc-card__desc {
    font-size: 14px !important;
    max-width: 100% !important;
    opacity: 0.88 !important;
  }
  /* Right-side label collapsed on touch — swap animation retired */
  .svc-card__topright,
  .svc-card__rhs {
    position: static !important;
    margin: 0 !important;
    min-width: 0 !important;
    height: auto !important;
    overflow: visible !important;
    order: 99;
  }
  .svc-card__topright .svc-label,
  .svc-card__rhs .svc-label {
    position: static !important;
    transform: none !important;
    display: block !important;
  }
  .svc-card__topright .svc-label--idle,
  .svc-card__rhs .svc-label--idle { display: none !important; }
  .svc-card__footer { padding: 0 !important; }
}

/* Tablet 576-991px: 2×2 bento grid */
@media (min-width: 576px) and (max-width: 991.98px) {
  .services-grid { grid-template-columns: 1fr 1fr; }
}

/* Mobile <576px: single-column stack */
@media (max-width: 575.98px) {
  .services-grid { grid-template-columns: 1fr; }
  .services-grid-section { padding: 48px 16px 64px; }
}

/* ==========================================================================
   Projects — text wrap + sizing fixes for tablet + mobile (<992px)
   IMPORTANT: The scroll-in slide animation + icon variant swap logic in JS
   are untouched — only layout/typography CSS is adjusted here.
   ========================================================================== */


/* ==========================================================================
   Projects — text wrap + sizing fixes for tablet + mobile (<992px)
   IMPORTANT: The scroll-in slide animation + icon variant swap logic in JS
   are untouched — only layout/typography CSS is adjusted here.
   ========================================================================== */
@media (max-width: 991.98px) {
  .projects-section {
    min-height: 100svh;
    padding: 100px 24px 60px;
    display: flex;
    align-items: center;
  }
  .projects-container {
    grid-template-columns: 1fr;
    grid-template-areas: "eyebrow" "statement" "description";
    gap: 24px;
    width: 100%;
  }
  .projects-desc {
    justify-self: start;
    max-width: 100%;
    font-size: 15px;
    line-height: 1.55;
  }
  .projects-statement {
    font-size: clamp(3rem, 13vw, 4.6rem);
    line-height: 0.95;
  }
  .ps-line { flex-wrap: wrap; gap: 0.08em 0.16em; }
  .ps-ghost, .ps-solid { white-space: normal; }
  .ps-icon {
    width: 0.85em;
    height: 0.85em;
    transform: translateY(-0.02em);
  }
}
@media (max-width: 575.98px) {
  .projects-section { padding: 90px 24px 60px; }
  .projects-statement {
    font-size: clamp(2.8rem, 13vw, 4.2rem);
    line-height: 0.95;
  }

  /* Line 1 — "WE HAVE" over "COMPLETED" (poster stack). */
  .ps-line--1 {
    flex-wrap: wrap;
    row-gap: 0.02em;
    column-gap: 0.16em;
  }
  .ps-line--1 .ps-ghost { flex: 1 0 100%; }

  /* Line 2 — "MORE THAN [icon] 100" kept on ONE row at fitting scale. */
  .ps-line--2 {
    flex-wrap: nowrap;
    align-items: center;
    gap: 0.12em;
  }
  .ps-line--2 .ps-icon {
    width: 0.55em;
    height: 0.55em;
    transform: translateY(-0.02em);
  }

  /* Line 3 — "EXCEPTIONAL" alone, then "[icon] PROJECTS." on next row. */
  .ps-line--3 {
    flex-wrap: wrap;
    align-items: center;
    row-gap: 0.02em;
    column-gap: 0.16em;
  }
  .ps-line--3 .ps-solid--exceptional {
    order: -1;
    flex: 1 0 100%;
  }
  .ps-line--3 .ps-icon {
    width: 0.72em;
    height: 0.72em;
    transform: translateY(-0.02em);
  }
}



/* ==========================================================================
   Section boundary fades — Services → Projects → Process should read as one
   continuous cream field. Using identical solid backgrounds on Projects and
   Process guarantees zero visible boundary. Prior linear-gradient attempts
   created a perceived "line" because the shading direction reversed at the
   join, which the eye reads as an edge.
   ========================================================================== */
.projects-section,
.process-v2-section {
  background: #f2e6d6;
}

/* Ensure the pinned poster & process box always sit above their bg */
.projects-container,
.process-box,
.services-grid {
  position: relative;
  z-index: 2;
}

/* ==========================================================================
   Outro — final black fold with continuously-marqueeing headline. Lines 1 & 2
   drift left→right, Line 3 drifts right→left. Fully CSS-driven — no scroll
   scrub — so motion is always alive regardless of scroll position.
   ========================================================================== */
.outro-section {
  position: relative;
  background: #0a0a0a;
  color: #ffffff;
  min-height: 92svh;
  padding: clamp(48px, 5vw, 88px) 0 clamp(72px, 6vw, 108px);
  overflow: hidden;
  display: flex;
  flex-direction: column;
  justify-content: center;
  isolation: isolate;
}

/* Subtle vignette so the centered CTA reads slightly against the type */
.outro-section::before {
  content: "";
  position: absolute;
  inset: 0;
  background: radial-gradient(
    ellipse at 50% 50%,
    rgba(10, 10, 10, 0) 0%,
    rgba(10, 10, 10, 0.30) 60%,
    rgba(10, 10, 10, 0.6) 100%
  );
  pointer-events: none;
  z-index: 1;
}

/* Headline — three stacked marquee rows, each occupies ~1/3 of the fold */
.outro-headline {
  position: relative;
  z-index: 2;
  display: flex;
  flex-direction: column;
  gap: clamp(4px, 0.6vw, 12px);
  padding: 0;
  margin: 0 auto;
  width: 100%;
}

.outro-line {
  position: relative;
  width: 100%;
  overflow: hidden;
}

/* Track holds N copies of the text; JS drives an infinite modulo-wrapped
   translate (see main.js: outroMarquee) so the loop has NO visible reset. */
.outro-section.in-view .outro-line__track { will-change: transform; }

.outro-line__track {
  display: flex;
  width: max-content;
  /* will-change is applied only while the outro is near the viewport — see the
     .outro-section.in-view rule just above. These are the largest layers on
     the page (three tracks, up to 7200px wide). */
  transform: translate3d(0, 0, 0);
}

/* Direction attributes exist purely for the JS to read — no CSS animation. */
.outro-line[data-outro-dir="1"] .outro-line__track,
.outro-line[data-outro-dir="-1"] .outro-line__track {
  animation: none;
}

/* Individual text copy — huge, brand sans, tight leading */
.outro-line__text {
  display: inline-block;
  flex-shrink: 0;
  padding-right: 0.5em; /* gap between duplicated copies, part of measured width */
  font-family: var(--font-sans, 'Onest'), 'Helvetica Neue', sans-serif;
  font-weight: 900;
  font-size: clamp(4rem, 26svh, 22rem);
  line-height: 0.88;
  letter-spacing: -0.045em;
  text-transform: uppercase;
  color: #ffffff;
  white-space: nowrap;
  -webkit-font-smoothing: antialiased;
}

/* Centered orange CTA overlapping the type */
.outro-cta {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  z-index: 5;
  display: inline-flex;
  align-items: center;
  gap: 16px;
  padding: 20px 30px 20px 36px;
  background: var(--accent, #ff6b3d);
  color: #ffffff;
  border-radius: 999px;
  text-decoration: none;
  font-family: var(--font-sans, 'Onest'), sans-serif;
  font-weight: 500;
  font-size: clamp(15px, 1.1vw, 19px);
  letter-spacing: 0.02em;
  box-shadow: 0 20px 60px -18px rgba(255, 107, 61, 0.55),
              0 6px 20px -10px rgba(0, 0, 0, 0.6);
  transition: transform 0.35s var(--ease-out),
              box-shadow 0.35s var(--ease-out),
              background-color 0.25s ease;
}

.outro-cta:hover,
.outro-cta:focus-visible {
  transform: translate(-50%, calc(-50% - 4px));
  background: #ff7a4f;
  box-shadow: 0 32px 80px -18px rgba(255, 107, 61, 0.65),
              0 10px 26px -10px rgba(0, 0, 0, 0.7);
  outline: none;
}

.outro-cta__label {
  font-family: var(--font-sans, 'Onest'), sans-serif;
  font-weight: 500;
}

.outro-cta__arrow {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 34px;
  height: 34px;
  border-radius: 999px;
  background: rgba(255, 255, 255, 0.18);
  color: #ffffff;
  transition: transform 0.35s var(--ease-out),
              background-color 0.25s ease;
}

.outro-cta:hover .outro-cta__arrow {
  background: rgba(255, 255, 255, 0.28);
  transform: translate(2px, -2px);
}

.outro-cta__arrow svg {
  width: 15px;
  height: 15px;
  display: block;
}

/* Meta line anchored bottom-left — brand sans, not mono */
.outro-meta {
  position: absolute;
  left: clamp(24px, 4vw, 72px);
  right: clamp(24px, 4vw, 72px);
  bottom: clamp(20px, 2.6vw, 40px);
  z-index: 3;
  display: inline-flex;
  align-items: center;
  gap: 10px;
  font-family: var(--font-sans, 'Onest'), sans-serif;
  font-weight: 400;
  font-size: 12px;
  letter-spacing: 0.04em;
  text-transform: none;
  color: rgba(255, 255, 255, 0.55);
}
.outro-meta__sep { opacity: 0.5; }
.outro-meta__link {
  color: rgba(255, 255, 255, 0.78);
  text-decoration: none;
  transition: color 0.2s ease;
}
.outro-meta__link:hover { color: #ffffff; }

/* Respect reduced-motion preference — halt marquee for users who ask */
@media (prefers-reduced-motion: reduce) {
  .outro-line__track { transform: translate3d(-8%, 0, 0); }
}

/* Responsive tuning */
@media (max-width: 991.98px) {
  .outro-section {
    min-height: 86svh;
    padding: clamp(52px, 7vw, 82px) 0 clamp(70px, 8vw, 96px);
  }
  .outro-line__text { font-size: clamp(3.4rem, 24svh, 14rem); }
  .outro-cta { padding: 16px 24px 16px 28px; gap: 12px; }
  .outro-cta__arrow { width: 30px; height: 30px; }
  .outro-meta { font-size: 11px; }
}

@media (max-width: 575.98px) {
  .outro-section {
    min-height: 80svh;
    padding: 84px 0 92px;
  }
  .outro-line__text {
    font-size: clamp(2.8rem, 22svh, 8rem);
    letter-spacing: -0.035em;
  }
  .outro-cta {
    padding: 14px 20px 14px 24px;
    gap: 10px;
    font-size: 13px;
  }
  .outro-cta__arrow { width: 26px; height: 26px; }
  .outro-cta__arrow svg { width: 13px; height: 13px; }
  .outro-meta {
    left: 20px;
    right: 20px;
    bottom: 20px;
    font-size: 10px;
    gap: 8px;
  }
}


/* ==========================================================================
   Hero — Kinetic 3-stage choreography (icons enter → stack + text emerges
   → shuffle to embedded row positions + labels + CTA reveal).
   The layout is a centered stack of 3 huge type rows with an "icon slot"
   between the left and right phrase of each row. Icons are absolutely
   positioned inside .hero-icons and driven by GSAP over the intro timeline.
   ========================================================================== */

/* Keep existing .hero-section rules; just override centering for kinetic mode */
.hero-content--kinetic {
  position: relative;
  width: 100%;
  height: 100%;
  min-height: min(88svh, 900px);
  z-index: 2;
  display: block; /* override the flex column of the old hero-content */
  text-align: initial;
}

/* ── Kinetic headline ─────────────────────────────────────────────────── */
.hero-title--kinetic {
  position: relative;
  z-index: 3;
  margin: 0;
  /* Bottom padding kept tight so row 3 sits close to the section end — no dead
     empty gap between EXPERIENCES and the next section. */
  padding: clamp(70px, 9vh, 110px) 0 clamp(24px, 3vh, 44px);
  display: flex;
  flex-direction: column;
  align-items: stretch;
  justify-content: space-around;
  gap: 0;
  height: 100%;
  min-height: min(80svh, 820px);
  font-family: var(--font-sans, 'Onest'), sans-serif;
  font-weight: 900;
  text-transform: uppercase;
  letter-spacing: -0.04em;
  line-height: 0.88;
  color: #0d0d0d;
}

.hero-row {
  display: flex;
  align-items: center;
  justify-content: flex-start;
  gap: clamp(0.14em, 0.6vw, 0.28em);
  font-size: clamp(2.6rem, 9.4vw, 9.4rem);
  padding: 0 clamp(16px, 3vw, 60px);
  /* Words start transparent; JS lights them up in Stage 2 */
  color: rgba(13, 13, 13, 0.14);
  transition: color 0.6s var(--ease-out);
  /* No will-change here: `color` is not a compositable property, so promoting
     these three full-width rows bought a layer that had to be re-rastered on
     every colour step of the intro. */
}

.hero-content--kinetic[data-hero-stage="3"] .hero-row {
  color: rgba(13, 13, 13, 0.88);
}
.hero-content--kinetic[data-hero-stage="3"] .hero-row--accent {
  color: var(--accent, #ff914d);
}

.hero-word {
  display: inline-block;
  white-space: nowrap;
  opacity: 0;
  transform: translateY(0.12em);
  will-change: opacity, transform;
}

.hero-slot {
  display: inline-block;
  /* Reserves square space for an icon at ~ line-height size */
  width: 1em;
  height: 1em;
  flex-shrink: 0;
}

/* ── Icons (absolutely positioned, driven by GSAP) ────────────────────── */
.hero-icons {
  position: absolute;
  inset: 0;
  z-index: 4;
  pointer-events: none;
}

.hero-icon {
  position: absolute;
  top: 50%;
  left: 50%;
  width: clamp(70px, 9vw, 150px);
  height: clamp(70px, 9vw, 150px);
  transform: translate(-50%, -50%) scale(0);
  opacity: 0;
  will-change: transform, opacity;
}
.hero-icon svg { width: 100%; height: 100%; display: block; }

/* ── Corner service labels (Stage 3) ───────────────────────────────────── */
.hero-services {
  position: absolute;
  inset: 0;
  z-index: 3;
  pointer-events: none;
  font-family: var(--font-sans, 'Onest'), sans-serif;
  /* Was weight 500 at rgba(13,13,13,0.55), which blends to rgb(122,117,113)
     over the cream field — 4.18:1, under the 4.5:1 WCAG AA floor for text this
     size. Weight 600 and 0.78 alpha take it to 8.6:1 and make the labels read
     as deliberate captions rather than something faded out.
     Tracking eases from 0.08em to 0.07em: heavier weight needs slightly less
     letter-spacing to hold the same optical rhythm at this size. */
  font-weight: 600;
  font-size: clamp(11.5px, 0.9vw, 15px);
  letter-spacing: 0.07em;
  text-transform: uppercase;
  color: rgba(13, 13, 13, 0.78);
  line-height: 1.4;
}
.hero-service {
  position: absolute;
  opacity: 0;
  transform: translateY(6px);
  will-change: opacity, transform;
}
/* Labels sit in the GUTTERS between rows created by space-around distribution.
   --tl/--bl are pushed further from row edges so their second line doesn't
   brush the tallest glyphs of rows 1 & 3. --br anchored via top:% (not
   bottom) so it sits close under row 3 instead of stranded at section end. */
.hero-service--tl { top: 38%; left: clamp(16px, 3vw, 60px); }
.hero-service--tr { top: clamp(50px, 6vh, 80px); right: clamp(16px, 3vw, 60px); text-align: right; }
.hero-service--bl { top: 67%; left: clamp(16px, 3vw, 60px); }
.hero-service--br { top: 92%; right: clamp(16px, 3vw, 60px); bottom: auto; text-align: right; }

/* ── Floating CTA pill (Stage 3) — anchored middle-right per reference ─ */
.hero-cta-float {
  position: absolute;
  /* Matched to the .hero-service gutter (was clamp(24px, 4vw, 80px), which sat
     the button ~15px inside the labels). The four labels and this button are
     the only things on the hero's right edge, so they need to share one margin
     — two near-identical offsets read as a mistake rather than a choice. */
  right: clamp(16px, 3vw, 60px);
  top: 50%;
  z-index: 5;
  opacity: 0;
  /* Vertical centering is applied via GSAP (yPercent: -50) so the animated
     transform stays in one matrix and can't clash with translateY(-50%). */
  transform-origin: right center;
  will-change: opacity, transform;
}
.hero-cta-float .hero-cta {
  background: var(--accent, #ff914d);
  color: #ffffff;
  padding: 20px 26px 20px 32px;
  font-size: clamp(14px, 1vw, 17px);
  gap: 14px;
  border-radius: 999px;
}
.hero-cta-float .hero-cta::before,
.hero-cta-float .hero-cta::after { display: none; }

/* ==========================================================================
   Hero — Responsive rules
   Bootstrap breakpoints: xs<576 / sm≥576 / md≥768 / lg≥992 / xl≥1200 / xxl≥1400
   ========================================================================== */

/* Large tablets & small laptops (992 → 1199) — slightly reduced type */
@media (max-width: 1199.98px) {
  .hero-row { font-size: clamp(2.4rem, 9vw, 7rem); }
  .hero-icon { width: clamp(64px, 8vw, 110px); height: clamp(64px, 8vw, 110px); }
  .hero-service--tr { top: clamp(60px, 8vh, 90px); }
}

/* Tablets in portrait & small laptops (768 → 991) — fill the fold */
@media (max-width: 991.98px) {
  .hero-section {
    min-height: 0;
    height: auto;
    position: relative;
  }
  .hero-content--kinetic {
    min-height: 0;
    height: auto;
  }
  .hero-title--kinetic {
    /* Zero top padding — the .hero-section already reserves the header gap.
       Content anchors to the top of the block so icons + text sit right
       under the header (no dead space). CTA below provides the bottom margin. */
    padding: 0;
    min-height: 0;
    height: auto;
    justify-content: flex-start;
    gap: clamp(28px, 5vh, 60px);
  }
  .hero-row {
    font-size: clamp(2.6rem, 10.5vw, 6rem);
    padding: 0 clamp(14px, 3vw, 40px);
    gap: 0.18em;
  }
  .hero-icon { width: clamp(56px, 10vw, 100px); height: clamp(56px, 10vw, 100px); }
  .hero-services { display: none; }
  .hero-cta-float {
    position: static;
    display: flex;
    justify-content: center;
    margin: clamp(24px, 4vh, 40px) auto 0;
    right: auto;
    top: auto;
    bottom: auto;
    transform: none;
  }
  .hero-cta-float .hero-cta { padding: 14px 22px 14px 26px; font-size: 14px; }
  body .custom-cursor { display: none !important; }
  * { cursor: auto; }
}

/* Small phones landscape & big phones portrait (576 → 767) */
@media (max-width: 767.98px) {
  .hero-title--kinetic {
    padding: 0;
    gap: clamp(16px, 3vh, 28px);
  }
  .hero-row {
    font-size: clamp(2.15rem, 10.6vw, 4.6rem);
    padding: 0 clamp(12px, 3vw, 28px);
    gap: 0.16em;
  }
  .hero-icon { width: clamp(48px, 11vw, 80px); height: clamp(48px, 11vw, 80px); }
  .hero-cta-float .hero-cta { padding: 13px 20px 13px 22px; font-size: 13px; }
}

/* Phones (<576) — Bootstrap xs — most aggressive scale-down */
@media (max-width: 575.98px) {
  .hero-title--kinetic {
    padding: 0;
    gap: clamp(12px, 2.5vh, 22px);
  }
  .hero-row {
    /* Keep it readable — min 1.85rem so short viewports don't shrink type
       into illegibility. */
    font-size: clamp(1.85rem, 10.2vw, 2.8rem);
    padding: 0 clamp(10px, 3vw, 20px);
    letter-spacing: -0.035em;
    gap: 0.14em;
  }
  .hero-icon { width: clamp(40px, 10vw, 58px); height: clamp(40px, 10vw, 58px); }
  .hero-cta-float .hero-cta {
    padding: 12px 18px 12px 20px;
    font-size: 12px;
    gap: 8px;
  }
  .hero-cta-float .hero-cta .arrow-badge {
    width: 24px;
    height: 24px;
  }
}

/* Very small phones (<380) */
@media (max-width: 379.98px) {
  .hero-row {
    font-size: clamp(1.65rem, 9.4vw, 2.2rem);
    padding: 0 8px;
  }
  .hero-icon { width: 36px; height: 36px; }
}

/* Coarse pointer devices (any width) — no custom cursor + no hover effects
   that require a fine pointer. */
@media (hover: none), (pointer: coarse) {
  body .custom-cursor,
  body .custom-cursor-follower { display: none !important; }
}

@media (prefers-reduced-motion: reduce) {
  .hero-icon { opacity: 1; transform: translate(-50%, -50%) scale(1); }
  .hero-word { opacity: 1; transform: none; }
  .hero-cta-float, .hero-service { opacity: 1; transform: none; }
  .hero-content--kinetic[data-hero-stage="0"] .hero-row { color: rgba(13, 13, 13, 0.88); }
}

/* ==========================================================================
   Contact Modal — animation swap: box-expand-from-CTA instead of shutter drop
   Coordinates come from JS via --modal-x / --modal-y CSS vars set on open.
   Panel scales from 0 at click point, with border-radius flattening from
   60px → 0 as it grows to fill the viewport. Content inside continues to
   use its existing delayed fade-in.
   ========================================================================== */
.contact-modal__panel {
  transform-origin: var(--modal-x, 50%) var(--modal-y, 50%);
  transform: scale(0);
  border-radius: clamp(28px, 4vw, 60px);
  transition: transform 0.65s var(--ease-in-out),
              border-radius 0.65s var(--ease-in-out);
}
.contact-modal[aria-hidden="false"] .contact-modal__panel {
  transform: scale(1);
  border-radius: 0;
}


/* ==========================================================================
   Services megamenu — appears on hover of the "Services" nav item.
   Wide panel dropped from the header centred on the trigger; contains 4
   cards (Engineering + 3 placeholders). Coming-soon items are non-nav
   and get a small toast via main.js.
   ========================================================================== */
.nav-services-wrap {
  position: relative;
  display: inline-flex;
  align-items: center;
}
.nav-link--dropdown {
  background: transparent;
  border: none;
  font-family: var(--font-sans);
  font-size: clamp(13px, 0.95vw, 15px);
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--text-primary);
  padding: 10px 18px;
  border-radius: 999px;
  cursor: none;
  display: inline-flex;
  align-items: center;
  gap: 6px;
  transition: background-color 0.35s var(--ease), color 0.35s var(--ease);
  white-space: nowrap;
}
.nav-link--dropdown .nav-link__chev {
  width: 10px;
  height: 10px;
  /* Stays pointing DOWN always — the panel expands out of the pill's
     bottom edge, so the arrow direction reads correctly. */
}
.nav-services-wrap:hover .nav-link--dropdown,
.nav-services-wrap.is-open .nav-link--dropdown {
  background-color: #0d0d0d;
  color: #fff3eb;
}

/* The panel — fully rounded independent shape sitting just below the pill
   with a small gap. Height animates from 0 → auto so it grows downward. */
.nav-mega {
  position: absolute;
  top: calc(100% + 8px);
  left: 50%;
  transform: translateX(-50%);
  width: clamp(560px, 44vw, 780px);
  padding: 0 12px;
  background: #0d0d0d;
  color: #fff3eb;
  border-radius: 22px;
  box-shadow: 0 30px 80px -30px rgba(0, 0, 0, 0.55);
  max-height: 0;
  overflow: hidden;
  opacity: 0;
  pointer-events: none;
  visibility: hidden;
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 8px;
  transition: max-height 0.45s var(--ease-out),
              padding 0.45s var(--ease-out),
              opacity 0.25s ease,
              visibility 0.45s;
  z-index: var(--z-dropdown);
}
.nav-services-wrap:hover .nav-mega,
.nav-services-wrap.is-open .nav-mega,
.nav-mega:hover {
  max-height: 560px;
  padding: 12px;
  opacity: 1;
  pointer-events: auto;
  visibility: visible;
}
/* Invisible bridge covering the 8 px gap so hover doesn't drop. */
.nav-mega::before {
  content: "";
  position: absolute;
  left: 0; right: 0;
  top: -12px;
  height: 14px;
}

/* All 4 cards get the same orange hover — soon-specific muted hover removed. */
.nav-mega__card--soon:hover {
  background: var(--accent, #ff914d);
  color: #0d0d0d;
  transform: translateY(-2px);
}

/* Reserve room on the right side of the card content so the growing
   preview thumbnail doesn't crash into the description below it. */
.nav-mega__card { padding-right: 72px; border-radius: 20px; }

.nav-mega__preview { transform-origin: top right; }
.nav-mega__card:hover .nav-mega__preview {
  /* Small growth only — stays clearly inside the reserved 72 px lane. */
  width: 52px;
  height: 52px;
  background: #0d0d0d;
  color: #fff3eb;
}
.nav-mega__card:hover .nav-mega__preview svg {
  transform: rotate(3deg) scale(1.04);
}

/* ── Focus blur — dim + blur everything behind the header when the
   megamenu is open so users focus on the menu. Applied to <main> and
   any main-level siblings. Header itself sits outside so it stays crisp. */
body.mega-open main,
body.mega-open #outro,
body.mega-open .svc-hero,
body.mega-open .svc-grid,
body.mega-open .contact-main {
  filter: blur(4px) brightness(0.94);
  transition: filter 0.4s var(--ease-out);
}
main, #outro, .svc-hero, .svc-grid, .contact-main {
  transition: filter 0.4s var(--ease-out);
}

/* ── Preview thumbnail on each mega card ─────────────────────────── */
.nav-mega__preview {
  position: absolute;
  top: 14px;
  right: 14px;
  width: 42px;
  height: 42px;
  border-radius: 10px;
  background: rgba(255, 243, 235, 0.08);
  display: flex;
  align-items: center;
  justify-content: center;
  color: rgba(255, 243, 235, 0.72);
  transform-origin: top right;
  transition: width 0.4s var(--ease-out),
              height 0.4s var(--ease-out),
              background-color 0.4s var(--ease-out),
              color 0.4s var(--ease-out);
  pointer-events: none;
  overflow: hidden;
}
.nav-mega__preview svg {
  width: 60%;
  height: 60%;
  transition: transform 0.5s var(--ease-out);
}

.nav-mega__card {
  position: relative;
  display: block;
  padding: 18px 18px 22px;
  border-radius: 16px;
  background: rgba(255, 243, 235, 0.04);
  color: inherit;
  text-decoration: none;
  transition: background-color 0.35s var(--ease), color 0.35s var(--ease), transform 0.35s var(--ease);
  cursor: none;
}
.nav-mega__card:hover {
  background: var(--accent, #ff914d);
  color: #0d0d0d;
  transform: translateY(-2px);
}
.nav-mega__num {
  display: block;
  font-family: var(--font-sans);
  font-size: 11px;
  font-weight: 500;
  letter-spacing: 0.16em;
  opacity: 0.55;
  margin-bottom: 8px;
}
.nav-mega__title {
  display: block;
  font-family: 'Barlow Condensed', var(--font-sans);
  font-weight: 800;
  font-size: 22px;
  line-height: 1.05;
  letter-spacing: -0.01em;
  text-transform: uppercase;
  margin-bottom: 6px;
}
.nav-mega__desc {
  display: block;
  font-family: var(--font-sans);
  font-size: 12px;
  font-weight: 400;
  line-height: 1.45;
  opacity: 0.72;
}
.nav-mega__arrow {
  position: absolute;
  top: 16px;
  right: 16px;
  width: 16px;
  height: 16px;
  opacity: 0.45;
  transition: opacity 0.35s var(--ease), transform 0.35s var(--ease);
}
.nav-mega__card:hover .nav-mega__arrow {
  opacity: 1;
  transform: translate(2px, -2px);
}
.nav-mega__soon {
  position: absolute;
  top: 16px;
  right: 16px;
  font-family: var(--font-sans);
  font-size: 10px;
  font-weight: 700;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  padding: 3px 8px;
  border-radius: 999px;
  background: rgba(255, 243, 235, 0.12);
  color: #fff3eb;
}
.nav-mega__card--soon:hover { background: var(--accent, #ff914d); color: #0d0d0d; transform: translateY(-2px); }

/* Hide on tablet+mobile — the hamburger menu handles it. */
@media (max-width: 991.98px) {
  .nav-mega { display: none !important; }
}

/* ── Mobile menu Services expand ────────────────────────────────────── */
.mobile-menu-group { width: 100%; }
.mobile-menu-link--toggle {
  width: 100%;
  background: none;
  border: none;
  padding: inherit;
  font: inherit;
  color: inherit;
  text-align: left;
  cursor: pointer;
  display: flex;
  align-items: center;
  gap: 16px;
}
.mobile-menu-link-chev {
  margin-left: auto;
  font-family: var(--font-sans);
  font-size: 22px;
  font-weight: 300;
  transition: transform 0.35s var(--ease);
  transform-origin: center;
}
.mobile-menu-group.is-open .mobile-menu-link-chev { transform: rotate(45deg); }
.mobile-menu-sublist {
  display: grid;
  grid-template-rows: 0fr;
  transition: grid-template-rows 0.4s var(--ease);
}
.mobile-menu-group.is-open .mobile-menu-sublist { grid-template-rows: 1fr; }
.mobile-menu-sublist > div {
  overflow: hidden;
  display: flex;
  flex-direction: column;
  gap: 4px;
  padding: 0 0 8px 42px;
}
.mobile-menu-sub {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 12px 4px;
  font-family: var(--font-sans);
  font-size: 15px;
  font-weight: 500;
  color: rgba(255, 243, 235, 0.85);
  text-decoration: none;
  border-bottom: 1px solid rgba(255, 243, 235, 0.08);
}
.mobile-menu-sub em {
  font-style: normal;
  font-size: 10px;
  font-weight: 700;
  letter-spacing: 0.14em;
  color: rgba(255, 243, 235, 0.5);
  padding: 3px 8px;
  border-radius: 999px;
  border: 1px solid rgba(255, 243, 235, 0.16);
}
.mobile-menu-sub--muted { color: rgba(255, 243, 235, 0.55); }

   Testimonial rotation control (WCAG 2.2.2)
   Shows a pause glyph while rotating, a play glyph once stopped.
   ========================================================================== */

/* ==========================================================================
   Reduced motion
   The existing blocks covered only the hero and the outro track. This extends
   it to everything else that moves on its own or on scroll: the marquee, the
   scroll-driven reveals, and the parallax. Scroll-linked animations are left
   in place but made instant, so nothing ends up stuck off-screen or invisible.
   ========================================================================== */
@media (prefers-reduced-motion: reduce) {
  *,
  *::before,
  *::after {
    animation-duration: 0.001ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.001ms !important;
    scroll-behavior: auto !important;
  }

  /* Endless marquee: park it rather than let it crawl. */
  .outro-line__track { transform: none !important; }

  /* Scroll-driven sections settle into their final, readable state. */
  .ps-line,
  .projects-desc,
  /* .process-item keeps its .is-in fill here — that is a colour change, which
     reduced motion should preserve; only the rise is dropped. */
  .process-box,
  .process-box__title,
  .process-item,
  .svc-card {
    opacity: 1 !important;
    transform: none !important;
  }
}

/* ==========================================================================
   MOTION SYSTEM
   ---------------------------------------------------------------------------
   Three things live here, in load order:
     1. Session curtain  — brand panel, first arrival of a session only.
     2. View transitions — removes the white flash between pages (MPA).
     3. Scroll reveals   — class-toggled by one IntersectionObserver in main.js.

   Deliberately CSS-driven. The site already runs Lenis + 7 ScrollTriggers and
   has a documented history of scroll stalls (docs/scroll_diagnostics_report.md),
   so reveals must not add per-frame main-thread work. JS only adds a class;
   the compositor does the rest.

   FENCED OFF — the homepage hero and #projects own their motion and are
   excluded by selector in main.js (REVEAL_EXCLUDE). Do not add [data-reveal]
   inside either subtree: elements inside a *pinned* container resolve their
   viewport position against the pin, so they fire at the wrong scroll offset.
   ========================================================================== */
:root {
  --z-curtain: 700;              /* above --z-cursor (600) */
  --reveal-duration: var(--dur-base);
  --reveal-rise: var(--rise-md);
}

/* ── 1. Session curtain ───────────────────────────────────────────────────
   No markup: the panel is html::before, the mark is html::after, so this works
   on every page including 404.html (which loads no JS at all). The inline
   <head> script sets data-curtain before first paint, so there is never a
   flash of content underneath, and it carries its own failsafe timer so a
   JS error can never strand a visitor behind an opaque panel. */
/* Deliberately no `overflow: hidden` here. Locking the scroller for the <1s the
   curtain is up violates "never lock out input during a transition", and in
   WebKit it also removes the scrollbar and the gutter does not restore
   identically — every element on the page shifted 3px horizontally, which the
   geometry fingerprint caught. If a visitor scrolls behind the panel they
   simply arrive already scrolled, which is the more responsive outcome. */
html[data-curtain="on"]::before,
html[data-curtain="leaving"]::before {
  content: "";
  position: fixed;
  inset: 0;
  z-index: var(--z-curtain);
  background: var(--bg-primary);
  transition: transform 520ms var(--ease-out);
}

html[data-curtain="on"]::after,
html[data-curtain="leaving"]::after {
  content: "";
  position: fixed;
  inset: 0;
  z-index: calc(var(--z-curtain) + 1);
  background: url("../assets/img/urbaniq-logo.svg") center center / 190px auto no-repeat;
  transition: opacity 240ms var(--ease-out),
              transform 520ms var(--ease-out);
}

/* Mark clears fractionally before the panel so the two read as one object
   leaving, rather than a logo riding a lift. */
html[data-curtain="leaving"]::before {
  transform: translateY(-100%);
  pointer-events: none;
}
html[data-curtain="leaving"]::after {
  opacity: 0;
  transform: translateY(-28px);
  pointer-events: none;
}

/* ── 2. Cross-document view transitions ───────────────────────────────────
   Native, compositor-driven, no JS. Unsupported browsers get today's instant
   cut, so this is purely additive. */
@view-transition {
  navigation: auto;
}

::view-transition-old(root),
::view-transition-new(root) {
  animation-duration: 260ms;
  animation-timing-function: var(--ease-out);
}

/* The header keeps its own name so it persists across navigation instead of
   cross-fading with the page body — the site reads as one surface, not eight
   documents. Exactly one element per page may carry a given name. */
.thread-nav-container {
  view-transition-name: uiq-header;
}

/* The cursor is position:fixed and would otherwise be baked into the outgoing
   snapshot, leaving a second frozen arrow on screen mid-transition. Lifting it
   into its own group and suppressing both halves keeps only the live one. */
.custom-cursor {
  view-transition-name: uiq-cursor;
}
::view-transition-old(uiq-cursor),
::view-transition-new(uiq-cursor) {
  display: none;
}

/* ── 3. Scroll reveals ────────────────────────────────────────────────────
   Small travel is the whole point: --rise-md reads as content settling, 60px
   reads as a template. Transform + opacity only.

   The :not([data-reveal="fade"]) guard matters. Plenty of elements on this site
   use `transform` for *layout* rather than motion — .outro-cta is centred with
   translate(-50%, -50%) — and writing a reveal transform on top of one silently
   knocks it out of position. Those elements get the fade variant, which never
   declares transform at all. JS picks the variant automatically. */
[data-reveal] {
  opacity: 0;
  transition: opacity var(--reveal-duration) var(--ease-out),
              transform var(--reveal-duration) var(--ease-out);
  transition-delay: var(--reveal-delay, 0ms);
  /* No will-change here on purpose. A 15-section page marks 100+ elements, and
     promoting every one of them to its own compositor layer up front costs far
     more memory than the first-frame hiccup it would avoid. Transform and
     opacity get a layer for the duration of the transition regardless. */
}

/* The initial hide must not itself animate. These elements already carry a
   global transition, so the moment JS applies opacity:0 the browser fades
   content that is currently on screen *out* over half a second. Locked for one
   frame after marking, then armed — so only the reveal ever animates. */
[data-reveal][data-reveal-lock] {
  transition: none !important;
}

/* Everything except the fade variant rises. */
[data-reveal]:not([data-reveal="fade"]):not([data-reveal="media"]) {
  transform: translate3d(0, var(--reveal-rise), 0);
}

/* Cards get a hair of scale. Never from 0 — 0.985 keeps them present. */
[data-reveal="card"] {
  transform: translate3d(0, 16px, 0) scale(0.985);
  --reveal-duration: var(--dur-base);
}

/* Media wipes rather than rises, reusing the showcase-grid language already
   on the homepage so the vocabulary stays consistent site-wide. */
[data-reveal="media"] {
  opacity: 1;
  transform: none;
  clip-path: inset(0 0 100% 0);
  transition: clip-path 700ms var(--ease-in-out);
  transition-delay: var(--reveal-delay, 0ms);
}
[data-reveal="media"].is-revealed {
  clip-path: inset(0 0 0 0);
}

[data-reveal].is-revealed {
  opacity: 1;
}
/* Same guard as the hidden state: never write transform on an element whose
   transform is its layout. */
[data-reveal]:not([data-reveal="fade"]):not([data-reveal="media"]).is-revealed {
  transform: none;
}

/* ── Header scroll edge ───────────────────────────────────────────────────
   Content flows under a floating material instead of meeting a hard divider.
   Ramps in only once the page has actually scrolled. */
.thread-nav-container::before,
.thread-host::before {
  transition: backdrop-filter 300ms var(--ease-out),
              opacity 300ms var(--ease-out);
}
.thread-nav-container.is-scrolled::before,
.thread-host.is-scrolled::before {
  backdrop-filter: blur(16px) saturate(160%);
  -webkit-backdrop-filter: blur(16px) saturate(160%);
  opacity: 0.92;
}

/* ── Section-header parallax ──────────────────────────────────────────────
   The one genuinely scroll-linked effect here, so it uses a native scroll
   timeline: it runs on the compositor with zero JS and cannot contend with
   Lenis. Gated behind @supports — unsupported browsers simply get no drift,
   which is a non-event. Travel is deliberately tiny (8%); parallax reads as
   cheap the moment it becomes noticeable. */
@supports (animation-timeline: view()) {
  @media (prefers-reduced-motion: no-preference) {
    .uiq-parallax {
      animation: uiq-drift linear both;
      animation-timeline: view();
      animation-range: entry 0% exit 100%;
    }
  }
}

@keyframes uiq-drift {
  from { transform: translate3d(0, 8%, 0); }
  to   { transform: translate3d(0, -8%, 0); }
}

/* ── Escape hatches ───────────────────────────────────────────────────────
   `data-motion="off"` resolves everything to its final state in one shot, for
   visual-regression runs that must not race a transition. */
html[data-motion="off"] [data-reveal] {
  opacity: 1 !important;
  transform: none !important;
  clip-path: none !important;
  transition: none !important;
}
html[data-motion="off"]::before,
html[data-motion="off"]::after {
  display: none !important;
}

/* Reduced motion keeps the comprehension cue (opacity) and drops every
   vestibular one — travel, wipe, curtain, parallax. */
@media (prefers-reduced-motion: reduce) {
  [data-reveal] {
    transform: none !important;
    clip-path: none !important;
    transition: opacity 200ms ease !important;
  }
  html[data-curtain]::before,
  html[data-curtain]::after {
    display: none !important;
  }
  .uiq-parallax {
    transform: none !important;
    animation: none !important;
  }
}

/* Coarse pointers: keep reveals (they're cheap and read well on phones) but
   drop the blur, which is expensive to composite on mobile GPUs. */
@media (hover: none), (pointer: coarse) {
  .thread-nav-container.is-scrolled::before,
  .thread-host.is-scrolled::before {
    backdrop-filter: none;
    -webkit-backdrop-filter: none;
  }
}
