/* beoffline — cold storage.
   Everything at rest is cool and grey-blue. Warmth appears only where bytes
   are actually moving, then cools back down once the file has landed.
   System fonts only: an offline tool has no business fetching a webfont. */

:root {
  color-scheme: light dark;

  /* cool, at rest */
  --ice:      #eef2f6;
  --surface:  #ffffff;
  --surface-2:#f6f9fb;
  --frost:    #dae2ea;
  --frost-2:  #c3cfdb;
  --steel:    #5e7186;
  --ink:      #131a21;

  /* warm, in transit — used nowhere else */
  --ember:    #c26a25;
  --ember-bg: rgba(217, 133, 59, 0.16);

  --fail:     #a4483a;
  --fail-bg:  rgba(164, 72, 58, 0.10);

  --lift:     0 1px 2px rgba(19, 26, 33, 0.04), 0 18px 40px -24px rgba(19, 26, 33, 0.28);

  --sans: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue",
          Arial, "Noto Sans", sans-serif;
  --mono: ui-monospace, SFMono-Regular, "SF Mono", "Cascadia Mono", "Segoe UI Mono",
          Menlo, Consolas, "Liberation Mono", monospace;

  --radius: 10px;
  --radius-sm: 7px;
  --gutter: clamp(1.25rem, 4vw, 3rem);
  --shell: 62rem;
  --ease: cubic-bezier(0.22, 0.75, 0.35, 1);
}

/* The dark palette is written twice: once for a chosen theme, once for a
   browser that prefers dark and has been given no choice to honour. CSS has no
   way to say "these two must agree", so tests/test_theme.py says it instead. */

:root[data-theme="dark"] {
  color-scheme: dark;

  --ice:      #0e1419;
  --surface:  #161e26;
  --surface-2:#1b242d;
  --frost:    #26323d;
  --frost-2:  #33424f;
  --steel:    #8194a7;
  --ink:      #e4ebf1;

  --ember:    #e2954e;
  --ember-bg: rgba(217, 133, 59, 0.20);

  --fail:     #d1705e;
  --fail-bg:  rgba(209, 112, 94, 0.13);

  --lift:     0 1px 2px rgba(0, 0, 0, 0.28), 0 18px 40px -24px rgba(0, 0, 0, 0.55);
}

/* Chosen light has to say so out loud: left at `light dark`, the native
   controls and scrollbars would still come up dark on a dark desktop. */
:root[data-theme="light"] {
  color-scheme: light;
}

@media (prefers-color-scheme: dark) {
  :root:not([data-theme]) {
    color-scheme: dark;

    --ice:      #0e1419;
    --surface:  #161e26;
    --surface-2:#1b242d;
    --frost:    #26323d;
    --frost-2:  #33424f;
    --steel:    #8194a7;
    --ink:      #e4ebf1;

    --ember:    #e2954e;
    --ember-bg: rgba(217, 133, 59, 0.20);

    --fail:     #d1705e;
    --fail-bg:  rgba(209, 112, 94, 0.13);

    --lift:     0 1px 2px rgba(0, 0, 0, 0.28), 0 18px 40px -24px rgba(0, 0, 0, 0.55);
  }
}

* { box-sizing: border-box; }

/* `hidden` has to beat whatever a component's own class says about display.
   Both are one class worth of specificity, so without !important it comes
   down to which rule sits lower in this file — and a component added at the
   bottom silently starts showing things that are meant to be hidden. */
[hidden] { display: none !important; }

/* Form controls do not inherit the page's font on their own: left alone, a
   button falls back to the browser's own interface face and looks like it
   wandered in from another program. Setting it once here means a control can
   never be one restyling away from that. */
button, input, select, textarea, optgroup { font: inherit; color: inherit; }

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

body {
  margin: 0;
  background: var(--ice);
  color: var(--ink);
  font-family: var(--sans);
  font-size: 16px;
  line-height: 1.5;
  -webkit-font-smoothing: antialiased;
}

.mono {
  font-family: var(--mono);
  font-size: 0.75rem;
  letter-spacing: 0.02em;
  font-variant-numeric: tabular-nums;
}

.sr-only {
  position: absolute; width: 1px; height: 1px;
  padding: 0; margin: -1px; overflow: hidden;
  clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}

.dot { opacity: 0.4; padding: 0 0.45em; }

:where(a, button, input, [tabindex]):focus-visible {
  outline: 2px solid var(--ember);
  outline-offset: 2px;
  border-radius: 4px;
}

/* ---------------------------------------------------------------- top bar */

.bar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  padding: 0.9rem var(--gutter);
  border-bottom: 1px solid var(--frost);
  background: var(--surface);
  position: sticky;
  top: 0;
  z-index: 10;
}

.wordmark {
  font-size: 1.25rem;
  font-weight: 700;
  letter-spacing: -0.03em;
  line-height: 1;
}
.wordmark__be { color: var(--steel); }
.wordmark__offline { color: var(--ink); }
/* The full stop takes the same grey as the first syllable, so the word is
   bracketed by the quiet colour and only the middle of it is stated plainly. */
.wordmark__dot { color: var(--steel); }

.bar__stats { color: var(--steel); }

.bar__right { display: flex; flex-wrap: wrap; align-items: center; gap: 0.75rem 1rem; }

/* One row of mutually exclusive options — language, theme. Quiet by design:
   a setting you touch once, not a feature to advertise. */
.pick {
  display: flex;
  border: 1px solid var(--frost);
  border-radius: var(--radius-sm);
  overflow: hidden;
}

.pick__opt {
  font-size: 0.75rem;
  line-height: 1.4;
  padding: 0.3rem 0.6rem;
  border: 0;
  background: transparent;
  color: var(--steel);
  cursor: pointer;
  transition: background 0.12s var(--ease), color 0.12s var(--ease);
}
.pick__opt + .pick__opt { border-left: 1px solid var(--frost); }
.pick__opt:hover { color: var(--ink); }
.pick__opt[aria-pressed="true"] { background: var(--ink); color: var(--ice); }

/* ----------------------------------------------------------------- layout */

main {
  max-width: var(--shell);
  margin: 0 auto;
  padding: clamp(1.5rem, 4vw, 2.5rem) var(--gutter) 4rem;
  display: flex;
  flex-direction: column;
  gap: clamp(1.15rem, 2.4vw, 1.6rem);
}

/* ------------------------------------------------------------------- tabs */

/* Where you are, not what you picked. The chips, the intake modes and the
   corner pickers all fill with ink to say "this is the one you chose"; a tab
   is not a choice about the work, it is a place, so it is marked by an
   underline instead and never borrows that filled look. */

.tabs {
  display: flex;
  gap: clamp(0.9rem, 3vw, 1.5rem);
  /* Four short words fit everywhere down to the narrowest phone; this is only
     here so a longer translation slides sideways instead of pushing the page
     wider than the screen. */
  overflow-x: auto;
  scrollbar-width: none;
  /* The rule under the strip, drawn inside rather than as a border: `overflow`
     on one axis makes the other clip too, which would trim a tab reaching down
     over a real border. Inset, it is painted under the tabs, so the selected
     one's underline covers it and the rest let it show through. */
  box-shadow: inset 0 -1px 0 var(--frost);
}
.tabs::-webkit-scrollbar { display: none; }

.tabs__tab {
  display: inline-flex;
  align-items: center;
  gap: 0.45rem;
  flex: none;
  padding: 0.6rem 0.2rem;
  border: 0;
  border-bottom: 2px solid transparent;
  background: none;
  color: var(--steel);
  font-size: 0.9375rem;
  font-weight: 550;
  letter-spacing: -0.01em;
  white-space: nowrap;
  cursor: pointer;
  transition: color 0.12s var(--ease), border-color 0.12s var(--ease);
}
.tabs__tab:hover { color: var(--ink); }
.tabs__tab[aria-selected="true"] {
  color: var(--ink);
  border-bottom-color: var(--ink);
}

/* A number that has to stay visible from the other tabs, or moving away from
   a panel means losing sight of what it was doing. */
.tabs__count {
  font-family: var(--mono);
  font-size: 0.6875rem;
  font-variant-numeric: tabular-nums;
  line-height: 1;
  padding: 0.25em 0.5em;
  border-radius: 999px;
  background: var(--frost);
  color: var(--steel);
}

/* The one warm number on the page: bytes actually moving. Everything else
   waiting stays cool. */
.tabs__count[data-tone="moving"] {
  background: var(--ember-bg);
  color: var(--ember);
}

/* Panels carry the same rhythm the page had when it was one long scroll. */
.panel {
  display: flex;
  flex-direction: column;
  gap: clamp(1.15rem, 2.4vw, 1.6rem);
}

/* ----------------------------------------------------------------- intake */

.intake h1 {
  margin: 0 0 1.1rem;
  font-size: clamp(1.75rem, 5vw, 2.6rem);
  line-height: 1.02;
  font-weight: 700;
  letter-spacing: -0.035em;
}

.intake__modes {
  display: flex;
  gap: 0.35rem;
  margin-bottom: 0.5rem;
}

.intake__mode {
  padding: 0.35rem 0.7rem;
  border: 1px solid var(--frost-2);
  border-radius: var(--radius-sm);
  background: transparent;
  color: var(--steel);
  cursor: pointer;
  transition: background 0.12s var(--ease), color 0.12s var(--ease),
              border-color 0.12s var(--ease);
}
.intake__mode:hover { color: var(--ink); border-color: var(--ink); }
.intake__mode[aria-selected="true"] {
  background: var(--ink);
  border-color: var(--ink);
  color: var(--ice);
}

.intake__form {
  display: flex;
  gap: 0.5rem;
  align-items: stretch;
  flex-wrap: wrap;
}

.intake__input {
  flex: 1 1 22rem;
  min-width: 0;
  font-size: 0.875rem;
  padding: 0.6rem 0.85rem;
  border: 1px solid var(--frost-2);
  border-radius: var(--radius);
  background: var(--surface);
  color: var(--ink);
  transition: border-color 0.15s var(--ease), box-shadow 0.15s var(--ease);
}
.intake__input::placeholder { color: var(--steel); opacity: 0.7; }
.intake__input:focus {
  outline: none;
  border-color: var(--ember);
  box-shadow: 0 0 0 3px var(--ember-bg);
}

.intake__note {
  margin: 0.9rem 0 0;
  min-height: 1.2em;
  color: var(--steel);
}
.intake__note[data-tone="error"] { color: var(--fail); }
.intake__note[data-tone="busy"] { color: var(--ember); }

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

.btn {
  font: inherit;
  font-size: 0.875rem;
  font-weight: 550;
  padding: 0.6rem 1.1rem;
  border-radius: var(--radius);
  border: 1px solid transparent;
  cursor: pointer;
  white-space: nowrap;
  transition: background 0.15s var(--ease), border-color 0.15s var(--ease),
              color 0.15s var(--ease), opacity 0.15s var(--ease);
}
.btn:disabled { opacity: 0.4; cursor: not-allowed; }

.btn--primary {
  background: var(--ink);
  color: var(--ice);
}
.btn--primary:not(:disabled):hover { background: var(--ember); }

.btn--ghost {
  background: transparent;
  border-color: var(--frost-2);
  color: var(--steel);
}
.btn--ghost:not(:disabled):hover { border-color: var(--ink); color: var(--ink); }

.btn--sm { padding: 0.4rem 0.7rem; font-size: 0.75rem; font-family: var(--mono); }

/* ---------------------------------------------------------------- preview */

.preview {
  border: 1px solid var(--frost);
  border-radius: var(--radius);
  background: var(--surface);
  overflow: hidden;
  animation: settle 0.35s var(--ease);
}

@keyframes settle {
  from { opacity: 0; transform: translateY(-6px); }
  to   { opacity: 1; transform: none; }
}

.preview__head {
  display: flex;
  gap: 0.85rem;
  align-items: center;
  padding: 0.8rem;
  border-bottom: 1px solid var(--frost);
}

.preview__thumb {
  flex: 0 0 auto;
  width: 168px;
  aspect-ratio: 16 / 9;
  border-radius: var(--radius-sm);
  overflow: hidden;
  background: var(--surface-2);
}
.preview__thumb img { width: 100%; height: 100%; object-fit: cover; display: block; }

.preview__meta { flex: 1 1 12rem; min-width: 0; }

.preview__name {
  margin: 0 0 0.3rem;
  font-size: 1.0625rem;
  font-weight: 600;
  letter-spacing: -0.015em;
  line-height: 1.3;
  overflow-wrap: anywhere;
}

.preview__sub { margin: 0; color: var(--steel); }

.picker {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(16rem, 1fr));
}

.picker__group {
  border: 0;
  border-top: 1px solid transparent;
  margin: 0;
  padding: 0.75rem 0.85rem;
}
.picker__group + .picker__group { border-left: 1px solid var(--frost); }

.picker__legend {
  padding: 0;
  margin-bottom: 0.7rem;
  color: var(--steel);
  text-transform: uppercase;
  letter-spacing: 0.1em;
}

.chips { display: flex; flex-wrap: wrap; gap: 0.4rem; }

.chip {
  font-family: var(--mono);
  font-size: 0.75rem;
  font-variant-numeric: tabular-nums;
  padding: 0.45rem 0.7rem;
  border: 1px solid var(--frost-2);
  border-radius: var(--radius-sm);
  background: var(--surface);
  color: var(--ink);
  cursor: pointer;
  display: inline-flex;
  align-items: baseline;
  gap: 0.4em;
  transition: border-color 0.12s var(--ease), background 0.12s var(--ease);
}
.chip:hover { border-color: var(--steel); }
.chip__note { color: var(--steel); font-size: 0.6875rem; }

.chip[aria-checked="true"] {
  background: var(--ink);
  border-color: var(--ink);
  color: var(--ice);
}
.chip[aria-checked="true"] .chip__note { color: var(--ice); opacity: 0.6; }

.subs {
  display: flex;
  align-items: center;
  gap: 0.75rem;
  flex-wrap: wrap;
  padding: 0.65rem 0.85rem;
  border-top: 1px solid var(--frost);
}

.subs__note { color: var(--steel); }
.subs__note[data-tone="weak"] { color: var(--ember); }

/* An unobtainable track reads as unavailable, not as something you forgot. */
.toggle input:disabled + .toggle__track { opacity: 0.4; }
.toggle:has(input:disabled) { cursor: not-allowed; opacity: 0.65; }

.preview__go {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  flex-wrap: wrap;
  padding: 0.7rem 0.85rem;
  border-top: 1px solid var(--frost);
  background: var(--surface-2);
}
.preview__choice { margin: 0; color: var(--steel); }

/* ------------------------------------------------------------------- bulk */

.bulk {
  border: 1px solid var(--frost);
  border-radius: var(--radius);
  background: var(--surface);
  overflow: hidden;
  animation: settle 0.35s var(--ease);
}

.bulk__input {
  display: block;
  width: 100%;
  border: 0;
  border-bottom: 1px solid var(--frost);
  border-radius: 0;
  padding: 1rem;
  font-size: 0.8125rem;
  line-height: 1.7;
  background: var(--surface);
  color: var(--ink);
  resize: vertical;
  min-height: 8rem;
}
.bulk__input::placeholder { color: var(--steel); opacity: 0.65; }
.bulk__input:focus { outline: none; background: var(--surface-2); }

.bulk__count {
  margin: 0;
  padding: 0.6rem 1rem;
  color: var(--steel);
  border-bottom: 1px solid var(--frost);
}
.bulk__count[data-tone="ready"] { color: var(--ink); }

/* --------------------------------------------------------------- channels */

.channels__add {
  border: 1px solid var(--frost);
  border-radius: var(--radius);
  background: var(--surface);
  overflow: hidden;
  margin: 0.9rem 0 1.2rem;
  animation: settle 0.3s var(--ease);
}

.channels__hint {
  margin: 0;
  padding: 0.9rem 1rem 0.5rem;
  color: var(--steel);
  font-size: 0.875rem;
}

/* The accepted forms, shown rather than described: this is the one place a
   person has to know what to paste. */
.channels__forms {
  list-style: none;
  margin: 0;
  padding: 0 1rem 0.5rem;
}
.channels__forms li {
  display: flex;
  gap: 0.6rem;
  align-items: baseline;
  flex-wrap: wrap;
  padding: 0.2rem 0;
  color: var(--steel);
  font-size: 0.6875rem;
}
.channels__forms code {
  font-family: var(--mono);
  color: var(--ink);
  background: var(--surface-2);
  border: 1px solid var(--frost);
  border-radius: 4px;
  padding: 0.1em 0.4em;
  white-space: nowrap;
}

.channels__add .bulk__input { border-top: 1px solid var(--frost); min-height: 5rem; }

.row__channel { color: var(--steel); }
.row--off .row__title { opacity: 0.5; }

/* ------------------------------------------------------------------ shelf */

.shelf__head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  flex-wrap: wrap;
  padding-bottom: 0.5rem;
  border-bottom: 1px solid var(--frost);
}

.shelf__title {
  margin: 0;
  color: var(--steel);
  text-transform: uppercase;
  letter-spacing: 0.14em;
  font-size: 0.75rem;
  font-weight: inherit;
}

/* A heading you can fold. It stays a heading for the document outline; the
   button inside it is what takes the click. */
.fold {
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
  padding: 0;
  border: 0;
  background: none;
  color: inherit;
  font: inherit;
  text-transform: inherit;
  letter-spacing: inherit;
  cursor: pointer;
}
.fold:hover { color: var(--ink); }

.fold__mark {
  width: 0;
  height: 0;
  border-left: 4px solid currentColor;
  border-top: 3.5px solid transparent;
  border-bottom: 3.5px solid transparent;
  transform: rotate(90deg);
  transition: transform 0.18s var(--ease);
}
.fold[aria-expanded="false"] .fold__mark { transform: none; }

.fold[aria-expanded="false"] + .shelf__tools { opacity: 0.55; }

[data-folded] { display: none; }

@media (prefers-reduced-motion: reduce) {
  .fold__mark { transition: none; }
}

.shelf__tools { display: flex; align-items: center; gap: 1rem; flex-wrap: wrap; }

.toggle {
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
  color: var(--steel);
  cursor: pointer;
  user-select: none;
}
.toggle input { position: absolute; opacity: 0; width: 0; height: 0; }

.toggle__track {
  width: 30px; height: 17px;
  border-radius: 9px;
  background: var(--frost-2);
  padding: 2px;
  flex: none;
  transition: background 0.18s var(--ease);
}
.toggle__dot {
  display: block;
  width: 13px; height: 13px;
  border-radius: 50%;
  background: var(--surface);
  transition: transform 0.18s var(--ease);
}
.toggle input:checked + .toggle__track { background: var(--ember); }
.toggle input:checked + .toggle__track .toggle__dot { transform: translateX(13px); }
.toggle input:focus-visible + .toggle__track {
  outline: 2px solid var(--ember); outline-offset: 2px;
}

.shelf__empty {
  margin: 0.9rem 0 0;
  color: var(--steel);
  opacity: 0.75;
}

/* The signature: a download has no separate progress bar. The row itself
   fills with warmth from the left as the bytes arrive, then cools on landing. */

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

.row {
  --fill: 0%;
  display: grid;
  /* Wide enough for a mark and a percentage, and no wider. It used to hold the
     longest status word too, which meant every finished row — most of them —
     reserved a band of empty column for a word that is no longer shown. */
  grid-template-columns: 3rem minmax(0, 1fr) auto;
  align-items: center;
  gap: 0.85rem;
  padding: 0.6rem 0.75rem;
  border-bottom: 1px solid var(--frost);
  background:
    linear-gradient(to right, var(--ember-bg) var(--fill), transparent var(--fill));
  transition: background 0.6s linear;
}
.row:first-child { border-top: 1px solid transparent; }

.row--settling { transition: background 0.7s var(--ease); }

.row__status {
  font-family: var(--mono);
  font-size: 0.75rem;
  font-variant-numeric: tabular-nums;
  color: var(--steel);
  display: flex;
  align-items: center;
  gap: 0.4em;
}
.row--downloading .row__status,
.row--queued .row__status { color: var(--ember); }
.row--failed .row__status { color: var(--fail); }

.row__glyph { font-size: 0.8125rem; line-height: 1; }

.row__body { min-width: 0; }

.row__title {
  font-size: 0.9375rem;
  font-weight: 500;
  letter-spacing: -0.01em;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.row--deleted .row__title { color: var(--steel); }

.row__meta {
  font-family: var(--mono);
  font-size: 0.6875rem;
  color: var(--steel);
  margin-top: 0.15rem;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.row--failed .row__meta { color: var(--fail); }

.row__actions { display: flex; gap: 0.35rem; align-items: center; }

.act {
  font-family: var(--mono);
  font-size: 0.6875rem;
  padding: 0.35rem 0.6rem;
  border: 1px solid var(--frost-2);
  border-radius: var(--radius-sm);
  background: transparent;
  color: var(--steel);
  cursor: pointer;
  text-decoration: none;
  display: inline-block;
  transition: border-color 0.12s var(--ease), color 0.12s var(--ease),
              background 0.12s var(--ease);
}
.act:hover { border-color: var(--ink); color: var(--ink); }
.act--save { border-color: var(--ink); color: var(--ink); font-weight: 600; }
.act--save:hover { background: var(--ink); color: var(--ice); }
.act--danger:hover { border-color: var(--fail); color: var(--fail); background: var(--fail-bg); }

/* Work that is happening elsewhere, on someone else's clock: the button reads
   as unfinished without pretending to be disabled, since it still opens the
   reader. Kept in the cool palette — warmth belongs to bytes in transit. */
.act--pending {
  opacity: 0.55;
  border-style: dashed;
  animation: breathe 1.8s ease-in-out infinite;
}
.act--pending:hover { opacity: 0.8; }

@keyframes breathe {
  0%, 100% { opacity: 0.45; }
  50%      { opacity: 0.75; }
}

@media (prefers-reduced-motion: reduce) {
  .act--pending { animation: none; opacity: 0.55; }
}

/* ----------------------------------------------------------------- reader */

.reader {
  position: fixed;
  inset: 0;
  z-index: 50;
  display: flex;
  justify-content: center;
  align-items: flex-end;
  background: rgba(11, 17, 23, 0.45);
  padding: 0;
}

.reader__sheet {
  display: flex;
  flex-direction: column;
  width: min(46rem, 100%);
  height: min(88vh, 100%);
  background: var(--surface);
  border: 1px solid var(--frost);
  border-bottom: 0;
  border-radius: var(--radius) var(--radius) 0 0;
  overflow: hidden;
  animation: rise 0.28s var(--ease);
}

@keyframes rise {
  from { transform: translateY(2rem); opacity: 0; }
  to   { transform: none; opacity: 1; }
}

.reader__bar {
  display: flex;
  align-items: flex-start;
  gap: 1rem;
  padding: 1rem 1.1rem 0.75rem;
}

.reader__heading { flex: 1 1 auto; min-width: 0; }

.reader__title {
  margin: 0;
  font-size: 1rem;
  font-weight: 600;
  letter-spacing: -0.015em;
  line-height: 1.35;
  overflow-wrap: anywhere;
}

.reader__sub { margin: 0.25rem 0 0; color: var(--steel); }

.reader__close {
  flex: none;
  width: 2rem; height: 2rem;
  font-size: 1.35rem;
  line-height: 1;
  border: 1px solid var(--frost-2);
  border-radius: var(--radius-sm);
  background: transparent;
  color: var(--steel);
  cursor: pointer;
}
.reader__close:hover { color: var(--ink); border-color: var(--ink); }

.reader__tabs {
  display: flex;
  gap: 0.35rem;
  padding: 0 1.1rem 0.75rem;
  border-bottom: 1px solid var(--frost);
}

.reader__tab {
  padding: 0.4rem 0.75rem;
  border: 1px solid var(--frost-2);
  border-radius: var(--radius-sm);
  background: transparent;
  color: var(--steel);
  cursor: pointer;
}
.reader__tab[aria-selected="true"] {
  background: var(--ink);
  border-color: var(--ink);
  color: var(--ice);
}

.reader__body {
  flex: 1 1 auto;
  overflow-y: auto;
  padding: 1.25rem 1.1rem 2rem;
  font-size: 0.9375rem;
  line-height: 1.65;
}
.reader__body:focus-visible { outline: 2px solid var(--ember); outline-offset: -2px; }

/* The transcript's own markup, kept plain so long text stays readable. */
.reader__body h2 {
  margin: 1.75rem 0 0.6rem;
  font-size: 0.75rem;
  font-family: var(--mono);
  text-transform: uppercase;
  letter-spacing: 0.12em;
  color: var(--steel);
}
.reader__body h3 {
  margin: 1.4rem 0 0.5rem;
  font-size: 0.9375rem;
  font-weight: 600;
}
.reader__body p { margin: 0 0 0.9rem; }
.reader__body ul { margin: 0 0 1rem; padding-left: 1.15rem; }
.reader__body li { margin-bottom: 0.35rem; }
.reader__body hr { border: 0; border-top: 1px solid var(--frost); margin: 1.25rem 0; }
.reader__body strong { font-weight: 600; }

/* The timestamp that opens each transcript paragraph. */
.reader__body .at {
  font-family: var(--mono);
  font-size: 0.6875rem;
  color: var(--steel);
  background: var(--surface-2);
  border: 1px solid var(--frost);
  border-radius: 4px;
  padding: 0.05em 0.35em;
  margin-right: 0.4em;
  white-space: nowrap;
}

.reader__empty { color: var(--steel); font-family: var(--mono); font-size: 0.75rem; }

.reader__foot {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  flex-wrap: wrap;
  padding: 0.75rem 1.1rem;
  border-top: 1px solid var(--frost);
  background: var(--surface-2);
  color: var(--steel);
}
.reader__actions { display: flex; gap: 0.35rem; }

@media (max-width: 640px) {
  .reader__sheet { height: 100%; border-radius: 0; }
}

/* ----------------------------------------------------------------- search */

/* A filter, not the headline act — it sits in the corner at a size that
   suits a few words rather than a sentence. */
.find {
  position: relative;
  margin: 0.6rem 0 0;
  width: min(18rem, 100%);
  margin-left: auto;
}

.find__input {
  width: 100%;
  font-size: 0.75rem;
  padding: 0.45rem 1.9rem 0.45rem 0.7rem;
  border: 1px solid var(--frost-2);
  border-radius: var(--radius);
  background: var(--surface);
  color: var(--ink);
  -webkit-appearance: none;
  appearance: none;
}
.find__input::placeholder { color: var(--steel); opacity: 0.7; }
.find__input::-webkit-search-cancel-button { display: none; }
.find__input:focus {
  outline: none;
  border-color: var(--ember);
  box-shadow: 0 0 0 3px var(--ember-bg);
}

.find__clear {
  position: absolute;
  right: 0.4rem;
  top: 50%;
  transform: translateY(-50%);
  width: 1.35rem;
  height: 1.35rem;
  font-size: 1rem;
  line-height: 1;
  border: 0;
  border-radius: var(--radius-sm);
  background: transparent;
  color: var(--steel);
  cursor: pointer;
}
.find__clear:hover { color: var(--ink); }

.find__note {
  margin: 0.45rem 0 0;
  text-align: right;
  min-height: 1.2em;
  color: var(--steel);
}

/* Where a phrase was said, not merely which video said it. */
.hit {
  display: flex;
  gap: 0.65rem;
  align-items: baseline;
  padding: 0.25rem 0;
  font-size: 0.8125rem;
  line-height: 1.5;
}

.hit__at {
  flex: none;
  font-family: var(--mono);
  font-size: 0.6875rem;
  color: var(--steel);
  background: var(--surface-2);
  border: 1px solid var(--frost);
  border-radius: 4px;
  padding: 0.05em 0.35em;
}

.hit__text { color: var(--steel); overflow-wrap: anywhere; }
.hit__text mark {
  background: var(--ember-bg);
  color: var(--ink);
  border-radius: 3px;
  padding: 0 0.15em;
}

.row__hits { margin-top: 0.45rem; }

/* ------------------------------------------------------------------ toast */

.toast {
  position: fixed;
  left: 50%;
  bottom: 1.5rem;
  transform: translateX(-50%);
  background: var(--ink);
  color: var(--ice);
  padding: 0.6rem 1rem;
  border-radius: var(--radius-sm);
  box-shadow: 0 6px 24px rgba(15, 25, 35, 0.22);
  z-index: 40;
  max-width: min(90vw, 34rem);
}
.toast[data-tone="error"] { background: var(--fail); }

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
  }
}

/* ------------------------------------------------------------------- door */

/* Quiet by construction: the word is the whole identity, and the card is the
   only object on the page. Nothing here competes with the two fields that are
   the reason anybody opened it. */

.door {
  display: grid;
  place-items: center;
  min-height: 100svh;
  padding: 2.5rem 1.25rem;
  background: linear-gradient(180deg, var(--surface-2) 0%, var(--ice) 55%);
}

.gate {
  width: 100%;
  max-width: 24rem;
  display: flex;
  flex-direction: column;
  align-items: stretch;
  gap: 1.75rem;
}

/* --- what this is ------------------------------------------------------ */

.gate__head { text-align: center; }

.gate__mark {
  font-size: 1.6rem;
  letter-spacing: -0.035em;
  line-height: 1;
}

/* --- the one-time hint ------------------------------------------------- */

/* Warm, because it marks the one thing genuinely happening here: a server
   being opened for the first time. It never appears again. */
.gate__first {
  margin: 0;
  padding: 0.85rem 1rem;
  border-radius: var(--radius-sm);
  background: var(--ember-bg);
  border: 1px solid rgba(194, 106, 37, 0.25);
  font-size: 0.875rem;
  line-height: 1.55;
  color: var(--ink);
}

/* --- the tray ---------------------------------------------------------- */

.gate__card {
  display: flex;
  flex-direction: column;
  gap: 1.25rem;
  padding: 1.9rem 1.75rem;
  border: 1px solid var(--frost);
  border-radius: 14px;
  background: var(--surface);
  box-shadow: var(--lift);
}

.gate__title {
  margin: 0;
  font-size: 1.125rem;
  font-weight: 600;
  letter-spacing: -0.015em;
}

.gate__why {
  margin: -0.75rem 0 0;
  font-size: 0.875rem;
  line-height: 1.55;
  color: var(--steel);
}

.field { display: flex; flex-direction: column; gap: 0.45rem; }

.field__row {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 0.75rem;
}

.field__label {
  font-size: 0.8125rem;
  font-weight: 500;
  color: var(--steel);
}

.field__input {
  width: 100%;
  padding: 0.7rem 0.8rem;
  border: 1px solid var(--frost-2);
  border-radius: var(--radius-sm);
  background: var(--surface-2);
  color: var(--ink);
  font-family: var(--sans);
  font-size: 1rem;
  transition: border-color 0.15s var(--ease), background 0.15s var(--ease),
              box-shadow 0.15s var(--ease);
}

/* A password issued by a server is random characters; monospace is the only
   way to tell a capital O from a zero while typing it. */
.field__input[type="password"], .field__input#password { font-family: var(--mono); }

.field__input:hover { border-color: var(--steel); }
.field__input:focus {
  outline: none;
  border-color: var(--ember);
  background: var(--surface);
  box-shadow: 0 0 0 3px var(--ember-bg);
}

.field__peek {
  border: 0;
  background: transparent;
  padding: 0;
  font: inherit;
  font-size: 0.8125rem;
  color: var(--steel);
  cursor: pointer;
  text-decoration: underline;
  text-underline-offset: 0.22em;
}
.field__peek:hover { color: var(--ink); }

.field__caps { margin: 0; font-size: 0.8125rem; color: var(--ember); }

.gate__go {
  width: 100%;
  padding-block: 0.75rem;
  font-size: 0.9375rem;
}

/* Last thing in the card, so an error grows it downwards and nothing above
   moves. No reserved line needed, and no dead band under the button. */
.gate__note {
  margin: -0.6rem 0 0;
  font-size: 0.875rem;
  line-height: 1.45;
  color: var(--fail);
}

/* --- settings, kept out of the way ------------------------------------- */

.gate__foot { display: flex; justify-content: center; gap: 0.6rem; }

@media (prefers-reduced-motion: no-preference) {
  .gate { animation: arrive 0.45s var(--ease) both; }
}

@keyframes arrive {
  from { opacity: 0; transform: translateY(0.6rem); }
  to   { opacity: 1; transform: none; }
}

/* Who you are, and the way out. Quiet: it matters once a session, at the end. */
.who { display: flex; align-items: center; gap: 0.5rem; color: var(--steel); }

.who__out {
  border: 0;
  background: transparent;
  padding: 0;
  color: var(--steel);
  font: inherit;
  text-decoration: underline;
  text-underline-offset: 0.2em;
  cursor: pointer;
}
.who__out:hover { color: var(--ink); }

/* A question mark, sized to be unmistakably a button and quiet enough not to
   compete with the way out beside it. */
.who__ask {
  width: 1.35rem;
  height: 1.35rem;
  flex: none;
  display: inline-grid;
  place-items: center;
  padding: 0;
  border: 1px solid var(--frost-2);
  border-radius: 50%;
  background: transparent;
  color: var(--steel);
  font-family: var(--mono);
  font-size: 0.75rem;
  line-height: 1;
  cursor: pointer;
  transition: border-color 0.12s var(--ease), color 0.12s var(--ease);
}
.who__ask:hover { border-color: var(--ink); color: var(--ink); }

/* ---------------------------------------------------------------- people */

.people__add {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.6rem;
  margin-bottom: 0.9rem;
}
.people__add .intake__input { flex: 1 1 12rem; width: auto; }

/* Shown once, so it has to be hard to miss and easy to select. */
.people__issued {
  margin: 0 0 0.8rem;
  padding: 0.6rem 0.75rem;
  border: 1px solid var(--ember);
  border-radius: var(--radius-sm);
  background: var(--ember-bg);
  color: var(--ink);
  user-select: all;
  word-break: break-all;
}

/* Same as the door: a note that has nothing to say takes no room, so there
   is no empty band between the form and the list. Both notes sit at the end
   of their block, so an error grows the block downwards and moves nothing. */
.people__note { margin: 0 0 0.5rem; color: var(--fail); }
.people__note:empty { display: none; }

/* Nothing to say, nothing to occupy. */
.gate__note:empty { display: none; }

/* The digest settings sit with the other per-person ones. */
.people__why {
  margin: 0 0 0.9rem;
  font-size: 0.875rem;
  line-height: 1.55;
  color: var(--steel);
  max-width: 44rem;
}

/* Whether the chat is linked, on a line of its own so nothing overwrites it. */
.tg__state {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  margin: 0 0 0.9rem;
  font-size: 0.875rem;
  color: var(--steel);
}

.tg__dot {
  width: 0.5rem;
  height: 0.5rem;
  border-radius: 50%;
  background: var(--frost-2);
  flex: none;
}
.tg__state[data-linked="yes"] .tg__dot { background: var(--ember); }
.tg__state[data-linked="yes"] { color: var(--ink); }

/* Two values, each selectable on its own: pasting the whole sentence into a
   chat is not what anybody wants. */
.tg__code {
  margin: 0 0 0.9rem;
  padding: 0.85rem 1rem;
  border: 1px solid var(--frost);
  border-radius: var(--radius-sm);
  background: var(--surface-2);
}

.tg__codeWhy { margin: 0 0 0.6rem; font-size: 0.8125rem; color: var(--steel); }

.tg__pair {
  display: flex;
  align-items: center;
  gap: 0.6rem;
  margin-bottom: 0.45rem;
}
.tg__pair:last-child { margin-bottom: 0; }

.tg__value {
  flex: 1 1 auto;
  padding: 0.4rem 0.6rem;
  border: 1px solid var(--frost-2);
  border-radius: var(--radius-sm);
  background: var(--surface);
  color: var(--ink);
  font-size: 0.9375rem;
  /* One click selects exactly this value and nothing around it. */
  user-select: all;
  word-break: break-all;
}

/* The code is the part that has to be transcribed correctly. */
.tg__value--key {
  font-weight: 600;
  letter-spacing: 0.06em;
  color: var(--ember);
  border-color: var(--ember);
  background: var(--ember-bg);
}

/* ---------------------------------------------------------------- topics */

/* A standing instruction, so it is written on a card rather than in a row:
   what it is called, how many channels it covers, what to watch for, and at
   what hour to say so. All four are things you set once and leave. */

.topics {
  list-style: none;
  margin: 0 0 1.1rem;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 0.75rem;
}

.topic {
  padding: 0.75rem 0.85rem;
  border: 1px solid var(--frost);
  border-radius: var(--radius);
  background: var(--surface);
}

.topic__head {
  display: flex;
  align-items: center;
  gap: 0.6rem;
  margin-bottom: 0.75rem;
}

/* Edited where it stands: it reads as this card's heading until a cursor
   lands in it, and the negative margin keeps that text on the same left edge
   as the fields below. */
.topic__name {
  flex: 1 1 8rem;
  min-width: 0;
  margin-left: -0.5rem;
  padding: 0.3rem 0.5rem;
  border: 1px solid transparent;
  border-radius: var(--radius-sm);
  background: transparent;
  color: var(--ink);
  font-size: 0.9375rem;
  font-weight: 600;
  letter-spacing: -0.01em;
  transition: border-color 0.12s var(--ease), background 0.12s var(--ease);
}
.topic__name:hover { border-color: var(--frost-2); }
.topic__name:focus {
  outline: none;
  background: var(--surface-2);
  border-color: var(--ember);
  box-shadow: 0 0 0 3px var(--ember-bg);
}

.topic__count { flex: none; }

/* The reason the last subject cannot go is worth saying, so the button stays
   pressable and answers instead of sitting there greyed and mute. */
.topic__head [aria-disabled="true"] { opacity: 0.5; }
.topic__head [aria-disabled="true"]:hover {
  border-color: var(--frost-2);
  background: transparent;
  color: var(--steel);
}

.topic__fields {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  gap: 0.7rem 0.85rem;
  align-items: start;
}

.topic__field { display: flex; flex-direction: column; gap: 0.35rem; min-width: 0; }
/* An hour is two digits; it has no use for the width of a paragraph. */
.topic__field:has(.topic__select) { align-items: start; }

.topic__legend {
  color: var(--steel);
  text-transform: uppercase;
  letter-spacing: 0.1em;
}

.topic__text {
  width: 100%;
  padding: 0.5rem 0.65rem;
  border: 1px solid var(--frost-2);
  border-radius: var(--radius-sm);
  background: var(--surface-2);
  color: var(--ink);
  font-size: 0.875rem;
  line-height: 1.5;
  resize: vertical;
}
.topic__text::placeholder { color: var(--steel); opacity: 0.7; }
.topic__text:focus {
  outline: none;
  border-color: var(--ember);
  box-shadow: 0 0 0 3px var(--ember-bg);
}

.topic__select {
  padding: 0.45rem 0.6rem;
  border: 1px solid var(--frost-2);
  border-radius: var(--radius-sm);
  background: var(--surface-2);
  color: var(--ink);
}

.topic__note { margin: 0.6rem 0 0; color: var(--steel); }
.topic__note:empty { display: none; }

/* Which subject a channel is filed under, on the row itself: the tally on the
   subjects tab is read-only, and this is where the moving happens. */
.row__topic {
  max-width: 9rem;
  padding: 0.35rem 0.4rem;
  border: 1px solid var(--frost-2);
  border-radius: var(--radius-sm);
  background: transparent;
  color: var(--steel);
  font-size: 0.6875rem;
  cursor: pointer;
  transition: border-color 0.12s var(--ease), color 0.12s var(--ease);
}
.row__topic:hover { border-color: var(--ink); color: var(--ink); }

/* The buttons on a person's row, which had no rule of their own and so sat
   wherever the grid dropped them. */
.row__tools { display: flex; align-items: center; gap: 0.35rem; flex-wrap: wrap; }

/* ------------------------------------------------------------------ help */

/* The sheet itself is the reader's, borrowed whole. Only what goes inside it
   is new: a list of terms, one per control, with what it does under each. */

.help__body { padding-top: 0.5rem; }

.help__section { margin-bottom: 2rem; }
.help__section:last-child { margin-bottom: 0; }

.help__heading {
  margin: 0 0 0.35rem;
  font-family: var(--mono);
  font-size: 0.75rem;
  text-transform: uppercase;
  letter-spacing: 0.12em;
  color: var(--steel);
}

.help__note {
  margin: 0 0 0.9rem;
  font-size: 0.875rem;
  line-height: 1.5;
  color: var(--steel);
}

/* A picture of the thing being described, boxed like the thing itself. */
.help__shot {
  margin: 0 0 1.2rem;
  border: 1px solid var(--frost);
  border-radius: var(--radius-sm);
  overflow: hidden;
  background: var(--surface-2);
  line-height: 0;
}
.help__shot img { width: 100%; height: auto; display: block; }

.help__list { margin: 0; }

.help__term {
  margin-top: 0.9rem;
  font-size: 0.9375rem;
  font-weight: 600;
  letter-spacing: -0.01em;
}
.help__term:first-child { margin-top: 0; }

.help__text {
  margin: 0.2rem 0 0;
  font-size: 0.9375rem;
  line-height: 1.6;
  color: var(--steel);
}

/* The status marks, set as the column of signs they are on the shelf. */
.help__marks { margin: 0.5rem 0 0; }

.help__mark {
  display: flex;
  align-items: baseline;
  gap: 0.7rem;
  padding: 0.15rem 0;
  font-size: 0.875rem;
  color: var(--steel);
}

.help__glyph {
  flex: none;
  width: 1.4rem;
  text-align: center;
  font-family: var(--mono);
  color: var(--ink);
}

/* ----------------------------------------------------------------- mobile */

/* Last in the file, and deliberately so. These are overrides, and an override
   at the same specificity only wins by sitting lower than what it overrides.
   Kept up with the components they narrow, they die the moment somebody
   appends a new component below them — which is exactly what happened to the
   subject card's rules, silently, until a phone-sized screenshot showed two
   columns where one had been asked for. New mobile rules go here, at the
   bottom, not next to the component. */

@media (max-width: 480px) {
  /* The shelf below already carries the totals; the language control does not
     have a second home, so the stats yield first. */
  .bar__stats { display: none; }

  /* The word gets the first line, the controls the next, rather than the
     wordmark sitting halfway up a three-storey stack of switches. */
  .bar { flex-wrap: wrap; gap: 0.6rem; }
  .bar__right { flex: 1 1 100%; justify-content: flex-start; gap: 0.6rem; }

  /* Enough of a target to hit with a thumb, and the labels still fit across
     the narrowest phone without sliding. */
  .tabs__tab { padding: 0.7rem 0.2rem; font-size: 0.875rem; }
}

@media (max-width: 640px) {
  .row {
    grid-template-columns: 2.6rem minmax(0, 1fr);
    row-gap: 0.6rem;
  }
  /* Both kinds of row put their controls on a line of their own: the shelf
     and channels use `actions`, a person's row uses `tools`. Four controls do
     not fit across a phone in one line, so they are allowed to wrap — without
     this the last of them is cut off by the edge of the screen and the whole
     page scrolls sideways. */
  .row__actions,
  .row__tools {
    grid-column: 1 / -1;
    justify-content: flex-start;
    flex-wrap: wrap;
  }
  .row__topic { max-width: none; }

  .preview__head { flex-wrap: wrap; }
  .preview__thumb { width: 100%; }
  .picker__group + .picker__group { border-left: 0; border-top: 1px solid var(--frost); }

  /* Nothing left to push the search box against, so it takes the width. */
  .find { width: 100%; }
  .find__note { text-align: left; }

  /* The subject's name gets the first line to itself; the tally and the way
     out follow underneath rather than squeezing it to nothing. And what to
     watch for stops sharing a line with the hour, which left the one field
     anybody actually types into about as wide as a thumb. */
  .topic__head { flex-wrap: wrap; }
  .topic__name { flex: 1 1 100%; }
  .topic__fields { grid-template-columns: minmax(0, 1fr); }
  .topic__text { min-height: 4.5rem; }
}
