/* =====================================================================
   The annotation desk (#/sample)
   =====================================================================
   One screen, three regions, and the reading order is the working order:
   the words at the top (what is being said), the waveform under them (when
   it was said), and the breath list beside both (what has been decided so
   far). The rail is the only part that scrolls independently, because the
   waveform must never leave the screen while a span is being dragged.

   The waveform itself runs right to left, like the قرآن it is timing: time
   0 sits at the canvas's right edge, the playhead moves toward the left as
   playback advances, and the minimap agrees (see the colour-policy comment
   at the top of annotator.js). Individual timecodes - "۰:۰۱٫۲۹ – ۰:۰۳٫۲۸",
   the numeric start/end fields - stay Latin-digit and left-to-right inside
   that, because a number is read the same way regardless of which way the
   ruler it came from runs. */

/* This screen is an editor, not a document, so it is laid out like one: the
   working surface fills the window and does not scroll, and the one column
   that holds a list - the rail - scrolls inside itself. The waveform used to
   sit in a page that simply grew, so on a 1280x800 window the canvas ran to
   y=908 and the selection bar to y=1009: you scrolled the player away from
   the words to see the controls under it, and scrolled back to see the wave.

   The escape hatch matters as much as the rule. Below `min-height` the body
   stops shrinking and the page scrolls as a whole, so a short window degrades
   into an ordinary scrolling page rather than into a squashed, unusable one. */
.ann-page { display: grid; gap: 8px; padding-bottom: 10px; }
/* Trimmed: this bar carries a filename, a status word and four buttons, and
   at 65px it was spending a tenth of a laptop's height doing it. */
.ann-bar {
  display: flex; align-items: center; gap: 10px; flex-wrap: wrap;
  padding: 6px 12px; border: 1px solid var(--border); border-radius: var(--radius);
  background: var(--surface); box-shadow: var(--shadow);
  position: sticky; top: 0; z-index: 5;
}
.ann-bar .icon-btn { width: 30px; height: 30px; }
.ann-bar .btn { padding: 5px 11px; }
.ann-bar-title { display: grid; min-width: 0; flex: 1; }
.ann-bar-title h2 { margin: 0; font-size: 0.92rem; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* The subtitle is a reminder of what the screen is, which is worth a line on
   a tall window and is the first thing to go on a short one. */
.ann-bar-sub { font-size: 0.72rem; color: var(--text-muted); }
@media (max-height: 820px) { .ann-bar-sub { display: none; } }

/* One word for the state of the work, in the one place the eye already is.
   Colour alone never carries it - the text says which state it is, because
   "is this saved" is not a question to answer by remembering a hue. */
.ann-status {
  flex: none; font-size: 0.74rem; padding: 3px 10px; border-radius: 999px;
  border: 1px solid var(--border-strong); color: var(--text-muted); background: var(--surface-2);
}
.ann-status.is-clean { color: var(--accent); border-color: var(--accent); background: var(--accent-soft); }
/* A stereo file with one side silent - the door to views/channels.js. */
.ann-channel-warn {
  flex: none; display: inline-flex; align-items: center; gap: 5px; cursor: pointer;
  font: inherit; font-size: 0.74rem; padding: 3px 10px; border-radius: 999px;
  border: 1px solid var(--danger); color: var(--danger); background: var(--danger-soft);
}
.ann-channel-warn[hidden] { display: none; }
.ann-channel-warn .ic { width: 13px; height: 13px; }
.ann-channel-warn:focus-visible { outline: 2px solid var(--danger); outline-offset: 1px; }
.ann-status.is-dirty { color: var(--gold); border-color: var(--gold); background: var(--gold-soft); }
.ann-status.is-bad { color: var(--danger); border-color: var(--danger); background: var(--danger-soft); }

.ann-banner {
  display: flex; align-items: center; gap: 12px; flex-wrap: wrap;
  padding: 10px 14px; border-radius: var(--radius-sm);
  background: var(--gold-soft); border: 1px solid var(--gold); color: var(--text);
}
.ann-banner > div { flex: 1; min-width: 200px; font-size: 0.85rem; }
/* An explicit `display` on the element itself outranks the user agent's
   `[hidden] { display: none }`, so a `hidden` banner stayed on screen as an
   empty gold bar. Every box here that both sets a display and is toggled
   with `hidden` needs this line. */
.ann-banner[hidden], .ann-problems[hidden] { display: none; }

/* One screen's worth, and no more. The subtraction is the chrome above and
   below: the shell's top bar, this view's padding, the trimmed .ann-bar and
   the page gaps - plus the now-playing bar when one is docked, which is the
   same variable .panel-sticky already budgets against. */
.ann-body {
  display: grid; grid-template-columns: minmax(0, 1fr) 10px minmax(260px, var(--ann-rail-width, 320px));
  gap: 4px; align-items: stretch;
  height: calc(100dvh - 168px - var(--studio-player-height, 0px));
  min-height: 430px;
}
/* Tag-qualified on purpose. This is the card that wraps the transport, the
   tools and the canvases; its padding and border must never reach a
   canvas. They did once - the waveform canvas was also class "ann-stage",
   and since a canvas paints its bitmap into its content box, the 14px of
   padding shrank and shifted everything the stage drew relative to the
   `live` layer sitting on top of it. The canvas is `ann-stage-canvas` now
   (see annotator.js), and this selector is narrowed so the same mistake
   cannot be made twice. */
/* The rows of the working surface. Only the canvas grows: the transport,
   the tools and the selection bar are their own height, and whatever is
   left over is waveform - the row that actually benefits from more pixels.
   (A word strip sat above the transport until the mushaf took over its job;
   its row was a fixed height that reflowed whenever a word was timed.) */
section.ann-stage {
  min-width: 0; min-height: 0; display: grid; gap: 10px;
  grid-template-rows: min-content min-content minmax(min-content, 1fr) min-content min-content min-content;
  overflow-y: auto; scrollbar-gutter: stable;
  background: var(--surface); border: 1px solid var(--border);
  border-radius: var(--radius); padding: 12px; box-shadow: var(--shadow);
}

/* ---- transport ---- */

.ann-transport {
  display: flex; align-items: center; gap: 12px; flex-wrap: wrap;
  padding: 8px 10px; border-radius: var(--radius-sm); background: var(--surface-2);
}
.ann-clock { font-variant-numeric: tabular-nums; color: var(--accent); direction: ltr; font-size: 0.86rem; }
/* Neutral until something is actually happening (see .ann-play.is-on below) -
   a transport button that is always the "go" colour stops meaning anything,
   which is exactly the "highlighted for no reason" complaint this button used
   to cause by carrying `--accent` unconditionally. Three classes on the rule
   itself, not two: `.icon-btn.is-on` (the generic rail-button "on" style,
   defined later in this file) ties with a two-class selector here and wins on
   source order, which is exactly how the old always-green background went
   unnoticed - the override looked applied in the markup and silently lost the
   cascade. */
.icon-btn.ann-play.is-on { background: var(--accent); color: var(--on-accent); border-color: var(--accent); }
.ann-rate { display: inline-flex; align-items: center; gap: 6px; font-size: 0.76rem; color: var(--text-muted); }
.ann-rate .field { width: auto; padding: 4px 8px; }

/* ---- review / edit ----
   Which job a word press is doing. A segmented pair rather than one toggle:
   both states are named, so neither has to be inferred from the other being
   off. Edit is the one that writes, so it is the one that gets the colour -
   and green here means the same thing it means everywhere else on this
   screen, "this control is currently doing something". */
.ann-mode {
  display: inline-flex; padding: 2px; gap: 2px; border-radius: 999px;
  background: var(--surface-3); border: 1px solid var(--border);
}
.ann-mode-btn {
  display: inline-flex; align-items: center; gap: 5px; cursor: pointer;
  border: none; background: none; color: var(--text-muted);
  font: inherit; font-size: 0.76rem; padding: 4px 11px; border-radius: 999px;
}
.ann-mode-btn .ic { width: 14px; height: 14px; }
.ann-mode-btn:hover { color: var(--text); }
.ann-mode-btn.is-on { background: var(--surface); color: var(--text); box-shadow: var(--shadow); }
.ann-mode-btn[aria-checked="true"]:last-child { color: var(--accent-strong); }
.ann-mode-btn:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }

/* ---- the canvas ---- */

.ann-canvas-wrap { direction: ltr; min-height: 0; display: flex; }
.ann-canvas-wrap > .ann-wrap { flex: 1; min-height: 0; }
.ann-wrap { display: grid; grid-template-rows: auto minmax(0, 1fr); gap: 6px; }
/* The whole file, small enough to see all of it at once - dragging its
   highlighted window pans the stage below without changing its zoom;
   clicking anywhere else on it jumps there outright. See annotator.js.
   Both this and `.ann` below draw their edge with an inset box-shadow
   rather than a `border`: a real border consumes a pixel of the box, so the
   `inset: 0` canvas inside it stops exactly *short* of that pixel - a
   permanent dead strip along the one edge a resize handle most needs to be
   grabbable right up to. A box-shadow paints the same line without taking
   any box space, so the canvas covers the whole visual rectangle, border
   included. */
.ann-minimap {
  position: relative; height: 28px; overflow: hidden; touch-action: none;
  box-shadow: inset 0 0 0 1px var(--border-strong); border-radius: var(--radius-sm);
  background: var(--surface-2); cursor: pointer;
}
.ann {
  /* Height comes from the row it is given, not from a fixed 260px that had
     to be scrolled to. `resize` stays for anyone who wants more wave than
     the automatic split gives them. */
  position: relative; height: auto; min-height: 120px; resize: vertical; overflow: hidden;
  box-shadow: inset 0 0 0 1px var(--border-strong); border-radius: var(--radius-sm);
  background: var(--surface-2); touch-action: none; outline: none;
}
.ann:focus-visible { box-shadow: inset 0 0 0 1px var(--accent), 0 0 0 3px var(--accent-soft); }
.ann-canvas { position: absolute; inset: 0; width: 100%; height: 100%; display: block; }
.ann-live { cursor: crosshair; }
.ann-audio { display: none; }
/* On and off, one look everywhere: a channel heard or muted beside its
   lane, an effect in or bypassed in the rack (and all of them at once). */
.onoff {
  display: inline-flex; align-items: center; justify-content: center; gap: 3px;
  border: 1px solid var(--border-strong); border-radius: 999px; background: var(--surface);
  color: var(--text); font: 600 0.68rem var(--mono); padding: 1px 6px; cursor: pointer; height: 22px;
}
.onoff .ic { width: 13px; height: 13px; }
.onoff[aria-pressed="true"] { border-color: var(--accent); color: var(--accent-strong); background: var(--accent-soft); }
.onoff[aria-pressed="false"] { color: var(--text-muted); text-decoration: line-through; }
.onoff:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
.ann-channels { position: absolute; inset: 0; pointer-events: none; }
.ann-channel { position: absolute; pointer-events: auto; }

/* ---- selection bar ---- */

/* Blue, not gold: this bar reads back a *choice* (the dragged span), never a
   sound - see the colour policy at the top of annotator.js. */
.ann-selection { display: grid; gap: 8px; padding: 10px 12px; border-radius: var(--radius-sm); background: var(--band-blue-soft); }
.ann-selection-read { display: flex; align-items: baseline; gap: 10px; flex-wrap: wrap; font-size: 0.85rem; }
.ann-selection-read .mono { direction: ltr; font-variant-numeric: tabular-nums; }
/* What the next word press will do. Quiet while it is only describing
   review, emphatic once a press would actually write something - the one
   state where knowing beforehand matters. */
.ann-armed { margin-inline-start: auto; font-size: 0.76rem; color: var(--text-muted); }
.ann-armed.is-armed { color: var(--accent-strong); font-weight: 650; }
.ann-tap { display: flex; align-items: center; gap: 8px; font-size: 0.82rem; color: var(--accent-strong); }

/* ---- problems ---- */

/* Capped: a pass with twenty overlaps must not push the canvas off the stage. */
.ann-problems { border: 1px solid var(--danger); background: var(--danger-soft); border-radius: var(--radius-sm); padding: 10px 14px; max-height: 9.5rem; overflow: auto; }
.ann-problems h4 { display: flex; align-items: center; gap: 6px; margin: 0 0 6px; font-size: 0.85rem; color: var(--danger); }
.ann-problems ul { margin: 0; padding-inline-start: 18px; display: grid; gap: 4px; }
.ann-problems .link { background: none; border: none; padding: 0; color: var(--danger); text-align: start; cursor: pointer; text-decoration: underline; font: inherit; }

/* ---- the rail ---- */

/* The one column that scrolls. It is the list of breaths and the detail for
   the selected one - genuinely unbounded content - so it gets its own
   scrollbar inside a body that does not scroll. */
.ann-rail { display: grid; gap: 10px; align-content: start; min-height: 0; height: 100%; overflow-y: auto; overscroll-behavior: contain; }
.ann-breaths, .ann-detail {
  background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius);
  padding: 12px; box-shadow: var(--shadow);
}
.ann-rail-head { display: flex; align-items: center; justify-content: space-between; gap: 8px; margin-bottom: 8px; }
.ann-rail-head h3 { margin: 0; font-size: 0.92rem; }
.ann-breath-list { display: grid; gap: 6px; }
/* A row, not a single button: the row selects, but the play action beside it
   has to be its own control, or "audition this one breath" would need a
   select first - exactly the two-step play the sample panel used to force
   for every item. */
.ann-breath {
  display: flex; align-items: center; gap: 6px;
  border: 1px solid var(--border); border-radius: var(--radius-sm);
  background: var(--surface-2); color: var(--text); padding: 4px 4px 4px 10px;
}
.ann-breath:hover { border-color: var(--border-strong); }
.ann-breath.is-on { border-color: var(--band-blue); background: var(--band-blue-soft); }
.ann-breath-main { display: grid; gap: 2px; flex: 1; min-width: 0; }
.ann-breath-pick {
  display: grid; gap: 2px; text-align: start; cursor: pointer;
  background: none; border: none; color: inherit; font: inherit; padding: 3px 0 0;
}
.ann-breath-name { font-size: 0.84rem; font-weight: 600; }
.ann-breath-time { font-size: 0.72rem; color: var(--text-muted); direction: ltr; font-variant-numeric: tabular-nums; }
.ann-breath-meta { display: flex; align-items: center; gap: 4px; flex-wrap: wrap; font-size: 0.72rem; color: var(--text-muted); }
.ann-breath-actions { display: flex; align-items: center; gap: 2px; flex: none; }
/* The nudge toward assigning an ayah: plain text everywhere else, a pressable
   chip only when there is nothing assigned yet - so the one breath that needs
   the click is the one offering it. */
.ann-breath-assign {
  border: 1px dashed var(--gold); border-radius: 999px; background: none;
  color: var(--gold); font: inherit; font-size: 0.72rem; padding: 1px 8px; cursor: pointer;
}
.ann-breath-assign:hover { background: var(--gold-soft); }

.ann-fields { display: flex; gap: 8px; flex-wrap: wrap; }
.ann-field { display: grid; gap: 4px; font-size: 0.72rem; color: var(--text-muted); }
.ann-field .field { width: 110px; direction: ltr; font-variant-numeric: tabular-nums; }
.ann-assign { margin-top: 12px; border-radius: var(--radius-sm); transition: background-color 0.3s ease; }
/* The beat of emphasis a "+ آیه" press asks for - see focusAyahAssignment in
   sample.js. Transitions back out on its own; nothing here needs cleanup. */
.ann-assign.is-flagged { background: var(--gold-soft); box-shadow: 0 0 0 2px var(--gold); }
.ann-assign h4, .ann-events h4 { margin: 0 0 6px; font-size: 0.8rem; }
.ann-events { margin-top: 12px; }
.ann-event-list { display: grid; gap: 4px; }
.ann-event {
  display: flex; align-items: center; gap: 8px; text-align: start; cursor: pointer;
  border: 1px solid var(--border); border-radius: 8px; background: var(--surface-2);
  color: var(--text); padding: 5px 8px; font-size: 0.76rem;
}
.ann-event.is-on { border-color: var(--band-blue); background: var(--band-blue-soft); }
.ann-event-pick { display: flex; align-items: center; gap: 8px; flex: 1; min-width: 0; text-align: start; cursor: pointer; background: none; border: none; color: inherit; font: inherit; padding: 0; }
.ann-event .mono { margin-inline-start: auto; direction: ltr; font-size: 0.7rem; color: var(--text-muted); }
.ann-field-row { display: flex; align-items: end; gap: 6px; }

/* On this screen the rail is the scroller, not the panel.
   `.panel-body` is `overflow: auto; overscroll-behavior: contain` because on
   the library screen it really is the thing that scrolls. Here the panel has
   no max-height, so the body never overflows - and a non-overflowing scroll
   container with `overscroll-behavior: contain` still *consumes* the wheel
   rather than chaining it to an ancestor. The pane sat there absorbing every
   scroll and moving nothing while the rail underneath it had 850px of
   content left to show. Handing the wheel back is the whole fix. */
.ann-side .panel { position: static; width: auto; max-height: none; }
.ann-side .panel-head[hidden] { display: none; }
.ann-split-handle { border-radius: 4px; background: var(--border); touch-action: none; }
.ann-split-handle:hover, .ann-split-handle:focus-visible { background: var(--accent); outline: 2px solid var(--accent); }
.ann-split-handle.is-horizontal { cursor: row-resize; }
.ann-split-handle.is-vertical { cursor: col-resize; }
.ann-side .ann-breaths, .ann-side .ann-detail { resize: vertical; overflow: auto; min-height: 100px; max-height: 65vh; }
.ann-side .panel-body { overflow: visible; overscroll-behavior: auto; }
.ann-empty { margin: 0; padding: 10px 0; display: grid; gap: 8px; justify-items: start; }
.ann-empty p { margin: 0; }
.btn-tiny { padding: 2px 7px; font-size: 0.7rem; }

/* ---- the telawat editor (views/telawat.js, telawat/editor.js) ----

   The workspace is the person's to arrange (telawat/layout.js): which side
   the rail is on, whether the mushaf is a rail tab or a column of its own. Grid areas rather than DOM order, so moving
   a column never moves - and so never reloads or re-lays-out - the nodes in
   it, except the one frame that genuinely changes parents. */
.tm-page .ann-body { grid-template-areas: "stage handle rail"; grid-template-columns: minmax(0, 1fr) 10px minmax(260px, var(--tm-rail-w)); }
/* The rail's width: its own, or the mushaf's wider one while it shows it. */
.tm-page .ann-body { --tm-rail-w: var(--ann-rail-width, 340px); }
.tm-page[data-side-tab="quran"]:not([data-mushaf="column"]) .ann-body { --tm-rail-w: var(--tm-quran-width, 560px); }
.tm-page .ann-body > .tm-quran-handle { display: none; }
.tm-page[data-side-tab="quran"]:not([data-mushaf="column"]) .ann-body > .tm-quran-handle { display: block; }
.tm-page[data-side-tab="quran"]:not([data-mushaf="column"]) .ann-body > .ann-split-handle.tm-rail-handle:not(.tm-quran-handle) { display: none; }
.tm-page .ann-body > section.ann-stage { grid-area: stage; }
.tm-page .ann-body > .ann-split-handle { grid-area: handle; }
.tm-page .ann-body > .ann-rail { grid-area: rail; }
.tm-page .ann-body > .tm-column { grid-area: column; min-height: 0; display: flex; flex-direction: column; gap: 6px; }
.tm-page .tm-column > .tm-mushaf { flex: 1 1 auto; min-height: 0; }
.tm-column-head { display: flex; align-items: center; gap: 8px; font-size: 0.82rem; }
.tm-column-head .ic { width: 15px; height: 15px; }
.tm-column-head b { margin-inline-end: auto; }
.tm-page .tm-column[hidden] { display: none; }
.tm-page .ann-body > .tm-column-handle { grid-area: chandle; display: none; }
.tm-page[data-mushaf="column"] .ann-body > .tm-column-handle { display: block; }
.tm-page[data-mushaf="column"] .ann-body { grid-template-areas: "stage handle rail chandle column"; grid-template-columns: minmax(0, 1fr) 10px minmax(260px, var(--tm-rail-w)) 10px minmax(280px, var(--tm-column-width, 420px)); }
.tm-page[data-rail-side="start"] .ann-body { grid-template-areas: "rail handle stage"; grid-template-columns: minmax(260px, var(--tm-rail-w)) 10px minmax(0, 1fr); }
.tm-page[data-rail-side="start"][data-mushaf="column"] .ann-body { grid-template-areas: "rail handle stage chandle column"; grid-template-columns: minmax(260px, var(--tm-rail-w)) 10px minmax(0, 1fr) 10px minmax(280px, var(--tm-column-width, 420px)); }
.tm-page[data-rail="false"] .ann-body { grid-template-areas: "stage"; grid-template-columns: minmax(0, 1fr); }
.tm-page[data-rail="false"][data-mushaf="column"] .ann-body { grid-template-areas: "stage chandle column"; grid-template-columns: minmax(0, 1fr) 10px minmax(280px, var(--tm-column-width, 420px)); }
.tm-page[data-rail="false"] .ann-rail, .tm-page[data-rail="false"] .ann-body > .ann-split-handle { display: none; }
.tm-loading { max-width: 720px; margin: 40px auto; }
.tm-kind { font-size: 0.72rem; }


/* The detector's drawer, under the wave. */
.ann-detector[hidden] { display: none; }
.tm-detector .panel-head { flex-wrap: wrap; }

/* The rail: tabs over one scrolling column. */
.tm-tabs { display: flex; gap: 2px; padding: 3px; border-radius: var(--radius-sm); background: var(--surface-3, var(--surface-2)); position: sticky; top: 0; z-index: 2; }
.tm-word-progress { font-size: 0.72rem; }
.tm-tablist { display: flex; gap: 2px; flex: 1; min-width: 0; }
.tm-tab {
  flex: 1; display: inline-flex; align-items: center; justify-content: center; gap: 6px;
  border: none; background: none; color: var(--text-muted); font: inherit; font-size: 0.78rem;
  padding: 6px 8px; border-radius: 7px; cursor: pointer;
}
.tm-tab:hover { color: var(--text); }
.tm-tab.is-on { background: var(--surface); color: var(--text); box-shadow: var(--shadow); font-weight: 650; }
.tm-tab:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }
.tm-tab-count { font: 0.66rem var(--mono); color: var(--text-muted); }
.ann-side[data-tab="quran"] { height: 100%; min-height: 0; display: grid; }
.tm-quran-tab { min-height: 0; display: grid; height: 100%; }

/* A breath's row: what it recites, its maqam, how many words. */
.ann-breath.is-picking { box-shadow: 0 0 0 2px var(--band-blue); }
.ann-breath-meta .ic { width: 12px; height: 12px; }
.tm-meta-ayat { font-family: var(--mono); direction: ltr; color: var(--text); }
.tm-meta-missing { color: var(--gold); }
.tm-meta-maqam { border-radius: 999px; padding: 0 7px; background: color-mix(in oklch, var(--maqam, var(--accent)) 18%, transparent); color: var(--text); }
.tm-meta-maqam.is-inherited { background: none; border: 1px dashed color-mix(in oklch, var(--maqam, var(--accent)) 60%, transparent); }
.tm-ayat, .tm-maqam, .tm-batch-tags { margin-top: 12px; display: grid; gap: 6px; }
.tm-ayat h4, .tm-maqam h4, .tm-batch-tags h4 { display: flex; align-items: center; gap: 6px; margin: 0; font-size: 0.8rem; }
.tm-ayat h4 .ic, .tm-maqam h4 .ic, .tm-batch-tags h4 .ic { width: 14px; height: 14px; }
.tm-ayat-field { direction: ltr; }
.tm-field-error { margin: 0; font-size: 0.74rem; color: var(--danger); }
.tm-field-error[hidden] { display: none; }
.tm-note { font-size: 0.74rem; margin: 4px 0 0; }
.tm-count { font-size: 0.72rem; }
.btn.is-on { border-color: var(--band-blue); background: var(--band-blue-soft); }

/* The mushaf: the course's own reader, in a frame that fills its box. */
/* A column, not a grid: the status line comes and goes, and with it hidden
   a grid's first (auto) row was the frame, sized to its minimum - the page
   took half the column and left the rest empty. */
.tm-mushaf { display: flex; flex-direction: column; min-height: 420px; height: 100%; gap: 8px; }
.tm-mushaf-frame { flex: 1 1 auto; width: 100%; min-height: 380px; border: 1px solid var(--border); border-radius: var(--radius); background: var(--surface); }
.tm-mushaf-status { display: grid; gap: 6px; padding: 10px 12px; border-radius: var(--radius-sm); background: var(--band-blue-soft); border: 1px solid var(--band-blue); font-size: 0.8rem; }
.tm-mushaf-status[hidden] { display: none; }
.tm-mushaf-status-text { display: flex; align-items: center; gap: 6px; flex-wrap: wrap; }
.tm-mushaf-status-text .ic { width: 14px; height: 14px; color: var(--band-blue); }
.tm-mushaf-hint { margin: 0; font-size: 0.72rem; }
.tm-column .tm-mushaf { height: 100%; }

/* Highlights. */
.tm-highlights, .tm-file { display: grid; gap: 10px; }
.tm-highlights { background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius); padding: 12px; box-shadow: var(--shadow); }
.tm-highlight-list { display: grid; gap: 6px; }
.tm-highlight { border: 1px solid var(--border); border-radius: var(--radius-sm); background: var(--surface-2); padding: 4px 6px 4px 10px; }
.tm-highlight.is-on { border-color: var(--band-rose); box-shadow: inset 3px 0 0 var(--band-rose); }
.tm-highlight-head { display: flex; align-items: center; gap: 6px; }
.tm-highlight-head .ann-breath-pick { flex: 1; min-width: 0; }
.tm-highlight-body { display: grid; gap: 6px; padding: 6px 0 4px; }
.seg-badge.is-new { background: var(--surface-3, var(--surface)); color: var(--text-muted); }
.seg-badge.is-published { background: var(--accent-soft); color: var(--accent-strong); }
.seg-badge.is-stale, .seg-badge.is-missing { background: var(--gold-soft); color: var(--gold); }
.seg-badge.is-meta { background: var(--band-blue-soft); color: var(--band-blue); }

/* Export. */
.tm-export .check, .tm-export label.check { display: flex; align-items: center; gap: 8px; font-size: 0.84rem; }
.tm-export-summary .diff { max-height: 180px; overflow: auto; }
label.check { display: flex; align-items: center; gap: 8px; font-size: 0.84rem; }

/* The picker. */
.tm-picker { max-width: 980px; margin: 0 auto; }
.tm-picker-list { max-height: 50vh; overflow: auto; }
.tm-picker-list a.seg { color: inherit; text-decoration: none; }

/* One column, and with it the fixed-height layout goes: stacked, the stage
   and the rail cannot both fit a window, so the page scrolls as a whole -
   which is the right degradation and the one a phone expects. */
@media (max-width: 1080px) {
  .ann-split-handle { display: none; }
  .ann-body, .tm-page .ann-body, .tm-page[data-mushaf="column"] .ann-body, .tm-page[data-rail-side="start"] .ann-body, .tm-page[data-rail-side="start"][data-mushaf="column"] .ann-body { grid-template-columns: 1fr; grid-template-areas: "stage" "rail" "column"; height: auto; min-height: 0; }
  .ann-rail { height: auto; max-height: none; overflow: visible; }
  section.ann-stage { grid-template-rows: none; overflow: visible; }
  .ann { height: 260px; }
}

/* ===== studio.css — the sample studio's whole skin =====

   The palette is the app's, and deliberately so: this tool maintains the
   course's recordings, and a maintainer moving between the two all afternoon
   should not be moving between two visual languages. The emerald ink, the
   rubrication gold reserved for the thing that is happening right now, the
   parchment ground, Vazirmatn — all of it is webapp/base.css's vocabulary.

   The tokens are restated here rather than imported, and that is the one
   deliberate duplication in this folder. studio/ is a development tool that
   is not shipped and not packaged (see studio/README.md); linking a
   stylesheet out of webapp/ would make the folder's independence a lie for
   the sake of forty lines. The typeface itself is *not* duplicated — that is
   a 100KB binary, and serve.mjs mounts webapp/fonts/ instead.

   What is not the app's: the density. A course reader is a document, read at
   arm's length, with generous rhythm. This is a workbench — 567 files, four
   axes of tags, a waveform to read at eighty milliseconds' resolution — so
   the scale is one step tighter throughout and the surfaces carry more
   structure, because structure is what makes a dense screen navigable.
   ======================================================================== */

@font-face {
  font-family: "AmiriQuran";
  src: url("/fonts/AmiriQuran.ttf") format("truetype");
  font-display: swap;
}

@font-face {
  font-family: "Vazirmatn";
  src: url("/fonts/Vazirmatn-Variable.woff2") format("woff2-variations"),
       url("/fonts/Vazirmatn-Variable.woff2") format("woff2");
  font-weight: 100 900;
  font-style: normal;
  font-display: swap;
}

:root {
  color-scheme: light;
  --accent: #0e6b57;
  --accent-strong: #0a4f40;
  --accent-soft: #e2f0ea;
  --gold: #b8873a;
  --gold-soft: #f5ead2;
  --bg: #fbf7ec;
  --surface: #ffffff;
  --surface-2: #f3eee0;
  --surface-3: #ebe4d2;
  --text: #17231d;
  /* 5.3:1 on --surface-3, the darkest surface small muted text sits on
     (the test session measured the old #5e6a5f at 4.47:1 there). */
  --text-muted: #535f54;
  --border: #e3dbc4;
  --border-strong: #cfc4a5;
  --danger: #a52f2f;
  --danger-soft: #fbe9e6;
  --band-blue: #3c5fb8;
  --band-blue-soft: #e5e9f6;
  --band-rose: #a8386a;
  /* Effects: the edits a render is made of (the «جلوه» lane, the effects tab). */
  --band-violet: #6d4bb8;
  --shadow: 0 1px 2px rgb(23 35 29 / 0.05), 0 8px 26px rgb(23 35 29 / 0.08);
  --shadow-lg: 0 14px 46px rgb(23 35 29 / 0.2);
  --radius: 14px;
  --radius-sm: 9px;
  --rail: 232px;
  --font: "Vazirmatn", "Segoe UI", Tahoma, system-ui, sans-serif;
  --mono: ui-monospace, "SF Mono", Menlo, Consolas, monospace;
  /* Ink for anything sitting ON the accent colour. A token rather than a
     per-theme override, because the override version was a specificity trap:
     `:root:not([data-theme="light"]) .tagfield-chip` (0,2,1) silently beat
     `.batch-chip.is-partial` (0,2,0), so in dark mode every chip that was
     *not* accent-filled - partial, staged-for-removal, undeclared - got
     near-black ink on a dark or red background and became unreadable. A
     variable cannot lose a specificity fight it never enters. */
  --on-accent: #ffffff;
}

@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) { color-scheme: dark; }
  :root:not([data-theme="light"]) {
    --on-accent: #07120e;
    --accent: #35c79b;
    --accent-strong: #64e3ba;
    --accent-soft: #123329;
    --gold: #dcae64;
    --gold-soft: #3a2f18;
    --bg: #10160f;
    --surface: #171d18;
    --surface-2: #1e2620;
    --surface-3: #26302a;
    --text: #f1efe3;
    --text-muted: #a7b0a3;
    --border: #2b332a;
    --border-strong: #3c463a;
    --danger: #ef8272;
    --danger-soft: #3a1f1c;
    --band-blue: #7b9aee;
    --band-blue-soft: #1c2740;
    --band-rose: #e585b0;
  --band-violet: #b39af0;
    --shadow: 0 1px 2px rgb(0 0 0 / 0.3), 0 8px 24px rgb(0 0 0 / 0.35);
    --shadow-lg: 0 12px 44px rgb(0 0 0 / 0.55);
  }
}

:root[data-theme="dark"] {
  color-scheme: dark;
  --on-accent: #07120e;
  --accent: #35c79b;
  --accent-strong: #64e3ba;
  --accent-soft: #123329;
  --gold: #dcae64;
  --gold-soft: #3a2f18;
  --bg: #10160f;
  --surface: #171d18;
  --surface-2: #1e2620;
  --surface-3: #26302a;
  --text: #f1efe3;
  --text-muted: #a7b0a3;
  --border: #2b332a;
  --border-strong: #3c463a;
  --danger: #ef8272;
  --danger-soft: #3a1f1c;
  --band-blue: #7b9aee;
  --band-blue-soft: #1c2740;
  --band-rose: #e585b0;
  --band-violet: #b39af0;
  --shadow: 0 1px 2px rgb(0 0 0 / 0.3), 0 8px 24px rgb(0 0 0 / 0.35);
  --shadow-lg: 0 12px 44px rgb(0 0 0 / 0.55);
}

/* ===== reset ===== */
* { box-sizing: border-box; }
html, body { height: 100%; }
body {
  margin: 0;
  background: var(--bg);
  color: var(--text);
  font-family: var(--font);
  font-size: 15px;
  line-height: 1.55;
  -webkit-tap-highlight-color: transparent;
  overflow: hidden;
}
h1, h2, h3, h4, p { margin: 0; }
button { font: inherit; color: inherit; background: none; border: none; cursor: pointer; }
input, select, textarea { font: inherit; color: inherit; }
a { color: var(--accent); }
:focus-visible { outline: 2px solid var(--gold); outline-offset: 2px; border-radius: 6px; }
.ic { width: 1.25em; height: 1.25em; flex: none; }

::-webkit-scrollbar { width: 11px; height: 11px; }
::-webkit-scrollbar-thumb {
  background: var(--border-strong);
  border: 3px solid transparent;
  border-radius: 9px;
  background-clip: content-box;
}
::-webkit-scrollbar-track { background: transparent; }

/* ===== shell ===== */
.app {
  display: grid;
  grid-template-columns: var(--rail) 1fr;
  height: 100%;
}

.rail {
  display: flex;
  flex-direction: column;
  gap: 2px;
  padding: 14px 10px;
  background: var(--surface-2);
  border-inline-start: 1px solid var(--border);
  overflow: hidden auto;
}

.brand {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 6px 8px 14px;
}
.brand-mark {
  width: 34px;
  height: 34px;
  border-radius: 10px;
  background: linear-gradient(140deg, var(--accent) 0%, var(--accent-strong) 100%);
  color: var(--gold-soft);
  display: grid;
  place-items: center;
  flex: none;
}
.brand-mark .ic { width: 20px; height: 20px; }
.brand-text { display: flex; flex-direction: column; line-height: 1.2; }
.brand-title { font-weight: 800; font-size: 0.95rem; }
.brand-sub { font-size: 0.72rem; color: var(--text-muted); }

.rail-nav { display: flex; flex-direction: column; gap: 2px; }
.rail-btn {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 9px 11px;
  border-radius: var(--radius-sm);
  color: var(--text-muted);
  font-weight: 600;
  font-size: 0.9rem;
  text-align: start;
  transition: background 0.14s ease, color 0.14s ease;
}
.rail-btn:hover { background: color-mix(in srgb, var(--accent) 8%, transparent); color: var(--text); }
.rail-btn.is-on { background: var(--accent-soft); color: var(--accent-strong); }
.rail-btn .ic { width: 18px; height: 18px; }
.rail-btn-count {
  margin-inline-start: auto;
  font-size: 0.72rem;
  font-weight: 700;
  padding: 1px 7px;
  border-radius: 99px;
  background: color-mix(in srgb, var(--text-muted) 14%, transparent);
}
.rail-btn.is-on .rail-btn-count { background: color-mix(in srgb, var(--accent) 20%, transparent); }

.rail-spacer { flex: 1; min-height: 12px; }
.rail-foot { display: flex; flex-direction: column; gap: 8px; padding-top: 10px; border-top: 1px solid var(--border); }
.rail-stat { display: flex; justify-content: space-between; font-size: 0.76rem; color: var(--text-muted); padding: 0 4px; }
.rail-stat b { font-weight: 700; color: var(--text); }

/* ===== main ===== */
.main { display: flex; flex-direction: column; min-width: 0; overflow: hidden; }

.topbar {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 12px 18px;
  border-bottom: 1px solid var(--border);
  background: color-mix(in srgb, var(--surface) 72%, transparent);
  backdrop-filter: blur(8px);
  flex: none;
}
.topbar h1 { font-size: 1.05rem; font-weight: 800; white-space: nowrap; }
.topbar-sub { font-size: 0.8rem; color: var(--text-muted); white-space: nowrap; }
.topbar-grow { flex: 1; min-width: 12px; }

.view { flex: 1; min-height: 0; overflow: auto; padding: 18px; }
.view-narrow { max-width: 1180px; margin-inline: auto; }

/* ===== controls ===== */
.btn {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  padding: 8px 14px;
  border-radius: var(--radius-sm);
  border: 1px solid var(--border-strong);
  background: var(--surface);
  font-weight: 650;
  font-size: 0.875rem;
  white-space: nowrap;
  transition: background 0.14s ease, border-color 0.14s ease, transform 0.06s ease;
}
.btn:hover { background: var(--surface-2); }
.btn:active { transform: translateY(1px); }
.btn:disabled { opacity: 0.45; cursor: not-allowed; transform: none; }
.btn .ic { width: 17px; height: 17px; }
.btn-primary { background: var(--accent); border-color: var(--accent); color: var(--on-accent); }
.btn-primary:hover { background: var(--accent-strong); }
.btn-danger { color: var(--danger); border-color: color-mix(in srgb, var(--danger) 45%, transparent); }
.btn-danger:hover { background: var(--danger-soft); }
.btn-quiet { border-color: transparent; background: transparent; color: var(--text-muted); }
.btn-quiet:hover { background: var(--surface-2); color: var(--text); }
.btn-sm { padding: 5px 10px; font-size: 0.8rem; }

.icon-btn {
  display: inline-grid;
  place-items: center;
  width: 34px;
  height: 34px;
  border-radius: var(--radius-sm);
  color: var(--text-muted);
  transition: background 0.14s ease, color 0.14s ease;
  flex: none;
}
.icon-btn:hover { background: var(--surface-2); color: var(--text); }
.icon-btn.is-on { background: var(--accent-soft); color: var(--accent-strong); }
.icon-btn .ic { width: 18px; height: 18px; }

.field {
  width: 100%;
  max-width: 100%;
  padding: 8px 11px;
  border-radius: var(--radius-sm);
  border: 1px solid var(--border-strong);
  background: var(--surface);
  font-size: 0.9rem;
  /* A `<textarea>` resizes both ways by default, and this is the only one on
     the sample panel - the Quran-ranges box in the rail. The rail's column is
     a fixed 280-340px (see `.ann-body`), so dragging that handle sideways,
     which a reader reaching for the corner to make the box *taller* can do
     by a few stray pixels, stretched it past the column and forced the
     whole panel into a horizontal scrollbar that clipped everything to its
     left - reported as "the left pane got wider and I can't read it". Height
     is still worth resizing, for a long list of ranges; width never is,
     because the column it sits in is never going to change. */
  resize: vertical;
}
.field:focus { outline: none; border-color: var(--accent); box-shadow: 0 0 0 3px var(--accent-soft); }
.field-mono { font-family: var(--mono); direction: ltr; text-align: left; }
label.lbl { display: block; font-size: 0.78rem; font-weight: 700; color: var(--text-muted); margin-bottom: 5px; }

/* ===== Quran finder =====
   A compact concordance inside the one place a maintainer assigns verses.
   The Arabic line is the visual anchor; coordinates stay small and LTR so
   listening, recognizing and assigning remain one short loop. */
.quran-search {
  position: relative;
  margin: 10px 0;
  padding: 10px;
  border: 1px solid color-mix(in srgb, var(--gold) 34%, var(--border));
  border-radius: var(--radius-sm);
  background: linear-gradient(145deg, var(--gold-soft), var(--surface) 58%);
}
.quran-search-label {
  display: flex;
  align-items: center;
  gap: 6px;
  margin-bottom: 7px;
  color: var(--text);
  font-size: .78rem;
  font-weight: 800;
}
.quran-search-label .ic { width: 15px; height: 15px; color: var(--gold); }
.quran-search-box {
  display: flex;
  align-items: center;
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-sm);
  background: var(--surface);
}
.quran-search-box:focus-within { border-color: var(--gold); box-shadow: 0 0 0 3px var(--gold-soft); }
.quran-search-box input {
  width: 100%;
  min-width: 0;
  padding: 8px 10px;
  border: 0;
  outline: 0;
  background: transparent;
  color: var(--text);
  font: inherit;
  font-size: .82rem;
}
.quran-search-status { min-height: 1.3em; margin-top: 5px; color: var(--text-muted); font-size: .7rem; }
.quran-search-results {
  max-height: min(390px, 48vh);
  margin-top: 7px;
  overflow-y: auto;
  overscroll-behavior: contain;
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  background: var(--surface);
  box-shadow: var(--shadow);
}
.quran-search-result {
  position: relative;
  display: grid;
  width: 100%;
  gap: 4px;
  padding: 10px 11px;
  border: 0;
  border-bottom: 1px solid var(--border);
  background: transparent;
  color: var(--text);
  text-align: start;
  cursor: pointer;
}
.quran-search-result:last-child { border-bottom: 0; }
.quran-search-result:hover,
.quran-search-result[aria-selected="true"] { background: var(--selection-fill); }
.quran-search-result:focus-visible { outline: 2px solid var(--selection-ink); outline-offset: -2px; }
.quran-search-meta { display: flex; align-items: center; gap: 7px; color: var(--text-muted); font-size: .68rem; }
.quran-search-meta b { color: var(--gold); font-size: .72rem; }
.quran-search-ayah {
  display: block;
  padding-inline-end: 2px;
  font-family: "AmiriQuran", "Vazirmatn", serif;
  font-size: 1.02rem;
  line-height: 1.9;
  text-align: right;
}
.quran-search-match {
  padding: .06em .12em;
  border-radius: .22em;
  background: color-mix(in srgb, var(--gold) 28%, transparent);
  color: inherit;
  box-shadow: inset 0 -.11em 0 color-mix(in srgb, var(--gold) 36%, transparent);
  -webkit-box-decoration-break: clone;
  box-decoration-break: clone;
}
.quran-search-result[aria-selected="true"] .quran-search-match {
  background: color-mix(in srgb, var(--gold) 38%, var(--selection-fill));
}
.quran-search-add { display: flex; align-items: center; gap: 3px; color: var(--selection-ink); font-size: .68rem; font-weight: 750; }
.quran-search-add .ic { width: 13px; height: 13px; }
.quran-range-preview { display: grid; gap: 5px; }
.quran-range-preview > span { font-family: "AmiriQuran", "Vazirmatn", serif; font-size: .92rem; line-height: 1.8; }

/* ===== search ===== */
.search {
  display: flex;
  align-items: center;
  gap: 8px;
  flex: 1;
  min-width: 200px;
  max-width: 560px;
  padding: 0 12px;
  border-radius: 99px;
  border: 1px solid var(--border-strong);
  background: var(--surface);
}
.search:focus-within { border-color: var(--accent); box-shadow: 0 0 0 3px var(--accent-soft); }
.search .ic { width: 17px; height: 17px; color: var(--text-muted); }
.search input {
  flex: 1;
  border: 0;
  background: none;
  padding: 9px 0;
  font-size: 0.9rem;
  min-width: 40px;
}
.search input:focus { outline: none; }
.search-hint {
  font-family: var(--mono);
  font-size: 0.72rem;
  color: var(--text-muted);
  direction: ltr;
  unicode-bidi: isolate;
}

/* ===== the library's filter bar (views/library.js, ui/facet-picker.js) =====
   One row of facet buttons, each opening a searchable picker; what is in
   force as removable tokens under it. Sticks to the top of the list. */
.lib-main { --lib-bar-h: 58px; min-width: 0; }
.lib-bar {
  position: sticky;
  top: -18px;
  z-index: 4;
  margin: -18px 0 14px;
  padding: 14px 0 10px;
  background: color-mix(in srgb, var(--bg) 92%, transparent);
  backdrop-filter: blur(8px);
  border-bottom: 1px solid var(--border);
  display: flex;
  flex-direction: column;
  gap: 8px;
}
.lib-bar-row { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.lib-bar-facets { gap: 6px; }
.lib-bar-status { gap: 6px; min-height: 30px; }
.lib-bar-sep { width: 1px; height: 20px; background: var(--border-strong); margin-inline: 2px; }
.lib-count { margin-inline-end: 4px; font-size: 0.8rem; color: var(--text-muted); white-space: nowrap; font-variant-numeric: tabular-nums; }
.lib-count b { color: var(--text); font-size: 0.92rem; }
.lib-group { display: inline-flex; align-items: center; gap: 6px; font-size: 0.76rem; color: var(--text-muted); white-space: nowrap; }
.field.field-sm { width: auto; padding: 4px 8px; font-size: 0.8rem; border-radius: 8px; }

.fb {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  height: 32px;
  padding: 0 10px 0 8px;
  border-radius: 9px;
  border: 1px solid var(--border-strong);
  background: var(--surface);
  color: var(--text);
  font: inherit;
  font-size: 0.82rem;
  font-weight: 600;
  cursor: pointer;
  white-space: nowrap;
  max-width: 260px;
  transition: background 0.12s ease, border-color 0.12s ease, box-shadow 0.12s ease;
}
.fb:hover { background: var(--surface-2); }
.fb:focus-visible { outline: none; box-shadow: 0 0 0 3px var(--accent-soft); border-color: var(--accent); }
.fb .ic { width: 14px; height: 14px; color: var(--text-muted); flex: none; transition: transform 0.15s ease; }
.fb.is-open .ic { transform: rotate(180deg); }
.fb-label { flex: none; }
.fb-value { overflow: hidden; text-overflow: ellipsis; font-weight: 800; color: var(--accent-strong); }
.fb.is-on { background: var(--accent-soft); border-color: color-mix(in srgb, var(--accent) 55%, var(--border)); }
.fb.is-on .fb-label { color: var(--text-muted); }
.fb.is-on .fb-label::after { content: ":"; }
.fb-add { border-style: dashed; color: var(--text-muted); background: transparent; }
.fb-add .ic { width: 15px; height: 15px; }

.lib-selected { display: inline-flex; align-items: center; gap: 2px; font-size: 0.78rem; color: var(--text-muted); }
.ftok-query .ftok-value { font-family: var(--mono); font-weight: 600; direction: ltr; unicode-bidi: isolate; }
.ftok {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  height: 26px;
  padding-inline: 9px 3px;
  border-radius: 99px;
  background: var(--surface-2);
  border: 1px solid var(--border);
  font-size: 0.78rem;
  max-width: 280px;
}
.ftok-facet { color: var(--text-muted); white-space: nowrap; }
.ftok-facet::after { content: ":"; }
.ftok-value { font-weight: 700; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.ftok-x {
  display: grid;
  place-items: center;
  width: 20px;
  height: 20px;
  border: 0;
  border-radius: 99px;
  background: none;
  color: var(--text-muted);
  cursor: pointer;
}
.ftok-x:hover { background: var(--surface-3); color: var(--danger); }
.ftok-x .ic { width: 12px; height: 12px; }
.lib-clear { border: 0; background: none; font: inherit; font-size: 0.78rem; font-weight: 700; color: var(--accent); cursor: pointer; padding: 3px 6px; border-radius: 6px; }
.lib-clear:hover { background: var(--accent-soft); }
.lib-query-note { font-size: 0.78rem; }

.lib-search { position: relative; }
.lib-search-key { flex: none; font-family: var(--mono); font-size: 0.7rem; color: var(--text-muted); border: 1px solid var(--border-strong); border-bottom-width: 2px; border-radius: 5px; padding: 0 6px; line-height: 1.5; }
.lib-search:focus-within .lib-search-key { display: none; }
.lib-search-help {
  flex: none;
  width: 22px;
  height: 22px;
  border-radius: 99px;
  border: 1px solid var(--border-strong);
  background: var(--surface-2);
  color: var(--text-muted);
  font: inherit;
  font-size: 0.74rem;
  font-weight: 800;
  cursor: pointer;
  line-height: 1;
}
.lib-search-help:hover { color: var(--accent); border-color: var(--accent); }

/* chip rows (the archive screen's filters) */
.filters { display: flex; flex-direction: column; gap: 10px; margin-bottom: 16px; }
.filter-row { display: flex; align-items: baseline; gap: 10px; flex-wrap: wrap; }
.filter-label {
  font-size: 0.76rem;
  font-weight: 800;
  color: var(--text-muted);
  min-width: 52px;
  flex: none;
}

/* the picker */
.fp {
  position: fixed;
  z-index: 72;
  display: flex;
  flex-direction: column;
  border-radius: 12px;
  border: 1px solid var(--border-strong);
  background: var(--surface);
  box-shadow: var(--shadow-lg);
  overflow: hidden;
  animation: fp-in 0.12s ease-out;
}
@keyframes fp-in { from { opacity: 0; transform: translateY(-4px); } }
@media (prefers-reduced-motion: reduce) { .fp { animation: none; } }
.fp-head { display: flex; align-items: center; gap: 8px; padding: 10px 12px 8px; }
.fp-title { font-size: 0.88rem; }
.fp-picked { font-size: 0.72rem; color: var(--accent-strong); background: var(--accent-soft); border-radius: 99px; padding: 1px 8px; }
.fp-order { display: inline-flex; border: 1px solid var(--border); border-radius: 8px; padding: 2px; background: var(--surface-2); }
.fp-order button { border: 0; background: none; font: inherit; font-size: 0.7rem; color: var(--text-muted); padding: 2px 7px; border-radius: 6px; cursor: pointer; }
.fp-order button.is-on { background: var(--surface); color: var(--text); box-shadow: 0 1px 2px rgb(0 0 0 / 0.08); font-weight: 700; }
.fp-clear { border: 0; background: none; font: inherit; font-size: 0.74rem; font-weight: 700; color: var(--danger); cursor: pointer; padding: 2px 4px; border-radius: 6px; }
.fp-clear:hover { background: var(--danger-soft); }
.fp-search { display: flex; align-items: center; gap: 7px; margin: 0 10px 8px; padding: 0 10px; border: 1px solid var(--border-strong); border-radius: 9px; background: var(--surface-2); }
.fp-search:focus-within { border-color: var(--accent); box-shadow: 0 0 0 3px var(--accent-soft); background: var(--surface); }
.fp-search .ic { width: 15px; height: 15px; color: var(--text-muted); }
.fp-input { flex: 1; min-width: 0; border: 0; background: none; font: inherit; font-size: 0.84rem; padding: 7px 0; color: var(--text); }
.fp-input:focus { outline: none; }
.fp-list { flex: 1; min-height: 0; overflow-y: auto; padding: 2px 6px 6px; border-top: 1px solid var(--border); overscroll-behavior: contain; }
.fp-list:focus { outline: none; }
.fp-opt {
  position: relative;
  display: flex;
  align-items: center;
  gap: 9px;
  padding: 6px 8px;
  margin-top: 2px;
  border-radius: 7px;
  font-size: 0.84rem;
  cursor: pointer;
  isolation: isolate;
}
.fp-opt.is-active { background: var(--surface-2); }
.fp-opt.is-empty { color: var(--text-muted); }
.fp-opt.is-undeclared .fp-label { color: var(--danger); text-decoration: underline dashed; }
.fp-check {
  flex: none;
  display: grid;
  place-items: center;
  width: 17px;
  height: 17px;
  border-radius: 5px;
  border: 1.5px solid var(--border-strong);
  background: var(--surface);
}
.fp-check .ic { width: 12px; height: 12px; }
.fp-opt.is-on .fp-check { background: var(--accent); border-color: var(--accent); color: var(--on-accent); }
.fp-opt.is-on .fp-label { font-weight: 700; }
.fp-label { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.fp-count { flex: none; min-width: 2.2em; text-align: end; font-size: 0.74rem; color: var(--text-muted); font-variant-numeric: tabular-nums; }
/* how much of the list a value would reach, as a faint bar under the row */
.fp-bar { position: absolute; inset-block: 3px; inset-inline-end: 4px; width: calc(var(--w) * 0.34); max-width: 34%; border-radius: 5px; background: var(--accent-soft); z-index: -1; opacity: 0.85; }
.fp-opt.is-on .fp-bar { background: color-mix(in srgb, var(--accent) 22%, transparent); }
.fp-only {
  display: none;
  flex: none;
  border: 1px solid var(--border-strong);
  background: var(--surface);
  font: inherit;
  font-size: 0.68rem;
  font-weight: 700;
  color: var(--accent-strong);
  border-radius: 6px;
  padding: 1px 6px;
  cursor: pointer;
}
.fp-opt.is-active .fp-only { display: inline-block; }
.fp-only:hover { border-color: var(--accent); background: var(--accent-soft); }
.fp-none { display: flex; align-items: center; justify-content: center; gap: 7px; padding: 22px 10px; font-size: 0.82rem; color: var(--text-muted); }
.fp-none .ic { width: 16px; height: 16px; }
.fp-foot { display: flex; align-items: center; gap: 8px; padding: 7px 12px; border-top: 1px solid var(--border); background: var(--surface-2); font-size: 0.72rem; color: var(--text-muted); }
.fp-foot:empty { display: none; }
.fp-more { border: 0; background: none; font: inherit; font-size: 0.72rem; color: var(--accent-strong); cursor: pointer; padding: 0; text-align: start; }
.fp-more:hover { text-decoration: underline; }
.fp-rule { margin-inline-start: auto; white-space: nowrap; }
.chip {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  padding: 3px 10px;
  border-radius: 99px;
  border: 1px solid var(--border);
  background: var(--surface);
  font-size: 0.8rem;
  font-weight: 600;
  color: var(--text-muted);
  transition: background 0.12s ease, color 0.12s ease, border-color 0.12s ease;
}
.chip:hover { border-color: var(--border-strong); color: var(--text); }
.chip.is-on { background: var(--accent); border-color: var(--accent); color: var(--on-accent); }
.chip.is-undeclared { border-style: dashed; border-color: var(--danger); color: var(--danger); }
.chip.is-empty { opacity: 0.45; }
/* Full ink: at 0.72 opacity a count measured 2.9:1 on its chip. */
.chip-count { font-size: 0.68rem; font-variant-numeric: tabular-nums; }

/* ===== the list ===== */
.group-head {
  display: flex;
  align-items: baseline;
  gap: 9px;
  margin: 22px 0 9px;
  padding-bottom: 6px;
  border-bottom: 1px solid var(--border);
  position: sticky;
  top: -18px;
  background: var(--bg);
  z-index: 2;
}
/* Under the library's sticky filter bar, the headings stick below it. */
.lib-main .group-head { top: calc(var(--lib-bar-h) - 18px); }
.group-head:first-child { margin-top: 0; }
.group-title { font-size: 0.95rem; font-weight: 800; }
.group-count { font-size: 0.76rem; color: var(--text-muted); }

.rows { display: flex; flex-direction: column; gap: 5px; }

.row {
  display: grid;
  grid-template-columns: 30px 34px 34px minmax(0, 1fr) auto;
  align-items: center;
  gap: 10px;
  padding: 8px 12px;
  border-radius: var(--radius-sm);
  border: 1px solid transparent;
  background: var(--surface);
  cursor: pointer;
  transition: border-color 0.12s ease, background 0.12s ease;
}
.row:hover { border-color: var(--border-strong); }
.row.is-focused { border-color: var(--accent); background: color-mix(in srgb, var(--accent) 5%, var(--surface)); }
.row.is-playing { box-shadow: inset 3px 0 0 var(--gold); }
.row.is-selected { background: color-mix(in srgb, var(--gold) 9%, var(--surface)); }
.row.is-missing { border-color: color-mix(in srgb, var(--danger) 40%, transparent); }

.row-check { display: grid; place-items: center; }
.row-check input { width: 16px; height: 16px; accent-color: var(--accent); cursor: pointer; }
.row-main { min-width: 0; display: flex; flex-direction: column; gap: 2px; }
/* ===== filenames in a right-to-left page =====

   A filename is Latin and has to do two unrelated things at once. It has to
   *render* left to right — "45.rast.mp3", with the number at the front,
   which is the only spelling that exists on disk. And it has to *sit* at the
   right-hand margin, where every other line in the row begins, or one row
   reads as two halves aligned to opposite edges.

   Both single-declaration attempts failed, each in its own way, and the
   failures are worth recording because they look like bugs in the data:

     dir="ltr" alone renders it correctly and pins it to the far margin.
     An isolate carrying the page's RTL direction pins it correctly and
     renders it as "rast.mp3.45" — a leading run of digits in an RTL
     paragraph is reordered to the other end, which is right for a price and
     catastrophic for a filename.

   So it takes three declarations doing three jobs: LTR direction for the
   glyph order, isolation so no neighbouring Persian character can reorder
   it, and right alignment inside its own box for the placement.
   ============================================= */
.row-name {
  display: block;
  font-family: var(--mono);
  font-size: 0.8rem;
  direction: ltr;
  unicode-bidi: isolate;
  text-align: right;
  color: var(--text);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.row-title { font-size: 0.86rem; color: var(--text-muted); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.row-tags { display: flex; gap: 4px; flex-wrap: wrap; align-items: center; }
.row-meta {
  display: flex;
  align-items: center;
  gap: 12px;
  font-size: 0.76rem;
  color: var(--text-muted);
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}
.row-meta-cell { min-width: 52px; text-align: end; }

.tag {
  display: inline-flex;
  padding: 1px 8px;
  border-radius: 99px;
  font-size: 0.72rem;
  font-weight: 650;
  background: var(--surface-2);
  color: var(--text-muted);
  border: 1px solid transparent;
}
.tag-maqam { background: var(--accent-soft); color: var(--accent-strong); }
.tag-qari { background: var(--gold-soft); color: var(--gold); }
.tag-technique { background: color-mix(in srgb, var(--band-blue) 15%, transparent); color: var(--band-blue); }
.tag-kind { background: color-mix(in srgb, var(--band-rose) 14%, transparent); color: var(--band-rose); }
.tag-auto {
  cursor: pointer;
  border-color: var(--border);
  background: transparent;
  font-weight: 600;
}
.tag-auto:hover { border-color: var(--border-strong); color: var(--text); }
.tag-auto.is-complete { color: var(--accent); border-color: color-mix(in srgb, var(--accent) 32%, var(--border)); }
.tag-auto.is-incomplete { color: var(--gold); border-color: color-mix(in srgb, var(--gold) 38%, var(--border)); }
.tag-auto.is-error { color: var(--danger); border-color: color-mix(in srgb, var(--danger) 42%, var(--border)); }
.tag-auto.is-active { background: var(--accent); border-color: var(--accent); color: var(--on-accent); }
.filter-row-automatic {
  padding-bottom: 10px;
  border-bottom: 1px dashed var(--border);
}
.chip-automatic:not(.is-on) { background: var(--surface-2); }

.flagdot { width: 7px; height: 7px; border-radius: 99px; flex: none; }
.flagdot-warn { background: var(--gold); }
.flagdot-error { background: var(--danger); }

/* ===== the inspector ===== */
/* Not `align-items: start`, which is the obvious choice and breaks the
   inspector. A grid item sized to its content is a containing block exactly
   as tall as the sticky panel inside it, and `position: sticky` can only
   move an element *within* its containing block - so the panel would scroll
   away with the list, silently, with every rule about it looking correct.
   Stretching the column gives the panel the room to stay put. */
.split { display: grid; grid-template-columns: minmax(0, 1fr) 400px; gap: 16px; align-items: stretch; }
/* Wide screens give the panel the width its graphs want. An equalizer curve
   is read across two decades of frequency, and forty more pixels is forty
   more pixels of 1kHz-to-10kHz - the part of the spectrum every correction
   on this material actually happens in. */
@media (min-width: 1600px) { .split { grid-template-columns: minmax(0, 1fr) 448px; } }
@media (max-width: 1180px) { .split { grid-template-columns: minmax(0, 1fr); } }

.panel {
  border: 1px solid var(--border);
  border-radius: var(--radius);
  background: var(--surface);
  box-shadow: var(--shadow);
  overflow: hidden;
}
.panel-sticky {
  position: sticky;
  top: 0;
  max-height: calc(100dvh - 110px - var(--studio-player-height, 0px));
  display: flex;
  flex-direction: column;
}

.panel-sticky .panel-body { flex: 1; min-height: 0; }
.panel-head {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 12px 14px;
  border-bottom: 1px solid var(--border);
  background: var(--surface-2);
}
.panel-title { font-size: 0.9rem; font-weight: 800; flex: 1; min-width: 0; }
.panel-body {
  padding: 14px;
  overflow: auto;
  overscroll-behavior: contain;
  scrollbar-gutter: stable;
}
.panel-foot { padding: 11px 14px; border-top: 1px solid var(--border); display: flex; gap: 8px; background: var(--surface-2); }

.kv { display: grid; grid-template-columns: auto 1fr; gap: 5px 12px; font-size: 0.82rem; margin-bottom: 14px; }
.kv dt { color: var(--text-muted); }
.kv dd { margin: 0; font-variant-numeric: tabular-nums; }
.kv dd.mono { font-family: var(--mono); direction: ltr; text-align: start; word-break: break-all; }

.section { margin-bottom: 16px; }
.section-title {
  font-size: 0.76rem;
  font-weight: 800;
  color: var(--text-muted);
  margin-bottom: 7px;
  display: flex;
  align-items: center;
  gap: 6px;
}

/* ===== a collapsible topic =====

   The side panel is not one form, it is six or seven subjects that happen to
   share a column - times, names, tags, effects, encoding, actions, the
   session. Each is a .disc: a header that says what it is and, while closed,
   what it currently holds, and a body that opens on a press and stays that
   way (see js/ui/disclosure.js for where that memory lives).

   The header is a row containing a button, not a button containing a row:
   «بازگشت به مشترک» and the level-match action sit beside the title of the
   thing they act on, and a nested <button> is both invalid and, in practice,
   a press that does the action and folds the evidence away.
   ======================================================================== */
.disc {
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  background: var(--surface);
  overflow: hidden;
}
.disc + .disc { margin-top: 8px; }
.disc-head { display: flex; align-items: center; gap: 4px; padding-inline-end: 6px; background: var(--surface-2); }
.disc.is-open .disc-head { border-bottom: 1px solid var(--border); }
.disc-toggle {
  flex: 1;
  min-width: 0;
  display: flex;
  align-items: center;
  gap: 7px;
  padding: 9px 10px;
  background: none;
  border: 0;
  font: inherit;
  color: inherit;
  text-align: start;
  cursor: pointer;
}
.disc-toggle:hover { background: var(--surface-3); }
.disc-toggle:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }
.disc-toggle .ic { width: 15px; height: 15px; color: var(--text-muted); flex: none; }
.disc-chevron .ic { width: 14px; height: 14px; transition: transform 0.15s ease; }
.disc.is-open .disc-chevron .ic { transform: rotate(180deg); }
.disc-title { font-size: 0.8rem; font-weight: 800; white-space: nowrap; }
.disc-summary {
  font-size: 0.72rem;
  color: var(--text-muted);
  margin-inline-start: auto;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  font-variant-numeric: tabular-nums;
}
.disc.is-open .disc-summary { opacity: 0.6; }
.disc-actions { display: flex; align-items: center; gap: 4px; flex: none; }
.disc-actions .icon-btn { width: 26px; height: 26px; }
.disc-actions .icon-btn .ic { width: 15px; height: 15px; }
.disc-body { padding: 12px; }
.disc-body.is-flush { padding: 0; }
.disc-body > .section:last-child { margin-bottom: 0; }
.disc-body > :last-child { margin-bottom: 0; }

/* ===== tag editor =====

   One combobox per axis. At rest the field shows only the values this
   recording actually carries, as removable chips inside the box; everything
   else is behind the search. See the note at the top of js/ui/tags.js for why
   this replaced a grid of 99 toggle chips.

   The menu is in the flow rather than absolutely positioned, because every
   screen that mounts this puts it inside a panel with `overflow: auto` -
   where an absolute menu is clipped and a fixed one has to chase a scroll
   container it cannot see.
   ======================================================================== */
.tag-editor { display: flex; flex-direction: column; gap: 12px; }

.tagfield-head { display: flex; align-items: center; gap: 7px; margin-bottom: 5px; }
.tagfield-label { font-size: 0.79rem; font-weight: 800; color: var(--text-muted); }
.tagfield-count {
  font-size: 0.68rem;
  font-weight: 800;
  color: var(--accent);
  background: var(--accent-soft);
  border-radius: 99px;
  padding: 0 6px;
}

.tagfield-box {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 4px;
  min-height: 38px;
  padding: 5px 8px;
  border-radius: var(--radius-sm);
  border: 1px solid var(--border-strong);
  background: var(--surface);
  cursor: text;
  transition: border-color 0.14s ease, box-shadow 0.14s ease;
}
.tagfield-box:hover { border-color: var(--accent); }
.tagfield-box.is-open,
.tagfield-box:focus-within {
  border-color: var(--accent);
  box-shadow: 0 0 0 3px var(--accent-soft);
}

.tagfield-chips { display: contents; }
.tagfield-chip {
  display: inline-flex;
  align-items: center;
  gap: 3px;
  padding: 2px 4px 2px 9px;
  border-radius: 99px;
  background: var(--accent);
  color: var(--on-accent);
  font-size: 0.79rem;
  font-weight: 650;
  max-width: 100%;
}
.tagfield-chip.is-undeclared { background: var(--danger); color: #fff; }
.tagfield-x {
  display: grid;
  place-items: center;
  width: 17px;
  height: 17px;
  border-radius: 99px;
  color: inherit;
  opacity: 0.72;
  flex: none;
}
.tagfield-x:hover { opacity: 1; background: rgb(255 255 255 / 0.25); }
.tagfield-x .ic { width: 12px; height: 12px; }

.tagfield-input {
  flex: 1;
  min-width: 90px;
  border: 0;
  background: none;
  padding: 3px 2px;
  font-size: 0.85rem;
}
.tagfield-input:focus { outline: none; }
.tagfield-input::placeholder { color: var(--text-muted); }

.tagfield-menu {
  margin-top: 4px;
  max-height: 214px;
  overflow: auto;
  border-radius: var(--radius-sm);
  border: 1px solid var(--border-strong);
  background: var(--surface);
  box-shadow: var(--shadow);
  padding: 4px;
}
.tagfield-option {
  display: flex;
  align-items: center;
  gap: 7px;
  width: 100%;
  padding: 6px 9px;
  border-radius: 7px;
  font-size: 0.84rem;
  text-align: start;
}
.tagfield-option.is-active { background: var(--accent-soft); color: var(--accent-strong); }
.tagfield-option-label { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.tagfield-option-count {
  font-size: 0.72rem;
  color: var(--text-muted);
  font-variant-numeric: tabular-nums;
}
.tagfield-option.is-active .tagfield-option-count { color: inherit; }
.tagfield-add { color: var(--accent); font-weight: 650; }
.tagfield-add .ic { width: 15px; height: 15px; }
.tagfield-empty { font-size: 0.8rem; color: var(--text-muted); padding: 7px 9px; }

/* The compact variant (the importer's cards, the segmenter's side panel) has
   less room below it, so its menu gives up a couple of rows rather than
   pushing the primary button off screen. */
.tag-editor.is-compact .tagfield-menu { max-height: 158px; }

/* ===== the inspector's transport ===== */
.tp { margin-bottom: 14px; }
.tp-stage {
  position: relative;
  height: 66px;
  border-radius: var(--radius-sm);
  border: 1px solid var(--border);
  background: var(--surface-2);
  overflow: hidden;
  cursor: pointer;
}
.tp-wave { display: block; width: 100%; height: 100%; }
.tp.is-loading .tp-stage { opacity: 0.55; }
.tp.is-undecodable .tp-stage { border-style: dashed; }

/* The range input is the keyboard and screen-reader half of the control; the
   canvas above is the half you aim at. Kept in the layout (not display:none,
   which removes it from the accessibility tree) but laid over the waveform
   with only its thumb visible. */
.tp-scrub {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  margin: 0;
  opacity: 0;
  /* Right to left, matching the waveform behind it and the annotation
     panel's timeline: time 0 is the right-hand edge. This said `ltr` while
     the canvas was painted left-to-right, and the two had to agree or a
     press landed nowhere near where it was aimed; they still have to agree,
     and the direction they agree on is now the page's own. */
  direction: rtl;
  /* And it does not take pointer input at all: the stage's own handler owns
     that, and owns it in one place with one set of arithmetic. This element
     exists for the keyboard and the screen reader, both of which still reach
     it by Tab. */
  pointer-events: none;
}
.tp-scrub:focus-visible { opacity: 1; outline: 2px solid var(--gold); outline-offset: -2px; }

.tp-bar { display: flex; align-items: center; gap: 8px; margin-top: 6px; }
.tp-play { flex: none; }
.tp-time { font-size: 0.8rem; font-variant-numeric: tabular-nums; }
.tp-dash { color: var(--text-muted); font-size: 0.8rem; }

/* ===== the now-playing bar =====
   Docked below the view, outside it, so it neither scrolls away with a screen
   nor belongs to one. It is the only transport in the application - see the
   header of js/ui/nowplaying.js for why it is not in the side panel. */
.np {
  flex: none;
  border-top: 1px solid var(--border);
  background: color-mix(in srgb, var(--surface) 88%, transparent);
  backdrop-filter: blur(8px);
  padding: 8px 18px 10px;
}
.np-inner {
  display: flex;
  align-items: center;
  gap: 14px;
  max-width: 1180px;
  margin-inline: auto;
}
.np-title {
  display: flex;
  align-items: center;
  gap: 7px;
  min-width: 0;
  width: 210px;
  flex: none;
  padding: 4px 8px;
  border-radius: var(--radius-sm);
  color: var(--text-muted);
}
.np-title:hover { background: var(--surface-2); color: var(--text); }
.np-title .ic { width: 16px; height: 16px; flex: none; color: var(--accent); }
.np-name {
  font-family: var(--mono);
  font-size: 0.76rem;
  direction: ltr;
  unicode-bidi: isolate;
  text-align: right;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
/* The transport fills the bar, so its own bottom margin is not wanted here. */
.np-transport { flex: 1; min-width: 0; }
.np-transport .tp { margin-bottom: 0; }
.np-transport .tp-stage { height: 44px; }
.np-transport .tp-bar { margin-top: 4px; }

@media (max-width: 860px) {
  .np-title { width: 120px; }
}

/* ===== the batch panel ===== */
.batch-list { display: flex; flex-direction: column; gap: 2px; max-height: 168px; overflow: auto; }
.batch-row {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 3px 6px;
  border-radius: 7px;
  font-size: 0.79rem;
  border: 1px solid transparent;
}
.batch-row:hover { background: var(--surface-2); }
/* Which of the selected recordings the staged operations would actually
   touch. Four of nine changing is a thing worth seeing before you write. */
.batch-row.is-changing { border-color: color-mix(in srgb, var(--accent) 45%, transparent); }
.batch-name {
  flex: 1;
  min-width: 0;
  font-family: var(--mono);
  font-size: 0.74rem;
  direction: ltr;
  unicode-bidi: isolate;
  text-align: right;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.batch-meta { color: var(--text-muted); font-variant-numeric: tabular-nums; font-size: 0.74rem; }
.batch-row .icon-btn { width: 22px; height: 22px; }
.batch-row .icon-btn .ic { width: 13px; height: 13px; }

/* A chip in the batch editor carries a third state the single editor has no
   need for: "some of them". Saying how many is the whole reason this is not
   an ordinary tag field - it is the difference between adding a tag and
   quietly overwriting eight facts. */
.batch-chip { padding-inline-start: 0; }
.batch-chip-body {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  padding: 2px 9px 2px 4px;
  border-radius: 99px;
  color: inherit;
  font: inherit;
}
.batch-frac {
  font-size: 0.7rem;
  font-variant-numeric: tabular-nums;
  opacity: 0.8;
  padding: 0 4px;
  border-radius: 99px;
  background: rgb(255 255 255 / 0.22);
}
.batch-chip.is-partial {
  background: var(--surface-2);
  color: var(--text);
  border: 1px dashed var(--border-strong);
}
/* The fraction is the whole point of a partial chip, so it gets the accent
   rather than blending into the chip it sits in. */
.batch-chip.is-partial .batch-frac {
  background: var(--accent-soft);
  color: var(--accent-strong);
  font-weight: 700;
}
.batch-chip.is-staged-add {
  background: var(--accent);
  border-style: solid;
  border-color: var(--accent);
  color: var(--on-accent);
  box-shadow: 0 0 0 2px var(--accent-soft);
}
.batch-chip.is-staged-remove {
  background: var(--danger-soft);
  border: 1px solid var(--danger);
  color: var(--danger);
  text-decoration: line-through;
}

.batch-op {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 5px 9px;
  border-radius: 7px;
  font-size: 0.82rem;
  background: var(--surface-2);
}
.batch-op .ic { width: 15px; height: 15px; flex: none; }
.batch-op-add { color: var(--accent-strong); }
.batch-op-add .ic { color: var(--accent); }
.batch-op-remove { color: var(--danger); }

/* The empty panel spells out the gestures, because a selection model nobody
   can discover is a selection model nobody uses. */
.empty-keys {
  display: flex;
  flex-direction: column;
  gap: 4px;
  align-items: center;
  margin-top: 12px;
  font-size: 0.78rem;
}
.empty-keys > div { display: flex; align-items: center; gap: 5px; }
kbd {
  font: inherit;
  font-size: 0.72rem;
  padding: 1px 6px;
  border-radius: 5px;
  border: 1px solid var(--border-strong);
  background: var(--surface-2);
  color: var(--text);
}

/* ===== the detection panel ===== */
.panel-settings { border-color: var(--border-strong); }
.panel-settings .panel-head .ic { width: 17px; height: 17px; color: var(--text-muted); }
.slider-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(230px, 1fr)); gap: 12px 16px; }
.slider .lbl { display: flex; align-items: baseline; gap: 6px; }
.lbl-hint { font-weight: 400; font-size: 0.72rem; color: var(--text-muted); }
.slider-value {
  font-size: 0.78rem;
  color: var(--text);
  min-width: 72px;
  text-align: end;
  font-variant-numeric: tabular-nums;
}

/* ===== segment list ===== */
.seg-panel { position: relative; padding-block: 10px; }
.seg-list {
  display: flex;
  flex-direction: column;
  gap: 3px;
  max-height: clamp(240px, 42vh, 520px);
  overflow: auto;
  overscroll-behavior: contain;
}
.seg-empty { padding-block: 34px; }
.seg-summary { margin-inline-end: auto; }
.seg-all {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font-size: 0.78rem;
  color: var(--text-muted);
  cursor: pointer;
  white-space: nowrap;
}
.seg-all input { width: 15px; height: 15px; accent-color: var(--accent); cursor: pointer; }

.seg {
  /* Eight columns for eight children - checkbox, number, state dot, name,
     badge, times, length, actions. One short and the actions wrap onto a
     second line for exactly the rows that have a badge, which is how a list
     ends up with two row heights. */
  display: grid;
  grid-template-columns: 18px 26px 9px minmax(0, 1fr) auto auto auto auto;
  align-items: center;
  gap: 9px;
  padding: 6px 9px;
  border-radius: var(--radius-sm);
  border: 1px solid transparent;
  background: var(--surface-2);
  cursor: pointer;
  font-size: 0.82rem;
  /* Shift-click is a range selection here, and without this the browser also
     drags a text selection across every row it passes. */
  user-select: none;
}
.seg:hover { border-color: var(--border-strong); }
/* Two different things, two different marks: selected is the set an action
   applies to, focused is the one the panel is describing. They were one
   highlight once, and nobody could tell which of five rows a button meant. */
.seg.is-selected { background: color-mix(in srgb, var(--accent) 9%, var(--surface)); }
.seg.is-on { border-color: var(--gold); box-shadow: inset 2px 0 0 var(--gold); }
.seg input[type="checkbox"] { width: 15px; height: 15px; accent-color: var(--accent); cursor: pointer; }
.seg-num { font-variant-numeric: tabular-nums; color: var(--text-muted); font-size: 0.76rem; text-align: center; }
.seg-dot { width: 8px; height: 8px; border-radius: 999px; background: var(--border-strong); }
.seg.is-imported .seg-dot { background: var(--accent); }
.seg.is-changed .seg-dot { background: var(--gold); }
.seg-name {
  font-family: var(--mono);
  font-size: 0.74rem;
  direction: ltr;
  unicode-bidi: isolate;
  text-align: right;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.seg-name.is-title { font-family: var(--font); font-size: 0.82rem; direction: rtl; text-align: start; }
.seg-time { font-variant-numeric: tabular-nums; font-size: 0.74rem; color: var(--text-muted); direction: ltr; }
.seg-len { font-variant-numeric: tabular-nums; font-size: 0.74rem; color: var(--text-muted); direction: ltr; min-width: 46px; text-align: end; }
.seg-actions { gap: 2px; opacity: 0.55; }
.seg:hover .seg-actions, .seg.is-on .seg-actions { opacity: 1; }
.seg-actions .icon-btn { width: 26px; height: 26px; }
.seg-actions .icon-btn .ic { width: 15px; height: 15px; }
.seg-badge {
  font-size: 0.68rem;
  font-weight: 700;
  padding: 1px 7px;
  border-radius: 999px;
  white-space: nowrap;
}
.seg-badge:empty { display: none; }
.seg-badge.is-imported { background: var(--accent-soft); color: var(--accent-strong); }
.seg-badge.is-changed { background: var(--gold-soft); color: var(--gold); }

/* ===== the effects rack =====

   An insert chain, read the way a channel strip reads one: the list of
   inserts, and the editor for the one selected. Not a fold per row - the
   studio has one way of saying "this is what you are working on", and the
   rack uses it rather than inventing a second one inside a panel that is
   already a stack of folding topics. It also settles how many graphs can be
   on screen at once at one, which is the only answer a 400px column has
   room for.

   Each row carries its bypass on the left (where a hand expects it, and
   where it does not move when the row is reordered), its name and a summary
   of what it is currently doing - "۳ نقطه، ۲ فعال", "+۴٫۵ dB" - and a ⋮ for
   everything else, the same idiom the segment list already uses for its own
   second-rank verbs.
   ======================================================================== */
.fx-rack {
  display: flex;
  flex-direction: column;
  min-width: 0;
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  background: var(--surface);
  overflow: hidden;
}
.fx-empty {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 8px;
  padding: 20px 12px;
  color: var(--text-muted);
  font-size: 0.82rem;
  text-align: center;
}
.fx-empty .ic { width: 22px; height: 22px; opacity: 0.6; }
.fx-list { display: flex; flex-direction: column; min-width: 0; background: var(--surface-2); }
.fx-row {
  display: flex;
  align-items: center;
  gap: 4px;
  padding: 4px 6px;
  min-width: 0;
  border-bottom: 1px solid var(--border);
}
.fx-row.is-bypassed { opacity: 0.55; }
.fx-row.is-selected { background: var(--surface); }
.fx-bypass.onoff { width: 30px; flex: none; padding: 0; }
.fx-head { display: flex; align-items: center; gap: 8px; padding: 6px 8px 0; font-size: 0.74rem; }
.fx-grip { flex: none; width: 18px; height: 28px; border: none; background: none; color: var(--text-muted); cursor: grab; padding: 0; touch-action: none; border-radius: 4px; }
.fx-grip .ic { width: 14px; height: 14px; }
.fx-grip:focus-visible { outline: 2px solid var(--accent); }
.fx-rack.is-sorting .fx-insert { transition: transform 0.14s ease; }
.fx-rack.is-sorting .fx-insert.is-dragging { transition: none; position: relative; z-index: 2; box-shadow: var(--shadow-lg); cursor: grabbing; }
.fx-row-pick {
  flex: 1;
  min-width: 0;
  display: flex;
  align-items: center;
  gap: 6px;
  padding: 4px 5px;
  border: 0;
  border-radius: 6px;
  background: none;
  font: inherit;
  color: inherit;
  text-align: start;
  cursor: pointer;
}
.fx-row-pick:hover { background: var(--surface-3); }
.fx-row-pick:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }
.fx-row-title { font-size: 0.82rem; font-weight: 700; white-space: nowrap; flex: none; }
.fx-row.is-selected .fx-row-title { color: var(--accent-strong); }
.fx-row-summary {
  font-size: 0.71rem;
  color: var(--text-muted);
  margin-inline-start: auto;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  font-variant-numeric: tabular-nums;
}
.fx-row .icon-btn { width: 24px; height: 24px; flex: none; }
.fx-row .icon-btn .ic { width: 14px; height: 14px; }
.fx-editor { min-width: 0; border-bottom: 1px solid var(--border); }
.fx-foot { padding: 8px; }
.fx-foot .fx-add { width: 100%; justify-content: center; }

/* ----- parameters: a fader with a scale, and a number you can type ----- */
.fx-params {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
  gap: 10px 14px;
  padding: 10px 12px;
  min-width: 0;
}
.fx-params.fx-channels { grid-template-columns: minmax(0, 1fr); }
.fx-note { margin: 0; font-size: 0.76rem; color: var(--text-muted); }
.fx-param { display: flex; flex-direction: column; gap: 4px; min-width: 0; }
.fx-param-head { display: flex; align-items: center; gap: 6px; min-width: 0; }
.fx-param-label {
  font-size: 0.74rem;
  color: var(--text-muted);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.fx-param-head .fx-param-label { flex: 1; min-width: 0; }

/* Latin digits and an LTR field, unlike everything else here - see the
   comment above numberField() in js/ui/effects-rack.js. */
.fx-numwrap { display: inline-flex; align-items: center; gap: 3px; direction: ltr; flex: none; }
.fx-numwrap.is-off { opacity: 0.5; }
.fx-num {
  width: 4.8em;
  padding: 2px 5px;
  border: 1px solid var(--border);
  border-radius: 5px;
  background: var(--surface);
  color: var(--text);
  font-family: var(--mono);
  font-size: 0.73rem;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
  text-align: end;
  direction: ltr;
  appearance: textfield;
  -moz-appearance: textfield;
}
.fx-num::-webkit-outer-spin-button,
.fx-num::-webkit-inner-spin-button { -webkit-appearance: none; margin: 0; }
.fx-num:focus { outline: 2px solid var(--accent); outline-offset: -1px; border-color: var(--accent); }
.fx-num:disabled { opacity: 0.5; background: var(--surface-2); }
.fx-unit { font-size: 0.67rem; color: var(--text-muted); direction: ltr; min-width: 1.6em; }

.fx-track { position: relative; direction: ltr; }
.fx-range {
  -webkit-appearance: none;
  appearance: none;
  display: block;
  width: 100%;
  height: 16px;
  margin: 0;
  background: transparent;
  cursor: pointer;
}
.fx-range::-webkit-slider-runnable-track {
  height: 5px;
  border-radius: 999px;
  background: linear-gradient(
    to right,
    var(--surface-3) 0 var(--from, 0%),
    var(--accent) var(--from, 0%) var(--to, 0%),
    var(--surface-3) var(--to, 0%) 100%
  );
}
.fx-range::-moz-range-track {
  height: 5px;
  border-radius: 999px;
  background: linear-gradient(
    to right,
    var(--surface-3) 0 var(--from, 0%),
    var(--accent) var(--from, 0%) var(--to, 0%),
    var(--surface-3) var(--to, 0%) 100%
  );
}
.fx-range::-webkit-slider-thumb {
  -webkit-appearance: none;
  width: 14px;
  height: 14px;
  margin-top: -4.5px;
  border-radius: 999px;
  background: var(--surface);
  border: 2px solid var(--accent);
  box-shadow: 0 1px 2px rgb(23 35 29 / 0.25);
}
.fx-range::-moz-range-thumb {
  width: 14px;
  height: 14px;
  border-radius: 999px;
  background: var(--surface);
  border: 2px solid var(--accent);
  box-shadow: 0 1px 2px rgb(23 35 29 / 0.25);
}
.fx-range:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; border-radius: 999px; }
.fx-ticks { position: relative; height: 4px; margin-top: -1px; }
.fx-tick { position: absolute; top: 0; width: 1px; height: 4px; background: var(--border-strong); }

/* ----- the two graphs ----- */
.fx-eq,
.fx-comp { display: flex; flex-direction: column; gap: 8px; padding: 10px 12px; min-width: 0; }
.fx-eqframe,
.fx-compframe {
  border: 1px solid var(--border);
  border-radius: 8px;
  background: var(--surface-2);
  overflow: hidden;
  min-width: 0;
}
.fx-eqgraph,
.fx-compgraph {
  display: block;
  width: 100%;
  height: 100%;
  /* The drags on these are pointer drags, so the browser must not decide
     mid-gesture that a vertical movement was a scroll instead. */
  touch-action: none;
}
.fx-eqframe { height: 156px; }
/* Input and output on one scale, so unity is a 45° line: the frame is a
   square, centred, rather than a wide box with a small square drawn inside
   it and parchment either side. */
.fx-compframe { width: min(100%, 236px); aspect-ratio: 1 / 1; margin-inline: auto; }
.fx-eqgraph { cursor: crosshair; }
.fx-eqgraph:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }

/* One row of values, one row of verbs, and nothing in either that belongs
   in the other. */
.fx-readout { display: flex; align-items: center; gap: 6px; flex-wrap: wrap; min-width: 0; }
.fx-readout .fx-num { width: 4.3em; }
.fx-eqreadout { flex-direction: column; align-items: stretch; gap: 6px; }
.fx-readout-top { display: flex; align-items: center; gap: 6px; min-width: 0; }
.fx-readout-empty { font-size: 0.74rem; color: var(--text-muted); }
.fx-readout-nums { display: flex; align-items: center; gap: 6px; flex-wrap: wrap; }
.fx-readout-nums .fx-numwrap { flex: 1; min-width: 0; }
.fx-readout-nums .fx-num { flex: 1; min-width: 0; width: auto; }
.fx-verbs { display: flex; align-items: center; gap: 2px; margin-inline-start: auto; flex: none; }
.fx-verbs .icon-btn { width: 26px; height: 26px; }
.fx-verbs .icon-btn .ic { width: 15px; height: 15px; }
.fx-bandno {
  width: 18px;
  height: 18px;
  flex: none;
  border-radius: 999px;
  background: var(--gold);
  color: var(--on-accent);
  font-size: 0.67rem;
  font-weight: 800;
  display: inline-flex;
  align-items: center;
  justify-content: center;
}
.fx-select {
  font: inherit;
  font-size: 0.73rem;
  padding: 2px 4px;
  max-width: 7.5em;
  border: 1px solid var(--border);
  border-radius: 5px;
  background: var(--surface);
  color: var(--text);
}

.fx-rack.is-compact .fx-params,
.fx-rack.is-compact .fx-eq,
.fx-rack.is-compact .fx-comp { padding: 8px 10px; }
.fx-rack.is-compact .fx-eqframe { height: 140px; }
.fx-rack.is-compact .fx-compframe { width: min(100%, 212px); }

/* The rows in the two lists on the empty screen - sessions and staged
   sources - which are the same shape as a segment row but a different set
   of columns. */
.seg-session { grid-template-columns: 22px minmax(0, 1fr) auto auto auto; }

/* ===== saving ===== */
.save-state { display: inline-flex; align-items: center; gap: 6px; font-size: 0.76rem; color: var(--text-muted); }
.save-dot { width: 8px; height: 8px; border-radius: 999px; background: var(--border-strong); flex: none; }
.save-dot.is-saved { background: var(--accent); }
.save-dot.is-dirty { background: var(--gold); }
.save-dot.is-saving { background: var(--gold); animation: pulse 1s ease-in-out infinite; }
.save-dot.is-error { background: var(--danger); }
@keyframes pulse { 50% { opacity: 0.3; } }

/* ===== menus ===== */
.menu {
  position: fixed;
  z-index: 70;
  min-width: 216px;
  padding: 5px;
  border-radius: var(--radius-sm);
  border: 1px solid var(--border-strong);
  background: var(--surface);
  box-shadow: var(--shadow-lg);
  display: flex;
  flex-direction: column;
  gap: 1px;
}
.menu-item {
  display: flex;
  align-items: center;
  gap: 9px;
  padding: 7px 9px;
  border-radius: 7px;
  font: inherit;
  font-size: 0.84rem;
  color: var(--text);
  background: none;
  border: 0;
  cursor: pointer;
  text-align: start;
}
.menu-item:hover:not(:disabled), .menu-item:focus-visible { background: var(--surface-2); outline: none; }
.menu-item:disabled { opacity: 0.4; cursor: not-allowed; }
.menu-item .ic { width: 16px; height: 16px; color: var(--text-muted); flex: none; }
.menu-item.is-danger { color: var(--danger); }
.menu-item.is-danger .ic { color: var(--danger); }
.menu-gap { width: 16px; flex: none; }
.menu-label { flex: 1; min-width: 0; }
.menu-hint { font-size: 0.68rem; opacity: 0.8; }
.menu-sep { height: 1px; margin: 4px 6px; background: var(--border); }
.menu-heading { padding: 4px 10px 2px; font-size: 0.74rem; color: var(--text-muted); max-width: 22rem; }
.tm-mushaf-hover { font-size: 0.74rem; color: var(--text-muted); padding: 2px 8px; min-height: 1.4em; }

/* ===== the shortcut sheet ===== */
.keys { display: grid; grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); gap: 4px 24px; }
.keys-group {
  grid-column: 1 / -1;
  font-size: 0.78rem;
  font-weight: 800;
  color: var(--text-muted);
  margin-top: 10px;
}
.keys-group:first-child { margin-top: 0; }
.keys-row { display: grid; grid-template-columns: 128px minmax(0, 1fr); gap: 10px; align-items: baseline; font-size: 0.82rem; }
.keys-keys { display: flex; gap: 3px; justify-content: flex-end; }
.keys .dialog-note { grid-column: 1 / -1; margin-top: 12px; }

.field.is-readonly { background: var(--surface-2); color: var(--text-muted); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

/* ===== the tag manager ===== */
.tagcat-head {
  display: flex;
  align-items: center;
  gap: 8px;
  flex: 1;
  min-width: 0;
  text-align: start;
  color: inherit;
}
.tagcat-head .ic { width: 16px; height: 16px; color: var(--text-muted); }
.tagcat-label { font-size: 0.9rem; font-weight: 800; }
.tagcat-id { font-size: 0.74rem; color: var(--text-muted); }

.taglist { display: flex; flex-direction: column; gap: 2px; }
.tagrow {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto auto 48px auto;
  align-items: center;
  gap: 10px;
  padding: 5px 9px;
  border-radius: var(--radius-sm);
  border: 1px solid transparent;
}
.tagrow:hover { background: var(--surface-2); border-color: var(--border); }
.tagrow-label { font-size: 0.86rem; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.tagrow-id {
  font-family: var(--mono);
  font-size: 0.74rem;
  color: var(--text-muted);
  direction: ltr;
  unicode-bidi: isolate;
}
/* A value nothing carries is not wrong - it may be the next thing to tag -
   so it is quiet rather than flagged. One that samples carry but the
   catalogue never declared *is* wrong: it fails the build. */
.tagrow.is-unused .tagrow-label { color: var(--text-muted); }
.tagrow.is-undeclared { background: var(--danger-soft); }
.tagrow-flag {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  font-size: 0.72rem;
  font-weight: 700;
  color: var(--danger);
}
.tagrow-flag .ic { width: 13px; height: 13px; }
.tagrow-count {
  font-size: 0.78rem;
  font-variant-numeric: tabular-nums;
  color: var(--text-muted);
  text-align: end;
  padding: 2px 6px;
  border-radius: 6px;
}
.tagrow-count:hover:not(:disabled) { background: var(--accent-soft); color: var(--accent-strong); }
.tagrow-count:disabled { opacity: 0.4; cursor: default; }
.tagrow-actions { display: flex; gap: 2px; }
.tagrow-actions .icon-btn { width: 28px; height: 28px; }
.tagrow-actions .icon-btn .ic { width: 15px; height: 15px; }

/* ===== stats ===== */
.stat-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); gap: 12px; margin-bottom: 20px; }
.stat {
  padding: 13px 15px;
  border-radius: var(--radius);
  border: 1px solid var(--border);
  background: var(--surface);
}
.stat-value { font-size: 1.5rem; font-weight: 800; line-height: 1.1; font-variant-numeric: tabular-nums; }
.stat-label { font-size: 0.78rem; color: var(--text-muted); margin-top: 2px; }
.stat-note { font-size: 0.72rem; color: var(--text-muted); margin-top: 5px; }

.bars { display: flex; flex-direction: column; gap: 3px; }
.bar-row {
  display: grid;
  grid-template-columns: 132px minmax(0, 1fr) 62px;
  align-items: center;
  gap: 10px;
  padding: 2px 0;
  border-radius: 6px;
  cursor: pointer;
}
button.bar-row { width: 100%; border: none; background: none; color: inherit; font: inherit; text-align: start; }
.bar-row:hover { background: var(--surface-2); }
.bar-row:focus-visible, .matrix-cell:focus-visible, .matrix:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
.matrix-cell { border: none; background: none; color: inherit; font: inherit; cursor: pointer; padding: 0; min-width: 100%; }
.sr-only { position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0; }
.bar-name { font-size: 0.8rem; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.bar-track { height: 15px; border-radius: 5px; background: var(--surface-2); overflow: hidden; }
.bar-fill { height: 100%; border-radius: 5px; background: linear-gradient(90deg, var(--accent) 0%, var(--accent-strong) 100%); }
.bar-fill.is-zero { background: var(--danger); opacity: 0.35; }
.bar-value { font-size: 0.76rem; color: var(--text-muted); font-variant-numeric: tabular-nums; text-align: end; }

.matrix { overflow: auto; border: 1px solid var(--border); border-radius: var(--radius); }
.matrix table { border-collapse: collapse; font-size: 0.76rem; width: 100%; }
.matrix th, .matrix td { padding: 4px 7px; text-align: center; white-space: nowrap; }
.matrix thead th { position: sticky; top: 0; background: var(--surface-2); font-weight: 700; z-index: 1; }
.matrix tbody th { position: sticky; inset-inline-start: 0; background: var(--surface-2); text-align: start; font-weight: 650; z-index: 1; }
.matrix td { font-variant-numeric: tabular-nums; }
.matrix td.is-zero { color: color-mix(in srgb, var(--text-muted) 45%, transparent); }

/* ===== health ===== */
.issues { display: flex; flex-direction: column; gap: 4px; }
.issue {
  display: grid;
  grid-template-columns: 20px 200px minmax(0, 1fr);
  align-items: baseline;
  gap: 10px;
  padding: 7px 11px;
  border-radius: var(--radius-sm);
  background: var(--surface);
  border: 1px solid var(--border);
  font-size: 0.82rem;
  cursor: pointer;
}
.issue:hover { border-color: var(--border-strong); }
.issue-src { font-family: var(--mono); font-size: 0.75rem; direction: ltr; unicode-bidi: isolate; text-align: right; overflow: hidden; text-overflow: ellipsis; }
.issue .ic { width: 15px; height: 15px; }
.issue-error { color: var(--danger); }
.issue-warn { color: var(--gold); }
.issue-info { color: var(--text-muted); }
.issue-message { color: var(--text); }

/* ===== dropzone ===== */
.drop {
  border: 2px dashed var(--border-strong);
  border-radius: var(--radius);
  padding: 34px 20px;
  text-align: center;
  color: var(--text-muted);
  background: var(--surface);
  transition: border-color 0.14s ease, background 0.14s ease;
}
.drop.is-over { border-color: var(--accent); background: var(--accent-soft); color: var(--accent-strong); }
.drop-title { font-weight: 800; font-size: 1rem; color: var(--text); margin-bottom: 5px; }
.drop-hint { font-size: 0.82rem; }
.drop .ic { width: 30px; height: 30px; margin-bottom: 8px; }

/* ===== progress ===== */
.progress { height: 5px; border-radius: 99px; background: var(--surface-2); overflow: hidden; }
.progress-fill { height: 100%; background: var(--accent); transition: width 0.16s linear; }
/* Work whose length is not knowable - a decode, an analysis - says so with a
   travelling bar rather than with a percentage it would have to invent. */
.progress.is-indeterminate .progress-fill { width: 32%; transition: none; animation: progress-slide 1.25s ease-in-out infinite; }
@keyframes progress-slide {
  from { transform: translateX(-110%); }
  to { transform: translateX(320%); }
}

/* ===== opening a recording =====
   The whole of §1 while a file is being read: it replaces the picker rather
   than sitting beside it, so there is no second button to press and no doubt
   about which file the screen is busy with. */
.loading { max-width: 620px; margin: 8vh auto 0; }
.loading .panel-body { display: flex; flex-direction: column; gap: 11px; padding: 24px; }
.loading-head { display: flex; align-items: center; gap: 12px; }
.loading-head .grow { min-width: 0; }
.loading-head .spinner { width: 21px; height: 21px; border-width: 3px; color: var(--accent); flex: none; }
.loading-stage { font-size: 1rem; font-weight: 800; }
.loading-name {
  font-family: var(--mono);
  font-size: 0.76rem;
  color: var(--text-muted);
  direction: ltr;
  unicode-bidi: isolate;
  text-align: end;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.loading-percent { font-size: 0.86rem; font-weight: 700; font-variant-numeric: tabular-nums; color: var(--text-muted); }
.loading-detail { font-size: 0.82rem; color: var(--text-muted); min-height: 1.15em; }
.loading-note { font-size: 0.76rem; color: var(--text-muted); line-height: 1.7; border-top: 1px solid var(--border); padding-top: 10px; }

.spinner {
  width: 15px;
  height: 15px;
  border-radius: 99px;
  border: 2px solid color-mix(in srgb, currentColor 25%, transparent);
  border-top-color: currentColor;
  animation: spin 0.72s linear infinite;
  display: inline-block;
}
@keyframes spin { to { transform: rotate(360deg); } }

/* ===== log ===== */
.log {
  font-family: var(--mono);
  font-size: 0.74rem;
  line-height: 1.5;
  direction: ltr;
  text-align: left;
  background: var(--surface-2);
  border-radius: var(--radius-sm);
  border: 1px solid var(--border);
  padding: 10px 12px;
  max-height: 320px;
  overflow: auto;
  white-space: pre-wrap;
  word-break: break-word;
}
.log-err { color: var(--danger); }

/* ===== toasts ===== */
.toasts {
  position: fixed;
  /* The bottom corner: at the top they covered the editor's «ذخیره» and the
     actions beside it (the test session, ISSUE-007), which sit at this end
     of every screen's bar. */
  bottom: 18px;
  left: 18px;
  display: flex;
  flex-direction: column;
  gap: 8px;
  z-index: 60;
  max-width: min(420px, calc(100vw - 36px));
}
.toast {
  display: flex;
  align-items: flex-start;
  gap: 10px;
  padding: 11px 13px;
  border-radius: var(--radius);
  border: 1px solid var(--border-strong);
  background: var(--surface);
  box-shadow: var(--shadow-lg);
  opacity: 0;
  transform: translateY(8px);
  transition: opacity 0.2s ease, transform 0.2s ease;
}
.toast.is-in { opacity: 1; transform: none; }
.toast-icon { display: grid; place-items: center; padding-top: 2px; }
.toast-icon .ic { width: 17px; height: 17px; }
.toast-ok .toast-icon { color: var(--accent); }
.toast-warn .toast-icon { color: var(--gold); }
.toast-error .toast-icon { color: var(--danger); }
.toast-work .toast-icon { color: var(--accent); }
.toast-body { flex: 1; min-width: 0; }
.toast-message { font-size: 0.86rem; font-weight: 650; }
.toast-detail { font-size: 0.76rem; color: var(--text-muted); margin-top: 2px; word-break: break-word; }
.toast-close { color: var(--text-muted); display: grid; place-items: center; }
.toast-close .ic { width: 15px; height: 15px; }

/* ===== dialog ===== */
.dialog {
  border: 1px solid var(--border-strong);
  border-radius: var(--radius);
  padding: 0;
  background: var(--surface);
  color: var(--text);
  box-shadow: var(--shadow-lg);
  width: min(520px, calc(100vw - 32px));
  max-height: calc(100vh - 60px);
}
.dialog-wide { width: min(860px, calc(100vw - 32px)); }
.dialog::backdrop { background: rgb(10 16 12 / 0.5); backdrop-filter: blur(2px); }
.dialog-form { display: flex; flex-direction: column; max-height: calc(100vh - 60px); }
.dialog-head { display: flex; align-items: center; gap: 10px; padding: 14px 16px; border-bottom: 1px solid var(--border); }
.dialog-title { font-size: 0.98rem; font-weight: 800; flex: 1; }
.dialog-body { padding: 16px; overflow: auto; display: flex; flex-direction: column; gap: 12px; }
.dialog-actions { display: flex; gap: 8px; justify-content: flex-end; padding: 12px 16px; border-top: 1px solid var(--border); background: var(--surface-2); }
.dialog-text { font-size: 0.88rem; }
.dialog-note { font-size: 0.8rem; color: var(--text-muted); }
.dialog-error { font-size: 0.8rem; color: var(--danger); }

.diff { display: flex; flex-direction: column; gap: 9px; }
.diff-file { border: 1px solid var(--border); border-radius: var(--radius-sm); overflow: hidden; }
.diff-path {
  font-family: var(--mono);
  font-size: 0.74rem;
  direction: ltr;
  text-align: left;
  padding: 6px 10px;
  background: var(--surface-2);
  border-bottom: 1px solid var(--border);
}
.diff-line {
  font-family: var(--mono);
  font-size: 0.72rem;
  direction: ltr;
  text-align: left;
  padding: 4px 10px;
  white-space: pre-wrap;
  word-break: break-all;
}
.diff-line + .diff-line { border-top: 1px dashed var(--border); }
.diff-lineno { color: var(--text-muted); user-select: none; }

/* ===== misc ===== */
.empty {
  text-align: center;
  padding: 56px 20px;
  color: var(--text-muted);
}
.empty .ic { width: 34px; height: 34px; margin-bottom: 10px; opacity: 0.55; }
.empty-title { font-size: 1rem; font-weight: 700; color: var(--text); margin-bottom: 4px; }

.callout {
  display: flex;
  gap: 10px;
  padding: 11px 13px;
  border-radius: var(--radius-sm);
  border: 1px solid var(--border);
  background: var(--surface-2);
  font-size: 0.82rem;
}
.callout .ic { width: 17px; height: 17px; flex: none; color: var(--text-muted); }
.callout-warn { background: var(--gold-soft); border-color: color-mix(in srgb, var(--gold) 40%, transparent); }
.callout-warn .ic { color: var(--gold); }
.callout-danger { background: var(--danger-soft); border-color: color-mix(in srgb, var(--danger) 40%, transparent); }
.callout-danger .ic { color: var(--danger); }

/* Inline, inside Persian prose, so only the first two apply - see the note
   above .row-name for why they are needed at all. */
.mono { font-family: var(--mono); direction: ltr; unicode-bidi: isolate; }
/* The exception: a path in a code block, where left-to-right is the whole
   point and the surrounding text is code too. */
.diff-path, .diff-line, .log { direction: ltr; unicode-bidi: normal; }
.muted { color: var(--text-muted); }
.grow { flex: 1; }
.hstack { display: flex; align-items: center; gap: 8px; }
.vstack { display: flex; flex-direction: column; gap: 8px; }
.wrap { flex-wrap: wrap; }

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

@media (max-width: 860px) {
  .app { grid-template-columns: 64px 1fr; }
  .rail { --rail: 64px; align-items: center; }
  .brand-text, .rail-btn span:not(.rail-btn-count), .rail-stat { display: none; }
  .rail-btn { justify-content: center; }
}

/* Selection is blue; green identifies actions and gold identifies playback. */
:root { --selection-ink: #2563b8; --selection-fill: #dceaff; }
:root[data-theme="dark"] { --selection-ink: #85b9ff; --selection-fill: #203b60; }
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) { --selection-ink: #85b9ff; --selection-fill: #203b60; }
}
.row.is-selected, .seg.is-selected {
  background: var(--selection-fill);
  box-shadow: inset -4px 0 var(--selection-ink);
}
.fx-insert { border: 1px solid var(--border); border-radius: 8px; overflow: hidden; background: var(--surface); }
.fx-list { gap: 8px; padding: 8px; }
.fx-row { min-height: 44px; }
.fx-row.is-selected { background: var(--accent-soft); }
.fx-row .icon-btn { width: 30px; height: 32px; }
.fx-row.is-bypassed { opacity: 1; }
.fx-row.is-bypassed .fx-row-pick { color: var(--text-muted); }
.fx-bypass[aria-pressed="false"] { color: var(--text-muted); background: var(--surface-2); }
.fx-bypass[aria-pressed="false"] .ic { opacity: .25; }
.fx-editor { border: 0; border-top: 1px solid var(--border); }
.fx-foot { display: flex; flex-wrap: wrap; gap: 6px; padding: 8px; background: var(--surface); }
.wf-play { background: var(--surface); border-color: var(--border-strong); color: var(--text); }
.wf-group .btn { min-height: 34px; white-space: nowrap; }
.seg-preview { white-space: nowrap; }


/* The settings page scrolls in .view; an unconstrained nested body must not trap wheel/touch scrolling. */
.studio-settings .panel-body { overflow: visible; overscroll-behavior: auto; }

/* A stereo source with one side silent - see channelWarning() in segment.js. */
.seg-channel-warn {
  display: flex; align-items: center; gap: 8px; flex-wrap: wrap; margin-top: 6px;
  font-size: 0.8rem; color: var(--danger);
}
.seg-channel-warn .ic { width: 15px; height: 15px; }
.seg-channel-warn .muted { color: var(--text-muted); }
.tm-meta-ayat.is-derived { color: var(--text-muted); font-style: italic; }
/* The canvas never shrinks below a readable wave over all of its lanes: the
   stage scrolls instead (with the detector's drawer open, say). */
.tm-page .ann { min-height: calc(100px + var(--tm-lanes, 2) * 26px); }
/* A breath's name and time on one line, so ninety of them are a list rather
   than a long scroll. */
.tm-page .ann-breath-pick { display: flex; align-items: baseline; gap: 8px; flex-wrap: wrap; }
.tm-page .ann-breath { padding-block: 2px; }
/* A banner above the stage is a real occupant of the screen: the stage gives
   it the room rather than the page starting to scroll. */
@media (min-width: 1081px) {
  .tm-page:has(> .ann-banner:not([hidden])) .ann-body { height: calc(100dvh - 168px - 66px - var(--studio-player-height, 0px)); }
}
/* The transport's toggles say what they are on a wide screen and are their
   icon (with its tooltip) on an ordinary one, so the transport is one or
   two lines rather than four, and the waveform keeps the room. */
.tm-page .ann-transport .chip { padding-inline: 8px; }
.tm-page .ann-transport .tm-chip-label { display: none; }
@media (min-width: 1700px) { .tm-page .ann-transport .tm-chip-label { display: inline; } }
.tm-page .ann-transport { row-gap: 6px; }

/* ---- the telawat editor's controls (telawat/features.js) ----
   Every feature with a key has a button; its glyph is its own, and the key
   is printed on it where there is room and in its tooltip always. */
.tm-transport-line { display: flex; align-items: center; gap: 8px 14px; flex-wrap: wrap; width: 100%; }
.tm-page .ann-transport { flex-direction: column; align-items: stretch; }
.tm-switches { gap: 4px; }
.icon-btn.tm-toggle.is-on { background: var(--accent-soft); color: var(--accent-strong); border-color: var(--accent); }
.tm-tools { display: flex; align-items: center; gap: 6px 14px; flex-wrap: wrap; padding: 6px 8px; border-radius: var(--radius-sm); background: var(--surface-2); border: 1px solid var(--border); }
.tm-page[data-tools="false"] .tm-tools { height: 0; padding: 0; border: 0; overflow: hidden; visibility: hidden; }
.tm-tool-group { display: flex; align-items: center; gap: 4px; flex-wrap: wrap; }
.tm-tool-title { font-size: 0.7rem; color: var(--text-muted); margin-inline-end: 2px; }
.btn.tm-tool { padding: 4px 8px; font-size: 0.78rem; gap: 5px; }
.btn.tm-tool .ic, .ann-mode-btn .ic { width: 15px; height: 15px; }
.tm-feature-key { background: none; font: 0.62rem var(--mono); color: var(--text-muted); border: 1px solid var(--border-strong); border-radius: 4px; padding: 0 4px; line-height: 1.5; direction: ltr; }
.btn-primary .tm-feature-key, .is-on > .tm-feature-key { color: inherit; border-color: currentColor; }
/* Labels where the stage is wide; the glyph, the key and the tooltip where
   it is not. Measured on the stage itself (a container query), not the
   window: the rail and the mushaf's column take their share first, and the
   bar grew two groups (the range, the sound) - on a 1500px window it wrapped
   onto four rows and pushed the selection bar off the screen. */
section.ann-stage { container-type: inline-size; container-name: stage; }
@container stage (max-width: 1240px) { .btn.tm-tool .tm-feature-label, .btn.tm-tool .tm-feature-key, .tm-tool-title, .ann-transport .tm-feature-key { display: none; } }
.tm-tools > .tm-close { margin-inline-start: auto; }
.icon-btn.tm-close { width: 24px; height: 24px; opacity: 0.6; }
.icon-btn.tm-close:hover { opacity: 1; }
.tm-tabs > .tm-close { flex: none; align-self: center; }
.tm-closed { gap: 4px; }
.tm-closed-chip { font-size: 0.72rem; border-style: dashed; }
.tm-closed-chip .ic { width: 12px; height: 12px; }

/* Maqamat: an ordered list of chips, coloured per maqam. */
.tm-maqam-field { display: grid; gap: 6px; }
/* A breath's maqam boxes, and the order number on every maqam chip. */
.tm-maqam-num { font: 700 0.66rem var(--mono); min-width: 15px; height: 15px; border-radius: 8px; display: inline-grid; place-items: center; background: var(--maqam, var(--accent)); color: #fff; margin-inline-end: 3px; }
.tm-maqam-boxes { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 3px; }
.tm-maqam-box { justify-content: flex-start; gap: 4px; }
.tm-maqam-box .tm-maqam-box-pick { font-weight: 600; }
.tm-maqam-box .tm-maqam-box-time { margin-inline-start: auto; color: var(--text-muted); font-size: 0.72rem; }

/* A breath's ayat, one row each, with the ayah's words: those it recites lit. */
.tm-ayat-parts { list-style: none; margin: 4px 0; padding: 0; display: flex; flex-direction: column; gap: 6px; }
.tm-ayat-part { border: 1px solid var(--border); border-radius: var(--radius-sm); padding: 5px 6px; display: grid; gap: 4px; }
.tm-ayat-part.is-lit { border-color: var(--gold); box-shadow: inset 3px 0 0 var(--gold); }
.tm-ayat-part-head { display: flex; align-items: center; gap: 6px; font-size: 0.78rem; }
.tm-ayat-part-head .tm-ayat-remove { margin-inline-start: auto; }
.tm-ayat-words { display: flex; flex-wrap: wrap; gap: 3px; direction: rtl; }
.tm-ayat-word {
  font: inherit; font-family: var(--quran-font, inherit); font-size: 0.95rem; line-height: 1.5; padding: 0 5px; border-radius: 6px;
  border: 1px dashed var(--border); background: none; color: var(--text-muted); cursor: pointer;
}
.tm-ayat-word.is-in { border-style: solid; border-color: color-mix(in srgb, var(--gold) 60%, transparent); background: color-mix(in srgb, var(--gold) 14%, var(--surface)); color: var(--text); }
.tm-ayat-word.is-timed { text-decoration: underline; text-decoration-color: var(--band-blue); text-underline-offset: 3px; }
.tm-ayat-word:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
.tm-maqam-chips { display: flex; flex-wrap: wrap; gap: 4px; align-items: center; }
.tm-maqam-chip { display: inline-flex; align-items: center; gap: 2px; border-radius: 999px; padding: 2px 8px; font-size: 0.78rem; background: color-mix(in oklch, var(--maqam, var(--accent)) 20%, transparent); border: 1px solid color-mix(in oklch, var(--maqam, var(--accent)) 55%, transparent); }
.tm-maqam-chip.is-inherited { background: none; border-style: dashed; color: var(--text-muted); }
.tm-chip-btn { border: none; background: none; color: inherit; cursor: pointer; padding: 0 3px; font: inherit; font-size: 0.9rem; line-height: 1; opacity: 0.65; }
.tm-chip-btn:hover { opacity: 1; }
.tm-maqam-add { width: auto; max-width: 14rem; padding: 3px 8px; font-size: 0.78rem; }

/* The breath card: which breath, what it recites, its maqamat; the rest folded. */
.tm-detail-head { display: flex; align-items: center; gap: 4px; margin-bottom: 8px; }
.tm-detail-title { display: grid; flex: 1; min-width: 0; text-align: center; }
.tm-detail-title h3 { margin: 0; font-size: 0.95rem; }
.tm-detail-title .mono { font-size: 0.72rem; direction: ltr; }
.tm-ayat-line { font-size: 0.86rem; text-align: right; padding: 2px 0; }
/* «+ عبارت» (P): a button beside the ayat buttons, gold where the breath's
   place suggests one (the opening, between surahs, the close) */
.tm-phrase-pick.is-suggested { border-color: var(--gold); color: var(--gold); font-weight: 700; }
/* a phrase among a breath's ayat: marked as a phrase, its words timed from it */
.tm-phrase-part { border-color: color-mix(in srgb, var(--band-blue) 40%, var(--border)); background: color-mix(in srgb, var(--band-blue) 5%, transparent); }
.tm-phrase-part .tm-ayat-part-head b { font-family: inherit; }
.tm-phrase-badge { display: inline-flex; align-items: center; gap: 3px; padding: 0 6px; border-radius: 99px; font-size: 0.7rem; font-weight: 700; color: var(--band-blue); background: var(--band-blue-soft); }
.tm-phrase-badge .ic { width: 12px; height: 12px; }
.tm-phrase-time { padding: 1px 8px; font-size: 0.74rem; }
.tm-phrase-time .ic { width: 13px; height: 13px; }
.tm-ayat-type summary { cursor: pointer; font-size: 0.74rem; color: var(--text-muted); }
.tm-ayat-type[open] summary { margin-bottom: 4px; }
/* The rail is a column the tabs fill. In a grid with auto rows, "height:
   100%" on the tab resolved to nothing and the mushaf and the breath list
   sat in the top half of an empty rail. As flex items they take the height:
   the mushaf all of it; the breath list what the card under it leaves, with
   a scrollbar of its own, the card keeping its natural size until it would
   squeeze the list below a few rows, and scrolling after that. The other
   tabs are documents and scroll with the rail as before. */
.tm-page .ann-rail { display: flex; flex-direction: column; gap: 10px; }
.tm-page .ann-rail > .tm-tabs { flex: none; }
.tm-page .ann-side[data-tab="quran"], .tm-page .ann-side[data-tab="breaths"] { flex: 1 1 auto; min-height: 0; height: auto; display: flex; flex-direction: column; gap: 10px; }
.tm-page .ann-side[data-tab="quran"] > .tm-quran-tab { flex: 1 1 auto; min-height: 0; display: flex; flex-direction: column; }
.tm-page .ann-side[data-tab="quran"] .tm-mushaf { flex: 1 1 auto; height: auto; min-height: 0; }
/* The card is sized first, to what it holds, up to two thirds of the rail
   (then it scrolls); the list takes the rest from a zero basis. With both
   on "auto" bases a long list out-shrank the card and squeezed it to a
   sliver - with nothing to scroll, since its content had been clipped. */
.tm-page .ann-side .ann-breaths { resize: none; max-height: none; overflow: auto; flex: 1 1 0; min-height: 120px; }
.tm-page .ann-side .ann-detail { resize: none; max-height: 66%; overflow-y: auto; flex: 0 0 auto; min-height: 0; }
.tm-page .ann-breath { cursor: pointer; }
.studio-settings-title { margin: 18px 0 6px; font-size: 0.95rem; }
.studio-settings-title:first-child { margin-top: 0; }
.studio-settings-note { margin: 0 0 8px; font-size: 0.8rem; }

/* ===== notifications (ui/toast.js) ===== */
.notif-bell { position: relative; }
.notif-badge { position: absolute; top: -2px; inset-inline-end: -2px; min-width: 16px; height: 16px; padding: 0 4px; border-radius: 999px; background: var(--accent); color: var(--on-accent, #fff); font: 650 0.62rem/16px var(--mono); text-align: center; }
.notif-badge[hidden] { display: none; }
.notif-bell.has-error .notif-badge { background: var(--danger); }
.notif-panel { position: fixed; z-index: 70; width: min(400px, calc(100vw - 16px)); max-height: min(70vh, 560px); display: flex; flex-direction: column; background: var(--surface); border: 1px solid var(--border-strong); border-radius: var(--radius); box-shadow: var(--shadow-lg); }
.notif-head { display: flex; align-items: center; gap: 8px; padding: 10px 12px; border-bottom: 1px solid var(--border); }
.notif-head h2 { margin: 0 0 0 auto; margin-inline-end: auto; font-size: 0.95rem; }
.notif-list { list-style: none; margin: 0; padding: 6px; overflow-y: auto; display: grid; gap: 4px; }
.notif-item { display: flex; align-items: flex-start; gap: 10px; padding: 8px 10px; border-radius: var(--radius-sm); }
.notif-item.is-unread { background: var(--surface-2); }
.notif-ok .toast-icon { color: var(--accent); }
.notif-warn .toast-icon { color: var(--gold); }
.notif-error .toast-icon { color: var(--danger); }
.notif-time { display: block; margin-top: 2px; font-size: 0.68rem; color: var(--text-muted); }
.notif-empty { margin: 0; padding: 24px; text-align: center; }
.tm-page[data-rail="false"] .ann-body > .tm-quran-handle { display: none !important; }

/* The rail's tabs other than «قرآن» draw into this; it takes no box of its
   own, so the breath list and card stay flex children of the side. */
.tm-side-body { display: contents; }
.tm-side-body[hidden], .tm-quran-tab[hidden] { display: none !important; }

/* ===== the mode bar (telawat/modes.js) =====
   In any state but idle the tools bar is that state's bar - shown even when
   the tools bar is closed, since it is the only way to see what is going on. */
.tm-page[data-task]:not([data-task="idle"]) .tm-tools { display: flex !important; }
.tm-modebar { display: flex; align-items: center; gap: 6px; flex-wrap: wrap; width: 100%; }
.tm-mode-title { display: flex; align-items: baseline; gap: 6px; padding-inline-end: 6px; margin-inline-end: 4px; border-inline-end: 1px solid var(--border); }
.tm-mode-name { font-size: 0.72rem; color: var(--text-muted); }
.tm-mode-title b { font-size: 0.9rem; }
.tm-mode-picks { font-size: 0.8rem; padding: 2px 8px; border-radius: 999px; background: var(--surface-2); max-width: 22rem; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.tm-mode-word { font: 1.05rem var(--quran-font, inherit); padding: 0 8px; }
.tm-mode-hint { font-size: 0.74rem; margin-inline-start: auto; }
.tm-tools[data-task], .tm-page:not([data-task="idle"]) .tm-tools { background: var(--band-blue-soft); border-color: var(--band-blue); }
.tm-tools.is-refusing { animation: tm-refuse 0.35s ease; }
@keyframes tm-refuse { 25% { transform: translateX(-3px); } 75% { transform: translateX(3px); } }

/* ===== the edits: the effects tab, the render (telawat-render.md) ===== */
.tm-render-chip[hidden] { display: none; }
.tm-render-chip { border-color: var(--gold); color: var(--text); background: var(--gold-soft); cursor: pointer; }
.tm-render-chip .ic { width: 13px; height: 13px; }
.tm-render-btn.is-pending { border-color: var(--gold); box-shadow: inset 0 0 0 1px var(--gold); }
.tm-effects { display: flex; flex-direction: column; gap: 10px; }
.tm-fx-status { display: grid; gap: 2px; }
.tm-fx-status-line { display: flex; align-items: center; gap: 6px; font-size: 0.82rem; font-weight: 600; }
.tm-fx-status-line .ic { width: 15px; height: 15px; }
.tm-fx-status-line.is-pending { color: var(--gold); }
.tm-fx-status-line.is-current { color: var(--accent); }
.tm-fx-all { display: flex; align-items: center; gap: 8px; margin-bottom: 8px; font-size: 0.76rem; }
.tm-fx-scopes { display: flex; flex-direction: column; gap: 3px; margin-bottom: 8px; }
.tm-fx-scope {
  display: grid; grid-template-columns: auto 1fr auto; align-items: center; gap: 8px;
  border: 1px solid var(--border); border-radius: var(--radius-sm); background: var(--surface);
  padding: 6px 9px; font: inherit; font-size: 0.8rem; color: var(--text); text-align: start; cursor: pointer;
}
.tm-fx-scope.is-on { border-color: var(--band-violet); box-shadow: inset 3px 0 0 var(--band-violet); }
.tm-fx-scope:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
.tm-fx-count { font-size: 0.72rem; color: var(--text-muted); }
.tm-fx-cuts { display: flex; flex-direction: column; gap: 3px; margin-top: 6px; }
.tm-fx-cut { display: flex; align-items: center; gap: 6px; border: 1px dashed color-mix(in srgb, var(--danger) 55%, transparent); border-radius: var(--radius-sm); padding: 3px 6px; }
.tm-fx-cut .ann-breath-pick { flex: 1; }

/* The maqam picker (ui/maqam-picker.js): every maqam as a chip, what is
   chosen marked with its place in the order, always inside the window. */
.maqam-picker {
  position: fixed; z-index: 80; width: min(420px, calc(100vw - 16px)); display: flex; flex-direction: column; gap: 8px;
  padding: 10px; border: 1px solid var(--border-strong); border-radius: var(--radius); background: var(--surface); box-shadow: var(--shadow-lg);
}
.maqam-head { display: flex; align-items: center; justify-content: space-between; gap: 8px; font-size: 0.86rem; }
.maqam-summary { font-size: 0.76rem; min-height: 1.2em; }
.maqam-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(112px, 1fr)); gap: 4px; overflow-y: auto; min-height: 0; flex: 1 1 auto; padding: 2px; }
.maqam-option {
  display: flex; align-items: center; gap: 6px; min-height: 32px; padding: 4px 8px; border-radius: 8px;
  border: 1px solid var(--border); background: var(--surface-2); color: var(--text); font: inherit; font-size: 0.8rem; cursor: pointer; text-align: start;
}
.maqam-option:hover { border-color: var(--border-strong); }
.maqam-option:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
.maqam-option[aria-checked="true"] { border-color: var(--maqam); background: color-mix(in oklch, var(--maqam) 18%, var(--surface)); font-weight: 650; }
.maqam-option[aria-checked="mixed"] { border-style: dashed; border-color: var(--maqam); }
.maqam-dot { width: 9px; height: 9px; border-radius: 50%; background: var(--maqam); flex: none; }
.maqam-label { flex: 1; }
.maqam-order { font: 700 0.7rem var(--mono); min-width: 16px; height: 16px; border-radius: 8px; display: grid; place-items: center; background: var(--maqam); color: #fff; }
.maqam-foot { display: flex; justify-content: space-between; gap: 8px; }
/* Grouped: each primary maqam heads a column of its compounds, and the
   columns are as many as fit - every maqam in view at once on any window
   the studio is used on, the box scrolling inside itself below that. */
.maqam-picker.is-grouped { width: min(940px, calc(100vw - 16px)); }
.maqam-picker.is-grouped .maqam-grid { display: block; columns: 150px auto; column-gap: 8px; }
.maqam-family { break-inside: avoid; display: flex; flex-direction: column; gap: 3px; margin-bottom: 8px; padding: 4px; border-radius: 8px; background: color-mix(in srgb, var(--surface-2) 55%, transparent); }
.maqam-family-name { font-size: 0.68rem; color: var(--text-muted); padding: 0 4px; }
.maqam-picker.is-grouped .maqam-option { min-height: 28px; padding: 3px 7px; }
.maqam-option.is-primary { font-weight: 650; border-width: 1.5px; }
.maqam-picker.is-grouped .maqam-option:not(.is-primary) { margin-inline-start: 10px; }
.maqam-lesson { font: 0.64rem var(--mono); color: var(--text-muted); }
.maqam-sequence { display: inline-flex; align-items: center; gap: 3px; }
.maqam-sequence .maqam-order { font-size: 0.64rem; min-width: 14px; height: 14px; }

/* ===== the archive and its review (docs/archive.md) =====
   One colour per answer to "whose is this, and how sure": a person's
   breath is the accent (solid, ticked); the machine's is dashed, in the
   colour of its least sure part - danger for low, gold for medium, the
   quiet border for high. Tokens of tokens, so both themes follow. */
:root {
  --review-low: var(--danger);
  --review-medium: var(--gold);
  --review-high: var(--border-strong);
  --review-human: var(--accent);
}

/* -- the archive screen -- */
.ar-split { align-items: start; }
.ar-main { min-width: 0; display: flex; flex-direction: column; }
/* the bar is the library's (.lib-bar); the list scrolls inside, so the bar need not stick */
.ar-bar { position: static; margin-top: -6px; }
.ar-qari { border: 0; background: none; padding: 0 3px; margin: 0 -3px; font: inherit; color: inherit; cursor: pointer; border-radius: 4px; }
.ar-qari:hover { color: var(--accent); background: var(--accent-soft); }
.ar-qari.is-active { color: var(--accent-strong); font-weight: 700; }
.ar-list {
  position: relative;
  height: calc(100dvh - 300px - var(--studio-player-height, 0px));
  min-height: 320px;
  overflow: auto;
  overscroll-behavior: contain;
  border: 1px solid var(--border);
  border-radius: var(--radius);
  background: var(--surface-2);
}
.ar-list:focus-visible { outline: 2px solid var(--band-blue); outline-offset: 2px; }
.ar-spacer { width: 1px; }
.ar-window { position: absolute; inset: 0 0 auto 0; padding: 2px 4px; }
.ar-loading, .ar-empty { padding: 18px; }
.ar-row {
  display: grid;
  grid-template-columns: 92px minmax(0, 1fr) 30px auto;
  align-items: center;
  gap: 10px;
  margin: 2px 0;
  padding: 0 10px;
  border-radius: var(--radius-sm);
  border: 1px solid transparent;
  background: var(--surface);
  cursor: pointer;
}
.ar-row:hover { border-color: var(--border-strong); }
.ar-row.is-on { border-color: var(--band-blue); background: var(--band-blue-soft); }
.ar-row-main { min-width: 0; display: grid; gap: 2px; }
.ar-row-title { font-size: 0.86rem; font-weight: 650; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.ar-row-sub { display: flex; gap: 8px; align-items: center; font-size: 0.74rem; color: var(--text-muted); min-width: 0; overflow: hidden; white-space: nowrap; }
.ar-key { direction: ltr; unicode-bidi: isolate; }
.ar-row-meta { display: flex; gap: 8px; align-items: center; font-size: 0.74rem; color: var(--text-muted); white-space: nowrap; }
.ar-dur { min-width: 4.5em; text-align: end; font-variant-numeric: tabular-nums; }
.ar-status {
  display: inline-flex; justify-content: center;
  padding: 2px 8px; border-radius: 99px;
  font-size: 0.72rem; font-weight: 700; white-space: nowrap;
  background: var(--surface-2); color: var(--text-muted); border: 1px solid var(--border);
}
.ar-status.is-pre-annotated { color: var(--gold); border-color: color-mix(in srgb, var(--gold) 40%, var(--border)); }
.ar-status.is-in-review { color: var(--band-blue); border-color: color-mix(in srgb, var(--band-blue) 45%, var(--border)); }
.ar-status.is-reviewed { color: var(--accent); border-color: color-mix(in srgb, var(--accent) 45%, var(--border)); }
.ar-status.is-promoted { background: var(--accent-soft); color: var(--accent-strong); border-color: transparent; }
.ar-status.is-missing, .ar-status.is-undecodable { color: var(--danger); border-style: dashed; }
.ar-q { padding: 1px 7px; border-radius: 99px; background: var(--surface-2); color: var(--text-muted); }
.ar-q.is-ayat { background: var(--danger-soft); color: var(--danger); }
.ar-q.is-ranges { background: var(--gold-soft); color: var(--gold); }
.ar-q.is-offdisk .ic { width: 14px; height: 14px; }
.ar-progress { width: 52px; height: 6px; border-radius: 99px; background: var(--surface-3); overflow: hidden; }
.ar-progress-bar { display: block; height: 100%; background: var(--review-human); }
.ar-panel .panel-head { gap: 10px; }
.ar-who { display: flex; gap: 12px; align-items: center; margin-bottom: 14px; }
.ar-photo { width: 64px; height: 64px; border-radius: 12px; object-fit: cover; flex: none; background: var(--surface-2); }
.ar-actions { gap: 8px; }
.ar-actions .btn kbd { margin-inline-start: auto; font-size: 0.7rem; opacity: 0.8; }
.ar-ranges .hstack { margin-top: 8px; gap: 6px; }
.ar-writes { margin: 0; padding-inline-start: 1.2em; display: grid; gap: 4px; font-size: 0.78rem; }
.ar-offdisk { color: var(--danger); }
@media (max-width: 1180px) { .ar-list { height: 60dvh; } }

/* -- the editor's review bar and card -- */
.tm-review[hidden] { display: none; }
.tm-review {
  display: grid; gap: 6px;
  padding: 8px 14px;
  border-bottom: 1px solid var(--border);
  background: color-mix(in srgb, var(--band-blue) 5%, var(--surface));
}
.tm-review-main { display: flex; flex-wrap: wrap; gap: 8px 14px; align-items: center; font-size: 0.82rem; }
.tm-review-progress { display: inline-flex; gap: 8px; align-items: center; }
.tm-review-meter { width: 110px; height: 7px; border-radius: 99px; background: var(--surface-3); overflow: hidden; }
.tm-review-meter > span { display: block; height: 100%; background: var(--review-human); transition: width 0.2s ease; }
.tm-review-open { display: inline-flex; gap: 6px; flex-wrap: wrap; }
.tm-review-q { padding: 1px 8px; border-radius: 99px; background: var(--surface-2); font-size: 0.76rem; font-weight: 650; }
.tm-review-q.is-ayat { background: var(--danger-soft); color: var(--danger); }
.tm-review-done { display: inline-flex; gap: 5px; align-items: center; color: var(--accent); font-weight: 650; }
.tm-review-done .ic { width: 16px; height: 16px; }
.tm-review-here { display: inline-flex; gap: 6px; align-items: center; color: var(--text-muted); }
.tm-review-actions { margin-inline-start: auto; gap: 6px; }
.tm-review-ranges {
  display: flex; flex-wrap: wrap; gap: 8px; align-items: center;
  padding: 6px 10px; border-radius: var(--radius-sm);
  background: var(--gold-soft); color: var(--text); font-size: 0.82rem;
}
.tm-review-ranges .ic { color: var(--gold); }
.tm-level { padding: 0 7px; border-radius: 99px; font-size: 0.74rem; font-weight: 700; border: 1px solid currentColor; }
.tm-level.is-low { color: var(--review-low); }
.tm-level.is-medium { color: var(--review-medium); }
.tm-level.is-high { color: var(--text-muted); }

.tm-review-card {
  display: grid; gap: 6px;
  margin: 0 0 12px; padding: 10px 12px;
  border-radius: var(--radius-sm);
  border: 1px dashed var(--review-high);
  border-inline-start: 4px solid var(--review-high);
  font-size: 0.82rem;
}
.tm-review-card.is-low { border-color: var(--review-low); background: var(--danger-soft); }
.tm-review-card.is-medium { border-color: var(--review-medium); background: var(--gold-soft); }
.tm-review-card.is-human { border-style: solid; border-color: var(--review-human); background: var(--accent-soft); }
.tm-review-who { display: flex; gap: 8px; align-items: center; }
.tm-review-who .ic { width: 18px; height: 18px; }
.tm-review-qs { display: flex; flex-wrap: wrap; gap: 6px; }
.tm-review-qbox { padding: 1px 8px; border-radius: 99px; background: var(--surface); border: 1px solid var(--border); font-variant-numeric: tabular-nums; }
.tm-review-qbox.is-low { border-color: var(--review-low); color: var(--review-low); font-weight: 700; }
.tm-review-qbox.is-medium { border-color: var(--review-medium); color: var(--review-medium); font-weight: 700; }
.tm-review-qbox.is-quiet { color: var(--text-muted); }
.tm-review-row { display: grid; grid-template-columns: 6.5em 1fr; gap: 8px; align-items: baseline; }
.tm-review-transcript { font-family: var(--quran-font, inherit); font-size: 1rem; line-height: 1.7; }
.tm-review-was { display: flex; flex-wrap: wrap; gap: 6px; align-items: baseline; }

/* the breaths list: whose each breath is */
.tm-meta-who {
  display: inline-grid; place-items: center;
  min-width: 1.6em; padding: 0 5px; border-radius: 99px;
  font-size: 0.7rem; font-weight: 800; line-height: 1.5;
  border: 1px dashed currentColor;
}
.tm-meta-who.is-human { color: var(--review-human); border-style: solid; }
.tm-meta-who.is-low { color: var(--review-low); }
.tm-meta-who.is-medium { color: var(--review-medium); }
.tm-meta-who.is-high { color: var(--text-muted); }
.ann-breath[data-by="machine"] { border-style: dashed; }
.ann-breath[data-by="machine"][data-level="low"] { border-inline-start: 3px solid var(--review-low); }
.ann-breath[data-by="machine"][data-level="medium"] { border-inline-start: 3px solid var(--review-medium); }
.ann-breath[data-by="human"] { border-inline-start: 3px solid var(--review-human); }

/* phrases: «+ فاصلهٔ سوره» lit where a surah ends or begins, and the
   non-Quran caption over the mushaf - never mushaf words */
.tm-transition.is-suggested { border-color: var(--gold); color: var(--gold); font-weight: 700; }
.tm-phrase-caption[hidden] { display: none; }
.tm-phrase-caption {
  display: flex; gap: 8px; align-items: center; justify-content: center; flex-wrap: wrap;
  padding: 5px 10px;
  border-bottom: 1px dashed var(--border-strong);
  background: var(--surface-2);
  color: var(--band-rose);
  font-size: 0.86rem; font-weight: 650;
}
.tm-phrase-caption .tm-phrase-mark { color: var(--text-muted); font-weight: 500; }
.tm-phrase-caption .is-sounding { text-decoration: underline; text-underline-offset: 4px; }
.insp-archive-note { font-size: 0.78rem; margin-top: 8px; }

/* ===== committing what the studio saves (ui/autogit.js) ===== */
.autogit-btn { position: relative; }
.autogit-btn.is-on { color: var(--accent); }
.autogit-btn.is-off { color: var(--text-muted); opacity: 0.75; }
.autogit-btn.is-off::after {
  content: ""; position: absolute; inset: 50% 6px auto; height: 1.5px;
  background: currentColor; transform: rotate(-35deg);
}
.autogit-btn.has-error { color: var(--danger); }
.autogit-badge {
  position: absolute; top: -2px; inset-inline-end: -2px;
  min-width: 16px; height: 16px; padding: 0 4px; border-radius: 99px;
  background: var(--gold); color: var(--on-accent, #fff);
  font-size: 0.66rem; font-weight: 800; line-height: 16px; text-align: center;
}
.autogit-badge[hidden] { display: none; }
.autogit-panel { gap: 14px; min-width: min(640px, 80vw); }
.autogit-panel h4 { margin: 0 0 6px; font-size: 0.84rem; }
.autogit-switch { display: flex; gap: 10px; align-items: flex-start; cursor: pointer; }
.autogit-switch input { width: 18px; height: 18px; margin-top: 3px; accent-color: var(--accent); }
.autogit-switch-text { display: grid; gap: 2px; font-size: 0.86rem; }
.autogit-list { margin: 0; padding: 0; list-style: none; display: grid; gap: 4px; max-height: 30vh; overflow: auto; }
.autogit-list li { display: flex; flex-wrap: wrap; gap: 8px; align-items: baseline; font-size: 0.8rem; }
.autogit-list code { direction: ltr; unicode-bidi: isolate; font-size: 0.76rem; }

/* the recording's ayat: fetch, organise, infer (views/inspector.js) */
.insp-quran-tools { gap: 6px; margin: 6px 0; }
.insp-infer-cols { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; }
.insp-infer-cols pre { margin: 0; padding: 8px; background: var(--surface-2); border-radius: var(--radius-sm); max-height: 40vh; overflow: auto; font-size: 0.8rem; }
.tm-review-range { cursor: pointer; font-family: var(--mono); direction: ltr; }
.tm-review-range .ic, .ar-range .ic { width: 13px; height: 13px; }
.ar-range { font-family: var(--mono); direction: ltr; text-decoration: none; }
.ar-taste { width: 28px; height: 28px; }
.ar-sampler { display: flex; flex-wrap: wrap; gap: 6px; align-items: center; margin-bottom: 12px; }
/* trim suggestions in the review bar (studio/shared/trim-suggest.mjs) */
.tm-review-trims {
  display: flex; flex-wrap: wrap; gap: 8px; align-items: center;
  padding: 6px 10px; border-radius: var(--radius-sm);
  background: color-mix(in srgb, var(--band-rose) 10%, var(--surface)); font-size: 0.82rem;
}
.tm-review-trims > .ic { color: var(--band-rose); }
.tm-trim-sugg { display: inline-flex; gap: 4px; align-items: center; }
.tm-trim-sugg .chip .ic { width: 13px; height: 13px; }

/* ===== annotation versions (an archive recording's; docs/archive.md "Versions") ===== */
.tm-version { cursor: pointer; gap: 6px; }
.tm-version .ic { width: 15px; height: 15px; }
.tm-version[data-kind="hand"] { border-color: color-mix(in srgb, var(--accent) 45%, var(--border)); color: var(--accent-strong); }
.tm-version[data-kind="machine"] { border-color: color-mix(in srgb, var(--band-violet) 45%, var(--border)); color: var(--band-violet); }
.tm-version-more { font-size: 0.7rem; color: var(--text-muted); }
.tm-compare {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 7px 14px;
  font-size: 0.82rem;
  background: color-mix(in srgb, var(--band-violet) 10%, var(--surface));
  border-bottom: 1px solid color-mix(in srgb, var(--band-violet) 35%, var(--border));
}
.tm-compare[hidden] { display: none; }
.tm-compare > .ic { width: 17px; height: 17px; color: var(--band-violet); flex: none; }
.tm-vers { display: flex; flex-direction: column; gap: 8px; }
.tm-ver {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 10px 12px;
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  background: var(--surface);
}
.tm-ver.is-live { border-color: var(--accent); box-shadow: inset 3px 0 0 var(--accent); background: color-mix(in srgb, var(--accent-soft) 45%, var(--surface)); }
.tm-ver-icon { display: grid; place-items: center; width: 34px; height: 34px; border-radius: 99px; background: var(--surface-2); flex: none; }
.tm-ver[data-kind="machine"] .tm-ver-icon { color: var(--band-violet); }
.tm-ver[data-kind="hand"] .tm-ver-icon { color: var(--accent); }
.tm-ver-icon .ic { width: 18px; height: 18px; }
.tm-ver-main { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 2px; }
.tm-ver-name { display: flex; align-items: center; gap: 7px; flex-wrap: wrap; }
.tm-ver-live { font-size: 0.7rem; font-weight: 800; color: var(--on-accent); background: var(--accent); border-radius: 99px; padding: 1px 9px; }
.tm-ver-tag { font-size: 0.7rem; color: var(--text-muted); border: 1px solid var(--border-strong); border-radius: 99px; padding: 0 8px; }
.tm-ver-meta, .tm-ver-diff { font-size: 0.76rem; color: var(--text-muted); }
.tm-ver-diff { font-variant-numeric: tabular-nums; }
.tm-ver-actions { display: flex; gap: 6px; flex: none; }

/* ===== the automatic «نرمال‌سازی» (ui/effects-rack.js, shared/normalize.mjs) ===== */
.fx-normalize { display: flex; flex-direction: column; gap: 10px; }
.fx-nrm-basis { line-height: 1.8; }
.fx-nrm-badge { display: inline-block; font-size: 0.7rem; font-weight: 800; border-radius: 99px; padding: 0 8px; margin-inline-end: 4px; }
.fx-nrm-badge.is-auto { background: var(--band-blue-soft); color: var(--band-blue); }
.fx-nrm-badge.is-hand { background: var(--gold-soft); color: var(--gold); }
.fx-nrm-row { display: flex; align-items: baseline; gap: 10px; flex-wrap: wrap; }
.fx-nrm-label { font-size: 0.76rem; font-weight: 800; color: var(--text-muted); min-width: 72px; }
.fx-trash-gap { width: 32px; flex: none; }
/* the archive list: what the measurements say is wrong with the sound */
.ar-audio.is-error { color: var(--danger); border-color: color-mix(in srgb, var(--danger) 45%, var(--border)); }
.ar-audio.is-warn { color: var(--gold); border-color: color-mix(in srgb, var(--gold) 45%, var(--border)); }
.tm-version[hidden] { display: none; }

/* «?» beside an entity (js/help.js), in the editor both studios share */
.help-q { display: inline-grid; place-items: center; width: 18px; height: 18px; margin-inline: 4px; padding: 0; border-radius: 50%; border: 1px solid var(--border, #ccc); background: var(--surface, #fff); color: var(--accent, #3c5fb8); font: 700 11px/1 inherit; cursor: help; vertical-align: middle; }
.help-pop { position: absolute; z-index: 60; padding: 10px 12px; border-radius: 12px; background: var(--surface, #fff); border: 1px solid var(--border, #ccc); box-shadow: 0 10px 30px rgb(0 0 0 / 0.18); font-size: 0.9rem; }
.help-pop p { margin: 6px 0 0; }
.help-pop-head { display: flex; justify-content: space-between; align-items: center; gap: 8px; }
.help-pop-k { font-weight: 600; }
