@import './tokens.css';

/* ── Case study page wrapper ────────────
 * Mirrors .main gutter (var(--space-12)) and .site-menu so the content
 * grid lines up edge-for-edge across page, menu, and audit overlays.
 * No max-width — the page fills the viewport on ultrawide monitors,
 * matching the menu's full-bleed surface. Text columns inside the
 * sections stay readable via their own max-widths. */
.cs-page {
  padding: var(--space-20) var(--space-12);
}

/* In-page anchor offset is handled site-wide by the [id] rule in
 * styles.css (clears the fixed header + --space-8). No case-study
 * override needed. */

/* ── Back navigation ─────────────────── */
.cs-back {
  display: inline-block;
  font-size: var(--font-size-caption);
  line-height: var(--line-height-caption);
  color: var(--color-text-tertiary);
  font-weight: var(--font-light);
  letter-spacing: var(--letter-spacing-caption);
  margin-bottom: var(--space-8);
  text-decoration: none;
  font-variant-numeric: tabular-nums;
  transition: color 260ms var(--ease-smooth);
}

.cs-back:hover {
  color: var(--color-text-primary);
}

/* ── Intro ───────────────────────────── */
.cs-intro {
  margin-bottom: var(--space-16);
}

.cs-intro-head {
  display: flex;
  justify-content: space-between;
  align-items: flex-start;
  gap: var(--space-8);
  margin-bottom: var(--space-10);
}

.cs-intro-text {
  flex: 1;
  min-width: 0;
}

.cs-category {
  font-size: var(--font-size-caption);
  line-height: var(--line-height-caption);
  color: var(--color-text-tertiary);
  font-weight: var(--font-light);
  letter-spacing: 0.06em;
  margin-bottom: var(--space-1);
}

.cs-title {
  font-size: var(--font-size-display-l);
  line-height: var(--line-height-display-l);
  letter-spacing: var(--letter-spacing-display-l);
  color: var(--color-text-heading);
  font-weight: var(--font-book);
  margin-top: var(--space-8);
  margin-bottom: var(--space-1);
}

.cs-tagline {
  font-size: var(--font-size-h1);
  line-height: var(--line-height-h1);
  letter-spacing: var(--letter-spacing-h1);
  color: var(--color-text-secondary);
  font-weight: var(--font-book);
  text-wrap: pretty;
}

.cs-published {
  display: block;
  font-size: var(--font-size-caption);
  line-height: var(--line-height-caption);
  color: var(--color-text-tertiary);
  font-weight: var(--font-light);
  margin-top: var(--space-4);
}

/* ── Metadata list ───────────────────── */
.cs-meta-list {
  list-style: none;
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
  margin-bottom: var(--space-8);
}

.cs-meta-list li {
  font-size: var(--font-size-body);
  line-height: var(--line-height-body);
  letter-spacing: var(--letter-spacing-body);
  color: var(--color-text-tertiary);
  font-weight: var(--font-weight-body);
}

.cs-meta-key {
  font-weight: var(--font-semi-bold);
  color: var(--color-text-primary);
}

/* ── CTAs ────────────────────────────── */
.cs-ctas {
  display: flex;
  align-items: center;
  gap: var(--space-1);
  margin-top: var(--space-1);
}

/* ── Table of contents ───────────────── */
.cs-toc {
  margin-bottom: var(--space-16);
}

.cs-toc-label {
  font-size: var(--font-size-caption);
  line-height: var(--line-height-caption);
  letter-spacing: var(--letter-spacing-caption);
  color: var(--color-text-tertiary);
  font-weight: var(--font-weight-caption);
  margin-bottom: var(--space-2);
}

.cs-toc-list {
  list-style: none;
  border-top: 1px solid var(--color-border-subtle);
}

.cs-toc-item {
  border-bottom: 1px solid var(--color-border-subtle);
}

.cs-toc-item a {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: var(--space-1) 0;
  color: var(--color-text-tertiary);
  font-size: var(--font-size-body-large);
  line-height: var(--line-height-body-large);
  letter-spacing: var(--letter-spacing-body-large);
  font-weight: var(--font-weight-body-large);
  text-decoration: none;
  transition: color 260ms var(--ease-smooth);
  font-variant-numeric: tabular-nums;
}

.cs-toc-item a:hover {
  color: var(--color-text-primary);
}

.cs-toc-name {
  flex: 1;
}

.cs-toc-num {
  color: var(--color-text-tertiary);
  flex-shrink: 0;
  padding-left: var(--space-8);
}

/* ── Hero image ──────────────────────── */
.cs-hero {
  margin-bottom: var(--space-16);
  background: var(--color-surface-tertiary);
  border-radius: var(--radius-sharp);
  aspect-ratio: 16 / 7;
  overflow: hidden;
}

.cs-hero-img {
  width: 100%;
  height: 100%;
  object-fit: cover;
}

/* ── Lede ────────────────────────────── */
.cs-lede {
  font-size: var(--font-size-display-s);
  line-height: 1.4;
  letter-spacing: -0.02em;
  color: var(--color-text-heading);
  font-weight: var(--font-weight-display-s);
  max-width: 880px;
  margin-bottom: var(--space-20);
}

/* ── Body ────────────────────────────── */
.cs-body {
  display: flex;
  flex-direction: column;
}

/* ── Section ─────────────────────────── */
/* Section divider rule matches the body text column width (--cs-col),
 * not the full section width — see responsive overrides below. */
.cs-section {
  --cs-col: 720px;
  padding-bottom: var(--space-24);
}

.cs-section::before {
  content: "";
  display: block;
  width: var(--cs-col);
  max-width: 100%;
  height: 1px;
  background: var(--color-border-default);
  margin-bottom: var(--space-24);
}

/* The brand-immersive's own vignette closes the band visually, so any
 * section that follows one shouldn't paint a second divider on top.
 * Two cases: (1) the hero immersive sits before the body, so the very
 * first section in the body is the one to suppress; (2) an in-section
 * immersive ends a section, in which case the next section is the one
 * to suppress. */
.cs-page:has(.cs-brand-immersive) .cs-body > .cs-section:first-child::before,
.cs-section:has(> .cs-section-content > .cs-brand-immersive:last-child) + .cs-section::before {
  display: none;
}

.cs-section-header {
  margin-bottom: var(--space-8);
}

.cs-section-heading {
  font-size: var(--font-size-h1);
  line-height: var(--line-height-h1);
  letter-spacing: var(--letter-spacing-h1);
  color: var(--color-text-heading);
  font-weight: var(--font-weight-h1-emphasized);
  margin-bottom: 0;
}

.cs-section-subtitle {
  font-size: var(--font-size-small);
  line-height: var(--line-height-small);
  color: var(--color-text-tertiary);
  font-weight: var(--font-light);
  letter-spacing: var(--letter-spacing-small);
}

.cs-section-content {
  display: flex;
  flex-direction: column;
  gap: 0;
}

/* Text-only children stay in a readable column; stat cards / media span full width */
.cs-section-content > p,
.cs-section-content > .cs-sub-heading,
.cs-section-content > .cs-list,
.cs-section-content > .cs-quote,
.cs-section-content > .cs-note,
.cs-section-content > .cs-cta-row,
.cs-section-content > .cs-flow-strip {
  max-width: 720px;
}

/* Default rhythm: 0.75× body line-height between content blocks */
.cs-section-content > * + * {
  margin-top: var(--space-5);
}

/* Media is a major content break — breathe like a sub-heading break.
 * The image→caption pair stays tight (caption sets its own margin-top). */
.cs-section-content > * + .cs-media,
.cs-section-content > .cs-media + *:not(.cs-media-caption) {
  margin-top: var(--space-10);
}

.cs-section-content p {
  font-size: var(--font-size-body);
  line-height: var(--line-height-body);
  letter-spacing: var(--letter-spacing-body);
  color: var(--color-text-primary);
  font-weight: var(--font-book);
}

/* ── Sub-headings ────────────────────── */
/* More breath before a new sub-heading to group it with what follows */
.cs-sub-heading {
  font-size: var(--font-size-h3);
  line-height: var(--line-height-h3);
  letter-spacing: var(--letter-spacing-h3);
  color: var(--color-text-heading);
  font-weight: var(--font-semi-bold);
  margin-top: var(--space-10);
}

.cs-sub-heading:first-child {
  margin-top: 0;
}

/* Tighten the heading to its own content — they belong together */
.cs-section-content > .cs-sub-heading + * {
  margin-top: var(--space-1);
}

/* Smaller sub-heading variant — used for CV entries (role + employer, degree + school) */
.cs-sub-heading--sm {
  font-size: var(--font-size-body-large);
  line-height: var(--line-height-body-large);
  letter-spacing: var(--letter-spacing-body-large);
  font-weight: var(--font-weight-body-large-emphasized);
  margin-top: var(--space-8);
}

/* Meta line that sits directly under a smaller heading: dates, location */
.cs-role-meta {
  font-size: var(--font-size-small);
  line-height: var(--line-height-small);
  letter-spacing: var(--letter-spacing-small);
  color: var(--color-text-tertiary);
  font-weight: var(--font-light);
}

.cs-section-content > .cs-sub-heading--sm + .cs-role-meta {
  margin-top: var(--space-1);
}

.cs-section-content > .cs-role-meta + * {
  margin-top: var(--space-3);
}

/* ── Lists ───────────────────────────── */
.cs-list {
  list-style: none;
  display: flex;
  flex-direction: column;
  gap: var(--space-1);
  padding-left: 0;
}

.cs-list li {
  font-size: var(--font-size-body);
  line-height: var(--line-height-body);
  letter-spacing: var(--letter-spacing-body);
  color: var(--color-text-primary);
  font-weight: var(--font-weight-body);
  padding-left: var(--space-3);
  position: relative;
}

.cs-list li::before {
  content: '•';
  position: absolute;
  left: 0;
  color: var(--color-text-tertiary);
}

.cs-list li strong,
.cs-section-content p strong {
  font-weight: var(--font-weight-body-emphasized);
}

/* ── Stats ─────────────────────────────
 * Display-scale number, sentence-case overline, and a hairline rule above the
 * label with two endpoint ticks. A small marker travels left → right along
 * the rule when the card enters the viewport, eased to match the count-up
 * (1400ms ease-out-cubic, same as the JS animator in artpilot.html). */
.cs-stats {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: var(--space-4);
}

.cs-stat {
  display: flex;
  flex-direction: column;
  padding: var(--space-6);
  background: var(--color-surface-secondary);
  border-radius: var(--radius-sharp);
  min-height: 272px;
  /* Card reveal — fades + slides in from the left and pops slightly. The
   * stagger lives in the count-up IO (artpilot.html), which adds .is-counted
   * 150ms apart per card, so the row composes L→R. */
  opacity: 0;
  transform: translateX(-8px) scale(0.96);
  transform-origin: left center;
  transition:
    opacity 600ms cubic-bezier(0.22, 0.61, 0.36, 1),
    transform 600ms cubic-bezier(0.22, 0.61, 0.36, 1);
}

.cs-stat.is-counted {
  opacity: 1;
  transform: translateX(0) scale(1);
}

@media (prefers-reduced-motion: reduce) {
  .cs-stat {
    opacity: 1;
    transform: none;
    transition: none;
  }
}

/* Sentence-case overline. Pulled from data-key on the .cs-stat element. */
.cs-stat::before {
  content: attr(data-key);
  font-size: var(--font-size-body);
  line-height: var(--line-height-body);
  letter-spacing: var(--letter-spacing-body);
  color: var(--color-text-heading);
  font-weight: var(--font-semi-bold);
}

.cs-stat-icon { display: none; }

.cs-stat-value {
  font-size: var(--font-size-display-m);
  line-height: var(--line-height-display-m);
  letter-spacing: var(--letter-spacing-display-m);
  color: var(--color-text-heading);
  font-weight: var(--font-book);
  font-variant-numeric: tabular-nums;
  margin-top: auto;
}

.cs-stat-label {
  position: relative;
  align-self: stretch;
  width: 100%;
  margin-top: var(--space-16);
  padding-top: var(--space-4);
  border-top: 1px solid var(--color-border-default);
  font-size: var(--font-size-caption);
  line-height: var(--line-height-caption);
  letter-spacing: var(--letter-spacing-caption);
  color: var(--color-text-primary);
  font-weight: var(--font-light);
}

/* Endpoint ticks at the rule's left and right ends. Same 1×9 bar; ::before is
 * left, ::after is right. */
.cs-stat-label::before,
.cs-stat-label::after {
  content: "";
  position: absolute;
  top: -4px;
  width: 1px;
  height: 9px;
  background: var(--color-text-primary);
}
.cs-stat-label::before { left: 0; }
.cs-stat-label::after { right: 0; }

/* Travelling marker. 5×5 square centred on the rule. The marker's right edge
 * lands at `--p` (0–1) of the rule's width — so a 78 reading parks at 78% of
 * the rule, an 80% at 80%, and a 100% docks inside the right-end tick. Stagger
 * is handled in the count-up IO (artpilot.html) so the .is-counted class is
 * added with a per-card delay; the transition itself stays in lockstep with
 * the count-up's 1400ms ease-out-cubic. */
.cs-stat-marker {
  position: absolute;
  top: -2px;
  left: 0;
  width: 5px;
  height: 5px;
  background: var(--color-text-primary);
  pointer-events: none;
  transition: left 1400ms cubic-bezier(0.215, 0.61, 0.355, 1);
}
.cs-stat.is-counted .cs-stat-marker {
  left: calc(var(--p, 1) * 100% - 5px);
}

/* Static reference tick — industry-average benchmark on the same rule. Shares
 * the endpoint-tick shape (1×9) but in tertiary colour to read as quieter.
 * Position via `--p-ref` (0–1). A small caption sits above the tick to label
 * the benchmark. Only present on cards that have a meaningful reference. */
.cs-stat-ref {
  position: absolute;
  top: -4px;
  left: calc(var(--p-ref, 0) * 100% - 0.5px);
  width: 1px;
  height: 9px;
  background: var(--color-text-tertiary);
  pointer-events: none;
}
.cs-stat-ref::before {
  content: attr(data-label);
  position: absolute;
  bottom: calc(100% + var(--space-1));
  left: 50%;
  transform: translateX(-50%);
  font-size: var(--font-size-caption);
  line-height: var(--line-height-caption);
  letter-spacing: var(--letter-spacing-caption);
  color: var(--color-text-primary);
  font-weight: var(--font-light);
  white-space: nowrap;
}

@media (prefers-reduced-motion: reduce) {
  .cs-stat-marker { transition: none; }
}

/* Variant: drop the rule, endpoint ticks, and travelling marker. Use when the
 * card reports a standalone magnitude rather than a position on a scale. */
.cs-stats--bare .cs-stat-label {
  border-top: 0;
  padding-top: 0;
}
.cs-stats--bare .cs-stat-label::before,
.cs-stats--bare .cs-stat-label::after,
.cs-stats--bare .cs-stat-marker {
  display: none;
}

/* ── Note ────────────────────────────── */
.cs-section-content .cs-note {
  font-size: var(--font-size-caption);
  line-height: var(--line-height-caption);
  letter-spacing: var(--letter-spacing-caption);
  color: var(--color-text-tertiary);
  font-weight: var(--font-weight-caption);
}

/* ── CTA row ─────────────────────────── */
.cs-cta-row {
  display: flex;
  align-items: center;
  gap: var(--space-4);
}

.cs-cta-row--center {
  justify-content: center;
}

.cs-section-content > .cs-cta-row--center {
  max-width: none;
}

.cs-cta-meta {
  font-size: var(--font-size-caption);
  line-height: var(--line-height-caption);
  letter-spacing: var(--letter-spacing-caption);
  color: var(--color-text-tertiary);
  font-weight: var(--font-light);
}

/* ── Pull quote ──────────────────────── */
.cs-quote {
  font-size: var(--font-size-display-s);
  line-height: 1.4;
  letter-spacing: -0.02em;
  color: var(--color-text-secondary);
  font-weight: var(--font-weight-display-s);
  border-left: 2px solid var(--color-border-subtle);
  padding: var(--space-2) 0 var(--space-2) var(--space-2);
  margin: var(--space-16) 0;
}

/* Cancel the default rhythm gap directly above and below a pull quote
 * so the explicit margins on .cs-quote drive the spacing. */
.cs-section-content > .cs-quote {
  margin-top: var(--space-16);
}
.cs-section-content > .cs-quote + * {
  margin-top: 0;
}
.cs-section-content > .cs-quote:last-child {
  margin-bottom: 0;
}

/* ── Timeline rail ───────────────────────
 * Aligned like a bulleted list: each .cs-timeline-role sits in the text
 * band, padded-left by --space-3, with the dot at the wrapper's left edge
 * (where a list bullet would sit) and the heading text indented past it.
 * A 1px hairline rail connects each dot to the next; the last wrapper
 * has no segment, so the rail never extends past either end. */
.cs-section-content.cs-timeline,
.cs-timeline-role {
  --tl-dot: 5px;
  --tl-gap: var(--space-8);
}

.cs-timeline-role {
  position: relative;
  padding-left: var(--space-5);
}

/* Override the default 20px sibling rhythm with a roomier inter-role gap */
.cs-timeline-role + .cs-timeline-role {
  margin-top: var(--tl-gap);
}

.cs-timeline-role::before {
  content: "";
  position: absolute;
  left: 0;
  top: calc((var(--line-height-body-large) - var(--tl-dot)) / 2);
  width: var(--tl-dot);
  height: var(--tl-dot);
  border-radius: 50%;
  background: var(--color-text-tertiary);
}

/* Rail segment: from this role's dot centre down to the next role's dot
 * centre. Centred on the dot column (dot left = 0, width = --tl-dot, so
 * dot centre = --tl-dot / 2; 1px rail's left = centre − 0.5px). Extends
 * below the wrapper by the inter-role gap plus half a heading line so it
 * lands exactly on the next dot. */
.cs-timeline-role:has(+ .cs-timeline-role)::after {
  content: "";
  position: absolute;
  left: calc(var(--tl-dot) / 2 - 0.5px);
  top: calc(var(--line-height-body-large) / 2);
  bottom: calc(-1 * (var(--tl-gap) + var(--line-height-body-large) / 2));
  width: 1px;
  background: var(--color-border-default);
}

/* Inside a wrapper, restate the rhythm that .cs-section-content > * + *
 * normally provides, since the heading / meta / paragraph are no longer
 * direct children of .cs-section-content. */
.cs-timeline-role > .cs-sub-heading--sm + .cs-role-meta {
  margin-top: var(--space-1);
}

.cs-timeline-role > .cs-role-meta + *,
.cs-timeline-role > .cs-sub-heading--sm + .cs-list,
.cs-timeline-role > .cs-sub-heading--sm + p:not(.cs-role-meta) {
  margin-top: var(--space-2);
}

/* Description blurb sits at caption scale to keep the dates and copy
 * quieter than the role heading. Applies to both paragraph and bullet
 * variants of the role body. */
.cs-timeline-role > p:not(.cs-role-meta),
.cs-timeline-role > .cs-list li {
  font-size: var(--font-size-caption);
  line-height: var(--line-height-caption);
  letter-spacing: var(--letter-spacing-caption);
  font-weight: var(--font-weight-caption);
  color: var(--color-text-tertiary);
}


/* ── Wide-screen article indent ──────────
 * Past ~1600px the article reads left-heavy: the 720px text column sits
 * flush against the left page padding while a wide empty band trails on
 * the right. Shift the article-flow elements one column (1/12 of the page
 * content area) right with a matching right margin so the reading column
 * sits more centred. Full-bleed media (.cs-brand-immersive, .cs-hero) is
 * intentionally untouched. */
@media (min-width: 1600px) {
  .cs-page > .cs-back,
  .cs-page > .cs-intro,
  .cs-page > .cs-toc,
  .cs-page > .cs-lede,
  .cs-body > .cs-section {
    margin-left: calc(100% / 12 * 2);
    margin-right: calc(100% / 12 * 2);
  }
}

/* ── Responsive ──────────────────────── */
@media (max-width: 1199px) {
  /* Match the page chrome gutter (.header / .main / .footer) so the
   * case-study content edge lines up with the footer columns below. */
  .cs-page {
    padding: var(--space-20) var(--space-6);
  }

  .cs-section {
    --cs-col: 640px;
  }

  .cs-section-content > p,
  .cs-section-content > .cs-sub-heading,
  .cs-section-content > .cs-list,
  .cs-section-content > .cs-quote,
  .cs-section-content > .cs-note,
  .cs-section-content > .cs-cta-row,
  .cs-section-content > .cs-flow-strip {
    max-width: 640px;
  }

  .cs-lede {
    max-width: none;
  }
}

@media (max-width: 768px) {
  .cs-page {
    padding: var(--space-20) var(--space-5);
  }

  .cs-title {
    font-size: var(--font-size-display-m);
    line-height: var(--line-height-display-m);
  }

  .cs-intro-head {
    flex-direction: column-reverse;
    gap: var(--space-2);
  }

  .cs-toc-item a {
    font-size: var(--font-size-body);
    line-height: var(--line-height-body);
  }

  /* Below the tablet breakpoint the two CTAs stay side-by-side and each takes
   * half the row (flex: 1 1 0 → equal width regardless of label length), so it
   * reads as 50% "View prototype" / 50% "View outcomes". */
  .cs-ctas {
    flex-direction: row;
    align-items: stretch;
  }

  .cs-ctas .btn {
    flex: 1 1 0;
  }

  .cs-section {
    --cs-col: 100%;
  }

  /* Step the lede + pull quote down from Display S (23px) to H3 (18px) so
   * they stop reading as walls of text on narrow screens. Line-height and
   * tracking stay as the base rules set them. */
  .cs-lede,
  .cs-quote {
    font-size: var(--font-size-h3);
  }

  .cs-section-content > p,
  .cs-section-content > .cs-sub-heading,
  .cs-section-content > .cs-list,
  .cs-section-content > .cs-quote,
  .cs-section-content > .cs-note,
  .cs-section-content > .cs-cta-row,
  .cs-section-content > .cs-flow-strip {
    max-width: none;
  }

}

/* Stats — tablet stacks to a single column but stays a sister to desktop:
 * same display-size number, more padding. The label rule already spans the
 * full card width at every breakpoint. */
@media (max-width: 1024px) {
  .cs-stats {
    grid-template-columns: 1fr;
    gap: var(--space-4);
  }

  .cs-stat {
    padding: var(--space-8);
    min-height: 240px;
  }

  .cs-stat-label {
    margin-top: var(--space-20);
  }
}

/* Mobile — tighten padding so a 375px viewport doesn't crush. Rule keeps its
 * full-width span from the tablet rule above. */
@media (max-width: 640px) {
  .cs-stat {
    padding: var(--space-5);
    min-height: auto;
  }

  .cs-stat-label {
    margin-top: var(--space-12);
  }
}

/* Brand-immersive scroller + Suisse specimen styles live in
 * css/cs-brand-immersive.css — opt-in, paired with cs-brand-immersive.js.
 * Pages without the immersive don't load either. */


/* ── Section media ──────────────────────────────────────────────────────
 * Inline media inside .cs-section-content. Three flavours:
 *   .cs-media          base — flush with the text column (720px)
 *   .cs-media-inline   constrained, with a tertiary surround for static
 *                      images that shouldn't blow out the column
 *   .cs-media-full     breaks out of .cs-page padding to viewport edges
 * Captions sit underneath as <p class="cs-media-caption">. */
.cs-media {
  width: 100%;
  border-radius: var(--radius-sharp);
  overflow: hidden;
  background: var(--color-surface-page);
}

.cs-media img,
.cs-media video {
  width: 100%;
  height: auto;
  display: block;
  object-fit: cover;
}

.cs-media-inline {
  max-width: 720px;
  background: var(--color-surface-page);
}

.cs-media-inline img {
  max-height: 520px;
  object-fit: contain;
  background: var(--color-surface-page);
}

/* Two-up media row. Sits in the spill band like a single .cs-media;
 * children render side by side on desktop, stacked on mobile. Use for
 * before/after or then/now comparisons that read better in parallel
 * than stacked. */
.cs-media-pair {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: var(--space-6);
  background: transparent;
}

.cs-media-pair > .cs-media {
  max-width: none;
  margin: 0;
}

.cs-media-pair > .cs-media img,
.cs-media-pair > .cs-media video {
  max-height: 520px;
  object-fit: contain;
  background: var(--color-surface-page);
}

/* Aspect-ratio container for the Artpilot mark-loop SVG.
 * Matches the homepage case-card media ratio (3/2). Inside a case study
 * the loop sits on the page surface (white) rather than the tertiary
 * grey it uses on the homepage card. */
.cs-media--mark-loop {
  aspect-ratio: 3 / 2;
  overflow: hidden;
}

.cs-media--mark-loop > .ap-mark-loop {
  background: var(--color-surface-page);
}

@media (max-width: 768px) {
  .cs-media-pair {
    grid-template-columns: 1fr;
  }
}

/* Persona — artpilot-only variant of .cs-media-inline. Frames the
 * persona document with a hairline border and pulls it back to the
 * text-band width (6 cols) so it reads as a referenced artefact
 * rather than a hero spill image. Layered on top of .cs-media-inline. */
.cs-media--persona {
  border: 1px solid var(--color-border-default);
  border-radius: var(--radius-sharp);
  overflow: hidden;
}

.cs-media-full {
  margin-left: calc(-1 * var(--space-20));
  width: 100vw;
  border-radius: 0;
}

.cs-media-caption,
p.cs-media-caption {
  font-size: var(--font-size-caption);
  line-height: var(--line-height-caption);
  color: var(--color-text-tertiary);
  font-weight: var(--font-weight-caption);
  letter-spacing: var(--letter-spacing-caption);
  margin-top: var(--space-1);
}

@media (min-width: 1025px) {
  .cs-media-caption--center {
    text-align: center;
  }
}

/* ── Wireframe placeholder ──────────────────────────────────────────────
 * Empty media slot used while real images are still being captured.
 * Renders as a tertiary-surface rectangle with a hairline border and a
 * caption-sized label centered in it (set via data-label). Drop as the
 * child of any .cs-media wrapper — the wrapper controls the band,
 * modifier classes control the aspect ratio. Remove every .cs-placeholder
 * (and the wrapper if it would be left empty) when real images arrive. */
.cs-placeholder {
  display: grid;
  place-items: center;
  width: 100%;
  aspect-ratio: 16 / 9;
  padding: var(--space-4);
  background: var(--color-surface-tertiary);
  border: 1px solid var(--color-border-default);
  border-radius: var(--radius-sharp);
  color: var(--color-text-tertiary);
  font-size: var(--font-size-caption);
  line-height: var(--line-height-caption);
  letter-spacing: var(--letter-spacing-caption);
  font-weight: var(--font-light);
  text-align: center;
}

.cs-placeholder::before {
  content: attr(data-label);
}

.cs-placeholder--16x9     { aspect-ratio: 16 / 9; }
.cs-placeholder--3x2      { aspect-ratio: 3 / 2; }
.cs-placeholder--4x3      { aspect-ratio: 4 / 3; }
.cs-placeholder--1x1      { aspect-ratio: 1 / 1; }
.cs-placeholder--portrait { aspect-ratio: 4 / 5; }

/* Hero variant — fills the .cs-hero-full wrapper. The hero wrapper
 * already paints --color-surface-tertiary; this only hosts the centered
 * label. Remove when a hero <video> element is dropped in. */
.cs-placeholder--hero {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  aspect-ratio: auto;
  background: transparent;
  border: none;
}

/* ── Flow strip ─────────────────────────────────────────────────────────
 * Inline diagram of step labels separated by chevrons. Sits above a
 * prototype demo to telegraph the journey before the user watches it.
 * Caption-sized, light weight, with rule lines above and below to read
 * as a discrete navigational band. */
.cs-flow-strip {
  list-style: none;
  padding: var(--space-3) 0;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-3);
  border-top: 1px solid var(--color-border-default);
  border-bottom: 1px solid var(--color-border-default);
  font-size: var(--font-size-caption);
  line-height: var(--line-height-caption);
  letter-spacing: var(--letter-spacing-caption);
  font-weight: var(--font-light);
  color: var(--color-text-tertiary);
}

.cs-flow-step {
  color: var(--color-text-primary);
}

.cs-flow-sep {
  color: var(--color-text-tertiary);
  user-select: none;
}

/* ── Flow chart ─────────────────────────────────────────────────────────
 * Linear process diagram: numbered square nodes connected by arrows,
 * with a short descriptor under each step. Heavier than .cs-flow-strip
 * — reads as a discrete diagram rather than a label row. Sits in the
 * spill band so five horizontal nodes have room to breathe. */
.cs-flow-chart {
  list-style: none;
  margin: 0;
  padding: var(--space-8) 0;
  border-top: 1px solid var(--color-border-default);
  border-bottom: 1px solid var(--color-border-default);
  display: grid;
  grid-auto-flow: column;
  grid-auto-columns: 1fr;
  align-items: start;
  column-gap: var(--space-2);
}

.cs-flow-node {
  display: grid;
  grid-template-rows: auto auto auto;
  row-gap: var(--space-3);
  justify-items: center;
  text-align: center;
  position: relative;
}

/* Arrow on every node except the last — sits to the right of the
 * numbered tile, vertically centered to its midline. */
.cs-flow-node:not(:last-child)::after {
  content: "→";
  position: absolute;
  top: calc(var(--space-12) / 2);
  right: calc(-1 * var(--space-2));
  transform: translate(50%, -50%);
  font-size: var(--font-size-body);
  line-height: 1;
  color: var(--color-text-tertiary);
  user-select: none;
}

.cs-flow-node-tile {
  width: var(--space-12);
  height: var(--space-12);
  display: grid;
  place-items: center;
  border: 1px solid var(--color-border-default);
  border-radius: var(--radius-sharp);
  background: var(--color-surface-tertiary);
  font-size: var(--font-size-body);
  line-height: 1;
  letter-spacing: var(--letter-spacing-body);
  font-weight: var(--font-weight-body-emphasized);
  color: var(--color-text-primary);
  font-variant-numeric: tabular-nums;
}

.cs-flow-node-name {
  font-size: var(--font-size-body);
  line-height: var(--line-height-body);
  letter-spacing: var(--letter-spacing-body);
  font-weight: var(--font-weight-body-emphasized);
  color: var(--color-text-heading);
}

.cs-flow-node-detail {
  font-size: var(--font-size-caption);
  line-height: var(--line-height-caption);
  letter-spacing: var(--letter-spacing-caption);
  color: var(--color-text-tertiary);
  max-width: 18ch;
}

/* Tablet: keep horizontal but tighten the gap and shrink the tile a
 * touch so five nodes still fit without the descriptors crashing. */
@media (max-width: 1024px) {
  .cs-flow-chart {
    padding: var(--space-6) 0;
    column-gap: var(--space-1);
  }
  .cs-flow-node-tile {
    width: var(--space-10);
    height: var(--space-10);
  }
  .cs-flow-node:not(:last-child)::after {
    top: calc(var(--space-10) / 2);
  }
}

/* Mobile: stack vertically, arrow rotates 90° to point downward. */
@media (max-width: 640px) {
  .cs-flow-chart {
    grid-auto-flow: row;
    grid-auto-columns: auto;
    row-gap: var(--space-6);
  }
  .cs-flow-node {
    grid-template-columns: var(--space-12) 1fr;
    grid-template-rows: auto auto;
    grid-template-areas:
      "tile name"
      "tile detail";
    column-gap: var(--space-4);
    row-gap: var(--space-1);
    justify-items: start;
    text-align: left;
  }
  .cs-flow-node-tile { grid-area: tile; align-self: start; }
  .cs-flow-node-name { grid-area: name; }
  .cs-flow-node-detail { grid-area: detail; max-width: none; }
  .cs-flow-node:not(:last-child)::after {
    content: "↓";
    top: auto;
    bottom: calc(-1 * var(--space-6));
    right: auto;
    left: calc(var(--space-12) / 2);
    transform: translate(-50%, 50%);
  }
}

/* ── Deliverable flow ───────────────────────────────────────────────────
 * Branching diagram: one source splits into two outcome columns
 * (e.g. delivered to client vs archived). Uses CSS borders for the
 * connecting lines so it scales cleanly and themes automatically. */
.cs-deliverable-flow {
  padding: var(--space-8) 0;
  border-top: 1px solid var(--color-border-default);
  border-bottom: 1px solid var(--color-border-default);
  display: grid;
  gap: 0;
}

.cs-df-source {
  justify-self: center;
  text-align: center;
  padding: var(--space-3) var(--space-6);
  border: 1px solid var(--color-border-default);
  border-radius: var(--radius-sharp);
  background: var(--color-surface-tertiary);
  min-width: 200px;
}

.cs-df-source-label {
  display: block;
  font-size: var(--font-size-body);
  line-height: var(--line-height-body);
  letter-spacing: var(--letter-spacing-body);
  font-weight: var(--font-weight-body-emphasized);
  color: var(--color-text-primary);
}

.cs-df-source-detail {
  display: block;
  margin-top: var(--space-1);
  font-size: var(--font-size-caption);
  line-height: var(--line-height-caption);
  letter-spacing: var(--letter-spacing-caption);
  color: var(--color-text-tertiary);
}

.cs-df-split {
  position: relative;
  height: var(--space-12);
  width: 100%;
}

.cs-df-split::before {
  content: "";
  position: absolute;
  left: 50%;
  top: 0;
  width: 1px;
  height: 50%;
  background: var(--color-border-default);
  transform: translateX(-0.5px);
}

.cs-df-split::after {
  content: "";
  position: absolute;
  left: 25%;
  right: 25%;
  top: 50%;
  height: 50%;
  border-top: 1px solid var(--color-border-default);
  border-left: 1px solid var(--color-border-default);
  border-right: 1px solid var(--color-border-default);
}

.cs-df-branches {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: var(--space-6);
}

.cs-df-branch {
  padding: var(--space-5);
  border: 1px solid var(--color-border-default);
  border-radius: var(--radius-sharp);
  background: var(--color-surface-page);
}

.cs-df-branch-eyebrow {
  display: block;
  font-size: var(--font-size-caption);
  line-height: var(--line-height-caption);
  letter-spacing: var(--letter-spacing-caption);
  color: var(--color-text-tertiary);
  margin-bottom: var(--space-2);
}

.cs-df-branch-title {
  margin: 0 0 var(--space-3);
  font-size: var(--font-size-h3);
  line-height: var(--line-height-h3);
  letter-spacing: var(--letter-spacing-h3);
  font-weight: var(--font-weight-h3);
  color: var(--color-text-heading);
}

.cs-df-list {
  list-style: none;
  padding: 0;
  margin: 0 0 var(--space-4);
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
}

.cs-df-list li {
  font-size: var(--font-size-body);
  line-height: var(--line-height-body);
  letter-spacing: var(--letter-spacing-body);
  color: var(--color-text-secondary);
  padding-left: var(--space-4);
  position: relative;
}

.cs-df-list li::before {
  content: "";
  position: absolute;
  left: 0;
  top: 0.6em;
  width: var(--space-2);
  height: 1px;
  background: var(--color-border-default);
}

.cs-df-meta {
  margin: 0;
  padding-top: var(--space-3);
  border-top: 1px solid var(--color-border-default);
  font-size: var(--font-size-caption);
  line-height: var(--line-height-caption);
  letter-spacing: var(--letter-spacing-caption);
  color: var(--color-text-tertiary);
}

@media (max-width: 1024px) {
  .cs-deliverable-flow {
    padding: var(--space-6) 0;
  }
  .cs-df-source {
    min-width: 0;
    width: 100%;
    max-width: 360px;
  }
  .cs-df-split {
    height: var(--space-6);
  }
  .cs-df-split::before {
    height: 100%;
  }
  .cs-df-split::after {
    content: none;
  }
  .cs-df-branches {
    grid-template-columns: 1fr;
    gap: 0;
  }
  .cs-df-branch + .cs-df-branch {
    margin-top: var(--space-6);
    position: relative;
  }
  .cs-df-branch + .cs-df-branch::before {
    content: "";
    position: absolute;
    left: 50%;
    top: calc(-1 * var(--space-6));
    width: 1px;
    height: var(--space-6);
    background: var(--color-border-default);
    transform: translateX(-0.5px);
  }
}

/* ── Phone frame ────────────────────────────────────────────────────────
 * Modifier on .cs-media that wraps a portrait prototype clip. The video
 * sits directly on the page surface (no surround) and is capped by
 * viewport height so it never exceeds the laptop viewport. The wrapper
 * shrink-wraps the video and centers within the section's media band. */
.cs-media.cs-phone-frame {
  width: fit-content;
  max-width: 320px;
  background: transparent;
  padding: 0;
  margin-left: auto;
  margin-right: auto;
}

.cs-media.cs-phone-frame video,
.cs-media.cs-phone-frame img {
  width: auto;
  max-width: 100%;
  max-height: 75vh;
  margin-left: auto;
  margin-right: auto;
  object-fit: contain;
  border-radius: var(--radius-sharp);
}

/* Centered-page override: the spill-band rule explicitly margins
 * .cs-media into a 6-col tall band, but the phone shell wants to sit
 * centered within whatever band the section uses. Auto-margin it.
 * Top margin overrides the default --space-5 rhythm to give the phone
 * a little more breathing room below the flow strip. */
.cs-page--centered .cs-section-content > .cs-media.cs-phone-frame {
  margin-top: var(--space-8);
  margin-left: auto;
  margin-right: auto;
}

@media (min-width: 1600px) {
  .cs-page--centered .cs-section-content > .cs-media.cs-phone-frame {
    margin-left: auto;
    margin-right: auto;
  }
}

/* ── Logo grid ──────────────────────────────────────────────────────────
 * Specimen for showing brand-mark variants. The standalone mark is the
 * hero — it sits on its own row at the top of the band and renders
 * large so it reads with weight. Wordmark + full lockup share row 2 as
 * supporting variants. Wordmark/lockup viewBoxes share a height of 210
 * (descender room for the "p"), so their A-glyph baselines align when
 * rendered at the same CSS height. Paths inherit fill from
 * currentColor, so the mark themes with the page surface. */
.cs-logo-grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: var(--space-6);
  width: 100%;
  margin: var(--space-8) 0;
}

.cs-logo-tile {
  display: flex;
  flex-direction: column;
  gap: var(--space-6);
  margin: 0;
  padding: var(--space-12) var(--space-8) var(--space-8);
  background: var(--color-surface-page);
  border: 1px solid var(--color-border-default);
  border-radius: var(--radius-sharp);
  color: var(--color-text-heading);
}

/* Hero mark tile: spans the full band, pads more generously, and
 * renders its SVG taller so the mark reads at proper scale on wide
 * displays. */
.cs-logo-tile:nth-child(1) {
  grid-column: 1 / -1;
  padding: var(--space-16) var(--space-8) var(--space-10);
  align-items: center;
}

.cs-logo-tile:nth-child(1) .cs-logo-tile-art {
  min-height: 160px;
}

.cs-logo-tile:nth-child(1) .cs-logo-svg {
  height: 160px;
}

.cs-logo-tile:nth-child(1) .cs-logo-caption {
  max-width: 520px;
  text-align: center;
}

.cs-logo-tile-art {
  flex: 1 1 auto;
  display: flex;
  align-items: center;
  justify-content: center;
  min-height: 96px;
}

.cs-logo-svg {
  display: block;
  width: auto;
  height: 64px;
  max-width: 100%;
  fill: currentColor;
}

.cs-logo-caption {
  font-size: var(--font-size-caption);
  line-height: var(--line-height-caption);
  letter-spacing: var(--letter-spacing-caption);
  color: var(--color-text-tertiary);
  font-weight: var(--font-light);
  margin: 0;
}

/* On 27"+ monitors the hero mark gets another size bump so it doesn't
 * shrink visually inside the wider band. */
@media (min-width: 1600px) {
  .cs-logo-tile:nth-child(1) .cs-logo-svg {
    height: 200px;
  }
  .cs-logo-tile:nth-child(1) .cs-logo-tile-art {
    min-height: 200px;
  }
}

@media (max-width: 768px) {
  .cs-logo-grid {
    grid-template-columns: 1fr;
    gap: var(--space-10);
  }
  /* Mobile: drop the card treatment entirely. Each specimen lays out like
   * an inline image — art flush-left, caption beneath, no border/fill/pad. */
  .cs-logo-tile,
  .cs-logo-tile:nth-child(1) {
    padding: 0;
    background: none;
    border: 0;
    align-items: flex-start;
    gap: var(--space-3);
  }
  .cs-logo-tile-art,
  .cs-logo-tile:nth-child(1) .cs-logo-tile-art {
    justify-content: flex-start;
    min-height: 0;
    width: 100%;
  }
  .cs-logo-tile:nth-child(1) .cs-logo-caption {
    max-width: none;
    text-align: left;
  }
  /* All three specimens render at one shared scale so the mark glyph (the
   * same paths in every SVG) is pixel-identical across them. The lockup
   * fills the column, mapping its 1660-unit viewBox width to 100%; each
   * other SVG takes width = its-viewBox-width / 1660 of the column. Result:
   * the standalone mark matches the mark inside the wordmark and lockup. */
  .cs-logo-tile:nth-child(3) .cs-logo-svg {
    width: 100%;
    height: auto;
  }
  .cs-logo-tile:nth-child(2) .cs-logo-svg {
    width: calc(100% * 905 / 1660);
    height: auto;
  }
  .cs-logo-tile:nth-child(1) .cs-logo-svg {
    width: calc(100% * 175 / 1660);
    height: auto;
  }
}

/* ── Zoomable image affordance ──────────────────────────────────────────
 * Apply to a .cs-media wrapper alongside data-lightbox / data-caption to
 * mark an image as expandable. Hover reveals the "Click to enlarge" pill;
 * the lightbox JS does the actual open/close. */
.cs-zoomable {
  cursor: zoom-in;
  position: relative;
  transition: transform var(--duration-base) var(--ease-smooth);
}

.cs-zoomable:hover {
  transform: translateY(-1px);
}

.cs-zoomable::after {
  content: "Click to enlarge";
  position: absolute;
  bottom: var(--space-3);
  right: var(--space-3);
  padding: 6px 10px;
  font-family: "Suisse Int'l Book", Inter, sans-serif;
  font-size: var(--font-size-caption);
  line-height: var(--line-height-caption);
  letter-spacing: var(--letter-spacing-caption);
  color: var(--color-text-inverse);
  background: rgba(0, 0, 0, 0.6);
  border-radius: var(--radius-sharp);
  opacity: 0;
  transition: opacity var(--duration-base) var(--ease-smooth);
  pointer-events: none;
}

.cs-zoomable:hover::after { opacity: 1; }

/* ── Before/after compare ──────────────────────────────────────────────
 * Two-image comparison slider. The "before" image is in flow and sets
 * the container's height; the "after" image is absolutely positioned
 * and clip-pathed from the left edge to --pos. Pointer-drag + arrow-key
 * handle in case-study.js → initCompareSlider(). */
.cs-compare {
  --pos: 50%;
  position: relative;
  display: block;
  width: 100%;
  overflow: hidden;
  background: var(--color-surface-tertiary);
  touch-action: none;
  user-select: none;
  cursor: ew-resize;
}

.cs-compare-img {
  display: block;
  width: 100%;
  height: auto;
  pointer-events: none;
  user-select: none;
}

.cs-compare-img--before {
  position: relative;
  z-index: 1;
}

.cs-compare-img--after {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  z-index: 2;
  clip-path: inset(0 0 0 var(--pos));
}

.cs-compare-divider {
  position: absolute;
  top: 0;
  bottom: 0;
  left: var(--pos);
  width: 2px;
  background: var(--color-text-inverse);
  transform: translateX(-50%);
  z-index: 3;
  pointer-events: none;
}

.cs-compare-handle {
  position: absolute;
  top: 50%;
  left: var(--pos);
  transform: translate(-50%, -50%);
  width: 44px;
  height: 44px;
  border-radius: var(--radius-sharp);
  background: var(--color-text-inverse);
  border: 1px solid var(--color-border-default);
  color: var(--color-text-primary);
  cursor: ew-resize;
  z-index: 4;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 0;
  font-family: "Suisse Int'l Book", Inter, sans-serif;
  font-size: var(--font-size-small);
  line-height: 1;
  font-variant-numeric: tabular-nums;
  letter-spacing: 0;
}

.cs-compare-handle:focus-visible {
  outline: 2px solid var(--color-border-focus);
  outline-offset: 3px;
}

.cs-compare-label {
  position: absolute;
  top: var(--space-4);
  font-size: var(--font-size-caption);
  line-height: var(--line-height-caption);
  letter-spacing: var(--letter-spacing-caption);
  color: var(--color-text-inverse);
  background: rgba(0, 0, 0, 0.55);
  padding: var(--space-1) var(--space-3);
  border-radius: var(--radius-sharp);
  z-index: 3;
  pointer-events: none;
  text-transform: none;
}

.cs-compare-label--before {
  left: var(--space-4);
}

.cs-compare-label--after {
  right: var(--space-4);
}

/* Mobile: the natural-ratio frame collapses to a tiny strip at phone
 * width. Instead, give it a portrait frame that fills the viewport
 * height, capping the aspect at 5:7 so it never becomes a skinny
 * sliver. Both images cover + crop (the before image stops driving
 * height here and fills the frame like the after image already does). */
@media (max-width: 640px) {
  .cs-compare {
    aspect-ratio: 5 / 7;
    max-height: 100svh;
  }

  .cs-compare-img--before {
    position: absolute;
    inset: 0;
    height: 100%;
    object-fit: cover;
  }
}

/* ── Lightbox overlay ───────────────────────────────────────────────────
 * Paired with the [data-lightbox] markup inside any .cs-zoomable wrapper.
 * Always present in the page shell — the case-study.js initLightbox()
 * binds open/close, and body.is-lightbox-open hides header chrome so the
 * close button is reachable without nav competing. */
.cs-lightbox {
  position: fixed;
  inset: 0;
  background: rgba(0, 0, 0, 0.92);
  z-index: 9999;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  padding: var(--space-12) var(--space-8) var(--space-8);
  gap: var(--space-8);
  opacity: 0;
  pointer-events: none;
  transition: opacity 250ms var(--ease-smooth);
  cursor: zoom-out;
}

.cs-lightbox.is-open {
  opacity: 1;
  pointer-events: auto;
}

.cs-lightbox-img {
  max-width: 95vw;
  max-height: 78vh;
  object-fit: contain;
  display: block;
  transform: scale(0.98);
  transition: transform 350ms var(--ease-smooth);
}

.cs-lightbox.is-open .cs-lightbox-img { transform: scale(1); }

.cs-lightbox-caption {
  max-width: 720px;
  color: rgba(255, 255, 255, 0.85);
  font-family: "Suisse Int'l Book", Inter, sans-serif;
  font-size: var(--font-size-caption);
  line-height: var(--line-height-caption);
  letter-spacing: var(--letter-spacing-caption);
  text-align: center;
  pointer-events: none;
}

.cs-lightbox-close {
  position: absolute;
  top: var(--space-6);
  right: var(--space-6);
  width: 44px;
  height: 44px;
  border-radius: 999px;
  background: var(--color-surface-inverse);
  color: var(--color-text-inverse);
  border: none;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: 0;
  cursor: pointer;
  transition: background 320ms var(--ease-smooth),
              transform 420ms var(--ease-smooth);
}

.cs-lightbox-close:hover { transform: rotate(90deg); }

.cs-lightbox-close:focus-visible {
  outline: none;
  box-shadow: 0 0 0 2px #000,
              0 0 0 4px var(--btn-focus-ring);
}

.cs-lightbox-close-x {
  font-size: 22px;
  line-height: 1;
  font-weight: 300;
  margin-top: -1px;
}

body.is-lightbox-open .header,
body.is-lightbox-open .theme-toggle {
  opacity: 0;
  pointer-events: none;
  transition: opacity 200ms var(--ease-smooth);
}

/* ── Full-screen hero video ─────────────────────────────────────────────
 * Optional opening pattern. Sits behind .cs-page (which scrolls up over
 * it). The header is transparent while the video is on-screen; the
 * case-study.js initHeroVideoFade() toggles the .video-gone class once
 * the user has scrolled past the halfway point so the editorial sections
 * always get the dark menu chrome. */
.cs-hero-full {
  position: fixed;
  top: 0;
  left: 0;
  width: 100%;
  height: 100vh;
  overflow: hidden;
  background: var(--color-surface-tertiary);
  z-index: 50;
  pointer-events: none;
}

.cs-hero-full video,
.cs-hero-full img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}

/* Slideshow hero — stacked, crossfading images with a per-slide Ken
 * Burns motion (zoom-in, zoom-out, pan-left, pan-right, drift-up,
 * drift-down). Motion variant is set per slide via data-motion; CSS
 * custom props feed a single transform expression so each slide drifts
 * in a different direction without per-variant transitions.
 * initHeroSlideshow() in case-study.js drives .is-current and writes
 * the slide caption + counter into .cs-hero-slide-caption /
 * .cs-hero-slide-counter inside .cs-hero-overlay. */
.cs-hero-slideshow {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
}

.cs-hero-slide {
  --kb-fs: 1.04;
  --kb-ts: 1.14;
  --kb-fx: 0%;
  --kb-tx: 0%;
  --kb-fy: 0%;
  --kb-ty: 0%;
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  opacity: 0;
  transform: scale(var(--kb-fs)) translate(var(--kb-fx), var(--kb-fy));
  filter: blur(16px) saturate(0.92);
  transition:
    opacity 2200ms ease-in-out,
    filter 1600ms cubic-bezier(0.22, 1, 0.36, 1);
  will-change: opacity, transform, filter;
}

.cs-hero-slide.is-current {
  opacity: 1;
  transform: scale(var(--kb-ts)) translate(var(--kb-tx), var(--kb-ty));
  filter: blur(0) saturate(1);
  transition:
    opacity 2200ms ease-in-out,
    transform 9500ms cubic-bezier(0.22, 0.61, 0.36, 1),
    filter 2400ms cubic-bezier(0.22, 1, 0.36, 1);
}

.cs-hero-slide[data-motion="zoom-out"]   { --kb-fs: 1.16; --kb-ts: 1.04; }
.cs-hero-slide[data-motion="pan-left"]   { --kb-fs: 1.12; --kb-ts: 1.12; --kb-fx: 3%;  --kb-tx: -3%; }
.cs-hero-slide[data-motion="pan-right"]  { --kb-fs: 1.12; --kb-ts: 1.12; --kb-fx: -3%; --kb-tx: 3%; }
.cs-hero-slide[data-motion="drift-up"]   { --kb-fs: 1.06; --kb-ts: 1.14; --kb-fy: 3%;  --kb-ty: -3%; }
.cs-hero-slide[data-motion="drift-down"] { --kb-fs: 1.14; --kb-ts: 1.06; --kb-fy: -3%; --kb-ty: 3%; }

/* Slide caption (bottom-left) + frame counter (bottom-right). Sit
 * inside .cs-hero-overlay so they hide with the rest of the hero
 * chrome when the user scrolls past. */
.cs-hero-slide-caption,
.cs-hero-slide-counter {
  position: absolute;
  bottom: 0;
  font-size: var(--font-size-caption);
  line-height: var(--line-height-caption);
  letter-spacing: var(--letter-spacing-caption);
  font-weight: var(--font-light);
  color: var(--color-text-inverse);
  opacity: 0.75;
  font-variant-numeric: tabular-nums;
  pointer-events: none;
  margin: 0;
  animation: scroll-cue-enter 900ms cubic-bezier(0.22, 1, 0.36, 1) 2000ms backwards;
}

.cs-hero-slide-caption {
  left: 0;
  max-width: 320px;
}

.cs-hero-slide-counter {
  right: 0;
}

.cs-hero-slide-caption-text,
.cs-hero-slide-counter-num {
  display: inline-block;
  transition: opacity 380ms ease, transform 380ms cubic-bezier(0.22, 1, 0.36, 1);
}

.cs-hero-slide-caption-text.is-leaving,
.cs-hero-slide-counter-num.is-leaving {
  opacity: 0;
  transform: translateY(-4px);
}

.cs-hero-slide-counter-sep,
.cs-hero-slide-counter-total {
  display: inline-block;
  opacity: 0.55;
}
.cs-hero-slide-counter-sep { margin: 0 0.2em; }

@media (prefers-reduced-motion: reduce) {
  .cs-hero-slide,
  .cs-hero-slide.is-current {
    transform: none;
    filter: none;
    transition: opacity 600ms ease;
  }
}

.cs-hero-canvas {
  width: 100%;
  height: 100%;
  display: block;
  background: transparent;
  pointer-events: auto;
  cursor: grab;
  /* pan-y so vertical page scroll passes through on touch devices; mouse
   * drag-to-orbit still works because the JS only acts on pointerType=mouse. */
  touch-action: pan-y;
}

.cs-hero-canvas.is-grabbing {
  cursor: grabbing;
}

.cs-hero-full::after {
  content: "";
  position: absolute;
  inset: 0;
  pointer-events: none;
  background: linear-gradient(
    180deg in oklab,
    rgba(0, 0, 0, 0.18) 0%,
    rgba(0, 0, 0, 0.10) 18%,
    rgba(0, 0, 0, 0.04) 36%,
    rgba(0, 0, 0, 0)    50%
  );
}

/* When a page uses .cs-hero-full, .cs-page slides up over the video and
 * needs to paint its own background so the fixed video doesn't bleed
 * through editorial sections. */
.cs-page--has-hero {
  position: relative;
  z-index: 2;
  background: var(--color-surface-page);
}

/* Scroll room above .cs-page so the fixed .cs-hero-full has room to fade
 * before .cs-page slides over it. Sits between the hero and the page,
 * one full viewport tall, non-interactive. */
.cs-hero-spacer {
  height: 100vh;
  pointer-events: none;
}

/* Header chrome adapts: white over the video, solid afterwards. */
.has-cs-hero .header {
  background: transparent !important;
  transition: background 400ms var(--ease-smooth),
              transform 420ms var(--ease-smooth),
              opacity 400ms var(--ease-smooth);
}

.has-cs-hero .header .brand {
  color: var(--color-text-inverse);
  transition: color 320ms var(--ease-smooth);
}

.has-cs-hero .header .menu-icon-bar {
  background: var(--color-text-inverse);
  transition: background 320ms var(--ease-smooth),
              transform 260ms var(--ease-smooth),
              opacity 260ms var(--ease-smooth);
}

.has-cs-hero .header.video-gone {
  background: var(--color-surface-page) !important;
}

.has-cs-hero .header.video-gone .brand {
  color: var(--color-text-primary);
}

.has-cs-hero .header.video-gone .menu-icon-bar {
  background: var(--color-text-primary);
}

/* ── Hero overlay (eyebrow + wordmark + tagline + CTA) ──────────────────
 * Sits inside .cs-hero-full at the bottom-left. The tagline + CTA hide
 * with .is-hero-hidden as soon as the user scrolls past a small
 * threshold; case-study.js initHeroVideoFade() owns the toggle. */
.cs-hero-overlay {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  z-index: 2;
  padding: var(--space-12);
  pointer-events: none;
  display: flex;
  justify-content: center;
  align-items: flex-end;
  transition: opacity 320ms cubic-bezier(0.22, 1, 0.36, 1),
              visibility 0s linear 0s;
}

/* Top-anchored variant: overlay sits below the header instead of at the bottom.
 * Pairs with the inverted gradient scrim below. Revert by removing this class
 * from the .cs-hero-overlay element. */
.cs-hero-overlay--top {
  bottom: auto;
  top: 0;
  padding-top: calc(var(--space-20) + env(safe-area-inset-top, 0px));
}

.cs-hero-overlay.is-hero-hidden {
  opacity: 0;
  visibility: hidden;
  transition: opacity 320ms cubic-bezier(0.22, 1, 0.36, 1),
              visibility 0s linear 320ms;
}

.cs-hero-overlay::before {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  height: 520px;
  background: linear-gradient(
    180deg in oklab,
    rgba(0, 0, 0, 0)    0%,
    rgba(0, 0, 0, 0.03) 50%,
    rgba(0, 0, 0, 0.18) 75%,
    rgba(0, 0, 0, 0.40) 90%,
    rgba(0, 0, 0, 0.58) 100%
  );
  pointer-events: none;
  z-index: -1;
  animation: scroll-cue-enter 900ms cubic-bezier(0.22, 1, 0.36, 1) 2000ms backwards;
}

.cs-hero-overlay--top::before {
  bottom: auto;
  top: 0;
  background: linear-gradient(
    180deg in oklab,
    rgba(0, 0, 0, 0.58) 0%,
    rgba(0, 0, 0, 0.42) 20%,
    rgba(0, 0, 0, 0.22) 45%,
    rgba(0, 0, 0, 0.06) 75%,
    rgba(0, 0, 0, 0)    100%
  );
}

.cs-hero-meta {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  pointer-events: auto;
}

/* The scroll cue itself (.scroll-cue, .scroll-cue-line) is defined in
 * styles.css as a shared component. Its inverse colouring for use over
 * the hero video is applied via the .scroll-cue--inverse modifier on
 * the <a> element. Placement here is owned by .cs-hero-overlay's flex
 * layout (justify-content: center, align-items: flex-end). */

@media (prefers-reduced-motion: reduce) {
  .cs-hero-overlay::before {
    animation: none;
  }
}

.cs-hero-eyebrow {
  margin: 0;
  font-family: "Suisse Int'l Book", Inter, sans-serif;
  font-size: var(--font-size-caption);
  line-height: var(--line-height-caption);
  letter-spacing: var(--letter-spacing-caption);
  font-weight: var(--font-light);
  color: var(--color-text-inverse);
  opacity: 0.7;
}

.cs-hero-wordmark {
  margin: var(--space-1) 0 0 0;
  font-family: "Suisse Int'l Book", Inter, sans-serif;
  font-size: var(--font-size-h3);
  line-height: var(--line-height-h3);
  letter-spacing: var(--letter-spacing-h3);
  font-weight: var(--font-semi-bold);
  color: var(--color-text-inverse);
  pointer-events: auto;
  cursor: default;
}

.cs-hero-wordmark-letter {
  display: inline-block;
}

.cs-hero-tagline {
  margin: var(--space-2) 0 0 0;
  font-family: "Suisse Int'l Book", Inter, sans-serif;
  font-size: var(--font-size-body);
  line-height: var(--line-height-body);
  letter-spacing: var(--letter-spacing-body);
  font-weight: var(--font-book);
  color: var(--color-text-inverse);
}

.cs-hero-cta {
  pointer-events: auto;
  align-self: flex-start;
  margin-top: var(--space-6);
  min-width: 200px;
}

@media (max-width: 768px) {
  .cs-hero-overlay {
    padding: var(--space-6);
    padding-bottom: calc(var(--space-12) + env(safe-area-inset-bottom, 0px));
  }
  .cs-hero-overlay--top {
    padding-top: calc(var(--space-16) + env(safe-area-inset-top, 0px));
    padding-bottom: var(--space-6);
  }
  .cs-hero-meta    { max-width: none; }
}

/* ── Prototype link card ────────────────────────────────────────────────
 * Used at the end of the prototype section to link to an external
 * Figma proto. Full-width row: label/sub stack on the left, arrow on
 * the right. The arrow nudges right on hover; border darkens to
 * primary text colour. */
.cs-proto-link {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-6);
  width: 100%;
  border: 1px solid var(--color-border-default);
  border-radius: var(--radius-sharp);
  padding: var(--space-5) var(--space-6);
  text-decoration: none;
  color: var(--color-text-primary);
  transition: border-color 260ms var(--ease-smooth),
              background 260ms var(--ease-smooth);
}

.cs-proto-link:hover {
  border-color: var(--color-text-primary);
  background: var(--color-surface-secondary);
}

.cs-proto-link:hover .cs-proto-arrow {
  transform: translateX(var(--space-1));
}

.cs-proto-label {
  font-size: var(--font-size-body);
  line-height: var(--line-height-body);
  letter-spacing: var(--letter-spacing-body);
  font-weight: var(--font-semi-bold);
}

.cs-proto-sub {
  font-size: var(--font-size-caption);
  line-height: var(--line-height-caption);
  color: var(--color-text-tertiary);
  font-weight: var(--font-light);
  margin-top: var(--space-1);
}

.cs-proto-arrow {
  flex-shrink: 0;
  font-size: var(--font-size-body);
  line-height: 1;
  color: var(--color-text-primary);
  transition: transform 260ms var(--ease-smooth);
}

/* ── Small per-element overrides ────────────────────────────────────────
 * TOC entries match body weight at section scale; lists tighten their
 * gap so multi-line bullets read as one block. */
.cs-toc-item a {
  font-size: var(--font-size-body);
  line-height: var(--line-height-body);
  letter-spacing: var(--letter-spacing-body);
  font-weight: var(--font-semi-bold);
}

.cs-list { gap: var(--space-1); }

/* Intro→body rhythm: extra breathing room before the first section. */
.cs-page > .cs-intro { margin-bottom: var(--space-16); }


/* ── Centered variant (experimental) ─────────────────────────────────────
 * Opt-in via .cs-page--centered on the .cs-page wrapper. Aligns the
 * article to the same 12-col page grid the rest of the site uses
 * (header, footer, work cards).
 *
 * Bands (each centered on the page):
 *   text     — 6 cols by default, 4 cols at >1600px (3 / 4 col indent)
 *   spill    — 8 cols (.cs-stats, .cs-media, card elevation imagery)
 *   logo     — 10 cols (.cs-logo-grid — three-up specimen needs breath)
 *   tall     — text-column width (.cs-media:has(video) — phone demos)
 *   bleed    — full bleed (.cs-brand-immersive, .cs-hero, .cs-media-full)
 *
 * Indents are written as percentages of the parent so they snap to the
 * same column gutters the audit overlay (G key) shows. To revert:
 * remove the modifier class from any case-study HTML using it, then
 * delete this block. */

/* TEXT BAND — 6 cols centered (3-col indent each side). Drops the
 * existing 720/880px caps so width is purely grid-derived.
 *
 * Indent math accounts for the 16px (--space-4) gutters between the
 * audit grid's 12 columns, so band edges land on real column lines
 * instead of pure 12ths of the container. Derivation: an N-col indent
 * is N × (colWidth + gutter); with colWidth = (W − 11g)/12 this
 * collapses to (N/12) × (W + g). Applied uniformly to every band so
 * the audit overlay reads as compliant at every breakpoint. */
.cs-page--centered > .cs-back,
.cs-page--centered > .cs-intro,
.cs-page--centered > .cs-toc,
.cs-page--centered > .cs-lede,
.cs-page--centered > .contact--page,
.cs-page--centered .cs-section-header,
.cs-page--centered .cs-section-content > p,
.cs-page--centered .cs-section-content > .cs-sub-heading,
.cs-page--centered .cs-section-content > .cs-list,
.cs-page--centered .cs-section-content > .cs-quote,
.cs-page--centered .cs-section-content > .cs-note,
.cs-page--centered .cs-section-content > .cs-cta-row,
.cs-page--centered .cs-section-content > .cs-flow-strip,
.cs-page--centered .cs-section-content > .cs-timeline-role {
  width: auto;
  max-width: none;
  margin-left: calc((100% + var(--space-4)) / 12 * 3);
  margin-right: calc((100% + var(--space-4)) / 12 * 3);
}

/* Section divider hairline — locks to the text band */
.cs-page--centered .cs-section::before {
  width: auto;
  max-width: none;
  margin-left: calc((100% + var(--space-4)) / 12 * 3);
  margin-right: calc((100% + var(--space-4)) / 12 * 3);
}

/* SPILL BAND — 8 cols centered (2-col indent each side). The width:
 * auto override is critical: .cs-media has width: 100% in the base
 * rule, which combined with explicit margins would overflow the
 * parent by 33%. Same for .cs-logo-grid and .cs-media-inline (which
 * also has its own 720px cap to override). */
.cs-page--centered .cs-section-content > .cs-stats,
.cs-page--centered .cs-section-content > .cs-media,
.cs-page--centered .cs-section-content > .cs-media-inline,
.cs-page--centered .cs-section-content > .cs-media-pair,
.cs-page--centered .cs-section-content > .cs-compare,
.cs-page--centered .cs-section-content > .cs-flow-chart {
  width: auto;
  max-width: none;
  margin-left: calc((100% + var(--space-4)) / 12 * 2);
  margin-right: calc((100% + var(--space-4)) / 12 * 2);
}

/* Persona override — sit at text-band width (6 cols) instead of the
 * spill band that .cs-media-inline normally inhabits. Cancels the
 * 2-col indent from .cs-media-inline above and applies the same
 * 3-col indent the text band uses, so the persona frame aligns with
 * the body copy on either side. */
.cs-page--centered .cs-section-content > .cs-media--persona {
  width: auto;
  max-width: none;
  margin-left: calc((100% + var(--space-4)) / 12 * 3);
  margin-right: calc((100% + var(--space-4)) / 12 * 3);
}

/* TALL BAND — phone-aspect demo videos sit at the text-column width
 * so portrait media doesn't blow vertical real-estate apart. */
.cs-page--centered .cs-section-content > .cs-media:has(video) {
  margin-left: calc((100% + var(--space-4)) / 12 * 3);
  margin-right: calc((100% + var(--space-4)) / 12 * 3);
}

/* LOGO BAND — 10 cols centered (1-col indent each side). The three-up
 * brand specimen reads better with breath on either side without
 * pushing all the way to full bleed. */
.cs-page--centered .cs-section-content > .cs-logo-grid {
  width: auto;
  margin-left: calc((100% + var(--space-4)) / 12 * 1);
  margin-right: calc((100% + var(--space-4)) / 12 * 1);
}

/* Below desktop the 12-col band system collapses — bands are a desktop
 * construct. At tablet (641–1024px) the page reads as an 8-col grid,
 * with a 2-col-of-12 indent each side so content doesn't sit edge-
 * to-edge; at mobile (≤640px) it's a single column flush to the page
 * padding (--space-5). width: auto and max-width: none from the
 * desktop rules stay in force so text fills the available band. */
@media (min-width: 641px) and (max-width: 1024px) {
  .cs-page--centered > .cs-back,
  .cs-page--centered > .cs-intro,
  .cs-page--centered > .cs-toc,
  .cs-page--centered > .cs-lede,
  .cs-page--centered > .contact--page,
  .cs-page--centered .cs-section-header,
  .cs-page--centered .cs-section::before,
  .cs-page--centered .cs-section-content > p,
  .cs-page--centered .cs-section-content > .cs-sub-heading,
  .cs-page--centered .cs-section-content > .cs-list,
  .cs-page--centered .cs-section-content > .cs-quote,
  .cs-page--centered .cs-section-content > .cs-note,
  .cs-page--centered .cs-section-content > .cs-cta-row,
  .cs-page--centered .cs-section-content > .cs-flow-strip,
  .cs-page--centered .cs-section-content > .cs-flow-chart,
  .cs-page--centered .cs-section-content > .cs-stats,
  .cs-page--centered .cs-section-content > .cs-media,
  .cs-page--centered .cs-section-content > .cs-media-inline,
  .cs-page--centered .cs-section-content > .cs-media--persona,
  .cs-page--centered .cs-section-content > .cs-media:has(video),
  .cs-page--centered .cs-section-content > .cs-logo-grid,
  .cs-page--centered .cs-section-content > .cs-timeline-role {
    margin-left: calc((100% + var(--space-4)) / 12 * 2);
    margin-right: calc((100% + var(--space-4)) / 12 * 2);
  }
}

@media (max-width: 640px) {
  .cs-page--centered > .cs-back,
  .cs-page--centered > .cs-intro,
  .cs-page--centered > .cs-toc,
  .cs-page--centered > .cs-lede,
  .cs-page--centered > .contact--page,
  .cs-page--centered .cs-section-header,
  .cs-page--centered .cs-section::before,
  .cs-page--centered .cs-section-content > p,
  .cs-page--centered .cs-section-content > .cs-sub-heading,
  .cs-page--centered .cs-section-content > .cs-list,
  .cs-page--centered .cs-section-content > .cs-quote,
  .cs-page--centered .cs-section-content > .cs-note,
  .cs-page--centered .cs-section-content > .cs-cta-row,
  .cs-page--centered .cs-section-content > .cs-flow-strip,
  .cs-page--centered .cs-section-content > .cs-flow-chart,
  .cs-page--centered .cs-section-content > .cs-stats,
  .cs-page--centered .cs-section-content > .cs-media,
  .cs-page--centered .cs-section-content > .cs-media-inline,
  .cs-page--centered .cs-section-content > .cs-media--persona,
  .cs-page--centered .cs-section-content > .cs-media:has(video),
  .cs-page--centered .cs-section-content > .cs-compare,
  .cs-page--centered .cs-section-content > .cs-logo-grid,
  .cs-page--centered .cs-section-content > .cs-timeline-role {
    margin-left: 0;
    margin-right: 0;
  }
}

/* ≥2000px — narrow the text band to 4 cols (4-col indent each side)
 * so reading line-length stays comfortable on ultrawide monitors.
 * Held until 2000px because at 1600–1999px the 6-col text band still
 * reads well, and dropping to 4 cols at 1600 made the column look a
 * hair thin. The tall band tracks the text band so the prototype
 * video stays in proportion. The existing >1600px rule on
 * .cs-body > .cs-section also gets cancelled here to avoid double-
 * indenting. */
@media (min-width: 2000px) {
  .cs-page--centered > .cs-back,
  .cs-page--centered > .cs-intro,
  .cs-page--centered > .cs-toc,
  .cs-page--centered > .cs-lede,
  .cs-page--centered > .contact--page,
  .cs-page--centered .cs-section-header,
  .cs-page--centered .cs-section::before,
  .cs-page--centered .cs-section-content > p,
  .cs-page--centered .cs-section-content > .cs-sub-heading,
  .cs-page--centered .cs-section-content > .cs-list,
  .cs-page--centered .cs-section-content > .cs-quote,
  .cs-page--centered .cs-section-content > .cs-note,
  .cs-page--centered .cs-section-content > .cs-cta-row,
  .cs-page--centered .cs-section-content > .cs-flow-strip,
  .cs-page--centered .cs-section-content > .cs-media:has(video),
  .cs-page--centered .cs-section-content > .cs-media--persona,
  .cs-page--centered .cs-section-content > .cs-timeline-role {
    margin-left: calc((100% + var(--space-4)) / 12 * 4);
    margin-right: calc((100% + var(--space-4)) / 12 * 4);
  }

  .cs-page--centered .cs-body > .cs-section {
    margin-left: 0;
    margin-right: 0;
  }
}


/* ── Project pager (bottom of case study + art pages) ─
 * Three-column row: previous / index / next. Wraps at the ends so
 * there are no dead ends. Markup is injected by cs-pager.js — never
 * write by hand.
 *
 * Interaction model mirrors .link-arrow: resting + hover both use
 * --color-text-primary (no link-blue shift, no weight change), the
 * arrow translates by --btn-arrow-offset on hover, and digit-scramble
 * runs from data-scramble. The eyebrow label is non-interactive
 * context — caption-scale, tertiary, doesn't shift on hover. */
/* Lives at the body root (sibling of .cs-page and #site-footer), so its
 * left/right edges share the page gutter with the footer brand and nav. */
.cs-pager {
  display: grid;
  grid-template-columns: 1fr auto 1fr;
  align-items: start;
  gap: var(--space-6);
  border-top: 1px solid var(--color-border-default);
  padding: var(--space-8) var(--space-12) 0;
  margin-bottom: var(--space-16);
  box-sizing: border-box;
}

.cs-pager-link {
  display: inline-flex;
  flex-direction: column;
  gap: var(--space-3);
  text-decoration: none;
  color: var(--color-text-primary);
}

.cs-pager-link.is-prev  { justify-self: start;  align-items: flex-start; }
.cs-pager-link.is-index { justify-self: center; align-items: center; }
.cs-pager-link.is-next  { justify-self: end;    align-items: flex-end; }

/* Eyebrow — caption-scale, tertiary, sentence case. Never shifts on
 * hover; it's quiet context above the title. */
.cs-pager-label {
  font-size: var(--font-size-caption);
  line-height: var(--line-height-caption);
  letter-spacing: var(--letter-spacing-caption);
  color: var(--color-text-tertiary);
  font-weight: var(--font-light);
}

/* Title — mirrors .link-arrow: small (13/21), weight 600, primary,
 * with the directional arrow rendered via ::after / ::before so the
 * only hover change is the arrow's translate. */
.cs-pager-title {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  font-size: var(--font-size-small);
  line-height: var(--line-height-small);
  letter-spacing: 0;
  font-weight: var(--font-semi-bold);
  color: var(--color-text-primary);
}

.is-next .cs-pager-title::after {
  content: '→';
  display: inline-block;
  transition: transform 320ms var(--ease-smooth);
}
.cs-pager-link.is-next:hover .cs-pager-title::after {
  transform: translateX(var(--btn-arrow-offset));
}

.is-prev .cs-pager-title::before {
  content: '←';
  display: inline-block;
  transition: transform 320ms var(--ease-smooth);
}
.cs-pager-link.is-prev:hover .cs-pager-title::before {
  transform: translateX(calc(-1 * var(--btn-arrow-offset)));
}

/* Index has no arrow — it's a lateral move, not directional. The only
 * hover visual is the digit-scramble from data-scramble. */

/* Mobile: the three-column row can't fit a long title between two others
 * without spilling the page gutter. Drop to a single-column stack — each
 * link spans the full width but keeps its resting alignment, so prev sits
 * left, index centred, next right. That staggered left → centre → right
 * cascade is the deliberate-looking artifact of the per-variant alignment;
 * no divider line between rows. */
@media (max-width: 640px) {
  .cs-pager {
    grid-template-columns: 1fr;
    row-gap: var(--space-6);
  }
  .cs-pager-link.is-prev,
  .cs-pager-link.is-index,
  .cs-pager-link.is-next {
    grid-column: 1;
    width: 100%;
  }
}

/* Gutter must track .cs-page so the pager's content edge lines up with the
 * article above and the footer below. .cs-page: space-12 → space-6 (≤1199)
 * → space-5 (≤768). Mirror those steps here. */
@media (max-width: 1199px) {
  .cs-pager {
    padding-left: var(--space-6);
    padding-right: var(--space-6);
  }
}
@media (max-width: 768px) {
  .cs-pager {
    padding-left: var(--space-5);
    padding-right: var(--space-5);
  }
}

