/* ─────────────────────────────────────────
   CASE STUDY PAGE
   Single-column centred layout.
   Depends on style.css for design tokens.
───────────────────────────────────────── */

/* ── READING PROGRESS BAR ── */
.cs-progress {
  position: fixed;
  top: 0;
  left: 0;
  height: 2px;
  width: 0%;
  background: var(--color-primary);
  z-index: 30;
  transition: width 80ms linear;
  pointer-events: none;
}

/* ── NAV ── */
.cs-nav {
  position: sticky;
  top: 0;
  z-index: 20;
  background: var(--color-bg);
  border-bottom: 1px solid var(--color-border);
  padding: 0 var(--sp-8);
  height: var(--nav-height);
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--sp-4);
  transition: background 300ms ease;
}

.cs-back {
  font-family: var(--font-mono);
  font-size: var(--text-2xs);
  letter-spacing: var(--tracking-wider);
  text-transform: uppercase;
  color: var(--color-text-muted);
  text-decoration: none;
  white-space: nowrap;
  transition: color 120ms ease;
  flex-shrink: 0;
}
.cs-back:hover {
  color: var(--color-primary);
  text-decoration: none;
}

.cs-nav-title {
  font-family: var(--font-mono);
  font-size: var(--text-2xs);
  letter-spacing: var(--tracking-widest);
  text-transform: uppercase;
  color: var(--color-text-subtle);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* ── MAIN & CONTAINER ── */
.cs-main {
  width: 100%;
  padding-bottom: var(--sp-24);
}

.cs-container {
  max-width: 720px;
  margin: 0 auto;
  padding-left: var(--sp-8);
  padding-right: var(--sp-8);
}

/* ── TOP BACK LINK (single-column pages, no left rail) ── */
.cs-back-link {
  display: inline-flex;
  align-items: center;
  gap: var(--sp-2);
  margin-top: var(--sp-10);
  font-size: var(--text-sm);
  color: var(--color-text);
  text-decoration: none;
  transition: color 120ms ease;
}
.cs-back-link:hover { color: var(--color-primary); }
.cs-back-link svg {
  width: 16px;
  height: 16px;
  fill: none;
  stroke: currentColor;
  stroke-width: 1.5;
  stroke-linecap: round;
  stroke-linejoin: round;
}

/* ── HERO ── */
.cs-hero {
  padding: var(--sp-16) 0 var(--sp-10);
}

.cs-hero-label {
  font-family: var(--font-mono);
  font-size: var(--text-2xs);
  letter-spacing: var(--tracking-widest);
  text-transform: uppercase;
  color: var(--color-text-muted);
  margin-bottom: var(--sp-5);
}

.cs-subtitle {
  font-size: var(--text-sm);
  color: var(--color-text-muted);
  margin-bottom: var(--sp-5);
}

.cs-hero-desc {
  font-size: var(--text-base);
  line-height: var(--leading-body);
  color: var(--color-text);
  margin-top: var(--sp-6);
  max-width: 600px;
}

/* ── HERO IMAGE ── */
.cs-hero-img-wrap {
  margin-top: var(--sp-10);
  border-radius: 8px;
  overflow: hidden;
  --_breakout: max(0px, calc((70vw - (720px - 4rem)) / 2));
  margin-left: calc(-1 * var(--_breakout));
  margin-right: calc(-1 * var(--_breakout));
}

.cs-hero-img {
  width: 100%;
  height: auto;
  display: block;
}

/* ── SECTIONS ── */
.cs-section {
  padding: var(--sp-16) 0 0;
}

.cs-section + .cs-section {
  border-top: 1px solid var(--color-border);
  margin-top: var(--sp-16);
}

.cs-divider {
  border: none;
  border-top: 1px solid var(--color-border);
  margin: var(--sp-16) 0 0;
}

.cs-list {
  font-size: var(--text-base);
  line-height: var(--leading-body);
  color: var(--color-text);
  margin-top: var(--sp-4);
  padding-left: var(--sp-6);
}

.cs-list li + li {
  margin-top: var(--sp-2);
}

/* ── FIGURES ── */
.cs-figure {
  margin-top: var(--sp-10);
  margin-bottom: var(--sp-10);
  --_breakout: max(0px, calc((70vw - (720px - 4rem)) / 2));
  margin-left: calc(-1 * var(--_breakout));
  margin-right: calc(-1 * var(--_breakout));
}

.cs-img {
  width: 100%;
  height: auto;
  display: block;
  border-radius: 6px;
}

.cs-figcaption {
  margin-top: var(--sp-3);
  font-family: var(--font-mono);
  font-size: var(--text-sm);
  letter-spacing: var(--tracking-wide);
  color: var(--color-text);
  text-align: center;
}

/* ── TWO-UP IMAGE PAIR ── */
.cs-img-pair {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: var(--sp-4);
  margin-top: var(--sp-10);
}

.cs-img-pair .cs-figure {
  margin-top: 0;
  margin-bottom: 0;
  --_breakout: 0px;
  margin-left: 0;
  margin-right: 0;
}

/* ── CALLOUT / INSIGHT BOX ── */
.cs-callout {
  margin-top: var(--sp-8);
  padding: var(--sp-6) var(--sp-8);
  background: var(--color-primary-muted);
  border-left: 2px solid var(--color-primary);
  border-radius: 0 4px 4px 0;
}

.cs-callout p {
  color: var(--color-text);
  font-style: italic;
}

/* ── OUTCOMES STATS ── */
.cs-outcomes {
  margin-top: var(--sp-8);
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: var(--sp-6);
  padding: var(--sp-8) 0;
  border-top: 1px solid var(--color-border);
  border-bottom: 1px solid var(--color-border);
}

.cs-stat-num {
  font-family: var(--font-display);
  font-size: var(--text-2xl);
  font-weight: 700;
  color: var(--color-primary);
  line-height: 1;
  margin-bottom: var(--sp-2);
}

.cs-stat-desc {
  font-family: var(--font-mono);
  letter-spacing: var(--tracking-wide);
  color: var(--color-text-muted);
}

/* ── PAGE FOOTER ──
   Project-to-project navigation: All projects ← → Next project.
   Injected by components/case-study.js. */
.cs-footer {
  margin-top: var(--sp-16);
  padding: var(--sp-10) 0;
  border-top: 1px solid var(--color-border);
}

.cs-footer-nav {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--sp-6);
  flex-wrap: wrap;
}

/* Forward CTA — mirror of .panel-cta--back: circle trails the label
   and rotates the same +45° the base .panel-cta hover already applies. */
.panel-cta--next .panel-cta-circle { order: 1; }


/* ═════════════════════════════════════════
   PASSWORD GATE  (protected case studies)
   Eyebrow-inline "Locked" pill + full-page password modal.
   While locked: eyebrow / H1 / hero stay crisp, the meta and the body
   skeleton below the hero sit under a frost blur, and the rail shows a
   blurred skeleton index — so unlocking never shifts the layout, it only
   fills things in. See components/case-study.js.
═════════════════════════════════════════ */
/* <body> hook while the page is locked (added by case-study.js). */

/* Meta row frosts over; the transition lives on the base element so the
   un-blur animates too. */
.cs-meta {
  transition: filter 500ms ease, opacity 500ms ease;
}
.cs-is-locked .cs-meta {
  filter: blur(5px);
  opacity: 0.5;
  pointer-events: none;
  user-select: none;
}

/* Eyebrow row: label + pill share one baseline. */
.cs-eyebrow-row {
  display: flex;
  align-items: center;
  gap: var(--sp-3);
}

/* The pill — sizing/color come from the shared .btn .btn--xs (style.css);
   this just adds the hover lift on top. */
.cs-lock-badge { transition: transform 120ms ease; }
.cs-lock-badge:hover { transform: translateY(-1px); }
.cs-lock-badge svg { width: 11px; height: 11px; display: block; }

/* Frosted skeleton standing in for the sections (inside #cs-locked-mount).
   Kept compact: while locked the page is just a teaser, so the footer
   should sit close under the hero instead of a viewport away. */
.cs-locked-skeleton {
  margin-top: var(--sp-10);
  padding-top: var(--sp-10);
  border-top: 1px solid var(--color-border);
  filter: blur(7px);
  opacity: 0.55;
  user-select: none;
  pointer-events: none;
}
.cs-skel-head {
  height: 14px; width: 120px;
  border-radius: 6px;
  background: var(--color-text-muted);
  opacity: 0.5;
  margin-bottom: var(--sp-6);
}
.cs-skel-line {
  height: 12px;
  border-radius: 6px;
  background: var(--color-text-muted);
  opacity: 0.28;
  margin-bottom: var(--sp-4);
}
.cs-skel-block {
  height: 120px;
  border-radius: 12px;
  background: var(--color-text-muted);
  opacity: 0.18;
  margin: var(--sp-6) 0;
}

/* Message + Unlock button floating on top of the skeleton — a second,
   more visible entry point into the same modal the eyebrow badge opens.
   A sibling of .cs-locked-skeleton (not a child), so the blur/opacity on
   the skeleton never touches it. */
#cs-locked-mount { position: relative; }
.cs-teaser-chip {
  position: absolute;
  top: 45%;
  left: 50%;
  transform: translate(-50%, -50%);
  width: min(320px, 90%);
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--sp-4);
  padding: var(--sp-6) var(--sp-8);
  background: var(--color-bg);
  border: 1px solid var(--color-border);
  border-radius: 14px;
  box-shadow: 0 10px 34px rgba(0, 0, 0, 0.22);
  text-align: center;
}
.cs-teaser-chip .cs-gate-lock { margin-bottom: 0; }
.cs-teaser-chip .cs-gate-title { margin-bottom: 0; font-size: var(--text-base); }

/* Locked pages are short teasers — pull the footer up so it doesn't
   strand a viewport of empty space under the skeleton. Beats the
   .cs-content > * + * flow margin via the extra class. */
.cs-is-locked .cs-content > .cs-footer { margin-top: var(--sp-10); }

/* Skeleton index in the rail — real titles are inside the encrypted
   payload, so blurred bars hold the shape until unlock swaps in the TOC. */
.cs-toc--skeleton { gap: var(--sp-4); }
.cs-toc-skel {
  height: 11px;
  border-radius: 5px;
  background: var(--color-text-muted);
  opacity: 0.3;
  filter: blur(3px);
}

/* Unlock reveal: the fresh TOC pops in item by item ("papapapapa"). */
.cs-just-unlocked .cs-toc-link {
  opacity: 0;
  transform: scale(0.6) translateY(-6px);
  animation: toc-pop 300ms cubic-bezier(0.65, 0, 0.35, 1) both;
}
/* …and the rail video avatar lands last, with the same slot-open +
   circle-drop entrance it gets when arriving from home (.from-transition).
   Delay set dynamically by staggerUnlockedToc() in case-study.js. */
.cs-just-unlocked .csv {
  overflow: hidden;   /* clip the circle to the opening slot */
  animation: csv-open 520ms cubic-bezier(0.34, 1.45, 0.64, 1) both;
}
.cs-just-unlocked .csv-btn {
  animation: csv-drop 520ms cubic-bezier(0.34, 1.45, 0.64, 1) both;
}

/* ── Full-page password modal (opened from the pill) ── */
.cs-lock-modal {
  position: fixed;
  inset: 0;
  z-index: 100;
  display: none;
  align-items: center;
  justify-content: center;
  padding: var(--sp-6);
}
.cs-lock-modal.is-open { display: flex; }
.cs-lock-modal-backdrop {
  position: absolute;
  inset: 0;
  background: color-mix(in srgb, var(--color-bg) 58%, transparent);
  backdrop-filter: blur(10px);
  -webkit-backdrop-filter: blur(10px);
}
.cs-lock-modal-card {
  position: relative;
  width: min(400px, 100%);
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--sp-3);
  padding: var(--sp-8);
  background: var(--color-bg);
  border: 1px solid var(--color-border);
  border-radius: 14px;
  box-shadow: 0 18px 60px rgba(0, 0, 0, 0.35);
  text-align: center;
}
.cs-lock-modal-card .cs-gate-lock { margin-bottom: var(--sp-2); }
.cs-lock-modal-card .cs-gate-title { margin-bottom: 0; }
.cs-lock-modal-card .cs-gate-note { max-width: 32ch; }
.cs-lock-modal-card .cs-gate-form { width: 100%; margin-top: var(--sp-3); }
.cs-lock-modal-close {
  position: absolute;
  top: var(--sp-3);
  right: var(--sp-3);
  width: 30px; height: 30px;
  display: flex; align-items: center; justify-content: center;
  border-radius: 50%;
  border: 1px solid var(--color-border);
  background: transparent;
  color: var(--color-text-muted);
  cursor: pointer;
  font-size: 15px;
  line-height: 1;
  transition: color 120ms ease;
}
.cs-lock-modal-close:hover { color: var(--color-text); }
@media (prefers-reduced-motion: no-preference) {
  .cs-lock-modal.is-open .cs-lock-modal-backdrop { animation: cs-modal-fade 180ms ease both; }
  .cs-lock-modal.is-open .cs-lock-modal-card { animation: cs-modal-pop 220ms cubic-bezier(0.34, 1.3, 0.64, 1) both; }
  .cs-lock-modal-card.is-shaking { animation: cs-gate-shake 320ms ease; }
}
@keyframes cs-modal-fade { from { opacity: 0; } to { opacity: 1; } }
@keyframes cs-modal-pop {
  from { transform: translateY(10px) scale(0.96); opacity: 0; }
  to   { transform: none; opacity: 1; }
}
html.cs-modal-open, html.cs-modal-open body { overflow: hidden; }

.cs-gate-lock {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 44px;
  height: 44px;
  border-radius: 50%;
  background: var(--color-bg);
  border: 1px solid var(--color-border);
  color: var(--color-primary);
  margin-bottom: var(--sp-6);
}

.cs-gate-title {
  margin-bottom: var(--sp-3);
}

.cs-gate-note {
  font-size: var(--text-sm);
  line-height: var(--leading-body);
  color: var(--color-text-muted);
  max-width: 34ch;
}

.cs-gate-form {
  display: flex;
  gap: var(--sp-3);
  margin-top: var(--sp-6);
}

.cs-gate-input {
  flex: 1;
  min-width: 0;
  padding: var(--sp-3) var(--sp-4);
  font-family: inherit;
  font-size: var(--text-base);
  color: var(--color-text);
  background: var(--color-bg);
  border: 1px solid var(--color-border);
  border-radius: 8px;
  outline: none;
  transition: border-color 120ms ease, box-shadow 120ms ease;
}
.cs-gate-input:focus {
  border-color: var(--color-primary);
  box-shadow: 0 0 0 3px var(--color-primary-muted);
}

/* Sizing/color come from the shared .btn .btn--md (style.css); this just
   keeps the button from shrinking in the input+button flex row. */
.cs-gate-btn { flex-shrink: 0; }
.cs-gate-form.is-checking { opacity: 0.6; pointer-events: none; }

.cs-gate-error {
  margin-top: var(--sp-4);
  font-family: var(--font-mono);
  font-size: var(--text-2xs);
  letter-spacing: var(--tracking-wide);
  color: var(--color-primary);
}

@keyframes cs-gate-shake {
  0%, 100% { transform: translateX(0); }
  20% { transform: translateX(-6px); }
  40% { transform: translateX(6px); }
  60% { transform: translateX(-4px); }
  80% { transform: translateX(4px); }
}

/* ═════════════════════════════════════════
   TWO-COLUMN LAYOUT  (sticky TOC rail + content)
   Used by pages that opt in with .cs-shell.
   Scoped so it never touches the single-column
   .cs-container pages (decision-module, on-demand).
═════════════════════════════════════════ */
.cs-shell {
  max-width: 1160px;
  margin: 0 auto;
  padding: var(--sp-16) var(--sp-8) 0;
  display: grid;
  grid-template-columns: 200px minmax(0, 1fr);
  gap: var(--sp-16);
}

/* ── LEFT RAIL: back link + table of contents ── */
.cs-rail {
  position: sticky;
  top: var(--sp-10);
  align-self: start;
  height: max-content;
}

/* Type comes from .b-label-link — this rule is layout only. */
.cs-rail-back {
  display: inline-flex;
  align-items: center;
  gap: var(--sp-2);
  margin-bottom: var(--sp-12);
}
.cs-rail-back svg {
  width: 13px;
  height: 13px;
  fill: none;
  stroke: currentColor;
  stroke-width: 1.5;
  stroke-linecap: round;
  stroke-linejoin: round;
}

.cs-toc {
  display: flex;
  flex-direction: column;
  gap: var(--sp-4);
}
/* Type comes from .b-label-link — this rule is layout + active state only. */
.cs-toc-link {
  line-height: 1.2;
}
.cs-toc-link.is-active { color: var(--color-text); }

/* ── BACKSTORY TOGGLE — reveals annotations.css's .ann/.ann-note
   strike + note, plus the .cs-toc-note rail breadcrumb. A quieter,
   more playful chip than .btn — this is a low-emphasis aside
   control, not a primary CTA. */
.cs-backstory-toggle {
  display: inline-flex;
  align-items: center;
  gap: var(--sp-2);
  margin-top: var(--sp-6);
  padding: var(--sp-2) var(--sp-3);
  font-family: var(--font-mono);
  font-size: var(--text-2xs);
  letter-spacing: var(--tracking-wider);
  text-transform: uppercase;
  color: var(--color-text-muted);
  background: none;
  border: 1px solid var(--color-border);
  border-radius: 6px;
  cursor: pointer;
  transition: color 160ms ease, border-color 160ms ease, background 160ms ease;
}
.cs-backstory-toggle:hover { color: var(--color-text); border-color: var(--color-text-muted); }
.cs-backstory-toggle[aria-pressed="true"] {
  color: var(--color-primary);
  border-color: var(--color-primary);
  background: var(--color-primary-muted);
}
.cs-backstory-glyph { font-size: 11px; line-height: 1; }

/* TOC index stagger — after a home→case-study view transition the
   individual index items pop in one-by-one ("papapapapa"). Applied by
   case-study.js via .from-transition on <body>. */
.from-transition .cs-toc-link {
  opacity: 0;
  transform: scale(0.6) translateY(-6px);
  animation: toc-pop 300ms cubic-bezier(0.65, 0, 0.35, 1) both;
}
/* Delays set dynamically by staggerToc() in case-study.js */
@keyframes toc-pop {
  from { transform: scale(0.6) translateY(-6px); opacity: 0; }
  to   { transform: scale(1) translateY(0); opacity: 1; }
}

/* ── RAIL VIDEO AVATAR — playable project overview ──
   The homepage identity avatar, brought into the case-study rail and made
   playable. Sits below "Back to work", above the TOC. Click → plays in
   place, stays a circle, no fullscreen; the rail is sticky so it rides
   along while you skim. Markup injected by case-study.js (buildRail).
   Explicit height lets the entrance collapse it to 0 and grow it back,
   which is what pushes the TOC down. */
.csv {
  margin: 0 0 var(--sp-12);
  width: max-content;
  height: 100px;
}
.csv-btn {
  position: relative;
  display: block;
  width: 100px;
  height: 100px;
  padding: 0;
  border: 0;
  border-radius: 50%;
  background: none;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
  transition: transform 320ms cubic-bezier(0.22, 1, 0.36, 1);
}
.csv-btn:hover { transform: scale(1.04); }
.csv-btn:focus-visible { outline: 2px solid var(--color-text); outline-offset: 4px; }
.csv-video {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
  border-radius: 50%;
  border: 1px solid var(--color-border);
  background: var(--color-surface);
}
/* Play affordance — a translucent disc + triangle; fades once playing. */
.csv-play {
  position: absolute;
  inset: 0;
  display: grid;
  place-items: center;
  border-radius: 50%;
  background: rgba(0, 0, 0, 0.28);
  transition: opacity 260ms ease, background 260ms ease;
}
.csv-play::before {
  content: "";
  width: 34px;
  height: 34px;
  border-radius: 50%;
  background: rgba(255, 255, 255, 0.92);
  box-shadow: 0 2px 10px rgba(0, 0, 0, 0.25);
}
.csv-play::after {
  content: "";
  position: absolute;
  margin-left: 3px;   /* nudge the play triangle to feel optically centred */
  border-style: solid;
  border-width: 7px 0 7px 12px;
  border-color: transparent transparent transparent #141414;
}
.csv-btn:hover .csv-play { background: rgba(0, 0, 0, 0.12); }
/* Progress ring — filled with a composited Web Animations API animation
   that completes in exactly the video's duration (no jitter). */
.csv-ring {
  position: absolute;
  inset: -6px;
  width: calc(100% + 12px);
  height: calc(100% + 12px);
  transform: rotate(-90deg);
  pointer-events: none;
  opacity: 0;
  transition: opacity 260ms ease;
}
.csv-ring circle {
  fill: none;
  stroke: var(--color-primary);
  stroke-width: 2.5;
  stroke-linecap: round;
  /* stroke-dasharray / -dashoffset set from the real circumference in JS */
}
.csv[data-state="playing"] .csv-play { opacity: 0; pointer-events: none; }
.csv[data-state="playing"] .csv-ring,
.csv[data-state="paused"]  .csv-ring { opacity: 1; }
.csv[data-state="paused"]  .csv-play { background: rgba(0, 0, 0, 0.18); }

/* ENTRANCE (only when arriving from home, via .from-transition): the avatar
   takes up NO space during the TOC "tuk-tuk" stagger, so the index sits in
   its natural spot right under "Back to work". Then its slot grows — pushing
   the whole TOC down — as the circle drops into place, landing last. Both
   share one JS-set delay, applied after the last TOC item lands. */
.from-transition .csv {
  overflow: hidden;   /* clip the circle to the opening slot */
  animation: csv-open 520ms cubic-bezier(0.34, 1.45, 0.64, 1) both;
}
.from-transition .csv-btn {
  animation: csv-drop 520ms cubic-bezier(0.34, 1.45, 0.64, 1) both;
}
@keyframes csv-open {
  0%   { height: 0; margin-bottom: 0; }
  100% { height: 100px; margin-bottom: var(--sp-12); }
}
@keyframes csv-drop {
  0%   { transform: translateY(-104px); opacity: 0; }
  28%  { opacity: 1; }
  100% { transform: translateY(0); opacity: 1; }
}
@media (prefers-reduced-motion: reduce) {
  .csv-btn, .csv-play, .csv-ring { transition: none; }
  .from-transition .csv,
  .from-transition .csv-btn { animation: none; overflow: visible; height: auto; }
}

/* Smooth anchor jumps on the TOC (only on these pages) */
@media (prefers-reduced-motion: no-preference) {
  html:has(.cs-page) { scroll-behavior: smooth; }
}

/* ── CONTENT COLUMN ── */
.cs-content {
  min-width: 0;
  max-width: 820px;
}

/* Hero + figures fill the content column — no page breakout here */
.cs-content .cs-hero-img-wrap {
  margin: var(--sp-10) 0 0;
}
.cs-content .cs-figure {
  margin-left: 0;
  margin-right: 0;
  --_breakout: 0px;
}

/* ── PROJECT META (Type / Highlights / Objective) ── */
.cs-meta {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: var(--sp-6);
  margin-top: var(--sp-10);
}
.cs-meta > div {
  display: flex;
  flex-direction: column;
  gap: var(--sp-3);
}
.cs-meta dt {
  font-family: var(--font-mono);
  font-size: var(--text-xs);
  font-weight: 500;
  letter-spacing: var(--tracking-wider);
  text-transform: uppercase;
  color: var(--color-text-muted);
}
.cs-meta dd {
  font-size: var(--text-base);
  color: var(--color-text);
}

/* Anchor offset so sections clear the top edge on jump */
.cs-content .cs-section {
  scroll-margin-top: var(--sp-8);
}
/* First section sits right under the meta block */
.cs-content .cs-section:first-of-type {
  padding-top: var(--sp-16);
  margin-top: var(--sp-16);
  border-top: 1px solid var(--color-border);
}

/* ═════════════════════════════════════════
   TWO-COLUMN RESPONSIVE
═════════════════════════════════════════ */
@media (max-width: 900px) {
  .cs-shell {
    grid-template-columns: minmax(0, 1fr);
    gap: 0;
    padding-top: var(--sp-10);
  }
  .cs-rail {
    position: static;
    margin-bottom: var(--sp-8);
  }
  .cs-rail-back { margin-bottom: var(--sp-6); }
  .cs-toc { display: none; }   /* TOC folds away on small screens */
  .cs-backstory-toggle { display: none; }   /* rail is gone, so is the toggle for it */
  /* Avatar sticks to the top on mobile so the reader's face stays
     visible while they scroll through the case study. */
  .csv {
    position: sticky;
    top: 0;
    z-index: 5;
    background: var(--color-bg);
    padding: var(--sp-3) 0;
    margin-bottom: 0;
  }
  .cs-content { max-width: none; }
  .cs-meta { grid-template-columns: 1fr 1fr; }
}


/* ─────────────────────────────────────────
   RESPONSIVE  (single-column pages)
───────────────────────────────────────── */
@media (max-width: 768px) {
  .cs-nav {
    padding: 0 var(--sp-6);
  }

  .cs-nav-title {
    display: none;
  }

  .cs-container {
    padding-left: var(--sp-6);
    padding-right: var(--sp-6);
  }

  .cs-hero {
    padding: var(--sp-12) 0 var(--sp-8);
  }

  .cs-img-pair {
    grid-template-columns: 1fr;
  }

  .cs-outcomes {
    grid-template-columns: 1fr 1fr;
  }

  .cs-footer-nav {
    gap: var(--sp-4);
  }
}

@media (max-width: 400px) {
  .cs-container {
    padding-left: var(--sp-5);
    padding-right: var(--sp-5);
  }

  .cs-outcomes {
    grid-template-columns: 1fr;
  }
}

/* ═══════════════════════════════════════════════════════════
   VERTICAL RHYTHM  ·  shared by every case study
   ───────────────────────────────────────────────────────────
   Flow model: every vertical gap is owned by the NEXT element's
   margin-top, set by the flow container (.cs-content / .cs-section),
   so each neighbour pair has one defined value and nothing collapses
   to zero. The values below are dialled per-transition with the dev
   spacing dial kit (dev/spacing-tuner.js, localhost + ?tune=1) and
   pasted back here — one rule per adjacency, retune anytime.
   ═══════════════════════════════════════════════════════════ */

/* Scaffolding the dial kit relies on but doesn't itself emit. */
.cs-content > * + *  { margin-top: 64px; }   /* header-zone fallback    */
.cs-section > * + *  { margin-top: 16px; }   /* reading-rhythm fallback */
.cs-section > .b-section-header { margin-bottom: 0; }
/* The eyebrow + sub-labels ship margin-bottom: var(--sp-5) (20px), which
   would collapse with and floor the following gap — zero it so the rules
   below own each space in full (this is what lets eyebrow→title reach 4px). */
.cs-content > .b-label,
.cs-section > .b-label { margin-bottom: 0; }

/* Header */
.cs-content > h1                { margin-top: 4px; }
.cs-content > .cs-hero-img-wrap { margin-top: 64px; }
.cs-content > .cs-hero-desc     { margin-top: 24px; }
.cs-content > .cs-meta          { margin-top: 24px; }

/* Chapters — a 64/64 void with the divider hairline centred. */
.cs-content > .cs-section {
  margin-top: 64px;
  padding-top: 64px;
  border-top: 1px solid var(--color-border);
}

/* Text */
.cs-section > .b-section-header + * { margin-top: 24px; }
.b-body + .b-body                   { margin-top: 16px; }

/* Figures */
.cs-section > .cs-figure     { margin-top: 64px; margin-bottom: 0; }
.cs-section > .cs-figure + * { margin-top: 64px; }
.cs-figcaption               { margin-top: 16px; }

/* Callout */
.cs-section > .cs-callout     { margin-top: 64px; }
.cs-section > .cs-callout + * { margin-top: 64px; }

/* Stats */
.cs-section > .cs-outcomes     { margin-top: 64px; }
.cs-section > .cs-outcomes + * { margin-top: 64px; }

/* List */
.cs-section > .b-label     { margin-top: 24px; }
.cs-section > .cs-list     { margin-top: 16px; }
.cs-section > .cs-list + * { margin-top: var(--sp-6); }

/* Graceful degradation if a hero image fails to load (see onerror on
   .cs-hero-img in each case-study page). */
.cs-hero-img-wrap--missing {
  background: var(--color-surface);
  min-height: 240px;
}
.cs-hero-img-wrap--missing .cs-hero-img { display: none; }
