/* decktalk.ai. Colour, type and motion tokens come from tokens.css, generated with the brand.
   The room is dark, the film is the brightest thing in it, and the voice colour appears only where the
   narration is: the spoken word, a live cue tick, the section being rebuilt, and the sound
   control's waveform while sound plays. The voice colour is tungsten gold. */

/* ---- faces. Self-hosted subsets with metric-matched fallbacks, so the text never shifts. */
@font-face {
  font-family: "Bricolage Grotesque";
  font-weight: 500;
  font-display: swap;
  src: url(fonts/bricolage-grotesque-500.woff2) format("woff2");
}
@font-face {
  font-family: "Instrument Serif";
  font-weight: 400;
  font-display: swap;
  src: url(fonts/instrument-serif.woff2) format("woff2");
}
@font-face {
  font-family: "Instrument Sans";
  font-weight: 400 700;
  font-display: swap;
  src: url(fonts/instrument-sans.woff2) format("woff2");
}
@font-face {
  font-family: "IBM Plex Mono";
  font-weight: 400;
  font-display: swap;
  src: url(fonts/plex-mono-400.woff2) format("woff2");
}
@font-face {
  font-family: "IBM Plex Mono";
  font-weight: 500;
  font-display: swap;
  src: url(fonts/plex-mono-500.woff2) format("woff2");
}
/* Fallbacks sized to the brand faces: size-adjust from the measured width of the headline and the subhead
   in each face against its fallback in Chromium, and the ascent and descent overrides from the faces'
   hhea metrics (Bricolage 930/270, Instrument Serif 990/310, Instrument Sans 970/250, Plex Mono
   1025/275 on 1000) over that size. Instrument Serif is far narrower than any system serif, hence 76%. */
@font-face {
  font-family: "Serif Fallback";
  src: local("Georgia"), local("Times New Roman"), local("Liberation Serif");
  size-adjust: 76.2%;
  ascent-override: 130%;
  descent-override: 40.7%;
  line-gap-override: 0%;
}
@font-face {
  font-family: "Bricolage Fallback";
  src: local("Helvetica Neue"), local("Arial");
  size-adjust: 93.7%;
  ascent-override: 99.3%;
  descent-override: 28.8%;
  line-gap-override: 0%;
}
@font-face {
  font-family: "Instrument Fallback";
  src: local("Helvetica Neue"), local("Arial");
  size-adjust: 101.6%;
  ascent-override: 95.5%;
  descent-override: 24.6%;
  line-gap-override: 0%;
}
@font-face {
  font-family: "Plex Fallback";
  src: local("Menlo"), local("Consolas"), local("Courier New");
  size-adjust: 100%;
  ascent-override: 102.5%;
  descent-override: 27.5%;
  line-gap-override: 0%;
}

:root {
  --font-title: "Instrument Serif", "Serif Fallback", var(--dt-font-title);
  --font-display: "Bricolage Grotesque", "Bricolage Fallback", var(--dt-font-display);
  --font-text: "Instrument Sans", "Instrument Fallback", var(--dt-font-text);
  --font-mono: "IBM Plex Mono", "Plex Fallback", var(--dt-font-mono);
  --beat: 472ms; /* the mean gap between word starts in the Halfway take; app.js sets it from data.js */
  --cut: calc(var(--beat) / 2);
  --stagger: calc(var(--beat) / 4);
  --wrap: 1120px;
  --stage-w: 780px;
  --gutter: 32px;
}

/* ---- ground */
* { box-sizing: border-box; }
html { scroll-behavior: auto; -webkit-text-size-adjust: 100%; }
body {
  margin: 0;
  background: var(--dt-bg);
  color: var(--dt-text);
  font-family: var(--font-text);
  font-size: var(--dt-size-md);
  line-height: var(--dt-leading-body);
  -webkit-font-smoothing: antialiased;
}
a { color: inherit; text-decoration: none; }
p { margin: 0; }
/* Headings are the title card: Instrument Serif, one weight, and a tenth of the display sans's negative
   tracking, because the face is already drawn tight. Its cap height is taller than the sans's at the
   same size, so the sizes below did not move. */
h1, h2, h3 { margin: 0; font-family: var(--font-title); font-weight: 400; letter-spacing: var(--dt-tracking-title); text-wrap: balance; }
h1 { font-size: clamp(38px, 5.2vw, 64px); line-height: var(--dt-leading-tight); }
h2, .block h1 { font-size: clamp(28px, 3.2vw, var(--dt-size-3xl)); line-height: var(--dt-leading-snug); }
h3 { font-size: var(--dt-size-xl); line-height: var(--dt-leading-snug); }
code, kbd, pre, .mono { font-family: var(--font-mono); font-feature-settings: "zero" 1; }
code { font-size: 0.92em; }
button { font: inherit; color: inherit; }
img, svg, video { display: block; max-width: 100%; height: auto; }
.wrap { max-width: var(--wrap); margin: 0 auto; padding: 0 var(--gutter); }
.sr { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; }
.kicker { font-family: var(--font-mono); font-size: var(--dt-size-sm); font-weight: 500; color: var(--dt-text-3); }
.fine { font-size: var(--dt-size-sm); color: var(--dt-text-3); }
.sub { color: var(--dt-text-2); }

/* Focus: one visible ring everywhere, 3:1 against the ground. */
:focus-visible { outline: 2px solid var(--dt-focus); outline-offset: 3px; border-radius: 4px; }
.skip { position: absolute; left: var(--gutter); top: -60px; padding: 10px 14px; background: var(--dt-paper); color: var(--dt-paper-ink); border-radius: 8px; font-weight: 500; z-index: 10; }
.skip:focus { top: 12px; }

/* Off the stage, sections appear once with a half-beat opacity cut and no transform. */
.js .cut { opacity: 0; }
.js .cut.in { opacity: 1; transition: opacity var(--cut) linear; }

/* ---- buttons: paper white, never the voice colour. */
.btn {
  display: inline-flex; align-items: center; gap: 10px; height: 44px; padding: 0 18px;
  border-radius: 12px; border: 1px solid var(--dt-border); background: transparent; color: var(--dt-text);
  font-weight: 500; font-size: 15px; cursor: pointer; white-space: nowrap;
}
.btn:hover { border-color: var(--dt-focus); }
.btn.solid { background: var(--dt-paper); color: var(--dt-paper-ink); border-color: var(--dt-paper); }
.btn.solid:hover { background: #fff; }
.btn svg { width: 18px; height: 18px; flex: none; }

/* The sound control, one control shown in two places. Its waveform glyph is gold only while sound plays,
   and app.js scales each bar from the clip's measured loudness at the playhead. */
.wave { display: inline-flex; gap: 2.5px; align-items: center; height: 18px; }
.wave i { display: block; width: 2.5px; height: 14px; border-radius: 1.5px; background: currentColor; transform-origin: center; transform: scaleY(0.35); }
.wave i:nth-child(1), .wave i:nth-child(5) { transform: scaleY(0.3); }
.wave i:nth-child(2), .wave i:nth-child(4) { transform: scaleY(0.55); }
.wave i:nth-child(3) { transform: scaleY(0.8); }
[aria-pressed="true"] .wave { color: var(--dt-voice); }
/* Both labels of a sound control share one cell, one of them invisible, so a press never changes the control's width. */
.lbl { display: inline-grid; justify-items: center; }
.lbl > span { grid-area: 1 / 1; white-space: nowrap; }

/* ---- nav */
.nav { height: 64px; }
.nav .wrap { display: flex; align-items: center; gap: 28px; height: 100%; }
.brand { display: inline-flex; align-items: center; gap: 10px; font-family: var(--font-display); font-weight: 500; font-size: 19px; letter-spacing: -0.01em; }
.brand svg { width: 24px; height: 24px; }
.nav ul { display: flex; gap: 24px; margin: 0 0 0 12px; padding: 0; list-style: none; font-size: 15px; color: var(--dt-text-2); }
.nav ul a { white-space: nowrap; }
.nav ul a:hover, .nav ul a[aria-current] { color: var(--dt-text); }
.nav ul small { color: var(--dt-text-3); font-size: 12px; margin-left: 6px; }
.nav .install { margin-left: auto; height: 36px; padding: 0 14px; }

/* ---- hero. Everything from the top of the nav to the bottom of the transport fits in 900 css px at 1440 by 900.
   Clipped, so the stage glow never widens the page on a phone. */
.hero { padding: 16px 0 40px; overflow: clip; }
.hero .wrap {
  display: grid; justify-items: start; column-gap: 16px; row-gap: 0;
  grid-template-columns: auto 1fr;
  grid-template-areas: "kicker kicker" "h1 h1" "sub sub" "caption caption" "stage stage";
}
.hero .kicker { grid-area: kicker; font-size: 16px; color: var(--dt-text-2); }
.hero h1 { grid-area: h1; margin-top: 8px; }
.hero .sub { grid-area: sub; margin-top: 10px; font-size: var(--dt-size-lg); max-width: 44ch; }
/* ---- the command component. One pill holds every command the site shows: the hero's, the four in
   the install section, and the film pages'. It wraps rather than clips, because the one line is
   44 characters and a phone is 358 px wide. The prompt is written so a reader sees a shell, and is
   not selectable, so a hand-selection yields `curl …` and not `$ curl …`, which no shell runs.
   The copy button is appended by app.js into .copy, so a page with no script shows the command and
   no button that cannot do what its label says. */
.pill {
  margin-top: 18px; display: inline-flex; align-items: center; flex-wrap: wrap; gap: 8px 12px;
  min-height: 44px; padding: 5px 6px 5px 16px; max-width: 100%;
  border: 1px solid var(--dt-border); border-radius: 12px; background: var(--dt-surface); font-family: var(--font-mono); font-size: 14px;
}
.pill code { min-width: 0; line-height: 1.5; overflow-wrap: anywhere; }
.pill .p { color: var(--dt-text-3); user-select: none; -webkit-user-select: none; }
.pill .copy:empty { display: none; }
/* Reading the script before running it is the whole argument for piping a URL into a shell, and the
   link that lets you was five thousand pixels below the command, in the install section. It belongs
   beside the command, where the decision is made. site/_headers serves the file as text/plain so a
   click shows it rather than downloading it. Set in the text face and pushed away from the command,
   because in the mono face against `| sh` it read as something you were about to run. */
.pill .view { margin-left: auto; font-family: var(--font-text); font-size: 13px; color: var(--dt-text-3); text-decoration: underline; text-decoration-color: var(--dt-border); text-underline-offset: 3px; white-space: nowrap; }
.pill .view:hover { color: var(--dt-text); }
.pill button { height: 32px; padding: 0 12px; border: 0; border-radius: 8px; background: var(--dt-surface-2); color: var(--dt-text); font-size: 13px; font-weight: 500; cursor: pointer; }
.pill button:hover { background: var(--dt-border); }
.hero .caption { grid-area: caption; justify-self: start; width: 100%; max-width: var(--stage-w); margin-top: 14px; font-size: var(--dt-size-sm); color: var(--dt-text-3); }
.hero .caption a { color: var(--dt-text-2); text-decoration: underline; text-decoration-color: var(--dt-border); text-underline-offset: 3px; }
.hero .caption a:hover { color: var(--dt-text); }

/* The stage: 16:9 inside a paper hairline, the dark page doing the letterboxing. A static glow behind it,
   capped well under the map's lightness, so the picture is always the brightest thing. */
/* Aligned with the headline, not centred in the wrap. Centred, an 780px stage in a 1120px wrap
   started 138px to the right of every other left edge on the page, so the hero had two left
   margins and read as two columns that did not line up. */
.stage { grid-area: stage; justify-self: start; width: 100%; min-width: 0; max-width: var(--stage-w); margin: 8px 0 0; position: relative; }
.stage::before {
  content: ""; position: absolute; inset: -12% -8%; z-index: 0; pointer-events: none;
  background: radial-gradient(ellipse at 50% 45%, rgba(251, 246, 238, 0.07), rgba(251, 246, 238, 0) 62%);
}
.frame {
  position: relative; z-index: 1; aspect-ratio: 16 / 9; overflow: hidden; border-radius: 8px;
  background: var(--dt-bg); box-shadow: 0 0 0 1px rgba(251, 246, 238, 0.15);
  /* A button sizes to its content, and everything in here is absolutely positioned, so it
     collapsed to 7px and app.js derived the film's scale from that width. */
  display: block; width: 100%; border: 0; padding: 0; font: inherit; color: inherit;
  cursor: pointer; text-align: left; -webkit-tap-highlight-color: transparent;
}
/* The badge the picture wears until the voice is on. Gold, because it is the voice, and gone the
   moment the control it belongs to reports itself pressed. */
.frame .unmute {
  position: absolute; left: 12px; bottom: 12px; z-index: 2; display: inline-flex; align-items: center; gap: 8px;
  height: 34px; padding: 0 13px 0 11px; border-radius: 999px; background: rgba(21, 17, 14, 0.82);
  border: 1px solid var(--dt-border); color: var(--dt-voice); font-size: 13px; font-weight: 500;
  backdrop-filter: blur(6px); pointer-events: none;
}
.frame .unmute svg { width: 15px; height: 15px; }
.hero:has(.sound[aria-pressed="true"]) .frame .unmute { display: none; }
.stage-deck { position: absolute; left: 0; top: 0; width: 1920px; height: 1080px; transform-origin: 0 0; transform: scale(var(--s, 0.4)); overflow: hidden; background: #15110e; }
.stage-deck .scene { position: absolute; inset: 0; }
.stage-deck .scene[hidden] { display: none; }
.stage-deck [data-in] { will-change: opacity, transform; }

.band { margin-top: 12px; min-height: 60px; font-family: var(--font-display); font-size: 20px; line-height: 1.4; letter-spacing: -0.01em; }
.band p { min-height: 28px; }
.band .prev { color: var(--dt-text-3); }
.band .cur { color: var(--dt-text); }
.band .cur .live { color: var(--dt-voice); }
/* Unspoken words are laid out but invisible, so a line never reflows as its words arrive and nothing below the band moves. */
.band .w { display: inline; }
.band .w[hidden] { display: inline; visibility: hidden; }

.transport { display: flex; align-items: center; gap: 12px; margin-top: 12px; }
.transport .ctl { width: 40px; height: 40px; padding: 0; border-radius: 10px; border: 1px solid var(--dt-border); background: transparent; color: var(--dt-text); cursor: pointer; display: inline-grid; place-items: center; }
.transport .ctl:hover { border-color: var(--dt-focus); }
.transport .ctl svg { width: 16px; height: 16px; }
.transport .ctl .i-play, .transport .ctl .i-replay, .transport .ctl .i-pause { display: none; }
.transport .ctl[data-state="playing"] .i-pause, .transport .ctl[data-state="paused"] .i-play, .transport .ctl[data-state="ended"] .i-replay { display: block; }
/* The voice loads only on press, so a cold cache or a slow host leaves a gap the viewer has to
   be able to see. The play glyph holds and dims rather than the control going blank. */
.transport .ctl[data-state="loading"] .i-play { display: block; opacity: 0.45; }
.transport .ctl[data-state="loading"] { cursor: progress; }
.transport .tc { font-family: var(--font-mono); font-size: 13px; color: var(--dt-text-3); min-width: 8ch; font-variant-numeric: tabular-nums; }
.transport .sound { height: 40px; padding: 0 12px; min-width: 44px; justify-content: center; border-radius: 10px; }
.scrub { flex: 1; height: 40px; margin: 0; background: transparent; cursor: pointer; -webkit-appearance: none; appearance: none; }
.scrub::-webkit-slider-runnable-track { height: 3px; border-radius: 2px; background: var(--dt-border); }
.scrub::-moz-range-track { height: 3px; border-radius: 2px; background: var(--dt-border); }
.scrub::-webkit-slider-thumb { -webkit-appearance: none; appearance: none; width: 14px; height: 14px; margin-top: -5.5px; border-radius: 50%; background: var(--dt-paper); border: 0; }
.scrub::-moz-range-thumb { width: 14px; height: 14px; border-radius: 50%; background: var(--dt-paper); border: 0; }

/* ---- sections */
/* The rule between sections is the width of the content, not of the window: a full-bleed hairline
   on a wide screen is twice the width of anything it separates and reads as a stray line. */
section.block { position: relative; padding: 72px 0; }
section.block::before {
  content: ""; position: absolute; top: 0; left: 50%; transform: translateX(-50%);
  width: min(100% - var(--gutter) * 2, var(--wrap) - var(--gutter) * 2);
  height: 1px; background: var(--dt-border);
}
section.block .lead { margin-top: 12px; color: var(--dt-text-2); max-width: 52ch; }

/* ---- the invitation that stands on the landing page where the explanation used to, and sends a reader to how.html. */
.how-more .btn { margin-top: 24px; }

/* ---- films: one row of small cards, each a link to the film's own page. */
.films { padding-top: 40px; padding-bottom: 48px; }
.reel { display: grid; grid-template-columns: repeat(auto-fit, minmax(280px, 340px)); gap: 20px; margin: 24px 0 0; padding: 0; list-style: none; }
.card { display: grid; grid-template-columns: 1fr auto; gap: 2px 10px; align-content: start; }
.card img { grid-column: 1 / -1; width: 100%; aspect-ratio: 16 / 9; border-radius: 6px; background: var(--dt-surface); box-shadow: 0 0 0 1px rgba(251, 246, 238, 0.15); margin-bottom: 8px; }
.card b { font-weight: 500; font-size: 15px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.card .lbl { grid-column: 1; font-size: var(--dt-size-xs); color: var(--dt-text-3); }
.card .dur { grid-column: 2; grid-row: 2; font-family: var(--font-mono); font-size: var(--dt-size-xs); color: var(--dt-text-3); font-variant-numeric: tabular-nums; }
.card .from { grid-column: 1 / -1; margin-top: 4px; font-size: var(--dt-size-xs); color: var(--dt-text-2); }
.card:hover b { text-decoration: underline; text-decoration-color: var(--dt-border); text-underline-offset: 3px; }

/* ---- how it works: five chapters scroll by on the left, the stage stays pinned on the right by native scroll alone. */
/* The run-out under the last chapter, so the panel is still pinned while chapter 5 is being read. It has to
   clear the panel's own height above the fold: anything shorter and sticky lets go mid-chapter. */
.how { --chapter-tail: 240px; }
.how .grid { display: grid; grid-template-columns: minmax(0, 5fr) minmax(0, 6fr); gap: 48px; margin-top: 36px; align-items: start; }
.chapters { display: grid; padding-bottom: var(--chapter-tail); }
/* Every chapter parks the stage on a moment only it shows, so the column is paid for by the chapter count,
   not by its height: 42vh is a screenful of reading between two different frames. The tail is what keeps the
   promise the sticky panel makes: the last chapter is the one with a new frame to show, and without a run-out
   below it the panel releases and slides off the top of the screen exactly as that frame arrives. The two come
   out of the same budget: the four chapters give up what the tail takes, so the section is no longer than before. */
.chapter { min-height: 42vh; display: grid; align-content: center; gap: 14px; padding: 24px 0; }
.chapter:first-child { min-height: 38vh; align-content: start; padding-top: 8px; }
.chapter .n { font-family: var(--font-mono); font-size: var(--dt-size-xs); color: var(--dt-text-3); letter-spacing: var(--dt-tracking-eyebrow); }
.chapter h3 { font-size: var(--dt-size-2xl); }
.panel {
  margin: 0; padding: 14px 16px; border: 1px solid var(--dt-border); border-radius: 10px; background: var(--dt-surface);
  font-family: var(--font-mono); font-size: 13px; line-height: 1.55; color: var(--dt-text-2); overflow: auto;
}
.panel .fn { display: block; margin-bottom: 8px; font-size: var(--dt-size-xs); color: var(--dt-text-3); }
.panel pre { margin: 0; white-space: pre-wrap; overflow-wrap: anywhere; }
.panel .hl { color: var(--dt-text); }
.panel .cue { color: var(--dt-text); }
.panel .t { color: var(--dt-text-3); }
.panel .ok { color: var(--dt-ok); }
.pinned { position: sticky; top: 24px; display: grid; gap: 16px; }
.stagepin { display: contents; }
.pinned .frame { max-width: 100%; }
.pinned .verify { font-family: var(--font-mono); font-size: 13px; color: var(--dt-text-2); min-height: 20px; }
.pinned .verify b { font-weight: 500; color: var(--dt-text); }

/* Chapters 4 and 5 write on the stage. At 4 each picture carries the `data-in` it was authored with, the
   attribute the panel beside it quotes, and at 5 the same badge carries what `decktalk verify` measured for
   that landing. Sizes are in the deck's own 1920 px space, so they scale with the frame like the deck's labels.
   Colours come from the stage's own palette, which is fixed dark whichever scheme the page is in.
   Where a badge sits is the picture's business, so each picture carries its own corner rather than the badge
   rule carrying a list of exceptions: the wordmark is a full-bleed row and hangs its badge under its middle. */
.pinned .stage-deck .scene > [data-in] { --tag-x: 0; --tag-y: -58px; --tag-shift: 0; }
.pinned .stage-deck .scene > .mark { --tag-x: 50%; --tag-y: calc(100% + 10px); --tag-shift: -50%; }
.pinned .stage-deck.named .scene > [data-in]::after,
.pinned .stage-deck.measured .scene > [data-in][data-cue-ms]::after {
  position: absolute; left: var(--tag-x); top: var(--tag-y); transform: translateX(var(--tag-shift)); z-index: 2;
  padding: 0 16px; height: 48px; border-radius: 10px;
  font: 500 34px/48px var(--dt-font-mono); letter-spacing: 0.02em; white-space: nowrap;
}
.pinned .stage-deck.named .scene > [data-in]::after {
  content: attr(data-in);
  background: var(--dt-bg); color: var(--dt-text); box-shadow: 0 0 0 3px var(--dt-border);
}
.pinned .stage-deck.measured .scene > [data-in][data-cue-ms]::after {
  content: attr(data-cue-ms);
  background: var(--dt-paper); color: var(--dt-paper-ink); box-shadow: 0 0 0 3px var(--dt-bg);
}

.catchup { display: grid; gap: 12px; }
.picker { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; font-family: var(--font-mono); font-size: 13px; color: var(--dt-text-3); }
.picker [role="radio"] { width: 30px; height: 30px; border-radius: 8px; border: 1px solid var(--dt-border); background: transparent; color: var(--dt-text-2); cursor: pointer; font-family: var(--font-mono); font-size: 13px; }
.picker [role="radio"][aria-checked="true"] { background: var(--dt-paper); color: var(--dt-paper-ink); border-color: var(--dt-paper); }
.transcript { font-family: var(--font-display); font-size: 20px; line-height: 1.7; letter-spacing: -0.01em; color: var(--dt-text-2); }
.transcript .w { display: inline; padding: 0; margin: 0; border: 0; background: none; font: inherit; color: inherit; cursor: pointer; border-radius: 3px; }
.transcript .w:hover { color: var(--dt-text); }
.transcript:focus-within .w.cursor { outline: 2px solid var(--dt-focus); outline-offset: 2px; }
.transcript .w.said { color: var(--dt-text); }
.transcript .w.live { color: var(--dt-voice); }
.transcript .phrase { text-decoration: underline; text-decoration-color: var(--dt-border); text-decoration-thickness: 1.5px; text-underline-offset: 5px; }
.transcript .at { font-family: var(--font-mono); font-size: 11px; vertical-align: super; line-height: 0; margin-left: 2px; color: var(--dt-text-3); text-decoration: none; }
.transcript .at:hover, .transcript .at.live { color: var(--dt-voice); }
.transcript .cuetick { color: var(--dt-voice); }
.catchup .row { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
.catchup .row .btn { height: 36px; padding: 0 12px; font-size: 14px; }
.catchup .help { font-size: var(--dt-size-xs); color: var(--dt-text-3); }

/* ---- change a sentence */
.takes { display: grid; gap: 12px; margin-top: 28px; }
.take { display: flex; align-items: center; gap: 14px; flex-wrap: wrap; padding: 12px 14px; border: 1px solid var(--dt-border); border-radius: 10px; background: var(--dt-surface); }
.take .btn { height: 36px; padding: 0 12px; font-size: 14px; }
.take .line { font-family: var(--font-display); font-size: 20px; letter-spacing: -0.01em; }
.take .line .rest { color: var(--dt-text-3); }
.take .line mark { background: none; color: var(--dt-text); text-decoration: underline; text-decoration-color: var(--dt-border); text-underline-offset: 4px; }
.take.after .line mark { color: var(--dt-voice); text-decoration-color: var(--dt-voice); }
/* The hash is the take's cache key, so it says so: it is the reason the lanes below read "cached". */
.take .hash { margin-left: auto; font-family: var(--font-mono); font-size: 12px; color: var(--dt-text-3); }
.take .hash b { font-weight: 400; color: var(--dt-text-2); }
.edit .files-note { margin-top: 16px; max-width: 92ch; }
/* The two disclosures: the diff and the build log. Native <details>, so they open with the script
   dead, and the marker is the only thing the summary borrows from the button style. */
.drop { margin-top: 10px; }
.drop > summary { display: flex; align-items: center; gap: 8px; width: max-content; max-width: 100%; padding: 6px 2px; font-size: var(--dt-size-sm); color: var(--dt-text-3); cursor: pointer; list-style: none; }
.drop > summary::-webkit-details-marker { display: none; }
.drop > summary::before { content: ""; width: 7px; height: 7px; flex: none; border-right: 1.5px solid currentcolor; border-bottom: 1.5px solid currentcolor; transform: rotate(-45deg); transition: transform var(--cut) linear; }
.drop[open] > summary::before { transform: rotate(45deg); }
.drop > summary:hover { color: var(--dt-text); }
.drop[open] > summary { color: var(--dt-text-2); }
/* The edit, file by file: the words that changed, with the words either side of them dimmed.
   The file name is a column on a wide screen and a line of its own on a phone. */
.diff { margin-top: 14px; display: grid; gap: 10px; list-style: none; padding: 0; }
.diff .file { display: grid; grid-template-columns: 148px 1fr; gap: 10px 14px; align-items: baseline; }
.diff .fn { font-family: var(--font-mono); font-size: 12px; color: var(--dt-text-3); }
.diff .rows { display: grid; gap: 6px; min-width: 0; list-style: none; padding: 0; margin: 0; }
.diff .row { display: grid; grid-template-columns: 120px 1fr; gap: 12px; align-items: baseline; min-width: 0; }
/* --dt-text-dim is 3.02:1 and the generator calls it decorative by design; this is read, so it
   takes --dt-text-3 at 5.86:1. No opacity on top: it composites back down below 4.5:1. */
.diff .at { font-family: var(--font-mono); font-size: 11.5px; color: var(--dt-text-3); }
.diff .at i { font-style: normal; color: var(--dt-text-3); }
.diff .txt { font-family: var(--font-mono); font-size: 12.5px; line-height: 1.6; color: var(--dt-text-2); overflow-wrap: anywhere; }
.diff .ctx { color: var(--dt-text-3); }
.diff s { text-decoration-color: var(--dt-text-dim); color: var(--dt-text-3); }
.diff b { font-weight: 500; color: var(--dt-voice); }

.lanes { display: grid; grid-template-columns: 90px repeat(4, 1fr); gap: 6px; margin-top: 24px; font-family: var(--font-mono); font-size: 12px; }
.lanes .hd { color: var(--dt-text-3); padding-bottom: 4px; }
.lanes .hd b { display: block; font-weight: 500; color: var(--dt-text-2); }
.lanes .rl { color: var(--dt-text-3); display: flex; align-items: center; }
.lanes .cell { height: 34px; display: flex; align-items: center; justify-content: center; border-radius: 8px; background: var(--dt-surface); border: 1px solid var(--dt-border); color: var(--dt-text-2); }
.lanes .cell.live { background: var(--dt-voice-soft); border-color: var(--dt-voice); color: var(--dt-voice); }
.lanes .cell.span { grid-column: 2 / -1; }
.js .lanes .cell { opacity: 0; }
.js .lanes.in .cell { opacity: 1; transition: opacity var(--cut) linear; transition-delay: calc(var(--k) * var(--stagger)); }
/* The rebuild's price, out of the log and onto the page: it is the claim, not a build artifact. */
.cost-line { margin-top: 22px; font-size: var(--dt-size-md); color: var(--dt-text-2); max-width: 78ch; }
.cost-line b { font-weight: 500; color: var(--dt-text); }
.cost-line code { font-family: var(--font-mono); font-size: 14px; color: var(--dt-text-3); }
/* A log line can be longer than the box that holds it, so each line is a block that wraps under a
   hanging indent. Nothing runs off the right edge of a phone, at any width, and the tab stop and
   its name stay for the case where something unbreakable still scrolls. */
.log:focus-visible { outline: 2px solid var(--dt-focus); outline-offset: 3px; }
.log { margin: 14px 0 0; padding: 16px 18px; border: 1px solid var(--dt-border); border-radius: 10px; background: var(--dt-surface); font-size: 12.5px; line-height: 1.55; color: var(--dt-text-2); overflow: auto; white-space: pre-wrap; overflow-wrap: anywhere; }
.log .ln { display: block; padding-left: 4.5ch; text-indent: -4.5ch; }
.log .p { color: var(--dt-text-3); }
.log .ln.gloss { margin-top: 10px; padding-left: 0; text-indent: 0; color: var(--dt-text-3); }
.log .live { color: var(--dt-voice); }
.edit .after-line { margin-top: 24px; font-family: var(--font-display); font-size: var(--dt-size-xl); letter-spacing: -0.015em; }

/* ---- install */
/* Each step is the command component and the sentence that says what it does. The column is wide
   enough for the longest of the four with its copy button beside it, so none of them wraps here. */
.steps { display: grid; gap: 24px; margin: 14px 0 0; padding: 0; list-style: none; max-width: 68ch; }
/* Half of this list never runs again, and nothing else on the page said so. Carries no .cut: the
   grouping is part of the instruction, so it survives a script that throws exactly as the
   commands do. */
.steps-label { margin: 28px 0 0; font-family: var(--font-mono); font-size: var(--dt-size-sm); color: var(--dt-text-3); }
.steps-label + .steps { margin-top: 10px; }
/* One column, like every other section. This was a two-column grid, command on the left and its
   sentence on the right, which put `decktalk install` about 500px from the words explaining it and
   left the reader's eye to pair them across a gutter nothing else on the page uses. The sentence
   now sits under the command it describes, at the lead's measure. */
.steps li { display: grid; gap: 8px; }
.steps .pill { margin-top: 0; justify-self: start; }
.steps code { color: var(--dt-text); }
.nb { white-space: nowrap; }
/* The step's sentence, and only that: the spans inside the command component keep the component's own colours. */
.steps li > span { color: var(--dt-text-2); }
.install-note { max-width: 68ch; }
.steps li > span a, .install-note a { color: var(--dt-text); text-decoration: underline; text-decoration-color: var(--dt-border); text-underline-offset: 3px; }
.install-note { margin-top: 24px; color: var(--dt-text-2); }

/* ---- footer */
.foot { border-top: 1px solid var(--dt-border); padding: 28px 0 40px; font-size: var(--dt-size-sm); color: var(--dt-text-3); }
.foot .wrap { display: flex; flex-wrap: wrap; gap: 10px 24px; align-items: center; }
.foot a:hover { color: var(--dt-text); }
.foot .brand { font-size: 16px; color: var(--dt-text-2); }
.foot .brand svg { width: 20px; height: 20px; }
.foot .links { display: flex; gap: 18px; margin-left: auto; flex-wrap: wrap; }

/* ---- phones */

/* ---- the film pages, and the install band that ends /how too. The film and the command share one
   screen: a visitor who clicked a poster lands on the film, and the line that makes one is the first
   thing under it, still in view when the film ends. Only the top and bottom pad, so .wrap's gutter
   survives on a phone. */
.film-page { padding-top: 20px; padding-bottom: 64px; display: grid; gap: 18px; }
.film-page .head { display: grid; gap: 4px; }
.film-page h1 { font-size: clamp(32px, 4vw, 48px); }
.film-page video { width: 100%; aspect-ratio: 16 / 9; border-radius: 8px; background: var(--dt-surface); box-shadow: 0 0 0 1px rgba(251, 246, 238, 0.15); }
.film-page .under { display: grid; gap: 12px; }
.film-page .meta { display: flex; flex-wrap: wrap; gap: 6px 14px; font-size: var(--dt-size-sm); color: var(--dt-text-3); }
/* The one-line install, reusing the pill. The prompt is not selectable, so a hand-selection yields a
   line that runs, and the pill wraps rather than clipping on a phone. */
/* A nav Install that lands here leaves a breath above the band rather than a flush edge. */
.cta-band { display: flex; flex-wrap: wrap; align-items: center; gap: 10px 18px; scroll-margin-top: 24px; }
.make { scroll-margin-top: 24px; }
.cta-band .pill { grid-area: auto; margin-top: 0; max-width: 100%; height: auto; min-height: 44px; padding: 6px 6px 6px 16px; }
.cta-band .pill code { min-width: 0; line-height: 1.5; overflow-wrap: anywhere; }
.cta-band .pill .p { user-select: none; -webkit-user-select: none; }
.cta-band .fine { flex: 1 1 24ch; }
.make h2 { margin-bottom: 18px; }
.script { margin-top: 26px; display: grid; gap: 24px; max-width: 64ch; }
.script > h2 { font-size: var(--dt-size-2xl); }
.script h3 { font-size: var(--dt-size-xl); display: flex; gap: 12px; align-items: baseline; }
.script h3 .t { font-family: var(--font-mono); font-size: 13px; font-weight: 400; color: var(--dt-text-3); letter-spacing: 0; }
.script p { font-family: var(--font-display); font-size: 20px; line-height: 1.6; letter-spacing: -0.01em; color: var(--dt-text-2); }
.script p .beat { color: var(--dt-text-3); font-family: var(--font-mono); font-size: 12px; margin: 0 4px; }
.script p mark { background: none; color: var(--dt-text); text-decoration: underline; text-decoration-color: var(--dt-border); text-decoration-thickness: 1.5px; text-underline-offset: 5px; }
.note { color: var(--dt-text-2); max-width: 64ch; }
.note.about { font-family: var(--font-text); font-size: 16px; }
.note a, .next a { color: var(--dt-text); text-decoration: underline; text-decoration-color: var(--dt-border); text-underline-offset: 3px; }
.edit h2 { font-size: var(--dt-size-2xl); margin-bottom: 12px; }
.edit p + p { margin-top: 12px; }
/* Every secondary page ends on the same line: where to go next, after the thing it came for. */
.next { margin-top: 26px; padding-top: 22px; border-top: 1px solid var(--dt-border); max-width: none; color: var(--dt-text-2); }

@media (max-width: 860px) {
  /* the file name and the cue each take their own line, so nothing truncates */
  .diff .file { grid-template-columns: 1fr; gap: 6px; }
  .diff .row { grid-template-columns: 1fr; gap: 2px; }

  :root { --gutter: 16px; }
  .nav { height: 56px; }
  /* The links used to be hidden here, which left a phone with a header of one logo and one
     button, and that button scrolled to four shell commands. A phone has no shell. So the phone
     keeps the two links that go somewhere it can use, and the footer keeps Docs and GitHub. */
  .nav ul { gap: 18px; margin-left: auto; font-size: 14px; }
  .nav ul li:nth-child(n + 3) { display: none; }
  .nav ul small { display: none; }
  .hero { padding-top: 4px; }
  /* The pill sits beside the play button on a wide screen, above the film. It used to drop to
     the very bottom here, under the stage, the band and the transport, so on a phone the one
     line a visitor came for arrived after the film rather than with it. Same order as the
     desktop grid now: read the promise, play it, take the line. */
  .hero .wrap { grid-template-columns: 1fr; grid-template-areas: "kicker" "h1" "sub" "caption" "stage"; }
  .hero .kicker { font-size: 12px; }
  .hero h1 { font-size: 34px; margin-top: 4px; }
  .hero .sub { margin-top: 6px; font-size: 16px; line-height: 1.4; max-width: 34ch; }
  .hero .caption { margin-top: 8px; font-size: 13px; }
  .stage { margin-top: 8px; }
  /* The band holds one dimmed line and up to two current lines, so the transport never moves. */
  .band { font-size: 17px; min-height: 72px; }
  .band .prev { min-height: 24px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
  .band .cur { min-height: 48px; }
  .transport .tc { display: none; }
  .transport .sound { min-width: 0; padding: 0 10px; gap: 7px; }
  .reel { grid-template-columns: repeat(2, 1fr); gap: 14px; }
  .how .grid { grid-template-columns: 1fr; gap: 16px; }
  /* The stage is pinned here too, at the top instead of beside. It used to be `position: static`
     above the chapters, so on a phone it scrolled away before the first chapter was read and the
     five frames it now draws all changed off screen: the whole reason for the mechanic, spent
     where nobody could see it. Chapters keep a screen-ish height so each one holds the stage long
     enough to be looked at, and the run-out still frees the panel after the last. */
  .how { --chapter-tail: 160px; }
  .chapter, .chapter:first-child { min-height: 58vh; padding: 18px 0; }
  /* Only the frame and its measurement pin. The catch-up widget is 350 px of transcript and
     controls, and pinning that too left a phone about 240 px in which to read the chapter the
     stage is illustrating. It goes after the chapters instead, where it reads as the invitation
     it is rather than as part of the explanation. */
  .pinned { display: contents; }
  .stagepin {
    display: grid; gap: 8px; order: -1; position: sticky; top: 8px; z-index: 1;
    background: var(--dt-bg); padding-bottom: 10px;
  }
  .catchup { order: 1; }
  /* One line, always: the pin's height is what the chapters scroll under, and a verify line that
     wrapped to two or three lines by section moved them 32 px on every pick. The line scrolls
     sideways instead, so every landing is still reachable, and chapter 5 lists them all. */
  .pinned .verify { font-size: 11.5px; white-space: nowrap; overflow-x: auto; }
  .cta-band .pill { width: 100%; }
  .lanes { grid-template-columns: 64px repeat(4, 1fr); font-size: 11px; }
  .steps { gap: 20px; }
  .steps li { grid-template-columns: 1fr; align-items: start; }
  /* One shape for every pill at this width: the command takes the row and the button the far
     edge, top aligned, whether the command wraps to two lines or fits on one. */
  .steps .pill, .cta-band .pill { display: grid; grid-template-columns: minmax(0, 1fr) auto; align-items: start; justify-self: stretch; }
  section.block { padding: 48px 0; }
  .foot .links { display: grid; grid-template-columns: repeat(3, max-content); justify-content: start; gap: 10px 18px; margin-left: 0; }
}

/* ---- reduced motion: cuts become plain visibility, the waveform holds still, nothing plays unasked. */
@media (prefers-reduced-motion: reduce) {
  .js .cut, .js .lanes .cell { opacity: 1; transition: none; }
  .pinned { position: static; }
  .how { --chapter-tail: 0px; }
  .chapter { min-height: 0; }
}
