/* Reporter — ink on paper, matching graphyt.in.
 *
 * The founder's own restyle of the collection app (collect/static) is the
 * reference, and site/DESIGN.md is the system behind both: paper #FFFFFF,
 * ink #111111, three warm greys, 1px hairlines, 22px cards, pill buttons, no
 * shadows. Humant carries all the chrome at two weights — 700 speaks, 500
 * reads — exactly as collect/static does. Source Serif 4 carries the report
 * and the final text: the one surface a radiologist reads for hours, and the
 * one that becomes the lab's document.
 *
 * Colour appears only as state, and only where ink cannot say it: the error
 * dot and a failed toast — and the radiologist's own ink, which waits behind
 * one ink "A" until asked for. What the old dark theme said in teal and amber
 * is said here with weight, inversion and a warm-grey wash, and each still
 * means one thing:
 *   weight (700 on 500)              = a fact in the transcript: a
 *                                      measurement or a template term
 *   wash (the paper ramp's #F4F3F1)  = the engine amended the document
 *   deeper wash (hairline)           = a correction — the corrector, or a
 *                                      manual line the engine was held off
 *   moving wash                      = an edit in flight
 *   inversion to ink                 = the microphone is hearing you
 *
 * Every url() below resolves against THIS file (/static/style.css), never
 * against the page. Chrome's preload scanner fetches whatever the markup names
 * from the root before index.html's <base> script has run, and only a few
 * files are routed at "/" — so the fonts and the mark are named here.
 *
 * The dictation pill is the instrument AND the control: a fixed-size capsule,
 * its bars moving on real worklet levels, tap (or Space) to pause and resume.
 * There is deliberately no separate listening button.
 */

@font-face {
  font-family: "Humant";
  src: url("fonts/humant-medium.woff2") format("woff2");
  font-weight: 500;
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: "Humant";
  src: url("fonts/humant-bold.woff2") format("woff2");
  font-weight: 700;
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: "Source Serif 4";
  src: url("fonts/source-serif-4-latin-400.woff2") format("woff2");
  font-weight: 400;
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: "Source Serif 4";
  src: url("fonts/source-serif-4-latin-600.woff2") format("woff2");
  font-weight: 600;
  font-style: normal;
  font-display: swap;
}

:root {
  --paper: #FFFFFF;
  --ink: #111111;
  --ink-muted: #5C5955;
  --statement: #8C8883;
  --ash: #C9C6C1;
  --hairline: #E4E2DD;
  --wash: #F4F3F1;           /* the paper ramp's first step: a pencil-light tint */
  --stop: #D92D20;           /* error only — reads on paper and on ink */

  --display: "Humant", ui-sans-serif, system-ui, sans-serif;
  --read: "Source Serif 4", "Iowan Old Style", "Palatino Linotype", Palatino, Georgia, serif;
  --ease: cubic-bezier(.65, 0, .35, 1);

  --topbar-h: 64px;
  --gap: 12px;
  --card: 22px;
}

*, *::before, *::after { box-sizing: border-box; }
* { scrollbar-width: thin; scrollbar-color: var(--ash) transparent; }

/* An author `display` beats the UA's `[hidden]` rule, so every element the JS
   toggles with `.hidden` would need its own guard — without one the pill
   animates through the whole demo and the panel claims to be hearing while
   paused. One rule, as collect/static has it, guards them all. */
[hidden] { display: none !important; }

html, body {
  height: 100%;
  margin: 0;
  overflow: hidden;              /* every pane scrolls itself; the page never does */
}

body {
  background: var(--paper);
  color: var(--ink);
  font-family: var(--display);
  font-weight: 500;
  font-size: 15px;
  line-height: 1.55;
  display: grid;
  grid-template-rows: var(--topbar-h) minmax(0, 1fr);
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
}

::selection { background: var(--ink); color: var(--paper); }
:focus-visible { outline: 2px solid var(--ink); outline-offset: 2px; }

kbd {
  font-family: var(--display);
  font-weight: 700;
  font-size: .75rem;
  border: 1px solid var(--hairline);
  border-bottom-width: 2px;
  border-radius: 6px;
  padding: 1px 7px;
  color: var(--ink);
  background: var(--paper);
}

/* ── the mark and wordmark, as graphyt.in sets them ──────────────────── */

.brand {
  display: flex;
  align-items: center;
  gap: 8px;
  min-width: 0;
  color: var(--ink);
  text-decoration: none;
}
.brand__mark {
  flex: none;
  width: 24px;
  height: 26px;                  /* the mark's own 1698 × 1867 proportion */
  background: url("media/logo.svg") center / contain no-repeat;
}
.brand__word {
  flex: none;
  font-weight: 700;
  font-size: 1.1875rem;
  letter-spacing: -.035em;
  line-height: 1;
}

/* ── top bar ─────────────────────────────────────────────────────────── */

.topbar {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  align-items: center;
  gap: 16px;
  padding: 0 24px;
  background: var(--paper);
  border-bottom: 1px solid var(--hairline);
  position: relative;
  z-index: 5;
}

.brand__rule {
  flex: none;
  width: 1px;
  height: 22px;
  background: var(--hairline);
  margin: 0 6px 0 8px;
}
.skill {
  min-width: 0;
  font-size: .9375rem;
  color: var(--ink-muted);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* The engine's state, one dot, in ink: solid is ready, a breathing ring is
   listening, a hollow pulsing ring is loading, grey is paused. Red only for
   an error — the one state that must never be mistaken for another. */
.livedot {
  flex: none;
  width: 8px;
  height: 8px;
  border-radius: 50%;
  margin: 0 2px 0 6px;
  background: var(--statement);
}
.livedot[data-state="ready"]     { background: var(--ink); }
.livedot[data-state="listening"] { background: var(--ink); animation: breathe 1.8s var(--ease) infinite; }
.livedot[data-state="loading"]   {
  background: transparent;
  box-shadow: inset 0 0 0 1.5px var(--ink-muted);
  animation: blink 1.2s var(--ease) infinite;
}
.livedot[data-state="paused"]    { background: var(--statement); }
.livedot[data-state="error"]     { background: var(--stop); }

@keyframes breathe {
  0%, 100% { box-shadow: 0 0 0 0 rgba(17, 17, 17, .22); }
  50%      { box-shadow: 0 0 0 7px rgba(17, 17, 17, 0); }
}
@keyframes blink { 50% { opacity: .3; } }

.badge {
  flex: none;
  font-weight: 700;
  font-size: .75rem;
  line-height: 1;
  letter-spacing: -.005em;
  padding: 5px 10px;
  border-radius: 999px;
  border: 1px solid var(--ink);
  color: var(--ink);
  background: var(--paper);
}
.badge.signed { background: var(--ink); color: var(--paper); }

.actions { display: flex; align-items: center; justify-content: flex-end; gap: 10px; }
.accession {
  font-size: .875rem;
  color: var(--ink-muted);
  font-variant-numeric: tabular-nums;
  margin-right: 6px;
}

/* ── buttons: pills, hairline at rest, ink on hover ──────────────────── */

.btn {
  font-family: var(--display);
  font-weight: 700;
  font-size: .9375rem;
  letter-spacing: -.015em;
  line-height: 1;
  min-height: 40px;
  padding: 0 18px;
  border-radius: 999px;
  border: 1px solid var(--hairline);
  background: var(--paper);
  color: var(--ink);
  cursor: pointer;
  transition: background 120ms var(--ease), color 120ms var(--ease),
              border-color 120ms var(--ease), transform 60ms ease;
}
.btn:active { transform: scale(.98); }
.btn.primary { background: var(--ink); color: var(--paper); border-color: var(--ink); }
/* At rest a disabled button — primary or not — is the paper button with its
   words in statement grey: Hairline-To-Ink read backwards. An opacity wash
   over the ink pill made a mid-grey capsule that is in no palette and looked
   broken rather than resting. */
.btn[disabled], .btn.primary[disabled] {
  background: var(--paper);
  border-color: var(--hairline);
  color: var(--statement);
  opacity: 1;
  cursor: default;
  transform: none;
}
.btn.big { width: 100%; min-height: 52px; font-size: 1.0625rem; padding: 0 26px; }
.btn.slim { min-height: 34px; font-size: .875rem; padding: 0 14px; }
@media (hover: hover) {
  .btn:not([disabled]):hover { border-color: var(--ink); }
  .btn.primary:not([disabled]):hover { background: var(--paper); color: var(--ink); }
}

/* ── main: two hairline sheets on paper ──────────────────────────────── */

main {
  display: grid;
  grid-template-columns: minmax(0, 3fr) minmax(0, 2fr);
  grid-template-rows: minmax(0, 1fr);
  gap: var(--gap);
  padding: var(--gap);
  /* The pill floats in a gutter that belongs to nobody: `main` reserves the
     strip, so the pill can never cover a pane's content. */
  padding-bottom: calc(var(--gap) + 68px);
  min-height: 0;
  position: relative;
  z-index: 1;
}

.pane { min-height: 0; overflow: auto; }

.report, .side > .pane {
  background: var(--paper);
  border: 1px solid var(--hairline);
  border-radius: var(--card);
  transition: border-color 160ms var(--ease);
}

/* ── the focus model: which pane the voice feeds ─────────────────────── */
/* Clicking the report = edit mode (the document takes the dictation);
 * clicking the dictation pane = content mode (findings queue for Generate).
 * The active pane's hairline turns ink — the site's whole state vocabulary,
 * and the whole announcement; a chip repeating it read as a popup. */

.pane.live { border-color: var(--ink); }

/* ── the report: the lab's template, verbatim ────────────────────────── */

.report { padding: 0 32px 80px; }

.report-head {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 10px 14px;
  position: sticky;
  top: 0;
  z-index: 3;
  background: var(--paper);
  padding: 14px 0 12px;
  margin-bottom: 22px;
  border-bottom: 1px solid var(--hairline);
}
.report-head .fmtbar { margin-right: auto; }

/* The document IS the view: the lab's exact template (verbatim from
 * kb/sources/lab-templates, itself a faithful mirror of the radiologist's
 * own .doc files), prefilled normal, amended only by dictation. Set in the
 * reading face because it is read all shift; pre-wrap because it is still
 * byte-for-byte what exports, tabs and all. */
.reporttext {
  font-family: var(--read);
  font-weight: 400;
  font-size: 16.5px;
  line-height: 1.72;
  color: var(--ink);
  white-space: pre-wrap;
  overflow-wrap: anywhere;
  /* centred in the pane, as the Word view centres its page: pushed left, the
     76ch column left a dead band of ~200px at 1440 and read as accidental
     beside the balanced Word view */
  margin: 0 auto;
  max-width: 76ch;
  border-radius: 6px;
}
.reporttext b, .reporttext strong { font-weight: 600; }
.reporttext.pulse { animation: pulse-row 1.4s ease-out; }
.reporttext:focus { outline: none; }
.reporttext[contenteditable="true"] { cursor: text; caret-color: var(--ink); }

@keyframes pulse-row {
  0%   { background: var(--wash); }
  100% { background: transparent; }
}
@keyframes flash-amber {         /* the name is app.js's; the colour is gone */
  from { background: var(--hairline); }
  to   { background: transparent; }
}
.flash-amber { animation: flash-amber 1.2s ease-out; border-radius: 4px; }

/* an edit in flight: the words being rewritten shimmer — in the document
 * (edit_selection) and in the transcript (the corrector pass) */
.reporttext .editing, .transcript-doc .editing {
  background: linear-gradient(90deg, var(--wash), var(--hairline), var(--wash));
  background-size: 200% 100%;
  border-radius: 3px;
  animation: edit-shimmer 1.1s linear infinite;
}
@keyframes edit-shimmer {
  from { background-position: 0 0; }
  to   { background-position: 200% 0; }
}
/* ...and the returned replacement lands with one beat */
.reporttext .edited { border-radius: 3px; animation: edited-in 1.2s ease-out; }
@keyframes edited-in {
  from { background: var(--hairline); }
  to   { background: transparent; }
}

/* The Word view: the report rendered from the REAL .docx bytes. The pages
 * sit on a wash so they read as pages; a hairline replaces docx-preview's
 * drop shadow, because nothing on this screen is lifted. */
.wordview .docx-wrapper {
  background: var(--wash);
  border-radius: 14px;
  padding: 24px 12px 40px;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 18px;
}
.wordview .docx-wrapper > section.docx {
  box-shadow: none;
  border: 1px solid var(--hairline);
  border-radius: 2px;
  max-width: 100%;
}

/* Text | Word — which face of the document the pane shows */
.viewtoggle {
  display: flex;
  gap: 2px;
  padding: 3px;
  border: 1px solid var(--hairline);
  border-radius: 999px;
}
.vt {
  font-family: var(--display);
  font-weight: 700;
  font-size: .8125rem;
  letter-spacing: -.01em;
  line-height: 1;
  background: transparent;
  color: var(--ink-muted);
  border: 0;
  border-radius: 999px;
  padding: 7px 14px;
  cursor: pointer;
  transition: background 120ms var(--ease), color 120ms var(--ease);
}
.vt:hover { color: var(--ink); }
.vt.is-active { background: var(--ink); color: var(--paper); }

/* ── formatting toolbar (report pane header) ─────────────────────────── */

.fmtbar { display: flex; align-items: center; gap: 2px; }
.fmt {
  font-family: var(--read);      /* B / I / U are the document's, not the UI's */
  font-size: 15px;
  width: 30px;
  height: 30px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  background: transparent;
  color: var(--ink-muted);
  border: 1px solid transparent;
  border-radius: 8px;
  cursor: pointer;
  padding: 0;
  transition: border-color 120ms var(--ease), color 120ms var(--ease);
}
.fmt:hover { border-color: var(--hairline); color: var(--ink); }
.fmt b { font-weight: 600; }
.fmt.swatch i {
  width: 12px;
  height: 12px;
  border-radius: 50%;
  display: block;
  font-style: normal;
}
.fmt.swatch i.marker {
  width: 14px;
  border-radius: 2px;
  background: linear-gradient(transparent 45%, #FFF1A8 45%);
  box-shadow: inset 0 -1px 0 var(--ash);
}
/* The colours, behind one ink "A" — Word's own text-colour glyph, drawn in
   ink with an ink rule under it, so the resting toolbar carries no hue. The
   popover is a hairline card, not a lifted menu: nothing here has a shadow. */
.fmt-colour { position: relative; display: inline-flex; }
.fmt-a {
  display: inline-flex;
  flex-direction: column;
  align-items: center;
  gap: 1px;
  font-weight: 600;
  line-height: 1;
}
.fmt-a::after {
  content: "";
  width: 12px;
  height: 2px;
  border-radius: 1px;
  background: currentColor;
}
.fmt[aria-expanded="true"] { border-color: var(--ink); color: var(--ink); }
.fmt-pop {
  position: absolute;
  top: calc(100% + 6px);
  left: 50%;
  transform: translateX(-50%);
  display: flex;
  gap: 2px;
  padding: 4px;
  background: var(--paper);
  border: 1px solid var(--hairline);
  border-radius: 12px;
  z-index: 4;
}
.fmt-sep { width: 1px; height: 16px; background: var(--hairline); margin: 0 5px; }

.fillbtn { min-height: 36px; font-size: .875rem; padding: 0 16px; }

/* ── the right stack: ONE pane — the dictation ───────────────────────── */

.side {
  display: grid;
  grid-template-rows: minmax(0, 1fr);
  min-height: 0;
}
.side > .pane { padding: 0 22px 18px; }

.dictation { display: grid; grid-template-rows: auto minmax(0, 1fr) auto; }

.panehead {
  font-weight: 700;
  font-size: 1rem;
  letter-spacing: -.015em;
  color: var(--ink);
  margin: 0;
  padding: 18px 0 14px;
  border-bottom: 1px solid var(--hairline);
  background: var(--paper);
  position: sticky;
  top: 0;
  z-index: 2;
  display: flex;
  align-items: baseline;
  gap: 10px;
}
/* honesty without noise: the chatter the engine dropped, as one quiet count */
.panehead .ignored {
  font-weight: 500;
  font-size: .8125rem;
  letter-spacing: 0;
  color: var(--ink-muted);
  border: 1px solid var(--hairline);
  border-radius: 999px;
  padding: 2px 10px;
  cursor: help;
  margin-left: auto;
  font-variant-numeric: tabular-nums;
}

/* The transcript is a DOCUMENT, not a log: each utterance its own sentence
 * on its own line, a paragraph per Generate batch with a hairline between
 * batches, measurements and the template's own terms marked where the words
 * actually said them, corrector repairs struck through in place. Humant, not
 * the reading face: this is what was heard, the report is what will be
 * signed, and the eye should never confuse the two.
 *
 * The capital and the full stop are DRAWN (app.js: span.cap, .open-end), not
 * typed into the words: the words are what a hand edit is diffed against,
 * and joined on without either, the pane read as one lower-case run-on. */
.transcript-doc {
  overflow: auto;
  font-size: 1rem;
  line-height: 1.6;
  color: var(--ink);
  padding: 16px 4px 0 0;
  overflow-wrap: anywhere;
}
.transcript-doc .para { margin: 0; padding: 0 0 12px; }
.transcript-doc .para + .para { border-top: 1px solid var(--hairline); padding-top: 12px; }
.transcript-doc .sent { display: block; border-radius: 4px; }
.transcript-doc .sent + .sent { margin-top: 6px; }
.transcript-doc .sent .cap { text-transform: uppercase; }
.transcript-doc .sent.open-end::after { content: "."; }
/* A fact is marked by weight alone, 700 on the 500 around it. The wash is
   kept for "the engine amended the document"; bold-on-wash for every term
   read as bold word soup. nowrap, because "10.2" and "cm" split across two
   lines at 768px. */
.transcript-doc mark.fact {
  background: none;
  color: inherit;
  font-weight: 700;
  white-space: nowrap;
}
/* Before the first words: what to do, near the top, in statement grey — the
   words app.js sets for the mode (data-empty). No graphic. */
.transcript-doc.is-empty::after {
  content: attr(data-empty);
  display: block;
  max-width: 36ch;
  font-weight: 500;
  font-size: .9375rem;
  line-height: 1.55;
  color: var(--statement);
  text-wrap: pretty;             /* no one-word last line ("it.") at 768px */
  pointer-events: none;
  user-select: none;
  -webkit-user-select: none;
}
.transcript-doc s.corrected {
  color: var(--statement);
  text-decoration: line-through;
  text-decoration-color: var(--ink-muted);
  text-decoration-thickness: 1px;
}
.transcript-doc .divider {
  color: var(--statement);
  font-size: .8125rem;
  text-align: center;
  margin: 6px 0 12px;
}
/* the transcript takes typed edits too — same caret language as the report */
.transcript-doc[contenteditable="true"] { caret-color: var(--ink); outline: none; }

.composer {
  display: flex;
  gap: 8px;
  padding-top: 14px;
  border-top: 1px solid var(--hairline);
}
.composer input {
  flex: 1;
  min-width: 0;
  font-family: var(--display);
  font-weight: 500;
  font-size: .9375rem;
  color: var(--ink);
  background: var(--paper);
  border: 1px solid var(--hairline);
  border-radius: 999px;
  padding: 0 16px;
  min-height: 40px;
}
.composer input::placeholder { color: var(--statement); }
.composer input:focus { outline: none; border-color: var(--ink); }

/* ── the dictation pill ──────────────────────────────────────────────── */
/* FIXED SIZE — the label changes, the geometry does not; a control that
 * resizes as it talks reads as unstable. States on data-state: locked
 * (loading, the reason on it) · paused (hairline, tap to start) · armed (ink
 * outline, the bars follow the room) · hearing (the voice gate is open: the
 * whole pill inverts to ink — the one inversion on the screen, for the one
 * thing that must be unmistakable). */

.hearing {
  display: flex;
  align-items: center;
  gap: 12px;
  cursor: pointer;
  user-select: none;
  -webkit-user-select: none;
}

main > .dictbar {
  position: fixed;
  left: 50%;
  bottom: 16px;
  transform: translateX(-50%);
  width: min(360px, calc(100vw - 32px));   /* constant, in every state */
  height: 52px;
  z-index: 12;
  background: var(--paper);
  border: 1px solid var(--hairline);
  border-radius: 999px;
  padding: 0 20px 0 7px;
  margin: 0;
  transition: background 160ms var(--ease), border-color 160ms var(--ease);
}
@media (hover: hover) {
  .hearing[data-state="paused"]:hover { border-color: var(--ink); }
}

.hearing-label {
  flex: none;
  width: 104px;
  font-weight: 700;
  font-size: .8125rem;
  letter-spacing: -.01em;
  color: var(--ink-muted);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;       /* geometry never moves */
}
/* app.js writes the states in lower case; the site sets labels in sentence case */
.hearing-label::first-letter { text-transform: uppercase; }

/* The meter: fixed geometry, bars animated with `transform: scaleY` only —
   no layout per frame, so it stays smooth while the models work. */
.wave {
  display: flex;
  align-items: center;
  gap: 2px;
  min-width: 0;
  overflow: hidden;
}
.dictbar .wave { flex: 1; height: 30px; }
.wave i {
  flex: 1;
  min-width: 1px;
  border-radius: 2px;
  background: var(--hairline);
}
.dictbar .wave i {
  height: 30px;                  /* full height; scaleY does the moving */
  transform: scaleY(.06);
  transform-origin: 50% 50%;
  transition: none;              /* the rAF loop eases; CSS must not fight it */
  will-change: transform;
}

/* the record button inside the pill: collect's own — an ink disc with a
 * paper dot says "press to record"; while live the dot becomes two bars. */
.recbtn {
  flex: none;
  width: 38px;
  height: 38px;
  border-radius: 50%;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  background: var(--ink);
  transition: background 160ms var(--ease);
}
.recbtn::before {
  content: "";
  width: 11px;
  height: 11px;
  border-radius: 50%;
  background: var(--paper);
}

.hearing[data-state="armed"] { border-color: var(--ink); }
.hearing[data-state="armed"] .hearing-label { color: var(--ink); }
.hearing[data-state="armed"] .wave i { background: var(--statement); }

.hearing[data-state="hearing"], .hearing.on {
  background: var(--ink);
  border-color: var(--ink);
  animation: breathe 1.8s var(--ease) infinite;
}
.hearing[data-state="hearing"] .hearing-label, .hearing.on .hearing-label { color: var(--paper); }
.hearing[data-state="hearing"] .wave i, .hearing.on .wave i { background: var(--paper); }
.hearing[data-state="hearing"] .recbtn, .hearing.on .recbtn { background: var(--paper); }

.hearing[data-state="armed"] .recbtn::before,
.hearing[data-state="hearing"] .recbtn::before, .hearing.on .recbtn::before {
  /* two pause bars, drawn with a gradient so no extra element is needed */
  width: 12px;
  height: 12px;
  border-radius: 1px;
  background: linear-gradient(90deg,
    var(--paper) 0 4px, transparent 4px 8px, var(--paper) 8px 12px);
}
.hearing[data-state="hearing"] .recbtn::before, .hearing.on .recbtn::before {
  background: linear-gradient(90deg,
    var(--ink) 0 4px, transparent 4px 8px, var(--ink) 8px 12px);
}

/* locked: the template is chosen but the engine is not ready — visibly an
   instrument, visibly not listening, the reason where the state would be. */
.hearing[data-state="locked"] { cursor: progress; border-color: var(--hairline); }
.hearing[data-state="locked"] .recbtn { background: var(--wash); }
.hearing[data-state="locked"] .recbtn::before { background: var(--ash); }
.hearing[data-state="locked"] .wave { display: none; }
.hearing[data-state="locked"] .hearing-label { color: var(--statement); width: auto; flex: 1; }

/* ── overlays ────────────────────────────────────────────────────────── */

.overlay {
  position: fixed;
  inset: 0;
  z-index: 20;
  display: grid;
  padding: 24px;
  overflow-y: auto;
  background: rgba(255, 255, 255, .78);
  -webkit-backdrop-filter: blur(6px);
  backdrop-filter: blur(6px);
}
/* margin:auto centres the card and, unlike place-items, never clips the top
   of one taller than the window */
.overlay > * { margin: auto; }

/* sign-in and the chooser are pages, not pop-ups: a sheet on plain paper,
   the way collect/static opens */
.sheet-overlay {
  background: var(--paper);
  -webkit-backdrop-filter: none;
  backdrop-filter: none;
  padding: 24px 20px;
}
.sheet { width: min(440px, 100%); padding: 32px 0 48px; }
.sheet--wide { width: min(760px, 100%); }
.sheet .brand { display: inline-flex; margin-bottom: 36px; }

.sheet h1 {
  font-weight: 700;
  font-size: clamp(2rem, 4.4vw, 2.75rem);
  line-height: 1.05;
  letter-spacing: -.035em;
  margin: 0 0 14px;
  text-wrap: balance;
}
.lede {
  font-size: 1.0625rem;
  color: var(--ink-muted);
  margin: 0 0 18px;
  max-width: 52ch;
}

/* Where the words go — the chooser's one statement of fact about privacy,
   boxed in a hairline the way collect/static boxes its consent. */
.where {
  font-size: .9375rem;
  line-height: 1.55;
  color: var(--ink-muted);
  border: 1px solid var(--hairline);
  border-radius: 14px;
  padding: 14px 16px;
  margin: 0 0 26px;
  max-width: 62ch;
}
.where b { color: var(--ink); font-weight: 700; }

.hint { font-size: .875rem; color: var(--ink-muted); margin: 16px 0 0; }

/* ── template picker: the whole lab folder, every entry equal ────────── */

.tpl-catalog {
  display: flex;
  flex-direction: column;
  border-top: 1px solid var(--hairline);
  margin-bottom: 22px;
}
.tpl-family {
  display: grid;
  grid-template-columns: 132px minmax(0, 1fr);
  gap: 10px 16px;
  align-items: baseline;
  padding: 16px 0;
  border-bottom: 1px solid var(--hairline);
}
.tpl-family-name {
  font-weight: 700;
  font-size: .9375rem;
  letter-spacing: -.015em;
  color: var(--ink);
  margin: 0;
}
.tpl-chips { display: flex; flex-wrap: wrap; gap: 8px; }
.tpl-chips:only-child { grid-column: 1 / -1; }   /* the file:// fallback has no family name */
.tpl-open {
  font-family: var(--display);
  font-weight: 700;
  font-size: .9375rem;
  letter-spacing: -.015em;
  line-height: 1;
  min-height: 40px;
  padding: 0 16px;
  color: var(--ink);
  background: var(--paper);
  border: 1px solid var(--hairline);
  border-radius: 999px;
  cursor: pointer;
  transition: border-color 120ms var(--ease), background 120ms var(--ease), color 120ms var(--ease);
}
.tpl-open:active { background: var(--ink); color: var(--paper); border-color: var(--ink); }
@media (hover: hover) {
  .tpl-open:hover { border-color: var(--ink); }
}
.tpl-reason { margin: 16px 0; }

.tick {
  display: flex;
  gap: 10px;
  align-items: center;
  font-size: .9375rem;
  color: var(--ink);
  cursor: pointer;
}
.tick input { width: 18px; height: 18px; margin: 0; accent-color: var(--ink); }

/* ── sign-in form: collect/static's field ────────────────────────────── */

.signin { display: grid; gap: 16px; margin-top: 8px; }
.field {
  display: block;
  font-size: .8125rem;
  font-weight: 700;
  letter-spacing: -.01em;
  color: var(--ink-muted);
}
.field input {
  display: block;
  width: 100%;
  margin-top: 6px;
  font-family: var(--display);
  font-weight: 500;
  font-size: 16px;               /* 16px, or iOS zooms the page on focus */
  color: var(--ink);
  background: var(--paper);
  border: 1px solid var(--hairline);
  border-radius: 12px;
  padding: 12px 14px;
  appearance: none;
}
.field input::placeholder { color: var(--statement); }
.field input:focus { outline: none; border-color: var(--ink); }

/* ── finish modal ────────────────────────────────────────────────────── */

.card {
  background: var(--paper);
  border: 1px solid var(--hairline);
  border-radius: var(--card);
}
.finish-card {
  width: min(880px, 100%);
  max-height: calc(100vh - 48px);
  display: grid;
  grid-template-rows: auto minmax(0, 1fr) auto;
  padding: 0 30px;
}
.finish-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  padding: 20px 0 16px;
  border-bottom: 1px solid var(--hairline);
}
.finish-title {
  font-weight: 700;
  font-size: 1.25rem;
  letter-spacing: -.015em;
  line-height: 1.25;
  margin: 0;
}
.finish-body { overflow: auto; padding: 22px 0; }
.finaltext {
  font-family: var(--read);
  font-size: 16px;
  line-height: 1.7;
  color: var(--ink);
  white-space: pre-wrap;
  overflow-wrap: anywhere;
  margin: 0;
  max-width: 76ch;
}
.finish-foot {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 8px;
  padding: 16px 0 20px;
  border-top: 1px solid var(--hairline);
}
.finish-foot .grow { flex: 1; }
.exported { font-size: .8125rem; color: var(--ink); overflow-wrap: anywhere; }
.editnote { font-size: .8125rem; color: var(--ink-muted); }

/* ── toasts: collect/static's ink capsule ────────────────────────────── */
/* In the pill's gutter, beside the pill, where there is nothing to cover:
   a toast over the composer hid the one field a typed-mode radiologist was
   using. Where the gutter is too narrow to share (below 1100px), the toasts
   centre above the pill instead. */

.toasts {
  position: fixed;
  right: 20px;
  bottom: 16px;
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  gap: 8px;
  z-index: 30;
  max-width: min(380px, calc(50vw - 208px));
}
@media (max-width: 1100px) {
  .toasts {
    right: auto;
    left: 50%;
    transform: translateX(-50%);
    bottom: 80px;
    align-items: center;
    width: min(420px, calc(100vw - 32px));
    max-width: none;
  }
}
.toast {
  font-size: .875rem;
  line-height: 1.45;
  background: var(--ink);
  color: var(--paper);
  border-radius: 16px;
  padding: 11px 16px;
  animation: tick-in .22s var(--ease);
}
.toast.bad::before {
  content: "";
  display: inline-block;
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: var(--stop);
  margin: 0 8px 1px 0;
  vertical-align: middle;
}
@keyframes tick-in {
  from { opacity: 0; transform: translateY(6px); }
  to   { opacity: 1; transform: none; }
}

/* ── tablet: the panes stack, report above dictation ─────────────────── */
/* Below ~900px two columns leave the report under 60 characters a line —
   too narrow to read a template in. Stacked, the report keeps the width and
   takes whatever height is left; the dictation is capped near a third of the
   screen. At 768×1024 a 3fr/2fr split gave the report ~540px and a mostly
   empty dictation box ~360px — the thing being produced got the smaller
   share. */

@media (max-width: 900px) {
  :root { --topbar-h: 58px; }
  .topbar { padding: 0 16px; gap: 10px; }
  main {
    grid-template-columns: minmax(0, 1fr);
    grid-template-rows: minmax(0, 1fr) minmax(200px, 30vh);
  }
  /* The pill's gutter is reserved only while there is a pill: in typed mode
     (and the ?mock=1 preview) it is hidden, and the reserved strip was ~80px
     of blank page under both panes. The toasts here float 80px up, above
     the composer, so nothing lands on the field being typed in. */
  main:has(> .dictbar[hidden]) { padding-bottom: var(--gap); }
  .report { padding: 0 20px 48px; }
  .side > .pane { padding: 0 18px 14px; }
  .tpl-family { grid-template-columns: minmax(0, 1fr); gap: 10px; }
  .tpl-chips:only-child { grid-column: auto; }
  .finish-card { padding: 0 20px; }
}
@media (max-width: 640px) {
  .brand__word, .brand__rule { display: none; }
  .sheet .brand__word { display: inline; }
  .accession { display: none; }
}

/* Reduced motion keeps the meaning and drops the movement: animations end
   on their first frame, and a correction holds its wash instead of fading. */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: .001ms !important;
    animation-iteration-count: 1 !important;
    transition: none !important;
  }
  .flash-amber { background: var(--hairline); }
}
