/* ============================================================
   DRAGONFLY · SCROLL NARRATIVE

   Interaction model approved as an override of the brief's
   presentation-only model (§2, §11, §19), on explicit
   instruction, referenced to farrah.recreaid.com.
   The composition freeze on pages 1 to 13 is NOT overridden:
   every page keeps its exact 1280x720 layout. Scroll changes
   how a page arrives, never what it looks like when it lands.

   Borrowed from the reference: pinned sections, scroll-driven
   reveal, word illumination, clip headline reveal, grain,
   long decelerated easing.
   Not borrowed: its palette, its typefaces, its content
   devices (scroll film, marquee, tag chips, testimonials).
   Dragonfly's identity is unchanged.

   No wheel or touch event is intercepted anywhere. Native
   scrolling only, so the reader keeps full control.
   ============================================================ */

:root {
  /* long decelerated curve. this is what makes motion read as
     expensive rather than snappy. */
  --ease: cubic-bezier(.22, 1, .36, 1);
  --t-reveal: 1100ms;
  --t-clip: 1150ms;
  --t-page: 600ms;
  --t-chrome: 240ms;
}

html { scroll-behavior: smooth; }
@media (prefers-reduced-motion: reduce) { html { scroll-behavior: auto; } }

/* ------------------------------------------------------------
   GRAIN
   A single fixed noise layer at very low opacity. Costs almost
   nothing and gives the black ground depth on a projector and
   on a laptop alike.
   ------------------------------------------------------------ */
#grain {
  position: fixed;
  inset: -120px;
  z-index: 90;
  pointer-events: none;
  opacity: 0.05;
  background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='140' height='140'><filter id='n'><feTurbulence type='fractalNoise' baseFrequency='0.85' numOctaves='3' stitchTiles='stitch'/><feColorMatrix type='saturate' values='0'/></filter><rect width='140' height='140' filter='url(%23n)'/></svg>");
  animation: grain 6s steps(6) infinite;
  will-change: transform;
}
@keyframes grain {
  0%   { transform: translate(0, 0); }
  20%  { transform: translate(-24px, 12px); }
  40%  { transform: translate(18px, -20px); }
  60%  { transform: translate(-14px, -12px); }
  80%  { transform: translate(22px, 16px); }
  100% { transform: translate(0, 0); }
}
@media (prefers-reduced-motion: reduce) { #grain { animation: none; } }

/* ------------------------------------------------------------
   SCROLL CONTAINER AND PINNED PAGES
   Each page is pinned for the length of its section while its
   choreography plays out, then releases to the next.
   ------------------------------------------------------------ */
/* No overflow property on #deck or body. Any value other than
   visible makes the element a scroll container, which breaks
   position: sticky on .pin. Horizontal overflow is contained by
   .pin's own overflow: hidden instead. */

.frame {
  position: relative;
  height: calc(var(--span, 200) * 1vh);
  background: var(--ink);
}
.frame--paper { background: var(--paper); }

.pin {
  position: sticky;
  top: 0;
  height: 100vh;
  height: 100dvh;
  display: grid;
  place-items: center;
  overflow: hidden;
}

/* the page. composition never changes, only scale. */
.slide {
  position: relative;
  width: calc(var(--stage-w) * 1px);
  height: calc(var(--stage-h) * 1px);
  flex: none;
  transform: scale(var(--scale, 1));
  transform-origin: center center;
  background: var(--ink);
  overflow: hidden;
}
.slide--paper { background: var(--paper); }

/* ------------------------------------------------------------
   CHOREOGRAPHY

   Five behaviours, not one. Each is derived from what the page
   IS, so the motion carries the page's own reading order rather
   than decorating it. --i is the reveal order, assigned at
   runtime from measured layout; --stagger is set per page type.

   reveal-1 is the page landing: headline and lead, as one block.
   reveal-2 is the content, which arrives in the page's own order.
   ------------------------------------------------------------ */
.reveal-1 {
  opacity: 0;
  transform: translateY(40px);
  transition: opacity var(--t-reveal) var(--ease), transform var(--t-reveal) var(--ease);
}
.frame.r1 .reveal-1 { opacity: 1; transform: none; }

.reveal-2 > * {
  opacity: 0;
  transform: translateY(var(--rise, 22px));
  transition:
    opacity var(--t-reveal) var(--ease),
    transform var(--t-reveal) var(--ease);
}
.frame.r2 .reveal-2 > * {
  opacity: 1;
  transform: none;
  transition-delay: calc(var(--i, 0) * var(--stagger, 0ms));
}

/* rules and marks draw rather than fade, on the same cue */
.reveal-2 > .rule, .reveal-2 > .mark,
.reveal-1 .rule, .reveal-1 .mark,
.slide > .rule, .slide > .mark {
  opacity: 1;
  transform: scaleX(0);
  transform-origin: left center;
  transition: transform var(--t-reveal) var(--ease);
}
.rule--v { transform: scaleY(0); transform-origin: center top; }
.frame.r2 .reveal-2 > .rule,
.frame.r2 .reveal-2 > .mark {
  transform: none;
  transition-delay: calc(var(--i, 0) * var(--stagger, 0ms));
}
.frame.r1 .reveal-1 .rule, .frame.r1 .reveal-1 .mark,
.frame.r1 .slide > .rule, .frame.r1 .slide > .mark { transform: none; }

/* ── per type ──────────────────────────────────────────────── */

/* STATEMENT · the page lands. Nothing cascades. Stillness is
   the point, so the content arrives as one piece. */
.frame[data-type="statement"], .frame[data-type="cover"] { --stagger: 0ms; --rise: 26px; }

/* MOMENT · page 6 carries the deck's central hypothesis.
   "The value." is held back and arrives alone, after a beat.
   The pause is the argument. */
.frame[data-type="moment"] { --stagger: 0ms; --rise: 26px; }
.frame[data-type="moment"] .reveal-1 .display-l.c-pink,
.frame[data-type="moment"] .reveal-1 .label:nth-of-type(3) {
  opacity: 0;
  transform: translateY(30px);
  transition: opacity 1.3s var(--ease), transform 1.3s var(--ease);
}
.frame[data-type="moment"].r1 .reveal-1 .label:nth-of-type(3) {
  opacity: 1; transform: none; transition-delay: 700ms;
}
.frame[data-type="moment"].r1 .reveal-1 .display-l.c-pink {
  opacity: 1; transform: none; transition-delay: 900ms;
}

/* FIGURES · the number arrives before its caption, so the
   figure is read as a figure and not as a label with a number. */
.frame[data-type="figures"] { --stagger: 70ms; --rise: 18px; }

/* ROWS · cascade down the page, following the reading order the
   content already has. The rule draws with its row. */
.frame[data-type="rows"] { --stagger: 60ms; --rise: 16px; }

/* GRID · arrives left to right across the columns. */
.frame[data-type="grid"] { --stagger: 50ms; --rise: 20px; }

/* IMAGE · the photograph settles rather than appears. */
.frame[data-type="image"] { --stagger: 40ms; --rise: 20px; }
.frame[data-type="image"] .img img {
  transform: scale(1.06);
  transition: transform 2600ms var(--ease);
}
.frame[data-type="image"].r1 .img img { transform: none; }

/* ── FAST SCROLL ───────────────────────────────────────────────
   Someone moving quickly should not be made to wait for content
   they have already scrolled past. Reveals snap instead. */
body.fast .reveal-1,
body.fast .reveal-2 > *,
body.fast .reveal-2 > .rule,
body.fast .reveal-2 > .mark,
body.fast .clip-line > span {
  transition-duration: 180ms !important;
  transition-delay: 0ms !important;
}

/* ------------------------------------------------------------
   CLIP HEADLINE REVEAL
   Each headline line rises out of a mask. Built at runtime so
   the source HTML stays plain text.
   ------------------------------------------------------------ */
.clip-line {
  display: block;
  overflow: hidden;
  padding-bottom: 0.16em;
  margin-bottom: -0.16em;
}
.clip-line > span {
  display: inline-block;
  transform: translateY(115%);
  transition: transform var(--t-clip) var(--ease);
}
.frame.r1 .clip-line > span { transform: none; }
.clip-line:nth-child(2) > span { transition-delay: 90ms; }
.clip-line:nth-child(3) > span { transition-delay: 170ms; }

/* ------------------------------------------------------------
   WORD ILLUMINATION
   The editorial serif lights word by word as the page is read.
   ------------------------------------------------------------ */
.serif .w { opacity: 0.16; transition: opacity 320ms linear; }
.serif .w.lit { opacity: 1; }
.slide--paper .serif .w { opacity: 0.2; }
.slide--paper .serif .w.lit { opacity: 1; }

/* ------------------------------------------------------------
   PROGRESS
   ------------------------------------------------------------ */
#progress {
  position: fixed;
  left: 0; top: 0;
  height: 2px;
  background: var(--pink);
  width: 0;
  z-index: 80;
  pointer-events: none;
}

/* ------------------------------------------------------------
   SPINE
   Persistent section navigation, set in the deck's own
   metadata style and drawn from its own section markers.
   ------------------------------------------------------------ */
/* ------------------------------------------------------------
   SPINE
   Lives in the letterbox gutter beside the page. When the
   viewport is too narrow to have a gutter it collapses to ticks
   and only opens on hover or focus, over a panel in the page's
   own ground colour. A solid panel rather than a blurred one:
   glass is on the brief's list of things not to do, and a flat
   editorial panel matches the deck's own language.
   ------------------------------------------------------------ */
#spine {
  position: fixed;
  left: var(--spine-left, 0px); top: 50%;
  transform: translateY(-50%);
  z-index: 70;
  opacity: 0;
  transition: opacity var(--t-page) var(--ease);
  pointer-events: none;
  padding: 18px calc(var(--spine-pad, 18px)) 18px 18px;
}
body.spine-on #spine { opacity: 1; pointer-events: auto; }

/* the panel. hidden when the spine has a gutter to sit in. */
#spine::before {
  content: "";
  position: absolute;
  inset: 0;
  background: var(--spine-panel, transparent);
  border-right: var(--rule-hair) solid transparent;
  opacity: 0;
  transition: opacity var(--t-chrome) var(--ease);
  pointer-events: none;
}
body.spine-tight #spine:hover::before,
body.spine-tight #spine:focus-within::before {
  opacity: 1;
  border-right-color: var(--spine-edge, transparent);
}

#spine ol { position: relative; list-style: none; margin: 0; padding: 0; display: grid; gap: 11px; }
#spine button {
  all: unset;
  cursor: pointer;
  display: grid;
  grid-template-columns: 20px auto;
  align-items: center;
  gap: 10px;
  font-size: 8.5px;
  font-weight: 600;
  letter-spacing: var(--track-meta);
  text-transform: uppercase;
  color: var(--spine-dim, #4A4A4A);
  transition: color var(--t-chrome) var(--ease);
}
#spine .sp-n { font-variant-numeric: tabular-nums lining-nums; }
#spine .sp-t {
  white-space: nowrap;
  opacity: 0;
  transform: translateX(-6px);
  transition: opacity var(--t-chrome) var(--ease), transform var(--t-chrome) var(--ease);
}
#spine button:hover { color: var(--pink); }
#spine li.is-on button { color: var(--pink); }

/* WIDE: a real gutter exists, so the spine can simply live in it */
body:not(.spine-tight) #spine .sp-t { opacity: 1; transform: none; }

/* TIGHT: no gutter. Collapse to ticks; open on hover or focus. */
body.spine-tight #spine button { grid-template-columns: 20px 0; }
body.spine-tight #spine .sp-n { opacity: 0.55; }
body.spine-tight #spine li.is-on .sp-n { opacity: 1; }
body.spine-tight #spine:hover button,
body.spine-tight #spine:focus-within button { grid-template-columns: 20px auto; }
body.spine-tight #spine:hover .sp-t,
body.spine-tight #spine:focus-within .sp-t { opacity: 1; transform: none; }
body.spine-tight #spine:hover .sp-n,
body.spine-tight #spine:focus-within .sp-n { opacity: 1; }

/* very short viewports: the spine is more trouble than it is worth */
@media (max-height: 420px) { #spine { display: none; } }

/* ------------------------------------------------------------
   SCROLL CUE, COVER ONLY
   ------------------------------------------------------------ */
#cue {
  position: fixed;
  left: 50%; bottom: 16px;
  transform: translateX(-50%);
  z-index: 70;
  display: grid;
  justify-items: center;
  gap: 7px;
  font-size: 8px;
  font-weight: 600;
  letter-spacing: var(--track-meta);
  text-transform: uppercase;
  color: var(--ink-foot);
  opacity: 0;
  transition: opacity 600ms var(--ease);
  pointer-events: none;
}
body.at-cover #cue { opacity: 1; }
.cue-r { width: 1px; height: 22px; background: var(--pink); transform-origin: top; animation: cue 2.4s var(--ease) infinite; }
@keyframes cue {
  0%   { transform: scaleY(0); opacity: 0; }
  30%  { transform: scaleY(1); opacity: 1; }
  70%  { transform: scaleY(1); opacity: 1; }
  100% { transform: scaleY(1) translateY(22px); opacity: 0; }
}
@media (prefers-reduced-motion: reduce) { .cue-r { animation: none; } }

/* ------------------------------------------------------------
   CHROME
   ------------------------------------------------------------ */
#zones { display: none; }
#chrome {
  position: fixed;
  right: 24px; bottom: 20px;
  z-index: 75;
  display: flex;
  align-items: center;
  gap: 14px;
  font-family: var(--sans);
  font-size: 9px;
  font-weight: 600;
  letter-spacing: var(--track-meta);
  text-transform: uppercase;
  color: var(--chrome-fg, #6E6E6E);
  opacity: 0;
  transition: opacity var(--t-chrome) var(--ease), color var(--t-page) var(--ease);
  pointer-events: none;
}
body.chrome-visible #chrome { opacity: 1; pointer-events: auto; }
#chrome button {
  all: unset; cursor: pointer; padding: 4px 2px; color: inherit;
  transition: color var(--t-chrome) var(--ease);
}
#chrome button:hover, #chrome button:focus-visible { color: var(--pink); }
#chrome .count { font-variant-numeric: tabular-nums lining-nums; }
#chrome .btn-prev, #chrome .btn-next { display: none; }
body.is-present #chrome .btn-prev,
body.is-present #chrome .btn-next { display: inline; }

/* ------------------------------------------------------------
   PRESENT MODE
   The room still needs a deck. Press P: the scroll narrative
   collapses to one page per screen, snapped, arrow-driven.
   Same pages, same composition, no scroll travel.
   ------------------------------------------------------------ */
body.is-present #deck { scroll-snap-type: y mandatory; }
body.is-present .frame {
  height: 100vh; height: 100dvh;
  scroll-snap-align: center;
  scroll-snap-stop: always;
}
body.is-present #spine,
body.is-present #cue,
body.is-present #grain { display: none; }
body.is-present .serif .w { opacity: 1; }
body.is-present #zones { display: flex; position: fixed; inset: 0; z-index: 20; }
body.is-present #zones .zone { height: 100%; }
body.is-present #zones .zone--prev { width: 33%; cursor: w-resize; }
body.is-present #zones .zone--next { flex: 1; cursor: e-resize; }
body.is-present.is-overview #zones { display: none; }

/* ------------------------------------------------------------
   OVERVIEW
   ------------------------------------------------------------ */
#overview {
  position: fixed; inset: 0; z-index: 85;
  background: var(--ink);
  padding: 44px 48px;
  overflow-y: auto;
  display: none; opacity: 0;
  transition: opacity var(--t-page) var(--ease);
}
body.is-overview #overview { display: block; opacity: 1; }
body.is-overview { overflow: hidden; }
#overview h2 {
  margin: 0 0 26px;
  font-size: 9.5px; font-weight: 600;
  letter-spacing: var(--track-meta); text-transform: uppercase;
  color: var(--ink-meta);
}
#ov-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(200px, 1fr)); gap: 20px; }
.ov-item { all: unset; cursor: pointer; display: block; position: relative; }
.ov-thumb {
  position: relative; width: 100%; aspect-ratio: 16 / 9;
  overflow: hidden; background: var(--ink);
  outline: 1px solid rgba(255,255,255,0.12); outline-offset: -1px;
  transition: outline-color var(--t-chrome) var(--ease);
}
.ov-thumb > .slide { position: absolute; inset: 0 auto auto 0; transform-origin: top left; }
.ov-thumb .reveal-1, .ov-thumb .reveal-2 { opacity: 1; transform: none; }
.ov-thumb .rule, .ov-thumb .mark, .ov-thumb .rule--v { transform: none; }
.ov-thumb .clip-line > span { transform: none; }
.ov-thumb .serif .w { opacity: 1; }
.ov-item:hover .ov-thumb, .ov-item:focus-visible .ov-thumb { outline-color: var(--pink); }
.ov-item[aria-current="true"] .ov-thumb { outline: 2px solid var(--pink); outline-offset: -2px; }
.ov-num {
  margin-top: 8px; font-size: 8.5px; font-weight: 600;
  letter-spacing: var(--track-meta); text-transform: uppercase;
  color: var(--ink-foot); font-variant-numeric: tabular-nums lining-nums;
}
.ov-item:hover .ov-num { color: var(--ink-hi); }

/* ------------------------------------------------------------
   HELP
   ------------------------------------------------------------ */
#help {
  position: fixed; inset: 0; z-index: 95;
  background: rgba(11,11,11,0.94);
  display: none; place-items: center;
}
body.is-help #help { display: grid; }
#help dl {
  margin: 0; display: grid; grid-template-columns: auto auto;
  gap: 14px 28px; align-items: baseline; font-family: var(--sans);
}
#help dt {
  font-size: 9.5px; font-weight: 600;
  letter-spacing: var(--track-meta); text-transform: uppercase;
  color: var(--pink); text-align: right;
}
#help dd { margin: 0; font-size: 13px; color: var(--ink-hi); }

/* ------------------------------------------------------------
   REDUCED MOTION
   ------------------------------------------------------------ */
@media (prefers-reduced-motion: reduce) {
  .reveal-1, .reveal-2, .reveal-2 > *, .rule, .mark, .clip-line > span {
    transition-duration: 1ms !important;
    transition-delay: 0s !important;
    transform: none !important;
    opacity: 1 !important;
  }
  .serif .w { opacity: 1 !important; }
}

/* ------------------------------------------------------------
   PRINT
   ------------------------------------------------------------ */
@media print {
  @page { size: 1280px 720px; margin: 0; }
  html, body { height: auto; overflow: visible; }
  #deck { height: auto; overflow: visible; scroll-snap-type: none; }
  .frame { height: auto; page-break-after: always; }
  .pin { position: static; height: auto; }
  .slide { transform: none; }
  #chrome, #zones, #progress, #overview, #help, #spine, #cue, #grain, #loader { display: none !important; }
  .reveal-1, .reveal-2, .reveal-2 > * { opacity: 1 !important; transform: none !important; }
  .rule, .mark, .clip-line > span { transform: none !important; }
  .serif .w { opacity: 1 !important; }
}

/* ------------------------------------------------------------
   OPENING GATE
   Holds the black until the fonts are ready, so the cover never
   flashes in a fallback face. Names the recipient, draws one
   pink rule, then leaves.
   ------------------------------------------------------------ */
#loader {
  position: fixed;
  inset: 0;
  z-index: 300;
  background: var(--ink);
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 20px;
  transition: opacity 900ms var(--ease), visibility 900ms;
}
#loader.done { opacity: 0; visibility: hidden; }

#loader .ld-for {
  font-family: var(--sans);
  font-size: 9px;
  font-weight: 600;
  letter-spacing: 0.42em;
  text-transform: uppercase;
  color: var(--ink-meta);
  opacity: 0;
  animation: ldUp 1.2s var(--ease) .15s forwards;
}
#loader .ld-name {
  font-family: var(--serif);
  font-weight: 400;
  font-size: clamp(34px, 5.5vw, 62px);
  letter-spacing: 0.04em;
  color: var(--paper);
  opacity: 0;
  animation: ldUp 1.2s var(--ease) .5s forwards;
}
#loader .ld-line {
  width: 0; height: 2px;
  background: var(--pink);
  animation: ldGrow 1.5s var(--ease) .95s forwards;
}
@keyframes ldUp {
  0%   { opacity: 0; transform: translateY(24px); }
  100% { opacity: 1; transform: none; }
}
@keyframes ldGrow { 100% { width: 120px; } }

/* the cover holds until the gate lifts */
body:not(.go) .frame:first-child .reveal-1,
body:not(.go) .frame:first-child .reveal-2 { opacity: 0; transform: translateY(40px); }

@media (prefers-reduced-motion: reduce) {
  #loader { display: none; }
  #loader .ld-for, #loader .ld-name, #loader .ld-line { animation: none; opacity: 1; }
}

/* ============================================================
   REFLOW · NARROW SCREENS

   The deck is a 1280x720 stage scaled to fit. That is exactly
   right down to a laptop and completely wrong on a phone: at
   390px the scale is 0.305, so 13.5px body copy renders at 4px
   and the 8.5px footer at 2.6px.

   Below a usable scale the page stops being a scaled picture
   and becomes a reading layout. Same content, same tokens, same
   type roles, same colours, same reading order. Only the
   composition is released, and only where it could not be read.

   Desktop and tablet-landscape are untouched: the frozen
   composition of pages 1 to 15 is preserved wherever it can
   actually be seen.

   Scoped to #deck so the overview thumbnails keep showing the
   real composition.
   ============================================================ */

/* Scoped to @media screen. Print must never reflow: the PDF
   export and the evidence audit both render through the print
   path, and a narrow print viewport would otherwise turn 23
   composed pages into 45 reflowed ones. */
@media screen {

  body.reflow { --gut: 22px; }

  body.reflow #deck .frame { height: auto; }
  body.reflow #deck .pin {
    position: static;
    height: auto;
    display: block;
    overflow: visible;
  }
  body.reflow #deck .slide {
    position: static;
    width: 100%;
    height: auto;
    transform: none;
    display: flex;
    flex-direction: column;
    /* stretch, not flex-start: a column flex item with align-items
       flex-start shrink-wraps to its content, so paragraphs would
       set their own narrow measure instead of using the column. */
    align-items: stretch;
    padding: 54px var(--gut) 40px;
    overflow: hidden;
  }

  /* reading order, computed from the authored layout */
  body.reflow #deck .slide > * { order: var(--o, 0); }

  /* ── positioned children become flow children ─────────────── */
  body.reflow #deck .bl {
    position: static;
    left: auto; right: auto; top: auto;
    width: auto;
    max-width: 100%;
    white-space: normal;
    text-align: left;
    margin: 0 0 var(--mb, 10px);
    /* a base size, so an element with no role class does not keep
       the size it was authored at for a 1280px stage. Role classes
       below have equal specificity and come later, so they win. */
    font-size: 18px;
    line-height: 1.4;
  }

  /* ── type scale · same roles, sized for a hand ─────────────── */
  body.reflow #deck .label {
    font-size: 10px; line-height: 1.5;
    letter-spacing: 0.16em; --mb: 12px;
  }
  body.reflow #deck .footer  { font-size: 9px;  line-height: 1.5; --mb: 0; }
  body.reflow #deck .pagenum { font-size: 11px; --mb: 0; margin-top: 22px; }
  body.reflow #deck .body    { font-size: 15px; line-height: 1.55; --mb: 14px; }
  body.reflow #deck .item    { font-size: 17px; line-height: 1.35; --mb: 8px; }
  body.reflow #deck .enum    { font-size: 11px; line-height: 1.4; --mb: 4px; }
  body.reflow #deck .serif   { font-size: 19px; line-height: 1.5; --mb: 20px; }
  body.reflow #deck .figure  { font-size: 34px; line-height: 1.05; --mb: 4px; letter-spacing: -0.02em; }

  body.reflow #deck .head,
  body.reflow #deck .head--3,
  body.reflow #deck .head--wide,
  body.reflow #deck .head--cred {
    font-size: clamp(25px, 7.4vw, 36px);
    line-height: 1.06;
    letter-spacing: -0.02em;
    --mb: 20px;
  }
  body.reflow #deck .display-xl {
    font-size: clamp(38px, 12.5vw, 60px);
    line-height: 0.98; letter-spacing: -0.03em; --mb: 20px;
  }
  body.reflow #deck .display-l {
    font-size: clamp(32px, 10.5vw, 48px);
    line-height: 1.02; letter-spacing: -0.025em; --mb: 14px;
  }

  /* headlines must not be clipped once they wrap */
  body.reflow #deck .clip-line { overflow: visible; padding: 0; margin: 0; }
  body.reflow #deck .clip-line > span { transform: none; display: inline; }

  /* ── rules, marks, images ──────────────────────────────────── */
  body.reflow #deck .rule {
    position: static;
    width: 100%;
    transform: none !important;
    margin: 2px 0 18px;
  }
  body.reflow #deck .rule--v { display: none; }
  body.reflow #deck .mb-dup { display: none; }
  body.reflow #deck .mark {
    position: static;
    align-self: flex-start;
    width: 42px;
    transform: none !important;
    margin: 4px 0 18px;
  }
  body.reflow #deck .mark--full { width: 100%; }
  body.reflow #deck .dot { position: static; margin: 0 0 8px; }
  body.reflow #deck .band { display: none; }

  body.reflow #deck .img {
    position: static;
    align-self: flex-start;
    width: min(100%, calc(var(--w) * 1px));
    height: auto;
    aspect-ratio: var(--w) / var(--h);
    margin: 4px 0 22px;
  }
  /* A photograph wants the full width of a phone. A wordmark at the
     same width would shout. Tagged at runtime by authored height. */
  body.reflow #deck .img.mb-photo {
    width: 100vw;
    margin-left: calc(var(--gut) * -1);
    margin-right: calc(var(--gut) * -1);
    margin-bottom: 26px;
  }
  body.reflow #deck .img.mb-mark {
    width: min(66%, calc(var(--w) * 1px));
    margin-bottom: 14px;
  }
  body.reflow #deck .img img { transform: none !important; }

  /* ── the two-tone pages ────────────────────────────────────────
     Several paper pages close on a black band. Adjacent full-bleed
     dark blocks with no gap between them read as one band, which
     keeps the alternation the deck is built on. */
  body.reflow #deck .mb-band {
    background: var(--ink);
    width: 100vw;
    margin-left: calc(var(--gut) * -1);
    margin-right: calc(var(--gut) * -1);
    padding: 0 var(--gut);
    --mb: 0;
    padding-bottom: 14px;
  }
  body.reflow #deck .mb-band-start { padding-top: 30px; margin-top: 14px; }
  body.reflow #deck .mb-band-end   { padding-bottom: 30px; }
  body.reflow #deck .slide--paper .mb-band.label   { color: var(--ink-meta); }
  body.reflow #deck .slide--paper .mb-band.body    { color: var(--ink-body); }
  body.reflow #deck .slide--paper .mb-band.item,
  body.reflow #deck .slide--paper .mb-band.figure,
  body.reflow #deck .slide--paper .mb-band.serif   { color: var(--paper); }
  /* Touch targets. The email and phone are the only things on the
     deck anyone taps, and an 18px line is well under the 44px
     minimum. Padding grows the target; the negative margin keeps
     the visual spacing exactly as laid out. */
  body.reflow #deck .slide a {
    display: inline-block;
    padding: 12px 0;
  }
  /* The padding supplies the spacing, so the paragraph must not add
     more. A negative margin looked tidier but made the two targets
     overlap by 12px, which meant a tap near the boundary could dial
     instead of open mail. */
  body.reflow #deck .bl:has(> a) { --mb: 0; }

  body.reflow #deck .slide--paper .mb-band.footer,
  body.reflow #deck .slide--paper .mb-band.pagenum { color: var(--ink-foot); }
  body.reflow #deck .mb-band.c-pink { color: var(--pink); }

  /* ── entrance · one gesture, no scroll-linked timing ───────── */
  body.reflow #deck .reveal-2 > * { transform: translateY(14px); }
  body.reflow #deck .serif .w { opacity: 1; }
  body.reflow #deck .frame.r2 .reveal-2 > * { transition-delay: calc(var(--i, 0) * 40ms); }

  /* ── chrome ────────────────────────────────────────────────── */
  body.reflow #spine,
  body.reflow #cue,
  body.reflow #zones { display: none !important; }
  body.reflow #chrome { right: 14px; bottom: 12px; font-size: 8.5px; gap: 10px; }
  body.reflow #chrome .btn-full { display: none; }
  body.reflow #overview { padding: 30px 20px; }
  body.reflow #ov-grid { grid-template-columns: repeat(auto-fill, minmax(140px, 1fr)); gap: 14px; }

}
