/* ── Inter ──────────────────────────────────────────────────────────
   Шрифт лежит У НАС, а не на fonts.googleapis.com. Причина ровно одна:
   тот <link> в <head> — ЗАГРУЖАЕМЫЙ ИЗВНЕ и БЛОКИРУЮЩИЙ показ: до ответа
   чужого хоста (DNS + TLS + сам файл, из Узбекистана это сотни миллисекунд)
   страница не рисуется вовсе, а файлы шрифта лежат на ТРЕТЬЕМ хосте
   (fonts.gstatic.com) — ещё одно рукопожатие. Со своего хоста соединение
   уже открыто, и показ ничего не ждёт.
   Файл ОДИН на все начертания (Inter — переменный шрифт), поэтому
   `font-weight: 100 900`; подмножеств четыре, и браузер берёт ровно те,
   чьи буквы есть на экране: узбекскому хватает latin, русскому —
   latin + cyrillic. `swap` — текст виден сразу своим запасным шрифтом,
   а не невидим, пока грузится Inter.
   Обновление: скачать заново с fonts.googleapis.com/css2?family=Inter — и
   не забыть unicode-range, без него браузер тянет все подмножества разом. */

@font-face {
  font-family: 'Inter'; font-style: normal; font-weight: 100 900; font-display: swap;
  src: url(../vendor/fonts/inter-v20-cyrillic-ext.woff2) format('woff2');
  unicode-range: U+0460-052F, U+1C80-1C8A, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F;
}
@font-face {
  font-family: 'Inter'; font-style: normal; font-weight: 100 900; font-display: swap;
  src: url(../vendor/fonts/inter-v20-cyrillic.woff2) format('woff2');
  unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
}
@font-face {
  font-family: 'Inter'; font-style: normal; font-weight: 100 900; font-display: swap;
  src: url(../vendor/fonts/inter-v20-latin-ext.woff2) format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
  font-family: 'Inter'; font-style: normal; font-weight: 100 900; font-display: swap;
  src: url(../vendor/fonts/inter-v20-latin.woff2) format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

/* ============================================================
   CAT Translator — Design System
   Источник облика — макет frontend/prototype-simple.html: его токены,
   шкала и компоненты перенесены сюда буква в букву, имена классов
   продакшна при этом не тронуты. Меняешь вид — сверяйся с макетом,
   а не с тем, «как было».
   ============================================================ */

/* ---------- Tokens: Light (default) ---------- */
:root {
  /* Brand + semantic. Акцент — приглушённый синий: он для ссылок, фокуса
     и информационных меток. Главная кнопка красится НЕ им (см. --btn):
     чёрная кнопка на белом читается как единственное главное действие. */
  --c-primary: #4E6FA8;
  --c-primary-hover: #41608F;
  --c-primary-press: #365079;
  --c-primary-soft: #EDF1F8;
  --c-success: #2C6A4A;
  --c-warning: #8A6014;
  --c-error: #B3272E;
  /* Синоним для .jsx, писавших --c-danger: переменной не существовало, и
     красный МОЛЧА падал в цвет обычного текста ровно там, где красный —
     сигнал (ошибка входа, снятое заверение). var() разворачивается в месте
     использования, поэтому одной строки хватает и тёмной теме. */
  --c-danger: var(--c-error);
  --c-info: #3E7C8C;
  --c-purple: #6B5BA8;

  /* Главное действие. Отдельно от акцента: у чёрной кнопки нет соперников
     на экране, а синим красятся ссылки и метки, которых на странице десятки. */
  --btn: #111110;
  --btn-hover: #2A2926;
  --btn-ink: #FFFFFF;

  /* Лист БЕЛЫЙ, и карточка на нём тоже белая — как в макете
     (frontend/prototype-simple.html): блок отделяет волосяная граница,
     а не подложка. Прежняя лестница «страница серее карточки» была нашим
     доводом про плохие экраны; заказчик посмотрел обе версии рядом и выбрал
     макет — это его решение. Порядок hover → sunken остаётся несущим:
     наведение (panel-2) светлее выбранного (panel), иначе подсказка
     громче решения. */
  --bg: #FFFFFF;
  --bg-elevated: #FFFFFF;
  --bg-sunken: #F7F6F4;
  --bg-hover: #FBFAF9;
  --card: #FFFFFF;
  --header-bg: #FFFFFF;

  /* Text. Четыре уровня, как в макете: ink → ink-2 → muted → faint.
     --text-3 красит подписи и плейсхолдеры (4.5:1 к белому), --text-4 —
     надстрочные метки, номера строк и шапку таблицы: это ориентиры,
     а не текст для чтения, и в макете они именно такие. */
  --text: #171614;
  --text-2: #42403B;
  --text-3: #6E6B64;
  --text-4: #9B978E;
  --text-on-accent: #ffffff;

  /* Линии — две, как в макете: --border (line) у полей, карточек и чипов,
     --border-strong (line-2) у того, что нажимают и что выбрано: обводка
     вторичной кнопки, фокус поля, чекбокс. --control-line оставлен
     синонимом второй — на него завязаны прежние правила. --scrim затемняет
     страницу под модальным окном: в тёмной теме полупрозрачный чёрный
     поверх почти чёрного не затемняет ничего. */
  --border: #E9E7E2;
  --border-strong: #DCD9D3;
  --control-line: var(--border-strong);
  --ring: rgba(78,111,168,0.20);
  --scrim: rgba(23,22,20,0.42);
  /* Подсветка найденного — жёлтая пара «предупреждения», как mark в макете:
     на почти белой строке под курсором она остаётся видна. */
  --hl-bg: #F8F0DE;
  --hl-fg: #8A6014;
  --att-bg: #FBE6E4;
  --att-fg: #A33A2B;
  /* Псевдонимы для .jsx, писавших несуществующие имена: нерезолвящаяся
     переменная в сокращённом свойстве (border-top: 1px solid var(--c-border))
     гасит СВОЙСТВО ЦЕЛИКОМ — разделитель просто исчезал. */
  --text-1: var(--text);
  --c-warn: var(--c-warning);
  --c-border: var(--border);

  /* Status backgrounds (row tints) */
  --st-new-bg: #F7F6F4;     --st-new-fg: #6E6B64;
  --st-trans-bg: #EDF1F8;   --st-trans-fg: #3F6493;
  --st-qa-bg: #F8F0DE;      --st-qa-fg: #8A6014;
  --st-conf-bg: #E9F2EC;    --st-conf-fg: #2C6A4A;
  --st-fail-bg: #F9E8E9;    --st-fail-fg: #B3272E;
  --st-review-bg: #F8F0DE;  --st-review-fg: #8A6014;
  --tm-orange-bg: #F6EEE3;  --tm-orange-fg: #8A5A12;

  /* Routing colors */
  --route-tm: #4E6FA8;
  --route-dup: #2C6A4A;
  --route-google: #8A6014;
  --route-gpt: #6B5BA8;
  --route-human: #B3272E;

  /* Elevation: плоско. Тень поднимает только то, что действительно висит
     над страницей, — всплывающие окна и подсказки. */
  --shadow-sm: 0 1px 2px rgba(23,22,20,0.07), 0 1px 1px rgba(23,22,20,0.04);
  --shadow-md: 0 2px 8px rgba(23,22,20,0.06);
  --shadow-lg: 0 16px 40px rgba(23,22,20,0.16);
  --shadow-pop: 0 10px 28px rgba(23,22,20,0.14);

  /* Spacing scale (8px base) */
  --sp-xs: 4px;  --sp-sm: 8px;  --sp-md: 16px;
  --sp-lg: 24px; --sp-xl: 32px; --sp-2xl: 48px; --sp-3xl: 64px;

  /* Radii */
  --r-sm: 7px; --r-md: 8px; --r-lg: 10px; --r-pill: 999px;

  /* Type */
  --font: "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
  /* Шкала макета (frontend/prototype-simple.html), буква в букву: тело 14px,
     заголовок страницы clamp(20…26), заголовок блока 14.5 (.h2 макета),
     подписи 12.5/11.5.
     Веса — 400/500/600, тяжелее 600 в макете нет ни одной строки; заказчик
     выбрал шкалу глазами на прототипе — это его решение, не наше. */
  --fs-h1: clamp(20px, 2.1vw, 26px); --fs-h2: 14.5px; --fs-h3: 14.5px;
  --fs-body: 14px; --fs-sm: 13px; --fs-xs: 11.5px;
  --lh-body: 1.45; --lh-ui: 1.4;
  /* Поля страницы — от ширины окна, как --pad у .wrap в макете. */
  --pad: clamp(14px, 2.2vw, 30px);
}

/* ---------- Tokens: Dark ---------- */
:root[data-theme="dark"] {
  --c-primary: #8FAEDC;
  --c-primary-hover: #A3BEE6;
  --c-primary-press: #7B9DD0;
  --c-primary-soft: #1A212B;
  --c-success: #7CC49A;
  --c-warning: #D9AC69;
  --c-error: #EA9598;
  --c-info: #86C3D2;
  --c-purple: #B0A2E0;

  --btn: #F2F1EC;
  --btn-hover: #E2E0D9;
  --btn-ink: #131312;

  --bg: #0E0E0D;
  --bg-elevated: #0E0E0D;
  --bg-sunken: #171716;
  --bg-hover: #131312;
  --card: #0E0E0D;
  --header-bg: #0E0E0D;

  --text: #F2F1EC;
  --text-2: #C9C6BE;
  --text-3: #948F86;
  --text-4: #726E66;
  --text-on-accent: #131312;

  --border: #26251F;
  --border-strong: #33322B;
  --control-line: var(--border-strong);
  --ring: rgba(143,174,220,0.28);
  --scrim: rgba(0,0,0,0.72);
  --hl-bg: #241D11;
  --hl-fg: #D9AC69;
  --att-bg: #2C1714;
  --att-fg: #E39182;
  --text-1: var(--text);
  --c-warn: var(--c-warning);
  --c-border: var(--border);

  --st-new-bg: #171716;    --st-new-fg: #948F86;
  --st-trans-bg: #1A212B;  --st-trans-fg: #9BB8DF;
  --st-qa-bg: #241D11;     --st-qa-fg: #D9AC69;
  --st-conf-bg: #152119;   --st-conf-fg: #7CC49A;
  --st-fail-bg: #2A1819;   --st-fail-fg: #EA9598;
  --st-review-bg: #241D11; --st-review-fg: #D9AC69;
  --tm-orange-bg: #241D11; --tm-orange-fg: #D9A06A;

  --route-tm: #8FAEDC;
  --route-dup: #7CC49A;
  --route-google: #D9AC69;
  --route-gpt: #B0A2E0;
  --route-human: #EA9598;

  --shadow-sm: 0 1px 2px rgba(0,0,0,0.35);
  --shadow-md: 0 2px 10px rgba(0,0,0,0.45);
  --shadow-lg: 0 16px 44px rgba(0,0,0,0.6);
  --shadow-pop: 0 10px 30px rgba(0,0,0,0.5);
}

/* ---------- Reset ---------- */
/* Тема по умолчанию светлая и жёсткая (см. app.jsx), поэтому системным
   элементам — выпадающему списку, автозаполнению, полосам прокрутки —
   надо сказать об этом отдельно: иначе у человека с тёмной ОС они
   останутся тёмными на белой странице. */
:root { color-scheme: light; }
:root[data-theme="dark"] { color-scheme: dark; }
*, *::before, *::after { box-sizing: border-box; }
html, body { height: 100%; }
body {
  margin: 0;
  font-family: var(--font);
  font-size: var(--fs-body);
  line-height: var(--lh-body);
  color: var(--text);
  background: var(--bg);
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}
#root { height: 100%; }
button, input, select, textarea { font-family: inherit; font-size: inherit; color: inherit; }
button { cursor: pointer; border: none; background: none; }
h1,h2,h3,h4,p { margin: 0; }
/* Жирнее 600 в макете нет: <b> и <strong> — 600, а не браузерные 700,
   которых у подключённого шрифта больше и не загружено. */
b, strong { font-weight: 600; }
a { color: var(--c-primary); }
::selection { background: rgba(78,111,168,0.30); }
:root[data-theme="dark"] ::selection { background: rgba(143,174,220,0.34); }

/* Smooth, calm theme transition (user-triggered, not auto) */
body, .card, .side, .topbar, .panel, input, select, textarea {
  transition: background-color .18s ease, border-color .18s ease, color .18s ease;
}
/* Suppress transitions during theme swap so var() colors repaint instantly
   (avoids browser bug where transitioned props keep the old custom-property value) */
:root.theme-switching * { transition: none !important; }

/* Focus — never removed, high contrast */
:focus-visible {
  outline: 2px solid var(--c-primary);
  outline-offset: 2px;
  border-radius: 4px;
}
button:focus:not(:focus-visible), input:focus:not(:focus-visible) { outline: none; }

/* ---------- Scrollbars ---------- */
*::-webkit-scrollbar { width: 11px; height: 11px; }
*::-webkit-scrollbar-thumb { background: var(--border-strong); border-radius: 8px; border: 3px solid transparent; background-clip: padding-box; }
*::-webkit-scrollbar-thumb:hover { background: var(--text-3); background-clip: padding-box; }
*::-webkit-scrollbar-track { background: transparent; }

/* ============================================================
   App layout
   ============================================================ */
.app { display: flex; flex-direction: column; height: 100%; min-height: 0; }

/* Прежние .header и .tabbar удалены вместе со своими компонентами:
   их заменили .side и .topbar выше. Мёртвые правила опасны не весом —
   следующий аудит считает их контраст и предлагает чинить то, чего нет. */
.main { flex: 1; min-height: 0; overflow: auto; }
/* Ширина и поля — как .wrap / .wrap.wide в макете. */
.page { max-width: 1120px; margin: 0 auto; padding: clamp(18px, 2.4vw, 30px) var(--pad) 72px; }
.page-wide { max-width: 1560px; }

.page-head { margin-bottom: clamp(18px, 2.2vw, 28px); }
.page-head h1 { font-size: var(--fs-h1); font-weight: 600; letter-spacing: -.02em; text-wrap: balance; }
.page-head .lead { color: var(--text-3); font-size: 13.5px; margin-top: 5px; max-width: 70ch; }

.section { margin-bottom: clamp(18px, 2.2vw, 28px); }
.section-title { font-size: var(--fs-h2); font-weight: 600; margin-bottom: 12px; letter-spacing: -.01em; }
.section-sub { color: var(--text-3); font-size: 12.5px; margin-top: 2px; }
/* Надстрочная метка — тихая подпись четвёртого уровня, как .eyebrow
   в макете: ни капса, ни разрядки, ни веса. Первым читается заголовок. */
.eyebrow { font-size: 11.5px; font-weight: 400; letter-spacing: 0; text-transform: none; color: var(--text-4); margin-bottom: 8px; }

/* ============================================================
   Оболочка: боковое меню + тонкая шапка
   Девять вкладок в строку не помещались и уезжали в горизонтальную
   прокрутку — половина экранов была не видна. Колонка держит их все,
   а ниже 900 px сама ложится лентой: разметка одна на оба случая.
   ============================================================ */
.shell { display: grid; grid-template-columns: 224px minmax(0, 1fr); height: 100%; min-height: 0; }
.shell-main { display: flex; flex-direction: column; min-width: 0; min-height: 0; }

/* Размеры и веса — .side / .nav / .org макета: марка 18px, пункт 13.5px,
   активный — подложкой и весом 500, а не 600; счётчик — тихая пилюля. */
.side {
  border-right: 1px solid var(--border); background: var(--header-bg);
  padding: 14px 10px; display: flex; flex-direction: column; gap: 20px;
  overflow-y: auto; min-height: 0;
}
.side-brand { display: flex; align-items: center; gap: 8px; padding: 2px 8px; font-weight: 600; font-size: 14px; letter-spacing: -.015em; }
.side-mark { width: 16px; height: 16px; border-radius: 4px; background: var(--btn); color: var(--btn-ink); display: grid; place-items: center; flex: 0 0 16px; }
.side-mark svg { width: 10px; height: 10px; }
.org-chip { display: flex; align-items: center; gap: 9px; padding: 7px 9px; border: 1px solid var(--border); border-radius: var(--r-md); font-size: 13px; font-weight: 500; color: var(--text); background: var(--bg); }
.org-chip:hover { border-color: var(--border-strong); }
.org-chip-sel { padding: 4px 6px 4px 9px; }
.org-chip .select { border: 0; background: none; padding: 2px 4px; height: 26px; font-size: 13px; font-weight: 500; }
.org-dot { width: 15px; height: 15px; border-radius: 50%; flex: 0 0 15px; background: conic-gradient(#C9743B, #D9B45C, #5C7F5A, #4E6FA8, #C9743B); }

.grp { display: flex; flex-direction: column; gap: 1px; }
.grp > h3 { font-size: 11px; font-weight: 450; color: var(--text-4); padding: 0 9px 6px; margin: 0; }
.navi {
  display: flex; align-items: center; gap: 10px; width: 100%;
  padding: 8px 9px; border-radius: var(--r-sm); border: 1px solid transparent;
  background: none; color: var(--text-2); font-size: 13.5px; text-align: left;
  transition: background-color .12s ease, color .12s ease;
}
.navi:hover { background: var(--bg-sunken); color: var(--text); }
.navi.on { background: var(--bg-sunken); color: var(--text); font-weight: 500; }
.navi .ic, .navi svg { opacity: .55; flex: 0 0 auto; }
.navi.on .ic, .navi.on svg { opacity: 1; }
.navi-t { flex: 1; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
/* Счётчик у пункта меню. Красная пилюля (.nav b макета) означает РАБОТУ,
   которая ждёт человека, — и только её. Число всего написанного («Редактор
   2692», «Знания 150») размером, а не задачей: покрась его красным, и меню
   будет кричать о том, с чем делать нечего, а настоящий вопрос на пять строк
   потеряется среди тысяч. Поэтому красит .todo, который ставит tabBadge,
   а забытый класс оставляет пилюлю тихой — ошибка молчит, а не орёт. */
.navi-n { font-size: 11px; font-weight: 500; padding: 1px 6px; border-radius: 10px; background: var(--bg-sunken); color: var(--text-3); font-variant-numeric: tabular-nums; }
.navi-n.todo { background: var(--st-fail-bg); color: var(--c-error); }
/* Открытый пункт и пункт под курсором сами сидят на подложке (--bg-sunken),
   и тихая пилюля на ней растворяется: остаются висеть одни цифры без формы.
   Поэтому на такой строке она берёт цвет ЛИСТА — силуэт держится в обеих
   темах. Красную это не касается (:not(.todo)): её фон и так свой, а правило
   на три класса перебило бы .navi-n.todo и выбелило бы тревогу. */
.navi:hover .navi-n:not(.todo), .navi.on .navi-n:not(.todo) { background: var(--bg); }

.side-foot { margin-top: auto; padding: 0 9px 2px; display: flex; flex-direction: column; gap: 6px; }
.sf-row { display: flex; justify-content: space-between; font-size: 11.5px; color: var(--text-3); }
.sf-row b { color: var(--text-2); font-weight: 500; font-variant-numeric: tabular-nums; }
.sf-meter { height: 3px; border-radius: 2px; background: var(--bg-sunken); overflow: hidden; }
.sf-meter i { display: block; height: 100%; background: var(--c-primary); }

.topbar {
  flex: 0 0 auto; display: flex; align-items: center; gap: 10px;
  padding: 10px var(--pad); border-bottom: 1px solid var(--border);
  background: var(--header-bg); z-index: 20;
}
.crumb { display: flex; align-items: center; gap: 8px; font-size: 13px; color: var(--text-3); min-width: 0; }
.crumb b { color: var(--text); font-weight: 500; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.crumb-sep { color: var(--border-strong); }
.tb-right { margin-left: auto; display: flex; align-items: center; gap: 8px; }

@media (max-width: 900px) {
  .shell { grid-template-columns: minmax(0, 1fr); grid-template-rows: auto minmax(0, 1fr); }
  .side {
    flex-direction: row; align-items: center; gap: 4px; overflow-x: auto; overflow-y: hidden;
    border-right: 0; border-bottom: 1px solid var(--border); padding: 8px 12px; scrollbar-width: none;
  }
  .side::-webkit-scrollbar { display: none; }
  .side-brand, .side-foot, .grp > h3, .org-chip { display: none; }
  .grp { flex-direction: row; gap: 4px; }
  .navi { width: auto; padding: 7px 11px; }
  .navi-t { white-space: nowrap; }
  .topbar { padding: 8px var(--sp-md); }
}

/* ============================================================
   Корзины «под ключ» карточками (экран «Что получилось»)
   ============================================================ */
.home-sum { display: flex; flex-direction: column; gap: 12px; }
.home-stack { display: flex; height: 6px; border-radius: 3px; overflow: hidden; background: var(--bg-sunken); flex: 1 1 220px; min-width: 160px; }
.home-legend { font-size: 12.5px; gap: 16px; color: var(--text-3); }
.home-legend i { display: inline-block; width: 6px; height: 6px; border-radius: 2px; margin-right: 7px; }
.st-cards { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(210px, 100%), 1fr)); gap: 12px; }
/* Карточка корзины — .st-card макета: ровная рамка без цветного канта
   (корзину различает значок), под курсором рамка потемнее и подложка. */
.st-card {
  border: 1px solid var(--border); border-radius: var(--r-lg); background: var(--card);
  padding: 14px 16px; display: flex; flex-direction: column; gap: 9px;
  transition: border-color .15s ease, background-color .15s ease;
}
.st-card:not(.st-empty) { cursor: pointer; }
.st-card:not(.st-empty):hover { border-color: var(--border-strong); background: var(--bg-hover); }
.st-card.st-empty { opacity: .55; }
.st-label { font-size: 13px; color: var(--text-3); font-weight: 400; }
.st-num { font-size: clamp(24px, 2.6vw, 30px); font-weight: 500; letter-spacing: -.03em; font-variant-numeric: tabular-nums; line-height: 1.05; }
.st-pct { font-size: 12.5px; color: var(--text-3); font-weight: 450; margin-left: 8px; }
.st-hint { font-size: 12.5px; color: var(--text-4); line-height: 1.45; }
.st-act { margin-top: auto; padding-top: 6px; }
.st-card.st-dim .st-num { font-size: 20px; color: var(--text-2); }
.st-top { display: flex; align-items: center; gap: 8px; }
.st-badge { width: 26px; height: 26px; border-radius: 7px; display: grid; place-items: center; flex: 0 0 26px; }
.st-ok .st-badge { background: var(--st-conf-bg); color: var(--c-success); }
.st-mach .st-badge { background: var(--c-primary-soft); color: var(--c-primary); }
.st-hum .st-badge { background: var(--st-fail-bg); color: var(--c-error); }
.st-mine .st-badge { background: var(--bg-sunken); color: var(--text-2); }
.st-go { margin-top: auto; padding-top: 4px; display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
/* Плитки редактора — в один ряд: три корзины и два среза (заверено вручную,
   не переведено) читаются одной строкой «сколько где», а не лесенкой.
   Пять в ряд помещаются только на мониторе: ниже 1360 px — по три,
   уже 900 px — по две, на телефоне — столбиком. */
.st-cards.st-row { grid-template-columns: repeat(5, minmax(0, 1fr)); }
@media (max-width: 1360px) { .st-cards.st-row { grid-template-columns: repeat(3, minmax(0, 1fr)); } }
@media (max-width: 900px) { .st-cards.st-row { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (max-width: 520px) { .st-cards.st-row { grid-template-columns: minmax(0, 1fr); } }
/* Плитка, чья выборка сейчас в таблице, — та же, что нажатая кнопка-фильтр. */
.st-card.st-on { border-color: var(--border-strong); background: var(--bg-sunken); }
/* У плиток редактора действие — кнопка «Показать N сегм.», сама плитка не жмётся. */
.st-row .st-card:not(.st-empty) { cursor: default; }
.st-row .st-card:not(.st-empty):not(.st-on):hover { border-color: var(--border); background: var(--card); }
.st-chip { font-size: 11.5px; font-weight: 500; padding: 2px 8px; border-radius: 10px; background: var(--bg-sunken); color: var(--text-3); }
/* полоса и тумблер — рядом, как в макете */
.duo { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(340px, 100%), 1fr)); gap: 12px; align-items: start; }
.home-strip { display: flex; align-items: center; gap: 14px; flex-wrap: wrap; padding: 12px 16px; border: 1px solid var(--border); border-radius: var(--r-lg); }
.sw { display: flex; gap: 12px; align-items: flex-start; padding: 13px 16px; border: 1px solid var(--border); border-radius: var(--r-lg); }
.sw .box { width: 34px; height: 20px; border-radius: 11px; background: var(--border-strong); flex: none; position: relative; cursor: pointer; border: 0; transition: background .18s; margin-top: 1px; }
.sw .box::after { content: ""; position: absolute; top: 2px; left: 2px; width: 16px; height: 16px; border-radius: 50%; background: var(--card); transition: transform .18s; }
.sw .box[aria-checked="true"] { background: var(--btn); }
.sw .box[aria-checked="true"]::after { transform: translateX(14px); }
.sw .t { font-weight: 500; font-size: 13.5px; display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.sw .s { font-size: 12.5px; color: var(--text-3); margin-top: 3px; max-width: 78ch; line-height: 1.5; }
.home-note { font-size: 12.5px; color: var(--text-3); line-height: 1.5; display: flex; gap: 14px; flex-wrap: wrap; }
/* фильтры-пилюли над таблицей */
.ed-head { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; padding: 4px 0 10px; }
.ed-head h2 { font-size: 14.5px; font-weight: 600; margin: 0; }
.filters { display: flex; gap: 7px; flex-wrap: wrap; align-items: center; }
.fbtn { border: 1px solid var(--border); background: var(--card); border-radius: 7px; padding: 6px 11px; font-size: 12.5px; color: var(--text-3); cursor: pointer; display: inline-flex; align-items: center; gap: 7px; }
.fbtn:hover { border-color: var(--border-strong); color: var(--text); }
.fbtn[aria-pressed="true"] { background: var(--bg-sunken); color: var(--text); border-color: var(--border-strong); }
.fbtn span { color: var(--text-4); font-variant-numeric: tabular-nums; }

/* ============================================================
   Buttons
   ============================================================ */
.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: 7px;
  height: 34px; padding: 0 14px; min-width: 0;
  border-radius: var(--r-sm);
  font-weight: 500; font-size: 13.5px; letter-spacing: 0;
  transition: background-color .15s ease, box-shadow .15s ease, border-color .15s ease, transform .05s ease;
  white-space: nowrap;
}
.btn:active { transform: translateY(1px); }
.btn .ic { flex: 0 0 auto; }
/* Кнопка не ужимается уже своей надписи. Рядом с ней в строке почти всегда
   стоит поле (.input шириной 100%), которое сжиматься отказывается — свою
   ширину по содержимому оно держит, — и всё сжатие доставалось кнопке:
   в админке «Добавить пользователя» показывалось как «ыдalanuvchi qo'shi»,
   обрезанное с обеих сторон. Сжиматься разрешено полю (min-width: 0 ниже),
   а не подписи действия. */
.btn { flex-shrink: 0; }
/* Три размера — .btn / .btn.sm / .btn.big макета. */
.btn-sm { height: 28px; padding: 0 10px; font-size: 12.5px; min-width: 0; gap: 6px; }
.btn-lg { height: 46px; padding: 0 22px; font-size: 15px; border-radius: 9px; }
.btn-block { width: 100%; }

/* Главное действие — чёрное (в тёмной теме белое). На экране их один-два,
   и они обязаны быть заметнее всего остального без единого цветного пятна. */
.btn-primary { background: var(--btn); color: var(--btn-ink); }
.btn-primary:hover { background: var(--btn-hover); }
.btn-primary:active { background: var(--btn-hover); }
/* Погашенная главная кнопка остаётся КНОПКОЙ: её гасят штатно (идёт
   прогон, исчерпан лимит), и человек должен видеть, что она тут есть. */
.btn-primary:disabled { background: var(--bg-sunken); color: var(--text-2); cursor: not-allowed;
                        box-shadow: inset 0 0 0 1px var(--border-strong); }

.btn-secondary { background: var(--bg); color: var(--text); box-shadow: inset 0 0 0 1px var(--border-strong); }
.btn-secondary:hover { background: var(--bg-sunken); }
.btn-secondary:disabled { color: var(--text-3); box-shadow: inset 0 0 0 1px var(--border); cursor: not-allowed; }

.btn-ghost { background: transparent; color: var(--text-3); min-width: 0; padding: 0 9px; }
.btn-ghost:hover { background: var(--bg-sunken); color: var(--text); }

.btn-danger { background: var(--c-error); color: var(--text-on-accent); }
.btn-danger:hover { filter: brightness(.94); }
.btn-danger-ghost { background: transparent; color: var(--c-error); min-width: 0; }
.btn-danger-ghost:hover { background: var(--st-fail-bg); }

.btn-success { background: var(--c-success); color: var(--text-on-accent); }
.btn-success:hover { filter: brightness(.95); }

/* Icon button */
/* Кнопка-значок — .iconbtn макета: в рамке, 31px, под курсором рамка темнее. */
.iconbtn {
  width: 31px; height: 31px; border-radius: 7px;
  display: inline-grid; place-items: center;
  border: 1px solid var(--border); background: var(--bg);
  color: var(--text-2); transition: background-color .15s ease, color .15s ease, border-color .15s ease;
}
.iconbtn:hover { border-color: var(--border-strong); color: var(--text); }
.iconbtn.sm { width: 29px; height: 29px; border-radius: 7px; }
.iconbtn.active { background: var(--bg-sunken); color: var(--text); border-color: var(--border-strong); }

/* ============================================================
   Cards / panels
   ============================================================ */
.card {
  background: var(--card);
  border: 1px solid var(--border);
  border-radius: var(--r-lg);
  /* Плоско: границы хватает. Тень на каждом блоке съедала иерархию —
     когда «поднято» всё, не поднято ничего. */
  box-shadow: none;
}
.card-pad { padding: 16px 18px; }
.card-pad-sm { padding: 14px 16px; }   /* блоки в узкой колонке: 24px по кругу съедали её треть */
.card-hover { transition: box-shadow .15s ease, transform .15s ease, border-color .15s ease; }
.card-hover:hover { border-color: var(--border-strong); background: var(--bg-hover); }

.grid { display: grid; gap: var(--sp-md); }
.grid-2 { grid-template-columns: repeat(2, 1fr); }
.grid-3 { grid-template-columns: repeat(3, 1fr); }
.grid-4 { grid-template-columns: repeat(4, 1fr); }
.row { display: flex; align-items: center; gap: var(--sp-md); }
.row-wrap { flex-wrap: wrap; }
.col { display: flex; flex-direction: column; }
.between { justify-content: space-between; }
.muted { color: var(--text-2); }
.dim { color: var(--text-3); }

/* Stat / metric card */
/* Число в карточке — .kvrow / .st-card .v макета: вес 500, не жирнее. */
.metric { padding: 16px 18px; }
.metric .m-label { font-size: 12.5px; color: var(--text-3); font-weight: 400; display: flex; align-items: center; gap: 7px; }
.metric .m-value { font-size: 28px; font-weight: 500; letter-spacing: -.03em; margin-top: 4px; line-height: 1.05; font-variant-numeric: tabular-nums; }
.metric .m-sub { font-size: 12.5px; color: var(--text-4); margin-top: 6px; }

/* ============================================================
   Badges
   ============================================================ */
.badge {
  display: inline-flex; align-items: center; gap: 6px;
  padding: 2px 8px; border-radius: 10px; line-height: 1.4;
  font-size: 11.5px; font-weight: 500; letter-spacing: 0;
  white-space: nowrap;
}
.badge .dot { width: 7px; height: 7px; border-radius: 50%; background: currentColor; flex: 0 0 7px; }
.badge-new { background: var(--st-new-bg); color: var(--st-new-fg); }
.badge-translated { background: var(--st-trans-bg); color: var(--st-trans-fg); }
.badge-qa { background: var(--st-qa-bg); color: var(--st-qa-fg); }
.badge-confirmed { background: var(--st-conf-bg); color: var(--st-conf-fg); }
.badge-failed { background: var(--st-fail-bg); color: var(--st-fail-fg); }
.badge-review { background: var(--st-review-bg); color: var(--st-review-fg); }
.badge-soft { background: var(--bg-sunken); color: var(--text-3); }
.badge-lang { background: var(--bg-sunken); color: var(--text-3); font-variant: tabular-nums; }

/* Priority chips */
.prio { display: inline-flex; align-items: center; gap: 6px; font-size: 12px; font-weight: 500; }
.prio .dot { width: 8px; height: 8px; border-radius: 50%; }
.prio-urgent .dot { background: var(--c-error); } .prio-urgent { color: var(--c-error); }
.prio-high .dot { background: var(--c-warning); } .prio-high { color: var(--c-warning); }
.prio-medium .dot { background: var(--c-warning); }
.prio-low .dot { background: var(--c-success); }

/* ============================================================
   Forms
   ============================================================ */
.field { display: flex; flex-direction: column; gap: 5px; }
/* Подпись поля — .lbl макета: 12.5px, обычный вес, второй план. */
.label { font-size: 12.5px; font-weight: 400; color: var(--text-3); letter-spacing: 0; }
.hint { font-size: 11.5px; color: var(--text-4); line-height: 1.5; }

/* Поле — .inp / .sfield макета: тонкая рамка --border, скругление 8,
   фокус — рамка потемнее, без свечения. */
.input, .select, .textarea {
  width: 100%; min-width: 0;   /* в строке сжимается поле, а не надпись кнопки */
  background: var(--bg);
  border: 1px solid var(--border);
  border-radius: var(--r-md);
  padding: 7px 10px;
  font-size: 13.5px; line-height: 1.45;
  color: var(--text);
  transition: border-color .15s ease, background-color .15s ease;
}
.input::placeholder, .textarea::placeholder { color: var(--text-4); }
.input:focus, .select:focus, .textarea:focus {
  outline: none;
  border-color: var(--border-strong);
  box-shadow: none;
  background: var(--bg);
}
/* Многострочное поле — textarea.tgt макета: чуть крупнее и на подложке,
   пока в него не вошли. */
.textarea { resize: vertical; min-height: 84px; padding: 9px 10px; border-radius: var(--r-sm); line-height: 1.5; font-size: 14.5px; background: var(--bg-hover); }
.textarea:focus { background: var(--bg); }
.select {
  appearance: none;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='14' height='14' viewBox='0 0 24 24' fill='none' stroke='%236E6B64' stroke-width='2.5' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='6 9 12 15 18 9'/%3E%3C/svg%3E");
  background-repeat: no-repeat; background-position: right 10px center;
  padding-right: 30px; cursor: pointer; color: var(--text-2);
}

.search {
  position: relative; display: flex; align-items: center;
}
.search .ic { position: absolute; left: 10px; color: var(--text-4); pointer-events: none; }
.search .input { padding-left: 33px; }

/* checkbox / radio — как в макете: 16px, чёрная заливка (accent у главного
   действия, а не у синего). Обычный чекбокс в таблице — тот же цвет.
   Системным элементам цвет задаётся ДО .check: тот прячет свой настоящий
   input нулевым размером, и правило размера, стоящее ниже, сняло бы
   маскировку — рядом с нарисованным квадратом появился бы второй, живой. */
input[type="checkbox"], input[type="radio"], input[type="range"] { accent-color: var(--btn); }
input[type="checkbox"] { width: 15px; height: 15px; cursor: pointer; margin: 1px 0 0; }
input[type="range"] { cursor: pointer; }
.check, .radio { display: inline-flex; align-items: center; gap: 9px; cursor: pointer; font-size: 13.5px; user-select: none; }
.check input, .radio input { position: absolute; opacity: 0; width: 0; height: 0; }
.check .box {
  width: 16px; height: 16px; border-radius: 4px; flex: 0 0 16px;
  border: 1.5px solid var(--border-strong); background: var(--bg);
  display: grid; place-items: center; transition: all .12s ease; color: var(--btn-ink);
}
.check input:checked + .box { background: var(--btn); border-color: var(--btn); }
.check input:focus-visible + .box { outline: 2px solid var(--c-primary); outline-offset: 2px; }
.check .box svg { opacity: 0; transition: opacity .1s; }
.check input:checked + .box svg { opacity: 1; }

.radio .ring { width: 17px; height: 17px; border-radius: 50%; flex: 0 0 17px; border: 1.5px solid var(--border-strong); display: grid; place-items: center; transition: all .12s ease; }
.radio input:checked + .ring { border-color: var(--btn); }
.radio input:checked + .ring::after { content: ""; width: 9px; height: 9px; border-radius: 50%; background: var(--btn); }
.radio input:focus-visible + .ring { outline: 2px solid var(--c-primary); outline-offset: 2px; }

/* Фильтр статусов — пилюли .fbtn макета, те же, что у корзин над таблицей:
   два ряда фильтров на одном экране обязаны выглядеть одинаково. Трека
   и «выступа» нет: выбранное — пилюля на подложке с рамкой потемнее. */
.segmented { display: inline-flex; gap: 7px; flex-wrap: wrap; align-items: center; }
.segmented button {
  display: inline-flex; align-items: center; gap: 7px;
  padding: 6px 11px; border-radius: 7px; border: 1px solid var(--border); background: var(--bg);
  font-size: 12.5px; font-weight: 400; color: var(--text-3);
  transition: background-color .12s ease, color .12s ease, border-color .12s ease;
}
.segmented button:hover { color: var(--text); border-color: var(--border-strong); }
.segmented button.on { background: var(--bg-sunken); color: var(--text); border-color: var(--border-strong); }
.segmented button .cnt { font-size: 11.5px; color: var(--text-4); font-weight: 400; font-variant-numeric: tabular-nums; }

/* Toggle switch — .sw .box макета: 34×20, включённый — чёрный. */
.switch { width: 34px; height: 20px; border-radius: 11px; background: var(--border-strong); position: relative; transition: background-color .18s ease; flex: 0 0 34px; }
.switch.on { background: var(--btn); }
.switch .knob { position: absolute; top: 2px; left: 2px; width: 16px; height: 16px; border-radius: 50%; background: var(--bg); transition: transform .18s ease; }
.switch.on .knob { transform: translateX(14px); }

/* ============================================================
   Expander
   ============================================================ */
/* overflow:hidden резал кольцо фокуса у заголовка (это настоящая кнопка).
   Скругление держат сами края, а не обрезка. */
.expander { border: 1px solid var(--border); border-radius: var(--r-md); background: var(--card); }
.expander-head { border-radius: var(--r-md) var(--r-md) 0 0; }
/* Внутри прокручиваемой таблицы кольцо рисуем ВНУТРЬ: наружное срезает
   край контейнера у первой и последней строки. */
.tbl :where(button, [tabindex]):focus-visible { outline-offset: -2px; }
.expander + .expander { margin-top: 10px; }
.expander-head {
  width: 100%; display: flex; align-items: center; gap: 10px;
  padding: 11px 14px; text-align: left; transition: background-color .12s ease;
}
.expander-head:hover { background: var(--bg-hover); }
.expander-head .chev { color: var(--text-4); transition: transform .18s ease; flex: 0 0 auto; }
.expander.open .expander-head .chev { transform: rotate(90deg); }
.expander-head .ex-title { font-weight: 500; font-size: 13.5px; flex: 1; display: flex; align-items: center; gap: 8px; }
.expander-body { padding: 0 14px 14px; border-top: 1px solid var(--border); padding-top: 14px; }

/* ============================================================
   Table
   ============================================================ */
.table-wrap { border: 1px solid var(--border); border-radius: var(--r-lg); overflow: hidden; background: var(--card); }
.tbl-scroll { overflow: auto; }
/* Таблица — .ed-list макета: шапка тихая (11.5px, четвёртый уровень, подложка
   panel-2), строки 13.5px с разделителем в волос, перевод вторым планом. */
table.tbl { width: 100%; border-collapse: collapse; font-size: 13.5px; }
.tbl thead th {
  position: sticky; top: 0; z-index: 2;
  background: var(--bg-hover); color: var(--text-4);
  font-size: 11.5px; font-weight: 400; letter-spacing: 0; text-transform: none;
  text-align: left; padding: 9px 14px; border-bottom: 1px solid var(--border);
  white-space: nowrap;
}
.tbl tbody td { padding: 11px 14px; border-bottom: 1px solid var(--border); vertical-align: top; }
.tbl tbody tr:last-child td { border-bottom: none; }
.tbl tbody tr { transition: background-color .1s ease; cursor: pointer; }
.tbl tbody tr:hover { background: var(--bg-hover); }
/* Выбранная строка — подложка panel, как .srow[aria-selected] в макете: она
   темнее наведения (panel-2), и этого порядка достаточно, чтобы решение
   читалось громче подсказки. Отмеченная галочкой — синяя (см. row-checked). */
.tbl tbody tr.selected { background: var(--bg-sunken); }
/* Время реплики у субтитров: начало, под ним конец. */
.tbl .col-time { width: 74px; white-space: nowrap; color: var(--text-3); font-size: 11.5px; font-variant-numeric: tabular-nums; padding-top: 13px; }
.tbl .col-id { width: 56px; color: var(--text-4); font-size: 11.5px; font-variant-numeric: tabular-nums; font-weight: 400; padding-top: 13px; }
.tbl .src-cell { color: var(--text); line-height: 1.42; }
.tbl .tgt-cell { color: var(--text-2); line-height: 1.42; }
/* Оригинал и перевод делят ширину поровну, а строка без пробелов рвётся
   где угодно. Таблица считает колонки по НАИМЕНЬШЕЙ ширине содержимого,
   и `break-word` у body её не уменьшает: строка вида «/uni0056/uni0069…»
   или текст вразрядку из PDF распирали «Как было» на весь экран, а перевод
   сжимался в полоску по слову в строке. `anywhere` — уменьшает. */
.tbl .src-cell, .tbl .tgt-cell { width: 44%; overflow-wrap: anywhere; }
.tbl .tgt-empty { color: var(--text-4); font-style: italic; }
/* термин в глоссарии кликабелен: ведёт в редактор с фильтром */
.linklike { background: none; border: 0; padding: 0; font: inherit; color: var(--c-primary);
            cursor: pointer; text-align: left; text-decoration: underline dotted; }
.linklike:hover { text-decoration: underline solid; }

/* подсветка совпадений поиска в таблице сегментов */
.tbl mark.hl { background: var(--hl-bg); color: var(--hl-fg); border-radius: 3px; padding: 0 2px; font-weight: 500; }
/* Цветного канта слева у строк больше нет: в макете статус несёт чип
   в колонке «Что тут», и второй сигнал о том же только пестрил. Классы
   row-status-* на строках остаются — по ним ходят тесты и выбор строки. */

/* Стили чипа TM убраны вместе с самим чипом: показывать было нечего.
   Переменные --tm-orange-* остались в обеих темах и больше никем не заняты —
   готовая пара под оранжевую метку, если такая понадобится. */

/* Compact secondary action buttons (detail panel) */
.mini-actions { display: flex; flex-wrap: wrap; gap: 6px; }
.mini-btn {
  display: inline-flex; align-items: center; gap: 6px;
  height: 28px; padding: 0 10px; border-radius: 7px;
  border: 1px solid var(--border); background: var(--bg);
  color: var(--text-3); font-size: 12.5px; font-weight: 500; white-space: nowrap;
  transition: background-color .12s ease, border-color .12s ease, color .12s ease;
}
.mini-btn:hover { background: var(--bg-sunken); color: var(--text); border-color: var(--border-strong); }
.mini-btn.on { background: var(--bg-sunken); border-color: var(--border-strong); color: var(--text); }
.mini-btn.readonly { cursor: default; }
.mini-btn.readonly:hover { background: var(--bg); color: var(--text-3); border-color: var(--border); }
.mini-btn .mb-val { font-variant-numeric: tabular-nums; }

/* Revertable status icon */
.status-cell-btn {
  display: inline-grid; place-items: center; width: 28px; height: 28px;
  border-radius: 7px; border: 1px solid transparent; background: transparent;
  transition: border-color .12s ease, background-color .12s ease;
}
.status-cell-btn.revertable { cursor: pointer; }
.status-cell-btn.revertable:hover { border: 1px dashed var(--border-strong); background: var(--bg-hover); }

/* Pagination */
.pagination {
  display: flex; align-items: center; gap: 8px; flex-wrap: wrap;
  margin-top: var(--sp-md);
}
/* Страницы — .pager / .pgbtn макета: 28px в рамке, текущая — на подложке. */
.page-num {
  min-width: 28px; height: 28px; padding: 0 6px; border-radius: 7px;
  display: inline-grid; place-items: center;
  font-size: 12.5px; font-weight: 500; font-variant-numeric: tabular-nums;
  color: var(--text-2); border: 1px solid var(--border); background: var(--bg);
  transition: background-color .12s ease, color .12s ease, border-color .12s ease;
}
.page-num:hover:not(:disabled):not(.on) { border-color: var(--border-strong); color: var(--text); }
.page-num.on { background: var(--bg-sunken); color: var(--text); border-color: var(--border-strong); }
.page-num:disabled { opacity: .35; cursor: not-allowed; }
.page-ellipsis { color: var(--text-4); padding: 0 2px; user-select: none; }
.page-goto { width: 56px; text-align: center; padding: 4px 8px; }

/* Status bar (bottom, MemSource-style) */
/* Строка итога — как .pager макета: тихая подпись под таблицей, а не
   залипающая плашка с рамкой. */
.statusbar {
  display: flex; align-items: center; justify-content: space-between; gap: 16px;
  padding: 10px 2px 0; margin-top: 4px;
  font-size: 12.5px; color: var(--text-3); flex-wrap: wrap;
}
.statusbar .sb-group { display: inline-flex; align-items: center; gap: 8px; }
.statusbar .sb-sep { color: var(--border-strong); }
.statusbar b { color: var(--text); font-variant-numeric: tabular-nums; font-weight: 500; }
.statusbar .sb-save { display: inline-flex; align-items: center; gap: 7px; color: var(--text-3); }
.statusbar .sb-dot { width: 8px; height: 8px; border-radius: 50%; background: var(--c-success); flex: 0 0 8px; }

/* Inline TM matches popover (detail panel) */
.tm-pop {
  border: 1px solid var(--border); border-radius: var(--r-md);
  background: var(--bg-sunken); padding: 12px; display: flex; flex-direction: column; gap: 8px;
  animation: slideup .15s ease;
}
.tm-pop .tmrow { background: var(--card); border: 1px solid var(--border); border-radius: var(--r-sm); padding: 10px; }

/* InfoTip ⓘ + tooltip */
.infotip {
  display: inline-flex; align-items: center; vertical-align: middle;
  margin-left: 4px; color: var(--text-3); opacity: .6; cursor: help;
  transition: opacity .12s ease, color .12s ease;
}
.infotip:hover, .infotip:focus-visible { opacity: 1; color: var(--c-primary); outline: none; }
.tooltip {
  position: fixed; z-index: 400; max-width: 320px; width: max-content;
  padding: 12px 16px; background: var(--bg-elevated);
  border: 1px solid var(--border-strong); border-radius: var(--r-md);
  box-shadow: var(--shadow-lg); pointer-events: none;
  animation: fade .15s ease-out;
}
.tooltip .tt-title { font-weight: 500; font-size: 13.5px; color: var(--text); margin-bottom: 6px; }
.tooltip .tt-body { font-size: 12.5px; line-height: 1.5; color: var(--text-2); white-space: pre-line; }
.tooltip .tt-code {
  display: inline-block; margin-top: 8px; font-family: ui-monospace, "SF Mono", Menlo, monospace;
  font-size: 11px; background: var(--bg-sunken); border: 1px solid var(--border);
  border-radius: 5px; padding: 2px 7px; color: var(--text-2); letter-spacing: .2px;
}

/* Route / Risk label (RU name + EN code secondary) */
.route-label { display: inline-flex; align-items: center; gap: 8px; }
.route-label .rl-dot { width: 9px; height: 9px; border-radius: 3px; flex: 0 0 9px; }
.route-label .rl-name { font-weight: 500; font-size: 13px; color: var(--text); }
.route-label .rl-code { font-size: 11px; color: var(--text-3); opacity: .8; }
@media (max-width: 760px) { .route-label .rl-code { display: none; } }

/* ============================================================
   Editor split layout
   ============================================================ */
.editor-toolbar {
  position: sticky; top: 0; z-index: 12;
  background: var(--bg); border-bottom: 1px solid var(--border);
  padding: 10px var(--pad);
  display: flex; flex-direction: column; gap: 10px;
}
/* Раскладка макета: содержимое живёт в обёртке .wrap.wide (1560px по центру),
   таблица и карточка сегмента — сетка .ed внутри неё: minmax(0,1fr) +
   minmax(300px,352px), зазор 14px, карточка залипает под панелью.
   Боковой панели во всю высоту с собственной рамкой в макете нет. */
.editor-main { width: 100%; max-width: 1560px; margin: 0 auto; padding: clamp(18px, 2.2vw, 28px) var(--pad) 0; min-width: 0; }
/* Колонка карточки — только пока карточка открыта (.has-side): закрыта —
   таблица во всю ширину, а не рядом с пустой рамкой «Сегмент не выбран». */
.editor-body {
  width: 100%; max-width: 1560px; margin: 0 auto; min-height: 0;
  display: grid; grid-template-columns: minmax(0, 1fr); gap: 14px; align-items: start;
  padding: clamp(18px, 2.2vw, 28px) var(--pad) 72px;
}
.editor-body.has-side { grid-template-columns: minmax(0, 1fr) minmax(300px, 352px); }
.editor-body .editor-main { padding: 0; max-width: none; margin: 0; }
.editor-side {
  border: 1px solid var(--border); border-radius: var(--r-lg); background: var(--card);
  padding: 16px 18px;
  position: sticky; top: 70px; align-self: start;
  max-height: calc(100vh - 84px); overflow: auto;
}
.editor-side > .empty { padding: 32px 12px; }
/* Таблица течёт вместе со страницей, как .ed-list в макете: шапка не залипает. */
.editor-body .tbl thead th { position: static; }
@media (max-width: 1100px) { .editor-body, .editor-body.has-side { grid-template-columns: 1fr; } .editor-side { position: static; max-height: none; } }
/* Слова из выборки «Проверки» — та же подсветка, что у поиска в таблице,
   но и в полосе фильтра, и в карточке сегмента. */
mark.hl { background: var(--hl-bg); color: var(--hl-fg); border-radius: 3px; padding: 0 2px; font-weight: 500; }
/* Подсветка ЖАЛОБЫ проверки — не того же цвета, что совпадение поиска:
   «нашлось то, что я искал» и «здесь дефект» — разные сообщения, и одним
   цветом они сливаются в «что-то подсвечено». Подчёркивание волной, а не
   другая заливка: на телефоне заливок и так много. */
mark.hl-att, .tbl mark.hl-att {
  background: var(--att-bg); color: var(--att-fg);
  text-decoration: underline wavy var(--att-fg); text-underline-offset: 3px;
}
/* Правка прямо в строке таблицы. Поле занимает ячейку целиком: своей ширины
   у него нет — таблица резиновая, а жёсткая ширина от 300 px на телефоне
   это горизонтальная прокрутка всей страницы. */
.tbl tr.selected .src-cell, .tbl tr.selected .tgt-cell { cursor: text; }
.tbl tr.selected .src-cell:hover, .tbl tr.selected .tgt-cell:hover {
  box-shadow: inset 0 -1px 0 var(--border-strong, var(--border));
}
.cell-edit { display: block; width: 100%; }
.cell-edit .cell-edit-ta {
  width: 100%; min-height: 96px; font-size: 13px; line-height: 1.5; resize: vertical;
}
.cell-edit-act { flex-wrap: wrap; }
.seg-terms { margin-top: 8px; font-size: 12.5px; line-height: 1.6; display: flex; flex-direction: column; gap: 6px; }
.seg-tgt-hl { padding: 8px 10px; border-radius: var(--r-md); background: var(--bg-sunken); font-size: 13px; line-height: 1.5; }
/* Сохранённый обратный перевод к прежнему тексту: виден, но назван устаревшим. */
.bc-stale { font-size: 12px; color: var(--c-warning); line-height: 1.5; }
.tmrow.is-stale { opacity: .6; }

/* Полоса идущего прогона — последняя строка залипающей панели.
   Прогон идёт минутами и порциями по пять сегментов; уходить за верхний край
   при первом же движении таблицы полоса не имеет права — иначе человек листает
   страницу вверх каждый раз, когда хочет узнать, где работа и как её остановить.
   Отдельным sticky-элементом её сделать нельзя: два залипающих соседа с top:0
   наезжают друг на друга, а высота панели фильтров переменная. */
.run-strip {
  display: flex; align-items: center; gap: 12px; flex-wrap: wrap;
  padding: 8px 11px; border-radius: var(--r-md);
  background: var(--bg-sunken); border: 1px solid var(--border);
}
.run-strip .rs-main { flex: 1 1 320px; min-width: 220px; }
.run-strip .rs-title { display: flex; align-items: baseline; gap: 8px; flex-wrap: wrap; font-size: 12.5px; font-weight: 500; margin-bottom: 5px; }
.run-strip .rs-num { font-variant-numeric: tabular-nums; font-weight: 500; color: var(--text-2); }

/* Шаги составного прогона. Галочка = шаг взял всё, что ему отвёл разбор;
   точка = ещё берёт. «Текущего» шага одного на прогон не бывает: каждая порция
   проходит все шаги по очереди, и врать об этом кружком нельзя. */
.run-steps { display: flex; flex-wrap: wrap; gap: 5px; flex: 1 1 100%; }
.run-step {
  display: inline-flex; align-items: center; gap: 5px;
  padding: 2px 9px; border-radius: var(--r-pill);
  font-size: 11.5px; line-height: 1.75; font-variant-numeric: tabular-nums;
  background: var(--card); border: 1px solid var(--border); color: var(--text-2);
}
.run-step b { color: var(--text); font-weight: 500; }
.run-step.on { border-color: var(--c-primary); }
.run-step.ok { color: var(--c-success); }
.run-step.ok b { color: var(--c-success); }
.run-step .rs-dot { width: 7px; height: 7px; border-radius: 50%; flex: 0 0 7px; background: var(--c-primary); animation: rspulse 1.4s ease-in-out infinite; }
@keyframes rspulse { 0%, 100% { opacity: 1; } 50% { opacity: .25; } }

/* Блоки запуска. Во всю ширину монитора они были простынёй: строка на 2000 px
   не читается, а до второй кнопки приходилось листать. Рядом друг с другом все
   видны целиком.
   Колонок три — составной прогон, одобрение терминов и «Анализ» (корзины
   «под ключ»; появляется после первого прогона). Соответствие глоссарию —
   секция внутри одобрения: смотрят на него сразу после одобрения, а отдельной
   колонкой оно дублировало и одобрение, и вкладку «Анализ». Не хватило экрана
   в ширину — третья колонка переносится строкой ниже, а не прячется. */
.run-decks {
  display: grid; grid-template-columns: minmax(0, 1.5fr) minmax(0, 1fr) minmax(0, 1fr);
  gap: var(--sp-md); align-items: start; max-width: 1760px;
}
@media (max-width: 1520px) { .run-decks { grid-template-columns: minmax(0, 1.4fr) minmax(0, 1fr); } }
@media (max-width: 1180px) { .run-decks { grid-template-columns: minmax(0, 1fr); } }

/* «Словарь книги»: три служебные панели — колонками (те же пороги, что
   у .run-decks), карточки очереди — сеткой. align-items: start обязателен:
   иначе короткая панель растягивается до высоты соседней. */
.kb-decks {
  display: grid; grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: var(--sp-md); align-items: start; margin-bottom: 14px;
}
@media (max-width: 1520px) { .kb-decks { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (max-width: 1180px) { .kb-decks { grid-template-columns: minmax(0, 1fr); } }
.kb-cands { display: grid; grid-template-columns: repeat(auto-fill, minmax(min(380px, 100%), 1fr)); gap: 10px; align-items: start; }
@media (max-width: 640px) { .kb-cands { grid-template-columns: minmax(0, 1fr); } }

/* ── «Словари» и «Проверка» налегке ──────────────────────────────────
   Человек видит вопросы, эксперт (store.can.super) — прежний экран.
   Вертикальный ритм задаёт gap колонки .page-stack (22px), а не маргины
   блоков: у маргинов нет соседа, и они схлопываются или удваиваются. */
.page-stack { display: flex; flex-direction: column; gap: 22px; }
.page-stack > .page-head { margin-bottom: 0; }
.page-stack > .section { margin-bottom: 0; }

/* полоса готовности: процент | полоса и легенда | одна кнопка */
.ready-strip { display: grid; grid-template-columns: auto minmax(0, 1fr) auto; gap: 26px; align-items: center;
  padding: 18px 20px; border: 1px solid var(--border); border-radius: 12px; background: var(--card); }
.ready-pct { font-size: 44px; font-weight: 500; letter-spacing: -.04em; line-height: 1; font-variant-numeric: tabular-nums; white-space: nowrap; }
.ready-pct small { font-size: 14px; color: var(--text-3); letter-spacing: 0; font-weight: 450; margin-left: 6px; }
.ready-meter { display: flex; height: 10px; border-radius: 5px; overflow: hidden; background: var(--bg-sunken); }
.ready-legend { display: flex; flex-wrap: wrap; gap: 6px 16px; font-size: 12px; color: var(--text-3); margin-top: 8px; }
.ready-legend span { display: inline-flex; align-items: center; gap: 6px; font-variant-numeric: tabular-nums; }
.ready-legend i { width: 7px; height: 7px; border-radius: 2px; }
.ready-cta { display: flex; flex-direction: column; align-items: flex-end; gap: 5px; }
@media (max-width: 900px) {
  .ready-strip { grid-template-columns: minmax(0, 1fr); gap: 14px; }
  .ready-cta { align-items: flex-start; }
}

/* список вопросов: полоса тяжести, текст, команды справа */
.ask-head { display: flex; align-items: baseline; gap: 10px; flex-wrap: wrap; }
.ask-head h2 { margin: 0; font-size: 15px; font-weight: 600; letter-spacing: -.01em; }
.ask-head .ask-n { font-size: 13px; color: var(--c-warning); font-weight: 500; font-variant-numeric: tabular-nums; }
.ask-head .ask-hint { margin-left: auto; font-size: 12px; color: var(--text-4); }
/* Таблица вопросов: полоса тяжести · что не так · где · ваш ответ · сколько
   изменится. Сетка ОДНА на шапку и на строки — колонки иначе разъезжаются,
   и список читается как сломанный. Числа — tabular-nums, иначе правая
   колонка пляшет на каждой строке. */
.ask-list { display: flex; flex-direction: column; border: 1px solid var(--border); border-radius: 12px; overflow: hidden; background: var(--card); }
.ask { display: grid; grid-template-columns: 4px minmax(0, 1.6fr) minmax(0, .9fr) auto minmax(0, 104px);
  gap: 0 16px; align-items: center;
  padding: 15px 16px 15px 0; border-bottom: 1px solid var(--border); }
.ask:last-child { border-bottom: 0; }
.ask-hd { padding-top: 10px; padding-bottom: 10px; background: var(--bg-sunken);
  font-size: 11.5px; font-weight: 600; letter-spacing: .06em; text-transform: uppercase; color: var(--text-4); }
.ask-hd .ask-cell, .ask-hd .ask-where, .ask-hd .ask-acts, .ask-hd .ask-scope { font-size: inherit; color: inherit; }
/* «Сколько изменится» в 104px не помещается: переносим, а не режем. */
.ask-hd .ask-scope { white-space: normal; }
.ask-sev { align-self: stretch; border-radius: 0 3px 3px 0; background: var(--border-strong); }
.ask-sev.warn { background: var(--c-warning); }
.ask-sev.bad { background: var(--c-error); }
.ask-cell { min-width: 0; }
.ask-t { font-size: 14px; color: var(--text); line-height: 1.4; }
.ask-t b { font-weight: 500; }
.ask-m { font-size: 12.5px; color: var(--text-3); margin-top: 3px; line-height: 1.45; }
.ask-where { min-width: 0; font-size: 12.5px; color: var(--text-3); line-height: 1.45;
  font-variant-numeric: tabular-nums; }
/* Пара «было → надо» — чипом по ширине текста, а не заливкой всей колонки:
   грид-элемент блокифицируется, поэтому ширину держит сам чип внутри. */
.ask-where.frag > span { color: var(--text-2); background: var(--bg-sunken);
  border-radius: 6px; padding: 5px 8px; display: inline-block; }
.ask-acts { display: flex; gap: 6px; flex-wrap: wrap; justify-content: flex-end; }
.ask-scope { text-align: right; font-size: 12.5px; color: var(--text-2); white-space: nowrap;
  font-variant-numeric: tabular-nums; }
.ask-quiet .ask-t { color: var(--text-2); }
.ask-empty { padding: 14px 16px; font-size: 13px; color: var(--text-3); }
/* Снятая отметка «проверено» — извещение, а не вопрос: над таблицей и красным
   (инвариант 14: отмена решения человека обязана быть видна). */
/* Карточка сегмента: подпись зоны. Три зоны отвечают на три вопроса —
   что в книге, что я написала, что мне не нравится. */
.seg-zone-t { display: flex; align-items: center; gap: 8px; margin-bottom: 8px;
  font-size: 11px; font-weight: 600; letter-spacing: .07em; text-transform: uppercase; color: var(--text-4); }
.seg-zone-t i { width: 17px; height: 17px; border-radius: 50%; background: var(--bg-sunken);
  color: var(--text-3); display: grid; place-items: center; font-style: normal;
  font-size: 10.5px; font-weight: 700; letter-spacing: 0; }
.ask-alarm { display: flex; align-items: center; justify-content: space-between; gap: 16px;
  flex-wrap: wrap; padding: 14px 16px; border-radius: 12px;
  background: var(--st-fail-bg); border: 1px solid transparent; }
.ask-alarm-t { font-size: 14px; font-weight: 600; color: var(--c-error); }
/* Стопка на просмотр: те же строки, но тише и без колонки ответа. */
.ask-stack { display: flex; flex-direction: column; gap: 8px; margin-top: 4px; }
.ask-stack-h { display: flex; align-items: baseline; gap: 10px; font-size: 13px; color: var(--text-3); }
.ask-stack-h .ask-n { color: var(--text-4); font-weight: 500; }
/* Порог тот же, где складывается остальной экран (.run-decks, .kb-decks):
   иначе на 1000px таблица ещё в строку, а блоки запуска уже в колонку,
   и на две текстовые колонки остаётся полсотни пикселей. */
@media (max-width: 1180px) {
  .ask { grid-template-columns: 4px minmax(0, 1fr); row-gap: 8px; padding-right: 12px; }
  .ask-hd { display: none; }
  .ask-sev { grid-row: 1 / -1; }
  .ask-where, .ask-acts, .ask-scope { grid-column: 2; justify-content: flex-start; text-align: left; }
}

/* подвал экрана: что машина сделала сама и дверь к подробностям */
.page-foot { display: flex; flex-wrap: wrap; align-items: center; gap: 6px 18px; font-size: 12.5px;
  color: var(--text-4); border-top: 1px dashed var(--border-strong); padding-top: 14px; }
.page-foot b { color: var(--text-3); font-weight: 500; }
.page-foot .spacer { flex: 1 1 auto; }

/* «Словари»: инструменты, строка автоматики, чипы-фильтры, статус записи */
.kb-tools { display: flex; gap: 8px; align-items: center; flex-wrap: wrap; }
.kb-tools > :first-child { flex: 1 1 300px; }
.kb-auto { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; font-size: 13px; color: var(--text-2);
  background: var(--st-conf-bg); border-radius: 9px; padding: 9px 13px; }
.kb-auto b { color: var(--st-conf-fg); font-weight: 600; }
.kb-auto .btn { margin-left: auto; }
.kb-chips { display: flex; gap: 6px; flex-wrap: wrap; }
.kb-chip { font: inherit; font-size: 12.5px; padding: 4px 10px; border-radius: 14px; border: 1px solid var(--border);
  color: var(--text-3); background: var(--card); cursor: pointer; font-variant-numeric: tabular-nums; }
.kb-chip:hover { border-color: var(--border-strong); color: var(--text); }
.kb-chip[aria-pressed="true"] { background: var(--btn); color: var(--btn-ink); border-color: var(--btn); }
.kb-chip:focus-visible { outline: 2px solid var(--ring); outline-offset: 2px; }
.kb-st { display: inline-block; font-size: 11.5px; border-radius: 9px; padding: 1px 8px; white-space: nowrap; }
.kb-st.ord { background: var(--st-conf-bg); color: var(--st-conf-fg); }
.kb-st.hint { background: var(--bg-sunken); color: var(--text-3); }
.kb-st.proj { background: var(--c-primary-soft); color: var(--c-primary); margin-left: 6px; }
.kb-card { border: 1px solid var(--border); border-radius: 11px; padding: 13px 14px; background: var(--bg-hover);
  display: flex; flex-direction: column; gap: 9px; }
.kb-card .kb-pair { font-size: 15px; line-height: 1.35; }
.kb-card .kb-pair b { font-weight: 600; }
.kb-card .kb-pair .kb-tgt { color: var(--c-primary); font-weight: 500; }
.kb-card .kb-ctx { font-size: 12px; color: var(--text-3); line-height: 1.5; }
.kb-card .kb-reach { margin-left: auto; font-size: 11.5px; color: var(--text-4); font-variant-numeric: tabular-nums; }

/* «Профиль»: слева «про меня», справа команды с таблицами. Сетки полей
   внутри узкой колонки (.grid-2 имени, .grid-3 пароля) складываются
   в одну: их медиазапросы смотрят на окно, а не на колонку в 380px. */
.profile-cols {
  display: grid; grid-template-columns: minmax(300px, 380px) minmax(0, 1fr);
  gap: 16px; align-items: start;
}
.profile-cols .profile-me .grid-2, .profile-cols .profile-me .grid-3 { grid-template-columns: 1fr; }
@media (max-width: 1180px) { .profile-cols { grid-template-columns: minmax(0, 1fr); } }

/* Поиск и переход по номеру — прямо над таблицей: от залипающей панели её
   отделяют два блока запуска высотой в экран. Маленькая строка стоит над
   колонкой «#» и по ширине равна двум первым колонкам таблицы (36 px чекбокса
   + 56 px номера) — туда и вводят номер сегмента. */
.table-head { display: flex; flex-direction: column; gap: 8px; margin-bottom: 10px; }
.seg-jump { position: relative; flex: 0 0 92px; width: 92px; }
.seg-jump .sj-hash {
  position: absolute; left: 11px; top: 50%; transform: translateY(-50%);
  color: var(--text-4); font-weight: 500; font-size: 13px; pointer-events: none;
}
.seg-jump .input { padding-left: 24px; font-variant-numeric: tabular-nums; }

/* Открытая зона — не весь файл, и молчать об этом нельзя: иначе «куда делись
   сегменты» человек ищет в фильтрах, которых мы же и не оставили. */
.zone-strip {
  display: flex; align-items: center; gap: 9px; flex-wrap: wrap;
  padding: 6px 11px; border-radius: var(--r-md);
  background: var(--bg-sunken); border: 1px solid var(--border);
  font-size: 12.5px; color: var(--text-2);
}

/* Переключатель «что выбрано» — .seg макета: одна рамка на группу, кнопки
   разделены волосом, выбранная сидит на подложке. Отдельно от .btn-primary
   намеренно: чёрная заливка на экране одна и означает «нажми сюда», а вкладка
   и выбор языка отвечают на другой вопрос — «где я сейчас». Двумя чёрными
   кнопками рядом главное действие перестаёт быть главным. */
.seg { display: inline-flex; border: 1px solid var(--border); border-radius: 7px; overflow: hidden; }
.seg button {
  border: 0; background: var(--bg); padding: 5px 10px; font-size: 12px;
  color: var(--text-3); white-space: nowrap;
  transition: background-color .12s ease, color .12s ease;
}
.seg button + button { border-left: 1px solid var(--border); }
.seg button:hover { color: var(--text); }
.seg button[aria-pressed="true"] { background: var(--bg-sunken); color: var(--text); }

/* Detail panel internal tabs */
/* Вкладки карточки — .seg макета: одна рамка на группу, кнопки внутри
   разделены волосом, выбранная — на подложке panel. */
.minitabs { display: flex; border: 1px solid var(--border); border-radius: 7px; overflow: hidden; background: var(--bg); }
.minitabs button { flex: 1; padding: 5px 6px; border-radius: 0; font-size: 12px; font-weight: 400; color: var(--text-3); }
.minitabs button + button { border-left: 1px solid var(--border); }
.minitabs button:hover { color: var(--text); }
.minitabs button.on { background: var(--bg-sunken); color: var(--text); }

/* ============================================================
   Modal
   ============================================================ */
.overlay {
  position: fixed; inset: 0; z-index: 100;
  background: var(--scrim); backdrop-filter: blur(2px);
  display: grid; place-items: center; padding: var(--sp-lg);
  animation: fade .16s ease;
}
.modal {
  width: 100%; max-width: 560px; max-height: 86vh; overflow: auto;
  background: var(--card); border: 1px solid var(--border);
  border-radius: 12px; box-shadow: none;
  animation: pop .16s ease;
}
/* Окно — .modal-card макета: скругление 12, без тени (её роль играет
   затемнение), заголовок 17px. Он НАМЕРЕННО крупнее заголовка блока (14.5):
   окно закрывает собой страницу и спрашивает об одном, и его вопрос обязан
   читаться первым — в макете это единственное место со своим размером. */
.modal-head { display: flex; align-items: center; gap: 10px; padding: 16px 20px; border-bottom: 1px solid var(--border); }
.modal-head h3 { font-size: 17px; font-weight: 600; flex: 1; letter-spacing: -.015em; }
.modal-body { padding: 18px 20px; display: flex; flex-direction: column; gap: 12px; }
.modal-foot { padding: 14px 20px; border-top: 1px solid var(--border); display: flex; justify-content: flex-end; gap: 8px; }

@keyframes fade { from { opacity: 0; } to { opacity: 1; } }
@keyframes pop { from { opacity: 0; transform: scale(.96) translateY(8px); } to { opacity: 1; transform: none; } }
@keyframes slideup { from { opacity: 0; transform: translateY(12px); } to { opacity: 1; transform: none; } }

/* ============================================================
   Toasts
   ============================================================ */
.toasts { position: fixed; right: 20px; bottom: 20px; z-index: 200; display: flex; flex-direction: column; gap: 10px; width: 360px; max-width: calc(100vw - 32px); }
/* Уведомление — тёмная плашка .toast макета: чёрное на белом листе (в тёмной
   теме — светлое). Значок красится цветом события ПРОТИВОПОЛОЖНОЙ темы:
   на чёрной плашке светлой темы читаются светлые оттенки, и наоборот. */
.toast {
  display: flex; align-items: flex-start; gap: 10px;
  padding: 10px 12px 10px 14px; border-radius: var(--r-md);
  background: var(--btn); color: var(--btn-ink); border: 0;
  box-shadow: var(--shadow-md); animation: slideup .2s ease;
}
.toast .t-ic { width: 22px; height: 22px; border-radius: 6px; display: grid; place-items: center; flex: 0 0 22px; margin-top: 1px; }
.toast.success .t-ic { color: #7CC49A; }
.toast.error .t-ic { color: #EA9598; }
.toast.warning .t-ic { color: #D9AC69; }
.toast.info .t-ic { color: #8FAEDC; }
:root[data-theme="dark"] .toast.success .t-ic { color: #2C6A4A; }
:root[data-theme="dark"] .toast.error .t-ic { color: #B3272E; }
:root[data-theme="dark"] .toast.warning .t-ic { color: #8A6014; }
:root[data-theme="dark"] .toast.info .t-ic { color: #4E6FA8; }
.toast .t-title { font-weight: 500; font-size: 13px; }
.toast .t-msg { font-size: 12.5px; opacity: .78; margin-top: 2px; }
.toast .t-close { color: inherit; opacity: .7; margin: -3px -4px 0 0; border: 0; background: none; }
.toast .t-close:hover { opacity: 1; color: inherit; border: 0; background: none; }

/* ============================================================
   Progress
   ============================================================ */
.pbar { height: 8px; background: var(--bg-sunken); border-radius: 999px; overflow: hidden; }
.pbar > span { display: block; height: 100%; background: var(--c-primary); border-radius: 999px; transition: width .4s ease; }
/* Стадия без счёта: сервер занят, но сколько осталось — не знает. Полоса
   бежит без числа, чтобы не выдумывать процент (см. ImpProgress). */
.pbar.pbar-indet > span { width: 35%; animation: pbar-indet 1.3s ease-in-out infinite; }
@keyframes pbar-indet { 0% { transform: translateX(-100%); } 100% { transform: translateX(300%); } }
@media (prefers-reduced-motion: reduce) { .pbar.pbar-indet > span { animation: none; width: 100%; opacity: .45; } }
.spinner { width: 20px; height: 20px; border-radius: 50%; border: 2.5px solid var(--ring); border-top-color: var(--c-primary); animation: spin 1s linear infinite; }
.spinner.lg { width: 40px; height: 40px; border-width: 4px; }
@keyframes spin { to { transform: rotate(360deg); } }

/* ============================================================
   Auth screen
   ============================================================ */
/* Вход — как .auth в макете: без карточки, колонка в 392px на белом листе,
   марка с названием сверху слева, заголовок и подпись по левому краю. */
.auth-wrap { min-height: 100%; display: grid; place-items: center; padding: 28px 18px; background: var(--bg); }
.auth-card { width: 100%; max-width: 392px; background: none; border: 0; border-radius: 0; box-shadow: none; padding: 0; }
.auth-brand { display: flex; align-items: center; gap: 9px; font-weight: 600; letter-spacing: -.015em; font-size: 15px; margin-bottom: 15px; }
.auth-logo { width: 18px; height: 18px; border-radius: 5px; background: var(--btn); color: var(--btn-ink); display: grid; place-items: center; flex: 0 0 18px; }
.auth-logo svg { width: 11px; height: 11px; }
.auth-card h1 { text-align: left; font-size: var(--fs-h1); font-weight: 600; letter-spacing: -.02em; text-wrap: balance; }
.auth-card .auth-sub { text-align: left; color: var(--text-3); font-size: 13.5px; margin-top: 5px; margin-bottom: 15px; max-width: 70ch; }
/* Файл с лендинга ждёт человека: строка над полями, чтобы вопрос «а мой
   документ не потерялся?» получил ответ раньше, чем он начнёт заполнять форму. */
.auth-handoff { display: flex; gap: 8px; align-items: flex-start; font-size: 13px; line-height: 1.45;
  color: var(--text-2); background: var(--c-primary-soft, var(--bg-2, transparent)); border: 1px solid var(--border);
  border-radius: 9px; padding: 9px 11px; margin-bottom: 14px; }
.auth-handoff svg { flex: 0 0 auto; margin-top: 2px; color: var(--c-primary); }
.auth-card .btn-block { height: 46px; font-size: 15px; border-radius: 9px; }
.auth-card .input { padding: 9px 11px; font-size: 14px; }
.auth-card .linklike { text-decoration: none; }
.auth-card .linklike:hover { text-decoration: underline; }
.auth-foot { text-align: left; font-size: 11.5px; color: var(--text-4); margin-top: 18px; line-height: 1.5; }
.auth-theme { position: fixed; top: 18px; right: 18px; }

/* Выбор языка интерфейса — ПОД подписью и по центру, одинаково на телефоне
   и на мониторе. Список, а не ряд кнопок: языков будет больше двух, а ряд
   при росте переносится на вторую строку и двигает всё под собой. Ширина
   по содержимому (не 100%, как у поля в форме): это не поле ввода, а подпись
   «на каком языке с вами разговаривают», и растянутый во всю карточку
   он читался бы как ещё одна строка формы. */
.auth-langs { display: flex; justify-content: center; align-items: center; gap: 7px; margin-top: 16px; }
.auth-langs .al-ic { display: inline-flex; color: var(--text-4); }
.auth-langs .select {
  width: auto; max-width: 100%; height: 30px; padding: 3px 28px 3px 8px;
  font-size: 12.5px; color: var(--text-3); background-color: transparent;
  border-color: transparent; background-position: right 8px center;
}
.auth-langs .select:hover { border-color: var(--border); color: var(--text); }
.auth-langs .select:focus { border-color: var(--border-strong); background-color: var(--bg); }

/* Upload dropzone */
.dropzone {
  border: 1.6px dashed var(--border-strong); border-radius: 12px;
  padding: 34px 20px; text-align: center; background: var(--bg-hover);
  transition: border-color .15s ease, background-color .15s ease; cursor: pointer;
}
.dropzone:hover { border-color: var(--text-4); }
.dropzone.drag { border-color: var(--c-primary); background: var(--c-primary-soft); }
.dropzone .dz-ic { color: var(--text-3); margin-bottom: 12px; }

/* Bars (preflight routing) */
.hbar-row { display: grid; grid-template-columns: 150px 1fr 130px; gap: 14px; align-items: center; padding: 9px 0; }
.hbar-track { height: 26px; background: var(--bg-sunken); border-radius: 7px; overflow: hidden; }
.hbar-fill { height: 100%; border-radius: 7px; display: flex; align-items: center; padding: 0 10px; color: var(--text-on-accent); font-size: 12px; font-weight: 500; min-width: fit-content; transition: width .5s ease; }

/* Ring */
.ring-wrap { display: grid; place-items: center; position: relative; }
.ring-center { position: absolute; text-align: center; }
.ring-center .rc-val { font-size: 30px; font-weight: 500; letter-spacing: -.03em; font-variant-numeric: tabular-nums; }
.ring-center .rc-lab { font-size: 12px; color: var(--text-3); font-weight: 500; }

/* Kanban */
.kanban { display: grid; grid-template-columns: repeat(4, 1fr); gap: var(--sp-md); align-items: start; }
.kcol { background: var(--bg-sunken); border: 1px solid var(--border); border-radius: var(--r-lg); padding: 12px; }
.kcol-head { display: flex; align-items: center; justify-content: space-between; padding: 4px 6px 12px; font-weight: 500; font-size: 13.5px; }
.kcard { background: var(--card); border: 1px solid var(--border); border-radius: var(--r-md); padding: 13px; margin-bottom: 10px; cursor: grab; transition: border-color .12s ease; }
.kcard:hover { border-color: var(--border-strong); }
.kcard:last-child { margin-bottom: 0; }

/* avatars */
.avatar { width: 30px; height: 30px; border-radius: 50%; display: grid; place-items: center; font-size: 12px; font-weight: 500; color: var(--text-on-accent); flex: 0 0 auto; }
/* Аватар в шапке — .ava макета: без личного цвета, в рамке на подложке.
   Цвет человека остаётся у списков команды, где он различает людей. */
.avatar.avatar-plain { background: var(--bg-sunken); color: var(--text-3); border: 1px solid var(--border); font-size: 11px; }
.facepile { display: flex; }
.facepile .avatar { margin-left: -8px; border: 2px solid var(--card); }
.facepile .avatar:first-child { margin-left: 0; }

/* empty state */
.empty { text-align: center; padding: 26px 16px; color: var(--text-3); font-size: 13.5px; }
.empty .e-ic { color: var(--border-strong); margin-bottom: 12px; }
.empty h3 { color: var(--text-2); font-size: 15px; font-weight: 500; margin-bottom: 5px; }

/* misc utilities */
.tnum { font-variant-numeric: tabular-nums; }
.divider { height: 1px; background: var(--border); margin: var(--sp-md) 0; }
.kbd { font-family: ui-monospace, monospace; font-size: 11px; background: var(--bg-sunken); border: 1px solid var(--border); border-bottom-width: 2px; border-radius: 5px; padding: 2px 6px; color: var(--text-2); }
.flag { font-size: 13px; }
.mono { font-family: ui-monospace, "SF Mono", Menlo, monospace; }

@media (max-width: 900px) {
  .grid-3, .grid-4 { grid-template-columns: repeat(2, 1fr); }
  .kanban { grid-template-columns: 1fr; }
  .hbar-row { grid-template-columns: 110px 1fr; }
  .hbar-row .hbar-cost { grid-column: 2; text-align: right; }
}
@media (max-width: 640px) {
  .cell-edit .cell-edit-ta { font-size: 16px !important; }
  .grid-2, .grid-3, .grid-4 { grid-template-columns: 1fr; }
  .page { padding: var(--sp-lg) var(--sp-md) var(--sp-2xl); }
  .st-num { font-size: 26px; }
}

/* ---- Drill-down: clickable metric cards & table rows → editor ---- */
tr.row-checked td { background: color-mix(in srgb, var(--c-primary) 8%, transparent) !important; }
.drill-metric { transition: box-shadow .12s, transform .12s; }
.drill-metric:hover { box-shadow: 0 0 0 2px var(--c-primary); transform: translateY(-1px); }
.drill-row { cursor: pointer; }
.drill-row:hover td { background: var(--bg-hover) !important; }

/* Панель «Перевести и доделать» (tab_preflight.jsx, RunPanel): шаги сеткой.
   Флекс здесь ломался: подпись действующей модели сжималась до ширины одного
   слова и вставала столбиком, а селектор судьи растягивался на всю строку.
   Ниже 720 px четыре колонки складываются в две — строка занимает две линии. */
.tk-grid { display: grid; grid-template-columns: minmax(110px, 150px) minmax(140px, 200px) minmax(0, 1fr) auto;
           gap: 6px 12px; align-items: center; font-size: 13px; }
.tk-grid .tk-label { min-width: 0; }
.tk-grid .tk-select { width: 100%; min-width: 0; font-size: 12.5px; padding: 3px 28px 3px 8px; }
.tk-grid .tk-eff { min-width: 0; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.tk-grid .tk-cost { white-space: nowrap; text-align: right; font-variant-numeric: tabular-nums; }
/* Без устройства прогона (не администратор) колонок модели и действующей
   модели нет вовсе: пустые ячейки оставили бы полстроки дыры между шагом
   и составом. Остаётся «шаг · сколько сегментов». */
.tk-grid-plain { grid-template-columns: minmax(0, 1fr) auto; }
@media (max-width: 720px) {
  /* Две колонки и ПЛОТНАЯ раскладка: dense сажает подпись модели в дыру
     рядом с названием шага, а цену — рядом с селектором. Без dense
     курсор авторазмещения не возвращается назад, и шаг растягивался
     бы на три строки с пустыми ячейками. */
  .tk-grid { grid-template-columns: minmax(0, 1fr) auto; gap: 4px 10px; grid-auto-flow: row dense; }
  .tk-grid .tk-select { grid-column: 1; }
  .tk-grid .tk-eff { grid-column: 2; text-align: right; max-width: 45vw; }
  .tk-grid .tk-cost { grid-column: 2; padding-bottom: 6px; }
}

/* ============================================================
   Резиновая вёрстка: окно любой ширины и телефон
   ------------------------------------------------------------
   Экраны системы уже складываются по порогам (1520 / 1180 / 1100 / 900 /
   720 / 640) — эта часть закрывает то, что от ширины НЕ зависит и потому
   в те пороги не попало: палец вместо курсора, зум поля в iOS, окно
   и уведомления во весь экран, таблица сегментов, которую на телефоне
   незачем возить вбок. Правила стоят ПОСЛЕДНИМИ намеренно: они правят
   готовые компоненты, а не заводят свои, и при равной силе побеждает
   то, что ниже.
   ============================================================ */

/* Длинное слово рвётся, а не выпирает за край страницы. Термин, адрес
   файла и латинское название вида шире колонки на телефоне обычное дело,
   а перенос по слогам браузер сделать не может — без этого одна строка
   растягивает экран целиком, и горизонтально ездит ВСЯ страница. */
body { overflow-wrap: break-word; }

/* Таблица в карточке: на широком экране не меняется НИЧЕГО (шапка таблицы
   залипает при прокрутке страницы, а свой контейнер прокрутки отрезал бы её
   от этой прокрутки), на узком — таблица ездит вбок внутри себя. Иначе вбок
   едет ВСЯ страница: шесть колонок в 360 px не помещаются никогда.
   Отдельно от `.tbl-scroll`: тот прокручивается всегда и стоит там, где
   таблица и так живёт в своей рамке (глоссарий, сегменты). */
.tbl-fit { min-width: 0; }
@media (max-width: 900px) { .tbl-fit { overflow-x: auto; } }

/* Прокрутка к карточке сегмента (нажали строку на узком экране) идёт под
   ЗАЛИПАЮЩУЮ панель прогона: без отступа верх карточки — номер строки
   и статус — оказывается под ней. Панель бывает выше (раскрытые фильтры,
   полоса идущего прогона), поэтому берём с запасом. */
.editor-side { scroll-margin-top: 96px; }

/* Подсказка висит на fixed-координатах, посчитанных от значка: у края
   экрана она вылезала наружу вместе со страницей. */
.tooltip { max-width: min(320px, calc(100vw - 24px)); }

/* Спор моделей по роли в админке: подсвечены ровно спорящие строки,
   пара выбранного предупреждения — сильнее (AdminSystemModels). */
.tbl tr.adm-dispute > td { background: color-mix(in srgb, var(--c-warning) 9%, transparent); }
.tbl tr.adm-dispute.on > td { background: color-mix(in srgb, var(--c-warning) 22%, transparent); }
.tbl tr.adm-dispute.on > td:first-child { box-shadow: inset 3px 0 0 var(--c-warning); }
.input.adm-dispute-input { border-color: var(--c-warning); }
.adm-conflict { cursor: pointer; border-radius: 6px; padding: 2px 6px; margin: 0 -6px; }
.adm-conflict:hover, .adm-conflict.on { background: color-mix(in srgb, var(--c-warning) 14%, transparent); }
.adm-conflict:focus-visible { outline: 2px solid var(--c-warning); outline-offset: 1px; }


/* ---------- Метрики (админка) ----------
   Экран читается сверху вниз и по ЧИСЛАМ: цифра стоит слева крупно
   и цветом вида (заработать / теряем / чинить), фраза — справа обычным
   кеглем. Обратный порядок («длинная фраза, а число где-то в ней»)
   заставляет читать всё подряд, а здесь нужно скользить взглядом
   по цифрам и останавливаться на крупной. */
.met-list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 8px; }
.met-hint { display: flex; gap: 10px; align-items: baseline; line-height: 1.45; }
.met-num { font-size: 18px; font-weight: 700; min-width: 64px; text-align: right; flex: none; }
.met-num.ok { color: var(--c-success); }
.met-num.warn { color: var(--c-warning); }
.met-num.bad { color: var(--c-error); }
/* Полоса доли расхода по шагу: сравнение глазами вместо чтения чисел. */
.met-bar { background: var(--c-border); border-radius: 4px; height: 8px; overflow: hidden; }
.met-bar > i { display: block; height: 100%; background: var(--c-primary); }
/* Сводка словами — моноширинным: её копируют целиком в Telegram и агенту,
   и переносы в ней несущие. */
.met-pre { white-space: pre-wrap; font-size: 12.5px; line-height: 1.5; margin: 0;
           max-height: 460px; overflow: auto; }
/* Парето на экране «Возможности»: число, полоса доли и фраза одной сеткой.
   Хвост (ниже 80%) приглушён — разница между «браться сейчас» и «браться
   последним» должна быть видна без чтения чисел. */
.opp-row { display: grid; grid-template-columns: 64px minmax(80px, 18%) 1fr; gap: 10px;
           align-items: baseline; line-height: 1.45; }
.opp-row .met-bar { align-self: center; }
.opp-row.tail { opacity: .62; }
.opp-cut { border-top: 1px dashed var(--c-border); padding-top: 8px; margin-top: 2px; }
.opp-steps { display: flex; flex-wrap: wrap; gap: 10px; align-items: center; }
.opp-step { border: 1px solid var(--c-border); border-radius: 10px; padding: 8px 12px; }
.opp-step b { display: block; font-size: 20px; line-height: 1.2; }
@media (max-width: 640px) { .met-num { min-width: 48px; font-size: 16px; }
  /* На телефоне полоса уезжает под строку: колонка в 18% от 360 px — это
     65 пикселей, на которых доля не читается. */
  .opp-row { grid-template-columns: 52px 1fr; }
  .opp-row .met-bar { grid-column: 1 / -1; order: 2; } }

/* ---------- Палец вместо курсора ----------
   Цель в 28 px промахивается пальцем, и промах на этих экранах стоит
   дорого: рядом с «Подтвердить» стоит «Перевести заново». Растёт только
   ВЫСОТА и поля — подписи, вес и цвета остаются макетными, потому что
   речь про попадание, а не про облик. Признак — не ширина окна (планшет
   в ландшафте шире ноутбука), но и не один только грубый указатель:
   у ноутбука с сенсорным экраном ОСНОВНОЙ указатель точный, а `hover: none`
   бывает только там, где наводить нечем, — то есть ровно на телефоне
   и планшете. Облик широких экранов при этом не меняется. */
/* ---------- Оплата (pay.jsx, инвариант 39) ---------- */
.pay-methods { display: grid; grid-template-columns: repeat(auto-fill, minmax(min(260px, 100%), 1fr)); gap: 8px; }
.pay-method { display: flex; align-items: center; gap: 10px; padding: 10px 12px; border: 1px solid var(--c-border);
  border-radius: var(--r-md); cursor: pointer; background: var(--bg-elevated); }
.pay-method:hover { background: var(--bg-hover); }
.pay-method.is-on { border-color: var(--c-primary); box-shadow: 0 0 0 1px var(--c-primary) inset; background: var(--c-primary-soft); }
.pay-method input { margin: 0; }
.pay-method-text { display: flex; flex-direction: column; gap: 4px; min-width: 0; }
.pay-method-text > .row { margin-bottom: 2px; }
.pay-method-text .dim { font-size: 12px; }
.pay-logo { flex: none; display: inline-flex; align-items: center; justify-content: center;
  width: 52px; height: 32px; padding: 4px 6px; box-sizing: border-box; background: #fff; border-radius: 6px;
  box-shadow: inset 0 0 0 1px rgba(0,0,0,.12); }
.pay-logo img { display: block; max-width: 100%; max-height: 100%; object-fit: contain; }
.pay-card .btn.is-on { background: var(--c-primary-soft); border-color: var(--c-primary); }
.pay-done { padding: 10px 12px; border-radius: var(--r-md); background: var(--c-primary-soft); }
.pay-bar { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; margin: 0 0 12px; padding: 10px 14px;
  border-radius: var(--r-md); background: var(--bg-sunken); border: 1px solid var(--c-warning); font-size: 13px; }
.pay-bar > span { flex: 1 1 240px; }
.pay-bar.is-ok { border-color: var(--c-success); }

@media (pointer: coarse) and (hover: none) {
  .btn { height: 40px; padding: 0 16px; }
  .btn-sm { height: 34px; padding: 0 12px; }
  .btn-lg { height: 50px; }
  .iconbtn { width: 38px; height: 38px; }
  .iconbtn.sm { width: 34px; height: 34px; }
  .status-cell-btn { width: 36px; height: 36px; }
  .fbtn, .segmented button { padding: 9px 13px; }
  .kb-chip { padding: 7px 12px; }
  .seg button { padding: 9px 13px; }
  .navi { padding: 11px 10px; }
  .page-num { min-width: 34px; height: 34px; }
  input[type="checkbox"] { width: 18px; height: 18px; }
  .check .box, .radio .ring { width: 20px; height: 20px; flex-basis: 20px; }
}

/* ---------- Телефон ---------- */
@media (max-width: 640px) {
  /* Поле мельче 16 px заставляет iOS Safari ПРИБЛИЗИТЬ страницу при входе
     в него, и обратно она сама не возвращается: человек дописывает перевод
     на экране, увеличенном вдвое, и не видит соседних строк. Лечится
     единственным известным способом — кеглем поля. !important здесь
     не украшение: часть полей несёт размер инлайновым стилем из .jsx
     (панели прогона), а инлайн сильнее любого селектора. */
  input, select, textarea,
  .input, .select, .textarea, .tk-grid .tk-select { font-size: 16px !important; }

  /* Переносится ряд «две группы по КРАЯМ»: на мониторе между ними пустое
     место, на телефоне второй группе места нет вовсе, и она уходит за край.
     Обычный `.row` не трогаем намеренно — в системе он значит «в строку»
     и противопоставлен `.row-wrap` (там перенос попросили явно); сделай
     перенос общим — и полосы шкалы back-check оторвутся от своих подписей:
     перенос считается по БАЗИСУ, до всякого сжатия. */
  .row.between { flex-wrap: wrap; }

  /* Поля страницы и панелей — по 14 px: на 360 px каждый лишний пиксель
     поля отнимается у текста, ради которого экран и открыли. */
  .editor-toolbar { padding: 8px 14px; gap: 8px; }
  .editor-main { padding-left: 14px; padding-right: 14px; }
  .editor-body { padding: 14px 14px 40px; gap: 12px; }
  .editor-body .editor-main { padding: 0; }
  .card-pad { padding: 14px; }
  /* Карточка сегмента на телефоне — рабочее место переводчика: поля по кругу
     отнимали у поля перевода пятую часть ширины, а высота в три строки
     не показывает и одного абзаца. */
  .editor-side { padding: 14px; }
  .textarea { min-height: 120px; }
  .ready-strip { padding: 14px; }
  .ready-pct { font-size: 34px; }
  .ask { padding-right: 12px; }
  .page-stack { gap: 16px; }

  /* Окно — листом снизу: на телефоне оно занимает экран целиком, а палец
     до нижнего края достаёт, до середины — нет. Скругление сверху и
     отсутствие нижней рамки говорят, откуда лист выехал. */
  .overlay { padding: 0; align-items: end; justify-items: stretch; }
  .modal { max-width: none; max-height: 92vh; max-height: 92dvh; border-radius: 14px 14px 0 0; border-bottom: 0; }
  .modal-body { padding: 16px; }
  .modal-head, .modal-foot { padding: 14px 16px; }
  /* Кнопки окна стоят у самой кромки экрана, а там полоса-индикатор
     и панель браузера: без отступа по безопасной зоне «Применить»
     и «Откатить» оказываются под ними. */
  .modal-foot { flex-wrap: wrap; padding-bottom: calc(14px + env(safe-area-inset-bottom)); }
  .modal-foot .btn { flex: 1 1 auto; }

  /* Уведомление в 360 px шириной у края экрана в 360 px — это уведомление,
     от которого видно половину. */
  .toasts { left: 10px; right: 10px; bottom: calc(10px + env(safe-area-inset-bottom)); width: auto; max-width: none; }
}

/* ---------- Таблица сегментов на телефоне ----------
   Шесть колонок в 360 px не помещаются никак: с горизонтальной лентой
   оригинал и перевод видны по очереди, а сравнить их — единственное, зачем
   эту таблицу открывают. Поэтому строка раскладывается СВЕРХУ ВНИЗ:
   служебная шапка (галочка, номер, слово о строке, действие), под ней
   оригинал, под ним перевод с отчёркиванием слева.
   Разметка при этом не трогается: порядок ячеек в `SegRow` постоянный
   (шесть штук, без colspan), и раскладка держится на их номерах. */
@media (max-width: 700px) {
  /* Шапку НЕ прячем: в первой ячейке живёт «отметить все на странице»
     (без неё пакетные команды на телефоне недоступны вовсе), а «Как было ·
     RU» и «Как стало · EN» — единственная подпись, по которой в карточке
     отличают оригинал от перевода. Остальные заголовки убираем: их колонок
     в карточке нет. */
  .editor-body table.tbl, .editor-body .tbl thead, .editor-body .tbl thead tr,
  .editor-body .tbl tbody { display: block; width: 100%; }
  /* Шапка переносится: обе подписи идут `white-space: nowrap` (это правило
     колонок), и в 360 px пара «Как было · RU» + «Как стало · EN» в одну
     строку не встаёт — без переноса она вылезла бы за край. */
  .editor-body .tbl thead tr { display: flex; align-items: center; flex-wrap: wrap; gap: 4px 10px; padding: 0 13px; }
  .editor-body .tbl thead th { padding: 8px 0; width: auto; }
  /* Поля строки держит сама строка, а не первая ячейка: ширина у неё
     задана ИНЛАЙНОМ (36 px в .jsx, и селектором её не перебить), и поле
     внутри отнималось бы у чекбокса — под палец он 18 px. */
  .editor-body .tbl thead th:first-child { min-width: 36px; }
  .editor-body .tbl thead th:nth-child(2),
  .editor-body .tbl thead th:nth-child(5),
  .editor-body .tbl thead th:nth-child(6) { display: none; }
  .editor-body .tbl tbody tr {
    display: grid; grid-template-columns: auto auto minmax(0, 1fr) auto;
    gap: 7px 10px; padding: 11px 13px; border-bottom: 1px solid var(--border);
  }
  .editor-body .tbl tbody tr:last-child { border-bottom: 0; }
  /* Отметку галочкой красит ЯЧЕЙКА (tr.row-checked td), а ячейки тут стали
     блоками без полей: подложка ложилась бы тремя пятнами по тексту, между
     которыми белые зазоры строки. Красим строку — как это делает «выбрана». */
  .editor-body .tbl tbody tr.row-checked { background: color-mix(in srgb, var(--c-primary) 8%, transparent); }
  .editor-body .tbl tbody tr.row-checked td { background: none !important; }
  .editor-body .tbl tbody td { display: block; padding: 0; border: 0; }
  .editor-body .tbl tbody td.src-cell, .editor-body .tbl tbody td.tgt-cell { width: auto; }
  /* Место каждой ячейки названо явно, все шесть: при смешанном размещении
     (часть ячеек со своей строкой, часть — самотёком) браузер ставит
     оставшиеся куда придётся, и порядок зависел бы от того, какая ячейка
     в этот раз пустая. */
  .editor-body .tbl tbody td:nth-child(1) { grid-column: 1; grid-row: 1; }
  .editor-body .tbl tbody td:nth-child(2) { grid-column: 2; grid-row: 1; width: auto; padding-top: 0; }
  .editor-body .tbl tbody td:nth-child(5) { grid-column: 3; grid-row: 1; justify-self: end; }
  .editor-body .tbl tbody td:nth-child(6) { grid-column: 4; grid-row: 1; }
  .editor-body .tbl tbody td:nth-child(3) { grid-column: 1 / -1; grid-row: 2; }
  .editor-body .tbl tbody td:nth-child(4) {
    grid-column: 1 / -1; grid-row: 3;
    padding-left: 11px; border-left: 2px solid var(--border);
  }
  /* Субтитры: седьмая ячейка — «Время» — стоит ТРЕТЬЕЙ, и все следующие
     сдвинуты на одну. Время — в первой строке карточки рядом с номером,
     одной строкой «начало конец». */
  .editor-body .tbl.tbl-cues thead th:nth-child(3),
  .editor-body .tbl.tbl-cues thead th:nth-child(7) { display: none; }
  .editor-body .tbl.tbl-cues thead th:nth-child(5) { display: revert; }
  .editor-body .tbl.tbl-cues thead th:nth-child(6) { display: none; }
  .editor-body .tbl.tbl-cues tbody tr { grid-template-columns: auto auto auto minmax(0, 1fr) auto; }
  .editor-body .tbl.tbl-cues tbody td:nth-child(3) { grid-column: 3; grid-row: 1; width: auto; padding-top: 0; }
  .editor-body .tbl.tbl-cues tbody td:nth-child(3) div { display: inline; margin-right: 6px; }
  .editor-body .tbl.tbl-cues tbody td:nth-child(6) { grid-column: 4; grid-row: 1; justify-self: end; }
  .editor-body .tbl.tbl-cues tbody td:nth-child(7) { grid-column: 5; grid-row: 1; }
  .editor-body .tbl.tbl-cues tbody td:nth-child(4) { grid-column: 1 / -1; grid-row: 2; padding-left: 0; border-left: 0; }
  .editor-body .tbl.tbl-cues tbody td:nth-child(5) {
    grid-column: 1 / -1; grid-row: 3; justify-self: stretch;
    padding-left: 11px; border-left: 2px solid var(--border);
  }
  /* Поиск съезжает на свою строку: не шириной в 100% (у поля она задана
     инлайном — `flex: 1` в SearchInput, и селектором её не перебить),
     а нижним пределом. Поле номера НЕ сужаем: в книге бывает пять цифр,
     а кегль на телефоне поднят до 16 px — в узком поле номер обрезался бы
     прямо при вводе. */
  .table-head .search { min-width: 190px; }
}

/* ---------- Оболочка на узком экране ----------
   Ниже 900 px меню ложится лентой (см. выше). Пряталось в ней всё, что
   не пункт меню, — вместе с ПЕРЕКЛЮЧАТЕЛЕМ КОМАНДЫ: человек, состоящий
   в двух организациях, с телефона не мог перейти во вторую вовсе, а её
   глоссарий и проекты и есть его работа. Подпись организации (когда
   команда одна и переключать нечего) прячется по-прежнему: она ничего
   не делает и занимает половину ленты. */
@media (max-width: 900px) {
  .side { padding-left: max(12px, env(safe-area-inset-left)); padding-right: max(12px, env(safe-area-inset-right)); }
  .side .org-chip-sel { display: flex; flex: 0 0 auto; }
  /* Высота коробки задана под кегль 13 px, а на телефоне поле крупнее
     (см. правило против зума iOS) — жёсткие 26 px подрезали бы выносные. */
  .side .org-chip-sel .select { max-width: 150px; height: auto; min-height: 30px; }
  .navi { flex: 0 0 auto; }
}

/* Экран «Скачать перевод»: слева выбор формата, справа состав файла.
   Порог — 980 px: ниже правая колонка сжимается до нечитаемого столбца
   слов, а состав файла читают сверху вниз ничуть не хуже. */
.export-cols { grid-template-columns: minmax(0, 1.4fr) minmax(0, 1fr); gap: 24px; align-items: start; }
@media (max-width: 980px) { .export-cols { grid-template-columns: minmax(0, 1fr); gap: 20px; } }


/* ---------- Поддержка и знакомство (onboarding.jsx) ----------
   Оба висят НАД оболочкой, поэтому живут здесь, а не среди правил вкладок.
   Цвета — только переменные: виджет обязан работать в обеих темах, а
   захардкоженный белый фон в тёмной теме — это светящийся прямоугольник. */

/* Кнопка-значок в правом нижнем углу. `position: fixed` с отступом от
   безопасной зоны: на телефоне с вырезом иначе садится на системную
   полосу жестов, и нажать её нельзя вовсе. */
.sup { position: fixed; z-index: 60;
  right: max(16px, env(safe-area-inset-right));
  bottom: max(16px, env(safe-area-inset-bottom)); }
.sup-fab { position: relative; display: grid; place-items: center;
  width: 52px; height: 52px; margin-left: auto; border: 0; border-radius: 50%;
  background: var(--btn); color: var(--btn-ink); cursor: pointer;
  box-shadow: var(--shadow-pop); }
.sup-fab:hover { background: var(--btn-hover); }
.sup-fab:focus-visible { outline: 2px solid var(--c-primary); outline-offset: 3px; }
/* Счётчик непрочитанного. Пилюля, а не точка: «вам ответили» и «вам
   ответили трижды» — разные новости. */
.sup-dot { position: absolute; top: -3px; right: -3px; min-width: 19px; height: 19px;
  padding: 0 5px; display: grid; place-items: center; border-radius: 10px;
  background: var(--c-error); color: #fff; font-size: 11px; font-weight: 700;
  font-style: normal; }

.sup-panel { position: absolute; right: 0; bottom: 62px; width: 340px;
  max-height: min(70vh, 520px); display: flex; flex-direction: column;
  background: var(--card); border: 1px solid var(--border);
  border-radius: 14px; box-shadow: var(--shadow-lg); overflow: hidden; }
.sup-head { display: flex; align-items: flex-start; justify-content: space-between;
  gap: 10px; padding: 12px 10px 12px 14px; border-bottom: 1px solid var(--border); }
.sup-head b { display: block; font-size: 14px; }
.sup-head span { display: block; margin-top: 2px; font-size: 12px; color: var(--text-3); }
.sup-body { flex: 1; overflow-y: auto; padding: 12px 14px;
  display: flex; flex-direction: column; gap: 8px; }
.sup-empty { margin: 0; font-size: 13px; color: var(--text-3); }
.sup-msg { max-width: 85%; padding: 8px 11px; border-radius: 12px; }
.sup-msg p { margin: 0; font-size: 13.5px; line-height: 1.45; white-space: pre-wrap;
  overflow-wrap: anywhere; }
.sup-msg time { display: block; margin-top: 3px; font-size: 10.5px; opacity: .65; }
.sup-msg.mine { align-self: flex-end; background: var(--c-primary-soft); color: var(--text); }
.sup-msg.theirs { align-self: flex-start; background: var(--bg-sunken);
  color: var(--text); }
.sup-form { display: flex; gap: 8px; align-items: flex-end; padding: 10px 12px;
  border-top: 1px solid var(--border); }
.sup-form textarea { flex: 1; resize: none; padding: 8px 10px; font: inherit;
  font-size: 13.5px; color: var(--text); background: var(--card);
  border: 1px solid var(--control-line); border-radius: 10px; }
.sup-form textarea:focus { outline: 2px solid var(--ring); outline-offset: 0; }
.sup-form .btn { padding: 9px 12px; }

/* Знакомство. Затемнение — ЧЕТЫРЕ прямоугольника вокруг цели, а не
   clip-path по одному: подсвеченный элемент остаётся нажимаемым, и человек
   может попробовать прямо сейчас, не дожидаясь конца тура. */
.tour { position: fixed; inset: 0; z-index: 70; pointer-events: none; }
.tour-veil { position: fixed; background: var(--scrim); pointer-events: auto; }
.tour-ring { position: fixed; border: 2px solid var(--c-primary); border-radius: 10px;
  box-shadow: 0 0 0 4px var(--ring); pointer-events: none; }
.tour-tip { position: fixed; z-index: 71; width: min(320px, calc(100vw - 24px));
  padding: 16px; background: var(--card); border: 1px solid var(--border);
  border-radius: 14px; box-shadow: var(--shadow-lg); pointer-events: auto; }
.tour-n { margin: 0 0 6px; font-size: 11px; font-weight: 700; letter-spacing: .04em;
  text-transform: uppercase; color: var(--text-4); }
.tour-tip h3 { margin: 0 0 6px; font-size: 16px; }
.tour-txt { margin: 0 0 14px; font-size: 13.5px; line-height: 1.5; color: var(--text-3); }
.tour-act { display: flex; gap: 8px; justify-content: flex-end; }

/* Телефон: виджет во всю ширину — панель в 340 px на экране в 360 px
   прижимается к краям и обрезается. Тур на узком экране подсказку
   не двигает: она уже считается по месту цели. */
@media (max-width: 480px) {
  .sup-panel { position: fixed; right: 10px; left: 10px; bottom: 76px; width: auto;
    max-height: 66vh; }
  /* Кегль поля 16 px: мельче iOS Safari ПРИБЛИЖАЕТ страницу при входе
     в поле и сам обратно не отдаляет (см. раздел про резиновую вёрстку). */
  .sup-form textarea { font-size: 16px; }
}

/* ---------- Вкладка «Обучение» (tab_learn.jsx) ----------
   Содержание и рисунки приходят с сервера, облик — НАШ: те же .card,
   те же переменные. Цвета в рисунках заданы через var(--…), поэтому
   светлую и тёмную тему они переживают сами. */

/* Три коротких факта строкой — как чипы на «Проверке». */
.learn-facts { display: flex; flex-wrap: wrap; gap: 8px; margin: 0 0 16px; padding: 0; list-style: none; }
.learn-facts li { font-size: 12.5px; color: var(--text-3);
  padding: 5px 11px; border: 1px solid var(--border); border-radius: 999px; }

/* Три действия в ряд: знакомство, поддержка, печатная версия.
   auto-fit с min() — ниже 520 px колонка не требует ширины больше
   контейнера (см. раздел про резиновую вёрстку). */
.learn-acts { display: grid; gap: 10px; margin-bottom: 20px;
  grid-template-columns: repeat(auto-fit, minmax(min(240px, 100%), 1fr)); }
.learn-act { display: flex; align-items: flex-start; gap: 10px; text-align: left;
  cursor: pointer; font: inherit; color: inherit; text-decoration: none; }
.learn-act:focus-visible { outline: 2px solid var(--c-primary); outline-offset: 2px; }
.learn-act-ic { flex: 0 0 auto; display: grid; place-items: center;
  width: 30px; height: 30px; border-radius: 9px;
  background: var(--c-primary-soft); color: var(--c-primary); }
.learn-act-t { display: grid; gap: 2px; min-width: 0; }
.learn-act-t b { font-size: 13.5px; font-weight: 600; }
.learn-act-t span { font-size: 12px; color: var(--text-3); }

/* Шаг: номер слева, текст и рисунок справа. Ниже 640 px номер встаёт
   над заголовком — в узкой колонке он съедал четверть ширины. */
.learn-step { display: grid; grid-template-columns: 40px 1fr; gap: 0 14px; align-items: start; }
.learn-n span { display: grid; place-items: center; width: 32px; height: 32px;
  border-radius: 50%; border: 1px solid var(--border-strong);
  font-size: 12.5px; font-weight: 700; color: var(--text-3); font-variant-numeric: tabular-nums; }
.learn-body { min-width: 0; }
.learn-kicker { margin: 0 0 2px; font-size: 11px; font-weight: 700; letter-spacing: .04em;
  text-transform: uppercase; color: var(--text-4); }
.learn-step h2 { margin: 0 0 8px; font-size: 16px; font-weight: 600; }
.learn-p { margin: 0 0 8px; font-size: 13.5px; line-height: 1.55; color: var(--text-2); max-width: 66ch; }
.learn-p b { color: var(--text); font-weight: 600; }
/* Рисунок схемы. Своя подложка, чтобы рамка «окна» не сливалась
   с карточкой: в светлой теме и то и другое белое.
   ЗДЕСЬ ЖЕ переименование переменных: SVG собирает `backend/tutorial.py`
   в палитре анкет (--panel, --mint, --stroke…), а у приложения палитра
   своя. Переписывать сам SVG нельзя — его же рисует страница /tutorial,
   где палитра анкет и есть родная; поэтому имена ПЕРЕОПРЕДЕЛЯЮТСЯ
   на нашей стороне, и обе темы работают сами. Без этого рисунок был бы
   невидим: нерезолвящаяся переменная гасит заливку целиком. */
.learn-shot { margin-top: 10px; padding: 10px; border-radius: var(--r-md);
  background: var(--bg-sunken); border: 1px solid var(--border);
  --panel: var(--bg-sunken);
  --panel-2: var(--card);
  --stroke: var(--border-strong);
  --ink: var(--text);
  --muted: var(--text-3);
  --mint: var(--c-success);
  --mint-ink: var(--text-on-accent);
  --mint-soft: var(--c-primary-soft);
  --teal: var(--c-info);
  --violet: var(--c-purple);
  --tan: var(--c-warning);
  --flag: var(--c-error); }
.learn-shot figure { margin: 0; }
/* Рисунок во всю ширину подложки, но не шире 560 px: дальше схема
   из пяти прямоугольников начинает выглядеть растянутой пустотой. */
.learn-shot svg { display: block; width: 100%; max-width: 560px; height: auto; margin: 0 auto; }
/* Подписи внутри рисунка — наши классы из tutorial.py; кегли заданы
   тут, а не в SVG, чтобы шрифт совпадал с остальной страницей. */
.learn-shot svg .cap { font-size: 11px; fill: var(--text-4); }
.learn-shot svg .sub { font-size: 12px; fill: var(--text-3); }
.learn-shot svg .mid { font-size: 14px; font-weight: 600; fill: var(--text); }
.learn-shot svg .big { font-size: 16px; font-weight: 700; }
.learn-shot svg .btn-t { font-size: 12px; font-weight: 600; }

.learn-h2 { margin: 26px 0 10px; font-size: 17px; font-weight: 600; }
.learn-a { margin: 0; font-size: 13.5px; line-height: 1.55; color: var(--text-2); max-width: 70ch; }

@media (max-width: 640px) {
  .learn-step { grid-template-columns: 1fr; gap: 8px; }
  .learn-n span { width: 28px; height: 28px; }
}

/* ---------- Путь из пяти шагов (app.jsx: STEP_KEYS, stepDone, NextStepBar) ----------
   Номер шага вместо значка и галочка у пройденного: «где я и что дальше»
   читается без единого слова. Галочку ставит только то, что браузер знает
   честно (см. stepDone). */
.step-n { flex: 0 0 18px; width: 18px; height: 18px; border-radius: 50%;
  display: grid; place-items: center; font-size: 11px; font-weight: 600;
  border: 1.5px solid var(--border-strong); color: var(--text-3);
  font-variant-numeric: tabular-nums; }
.navi.on .step-n { border-color: var(--text); color: var(--text); }
.step-n.done { background: var(--c-success); border-color: transparent; color: var(--bg); }
.step-n.done svg { opacity: 1; }
/* «Дальше: …» внизу шага: по полям страницы, не липкая — липкой на телефоне
   тесно рядом с нижней панелью шагов. */
.next-step { padding: 0 var(--pad) 40px;
  display: flex; justify-content: flex-end; }

/* Телефон: пять шагов — нижняя панель под большой палец, остальное
   (Обучение, Профиль, Организация, команда) — в верхней ленте. Сверху пять
   шагов уезжали за край: на снимке с телефона от «Проверки» был виден один значок. */
@media (max-width: 640px) {
  .side .grp-work {
    position: fixed; left: 0; right: 0; bottom: 0; z-index: 50;
    display: grid; grid-template-columns: repeat(5, minmax(0, 1fr)); gap: 0;
    background: var(--header-bg); border-top: 1px solid var(--border);
    padding: 4px max(4px, env(safe-area-inset-right)) max(4px, env(safe-area-inset-bottom)) max(4px, env(safe-area-inset-left));
  }
  .side .grp-work .navi { flex-direction: column; gap: 3px; width: auto; min-width: 0;
    padding: 6px 2px; font-size: 11px; text-align: center; justify-content: center; position: relative; }
  .side .grp-work .navi-t { flex: 0 0 auto; max-width: 100%; }
  .side .grp-work .navi-n { position: absolute; top: 2px; right: 6px; padding: 0 5px; font-size: 10px; }
  .side .grp-work .step-n { width: 22px; height: 22px; flex-basis: 22px; font-size: 12px; }
  /* Место под панель: и у прокрутки страницы, и у плавающих штук. */
  .main { padding-bottom: calc(62px + env(safe-area-inset-bottom)); }
  .sup { bottom: calc(70px + env(safe-area-inset-bottom)); }
  .toasts { bottom: calc(76px + env(safe-area-inset-bottom)); }
  .next-step { padding-bottom: 20px; }
  .next-step .btn { width: 100%; justify-content: center; }
}
@media (max-width: 480px) {
  .sup-panel { bottom: calc(136px + env(safe-area-inset-bottom)); max-height: 58vh; }
}
/* Открытая клавиатура поднимает фиксированную панель над собой, и та
   закрывает поле, в которое человек пишет. Пока фокус в поле — панели нет. */
@media (max-width: 640px) {
  /* Галочки и переключатели клавиатуры не открывают — на них панель остаётся. */
  .shell:has(input:not([type="checkbox"]):not([type="radio"]):focus, textarea:focus, select:focus, [contenteditable]:focus) .side .grp-work { display: none; }
  .side .grp-work .navi-t { overflow: hidden; text-overflow: ellipsis; }
}
/* Первый файл (tab_import.jsx: ImpFirstFile): одна большая зона, дальше —
   один вопрос про язык и одна кнопка. */
.first-file { display: flex; flex-direction: column; gap: 14px; max-width: 640px; margin: 0 auto; }
.dropzone.dropzone-big { padding: 44px 20px; }
/* Открытый проект (tab_import.jsx: ImpFolderView). Три главных места —
   «Переводить», «Проверить строки», «Добавить файл» — видны с первого
   взгляда: файлы слева шире, добавление справа; ниже 980 px — столбиком.
   Цветов своих нет: только токены облика. */
.imp-work { display: grid; grid-template-columns: minmax(0, 3fr) minmax(0, 2fr); gap: var(--sp-md); align-items: start; }
.imp-work.is-empty { grid-template-columns: minmax(0, 640px); justify-content: center; }
.imp-files { display: flex; flex-direction: column; gap: 12px; min-width: 0; }
.imp-file { display: flex; flex-direction: column; gap: 14px; }
.imp-file-title { display: flex; align-items: center; gap: 8px; min-width: 0; font-weight: 600; font-size: 17px; letter-spacing: -.2px; }
.imp-file-title > span { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.imp-tip { display: flex; gap: 12px; align-items: flex-start; padding: 14px 16px; border-radius: var(--r-md); background: var(--c-primary-soft); }
.imp-tip-ic { color: var(--c-primary); flex: 0 0 auto; margin-top: 1px; }
.imp-tip-t { font-weight: 600; font-size: 14.5px; }
.imp-tip p { margin: 3px 0 12px; font-size: 13.5px; line-height: 1.45; color: var(--text); }
.imp-actions { display: flex; gap: 8px; flex-wrap: wrap; align-items: center; }
.imp-more { font-size: 12.5px; }
.imp-more summary { cursor: pointer; color: var(--text-3); width: max-content; max-width: 100%; padding: 6px 0; }
.imp-more summary:hover { color: var(--text); }
.imp-more > div { margin-top: 6px; line-height: 1.45; }
.imp-add { display: flex; flex-direction: column; gap: 12px; }
.imp-add .dropzone { background: var(--card); }
.imp-add .dropzone .btn { pointer-events: none; }
.imp-dicts-min { display: flex; align-items: center; gap: 12px; }
.imp-danger { display: flex; justify-content: flex-end; margin-top: var(--sp-lg); padding-top: var(--sp-md); border-top: 1px solid var(--border); }
@media (max-width: 980px) { .imp-work { grid-template-columns: minmax(0, 1fr); } }
@media (max-width: 640px) {
  .imp-actions .btn-primary { flex: 1 1 100%; }
  .imp-tip .btn { width: 100%; }
  .imp-danger { justify-content: center; }
}
/* Текст только для читалки экрана: номер шага и «пройден» (app.jsx). */
.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; }
/* Линия пути между кружками шагов: от пройденного шага к следующему —
   сплошная зелёная, от непройденного — пунктир; непройденный кружок тоже
   пунктирный. Линия рисуется ПОД кружком пункта и доходит до кружка
   следующего: 100% — высота пункта, 1px — зазор между пунктами, 18px —
   диаметр кружка. Отсчёт — от края ВНУТРЕННЕГО отступа (рамка не в счёт):
   левый край — поле (9 px) + радиус (9) − половина линии (1); по высоте
   линия идёт от низа своего кружка (+1 px) до верха следующего (−1 px),
   и это 100% − 16 px при любом вертикальном поле. z-index — чтобы фон
   следующего пункта (под курсором, открытый) не съедал кончик линии. */
.grp-work .navi { position: relative; }
.grp-work .navi:not(:last-child)::after {
  content: ""; position: absolute; left: 17px; top: calc(50% + 10px); z-index: 1;
  height: calc(100% - 16px); border-left: 2px dashed var(--border-strong); pointer-events: none; }
.grp-work .navi.step-done:not(:last-child)::after { border-left-style: solid; border-left-color: var(--c-success); }
.step-n:not(.done) { border-style: dashed; }
.navi.on .step-n:not(.done) { border-style: solid; }
@media (pointer: coarse) and (hover: none) { .grp-work .navi:not(:last-child)::after { left: 18px; } }
/* Лента и нижняя панель — линия пути там не нужна (пункты стоят в строку). */
@media (max-width: 900px) { .grp-work .navi::after { display: none; } }
/* Файл-картинка в «Переводе» (tab_editor.jsx: EdPictureCard): что внутри
   картинки — тремя плитками, и одна кнопка. */
.pic-card { display: flex; flex-direction: column; gap: 14px; margin-bottom: 16px; }
.pic-tiles { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 10px; }
.pic-tile { display: flex; flex-direction: column; gap: 2px; padding: 12px 14px;
  border-radius: var(--r-md); background: var(--bg-sunken); }
.pic-tile b { font-size: 22px; font-weight: 600; font-variant-numeric: tabular-nums; }
.pic-tile span { font-size: 12.5px; color: var(--text-3); }
.pic-card .btn-lg { align-self: flex-start; }
@media (max-width: 640px) { .pic-card .btn-lg { align-self: stretch; } }

/* «Скоро»: функция видна, но закрыта — заливка поверх блока. Право
   проверяет сервер (403), заливка — только чтобы не звать к кнопке, которая
   откажет. Под ней ничего не нажимается. */
.soon-wrap { position: relative; }
.soon-veil { position: absolute; inset: -6px; display: flex; align-items: center; justify-content: center;
  background: color-mix(in srgb, var(--card) 78%, transparent); border-radius: 10px; z-index: 2;
  backdrop-filter: blur(1.5px); }
.soon-veil > span { font-weight: 600; letter-spacing: .08em; text-transform: uppercase; font-size: 13px;
  padding: 4px 12px; border-radius: 999px; background: var(--c-primary-soft); color: var(--c-primary); }
