/* ═══════════════════════════════════════════════════════════════════════
   THE DECK · style
   ─────────────────────────────────────────────────────────────────────
   Built for a PORTRAIT PANE, roughly half a laptop screen, sitting beside
   a live browser, both mirrored to the TV. Everything is fluid: it lays out
   correctly from ~640px wide up to fullscreen.

   Colour carries meaning, it does not decorate.
     bone + slate   you are listening to him
     dark + amber   a demo is running in the window next door
   ═══════════════════════════════════════════════════════════════════════ */

:root{
  font-size: clamp(14px, 1.66vh + 0.52vw, 24px);

  --font-head:"Avenir Next","Helvetica Neue",Helvetica,Arial,sans-serif;
  --font-ui:-apple-system,BlinkMacSystemFont,"Helvetica Neue",Helvetica,sans-serif;
  --font-mono:"SF Mono",ui-monospace,Menlo,monospace;

  --paper:#F2F0EC;
  --ink:#16161A;
  --soft:#5C5A57;
  --faint:#93908B;
  --rule:rgba(22,22,26,.13);
  --accent:#2F4E70;
  --pad:clamp(1.4rem, 4vw, 2.6rem);
}

*{ box-sizing:border-box; }
html,body{ height:100%; margin:0; overflow:hidden; }
body{
  background:var(--paper);
  color:var(--ink);
  font-family:var(--font-ui);
  -webkit-font-smoothing:antialiased;
  transition:background .32s ease, color .32s ease;
}

/* a demo is running next door */
body.demo{
  --paper:#14161A;
  --ink:#F1EFEA;
  --soft:#A6A29B;
  --faint:#6E6A64;
  --rule:rgba(241,239,234,.15);
  --accent:#E08A4C;
}

#app{
  height:100%;
  display:flex; flex-direction:column;
  max-width:74rem; margin:0 auto;
  padding:var(--pad);
  gap:1.1rem;
}

/* ── the rail, always visible, so you are never lost ───────────────── */
#railwrap{ flex:0 0 auto; }
/* A scale, not a barcode: one continuous baseline, a fine graduation per
   stop, stages as dots, demos in the demo colour, you as the tall mark. */
/* N equal slots in one row, no per-chapter boxes, no gap. Chapter width is
   emergent (6 screens = 6 slots) and the pitch is identical everywhere by
   construction rather than by arithmetic. */
#rail{
  position:relative;
  height:1.5rem;
  display:flex; align-items:center;
  gap:0;
}
/* one baseline for the whole rail, drawn behind the marks */
#rail::before{
  content:''; position:absolute; left:0; right:0; top:50%;
  height:1px; background:color-mix(in srgb, var(--ink) 22%, transparent);
}
/* Every stop occupies an identical slot, whatever mark it carries. Sizing the
   MARK as the flex item (the old way) meant a .62rem waypoint dot and a 1.5px
   hairline were different-width items, and the leftover space got distributed
   around them, so one fat dot shifted every tick beside it off-pitch.
   The slot is the item now; the <i> is the mark, centred inside it.
   The whole slot is the click target, which also makes the rail easy to hit. */
.tick{
  position:relative; z-index:1;
  flex:1 1 0; min-width:0; height:100%;
  display:flex; align-items:center; justify-content:center;
  cursor:pointer;
}
.tick i{
  display:block; position:relative; z-index:1;
  width:1.5px; height:.34rem;
  background:color-mix(in srgb, var(--ink) 32%, transparent);
  transition:background .18s ease, height .18s ease, opacity .12s ease;
}
.tick.done i{ background:color-mix(in srgb, var(--ink) 50%, transparent); }
.tick.wp i{
  width:.62rem; height:.62rem; border-radius:50%;
  background:color-mix(in srgb, var(--ink) 60%, transparent);
}
.tick.demo i{
  width:.5rem; height:.5rem; border-radius:1px;
  background:color-mix(in srgb, var(--accent) 75%, transparent);
}
/* The active stop keeps its own shape. A separate little person travels over
   the rail, which means the cursor never disappears inside a circle or square. */
.tick.now i{ background:var(--accent); }
.tick.beat.now i{ opacity:0; }
.tick.wp.now i{ width:.72rem; height:.72rem; border-radius:50%; background:var(--accent); }
.tick.demo.now i{ width:.6rem; height:.6rem; border-radius:1px; background:var(--accent); }

#railwalker{
  --walk-time:280ms;
  position:absolute; z-index:4;
  left:0; bottom:50%;
  width:.9rem; height:1.15rem;
  color:var(--accent);
  transform:translateX(-50%);
  transition:left var(--walk-time) cubic-bezier(.4,0,.2,1);
  pointer-events:none;
  will-change:left;
}
#railwalker .rail-person{
  --face:1;
  position:absolute; inset:0;
  transform-origin:50% 100%;
  transform:scaleX(var(--face));
}
#railwalker.flipped .rail-person{ --face:-1; }
#railwalker .part{
  position:absolute; display:block;
  margin:0; padding:0; background:currentColor;
}
#railwalker .head{
  top:.02rem; left:50%;
  width:.29rem; height:.29rem;
  border-radius:50%; transform:translateX(-50%);
}
#railwalker .body{
  top:.3rem; left:calc(50% - 1px);
  width:2px; height:.38rem;
  border-radius:2px;
}
#railwalker .arm{
  top:.4rem; width:.34rem; height:2px;
  border-radius:2px;
}
#railwalker .arm.left{
  right:50%; transform-origin:100% 50%; transform:rotate(-25deg);
}
#railwalker .arm.right{
  left:50%; transform-origin:0 50%; transform:rotate(25deg);
}
#railwalker .leg{
  top:.64rem; left:calc(50% - 1px);
  width:2px; height:.43rem;
  border-radius:2px; transform-origin:50% 0;
}
#railwalker .leg.left{ transform:rotate(24deg); }
#railwalker .leg.right{ transform:rotate(-24deg); }
#railwalker .hat{ display:none; }

/* In transit it turns sideways and makes one ridiculous leap. The whole
   person flips to match the direction of travel. */
#railwalker.moving .rail-person{
  animation:rail-jump var(--walk-time) cubic-bezier(.4,0,.2,1) both;
}
#railwalker.moving .body{
  transform-origin:50% 100%; transform:rotate(-12deg);
}
#railwalker.moving .arm.left,
#railwalker.moving .leg.left{ opacity:.48; }
#railwalker.moving .leg.left{ animation:rail-jump-leg-left var(--walk-time) ease-in-out both; }
#railwalker.moving .leg.right{ animation:rail-jump-leg-right var(--walk-time) ease-in-out both; }
#railwalker.moving .arm.left{ animation:rail-jump-arm-left var(--walk-time) ease-in-out both; }
#railwalker.moving .arm.right{ animation:rail-jump-arm-right var(--walk-time) ease-in-out both; }

@keyframes rail-jump{
  0%,100%{ transform:translateY(0) scaleX(var(--face)); }
  48%{ transform:translateY(-.38rem) scaleX(var(--face)); }
}
@keyframes rail-jump-leg-left{
  0%,100%{ transform:rotate(30deg); }
  48%{ transform:rotate(72deg); }
}
@keyframes rail-jump-leg-right{
  0%,100%{ transform:rotate(-34deg); }
  48%{ transform:rotate(-68deg); }
}
@keyframes rail-jump-arm-left{
  0%,100%{ transform:rotate(-34deg); }
  48%{ transform:rotate(48deg); }
}
@keyframes rail-jump-arm-right{
  0%,100%{ transform:rotate(32deg); }
  48%{ transform:rotate(-50deg); }
}

/* At a stage circle, the marker is the lower snowball. The cursor supplies
   the middle, head, hat, face, and inexplicable stick arms. */
#railwalker.snowman .leg{ display:none; }
#railwalker.snowman .body{
  top:.39rem; left:50%;
  width:.44rem; height:.44rem;
  transform:translateX(-50%);
  border-radius:50%;
  background:currentColor;
}
#railwalker.snowman .body::before{
  content:''; position:absolute;
  top:.12rem; left:calc(50% - 1px);
  width:2px; height:2px; border-radius:50%;
  background:var(--paper);
  box-shadow:0 .13rem 0 var(--paper);
}
#railwalker.snowman .head{
  top:.1rem; width:.31rem; height:.31rem;
  background:currentColor;
}
#railwalker.snowman .head::before{
  content:''; position:absolute;
  top:.09rem; left:.08rem;
  width:2px; height:2px; border-radius:50%;
  background:var(--paper);
  box-shadow:.13rem 0 0 var(--paper);
}
#railwalker.snowman .head::after{
  content:''; position:absolute;
  top:.16rem; right:-.11rem;
  width:.13rem; height:1.5px;
  background:var(--ink); transform:rotate(8deg);
}
#railwalker.snowman .arm{
  top:.56rem; width:.3rem; height:1.5px;
  background:var(--ink);
}
#railwalker.snowman .arm.left{ transform:rotate(18deg); }
#railwalker.snowman .arm.right{ transform:rotate(-18deg); }
#railwalker.snowman .hat{
  display:block; top:.055rem; left:50%;
  width:.37rem; height:.055rem;
  transform:translateX(-50%); border-radius:1px;
  background:var(--ink);
}
#railwalker.snowman .hat::before{
  content:''; position:absolute;
  left:.075rem; bottom:0;
  width:.22rem; height:.13rem;
  background:var(--ink); border-radius:1px 1px 0 0;
}

/* At a demo square, the square is the robot body. */
#railwalker.robot .body,
#railwalker.robot .leg{ display:none; }
#railwalker.robot .head{
  top:.43rem; width:.4rem; height:.34rem;
  border-radius:2px;
  background:var(--paper); border:1.5px solid currentColor;
}
#railwalker.robot .head::before{
  content:''; position:absolute;
  top:.11rem; left:.08rem;
  width:2px; height:2px; border-radius:50%;
  background:currentColor;
  box-shadow:.15rem 0 0 currentColor;
}
#railwalker.robot .arm{ top:.63rem; width:.25rem; height:1.5px; }
#railwalker.robot .arm.left{ transform:rotate(8deg); }
#railwalker.robot .arm.right{ transform:rotate(-8deg); }
#railwalker.robot .hat{
  display:block; top:.28rem; left:calc(50% - .75px);
  width:1.5px; height:.17rem;
}
#railwalker.robot .hat::before{
  content:''; position:absolute;
  left:-.05rem; top:-.07rem;
  width:.11rem; height:.11rem;
  border-radius:50%; background:currentColor;
}

#railwalker.final .arm.right{
  animation:rail-wave .5s ease-in-out infinite alternate;
}
@keyframes rail-wave{ to{ transform:rotate(-65deg); } }

#where{
  display:flex; align-items:baseline; justify-content:space-between;
  gap:1rem; margin-top:.7rem;
  font-family:var(--font-mono);
  font-size:.68rem; letter-spacing:.15em; text-transform:uppercase;
}
#where .ch{ color:var(--accent); font-weight:700; }
#where .ct{ color:var(--faint); }

/* ── the beat ───────────────────────────────────────────────────────── */
/* Top-aligned on purpose: the kicker and headline land in the same place on
   every screen, so advancing doesn't make the room's eyes jump. */
#screen{
  flex:1 1 auto; min-width:0; min-height:0; width:100%;
  display:flex; flex-direction:column; justify-content:flex-start;
  gap:.9rem; padding-top:.6rem;
  overflow-x:hidden; overflow-y:auto; overscroll-behavior:contain;   /* never silently crop a beat */
  overflow-anchor:none;                       /* late image/layout changes stay pinned to the beat's top */
  scrollbar-width:none;
}
#screen > *{ min-width:0; max-width:100%; margin:0; }
#screen > h1{ margin-bottom:.2rem; }

/* Kickers carry the "where am I" load, so they are set to be read from the
   back of the room, not to be found. */
.k{
  font-family:var(--font-mono);
  font-size:1.02rem; letter-spacing:.12em; text-transform:uppercase;
  color:var(--accent); font-weight:700;
}

/* ── stage / demo badges: the same shapes they wear on the rail ──── */
.badge{ display:flex; align-items:center; gap:.7rem; }
.badge i{
  flex:0 0 auto;
  display:flex; align-items:center; justify-content:center;
  width:2.5rem; height:2.5rem;
  font-family:var(--font-mono); font-size:1rem; font-weight:700; font-style:normal;
  color:var(--paper); background:var(--accent);
}
.badge.wp i{ border-radius:50%; }
.badge.demo i{ border-radius:3px; }
.badge span{
  font-family:var(--font-mono);
  font-size:1.02rem; letter-spacing:.12em; text-transform:uppercase;
  color:var(--accent); font-weight:700;
}

.legend{
  display:flex; flex-wrap:wrap; gap:1.1rem;
  font-family:var(--font-mono); font-size:.72rem;
  letter-spacing:.1em; text-transform:uppercase; color:var(--faint);
}
.legend span{ display:flex; align-items:center; gap:.38rem; }
.legend .sh{ display:block; background:color-mix(in srgb, var(--ink) 55%, transparent); }
.legend .sh.wp  { width:.52rem; height:.52rem; border-radius:50%; }
.legend .sh.demo{ width:.44rem; height:.44rem; border-radius:1px;
                  background:color-mix(in srgb, var(--accent) 70%, transparent); }
.legend .sh.now { width:2px; height:1rem; background:var(--accent); }

/* ── color by numbers ───────────────────────────────────────────────── */
.cgrid{
  display:grid; grid-template-columns:repeat(2, minmax(0,1fr));
  gap:1.4rem; align-items:start;
}
@media (max-width:560px){
  .cgrid{ grid-template-columns:1fr; gap:1rem; }
}
.cgroup{ display:flex; flex-direction:column; gap:.1rem; }
/* Side by side they are separated by the gap, so the stacked-layout rule
   above them would just draw a stray line down the middle of a column. */
.cgrid .cgroup + .cgroup{ margin-top:0; padding-top:0; border-top:0; }
.ctitle{
  font-family:var(--font-mono); font-size:.76rem;
  letter-spacing:.14em; text-transform:uppercase;
  color:var(--faint); font-weight:600; margin-bottom:.45rem;
}
.check{
  display:flex; align-items:flex-start; gap:.7rem;
  padding:.26rem 0; cursor:pointer;
  font-size:.96rem; line-height:1.3;
  -webkit-user-select:none; user-select:none;
}
.check i{
  flex:0 0 auto;
  width:1.15rem; height:1.15rem; margin-top:.06rem;
  border:2px solid color-mix(in srgb, var(--ink) 34%, transparent);
  border-radius:2px; background:transparent;
  transition:background .16s ease, border-color .16s ease;
}
.check:hover i{ border-color:var(--accent); }
/* the point of the metaphor: filling one in adds colour to the picture */
.check.on i{ background:var(--accent); border-color:var(--accent); }
.check.on span{ color:var(--ink); }
.cgroup.dim .check span{ color:var(--soft); }
.cgroup.dim .check.on i{ background:var(--faint); border-color:var(--faint); }
.cfoot{
  margin-top:.8rem; padding-top:.7rem; border-top:1.5px solid var(--rule);
  font-size:.92rem; color:var(--soft);
}
h1{
  font-family:var(--font-head);
  font-weight:700; font-size:2.05rem; line-height:1.1;
  letter-spacing:-.025em; text-wrap:balance;
}
h1.xl{ font-size:2.75rem; line-height:1.04; }
em{ font-style:italic; color:var(--accent); }

.lede{ font-size:1.02rem; line-height:1.45; color:var(--soft); max-width:44ch; }
.close{
  font-family:var(--font-head); font-weight:700;
  font-size:1.35rem; line-height:1.22; letter-spacing:-.02em;
  padding-top:.9rem; border-top:1.5px solid var(--rule);
}
.mono{ font-family:var(--font-mono); font-size:.82rem; }
.dim{ color:var(--soft); }

/* lists */
ol,ul{ list-style:none; padding:0; display:flex; flex-direction:column; }
.rows{ gap:.1rem; }
.rows li{
  display:grid; grid-template-columns:2.2rem minmax(0,1fr); gap:.6rem;
  align-items:baseline;
  padding:.62rem 0; border-bottom:1.5px solid var(--rule);
  font-size:1rem; line-height:1.35;
}
.rows.big li{ font-size:1.14rem; }
.rows li:last-child{ border-bottom:0; }
.num{ font-family:var(--font-mono); font-size:.76rem; color:var(--accent); }

.spine{ gap:0; }
.spine li{
  display:grid; grid-template-columns:2.2rem minmax(0,1fr); gap:.6rem;
  align-items:baseline; padding:.42rem 0;
  font-size:1.05rem;
}
.spine li b{ font-weight:600; }
.spine .between{
  display:block;                       /* not the two-column grid above */
  font-style:italic; color:var(--soft); font-size:.86rem;
  padding:.12rem 0 .12rem 2.8rem;
}
.spine .between.open{ color:var(--faint); }

.when{ gap:0; }
.when li{
  display:grid; grid-template-columns:6.2rem minmax(0,1fr); gap:.7rem;
  align-items:baseline; padding:.6rem 0;
  border-bottom:1.5px solid var(--rule);
  font-size:.98rem; line-height:1.35;
}
.when.no-tail-rule li:last-child{ border-bottom:0; }
.when li b{ font-family:var(--font-mono); font-size:.78rem; color:var(--accent); font-weight:400; }

.claims{ gap:.7rem; }
.claims li{ font-size:1.05rem; line-height:1.32; font-weight:600; }

.flow{ font-family:var(--font-head); font-weight:700; font-size:1.3rem; letter-spacing:-.015em; line-height:1.4; }
.flow i{ font-style:normal; color:var(--accent); padding:0 .2em; }

.dead{ gap:0; }
.dead li{
  display:flex; justify-content:space-between; align-items:baseline;
  padding:.55rem 0; border-bottom:1.5px solid var(--rule);
}
.dead li b{ font-size:1.2rem; }
.dead li span{ font-family:var(--font-mono); font-size:.78rem; color:var(--soft); }

.score{ gap:0; }
.score li{
  display:flex; justify-content:space-between; align-items:baseline;
  padding:.5rem 0; border-bottom:1.5px solid var(--rule);
}
.score li b{ font-family:var(--font-head); font-size:1.65rem; letter-spacing:-.03em; }
.score li span{ font-size:.85rem; color:var(--soft); }
.score li.lit b{ color:var(--accent); }

.steps{ gap:0; }
.steps li{
  display:grid; grid-template-columns:2.2rem minmax(0,1fr); gap:.6rem;
  padding:.6rem 0; border-bottom:1.5px solid var(--rule);
}
.steps.no-tail-rule li:last-child{ border-bottom:0; }
.steps li b{ font-size:1rem; }
/* third child of a two-column row belongs under the second, not in the gutter */
.steps li span:last-child{ grid-column:2; display:block; font-size:.86rem; color:var(--soft); margin-top:.1rem; }
.steps li.lit b{ color:var(--accent); }

.takeaway{ gap:0; }
.takeaway li{
  display:grid; grid-template-columns:2.2rem minmax(0,1fr); gap:.6rem;
  padding:.62rem 0; border-bottom:1.5px solid var(--rule);
}
.takeaway li b{ font-size:1rem; display:block; }
.takeaway li span:last-child{ grid-column:2; font-size:.92rem; color:var(--soft); }

.cast{ gap:0; }
.cast li{
  display:grid; grid-template-columns:5.4rem minmax(0,1fr); gap:.7rem;
  align-items:baseline; padding:.5rem 0;
  border-bottom:1.5px solid var(--rule);
}
.cast li b{ font-size:1.05rem; }
.cast li span{ font-size:.94rem; line-height:1.32; color:var(--soft); }

.board{ display:flex; flex-direction:column; gap:.9rem; }
.board > div{ display:flex; flex-direction:column; gap:.16rem; }
.board b{
  font-family:var(--font-mono); font-size:.72rem; letter-spacing:.1em;
  text-transform:uppercase; color:var(--accent); font-weight:700;
  padding-bottom:.3rem; margin-bottom:.2rem;
  border-bottom:2px solid var(--accent);
}
.board span{ font-size:.95rem; }

.stats{ display:flex; gap:1.6rem; padding:.7rem 0; border-top:1.5px solid var(--rule); border-bottom:1.5px solid var(--rule); }
.stats div{ display:flex; flex-direction:column; }
.stats b{ font-family:var(--font-mono); font-size:1.2rem; color:var(--accent); }
.stats span{ font-size:.8rem; color:var(--soft); }

blockquote{
  margin:0; padding:.2rem 0 .2rem 1rem;
  border-left:3px solid var(--accent);
  font-size:1.05rem; line-height:1.35; color:var(--soft);
}
blockquote .src{
  display:block; font-family:var(--font-mono); font-size:.7rem;
  letter-spacing:.12em; text-transform:uppercase; color:var(--faint);
  margin-bottom:.3rem;
}

/* ── a half-and-half beat: words left, one picture right ────────────── */
/* The picture gets its own half, floor to ceiling, instead of sitting under
   the text at thumbnail size. It FITS rather than crops. This slot holds
   screenshots, and a cropped screenshot is just a broken screenshot.
   Collapses to a stack on a phone-width window. */
.split{
  flex:1 1 auto; min-height:0;
  display:grid; grid-template-columns:repeat(2, minmax(0,1fr));
  gap:1.1rem; align-items:stretch;
}
@media (max-width:560px){
  .split{ grid-template-columns:1fr; }
}
.split > .col{
  min-height:0;
  display:flex; flex-direction:column; gap:.7rem;
}
/* .stats already closes itself with a bottom rule and .close opens with a top
   one. Stacked, that draws two lines a few millimetres apart with nothing
   between them. Keep the stats rule, drop the other. */
.split .stats + .close{ border-top:0; padding-top:0; }
/* Half the width means half the measure: the full-slide size wraps to four
   lines here and swamps everything above it. */
.split .close{ font-size:1.28rem; }
.split .lede{ max-width:none; }
.split .shot{ flex:1 1 auto; min-height:0; }
/* Centred, not top-aligned: a shot squarer than the slot letterboxes, and
   splitting that space above and below reads as deliberate where dumping it
   all at the bottom reads as a bug. */
.split .shot img{ object-fit:contain; object-position:center; }

/* ── title slide: one bounded canvas ─────────────────────────────────── */
/* Nothing in this composition derives its height from its width. The middle
   row owns whatever height is left after the copy and footer; photos and link
   cards must fit that row instead of making #screen taller. */
.title-slide{
  --title-gap:clamp(8px, 1.45vh, 16px);
  flex:1 1 auto; min-width:0; min-height:0;
  width:100%; height:100%; overflow:hidden;
  display:grid;
  grid-template-rows:auto minmax(0,1fr) auto;
  gap:var(--title-gap);
}
.title-copy{
  min-width:0; min-height:0;
  display:flex; flex-direction:column;
  gap:clamp(7px, 1.2vh, 13px);
}
.title-copy > *{ margin:0; }
.title-copy .k{ font-size:clamp(12px, 1.8vh, 20px); }
.title-copy h1{
  font-size:clamp(30px, 4.8vh, 52px);
  line-height:1.04;
}
.title-copy .lede{
  font-size:clamp(14px, 2.1vh, 22px);
  line-height:1.35;
}
.title-visual{
  min-width:0; min-height:0; overflow:hidden;
  display:grid;
  grid-template-columns:repeat(3,minmax(0,1fr));
  gap:var(--title-gap); align-items:stretch;
  padding-top:clamp(6px, 1.1vh, 12px);
  border-top:1.5px solid var(--rule);
}
.title-person{
  min-width:0; min-height:0;
  display:grid; grid-template-rows:minmax(0,1fr) auto;
  gap:clamp(5px, .8vh, 9px);
  align-items:stretch; text-align:center;
}
.title-person .shot{
  position:relative;
  width:100%; height:100%; min-width:0; min-height:0;
  overflow:hidden; border-radius:3px;
}
.title-person .shot img{
  display:block; width:100%; height:100%; min-height:0;
  flex:none; object-fit:cover; object-position:center top;
}
.title-person .shot figcaption{ display:none; }
.title-person.crowd .shot figcaption{
  display:block; position:absolute; z-index:2;
  left:8px; bottom:7px;
  padding:3px 6px; border-radius:2px;
  background:rgba(0,0,0,.48); color:rgba(255,255,255,.82);
  font-size:clamp(9px, 1.1vh, 12px); line-height:1.15; letter-spacing:.02em;
  text-transform:none;
}
.title-person > b, .title-person > span{
  min-width:0; overflow:hidden;
  font-size:clamp(14px, 2.1vh, 21px);
  line-height:1.15; font-weight:700;
  white-space:nowrap; text-overflow:ellipsis;
}

/* Four links stacked down the right third. Set small on purpose: nobody in
   the room reads these, they follow them in the published deck. */
.cards{
  display:flex; flex-direction:column; gap:.4rem;
  justify-content:flex-start;
}
.card{
  position:relative;
  display:flex; flex-direction:column; gap:.08rem;
  padding:.5rem 1.5rem .5rem .6rem;   /* right gutter reserved for the ↗ */
  border:1.5px solid var(--rule); border-radius:3px;
  text-decoration:none; color:inherit;
  transition:border-color .16s ease, background .16s ease;
}
/* The corner arrow is the affordance: it says "this leaves the page" without
   a word of explanation. Parked absolutely so a wrapping URL can't push it
   onto its own line. */
.card::after{
  content:'\2197';                     /* ↗ */
  position:absolute; top:.42rem; right:.5rem;
  font-size:.78rem; line-height:1; color:var(--faint);
  transition:color .16s ease, transform .16s ease;
}
.card:hover{
  border-color:var(--accent);
  background:color-mix(in srgb, var(--accent) 6%, transparent);
}
.card:hover::after{
  color:var(--accent);
  transform:translate(.08rem, -.08rem);
}
.card b{ font-size:.92rem; line-height:1.2; }
.card span{ font-size:.78rem; line-height:1.25; color:var(--soft); }
.card i{
  font-family:var(--font-mono); font-style:normal;
  font-size:.6rem; line-height:1.3; letter-spacing:.02em;
  color:var(--accent); overflow-wrap:anywhere; margin-top:.15rem;
}
.title-links.cards{
  min-width:0; min-height:0; overflow:hidden;
  display:grid;
  grid-template-rows:repeat(4,minmax(0,1fr));
  gap:clamp(5px, .8vh, 9px);
}
.title-links .card{
  min-width:0; min-height:0; overflow:hidden;
  justify-content:center;
  gap:clamp(1px, .25vh, 3px);
  padding:clamp(5px, .8vh, 9px) 28px clamp(5px, .8vh, 9px) 10px;
}
.title-links .card::after{
  top:clamp(6px, .9vh, 10px); right:9px;
  font-size:clamp(12px, 1.7vh, 17px);
}
.title-links .card b{
  overflow:hidden; white-space:nowrap; text-overflow:ellipsis;
  font-size:clamp(14px, 2vh, 20px); line-height:1.1;
}
.title-links .card span{
  overflow:hidden; white-space:nowrap; text-overflow:ellipsis;
  font-size:clamp(11px, 1.55vh, 16px); line-height:1.15;
}
.title-links .card i{
  overflow:hidden; overflow-wrap:normal; white-space:nowrap; text-overflow:ellipsis;
  font-size:clamp(9px, 1.25vh, 12px); line-height:1.15; margin-top:1px;
}
.cards.contact{ gap:.7rem; }
.contact .card{ padding:1rem 2rem 1rem 1rem; }
.contact .card b{ font-size:1.25rem; }
.contact .card i{ font-size:.82rem; }
.online{
  min-width:0; margin:0;
  font-size:clamp(12px, 1.8vh, 18px); line-height:1.3; color:var(--soft);
  padding-top:clamp(6px, 1vh, 11px); border-top:1.5px solid var(--rule);
}
/* Reads as part of the sentence, not as a control: same size and weight as
   the text around it, marked only by the underline. The href is real so the
   link survives with JS off; the deck intercepts the click. */
.online .here{ color:inherit; text-decoration:underline; text-underline-offset:2px; }
.online .here em{ font:inherit; font-style:italic; }

/* Landscape rearranges the same bounded regions; it never changes how their
   heights are calculated. */
@media (min-width:800px) and (min-aspect-ratio:6/5){
  .title-slide{
    grid-template-columns:minmax(0,.9fr) minmax(0,1.1fr);
    grid-template-rows:minmax(0,1fr) auto;
    column-gap:clamp(14px, 2.2vw, 30px);
  }
  .title-copy{ grid-column:1; grid-row:1; align-self:start; }
  .title-visual{ grid-column:2; grid-row:1 / 3; }
  .title-slide > .online{ grid-column:1; grid-row:2; align-self:end; }
}

/* During an extremely shallow drag, keep the bounded canvas readable by
   dropping secondary copy instead of letting any region overflow another. */
@media (max-height:420px) and (min-width:700px) and (min-aspect-ratio:6/5){
  .title-slide{ --title-gap:6px; }
  .title-copy{ gap:5px; }
  .title-copy h1{ font-size:clamp(22px, 9vh, 30px); line-height:1; }
  .title-copy .lede{ display:none; }
  .title-visual{ gap:6px; padding-top:4px; }
  .title-person{ grid-template-rows:minmax(0,1fr); }
  .title-person > b, .title-person > span{ display:none; }
  .title-links.cards{ gap:4px; }
  .title-links .card{ padding:3px 20px 3px 7px; }
  .title-links .card span, .title-links .card i{ display:none; }
  .title-slide > .online{ padding-top:4px; font-size:10px; }
}

/* ── credits ─────────────────────────────────────────────────────────── */
.credits{ gap:0; }
.credits li{
  display:grid; grid-template-columns:7rem 1fr; gap:.7rem;
  align-items:baseline; padding:.6rem 0;
  border-bottom:1.5px solid var(--rule);
}
.credits li b{
  font-family:var(--font-mono); font-size:.76rem;
  color:var(--accent); font-weight:400;
}
.credits li span{ font-size:.92rem; line-height:1.4; color:var(--soft); }
.credits a{ color:var(--accent); text-decoration:underline; text-underline-offset:2px; }

.byline{
  display:flex; flex-wrap:wrap; gap:.9rem; align-items:baseline;
  padding-top:.8rem; border-top:1.5px solid var(--rule);
  font-size:.82rem; color:var(--soft);
}
.byline b{ color:var(--ink); }
.byline span{ color:var(--accent); }

/* ── images, drop a file in deck/img/ and it appears ───────────────── */
.shot{
  margin:0; min-height:0; flex:1 1 auto;
  display:flex; flex-direction:column; gap:.4rem;
}
.shot img{
  width:100%; flex:1 1 auto; min-height:0;
  object-fit:contain; object-position:left center;
  border-radius:3px;
}
.shot figcaption{
  font-family:var(--font-mono); font-size:.68rem;
  letter-spacing:.1em; text-transform:uppercase; color:var(--faint);
}
.shot.missing img{ display:none; }
.shot.missing::before{
  content:"drop  deck/img/" attr(data-img) ".png";
  flex:1 1 auto; min-height:5rem;
  display:flex; align-items:center; justify-content:center;
  border:2px dashed color-mix(in srgb, var(--accent) 40%, transparent);
  border-radius:3px;
  font-family:var(--font-mono); font-size:.74rem;
  color:color-mix(in srgb, var(--accent) 80%, transparent);
  text-align:center; padding:1rem;
}
.tbd{
  font-family:var(--font-mono); font-size:.85em;
  color:color-mix(in srgb, var(--accent) 80%, transparent);
  border-bottom:2px dashed color-mix(in srgb, var(--accent) 35%, transparent);
}

/* ── the cue: a reminder for the speaker, floating bottom-right ─────── */
/* Fixed, so it takes NO height from the beat: a slide with a cue lays out
   exactly like a slide without one. Deliberately quiet: the room can read
   it, but it sits low enough in the hierarchy that nobody does. Same corner
   on every screen, so you find it without looking for it.
   #hint occupies the bottom-LEFT, so the two never meet. */
#cue{
  /* #app is centred and capped at 74rem. Past that width the viewport edge
     and the content edge part company, track the content edge, so the cue
     stays under the text instead of drifting into the margin. */
  position:fixed; bottom:.6rem;
  right:max(var(--pad), calc((100vw - 74rem) / 2 + var(--pad)));
  display:none; z-index:10;
  max-width:min(26rem, 55vw); text-align:right; text-wrap:balance;
  font-family:var(--font-mono); font-size:.72rem; line-height:1.35;
  letter-spacing:.04em; color:var(--faint);
  pointer-events:none;                 /* clicking it still advances */
}
#cue.on{ display:block; }
#cue::before{
  content:'· '; color:var(--accent);
}
/* While a demo runs, the watch strip owns the bottom of the pane. Keep its
   bullets clear of the cue rather than letting the two overlap. */
body.demo #watch.on{ padding-right:0; }

/* ── what to watch for, while a demo runs next door ─────────────────── */
#watch{
  flex:0 0 auto;
  display:none;
  padding-top:.9rem;
  border-top:1.5px solid var(--rule);
}
body.demo #watch.on{ display:block; }
#watch .lbl{
  font-family:var(--font-mono); font-size:.76rem;
  letter-spacing:.16em; text-transform:uppercase;
  color:var(--accent); font-weight:700; margin-bottom:.6rem;
}
#watch ul{ gap:.34rem; }
#watch li{ font-size:1.08rem; line-height:1.32; display:flex; gap:.6rem; }
#watch li::before{ content:'·'; color:var(--accent); font-weight:700; }

/* ── overlays ───────────────────────────────────────────────────────── */
#notes{
  position:fixed; inset:auto 0 0 0;
  background:rgba(12,12,15,.96); color:#EFEDE8;
  padding:1.2rem var(--pad);
  transform:translateY(101%); transition:transform .24s ease;
  z-index:20; max-height:70vh; overflow:auto;
}
#notes.on{ transform:none; }
#notes .where{
  font-family:var(--font-mono); font-size:.68rem; letter-spacing:.14em;
  text-transform:uppercase; color:#8B8880; margin-bottom:.5rem;
}
#notes .body{ font-size:.95rem; line-height:1.5; }

/* ── the page finder: a sidebar off the left edge ──────────────────── */
#index{
  position:fixed; right:0; top:0; bottom:0;
  width:min(22rem, 82vw);
  background:rgba(12,12,15,.97); color:#EFEDE8;
  border-left:1px solid rgba(255,255,255,.13);
  padding:.7rem .55rem 1.2rem;
  transform:translateX(101%); transition:transform .22s ease;
  z-index:21; overflow-y:auto; overscroll-behavior:contain;
  scrollbar-width:none;
}
#index.on{ transform:none; }
#index::-webkit-scrollbar{ display:none; }

#index .ch-h{
  font-family:var(--font-mono); font-size:.6rem;
  letter-spacing:.15em; text-transform:uppercase; color:#78746D;
  padding:.75rem .35rem .3rem;
  margin-top:.3rem; border-top:1px solid rgba(255,255,255,.1);
}
#index .ch-h:first-child{ border-top:0; margin-top:0; padding-top:.15rem; }

#index .row{
  display:grid; grid-template-columns:1.8rem 1fr .9rem; gap:.45rem;
  align-items:baseline;
  padding:.2rem .35rem .2rem .4rem;
  font-size:.8rem; line-height:1.25;
  cursor:pointer; border-radius:2px;
  border-left:3px solid transparent;
}
#index .row:hover{ background:rgba(255,255,255,.07); }
#index .row.now{ background:rgba(255,255,255,.13); border-left-color:#7FA6D6; }
#index .row.now .t{ font-weight:700; }
#index .row .n{ font-family:var(--font-mono); font-size:.66rem; color:#78746D; }
#index .row .i{ font-size:.72rem; color:#8E8A82; text-align:right; }
#index .row.d .t{ color:#E08A4C; }

/* The "you are already here" gag. Sits above every other panel because it is
   a dead end: the only thing to do with it is dismiss it. */
#here, #here2{
  position:fixed; inset:0; z-index:40;
  display:none; place-items:center;
  background:rgba(12,12,15,.72);
}
#here.on, #here2.on{ display:grid; }
#here .box, #here2 .box{
  max-width:min(30rem, 86vw); text-align:center;
  background:#FBFAF7; color:#17161A;
  border-radius:6px; padding:2rem 2.2rem 1.6rem;
  box-shadow:0 24px 60px rgba(0,0,0,.4);
}
#here .box b, #here2 .box b{ display:block; font-size:3.2rem; line-height:1; }
#here .box p, #here2 .box p{
  margin:.9rem 0 0; font-size:1.35rem; line-height:1.3; font-weight:700;
}
/* The word that springs the second trap. Underlined so it looks like the one
   thing in the box you can click, which is the whole joke. */
#here .box p em{ font-style:normal; text-decoration:underline; cursor:pointer; }

/* The second one lands on top of the first, so both scrims stack and the room
   can see it got worse. */
#here2{ z-index:41; }
#here2 .box{ transform:rotate(-2deg); }
#here2 .box p{ font-size:1.7rem; letter-spacing:-.01em; }

#here .box span, #here2 .box span{
  display:block; margin-top:1rem;
  font-family:var(--font-mono); font-size:.62rem;
  letter-spacing:.14em; text-transform:uppercase; color:#78746D;
}

#hint{
  position:fixed; left:var(--pad); bottom:.6rem;
  font-family:var(--font-mono); font-size:.62rem; letter-spacing:.1em;
  color:var(--faint); opacity:0; transition:opacity .6s ease; z-index:10;
}
#hint.on{ opacity:.8; }

/* ── the touch bar ──────────────────────────────────────────────────── */
/* Tapping the page does not advance, that was cut so a mouse can select and
   copy a line between rehearsals. On a phone that left nothing at all: no
   arrow keys, and at that width the rail's ticks are two pixels apart.
   So touch gets real controls. Not a gesture and not a hint that fades:
   a bar welded to the bottom edge of the phone, always there, three
   targets you can hit without looking.

   Shown ONLY where there is no hover and the pointer is coarse. The
   presenting laptop never grows presenter chrome it does not need, and a
   trackpad user still gets to select text. */
#touch{
  display:none;
  position:fixed; z-index:23;              /* above the page finder (21) */
  left:0; right:0; bottom:0;
  /* Flush to the glass. The inset padding keeps the targets clear of the
     home indicator while the BAR itself still reaches the bottom edge, so
     there is never a stripe of slide showing underneath it. */
  padding-bottom:env(safe-area-inset-bottom, 0px);
  background:color-mix(in srgb, var(--paper) 92%, var(--ink));
  border-top:1px solid var(--rule);
  box-shadow:0 -2px 14px rgba(0,0,0,.10);
  transition:background .32s ease;         /* follows the demo colour flip */
}
#touch button{
  -webkit-appearance:none; appearance:none;
  height:3.4rem; margin:0; padding:0;
  display:flex; align-items:center; justify-content:center;
  background:transparent; border:0; color:var(--ink);
  font-family:var(--font-ui); font-size:1.75rem; line-height:1;
  -webkit-user-select:none; user-select:none;
  -webkit-tap-highlight-color:transparent;
  touch-action:manipulation;               /* no 300ms double-tap wait */
  transition:background .12s ease;
}
/* Back and forward take the whole half of the bar they sit in: the target
   is the area, not the glyph, so a thumb anywhere on that side works. */
#touch #t-prev, #touch #t-next{ flex:1 1 0; }
#touch #t-prev{ border-right:1px solid var(--rule); }
#touch #t-next{ border-left:1px solid var(--rule); }
#touch #t-pages{
  flex:0 0 auto; padding:0 1.1rem; gap:.45rem;
  font-family:var(--font-mono); font-size:.72rem; letter-spacing:.1em;
  color:var(--soft);
}
#touch #t-pages i{ font-style:normal; font-size:.9rem; }
#touch button:active{ background:color-mix(in srgb, var(--paper) 78%, var(--ink)); }
/* At either end the move is impossible; say so rather than failing silently. */
#touch button[disabled]{ color:var(--faint); opacity:.35; }

@media (hover:none) and (pointer:coarse){
  #touch{ display:flex; }
  /* The bar owns that strip. Nothing else may lay out under it. */
  #app{ padding-bottom:calc(var(--pad) + 3.4rem + env(safe-area-inset-bottom, 0px)); }
  #cue{ bottom:calc(4rem + env(safe-area-inset-bottom, 0px)); }
  #index{ padding-bottom:calc(3.4rem + env(safe-area-inset-bottom, 0px) + 1.2rem); }
  /* No hints on touch. There is nothing to discover, the controls are
     visible and they never move. */
  #hint{ display:none; }
}
/* The notes sheet is keyboard-only ('s'), so it cannot be raised on a phone,
   but if it ever is, it comes up over the bar rather than under it. */
#notes.on ~ #touch{ display:none; }

/* Narrow windows are for reading the published deck, not presenting beside
   another browser. Keep the hierarchy, but let compositions grow vertically
   and scroll instead of compressing or overflowing sideways. */
@media (max-width:560px){
  :root{
    font-size:13px;
    --pad:1rem;
  }
  #app{ gap:.65rem; }
  #rail{ height:1.25rem; }
  #where{ margin-top:.35rem; font-size:.58rem; gap:.5rem; }
  #screen{ gap:.65rem; padding-top:.25rem; }
  #screen, #screen *{ overflow-wrap:anywhere; }

  h1{ font-size:1.8rem; text-wrap:wrap; }
  h1.xl{ font-size:2.15rem; }
  .k, .badge span{ font-size:.82rem; }
  .badge{ align-items:flex-start; gap:.55rem; }
  .badge i{ width:2.1rem; height:2.1rem; font-size:.85rem; }
  .lede{ font-size:.96rem; }
  .close{ font-size:1.16rem; padding-top:.65rem; }

  .when li{ grid-template-columns:5rem minmax(0,1fr); gap:.5rem; padding:.48rem 0; }
  .cast li{ grid-template-columns:4.2rem minmax(0,1fr); gap:.5rem; }

  .split{
    flex:0 0 auto; min-height:auto;
    grid-template-columns:1fr; align-items:start;
  }
  .split > .col{ min-width:0; min-height:auto; }
  .split .shot{ flex:0 0 auto; height:min(20rem, 42vh); }
  .split .shot img{ height:100%; max-height:100%; }

  .title-slide{
    flex:0 0 auto; height:auto; min-height:auto; overflow:visible;
    grid-template-rows:auto auto auto;
  }
  .title-visual{
    min-height:auto; overflow:visible;
    grid-template-columns:repeat(2,minmax(0,1fr));
    grid-template-rows:min(42vh, 22rem) auto;
  }
  .title-person .shot{ min-height:0; }
  .title-links.cards{
    grid-column:1 / -1;
    grid-template-columns:repeat(2,minmax(0,1fr));
    grid-template-rows:repeat(2,minmax(4.5rem,auto));
  }
  .card{ padding:.42rem 1.35rem .42rem .5rem; }
  .card b{ font-size:.82rem; }
  .card span{ font-size:.7rem; }
  .card i{ font-size:.55rem; }
  .online{ font-size:.76rem; padding-top:.5rem; }

  #cue{
    right:var(--pad); bottom:.35rem;
    max-width:68vw; font-size:.58rem; line-height:1.25;
  }
  #watch{ padding-top:.65rem; }
  #watch .lbl{ font-size:.68rem; margin-bottom:.4rem; }
  #watch ul{ gap:.24rem; }
  #watch li{ font-size:.96rem; }
  #hint{ display:none; }
}

@media (prefers-reduced-motion: reduce){
  *{ transition-duration:.01ms !important; }
}

#screen::-webkit-scrollbar{ display:none; }
