/* ════════════════════════════════════════════════════════════════════
   PAGE — PROJECT DETAIL (shared by page-detail-1941-nash-ambassador +
   page-detail-1937-imperial-black-betty). ONE builder, ×2 pages, ONE stylesheet.

   Authority: WOW_CONTRACTS page:detail + picks.json page:detail (G001 board +
   G003 grafts) + DESIGN.md token block + the locked slice
   (section-page-detail.jpg). The project case file, rendered in stamped steel:
     · a bleed-under-headline detail HERO (matches the board's title-over-photo)
     · a single scrolling stack: OVERVIEW / THE PROCESS / BEFORE & AFTER /
       GALLERY / SPECIFICATIONS (the 5-tab nav was killed — David found it
       unintuitive + the Overview/Specs tabs near-redundant)
     · a mono spec plate (YEAR/MAKE/MODEL/BODY/TYPE/COMPLETED) that odometer-rolls
     · a CONTAINED echo of the cell-4 resurrection (real stage stills) in PROCESS
     · the §1.7 scroll/drag before-after seam (ONLY where a real pair exists)
     · a photo contact-sheet GALLERY (their weekly-photo-documentation model as UI)
     · the real per-car YouTube build playlist + SEE THE NEXT PROJECT

   Scoped under the page-root .detail so nothing leaks into shared chrome.
   Palette sampled to tokens: base #222222 · Signal Red #C2231B · radius 0px.
   Alive at t0 (no scroll-reveal-gated content); tight, never laggy; hooks the
   shared .tungsten-idle for ambient life. Cohesive with the home cells — same
   plate/mono/stamped-steel vocabulary, same --resto seam idiom.
   ════════════════════════════════════════════════════════════════════ */

/* the subpage <main> sits under the fixed nav; give the hero room to bleed up */
.detail { --plate-edge: rgba(255,255,255,0.12); --plate-face: #2c2c2c; }

/* ─────────────────────────────────────────────────────────────────────
   1 · DETAIL HERO — the case file cover. Title bleeds OVER a full-bleed car
   photo (board: text-left, photo-right, both under one dark ground). On the
   board the photo is a right-hand bleed; we honour that at desktop and stack
   to a full-bleed banner on mobile. Kills the default .section padding.
   ───────────────────────────────────────────────────────────────────── */
.detail-hero {
  position: relative;
  padding-block: 0;
  background: var(--ink-900);
  overflow: hidden;
  isolation: isolate;
}
.detail-hero__grid {
  position: relative;
  display: grid;
  grid-template-columns: minmax(0, 0.92fr) minmax(0, 1.08fr);
  align-items: stretch;
  min-height: clamp(30rem, 46vw, 40rem);
  padding-top: var(--nav-h);           /* clear the fixed nav */
}
/* the photo lane — full-bleed to the right screen edge. Explicitly pinned to
   row 1 so it fills the FULL hero height in column 2 (the title overlaps it in
   the same cell via .detail-hero__inner spanning both columns). */
.detail-hero__media {
  position: relative;
  grid-column: 2; grid-row: 1;
  margin-right: calc((100vw - min(100vw, var(--maxw-wide))) / -2 - var(--gutter));
  overflow: hidden;
}
.detail-hero__img {
  position: absolute; inset: 0;
  width: 100%; height: 100%;
  object-fit: cover; object-position: center 42%;
  /* the ONE consistent grade: slight desat + warm lift (DESIGN.md) */
  filter: saturate(0.9) contrast(1.03) brightness(1.02);
}
/* left→right scrim so the title reads over the photo's left flank + a bottom
   feather so the spec-chip line sits on calm ground (T2.19 — dissolve, no seam) */
.detail-hero__scrim {
  position: absolute; inset: 0; z-index: 1; pointer-events: none;
  background:
    linear-gradient(90deg,
      var(--ink-900) 0%, rgba(34,34,34,0.86) 30%,
      rgba(34,34,34,0.30) 56%, rgba(34,34,34,0) 74%),
    linear-gradient(0deg,
      rgba(20,20,20,0.55) 0%, rgba(20,20,20,0) 34%);
}
/* the warm tungsten worklight glow — hooks the shared idle; NEVER on the photo */
.detail-hero__glow {
  position: absolute; inset: 0; z-index: 1; pointer-events: none;
  background: radial-gradient(60% 80% at 78% 30%,
    rgba(255,176,96,0.16) 0%, rgba(255,150,70,0.05) 40%, transparent 72%);
  mix-blend-mode: screen;
}
.detail-hero__inner {
  position: relative; z-index: 2;
  grid-column: 1 / -1; grid-row: 1;
  align-self: center;
  padding-block: var(--space-xl) var(--space-l);
  max-width: min(38rem, 60vw);
}
.detail-hero__file {
  display: inline-flex; align-items: center; gap: 0.7em;
  font-family: var(--font-mono); font-size: var(--step--1);
  letter-spacing: var(--tracking-label); text-transform: uppercase;
  color: var(--slate-400);
  margin-bottom: var(--space-m);
}
.detail-hero__file::before {
  content: ""; width: 2.2rem; height: 1px; background: var(--support); flex: none;
}
.detail-hero__file .is-status { color: var(--breath); }
.detail-hero__title {
  font-family: var(--font-display); font-weight: 800;
  font-size: var(--step-hero); line-height: 0.92;
  letter-spacing: 0.005em; text-transform: uppercase;
  color: var(--breath);
  margin-bottom: var(--space-m);
}
.detail-hero__title .em { font-style: normal; color: var(--accent); }  /* H1A-red word */
.detail-hero__spec {
  display: flex; flex-wrap: wrap; align-items: center; gap: 0.55em 1.1em;
  font-family: var(--font-mono); font-size: var(--step--1);
  letter-spacing: 0.16em; text-transform: uppercase;
  color: var(--slate-400);
}
.detail-hero__spec span { display: inline-flex; align-items: center; gap: 1.1em; }
.detail-hero__spec span::after {
  content: "·"; color: var(--support); opacity: 0.7;
}
.detail-hero__spec span:last-child::after { content: none; }

/* ─────────────────────────────────────────────────────────────────────
   2 · STACKED PANELS — the case file is now ONE scrolling page (the tab bar +
   NEXT button were killed: David found the 5-tab interface unintuitive and the
   Overview/Specifications tabs near-redundant). The five panels — overview →
   process → before&after → gallery → specs — stack top-to-bottom and read as a
   composed scroll. A hairline rule + the red mono section-marker (promoted from
   the old no-JS-only .detail-panel__label) separate the panels so the stack
   reads as distinct sections, not one undifferentiated column.
   ───────────────────────────────────────────────────────────────────── */
.detail-body { background: var(--ink-900); }
.detail-panel { padding-block: var(--space-xl); }
/* hairline separator between stacked sections (skip the first) */
.detail-panel + .detail-panel { border-top: 1px solid var(--ink-700); }

/* the mono section marker — was no-JS-only; now it's the visible eyebrow that
   heads every stacked panel so the scroll reads section-by-section. The red
   accent + rule echoes the .detail-sec-head__eyebrow idiom. */
.detail-panel__label {
  display: inline-flex; align-items: center; gap: 0.7em;
  font-family: var(--font-mono); font-size: var(--step--1);
  letter-spacing: var(--tracking-label); text-transform: uppercase;
  color: var(--accent); margin-bottom: var(--space-m);
}
.detail-panel__label::before {
  content: ""; width: 1.8rem; height: 1px; background: var(--accent);
}

.detail-sec-head {
  display: flex; align-items: baseline; gap: 0.9em; flex-wrap: wrap;
  margin-bottom: var(--space-l);
}
.detail-sec-head__eyebrow {
  font-family: var(--font-mono); font-size: var(--step--1);
  letter-spacing: var(--tracking-label); text-transform: uppercase;
  color: var(--slate-400);
  display: inline-flex; align-items: center; gap: 0.7em;
}
.detail-sec-head__eyebrow::before {
  content: ""; width: 1.8rem; height: 1px; background: var(--support);
}
.detail-h2 {
  font-family: var(--font-display); font-weight: 700;
  font-size: var(--step-section); line-height: 1; text-transform: uppercase;
  letter-spacing: 0.01em; color: var(--breath);
}

/* ── 3a · OVERVIEW panel — narrative left, spec plate + playlist right ── */
.detail-overview {
  display: grid; grid-template-columns: minmax(0, 1.15fr) minmax(0, 1fr);
  gap: clamp(2rem, 4vw, 4rem); align-items: start;
}
.detail-lede p { color: var(--text-on-dark); max-width: 42ch; }
.detail-lede p + p { margin-top: var(--space-m); }
/* ── the AWARDS card — MOVED out of the left story lede and re-seated in the
   right aside, directly UNDER the build playlist (David: the lone playlist card
   left a dead ~25% bottom-right corner; the awards fill it). It now reads as a
   PAIRED plate with the playlist — a full 1px plate border (matching
   .detail-playlist) instead of the old story-pullquote left-bar, with a compact
   red accent stripe at the top-left corner keeping the signal-red idiom. Shrunk:
   tighter block padding + smaller chips so it seats cleanly under the playlist. */
.detail-lede__award {
  margin-top: var(--space-m);
  padding: var(--space-s) var(--space-m);
  background: var(--ink-850);
  border: 1px solid var(--ink-700);
  position: relative;
}
.detail-lede__award::before {
  content: ""; position: absolute; left: 0; top: 0; width: 3px; height: 2.2rem;
  background: var(--accent);
}
.detail-lede__award-eyebrow {
  font-family: var(--font-mono); font-size: 0.62rem; letter-spacing: 0.16em;
  text-transform: uppercase; color: var(--accent); display: block; margin-bottom: 0.55em;
}
/* the award chips read as ONE tidy cluster — never an orphaned "Best in Class"
   dropping alone (David flagged the lone-chip wrap + the dead gap beside it). The
   chips balance across the row via flex; tightened tracking/padding lets the five
   awards sit without a half-empty last line. */
.detail-lede__award ul { list-style: none; display: flex; flex-wrap: wrap; gap: 0.35rem 0.4rem; }
.detail-lede__award li {
  font-family: var(--font-mono); font-size: 0.62rem; letter-spacing: 0.03em;
  text-transform: uppercase; color: var(--breath); white-space: nowrap;
  padding: 0.3em 0.5em; background: #232323; border: 1px solid var(--ink-700);
}
.detail-lede__award-cite {
  margin-top: 0.6rem; font-family: var(--font-body); font-size: 0.72rem;
  color: var(--slate-400); font-style: normal; line-height: 1.4;
}

/* ── the SPEC PLATE — mono ruled table; values odometer-roll when the specs
   section scrolls into view (was: on spec-tab open) ── */
.spec-plate {
  border: 1px solid var(--ink-700);
  background: linear-gradient(180deg, #262626 0%, #202020 100%);
  box-shadow: inset 0 0 0 1px rgba(255,255,255,0.03), 0 14px 32px -22px #000;
}
.spec-plate__head {
  display: flex; align-items: center; justify-content: space-between;
  padding: 0.85rem 1.1rem; border-bottom: 1px solid var(--ink-700);
  font-family: var(--font-mono); font-size: 0.68rem; letter-spacing: 0.18em;
  text-transform: uppercase; color: var(--slate-400);
}
.spec-plate__head-id { color: var(--accent); }
.spec-plate__row {
  display: grid; grid-template-columns: 8.5rem 1fr;
  border-bottom: 1px solid var(--ink-700);
}
.spec-plate__row:last-child { border-bottom: 0; }
.spec-plate__key,
.spec-plate__val {
  font-family: var(--font-mono); font-size: 0.82rem; letter-spacing: 0.08em;
  text-transform: uppercase; padding: 0.72rem 1.1rem;
}
.spec-plate__key { color: var(--slate-400); border-right: 1px solid var(--ink-700); }
.spec-plate__val { color: var(--breath); font-weight: 500; }
/* ── the REAL odometer: each character in its OWN boxed steel cell; the JS
   spins each cell's digit column L→R and lands hard on the true glyph (build it
   real or don't). Non-numeric glyphs (spaces, etc.) render as plain inline text —
   only 0-9 get a boxed spinning cell. ── */
.spec-plate__val .odo {
  display: inline-flex; align-items: stretch; gap: 2px;
  vertical-align: baseline;
}
.spec-plate__val .odo__digit {
  position: relative; display: inline-block; overflow: hidden;
  width: 0.72em; height: 1.28em; line-height: 1.28em;
  background: #191919;
  border: 1px solid var(--ink-700);
  box-shadow: inset 0 6px 8px -6px #000, inset 0 -6px 8px -6px #000;  /* drum curvature */
  text-align: center;
}
.spec-plate__val .odo__strip {
  display: block; will-change: transform;
  /* JS sets the transition per-cell for the staggered L→R landing */
}
.spec-plate__val .odo__cell {
  display: block; width: 100%; height: 1.28em; line-height: 1.28em;
  font-variant-numeric: tabular-nums;
}
/* the machined seam across the drum centre — reads as a real counter wheel */
.spec-plate__val .odo__digit::after {
  content: ""; position: absolute; left: 0; right: 0; top: 50%; height: 1px;
  background: rgba(0,0,0,0.55); transform: translateY(-0.5px); z-index: 2; pointer-events: none;
}
.spec-plate__val .odo__sep {
  display: inline-block; padding: 0 0.06em; line-height: 1.28em;
}

/* the OVERVIEW right column — the build PLAYLIST lives here (the story is the
   left lede). The full spec table was pulled from OVERVIEW so it no longer
   duplicates the SPECIFICATIONS tab (T2.25 — no two tabs doing the same job). */
.detail-overview__aside { display: flex; flex-direction: column; }
.detail-overview__aside-eyebrow {
  font-family: var(--font-mono); font-size: var(--step--1);
  letter-spacing: var(--tracking-label); text-transform: uppercase;
  color: var(--slate-400); margin-bottom: var(--space-m);
  display: inline-flex; align-items: center; gap: 0.7em;
}
.detail-overview__aside-eyebrow::before {
  content: ""; width: 1.8rem; height: 1px; background: var(--support);
}

/* ── the BUILD PLAYLIST plate (board: bottom-right) ──
   WHOLE tile is one hit target now (David: "I just assumed clicking on anywhere
   on that tile would bring me somewhere" → new tab into the build playlist, T2.38).
   The tile is positioned so a single full-bleed link overlay covers stage + meta;
   the poster play-plate stays a visual affordance (its own link handles the
   lazy-iframe-vs-YouTube fork; the overlay is the fallback for the rest of the
   tile). Relative so the overlay pins to the whole plate. */
.detail-playlist {
  position: relative;
  border: 1px solid var(--ink-700); background: var(--ink-850);
  display: grid; grid-template-columns: minmax(0, 1.3fr) minmax(0, 1fr);
  transition: border-color var(--dur-fast) var(--ease-out);
}
/* the full-tile link — floats OVER the whole plate (stage + meta are visual only,
   so covering them is fine) EXCEPT the play control, which is lifted above it so
   its click still mounts the inline player. Anywhere else on the tile → new tab. */
.detail-playlist__tilelink {
  position: absolute; inset: 0; z-index: 2;
  /* no visible box — the whole plate is the target */
}
/* the poster's play link fills the stage and sits ABOVE the tile-link; both target
   the same playlist in a new tab, so wherever the visitor clicks the tile they land
   on the build playlist (David's whole-tile call). */
.detail-playlist__play { z-index: 3; }
@media (hover: hover) {
  .detail-playlist:hover { border-color: var(--accent); }
}
.detail-playlist__stage {
  /* R87c (David's "rethink this"): the poster fills the FULL card height —
     when the meta column runs taller than 16:9 (Betty), the fixed ratio left
     a dead ink band under the car inside the card. Stretch wins; the 16:9
     preference only matters stacked (see the ≤760 block). */
  position: relative; height: 100%; min-height: 13rem; overflow: hidden;
  background: #111; border-right: 1px solid var(--ink-700);
}
.detail-playlist__stage iframe { position: absolute; inset: 0; width: 100%; height: 100%; border: 0; }
/* poster fallback / no-JS: a framed still + a stamped red play plate that links out */
.detail-playlist__poster { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover;
  filter: saturate(0.9) contrast(1.03) brightness(0.96); }
.detail-playlist__play {
  position: absolute; inset: 0; display: grid; place-items: center; z-index: 2;
}
.detail-playlist__play-plate {
  width: 4.4rem; height: 3.1rem; display: grid; place-items: center;
  background: var(--accent); border-radius: var(--radius);
  box-shadow: 0 4px 0 var(--terracotta-deep);
  transition: transform 140ms var(--ease-out), box-shadow 140ms var(--ease-out);
}
.detail-playlist__play-plate::after {
  content: ""; width: 0; height: 0; margin-left: 4px;
  border-style: solid; border-width: 0.68rem 0 0.68rem 1.05rem; border-color: transparent transparent transparent #fff;
}
/* ambient red-ring pulse — powered-on at rest (WOW: play plates not too-YouTube) */
.detail-playlist__play::before {
  content: ""; position: absolute; width: 5.4rem; height: 5.4rem; border-radius: 50%;
  border: 2px solid var(--accent); opacity: 0; z-index: 1;
  animation: play-ring 3.4s ease-out infinite;
}
@keyframes play-ring {
  0% { transform: scale(0.72); opacity: 0.55; }
  70% { opacity: 0; }
  100% { transform: scale(1.12); opacity: 0; }
}
@media (hover: hover) {
  .detail-playlist__play:hover .detail-playlist__play-plate { transform: translateY(3px); box-shadow: 0 1px 0 var(--terracotta-deep); }
}
@media (prefers-reduced-motion: reduce) { .detail-playlist__play::before { animation: none; } }
.detail-playlist__meta { padding: var(--space-m); display: flex; flex-direction: column; }
.detail-playlist__title {
  font-family: var(--font-display); font-weight: 700; font-size: 1.35rem;
  text-transform: uppercase; letter-spacing: 0.04em; color: var(--breath); margin-bottom: 0.5em;
}
.detail-playlist__desc { font-size: var(--step--1); color: var(--slate-400); line-height: 1.55; }
.detail-playlist__count {
  margin-top: auto; padding-top: var(--space-s);
  font-family: var(--font-mono); font-size: 0.7rem; letter-spacing: 0.14em; text-transform: uppercase;
  color: var(--breath);
}
.detail-playlist__count b { color: var(--accent); font-weight: 500; }

/* ── 3b · THE PROCESS panel — contained echo of the cell-4 resurrection.
   A pinned, scroll-scrubbed stage that steps through FOUND→STRIPPED→REBUILT→
   DELIVERED using REAL stage stills of THIS car's process (honestly framed as
   "the shop's process, stage by stage" — NOT four cars pretending to be one).
   Same --resto idiom as cell-4 for cohesion; lighter (a stepped cross-dissolve,
   not a seam-wipe, unless a real same-car BEFORE/AFTER pair drives it). ── */
.proc {
  --resto: 62%;   /* rest = a composed mid-process frame; content-complete pre-pin */
  display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1.35fr);
  /* R87b: start-aligned + CSS-sticky stage. The GSAP pin died — pinning a grid
     child whose row grew (numbers joined the rail) made the stage JUMP to its
     lock point (pin-spacer vs centered row). Sticky is native: the stage docks
     under the nav and the rail scrolls past it, no snap, every width. */
  gap: clamp(1.5rem, 3vw, 3rem); align-items: start;
  /* R87d: the walk needs track — the grid row stretches to this floor and the
     sticky stage rides docked through all of it, so each frame owns a real
     third of the scroll instead of a handful of pixels. Desktop only (the
     ≤760 stack resets it). */
  min-height: 145vh;
}
.proc__stage {
  position: sticky; top: calc(var(--nav-h) + 1.1rem);
  aspect-ratio: 16 / 10; overflow: hidden;
  border: 1px solid var(--ink-700); background: #141414;
}
.proc__frame { position: absolute; inset: 0; }
.proc__frame img {
  position: absolute; inset: 0; width: 100%; height: 100%;
  object-fit: cover; filter: saturate(0.9) contrast(1.03) brightness(1.0);
}
/* stepped cross-dissolve: each frame's opacity is gated by a per-frame class the
   JS toggles from floor(--resto/25). Base opacity 0 so only the active stage shows. */
.proc__frame { opacity: 0; transition: opacity 0.5s var(--ease-out); }
.proc__frame.is-active { opacity: 1; }
.proc.proc--nojs .proc__frame { position: relative; opacity: 1; aspect-ratio: 16/10; }
/* the tungsten worklight over the stage — shared idle, warm layer only */
.proc__glow {
  position: absolute; inset: 0; z-index: 2; pointer-events: none;
  background: radial-gradient(70% 90% at 30% 20%, rgba(255,168,92,0.14), transparent 70%);
  mix-blend-mode: screen;
}
/* the stage label plate, bottom-left, over calm ground (T2.8 corner rule) */
.proc__stagetag {
  position: absolute; left: 0; bottom: 0; z-index: 3;
  font-family: var(--font-mono); font-size: 0.72rem; letter-spacing: 0.16em; text-transform: uppercase;
  color: var(--breath); background: color-mix(in srgb, var(--ink-deep) 85%, transparent);
  padding: 0.5em 0.9em; border-top: 1px solid var(--ink-700); border-right: 1px solid var(--ink-700);
}
.proc__stagetag b { color: var(--accent); font-weight: 500; margin-right: 0.6em; }

.proc__rail { display: flex; flex-direction: column; gap: 0; }
.proc__caseplate {
  font-family: var(--font-mono); font-size: 0.7rem; letter-spacing: 0.16em; text-transform: uppercase;
  color: var(--accent); margin-bottom: var(--space-m);
  display: inline-flex; align-items: center; gap: 0.7em;
}
.proc__caseplate::before { content: ""; width: 1.6rem; height: 1px; background: var(--accent); }
.proc__steps { list-style: none; }
.proc__step {
  position: relative; display: grid; grid-template-columns: 3.2rem 1fr;
  gap: 0.9rem; padding: 0.85rem 0; align-items: start;
}
.proc__step + .proc__step { border-top: 1px solid var(--ink-700); }
.proc__step-num {
  font-family: var(--font-mono); font-size: 0.78rem; color: var(--slate-500);
  padding-top: 0.15em; transition: color var(--dur-fast) var(--ease-out);
}
.proc__step-title {
  font-family: var(--font-display); font-weight: 700; font-size: 1.35rem;
  text-transform: uppercase; letter-spacing: 0.04em; color: var(--slate-400);
  transition: color var(--dur-fast) var(--ease-out);
}
.proc__step-desc { font-size: var(--step--1); color: var(--slate-400); margin-top: 0.15em; opacity: 0.85; }
.proc__step.is-active .proc__step-num { color: var(--accent); }
.proc__step.is-active .proc__step-title { color: var(--breath); }
.proc__step.is-passed .proc__step-title { color: var(--support); }
/* red node marker on the active step */
.proc__step::before {
  content: ""; position: absolute; left: -1px; top: 1.15rem; width: 3px; height: 0;
  background: var(--accent); transition: height var(--dur-fast) var(--ease-out);
}
.proc__step.is-active::before { height: 1.6rem; }

/* the rebuild gauge — RESTORATION 0→100%, rides --resto (cell-4 idiom) */
.proc__gauge { margin-top: var(--space-l); }
.proc__gauge-head { display: flex; align-items: baseline; justify-content: space-between; margin-bottom: 0.5rem; }
.proc__gauge-label { font-family: var(--font-mono); font-size: 0.68rem; letter-spacing: 0.16em; text-transform: uppercase; color: var(--slate-400); }
.proc__gauge-pct { font-family: var(--font-display); font-weight: 800; font-size: 1.6rem; color: var(--breath); line-height: 1; }
.proc__gauge-pct .unit { color: var(--accent); font-size: 1rem; }
.proc__gauge-track { height: 6px; background: #191919; border: 1px solid var(--ink-700); overflow: hidden; }
.proc__gauge-fill { height: 100%; width: var(--resto); background: linear-gradient(90deg, var(--terracotta-deep), var(--accent)); }

/* ── 3c · BEFORE & AFTER panel — the §1.7 seam. AFTER is the base; BEFORE is
   clipped from the right by --ba; a red seam-line rides the clip edge. Drag OR
   scroll-scrub via JS; keyboard range for a11y. AR locked to the pair.
   ONLY built where a hand-confirmed real pair exists; else the panel carries an
   honest "no matched pair" note + points at the process scrub. ── */
.ba { --ba: 50%; max-width: 62rem; margin-inline: auto; }
.ba__stage {
  position: relative; aspect-ratio: 3 / 2; overflow: hidden;
  border: 1px solid var(--ink-700); background: #141414; touch-action: pan-y;
}
.ba__img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover;
  filter: saturate(0.9) contrast(1.03) brightness(1.0); user-select: none; -webkit-user-drag: none; }
/* Registration — the matched David-enhanced pair (P0317-de before / P0583-de
   after) are both 3:2 (2000×1334), both shot/graded from the LEFT (front-left,
   rear-right) so the car reads as ONE vehicle changing state across the seam —
   NOT a mirror. (The prior P0317.webp before was horizontally flipped vs David's
   master, making the two cars face opposite ways at the seam — killed here.)
   Both sit at 50% 50% in the 3/2 stage (minimal cover-crop); no per-side scale
   needed. Honest BEFORE/AFTER tags stay as the safety read. */
.ba__img--after  { object-position: 50% 50%; }
.ba__img--before { object-position: 50% 50%; clip-path: inset(0 var(--ba) 0 0); will-change: clip-path; }
.ba__seam { position: absolute; top: 0; bottom: 0; left: calc(100% - var(--ba)); width: 0; z-index: 3; pointer-events: none; }
.ba__seam-line { position: absolute; top: 0; bottom: 0; left: -1px; width: 2px; background: var(--accent); box-shadow: 0 0 12px 1px rgba(194,35,27,0.5); }
.ba__grip {
  position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%);
  width: 2.6rem; height: 2.6rem; display: grid; place-items: center;
  background: var(--accent); border-radius: var(--radius); box-shadow: 0 3px 0 var(--terracotta-deep);
}
.ba__grip::before, .ba__grip::after {
  content: ""; width: 0; height: 0; border-style: solid;
}
.ba__grip::before { border-width: 0.34rem 0.44rem 0.34rem 0; border-color: transparent #fff transparent transparent; margin-right: 0.35rem; }
.ba__grip::after  { border-width: 0.34rem 0 0.34rem 0.44rem; border-color: transparent transparent transparent #fff; }
/* BEFORE/AFTER tags — small chips seated UNDER the letters, bottom-anchored.
   NOTE: sections.css's own `.ba__tag { position:absolute; top: var(--space-s) }`
   (the homepage cell-4 seam uses the same class names) LEAKS onto this page — it
   loads before detail.css, so without an explicit `top` here the tag inherits
   BOTH top AND bottom and stretches to the full stage height (David saw them as
   full-height dark bars). `top: auto` releases the top edge so the chip collapses
   to its content and sits on the bottom line. Verified live: 545px bar → ~28px
   chip. Keyboard/drag interactions are untouched. */
.ba__tag {
  position: absolute; top: auto; bottom: 0.7rem; height: auto; z-index: 4;
  font-family: var(--font-mono); font-size: 0.68rem; letter-spacing: 0.16em; text-transform: uppercase;
  color: var(--breath); background: color-mix(in srgb, var(--ink-deep) 82%, transparent);
  padding: 0.4em 0.7em; border: 1px solid var(--ink-700);
}
.ba__tag--before { left: 0.7rem; }
.ba__tag--after  { right: 0.7rem; }
/* transparent native range over the stage for keyboard + a11y */
.ba__range {
  position: absolute; inset: 0; z-index: 5; width: 100%; height: 100%;
  margin: 0; opacity: 0; cursor: ew-resize;
}
/* idle breath on the seam at rest (before drag/scrub owns it) */
.ba__seam-line { animation: ba-seam-breath 4.2s ease-in-out infinite; }
.ba.is-active .ba__seam-line { animation: none; opacity: 1; }
@keyframes ba-seam-breath { 0%,100% { opacity: 0.9; } 50% { opacity: 0.6; } }
@media (prefers-reduced-motion: reduce) { .ba__seam-line { animation: none; } }
.ba__caption { margin-top: var(--space-m); text-align: center; font-size: var(--step--1); color: var(--slate-400); }
.ba__caption b { color: var(--breath); font-weight: 500; }
/* honest "no pair" note (Nash) — never fakes a pair */
.ba-note {
  max-width: 44rem; margin-inline: auto; text-align: center;
  padding: var(--space-l); border: 1px dashed var(--ink-700); background: var(--ink-850);
}
.ba-note p { color: var(--slate-400); }
.ba-note p + p { margin-top: var(--space-s); }

/* ── 3d · GALLERY panel — the photo contact-sheet (their weekly-photo model as
   UI). A film-strip grid of stamped negatives; tungsten sheen crawl at rest. ── */
.contact-sheet {
  display: grid; grid-template-columns: repeat(4, 1fr); gap: 6px;
  background: var(--ink-deep); padding: 6px; border: 1px solid var(--ink-700);
}
/* each cell is now a <button> (was an <a href="…raw.webp"> that opened the raw
   file in a new tab — T2.38 violation). The full-size src moved to data-full and
   a click opens the in-page lightbox. Reset button chrome so it renders exactly
   like the old cell. */
.contact-sheet__cell {
  position: relative; overflow: hidden; aspect-ratio: 3 / 2;
  background: #191919; display: block;
  width: 100%; margin: 0; padding: 0; border: 0;
  font: inherit; color: inherit; text-align: inherit;
  cursor: zoom-in; -webkit-appearance: none; appearance: none;
}
.contact-sheet__cell:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.contact-sheet__cell img {
  width: 100%; height: 100%; object-fit: cover;
  filter: saturate(0.88) contrast(1.04) brightness(0.98);
  transition: transform 0.5s var(--ease-out), filter 0.4s var(--ease-out);
}
/* A contact sheet is a DISCIPLINED uniform grid of identical negatives (T2.6 —
   organic or disciplined, nothing between; a real film contact sheet is uniform).
   Every cell is the same 3:2 frame; no span variants (they read as a half-reasoned
   stagger). The .cell--wide/--tall hooks are neutralised to keep the metaphor true. */
.contact-sheet__cell--wide,
.contact-sheet__cell--tall { grid-column: auto; grid-row: auto; }
.contact-sheet__no {
  position: absolute; top: 0.35rem; left: 0.4rem; z-index: 2;
  font-family: var(--font-mono); font-size: 0.6rem; letter-spacing: 0.08em;
  color: var(--breath); text-shadow: 0 1px 2px #000; opacity: 0.85;
}
/* the tungsten sheen crawl — a slow diagonal highlight; compositor-cheap transform */
.contact-sheet__cell::after {
  content: ""; position: absolute; inset: 0; z-index: 1; pointer-events: none;
  background: linear-gradient(115deg, transparent 38%, rgba(255,200,140,0.12) 50%, transparent 62%);
  transform: translateX(-120%);
  animation: sheen-crawl 9s ease-in-out infinite;
}
.contact-sheet__cell:nth-child(2n)::after { animation-delay: 2.2s; }
.contact-sheet__cell:nth-child(3n)::after { animation-delay: 4.4s; }
@keyframes sheen-crawl { 0% { transform: translateX(-120%); } 55%,100% { transform: translateX(120%); } }
@media (hover: hover) {
  .contact-sheet__cell:hover img { transform: scale(1.04); filter: saturate(0.95) contrast(1.05) brightness(1.05); }
}
@media (prefers-reduced-motion: reduce) { .contact-sheet__cell::after { animation: none; } }
.contact-sheet__caption { margin-top: var(--space-m); font-size: var(--step--1); color: var(--slate-400); text-align: center; }

/* ── 3e · GALLERY LIGHTBOX — clicking a contact-sheet cell opens the full image
   in an in-page overlay (never a raw-webp tab). Fixed full-viewport backdrop with
   the image centred; a stamped close plate + prev/next chevrons; body scroll is
   locked (.lb-open) while it's up. Alive only on open (JS toggles [hidden]). ── */
body.lb-open { overflow: hidden; }
.lightbox {
  position: fixed; inset: 0; z-index: 1000;
  display: grid; place-items: center;
}
.lightbox[hidden] { display: none; }
.lightbox__backdrop {
  position: absolute; inset: 0;
  background: rgba(10,10,10,0.9);
  -webkit-backdrop-filter: blur(4px); backdrop-filter: blur(4px);
  animation: lb-fade 0.22s var(--ease-out);
}
.lightbox__frame {
  position: relative; z-index: 1;
  max-width: min(92vw, 78rem); max-height: 88vh;
  display: flex; align-items: center; justify-content: center;
  animation: lb-rise 0.24s var(--ease-out);
}
.lightbox__img {
  display: block; max-width: 100%; max-height: 88vh;
  object-fit: contain; border: 1px solid var(--ink-700);
  background: #141414; box-shadow: 0 24px 60px -20px #000;
}
.lightbox__caption {
  position: absolute; left: 0; bottom: -1.9rem; z-index: 2;
  font-family: var(--font-mono); font-size: 0.62rem; letter-spacing: 0.14em;
  text-transform: uppercase; color: var(--slate-400);
}
.lightbox__close {
  position: absolute; top: -1.1rem; right: -1.1rem; z-index: 3;
  width: 2.6rem; height: 2.6rem; display: grid; place-items: center;
  font-size: 1.5rem; line-height: 1; color: var(--breath);
  background: var(--ink-deep); border: 1px solid var(--ink-700);
  border-radius: var(--radius); cursor: pointer;
  transition: background var(--dur-fast) var(--ease-out), color var(--dur-fast) var(--ease-out);
}
.lightbox__nav {
  position: absolute; top: 50%; transform: translateY(-50%); z-index: 3;
  width: 3rem; height: 3rem; display: grid; place-items: center;
  font-size: 1.4rem; line-height: 1; color: var(--breath);
  background: color-mix(in srgb, var(--ink-deep) 78%, transparent);
  border: 1px solid var(--ink-700); border-radius: var(--radius); cursor: pointer;
  transition: background var(--dur-fast) var(--ease-out);
}
.lightbox__nav--prev { left: -1.4rem; }
.lightbox__nav--next { right: -1.4rem; }
.lightbox__close:focus-visible,
.lightbox__nav:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
@media (hover: hover) {
  .lightbox__close:hover { background: var(--accent); color: #fff; }
  .lightbox__nav:hover { background: var(--accent); }
}
@keyframes lb-fade { from { opacity: 0; } to { opacity: 1; } }
@keyframes lb-rise { from { opacity: 0; transform: translateY(8px) scale(0.985); } to { opacity: 1; transform: none; } }
@media (prefers-reduced-motion: reduce) {
  .lightbox__backdrop, .lightbox__frame { animation: none; }
}
@media (max-width: 760px) {
  .lightbox__nav--prev { left: 0.3rem; }
  .lightbox__nav--next { right: 0.3rem; }
  .lightbox__close { top: 0.3rem; right: 0.3rem; }
}

/* ─────────────────────────────────────────────────────────────────────
   4 · SEE THE NEXT PROJECT — stamped band, plate-press CTA. Bookends the file.
   ───────────────────────────────────────────────────────────────────── */
.detail-next {
  border-top: 1px solid var(--ink-700);
  background: var(--ink-850);
  padding-block: var(--space-m);
}
/* "Next in the file" label KILLED in markup (David: redundant once See-the-Next
   lives inside the single viewport). The band is now just the See-Next action,
   left-aligned so it reads as the closing line of the case file. */
.detail-next__inner { display: flex; align-items: center; justify-content: flex-start; gap: var(--space-m); flex-wrap: wrap; }
.detail-next__link {
  display: inline-flex; align-items: center; gap: 0.8em;
  font-family: var(--font-display); font-weight: 700; font-size: clamp(1.3rem, 2.4vw, 1.9rem);
  text-transform: uppercase; letter-spacing: 0.03em; color: var(--breath);
  transition: color var(--dur-fast) var(--ease-out);
}
.detail-next__link .next-model { color: var(--accent); }
.detail-next__link .arrow { transition: transform var(--dur-fast) var(--ease-out); }
@media (hover: hover) {
  .detail-next__link:hover { color: var(--breath); }
  .detail-next__link:hover .arrow { transform: translateX(5px); }
}

/* ─────────────────────────────────────────────────────────────────────
   5 · RESPONSIVE — desktop RECOMPOSED, not squished (T2.31). Grids re-grid.
   ───────────────────────────────────────────────────────────────────── */
@media (max-width: 960px) {
  .detail-overview { grid-template-columns: 1fr; gap: var(--space-l); }
  .proc { grid-template-columns: 1fr; gap: var(--space-l); min-height: 0; }
  .detail-playlist { grid-template-columns: 1fr; }
  /* R98 (David's Betty bleed screenshot): min-height 0 is LOAD-BEARING — the
     base min-height:13rem transfers through aspect-ratio into a 369.8px
     min-WIDTH at 390, shoving the poster past the card's right border. */
  .detail-playlist__stage { border-right: 0; border-bottom: 1px solid var(--ink-700); height: auto; min-height: 0; aspect-ratio: 16 / 9; }
  .contact-sheet { grid-template-columns: repeat(3, 1fr); }
}
@media (max-width: 760px) {
  .detail-hero__grid { grid-template-columns: 1fr; min-height: 0; padding-top: calc(var(--nav-h) + 0.5rem); }
  /* mobile: the photo becomes a full-bleed banner UNDER the title block */
  .detail-hero__media {
    position: relative; grid-column: 1; grid-row: 2;
    margin-right: calc(var(--gutter) * -1); margin-left: calc(var(--gutter) * -1);
    aspect-ratio: 16 / 11; min-height: 0;
  }
  .detail-hero__img { position: absolute; }
  .detail-hero__inner { grid-row: 1; max-width: none; padding-block: var(--space-l) var(--space-m); }
  .detail-hero__scrim {
    background:
      linear-gradient(180deg, var(--ink-900) 0%, rgba(34,34,34,0.2) 100%);
  }
  .contact-sheet { grid-template-columns: repeat(2, 1fr); }
  .ba__stage { aspect-ratio: 4 / 3; }
  .detail-next__inner { flex-direction: column; align-items: flex-start; gap: var(--space-xs); }
}

/* solo/reduced-motion: reveal handled by main.js; all panels are stacked +
   visible. The proc/ba/spec floors are content-complete at their rest
   --resto/--ba. */
