/* ==========================================================================
   EXPERIENCE.CSS — single-scroll page, additive over manual.css
   Reuses manual.css tokens (--blueprint, --hatch, --graph, --s*, fonts).
   Loaded AFTER manual.css. Only styles the one-scroll page; other pages
   never link this file.
   Sections: A sections/anchors · B pinned title band · C hatch dividers
   D parallax · E staggered reveal · F scrollspy · G section-progress rail
   ========================================================================== */

/* .plate-open and .plate:hover moved to manual.css so pages that don't load
   experience.css (e.g. design.html) still get a clickable plate overlay. */

/* A ── SECTIONS + ANCHOR OFFSET ------------------------------------------- */
/* header is sticky; land anchored scrolls just below it. --headh is the
   compressed sticky-header height; the real header is taller on load but
   compresses (.is-stuck) the instant you scroll, which is when anchors land. */
:root { --headh: 132px; }

.exp-section {
  position: relative;
  scroll-margin-block-start: var(--headh);
  padding-block: var(--s5);
}
.exp-section:first-of-type { padding-block-start: var(--s4); }

/* B ── PINNED SECTION-TITLE BAND ----------------------------------------- */
/* the label pins under the header for a beat, then releases as the section
   scrolls past. Purely decorative wayfinding; theme-matched mono label. */
.section-title {
  /* was position:sticky; the pinned paper band masked the top edge of the
     photography grid (and other section content) as it scrolled underneath.
     The band only holds a decorative hatch bar, so it no longer pins. */
  position: static;
  display: flex;
  align-items: baseline;
  gap: var(--s2);
  margin: 0 0 var(--s4);
  padding-block: var(--s1);
  background: var(--paper);
}
.section-title .tag {
  font-family: var(--font-mono);
  font-size: 11px;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--ink-muted);
  white-space: nowrap;
}
.section-title .bar {
  flex: 1;
  block-size: 6px;
  background: var(--hatch);
}

/* C ── HATCH DIVIDERS (draw in on scroll) -------------------------------- */
.section-divider {
  block-size: 12px;
  margin: 0;
  background: var(--hatch);
  /* collapsed until observed; draws left-to-right */
  clip-path: inset(0 100% 0 0);
  transition: clip-path 900ms cubic-bezier(0.22, 1, 0.36, 1);
}
.section-divider.is-in { clip-path: inset(0 0 0 0); }

/* D ── PARALLAX ----------------------------------------------------------- */
/* JS sets --p (px) from scroll position; figures drift slightly against the
   scroll. Transform only — no layout thrash. */
.parallax {
  transform: translate3d(0, var(--p, 0px), 0);
  will-change: transform;
}

/* E ── STAGGERED REVEAL --------------------------------------------------- */
/* manual.css supplies .reveal / .reveal.is-in (fade + 12px rise). Here we
   cascade siblings by their --i index so plates/frames arrive in sequence. */
.reveal[style*="--i"], .reveal[data-i] { transition-delay: calc(var(--i, 0) * 70ms); }
.plate-grid > .reveal:nth-child(1) { --i: 0; }
.plate-grid > .reveal:nth-child(2) { --i: 1; }
.plate-grid > .reveal:nth-child(3) { --i: 2; }
.plate-grid > .reveal:nth-child(4) { --i: 3; }
.plate-grid > .reveal:nth-child(5) { --i: 4; }
.plate-grid > .reveal:nth-child(6) { --i: 5; }

/* F ── SCROLLSPY (active nav link) --------------------------------------- */
/* mirror the existing aria-current treatment: filled square + solid underline */
.scrollspy a.is-active {
  text-decoration: underline;
  text-decoration-thickness: 1px;
  text-underline-offset: 4px;
}
.scrollspy a.is-active::before {
  content: "";
  inline-size: 8px;
  block-size: 8px;
  background: var(--blueprint);
}
.nav-overlay a.is-active { background: var(--blueprint-tint); }

/* header nav uses the sliding blue pill instead — no square dot, no underline */
.site-nav a.is-active { text-decoration: none; color: var(--paper); }
.site-nav a.is-active::before { content: none; }

/* G ── SECTION-PROGRESS RAIL (right edge, drafting tick marks) ------------ */
.scroll-rail {
  position: fixed;
  inset-block-start: 50%;
  inset-inline-end: 14px;
  transform: translateY(-50%);
  z-index: 40;
  display: flex;
  flex-direction: column;
  gap: 10px;
  list-style: none;
  margin: 0;
  padding: 0;
}
.scroll-rail a {
  display: block;
  inline-size: 14px;
  block-size: 14px;
  position: relative;
  text-decoration: none;
}
.scroll-rail a::after {
  content: "";
  position: absolute;
  inset-block-start: 50%;
  inset-inline-end: 0;
  inline-size: 18px;
  block-size: 2px;
  background: var(--blueprint);
  opacity: 0.55;
  transform: translateY(-50%);
  transition: inline-size 150ms linear, opacity 150ms linear;
}
.scroll-rail a.is-active::after { inline-size: 28px; block-size: 3px; opacity: 1; }
.scroll-rail a:hover::after { inline-size: 28px; opacity: 1; }
.scroll-rail a .rail-label {
  position: absolute;
  inset-inline-end: 22px;
  inset-block-start: 50%;
  transform: translateY(-50%);
  font-family: var(--font-mono);
  font-size: 11px;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--blueprint);
  background: var(--paper);
  padding: 2px 6px;
  border: 1px solid var(--blueprint);
  white-space: nowrap;
  opacity: 0;
  pointer-events: none;
  transition: opacity 120ms linear;
}
.scroll-rail a:hover .rail-label,
.scroll-rail a:focus-visible .rail-label { opacity: 1; }

/* over the blue footer, invert the rail ticks to white so they stay visible */
.scroll-rail.over-footer a::after { background: var(--paper); }

/* H ── RESPONSIVE + MOTION ----------------------------------------------- */
@media (max-width: 899px) {
  :root { --headh: 148px; }
  .scroll-rail { display: none; }
  .section-title { inset-block-start: 0; }
}

@media (prefers-reduced-motion: reduce) {
  .section-divider { clip-path: none; transition: none; }
  .parallax { transform: none; }
  .reveal[style*="--i"], .reveal[data-i] { transition-delay: 0ms; }
  .section-title { position: static; }
}

/* I ── GSAP PINNED SEQUENCES (gsap-scroll.js) ----------------------------- */
/* All of section I only takes effect once gsap-scroll.js adds .gsap-on to
   <html>. Without GSAP (no JS, reduced motion, mobile, load failure) none of
   these rules match, so the page falls back to the plain single-scroll layout
   already defined above. Nothing here is required for the page to read. */

/* I.1 photography horizontal filmstrip.
   When active, the contact sheet is a single horizontal row inside a pinned
   viewport; gsap translates the row on the x axis across the extra scroll.
   Only grid view opts in — list view (main.js toggles .view-list) drops out
   and the strip is torn down by JS. */
/* strip fills the viewport below the (live-measured) header so the pinned
   section has no empty band beneath it. Frames stretch to that height and the
   SVGs letterbox (default preserveAspectRatio=meet) — 4:3 kept, no distortion. */
:root { --strip-h: calc(100svh - var(--head-now, var(--headh)) - var(--s5)); }
.gsap-on .hstrip-viewport { overflow: hidden; position: relative; }
.gsap-on .contact-sheet.is-hstrip {
  grid-template-columns: none;
  grid-auto-flow: column;
  grid-auto-columns: clamp(320px, calc(var(--strip-h) * 0.85), 76vw);
  block-size: var(--strip-h);
  align-items: stretch;
  will-change: transform;
}
.gsap-on .contact-sheet.is-hstrip > figure {
  min-inline-size: 0;
  block-size: var(--strip-h);
  box-sizing: border-box;
  padding-block-end: var(--s3); /* keep captions clear of the bottom progress bar */
  display: flex;
  flex-direction: column;
  gap: var(--s1);
}
.gsap-on .contact-sheet.is-hstrip .frame-btn {
  flex: 1 1 auto;
  min-block-size: 0;
  display: flex;
}
.gsap-on .contact-sheet.is-hstrip .ph-art {
  inline-size: 100%;
  block-size: 100%;
  max-inline-size: none;
}
/* dither plates: height comes from the flex row, not the 4:5 ratio */
.gsap-on .contact-sheet.is-hstrip .plate {
  flex: 1 1 auto;
  inline-size: 100%;
  block-size: 100%;
  aspect-ratio: auto;
}
/* progress hairline pinned to the bottom of the strip, drafting tick that fills */
.gsap-on .hstrip-progress {
  position: absolute;
  inset-block-end: 0;
  inset-inline: 0;
  block-size: 2px;
  margin: 0;
  background: var(--hatch);
}
.gsap-on .hstrip-progress::before {
  content: "";
  position: absolute;
  inset: 0 auto 0 0;
  inline-size: var(--hp, 0%);
  background: var(--blueprint);
}

/* the horizontal strip is a desktop, motion-OK affordance only */
@media (max-width: 899px), (prefers-reduced-motion: reduce) {
  .gsap-on .contact-sheet.is-hstrip {
    grid-auto-flow: row;
    grid-template-columns: repeat(3, 1fr);
    block-size: auto;
    grid-auto-columns: initial;
  }
  .gsap-on .contact-sheet.is-hstrip > figure {
    block-size: auto;
    display: block;
  }
  .gsap-on .contact-sheet.is-hstrip .frame-btn { display: block; }
  .gsap-on .contact-sheet.is-hstrip .ph-art { block-size: auto; }
  .gsap-on .contact-sheet.is-hstrip .plate { block-size: auto; aspect-ratio: 4 / 5; }
}

/* J ── CASE-STUDY STICKY STACK -------------------------------------------- */
/* Full-width bars park one under another using position: sticky with
   cumulative top offsets; because all bars share one containing block
   (.cs-stack) they release together when the stack scrolls out. Pure CSS —
   works with no JS, no GSAP, and under reduced motion. gsap-scroll.js only
   adds the entry rise + .is-parked class. Nothing in the ancestor chain sets
   overflow/transform/height:100%, which would silently break sticky. */
:root { --bar-h: 52px; }

/* only this section drops the sticky title band, so the stack owns the top */
#projects .section-title { position: static; }

.cs-stack { position: relative; }

.cs-bar {
  position: sticky; /* fallback when GSAP is absent; .gsap-pinned turns this off */
  inset-block-start: calc(var(--head-now, var(--headh)) + var(--i) * var(--bar-h));
  z-index: calc(10 + var(--i));
  block-size: var(--bar-h);
  box-sizing: border-box;
  display: flex;
  align-items: center;
  gap: var(--s2);
  padding-inline: var(--s3);
  background: var(--paper);
  /* one rule under every bar; parked bars abut so only the upper bar's rule
     shows between them → a single consistent 1.5px line everywhere */
  border-block-end: 1.5px solid var(--blueprint);
  font-family: var(--font-mono);
  text-transform: uppercase;
  letter-spacing: 0.12em;
}
.cs-stack > .cs-bar:first-child { border-block-start: 1.5px solid var(--blueprint); }

/* GSAP owns positioning while active, so the CSS sticky fallback stands down */
.cs-stack.gsap-pinned .cs-bar { position: static; }
.cs-bar .cs-no { font-size: 11px; color: var(--ink-muted); }
.cs-bar .cs-title { font-size: 12px; color: var(--blueprint); }
.cs-bar.is-parked { background: var(--blueprint-tint); }
.cs-bar .proj-ico { margin-inline-start: var(--s1); color: var(--blueprint); }

/* clickable bars (studies with a real page) — hover highlight + arrow */
a.cs-bar { text-decoration: none; color: inherit; cursor: pointer; }
.cs-go { margin-inline-start: auto; color: var(--blueprint); transition: transform 120ms linear; }
/* hover background is the row's own brand color, not a fixed blue - each
   bar carries data-brand (also used by its .proj-ico icon) so the whole
   strip reads as "this is the GitHub/Airbnb one" on hover, not just a tint
   on the small icon. */
a.cs-bar:hover { background: var(--blueprint); }
a.cs-bar[data-brand="github"]:hover { background: var(--brand-github); }
a.cs-bar[data-brand="airbnb"]:hover { background: var(--brand-airbnb); }
a.cs-bar:hover .cs-no,
a.cs-bar:hover .cs-title,
a.cs-bar:hover .cs-go,
a.cs-bar:hover .proj-ico { color: var(--paper); }
a.cs-bar:hover .cs-go { transform: translateX(4px); }
/* white text on Airbnb's red fails WCAG AA (~3:1) - fixed dark ink
   (not the theme-swapping --ink token, since this must stay dark in both
   themes against a fixed red background) restores ~5.6:1. */
a.cs-bar[data-brand="airbnb"]:hover .cs-no,
a.cs-bar[data-brand="airbnb"]:hover .cs-title,
a.cs-bar[data-brand="airbnb"]:hover .cs-go,
a.cs-bar[data-brand="airbnb"]:hover .proj-ico { color: #15171C; }

.cs-panel {
  min-block-size: 52vh;
  padding-block: var(--s4);
  max-inline-size: 640px;
}
.cs-panel .cs-line { margin-block: 0 var(--s2); }
.cs-panel .cs-key {
  font-family: var(--font-mono);
  font-size: 11px;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--ink-muted);
  display: block;
  margin-block-end: 2px;
}

@media (max-width: 899px) {
  :root { --bar-h: 44px; }
  .cs-panel { min-block-size: 44vh; }
}

/* the footer's own back-to-top link was removed, so the centered floating
   button now stays visible at the footer too (no .at-footer hide). */

