/* ─────────────────────────────────────────────────────────────────────────
   deck.css: the slide format for case studies.
   One <section class="slide"> is one idea. Shared by every case study deck;
   a deck only supplies its own copy and media.

   Type and colour come from the portfolio: Google Sans Flex, the ink and
   greys in styles.css, and the peach marker already used by .case-mark.
   Each deck sets its own --accent and --mark to the brand it is about.
   ───────────────────────────────────────────────────────────────────────── */

:root {
  /* Rhythm for a block with a drawing over text: the space inside a block
     (top rule to drawing, drawing to text) is smaller than the space between
     blocks, so a drawing reads as attached to its own words. */
  --cell-pad: 28px;
  --ill-gap: 28px;
  --block-gap: 56px;
  --bg: #ffffff;
  --ink: #161513;
  --muted: #5f5c55;
  --faint: #8a877f;
  --line: rgba(22, 21, 19, 0.1);
  --card: #f5f4f0;
  --accent: #4a4842;
  --mark: rgba(253, 224, 200, 0.95);
  /* The pen that marks up a screenshot. */
  --note: #d9480f;

  --gutter: 24px;
  --pad: clamp(32px, 6vw, 96px);

  /* The type scale. Six sizes, nothing between them. */
  --fs-eyebrow: 12px;
  --fs-h: 48px;
  --fs-hmd: 36px;
  --fs-body: 17px;
  --fs-row: 22px;
  --fs-stat: 56px;
  --fs-cap: 14px;
}

@media (min-width: 1600px) {
  :root {
    --cell-pad: 32px;
    --ill-gap: 32px;
    --block-gap: 72px;
    --fs-eyebrow: 14px;
    --fs-h: 62px;
    --fs-hmd: 46px;
    --fs-body: 20px;
    --fs-row: 28px;
    --fs-stat: 68px;
    --fs-cap: 16px;
  }
}

/* Height binds before width on a laptop, so step the scale down, not the layout. */
@media (max-height: 840px) {
  :root {
    --cell-pad: 22px;
    --ill-gap: 22px;
    --block-gap: 48px;
    --pad: clamp(22px, 3.6vw, 56px);
    --fs-h: 40px;
    --fs-hmd: 30px;
    --fs-body: 15px;
    --fs-row: 19px;
    --fs-stat: 44px;
    --fs-cap: 13px;
  }
}

@media (max-width: 900px) {
  :root {
    --pad: 24px;
    --fs-h: 34px;
    --fs-hmd: 28px;
    --fs-body: 16px;
    --fs-row: 19px;
    --fs-stat: 40px;
    --fs-cap: 14px;
  }
}

* { box-sizing: border-box; }

html {
  background: var(--bg);
  -webkit-text-size-adjust: 100%;
}

body {
  margin: 0;
  background: var(--bg);
  color: var(--ink);
  font-family: "Google Sans Flex", -apple-system, BlinkMacSystemFont, "system-ui", "Segoe UI", "Helvetica Neue", Arial, sans-serif;
  font-size: var(--fs-body);
  line-height: 1.5;
  letter-spacing: -0.01em;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
  overflow-x: hidden;
}

a { color: inherit; }
img, video { max-width: 100%; }
p, h1, h2, h3, figure, ul, ol { margin: 0; }
ul, ol { padding: 0; list-style: none; }

/* ── The slide shell ───────────────────────────────────────────────────── */

.slide {
  position: relative;
  min-height: 100vh;
  min-height: 100svh;
  display: flex;
  align-items: center;
  padding: var(--pad);
}

/* The Work link is fixed at the top left, so on a paged deck every slide's
   content starts below it. */
@media (min-width: 700px) {
  .slide { --pad-top: max(var(--pad), 64px); padding-top: var(--pad-top); }
}

/* The block sits in the middle of the page, however wide the window; what is
   inside it lines up on the block's left edge. */
.slide-inner {
  width: 100%;
  max-width: 1280px;
  margin-inline: auto;
  display: flex;
  flex-direction: column;
  gap: var(--sg, 24px);
}

@media (min-width: 1600px) {
  .slide-inner { max-width: 1560px; }
}

.slide-inner--center {
  align-items: center;
  text-align: center;
}

/* On a screen big enough to hold a slide, the deck pages: deck.js turns one
   scroll gesture or one key press into exactly one slide, and owns where the
   page comes to rest. Without script, the page still settles on slide tops.
   That is a laptop or a tablet, held either way up: 700px wide or more. Only
   a phone gets the deck as one long page. Between 700 and 900px wide the
   slides keep their shape and the rows inside them fold to two columns. */
@media (min-width: 700px) and (min-height: 620px) {
  html:not(.js) { scroll-snap-type: y proximity; }
  html:not(.js) .slide { scroll-snap-align: start; }
}

/* On a phone the deck is a page: no fixed heights, sections stack. */
@media (max-width: 699px) {
  .slide {
    min-height: 0;
    padding-block: 56px;
    border-top: 1px solid var(--line);
  }
  .slide:first-of-type {
    border-top: 0;
    padding-top: 88px;
  }
}

/* ── Type ──────────────────────────────────────────────────────────────── */

.eyebrow {
  font-size: var(--fs-eyebrow);
  font-weight: 500;
  letter-spacing: 0.09em;
  text-transform: uppercase;
  color: var(--accent);
  margin-bottom: calc(16px - var(--sg, 24px));
}

.statement {
  font-size: var(--fs-h);
  font-weight: 500;
  line-height: 1.08;
  letter-spacing: -0.035em;
  max-width: 21em;
  text-wrap: balance;
}

.statement--md {
  font-size: var(--fs-hmd);
  line-height: 1.12;
  letter-spacing: -0.03em;
  max-width: 24em;
}

.subline {
  font-size: var(--fs-body);
  color: var(--muted);
  max-width: 46em;
  text-wrap: pretty;
}

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

/* The highlight: a soft wash with a hairline underline in the deck's marker
   colour. One per slide, on the phrase the slide exists to make. */
.hl,
.hl--soft {
  background: var(--mark-soft, rgba(255, 184, 0, 0.16));
  border-bottom: 1px solid var(--mark-line, rgba(214, 150, 0, 0.75));
  padding: 0 2px;
  -webkit-box-decoration-break: clone;
  box-decoration-break: clone;
}

.label {
  display: block;
  font-size: var(--fs-eyebrow);
  font-weight: 700;
  letter-spacing: 0.09em;
  text-transform: uppercase;
  color: var(--faint);
}

.label--accent { color: var(--accent); }

/* ── Components ────────────────────────────────────────────────────────── */

/* Columns under a hairline: preview, stats, keys, outcome. */
.cols {
  display: grid;
  grid-template-columns: repeat(var(--n, 3), minmax(0, 1fr));
  gap: var(--gutter) var(--block-gap);
}

/* A column: an optional drawing, a small accent label, a bold line, a quiet
   explanation. The same shape on every slide that lists things. */
.col {
  display: flex;
  flex-direction: column;
  gap: 10px;
  padding: var(--cell-pad) 0 0;
  border-top: 1px solid var(--line);
}

.col-lead {
  font-size: 20px;
  font-weight: 600;
  line-height: 1.3;
  letter-spacing: -0.01em;
  text-wrap: pretty;
}

.col-text {
  font-size: 15px;
  line-height: 1.6;
  color: var(--muted);
  text-wrap: pretty;
}

.col-num {
  font-size: var(--fs-eyebrow);
  font-weight: 700;
  letter-spacing: 0.09em;
  text-transform: uppercase;
  color: var(--accent);
}

/* A customer's words, as written, on the sticky note they left them on: paper
   colours from the feedback board, a slight tilt, a soft paper shadow. */
.sticky {
  margin: 0;
  padding: 14px 16px 16px;
  border-radius: 3px;
  background: var(--tint, #fdf3c4);
  box-shadow: 0 1px 1px rgba(22, 21, 19, 0.06), 0 8px 14px -10px rgba(22, 21, 19, 0.28);
  font-size: 15px;
  line-height: 1.45;
  color: var(--ink);
  transform: rotate(var(--rot, 0deg));
  text-wrap: pretty;
}

.sticky + .sticky { margin-top: 6px; }

.sticky[data-tint="yellow"]   { --tint: #fdf3c4; }
.sticky[data-tint="pink"]     { --tint: #fde3e9; }
.sticky[data-tint="cyan"]     { --tint: #def2f6; }
.sticky[data-tint="green"]    { --tint: #e1f2e3; }
.sticky[data-tint="lavender"] { --tint: #ebe6fa; }
.sticky[data-tint="peach"]    { --tint: #fde5d2; }

@media (min-width: 1500px) { .sticky { font-size: 16px; } }

/* The drawing at the head of a column or a step. */
.col-ill {
  display: block;
  width: 100%;
  max-width: 168px;
  height: 96px;
  margin: 0 0 calc(var(--ill-gap) - 10px);
  overflow: visible;
}

@media (min-width: 1500px) {
  .col-lead { font-size: 22px; }
  .col-text { font-size: 16px; }
}

@media (min-width: 1600px) {
  .col-ill { max-width: 200px; height: 114px; }
}

@media (max-height: 840px) {
  .col-lead { font-size: 18px; }
}

/* A short window keeps the words and drops the drawings. */
@media (max-height: 760px) {
  .col-ill { display: none; }
}

.stat-num {
  font-size: var(--fs-stat);
  font-weight: 500;
  line-height: 1;
  letter-spacing: -0.04em;
  font-feature-settings: "tnum";
}

.stat-label { font-weight: 500; }
.stat-sub { color: var(--muted); font-size: var(--fs-cap); }

/* A frame around media. */
.frame {
  position: relative;
  overflow: hidden;
  border-radius: 14px;
  border: 1px solid var(--line);
  background: var(--card);
}

.frame > img,
.frame > video {
  display: block;
  width: 100%;
  height: auto;
}

/* A frame sized from the height the slide can spare, at the media's own ratio. */
.frame--fit {
  align-self: flex-start;
  height: var(--mh, 56svh);
  aspect-ratio: var(--ar, 16 / 10);
  max-width: 100%;
}

.frame--fit > img,
.frame--fit > video {
  width: 100%;
  height: 100%;
  object-fit: contain;
}

/* A fixed-ratio frame, for media that sits in a row and must match its neighbours. */
.frame--ratio { aspect-ratio: var(--ar, 16 / 10); }

.frame--ratio > img,
.frame--ratio > video {
  width: 100%;
  height: 100%;
  object-fit: contain;
}

.frame--cover > img { object-fit: cover; object-position: top left; }

.media-row {
  display: grid;
  grid-template-columns: repeat(var(--n, 3), minmax(0, 1fr));
  gap: var(--gutter);
  width: 100%;
  max-width: var(--row-max, none);
}

.media-item {
  display: flex;
  flex-direction: column;
  gap: 12px;
}

.media-cap {
  font-size: var(--fs-cap);
  color: var(--muted);
  text-wrap: pretty;
}

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

/* Where a slide's figures come from: one quiet line at the slide's foot, on
   the line the slide counter sits on and at the other end of it, under the
   right edge of the slide's block. It reads as a footnote, not as a sentence
   left standing under the figures, and it takes no room from the slide. On
   a phone, where slides are one long page with no counter, it closes the
   block it belongs to. */
.source-line {
  position: absolute;
  right: max(var(--pad), calc(50% - 640px));
  bottom: 18px;
  margin: 0;
  font-size: 12px;
  letter-spacing: 0.02em;
  color: var(--muted);
  text-align: right;
}

.source-line b {
  margin-right: 10px;
  font-weight: 600;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--faint);
}

@media (min-width: 1600px) {
  .source-line { right: max(var(--pad), calc(50% - 780px)); }
}

/* It is a child of the slide, not of the slide's block: on a phone it takes
   its own row under the block. Behind the password prompt it goes with the
   rest of the slide. */
@media (max-width: 699px) {
  .slide:has(> .source-line) { flex-wrap: wrap; row-gap: 20px; }
  .source-line { position: static; flex: 0 0 100%; text-align: left; }
}

html:not(.is-paywall-authed) [data-paywall-blur] ~ .slide > .source-line { visibility: hidden; }

/* Copy one side, media the other. */
.split {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1.1fr);
  gap: 56px;
  align-items: center;
}

.split-copy {
  display: flex;
  flex-direction: column;
  gap: var(--sg, 24px);
}

/* Decisions: what was stopped, deferred, or left open. */
.status {
  align-self: flex-start;
  display: inline-flex;
  align-items: center;
  gap: 8px;
  padding: 4px 10px;
  border-radius: 100px;
  border: 1px solid var(--line);
  font-size: var(--fs-eyebrow);
  font-weight: 500;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--muted);
}

.status--stopped {
  color: var(--accent);
  border-color: currentColor;
}

/* A sequence where each step gates the next. */
.road {
  display: grid;
  grid-template-columns: repeat(var(--n, 6), minmax(0, 1fr));
  gap: 0;
}

.road-step {
  position: relative;
  display: flex;
  flex-direction: column;
  gap: 10px;
  padding: var(--cell-pad) var(--block-gap) 0 0;
  border-top: 1px solid var(--line);
}

.road-step::before {
  content: "";
  position: absolute;
  top: -5px;
  left: 0;
  width: 9px;
  height: 9px;
  border-radius: 50%;
  background: var(--ink);
}

.road-step--gate { border-top-color: var(--accent); }
.road-step--gate::before { background: var(--accent); }
.road-step--later::before {
  background: var(--bg);
  border: 1px solid var(--faint);
}

/* The evidence under a step: one number, and what it counts. */
.road-proof {
  display: flex;
  flex-direction: column;
  gap: 10px;
  /* room on both sides of the rule, so the claim above and the number below
     read as two things */
  margin-top: 20px;
  padding-top: 26px;
  border-top: 1px solid var(--line);
}
@media (max-height: 840px) { .road-proof { margin-top: 12px; padding-top: 18px; } }
.road-proof-num {
  font-size: 40px;
  font-weight: 500;
  line-height: 1.05;
  letter-spacing: -0.02em;
  color: var(--ink);
}
@media (min-width: 1600px) { .road-proof-num { font-size: 48px; } }
@media (max-height: 840px) { .road-proof-num { font-size: 34px; } }
.road-proof-cap { font-size: var(--fs-cap); color: var(--muted); text-wrap: pretty; }

/* Steps that carry evidence line up row by row, so the numbers sit on one
   level however long the text above each of them runs. */
@supports (grid-template-rows: subgrid) {
  .road:has(.road-proof) .road-step {
    display: grid;
    grid-template-rows: subgrid;
    grid-row: span 5;
    row-gap: 10px;
    align-content: start;
  }
}

/* A road read as a flow: one quiet rule with no markers on it. An arrow leads
   from each step's drawing to the next, so the order is plain. */
.road--flow .road-step { border-top-color: var(--line); }
.road--flow .road-step::before { display: none; }

.road-art {
  display: flex;
  align-items: center;
  margin: 0 0 calc(var(--ill-gap) - 10px);
}

.road-art .col-ill { flex: 0 0 auto; margin: 0; }

.road-arrow {
  flex: 0 0 66px;
  width: 66px;
  height: 24px;
  margin-inline: auto;
  overflow: visible;
}

.chart.road-arrow path { stroke: rgba(22, 21, 19, 0.55); stroke-width: 2.2; }

/* Where the drawings are hidden or the steps stack, the arrows go with them;
   on a short window a small arrow after each label keeps the order. */
@media (max-height: 760px) {
  .road-art { display: none; }
  .road--flow .road-step:not(:last-child) > :is(.col-num, .label)::after { content: "\2192"; margin-left: 12px; }
}

@media (max-width: 900px) {
  .road-arrow { display: none; }
  .road--flow .road-step:not(:last-child) > :is(.col-num, .label)::after { content: none; }
}

.road-title {
  font-size: var(--fs-body);
  font-weight: 600;
  line-height: 1.3;
  text-wrap: pretty;
}

.road-meta {
  font-size: var(--fs-cap);
  color: var(--muted);
  text-wrap: pretty;
}

/* A fact still to be supplied. Visible on purpose: it must not ship. */
.todo {
  display: inline-block;
  padding: 2px 8px;
  border-radius: 4px;
  outline: 1px dashed var(--accent);
  color: var(--accent);
  font-size: var(--fs-cap);
  font-weight: 500;
}

/* A heading still to be supplied: the same mark, at the heading's own size,
   so a slide that is waiting for its words keeps its shape. */
.statement .todo {
  padding: 4px 12px;
  border-radius: 8px;
  font-size: inherit;
  font-weight: inherit;
}

/* A big number still to be supplied. */
.stat-num.todo {
  display: inline-block;
  align-self: flex-start;
  padding: 4px 12px;
  border-radius: 8px;
  font-size: var(--fs-stat);
  font-weight: 500;
}

/* An image still to be supplied, at the size it will take. */
.ph {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 6px;
  width: 100%;
  aspect-ratio: var(--ar, 16 / 10);
  max-height: var(--mh, none);
  padding: 24px;
  border-radius: 14px;
  border: 1px dashed rgba(22, 21, 19, 0.22);
  background: var(--card);
  text-align: center;
}

.ph-label {
  font-size: var(--fs-eyebrow);
  font-weight: 500;
  letter-spacing: 0.09em;
  text-transform: uppercase;
  color: var(--accent);
}

.ph-brief {
  font-size: var(--fs-cap);
  color: var(--muted);
  max-width: 28em;
}

/* Copy on top, media underneath taking whatever height the copy leaves.
   The slide is exactly one window tall; the slot fills the rest of it and
   the media sits flush left at its own ratio, as large as the slot allows. */
.slide--fill { height: 100vh; height: 100svh; }
.slide--fill .slide-inner { height: 100%; }

.media-slot {
  position: relative;
  flex: 1 1 auto;
  min-height: 220px;
  width: 100%;
}

.media-slot > .frame,
.media-slot > .ph {
  position: absolute;
  top: 0;
  left: 0;
  height: 100%;
  width: auto;
  max-width: 100%;
  max-height: none;
  aspect-ratio: var(--ar, 16 / 10);
}

.media-slot > .frame > img,
.media-slot > .frame > video {
  width: 100%;
  height: 100%;
  object-fit: contain;
}

/* One whole picture, hugged: the box is as wide as the slot and as tall as
   the picture, up to the slot's height, so the frame never stands taller
   than what it holds, and a caption or a switch can sit right under it. */
.media-slot > .hug {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  max-height: 100%;
  aspect-ratio: var(--fit, 16 / 10);
}

.hug > .frame {
  position: absolute;
  inset: 0;
}

/* The same, but never cropped: the box is the largest that fits the slot at
   the picture's own ratio. Its frame can be given part of the width, leaving
   the rest as a white margin where notes sit and point in. */
.media-slot.is-box { container-type: size; }

.media-slot > .hug--contain {
  width: min(100cqw, calc(100cqh * var(--fitn, 1.6)));
  max-height: none;
}

/* A picture that can be zoomed and moved where it stands. At rest the box
   holds the whole picture and does not scroll. Zoomed in, the picture is
   wider than the box and the box scrolls both ways, with no scrollbars; two
   round buttons sit in its corner. */
.pan-box {
  position: absolute;
  inset: 0;
  overflow: auto;
  scrollbar-width: none;
  outline: none;
}
.pan-box::-webkit-scrollbar { display: none; }
.pan-box:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }
.pan-box > img {
  display: block;
  width: 100%;
  height: auto;
  max-width: none;
  -webkit-user-select: none;
  user-select: none;
  -webkit-user-drag: none;
}
.pan.is-zoomed .pan-box { cursor: grab; }
.pan.is-dragging .pan-box { cursor: grabbing; }

@media (hover: hover) and (pointer: fine) {
  .pan-box { overscroll-behavior: contain; }
}

.pan-bar {
  position: absolute;
  right: 14px;
  bottom: 14px;
  display: flex;
  gap: 6px;
}
.pan-btn {
  width: 34px;
  height: 34px;
  padding: 0;
  border-radius: 50%;
  border: 1px solid var(--line);
  background: rgba(255, 255, 255, 0.94);
  box-shadow: 0 1px 4px rgba(22, 21, 19, 0.12);
  font: inherit;
  font-size: 17px;
  font-weight: 500;
  line-height: 1;
  color: var(--ink);
  cursor: pointer;
}
.pan-btn:disabled { color: var(--faint); box-shadow: none; cursor: default; }
.pan-btn:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

/* Says how to zoom, beside the buttons, until the picture has been zoomed. */
.pan > .page-hint { right: 98px; bottom: 18px; }
.pan.is-used > .page-hint { opacity: 0; }

/* On a phone the picture is small and the line would cover it: the two
   buttons say it alone. */
@media (max-width: 640px) {
  .pan > .page-hint { display: none; }
}

/* A whole picture with a line of words under it. The slot is no taller than
   the picture is at the column's full width, so the words sit right under
   the picture and not at the foot of the slide; in a short window the slot
   gives way and the picture shrinks to fit. Where that leaves room to
   spare, the whole slide sits in the middle of the window's height. */
.media-slot--snug { flex: 0 1 auto; min-height: 0; aspect-ratio: var(--fit, 16 / 10); }
.slide--fill .slide-inner:has(> .media-slot--snug) { justify-content: center; }

/* A screen with something popping out of it. The picture is wider than the
   screen it shows, because a card hangs past the screen's right edge. The
   frame's line goes round the screen only; the card lies over the line and
   beyond it, shadow and all, as it was drawn. The picture is used twice:
   once clipped to the screen, under the line, and once cut down to what
   hangs out (--cut), over it.
   --fit is the screen's ratio, --fitn the same as a number, and --over how
   many times wider the whole picture is than the screen. Notes may sit in
   the white beside what hangs out; --room is then how many screen widths
   the picture and its notes take together, and --whole the whole picture's
   ratio, so the notes are drawn in the picture's own grid. */
.media-slot > .popout {
  position: absolute;
  top: 0;
  left: 0;
  width: min(calc(100cqw / var(--room, var(--over, 1))), calc(100cqh * var(--fitn, 1.6)));
  aspect-ratio: var(--fit, 16 / 10);
}

.popout > .chart.marks {
  inset: auto;
  top: 0;
  left: 0;
  z-index: 3;
  width: calc(100% * var(--over, 1));
  height: auto;
  aspect-ratio: var(--whole, 16 / 10);
}

.popout-screen {
  position: absolute;
  inset: 0;
  overflow: hidden;
  border-radius: 14px;
  background: #fff;
}

.popout img {
  display: block;
  width: calc(100% * var(--over, 1));
  max-width: none;
  height: auto;
  pointer-events: none;
  -webkit-user-select: none;
  user-select: none;
}

.popout::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 1;
  border: 1px solid var(--line);
  border-radius: 14px;
  pointer-events: none;
}

.popout > .popout-over {
  position: absolute;
  top: 0;
  left: 0;
  z-index: 2;
  clip-path: var(--cut);
}

/* A window too short to share: the slide grows and the media takes a fixed band. */
@media (max-height: 640px) {
  .slide--fill,
  .slide--fill .slide-inner { height: auto; }
  .media-slot { flex: none; height: 320px; }
}

/* Carousel: cards in a row. The active card takes the main spot and the
   next one peeks in beside it, dimmed. Swipe, drag or click the peeking card
   and the row slides it into the main spot. With two cards the first snaps
   to the left edge and the last to the right, so whichever is active, the
   other always peeks. The slot is a size container: a card is as tall as the
   slot and as wide as its ratio allows, held between 62% and 84% of the slot. */
.has-carousel { container-type: size; }

.carousel {
  position: absolute;
  inset: 0;
  display: flex;
  gap: 20px;
  overflow-x: auto;
  overflow-y: hidden;
  scroll-snap-type: x mandatory;
  overscroll-behavior-x: contain;
  scrollbar-width: none;
  outline: none;
}

.carousel::-webkit-scrollbar { display: none; }

.carousel:focus-visible .carousel-card.is-active {
  outline: 2px solid var(--accent);
  outline-offset: -2px;
}

.carousel.is-dragging {
  scroll-snap-type: none;
  cursor: grabbing;
}

/* While deck.js sends the row to a card, it goes to the exact spot itself. */
.carousel.is-driven { scroll-snap-type: none; }

.carousel-card {
  /* --d is how far this card is from the main spot, 0 (in it) to 1 (peeking),
     written by deck.js on every scroll frame, so the depth follows the finger.
     --ox is the edge it shrinks toward: the side nearest the main card. */
  --d: 0;
  --ox: 0%;
  position: relative;
  /* At least 62% of the slot, so the other card can only ever peek; at most
     84%, so it always does. Between those, the media's own ratio. */
  flex: 0 0 clamp(62cqw, calc(100cqh * var(--arn, 1.6)), 84cqw);
  height: 100%;
  margin: 0;
  overflow: hidden;
  border-radius: 14px;
  border: 1px solid var(--line);
  background: var(--card);
  scroll-snap-align: start;
  transform-origin: var(--ox) 50%;
  transform: scale(calc(1 - 0.12 * var(--d)));
  opacity: calc(1 - 0.55 * var(--d));
  will-change: transform, opacity;
}

.carousel-card:last-child { scroll-snap-align: end; }

.carousel-card > img,
.carousel-card > .ph {
  display: flex;
  width: 100%;
  height: 100%;
  object-fit: contain;
  border: 0;
  border-radius: 0;
  aspect-ratio: auto;
  pointer-events: none;
}

.carousel-card:not(.is-active) { cursor: pointer; }
.carousel-card:not(.is-active):hover { opacity: calc(1 - 0.35 * var(--d)); }

.card-tag {
  position: absolute;
  top: 14px;
  left: 14px;
  padding: 4px 10px;
  border-radius: 100px;
  background: rgba(255, 255, 255, 0.92);
  box-shadow: 0 1px 4px rgba(22, 21, 19, 0.12);
  font-size: var(--fs-eyebrow);
  font-weight: 600;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--muted);
  pointer-events: none;
}

/* The tag of the card in view is in the colour of the handwritten notes, not
   the deck's accent: on a Booking.com screen a blue tag reads as part of the
   design it sits on. */
.carousel-card.is-active .card-tag { color: var(--note); }

/* A long tag on a narrow card keeps the same margin on its right as on its
   left, and where it has to take two lines it is a rounded box, not a
   stretched pill. On a phone its letters sit a little closer, so a tag of
   three short parts still fits on one line. */
.card-tag { max-width: calc(100% - 28px); text-wrap: balance; }
@media (max-width: 560px) {
  .card-tag { border-radius: 14px; letter-spacing: 0.05em; }
}

/* A page in a card: a screenshot of a whole page, far taller than the card,
   shown at the card's full width. Its header stays put and the rest scrolls
   underneath, the way the page itself behaves. With a mouse or trackpad the
   scroll stops at the page's ends and deck.js hands it on to the deck; a
   finger carries on into the deck natively. */
.page {
  position: absolute;
  inset: 0;
  overflow-x: hidden;
  overflow-y: auto;
  scrollbar-width: none;
  background: #fff;
  outline: none;
  /* The pinned header stacks above the page's body, not above the card's tag. */
  isolation: isolate;
}

.page::-webkit-scrollbar { display: none; }

@media (hover: hover) and (pointer: fine) {
  .page { overscroll-behavior-y: contain; }
}

.page:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: -2px;
}

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

.page > .page-head {
  position: sticky;
  top: 0;
  z-index: 1;
}

/* Marks on a screenshot: a hand-drawn ring round the thing to look at, an
   arrow, and a note in handwriting, the way a printout gets marked up. They
   are drawn in the picture's own pixel grid (the viewBox is the picture's
   size), so they sit on the same spot at every size and scroll with it. */
.shot { position: relative; }

/* Marks may run over the picture's edge: labels sit above it, in the room
   this leaves, each over the part of the picture it names. */
.shot--over { margin-top: 2%; }

.chart.marks {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  overflow: visible;
  pointer-events: none;
}

.chart.marks path { stroke: var(--note); stroke-width: 3; }

/* A screenshot with a pale scrim is too light for white notes and too dark
   for orange ones. There the notes layer carries its own shade: it darkens
   the picture around the part the notes are about, with that part cut out.
   It belongs to the notes, so it goes when they are switched off. */
.chart.marks path.scrim {
  fill: rgba(0, 0, 0, 0.42);
  stroke: none;
}

.chart.marks text {
  font-family: "Caveat", "Bradley Hand", cursive;
  font-size: var(--note-size, 30px);
  font-weight: 700;
  fill: var(--note);
}

.chart.marks { transition: opacity 0.2s ease; }

/* Notes on, notes off: a small switch for seeing a marked-up design bare.
   It sits just under the picture, outside it, in line with its left edge,
   where a caption would go. */
.notes-toggle {
  position: absolute;
  top: calc(100% + 12px);
  left: 0;
  display: inline-flex;
  align-items: center;
  gap: 8px;
  margin: 0;
  padding: 2px 0;
  border: 0;
  background: none;
  font: inherit;
  font-size: var(--fs-eyebrow);
  font-weight: 600;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--ink);
  cursor: pointer;
}

.notes-toggle:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 4px;
  border-radius: 4px;
}

.notes-switch {
  position: relative;
  flex: none;
  width: 26px;
  height: 14px;
  border-radius: 100px;
  background: var(--note);
  transition: background 0.15s ease;
}

.notes-switch::after {
  content: "";
  position: absolute;
  top: 2px;
  left: 14px;
  width: 10px;
  height: 10px;
  border-radius: 50%;
  background: #fff;
  transition: left 0.15s ease;
}

.notes-toggle[aria-pressed="false"] { color: var(--muted); }
.notes-toggle[aria-pressed="false"] .notes-switch { background: rgba(22, 21, 19, 0.28); }
.notes-toggle[aria-pressed="false"] .notes-switch::after { left: 2px; }

.notes-off .chart.marks { opacity: 0; }

/* The switch needs the script. */
html:not(.js) .notes-toggle { display: none; }

/* On a short window the foot of the slide is where the slide counter sits,
   so the switch goes to the other end. */
@media (max-height: 820px) {
  .notes-toggle { left: auto; right: 0; }
}

/* Says the page scrolls, until it has been scrolled. */
.page-hint {
  position: absolute;
  right: 14px;
  bottom: 14px;
  margin: 0;
  padding: 4px 10px;
  border-radius: 100px;
  background: rgba(255, 255, 255, 0.92);
  box-shadow: 0 1px 4px rgba(22, 21, 19, 0.12);
  font-size: var(--fs-eyebrow);
  font-weight: 600;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  white-space: nowrap;
  /* The colour of the handwritten notes, like the tags: on a Booking.com
     screen a blue prompt reads as part of the design it sits on. */
  color: var(--note);
  pointer-events: none;
  transition: opacity 0.2s ease;
}

.carousel-card:not(.is-active) .page-hint,
.page.is-scrolled ~ .page-hint { opacity: 0; }

/* A page that scrolls itself needs no prompt to scroll, and a page that fits
   in its card whole has nothing to scroll. */
.page.is-auto ~ .page-hint,
.page.is-whole ~ .page-hint { opacity: 0; }

/* Where a page that scrolls itself ends its trip: a point on the picture,
   placed with top as a share of the picture's height. It sits in the .shot. */
.page-stop,
.page-keep {
  position: absolute;
  left: 0;
  width: 1px;
  height: 1px;
  pointer-events: none;
}

/* The part of the picture that has to be in view when the page rests: top
   and height are set on the mark, as shares of the picture's height. */

/* A page with more than one state: the same page drawn again with one of
   its controls set the other way. The pictures lie on top of each other and
   one shows; the page is as tall as the one that shows. */
.page-state { transition: opacity 0.22s ease; }
.page-state:not(.is-on) {
  position: absolute;
  top: 0;
  left: 0;
  opacity: 0;
  pointer-events: none;
}

/* A layer over the picture, measured in the picture's own units: it is 100
   units tall, so one per cent down it is one unit, however tall the picture
   that shows. --w is the picture's width in those units. */
.page-over {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  aspect-ratio: var(--w, 1164) / 100;
  pointer-events: none;
}
.page-over > * { position: absolute; }

/* The place on the picture where a control's setting is: pressing it shows
   that state. Nothing is drawn; the picture already shows the control. */
.page-spot {
  margin: 0;
  padding: 0;
  border: 0;
  border-radius: 6px;
  background: none;
  cursor: pointer;
  pointer-events: auto;
}
.page-spot:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

/* The ring that shows a press when the page changes state by itself. */
.page-tap { width: 0; height: 0; pointer-events: none; }
.page-tap::after {
  content: "";
  position: absolute;
  left: -20px;
  top: -20px;
  width: 40px;
  height: 40px;
  border-radius: 50%;
  border: 2px solid var(--accent);
  background: color-mix(in srgb, var(--accent) 16%, transparent);
  opacity: 0;
  transform: scale(0.4);
}
.page-tap.is-pressing::after { animation: pageTap 0.75s ease-out; }
@keyframes pageTap {
  0% { opacity: 0; transform: scale(0.4); }
  25% { opacity: 1; transform: scale(0.85); }
  100% { opacity: 0; transform: scale(1.5); }
}
@media (prefers-reduced-motion: reduce) {
  .page-state { transition: none; }
  .page-tap.is-pressing::after { animation: none; }
}

/* Notes on a page that scrolls itself wait until the page has got to them. */
.js .slide.in .page.is-before-stop .chart.marks path { transition: none; stroke-dashoffset: var(--len); }
.js .slide.in .page.is-before-stop .chart.marks text { transition: none; opacity: 0; }


/* A page has no ratio of its own, so in a row of pages every card is as wide
   as the row allows, and the page is shown as large as it can be. */
.has-pages .carousel-card { flex-basis: 84cqw; }

/* A card that holds one whole picture is as tall as the picture, up to the
   height of the row, so the picture is never left floating in a taller card. */
.carousel-card.card--fit {
  height: auto;
  max-height: 100%;
  aspect-ratio: var(--fit, 16 / 10);
  align-self: flex-start;
}

/* A tag names a picture: Before, After, Vision, Exploration 1. It is set
   above the picture, outside it and level with its left edge, so it is read
   before the picture, covers none of it, and can never sit on a handwritten
   note. The card is then a column: the tag, and under it the picture in its
   own rounded frame. --tag-room is what one line of tag takes: the card is
   as wide as the picture that is left under it, and its least width comes
   down by the same share, so a picture that filled its card before the tag
   moved still fills it. */
.carousel-card:has(> .card-tag) {
  --tag-room: 30px;
  display: flex;
  flex-direction: column;
  flex-basis: clamp(
    calc(62cqw - var(--tag-room) * var(--arn, 1.6)),
    calc((100cqh - var(--tag-room)) * var(--arn, 1.6)),
    84cqw
  );
  overflow: visible;
  border: 0;
  border-radius: 0;
  background: none;
}

.has-pages .carousel-card:has(> .card-tag) { flex-basis: 84cqw; }

.carousel-card:has(> .card-tag) > .card-tag {
  position: static;
  order: -1;
  flex: none;
  align-self: flex-start;
  max-width: 100%;
  margin: 0 0 10px;
  padding: 0;
  border-radius: 0;
  background: none;
  box-shadow: none;
  line-height: 20px;
}

.carousel-card:has(> .card-tag) > img,
.carousel-card:has(> .card-tag) > .ph,
.carousel-card:has(> .card-tag) > .page {
  flex: 1 1 0;
  min-height: 0;
  height: auto;
  border: 1px solid var(--line);
  border-radius: 14px;
  background: var(--card);
}

.carousel-card:has(> .card-tag) > .ph { border-style: dashed; border-color: rgba(22, 21, 19, 0.22); }
.carousel-card:has(> .card-tag) > .page { position: relative; inset: auto; background: #fff; }

/* A card that is as tall as its one whole picture: the picture keeps its own
   ratio, and the tag adds its line above it. */
.carousel-card.card--fit:has(> .card-tag) { aspect-ratio: auto; }
.carousel-card.card--fit:has(> .card-tag) > .page {
  flex: 0 1 auto;
  aspect-ratio: var(--fit, 16 / 10);
}

/* The ring that shows which picture the keyboard is on goes round the
   picture, not round the tag over it. */
.carousel:focus-visible .carousel-card.is-active:has(> .card-tag) { outline: 0; }
.carousel:focus-visible .carousel-card.is-active:has(> .card-tag) > :is(img, .ph, .page) {
  outline: 2px solid var(--accent);
  outline-offset: -2px;
}

/* A page that does not scroll: the card shows its top, header and all. */
.page--still { overflow: hidden; }

/* A title header: eyebrow, headline, the short description and a quiet meta
   line, held close together so they read as one block above the media. */
.title-main {
  --sg: 14px;
  display: flex;
  flex-direction: column;
  gap: var(--sg);
}

.title-main .subline { margin-top: 4px; }

.meta-line {
  margin-top: -6px;
  font-size: var(--fs-cap);
  color: var(--faint);
}

/* Words beside the picture (.slide--beside). In a window wider than it is
   tall, a laptop's, words over a picture leave the picture about half the
   window's height. There the slide can turn into two columns: the words on
   the left, the picture on the right, as large as the slide's height
   allows. deck.js lays the slide out both ways and keeps the two columns
   only where the picture comes out larger than with the words over it,
   and the words fit; anywhere else the slide stays exactly as it was, so a
   picture is never made smaller. Nothing else about the slide changes: the
   outline, the numbers and their lines keep their own look.

   One grid for every such slide, so nothing moves as the deck is paged:
   - The words column is the same width on every slide: 30% of the block,
     so the picture has the rest.
   - The label's line runs across the slide. Over the picture it carries the
     picture's names, where a row of pictures is shown one at a time
     (Vision, Before), and the notes switch, at the far right.
   - The picture starts on the next line down, level with the top of the
     headline, and is as tall as the rest of the slide.
   - A picture with a fixed shape (.media-slot--fit, with --side-fit and
     --side-fitn) is as large as that shape allows; if that leaves it
     narrower than its column, the words and the picture move together to
     the middle of the window, the way a picture slide's column is centred.
     A scrolling page, or a stage for phones, takes the whole column.
   The words are in .title-main or .slide-words; .slide-words is not a box
   anywhere else, so the words keep their usual place on a phone or a
   tablet. deck.js builds the names. A window taller than it is wide, or
   shorter than 641px, keeps the words over the picture. */
.slide-words { display: contents; }
.picture-bar { display: none; }

@media (min-width: 901px) and (min-height: 641px) and (min-aspect-ratio: 4 / 3) {
  .slide--beside .slide-inner {
    --label-line: calc(var(--fs-eyebrow) * 1.5 + 16px);
    --words: clamp(320px, 30cqw, 460px);
    --col-w: calc(100cqw - var(--words) - var(--block-gap));
    --col-h: calc(100cqh - var(--label-line));
    container-type: size;
    flex-direction: row;
    align-items: flex-start;
    justify-content: center;
    gap: var(--block-gap);
  }

  .slide--beside :is(.title-main, .slide-words) {
    display: flex;
    flex-direction: column;
    flex: 0 0 var(--words);
    min-width: 0;
  }

  .slide--beside .slide-words { gap: var(--sg, 24px); }

  /* A label longer than the column takes two even lines; deck.js then sets
     --label-line to the label's height, so the picture still starts level
     with the headline. */
  .slide--beside .eyebrow { text-wrap: balance; }

  .slide--beside .slide-inner > .media-slot {
    flex: 0 1 auto;
    width: var(--col-w);
    height: var(--col-h);
    min-width: 0;
    min-height: 0;
    margin-top: var(--label-line);
    container-type: size;
  }

  .slide--beside .slide-inner > .media-slot--fit {
    width: min(var(--col-w), calc(var(--col-h) * var(--side-fitn)));
    height: auto;
    aspect-ratio: var(--side-fit);
  }

  /* A frame fills its slot: a page or a stage of phones takes the column,
     and a slot with a fixed shape is already the picture's shape. */
  .slide--beside .media-slot > .frame {
    width: 100%;
    max-width: none;
    aspect-ratio: auto;
  }

  /* A row of pictures: one at a time, the whole slot, named on the line
     above. A page fills its card; a whole picture keeps its own shape
     inside it, from the card's top left, under its name. */
  .slide--beside .has-bar .carousel-card:has(> .card-tag) {
    flex-basis: 100cqw;
    display: block;
  }
  .slide--beside .has-bar .carousel-card > .card-tag { display: none; }
  .slide--beside .has-bar .carousel-card > :is(.page, .ph) { height: 100%; }
  .slide--beside .has-bar .carousel-card > img {
    display: block;
    width: auto;
    height: auto;
    max-width: 100%;
    max-height: 100%;
  }

  /* The line over the picture. */
  .slide--beside .picture-bar {
    position: absolute;
    top: calc(-1 * var(--label-line));
    left: 0;
    display: flex;
    gap: 22px;
  }

  .slide--beside .media-slot > .notes-toggle,
  .slide--beside .media-slot > .hug > .notes-toggle {
    top: calc(-1 * var(--label-line));
    right: 0;
    left: auto;
    padding: 0;
    line-height: 1.5;
    transition: opacity 0.2s ease, visibility 0.2s;
  }

  /* A picture with no notes on it (a Before) has nothing to switch off. */
  .slide--beside .has-bar.is-bare > .notes-toggle {
    opacity: 0;
    visibility: hidden;
  }

  /* A picture with its numbers (.media-side): the numbers go under the
     words, in the same column, and the picture takes the other column
     whole. The pair is a grid: words over numbers on the left, the picture
     down the right. */
  .slide--beside .slide-inner:has(> .media-side) {
    display: grid;
    grid-template-columns: clamp(320px, 30%, 460px) auto;
    grid-template-rows: auto minmax(0, 1fr);
    justify-content: center;
    align-items: start;
    column-gap: var(--block-gap);
    row-gap: 0;
  }
  .slide--beside .slide-inner:has(> .media-side) > .slide-words { grid-column: 1; grid-row: 1; }
  .slide--beside .media-side { display: contents; }
  /* The numbers keep their own look and width, under the words. */
  .slide--beside .media-side > .stat-trio {
    grid-column: 1;
    grid-row: 2;
    margin-top: var(--block-gap);
  }

  /* Numbers two to a row (.stat-trio--pairs), for a slide with more
     numbers than fit one under another beside its picture. Each number
     keeps its look. As on a slide of numbers, a line runs over every row
     but the first, and the two numbers of a row stand level: each cell
     shares the rows of the grid (a group's name, then the number, then its
     line), so a name over one number does not push it below its neighbour. */
  .slide--beside .media-side > .stat-trio--pairs {
    width: auto;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    column-gap: 24px;
  }
  .slide--beside .media-side > .stat-trio--pairs > .stat-cell {
    display: grid;
    grid-row: span 4;
    grid-template-rows: subgrid;
    align-content: start;
  }
  .slide--beside .stat-trio--pairs .stat-kicker--head { grid-row: 1; }
  .slide--beside .stat-trio--pairs .stat-kicker:not(.stat-kicker--head) { grid-row: 2; }
  .slide--beside .stat-trio--pairs .stat-num { grid-row: 3; }
  .slide--beside .stat-trio--pairs .stat-label { grid-row: 4; }
  .slide--beside .media-side > .stat-trio--pairs > .stat-cell:nth-child(-n + 2) {
    border-top: 0;
    padding-top: 0;
  }
  .slide--beside .media-side > .media-slot {
    grid-column: 2;
    grid-row: 1 / 3;
    width: var(--col-w);
    height: var(--col-h);
    min-height: 0;
    margin-top: var(--label-line);
    container-type: size;
  }
  .slide--beside .media-side > .media-slot--fit {
    width: min(var(--col-w), calc(var(--col-h) * var(--side-fitn)));
    height: auto;
    aspect-ratio: var(--side-fit);
  }

  /* With no box of its own, the numbers' block cannot rise in as one, so
     its two parts rise in as it would have. */
  .js .slide--beside .media-side.r > * {
    opacity: 0;
    transform: translateY(18px);
    transition: opacity 0.7s cubic-bezier(0.22, 1, 0.36, 1), transform 0.7s cubic-bezier(0.22, 1, 0.36, 1);
  }
  .js .slide--beside.in .media-side.r > * {
    opacity: 1;
    transform: none;
    transition-delay: 0.26s;
  }
  .js .slide--beside.in.is-arriving .media-side.r > * {
    transition: none;
    opacity: 0;
    transform: translateY(18px);
  }
}

@media (min-width: 901px) and (min-height: 641px) and (min-aspect-ratio: 4 / 3) and (prefers-reduced-motion: reduce) {
  .js .slide--beside .media-side.r > * {
    opacity: 1 !important;
    transform: none !important;
    transition: none !important;
  }
}

.picture-bar > button {
  margin: 0;
  padding: 0;
  border: 0;
  background: none;
  font: inherit;
  font-size: var(--fs-eyebrow);
  font-weight: 600;
  line-height: 1.5;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--muted);
  cursor: pointer;
  transition: color 0.15s ease;
}

.picture-bar > button:hover { color: var(--ink); }

/* The rest of a name in parts, shown for the picture in view only. */
.picture-bar > button:not([aria-pressed="true"]) > .picture-bar-more,
.picture-bar.is-tight .picture-bar-more { display: none; }

/* The picture in view is named in the colour of the handwritten notes, as
   its tag was, and underlined: the line says which one is showing. */
.picture-bar > button[aria-pressed="true"] {
  color: var(--note);
  text-decoration: underline;
  text-decoration-thickness: 1px;
  text-underline-offset: 6px;
  cursor: default;
}

.picture-bar > button:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 4px;
  border-radius: 4px;
}

/* Stats with drawings, from the case-study-deck system: each stat carries a
   small hand-drawn illustration of its sentence, in ink with one accent
   element, that draws itself in when the slide arrives. */
.stat-trio {
  width: 100%;
  display: grid;
  grid-template-columns: repeat(var(--n, 3), minmax(0, 1fr));
  gap: 0 var(--block-gap);
}

.stat-cell {
  border-top: 1px solid var(--line);
  padding-top: var(--cell-pad);
}

.stat-ill {
  display: block;
  width: 100%;
  max-width: 200px;
  height: 78px;
  margin-bottom: var(--ill-gap);
  overflow: visible;
}

.stat-trio .stat-num {
  display: block;
  font-size: 48px;
  font-weight: 500;
  line-height: 1.05;
  letter-spacing: -0.022em;
  font-feature-settings: normal;
}

.stat-trio .stat-num.todo { display: inline-block; }

.stat-trio .stat-label {
  display: block;
  margin-top: 12px;
  font-size: 16px;
  font-weight: 400;
  color: var(--ink);
}

.stat-range {
  display: block;
  margin-top: 8px;
  font-size: 12px;
  font-weight: 500;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--muted);
}

.chart path {
  fill: none;
  stroke-linecap: round;
  stroke-linejoin: round;
}

.chart .ax { stroke: rgba(22, 21, 19, 0.8); stroke-width: 3; }
.chart .dt { stroke: var(--accent); stroke-width: 3.5; }
.chart .pale { stroke: rgba(22, 21, 19, 0.22); stroke-width: 1.6; }
.chart .bar-bg { stroke: rgba(22, 21, 19, 0.13); stroke-width: 13; stroke-linecap: butt; }
.chart .bar-ink { stroke: rgba(22, 21, 19, 0.55); stroke-width: 13; stroke-linecap: butt; }
.chart .bar-fg { stroke: var(--accent); stroke-width: 13; stroke-linecap: butt; }
.chart text { font-size: 11px; fill: var(--muted); }
.chart text.dnum { font-weight: 600; }

.js .chart path {
  stroke-dasharray: var(--len);
  stroke-dashoffset: var(--len);
}

.js .slide.in .chart path {
  stroke-dashoffset: 0;
  transition: stroke-dashoffset 1s cubic-bezier(0.5, 0, 0.2, 1) var(--d, 0.3s);
}

/* Pointing at a block replays its drawing: the start state, applied for one
   frame, after which the normal transition draws it in again. */
.js .slide.in .chart.is-replaying path { transition: none; stroke-dashoffset: var(--len); }
.js .slide.in .chart.is-replaying text { transition: none; opacity: 0; }

.js .chart text { opacity: 0; }
.js .slide.in .chart text { opacity: 1; transition: opacity 0.4s ease var(--d, 1s); }

@media (prefers-reduced-motion: reduce) {
  .js .chart path { stroke-dashoffset: 0; transition: none; }
  .js .chart text { opacity: 1; transition: none; }
}

@media (min-width: 1600px) {
  .stat-ill { max-width: 220px; height: 86px; }
  .stat-trio .stat-num { font-size: 56px; }
  .stat-trio .stat-label { font-size: 17px; }
  .stat-range { font-size: 13px; }
}

@media (max-height: 840px) {
  .stat-ill { height: 66px; }
  .stat-trio .stat-num { font-size: 40px; }
  .stat-trio .stat-label { font-size: 15px; }
}

/* Isometric line drawings (the hairline look): rounded solids in one stroke,
   a dim crease inside each silhouette, and one part in the deck's blue. */
.iso {
  --iso-plate: var(--bg, #fff);
  --iso-hi: var(--accent);
  --iso-edge: #47484d;
  --iso-mid: #8b8d94;
  --iso-lo: #c6c8ce;
  overflow: visible;
}
.iso path,
.iso ellipse {
  fill: var(--iso-plate);
  stroke: var(--iso-mid);
  stroke-width: 1.4;
  vector-effect: non-scaling-stroke;
  stroke-linejoin: round;
  stroke-linecap: round;
}
.iso .nf { fill: none; }
.iso .sil { stroke: var(--iso-edge); }
.iso .hi { stroke: var(--iso-hi); }
.iso .lo { stroke: var(--iso-lo); }
.iso .dash { stroke-dasharray: 1 3; }
.iso .dot { stroke: none; fill: var(--iso-hi); }
.iso .dot.m { fill: var(--iso-edge); }
.iso .dot.off { fill: var(--iso-lo); }

/* In a drawing slot they keep their own shape: a set height, and the width
   that goes with it, held to the left of the column. */
.iso.stat-ill,
.iso.col-ill { width: auto; max-width: 100%; height: 112px; align-self: flex-start; }
.road-art .iso.col-ill { max-width: calc(100% - 78px); }

@media (min-width: 1600px) {
  .iso.stat-ill,
  .iso.col-ill { height: 132px; }
}

@media (max-height: 840px) {
  .iso.stat-ill,
  .iso.col-ill { height: 92px; }
}

/* A drawing that answers the pointer (deck-figures.js). Its place holds the
   still drawing until the script mounts the live figure; then the place takes
   the figure's resting shape at the same height, and the figure's 400 by 320
   stage is laid over it so that the resting figure fills the place exactly. */
.iso-fig.stat-ill,
.iso-fig.col-ill {
  --ill-h: 112px;
  position: relative;
  width: fit-content;
  max-width: none;
  height: var(--ill-h);
  align-self: flex-start;
}
.iso-fig > svg.iso { display: block; width: auto; height: 100%; }
.iso-fig.is-live { width: calc(var(--ill-h) * var(--fw) / var(--fh)); }
.iso-fig.is-live > svg.iso { display: none; }

.iso-stage {
  position: absolute;
  left: calc(var(--fx) / var(--fw) * -100%);
  top: calc(var(--fy) / var(--fh) * -100%);
  width: calc(400 / var(--fw) * 100%);
  height: calc(320 / var(--fh) * 100%);
  aspect-ratio: auto;
  --hairline-plate: var(--bg, #fff);
  --hairline-hi: var(--accent);
  --hairline-edge: #47484d;
  --hairline-mid: #8b8d94;
  --hairline-lo: #c6c8ce;
  --hairline-stroke: 1.4;
}

@media (min-width: 1600px) {
  .iso-fig.stat-ill,
  .iso-fig.col-ill { --ill-h: 132px; }
}

@media (max-height: 840px) {
  .iso-fig.stat-ill,
  .iso-fig.col-ill { --ill-h: 92px; }
}

/* The arrow between two steps, in the same line as the drawings. */
.chart.road-arrow--line path { stroke: #8b8d94; stroke-width: 1.4; }

/* A drawing of several things side by side needs more height than a drawing
   of one, or each thing in it comes out too small to read. It is also wide,
   up to 2.9 times its height, so in a narrow window its height gives way
   until it fits its column: half the page in two columns, all of it in one. */
.iso-fig.col-ill--lg {
  --ill-max: 144px;
  --ill-h: min(var(--ill-max), calc((100vw - 2 * var(--pad) - var(--block-gap)) / 2 / 2.9));
}
@media (min-width: 1600px) { .iso-fig.col-ill--lg { --ill-max: 168px; } }
@media (max-height: 840px) { .iso-fig.col-ill--lg { --ill-max: 120px; } }
@media (max-width: 560px) {
  .iso-fig.col-ill--lg { --ill-h: min(var(--ill-max), calc((100vw - 2 * var(--pad)) / 2.9)); }
}

/* A row of numbers small enough to share a slide with a picture. */
.stat-trio.stat-trio--sm { gap: 0 32px; }
.stat-trio--sm .stat-cell { padding-top: 12px; }

.stat-trio.stat-trio--sm .stat-num {
  font-size: 26px;
  line-height: 1.1;
  letter-spacing: -0.015em;
  white-space: nowrap;
}

.stat-trio.stat-trio--sm .stat-label {
  margin-top: 4px;
  font-size: var(--fs-cap);
  color: var(--muted);
}

@media (min-width: 1600px) {
  .stat-trio.stat-trio--sm .stat-num { font-size: 30px; }
}

/* A picture with its numbers beside it. The numbers stack down the left,
   under the copy; the picture takes the rest of the width and whatever
   height the copy leaves. */
.media-side {
  flex: 1 1 auto;
  min-height: 220px;
  width: 100%;
  display: flex;
  gap: var(--block-gap);
}

.media-side > .media-slot { flex: 1 1 0; width: auto; min-width: 0; }

/* The name of a group of numbers, over the first of them. */
.stat-kicker {
  display: block;
  margin-bottom: 8px;
  font-size: 12px;
  font-weight: 600;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--accent);
}

/* The numbers, stacked: each under its own hairline, with room between
   them so the column reads as five separate figures, not a block. */
.media-side > .stat-trio { flex: 0 0 264px; width: 264px; align-self: flex-start; }
.media-side > .stat-trio .stat-cell { padding: 18px 0 22px; }
.media-side > .stat-trio .stat-cell:first-child { padding-top: 16px; }
.media-side > .stat-trio .stat-cell:last-child { padding-bottom: 0; }
.media-side > .stat-trio.stat-trio--sm .stat-label { margin-top: 6px; }

/* The heading of a whole column of numbers: one word, marked with the same
   highlight as the slide's title, standing clear of what follows. The cell
   it opens has no rule over it, so the mark sits level with the top of the
   picture beside it. The mark overhangs to the left, so the word itself
   lines up with the numbers under it. */
.stat-kicker--head {
  margin-bottom: 22px;
  line-height: 1;
  color: var(--ink);
}
.stat-kicker--head .hl { display: inline-block; margin-left: -5px; padding: 4px 5px 3px; }
.media-side > .stat-trio .stat-cell.stat-cell--open:first-child { border-top: 0; padding-top: 0; }

@media (min-width: 1600px) {
  .media-side > .stat-trio { flex-basis: 288px; width: 288px; }
  .media-side > .stat-trio .stat-cell { padding: 22px 0 26px; }
  .stat-kicker--head { margin-bottom: 26px; }
}

@media (max-height: 840px) {
  .media-side > .stat-trio .stat-cell { padding: 12px 0 14px; }
  .stat-kicker--head { margin-bottom: 16px; }
}

@media (max-height: 640px) {
  .media-side { flex: none; }
}

/* On a tablet the numbers sit over the picture, in two columns, and the
   picture takes the height that is left. */
@media (min-width: 700px) and (max-width: 900px) {
  .media-side { flex-direction: column; gap: 20px; }
  .media-side > .stat-trio { flex: none; width: 100%; }
  .media-side > .stat-trio .stat-cell { padding: 12px 0 14px; }
}

@media (max-width: 699px) {
  .media-side { display: block; }
  .media-side > .stat-trio { width: 100%; margin-bottom: 24px; }
  .media-slot > .hug { position: relative; max-height: none; margin-bottom: 44px; }
  .media-slot.is-box { container-type: inline-size; }
  .media-slot > .hug--contain { width: 100%; }
  .media-slot > .popout { position: relative; width: calc(100% / var(--room, var(--over, 1))); }
}

/* Numbers in two groups, a "before" and an "after", each under its own rule
   and its own name, so the contrast is seen before it is read. The groups
   share one three-column grid, so every number keeps the column it would
   have in a plain row of three. */
.stat-split {
  width: 100%;
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 0 var(--block-gap);
}
.stat-group { border-top: 1px solid var(--line); padding-top: var(--cell-pad); }
.stat-group--2 { grid-column: span 2; }
.stat-group > .stat-kicker { margin-bottom: 18px; }
.stat-group > .stat-trio .stat-cell { border-top: 0; padding-top: 0; }

/* A line of proof under one number, in the caption's voice. */
.stat-note {
  display: block;
  margin-top: 10px;
  font-size: var(--fs-cap);
  color: var(--muted);
  text-wrap: balance;
}

@media (max-height: 840px) { .stat-group > .stat-kicker { margin-bottom: 12px; } }

@media (max-width: 900px) {
  .stat-split { grid-template-columns: 1fr; row-gap: 28px; }
  .stat-group--2 { grid-column: auto; }
}

.cut {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 10px 14px;
  padding-top: 18px;
  border-top: 1px solid var(--line);
}

/* The last slide: back to all work, on to the next case study. Pointing at
   the next card previews it. */
.slide--next .slide-inner { justify-content: center; min-height: calc(100svh - var(--pad) - var(--pad-top, var(--pad))); }

.dnext {
  position: relative;
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 56px;
  /* The full width of the slide's content, so "All work" starts on its left
     edge and the next case study ends on its right edge. */
  width: 100%;
}

.dnext-card {
  position: relative;
  display: flex;
  flex-direction: column;
  gap: 12px;
  max-width: 480px;
  color: inherit;
  text-decoration: none;
}

.dnext-card--next { align-items: flex-end; text-align: right; margin-left: auto; }

.dnext-label {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--accent);
}

.dnext-card--prev .dnext-label::before { content: "\2190"; font-size: 13px; transition: transform 0.3s ease; }
.dnext-card--next .dnext-label::after { content: "\2192"; font-size: 13px; transition: transform 0.3s ease; }
.dnext-card--prev:is(:hover, :focus-visible) .dnext-label::before { transform: translateX(-3px); }
.dnext-card--next:is(:hover, :focus-visible) .dnext-label::after { transform: translateX(3px); }

.dnext-title {
  font-size: 26px;
  font-weight: 500;
  line-height: 1.2;
  letter-spacing: -0.016em;
  color: var(--ink);
  transition: color 0.2s ease;
}

.dnext-card:is(:hover, :focus-visible) .dnext-title { color: var(--accent); }

.dnext-card:focus-visible { outline: 2px solid var(--accent); outline-offset: 6px; border-radius: 6px; }

.dnext-meta { font-size: 14px; line-height: 1.5; color: var(--muted); }

.dnext-shot {
  position: absolute;
  right: 0;
  bottom: calc(100% + 22px);
  width: 400px;
  aspect-ratio: 16 / 10;
  overflow: hidden;
  border: 1px solid var(--line);
  border-radius: 10px;
  background: var(--card);
  box-shadow: 0 18px 40px -18px rgba(22, 21, 19, 0.28);
  pointer-events: none;
  opacity: 0;
  transform: translateY(10px) scale(0.97);
  transition: opacity 0.3s ease, transform 0.3s ease;
}

.dnext-shot img { display: block; width: 100%; height: 100%; object-fit: cover; object-position: top center; }

.dnext-card--next:is(:hover, :focus-visible) .dnext-shot { opacity: 1; transform: none; }

@media (hover: none) { .dnext-shot { display: none; } }

@media (prefers-reduced-motion: reduce) {
  .dnext-shot,
  .dnext-label::before,
  .dnext-label::after,
  .dnext-title { transition: none; }
}

@media (max-width: 900px) {
  .dnext { flex-direction: column; align-items: stretch; gap: 30px; }
  .dnext-card { max-width: 100%; }
  .dnext-card--next { align-items: flex-start; text-align: left; margin-left: 0; }
  .dnext-title { font-size: 22px; }
}

@media (max-width: 699px) {
  .slide--next .slide-inner { min-height: 0; }
}

.quote {
  font-size: var(--fs-row);
  font-weight: 500;
  letter-spacing: -0.02em;
}

.deck-links {
  display: flex;
  flex-wrap: wrap;
  gap: 12px 28px;
  padding-top: 18px;
  border-top: 1px solid var(--line);
  font-size: var(--fs-cap);
}

.deck-links a {
  text-decoration: none;
  color: var(--muted);
}

.deck-links a:hover,
.deck-links a:focus-visible { color: var(--ink); }

/* ── Deck chrome ───────────────────────────────────────────────────────── */

.deck-back {
  position: fixed;
  top: 20px;
  left: 20px;
  z-index: 20;
  display: inline-flex;
  align-items: center;
  gap: 8px;
  padding: 7px 14px;
  border-radius: 100px;
  border: 1px solid var(--line);
  background: rgba(255, 255, 255, 0.86);
  -webkit-backdrop-filter: blur(10px);
  backdrop-filter: blur(10px);
  font-size: 13px;
  font-weight: 500;
  text-decoration: none;
}

.deck-count {
  position: fixed;
  left: 24px;
  bottom: 18px;
  z-index: 20;
  font-size: 12px;
  letter-spacing: 0.06em;
  color: var(--faint);
  font-feature-settings: "tnum";
}

.deck-bar {
  position: fixed;
  left: 0;
  bottom: 0;
  z-index: 20;
  height: 2px;
  width: 0;
  background: var(--accent);
  transition: width 0.35s ease;
}

/* On a window wider than the page's block, the way back and the slide count
   come in from the screen's corners to the block's left edge, the line the
   slides start on. On anything narrower they stay where they were. */
@media (min-width: 1600px) {
  .deck-back,
  .deck-count { left: max(var(--pad), calc(50% - 780px)); }
}

@media (max-width: 699px) {
  .deck-count { display: none; }
}

/* ── Reveals ───────────────────────────────────────────────────────────── */

/* Content is visible without script. With script, a slide's pieces rise in
   once, when the slide arrives. */
.js .r {
  opacity: 0;
  transform: translateY(18px);
  transition: opacity 0.7s cubic-bezier(0.22, 1, 0.36, 1), transform 0.7s cubic-bezier(0.22, 1, 0.36, 1);
}

.js .slide.in .r {
  opacity: 1;
  transform: none;
}

.js .slide.in .r[data-d="1"] { transition-delay: 0.08s; }
.js .slide.in .r[data-d="2"] { transition-delay: 0.16s; }
.js .slide.in .r[data-d="3"] { transition-delay: 0.26s; }
.js .slide.in .r[data-d="4"] { transition-delay: 0.36s; }

/* Every arrival by the pager plays the slide in again. The content fades and
   rises in as one page, while its pieces and drawings start from hidden for
   one frame, so they rise and draw as they did the first time. */
@keyframes deckSwapIn {
  from { opacity: 0; transform: translateY(14px); }
  to { opacity: 1; transform: none; }
}

.deck-swap-in { animation: deckSwapIn 0.5s cubic-bezier(0.22, 1, 0.36, 1) both; }

.js .slide.in.is-arriving .r { transition: none; opacity: 0; transform: translateY(18px); }
.js .slide.in.is-arriving .chart path { transition: none; stroke-dashoffset: var(--len); }
.js .slide.in.is-arriving .chart text { transition: none; opacity: 0; }

@media (prefers-reduced-motion: reduce) {
  .js .r {
    opacity: 1 !important;
    transform: none !important;
    transition: none !important;
  }
  .deck-swap-in { animation: none; }
  .deck-bar { transition: none; }
}

/* ── Narrow screens ────────────────────────────────────────────────────── */

@media (max-width: 1100px) {
  .road { grid-template-columns: repeat(3, minmax(0, 1fr)); row-gap: 28px; }
}

@media (max-width: 900px) {
  .cols { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .media-row { grid-template-columns: 1fr; }
  .split { grid-template-columns: 1fr; gap: 28px; }
  .stat-trio { grid-template-columns: repeat(2, minmax(0, 1fr)); row-gap: 28px; }
}

/* A tablet keeps a row of steps three across (the rule above, for windows
   under 1100px), so a slide of steps still fits one screen. It has room at
   its foot for the notes switch, which goes to the right, clear of the
   slide counter. */
@media (min-width: 700px) and (max-width: 900px) {
  .slide:has(.notes-toggle) { padding-bottom: 64px; }
  .notes-toggle { left: auto; right: 0; }
  /* A large drawing must still fit a column half the tablet's width. */
  .iso-fig.col-ill--lg { --ill-max: 112px; }
}

/* A phone: the slide is as tall as what it holds, and a picture is a block
   in the page, not a share of the window. */
@media (max-width: 699px) {
  .road { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .frame--fit {
    height: auto;
    width: 100%;
  }
  .slide--fill,
  .slide--fill .slide-inner { height: auto; }
  .media-slot { flex: none; min-height: 0; }
  .has-carousel { container-type: inline-size; }
  .carousel { position: static; }
  .carousel-card {
    flex-basis: 84cqw;
    height: auto;
    aspect-ratio: var(--ar, 16 / 10);
  }
  /* With a tag over it, the picture keeps that ratio and the tag adds its
     own line, or two where a long tag wraps. */
  .carousel-card:has(> .card-tag) { flex-basis: 84cqw; aspect-ratio: auto; }
  .carousel-card:has(> .card-tag) > :is(img, .ph, .page),
  .carousel-card.card--fit:has(> .card-tag) > .page {
    flex: none;
    width: 100%;
    aspect-ratio: var(--ar, 16 / 10);
  }
  .media-slot > .frame,
  .media-slot > .ph {
    position: static;
    width: 100%;
    height: auto;
  }
}

@media (max-width: 560px) {
  .stat-trio { grid-template-columns: 1fr; }
  .cols { grid-template-columns: 1fr; }
  .road { grid-template-columns: 1fr; }
}

/* ── The password prompt ─────────────────────────────────────────────────
   The same soft gate as the long-form case studies. A [data-paywall-blur]
   marker sits in <main> before the first private slide. Until the visitor
   has the pass, every slide after the marker is blurred and its pictures
   are hidden, and deck.js raises a sheet at the foot of the window that
   asks for the password. The page's source still holds everything: this is
   a courtesy lock, not a vault. */
[data-paywall-blur] {
  display: block;
  width: 0;
  height: 0;
  margin: 0;
  padding: 0;
  border: 0;
}

html:not(.is-paywall-authed) [data-paywall-blur] ~ .slide > .slide-inner {
  filter: blur(8px) saturate(0.9);
  pointer-events: none;
  -webkit-user-select: none;
  user-select: none;
  -webkit-touch-callout: none;
}

/* Pictures are taken out altogether, so nothing reads through the blur. */
html:not(.is-paywall-authed) [data-paywall-blur] ~ .slide :is(img, video, iframe) { visibility: hidden; }

/* At the moment of unlocking, and only then, the blur lifts as a change of
   focus. A visitor who already has the pass never sees it. */
html.is-paywall-lifting [data-paywall-blur] ~ .slide > .slide-inner {
  transition: filter 0.45s cubic-bezier(0.25, 0.46, 0.45, 0.94);
}

/* The sheet: white frosted glass along the foot of the window, the same
   surface and wording as the long-form case studies' prompt. */
.gate {
  --g400: #a8a59e;
  --g500: #8a877f;
  --g700: #4a4842;
  --gate-ease: cubic-bezier(0.25, 0.46, 0.45, 0.94);
  position: fixed;
  left: 0;
  right: 0;
  bottom: 0;
  z-index: 70;
  display: flex;
  align-items: center;
  justify-content: center;
  min-height: max(42vh, 360px);
  padding: 56px 48px max(48px, env(safe-area-inset-bottom));
  background: rgba(255, 255, 255, 0.82);
  -webkit-backdrop-filter: blur(28px) saturate(160%);
  backdrop-filter: blur(28px) saturate(160%);
  border-top: 1px solid rgba(255, 255, 255, 0.6);
  box-shadow:
    0 -1px 2px rgba(15, 23, 42, 0.04),
    0 -24px 60px -16px rgba(15, 23, 42, 0.18);
  /* Away: below the window, its top fade with it. */
  transform: translateY(calc(100% + 60px));
  visibility: hidden;
  transition: transform 0.5s var(--gate-ease), visibility 0s linear 0.5s;
  will-change: transform;
}

@supports not ((-webkit-backdrop-filter: blur(1px)) or (backdrop-filter: blur(1px))) {
  .gate { background: rgba(255, 255, 255, 0.98); }
}

/* A fade above the sheet, so its top edge is not a hard line. */
.gate::before {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  top: -56px;
  height: 56px;
  background: linear-gradient(to bottom, rgba(255, 255, 255, 0) 0%, rgba(255, 255, 255, 0.45) 100%);
  pointer-events: none;
}

.gate.is-visible {
  transform: none;
  visibility: visible;
  transition: transform 0.5s var(--gate-ease), visibility 0s;
}

/* Copy on the left, a hairline, the form on the right, held together in
   the middle of the sheet. */
.gate-main {
  display: grid;
  grid-template-columns: auto auto auto;
  align-items: center;
  justify-content: center;
  gap: 36px;
  width: 100%;
  max-width: 1180px;
}

.gate-copy { min-width: 0; }

.gate-eyebrow {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  margin-bottom: 18px;
  padding: 5px 11px;
  background: rgba(255, 255, 255, 0.6);
  border: 1px solid rgba(0, 0, 0, 0.05);
  border-radius: 100px;
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.09em;
  text-transform: uppercase;
  color: var(--g700);
}

.gate-eyebrow-dot {
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: var(--ink);
}

.gate-title {
  max-width: 18ch;
  margin: 0 0 14px;
  font-size: 30px;
  font-weight: 600;
  line-height: 1.15;
  letter-spacing: -0.022em;
  color: var(--ink);
}

.gate-blurb {
  max-width: 38ch;
  margin: 0;
  font-size: 15px;
  line-height: 1.55;
  color: var(--g500);
}

.gate-blurb a {
  color: var(--ink);
  text-decoration: underline;
  text-decoration-color: rgba(0, 0, 0, 0.25);
  text-underline-offset: 3px;
  transition: text-decoration-color 0.18s var(--gate-ease);
}

.gate-blurb a:hover { text-decoration-color: var(--ink); }

.gate-divider {
  align-self: stretch;
  width: 1px;
  max-height: 140px;
  background: linear-gradient(to bottom, transparent 0%, rgba(0, 0, 0, 0.1) 20%, rgba(0, 0, 0, 0.1) 80%, transparent 100%);
}

.gate-form {
  display: flex;
  flex-direction: column;
  align-items: stretch;
  gap: 12px;
}

.gate-label {
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.09em;
  text-transform: uppercase;
  color: var(--g500);
}

.gate-field {
  display: flex;
  align-items: stretch;
  gap: 8px;
}

.gate-input {
  box-sizing: border-box;
  width: 260px;
  padding: 14px 18px;
  border: 1px solid rgba(0, 0, 0, 0.12);
  border-radius: 12px;
  outline: none;
  background: rgba(255, 255, 255, 0.7);
  font-family: inherit;
  font-size: 15px;
  line-height: 1.3;
  color: var(--ink);
  -webkit-appearance: none;
  appearance: none;
  transition: border-color 0.18s var(--gate-ease), box-shadow 0.18s var(--gate-ease), background 0.18s var(--gate-ease);
}

.gate-input::placeholder { color: var(--g400); }
.gate-input:hover { border-color: rgba(0, 0, 0, 0.22); }

.gate-input:focus-visible {
  border-color: var(--ink);
  background: rgba(255, 255, 255, 0.92);
  box-shadow: 0 0 0 3px rgba(22, 21, 19, 0.08);
}

.gate-input.is-error {
  border-color: rgba(192, 28, 28, 0.55);
  box-shadow: 0 0 0 3px rgba(192, 28, 28, 0.1);
}

.gate-submit {
  flex: 0 0 auto;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  padding: 14px 22px;
  border: 0;
  border-radius: 12px;
  background: var(--ink);
  font-family: inherit;
  font-size: 14px;
  font-weight: 500;
  letter-spacing: -0.003em;
  white-space: nowrap;
  color: var(--bg);
  cursor: pointer;
  -webkit-appearance: none;
  appearance: none;
  transition: opacity 0.18s var(--gate-ease);
}

.gate-submit:hover { opacity: 0.92; }
.gate-submit:disabled { opacity: 0.55; cursor: progress; }

.gate-submit:focus-visible {
  outline: 2px solid var(--ink);
  outline-offset: 2px;
}

.gate-arrow {
  flex-shrink: 0;
  width: 14px;
  height: 14px;
}

@media (max-width: 860px) {
  .gate {
    min-height: max(44vh, 360px);
    padding: 28px 22px max(24px, env(safe-area-inset-bottom));
  }
  .gate-main {
    grid-template-columns: 1fr;
    gap: 22px;
  }
  .gate-divider { display: none; }
  .gate-title {
    max-width: none;
    font-size: 24px;
  }
  .gate-blurb {
    max-width: none;
    font-size: 14px;
  }
  .gate-form,
  .gate-field { width: 100%; }
  .gate-input {
    flex: 1 1 auto;
    width: auto;
    min-width: 0;
  }
}

@media (max-height: 560px) {
  .gate {
    min-height: 0;
    padding: 22px 32px max(20px, env(safe-area-inset-bottom));
  }
  .gate-title {
    margin-bottom: 6px;
    font-size: 22px;
  }
  .gate-eyebrow { margin-bottom: 10px; }
}

@media (prefers-reduced-motion: reduce) {
  .gate,
  .gate.is-visible,
  html.is-paywall-lifting [data-paywall-blur] ~ .slide > .slide-inner { transition: none; }
}
