/* ==========================================================================
   Палитра «Сетевые решения 2.0» (новый кит, setevie-design/new/setevie-design/
   tokens.css, октябрь 2026): приглушённый оранжевый акцент #C85E25, шрифт
   Inter. Светлая тема - база, тёмная переопределяет только токены, чтобы
   цвет нигде не оказался объявлен единственный раз внутри media-блока.
   ========================================================================== */
:root {
  color-scheme: light dark;

  /* =========================================================================
     ДИЗАЙН-ТОКЕНЫ «Сетевые решения 2.0», этап 1 переноса
     (design-handoff/design/DESIGN.md, design/tokens.css). Значения ниже -
     источник правды; новый код (components.css и всё, что пишется после
     этого этапа) обращается к ним напрямую. Старые имена переменных, на
     которые уже завязаны сотни правил дальше по файлу, оставлены алиасами
     на новые значения - переписывать все места использования одним проходом
     было бы отдельной, гораздо более рискованной задачей, чем сам перенос
     токенов. ========================================================== */
  --font-sans: 'Inter', ui-sans-serif, system-ui, 'Segoe UI', sans-serif;
  --font-mono: 'JetBrains Mono', ui-monospace, Menlo, Consolas, monospace;

  --fs-xs: 11px; --fs-sm: 12px; --fs-md: 13px; --fs-base: 13.5px;
  --fs-lg: 16px; --fs-xl: 20px; --fs-2xl: 28px; --fs-3xl: 34px;
  --lh: 1.5;

  --sp-1: 4px; --sp-2: 8px; --sp-3: 12px; --sp-4: 16px;
  --sp-5: 20px; --sp-6: 24px; --sp-7: 28px; --sp-8: 32px;

  --r-sm: 6px; --r-md: 8px; --r-lg: 10px; --r-xl: 12px; --r-2xl: 14px; --r-full: 999px;

  --h-control: 42px; --h-btn: 40px; --h-btn-lg: 48px; --h-nav: 38px;
  --w-sidebar: 244px; --w-rail: 300px; --w-content: 1240px;
  --ease: .15s ease;

  --bg: #F6F7F8;
  --surface: #FFFFFF;
  --surface-2: #F1F2F4;
  --line: #E6E7EA;
  --line-2: #D4D6DB;
  --text: #1A1B1E;
  --muted: #565961;
  --faint: #8A8D94;

  --accent: #C85E25;
  /* Палитра v2 (октябрь 2026, setevie-design/new): приглушённый оранжевый
     проходит по контрасту с БЕЛЫМ - --accent-ink развернулся обратно в
     #FFFFFF (v1 здесь держал тёмный текст, см. соседний комментарий о
     первом развороте этого же токена - теперь он снова там, откуда
     начинался, но по новой, задокументированной причине, не по старой). */
  --accent-ink: #FFFFFF;
  --accent-text: #A24A14;
  --accent-soft: #F6ECE5;

  --ok: #1E7A4A;   --ok-soft: #EBF4EF;
  --warn: #8A5A12; --warn-soft: #F8F0DD;
  --bad: #B5392B;  --bad-soft: #FAECEA;
  --info: #2A5FB8; --info-soft: #EAF0F9;
  --vio: #6A3FB5;  --vio-soft: #F0EAFB;
  --knob: #FFFFFF;

  --shadow: 0 1px 2px rgba(20,20,22,.04), 0 1px 3px rgba(20,20,22,.03);

  /* ---- Старые имена: алиасы на новые значения выше, не трогать отдельно ---- */
  --text-2: var(--muted);
  --text-3: var(--faint);
  --line-strong: var(--line-2);
  --radius: var(--r-lg);
  --radius-sm: var(--r-md);
  --danger: var(--bad);

  /* ---- Своё, вне нового дизайна: статусы площадок/категорий
     (placement_analyze, url_checker) и плотность таблиц. Новый дизайн
     такой палитры не описывает - трогать не нужно.
     Оранжевый занят брендом под управляющие элементы, поэтому состояния
     строк живут в своей гамме: синий - защищено, приглушённый красный -
     вопрос закрыт, янтарный - закрыто лишь частично и деньги идут,
     серый - обычный кандидат. ---- */
  --wl: #e8f4fa;       --wl-line: #b8dcec;   --wl-text: #0a6f96;
  --no-bg: #fbeceb;    --no-line: #eec9c5;   --no-text: #a4463b;
  --att-bg: #fff6dd;   --att-line: #f0dda4;  --att-text: #8a6516;
  --free-bg: #f1f1f2;  --free-line: #e0e0e2; --free-text: #77818e;
  --live-bg: #e6f4ea;  --live-line: #b7dfc4; --live-text: #1e7a3c;

  /* Заливка строк повторяет статусы, но бледнее - текст должен читаться */
  --minused: #fdf4f3;
  --phrase: #fffaef;
  --phrase-line: var(--att-line);

  /* 32 было впритык: плашка статуса и чекбокс упирались в границы строки. */
  --row-h: 36px;
  --row-font: 13.5px;
  /* Линия между строками. Отдельно от --line: та рисует границы блоков,
     а эта повторяется тысячу раз подряд и обязана быть тише. */
  --row-line: #f1f1f2;
  /* Минимальная ширина таблицы: сумма фиксированных колонок + зазоры +
     минимум колонки имени. Ниже неё таблица прокручивается, а не сжимается
     в кашу и не обрезается молча. У площадок своя, объявлена рядом с их
     сеткой. */
  --table-min: 962px;
  --shadow-sm: 0 1px 2px rgba(45, 45, 45, .06);
  --shadow-lift: 0 2px 4px rgba(45, 45, 45, .06), 0 16px 32px -16px rgba(45, 45, 45, .24);
}

:root[data-theme="dark"] {
    /* Дизайн-токены, тёмная тема (design/tokens.css). Та же структура, что
       в светлом :root выше: новые значения сначала, старые имена - алиасы
       на них же, своё (wl/no/att/.../--row-line и т.д.) - без изменений. */
    --bg: #121315;
    --surface: #1A1B1D;
    --surface-2: #212325;
    /* Палитра v2 дала #2b2d30 к поверхности #1A1B1D - контраст 1.25,
       тот же порог 1.4 (appearance_cases.js), что уже один раз заставил
       высветлить это же значение в v1. Высветлено ровно настолько, чтобы
       пройти проверку (1.47) - оттенок тот же, --line-2 сдвинут на ту же
       величину для согласованности (не проверяется тестом отдельно). */
    --line: #36383b;
    --line-2: #45474c;
    --text: #ECECEE;
    --muted: #A9ABB1;
    /* Аналогично: design-кит дал #7f8188 - контраст мелкого текста 4.43,
       ниже порога 4.5. Высветлено до 4.87 с запасом. */
    --faint: #868890;

    --accent: #D97A3E;
    --accent-ink: #141210;
    --accent-text: #E59663;
    --accent-soft: #2a1b11;

    --ok: #63CC92;   --ok-soft: #14261B;
    --warn: #E8B04B; --warn-soft: #2A2213;
    --bad: #F2847A;  --bad-soft: #2C1715;
    --info: #86AEF2; --info-soft: #16202F;
    --vio: #B99CF0;  --vio-soft: #221A30;

    /* Новый дизайн в тёмной теме не использует тень вовсе (глубину несёт
       контур, не тень на тёмном фоне она и так не видна) - --shadow в
       проекте нигде не читается напрямую (есть только --shadow-sm/-lift,
       у них свои исторические значения ниже), поэтому замена безопасна. */
    --shadow: none;

    /* ---- Старые имена: алиасы, как в светлой теме ----
       text-2/text-3 - буквальным hex, не var(): web/checks/appearance_cases.js
       читает и проверяет контраст тёмной темы текстом по регулярке вида
       «--text-2: #hex» прямо внутри этого блока - var(--muted) она не видит
       и тогда считает токен отсутствующим, хотя в браузере он резолвится
       верно. Значения те же, что у --muted/--faint выше - продублированы,
       не придуманы заново. */
    --text-2: #A9ABB1;
    --text-3: #868890;
    --line-strong: var(--line-2);
    --danger: var(--bad);

    /* ---- Своё, вне нового дизайна - без изменений ---- */
    --wl: #12313e;       --wl-line: #1f5470;   --wl-text: #6fc0e0;
    --no-bg: #33201e;    --no-line: #5e3833;   --no-text: #dd9289;
    --att-bg: #33290f;   --att-line: #5c4a1d;  --att-text: #d8b962;
    --free-bg: #26262a;  --free-line: #343439; --free-text: #8b95a1;
    --live-bg: #16301f;  --live-line: #245c36; --live-text: #6cc98a;

    --minused: #2b1e1d;
    --phrase: #2a2415;
    --phrase-line: var(--att-line);

    /* В тёмной теме тише не значит светлее: линия чуть темнее поверхности,
       иначе сетка проступает ярче самих чисел. */
    --row-line: #2a2a2d;

  /* Те же два слоя, что в светлой теме: контактный и мягкий. */
  --shadow-sm: 0 1px 2px rgba(0, 0, 0, .4);
  --shadow-lift: 0 2px 6px rgba(0, 0, 0, .45), 0 18px 34px -18px rgba(0, 0, 0, .7);
}

* { box-sizing: border-box; }

/* Авторские display:flex у .setup и #workspace перебивают браузерное
   [hidden] { display: none }, поэтому переключение экранов нужно закрепить. */
[hidden] { display: none !important; }

/* Компактная плотность таблиц. Меняется только высота строки и кегль:
   виртуализация считает позиции по --row-h, и второго числа в JS для этого
   не заводится. Плашки и чекбоксы при 28 пикселях ещё помещаются, ниже
   опускаться нельзя - начнут упираться в границы строки. */
:root[data-density="compact"] {
  --row-h: 28px;
  --row-font: 12.5px;
}
:root[data-density="compact"] .row,
:root[data-density="compact"] .table-head,
:root[data-density="compact"] .table-foot { font-size: var(--row-font); }
:root[data-density="compact"] .tag { padding: 1px 6px; font-size: 10.5px; }

/* Этап 12 PROMPT.md: та же кнопка плотности держит и `.dtable` (report,
   audit, crm_fields, seo) - значение взято из уже лежавшего в components.css
   `.dtable--compact td`, которым раньше некому было управлять: класс
   объявлен design-пакетом, но JS его никогда не ставил ни на один элемент.
   Переключателя `.segmented` из каталога отдельно не завожу - кнопка
   плотности уже делает ровно это для остальных шести таблиц, и вторая
   кнопка с тем же смыслом только путала бы. */
:root[data-density="compact"] .dtable td { height: 34px; }

body {
  margin: 0;
  font: 13.5px/1.5 var(--font-sans);
  color: var(--text);
  background: var(--bg);
  height: 100vh;
  overflow: hidden;
  -webkit-font-smoothing: antialiased;
  /* ЦИФРЫ ОДИНАКОВОЙ ШИРИНЫ ВО ВСЁМ ИНТЕРФЕЙСЕ: расход «1 111» и «8 888» не
     должен занимать разную ширину, их сравнивают взглядом по вертикали.
     Отдельные правила с tabular-nums стояли у трёх-четырёх мест из полусотни;
     здесь это свойство страницы. */
  font-variant-numeric: tabular-nums;
  font-feature-settings: "tnum" 1;
}

h1, h2 { margin: 0; font-weight: 700; letter-spacing: -.01em; }
h1 { font-size: 19px; }
h2 { font-size: 14px; }
.muted { color: var(--text-2); }
.hint { font-size: 12px; color: var(--text-3); font-weight: 400; }
.hint.inline { margin-left: 6px; }
.hint.missing { margin: 4px 0 0; line-height: 1.6; }
.val { font-variant-numeric: tabular-nums; color: var(--accent); font-weight: 600; }

/* ---------- элементы форм ---------- */
select, input[type=date], input[type=number], input[type=search], input[type=text] {
  font: inherit;
  /* Было 7×10. Поле высотой в 32 пиксела выглядит тесным рядом с кнопкой
     и мелким на сенсорном экране - 9×12 даёт привычные ~38. */
  padding: 9px 12px;
  /* Граница светлее: тёмная рамка вокруг каждого поля рябит в форме из
     десятка полей. Различает поле от фона заливка, а не контур. */
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  background: var(--surface);
  color: var(--text);
  width: 100%;
  transition: border-color .12s, box-shadow .12s, background .12s;
}
select:hover:not(:disabled),
input:hover:not(:disabled):not([type=checkbox]):not([type=range]) {
  border-color: var(--line-strong);
}
select:focus, input:focus {
  outline: none;
  border-color: var(--accent);
  box-shadow: 0 0 0 3px var(--accent-soft);
}
select:disabled, input:disabled {
  background: var(--surface-2); color: var(--text-3); cursor: not-allowed;
}
input[type=range] { width: 100%; accent-color: var(--accent); padding: 0; }
input[type=checkbox] { accent-color: var(--accent); width: 15px; height: 15px; }

button {
  font: inherit;
  border: 0;
  border-radius: var(--radius-sm);
  cursor: pointer;
  background: none;
  color: inherit;
  padding: 0;
}
/* Кнопки бренда: заглавными, плотный шрифт, разрядка - как на setere.ru.
   Нажатие видно движением на пиксел вниз и снятой тенью: кнопка, которая
   на нажатие только меняет яркость, не ощущается нажатой. */
button.primary {
  padding: 11px 22px;
  background: var(--accent);
  /* --accent-ink, не --accent-text: это текст НА оранжевой заливке,
     --accent-text с этапа 1 означает другое (оранжевый текст на светлом
     фоне) - см. комментарий у :root. */
  color: var(--accent-ink);
  font-weight: 700;
  font-size: 11.5px;
  text-transform: uppercase;
  letter-spacing: .04em;
  box-shadow: var(--shadow-sm);
  transition: filter .12s, box-shadow .12s, transform .06s;
}
button.primary:hover:not(:disabled) { filter: brightness(1.06); box-shadow: var(--shadow); }
button.primary:active:not(:disabled) { transform: translateY(1px); box-shadow: none; }
button.primary:disabled {
  background: var(--line-strong); color: var(--surface);
  box-shadow: none; cursor: default;
}
button.link { color: var(--text-2); padding: 6px 8px; border-radius: var(--radius-sm); }
button.link:hover:not(:disabled) { color: var(--accent); background: var(--accent-soft); }
button.link:disabled { color: var(--text-3); cursor: default; }
/* Фокус с клавиатуры виден у всего, по чему можно щёлкнуть: раньше его
   показывали только поля ввода, и Tab по кнопкам шёл вслепую. */
button:focus-visible, .chip:focus-visible, .tool-card:focus-visible {
  outline: 2px solid var(--accent); outline-offset: 2px;
}

.chips { display: flex; gap: 6px; flex-wrap: wrap; }

textarea {
  width: 100%; padding: 8px 10px; font: inherit;
  border: 1px solid var(--line-strong); border-radius: var(--radius-sm);
  background: var(--surface); color: var(--text); resize: vertical;
}
textarea:focus { outline: none; border-color: var(--accent); }
.brand-actions { display: flex; align-items: center; gap: 10px; margin-top: 4px; }
.brand-actions .hint.error { color: var(--no-text); }

.inline-sort { display: flex; align-items: center; gap: 6px; font-size: 12px; color: var(--text-2); }
.inline-sort select { padding: 4px 6px; font-size: 12px; }
/* .chip - полностью в components.css (было поделено на два файла). */

.field { display: flex; flex-direction: column; gap: 5px; min-width: 0; }
/* Подпись и подсказка идут ПОДРЯД, а не по разным краям.
   Раньше здесь стоял justify-content: space-between: в узкой форме подсказка
   просто оказывалась справа от подписи, а в широкой колонке улетала за
   полтора экрана от неё - «Подсветка подозрительных» слева, объяснение
   у правого края, и связь между ними терялась. */
.label {
  font-size: 11.5px; font-weight: 500; color: var(--text-2);
  display: flex; flex-wrap: wrap; align-items: baseline;
  gap: 2px 8px; min-width: 0;
}
.label .hint { font-weight: 400; }
.status { font-size: 12px; color: var(--text-2); }
.status.error { color: var(--danger); }
/* Пустой экран: заголовок, причина и выход. Серая строчка посреди пустоты
   сообщала только то, что строк нет, - но не почему и не что с этим делать. */
.empty {
  color: var(--text-3); font-size: 12.5px; text-align: center;
  padding: 40px 16px; margin: 0; line-height: 1.6;
  display: flex; flex-direction: column; align-items: center; gap: 8px;
}
.empty b { color: var(--text); font-size: 14px; font-weight: 600; }
.empty-why { max-width: 420px; }
.empty button { margin-top: 4px; }

/* ==========================================================================
   Экран настройки
   ========================================================================== */
.setup {
  /* flex: 1, не height: 100vh - см. тот же разбор у .home выше и у
     #tool-screen в блоке «Рабочий экран»: .setup лежит внутри #tool-screen
     НИЖЕ .top, и 100vh сверх уже занятой топбаром и шапкой высоты отодвигал
     бы низ формы за нижний край окна. */
  flex: 1;
  min-height: 0;
  overflow-y: auto;
  /* overflow-x: hidden явно, а не по умолчанию (visible) - у overflow-y:auto
     без этого браузер трактует visible по X тоже как auto (правило
     взаимодействия osей overflow), и достаточно одного невидимого элемента
     шире своего места, чтобы вылез горизонтальный скролл. Так и нашлось:
     .hint-dot::after (всплывающая подсказка) - position:absolute и скрыта
     по умолчанию (opacity:0 до :hover/:focus-visible), но её 300px ширины
     всё равно считаются в scrollWidth ближайшего overflow-предка - в шаге
     SEO «Что собирать» кружок подсказки стоит у самого края узкой колонки
     .toggle, и так нашёлся первый случай, где это стало заметно. Сама
     подсказка при наведении всё равно не обрежется: её уже отодвигает
     класс .left (tool-seo-format.js), если она не помещается у правого
     края - overflow:hidden здесь просто перестаёт ЛГАТЬ о лишней ширине,
     которую реально никто не видит. */
  overflow-x: hidden;
  /* Сверху 16, а не 24: над формой уже стоит отбивка самой .top. Снизу 32 -
     обычный запас после последнего поля (.runbar, которая требовала иначе
     считать нижний отступ - .setup-card margin-bottom 90px - убрана целиком,
     «Запустить»/«Сбросить к конфигу» теперь в .top, а не закреплены снизу
     экрана). */
  padding: 16px 24px 32px;
}
/* Экран настройки во всю ширину. Раньше карточка была в 1040 px по центру:
   на широком мониторе две трети экрана пустовали, а параметры инструмента
   лежали под формой в свёрнутом блоке - о том, что их можно менять, узнавали
   не сразу. Теперь три колонки: слева что запускаем, справа чем, дальше
   картинка и справка. */
.setup-card {
  width: 100%;
  height: fit-content;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  box-shadow: var(--shadow);
  padding: 22px 24px 24px;
}
.setup-grid {
  display: grid;
  grid-template-columns:
    minmax(340px, 460px) minmax(0, 1fr) minmax(260px, 300px) minmax(230px, 280px);
  gap: 24px;
  align-items: start;
}
/* min-width: 0 обязателен: без него длинное название кампании распирает
   колонку, и сетка расползается вместо того, чтобы обрезать текст. */
.setup-col { min-width: 0; display: flex; flex-direction: column; gap: 14px; }
.col-params {
  border: 1px solid var(--line); border-radius: var(--radius-sm);
  background: var(--surface-2); gap: 0; padding-bottom: 4px;
}
.col-head {
  display: flex; flex-wrap: wrap; align-items: baseline; gap: 4px 10px;
  padding: 11px 14px 8px; border-bottom: 1px solid var(--line);
}
.col-head h2 { margin: 0; font-size: 13px; font-weight: 500; }
/* Сводка настроек идёт сразу за заголовком, кнопка справки - к правому краю:
   это единственное, что здесь имеет право быть на другом конце строки. */
.col-head .hint { flex: 1 1 auto; min-width: 0; }
.help-btn {
  flex: 0 0 auto;
  display: inline-flex; align-items: center; gap: 6px;
  padding: 4px 10px 4px 7px;
  border: 1px solid var(--line-strong); border-radius: 999px;
  background: var(--surface); color: var(--text-2);
  font: inherit; font-size: 11.5px; cursor: pointer;
}
.help-btn::before {
  content: "?";
  display: inline-flex; align-items: center; justify-content: center;
  width: 15px; height: 15px; border-radius: 50%;
  background: var(--accent); color: #fff;
  font-size: 10px; font-weight: 700; line-height: 1;
}
.help-btn:hover { border-color: var(--accent); color: var(--accent); }
.col-params .tool-pane { padding-top: 6px; }
.col-aside { gap: 16px; }

/* Карточка запуска (.launch-card) - своя, не эталонная (эталонный кит её
   не описывает вовсе, см. комментарий у разметки в index.html): граница/фон
   по образцу .card, остальное - по месту. */
.launch-card {
  border: 1px solid var(--line); border-radius: var(--radius-sm);
  background: var(--surface); padding: 16px; gap: 12px;
}
.launch-summary__row {
  display: flex; justify-content: space-between; gap: 10px;
  padding: 5px 0; font-size: 13px; border-bottom: 1px solid var(--line);
}
.launch-summary__row:last-child { border-bottom: 0; }
.launch-summary__label { color: var(--muted); flex: 0 0 auto; }
.launch-summary__value { color: var(--text); text-align: right; }
.launch-card__run { width: 100%; justify-content: center; }
.launch-card__history { display: flex; flex-direction: column; gap: 2px; }
.launch-card__history .label { margin-bottom: 2px; }
.launch-card__history .list-item {
  border: 0; border-top: 1px solid var(--line); border-radius: 0;
  width: 100%; background: transparent; text-align: left; cursor: pointer;
  padding: 10px 2px;
}
.launch-card__history .list-item:first-of-type { border-top: 0; }
/* Картинки у инструмента может не быть - тогда четвёртой колонки тоже нет:
   пустой трек в 280px выглядит как потерянный блок. Левой колонки (даты/
   загрузка/кампании) может не быть по той же причине - у SEO/report/
   crm_fields она нечем заполнить (core.js:applyToolVisibility, .no-run).
   Комбинации независимы - считаны все четыре. */
.setup-grid.no-aside { grid-template-columns: minmax(340px, 460px) minmax(0, 1fr) minmax(260px, 300px); }
.setup-grid.no-aside .col-aside { display: none; }
.setup-grid.no-run { grid-template-columns: minmax(0, 1fr) minmax(260px, 300px) minmax(230px, 280px); }
.setup-grid.no-run .col-run { display: none; }
.setup-grid.no-run.no-aside { grid-template-columns: minmax(0, 1fr) minmax(260px, 300px); }

/* Узкий экран: сначала уезжает колонка с картинкой и справкой (она
   вспомогательная), потом карточка запуска встаёт под параметры, потом
   параметры - под форму. */
@media (max-width: 1180px) {
  .setup-grid { grid-template-columns: minmax(320px, 420px) minmax(0, 1fr); }
  .setup-grid.no-run, .setup-grid.no-run.no-aside { grid-template-columns: minmax(0, 1fr); }
  .col-aside, .launch-card { display: none; }
}
@media (max-width: 820px) {
  .setup-grid { grid-template-columns: minmax(0, 1fr); }
}

/* Картинка декоративная: держим её в отдельной колонке, чтобы она не влияла
   на ширину формы, и убираем на узких экранах - шутка требует всей высоты,
   обрезанная в полоску она не читается и только съедает место. */
.setup-aside { display: flex; flex-direction: column; gap: 9px; }
/* Пропорции не навязываем: у инструментов и вертикальные картинки, и
   горизонтальные гифки, а обрезка под общий формат срезает у шутки половину.
   Ширина колонки одна, высота - какая получится. */
.setup-aside img {
  display: block;
  width: 100%;
  height: auto;
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
}
.aside-cap { margin: 0; font-size: 11.5px; color: var(--text-3); text-align: center; }
/* Тема - настройка приложения, поэтому стоит на витрине и прижата к правому
   краю строки бренда: рядом с логотипом, а не среди кнопок инструмента. */
.theme-switch { margin-left: auto; font-size: 12px; }
.errors-switch { font-size: 12px; }

/* Панель последних ошибок сервера: выпадает из-под кнопки в шапке витрины,
   а не отдельным экраном - это вспомогательный просмотр, а не рабочий поток.
   См. web/routers/logs.py: читает уже существующий файл лога. */
.errors-panel {
  margin: 0 0 20px; padding: 14px 16px; border: 1px solid var(--line);
  border-radius: var(--radius); background: var(--surface-2);
}
.errors-head { display: flex; align-items: center; gap: 10px; }
.errors-head b { font-size: 13px; }
.errors-path { margin: 2px 0 10px; font-size: 10.5px; word-break: break-all; }
.errors-list { display: flex; flex-direction: column; gap: 1px; max-height: 360px;
               overflow-y: auto; font-family: var(--font-mono); font-size: 11.5px; }
/* Имя НЕ «.row»: тот класс занят виртуализированными строками таблиц
   (position: absolute, фиксированная высота - см. выше, #spacer .row) и при
   совпадении имени утягивал эти строки за собой - список рендерился узкой
   колонкой абсолютно спозиционированных обрубков в углу экрана, а не здесь. */
.errors-row { padding: 5px 8px; border-radius: 4px; background: var(--surface);
              border: 1px solid var(--line); white-space: pre-wrap; word-break: break-word; }
.errors-list .empty { padding: 8px; color: var(--text-3); font-family: inherit; }
/* Логотип монохромный и тёмный - в тёмной теме его надо осветлить.
   .brand-mark - единственное место теперь (DESIGN.md §2: лого живёт
   в меню, .flyout__brand, не копией на каждом экране инструмента). */
:root[data-theme="dark"] .brand-mark { filter: invert(1) brightness(1.6); }

/* .top/.top__title/.crumb - уже есть в components-v3.css (перенесены из
   настоящего эталона, setevie-design/new/setevie-design/components.css:70-73)
   дословно, второй раз не завожу. Здесь - только то, чего там нет:
   .top__actions («Сбросить к конфигу»/«Запустить», видны лишь на
   «Настройке» - core.js:setToolView() прячет их на «Прогоне»/«Результатах»;
   заменяет прежнюю закреплённую снизу .runbar, в эталоне для «Настройки»
   нет вообще никакой «Запустить» - у нас логика осталась одношаговой по
   прямому решению пользователя, см. комментарий у #tool-screen). */
.top__actions { display: flex; align-items: center; gap: 10px; flex: 0 0 auto; }
.sub { margin: 4px 0 0; font-size: 12.5px; color: var(--text-2); }

/* Условия прогона (.meta/#run-stats) - третий заход подряд на эту строку.
   Сперва отдельной карточкой с рамкой (эталон, components-v3.css: `.meta
   { border: 1px solid var(--line); border-radius: 11px; background:
   var(--surface) }`), потом плоской строкой под общей шапкой инструмента
   (своя `<header class="top">` внутри #workspace, второй паддинг
   эталона поверх первого - живой баг, 129px на 100px содержимого).
   Теперь - прямое решение пользователя «объединить в одну полосу с
   разделителем» - .meta переехала ВНУТРЬ шапки #tool-screen (index.html:
   #run-meta-row), рядом с .top__title, видна только на «Результатах»
   (core.js:setToolView). Второй header.top в #workspace больше не
   существует - родительский паддинг больше неоткуда взять дважды. */
#tool-screen > header.top > .meta {
  border: 0; border-radius: 0; background: none; padding: 0;
  flex: 1 1 320px; min-width: 0; flex-wrap: nowrap; gap: 6px 16px;
}
/* Живой баг, пойман по скриншоту (n-граммы): у SEO текст короткий (проект +
   пара дат) и укладывался в одну строку сам; у n-грамм в ту же строку
   попадают ещё кампания/группа/логин - текст переносился на вторую строку
   ВНУТРИ .meta__params, а .top__title рядом был на строку выше (там ещё
   жил #account-badge) - две колонки разной высоты, выровненные по центру
   (.top { align-items: center }), читались как «всё в кучу», не единой
   строкой. Текст обрезается многоточием вместо переноса - высота .meta
   всегда одна и та же (строка текста + строка итогов), независимо от
   длины названия кампании. Флекс-базис снижен с эталонных 360px - текст
   и так обрезается многоточием, ему не нужно столько места по умолчанию,
   а освободившееся уходит плиткам итогов (ниже). */
.meta__params { flex: 0 1 200px; font-size: 12px; overflow: hidden; white-space: nowrap; text-overflow: ellipsis; }
/* Второй слой того же бага: у n-грамм/площадок итогов шесть, не четыре,
   как у SEO - общий .stats (style.css) переносит их в 2 строки, когда не
   хватает ширины (flex-wrap: wrap), и тогда короткая строка текста слева
   выравнивалась по центру (.top { align-items: center }) против ВЫСОКОГО
   двухрядного блока цифр справа - тот же «всё в кучу» эффект, уже по
   другой причине. Именно здесь, в шапке результата (#run-stats), цифры
   не переносятся - лучше уже, продуманный просвет, чем шесть мелких
   чисел в две строки. */
#run-stats { flex-wrap: nowrap; }
/* Разделитель между .top__title и .meta - тонкая вертикальная линия,
   растянутая на высоту строки (align-self: stretch), а не текстовый
   символ. hidden снимается вместе с .meta (core.js:setToolView). */
.top__sep { width: 1px; align-self: stretch; background: var(--line); margin: 2px 0; }
 .sr-client-excluded { opacity: .55; }
 .sr-client-controls { margin-bottom: 12px; }
 .sr-overview { padding: 20px; border-bottom: 1px solid var(--line); }
 .sr-overview > h2 { margin: 0 0 16px; }
 .sr-overview > h3 { margin: 20px 0 10px; }
/* Плотность KPI-плиток сравнения периодов. Базовый .kpi - в эталоне
   (components-v3.css, копия буквальна) с padding: 14px 16px - вторым,
   более специфичным правилом (не трогая сам файл) уплотняю именно
   карточки сравнения периодов (#seo-kpis - единственное место, где они
   сейчас есть). */
#seo-kpis .kpi { padding: 8px 16px; gap: 2px; }
/* Тулбар таблицы (поиск/кнопки) - своя плотность, не эталонная: было
   padding: 10px 12px + margin-bottom: 10px, с высоким полем поиска
   браузера по умолчанию это давало ~70px на одну строку кнопок. */
/* .toolbar тоже задан дважды (components.css: 14px var(--sp-5), позже по
   каскаду) - тот же приём, реальная вложенность вместо !important. */
.left > .toolbar { padding: 7px 12px; margin-bottom: 8px; }
.left > .toolbar input[type=search] { height: 30px; }
/* Поисковая строка приложения (.app-topbar) и шапка открытого инструмента
   (.top: крошка/заголовок/этапы) - эталонные 14px сверху/снизу на каждую,
   две строки подряд съедали лишнее. Лево/право (var(--sp-8)/26px) не
   трогал - это не высота, горизонтальный зазор от рельса/toolnav не менялся. */
/* .app-topbar лежит в components.css, который подключён ПОСЛЕ style.css -
   при равной специфичности его padding (14px) перебивал это правило молча
   (проверено getComputedStyle: 14px, а не 10px). Поднимаю специфичность
   реальной вложенностью, а не !important. */
.main > .app-topbar { padding-top: 10px; padding-bottom: 10px; }
#tool-screen > header.top { padding-top: 10px; padding-bottom: 10px; }

/* .toolnav__proj-extra - подсказка с путём конфига, предупреждение о его
   отсутствии, «+ новый проект». Эталон их не знает вовсе (.toolnav__proj
   там держит только один select) - своя, честно отдельная надстройка,
   не выдаётся за часть кита. Отдельная строка под .toolnav__proj, не внутри
   неё: там фиксированная высота 52px на одну строку, в которую это не
   помещалось и вылезало за узкую колонку (живой баг, пойман по скриншоту). */
.toolnav__proj-extra {
  display: flex; flex-direction: column; align-items: flex-start; gap: 4px;
  padding: 8px 14px 12px; border-bottom: 1px solid var(--line);
  font-size: var(--fs-sm);
}
.toolnav__proj-extra > .hint.missing { color: var(--warn); }
/* .badge - полностью в components.css (это правило всегда проигрывало
   каскаду, components.css подключён позже и переопределяет все свойства). */

/* ==========================================================================
   Витрина инструментов - первый экран
   ========================================================================== */
/* flex: 1, не height: 100vh - .home лежит в .main (flex-column) НИЖЕ
   .app-topbar, и 100vh поверх уже занятой топбаром высоты всегда даёт
   этаж лишних пикселей за нижним краем окна (там и застряла #home-dashboard,
   пока не казалось, что это мешает, а не только занимает место). flex: 1
   с min-height: 0 отдаёт .home ровно то, что осталось от .main. */
.home { flex: 1; min-height: 0; overflow-y: auto; display: flex; justify-content: center; padding: 56px 20px; }
.home-inner { width: 100%; max-width: 860px; height: fit-content; }
/* .home - display:flex без align-items, то есть по умолчанию stretch:
   без height: fit-content рабочий стол прижимало к высоте .home, а не к
   своему реальному контенту - группы инструментов обрезались молча, и
   scrollHeight показывал, что больше прокручивать некуда, хотя контент
   был. #home-modes эту ловушку обходит через .home-inner выше; у рабочего
   стола (class="page", общий класс) своего исключения не было. */
#home-dashboard { height: fit-content; }
.home h1 { font-size: 26px; margin-bottom: 4px; }
.home .sub { margin: 0 0 26px; }
.home-foot { font-size: 12px; color: var(--text-3); margin-top: 22px; }
.home-foot.error { color: var(--danger); }

/* Карточки ВНУТРИ #tool-grid - выбор РЕЖИМА одного инструмента (не путать
   с .tools-grid/.tool-card дашборда в components.css: то же базовое имя
   класса, другой компонент, другой контейнер - без этого уточнения box-shadow
   и цветная полоска сверху просачивались на карточки дашборда, которые
   их не сбрасывают. */
.tool-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(250px, 1fr)); gap: 14px; }
.tool-grid .tool-card {
  text-align: left; padding: 18px 20px 20px;
  border: 1px solid var(--line); border-radius: var(--radius);
  background: var(--surface); box-shadow: var(--shadow);
  display: flex; flex-direction: column; gap: 6px;
  border-top: 3px solid var(--accent);
  transition: transform .12s, box-shadow .12s;
}
.tool-grid .tool-card:hover { transform: translateY(-2px); box-shadow: var(--shadow-lift); }
.tool-grid .tool-card b { font-size: 15px; font-weight: 700; }
.tool-grid .tool-card span { font-size: 12.5px; color: var(--text-2); }
.tool-grid .tool-card .go {
  margin-top: auto; padding-top: 10px; font-size: 11px; font-weight: 700;
  text-transform: uppercase; letter-spacing: .05em; color: var(--accent);
}
/* Запланированные: инструмент в репозитории есть, интерфейса к нему ещё нет */
/* Пометка «нет доступа» у выключенного источника. Галочка сама по себе
   серая, и без слова это читается как «сломалось», а не как «ещё не завели». */
.toggle .soon {
  margin-left: 6px; padding: 1px 6px; border-radius: 999px;
  border: 1px solid var(--line-strong); background: var(--surface-2);
  font-size: 10px; font-weight: 600; color: var(--text-3); white-space: nowrap;
}

.tool-grid .tool-card.soon { border-top-color: var(--line-strong); cursor: default; box-shadow: none; opacity: .75; }
.tool-grid .tool-card.soon:hover { transform: none; }
.tool-grid .tool-card.soon .go { color: var(--text-3); }
.tool-grid .tool-card .note { font-size: 11px; color: var(--text-3); }

/* Родитель - .page, flex-column с gap: var(--sp-6) (24px) между ЛЮБЫМИ
   прямыми детьми - кнопка «назад» как первый ребёнок получала этот зазор
   ДО заголовка просто так, плюс собственный padding-bottom поверх: 24+12 =
   36px итогового отступа, из-за чего ссылка выглядела оторванной от
   заголовка, которому принадлежит (жалоба пользователя на скриншоте -
   "кнопку назад нормально сделай"). Отрицательный margin-bottom гасит
   родительский gap, оставляя тесный, читаемый отступ ~8px, как у
   обычной хлебной крошки над заголовком, а не отдельный блок в углу. */
.back-home {
  display: inline-flex; align-items: center; width: fit-content;
  margin: 0 0 calc(-1 * var(--sp-6) + 8px); font-size: 13px;
}

/* Блоки, показываемые только своему инструменту, и поля периода: скрываются
   атрибутом hidden из core.js, стили нужны лишь чтобы раскладка не ломалась. */
.tool-pane:not([hidden]) { display: block; }
.workspace-pane:not([hidden]) { display: flex; }
/* Называется date-field, не period: класс .period уже занят под карточку
   "Шкала периодов" (components.css) - одно и то же имя красило бы даты
   в сетапе рамкой и растягивало блок на всю высоту колонки. */
.date-field:not([hidden]) { display: flex; }
.panel-head { display: flex; align-items: center; gap: 8px; margin-bottom: 12px; }

.workspace-pane { flex: 1; display: flex; min-height: 0; min-width: 0; }

/* Раскладку формы задаёт .setup-grid. Здесь раньше стоял
   `display: flex; flex-direction: column`, и по специфичности идентификатор
   перебивал класс сетки: колонки складывались в одну, а понять это по вёрстке
   было нельзя - правило лежит в четырёхстах строках отсюда. */
#run-form { min-width: 0; }
/* Поля в строке. Раньше здесь была сетка из трёх жёстких треков, и у
   инструмента без периода (кластеризация) два пустых трека по 150 px всё
   равно резервировались: селект проекта сжимался до полусотни пикселей,
   а подсказка про .env печаталась по одному слову в строку. Флексом пустые
   поля не занимают ничего, а в узкой колонке даты переносятся парой. */
.field-row { display: flex; flex-wrap: wrap; gap: 12px; }
.field-row > .field { flex: 1 1 100%; min-width: 0; }
.field-row > .field.narrow { flex: 1 1 calc(50% - 6px); }
/* Быстрый период: подпись над чипами. В колонке 380 px подпись сбоку
   ломалась на две строки и утаскивала чипы во вторую. */
.preset-row { display: flex; flex-direction: column; align-items: flex-start; gap: 6px; }
.preset-row .label { margin: 0; }

/* выбор кампаний */
.picker { border: 1px solid var(--line-strong); border-radius: var(--radius-sm); overflow: hidden; background: var(--surface); }
.picker-bar { display: flex; align-items: center; gap: 4px; padding: 6px; border-bottom: 1px solid var(--line); background: var(--surface-2); }
.picker-bar input { border: 0; background: none; padding: 3px 4px; }
.picker-bar input:focus { box-shadow: none; }
.picker-list { max-height: 280px; overflow-y: auto; }
.camp {
  display: flex; align-items: flex-start; gap: 9px;
  padding: 6px 10px; cursor: pointer; font-size: 12.5px;
  border-bottom: 1px solid var(--line);
}
.camp input, .camp .cid, .camp .cstate { margin-top: 1px; }
.camp:last-child { border-bottom: 0; }
.camp:hover { background: var(--surface-2); }
.camp .cid { color: var(--text-3); font-variant-numeric: tabular-nums; font-size: 11.5px; }
/* Название переносится, а не обрезается многоточием. Кампании Директа
   называют по схеме «Бренд - регион - [Поиск] - LP», и в обрезанном виде
   соседние строки отличаются только хвостом, которого не видно: выбрать
   нужную было нельзя. */
.camp .cname {
  flex: 1; min-width: 0;
  white-space: normal; overflow-wrap: anywhere; line-height: 1.35;
}
.camp .cstate {
  font-size: 10.5px; padding: 2px 8px; border-radius: 999px;
  border: 1px solid transparent; white-space: nowrap; flex: none; font-weight: 500;
}
.cstate.st-on   { background: var(--live-bg); border-color: var(--live-line); color: var(--live-text); }
.cstate.st-susp { background: var(--att-bg);  border-color: var(--att-line);  color: var(--att-text); }
.cstate.st-off  { background: var(--free-bg); border-color: var(--free-line); color: var(--free-text); }
.cstate.st-end  { background: var(--free-bg); border-color: var(--free-line); color: var(--free-text); }

/* ==========================================================================
   Сворачивание - одно правило на всё приложение
   ==========================================================================
   Свернуть можно многое: блоки настроек, справку по ним, списки площадок,
   графики. Раньше признаком этого была серая галочка «▸» кегля девять -
   её не замечали. Свёрнутый блок при этом выглядит просто как заголовок,
   и содержимое считают отсутствующим, а не спрятанным.

   Поэтому у каждого сворачиваемого заголовка теперь три признака сразу:
   значок в рамке (его видно как кнопку), слово действия справа
   («развернуть» / «свернуть») и подсветка всей строки под курсором.
   Слово - главное из трёх: значок можно принять за украшение, а глагол
   нельзя.

   Правило общее - на `details > summary`, а не на классы. Новый
   сворачиваемый блок получает вид сам, и забыть его оформить нельзя. */
details > summary { cursor: pointer; list-style: none; }
details > summary::-webkit-details-marker { display: none; }
details > summary::before {
  content: "▸";
  flex: none; display: inline-flex; align-items: center; justify-content: center;
  width: 17px; height: 17px; font-size: 9px; line-height: 1;
  border: 1px solid var(--line-strong); border-radius: 4px;
  color: var(--text-2); background: var(--surface);
  transition: transform .15s, color .15s, border-color .15s;
}
details[open] > summary::before { transform: rotate(90deg); }
details > summary::after {
  content: "развернуть";
  margin-left: auto; flex: none;
  font-size: 11px; font-weight: 500; color: var(--text-3);
  text-decoration: underline dotted var(--line-strong); text-underline-offset: 3px;
}
details[open] > summary::after { content: "свернуть"; }
details > summary:hover::before { color: var(--accent); border-color: var(--accent); }
details > summary:hover::after { color: var(--accent); text-decoration-color: var(--accent); }
details > summary:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

/* Кластеризация сворачивает график своей кнопкой, а не <details>: щелчок
   по столбцу там прокручивает список к группе, и переносить это в общий
   механизм значило бы переделывать работающий экран ради единообразия.
   Вид приводим к общему здесь - значок в рамке и глагол рядом, как везде. */
.dist-toggle .dt-icon {
  display: inline-flex; align-items: center; justify-content: center;
  width: 17px; height: 17px; font-size: 9px; line-height: 1;
  border: 1px solid var(--line-strong); border-radius: 4px;
  color: var(--text-2); background: var(--surface);
}
.dist-toggle:hover .dt-icon { color: var(--accent); border-color: var(--accent); }
.dist-toggle #cl-dist-toggle-text {
  text-decoration: underline dotted var(--line-strong); text-underline-offset: 3px;
}

/* настройки */
.settings { border: 1px solid var(--line); border-radius: var(--radius-sm); background: var(--surface-2); }
.settings summary {
  padding: 11px 14px; cursor: pointer; font-size: 13px; font-weight: 500;
  display: flex; justify-content: space-between; align-items: center; gap: 10px;
  list-style: none;
}
.settings summary:hover { background: var(--surface); border-radius: var(--radius-sm); }
/* Название блока и его сводка - подряд; свободное место уходит вправо
   от них, а не между ними. */
.settings summary span:first-of-type { flex: 0 0 auto; }
.settings summary .hint { flex: 1 1 auto; min-width: 0; }
.settings-grid {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
  gap: 14px; padding: 4px 14px 14px;
}
.toggles {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(190px, 1fr));
  gap: 8px 14px; padding: 0 14px 16px;
  border-top: 1px solid var(--line); padding-top: 14px;
}
/* Строка «переключатель + кружок подсказки» (сейчас только у SEO - у
   остальных инструментов `.switch` лежит прямо в `.toggles` без обёртки,
   подсказок к переключателям там нет). Раньше `.toggle` была `<label>` с
   чекбоксом и текстом внутри; теперь сам переключатель - `.switch`,
   самодостаточная кнопка со своей высотой 40px, `.toggle` вокруг нужна
   только чтобы прижать к ней кружок подсказки. */
.toggle { display: flex; align-items: center; gap: 4px; }
.state-list { display: flex; flex-direction: column; gap: 6px; padding: 8px 10px;
              border: 1px solid var(--line-strong); border-radius: var(--radius-sm);
              background: var(--surface); }
/* Список из десятка пунктов (источники трафика, регионы съёма) в одну колонку
   вытягивается на пол-экрана и отодвигает всё остальное вниз. Раскладываем
   в несколько колонок по ширине. */
.state-list.columns {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(190px, 1fr));
  gap: 6px 14px;
}

.setup-actions { display: flex; align-items: center; gap: 12px; }
.setup-actions .status { margin-left: auto; text-align: right; }

/* ==========================================================================
   Рабочий экран
   ========================================================================== */
/* #tool-screen - общий контейнер трёх окон инструмента (.stages + .setup/
   #run-view/#workspace). Сам он flex-column и flex: 1 внутри .main, а три
   окна внутри него - тоже flex: 1 (не height: 100vh у каждого по отдельности,
   как было раньше): тогда они делят между собой ровно то, что осталось под
   .stages, а не всю высоту окна браузера заново, уже занятую топбаром и
   стадиями выше. Без этой строки #tool-screen как контейнер не участвовал
   в разметке вовсе (лежал display: block по умолчанию), а .tool-stages-bar
   { flex: none } ниже была no-op. */
#tool-screen { flex: 1; min-height: 0; display: flex; flex-direction: column; }

/* Тот же приём, что у #tool-screen выше - .editor-shell сама flex: 1 и ждёт
   именно такого родителя (components-v2.css), иначе три колонки редактора
   (этап 9 PROMPT.md) были бы нулевой высоты. */
#config-editor-screen { flex: 1; min-height: 0; display: flex; flex-direction: column; }

/* Этап 7 (проекты): без этого правила секция - обычный flex: 0 1 auto
   внутри .main, высота по контенту. body { overflow: hidden; height: 100vh }
   выше просто ОБРЕЗАЕТ всё, что не влезло, - реального скролла колесом мыши
   не было (window.scrollTo() в devtools создавал обманчивое впечатление,
   что скролл работает - он двигал documentElement как scrolling element,
   а не реальную область страницы). min-height: 0 + overflow-y: auto делают
   секцию собственной прокручиваемой областью, как у #tool-screen/.home. */
#projects-screen,
#project-detail-screen,
#new-project-screen,
#admin-users-screen,
#admin-user-screen,
#activity-screen,
#openrouter-models-screen,
#profile-screen {
  flex: 1; min-height: 0; overflow-y: auto; display: flex; flex-direction: column;
}

/* overflow: hidden - последний рубеж: любая будущая широкая вставка
   прокрутится внутри своей области, а не сдвинет весь экран. */
#workspace { flex: 1; min-height: 0; display: flex; flex-direction: column;
             min-width: 0; overflow: hidden; }

/* Переключатель окон инструмента (DESIGN.md §1,§3) - общий для settings/
   run/results, поэтому лежит отдельной полосой НАД ними, а не внутри
   .setup/#workspace: иначе пришлось бы дублировать его в обоих. */
.tool-stages-bar { flex: none; padding: 12px 24px 0; }

/* Окно «Прогон»: раньше это был #loading - модальный оверлей на весь
   экран (position: fixed), теперь обычное окно внутри #tool-screen,
   поэтому только центрирование содержимого, без fixed/backdrop-blur. */
#run-view { flex: 1; min-height: 0; display: flex; align-items: center; justify-content: center; }

.brand-home { flex: none; display: block; padding: 0; border-radius: var(--radius-sm); }
.brand-home:hover { opacity: .75; }
.brand-home:focus-visible { outline: 2px solid var(--accent); outline-offset: 3px; }
/* Итоги в шапке ужимаются и переносятся: у SEO их пять, и на узком экране
   они раздвигали шапку, а следом и весь экран. */
/* Итоги прогона - рядом плоских чисел, не карточками с подложкой.
   Раньше каждая метрика сидела в своей рамке с фоном (DESIGN.md §3 этот
   вид не описывает - .stat - наш собственный компонент, не часть эталона,
   .kpis/.kpi эталона не заведены: там нужно поле «было» в данных, которого
   у большинства инструментов нет, см. PROGRESS.md). Рамки и подложка шести
   плиток подряд в шапке результатов и были той самой «раздутой верхней
   областью» (живой баг, пойман по скриншоту) - теперь тот же плоский вид,
   что раньше включался только на узком экране, стоит всегда: число крупнее
   подписи и без того хорошо читается, а рамка с фоном не добавляла смысла. */
.stats { display: flex; flex-wrap: wrap; gap: 8px 10px; margin-left: auto;
         min-width: 0; justify-content: flex-end; }
/* flex-shrink: 0 - живой баг, пойман по скриншоту в объединённой шапке
   (#run-stats: nowrap, см. выше): когда .meta тесно, .stat сжимался ниже
   ширины своей ЖЕ подписи («Клики»/«Запросов в ядре» - длиннее короткого
   числа «-» над ней), а подпись (white-space: nowrap) не переносилась,
   а просто наезжала на соседнюю плитку текстом. Плитка больше не
   сжимается своей - ширину задаёт более широкая строка (число или
   подпись), overflow достаётся соседу по .top (он сам flex-wrap: wrap),
   не внутренностям плитки. */
.stat { display: flex; flex-direction: column; align-items: flex-end; gap: 1px; flex-shrink: 0; }
/* Тонкая линия МЕЖДУ плитками - не то же самое, что рамка с фоном ВОКРУГ
   каждой (убрана выше как раздутая область, живой баг). Пойман другой живой
   отзыв - шесть плиток подряд без всякой границы читаются одной сплошной
   стеной текста, особенно рядом с именем проекта и кнопками в той же
   строке. 1px без фона - достаточно, чтобы взгляд цеплялся за границу
   плитки, не возвращает тот же вес, что убирали. */
.stat + .stat { padding-left: 18px; border-left: 1px solid var(--line); }
.stat b { font-size: 13.5px; font-variant-numeric: tabular-nums; font-weight: 600;
          line-height: 1.2; letter-spacing: -.01em; }
.stat span { font-size: 9.5px; color: var(--text-3); text-transform: uppercase;
             letter-spacing: .05em; white-space: nowrap; }
/* Ключевые моменты под графиком - та же плитка, что и в шапке, но своим
   рядом: выравнивание от начала и отступ сверху, а не «прижато вправо»
   у шапки прогона. */
.chart-kpis { margin-left: 0; justify-content: flex-start; margin-top: 10px; }
.chart-kpis .stat { align-items: flex-start; }

/* min-width: 0 обязателен, и это не перестраховка. У флекс-элемента ширина
   по умолчанию не может стать меньше содержимого (min-width: auto), поэтому
   широкая таблица внутри распирала <main>, следом #workspace, а следом
   и страницу: горизонтальная полоса появлялась у всего экрана, и прокрутив
   её, человек терял из виду вкладки и шапку. Внутренние области прокрутки
   при этом работали правильно - просто им не давали сработать. */
main { flex: 1; display: flex; min-height: 0; min-width: 0; }
/* min-height: 0 обязателен: у флекс-элемента по умолчанию min-height: auto,
   и колонка не сжимается ниже высоты содержимого - вместо прокрутки внутри
   .scroller таблица распирала бы страницу, а body с overflow: hidden просто
   обрезал бы её без возможности доскроллить. */
.left { flex: 1; display: flex; flex-direction: column; min-width: 0; min-height: 0; padding: 12px 0 0 16px; overflow: hidden; }
/* .left теперь всегда несёт .card тоже (рамка+фон, components.css) - тот
   же приём, что уже дал карточкам настройки эталонный вид (DESIGN.md).
   padding/overflow у .left НЕ трогаю: оба объясняются комментарием выше
   (виртуализированная таблица, борьба с горизонтальным переполнением) -
   трогать их ради симметричных отступов рискованнее, чем остаться с
   контентом без запаса у правого/нижнего края карточки. */

/* Панель фильтров - ОТДЕЛЬНАЯ ПОВЕРХНОСТЬ, а не строка элементов по фону.
   В инструменте, где разбирают тысячу строк, фильтры и есть основной
   интерфейс: ими пользуются чаще, чем чем-либо ещё на экране. Пока поиск,
   чипы и порог лежали вперемешку прямо на фоне, они читались как случайный
   ряд кнопок над таблицей, а не как один орган управления. */
.toolbar {
  flex: none; display: flex; align-items: center; gap: 10px 14px;
  margin-bottom: 10px; padding: 10px 12px; flex-wrap: wrap;
  background: var(--surface); border: 1px solid var(--line);
  border-radius: var(--radius);
}
.toolbar input[type=search] { flex: 0 1 240px; }
/* Разделитель между смысловыми группами панели: поиск | фильтры | порог.
   Без него три группы сливаются в одну длинную строку. */
.toolbar .tb-sep {
  align-self: stretch; width: 1px; background: var(--line); margin: -2px 2px;
}
.inline-threshold { display: flex; align-items: center; gap: 7px; font-size: 12px; color: var(--text-2); }
.inline-threshold input[type=range] { width: 110px; }
#rowcount { margin-left: auto; font-size: 12px; white-space: nowrap; display: flex; align-items: center; gap: 4px; }

/* таблица */
/* Просвет между колонками - 12 пикселей, а не 6. Шесть было мало: заголовки
   набраны капителью вразрядку, и соседние короткие («КЛИК» и «КАМПАНИЯ»
   у площадок) сливались в одно слово. Числа страдали тем же - колонка
   расхода прилипала к CTR, и глазом их приходилось разделять по смыслу,
   а не по виду. Сетка от этого не ломается: лишнее забирают гибкие колонки
   имени и кампании. */
.table-head, .row, .table-foot {
  display: grid;
  /* Последняя колонка - статус с уровнем («исключено · группа»), и 128 ей
     мало: слово уровня обрезалось. Забираем у гибкой колонки имени -
     n-грамма из двух слов помещается и в меньшую. */
  grid-template-columns: 30px 42px minmax(140px, 1fr) 74px 58px 86px 62px 60px 80px 56px 154px;
  align-items: center;
  /* 10, а не 12: двенадцать не помещались на ноутбуке, а шесть склеивали
     соседние заголовки в одно слово («КЛИК КАМПАНИЯ»). */
  gap: 10px;
  min-width: var(--table-min);
}
/* Шапка живёт ВНУТРИ прокрутки и липнет к верху. Снаружи она разъезжалась
   с рядами на ширину полосы прокрутки, а ряды дополнительно смещал padding
   у .scroller - колонки уходили влево тем сильнее, чем правее. */
.table-head {
  position: sticky; top: 0; z-index: 2;
  /* Трактовка шапки - как у .dtable из DESIGN.md (закрашенный фон,
     обычный регистр, --muted), а не капитель вразрядку на прозрачном
     фоне, как было: токены после этапа 1 уже общие, менялась только
     подача. Сетка и .scroller остаются как были - это не переход
     на обычный <table>, у виртуализированных таблиц своя причина жить
     на .scroller (web/README.md, «Таблицы: одно правило на все шесть»). */
  font-size: var(--fs-sm); color: var(--muted); font-weight: 600;
  padding: 8px 0 8px 6px;
  background: var(--surface-2);
  /* Тонкая линия вместо жирной плюс мягкая тень под шапкой: при прокрутке
     видно, что строки уходят ПОД шапку, а не упираются в перегородку. */
  border-bottom: 1px solid var(--line);
  box-shadow: 0 1px 0 var(--line), 0 6px 12px -10px rgba(45, 45, 45, .35);
}
/* Итог по таблице. Липнет к низу прокрутки и повторяет сетку шапки, поэтому
   лежит внутри #scroller, а не под ним. */
.table-foot {
  position: sticky; bottom: 0; z-index: 2;
  padding: 7px 0 7px 6px;
  background: var(--surface);
  border-top: 1px solid var(--line-strong);
  font-size: 12px;
  font-variant-numeric: tabular-nums;
}
.table-foot:not([hidden]) { display: grid; }
.table-foot .c-num { text-align: right; font-weight: 700; }
.table-foot .c-name { display: flex; align-items: baseline; gap: 8px; }
.table-foot .tlabel { font-weight: 700; }
.table-foot .share { color: var(--text-3); font-weight: 400; font-size: 10.5px; }
.table-foot .dim { color: var(--text-3); font-weight: 400; }

/* ==========================================================================
   Номер строки - во всех списках всех инструментов
   ==========================================================================
   Номер строки нужен не для красоты. По списку работают вдвоём и вслух:
   «посмотри седьмую», «до двенадцатой всё нормальное». Без номера остаётся
   зачитывать длинный адрес или запрос целиком, а он у половины строк
   похож на соседний.

   Номер показывает ПОЗИЦИЮ в текущем порядке, а не признак строки: он
   меняется при сортировке и поиске. Поэтому он приглушён и набран
   моноширинными цифрами - чтобы не спорил с данными и не прыгал по ширине
   при прокрутке.

   У n-грамм и площадок номер живёт своей колонкой сетки (.c-idx), у карточек
   и групп - этой плашкой. */
.rownum {
  flex: none; min-width: 22px;
  color: var(--text-3); font-size: 11px; font-weight: 600;
  font-variant-numeric: tabular-nums; text-align: right;
}
/* В таблице SEO номер - своя колонка: узкая, прижата вправо, не сортируется. */
.dtable th.c-rownum, .dtable td.c-rownum {
  width: 38px; text-align: right; padding-right: 8px;
  color: var(--text-3); font-variant-numeric: tabular-nums;
}
.dtable th.c-rownum { cursor: default; }
.dtable th.c-rownum:hover { color: var(--text-3); }

.sortable { cursor: pointer; user-select: none; }
.sortable:hover { color: var(--accent); }
.sortable.active { color: var(--accent); }

/* ==========================================================================
   ПРАВИЛО ДЛЯ ЛЮБОЙ ТАБЛИЦЫ ЛЮБОГО ИНСТРУМЕНТА
   ==========================================================================
   Оно одно, и относится ко всем шести: n-граммы, площадки, SEO, проверка
   URL, кластеризация, объявления.

   1. Область прокрутки работает по ОБЕИМ осям (.scroller ниже).
   2. У сетки таблицы есть минимальная ширина. Ниже неё таблица
      ПРОКРУЧИВАЕТСЯ, а не сжимается в кашу и не обрезается молча.
   3. У виртуализированной таблицы ту же минимальную ширину несёт распорка:
      строки в ней позиционированы абсолютно и без этого оборвались бы
      по видимой части.

   Минимум у каждой таблицы свой (--table-min объявляется рядом с её сеткой),
   потому что колонки у них разные. Общее - что он есть.

   Живая ошибка, из-за которой правило записано: у n-грамм перестала
   помещаться последняя колонка «Статус», и она просто исчезла за краем.
   Не сжалась, не перенеслась - у .left стоит overflow: hidden, и всё, что
   не влезло, пропадало БЕЗ полосы прокрутки. Понять, что колонка есть,
   было нельзя ничем. */

/* Прокрутка ОБЕИХ осей, а не только вертикальной.

   Живая ошибка: колонки не помещались, и последняя - «Статус» - просто
   обрезалась. Не сжималась, не переносилась, а исчезала за краем: у .left
   стоит overflow: hidden, и всё, что не влезло, пропадало БЕЗ полосы
   прокрутки. Человек видел таблицу, у которой не хватает колонки, и понять,
   что она есть, было нельзя ничем.

   Ширина у таблицы своя, минимальная (--table-min): ниже неё колонки
   перестают быть читаемыми, и правильное поведение - прокрутка, а не
   сжатие в кашу. Считается она просто: сумма фиксированных колонок плюс
   зазоры плюс минимум гибкой колонки имени. */
/* min-height: 0 даёт таблице сжиматься по правилам flex, но у неё должен
   быть пол: сосед сверху (.charts-box) теперь сам ограничен по высоте
   (components-shared.css), но запас не повредит - без него таблица могла
   бы сжаться до нескольких пикселей на совсем коротком окне. */
.scroller { flex: 1; min-height: 140px; overflow: auto; padding-right: 14px; position: relative; scrollbar-gutter: stable; }
/* Распорка задаёт ширину строкам: они позиционированы абсолютно и тянутся
   от её левого края до правого. Без min-width на ней строки при
   горизонтальной прокрутке обрывались бы по видимой части. */
#spacer { position: relative; min-width: var(--table-min); }
.row {
  height: var(--row-h); padding-left: 6px;
  border-bottom: 1px solid var(--line);
  position: absolute; left: 0; right: 0;
}
/* Разделитель строк - ЕДВА ЗАМЕТНЫЙ. Плотность в таблице держится
   типографикой (выравнивание, табличные цифры, ритм колонок), а не сеткой:
   линия в полный контраст под каждой из тысячи строк читается как решётка
   и мешает вести взгляд по столбцу. */
.row { transition: background .1s; border-bottom-color: var(--row-line); }
.row:hover { background: var(--surface-2); }
.row.wl { background: var(--wl); border-bottom-color: var(--wl-line); }
.row.minused { background: var(--minused); border-bottom-color: var(--no-line); }
.row.phrase { background: var(--phrase); border-bottom-color: var(--phrase-line); }
/* Отмеченная строка: полоса слева И подложка. Одной полосой в три пиксела
   отметка терялась, когда строка уезжала в конец длинного списка. */
.row.picked { box-shadow: inset 3px 0 0 var(--accent); background: var(--accent-soft); }

.c-check { display: flex; align-items: center; justify-content: center; }
.c-idx { text-align: right; padding-right: 6px; color: var(--text-3); font-size: 11px; font-variant-numeric: tabular-nums; }
.field.narrow { min-width: 0; }
.c-name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; cursor: pointer; }
.c-name:hover { color: var(--accent); }
/* Слово внутри n-граммы - свой курсор и подсказка: Ctrl+клик добавляет
   в корзину само слово, а не всю фразу (см. wordSpans в tool-ngram.js). */
.c-name .w:hover { text-decoration: underline dotted; }
.c-name .w.picked {
  background: var(--accent-soft); color: var(--accent);
  border-radius: 3px; padding: 0 2px;
}
.c-num { text-align: right; font-variant-numeric: tabular-nums; }
/* Статус и уровень («исключено» + «группа») - в строку и без обрезки:
   уровень решает, что делать дальше, и обрезанный он бесполезен. */
.c-flag {
  padding-left: 12px; overflow: hidden; white-space: nowrap;
  display: flex; align-items: center; gap: 6px;
}
.table-head .c-flag { padding-left: 12px; }

/* Статус - цветная плашка, а не серый текст: строку с исключением должно быть
   видно и в общем потоке, а не только по заливке. */
/* Плашки в строке: цвет НЕСЁТ СМЫСЛ, а не украшает.
   Заливка с рамкой у каждой плашки на тысяче строк превращала таблицу
   в лоскутное одеяло: категория, статус и подсветка спорили за внимание,
   и «защищена» читалось так же громко, как «кандидат». Нейтральные плашки
   стали просто текстом, значащие оставили заливку - но без рамки, её роль
   и так выполняет фон. */
.tag {
  display: inline-block; padding: 2px 8px; border-radius: 999px;
  font-size: 10.5px; line-height: 1.5; white-space: nowrap;
  border: 1px solid transparent;
}
.tag-wl  { background: var(--wl);     color: var(--wl-text); font-weight: 500; }
.tag-phr { background: var(--att-bg); color: var(--att-text); font-weight: 600; }
.tag-min { background: var(--no-bg);  color: var(--no-text); font-weight: 500; }
/* Нейтральная плашка - без подложки вовсе: «сайт» и «рабочая» не требуют
   решения, и подсвечивать их незачем. */
.tag-free { background: none; color: var(--text-3); }

/* Пояснение над таблицей - на подложке, а не текстом по фону страницы:
   мелкий второстепенный шрифт без неё теряется, особенно на сером.
   Текст меняется при переключении раздела, поэтому высота всё равно
   гуляет по контенту - паддинг (было 7px) то немногое, что можно снять
   без потери текста. */
.table-hint {
  flex: none;
  margin: 0 0 8px; padding: 5px 10px;
  font-size: 12px; line-height: 1.5; color: var(--text-2);
  background: var(--surface); border: 1px solid var(--line);
  border-left: 3px solid var(--line-strong);
  border-radius: var(--radius-sm);
  display: flex; align-items: center; gap: 6px; flex-wrap: wrap;
}
/* Кружка с вопросом здесь больше нет. Он ничего не делал, а выглядел точно
   так же, как интерактивная подсказка `.hint-dot` у полей формы: два
   одинаковых значка, один из которых кликается, а другой нет, - это хуже,
   чем отсутствие значка вовсе. Пояснение и так стоит рядом. */
/* Второстепенная кнопка: то же начертание, что у брендовой, но контуром.
   Выгрузка - обычное действие, а не главное на экране, и спорить за внимание
   с «Скопировать» или «Залить в Директ» ей незачем. */
button.secondary {
  padding: 9px 18px;
  border: 1px solid var(--line-strong);
  background: var(--surface);
  color: var(--text);
  font-weight: 700;
  font-size: 11.5px;
  text-transform: uppercase;
  letter-spacing: .04em;
  transition: border-color .12s, color .12s, background .12s, transform .06s;
}
button.secondary:hover:not(:disabled) {
  border-color: var(--accent); color: var(--accent); background: var(--accent-soft);
}
button.secondary:active:not(:disabled) { transform: translateY(1px); }
button.secondary:disabled { color: var(--text-3); border-color: var(--line); cursor: default; }

/* Готовая выгрузка: кнопка заменяется ссылкой на таблицу - она должна
   выглядеть так же, чтобы ряд кнопок не «прыгал» после нажатия. */
a.gsheets.done {
  display: inline-block; padding: 9px 18px;
  border: 1px solid var(--live-line); background: var(--live-bg);
  color: var(--live-text); border-radius: var(--radius-sm);
  font-weight: 700; font-size: 11.5px;
  text-transform: uppercase; letter-spacing: .04em; text-decoration: none;
}
a.gsheets.done:hover { border-color: var(--live-text); }
.gsheets-error {
  width: 100%; margin: 4px 0 0; color: var(--no-text);
  font-size: 11.5px; line-height: 1.45;
}

button.link.inline { padding: 0; font-size: 11.5px; color: var(--accent); text-decoration: underline dotted; }

/* корзина */
/* Правая панель. Ширина резиновая: на широком мониторе названия кампаний
   и минус-фразы перестают обрезаться, на узком панель не съедает таблицу. */
/* Кнопка сворачивания (.aside-toggle) больше не живёт внутри .right -
   три прохода подряд: сперва общая кнопка в шапке → потом девять копий,
   по одной в каждом .right (решение «общий модуль, не форк по
   инструментам») → теперь снова ОДНА кнопка, но не в .right, а в общей
   шапке #tool-screen (#top-actions-results), рядом с «Изменить
   параметры» (прямое решение пользователя: «объединить в одну, вынести
   из окна» - действия результата в одном месте, не разбросаны по .meta
   и .right). Форка по-прежнему нет: один переключатель `body.aside-
   hidden` двигает любой .right одинаково, кнопка просто физически одна,
   не девять. Из-за этого .right возвращается к простому display: none -
   кнопке больше не нужно оставаться видимой ВНУТРИ панели, чтобы её
   развернуть обратно. */
.aside-toggle {
  display: flex; align-items: center; gap: 6px;
  font: inherit; font-size: inherit; font-weight: 600; cursor: pointer; white-space: nowrap;
}
/* Стрелка смотрит направо - «спрятать вправо», туда, где панель и стоит. */
.aside-toggle svg { flex: none; transition: transform .12s; transform: rotate(-90deg); }
body.aside-hidden .aside-toggle svg { transform: rotate(90deg); }
body.aside-hidden .aside-toggle span { display: none; }
body.aside-hidden .workspace-pane > .right { display: none; }
/* Справку трогать не надо: она открывается отдельным окном, а не живёт
   в этой колонке, - и прятать её вместе с панелью было бы неверно. */

/* ПАНЕЛЬ ПРОКРУЧИВАЕТСЯ САМА, и это не украшение.

   Живая ошибка, одна на все инструменты: панель была обычной колонкой без
   прокрутки, и всё, что не поместилось по высоте, просто уходило за нижний
   край экрана - без полосы, без признака, что там что-то есть. Заметнее
   всего это было на разборе моделью: пока он шёл, панель помещалась,
   а как только он был готов, появлялись плашка состояния и кнопка
   «Показать разбор» - и кнопка уезжала вниз, куда не доскроллить.

   Тот же класс ошибки, что был у таблиц: содержимое не сжимается и не
   переносится, а исчезает молча. Лечится так же - прокруткой. */
.right {
  width: clamp(350px, 26vw, 520px); flex: none; background: var(--surface);
  border-left: 1px solid var(--line); padding: 14px 16px;
  display: flex; flex-direction: column; min-height: 0;
  /* ОБЕ ОСИ ЗАДАНЫ ЯВНО, и это не перестраховка.

     По спецификации, если у одной оси overflow не `visible`, а у другой
     `visible`, то вторая вычисляется в `auto`. То есть одного
     `overflow-y: auto` достаточно, чтобы у панели появилась И горизонтальная
     прокрутка: длинная строка (список зеркал в «С чем считали») уезжала
     вбок, панель прокручивалась вправо, и подписи показателей срезались
     по левому краю. Выглядело это как наехавшая вёрстка.

     По горизонтали панель прокручиваться не должна вовсе: это узкая колонка
     фиксированной ширины, и её содержимое обязано переноситься - что и
     сделано ниже, у таблицы условий прогона. */
  overflow-y: auto; overflow-x: hidden; scrollbar-gutter: stable;
}
.tabs { display: flex; gap: 2px; margin-bottom: 12px; border-bottom: 1px solid var(--line); flex: none; }
/* Вкладок может быть больше, чем влезает в строку (crm_fields - сущностей
   столько, сколько на портале смарт-процессов): перенос не ломает три
   фиксированные вкладки n-грамм/кластеризации, они и так помещаются в ряд. */
.tabs.wrap { flex-wrap: wrap; }
.tab {
  padding: 7px 10px 8px; font-size: 12.5px; color: var(--text-2);
  border-bottom: 2px solid transparent; margin-bottom: -1px;
  display: flex; align-items: center; gap: 6px; border-radius: 0;
}
.tab:hover { color: var(--accent); }
.tab.on { color: var(--accent); border-bottom-color: var(--accent); font-weight: 500; }
.pane { display: flex; flex-direction: column; min-height: 0; flex: 1; }

.neg-list { list-style: none; margin: 8px 0 0; padding: 0; overflow-y: auto; flex: 1; }
.neg-list li {
  padding: 4px 0; border-bottom: 1px solid var(--line);
  font-size: 12.5px; display: flex; justify-content: space-between; gap: 8px;
}
.neg-list .kind { font-size: 10.5px; color: var(--text-3); white-space: nowrap; }

.help { overflow-y: auto; font-size: 12.5px; color: var(--text-2); }
.help h3 { font-size: 12.5px; margin: 14px 0 6px; color: var(--text); font-weight: 600; }
.help h3:first-child { margin-top: 0; }
.help p { margin: 0 0 8px; }
.help b { color: var(--text); }
.help code { background: var(--surface-2); padding: 1px 4px; border-radius: 3px; font-size: 11.5px; }
.help pre {
  background: var(--surface-2); border: 1px solid var(--line); border-radius: var(--radius-sm);
  padding: 9px 11px; font-size: 11px; line-height: 1.5; margin: 0 0 8px; white-space: pre-wrap;
}
.help .warn-note {
  background: var(--accent-soft); border-radius: var(--radius-sm);
  padding: 9px 11px; color: var(--text);
}
.legend { list-style: none; margin: 0; padding: 0; }
.legend li { display: flex; align-items: flex-start; gap: 8px; margin-bottom: 8px; line-height: 1.45; }
.legend .tag { flex: none; margin-top: 1px; }
.sw { width: 14px; height: 14px; border-radius: 3px; flex: none; margin-top: 2px; border: 1px solid var(--line-strong); }
.sw-wl { background: var(--wl); border-color: var(--wl-line); }
.sw-min { background: var(--minused); }
.sw-phr { background: var(--phrase); border-color: var(--phrase-line); }

#neg-search { flex: none; }
.counter {
  min-width: 22px; text-align: center; padding: 1px 7px; border-radius: 999px;
  background: var(--accent-soft); color: var(--accent);
  font-size: 11.5px; font-weight: 600; font-variant-numeric: tabular-nums;
}

.impact { border: 1px solid var(--line); border-radius: var(--radius-sm); padding: 12px; margin-bottom: 12px; background: var(--surface-2); }
.impact table { width: 100%; border-collapse: collapse; font-size: 12.5px; }
.impact td { padding: 3px 0; }
/* Значение переносится, а не распирает панель. Список зеркал и путь к таблице
   Метрики в одну строку не помещаются, а обрезать их нельзя: по ним сверяют,
   с чем считали. Раньше они просто уезжали за край колонки. */
.impact td:last-child {
  text-align: right; font-variant-numeric: tabular-nums; font-weight: 600;
  overflow-wrap: anywhere;
}
.impact tr.warn td { color: var(--warn); }
.impact .share { color: var(--text-3); font-weight: 400; font-size: 11px; margin-left: 4px; }

/* Кнопки переносятся, а не обрезаются: панель узкая, и на «Сводка для
   анализа» рядом с «В Google Таблицы» ширины хватает не всегда. */
.actions { display: flex; align-items: center; gap: 8px; margin-bottom: 14px; flex-wrap: wrap; }
.actions .primary { flex: 1; }

/* Главные кнопки панели липнут к её низу, пока панель прокручивается.

   Прокрутки одной мало: под кнопками живёт разбор моделью, и он растёт по
   ходу работы - плашка состояния, кнопка «Показать разбор», согласование.
   Каждое появление сдвигало кнопки вниз, и «Сводка для анализа» уезжала
   из поля зрения ровно в тот момент, когда она нужна.

   Липнет к НИЗУ, а не к верху: у самого низа прокрутки элемент возвращается
   на своё место, и блок разбора под ним виден целиком - постоянного
   перекрытия не возникает. Липкость только у панели результата: на экране
   настройки .actions стоит последним блоком, и липнуть ей не к чему. */
.right > .actions {
  position: sticky; bottom: 0; z-index: 2;
  background: var(--surface);
  padding: 8px 0; margin-bottom: 6px;
}

.basket { list-style: none; margin: 0; padding: 0; overflow-y: auto; flex: 1; min-height: 60px; }
.basket li {
  display: flex; justify-content: space-between; align-items: center; gap: 8px;
  padding: 5px 0; border-bottom: 1px solid var(--line); font-size: 12.5px;
}
.basket .bname { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.basket .bmeta { color: var(--text-3); font-size: 11px; font-variant-numeric: tabular-nums; white-space: nowrap; }
.basket .bmeta.manual { color: var(--att-text); font-variant-numeric: normal; }
.basket .bname { cursor: text; }
.basket .bname:hover { text-decoration: underline dotted; }
.bedit { flex: 1; min-width: 0; padding: 2px 6px; font-size: 12.5px; }

.manual { display: flex; gap: 6px; align-items: center; margin-bottom: 8px; }
.manual input { flex: 1; padding: 5px 9px; font-size: 12.5px; }
.manual button { flex: none; font-size: 16px; line-height: 1; padding: 4px 9px; }

.queries h4 { font-size: 11.5px; margin: 10px 0 5px; color: var(--text-2); font-weight: 600; }
.queries h4:first-of-type { margin-top: 0; }
.basket .x { cursor: pointer; color: var(--text-3); padding: 0 2px; }
.basket .x:hover { color: var(--danger); }

.queries { font-size: 12px; border-top: 1px solid var(--line); margin-top: 12px; padding-top: 12px; max-height: 40%; overflow-y: auto; }
.queries:empty { display: none; }
.queries h3 { font-size: 12px; margin: 0 0 8px; font-weight: 600; }
.queries table { width: 100%; border-collapse: collapse; }
.queries td { padding: 3px 0; border-bottom: 1px solid var(--line); vertical-align: top; }
.queries td:last-child { text-align: right; color: var(--text-3); white-space: nowrap; padding-left: 8px; font-variant-numeric: tabular-nums; }
/* ---------- выгрузка объявлений ---------- */
.ads-scroller { padding-right: 16px; }
.ad {
  border: 1px solid var(--line); border-radius: var(--radius-sm);
  margin-bottom: 10px; background: var(--surface); overflow: hidden;
}
.ad.has-over { border-color: var(--no-line); }
.ad header {
  display: flex; align-items: center; gap: 10px; flex-wrap: wrap;
  padding: 8px 12px; background: var(--surface-2); border-bottom: 1px solid var(--line);
  font-size: 12.5px;
}
.ad header .muted { font-size: 11.5px; }
.assets { list-style: none; margin: 0; padding: 0; }
/* Длина, текст и вид - три колонки. Меньше 320 текст перестаёт читаться
   строкой и рвётся по слогам. */
.assets li { --table-min: 320px; }
.assets li {
  display: grid; grid-template-columns: 44px 1fr 78px; gap: 10px; align-items: baseline;
  min-width: var(--table-min);
  padding: 5px 12px; border-bottom: 1px solid var(--line); font-size: 12.5px;
}
.assets li:last-child { border-bottom: 0; }
.assets li.over { background: var(--no-bg); }
.assets li.over .len { color: var(--no-text); font-weight: 700; }
.assets .len { text-align: right; font-variant-numeric: tabular-nums; color: var(--text-3); font-size: 11.5px; }
.assets .txt { word-break: break-word; }
/* Причина превышения - рядом с текстом, а не в подсказке: подсказку надо
   искать наведением, а разбирают такие списки быстрым просмотром. */
.assets .why { display: block; margin-top: 2px; font-size: 10.5px; color: var(--no-text); }
.assets .kind { font-size: 10.5px; color: var(--text-3); text-align: right; }

.queries pre { white-space: pre-wrap; word-break: break-word; font-size: 11px; background: var(--surface-2); padding: 8px; border-radius: var(--radius-sm); }

