/* ═══════════════════════════════════════════════════════════════
   § FONT FACES
═══════════════════════════════════════════════════════════════ */
@font-face {
  font-family: 'D-DIN';
  src: url('assets/fonts/d-din-webfont.woff2') format('woff2'),
       url('assets/fonts/d-din-webfont.woff') format('woff'),
       url('assets/fonts/D-DIN.otf') format('opentype');
  font-weight: 300 400; font-style: normal; font-display: swap;
}
@font-face {
  font-family: 'D-DIN';
  src: url('assets/fonts/d-din-italic-webfont.woff2') format('woff2'),
       url('assets/fonts/d-din-italic-webfont.woff') format('woff'),
       url('assets/fonts/D-DIN-Italic.otf') format('opentype');
  font-weight: 300 400; font-style: italic; font-display: swap;
}
@font-face {
  font-family: 'D-DIN';
  src: url('assets/fonts/d-din-bold-webfont.woff2') format('woff2'),
       url('assets/fonts/d-din-bold-webfont.woff') format('woff'),
       url('assets/fonts/D-DIN-Bold.otf') format('opentype');
  font-weight: 700; font-style: normal; font-display: swap;
}
@font-face {
  font-family: 'D-DIN Condensed';
  src: url('assets/fonts/d-dincondensed-webfont.woff2') format('woff2'),
       url('assets/fonts/d-dincondensed-webfont.woff') format('woff'),
       url('assets/fonts/D-DINCondensed.otf') format('opentype');
  /* font-display: swap — D-DIN Condensed is the heading font used for the nav
     logo, nav links, project titles, and all heading/micro text. 'swap' ensures
     the font renders as soon as it loads on cold visits instead of the 'optional'
     behaviour that suppresses it entirely if it misses the first render window. */
  font-weight: 100 600; font-style: normal; font-display: swap;
}
@font-face {
  font-family: 'D-DIN Condensed';
  src: url('assets/fonts/d-dincondensed-bold-webfont.woff2') format('woff2'),
       url('assets/fonts/d-dincondensed-bold-webfont.woff') format('woff'),
       url('assets/fonts/D-DINCondensed-Bold.otf') format('opentype');
  font-weight: 700; font-style: normal; font-display: swap;
}

/* ── Registered custom properties ── */
/* --cur-scale must be registered with a numeric syntax so the browser can
   interpolate it in the CSS transition on #cur-r. Without @property the
   browser treats it as an opaque string and snaps instead of transitioning. */
@property --cur-scale {
  syntax: '<number>';
  inherits: true;
  initial-value: 1;
}

/* ═══════════════════════════════════════════════════════════════
   § DESIGN TOKENS
═══════════════════════════════════════════════════════════════ */
:root {
  /* ── Color Palette ── */
  --c-bg:         #080909;
  --c-bg2:        #0d0e0e;
  --c-surface:    rgba(255,255,255,0.032);
  --c-surface2:   rgba(255,255,255,0.055);
  --c-primary:    #edeeed;
  --c-secondary:  #96a8aa;
  --c-tertiary:   #5e7172;
  --c-accent:     #5ba0a4;
  --c-accent-hi:  #7ec8cc;
  --c-accent-lo:  rgba(91,160,164,0.12);
  --c-muted:      rgba(150,168,170,0.35);
  --c-rule:       rgba(255,255,255,0.055);
  --c-rule-hi:    rgba(255,255,255,0.10);

  /* ── Typography — roles ── */
  --f-heading:   'D-DIN Condensed', sans-serif;
  --f-body:      'D-DIN', sans-serif;
  --f-ui:        'D-DIN', sans-serif;
  --f-meta:      'D-DIN Condensed', sans-serif;

  /* ── Type Scale — fluid clamp() ── */
  --t-display:   clamp(80px, 14vw, 200px);
  --t-hero:      clamp(72px, 12.5vw, 160px);
  --t-title:     clamp(54px, 8vw, 112px);
  --t-section:   clamp(46px, 7vw, 100px);
  --t-heading:   clamp(38px, 5.5vw, 72px);
  --t-subhead:   clamp(20px, 2.4vw, 30px);
  --t-body:      17px;
  --t-ui:        15px;
  --t-meta:      13px;
  --t-micro:     11px;
  --t-nano:      10px;

  /* ── Line Heights ── */
  --lh-display:  0.86;
  --lh-heading:  0.90;
  --lh-snug:     1.20;
  --lh-text:     1.62;
  --lh-body:     1.88;

  /* ── Letter Spacing ── */
  --ls-tight:   -0.04em;
  --ls-zero:    -0.02em;
  --ls-base:     0.00em;
  --ls-open:     0.01em;
  --ls-stencil:  0.20em;
  --ls-wide:     0.26em;
  --ls-xwide:    0.32em;

  /* ── Spacing Scale (8px base) ── */
  --s-1:    8px;
  --s-2:   16px;
  --s-3:   24px;
  --s-4:   32px;
  --s-5:   40px;
  --s-6:   48px;
  --s-7:   64px;
  --s-8:   80px;
  --s-9:   96px;
  --s-10: 120px;
  --s-11: 160px;
  --s-12: 200px;

  /* ── Layout ── */
  --pad:         clamp(24px, 4.5vw, 56px);
  --pad-sm:      clamp(16px, 3vw, 32px);
  --max-w:       1500px;
  --max-w-text:  680px;

  /* ── Easing ── */
  --ease:        cubic-bezier(0.16, 1, 0.3, 1);
  --ease-in:     cubic-bezier(0.4, 0, 1, 1);
  --snap:        cubic-bezier(0.77, 0, 0.18, 1);
  --ease-out:    cubic-bezier(0.0, 0, 0.2, 1);
  --spring:      cubic-bezier(0.34, 1.56, 0.64, 1);

  /* ── Durations ── */
  --dur-instant: 0.15s;
  --dur-fast:    0.28s;
  --dur-mid:     0.52s;
  --dur-slow:    0.78s;
  --dur-xl:      1.10s;
  --dur-xxl:     1.60s;

  /* ── Z-index Layers ── */
  --z-base:      1;
  --z-gallery:   5;
  --z-text:      10;
  --z-raised:    20;
  --z-carousel:  50;
  --z-about:     400;
  --z-nav:       500;
  --z-overlay:   600;
  --z-stage:     200; /* stage-edge and panel vignette */
  --z-cursor:    9998;
  --z-top:       9999;
  --z-veil:      10000;

  /* ── Shadows ── */
  --shadow-sm:   0 2px 12px rgba(0,0,0,0.45);
  --shadow-md:   0 8px 32px rgba(0,0,0,0.55), 0 2px 8px rgba(0,0,0,0.35);
  --shadow-lg:   0 20px 60px rgba(0,0,0,0.65), 0 4px 16px rgba(0,0,0,0.40);
  --shadow-glow: 0 0 40px rgba(91,160,164,0.08);
}

/* ── Light Theme ── */
[data-theme="light"] {
  --c-bg:        #edeeed;
  --c-bg2:       #e4e6e4;
  --c-surface:   rgba(0,0,0,0.042);
  --c-surface2:  rgba(0,0,0,0.07);
  --c-primary:   #080909;
  --c-secondary: #3d5052;
  --c-tertiary:  #6a8082;
  --c-muted:     rgba(8,9,9,0.45);
  --c-rule:      rgba(8,9,9,0.12);
  --c-rule-hi:   rgba(8,9,9,0.20);
  --shadow-sm:   0 2px 12px rgba(0,0,0,0.10);
  --shadow-md:   0 8px 32px rgba(0,0,0,0.12), 0 2px 8px rgba(0,0,0,0.08);
  --shadow-lg:   0 20px 60px rgba(0,0,0,0.14), 0 4px 16px rgba(0,0,0,0.08);
}

/* ── Nav ── */
[data-theme="light"] nav { background: linear-gradient(to bottom, rgba(237,238,237,.90) 0%, transparent 100%); }
[data-theme="light"] nav.scrolled {
  background: rgba(237,238,237,0.94);
  -webkit-backdrop-filter: blur(20px) saturate(1.5);
  backdrop-filter: blur(20px) saturate(1.5);
  border-bottom: 1px solid rgba(8,9,9,0.10);
}
/* Nav links and logo — ensure they read against the light nav bg */
[data-theme="light"] .n-links a { color: var(--c-secondary); }
[data-theme="light"] .n-links a:hover { color: var(--c-primary); }
[data-theme="light"] .n-logo { color: var(--c-secondary); }
/* Nav drawer — dark overlay becomes light panel */
[data-theme="light"] .nav-drawer {
  background: rgba(237,238,237,.98);
  -webkit-backdrop-filter: blur(24px) saturate(1.4);
  backdrop-filter: blur(24px) saturate(1.4);
}
[data-theme="light"] .nav-drawer a { color: var(--c-secondary); }
[data-theme="light"] .nav-drawer a:hover { color: var(--c-accent); }

/* ── Hero ── */
[data-theme="light"] .hero-vignette { background: linear-gradient(to right, rgba(237,238,237,0.97) 0%, rgba(237,238,237,0.75) 28%, rgba(237,238,237,0.25) 58%, transparent 78%), linear-gradient(to top, rgba(237,238,237,0.95) 0%, rgba(237,238,237,0.45) 7%, transparent 18%), linear-gradient(to bottom, rgba(237,238,237,0.60) 0%, transparent 16%); }
/* Model metadata label — too faint against light bg */
[data-theme="light"] .model-label { color: rgba(91,160,164,0.45); }
/* Cursor reticle centre-gap uses --c-bg which adapts, but bg shadow of reticle needs to be light */
[data-theme="light"] #cur::before { background: var(--c-bg); }

/* ── Grain ── */
[data-theme="light"] #grain { opacity: .02; }

/* ── Carousel — force light text in light mode
   Slides always have dark photographic backgrounds + dark gradient veils.
   --c-primary flips to near-black in light mode, making text invisible
   against the dark images. Pin text to the dark-mode light values. ── */
[data-theme="light"] .projects .proj-title,
[data-theme="light"] .projects .proj-counter,
[data-theme="light"] .projects .proj-counter-chevron,
[data-theme="light"] .projects .proj-swipe-hint,
[data-theme="light"] .projects .sc-cur { color: #edeeed; }
[data-theme="light"] .projects .proj-desc { color: rgba(237,238,237,0.75); }
[data-theme="light"] .projects .proj-open { color: #edeeed; }

/* ── Protected slide ── */
/* No light-mode overrides for carousel/about — dark values used throughout */

/* ── About — desktop light mode ──────────────────────────────────────────
   Desktop About is a two-column layout: photo left, text right. In light mode
   we want the stage background to use var(--c-bg) so the text column reads
   cleanly against a light surface. The intro panel vignette already uses
   var(--c-bg) tokens so it adapts automatically.

   Mobile stays dark (image-forward, gradient-contrast design). Mobile
   overrides further down in the responsive block lock the gradients to the
   dark palette explicitly so light mode cannot bleed in.
   ─────────────────────────────────────────────────────────────────────── */
@media (min-width: 769px) {
  [data-theme="light"] .about-stage {
    background: var(--c-bg);
  }
  /* Top/bottom edge vignettes — match the light bg */
  [data-theme="light"] .about-stage .stage-edge-top {
    background: linear-gradient(to bottom, var(--c-bg) 0%, transparent 100%);
  }
  [data-theme="light"] .about-stage .stage-edge-bot {
    background: linear-gradient(to top, var(--c-bg) 0%, transparent 100%);
  }
  /* panels-host depth vignette — match the light bg */
  [data-theme="light"] .panels-host::after {
    background: linear-gradient(to bottom, var(--c-bg) 0%, transparent 6%, transparent 94%, var(--c-bg) 100%);
  }
  /* col-bottom label/chapter gradient — fade from transparent to light bg */
  [data-theme="light"] .col-bottom {
    background: linear-gradient(to bottom, transparent 0%, var(--c-bg) 55%);
  }
  /* Statement panel video frame background */
  [data-theme="light"] .stmt-video-frame {
    background: var(--c-bg);
  }
}

/* ── Contact (now panel-contact inside About stage) ── */
/* Background image — more visible on light, slightly more saturated */
[data-theme="light"] .panel-contact .contact-img { opacity: .20; filter: saturate(.45); }
/* Watermark slightly stronger so it reads as intentional, not faded */
[data-theme="light"] .panel-contact .contact-wm { -webkit-text-stroke: 1px rgba(91,160,164,.10); }
/* c-field hover — --c-surface on light is very faint; boost it */
[data-theme="light"] .panel-contact .c-field:hover { background: rgba(8,9,9,.055); }

/* ═══════════════════════════════════════════════════════════════
   § RESET & BASE
═══════════════════════════════════════════════════════════════ */
*, *::before, *::after { margin: 0; padding: 0; box-sizing: border-box; }
html { scroll-behavior: auto; overflow-x: hidden; -webkit-text-size-adjust: 100%; overscroll-behavior: none; }
body {
  background: var(--c-bg);
  color: var(--c-primary);
  font-family: var(--f-body);
  font-weight: 400;
  font-size: var(--t-body);
  line-height: var(--lh-body);
  cursor: none;
  overflow-x: hidden;
  overscroll-behavior: none;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  opacity: 0;
  transition: opacity var(--dur-slow) var(--ease);
}
body.ready { opacity: 1; }
:focus { outline: none; }
:focus-visible { outline: 1.5px solid var(--c-accent); outline-offset: 4px; border-radius: 2px; }
img { display: block; max-width: 100%; height: auto; }
em { font-style: italic; color: var(--c-accent); }
strong { color: var(--c-primary); font-weight: 700; }

/* ── Screen reader only ── */
.sr-only {
  position: absolute; width: 1px; height: 1px; padding: 0;
  margin: -1px; overflow: hidden; clip: rect(0,0,0,0);
  white-space: nowrap; border: 0;
}

/* ═══════════════════════════════════════════════════════════════
   § TYPOGRAPHY SYSTEM — Systematic roles
═══════════════════════════════════════════════════════════════ */
/* Role: display — largest hero wordmark */
.t-display {
  font-family: var(--f-heading); font-size: var(--t-display);
  font-weight: 100; letter-spacing: var(--ls-tight);
  line-height: var(--lh-display); text-transform: uppercase;
}
/* Role: section-title — major section headings */
.t-title {
  font-family: var(--f-heading); font-size: var(--t-title);
  font-weight: 100; letter-spacing: var(--ls-tight);
  line-height: var(--lh-heading); text-transform: uppercase;
}
/* Role: subheading — supporting headings */
.t-heading {
  font-family: var(--f-heading); font-size: var(--t-heading);
  font-weight: 100; letter-spacing: var(--ls-tight);
  line-height: var(--lh-heading); text-transform: uppercase;
  color: var(--c-primary);
}
/* Role: subhead — tertiary headings */
.t-subhead {
  font-family: var(--f-heading); font-size: var(--t-subhead);
  font-weight: 100; letter-spacing: var(--ls-tight);
  line-height: var(--lh-heading); text-transform: uppercase;
}
/* Role: body — paragraph text */
.t-body {
  font-family: var(--f-body); font-size: var(--t-body);
  font-weight: 400; letter-spacing: var(--ls-open); line-height: var(--lh-body);
}
/* Role: ui — compact body, labels */
.t-ui {
  font-family: var(--f-body); font-size: var(--t-ui);
  font-weight: 400; letter-spacing: var(--ls-open); line-height: var(--lh-text);
}
/* Role: label — accent labels (slightly elevated from micro for interactive use) */
.t-label {
  font-family: var(--f-meta); font-size: 12px;
  font-weight: 700; letter-spacing: var(--ls-stencil); text-transform: uppercase;
  color: var(--c-accent);
}
/* Role: meta — metadata, dates (larger, lighter weight to read as secondary info) */
.t-meta {
  font-family: var(--f-meta); font-size: var(--t-meta);
  font-weight: 400; letter-spacing: var(--ls-open); text-transform: uppercase;
}
/* Role: caption — smallest text, true micro-copy */
.t-micro {
  font-family: var(--f-meta); font-size: var(--t-micro);
  font-weight: 700; letter-spacing: var(--ls-stencil); text-transform: uppercase;
}
/* Role: chapter — decorative ghost text */
.t-chapter {
  font-family: var(--f-heading); font-size: clamp(42px, 5.5vw, 74px);
  font-weight: 100; letter-spacing: var(--ls-tight); line-height: 0.92;
  text-transform: uppercase; color: var(--c-secondary); opacity: 0.36;
}
/* Role: statement — personal statement body */
.t-statement {
  font-family: var(--f-body); font-size: clamp(19px, 1.9vw, 27px);
  font-weight: 400; letter-spacing: var(--ls-open); line-height: var(--lh-body);
  color: var(--c-secondary);
}

h1 em, h2 em, h3 em { font-style: italic; color: var(--c-accent); font-weight: inherit; }
p strong { color: var(--c-primary); font-weight: 700; font-family: var(--f-body); }

/* ═══════════════════════════════════════════════════════════════
   § AMBIENT OVERLAYS
═══════════════════════════════════════════════════════════════ */
/* Film grain — single static SVG noise layer, zero animation cost.
   The original two-layer steps(1) flicker was barely perceptible at opacity 0.028
   and fired a style-recalc every 1.1 s even with contain:strict.
   A single static layer at the same average opacity is visually equivalent and
   costs zero compositor wakeups. mix-blend-mode:overlay gives it the same
   photographic character at negligible extra composite cost. */
#grain {
  position: fixed; inset: 0; width: 100%; height: 100%;
  pointer-events: none; z-index: var(--z-top);
  contain: strict;
  background-image: url("data:image/svg+xml,%3Csvg viewBox='0 0 256 256' xmlns='http://www.w3.org/2000/svg'%3E%3Cfilter id='g'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.75' numOctaves='4' seed='2' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23g)'/%3E%3C/svg%3E");
  background-size: 256px 256px;
  opacity: 0.025;
  mix-blend-mode: overlay;
}
/* #grain::before and #grain::after removed — grain is now a single static
   background on #grain itself (no pseudo-elements, no animation). */

/* Page entrance veil */
#veil {
  position: fixed; inset: 0; z-index: var(--z-veil);
  background: linear-gradient(135deg, var(--c-accent) 0%, var(--c-accent-hi) 100%);
  /* translateX() is compositor-only — clip-path triggers paint on every frame.
     The veil slides left to reveal the page (same visual result as clip-path shrinking from right). */
  transform: translateX(0);
  pointer-events: all;
  will-change: transform;
}
#veil.gone    { transform: translateX(-100%); pointer-events: none; transition: transform var(--dur-slow) var(--snap); }
#veil.instant { transition: none !important; transform: translateX(-100%) !important; pointer-events: none; }

/* ── Cursor: CAD crosshair reticle — matches project pages exactly ── */
/* Horizontal arm */
#cur {
  position: fixed; z-index: var(--z-veil); pointer-events: none;
  width: 16px; height: 1px;
  background: var(--c-accent);
  /* JS sets --cx/--cy on :root; translate is pure compositor — no left/top layout cost */
  transform: translate(-100vw, -100vw) translate(-50%, -50%); /* initial offscreen; JS sets this directly via style.transform */
  will-change: transform;
  transition: opacity var(--dur-fast) var(--ease);
}
/* Center gap — punched out of horizontal arm */
#cur::before {
  content: '';
  position: absolute; top: 0; left: 50%;
  width: 4px; height: 1px;
  background: var(--c-bg);
  transform: translateX(-50%);
}
/* Vertical arm */
#cur::after {
  content: '';
  position: absolute; left: 50%; top: 50%;
  width: 1px; height: 16px;
  background: var(--c-accent);
  transform: translate(-50%, -50%);
}
/* Corner bracket — lazy follower */
#cur-r {
  position: fixed; z-index: calc(var(--z-veil) - 1); pointer-events: none;
  width: 24px; height: 24px;
  /* Position from --crx/--cry, scale from --cur-scale. Kept separate so JS
     position writes never clobber the CSS scale transition, and vice versa. */
  transform: translate(-100vw, -100vw) translate(-50%, -50%) scale(var(--cur-scale, 1)); /* position set by JS; scale via CSS var for transition */
  will-change: transform;
  transition: --cur-scale var(--dur-mid) var(--ease),
              opacity var(--dur-fast);
}
#cur-r::before,
#cur-r::after {
  content: '';
  position: absolute;
  width: 7px; height: 7px;
  border-color: rgba(91,160,164,.5);
  border-style: solid;
  transition: border-color var(--dur-fast);
}
#cur-r::before { top: 0; left: 0; border-width: 1px 0 0 1px; }
#cur-r::after  { bottom: 0; right: 0; border-width: 0 1px 1px 0; }

/* Cursor state variants — only --cur-scale changes; position vars are untouched
   so JS and CSS never fight over the same transform. */
body.cur-link  { --cur-scale: 1.583; }
body.cur-link  #cur-r::before,
body.cur-link  #cur-r::after  { border-color: rgba(91,160,164,.85); }
body.cur-proj  { --cur-scale: 2.25; }
body.cur-proj  #cur-r::before,
body.cur-proj  #cur-r::after  { border-color: rgba(91,160,164,.25); }
body.cur-click #cur   { opacity: .55; }
body.cur-click { --cur-scale: 0.667; }

/* Section indicator — removed in favour of nav active state */
#section-indicator {
  display: none !important;
}

/* Nav active state — highlights the link for the section currently in view.
   JS adds .nav--work / .nav--about / .nav--contact directly to the <nav> element
   (via PageChrome.notifySection). Scoping to the nav avoids the browser evaluating
   body[data-section]-style rules against every element on the page on each
   section change. data-section on body is still set for other CSS consumers. */
nav.nav--work    .n-links a[href="#work"],
nav.nav--about   .n-links a[href="#about"],
nav.nav--contact .n-links a[href="#contact"] {
  color: var(--c-primary);
}
nav.nav--work    .n-links a[href="#work"]::after,
nav.nav--about   .n-links a[href="#about"]::after,
nav.nav--contact .n-links a[href="#contact"]::after {
  transform: scaleX(1);
}
/* Mobile drawer active state */
nav.nav--work    .nav-drawer a[href="#work"],
nav.nav--about   .nav-drawer a[href="#about"],
nav.nav--contact .nav-drawer a[href="#contact"] {
  color: var(--c-accent);
}

/* ═══════════════════════════════════════════════════════════════
   § NAVIGATION
═══════════════════════════════════════════════════════════════ */
nav#nav {
  position: fixed; top: 0; left: 0; right: 0; z-index: var(--z-nav);
  /* Three equal columns so .n-links is always centered to the viewport,
     fully independent of the logo or right-controls width. The logo
     scramble can reflow freely without nudging the nav links at all. */
  display: grid; grid-template-columns: 1fr auto 1fr; align-items: center;
  padding: var(--s-3) var(--pad);
  background: linear-gradient(to bottom, rgba(8,9,9,.72) 0%, transparent 100%);
  transition: padding var(--dur-mid) var(--ease), background var(--dur-mid), border-color var(--dur-mid);
}
nav#nav.scrolled {
  padding: var(--s-2) var(--pad);
  background: rgba(8,9,9,.90);
  -webkit-backdrop-filter: blur(20px) saturate(1.6);
  backdrop-filter: blur(20px) saturate(1.6);
  border-bottom: 1px solid var(--c-rule);
}

/* Logo */
.n-logo {
  font-family: var(--f-heading); font-size: clamp(12px, 1.2vw, 15px);
  font-weight: 700; letter-spacing: var(--ls-wide); text-transform: uppercase;
  color: var(--c-secondary); text-decoration: none; cursor: none;
  opacity: 0; animation: fd .6s var(--ease) .05s forwards;
  display: inline-flex; align-items: baseline; gap: .35em;
  transition: color .28s var(--ease);
}
.logo-first { transition: color .25s; }
.logo-sep   { font-weight: 700; letter-spacing: 0; color: rgba(91,160,164,.4); margin: 0 -.15em; }
.logo-last  { font-style: italic; color: var(--c-accent); transition: color .25s; }
.n-logo:hover .logo-first,
.n-logo:hover .logo-last { color: var(--c-primary); }

/* Desktop nav links */
.n-links {
  display: flex; align-items: center; gap: var(--s-4);
  opacity: 0; animation: fd .6s var(--ease) .12s forwards;
}
.n-links a {
  font-family: var(--f-heading); font-size: 14px; font-weight: 700;
  letter-spacing: var(--ls-stencil); text-transform: uppercase;
  color: var(--c-secondary); text-decoration: none; cursor: none;
  position: relative; overflow: hidden; transition: color .25s;
}
.n-links a::after {
  content: ''; position: absolute; bottom: 0; left: 0;
  width: 100%; height: 1px; background: var(--c-accent);
  transform: scaleX(0); transform-origin: left;
  transition: transform .35s var(--ease);
}
.n-links a:hover { color: var(--c-primary); }
.n-links a:hover::after { transform: scaleX(1); }

/* Right controls group */
.n-right {
  display: flex; align-items: center; gap: var(--s-2);
  opacity: 0; animation: fd .6s var(--ease) .18s forwards;
  justify-self: end;
}

/* Availability badge */
/* Availability badge — only visible on the hero section */
.n-avail {
  font-family: var(--f-heading); font-size: var(--t-micro); font-weight: 700;
  letter-spacing: var(--ls-stencil); text-transform: uppercase;
  color: var(--c-secondary); display: flex; align-items: center; gap: 7px;
  transition: opacity var(--dur-mid) var(--ease);
}
/* Hide when not on hero — body gets data-section set by JS notifySection */
body:not([data-section="hero"]) .n-avail {
  opacity: 0;
  pointer-events: none;
}
.n-avail-dot {
  width: 6px; height: 6px; border-radius: 50%; background: var(--c-accent);
  flex-shrink: 0; position: relative;
}
/* Ripple ring — uses transform+opacity (compositor-only, no repaint) */
.n-avail-dot::after {
  content: ''; position: absolute;
  inset: -4px; border-radius: 50%;
  border: 1px solid rgba(91,160,164,.55);
  animation: ping 2.5s ease infinite;
  will-change: transform, opacity;
}
@keyframes ping {
  0%   { transform: scale(0.5); opacity: .8; }
  70%  { transform: scale(2.2); opacity: 0; }
  100% { transform: scale(2.2); opacity: 0; }
}

/* Theme toggle */
.n-theme {
  background: none; border: none; cursor: none; padding: 6px;
  color: var(--c-secondary); transition: color .25s;
  display: flex; align-items: center; justify-content: center;
}
.n-theme:hover { color: var(--c-accent); }

/* Hamburger */
.n-ham {
  background: none; border: none; cursor: none;
  width: 36px; height: 36px; padding: 8px;
  display: none; flex-direction: column; justify-content: center; gap: 5px;
}
.n-ham span {
  display: block; width: 100%; height: 1px; background: var(--c-secondary);
  transition: transform var(--dur-mid) var(--ease), opacity var(--dur-fast), background var(--dur-fast);
  transform-origin: center;
}
body.menu-open .n-ham span:nth-child(1) { transform: translateY(6px) rotate(45deg); background: var(--c-primary); }
body.menu-open .n-ham span:nth-child(2) { opacity: 0; }
body.menu-open .n-ham span:nth-child(3) { transform: translateY(-6px) rotate(-45deg); background: var(--c-primary); }
body.menu-open { overflow: hidden; }

/* Mobile drawer */
.nav-drawer {
  position: fixed; inset: 0; z-index: calc(var(--z-nav) - 1);
  background: rgba(8,9,9,.98);
  -webkit-backdrop-filter: blur(24px) saturate(1.4);
  backdrop-filter: blur(24px) saturate(1.4);
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: var(--s-3);
  transform: translateY(-100%);
  transition: transform var(--dur-mid) var(--snap);
  pointer-events: none;
}
body.menu-open .nav-drawer { transform: translateY(0); pointer-events: auto; }
.nav-drawer a {
  font-family: var(--f-heading); font-size: clamp(44px, 10vw, 72px);
  font-weight: 100; letter-spacing: var(--ls-tight); text-transform: uppercase;
  color: var(--c-secondary); text-decoration: none; cursor: none;
  transition: color .3s var(--ease);
}
.nav-drawer a:hover { color: var(--c-accent); }

/* ═══════════════════════════════════════════════════════════════
   § HERO SECTION
═══════════════════════════════════════════════════════════════ */
.hero {
  position: relative;
  min-height: 100svh;
  overflow: hidden;
  contain: paint;
}

model-viewer#mini-viewer {
  position: absolute; inset: 0;
  width: 100%; height: 100%;
  --progress-bar-color: transparent;
  --progress-mask: transparent;
  background: var(--c-bg);
}

/* FPS fallback — static hero image shown when framerate is too low for the
   3D viewer. Matches the hero section's full-viewport footprint exactly so
   the vignette, text, and all other hero overlays layer identically on top.
   min-height: 100svh mirrors .hero so the image always fills the screen even
   on browsers where height:100% on an absolute child of a min-height parent
   collapses to zero. */
.hero-fps-fallback {
  position: absolute; inset: 0;
  width: 100%; height: 100%; min-height: 100svh;
  object-fit: cover; object-position: center;
  background: var(--c-bg);
  display: block;
}

/* Vignette gradient — left-weighted for text legibility */
.hero-vignette {
  position: absolute; inset: 0; pointer-events: none;
  background:
    linear-gradient(to right,   rgba(8,9,9,0.97) 0%, rgba(8,9,9,0.72) 28%, rgba(8,9,9,0.22) 58%, transparent 78%),
    linear-gradient(to top,     rgba(8,9,9,0.95) 0%, rgba(8,9,9,0.42) 7%,  transparent 18%),
    linear-gradient(to bottom,  rgba(8,9,9,0.55) 0%, transparent 16%);
}

/* Hero — ensure the text block never overlaps the fixed nav.
   On very short viewports the large H1 can stack up into the navbar.
   A max-height capped to the usable area (below nav, above bottom margin)
   with overflow:hidden clips it cleanly without restructuring layout. */
.hero {
  padding-top: 0; /* nav is fixed, so hero fills full viewport */
}

/* Hero text — bottom left, revealed on load */
.hero-text {
  position: absolute; bottom: var(--s-8); left: var(--pad);
  z-index: 20; max-width: 62vw;
  /* Prevent bleeding into the fixed nav on short viewports.
     nav ≈ 72px + bottom offset 80px = 152px reserved. */
  max-height: calc(100svh - 152px);
  overflow: hidden;
  opacity: 0; transform: translateY(20px);
  transition: opacity var(--dur-slow) var(--ease) 0.3s,
              transform var(--dur-xl) var(--ease) 0.3s;
}
.hero-text.is-revealed { opacity: 1; transform: translateY(0); }

/* Eyebrow line */
.hero-eyebrow {
  display: flex; align-items: center; gap: var(--s-2);
  margin-bottom: var(--s-3);
  font-family: var(--f-meta); font-size: var(--t-micro); font-weight: 700;
  letter-spacing: var(--ls-stencil); text-transform: uppercase;
  color: var(--c-accent);
  transform: translateX(-6px);
  transition: transform 0.6s var(--ease) 0.22s;
}
.hero-text.is-revealed .hero-eyebrow {
  transform: translateX(0);
}
.hero-eyebrow-line {
  display: block; width: var(--s-4); height: 1px; background: var(--c-accent);
  /* scaleX() is compositor-only — width animation causes layout on every frame.
     transform-origin: left keeps the line growing from the left edge. */
  transform: scaleX(0);
  transform-origin: left center;
  transition: transform var(--dur-slow) var(--ease) 0.55s;
}
.hero-text.is-revealed .hero-eyebrow-line { transform: scaleX(1); }

/* Hero H1 — staggered word reveal */
.hero-h1 {
  font-family: var(--f-heading);
  font-size: var(--t-hero);
  font-weight: 100; letter-spacing: var(--ls-tight);
  line-height: var(--lh-display); text-transform: uppercase;
  color: var(--c-primary); margin-bottom: var(--s-5);
}
.hero-h1 .line { display: block; overflow: hidden; }
.hero-h1 .word {
  display: inline-block; opacity: 0; transform: translateY(40px);
  will-change: opacity, transform;
}
.hero-text.is-revealed .hero-h1 .line:nth-child(1) .word { animation: slideUp .9s var(--ease) .38s forwards; }
.hero-text.is-revealed .hero-h1 .line:nth-child(2) .word { animation: slideUp .9s var(--ease) .50s forwards; }
.hero-text.is-revealed .hero-h1 .line:nth-child(3) .word { animation: slideUp .9s var(--ease) .62s forwards; }
.hero-text.is-revealed .hero-h1 .line:nth-child(4) .word { animation: slideUp .9s var(--ease) .74s forwards; }
@keyframes slideUp { to { opacity: 1; transform: translateY(0); } }
.hero-h1 em { font-style: italic; color: var(--c-accent); letter-spacing: -.03em; }

/* Hero subtext */
.hero-sub {
  font-family: var(--f-body); font-size: var(--t-ui); font-weight: 400;
  letter-spacing: var(--ls-open); line-height: 1.75; text-transform: uppercase;
  color: var(--c-primary); max-width: 390px;
  opacity: 0; transform: translateY(10px);
  transition: opacity var(--dur-slow) var(--ease) 0.82s,
              transform var(--dur-slow) var(--ease) 0.82s;
}
.hero-text.is-revealed .hero-sub { opacity: .52; transform: translateY(0); }

/* Availability badge */
.hero-avail {
  display: inline-flex; align-items: center; gap: 7px; margin-top: var(--s-4);
  font-family: var(--f-heading); font-size: var(--t-micro); font-weight: 700;
  letter-spacing: var(--ls-stencil); text-transform: uppercase;
  color: var(--c-secondary);
  opacity: 0; transition: opacity var(--dur-slow) var(--ease) 0.95s;
}
.hero-text.is-revealed .hero-avail { opacity: 1; }
.hero-avail-dot {
  width: 6px; height: 6px; border-radius: 50%; background: var(--c-accent);
  flex-shrink: 0; position: relative;
}
.hero-avail-dot::after {
  content: ''; position: absolute;
  inset: -4px; border-radius: 50%;
  border: 1px solid rgba(91,160,164,.55);
  animation: ping 2.5s ease infinite;
  will-change: transform, opacity;
}

/* Hero counter — replaces old scroll hint. Bottom-centre, matches proj-counter style.
   Shows "Scroll" on all slides except the last hero "slide" (single hero),
   where it shows "Next Section". Since hero has no carousel, JS controls
   visibility. The element fades out on scroll just like the old hint. */
.hero-counter {
  position: fixed; bottom: 32px; left: 50%; transform: translateX(-50%);
  z-index: var(--z-raised);
  display: flex; flex-direction: column; align-items: center; gap: 6px;
  font-family: var(--f-heading); font-size: 13px; font-weight: 700;
  letter-spacing: var(--ls-stencil); text-transform: uppercase;
  color: rgba(91,160,164,.5);
  opacity: 0; pointer-events: none;
  transition: opacity var(--dur-slow) var(--ease) 0.95s;
}
.hero-counter.is-revealed { opacity: .72; }
.hero-counter.hide { opacity: 0 !important; transition-duration: .4s !important; }
.hero-counter-label {
  color: rgba(91,160,164,.85);
  font-family: var(--f-heading); font-size: 13px; font-weight: 700;
  letter-spacing: var(--ls-stencil); text-transform: uppercase;
}
.hero-counter-chevron {
  display: block; width: 14px; height: 9px;
  animation: counterChevron 1.8s ease-in-out infinite;
  color: rgba(91,160,164,.65);
}

/* Model metadata label */
.model-label {
  position: absolute; z-index: var(--z-raised); pointer-events: none;
  color: rgba(91,160,164,0.18);
  opacity: 0; transition: opacity var(--dur-slow) var(--ease) 1.1s;
}
.model-label.is-revealed { opacity: 1; }
.model-label-br {
  bottom: var(--s-2); right: var(--s-3); text-align: right;
}

/* Mobile 360 badge — only shown on mobile (injected by JS, hidden on desktop) */
.hero-mobile-360 { display: none; }

/* Drag affordance — top right of model viewer with standard padding */
.model-hint {
  position: absolute; top: var(--pad); right: var(--pad);
  z-index: var(--z-raised); color: rgba(91,160,164,0.75);
  font-family: var(--f-heading); font-size: var(--t-micro);
  font-weight: 700; letter-spacing: var(--ls-stencil); text-transform: uppercase;
  display: flex; align-items: center; gap: 7px;
  white-space: nowrap; pointer-events: none;
  opacity: 0; transition: opacity var(--dur-mid) var(--ease) 1.3s;
}
.model-hint.is-revealed { opacity: 1; }
.model-hint.hidden       { opacity: 0 !important; transition-duration: .8s !important; }
/* 360° rotation badge */
.model-360-badge {
  position: relative; width: 44px; height: 44px;
  display: flex; align-items: center; justify-content: center;
  flex-shrink: 0;
}
.model-360-ring {
  position: absolute; inset: 0; width: 100%; height: 100%;
  color: var(--c-accent);
  animation: hintSpin 8s linear infinite;
}
.model-hint.hidden .model-360-ring,
.model-hint:not(.is-revealed) .model-360-ring { animation-play-state: paused; }
.model-360-label {
  position: relative; z-index: 1;
  font-family: var(--f-heading); font-size: 10px; font-weight: 700;
  letter-spacing: 0.04em; color: rgba(91,160,164,0.85);
}
/* Pause ripple animations when hero is not the active section to save GPU */
body:not([data-section="hero"]) .hero-avail-dot::after,
body:not([data-section="hero"]) .n-avail-dot::after { animation-play-state: paused; }

/* 360° ring spin — used by model-360-ring */
@keyframes hintSpin {
  from { transform: rotate(0deg); }
  to   { transform: rotate(360deg); }
}

/* Model error fallback */
.model-error {
  display: none; position: absolute; inset: 0; z-index: var(--z-raised);
  align-items: center; justify-content: center;
  flex-direction: column; gap: var(--s-2);
  color: rgba(91,160,164,0.35); text-align: center;
}
.model-error.visible { display: flex; }



/* ═══════════════════════════════════════════════════════════════
   § PROJECTS CAROUSEL — fixed fullscreen scroll-lock
═══════════════════════════════════════════════════════════════ */

.projects {
  position: fixed; inset: 0; z-index: var(--z-carousel);
  pointer-events: none; visibility: hidden;
  contain: layout style paint;
  touch-action: none;
}
.projects.carousel-active { pointer-events: auto; visibility: visible; }
/* overflow:hidden here clips the proj-ghost bleed and proj-img scale(1.06)
   overhang at the track level. This is a non-promoted element so it doesn't
   create the forced-rasterization cost that overflow:hidden on each .proj did. */
.proj-track { position: absolute; inset: 0; overflow: hidden; }

/* Individual project slide */
.proj {
  position: absolute; inset: 0; height: 100%;
  /* overflow:hidden removed — it combined with will-change:transform to force
     the browser to rasterize the entire slide (including the giant proj-ghost
     text) into a GPU texture for every promoted layer (active + prev + next).
     Clipping is now delegated to child elements that actually need it. */
  cursor: none;
  display: block; text-decoration: none;
  /* No global will-change — we promote selectively below */
  transition: transform 0.68s var(--snap);
  transform: translateY(100%) translateZ(0);
}
/* Promote only the active slide as a compositor layer.
   prev/next were previously promoted here too, but that kept 3 large GPU
   textures alive simultaneously and ran counterChevron animations on
   off-screen layers. The browser will still animate prev/next correctly
   on the transition frame — it promotes on demand. */
.proj[data-pos="active"] { will-change: transform; }
/* far-above / far-below: translateZ(0) removed — GPU promotion buys nothing on
   hidden, non-animating layers and keeps large texture memory occupied.
   content-visibility:hidden skips style recalc on the entire subtree (including
   the 10-15 .ch character spans with transitions). contain-intrinsic-size keeps
   layout stable so removal doesn't trigger a reflow. */
.proj[data-pos="far-above"] { transform: translateY(-200%); visibility: hidden; transition: none !important; content-visibility: hidden; contain-intrinsic-size: 100vw 100vh; }
.proj[data-pos="prev"]      { transform: translateY(-100%) translateZ(0); }
.proj[data-pos="active"]    { transform: translateY(0) translateZ(0); }
.proj[data-pos="next"]      { transform: translateY(100%) translateZ(0); }
.proj[data-pos="far-below"] { transform: translateY(200%); visibility: hidden; transition: none !important; content-visibility: hidden; contain-intrinsic-size: 100vw 100vh; }

/* Background image */
.proj-img {
  position: absolute; inset: 0;
  background-size: cover; background-position: center;
  transform: scale(1.06);
  /* filter: removed — filter on a background-image element forces a repaint
     on every frame that anything on the page changes (cursor, animations etc).
     Darkening is now handled by a ::after overlay using opacity, which is
     compositor-only and costs nothing at rest. */
  transition: transform 1.15s var(--ease);
}
/* Dark overlay — compositor-friendly opacity transition.
   mix-blend-mode:multiply removed: it prevented the compositor from isolating
   the carousel layer, forcing a cross-layer composite on every frame against
   everything below (including the model-viewer WebGL canvas). */
.proj-img::after {
  content: '';
  position: absolute; inset: 0;
  background: rgba(8,9,9,.55);
  opacity: 1;
  transition: opacity 1.15s ease;
}
.proj[data-pos="active"] .proj-img { will-change: transform; }
.proj:hover .proj-img { transform: scale(1.01); }
.proj:hover .proj-img::after { opacity: 0.18; }

/* Directional gradient veil */
.proj:nth-child(odd)  .proj-veil { position: absolute; inset: 0; background: linear-gradient(to right, rgba(8,9,9,.95) 0%, rgba(8,9,9,.6) 42%, transparent 72%); }
.proj:nth-child(even) .proj-veil { position: absolute; inset: 0; background: linear-gradient(to left,  rgba(8,9,9,.95) 0%, rgba(8,9,9,.6) 42%, transparent 72%); }

/* Side accent line on hover */
.proj-line {
  position: absolute; top: 0; bottom: 0; width: 2px;
  background: linear-gradient(to bottom, transparent 0%, var(--c-accent) 30%, var(--c-accent) 70%, transparent 100%);
  opacity: 0; transition: opacity .52s var(--ease), transform .64s var(--ease);
  transform: scaleY(0); transform-origin: center;
}
.proj:nth-child(odd)  .proj-line { left: 0; }
.proj:nth-child(even) .proj-line { right: 0; }
.proj:hover .proj-line { opacity: 1; transform: scaleY(1); }

/* Text block — vertically centered */
.proj-text {
  position: absolute; top: 50%; transform: translateY(-50%);
  width: 44%; z-index: 2;
  transition: transform .7s var(--ease);
}
.proj:nth-child(odd)  .proj-text { left: var(--pad); }
.proj:nth-child(even) .proj-text { right: var(--pad); text-align: right; }
.proj:hover .proj-text { transform: translateY(calc(-50% - 10px)); }

/* Project number label */
.proj-num {
  font-family: var(--f-heading); font-size: var(--t-micro); font-weight: 700;
  letter-spacing: var(--ls-stencil); text-transform: uppercase;
  color: var(--c-accent); margin-bottom: var(--s-2);
  display: flex; align-items: center; gap: var(--s-1);
}
.proj:nth-child(even) .proj-num { justify-content: flex-end; }
.proj-num::before { content: ''; width: var(--s-2); height: 1px; background: var(--c-accent); }
.proj:nth-child(even) .proj-num::before { display: none; }
.proj:nth-child(even) .proj-num::after  { content: ''; width: var(--s-2); height: 1px; background: var(--c-accent); }

/* Project title — per-char animation */
.proj-title {
  font-family: var(--f-heading); font-size: var(--t-title);
  font-weight: 100; letter-spacing: var(--ls-tight); line-height: var(--lh-heading);
  text-transform: uppercase; color: var(--c-primary);
  margin-bottom: var(--s-3);
}
.proj-title .ch {
  display: inline-block; opacity: 0; transform: translateY(8px);
  transition: opacity .4s ease, transform .4s ease;
}
.proj-title .ch.show { opacity: 1; transform: translateY(0); }

/* Description — reveals on hover */
.proj-desc {
  font-family: var(--f-body); font-size: var(--t-body); line-height: var(--lh-body);
  color: var(--c-secondary); font-weight: 400; letter-spacing: var(--ls-open);
  opacity: 0; transform: translateY(var(--s-2));
  transition: opacity .55s var(--ease) .05s, transform .55s var(--ease) .05s;
  max-width: 340px; margin-bottom: var(--s-4);
}
.proj:nth-child(even) .proj-desc { margin-left: auto; }
.proj:hover .proj-desc { opacity: .75; transform: translateY(0); }

/* Tag pills */
.proj-tags {
  display: flex; flex-wrap: wrap; gap: var(--s-1);
  opacity: 0;
  transition: opacity .5s var(--ease) .12s;
}
.proj:nth-child(even) .proj-tags { justify-content: flex-end; }
/* Tags fade in already at accent colour — no "unloaded" white state */
.proj:hover .proj-tags { opacity: 1; }
.proj-tag {
  font-family: var(--f-heading); font-size: 12px; font-weight: 700;
  letter-spacing: .18em; text-transform: uppercase;
  color: var(--c-accent);
  border: 1px solid rgba(91,160,164,.38); padding: 6px 12px 5px;
  background: var(--c-accent-lo);
  transition: border-color .25s, color .25s, background .25s;
}
.proj:hover .proj-tag { border-color: rgba(91,160,164,.65); color: var(--c-accent); background: rgba(91,160,164,.18); }

/* Ghost number watermark */
.proj-ghost {
  position: absolute;
  font-family: var(--f-heading); font-size: clamp(240px, 36vw, 520px);
  font-weight: 700; color: transparent;
  -webkit-text-stroke: 1px rgba(91,160,164,.05);
  line-height: 1; pointer-events: none; user-select: none;
  bottom: -60px; letter-spacing: -.06em;
  transition: -webkit-text-stroke .5s, transform var(--dur-slow) var(--ease);
}
.proj:nth-child(odd)  .proj-ghost { right: -30px; }
.proj:nth-child(even) .proj-ghost { left: -30px; }
.proj:nth-child(odd):hover  .proj-ghost { transform: translateX(-6px); }
.proj:nth-child(even):hover .proj-ghost { transform: translateX(6px); }
.proj:hover .proj-ghost { -webkit-text-stroke: 1px rgba(91,160,164,.12); }

/* Slide counter */
.proj-counter {
  position: absolute; bottom: var(--pad); left: 50%; transform: translateX(-50%);
  font-family: var(--f-heading); font-size: 16px; font-weight: 700;
  letter-spacing: var(--ls-stencil); text-transform: uppercase;
  color: rgba(91,160,164,.5); display: flex; flex-direction: column; align-items: center; gap: 6px;
}
.proj-counter-nums { display: flex; align-items: center; gap: 4px; }
/* "Next Section" label on the last slide — single span, inherits .sc-cur colour */
.proj-counter-next { white-space: nowrap; }
.sc-cur { color: rgba(91,160,164,.85); }
.sc-sep { margin: 0 2px; }
.proj-counter-chevron {
  display: block; width: 14px; height: 9px;
  animation: counterChevron 1.8s ease-in-out infinite;
  color: rgba(91,160,164,.65);
}
.proj-counter-chevron.hide { opacity: 0; }
/* Pause the animation on slides that are off-screen — saves a compositor
   wakeup every frame on layers that the user can't see */
.proj:not([data-pos="active"]) .proj-counter-chevron {
  animation-play-state: paused;
}
@keyframes counterChevron {
  0%, 100% { transform: translateY(0); opacity: .5; }
  50%       { transform: translateY(4px); opacity: 1; }
}

/* Arrow CTA */
.proj-open {
  position: absolute; bottom: var(--pad);
  width: var(--s-7); height: var(--s-7);
  border: 1px solid rgba(255,255,255,.15);
  display: flex; align-items: center; justify-content: center;
  color: var(--c-primary);
  opacity: 0; transition: opacity .45s var(--ease), border-color .25s, background .25s, color .25s, transform .25s var(--ease);
  cursor: none;
}
.proj:nth-child(odd)  .proj-open { right: var(--pad); }
.proj:nth-child(even) .proj-open { left: var(--pad); }
.proj:hover .proj-open { opacity: 1; }
.proj-open:hover { border-color: var(--c-accent); background: var(--c-accent); color: var(--c-bg); }
.proj[data-pos="active"] .proj-open { opacity: 1; }

/* Mobile swipe hint — hidden on desktop */
.proj-swipe-hint { display: none !important; }

/* Dog-ear removed */

/* ── Hero mobile swipe hint — one-time, bottom strip ── */
#hero-mobile-hint {
  display: none; /* shown only on coarse pointer below */
  position: absolute;
  bottom: 0; left: 0; right: 0;
  height: var(--s-7); /* 64px — same as hero-text bottom offset so they don't overlap */
  align-items: center; justify-content: center; gap: var(--s-2);
  font-family: var(--f-heading); font-size: 10px; font-weight: 700;
  letter-spacing: .18em; text-transform: uppercase;
  color: var(--c-secondary); opacity: 0;
  pointer-events: none; z-index: var(--z-text);
  transition: opacity .6s var(--ease);
}
#hero-mobile-hint.visible { opacity: .72; }
#hero-mobile-hint.gone    { opacity: 0; pointer-events: none; transition: opacity .4s ease; }
.hmh-icon { flex-shrink: 0; }

/* Carousel dot nav — right rail */
#c-dots {
  position: fixed; right: 20px; top: 50%; transform: translateY(-50%);
  z-index: var(--z-overlay); display: flex; flex-direction: column; gap: 12px;
  pointer-events: none; opacity: 0; transition: opacity .4s;
  align-items: center;
  padding: 4px 0;
}
.c-dot-wrap {
  display: flex; align-items: center; justify-content: center;
  width: 18px; height: 18px;
  cursor: pointer; pointer-events: auto;
  position: relative;
}
.c-dot {
  width: 5px; height: 5px; border-radius: 50%;
  background: rgba(91,160,164,.55); display: block;
  transition: background .3s, transform .3s, box-shadow .3s;
}
.c-dot-wrap.on .c-dot {
  background: var(--c-accent);
  transform: scale(2);
  box-shadow: 0 0 0 3px rgba(91,160,164,.18);
}

/* Next-section arrow — aligned to dot column center */
.c-next-arrow {
  align-self: center;
  margin-top: 6px;
  color: var(--c-accent);
  opacity: 0; pointer-events: none;
  transition: opacity 0.5s var(--ease);
}
.c-next-arrow.visible { opacity: 1; }
.c-next-arrow svg {
  display: block;
  width: 6px; height: 9px;
  animation: moreBelow 2s ease-in-out infinite;
}
@keyframes moreBelow {
  0%, 100% { transform: translateY(0); opacity: 0.55; }
  50%       { transform: translateY(5px); opacity: 1; }
}

/* Stage edge fades */
.stage-edge {
  position: absolute; left: 0; right: 0; z-index: var(--z-stage);
  height: 80px; pointer-events: none;
  opacity: 0; transition: opacity .5s var(--ease);
}
.stage-edge-top { top: 0; background: linear-gradient(to bottom, #080909 0%, transparent 100%); }
.stage-edge-bot { bottom: 0; background: linear-gradient(to top, #080909 0%, transparent 100%); }
.projects.carousel-active .stage-edge { opacity: 1; }
.about-stage.engaged .stage-edge { opacity: 1; }

/* ═══════════════════════════════════════════════════════════════
   § ABOUT — scroll-lock panel engine
═══════════════════════════════════════════════════════════════ */
.about-spacer { pointer-events: none; }

.about-stage {
  position: fixed; inset: 0; z-index: var(--z-about);
  background: #080909; /* always dark — about is a cinematic dark experience */
  pointer-events: none; visibility: hidden;
  opacity: 0;
  transition: opacity 0.35s var(--ease), visibility 0s linear 0.35s;
  contain: layout style paint;
  touch-action: none;
}
.about-stage.engaged {
  pointer-events: auto; visibility: visible;
  opacity: 1;
  transition: opacity 0.35s var(--ease), visibility 0s linear 0s;
}

.panels-host {
  position: relative; width: 100%; height: 100%; overflow: hidden;
}
/* Subtle vignette overlay for depth — always dark since about-stage is always dark */
.panels-host::after {
  content: '';
  position: absolute; inset: 0; z-index: var(--z-stage);
  pointer-events: none;
  background:
    linear-gradient(to bottom, #080909 0%, transparent 6%, transparent 94%, #080909 100%);
}

/* Decorative vertical rules */
.v-rules {
  position: absolute; inset: 0;
  display: flex; justify-content: space-between;
  padding: 0 var(--pad); pointer-events: none; z-index: 1;
}
.v-rule { width: 1px; height: 100%; background: var(--c-rule); }

/* Kill staggered child transitions the moment a panel stops being active —
   prevents text/media from slowly fading while the panel slides away */
.panel.is-after *,
.panel.is-below * {
  transition-duration: 0s !important;
  transition-delay:    0s !important;
}

/* Panel transitions */
.panel {
  position: absolute; inset: 0;
  display: grid; grid-template-columns: 1fr 1fr;
  z-index: 1;
  /* No will-change here — idle panels don't need compositor promotion.
     The browser creates a layer on demand when opacity/transform animates. */
  opacity: 0; transform: translateY(56px) translateZ(0); pointer-events: none;
  transition: transform 0.85s var(--ease), opacity 0.70s var(--ease);
}
.panel.is-active {
  /* Promote only the visible panel — keeps GPU layer count minimal so the
     cursor compositor thread isn't competing with 4 unused panel layers. */
  will-change: transform, opacity;
  opacity: 1; transform: translateY(0) translateZ(0); pointer-events: auto;
  /* Explicitly reset content-visibility so the panel is fully painted when active.
     Required because is-after/is-below set content-visibility:hidden. */
  content-visibility: visible;
}
.panel.is-after {
  opacity: 0; transform: translateY(-46px) translateZ(0); pointer-events: none;
  transition: transform 0.56s var(--snap), opacity 0.46s var(--ease-out);
  /* Off-screen panels: skip subtree style recalc and paint entirely.
     content-visibility:hidden keeps the panel laid out (no reflow on reveal)
     but skips painting and style evaluation for all descendants.
     contain-intrinsic-size prevents the stage from collapsing. */
  content-visibility: hidden;
  contain-intrinsic-size: 100vw 100vh;
}
.panel.is-below {
  opacity: 0; transform: translateY(56px) translateZ(0); pointer-events: none;
  transition: transform 0.56s var(--snap), opacity 0.46s var(--ease-out);
  content-visibility: hidden;
  contain-intrinsic-size: 100vw 100vh;
}

/* Left column */
.col-left {
  position: relative; display: flex; flex-direction: column;
  border-right: 1px solid var(--c-rule); overflow: hidden;
}
.ghost-num {
  position: absolute; top: -0.06em; left: -0.04em;
  font-family: var(--f-heading); font-size: clamp(180px, 26vw, 360px);
  font-weight: 100; letter-spacing: var(--ls-tight); line-height: 1;
  text-transform: uppercase; color: transparent;
  -webkit-text-stroke: 1px rgba(91,160,164,0.065);
  pointer-events: none; user-select: none; z-index: 1;
}
.col-top { flex: 1 1 0; min-height: 0; position: relative; overflow: hidden; z-index: 5; }
.col-bottom {
  flex: 0 0 auto; position: relative; z-index: 10;
  padding: var(--s-3) var(--pad) var(--s-8);
  background: linear-gradient(to bottom, transparent 0%, rgba(8,9,9,0.8) 25%, #080909 55%);
}

/* Chapter index */
.ch-index {
  display: flex; align-items: center; gap: var(--s-2); margin-bottom: var(--s-4);
}
.ch-index::before {
  content: ''; display: block; width: 32px; height: 1.5px;
  background: linear-gradient(to right, var(--c-accent), var(--c-accent-hi)); flex-shrink: 0;
}
/* Mobile duplicate — hidden on desktop, shown inside col-right on mobile */
.ch-index-mobile { display: none; }

/* Photo gallery */
.photo-gallery { position: absolute; inset: 0; pointer-events: none; }
/* Intro panel (panel 0) — portrait hero */
.panel[data-panel="0"] .pf-main   { position: absolute; top: 50%; left: 50%; transform: translate(-50%,-50%); width: 72%; aspect-ratio: 3/4; }
.panel[data-panel="0"] .pf-accent { display: none; }

/* Engine Bay (panel 1) */
.panel[data-panel="1"] .pf-main   { position: absolute; top: 50%; left: 50%; transform: translate(-52%,-52%) rotate(-1.4deg); width: 66%; aspect-ratio: 4/3; }
.panel[data-panel="1"] .pf-accent { position: absolute; bottom: 5%; right: 6%; width: 28%; aspect-ratio: 3/4; transform: rotate(2.2deg); z-index: 2; }

/* Shop Floor (panel 2) */
.panel[data-panel="2"] .pf-main   { position: absolute; top: 8%; left: 7%; width: 52%; aspect-ratio: 3/4; transform: rotate(-0.8deg); }
.panel[data-panel="2"] .pf-accent { position: absolute; bottom: 5%; right: 5%; width: 46%; aspect-ratio: 4/3; transform: rotate(1.6deg); z-index: 2; }

/* Practice Room (panel 3) */
.panel[data-panel="3"] .pf-main   { position: absolute; top: 10%; left: 5%; width: 72%; aspect-ratio: 16/10; transform: rotate(0.6deg); }
.panel[data-panel="3"] .pf-accent { position: absolute; bottom: 5%; right: 4%; width: 32%; aspect-ratio: 1/1; transform: rotate(-2deg); z-index: 2; }

.photo-frame {
  overflow: hidden;
  border: 1px solid rgba(91,160,164,0.16);
  box-shadow: var(--shadow-lg), inset 0 0 0 1px rgba(255,255,255,0.038);
  opacity: 0; transition: opacity 0.9s var(--ease), box-shadow 0.6s ease, transform 0.42s var(--ease);
}
.panel.is-active .pf-main   { opacity: 1; transition-delay: .05s; }
.panel.is-active .pf-accent { opacity: 1; transition-delay: .22s; }
.photo-frame img {
  width: 100%; height: 100%; object-fit: cover; display: block;
  filter: saturate(0.72) brightness(0.85);
  transition: filter .7s ease, transform .7s var(--ease);
}
.photo-placeholder {
  width: 100%; height: 100%;
  background:
    linear-gradient(135deg, rgba(91,160,164,0.07) 0%, transparent 60%),
    repeating-linear-gradient(0deg, transparent, transparent 39px, rgba(91,160,164,0.045) 39px, rgba(91,160,164,0.045) 40px),
    repeating-linear-gradient(90deg, transparent, transparent 39px, rgba(91,160,164,0.045) 39px, rgba(91,160,164,0.045) 40px);
  display: flex; align-items: center; justify-content: center;
}
.placeholder-label {
  font-family: var(--f-heading); font-size: var(--t-micro); font-weight: 700;
  letter-spacing: var(--ls-stencil); text-transform: uppercase;
  color: rgba(91,160,164,0.28); text-align: center; line-height: 1.8;
}

/* Right column */
.col-right {
  display: flex; flex-direction: column; justify-content: center;
  padding: var(--s-8) var(--pad) var(--s-8) var(--s-7);
}
.ch-rule {
  display: block; width: 40px; height: 1px; background: var(--c-accent);
  margin-bottom: var(--s-4); transform: scaleX(0); transform-origin: left;
  transition: transform 0.6s var(--ease) 0.1s;
}
.panel.is-active .ch-rule { transform: scaleX(1); }
.ch-title { margin-bottom: var(--s-5); line-height: var(--lh-heading); }

/* Per-word reveal in titles */
.ch-title .tline { display: block; overflow: hidden; padding-bottom: .06em; }
.ch-title .tword {
  display: inline-block; transform: translateY(110%); opacity: 0;
  transition: transform 0.75s var(--ease), opacity 0.60s var(--ease);
}
.panel.is-active .tline:nth-child(1) .tword { transform: translateY(0); opacity: 1; transition-delay: .08s; }
.panel.is-active .tline:nth-child(2) .tword { transform: translateY(0); opacity: 1; transition-delay: .20s; }
.panel.is-active .tline:nth-child(3) .tword { transform: translateY(0); opacity: 1; transition-delay: .32s; }
.ch-body {
  max-width: var(--max-w-text); color: var(--c-secondary);
  opacity: 0; transform: translateY(16px);
  transition: opacity 0.75s var(--ease) .44s, transform 0.75s var(--ease) .44s;
}
.panel.is-active .ch-body { opacity: 1; transform: translateY(0); }

/* Statement panel */
.panel-statement {
  grid-template-columns: 1fr 1fr; align-items: center;
  padding: var(--s-8) var(--pad);
  gap: var(--s-7);
}
/* Statement video — right side on desktop */
.stmt-video-row {
  opacity: 0; transform: translateY(20px);
  transition: opacity .9s var(--ease) .30s, transform .9s var(--ease) .30s;
}
.panel.is-active .stmt-video-row { opacity: 1; transform: none; }
.stmt-video-frame {
  position: relative; width: 100%; aspect-ratio: 16/9;
  background: var(--c-bg); overflow: hidden;
  border: 1px solid var(--c-rule);
  box-shadow: var(--shadow-lg);
}
.stmt-video-frame iframe {
  position: absolute; inset: 0; width: 100%; height: 100%; border: none; display: block;
}
.stmt-wm {
  position: absolute; right: calc(var(--pad) - 0.04em); bottom: -0.1em;
  font-family: var(--f-heading); font-size: clamp(120px, 18vw, 260px);
  font-weight: 700; letter-spacing: var(--ls-tight); line-height: 1;
  color: transparent; -webkit-text-stroke: 1px rgba(91,160,164,0.09);
  user-select: none; pointer-events: none;
  opacity: 0; transition: opacity 1s var(--ease) .5s;
}
.panel.is-active .stmt-wm { opacity: 1; }
.stmt-wrap { max-width: 820px; }
.stmt-kicker {
  display: flex; align-items: center; gap: var(--s-2); margin-bottom: var(--s-5);
  opacity: 0; transform: translateY(12px);
  transition: opacity .6s var(--ease), transform .6s var(--ease);
}
.stmt-kicker::before { content: ''; display: block; width: 32px; height: 1px; background: var(--c-accent); flex-shrink: 0; }
.panel.is-active .stmt-kicker { opacity: 1; transform: none; }
.stmt-text {
  opacity: 0; transform: translateY(20px);
  transition: opacity .9s var(--ease) .18s, transform .9s var(--ease) .18s;
}
.panel.is-active .stmt-text { opacity: 1; transform: none; }

/* About progress dots — desktop: exact mirror of #c-dots (project carousel style).
   Mobile: hidden (replaced by the about-counter indicator). */
.about-stage .prog-dots {
  position: absolute; right: 20px; top: 50%; transform: translateY(-50%);
  display: flex; flex-direction: column; gap: 12px;
  align-items: center;
  z-index: calc(var(--z-stage) + 10); /* above panels-host vignette */
  pointer-events: none;
  padding: 4px 0;
  transition: opacity 0.4s var(--ease);
}
/* Wrapper — exact copy of .c-dot-wrap */
.prog-dot-wrap {
  display: flex; align-items: center; justify-content: center;
  width: 18px; height: 18px;
  position: relative;
}
/* Inner dot — exact copy of .c-dot */
.prog-dot {
  width: 5px; height: 5px; border-radius: 50%;
  background: rgba(91,160,164,.55); display: block;
  transition: background .3s, transform .3s, box-shadow .3s;
}
.prog-dot-wrap.on .prog-dot {
  background: var(--c-accent);
  transform: scale(2);
  box-shadow: 0 0 0 3px rgba(91,160,164,.18);
}

/* Hide progress dots on the contact panel */
.about-stage:has([data-panel="5"].is-active) .prog-dots {
  opacity: 0;
  pointer-events: none;
}

/* ── About counter — chevron + N/N indicator, same style as proj-counter ──
   Now lives inside each .panel (matches carousel's per-slide static counter).
   Positioned bottom-centre of the panel. */
.about-counter {
  position: absolute; bottom: var(--pad); left: 50%; transform: translateX(-50%);
  font-family: var(--f-heading); font-size: 16px; font-weight: 700;
  letter-spacing: var(--ls-stencil); text-transform: uppercase;
  color: rgba(91,160,164,.5);
  display: flex; flex-direction: column; align-items: center; gap: 6px;
  z-index: calc(var(--z-stage) + 10);
  pointer-events: none;
}
.about-counter-nums { display: flex; align-items: center; gap: 4px; }
.ac-cur { color: rgba(91,160,164,.85); }
.ac-sep { margin: 0 2px; }
.about-counter-chevron {
  display: block; width: 14px; height: 9px;
  animation: counterChevron 1.8s ease-in-out infinite;
  color: rgba(91,160,164,.65);
}
/* "Next Section" label on the contact panel */
.about-counter-next-label {
  display: block;
  font-family: var(--f-heading); font-size: 13px; font-weight: 700;
  letter-spacing: var(--ls-stencil); text-transform: uppercase;
  color: rgba(91,160,164,.65);
  animation: counterChevron 1.8s ease-in-out infinite;
}

/* Pause about-counter chevron + "Next Section" label animations when the
   about section is not active — these elements exist in all panels but only
   the active one is visible; running the animation on hidden panels wastes
   compositor resources. */
body:not([data-section="about"]) .about-counter-chevron,
body:not([data-section="about"]) .about-counter-next-label { animation-play-state: paused; }

/* Light mode overrides for about-counter (about stage is always dark, so pin colours) */
[data-theme="light"] .about-counter { color: rgba(91,160,164,.5); }
[data-theme="light"] .about-counter .ac-cur { color: rgba(91,160,164,.85); }

/* ═══════════════════════════════════════════════════════════════
   § SCROLL REVEAL
═══════════════════════════════════════════════════════════════ */
.rev {
  opacity: 0; transform: translateY(var(--s-3));
  transition: opacity var(--dur-slow) var(--ease), transform var(--dur-xl) var(--ease);
}
.rev.in { opacity: 1; transform: translateY(0); }

/* Rev-stagger: animated children with cascade delay */
.rev-stagger > * {
  opacity: 0; transform: translateY(24px);
  transition: opacity var(--dur-slow) var(--ease), transform var(--dur-xl) var(--ease);
}
.rev-stagger.in > *:nth-child(1) { opacity: 1; transform: none; transition-delay: 0.00s; }
.rev-stagger.in > *:nth-child(2) { opacity: 1; transform: none; transition-delay: 0.09s; }
.rev-stagger.in > *:nth-child(3) { opacity: 1; transform: none; transition-delay: 0.18s; }
.rev-stagger.in > *:nth-child(4) { opacity: 1; transform: none; transition-delay: 0.27s; }
.rev-stagger.in > *:nth-child(5) { opacity: 1; transform: none; transition-delay: 0.36s; }
.rev-stagger.in > *:nth-child(6) { opacity: 1; transform: none; transition-delay: 0.45s; }

/* Reduced motion — consolidated below in § ACCESSIBILITY */

.d1 { transition-delay: 0.08s; }
.d2 { transition-delay: 0.20s; }
.d3 { transition-delay: 0.34s; }

/* ═══════════════════════════════════════════════════════════════
   § KEYFRAMES
═══════════════════════════════════════════════════════════════ */
@keyframes fu { from { opacity: 0; transform: translateY(var(--s-2)); } to { opacity: 1; transform: translateY(0); } }
@keyframes fd { from { opacity: 0; transform: translateY(-10px); }       to { opacity: 1; transform: translateY(0); } }
@keyframes fadeIn { from { opacity: 0; } to { opacity: 1; } }

/* ═══════════════════════════════════════════════════════════════
   § ACCESSIBILITY
═══════════════════════════════════════════════════════════════ */
@media (pointer: coarse) {
  #cur, #cur-r { display: none !important; }
  body { cursor: auto; }
  a, button, .n-ham { cursor: pointer; }
  .nav-drawer a { cursor: pointer; }
  .proj { cursor: pointer; }
  .c-link, .c-soc, .c-dl { cursor: pointer; }
  /* swipe hint hidden on mobile */
  .proj[data-pos="active"] .proj-open  { opacity: 1; }
  .proj[data-pos="active"] .proj-title .ch { opacity: 1; transform: translateY(0); }
  /* Body text and tags are hover-only on desktop; on touch show them
     directly on the active slide since hover doesn't exist */
  .proj[data-pos="active"] .proj-desc { opacity: .75; transform: translateY(0); }
  .proj[data-pos="active"] .proj-tags { opacity: 1; }
  /* Show hero mobile hint on touch devices */
  #hero-mobile-hint { display: flex; }
}
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: .01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: .01ms !important;
  }
  #grain { display: none; }
  .hero-text, .model-label, .model-hint, .hero-coords,
  .hero-h1 .word, .n-logo, .n-links, .n-right { opacity: 1 !important; transform: none !important; }
  .hero-eyebrow-line { transform: scaleX(1) !important; }
  .hero-sub, .hero-avail { opacity: 1 !important; }
}

/* ═══════════════════════════════════════════════════════════════
   § RESPONSIVE
═══════════════════════════════════════════════════════════════ */
@media (max-width: 1024px) {
  .proj-text { width: 55%; }
}

@media (max-width: 768px) {
  /* Nav — flex so logo is left, controls are always right */
  nav#nav {
    display: flex;
    justify-content: space-between;
    align-items: center;
    background: linear-gradient(to bottom, rgba(8,9,9,.95) 0%, rgba(8,9,9,.75) 60%, transparent 100%);
  }
  nav#nav.scrolled { background: rgba(8,9,9,.95); }
  [data-theme="light"] nav#nav { background: linear-gradient(to bottom, rgba(237,238,237,.95) 0%, rgba(237,238,237,.75) 60%, transparent 100%); }
  [data-theme="light"] nav#nav.scrolled { background: rgba(237,238,237,.96); }
  .n-links, .n-avail { display: none; }
  .n-ham { display: flex; }
  .n-right { display: flex; align-items: center; gap: var(--s-1); justify-self: unset; }
  #section-indicator { display: none; }

  /* Hero */
  .hero-text { max-width: 88vw; bottom: var(--s-7); }
  .hero-h1   { font-size: clamp(54px, 14vw, 88px); }
  .hero-coords { display: none; }
  .model-label-br { display: none; }
  .model-hint { display: none; }

  /* 360 drag icon — mobile hero, sits responsively below the navbar */
  .hero-mobile-360 {
    display: flex;
    position: absolute;
    top: calc(60px + var(--s-3)); /* nav height + breathing room */
    right: var(--pad);
    z-index: var(--z-raised);
    align-items: center; gap: 7px;
    font-family: var(--f-heading); font-size: var(--t-micro); font-weight: 700;
    letter-spacing: var(--ls-stencil); text-transform: uppercase;
    color: rgba(91,160,164,0.75);
    white-space: nowrap; pointer-events: none;
    opacity: 0; transition: opacity var(--dur-mid) var(--ease) 1.3s;
  }
  .hero-mobile-360.is-revealed { opacity: 1; }
  .hero-mobile-360.hidden { opacity: 0 !important; transition-duration: .8s !important; }
  .hero-mobile-360-badge {
    position: relative; width: 36px; height: 36px;
    display: flex; align-items: center; justify-content: center;
    flex-shrink: 0;
  }
  .hero-mobile-360-ring {
    position: absolute; inset: 0; width: 100%; height: 100%;
    color: var(--c-accent);
    animation: hintSpin 8s linear infinite;
  }
  .hero-mobile-360-label {
    position: relative; z-index: 1;
    font-family: var(--f-heading); font-size: 9px; font-weight: 700;
    letter-spacing: 0.04em; color: rgba(91,160,164,0.85);
  }
  .hero-vignette {
    background:
      linear-gradient(to right,  rgba(8,9,9,0.97) 0%, rgba(8,9,9,0.75) 28%, rgba(8,9,9,0.25) 58%, transparent 78%),
      linear-gradient(to top,    rgba(8,9,9,0.95) 0%, rgba(8,9,9,0.45) 7%,  transparent 18%),
      linear-gradient(to bottom, rgba(8,9,9,0.60) 0%, transparent 16%);
  }
  [data-theme="light"] .hero-vignette {
    background:
      linear-gradient(to right,  rgba(237,238,237,0.97) 0%, rgba(237,238,237,0.75) 28%, rgba(237,238,237,0.25) 58%, transparent 78%),
      linear-gradient(to top,    rgba(237,238,237,0.95) 0%, rgba(237,238,237,0.45) 7%,  transparent 18%),
      linear-gradient(to bottom, rgba(237,238,237,0.60) 0%, transparent 16%);
  }

  /* Carousel */
  .proj:nth-child(odd)  .proj-text,
  .proj:nth-child(even) .proj-text {
    width: 88%; top: auto; transform: none;
    bottom: 100px; left: var(--pad); right: auto; text-align: left;
  }
  .proj:hover .proj-text { transform: none; }
  .proj:nth-child(odd)  .proj-num,
  .proj:nth-child(even) .proj-num { justify-content: flex-start; }
  .proj:nth-child(even) .proj-num::after  { display: none; }
  .proj:nth-child(even) .proj-num::before { display: inline-block; }
  .proj:nth-child(even) .proj-veil { background: linear-gradient(to right, rgba(8,9,9,.95) 0%, rgba(8,9,9,.6) 42%, transparent 72%); }
  .proj:nth-child(odd)  .proj-tags,
  .proj:nth-child(even) .proj-tags { justify-content: flex-start; }
  .proj:nth-child(odd)  .proj-open,
  .proj:nth-child(even) .proj-open { bottom: var(--pad); right: var(--pad); left: auto; }
  .proj-ghost { display: none; }
  .proj-title { font-size: clamp(44px, 13vw, 80px); }
  .proj-desc { max-width: 100%; }

  /* ═══════════════════════════════════════════════════════════
     ABOUT — mobile layout
     Each panel fills the fixed fullscreen stage (position:absolute inset:0).
     The image fills the WHOLE panel as a background.
     Text is layered at the bottom over a strong gradient — like a film poster.
     No splitting, no cropping. AppController owns all touch so no overflow scroll.
  ═══════════════════════════════════════════════════════════ */

  /* Panel: image fills everything, text floats at bottom */
  .panel {
    display: block; /* kill desktop grid */
    position: absolute; inset: 0; /* already set in base, reinforce */
  }

  /* col-left becomes the full-panel image background */
  .col-left {
    position: absolute;
    inset: 0;
    display: block;
    border-right: none;
    overflow: hidden;
    /* remove desktop flex sizing */
    flex: none;
    height: auto;
    min-height: 0;
    max-height: none;
  }

  /* col-top (photo frames container) fills col-left entirely */
  .col-top {
    position: absolute; inset: 0;
    overflow: hidden;
    flex: none;
  }

  /* Photo gallery sits inside col-top, also full coverage */
  .photo-gallery {
    position: absolute; inset: 0;
    pointer-events: none;
  }

  /* Desktop: photos are floating frames. Mobile: pf-main goes full-bleed,
     pf-accent is hidden — one clean, confident image fills the screen */
  .pf-main {
    position: absolute !important;
    inset: 0 !important;
    width: 100% !important;
    height: 100% !important;
    top: auto !important;
    left: auto !important;
    transform: none !important;
    aspect-ratio: unset !important;
    border: none !important;
    box-shadow: none !important;
  }
  .pf-main img {
    width: 100%; height: 100%;
    object-fit: cover;
    filter: saturate(0.80) brightness(0.72);
  }
  .pf-accent { display: none !important; }

  /* Ghost number — hide on mobile */
  .ghost-num { display: none; }

  /* Strong gradient from bottom so text pops — extends higher to cover
     the full text block now that col-right also carries a scrim */
  .col-bottom {
    position: absolute;
    bottom: 0; left: 0; right: 0;
    /* Text lives here — push it up to give breathing room above safe area */
    padding: var(--s-10) var(--pad) var(--s-6);
    z-index: 10;
    background:
      linear-gradient(to top,
        rgba(8,9,9,1.00) 0%,
        rgba(8,9,9,0.92) 20%,
        rgba(8,9,9,0.70) 40%,
        rgba(8,9,9,0.35) 60%,
        rgba(8,9,9,0.10) 75%,
        transparent 90%);
  }
  [data-theme="light"] .col-bottom {
    background: linear-gradient(to top,
      rgba(8,9,9,1.00) 0%,
      rgba(8,9,9,0.92) 20%,
      rgba(8,9,9,0.70) 40%,
      rgba(8,9,9,0.35) 60%,
      rgba(8,9,9,0.10) 75%,
      transparent 90%);
  }
  .ch-index  { margin-bottom: var(--s-2); }
  .t-chapter { font-size: clamp(36px, 9vw, 56px); line-height: 0.90; opacity: 0.55; }

  /* col-right: text block sits OVER the image, at the bottom.
     The blur lives on a ::before pseudo so the mask never clips the text. */
  .col-right {
    position: absolute;
    bottom: 0; left: 0; right: 0;
    padding: var(--s-6) var(--pad) var(--s-7) var(--pad);
    z-index: 20;
    display: flex;
    flex-direction: column;
    justify-content: flex-end;
    background: none;
    isolation: isolate;
  }
  .col-right::before {
    content: '';
    position: absolute;
    inset: 0;
    z-index: -1;
    /* backdrop-filter removed: the strong gradient already provides enough scrim
       for text legibility (rgba 0.94 at bottom). Replacing blur with a slightly
       more opaque solid gradient saves the GPU texture snapshot on panel transitions. */
    background: linear-gradient(to top,
      rgba(8,9,9,0.97) 0%,
      rgba(8,9,9,0.86) 28%,
      rgba(8,9,9,0.50) 54%,
      transparent 78%);
    -webkit-mask-image: linear-gradient(to top, black 0%, black 62%, transparent 92%);
    mask-image: linear-gradient(to top, black 0%, black 62%, transparent 92%);
  }
  [data-theme="light"] .col-right::before {
    background: linear-gradient(to top,
      rgba(8,9,9,0.94) 0%,
      rgba(8,9,9,0.78) 28%,
      rgba(8,9,9,0.42) 54%,
      transparent 78%);
  }

  /* ch-label (The Engine Bay / The Shop Floor / The Practice Room) — hide on mobile */
  .ch-label { display: none !important; }

  /* ch-index in col-bottom — hidden on mobile (replaced by mobile copy in col-right) */
  .col-bottom .ch-index { display: none; }

  /* ch-index-mobile — hidden on mobile (removed for cleanliness) */
  .ch-index-mobile {
    display: none !important;
  }

  /* Accent rule */
  .ch-rule { margin-bottom: var(--s-3); }
  /* Big, confident heading — always white on mobile (dark scrim behind) */
  .ch-title {
    font-size: clamp(38px, 10vw, 58px);
    line-height: 0.90;
    margin-bottom: var(--s-3);
    color: #edeeed;
    text-shadow:
      0 1px 12px rgba(0,0,0,0.70),
      0 2px 32px rgba(0,0,0,0.50);
  }
  [data-theme="light"] .ch-title {
    color: #edeeed;
    text-shadow:
      0 1px 12px rgba(0,0,0,0.70),
      0 2px 32px rgba(0,0,0,0.50);
  }
  /* Body copy — always white/light on mobile, pops off dark scrim */
  .ch-body {
    font-size: 15px;
    line-height: 1.62;
    max-width: 100%;
    color: #edeeed;
    opacity: 0.82;
    text-shadow: 0 1px 8px rgba(0,0,0,0.60);
  }
  [data-theme="light"] .ch-body {
    color: #edeeed;
    opacity: 0.82;
    text-shadow: 0 1px 8px rgba(0,0,0,0.60);
  }

  /* ── Intro panel (panel 0) — portrait fills screen ── */
  /* panel-intro doesn't use col-top/photo-gallery — it uses intro-portrait-wrap directly */
  .panel-intro .col-left {
    position: absolute; inset: 0;
    display: block;
    background: var(--c-bg);
  }
  .intro-portrait-wrap {
    position: absolute; inset: 0;
  }
  .intro-portrait-wrap img {
    width: 100%; height: 100%;
    object-fit: cover; object-position: top center;
    filter: saturate(0.72) brightness(0.78);
  }
  .intro-portrait-vignette {
    background:
      linear-gradient(to top,
        rgba(8,9,9,1.00) 0%,
        rgba(8,9,9,0.88) 28%,
        rgba(8,9,9,0.55) 48%,
        rgba(8,9,9,0.15) 68%,
        transparent 85%);
  }
  [data-theme="light"] .intro-portrait-vignette {
    background: linear-gradient(to top,
      rgba(8,9,9,1.00) 0%,
      rgba(8,9,9,0.88) 28%,
      rgba(8,9,9,0.55) 48%,
      rgba(8,9,9,0.15) 68%,
      transparent 85%);
  }
  /* Text over portrait */
  .panel-intro .col-right {
    position: absolute;
    bottom: 0; left: 0; right: 0;
    padding: var(--s-6) var(--pad) var(--s-7) var(--pad);
    z-index: 20;
    justify-content: flex-end;
    background: none;
  }
  .panel-intro .col-right::before {
    content: '';
    position: absolute;
    inset: 0;
    z-index: -1;
    /* backdrop-filter removed — solid gradient matches legibility at lower GPU cost */
    background: linear-gradient(to top,
      rgba(8,9,9,0.97) 0%,
      rgba(8,9,9,0.86) 28%,
      rgba(8,9,9,0.50) 54%,
      transparent 78%);
    -webkit-mask-image: linear-gradient(to top, black 0%, black 62%, transparent 92%);
    mask-image: linear-gradient(to top, black 0%, black 62%, transparent 92%);
  }
  [data-theme="light"] .panel-intro .col-right::before {
    background: linear-gradient(to top,
      rgba(8,9,9,0.94) 0%,
      rgba(8,9,9,0.78) 28%,
      rgba(8,9,9,0.42) 54%,
      transparent 78%);
  }
  .intro-eyebrow { margin-bottom: var(--s-2); }
  .intro-name {
    font-size: clamp(42px, 11vw, 64px);
    line-height: 0.88;
    margin-bottom: var(--s-3);
    color: #edeeed;
    text-shadow:
      0 1px 12px rgba(0,0,0,0.72),
      0 2px 28px rgba(0,0,0,0.48);
  }
  [data-theme="light"] .intro-name {
    color: #edeeed;
    text-shadow:
      0 1px 12px rgba(0,0,0,0.72),
      0 2px 28px rgba(0,0,0,0.48);
  }
  .intro-meta {
    font-size: 15px;
    line-height: 1.60;
    color: #edeeed;
    opacity: 0.80;
    text-shadow: 0 1px 6px rgba(0,0,0,0.55);
  }
  [data-theme="light"] .intro-meta {
    color: #edeeed;
    opacity: 0.82;
    text-shadow: 0 1px 6px rgba(0,0,0,0.55);
  }

  /* ── Statement panel (panel 4) — no image, full-height text ── */
  .panel-statement {
    display: flex;
    flex-direction: column;
    justify-content: center;
    padding: var(--s-6) var(--pad) var(--s-6) var(--pad);
    overflow-y: auto;
    overflow-x: hidden;
    /* override any grid from above */
    grid-template-columns: unset;
    gap: var(--s-5);
  }
  .stmt-wm        { display: none; }
  /* Video: shown on mobile, sits ABOVE the text wrap, constrained so it
     doesn't crowd the statement copy on small screens */
  .stmt-video-row {
    display: block;
    order: -1;      /* push above .stmt-wrap */
    width: 100%;
    max-width: 100%;
    opacity: 1 !important;  /* always visible — no animated fade on mobile */
    transform: none !important;
  }
  .stmt-video-frame {
    width: 100%;
    aspect-ratio: 16 / 9;
    max-height: 38vh;  /* cap height so text still fits on short screens */
    overflow: hidden;
  }
  .stmt-wrap      { max-width: 100%; order: 0; }
  .stmt-kicker    { margin-bottom: var(--s-4); }
  .stmt-text      { font-size: clamp(16px, 5vw, 24px); line-height: 1.65; }

  /* Progress dots — hidden on mobile (about-counter handles navigation feedback) */
  .prog-dots { display: none !important; }

  /* About stage — always dark on mobile (image-forward design) */
  [data-theme="light"] .about-stage { background: #080909; }
  [data-theme="light"] .about-stage .stage-edge-top { background: linear-gradient(to bottom, #080909 0%, transparent 100%); }
  [data-theme="light"] .about-stage .stage-edge-bot { background: linear-gradient(to top, #080909 0%, transparent 100%); }
  [data-theme="light"] .panels-host::after {
    background: linear-gradient(to bottom, #080909 0%, transparent 6%, transparent 94%, #080909 100%);
  }
  /* Statement + contact panels on mobile light mode: keep bg light (no image) */
  [data-theme="light"] .panel-statement,
  [data-theme="light"] .panel-contact {
    background: var(--c-bg);
  }

  /* Progress dots light mode — removed (dots hidden on mobile) */

  /* Contact panel — mobile: single column, content centred in viewport */
  .panel-contact {
    display: flex;
    flex-direction: column;
    justify-content: center;
    padding: 0;
    background: var(--c-bg);
    overflow-x: hidden;
  }
  .panel-contact::before { display: none; }
  .panel-contact .contact-img,
  .panel-contact .contact-wm { display: none; }
  /* Override grid to flex column so items stack top-down */
  .panel-contact .contact-inner {
    display: flex !important;
    flex-direction: column !important;
    grid-template-columns: unset !important;
    /* Single gap value controls all vertical spacing between children —
       no individual item margins or paddings should fight this */
    gap: var(--s-3);
    width: 100%;
    max-width: 100%;
    /* var(--pad) is the single left/right rail — everything aligns to it */
    padding: var(--s-6) var(--pad) calc(var(--s-6) + 44px) !important;
    margin-bottom: 0;
    overflow-x: hidden;
  }
  /* Strip all grid placement and individual spacing that would misalign
     items relative to the flex column's single left rail */
  .panel-contact .contact-kicker,
  .panel-contact .contact-h,
  .panel-contact .contact-sub,
  .panel-contact .contact-right {
    grid-column: unset; grid-row: unset;
    align-self: unset;
    margin: 0;
  }
  /* Kill kicker's bottom padding — gap handles spacing */
  .panel-contact .contact-kicker { padding-bottom: 0; }
  .panel-contact .contact-h {
    font-size: clamp(52px, 13vw, 80px);
    color: var(--c-primary);
    /* No extra padding — gap handles spacing above and below */
    padding-bottom: 0;
  }
  /* Kill sub-copy's bottom padding — gap handles it */
  .panel-contact .contact-sub { padding-bottom: 0; max-width: 100%; }
  .panel-contact .contact-right { width: 100%; max-width: 100%; }
  .panel-contact .contact-fields { width: 100%; }
  /* Force field label column width — base clamp() rule comes later in cascade */
  .panel-contact .c-field {
    grid-template-columns: 72px 1fr !important;
    gap: var(--s-2);
    width: 100%;
    /* No extra horizontal padding on the field itself — inner rail = contact-inner padding */
    padding-left: 0 !important;
  }
  .panel-contact .c-row { flex-wrap: wrap; max-width: 100%; gap: var(--s-2); }
  .panel-contact .c-dl--primary { font-size: clamp(28px, 7.5vw, 44px); cursor: pointer; display: block; max-width: 100%; overflow: hidden; text-overflow: ellipsis; }
  .panel-contact .c-soc { cursor: pointer; font-size: clamp(16px, 5vw, 24px); max-width: 100%; display: inline-block; }
  /* Panel footer — mobile */
  .panel-footer { padding: var(--s-2) var(--pad); flex-direction: column; gap: 2px; align-items: center; text-align: center; }
}

@media (max-width: 480px) {
  .hero-h1   { font-size: clamp(48px, 14vw, 68px); }
  .ch-title  { font-size: clamp(34px, 9.5vw, 52px); }
  .intro-name { font-size: clamp(38px, 10.5vw, 58px); }
  .t-statement { font-size: clamp(16px, 4.8vw, 24px); }
  .panel-contact .contact-h { font-size: clamp(44px, 13.5vw, 64px); }
  .panel-contact .c-soc { font-size: clamp(15px, 5.5vw, 22px); }
  .panel-contact .c-dl--primary { font-size: clamp(24px, 7.5vw, 36px); }
  .panel-contact .c-field { grid-template-columns: 72px 1fr !important; }}

/* ═══════════════════════════════════════════════════════════════
   § ENHANCED MICRO-INTERACTIONS & AWWWARDS POLISH
═══════════════════════════════════════════════════════════════ */

/* ── Cursor blend enhancement ── */
/* cursor enhancements handled in crosshair section above */

/* Nav link padding-bottom already set above for underline space */

/* Pause next-section arrow animation when carousel is not active */
body:not([data-section="carousel"]) .c-next-arrow svg { animation-play-state: paused; }

/* ── Project slide: image parallax depth ── */
.proj[data-pos="active"] .proj-img {
  transform: scale(1.0);
}

/* ── Protected work slide ── */
/* Multi-layer background: dark base + faint grid + accent glow + noise */
.proj-img--protected {
  background-color: #070809;
  /* Unsplash placeholder — moody architectural interior, desaturated to fit the palette */
  background-image: url('https://raw.githubusercontent.com/kieranjgreen18-cyber/KieranGreen/refs/heads/main/assets/images/CC_Final%20Presentation_0272.webp');
  background-size: cover;
  background-position: center 40%;
  opacity: 1 !important;
  /* filter removed — replaced by ::after overlay on .proj-img for consistency */
  transition: transform 1.15s var(--ease) !important;
}
/* Protected slide resting overlay — heavier dark than the base .proj-img::after */
.proj-img--protected::after {
  background: rgba(4,5,5,.72);
  opacity: 1;
}
/* Suppress the base scale nudge — stays at resting scale on hover */
.proj--protected:hover .proj-img--protected {
  transform: scale(1.06) !important;
}
.proj--protected:hover .proj-img--protected::after { opacity: 0.52; }
.proj[data-pos="active"].proj--protected .proj-img--protected::after { opacity: 0.52; }
/* Protected slide veil — slide 05 is nth-child(odd), text sits left, so gradient fades from left */
.proj--protected .proj-veil,
.proj--protected:nth-child(odd) .proj-veil {
  background: linear-gradient(to right, rgba(7,8,9,0.97) 0%, rgba(7,8,9,0.65) 38%, transparent 68%);
}
/* Ghost number — slightly dimmer, accent-tinted */
.proj-ghost--protected {
  -webkit-text-stroke: 1px rgba(91,160,164,0.07);
  color: transparent;
}
/* Lock icon replaces arrow in open button */
.proj-open--protected {
  border-color: rgba(91,160,164,0.22);
}
.proj--protected:hover .proj-open--protected,
.proj[data-pos="active"].proj--protected .proj-open--protected {
  background: rgba(91,160,164,0.10);
  border-color: rgba(91,160,164,0.50);
  color: var(--c-accent);
}
/* Tags — muted style for NDA/protected */
.proj-tag--protected {
  border-color: rgba(91,160,164,0.14);
  color: var(--c-tertiary);
}
.proj-open:hover {
  transform: scale(1.08);
}
.proj-open:active {
  transform: scale(0.96);
  transition-duration: 0.08s;
}

/* ── Project tag stagger on slide active ── */
.proj[data-pos="active"] .proj-tag:nth-child(1) { transition-delay: 0.60s; }
.proj[data-pos="active"] .proj-tag:nth-child(2) { transition-delay: 0.68s; }
.proj[data-pos="active"] .proj-tag:nth-child(3) { transition-delay: 0.76s; }

/* ── Intro panel (panel 0) — portrait + greeting ── */
.panel-intro .col-left {
  display: flex; align-items: center; justify-content: center;
  background: var(--c-bg);
}
.intro-portrait-wrap {
  position: absolute; inset: 0; overflow: hidden;
}
.intro-portrait-wrap img {
  width: 100%; height: 100%; object-fit: cover; object-position: center 20%;
  filter: saturate(0.65) brightness(0.78);
  transition: filter .7s ease;
}
.panel.is-active .intro-portrait-wrap img { filter: saturate(0.82) brightness(0.88); }
.intro-portrait-vignette {
  position: absolute; inset: 0; pointer-events: none;
  background:
    linear-gradient(to right, transparent 60%, var(--c-bg) 100%),
    linear-gradient(to bottom, transparent 50%, var(--c-bg) 95%);
}
@media (min-width: 769px) {
  .panel-intro .col-right {
    display: flex; flex-direction: column; justify-content: center;
    padding: var(--s-8) var(--pad) var(--s-7);
  }
}
.intro-eyebrow {
  display: flex; align-items: center; gap: var(--s-2);
  margin-bottom: var(--s-4);
  opacity: 0; transform: translateY(10px);
  transition: opacity .6s var(--ease), transform .6s var(--ease);
}
.panel.is-active .intro-eyebrow { opacity: 1; transform: none; }
.intro-name {
  font-family: var(--f-heading);
  font-size: clamp(44px, 6.5vw, 88px);
  font-weight: 100; letter-spacing: var(--ls-tight); line-height: .92;
  text-transform: uppercase; color: var(--c-primary);
  margin-bottom: var(--s-5);
}
.intro-name .tline { display: block; overflow: hidden; padding-bottom: .05em; }
.intro-name .tword {
  display: inline-block; transform: translateY(110%); opacity: 0;
  transition: transform 0.75s var(--ease), opacity 0.60s var(--ease);
}
.panel.is-active .intro-name .tline:nth-child(1) .tword { transform: translateY(0); opacity: 1; transition-delay: .08s; }
.panel.is-active .intro-name .tline:nth-child(2) .tword { transform: translateY(0); opacity: 1; transition-delay: .20s; }
.panel.is-active .intro-name .tline:nth-child(3) .tword { transform: translateY(0); opacity: 1; transition-delay: .32s; }
.intro-meta {
  max-width: 400px; color: var(--c-secondary);
  opacity: 0; transform: translateY(16px);
  transition: opacity 0.75s var(--ease) .44s, transform 0.75s var(--ease) .44s;
}
.panel.is-active .intro-meta { opacity: 1; transform: translateY(0); }


.panel.is-active .photo-frame:hover {
  transform: translateY(-4px) rotate(0.5deg);
  box-shadow: var(--shadow-lg), 0 0 24px rgba(91,160,164,0.10);
  transition: transform 0.42s var(--ease), box-shadow 0.42s ease;
}
/* photo-frame transitions consolidated above */

/* ── Contact field row hover ── */
.c-field {
  transition: background var(--dur-fast) ease, padding-left var(--dur-fast) var(--ease);
  border-radius: 2px;
  padding-left: 0;
}
.c-field:hover {
  background: var(--c-surface);
  padding-left: var(--s-2);
}

/* ═══════════════════════════════════════════════════════════════
   § CONTACT PANEL (embedded as the last About carousel slide)
   Desktop: full-bleed bg image, content anchored bottom-left.
            Left col = kicker + monumental heading (bottom-aligned).
            Right col = sub-copy + links (bottom-aligned, matches heading base).
   Mobile:  single column, no image, content starts at top.
═══════════════════════════════════════════════════════════════ */
/* ── Contact panel: typography & visual — all viewports ── */
.panel-contact .contact-img {
  position: absolute; inset: 0;
  background-image: url('assets/images/sabrettahero.png');
  background-size: cover;
  background-position: 65% center;
  opacity: .32;
  filter: saturate(.5) contrast(1.05);
  mask-image:
    linear-gradient(to right,  rgba(0,0,0,1) 0%, rgba(0,0,0,0.55) 40%, rgba(0,0,0,0.12) 68%, transparent 100%),
    linear-gradient(to top,    rgba(0,0,0,1) 0%, rgba(0,0,0,0.6) 30%, transparent 60%);
  -webkit-mask-image:
    linear-gradient(to right,  rgba(0,0,0,1) 0%, rgba(0,0,0,0.55) 40%, rgba(0,0,0,0.12) 68%, transparent 100%),
    linear-gradient(to top,    rgba(0,0,0,1) 0%, rgba(0,0,0,0.6) 30%, transparent 60%);
  mask-composite: intersect;
  -webkit-mask-composite: source-in;
}
.panel-contact::before {
  content: '';
  position: absolute; left: -5%; bottom: -10%; width: 50%; height: 65%;
  background: radial-gradient(ellipse at 25% 85%, rgba(91,160,164,0.07) 0%, transparent 60%);
  pointer-events: none; z-index: 1;
}
.panel-contact .contact-wm {
  position: absolute; right: -3vw; top: 50%; transform: translateY(-50%);
  font-family: var(--f-heading); font-size: clamp(200px, 30vw, 440px); font-weight: 700;
  color: transparent; -webkit-text-stroke: 1px rgba(91,160,164,.05);
  pointer-events: none; user-select: none;
  line-height: 1; letter-spacing: -.06em; text-transform: uppercase; white-space: nowrap;
  z-index: 1;
}
.panel-contact .contact-inner {
  position: relative; z-index: 2;
  width: 100%;
  margin-bottom: 0;
}
.panel-contact .contact-kicker {
  display: flex; align-items: center; gap: var(--s-2);
  font-family: var(--f-heading); font-size: var(--t-micro); font-weight: 700;
  letter-spacing: var(--ls-stencil); text-transform: uppercase; color: var(--c-accent);
}
.panel-contact .contact-kicker::before {
  content: ''; width: 20px; height: 1px; background: var(--c-accent); flex-shrink: 0;
}
.panel-contact .contact-h {
  font-family: var(--f-heading);
  font-size: clamp(52px, 13vw, 148px);
  font-weight: 100;
  letter-spacing: var(--ls-tight); line-height: 0.88; text-transform: uppercase; color: var(--c-primary);
}
.panel-contact .contact-h em {
  font-style: italic; color: var(--c-accent); font-weight: 100;
  text-shadow: 0 0 60px rgba(91,160,164,0.20);
}
.panel-contact .contact-sub {
  font-family: var(--f-body); font-size: var(--t-body); line-height: var(--lh-body);
  color: var(--c-secondary); font-weight: 400; letter-spacing: var(--ls-open);
}
.panel-contact .contact-fields { display: flex; flex-direction: column; gap: 0; }
.panel-contact .c-field {
  padding: clamp(var(--s-2), 1.5vh, var(--s-3)) 0;
  border-bottom: 1px solid var(--c-rule);
  display: grid; grid-template-columns: clamp(72px, 9vw, 110px) 1fr;
  gap: var(--s-3); align-items: center;
  transition: background var(--dur-fast) ease, padding-left var(--dur-fast) var(--ease);
  border-radius: 2px; padding-left: 0;
}
.panel-contact .c-field:hover { background: var(--c-surface); padding-left: var(--s-2); }
.panel-contact .c-field:first-child { border-top: 1px solid var(--c-rule); }
.panel-contact .c-field-label {
  font-family: var(--f-heading); font-size: var(--t-micro); font-weight: 700;
  letter-spacing: var(--ls-stencil); text-transform: uppercase;
  color: var(--c-accent); opacity: .55;
}
.panel-contact .c-soc {
  font-family: var(--f-heading); font-size: clamp(18px, 2vw, 26px); font-weight: 100;
  letter-spacing: var(--ls-tight); text-transform: uppercase;
  color: var(--c-primary); text-decoration: none; cursor: none;
  display: inline-flex; align-items: center; gap: var(--s-2);
  transition: color .25s, gap .3s var(--ease); position: relative;
}
.panel-contact .c-soc::after {
  content: ''; position: absolute; bottom: -2px; left: 0;
  width: 100%; height: 1px; background: var(--c-accent);
  transform: scaleX(0); transform-origin: left; transition: transform .35s var(--ease);
}
.panel-contact .c-soc:hover { color: var(--c-accent); gap: var(--s-3); }
.panel-contact .c-soc:hover::after { transform: scaleX(1); }
.panel-contact .c-dl--primary {
  font-family: var(--f-heading); font-size: clamp(24px, 2.8vw, 42px); font-weight: 100;
  letter-spacing: var(--ls-tight); text-transform: uppercase;
  color: var(--c-accent); text-decoration: none; cursor: none;
  display: inline-flex; align-items: center; gap: var(--s-2);
  transition: color .25s, gap .3s var(--ease); position: relative;
}
.panel-contact .c-dl--primary::after {
  content: ''; position: absolute; bottom: -2px; left: 0;
  width: 100%; height: 1px; background: var(--c-accent);
  transform: scaleX(0); transform-origin: left; transition: transform .35s var(--ease);
}
.panel-contact .c-dl--primary:hover { color: var(--c-accent-hi); gap: var(--s-3); }
.panel-contact .c-dl--primary:hover::after { transform: scaleX(1); }
.panel-contact .c-row { display: flex; gap: var(--s-3); flex-wrap: wrap; align-items: center; }

/* ── Contact panel: layout — desktop only ── */
@media (min-width: 769px) {
  .panel-contact {
    position: absolute; inset: 0;
    display: flex; flex-direction: column; justify-content: flex-end;
    overflow: hidden;
  }
  .panel-contact .contact-inner {
    padding: clamp(var(--s-9), 12vh, var(--s-12)) var(--pad) clamp(var(--s-7), 8vh, var(--s-10));
    display: grid;
    grid-template-columns: 52fr 48fr;
    grid-template-rows: auto auto auto;
    column-gap: clamp(var(--s-6), 5vw, var(--s-10));
    row-gap: 0;
    align-items: end;
  }
  .panel-contact .contact-kicker {
    grid-column: 1; grid-row: 1;
    padding-bottom: var(--s-3);
  }
  .panel-contact .contact-h {
    grid-column: 1; grid-row: 2 / 4;
    align-self: end;
    font-size: clamp(64px, 9.5vw, 148px);
  }
  .panel-contact .contact-sub {
    grid-column: 2; grid-row: 2;
    align-self: end;
    max-width: 38ch;
    padding-bottom: var(--s-4);
  }
  .panel-contact .contact-right {
    grid-column: 2; grid-row: 3;
    align-self: end;
  }
  .panel-contact .c-field {
    grid-template-columns: clamp(72px, 9vw, 110px) 1fr;
  }
} /* end desktop layout — contact panel */

/* Panel footer — pinned to bottom of contact panel */
.panel-footer {
  position: absolute; bottom: 0; left: 0; right: 0;
  padding: var(--s-2) var(--pad);
  border-top: 1px solid var(--c-rule);
  display: flex; align-items: center; justify-content: space-between;
  z-index: 3;
}
.pf-logo {
  font-family: var(--f-heading); font-size: var(--t-nano); font-weight: 700;
  letter-spacing: var(--ls-stencil); text-transform: uppercase; color: var(--c-secondary);
  opacity: 0.55;
}
.pf-logo em { font-style: italic; color: var(--c-accent); }
.pf-copy {
  font-family: var(--f-meta); font-size: var(--t-nano); font-weight: 700;
  letter-spacing: var(--ls-stencil); text-transform: uppercase; color: var(--c-muted);
  opacity: 0.55;
}

/* Desktop light mode: statement (panel 4) and contact (panel 5) use light bg */
@media (min-width: 769px) {
  [data-theme="light"] .panel-statement {
    background: var(--c-bg);
  }
  [data-theme="light"] .panel-contact {
    background: var(--c-bg);
  }
  [data-theme="light"] .panel-contact .contact-h { color: var(--c-primary); }
  [data-theme="light"] .panel-contact .panel-footer { border-top-color: var(--c-rule); }
}


/* ── Dot glow when active — carousel and about ── */
.c-dot-wrap.on .c-dot,
.prog-dot-wrap.on .prog-dot {
  box-shadow: 0 0 5px rgba(91,160,164,0.50);
}

/* ── About stage: vertical rule fade-in ── */
.about-stage.engaged .v-rule {
  animation: fadeIn 0.8s var(--ease) both;
}
.about-stage.engaged .v-rule:nth-child(1) { animation-delay: 0.05s; }
.about-stage.engaged .v-rule:nth-child(2) { animation-delay: 0.10s; }
.about-stage.engaged .v-rule:nth-child(3) { animation-delay: 0.15s; }
.about-stage.engaged .v-rule:nth-child(4) { animation-delay: 0.20s; }
.about-stage.engaged .v-rule:nth-child(5) { animation-delay: 0.25s; }

/* ── Ghost number enhanced depth ── */
.proj:hover .proj-ghost {
  -webkit-text-stroke: 1px rgba(91,160,164,0.14);
}

/* Hero counter hide — JS adds .hide to fade it out on scroll */
.hero-counter.hide { opacity: 0 !important; transition-duration: .4s !important; }

/* ── Mobile: swipe hint entry + fade animations ── */
@keyframes swipeReveal {
  from { opacity: 0; transform: translateX(-50%) translateY(6px); }
  to   { opacity: 0.68; transform: translateX(-50%) translateY(0); }
}
@keyframes swipeHintFade {
  0%   { opacity: 0.68; }
  70%  { opacity: 0.68; }
  100% { opacity: 0; }
}
.proj-swipe-hint {
  animation: swipeReveal 0.5s var(--ease) 0.4s both, swipeHintFade 2.5s ease 1s forwards;
}

/* ── Keyboard focus ring enhancement ── */
a:focus-visible,
button:focus-visible {
  outline: 2px solid var(--c-accent);
  outline-offset: 4px;
  border-radius: 2px;
}

/* ── Selection color ── */
::selection {
  background: rgba(91,160,164,0.28);
  color: var(--c-primary);
}


/* ── Custom scrollbar ── */
::-webkit-scrollbar { width: 4px; }
::-webkit-scrollbar-track { background: var(--c-bg); }
::-webkit-scrollbar-thumb { background: var(--c-tertiary); border-radius: 2px; }
::-webkit-scrollbar-thumb:hover { background: var(--c-accent); }


/* ── Skip navigation link ── */
.skip-link {
  position: fixed;
  top: var(--s-3);
  left: var(--s-3);
  z-index: calc(var(--z-veil) + 1);
  background: var(--c-accent);
  color: var(--c-bg);
  font-family: var(--f-meta);
  font-size: var(--t-micro);
  font-weight: 700;
  letter-spacing: var(--ls-stencil);
  text-transform: uppercase;
  text-decoration: none;
  padding: var(--s-2) var(--s-3);
  opacity: 0;
  pointer-events: none;
  transform: translateY(-8px);
  transition: opacity var(--dur-fast) var(--ease), transform var(--dur-fast) var(--ease);
}
.skip-link:focus {
  opacity: 1;
  pointer-events: auto;
  transform: translateY(0);
  outline: none;
}


/* ── Performance: CSS containment ── */
/* Note: contain:style removed from .proj and .panel — it creates a new
   stacking context that conflicts with will-change compositing layers
   and breaks fixed/absolute z-index children within overlays. */
.proj           { contain: layout; }
.panel          { contain: layout; }

/* Promote hero vignette and overlay elements to their own compositor layers.
   These fade in/out on every section transition — promoting them prevents
   paint invalidations from bubbling up to the hero background. */
/* hero-vignette is a static gradient — no will-change needed.
   The veil slide-out (#veil) is the only overlay that truly animates on load. */
#veil           { will-change: transform; }

/* model-viewer canvas: hint the browser it is a GPU-composited surface.
   Avoids the canvas being torn out of the compositing tree on low-memory
   devices when other composited layers (nav blur, vignette) force a flush. */
model-viewer    { isolation: isolate; }

