@font-face {
  font-family: 'FreeSerif';
  src: url('../fonts/FreeSerif.ttf') format('truetype');
  font-display: swap;
}
/* The real bold / italic / bold-italic faces of the default family (they
   ship in fonts/fontbook/), so the standard font renders TRUE italics and
   weights — the italic phonetics line, misspeak <i>, and L2 rows — instead
   of a browser-synthesized slant. A chosen Fontbook family already brings
   its own faces (js/fontbook.js); this covers the no-font-chosen default. */
@font-face {
  font-family: 'FreeSerif';
  src: url('../fonts/fontbook/FreeSerif-Bold.ttf') format('truetype');
  font-weight: bold;
  font-display: swap;
}
@font-face {
  font-family: 'FreeSerif';
  src: url('../fonts/fontbook/FreeSerif-Italic.ttf') format('truetype');
  font-style: italic;
  font-display: swap;
}
@font-face {
  font-family: 'FreeSerif';
  src: url('../fonts/fontbook/FreeSerif-BoldItalic.ttf') format('truetype');
  font-weight: bold;
  font-style: italic;
  font-display: swap;
}

:root {
  --bg: #f6f4ef;
  --panel: #ffffff;
  --ink: #22281f;
  --muted: #6b7263;
  --accent: #1d3a2a;
  --accent-ink: #ffffff;
  --border: #d8d4c8;
  --log-bg: #f0ede4;
  /* The wave's ground is dark in BOTH themes — the lane is soft light bars on
     a dark field (owner ruling, July 18), and it would lose the bars entirely
     on a light background. So it is its own token rather than the shell's, but
     it now sits in the same COOL family as the dark shell instead of the warm
     #211f19 it used to be, which read as a different colour rather than a
     different surface (owner report, July 20). */
  --wave-ground: #1b1f18;
  /* One source for the transcript scrollbar's width: the bar itself is drawn
     this wide, the gutter reserved on both sides is this wide, and the footer
     line offsets by it to land on the footnote rail. */
  --sb-gutter: 12px;
  /* Focus rings are RUST, like every other "this is active" state in the app
     (owner report, July 20: the green ring on a toggle read as an unexplained
     box). --accent is green in the dark theme, which is why the generic
     button ring stood out against the app's own convention. */
  --focus: #963d16;
}

@media (prefers-color-scheme: dark) {
  :root {
    /* ONE dark ground for the shell (owner ruling, July 20). It used to be two
       that nobody could tell apart — the page at #191d17 and the bands at
       #1b1f18, a single percent of lightness between them, which buys nothing
       and only ever drifts. (The wave's ground is --wave-ground above: also
       cool now, but a separate token because it stays dark in the light theme
       too.) */
    --ground: #1b1f18;
    --bg: var(--ground);
    --log-bg: var(--ground);
    /* --panel stays deliberately lighter: it is the raised card behind the
       Speakers / Fontbook / About pages, and flattening it into the ground
       would take the sense of elevation with it. */
    --panel: #22271f;
    --ink: #e8e6dd;
    --muted: #a0a695;
    --accent: #4e8b66;
    --accent-ink: #10140e;
    --border: #3a4034;
  }
}

/* ── THE WAVE VIEW'S COLOUR SEAM ──────────────────────────────────────────
   ᏗᏍᏓᏅᏅᏙᏗ has ONE wave view, shared by every subprogram, and COLOUR is the
   only difference a seat is allowed (owner ruling, 2026-07-25). This block is
   where ᏗᎪᏣᎳᎩᏍᎩ exercises that one freedom. Everything else about the
   component — its bands, icons, positions, time scale — comes from
   _Shared-Code/wave-view and may not be edited here.

   The shared css/waveview.css and js/waveview.js name ONLY these properties
   and hold no colour literal of their own; `sync.py check` enforces that, so
   this block is genuinely the whole surface. To restyle the wave, change a
   value here — never a line of the shared files.

   The values below reproduce the rust-and-paper wave exactly as it was before
   the component was factored out, including its light-theme chrome staying
   light in the dark theme. That was the behaviour, not a decision; a dark
   override belongs in the dark-theme block above whenever it is wanted. */
:root {
  --wv-ground: var(--wave-ground); /* the dark lane, both themes */
  --wv-bar: #c9cdb0;               /* lichen: bars, ruler ticks, syllabary */
  --wv-accent: #963d16;            /* rust: playhead, selection, armed track */
  --wv-accent-hover: #7d3312;
  --wv-accent-ink: #f0ede4;        /* a glyph sitting on the rust */
  --wv-playhead: #48b39c;          /* verdigris: the playhead, and only it */
  --wv-mark: #cf8636;              /* amber: a standalone mark, a point pinned */
  --wv-scale: #a0a695;             /* the dB rulers down both gutters */
  --wv-pho: #9aa08d;               /* the phonetics line inside a band */
  --wv-surface: #f7f2e2;           /* button faces */
  --wv-surface-hover: #efe7cf;
  --wv-panel: var(--panel);        /* the help popover */
  --wv-ink: #000;
  --wv-muted: var(--muted);
  --wv-border: var(--border);
  --wv-rule: #a8a488;              /* the resize handle's hairline */
  --wv-rule-strong: #7a6a48;
  --wv-knob: #fff;                 /* the switch's travelling dot */
  --wv-focus-ring: rgba(150, 61, 22, 0.45);
  --wv-shadow: rgba(0, 0, 0, 0.15);
  --wv-mono: ui-monospace, SFMono-Regular, Menlo, monospace;
}

* { box-sizing: border-box; }
[hidden] { display: none !important; }

/* --app-font is set by the Fontbook's "Use in app" (js/fontbook.js); when it
   is absent the stack below starts at the standard font. */
body {
  margin: 0;
  font-family: var(--app-font, 'FreeSerif'), 'FreeSerif', 'Noto Sans Cherokee', Georgia, serif;
  font-size: 18px;
  background: var(--bg);
  color: var(--ink);
  min-height: 100vh;
  display: flex;
  flex-direction: column;
}

/* Header (owner ruling, July 19): app title as "home" on the left, the ☰
   menu on the right — Transcript is the app, so there's no main tab row. */
/* Owner ruling, July 20: the recording tabs live in this row, on the LEFT; the
   title and ☰ sit at the RIGHT. align-items:flex-end so the tabs meet the
   bottom rule browser-style while the title rides the same baseline. */
/* body > header: ONLY the app's own chrome — the ᏗᏥᎶᏍᏙᏗ composer has a
   <header class="dts-top"> of its own inside its panel, and these window
   rules must never grab it (it ended up pinned into the title bar).
   :not(.cv-head) is the same guard for the consultant window's header, which
   IS a direct child of its body: unguarded it inherited this bar's flex layout
   and, worse, the fixed-into-the-title-bar rule below (July 27). */
body > header:not(.cv-head) {
  padding: 0.5rem 1.5rem 0;
  display: flex; align-items: flex-end; justify-content: space-between; gap: 1rem;
  border-bottom: 2px solid var(--border);
  flex: none; /* window chrome — never scrolls, never shrinks */
}
header .tr-tabrow { flex: 1; min-width: 0; }
header .apphome { padding-bottom: 0.45rem; flex: none; }
header .appmenu { padding-bottom: 0.35rem; }
header .applog { padding-bottom: 0.35rem; flex: none; }
/* The status line lives at the right end of this row now (owner ruling,
   July 28 2026) — see the note in index.html. It spends only height the chrome
   already owns, so it must stay ONE line: it ellipsizes, and status() hangs the
   whole message on the element's title, so the tail is a hover away. It also
   never takes room from the tabs (flex: 0 1 auto — it gives way first). */
header .tr-status {
  flex: 0 1 auto;
  min-width: 0;
  /* the cap keeps a long message from eating a busy tab strip (which scrolls
     rather than wrapping), and keeps it from sprawling on a wide monitor */
  max-width: min(32rem, 38%);
  margin: 0 0 0.5rem;
  min-height: 0;
  text-align: right;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
/* the title-bar drag strip exists only when the app owns the title bar */
.tr-titledrag { display: none; }

/* "back to main window" — a popped-out window's way home (owner request,
   July 20). Hidden by CSS rather than [hidden] so body.popout can reveal it. */
#tr-popback { display: none; }
body.popout #tr-popback {
  display: inline-flex;
  align-items: center;
  gap: 0.3rem;
  font: inherit;
  font-size: 0.72rem;
  padding: 0.2rem 0.6rem;
  border: 1px solid var(--border);
  border-radius: 999px;
  background: var(--panel);
  color: var(--muted);
  cursor: pointer;
}
body.popout #tr-popback:hover { color: var(--ink); border-color: #963d16; }

/* ---- Window Controls Overlay (owner ruling, July 20) ------------------------
   When the app runs INSTALLED in a browser that supports it, the window's title
   bar becomes ours and this header moves into it — recording tabs at one end,
   ᏗᎪᏣᎳᎩᏍᎩ and the version at the other, reclaiming the whole 51px row.

   Everything here is an ENHANCEMENT. Safari and Firefox never match this query,
   an ordinary browser tab never matches it, and they all keep the normal header
   above — which is exactly why that layout stays in the file.

   The env() values are the browser telling us where the window buttons actually
   sit: on the LEFT on macOS, on the RIGHT on Windows. Driving the strip off them
   rather than off fixed padding is what makes one rule work on both.

   EVERY rule in here also asks for body.wco — js/wco.js puts that class on only
   while navigator.windowControlsOverlay.visible is true. The media query alone
   was not enough (owner report, July 28 2026: "my tabs disappear out of the
   window border when in full screen"): going full screen takes the strip away
   but leaves env(titlebar-area-*) DEFINED AND ZERO, and an env() fallback
   stands in only when the variable is missing, never when it is 0. The header
   became a 0x0 fixed box pinned at the top left, and its tabs spilled outside
   the window. Without .wco none of this applies and the ordinary header below
   stands, which is the right layout for full screen. */
@media (display-mode: window-controls-overlay) {
  /* The title-bar strip is part of the VIEWPORT here, so a header left in
     normal flow scrolls up out of it and the page's own content slides through
     the title bar — which is the hole the owner saw appear in the middle of the
     top border on the Speakers / Fontbook / Legacy / About / Guide pages
     (July 20). Those panels scroll; the transcript does not, which is why it
     never showed there. Pinning the header fixes it for every panel. */
  body.wco { padding-top: env(titlebar-area-height, 38px); }
  body.wco > header:not(.cv-head) {
    position: fixed;
    top: env(titlebar-area-y, 0);
    z-index: 60;
    /* sit exactly on the strip the browser has left free */
    left: env(titlebar-area-x, 0);
    margin-left: 0;
    width: env(titlebar-area-width, 100%);
    height: env(titlebar-area-height, 38px);
    padding: 0 0.55rem;
    align-items: center;
    /* match the manifest's theme_color so the strip and the window frame
       around the buttons read as one surface */
    background: #1d3a2a;
    border-bottom: none;
    /* The header itself is explicitly NOT a drag region. A drag region swallows
       every control inside it that is not marked no-drag, and the recording
       tabs are <span> chips, not buttons — making the whole bar draggable
       killed tab clicks and the pop-out button (owner report, July 20). Only
       the dedicated empty strip below drags. */
    -webkit-app-region: no-drag;
    app-region: no-drag;
  }
  /* the tabs no longer take the slack — the drag strip does */
  body.wco header .tr-tabrow { flex: 0 1 auto; }
  body.wco .tr-titledrag {
    display: block;
    flex: 1 1 auto;
    align-self: stretch;
    min-width: 2.5rem;
    -webkit-app-region: drag;
    app-region: drag;
  }

  body.wco header h1 { font-size: 0.95rem; }
  body.wco header .version { font-size: 0.6rem; }
  body.wco header .apphome,
  body.wco header .appmenu,
  body.wco header .applog { padding-bottom: 0; }
  body.wco header .menu-btn { height: 1.7rem; }
  /* the row is centred here, not baseline-aligned, so the status line drops its
     bottom margin and rides the middle of the strip with the title */
  body.wco header .tr-status { margin-bottom: 0; }

  /* A POP-OUT has no tab strip (body.popout hides it), so without this the bar
     would be empty — and an empty bar with no drag target is a window you
     cannot move. The empty tab row still carries app-region: drag, so the
     strip stays grabbable; this just keeps it from collapsing to nothing. */
  body.wco.popout header .tr-tabrow { min-height: env(titlebar-area-height, 38px); }
}

/* smaller than it was: the title now shares the header row with the recording
   tabs, which are the primary control there (owner ruling, July 20) */
h1 { margin: 0; font-size: 1.15rem; }
.version {
  font-size: 0.75rem;
  color: var(--muted);
  font-family: ui-monospace, monospace;
  vertical-align: middle;
}
/* the title doubles as a home button (back to the transcript) */
.apphome {
  background: none; border: none; padding: 0; margin: 0; cursor: pointer;
  color: inherit; font: inherit; text-align: left; display: inline-flex; align-items: baseline;
}
.apphome:hover h1 { color: var(--accent); }
.apphome:focus-visible { outline: 3px solid var(--focus); outline-offset: 3px; border-radius: 6px; }

/* ☰ menu: a three-bar button that expands a dropdown of the other sections */
.appmenu { position: relative; flex: none; }
.menu-btn {
  display: flex; flex-direction: column; justify-content: center; gap: 4px;
  width: 2.6rem; height: 2.2rem; padding: 0 0.6rem;
  background: none; border: 1px solid var(--border); border-radius: 8px; cursor: pointer;
}
.menu-btn span {
  display: block; height: 2px; border-radius: 2px; background: var(--ink);
  transition: background 0.12s;
}
.menu-btn:hover { border-color: var(--accent); }
.menu-btn[aria-expanded="true"] { border-color: var(--accent); background: var(--panel); }
.menu-panel {
  position: absolute; right: 0; top: calc(100% + 0.5rem); z-index: 50;
  min-width: 13rem; padding: 0.35rem;
  background: var(--panel); border: 1px solid var(--border); border-radius: 10px;
  box-shadow: 0 8px 24px rgba(0, 0, 0, 0.35);
  display: flex; flex-direction: column;
}
.menu-panel button, .menu-panel a {
  font: inherit; text-align: left; padding: 0.5rem 0.7rem; border: none;
  background: none; color: var(--ink); cursor: pointer; border-radius: 7px;
  display: block; text-decoration: none;
}
.menu-panel button:hover, .menu-panel a:hover { background: var(--bg); }
.menu-panel button[aria-current="true"] { color: var(--accent); font-weight: bold; }
.menu-sep { height: 1px; background: var(--border); margin: 0.35rem 0.4rem; }
/* nested menu group: a parent that expands a submenu (Grammar → Published grammars) */
.menu-group { display: flex; flex-direction: column; }
.menu-parent {
  font: inherit; text-align: left; padding: 0.5rem 0.7rem; border: none; background: none;
  color: var(--ink); cursor: pointer; border-radius: 7px;
  display: flex; align-items: center; justify-content: space-between; gap: 0.5rem;
}
.menu-parent:hover { background: var(--bg); }
.menu-chev { font-size: 0.7em; color: var(--muted); transition: transform 0.15s; }
.menu-parent[aria-expanded="true"] .menu-chev { transform: rotate(180deg); }
.menu-sub { display: flex; flex-direction: column; padding-bottom: 0.2rem; }
.menu-sub-label {
  font-size: 0.6rem; text-transform: uppercase; letter-spacing: 0.06em;
  color: var(--muted); padding: 0.35rem 0.9rem 0.15rem;
}
.menu-sub button { padding-left: 1.4rem !important; font-size: 0.9rem; }

/* a popped-out window is one transcript — no menu, no log button (its
   status() calls still LOG — localStorage is shared — they just read from
   the main window's panel) */
body.popout .appmenu, body.popout .applog { display: none; }
body.popout .apphome { pointer-events: none; }

/* ---- the activity log (owner ruling, July 29) ------------------------------
   Button beside the ☰ (same chrome as .menu-btn); panel = the full text of
   every status message, flat by recency or tiered program → collection →
   file. */
.log-btn { align-items: center; }
.log-btn svg { width: 1.15rem; height: 1.15rem; color: var(--ink); }
.log-btn:hover svg, .log-btn[aria-current="true"] svg { color: var(--accent); }
.log-btn[aria-current="true"] { border-color: var(--accent); background: var(--panel); }

#panel-log { max-width: 54rem; } /* messages are sentences — give them a line's room */
.log-head { display: flex; align-items: center; justify-content: space-between; gap: 1rem; }
.log-head h2 { margin: 0; }
.log-controls { display: flex; align-items: center; gap: 0.6rem; }
.log-viewtoggle { display: flex; }
.log-viewtoggle button, #log-clear {
  font: inherit; font-size: 0.85rem; cursor: pointer;
  background: none; color: var(--ink);
  border: 1px solid var(--border); padding: 0.25rem 0.7rem;
}
.log-viewtoggle button:first-child { border-radius: 7px 0 0 7px; }
.log-viewtoggle button:last-child { border-radius: 0 7px 7px 0; border-left: none; }
.log-viewtoggle button[aria-pressed="true"] {
  color: var(--accent); font-weight: bold; background: var(--bg); border-color: var(--accent);
}
/* the pressed border wins the shared edge back from the unpressed neighbour */
.log-viewtoggle button[aria-pressed="true"] + button { border-left: 1px solid var(--accent); }
#log-clear { border-radius: 7px; color: var(--muted); }
#log-clear:hover { border-color: var(--accent); color: var(--ink); }

.log-list { margin-top: 0.9rem; }
.log-entry { padding: 0.45rem 0; border-top: 1px solid var(--border); }
.log-entry:first-child { border-top: none; }
.log-entry-meta { font-size: 0.75rem; color: var(--muted); }
.log-entry-meta .log-prog { color: var(--accent); }
.log-entry-meta .log-file { color: var(--ink); }
.log-entry-msg { margin-top: 0.1rem; overflow-wrap: anywhere; }

/* the tiers: nested <details>, each level stepping in under its rail */
.log-tier { margin: 0.3rem 0; }
.log-tier > summary {
  cursor: pointer; display: flex; align-items: baseline; gap: 0.5rem;
  padding: 0.25rem 0.2rem; border-radius: 6px;
}
.log-tier > summary:hover { background: var(--bg); }
.log-tier .log-tier, .log-tier .log-entry {
  margin-left: 0.6rem; padding-left: 0.8rem; border-left: 2px solid var(--border);
}
.log-tier .log-entry { border-top: none; padding-top: 0.3rem; padding-bottom: 0.3rem; }
.log-tier-program > summary { font-weight: bold; }
.log-tier-collection > summary { color: var(--accent); }
.log-tier-file > summary { font-size: 0.9rem; }
.log-count {
  font-size: 0.7rem; font-weight: normal; color: var(--muted);
  border: 1px solid var(--border); border-radius: 999px; padding: 0 0.45rem;
}

nav[role="tablist"] {
  display: flex;
  gap: 0.4rem;
  flex-wrap: wrap;
  border-bottom: 2px solid var(--border);
}

nav[role="tablist"] button {
  font: inherit;
  padding: 0.5rem 1rem;
  border: 2px solid var(--border);
  border-bottom: none;
  border-radius: 8px 8px 0 0;
  background: transparent;
  color: var(--ink);
  cursor: pointer;
}

nav[role="tablist"] button[aria-selected="true"] {
  background: var(--panel);
  border-color: var(--accent);
  font-weight: bold;
}

/* Subtabs (inside the Legacy tab): pills instead of file-folder tabs. */
nav.subtabs {
  border-bottom: 1px solid var(--border);
  padding-bottom: 0.8rem;
  margin-bottom: 1rem;
}
nav.subtabs button {
  font-size: 0.9rem;
  padding: 0.3rem 0.9rem;
  border: 1px solid var(--border);
  border-radius: 999px;
}
nav.subtabs button[aria-selected="true"] {
  background: var(--accent);
  border-color: var(--accent);
  color: var(--accent-ink);
}

main { flex: 1; padding: 0 1.5rem; }

.panel {
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: 10px;
  padding: 1.2rem 1.5rem;
  max-width: 44rem;
  margin-top: 1.2rem;
}
/* the transcript is the app: its recording tab bar rides at the very top of
   the panel, so it wants less headroom above it */
#panel-transcript { padding-top: 0.7rem; }
.infopanel h2 { margin-top: 0; }

.hint { margin-top: 0; color: var(--muted); }

/* ---- Keys panel (owner ruling, 2026-07-29) --------------------------------
   A static list, no rebinding. Two views over one list, switched by a pair of
   radios and a :checked rule — no script, so it also prints as whatever view
   is on screen. The by-key view is wider than the rest of the info panels
   because it carries a column per program, so this panel opts out of the
   44rem cap the others share. */
#panel-keys { max-width: 60rem; }
.keysview { position: relative; margin-top: 0.9rem; }
.keysview > input { position: absolute; opacity: 0; pointer-events: none; }
.keystabs { display: flex; gap: 0.4rem; flex-wrap: wrap; margin-bottom: 0.7rem; }
.keystabs label {
  font: inherit; font-size: 0.82rem; line-height: 1;
  padding: 0.34rem 0.7rem;
  border: 1px solid var(--border); border-radius: 7px;
  color: var(--muted); cursor: pointer;
}
.keystabs label:hover { color: var(--ink); border-color: var(--ink); }
#keys-fn:checked ~ .keystabs label[for="keys-fn"],
#keys-in:checked ~ .keystabs label[for="keys-in"] {
  color: #963d16; border-color: #963d16;
  background: color-mix(in srgb, #963d16 8%, transparent);
}
.keysv { display: none; }
#keys-fn:checked ~ .keysbody .keysv-fn,
#keys-in:checked ~ .keysbody .keysv-in { display: block; }
/* a column per program overflows a narrow window before it overflows the
   panel — let the table scroll on its own rather than the page going wide */
.keysbody { overflow-x: auto; }

.keystable { border-collapse: collapse; width: 100%; font-size: 0.82rem; }
.keystable th, .keystable td {
  text-align: left; padding: 0.3rem 0.45rem;
  border-bottom: 1px solid var(--border); vertical-align: top;
}
.keystable th {
  color: var(--muted); font-weight: normal; font-size: 0.68rem;
  letter-spacing: 0.09em; text-transform: uppercase; white-space: nowrap;
}
.keystable tr.keysgrp td {
  background: color-mix(in srgb, var(--muted) 10%, transparent);
  font-family: ui-monospace, Menlo, monospace;
  font-size: 0.66rem; letter-spacing: 0.09em; text-transform: uppercase;
  color: var(--muted);
}
.keystable td.kk { white-space: nowrap; }
.keystable kbd {
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: 0.74rem;
  background: #f7f2e2; color: #000;
  border: 1px solid var(--border); border-bottom-width: 2px;
  border-radius: 4px; padding: 0.02rem 0.32rem; white-space: nowrap;
}
.keystable .orsep { color: var(--muted); font-size: 0.76rem; }
.keystable .kcond {
  font-family: ui-monospace, Menlo, monospace; font-size: 0.68rem; color: #c9861f;
}
.keystable .knone { color: var(--muted); opacity: 0.4; }
.keystable .korigin {
  font-family: ui-monospace, Menlo, monospace; font-size: 0.64rem;
  padding: 1px 5px; border-radius: 4px; white-space: nowrap;
}
/* the shared component vs. a program's own code — the distinction that decides
   whether changing a key changes one program or all of them */
.keystable .korigin.kw { color: #2f4a70; border: 1px solid rgba(47, 74, 112, 0.45); }
.keystable .korigin.kh { color: var(--muted); border: 1px solid var(--border); }
.keystable tr.kclash { background: color-mix(in srgb, #c9861f 14%, transparent); }
.keysclashnote { margin-top: 0.7rem; font-size: 0.8rem; }
@media (prefers-color-scheme: dark) {
  .keystable kbd { background: #2a2f27; color: var(--ink); }
  .keystable .korigin.kw { color: #7f9fb8; border-color: rgba(127, 159, 184, 0.45); }
}

/* ---- Guide panel (owner request, 2026-08-14) -------------------------------
   Static like Keys — headings, prose, and two-column tables that borrow
   .keystable's ruling. Slightly wider than the 44rem infopanels so the
   notation table's example column breathes. */
#panel-guide { max-width: 48rem; }
.guide h3 {
  margin: 1.5rem 0 0.4rem;
  font-size: 0.98rem;
  letter-spacing: 0.02em;
}
.guide p, .guide li { font-size: 0.88rem; line-height: 1.55; }
.guide ol { padding-left: 1.3rem; }
.guide li { margin-bottom: 0.45rem; }
.guide code {
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: 0.8rem;
  background: color-mix(in srgb, var(--muted) 12%, transparent);
  border-radius: 4px; padding: 0.02rem 0.28rem;
}
.guide kbd {
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: 0.74rem;
  background: #f7f2e2; color: #000;
  border: 1px solid var(--border); border-bottom-width: 2px;
  border-radius: 4px; padding: 0.02rem 0.32rem; white-space: nowrap;
}
.guidetable { margin: 0.5rem 0 0.6rem; }
.guidetable td { font-size: 0.84rem; }
@media (prefers-color-scheme: dark) {
  .guide kbd { background: #2a2f27; color: var(--ink); }
}

.field {
  display: block;
  margin: 0.9rem 0;
}

.field input[type="file"],
.field input[type="text"],
.field select {
  display: block;
  margin-top: 0.3rem;
  font: inherit;
  width: 100%;
  max-width: 100%;
  padding: 0.35rem 0.4rem;
  border: 1px solid var(--border);
  border-radius: 6px;
  background: var(--bg);
  color: var(--ink);
}

.check { display: block; margin: 0.7rem 0; }

.buttons { display: flex; gap: 0.6rem; flex-wrap: wrap; }

button.primary,
.buttons button {
  font: inherit;
  padding: 0.55rem 1.2rem;
  border-radius: 8px;
  border: 1px solid var(--accent);
  background: var(--panel);
  color: var(--ink);
  cursor: pointer;
  margin-top: 0.5rem;
}

button.primary {
  background: var(--accent);
  color: var(--accent-ink);
}

button:focus-visible { outline: 3px solid var(--focus); outline-offset: 2px; }
/* Keyboard-reachable transcript rows (code review, July 14): the focused
   row shows an inset ring; Enter/Space plays it. */
.tr-row:focus-visible { outline: 2px solid var(--focus); outline-offset: -2px; }

.log {
  margin-top: 1rem;
  padding: 0.7rem;
  min-height: 2.5rem;
  max-height: 16rem;
  overflow: auto;
  background: var(--log-bg);
  border-radius: 6px;
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: 0.8rem;
  white-space: pre-wrap;
  word-break: break-word;
}

footer {
  padding: 1rem 1.5rem;
  color: var(--muted);
  font-size: 0.85rem;
}

footer a { color: inherit; }

/* ---- Transcript tool (rivercane scheme: lichen ground, black text,
   bloodroot rules, walnut controls, deep-cane timestamps) ---- */

/* Loaded transcript takes the full page width — and drops the card frame
   (owner review, July 19): once the transcript IS the app, an enclosing
   border/rounded box is a window-in-a-window. It breathes to the page margins
   under the header's rule instead. The card frame stays for the narrow
   sections (Speakers, Fontbook, the new-tab welcome page). */
#panel-transcript.loaded {
  max-width: none;
  border: none;
  border-radius: 0;
  margin-top: 0;
  /* Variant D (owner pick, July 19): drop the outer card frame, but keep a
     side buffer. The chrome (tabs · status · title · ribbon · transport) sits
     at main's 1.5rem inset; only the lichen row sheet breathes closer to the
     edge (see #tr-list below). Revisit during polish — mocks in _padding-mock.html. */
  /* top trimmed 0.6 → 0.3rem (owner request, Aug 3): with the header folded
     this padding is most of the dark slack ABOVE the toggle chips, while
     below them there was none — see .tr-headrow's folded padding-bottom. */
  padding: 0.3rem 0 1.2rem;
}
/* the transcript's lichen sheet gets a GENTLE inset (~0.8rem from the window
   edge) — 0.7rem tighter than the chrome's 1.5rem, so the rows breathe wider
   than the controls above them without going full-bleed. */
#panel-transcript.loaded #tr-list {
  margin-left: -0.7rem;
  margin-right: -0.7rem;
}

/* ---- Fixed app shell (owner ruling, July 20) --------------------------------
   Microsoft Word's model: the header, the recording tabs and the ribbon bands
   are part of the WINDOW and never move; only the document scrolls, in its own
   pane. Gated with :has() so it applies ONLY to a loaded transcript that is the
   active panel — the welcome screen, Speakers/Fontbook/About and popped-out
   windows keep ordinary page scrolling. Where :has() is unsupported the page
   simply scrolls as before, which is why .tr-topbar keeps its sticky fallback. */
body:not(.popout):has(#panel-transcript.loaded.active) {
  height: 100vh;
  overflow: hidden;
}
body:not(.popout):has(#panel-transcript.loaded.active) main {
  flex: 1;
  min-height: 0;
  display: flex;
  flex-direction: column;
  overflow: hidden;
}
body:not(.popout):has(#panel-transcript.loaded.active) #panel-transcript.loaded {
  flex: 1;
  min-height: 0;
  display: flex;
  flex-direction: column;
  padding-bottom: 0;
}
body:not(.popout):has(#panel-transcript.loaded.active) #tr-view {
  flex: 1;
  min-height: 0;
  display: flex;
  flex-direction: column;
}
/* the bands are chrome: fixed height, never scrolled, never squeezed */
body:not(.popout):has(#panel-transcript.loaded.active) .tr-topbar { flex: none; }
/* …and the rows are the one thing that moves */
body:not(.popout):has(#panel-transcript.loaded.active) #tr-scrollpane {
  flex: 1;
  min-height: 0;
  overflow-y: auto;
  overflow-x: hidden;
  /* Reserve the scrollbar's width on BOTH sides, so the document sits the same
     distance from each edge of the window (owner ruling, July 20). Without it
     the bar eats the right edge only and the sheet lands 13px from the left but
     28px from the right. Styling the bar below makes it a real, space-taking
     scrollbar, which is what makes this gutter — and therefore the symmetry —
     the same on every machine instead of depending on the OS overlay setting. */
  scrollbar-gutter: stable both-edges;
  /* Firefox */
  scrollbar-width: thin;
  scrollbar-color: #4f5647 transparent;
}
/* The transcript's scrollbar in the app's own palette rather than the OS's
   (owner ruling, July 20): a lichen-dark thumb on nothing, inset by a
   transparent border so it reads as a slim capsule with air around it. */
#tr-scrollpane::-webkit-scrollbar { width: var(--sb-gutter); }
#tr-scrollpane::-webkit-scrollbar-track { background: transparent; }
#tr-scrollpane::-webkit-scrollbar-thumb {
  background: #4f5647;
  border-radius: 999px;
  border: 3px solid transparent;
  background-clip: content-box;
}
#tr-scrollpane::-webkit-scrollbar-thumb:hover {
  background: #6b7263;
  background-clip: content-box;
}
#tr-scrollpane::-webkit-scrollbar-corner { background: transparent; }
/* the lichen sheet fills the pane even when the transcript is short, so a
   two-line recording doesn't leave a dark slab under it (July 20) */
body:not(.popout):has(#panel-transcript.loaded.active) #tr-list { min-height: 100%; }

/* ---- Full-bleed chrome, floating document (owner ruling, July 20) -----------
   Word's structure: the furniture belongs to the WINDOW and spans it edge to
   edge, separated only by hairlines, and the transcript is the one inset,
   rounded thing floating inside it.

   Replaces a frame where the left edge stepped 0 → 24 → 29 → 13 down the app,
   with three corner radii, three border treatments and two different darks.
   Each of those was defensible alone; together they read as an accident. Now
   there is ONE vertical edge to get right instead of six.

   Only the loaded transcript goes full-bleed — the welcome screen and the
   Speakers / Fontbook / About pages keep main's normal inset, since those are
   documents in their own right rather than window furniture. */
body:not(.popout):has(#panel-transcript.loaded.active) main { padding: 0; }

#panel-transcript.loaded {
  --chrome-pad: 0.9rem;  /* the one horizontal inset for every chrome band */
  --sheet-inset: 0.8rem; /* …and the one the document floats at */
}
/* The topbar no longer has to reach out PAST the sheet to cover it (the
   lichen-sliver fix of July 19) — full-bleed chrome is now the wider thing,
   so that negative margin is retired. */
#panel-transcript.loaded .tr-topbar {
  margin-left: 0;
  margin-right: 0;
  padding-left: 0;
  padding-right: 0;
}
/* every chrome band: edge to edge, square corners, no side borders.
   (The toggle strip's lower slot retired July 31 — the strip rides inside
   .tr-headrow now, so the header row's inset is the only one it needs.) */
#panel-transcript.loaded .tr-headrow,
#panel-transcript.loaded .tr-controls,
#panel-transcript.loaded .tr-transport,
#panel-transcript.loaded .tr-waverow {
  margin-left: 0;
  margin-right: 0;
  border-left: none;
  border-right: none;
  border-top: none;
  border-radius: 0;
  padding-left: var(--chrome-pad);
  padding-right: var(--chrome-pad);
}
/* …separated only by hairlines */
#panel-transcript.loaded .tr-controls,
#panel-transcript.loaded .tr-transport,
#panel-transcript.loaded .tr-waverow {
  margin-top: 0;
  border-bottom: 1px solid var(--border);
}
/* the wave lane is a band too — it keeps its own darker ground, because that
   is the colour the canvas paints, but loses its frame and corners */
#panel-transcript.loaded .tr-wave {
  border: none;
  border-radius: 0;
}
#panel-transcript.loaded .tr-wavemini { border-radius: 0; }
/* (The status line used to be padded to the chrome inset here. It moved into
   <header> on July 28 2026 — window chrome, not panel content — so there is
   nothing left inside the panel to inset. See `header .tr-status`.) */
/* THE DOCUMENT: the one thing that is inset and rounded */
#panel-transcript.loaded #tr-list {
  margin-left: var(--sheet-inset);
  margin-right: var(--sheet-inset);
}
/* one hairline weight throughout, the header included */
body > header:not(.cv-head) { border-bottom-width: 1px; }
/* The sticky header has to COVER that wider sheet as it scrolls beneath it.
   Widen the bar's opaque ground by the same 0.7rem on each side and pad its
   contents back to the chrome's inset — otherwise the lichen peeks out in a
   thin strip down both edges while scrolling (owner report, July 19). Kept
   next to the rule above so the two insets never drift apart. */
#panel-transcript.loaded .tr-topbar {
  margin-left: -0.7rem;
  margin-right: -0.7rem;
  padding-left: 0.7rem;
  padding-right: 0.7rem;
}
#panel-transcript.loaded .field { max-width: 44rem; }

.tr-status { color: var(--muted); font-size: 0.8rem; min-height: 1em; margin: 0.2rem 0 0.6rem; }
/* With the region count retired (July 20) this line is empty most of the time,
   and min-height would still hold a row open for nothing. Collapse it when it
   has nothing to say; it reappears the moment a real message arrives. */
.tr-status:empty { display: none; }

/* ---- Kanona loader: the pestle lifts, fans a little to one side, and
   pounds; corn hops and dust puffs on each strike. One 1.8s cycle holds
   two strikes (left fan, then right fan); the right-side puff reuses the
   left's keyframes half a cycle out of phase. ---- */
.kanona-loader { display: flex; justify-content: center; padding: 1rem 0 0.2rem; color: var(--ink); }
.kanona-shadow { fill: currentColor; opacity: 0.15; }
.kanona-pestle {
  animation: kanona-pound 1.8s infinite;
  transform-box: view-box;
  transform-origin: 16px 23px; /* the pestle tip, inside the hollow */
}
@keyframes kanona-pound {
  0%   { transform: translateY(0)      rotate(0deg); }
  6%   { transform: translateY(0)      rotate(0deg);    animation-timing-function: cubic-bezier(0.2, 0.6, 0.4, 1); }
  30%  { transform: translateY(-7px)   rotate(-14deg); }
  40%  { transform: translateY(-7.5px) rotate(-9deg);   animation-timing-function: cubic-bezier(0.6, 0, 1, 0.8); }
  50%  { transform: translateY(0)      rotate(0deg); }
  56%  { transform: translateY(0)      rotate(0deg);    animation-timing-function: cubic-bezier(0.2, 0.6, 0.4, 1); }
  80%  { transform: translateY(-7px)   rotate(14deg); }
  90%  { transform: translateY(-7.5px) rotate(9deg);    animation-timing-function: cubic-bezier(0.6, 0, 1, 0.8); }
  100% { transform: translateY(0)      rotate(0deg); }
}
.kanona-corn-a, .kanona-corn-b { animation: kanona-hop 1.8s infinite; }
.kanona-corn-b { animation-delay: 0.07s; }
@keyframes kanona-hop {
  0%, 7%, 50%, 57%, 100% { transform: translateY(0); }
  3%, 53% { transform: translateY(-2px); }
}
.kanona-puff { opacity: 0; animation: kanona-puff 1.8s infinite; }
.kanona-puff-r { animation-delay: -0.9s; } /* fires on the other strike */
@keyframes kanona-puff {
  0%, 50% { opacity: 0; transform: translate(0, 0); }
  53% { opacity: 0.85; transform: translate(-0.5px, -1px); }
  66%, 100% { opacity: 0; transform: translate(-2px, -3px); }
}
.kanona-pop { opacity: 0; animation: kanona-pop 3.6s infinite; } /* every other strike */
@keyframes kanona-pop {
  0%, 24% { opacity: 0; transform: translate(0, 0); }
  26% { opacity: 1; transform: translate(1px, -3px); }
  29% { transform: translate(2px, -4px); }
  33% { opacity: 1; transform: translate(3px, -1px); }
  34%, 100% { opacity: 0; transform: translate(0, 0); }
}
@media (prefers-reduced-motion: reduce) {
  .kanona-pestle, .kanona-corn-a, .kanona-corn-b, .kanona-puff, .kanona-pop { animation: none; }
  .kanona-puff, .kanona-pop { opacity: 0; }
}

.tr-topbar {
  position: sticky;
  top: 0;
  z-index: 5;
  background: var(--panel);
  /* trimmed (owner ruling, July 20) — this wrapper's own padding was adding a
     third of a row above and half a row below every band inside it */
  padding: 0.15rem 0 0.2rem;
}
/* ONE line, not two (owner report, 2026-07-30). The wave view ends in its
   draggable grip; this wrapper's own background then ran on for another 0.2rem
   before the transcript began, so the grip's hairline and the panel's bottom
   edge read as two parallel rules a few pixels apart. With the dock open the
   grip IS the boundary, so the wrapper stops exactly there — the padding stays
   for every other state, where there is no grip to serve as the edge. */
.tr-topbar:has(#tr-wavedock:not([hidden])) { padding-bottom: 0; }
.tr-title { font-size: 1.25rem; font-weight: bold; }
/* Header title row: the title, then the project-kind chip and the red
   working-draft badge (owner ruling, July 19). Both hide when unset. */
/* line-height: FreeSerif's default leading is tall (its metrics cover many
   scripts), and across the header's three stacked rows that tallness read as
   blank space (owner ruling, July 31 — tighten the header). Set explicitly
   here and on .tr-speakers/.tr-meta below; the syllabary's combining marks
   still draw fine — line-height spaces the rows, it never clips glyphs. */
.tr-titlerow { display: flex; align-items: baseline; flex-wrap: wrap; gap: 0.25rem 0.5rem;
  line-height: 1.15; }
.tr-kindchip {
  font-size: 0.7rem; text-transform: uppercase; letter-spacing: 0.04em;
  color: var(--muted); border: 1px solid var(--border); border-radius: 5px;
  padding: 0.05rem 0.4rem; align-self: center;
}
.tr-draftbadge {
  font-size: 0.7rem; text-transform: uppercase; letter-spacing: 0.04em;
  color: #ffffff; background: #c0331b; border-radius: 5px;
  padding: 0.05rem 0.4rem; align-self: center;
}
/* The rename pencil: a quiet edit-mode affordance beside the title — muted
   until hovered, like the cover's minimize control. */
.tr-renamebtn {
  background: none; border: none; padding: 0 0.15rem; align-self: center;
  font-size: 0.95rem; color: var(--muted); cursor: pointer;
}
.tr-renamebtn:hover { background: none; color: var(--ink); }
/* The rename row stands in for the title row while it is open: the new file
   base, the folder around it, and the two buttons that settle it. */
.tr-renamerow {
  display: flex; align-items: center; flex-wrap: wrap; gap: 0.35rem 0.6rem;
  margin: 0.15rem 0;
}
.tr-renamefield {
  display: inline-flex; align-items: center; gap: 0.3rem;
  font-size: 0.8rem; color: var(--muted);
}
.tr-renamefield input {
  font-size: 0.9rem; min-width: 16rem; color: var(--ink);
}
.tr-renamefield input:disabled { opacity: 0.5; }
.tr-renameext { color: var(--muted); font-size: 0.85rem; }
/* A roster speaker who isn't on any clip yet: the number key reaches them, but
   they are an OFFER, not a fact about this recording. The chip KEEPS its full
   lichen ground — the dyes only read against it (dark dye text on the dark
   theme's bare background is invisible) — and a dashed ring alone says
   "available, not yet used". */
.tr-dye.tr-dye-unused {
  outline: 1px dashed currentColor;
  outline-offset: -1px;
}
/* The chip's key number (owner ruling, 2026-07-28): 1–9 on the wave lane mark
   a clip as that speaker, so the chip wears its own number rather than asking
   anyone to remember the order. Reads as a key cap, not as a count — set into
   the chip on a darker patch, monospace, and never bolder than the name. */
.tr-spk-key {
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: 0.68rem; font-weight: 600;
  padding: 0 0.28rem; border-radius: 3px;
  background: rgba(0, 0, 0, 0.16);
  opacity: 0.75;
}
/* Inline "＋ add" on a speaker chip whose initials aren't in the book. */
.tr-spk-add {
  font: inherit; font-size: 0.72rem; font-weight: normal;
  border: none; border-radius: 4px; padding: 0 0.3rem; cursor: pointer;
  background: rgba(0, 0, 0, 0.14); color: inherit;
}
/* Recording cover photo (cover.<ext> at the Data-folder root): a modest
   thumbnail beside the title/speaker block; click enlarges via the same
   lightbox the margin figures use. No cover → the img stays hidden. */
.tr-head { display: flex; gap: 0.75rem; align-items: flex-start; }
.tr-headtext { min-width: 0; }
/* Cover photo + its corner resize grip (owner request, July 19). The wrap is
   the positioning context; the photo keeps its default size until the grip is
   dragged, at which point JS writes an explicit width/height inline. */
.tr-coverwrap { position: relative; flex: none; display: inline-block; line-height: 0; }
.tr-coverwrap[hidden] { display: none; }
#tr-cover {
  height: 140px; /* owner, July 18: a little bigger again */
  max-width: 240px;
  /* contain, not cover (owner ruling, July 20): the grip scales the photo
     whole, so the frame already carries the picture's own proportions —
     contain guarantees nothing is ever cropped away, including for a size
     saved back when the frame could be dragged out of shape. */
  object-fit: contain;
  border-radius: 6px;
  cursor: zoom-in;
  display: block;
}
#tr-cover[hidden] { display: none; }
/* the grip: the familiar diagonal ridges in the bottom-right corner. It rides
   ON the photo, so it carries its own dark backing — a photo can be any
   brightness underneath and the ridges still read. */
.tr-covergrip {
  position: absolute;
  right: 2px;
  bottom: 2px;
  width: 16px;
  height: 16px;
  cursor: nwse-resize;
  touch-action: none;
  border-radius: 0 0 5px 0;
  background:
    repeating-linear-gradient(315deg,
      rgba(240, 237, 228, 0.95) 0 1.4px, transparent 1.4px 4.3px),
    rgba(0, 0, 0, 0.34);
  opacity: 0.72;
  transition: opacity 0.12s;
}
.tr-coverwrap:hover .tr-covergrip,
.tr-covergrip.dragging { opacity: 1; }
/* Minimize, in the opposite corner from the grip: folds the photo down to the
   height of the title block beside it, and back (owner request, July 20).
   Carries its own dark backing for the same reason the grip does — it sits on
   a photograph, which can be any brightness. */
.tr-covermin {
  position: absolute;
  right: 2px;
  top: 2px;
  width: 16px;
  height: 16px;
  padding: 0;
  border: none;
  border-radius: 0 5px 0 0;
  background: rgba(0, 0, 0, 0.34);
  color: rgba(240, 237, 228, 0.95);
  font-size: 0.58rem;
  line-height: 16px;
  text-align: center;
  cursor: pointer;
  opacity: 0.72;
  transition: opacity 0.12s;
}
.tr-coverwrap:hover .tr-covermin { opacity: 1; }
.tr-covermin:focus-visible {
  outline: none;
  opacity: 1;
  box-shadow: 0 0 0 2px rgba(150, 61, 22, 0.75);
}
.tr-covergrip:focus-visible {
  outline: none;
  opacity: 1;
  box-shadow: 0 0 0 2px rgba(150, 61, 22, 0.75);
}
.tr-speakers { font-size: 0.95rem; line-height: 1.25; /* see .tr-titlerow */ }
.tr-speakers:empty { display: none; }
/* Credit line (multi-voice recordings): each speaker's credit in their dye
   on a small lichen chip, so the dyes read the same in light and dark
   themes. The pickers live in the "Speaker colors ▾" dropdown. */
.tr-speakers.has-dyes { display: flex; flex-wrap: wrap; gap: 0.3rem 0.6rem; align-items: baseline; }
.tr-dye {
  display: inline-block;
  font-weight: bold;
  background: #c3cdad;
  border-radius: 6px;
  padding: 0.05rem 0.45rem;
}
/* The panel hugs its content: two snug columns (chip, picker), no spread. */
#tr-dyepanel { width: max-content; max-width: 80vw; }
#tr-dyepanel .tr-metanote { max-width: 17rem; }
#tr-dyerows {
  display: grid;
  grid-template-columns: max-content max-content;
  gap: 0.3rem 0.7rem;
  align-items: baseline;
}
.tr-dyerow { display: contents; }
#tr-dyerows .tr-dye { justify-self: start; } /* hug the name, don't fill the column */
.tr-dyepick {
  font: inherit;
  font-size: 0.78rem;
  font-weight: bold;
  border: 1px solid #9aa584;
  border-radius: 4px;
  background: #c3cdad; /* lichen, like the chips — dye text reads in both themes */
  padding: 0.05rem 0.15rem;
  cursor: pointer;
}
.tr-meta { color: var(--muted); font-size: 0.8rem; line-height: 1.25; /* see .tr-titlerow */ }

/* WAV details: dropdown showing the tags a stamp would write */
.tr-metawrap { position: relative; }
.tr-metapanel {
  position: absolute; left: 0; top: calc(100% + 0.4rem); z-index: 30;
  width: 24rem; max-width: 80vw;
  background: var(--panel); border: 1px solid var(--muted); border-radius: 8px;
  padding: 0.7rem 0.9rem; box-shadow: 0 6px 18px rgba(0, 0, 0, 0.35);
}
.tr-metafields { display: grid; grid-template-columns: auto 1fr; gap: 0.15rem 0.7rem; margin-bottom: 0.6rem; }
.tr-metafields .k { color: var(--muted); font-size: 0.75rem; text-align: right; }
.tr-metafields .v { font-size: 0.85rem; overflow-wrap: anywhere; }
.tr-metanote { color: var(--muted); font-size: 0.7rem; margin: 0.5rem 0 0; }
/* The About record, now inline in the details band (owner ruling July 18) —
   a compact wrapping set of fields behind the edit toggle. */
.tr-about { display: flex; flex-wrap: wrap; gap: 0.1rem 1rem; align-items: flex-start; max-width: 46rem; }
.tr-about .tr-metasub { flex-basis: 100%; margin: 0; }
/* read-only transcript facts line at the top of the About record (owner
   ruling, July 19): line/flag counts, duration, audio format. */
.tr-about-facts { flex-basis: 100%; color: var(--muted); font-size: 0.72rem; margin: 0 0 0.15rem; }
.tr-about-facts:empty { display: none; }
/* The details card's About record: real columns, each a fixed stack of fields
   (owner ruling, July 27, replacing the July 19 two-up auto-flow grid).
   Auto-flow was the whole problem — updateFieldVisibility hides every empty
   field when "edit" is off, so each recording reflowed the grid differently and
   no two labels ever lined up. Now the grid lays out COLUMNS, and the fields
   inside a column are a plain vertical stack, so hiding one shortens its column
   and moves nothing else. Columns collapse 3 → 2 → 1 as the window narrows. */
.tr-aboutgrid { flex-basis: 100%; display: grid; grid-template-columns: 1fr;
  gap: 0.5rem 2.2rem; align-items: start; margin: 0.2rem 0; }
.af-col { display: flex; flex-direction: column; gap: 0.5rem; min-width: 0; }
/* Three columns or one — never two. Two columns of three stacks always wraps
   the third under the first, and grid row alignment then leaves a hole the
   height of the tallest stack. Below the breakpoint the three stacks simply
   run on as one list. */
/* …and the first of the three is the narrow one — a project-type menu, a
   checkbox and a place name don't need the width the credit chips and the
   archive names do. */
@media (min-width: 1000px) { .tr-aboutgrid { grid-template-columns: 0.72fr 1fr 1fr; } }
.tr-aboutgrid .af { display: flex; flex-direction: column; gap: 0.15rem;
  font-size: 0.78rem; min-width: 0; }
.tr-aboutgrid .af-l { color: var(--muted); }
.tr-aboutgrid .af.af-toggle { flex-direction: row; align-items: center; gap: 0.35rem; }
.tr-aboutgrid .af input[type="text"], .tr-aboutgrid .af select, .tr-aboutgrid .af textarea {
  font: inherit; font-size: 0.78rem; width: 100%; padding: 0.3rem 0.35rem;
  border: 1px solid var(--border); border-radius: 6px; background: var(--bg); color: var(--ink); }
.tr-aboutgrid .af textarea { resize: vertical; }
.tr-aboutgrid .af input:disabled, .tr-aboutgrid .af select:disabled, .tr-aboutgrid .af textarea:disabled { opacity: 0.6; }
/* chip lists (people, subjects, patron) */
.tr-chips { display: flex; flex-wrap: wrap; gap: 0.25rem; align-items: center; min-height: 1.7rem; }
.tr-chip { display: inline-flex; align-items: center; gap: 0.2rem;
  background: #c3cdad; color: #26221c; border-radius: 999px; padding: 0.05rem 0.5rem; font-size: 0.75rem; }
.tr-chip-x { border: none; background: none; color: inherit; cursor: pointer;
  font-size: 0.85rem; line-height: 1; padding: 0; }
.tr-chip-add { font: inherit; font-size: 0.72rem; width: 6.5rem; padding: 0.1rem 0.45rem;
  border: 1px dashed var(--border); border-radius: 999px; background: transparent; color: var(--ink); }
.tr-chip-empty { color: var(--muted); }
/* Details band as ONE stacked card (owner ruling, July 19) — not the ribbon's
   side-by-side groups: the read-only Recording/Transcript facts on top, the
   editable About record below, then the photo/voices actions. */
.tr-detailscol { flex-direction: column; align-items: stretch; gap: 0.6rem; }
.tr-detailscol > .tr-ribgroup { width: 100%; }
/* read-only Recording/Transcript on the LEFT, editable About on the RIGHT
   (owner ruling, July 19). */
/* The read-only column takes the wider share (owner ruling, July 27): it holds
   the long bilingual title and the bilingual date, which wrap at 17rem, while
   the About column beside it held only a project-type menu and a place name. */
.tr-detailscol .tr-about { display: grid; grid-template-columns: minmax(13rem, 23rem) 1fr;
  gap: 0.5rem 1.8rem; align-items: start; max-width: 76rem; width: 100%; }
/* the ribbon's .row centres its children at their intrinsic width; in this
   stacked card the About grid has to actually FILL the band, or the three
   columns buy nothing but narrower inputs */
.tr-detailscol .tr-ribgroup .row { width: 100%; }
.tr-aboutedit { grid-column: 2; min-width: 0; }
/* read-only Recording + Transcript sections */
.tr-readonly { grid-column: 1; display: flex; flex-direction: column; gap: 0.15rem; }
.tr-rosec { display: flex; flex-direction: column; gap: 0.1rem; }
.tr-rohead { font-size: 0.62rem; letter-spacing: 0.06em; text-transform: uppercase;
  color: var(--muted); border-bottom: 1px solid var(--border); padding-bottom: 0.15rem; margin: 0.5rem 0 0.25rem; }
.tr-rorow { display: grid; grid-template-columns: 5.2rem 1fr; gap: 0.1rem 0.6rem; align-items: baseline; }
.tr-rolab { font-size: 0.72rem; color: var(--muted); }
.tr-roval { font-size: 0.8rem; }
.tr-roval sup { font-size: 0.62em; }
.tr-chip-ro { background: #c3cdad; color: #26221c; border-radius: 6px; padding: 0.02rem 0.35rem; font-size: 0.72rem; }
/* The English date sits UNDER the ᏣᎳᎩ one (owner ruling, July 27), not trailing
   it — the two read as a stacked pair, and the ᏣᎳᎩ line keeps the full column
   width instead of being squeezed by whatever the English needs. */
.tr-rodate-eng { color: var(--muted); display: block; }
.tr-about .field { margin: 0.15rem 0; font-size: 0.78rem; }
.tr-about .field input[type="text"], .tr-about .field textarea {
  display: block; margin-top: 0.15rem; font: inherit; font-size: 0.8rem;
  width: 13rem; max-width: 100%; padding: 0.25rem 0.4rem;
  border: 1px solid var(--border); border-radius: 6px; background: var(--bg); color: var(--ink);
}
.tr-about .field textarea { resize: vertical; }
.tr-about .field input:disabled, .tr-about .field textarea:disabled { opacity: 0.6; }
.tr-about > button, .tr-about > .tr-metanote { flex-basis: 100%; }
/* Products band: the inline checklist + options (deck-builder drawer removed,
   owner ruling July 18). */
.tr-prodlog { margin: 0.4rem 0.3rem 0; }
.tr-prodlog:empty { display: none; }

/* About this recording: the session/consent record panel. (Left-anchored
   like the others — inside the ribbon its button sits toward the left.) */
.tr-metapanel .field { margin: 0.5rem 0; font-size: 0.8rem; }
.tr-metapanel .field textarea {
  display: block; margin-top: 0.3rem; font: inherit; width: 100%; max-width: 100%;
  padding: 0.35rem 0.4rem; border: 1px solid var(--border); border-radius: 6px;
  background: var(--bg); color: var(--ink); resize: vertical;
}

.tr-controls {
  position: relative;          /* anchors the corner collapse ⌃ */
  margin-top: 0.5rem;
  font-size: 0.85rem;
  background: var(--log-bg);
  border: 1px solid var(--border);
  border-radius: 8px;
  padding: 0.4rem 0.45rem;
}
.tr-toggle { color: var(--muted); cursor: pointer; }
/* Custom checkboxes across the ribbon (owner pick "C", July 18): the default
   box redrawn in the palette — a rounded box that fills with the accent and
   shows a tick when on. Pure CSS, keyboard-accessible, theme-aware. */
.tr-controls input[type="checkbox"] {
  appearance: none;
  -webkit-appearance: none;
  width: 1rem;
  height: 1rem;
  margin: 0 0.35rem 0 0;
  vertical-align: -0.15rem;
  border: 1.5px solid var(--muted);
  border-radius: 5px;
  background: transparent no-repeat center / 0.72rem;
  cursor: pointer;
  transition: background-color .12s, border-color .12s;
}
.tr-controls input[type="checkbox"]:hover { border-color: var(--ink); }
.tr-controls input[type="checkbox"]:checked {
  background-color: var(--accent);
  border-color: var(--accent);
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath fill='none' stroke='%23ffffff' stroke-width='2.6' stroke-linecap='round' stroke-linejoin='round' d='M3.5 8.5l3 3 6-7'/%3E%3C/svg%3E");
}
.tr-controls input[type="checkbox"]:focus-visible { outline: 2px solid var(--rust); outline-offset: 1px; }
/* a display group whose only control is hidden (notes = footnotes, when the
   recording has none) collapses so no empty cap shows */
.tr-notesgrp:not(:has(.row > label:not([hidden]))) { display: none; }
.tr-toggle select {
  font: inherit;
  font-size: 0.85rem;
  padding: 0.1rem 0.3rem;
  border: 1px solid #5a4526;
  border-radius: 6px;
  background: #f0e3c0;
  color: #5a4526;
  cursor: pointer;
}
.tr-spacer { flex: 1; }
.tr-controls button {
  font: inherit;
  font-size: 0.85rem;
  padding: 0.25rem 0.8rem;
  border-radius: 999px;
  border: 1px solid #5a4526;
  background: #5a4526;
  color: #f0e3c0;
  cursor: pointer;
}
.tr-controls button:hover { background: #f0e3c0; color: #5a4526; }
/* A disabled ribbon button has to LOOK unavailable — "Save details to WAV" now
   sleeps until the record changes, and the products buttons have always slept
   until there are clips, but both read as pressable (owner report, July 27).
   Same treatment the wave and fontbook controls already use. */
.tr-controls button:disabled { opacity: 0.4; cursor: default; }
/* hover must not light it up — but pointer-events stay ON so the title
   attribute can still say WHY it is asleep */
.tr-controls button:disabled:hover { background: #5a4526; color: #f0e3c0; }

/* ---- Ribbon BANDS (owner ruling, July 18): the tab row opens inline
   flat-drawer "bands" below it — one at a time, collapsible from the corner
   ⌃. edit + Save sit at the row's end behind a divider. The transport strip
   + wave dock below are persistent siblings (they survive the collapse). ---- */
.tr-ribtabs { display: flex; align-items: center; gap: 0.35rem; flex-wrap: wrap; }
/* Header row: header block on the left, EVERY toggle pinned top-right in
   .tr-toptoggles — the header toggle and, since July 31 (owner ruling: the
   strip's own row above bar 1 was a band's height of empty space), the
   .tr-bartoggles strip. The cluster is a sibling of .tr-head so the header
   toggle can fold the header without folding itself. */
/* position:relative so the footer line can be placed against this row's own
   box — it is absolutely positioned to land on the footnote rail */
.tr-headrow { display: flex; align-items: flex-start; gap: 0.75rem; position: relative; }
.tr-headrow .tr-head { flex: 1; min-width: 0; }
/* The cluster is a 2×2 GRID (owner ruling, July 31): the header toggle holds
   row 1 col 1 and NEVER moves; the 4-chip strip sits in row 2 col 2 — tucked
   inside the header block's own height, so it still costs no row of its own —
   and rises STRAIGHT UP into row 1 col 2 when the header folds. This is the
   exact geometry the retired JS width-reserve used to measure out (header
   toggle just-left of where the four land), now plain CSS. */
.tr-toptoggles { display: grid; grid-template-columns: auto auto;
  gap: 0.3rem 0.55rem; align-items: center; margin: 0 0.1rem 0 auto; }
#tr-headtoggle { grid-area: 1 / 1; }
.tr-toptoggles .tr-bartoggles { grid-area: 2 / 2; }
/* folded: the four snap up beside the header toggle; row 2 collapses away */
.tr-headrow:has(#tr-head[hidden]) .tr-bartoggles { grid-area: 1 / 2; }
/* …and the chips inherit the band. They were sitting ON the sheet — the
   topbar's own padding-bottom goes to 0 whenever the wave dock exists, dock
   drawn or not — while all the slack pooled above them (owner request,
   Aug 3: clip the top, cushion the bottom). Folded state only: open, the
   chips sit mid-header with the title block's own text below them. */
.tr-headrow:has(#tr-head[hidden]) { padding-bottom: 0.3rem; }
/* Breathing room between the chips (owner request, July 31): the shared
   dock's snug 0.35rem gap and 0.45rem side padding read cramped now the six
   ride as one cluster. Overridden HERE rather than in waveview.css — that
   sheet is vendored identical across seats and also loads after this one,
   hence the descendant selectors, which outweigh its plain classes. */
.tr-toptoggles .tr-bartoggles { gap: 0.55rem; }
.tr-toptoggles .tr-wavetoggle { padding: 0.2rem 0.55rem; }
/* The running footer shares that row, taking the empty left of it (owner
   ruling, July 20). margin-right:auto rather than space-between, so the
   toggles stay right-aligned even when no recording is open and this is
   hidden. It truncates rather than wrapping — the row must stay one line
   tall, and the full text is in the title attribute. */
.tr-footline {
  /* Ends on the footnote rail (owner ruling, July 20): the line's right edge
     lands on the same vertical the footnote divider draws, so the two rhyme
     instead of the text simply running out from the left. Built from the same
     --fnote-marg the divider uses — set on the panel, so this can read it —
     plus the list's 0.8rem and the row's 0.3rem, the sheet's inset and the
     scrollbar gutter. It holds that line whether or not footnotes are
     currently displayed, so turning them on and off never shifts it.
     Positioned ABSOLUTELY rather than laid out in the row: it shares that row
     with the toggle strip, and as a flex item its right margin would measure
     from the toggles — which are a different width for every recording — not
     from the window. Out of flow, the offset is the rail's and nothing else's.
     Offsets resolve against the row's padding box, so --chrome-pad is NOT
     subtracted here; the padding box already ends at the window edge. */
  position: absolute;
  right: calc(
    var(--sheet-inset, 0.8rem) + var(--sb-gutter) + 1.1rem + var(--fnote-marg, 15.5rem));
  left: 0;
  top: 50%;
  transform: translateY(-50%);
  overflow: hidden;
  white-space: nowrap;
  text-overflow: ellipsis;
  text-align: right;
  /* Set like a running footer rather than like UI chrome (owner ruling,
     July 20): the book face it is already in, a little letter-spacing to open
     it up at this size, and tabular figures so the date and version stamp keep
     their columns instead of jittering as they change. */
  font-size: 0.74rem;
  letter-spacing: 0.045em;
  font-variant-numeric: tabular-nums;
  color: var(--muted);
}
/* the separators carry the rhythm, so they sit back a step from the words */
.tr-footsep { opacity: 0.45; padding: 0 0.4em; }
/* the one red thing, exactly as the export footer carries it */
.tr-footdraft { color: #c0592b; font-weight: bold; }
/* the strip itself: reuses the .tr-wavetoggle chip look; sits inline whether in
   its lower slot or snapped up beside the header toggle */
/* Hairline between the two families in the strip: chrome rows to its left
   (header, band shell, transport), the wave's own three to its right. Without
   it the six read as six unrelated glyphs. Sits inside .tr-bartoggles, so it
   travels with the strip when the header folds and it snaps up top. */
.tr-ribspacer { flex: 1; min-width: 0.5rem; }
.tr-editend { display: inline-flex; align-items: center; gap: 0.6rem;
  padding-left: 0.6rem; border-left: 1px solid var(--border); }
.tr-controls .tr-ribtab {
  background: #5a4526;
  color: #f0e3c0;
  border: 1px solid #5a4526;
  border-radius: 999px;
  padding: 0.2rem 0.8rem;
}
/* hover only warms the dark pill; the full tan inversion is reserved to mean
   "this band is deployed". No weight or padding change — the pill never
   resizes, so the strip doesn't shuffle as you open bands. */
/* colour is restated because .tr-controls button:hover above flips text dark */
.tr-controls .tr-ribtab:hover { background: #6d5430; border-color: #6d5430; color: #f0e3c0; }
.tr-controls .tr-ribtab.active,
.tr-controls .tr-ribtab.active:hover { background: #f0e3c0; color: #5a4526; }
/* fixed cell for the ▸/▾ caret — the two glyphs are not the same width */
.tr-ribtab-caret { display: inline-block; width: 0.7rem; text-align: center; }
.tr-ribbon { /* drawer area — no box of its own; the open band draws the rule */ }
.tr-ribpage { border-top: 1px solid var(--border); margin-top: 0.4rem; padding-top: 0.5rem; }
.tr-transport { display: flex; align-items: center; gap: 0.7rem; flex-wrap: wrap;
  margin-top: 0.4rem; background: var(--log-bg); border: 1px solid var(--border);
  border-radius: 8px; padding: 0.3rem 0.6rem; }
/* play/pause: an icon-only rust disc, no text (owner ruling, July 19) */
/* Playback controls folded into the wave toggles bar (owner ruling July 22):
   play, then the mockingbird (volume) and turtle (speed), grouped at the bar's
   left end. The group itself reads left-to-right even though the bar around it
   is row-reverse (it goes in last, so it lands leftmost). */
.tr-ribrow { display: flex; align-items: stretch; flex-wrap: wrap; row-gap: 0.5rem; }
.tr-ribgroup {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.35rem;
  padding: 0 0.9rem;
  border-right: 1px solid var(--border);
}
.tr-ribgroup:last-child { border-right: none; }
.tr-ribgroup .row {
  display: flex;
  align-items: center;
  gap: 0.5rem 0.9rem;
  flex-wrap: wrap;
  justify-content: center;
  flex: 1;
}
.tr-ribgroup .cap {
  font-size: 0.62rem;
  color: var(--muted);
  text-transform: uppercase;
  letter-spacing: 0.09em;
}
#tr-consultant-extras { display: inline-flex; align-items: center; gap: 0.6rem; }
/* off: keep the width so the size steppers + lock slot in without the
   Consultant view button jumping (owner ruling, July 19). The !important
   overrides the global `[hidden] { display: none !important }` reset — we
   want this one hidden box to hold its space, not collapse. */
#tr-consultant-extras[hidden] { display: inline-flex !important; visibility: hidden; }

/* "display silent vowels: [ ] parenthesis [ ] underline" — a labeled pair
   of mutually exclusive checkboxes (owner wording, July 14) */
.tr-silent { color: var(--muted); display: inline-flex; align-items: center; gap: 0.5rem; }
.tr-silent[hidden] { display: none; }
/* The consultant group's phonetics half (owner ruling, July 27): the same
   controls as the transcript's "phonetics" group, but for the other screen, so
   they ride in one row with the line toggles under a single caption. The rule
   before them separates the two halves without spending a second caption. */
.tr-cvpho {
  display: inline-flex; align-items: center; gap: 0.5rem;
  padding-left: 0.7rem; margin-left: 0.2rem;
  border-left: 1px solid var(--border);
}
.tr-cvpho[hidden] { display: none; }

/* The scrub line: position in the whole recording; click or drag to seek. */
.tr-scrubrow { display: flex; align-items: center; gap: 0.6rem; margin-top: 0.5rem; padding: 0 0.3rem; }
.tr-scrub {
  position: relative;
  flex: 1;
  height: 10px;
  border-radius: 999px;
  background: #c3cdad;
  border: 1px solid var(--border);
  overflow: hidden;
  cursor: pointer;
  touch-action: none;
}
.tr-scrubfill { position: absolute; top: 0; bottom: 0; left: 0; width: 0; background: #963d16; }
.tr-scrubtime {
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: 0.72rem;
  color: var(--muted);
  white-space: nowrap;
}

/* The wave view: an in-app waveform EDITOR — the recording's shape with
   region bands in the speaker dyes, a zoom/pan viewport, a time ruler, and
   a whole-file overview minimap. Click seeks; dragging open wave sweeps out
   a new clip; dragging a region edge moves a boundary. See js/waveview.js.
   It lives in its OWN dock under the ribbon (owner ruling, July 16) —
   outside the tab pages, still inside the sticky topbar — behind a little
   arrow at the top right (owner request, July 16), collapsed by default
   each load. The dock is position:relative so the arrow can sit in the
   wave view's top-right corner when open without eating a bar of height. */
/* the two dropdown toggles that ride at the transport's far right (owner
   ruling, July 19): understated on the dark strip, warming to the rust accent
   when their dropdown is open. */
/* Wave edits band (owner ruling, July 22): its own dropdown for the tools that
   rewrite the audio, sitting between the display bar and the minimap. Left-
   packed with a muted label, so it reads as a labelled group that grows
   rightward as more destructive edits join gain. */
/* zoom in/fit/out fused into one segmented control */
/* the two extra zoom actions (zoom-to-selection, zoom-back) as icon buttons */
/* soft on/off switch in place of the bare checkbox (on = the wave's rust) */
/* the shortcuts hint now lives in a popover on this help bubble */
/* Ribbon dB meter (July 21 candidate B) retired July 22: the level meter now
   lives only as the right-edge column, with its scale on its right, and the
   wave carries its own amplitude scale on the left — the OcenAudio layout. */
/* the gain form's dB field — a small right-aligned number, not the
   newclip text box's stretching line */
/* Draggable bottom edge (owner ruling, July 16): the expanded panel's
   bottom line is a grab handle — drag down to grow the waveform lane
   (min = the stylesheet 150px, max ≈ 4× or 40% of the window; js/waveview.js
   clamps), up to shrink back. Same splitter pattern as #tr-fndivider, same
   hairline dyes; height resets each load, display-toggle parity. */

.tr-list {
  background: #c3cdad;
  color: #000;
  border-radius: 8px;
  padding: 0.2rem 0.8rem;
  font-size: 16px; /* adjustable via the text-size stepper */
  position: relative; /* anchors the continuous footnote-margin divider line */
}
/* The footnote margin divider: ONE continuous hairline down the whole page
   (owner ruling, July 15 — supersedes the per-row .has-marg hairline), sitting
   at the left edge of the margin column. The margin width (--fnote-marg, from
   the Display-tab slider) sets how far in from the right the line sits; the
   0.8rem is the list's own right padding, 0.3rem the row's. Hidden when the
   margin is folded away and on the narrow-screen fallback below. */
.tr-list.fnotes-on::after,
.tr-list.edit-mode::after {
  content: ''; position: absolute; top: 0; bottom: 0;
  right: calc(0.8rem + 0.3rem + var(--fnote-marg, 15.5rem));
  width: 1px; background: #a8a488; pointer-events: none;
}
/* The divider is DRAGGABLE (owner ruling, July 15 — supersedes the margin
   slider + note-size stepper): a transparent grab strip centred on the
   hairline, ew-resize cursor. Dragging sets --fnote-marg (the column width);
   the note text scales with the width up to a cap (js/transcript.js). Screen
   only; the PDF/Word transcript keep their set sizes. Same visibility as the
   hairline, so it's absent when the margin is folded away. */
#tr-fndivider { display: none; }
.tr-list.fnotes-on > #tr-fndivider,
.tr-list.edit-mode > #tr-fndivider {
  display: block; position: absolute; top: 0; bottom: 0;
  right: calc(0.8rem + 0.3rem + var(--fnote-marg, 15.5rem) - 5px);
  width: 11px; cursor: ew-resize; z-index: 4; background: transparent;
}
/* while dragging (or hovering the strip) the hairline thickens and darkens */
.tr-list.fndrag { user-select: none; }
.tr-list.fndrag::after,
.tr-list:has(#tr-fndivider:hover)::after { width: 2px; background: #7a6a48; }

/* Companion video: a fixed pane at the top right. The WAV is the clock;
   the video mirrors it, muted, with a live caption of the active region. */
#tr-videopane {
  position: fixed;
  top: 7.5rem;
  right: 1.2rem;
  z-index: 6;
  width: min(30rem, 44vw);
  min-width: 14rem;
  max-width: 92vw;
  background: #26221c;
  border: 1px solid #5a4526;
  border-radius: 10px;
  padding: 0.4rem;
  box-shadow: 0 6px 20px rgba(0, 0, 0, 0.4);
  cursor: grab;
  resize: horizontal;
  overflow: hidden;
}
#tr-videoel { display: block; width: 100%; border-radius: 6px; background: #000; }
.tr-videocap {
  text-align: center;
  color: #f0e3c0;
  padding: 0.35rem 0.5rem 0.15rem;
  min-height: 2.6em;
}
.tr-videocap > div:first-child { font-size: 1.1rem; white-space: pre-wrap; }
.tr-videocap > div:last-child { font-size: 0.85rem; color: #c9bfa8; }
.tr-videoctl {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  color: #c9bfa8;
  font-size: 0.8rem;
  padding: 0.15rem 0.3rem 0.1rem;
}
.tr-videoctl button {
  font: inherit;
  font-size: 0.75rem;
  padding: 0.1rem 0.55rem;
  border-radius: 999px;
  border: 1px solid #5a4526;
  background: #5a4526;
  color: #f0e3c0;
  cursor: pointer;
}
.tr-videoctl button:hover { background: #f0e3c0; color: #5a4526; }
/* Script layout: fixed right-aligned gutter (timestamp over "Name:"), all
   text starting at one margin. */
.tr-row {
  display: grid;
  grid-template-columns: 6.2em 1fr;
  gap: 0 0.8rem;
  align-items: baseline;
  padding: 0.6rem 0.3rem;
  border-bottom: 1px solid #963d16;
  cursor: pointer;
  position: relative; /* anchors the seam clamp straddling the top border */
}
.tr-row:last-child { border-bottom: none; }
.tr-row:hover { background: #b9c49e; }
/* Each row wears its speaker's dye as a left-edge line (--spk set by the
   renderer; multi-voice recordings only). The playing row keeps its darker
   ground and thickens the line. RATIFIED (owner, July 18): "a thin left
   line which bulges out when the clip is in focus, corresponding to
   speaker color" — this closes the July 13 provisional as KEEP; an
   earlier same-day discard ruling in a parallel session was reversed. */
.tr-row { box-shadow: inset 3px 0 0 var(--spk, transparent); }
.tr-row.active { background: #aebc90; box-shadow: inset 7px 0 0 var(--spk, #963d16); }
/* Select-to-copy (owner ruling, July 23): a cursor sweep over the transcript
   selects its text for copying — timecodes, speaker names and every tier line
   included — while a plain click still plays the clip (the drag-vs-click split
   lives in transcript.js). The row chrome that isn't part of the record — the
   pick checkbox and the seam/merge handles — is held out of the selection so a
   copied passage carries only words, no stray control glyphs. */
.tr-pickbox, .tr-seam, .tr-mergeseam { user-select: none; -webkit-user-select: none; }
/* Over the record's words the pointer becomes an I-beam, inviting a
   select-drag; the hand (cursor: pointer on .tr-row) stays on the row's open
   space, where a plain click plays the clip. */
.tr-row .tr-t, .tr-row .tr-spk,
.tr-row .tr-syl, .tr-row .tr-mod, .tr-row .tr-pho,
.tr-row .tr-eng, .tr-row .tr-igt, .tr-row .tr-wfw { cursor: text; }
/* The selection highlight: a bright, see-through blue so the syllabary and
   romanization stay legible through it (owner ruling, July 24). */
.tr-list ::selection { background: rgba(64, 150, 255, 0.4); }
.tr-list ::-moz-selection { background: rgba(64, 150, 255, 0.4); }
.tr-row .tr-t { grid-area: 1 / 1; }
.tr-row .tr-spk { grid-area: 2 / 1; }
.tr-row .tr-syl { grid-area: 2 / 2; }
.tr-row .tr-modedit { grid-area: 3 / 2; }
.tr-row .tr-mod { grid-area: 4 / 2; }
.tr-row .tr-pho { grid-area: 5 / 2; }
.tr-row .tr-romedit { grid-area: 6 / 2; }
.tr-row .tr-igt { grid-area: 7 / 2; }
.tr-row .tr-glosswarn { grid-area: 8 / 2; }
/* word-for-word view: the columns take the interlinear block's rows — the
   two never show together (word-mode folds the gloss rows into the columns) */
.tr-row .tr-wfw { grid-area: 7 / 2; }
.tr-row .tr-wfwwarn { grid-area: 8 / 2; }
.tr-row .tr-morphedit { grid-area: 9 / 2; }
.tr-row .tr-glossedit { grid-area: 10 / 2; }
.tr-row .tr-bindrow { grid-area: 11 / 2; } /* Cherokee mirror above… */
/* (grid row 12 freed Aug 3 — the English boxes live inside the binding row now) */
.tr-row .tr-eng { grid-area: 12 / 2; }
.tr-row .tr-engedit { grid-area: 13 / 2; }
.tr-pickbox { display: none; align-items: flex-start; padding-top: 0.15rem; }
.tr-list.select-mode .tr-row { grid-template-columns: 2.5rem 6.2em 1fr; }
.tr-list.select-mode .tr-pickbox {
  display: flex; grid-area: 1 / 1 / 14 / 2; align-self: start;
  padding-left: 0.95rem; /* clears a lane at the far left for the seam clamp */
}
.tr-list.select-mode .tr-row .tr-t { grid-area: 1 / 2; }
.tr-list.select-mode .tr-row .tr-spk { grid-area: 2 / 2; }
.tr-list.select-mode .tr-row .tr-syl { grid-area: 2 / 3; }
.tr-list.select-mode .tr-row .tr-modedit { grid-area: 3 / 3; }
.tr-list.select-mode .tr-row .tr-mod { grid-area: 4 / 3; }
.tr-list.select-mode .tr-row .tr-pho { grid-area: 5 / 3; }
.tr-list.select-mode .tr-row .tr-romedit { grid-area: 6 / 3; }
.tr-list.select-mode .tr-row .tr-igt { grid-area: 7 / 3; }
.tr-list.select-mode .tr-row .tr-glosswarn { grid-area: 8 / 3; }
.tr-list.select-mode .tr-row .tr-wfw { grid-area: 7 / 3; }
.tr-list.select-mode .tr-row .tr-wfwwarn { grid-area: 8 / 3; }
.tr-list.select-mode .tr-row .tr-morphedit { grid-area: 9 / 3; }
.tr-list.select-mode .tr-row .tr-glossedit { grid-area: 10 / 3; }
.tr-list.select-mode .tr-row .tr-bindrow { grid-area: 11 / 3; }
.tr-list.select-mode .tr-row .tr-eng { grid-area: 12 / 3; }
.tr-list.select-mode .tr-row .tr-engedit { grid-area: 13 / 3; }
.tr-pick { accent-color: #5a4526; width: 1rem; height: 1rem; }

/* Seam clamp (owner ruling, July 15): the ONLY bind handle now. It straddles
   the border between two adjacent clips, in the far-left gutter by the
   checkboxes. Two bloodroot jaws close (bound) or gape (ghost offer). Sits a
   touch off the very edge so it never reads as a row's speaker-dye line. */
.tr-seam {
  position: absolute;
  top: -0.65rem;              /* centre the 1.3rem box on the top border (seam) */
  left: 0.3rem;
  width: 0.85rem;
  height: 1.3rem;
  z-index: 3;
  cursor: pointer;
  color: #a52f0b;             /* bloodroot */
}
.tr-seam[hidden] { display: none; }
/* Merge button (owner ruling, July 16): the clamp's real-edit sibling —
   ▾ over ▴ collapsing into one clip. Walnut vs the clamp's bloodroot. */
.tr-mergeseam {
  position: absolute;
  top: -0.65rem;
  left: 1.35rem;              /* just right of the clamp */
  width: 0.85rem;
  height: 1.3rem;
  z-index: 3;
  cursor: pointer;
  color: #452c10;             /* walnut */
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  font-size: 0.55rem;
  line-height: 0.5;
  opacity: 0.45;              /* a quiet offer, like the clamp ghost */
}
.tr-mergeseam:hover, .tr-mergeseam:focus-visible { opacity: 1; }
.tr-mergeseam[hidden] { display: none; }
.tr-seam svg { width: 100%; height: 100%; display: block; pointer-events: none; }
.tr-jaw {
  fill: currentColor;
  transition: transform 180ms cubic-bezier(0.34, 1.56, 0.64, 1); /* springy snap */
}
/* Open (ghost / unbound): the jaws are pulled apart, gaping at the seam. */
.tr-seam .tr-jaw-up { transform: translateY(-2.6px); }
.tr-seam .tr-jaw-dn { transform: translateY(2.6px); }
.tr-seam.is-ghost { opacity: 0.4; } /* a dim, click-to-bind offer */
/* Bound (solid): full bloodroot, jaws snapped shut to meet at the seam. */
.tr-seam.is-bound .tr-jaw-up,
.tr-seam.is-bound .tr-jaw-dn { transform: translateY(0); }
.tr-seam:focus-visible {
  outline: 2px solid #a52f0b;
  outline-offset: 1px;
  border-radius: 2px;
}
.tr-t {
  font-family: ui-monospace, Menlo, monospace;
  font-size: 0.72em;
  /* Was #6b6355 to match the print export, but that gray only clears WCAG AA
     on WHITE paper (5.9:1) — on the sage row it measured 2.94:1, well under
     the 4.5:1 body-text floor (contrast audit, July 20). Darkened for SCREEN
     only; #tr-print / products.js keep #6b6355, where it passes. */
  color: #4d473d;
  text-align: right;
  margin-bottom: 0.1rem;
}
/* the rust reads 3.49:1 on the ACTIVE row's darker sage — deepened just for
   this one on-panel use; the rust accent itself is unchanged everywhere else */
.tr-row.active .tr-t { color: #7b3212; }
/* Owner ruling, July 20: KEEP the dye palette as it is and lift the label off
   the sage with a thin dark outline instead of darkening the colours. The
   outline is drawn with paint-order:stroke fill, so the stroke sits BEHIND the
   glyph and the letterform keeps its full weight — a plain text-stroke centres
   on the outline and eats the counters shut at this size.
   Owner ruling, July 20 (later): raise the label to 0.9em and set it regular,
   untracked — the speaker is part of the record, not machine metadata, and
   should read as a name rather than a tag or share the timecode's size. Bold +
   tracking were also the widest treatment in the fixed 6.2em gutter, so
   dropping them buys room for longer names. The STROKE stays: without it half
   the dye palette (yellowroot, butternut, bloodroot, wild indigo) drops below
   AA 4.5:1 on the active row even at 14.4px — the very case the outline was
   added for. */
.tr-spk {
  font-size: 0.9em; font-weight: normal; text-align: right;
  paint-order: stroke fill;
  -webkit-text-stroke: 0.7px rgba(0, 0, 0, 0.9);
  text-stroke: 0.7px rgba(0, 0, 0, 0.9);
}
/* Speaker rotation (hexes mirror js/dyes.js): walnut, bloodroot,
   yellowroot, pokeberry, butternut, wild indigo, elderberry, sumac.
   (No greens — they vanish into the lichen ground.) */
.tr-spk-0 { color: #452c10; }
.tr-spk-1 { color: #a52f0b; }
.tr-spk-2 { color: #8a6d00; }
.tr-spk-3 { color: #83175f; }
.tr-spk-4 { color: #8f5d2b; }
.tr-spk-5 { color: #2f4a70; }
.tr-spk-6 { color: #46237e; }
.tr-spk-7 { color: #701a26; }
/* Variant 09 (owner ruling, July 20): a continuing speaker is NOT re-named —
   like a playscript, only a change of voice prints the name. renderRows tags
   a row .tr-spkcont when its speaker matches the row above; the dye left-edge
   still marks it, so continuity reads without the repeated label.
   visibility:hidden (not display:none) keeps the gutter column width so the
   syllabary stays aligned. Applies in reading AND edit mode (owner ruling,
   July 30 — the dye edge names the row well enough while editing). Only
   select mode re-shows every name: there rows are picked out of order for
   bulk work, and a nameless one would be ambiguous. Exports are unaffected —
   they build from state.rows (products.js), not this DOM, so every name is
   still emitted. */
.tr-list:not(.select-mode) .tr-row.tr-spkcont .tr-spk {
  visibility: hidden;
}
.tr-syl { font-size: 1.3em; white-space: pre-wrap; }
/* Marked syllabary matches the clean syllabary's size (owner ruling, July 20):
   it is the SAME text, only carrying the devoicing/marks, so it reads as a
   variant of the syllabary line rather than a step below it. */
.tr-mod { font-size: 1.3em; white-space: pre-wrap; display: none; }
.tr-list.show-mod .tr-mod { display: block; }
/* Phonetics wore the print transcript's muted gray (#6b6355, July 18) so
   screen and page read as one design — but on the sage ground that measured
   2.94:1, far under the 4.5:1 AA floor for body text, and romanization is
   exactly what the transcriber reads all day (contrast audit, July 20).
   The print export keeps #6b6355, which is fine on white.
   Owner ruling, July 20 (later): the old 0.9em sat a full 1.44x under the
   1.3em syllabary/English lines and read as a caption, not a tier. Raised to
   ONE step of the ratio below them — 1.3em / 1.155 (the same root-4/3 that
   ties the print sizes) = ~18px, so the line reads as its own tier while
   staying clearly subordinate. Colour deepened again to #3a352d: it clears AA
   on the PLAYING row (#aebc90, the darkest ground any line takes) at 6.02:1,
   where the earlier #4d473d only reached 4.55:1, and it still steps back from
   the black syllabary. Serves the main window and the consultant window alike
   (.tr-row both); the consultant overrides only the SIZE (see .cv-list). */
.tr-pho { font-style: italic; font-size: 1.126em; color: #3a352d; }
.tr-list.no-italic .tr-pho { font-style: normal; }
/* English matches the syllabary's size (owner ruling, July 20) — the free
   translation is read as closely as the Cherokee, so it is set as large. */
.tr-eng { font-size: 1.3em; }
/* Per-line visibility (owner ruling, July 15): a student can turn off any
   line and read just clean syllabary, just marked, just phonetics, just
   English, or nothing. Marked uses show-mod (opt-in) above; the other three
   default on and hide via these classes. Reading mode only — edit mode always
   shows the read-only lines and their editors, so flags never land hidden. */
.tr-list:not(.edit-mode).hide-syl .tr-syl { display: none; }
.tr-list:not(.edit-mode).hide-pho .tr-pho { display: none; }
.tr-list:not(.edit-mode).hide-eng .tr-eng { display: none; }
/* Second-language learner (L2): every tier of the row renders italic,
   always (owner ruling, July 15). One rule serves both the transcript
   window and the consultant window — both build .tr-row rows. */
.tr-row.tr-l2 .tr-syl,
.tr-row.tr-l2 .tr-mod,
.tr-row.tr-l2 .tr-pho,
.tr-row.tr-l2 .tr-eng,
.tr-row.tr-l2 .tr-igt,
.tr-row.tr-l2 .tr-wfw,
.tr-row.tr-l2 .tr-modedit,
.tr-row.tr-l2 .tr-romedit,
.tr-row.tr-l2 .tr-engedit { font-style: italic; }
/* "no italic" turns off the phonetics-tier style for L1, but L2 stays
   italic on every tier by ruling — win on specificity. */
.tr-list.no-italic .tr-row.tr-l2 .tr-pho { font-style: italic; }
.tr-list.no-italic .tr-row.tr-l2 .wfw-pho { font-style: italic; }

/* Interlinear glossing (the "glossing" toggle): morpheme and gloss words
   stacked in columns, aligned by position — the Leipzig mechanism. */
.tr-igt, .tr-glosswarn { display: none; }
/* Split toggles (owner ruling, July 18): the block shows if EITHER the
   morpheme line or the gloss line is on; each line hides unless its own
   toggle is set. */
.tr-list.show-morph .tr-igt:not(:empty),
.tr-list.show-gloss .tr-igt:not(:empty) {
  display: flex;
  flex-wrap: wrap;
  gap: 0.1rem 1.1em;
  margin: 0.15rem 0;
}
.igt-m { font-size: 0.9em; }
.igt-g { font-size: 0.78em; color: #3d4a22; }
.tr-list:not(.show-morph) .igt-m { display: none; }
.tr-list:not(.show-gloss) .igt-g { display: none; }
.tr-list.show-morph .tr-glosswarn:not(:empty),
.tr-list.show-gloss .tr-glosswarn:not(:empty) {
  display: block;
  font-size: 0.78em;
  color: #7b3212; /* deepened rust — #963d16 was 3.49:1 on sage (audit July 20) */
}
/* in-progress is not wrong (owner ruling, Aug 16): the ✎ progress note
   sits quiet where a complaint would shout */
.tr-list.show-morph .tr-glosswarn.quiet:not(:empty),
.tr-list.show-gloss .tr-glosswarn.quiet:not(:empty) { color: #6b7263; }

/* Word-for-word view (the "word for word" toggle): the sentence re-set as
   word columns — syllabary over phonetics over the plain-English word gloss,
   the free translation staying a sentence line below. Reading mode only;
   edit mode keeps the ordinary lines and adds the word-for-word edit box. */
.tr-wfw, .tr-wfwwarn { display: none; }
/* The shared look (owner ruling Aug 3 2026, _Shared-Code/language/WFW-LOOK.md):
   the review-edition styling — columns at the block's base size, lower lines
   muted by fade rather than color or rules, columns capped so a long gloss
   wraps inside its own column. */
.tr-list:not(.edit-mode).word-mode .tr-wfw:not(:empty) {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-start;
  gap: 0.5em 1.15em;
  margin: 0.15rem 0;
}
/* no column width cap (owner ruling, Aug 4) — a long gloss WIDENS its
   column; wrapping inside the column split phrases mid-gloss */
/* The universal stack (owner ruling, Aug 15 2026): gloss over morphemes
   over syllabary over phonetics over the English word/phrase. The analysis
   pair steps down in size to fit and rides PER WORD in .wfw-perword — a
   bound phrase keeps one stack per word; only the English merges. */
.wfw-syl { font-size: 1.15em; }
.wfw-pho { font-style: italic; font-size: 1em; opacity: 0.85; }
.tr-list.no-italic .wfw-pho { font-style: normal; }
.wfw-m { font-size: 0.8em; opacity: 0.85; font-family: ui-monospace, Menlo, monospace; }
.wfw-g { font-size: 0.72em; opacity: 0.8; font-family: ui-monospace, Menlo, monospace; }
.wfw-e { font-size: 0.95em; opacity: 0.8; }
.wfw-perword { display: flex; gap: 1.05em; align-items: flex-end; }
/* one word's pair, piece against piece (owner ruling, Aug 16): the Nth
   gloss piece shares a mini-column with the Nth morpheme piece */
.wfw-pw { display: flex; gap: 0.45em; align-items: flex-end; }
.wfw-piece { display: flex; flex-direction: column; align-items: center; }
/* both glossing toggles off → the pair's empty shell takes no room */
.tr-list:not(.show-morph):not(.show-gloss) .wfw-perword { display: none; }
/* the columns honour the same per-line toggles as the sentence view */
.tr-list.hide-syl .wfw-syl { display: none; }
.tr-list.hide-pho .wfw-pho { display: none; }
.tr-list:not(.show-morph) .wfw-m { display: none; }
.tr-list:not(.show-gloss) .wfw-g { display: none; }
/* the sentence-set lines the columns replace */
.tr-list:not(.edit-mode).word-mode .tr-syl,
.tr-list:not(.edit-mode).word-mode .tr-pho { display: none; }
.tr-list.word-mode.show-morph .tr-igt:not(:empty),
.tr-list.word-mode.show-gloss .tr-igt:not(:empty) { display: none; }
.tr-list.word-mode.show-morph .tr-glosswarn:not(:empty),
.tr-list.word-mode.show-gloss .tr-glosswarn:not(:empty) { display: none; }
.tr-list:not(.edit-mode).word-mode .tr-wfwwarn:not(:empty) {
  display: block;
  font-size: 0.78em;
  color: #963d16;
}
.tr-list:not(.edit-mode).word-mode .tr-wfwwarn.quiet:not(:empty) { color: #6b7263; }

/* Speaker-registry warning under the header credit line */
.tr-spkwarn { color: #963d16; font-size: 0.8rem; }

/* Edit mode: authored tiers editable in place. Each edit box is set at the
   SAME size as the read line it stands in for (July 20 typography rulings:
   marked = clean syllabary 1.3em, English = syllabary 1.3em, romanization on
   the phonetics step 1.126em), so entering edit mode never shrinks the text. */
.tr-modedit, .tr-romedit, .tr-engedit { display: none; }
.tr-list.edit-mode .tr-modedit {
  display: block;
  font-size: 1.3em;
  white-space: pre-wrap;
  background: #d3dbc0;
  border: 1px dashed #5a4526;
  border-radius: 5px;
  padding: 0.15rem 0.45rem;
  margin: 0.2rem 0;
  cursor: text;
}
.tr-list.edit-mode .tr-engedit {
  display: block;
  font-size: 1.3em;
  background: #d3dbc0;
  border: 1px dashed #5a4526;
  border-radius: 5px;
  padding: 0.1rem 0.45rem;
  margin-top: 0.15rem;
  cursor: text;
}
/* The authored romanization tier (packages with features.romanization):
   its edit box replaces the read-only phonetic line while editing. */
.tr-list.edit-mode.has-roman .tr-romedit {
  display: block;
  font-style: italic;
  font-size: 1.126em;
  background: #d3dbc0;
  border: 1px dashed #5a4526;
  border-radius: 5px;
  padding: 0.1rem 0.45rem;
  margin-top: 0.15rem;
  cursor: text;
}
.tr-list.edit-mode.has-roman .tr-pho { display: none; }
.tr-list.edit-mode .tr-eng, .tr-list.edit-mode .tr-mod { display: none; }
/* The full-line morpheme/gloss edit boxes are RETIRED as a surface
   (owner ruling, Aug 17 — the universal standard: glossing edits look
   the same in every subprogram, and that look is the per-word column
   boxes of the binding row, seat 2's band). They stay in the DOM as the
   stored lines' silent mirrors — the shared input handler reads them —
   but they never display. Glossing stays opt-in furniture: the binding
   row appears in edit mode under the glossing toggles (or word-for-word),
   never unasked. */
.tr-morphedit, .tr-glossedit { display: none; }
.tr-list.edit-mode.show-morph .tr-igt,
.tr-list.edit-mode.show-gloss .tr-igt { display: none; } /* the binding row replaces the block */
/* The binding row (reworked Aug 3, owner rulings): the word-for-word
   editing surface, in the view's tier order. One group per unit — the
   CLEAN syllabary's words as chips (the marked stays in the edit line),
   the unit's autogenerated phonetics beneath, then its English box.
   The seams are the sentence clamp's bloodroot jaws turned on their
   side: gaping between words that gloss apart, snapped shut inside a
   bound phrase — the same springy toggle. Edit-mode + word-for-word
   toggle only. */
.tr-bindrow { display: none; }
/* the ONE glossing edit surface (universal standard, Aug 17): the binding
   row shows in edit mode under word-for-word OR either glossing toggle —
   the same band seat 2 draws */
.tr-list.edit-mode.word-mode .tr-bindrow,
.tr-list.edit-mode.show-morph .tr-bindrow,
.tr-list.edit-mode.show-gloss .tr-bindrow {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-end; /* unit bottoms align — the seams anchor off them */
  gap: 0.35rem 0.3rem;
  font-size: 1em;
  margin-top: 0.15rem;
}
.tr-bindunit {
  display: inline-flex;
  flex-direction: column;
  align-items: stretch;
  gap: 0.15rem;
}
.tr-bindwords {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-end;
  gap: 0.05rem;
}
/* One word's cell in the schema's order (owner ruling, Aug 15): gloss box,
   morpheme box, chip. The pair is per word — a bind never merges it. */
.tr-bindwordcol {
  display: inline-flex;
  flex-direction: column;
  align-items: stretch;
  gap: 0.15rem;
}
/* the piece grid: gloss boxes over morpheme boxes, one mini-column per
   morpheme–gloss pair, the ＋ at the lines' seam growing a new pair */
.tr-piecewrap { display: inline-flex; align-items: center; gap: 0.15rem; }
.tr-piecegrid { display: inline-flex; gap: 0.15rem; align-items: stretch; }
.tr-piececol { display: inline-flex; flex-direction: column; gap: 0.15rem; align-items: stretch; }
.tr-addpiece {
  border: 1px solid #d8d4c8;
  background: transparent;
  border-radius: 5px;
  color: #6b7263;
  font-size: 0.7em;
  line-height: 1;
  padding: 0.15rem 0.25rem;
  cursor: pointer;
  align-self: center;
  opacity: 0.6;
}
.tr-addpiece:hover, .tr-addpiece:focus-visible {
  opacity: 1;
  color: #963d16;
  border-color: #963d16;
}
.tr-list:not(.show-morph):not(.show-gloss) .tr-piecewrap { display: none; }
/* the morpheme-tag dropdown (owner request, Aug 16): catalogue suggestions
   under a gloss box being typed in — shared component, seat-styled */
.mtag-menu {
  position: fixed;
  z-index: 400;
  background: #f7f2e2;
  border: 1px solid #5a4526;
  border-radius: 5px;
  box-shadow: 0 3px 10px rgba(0, 0, 0, 0.18);
  padding: 0.15rem;
  max-height: 14em;
  overflow-y: auto;
}
.mtag-item {
  display: flex;
  gap: 0.6em;
  align-items: baseline;
  padding: 0.1rem 0.4rem;
  border-radius: 4px;
  cursor: pointer;
}
.mtag-item.mtag-on, .mtag-item:hover { background: #d3dbc0; }
.mtag-tag { font-family: ui-monospace, Menlo, monospace; font-size: 0.8em; }
.mtag-eng { font-size: 0.75em; color: #6b7263; margin-left: auto; }
.tr-mgbox {
  background: #d3dbc0;
  border: 1px dashed #5a4526;
  border-radius: 5px;
  padding: 0.08rem 0.35rem;
  cursor: text;
  min-width: 2em;
  text-align: center;
  font-family: ui-monospace, Menlo, monospace;
}
.tr-bindg { font-size: 0.72em; }
.tr-bindm { font-size: 0.8em; }
.tr-mgbox:empty::before {
  content: attr(data-hint);
  color: #5a4526;
  opacity: 0.55;
}
.tr-mgbox:focus {
  outline: 2px solid #963d16;
  border-style: solid;
}
/* the glossing toggles govern the pair here as everywhere */
.tr-list:not(.show-gloss) .tr-bindg { display: none; }
.tr-list:not(.show-morph) .tr-bindm { display: none; }
/* with the binding row up, the per-word boxes ARE the analysis surface —
   the full-line morpheme/gloss boxes stand down (they stay the stored
   lines' silent mirrors) and the complaint moves to the word-for-word
   warn line, one at a time, units first */
.tr-list.edit-mode.word-mode .tr-glosswarn,
.tr-list.edit-mode.show-morph .tr-glosswarn,
.tr-list.edit-mode.show-gloss .tr-glosswarn { display: none; }
.tr-list.edit-mode.word-mode .tr-wfwwarn:not(:empty),
.tr-list.edit-mode.show-morph .tr-wfwwarn:not(:empty),
.tr-list.edit-mode.show-gloss .tr-wfwwarn:not(:empty) {
  display: block;
  font-size: 0.78em;
  color: #963d16;
}
.tr-list.edit-mode.word-mode .tr-wfwwarn.quiet:not(:empty),
.tr-list.edit-mode.show-morph .tr-wfwwarn.quiet:not(:empty),
.tr-list.edit-mode.show-gloss .tr-wfwwarn.quiet:not(:empty) { color: #6b7263; }
.tr-bindword {
  padding: 0.05rem 0.15rem;
  color: #2f2a18;
  font-size: 1.15em; /* the view's word-column syllabary size */
}
.tr-bindpho {
  font-style: italic;
  font-size: 1em;
  opacity: 0.85;
  padding: 0 0.15rem;
}
.tr-list.no-italic .tr-bindpho { font-style: normal; }
.tr-bindseam {
  border: none;
  background: transparent;
  padding: 0.1rem 0.05rem;
  width: 1.3em;
  align-self: flex-end;      /* the chips are the row's floor — jaws sit on it */
  margin-bottom: 0.3rem;
  color: #a52f0b;            /* bloodroot — the sentence clamp's color */
  opacity: 0.45;             /* a quiet offer, like the clamp ghost */
  cursor: pointer;
}
/* between units the seam still sits at chip height — measured up from the
   unit's floor past the English box and the phonetics line */
.tr-bindrow > .tr-bindseam { align-self: flex-end; margin-bottom: 3.3rem; }
.tr-bindseam svg { width: 100%; display: block; pointer-events: none; }
/* open: the jaws pulled apart, gaping at the gap between two units */
.tr-bindseam .tr-wjaw-l { transform: translateX(-2.2px); }
.tr-bindseam .tr-wjaw-r { transform: translateX(2.2px); }
/* shut: snapped together on the seam inside a bound phrase */
.tr-bindseam.tr-bindon .tr-wjaw-l,
.tr-bindseam.tr-bindon .tr-wjaw-r { transform: translateX(0); }
.tr-bindseam.tr-bindon { opacity: 1; }
.tr-bindseam:hover, .tr-bindseam:focus-visible { opacity: 1; }
.tr-bindseam:focus-visible {
  outline: 2px solid #a52f0b;
  outline-offset: 1px;
  border-radius: 2px;
}
/* each unit's English, under its words — these compose the wordgloss
   tier (still one semicolon-delimited line in storage) */
.tr-wgbox {
  font-size: 0.9em;
  background: #d3dbc0;
  border: 1px dashed #5a4526;
  border-radius: 5px;
  padding: 0.1rem 0.45rem;
  cursor: text;
  min-width: 3em;
}
.tr-wgbox:empty::before {
  content: attr(data-hint);
  color: #5a4526;
  opacity: 0.55;
}
.tr-wgbox:focus {
  outline: 2px solid #963d16;
  border-style: solid;
}
/* a gloss with no word above it (the sentence shrank) — kept visible,
   never silently dropped */
.tr-wgorphan .tr-wgbox { border-color: #963d16; }
/* a bound phrase column in the word-for-word reading view carries no marker
   (owner ruling Aug 3 2026 — the joined words simply share a column); the
   .wfw-bound class stays as the hook for edit-mode furniture */
.tr-modedit:focus, .tr-romedit:focus, .tr-engedit:focus,
.tr-morphedit:focus, .tr-glossedit:focus {
  outline: 2px solid #963d16;
  border-style: solid;
}
/* live ring while a consultant window is open */
#tr-consultant.live {
  box-shadow: 0 0 0 2.5px #963d16;
  border-color: #f0e3c0;
}

/* font-size steppers (own text / consultant view) */
.tr-fontctl {
  display: inline-flex;
  align-items: center;
  gap: 0.25rem;
  color: var(--muted);
}
.tr-fontctl .val {
  min-width: 1.6rem;
  text-align: center;
  font-family: ui-monospace, Menlo, monospace;
  color: var(--ink);
}
.tr-fontctl button {
  font-size: 0.7rem;
  padding: 0.1rem 0.45rem;
  border-radius: 6px;
  border: 1px solid #5a4526;
  background: #5a4526;
  color: #f0e3c0;
  cursor: pointer;
  line-height: 1.2;
}
.tr-fontctl button:hover { background: #f0e3c0; color: #5a4526; }
.tr-fontctl button:disabled { opacity: 0.4; cursor: default; }
.tr-fontctl button:disabled:hover { background: #5a4526; color: #f0e3c0; }

/* padlock checkbox: locks the consultant text size to the main text size —
   an open padlock when unchecked, a closed rust padlock when checked */
.tr-lock {
  position: relative;
  display: inline-flex;
  align-items: center;
  color: var(--muted);
  cursor: pointer;
  line-height: 0;
}
.tr-lock input {
  position: absolute;
  inset: 0;
  margin: 0;
  opacity: 0;
  cursor: pointer;
}
.tr-lock .lock-closed { display: none; }
.tr-lock input:checked ~ .lock-open { display: none; }
.tr-lock input:checked ~ .lock-closed { display: inline; color: #963d16; }
.tr-lock input:focus-visible ~ svg { outline: 2px solid rgba(150, 61, 22, 0.45); border-radius: 3px; }

/* (The footnote margin slider + note-size stepper were replaced July 15 by the
   draggable divider — see the #tr-fndivider rules by the footnote hairline.) */
/* The volume bar and the speed bar moved into the SILL with the rest of the
   transport (owner ruling, 2026-07-30) and their styles moved with them into
   the shared chrome — css/waveview.css, in this program's own tokens. */

/* ⓘ + pinned note for the Windows-filenames conversions */
/* `button.tr-info`, not `.tr-info`: the ribbon bands live inside .tr-controls,
   whose `.tr-controls button` rule (rust pill, 999px radius) outranks a bare
   class and was turning every ⓘ into a filled pill. Matching its specificity
   and coming later in the file wins it back. */
/* A drawn ring around a small italic "i" rather than the ⓘ glyph (owner
   ruling, July 27): one colour, thin outline, and the band's own background
   showing through the gap — low-key enough to sit beside a field label without
   competing with it. `currentColor` on the border is what keeps ring and
   letter the single colour. */
button.tr-info {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 1.05em;
  height: 1.05em;
  padding: 0;
  border: 1px solid currentColor;
  border-radius: 50%;
  background: none;
  color: var(--muted);
  font-family: Georgia, "Times New Roman", serif;
  font-style: italic;
  font-size: 0.85rem;
  line-height: 1;
  cursor: pointer;
  vertical-align: -0.15em;
}
button.tr-info:hover { background: none; color: var(--ink); }
.tr-help {
  max-width: 34rem;
  background: #f0e3c0;
  color: #26221c;
  border: 1px solid #963d16;
  border-radius: 6px;
  padding: 0.45rem 0.7rem;
  font-size: 0.8rem;
  margin: 0.2rem 0 0.4rem;
}
/* ⓘ hover bubbles (owner ruling, July 27). The details card's small print used
   to be one paragraph pinned under the Save button, permanently on screen and
   answering four different questions at once. It is now one bubble per control,
   hung beside the thing it explains and shown only when asked for. Hover opens
   it; so does keyboard focus, and a tap on the glyph focuses the button, which
   is what opens it on touch. Same visual family as .tr-flaghover, the app's
   other hover reveal. No JS. */
.tr-tipwrap { position: relative; display: inline-flex; align-items: center; }
button.tr-info.sm { font-size: 0.72rem; } /* beside a label, not a button */
.tr-tip {
  position: absolute; top: calc(100% + 0.35rem); left: 0; z-index: 60;
  width: max-content; max-width: 25rem;
  background: var(--panel); color: var(--ink);
  border: 1px solid var(--muted); border-radius: 8px;
  box-shadow: 0 6px 18px rgba(0, 0, 0, 0.35);
  padding: 0.55rem 0.7rem;
  /* the bubbles hang off uppercase captions and bold subheads — reset the
     inherited type so the prose inside reads as prose */
  font-size: 0.75rem; line-height: 1.45; font-weight: normal;
  text-align: left; text-transform: none; letter-spacing: normal;
  opacity: 0; visibility: hidden;
  transition: opacity 0.12s ease, visibility 0s linear 0.12s;
}
.tr-tipwrap:hover > .tr-tip,
.tr-tipwrap:focus-within > .tr-tip {
  opacity: 1; visibility: visible;
  transition: opacity 0.12s ease 0.1s, visibility 0s linear 0.1s;
}
/* hung near the right edge of the card — open leftward so it stays on screen */
.tr-tipwrap.tip-left > .tr-tip { left: auto; right: 0; }
.tr-tip p { margin: 0 0 0.45rem; }
.tr-tip p:last-child { margin-bottom: 0; }
.tr-tip ul { margin: 0.35rem 0 0.45rem; padding-left: 1rem; list-style: disc; }
.tr-tip li { margin-bottom: 0.2rem; }
.tr-tip .k { color: var(--muted); }
/* the subhead's two ends: the title (+ its ⓘ) left, the edit tick (+ its ⓘ)
   right — each a group, so `justify-content: space-between` still has exactly
   two things to push apart */
.tr-metasub-t, .tr-metasub-r { display: inline-flex; align-items: center; gap: 0.35rem; }
/* The card's two corners (owner ruling, July 27): the edit tick rides the TOP
   RIGHT of the band, the Save button the BOTTOM RIGHT. .tr-metasub is already
   space-between, which puts edit at the right edge; `margin-top: auto` on the
   stamp row is what pins Save to the true bottom of the card however tall the
   columns come out. */
.tr-stamprow { display: flex; align-items: center; justify-content: flex-end;
  gap: 0.45rem; margin-top: auto; padding-top: 0.6rem; }
.tr-detailscol .tr-about { align-items: stretch; }
.tr-aboutedit { display: flex; flex-direction: column; }
/* …and the heading / grid keep their own height inside that column — .af and
   .tr-metasub both carry `flex-basis: 100%` from the old wrapping-flex layout,
   which in a COLUMN flex would mean "be as tall as the card" */
.tr-detailscol .tr-aboutedit > * { flex: 0 0 auto; }
/* A field that belongs to the one above it. "Their catalog no." sitting flush
   beside Courtesy of read as OUR number for the recording; it is the source
   archive's. The ↳ is positioned OUT of flow — .af is a column flex, so a
   plain ::before would land on its own line above the label. */
.tr-aboutgrid .af.af-sub { position: relative; margin-left: 1.15rem; }
.tr-aboutgrid .af.af-sub::before {
  content: "↳"; position: absolute; left: -1.05rem; top: 0;
  color: var(--muted); font-size: 0.8rem; line-height: 1.3;
}
/* the ring on the read-only "Recording" head, which is a 0.62rem uppercase
   rule — align-items: baseline would drop the circle, so centre it */
.tr-rohead { display: flex; align-items: center; gap: 0.35rem; }

/* ---- transcript print rendering (Save transcript as PDF) ---- */
#tr-print { display: none; }
@media print {
  @page { margin: 0.9in 1in; }
  body.printing { display: block !important; background: #fff !important; color: #26221c !important; }
  body.printing > :not(#tr-print) { display: none !important; }
  body.printing #tr-print { display: block; }
  #tr-print h1 { font-size: 20pt; margin: 0 0 2pt; color: #26221c; }
  #tr-print .pspk { font-size: 12pt; margin: 0 0 2pt; color: #26221c; }
  #tr-print .psub { font-size: 10pt; color: #6b6355; margin-bottom: 18pt; }
  #tr-print .prow {
    display: grid; grid-template-columns: 1.1in 1fr; gap: 0 0.22in;
    align-items: baseline; break-inside: avoid; margin-bottom: 12pt;
  }
  #tr-print .t { grid-area: 1 / 1; text-align: right; font-size: 9pt; color: #6b6355; font-family: inherit; }
  #tr-print .spk { grid-area: 2 / 1; text-align: right; font-size: 9pt; font-weight: bold; }
  #tr-print .syl { grid-area: 2 / 2; font-size: 14pt; white-space: pre-wrap; }
  /* Marked syllabary = clean syllabary size (owner ruling, July 20). The row
     builder (products.js) already emits <div class="mod">, but print carried no
     rule for it, so it fell to auto-placement, unstyled — the tier effectively
     went missing on paper. Given its own row here at 14pt, between the clean
     line and the gloss, matching the screen and the products HTML. */
  #tr-print .mod { grid-area: 3 / 2; font-size: 14pt; white-space: pre-wrap; }
  #tr-print .igt { grid-area: 4 / 2; display: flex; flex-wrap: wrap; gap: 2pt 14pt; }
  #tr-print .igt-m { font-size: 10.5pt; }
  #tr-print .igt-g { font-size: 9pt; color: #6b6355; }
  /* English matches the syllabary size (both 1.3em on screen — owner ruling
     July 23), so paper reads as the app does. Phonetics stays one ratio-step
     below English (÷1.155, as on screen): 14 / 1.155 ≈ 12pt. Colour stays
     #6b6355, which passes on white. */
  #tr-print .pho { grid-area: 5 / 2; font-style: italic; font-size: 12pt; color: #6b6355; }
  #tr-print .eng { grid-area: 6 / 2; font-size: 14pt; }
  #tr-print .prow.tr-l2 .syl, #tr-print .prow.tr-l2 .mod, #tr-print .prow.tr-l2 .pho,
  #tr-print .prow.tr-l2 .eng, #tr-print .prow.tr-l2 .igt-m, #tr-print .prow.tr-l2 .igt-g { font-style: italic; }
  /* Running footer (DESIGN-BRIEF "Running footer"): a <tfoot> the browser
     repeats at the foot of every printed page, reserving its height so it never
     overlaps the body. The cover sits outside this table, so the footer skips it. */
  #tr-print .psheet { width: 100%; border-collapse: collapse; }
  #tr-print .psheet > tbody > tr > td, #tr-print .psheet > tfoot > tr > td { padding: 0; }
  #tr-print .psheet-foot { display: table-footer-group; }
  #tr-print .runfoot { text-align: center; font-size: 8pt; color: #6b6355; padding-top: 10pt; }
  #tr-print .runfoot .rf-sep { margin: 0 0.45em; color: #a8a488; }
  #tr-print .runfoot .rf-draft { color: #c0331b; }
  #tr-print mark {
    border-radius: 3px; padding: 0 2px; color: #26221c;
    -webkit-print-color-adjust: exact; print-color-adjust: exact;
  }
  #tr-print .fnref { color: #963d16; font-weight: bold; }
  #tr-print .prow.has-fig { grid-template-columns: 1.1in 1fr 2.3in; }
  #tr-print .pfig { grid-area: 1 / 3 / 7 / 4; align-self: start;
    border-left: 1pt solid #a8a488; padding-left: 8pt; }
  #tr-print .pfig img { max-width: 100%; border-radius: 2px; }
  #tr-print .pfigcap { font-size: 8pt; color: #6b5f4a; margin-top: 2pt; }
  #tr-print .pfigmissing { border: 1pt dashed #a8a488; padding: 6pt;
    font-size: 8pt; color: #6b5f4a; word-break: break-all; }
  #tr-print .pfnotes { margin-top: 18pt; break-inside: avoid; border-top: 1px solid #6b6355; padding-top: 6pt; }
  #tr-print .pfnotes-h { font-size: 12pt; font-weight: bold; margin-bottom: 4pt; }
  #tr-print .pfn { font-size: 9pt; margin-bottom: 2pt; }
  #tr-print .pfn .n { color: #963d16; font-weight: bold; }
  #tr-print .pflags { margin-top: 18pt; break-inside: avoid; }
  #tr-print .pflags-h { font-size: 12pt; font-weight: bold; margin-bottom: 4pt; }
  #tr-print .pflag { font-size: 9pt; color: #6b6355; margin-bottom: 2pt; }
  #tr-print .ptitle { text-align: center; page-break-after: always; padding-top: 1.1in; color: #26221c; }
  #tr-print .pt-chr { font-size: 24pt; }
  #tr-print .pt-eng { font-size: 20pt; margin-top: 4pt; }
  #tr-print .pt-date { font-size: 12pt; margin-top: 12pt; }
  #tr-print .pt-photo { margin: 22pt 0 10pt; }
  #tr-print .pt-photo img { max-width: 4.6in; max-height: 4.2in; border-radius: 2px; }
  #tr-print .pt-line { font-size: 12pt; margin-top: 12pt; }
  #tr-print .pt-arch { font-size: 11pt; margin-top: 24pt; color: #6b6355; }
  /* Template B (Story cover) — every credit its own stacked bilingual pair,
     rather than one prose sentence (owner ruling, July 23). */
  #tr-print .ptb2 { max-width: 5in; margin: 0 auto; }
  #tr-print .ptb2-chr { font-size: 15pt; margin-top: 16pt; }
  #tr-print .ptb2-eng { font-size: 12pt; margin-top: 2pt; }
  #tr-print .ptb2-names { font-size: 12pt; margin-top: 2pt; }
  #tr-print .ptb-note { font-size: 11pt; line-height: 1.45; margin-top: 14pt; color: #6b6355; }
  #tr-print .ptb2-arch { margin-top: 24pt; }
  #tr-print .s0 { color: #452c10; }
  #tr-print .s1 { color: #a52f0b; }
  #tr-print .s2 { color: #8a6d00; }
  #tr-print .s3 { color: #83175f; }
  #tr-print .s4 { color: #8f5d2b; }
  #tr-print .s5 { color: #2f4a70; }
  #tr-print .s6 { color: #46237e; }
  #tr-print .s7 { color: #701a26; }
}

/* ---- Speakers tab ---- */
#panel-speakers.panel { max-width: none; }

/* durable auto-save link line: a status dot, a plain-language note, and one
   context button (Link / Reconnect / Unlink). */
.sp-autosave {
  display: flex; align-items: center; gap: 0.5rem; flex-wrap: wrap;
  margin: 0.7rem 0 0.2rem; font-size: 0.85rem; color: var(--muted);
}
.sp-autosave-dot {
  flex: none; width: 0.6rem; height: 0.6rem; border-radius: 50%;
  background: var(--muted); box-shadow: 0 0 0 2px var(--panel);
}
.sp-dot-linked { background: var(--accent); }
.sp-dot-reconnect { background: #c0592b; }
.sp-dot-unlinked { background: var(--border); }
.sp-autosave-text { flex: 1 1 16rem; }
.sp-autosave-btn {
  flex: none; font: inherit; font-size: 0.82rem; cursor: pointer;
  padding: 0.3rem 0.8rem; border-radius: 7px;
  border: 1px solid var(--border); background: var(--bg); color: var(--ink);
}
.sp-autosave-btn:hover { border-color: var(--accent); }
.sp-file-actions { flex: none; display: flex; gap: 0.4rem; flex-wrap: wrap; }
.sp-file-primary { border-color: var(--accent); font-weight: bold; }

.sp-wrap { overflow-x: auto; margin-top: 0.6rem; }
#sp-table {
  border-collapse: collapse;
  background: #c3cdad;
  color: #000;
  border-radius: 8px;
  overflow: hidden;
  min-width: 44rem;
}
#sp-table th {
  background: #5a4526;
  color: #f0e3c0;
  font-size: 0.85rem;
  font-weight: bold;
  text-align: left;
  padding: 0.4rem 0.7rem;
}
#sp-table td {
  border-bottom: 1px solid #963d16;
  padding: 0.35rem 0.7rem;
  min-width: 6rem;
  font-size: 0.95rem;
}
#sp-table td:focus { outline: 2px solid #963d16; background: #d3dbc0; }
#sp-table tr.sp-conflict td { background: #e0b9a0; }
#sp-table td.sp-del { border-bottom-color: transparent; min-width: 0; }
/* Section band splitting first-language speakers from second-language learners */
#sp-table tr.sp-section td {
  background: #4a3a1f;
  color: #f0e3c0;
  font-weight: bold;
  font-size: 0.8rem;
  letter-spacing: 0.03em;
  text-transform: uppercase;
  padding: 0.3rem 0.7rem;
  min-width: 0;
}
/* Per-section "add a person" row: a dashed add-affordance that files the new
   speaker under its section (which sets their L1/L2). Warm palette, always
   light (the table doesn't follow the app's dark theme). */
#sp-table tr.sp-addrow td { border-bottom: none; padding: 0.35rem 0.7rem; }
#sp-table .sp-addbtn {
  font: inherit;
  font-size: 0.85rem;
  cursor: pointer;
  padding: 0.3rem 0.8rem;
  border: 1px dashed #a86b43;
  border-radius: 6px;
  background: transparent;
  color: #5a4526;
}
#sp-table .sp-addbtn:hover { border-style: solid; border-color: #5a4526; background: #f2efe4; }
#sp-table .sp-addbtn:focus-visible { outline: 2px solid #963d16; outline-offset: 1px; }
.sp-del button {
  border: none;
  background: none;
  color: #963d16;
  cursor: pointer;
  font-size: 0.9rem;
}
.sp-warn { color: #963d16; font-weight: bold; }

/* ---- Fontbook tab ---- */
#panel-fontbook.panel { max-width: 60rem; }
.fb-addlang {
  border: 1px solid var(--border);
  border-radius: 8px;
  padding: 0.2rem 1rem 0.9rem;
  margin: 0.8rem 0;
  background: var(--bg);
  max-width: 30rem;
}
.fb-locallist {
  max-height: 16rem;
  overflow-y: auto;
  border: 1px solid var(--border);
  border-radius: 6px;
  padding: 0.3rem 0.6rem;
  margin: 0.4rem 0;
  background: var(--panel);
}
.fb-localrow {
  display: flex;
  align-items: baseline;
  gap: 0.5rem;
  padding: 0.12rem 0;
  cursor: pointer;
}
.fb-samplebar {
  display: flex;
  align-items: center;
  gap: 0.6rem 1.2rem;
  flex-wrap: wrap;
  margin-bottom: 0.8rem;
}
.fb-samplefield { flex: 1; min-width: 16rem; margin: 0.4rem 0; }
#fb-lang-remove {
  font: inherit;
  font-size: 0.8rem;
  border: none;
  background: none;
  color: var(--muted);
  cursor: pointer;
}
#fb-lang-remove:hover { color: #963d16; }
.fb-font {
  border: 1px solid var(--border);
  border-radius: 8px;
  padding: 0.5rem 0.9rem 0.7rem;
  margin-bottom: 0.7rem;
  background: var(--bg);
}
.fb-font-head { display: flex; align-items: baseline; gap: 0.7rem; flex-wrap: wrap; }
.fb-font-name { font-weight: bold; }
.fb-font-file {
  color: var(--muted);
  font-weight: normal;
  font-size: 0.75rem;
  font-family: ui-monospace, Menlo, monospace;
}
.fb-badge {
  font-size: 0.75rem;
  padding: 0.1rem 0.6rem;
  border-radius: 999px;
  border: 1px solid var(--border);
  color: var(--muted);
}
.fb-badge.fb-ok { color: #2e7d4f; border-color: #2e7d4f; }
.fb-badge.fb-miss { color: #963d16; border-color: #963d16; }
.fb-use {
  font: inherit;
  font-size: 0.8rem;
  margin-left: auto;
  padding: 0.2rem 0.8rem;
  border-radius: 999px;
  border: 1px solid var(--accent);
  background: var(--panel);
  color: var(--ink);
  cursor: pointer;
}
.fb-use:hover:not(:disabled) { background: var(--accent); color: var(--accent-ink); }
.fb-use.fb-inuse {
  background: var(--accent);
  color: var(--accent-ink);
  cursor: default;
}
.fb-del {
  border: none;
  background: none;
  color: #963d16;
  cursor: pointer;
  font-size: 0.9rem;
}
.fb-sample {
  font-size: var(--fb-size, 28px);
  line-height: 1.35;
  margin-top: 0.4rem;
  white-space: pre-wrap;
  word-break: break-word;
}
.fb-font.fb-none .fb-sample { opacity: 0.35; }
/* Families with no coverage of the page's language are hidden; this note
   at the bottom of the list says so and toggles peeking at them. */
.fb-hiddennote {
  font: inherit;
  font-size: 0.8rem;
  display: block;
  margin: 0.3rem 0 0.2rem;
  padding: 0.3rem 0;
  border: none;
  background: none;
  color: var(--muted);
  cursor: pointer;
  text-decoration: underline;
  text-underline-offset: 3px;
}
.fb-hiddennote:hover { color: var(--ink); }
/* The family's other styles (bold, italic, extra weights), one row each. */
.fb-variant {
  display: flex;
  align-items: baseline;
  gap: 0.7rem;
  border-top: 1px dashed var(--border);
  margin-top: 0.45rem;
  padding-top: 0.4rem;
}
.fb-varlabel {
  flex: 0 0 7.5rem;
  color: var(--muted);
  font-size: 0.8rem;
}
.fb-varsample {
  flex: 1;
  font-size: calc(var(--fb-size, 28px) * 0.62);
  line-height: 1.35;
  white-space: pre-wrap;
  word-break: break-word;
}

/* ---- consultant view (consultant.html) ---- */
body.cv { padding: 0.8rem 1.4rem 2rem; display: block; }
/* Title-bar drag strip (owner report, July 27: "the top of the page does not
   work like the window"). Same story as the main window's .tr-titledrag — when
   the app runs INSTALLED, the window's own title bar is handed to the page, and
   a page with no drag region there is a window that cannot be moved. The
   consultant window is opened with window.open, so it inherits the overlay just
   as the pop-outs do, but it had no strip of its own and so nothing to grab.
   Hidden in an ordinary browser tab, where the browser still owns the bar. */
.cv-drag { display: none; }
/* body.wco as well as the media query, for the reason spelled out at the main
   window's copy of this block: full screen leaves the mode reported and the
   env() geometry at zero, which would collapse this strip to nothing. */
@media (display-mode: window-controls-overlay) {
  /* the strip is part of the VIEWPORT here, so the page starts below it */
  body.cv.wco { padding-top: calc(env(titlebar-area-height, 38px) + 0.8rem); }
  /* …and the sticky header parks under the strip, not beneath it */
  body.wco .cv-head { top: env(titlebar-area-height, 38px); }
  body.wco .cv-drag {
    display: flex;
    align-items: center;
    /* the name sits at the RIGHT end of the strip, as it does in the main
       window's title bar — on macOS the left end is where the window buttons
       are, and the label read as a caption hanging off them */
    justify-content: flex-end;
    position: fixed;
    z-index: 60;
    /* sit exactly on the strip the browser has left free — env() puts the
       window buttons on the left on macOS, on the right on Windows */
    top: env(titlebar-area-y, 0);
    left: env(titlebar-area-x, 0);
    width: env(titlebar-area-width, 100%);
    height: env(titlebar-area-height, 38px);
    padding: 0 0.7rem;
    /* the manifest's theme_color, so strip and window frame read as one */
    background: #1d3a2a;
    color: #f0e3c0;
    font-size: 0.72rem;
    /* the whole strip drags: unlike the main window's header it holds no
       controls, so nothing gets swallowed */
    -webkit-app-region: drag;
    app-region: drag;
  }
}
/* The header band (owner ruling, July 27): what is playing, over this window's
   own display controls. Sticky, so a toggle is always in reach — the presenter
   should never have to scroll back to the top mid-session. The negative side
   margins let it cover the full width as rows scroll beneath it; body.cv's own
   padding is paid back inside. */
.cv-head {
  position: sticky;
  top: 0;
  z-index: 5;
  margin: -0.8rem -1.4rem 0.7rem;
  padding: 0.7rem 1.4rem 0.55rem;
  background: var(--bg);
  border-bottom: 1px solid var(--border);
}
.cv-headline {
  display: flex; flex-wrap: wrap; align-items: baseline; gap: 0.1rem 0.8rem;
  min-width: 0;
}
.cv-title { font-size: 1.35rem; font-weight: bold; }
.cv-speakers { font-size: 1rem; }
.cv-speakers:empty { display: none; }
.cv-meta { color: var(--muted); font-size: 0.85rem; }
/* (The in-window toggles that used to sit here are gone — owner ruling,
   July 27: an audience screen carries no controls. Every switch that shapes
   this window is in the main window's ribbon, under "consultant view".) */
/* Tier sizes: NO overrides here (owner ruling, July 27). This window used to
   keep its own scale — syllabary 1.5em, English 1.05em, phonetics 0.909em —
   from before the July 20 rulings reset the transcript's tiers (syllabary,
   marked and English all 1.3em; phonetics one ratio-step below at 1.126em).
   Left in place it re-created exactly what those rulings threw out: a giant
   syllabary over an English line that read as a caption. The consultant now
   inherits the base .tr-syl / .tr-mod / .tr-pho / .tr-eng, so the two windows
   hold ONE set of ratios and the presenter's px stepper scales all of them
   together. Colour also comes from the base .tr-pho (#3a352d), which is right
   on the consultant's sage ground too. */
/* In-app transcript tabs (owner ruling, July 19): a browser tab bar at the
   top of the Transcript panel. Always present — a recording tab per open
   WAV, blank "+" new-tabs, and a "+" to add one. The active tab lifts into
   the panel below with a single warm bloodroot top-hair; an unsaved
   recording carries a ● before its title. */
.tr-tabrow { display: flex; align-items: flex-end; }
.tr-tabrow #tr-tabstrip { flex: 1; min-width: 0; }
/* One row, always (owner ruling, July 24): tabs never stack onto a second
   line. A crowded bar bunches up the way a browser's does — every tab gives
   ground evenly until its title is an ellipsis, and only past that floor does
   the strip scroll sideways. The 2px top padding is headroom for the active
   tab's bloodroot hair, which sits a pixel above the chip and would otherwise
   be shaved off by the scroll clip; the margin gives the same 2px back. */
#tr-tabstrip {
  display: flex; align-items: flex-end; flex-wrap: nowrap;
  gap: 2px; margin: calc(0.2rem - 2px) 0 0; padding-top: 2px;
  min-width: 0; overflow-x: auto; overflow-y: hidden;
  scrollbar-width: none;
}
#tr-tabstrip::-webkit-scrollbar { display: none; }
#tr-tabstrip[hidden] { display: none; }
/* Save rides INSIDE its own tab (owner ruling, July 20): one per dirty
   recording, so several tabs with pending work each carry their own. Unlike
   the ⤢ and ✕ beside it this never fades out — an unsaved recording should
   say so from across the room, whether or not that tab is the active one. */
.tr-tabsave {
  flex: none;
  border: 1px solid #7a3a1e;
  background: none;
  color: #d1703f;
  border-radius: 999px;
  margin: 0 0.1rem 0 0.25rem;
  padding: 0.05rem 0.34rem;
  font-size: 0.78rem;
  line-height: 1.15;
  cursor: pointer;
  opacity: 1;
}
.tr-tabsave:hover {
  background: rgba(150, 61, 22, 0.2);
  color: #e88a52;
  border-color: #963d16;
}
/* While the write is running the ↧ steps aside for this ring — a big WAV
   can take a while to land, and a still button reads as a missed click. */
.tr-tabsaving {
  flex: none;
  box-sizing: border-box;
  width: 0.9rem;
  height: 0.9rem;
  margin: 0 0.15rem 0 0.3rem;
  border: 2px solid rgba(209, 112, 63, 0.3);
  border-top-color: #d1703f;
  border-radius: 50%;
  animation: tr-tabsaving-spin 0.9s linear infinite;
}
@keyframes tr-tabsaving-spin { to { transform: rotate(360deg); } }
.tr-tabchip {
  display: inline-flex; align-items: center; position: relative; top: 1px;
  /* shrink before the row does: a chip gives up width down to a floor that
     still holds a sliver of title and its ✕ */
  flex: 0 1 auto; min-width: 5.5rem;
  border: 1px solid var(--border); border-bottom: none;
  border-radius: 9px 9px 0 0; background: transparent; overflow: hidden;
  color: var(--muted); transition: background 0.12s, color 0.12s;
}
.tr-tabchip:hover { color: var(--ink); }
.tr-tabchip.active {
  background: var(--panel); color: var(--ink); border-color: var(--border);
}
/* the one warm line: bloodroot hair across the active tab's top edge */
.tr-tabchip.active::before {
  content: ''; position: absolute; left: -1px; right: -1px; top: -1px;
  height: 2px; background: #c0592b; border-radius: 9px 9px 0 0;
}
.tr-tablabel {
  border: none; background: none; color: inherit;
  flex: 1 1 auto; min-width: 0; /* the title is what gives when a chip narrows */
  padding: 0.4rem 0.4rem 0.45rem 0.7rem;
  font-size: 0.85rem; cursor: pointer; max-width: 15rem;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.tr-tabchip.active .tr-tablabel { font-weight: bold; }
.tr-tabchip-new .tr-tablabel { font-style: italic; opacity: 0.9; }
/* unsaved dot: shows only on a dirty recording tab */
.tr-tabdot { color: #c0592b; font-size: 0.65rem; margin-right: 0.3rem; vertical-align: 1px; }
.tr-tabchip:not(.dirty) .tr-tabdot { display: none; }
.tr-tabpop, .tr-tabclose {
  flex: none;
  border: none; background: none; color: inherit; border-radius: 5px;
  padding: 0.1rem 0.3rem; margin: 0;
  font-size: 0.8rem; line-height: 1; cursor: pointer; opacity: 0;
}
.tr-tabpop { margin-left: 0.15rem; }
.tr-tabclose { margin-right: 0.2rem; }
/* the pop-out ⤢ and close ✕ stay quiet until you're on or over the tab */
.tr-tabchip:hover .tr-tabpop,
.tr-tabchip:hover .tr-tabclose,
.tr-tabchip.active .tr-tabpop,
.tr-tabchip.active .tr-tabclose { opacity: 0.5; }
.tr-tabpop:hover { opacity: 1 !important; background: rgba(78,139,102,0.28); }
.tr-tabclose:hover { opacity: 1 !important; background: rgba(192,89,43,0.22); }

/* a popped-out window is one recording, no app chrome: the main nav, the
   recording tab bar, and the new-tab page all go away. */
body.popout #tr-tabstrip,
body.popout .tr-tabplus,
body.popout #tr-newtab { display: none; }
body.popout #panel-transcript { border-radius: 10px; border-top: 1px solid var(--border); }
.tr-tabplus {
  flex: none; border: none; background: transparent; color: var(--muted);
  width: 1.9rem; height: 1.9rem; margin: 0 0 1px 0.15rem;
  border-radius: 8px; font-size: 1.1rem; line-height: 1; cursor: pointer;
}
.tr-tabplus:hover { background: var(--panel); color: var(--ink); }

/* new-tab page (== the old open screen): the four open options as soft
   cards. Sits flush under the tab bar; the panel keeps its own border. */
#tr-newtab { padding-top: 0.4rem; }
.tr-openopts {
  display: grid; grid-template-columns: 1fr 1fr; gap: 0.7rem;
  max-width: 44rem; margin: 1.2rem 0 0.4rem;
}
.tr-opt {
  display: flex; flex-direction: column; gap: 0.2rem; text-align: left;
  padding: 0.85rem 1rem; border: 1px solid var(--border); border-radius: 10px;
  background: var(--bg); color: var(--ink); cursor: pointer; font: inherit;
  width: 100%; margin: 0; transition: border-color 0.12s, background 0.12s;
}
.tr-opt:hover { border-color: var(--accent); }
.tr-opt.primary {
  border-color: var(--accent); background: color-mix(in srgb, var(--accent) 14%, var(--bg));
  color: var(--ink);
}
.tr-opt-t { font-size: 0.98rem; }
.tr-opt-d { font-size: 0.78rem; color: var(--muted); line-height: 1.35; }
.tr-opt-recents { position: relative; display: block; }
.tr-opt-caret { font-size: 0.8em; }

/* Recent recordings (open-by-folder): a dropdown beside the open buttons —
   one row per remembered recording, a reopen button and a forget ✕. */
#tr-recents-list { display: flex; flex-direction: column; gap: 0.35rem; }
.tr-recent { display: flex; gap: 0.35rem; }
.tr-recent > button:first-child {
  flex: 1; text-align: left;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.tr-recent-x { flex: 0 0 auto; opacity: 0.65; }
.tr-recent-x:hover { opacity: 1; }

/* The library (open-by-root): the whole working tree, units grouped by the
   shelf folder they live in. Compact so a large library stays scannable. */
#tr-library { margin-top: 1.4rem; max-width: 44rem; }
.tr-lib-head {
  display: flex; align-items: baseline; justify-content: space-between;
  gap: 0.6rem; flex-wrap: wrap;
}
#tr-lib-root { margin: 0; font-weight: 600; color: var(--ink); }
.tr-lib-mini {
  font: inherit; font-size: 0.78rem; padding: 0.25rem 0.6rem;
  border: 1px solid var(--border); border-radius: 7px;
  background: var(--bg); color: var(--muted); cursor: pointer;
}
.tr-lib-mini:hover { border-color: var(--accent); color: var(--ink); }
/* One heading per LIBRARY when a program owns more than one (owner ruling,
   July 28). Above the shelf headings, and heavier than them: the library is
   which shelf you are looking at, the shelf is where inside it you filed. The
   program-wide ᏗᏍᏓᏅᏅᏙᏗ shelf carries its own colour so a compiled recording
   never reads as if it lived in one program's own library. */
.tr-lib-name {
  margin-top: 1.3rem; font-size: 0.9rem; font-weight: 600; color: var(--ink);
  border-bottom: 1px solid var(--border); padding-bottom: 0.25rem;
}
.tr-lib-name:first-child { margin-top: 0.6rem; }
.tr-lib-name.shared { color: var(--focus); border-bottom-style: dashed; }
.tr-lib-shelf { margin-top: 0.9rem; }
.tr-lib-shelf-name {
  font-size: 0.76rem; letter-spacing: 0.02em; text-transform: uppercase;
  color: var(--muted); margin-bottom: 0.45rem;
}
.tr-lib-units { display: flex; flex-wrap: wrap; gap: 0.5rem; }
.tr-lib-unit {
  font: inherit; font-size: 0.9rem; text-align: left;
  padding: 0.5rem 0.75rem; border: 1px solid var(--border); border-radius: 9px;
  background: var(--bg); color: var(--ink); cursor: pointer;
  transition: border-color 0.12s, background 0.12s;
}
.tr-lib-unit:hover {
  border-color: var(--accent);
  background: color-mix(in srgb, var(--accent) 10%, var(--bg));
}

/* Timecode: no size override either (owner ruling, July 27). The clock is
   machine metadata in BOTH windows — it inherits the base .tr-t (0.72em), and
   the old 0.85em here left it larger on the presentation screen than on the
   transcriber's, which is backwards. The speaker likewise inherits the base
   .tr-spk (0.9em, regular), so it reads as a name, not as another piece of
   metadata sized with the clock. With the clock back to its own size the
   gutter matches the transcript's 6.2em too. */
.cv-list .tr-row { grid-template-columns: 6.2em 1fr; }
/* Consultant footnotes (owner ruling July 14): behind the presenter's own
   checkbox — off means clean, not even a superscript. */
.cv-list .tr-fnref { display: none; }
.tr-list.cv-list.fnotes-on .tr-fnref { display: inline; }
/* var(--fnote-marg), not a hard 15.5rem: the main window's margin slider sends
   the column width over the channel and consultant.js sets the variable here —
   a fixed width silently threw that away (July 27). */
.tr-list.cv-list.fnotes-on .tr-row {
  grid-template-columns: 6.2em 1fr var(--fnote-marg, 15.5rem);
}
@media (max-width: 760px) {
  .tr-list.cv-list.fnotes-on .tr-row { grid-template-columns: 6.2em 1fr; }
}

/* Deck builder: a page-wide drawer along the bottom of the window; the
   transcript stays visible and scrollable above it. */
#tr-drawer {
  position: fixed;
  left: 0;
  right: 0;
  bottom: 0;
  z-index: 20;
  background: var(--panel);
  border-top: 3px double #963d16;
  box-shadow: 0 -6px 18px rgba(0, 0, 0, 0.25);
}
.tr-drawer-inner {
  max-width: 60rem;
  margin: 0 auto;
  padding: 0.6rem 1.5rem 0.9rem;
  display: grid;
  grid-template-columns: 3fr 1fr;
  gap: 0 1.5rem;
  align-items: end;
}
.tr-drawer-inner .field { margin: 0.4rem 0; }
.tr-drawer-form .buttons { margin-top: 0.5rem; }
/* ================= Products band, model A (owner ruling, July 27) =========
   Two columns and an action bar, replacing six equal side-by-side groups that
   showed most of their controls whether or not anything used them. Left: the
   products, each unfolding its OWN options when checked. Right: only what is
   genuinely shared. Foot: scope, then Make. */
.tr-prodcols {
  display: grid; grid-template-columns: minmax(0, 1fr) 17rem; gap: 0 2rem;
  padding: 0.2rem 0.3rem 0.6rem;
}
.tr-prodcols > .tr-prodshared {
  border-left: 1px solid var(--border); padding-left: 2rem; margin-left: -1rem;
}
.tr-prodcols .cap {
  font-size: 0.62rem; color: var(--muted); text-transform: uppercase;
  letter-spacing: 0.09em; margin: 0 0 0.45rem;
}
.tr-capsub { text-transform: none; letter-spacing: 0; }

/* one product: its name, then its own settings under it */
.tr-prodname {
  display: flex; align-items: baseline; gap: 0.5rem;
  font-size: 0.95rem; cursor: pointer; padding: 0.16rem 0;
}
.tr-prodname input { accent-color: var(--focus); margin: 0; flex: none;
  position: relative; top: 1px; }
.tr-prodsub {
  margin: 0.15rem 0 0.5rem 1.55rem; padding-left: 0.75rem;
  border-left: 2px solid var(--border);
}
/* Unchecked, a product's options are not there at all — the whole point of the
   rebuild. display:none rather than [hidden] so the rule holds without JS
   setting an attribute on every row. */
.tr-prod:not(.on) .tr-prodsub { display: none; }
.tr-prodsub .field { display: block; margin: 0.25rem 0; font-size: 0.78rem; color: var(--muted); }
.tr-prodsub .field input[type="text"], .tr-prodsub .field select { width: 13rem; }
.tr-prodsub .check { margin: 0.25rem 0; font-size: 0.82rem; }
.tr-prodnote { color: var(--muted); font-size: 0.74rem; line-height: 1.45; margin: 0.3rem 0 0; }
.tr-prodwarn {
  color: #c1703f; font-size: 0.74rem; margin: 0.35rem 0 0;
  display: flex; align-items: baseline; gap: 0.35rem;
}
.tr-prodwarn::before { content: "▲"; font-size: 0.6rem; }
.tr-prodroute {
  display: flex; align-items: center; gap: 0.9rem; flex-wrap: wrap;
  font-size: 0.78rem; color: var(--muted); margin: 0.2rem 0;
}
.tr-prodroute label { display: inline-flex; align-items: center; gap: 0.3rem; cursor: pointer; }
.tr-prodroute input { accent-color: var(--focus); margin: 0; }

.tr-prodshared .check { display: block; margin: 0.2rem 0; font-size: 0.84rem; }
.tr-prodshared .check input { accent-color: var(--focus); }
.tr-prodgroup + .tr-prodgroup {
  margin-top: 0.8rem; padding-top: 0.8rem; border-top: 1px solid var(--border);
}
/* the shared page settings stand down when nothing prints (JS sets [hidden]) */
.tr-prodgroup[hidden] { display: none; }

/* The action bar: what the products are made FROM, then the one button that
   makes them — which used to sit mid-column inside the checklist. */
.tr-prodbar {
  display: flex; align-items: center; gap: 0.7rem; flex-wrap: wrap;
  border-top: 1px solid var(--border); background: var(--log-bg);
  padding: 0.5rem 0.8rem;
}
.tr-clipcount { font-size: 0.78rem; color: var(--muted); }
.tr-clipcount b { color: var(--ink); font-weight: normal; }
/* Make is the one thing in the bar you press to get a file, so it carries the
   app's rust rather than sharing the tan pill with Select all / Send to. */
.tr-prodbar #tr-makeall {
  margin-top: 0;
  background: var(--focus); border-color: var(--focus); color: #f7ece2;
}
.tr-prodbar #tr-makeall:hover { background: #7d3312; border-color: #7d3312; color: #f7ece2; }
.tr-prodbar #tr-makeall:disabled { opacity: 0.45; cursor: default; }
.tr-prodbar #tr-makeall:disabled:hover { background: var(--focus); border-color: var(--focus); }

/* ---- where products land (owner ruling, July 27) ----
   The band names the destination folder before you make anything, and the
   panel lists what is actually in it. A web page cannot open a Finder window,
   so the panel is the app's own answer to "where did that go". */
.tr-prodwhere {
  margin: 0 0 0.45rem; font-size: 0.78rem; color: var(--muted);
  max-width: 16rem; overflow-wrap: anywhere;
}
.tr-prodpanel { width: 30rem; }
.tr-prodfiles {
  max-height: 13rem; overflow-y: auto;
  border: 1px solid var(--border); border-radius: 6px;
  background: var(--log-bg); padding: 0.3rem 0.1rem;
}
.tr-prodfiles:empty { display: none; }
.tr-prodfile {
  display: flex; align-items: baseline; gap: 0.6rem;
  padding: 0.16rem 0.5rem; font-size: 0.76rem;
}
.tr-prodfile + .tr-prodfile { border-top: 1px solid var(--border); }
.tr-prodfile-name { flex: 1; min-width: 0; overflow-wrap: anywhere; }
.tr-prodfile-meta { flex: none; color: var(--muted); font-size: 0.68rem; white-space: nowrap; }
.tr-prodpanel-btns { display: flex; gap: 0.5rem; margin-top: 0.6rem; }
.tr-prodpanel-note { margin-top: 0.4rem; }

/* Both drawer pages share one grid cell, so the drawer keeps the height of
   the taller page instead of jumping when flipping between them. */
.tr-drawer-form { display: grid; }
#tr-page1, #tr-page2 { grid-area: 1 / 1; }
#tr-page1[hidden], #tr-page2[hidden] {
  display: block !important;
  visibility: hidden;
  pointer-events: none;
}
.tr-drawer-log { margin-top: 0.5rem; max-height: 4rem; font-size: 0.7rem; }

.tr-page-arrow {
  position: absolute;
  top: 0.45rem;
  width: 1.7rem;
  height: 1.7rem;
  border-radius: 50%;
  border: 1px solid #5a4526;
  background: #5a4526;
  color: #f0e3c0;
  font-size: 1rem;
  line-height: 1;
  cursor: pointer;
}
.tr-page-arrow:hover { background: #f0e3c0; color: #5a4526; }
.tr-arrow-right { right: 0.6rem; }
.tr-arrow-left { left: 0.6rem; }
body.tr-drawer-open main { padding-bottom: 22rem; }
@media (max-width: 40rem) {
  .tr-drawer-inner { grid-template-columns: 1fr; }
}

/* ---- companion pages (owner design, July 15) --------------------------------
   A companion opens in a side panel docked to the right; the transcript keeps
   its normal width-sharing (main gains right padding while the panel is open,
   the drawer's own coexistence pattern). The list lives in the Companions
   ribbon tab; the panel reads and edits. */
.tr-cmp-list { display: flex; flex-wrap: wrap; gap: 0.35rem; align-items: center; max-width: 44rem; }
.tr-cmp-listgroup { flex: 1 1 auto; }
.tr-cmp-item {
  display: inline-flex; align-items: center; gap: 0.4rem;
  border: 1px solid #5a4526; background: var(--panel);
  border-radius: 999px; padding: 0.15rem 0.7rem;
  font-size: 0.85rem; cursor: pointer; color: var(--ink);
}
.tr-cmp-item:hover { background: #f0e3c0; color: #5a4526; }
.tr-cmp-item .tr-cmp-itemauth { color: var(--muted); font-size: 0.78rem; }
.tr-cmp-empty { color: var(--muted); font-size: 0.85rem; font-style: italic; }
.tr-cmp-hint { color: var(--muted); font-size: 0.78rem; margin: 0.4rem 0 0; }
/* a companion chip with its export-pick checkbox */
.tr-cmp-row { display: inline-flex; align-items: center; gap: 0.3rem; }
.tr-cmp-pick { cursor: pointer; }
.tr-cmp-exphint { color: var(--muted); font-size: 0.78rem; margin: 0.1rem 0 0.2rem; }

.tr-cmpbadge {
  display: inline-block; font-size: 0.72rem; font-weight: 700;
  border-radius: 999px; padding: 0.1rem 0.6rem; white-space: nowrap;
}

/* side panel */
#tr-cmppanel {
  position: fixed;
  top: 7.5rem; right: 0; bottom: 0;
  width: min(30rem, 42vw); min-width: 16rem;
  z-index: 8;
  background: var(--panel);
  border-left: 3px double #963d16;
  box-shadow: -6px 0 18px rgba(0, 0, 0, 0.18);
  display: flex; flex-direction: column;
  overflow: hidden;
}
body.tr-cmp-open main { padding-right: min(31rem, 44vw); }
@media (max-width: 52rem) {
  #tr-cmppanel { width: 100vw; top: 0; z-index: 22; }
  body.tr-cmp-open main { padding-right: 1.5rem; }
}
.tr-cmphead {
  display: flex; align-items: center; gap: 0.5rem;
  padding: 0.5rem 0.7rem; border-bottom: 1px solid var(--border);
  background: #efe7d4;
}
@media (prefers-color-scheme: dark) { .tr-cmphead { background: #2b3125; } }
.tr-cmphead .tr-spacer { flex: 1; }
.tr-cmpmeta { color: var(--muted); font-size: 0.8rem; }
.tr-cmphead button {
  font: inherit; font-size: 0.8rem; padding: 0.1rem 0.6rem;
  border-radius: 6px; border: 1px solid #5a4526;
  background: #5a4526; color: #f0e3c0; cursor: pointer;
}
.tr-cmphead button:hover { background: #f0e3c0; color: #5a4526; }
.tr-cmpread {
  padding: 0.8rem 1rem; overflow-y: auto; flex: 1;
  line-height: 1.5; color: var(--ink);
}
.tr-cmpread h1, .tr-cmpread h2, .tr-cmpread h3 { line-height: 1.25; margin: 0.8rem 0 0.4rem; }
.tr-cmpread h1 { font-size: 1.3rem; }
.tr-cmpread h2 { font-size: 1.12rem; }
.tr-cmpread h3 { font-size: 1rem; }
.tr-cmpread p { margin: 0.5rem 0; }
.tr-cmpread blockquote {
  border-left: 3px solid #c8b48a; margin: 0.6rem 0;
  padding: 0.15rem 0 0.15rem 0.8rem; color: var(--muted);
}
.tr-cmpread ul { margin: 0.4rem 0; padding-left: 1.3rem; }
.tr-cmpread pre.cmp-code {
  background: var(--log-bg); border: 1px solid var(--border);
  border-radius: 6px; padding: 0.5rem 0.7rem; overflow-x: auto; font-size: 0.85rem;
}
/* Rust stays — it is the app's "this is active" convention (see --focus).
   The DOTTED underline does not: dotted + warm red is the spellcheck idiom
   and citations read as misspellings (owner report, July 27 2026). Solid. */
.tr-cmpread a.cmp-cite {
  color: #8a3c12; text-decoration: underline; cursor: pointer;
}
@media (prefers-color-scheme: dark) { .tr-cmpread a.cmp-cite { color: #e0975f; } }
.tr-cmpread a.cmp-cite:hover { background: #f0e3c0; color: #5a4526; }

/* editor */
.tr-cmpeditpane { display: flex; flex-direction: column; flex: 1; overflow-y: auto; padding: 0.6rem 0.8rem; gap: 0.5rem; }
.tr-cmpfields .field, .tr-cmpfrow .field { display: flex; flex-direction: column; font-size: 0.78rem; color: var(--muted); gap: 0.15rem; }
.tr-cmpfields input, .tr-cmpfields select, .tr-cmpfrow input, .tr-cmpfrow select {
  font: inherit; font-size: 0.9rem; padding: 0.2rem 0.4rem;
  border: 1px solid var(--border); border-radius: 5px; background: var(--bg); color: var(--ink);
}
.tr-cmpfrow { display: flex; gap: 0.5rem; margin-top: 0.4rem; }
.tr-cmpfrow .field { flex: 1; }
.tr-cmptoolbar { display: flex; gap: 0.3rem; flex-wrap: wrap; }
.tr-cmptoolbar button {
  font: inherit; font-size: 0.85rem; min-width: 2rem; padding: 0.15rem 0.5rem;
  border: 1px solid #5a4526; background: var(--panel); color: var(--ink);
  border-radius: 5px; cursor: pointer;
}
.tr-cmptoolbar button:hover { background: #f0e3c0; color: #5a4526; }
.tr-cmptoolbar button.arming { background: #c8891b; color: #3a2600; border-color: #c8891b; }
#tr-cmp-src {
  font-family: ui-monospace, Menlo, monospace; font-size: 0.85rem;
  min-height: 9rem; resize: vertical; padding: 0.5rem;
  border: 1px solid var(--border); border-radius: 6px;
  background: var(--bg); color: var(--ink); line-height: 1.4;
}
.tr-cmppreview-label { font-size: 0.7rem; text-transform: uppercase; letter-spacing: 0.05em; color: var(--muted); }
.tr-cmppreview { border: 1px dashed var(--border); border-radius: 6px; padding: 0.4rem 0.7rem; max-height: 16rem; flex: 0 1 auto; }
.tr-cmpeditbtns { display: flex; gap: 0.5rem; flex-wrap: wrap; }
.tr-cmpeditbtns button {
  font: inherit; font-size: 0.85rem; padding: 0.25rem 0.7rem;
  border-radius: 6px; border: 1px solid #5a4526; background: var(--panel); color: var(--ink); cursor: pointer;
}
.tr-cmpeditbtns button.primary { background: var(--accent); color: var(--accent-ink); border-color: var(--accent); }
.tr-cmpeditbtns button.tr-cmp-danger { border-color: #963d16; color: #963d16; margin-left: auto; }
/* inline delete confirm (holds the "also remove file from disk" checkbox) */
.tr-cmp-delconfirm {
  border: 1px solid #963d16; border-radius: 6px; background: #f7ead0;
  padding: 0.5rem 0.7rem; display: flex; flex-direction: column; gap: 0.4rem;
}
.tr-cmp-delmsg { margin: 0; font-size: 0.82rem; color: #4a2413; }
.tr-cmp-delconfirm .check { font-size: 0.82rem; color: #4a2413; }
.tr-cmp-delbtns { display: flex; gap: 0.5rem; }
.tr-cmp-delbtns button {
  font: inherit; font-size: 0.85rem; padding: 0.2rem 0.7rem;
  border-radius: 6px; border: 1px solid #5a4526; background: var(--panel); color: var(--ink); cursor: pointer;
}
.tr-cmp-delbtns button.tr-cmp-danger { border-color: #963d16; color: #963d16; }
@media (prefers-color-scheme: dark) {
  .tr-cmp-delconfirm { background: #2e1c12; }
  .tr-cmp-delmsg, .tr-cmp-delconfirm .check { color: #e8c9a8; }
}
/* the transcript is in one-shot "click a clip to link" mode */
body.tr-cmp-arming .tr-list .tr-row { cursor: crosshair; }
body.tr-cmp-arming .tr-list .tr-row:hover { outline: 2px solid #c8891b; outline-offset: -2px; }

/* ELAN .eaf import: the tier-mapping panel on the Transcript open screen */
#tr-eafmap {
  border: 1px solid var(--border);
  border-radius: 8px;
  padding: 0.2rem 1rem 0.9rem;
  margin: 0.8rem 0;
  background: var(--bg);
  max-width: 34rem;
}
#tr-eafmap .field { margin: 0.5rem 0; }

/* Flags: colored word highlights + the flags panel (js/flags.js) */
.tr-flaghl { border-radius: 3px; padding: 0 2px; color: #24291c; }
.tr-flagpanel { right: 0; left: auto; }

/* ---- the ⚑ handle (owner ruling, July 30): a small tab on the transcript's
   RIGHT EDGE, level with the top of the panel it draws out. It replaced the
   band strip's ⚑ button — the panel is a thing of the page, not of the
   ribbon, so its control lives on the page's edge. ‹ draws it out, › puts it
   back; the handle rides out with the panel so the arrow always points the
   way it will move. */
.tr-flaghandle {
  position: fixed; top: var(--flagtop, 6rem); right: 0; z-index: 41;
  display: flex; flex-direction: column; align-items: center; gap: 0.15rem;
  background: #5a4526; color: #f0e3c0;
  border: 1px solid #5a4526; border-right: none;
  border-radius: 8px 0 0 8px;
  padding: 0.45rem 0.3rem;
  font: inherit; font-size: 0.85rem; line-height: 1; cursor: pointer;
  transition: right 0.18s ease, background 0.12s;
}
.tr-flaghandle:hover { background: #6d5430; border-color: #6d5430; color: #f0e3c0; }
/* deployed: ride out to the panel's near edge and flip the arrow */
.tr-flaghandle.deployed { right: calc(0.6rem + min(24rem, 92vw)); }
.tr-flaghandle.deployed .tr-flaghandle-arrow { transform: rotate(180deg); }
.tr-flaghandle-arrow { display: block; font-size: 1rem; transition: transform 0.18s ease; }
.tr-flaghandle-n { font-variant-numeric: tabular-nums; font-size: 0.7rem; }
@media (prefers-reduced-motion: reduce) {
  .tr-flaghandle, .tr-flaghandle-arrow { transition: none; }
}

/* ---- the slide-out itself (owner ruling, July 28; aligned to ᏧᏪᏘ ᏗᎪᎵᏰᏗ's
   questions panel). Parked off-canvas to the right and slid in by .deployed;
   visibility rides the same transition so a parked panel is not tabbable. */
/* A notepad INSIDE the transcript, not a drawer glued to the window (owner
   ruling, July 30): it starts just below the bottom-most band — never over
   the title bar or the bands — and it is only as tall as it has flags.
   --flagtop and --flagmax are measured from the live band strip by
   placeFlagPanel(); the fallbacks only cover the first frame. When the flags
   outrun --flagmax the list inside scrolls, so the panel itself never runs
   past the foot of the transcript page. */
.tr-flagaside {
  position: fixed; top: var(--flagtop, 6rem); bottom: auto; right: -26rem;
  z-index: 40;
  width: 24rem; max-width: 92vw;
  height: auto; max-height: var(--flagmax, calc(100vh - 8rem));
  display: flex; flex-direction: column;
  background: var(--panel); border: 1px solid var(--muted); border-radius: 10px;
  box-shadow: -6px 0 18px rgba(0, 0, 0, 0.35);
  padding: 0.7rem 0.9rem;
  visibility: hidden;
  transition: right 0.18s ease, visibility 0.18s;
}
.tr-flagaside.deployed { right: 0.6rem; visibility: visible; }
@media (prefers-reduced-motion: reduce) { .tr-flagaside { transition: none; } }
.tr-flagasidehead { display: flex; align-items: center; gap: 0.4rem; }
.tr-flagasidehead h2 { margin: 0; font-size: 1rem; }
.tr-flagasidex {
  margin-left: auto; background: none; border: none; color: var(--muted);
  font-size: 1rem; cursor: pointer; padding: 0 0.2rem;
}
.tr-flagasidex:hover { color: inherit; }
.tr-flagasidebar {
  display: flex; flex-wrap: wrap; align-items: center; gap: 0.6rem;
  margin: 0.5rem 0 0.2rem; padding-bottom: 0.5rem;
  border-bottom: 1px solid var(--border);
}
/* the colors key — the ONE place type names are spelled out, since the rows
   carry only their color (owner ruling, July 28) */
.tr-flagkey { margin-top: auto; border-top: 1px solid var(--border); padding-top: 0.5rem; }
.tr-flagkey summary {
  font-size: 0.74rem; text-transform: uppercase; letter-spacing: 0.05em;
  color: var(--muted); cursor: pointer;
}
.tr-flagkeyrow { display: flex; align-items: center; font-size: 0.8rem; margin: 0.3rem 0; }
.tr-flagkeyrow .chip {
  display: inline-block; width: 0.7rem; height: 0.7rem;
  border-radius: 3px; margin-right: 0.45rem;
}
/* The list takes its natural height — the panel is only as long as there are
   flags — and shrinks to a scroller ONLY once the panel meets --flagmax
   (owner ruling, July 30). flex-shrink with min-height:0 is what lets it. */
.tr-flagaside .tr-flaglist { max-height: none; flex: 0 1 auto; min-height: 0; }
.tr-flagasidehead, .tr-flagasidebar, .tr-flagkey { flex: none; }

/* Whole-region wash (owner ruling, July 23): a flag with no word, or a word
   that matches no visible line, colors its whole clip instead of a word —
   a left bar at full strength, the tint faint enough to read syllabary
   through. --flagwash is set per row by applyFlagHighlights. */
.tr-row.tr-flagwash {
  box-shadow: inset 3px 0 0 0 var(--flagwash, transparent);
  background-image: linear-gradient(
    to right,
    color-mix(in srgb, var(--flagwash, transparent) 14%, transparent),
    color-mix(in srgb, var(--flagwash, transparent) 6%, transparent)
  );
}
.tr-flaglist { max-height: 18rem; overflow-y: auto; margin: 0.4rem 0; }
.tr-flagitem {
  display: block; width: 100%; text-align: left; background: none;
  border: none; padding: 0.35rem 0.3rem; cursor: pointer;
  border-radius: 6px; color: inherit; font: inherit; font-size: 0.85rem;
}
.tr-flagitem:hover { background: rgba(127, 127, 127, 0.15); }
.tr-flagitem .chip, .tr-flaghead .chip {
  display: inline-block; width: 0.7rem; height: 0.7rem;
  border-radius: 3px; margin-right: 0.45rem;
}
.tr-flagitem .t { color: var(--muted); font-size: 0.75rem; margin-right: 0.5rem; }
.tr-flagitem .note { display: block; color: var(--muted); font-size: 0.75rem; margin-left: 1.15rem; }
.tr-flaghead { margin: 0.55rem 0 0.1rem; font-size: 0.78rem; color: var(--muted); }
/* a general mark's type selector (assignment rewrites its marker label) */
.tr-flagtype { display: block; margin: 0.2rem 0 0 1.15rem; font-size: 0.75rem; }
/* a wordless mark's note edits in place (press-m flow): quiet until it has
   the focus, so a row reads as a row, not a form */
.tr-flagnotedit {
  font: inherit; font-size: 0.85rem; color: inherit;
  background: none; border: none; border-bottom: 1px dashed transparent;
  padding: 0 0.1rem; min-width: 9rem;
}
.tr-flagnotedit:hover { border-bottom-color: var(--muted); }
.tr-flagnotedit:focus {
  outline: none; border-bottom: 1px solid var(--muted);
  background: rgba(127, 127, 127, 0.12); border-radius: 3px;
}
/* the empty panel's one quiet state line */
.tr-flagnone { color: var(--muted); font-size: 0.85rem; padding: 0.4rem 0.3rem; }
#tr-flagaddmark { font-size: 0.8rem; }
#tr-flagnewtype { font-size: 0.8rem; margin: 0.1rem 0 0.35rem; }
@keyframes tr-flagpulse {
  0% { box-shadow: inset 0 0 0 3px rgba(242, 223, 102, 0.95); }
  100% { box-shadow: inset 0 0 0 3px rgba(242, 223, 102, 0); }
}
.tr-flagjump { animation: tr-flagpulse 1.4s ease-out; }
.tr-flagmark { margin-left: 0.3rem; font-size: 0.85em; cursor: help; }
/* resolving a flag removes its marker from the WAV on the next save */
.tr-flagresolve { font-size: 0.72rem; padding: 0 0.4rem; margin-left: 0.55rem; }
/* the hover reveal: one shared fixed panel, repositioned per flag */
.tr-flaghover {
  position: fixed; z-index: 60; max-width: 24rem;
  background: var(--panel); border: 1px solid var(--muted); border-radius: 8px;
  padding: 0.45rem 0.65rem; font-size: 0.85rem;
  box-shadow: 0 6px 18px rgba(0, 0, 0, 0.35);
}
.tr-flaghover .chip {
  display: inline-block; width: 0.7rem; height: 0.7rem;
  border-radius: 3px; margin-right: 0.45rem;
}
.tr-flaghover .type { color: var(--muted); font-size: 0.78rem; }
.tr-flaghover .note { color: var(--muted); font-size: 0.8rem; margin-top: 0.15rem; }
.tr-flaghover .tr-flagresolve { display: block; margin: 0.45rem 0 0; }
/* word-flag creation: the "⚑ Flag" affordance floated by a text selection
   and the compact form it opens — the hover reveal's fixed-panel look
   (.tr-flagadd shares .tr-flaghover), staged by data-stage */
.tr-flagadd[data-stage='button'] { padding: 0.25rem; }
#tr-flagaddbtn { font-size: 0.8rem; }
.tr-flagadd input, .tr-flagadd select {
  display: block; width: 100%; box-sizing: border-box;
  margin: 0.2rem 0; font: inherit; font-size: 0.8rem;
}
#tr-flagword { min-width: 14rem; }
.tr-flagadd .row { display: flex; gap: 0.4rem; margin-top: 0.4rem; }
/* inline flag lines (Display → "flags inline"): each flag's full text on a
   small line above its clip's clean-syllabary line, in the category color.
   Always in the DOM; folded away by CSS while the checkbox is off. */
.tr-row .tr-flaglines { grid-area: 1 / 2; }
.tr-list.select-mode .tr-row .tr-flaglines { grid-area: 1 / 3; }
.tr-list:not(.flags-inline) .tr-flaglines { display: none; }
.tr-flagline {
  display: inline-block; font-size: 0.76rem; color: #24291c;
  padding: 0 0.4rem; border-radius: 3px; margin: 0.05rem 0.4rem 0.15rem 0;
}
/* Edit mode: the line carries the marker's raw label, editable in place —
   the same dashes-mean-editable language as the tier fields. */
.tr-flagline .tr-flagedit {
  display: inline-block; min-width: 8ch; cursor: text; outline: none;
  border-bottom: 1px dashed #6b5f4a;
}
.tr-flagline .tr-flagedit:focus { border-bottom-style: solid; }

/* ---- footnote margin fold-out (owner design July 14) ----
   Superscript numbers always show on footnoted rows; the notes fold out
   into a right-margin rail on the same paper — reader's-edition
   marginalia. Grid columns grow a rail only while folded out, so folded
   in costs no width. */
/* The marker is a <sup>; browsers auto-shrink sup to ~0.83x, but of the 1.3em
   English line that hosts it — landing it at ~17.3px, LARGER than the 0.9em
   (14.4px) footnote it points to (owner: the marker read too big). Sized to
   match the note instead: 0.69em of the 1.3em host = 14.4px. It stays raised
   (vertical-align:super is untouched); only the size is pinned. Keeps its rust
   + bold, which mark it as the clickable reference. */
.tr-fnref { color: #963d16; font-weight: bold; margin-left: 1px; cursor: pointer; font-size: 0.69em; }
/* The margin rail: one wrapper (.tr-marg) per row carries the footnote and
   the figure together in the right-margin cell. */
.tr-row .tr-marg { display: none; }
/* Edit mode opens the rail on every row too (owner ruling, July 14 night:
   footnotes fully editable from the transcript) — the note edits in place,
   an empty hinted box adds one to a region that has none. */
.tr-list.fnotes-on .tr-row,
.tr-list.edit-mode .tr-row { grid-template-columns: 6.2em 1fr var(--fnote-marg, 15.5rem); }
/* Every row owns a rail cell now (it hosts the edit box), but folded-out
   display still draws the hairline only where a note or figure sits
   (.has-marg) — the reader's-edition look is unchanged. */
.tr-list.fnotes-on .tr-row.has-marg .tr-marg,
.tr-list.edit-mode .tr-row .tr-marg {
  display: block; grid-area: 1 / 3 / 13 / 4; align-self: start;
  padding: 0.1rem 0.4rem 0 0.8rem; /* the divider is the list's continuous line, not a per-row border */
}
/* --fnote-size is the screen-only slider in the Display tab (owner ruling
   July 15) — set from JS on #tr-list / #cv-list; absent, the note keeps its
   typographically-set default. Print and Word have their own set sizes. */
/* Footnotes hold PARITY with the phonetics line (owner ruling, July 20): the
   same 0.9em, so they track the text-size stepper together and dragging the
   margin wider no longer changes the type size — it only changes the column.
   Colour is the AA-corrected rail gray (#6b5f4a measured 3.09:1 on the sage,
   under the floor; the print export keeps #6b5f4a, which passes on white). */
.tr-fnote { font-size: 0.9em; color: #504738; line-height: 1.35; }
.tr-list.select-mode.fnotes-on .tr-row,
.tr-list.select-mode.edit-mode .tr-row { grid-template-columns: 1.6rem 6.2em 1fr var(--fnote-marg, 15.5rem); }
.tr-list.select-mode.fnotes-on .tr-row .tr-marg,
.tr-list.select-mode.edit-mode .tr-row .tr-marg { grid-area: 1 / 4 / 13 / 5; }
/* The footnote edit box replaces the read-only margin note while editing. */
.tr-fnwrap { display: none; }
.tr-list.edit-mode .tr-fnote { display: none; }
.tr-list.edit-mode .tr-fnwrap {
  display: flex; gap: 0.35rem; align-items: baseline;
}
.tr-fnedit {
  flex: 1; font-size: 0.8rem; line-height: 1.35; white-space: pre-wrap;
  background: #d3dbc0; border: 1px dashed #5a4526; border-radius: 5px;
  padding: 0.1rem 0.45rem; cursor: text;
}
/* the hint was #5a4526 at opacity .55, which composited to well under AA on
   the box's #d3dbc0 — placeholder text has to be readable too (audit, July 20) */
.tr-fnedit:empty::before { content: attr(data-hint); color: #5f5544; }
.tr-fnedit:focus { outline: 2px solid #963d16; border-style: solid; }
.tr-fnn { color: #963d16; font-weight: bold; font-size: 0.72rem; margin-right: 0.3rem; }
/* Inside a displayed note the number rides the size slider with its text
   (0.9em of the note ≈ the 0.72rem default); the edit-mode chip keeps the
   fixed size above. */
.tr-fnote .tr-fnn { font-size: 0.9em; }
/* Margin figures (photos): thumbnail + "Figure N." caption in the rail. */
.tr-fig { margin-top: 0.35rem; }
.tr-fnote + .tr-fig { margin-top: 0.6rem; }
.tr-fig img { display: block; max-width: 100%; border-radius: 3px; cursor: zoom-in; }
.tr-figcap { font-size: 0.75rem; color: #504738; line-height: 1.3; margin-top: 0.25rem; }
.tr-figmissing {
  border: 1px dashed #a8a488; border-radius: 3px; padding: 0.5rem 0.4rem;
  font-size: 0.72rem; color: #504738; word-break: break-all; /* AA on sage */
}
/* Too narrow for a margin (consultant on a phone, split screen): the open
   toggle tucks each note under its own clip instead. */
@media (max-width: 760px) {
  /* no margin column here, so no continuous divider — notes tuck under clips */
  .tr-list.fnotes-on::after,
  .tr-list.edit-mode::after { display: none; }
  .tr-list.fnotes-on > #tr-fndivider,
  .tr-list.edit-mode > #tr-fndivider { display: none; }
  .tr-list.fnotes-on .tr-row,
  .tr-list.edit-mode .tr-row { grid-template-columns: 6.2em 1fr; }
  /* .has-marg here too, not just .fnotes-on: the wide rule above is
     `.tr-list.fnotes-on .tr-row.has-marg .tr-marg` — one class MORE specific
     than this used to be, so it won even inside this media query and the note
     stayed in a third column that no longer exists in the template. The column
     was implicit and auto-sized, which is what crushed the text beside it on a
     narrow consultant window (found July 28 2026, probing the footnote rail). */
  .tr-list.fnotes-on .tr-row.has-marg .tr-marg,
  .tr-list.edit-mode .tr-row .tr-marg {
    grid-area: 13 / 2; border-left: 2px solid #a8a488;
    padding-left: 0.55rem; margin-top: 0.3rem;
  }
  .tr-list.select-mode.fnotes-on .tr-row,
  .tr-list.select-mode.edit-mode .tr-row { grid-template-columns: 1.6rem 6.2em 1fr; }
  .tr-list.select-mode.fnotes-on .tr-row .tr-marg,
  .tr-list.select-mode.edit-mode .tr-row .tr-marg { grid-area: 13 / 3; }
  .tr-fig img { max-width: 14rem; }
}
/* Figure lightbox: click to enlarge, click anywhere to close. */
#tr-lightbox {
  position: fixed; inset: 0; z-index: 60; background: rgba(20, 18, 14, 0.88);
  display: flex; align-items: center; justify-content: center; cursor: zoom-out;
}
#tr-lightbox[hidden] { display: none; }
#tr-lightbox img { max-width: 92vw; max-height: 92vh; border-radius: 4px; }
/* Add photo panel */
#tr-photo-preview img { max-width: 12rem; max-height: 9rem; border-radius: 3px; display: block; margin: 0.3rem 0; }
#tr-photopanel { max-width: 24rem; }

/* Merged WAV-details panel (owner ruling, July 15): the About subheading
   with its edit checkbox, and consent fields that read as plain text
   while locked. */
.tr-metasub {
  font-weight: 600;
  margin: 10px 0 2px;
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 12px;
}
.tr-metapanel .field input:disabled,
.tr-metapanel .field textarea:disabled {
  border-color: transparent;
  background: transparent;
  color: inherit;
  opacity: 1;
  resize: none;
}

/* ---- ᏗᏥᎶᏍᏙᏗ — the page composer (js/ditsilosdodi.js builds its DOM
   into a #dts-host; these rules serve both homes: the in-app panel and
   the standalone ditsilosdodi.html page) ---- */
.dts-root { display: flex; flex-direction: column; flex: 1; min-height: 0; }
body.dts { margin: 0; height: 100vh; overflow: hidden; display: flex; flex-direction: column; }
/* in-app: when the composer panel is up, pin the body to the viewport (same
   move the loaded transcript makes) so inner panes — the clips tray, the
   canvas — scroll inside themselves instead of stretching the page taller than
   the window (owner report, July 23: a long clips tray ran off the bottom) */
body:not(.popout):has(#panel-ditsilosdodi.active) {
  height: 100vh; overflow: hidden; }
body:not(.popout):has(#panel-ditsilosdodi.active) main {
  padding: 0; display: flex; flex-direction: column; min-height: 0; }
/* the composer escapes the .panel card frame the same way the loaded
   transcript does — full width, no border, no inset (owner report, July 21:
   the 44rem card squeezed it into a corner of a wide window) */
#panel-ditsilosdodi.active {
  flex: 1; display: flex; flex-direction: column; min-height: 0;
  max-width: none; border: none; border-radius: 0; margin-top: 0; padding: 0;
}
.dts-top { display: flex; align-items: center; gap: 0.8rem; padding: 0.45rem 0.9rem;
  border-bottom: 1px solid var(--border); background: var(--panel); flex-wrap: wrap; }
.dts-brand { font-weight: bold; font-size: 1.05rem; }
.dts-muted { color: var(--muted); font-size: 0.85rem; }
.dts-top button, .dts-inspector button, .dts-modal button, .dts-viewbtns button {
  font: inherit; font-size: 0.85rem; padding: 0.3rem 0.7rem; cursor: pointer;
  border: 1px solid var(--border); border-radius: 7px; background: var(--panel); color: var(--ink); }
.dts-top button:hover, .dts-inspector button:hover, .dts-modal button:hover,
.dts-viewbtns button:hover { background: var(--bg); }
.dts-btn-ico { vertical-align: -2px; margin-right: 2px; }
.dts-viewbtns button[aria-pressed="true"] { background: var(--accent); color: var(--accent-ink); }
.dts-viewbtns button:disabled { opacity: 0.45; cursor: default; }
.dts-pagestrip-row { display: flex; align-items: flex-end; gap: 0.6rem; padding: 0.25rem 0.9rem 0;
  background: var(--panel); border-bottom: 1px solid var(--border); flex: none; }
.dts-pagestrip { display: flex; gap: 2px; overflow-x: auto; flex: 1;
  /* same capsule scrollbar the transcript wears (owner ruling, July 20):
     lichen-dark slim thumb on nothing, air around it */
  scrollbar-width: thin; scrollbar-color: #4f5647 transparent;
  padding-bottom: 2px; }
.dts-pagestrip::-webkit-scrollbar { height: 10px; }
.dts-pagestrip::-webkit-scrollbar-track { background: transparent; }
.dts-pagestrip::-webkit-scrollbar-thumb {
  background: #4f5647;
  border-radius: 999px;
  border: 3px solid transparent;
  background-clip: content-box;
}
.dts-pagestrip::-webkit-scrollbar-thumb:hover {
  background: #6b7263;
  background-clip: content-box;
}
.dts-pagestrip button { font: inherit; font-size: 0.75rem; padding: 0.3rem 0.55rem 0.2rem; cursor: pointer;
  border: 1px solid var(--border); border-bottom: none; border-radius: 7px 7px 0 0;
  background: var(--bg); color: var(--muted); white-space: nowrap;
  display: flex; flex-direction: column; align-items: center; gap: 2px; }
.dts-pagestrip button img, .dts-pagestrip .dts-thumb-ph {
  width: 56px; height: 56px; object-fit: cover; border-radius: 4px;
  display: block; pointer-events: none; background: var(--log-bg); }
.dts-pagestrip button[aria-current="true"] { background: var(--panel); color: var(--ink);
  font-weight: bold; border-color: var(--accent); }
.dts-pagestrip button.dts-inspread { border-color: var(--accent); }
.dts-viewbtns { display: flex; gap: 0.3rem; padding-bottom: 0.25rem; }
.dts-main { display: flex; flex: 1; min-height: 0; }
.dts-rail { display: flex; flex-direction: column; gap: 0.3rem; padding: 0.5rem 0.35rem;
  border-right: 1px solid var(--border); background: var(--panel); align-items: center;
  overflow-y: auto; }
.dts-tool { width: 2.1rem; height: 2.1rem; font: inherit; cursor: pointer; border-radius: 7px;
  border: 1px solid var(--border); background: var(--panel); color: var(--ink); flex: none; }
.dts-tool[aria-pressed="true"] { background: var(--accent); color: var(--accent-ink); }
.dts-rail-sep { height: 1px; width: 70%; background: var(--border); margin: 0.3rem 0; flex: none; }
/* right-click color popover for the pencil / highlighter tools */
.dts-toolpop { position: fixed; z-index: 60; background: var(--panel);
  border: 1px solid var(--border); border-radius: 9px; padding: 0.5rem 0.6rem;
  box-shadow: 0 4px 16px rgba(0,0,0,0.3); display: flex; flex-direction: column; gap: 0.4rem; }
.dts-toolpop-title { font-size: 0.75rem; color: var(--muted); }
.dts-toolpop-row { display: flex; align-items: center; gap: 0.45rem; }
.dts-toolpop-color { width: 3rem; height: 2rem; padding: 0; border: 1px solid var(--border);
  border-radius: 6px; background: var(--bg); cursor: pointer; }
.dts-toolpop-pick { font: inherit; width: 2rem; height: 2rem; padding: 0; cursor: pointer;
  border: 1px solid var(--border); border-radius: 6px; background: var(--panel); color: var(--ink); }
.dts-toolpop-pick:hover { background: var(--bg); }
/* the pop-out live viewer window (read-only page reader) */
.dts-popview { display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: 1rem; padding: 1.4rem; box-sizing: border-box; min-height: 100vh;
  background: var(--log-bg); overflow: auto; }
.dts-pv-stage { display: flex; gap: 14px; align-items: center; justify-content: center;
  max-width: 100%; flex-wrap: wrap; }
.dts-pv-page { background: #fff; box-shadow: 0 3px 18px rgba(0,0,0,0.35);
  width: auto; height: auto; max-width: 100%; max-height: 90vh; }
.dts-pv-status { position: fixed; bottom: 8px; left: 12px; font-size: 0.8rem; color: var(--muted); }
.dts-pv-empty { color: var(--muted); font-size: 0.95rem; text-align: center; }
.dts-canvas-wrap { flex: 1; overflow: auto; position: relative; background: var(--log-bg); }
.dts-canvas-inner { position: relative; margin: 24px auto; width: max-content; }
.dts-root #dts-svg { display: block; background: transparent; }
/* SVG2 follows CSS whitespace rules — without this, Chrome COLLAPSES the
   double word-spaces the owner's book uses, narrowing every line (~6% on
   Page 1; found July 22 comparing rendered advance to the font's metrics) */
.dts-root #dts-svg text { white-space: pre; }
.dts-root #dts-svg image, .dts-root #dts-svg g[data-page] > rect:first-child {
  box-shadow: 0 2px 14px rgba(0,0,0,0.25); }
.dts-root .dts-frame { fill: none; stroke: var(--accent); stroke-width: 1.5; vector-effect: non-scaling-stroke; }
.dts-root .dts-handle { fill: var(--accent); stroke: white; stroke-width: 1; vector-effect: non-scaling-stroke; }
.dts-editor { position: absolute; outline: 2px solid var(--accent); background: transparent;
  white-space: pre-wrap; overflow-wrap: normal; transform-origin: top left; caret-color: #d33; }
.dts-inspector { width: 15.5rem; flex: none; border-left: 1px solid var(--border); background: var(--panel);
  padding: 0.7rem; display: flex; flex-direction: column; gap: 0.55rem; overflow-y: auto; }
.dts-inspector label { font-size: 0.78rem; color: var(--muted); display: block; margin-bottom: 0.15rem; }
.dts-inspector input[type="number"] { width: 4.2rem; font: inherit; padding: 0.2rem 0.3rem;
  border: 1px solid var(--border); border-radius: 5px; background: var(--bg); color: var(--ink); }
.dts-inspector input[type="color"] { width: 2.4rem; height: 1.7rem; padding: 0; border: 1px solid var(--border);
  border-radius: 5px; background: var(--bg); }
.dts-check { font-size: 0.8rem; color: var(--ink); display: flex; align-items: center; gap: 0.3rem; }
.dts-row { display: flex; gap: 0.4rem; align-items: center; flex-wrap: wrap; }
.dts-seg button[aria-pressed="true"] { background: var(--accent); color: var(--accent-ink); }
#dts-insp-text { display: flex; flex-direction: column; gap: 0.55rem; }
/* the inspector's band bar: Text · Special effects, each dropping its band */
.dts-insp-bar { display: flex; gap: 0.3rem; }
.dts-insp-bar button { flex: 1; font-size: 0.8rem !important; padding: 0.3rem 0.4rem !important; }
.dts-insp-bar button[aria-pressed="true"] { background: var(--accent) !important; color: var(--accent-ink) !important; }
.dts-band { display: flex; flex-direction: column; gap: 0.55rem;
  border-left: 2px solid var(--border); padding-left: 0.55rem; }
/* sub-bands inside Special effects: Glow · Dropshadow */
.dts-subband-btn { text-align: left !important; font-size: 0.78rem !important;
  padding: 0.25rem 0.5rem !important; }
.dts-subband-btn::before { content: '▸ '; color: var(--muted); }
.dts-subband-btn[aria-pressed="true"]::before { content: '▾ '; }
.dts-subband-btn[aria-pressed="true"] { border-color: var(--accent) !important; }
.dts-subband { display: flex; flex-direction: column; gap: 0.55rem;
  border-left: 2px solid var(--border); padding-left: 0.55rem; margin-left: 0.2rem; }
/* the clip tray (owner design July 21, built July 22): project clips above,
   the ᏗᏥᎶᏍᏙᏗ library below. An in-flow drawer between the tool rail and
   the canvas — closed, it is just its named edge tab. */
/* The drawers: a DOCKED left column (owner ruling, July 24: never in front of
   the page — the canvas sits to its right). Clips on top, Layers pinned to the
   bottom; each is its own drawer with a vertical tab that expands its body to
   the right. Collapsed, the column is just the two thin tabs. */
.dts-drawers { flex: none; display: flex; flex-direction: column; align-items: flex-start;
  min-height: 0; border-right: 1px solid var(--border); }
.dts-tray { display: flex; min-height: 0; }
.dts-tray.open { flex: 1; }
.dts-tray-tab { flex: none; writing-mode: vertical-rl; font: inherit; font-size: 0.78rem;
  padding: 0.7rem 0.18rem; border: none; background: var(--panel);
  color: var(--muted); cursor: pointer; }
.dts-tray-tab:hover { background: var(--bg); color: var(--ink); }
.dts-tray.open .dts-tray-tab { color: var(--ink); }
.dts-tray-body { width: 16rem; min-height: 0; overflow-y: auto; display: none;
  padding: 0.5rem 0.6rem; flex-direction: column; gap: 0.5rem; background: var(--panel);
  scrollbar-width: thin; scrollbar-color: #4f5647 transparent; }
.dts-tray.open .dts-tray-body { display: flex; }
.dts-tray-sechead { font: inherit; font-size: 0.78rem; text-align: left;
  color: var(--muted); background: none; border: none; cursor: pointer;
  padding: 0.15rem 0; border-bottom: 1px solid var(--border); }
.dts-tray-sechead:hover { color: var(--ink); }
.dts-clip-group { font-size: 0.72rem; color: var(--muted); margin: 0.35rem 0 0.1rem;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.dts-clip { display: flex; align-items: center; gap: 0.4rem; padding: 0.22rem 0.1rem;
  border-radius: 6px; cursor: grab; }
.dts-clip:hover { background: var(--bg); }
.dts-clip-play, .dts-clip-x { flex: none; width: 1.6rem; height: 1.5rem; padding: 0;
  font: inherit; font-size: 0.75rem; border: 1px solid var(--border); border-radius: 6px;
  background: var(--panel); color: var(--ink); cursor: pointer; }
.dts-clip-play:hover, .dts-clip-x:hover { background: var(--bg); }
.dts-clip-text { flex: 1; min-width: 0; display: flex; flex-direction: column; }
.dts-clip-main { font-size: 0.88rem; overflow: hidden; text-overflow: ellipsis;
  white-space: nowrap; }
.dts-clip-cite { font-size: 0.68rem; color: var(--muted); overflow: hidden;
  text-overflow: ellipsis; white-space: nowrap; }
.dts-clip-missing .dts-clip-cite { color: #b04a3a; }
.dts-clip-check { flex: none; margin: 0; cursor: pointer; appearance: none; -webkit-appearance: none;
  width: 1.05rem; height: 1.05rem; border: 1px solid var(--border); border-radius: 4px;
  background: var(--bg); display: grid; place-content: center; }
.dts-clip-check:hover { border-color: var(--accent); }
.dts-clip-check:checked { background: var(--accent); border-color: var(--accent); }
.dts-clip-check:checked::after { content: '✓'; font-size: 0.72rem; line-height: 1; color: var(--accent-ink); }
.dts-clip-sel { background: var(--bg); box-shadow: inset 2px 0 0 var(--accent, #7c8f6a); }
.dts-clip-selbar { display: flex; align-items: center; gap: 0.4rem; margin: 0.1rem 0 0.35rem;
  padding: 0.2rem 0.1rem; border-bottom: 1px solid var(--border); }
.dts-clip-selcount { font-size: 0.72rem; color: var(--muted); flex: 1; }
.dts-clip-seldel, .dts-clip-selclear { font: inherit; font-size: 0.72rem; padding: 0.12rem 0.45rem;
  border: 1px solid var(--border); border-radius: 6px; background: var(--panel);
  color: var(--ink); cursor: pointer; }
.dts-clip-seldel { color: #b04a3a; border-color: #b04a3a; }
.dts-clip-seldel:hover { background: #b04a3a; color: #fff; }
.dts-clip-selclear:hover { background: var(--bg); }
.dts-tray .dts-hint { font-size: 0.72rem; }
/* the on-page play pins: clickable in select mode */
.dts-root .dts-audiopin { cursor: pointer; }

/* the layers panel: bottom-left drawer, slides out from the window's left
   edge on its little arrow tab (owner spec, July 22) */
.dts-layers { display: flex; margin-top: auto; min-height: 0; }
.dts-layers-tab { flex: none; writing-mode: vertical-rl; font: inherit; font-size: 0.78rem;
  padding: 0.7rem 0.18rem; border: none; border-top: 1px solid var(--border);
  background: var(--panel); color: var(--muted); cursor: pointer; }
.dts-layers-tab:hover { background: var(--bg); color: var(--ink); }
.dts-layers.open .dts-layers-tab { color: var(--ink); }
.dts-layers-panel { width: 16rem; max-height: 44vh; min-height: 0; display: none;
  flex-direction: column; background: var(--panel); border-top: 1px solid var(--border); }
.dts-layers.open .dts-layers-panel { display: flex; }
.dts-layers-head { display: flex; justify-content: space-between; align-items: center;
  padding: 0.35rem 0.6rem; border-bottom: 1px solid var(--border);
  font-size: 0.8rem; color: var(--muted); }
.dts-layers-actions button { font: inherit; font-size: 0.85rem; width: 1.6rem; height: 1.5rem;
  padding: 0; cursor: pointer; border: 1px solid var(--border); border-radius: 6px;
  background: var(--panel); color: var(--ink); }
.dts-layers-actions button:hover { background: var(--bg); }
.dts-layers-list { flex: 1; min-height: 0; overflow-y: auto; padding: 0.25rem 0;
  scrollbar-width: thin; scrollbar-color: #4f5647 transparent; }
.dts-layer-row { display: flex; align-items: center; gap: 0.45rem;
  padding: 0.22rem 0.6rem; cursor: pointer; font-size: 0.78rem; color: var(--ink);
  border-left: 2px solid transparent; }
.dts-layer-row:hover { background: var(--bg); }
.dts-layer-row.selected { background: var(--bg); border-left-color: var(--accent); }
.dts-layer-row.dts-drop { box-shadow: inset 0 2px 0 var(--accent); }
.dts-eye { width: 1.5rem; height: 1.3rem; flex: none; font-size: 0.8rem; line-height: 1;
  border: 1px solid var(--border); border-radius: 5px; background: var(--bg);
  cursor: pointer; padding: 0; color: var(--ink); }
.dts-layer-label { flex: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.dts-layer-label-edit { white-space: pre-wrap; overflow: visible; text-overflow: clip;
  outline: 1px solid var(--accent); border-radius: 4px; padding: 1px 3px;
  background: var(--bg); color: var(--ink); cursor: text; caret-color: var(--accent); }
.dts-layer-del { flex: none; width: 1.4rem; height: 1.3rem; padding: 0; font-size: 0.75rem;
  line-height: 1; border: 1px solid var(--border); border-radius: 5px; background: var(--bg);
  color: var(--muted); cursor: pointer; opacity: 0; transition: opacity 0.12s ease; }
.dts-layer-row:hover .dts-layer-del, .dts-layer-row.selected .dts-layer-del { opacity: 1; }
.dts-layer-del:hover { color: var(--ink); border-color: var(--accent); }
.dts-layer-thumb { width: 32px; height: 32px; flex: none;
  border: 1px solid var(--border); border-radius: 4px;
  /* the transparency checkerboard, in the app's own palette */
  background: repeating-conic-gradient(var(--border) 0% 25%, var(--log-bg) 0% 50%) 0 0 / 10px 10px; }

/* the shadow angle dial: a compass face whose needle points at the light */
.dts-dial { width: 34px; height: 34px; border-radius: 50%; position: relative;
  border: 1px solid var(--border); background: var(--bg); cursor: grab;
  flex: none; touch-action: none; }
.dts-dial:active { cursor: grabbing; }
.dts-dial-needle { position: absolute; left: 50%; top: 50%; width: 45%; height: 2px;
  background: var(--accent); transform-origin: left center; border-radius: 2px;
  transform: rotate(-120deg); pointer-events: none; }
.dts-dial::after { content: ''; position: absolute; left: 50%; top: 50%;
  width: 6px; height: 6px; margin: -3px 0 0 -3px; border-radius: 50%;
  background: var(--muted); }
.dts-root #dts-shadow-angle { width: 3.4rem; }
.dts-fontdrop { position: relative; }
.dts-root #dts-font-btn { width: 100%; box-sizing: border-box; text-align: left;
  font: inherit; font-size: 0.95rem; padding: 0.3rem 0.7rem; cursor: text;
  border: 1px solid var(--border); border-radius: 7px; background: var(--panel);
  color: var(--ink); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.dts-root #dts-font-btn:focus { border-color: var(--accent); outline: none; }
.dts-fontlist .dts-fonthi { background: var(--bg); }
.dts-fontlist { position: absolute; z-index: 30; top: 100%; left: 0; width: 100%; max-height: 22rem;
  overflow-y: auto; background: var(--panel); border: 1px solid var(--border); border-radius: 7px;
  box-shadow: 0 6px 20px rgba(0,0,0,0.3); }
.dts-fontlist .dts-fontsec { font-size: 0.7rem; color: var(--muted); padding: 0.3rem 0.6rem 0.1rem;
  text-transform: uppercase; letter-spacing: 0.05em; }
.dts-fontlist button { display: block; width: 100%; text-align: left; border: none; background: none;
  font-size: 1.02rem; padding: 0.35rem 0.6rem; cursor: pointer; color: var(--ink); border-radius: 0; }
.dts-fontlist button:hover { background: var(--bg); }
.dts-fontlist .dts-nocover { color: var(--muted); }
.dts-fontlist .dts-nocover::after { content: " ▵"; color: #c67f24; font-size: 0.75rem; }
.dts-status { margin-left: auto; }
.dts-modal-back { position: fixed; inset: 0; background: rgba(0,0,0,0.45); z-index: 40;
  display: flex; align-items: center; justify-content: center; }
.dts-modal { background: var(--panel); border: 1px solid var(--border); border-radius: 10px;
  padding: 1.2rem 1.4rem; width: min(26rem, 92vw); max-height: 80vh; overflow-y: auto; }
.dts-modal h2 { margin: 0 0 0.7rem; font-size: 1.05rem; }
.dts-modal input[type="text"] { font: inherit; width: 100%; box-sizing: border-box; padding: 0.35rem 0.5rem;
  border: 1px solid var(--border); border-radius: 6px; background: var(--bg); color: var(--ink); }
.dts-projlist { display: flex; flex-direction: column; gap: 0.3rem; margin: 0.6rem 0; }
.dts-projrow { display: flex; gap: 0.3rem; align-items: stretch; }
.dts-projrow button:first-child { flex: 1; text-align: left; }
/* a project row: the display name on top, its storage path underneath in
   understated gray (owner design, July 23 — name and folder diverge on rename,
   so the path stays in view to keep them associated) */
/* shelf headings in the project picker (Projects / Finished) */
.dts-shelf-head { font-size: 0.72rem; text-transform: uppercase; letter-spacing: 0.08em;
  color: var(--muted); margin: 0.5rem 0 0.1rem; padding-bottom: 0.1rem;
  border-bottom: 1px solid var(--border); }
.dts-shelf-head:first-child { margin-top: 0; }
.dts-projname { display: flex; flex-direction: column; align-items: flex-start; gap: 0.1rem; }
.dts-projname > span:first-child { font-size: 0.95rem; }
.dts-projpath { font-size: 0.72rem; color: var(--muted); font-variant: normal;
  opacity: 0.85; letter-spacing: 0.01em; }
/* the New dialog's page setup: size + unit, DPI, ground, mode, depth */
.dts-newsetup { display: flex; flex-direction: column; gap: 0.5rem; margin-top: 0.55rem;
  padding: 0.6rem 0.65rem; border: 1px solid var(--border); border-radius: 8px; }
.dts-ns-row { display: flex; gap: 0.6rem; align-items: flex-end; }
.dts-ns-row label { display: flex; flex-direction: column; gap: 0.2rem; flex: 1;
  min-width: 0; font-size: 0.78rem; color: var(--muted); }
.dts-newsetup input[type="number"], .dts-newsetup select { font: inherit; width: 100%;
  box-sizing: border-box; padding: 0.32rem 0.4rem; border: 1px solid var(--border);
  border-radius: 6px; background: var(--bg); color: var(--ink); }
.dts-newsetup input[type="color"] { width: 100%; height: 1.95rem; padding: 0.12rem;
  box-sizing: border-box; border: 1px solid var(--border); border-radius: 6px;
  background: var(--bg); }
.dts-hint { font-size: 0.78rem; color: var(--muted); line-height: 1.45; }
/* composer project chips in the tab strip + the new-tab page's section */
.tr-tabchip.tr-tabchip-dts .tr-tablabel { font-style: italic; }
#tr-dts-open { margin-top: 1.4rem; }

/* ── Grammar tab chip (☰ → Grammar opens its own tab) ───────────────────── */
.tr-tabchip-grammar .tr-tablabel { font-variant: small-caps; letter-spacing: 0.04em; }

/* ── Grammar library reader (js/library.js) ────────────────────────────────
   Owner's north star: simplistic, soft, elegant. A quiet sidebar of sections
   and a centred serif reading column. Themes light/dark from the shared
   tokens; the reading column is the scroll region. */
/* the reader fills the whole window (owner ruling, July 22): the panel spans
   main and, via the app's :has() full-height pattern, the two panes take the
   viewport height and scroll internally — no page scroll, no floating card. */
#panel-grammar.panel { max-width: none; border: none; background: none; padding: 0; margin-top: 0.8rem; }
#panel-grammar .hint { max-width: 46rem; }
body:not(.popout):has(#panel-grammar.active) { height: 100vh; overflow: hidden; }
body:not(.popout):has(#panel-grammar.active) main {
  flex: 1; min-height: 0; display: flex; flex-direction: column; overflow: hidden;
}
body:not(.popout):has(#panel-grammar.active) #panel-grammar {
  flex: 1; min-height: 0; display: flex; flex-direction: column;
}
body:not(.popout):has(#panel-grammar.active) .lib-layout { flex: 1; min-height: 0; }
.lib-layout {
  position: relative; /* anchor for the side rail, drawer, and font toolbar */
  /* three columns: published-grammars rail · contents TOC · reader */
  display: grid; grid-template-columns: 11rem 14.5rem 1fr; gap: 0;
  transition: grid-template-columns 0.2s ease;
  border: 1px solid var(--border); border-radius: 12px; overflow: hidden;
  margin: 0.8rem 0; background: var(--panel);
}
.lib-layout.shelf-collapsed { grid-template-columns: 2.2rem 14.5rem 1fr; }
/* when the annotations drawer is open, reflow the reading column left so the
   drawer never overtakes the text or the (left-margin) page numbers */
.lib-layout.drawer-open .lib-reader { padding-right: 24rem; }
.lib-nav {
  border-right: 1px solid var(--border); padding: 1rem 0.6rem;
  min-height: 0; overflow-y: auto; background: var(--log-bg);
}
.lib-crumb {
  font-size: 0.66rem; color: var(--muted); text-transform: uppercase;
  letter-spacing: 0.08em; padding: 0.1rem 0.6rem 0.7rem;
}
/* "Published grammars" — its own side rail (chronological), retractable
   horizontally; separate from the contents TOC. */
.lib-shelf {
  border-right: 1px solid var(--border); background: var(--log-bg);
  display: flex; flex-direction: column; min-height: 0; overflow: hidden;
}
.lib-shelf-toggle {
  display: flex; align-items: center; justify-content: space-between; gap: 0.4rem; width: 100%;
  background: none; border: none; font: inherit; cursor: pointer; color: var(--muted);
  font-size: 0.66rem; text-transform: uppercase; letter-spacing: 0.07em; white-space: nowrap;
  padding: 0.75rem 0.55rem 0.5rem;
}
.lib-shelf-toggle:hover { color: var(--ink); }
.lib-shelf-lbl { overflow: hidden; text-overflow: ellipsis; }
.lib-shelf-chev { flex: none; font-size: 1rem; line-height: 1; transition: transform 0.15s; }
.lib-shelf-list { list-style: none; margin: 0; padding: 0 0.35rem 0.6rem; min-height: 0; overflow-y: auto; }
/* collapsed: a thin vertical rail with the label rotated */
.lib-layout.shelf-collapsed .lib-shelf-list { display: none; }
.lib-layout.shelf-collapsed .lib-shelf-toggle {
  writing-mode: vertical-rl; justify-content: flex-start; height: 100%;
  padding: 0.75rem 0.35rem; gap: 0.6rem;
}
.lib-layout.shelf-collapsed .lib-shelf-chev { transform: rotate(180deg); }
.lib-pub {
  display: flex; align-items: baseline; justify-content: space-between; gap: 0.5rem; width: 100%;
  text-align: left; font: inherit; background: none; border: none; color: var(--ink); cursor: pointer;
  padding: 0.4rem 0.55rem; border-radius: 7px; border-left: 2px solid transparent;
}
.lib-pub:hover { background: color-mix(in oklab, var(--accent) 8%, transparent); }
.lib-pub.on { background: color-mix(in oklab, var(--accent) 12%, transparent); border-left-color: #c0592b; }
.lib-pub-t { font-size: 0.82rem; line-height: 1.3; }
.lib-pub.on .lib-pub-t { color: var(--accent); font-weight: 600; }
.lib-pub-yr { font-size: 0.68rem; color: var(--muted); font-variant-numeric: tabular-nums; flex: none; }

.lib-toc-head {
  font-size: 0.66rem; text-transform: uppercase; letter-spacing: 0.07em;
  color: var(--muted); padding: 0.1rem 0.55rem 0.5rem;
}
.lib-sections { list-style: none; margin: 0 0 0.3rem; padding: 0; }
.lib-sec {
  display: block; width: 100%; text-align: left; font: inherit; background: none;
  border: none; color: var(--ink); cursor: pointer; position: relative;
  padding: 0.5rem 0.7rem 0.55rem 0.9rem; border-radius: 8px; transition: background 0.12s;
}
.lib-sec::before {
  content: ''; position: absolute; left: 0.35rem; top: 0.7rem; bottom: 0.7rem;
  width: 2px; border-radius: 2px; background: transparent; transition: background 0.12s;
}
.lib-sec:hover { background: color-mix(in oklab, var(--accent) 8%, transparent); }
.lib-sec[aria-current="true"] { background: color-mix(in oklab, var(--accent) 12%, transparent); }
.lib-sec[aria-current="true"]::before { background: #c0592b; }
.lib-sec-t { display: block; font-size: 0.85rem; line-height: 1.35; }
.lib-sec[aria-current="true"] .lib-sec-t { font-weight: 600; }
.lib-sec-s { display: block; font-size: 0.68rem; color: var(--muted); margin-top: 0.15rem; }

.lib-reader { --lib-fs: 1; min-height: 0; overflow-y: auto; padding: 2.4rem 2rem 3rem;
  transition: padding-right 0.2s ease; }
/* the app's slim lichen scrollbar (owner convention, July 20) on both panes */
.lib-nav, .lib-reader { scrollbar-width: thin; scrollbar-color: #4f5647 transparent; }
.lib-nav::-webkit-scrollbar, .lib-reader::-webkit-scrollbar { width: 12px; }
.lib-nav::-webkit-scrollbar-track, .lib-reader::-webkit-scrollbar-track { background: transparent; }
.lib-nav::-webkit-scrollbar-thumb, .lib-reader::-webkit-scrollbar-thumb {
  background: #4f5647; border-radius: 999px; border: 3px solid transparent; background-clip: content-box;
}
.lib-nav::-webkit-scrollbar-thumb:hover, .lib-reader::-webkit-scrollbar-thumb:hover {
  background: #6b7263; background-clip: content-box;
}
/* prose reads at a comfortable measure, centred; paradigm tables & chart
   blocks break to the full reader width so their columns (form / gloss /
   analysis) don't crush together */
.lib-placard, .lib-titlerow, .lib-sub,
.lib-body > p, .lib-body > .lib-h {
  max-width: 40rem; margin-left: auto; margin-right: auto;
}
.lib-body { position: relative; } /* positioning context for marginal page nums */
.lib-titlerow { display: flex; align-items: flex-start; gap: 0.6rem; }
.lib-titlerow .lib-title { flex: 1; margin: 0; }
.lib-mark-star {
  background: none; border: none; cursor: pointer; font-size: 1.25rem; line-height: 1;
  color: var(--muted); padding: 0.1rem 0.2rem; flex: none; transition: color 0.12s, transform 0.1s;
}
.lib-mark-star:hover { color: var(--ink); transform: scale(1.1); }
.lib-mark-star[aria-pressed="true"] { color: #c0592b; }

/* continuous document: one publication header, then every section flows in
   sequence (so the whole grammar reads top-to-bottom and Ctrl+F finds it all).
   scroll-margin keeps a scrolled-to heading clear of the top edge. */
.lib-dochead { max-width: 40rem; margin: 0 auto 0.5rem; text-align: center; }
.lib-doctitle {
  font-size: 0.72rem; text-transform: uppercase; letter-spacing: 0.12em; color: var(--muted);
}
.lib-dochead .lib-placard { border-top: none; padding-top: 0.4rem; margin-top: 0.35rem; text-align: center; }
.lib-docsec { scroll-margin-top: 1.2rem; padding-top: 1.6rem; }
.lib-docsec + .lib-docsec { border-top: 1px solid var(--border); margin-top: 1.4rem; }
mark.lib-hl { scroll-margin-top: 6rem; }
.lib-body { max-width: none; }
/* tables/blocks break wider than the prose measure but stay centred, and keep
   a right gutter free (6rem) so the marginal page numbers never collide */
.lib-body > .lib-tablewrap, .lib-body > .lib-block { max-width: min(60rem, 100% - 6rem); }
.lib-title {
  font-size: calc(1.5rem * var(--lib-fs, 1)); font-weight: 600; line-height: 1.25; margin: 0 auto 0.25rem;
  letter-spacing: -0.01em;
}
.lib-sub { font-size: calc(0.8rem * var(--lib-fs, 1)); color: var(--muted); margin: 0 auto 0.6rem; }
.lib-placard {
  font-size: 0.72rem; color: var(--muted); font-style: italic; line-height: 1.5;
  border-top: 1px solid var(--border); padding-top: 0.7rem; margin-top: 0.9rem;
}
.lib-hint, .lib-loading, .lib-err { color: var(--muted); font-size: 0.95rem; line-height: 1.6; }
.lib-err { color: #c0592b; }

.lib-body { font-size: calc(1.02rem * var(--lib-fs, 1)); line-height: 1.75; color: var(--ink); margin-top: 1.6rem; }
.lib-body p { margin: 0 auto 1rem; } /* auto keeps the 40rem column centred */
.lib-h { margin: 1.8rem auto 0.6rem; line-height: 1.3; color: var(--accent); font-weight: 600; }
h3.lib-h { font-size: calc(1.12rem * var(--lib-fs, 1)); }
h4.lib-h { font-size: calc(0.98rem * var(--lib-fs, 1)); }
h5.lib-h { font-size: calc(0.88rem * var(--lib-fs, 1)); }
.lib-body strong { font-weight: 600; }
.lib-body u { text-decoration: none; border-bottom: 1.5px solid color-mix(in oklab, #c0592b 55%, transparent); }
.lib-body em { font-style: italic; }

/* page markers — the printed page number, kept in the MARGIN beside the text
   (Gutenberg style: pulled out of the flow so reading is uninterrupted).
   Narrow fallback below: a plain centred label when there's no margin room. */
.lib-body > .lib-pagemark {
  max-width: 40rem; margin: 1.5rem auto 1rem; text-align: center;
  color: var(--muted); font-size: 0.62rem; letter-spacing: 0.1em; text-transform: uppercase;
}
.lib-body > .lib-pagemark span { font-variant-numeric: tabular-nums; }
/* wide/fullscreen: the reading column is centred, so a real right margin
   exists — set the page number into it, out of the text flow */
@media (min-width: 62rem) {
  /* reserve a dedicated left gutter so the marginal page number can never sit
     in the text — even when the annotations drawer squeezes the column */
  .lib-body { padding-left: 3.4rem; }
  .lib-body > .lib-pagemark {
    position: absolute; left: 0.3rem; right: auto; max-width: none; margin: 0; width: 2.6rem;
    text-align: left; text-transform: none; letter-spacing: 0.02em; font-size: 0.72rem;
    transform: translateY(-0.05rem); user-select: none; pointer-events: none;
  }
}

/* paradigm / chart blocks: monospaced so the book's alignment survives */
.lib-block {
  font-family: ui-monospace, Menlo, monospace; font-size: calc(0.76rem * var(--lib-fs, 1)); line-height: 1.5;
  background: var(--log-bg); border: 1px solid var(--border); border-radius: 8px;
  padding: 0.9rem 1.1rem; overflow-x: auto; white-space: pre; margin: 0 auto 1.2rem;
}
.lib-tablewrap { overflow-x: auto; margin: 0 auto 1.3rem; }
.lib-table { border-collapse: collapse; font-size: calc(0.88rem * var(--lib-fs, 1)); width: 100%; }
.lib-table th, .lib-table td {
  padding: 0.4rem 0.7rem; text-align: left; vertical-align: top;
  border-bottom: 1px solid var(--border);
}
.lib-table th {
  font-size: 0.68rem; text-transform: uppercase; letter-spacing: 0.05em;
  color: var(--muted); font-weight: 600; border-bottom: 1.5px solid var(--border);
}
.lib-table tbody tr:last-child td { border-bottom: none; }

/* ── marks: bookmarks + highlights ──────────────────────────────────────── */
/* highlight colours — soft translucent washes legible in both themes */
mark.lib-hl {
  background: color-mix(in oklab, #e0b34d 40%, transparent);
  color: inherit; border-radius: 3px; padding: 0 0.5px; cursor: pointer;
  box-decoration-break: clone; -webkit-box-decoration-break: clone;
}
mark.lib-hl[data-color="green"] { background: color-mix(in oklab, #7faa72 42%, transparent); }
mark.lib-hl[data-color="rose"] { background: color-mix(in oklab, #cf7a86 44%, transparent); }
mark.lib-hl[data-color="amber"] { background: color-mix(in oklab, #e0b34d 40%, transparent); }

/* font-size stepper — top-right of the grammar view, left of the drawer tab */
.lib-toolbar {
  position: absolute; top: 0.5rem; right: 2.5rem; z-index: 6; display: flex; gap: 0.25rem;
}
.lib-fontbtn {
  font: inherit; font-size: 0.82rem; line-height: 1; cursor: pointer;
  background: var(--log-bg); color: var(--muted); border: 1px solid var(--border);
  border-radius: 6px; padding: 0.25rem 0.5rem; min-width: 1.8rem;
}
.lib-fontbtn:hover { color: var(--ink); background: var(--panel); border-color: var(--accent); }

/* Highlights & Annotations — a drawer that slides in from the right edge of
   the reading area, with the toggle tab pinned to that edge. */
.lib-drawer-toggle {
  position: absolute; top: 0.9rem; right: 0; z-index: 6;
  writing-mode: vertical-rl; text-orientation: mixed;
  display: flex; align-items: center; gap: 0.45rem;
  background: var(--log-bg); color: var(--muted);
  border: 1px solid var(--border); border-right: none; border-radius: 8px 0 0 8px;
  padding: 0.85rem 0.35rem; font: inherit; font-size: 0.7rem; letter-spacing: 0.03em; cursor: pointer;
}
.lib-drawer-toggle:hover { color: var(--ink); background: var(--panel); }
.lib-drawer-count {
  writing-mode: horizontal-tb; background: var(--focus); color: #fff;
  border-radius: 999px; font-size: 0.58rem; line-height: 1; padding: 0.15rem 0.32rem; min-width: 0.9rem; text-align: center;
}
.lib-drawer {
  /* floats at the top-right, only as tall as its content (capped, then the
     list scrolls) — never a full-height slab over the book */
  position: absolute; top: 0.9rem; right: 0; bottom: auto;
  width: 23rem; max-width: 88%; max-height: calc(100% - 1.8rem);
  display: flex; flex-direction: column; z-index: 7; background: var(--panel);
  border: 1px solid var(--border); border-right: none; border-radius: 9px 0 0 9px;
  box-shadow: -10px 6px 26px rgba(0, 0, 0, 0.28);
  transform: translateX(100%); transition: transform 0.2s ease;
}
.lib-drawer.open { transform: translateX(0); }
.lib-drawer-bar {
  display: flex; align-items: center; justify-content: space-between;
  padding: 0.7rem 0.5rem 0.7rem 0.9rem; border-bottom: 1px solid var(--border);
}
.lib-drawer-title { font-size: 0.85rem; font-weight: bold; margin: 0; }
.lib-drawer-close {
  background: none; border: none; color: var(--muted); font-size: 0.9rem; cursor: pointer; padding: 0.2rem 0.4rem;
}
.lib-drawer-close:hover { color: var(--ink); }
.lib-drawer-list { flex: 0 1 auto; min-height: 0; overflow-y: auto; padding: 0.5rem 0.8rem 1rem; }
.lib-drawer-empty { color: var(--muted); font-size: 0.85rem; line-height: 1.55; padding: 0.8rem 0.2rem; }
.lib-anno-group {
  font-size: 0.63rem; text-transform: uppercase; letter-spacing: 0.07em;
  color: var(--muted); margin: 1rem 0 0.45rem;
}
.lib-anno {
  border: 1px solid var(--border); border-radius: 9px; background: var(--log-bg);
  padding: 0.5rem 0.6rem; margin-bottom: 0.55rem;
}
.lib-anno-bm { display: flex; align-items: center; justify-content: space-between; }
.lib-anno-head { display: flex; align-items: flex-start; gap: 0.4rem; }
.lib-anno-jump {
  flex: 1; min-width: 0; text-align: left; background: none; border: none; font: inherit;
  color: var(--ink); cursor: pointer; display: flex; gap: 0.45rem; padding: 0; line-height: 1.4;
}
.lib-anno-jump:hover .lib-anno-quote { text-decoration: underline; }
.lib-anno-star { color: #c0592b; flex: none; }
.lib-anno-sw { flex: none; width: 0.75rem; height: 1rem; border-radius: 3px; margin-top: 0.15rem; border: 1px solid rgba(0, 0, 0, 0.2); }
.lib-anno-sw[data-color="amber"] { background: #e0b34d; }
.lib-anno-sw[data-color="green"] { background: #7faa72; }
.lib-anno-sw[data-color="rose"] { background: #cf7a86; }
.lib-anno-quote { font-size: 0.82rem; }
.lib-anno-del {
  flex: none; background: none; border: none; color: var(--muted); cursor: pointer;
  font-size: 0.72rem; padding: 0 0.2rem;
}
.lib-anno-del:hover { color: #c0592b; }
.lib-anno-sec { font-size: 0.66rem; color: var(--muted); margin: 0.3rem 0 0.45rem 1.2rem; }
.lib-anno-note {
  width: 100%; box-sizing: border-box; font: inherit; font-size: 0.8rem; line-height: 1.4;
  border: 1px solid var(--border); border-radius: 6px; background: var(--panel); color: var(--ink);
  padding: 0.35rem 0.45rem; resize: vertical; min-height: 2.3rem;
}
.lib-anno-note:focus { outline: none; border-color: var(--focus); }
/* a highlight that carries a note gets a subtle dotted underline cue */
mark.lib-hl[data-noted] { text-decoration: underline dotted; text-underline-offset: 3px; text-decoration-thickness: 1px; }

/* drawer controls: source filter + organize dropdown */
.lib-drawer-ctrls {
  display: flex; gap: 0.5rem; padding: 0.1rem 0.1rem 0.6rem; position: sticky; top: 0;
  background: var(--panel); z-index: 1;
}
.lib-ctrl {
  flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 0.15rem;
  font-size: 0.58rem; text-transform: uppercase; letter-spacing: 0.06em; color: var(--muted);
}
.lib-ctrl select {
  font: inherit; font-size: 0.76rem; text-transform: none; letter-spacing: 0;
  background: var(--log-bg); color: var(--ink); border: 1px solid var(--border);
  border-radius: 6px; padding: 0.2rem 0.35rem; cursor: pointer; width: 100%;
}
.lib-anno-boxes { display: flex; flex-direction: column; }
/* the source · page · section line at the top of each box */
.lib-anno-meta {
  display: flex; align-items: center; gap: 0.4rem; flex-wrap: wrap;
  font-size: 0.64rem; color: var(--muted); margin-bottom: 0.35rem;
}
.lib-anno-src { color: var(--accent); font-weight: 600; }
.lib-anno-pg { font-variant-numeric: tabular-nums; }
.lib-anno-src::after, .lib-anno-pg::after { content: '·'; margin-left: 0.4rem; color: var(--border); }
.lib-anno-secname { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; flex: 1; }
.lib-anno-meta .lib-anno-del { margin-left: auto; }
.lib-anno-meta .lib-anno-sw, .lib-anno-meta .lib-anno-star { margin-top: 0; }
/* drag-to-reorder (custom mode) */
.lib-anno-grip { cursor: grab; color: var(--muted); flex: none; letter-spacing: -0.15em; }
.lib-anno-boxes.dragmode .lib-anno { cursor: grab; }
.lib-anno.lib-dragging { opacity: 0.5; border-style: dashed; border-color: var(--accent); }

/* floating highlight toolbar (on selection) + per-highlight popover */
.lib-hltool, .lib-hlpop {
  position: fixed; z-index: 60; display: none; align-items: center; gap: 0.4rem;
  background: var(--panel); border: 1px solid var(--border);
  box-shadow: 0 6px 20px rgba(0, 0, 0, 0.35); padding: 0.35rem 0.5rem;
}
.lib-hltool { transform: translate(-50%, -100%); border-radius: 999px; }
.lib-hlpop { border-radius: 9px; }
.lib-hltool.show, .lib-hlpop.show { display: inline-flex; }
.lib-hlsw {
  width: 1.15rem; height: 1.15rem; border-radius: 50%; cursor: pointer;
  border: 1px solid rgba(0, 0, 0, 0.25); padding: 0; transition: transform 0.1s;
}
.lib-hlsw:hover { transform: scale(1.15); }
.lib-hlsw[data-color="amber"] { background: #e0b34d; }
.lib-hlsw[data-color="green"] { background: #7faa72; }
.lib-hlsw[data-color="rose"] { background: #cf7a86; }
.lib-hlnote, .lib-hlrm {
  font: inherit; font-size: 0.75rem; background: none; border: none; cursor: pointer;
  color: var(--muted); padding: 0.1rem 0.35rem 0.1rem 0.5rem; border-left: 1px solid var(--border);
  white-space: nowrap;
}
.lib-hlnote:hover { color: var(--ink); }
.lib-hlrm:hover { color: #c0592b; }

@media (max-width: 48rem) {
  .lib-layout,
  .lib-layout.shelf-collapsed { grid-template-columns: 1fr; }
  .lib-shelf { border-right: none; border-bottom: 1px solid var(--border); }
  .lib-shelf-list { max-height: 8rem; }
  .lib-nav { max-height: none; border-right: none; border-bottom: 1px solid var(--border); }
  .lib-reader { padding: 1.6rem 1.1rem 2rem; }
  .lib-layout.drawer-open .lib-reader { padding-right: 1.1rem; } /* drawer overlays on small screens */
}
.dts-eyedrop { width: 1.9rem; height: 1.7rem; padding: 0 !important; font-size: 1rem; }
.dts-bi { width: 1.9rem; height: 1.7rem; padding: 0 !important; }
.dts-bi[aria-pressed="true"] { background: var(--accent) !important; color: var(--accent-ink) !important; }
.dts-inspector input[type="range"] { width: 100%; margin-top: 0.25rem; }

/* ---- the program dashboards (owner design, July 28–29; js/dashboard.js) ----
   One dashboard per program on the new-tab page, stacked last-opened first.
   Each wears its own program's signature: ᏗᎪᏣᎳᎩᏍᎩ the transcript's green
   header, ᏗᏥᎶᏍᏙᏗ the workshop's neutral band. A library's key colour is the
   speaker-dye rotation; the shared ᏗᏍᏓᏅᏅᏙᏗ shelf keeps its own walnut so a
   compiled item never reads as one program's own. */
/* The first page uses the WINDOW, not the reading column (owner ruling,
   July 30: "we have a lot of space — no need of ellipses"). The 44rem .panel
   cap is for prose pages; while the dashboards are up, the panel widens. */
/* :not(.loaded) is load-bearing: once a recording opens, only the dashboards'
   PARENT (#tr-newtab) is hidden — #tr-dashboards itself stays un-hidden, and
   this rule's two ids would out-rank .loaded's max-width:none and cap the
   open transcript at 88rem (found live, July 30). */
#panel-transcript:not(.loaded):has(#tr-dashboards:not([hidden])) { max-width: 88rem; }
/* First-paint guard: while the boot re-detects a remembered Libraries home,
   the new-tab page holds back everything except the dashboards' host —
   otherwise the old open screen paints first and the dashboards visibly
   replace it (owner report, July 30). The class is set by the inline script
   in <head> and dropped by renderProgramDashboards once the real state is
   known, so whichever blocks that state wants reappear together. */
html.tr-await-dashboards #tr-newtab > :not(#tr-dashboards) { display: none; }
#tr-dashboards { display: flex; flex-direction: column; gap: 0.8rem; margin: 1rem 0; }
/* connected: the one-line home row that replaces the Connect button */
.tr-homeline { display: flex; align-items: center; gap: 0.55rem; flex-wrap: wrap;
  font-size: 0.85rem; color: var(--muted); margin: 0.4rem 0 0; }
.tr-homeline b { color: var(--ink); font-weight: 600; }
/* No overflow:hidden here — it cropped every ⓘ bubble inside the card to a
   sliver (owner report, Aug 13). The head rounds its own top corners instead. */
.trd-dash { border: 1px solid var(--border); border-radius: 8px;
  --trd-shared: #5a4526; }
@media (prefers-color-scheme: dark) { .trd-dash { --trd-shared: #b09a6a; } }
.trd-head { display: flex; align-items: center; gap: 0.4rem 0.6rem; flex-wrap: wrap;
  padding: 0.3rem 0.7rem; border-radius: 7px 7px 0 0; }
.trd-name { font-size: 1rem; }
.trd-en, .trd-when { color: var(--muted); font-size: 0.75rem; }
.trd-spacer { flex: 1; min-width: 0.4rem; }
.trd-transcripts { border-left: 3px solid var(--accent); }
.trd-transcripts .trd-head { background: var(--accent); color: var(--accent-ink); }
.trd-transcripts .trd-head .trd-en,
.trd-transcripts .trd-head .trd-when { color: var(--accent-ink); opacity: 0.75; }
.trd-ditsilosdodi { border-left: 3px solid var(--trd-shared); }
.trd-ditsilosdodi .trd-head { background: var(--log-bg); border-bottom: 1px solid var(--border); }

.trd-libbar { display: flex; align-items: center; gap: 0.35rem; flex-wrap: wrap;
  padding: 0.3rem 0.7rem; border-bottom: 1px solid var(--border); font-size: 0.78rem; }
.trd-chip { display: inline-flex; align-items: center; gap: 0.3rem;
  padding: 0.05rem 0.55rem; border-radius: 999px;
  border: 1px solid var(--border); background: var(--panel); }
.trd-chip.shared { border-style: dashed; border-color: var(--trd-shared); color: var(--trd-shared); }
.trd-key { width: 0.42rem; height: 0.42rem; border-radius: 50%;
  background: var(--k, var(--trd-shared)); flex: none; }
.trd-chip .trd-n { color: var(--muted); font-size: 0.72rem; }
.trd-sep { width: 1px; align-self: stretch; background: var(--border); margin: 0 0.15rem; }
.trd-add { font: inherit; font-size: 0.75rem; padding: 0.1rem 0.55rem; border-radius: 999px;
  border: 1px dashed var(--muted); background: transparent; color: var(--muted); cursor: pointer; }
.trd-add:hover { color: var(--ink); border-color: var(--ink); }

.trd-body { display: grid; grid-template-columns: 1.35fr 1fr; gap: 0 0.8rem;
  padding: 0.45rem 0.7rem 0.55rem; }
@media (max-width: 56rem) { .trd-body { grid-template-columns: 1fr; gap: 0.6rem; } }
.trd-cap { font-size: 0.62rem; color: var(--muted); text-transform: uppercase;
  letter-spacing: 0.09em; display: flex; align-items: center; gap: 0.35rem;
  margin-bottom: 0.25rem; }

.trd-cards { display: grid; grid-template-columns: repeat(auto-fill, minmax(17rem, 1fr));
  gap: 0.3rem; }
/* The dyes are DARK natural pigments — walnut, pokeberry — readable as text on
   the light theme, invisible on the dark one (owner report, July 29). So text
   never wears the dye: the subhead reads in --muted, and only the key dot and
   the card's left border carry the colour — lightened toward the ink in the
   dark theme, the same move the mock's dark palette made by hand. */
.trd-subhead { grid-column: 1 / -1; font-size: 0.6rem; color: var(--muted);
  text-transform: uppercase; letter-spacing: 0.07em; margin: 0.35rem 0 0.1rem;
  display: flex; align-items: center; gap: 0.3rem; }
.trd-subhead:first-child { margin-top: 0; }
/* the shared shelf's own colour is theme-aware already, so its subhead may
   keep it — walnut ink light, pale walnut dark */
.trd-subhead.shared { border-top: 1px dashed var(--border); padding-top: 0.35rem;
  color: var(--trd-shared); }
.trd-zone { font-size: 0.9em; opacity: 0.55; text-transform: none; letter-spacing: 0; }
.trd-card { display: grid; grid-template-columns: auto 1fr; gap: 0 0.45rem;
  align-items: center; padding: 0.32rem 0.5rem; cursor: pointer; text-align: left;
  font: inherit; color: var(--ink); background: var(--bg);
  border: 1px solid var(--border); border-radius: 8px;
  border-left: 3px solid var(--k, var(--trd-shared)); }
.trd-card:hover { background: color-mix(in srgb, var(--accent) 10%, var(--bg)); }
.trd-card.shared { border-left-style: dotted; }
.trd-card.muted { opacity: 0.72; border-style: dashed;
  border-left-style: dashed; cursor: pointer; }
.trd-card:disabled { cursor: default; }
.trd-card-txt { display: block; min-width: 0; }
/* titles and speakers WRAP, never clip (owner ruling, July 30) — a long
   historical name earns its second line */
.trd-card-t1 { display: block; font-size: 0.85rem; line-height: 1.3; }
.trd-card-en { color: var(--muted); font-size: 0.76rem; }
.trd-card-t2 { display: block; font-size: 0.75rem; color: var(--muted); line-height: 1.3; }
.trd-card-t3 { display: flex; gap: 0.4rem; align-items: baseline;
  font-size: 0.68rem; color: var(--muted); }
/* the header's .tr-draftbadge red, sized down to sit in the card's meta row */
.trd-card-draft { color: #ffffff; background: #c0331b; border-radius: 4px;
  padding: 0 0.3rem; text-transform: uppercase; letter-spacing: 0.04em;
  font-size: 0.9em; white-space: nowrap; }
.trd-card-when { margin-left: auto; font-family: ui-monospace, Menlo, monospace;
  font-size: 0.95em; white-space: nowrap; }
.trd-overflow { grid-column: 1 / -1; font-size: 0.7rem; color: var(--muted);
  padding: 0.1rem 0.5rem; }

.trd-feed { display: flex; flex-direction: column; }
.trd-feed-coll { font-size: 0.6rem; color: var(--muted); text-transform: uppercase;
  letter-spacing: 0.07em; margin: 0.3rem 0 0.1rem; }
.trd-feed-coll:first-child { margin-top: 0; }
.trd-ev { display: grid; grid-template-columns: 1fr auto; align-items: baseline;
  gap: 0 0.45rem; padding: 0.15rem 0.15rem; border-bottom: 1px solid var(--border); }
.trd-ev:last-child { border-bottom: none; }
/* feed text WRAPS rather than clipping — with the widened panel an ellipsis
   would only ever hide words the page has room for (owner ruling, July 30) */
.trd-ev-txt { min-width: 0; font-size: 0.78rem; line-height: 1.35; }
.trd-ev-where { color: var(--muted); font-size: 0.9em; }
.trd-ev-t { color: var(--muted); font-size: 0.66rem;
  font-family: ui-monospace, Menlo, monospace; white-space: nowrap; }
.trd-feed-empty { color: var(--muted); font-size: 0.78rem; margin: 0.2rem 0; }
.trd-more { font: inherit; font-size: 0.72rem; color: var(--muted); background: none;
  border: none; padding: 0.35rem 0.15rem 0; cursor: pointer; text-align: left; }
.trd-more:hover { color: var(--ink); }
/* the program's own gestures ride the dashboard foot as compact chips — the
   moved-in button keeps its identity (and listener), sheds its long caption */
.trd-foot { display: flex; align-items: center; gap: 0.7rem; flex-wrap: wrap; }
.trd-gestures .tr-opt { padding: 0.2rem 0.7rem; font-size: 0.74rem;
  border-radius: 999px; }
.trd-gestures .tr-opt-d { display: none; }

.trd-empty { display: flex; align-items: center; gap: 0.6rem; flex-wrap: wrap;
  padding: 0.55rem 0.7rem 0.65rem; }
.trd-say { font-size: 0.8rem; color: var(--muted); margin: 0; flex: 1;
  min-width: 14rem; line-height: 1.4; }
.trd-bigadd { font: inherit; font-size: 0.82rem; padding: 0.25rem 0.8rem;
  border-radius: 999px; border: 1px solid var(--focus); background: var(--focus);
  color: #f6f0e6; cursor: pointer; white-space: nowrap; }
.trd-bigadd:hover { background: transparent; color: var(--focus); }

/* ⓘ — the thin ring with a hover bubble (owner convention) */
.trd-info { position: relative; display: inline-flex; align-items: center;
  justify-content: center; width: 0.9rem; height: 0.9rem;
  border: 1px solid currentColor; border-radius: 50%; font-size: 0.58rem;
  font-family: Georgia, serif; font-style: italic; cursor: help; flex: none;
  opacity: 0.6; }
.trd-info:hover { opacity: 1; }
.trd-bub { position: absolute; bottom: 140%; left: 50%; transform: translateX(-50%);
  width: 17rem; background: var(--panel); color: var(--ink);
  border: 1px solid var(--border); border-radius: 7px; padding: 0.4rem 0.6rem;
  font-size: 0.72rem; font-style: normal; line-height: 1.45; text-align: left;
  box-shadow: 0 3px 10px rgba(0, 0, 0, 0.2); opacity: 0; visibility: hidden;
  z-index: 40; text-transform: none; letter-spacing: 0; }
.trd-info:hover .trd-bub { opacity: 1; visibility: visible; }
/* An ⓘ at the END of its row (after ＋ Add library…) hangs its bubble from
   the right edge — centered, half of it would fall past the card and the
   window (owner report, Aug 13). */
.trd-libbar .trd-info:last-child .trd-bub,
.trd-empty .trd-info:last-child .trd-bub {
  left: auto; right: -0.4rem; transform: none; }

/* The Add-library chooser — a native <dialog> wearing the panel's clothes.
   Its two choices are the ordinary open-option buttons (.tr-opt), so the
   modal reads like the rest of the open screen, not like a browser box. */
.tr-addlib-choice { background: var(--panel); color: var(--ink);
  border: 1px solid var(--border); border-radius: 8px;
  padding: 0.8rem 1rem 0.7rem; max-width: 30rem; }
.tr-addlib-choice::backdrop { background: rgba(0, 0, 0, 0.35); }
.tr-addlib-title { margin: 0 0 0.6rem; font-size: 0.95rem; font-weight: 600; }
.tr-addlib-cancel { margin-top: 0.6rem; }

/* Dark theme: the dye stays on the dot and the card edge, lightened toward
   the ink — the raw pigments are too dark to read against the dark ground.
   This block sits AFTER the .trd-card base rule on purpose: its border-left
   shorthand would otherwise re-assert the raw dye (found July 29). */
@media (prefers-color-scheme: dark) {
  .trd-key { background: color-mix(in srgb, var(--k, var(--trd-shared)) 55%, var(--ink)); }
  .trd-card { border-left-color: color-mix(in srgb, var(--k, var(--trd-shared)) 55%, var(--ink)); }
}
