/* Quiet Transcript — "the recorder is yours, and the paperwork is yours".
 *
 * Two materials. THE MACHINE is the transport bay on the tool page: inset panels, small
 * letterspaced sans, a drawn cassette, a counter. THE PAPERWORK is everything else: cream card
 * stock and serif prose. Only the tool page gets the machine, because it is the only page where
 * a machine is doing anything; a privacy page dressed as hardware reads as a costume, and this
 * site's entire promise depends on being believed literally.
 *
 * The full reasoning, the measured contrast ratios and the list of things the metaphor is
 * deliberately NOT allowed to do live in docs/art-direction.md.
 *
 * Nothing here loads: no font file, no image, no gradient mesh, no third-party anything. Every
 * texture is a border, a shadow or a drawn shape in the page's own markup, and the site's test
 * asserts that no page fetches from an origin that is not this one, the analytics collector, or
 * the named Google advertising hosts. That last clause is new: the ad-bearing CSP this site now
 * serves under admits `img-src https:`, so the browser would allow a remote image where it
 * previously refused one. The rule is therefore ours to keep rather than the header's to enforce.
 */

:root {
  color-scheme: light dark;

  /* Card stock and ferric oxide. The darkest value on this site is a bitumen brown; there is
     no #000 anywhere, in either scheme, on purpose. */
  --paper: #f6f0e4;
  --panel: #efe6d5;
  --inset: #e7dbc6;
  --line: #d9cdb8;
  --ink: #2b211a;          /* 13.86:1 on paper, 12.69:1 on panel */
  --muted: #6a5c4f;        /*  5.68:1 on paper,  5.20:1 on panel, 4.71:1 on inset */
  --oxide: #9e3f1b;        /*  5.82:1 on paper,  5.33:1 on panel; paper on it is 5.82:1 */
  --oxide-lamp: #b85327;   /* graphics only — the label band and the running lamp, never text */
  --teal: #1f5e5b;         /*  6.58:1 on paper,  6.03:1 on panel */
  --error: #8e2c17;        /*  7.33:1 on paper,  6.72:1 on panel */
  --steel: #8c8578;        /* screws, rails, furniture — never text */

  --edge-hi: rgba(255, 255, 255, 0.72);
  --sunk: rgba(43, 33, 26, 0.13);

  /* The room the deck sits in. ONE rule governs these and it is not a taste rule: a ground
     overlay may only move the page AWAY from the ink — lighter in this scheme, darker in the
     dark one — so no measured text contrast in docs/art-direction.md can fall because of a
     background. Anything that moves the ground TOWARD the ink is confined to the viewport
     margins outside the 38rem reading column, and only at widths where those margins exist. */
  --ground-away: rgba(255, 252, 243, 0.92);   /* the pool of light the machine stands in */
  --ground-toward: rgba(43, 33, 26, 0.07);    /* the desk falling away — margins only */
  --ground-bloom: rgba(158, 63, 27, 0.05);    /* oxide warmth — margins only */
  --card-lift: rgba(255, 255, 255, 0.40);     /* the light caught by the top of a card */

  /* The running-tape field. Oxide, and strong enough to read from the other side of a desk;
     the counter that sits on it is `--paper`, which is the same 5.82:1 pair the primary button
     already uses, so nothing new had to be measured. */
  --rec-field: rgba(158, 63, 27, 0.16);
  --rec-edge: #9e3f1b;

  /* The drawn cassette. Held as tokens so the object recolours with the scheme instead of
     becoming a second, unrelated drawing at night. */
  --deck-shell: #e3d6bc;
  --deck-shell-edge: #c0ac8a;
  --deck-label: #f8f3e7;
  --deck-band: #b85327;
  --deck-window: #2a2019;
  --deck-tape: #6b4326;
  --deck-hub: #f0e8d7;
  --deck-rule: #c9b99b;
  --deck-screw: #9a8b72;

  /* Paper is serif; the machine is sans. No download — the CSP forbids one and, given a free
     choice, a 1970s display face on a page about privilege reads as fancy dress. */
  --font-body: ui-serif, Georgia, "Iowan Old Style", "Times New Roman", serif;
  --font-panel: ui-sans-serif, -apple-system, "Helvetica Neue", "Segoe UI", Roboto, Arial, sans-serif;
  --font-counter: ui-monospace, "SF Mono", Menlo, Consolas, monospace;
}

/* Kept, not dropped: half this audience works at two in the morning and the OS setting is a
   real preference. It is the same deck under a desk lamp — warm bitumen at #251e17, no black
   and no blue-grey. */
@media (prefers-color-scheme: dark) {
  :root {
    --paper: #251e17;
    --panel: #31281f;
    --inset: #1d1712;
    --line: #4a3d2f;
    --ink: #f2e8d8;        /* 13.56:1 on paper, 11.91:1 on panel */
    --muted: #bcab94;      /*  7.36:1 on paper,  6.46:1 on panel */
    --oxide: #e6885a;      /*  6.29:1 on paper,  5.53:1 on panel; ink on it is 6.29:1 */
    --oxide-lamp: #e6885a;
    --teal: #7fc6bb;       /*  8.38:1 on paper,  7.36:1 on panel */
    --error: #f0a48c;      /*  7.15:1 on panel */
    --steel: #8b7c64;

    --edge-hi: rgba(255, 238, 208, 0.07);
    --sunk: rgba(0, 0, 0, 0.34);

    /* Mirrored, not inverted by accident: at night the reading column is the DARKEST part of
       the page, so the same "away from the ink" rule holds and the same contrast table stands. */
    --ground-away: rgba(11, 8, 5, 0.55);
    --ground-toward: rgba(255, 232, 198, 0.045);
    --ground-bloom: rgba(230, 136, 90, 0.07);
    /* A card at night is lifted by being DEEPER than its surroundings, not brighter: the
       highlight that reads as light on cream would eat 1.3 stops of contrast on bitumen. */
    --card-lift: rgba(0, 0, 0, 0.22);

    --rec-field: rgba(230, 136, 90, 0.17);
    --rec-edge: #e6885a;

    --deck-shell: #3e3327;
    --deck-shell-edge: #584835;
    --deck-label: #d6c6a6;
    --deck-band: #c4632f;
    --deck-window: #171210;
    --deck-tape: #7a4e2c;
    --deck-hub: #cdbea2;
    --deck-rule: #a08d6c;
    --deck-screw: #7a6a52;
  }
}

* { box-sizing: border-box; }

body {
  margin: 0;
  /* max() rather than a bare env(): the viewport is not `viewport-fit=cover`, so the insets
     resolve to zero and the literal wins. It costs nothing and is already right if an
     edge-to-edge surface ever justifies the cover flag. */
  padding: 0 max(1.25rem, env(safe-area-inset-right)) calc(4rem + env(safe-area-inset-bottom))
           max(1.25rem, env(safe-area-inset-left));
  background: var(--paper);
  color: var(--ink);
  font: 1.05rem/1.65 var(--font-body);
  -webkit-text-size-adjust: 100%;
}

header, main, footer { max-width: 38rem; margin: 0 auto; }

/* ---- the room ------------------------------------------------------------------------------
   The operator asked for a richer ground and the brief forbids the easy answers: no photograph,
   no third-party asset, no grain or fibre overlay behind text (it moirés at 3× and it makes
   16px serif worse). What is left is light. One soft pool of it falls from above the deck, and
   on a wide screen the desk beyond the reading column falls away at the edges. That is all: the
   hairline reel that used to be drawn in the far margin is gone, for the reason recorded below.

   It costs no request and no image decode: ONE `position: fixed` layer of pure gradient, so it
   paints once and composites on scroll instead of repainting. On a phone it is a single radial
   gradient over a solid colour, which is why the LCP budget survived it — measured, not
   assumed. */
body::before {
  content: "";
  position: fixed;
  inset: 0;
  z-index: -1;
  pointer-events: none;
  background: radial-gradient(140% 52% at 50% -6%, var(--ground-away) 0, transparent 62%);
}

@media (min-width: 64rem) {
  body::before {
    background:
      radial-gradient(140% 52% at 50% -6%, var(--ground-away) 0, transparent 62%),
      radial-gradient(60% 60% at 3% 108%, var(--ground-bloom) 0, transparent 70%),
      /* Stops chosen so the shade never reaches the 38rem column: at this breakpoint the
         column occupies the middle ~59% of the viewport and the shade stops at 17%. */
      linear-gradient(90deg, var(--ground-toward) 0, transparent 17%, transparent 83%, var(--ground-toward) 100%);
  }
}

/* There used to be a second fixed layer here: two concentric hairline circles in the right-hand
   margin above 80rem, meant to read as a reel machined into the desk. It read as an unfinished
   wireframe, and the operator said so.

   It is REMOVED rather than redrawn, and the reason is the one rule this design already had.
   The cassette earns its place because every part of it reports something true — the reels turn
   when tape moves, the packs thin and thicken with the playhead, the record key lights when the
   microphone is live. A ring in the margin reported nothing. By this site's own standard that
   makes it a sticker, and the standard is worth more than the ornament. What is left in the
   margins is light, which is a property of the room rather than a shape drawn in it.

   It also cost something: one more `position: fixed` compositing layer on every page, including
   the four that have no machine on them at all. */

/* ---- the plate ---------------------------------------------------------------------------
   A cassette mark and the site's name, set like something silkscreened on a machine. It is the
   only navigation above the fold, so it is a full 44px target rather than a decorative logo. */

.site-header { padding: 0.9rem 0 0.4rem; }


.brand {
  display: inline-flex;
  align-items: center;
  gap: 0.6rem;
  min-height: 44px;
  color: var(--oxide);
  text-decoration: none;
  font: 600 0.8rem/1 var(--font-panel);
  letter-spacing: 0.15em;
  text-transform: uppercase;
}

.brand .mark { width: 30px; height: 19px; flex: 0 0 auto; }

a { color: var(--oxide); text-decoration-thickness: 1px; text-underline-offset: 0.18em; }

/* The ring is drawn OUTSIDE the control, on the page ground, so its contrast is ink-on-paper
   (13.9:1) whatever colour the control itself is. A ring painted on the oxide button would be
   the one place this palette could not guarantee it. */
a:focus-visible, button:focus-visible, input:focus-visible, [tabindex]:focus-visible {
  outline: 3px solid var(--ink);
  outline-offset: 3px;
  border-radius: 4px;
}

h1 {
  font: 1.6rem/1.18 var(--font-body);
  font-size: clamp(1.6rem, 5.2vw, 2.3rem);
  letter-spacing: -0.012em;
  margin: 0.4rem 0 0;
}

/* The leader tape: three centimetres of oxide before the recording starts. It is the one piece
   of pure decoration on the site and it is four lines of CSS. */
h1::after {
  content: "";
  display: block;
  width: 3rem;
  height: 4px;
  margin: 0.75rem 0 0.9rem;
  border-radius: 2px;
  background: var(--oxide);
}

h2 {
  font: 600 1.2rem/1.35 var(--font-body);
  margin: 2.1rem 0 0.6rem;
}

/* Prose sections read as a spec sheet: each one begins on its own rule. Deliberately scoped to
   direct children of main, so the result panel's own heading stays part of its card. */
main > h2 {
  padding-top: 1.5rem;
  border-top: 1px solid var(--line);
}

/* A sub-head inside the capability section. Panel type, because it labels a list of machine
   facts rather than opening an argument. */
h3 {
  margin: 1.6rem 0 0.55rem;
  color: var(--muted);
  font: 600 0.8rem/1.4 var(--font-panel);
  letter-spacing: 0.11em;
  text-transform: uppercase;
}

p { margin: 0 0 1rem; }

/* The legal pages quote machine strings — a cache key, a collector host, three event names —
   and those are the one place a reader has to compare characters against their own developer
   tools. They get the counter face and a chip, so a hostname is visibly a hostname rather than
   a phrase, and break-word keeps `analytics.pagefabrica.com` inside a 320px column. */
code {
  padding: 0.1em 0.32em;
  border: 1px solid var(--line);
  border-radius: 4px;
  background: var(--panel);
  font: 0.88em/1.45 var(--font-counter);
  overflow-wrap: break-word;
}
.lede { color: var(--muted); font-size: 1.11rem; }
.note { color: var(--muted); font-size: 0.97rem; margin-bottom: 0; }

/* ---- panels ------------------------------------------------------------------------------ */

.card {
  position: relative;
  /* The card catches the same light the page does: a short lift at the top over the flat panel
     colour. It only lightens in the light scheme and only deepens in the dark one, so no
     contrast pair on a card changes. */
  background:
    linear-gradient(180deg, var(--card-lift) 0, transparent 96px),
    var(--panel);
  border: 1px solid var(--line);
  border-radius: 14px;
  padding: 1.5rem 1.15rem 1.15rem;
  margin: 1.5rem 0;
  box-shadow: inset 0 1px 0 var(--edge-hi);
}

/* One 6px hairline rail across the top of the two cards that are the instrument. This is the
   entire "brushed metal" budget: no image, no blur, no photograph of a machine. */
#intake::before,
#result::before {
  content: "";
  position: absolute;
  inset: 0 0 auto 0;
  height: 6px;
  border-radius: 13px 13px 0 0;
  background: repeating-linear-gradient(90deg, var(--steel) 0 1px, transparent 1px 4px);
  opacity: 0.4;
}

/* ---- the transport bay -------------------------------------------------------------------- */

.bay {
  background: var(--inset);
  border: 1px solid var(--line);
  border-radius: 12px;
  padding: 0.8rem;
  margin-bottom: 1.25rem;
  box-shadow: inset 0 2px 7px var(--sunk);
}

/* The deck and the control that sits on it share one box, so the overlay can be placed in
   percentages of the drawing and stay on the tape window at every width. The SVG's own aspect
   ratio does the work; there is no measurement in JavaScript and nothing to keep in step. */
.deck-wrap {
  position: relative;
  width: 100%;
  max-width: 24rem;
  margin: 0 auto;
}

.deck { display: block; width: 100%; height: auto; }

/* The reel, as a control. Its box is the tape window: 176×68 of the 320×208 drawing, which is
   over 44px in both directions at the 320px floor. `touch-action: none` is what makes a
   rotational drag possible on glass at all — without it the first vertical movement is taken by
   the page scroller and the gesture never arrives. */
.reel-control {
  position: absolute;
  left: 22.5%;
  top: 50.96%;
  width: 55%;
  height: 32.7%;
  border-radius: 8px;
  background: transparent;
  cursor: grab;
  touch-action: none;
}

.reel-control:active { cursor: grabbing; }
.reel-control[aria-disabled="true"] { cursor: default; }

/* A control with no ink of its own still has to say it is a control. The hint is drawn only when
   there is something to turn, and only on hover or focus, so an empty deck is not covered in
   affordances for something it cannot do. */
/* A hairline inside the window whenever there is tape to wind, and nothing at all when there is
   not. Hover cannot carry this on its own — a phone has none — and a control with no visible
   edge is a control nobody discovers. It is drawn INSIDE the box so it cannot be mistaken for
   the focus ring, which is a 3px ink outline drawn outside it. */
.reel-control:not([aria-disabled="true"]) {
  box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--oxide) 55%, transparent);
}

@media (hover: hover) {
  .reel-control:not([aria-disabled="true"]):hover { box-shadow: inset 0 0 0 2px var(--oxide); }
}

.reel-control:focus-visible { outline: 3px solid var(--ink); outline-offset: 3px; }

/* The reels turn while the tape actually moves, and that is now three states rather than one:
   the model working, the recording playing, and the microphone running. Recording is the most
   literal of them — it is the one case where tape would really be going past a head.

   All three are driven by state the controller publishes anyway: `#status[data-tone]` for the
   model and `data-transport` on the bay for the transport. No animation loop, no rAF, nothing
   that can disagree with the truth. Where :has() is unsupported the first rule simply never
   matches and the others still do, because they need no :has() at all. The take-up hub runs
   faster because its pack is smaller.

   `--qt-turn` is the angle the visitor has wound the reel to by hand. It applies whenever no
   animation is running, which is every state except the three above — so a paused, stopped or
   scrubbed deck shows the reel at the position the counter is reading, and a running one is
   driven by the keyframes. */
.qt-reel {
  transform-box: fill-box;
  transform-origin: 50% 50%;
  transform: rotate(var(--qt-turn, 0deg));
}

body:has(#status[data-tone="working"]) .qt-reel,
.bay[data-transport="playing"] .qt-reel,
.bay[data-transport="recording"] .qt-reel { animation: qt-spin 3.4s linear infinite; }
body:has(#status[data-tone="working"]) .qt-reel--take,
.bay[data-transport="playing"] .qt-reel--take,
.bay[data-transport="recording"] .qt-reel--take { animation-duration: 2.1s; }

@keyframes qt-spin { to { transform: rotate(360deg); } }

/* The deck's record key lights when the microphone is actually live, and only then. */
.qt-rec-key { transition: fill 120ms linear; }
.bay[data-transport="recording"] .qt-rec-key { fill: var(--oxide-lamp); opacity: 1; }
.bay[data-transport="rec-paused"] .qt-rec-key { fill: var(--oxide-lamp); opacity: 0.5; }

/* ---- the tape is running --------------------------------------------------------------------
   The one state on this site that has to be readable from across a desk, without reading. A
   journalist puts the laptop down and talks to a person; they must be able to glance at it and
   know. So it is carried four times over: an oxide field behind the whole bay, a filled lamp,
   the word REC, and a counter at 2.1rem whose digits are visibly advancing. Remove any one and
   the state is still legible — which is also what makes it survive `prefers-reduced-motion`,
   where the lamp stops pulsing and the reels stop turning and nothing else changes. */

.bay[data-transport="recording"] {
  background: var(--rec-field);
  border-color: var(--rec-edge);
  box-shadow: inset 0 2px 7px var(--sunk), 0 0 0 2px var(--rec-edge);
}

.bay[data-transport="rec-paused"] { border-color: var(--steel); }

.rec-panel {
  display: flex;
  align-items: center;
  gap: 0.7rem;
  margin-top: 0.85rem;
  padding: 0.55rem 0.75rem;
  background: var(--panel);
  border: 1px solid var(--line);
  border-radius: 9px;
}

.rec-panel[hidden] { display: none; }

.rec-lamp {
  display: flex;
  align-items: center;
  gap: 0.45rem;
  flex: 0 0 auto;
  margin: 0;
  color: var(--oxide);
  font: 700 0.82rem/1 var(--font-panel);
  letter-spacing: 0.16em;
}

.rec-lamp::before {
  content: "";
  width: 14px;
  height: 14px;
  border-radius: 50%;
  background: var(--oxide-lamp);
  box-shadow: 0 0 0 3px var(--inset);
}

.bay[data-transport="recording"] .rec-lamp::before { animation: qt-lamp 1.4s ease-in-out infinite; }
.bay[data-transport="rec-paused"] .rec-lamp { color: var(--muted); }
.bay[data-transport="rec-paused"] .rec-lamp::before { background: var(--steel); }

/* The counter. Tabular figures so the digits do not jitter, and large enough to be the thing a
   glance lands on. It is the only place on this site the counter face is set above body size. */
.rec-clock {
  flex: 0 0 auto;
  margin: 0;
  color: var(--ink);
  font: 1.5rem/1 var(--font-counter);
  font-size: clamp(1.5rem, 6.5vw, 2.1rem);
  font-variant-numeric: tabular-nums;
  letter-spacing: -0.01em;
}

/* What the microphone is actually hearing. It is not decoration and it is not a progress bar:
   a muted input is the one recording failure that is invisible while it happens and total
   afterwards, and this is how it gets caught in the first ten seconds. The fill is a transform,
   so a value arriving every animation frame costs a composite and never a layout. The segments
   are a mask over it, drawn in CSS because the CSP refuses a data: URI. */
.rec-meter {
  flex: 1 1 auto;
  min-width: 3rem;
  height: 14px;
  border: 1px solid var(--line);
  border-radius: 4px;
  background: var(--inset);
  overflow: hidden;
}

.rec-meter-fill {
  display: block;
  width: 100%;
  height: 100%;
  transform: scaleX(var(--level, 0));
  transform-origin: left center;
  background:
    repeating-linear-gradient(90deg, var(--oxide) 0 4px, transparent 4px 6px);
}

/* ---- the counter strip -------------------------------------------------------------------- */

#status {
  display: flex;
  /* flex-start, not center: an error message runs to three lines on a 390px phone and a lamp
     centred against three lines floats in the middle of the sentence instead of marking where
     it starts. The margin puts it on the first line's optical centre. */
  align-items: flex-start;
  gap: 0.65rem;
  margin: 0.85rem 0 0;
  padding: 0.7rem 0.85rem;
  background: var(--panel);
  border: 1px solid var(--line);
  border-radius: 9px;
  color: var(--muted);
  font: 600 0.82rem/1.45 var(--font-panel);
  letter-spacing: 0.02em;
}

/* The lamp. State is carried three ways — this colour, the words, and whether the reels are
   moving — so removing any one of them leaves the state legible. */
#status::before {
  content: "";
  flex: 0 0 auto;
  width: 11px;
  height: 11px;
  margin-top: 0.25rem;
  border-radius: 50%;
  background: var(--steel);
  box-shadow: 0 0 0 2px var(--inset);
}

#status[data-tone="working"] { color: var(--ink); }
#status[data-tone="working"]::before {
  background: var(--oxide-lamp);
  animation: qt-lamp 1.7s ease-in-out infinite;
}
#status[data-tone="ready"] { color: var(--ink); }
#status[data-tone="ready"]::before { background: var(--teal); }
#status[data-tone="error"] { color: var(--error); }
#status[data-tone="error"]::before { background: var(--error); }
/* Recording gets its own tone rather than borrowing `working`: the two states mean opposite
   things to the person on the other side of the microphone, and the reels are already turning
   for both. The lamp is filled and ringed so it is the brightest thing in the strip. */
#status[data-tone="recording"] { color: var(--ink); }
#status[data-tone="recording"]::before {
  background: var(--oxide-lamp);
  box-shadow: 0 0 0 3px var(--rec-field);
  animation: qt-lamp 1.4s ease-in-out infinite;
}

/* Two tones that exist to keep the reels honest, and they were found by driving the page rather
   than by reading it. `working` turns the reels through the :has() rule above, and a PAUSED
   recording was announced with it — so the cassette span merrily while the tape was stopped,
   which is the exact dishonesty the motion rule exists to prevent. So does a "press again to
   delete" prompt, where nothing is happening at all.
     paused — nothing is moving, and the lamp says so in steel.
     asking — the machine is waiting for you, so the lamp is lit but still. */
#status[data-tone="paused"] { color: var(--ink); }
#status[data-tone="paused"]::before { background: var(--steel); }
#status[data-tone="asking"] { color: var(--ink); }
#status[data-tone="asking"]::before { background: var(--oxide-lamp); }

@keyframes qt-lamp { 50% { opacity: 0.35; } }

/* ---- the transport --------------------------------------------------------------------------
   Flat, labelled controls inside the machine's own inset panel. No embossed keys, no chrome, no
   seven-segment font: a tool used once under deadline pressure cannot afford an affordance that
   has to be decoded, and the art direction stops the metaphor exactly here. */

.transport {
  margin-top: 0.85rem;
  padding: 0.75rem 0.8rem 0.6rem;
  background: var(--panel);
  border: 1px solid var(--line);
  border-radius: 9px;
}

.transport-keys { display: flex; flex-wrap: wrap; gap: 0.5rem; }
.transport-keys button { flex: 1 1 auto; min-width: 6.5rem; padding: 0.7rem 0.9rem; }
#play { flex: 1 1 100%; background: var(--oxide); color: var(--paper); }

.transport-seek { margin-top: 0.45rem; }

.transport-clock {
  display: block;
  margin-top: 0.3rem;
  color: var(--ink);
  font: 0.85rem/1 var(--font-counter);
  font-variant-numeric: tabular-nums;
  text-align: right;
}

/* ---- the recording, drawn as sound ------------------------------------------------------------
   A recessed readout, on the inset ground and inside the hairline everything else the machine
   reports through. The wave is ruled rather than solid — a bar every 2px, 1px of ink — because
   ruled striation is already this drawing's vocabulary: the card's top rail and the level meter
   are both repeating gradients. A solid envelope would be a different material; a neon bar chart
   would be a costume. The rest of the reasoning is in `docs/art-direction.md`, fifth pass.

   THE WAVE IS THE RAIL. The transport had a plain range under the deck doing one job — a position
   in a length — and a drawing that answers that question and three more does not go BESIDE it.
   The input is still here, full size, over the canvases, invisible except for its thumb, and its
   thumb is the playhead. One element, one value, one thing to read and one thing to watch.

   Nothing here animates: the playhead moves because the transport does, exactly as the counter's
   digits do, so `prefers-reduced-motion` has nothing to switch off. The one piece of motion that
   WAS decorative — the wave drawing itself in while the peaks are computed — is dropped by the
   controller. */
.wave {
  --wave-h: 76px;
  position: relative;
  height: var(--wave-h);
  border: 1px solid var(--line);
  border-radius: 6px;
  background: var(--inset);
  box-shadow: inset 0 1px 4px var(--sunk);
  overflow: hidden;
  cursor: pointer;
  /* `pan-y`, not the reel's `none`: that gesture is rotational and has to take a vertical drag,
     this one is horizontal, so a swipe starting on the wave still scrolls the page. The
     controller also waits for 6px of sideways travel on a touch pointer before it moves
     anything, so a scroll that begins here does not first drag the playhead. */
  touch-action: pan-y;
}

.wave-ink { position: absolute; inset: 0; display: block; width: 100%; height: 100%; }

/* Played over unplayed, the way a tape's used and unused sides read: one envelope painted twice,
   oxide over steel, the oxide copy clipped at the playhead. `--played` is the percentage the
   counter already maintains, so the two cannot drift; clipping a painted canvas costs nothing per
   frame where repainting one would cost a full redraw sixty times a second. */
.wave-played { clip-path: inset(0 calc(100% - var(--played, 0%)) 0 0); }

/* `pointer-events: none` hands every press and drag to the wrapper. A native range's response to
   a press on its track is an engine decision, and the one thing this control cannot be is
   approximately the same in four browsers. Keyboard focus, arrows, Home and End stay native. */
#seek {
  -webkit-appearance: none;
  appearance: none;
  position: absolute;
  inset: 0;
  width: 100%;
  height: var(--wave-h);
  margin: 0;
  padding: 0;
  background: transparent;
  pointer-events: none;
}

/* Drawn inside the box: the wrapper clips its overflow, so an outline outside it is cut in half. */
#seek:focus-visible { outline: 3px solid var(--ink); outline-offset: -3px; }

/* The track carries nothing — the canvases under it are the track. The thumb is the playhead:
   3px of ink the full height of the box, ringed so it reads against both halves of the wave. */
#seek::-webkit-slider-runnable-track { height: var(--wave-h); border: none; background: none; }
#seek::-moz-range-track { height: var(--wave-h); border: none; background: none; }
#seek::-moz-range-progress { background: none; }
#seek::-webkit-slider-thumb {
  -webkit-appearance: none;
  appearance: none;
  width: 3px;
  height: var(--wave-h);
  margin-top: 0;
  border: none;
  border-radius: 0;
  background: var(--ink);
  box-shadow: 0 0 0 1px var(--paper);
}
#seek::-moz-range-thumb {
  width: 3px;
  height: var(--wave-h);
  border: none;
  border-radius: 0;
  background: var(--ink);
  box-shadow: 0 0 0 1px var(--paper);
}

.transport-rate { display: flex; align-items: center; gap: 0.6rem; margin-top: 0.25rem; }

.inline-label { display: inline-block; margin: 0; }

/* `appearance: none` is not a style preference here. WebKit ignores `min-height` on a native
   menulist entirely — measured at 18px against a 44px rule, in the engine every iPhone runs,
   which is a failed touch target on the whole Apple half of the matrix. Chromium and Firefox
   honour it, so nothing but a real WebKit run could have said so.

   The chevron is two linear gradients, not an image: this site's CSP is `default-src 'self'`,
   which does not match `data:` either, so a data-URI SVG arrow would be refused exactly as an
   off-origin one would. */
select {
  -webkit-appearance: none;
  appearance: none;
  min-height: 44px;
  padding: 0 2.1rem 0 0.7rem;
  border: 1px solid var(--steel);
  border-radius: 8px;
  background-color: var(--paper);
  background-image:
    linear-gradient(45deg, transparent 50%, var(--oxide) 50%),
    linear-gradient(135deg, var(--oxide) 50%, transparent 50%);
  background-position: right 1.15rem center, right 0.8rem center;
  background-size: 6px 6px;
  background-repeat: no-repeat;
  color: var(--ink);
  font: 0.9rem var(--font-panel);
}

#transport-note { margin-top: 0.7rem; }

/* ---- controls ----------------------------------------------------------------------------- */

label {
  display: block;
  margin-bottom: 0.5rem;
  color: var(--muted);
  font: 600 0.78rem/1.4 var(--font-panel);
  letter-spacing: 0.11em;
  text-transform: uppercase;
}

/* ---- the two ways in ------------------------------------------------------------------------
   Recording first, loading second, and a rule between them rather than a tab or a segmented
   control. A tab hides one half behind a press; on a page whose whole argument is that nothing
   is hidden, both doors stay open and the order is the recommendation.

   They stack on a phone and sit side by side once there is room for two readable columns. The
   record side keeps its explanation directly under its button, because the sentence about when
   the microphone is asked for is the most important sentence on this card. */

.sources { display: grid; gap: 0.4rem; }

.source + .source {
  margin-top: 0.9rem;
  padding-top: 1.1rem;
  border-top: 1px solid var(--line);
}

/* A heading that is machine furniture rather than paperwork: it labels a panel, so it is set in
   the panel face at panel size instead of in the serif the prose uses. */
h2.panel-label {
  margin: 0 0 0.65rem;
  padding: 0;
  border: 0;
  color: var(--muted);
  font: 600 0.78rem/1.4 var(--font-panel);
  letter-spacing: 0.11em;
  text-transform: uppercase;
}

#record { background: var(--oxide); color: var(--paper); }
/* Stop is the consequential press while the tape runs — it is what ends the take and hands the
   recording over — so it is the filled one. Pause and Discard stay ghosts. */
#stop-rec { flex: 1 1 6rem; background: var(--oxide); color: var(--paper); }
#mic-note { margin-top: 0.8rem; }

@media (min-width: 40rem) {
  .sources { grid-template-columns: 1fr 1fr; gap: 1.6rem; }
  .source + .source {
    margin-top: 0;
    padding-top: 0;
    padding-left: 1.6rem;
    border-top: 0;
    border-left: 1px solid var(--line);
  }
}

input[type="file"] {
  width: 100%;
  min-height: 3.2rem;
  padding: 0.6rem;
  /* --steel, not --line: a control's own boundary has to reach 3:1 to be a boundary. The
     hairline that separates two blocks of prose is not strong enough to be the edge of the
     one field on the page. */
  border: 2px dashed var(--steel);
  border-radius: 10px;
  background: var(--paper);
  color: var(--ink);
  font: 0.92rem var(--font-panel);
}

/* The native button inside the control is the thing a thumb actually lands on, so it is sized
   and coloured rather than left at whatever the platform draws — which on iOS is small, grey
   and easy to miss beside a 3rem dashed box. */
input[type="file"]::file-selector-button {
  min-height: 40px;
  margin-right: 0.8rem;
  padding: 0 0.9rem;
  border: 1px solid var(--oxide);
  border-radius: 7px;
  background: var(--panel);
  color: var(--oxide);
  cursor: pointer;
  font: 600 0.78rem var(--font-panel);
  letter-spacing: 0.07em;
  text-transform: uppercase;
}

.actions { display: flex; flex-wrap: wrap; gap: 0.7rem; margin-top: 1.1rem; }

button {
  min-height: 48px;
  padding: 0.8rem 1.3rem;
  border: 1px solid var(--oxide);
  border-radius: 9px;
  background: var(--oxide);
  color: var(--paper);
  cursor: pointer;
  font: 600 0.95rem/1.2 var(--font-panel);
}

button[type="button"] { background: var(--panel); color: var(--oxide); }

/* Hierarchy follows importance, not the type attribute. `#transcribe` is a `type="button"`
   because it must not submit the form, but it is the most consequential press on the site —
   it starts the model. It gets the filled treatment; "Start over" keeps the ghost. */
#transcribe { background: var(--oxide); color: var(--paper); }

button:disabled { opacity: 0.55; cursor: default; }

/* Never the only affordance: every button is already filled or outlined, bordered and 48px
   without it. Nothing on this site is discoverable by hover alone. color-mix keeps the darker
   press honest in both schemes; where it is unsupported the base colour simply stays. */
@media (hover: hover) {
  button:hover:not(:disabled) { background: color-mix(in srgb, var(--oxide) 84%, var(--ink)); }
  button[type="button"]:hover:not(:disabled) { background: var(--inset); }
  #transcribe:hover:not(:disabled) { background: color-mix(in srgb, var(--oxide) 84%, var(--ink)); }
}

/* ---- the readout -------------------------------------------------------------------------- */

#result dl {
  display: grid;
  /* Stacked by default. `MODEL WINDOWS` is a wide label and the source is a 25-character
     monospace string, so a two-column readout put the recording's name on three lines inside a
     390px card. The columns come back the moment there is width for them. */
  grid-template-columns: minmax(0, 1fr);
  gap: 0.1rem;
  margin: 0 0 1.2rem;
  padding: 0.85rem 1rem;
  background: var(--inset);
  border: 1px solid var(--line);
  border-radius: 10px;
  box-shadow: inset 0 1px 4px var(--sunk);
}

@media (min-width: 27rem) {
  #result dl { grid-template-columns: auto minmax(0, 1fr); gap: 0.45rem 1.1rem; }
}

dt {
  color: var(--muted);
  font: 600 0.78rem/1.6 var(--font-panel);
  letter-spacing: 0.11em;
  text-transform: uppercase;
}

dd {
  margin: 0;
  color: var(--ink);
  font: 1rem/1.45 var(--font-counter);
  font-variant-numeric: tabular-nums;
  /* A file somebody chose can be named anything at all, and this readout is inside a two-column
     grid on a 320px screen. `anywhere` is the only value that lets the track actually shrink. */
  overflow-wrap: anywhere;
}

/* The two settings that change what the next press produces: which language the model is told to
   expect, and which of the two models is asked. They sit above the Transcribe button rather than
   beside the transcript, because both are decisions about a run that has not happened yet.

   They stack on a phone and pair up once there is room, and the selects take the full width of
   their column: one of them holds ninety-nine options and a menu the width of the word "Detect"
   is a menu nobody can read the current value of. The 44px height and the drawn chevron come from
   the `select` rule above — WebKit ignores min-height on a native menulist, which is why that
   rule sets `appearance: none` for every select on the site rather than case by case. */
.run-options {
  display: grid;
  gap: 0.8rem;
  margin-top: 1.1rem;
}

.run-option { display: grid; gap: 0.35rem; }
.run-option select { width: 100%; }

@media (min-width: 34rem) {
  .run-options { grid-template-columns: 1fr 1fr; gap: 1.1rem; }
}

#run-note { margin-top: 0.75rem; }

/* The storage decision, printed beside the button that makes it, every time. It is not a
   dismissible hint and there is nothing to remember it: the note appears whenever the button
   does, because the button is only ever offered for a recording that is not yet kept. */
#keep-note { margin-top: 0.85rem; }

/* The transcript is paper coming out of the machine, and it is now paper somebody writes on:
   it scrolls inside its own box so a two-hour deposition cannot make the whole document slide,
   and `position: relative` is load-bearing rather than cosmetic — the follow-the-playhead scroll
   measures a line's offsetTop against THIS box, and without it the offset parent is the card. */
#transcript {
  position: relative;
  margin: 0 0 1.2rem;
  padding: 0.4rem;
  max-height: 26rem;
  overflow: auto;
  background: var(--paper);
  /* The 4px oxide rail this box used to carry on its left edge is gone. It read as "this is the
     transcript" when the transcript was one block of text; now every line has a rail of its own
     to say which one the playhead is on, and two oxide rails 4px apart said neither thing. */
  border: 1px solid var(--line);
  border-radius: 8px;
  color: var(--ink);
}

#transcript:empty, #downloads:empty { display: none; }

/* The two switches over the transcript. The input keeps its native box — enlarging a checkbox
   glyph only distorts it — and the LABEL is the 44px target, which is both the accessible
   pattern and the one the fleet's responsive gate measures. */
.lines-tools {
  display: flex;
  flex-wrap: wrap;
  gap: 0.2rem 1.2rem;
  margin: 0 0 0.6rem;
}

/* `display: flex` in a class rule beats the UA stylesheet's `[hidden] { display: none }`, so
   these two switches sat over an empty transcript from the moment a recording was loaded. Found
   by looking at the result panel, not by reading the controller, which sets `.hidden` correctly. */
.lines-tools[hidden] { display: none; }

.toggle {
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
  min-height: 44px;
  margin: 0;
  color: var(--muted);
  font: 0.94rem/1.3 var(--font-body);
  letter-spacing: 0;
  text-transform: none;
  cursor: pointer;
}

.toggle input { width: 20px; height: 20px; margin: 0; accent-color: var(--oxide); }

.lines { margin: 0; padding: 0; list-style: none; }

/* One line of the transcript: a time you can press, a speaker you can type, and words you can
   correct. Stacked on a phone — a 320px screen cannot hold a label column and a readable
   measure at once — and a two-column deposition layout once there is room for one. */
.line {
  display: grid;
  gap: 0.15rem 0.7rem;
  padding: 0.35rem 0.55rem 0.5rem;
  border-radius: 7px;
  border-left: 3px solid transparent;
}

.line + .line { margin-top: 0.1rem; }

.line-meta { display: flex; align-items: center; gap: 0.5rem; }

.line-time {
  flex: 0 0 auto;
  min-width: 44px;
  min-height: 44px;
  padding: 0 0.5rem;
  border: 1px solid var(--line);
  border-radius: 7px;
  background: transparent;
  color: var(--muted);
  font: 0.82rem/1 var(--font-counter);
  font-variant-numeric: tabular-nums;
}

.line-speaker {
  flex: 0 0 6rem;
  min-width: 0;
  min-height: 44px;
  padding: 0 0.5rem;
  border: 1px solid var(--line);
  border-radius: 7px;
  background: var(--panel);
  color: var(--ink);
  font: 600 0.84rem var(--font-panel);
}

/* An inherited label is shown as a placeholder rather than written into the field: the switch
   above can be turned off and what you actually typed comes back untouched. */
.line-speaker::placeholder { color: var(--muted); opacity: 1; }
.line-speaker.is-inherited::placeholder { font-style: italic; }

.line-text {
  min-height: 1.7em;
  padding: 0.15rem 0.1rem;
  border-radius: 5px;
  font: 1rem/1.6 var(--font-body);
  overflow-wrap: break-word;
}

.line-text:focus-visible { outline: 3px solid var(--ink); outline-offset: 2px; }

/* The line under the playhead. Marked three ways — a rail, a ground and a weight — because a
   ground tint alone is a colour-only signal, and this list is read while audio is running. */
.line.is-playing {
  border-left-color: var(--oxide);
  background: var(--panel);
}
.line.is-playing .line-text { font-weight: 600; }
.line.is-playing .line-time { color: var(--oxide); border-color: var(--oxide); }

/* Where there is room, the label column moves beside the words instead of above them: a
   deposition reads as Q and A down the left margin, and a line stops costing two rows of height
   for two characters of label. Stacked below this width because a 320px screen cannot hold a
   label column and a readable measure at the same time. */
/* An explicit track, not `auto`: a text input's intrinsic width comes from its `size`, not from
   its flex-basis, and Firefox sized the label column off that and left a 120px hole between the
   speaker box and the words. A named width is also the thing a deposition wants — Q and A in a
   fixed left margin all the way down the page. */
@media (min-width: 30rem) {
  .line { grid-template-columns: 10rem minmax(0, 1fr); align-items: start; }
  .line-meta { padding-top: 0.1rem; }
  .line-speaker { flex: 1 1 auto; }
}

#downloads { margin: 0 0 1.2rem; }

/* Tape labels. Seven of them now — six formats and a copy — so they are chips rather than
   sentences: `.srt`, `.pdf`, `.docx`. The extension IS the label a visitor is choosing between,
   and the full name travels in the accessible name and the tooltip rather than in the width. */
#downloads a,
#downloads button.export {
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
  min-height: 44px;
  padding: 0 0.75rem;
  border: 1px solid var(--oxide);
  border-radius: 8px;
  background: var(--panel);
  color: var(--oxide);
  text-decoration: none;
  font: 600 0.78rem var(--font-counter);
  letter-spacing: 0.02em;
  text-transform: none;
}

/* What pressing it costs, once, before it is pressed. Only the two formats that fetch a
   document library carry one, and the number comes from the registry rather than this file. */
.export-cost {
  /* Ruled off from the extension rather than merely spaced: in a monospace chip `.pdf 420 KB`
     reads as one token, and the number is a different kind of fact from the name. */
  padding-left: 0.45rem;
  border-left: 1px solid var(--line);
  color: var(--muted);
  font: 600 0.68rem var(--font-panel);
  letter-spacing: 0.04em;
}

#downloads button.export:disabled { opacity: 0.55; cursor: default; }

/* Where a refused clipboard puts the transcript so it is not lost. Hidden until it is needed,
   and read-only: this is a copy of what is above it, not a second place to edit. */
#copied {
  display: block;
  width: 100%;
  margin: 0 0 1.2rem;
  padding: 0.75rem;
  background: var(--paper);
  border: 1px solid var(--steel);
  border-radius: 8px;
  color: var(--ink);
  font: 0.88rem/1.6 var(--font-counter);
  resize: vertical;
}

#copied[hidden] { display: none; }

/* ---- the shelf -------------------------------------------------------------------------------
   A card, not a drawer. What is sitting on a machine other people can use is a fact its owner
   should be able to see without opening anything, and the empty state is the most important one
   on the page: "Nothing is kept on this device" is the only proof a visitor gets that the tool
   did not quietly keep the last thing they did. */

#shelf[hidden] { display: none; }

.tapes { margin: 0; padding: 0; list-style: none; }

.tape {
  display: grid;
  gap: 0.5rem;
  padding: 0.8rem 0;
}

.tape + .tape { border-top: 1px solid var(--line); }

.tape-name {
  margin: 0 0 0.2rem;
  color: var(--ink);
  font: 600 0.95rem/1.35 var(--font-panel);
  overflow-wrap: anywhere;
}

/* Date, length, size. Counter face, because all three are quantities a visitor compares between
   rows rather than reads as a sentence. */
.tape-facts {
  display: flex;
  flex-wrap: wrap;
  gap: 0.1rem 0.9rem;
  margin: 0;
  color: var(--muted);
  font: 0.8rem/1.5 var(--font-counter);
  font-variant-numeric: tabular-nums;
}

.tape-actions { display: flex; flex-wrap: wrap; gap: 0.5rem; }

.tape-actions .tape-action {
  flex: 1 1 auto;
  min-width: 5rem;
  min-height: 44px;
  padding: 0 0.75rem;
  background: var(--panel);
  color: var(--oxide);
  font: 600 0.82rem/1.2 var(--font-panel);
}

/* Deleting is the one press on this site that destroys the only copy of something. It is drawn
   as what it is — outlined in the error colour rather than in oxide — and it takes two presses,
   the second of which the button asks for in words. */
.tape-action.tape-delete { flex: 0 1 auto; border-color: var(--error); color: var(--error); }
.tape-action[data-armed="yes"] {
  flex: 1 1 100%;
  background: var(--error);
  border-color: var(--error);
  color: var(--paper);
}

#clear-all[data-armed="yes"], #discard-rec[data-armed="yes"] {
  background: var(--error);
  border-color: var(--error);
  color: var(--paper);
}

.shelf-foot {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 0.7rem;
  margin-top: 0.9rem;
  padding-top: 0.9rem;
  border-top: 2px solid var(--line);
}

.shelf-foot[hidden] { display: none; }

.shelf-usage {
  flex: 1 1 12rem;
  margin: 0;
  color: var(--muted);
  font: 0.85rem/1.4 var(--font-counter);
  font-variant-numeric: tabular-nums;
}

#clear-all { border-color: var(--error); color: var(--error); background: var(--panel); }

@media (hover: hover) {
  .tape-action.tape-delete:hover:not(:disabled),
  #clear-all:hover:not(:disabled) { background: var(--inset); }
  .tape-action[data-armed="yes"]:hover,
  #clear-all[data-armed="yes"]:hover,
  #discard-rec[data-armed="yes"]:hover { background: color-mix(in srgb, var(--error) 84%, var(--ink)); }
}

@media (min-width: 34rem) {
  .tape { grid-template-columns: minmax(0, 1fr) auto; align-items: center; gap: 0.5rem 1rem; }
  .tape-actions { justify-content: flex-end; }
  .tape-actions .tape-action { flex: 0 0 auto; }
}

/* ---- the plain statement of capability ------------------------------------------------------
   Prose lists, set as paperwork rather than as panel type: this is the section a lawyer reads
   before deciding whether the tool is usable for the matter in front of them. */

main ul { margin: 0 0 1rem; padding-left: 1.25rem; }
main li { margin-bottom: 0.5rem; }
main li:last-child { margin-bottom: 0; }

/* What the browser said it can play. The verdict is written by the page at load time from
   HTMLAudioElement.canPlayType, so this list is a measurement rather than a claim — which is
   why the states are three and not two. Read as rows on a phone and as a two-column spec sheet
   where there is room. */
.formats {
  margin: 0 0 1rem;
  padding: 0.35rem 0.9rem;
  list-style: none;
  background: var(--panel);
  border: 1px solid var(--line);
  border-radius: 10px;
}

.formats li {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 0.15rem 0.7rem;
  margin: 0;
  padding: 0.55rem 0;
}

.formats li + li { border-top: 1px solid var(--line); }

.formats-name { flex: 1 1 9rem; font-weight: 600; }

.formats-verdict {
  flex: 1 1 auto;
  color: var(--muted);
  font: 0.82rem/1.4 var(--font-panel);
  letter-spacing: 0.02em;
}

/* State is carried by the word first; the colour is a second channel, never the only one. */
.formats li[data-support="yes"] .formats-verdict { color: var(--teal); }
.formats li[data-support="no"] .formats-verdict { color: var(--error); }

/* ---- the advertisement ------------------------------------------------------------------------
   One box, at the very bottom of the front page, after every part of the page anybody came here
   to use. It is not on the legal pages, not on the error page, and never inside or beside the
   machine: an advertisement next to a deposition transcript is misclick bait on the one screen
   this site cannot afford to make ambiguous.

   THE HEIGHTS ARE NOT CHOSEN HERE. They are `reservedBlockSizePx("D", …)` from
   packages/ad-slot/src/states.js — 280px on a phone, 106px from 768px up — and test/site.test.js
   compares all three against that function rather than trusting this comment. A phone gets a
   rectangle, a laptop gets a band, and both are shapes AdSense actually fills.

   `block-size` is fixed, never `min-height` and never `auto`. `min-height` is a floor rather than
   a size, and a 250px creative walks straight through it; `contain: size` makes the box's layout
   size causally independent of its child, so nothing Google returns can grow it, and
   `overflow: clip` keeps an oversized one from widening the document. Every state is the same
   height, so no transition between them moves a single line of the page. */
:root { --adslot-h-D: 280px; }
@media (min-width: 768px) { :root { --adslot-h-D: 106px; } }
@media (min-width: 1280px) { :root { --adslot-h-D: 106px; } }

.sf-adslot {
  display: grid;
  place-items: center;
  /* The containing block for the creative below, which is taken out of flow precisely so that
     arriving cannot resize anything. `contain: layout` alone would make this element that
     containing block; `position: relative` is written anyway, because relying on a side effect
     of a containment keyword is how a rule survives until somebody tunes the containment. */
  position: relative;
  inline-size: 100%;
  block-size: var(--sf-adslot-h);
  overflow: clip;
  overflow-clip-margin: 0px;
  contain: layout size paint;
  margin: 3.25rem 0 0;
  padding: 0 1rem;
  background: var(--inset);
  border: 1px solid var(--line);
  border-radius: 10px;
  color: var(--muted);
  text-align: center;
}

/* Panel type, like every other label on this site that names a mechanism rather than speaking
   prose. It says what the box is before anything is in it, which is the labelling AdSense's own
   policy asks for and the wording a reader is owed. */
.sf-adslot-note {
  margin: 0;
  font: 600 0.76rem/1.5 var(--font-panel);
  letter-spacing: 0.1em;
  text-transform: uppercase;
}

/* Once a creative is in the box the house line steps aside. The box does not move. */
.sf-adslot[data-sf-adslot-state="filled"] .sf-adslot-note { display: none; }

/* A box with no ad unit renders NOTHING — no border, no caption, no reserved height.

   AdSense mints unit ids only after it approves a site, and approval takes days to weeks. Holding
   280px of bordered emptiness captioned ADVERTISEMENT for every visitor across that window is a
   hole in the page: it looks broken to a reader and it is what the reviewer sees too. The
   zero-CLS argument does not apply to it — layout shift is a creative landing during a page view,
   and no creative can land in a box with no unit id, so there is no shift here to prevent.
   Reserving space for an advertisement that cannot arrive buys nothing and costs the page.

   The reserved height comes back with the unit id, in the same deploy — which is a new page load
   rather than a shift. */
.sf-adslot[data-ad-unit=""] { display: none; }

/* THE CREATIVE IS OUT OF FLOW, and that is the second half of the zero-shift story.
   `contain: layout size paint` stops the box from moving the PAGE. It does not stop things
   moving INSIDE the box, and the layout-shift API scores those the same as any other. Left in
   flow, the `<ins>` joins the grid and re-sizes the track it shares with the house line, which
   re-wraps and jumps: measured in Chromium at 0.0086, small but real and paid by exactly the
   visitors who scrolled far enough to see an advertisement. Absolutely positioned, it cannot
   affect the line at all — it simply covers it, and then the line is hidden. */
.sf-adslot .adsbygoogle {
  position: absolute;
  inset: 0;
  inline-size: 100%;
  block-size: 100%;
}

/* ---- the spec plate ----------------------------------------------------------------------- */

footer {
  display: flex;
  flex-wrap: wrap;
  margin-top: 3.25rem;
  padding-top: 0.6rem;
  border-top: 2px solid var(--line);
}

/* A footer link is a navigation target, not prose, so it must be tappable. Inline-block with no
   height renders about 24px tall, which the fleet's responsive gate fails on every phone in the
   matrix: a thumb that misses a legal link is a legal link the visitor does not read. */
footer a {
  display: inline-flex;
  align-items: center;
  min-height: 44px;
  padding: 0 0.5rem;
  color: var(--muted);
  font: 600 0.76rem/1 var(--font-panel);
  letter-spacing: 0.1em;
  text-transform: uppercase;
  text-decoration: underline;
  text-decoration-color: var(--line);
  text-underline-offset: 0.5em;
}

/* Optical alignment with the body text above, without pushing the focus ring off the screen
   edge: the ring is drawn 3px outside the box and the row already starts inside the page's
   own 1.25rem gutter. */
footer a:first-child { margin-left: -0.5rem; }

@media (hover: hover) {
  footer a:hover { color: var(--oxide); text-decoration-color: currentColor; }
}

/* ---- the first screen on a phone -------------------------------------------------------------
   A fold rule, not a taste one, and it lives at the END of this file on purpose: an identical
   selector later in the source wins whatever the media query says, and the first version of this
   block sat above `.lede`, `.card` and `h1::after` and did almost nothing. Measured, not assumed
   — the values below did not move a pixel until the block moved.

   What it is for: on an iPhone 17 in real Mobile Safari the visible viewport is about 745px, and
   the Record button's box ended at exactly 745 with half of it under the browser's own floating
   toolbar. The primary action of a tool has to be whole on the first screen. Every value here is
   a few pixels off furniture; the one type change drops the lede to body size rather than losing
   a clause, because that paragraph is what tells four professions the tool is for them. */
@media (max-width: 40rem) {
  .site-header { padding: 0.7rem 0 0.3rem; }
  h1::after { margin: 0.6rem 0 0.75rem; }
  .lede { font-size: 1.04rem; }
  .card { padding: 1.25rem 1rem 1.1rem; }
  .bay { padding: 0.65rem; margin-bottom: 1rem; }
  h2.panel-label { margin-bottom: 0.5rem; }
  /* The wave is shorter on a phone for the same reason everything else in this block is: the
     Record button has to stay clear of Safari's own toolbar. 58px still reads as an envelope and
     is still a 44px-plus target, and it is the whole width of the card rather than a rail sharing
     a row with a counter. Declared here, at the end of the file, because a media query adds no
     specificity and `.wave` is defined later than this block used to sit (QT-R46). */
  .wave { --wave-h: 58px; }
}

/* ---- honour the setting -------------------------------------------------------------------- */

/* `*` alone does NOT match a pseudo-element, and this site now animates one: the status lamp
   pulses through #status::before. Measured in Chromium with reducedMotion=reduce — the reels
   stopped and the lamp kept breathing, which is the exact promise this rule exists to keep.
   The pseudo-element selectors are the fix, not decoration. */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after { animation: none !important; transition: none !important; }
}

/* A transcript gets printed and marked up against the recording, which is most of the point.
   The machine does not print: the bay, the buttons and the navigation come off, and the
   transcript keeps its whole length instead of its 26rem window. */
@media print {
  body { background: #fff; color: #000; padding: 0; font-size: 11pt; }
  body::before { display: none; }
  .bay, .actions, #status, footer, #downloads, .brand, .lines-tools,
  .sources, #shelf, #keep-note, .sf-adslot { display: none; }
  .card { border: none; background: none; box-shadow: none; padding: 0; }
  #intake::before, #result::before { display: none; }
  #transcript { max-height: none; overflow: visible; border: none; padding: 0; }
  /* A printed transcript is a document, not a screen: the seek buttons and the empty speaker
     boxes come off, and a label the visitor typed prints as the text it is. */
  .line { padding: 0; border-left: none; }
  .line.is-playing { background: none; }
  .line.is-playing .line-text { font-weight: 400; }
  .line-time { border: none; padding: 0; color: #444; }
  .line-speaker { border: none; background: none; padding: 0; min-height: 0; }
  .line-text { font-size: 11pt; }
}
