/* Nitin Sukthe — portfolio, all styles.
   Sections run in the order the page needs them, so the cascade reads
   top to bottom: shared tokens first, then one block per page. */

/* ===========================================================================
   INTRO — opening animation
   =========================================================================== */

/* ==========================================================================
   Opening animation — "NITIN SUKTHE" wordmark intro
   State classes on #intro are toggled by intro.js:
     .is-dot     dot appears at center
     .is-line    dot stretches into a thin horizontal seam
     .is-open    seam opens vertically to reveal the wordmark
     .is-glitch  brief flicker window (per-letter slices driven by .intro__char.is-glitch)
     .is-settled glow subsides, seam fades, wordmark rests
     .is-done    overlay fades out
   ========================================================================== */

:root {
  --bg: #050505;
  --fg: #ffffff;
  --font: 'Manrope', system-ui, -apple-system, 'Segoe UI', sans-serif;
  --ease-out: cubic-bezier(0.16, 1, 0.3, 1);
  --ease-inout: cubic-bezier(0.65, 0, 0.35, 1);
  --track-start: 0.5em;
  --track-end: 0.24em;
}

html, body, main {
  margin: 0;
  background: var(--bg);
  color: var(--fg);
  min-height: 100dvh;
}

html.is-intro { overflow: hidden; }

/* Overlay -------------------------------------------------------------- */

.intro {
  position: fixed;
  inset: 0;
  z-index: 9999;
  display: grid;
  place-items: center;
  background: var(--bg);
  opacity: 1;
  transition: opacity 600ms var(--ease-inout);
}

.intro > * { grid-area: 1 / 1; }   /* stack dot, seam and wordmark on the same center point */

.intro.is-done {
  opacity: 0;
  pointer-events: none;
}

/* Dot ------------------------------------------------------------------ */

.intro__dot {
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: var(--fg);
  box-shadow: 0 0 14px rgba(255, 255, 255, 0.55);
  opacity: 0;
  transform: scale(0);
  transition: opacity 160ms var(--ease-out), transform 160ms var(--ease-out);
}

.intro.is-dot .intro__dot {
  opacity: 1;
  transform: scale(1);
}

/* dot flattens and stretches into the seam as the seam grows */
.intro.is-line .intro__dot {
  opacity: 0;
  transform: scaleX(6) scaleY(0.15);
  transition: opacity 180ms var(--ease-inout) 100ms, transform 260ms var(--ease-out);
}

/* Seam ----------------------------------------------------------------- */

.intro__line {
  width: var(--word-w, 40vw);
  height: 1px;
  background: var(--fg);
  box-shadow: 0 0 10px rgba(255, 255, 255, 0.45);
  transform: scaleX(0);
  transform-origin: center;
  transition: transform 300ms var(--ease-out), opacity 200ms var(--ease-inout);
}

.intro.is-line .intro__line { transform: scaleX(1); }

/* fades 800–1000ms, after the wordmark has opened through it */
.intro.is-open .intro__line {
  opacity: 0;
  transition: transform 300ms var(--ease-out), opacity 200ms var(--ease-inout) 380ms;
}

/* Wordmark ------------------------------------------------------------- */

.intro__word {
  margin: 0;
  padding: 0;
  font-family: var(--font);
  font-weight: 600;
  font-size: clamp(1.5rem, 6.5vw, 4.5rem);
  line-height: 1.1;
  text-transform: uppercase;
  white-space: nowrap;
  max-width: calc(100vw - 32px);
  color: var(--fg);
  letter-spacing: var(--track-start);
  opacity: 0;
  clip-path: inset(50% 0 50% 0);
  text-shadow: 0 0 24px rgba(255, 255, 255, 0.35);
  transition:
    opacity 240ms var(--ease-out),
    clip-path 480ms var(--ease-out),
    letter-spacing 520ms var(--ease-out),
    text-shadow 300ms var(--ease-inout);
}

.intro__word.is-measure {
  transition: none;
  letter-spacing: var(--track-end);
}

.intro.is-open .intro__word {
  opacity: 1;
  clip-path: inset(0 0 0 0);
  letter-spacing: var(--track-end);
}

.intro.is-glitch .intro__word {
  animation: intro-flicker 240ms steps(1, end) both;
}

.intro.is-settled .intro__word {
  text-shadow: 0 0 0 rgba(255, 255, 255, 0);
}

@keyframes intro-flicker {
  0%   { opacity: 1; }
  18%  { opacity: 0.55; }
  30%  { opacity: 1; }
  58%  { opacity: 0.75; }
  70%  { opacity: 1; }
  84%  { opacity: 0.6; }
  100% { opacity: 1; }
}

/* Per-letter glitch slices --------------------------------------------- */

.intro__char {
  position: relative;
  display: inline;   /* inline (not inline-block) so letter-spacing isn't applied twice per glyph */
}

/* no trailing tracking after the last glyph, so the line box is exactly the glyph run and centers optically */
.intro__char:last-child { letter-spacing: 0; }

.intro__char::before,
.intro__char::after {
  content: attr(data-char);
  position: absolute;
  top: 0;
  left: 0;
  opacity: 0;
  pointer-events: none;
}

.intro__char.is-glitch { opacity: 0.85; }

/* --gx: horizontal offset, --ct/--cb: slice top/bottom inset, --gop: ghost opacity (set by intro.js) */
.intro__char.is-glitch::before {
  opacity: var(--gop, 0.4);
  transform: translateX(var(--gx, 3px));
  clip-path: inset(var(--ct, 20%) 0 var(--cb, 50%) 0);
}

.intro__char.is-glitch::after {
  opacity: var(--gop, 0.4);
  transform: translateX(calc(var(--gx, 3px) * -1));
  clip-path: inset(var(--cb, 50%) 0 var(--ct, 20%) 0);
}

/* Reduced motion: plain fade in / fade out, no dot, seam, scramble or slices */

@media (prefers-reduced-motion: reduce) {
  .intro__dot,
  .intro__line { display: none; }

  .intro__word {
    clip-path: none;
    letter-spacing: var(--track-end);
    text-shadow: none;
    transition: opacity 400ms var(--ease-inout);
  }

  .intro.is-glitch .intro__word { animation: none; }
  .intro__char.is-glitch,
  .intro__char.is-glitch::before,
  .intro__char.is-glitch::after { opacity: 1; transform: none; clip-path: none; }
  .intro__char.is-glitch::before,
  .intro__char.is-glitch::after { opacity: 0; }
}


/* ===========================================================================
   SITE — tokens, hero (home page)
   =========================================================================== */

/* ==========================================================================
   Site styles — first page (hero card). Loaded after intro.css.
   Playwrite FR Moderne is scoped to the card copy; everything else uses
   --font (Manrope, from intro.css).
   ========================================================================== */

:root {
  --page-bg: #f0990c;
  --card-bg: #ffffff;
  --accent: #81ed19;
  --accent-ink: #163300;
  --ink: #2b2b2b;
  --ink-soft: #4a4a4a;
  --font-script: 'Playwrite FR Moderne', 'Playwrite France Moderne', cursive;
  --radius-card: 32px;
}

html,
body,
main {
  background: var(--page-bg);
}

html {
  /* Stops iOS Safari inflating body text when the phone is rotated. */
  -webkit-text-size-adjust: 100%;
  text-size-adjust: 100%;
  /* The grey flash box on tap; every control has its own :focus-visible ring,
     so nothing becomes invisible to keyboard users. */
  -webkit-tap-highlight-color: transparent;
}

/* Single-screen page: no rubber-band bounce at the edges. */
html,
body {
  overscroll-behavior: none;
}

/* Hero ----------------------------------------------------------------- */

.hero {
  min-height: 100dvh;
  display: grid;
  place-content: center;
  /* If the card ever outgrows the screen, overflow only downwards, where it
     can be scrolled to, instead of half of it going off the top. */
  align-content: safe center;
  justify-items: center;
  padding: 104px 16px 48px;
  box-sizing: border-box;
  opacity: 0;
  translate: 0 14px;
  transition: opacity 600ms var(--ease-out), translate 600ms var(--ease-out);
}

.hero.is-visible {
  opacity: 1;
  translate: 0 0;
}

/* Hidden while the menu is open. */
.hero.is-hidden {
  opacity: 0;
  pointer-events: none;
  transition: opacity 400ms var(--ease-inout);
}

/* Keeps orange from flashing behind the menu's crossfade. */
body.is-menu,
body.is-menu main { background: #050505; }

/* The menu is one fixed screen — don't let the hero underneath scroll it. */
html.is-menu,
body.is-menu { overflow: hidden; }

/* Wide rectangle: every point sits on its own single line. */
.hero__card {
  position: relative;
  width: min(100%, 1040px);
  box-sizing: border-box;
  background: var(--card-bg);
  border-radius: var(--radius-card);
  padding: 100px 72px 56px;
  text-align: center;
  box-shadow:
    0 30px 80px rgba(0, 0, 0, 0.22),
    0 4px 12px rgba(0, 0, 0, 0.08);
}

.hero__avatar {
  position: absolute;
  top: -70px;
  left: 50%;
  translate: -50% 0;
  width: 140px;
  height: 140px;
  border-radius: 50%;
  object-fit: cover;
  object-position: 50% 50%;
  border: 6px solid var(--card-bg);
  box-sizing: content-box;
  background: var(--card-bg);
  box-shadow: 0 12px 28px rgba(0, 0, 0, 0.22);
}

.hero__title {
  margin: 0 0 22px;
  font-family: var(--font-script);
  font-weight: 300;
  font-size: clamp(1.4rem, 3vw, 2.3rem);
  line-height: 1.5;
  letter-spacing: 0.01em;
  white-space: nowrap;
  color: var(--ink);
}

.hero__text {
  margin: 0 0 10px;
  font-family: var(--font-script);
  font-weight: 400;
  font-size: clamp(0.9rem, 1.3vw, 1.15rem);
  line-height: 1.9;
  white-space: nowrap;
  color: var(--ink-soft);
}

.hero__text:last-child { margin-bottom: 0; }

.hero__menu {
  margin-top: 36px;
  font-family: var(--font);
  font-weight: 600;
  font-size: 0.85rem;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--accent-ink);
  background: var(--accent);
  border: 0;
  border-radius: 999px;
  padding: 14px 40px;
  cursor: pointer;
  box-shadow: 0 10px 24px rgba(0, 0, 0, 0.15);
  transition: transform 160ms var(--ease-out), filter 160ms var(--ease-out);
}

.hero__menu:hover,
.hero__menu:focus-visible {
  transform: translateY(-1px);
  filter: brightness(1.05);
}

 /* Keyboard focus reads as a glow around the pill, not an outline ring. */
.hero__menu:focus-visible {
  outline: none;
  box-shadow:
    0 12px 30px rgba(0, 0, 0, 0.2),
    0 0 0 3px rgba(255, 255, 255, 0.85),
    0 0 26px 8px rgba(129, 237, 25, 0.5);
}

.hero__menu:active { transform: translateY(0); }

/* Narrow screens: single-line points are no longer possible, let them wrap */

@media (max-width: 700px) {
  .hero { padding-top: 92px; }
  .hero__card { padding: 84px 22px 36px; }
  .hero__avatar { width: 120px; height: 120px; top: -60px; }
  .hero__title { font-size: clamp(1.15rem, 5.4vw, 1.5rem); white-space: normal; text-wrap: balance; }
  .hero__text { font-size: 0.9rem; line-height: 1.8; white-space: normal; }
}

/* Short screens (small phones upright, any phone sideways): tighten the
   rhythm so the card and the VIEW button still land on one screen. */
@media (max-height: 620px) {
  .hero { padding: 14px 14px 16px; }
  .hero__card { padding: 52px 26px 22px; }
  .hero__avatar { width: 82px; height: 82px; top: -41px; border-width: 4px; }
  .hero__title { margin-bottom: 10px; font-size: clamp(1.05rem, 2.6vw, 1.45rem); line-height: 1.35; }
  .hero__text { margin-bottom: 4px; font-size: clamp(0.78rem, 1.15vw, 0.95rem); line-height: 1.55; }
  /* Still a thumb-sized target after the type shrinks. */
  .hero__menu {
    margin-top: 16px;
    padding: 11px 32px;
    min-height: 44px;
    box-sizing: border-box;
    font-size: 0.78rem;
  }
}

/* Narrowest phones: the points wrap the most here, so give them the width
   back and take it out of the padding. */
@media (max-width: 360px) {
  .hero { padding-left: 10px; padding-right: 10px; }
  .hero__card { padding-left: 16px; padding-right: 16px; }
  .hero__title { font-size: 1.12rem; }
  .hero__text { font-size: 0.83rem; line-height: 1.6; }
}

@media (prefers-reduced-motion: reduce) {
  .hero { translate: 0 0; transition: opacity 400ms var(--ease-inout); }
  .hero__menu { transition: none; }
}


/* ===========================================================================
   MENU — brain and the five nodes
   =========================================================================== */

/* ==========================================================================
   Menu page — line-art brain hub ringed by five nodes.
   Node positions live here; the connector lines are measured and drawn by
   menu.js, so the geometry stays correct at every breakpoint.
   ========================================================================== */

.menu {
  --menu-bg: #050505;
  --menu-ink: #ffffff;
  --menu-line: rgba(255, 255, 255, 0.28);
  --gh-ink: #181717;
  --li: #0a66c2;

  position: fixed;
  inset: 0;
  z-index: 10;
  display: grid;
  place-items: center;
  background: var(--menu-bg);
  overflow: hidden;
  opacity: 0;
  transition: opacity 500ms var(--ease-inout);
}

.menu[hidden] { display: none; }
.menu.is-open { opacity: 1; }

/* Stage: the composition scales as one unit. */
.menu__stage {
  position: relative;
  width: min(94vw, 980px);
  aspect-ratio: 10 / 7;
  max-height: 84dvh;
}

.menu__links {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  overflow: visible;
}

.menu__links line {
  stroke: var(--menu-line);
  stroke-width: 1;
  transition: stroke 200ms var(--ease-out);
}

.menu__links line.is-active { stroke: #fff; }

.menu.is-open .menu__links line {
  animation: menu-draw 700ms var(--ease-out) both;
  animation-delay: calc(160ms + var(--i, 0) * 80ms);
}

@keyframes menu-draw {
  from { stroke-dashoffset: var(--len, 400); }
  to   { stroke-dashoffset: 0; }
}

/* Brain --------------------------------------------------------------- */

.menu__brain {
  position: absolute;
  top: 50%;
  left: 50%;
  translate: -50% -50%;
  width: clamp(180px, 26vw, 300px);
  height: auto;
  filter: drop-shadow(0 0 18px rgba(255, 255, 255, 0.28));
  opacity: 0;
}

.menu.is-open .menu__brain {
  animation:
    menu-brain-in 700ms var(--ease-out) 60ms both,
    menu-breathe 6s var(--ease-inout) 800ms infinite;
}

@keyframes menu-brain-in {
  from { opacity: 0; scale: 0.92; }
  to   { opacity: 1; scale: 1; }
}

@keyframes menu-breathe {
  0%, 100% { opacity: 0.84; scale: 1; }
  50%      { opacity: 1; scale: 1.03; }
}

/* Nodes ---------------------------------------------------------------- */

.menu__node {
  position: absolute;
  translate: -50% -50%;
  color: var(--menu-ink);
  text-decoration: none;
  opacity: 0;
  /* The three in-page nodes are <button> (an href would make the browser print
     the page's file path in its status bar on hover), so strip the UA chrome. */
  margin: 0;
  border: 0;
  background: none;
  font: inherit;
  cursor: pointer;
}

.menu.is-open .menu__node {
  animation: menu-node-in 520ms var(--ease-out) both;
  animation-delay: calc(320ms + var(--i) * 70ms);
}

@keyframes menu-node-in {
  from { opacity: 0; translate: -50% calc(-50% + 10px); }
  to   { opacity: 1; translate: -50% -50%; }
}

/* Text nodes: bare uppercase words, like the reference. */
.menu__node:not(.menu__node--icon) {
  padding: 10px 8px;
  font-family: var(--font);
  font-weight: 600;
  font-size: clamp(0.72rem, 1.1vw, 0.9rem);
  letter-spacing: 0.22em;
  white-space: nowrap;
}

.menu__node:not(.menu__node--icon)::after {
  content: '';
  display: block;
  height: 1px;
  margin-top: 7px;
  background: currentColor;
  scale: 0 1;
  transition: scale 260ms var(--ease-out);
}

.menu__node:not(.menu__node--icon):hover::after,
.menu__node:not(.menu__node--icon):focus-visible::after { scale: 1 1; }

.menu__node:not(.menu__node--icon):active { scale: 0.96; }

/* Icon nodes: brand chips, no label. */
.menu__node--icon {
  display: grid;
  place-items: center;
  width: 48px;
  height: 48px;
  border-radius: 50%;
  box-shadow: 0 0 0 1px rgba(255, 255, 255, 0.14);
  transition: scale 200ms var(--ease-out), box-shadow 200ms var(--ease-out);
}

.menu__node--icon svg { width: 24px; height: 24px; }

.menu__node--github {
  background: #ffffff;
  color: var(--gh-ink);
}

.menu__node--github svg { fill: currentColor; }

.menu__node--linkedin {
  background: var(--li);
  color: #ffffff;
}

.menu__node--linkedin svg { fill: currentColor; }

.menu__node--icon:hover,
.menu__node--icon:focus-visible { scale: 1.08; }

.menu__node--github:hover,
.menu__node--github:focus-visible { box-shadow: 0 0 0 1px rgba(255, 255, 255, 0.5), 0 0 24px rgba(255, 255, 255, 0.35); }

.menu__node--linkedin:hover,
.menu__node--linkedin:focus-visible { box-shadow: 0 0 0 1px rgba(10, 102, 194, 0.7), 0 0 24px rgba(10, 102, 194, 0.55); }

.menu__node:focus-visible {
  outline: 2px solid #fff;
  outline-offset: 6px;
  border-radius: 4px;
}

.menu__node--icon:focus-visible { border-radius: 50%; }

/* Desktop ring around the brain. */
.menu__node--profile  { left: 18%; top: 20%; }
.menu__node--projects { left: 82%; top: 20%; }
.menu__node--moments  { left: 50%; top: 88%; }
.menu__node--github   { left: 10%; top: 64%; }
.menu__node--linkedin { left: 90%; top: 64%; }

/* Back --------------------------------------------------------------- */

.menu__close {
  position: absolute;
  top: 26px;
  left: 26px;
  display: grid;
  place-items: center;
  width: 44px;
  height: 44px;
  padding: 0;
  border: 0;
  background: transparent;
  color: rgba(255, 255, 255, 0.62);
  cursor: pointer;
  transition: color 220ms var(--ease-out), rotate 420ms var(--ease-out);
}

.menu__close svg { width: 26px; height: 26px; }

/* Return swirl: an arc that winds back on itself. */
.menu__close:hover,
.menu__close:focus-visible {
  color: #fff;
  rotate: -28deg;
}

.menu__close:focus-visible { outline: 2px solid #fff; outline-offset: 4px; border-radius: 50%; }

.menu__close:active { scale: 0.92; }

/* Phone: taller stage, nodes stack around a smaller brain. */

@media (max-width: 700px) {
  .menu__stage {
    width: 92vw;
    aspect-ratio: 3 / 4;
    max-height: 78dvh;
  }

  .menu__brain { width: clamp(140px, 40vw, 200px); }

  .menu__node--profile  { left: 50%; top: 5%; }
  .menu__node--projects { left: 21%; top: 21%; }
  .menu__node--moments  { left: 79%; top: 21%; }
  .menu__node--github   { left: 30%; top: 93%; }
  .menu__node--linkedin { left: 70%; top: 93%; }

  .menu__close { top: 16px; left: 16px; }
}

@media (prefers-reduced-motion: reduce) {
  .menu.is-open .menu__links line,
  .menu.is-open .menu__brain,
  .menu.is-open .menu__node { animation: none; }

  .menu__brain { opacity: 1; }
  .menu__node { opacity: 1; }
  .menu__links line { stroke-dashoffset: 0 !important; }
}


/* ===========================================================================
   PROFILE — about me and the stack marquee
   =========================================================================== */

/* ==========================================================================
   Profile page — a cream "About Me" card floating on near-black, above the
   "The Stack I Master" marquee. Capsule proportions follow the existing
   "Tools I Use" component in the nitinsukthe site: 58px capsule, 40px chips.
   ========================================================================== */

.profile {
  --cream: #f1eee7;
  --olive: #837c36;
  --ink: #14130f;
  --ink-soft: #3a382f;
  --surround: #f1eee7;   /* one flat cream surface across the whole page */
  --font-display: 'Anton', 'Arial Narrow', sans-serif;
  --font-mono: 'JetBrains Mono', ui-monospace, 'Cascadia Mono', monospace;

  /* The cut-out figure: how tall it stands, where it crosses the title, and
     the lane the copy is inset by so it sits to the figure's right. */
  --figure-h: clamp(320px, 40vw, 580px);
  --figure-top: clamp(84px, 9.5vw, 150px);
  /* The copy block's floor. Smaller than the figure on purpose: the signature
     sits to the right, so the figure may run past it without colliding. */
  --grid-min: clamp(190px, 21vw, 310px);
  --figure-lane: clamp(190px, 26%, 340px);

  position: fixed;
  inset: 0;
  z-index: 20;
  overflow-x: hidden;
  overflow-y: auto;
  background: var(--surround);
  opacity: 0;
  transition: opacity 320ms var(--ease-inout);
}

.profile[hidden] { display: none; }
.profile.is-open { opacity: 1; }

/* Curtain that sweeps up over the menu on entry.
   Uses `transform` (not the `translate` property) because GSAP animates
   transform — the two would otherwise compose and leave it covering the page.
   Hidden unless the timeline is running, so a missing GSAP can never strand a
   cream panel over the content. */
.profile__curtain {
  position: fixed;
  inset: 0;
  z-index: 3;
  background: var(--cream);
  visibility: hidden;
  /* No CSS transform here: GSAP treats an existing translate as its baseline
     and adds yPercent on top, which offset the whole sweep by a viewport. The
     curtain is invisible unless the timeline is running, so it needs no
     resting transform of its own. */
  pointer-events: none;
}

.profile.is-animating .profile__curtain { visibility: visible; }

/* About ---------------------------------------------------------------- */

.about {
  display: grid;
  place-items: center;
  box-sizing: border-box;
  padding: clamp(6px, 1vw, 14px) clamp(14px, 2.6vw, 38px) 0;
  background: var(--surround);
}

/* No card framing — the content sits directly on the page. The display type is
   still wider than the column and is clipped by this block's edges. */
.about__card {
  position: relative;
  width: min(100%, 1120px);
  box-sizing: border-box;
  padding: clamp(10px, 1.4vw, 20px) clamp(22px, 3.4vw, 54px) 0;
  color: var(--ink);
  overflow: hidden;
  /* overflow:hidden clips the title's bleed, so the card itself has to be tall
     enough to contain the figure or his feet get cut off. */
  min-height: calc(var(--figure-top) + var(--figure-h) + 16px);
}

.about__eyebrow {
  margin: 0 0 clamp(16px, 2.2vw, 30px);
  font-family: var(--font-mono);
  font-weight: 500;
  font-size: clamp(0.54rem, 0.82vw, 0.7rem);
  letter-spacing: 0.34em;
  text-align: center;
  color: var(--olive);
}

.about__title {
  margin: 0 0 clamp(10px, 1.6vw, 24px);
  font-family: var(--font-display);
  font-weight: 400;
  font-size: clamp(3.8rem, 22vw, 18rem);
  line-height: 0.78;
  letter-spacing: -0.01em;
  text-align: center;
  white-space: nowrap;
  color: var(--olive);
}

/* Single copy column, inset so it clears the figure standing to its left. */
.about__grid {
  display: grid;
  grid-template-columns: 1fr;
  padding-left: var(--figure-lane);
  /* Floor for the copy column; the signature follows right after it. */
  min-height: var(--grid-min);
}

/* A cut-out, not a framed photo: it stands on the page and overlaps the lower
   part of the title, which reads behind it. */
.about__photo {
  position: absolute;
  left: clamp(14px, 5vw, 96px);
  /* Top-anchored so his head reliably crosses the lower part of the title;
     his height then carries him down to the foot of the copy block. */
  top: var(--figure-top);
  z-index: 1;
  margin: 0;
  pointer-events: none;
}

.about__photo img {
  display: block;
  height: var(--figure-h);
  width: auto;
  /* Grounds the figure instead of leaving it floating on the cream. */
  filter: drop-shadow(0 18px 20px rgba(74, 52, 35, 0.18));
}

.about__copy {
  max-width: 68ch;
  padding-bottom: clamp(10px, 1.4vw, 18px);
  font-family: var(--font);
  font-weight: 400;
  font-size: clamp(0.84rem, 1.06vw, 1rem);
  line-height: 1.85;
  letter-spacing: 0.005em;
  text-align: justify;
  color: var(--ink-soft);
}

.about__copy p { margin: 0 0 1.25em; }
.about__copy p:last-child { margin-bottom: 0; }

.about__copy strong {
  font-weight: 500;
  color: var(--ink);
}

/* Pill sits on its own under the marquee. */
.about__cta {
  position: relative;
  z-index: 2;
  margin-top: clamp(24px, 3.2vw, 40px);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  /* Thumb-sized: the pill was 34px tall, under the 44px touch guideline. */
  min-height: 44px;
  box-sizing: border-box;
  padding: 9px 22px;
  border: 1px solid var(--olive);
  border-radius: 999px;
  background: var(--olive);
  font-family: var(--font);
  font-weight: 600;
  font-size: clamp(0.66rem, 0.8vw, 0.76rem);
  letter-spacing: 0.08em;
  color: var(--cream);
  text-decoration: none;
  transition: background 200ms var(--ease-out), transform 200ms var(--ease-out);
}

.about__cta:hover,
.about__cta:focus-visible {
  background: #6f6a2e;
  transform: translateY(-1px);
}

.about__cta:active { transform: translateY(0); }

.about__cta:focus-visible { outline: 2px solid var(--olive); outline-offset: 4px; }

@media (prefers-reduced-motion: reduce) {
  .about__cta { transition: none; }
  .about__cta:hover,
  .about__cta:focus-visible { transform: none; }
}

/* In flow, not absolute: floating it over the column made it collide with the
   last line of the paragraph at some widths. */
.about__sign {
  /* Lives inside the copy column, so it signs off flush with the text's right
     edge. (A max-width in `ch` here would not match — `ch` resolves against
     this element's own script face, not the body font.) */
  display: flex;
  justify-content: flex-end;
  align-items: center;
  gap: 12px;
  margin: clamp(-6px, -0.4vw, 0px) 0 clamp(10px, 1.6vw, 20px);
  font-family: 'Playwrite FR Moderne', cursive;
  font-weight: 300;
  font-size: clamp(0.85rem, 1.15vw, 1.15rem);
  color: var(--ink);
  pointer-events: none;
}

.about__sign svg { width: clamp(17px, 1.5vw, 21px); height: auto; }

/* Stack ---------------------------------------------------------------- */

.stack {
  position: relative;
  box-sizing: border-box;
  display: grid;
  /* minmax(0, 1fr) pins the column to the container: an auto column would size
     itself to the marquee strip's max-content and shove the capsule off-screen. */
  grid-template-columns: minmax(0, 1fr);
  justify-items: center;
  padding: clamp(10px, 1.6vw, 24px) 0 clamp(36px, 4.5vw, 64px);
  background: var(--surround);
  overflow: hidden;
}

.stack__label {
  position: relative;
  margin: 0 0 clamp(14px, 1.8vw, 24px);
  font-family: 'Courier New', ui-monospace, monospace;
  font-weight: 900;
  font-size: clamp(0.95rem, 1.7vw, 1.45rem);
  letter-spacing: 0.24em;
  text-transform: uppercase;
  text-align: center;
  color: var(--olive);
}

/* Explicit width: without it the grid parent shrink-wraps this to the strip's
   max-content (thousands of px), which throws the capsule far off-centre. */
.stack__rows {
  position: relative;
  width: 100%;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 12px;
}

/* No tray: the chips run straight across the page. Percentage width (not vw)
   so a scrollbar can never push the track off-screen. */
.stack__row {
  position: relative;
  width: min(94%, 1100px);
  height: 56px;
  box-sizing: border-box;
  display: flex;
  align-items: center;
}

.stack__strip {
  display: flex;
  align-items: center;
  gap: 9px;
  width: max-content;
  flex-shrink: 0;
  will-change: transform;
  /* The fade rides on the chips, not the capsule, so the tray keeps its crisp
     border while the icons dissolve at each end. */
  -webkit-mask-image: linear-gradient(90deg, transparent 0, #000 6%, #000 94%, transparent 100%);
  mask-image: linear-gradient(90deg, transparent 0, #000 6%, #000 94%, transparent 100%);
  -webkit-mask-size: var(--mask-w, 100%) 100%;
  mask-size: var(--mask-w, 100%) 100%;
  -webkit-mask-repeat: no-repeat;
  mask-repeat: no-repeat;
}

.stack__tile {
  position: relative;
  flex: 0 0 40px;
  width: 40px;
  height: 40px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border: 1px solid rgba(131, 124, 54, 0.22);
  border-radius: 11px;
  background: #ffffff;
  box-shadow: 0 6px 14px rgba(20, 19, 15, 0.12);
  transition: transform 220ms var(--ease-out), box-shadow 220ms var(--ease-out),
              border-color 220ms var(--ease-out);
}

.stack__tile img {
  display: block;
  width: 27px;
  height: 27px;
  object-fit: contain;
}

/* Technologies with no real logo get a typographic chip, never a fake mark. */
.stack__tile--text {
  padding: 3px;
  font-family: 'Courier New', ui-monospace, monospace;
  font-weight: 900;
  font-size: 9px;
  letter-spacing: 0.2px;
  line-height: 1.12;
  text-align: center;
  text-transform: uppercase;
  color: #111;
  word-break: break-word;
}

.stack__tile:hover,
.stack__tile:focus-visible {
  transform: translateY(-2px) scale(1.06);
  border-color: rgba(131, 124, 54, 0.6);
  box-shadow: 0 12px 24px rgba(20, 19, 15, 0.2);
  z-index: 2;
}

.stack__tip {
  position: absolute;
  bottom: calc(100% + 8px);
  left: 50%;
  translate: -50% 0;
  padding: 4px 9px;
  border-radius: 6px;
  background: rgba(20, 19, 15, 0.95);
  border: 1px solid rgba(255, 255, 255, 0.14);
  z-index: 4;
  font-family: var(--font);
  font-weight: 600;
  font-size: 0.55rem;
  letter-spacing: 0.05em;
  text-transform: none;
  white-space: nowrap;
  color: #fff;
  opacity: 0;
  pointer-events: none;
  transition: opacity 180ms var(--ease-out);
}

.stack__tile:focus-visible .stack__tip,
.stack__tile.is-named .stack__tip { opacity: 1; }

.stack__tile.is-named {
  transform: translateY(-2px) scale(1.06);
  border-color: rgba(131, 124, 54, 0.6);
  box-shadow: 0 12px 24px rgba(20, 19, 15, 0.2);
  z-index: 3;
}

/* Back ----------------------------------------------------------------- */

/* Back (to the menu) at top-left, Home (to the hero) at top-right. */
.profile__back,
.profile__home {
  position: fixed;
  top: 20px;
  z-index: 4;
  display: grid;
  place-items: center;
  width: 44px;
  height: 44px;
  padding: 0;
  border: 0;
  background: transparent;
  color: rgba(20, 19, 15, 0.5);
  cursor: pointer;
  transition: color 220ms var(--ease-out), rotate 420ms var(--ease-out),
              translate 260ms var(--ease-out);
}

.profile__back { left: 20px; }
.profile__home { right: 20px; }

.profile__back svg,
.profile__home svg { width: 25px; height: 25px; }

.profile__back:hover,
.profile__back:focus-visible { color: var(--ink); rotate: -28deg; }

.profile__home:hover,
.profile__home:focus-visible { color: var(--ink); rotate: 90deg; }

.profile__back:focus-visible,
.profile__home:focus-visible { outline: 2px solid var(--olive); outline-offset: 4px; border-radius: 50%; }

.profile__back:active,
.profile__home:active { scale: 0.92; }

@media (prefers-reduced-motion: reduce) {
  .profile__back:hover, .profile__back:focus-visible { rotate: none; }
  .profile__home:hover, .profile__home:focus-visible { rotate: none; }
}

/* Responsive ----------------------------------------------------------- */

@media (max-width: 900px) {
  /* The figure keeps standing to the left of the copy exactly as it does on
     desktop; only the proportions come down so the text column stays usable. */
  .profile {
    /* The cut-out is a narrow standing figure (277x1100), so its width is only
       a quarter of its height; the lane is sized to that width plus a gap. */
    --figure-h: clamp(264px, 85vw, 430px);
    --figure-top: clamp(50px, 10vw, 96px);
    --figure-lane: clamp(94px, 27vw, 215px);
    --grid-min: 0px;
  }
  .about__photo { left: clamp(6px, 2vw, 40px); }
  /* Justified text opens rivers once the column is this narrow. */
  .about__copy { text-align: left; padding-bottom: clamp(44px, 9vw, 70px); }
}

@media (max-width: 700px) {
  .stack__row { width: 94%; height: 50px; }
  .stack__strip { gap: 8px; }
  .stack__tile { flex-basis: 36px; width: 36px; height: 36px; border-radius: 10px; }
  .stack__tile img { width: 24px; height: 24px; }
  .stack__tile--text { font-size: 8px; }
  .stack__label { font-size: 0.85rem; letter-spacing: 0.18em; }
  .profile__back { top: 12px; left: 12px; }
  .profile__home { top: 12px; right: 12px; }
}

/* Motion --------------------------------------------------------------- */

/* Only hidden while the entrance timeline is actually going to run. */
.profile.is-animating .about__eyebrow,
.profile.is-animating .about__title,
.profile.is-animating .about__photo,
.profile.is-animating .about__copy > * { opacity: 0; }

@media (prefers-reduced-motion: reduce) {
  .profile__curtain { display: none; }
  .stack__tile { transition: none; }
  .stack__tile:hover,
  .stack__tile:focus-visible { transform: none; }

  .profile.is-animating .about__eyebrow,
  .profile.is-animating .about__title,
  .profile.is-animating .about__copy > * { opacity: 1; }
}


/* ===========================================================================
   PROJECTS — folder tabs and case studies
   =========================================================================== */

/* ==========================================================================
   Projects page — overlapping folder tabs that open into full case studies.
   Deep-green field with manila and mint folders, sampled from the reference
   (tokens are scoped per page, so they are declared here rather than inherited).
   ========================================================================== */

.projects {
  /* Sampled from the folder reference: deep green field, manila and mint tabs. */
  --deep: #01362e;
  --deep-lift: #073f36;
  --manila: #c9c2ba;
  --mint: #8fb294;
  --paper: #e6e1d8;
  --paper-soft: #bfc6bb;
  --ink: #01362e;
  --font-display: 'Anton', 'Arial Narrow', sans-serif;
  --font-mono: 'JetBrains Mono', ui-monospace, 'Cascadia Mono', monospace;

  position: fixed;
  inset: 0;
  z-index: 24;
  overflow-x: hidden;
  overflow-y: auto;
  background: var(--deep);
  color: var(--paper);
  opacity: 0;
  transition: opacity 320ms var(--ease-inout);
}

.projects[hidden] { display: none; }
.projects.is-open { opacity: 1; }

.projects__curtain {
  position: fixed;
  inset: 0;
  z-index: 3;
  background: var(--mint);
  visibility: hidden;
  /* No CSS transform here: GSAP treats an existing translate as its baseline
     and adds yPercent on top, which offset the whole sweep by a viewport. The
     curtain is invisible unless the timeline is running, so it needs no
     resting transform of its own. */
  pointer-events: none;
}

.projects.is-animating .projects__curtain { visibility: visible; }

/* Index ---------------------------------------------------------------- */

.projects__index {
  box-sizing: border-box;
  min-height: 100dvh;
  padding: clamp(46px, 6vw, 84px) clamp(20px, 4vw, 60px) clamp(40px, 5vw, 70px);
  display: flex;
  flex-direction: column;
  align-items: center;
}

.projects__index[hidden] { display: none; }

.projects__eyebrow {
  margin: 0 0 clamp(14px, 2vw, 24px);
  font-family: var(--font-mono);
  font-weight: 500;
  font-size: clamp(0.54rem, 0.82vw, 0.7rem);
  letter-spacing: 0.34em;
  color: var(--mint);
}

.projects__title {
  margin: 0 0 clamp(30px, 4.5vw, 58px);
  font-family: var(--font-display);
  font-weight: 400;
  font-size: clamp(3.2rem, 13vw, 10rem);
  line-height: 0.82;
  letter-spacing: -0.005em;
  color: var(--paper);
}

/* Folder cascade ------------------------------------------------------- */

.projects__stack {
  --step: 94px;    /* vertical drop between folders; also the visible strip height */
  --body: 200px;   /* full height of the front-most folder */
  --tab-w: 250px;  /* horizontal step between tabs */

  position: relative;
  width: min(100%, 820px);
  /* Tab of the first folder sits 30px above the stack, hence the top padding. */
  padding-top: 31px;
  height: calc(var(--step) * 2 + var(--body));
}

/* Full-width sheets dropping down the stack; each sits in front of the last, so
   only a strip of the ones behind shows — their tabs are staggered sideways to
   stay visible, as in the reference. */
.folder {
  position: absolute;
  left: 0;
  right: 0;
  top: calc(31px + var(--i) * var(--step));
  z-index: calc(10 + var(--i));

  /* Flex column, not block: a <button> centres its content vertically by
     default, which pushed the label down behind the next folder. */
  display: flex;
  flex-direction: column;
  justify-content: flex-start;
  align-items: stretch;
  padding: 0;
  height: var(--body);
  border: 0;
  border-radius: 18px 18px 0 0;
  background: var(--sheet, var(--manila));
  box-shadow: 0 -1px 0 rgba(255, 255, 255, 0.25) inset,
              0 -16px 30px rgba(0, 0, 0, 0.3);
  cursor: pointer;
  text-align: left;
  transition: transform 260ms var(--ease-out), box-shadow 260ms var(--ease-out);
}

/* The raised tab at the folder's top-right, as in the reference. */
.folder__tab {
  position: absolute;
  top: -30px;
  right: calc((2 - var(--i)) * var(--tab-w) + 16px);
  display: inline-flex;
  align-items: center;
  height: 31px;
  padding: 0 clamp(14px, 2.2vw, 26px);
  border: 0;
  border-radius: 14px 14px 0 0;
  background: inherit;
  font-family: var(--font);
  font-weight: 600;
  font-size: clamp(0.6rem, 0.85vw, 0.74rem);
  letter-spacing: 0.18em;
  text-transform: uppercase;
  white-space: nowrap;
  color: var(--ink);
}

.folder__meta {
  display: block;
  /* Kept inside the strip that stays visible above the next folder. */
  padding: 20px clamp(18px, 2.6vw, 30px) 0;
  font-family: var(--font-mono);
  font-weight: 300;
  font-size: clamp(0.62rem, 0.84vw, 0.76rem);
  line-height: 1.7;
  color: rgba(1, 54, 46, 0.78);
}

.folder:hover,
.folder:focus-visible { transform: translateY(-10px); box-shadow: 0 -20px 38px rgba(0, 0, 0, 0.4); }
.folder:focus-visible { outline: 2px solid var(--sheet, var(--mint)); outline-offset: 3px; }
.folder:active { transform: translateY(-4px); }

.projects__hint {
  margin: clamp(26px, 3.4vw, 44px) 0 0;
  font-family: var(--font-mono);
  font-weight: 300;
  font-size: clamp(0.6rem, 0.8vw, 0.72rem);
  letter-spacing: 0.06em;
  color: rgba(191, 198, 187, 0.75);
}

/* Case study ----------------------------------------------------------- */

.case {
  box-sizing: border-box;
  min-height: 100dvh;
  padding: clamp(52px, 6vw, 88px) clamp(20px, 5vw, 76px) clamp(56px, 7vw, 100px);
  background: var(--deep);
}

.case[hidden] { display: none; }

.case__inner { max-width: 1240px; margin: 0 auto; }

/* Reading column beside a sticky fact sheet. Below 1080px it collapses to one
   column and the fact sheet simply follows the text. */
.case__cols {
  display: grid;
  grid-template-columns: minmax(0, 1fr) 300px;
  gap: clamp(30px, 4vw, 64px);
  /* Stretch, not start: a shrink-wrapped column leaves the sticky panel no
     room to travel, so it would scroll away with the text. */
  align-items: stretch;
}

.case__aside { min-width: 0; }

.case__sticky {
  position: sticky;
  top: clamp(24px, 4vw, 48px);
  padding: clamp(18px, 2vw, 26px) clamp(18px, 2vw, 24px) clamp(20px, 2.2vw, 26px);
  border: 1px solid color-mix(in srgb, var(--mint) 30%, transparent);
  border-radius: 16px;
  background: var(--deep-lift);
}

/* Outranks the generic `.case h3` spacing, which is declared later. */
.case .case__sticky h3 { margin-top: clamp(20px, 2.4vw, 28px); margin-bottom: 12px; }
.case .case__sticky h3:first-child { margin-top: 0; }
.case .case__sticky .case__repo { margin-top: clamp(18px, 2.2vw, 26px); }
.case .case__sticky .case__chip { font-size: clamp(0.54rem, 0.7vw, 0.64rem); padding: 5px 11px; }

/* At-a-glance list */
.case__facts {
  margin: 0;
  display: grid;
  grid-template-columns: auto 1fr;
  gap: 7px 14px;
  font-family: var(--font-mono);
  font-size: clamp(0.56rem, 0.74vw, 0.68rem);
  line-height: 1.6;
}

.case__facts dt {
  color: var(--mint);
  letter-spacing: 0.04em;
  white-space: nowrap;
}

.case__facts dd {
  margin: 0;
  color: var(--paper-soft);
}

.case__kicker {
  margin: 0 0 10px;
  font-family: var(--font-mono);
  font-weight: 500;
  font-size: clamp(0.54rem, 0.8vw, 0.68rem);
  letter-spacing: 0.32em;
  color: var(--mint);
}

.case__title {
  margin: 0 0 clamp(12px, 1.6vw, 20px);
  font-family: var(--font-display);
  font-weight: 400;
  font-size: clamp(2.4rem, 7.5vw, 5.5rem);
  line-height: 0.88;
  color: var(--paper);
}

.case__lede {
  margin: 0 0 clamp(28px, 3.4vw, 46px);
  max-width: 68ch;
  font-family: var(--font);
  font-weight: 400;
  font-size: clamp(0.92rem, 1.2vw, 1.15rem);
  line-height: 1.7;
  color: var(--paper);
}

.case__body p {
  margin: 0 0 1.15em;
  max-width: 72ch;
  font-family: var(--font);
  font-weight: 400;
  font-size: clamp(0.82rem, 1.02vw, 0.96rem);
  line-height: 1.85;
  color: var(--paper-soft);
}

.case h3 {
  margin: clamp(32px, 4vw, 52px) 0 clamp(12px, 1.6vw, 18px);
  font-family: var(--font);
  font-weight: 700;
  font-size: clamp(0.66rem, 0.85vw, 0.78rem);
  letter-spacing: 0.24em;
  text-transform: uppercase;
  color: var(--mint);
}

/* Tech chips */
.case__chips { display: flex; flex-wrap: wrap; gap: 8px; }

.case__chip {
  padding: 6px 13px;
  border: 1px solid color-mix(in srgb, var(--mint) 40%, transparent);
  border-radius: 999px;
  background: rgba(255, 255, 255, 0.05);
  font-family: var(--font-mono);
  font-weight: 400;
  font-size: clamp(0.58rem, 0.76vw, 0.7rem);
  letter-spacing: 0.02em;
  color: var(--paper-soft);
}

/* Architecture list */
.case__flow { margin: 0; padding: 0; list-style: none; counter-reset: step; }

.case__flow li {
  position: relative;
  counter-increment: step;
  padding: 0 0 0 40px;
  margin-bottom: 0.9em;
  max-width: 72ch;
  font-family: var(--font);
  font-weight: 400;
  font-size: clamp(0.82rem, 1.02vw, 0.95rem);
  line-height: 1.75;
  color: var(--paper-soft);
}

.case__flow li::before {
  content: counter(step, decimal-leading-zero);
  position: absolute;
  left: 0;
  top: 0.15em;
  font-family: var(--font-mono);
  font-size: 0.72em;
  letter-spacing: 0.06em;
  color: var(--mint);
}

/* Diagram */
.case__diagram {
  margin: clamp(10px, 1.4vw, 18px) 0 0;
  padding: clamp(16px, 2.4vw, 28px);
  border: 1px solid color-mix(in srgb, var(--mint) 30%, transparent);
  border-radius: 16px;
  background: var(--deep-lift);
  overflow-x: auto;
  color: var(--mint);
}

.case__diagram svg { display: block; width: 100%; height: auto; min-width: 560px; }

/* Screenshots */
.case__shots {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
  gap: clamp(16px, 2.2vw, 28px);
}

.case__shot { margin: 0; }

.case__shot img {
  display: block;
  width: 100%;
  height: auto;
  border: 1px solid color-mix(in srgb, var(--mint) 25%, transparent);
  border-radius: 12px;
  background: var(--deep-lift);
}

.case__shot figcaption {
  margin-top: 9px;
  font-family: var(--font-mono);
  font-weight: 300;
  font-size: clamp(0.58rem, 0.76vw, 0.7rem);
  line-height: 1.6;
  color: var(--paper-soft);
}

/* The phone capture is far taller than the desktop ones; cap it so it doesn't
   stretch its grid row. */
.case__shot--tall img { max-height: 560px; width: auto; }

/* Metrics */
.case__metrics {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(170px, 1fr));
  gap: clamp(12px, 1.8vw, 22px);
}

.case__metric {
  padding: clamp(14px, 1.8vw, 20px) clamp(14px, 1.8vw, 20px) clamp(12px, 1.6vw, 18px);
  border: 1px solid color-mix(in srgb, var(--mint) 30%, transparent);
  border-radius: 14px;
  background: var(--deep-lift);
}

.case__metric b {
  display: block;
  font-family: var(--font-display);
  font-weight: 400;
  font-size: clamp(1.5rem, 2.6vw, 2.2rem);
  line-height: 1;
  letter-spacing: 0.01em;
  color: var(--mint);
}

.case__metric span {
  display: block;
  margin-top: 8px;
  font-family: var(--font-mono);
  font-weight: 300;
  font-size: clamp(0.56rem, 0.74vw, 0.68rem);
  line-height: 1.55;
  color: var(--paper-soft);
}

/* An honest note about where a number came from. */
.case__caveat {
  margin: clamp(12px, 1.6vw, 18px) 0 0;
  max-width: 72ch;
  padding-left: 13px;
  border-left: 2px solid color-mix(in srgb, var(--mint) 45%, transparent);
  font-family: var(--font-mono);
  font-weight: 300;
  font-size: clamp(0.58rem, 0.76vw, 0.7rem);
  line-height: 1.75;
  color: var(--paper-soft);
}

.case__repo {
  display: inline-flex;
  align-items: center;
  margin-top: clamp(28px, 3.4vw, 44px);
  justify-content: center;
  min-height: 44px;
  box-sizing: border-box;
  padding: 9px 22px;
  border: 1px solid var(--mint);
  border-radius: 999px;
  background: var(--mint);
  font-family: var(--font);
  font-weight: 600;
  font-size: clamp(0.66rem, 0.8vw, 0.76rem);
  letter-spacing: 0.08em;
  color: var(--deep);
  text-decoration: none;
  transition: filter 200ms var(--ease-out), transform 200ms var(--ease-out);
}

.case__repo:hover,
.case__repo:focus-visible { filter: brightness(1.1); transform: translateY(-1px); }
.case__repo:focus-visible { outline: 2px solid var(--mint); outline-offset: 4px; }

/* Controls ------------------------------------------------------------- */

.projects__back,
.projects__home {
  position: fixed;
  top: 20px;
  z-index: 4;
  display: grid;
  place-items: center;
  width: 44px;
  height: 44px;
  padding: 0;
  border: 0;
  background: transparent;
  color: rgba(230, 225, 216, 0.6);
  cursor: pointer;
  transition: color 220ms var(--ease-out), rotate 420ms var(--ease-out);
}

.projects__back { left: 20px; }
.projects__home { right: 20px; }

.projects__back svg,
.projects__home svg { width: 25px; height: 25px; }

.projects__back:hover,
.projects__back:focus-visible { color: var(--paper); rotate: -28deg; }

.projects__home:hover,
.projects__home:focus-visible { color: var(--paper); rotate: 90deg; }

.projects__back:focus-visible,
.projects__home:focus-visible { outline: 2px solid var(--mint); outline-offset: 4px; border-radius: 50%; }

.projects__back:active,
.projects__home:active { scale: 0.92; }

/* Responsive ----------------------------------------------------------- */

@media (max-width: 900px) {
  .projects__stack { --tab-w: 30%; }
}

@media (max-width: 1080px) {
  .case__cols { grid-template-columns: minmax(0, 1fr); }
  .case__sticky { position: static; }
  .case__facts { max-width: 520px; }
}

@media (max-width: 760px) {
  .projects__stack { --step: 92px; --body: 170px; --tab-w: 0px; }
  /* No room to stagger sideways, so tabs sit left and the strip carries the label. */
  .folder__tab { right: auto; left: 14px; font-size: 0.56rem; letter-spacing: 0.1em; padding: 0 12px; }
  .folder__meta { font-size: 0.6rem; }
  .case__shots { grid-template-columns: 1fr; }
  .case__shot--tall img { max-height: none; width: 100%; max-width: 330px; }
  .case__diagram svg { min-width: 460px; }
  .projects__back { top: 12px; left: 12px; }
  .projects__home { top: 12px; right: 12px; }
}

/* Motion --------------------------------------------------------------- */

.projects.is-animating .projects__eyebrow,
.projects.is-animating .projects__title,
.projects.is-animating .folder,
.projects.is-animating .projects__hint { opacity: 0; }

@media (prefers-reduced-motion: reduce) {
  .projects__curtain { display: none; }
  .folder { transition: none; }
  .folder:hover,
  .folder:focus-visible { transform: none; }
  .case__repo { transition: none; }
  .projects__back:hover, .projects__back:focus-visible,
  .projects__home:hover, .projects__home:focus-visible { rotate: none; }

  .projects.is-animating .projects__eyebrow,
  .projects.is-animating .projects__title,
  .projects.is-animating .folder,
  .projects.is-animating .projects__hint { opacity: 1; }
}


/* ===========================================================================
   MOMENTS — ribbon, polaroids, lightbox
   =========================================================================== */

/* ==========================================================================
   Moments — the bow-and-banner wordmark over a polaroid reel, the photos
   strung along a red thread. Palette from the ribbon: cream #dbcab8,
   bow brown #62422b, with a thread red taken from the collage reference.
   ========================================================================== */

.moments {
  --paper: #faf5ee;
  --band: #dbcab8;
  --bow: #62422b;
  --ink: #4a3423;
  --ink-soft: #7d654f;
  --thread: #b53a30;
  --frame: #fffdfa;
  --font-script: 'Playwrite FR Moderne', 'Playwrite France Moderne', cursive;

  position: fixed;
  inset: 0;
  z-index: 26;
  overflow-x: hidden;
  overflow-y: auto;
  background:
    radial-gradient(ellipse 70% 45% at 50% 18%, #fffdfa 0%, var(--paper) 55%, #f2ebe1 100%);
  color: var(--ink);
  opacity: 0;
  transition: opacity 320ms var(--ease-inout);
}

/* A faint paper grain, so the cream is not a flat wash behind the photos. */
.moments::before {
  content: '';
  position: fixed;
  inset: 0;
  pointer-events: none;
  opacity: 0.5;
  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'/%3E%3CfeColorMatrix type='saturate' values='0'/%3E%3C/filter%3E%3Crect width='180' height='180' filter='url(%23n)' opacity='0.05'/%3E%3C/svg%3E");
}

.moments[hidden] { display: none; }
.moments.is-open { opacity: 1; }

.moments__curtain {
  position: fixed;
  inset: 0;
  z-index: 3;
  background: var(--band);
  visibility: hidden;
  pointer-events: none;
  /* No CSS transform here: GSAP treats an existing translate as its baseline
     and adds yPercent on top, which offset the whole sweep by a viewport. The
     curtain is invisible unless the timeline is running, so it needs no
     resting transform of its own. */
}

.moments.is-animating .moments__curtain { visibility: visible; }

/* Ribbon header --------------------------------------------------------- */

.moments__stage {
  position: relative;
  box-sizing: border-box;
  width: min(94vw, 1100px);
  margin: 0 auto;
  padding: clamp(46px, 6vw, 84px) 0 clamp(60px, 8vw, 110px);
}

.ribbon {
  position: relative;
  margin: 0 auto clamp(36px, 4.6vw, 64px);
  width: min(100%, 720px);
  aspect-ratio: 555 / 164;
}

.ribbon__img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: contain;
  filter: drop-shadow(0 12px 24px rgba(74, 52, 35, 0.14));
}

/* Sits on the banner, clear of the bow on the left. */
.ribbon__name {
  position: absolute;
  top: 50%;
  left: 58%;
  translate: -50% -50%;
  width: 62%;
  font-family: var(--font-script);
  font-weight: 400;
  font-size: clamp(1.15rem, 3.4vw, 2.3rem);
  line-height: 1.1;
  text-align: center;
  white-space: nowrap;
  color: var(--bow);
}

/* Reel ------------------------------------------------------------------ */

/* Narrower than the header, so the zigzag reads as a weave rather than
   photos marooned on either side of an empty column. */
.reel {
  position: relative;
  width: min(100%, 1020px);
  margin: 0 auto;
}

/* The thread is drawn behind the photos, its path measured by moments.js. */
.reel__thread {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  overflow: visible;
  pointer-events: none;
  z-index: 0;
}

.reel__thread path {
  fill: none;
  stroke: var(--thread);
  stroke-width: 1.9;
  stroke-linecap: round;
  opacity: 0.9;
}

/* Little bows sit where the thread knots, echoing the banner's ribbon. */
.reel__thread .knot {
  fill: none;
  stroke: var(--thread);
  stroke-width: 1.7;
  stroke-linejoin: round;
}

/* Two staggered columns on a wide screen, so the wall fills the page instead
   of running 3000px down it. The thread is measured, so it follows either way. */
.reel__items {
  position: relative;
  z-index: 1;
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: clamp(34px, 3.8vw, 54px) clamp(30px, 4.4vw, 66px);
  align-items: start;
  justify-items: center;
}

/* Right-hand column hangs lower, so the rows interlock. */
.reel__items > :nth-child(even) { margin-top: clamp(40px, 6vw, 86px); }

/* Seven photos leave the last row half empty, so the closing shot spans the
   width and centres — it reads as the finale rather than a gap. */
.reel__items > :last-child:nth-child(odd) {
  grid-column: 1 / -1;
  justify-self: center;
  margin-top: clamp(10px, 2vw, 24px);
}

/* Photos zigzag down the page, alternating side and tilt. Each print is the
   same height and takes its width from the photo, so a portrait and a
   landscape hang on the same line instead of one towering over the other. */
.polaroid {
  --print: clamp(200px, 23vw, 296px);

  position: relative;
  margin: 0;
  width: max-content;
  max-width: 100%;
  padding: 13px 13px 0;
  background: var(--frame);
  border-radius: 2px;
  box-shadow: 0 14px 30px rgba(74, 52, 35, 0.18), 0 2px 5px rgba(74, 52, 35, 0.1);
  cursor: pointer;
  rotate: var(--tilt, 0deg);
  transition: rotate 320ms var(--ease-out), scale 320ms var(--ease-out),
              box-shadow 320ms var(--ease-out);
}

.polaroid:nth-child(4n + 1) { --tilt: -3.5deg; }
.polaroid:nth-child(4n + 2) { --tilt: 2.5deg; }
.polaroid:nth-child(4n + 3) { --tilt: 1.8deg; }
.polaroid:nth-child(4n + 4) { --tilt: -2.4deg; }

.polaroid img {
  display: block;
  height: var(--print);
  width: auto;
  max-width: min(78vw, 400px);
  background: #e9e2d7;
}

.polaroid figcaption {
  /* width:0 + min-width:100% keeps the caption from widening the frame — with
     the frame sized to its content, a long caption would otherwise stretch it
     far wider than the photo. */
  width: 0;
  min-width: 100%;
  box-sizing: border-box;
  /* A floor on the height so long and short captions still end level. */
  display: grid;
  align-content: center;
  min-height: 4.4em;
  padding: 12px 6px 15px;
  font-family: var(--font-script);
  font-weight: 300;
  font-size: clamp(0.66rem, 1.05vw, 0.84rem);
  line-height: 1.45;
  text-align: center;
  text-wrap: balance;
  color: var(--bow);
}

.polaroid:hover,
.polaroid:focus-visible {
  rotate: 0deg;
  scale: 1.035;
  box-shadow: 0 22px 44px rgba(74, 52, 35, 0.24), 0 3px 8px rgba(74, 52, 35, 0.12);
  z-index: 2;
}

.polaroid:focus-visible { outline: 2px solid var(--thread); outline-offset: 6px; }

/* Vibe link ------------------------------------------------------------ */

.vibe {
  display: inline-flex;
  align-items: center;
  gap: 9px;
  margin: clamp(46px, 6vw, 78px) auto 0;
  justify-content: center;
  min-height: 44px;
  box-sizing: border-box;
  padding: 11px 26px;
  border: 1px solid var(--bow);
  border-radius: 999px;
  background: var(--bow);
  font-family: var(--font);
  font-weight: 600;
  font-size: clamp(0.68rem, 0.85vw, 0.8rem);
  letter-spacing: 0.14em;
  color: var(--paper);
  text-decoration: none;
  transition: background 200ms var(--ease-out), transform 200ms var(--ease-out);
}

.vibe svg { width: 16px; height: 16px; fill: currentColor; }

.vibe:hover,
.vibe:focus-visible { background: #7a5637; transform: translateY(-1px); }
.vibe:focus-visible { outline: 2px solid var(--bow); outline-offset: 4px; }

.vibe__row { display: grid; justify-items: center; }

/* Lightbox -------------------------------------------------------------- */

.lightbox {
  position: fixed;
  inset: 0;
  z-index: 40;
  display: grid;
  place-items: center;
  padding: clamp(18px, 4vw, 48px);
  background: rgba(38, 26, 18, 0.88);
  opacity: 0;
  transition: opacity 260ms var(--ease-inout);
}

.lightbox[hidden] { display: none; }
.lightbox.is-open { opacity: 1; }

.lightbox__figure {
  margin: 0;
  max-width: min(92vw, 760px);
  padding: 13px 13px 0;
  background: var(--frame);
  border-radius: 2px;
  box-shadow: 0 30px 70px rgba(0, 0, 0, 0.5);
}

.lightbox__img {
  display: block;
  width: 100%;
  max-height: 72dvh;
  object-fit: contain;
  background: #e9e2d7;
}

.lightbox__caption {
  padding: 15px 6px 18px;
  font-family: var(--font-script);
  font-weight: 300;
  font-size: clamp(0.7rem, 1.1vw, 0.9rem);
  text-align: center;
  color: var(--bow);
}

.lightbox__close {
  position: absolute;
  top: clamp(12px, 2.4vw, 26px);
  right: clamp(12px, 2.4vw, 26px);
  width: 46px;
  height: 46px;
  display: grid;
  place-items: center;
  padding: 0;
  border: 0;
  border-radius: 50%;
  background: transparent;
  font-size: 1.7rem;
  line-height: 1;
  color: rgba(255, 253, 250, 0.75);
  cursor: pointer;
  transition: color 200ms var(--ease-out), scale 200ms var(--ease-out);
}

.lightbox__close:hover,
.lightbox__close:focus-visible { color: #fff; scale: 1.08; }
.lightbox__close:focus-visible { outline: 2px solid #fff; outline-offset: 3px; }

/* Controls -------------------------------------------------------------- */

.moments__back,
.moments__home {
  position: fixed;
  top: 20px;
  z-index: 4;
  display: grid;
  place-items: center;
  width: 44px;
  height: 44px;
  padding: 0;
  border: 0;
  background: transparent;
  color: rgba(74, 52, 35, 0.5);
  cursor: pointer;
  transition: color 220ms var(--ease-out), rotate 420ms var(--ease-out);
}

.moments__back { left: 20px; }
.moments__home { right: 20px; }

.moments__back svg,
.moments__home svg { width: 25px; height: 25px; }

.moments__back:hover,
.moments__back:focus-visible { color: var(--ink); rotate: -28deg; }

.moments__home:hover,
.moments__home:focus-visible { color: var(--ink); rotate: 90deg; }

.moments__back:focus-visible,
.moments__home:focus-visible { outline: 2px solid var(--bow); outline-offset: 4px; border-radius: 50%; }

.moments__back:active,
.moments__home:active { scale: 0.92; }

/* Responsive ------------------------------------------------------------ */

@media (max-width: 760px) {
  /* One column: the photos zigzag down, as in the collage reference. */
  .reel__items {
    display: flex;
    flex-direction: column;
    gap: clamp(30px, 6vw, 46px);
  }
  .reel__items > :nth-child(even) { margin-top: 0; }
  .polaroid:nth-child(3n + 1) { align-self: flex-start; }
  .polaroid:nth-child(3n + 2) { align-self: center; }
  .polaroid:nth-child(3n + 3) { align-self: flex-end; }
  .polaroid { --print: clamp(190px, 46vw, 250px); }
}

@media (max-width: 700px) {
  .moments__back { top: 12px; left: 12px; }
  .moments__home { top: 12px; right: 12px; }
  .ribbon__name { font-size: clamp(1rem, 5.4vw, 1.5rem); }
  /* Less tilt in a narrow column, so nothing crowds the gutters. */
  .polaroid:nth-child(3n + 1) { --tilt: -2deg; }
  .polaroid:nth-child(3n + 2) { --tilt: 1.5deg; }
  .polaroid:nth-child(3n + 3) { --tilt: -1.5deg; }
}

/* Motion ---------------------------------------------------------------- */

.moments.is-animating .ribbon__img,
.moments.is-animating .ribbon__name { opacity: 0; }

@media (prefers-reduced-motion: reduce) {
  .moments__curtain { display: none; }
  .polaroid { transition: none; }
  .polaroid:hover, .polaroid:focus-visible { rotate: var(--tilt, 0deg); scale: 1; }
  .moments__back:hover, .moments__back:focus-visible,
  .moments__home:hover, .moments__home:focus-visible { rotate: none; }

  .moments.is-animating .ribbon__img,
  .moments.is-animating .ribbon__name { opacity: 1; }
}

