[x-cloak] { display: none !important; }

/* Thin header — brand + tiny progress strip. Override Pico's generous
   nav padding so the chrome is as low as possible. Sticky so the
   sidebar toggle + chapter-position strip stay reachable while
   scrolling through long chapters. */
header.container {
  padding-top: 0.15rem;
  padding-bottom: 0.15rem;
  border-bottom: 1px solid var(--pico-muted-border-color);
  margin-bottom: 0.5rem;
  position: sticky;
  top: 0;
  z-index: 90;
  background: var(--pico-background-color);
}
header.container nav {
  padding: 0;
  min-height: 0;
  /* Pico's nav defaults to space-between; with our single <ul> child
     the ul ends up flush-left. Override to center the ul horizontally
     inside the header strip. */
  justify-content: center;
}
header.container nav ul {
  margin: 0;
  padding: 0;
  /* Centered: brand + language pair + chapter-progress sit in the
     middle of the header strip. The sidebar toggle was moved to a
     fixed-position element on the left edge so it stays reachable
     and doesn't tug the rest of the header off-center. */
  justify-content: center;
  gap: 0.6rem;
}
header.container nav ul li {
  padding: 0.05rem 0;
}

header.container .brand {
  text-decoration: none;
}
header.container .brand strong {
  font-size: 1rem;
}
header.container .lang-pair small {
  color: var(--pico-muted-color);
  font-size: 0.8rem;
  white-space: nowrap;
}
/* Compact chapter-position indicator: "27/70" + a tiny progress bar.
   The bar is purely cosmetic — exact numbers are in the text. */
header.container .chapter-progress {
  display: flex;
  align-items: center;
  gap: 0.4rem;
}
header.container .chapter-progress small {
  font-variant-numeric: tabular-nums;
  font-size: 0.8rem;
  color: var(--pico-muted-color);
}
header.container .chapter-progress progress {
  width: 80px;
  height: 5px;
  margin: 0;
}

/* Header "✓ Saved" badge — fades in briefly after every successful
   persistNow, fades out under JS control after SAVE_BADGE_MS. The
   transition only applies to the show direction (no exit animation)
   because x-show toggles `display: none`, which can't be transitioned. */
.save-indicator {
  color: var(--pico-color-green-600, #2c8c3a);
  opacity: 0.85;
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
  transition: opacity 0.2s ease-in;
}

/* Apply rules — three-column grid: translation (left) | buttons
   (middle) | original (right). The translation column carries the
   current paragraph and, on diff rows, the suggestion stacked
   beneath it. The middle column holds the accept/reject buttons
   when a suggestion is pending and stays empty otherwise — its
   width is fixed so non-diff rows keep the same layout, just with
   nothing in the middle slot. */
.rules-view .rules-paragraphs {
  margin-top: 1rem;
  /* 3-col template: prose | icon-button column | prose. The middle
     column is just wide enough for a single ✓/✕ icon button (~2.5rem
     including padding), eaten symmetrically out of the two prose
     columns so they keep the editor's --split-pct ratio. */
  grid-template-columns:
    calc(var(--split-pct, 50%) - 1.5rem)
    2.5rem
    calc(100% - var(--split-pct, 50%) - 1.5rem);
}

/* Explicit column placement so non-diff rows (which only emit
   left + right + an empty middle marker) still land in cols 1
   and 3, leaving col 2 visually empty. */
.rules-view .rules-col-left  { grid-column: 1; }
.rules-view .rules-col-mid   { grid-column: 2; }
.rules-view .rules-col-mid-empty {
  grid-column: 2;
  /* Marker only — the empty middle column needs an explicit grid
     item so the next sibling lands in col 3 instead of col 2.
     Zero size so it doesn't shift the row vertically. */
  width: 0; height: 0; overflow: hidden;
}
.rules-view .rules-col-right { grid-column: 3; }

/* Left column wrap: current on top, suggestion stacked below it
   on diff rows. Spacing between is set by the suggestion's margin
   so non-diff rows don't get an extra gap. */
.rules-view .rules-wrap {
  display: flex;
  flex-direction: column;
}

/* Diff highlighting is intentionally restrained: text reads as
   prose, with just enough marking to spot the before-vs-after
   boundary. Current gets a thin red gutter; suggestion gets a
   thin yellow gutter. No fills — backgrounds drowned out short
   paragraphs and made the column look like an alert box. */
.rules-view .rules-pair.has-diff .rules-current {
  border-left: 3px solid var(--pico-color-red-400, #e58383);
  padding-left: 0.6rem;
}
.rules-view .rules-suggestion {
  margin-top: 0.6rem;
  border-left: 3px solid var(--pico-color-yellow-500, #d4a700);
  padding-left: 0.6rem;
  white-space: pre-wrap;
}

/* Rules-prompt textarea: rules can be long (multi-paragraph editorial
   instructions), and a hard-capped rows="6" made half the text scroll
   out of view while typing. `field-sizing: content` (widely supported
   in modern Chromium / Firefox / Safari) lets the box grow with the
   text; `min-height` gives it a comfortable empty baseline; `max-height`
   + `overflow-y: auto` cap runaway growth so a very long prompt scrolls
   internally instead of pushing the Run button off-screen.

   Width: cap the default at a comfortable reading measure (~70ch)
   instead of stretching the textarea across the full editor-main
   column, which on wide screens produced 150+ character lines that
   were hard to scan while writing rules. `resize: both` keeps the
   native drag handle so the user can pull the box wider (up to the
   parent's width) or narrower if 70ch isn't quite right for their
   font-size / screen. min-width guards against accidental collapse. */
.rules-view .rules-prompt {
  field-sizing: content;
  min-height: 10em;
  max-height: 60vh;
  overflow-y: auto;
  width: 100%;
  max-width: 70ch;
  min-width: 30ch;
  resize: both;
  line-height: 1.5;
}

/* Buttons stack vertically inside the middle column so the
   column stays narrow regardless of label length. align-self:
   center on the col-mid item itself centers the button stack
   VERTICALLY in the row — so on diff rows they sit literally
   between the current (top of left col) and the suggestion
   (bottom of left col). Equi-width via column's fixed track +
   width: 100% on each button so labels never look ragged. */
.rules-view .rules-actions {
  align-self: center;
  display: flex;
  flex-direction: column;
  gap: 0.3rem;
  align-items: center;
  justify-content: center;
  padding: 0;
}
/* Square-ish icon buttons: just the symbol, no label, fixed width
   so accept and reject align vertically. Slightly larger font so
   the ✓ / ✕ glyphs are readable at this size. */
.rules-view .rules-actions button.tiny {
  width: 2rem;
  height: 1.7rem;
  padding: 0;
  font-size: 0.95rem;
  line-height: 1;
  margin: 0;
  min-height: 0;
  text-align: center;
  font-variant-numeric: tabular-nums;
}

/* Glossary provenance column: a short row of chapter links. Narrow and
   non-wrapping so it can't stretch the table — the term/translation
   columns are the ones that need the width. */
/* Колонка пола — одна буква; растягивать её на равную долю таблицы
   незачем. */
.gloss-table td.gloss-gender {
  width: 1%;
  white-space: nowrap;
}
.gloss-table td.gloss-gender select {
  width: auto;
  margin: 0;
  padding: 0.2rem 1.4rem 0.2rem 0.4rem;
}

.gloss-table td.gloss-chapters {
  white-space: nowrap;
  font-size: 0.85rem;
  vertical-align: middle;
  width: 1%;
}
.gloss-table td.gloss-chapters a {
  display: inline-block;
  padding: 0 0.25rem;
  font-variant-numeric: tabular-nums;
}
.gloss-table td.gloss-chapters .muted {
  color: var(--pico-muted-color);
  margin-left: 0.15rem;
}

/* Bulk accept/reject bar, repeated above and below the grid. Kept to
   its content width and left-aligned: Pico's role="group" would
   otherwise stretch "Accept all" across the whole editor, which is a
   very large target for an action that rewrites the entire chapter. */
.rules-view .rules-bulk {
  display: flex;
  gap: 0.5rem;
  width: auto;
}
.rules-view .rules-bulk button {
  width: auto;
  margin: 0;
  padding: 0.3rem 0.9rem;
  font-size: 0.85rem;
}

/* Sidebar marker: tiny dot next to a chapter title when it has a
   pending Apply-rules pass. Lets the user spot pending work without
   clicking through every chapter. */
.rules-view .sidebar .rules-pending-mark {
  color: var(--pico-color-yellow-500, #d4a700);
  margin-left: 0.25rem;
  font-size: 0.7rem;
  vertical-align: super;
}
.rules-view article.warning {
  border-left: 4px solid var(--pico-color-yellow-500, #d4a700);
  background: var(--pico-color-yellow-50, #fffbe6);
  padding: 0.6rem 0.8rem;
  margin-top: 0.75rem;
}

/* Editor status bar — tiny, fixed at the bottom of the viewport,
   always visible while editing. Shows total chars / chars left /
   time left for the current chapter. Stays out of the way (low
   contrast, single line, narrow padding) so it doesn't crowd the
   editor; widens to full width so the readout is centred and
   doesn't shift when its numbers change. */
/* Apply-rules: floating "next suggestion" button. Sits in the
   bottom-right (just above the editor progress dial would be, but
   since the dial only shows in editor view, they don't collide).
   Primary-tinted so it stands out as a navigational action. */
.next-suggestion {
  position: fixed;
  right: 0.6rem;
  bottom: 0.6rem;
  z-index: 11;
  padding: 0.4rem 0.85rem;
  font-size: 0.8rem;
  line-height: 1.3;
  margin: 0;
  min-height: 0;
  width: auto;
  white-space: nowrap;
  border-radius: 4px;
  filter: drop-shadow(0 1px 3px rgba(0, 0, 0, 0.12));
}

/* Editor progress dial — fixed-position circular indicator, right
   edge of the viewport. Arc fills with chapter scroll progress;
   center text is the estimated minutes-until-finished. Hover for
   the full chars-left + rate breakdown via title attr. */
.progress-dial {
  position: fixed;
  right: 0.6rem;
  bottom: 0.6rem;
  width: 60px;
  height: 60px;
  z-index: 10;
  display: flex;
  align-items: center;
  justify-content: center;
  pointer-events: auto;
  font-variant-numeric: tabular-nums;
  filter: drop-shadow(0 1px 3px rgba(0, 0, 0, 0.08));
}
.progress-dial-svg {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  transform: rotate(-90deg);   /* arc starts at 12 o'clock */
}
.progress-dial-track,
.progress-dial-arc {
  fill: var(--pico-background-color, #fff);
  stroke-width: 3;
}
.progress-dial-track {
  stroke: var(--pico-muted-border-color, #ddd);
}
.progress-dial-arc {
  stroke: var(--pico-primary, #4f46e5);
  stroke-linecap: round;
  transition: stroke-dasharray 0.18s ease-out;
}
.progress-dial-text {
  position: relative;
  z-index: 1;
  font-size: 0.78rem;
  color: var(--pico-color);
  text-align: center;
}

/* Header chars/min rolling-rate badge — sits next to the chapter count
   and the save indicator. Same tabular-nums treatment so the digits
   don't jump as the rate ticks up; muted so it reads as ambient info,
   not an alert. */
.rate-indicator {
  opacity: 0.7;
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}

/* Stats view tables: dense, right-aligned numerics, the same chapter
   status dot the editor sidebar uses. Pico's default <table> styles are
   light-touch enough that we mostly just need the numeric alignment. */
.stats-view table { margin-bottom: 1.5rem; }
.stats-view td.num,
.stats-view th.num { text-align: right; font-variant-numeric: tabular-nums; }
.stats-view .stats-summary {
  margin-top: 0.5rem;
  padding-top: 0.75rem;
  border-top: 1px solid var(--pico-muted-border-color, #ddd);
}

:root {
  --pico-font-size: 16px;
}

main.container-fluid { padding: 1rem 1.5rem; }

article[role="alert"] {
  border-left: 4px solid var(--pico-color-red-500);
  background: var(--pico-color-red-50);
  color: var(--pico-color-red-900);
  display: flex;
  align-items: center;
  gap: 0.5rem;
}

.gloss-progress {
  margin-top: 0.75rem;
}
.gloss-progress small { opacity: 0.75; }
.gloss-progress progress {
  width: 100%;
  margin: 0.25rem 0 0;
}

article[role="alert"] .close {
  margin-left: auto;
  background: transparent;
  border: none;
  color: inherit;
  font-size: 1.25rem;
  cursor: pointer;
  padding: 0 0.5rem;
  width: auto;
}

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

.editor-grid {
  display: grid;
  grid-template-columns: 220px 1fr;
  gap: 1rem;
  align-items: start;
}

/* Translation-only view mode — distraction-free single-column editing.
   Glossary aside is hidden via x-show in the markup; the editor-main
   column expands to fill the row, and the inner `.paragraphs` grid
   below collapses to one column + max-width center for Word-like
   reading width. Toggle persists per-user via prefs (translationOnly).
   See component.js + admin/i18n keys editor.translation_only.*. */
.editor-grid.translation-only-mode {
  grid-template-columns: 1fr;
}
.paragraphs.translation-only {
  grid-template-columns: 1fr;
  /* Width is user-adjustable via the right-edge handle
     (.translation-only-handle); JS sets `--translation-only-width` as
     a percent of the enclosing .editor-grid. 55% ≈ the pre-handle
     760px default on a typical 1400px viewport. */
  max-width: var(--translation-only-width, 55%);
  margin: 0 auto;
}
.paragraphs.translation-only .original {
  display: none;
}
/* The two-column split handle only makes sense when both columns are
   visible. Hide it in translation-only mode; the width handle below
   takes its place. */
.paragraphs.translation-only .resize-handle {
  display: none;
}

/* Width handle for translation-only mode. Straddles the RIGHT edge of
   the centered column so pulling it outward widens the column
   symmetrically (margin: 0 auto keeps it centered). Same visual
   treatment as .resize-handle: thin muted line by default, primary
   accent on hover/drag. Only visible when translation-only mode is on. */
.translation-only-handle {
  display: none;
}
.paragraphs.translation-only .translation-only-handle {
  display: flex;
  position: absolute;
  top: 0;
  bottom: 0;
  right: 0;
  transform: translateX(50%);
  width: 1.25rem;
  cursor: col-resize;
  z-index: 5;
  align-items: stretch;
  justify-content: center;
  user-select: none;
}
.translation-only-handle::before {
  content: '';
  width: 1px;
  background: var(--pico-muted-border-color);
  opacity: 0.5;
  transition: opacity 0.15s, background 0.15s, width 0.15s;
}
.translation-only-handle:hover::before,
.translation-only-handle:active::before {
  width: 3px;
  background: var(--pico-primary);
  opacity: 0.7;
}

.sidebar {
  position: sticky;
  top: 1rem;
  border-right: 1px solid var(--pico-muted-border-color);
  padding-right: 0.75rem;
  max-height: calc(100vh - 2rem);
  overflow-y: auto;
}

.sidebar h4 {
  font-size: 0.72rem;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  opacity: 0.55;
  margin: 0 0 0.4rem 0;
  padding-left: 0.5rem;
}

.sidebar ul.chapters {
  list-style: none;
  padding: 0;
  margin: 0 0 0.75rem 0;
}

.sidebar ul.chapters li a {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 0.4rem;
  padding: 0.18rem 0.5rem;
  border-radius: 3px;
  text-decoration: none;
  color: var(--pico-color);
  font-size: 0.78rem;
  line-height: 1.35;
}

.sidebar ul.chapters li a:hover { background: var(--pico-secondary-background); }
.sidebar ul.chapters li a.active { background: var(--pico-primary); color: var(--pico-primary-inverse); }
.sidebar ul.chapters li a.disabled { opacity: 0.45; pointer-events: none; }

.sidebar .chapter-title {
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  flex: 1;
  min-width: 0;
}

/* Status as a small dot — pending = empty ring, translated = half opacity
   filled, accepted = fully filled. Inherits currentColor so it stays
   readable on the active (primary-background) row. */
.chapter-status {
  display: inline-block;
  width: 7px;
  height: 7px;
  border-radius: 50%;
  border: 1.5px solid currentColor;
  flex-shrink: 0;
  background: transparent;
  opacity: 0.6;
}
.chapter-status.translated {
  background: currentColor;
  opacity: 0.45;
}
.chapter-status.accepted {
  background: currentColor;
  opacity: 1;
}

/* Sidebar glossary — vertically stacked entries so each input gets the
   full sidebar width (the previous side-by-side table squeezed words to
   illegibility at 180px). Filtered to the current chapter's subset; the
   full Glossary view is for editing the rest. */
.sidebar details > summary { font-size: 0.85rem; }
.sidebar details .gloss-count {
  opacity: 0.55;
  font-weight: normal;
  margin-left: 0.3rem;
}
.sidebar .gloss-hint {
  font-size: 0.7rem;
  opacity: 0.55;
  margin: 0.3rem 0 0.5rem 0;
  line-height: 1.3;
}

/* "+ term" button on top — compact, full-width so it lines up
   with the filter input below it. */
.sidebar .gloss-add {
  width: 100%;
  margin: 0 0 0.4rem 0;
  padding: 0.25rem 0.6rem;
  font-size: 0.75rem;
}

/* Live text filter — case-insensitive substring match over each
   entry's term / translation / notes / originalForm. Sits between
   the add-term button and the list. */
.sidebar .gloss-filter {
  width: 100%;
  margin: 0 0 0.5rem 0;
  padding: 0.2rem 0.4rem;
  font-size: 0.75rem;
  height: auto;
  min-height: 0;
  line-height: 1.4;
  border-radius: 2px;
}

.sidebar .gloss-mini {
  display: flex;
  flex-direction: column;
  gap: 0.55rem;
  margin: 0.4rem 0;
}

.sidebar .gloss-mini-entry {
  display: flex;
  flex-direction: column;
  gap: 0.15rem;
  padding-bottom: 0.45rem;
  border-bottom: 1px dotted var(--pico-muted-border-color);
}

.sidebar .gloss-mini input {
  width: 100%;
  /* Размер настраивается наравне с колонками оригинала и перевода.
     Прежнее жёсткое 0.75rem осталось запасным значением. */
  font-size: var(--glossary-font-size, 0.75rem);
  padding: 0.18rem 0.35rem;
  margin: 0;
  border: 1px solid var(--pico-muted-border-color);
  background: var(--pico-background-color);
  border-radius: 2px;
  height: auto;
  min-height: 0;
  line-height: 1.4;
}
/* Term + translation columns mirror the source / translation panes:
   term in muted (source-side), translation in default body color
   (translation-side) — same scheme as `.original` / `.translation`. */
/* Перевод и пол стоят в одной строке: пол — это одна буква, и
   отдельная строка под неё съела бы высоту панели, где статей сотни. */
.sidebar .gloss-mini-row {
  display: flex;
  align-items: center;
  gap: 0.2rem;
}
.sidebar .gloss-mini-row input {
  flex: 1 1 auto;
  min-width: 0;
}
.sidebar .gloss-mini select.gloss-mini-gender {
  flex: 0 0 auto;
  width: auto;
  font-size: var(--glossary-font-size, 0.75rem);
  padding: 0.18rem 0.9rem 0.18rem 0.35rem;
  margin: 0;
  height: auto;
  min-height: 0;
  line-height: 1.4;
  background-position: right 0.2rem center;
  background-size: 0.6rem auto;
}

.sidebar .gloss-mini input.gloss-mini-term {
  color: var(--pico-muted-color);
}
.sidebar .gloss-mini input.gloss-mini-trans {
  color: var(--pico-color);
}

.sidebar .gloss-mini button {
  margin-top: 0.4rem;
  padding: 0.25rem 0.6rem;
  font-size: 0.75rem;
  width: auto;
}

/* ---------- A/B test view ---------- */

.abtest-running .ab-source {
  background: var(--pico-card-background-color);
  border-left: 3px solid var(--pico-muted-border-color);
  padding: 0.75rem 1rem;
  margin-bottom: 1rem;
}
.abtest-running .ab-source header { margin: 0 0 0.25rem 0; opacity: 0.6; }

.abtest-running .ab-pair {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 1rem;
}
.abtest-running .ab-side {
  margin: 0;
  padding: 0.85rem 1rem;
  border: 1px solid var(--pico-muted-border-color);
  border-radius: 6px;
  background: transparent;
}
.abtest-running .ab-side header { margin: 0 0 0.4rem 0; }

.abtest-running .ab-text {
  white-space: pre-wrap;
  line-height: 1.55;
}
/* renderBlockMd splits source/A/B on blank lines into <p> elements;
   trim outer margins so the rendered block hugs the card padding. */
.abtest-running .ab-text > p { margin: 0 0 0.6em 0; }
.abtest-running .ab-text > p:last-child { margin-bottom: 0; }

.abtest-running .ab-buttons {
  margin-top: 1.25rem;
  display: flex;
  gap: 0.5rem;
  justify-content: center;
}
.abtest-running .ab-buttons button {
  flex: 1;
  max-width: 14rem;
}

.ab-reveal li { font-size: 0.85rem; line-height: 1.5; margin-bottom: 0.25rem; }

/* Bilingual setup: chapter-count mismatch warning. Pico's role="alert"
   defaults to error red; soften to amber for a non-blocking heads-up. */
article[role="alert"].warning {
  background: #fff8e1;
  border-color: #ffb74d;
  color: #855600;
}

@media (max-width: 700px) {
  .abtest-running .ab-pair { grid-template-columns: 1fr; }
}

/* Editor toolbar (chapter info + font-size selectors) sits above the
   paragraph grid. Flex row that wraps on narrow viewports. */
/* Chapter prev/next pair in the editor toolbar. Compact tiny buttons
   that sit on the left while the font-size controls float to the
   right (toolbar is space-between). */
.chapter-nav {
  display: flex;
  gap: 0.4rem;
  align-items: center;
}
.chapter-nav button {
  padding: 0.15rem 0.55rem;
  font-size: 0.75rem;
  line-height: 1.4;
  margin: 0;
  min-height: 0;
  width: auto;
  white-space: nowrap;
}

.editor-toolbar {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 1rem;
  margin-bottom: 0.4rem;
  flex-wrap: wrap;
}
.editor-toolbar .chapter-info { margin: 0; }
.font-size-controls {
  display: flex;
  gap: 0.75rem;
  align-items: center;
}
.font-size-controls label {
  display: flex;
  gap: 0.4rem;
  align-items: center;
  margin: 0;
  white-space: nowrap;
}
.font-size-controls select {
  margin: 0;
  padding: 0.15rem 0.4rem;
  font-size: 0.8rem;
  width: auto;
  min-width: 0;
  height: auto;
}
.font-size-controls small {
  color: var(--pico-muted-color);
  font-size: 0.72rem;
}

/* ---------- two-column paragraph editor ----------
   Reads like prose: no per-cell borders or backgrounds, no internal padding.
   Row-gap separates paragraphs visually; the boundary between the two columns
   is a draggable handle (.resize-handle) backed by --split-pct on this grid. */

.paragraphs {
  display: grid;
  grid-template-columns:
    calc(var(--split-pct, 50%) - 0.75rem)
    calc(100% - var(--split-pct, 50%) - 0.75rem);
  column-gap: 1.5rem;
  row-gap: 0.85rem;
  align-items: start;
  position: relative;
}

.original,
textarea.translation,
.translation.rendered {
  padding: 0;
  margin: 0;
  border: none;
  background: transparent;
  font-family: var(--pico-font-family);
  line-height: 1.6;
  /* Justify paragraphs by width — both source and translation columns
     read as proper prose blocks instead of left-ragged lines. */
  text-align: justify;
  hyphens: auto;
}

.original {
  white-space: pre-wrap;
  color: var(--pico-muted-color);
  font-size: var(--original-font-size, 1rem);
}

textarea.translation,
.translation.rendered {
  font-size: var(--translation-font-size, 1.15rem);
}

textarea.translation {
  width: 100%;
  field-sizing: content;
  resize: none;
  overflow: hidden;
  color: var(--pico-color);
  /* No visible border by default — the textarea blends into prose.
     Focus shows a subtle outline so the editor knows what's active. */
}
textarea.translation:focus {
  outline: 1px solid var(--pico-primary);
  outline-offset: 4px;
}

/* Rendered-markdown view of a translation paragraph. Identical typography
   to the textarea so the swap on click is invisible (no layout jump).
   Click anywhere inside to switch to the textarea editor. */
.translation.rendered {
  width: 100%;
  white-space: pre-wrap;
  cursor: text;
  min-height: 1.6em;
  /* Subtle hover-focus on rendered paragraphs — the click target needs
     SOME visible affordance even before the user mouses over the
     hover-revealed retranslate buttons. Theme-neutral gray so it
     reads on both light and dark Pico themes. */
  border-radius: 3px;
  transition: background-color 0.12s ease;
}
.translation.rendered:hover {
  background-color: rgba(127, 127, 127, 0.07);
}
.translation.rendered em { font-style: italic; }
.translation.rendered strong { font-weight: 600; }
.translation-placeholder {
  color: var(--pico-muted-color);
  opacity: 0.45;
  font-style: italic;
}

/* Same rendered styling on the (read-only) original side: inherit prose
   look, just italic / bold get their HTML treatment instead of literal
   asterisks in the source. */
.original em { font-style: italic; }
.original strong { font-weight: 600; }

/* Bold on the dark theme.
   600 reads clearly as dark type on a light page, but light-on-dark
   text blooms: the stroke thickens optically and the gap between 400
   and 600 closes until bold is almost indistinguishable from body
   text. The proofreader has to look for emphasis rather than see it.
   700 restores the weight difference, and on the translation side a
   brighter colour does the rest — Pico's dark body text is a light
   grey, so pushing emphasis toward white separates it without
   shouting. The original column keeps its muted colour on purpose;
   it is reference text, and brightening it would fight the two-column
   contrast that tells the panes apart. */
[data-theme="dark"] .translation.rendered strong,
[data-theme="dark"] .rules-suggestion strong,
[data-theme="dark"] .rules-current strong {
  font-weight: 700;
  color: #fff;
}
[data-theme="dark"] .original strong {
  font-weight: 700;
}
/* Same treatment for the editable side: a textarea can't render bold,
   but the glossary and title inputs can. */
[data-theme="dark"] .gloss-table strong,
[data-theme="dark"] .title-row strong {
  font-weight: 700;
}

/* Title row — bigger and separated from the body, but still no boxes. */
.title-row {
  font-size: 1.4rem;
  font-weight: 600;
  line-height: 1.3;
  margin-bottom: 0.75rem;
}
/* Title row colours inherit from the base .original / .translation
   rules: original-side title in muted (matches the source pane),
   translation-side title in the default color (matches the
   translation pane). No per-title-row color overrides. */
input.translation.title-row {
  font-size: 1.4rem;
  font-weight: 600;
  padding: 0;
  margin: 0;
  height: auto;
  min-height: 0;
  border: none;
  background: transparent;
}
input.translation.title-row:focus {
  outline: 1px solid var(--pico-primary);
  outline-offset: 4px;
}

/* Wraps the textarea + per-paragraph retranslate buttons; goes in the
   first grid column because `.pair` uses `display: contents`. */
.translation-wrap {
  display: flex;
  flex-direction: column;
  gap: 0.2rem;
}

.paragraph-actions {
  display: flex;
  gap: 0.35rem;
  justify-content: flex-end;
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.15s;
}
.translation-wrap:hover .paragraph-actions,
.translation-wrap:focus-within .paragraph-actions {
  opacity: 1;
  pointer-events: auto;
}

.paragraph-actions button.tiny {
  padding: 0.1rem 0.45rem;
  font-size: 0.7rem;
  line-height: 1.4;
  margin: 0;
  min-height: 0;
  width: auto;
  background: transparent;
  color: var(--pico-muted-color);
  border: 1px solid var(--pico-muted-border-color);
}
.paragraph-actions button.tiny:hover:not(:disabled) {
  color: var(--pico-primary);
  border-color: var(--pico-primary);
  background: transparent;
}
.paragraph-actions button.tiny:disabled {
  opacity: 0.4;
  cursor: not-allowed;
}

/* Drag handle for resizing the column split. Sits at the column boundary
   (left = --split-pct), absolutely positioned so it doesn't take a grid
   slot. Visible-but-faint by default; brightens on hover/active. */
.resize-handle {
  position: absolute;
  top: 0;
  bottom: 0;
  left: var(--split-pct, 50%);
  transform: translateX(-50%);
  width: 1.25rem;
  cursor: col-resize;
  z-index: 5;
  display: flex;
  align-items: stretch;
  justify-content: center;
  user-select: none;
}
.resize-handle::before {
  content: '';
  width: 1px;
  background: var(--pico-muted-border-color);
  opacity: 0.5;
  transition: opacity 0.15s, background 0.15s, width 0.15s;
}
.resize-handle:hover::before,
.resize-handle:active::before {
  width: 3px;
  background: var(--pico-primary);
  opacity: 0.7;
}

/* End-of-chapter action row: NOT sticky — surfaces only when the
   reader scrolls to the bottom of the chapter. Keeps the chapter
   reading experience uncluttered until a decision is needed. */
.editor-actions {
  margin-top: 1.5rem;
  display: flex;
  gap: 0.5rem;
  justify-content: flex-end;
  flex-wrap: wrap;
  padding: 1rem 0 2rem;
}

.editor-actions button { margin: 0; }

/* Почему кнопка не работает. Занимает всю ширину ряда, чтобы объяснение
   не жалось между кнопками, и стоит под ними — там, куда человек уже
   смотрит после неудачного нажатия. Раньше на этом месте было молчание,
   и погашенная кнопка читалась как поломка системы. */
.blocked-reason {
  flex-basis: 100%;
  text-align: right;
  color: var(--pico-muted-color);
  line-height: 1.4;
  padding-top: 0.35rem;
}
.blocked-reason strong {
  display: inline-block;
  width: 1.15rem;
  height: 1.15rem;
  line-height: 1.15rem;
  text-align: center;
  border-radius: 50%;
  margin-right: 0.35rem;
  background: #b07a34;
  color: #fff;
}

/* Project view — landing dashboard. Cards link to each section so
   the editor view can stay focused on the chapter being edited. */
.project-view hgroup p { color: var(--pico-muted-color); margin-top: 0.25rem; }
.project-cards {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
  gap: 0.75rem;
  margin: 1rem 0 1.5rem;
}
.project-card {
  display: block;
  padding: 1rem;
  border: 1px solid var(--pico-muted-border-color);
  border-radius: 4px;
  text-decoration: none;
  color: var(--pico-color);
  transition: border-color 0.12s, background-color 0.12s;
}
.project-card:hover {
  border-color: var(--pico-primary);
  background-color: rgba(127, 127, 127, 0.04);
}
.project-card.disabled {
  opacity: 0.45;
  pointer-events: none;
}
.project-card strong { display: block; }
.project-card small {
  color: var(--pico-muted-color);
  display: block;
  font-size: 0.78rem;
  line-height: 1.5;
}

/* Section title that introduces a row of project cards (Pipeline /
   Tools). Small, muted; double-acts as a hierarchy hint. */
.project-section-title {
  margin: 0.5rem 0 0.4rem;
  font-size: 0.75rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--pico-muted-color);
}

/* Numbered step badge in the top-right of pipeline cards (1, 2, 3).
   When the step is `checked` (Alpine class on the card), the same
   slot becomes a green ✓ — bigger, brighter — to show "this stage
   is done" at a glance. */
.project-card .step-num {
  position: absolute;
  top: 0.5rem;
  right: 0.6rem;
  font-size: 0.7rem;
  font-weight: 600;
  color: var(--pico-muted-color);
  letter-spacing: 0;
}
.project-card.checked {
  border-color: var(--pico-color-green-300, #9bd3a4);
}
.project-card .check-mark {
  color: var(--pico-color-green-600, #2c8c3a);
  margin-right: 0.25rem;
}
.project-card { position: relative; }

/* Progress bar inside the Editor card (accepted / total). */
.project-card .step-progress {
  display: block;
  width: 100%;
  height: 4px;
  margin-top: 0.4rem;
}

/* Project-view chapter list. Wider than the old sidebar copy, so it
   flows in a multi-column grid; reuses the .sidebar ul.chapters li a
   visual styling for the rows themselves. */
.chapters.project-chapters {
  list-style: none;
  padding: 0;
  margin: 0 0 1rem 0;
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
  gap: 0.15rem 0.4rem;
}
.project-actions {
  display: flex;
  gap: 0.5rem;
  flex-wrap: wrap;
  align-items: center;
}
.project-actions .project-save {
  margin-left: auto;
}

/* Setup view's Import-project button — sits above the book-input
   block so the user can either paste fresh markdown or restore a
   previously exported project. */
.setup-import {
  margin: 0.5rem 0 1rem;
}

/* Tiny hint shown under the source textareas in Setup once they're
   locked (glossary already built). Same muted style as Pico's <small>
   but italic to flag it as a status note rather than a label. */
.readonly-hint {
  font-style: italic;
  color: var(--pico-muted-color);
}

/* Bottom of Setup. Red-tinted outline so the destructive action is
   unmistakable; not solid-filled so it doesn't look inviting to click. */
.setup-danger {
  margin-top: 2.5rem;
  padding-top: 1rem;
  border-top: 1px solid var(--pico-muted-border-color);
}
.setup-danger .danger-action {
  color: var(--pico-color-red-600, #c14040);
  border-color: var(--pico-color-red-300, #e8a4a4);
}
.setup-danger .danger-action:hover:not(:disabled) {
  background: var(--pico-color-red-50, #fff5f5);
  border-color: var(--pico-color-red-500, #d35454);
}

.translation-stats {
  margin: 1rem 0 0;
  font-size: 0.85rem;
  color: var(--pico-muted-color);
  text-align: right;
}

/* ---------- glossary table ---------- */

.gloss-table { overflow-x: auto; }
.gloss-table input { margin: 0; }

/* ---------- responsive ---------- */

@media (max-width: 900px) {
  .editor-grid { grid-template-columns: 1fr; }
  .sidebar {
    position: static;
    border-right: none;
    border-bottom: 1px solid var(--pico-muted-border-color);
    padding: 0 0 0.75rem;
    max-height: none;
  }
  .paragraphs {
    grid-template-columns: 1fr;
    column-gap: 0;
    row-gap: 0.4rem;
  }
  .original {
    background: var(--pico-card-background-color);
    padding: 0.5rem 0.75rem;
    border-radius: 4px;
    margin-top: 0.4rem;
  }
  .resize-handle { display: none; }
  .paragraphs.translation-only { max-width: none; }
  .paragraphs.translation-only .translation-only-handle { display: none; }
}

/* `<center>…</center>` — scene break / epigraph / occasional centered
   line that the renderer (markdown.js) lets through as a literal tag.
   Browsers default <center> to display:block with text-align:center,
   but inside our paragraph wrapper that block can collapse oddly when
   the surrounding text-align is justify. Force the rules explicitly so
   the centered content always wins, and add a touch of breathing room
   around it because in practice centered text is used for separators
   («* * *», «◆◆◆») where vertical white space matters more than
   horizontal. */
.translation center,
.original center,
.rendered center {
  display: block;
  text-align: center;
  margin: 0.5rem 0;
}

/* Несохранённая правка абзаца. Красное и рядом с текстом: ошибка в
   баннере наверху страницы человеком не читается — он смотрит на
   абзац, который правит. */
.save-failed {
  color: #b03434;
  font-size: 0.75rem;
  font-weight: 600;
  padding: 0.15rem 0 0;
}
.save-failed-field {
  border-color: #b03434 !important;
  background: rgba(176, 52, 52, 0.06);
}

/* Абзац переводится / перевод только что приехал.
   До этого единственной приметой работы было то, что кнопка не
   нажимается: человек не понимал, началось ли что-нибудь, и жал ещё раз —
   а страж повторного запуска молча отбрасывал второе нажатие. */
.translation-wrap { position: relative; }

.para-spinner {
  position: absolute;
  top: 0.35rem;
  right: 0.35rem;
  width: 0.9rem;
  height: 0.9rem;
  border: 2px solid var(--pico-muted-border-color, #ccc);
  border-top-color: var(--pico-primary, #0172ad);
  border-radius: 50%;
  animation: para-spin 0.8s linear infinite;
  pointer-events: none;
}
@keyframes para-spin { to { transform: rotate(360deg); } }

/* Пока идёт перевод, старый текст приглушён: видно, что он уже не
   окончательный, но читать не мешает. */
.para-busy .translation { opacity: 0.55; }

/* Подсветка прибывшего текста. Гаснет плавно и сама. */
.para-fresh .translation {
  background: rgba(176, 122, 52, 0.16);
  transition: background 1.2s ease-out;
}
@media (prefers-reduced-motion: reduce) {
  .para-spinner { animation: none; }
  .para-fresh .translation { transition: none; }
}

/* ---------- подключённые словари (только чтение) ---------- */

.packs {
  margin-top: 1.5rem;
}
.packs .pack-rules {
  white-space: pre-wrap;
  font-size: 0.8rem;
  padding: 0.6rem 0.8rem;
  margin: 0 0 0.6rem;
  background: var(--pico-card-sectioning-background-color);
  border-radius: 4px;
}
/* Статья, которую книга переспорила своей. Не ошибка и не запрет —
   так и задумано, — поэтому приглушаем, а не подсвечиваем красным. */
.packs .pack-overridden td {
  opacity: 0.5;
  text-decoration: line-through;
}
.packs .pack-overridden td:last-child {
  text-decoration: none;
}
