/* omi — paper mode.
 *
 * The e-ink sheet. A complete stylesheet, not a layer of overrides on admin.css:
 * the two are alternatives, chosen server-side from the `omi_theme` cookie, and
 * only one of them is ever linked. Overriding a dark theme into a paper one
 * means every rule in the other file is a rule this one has to remember to
 * undo, which is exactly how a theme ends up almost right in six places.
 *
 * The vocabulary is printed matter — ink on paper, hairline rules, crop marks,
 * engraved readouts, one rubber stamp — and it comes from a design kit whose
 * rules are worth restating, because most of them are the opposite of a normal
 * web stylesheet's instincts:
 *
 *   1. Two colours. `--pm-paper` and `--pm-ink`. `--pm-stamp` is a third and is
 *      spent once per sheet — here, on recording and on nothing else, which is
 *      the same rule firmware/src/theme.h keeps for pink-red.
 *   2. Hairlines separate; whitespace never does. Adjacent regions share a 1px
 *      ink rule. Two things that are only spaced apart belong to one region.
 *   3. No shadows, no radii, no gradients — except a gradient that is literally
 *      a dither pattern or a tick ladder.
 *   4. Selection is inked, not tinted: a chosen thing fills solid and flips its
 *      label to paper. There is no third state and no tint.
 *   5. Type is either ~10px mono with 0.2em tracking, or the display face at
 *      1.6rem and up. Nothing in between. Readouts are display; labels are
 *      tracked mono caps.
 *
 * Two deliberate departures from the kit, both because this is a pointer-driven
 * application and not an artboard:
 *
 *   - **Hover inks.** The kit says hover does nothing. On a screen with a mouse
 *     that reads as a dead interface, so a hoverable row or button fills solid —
 *     borrowing the kit's own selection style rather than inventing a hover
 *     colour, which keeps the two-colour rule intact. It is inside
 *     `@media (hover: hover)`, so a touchscreen and an e-paper tablet never see
 *     it at all.
 *   - **The label floor is ~10px, not 9.** 9px is right on a 1404x1872 panel
 *     viewed at arm's length. At browser zoom on a laptop it is a squint.
 *
 * The two paper variants differ in exactly one thing: the ground. `paper` is
 * pure white and near-black, which an e-paper panel renders with no dithering
 * at all — every tone on the sheet is either paper, ink, or a real dither
 * pattern, so nothing turns to noise. `paper-warm` is the printed stock the kit
 * ships; it is the better-looking of the two on a monitor and the worse of the
 * two on a panel, where the background itself becomes a grey dither field.
 *
 * **No web fonts, and one typeface.** The CSP is `default-src 'none'` with no
 * `font-src`, so a self-hosted face costs a deliberate policy edit on both
 * surfaces and a binary in the repo. Departure Mono was fitted and taken back
 * off; the sheet is IBM Plex Mono throughout, falling through to whatever
 * grotesque mono the machine has, and the didone that used to carry headings is
 * gone too. One face is not a shortage here — a sheet of tracked mono caps and
 * ruled hairlines is what the instrument this thing controls looks like.
 */

:root {
  color-scheme: light;

  --pm-paper:      #ffffff;
  --pm-paper-2:    #f4f4f2;   /* a well: inputs, prose, the panel ground */
  --pm-ink:        #111111;
  --pm-ink-soft:   rgba(17, 17, 17, 0.42);   /* soft hairline, never text */
  --pm-ink-mid:    rgba(17, 17, 17, 0.56);   /* link underlines, partial fills */
  --pm-ink-mute:   #5b5b58;   /* secondary text; the one grey that is allowed */
  --pm-stamp:      #b4442e;   /* recording, and nothing else */

  --pm-hair:       1px solid var(--pm-ink);
  --pm-hair-soft:  1px solid var(--pm-ink-soft);
  --pm-hair-mid:   1px solid var(--pm-ink-mid);

  --pm-mono: "IBM Plex Mono", ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, "DejaVu Sans Mono", monospace;

  --pm-label: .64rem;         /* tracked mono caps: nav, buttons, labels */
  --pm-body: .78rem;          /* 12.5px mono, the kit's body size */
  --pm-track: .2em;

  /* uiScreen: the rule under the masthead takes the colour of what the screen
   * is doing. There is only one thing it can be, and that is recording. */
  --pm-mode: var(--pm-ink);
}

:root[data-theme="paper-warm"] {
  --pm-paper:    #d6d3ca;
  --pm-paper-2:  #cbc8bf;
  --pm-ink:      #2f2f31;
  --pm-ink-soft: rgba(47, 47, 49, 0.34);
  --pm-ink-mid:  rgba(47, 47, 49, 0.40);
  --pm-ink-mute: #5f5d59;
}

/* The warm sheet inked the other way round. It is the *paper* layout on a dark
 * ground — crop marks, hairlines, tracked caps, the contour — and not the
 * Cardputer palette, which is what `dark` (admin.css) is.
 *
 * Two things are not a straight inversion, and both are the reason this is a
 * hand-made palette rather than a filter:
 *
 *   1. **The alphas come down.** A light hairline on a dark ground reads about a
 *      third heavier than the same hairline reversed — the eye spreads a bright
 *      line into the dark around it (irradiation), and it does not spread a dark
 *      one into light. Held at the light theme's .34/.40 the rules stopped being
 *      hairlines and started being borders. They are .22/.34 here.
 *   2. **The stamp lightens.** `#b4442e` has almost no contrast against
 *      `#1b1a18` — it is a dark red on a near-black, which on the one element
 *      allowed to shout is exactly wrong. `#d97b5d` is the same hue carried up
 *      to where it can be read.
 *
 * `color-scheme` matters as much as the tokens: without it the browser paints
 * form controls, scrollbars and autofill for a light page, and a dark sheet gets
 * a white scrollbar down the side of it. */
:root[data-theme="paper-night"] {
  color-scheme: dark;

  --pm-paper:    #1b1a18;
  --pm-paper-2:  #100f0e;
  --pm-ink:      #d8d2c3;
  --pm-ink-soft: rgba(216, 210, 195, 0.22);
  --pm-ink-mid:  rgba(216, 210, 195, 0.34);
  --pm-ink-mute: #a19d91;
  --pm-stamp:    #d97b5d;
}

[data-accent="rec"] { --pm-mode: var(--pm-stamp); }

* { box-sizing: border-box; }

/* Must beat any `display:` rule below, or a pane the recorder put away renders. */
[hidden] { display: none !important; }

html { -webkit-text-size-adjust: 100%; }

body {
  margin: 0;
  min-height: 100dvh;
  display: flex;
  flex-direction: column;
  background: var(--pm-paper);
  color: var(--pm-ink);
  font: var(--pm-body)/1.8 var(--pm-mono);
  font-weight: 400;
}

/* A link underline is `--pm-ink-mid`, not `--pm-ink-soft`: it has to read as a
   deliberate mark on a word, where a structural hairline has to disappear into
   the page. Same reason the palette names them separately. */
a { color: var(--pm-ink); text-decoration: none; border-bottom: var(--pm-hair-mid); }
a:hover { border-bottom-color: var(--pm-ink); }

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

::selection { background: var(--pm-ink); color: var(--pm-paper); }

/* ── masthead ─────────────────────────────────────────────────────────────
 * A band of hairline-divided cells. `.brand` is the inked key cell — the one
 * that says whose sheet this is. */

.topbar {
  position: sticky;
  top: 0;
  z-index: 20;
  flex: none;
  display: flex;
  align-items: stretch;
  background: var(--pm-paper);
  border-bottom: 2px solid var(--pm-mode);
}

.topbar > * { border-right: var(--pm-hair); }

.brand {
  display: inline-flex;
  align-items: center;
  padding: 0 1rem;
  background: var(--pm-ink);
  color: var(--pm-paper);
  border-bottom: 0;
  font-size: var(--pm-label);
  font-weight: 500;
  letter-spacing: .34em;
  text-transform: uppercase;
}
.brand:hover { border-bottom: 0; }

.topbar nav {
  display: flex;
  flex: 1;
  min-width: 0;
  border-right: 0;
}

.topbar nav a {
  display: inline-flex;
  align-items: center;
  padding: .75rem 1rem;
  border: 0;
  border-right: var(--pm-hair);
  font-size: var(--pm-label);
  letter-spacing: var(--pm-track);
  text-transform: uppercase;
  color: var(--pm-ink);
  white-space: nowrap;
}
/* Rule 4: a chosen thing fills solid and flips its label to paper. */
.topbar nav a.on { background: var(--pm-ink); color: var(--pm-paper); }

/* A plain ruled spacer, closing the nav band against the width the cells do not
   want. It was the kit's `.pm-bar__fill` — the same spacer with a tick texture
   printed across it — and the texture was the wrong call: in the kit that fill is
   a short gap between many small cells, here it was 450px of dots carrying no
   information at all. The rule that ends the band is the part that was doing the
   work. `nav` already carries `flex: 1`, so this is one pseudo-element and no
   markup. */
.topbar nav::after {
  content: "";
  flex: 1;
  min-width: 1.5rem;
  border-right: var(--pm-hair);
}

.status {
  display: flex;
  align-items: center;
  gap: 1rem;
  padding: 0 1rem;
  font-size: var(--pm-label);
  letter-spacing: .12em;
  text-transform: uppercase;
  white-space: nowrap;
}
.status b { font-weight: 500; }
/* No amber, no second accent: work in progress is inked, like a selection. */
.status .warnish { background: var(--pm-ink); color: var(--pm-paper); padding: .1rem .45rem; }

.topbar form.inline { display: flex; margin: 0; width: auto; }
.topbar .linkish {
  border: 0;
  margin: 0;
  padding: 0 1rem;
  height: 100%;
  background: transparent;
}

main {
  flex: 1;
  width: 100%;
  max-width: 68rem;
  margin: 0 auto;
  padding: 1.75rem 1.5rem 2.5rem;
  min-height: 0;
}

/* ── the bottom rule ──────────────────────────────────────────────────────
 * `.pm-bar--foot`: the same band with its rule on the top edge. It is the last
 * child of a flex-column body, so on a short page it sits at the foot of the
 * viewport and on a long one at the foot of the document — a printed sheet
 * either way, never a floating overlay. */

.hintbar {
  flex: none;
  background: var(--pm-paper);
  border-top: var(--pm-hair);
  padding: .55rem 1rem;
  font-size: var(--pm-label);
  letter-spacing: .12em;
  text-transform: uppercase;
  color: var(--pm-ink);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.hintbar kbd {
  font: inherit;
  border: 0;
  background: none;
  padding: 0;
  text-transform: uppercase;
}

/* Only the recorder divides its bottom rule into cells — it has a readout to
   put in the right-hand one. Everywhere else the bar is a single legend, and
   ruling every `<kbd>` in it would chop the sentence into confetti. */
body.sheet-page .hintbar { display: flex; align-items: stretch; padding: 0; }
body.sheet-page .hintbar > * { display: block; padding: .55rem 1rem; border-right: var(--pm-hair); }
body.sheet-page .hintbar > :last-child { border-right: 0; }

/* A ruled spacer that eats the remaining width. */
/* The ladder's weight lives in `--pm-ink-soft`, not in an `opacity` on the
   element. Same picture, but the fade then travels with the theme — on a dark
   sheet a ruled ground at the light theme's strength reads as a solid band. */
.foot-fill {
  flex: 1;
  padding: 0;
  background-image: repeating-linear-gradient(90deg, var(--pm-ink-soft) 0 1px, transparent 1px 7px);
}

/* The clock. Bottom-right corner, set in the title face — a readout on the sheet
 * rather than a headline over the panel. */
.foot-clock {
  font-family: var(--pm-mono);
  font-weight: 500;
  font-size: 1.5rem;
  line-height: 1;
  letter-spacing: 0;
  padding: .25rem 1rem;
  display: inline-flex;
  align-items: center;
  font-variant-numeric: tabular-nums;
}

/* ── type ─────────────────────────────────────────────────────────────────
 * Two sizes and nothing between them: tracked mono caps, or the display face. */

/* Every page opens the way the recorder's panes do: a name, then a rule. The
 * rule is what makes a heading the top of a *region* rather than a line of large
 * text floating above unrelated content — the same reason a pane summary has
 * one. `main > h1` only, so a card that is its own sheet (login, error) keeps
 * its heading tight. */
/* Titles are the recorder's own label treatment, scaled up: 500-weight mono
 * caps on wide tracking, which is exactly what `drawFrame()` paints `IDLE` and
 * `REC` with on the canvas (`500 <px> <mono>`, tracked 0.18em). One face on the
 * sheet, and the screen you spend the most time looking at sets it.
 *
 * The size and the tracking are the whole hierarchy, so they have to be
 * deliberate: a field label is .64rem at .2em, a panel title is .88rem at .24em,
 * a page title is 1.3rem at .3em. Wider tracking as things get larger is a
 * printed-matter convention and it is what keeps three ranks of tracked caps
 * from collapsing into one. */
h1 {
  font: 500 1.3rem/1.3 var(--pm-mono);
  letter-spacing: .3em;
  text-transform: uppercase;
  margin: 0 0 1.25rem;
}
main > h1 {
  padding-bottom: .9rem;
  border-bottom: var(--pm-hair);
  margin-bottom: 1.5rem;
}
main > h1 + .lede { margin-top: -.9rem; }

h2 {
  font: 500 var(--pm-label)/1 var(--pm-mono);
  letter-spacing: var(--pm-track);
  text-transform: uppercase;
  margin: 2.25rem 0 .7rem;
}
/* A panel's heading is set in the **display face**, and that is the fix for a
 * problem three earlier attempts were all downstream of.
 *
 * Kit rule 5: type is either ~10px tracked mono caps or the display face at
 * 1.6rem and up, nothing between — labels are mono, headings and readouts are
 * display. A panel title was mono caps, which by that rule made it a *label*,
 * and it sat directly above `CURRENT PASSWORD`, which is also a label, in the
 * same face at the same size in the same case. No amount of fill fixes two
 * things that are typographically the same thing: a grey band was muddy, a full
 * inked band turned a page of short panels into a zebra, and an inked tab was
 * still a mono label wearing a box. Setting it in the display face makes it a
 * heading, and then it needs no fill at all.
 *
 * It also gives the page a hierarchy it did not have: `h1` at 2.2rem, a panel at
 * 1.5rem, labels at .64rem — the same three steps the share document has, which
 * is the page in this UI that has always read best.
 *
 * The rule runs **full bleed to the panel's edges** while the text keeps the
 * panel's own inset — kit rule 6. The negative side margins have to match
 * `.panel`'s padding exactly. */
.panel > h2:first-child,
.panel > .panel-head:first-child {
  margin: 0 -1.25rem 1.35rem;
  padding: 0 1.25rem .65rem;
  border-bottom: var(--pm-hair);
}
.panel > h2:first-child,
.panel > .panel-head:first-child > h2 {
  font: 500 .88rem/1.3 var(--pm-mono);
  letter-spacing: .24em;
  text-transform: uppercase;
  color: var(--pm-ink);
}
/* A head with controls beside the title. `baseline` rather than `center`: the
   title is now twice the height of a button, and centring it left the button
   floating in the middle of a serif capital. */
.panel > .panel-head:first-child {
  align-items: baseline;
  gap: 1rem;
}
.panel > .panel-head:first-child > .acts { margin-left: auto; }
.panel h2 { margin: 0 0 1rem; }

p { margin: 0 0 .75rem; }
p:last-child { margin-bottom: 0; }

.muted { color: var(--pm-ink-mute); }
.small { font-size: .72rem; }
.mono { font-family: var(--pm-mono); }
.grow { flex: 1; min-width: 0; }
.lede { color: var(--pm-ink-mute); margin: -.65rem 0 1.5rem; max-width: 42rem; }

.crumb { font-size: var(--pm-label); letter-spacing: .12em; text-transform: uppercase; margin: 0 0 1rem; }

.wordmark {
  font: 500 var(--pm-label)/1 var(--pm-mono);
  letter-spacing: .4em;
  text-transform: uppercase;
  background: var(--pm-ink);
  color: var(--pm-paper);
  display: inline-block;
  padding: .5rem .9rem;
  margin: 0 0 1.25rem;
}

/* ── regions ──────────────────────────────────────────────────────────────
 * Rule 2: a region is bounded by a rule, not by air. */

.panel, .card {
  border: var(--pm-hair);
  padding: 1.1rem 1.25rem 1.25rem;
  margin: 0 0 1.5rem;
  background: var(--pm-paper);
}
.card.narrow { width: min(24rem, 100%); }

/* ── the plate: registration marks, reusable ──────────────────────────────
 *
 * The frame around the recorder panel, lifted out of the recorder. In the kit
 * it is `.pm-mount`, and it means one specific thing: **this is an object on
 * the sheet**, a physical thing the sheet is presenting, as opposed to a region
 * of the sheet itself. That is why it is not applied to every panel — a form is
 * part of the page, a recording is a thing on it.
 *
 * Built entirely from background layers rather than pseudo-elements, which is
 * what makes it reusable. Four corners need eight strokes, and an element has
 * only two pseudo-elements, so `.mount` needed a spare `<i class="crop">` child
 * to carry the bottom pair. Background layers need no markup at all, so this can
 * go on an element the template already has — which matters most on `/s`, where
 * the markup is public and shared with a second stylesheet.
 *
 * `--ruled` adds the tick ladders down both edges. Those are the recorder's, but
 * they are drawn there on the canvas by `drawFrame()`; here they are two
 * repeating gradients, in `--pm-ink-soft` so the crisp corner marks stay the
 * thing you read the frame by. Ladders are opt-in: on a small object they are
 * noise, and on a phone they are always noise.
 */

.plate,
.card,
body.doc > main > article {
  --plate-arm: 13px;      /* the length of one arm of a corner mark */
  --plate-in: 8px;        /* how far the marks sit inside the box */
  --plate-tick: 5px;      /* how far a rail tick reaches in */
  --plate-pitch: 9px;     /* rung spacing on the rails */
  --plate-rail-in: 30px;  /* rails stop short of the corner marks */
  position: relative;
  background-repeat: no-repeat;
  background-image:
    linear-gradient(var(--pm-ink), var(--pm-ink)), linear-gradient(var(--pm-ink), var(--pm-ink)),
    linear-gradient(var(--pm-ink), var(--pm-ink)), linear-gradient(var(--pm-ink), var(--pm-ink)),
    linear-gradient(var(--pm-ink), var(--pm-ink)), linear-gradient(var(--pm-ink), var(--pm-ink)),
    linear-gradient(var(--pm-ink), var(--pm-ink)), linear-gradient(var(--pm-ink), var(--pm-ink));
  background-size:
    var(--plate-arm) 1px, 1px var(--plate-arm),
    var(--plate-arm) 1px, 1px var(--plate-arm),
    var(--plate-arm) 1px, 1px var(--plate-arm),
    var(--plate-arm) 1px, 1px var(--plate-arm);
  background-position:
    left var(--plate-in) top var(--plate-in),
    left var(--plate-in) top var(--plate-in),
    right var(--plate-in) top var(--plate-in),
    right var(--plate-in) top var(--plate-in),
    left var(--plate-in) bottom var(--plate-in),
    left var(--plate-in) bottom var(--plate-in),
    right var(--plate-in) bottom var(--plate-in),
    right var(--plate-in) bottom var(--plate-in);
}

/* Same eight strokes, plus a bracket of ticks at each corner and the centre
   marks on the top and bottom edges. Written out rather than layered on top:
   `background-image` is a single property, so a second rule can only replace the
   list, never append to it.

   The brackets are **fixed length, not full height**. The recorder's ladders run
   the whole edge because the panel is roughly square and the edge is short; a
   document is several screens tall, and the same rule there stops being
   registration and becomes wallpaper — two dotted columns the length of the
   page. Measured on this summary: 970px of ladder against a 96px bracket. */
.plate--ruled,
body.doc > main > article {
  --plate-rail-len: 96px;
  background-image:
    repeating-linear-gradient(180deg, var(--pm-ink-soft) 0 1px, transparent 1px var(--plate-pitch)),
    repeating-linear-gradient(180deg, var(--pm-ink-soft) 0 1px, transparent 1px var(--plate-pitch)),
    repeating-linear-gradient(0deg, var(--pm-ink-soft) 0 1px, transparent 1px var(--plate-pitch)),
    repeating-linear-gradient(0deg, var(--pm-ink-soft) 0 1px, transparent 1px var(--plate-pitch)),
    linear-gradient(var(--pm-ink), var(--pm-ink)), linear-gradient(var(--pm-ink), var(--pm-ink)),
    linear-gradient(var(--pm-ink), var(--pm-ink)), linear-gradient(var(--pm-ink), var(--pm-ink)),
    linear-gradient(var(--pm-ink), var(--pm-ink)), linear-gradient(var(--pm-ink), var(--pm-ink)),
    linear-gradient(var(--pm-ink), var(--pm-ink)), linear-gradient(var(--pm-ink), var(--pm-ink)),
    linear-gradient(var(--pm-ink), var(--pm-ink)), linear-gradient(var(--pm-ink), var(--pm-ink));
  background-size:
    var(--plate-tick) var(--plate-rail-len),
    var(--plate-tick) var(--plate-rail-len),
    var(--plate-tick) var(--plate-rail-len),
    var(--plate-tick) var(--plate-rail-len),
    1px 7px, 1px 7px,
    var(--plate-arm) 1px, 1px var(--plate-arm),
    var(--plate-arm) 1px, 1px var(--plate-arm),
    var(--plate-arm) 1px, 1px var(--plate-arm),
    var(--plate-arm) 1px, 1px var(--plate-arm);
  background-position:
    left var(--plate-in) top var(--plate-rail-in),
    right var(--plate-in) top var(--plate-rail-in),
    left var(--plate-in) bottom var(--plate-rail-in),
    right var(--plate-in) bottom var(--plate-rail-in),
    center top var(--plate-in),
    center bottom var(--plate-in),
    left var(--plate-in) top var(--plate-in),
    left var(--plate-in) top var(--plate-in),
    right var(--plate-in) top var(--plate-in),
    right var(--plate-in) top var(--plate-in),
    left var(--plate-in) bottom var(--plate-in),
    left var(--plate-in) bottom var(--plate-in),
    right var(--plate-in) bottom var(--plate-in),
    right var(--plate-in) bottom var(--plate-in);
}

/* Every free-standing card gets the marks, and no panel does. That is the whole
   rule, and it is the kit's: a `.card` is an object standing on the sheet (the
   way in, an error, the key legend), a `.panel` is a region *of* the sheet. The
   distinction is worth keeping sharp, because marks sprinkled on everything stop
   meaning anything.

   The audio player was tried as a third case and rejected with a screenshot: a
   `.panel`'s inked `h2` band sits over the top two marks, so the frame comes out
   with only its bottom corners — the exact half-printed look the card had before
   this. It also already has a border, and a `<audio>` element is chrome enough. */
.card {
  --plate-arm: 10px;
  --plate-in: 7px;
}
body.centred main.card { padding: 2rem 1.75rem 1.75rem; }

/* No coloured borders: a panel that matters gets a heavier rule, and the one
 * that is about a recording in progress gets the stamp. */
.panel.bad { border-width: 2px; }
.panel.good { border-width: 2px; }
.danger-zone { border-width: 2px; }
.danger-zone form + form { margin-top: 1rem; padding-top: 1rem; border-top: var(--pm-hair-soft); }

.flash {
  border: var(--pm-hair);
  border-left-width: 6px;
  padding: .7rem 1rem;
  margin: 0 0 1.5rem;
  font-size: var(--pm-body);
}
.flash.err { border-left-color: var(--pm-stamp); }

/* A settings page is a form, and a form wants a measure. At the sheet's full
   68rem the account page was three 385px fields sitting in the top-left corner
   of a 1040px panel, with the other two thirds of every box empty. */
body.settings main { max-width: 52rem; }

body.centred {
  align-items: center;
  justify-content: center;
  padding: 2rem 1.25rem;
}
/* The login and error pages *are* the card — `<main class="card narrow">` — so
   this must not zero the padding out from under it. */
body.centred main { flex: none; max-width: none; margin: 0; }

/* ── tiles: the dashboard's readouts ──────────────────────────────────────
 * A printed table of numerals, hairline-divided both ways, not six cards
 * floating in whitespace. */

.tiles {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(9rem, 1fr));
  gap: 1px;
  border: var(--pm-hair);
  background: var(--pm-ink);   /* shows through the 1px gap as the rule between cells */
  margin: 0 0 1.75rem;
}
.tile {
  display: flex;
  flex-direction: column;
  gap: .35rem;
  padding: .9rem 1rem 1rem;
  background: var(--pm-paper);
}
.tile .big {
  font: 500 1.75rem/1 var(--pm-mono);
  font-variant-numeric: tabular-nums;
}
.tile .cap {
  font-size: var(--pm-label);
  letter-spacing: .14em;
  text-transform: uppercase;
  color: var(--pm-ink-mute);
}
/* A tile that is carrying something inks itself. Same rule as a selection. */
.tile.bad, .tile.work { background: var(--pm-ink); color: var(--pm-paper); }
.tile.bad .cap, .tile.work .cap { color: var(--pm-paper); }

/* ── printed tables ───────────────────────────────────────────────────────── */

.rows {
  list-style: none;
  margin: 0 0 1.5rem;
  padding: 0;
  border: var(--pm-hair);
}
/* Inside a panel a list is not a box, it is the panel's own ruled body — kit
   rule 2, hairlines separate and whitespace never does. A bordered list inside a
   bordered panel is a box in a box with a gutter of dead paper between them, and
   on the account page that gutter was wrapped around thirteen rows. The side
   margins have to match `.panel`'s padding. */
.panel > .rows,
.panel > form > .rows {
  margin: 0 -1.25rem -1.25rem;
  border: 0;
  border-top: var(--pm-hair);
}
.panel > .rows > li:last-child,
.panel > form > .rows > li:last-child { border-bottom: 0; }
/* A list that starts straight under the panel's title already has a rule above
   it — the title's. Two hairlines 20px apart is a mistake, not a band, so the
   list butts up against the one that is already there. The negative margin is
   exactly the heading's bottom margin. */
.panel > h2:first-child + .rows,
.panel > .panel-head:first-child + .rows {
  margin-top: -1.35rem;
  border-top: 0;
}
/* A button in a row must not carry the standalone button's top margin: it is
   what made every browser row two lines tall with nothing in the second. */
.rows button, .rows .btn { margin-top: 0; }
.rows form.inline { margin: 0; }
.rows li { border-bottom: var(--pm-hair-soft); }
.rows li:last-child { border-bottom: 0; }

.rows li > a,
.rows.tight li {
  display: flex;
  align-items: center;
  gap: .8rem;
  padding: .6rem .9rem;
  border: 0;
  color: var(--pm-ink);
}
.rows li > a { text-decoration: none; }

.rows .meta {
  font-size: .72rem;
  letter-spacing: .06em;
  color: var(--pm-ink-mute);
  white-space: nowrap;
  font-variant-numeric: tabular-nums;
}
/* An empty list is a ruled blank on a form, not an absence. */
.rows .empty {
  padding: 1.1rem .9rem;
  color: var(--pm-ink-mute);
  font-size: var(--pm-label);
  letter-spacing: .16em;
  text-transform: uppercase;
  text-align: center;
  background-image: repeating-linear-gradient(
    135deg, transparent 0 6px, var(--pm-ink-soft) 6px 7px, transparent 7px 13px);
}

/* A share row is three stacked facts, not one line: top-align the stamp and the
   revoke button against them or both float in the middle of a tall row. */
.rows li.sharerow { align-items: flex-start; }
.sharerow .grow { display: flex; flex-direction: column; gap: .35rem; align-items: flex-start; }
.sharerow .pill { margin-top: .2rem; }
.sharerow form.inline { width: auto; }

.rows li > a.cursor { background: var(--pm-ink); color: var(--pm-paper); outline: none; }
.rows li > a.cursor .meta,
.rows li > a.cursor .muted { color: var(--pm-paper); }

@media (hover: hover) {
  .rows li > a:hover { background: var(--pm-ink); color: var(--pm-paper); }
  .rows li > a:hover .meta,
  .rows li > a:hover .muted { color: var(--pm-paper); }
  .rows li > a:hover .pill { border-color: var(--pm-paper); color: var(--pm-paper); }
}

/* ── stamps ───────────────────────────────────────────────────────────────
 * A status is a stamped word in a ruled box. There is no colour coding, because
 * there is no colour: the states differ by whether they are inked. */

.pill {
  display: inline-block;
  flex: none;
  padding: .1rem .45rem;
  border: var(--pm-hair);
  font-size: .6rem;
  letter-spacing: .16em;
  text-transform: uppercase;
  line-height: 1.6;
}
/* Still moving: inked, so the eye finds it in a list of finished ones. */
.pill.uploading, .pill.uploaded, .pill.transcribing,
.pill.transcribed, .pill.summarizing, .pill.running, .pill.pending {
  background: var(--pm-ink);
  color: var(--pm-paper);
}
.pill.failed { border-color: var(--pm-stamp); color: var(--pm-stamp); }
.pill.duplicate, .pill.dead { border-color: var(--pm-ink-soft); color: var(--pm-ink-mute); }

.tag {
  display: inline-block;
  padding: .05rem .4rem;
  border: 1px dashed var(--pm-ink);
  font-size: .6rem;
  letter-spacing: .14em;
  text-transform: uppercase;
}

/* ── forms ────────────────────────────────────────────────────────────────── */

/* A field label is the quietest rank of tracked caps on the sheet, and it has to
   be, because a panel title is now the same face and case one step above it.
   Weight and colour carry that difference; size alone did not. */
label {
  display: block;
  margin: 1rem 0 .35rem;
  font-size: var(--pm-label);
  font-weight: 400;
  letter-spacing: var(--pm-track);
  text-transform: uppercase;
  color: var(--pm-ink-mute);
}
label .muted { text-transform: none; letter-spacing: .04em; }

/* A ruled checklist row: label left, a square of ink right, no tick glyph.
 * `row-reverse` rather than reordering the markup — the input must stay first in
 * the DOM so a screen reader and the label's own hit area still pair correctly,
 * and so the same markup styles in both themes. */
/* A choice row is not a field label: its text is the *option*, and the greyed
   note beside it is what recedes. Inheriting the field label's mute made both
   the same colour and the option name stopped being the thing you read. */
label.radio {
  color: var(--pm-ink);
  display: flex;
  flex-direction: row-reverse;
  align-items: center;
  justify-content: flex-end;
  gap: .8rem;
  margin: 0;
  padding: .6rem 0;
  border-top: var(--pm-hair-soft);
  text-transform: none;
  letter-spacing: .04em;
  font-size: var(--pm-body);
  cursor: pointer;
}

input[type="checkbox"], input[type="radio"] {
  appearance: none;
  -webkit-appearance: none;
  flex: none;
  width: 15px;
  height: 15px;
  margin: 0;
  border: var(--pm-hair);
  background: transparent;
  cursor: pointer;
}
input[type="checkbox"]:checked, input[type="radio"]:checked { background: var(--pm-ink); }

input[type="text"], input[type="password"], input[type="search"],
input[type="number"], input:not([type]), select, textarea {
  display: block;
  width: 100%;
  font: var(--pm-body)/1.6 var(--pm-mono);
  color: var(--pm-ink);
  background: var(--pm-paper-2);
  border: var(--pm-hair);
  padding: .5rem .65rem;
  margin-top: .3rem;
}
input:focus, select:focus, textarea:focus { outline: none; background: var(--pm-paper); box-shadow: inset 0 0 0 1px var(--pm-ink); }
textarea { resize: vertical; line-height: 1.8; }
input::placeholder, textarea::placeholder { color: var(--pm-ink-mute); }
/* The native control arrives with a radius, a grey ground and a system chevron,
   none of which belong on the sheet. The caret is drawn from two straight-line
   gradients — a mark, not a tint, so rule 3 stands. */
select {
  cursor: pointer;
  text-transform: none;
  appearance: none;
  -webkit-appearance: none;
  border-radius: 0;
  padding-right: 2rem;
  background-image:
    linear-gradient(45deg, transparent 50%, var(--pm-ink) 50%),
    linear-gradient(135deg, var(--pm-ink) 50%, transparent 50%);
  background-position: right 1.05rem center, right .75rem center;
  background-size: .3rem .3rem, .3rem .3rem;
  background-repeat: no-repeat;
}

fieldset { border: var(--pm-hair); padding: .3rem .9rem .9rem; margin: 1rem 0; }
legend {
  font-size: var(--pm-label);
  letter-spacing: var(--pm-track);
  text-transform: uppercase;
  padding: 0 .4rem;
}

.two { display: grid; grid-template-columns: 1fr 1fr; gap: 1.1rem; }
.narrow-form { max-width: 24rem; }
.inline { display: inline-flex; gap: .5rem; align-items: center; flex-wrap: wrap; margin: 0; }
.inline input, .inline select { width: auto; margin-top: 0; }
.filters, .controls { display: flex; gap: .5rem; flex-wrap: wrap; align-items: center; margin: 0 0 1.25rem; }
.filters input[type="search"] { flex: 1; min-width: 12rem; margin-top: 0; }
.filters select, .controls input[type="search"] { width: auto; margin-top: 0; }
.filters button, .filters .btn, .controls button, .controls .btn, .panel-head button { margin-top: 0; }
.controls input[type="search"] { max-width: 18rem; }

.themepick { margin-top: .5rem; }
.themepick label.radio .grow { text-transform: none; }
.themepick button { margin-top: 1rem; }

/* The transcription picker's measured line. Tracked mono caps, which is this
 * sheet's form for a figure rather than a sentence, and it has to say
 * `text-transform` for itself: `.themepick label.radio .grow` sets `none` and
 * that inherits into every child. Tabular, for the same column-of-numbers
 * reason as the dark sheet. */
.themepick .figures {
  display: block;
  margin-top: .35rem;
  font-family: var(--pm-mono);
  font-size: var(--pm-label);
  letter-spacing: var(--pm-track);
  text-transform: uppercase;
  font-variant-numeric: tabular-nums;
  color: var(--pm-ink-mute);
}

/* ── buttons ──────────────────────────────────────────────────────────────
 * Tracked mono caps in a ruled box. `--primary` inks it; `aria-pressed` inks
 * anything. No radius, ever. */

button, .btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  font: 500 var(--pm-label)/1 var(--pm-mono);
  letter-spacing: .18em;
  text-transform: uppercase;
  color: var(--pm-ink);
  background: transparent;
  border: var(--pm-hair);
  padding: .65rem 1rem;
  margin-top: .9rem;
  cursor: pointer;
}
.btn { border-bottom: var(--pm-hair); }

button.primary, .btn.primary,
button[aria-pressed="true"] { background: var(--pm-ink); color: var(--pm-paper); }

button.wide { width: 100%; }
button.ghost { background: transparent; }
/* Destructive: the dashed border is the kit's mark for an action that reaches
   outside the sheet, which is exactly what deleting is.
   **The colour is not spent at rest.** `--pm-stamp` is a once-per-sheet colour,
   and a list of signed-in browsers put thirteen red boxes on one page — the
   loudest thing on the sheet was also the least important control on it. The
   dash carries the meaning while you are reading; the colour arrives on hover,
   when you are about to do it. */
button.danger { border-style: dashed; }
button.linkish { border: 0; padding: .5rem .4rem; margin-top: 0; }
button:disabled { opacity: .4; cursor: not-allowed; }

@media (hover: hover) {
  /* `.panel-tap` is excluded here and not merely overridden below: it is a
     <button>, so `button:hover:not(:disabled)` (0,2,1) beats any plain
     `.panel-tap:hover` (0,1,1) and fills the whole instrument solid black over
     the canvas. The panel gets its own affordance — see `.mount:hover`. */
  button:not(.panel-tap):hover:not(:disabled), .btn:hover { background: var(--pm-ink); color: var(--pm-paper); }
  button.danger:hover:not(:disabled) { background: var(--pm-stamp); color: var(--pm-paper); border-color: var(--pm-stamp); }
  button.linkish:hover { background: var(--pm-ink); color: var(--pm-paper); }
}

details { margin-top: 1.25rem; border-top: var(--pm-hair-soft); padding-top: .75rem; }
summary {
  cursor: pointer;
  font-size: var(--pm-label);
  letter-spacing: var(--pm-track);
  text-transform: uppercase;
}

.urlbox { font: .7rem/1.5 var(--pm-mono); margin-top: 0; }
.urlbox.wide { width: 100%; }

/* ── prose, readouts ──────────────────────────────────────────────────────
 * A transcript is machine transcription and a summary is model output. Neither
 * is ever rendered as markup — pre-wrap is the entire rendering strategy, in
 * this theme exactly as in the other one. */

.prose {
  white-space: pre-wrap;
  overflow-wrap: anywhere;
  max-height: 32rem;
  overflow-y: auto;
  padding: .9rem 1rem;
  background: var(--pm-paper-2);
  border: var(--pm-hair);
  font-size: var(--pm-body);
  line-height: 1.85;
}

mark { background: var(--pm-ink); color: var(--pm-paper); }

kbd {
  font: .95em/1 var(--pm-mono);
  border: var(--pm-hair-soft);
  padding: .1em .3em;
  text-transform: uppercase;
}

code { font: .95em/1 var(--pm-mono); border-bottom: var(--pm-hair-soft); }

audio { width: 100%; margin: .4rem 0 .6rem; filter: grayscale(1); }

/* A ruled bar: a continuous quantity against a ticked ground. */
.bar {
  width: 100%;
  max-width: 26rem;
  height: 12px;
  border: var(--pm-hair);
  position: relative;
  overflow: hidden;
}
.bar::before {
  content: "";
  position: absolute;
  inset: 0;
  background-image: repeating-linear-gradient(90deg, var(--pm-ink-soft) 0 1px, transparent 1px 10px);
}
.bar > i { position: relative; display: block; height: 100%; width: 0; background: var(--pm-ink); }

.panel-head {
  display: flex;
  align-items: center;
  gap: .8rem;
  flex-wrap: wrap;
  margin: 0 0 1rem;
}
.panel-head h2 { margin: 0; flex: 1; min-width: 6rem; }
.panel-head .acts { display: flex; gap: .4rem; }
.panel-head .acts button { margin-top: 0; padding: .4rem .7rem; }

.rec-head { margin-bottom: 1.75rem; padding-bottom: 1rem; border-bottom: var(--pm-hair); }
.rec-head h1 { margin-bottom: .6rem; }
.rec-head p {
  margin: 0;
  display: flex;
  gap: .6rem;
  flex-wrap: wrap;
  align-items: center;
  font-size: .72rem;
  letter-spacing: .06em;
  color: var(--pm-ink-mute);
}

.pager {
  display: flex;
  gap: 1rem;
  margin: 0 0 1.5rem;
  font-size: var(--pm-label);
  letter-spacing: var(--pm-track);
  text-transform: uppercase;
}
.pager .right { margin-left: auto; }

/* ── the recorder ─────────────────────────────────────────────────────────
 *
 * A fixed sheet, sized to the viewport and deliberately unscrollable: the panel
 * takes whatever height the two rules leave, and the transcript scrolls inside
 * its own column. The whole point of the contour is that it is one object you
 * look at, and a page that pushes it half off the screen has thrown that away.
 */

body.sheet-page { height: 100dvh; overflow: hidden; }
body.sheet-page main {
  display: flex;
  flex-direction: column;
  max-width: none;
  padding: 0;
  min-height: 0;
}

/* Everything above the panel is chrome, and on this one page chrome is the thing
   in the way: the instrument is what you came here to look at. So the masthead
   loses its status corner (the clock is already in the foot rule, and how much
   audio is on disk is a dashboard question), its cells tighten, and the flash
   becomes one line instead of a block. */
/* The masthead is **identical on every page**. It used to be tightened here to
 * buy the instrument a few pixels, and the cost was that the navigation changed
 * height as you arrived on this tab — a shift you cannot unsee once you have
 * noticed it, for about twelve pixels of panel. Only the status corner goes,
 * and it carries no height. */
body.sheet-page .status { display: none; }

body.sheet-page .flash {
  flex: none;
  margin: 0;
  padding: .35rem 1rem;
  border: 0;
  border-bottom: var(--pm-hair);
  font-size: var(--pm-label);
  letter-spacing: .1em;
  text-transform: uppercase;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
body.sheet-page .flash.err { border-bottom-color: var(--pm-stamp); }

/* ── recording is a mode, not a state of a form ───────────────────────────
 *
 * `data-rec` on <body> is the phase, written by `syncControls()`. While a
 * recording runs the sheet strips back to the instrument, the clock and one
 * hint: no navigation, no settings, no flash, no caption. Holding it brings all
 * of that back, which is what makes hold-then-choose feel like a mode you step
 * out of rather than a dialog that appeared.
 *
 * It must also be unmistakable that it *is* running. Two marks, both the stamp,
 * both saying the one thing: the masthead rule thickens to 3px and the elapsed
 * time in the foot fills solid. The panel's own `REC` label is the third, and it
 * is inside the instrument rather than on the sheet. */

body[data-rec="recording"] .topbar nav,
body[data-rec="recording"] .topbar form,
body[data-rec="recording"] .flash,
body[data-rec="recording"] .unfinished,
body[data-rec="recording"] .caption { display: none; }

body[data-rec="recording"] .topbar { border-bottom-width: 3px; }

body[data-rec="recording"] .foot-clock {
  background: var(--pm-stamp);
  color: var(--pm-paper);
}

/* One hint while running, the key legend the rest of the time. Both selectors
   are written through `.hintbar >` so they outrank the cell rule above, which
   sets `display: block` on every child and otherwise wins on specificity. */
body.sheet-page .hintbar > .foot-hold { display: none; }
body.sheet-page[data-rec="recording"] .hintbar > .foot-hold { display: block; }
body.sheet-page[data-rec="recording"] .hintbar > .foot-keys { display: none; }

/* Held: the options arrive as one ruled row and nothing else changes position,
   so the eye does not have to find the panel again. */
body[data-rec="paused"] .caption,
body[data-rec="stalled"] .caption { color: var(--pm-ink); }

/* The unfinished-uploads band. It is rare, it matters when it happens, and it is
   not worth three lines of prose above the instrument — one line and the two
   verbs, on a strip that scrolls if there are several. */
.unfinished {
  flex: none;
  margin: 0;
  padding: 0;
  border: 0;
  border-bottom: var(--pm-hair);
  max-height: 22vh;
  overflow-y: auto;
}
.unfinished h2 { display: none; }
.unfinished .note {
  margin: 0;
  padding: .4rem 1rem;
  font-size: var(--pm-label);
  letter-spacing: .1em;
  text-transform: uppercase;
  color: var(--pm-ink-mute);
  border-bottom: var(--pm-hair-soft);
}
.unfinished .rows { margin: 0; border: 0; }
.unfinished .rows.tight li { padding: .3rem 1rem; gap: .6rem; }
.unfinished button { margin-top: 0; padding: .35rem .7rem; }

.sheet {
  flex: 1;
  min-height: 0;
  display: grid;
  grid-template-columns: auto minmax(0, 1fr) minmax(17rem, 22rem);
  margin: 0;
}

/* Rotated rail down the left edge. It is what anchors a wide sheet — without it
   the masthead is holding the whole layout on its own. */
.spine {
  writing-mode: vertical-rl;
  text-orientation: mixed;
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 1.25rem .7rem;
  border-right: var(--pm-hair);
  font-size: var(--pm-label);
  letter-spacing: .34em;
  text-transform: uppercase;
  color: var(--pm-ink-mute);
}

.stage {
  display: flex;
  flex-direction: column;
  min-height: 0;
  min-width: 0;
  width: 100%;
  max-width: calc(100dvh - 8rem);
  margin-inline: auto;
  padding: .6rem;
}

/* Registration marks around the panel. Four 12px corners, inset from the edge —
   the mount's own two, plus the pair on `.crop`. */
.mount {
  position: relative;
  flex: 1;
  min-height: 0;
  min-width: 0;
  padding: .5rem;
}
.mount::before, .mount::after,
.mount > .crop::before, .mount > .crop::after {
  content: "";
  position: absolute;
  width: 12px;
  height: 12px;
}
.mount::before { top: 2px; left: 2px; border-left: var(--pm-hair); border-top: var(--pm-hair); }
.mount::after { top: 2px; right: 2px; border-right: var(--pm-hair); border-top: var(--pm-hair); }
.mount > .crop::before { bottom: 2px; left: 2px; border-left: var(--pm-hair); border-bottom: var(--pm-hair); }
.mount > .crop::after { bottom: 2px; right: 2px; border-right: var(--pm-hair); border-bottom: var(--pm-hair); }

/* The panel *is* the control, so it is a real button — but it carries none of
   the button styling above, because it must read as an instrument. */
.panel-tap {
  display: block;
  width: 100%;
  height: 100%;
  margin: 0;
  padding: 0;
  border: 0;
  background: transparent;
  cursor: pointer;
}
.panel-tap:hover { background: transparent; }
.panel-tap canvas { display: block; width: 100%; height: 100%; }

.stageline {
  flex: none;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: .7rem;
  padding: .5rem 0 .3rem;
}

.caption {
  margin: 0;
  font-size: var(--pm-label);
  letter-spacing: .16em;
  text-transform: uppercase;
  color: var(--pm-ink-mute);
  text-align: center;
}
/* A failure is the one caption that is a *sentence*, not a label. Tracked mono
   caps are right for "TAP THE PANEL TO BEGIN" and wrong for six lines telling
   somebody which iOS Settings row to open — letterspaced uppercase is read
   letter by letter, which is exactly what you do not want from instructions.
   So the problem state drops the caps, drops the tracking, and reads as prose;
   it keeps the stamp colour, which is what marks it as a failure. */
.caption.problem {
  color: var(--pm-stamp);
  text-transform: none;
  letter-spacing: 0;
  font-size: var(--pm-body);
  line-height: 1.5;
  max-width: 44ch;
  margin: 0 auto;
  text-wrap: pretty;
}

/* The row that does not exist until the panel is held. A segmented picker:
   one row, hairline dividers, the primary action inked. */
.opts { display: flex; border: var(--pm-hair); }
.opts button {
  margin-top: 0;
  border: 0;
  border-right: var(--pm-hair);
  padding: .7rem 1.1rem;
}
.opts button:last-child { border-right: 0; }
.opts button.danger { border-style: solid; border-right: 0; }

/* ── the control column ───────────────────────────────────────────────── */

.control {
  display: flex;
  flex-direction: column;
  min-height: 0;
  overflow: hidden;
  border-left: var(--pm-hair);
}
.control > * { border-bottom: var(--pm-hair); }
.control > :last-child { border-bottom: 0; }

/* ── the two side panes ───────────────────────────────────────────────────
 * Native `<details>`. The summary is a ruled row with a drawn +/- rather than
 * the browser's triangle, which is the one disclosure marker that fits a sheet
 * set in tracked mono caps. */

/* The generic `details` rule (a spaced, ruled disclosure, for the share form)
   must not reach these: it was adding 20px of margin and 12px of padding above
   each pane, which is 64px of nothing taken off the instrument on a phone. */
.control > details { flex: none; margin: 0; padding: 0; border-top: 0; }
.control > details > summary {
  display: block;
  padding: .6rem 1.25rem;
  cursor: pointer;
  font-size: var(--pm-label);
  letter-spacing: var(--pm-track);
  text-transform: uppercase;
  color: var(--pm-ink-mute);
  list-style: none;
  border-bottom: 0;
}
.control > details > summary::-webkit-details-marker { display: none; }
.control > details > summary::before { content: "+"; margin-right: .7rem; }
.control > details[open] > summary::before { content: "\2013"; }
.control > details[open] > summary { border-bottom: var(--pm-hair-soft); color: var(--pm-ink); }

@media (hover: hover) {
  .control > details > summary:hover { background: var(--pm-ink); color: var(--pm-paper); }
}

/* An open pane is a column whose *body* scrolls, never one that grows without
   limit. The transcript is the case that matters: it is fed continuously, so
   without this it goes on taking height off the instrument for as long as
   somebody keeps talking. */
.control > details[open] { display: flex; flex-direction: column; min-height: 0; }

/* Chrome 131+ inserts a `::details-content` box between a `<details>` and its
 * content. `display: flex` on the `<details>` therefore does **not** make the
 * body a flex item — the pseudo-element is the flex item, and the body is a
 * plain block inside it that grows to its content and overflows. That is
 * exactly what it did: a 1715px transcript inside a 387px pane, sailing past
 * the frame instead of scrolling. Firefox and Safari have no such box yet and
 * flex the children directly, so both paths have to be spelled out; browsers
 * that do not know this selector ignore the rule and already behave. */
.control > details[open]::details-content {
  display: flex;
  flex-direction: column;
  flex: 1;
  min-height: 0;
  overflow: hidden;
}

.setup[open] { flex: 0 1 auto; }
.live[open] { flex: 1; }
.setupbody, .livebody { flex: 1; min-height: 0; overflow-y: auto; }

.setupbody { padding: 1.1rem 1.25rem 1.25rem; }
.setup .field label { margin-top: 0; }
.setup .field + .field { margin-top: 1rem; }
.setup .toggles { margin-top: 1.25rem; }
.setup .field:first-child label { margin-top: 0; }
.setup .note {
  margin: 1rem 0 0;
  padding-top: .8rem;
  border-top: var(--pm-hair-soft);
  font-size: .68rem;
  line-height: 1.7;
  color: var(--pm-ink-mute);
}

/* The transcript. It keeps the kit's body spec — mono at the sheet's body size,
   line-height 1.8 — because it is the one long-form thing on the page and the
   whole sheet is set in one face. */
.live {
  flex: 1;
  min-height: 0;
  display: flex;
  flex-direction: column;
}
.livebody {
  flex: 1;
  min-height: 0;
  overflow-y: auto;
  padding: .9rem 1.25rem 1.25rem;
  font-size: var(--pm-body);
  line-height: 1.85;
  overflow-wrap: anywhere;
}
/* Committed text is ink; the sentence still being spoken is not committed to
   anything yet, and is set lighter until it is. */
.live .said { color: var(--pm-ink-mute); }
.live .said:last-of-type { color: var(--pm-ink); }
.live .partial { color: var(--pm-ink-mute); }
.live .idle { color: var(--pm-ink-mute); }
.live .idle.problem { color: var(--pm-stamp); }

/* The dark recorder's own furniture, for the shared bits of markup. */
.panel.recorder { max-width: 44rem; margin-inline: auto; }

/* ── keyboard help ────────────────────────────────────────────────────── */

.keyhelp {
  position: fixed;
  inset: 0;
  z-index: 40;
  display: grid;
  place-items: center;
  padding: 1.5rem;
  background: color-mix(in srgb, var(--pm-paper) 88%, transparent);
}
.keyhelp .card { max-width: 26rem; margin: 0; border-width: 2px; }
.keyhelp dl { display: grid; grid-template-columns: auto 1fr; gap: .1rem 1rem; margin: 0; }
.keyhelp dt {
  font-size: var(--pm-label);
  letter-spacing: .12em;
  text-transform: uppercase;
  padding: .35rem 0;
  border-top: var(--pm-hair-soft);
}
.keyhelp dd {
  margin: 0;
  padding: .35rem 0;
  border-top: var(--pm-hair-soft);
  color: var(--pm-ink-mute);
}

/* ── public share pages ───────────────────────────────────────────────────
 *
 * `/s` reads the same cookie, so an owner opening their own link gets the sheet
 * the rest of the UI is wearing. A recipient has no cookie and never sees any of
 * this — `SHARE_DEFAULT_THEME` hands them the calm document in `share.css`, and
 * that is deliberate: a link is a thing you send to somebody else, and defaulting
 * *them* into a display preference would mean every link ever minted quietly
 * changed clothes.
 *
 * Which is why this is written against the markup that already exists, plus one
 * `class="doc"` on `<body>`. The masthead and the foot colophon are generated
 * content; the frame is background layers on the `<article>`. Nothing here needs
 * an element `share.css` would then have to know about, so the calm document
 * stays exactly as calm as it was.
 *
 * Three bands and nothing else: a masthead, the document as a plate — an object
 * presented on the sheet rather than the sheet itself — and a foot colophon. The
 * kit's spine and its rubber stamp were both tried here and both came back off;
 * the notes below say why, because both are the obvious thing to add again.
 */

/* Masthead. One word and a rule under it. */
body.doc::before {
  content: "OMI";
  flex: none;
  padding: .8rem 1.5rem;
  border-bottom: 2px solid var(--pm-ink);
  font-size: var(--pm-label);
  font-weight: 500;
  letter-spacing: .34em;
  text-transform: uppercase;
}

/* The colophon. A printed sheet says what press it came off; this one says what
   the reader is looking at, which nothing else on the page does. It is the last
   child of a flex-column body, so it sits at the foot of the viewport on a short
   document and at the foot of the document on a long one. */
body.doc::after {
  content: "SELF-HOSTED · NOT INDEXED · NO ANALYTICS";
  flex: none;
  padding: .7rem 1.5rem;
  border-top: var(--pm-hair);
  font-size: var(--pm-label);
  letter-spacing: var(--pm-track);
  color: var(--pm-ink-mute);
}

body.doc > main {
  max-width: 64rem;
  padding: 2.75rem 1.5rem 3.25rem;
}

/* The document, framed. Padding has to clear the marks *and* the rails, or the
   first line of the summary sits on a tick ladder — and the bottom has to clear
   them by more than the top does, because the last thing in the box is a line of
   small grey text rather than a heading, and it prints straight into the corner
   at the padding the top can live with. */
body.doc > main > article {
  max-width: 47rem;
  margin: 0 auto;
  padding: 2.5rem 2.75rem 2.6rem;
}

/* No spine. The kit puts a rotated rail down the left edge to hold a wide sheet
   together, and on an artboard whose panels span the width it does. Here the
   document is a 47rem column in the middle of a 64rem sheet, so the rail landed
   a couple of hundred pixels out in open paper with nothing between it and the
   text — a rule that divides nothing from nothing. The masthead, the plate and
   the colophon are the composition; it does not need a fourth edge. */

article > header { padding-bottom: 1.1rem; border-bottom: 2px solid var(--pm-ink); margin-bottom: 1.75rem; }
/* A recording's name is a sentence somebody or something wrote, so on the share
   sheet the title keeps its own case and its own tracking — uppercasing a whole
   sentence throws away case the reader needs and, at this tracking, took three
   lines on a phone. It is the one title on the sheet that is *content*. */
article h1 {
  font-size: 1.5rem;
  line-height: 1.35;
  letter-spacing: .04em;
  text-transform: none;
  margin-bottom: .6rem;
}
article .meta {
  margin: 0;
  font-size: var(--pm-label);
  letter-spacing: .12em;
  text-transform: uppercase;
  color: var(--pm-ink-mute);
}

/* Section heads are tracked caps over a hairline that runs the full column —
   rule 2 of the kit: adjacent regions share a rule, they are not spaced apart. */
body.doc article h2 {
  margin: 2rem 0 .9rem;
  padding-bottom: .35rem;
  border-bottom: var(--pm-hair-soft);
}
body.doc article h2 + .meta { margin-bottom: .6rem; }

article ul { padding-left: 1.1rem; margin: 0 0 1rem; }
article li { margin: .25rem 0; }
.actions .who, .actions .due { color: var(--pm-ink-mute); font-size: .72rem; text-transform: uppercase; letter-spacing: .08em; }
.prose.transcript { background: var(--pm-paper-2); }

/* The foot of the document. A rule and one line, with room under it: the plate's
   bottom marks are 8px from the edge of the box, so a footer that ends where the
   padding ends prints its last line straight into them.

   There is no rubber stamp here. The kit allows one per sheet and this looked
   like the place for it — the link is private and it dies — but on a document
   somebody else opens, a rotated red box in the corner is a costume rather than
   a fact, and the sentence beside it already said the same thing better. The
   third colour stays spent on recording, and on nothing else. */
body.doc article > footer {
  margin-top: 2.75rem;
  padding-top: 1rem;
  border-top: var(--pm-hair);
  color: var(--pm-ink-mute);
  font-size: .72rem;
}
body.doc article > footer p { margin: 0; }

article.centred { text-align: center; padding-top: 3rem; }
article.centred form { margin-top: 1.5rem; display: inline-flex; flex-direction: column; gap: .4rem; text-align: left; }
article.centred input { min-width: 16rem; }
.error { color: var(--pm-stamp); }

/* The gate and the dead-link page are one short paragraph. A full plate around
   three lines of text is a frame around nothing, so they keep the marks and drop
   the rails and the stamp. */
body.doc > main > article.centred {
  --plate-rail-len: 0px;
  max-width: 34rem;
  padding-block: 2.75rem;
}
body.doc > main > article.centred > footer::after { content: none; }

/* ── narrow ───────────────────────────────────────────────────────────────
 * The phone is a first-class recorder — it is the microphone most likely to be
 * in the room. The sheet stacks rather than shrinking: the panel keeps the top
 * half, the transcript takes the bottom, and neither scrolls the page. */

@media (max-width: 46rem) {
  main { padding: 1.1rem 1rem 1.5rem; }

  /* The nav becomes a band of five cells filling the width; a ruled spacer there
     would take the width the labels need. */
  .topbar nav::after { display: none; }

  /* The share sheet on a phone. The frame stays — it is what says this is a
     document and not a web page — but the ladders come off: at 390px they sit
     against the text rather than beside it, and the plate's desk padding costs
     a third of the measure. */
  body.doc > main { padding: 1.4rem .9rem 2rem; }
  body.doc > main > article {
    --plate-arm: 10px;
    --plate-in: 3px;
    --plate-rail-len: 0px;
    padding: 1.6rem 1.15rem 1.2rem;
  }
  body.doc article h1 { font-size: 1.7rem; }
  body.doc article > footer { flex-wrap: wrap; gap: 1rem; }
  body.doc::before, body.doc::after { padding-inline: 1rem; }
  h1 { font-size: 1.75rem; }

  /* The masthead becomes two bands: identity and sign-out on the first, the five
     nav cells filling the second. Five tracked-caps cells do fit across 390px,
     but only once the tracking comes off — so it comes off here and nowhere
     else. `min-width: 0` is what stops the longest label pushing the whole bar
     wider than the screen, which took the clock off the right-hand edge. */
  .topbar { flex-wrap: wrap; }
  .topbar nav { order: 3; flex: 1 0 100%; border-top: var(--pm-hair); }
  .topbar nav a {
    flex: 1 1 0;
    min-width: 0;
    justify-content: center;
    overflow: hidden;
    text-overflow: ellipsis;
    padding: .6rem .2rem;
    font-size: .56rem;
    letter-spacing: .04em;
  }
  .topbar nav a:last-child { border-right: 0; }
  .status { display: none; }
  .brand { flex: 1; }
  .topbar .linkish { padding: 0 .9rem; }

  /* Stacked, and the panel keeps a floor: a recorder whose visualiser has been
     squeezed to a thumbnail by a settings form is not a recorder. */
  .spine { display: none; }
  /* The instrument keeps 46vh whatever the transcript is doing, and the panes
     get the rest. Without the floor the transcript simply took the screen: it
     grows with every sentence, so a long meeting ended with a full-height wall
     of text and a visualiser squeezed to a strip. Past this the text scrolls
     inside its own pane and the panel never moves again. The two numbers are
     one number — a floor on the instrument is a ceiling on the panes. */
  .sheet { grid-template-columns: 1fr; grid-template-rows: minmax(46vh, 1fr) auto; }
  .stage { padding: .75rem; max-width: none; }
  .mount { padding: .75rem; }
  .control { border-left: 0; border-top: var(--pm-hair); max-height: 46vh; }
  .control > details > summary { padding: .7rem 1rem; }
  .setupbody { padding: .9rem 1rem 1rem; }
  .livebody { padding: .8rem 1rem 1rem; }
  .topbar nav a { padding: .5rem .2rem; }

  /* The clock is the one thing on this bar that must survive a phone. */
  .hintbar .foot-cell.desk-only { display: none; }
  .desk-only { display: none; }

  .two { grid-template-columns: 1fr; }
  .tiles { grid-template-columns: repeat(2, 1fr); }
  .rows li > a, .rows.tight li { flex-wrap: wrap; row-gap: .3rem; }
  .rows .meta { flex: 1 0 100%; white-space: normal; }
  .sharerow .grow { width: 100%; }
  .inline { width: 100%; }
  .panel-head .acts { width: 100%; }
  .panel-head .acts button { flex: 1; }
  .prose { max-height: 24rem; }
  .keyhelp { display: none; }
  article h1 { font-size: 1.9rem; }
}

/* Touch targets: 44px is the floor both Apple and Google publish, and they are
   right — a .6rem button is comfortable with a mouse and a miss with a thumb. */
@media (pointer: coarse) {
  :root { --pm-label: .68rem; --pm-body: .84rem; }
  button, .btn { min-height: 44px; }
  .topbar nav a { min-height: 44px; }
  .rows li > a, .rows.tight li { min-height: 48px; }
  input, select, textarea { min-height: 44px; font-size: 16px; }  /* 16px stops iOS zooming on focus */
  input[type="checkbox"], input[type="radio"] { width: 20px; height: 20px; min-height: 0; }
  label.radio { min-height: 44px; }
  .opts button { min-height: 48px; }
}

/* An e-paper panel is not going to animate anything, and neither is this sheet:
   there is no transition in this file. The block is here so a reduced-motion
   preference is honoured by the one thing that does move — a `<details>` that
   the browser animates on its own. */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: .001ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: .001ms !important;
  }
}

/* ── the notes ─────────────────────────────────────────────────────────────
 *
 * The readable form of a summary on the sheet: an orientation paragraph, then
 * one ruled section per subject.
 *
 * A topic heading **keeps its own case**, for exactly the reason the share
 * document's h1 does: it is content, not chrome. A subject name is a phrase a
 * person or a model wrote — "Ветвление и выкатка на dev" — and setting it in
 * tracked caps like a panel title throws away case the reader needs and, at
 * heading tracking, wraps a Russian noun phrase onto three lines on a phone.
 *
 * That puts it outside the tracked-caps ladder (.64 label / .88 panel / 1.3
 * page), so it is separated from body by size and weight instead: 1rem at 500
 * against a .78rem body. It takes a hairline above rather than a fill, because
 * a run of eight filled bands down a long document is the zebra the panel-title
 * rule already rejected once.
 */

/* ── reading measure ───────────────────────────────────────────────────────
 *
 * 38rem, not the 46rem the rest of the UI uses, and the difference is the whole
 * legibility fix. A monospace glyph is ~0.6em wide, so 46rem of IBM Plex Mono is
 * about **90 characters a line** — half again past the 60-75 that prose is
 * comfortable at, and the reason a page of Russian notes read as a wall. This is
 * the one long-form reading surface in the admin UI and it is the only place
 * that gets its own measure.
 *
 * The size goes up with it. `--pm-body` is 12.5px, which is right for a dense
 * control surface and wrong for several hundred words: everything inside the
 * notes is set a step above it, and the leading opens to match. Nothing else on
 * the sheet moves. */
/* The notes fill their panel. A measure was tried twice — once on the text
 * inside a full-width box, which left a third of every section empty on the
 * right, and once on the page column, which turned a wide monitor into a
 * ribbon with margins bigger than the content. Neither is worth having; the
 * legibility that mattered was size, leading and hierarchy, and those stay. */
.notes {
  margin: 0 0 1.75rem;
  font-size: .84rem;
  line-height: 1.8;
}
/* The lede: the paragraph you read first, so it is the largest thing that is not
 * a heading and it carries full ink rather than the body's inherited colour. */
.notes .overview {
  margin: 0 0 1.75rem;
  font-size: .89rem;
  line-height: 1.85;
  color: var(--pm-ink);
}
/* A topic heading sits well clear of the body now — 1.15rem against .84rem is a
 * 1.37x step, where 1rem against .78rem was 1.28x and read as bold body text. */
.notes h3 {
  font: 500 1.15rem/1.35 var(--pm-mono);
  letter-spacing: 0;
  text-transform: none;
  color: var(--pm-ink);
  margin: 1.6rem 0 .4rem;
  padding-top: .8rem;
  border-top: var(--pm-hair-soft);
  display: flex;
  align-items: baseline;
  gap: .6rem;
}
.notes h3 { margin: 2.1rem 0 .5rem; padding-top: 1.1rem; }
.notes h3:first-child { margin-top: 0; padding-top: 0; border-top: 0; }
.notes .gist { color: var(--pm-ink-mute); margin: 0 0 .7rem; line-height: 1.7; }
.notes ul { margin: 0; padding-left: 1.15rem; }
.notes li { line-height: 1.8; margin-bottom: .5rem; }
/* The marker was the same ink as the sentence, so a wrapped point read as two
 * points. Dimmer marker, and the text hangs clear of it. */
.notes li::marker { color: var(--pm-ink-mute); }

/* The timestamp is a measurement, so it is mono — the one thing mono is for
 * here (data, never chrome). It never takes `--pm-stamp`: that colour is spent
 * on recording and nothing else, and a column of red time marks down a page of
 * notes would be the loudest thing on a sheet that is not recording anything. */
.stamp {
  font: 400 .75rem/1 var(--pm-mono);
  letter-spacing: .04em;
  color: var(--pm-ink-mute);
  background: none;
  border: 0;
  padding: 0;
  margin: 0;
  flex: none;
  white-space: nowrap;
}
button.stamp { cursor: pointer; }
button.stamp:hover:not(:disabled),
button.stamp:focus-visible {
  color: var(--pm-ink);
  background: none;
  text-decoration: underline;
}

/* See admin.css — the same line on the sheet. */
.stale-summary { display: flex; align-items: baseline; gap: .6rem; flex-wrap: wrap; }
.stale-summary form { display: inline; }

/* A list head names Decisions, Action items or Open questions. It was set at the
 * .64rem tracked-caps label rank in `--pm-ink-mute`, which is the *field label*
 * rank — so on a page with no fields visible it had nothing to be quieter than
 * and simply receded. It keeps the caps (it is chrome, not content, and it is
 * the one rank where caps still belong) and takes full ink and a weight, which
 * is what separates a section head from a caption. */
.notes h4 {
  font: 600 .7rem/1 var(--pm-mono);
  letter-spacing: var(--pm-track);
  text-transform: uppercase;
  color: var(--pm-ink);
  margin: 2.4rem 0 .9rem;
  padding-bottom: .4rem;
  border-bottom: var(--pm-hair);
}
/* A summary with lists but no overview and no topics starts here, and the panel
 * title's own full-bleed rule is already directly above — two hairlines twenty
 * pixels apart is a mistake, not a band. */
.notes h4:first-child { margin-top: 0; }

/* `.actions .who, .actions .due` is defined unscoped at paper.css:1428 for the
 * share document — `--pm-ink-mute` at .72rem, tracked caps — so the admin notes
 * already inherit it. Deliberately NOT redefined here: one mark on both
 * surfaces and no second definition to drift. (The corollary is the reason the
 * notes stay in a `<div class="notes">` and never become an `<article>`:
 * `article ul` and `article li` at 1426-1427 are unscoped too, and would
 * silently rewrite this page's list rhythm in every paper theme.) */

/* Filing metadata, after the reading. `.tag` is already a dashed inked chip. */
.notes .tags { display: flex; flex-wrap: wrap; gap: .4rem; margin: 1.5rem 0 0; }

/* ── editing the notes ─────────────────────────────────────────────────────
 *
 * `details` on this sheet already carries `border-top: var(--pm-hair-soft)` and
 * `padding-top: .75rem`, and `summary` is already a control in the tracked-caps
 * label rank. That is rule 2 satisfied by a rule that already ships: a new
 * region is bounded by a hairline, not by air. So the drawer needs the distance
 * and the measure and nothing else — no second hairline is drawn twenty pixels
 * from the one `details` prints, no fill (it is not selected, active or
 * hovered), no radius, no tint, no transition, and no --pm-stamp: this is not
 * recording anything.
 *
 * The reason it exists at all is sharper on this sheet than on the other one. A
 * field `label` here is .64rem mono caps at .2em; a list head is now .64rem mono
 * caps at .2em one weight up. With the form disclosed, "DECISIONS" the list head
 * and "DECISIONS" the field label are never on screen together. */
.editnotes { margin-top: 1.75rem; }
/* The editor is as wide as the notes it edits, which is the panel. */

/* See admin.css — Re-summarise inside the drawer, under its own hairline. */
.rebuild { margin-top: 1.25rem; padding-top: 1rem; border-top: var(--pm-hair-soft); }
/* The caption is a block; the control row beside the button is not. This was a
 * bare `.rebuild span`, written when the caption was the only span in here — and
 * at (0,1,1) it beat `.inline` (0,1,0) and flattened the style picker into three
 * stacked lines. Naming the exception is cheaper than remembering which of the
 * two selectors wins. */
.rebuild span:not(.inline) { display: block; margin-top: .5rem; max-width: 30rem; }
.rebuild .inline { gap: .6rem; }
