/* ═══════════════════════════════════════════════════════════════════════
   CRA UI v3 — слой КОМПОНЕНТОВ. Токены: cra-tokens.css (не дублировать).
   Полный текст стандарта: docs/ak-air-design-v3_0.md (П5).

   Здесь реализованы ТОЛЬКО согласованные компоненты, один в один по файлам
   дизайна; ничего сверх них в этом слое не заводится (ст. 9.1, 13.3):
     · CRA_components-small_v1.1  — кнопки, поля, выбор, статусы, шкалы,
                                    чипы, счётчики, ссылки, сообщения;
     · Crawler_table_v1.4         — оболочка таблицы, тулбар, панель выделения,
                                    строки трёх уровней, подвал, пустота;
     · Crawler_card_v1.5          — носитель карточки: сцена, затемнение,
                                    шапка, чипы, вкладки, тело;
     · Crawler_modal_v1.1         — модалки, тосты, плашка долгой операции;
     · CRA_header-proto_v1.1      — шапка: логотип, разделы, выпадашки,
                                    глобальная зона, меню пользователя;
     · Crawler_states_v1.3        — свод состояний: пустота, скелет загрузки,
                                    ошибка и отказ доступа, тосты (разд. 15).

   ── Префикс классов: cra- ──
   Выбран один на весь слой и держится без исключений. Почему именно он:
   1) имена из макетов (.btn, .st, .note, .wrap, .empty, .inp, .seg, .shell,
      .toolbar, .count, .pxbar) — локальные имена прототипов; первые пять уже
      заняты в app.css, и перенос «как есть» перекрасил бы старые экраны;
   2) префикс ak- занят живым слоем ak.css (996 строк): второе объявление
      .ak-table рядом с первым — это расходящиеся копии одного компонента,
      тот же класс ошибки, что расходящиеся копии фильтра live_links();
   3) ст. 9.1 стандарта на такой случай велит брать префикс ПРОЕКТА — проект
      в дизайн-папке называется CRA (CRA_theme-passport, CRA_header-proto),
      отсюда cra-. Отступление от «одного набора ak- на проект» записано
      в docs/deviations.md в момент принятия решения.
   Имена компонентов соответствуют словарю Приложения Б: cra-table,
   cra-card, cra-modal, cra-toast, cra-empty, cra-badge → cra-status и т.д.

   ── Область действия ──
   Каждое правило начинается с [data-ui="v3"]: где атрибута на <html> нет,
   слой не применяется. Атрибут стоит РОВНО В ОДНОМ месте — в новой
   оболочке app/ui/templates/cra_shell.html; base.html и JS его не ставят,
   поэтому 24 старых экрана этот слой не видят вовсе.

   Правила слоя (гл. 9): ни одного цветового литерала мимо var(); без
   !important; без стилей по id; без классов под конкретную таблицу или
   страницу; логические свойства вместо left/right.
   ═══════════════════════════════════════════════════════════════════════ */

/* ══ 0. ОБОЛОЧКА СТРАНИЦЫ ══════════════════════════════════════════════
   Паспорт ст. 6.6: фон — сетка-точки 16px зелёным; на панелях сетки нет. */
[data-ui="v3"] { color-scheme: dark; }
[data-ui="v3"][data-theme="light"] { color-scheme: light; }

[data-ui="v3"] * { box-sizing: border-box; }

/* Кнопка, поле и список НЕ наследуют шрифт страницы — это умолчание
   браузера, а не решение набора. Видно было на одном компоненте сразу:
   ui.ib рисует то <a>, то <button>, и ссылка выходила «CRA Inter», а
   кнопка тем же классом — системным Arial (на /system/refs 39 против 14).
   Гарнитур в теме две (паспорт ст. 4), Arial среди них нет.
   Правило задаёт ПОЛ, а не вид: у компонента со своим шрифтом объявление
   сильнее (у него есть [data-ui] и класс), и оно продолжает действовать.
   Вес правила — [data-ui] плюс тег: оно обязано выигрывать у голого
   `button { font-family: … }` старого слоя на страницах legacy-оболочки,
   иначе тот же компонент снова рисуется по-разному в двух оболочках.
   Любому правилу кита (там есть класс) оно проигрывает.
   Наследуется только ГАРНИТУРА, не весь шрифт: `font: inherit` заодно
   подтянул бы кегль страницы, и контролы без своего размера подросли бы
   на два пикселя — вид поехал бы там, где чинили только шрифт. Кегль —
   отдельный вопрос и отдельное решение. */
[data-ui="v3"] button,
[data-ui="v3"] input,
[data-ui="v3"] select,
[data-ui="v3"] textarea { font-family: inherit; }

/* Фокус клавиатуры — паспорт ст. 7: рамка 2px --accent-dim со смещением
   2px, видима ВСЕГДА. Объявлялась она покомпонентно, и у половины слоя её
   не оказалось вовсе: имя объекта, ссылка, значок-стрелка, вкладка, пункт
   рейки, плитка, галочка в таблице получали умолчание браузера — голубое
   кольцо rgb(153,200,255). Синего в палитре проекта нет, и «видима
   всегда» — не про эстетику: без рамки слой недоступен с клавиатуры.
   Здесь рамка задана ОДИН раз всему, что фокусируется. Компонент со
   своей рамкой (красная у danger, вдавленная у сегмента и пункта меню)
   перебивает её своей специфичностью — список исключений вести не нужно,
   и новый компонент получает рамку по построению, а не по недосмотру. */
:where([data-ui="v3"]) :focus-visible {
  outline: 2px solid var(--vc-v3-accent-dim); outline-offset: 2px;
}

[data-ui="v3"] body.cra-body {
  margin: 0;
  background: var(--bg);
  color: var(--text);
  font: var(--vc-v3-fs-lead) / 1.55 var(--font-body);
  background-image: radial-gradient(circle,
    color-mix(in srgb, var(--accent) 14%, transparent) 1px, transparent 1px);
  background-size: 16px 16px;
}

/* ak-shell: рабочая поверхность на всю ширину окна (ст. 3.2) */
[data-ui="v3"] .cra-shell {
  display: flex;
  flex-direction: column;
  min-height: 100vh;
}
[data-ui="v3"] .cra-main {
  padding: var(--pad-page);
  /* Воздух под последним блоком страницы — межблочный шаг паспорта. */
  padding-block-end: var(--vc-v3-gap-block);
  flex: 1;
}

/* Три уровня заголовков (ст. 3.4): страница → блок → секция */
[data-ui="v3"] .cra-h1,
[data-ui="v3"] .cra-h2,
[data-ui="v3"] .cra-h3 { font-family: var(--font-display); font-weight: 600; }
[data-ui="v3"] .cra-h1 { font-size: var(--vc-v3-fs-page); margin: 0 0 var(--vc-v3-gap-inner); }
[data-ui="v3"] .cra-h2 { font-size: var(--vc-v3-fs-block); margin: var(--vc-v3-gap-group) 0 var(--vc-v3-gap-item); }
[data-ui="v3"] .cra-h3 { font-size: var(--vc-v3-fs-lead); margin: var(--vc-v3-gap-item) 0 var(--vc-v3-gap-inner); }
[data-ui="v3"] .cra-eyebrow {
  font: var(--vc-v3-fs-label) var(--font-mono);
  letter-spacing: .18em; text-transform: uppercase;
  color: var(--vc-v3-accent-ink);
  margin-block-end: var(--vc-v3-gap-inner);
}
[data-ui="v3"] .cra-eyebrow::before { content: "■ "; font-size: var(--vc-v3-px-atom); }
[data-ui="v3"] .cra-muted { color: var(--muted); }
[data-ui="v3"] .cra-mono { font-family: var(--font-mono); }
/* Мера строки ~90 символов для связного текста (ст. 3.2) */
[data-ui="v3"] .cra-prose { max-width: 66ch; }

/* ══ 1. КНОПКИ — CRA_components-small_v1.1, разд. 1 ═══════════════════ */
/* text-decoration: none — не украшение, а условие узнаваемости компонента.
   Кнопка набора рисуется то <button>, то <a> (btn(href=…) — переход обязан
   быть настоящей ссылкой, ст. 5.8), и у ссылочной ветки браузер сам
   добавлял подчёркивание: сплошная зелёная «Новый сбор» читалась ссылкой
   на четырёх экранах сразу. Правило снимает подчёркивание у КОМПОНЕНТА,
   а не у страницы, — иначе оно возвращалось бы с каждым новым носителем.
   То же сделано ниже везде, где компонент бывает и ссылкой, и кнопкой:
   .cra-ib, .cra-menuitem, .cra-rail__item, .cra-tile, .cra-topbar__home. */
[data-ui="v3"] .cra-btn {
  display: inline-flex; align-items: center; gap: var(--vc-v3-gap-inner);
  height: var(--vc-v3-ctrl-h); padding-inline: 16px;
  border: 1px solid var(--line);
  background: var(--surface-2); color: var(--text);
  font: 600 13px var(--font-body); cursor: pointer; text-decoration: none;
  transition: border-color .12s, background .12s;
}
[data-ui="v3"] .cra-btn:not(.cra-btn--danger):not(.cra-btn--primary):hover { border-color: var(--vc-v3-accent-dim); }
[data-ui="v3"] .cra-btn:focus-visible { outline: 2px solid var(--vc-v3-accent-dim); outline-offset: 2px; }
[data-ui="v3"] .cra-btn--primary { background: var(--accent); border-color: var(--accent); color: var(--on-color); }
[data-ui="v3"] .cra-btn--primary::before { content: ""; width: 8px; height: 8px; background: var(--on-color); }
[data-ui="v3"] .cra-btn--primary:hover { filter: brightness(1.12); }
/* Ховер и фокус наследуют смысловой цвет элемента (паспорт ст. 7, РД-12):
   красное подсвечивается своим красным, зелёный акцент на него не залезает. */
[data-ui="v3"] .cra-btn--danger { background: transparent; border-color: var(--err); color: var(--vc-v3-err-ink); }
[data-ui="v3"] .cra-btn--danger:hover { background: color-mix(in srgb, var(--err) 12%, transparent); border-color: var(--err); }
[data-ui="v3"] .cra-btn--danger:focus-visible { outline-color: var(--err); }
[data-ui="v3"] .cra-btn--ghost { background: transparent; }
[data-ui="v3"] .cra-btn:disabled { opacity: .5; cursor: not-allowed; filter: none; }
[data-ui="v3"] .cra-btn--primary:disabled { background: var(--surface-2); border-color: var(--line); color: var(--muted); }
[data-ui="v3"] .cra-btn--primary:disabled::before { background: var(--muted); }
[data-ui="v3"] .cra-btn--loading { position: relative; color: transparent; }
[data-ui="v3"] .cra-btn--loading::before { display: none; }
[data-ui="v3"] .cra-btn--loading::after {
  content: ""; position: absolute; inset-inline-start: 50%; inset-block-start: 50%;
  width: 5px; height: 5px; margin: -2.5px 0 0 -2.5px; background: transparent;
  animation: cra-pxring .9s steps(8) infinite;
}

/* ══ 2. ПОЛЯ ВВОДА — CRA_components-small_v1.1, разд. 2 ═══════════════ */
[data-ui="v3"] .cra-input,
[data-ui="v3"] .cra-select,
[data-ui="v3"] .cra-textarea {
  background: var(--bg); border: 1px solid var(--line); color: var(--text);
  padding-inline: 12px; font: 13px var(--font-mono);
  height: var(--vc-v3-ctrl-h); min-width: 220px;
}
[data-ui="v3"] .cra-textarea {
  padding: 9px 12px; height: auto; min-height: 76px;
  font-family: var(--font-body); font-size: var(--vc-v3-fs-data); resize: vertical;
}
[data-ui="v3"] .cra-input::placeholder,
[data-ui="v3"] .cra-textarea::placeholder { color: var(--muted); }
[data-ui="v3"] .cra-input:focus-visible,
[data-ui="v3"] .cra-select:focus-visible,
[data-ui="v3"] .cra-textarea:focus-visible { outline: 2px solid var(--vc-v3-accent-dim); outline-offset: 1px; }
/* Выбор файла. Своего компонента в ките нет, и он не заводится: это то же
   поле набора, у которого браузер рисует внутри СВОЮ кнопку — серую
   «Choose File» с системным шрифтом, единственный неоформленный контрол
   на восьми экранах («Загрузить новую версию», /system/update).
   Кнопка внутри поля — псевдоэлемент, и достать её можно только по типу
   поля; вид ей даётся тот же, что у кнопки набора, значениями кнопки, а
   не новыми. Отступ справа отделяет её от имени выбранного файла. */
[data-ui="v3"] input[type="file"] {
  display: inline-flex; align-items: center;
  font: 400 var(--vc-v3-fs-meta) var(--font-mono); color: var(--muted);
}
[data-ui="v3"] input[type="file"]::file-selector-button {
  height: 28px; padding-inline: 12px; margin-inline-end: var(--vc-v3-gap-inner);
  border: 1px solid var(--line); background: var(--surface-2); color: var(--text);
  font: 600 var(--vc-v3-fs-meta) var(--font-body); cursor: pointer;
}
[data-ui="v3"] input[type="file"]::file-selector-button:hover { border-color: var(--vc-v3-accent-dim); }
[data-ui="v3"] .cra-input--err { border-color: var(--err); }
[data-ui="v3"] .cra-input--err:focus-visible { outline-color: var(--err); }
[data-ui="v3"] .cra-field { display: flex; flex-direction: column; gap: 5px; }
[data-ui="v3"] .cra-label {
  font: 600 var(--vc-v3-fs-label) var(--font-body);
  letter-spacing: .09em; text-transform: uppercase; color: var(--muted);
}
[data-ui="v3"] .cra-hint { font-size: var(--vc-v3-fs-meta); color: var(--muted); }
[data-ui="v3"] .cra-errtext { font-size: var(--vc-v3-fs-meta); color: var(--vc-v3-err-ink); }
[data-ui="v3"] .cra-select {
  appearance: none;
  background-image: linear-gradient(45deg, transparent 50%, var(--muted) 50%),
                    linear-gradient(135deg, var(--muted) 50%, transparent 50%);
  background-position: calc(100% - 16px) 55%, calc(100% - 11px) 55%;
  background-size: 5px 5px; background-repeat: no-repeat;
  padding-inline-end: 32px;
}

/* ══ 3. ВЫБОР: галочка, радио, тумблер — всё квадратное (паспорт ст. 6.4)
   Паспорт ст. 6.4: «маркеры списков, буллиты, чекбоксы — квадратные»;
   скруглений в теме нет вовсе (ст. 5), единственное исключение — плашка
   .cra-mk 3px. Поэтому вид задан ВСЯКОМУ полю выбора внутри слоя, а не
   только тому, что носит класс набора: нативная галочка и нативное радио
   рисуются системным СИНИМ, а синего в палитре проекта нет. Синее радио
   и жило посреди формы «Новый сбор» — разметку её зон собирает скрипт
   экрана, классы набора на поля не попадали, и правило по классу до них
   не доставало.
   Радио отличается от галочки не формой, а ЛОГИКОЙ (кит v1.4 так и
   называет разд. 3: «чекбокс · радио-логика · тумблер»): выбор один из
   нескольких обеспечивает браузер по атрибуту name. Круглое радио макета
   «Нового сбора» (.ro .r{border-radius:50%}) — расхождение макета с
   паспортом, записано DR-72. */
[data-ui="v3"] input[type="checkbox"],
[data-ui="v3"] input[type="radio"] {
  appearance: none; width: 15px; height: 15px; margin: 0; flex: none;
  border: 1px solid var(--line); background: var(--bg); cursor: pointer;
  display: inline-grid; place-content: center; vertical-align: middle;
}
[data-ui="v3"] input[type="checkbox"]:checked,
[data-ui="v3"] input[type="radio"]:checked { border-color: var(--accent); }
[data-ui="v3"] input[type="checkbox"]:checked::before,
[data-ui="v3"] input[type="radio"]:checked::before {
  content: ""; width: 7px; height: 7px; background: var(--accent);
}
[data-ui="v3"] input[type="checkbox"]:focus-visible,
[data-ui="v3"] input[type="radio"]:focus-visible { outline: 2px solid var(--vc-v3-accent-dim); outline-offset: 2px; }

[data-ui="v3"] .cra-check { display: inline-flex; gap: 9px; align-items: center; cursor: pointer; font-size: var(--vc-v3-fs-data); }
/* Два носителя у одной галочки, и оба принятые:
     · С ПОДПИСЬЮ — <label class="cra-check"><input>, кит v1.4 разд. 3;
     · БЕЗ ПОДПИСИ — <input class="cra-check">, таблица v1.6 (.ck): в
       колонке выделения подписи нет, подпись даёт aria-label.
   Правило было написано только на первый (.cra-check input), поэтому
   галочка выделения строки во ВСЕХ таблицах рисовалась браузерной —
   синей, а синего в палитре проекта нет вовсе. Селектор обязан ловить
   оба вида: разметку выдают сами макросы набора (table.head(select_all),
   table.cell_select), и «неправильной» разметки тут не было. */
[data-ui="v3"] .cra-check input,
[data-ui="v3"] input.cra-check {
  appearance: none; width: 16px; height: 16px; margin: 0; cursor: pointer;
  border: 1px solid var(--line); background: var(--bg);
  display: grid; place-content: center;
}
/* Табличная — 15×15 с отметкой 7×7 (Crawler_table_v1.6, .ck): своё
   значение эталона, а не округление кита; vertical-align нужен потому,
   что в ячейке она стоит без подписи и без флекса. */
[data-ui="v3"] input.cra-check { width: 15px; height: 15px; vertical-align: middle; }
[data-ui="v3"] .cra-check input:checked,
[data-ui="v3"] input.cra-check:checked { border-color: var(--accent); }
[data-ui="v3"] .cra-check input:checked::before,
[data-ui="v3"] input.cra-check:checked::before { content: ""; width: 8px; height: 8px; background: var(--accent); }
[data-ui="v3"] input.cra-check:checked::before { width: 7px; height: 7px; }
[data-ui="v3"] .cra-check input:indeterminate::before,
[data-ui="v3"] input.cra-check:indeterminate::before { content: ""; width: 8px; height: 2px; background: var(--accent); }
[data-ui="v3"] .cra-check input:focus-visible,
[data-ui="v3"] input.cra-check:focus-visible { outline: 2px solid var(--vc-v3-accent-dim); outline-offset: 2px; }
[data-ui="v3"] .cra-toggle {
  position: relative; width: 34px; height: 18px; padding: 0; border: none;
  background: var(--line); cursor: pointer; transition: background .12s;
}
[data-ui="v3"] .cra-toggle::before {
  content: ""; position: absolute; inset-inline-start: 2px; inset-block-start: 2px;
  width: 14px; height: 14px; background: var(--surface); transition: inset-inline-start .12s;
}
[data-ui="v3"] .cra-toggle[aria-checked="true"] { background: var(--accent); }
[data-ui="v3"] .cra-toggle[aria-checked="true"]::before { inset-inline-start: 18px; background: var(--on-color); }
[data-ui="v3"] .cra-toggle:focus-visible { outline: 2px solid var(--vc-v3-accent-dim); outline-offset: 2px; }

/* ══ 4. СТАТУСЫ — слово на плашке с бортом 2px (паспорт ст. 6.2) ══════
   Круглой точки у статуса нет: скругления в теме 0px. Единственный
   движущийся элемент — мигающий пиксель у состояний «идёт…». */
[data-ui="v3"] .cra-status { display: inline-block; padding: 3px 9px; font: 400 var(--vc-v3-fs-meta) var(--font-body); white-space: nowrap; }
[data-ui="v3"][data-theme="light"] .cra-status { font-weight: 500; }
[data-ui="v3"] .cra-status--ok { background: color-mix(in srgb, var(--accent) 12%, transparent); color: var(--vc-v3-accent-ink); box-shadow: inset 2px 0 0 var(--accent); }
[data-ui="v3"] .cra-status--run { background: color-mix(in srgb, var(--warn) 11%, transparent); color: var(--vc-v3-warn-ink); box-shadow: inset 2px 0 0 var(--warn); }
[data-ui="v3"] .cra-status--err { background: color-mix(in srgb, var(--err) 11%, transparent); color: var(--vc-v3-err-ink); box-shadow: inset 2px 0 0 var(--err); }
[data-ui="v3"] .cra-status--none { background: color-mix(in srgb, var(--muted) 10%, transparent); color: var(--muted); box-shadow: inset 2px 0 0 var(--line); }
[data-ui="v3"] .cra-status--working::after {
  content: ""; display: inline-block; width: 6px; height: 6px;
  margin-inline-start: 7px; background: var(--warn);
  animation: cra-blink 1s steps(2) infinite;
}

/* ══ 5. ШКАЛА ПОКРЫТИЯ И ПРОГРЕСС — атом 9×9 (паспорт ст. 6.1, 6.4) ═══ */
[data-ui="v3"] .cra-pxbar { display: inline-flex; gap: 3px; vertical-align: middle; }
[data-ui="v3"] .cra-pxbar > i { width: var(--vc-v3-px-atom); height: var(--vc-v3-px-atom); background: var(--accent); }
[data-ui="v3"] .cra-pxbar > i.cra-pxbar__off { background: var(--line); }
/* Третье состояние клетки — «не ожидается». Заведено 03.09.2026 по заявке
   e3-01: матрица покрытия семейств на /gaps показывает по каждому вендору
   три РАЗНЫХ факта — собрано · ожидается профилем, но нет · для этого
   класса не ожидается вовсе. Двумя состояниями паспорта (ст. 6.1) третий
   не выражается, а погашенной клеткой он выражается ВРАНЬЁМ: недобор и
   отсутствие ожидания смотрелись бы одинаково.
   Основание — то же правило, что у прочерка в таблице (разд. 8): «сбора
   не было» и «собрано ноль» разные факты. Здесь: «ждали и не собрали» и
   «не ждали». Поэтому клетка не залита и не погашена, а обведена: место
   в шкале занимает, на заполнение не претендует. */
[data-ui="v3"] .cra-pxbar > i.cra-pxbar__none {
  background: transparent; border: 1px solid color-mix(in srgb, var(--line) 70%, transparent);
}
[data-ui="v3"] .cra-cov { display: inline-flex; gap: var(--vc-v3-gap-inner); align-items: center; font-size: 13px; }
[data-ui="v3"] .cra-cov__word { color: var(--muted); }
[data-ui="v3"] .cra-prog { display: inline-flex; gap: 3px; }
[data-ui="v3"] .cra-prog > i { width: var(--vc-v3-px-atom); height: var(--vc-v3-px-atom); background: var(--line); }
[data-ui="v3"] .cra-prog > i.cra-prog__on { background: var(--accent); }
[data-ui="v3"] .cra-prog > i.cra-prog__head { background: var(--vc-v3-accent-ink); animation: cra-blink 1s steps(2) infinite; }
/* Полоса хода ЗАВЕРШЁННОГО прогона: та же шкала, другой цвет. Зелёная
   полоса у законченного прогона говорила бы «идёт», а это неправда;
   синий здесь значит «закончился» и с «новым» (--info) не смешивается —
   паспорт ст. 3 разводит их прямо. Мигающей головы у завершённого не
   бывает: двигаться нечему. */
[data-ui="v3"] .cra-prog--done > i.cra-prog__on { background: var(--vc-v3-run-done); }
[data-ui="v3"] .cra-prog--done > i.cra-prog__head { animation: none; background: var(--vc-v3-run-done); }

/* ══ 6. ЧИПЫ, СЧЁТЧИКИ, ССЫЛКИ — CRA_components-small_v1.1, разд. 6 ═══ */
/* nowrap — свойство самого чипа, а не таблицы, в которой он стоит: чип
   это однострочная метка (кит разд. 6), и у соседней плашки полноты
   nowrap стоит по той же причине. В тулбаре перенос незаметен, а в
   ячейке колонка ужимается по содержимому, чип из одного длинного слова
   переносится, и строка вырастает с 34px до 43px. */
/* Метрики из макета (`.chip{padding:2px 9px;font:400 11.5px моно;
   border:1px solid color-mix(line 70%)}`, документы v3.1): было 500 12px
   и сплошная рамка — плашка выходила заметно тяжелее макетной и в
   колонке «Тип» спорила с данными. Заявка v3-09. */
[data-ui="v3"] .cra-chip {
  display: inline-flex; gap: 7px; align-items: center; white-space: nowrap;
  border: 1px solid color-mix(in srgb, var(--line) 70%, transparent);
  padding: 2px 9px;
  font: 400 11.5px var(--font-mono); color: var(--muted);
}
[data-ui="v3"] .cra-chip b { color: var(--text); font-weight: 500; }
[data-ui="v3"] .cra-chip--on { border-color: var(--vc-v3-accent-dim); color: var(--vc-v3-accent-ink); }
[data-ui="v3"] .cra-chip__x { cursor: pointer; opacity: .6; }
[data-ui="v3"] .cra-chip__x:hover { opacity: 1; }
/* Счётчик стоит в одном ряду с полем, сегментом и кнопкой (все 36px), и
   без своей высоты провисал на 24px. Значение и способ — из эталона:
   Crawler_table_v1.6, .count — height:36px, inline-flex, поля 0 10px. */
[data-ui="v3"] .cra-count {
  font: 500 var(--vc-v3-fs-meta) var(--font-mono); color: var(--muted);
  border: 1px solid var(--line); padding-inline: 10px;
  height: var(--vc-v3-ctrl-h); display: inline-flex; align-items: center;
}
[data-ui="v3"] a.cra-link { color: var(--vc-v3-accent-ink); text-decoration: none; border-block-end: 1px solid color-mix(in srgb, var(--vc-v3-accent-ink) 40%, transparent); }
[data-ui="v3"] a.cra-link:hover { border-block-end-color: var(--vc-v3-accent-ink); }
/* Имя объекта: в ПОКОЕ обычный текст, пунктир и зелёный — по наведению.
   Кит v1.4 (a.ak-obj) рисует пунктир постоянно, и слой воспроизводил кит
   буквально. Но имя объекта живёт в ПЕРВОЙ КОЛОНКЕ таблицы, а не строкой
   в тексте: пять согласованных макетов подряд (документы v3.1, источники
   v2.3, продукты v3.0, прогоны v1.9, карточка рынка v2.8) рисуют его
   обычным текстом — `.docname{color:var(--text)}` — и красят в акцент
   только на :hover. Постоянный пунктир превращал целый столбец в список
   ссылок и спорил с данными; переход при этом никуда не делся — он в
   курсоре строки, в наведении и в самом адресе.
   Расхождение кита и пяти макетов записано: DR-71. */
[data-ui="v3"] a.cra-obj { color: var(--text); text-decoration: none; border-block-end: 1px dotted transparent; }
[data-ui="v3"] a.cra-obj:hover { color: var(--vc-v3-accent-ink); border-block-end-color: var(--accent); }
/* Внешний оригинал — стрелка ↗ (ст. 8.4): переход наружу и переход
   на свою карточку не должны выглядеть одинаково. */
[data-ui="v3"] a.cra-ext::after { content: " ↗"; font-size: var(--vc-v3-fs-label); color: var(--muted); }

/* ══ 7. СООБЩЕНИЯ В ПОТОКЕ — борт 2px цветом смысла ══════════════════ */
[data-ui="v3"] .cra-note {
  border-inline-start: 2px solid var(--accent);
  background: color-mix(in srgb, var(--accent) 7%, transparent);
  padding: 11px 14px; font-size: var(--vc-v3-fs-data);
}
[data-ui="v3"] .cra-note--warn { border-color: var(--warn); background: color-mix(in srgb, var(--warn) 8%, transparent); }
[data-ui="v3"] .cra-note--err { border-color: var(--err); background: color-mix(in srgb, var(--err) 8%, transparent); }

/* ══ 8. ТАБЛИЦА — Crawler_table_v1.4 ══════════════════════════════════
   Строка = одна строка текста (ст. 5.1), высота — токен --row-h.
   Линии только горизонтальные, зебры и вертикальных нет (ст. 5.16). */
/* Широкая таблица прокручивается ВНУТРИ своей оболочки: за вьюпорт не
   выталкивается ни строка с двухсотзначным названием, ни лишняя колонка
   (ст. 9.4, проверка А10). Тот же приём, что у старого слоя (.wrap). */
/* Оболочка таблицы стоит на --surface, то есть СВЕТЛЕЕ страницы: в
   макете фон и рамку несёт сама таблица (`table.docs{background:
   var(--surface);border:1px solid var(--line)}`, документы v3.1). Было
   наоборот — тело на цвете страницы, а заливку получала только шапка, и
   контраст выходил перевёрнутым: заголовок светлее данных, таблица
   проваливалась в фон. Фон шапки остаётся тем же цветом (она sticky и
   обязана быть непрозрачной) — отдельной заливки на вид больше нет. */
/* --vc-v3-ground — «чем закрашено под содержимым». Объявляется РЯДОМ с
   самой закраской и тем же значением: разойдутся — признак прокрутки
   (.cra-tablescroll--hint ниже) начнёт рисовать крышку чужим цветом, и
   она станет видимой полосой. Переменные наследуются, поэтому потомку
   достаточно спросить var(--vc-v3-ground) — знать своего предка ему не
   нужно. Умолчание стоит на body: страница закрашена --bg. */
[data-ui="v3"] body.cra-body { --vc-v3-ground: var(--bg); }
[data-ui="v3"] .cra-tableshell {
  border: 1px solid var(--line); background: var(--surface);
  --vc-v3-ground: var(--surface);
}
/* Горизонтальную прокрутку держит обёртка ТАБЛИЦЫ, а не вся оболочка.
   Причина названная: overflow-x на оболочке делает её скролл-контейнером и
   по вертикали тоже, и выпадашка тулбара («Колонки ▾», «Ещё ▾», фильтр
   рынка) обрезается его нижней границей — меню видно наполовину. Тулбару
   ездить вбок и не нужно: вбок уезжает широкая таблица (ст. 9.4, А10), и
   она по-прежнему делает это ВНУТРИ оболочки, за вьюпорт не вылезая. */
[data-ui="v3"] .cra-tablescroll { overflow-x: auto; }

/* ── Признак того, что таблица едет вбок (DR-68, приёмка 05.09.2026) ──
   Прокрутка сама себя не показывает. Полоса прокрутки на macOS накладная:
   в покое её не рисуют вовсе (offsetHeight − clientHeight = 0), и
   обрезанный последний столбец без единого признака, что таблицу можно
   сдвинуть, читается как ПОЛОМКА — ровно так его и прочитал проверяющий
   на карточке вендора при окне 1280.

   Как это работает — без единой строки скрипта. Четыре слоя фона на
   одном элементе, различающиеся background-attachment:
     · ТЕНЬ  (attachment: scroll) прибита к рамке контейнера и стоит на
       месте, сколько ни крути;
     · КРЫШКА (attachment: local) едет вместе с содержимым и закрашена
       цветом поверхности; в крайнем положении она приходит ровно на
       свою тень и гасит её.
   Итог: у края, в сторону которого ехать НЕКУДА, тени нет; у края, за
   которым содержимое ещё есть, — есть. Ни замера, ни обработчика
   прокрутки, ни класса, который надо снимать.

   Края обработаны ОБА и одинаково, поэтому правило не выбирает сторону
   и одинаково верно в LTR и в RTL: «конечный край» там разный, а
   симметричному правилу это безразлично (ст. 9.3 — физической стороны
   здесь не решается ни одной; логического аналога у background-position
   в CSS не существует вовсе).

   Цвет тени — смесь с --muted, приглушённым тоном темы: в тёмной теме он
   светлее поверхности, в светлой темнее, то есть в обеих читается как
   обрыв плоскости. --line, токен КРАЯ, по смыслу подошёл бы лучше, но
   замером не читается: в тёмной теме он отличается от подложки на
   10–19 единиц канала против 20–22 у смеси (кадры от 05.09.2026), а
   признак, который надо высматривать, — не признак. Литералов нет.

   ЦВЕТ КРЫШКИ — не --surface, а --vc-v3-ground, наследуемая переменная
   «чем закрашено под таблицей». Причина замером: на экране 3.1 таблица
   лежит в оболочке (--surface), а на карточке вендора — прямо в карточке
   (--bg), и крышка, зашитая одним цветом, во втором случае сама стала бы
   видимой полосой у края (замер: остаток 8–19 единиц канала там, где не
   должно быть НИЧЕГО). Переменная объявляется там, где подложку
   действительно красят, и наследуется вниз.

   Крышка ШИРЕ тени и первую половину идёт сплошной (`ground 50%`): фаду
   крышки и фаду тени не совпасть по форме — линейная против радиальной, —
   и на стыке оставался видимый остаток 8–9 единиц канала там, где не
   должно быть ничего. Со сплошным участком остаток ровно 0 (замер
   05.09.2026), то есть в крайнем положении признака НЕТ, а не «почти
   нет». Отсюда 40 px крышки против 18 px тени: это не запас на глаз.

   ГРАНИЦА ПРИЁМА. Шапка таблицы непрозрачна и sticky (её фон обязателен:
   иначе при прокрутке страницы сквозь неё видно данные), поэтому в
   полосе заголовков тень не проступает — она идёт вдоль СТРОК ДАННЫХ,
   начинаясь на 38 px ниже. Это осознанный предел приёма, а не недосмотр:
   вынести тень поверх содержимого можно только липким элементом внутри
   контейнера, то есть лишним узлом в разметке каждой таблицы.

   МОДИФИКАТОР, а не общее правило. Экраны 3.1, 3.3 и 4.1 при 1280 тоже
   переполнены (у «Продуктов» — на 9 px) и уже приняты по кадрам: общий
   признак изменил бы их вид и обесценил приёмку. Таблица объявляет
   признак у себя — tbl.table(scroll_hint=true), рядом с tight. */
[data-ui="v3"] .cra-tablescroll--hint {
  background-image:
    linear-gradient(to right, var(--vc-v3-ground) 50%, transparent),
    linear-gradient(to left, var(--vc-v3-ground) 50%, transparent),
    radial-gradient(farthest-side at 0 50%,
                    color-mix(in srgb, var(--muted) 30%, transparent), transparent),
    radial-gradient(farthest-side at 100% 50%,
                    color-mix(in srgb, var(--muted) 30%, transparent), transparent);
  background-position: 0 0, 100% 0, 0 0, 100% 0;
  background-repeat: no-repeat;
  background-size: 40px 100%, 40px 100%, 18px 100%, 18px 100%;
  /* Порядок обязателен: крышки объявлены ПЕРВЫМИ и потому лежат ПОВЕРХ
     теней. Поменяй местами — тень окажется сверху и не погаснет никогда. */
  background-attachment: local, local, scroll, scroll;
}

/* Рамка фокуса — ВНУТРЬ (offset −2px), а не наружу общим правилом:
   контейнер вплотную прилегает к рамке оболочки, и вынесенная наружу
   рамка легла бы на неё и на соседние блоки. Сам фокус контейнеру даёт
   слой поведения (cra-ui.js, разд. 11) и только при переполнении. */
[data-ui="v3"] .cra-tablescroll:focus-visible { outline-offset: -2px; }

[data-ui="v3"] .cra-toolbar {
  display: flex; gap: 10px; align-items: center; flex-wrap: wrap;
  padding: 12px 14px; border-block-end: 1px solid var(--line); background: var(--surface);
}
[data-ui="v3"] .cra-toolbar__grow { margin-inline-start: auto; }
[data-ui="v3"] .cra-seg { display: inline-flex; border: 1px solid var(--line); }
[data-ui="v3"] .cra-seg > button {
  border: none; background: transparent; color: var(--muted);
  font: 500 var(--vc-v3-fs-meta) var(--font-body);
  padding-inline: 12px; height: 34px; cursor: pointer;
  border-inline-end: 1px solid var(--line);
}
[data-ui="v3"] .cra-seg > button:last-child { border-inline-end: none; }
[data-ui="v3"] .cra-seg > button.cra-seg--on { background: var(--surface-2); color: var(--vc-v3-accent-ink); }
[data-ui="v3"] .cra-seg > button:focus-visible { outline: 2px solid var(--vc-v3-accent-dim); outline-offset: -2px; }

/* Панель массовых действий: появляется с выделения (ст. 5.10) */
[data-ui="v3"] .cra-bulk {
  display: none; gap: 12px; align-items: center; padding: 9px 14px;
  background: color-mix(in srgb, var(--accent) 9%, var(--surface));
  border-block-end: 1px solid var(--vc-v3-accent-dim);
  font-size: var(--vc-v3-fs-data);
}
[data-ui="v3"] .cra-bulk--on { display: flex; }
[data-ui="v3"] .cra-bulk b { color: var(--vc-v3-accent-ink); font-weight: 600; }
[data-ui="v3"] .cra-bulk button {
  border: 1px solid var(--line); background: transparent; color: var(--text);
  font: 600 var(--vc-v3-fs-meta) var(--font-body); padding: 6px 12px; cursor: pointer;
}
[data-ui="v3"] .cra-bulk button:hover { border-color: var(--vc-v3-accent-dim); }
/* «Снять выделение» — ТЕКСТ без рамки (макет: `border:none;color:--muted`).
   Носитель назван вместе с тегом, `.cra-bulk button.cra-bulk__end`: правило
   рамки выше задано как `.cra-bulk button` и было специфичнее одного класса,
   поэтому рамка оставалась видна, а `border-color: transparent` её только
   обесцвечивал. !important запрещён (ст. 9.3), и специфичность — законный
   способ: правило говорит о ТОЙ ЖЕ кнопке, что и общее, только уточняет её. */
[data-ui="v3"] .cra-bulk button.cra-bulk__end {
  margin-inline-start: auto; border: none; color: var(--muted); padding-inline: 0;
}
[data-ui="v3"] .cra-bulk button.cra-bulk__end:hover { border: none; color: var(--text); }

[data-ui="v3"] .cra-table { width: 100%; border-collapse: collapse; font-size: var(--vc-v3-fs-data); }
[data-ui="v3"] .cra-table th {
  /* 10.5px и разрядка .08em — значения макетов (документы v3.1, источники
     v2.3); было 11px/.1em, и заголовок спорил с данными. Высота строки
     заголовка оставлена по таблице v1.6: она общая мера, а не частная. */
  font: 600 10.5px var(--font-body); letter-spacing: .08em; text-transform: uppercase;
  color: var(--muted); text-align: start; padding-inline: 12px; height: 38px;
  border-block-end: 1px solid var(--line); white-space: nowrap; user-select: none;
  position: sticky; inset-block-start: 0; background: var(--surface); z-index: 1;
}
[data-ui="v3"] .cra-table th.cra-th--sortable { cursor: pointer; }
/* Колонка выделения — свойство компонента, а не экрана: галочка в любой
   таблице одна и та же, и её ширина не должна ездить от таблицы к таблице. */
[data-ui="v3"] .cra-table th.cra-th--select { width: 34px; }
/* Колонка действий — по тому же доводу, что и колонка выделения: значки
   в любой таблице одни и те же. Числом её ширину задать нельзя (значков
   бывает от одного до трёх), поэтому мера другая: колонка сжимается до
   содержимого и не переносит значки на вторую строку. width:1% — это и
   есть «по содержимому» у таблицы с автоматической раскладкой; в
   Crawler_table_v1.6 колонка действий стоит последней и ведёт себя так же. */
[data-ui="v3"] .cra-table th.cra-th--actions { width: 1%; white-space: nowrap; }
[data-ui="v3"] .cra-table td.cra-td--actions { white-space: nowrap; }

/* ── Ширины колонок: ЗАКРЫТЫЙ НАБОР ───────────────────────────────────
   Решение владельца от 31.08.2026, вариант «а». Модификаторов ЧЕТЫРЕ,
   плюс ширина колонки выделения выше — и всё. Набор расширяется ТОЛЬКО
   решением владельца: как только ширину можно назначить любым числом,
   одинаковые по смыслу колонки в двух разделах оказываются разной
   ширины, и таблицы перестают выглядеть одной таблицей. Заодно закрыт
   инлайн: ни одного style= в шаблонах (ст. 9.3), а класс — не «под
   конкретную таблицу» (ст. 9.1), а общая мера на весь слой.

   Значение «широкой» — 34%: ровно столько у колонки имени в
   согласованном Crawler_table_v1.4, и именно оно воспроизводит его
   пропорции. Узкая и средняя — та же шкала третями от неё.

   Колонка БЕЗ модификатора раскладывается по содержимому. Это не забытый
   случай, а основной: в макете так лежат пять колонок из семи, и числа с
   короткими заголовками сами занимают ровно столько, сколько им нужно. */
[data-ui="v3"] .cra-table th.cra-col--narrow { width: 10%; }
[data-ui="v3"] .cra-table th.cra-col--mid    { width: 20%; }
[data-ui="v3"] .cra-table th.cra-col--wide   { width: 34%; }
/* Тянущаяся забирает ВЕСЬ остаток, а не «очень много»: остальные колонки
   ужимаются до содержимого. Поэтому в одной таблице она может быть
   только одна — две такие поделят остаток пополам и обе окажутся уже,
   чем автор ожидал. */
[data-ui="v3"] .cra-table th.cra-col--grow   { width: 100%; }
[data-ui="v3"] .cra-table th.cra-th--num { text-align: end; }
/* Знак сортировки виден у КАЖДОЙ сортируемой колонки, а не только у
   отсортированной. `visibility: hidden` до нажатия означала, что
   сортируемость на неподвижном экране не читается вовсе: единственным
   признаком оставался курсор, а его не видно, пока не наведёшь. Аудит
   так и записал — «ни одного сортируемого заголовка», хотя разметка
   правильная и сортировка работает.
   В макетах знак стоит приглушённым и разгорается на активной колонке
   (`th.ths .ar{opacity:.25}` → `th.ths.sorted .ar{opacity:1}`, документы
   v3.1, источники v2.3). Приглушение сделано смесью с поверхностью, а не
   opacity: паспорт гасить прозрачностью запрещает, а шапка таблицы имеет
   свой фон — смесь даёт ровно тот же тон сплошным цветом.
   Место под знак было зарезервировано и раньше, строка не сдвинулась. */
[data-ui="v3"] .cra-table th .cra-arrow {
  font-size: var(--vc-v3-px-atom); margin-inline-start: 5px;
  color: color-mix(in srgb, var(--vc-v3-accent-ink) 25%, var(--surface));
}
[data-ui="v3"] .cra-table th.cra-th--sorted .cra-arrow { color: var(--vc-v3-accent-ink); }
/* У правых (числовых) колонок макет ставит знак ПЕРЕД подписью — тогда
   отбивка нужна с другой стороны, иначе знак липнет к слову. */
[data-ui="v3"] .cra-table th > .cra-arrow:first-child {
  margin-inline-start: 0; margin-inline-end: 5px;
}
/* Заголовок сортируемой колонки отзывается на наведение — тот же приём,
   что у строки: контрол обязан показывать, что он контрол. */
[data-ui="v3"] .cra-table th.cra-th--sortable:hover { color: var(--vc-v3-accent-ink); }
/* padding-block: 0 обязателен явно. Без него у ячейки остаётся браузерное
   умолчание td{padding:1px}, и строка с направляющей выходит 37px вместо
   --row-h 34: .cra-guide ровно 34px высотой, плюс два раза по пикселю.
   В эталоне вертикаль обнулена так же — таблица v1.6: td{padding:0 14px}. */
[data-ui="v3"] .cra-table td {
  padding-inline: 14px; padding-block: 0; height: var(--row-h);
  border-block-end: 1px solid color-mix(in srgb, var(--line) 60%, transparent);
}
/* Указатель — только у строки, которая ДЕЙСТВИТЕЛЬНО ведёт по адресу.
   Раньше «рука» стояла у всякой строки, а перехода не было ни одного:
   курсор обещал действие, которого нет. Признак перехода один и тот же
   и для вида, и для поведения — data-cra-href, его ставит row_start(href),
   читает cra-ui.js (разд. 10). */
[data-ui="v3"] .cra-row[data-cra-href] { cursor: pointer; }
[data-ui="v3"] .cra-row:hover td { background: color-mix(in srgb, var(--accent) 5%, transparent); }
[data-ui="v3"] .cra-row--sel td { background: color-mix(in srgb, var(--accent) 9%, transparent); }
/* Строка без собранных данных или выключенная: приглушается ЦВЕТОМ, а не
   прозрачностью (паспорт: прозрачностью не гасим). Штриховка сюда не
   идёт намеренно — она метит выключенный КОНТРОЛ, а строка данных не
   контрол: заштрихованная строка читалась бы как «нажать нельзя».
   ИМЯ ОДНО: `cra-row--muted`. Второе имя `cra-row--dim` встречается в
   разметке «Рынков» и правила не имеет вовсе — строка без данных там
   выглядит обычной. Псевдонима здесь не заводится: два класса под одно
   состояние — это две копии компонента, которые разойдутся при первой
   правке (ст. 9.1). Разметка приводится к `--muted`. */
[data-ui="v3"] .cra-row--muted td,
[data-ui="v3"] .cra-row--muted td a { color: var(--muted); }
/* Галочка выделения строки видна ВСЕГДА, а не по наведению.
   Правило «контролы у строк по наведению» (паспорт ст. 7) писалось про
   КНОПКИ ДЕЙСТВИЙ: ряд значков в каждой строке спорит с данными, а
   действие в покое и не нужно. Галочка — не действие, а СОСТОЯНИЕ
   строки: пока она спрятана, колонка выбора пуста, «выбрать всё» в
   шапке висит один и непонятно, к чему относится, а сам факт «строки
   можно отбирать» на неподвижном экране не читается вовсе.
   В согласованной таблице v1.6 и во всех макетах экранов `.ck` стоит в
   каждой строке с opacity 1. Поэтому здесь правило снято совсем: своего
   объявления у галочки в таблице больше нет, она рисуется общим видом
   галочки набора (разд. 3). Быстрые действия (.cra-quick, .cra-rowmenu,
   .cra-rate) по-прежнему появляются по наведению — они действия. */
/* Числа: моно, вправо, tabular-nums (ст. 3.5) */
[data-ui="v3"] .cra-num { font-family: var(--font-mono); font-size: var(--vc-v3-fs-mono); text-align: end; font-variant-numeric: tabular-nums; }
[data-ui="v3"] .cra-sub { color: var(--muted); font-size: var(--vc-v3-fs-mono); }
/* Число со смыслом: «новых» зелёным, «ошибок» красным (макет прогонов
   v1.9, `.num.g` и `.errn`). Меняется ТОЛЬКО цвет — шрифт, кегль и
   выключка остаются общими для всех чисел, иначе колонка перестанет
   сравниваться взглядом с соседней. Правило «цвет = отклонение» тут не
   нарушено: зелёное здесь — прирост, а не «всё хорошо». Заявка v4-01. */
[data-ui="v3"] .cra-num--pos { color: var(--vc-v3-accent-ink); }
[data-ui="v3"] .cra-num--err { color: var(--vc-v3-err-ink); }

/* Иерархия вендор → продукт → модуль: контрол свёртывания и направляющая */
/* min-inline-size:0 обязателен: без него flex-потомок не сжимается и
   многоточие в .cra-name не срабатывает — ячейка растит таблицу. */
/* block-size:100% у ячейки имени и у направляющей — вместо жёстких 34px.
   Направляющая ростом ровно в --row-h делала строку на пиксель выше
   самой строки: у ячейки box-sizing:border-box, её 34px включают нижнюю
   рамку, и содержимое в 34px в них не помещалось. Паспорт ст. 5 требует
   ОДНОЙ высоты строки на таблицу, поэтому мера здесь — высота ячейки, а
   не повторённое число: тогда строка с направляющей и строка без неё
   совпадают по построению, а не по совпадению значений. */
[data-ui="v3"] .cra-namecell { display: flex; align-items: center; gap: 0; min-inline-size: 0; block-size: 100%; }
[data-ui="v3"] .cra-tree-tg {
  flex: none; width: 22px; height: 22px; display: grid; place-content: center;
  color: var(--text); font-size: 14px; line-height: 1;
  border: 1px solid transparent; cursor: pointer;
}
[data-ui="v3"] .cra-tree-tg:hover { border-color: var(--vc-v3-accent-dim); color: var(--vc-v3-accent-ink); }
[data-ui="v3"] .cra-tree-tg--none { visibility: hidden; cursor: default; }
[data-ui="v3"] .cra-guide {
  flex: none; width: 18px; block-size: 100%;
  border-inline-start: 1px solid color-mix(in srgb, var(--line) 80%, transparent);
  margin-inline-start: 9px;
}
/* Переполнение: обрезка и полное значение в title (ст. 8.5, А14) */
[data-ui="v3"] .cra-name { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; min-inline-size: 0; }
/* ── ПЛОТНАЯ ТАБЛИЦА: у имени есть предел ширины (заявка З-1, DR-68) ──
   Обрезка выше сама по себе не работает НИКОГДА, и это не описка в
   правиле, а свойство раскладки. При white-space:nowrap минимальная
   ширина ячейки равна всей строке целиком: колонка не может стать у́же
   самого длинного имени, обрезать нечего, многоточие не появляется, а
   таблица растёт за окно. Побочное следствие — модификаторы ширины
   (.cra-col--narrow/mid/wide/grow) перестают влиять на что бы то ни было:
   доли раздаются от ширины, которой уже нет, все колонки лежат по
   min-content. Пока у имени нет предела, ширины назначать бесполезно.

   Предел дан МОДИФИКАТОРОМ, а не всем таблицам разом, намеренно.
   Общее правило поменяло бы вид уже ПРИНЯТЫХ экранов — 3.1 «Продукты»,
   3.3 «Карточка продукта», 4.1 «Документы», — а их приёмка проведена по
   кадрам: правка, которую никто не заказывал, обесценила бы её. Поэтому
   правило заводится здесь ОДИН РАЗ, а таблица, которой оно нужно,
   объявляет себя плотной: <table class="cra-table cra-table--tight">.

   16ch — замер заявителя при окне 1280: «Источники» 1280→1222 (перебора
   нет), таблица «Продукты» карточки вендора 1316→1243 при поле 1177.
   Единица ch, а не пиксели: предел меряется знаками имени, и при смене
   кегля данных он едет вместе с ним, а не остаётся от старого шрифта.
   Полное значение при обрезке остаётся в подсказке (ст. 8.5, А14) —
   это забота разметки ячейки, правило её не отменяет. */
[data-ui="v3"] .cra-table--tight .cra-name { max-inline-size: var(--vc-name-cap, 16ch); }
/* ── ВТОРАЯ СТУПЕНЬ ПРЕДЕЛА ИМЕНИ: 28 знаков (--name-wide) ─────────────
   16ch хватает там, где имя короткое и различается с первых знаков. На
   карточке вендора это не так: имена продуктов одного вендора начинаются
   с ЕГО ЖЕ названия, и обрезка по шестнадцатому знаку делала двадцать две
   строки подряд неотличимыми друг от друга — «Acme Shield E…» ×22. Список
   переставал отвечать на вопрос, ради которого его открыли.

   Ступень, а не новое умолчание: 16ch стоит на ПРИНЯТОМ экране 6.1
   «Источники», и поднятие общего предела изменило бы его кадр без заказа
   — тот же довод, по которому pack заведён модификатором.

   28 знаков — замер: при доле «средняя» (20 %) колонка имени на карточке
   вендора получает 267 px, из них на текст остаётся ~247 px, то есть
   около 30 знаков кегля данных. Предел взят чуть ниже полученного места,
   чтобы имя обрезалось многоточием ВНУТРИ своей колонки, а не распирало
   её и не отбирало ширину у соседок (см. оговорку про nowrap выше).
   Полное имя остаётся в подсказке ячейки (ст. 8.5, А14). */
[data-ui="v3"] .cra-table--name-wide { --vc-name-cap: 28ch; }
/* ── ТРЕТЬЯ СТУПЕНЬ: 48 знаков (--name-long) ──────────────────────────
   Предел имени — не «сколько не жалко», а СКОЛЬКО ВЛЕЗАЕТ В КОЛОНКУ:
   имя обязано обрезаться внутри своей колонки, а не распирать её. Место
   же зависит от того, во весь ли экран лежит таблица.
   Замер (окно 1440): у таблицы В ПОЛЕ КАРТОЧКИ (3.2) колонка имени при
   доле «средняя» — 267 px, около 28 знаков; у таблицы ВО ВЕСЬ ЭКРАН
   (3.1 «Продукты», 5.1 «Прогоны») колонка имени при доле «широкая» —
   468 px, около 50 знаков. Один предел на оба случая либо режет там, где
   место есть (28 знаков в колонке на 50 — двадцать две строки
   «Acme Security Acme Shield …» подряд), либо не держит там, где места
   нет.
   Ступени закрыты и названы, как ширины колонок: 16 · 28 · 48. Четвёртой
   не заводится — она означала бы, что предел подгоняют под конкретную
   таблицу, а это ровно то, что запрещает ст. 9.1. */
[data-ui="v3"] .cra-table--name-long { --vc-name-cap: 48ch; }

/* ── УПАКОВАННАЯ ТАБЛИЦА: горизонтальные отступы ячеек 8/10 (DR-68) ────
   Решение владельца 05.09.2026 по остатку DR-68: «сделать что-то
   компактнее, чтобы влезло», не вырезая содержимого — колонку «Страниц»
   не убирать, имя продукта не сокращать. Резерв взят там, где владелец
   назвал его первым: в отступах ячеек.

   Почему именно они, а не ширины колонок. Замер 05.09.2026 (окно 1280,
   таблица «Продукты» карточки вендора): девять колонок из двенадцати
   лежат по СВОЕМУ ЗАГОЛОВКУ — «Документов» 109 px при тексте 85,
   «Аналитика» 100 при 76, «Обновлено» 99 при 75. Данных в них прочерк,
   сжимать в них нечего вовсе: всю лишнюю ширину держит воздух по краям
   ячейки. 12 px у заголовка и 14 у данных на двенадцати колонках дают
   336 px одних отступов при поле 1177.

   Значения 8 и 10, а не «поменьше»: шаг между заголовком и данными
   сохранён (у заголовка на 2 px теснее, как в общем правиле выше), и
   отступ остаётся больше половины прежнего — колонки не слипаются.
   Экономия ровно 8 px на колонку, на двенадцати — 96 px при недостаче 67.

   МОДИФИКАТОР, а не добавка к --tight, и тем более не общее правило.
   Плотную таблицу объявляет и экран 6.1 «Источники», а он ПРИНЯТ по
   кадрам 05.09.2026: там колонка имени тянущаяся, и сэкономленные
   пиксели ушли бы ей — общая ширина осталась бы прежней, а вид кадра
   изменился бы без заказа. Тот же довод, что у --tight и scroll_hint:
   правило заводится в ките один раз, а берёт его та таблица, которой оно
   нужно, — tbl.table(pack=true).

   Цена названа: у макета (Crawler_vendor-card_v2.1, `td{padding:9px 14px}`)
   отступ 14 px, здесь 10 — таблица плотнее макетной. Это осознанная
   плата за отказ и от прокрутки, и от резки содержимого; записано в
   DR-68. Вертикали правило не касается: строка остаётся --row-h 34 px. */
[data-ui="v3"] .cra-table--pack th { padding-inline: 8px; }
[data-ui="v3"] .cra-table--pack td { padding-inline: 10px; }
[data-ui="v3"] .cra-row--lv0 .cra-name { font-weight: 600; }
[data-ui="v3"] .cra-row--lv1 .cra-name { font-weight: 400; }
[data-ui="v3"] .cra-row--lv2 .cra-name { font-weight: 400; color: color-mix(in srgb, var(--text) 82%, var(--muted)); }
[data-ui="v3"] a.cra-extmark { margin-inline-start: 7px; color: var(--muted); text-decoration: none; font-size: var(--vc-v3-fs-label); }
[data-ui="v3"] a.cra-extmark:hover { color: var(--vc-v3-accent-ink); }

[data-ui="v3"] .cra-table tfoot td { height: 40px; color: var(--muted); font: var(--vc-v3-fs-meta) var(--font-mono); border-block-end: none; }
/* Три подвала под три задачи (ст. 5.20) */
[data-ui="v3"] .cra-foot-more,
[data-ui="v3"] .cra-foot-all,
[data-ui="v3"] .cra-foot-card { font: var(--vc-v3-fs-meta) var(--font-mono); color: var(--muted); padding: 12px 14px; }
/* Подвал-итог выключен по НАЧАЛУ строки, как в эталоне (таблица v1.6,
   tfoot; макет Crawler_products_v3.0) — прижатый вправо итог отрывался
   от столбца, о котором он говорит. Носитель остался прежним, div под
   таблицей: три подвала под три задачи (ст. 5.20) — принятое решение
   набора, и tfoot им не заменяется. */
[data-ui="v3"] .cra-foot-all { text-align: start; display: flex; align-items: center; gap: var(--vc-v3-gap-item); flex-wrap: wrap; }
/* Второй конец строки подвала: слева счёт, справа приписка («сортировка:
   клик по заголовку»). Распорка — тот же приём, что у тулбара и строки
   списка, а не второй носитель. */
[data-ui="v3"] .cra-foot-all__end { margin-inline-start: auto; }

/* Пустота — приглашение, а не тупик (ст. 8.6) */
[data-ui="v3"] .cra-empty { padding: 56px 20px; text-align: center; }
/* Правило растровой пиктограммы (.cra-empty__img, PNG из Crawler_table_v1.4)
   снято 31.08.2026 решением владельца: паука рисует векторный
   .cra-empty__ghost по листу Crawler_states_v1.3 (разд. 15). Два способа
   нарисовать одну пиктограмму разошлись бы при первой же правке. */
[data-ui="v3"] .cra-empty__text { color: var(--muted); font-size: var(--vc-v3-fs-data); margin: 12px 0 16px; }
[data-ui="v3"] .cra-tableshell--empty .cra-table { display: none; }

/* ══ 9. КАРТОЧКА-НОСИТЕЛЬ — Crawler_card_v1.5 ═════════════════════════
   Панель выезжает справа, левый борт 2px --accent, затемнение ~55%
   (паспорт ст. 7). Esc и клик по затемнению закрывают. */
/* Сцена держит высоту сама: панель внутри позиционирована абсолютно и в
   поток не попадает, поэтому без min-block-size сцена схлопывается до
   высоты списка под ней, а выехавшая карточка обрезается. */
[data-ui="v3"] .cra-stage {
  position: relative; overflow: hidden; min-block-size: 560px;
  border: 1px solid var(--line); background: var(--bg);
}
[data-ui="v3"] .cra-stage__scrim {
  position: absolute; inset: 0; background: var(--vc-v3-scrim-drawer);
  opacity: 0; pointer-events: none; transition: opacity .18s; z-index: 4;
}
[data-ui="v3"] .cra-stage--open .cra-stage__scrim { opacity: 1; pointer-events: auto; }
[data-ui="v3"] .cra-card {
  position: absolute; inset-block: 0; inset-inline-end: -93%;
  /* Подложка карточки объявлена и переменной — см. --vc-v3-ground у
     .cra-tableshell: таблица внутри карточки лежит НЕ на --surface. */
  width: 92%; background: var(--bg); --vc-v3-ground: var(--bg);
  border-inline-start: 2px solid var(--accent);
  z-index: 5; transition: inset-inline-end .22s;
  display: flex; flex-direction: column;
}
[data-ui="v3"] .cra-stage--open .cra-card { inset-inline-end: 0; }
[data-ui="v3"] .cra-card__top { position: relative; background: var(--surface); border-block-end: 1px solid var(--line); padding: 14px 22px 0; flex: none; }
[data-ui="v3"] .cra-crumbrow { display: flex; align-items: center; gap: 12px; }
[data-ui="v3"] .cra-crumb { font: var(--vc-v3-fs-label) var(--font-mono); color: var(--muted); }
[data-ui="v3"] .cra-addr { margin-inline-start: auto; font: var(--vc-v3-fs-label) var(--font-mono); color: var(--muted); }
[data-ui="v3"] .cra-addr b { color: var(--vc-v3-accent-ink); font-weight: 500; }
[data-ui="v3"] .cra-card__title { display: flex; align-items: center; gap: 12px; margin-block-start: var(--vc-v3-gap-inner); flex-wrap: wrap; }
[data-ui="v3"] .cra-card__title h3 { font-size: 22px; margin: 0; min-inline-size: 0; overflow-wrap: anywhere; }
/* Тип объекта — плашка в стиле чипов рынков: моно, тонкая */
[data-ui="v3"] .cra-type {
  display: inline-block; border: 1px solid color-mix(in srgb, var(--line) 70%, transparent);
  padding: 2px 9px; font: 400 10.5px var(--font-mono); letter-spacing: .08em;
  color: var(--muted); text-transform: uppercase; margin: 2px 0 var(--vc-v3-gap-inner);
}
/* Дата: относительная с точной в подсказке (ст. 3.6); подсказка держится
   три секунды, чтобы по ссылке внутри неё можно было дойти мышью. */
[data-ui="v3"] .cra-date { position: relative; align-self: center; margin-block-start: 5px; font: 400 var(--vc-v3-fs-label) var(--font-mono); color: var(--muted); cursor: default; border-block-end: 1px dotted var(--line); }
[data-ui="v3"] .cra-date__tip {
  position: absolute; inset-block-start: calc(100% + 6px); inset-inline-start: 0;
  background: var(--surface); border: 1px solid var(--line);
  padding: 7px 11px; font: var(--vc-v3-fs-meta) var(--font-mono); color: var(--muted);
  white-space: nowrap; display: none; z-index: 9;
}
[data-ui="v3"] .cra-date__tip a { color: var(--vc-v3-accent-ink); }
/* Показывается по наведению и по фокусу — как всякая подсказка набора
   (.cra-tip у значка шапки). Класс cra-date--tipon оставлен: его ставит
   поведение слоя, когда подсказку надо задержать после ухода мыши.
   До этой правки класс не ставил НИКТО, и подсказка «точное значение»
   была недостижима ни мышью, ни клавиатурой — то есть причина, по
   которой дата пуста, лежала в разметке и не читалась человеком. */
[data-ui="v3"] .cra-date:hover .cra-date__tip,
[data-ui="v3"] .cra-date:focus-visible .cra-date__tip,
[data-ui="v3"] .cra-date--tipon .cra-date__tip { display: block; }
/* Строка-мета под именем объекта: короткие значения в ряд, разделённые
   средней точкой. Отдельным рядом, а не в строке с именем: в строке
   имени живут имя, статус, дата и действия — пятый жилец превращает её
   в перечисление. Точка ставится псевдоэлементом, чтобы разделитель не
   попадал в разметку и не копировался вместе со значением. */
[data-ui="v3"] .cra-metarow {
  display: flex; gap: var(--vc-v3-gap-inner); flex-wrap: wrap;
  margin-block-start: 6px; font-size: var(--vc-v3-fs-meta); color: var(--muted);
}
[data-ui="v3"] .cra-metarow__i + .cra-metarow__i::before {
  content: "·"; margin-inline-end: var(--vc-v3-gap-inner); color: var(--line);
}
[data-ui="v3"] .cra-chiprow { display: flex; gap: var(--vc-v3-gap-inner); margin-block: 10px 6px; flex-wrap: wrap; }
[data-ui="v3"] a.cra-mkchip {
  display: inline-block; border: 1px solid color-mix(in srgb, var(--line) 70%, transparent);
  padding: 2px 9px; font: 400 11.5px var(--font-mono); color: var(--muted); text-decoration: none;
}
[data-ui="v3"] a.cra-mkchip:hover { border-color: var(--vc-v3-accent-dim); color: var(--vc-v3-accent-ink); }
/* Название рынка бывает длинным: чип обрезается многоточием, полное
   значение — в подсказке (ст. 8.5), но шапку карточки он не разрывает. */
[data-ui="v3"] a.cra-mkchip { max-inline-size: 24ch; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* В ЯЧЕЙКЕ тот же чип у́же (заявка З-2). Предел 24ch писался под шапку
   карточки, где чипов один-два и место есть. В колонке «Рынок» он ставит
   чип в 145px против 131 в макете и отбирает ширину у соседок: колонка
   раскладывается по содержимому, и лишние 14px берутся не из воздуха.
   Меняется ТОЛЬКО предел и только внутри таблицы — вид чипа общий, и
   двух его версий в наборе не заводится. Замер заявителя: 12ch → 111px. */
/* Предел здесь БЕЗ отступов самого чипа. При box-sizing: border-box в
   12ch укладывалась и рамка, и padding 9px с двух сторон, и на текст
   оставалось около девяти знаков: «API Security» — двенадцать — резалось
   многоточием в КАЖДОЙ строке таблицы, и колонка рынка переставала
   называть рынок. Замер (окно 1440, карточка вендора): чип 81 px при
   нужных 94–101. Двенадцать знаков названия плюс собственные 20 px
   отступов и рамки дают 103 px — имя рынка целиком, и колонка не шире
   макетной. Названия длиннее по-прежнему обрезаются многоточием с полным
   значением в подсказке. */
[data-ui="v3"] .cra-table a.cra-mkchip { max-inline-size: calc(12ch + 20px); }
[data-ui="v3"] .cra-card__act { margin-inline-start: auto; display: flex; gap: 10px; }
[data-ui="v3"] .cra-tabs { display: flex; gap: 2px; margin-block-start: 18px; flex-wrap: wrap; }
[data-ui="v3"] .cra-tabs a {
  padding: 9px 14px; color: var(--muted); text-decoration: none;
  font: 500 13px var(--font-body); border-block-end: 2px solid transparent; position: relative;
}
[data-ui="v3"] .cra-tabs a:hover { color: var(--text); }
[data-ui="v3"] .cra-tabs a.cra-tabs--on { color: var(--text); border-block-end-color: var(--accent); }
[data-ui="v3"] .cra-tabs a .cra-tip {
  position: absolute; inset-block-start: calc(100% + 4px); inset-inline-start: 0;
  background: var(--surface); border: 1px solid var(--line); padding: 7px 11px;
  font-size: var(--vc-v3-fs-meta); color: var(--text); white-space: nowrap; display: none; z-index: 9;
}
[data-ui="v3"] .cra-tabs a:hover .cra-tip { display: block; }
[data-ui="v3"] .cra-card__body { padding: 20px 22px; overflow: auto; flex: 1; }
[data-ui="v3"] .cra-ph { border: 1px dashed var(--line); color: var(--muted); padding: 24px; text-align: center; font-size: var(--vc-v3-fs-mono); }
[data-ui="v3"] .cra-subviews { display: flex; gap: var(--vc-v3-gap-inner); margin-block-end: 12px; }
[data-ui="v3"] .cra-subviews span { border: 1px solid var(--line); padding: 4px 11px; font: 500 var(--vc-v3-fs-meta) var(--font-body); color: var(--muted); cursor: pointer; }
[data-ui="v3"] .cra-subviews span.cra-subviews--on { border-color: var(--vc-v3-accent-dim); color: var(--vc-v3-accent-ink); }
/* ── РАЗДЕЛ: ТИХАЯ МЕТКА И БЛОК-КОРОБКА ──────────────────────────────
   Раздел внутри страницы или карточки бывает в наборе ДВУХ видов, и оба
   взяты из согласованных макетов:

   1) МЕТКА над содержимым — `.blk h3{font:600 11px;letter-spacing:.08em;
      text-transform:uppercase;color:var(--muted);margin:0 0 8px}`
      (карточка рынка v2.8, карточка источника v1.2, карточка прогона
      v5.7, «Аккаунт» v1.0, «Настройки» v1.3). Это служебная надпись, а
      не заголовок: она называет блок и уступает имени объекта.
      До сих пор раздел рисовался `.cra-h3` — 15px обычным регистром
      цветом текста, и на «Аккаунте» получалось четыре белых подзаголовка
      вместо четырёх тихих меток.
      `.cra-h3` при этом НЕ меняется: он остаётся заголовком третьего
      уровня ст. 3.4 и в макетах живёт своей жизнью — шаги формы «Новый
      сбор» это ровно `600 15px` с нижней линией (v1.7, `section h2`).

   2) КОРОБКА — `.card{background:var(--surface);border:1px solid
      var(--line)}`, а метка стоит ВНУТРИ неё шапкой с нижней границей и
      полем 12/16 (карточка документа v3.4, карточка прогона v5.7),
      либо `.panel` дашборда v1.9 с полем 11/14. В той же шапке справа —
      ссылка действия («все прогоны →», `.hact`).
      Своего носителя под это в слое не было вовсе: блок был голым
      заголовком на фоне страницы, а ссылка действия уезжала под таблицу.
      Прошлой волной по заявке e5-04 в этом было отказано с доводом
      «`.cra-section` умеет то же» — довод неверен: у секции нет ни
      рамки, ни фона, ни шапки с границей, ни места под ссылку.

   Почему не новый компонент, а модификатор: носитель тот же раздел
   карточки, различие в одном — рисуется он коробкой или нет. Два имени
   под одну сущность разошлись бы при первой правке (ст. 9.1).

   Кегль шапки-коробки взят от метки (11px капс), а не 13px обычным
   регистром, как в `.panel h2` дашборда: заголовок одного уровня не
   может выглядеть по-разному от экрана к экрану, а ст. 3.4 знает три
   уровня, не четыре. Расхождение с дашбордом записано: DR-73. */
[data-ui="v3"] .cra-section { margin-block: var(--vc-v3-gap-group); }
[data-ui="v3"] .cra-section:first-child { margin-block-start: 0; }
[data-ui="v3"] .cra-section__h {
  display: flex; align-items: center; gap: 10px; flex-wrap: wrap;
  font: 600 var(--vc-v3-fs-label) var(--font-body);
  letter-spacing: .08em; text-transform: uppercase; color: var(--muted);
  margin: 0 0 var(--vc-v3-gap-inner);
}
/* Приписка к метке («последние 20», «из 4 рынков») — величина, а не
   продолжение метки: моно и без капса, иначе читается одной фразой. */
[data-ui="v3"] .cra-section__sub {
  font: 400 var(--vc-v3-fs-label) var(--font-mono); color: var(--muted);
  text-transform: none; letter-spacing: normal;
}
/* Ссылка действия у правого края шапки — `.hact` макетов. Регистр и
   разрядка сбрасываются: капс метки на фразу «все прогоны →» не идёт. */
[data-ui="v3"] .cra-section__act {
  margin-inline-start: auto; font: 400 var(--vc-v3-fs-meta) var(--font-body);
  text-transform: none; letter-spacing: normal;
  color: var(--vc-v3-accent-ink); text-decoration: none;
}
[data-ui="v3"] a.cra-section__act:hover { border-block-end: 1px solid var(--vc-v3-accent-ink); }
/* ── ВТОРОЙ КОНЕЦ ШАПКИ РАЗДЕЛА (заявка В1-07) ───────────────────────
   В шапке панели бывает не два конца, а три: метка слева, ссылка
   действия справа и ПЕРЕД НЕЙ носитель состояния — `.okchip` дашборда
   v1.9 («Состояние системы · всё в норме · диагностика →», стр. 196).
   Приём тот же, что у подвала-итога (.cra-foot-all__end) и строки
   списка: распорка автоотступом, а не второй носитель.

   Второе правило обязательно и не является перестраховкой. У
   .cra-section__act свой margin-inline-start:auto, а флекс делит
   свободное место между ВСЕМИ автоотступами поровну: с двумя такими
   элементами чип встал бы не у конца шапки, а посередине пустоты. Как
   только перед действием появился элемент с этим классом, распорка у
   действия снимается — конец шапки один, и держит его первый из них.

   Регистр и разрядка сбрасываются по тому же доводу, что у приписки и у
   ссылки действия: капс с разрядкой .08em — свойство МЕТКИ, а у конца
   шапки стоит значение («всё в норме»), и вытянутый капс читает его
   второй меткой. Без сброса чип наследовал их от h3 и выходил
   «В С Ё   В   Н О Р М Е» — в макете он строчными. */
[data-ui="v3"] .cra-section__end {
  margin-inline-start: auto; text-transform: none; letter-spacing: normal;
}
[data-ui="v3"] .cra-section__h .cra-section__end ~ .cra-section__act { margin-inline-start: 0; }
/* Коробка бывает ДВУХ видов, и оба из макетов:
     · метка ВНУТРИ шапкой с нижней границей — карточка документа v3.4,
       карточка прогона v5.7, дашборд v1.9 (`.cra-section--panel`);
     · метка НАД коробкой, а в коробке только строки — «Аккаунт» v1.0 и
       «Настройки» v1.3 (`.sblk h3` + `.scb`, макрос section(head_out)).
   Разное здесь одно — где стоит метка, поэтому вид самой коробки задан
   обоим носителям одним объявлением, а не переписан второй раз. */
[data-ui="v3"] .cra-section--panel,
[data-ui="v3"] .cra-section__box {
  background: var(--surface); border: 1px solid var(--line);
}
[data-ui="v3"] .cra-section--panel > .cra-section__h {
  margin: 0; padding: 11px 14px; border-block-end: 1px solid var(--line);
}
[data-ui="v3"] .cra-section__body { padding: 12px 14px; }
/* Строка «подпись — значение» внутри раздела. Значения из макетов:
   `.frow{padding:12px 16px;border-bottom:…55%}` и `.frow:last-child{
   border-bottom:0}` («Аккаунт» v1.0), `.meta .row{padding:7px 0;font:
   400 12.5px}` (карточка документа v3.4).
   Своих боковых полей у строки не было вовсе, и отступ ей приходилось
   давать полями КОРОБКИ — тогда разделитель не доходил до её краёв, как
   в макете, а вдвигался внутрь. Поля перенесены в саму строку: коробка
   зовётся без внутренних полей (pad=false), и линия идёт от края до края.
   Кегль — 13.5px шкалы данных вместо унаследованных 15px страницы: в
   макетах строка набрана 12.5-13px, и на 15px блок «Свойства» выходил
   вдвое выше макета.
   Граница у последней строки снята: над рамкой коробки она давала
   двойную черту в двенадцати пикселях одна от другой. */
[data-ui="v3"] .cra-listrow {
  display: flex; gap: var(--vc-v3-gap-item); align-items: center;
  padding: 7px 16px; font-size: 12.5px;
  border-block-end: 1px solid color-mix(in srgb, var(--line) 45%, transparent);
}
[data-ui="v3"] .cra-listrow:last-child { border-block-end: none; }
/* Распорка строки списка — тот же приём, что у тулбара (.cra-toolbar__grow,
   разд. 8): всё, что после неё, уезжает к концу строки. Заведена 03.09.2026
   по заявке e4-02.
   Отдельным элементом, а НЕ правилом «первый ребёнок тянется»: у уже
   работающих строк (карточка документа, doc_view.html) первый ребёнок —
   подпись фиксированной ширины, и растягивание сдвинуло бы все значения
   в правый край. Распорку ставит тот, кому она нужна; остальные строки
   не меняются вовсе. */
[data-ui="v3"] .cra-listrow__grow { margin-inline-start: auto; }

/* ══ 10. МОДАЛКИ — Crawler_modal_v1.1 ════════════════════════════════
   Модалка = только операция; заголовок — глагол; необратимое красное;
   Esc и клик по фону = «Отмена». Нативные confirm/prompt запрещены. */
[data-ui="v3"] .cra-modalscrim { position: fixed; inset: 0; background: var(--vc-v3-scrim-modal); display: none; z-index: 50; }
[data-ui="v3"] .cra-modalscrim--open { display: grid; place-items: center; }
[data-ui="v3"] .cra-modal { width: 440px; max-width: 92vw; background: var(--surface); border: 1px solid var(--line); border-block-start: 2px solid var(--accent); }
[data-ui="v3"] .cra-modal--danger { border-block-start-color: var(--err); }
[data-ui="v3"] .cra-modal__head { display: flex; align-items: center; padding: 16px 20px 0; }
[data-ui="v3"] .cra-modal__head h3 { font-size: 17px; margin: 0; }
[data-ui="v3"] .cra-modal__close {
  margin-inline-start: auto; width: 28px; height: 28px;
  border: 1px solid transparent; background: transparent; color: var(--muted); cursor: pointer;
}
[data-ui="v3"] .cra-modal__close:hover { border-color: var(--line); color: var(--text); }
[data-ui="v3"] .cra-modal__body { padding: 12px 20px 4px; font-size: var(--vc-v3-fs-data); }
[data-ui="v3"] .cra-modal__cons { color: var(--muted); font-size: var(--vc-v3-fs-mono); margin-block-start: var(--vc-v3-gap-inner); }
[data-ui="v3"] .cra-modal__foot { display: flex; gap: 10px; justify-content: flex-end; padding: 16px 20px; }

/* ══ 11. ТОСТЫ — Crawler_modal_v1.1 ══════════════════════════════════
   Успех живёт 5 секунд, ошибка висит до закрытия и всегда предлагает
   «Повторить» (ст. 4.12); стек — правый нижний угол, новые сверху. */
/* v7.25.0, П5: стек тостов НЕ ПЕРЕХВАТЫВАЕТ указатель. Тосты всплывают в
   правом нижнем углу — там же, где главная кнопка подвала панели («Запустить
   сбор»). Приёмка 10.09.2026: тост вылез точно под курсором, поймал наведение,
   таймер снялся, и кнопка была закрыта, пока мышь не увели. Теперь сквозь тело
   тоста клик проходит, а нажимаются только его собственные кнопки. */
[data-ui="v3"] .cra-toasts {
  position: fixed; inset-inline-end: 18px; inset-block-end: 18px;
  display: flex; flex-direction: column; gap: 10px; z-index: 60;
  width: 360px; max-width: 92vw;
  pointer-events: none;
}
[data-ui="v3"] .cra-toast {
  background: var(--surface); border: 1px solid var(--line);
  border-inline-start: 2px solid var(--accent); padding: 12px 14px;
  font-size: var(--vc-v3-fs-data); display: flex; gap: 12px; align-items: flex-start;
  animation: cra-toast-in .18s;
  pointer-events: none;
}
[data-ui="v3"] .cra-toast__retry,
[data-ui="v3"] .cra-toast__close { pointer-events: auto; }
[data-ui="v3"] .cra-toast--err { border-inline-start-color: var(--err); }
[data-ui="v3"] .cra-toast__text b { display: block; font-size: var(--vc-v3-fs-data); }
[data-ui="v3"] .cra-toast__text span { color: var(--muted); font-size: var(--vc-v3-fs-mono); }
[data-ui="v3"] .cra-toast__act { margin-inline-start: auto; display: flex; gap: var(--vc-v3-gap-inner); align-items: center; }
[data-ui="v3"] .cra-toast__retry {
  border: 1px solid var(--line); background: transparent; color: var(--vc-v3-accent-ink);
  font: 600 var(--vc-v3-fs-meta) var(--font-body); padding: 5px 10px; cursor: pointer;
}
[data-ui="v3"] .cra-toast--err .cra-toast__retry { color: var(--vc-v3-err-ink); border-color: var(--err); }
[data-ui="v3"] .cra-toast__close { border: none; background: transparent; color: var(--muted); font-size: 14px; padding: 2px 4px; cursor: pointer; }

/* ══ 12. ДОЛГАЯ ОПЕРАЦИЯ — Crawler_modal_v1.1 ════════════════════════
   Запустил — модалка закрылась, осталась плашка с прогрессом и
   переходом к прогону; интерфейс не блокируется (ст. 4.14). */
[data-ui="v3"] .cra-longop {
  background: var(--surface); border: 1px solid var(--line);
  border-inline-start: 2px solid var(--warn); padding: 12px 14px; font-size: var(--vc-v3-fs-data);
}

/* ══ 13. ШАПКА — CRA_header-proto_v1.1 ═══════════════════════════════
   Разделы слева, глобальная зона справа (ст. 4.2); активный раздел —
   подчёркивание 2px изнутри плюс лёгкая заливка (паспорт ст. 7);
   выпадашки по клику, не по наведению. Высота не фиксирована: меню
   переросло строку однажды и накрыло соседний контрол. */
[data-ui="v3"] .cra-topbar {
  display: flex; align-items: center; gap: var(--vc-v3-gap-group);
  background: var(--surface); border-block-end: 1px solid var(--line);
  padding-inline: 18px; min-height: 54px; position: relative;
}
/* Марка — ссылка на дашборд, и правила у неё не было вовсе: <a> вокруг
   двух картинок оставался строчным, получал подчёркивание и лишний
   зазор под базовой линией. Класс шаблон ставил, кит его не знал. */
[data-ui="v3"] .cra-topbar__home { display: flex; align-items: center; text-decoration: none; }
[data-ui="v3"] .cra-topbar__logo { height: 24px; image-rendering: pixelated; display: block; }
[data-ui="v3"][data-theme="light"] .cra-logo--dark { display: none; }
[data-ui="v3"]:not([data-theme="light"]) .cra-logo--light { display: none; }
[data-ui="v3"] .cra-nav { display: flex; gap: 2px; align-self: stretch; }
[data-ui="v3"] .cra-nav__slot { position: relative; display: flex; align-self: stretch; }
/* Ни логотип, ни разделы, ни глобальная зона НЕ сжимаются: строка шапки
   не резиновая, и «влезло или нет» решается переполнением, а не тем, что
   флекс молча ужмёт соседа до нечитаемого. Это же делает замер честным —
   getBoundingClientRect каждого узла возвращает его НАТУРАЛЬНУЮ ширину
   даже тогда, когда сумма уже не помещается в окно
   (app/ui/static/cra-navoverflow.js). */
[data-ui="v3"] .cra-topbar__home,
[data-ui="v3"] .cra-nav,
[data-ui="v3"] .cra-glob,
[data-ui="v3"] .cra-nav__slot { flex: 0 0 auto; }
/* Раздел, уехавший в «Ещё», прячется атрибутом hidden — но у слота
   display:flex, а он атрибут перебивает: без этого правила «спрятанный»
   раздел остаётся на экране. Правило на display:none, а не на видимость:
   узел обязан выйти и из потока, и из обхода клавиатурой — иначе таб
   ходит по разделам, которых не видно. */
[data-ui="v3"] .cra-nav__slot[hidden],
[data-ui="v3"] .cra-nav__more[hidden] { display: none; }
[data-ui="v3"] .cra-nav__link {
  display: flex; align-items: center; gap: 6px; padding-inline: 15px;
  color: var(--muted); text-decoration: none; font: 500 var(--vc-v3-fs-data) var(--font-body);
  border-block-end: 2px solid transparent;
  /* Подпись раздела не переносится: «Продукты и вендоры» на второй строке
     поднимает шапку вдвое, а высота шапки не фиксирована (см. выше). */
  white-space: nowrap;
}
[data-ui="v3"] .cra-nav__link:hover { color: var(--text); }
[data-ui="v3"] .cra-nav__link--on {
  color: var(--text); border-block-end-color: var(--accent);
  background: color-mix(in srgb, var(--accent) 6%, transparent);
}
[data-ui="v3"] .cra-nav__link:focus-visible { outline: 2px solid var(--vc-v3-accent-dim); outline-offset: -2px; }
[data-ui="v3"] .cra-caret { font-size: var(--vc-v3-px-atom); color: var(--muted); }

/* ── «Ещё ▾»: переполнение разделов ──────────────────────────────────
   Компонент взят готовый — выпадашка тулбара .cra-dd (разд. 23), чей
   канонический образец в ките так и называется «Ещё ▾». Здесь ей дан
   ВИД РАЗДЕЛА: кнопка тулбара носит рамку и заливку surface-2, а в
   шапке рядом с разделами это читалось бы как чужой контрол. Меняется
   только внешность и место — поведение (клик, Esc, клик мимо,
   aria-expanded, автоповорот панели у края) остаётся общим, и второй
   копии его в скрипте нет.

   Селекторы намеренно длиннее нужного (.cra-topbar .cra-nav__more …):
   правила .cra-dd лежат НИЖЕ по файлу, и при равном весе побеждали бы
   они. Поднимать вес !important-ом нельзя (ст. 9.2), поэтому вес
   поднят местом в дереве — оно и так истинно: это шапка. */
[data-ui="v3"] .cra-nav__more { align-self: stretch; }
[data-ui="v3"] .cra-topbar .cra-nav__more .cra-dd__btn {
  height: auto; align-self: stretch; gap: 6px; padding-inline: 15px;
  border: none; border-block-end: 2px solid transparent; background: transparent;
  color: var(--muted); font: 500 var(--vc-v3-fs-data) var(--font-body);
  white-space: nowrap;
}
[data-ui="v3"] .cra-topbar .cra-nav__more .cra-dd__btn:hover { color: var(--text); }
[data-ui="v3"] .cra-topbar .cra-nav__more .cra-dd__btn:focus-visible {
  outline: 2px solid var(--vc-v3-accent-dim); outline-offset: -2px;
}
/* Активный раздел, уехавший в переполнение, не теряется: подчёркивание
   и заливка переезжают на саму кнопку. Иначе на узком окне ни один
   раздел не подсвечен, и по шапке не понять, где находишься (ст. 4.3). */
[data-ui="v3"] .cra-topbar .cra-nav__more--on .cra-dd__btn {
  color: var(--text); border-block-end-color: var(--accent);
  background: color-mix(in srgb, var(--accent) 6%, transparent);
}
/* Панель — та же .cra-menu, но с размерами МЕНЮ ШАПКИ, а не тулбара:
   раскрывается вплотную к строке шапки и шириной 210px, как соседние
   выпадашки разделов. Иначе одно меню шапки висело бы на 4px ниже
   других и было бы на 28px шире. */
[data-ui="v3"] .cra-topbar .cra-nav__more .cra-menu {
  inset-block-start: 100%; padding-block: 6px; padding-inline: 0;
  min-width: 210px; z-index: 8;
}

[data-ui="v3"] .cra-menu {
  position: absolute; inset-inline-start: 0; inset-block-start: 100%;
  min-width: 210px; background: var(--surface); border: 1px solid var(--line);
  box-shadow: var(--vc-v3-menu-shadow); display: none; z-index: 8; padding-block: 6px;
}
[data-ui="v3"] .cra-nav__slot--open .cra-menu { display: block; }
/* Панель, не влезающая в окно, раскрывается в другую сторону (DR-81).

   Панель шире своей кнопки и по умолчанию растёт от НАЧАЛЬНОГО края
   хозяина. У кнопки, стоящей у правого края («Экспорт отбора» на 4.1 при
   окне 1280), она уезжает за окно, и правая часть пунктов не видна.
   Ручной модификатор .cra-dd--end для этого уже был, но он требует, чтобы
   разметка ЗНАЛА, где кнопка окажется, — а место кнопки зависит от ширины
   окна, набора колонок и длины подписей. Знать это может только браузер.

   Поэтому правило заведено ОДИН РАЗ и здесь, а класс проставляет
   cra-ui.js по замеру при открытии (flipIfClipped). Экраны у себя не
   объявляют ничего.

   Класс висит на ПАНЕЛИ, а не на хозяине, намеренно: хозяев меню три
   (.cra-nav__slot, .cra-user, .cra-dd), и модификатор хозяина потребовал
   бы трёх правил здесь и четвёртого на каждого нового хозяина. Панель у
   всех троих одна и та же — правило тоже одно, и оно одинаково работает
   у меню шапки, выпадашки тулбара и меню строки таблицы.

   С ручным .cra-dd--end не спорит: тот выставляет ровно эти же значения,
   и панель, уже прижатая к конечному краю, за правый край не выходит —
   класс ей просто не ставится. */
[data-ui="v3"] .cra-menu.cra-menu--flip { inset-inline-start: auto; inset-inline-end: 0; }
[data-ui="v3"] .cra-menu a {
  display: flex; justify-content: space-between; gap: var(--vc-v3-gap-item);
  padding: 8px 14px; color: var(--text); text-decoration: none;
  font-size: var(--vc-v3-fs-data); position: relative;
}
[data-ui="v3"] .cra-menu a:hover { background: var(--surface-2); }
[data-ui="v3"] .cra-menu a:focus-visible { outline: 2px solid var(--vc-v3-accent-dim); outline-offset: -2px; }
[data-ui="v3"] .cra-menu__count { color: var(--muted); font: var(--vc-v3-fs-label) var(--font-mono); }
[data-ui="v3"] .cra-menu__sep { border: none; border-block-start: 1px solid var(--line); margin-block: 6px; }
[data-ui="v3"] .cra-menu__group {
  font: 600 10px var(--font-body); letter-spacing: .12em; text-transform: uppercase;
  color: var(--muted); padding: 7px 14px 3px;
}
/* Сегментный переключатель внутри меню (сегодня — выбор темы): у пунктов
   меню отступы свои, у сегментов своих нет, и без обёртки они прилипают
   к краю панели. Компонент при этом тот же .cra-seg, что на «Системе», —
   меняется только место, где он стоит. */
[data-ui="v3"] .cra-menu__seg { padding: 2px 14px 8px; }
[data-ui="v3"] .cra-menu__seg .cra-seg { width: 100%; }
/* nowrap обязателен: «Как в системе» ломалось на две строки и растягивало
   один сегмент вдвое выше соседних. Панель меню шире своего min-width
   вырастет сама — это ей и положено. */
[data-ui="v3"] .cra-menu__seg .cra-seg > button {
  flex: 1; height: 28px; padding-inline: 10px; white-space: nowrap;
}
/* Пункт с флайаутом: ссылка и панель второго уровня — СОСЕДИ внутри
   слота, а не вложены друг в друга. Ссылка в ссылке недопустима, браузер
   такую разметку чинит сам и выносит пункты флайаута в главную панель
   плоским списком (подробности — в cra_macros/header.html). Слот и есть
   система координат панели: позиционируется она от него. */
[data-ui="v3"] .cra-menu__slot { position: relative; }
/* Каретка флайаута — 10px, на пиксель крупнее каретки раздела: так в
   прототипе, и разведены они не случайно. «◂» у́же, чем «▾», и на 9px
   оседает в точку у самого края панели. */
[data-ui="v3"] .cra-menu__sub .cra-caret { font-size: 10px; }
[data-ui="v3"] .cra-menu__fly {
  position: absolute; inset-inline-end: 100%; inset-block-start: -7px;
  min-width: 220px; background: var(--surface); border: 1px solid var(--line);
  box-shadow: var(--vc-v3-menu-shadow); display: none; padding-block: 6px;
}
[data-ui="v3"] .cra-menu__slot:hover .cra-menu__fly,
[data-ui="v3"] .cra-menu__slot:focus-within .cra-menu__fly,
[data-ui="v3"] .cra-menu__fly:hover { display: block; }
[data-ui="v3"] .cra-menu a.cra-menu__danger { color: var(--vc-v3-err-ink); }

[data-ui="v3"] .cra-glob { margin-inline-start: auto; display: flex; gap: 12px; align-items: center; align-self: stretch; }
[data-ui="v3"] .cra-iconbtn {
  width: 30px; height: 30px; border: 1px solid transparent; background: transparent;
  display: grid; place-content: center; cursor: pointer; color: var(--muted); position: relative;
}
[data-ui="v3"] .cra-iconbtn:hover { border-color: var(--line); background: var(--surface-2); color: var(--text); }
[data-ui="v3"] .cra-iconbtn:focus-visible { outline: 2px solid var(--vc-v3-accent-dim); outline-offset: 2px; }
[data-ui="v3"] .cra-iconbtn--help { font: 600 13px var(--font-mono); }
/* Лампочка «Режим обучения» (Л1): выключена — обводка, включена —
   заливка со свечением. Глобальный переключатель, не галочка формы. */
[data-ui="v3"] .cra-iconbtn--lamp { width: 34px; height: 34px; }
[data-ui="v3"] .cra-iconbtn--lamp .cra-lamp__on { display: none; }
[data-ui="v3"] .cra-iconbtn--lamp.cra-iconbtn--on .cra-lamp__off { display: none; }
[data-ui="v3"] .cra-iconbtn--lamp.cra-iconbtn--on .cra-lamp__on { display: block; }
[data-ui="v3"] .cra-iconbtn--lamp.cra-iconbtn--on { filter: drop-shadow(0 0 7px color-mix(in srgb, var(--vc-v3-accent-ink) 55%, transparent)); }

/* Подсказка — свой дескриптор вместо нативного title с его задержкой (ст. 8.9) */
[data-ui="v3"] .cra-tip {
  position: absolute; inset-block-start: calc(100% + 8px); inset-inline-end: 0;
  background: var(--surface); border: 1px solid var(--line); padding: 7px 11px;
  font-size: var(--vc-v3-fs-meta); color: var(--text); white-space: nowrap; display: none; z-index: 9;
}
[data-ui="v3"] .cra-iconbtn:hover .cra-tip,
[data-ui="v3"] .cra-iconbtn:focus-visible .cra-tip { display: block; }

/* Текущий пользователь виден с любого экрана, «Выйти» — в два клика (ст. 4.4) */
[data-ui="v3"] .cra-user { position: relative; display: flex; align-items: center; align-self: stretch; }
[data-ui="v3"] .cra-user__btn {
  display: flex; gap: var(--vc-v3-gap-inner); align-items: center; height: 34px;
  padding-inline: 12px; background: transparent; border: 1px solid transparent;
  color: var(--text); font: 500 13px var(--font-body); cursor: pointer;
}
[data-ui="v3"] .cra-user__btn::before { content: ""; width: var(--vc-v3-px-atom); height: var(--vc-v3-px-atom); background: var(--accent); }
[data-ui="v3"] .cra-user__btn:hover,
[data-ui="v3"] .cra-user--open .cra-user__btn { border-color: var(--line); background: var(--surface-2); }
[data-ui="v3"] .cra-user__btn:focus-visible { outline: 2px solid var(--vc-v3-accent-dim); outline-offset: 2px; }
[data-ui="v3"] .cra-user .cra-menu { inset-inline-start: auto; inset-inline-end: 0; min-width: 240px; }
[data-ui="v3"] .cra-user--open .cra-menu { display: block; }

/* ══ 15. СОСТОЯНИЯ — Crawler_states_v1.3 ══════════════════════════════
   Лист 8.1 — не экран, а свод: один набор состояний на весь интерфейс.
   Новых компонентов он не заводил и здесь не заводит: пустота живёт на
   .cra-empty (разд. 8), сообщение в блоке — на .cra-note--err (разд. 7),
   тосты — на .cra-toast (разд. 11), кнопка в работе — на .cra-btn--loading
   (разд. 1). Ниже добавлено ровно то, чего в ките ещё не было: анатомия
   пустоты (паук · заголовок · объяснение · действие), скелет загрузки,
   страница ошибки и внутренности плашки долгой операции.

   Тексты состояний здесь НЕ живут: они в cra_macros/texts.html, одним
   словарём на весь интерфейс. Формулировка «ничего не найдено» в двух
   разделах разными словами — та же беда, что расходящиеся копии фильтра,
   только читает её пользователь. */

/* Пустота: два разных случая и два разных текста (пусто по построению —
   «прогонов ещё не было»; пусто по отбору — «ничего не найдено», и там
   отбор назван словами). Разметка одна, различие в тексте и действии. */
[data-ui="v3"] .cra-empty__ghost {
  width: 52px; height: 52px; display: block; margin: 0 auto var(--vc-v3-gap-inner);
  color: var(--vc-v3-accent-ink); opacity: .14;
}
[data-ui="v3"] .cra-empty__title { font: 500 var(--vc-v3-fs-lead) var(--font-body); display: block; }
[data-ui="v3"] .cra-empty__act { display: flex; gap: var(--vc-v3-gap-inner); justify-content: center; }
/* Пустота внутри таблицы приходит одной ячейкой на всю ширину: свои
   отступы у ячейки обнуляются, иначе к паддингу пустоты прибавляется
   паддинг td и блок съезжает вниз и вправо. */
[data-ui="v3"] .cra-table td.cra-empty-cell { padding: 0; height: auto; border-block-end: none; }

/* Скелет вместо крутилки (правило листа): ждём данные — показываем форму
   того, что придёт. Спиннер кита живёт только внутри кнопки. */
[data-ui="v3"] .cra-skel { padding-block-end: var(--vc-v3-gap-inner); }
[data-ui="v3"] .cra-skel__row {
  height: 38px;
  border-block-end: 1px solid color-mix(in srgb, var(--line) 55%, transparent);
  background-image: linear-gradient(90deg, transparent,
    color-mix(in srgb, var(--text) 5%, transparent), transparent);
  background-size: 600px 100%;
  animation: cra-shim 1.4s linear infinite;
}
[data-ui="v3"] .cra-skelblock { display: flex; flex-direction: column; gap: 10px; }
[data-ui="v3"] .cra-skel__line {
  height: 12px; display: block;
  background-color: color-mix(in srgb, var(--text) 7%, transparent);
  background-image: linear-gradient(90deg, transparent,
    color-mix(in srgb, var(--text) 5%, transparent), transparent);
  background-size: 600px 100%;
  animation: cra-shim 1.4s linear infinite;
}
[data-ui="v3"] .cra-skel__line--w40 { width: 40%; }
[data-ui="v3"] .cra-skel__line--w55 { width: 55%; }
[data-ui="v3"] .cra-skel__line--w70 { width: 70%; }

/* Страница ошибки: код мелко и тихо, человеческий текст крупно, и всегда
   есть куда нажать. Отказ доступа (403) — та же разметка, свой текст:
   отдельного компонента под него лист не заводил. */
[data-ui="v3"] .cra-errpage {
  display: flex; flex-direction: column; align-items: center; gap: var(--vc-v3-gap-inner);
  padding: 44px 24px; text-align: center;
}
[data-ui="v3"] .cra-errpage__code {
  font: 500 var(--vc-v3-fs-meta) var(--font-mono); color: var(--muted);
  border: 1px solid var(--line); padding: 2px 10px;
}
[data-ui="v3"] .cra-errpage__title { font: 500 17px var(--font-body); }
[data-ui="v3"] .cra-errpage__text { font: 400 13px var(--font-body); color: var(--muted); max-width: 52ch; line-height: 1.5; }
[data-ui="v3"] .cra-errpage__act { margin-block-start: 12px; display: flex; gap: var(--vc-v3-gap-inner); }

/* Долгая операция: заголовок, строка подробностей, пиксельная полоса и
   переход «К прогону». Сам носитель объявлен в разд. 12. */
[data-ui="v3"] .cra-longop__title { font: 500 14px var(--font-body); display: block; }
[data-ui="v3"] .cra-longop__sub { font: 400 var(--vc-v3-fs-mono) var(--font-body); color: var(--muted); margin-block-start: 3px; }
[data-ui="v3"] .cra-longop .cra-prog { margin-block-start: 10px; }
[data-ui="v3"] .cra-longop__act { margin-block-start: 12px; }

/* Ссылка-действие внутри сообщения в потоке («Повторить»): это кнопка по
   смыслу, но живёт строкой текста, поэтому без рамки и высоты контрола.
   Подчёркивания у неё нет: в согласованном листе состояний это `.lnk` —
   только цвет --accent-ink и курсор (Crawler_states_v1.3:471). Оно тут и
   стояло, единственное подчёркивание на весь слой; ни один компонент
   набора текстом-подчёркиванием не рисуется — «ссылочность» показывают
   цвет и нижняя граница (.cra-link). */
[data-ui="v3"] .cra-note__act {
  background: none; border: none; padding: 0; cursor: pointer;
  font: inherit; color: var(--vc-v3-accent-ink); text-decoration: none;
}
[data-ui="v3"] .cra-note--err .cra-note__act { color: var(--vc-v3-err-ink); }
[data-ui="v3"] .cra-note__act:focus-visible { outline: 2px solid var(--vc-v3-accent-dim); outline-offset: 2px; }

@keyframes cra-shim { 0% { background-position: -300px 0; } 100% { background-position: 600px 0; } }

/* ══ 14. ДВИЖЕНИЕ ════════════════════════════════════════════════════
   Единственный движущийся элемент плашки — мигающий пиксель «идёт…»;
   лоадер кнопки — кольцо из восьми бегущих квадратов (кит, разд. 1).
   Всё гасится под prefers-reduced-motion (паспорт ст. 7). */
@keyframes cra-blink { 50% { opacity: .25; } }
@keyframes cra-toast-in { from { transform: translateY(8px); opacity: 0; } }

/* Кольцо лоадера: восемь квадратов атома, голова обегает круг за 0.9с.
   Прозрачности — от --on-color через color-mix: ни одного цветового
   литерала мимо токена (ст. 9.3, А13). */
@keyframes cra-pxring {
  0% { box-shadow:
    -7px -7px 0 0 color-mix(in srgb, var(--on-color) 100%, transparent),
    0px -7px 0 0 color-mix(in srgb, var(--on-color) 12%, transparent),
    7px -7px 0 0 color-mix(in srgb, var(--on-color) 12%, transparent),
    7px 0px 0 0 color-mix(in srgb, var(--on-color) 12%, transparent),
    7px 7px 0 0 color-mix(in srgb, var(--on-color) 12%, transparent),
    0px 7px 0 0 color-mix(in srgb, var(--on-color) 12%, transparent),
    -7px 7px 0 0 color-mix(in srgb, var(--on-color) 35%, transparent),
    -7px 0px 0 0 color-mix(in srgb, var(--on-color) 65%, transparent);
  }
  12% { box-shadow:
    -7px -7px 0 0 color-mix(in srgb, var(--on-color) 65%, transparent),
    0px -7px 0 0 color-mix(in srgb, var(--on-color) 100%, transparent),
    7px -7px 0 0 color-mix(in srgb, var(--on-color) 12%, transparent),
    7px 0px 0 0 color-mix(in srgb, var(--on-color) 12%, transparent),
    7px 7px 0 0 color-mix(in srgb, var(--on-color) 12%, transparent),
    0px 7px 0 0 color-mix(in srgb, var(--on-color) 12%, transparent),
    -7px 7px 0 0 color-mix(in srgb, var(--on-color) 12%, transparent),
    -7px 0px 0 0 color-mix(in srgb, var(--on-color) 35%, transparent);
  }
  25% { box-shadow:
    -7px -7px 0 0 color-mix(in srgb, var(--on-color) 35%, transparent),
    0px -7px 0 0 color-mix(in srgb, var(--on-color) 65%, transparent),
    7px -7px 0 0 color-mix(in srgb, var(--on-color) 100%, transparent),
    7px 0px 0 0 color-mix(in srgb, var(--on-color) 12%, transparent),
    7px 7px 0 0 color-mix(in srgb, var(--on-color) 12%, transparent),
    0px 7px 0 0 color-mix(in srgb, var(--on-color) 12%, transparent),
    -7px 7px 0 0 color-mix(in srgb, var(--on-color) 12%, transparent),
    -7px 0px 0 0 color-mix(in srgb, var(--on-color) 12%, transparent);
  }
  37% { box-shadow:
    -7px -7px 0 0 color-mix(in srgb, var(--on-color) 12%, transparent),
    0px -7px 0 0 color-mix(in srgb, var(--on-color) 35%, transparent),
    7px -7px 0 0 color-mix(in srgb, var(--on-color) 65%, transparent),
    7px 0px 0 0 color-mix(in srgb, var(--on-color) 100%, transparent),
    7px 7px 0 0 color-mix(in srgb, var(--on-color) 12%, transparent),
    0px 7px 0 0 color-mix(in srgb, var(--on-color) 12%, transparent),
    -7px 7px 0 0 color-mix(in srgb, var(--on-color) 12%, transparent),
    -7px 0px 0 0 color-mix(in srgb, var(--on-color) 12%, transparent);
  }
  50% { box-shadow:
    -7px -7px 0 0 color-mix(in srgb, var(--on-color) 12%, transparent),
    0px -7px 0 0 color-mix(in srgb, var(--on-color) 12%, transparent),
    7px -7px 0 0 color-mix(in srgb, var(--on-color) 35%, transparent),
    7px 0px 0 0 color-mix(in srgb, var(--on-color) 65%, transparent),
    7px 7px 0 0 color-mix(in srgb, var(--on-color) 100%, transparent),
    0px 7px 0 0 color-mix(in srgb, var(--on-color) 12%, transparent),
    -7px 7px 0 0 color-mix(in srgb, var(--on-color) 12%, transparent),
    -7px 0px 0 0 color-mix(in srgb, var(--on-color) 12%, transparent);
  }
  62% { box-shadow:
    -7px -7px 0 0 color-mix(in srgb, var(--on-color) 12%, transparent),
    0px -7px 0 0 color-mix(in srgb, var(--on-color) 12%, transparent),
    7px -7px 0 0 color-mix(in srgb, var(--on-color) 12%, transparent),
    7px 0px 0 0 color-mix(in srgb, var(--on-color) 35%, transparent),
    7px 7px 0 0 color-mix(in srgb, var(--on-color) 65%, transparent),
    0px 7px 0 0 color-mix(in srgb, var(--on-color) 100%, transparent),
    -7px 7px 0 0 color-mix(in srgb, var(--on-color) 12%, transparent),
    -7px 0px 0 0 color-mix(in srgb, var(--on-color) 12%, transparent);
  }
  75% { box-shadow:
    -7px -7px 0 0 color-mix(in srgb, var(--on-color) 12%, transparent),
    0px -7px 0 0 color-mix(in srgb, var(--on-color) 12%, transparent),
    7px -7px 0 0 color-mix(in srgb, var(--on-color) 12%, transparent),
    7px 0px 0 0 color-mix(in srgb, var(--on-color) 12%, transparent),
    7px 7px 0 0 color-mix(in srgb, var(--on-color) 35%, transparent),
    0px 7px 0 0 color-mix(in srgb, var(--on-color) 65%, transparent),
    -7px 7px 0 0 color-mix(in srgb, var(--on-color) 100%, transparent),
    -7px 0px 0 0 color-mix(in srgb, var(--on-color) 12%, transparent);
  }
  87% { box-shadow:
    -7px -7px 0 0 color-mix(in srgb, var(--on-color) 12%, transparent),
    0px -7px 0 0 color-mix(in srgb, var(--on-color) 12%, transparent),
    7px -7px 0 0 color-mix(in srgb, var(--on-color) 12%, transparent),
    7px 0px 0 0 color-mix(in srgb, var(--on-color) 12%, transparent),
    7px 7px 0 0 color-mix(in srgb, var(--on-color) 12%, transparent),
    0px 7px 0 0 color-mix(in srgb, var(--on-color) 35%, transparent),
    -7px 7px 0 0 color-mix(in srgb, var(--on-color) 65%, transparent),
    -7px 0px 0 0 color-mix(in srgb, var(--on-color) 100%, transparent);
  }
  100% { box-shadow:
    -7px -7px 0 0 color-mix(in srgb, var(--on-color) 100%, transparent),
    0px -7px 0 0 color-mix(in srgb, var(--on-color) 12%, transparent),
    7px -7px 0 0 color-mix(in srgb, var(--on-color) 12%, transparent),
    7px 0px 0 0 color-mix(in srgb, var(--on-color) 12%, transparent),
    7px 7px 0 0 color-mix(in srgb, var(--on-color) 12%, transparent),
    0px 7px 0 0 color-mix(in srgb, var(--on-color) 12%, transparent),
    -7px 7px 0 0 color-mix(in srgb, var(--on-color) 35%, transparent),
    -7px 0px 0 0 color-mix(in srgb, var(--on-color) 65%, transparent);
  }
}

@media (prefers-reduced-motion: reduce) {
  [data-ui="v3"] *,
  [data-ui="v3"] *::before,
  [data-ui="v3"] *::after {
    animation-duration: .001ms;
    animation-iteration-count: 1;
    transition-duration: .001ms;
  }
}

/* ═══════════════════════════════════════════════════════════════════════
   ДОПОЛНЕНИЕ КИТА — CRA_components-small_v1.4, разд. 8–13.
   Волна 1 (перенос экранов).

   Слой был собран по киту v1.1. Кит с тех пор дошёл до v1.4 и завёл шесть
   компонентов (основание — Crawler_components-kit-delta_v1.1) плюс статус
   «идёт работа по плану». Расхождение было записано строкой в индексе
   дизайн-папки и отложено «до волны переноса» — вот она.

   Ниже — ровно то, что в ките есть, и ничего сверх: оценка в строке,
   сворачиваемый блок, блок вывода, пункт-переключатель меню, выключено
   с названной причиной, плашки полноты, статус --work. Значения взяты
   из кита; цветовых литералов нет ни одного — только роли токенов.
   ═══════════════════════════════════════════════════════════════════════ */

/* ══ 16. СТАТУС «ИДЁТ РАБОТА ПО ПЛАНУ» — паспорт ст. 6.2, РД-25 ═══════
   Жёлтый --run остаётся ТОЛЬКО за отклонением (темп снижен · нужен
   вход · квота исчерпана · пауза). Идущее по плану — зелёное. Мигает
   лишь то, что происходит прямо сейчас: «в очереди» зелёное, но без
   квадрата (РД-28) — объект ждёт, а не работает. */
[data-ui="v3"] .cra-status--work {
  background: color-mix(in srgb, var(--accent) 11%, transparent);
  color: var(--vc-v3-accent-ink); box-shadow: inset 2px 0 0 var(--accent);
}
[data-ui="v3"] .cra-status--work.cra-status--working::after { background: var(--accent); }

/* ══ 17. ОЦЕНКА В СТРОКЕ — кит v1.4, разд. 8 ═════════════════════════
   Три кнопки: годится · не годится · теги и комментарий. Появляются по
   наведению на строку (паспорт ст. 7) — выставленная оценка видна
   всегда, иначе её нельзя перечитать, не водя мышью по таблице. */
[data-ui="v3"] .cra-rate { display: inline-flex; gap: 2px; }
[data-ui="v3"] .cra-rate__b {
  width: 26px; height: 26px; border: 1px solid transparent; background: transparent;
  color: var(--muted); font: 400 13px var(--font-body); cursor: pointer;
  display: grid; place-content: center; padding: 0;
}
[data-ui="v3"] .cra-rate__b--yes:hover,
[data-ui="v3"] .cra-rate__b--yes.cra-rate__b--on {
  color: var(--vc-v3-accent-ink); background: color-mix(in srgb, var(--accent) 12%, transparent);
}
[data-ui="v3"] .cra-rate__b--yes.cra-rate__b--on {
  border-color: var(--vc-v3-accent-dim); background: color-mix(in srgb, var(--accent) 16%, transparent);
}
[data-ui="v3"] .cra-rate__b--no:hover,
[data-ui="v3"] .cra-rate__b--no.cra-rate__b--on {
  color: var(--vc-v3-err-ink); background: color-mix(in srgb, var(--err) 12%, transparent);
}
[data-ui="v3"] .cra-rate__b--no.cra-rate__b--on {
  border-color: var(--err); background: color-mix(in srgb, var(--err) 16%, transparent);
}
[data-ui="v3"] .cra-rate__b--more:hover { color: var(--text); background: color-mix(in srgb, var(--muted) 14%, transparent); }
[data-ui="v3"] .cra-rate__b:focus-visible { outline: 2px solid var(--vc-v3-accent-dim); outline-offset: 2px; }
[data-ui="v3"] .cra-rate__b--no:focus-visible { outline-color: var(--err); }
/* Контролы у строки — по наведению (паспорт ст. 7): выставленное видно всегда. */
[data-ui="v3"] .cra-row .cra-rate__b:not(.cra-rate__b--on) { opacity: 0; transition: opacity .12s; }
[data-ui="v3"] .cra-row:hover .cra-rate__b,
[data-ui="v3"] .cra-row:focus-within .cra-rate__b { opacity: 1; }

/* ══ 18. СВОРАЧИВАЕМЫЙ БЛОК СО СВОДКОЙ — кит v1.4, разд. 9 ═══════════
   По умолчанию закрыт: сводка в заголовке отвечает на вопрос «есть ли
   там что-нибудь» без раскрытия. Заголовок — кнопка, а не div: блок
   обязан открываться с клавиатуры. */
[data-ui="v3"] .cra-fold { border: 1px solid var(--line); background: var(--surface); }
[data-ui="v3"] .cra-fold__h {
  display: flex; align-items: center; gap: var(--vc-v3-gap-item);
  width: 100%; height: 38px; padding-inline: 16px; background: transparent;
  border: 0; color: var(--text); cursor: pointer; text-align: start;
}
[data-ui="v3"] .cra-fold__h:focus-visible { outline: 2px solid var(--vc-v3-accent-dim); outline-offset: -2px; }
[data-ui="v3"] .cra-fold__arrow { color: var(--muted); font-size: 10px; }
[data-ui="v3"] .cra-fold__title {
  font: 600 var(--vc-v3-fs-label) var(--font-body); letter-spacing: .08em;
  text-transform: uppercase; color: var(--muted);
}
[data-ui="v3"] .cra-fold__sum { font: 400 var(--vc-v3-fs-meta) var(--font-body); color: var(--muted); }
[data-ui="v3"] .cra-fold__h .cra-count { margin-inline-start: auto; }
[data-ui="v3"] .cra-fold__body { border-block-start: 1px solid var(--line); display: none; }
[data-ui="v3"] .cra-fold--open .cra-fold__body { display: block; }

/* ══ 19. БЛОК ВЫВОДА — кит v1.4, разд. 10 ════════════════════════════
   Отчёт сухого прогона и журнал операции: моно, строки подсвечены по
   смыслу. Прокрутка внутри блока, наружу не выталкивает. */
[data-ui="v3"] .cra-out {
  background: var(--bg); border: 1px solid var(--line); padding: 12px 14px;
  font: 400 var(--vc-v3-fs-meta) var(--font-mono); line-height: 1.65;
  color: var(--text); white-space: pre; overflow: auto;
  min-height: 120px; max-height: 320px;
}
/* Модификаторы вывода для текста ДОКУМЕНТА (экран 4.2): машинный вывод
   не переносят — в логе перенос соврал бы о строке, — а извлечённый текст
   документа это проза, и читать её без переноса нельзя. --full снимает
   потолок высоты по кнопке «Открыть превью целиком». Запись DR-37. */
[data-ui="v3"] .cra-out--wrap { white-space: pre-wrap; word-break: break-word; }
[data-ui="v3"] .cra-out--full { max-height: none; }
[data-ui="v3"] .cra-out__mute { color: var(--muted); }
[data-ui="v3"] .cra-out__ok { color: var(--vc-v3-accent-ink); }
[data-ui="v3"] .cra-out__warn { color: var(--vc-v3-warn-ink); }
[data-ui="v3"] .cra-out__err { color: var(--vc-v3-err-ink); }

/* ══ 20. ПУНКТ МЕНЮ И ПУНКТ-ПЕРЕКЛЮЧАТЕЛЬ — кит v1.4, разд. 11 ═══════
   Переключатель меню МЕНЮ НЕ ЗАКРЫВАЕТ — в отличие от пункта-действия
   рядом: колонки включают пачкой, и закрытие после каждой галочки
   превращает настройку в десять открываний. */
/* min-height вместо height: подпись, не влезшая в строку, переносится, и
   при жёсткой высоте строки НАЛЕЗАЛИ друг на друга — замерено в меню
   «Действия» карточки прогона («Скопировать сводку в буфер» поверх
   «Скачать сводку (.txt)»). Высота пункта в покое та же 30px. */
[data-ui="v3"] .cra-menuitem {
  display: flex; align-items: center; gap: 10px; width: 100%; min-height: 30px;
  padding-block: 4px;
  padding-inline: 10px; border: 1px solid transparent; background: transparent;
  color: var(--text); font: 400 var(--vc-v3-fs-mono) var(--font-body);
  text-align: start; cursor: pointer; text-decoration: none;
}
[data-ui="v3"] .cra-menuitem:hover { background: color-mix(in srgb, var(--accent) 6%, transparent); }
[data-ui="v3"] .cra-menuitem:focus-visible { outline: 2px solid var(--vc-v3-accent-dim); outline-offset: -2px; }
/* Пункт, выключенный СОСТОЯНИЕМ ОБЪЕКТА («В архив», пока прогон идёт), —
   тот же вид, что у .cra-btn:disabled, и намеренно НЕ штриховка разд. 21.
   Паспорт ст. 7 разводит их прямо: здесь причина внутренняя и уйдёт сама,
   как только прогон встанет на паузу, — штриховка же говорит «этого в
   системе нет», то есть врёт о природе причины. Причина у такого пункта
   стоит СЛОВАМИ рядом, в подписи-хинте (макрос menuitem(disabled=…, hint=…)),
   а не в подсказке: она видна без наведения, как и требует паспорт. */
[data-ui="v3"] .cra-menuitem:disabled { opacity: .5; cursor: not-allowed; }
[data-ui="v3"] .cra-menuitem:disabled:hover { background: transparent; }
[data-ui="v3"] .cra-menuitem__hint { margin-inline-start: auto; font: 400 var(--vc-v3-fs-label) var(--font-body); color: var(--muted); }
[data-ui="v3"] .cra-menuitem--check input {
  appearance: none; flex: none; width: 15px; height: 15px;
  border: 1px solid var(--line); background: var(--bg); margin: 0;
  display: grid; place-content: center; cursor: pointer;
}
[data-ui="v3"] .cra-menuitem--check input:checked { border-color: var(--accent); }
[data-ui="v3"] .cra-menuitem--check input:checked::before { content: ""; width: 7px; height: 7px; background: var(--accent); }
/* Зелёной подписи у включённого пункта-переключателя НЕТ: в макете
   («Колонки ▾», Crawler_products_v3.1) подписи обычные, зелёная только
   сама отметка в квадрате. Правило `:has(input:checked){color:accent-ink}`
   красило половину списка колонок и спорило с правилом «цвет = отклонение»:
   включённая колонка — норма, а не отклонение. Отметка отличает её и без
   краски текста. */

/* ══ 21. ВЫКЛЮЧЕНО С НАЗВАННОЙ ПРИЧИНОЙ — кит v1.4, разд. 12 ═════════
   Штриховка вместо приглушения, причина обязательна, элемент остаётся
   в tab-порядке. Техническая опора правила этапа «переносим, но
   выключаем с причиной»: приглушённый элемент читается как «сломалось»,
   заштрихованный — как «пока нельзя, и вот почему».
   .cra-btn:disabled НЕ отменяется и разведён с этим состоянием словами:
   там причина в состоянии формы и уходит сама, здесь причина внешняя.

   Пункт меню взят в тот же список, а не получил свой вид: выключенный
   пункт в меню колонок и выключенный пункт в меню «Действия» — одно
   состояние, и различаться им не за что. Ссылка без href не кликается и
   не наводится, отдельного правила на ховер не нужно — фон у неё уже
   тот, что даёт ховер соседям.

   ИСКЛЮЧЕНИЕ — ШАПКА: в ней штриховки нет вовсе, ни у значка, ни у
   пункта меню (решение владельца 04.09.2026 по DR-66; вид — ниже, в
   разд. 23, там же и довод). Здесь остаётся ровно то, что видно на
   рабочей поверхности страницы. */
[data-ui="v3"] .cra-btn.cra-off,
[data-ui="v3"] .cra-ib.cra-off,
[data-ui="v3"] .cra-menuitem.cra-off,
[data-ui="v3"] .cra-menu a.cra-off {
  position: relative; opacity: 1; color: var(--muted);
  border-color: color-mix(in srgb, var(--line) 60%, transparent);
  cursor: not-allowed;
  /* Полосы — ФОН элемента, а не слой поверх него. Пока они лежали в
     ::after{inset:0}, они рисовались ПОВЕРХ подписи, и чем плотнее были
     чернила, тем сильнее съедали буквы: приёмка 7.24.0 замерила потерю
     трети пикселей подписи в тёмной теме. Содержимое рисуется поверх
     собственного фона всегда — значит плотность чернил больше не спорит
     с читаемостью, и подобрать её можно по различимости поверхности, ради
     которой она и заведена. */
  background-color: var(--surface-2);
  background-image: repeating-linear-gradient(135deg, var(--vc-v3-off-hatch) 0 2px, transparent 2px 6px);
}
/* ── Штриховка ТЕМНИТ, а не светлит — и поэтому у тем разные чернила ──
   Жалоба владельца 09.09.2026: «в светлой теме кнопка показана
   выключенной, а в тёмной выглядит активной». Правило было одно на обе
   темы (--muted @14 %), и в этом ровно и была причина.

   Замер (CIELAB, ΔL* полосы штриховки относительно фона --surface-2):
     светлая:  --muted @14 % → ΔL* −6.8 — полоса ТЕМНЕЕ фона;
     тёмная:   --muted @14 % → ΔL* +8.4 — полоса СВЕТЛЕЕ фона.
   Полосы занимают треть площади (2px из 6), то есть средняя светлота
   кнопки в светлой теме падает на 2.3, а в тёмной РАСТЁТ на 2.8 — и
   выключенная кнопка оказывается ярче стоящей рядом рабочей (её фон —
   тот же --surface-2 без всякой добавки). Приглушение, которое подсвечивает,
   выключенным не читается: одинаков был механизм, а не результат.

   Одним токеном это не чинится, и вот почему: «темнее, чем --surface-2» —
   свойство ТЕМЫ, а не палитры. В тёмной темнее только --bg и --surface, в
   светлой они обе светлее. Поэтому чернила штриховки названы токеном
   --vc-v3-off-hatch (префикс --vc-, ст. 9.2) и переопределяются в светлой
   ветке ровно тем же способом, каким тема переопределяет остальные
   значения, — без второго правила на носителя.
   Тёмное значение подобрано по замеру, а не на глаз: --bg @80 % даёт
   ΔL* −7.0 при светлых −6.8, то есть ту же различимость и тот же ЗНАК.

   Взяты только носители с СОБСТВЕННЫМ фоном --surface-2 (кнопка, значок,
   пункт меню): под ними известно, на чём лежит штриховка. У галочки,
   сегмента, ссылки и контрола дерева (ниже) фона своего нет — они лежат
   на чём придётся, вплоть до --bg, где тёмные чернила исчезли бы вовсе, —
   и остаются на прежнем правиле.
   Расхождение с каноном разд. 12 (одни чернила на обе темы) записано:
   docs/deviations.md, «разд. 12 кита — чернила штриховки у тёмной темы
   свои», 09.09.2026. */
[data-ui="v3"] { --vc-v3-off-hatch: color-mix(in srgb, var(--bg) 80%, transparent); }
html[data-ui="v3"][data-theme="light"] { --vc-v3-off-hatch: color-mix(in srgb, var(--muted) 14%, transparent); }
[data-ui="v3"] .cra-btn--primary.cra-off::before { background: var(--muted); }
[data-ui="v3"] .cra-btn--danger.cra-off { border-color: color-mix(in srgb, var(--line) 60%, transparent); }
/* Причина доходит и до ФОКУСА, а не только до наведения. Нативный title
   браузер показывает по указателю и не показывает при переходе на кнопку
   с клавиатуры — а кнопка остаётся в обходе намеренно (aria-disabled, не
   disabled: решение DR-66, случай 1), и человек, дошедший до неё Tab'ом,
   упирался в мёртвую кнопку без единого слова о том, почему она мёртвая.
   Носитель — тот же дескриптор .cra-tip, которым уже сказана причина у
   выключенного значка шапки (разд. 23) и у приглушённой вкладки: своего
   не заводится. Кнопка уже position:relative — правилом выше. */
[data-ui="v3"] .cra-btn.cra-off:hover .cra-tip,
[data-ui="v3"] .cra-btn.cra-off:focus-visible .cra-tip { display: block; }
/* Причина у кнопки — ФРАЗА, а не подпись значка: базовый .cra-tip не
   переносит строк (nowrap), и «запуска сбора по рынку одной кнопкой в
   бэкенде нет…» вытянулось бы лентой шире экрана. Дескриптор растёт по
   содержимому до 320px и дальше переносит; растёт он ВЛЕВО от правого
   края кнопки (inset-inline-end:0 базового правила), поэтому вбок
   страницу не толкает. */
[data-ui="v3"] .cra-btn.cra-off .cra-tip {
  white-space: normal; inline-size: max-content; max-inline-size: 320px;
  text-align: start; font-weight: 400;
}

/* ── Носители, добавленные волной 2 ──────────────────────────────────
   Правило разд. 12 описывает СОСТОЯНИЕ, а не список хозяев, и список
   отставал от разметки: галочка, кнопка сегмента, контрол дерева, поле
   ввода и вкладка получали класс cra-off и не менялись ни на пиксель —
   то есть выключенное было неотличимо от рабочего ровно там, где
   правило этапа велит показывать «пока нельзя, и вот почему».

   Галочка, кнопка сегмента и контрол дерева — обычные носители: у них
   есть площадь и своё ::after, штриховка ложится каноном. */
[data-ui="v3"] .cra-check.cra-off,
[data-ui="v3"] .cra-choice.cra-off,
[data-ui="v3"] a.cra-link.cra-off,
[data-ui="v3"] .cra-seg > button.cra-off,
[data-ui="v3"] .cra-seg > a.cra-off,
[data-ui="v3"] .cra-tree-tg.cra-off,
[data-ui="v3"] .cra-section__act.cra-off,
[data-ui="v3"] .cra-dyn.cra-off {
  position: relative; opacity: 1; color: var(--muted); cursor: not-allowed;
}
[data-ui="v3"] .cra-check.cra-off::after,
[data-ui="v3"] .cra-choice.cra-off::after,
[data-ui="v3"] a.cra-link.cra-off::after,
[data-ui="v3"] .cra-seg > button.cra-off::after,
[data-ui="v3"] .cra-seg > a.cra-off::after,
[data-ui="v3"] .cra-tree-tg.cra-off::after,
[data-ui="v3"] .cra-section__act.cra-off::after,
[data-ui="v3"] .cra-dyn.cra-off::after {
  content: ""; position: absolute; inset: 0;
  background: repeating-linear-gradient(135deg, color-mix(in srgb, var(--muted) 14%, transparent) 0 2px, transparent 2px 6px);
  pointer-events: none;
}
/* Само поле галочки внутри выключенной подписи тоже гаснет: борт тише,
   отметка — цветом «выключено», а не акцентом, иначе отмеченная
   выключенная галочка выглядит активнее включённой соседки. */
[data-ui="v3"] a.cra-link.cra-off { border-block-end-color: color-mix(in srgb, var(--line) 60%, transparent); }
[data-ui="v3"] .cra-check.cra-off input,
[data-ui="v3"] .cra-choice.cra-off input,
[data-ui="v3"] input.cra-check.cra-off {
  border-color: color-mix(in srgb, var(--line) 60%, transparent); cursor: not-allowed;
}
[data-ui="v3"] .cra-check.cra-off input:checked::before,
[data-ui="v3"] .cra-choice.cra-off input:checked::before,
[data-ui="v3"] input.cra-check.cra-off:checked::before { background: var(--muted); }
/* Выключенный вариант не подсвечивается как выбранный: у коробки есть
   правило `:has(input:checked)`, и без этой отмены заштрихованный
   «Запланировать» на шаге 4 выглядел бы одновременно и выключенным, и
   выбранным. Довод тот же, что у выключенной галочки строкой выше. */
[data-ui="v3"] .cra-choice--boxed.cra-off:has(input:checked) {
  border-color: color-mix(in srgb, var(--line) 60%, transparent);
  background: var(--surface);
}
[data-ui="v3"] .cra-choice--boxed.cra-off:hover { border-color: color-mix(in srgb, var(--line) 60%, transparent); }

/* Поле ввода — особый носитель: у input нет ::after (замещаемый элемент),
   штриховку нельзя положить псевдоэлементом. Поэтому она едет фоновой
   картинкой самого поля — рисунок и шаг те же, что у ::after выше.
   Атрибута disabled по-прежнему нет (паспорт ст. 7): поле остаётся в
   tab-порядке, а от правки его держит readonly (см. макрос input_off).
   Выпадашку readonly не берёт — её переключение гасит скрипт экрана;
   вид выключенного она получает здесь. */
[data-ui="v3"] .cra-input.cra-off,
[data-ui="v3"] .cra-textarea.cra-off {
  color: var(--muted); cursor: not-allowed;
  border-color: color-mix(in srgb, var(--line) 60%, transparent);
  background-image: repeating-linear-gradient(135deg, color-mix(in srgb, var(--muted) 14%, transparent) 0 2px, transparent 2px 6px);
}
[data-ui="v3"] .cra-input.cra-off::placeholder,
[data-ui="v3"] .cra-textarea.cra-off::placeholder { color: var(--muted); }
/* У выпадашки фон уже занят стрелкой из двух градиентов — штриховка
   встаёт ПЕРВЫМ слоем, стрелка остаётся на своём месте. */
[data-ui="v3"] .cra-select.cra-off {
  color: var(--muted); cursor: not-allowed;
  border-color: color-mix(in srgb, var(--line) 60%, transparent);
  background-image:
    repeating-linear-gradient(135deg, color-mix(in srgb, var(--muted) 14%, transparent) 0 2px, transparent 2px 6px),
    linear-gradient(45deg, transparent 50%, var(--muted) 50%),
    linear-gradient(135deg, var(--muted) 50%, transparent 50%);
  background-position: 0 0, calc(100% - 16px) 55%, calc(100% - 11px) 55%;
  background-size: auto, 5px 5px, 5px 5px;
  background-repeat: repeat, no-repeat, no-repeat;
}
/* Кнопка-значок кита (разд. 12): квадрат 28×28 с рамкой. */
[data-ui="v3"] .cra-ib {
  width: 28px; height: 28px; border: 1px solid var(--line); background: transparent;
  color: var(--text); display: inline-grid; place-content: center;
  font-size: 13px; cursor: pointer; padding: 0; text-decoration: none;
}
[data-ui="v3"] .cra-ib:not(.cra-off):hover { border-color: var(--vc-v3-accent-dim); color: var(--vc-v3-accent-ink); }
[data-ui="v3"] .cra-ib:focus-visible { outline: 2px solid var(--vc-v3-accent-dim); outline-offset: 2px; }
/* Тихая строка-причина рядом с тем, чего нет. Двух носителей под это не
   заводится: вид у причины один, а поводов два, и оба записаны здесь —
   выключенное ДЕЙСТВИЕ («почему нельзя нажать») и пустой БЛОК («почему
   здесь ничего нет»). Второй появился 04.09.2026 вместе с разведением
   состояний (разд. 21а): у пустого блока прочерка нет — объяснять нечего
   кроме самого факта, — и легенда прочерка ему не подходит.
   Тише самого места, о котором говорит: это приписка, а не содержимое. */
[data-ui="v3"] .cra-why { color: var(--muted); font-size: var(--vc-v3-fs-meta); }

/* ══ 21а. ДАННЫХ ПОКА НЕТ — ПРОЧЕРК С НАЗВАННОЙ ПРИЧИНОЙ ═════════════
   Решение владельца 04.09.2026 по DR-66. Штриховкой разд. 21 показывались
   ДВЕ РАЗНЫЕ вещи, и от этого интерфейс читался как нерабочий: под одним
   видом стояли «этой функции в системе нет» и «величины пока не отдаёт
   бэкенд». Владелец их развёл, и разводятся они по вопросу «что сделает
   человек, когда это починят»:

     · ФУНКЦИИ НЕТ — действие без обработчика, настройка, которой нет в
       бэкенде. Появится — по ней нажмут. Остаётся ШТРИХОВКА разд. 21:
       она обещает «пока нельзя», и обещание это честное;
     · ДАННЫХ ПОКА НЕТ — счётчик, которого бэкенд не отдаёт, пустая
       величина, блок без содержимого. Нажимать там нечего и тогда, и
       сейчас: появится не действие, а число. Поэтому ПРОЧЕРК по легенде
       подвала и НИКАКОЙ штриховки — штриховка на пустой величине
       обещает несуществующее действие.

   Причина обязательна в обоих случаях: пусто без сказанного вслух
   «почему» неотличимо от «сломалось» — тот же довод, что в разд. 21.
   Носитель несёт title и aria-disabled и остаётся в обходе клавиатурой
   через tabindex (атрибут disabled не ставится — паспорт ст. 7), иначе
   причину нельзя прочитать без мыши; рамку фокуса даёт общее правило
   слоя в начале файла, своей здесь не нужно.
   Цвет прочерка — общий с .cra-dash (разд. 24), второго не заводится.
   Обе темы работают: цвет собран из --muted, у которого своё значение в
   каждой теме, а штриховки, которая в светлой теме читается иначе, тут
   нет вовсе. */
[data-ui="v3"] .cra-nodata,
[data-ui="v3"] .cra-tile--empty {
  /* Курсор «справка», а не not-allowed: not-allowed говорит о ЗАПРЕТЕ
     действия, а здесь действия нет вовсе — есть объяснение в подсказке,
     и курсор обещает ровно его.
     Плитка без величины названа вторым носителем, а не переведена на
     класс .cra-nodata целиком: состояние здесь у ВЕЛИЧИНЫ, а не у
     коробки. Прочерк внутри плитки уже нарисован .cra-dash (макрос
     tile), и класс состояния на всей плитке добавил бы к этому только
     спор двух правил о цвете самой коробки — притом что подсказка и
     tab-порядок у плитки и так свои. Курсор же нужен по всей её площади:
     подсказка висит на плитке целиком. */
  cursor: help;
}

/* ══ 22. ПЛАШКИ ПОЛНОТЫ — кит v1.4, разд. 13 ═════════════════════════
   «Цвет = отклонение»: «полный» нейтрален, как «собран»; жёлтым только
   недобор. Архивная плашка — приглушённая рамка без заливки. */
[data-ui="v3"] .cra-badge {
  display: inline-block; font: 400 11.5px var(--font-mono); padding: 1px 8px;
  border: 1px solid var(--line); color: var(--text); white-space: nowrap;
}
[data-ui="v3"] .cra-badge--full { border-color: var(--line); color: var(--text); }
[data-ui="v3"] .cra-badge--part {
  border-color: color-mix(in srgb, var(--warn) 55%, transparent);
  color: var(--vc-v3-warn-ink); background: color-mix(in srgb, var(--warn) 12%, transparent);
}
/* Третья ступень шкалы полноты (паспорт ст. 6.1-6.2: полно · частично ·
   мало · нет). Двумя видами четырёх делений не показать, и 45% с 5%
   выглядели одинаково. «Мало» — уже не недобор, а провал, поэтому цвет
   отклонения красный; --archive третьей ступенью не является, она про
   архив, а не про «хуже, чем part». */
[data-ui="v3"] .cra-badge--low {
  border-color: color-mix(in srgb, var(--err) 55%, transparent);
  color: var(--vc-v3-err-ink); background: color-mix(in srgb, var(--err) 12%, transparent);
}
[data-ui="v3"] .cra-badge--archive {
  border-color: color-mix(in srgb, var(--muted) 45%, transparent);
  color: var(--muted); background: transparent;
}
/* Плашка, выключенная с причиной: та же штриховка, что у контролов. */
[data-ui="v3"] .cra-badge.cra-off {
  position: relative; color: var(--muted);
  border-color: color-mix(in srgb, var(--line) 60%, transparent);
}
[data-ui="v3"] .cra-badge.cra-off::after {
  content: ""; position: absolute; inset: 0;
  background: repeating-linear-gradient(135deg, color-mix(in srgb, var(--muted) 14%, transparent) 0 2px, transparent 2px 6px);
  pointer-events: none;
}

/* ══ 23. ВЫПАДАШКА ТУЛБАРА ═══════════════════════════════════════════
   Своего компонента у неё в ките нет, а на экранах она есть (фильтр
   рынка, «Колонки ▾», «Ещё ▾»). Нового не заводим: панель — тот же
   .cra-menu, что у шапки, а хозяин отличается только тем, где он висит
   и как выглядит его кнопка. Расхождение записано в реестр
   product/09-design-discrepancies.md. */
[data-ui="v3"] .cra-dd { position: relative; display: inline-flex; }
[data-ui="v3"] .cra-dd__btn {
  display: inline-flex; align-items: center; gap: 7px; height: var(--vc-v3-ctrl-h);
  padding-inline: 12px; border: 1px solid var(--line); background: var(--surface-2);
  color: var(--text); font: 500 var(--vc-v3-fs-mono) var(--font-body); cursor: pointer;
}
[data-ui="v3"] .cra-dd__btn:hover { border-color: var(--vc-v3-accent-dim); }
[data-ui="v3"] .cra-dd__btn:focus-visible { outline: 2px solid var(--vc-v3-accent-dim); outline-offset: 2px; }
[data-ui="v3"] .cra-dd--set .cra-dd__btn { border-color: var(--vc-v3-accent-dim); color: var(--vc-v3-accent-ink); }
/* 238px — ширина панели из макета (`.dd .pop{min-width:238px}`,
   Crawler_products_v3.1). При общих 210px пункт «Найти ссылки в
   документацию…» переносился на две строки, в макете он в одну.
   Ширина дана ВЫПАДАШКЕ ТУЛБАРА, а не всем .cra-menu: у меню шапки свои
   размеры из своего прототипа (210px разделы, 240px меню пользователя),
   и раздвигать их заодно значило бы править шапку правкой таблицы. */
[data-ui="v3"] .cra-dd .cra-menu {
  inset-block-start: calc(100% + 4px); padding: 6px; z-index: 9; min-width: 238px;
}
/* Выпадашка, стоящая у ПРАВОГО края, раскрывается влево. Панель шире
   кнопки и открывалась от её левого края: у меню «Действия» карточки
   прогона правый край приходился на 1501px при окне 1440 — меню уезжало
   за экран. Для меню строки таблицы такое правило уже было; здесь оно
   дано и хозяину в шапке карточки, и любому, кто попросит модификатором.
   Это ЯВНОЕ НАМЕРЕНИЕ РАЗМЕТКИ и оно остаётся: разметка говорит «здесь
   всегда влево», не дожидаясь замера. Случай, когда место кнопки заранее
   не известно, закрывает автоматический .cra-menu--flip (разд. 14) —
   значения у обоих одни и те же, драться им не о чем. */
[data-ui="v3"] .cra-dd--end .cra-menu,
[data-ui="v3"] .cra-card__act .cra-dd .cra-menu {
  inset-inline-start: auto; inset-inline-end: 0;
}
[data-ui="v3"] .cra-dd--open .cra-menu { display: block; }
[data-ui="v3"] .cra-dd .cra-menu__group { padding-inline: 10px; }
/* Кнопка-значок шапки, выключенная с причиной — БЕЗ ШТРИХОВКИ.

   Штриховка разд. 12 кита рассчитана на блок или кнопку с подписью: там
   есть площадь, по которой полосы читаются как «пока нельзя». На значке
   20×20 в шапке та же штриховка ложится поверх самого знака и делает из
   него грязное пятно — владелец при осмотре стенда прочитал это как дыру
   в интерфейсе, а не как выключенное состояние.

   Поэтому у значка выключённость показывается ПРИГЛУШЕНИЕМ: цвет тише,
   рамки и подложки при наведении не появляется, курсор говорит «нельзя».
   Причина при этом обязательна и никуда не девается — она в подсказке
   (.cra-tip), которая у выключенного значка показывается и по наведению,
   и по фокусу с клавиатуры; из обхода клавиатурой значок не выкидывается
   (aria-disabled, а не disabled — см. макрос iconbtn).

   Расхождение с каноном записано: docs/deviations.md, DR-45. */
[data-ui="v3"] .cra-iconbtn.cra-off {
  position: relative; cursor: not-allowed;
  /* Тише обычного значка, но всё ещё различимо. Приглушение через
     color-mix, а не через opacity: opacity создала бы группу и погасила
     бы вместе со значком его подсказку — то есть саму причину. */
  color: color-mix(in srgb, var(--muted) 55%, transparent);
}
[data-ui="v3"] .cra-iconbtn.cra-off:hover {
  border-color: transparent; background: transparent;
  color: color-mix(in srgb, var(--muted) 55%, transparent);
}
/* Значок-лампочка нарисован SVG-путями по currentColor — приглушение
   доезжает до них тем же цветом, отдельного правила не нужно. */

/* Пункт меню ШАПКИ и МЕНЮ ПОЛЬЗОВАТЕЛЯ, выключенный с причиной, —
   тоже БЕЗ ШТРИХОВКИ (решение владельца 04.09.2026 по DR-66).

   Замер визуального аудита: в меню пользователя заштрихованы семь
   пунктов из пятнадцати. Полосы по всей ширине панели читаются не как
   «этого пока нет», а как «меню сломано»: в шапке нет данных, которые
   можно объяснить, — есть только переходы, и половина панели в полоску
   говорит о системе, а не о разделе. Владелец распространил на всю шапку
   то решение, которое уже принято для её значков (DR-45 выше): в шапке
   выключенное ПРИГЛУШАЕТСЯ, а причина остаётся в подсказке.

   Приглушение через color-mix, а не через opacity, и по той же причине,
   что у значка: opacity создала бы группу и погасила бы вместе с пунктом
   его подсказку — то есть саму причину.

   Носитель выбран зоной, а не классом. .cra-menuitem живёт не только в
   шапке: тем же классом набраны меню колонок, отборов и «Действия»
   тулбара (documents.html, runs.html, карточки), и там штриховка
   остаётся — правило разд. 21 для них не менялось. Проверено грепом по
   app/ui/templates: cra-off у .cra-menuitem сейчас встречается ТОЛЬКО
   в выпадашках тулбара, а голая <a class="cra-off"> — только в шапке;
   правило дано обоим носителям в границах .cra-topbar, чтобы вид зоны
   не зависел от того, каким из двух её соберут завтра.

   Расхождение с каноном разд. 21 записано: docs/deviations.md, DR-66. */
[data-ui="v3"] .cra-topbar .cra-menuitem.cra-off,
[data-ui="v3"] .cra-topbar .cra-menu a.cra-off {
  background: transparent; border-color: transparent;
  color: color-mix(in srgb, var(--muted) 55%, transparent);
}
/* Ховер у выключенного пункта шапки не проявляется: .cra-menu a:hover
   красит подложку --surface-2 и на ссылке без href тоже (:hover в CSS не
   спрашивает про адрес), и пункт выглядел бы наводимым. */
[data-ui="v3"] .cra-topbar .cra-menuitem.cra-off:hover,
[data-ui="v3"] .cra-topbar .cra-menu a.cra-off:hover {
  background: transparent; border-color: transparent;
  color: color-mix(in srgb, var(--muted) 55%, transparent);
}
[data-ui="v3"] .cra-topbar .cra-menuitem.cra-off::after,
[data-ui="v3"] .cra-topbar .cra-menu a.cra-off::after { content: none; }

/* ══ 24. МЕЛОЧИ РАЗМЕТКИ ЭКРАНА ══════════════════════════════════════
   Не компоненты кита, а примитивы, без которых разметка экрана начнёт
   обрастать инлайновыми style= (ст. 9.3 запрещает их полностью).
   Каждый — одно правило и одна причина. */

/* Заголовок страницы. Своего класса у h1 в ките нет, потому что кит —
   про компоненты; заголовок описан стандартом (ст. 3.4, три уровня). */
[data-ui="v3"] .cra-h1 {
  font: 600 var(--vc-v3-fs-page) var(--font-display);
  margin: 0 0 var(--vc-v3-gap-item); color: var(--text);
}

/* Поле поиска в тулбаре: моно, как в макете, — в него набирают коды. */
[data-ui="v3"] .cra-input--search { inline-size: 220px; min-inline-size: 220px; font-family: var(--font-mono); }

/* Групповой разделитель колонок (таблица v1.6): тонкая вертикаль ТОЛЬКО
   между смысловыми группами, не больше трёх на таблицу. */
[data-ui="v3"] .cra-th--sep,
[data-ui="v3"] .cra-td--sep { border-inline-start: 1px solid color-mix(in srgb, var(--line) 70%, transparent); }

/* Прочерк — не ноль: «сбора не было» и «собрано ноль» разные факты, и
   прочерк тише данных, чтобы пустая колонка не спорила с заполненной.

   Тем же правилом красится состояние «данных пока нет» (.cra-nodata,
   разд. 21а): цвет прочерка в интерфейсе ОДИН, и второго объявления ему
   не заводится — разошедшиеся копии одного цвета дают два разных
   прочерка на соседних экранах.
   Третьим селектором назван надстрочник прироста: свой цвет (--warn-ink,
   «обновлено») он получает ниже по файлу, и без явного упоминания здесь
   пустое «~—» вышло бы жёлтым, то есть выглядело бы данными. */
[data-ui="v3"] .cra-dash,
[data-ui="v3"] .cra-nodata,
[data-ui="v3"] .cra-dyn.cra-nodata { color: color-mix(in srgb, var(--muted) 45%, transparent); }

/* Число-ссылка внутри ячейки: полнота остаётся плашкой, ПЕРЕХОД даёт
   число рядом с ней (решение по расхождению 4 экрана 3.1). */
/* Число-переход в ячейке: в ПОКОЕ тихое число, зелёное и с линией — по
   наведению. Зелёного в покое нет по тому же доводу, что у имени объекта
   (разд. 6, DR-71): шесть зелёных подчёркнутых столбцов превращали
   таблицу в гирлянду. Заявка v1-02.
   Цвет покоя — --muted, как в починенном макете (`a.dl{color:var(--muted)}`,
   Crawler_products_v3.1). Здесь стоял --text, и число рядом с плашкой
   полноты выходило ярче самой плашки: взгляд уходил на переход, а не на
   меру собранности, ради которой колонка и заведена. Обычное число
   колонки (.cra-num) цвета не менялось — оно данные, а не переход. */
[data-ui="v3"] a.cra-cellnum {
  margin-inline-start: 7px; font: 500 var(--vc-v3-fs-mono) var(--font-mono);
  color: var(--muted); text-decoration: none;
  border-block-end: 1px solid transparent;
}
[data-ui="v3"] a.cra-cellnum:hover,
[data-ui="v3"] a.cra-cellnum:focus-visible {
  color: var(--vc-v3-accent-ink); border-block-end-color: var(--vc-v3-accent-ink);
}

/* Приписка к имени в плоском виде: вендор продукта. Ссылка внутрь
   системы, поэтому пунктир, как у cra-obj, но тише — это приписка. */
[data-ui="v3"] a.cra-name-sub { color: var(--muted); text-decoration: none; }
[data-ui="v3"] a.cra-name-sub:hover { color: var(--vc-v3-accent-ink); }
/* Приписка видна только в плоском виде: в дереве вендор и так строкой выше. */
[data-ui="v3"] [data-cra-flatonly] { display: none; }
[data-ui="v3"] .cra-table--flat [data-cra-flatonly] { display: inline; }
/* В плоском виде иерархии нет: направляющие уровня и контрол свёртывания
   убираются вместе с ней. Разметку строк сервер отдаёт одну на оба вида —
   вторая копия таблицы разошлась бы с первой при первой же правке. */
[data-ui="v3"] .cra-table--flat .cra-guide,
[data-ui="v3"] .cra-table--flat .cra-tree-tg { display: none; }

/* Отступ между блоками страницы (паспорт: шаг --vc-v3-gap-block). */
[data-ui="v3"] .cra-blockgap { margin-block-start: var(--vc-v3-gap-block); }

/* ── ТЕЛО КАРТОЧКИ В ДВЕ КОЛОНКИ ─────────────────────────────────────
   Узкая колонка фиксированной ширины плюс тянущаяся, и ряд во всю ширину
   под ними: `.pcols{grid-template-columns:340px 1fr;gap:22px}` и
   `.pcols .row2{grid-column:1/-1;grid-template-columns:1fr 1fr}`
   (карточка документа v3.4). Заявка v3-02.
   Ближайшего в наборе не было: `.cra-tiles` — колонки РАВНОЙ ширины под
   плитки, `.cra-nr-grid` — колонки формы «Новый сбор»; ни то ни другое не
   даёт «340 плюс остаток», и звать их на карточке значило бы называть
   раскладку чужим словом. Отступ секции внутри колонки снимается по тому
   же доводу, что в паре блоков ниже. */
[data-ui="v3"] .cra-cardcols {
  display: grid; grid-template-columns: 340px 1fr; gap: 22px;
  align-items: start; margin-block-start: 18px;
}
[data-ui="v3"] .cra-cardcols__row2 {
  grid-column: 1 / -1; display: grid; grid-template-columns: 1fr 1fr; gap: 22px;
}
[data-ui="v3"] .cra-cardcols > .cra-section,
[data-ui="v3"] .cra-cardcols__row2 > .cra-section { margin-block: 0; }
@media (max-width: 1100px) {
  [data-ui="v3"] .cra-cardcols,
  [data-ui="v3"] .cra-cardcols__row2 { grid-template-columns: 1fr; }
}

/* ── ПРЕВЬЮ ТЕКСТА ДОКУМЕНТА ─────────────────────────────────────────
   Извлечённый текст — это ПРОЗА, а не машинный вывод: набирается Inter
   с межстрочным 1.7, обрезается снизу градиентом в поверхность панели, и
   поверх градиента по центру стоит переход «Открыть превью целиком».
   Раньше он показывался блоком вывода `.cra-out` — моноширинной коробкой
   с рамкой: тот компонент для журнала и отчёта сухого прогона, где
   важна колонка символов. Значения — из макета карточки документа v3.4
   (`.pv-teaser`, `.pv-open`). Заявка v3-06. */
[data-ui="v3"] .cra-teaser {
  position: relative; overflow: hidden; max-block-size: 300px;
  padding: 18px 20px; font: 400 13.5px/1.7 var(--font-body);
  color: color-mix(in srgb, var(--text) 88%, var(--muted));
  white-space: pre-wrap; word-break: break-word;
}
[data-ui="v3"] .cra-teaser::after {
  content: ""; position: absolute; inset-inline: 0; inset-block-end: 0;
  block-size: 120px; background: linear-gradient(transparent, var(--surface));
  pointer-events: none;
}
[data-ui="v3"] .cra-teaser h4 { font: 600 14px var(--font-body); color: var(--text); margin: 16px 0 6px; }
[data-ui="v3"] .cra-teaser h4:first-child { margin-block-start: 0; }
/* Раскрытое превью: обрезки нет, значит нет и градиента над текстом. */
[data-ui="v3"] .cra-teaser--full { max-block-size: none; }
[data-ui="v3"] .cra-teaser--full::after { display: none; }
[data-ui="v3"] .cra-teaser__open {
  display: flex; justify-content: center; padding-block-end: 16px;
  margin-block-start: -40px; position: relative; z-index: 2;
}

/* ── РЯД ИЗ ДВУХ БЛОКОВ ──────────────────────────────────────────────
   Раскладка, а не компонент: сетка под БЛОКИ, как `.cra-tiles` — сетка
   под плитки. Значения из макетов: `.row2{grid-template-columns:1fr 1fr;
   gap:16px}` (дашборд v1.9, применён дважды) и `.two` карточки рынка
   v2.8 с переопределением `3fr 1fr` на вкладке «Обзор».
   Заведено по заявке v1-01. Обходились `.cra-tiles`, и это давало вот
   что: у `.cra-section` межгрупповой отступ сверху, а обнуляет его
   только `:first-child` — первая колонка вставала вплотную, вторая
   уезжала на 26px вниз, и ряд выходил ступенькой. Отсюда правило ниже:
   внутри ряда отступ секции снимается у ЛЮБОЙ колонки, а не у первой.
   Экранам это снимает обёртку вокруг каждой панели. */
[data-ui="v3"] .cra-pair {
  display: grid; grid-template-columns: 1fr 1fr; gap: 16px;
  margin-block: var(--vc-v3-gap-group);
}
[data-ui="v3"] .cra-pair--wide { grid-template-columns: 3fr 1fr; }
[data-ui="v3"] .cra-pair > .cra-section { margin-block: 0; }
/* Коробка тянется до высоты соседки (заявка В2-02). Ячейки сетки и так
   равной высоты, но при метке НАД коробкой (head_out=true) секция —
   обычный блок: коробка внутри неё занимает столько, сколько ей нужно, и
   более короткая колонка не доходит до низа. Ряд из-за этого выглядит
   рваным там, где в макете он ровный: `.two>.blk{display:flex;
   flex-direction:column}` плюс `.cb{flex:1}` (карточка рынка v2.8).
   Правило говорит про КОРОБКУ, а не про содержимое: тянется рамка,
   строки внутри не растягиваются и не разъезжаются. */
/* Колонка сетки НЕ сжимается ниже своего содержимого, пока у неё
   min-width: auto — это умолчание grid, и именно из-за него дашборд
   выталкивал документ вбок на 202 px даже при 1280 (приёмка 7.24.0):
   таблица «Последние документы» шире колонки, колонка растёт под неё,
   ряд перестаёт помещаться. Ширину держит сетка, а длинное содержимое
   прокручивается внутри своей .cra-tablescroll — она для того и есть. */
[data-ui="v3"] .cra-pair > .cra-section,
[data-ui="v3"] .cra-cardcols > .cra-section,
[data-ui="v3"] .cra-cardcols__row2 > .cra-section { min-inline-size: 0; }
[data-ui="v3"] .cra-pair > .cra-section { display: flex; flex-direction: column; }
[data-ui="v3"] .cra-pair .cra-section__box { flex: 1; }
/* Узкое окно: колонка одна. Значение из тех же макетов (1100px). */
@media (max-width: 1100px) {
  [data-ui="v3"] .cra-pair,
  [data-ui="v3"] .cra-pair--wide { grid-template-columns: 1fr; }
}

/* Дополнительное действие внутри тела модалки («Показать, что найдётся»):
   оно не главное и потому живёт не в подвале, а в теле, над отчётом. */
[data-ui="v3"] .cra-modal__extra { margin-block: var(--vc-v3-gap-item); }

/* Строка, скрытая отбором. hidden не годится: у <tr> он перебивается
   собственным display таблицы в части браузеров. */
[data-ui="v3"] .cra-row--hidden { display: none; }

/* Фиксированные ширины колонок с плашками (паспорт ст. 5). Отдельно от
   пропорционального закрытого набора: там доля таблицы, здесь ровно
   столько пикселей, сколько занимает самая длинная плашка набора.
   Ячейка не переносится: плашка полноты и число-переход стоят в строку —
   в макете они на одной линии, и перенос читается как две разные ячейки. */
[data-ui="v3"] .cra-col--badge172 { inline-size: var(--col-172); min-inline-size: var(--col-172); }
[data-ui="v3"] .cra-col--badge150 { inline-size: var(--col-150); min-inline-size: var(--col-150); }
[data-ui="v3"] td[data-col="td"], [data-ui="v3"] td[data-col="mk"] { white-space: nowrap; }
/* Число и приписка к нему («349 (53 своих)») — одна величина и один
   взгляд: перенос делает из неё две строки и ломает сравнение по столбцу. */
[data-ui="v3"] td.cra-num { white-space: nowrap; }

/* Пояснение внутри выпадашки: это не подзаголовок группы, а фраза —
   капслок и разрядка подзаголовка на ней не читаются. */
[data-ui="v3"] .cra-menu__note {
  padding: 7px 10px 3px; color: var(--muted);
  font: 400 var(--vc-v3-fs-label) var(--font-body); max-inline-size: 260px;
}

/* ══ 25. КАРТОЧКА КАК СТРАНИЦА ═══════════════════════════════════════
   Носитель карточки — выезжающая панель (Crawler_card_v1.6), но адрес у
   неё свой (ст. 6.1: полная карточка — страница). Значит сцена занимает
   всю рабочую область, а не висит поверх списка: списка под ней нет,
   рисовать его копией значило бы держать две «Продукты» сразу. Панель
   при этом остаётся собой — та же ширина, тот же левый борт, тот же
   выезд; закрытие ведёт назад по адресу. */
[data-ui="v3"] .cra-stage--page { position: relative; min-block-size: calc(100vh - 54px); }
[data-ui="v3"] .cra-stage--page .cra-stage__scrim { position: absolute; }
/* ── Почему у карточки-СТРАНИЦЫ нет ни затемнения, ни узкой панели ──
   Выезжающая панель занимает 92% и затемняет остальное потому, что за
   ней стоит список, из которого её открыли: затемнение говорит «список
   на месте, но сейчас не он». У карточки-страницы за панелью нет
   НИЧЕГО — адрес свой, списка под ним не рисуется. Затемнять пустоту
   нечем, и 8% сцены слева читались ровно так, как их прочитал владелец
   при осмотре стенда: широкая серая колонка, которой нет в макете
   (в макете на её месте виден настоящий список).
   Поэтому на карточке-странице панель занимает всю ширину сцены, а
   затемнение не рисуется вовсе. Панель остаётся собой: тот же левый
   борт --accent, тот же выезд, то же закрытие назад по адресу. Правило
   общее для всех карточек-страниц — вендор, продукт, документ, прогон,
   — а не заплатка под один экран. Запись DR-46. */
/* Карточка-страница стоит В ПОТОКЕ, а выезжает СДВИГОМ.
   Выезжающая панель позиционирована абсолютно и прокручивает своё тело —
   ей так и надо: она во всю высоту экрана поверх списка. Карточка-
   страница — это страница, и прокручиваться должна страница. Пока она
   была абсолютной, сцена не могла вырасти под содержимое, тело
   скроллилось само (замер: видно 840px при содержимом 1242, полосы у
   страницы нет), и из шести разделов карточки документа на экране
   оказывались два.
   Поэтому здесь position: static — сцена растёт по содержимому, — а
   выезд даёт transform, как в макете (`.panel{transform:translateX(100%)}`
   → `.panel.on{transform:translateX(0)}`). Выезд сохранён, прокрутка
   вернулась странице.
   Борт слева нейтральный: акцентная вертикаль отделяет ПАНЕЛЬ от списка
   под ней, а под страницей ничего нет — в макете там `border-left: 1px
   solid var(--line)`. Запись DR-46 про ширину и затемнение в силе. */
[data-ui="v3"] .cra-stage--page .cra-card {
  position: static; width: auto;
  border-inline-start: 1px solid var(--line);
  transform: translateX(101%); transition: transform .22s;
}
[data-ui="v3"] .cra-stage--page.cra-stage--open .cra-card { transform: none; }
[data-ui="v3"] .cra-stage--page .cra-card__body { overflow: visible; }
[data-ui="v3"] .cra-stage--page .cra-stage__scrim { display: none; }
/* Заголовок карточки-страницы: тот же кегль, что у h3 панели, но это
   единственный h1 страницы (ст. 3.4 — заголовок на странице один). */
[data-ui="v3"] .cra-card__title h1 { font-size: 22px; margin: 0; min-inline-size: 0; overflow-wrap: anywhere; }

/* ══ 26. МАТРИЦА СВОДКИ ══════════════════════════════════════════════
   Таблица «строка-показатель × колонка-класс» на обзоре карточки. Это
   таблица кита с одной особенностью: первая колонка — не данные, а
   подпись показателя, и она тише остальных. */
[data-ui="v3"] .cra-matrix td:first-child { color: var(--muted); white-space: nowrap; }
[data-ui="v3"] .cra-matrix th:first-child { inline-size: 200px; }

/* Счётчик у вкладки карточки: число рядом с подписью. Жёлтый — там, где
   счётчик показывает отклонение (диагностика), нейтральный во всех
   остальных: цвет = отклонение. */
/* Счётчик у подписи. Живёт и у вкладки карточки, и у пункта сегмента
   («Все 16 · Мои загрузки 9», источники v2.3): это одно и то же число
   рядом с подписью, и второго компонента под него не заводится —
   правило просто перестало быть привязанным к вкладкам. Заявка v3-07. */
[data-ui="v3"] .cra-seg > button.cra-seg--on .cra-tabcnt,
[data-ui="v3"] .cra-seg > a.cra-seg--on .cra-tabcnt { color: var(--vc-v3-accent-ink); }
[data-ui="v3"] .cra-tabcnt {
  font: 500 10.5px var(--font-mono); color: var(--muted);
  background: var(--surface-2); border: 1px solid color-mix(in srgb, var(--line) 70%, transparent);
  padding: 1px 7px; margin-inline-start: 7px;
}
[data-ui="v3"] .cra-tabcnt--warn {
  color: var(--vc-v3-warn-ink); border-color: color-mix(in srgb, var(--warn) 45%, transparent);
}
/* Строка «показано N из M» под таблицей вкладки: слева счёт, справа
   переход в полный реестр. */
[data-ui="v3"] .cra-shownrow {
  display: flex; justify-content: space-between; align-items: center;
  margin-block-start: var(--vc-v3-gap-item); font-size: var(--vc-v3-fs-data);
}
[data-ui="v3"] .cra-shownrow__count { font: var(--vc-v3-fs-mono) var(--font-mono); color: var(--muted); }

/* Приглушённая вкладка — раздел, в котором НЕТ ДАННЫХ (решение владельца
   04.09.2026 по DR-66; разд. 21а). Разбор носителя:

   До этой правки вкладка гасилась штриховкой, курсором not-allowed и
   атрибутом aria-disabled, по которому поведение слоя запрещало
   переключение. Основание было такое: «показывать в ней нечего, и это
   честнее, чем открыть и показать пустоту». Основание не подтвердилось —
   панели у всех приглушённых вкладок УЖЕ НАПИСАНЫ, и в них лежит честная
   пустота со словами, чего именно нет и куда идти за ближайшим
   (projects.html, product_card.html, vendor_card.html). То есть дверь
   была заперта перед готовой комнатой: человек видел полосы и
   «нельзя», а за ними лежало объяснение, которое ему и нужно.

   Правило владельца разводит вкладку однозначно: за ней нет ДАННЫХ, а не
   функции. Появится тут не действие, а содержимое. Значит:
     · штриховки нет — она обещает «нажать нельзя», а нажать можно;
     · курсор pointer, а не not-allowed: not-allowed на открывающейся
       вкладке — прямая ложь указателем;
     · aria-disabled макрос больше не ставит (см. tablist в card.html):
       по нему поведение слоя блокировало клик, и диктор объявлял
       «отключено» о вкладке, которая открывается;
     · приглушение цветом ОСТАЁТСЯ — это и есть прочерк в масштабе
       вкладки: «зайти можно, внутри пусто». Причина, как и у прочерка,
       обязательна и живёт в подсказке.

   Цвет смешивается с ПОВЕРХНОСТЬЮ, а не с прозрачностью. Разница не
   косметическая: прозрачный текст просвечивает всё, что под ним, и
   паспорт её запрещает; смесь с поверхностью — обычный сплошной цвет.
   Просто взять --muted нельзя: у невыключенной вкладки он же и есть,
   и приглушать было бы нечем — вкладка перестала бы отличаться. */
[data-ui="v3"] .cra-tabs a.cra-tabs--muted {
  cursor: pointer;
  color: color-mix(in srgb, var(--muted) 65%, var(--surface));
}
/* Наведение оживляет её, как и всякую вкладку: общее правило .cra-tabs a
   выше даёт --text. Прежнего запрета на ховер здесь больше нет — он
   говорил «мимо», а вкладка кликается. */
/* Причина у приглушённой вкладки читается и с клавиатуры: подсказка
   показывалась только по наведению, и тот, кто идёт табом, узнать её не
   мог. Вкладка адреса не имеет (макрос не выдумывает маршрут), в обходе
   держится через tabindex — значит фокус на ней бывает. */
[data-ui="v3"] .cra-tabs a:focus-visible .cra-tip { display: block; }

/* ══ 27. ЭКРАН «ДОКУМЕНТЫ»: СВОДКА, РЯД ФИЛЬТРОВ, РЕЗУЛЬТАТЫ ══════════
   Не новые компоненты: строка сводки и ряд фильтров — раскладки уже
   принятых элементов (счётчик, выпадашка тулбара, чип). Правила ниже
   задают только то, как они стоят на этом экране. */

/* Заголовок и переключатель режимов — одной строкой (макет v3.1). */
[data-ui="v3"] .cra-headrow { display: flex; align-items: center; gap: var(--vc-v3-gap-group); flex-wrap: wrap; }
[data-ui="v3"] .cra-headrow .cra-h1 { margin-block-end: 0; }

/* Сегмент как ссылки: режим живёт в адресе, и его можно переслать. */
[data-ui="v3"] .cra-seg > a {
  display: inline-flex; align-items: center; padding-inline: 12px; height: 34px;
  color: var(--muted); text-decoration: none;
  font: 500 var(--vc-v3-fs-meta) var(--font-body);
  border-inline-end: 1px solid var(--line);
}
[data-ui="v3"] .cra-seg > a:last-child { border-inline-end: none; }
[data-ui="v3"] .cra-seg > a.cra-seg--on { background: var(--surface-2); color: var(--vc-v3-accent-ink); }

/* Большое поле поиска: главный контрол режима, поэтому крупнее обычного. */
[data-ui="v3"] .cra-bigsearch {
  display: flex; gap: 12px; align-items: center; background: var(--surface);
  border: 1.5px solid var(--vc-v3-accent-dim); padding: 6px 8px 6px 16px;
  margin-block-end: var(--vc-v3-gap-item);
}
[data-ui="v3"] .cra-bigsearch:focus-within { border-color: var(--accent); }
[data-ui="v3"] .cra-bigsearch__q {
  flex: 1; background: transparent; border: 0; outline: none; color: var(--text);
  font: 400 16px var(--font-body); padding: 10px 4px; min-inline-size: 0;
}
[data-ui="v3"] .cra-bigsearch__help {
  flex: none; inline-size: 26px; block-size: 26px; display: grid; place-content: center;
  border: 1px solid var(--line); color: var(--muted);
  font: 600 var(--vc-v3-fs-meta) var(--font-mono); cursor: help;
}

/* Строка сводки: что отобрано и сколько, плюс выгрузка отбора справа.
   Это ПЛАШКА, а не строка текста: `.sumline{background:var(--surface);
   border:1px solid var(--line);padding:11px 16px;margin-bottom:12px}`
   (документы v3.1). Без фона и рамки сводка и кнопка «Экспорт отбора»
   висели прямо на фоне страницы, а в макете кнопка лежит ВНУТРИ плашки. */
[data-ui="v3"] .cra-sumline {
  display: flex; gap: var(--vc-v3-gap-group); align-items: center; flex-wrap: wrap;
  background: var(--surface); border: 1px solid var(--line);
  padding: 11px 16px; margin-block-end: 12px;
  font-size: var(--vc-v3-fs-data);
}
[data-ui="v3"] .cra-sumline__end { margin-inline-start: auto; }

/* Ряд фильтров: выпадашки тулбара, стоящие над таблицей, а не в ней. */
[data-ui="v3"] .cra-filterrow {
  display: flex; gap: var(--vc-v3-gap-inner); align-items: center; flex-wrap: wrap;
  margin-block-end: var(--vc-v3-gap-inner);
}
[data-ui="v3"] .cra-filterrow__inline { display: inline-flex; margin: 0; }
[data-ui="v3"] .cra-input--tiny { inline-size: 120px; min-inline-size: 120px; font-family: var(--font-mono); }
[data-ui="v3"] a.cra-chip__x { text-decoration: none; color: inherit; }

/* Результаты поиска: номер, путь, название, метаданные. Мера строки —
   как у связного текста: длинную строку результата читать нечем. */
[data-ui="v3"] .cra-hits { max-inline-size: 76ch; }
[data-ui="v3"] .cra-hit { display: flex; gap: var(--vc-v3-gap-item); margin-block-end: 22px; }
[data-ui="v3"] .cra-hit__n { flex: none; inline-size: 26px; text-align: end; color: var(--muted); font: 400 var(--vc-v3-fs-data) var(--font-mono); padding-block-start: 2px; }
[data-ui="v3"] .cra-hit__body { min-inline-size: 0; }
[data-ui="v3"] .cra-hit__crumb { font: 400 var(--vc-v3-fs-meta) var(--font-mono); color: var(--muted); margin-block-end: 2px; }
[data-ui="v3"] .cra-hit__sep { opacity: .5; }
[data-ui="v3"] .cra-hit__title { font: 500 17px var(--font-body); line-height: 1.3; }
[data-ui="v3"] .cra-hit__title a { color: var(--vc-v3-accent-ink); text-decoration: none; }
[data-ui="v3"] .cra-hit__title a:hover { border-block-end: 1px dotted var(--vc-v3-accent-dim); }
[data-ui="v3"] .cra-hit__meta { font: 400 var(--vc-v3-fs-meta) var(--font-body); color: var(--muted); margin-block-start: 3px; }

/* Код объекта перед его именем: моно, тише имени. Читается как адрес, а
   не как часть названия (экран «Рынки», где код — сквозной счётчик). */
[data-ui="v3"] .cra-code {
  font: 400 var(--vc-v3-fs-label) var(--font-mono); color: var(--muted);
  margin-inline-end: var(--vc-v3-gap-inner); flex: none;
}

/* ══ 28. ПЛИТКА ПОКАЗАТЕЛЯ ═══════════════════════════════════════════
   Сводка карточки набирается плитками. Своего компонента в ките нет:
   плитка собрана из принятого — поверхность и рамка как у полей, число
   моно, как все числа интерфейса. Запись DR-20 в реестре расхождений. */
/* Ряд плиток — СЕТКА равных колонок во всю ширину, а не резиновый флекс.
   В макетах ряд задан числом: `repeat(5,1fr)` на дашборде и карточке
   рынка, `repeat(6,1fr)` и `repeat(7,1fr)` на карточке прогона, зазор 14.
   Флекс `1 1 150px` с потолком 260px давал другое: восемь плиток по
   160px не доходили до правого края, а девятая переносилась и выходила
   заметно ШИРЕ остальных — ряд ломался ровно там, где макет ровный.
   Число колонок берётся из числа плиток (до семи — все в одну строку,
   как в макетах), дальше сетка сама набирает столько колонок, сколько
   влезает по 150px. Считать плитки приходится селектором, потому что
   разметку рядов пишут одиннадцать разных экранов и модификатор
   «сколько колонок» они проставляли бы вручную и вразнобой. */
[data-ui="v3"] .cra-tiles {
  display: grid; gap: var(--vc-v3-gap-item);
  grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
}
[data-ui="v3"] .cra-tiles:has(> :nth-child(1):last-child) { grid-template-columns: repeat(1, 1fr); }
[data-ui="v3"] .cra-tiles:has(> :nth-child(2):last-child) { grid-template-columns: repeat(2, 1fr); }
[data-ui="v3"] .cra-tiles:has(> :nth-child(3):last-child) { grid-template-columns: repeat(3, 1fr); }
[data-ui="v3"] .cra-tiles:has(> :nth-child(4):last-child) { grid-template-columns: repeat(4, 1fr); }
[data-ui="v3"] .cra-tiles:has(> :nth-child(5):last-child) { grid-template-columns: repeat(5, 1fr); }
[data-ui="v3"] .cra-tiles:has(> :nth-child(6):last-child) { grid-template-columns: repeat(6, 1fr); }
[data-ui="v3"] .cra-tiles:has(> :nth-child(7):last-child) { grid-template-columns: repeat(7, 1fr); }
/* Восемь и девять — по заявке В2-02/В2-03 (карточка рынка). Потолок в
   семь стоял не по замыслу, а по числу рядов, которые встречались на тот
   день; ряд из девяти по auto-fit набирал восемь колонок и переносил
   девятую плитку отдельной строкой во всю ширину — ровно та поломка,
   ради которой ряд и переведён с флекса на сетку.
   Замер заявителя на карточке рынка: девять плиток по 118px, высота ряда
   85px как в макете, обрезки нет.
   Число колонок — не разрешение ставить их сколько угодно: подпись
   плитки капсом («РЫНКИ С ДАННЫМИ») на 123px ломается на две строки, и
   дашборд по этой причине раскладывает свои девять двумя ровными рядами
   5+4 (заявка В1-05 отклонена этим доводом). Правило говорит, что ряд из
   девяти НЕ РАССЫПЛЕТСЯ; годится ли он на этом экране — решает экран. */
[data-ui="v3"] .cra-tiles:has(> :nth-child(8):last-child) { grid-template-columns: repeat(8, 1fr); }
[data-ui="v3"] .cra-tiles:has(> :nth-child(9):last-child) { grid-template-columns: repeat(9, 1fr); }
[data-ui="v3"] .cra-tile {
  min-inline-size: 0;
  background: var(--surface); border: 1px solid var(--line);
  padding: 12px 14px; text-decoration: none; color: var(--text); display: block;
}
[data-ui="v3"] a.cra-tile:hover { border-color: var(--vc-v3-accent-dim); }
/* Ряд показателей, стоящий ВНУТРИ панели: коробка одна на весь ряд, у
   самих показателей ни рамки, ни фона — так собраны `.docsgrp` и «Состояние
   системы» на дашборде v1.9. Поля не рисуются: их даёт тело панели.
   Заявка v1-03. */
[data-ui="v3"] .cra-tiles--plain > .cra-tile {
  background: transparent; border: 0; padding: 0;
}
[data-ui="v3"] .cra-tile__k {
  display: block; font: 600 10px var(--font-body); letter-spacing: .12em;
  text-transform: uppercase; color: var(--muted);
}
/* 22px — значение макетов (`.tile .v{font:500 22px}` в дашборде, карточке
   рынка и карточке прогона). 18px здесь были округлением «на глаз», и
   главное число сводки читалось тише подписей вокруг. */
[data-ui="v3"] .cra-tile__v {
  display: block; font: 500 22px var(--font-mono);
  margin-block-start: 6px; white-space: nowrap;
  /* В сетке равных колонок соседняя плитка рисуется ПОВЕРХ вылезшего
     числа, и длинное значение выглядело бы обрезанным ножом. Обрезка
     многоточием — та же мера, что у имени в таблице (ст. 8.5): она хотя
     бы честно говорит, что значение длиннее места. Место при этом
     лечится не здесь: вторая величина показателя — это подстрочник
     .cra-tile__s (макрос tile(sub=…)), а не хвост главного числа. */
  overflow: hidden; text-overflow: ellipsis;
}
[data-ui="v3"] a.cra-tile .cra-tile__v { color: var(--vc-v3-accent-ink); }
/* Подстрочник величины: «страниц: 91 481», «сейчас 12 % · пик 84 %» —
   вторая, более мелкая величина того же показателя. Отдельной строкой
   под числом: приписанная к числу, она читалась бы как часть числа. */
[data-ui="v3"] .cra-tile__s {
  display: block; font: 400 var(--vc-v3-fs-meta) var(--font-mono);
  color: var(--muted); margin-block-start: 4px;
}
/* Плитка без величины — это «ДАННЫХ ПОКА НЕТ», а не «функции нет»
   (разд. 21а, решение владельца 04.09.2026 по DR-66): считать плитке
   нечего, потому что бэкенд не отдаёт числа, — нажимать в ней нечего и
   после починки. Поэтому штриховки у неё больше нет: до 04.09.2026 она
   приезжала общим ::after разд. 21, и пятнадцать заштрихованных плиток
   дашборда читались как поломка экрана.
   Осталось то, что говорит правду: прочерк вместо числа (его ставит
   макрос tile), подпись тише обычной и причина в title. Плитка получает
   tabindex (тот же макрос): у неинтерактивного узла title диктору не
   объявляется, и «названная причина» держалась бы только на глазах. */
[data-ui="v3"] .cra-tile--empty .cra-tile__k { color: var(--muted); }
[data-ui="v3"] .cra-tile--empty:focus-visible { outline: 2px solid var(--vc-v3-accent-dim); outline-offset: 2px; }

/* ══ 28а. ПОМЕТКА «НОВЫЙ» / «ОБНОВЛЁН» И НАДСТРОЧНИК ПРИРОСТА ════════
   Единственное место темы, где радиус не 0: паспорт ст. 5 разрешает
   плашке-пометке 3px и НИГДЕ больше — 3px отличают пометку от плашки
   статуса и от чипа с первого взгляда. Здесь же единственное применение
   пары --info / --info-ink (паспорт ст. 2–3): синий значит «появилось
   впервые», жёлтый — «изменилось». Заводить их где-то ещё нельзя. */
[data-ui="v3"] .cra-mk {
  display: inline-block; font: 500 10.5px var(--font-mono);
  padding: 1px 7px; border-radius: 3px; white-space: nowrap;
  border: 1px solid transparent;
}
[data-ui="v3"] .cra-mk--new {
  background: color-mix(in srgb, var(--info) 18%, transparent);
  border-color: color-mix(in srgb, var(--info) 55%, transparent);
  color: var(--vc-v3-info-ink);
}
[data-ui="v3"] .cra-mk--upd {
  background: color-mix(in srgb, var(--warn) 14%, transparent);
  border-color: color-mix(in srgb, var(--warn) 55%, transparent);
  color: var(--vc-v3-warn-ink);
}
/* Надстрочник прироста у числа плитки: «+8» новых, «~44» обновлённых.
   vertical-align: top, а не sup по умолчанию, — иначе он приподнимает
   строку с числом и плитка выше соседних. */
[data-ui="v3"] .cra-dyn {
  font: 500 12px var(--font-mono); color: var(--vc-v3-info-ink);
  vertical-align: top; margin-inline-start: 5px;
}
[data-ui="v3"] .cra-dyn--upd { color: var(--vc-v3-warn-ink); }
/* Надстрочник без числа («~—») — «ДАННЫХ ПОКА НЕТ», разд. 21а: прироста
   не отдаёт бэкенд, а не «функции нет». Своего правила ему не нужно:
   цвет прочерка он берёт в общем правиле .cra-nodata (разд. 24), где
   назван явно, — иначе его перекрасил бы --warn-ink соседней строкой.
   Прежнее `display: inline-block` снято вместе со штриховкой: оно
   существовало только затем, чтобы у sup была коробка под ::after. */

/* Сводка под подписью пункта рейки шагов: «что уже выбрано». Отдельная
   строка, а не приписка в ту же, — иначе подпись шага и её сводка
   сливаются в одну фразу. */
[data-ui="v3"] .cra-rail__s {
  display: block; font-size: var(--vc-v3-fs-meta); color: var(--muted);
  margin-block-start: 2px;
}

/* Однострочная ячейка ЛЮБОЙ колонки: обрезка многоточием, полное
   значение — в title (ст. 8.5, А14). До сих пор такой приём был только
   у имени (.cra-name внутри .cra-namecell), и колонки вроде «Класс»
   переносились на вторую строку, разгоняя строку с 34px до 42.
   Работает там же, где и .cra-name: у таблицы есть тянущаяся колонка
   (cra-col--grow), которая забирает остаток и не даёт соседкам расти. */
[data-ui="v3"] .cra-cellone {
  display: block; white-space: nowrap; overflow: hidden;
  text-overflow: ellipsis; min-inline-size: 0;
}

/* Приписка к имени в ячейке (cell_name(suffix=…)): у .cra-namecell
   gap:0, и без своего отступа приписка слипалась с именем — «crawl№1». */
[data-ui="v3"] .cra-namecell .cra-sub { margin-inline-start: var(--vc-v3-gap-inner); }

/* Быстрые действия в строке таблицы: появляются по наведению (паспорт
   ст. 7) — иначе ряд значков в каждой строке спорит с данными. */
[data-ui="v3"] .cra-quick { white-space: nowrap; }
[data-ui="v3"] .cra-quick .cra-ib { opacity: 0; transition: opacity .12s; }
[data-ui="v3"] .cra-row:hover .cra-quick .cra-ib,
[data-ui="v3"] .cra-row:focus-within .cra-quick .cra-ib { opacity: 1; }
/* Меню строки «⋯» — та же выпадашка, что в тулбаре, только кнопка тише. */
[data-ui="v3"] .cra-rowmenu .cra-dd__btn {
  height: 26px; padding-inline: 8px; border-color: transparent; background: transparent;
  opacity: 0; transition: opacity .12s;
}
/* Тот же довод, что у быстрых действий выше: контрол строки в покое не
   спорит с данными. У ОТКРЫТОГО меню кнопка обязана остаться видимой —
   иначе, стоит мыши уйти со строки, меню повиснет без хозяина. */
[data-ui="v3"] .cra-row:hover .cra-rowmenu .cra-dd__btn,
[data-ui="v3"] .cra-row:focus-within .cra-rowmenu .cra-dd__btn,
[data-ui="v3"] .cra-rowmenu .cra-dd__btn:focus-visible,
[data-ui="v3"] .cra-rowmenu .cra-dd--open .cra-dd__btn { opacity: 1; }
[data-ui="v3"] .cra-rowmenu .cra-dd__btn .cra-caret { display: none; }
[data-ui="v3"] .cra-rowmenu .cra-menu { inset-inline-start: auto; inset-inline-end: 0; }
/* Сводка, вставленная ВНУТРЬ оболочки таблицы, а не стоящая над ней:
   рамку и фон рисует оболочка, у сводки остаётся только линия снизу —
   иначе внутри коробки появилась бы вторая рамка. */
[data-ui="v3"] .cra-sumline--inset {
  background: transparent; border: 0;
  border-block-end: 1px solid var(--line);
  padding-inline: 14px; margin-block-end: 0;
}

/* ══ 29. ПАНЕЛЬ ОПЕРАЦИИ И ФОРМА «НОВЫЙ СБОР» ════════════════════════
   Модалка-панель: операция во всю высоту справа (Crawler_modal_v1.3,
   ст. 6.1). От обычной модалки отличается только тем, что не по центру
   и во весь рост — компонент тот же. */
[data-ui="v3"] .cra-modalscrim--panel .cra-modal--panel {
  position: fixed; inset-block: 0; inset-inline-end: 0;
  inline-size: min(1080px, 94vw); max-inline-size: none; max-block-size: none;
  display: flex; flex-direction: column;
  border-inline-start: 2px solid var(--accent);
}
[data-ui="v3"] .cra-nr-body { overflow: auto; flex: 1; }
[data-ui="v3"] .cra-nr-name { flex: 1; min-inline-size: 0; margin-inline: var(--vc-v3-gap-item); }
[data-ui="v3"] .cra-nr-grid { display: flex; gap: var(--vc-v3-gap-group); align-items: flex-start; }
[data-ui="v3"] .cra-nr-form { flex: 1; min-inline-size: 0; }
[data-ui="v3"] .cra-nr-est {
  font: 400 var(--vc-v3-fs-mono) var(--font-mono); color: var(--muted);
  margin-inline-end: auto; min-inline-size: 0; overflow: hidden; text-overflow: ellipsis;
}

/* Рейка шагов формы: где я и что уже выбрано. Пункты — кнопки: адреса у
   шага нет, а ссылка без адреса лжёт.
   У ТОЙ ЖЕ рейки в разделе «Система» (sysnav) адреса есть, и там пункт —
   настоящая ссылка. Отсюда подчёркивание у всех одиннадцати пунктов:
   правило писалось на кнопку и сброса не имело. В макете рейки его нет
   прямо — `.srail a{text-decoration:none}` (Crawler_account_v1.0). */
[data-ui="v3"] .cra-rail {
  position: sticky; inset-block-start: 0; flex: none; inline-size: 210px;
  display: flex; flex-direction: column; gap: 2px;
}
/* ── ЛИПНЕТ СПИСОК, А НЕ РЕЙКА (заявка sys-04) ───────────────────────
   Липкость у самой рейки (правило выше) работает, пока рейка ростом со
   свой список. В разделе «Система» она растянута до низа коробки
   (block-size:100% разд. 32 — так рисуется линия-разделитель во всю
   высоту), а элемент, занимающий всю высоту контейнера, из виду не
   уезжает, и прилипать ему не к чему: на /system/refs список уходил
   вверх на −1503px при прокрутке на 1600.

   Поэтому липнет ВЛОЖЕННЫЙ список, а рейка остаётся полновысотной.
   Обёртку даёт разметка (`.cra-rail__stick` в sysnav.html): у рейки
   ДВЕ разные роли — колонка с фоном и линией у одного носителя и
   короткий столбик пунктов у другого, — и одним элементом обе не
   играются.

   `.cra-rail` при этом НЕ ТРОГАЕТСЯ, и это не осторожность: та же рейка
   без обёртки стоит рейкой шагов в форме нового сбора (runs.html), и
   снятие с неё position:sticky сломало бы форму заодно с починкой
   раздела.

   Флекс с зазором 2px повторён здесь не для красоты: раскладку пунктов
   держала рейка, а теперь между ней и пунктами стоит обёртка — обычный
   блок. Без этих трёх свойств зазоры между пунктами пропадают и рейка
   становится примерно на 20px короче. */
[data-ui="v3"] .cra-rail__stick {
  position: sticky; inset-block-start: 0;
  display: flex; flex-direction: column; gap: 2px;
}
[data-ui="v3"] .cra-rail__item {
  display: flex; align-items: center; gap: var(--vc-v3-gap-inner);
  padding: 8px 10px; background: transparent; border: 1px solid transparent;
  border-inline-start: 2px solid transparent;
  color: var(--muted); font: 500 var(--vc-v3-fs-mono) var(--font-body);
  text-align: start; cursor: pointer; text-decoration: none;
}
[data-ui="v3"] .cra-rail__item:hover { color: var(--text); }
/* Пункт рейки, выключенный с причиной. Правила у него не было вовсе, и
   «Политика сбора» с «Журналом действий» выглядели такими же живыми,
   как соседние, — то есть выключенное было неотличимо от рабочего.
   Здесь применяется канон разд. 12 как есть: у пункта рейки, в отличие
   от значка шапки, подпись и площадь под штриховку. */
[data-ui="v3"] .cra-rail__item.cra-off {
  position: relative; color: var(--muted); cursor: not-allowed;
}
[data-ui="v3"] .cra-rail__item.cra-off:hover { color: var(--muted); }
[data-ui="v3"] .cra-rail__item.cra-off::after {
  content: ""; position: absolute; inset: 0;
  background: repeating-linear-gradient(135deg, color-mix(in srgb, var(--muted) 14%, transparent) 0 2px, transparent 2px 6px);
  pointer-events: none;
}
[data-ui="v3"] .cra-rail__item--on {
  color: var(--text); border-inline-start-color: var(--accent);
  background: color-mix(in srgb, var(--accent) 6%, transparent);
}
[data-ui="v3"] .cra-rail__n {
  flex: none; inline-size: 18px; text-align: center;
  font: 500 var(--vc-v3-fs-label) var(--font-mono); color: var(--muted);
}
/* Подпись группы в рейке отбита от края так же, как текст пункта
   (заявка sys-02). У пункта до текста 12px — 2px борта состояния плюс
   10px поля, — а у подписи не было ничего, и «СИСТЕМА» стояла левее
   «Общие»: заголовок группы выглядел выпавшим из списка, который он
   озаглавливает. Отбивка повторяет ПОСТРОЕНИЕ пункта, а не совпадает с
   ним числом случайно; в макете у обоих оно тоже одно (`.srail .grp` и
   `.srail a`, по 18px, Crawler_settings_v1.3). */
[data-ui="v3"] .cra-rail__grp { padding-inline-start: 12px; }

/* Выбор одного из нескольких с пояснением под подписью: радио или
   галочка плюс текст. Рамка — у «коробочного» варианта. */
[data-ui="v3"] .cra-choices { display: flex; flex-direction: column; gap: 4px; }
[data-ui="v3"] .cra-choices--nested { margin-inline-start: 26px; }
/* ── Три раскладки списка выбора, все из макета «Нового сбора» v1.7 ──
   Список вариантов бывает не только столбцом: на шаге 1 это РЯД из пяти
   карточек (`#src{grid-template-columns:repeat(5,minmax(0,1fr));gap:8px}`),
   на шагах 2 и 4 — сетка 2×2 (`#goals{grid-template-columns:1fr 1fr}`), на
   шаге 3 — сегментный переключатель из двух кнопок в одной рамке.
   Классы в разметке экрана уже стояли, правил под них не было, и вся
   форма оставалась вертикальным списком — единственный экран, который
   аудит назвал черновиком. Заявка v4-01, пункты 1-3.

   Зона, раскрывающаяся под выбранным вариантом (.cra-zone, .cra-choice-body),
   в сетке занимает ВСЮ ширину и уходит под ряд: order:2 в макете. Свой
   левый отступ она при этом теряет — он был нужен для отбивки от радио в
   столбце, а под рядом карточек отбивать не от чего. */
[data-ui="v3"] .cra-choices--cards {
  display: grid; grid-template-columns: repeat(5, minmax(0, 1fr)); gap: var(--vc-v3-gap-inner);
}
[data-ui="v3"] .cra-choices--cards > .cra-choice { order: 1; flex-direction: column; gap: 6px; }
[data-ui="v3"] .cra-choices--2col {
  display: grid; grid-template-columns: 1fr 1fr; gap: var(--vc-v3-gap-inner);
}
[data-ui="v3"] .cra-choices--cards > .cra-zone,
[data-ui="v3"] .cra-choices--cards > .cra-choice-body,
[data-ui="v3"] .cra-choices--2col > .cra-zone,
[data-ui="v3"] .cra-choices--2col > .cra-choice-body {
  order: 2; grid-column: 1 / -1; margin-inline-start: 0;
}
/* Узкое окно: пять карточек в ряд не читаются. */
@media (max-width: 1100px) {
  [data-ui="v3"] .cra-choices--cards { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
/* Сегментный вариант — тот же .cra-seg по виду, но носитель остаётся
   вариантом выбора: у сегмента кнопки, а здесь поля формы, и подменять
   их кнопками значит терять и tab-порядок, и отправку формы. Поэтому не
   новый компонент, а раскладка: рамка одна на ряд, разделители между
   вариантами, включённый красится как включённый сегмент. */
[data-ui="v3"] .cra-choices--seg {
  display: inline-flex; flex-direction: row; gap: 0;
  border: 1px solid var(--line); flex-wrap: wrap;
}
[data-ui="v3"] .cra-choices--seg > .cra-choice {
  border: 0; border-inline-end: 1px solid var(--line);
  padding: 8px 14px; gap: 0; color: var(--muted);
  font: 500 var(--vc-v3-fs-meta) var(--font-body);
}
[data-ui="v3"] .cra-choices--seg > .cra-choice:last-child { border-inline-end: 0; }
[data-ui="v3"] .cra-choices--seg > .cra-choice:has(input:checked) {
  background: var(--surface-2); color: var(--vc-v3-accent-ink);
}
/* Пояснение под подписью в сегменте не помещается и в макете скрыто.
   Само поле выбора остаётся на месте и в tab-порядке: состояние читается
   заливкой сегмента, а не точкой в поле. */
[data-ui="v3"] .cra-choices--seg .cra-choice__note { display: none; }
[data-ui="v3"] .cra-choice {
  display: flex; align-items: flex-start; gap: 10px; padding: 7px 2px;
  font-size: var(--vc-v3-fs-data); cursor: pointer;
}
[data-ui="v3"] .cra-choice--boxed {
  border: 1px solid var(--line); background: var(--surface); padding: 10px 12px;
}
[data-ui="v3"] .cra-choice--boxed:hover { border-color: var(--vc-v3-accent-dim); }
/* ВЫБРАННЫЙ вариант виден по самой коробке, а не только по точке внутри
   поля: в макете `.ro.on` — борт --accent-dim и заливка «акцент 7 %»
   (Crawler_new-run_v1.7), и выбранное читается за метр. Состояние берётся
   у самого поля через :has, а не классом от скрипта: класс пришлось бы
   ставить и снимать на каждое переключение в каждом экранном скрипте, и
   первый же забытый обработчик оставил бы подсветку на бывшем выборе. */
[data-ui="v3"] .cra-choice--boxed:has(input:checked) {
  border-color: var(--vc-v3-accent-dim);
  background: color-mix(in srgb, var(--accent) 7%, var(--surface));
}
/* Зона приёма файла. Своего компонента «перетащите файл» в ките нет, и он
   не заводится (заявка e3-02): зона — это тот же «коробочный» выбор, у
   которого не хватало двух СОСТОЯНИЙ, а не новый вид.
   1) «файл над зоной» — без него человек не понимает, отпустит он файл в
      зону или мимо (паспорт ст. 7: у контрола обязано быть видимое
      состояние приёма);
   2) фокус с клавиатуры — ст. 5.9: что делается мышью, делается и с
      клавиатуры. Поле выбора файла внутри зоны прячется классом ниже, а
      не display:none: display:none выкидывает поле из tab-порядка, и зона
      становится недостижимой вовсе (так было и на старом слое). */
[data-ui="v3"] .cra-choice--over {
  border-color: var(--accent); background: color-mix(in srgb, var(--accent) 8%, transparent);
}
[data-ui="v3"] .cra-choice--boxed:focus-within { outline: 2px solid var(--vc-v3-accent-dim); outline-offset: 2px; }
[data-ui="v3"] .cra-choice__file {
  inline-size: 0; block-size: 0; min-width: 0; padding: 0; border: 0; opacity: 0;
}
[data-ui="v3"] .cra-choice__text { display: block; min-inline-size: 0; }
[data-ui="v3"] .cra-choice__note {
  display: block; color: var(--muted); font-size: var(--vc-v3-fs-meta); margin-block-start: 2px;
}
[data-ui="v3"] .cra-choice-body { margin: 4px 0 0 26px; }
[data-ui="v3"] .cra-zone { margin: 6px 0 10px 26px; }

/* Поле в строку: подпись и контрол рядом, а не друг под другом — так
   набирают числа и короткие значения в форме сбора. */
[data-ui="v3"] .cra-inline {
  display: flex; gap: var(--vc-v3-gap-item); align-items: center;
  flex-wrap: wrap; margin-block-start: var(--vc-v3-gap-item);
}
[data-ui="v3"] .cra-label-inline {
  display: inline-flex; align-items: center; gap: var(--vc-v3-gap-inner);
  font-size: var(--vc-v3-fs-meta); color: var(--muted);
}
[data-ui="v3"] .cra-input--num { inline-size: 90px; min-inline-size: 90px; font-family: var(--font-mono); }
[data-ui="v3"] .cra-select--inline { inline-size: auto; }
[data-ui="v3"] .cra-w-full { inline-size: 100%; }
/* Спрятано скриптом формы. Без !important (ст. 9.2 его запрещает):
   селектор перебивает .cra-choices и .cra-inline их же собственной
   специфичностью, а не силой. */
[data-ui="v3"] .cra-hidden,
[data-ui="v3"] .cra-choices.cra-hidden,
[data-ui="v3"] .cra-inline.cra-hidden,
[data-ui="v3"] .cra-choice-body.cra-hidden,
[data-ui="v3"] .cra-nr-draft.cra-hidden { display: none; }
/* Номер пункта спецификации у поля: он нужен при разборе, но спорить с
   подписью не должен. */
[data-ui="v3"] .cra-ref {
  font: 400 10px var(--font-mono); color: color-mix(in srgb, var(--muted) 60%, transparent);
  margin-inline-start: 4px;
}
/* ПРИЗНАК «ЗДЕСЬ ЕСТЬ ПОДСКАЗКА» — не новый компонент, а сквозной признак.
   Приёмка 7.24.0 (продакт): подсказку у «Отобрано:» нельзя было ни найти
   глазами, ни достать с клавиатуры — ровно та болезнь, которую эта же
   волна лечила у выключенной кнопки, и два разных ответа на один вопрос
   в одной волне. Вид взят у .cra-date (кит, разд. 9): пунктир снизу и
   курсор help; tabindex ставит разметка — фокус нужен, чтобы подсказку
   отдала клавиатура, а не только мышь. */
[data-ui="v3"] .cra-hastip {
  cursor: help; border-block-end: 1px dotted var(--line);
}
[data-ui="v3"] .cra-hintmark {
  display: inline-grid; place-content: center; inline-size: 16px; block-size: 16px;
  border: 1px solid var(--line); color: var(--muted);
  font: 600 10px var(--font-mono); cursor: help;
}
[data-ui="v3"] .cra-field--invalid .cra-select,
[data-ui="v3"] .cra-select.cra-field--invalid { border-color: var(--err); }
[data-ui="v3"] .cra-nr-draft { display: flex; align-items: center; gap: var(--vc-v3-gap-item); }
[data-ui="v3"] .cra-sep-top { border-block-start: 1px solid var(--line); padding-block-start: var(--vc-v3-gap-item); margin-block-start: var(--vc-v3-gap-item); }
[data-ui="v3"] .cra-stack { display: flex; flex-direction: column; gap: var(--vc-v3-gap-inner); }
/* Комбобокс поиска рынков в форме сбора. */
[data-ui="v3"] .cra-combo { position: relative; }
[data-ui="v3"] .cra-combo__field {
  min-block-size: var(--vc-v3-ctrl-h); border: 1px solid var(--line);
  background: var(--bg); padding: 5px 8px; display: flex; gap: 6px;
  flex-wrap: wrap; align-items: center; cursor: text;
}
[data-ui="v3"] .cra-combo__drop {
  position: absolute; inset-inline: 0; inset-block-start: calc(100% + 4px);
  background: var(--surface); border: 1px solid var(--line);
  box-shadow: var(--vc-v3-menu-shadow); max-block-size: 280px; overflow: auto;
  display: none; z-index: 9; padding: 6px;
}
[data-ui="v3"] .cra-combo--open .cra-combo__drop,
[data-ui="v3"] .cra-combo__drop--open { display: block; }
/* Поле НАБОРА внутри комбобокса. Коробку рисует .cra-combo__field, и
   второй рамки внутри неё быть не должно — поле здесь прозрачное и без
   борта. Правила не было вовсе: посреди формы «Новый сбор» стоял
   браузерный прямоугольник с вдавленной рамкой (в светлой теме белый),
   потому что поле, собранное скриптом экрана, класса не носит, а вид
   ЧАСТИ компонента обязан задавать сам компонент, а не его хозяин. */
[data-ui="v3"] .cra-combo__field input {
  flex: 1; min-inline-size: 140px; border: 0; background: transparent;
  color: var(--text); font: 13px var(--font-body); block-size: 24px;
  padding: 0; outline: none;
}
[data-ui="v3"] .cra-combo__field input::placeholder { color: var(--muted); }
[data-ui="v3"] .cra-combo__field--open,
[data-ui="v3"] .cra-combo__field:focus-within { border-color: var(--vc-v3-accent-dim); }
/* Узел дерева классификатора в выпадашке: строка-переключатель с
   галочкой, вложенность — отступом. Тот же вид, что у пункта меню. */
[data-ui="v3"] .cra-combo__row {
  display: flex; align-items: center; gap: var(--vc-v3-gap-inner);
  padding: 5px 8px; cursor: pointer; font-size: var(--vc-v3-fs-data);
}
[data-ui="v3"] .cra-combo__row:hover { background: color-mix(in srgb, var(--accent) 6%, transparent); }
[data-ui="v3"] .cra-combo__kids { margin-inline-start: 18px; }

/* Поиск по базе: поле во всю ширину зоны плюс своя выпадашка с находками.
   Носитель выпадашки тот же, что у комбобокса, — второго вида списка
   находок в наборе не заводится. */
[data-ui="v3"] .cra-finder { inline-size: 100%; position: relative; }
[data-ui="v3"] .cra-finder input {
  inline-size: 100%; block-size: var(--vc-v3-ctrl-h);
  background: var(--bg); border: 1px solid var(--line); color: var(--text);
  padding-inline: 12px; font: 13px var(--font-mono);
}
[data-ui="v3"] .cra-finder input::placeholder { color: var(--muted); }
[data-ui="v3"] .cra-finder input:focus-visible { outline: 2px solid var(--vc-v3-accent-dim); outline-offset: 1px; }
[data-ui="v3"] .cra-finder__drop {
  position: absolute; inset-inline: 0; inset-block-start: calc(100% + 4px);
  background: var(--surface); border: 1px solid var(--line);
  box-shadow: var(--vc-v3-menu-shadow); max-block-size: 280px; overflow: auto;
  display: none; z-index: 9; padding: 6px;
}
[data-ui="v3"] .cra-finder__drop--open { display: block; }
[data-ui="v3"] .cra-finder__item {
  display: flex; align-items: center; gap: var(--vc-v3-gap-inner);
  padding: 6px 8px; cursor: pointer; font-size: var(--vc-v3-fs-data);
}
[data-ui="v3"] .cra-finder__item:hover { background: color-mix(in srgb, var(--accent) 6%, transparent); }
[data-ui="v3"] .cra-finder__item.cra-dash { cursor: default; }
[data-ui="v3"] .cra-finder__item.cra-dash:hover { background: transparent; }

/* ── 30. СНИМОК ДОКУМЕНТА (экран 4.2) ────────────────────────────────
   Сохранённый при сборе файл показывается в песочнице: это чужая
   страница, и выполнять её скрипты у себя мы не будем. Рамка и
   поверхность — как у полей, других своих правил у снимка нет.
   Запись DR-36 в реестре расхождений: своего компонента «встроенный
   документ» в ките нет. */
[data-ui="v3"] .cra-snap {
  inline-size: 100%; block-size: 60vh; min-block-size: 320px;
  border: 1px solid var(--line); background: var(--bg);
}

/* ── 31. ЭКРАН ВХОДА ─────────────────────────────────────────────────
   Своя страница без оболочки: карточка по центру, марка, два поля и
   кнопка. Компоненты все принятые — поле, кнопка, плашка ошибки; здесь
   только раскладка страницы, которой у кита нет и быть не может: вход
   единственный экран без шапки. */
[data-ui="v3"] .cra-body--login { min-block-size: 100vh; display: flex; }
/* Размеры взяты из макета Crawler_login_v1.5 (стр. 14-34) — все до одного
   расходились: карточка 360 против 400, поля 28/26 против 34/34/26, не
   было верхнего борта акцентом, кнопка растягивалась во всю карточку,
   строка версии стояла по центру под ней. Заявка v5-01.
   Ширина 400 чинит заодно перенос подписи «Оставаться в системе на этом
   устройстве»: ей нужно 282px, а 360 минус поля давали 281 — подпись
   ломалась на две строки из-за одного пикселя. */
[data-ui="v3"] .cra-login {
  margin: auto; inline-size: min(400px, 92vw);
  display: flex; flex-direction: column; gap: var(--vc-v3-gap-item);
}
[data-ui="v3"] .cra-login__card {
  background: var(--surface); border: 1px solid var(--line);
  border-block-start: 2px solid var(--accent);
  padding: 34px 34px 26px; display: flex; flex-direction: column;
  gap: var(--vc-v3-gap-item);
}
/* Марка по ЦЕНТРУ, отбивка снизу 26px — раскладка макета.
   В макете это СТОЛБИК из двух картинок: знак 46px и слово «КРАУЛЕР»
   17px. Собрать его нечем — файла со словом в поставке бренда нет вовсе
   (DR-56), поэтому здесь только раскладка: горизонтальная марка шапки,
   поставленная по центру. Высоту знака 46px на неё не переношу: у
   горизонтальной марки слово уже внутри, и 46px растянули бы её на пол-
   карточки. Появится файл слова — меняется разметка, не эти правила. */
[data-ui="v3"] .cra-login__mark { block-size: 26px; align-self: center; margin-block-end: 26px; }
[data-ui="v3"] .cra-login__card .cra-check { align-items: start; }
/* Кнопка прижата ВПРАВО и по ширине подписи. В макете под неё отведена
   строка действий `.actions{justify-content:flex-end}`; здесь хватает
   одного `align-self`, и новый носитель не заводится — правило проекта
   прямое: изобретать компонент, пока задача решается принятым, нельзя.
   Без этой строки кнопку растягивала во всю карточку сама колонка-флекс
   (align-items: stretch), и главное действие приложения выходило 306px
   вместо 102: первое, что видит человек, было не как в макете. */
[data-ui="v3"] .cra-login__go { align-self: end; margin-block-start: 6px; }
/* Версия — в левом нижнем углу ОКНА, а не под карточкой: она относится к
   установке, а не к форме входа (макет, `.ver`).
   Приглушение — смесью с фоном, не `opacity`: паспорт гасить прозрачностью
   запрещает, и тот же приём уже применён к прочерку (.cra-dash). */
[data-ui="v3"] .cra-login__foot {
  position: fixed; inset-inline-start: 16px; inset-block-end: 16px;
  font: 400 10.5px var(--font-mono);
  color: color-mix(in srgb, var(--muted) 70%, transparent);
}

/* ── 32. РАЗДЕЛ «СИСТЕМА»: РЕЙКА СЛЕВА, СОДЕРЖИМОЕ СПРАВА ────────────
   Раскладка страницы раздела. Сама рейка — принятый компонент кита (тот
   же, что в форме нового сбора); здесь только две колонки. DR-42.

   sys-01: рейка и содержимое лежат в ОДНОЙ обрамлённой коробке, между
   ними вертикальная линия — `.setwrap` / `.srail` / `.scontent` макета
   «Настройки» v1.3 (стр. 349–357). Было два самостоятельных блока с
   зазором 44px: рейка голым списком, содержимое без обрамления, и раздел
   не читался единым экраном. Зазор снят намеренно — его место занимает
   линия, а не пустота; двух разделителей подряд в наборе не бывает.

   Сетка, а не флекс: колонка рейки задана числом (236px из макета), и
   при флексе та же ширина держалась бы её собственным inline-size —
   мерой компонента, а не раскладки. Рейка — общий компонент кита, и в
   форме нового сбора она стоит своей ширины 210px; менять её ради
   раскладки одного раздела нельзя, поэтому ширину диктует колонка.
   minmax(0,1fr) у второй колонки обязателен: без него содержимое с
   широкой таблицей растит колонку по min-content и выносит раздел за
   окно.

   Растяжка рейки до низа коробки (block-size:100%) отключает её
   sticky — прилипать нечему, элемент и так не уезжает. Это не потеря:
   в макете рейка полновысотная, а её липкость была свойством прежней
   раскладки, где она стояла отдельным коротким блоком. */
[data-ui="v3"] .cra-syslayout {
  display: grid; grid-template-columns: 236px minmax(0, 1fr);
  border: 1px solid var(--line); background: var(--bg); align-items: stretch;
}
[data-ui="v3"] .cra-syslayout > .cra-rail {
  inline-size: auto; block-size: 100%; padding-block: var(--vc-v3-gap-item);
  background: var(--surface); border-inline-end: 1px solid var(--line);
}
[data-ui="v3"] .cra-sysbody { min-inline-size: 0; padding: 20px 24px 32px; }

/* Пункт меню пользователя, на странице которого мы сейчас: та же роль, что
   у активного раздела в шапке, — человек должен видеть, где он. */
[data-ui="v3"] .cra-menu a.cra-menu__item--on { color: var(--vc-v3-accent-ink); }

/* Складка на нативном <details> (дерево классификатора): та же складка
   кита, но раскрытием управляет браузер, а не скрипт — дерево на сто
   сорок пять узлов не нуждается в своём обработчике на каждый узел. */
[data-ui="v3"] details.cra-fold > summary { list-style: none; cursor: pointer; }
[data-ui="v3"] details.cra-fold > summary::-webkit-details-marker { display: none; }
[data-ui="v3"] details.cra-fold[open] > .cra-fold__body { display: block; }
[data-ui="v3"] details.cra-fold[open] > summary .cra-fold__tg { transform: rotate(90deg); }
[data-ui="v3"] .cra-cls { margin-block-end: 4px; }
[data-ui="v3"] .cra-cls .cra-cls { margin: 4px 0 4px 14px; }

/* ══ 33. ЭКРАН СО СТАРЫМ СОДЕРЖИМЫМ В НОВОЙ ОБОЛОЧКЕ ═════════════════
   Девять экранов переведены на слой ОБРАМЛЕНИЕМ: шапка, тема, отступы,
   заголовок — новые, содержимое пока старое (cra_shell_legacy.html,
   отступление от ст. 9.5 записано в docs/deviations.md).

   На таких страницах рядом с новым слоем подключён старый app.css. Он
   задаёт `main { padding: 20px var(--pad-page); max-width: 1680px }` —
   то есть целится в ТОТ ЖЕ тег, что несёт рабочую область слоя. Отступы
   перекрывает .cra-main (специфичность выше), а вот max-width он не
   объявляет вовсе, и страница получала бы ширину, которой нет ни у
   одного другого экрана слоя. Здесь это и снимается — одним правилом и
   на всех девяти сразу, а не по экрану.

   Больше старому слою в обрамление ничего не достаётся: остальные его
   правила относятся к классам, которых новая оболочка не носит, либо
   проигрывают по специфичности правилам с [data-ui="v3"]. */
[data-ui="v3"] .cra-main { max-width: none; }
/* Заголовок старого экрана внутри новой оболочки: h1 в .page-head едет
   от app.css с кеглем 19 px, тогда как страница слоя носит .cra-h1
   (20 px, шрифт-дисплей). Приравниваем — заголовок это обрамление, и
   отличаться от соседних экранов он не должен. */
[data-ui="v3"] .cra-legacy .page-head h1 {
  font: 600 var(--vc-v3-fs-page) var(--font-display);
  margin: 0 0 var(--vc-v3-gap-inner); letter-spacing: normal; color: var(--text);
}

/* ── КОМПОНЕНТ НАБОРА ВНУТРИ СТРАНИЦЫ СО СТАРЫМ СЛОЕМ ────────────────
   Утверждение выше («больше старому слою ничего не достаётся») оказалось
   неверным, и это ИЗМЕРЕНО, а не выведено из специфичности: страница
   слоя и страница legacy-оболочки сравнены пробником по вычисленным
   стилям одних и тех же компонентов. Расхождения были такие:

     · h2.cra-h2 — приглушённый цвет, КАПС и разрядка (голый `h2` в
       app.css задаёт три свойства, которых .cra-h2 не объявляет вовсе);
     · h1.cra-h1 — разрядка −0.2px (голый `h1`);
     · каждый контрол набора — скругление 2px и padding-block 5px от
       голых `button`/`input`/`select` (кит объявляет только высоту и
       поля по горизонтали, остальное протекает). Скругление 0 —
       требование темы, 2px не бывает нигде, кроме плашки-пометки .mk;
     · ячейки .cra-table — свои padding от `thead th`/`tbody td`: строка
       выходила 36px вместо --row-h 34px;
     · подсветка строки по наведению от `tbody tr:hover`.

   Область — ВСЯ страница legacy-оболочки (.cra-body--legacy), а не
   .cra-legacy: шапка лежит вне содержимого, а протекало и в неё —
   кнопка меню пользователя приезжала со скруглением. Обрамление обязано
   быть одинаковым везде, значит и лечится это на уровне страницы.

   Скоуп завёрнут в :where(), чтобы не поднимать вес правила: оно обязано
   бить голый тег старого слоя и обязано ПРОИГРЫВАТЬ любому правилу кита
   (у того всегда есть [data-ui] плюс класс). Так значения набора
   остаются единственным источником, а здесь гасится только то, чего
   набор про свои компоненты не говорит.

   Значение — revert, а не своё число: задача не «поставить правильно», а
   СНЯТЬ добавленное старым слоем. Своё число здесь было бы второй копией
   умолчания, и на странице слоя (где этого правила нет) компонент всё
   равно вышел бы другим. revert возвращает ровно то, что компонент
   получил бы без app.css, — то есть делает две оболочки одинаковыми по
   построению, а не по совпадению значений. */
:where([data-ui="v3"] .cra-body--legacy) h1.cra-h1,
:where([data-ui="v3"] .cra-body--legacy) h2.cra-h2,
:where([data-ui="v3"] .cra-body--legacy) h3.cra-h3 {
  color: var(--text); text-transform: none; letter-spacing: normal;
}
:where([data-ui="v3"] .cra-body--legacy) button[class*="cra-"],
:where([data-ui="v3"] .cra-body--legacy) input[class*="cra-"],
:where([data-ui="v3"] .cra-body--legacy) select[class*="cra-"],
:where([data-ui="v3"] .cra-body--legacy) textarea[class*="cra-"],
:where([data-ui="v3"] .cra-body--legacy) .cra-check input,
:where([data-ui="v3"] .cra-body--legacy) .cra-menuitem input,
:where([data-ui="v3"] .cra-body--legacy) .cra-seg button,
:where([data-ui="v3"] .cra-body--legacy) .cra-bulk button,
:where([data-ui="v3"] .cra-body--legacy) .cra-modal button,
:where([data-ui="v3"] .cra-body--legacy) .cra-toast button {
  border-radius: revert; padding-block: revert; font-size: revert;
}
/* Ячейки: свои padding и выравнивание по ВЕРХУ от `thead th`/`tbody td`.
   Выравнивание видно сразу, как только содержимое ячейки в две строки.
   Остаточное расхождение, измеренное и оставленное: padding-block у
   ячейки выходит 0 вместо 1px браузерного умолчания — revert его не
   восстанавливает (проверено в браузере). Высота строки фиксирована
   (--row-h), содержимое центрируется, видимого следствия нет. */
:where([data-ui="v3"] .cra-body--legacy) .cra-table th,
:where([data-ui="v3"] .cra-body--legacy) .cra-table td {
  padding-block: revert; vertical-align: revert;
}
/* Наведение на строку рисует кит (.cra-row:hover td) — своей заливки у
   строки быть не должно, иначе подсвечиваются и те строки, которым кит
   подсветки не давал. */
:where([data-ui="v3"] .cra-body--legacy) .cra-table tbody tr:hover { background: transparent; }
/* Ссылка, нарисованная кнопкой или пунктом меню: подчёркивания у неё нет
   нигде в наборе, а голый `a:hover` старого слоя его добавляет. */
:where([data-ui="v3"] .cra-body--legacy) a.cra-btn:hover,
:where([data-ui="v3"] .cra-body--legacy) a.cra-ib:hover,
:where([data-ui="v3"] .cra-body--legacy) a.cra-menuitem:hover { text-decoration: none; }
