/* =============================================================================
   PIXNUT — Brand, Marketing and Original IP Studio
   Dark, editorial, image-led. One continuous signal carries the whole page.

   Type:   Clash Display (display) · Satoshi (text)
   Colour: near-black ground, warm off-white, sky signal
   Motion: one native-scroll authority writes CSS custom properties
   ============================================================================= */

@import url("/css/fonts.css");

/* =============================================================================
   1. Tokens
   ============================================================================= */
:root {
  /* Ground */
  --bg:        #070707;
  --bg-2:      #0c0c0d;
  --bg-3:      #131314;
  --bg-raised: #101011;

  /* Rules and edges */
  --line:      rgba(241, 236, 226, 0.10);
  --line-soft: rgba(241, 236, 226, 0.055);
  --line-hard: rgba(241, 236, 226, 0.18);

  /* Ink */
  --text:   #f1ece2;
  --text-2: #9c968b;
  --text-3: #625d55;

  /* Signal — sky. A pale, desaturated blue: 10.8:1 on the ground, so it reads
     at hairline weight, and far enough from the saturated cobalt of a default
     dark template to avoid that association.
     It is LIGHT, so anything sitting on top of it must be dark ink, not white
     (white on this is 1.9:1). See the ink choices below. */
  --accent:      #7fc4fd;
  --accent-2:    #b9dffe;
  --accent-deep: #466c8b;
  --on-accent:   #06121c;   /* 12.6:1 on --accent */
  --accent-glow: color-mix(in srgb, var(--accent) 45%, transparent);

  /* Gulsabi — used only inside the Gulsabi chapter */
  --g-cream: #fff3de;
  --g-gold:  #ffb23e;
  --g-coral: #ff6b6b;
  --g-teal:  #4ecdc4;
  --g-ink:   #241a12;

  /* Type families */
  --font-display: "Clash Display", "Satoshi", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
  --font-text:    "Satoshi", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;

  /* Type scale */
  --fs-hero:  clamp(1.6rem, 7.6vw, 6.7rem);
  --fs-h2:    clamp(2.05rem, 5.6vw, 4.4rem);
  --fs-h3:    clamp(1.55rem, 3.2vw, 2.5rem);
  --fs-h4:    clamp(1.2rem, 2.2vw, 1.7rem);
  --fs-lede:  clamp(1.05rem, 0.97rem + 0.42vw, 1.3rem);
  --fs-body:  clamp(0.98rem, 0.94rem + 0.2vw, 1.06rem);
  --fs-small: 0.88rem;
  --fs-label: 0.7rem;

  /* Rhythm — deliberately generous. The whitespace is the argument. */
  --maxw:   1280px;
  --gutter: clamp(1.4rem, 6vw, 7rem);
  --sec-pad: clamp(8rem, 20vh, 18rem);

  /* Motion */
  --e-out:   cubic-bezier(0.16, 1, 0.30, 1);
  --e-inout: cubic-bezier(0.65, 0, 0.35, 1);
  --e-soft:  cubic-bezier(0.33, 1, 0.68, 1);
  --dur:     0.9s;

  /* Set by JS — global scroll progress 0..1 */
  --scroll: 0;
}

/* =============================================================================
   2. Reset and base
   ============================================================================= */
*, *::before, *::after { margin: 0; padding: 0; box-sizing: border-box; }

html {
  scroll-behavior: smooth;
  -webkit-text-size-adjust: 100%;
  scrollbar-color: #2a2a2c var(--bg);
}
@media (prefers-reduced-motion: reduce) { html { scroll-behavior: auto; } }

body {
  font-family: var(--font-text);
  font-size: var(--fs-body);
  line-height: 1.6;
  font-weight: 400;
  background: var(--bg);
  color: var(--text);
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  overflow-x: hidden;
  text-rendering: optimizeLegibility;
}
body.menu-open { overflow: hidden; }

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

::selection { background: var(--accent); color: var(--on-accent); }

:focus-visible {
  outline: 2px solid var(--accent-2);
  outline-offset: 4px;
  border-radius: 3px;
}

/* =============================================================================
   3. Atmosphere — grain and bloom, fixed behind everything
   ============================================================================= */
.atmosphere {
  position: fixed; inset: 0; z-index: 0; pointer-events: none;
  contain: strict;
}
.atmosphere .bloom {
  position: absolute; inset: -20%;
  background:
    radial-gradient(48vw 48vw at 78% 12%, color-mix(in srgb, var(--accent) 11%, transparent), transparent 62%),
    radial-gradient(42vw 42vw at 8% 78%, color-mix(in srgb, var(--accent-deep) 14%, transparent), transparent 65%);
  will-change: transform;
  transform: translate3d(0, calc(var(--scroll) * -6vh), 0);
}
.atmosphere .grain {
  position: absolute; inset: 0;
  opacity: 0.032;
  mix-blend-mode: overlay;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='180' height='180'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.85' numOctaves='3' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E");
}
@media (prefers-reduced-motion: reduce) {
  .atmosphere .bloom { transform: none; }
}

/* =============================================================================
   4. Layout helpers
   ============================================================================= */
.wrap {
  width: 100%; max-width: var(--maxw);
  margin-inline: auto; padding-inline: var(--gutter);
}
.section { position: relative; z-index: 1; padding-block: var(--sec-pad); }
.section--flush { padding-block: 0; }

/* Hairline that draws itself in on entry */
.rule {
  position: relative; height: 1px; background: var(--line-soft);
  overflow: hidden;
}
.rule::after {
  content: ""; position: absolute; inset: 0;
  background: linear-gradient(90deg, var(--accent), transparent);
  transform: scaleX(0); transform-origin: left;
  transition: transform 1.4s var(--e-out);
}
.rule.in::after { transform: scaleX(1); }

/* =============================================================================
   5. Typography primitives
   ============================================================================= */
.display {
  font-family: var(--font-display);
  font-weight: 600;
  letter-spacing: -0.035em;
  line-height: 0.92;
  text-transform: uppercase;
}

/* Display headings are set uppercase with negative tracking, which closes the
   word space to nothing. Give it back everywhere the display face is used. */
h1, h2, h3, h4, h5 {
  font-family: var(--font-display);
  font-weight: 600;
  word-spacing: 0.11em;
}

.label {
  font-family: var(--font-text);
  font-size: var(--fs-label);
  font-weight: 500;
  letter-spacing: 0.26em;
  text-transform: uppercase;
  color: var(--text-2);
  display: inline-flex; align-items: center; gap: 0.75rem;
  line-height: 1;
}
.label::before {
  content: ""; width: 1.75rem; height: 1px; background: var(--accent);
  flex: none;
}
.label--bare::before { display: none; }

.lede {
  font-size: var(--fs-lede);
  color: var(--text-2);
  line-height: 1.55;
  max-width: 46ch;
}

.section-head { display: grid; gap: 1.8rem; margin-bottom: clamp(4rem, 11vh, 9rem); }
/* Breaks are authored with <br>; the cap only stops runaway width on ultrawide. */
.section-head h2 {
  font-size: var(--fs-h2);
  letter-spacing: -0.04em;
  line-height: 0.95;
  text-transform: uppercase;
  max-width: 26ch;
}
.section-head .lede { max-width: 52ch; }

/* =============================================================================
   6. Motion primitives
   ============================================================================= */

/* Fade-and-rise, used sparingly */
.reveal {
  opacity: 0;
  transform: translate3d(0, 26px, 0);
  transition: opacity 1.1s var(--e-out), transform 1.1s var(--e-out);
}
.reveal.in { opacity: 1; transform: none; }

/* Line mask — a block slides up out of an overflow-hidden frame */
.mask { display: block; overflow: hidden; }
.mask > * {
  display: block;
  transform: translate3d(0, 110%, 0);
  transition: transform 1.15s var(--e-out);
}
.mask.in > * { transform: none; }

/* Stagger */
[data-delay="1"] { transition-delay: 0.07s; }
[data-delay="2"] { transition-delay: 0.14s; }
[data-delay="3"] { transition-delay: 0.21s; }
[data-delay="4"] { transition-delay: 0.28s; }
[data-delay="5"] { transition-delay: 0.35s; }
[data-delay="6"] { transition-delay: 0.42s; }

/* Media wipe — a curtain retracts downward off the frame.
   Note: this deliberately does NOT clip the frame itself. Hiding the element
   with clip-path makes its visible area zero, which makes IntersectionObserver
   report ratio 0 forever — so the observer could never add the class that
   removes the clip. The curtain keeps the frame's box intact and observable. */
.media {
  position: relative; overflow: hidden;
  background: var(--bg-2);
}
.media::before {
  content: ""; position: absolute; inset: 0; z-index: 3;
  background: var(--bg);
  transform-origin: 50% 100%;
  transform: scaleY(1);
  transition: transform 1.25s var(--e-out);
}
.media.in::before { transform: scaleY(0); }

/* Parallax drift, driven by --mp (media progress, -1..1). Never transitioned. */
.media img {
  width: 100%; height: 100%; object-fit: cover;
  transform: translate3d(0, calc(var(--mp, 0) * 3.2%), 0) scale(1.12);
  will-change: transform;
}

/* Media without parallax releases from a scale-up instead. */
.media:not([data-parallax]) img {
  transform: scale(1.16);
  transition: transform 1.6s var(--e-out);
}
.media:not([data-parallax]).in img { transform: scale(1); }

@media (prefers-reduced-motion: reduce) {
  .reveal, .mask > *, .media, .media img {
    opacity: 1 !important;
    transform: none !important;
    clip-path: none !important;
    transition: none !important;
  }
  .media::before { display: none; }
  .rule::after { transform: scaleX(1) !important; transition: none !important; }
}

/* =============================================================================
   7. Preloader
   ============================================================================= */
#preloader {
  position: fixed; inset: 0; z-index: 300;
  background: var(--bg);
  display: grid; place-items: center;
  transition: opacity 0.7s var(--e-out), visibility 0.7s var(--e-out);
}
#preloader.done { opacity: 0; visibility: hidden; pointer-events: none; }
.pre-mark { display: flex; align-items: center; gap: 0.6rem; }
.pre-pixel {
  width: 11px; height: 11px; background: var(--accent);
  box-shadow: 0 0 30px var(--accent-glow);
  animation: prePulse 0.9s var(--e-out) both;
}
.pre-word {
  overflow: hidden; white-space: nowrap;
  font-family: var(--font-display); font-weight: 600;
  text-transform: uppercase; letter-spacing: -0.02em;
  font-size: 1.5rem; line-height: 1;
  width: 0; animation: preWord 0.8s var(--e-out) 0.18s forwards;
}
.pre-bar {
  position: absolute; left: 0; right: 0; bottom: 0; height: 1px;
  background: var(--accent); transform-origin: left;
  transform: scaleX(0); animation: preBar 1.1s var(--e-inout) forwards;
}
@keyframes prePulse { from { opacity: 0; transform: scale(0.2); } to { opacity: 1; transform: scale(1); } }
@keyframes preWord  { to { width: 6.6ch; } }
@keyframes preBar   { to { transform: scaleX(1); } }
@media (prefers-reduced-motion: reduce) {
  .pre-pixel, .pre-word, .pre-bar { animation: none; }
  .pre-word { width: auto; }
  .pre-bar { display: none; }
}

/* =============================================================================
   8. The Pixnut Signal — one continuous rail down the page
   ============================================================================= */
#signal-rail {
  position: fixed; top: 0; z-index: 4;
  left: max(1.4rem, calc((100vw - var(--maxw)) / 2 - 1.75rem));
  width: 2px; height: 100vh; pointer-events: none;
}
#signal-rail .track { position: absolute; inset: 0; width: 1px; background: var(--line-soft); }
#signal-rail .fill {
  position: absolute; top: 0; left: 0; width: 1px; height: 100%;
  background: linear-gradient(180deg, var(--accent), var(--accent-2));
  transform-origin: top;
  transform: scaleY(var(--scroll));
}
#signal-rail .dot {
  position: absolute; left: -3.5px; top: 0;
  width: 8px; height: 8px; border-radius: 50%;
  background: var(--accent); box-shadow: 0 0 14px var(--accent-glow);
  transform: translate3d(0, calc(var(--scroll) * (100vh - 8px)), 0);
}
@media (max-width: 1000px) { #signal-rail { display: none; } }
@media (prefers-reduced-motion: reduce) { #signal-rail .dot { box-shadow: none; } }

/* =============================================================================
   9. Navigation
   ============================================================================= */
header.nav {
  position: fixed; top: 0; left: 0; right: 0; z-index: 100;
  transition: background 0.5s var(--e-out), backdrop-filter 0.5s var(--e-out),
              border-color 0.5s var(--e-out), transform 0.6s var(--e-out);
  border-bottom: 1px solid transparent;
}
header.nav.scrolled {
  background: rgba(7, 7, 7, 0.66);
  backdrop-filter: blur(18px) saturate(1.3);
  -webkit-backdrop-filter: blur(18px) saturate(1.3);
  border-bottom-color: var(--line-soft);
}
header.nav.hidden { transform: translate3d(0, -100%, 0); }

.nav-inner { display: flex; align-items: center; justify-content: space-between; height: 80px; }
.nav-logo { display: inline-flex; align-items: center; }
.nav-logo img { height: 26px; width: auto; }

.nav-links { display: flex; align-items: center; gap: 2.4rem; }
.nav-links a {
  font-size: var(--fs-small); font-weight: 500; color: var(--text-2);
  position: relative; transition: color 0.3s var(--e-out);
}
.nav-links a:not(.nav-cta)::after {
  content: ""; position: absolute; left: 0; right: 0; bottom: -5px; height: 1px;
  background: var(--accent);
  transform: scaleX(0); transform-origin: right;
  transition: transform 0.45s var(--e-out);
}
.nav-links a:not(.nav-cta):hover::after,
.nav-links a:not(.nav-cta):focus-visible::after { transform: scaleX(1); transform-origin: left; }
.nav-links a:hover, .nav-links a:focus-visible { color: var(--text); }

.nav-cta {
  border: 1px solid var(--line); border-radius: 999px;
  padding: 0.55rem 1.2rem; color: var(--text) !important;
  transition: border-color 0.35s var(--e-out), background 0.35s var(--e-out);
}
.nav-cta:hover { border-color: var(--accent); background: color-mix(in srgb, var(--accent) 10%, transparent); }

.nav-toggle {
  display: none; background: none; border: 0; cursor: pointer;
  width: 44px; height: 44px; position: relative; margin-right: -10px;
}
.nav-toggle span {
  position: absolute; left: 11px; right: 11px; height: 1.5px; background: currentColor;
  transition: transform 0.45s var(--e-out), opacity 0.25s ease;
}
.nav-toggle span:nth-child(1) { top: 18px; }
.nav-toggle span:nth-child(2) { top: 26px; }
body.menu-open .nav-toggle span:nth-child(1) { transform: translateY(4px) rotate(45deg); }
body.menu-open .nav-toggle span:nth-child(2) { transform: translateY(-4px) rotate(-45deg); }

/* Mobile menu */
#mobile-menu {
  position: fixed; inset: 0; z-index: 90;
  background: var(--bg-2);
  display: flex; flex-direction: column; justify-content: center;
  padding: var(--gutter);
  clip-path: inset(0 0 100% 0);
  visibility: hidden;                      /* keeps links out of the tab order */
  transition: clip-path 0.7s var(--e-out), visibility 0s linear 0.7s;
}
body.menu-open #mobile-menu {
  clip-path: inset(0 0 0 0);
  visibility: visible;
  transition: clip-path 0.7s var(--e-out), visibility 0s;
}
#mobile-menu a {
  font-family: var(--font-display); font-weight: 600; text-transform: uppercase;
  font-size: clamp(2rem, 10vw, 3.2rem); letter-spacing: -0.035em; line-height: 1.1;
  padding-block: 0.35rem; color: var(--text-3);
  transition: color 0.3s var(--e-out), transform 0.5s var(--e-out);
}
#mobile-menu a:hover, #mobile-menu a:focus-visible { color: var(--text); }
#mobile-menu .mm-foot {
  margin-top: 2.5rem; padding-top: 1.6rem; border-top: 1px solid var(--line-soft);
  color: var(--text-3); font-size: var(--fs-small);
  display: flex; justify-content: space-between; gap: 1rem; flex-wrap: wrap;
}

@media (max-width: 900px) {
  .nav-links { display: none; }
  .nav-toggle { display: block; }
  .nav-inner { height: 68px; }
}

/* =============================================================================
   10. Buttons
   ============================================================================= */
.btn {
  --btn-ink: var(--text);
  position: relative; overflow: hidden;
  display: inline-flex; align-items: center; gap: 0.65rem;
  padding: 1rem 1.7rem; border-radius: 999px;
  font-size: var(--fs-small); font-weight: 500; letter-spacing: 0.01em;
  cursor: pointer; border: 1px solid transparent;
  color: var(--btn-ink);
  transition: color 0.4s var(--e-out), border-color 0.4s var(--e-out), transform 0.4s var(--e-out);
  white-space: nowrap;
  isolation: isolate;
}
.btn > * { position: relative; z-index: 1; }
.btn .arw { transition: transform 0.45s var(--e-out); display: inline-block; }
.btn:hover .arw { transform: translateX(5px); }
.btn:hover { transform: translateY(-2px); }

/* Ink-fill sweep on hover */
.btn::before {
  content: ""; position: absolute; inset: 0; z-index: 0;
  background: var(--btn-fill, var(--accent));
  transform: translate3d(0, 101%, 0);
  transition: transform 0.55s var(--e-out);
}
.btn:hover::before, .btn:focus-visible::before { transform: none; }

.btn-primary { background: var(--text); --btn-ink: #0a0a0a; --btn-fill: var(--accent); }
.btn-primary:hover { --btn-ink: var(--on-accent); }
.btn-ghost { border-color: var(--line); --btn-fill: color-mix(in srgb, var(--accent) 14%, transparent); }
.btn-ghost:hover { border-color: var(--accent); }
.btn-gulsabi { background: var(--g-gold); --btn-ink: var(--g-ink); --btn-fill: var(--g-coral); }
.btn-gulsabi:hover { --btn-ink: #fff; }
.btn-outline-warm { border-color: rgba(255, 243, 222, 0.28); --btn-ink: var(--g-cream); --btn-fill: rgba(255, 243, 222, 0.12); }
.btn-outline-warm:hover { border-color: var(--g-teal); --btn-ink: #fff; }

@media (prefers-reduced-motion: reduce) {
  .btn, .btn::before, .btn .arw { transition: none !important; }
  .btn:hover { transform: none; }
}

/* =============================================================================
   11. Hero
   ============================================================================= */
.hero { min-height: 100svh; display: flex; position: relative; overflow: hidden; }
.hero-inner {
  display: flex; flex-direction: column;
  width: 100%; padding-block: 7.5rem 2.6rem;
  position: relative; z-index: 1;
}

/* Hero depth layer — the deep-space plate, full-bleed and held far back */
.hero-plate {
  position: absolute; inset: 0; z-index: 0; pointer-events: none;
  opacity: 0.42;
  -webkit-mask-image: radial-gradient(70% 65% at 72% 34%, #000 0%, transparent 72%);
  mask-image: radial-gradient(70% 65% at 72% 34%, #000 0%, transparent 72%);
}
.hero-plate img {
  width: 100%; height: 100%; object-fit: cover;
  transform: scale(1.08) translate3d(0, calc(var(--hp, 0) * 5%), 0);
  will-change: transform;
}

/* auto block margins centre the headline block, leaving the foot pinned low.
   --hp (0..1 as the hero scrolls out) recedes the whole block so the hero hands
   off to the manifesto as one continuous move rather than a cut. */
.hero-grid {
  position: relative; z-index: 1; display: grid; gap: 2.2rem; width: 100%;
  margin-block: auto;
  transform: translate3d(0, calc(var(--hp, 0) * -6vh), 0) scale(calc(1 - var(--hp, 0) * 0.055));
  transform-origin: 0% 50%;
  opacity: calc(1 - var(--hp, 0) * 0.85);
  will-change: transform, opacity;
}
@media (prefers-reduced-motion: reduce) {
  .hero-grid { transform: none !important; opacity: 1 !important; }
}

.hero h1 {
  font-family: var(--font-display);
  font-size: var(--fs-hero);
  font-weight: 600;
  line-height: 0.86;
  letter-spacing: -0.045em;
  /* Tight tracking closes the word space in uppercase; give it back. */
  word-spacing: 0.14em;
  text-transform: uppercase;
  margin-top: 1.6rem;
}
.hero h1 .line { display: block; overflow: hidden; padding-block: 0.06em; }
.hero h1 .line > span {
  display: block;
  transform: translate3d(0, 110%, 0);
  transition: transform 1.25s var(--e-out);
}
.hero h1 .line:nth-child(2) > span { transition-delay: 0.12s; }
.hero.in h1 .line > span { transform: none; }

/* "Worlds" is filled with the actual Gulsabi world — the hero states the promise
   in the material the studio actually made. */
.hero h1 .worlds {
  background-image: url("/media/gulsabi/world-cool.jpg");
  background-size: 118% auto;
  background-position: 26% calc(46% + var(--hp, 0) * 16%);
  -webkit-background-clip: text; background-clip: text;
  color: transparent;
  -webkit-text-stroke: 0.5px rgba(255, 243, 222, 0.22);
  /* The plate is pastel; push it until it reads as an image, not a tint. */
  filter: saturate(1.38) contrast(1.1);
}
/* The full stop is the Pixnut pixel, the same mark the preloader draws.
   It carries no negative margin: pulling it left tucked it under the previous
   letter, and because only the second line wrapped its last word in .worlds
   (which had its own right padding), the two lines overlapped by different
   amounts, 13.9px against 7.5px. Both now sit on the typeface's own sidebearing
   and match exactly. */
.hero h1 .dot-accent { color: var(--accent); -webkit-text-stroke: 0; }

.hero-sub {
  max-width: 44ch; color: var(--text-2);
  font-size: var(--fs-lede); line-height: 1.5;
  margin-top: 1rem;
}
.hero-actions { display: flex; flex-wrap: wrap; gap: 0.9rem; margin-top: 1.4rem; }

/* Hero footer strip: meta on the right, scroll cue on the left */
.hero-foot {
  position: relative; z-index: 1;
  margin-top: auto;
  border-top: 1px solid var(--line-soft);
  padding-top: 1.3rem;
  display: flex; align-items: flex-start; justify-content: space-between;
  gap: 1.5rem 2.5rem; flex-wrap: wrap;
}
.scroll-hint {
  font-size: var(--fs-label); letter-spacing: 0.26em; text-transform: uppercase;
  color: var(--text-3); display: inline-flex; align-items: center; gap: 0.7rem;
}
.scroll-hint::after {
  content: ""; width: 1px; height: 2.6rem;
  background: linear-gradient(var(--text-3), transparent);
  animation: scrollLine 2.4s var(--e-inout) infinite;
}
@keyframes scrollLine {
  0%, 100% { transform: scaleY(0.35); transform-origin: top; opacity: 0.35; }
  50%      { transform: scaleY(1);    transform-origin: top; opacity: 1; }
}

@media (prefers-reduced-motion: reduce) {
  .hero h1 .line > span { transform: none !important; }
  .hero-plate img { transform: scale(1.04) !important; }
  .scroll-hint::after { animation: none; }
}

@media (max-width: 720px) {
  .hero-inner { padding-block: 6rem 1.6rem; }
  .hero-grid { gap: 1.5rem; }
  .hero h1 { margin-top: 0.9rem; line-height: 0.9; }
  .hero-actions { margin-top: 0.6rem; gap: 0.7rem; }
  .hero-foot { flex-direction: column; align-items: stretch; gap: 1.2rem; }
  .scroll-hint { display: none; }
}

/* On a phone the headline is allowed to wrap; a bigger, denser block reads far
   better than one long line shrunk to fit. */
@media (max-width: 600px) {
  :root { --fs-hero: 13vw; }
}

/* =============================================================================
   12. Manifesto — each line lights through a scroll-driven wipe
   ============================================================================= */
.manifesto { padding-block: clamp(9rem, 24vh, 22rem); }
.manifesto-lines { display: grid; gap: clamp(1rem, 3.5vh, 2.4rem); margin-top: clamp(4rem, 11vh, 8rem); }
/* The turn in the argument: extra air before the four-line build. */
.manifesto p.spacer-line { margin-top: clamp(1.5rem, 5vh, 4rem); }
.manifesto p.final { margin-top: clamp(1.5rem, 5vh, 4rem); }

.manifesto p {
  font-family: var(--font-display);
  font-size: clamp(1.6rem, 4.6vw, 3.3rem);
  font-weight: 500;
  line-height: 1.12;
  letter-spacing: -0.035em;
  word-spacing: 0.11em;
  text-transform: uppercase;
  max-width: 21ch;

  /* The wipe: --lit runs 0 → 1 as the line crosses the reading band.
     The stops are feathered so the leading edge reads as light passing over
     the words rather than a hard vertical cut through them. */
  background-image: linear-gradient(
    100deg,
    var(--text)   0%,  var(--text)   42%,
    var(--text-3) 58%, var(--text-3) 100%
  );
  background-size: 220% 100%;
  background-position: calc(100% - var(--lit, 0) * 100%) 0;
  -webkit-background-clip: text; background-clip: text;
  color: transparent;
}
.manifesto p.final {
  background-image: linear-gradient(
    100deg,
    var(--accent-2) 0%,  var(--accent-2) 42%,
    var(--text-3)   58%, var(--text-3)   100%
  );
  font-weight: 600;
}

@media (prefers-reduced-motion: reduce) {
  .manifesto p { background-position: 0 0; }
}

/* =============================================================================
   13. Studio — four chapters, sticky index column, real work alongside
   ============================================================================= */
.chapters { display: grid; }

.chapter {
  display: grid;
  grid-template-columns: minmax(0, 0.8fr) minmax(0, 1.2fr);
  gap: clamp(2.5rem, 6vw, 6rem);
  align-items: start;
  padding-block: clamp(5rem, 13vh, 10rem);
  border-top: 1px solid var(--line-soft);
}

.chapter-text { position: sticky; top: 120px; }

.chapter-index { display: flex; align-items: baseline; gap: 1.1rem; }
.chapter-index .num {
  font-family: var(--font-display); font-weight: 400;
  font-size: clamp(2.6rem, 6vw, 4.4rem); line-height: 1;
  color: transparent;
  -webkit-text-stroke: 1px var(--accent);
  letter-spacing: -0.02em;
  transition: color 0.7s var(--e-out);
}
.chapter.lit .chapter-index .num { color: var(--accent); }
.chapter-index .label {
  font-size: var(--fs-label); letter-spacing: 0.26em; text-transform: uppercase;
  color: var(--text-2); font-weight: 500;
}

.chapter h3 {
  font-size: var(--fs-h3);
  font-weight: 600; letter-spacing: -0.035em; line-height: 1.02;
  text-transform: uppercase;
  margin-top: 1.3rem; max-width: 15ch;
}
.chapter .lede { margin-top: 1.2rem; max-width: 36ch; }

.chapter-media {
  position: relative; aspect-ratio: 16 / 10; border-radius: 4px;
  border: 1px solid var(--line-soft);
}
.chapter-media::after {
  content: ""; position: absolute; inset: 0; pointer-events: none;
  background: linear-gradient(180deg, transparent 45%, rgba(7, 7, 7, 0.5));
}
/* ---- AI Creative: the range wall ------------------------------------------
   The claim in this chapter is range, so the visual holds six directions at
   once instead of one hero still. Same three-layer motion model used elsewhere:
   the card owns the entrance, the image owns scroll parallax and ambient drift.
   --------------------------------------------------------------------------- */
.ai-gal {
  position: relative;
  width: 100%;
  aspect-ratio: 16 / 10;
  --ai-throw: 40px;
  /* No panel behind the wall. The cards sit on the page itself, so the section
     reads as work floating in the page rather than work inside a box. Visible
     overflow lets them spread past the layout box as the section scrolls. */
  overflow: visible;
}
.ai-card {
  position: absolute; display: block;
  border-radius: 9px; overflow: hidden;
  border: 1px solid rgba(241, 236, 226, 0.14);
  background: #0a0a0c;
  /* Carries its own separation now that there is no panel to sit against. */
  box-shadow: 0 22px 44px rgba(0, 0, 0, 0.72), 0 2px 8px rgba(0, 0, 0, 0.5);
}
.ai-card > img {
  width: 100%; height: 100%; object-fit: cover; display: block;
  translate: 0 calc(var(--gy, 0) * var(--d, 1) * -1 * var(--ai-throw));
  will-change: translate;
}

/* Laid out as in the reference: a large card holding the centre, five smaller
   ones ringing it at varied sizes so the wall reads as a spread of work. */
.ai-noir    { left: 0%;  top: 15%;    width: 31%; aspect-ratio: 4/3; z-index: 2; }
.ai-shoe    { left: 31%; top: 0%;     width: 29%; aspect-ratio: 4/3; z-index: 2; }
.ai-orb     { right: 0%; top: 13%;    width: 28%; aspect-ratio: 4/3; z-index: 2; }
.ai-perfume { left: 1%;  bottom: 4%;  width: 31%; aspect-ratio: 4/3; z-index: 2; }
.ai-castle  { right: 0%; bottom: 5%;  width: 27%; aspect-ratio: 4/3; z-index: 2; }
.ai-hero    { left: 30%; top: 36%;    width: 41%; aspect-ratio: 5/4; z-index: 4;
              box-shadow: 0 30px 60px rgba(0, 0, 0, 0.82), 0 2px 10px rgba(0, 0, 0, 0.6); }

/* Entrance: the whole wall bursts out of a single point. Every card starts
   collapsed at the centre of the gallery and flies to its own position, so the
   six pieces arrive as one gesture rather than six fades. --bx/--by is the
   vector from each card's resting centre back to that point; site.js measures
   it from layout, which keeps it honest across every breakpoint. */
.ai-card {
  opacity: 0;
  transform: translate3d(var(--bx, 0px), var(--by, 0px), 0) scale(0.08);
  transition: opacity 0.5s var(--e-out), transform 1.25s var(--e-out);
  transition-delay: calc(var(--i, 0) * 60ms);
}
.ai-gal.in .ai-card { opacity: 1; transform: none; }
.ai-hero { transition-duration: 1.45s; }

/* Then it keeps opening. --sp runs 0 to 1 across the section, pushing every
   card a little further out along the same vector it flew in on. Slow enough
   that you feel the wall breathing rather than watch it move. */
.ai-card {
  translate:
    calc(var(--bx, 0px) * -0.075 * var(--sp, 0))
    calc(var(--by, 0px) * -0.075 * var(--sp, 0));
}

/* Each card drifts on its own period, so the wall is never still and never
   pulses together. */
@media (prefers-reduced-motion: no-preference) {
  .ai-gal.in .ai-card > img { animation: aiDrift 9s ease-in-out infinite alternate; }
  .ai-gal.in .ai-shoe    > img { animation-duration: 7s;  }
  .ai-gal.in .ai-orb     > img { animation-duration: 11s; }
  .ai-gal.in .ai-perfume > img { animation-duration: 8s;  }
  .ai-gal.in .ai-castle  > img { animation-duration: 12s; }
  .ai-gal.in .ai-hero    > img { animation-duration: 10s; }
  .ai-gal.in .ai-card > img { animation-delay: calc(var(--i, 0) * 60ms + 1400ms); }
}
@keyframes aiDrift { from { transform: scale(1.04) translateY(0); }
                     to   { transform: scale(1.04) translateY(-10px); } }

/* Hover lifts a card out of the wall. */
.ai-card { transition-property: opacity, transform, border-color, box-shadow; }
.ai-gal.in .ai-card:hover {
  border-color: color-mix(in srgb, var(--accent) 55%, transparent);
  box-shadow: 0 26px 46px rgba(0, 0, 0, 0.7), 0 0 0 1px color-mix(in srgb, var(--accent) 30%, transparent);
  z-index: 6;
}

@media (max-width: 900px) {
  .ai-gal { aspect-ratio: 4 / 3; --ai-throw: 28px; }
  .ai-noir    { left: 0%;  top: 12%;   width: 34%; }
  .ai-shoe    { left: 32%; top: 0%;    width: 33%; }
  .ai-orb     { right: 0%; top: 11%;   width: 32%; }
  .ai-perfume { left: 1%;  bottom: 4%; width: 34%; }
  .ai-castle  { right: 0%; bottom: 5%; width: 31%; }
  .ai-hero    { left: 29%; top: 34%;   width: 43%; }
}
/* Phone: the wall thins to four cards so each is still readable, and they
   overlap properly at this size instead of sitting in their own corners. */
@media (max-width: 620px) {
  /* Four cards in a square frame, and the depths are far enough apart that a
     large throw slides them over each other. 16px keeps the wall a wall. */
  .ai-gal { aspect-ratio: 1 / 1; --ai-throw: 16px; }
  .ai-shoe, .ai-castle { display: none; }
  /* noir ends at 36% and perfume began at 30%, so the two sat on top of each
     other at rest and ground into each other as the section scrolled. They are
     separated now, and the centre card still overlaps both deliberately. */
  .ai-noir    { left: 0%;  top: 0%;    width: 48%; }
  .ai-orb     { right: 0%; top: 6%;    width: 44%; }
  .ai-perfume { left: 0%;  top: 39%;   bottom: auto; width: 44%; }
  .ai-hero    { left: 32%; top: 46%;   width: 64%; }
}
@media (prefers-reduced-motion: reduce) {
  .ai-card { opacity: 1 !important; transform: none !important; transition: none !important;
             translate: none !important; }
  .ai-card > img { translate: none !important; animation: none !important; }
}

/* ---- Original IP: the cast in depth ----------------------------------------
   Replaces the flat still in chapter 04. Same three-layer motion model as the
   Gulsabi scene: the slot owns entrance, the image owns scroll parallax and
   ambient drift, so none of the three overwrite each other.
   Depth order back to front: planet, the two standing children, the lying boy.
   --------------------------------------------------------------------------- */
.ip-scene {
  position: relative;
  width: 100%;
  aspect-ratio: 4 / 3;
  /* No panel. These are cut-outs, so they belong on the page rather than inside
     a lit box: a border and a fill turn a cast into a product screenshot. What
     is left is one glow behind the planet, sized so it reaches zero before it
     meets any edge. A gradient still visible at the edge draws the rectangle
     back in as a hard cut, which is the box by another name. */
  overflow: visible;
  background:
    radial-gradient(40% 36% at 50% 40%, color-mix(in srgb, var(--accent) 16%, transparent), transparent 66%);
}
/* Sized by HEIGHT, not width: these are tall portrait cut-outs in a short box,
   so a width-based size makes them overflow it several times over. */
.ip-slot { position: absolute; display: block; width: auto; }
.ip-slot > img { height: 100%; width: auto; display: block; transform-origin: 50% 92%; }

/* scroll parallax, written every frame, never transitioned */
.ip-scene { --ip-throw: 46px; }
.ip-slot > img {
  translate: 0 calc(var(--gy, 0) * var(--d, 1) * -1 * var(--ip-throw));
  will-change: translate;
}

/* placement: planet behind and high, the pair together, the lying boy nearest */
.ip-planet { left: 15%; top: 2%;    height: 96%; z-index: 1; }
.ip-girl   { left: 28%; bottom: 4%;  height: 76%; z-index: 2; }
.ip-boy    { left: 45%; bottom: 1%;  height: 84%; z-index: 3; }
.ip-lying  { left: 8%;  bottom: 2%;  height: 42%; z-index: 4;
             filter: drop-shadow(0 14px 18px rgba(0, 6, 14, 0.6)); }
.ip-girl > img, .ip-boy > img { filter: drop-shadow(0 16px 20px rgba(0, 6, 14, 0.55)); }

/* entrance: the world arrives first, the pair rise, the lying boy slides in last */
.ip-slot {
  opacity: 0;
  transition: opacity 0.8s var(--e-out), transform 1.1s var(--e-out);
  transition-delay: calc(var(--i, 0) * 110ms);
}
.ip-scene.in .ip-slot { opacity: 1; transform: none; }
.ip-planet { transform: translate3d(0, -40px, 0) scale(0.72); transform-origin: 50% 40%; }
.ip-girl   { transform: translate3d(0, 70px, 0) scale(0.9); transform-origin: 50% 100%; }
.ip-boy    { transform: translate3d(0, 70px, 0) scale(0.9); transform-origin: 50% 100%; }
.ip-lying  { transform: translate3d(-70px, 26px, 0) scale(0.92); transform-origin: 0% 100%; }

/* ambient: the planet turns slowly, the children breathe on their own timings */
@media (prefers-reduced-motion: no-preference) {
  .ip-scene.in .ip-planet > img { animation: ipOrbit 22s ease-in-out infinite alternate; }
  .ip-scene.in .ip-girl   > img { animation: ipBreathe 5.5s ease-in-out infinite alternate; }
  .ip-scene.in .ip-boy    > img { animation: ipBreathe 4.4s ease-in-out infinite alternate -1.4s; }
  .ip-scene.in .ip-lying  > img { animation: ipBreathe 6.5s ease-in-out infinite alternate -2.6s; }
  .ip-scene.in .ip-slot > img { animation-delay: calc(var(--i, 0) * 110ms + 1200ms); }
}
@keyframes ipOrbit   { from { transform: translateY(0) rotate(-2.5deg); }
                       to   { transform: translateY(-18px) rotate(2.5deg); } }
@keyframes ipBreathe { from { transform: translateY(0) scale(1); }
                       to   { transform: translateY(-9px) scale(1.012); } }

@media (max-width: 900px) {
  .ip-scene { aspect-ratio: 1 / 1; --ip-throw: 30px; }
  .ip-planet { left: 10%; top: 4%;   height: 88%; }
  .ip-girl   { left: 26%; bottom: 5%; height: 70%; }
  .ip-boy    { left: 45%; bottom: 2%; height: 78%; }
  .ip-lying  { left: 6%;  bottom: 0%; height: 38%; }
}
@media (max-width: 620px) {
  .ip-scene { aspect-ratio: 5 / 6; --ip-throw: 20px; }
  .ip-planet { left: 6%;  top: 6%;   height: 76%; }
  .ip-girl   { left: 24%; bottom: 6%; height: 64%; }
  .ip-boy    { left: 46%; bottom: 3%; height: 71%; }
  .ip-lying  { left: 3%;  bottom: 0%; height: 33%; }
}
@media (prefers-reduced-motion: reduce) {
  .ip-slot { opacity: 1 !important; transform: none !important; transition: none !important; }
  .ip-slot > img { translate: none !important; animation: none !important; }
}

.chapter-caption {
  margin-top: 0.9rem; display: flex; justify-content: space-between;
  gap: 1rem; flex-wrap: wrap;
  font-size: var(--fs-label); letter-spacing: 0.18em; text-transform: uppercase;
  color: var(--text-3);
}

/* A ruled two-column index, not pills. Bordered capability chips read as SaaS
   chrome; a plain ruled list is quieter and says more about the practice. */
.chapter ul {
  list-style: none;
  display: grid; grid-template-columns: repeat(2, minmax(0, 1fr));
  column-gap: clamp(1rem, 2.5vw, 2.5rem);
  margin-top: 2rem;
  border-top: 1px solid var(--line-soft);
}
.chapter li {
  font-size: 0.86rem; color: var(--text-2); font-weight: 400;
  padding: 0.6rem 0;
  border-bottom: 1px solid var(--line-soft);
  transition: color 0.35s var(--e-out);
}
.chapter li:hover { color: var(--text); }
@media (max-width: 480px) { .chapter ul { grid-template-columns: 1fr; } }

.chapter .note {
  font-size: var(--fs-small); color: var(--text-3);
  margin-top: 1.5rem; max-width: 44ch;
  padding-left: 1rem; border-left: 1px solid var(--line-soft);
}

@media (max-width: 900px) {
  .chapter { grid-template-columns: 1fr; gap: 1.8rem; }
  .chapter-text { position: static; }
  .chapter-media { aspect-ratio: 4 / 3; }
}

/* =============================================================================
   14. Workflow — one line, six stages
   ============================================================================= */
.flow-track { position: relative; padding-left: clamp(2rem, 5vw, 3.5rem); }
.flow-track::before {
  content: ""; position: absolute; left: 0; top: 0.5rem; bottom: 0.5rem;
  width: 1px; background: var(--line-soft);
}
.flow-fill {
  position: absolute; left: 0; top: 0.5rem; width: 1px;
  height: calc(100% - 1rem);
  background: linear-gradient(180deg, var(--accent), var(--accent-2));
  transform-origin: top; transform: scaleY(var(--fp, 0));
}

.stage {
  position: relative;
  padding-block: clamp(1.5rem, 4vh, 2.8rem);
  display: grid; grid-template-columns: auto 1fr;
  gap: clamp(1.4rem, 4vw, 3.2rem); align-items: baseline;
}
.stage::before {
  content: ""; position: absolute;
  left: calc(-1 * clamp(2rem, 5vw, 3.5rem) - 4.5px);
  top: calc(clamp(1.5rem, 4vh, 2.8rem) + 0.6rem);
  width: 10px; height: 10px; border-radius: 50%;
  background: var(--bg); border: 1px solid var(--text-3);
  transition: background 0.5s var(--e-out), border-color 0.5s var(--e-out), box-shadow 0.5s var(--e-out);
}
.stage.active::before {
  background: var(--accent); border-color: var(--accent);
  box-shadow: 0 0 0 4px color-mix(in srgb, var(--accent) 14%, transparent), 0 0 16px var(--accent-glow);
}
.stage .st-num {
  font-family: var(--font-display); font-weight: 400; font-size: 1.4rem;
  color: var(--text-3); transition: color 0.5s var(--e-out); line-height: 1;
}
.stage.active .st-num { color: var(--accent-2); }
.stage h4 {
  font-size: var(--fs-h4); font-weight: 600;
  letter-spacing: -0.03em; text-transform: uppercase; line-height: 1.05;
  color: var(--text-3); transition: color 0.5s var(--e-out);
}
.stage.active h4 { color: var(--text); }
.stage p { color: var(--text-2); margin-top: 0.55rem; max-width: 46ch; font-size: var(--fs-small); }

/* =============================================================================
   15. Gulsabi — the colour high point, built on the real world plate
   ============================================================================= */
#gulsabi {
  --gt: 0;                       /* section presence, 0..1 */
  --gz: 0;                       /* camera push through the pin, 0..1 */
  /* No overflow:hidden here. It would make this element a scroll container and
     silently break position:sticky for the pinned stage inside it. The radial
     backgrounds are backgrounds; they do not overflow. */
  position: relative;
  padding-block: clamp(6rem, 14vh, 11rem) 0;
  background:
    radial-gradient(90vw 55vh at 80% -8%,  rgba(255, 178, 62, calc(0.11 * var(--gt))), transparent 62%),
    radial-gradient(70vw 45vh at 2% 104%,  rgba(78, 205, 196, calc(0.10 * var(--gt))), transparent 58%),
    linear-gradient(180deg, var(--bg) 0%, color-mix(in srgb, var(--g-ink) calc(40% * var(--gt)), var(--bg)) 62%, var(--bg) 100%);
}

.gulsabi-tag {
  display: inline-flex; align-items: center; gap: 0.6rem;
  font-size: var(--fs-label); letter-spacing: 0.26em; text-transform: uppercase; font-weight: 600;
  color: var(--g-gold);
  border: 1px solid color-mix(in srgb, var(--g-gold) 38%, transparent);
  border-radius: 999px; padding: 0.45rem 1rem;
}
#gulsabi h2 {
  font-family: var(--font-display);
  font-size: clamp(3.2rem, 15vw, 11rem);
  font-weight: 600; letter-spacing: -0.05em; line-height: 0.86;
  text-transform: uppercase; margin-top: 1.5rem;
}
#gulsabi h2 .g-color {
  background: linear-gradient(100deg, var(--g-gold), var(--g-coral) 52%, var(--g-teal));
  -webkit-background-clip: text; background-clip: text; color: transparent;
}
.gulsabi-lede {
  max-width: 44ch; font-size: var(--fs-lede); margin-top: 1.6rem;
  color: color-mix(in srgb, var(--g-cream) 74%, var(--text-2));
}

/* The world stage: real Gulsabi environment, character on the measured anchor */
/* Pin: one viewport of hold, plus the travel that drives the push. */
/* ---- Gulsabi's world -------------------------------------------------------
   Three motions run on every element at once, so each gets its own layer and
   its own property and none can overwrite another:

     .gs-slot  transform + opacity   entrance, transitioned once on reveal
     img       translate             scroll parallax, written every frame
     img       transform (keyframes) ambient drift, looping

   --gy is the scene's scroll progress (-1..1) and --d each element's depth;
   their product is how far it travels, which is what makes this parallax
   rather than a picture sliding as one piece.
   --------------------------------------------------------------------------- */
.gs {
  position: relative;
  width: 100%;
  aspect-ratio: 16 / 10;
  margin-top: clamp(2rem, 6vh, 4rem);
  /* The ground row is deliberately bled past both edges; clip it here so it can
     never widen the document. */
  overflow: hidden;
  /* Both glows reach zero before every edge. At their old size they were still
     lit where the clip fell, which drew a hard rectangle around the world and
     made a place look like a panel. */
  background:
    radial-gradient(42% 38% at 50% 74%, color-mix(in srgb, var(--g-teal) 14%, transparent), transparent 68%),
    radial-gradient(34% 30% at 30% 30%, color-mix(in srgb, var(--accent) 12%, transparent), transparent 68%);
}
.gs-sky, .gs-mid, .gs-ground { position: absolute; inset: 0; }
.gs-ground { z-index: 4; }
.gs-char   { z-index: 3; }

/* --- the slot: position and entrance --- */
.gs-slot { position: absolute; display: block; }
.gs-slot > img { width: 100%; height: auto; display: block; transform-origin: 50% 92%; }

/* --- scroll parallax, on the image, never transitioned ---
   Sky depths are negative, so the sky drifts against the ground: that
   counter-motion is what the eye reads as depth, and it is why the throw can be
   this large without anything looking like it is sliding. */
.gs { --gs-throw: 78px; --gs-floor: 22px; }
.gs-slot > img {
  translate: 0 calc(var(--gy, 0) * var(--d, 1) * -1 * var(--gs-throw));
  will-change: translate;
}
/* The ground is the floor: it may dip toward the viewer but must never lift,
   or a gap opens beneath it. Biasing --gy by 1 keeps its travel one-directional. */
.gs-ground .gs-slot > img {
  translate: 0 calc((var(--gy, 0) - 1) * var(--d, 1) * -1 * var(--gs-floor));
}

/* --- placement --- */
.gs-stars  { left: 2%;   top: 19%; width: 30%; opacity: 0.9; }
.gs-moon   { left: 24%;  top: 11%; width: 12%; }
.gs-clouds { left: 43%;  top: 9%;  width: 32%; }
.gs-island { right: 2%;  top: 12%; width: 21%; }

.gs-tree-lg { left: 2%;   bottom: 12%; width: 19%; }
.gs-tree-sm { right: 2%;  bottom: 15%; width: 14%; }
.gs-bush    { left: 27%;  bottom: 13%; width: 15%; }
.gs-bushf   { right: 22%; bottom: 11%; width: 17%; }

.gs-char { left: 56%; bottom: 2%;  width: 21%; }
.gs-char > img { filter: drop-shadow(0 20px 26px rgba(0, 8, 16, 0.55)); }

/* All fully inside the frame: no negative insets, nothing clipped at an edge.
   g3 is the cluster that sits in front of the character. */
/* The floor is three pieces and they have to overlap, not merely meet. At 36%
   wide starting at 0 and 40% the first two only touched on paper: each PNG
   carries transparent margin, so the visible grass left a black notch in the
   middle of the ground row. Every piece now runs past its neighbour. */
.gs-g2 { left: 0%;   bottom: 1%;  width: 46%; }
.gs-g3 { left: 38%;  bottom: 0%;  width: 40%; }
.gs-g4 { right: 0%;  bottom: 3%;  width: 36%; }

/* =============================================================================
   Entrance. The world assembles itself: ground rises first, plants grow out of
   it, the sky settles in, and Gulsabi arrives last as the payoff. Each element
   enters from the direction it belongs to, which is why this reads as a place
   coming together rather than a row of images fading up.
   ============================================================================= */
.gs-slot {
  opacity: 0;
  transition:
    opacity 0.8s var(--e-out),
    transform 1.15s var(--e-out);
  transition-delay: calc(var(--i, 0) * 95ms);
}
.gs.in .gs-slot { opacity: 1; transform: none; }
.gs-stars.gs-slot { opacity: 0; }
.gs.in .gs-stars.gs-slot { opacity: 0.9; }

/* sky drifts down into place */
.gs-sky .gs-slot   { transform: translate3d(0, -78px, 0) scale(0.88); }
/* plants grow up out of the ground. The growth reads through the scale delta
   and the bottom origin, not through an overshoot. */
.gs-mid .gs-slot   { transform: translate3d(0, 48px, 0) scale(0.62); transform-origin: 50% 100%; }
/* ground rises from below */
.gs-ground .gs-slot { transform: translate3d(0, 118px, 0); }
/* the character rises from behind the grass. Longer travel and a longer
   duration carry the arrival; it decelerates like a real body would. */
.gs-char.gs-slot {
  transform: translate3d(0, 142px, 0) scale(0.70);
  transform-origin: 50% 100%;
  transition-duration: 1.3s;
}

/* =============================================================================
   Ambient life, on the image so it composes with both of the above. Every
   element has its own period, so the scene never pulses in unison.
   ============================================================================= */
@media (prefers-reduced-motion: no-preference) {
  .gs.in .gs-moon   > img { animation: gsFloat 9s  ease-in-out infinite alternate; }
  .gs.in .gs-island > img { animation: gsFloat 7s  ease-in-out infinite alternate -2s; }
  .gs.in .gs-clouds > img { animation: gsDrift 16s ease-in-out infinite alternate; }
  .gs.in .gs-stars  > img { animation: gsTwinkle 5s ease-in-out infinite alternate; }
  .gs.in .gs-tree-lg > img { animation: gsSway 8s ease-in-out infinite alternate; }
  .gs.in .gs-tree-sm > img { animation: gsSway 6s ease-in-out infinite alternate -1.5s; }
  .gs.in .gs-bush   > img { animation: gsSway 7s  ease-in-out infinite alternate -3s; }
  .gs.in .gs-bushf  > img { animation: gsSway 9s  ease-in-out infinite alternate -1s; }
  .gs.in .gs-char   > img { animation: gsBob 4.5s ease-in-out infinite alternate; }
  /* Ambient waits out the entrance so the two never fight for the eye. */
  .gs.in .gs-slot > img { animation-delay: calc(var(--i, 0) * 95ms + 1100ms); }
}
@keyframes gsFloat   { from { transform: translateY(0) scale(1); } to { transform: translateY(-28px) scale(1.03); } }
@keyframes gsDrift   { from { transform: translate(0,0); }   to { transform: translate(58px,-16px); } }
@keyframes gsSway    { from { transform: rotate(-2.2deg); }  to { transform: rotate(2.2deg); } }
@keyframes gsBob     { from { transform: translateY(0) scale(1); } to { transform: translateY(-13px) scale(1.015); } }
@keyframes gsTwinkle { from { opacity: 0.45; }               to { opacity: 1; } }

.g-scene-caption {
  display: flex; justify-content: space-between; gap: 0.4rem 1rem; flex-wrap: wrap;
  padding-top: 0.9rem;
  font-size: var(--fs-label); letter-spacing: 0.2em; text-transform: uppercase;
  color: var(--text-3);
  pointer-events: none;
}

/* Tablet: thin the scene rather than shrink everything. */
@media (max-width: 900px) {
  .gs { aspect-ratio: 4 / 3; }
  .gs-bush { display: none; }
  .gs-char { width: 26%; left: 54%; bottom: 1%; }
  .gs-tree-lg { width: 23%; }
  .gs-tree-sm { width: 17%; }
  .gs-stars { width: 38%; }
  .gs-moon { width: 15%; left: 20%; }
  .gs-clouds { width: 38%; }
  .gs-island { width: 25%; }

  /* Same problem as the phone, one step milder. A 4:3 frame is much shorter
     than a 16:10 one at the same width, so the desk throw of 78px against a
     480px tall scene was 16% of it and lifted the tree clear of the ground it
     stands in. Floor tuned to the tree again: 0.24 x 44 against 1.40 x 8 is
     10.6px per unit against 11.2. */
  .gs { --gs-throw: 44px; --gs-floor: 8px; }
}

/* Phone: portrait, and only what stays legible.

   Everything on this layout has to be standing on something. The tree used to
   float with its trunk ending in mid air well above the grass, and a bush sat
   beside it with nothing underneath at all. The bush is gone, the tree is
   planted in the ground row and grown, and clouds come back to fill the sky the
   island and the second tree left empty. */
@media (max-width: 620px) {
  .gs { aspect-ratio: 3 / 4; }
  .gs-island, .gs-tree-lg, .gs-bushf { display: none; }
  /* The third ground piece was hidden on phones, which left the right of the
     scene with no floor at all: the tree stood on nothing and the corner under
     Gulsabi's right side was bare. It is the only ground artwork that reaches
     that far, so it comes back. */
  .gs-g4 { display: block; right: -5%; bottom: 1%; width: 46%; }
  /* Nothing held it up. */
  .gs-bush    { display: none; }
  /* Wider and lower, because the band between the stars and the bushes was the
     one part of the frame with nothing in it. */
  .gs-stars   { left: -5%;  top: 13%; width: 66%; }
  /* Sky depths are negative, so the moon climbs as you scroll. Resting at 3% it
     had nowhere to climb to and left through the top. Lower start, same drift. */
  .gs-moon    { left: 62%;  top: 11%; width: 24%; }
  /* A couple of clouds under the moon, bled past the right edge so the group
     reads as part of a wider sky rather than a motif placed in a box. */
  .gs-clouds  { display: block; right: -2%; top: 24%; width: 56%; }
  .gs-char    { left: 42%;  bottom: 1%; width: 44%; }
  /* Larger, and low enough that the trunk disappears into the grass instead of
     stopping in the air. It sits behind Gulsabi, which is what gives the corner
     any depth at all. */
  .gs-tree-sm { right: -9%; bottom: 14%; width: 44%; }
  .gs-g2 { left: 0%;  bottom: 2%; width: 62%; }
  .gs-g3 { left: 34%; bottom: 0%; width: 64%; }

  /* The throw is measured in pixels but the frame is not. On a wide 16:10 desk
     scene 78px of travel is a tenth of the height and reads as depth. In a 3:4
     portrait frame the same 78px is a quarter of it, and the scene comes apart:
     the tree lifted 4% clear of the ground it was planted in, Gulsabi walked his
     feet 5% past the bottom edge, and the moon left the frame entirely.

     Smaller throw, and the floor tuned so that ground and trees travel at the
     same rate: 0.24 x 26 for the tree against 1.40 x 4.5 for the ground row is
     6.2px per unit against 6.3, so the tree stays planted wherever you stop. */
  .gs { --gs-throw: 26px; --gs-floor: 4.5px; }
}

@media (prefers-reduced-motion: reduce) {
  .gs-slot { opacity: 1 !important; transform: none !important; transition: none !important; }
  .gs-stars.gs-slot { opacity: 0.9 !important; }
  .gs-slot > img { translate: none !important; animation: none !important; }
}

.gulsabi-body {
  display: grid; grid-template-columns: 1fr 1fr;
  gap: clamp(2rem, 5vw, 4rem);
  align-items: start;
  margin-top: clamp(3rem, 7vh, 4.5rem);
  padding-bottom: clamp(6rem, 14vh, 11rem);
}
.gulsabi-actions {
  display: grid; grid-template-columns: repeat(2, 1fr); gap: 1px;
  background: color-mix(in srgb, var(--g-gold) 18%, transparent);
  border: 1px solid color-mix(in srgb, var(--g-gold) 18%, transparent);
  border-radius: 14px; overflow: hidden;
}
.g-act {
  background: rgba(14, 11, 9, 0.72);
  padding: clamp(1.3rem, 3vw, 1.9rem);
  transition: background 0.45s var(--e-out);
}
.g-act:hover { background: rgba(26, 20, 14, 0.9); }
.g-act h5 {
  font-family: var(--font-display); font-size: 1.05rem; font-weight: 600;
  text-transform: uppercase; letter-spacing: -0.01em;
  color: var(--g-cream); display: flex; align-items: center; gap: 0.55rem;
}
.g-act h5 .dot { width: 7px; height: 7px; border-radius: 50%; flex: none; }
.g-act:nth-child(1) .dot { background: var(--g-gold); }
.g-act:nth-child(2) .dot { background: var(--g-coral); }
.g-act:nth-child(3) .dot { background: var(--g-teal); }
.g-act:nth-child(4) .dot { background: #ffd600; }
.g-act p {
  color: color-mix(in srgb, var(--g-cream) 58%, var(--text-2));
  font-size: var(--fs-small); margin-top: 0.5rem;
}

.gulsabi-side { display: grid; gap: 1.6rem; }
.gulsabi-side .lede { color: color-mix(in srgb, var(--g-cream) 66%, var(--text-2)); }
.gulsabi-cta { display: flex; flex-wrap: wrap; gap: 0.9rem; align-items: center; }

@media (max-width: 900px) {
  .gulsabi-body { grid-template-columns: 1fr; }
  .gulsabi-actions { grid-template-columns: 1fr; }
}

/* =============================================================================
   16. Selected work — an editorial gallery of real projects
   ============================================================================= */
/* ---- Pinned horizontal gallery ----------------------------------------------
   The section is tall; its inner sticks for one viewport while the track is
   translated sideways by scroll progress. Section height is set from JS to
   exactly the track's overflow, so travel finishes as the pin releases and
   reverses precisely on the way back up. */
.work-pin { position: relative; }
/* Everything inside must fit one viewport: the inner centres its children and
   clips, so any overflow would be cut at BOTH ends. Sizes below are budgeted
   against 100svh rather than left to grow. */
.work-pin-inner {
  position: sticky; top: 0;
  height: 100svh; overflow: hidden;
  display: flex; flex-direction: column; justify-content: center;
  gap: clamp(1.1rem, 3vh, 2.2rem);
  padding-top: 80px;                 /* clears the fixed nav */
}
.work-pin-head { display: block; }
.work-pin-head .section-head { margin-bottom: 0; gap: 0.8rem; }
.work-pin-head .section-head h2 {
  max-width: none;                   /* one authored line; do not re-wrap it */
  font-size: clamp(1.6rem, 3.4vw, 2.7rem);
}

/* Travel indicator, set along the foot of the pinned frame where it reads as a
   scroll position rather than a stray rule beside the heading. */
.work-progress {
  position: relative; height: 1px;
  background: var(--line);
  width: 100%; max-width: var(--maxw);
  margin: 0 auto; flex: none;
}
.work-progress .wp-fill {
  position: absolute; inset: 0; background: var(--accent);
  transform-origin: left; transform: scaleX(var(--wp, 0));
}

.work-track {
  display: flex; align-items: flex-start;
  gap: clamp(1.5rem, 4vw, 4.5rem);
  padding-inline: var(--gutter);
  width: max-content;
  transform: translate3d(calc(var(--wx, 0px) * -1), 0, 0);
  will-change: transform;
}
.work-slide { display: grid; gap: 0.95rem; align-content: start; }
.work-slide .work-frame {
  height: min(40svh, 400px); width: auto;
  aspect-ratio: var(--ar, 3 / 2);
}
.work-slide .work-info { max-width: 40ch; }
.work-slide .work-tags { margin-top: 0.3rem; }

.work-slide--end { display: grid; align-items: center; height: min(40svh, 400px); }
.work-end { display: grid; gap: 1.2rem; justify-items: start; max-width: 34ch; }
.work-end p { color: var(--text-2); font-size: var(--fs-lede); line-height: 1.45; }

/* Each frame takes its source's aspect so nothing important is cropped away.
   The mixed ratios also give the gallery an editorial rhythm. */
.work-frame {
  border-radius: 4px;
  border: 1px solid var(--line-soft);
  aspect-ratio: var(--ar, 3 / 2);
  transition: border-color 0.5s var(--e-out);
  display: block;
}
.work-frame img { transform: translate3d(0, calc(var(--mp, 0) * 2.4%), 0) scale(1.06); }
/* Transform is written every frame (parallax); only filter is transitioned. */
.work-frame img {
  filter: saturate(0.82) contrast(1.03) brightness(0.94);
  transition: filter 0.7s var(--e-out);
}
.work-item:hover .work-frame { border-color: var(--line-hard); }
.work-item:hover .work-frame img { filter: saturate(1) contrast(1.02) brightness(1); }

.work-info { display: grid; gap: 0.55rem; align-content: start; }
.work-no {
  font-family: var(--font-display); font-weight: 400; font-size: 0.9rem;
  color: var(--text-3); letter-spacing: 0.06em;
}
.work-info h3 {
  font-size: clamp(1.5rem, 2.6vw, 2.2rem); font-weight: 600;
  letter-spacing: -0.04em; line-height: 0.95; text-transform: uppercase;
}
.work-info p { color: var(--text-2); max-width: 40ch; font-size: var(--fs-small); }
/* Scope, set as one quiet line rather than a row of chips. */
.work-tags {
  list-style: none; display: flex; flex-wrap: wrap;
  gap: 0.1rem 0.55rem; margin-top: 0.45rem;
}
.work-tags li {
  font-size: 0.7rem; letter-spacing: 0.14em; text-transform: uppercase;
  color: var(--text-3);
  display: inline-flex; align-items: center; gap: 0.55rem;
}
.work-tags li + li::before {
  content: ""; width: 3px; height: 3px; border-radius: 50%;
  background: var(--text-3); opacity: 0.6;
}

/* Profile download — the closing panel of the work section */
.profile-panel {
  position: relative; overflow: hidden;
  display: grid; grid-template-columns: minmax(0, 1.1fr) minmax(0, 1fr);
  gap: clamp(1.5rem, 4vw, 3rem); align-items: center;
  border: 1px solid var(--line-soft); border-radius: 6px;
  background: var(--bg-2);
  margin-top: clamp(3rem, 8vh, 6rem);
}
.profile-copy { padding: clamp(1.8rem, 4vw, 3.2rem); display: grid; gap: 1.1rem; }
.profile-copy h3 {
  font-size: clamp(1.7rem, 3.6vw, 2.6rem); font-weight: 600;
  letter-spacing: -0.035em; text-transform: uppercase; line-height: 1;
}
.profile-copy p { color: var(--text-2); max-width: 40ch; font-size: var(--fs-small); }
.brochure-meta {
  display: flex; gap: 1.2rem; flex-wrap: wrap;
  font-size: var(--fs-label); letter-spacing: 0.2em; text-transform: uppercase;
  color: var(--text-3);
}
/* The cover is presented as the document it is — a physical artifact on a dark
   field — rather than a bleeding backdrop, so its own wordmark and title do not
   compete with the panel's heading. */
.profile-visual {
  position: relative; align-self: stretch; min-height: 260px;
  display: grid; place-items: center;
  padding: clamp(1.5rem, 3.5vw, 2.6rem);
  background: radial-gradient(70% 70% at 50% 45%, color-mix(in srgb, var(--accent) 10%, transparent), transparent 70%);
}
.profile-visual img {
  width: auto; max-width: 100%; max-height: 300px; height: auto;
  object-fit: contain;
  border-radius: 3px;
  transform: rotate(-2.4deg) translate3d(0, calc(var(--mp, 0) * -2%), 0);
  box-shadow:
    0 2px 6px rgba(0, 0, 0, 0.5),
    0 24px 48px rgba(0, 0, 0, 0.55);
  will-change: transform;
}

/* Short desktop viewports (e.g. 1024x640) cannot afford the full frame budget.
   Shrink the frame rather than let the pinned column clip. */
@media (min-width: 901px) and (max-height: 720px) {
  .work-pin-inner { padding-top: 68px; gap: 0.8rem; }
  .work-pin-head .section-head { gap: 0.6rem; }
  .work-pin-head .section-head h2 { font-size: clamp(1.4rem, 2.4vw, 1.9rem); }
  .work-slide .work-frame { height: 34svh; }
  .work-slide--end { height: 34svh; }
  .work-slide .work-info p { display: none; }
}

/* Below 900px the gallery unpins into a plain stacked column. Horizontal travel
   under vertical scroll reads as a scroll trap on touch, and the slides would
   be too small to be worth it. JS checks the same breakpoint before it takes
   over the section height. */
@media (max-width: 900px) {
  .work-pin { padding-block: var(--sec-pad); }
  .work-pin-inner {
    position: static; height: auto; overflow: visible;
    display: block;
  }
  .work-pin-head { display: block; margin-bottom: clamp(2.5rem, 7vh, 4rem); }
  .work-progress { display: none; }
  .work-track {
    display: grid; width: auto; transform: none; will-change: auto;
    padding-inline: 0; gap: clamp(2.5rem, 7vh, 4rem);
  }
  .work-track { max-width: var(--maxw); margin-inline: auto; padding-inline: var(--gutter); }
  .work-slide .work-frame { height: auto; width: 100%; }
  .work-slide--end { height: auto; }
}

@media (max-width: 860px) {
  .profile-panel { grid-template-columns: 1fr; }
  .profile-visual { min-height: 0; order: -1; padding-bottom: 0; }
  .profile-visual img { max-height: 220px; }
}

/* =============================================================================
   18. Contact
   ============================================================================= */
.contact { position: relative; }
.contact h2 {
  font-size: clamp(2.2rem, 7vw, 5.4rem); font-weight: 600;
  letter-spacing: -0.045em; line-height: 0.94; text-transform: uppercase;
  max-width: 24ch; margin-top: 1.6rem;
}
.contact h2 .accent { color: var(--accent-2); }

.email-block {
  margin-top: clamp(2.5rem, 6vh, 4rem);
  display: flex; flex-wrap: wrap; align-items: center; gap: 1rem 1.6rem;
}
.email-link {
  position: relative;
  font-family: var(--font-display); font-weight: 500;
  font-size: clamp(1.4rem, 5.2vw, 3.1rem);
  letter-spacing: -0.03em; line-height: 1.1;
  padding-bottom: 0.1em;
}
.email-link::after {
  content: ""; position: absolute; left: 0; right: 0; bottom: 0; height: 1px;
  background: var(--accent);
  transform: scaleX(0.999); transform-origin: left;
  transition: transform 0.5s var(--e-out);
}
.email-link:hover::after { transform: scaleX(1); }
/* On a phone this is the only way to contact the studio, and its box was 27px
   tall because an inline link is only as tall as its line. It becomes a block
   with vertical padding, and the underline moves up by the same amount so it
   stays against the text rather than riding down with the box.

   Vertical only. The Copy button sits beside this, and at 320px there is not
   enough room to grow sideways without swallowing it. The parent is a centred
   flex row, so the taller box keeps both aligned. */
@media (max-width: 620px) {
  .email-link { display: inline-block; padding-block: 10px; }
  .email-link::after { bottom: 10px; }
}
.email-link::before {
  content: ""; position: absolute; left: 0; right: 0; bottom: 0; height: 1px;
  background: var(--line);
}

.copy-email {
  display: inline-flex; align-items: center; gap: 0.5rem; cursor: pointer;
  background: none; border: 1px solid var(--line-soft); border-radius: 999px;
  padding: 0.55rem 1.05rem; color: var(--text-3); font-size: 0.8rem;
  letter-spacing: 0.06em;
  transition: color 0.3s var(--e-out), border-color 0.3s var(--e-out);
}
.copy-email:hover { color: var(--text); border-color: var(--accent); }
.copy-email .check { display: none; }
.copy-email.copied { color: var(--g-teal); border-color: var(--g-teal); }
.copy-email.copied .label-copy { display: none; }
.copy-email.copied .check { display: inline; }

.contact-actions { margin-top: 2.6rem; display: flex; flex-wrap: wrap; gap: 0.9rem; }

/* Location lives here and nowhere else on the page. */
.contact-meta {
  margin-top: clamp(3.5rem, 9vh, 6rem);
  padding-top: 1.4rem;
  border-top: 1px solid var(--line-soft);
  display: flex; flex-wrap: wrap; gap: 1.4rem clamp(2rem, 5vw, 4.5rem);
}
.contact-meta div { display: grid; gap: 0.35rem; }
.contact-meta dt {
  font-size: var(--fs-label); letter-spacing: 0.24em; text-transform: uppercase;
  color: var(--text-3); font-weight: 500;
}
.contact-meta dd { font-size: var(--fs-small); color: var(--text); font-weight: 500; }

/* =============================================================================
   19. Footer
   ============================================================================= */
/* The footer is the site's information and trust layer, and it descends in three
   steps: navigation, then who the company legally is, then the wordmark as the
   last thing on the page. Identical markup on every page, generated by
   tools/build.mjs, so the homepage and a policy page cannot drift apart. */
/* The wordmark is the last thing on the page, so the scroll chrome gets out of
   its way. The rail and the section counter otherwise sit on top of it at full
   strength, which turns a closing statement into a screen with furniture on it. */
#signal-rail { transition: opacity 0.6s var(--e-out); }
body.at-footer #signal-rail,
body.at-footer #section-index { opacity: 0; }

footer.site-footer {
  position: relative; z-index: 1;
  border-top: 1px solid var(--line-soft);
  padding-top: clamp(3.5rem, 9vh, 6rem);
  background: var(--bg-2);
  overflow: hidden;   /* the wordmark is deliberately wider than its box */
}
.footer-top {
  display: grid; grid-template-columns: minmax(15rem, 1fr) 2.6fr;
  gap: clamp(2.4rem, 5vw, 4.5rem);
}
.footer-brand img { height: 24px; width: auto; margin-bottom: 1.3rem; }
.footer-brand p { color: var(--text-2); font-size: var(--fs-small); max-width: 32ch; }
.footer-mail {
  display: inline-block; margin-top: 1.1rem;
  font-size: var(--fs-small); color: var(--text-2);
  border-bottom: 1px solid var(--line-soft); padding-bottom: 2px;
  transition: color 0.3s var(--e-out), border-color 0.3s var(--e-out);
}
.footer-mail:hover { color: var(--text); border-bottom-color: var(--accent); }

.footer-nav {
  display: grid; grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: clamp(1.6rem, 3vw, 2.6rem);
}
.footer-col h2, .footer-col h5 {
  font-family: var(--font-text); word-spacing: normal;
  font-size: var(--fs-label); letter-spacing: 0.22em; text-transform: uppercase;
  color: var(--text-3); margin-bottom: 1.1rem; font-weight: 500;
}
.footer-col a {
  display: block; color: var(--text-2); font-size: var(--fs-small);
  /* Comfortable to hit with a thumb without opening the list up on desktop. */
  padding-block: 0.42rem; width: fit-content; max-width: 100%;
  transition: color 0.3s var(--e-out), transform 0.35s var(--e-out);
}
.footer-col a:hover, .footer-col a:focus-visible { color: var(--text); transform: translateX(3px); }
/* Leaves the site, so it carries a mark that moves the way the link does. */
.footer-col a.ext-link { display: inline-flex; align-items: baseline; gap: 0.4rem; }
.ext-link .ext {
  font-size: 0.78em; color: var(--accent);
  transition: transform 0.35s var(--e-out);
}
.ext-link:hover .ext { transform: translate(2px, -2px); }
@media (prefers-reduced-motion: reduce) {
  .footer-col a:hover, .footer-col a:focus-visible { transform: none; }
  .ext-link:hover .ext { transform: none; }
}

/* Who this actually is, in law. Understated, but not buried in a policy page. */
.footer-legal {
  margin-top: clamp(2.6rem, 6vh, 4rem); padding-top: 1.7rem;
  border-top: 1px solid var(--line-soft);
  display: flex; justify-content: space-between; align-items: flex-end;
  flex-wrap: wrap; gap: 1rem 2rem;
  font-size: 0.8rem; color: var(--text-2);
}
.footer-entity { font-style: normal; display: grid; gap: 0.25rem; }
.footer-entity strong { color: var(--text); font-weight: 500; }
.footer-copy { color: var(--text-2); }

/* The last statement on the page. The mark is an SVG so it scales without ever
   softening, and width alone drives it: setting a height as well is what
   stretched this logo the last time. */
.footer-mega {
  margin-top: clamp(3rem, 9vh, 6.5rem);
  padding-inline: clamp(0.9rem, 2.5vw, 2.2rem);
  line-height: 0;
}
.footer-mega img {
  display: block; width: 100%; height: auto;
  opacity: 0.1;
  transition: opacity 1.2s var(--e-out);
}
footer.site-footer:hover .footer-mega img { opacity: 0.16; }
@media (prefers-reduced-motion: reduce) { .footer-mega img { transition: none; } }

@media (max-width: 1000px) {
  .footer-top { grid-template-columns: 1fr; }
  .footer-nav { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 2rem 1.5rem; }
}
@media (max-width: 560px) {
  /* Two columns of 100px would be unreadable, so the groups stack full width
     and the touch targets grow instead of shrinking. */
  .footer-nav { grid-template-columns: 1fr; gap: 1.9rem; }
  .footer-col a { padding-block: 0.55rem; font-size: 0.92rem; }
  .footer-legal { flex-direction: column; align-items: flex-start; }
  .footer-mega { margin-top: clamp(2.4rem, 7vh, 4rem); }
  .footer-mega img { opacity: 0.13; }
}

/* =============================================================================
   19b. Live section index — quiet editorial position marker
   ============================================================================= */
#section-index {
  position: fixed; z-index: 80;
  right: max(1.4rem, calc((100vw - var(--maxw)) / 2 + var(--gutter) - 1rem));
  bottom: 1.6rem;
  display: flex; align-items: baseline; gap: 0.6rem;
  font-size: var(--fs-label); letter-spacing: 0.22em; text-transform: uppercase;
  color: var(--text-3);
  opacity: 0; pointer-events: none;
  transition: opacity 0.6s var(--e-out);
  /* No blend mode: over bright work imagery difference-blending turns this into
     an unpredictable colour. A muted ink reads on both dark ground and photos. */
  text-shadow: 0 1px 8px rgba(7, 7, 7, 0.85);
}
#section-index.on { opacity: 1; }
#section-index .si-no {
  font-family: var(--font-display); font-weight: 500; letter-spacing: 0.04em;
  color: var(--text-2);
}
@media (max-width: 900px) { #section-index { display: none; } }

/* =============================================================================
   20. Cursor — fine pointers only
   ============================================================================= */
/* The cursor is a zero-size point that JS translates every frame; the ring and
   the label are grid-stacked children centred on it. The ring resizes with
   transform, never width/height — animating box size would force layout on
   every frame of the hover transition, while the position write is already
   happening each frame. The label is a sibling, not a child of the ring, so it
   is not counter-scaled. */
#cursor {
  position: fixed; top: 0; left: 0; z-index: 250;
  width: 0; height: 0;
  pointer-events: none; opacity: 0;
  transition: opacity 0.35s var(--e-out);
}
#cursor.on { opacity: 1; }
/* Centred on the origin with negative margins, not with a transform — the ring's
   transform belongs to the scale. (Grid centring is wrong here: auto tracks
   size to the item, so a 0x0 grid parent anchors it at the origin instead.) */
#cursor .ring {
  position: absolute; top: 0; left: 0;
  width: 34px; height: 34px; margin: -17px 0 0 -17px;
  border-radius: 50%;
  border: 1px solid var(--line-hard);
  transform: scale(1);
  transition: transform 0.4s var(--e-out), background 0.4s var(--e-out),
              border-color 0.4s var(--e-out);
}
#cursor .cur-label {
  position: absolute; top: 0; left: 0;
  transform: translate(-50%, -50%);
  font-size: 0.58rem; letter-spacing: 0.16em; text-transform: uppercase;
  font-weight: 600; color: #000; opacity: 0; transition: opacity 0.3s var(--e-out);
  white-space: nowrap;
}
/* 22 / 34 and 78 / 34 — the sizes the ring used to animate to. */
#cursor.hot  .ring { transform: scale(0.647); background: var(--text); border-color: transparent; }
#cursor.view .ring { transform: scale(2.294); background: var(--text); border-color: transparent; }
#cursor.view .cur-label { opacity: 1; }

@media (hover: none), (pointer: coarse), (prefers-reduced-motion: reduce) {
  #cursor { display: none !important; }
}

/* =============================================================================
   21. Utility
   ============================================================================= */
.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;
}
.skip-link {
  position: fixed; top: 0; left: 50%; transform: translate(-50%, -120%);
  z-index: 400; background: var(--accent); color: var(--on-accent);
  padding: 0.7rem 1.3rem; border-radius: 0 0 8px 8px; font-size: var(--fs-small);
  transition: transform 0.3s var(--e-out);
}
.skip-link:focus-visible { transform: translate(-50%, 0); }

/* Deep-linked chapters clear the fixed header instead of hiding beneath it. */
.chapter { scroll-margin-top: clamp(5rem, 12vh, 7rem); }

/* =============================================================================
   22. Reading pages — policies, FAQ, sitemap
   These pages exist to be read and to be trusted, so the homepage machinery
   (preloader, custom cursor, scroll rail, entrance reveals) is deliberately not
   carried over. Nothing here fades in, nothing waits for a scroll position, and
   the text is legible the instant the page paints. The type, colour and rhythm
   are the site's own, so it still reads as Pixnut rather than as a document
   pasted into a template.
   ============================================================================= */
.doc-page { background: var(--bg); }
/* The homepage nav is transparent until site.js marks it scrolled, over a hero
   built to carry it. A reading page has no hero and does not load site.js, so
   this one carries its own surface from the first pixel. Without it the footer
   scrolls straight through the logo. */
header.nav--doc {
  background: rgba(7, 7, 7, 0.82);
  -webkit-backdrop-filter: blur(18px) saturate(1.3);
  backdrop-filter: blur(18px) saturate(1.3);
  border-bottom-color: var(--line-soft);
}
/* Links inside the opening paragraph read as links, the same as in the body. */
.doc-lede a {
  color: var(--accent-2); text-decoration: underline;
  text-decoration-color: color-mix(in srgb, var(--accent) 40%, transparent);
  text-underline-offset: 3px; transition: color 0.25s var(--e-out);
}
.doc-lede a:hover, .doc-lede a:focus-visible { color: var(--accent); }
.doc { padding-block: clamp(6.5rem, 15vh, 10rem) clamp(4rem, 10vh, 7rem); }

/* Breadcrumb: two levels, so it orients without becoming furniture. */
.crumb ol { list-style: none; display: flex; flex-wrap: wrap; gap: 0.5rem; }
.crumb li { font-size: var(--fs-label); letter-spacing: 0.18em; text-transform: uppercase; color: var(--text-2); }
.crumb li + li::before { content: "/"; margin-right: 0.5rem; color: var(--text-3); }
.crumb a {
  color: var(--text-2); transition: color 0.3s var(--e-out);
  /* 14px of text is a poor thing to aim a thumb at. The type stays the size it
     should be and the hit area grows around it. */
  display: inline-block; padding-block: 0.55rem; margin-block: -0.55rem;
}
.crumb a:hover, .crumb a:focus-visible { color: var(--accent); }

.doc-head { margin-top: clamp(1.6rem, 4vh, 2.6rem); }
/* The headline wants a short measure. The paragraph under it does not, and it
   was inheriting the headline's, so the intro read at half the width of the body
   it introduces and then the page abruptly widened. */
.doc h1 { max-width: 15ch; }
.doc h1 {
  font-family: var(--font-display); font-weight: 600;
  font-size: clamp(2.1rem, 6vw, 4.2rem); line-height: 0.98;
  letter-spacing: -0.035em; text-transform: uppercase; word-spacing: 0.11em;
}
.doc-lede {
  margin-top: 1.5rem; max-width: 62ch;
  font-size: var(--fs-lede); line-height: 1.62; color: var(--text-2);
}
.doc-meta {
  margin-top: 1.8rem; padding-top: 1.1rem; border-top: 1px solid var(--line-soft);
  display: flex; flex-wrap: wrap; gap: 0.4rem 1.8rem; max-width: 62ch;
  font-size: var(--fs-label); letter-spacing: 0.16em; text-transform: uppercase;
  color: var(--text-2);
}

/* Contents. Long policies are navigable rather than scrollable. */
.doc-toc {
  margin-top: clamp(2.6rem, 6vh, 4rem); padding: clamp(1.4rem, 3vw, 2rem);
  border: 1px solid var(--line-soft); border-radius: 4px; background: var(--bg-2);
  max-width: 62ch;
}
.doc-toc h2 {
  font-family: var(--font-text); font-size: var(--fs-label); font-weight: 500;
  letter-spacing: 0.22em; text-transform: uppercase; color: var(--text-3);
  margin-bottom: 0.9rem;
}
.doc-toc ol { list-style: none; counter-reset: toc; display: grid; gap: 0.15rem; }
.doc-toc li { counter-increment: toc; }
.doc-toc a {
  display: block; padding-block: 0.36rem; color: var(--text-2);
  font-size: var(--fs-small); transition: color 0.3s var(--e-out);
}
.doc-toc li a::before {
  content: counter(toc, decimal-leading-zero); margin-right: 0.85rem;
  color: var(--text-3); font-size: 0.78em; letter-spacing: 0.1em;
}
.doc-toc a:hover, .doc-toc a:focus-visible { color: var(--accent); }

.doc-body { margin-top: clamp(2.6rem, 6vh, 4rem); }
/* Anchored headings must clear the sticky header when jumped to. */
.doc-section { scroll-margin-top: 6rem; }
.doc-section + .doc-section { margin-top: clamp(2.8rem, 7vh, 4.5rem); }
.doc-section h2 {
  font-family: var(--font-display); font-weight: 600;
  font-size: clamp(1.35rem, 2.6vw, 1.85rem); line-height: 1.14;
  letter-spacing: -0.02em; text-transform: uppercase; word-spacing: 0.11em;
  padding-bottom: 0.9rem; border-bottom: 1px solid var(--line-soft);
  max-width: 62ch;
}
.doc-section h3 {
  font-size: 1.02rem; font-weight: 600; letter-spacing: -0.01em;
  color: var(--text); margin-top: 2rem;
}
.doc-body p, .doc-body li {
  color: var(--text-2); font-size: var(--fs-body); line-height: 1.75; max-width: 66ch;
}
.doc-body p { margin-top: 1.1rem; }
.doc-body ul, .doc-body ol { margin-top: 1.1rem; padding-left: 1.35rem; display: grid; gap: 0.55rem; }
.doc-body li::marker { color: var(--text-3); }
.doc-body strong { color: var(--text); font-weight: 500; }
.doc-body a {
  color: var(--accent-2); text-decoration: underline;
  text-decoration-color: color-mix(in srgb, var(--accent) 40%, transparent);
  text-underline-offset: 3px; transition: color 0.25s var(--e-out), text-decoration-color 0.25s var(--e-out);
}
.doc-body a:hover, .doc-body a:focus-visible { color: var(--accent); text-decoration-color: var(--accent); }
.doc-steps { counter-reset: step; list-style: none; padding-left: 0; }
.doc-steps li { counter-increment: step; padding-left: 2.4rem; position: relative; }
.doc-steps li::before {
  content: counter(step, decimal-leading-zero); position: absolute; left: 0; top: 0.05em;
  font-size: 0.8em; letter-spacing: 0.1em; color: var(--accent);
}
.doc-back { margin-top: clamp(3rem, 8vh, 5rem); padding-top: 1.6rem; border-top: 1px solid var(--line-soft); }

/* Tables scroll inside themselves. The page never scrolls sideways. */
.tablewrap { margin-top: 1.4rem; overflow-x: auto; border: 1px solid var(--line-soft); border-radius: 4px; }
.doc-table { border-collapse: collapse; width: 100%; min-width: 44rem; font-size: var(--fs-small); }
.doc-table th, .doc-table td {
  text-align: left; vertical-align: top; padding: 0.85rem 1rem;
  border-bottom: 1px solid var(--line-soft); color: var(--text-2); line-height: 1.6;
}
.doc-table thead th {
  font-size: var(--fs-label); letter-spacing: 0.16em; text-transform: uppercase;
  color: var(--text-2); font-weight: 500; background: var(--bg-2);
}
.doc-table tbody th { color: var(--text); font-weight: 500; white-space: nowrap; }
.doc-table code { font-family: ui-monospace, "SF Mono", Menlo, monospace; font-size: 0.92em; color: var(--accent-2); }
.doc-table tbody tr:last-child th, .doc-table tbody tr:last-child td { border-bottom: 0; }

/* A five-column table is a five-column table: below the width where all five
   columns actually fit, the rest sit off screen and every row stretches to the
   height of text nobody can see, so the visible part is mostly empty cells. Each
   entry becomes its own card instead, carrying its column name with it, and the
   header row is redundant once every cell is labelled.

   The cut is 900px, not a phone width. Measured: the table needs 704px of column
   and only gets it from about 820px of viewport, and even at 820 the five
   columns are sharing 144px each, which is not reading, it is squinting. 900 is
   also where the nav switches, so the layout changes on one line rather than
   two. */
@media (max-width: 900px) {
  .tablewrap { overflow: visible; border: 0; border-radius: 0; }
  .doc-table { min-width: 0; display: block; }
  .doc-table thead { display: none; }
  .doc-table tbody, .doc-table tr, .doc-table th, .doc-table td { display: block; width: 100%; }
  .doc-table tr {
    border: 1px solid var(--line-soft); border-radius: 4px;
    background: var(--bg-2); padding: 1rem 1.1rem;
  }
  .doc-table tr + tr { margin-top: 0.9rem; }
  .doc-table tbody th {
    border-bottom: 1px solid var(--line-soft); padding: 0 0 0.7rem;
    white-space: normal; font-size: 0.95rem;
  }
  .doc-table td { padding: 0.7rem 0 0; border-bottom: 0; }
  .doc-table td::before {
    content: attr(data-label); display: block; margin-bottom: 0.2rem;
    font-size: var(--fs-label); letter-spacing: 0.16em; text-transform: uppercase;
    color: var(--text-3);
  }
  .doc-table tbody tr:last-child td { border-bottom: 0; }
}

/* FAQ. Native disclosure elements, so keyboard and screen reader behaviour is
   the browser's and not a reimplementation of it. Every answer is in the DOM
   whether or not it is open, which is what the structured data promises. */
.faq-list { margin-top: 1.4rem; border-top: 1px solid var(--line-soft); }
.faq-item { border-bottom: 1px solid var(--line-soft); }
.faq-item summary {
  list-style: none; cursor: pointer; display: flex; align-items: flex-start; gap: 1rem;
  padding: 1.15rem 0; color: var(--text); font-size: 1.02rem; line-height: 1.5;
  transition: color 0.3s var(--e-out);
}
.faq-item summary::-webkit-details-marker { display: none; }
.faq-item summary::after {
  content: ""; flex: none; margin-left: auto; margin-top: 0.55em;
  width: 11px; height: 11px;
  border-right: 1.5px solid var(--text-3); border-bottom: 1.5px solid var(--text-3);
  transform: rotate(45deg); transform-origin: 60% 60%;
  transition: transform 0.4s var(--e-out), border-color 0.3s var(--e-out);
}
.faq-item[open] summary::after { transform: rotate(-135deg); border-color: var(--accent); }
.faq-item summary:hover, .faq-item summary:focus-visible { color: var(--accent-2); }
.faq-a { padding-bottom: 1.3rem; }
.faq-a p { margin-top: 0; max-width: 66ch; }
@media (prefers-reduced-motion: reduce) { .faq-item summary::after { transition: none; } }

/* HTML sitemap */
.sm-body { display: grid; gap: clamp(2.4rem, 6vh, 3.6rem); }
.sm-body .doc-section + .doc-section { margin-top: 0; }
.sm-group h2 { border-bottom: 1px solid var(--line-soft); }
/* Beats `.doc-body ul`, so the rows align with the heading above them. */
.doc-body .sm-list { list-style: none; padding-left: 0; margin-top: 0.4rem; display: grid; gap: 0; }
.sm-list li {
  display: flex; flex-wrap: wrap; align-items: baseline; gap: 0.4rem 1.2rem;
  padding-block: 0.7rem; border-bottom: 1px solid var(--line-soft);
}
.sm-list a { font-size: 1.02rem; text-decoration: none; color: var(--text); }
.sm-list a:hover, .sm-list a:focus-visible { color: var(--accent); }
.sm-list span { color: var(--text-2); font-size: var(--fs-small); }

/* The privacy notice sits with the enquiry route it describes, not only in the
   footer, because that is where someone decides whether to write to us. */
.contact-privacy {
  margin-top: clamp(1.6rem, 4vh, 2.4rem); max-width: 58ch;
  font-size: var(--fs-small); line-height: 1.65; color: var(--text-2);
}
.contact-privacy a {
  color: var(--text-2); text-decoration: underline;
  text-decoration-color: color-mix(in srgb, var(--accent) 40%, transparent);
  text-underline-offset: 3px; transition: color 0.25s var(--e-out);
}
.contact-privacy a:hover, .contact-privacy a:focus-visible { color: var(--accent); }

/* WhatsApp sits beside the email in the footer, same weight: it is a second way
   to reach the studio, not a badge. */
.footer-wa { display: inline-block; margin-top: 0.55rem; }
.contact-meta dd a {
  color: inherit; border-bottom: 1px solid var(--line-soft);
  transition: color 0.3s var(--e-out), border-color 0.3s var(--e-out);
}
.contact-meta dd a:hover, .contact-meta dd a:focus-visible {
  color: var(--accent); border-bottom-color: var(--accent);
}

/* =============================================================================
   23. Enquiry form
   Rendered only when it can deliver. Same type and rhythm as the rest of the
   site: a form here should look like the studio wrote it, not like a widget was
   dropped in.
   ============================================================================= */
.enquiry { margin-top: clamp(2rem, 5vh, 3rem); max-width: 44rem; }
.enquiry-grid {
  display: grid; grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 1rem clamp(1rem, 2.5vw, 1.6rem);
}
.enquiry-grid .field:last-child { grid-column: 1 / -1; }
.field { display: grid; gap: 0.45rem; margin-top: 1rem; }
.enquiry-grid .field { margin-top: 0; }
.field label {
  font-size: var(--fs-label); letter-spacing: 0.16em; text-transform: uppercase;
  color: var(--text-2);
}
.field .optional { color: var(--text-3); letter-spacing: 0.1em; }
.field input, .field textarea {
  font: inherit; font-size: var(--fs-body); color: var(--text);
  background: var(--bg-2); border: 1px solid var(--line-soft); border-radius: 4px;
  padding: 0.85rem 1rem; width: 100%;
  transition: border-color 0.3s var(--e-out), background 0.3s var(--e-out);
}
.field textarea { resize: vertical; min-height: 8rem; line-height: 1.6; }
.field input:hover, .field textarea:hover { border-color: var(--line); }
.field input:focus-visible, .field textarea:focus-visible {
  outline: none; border-color: var(--accent);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--accent) 22%, transparent);
}
/* The error is announced and coloured, never colour alone. */
.field.has-error input, .field.has-error textarea { border-color: #ff8a7a; }
.field-error { display: none; font-size: var(--fs-small); color: #ff9c8e; }
.field.has-error .field-error { display: block; }

/* Off-screen rather than display:none, which some bots treat as a tell. */
.enquiry-trap {
  position: absolute; left: -9999px; width: 1px; height: 1px; overflow: hidden;
}

.enquiry .cf-turnstile { margin-top: 1.4rem; }
.enquiry-foot {
  margin-top: 1.5rem; display: flex; flex-wrap: wrap; align-items: center;
  gap: 0.9rem 1.4rem;
}
.enquiry-status { font-size: var(--fs-small); line-height: 1.5; max-width: 40ch; }
.enquiry-status.is-busy { color: var(--text-2); }
.enquiry-status.is-ok   { color: var(--g-teal); }
.enquiry-status.is-error{ color: #ff9c8e; }
.enquiry.is-sending { opacity: 0.75; }
/* Once it has sent, the fields go: leaving a filled form under a thank-you
   invites a second identical enquiry. */
.enquiry.is-sent .enquiry-grid,
.enquiry.is-sent > .field,
.enquiry.is-sent .cf-turnstile,
.enquiry.is-sent [data-enquiry-submit] { display: none; }

@media (max-width: 620px) {
  .enquiry-grid { grid-template-columns: 1fr; }
  .enquiry-foot { flex-direction: column; align-items: stretch; }
  .enquiry-foot .btn { justify-content: center; }
}
@media (prefers-reduced-motion: reduce) {
  .field input, .field textarea { transition: none; }
}
