/* Pharma Upsell MVP — Design system utilities (ADR-030, PUM-494)
 *
 * Утилиты, зеркалящие legacy-классы разметки вкладки Территории.
 * Все значения — через var(--token) из tokens.css.
 *
 * Секции сгруппированы по семантическим ролям. Каждая секция начинается
 * комментарием "/* {Role} — mirrors {legacy list} *\/" — это и есть
 * маппинг legacy → .ds-* для читателя.
 *
 * В этой задаче классы НИГДЕ не применяются (инфраструктурный шаг).
 */

/* ── Основа страницы ───
   Ширина и высота элемента включают его отступы и рамку. На это рассчитан
   каждый атом ниже: `width: 100%` с padding не выходит за контейнер.
   Правила живут здесь, а не в стиле страницы: вход, смена пароля и оператор
   грузят только tokens.css + ds-utilities.css (§12.1). */
* {
  box-sizing: border-box;
}

/* Браузерный отступ body (8px) снят: фон страницы несёт корневой блок
   (.ds-auth-page, .ds-shell), и отступ оставлял по краю белую рамку. */
body {
  margin: 0;
}

/* ── Tables — mirrors .exp-table-wrap, .exp-table, .exp-col-mono, .exp-col-right, .exp-row ─── */

.ds-table-wrap {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  overflow: clip;
  /* overflow: clip (не hidden) — clip не создаёт scroll-context, поэтому position: sticky
     у дочерних thead th работает корректно. overflow: hidden создаёт новый scroll-context
     и ломает sticky у потомков (classic CSS pitfall). */
  box-shadow: none;
  /* §8.1: legacy var(--shadow) был undefined (фактически none).
     Осознанно фиксируем hairline-border вместо тени — §3 design-system.md. */
}

.ds-table {
  width: 100%;
  border-collapse: separate;
  border-spacing: 0;
  table-layout: fixed;
  font-size: var(--fs-body);
}

.ds-table tbody[data-tab-key] {
  overflow-anchor: none;
  /* records/virtualizer.js уже компенсирует позицию прокрутки сам (_render,
     records/virtualizer.js — измеряет реальные высоты строк и зовёт
     window.scrollBy на алгебраическую дельту). Браузерный scroll anchoring
     — та же самая коррекция независимо от нашей, только по своей
     эвристике; вместе они дают ДВОЙНУЮ поправку и дёргают позицию сильнее,
     чем без обеих. Это поведенческое свойство, не визуальное — под
     NON-GOALS «внешний вид не меняется» не попадает.
     Селектор сужен до [data-tab-key] (PUM-1313 B3, независимый code
     review): tabKeyAttr() (records/active_row.js) ставит этот атрибут
     ТОЛЬКО на tbody таблиц, построенных поверх виртуализатора
     (records/virtualizer.js) — сегодня «Записи»/«Сотрудники»
     (records/checks_table.js) и «Банк» (bank_records/bank_table.js,
     PUM-1413) — не на голый факт «несёт .ds-table». «Отложенные» несут
     собственную панель без virtualizer и своего scroll anchoring не
     отключают (NON-GOALS: их поведение не меняется). Голый
     `.ds-table tbody` бил бы по всем производителям `.ds-table`, включая
     «Отложенных». */
}

.ds-table thead th {
  position: sticky;
  top: var(--exp-player-height, 0px);
  /* §B PUM-503: прежний ряд вкладок больше не sticky → sticky-отступ
     таблицы больше не учитывает его высоту (переменная снята вместе с
     рядом, §11.68 PUM-1506). Sticky thead прилипает к плееру (если активен)
     или к 0. PUM-530. */
  z-index: 10;
  background: var(--surface-2);
  box-shadow: inset 0 -2px 0 var(--border);
  /* box-shadow hairline requires border-collapse: separate on the parent table (см. §7 design-system.md, PUM-530). */
}

.ds-table th {
  padding: var(--table-header-padding-y) var(--table-cell-padding-x);
  text-align: left;
  font-size: var(--fs-th);
  font-weight: var(--fw-th);
  color: var(--text-muted);
  white-space: nowrap;
  border-bottom: 1px solid var(--border);
  background: var(--surface-2);
  /* §13: убраны text-transform: uppercase и letter-spacing: 0.04em */
}

.ds-table td {
  padding: var(--table-cell-padding-y) var(--table-cell-padding-x);
  vertical-align: middle;
  border-bottom: 1px solid var(--border-muted);
  /* Zebra убрана §13: ритм через hairlines, не фон */
}

/* .ds-row — pseudo-class anchor для строк внутри .ds-table.
   Сам класс в разметке Территорий не применяется самостоятельно — ему не нужны
   правила типа `.ds-row { ... }`. Используется только как target для :hover, :last-child и .ds-row-active. */

.ds-row:last-child > td {
  border-bottom: none;
}

.ds-row:hover > td {
  background: var(--hover-row);
  transition: background var(--dur-fast) var(--ease-standard);
}

.ds-row.ds-row-active > td {
  background: var(--active-row);
  /* Без !important — у нас нет zebra, которую нужно побеждать */
}

.ds-row.ds-row-active > td:first-child {
  box-shadow: inset 2px 0 0 var(--active-row-edge);
  /* 2px — active-row subtle-маргиналия (design-system §7.2); 3px — только focus-row §8.1. */
}

.ds-row.ds-row-active:hover > td {
  background: var(--brand-100);
}

.ds-row-dimmed > td {
  opacity: 0.6;
  /* Погашенная строка (§7.2): выведена из работы, но остаётся в выдаче и кликабельна.
     Тон ставится по состоянию строки, не по факту действия — узел перерисовывается
     виртуализатором из данных, и след нажатия не пережил бы прокрутку. */
}

.ds-col-mono {
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}

.ds-col-right,
.ds-table thead th.ds-col-right,
.ds-table tbody td.ds-col-right,
.ds-summary-table thead th.ds-col-right,
.ds-summary-table tbody td.ds-col-right {
  /* PUM-498: контейнерные селекторы повышают specificity до (0,0,2,1), чтобы
     перекрыть `.ds-table th`, `.ds-summary-table th` (оба 0,0,1,1 с text-align:left).
     Без этого right-align на th не применяется, только на td (у которых нет базового text-align). */
  text-align: right;
  font-variant-numeric: tabular-nums;
  font-weight: var(--fw-table-num);
}

/* ── States — mirrors .dash-loading, .dash-empty, .dash-block-error, .dash-retry-btn, .dash-block-title ─── */

.ds-state-loading {
  text-align: center;
  padding: var(--space-10) var(--space-4);
  font-size: var(--fs-body);
  color: var(--text-muted);
}

.ds-state-empty {
  text-align: center;
  padding: var(--space-10) var(--space-4);
  font-size: var(--fs-body);
  color: var(--text-muted);
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
}

.ds-state-error {
  text-align: center;
  color: var(--neg-700);
  padding: var(--space-5) var(--space-4);
  font-size: var(--fs-label);
}

.ds-btn-retry {
  background: none;
  border: none;
  cursor: pointer;
  color: var(--primary);
  font-size: var(--fs-label);
  padding: 0;
  text-decoration: underline;
  margin-left: var(--gap-inline);
}

.ds-btn-retry:hover {
  color: var(--primary-hover);
}

.ds-block-title {
  font-family: var(--font-serif);
  font-size: var(--fs-body);
  font-weight: var(--fw-th);
  color: var(--text);
  margin: var(--gap-section-header) 0 var(--gap-section-items);
}

/* ── Subtabs — design-system utilities ─────────────────── */

.ds-subtabs {
  display: flex;
  gap: 0;
  padding: var(--space-3) var(--space-1) 0;
  /* Боковой отступ var(--space-1) (4px) — запас под 3px-сдвиг --focus-ring (box-shadow, не
     outline) у первой/последней кнопки: без него клип прокручиваемой области срезает обводку
     сбоку (замер — зазор до края области 0) (§11.54, PUM-1390). Компенсируется отрицательным
     margin ниже, чтобы кнопки не сдвинулись. */
  margin: 0 calc(-1 * var(--space-1));
  /* Возвращает кнопки на прежнее место после бокового padding выше: без компенсации весь бар
     сдвинулся бы на 4px, а контракт требует, чтобы оба навигационных бара визуально не
     изменились (§11.54, PUM-1390). */
  /* Ряд длиннее доступной ширины прокручивается внутри бара, не растягивает страницу (§11.54,
     PUM-1390). overflow-x: auto коэрсит overflow-y в auto по CSS-спеке (продукт уже опирается
     на этот факт — .ds-cc-content-wrap);
     вертикальной
     прокрутки при этом не возникает — высота контейнера auto и всегда совпадает с высотой
     содержимого (align-items: stretch по умолчанию выравнивает все кнопки на самую высокую). */
  overflow-x: auto;
  /* Линия-граница — фон контейнера, не border: клип прокручиваемой области идёт по padding-box
     и срезал бы border/box-shadow дочерних элементов, но background-attachment: scroll (значение
     по умолчанию) рисует фон относительно собственного бокса контейнера, а не относительно
     прокручиваемого содержимого — линия не едет вместе с рядом при скролле и не срезается им
     (§11.54, PUM-1390). */
  background-image: linear-gradient(to right, var(--border), var(--border));
  background-repeat: no-repeat;
  background-position: bottom;
  background-size: 100% 2px;
  background-origin: content-box;
  /* Боковой padding выше растянул бы фон (padding-box по умолчанию) на добавленные 4px с
     каждой стороны — линия-граница вылезла бы за прежние края. content-box держит линию
     прежней ширины и на прежнем месте (§11.54, PUM-1390). */
}

/* Полоса прокрутки ряда — видна постоянно, пока ряд не помещается, не overlay (§11.54, PUM-1390).
   Стандартный диалект (scrollbar-width/scrollbar-color) достаётся только тем движкам, которые
   webkit-диалект (::-webkit-scrollbar*) не понимают — иначе Blink гасит откалиброванную толщину
   webkit-правила: безусловные стандартные свойства рядом с ним убирают резерв места под полосу,
   и она пропадает из виду вовсе (замер Chrome 151 на живом переполненном баре: offsetHeight -
   clientHeight даёт 0 вместо прежних 6, на скриншоте полосы нет). @supports not
   selector(::-webkit-scrollbar) исполняется только там, где сам селектор непонятен движку —
   Gecko не понимает его (условие истинно), Blink понимает (условие ложно, CSS.supports подтверждает
   это `true` для прямого селектора) — поэтому в Chrome/Safari стандартный блок не применяется
   вовсе, а в Firefox он остаётся единственным источником стиля полосы. Толщина стандартным
   свойством не выражается: Gecko принимает только thin|auto|none, там она системная «тонкая» —
   разная толщина между движками принята явно. Тон в обоих — var(--border), второго определения
   цвета не заводится. */
@supports not selector(::-webkit-scrollbar) {
  .ds-subtabs {
    scrollbar-width: thin;
    scrollbar-color: var(--border) transparent;
  }
}

.ds-subtabs::-webkit-scrollbar {
  height: 6px; /* литерал — калибруется на §13b, токена под толщину не заводится (§11.54) */
}

.ds-subtabs::-webkit-scrollbar-track {
  background: transparent;
}

.ds-subtabs::-webkit-scrollbar-thumb {
  background: var(--border);
}

.ds-subtab {
  /* Подчёркивание активной вкладки — border-bottom: 2px самой кнопки, ложится на нижние 2px
     padding-box контейнера (padding-bottom: 0 у .ds-subtabs + align-items: stretch по умолчанию
     растягивает кнопку до низа контейнера) и перекрывает фоновую линию собой. Прежний приём
     margin-bottom: -2px здесь не нужен и с прокруткой несовместим: клип прокручиваемой области
     идёт по padding-box и срезал бы отрицательный отступ вместе с подчёркиванием (§11.54, PUM-1390). */
  background: none;
  border: none;
  border-bottom: 2px solid transparent;
  padding: var(--space-2) var(--space-4);
  font-size: var(--fs-label);
  font-weight: var(--fw-microcopy);
  color: var(--text-muted);
  cursor: pointer;
  transition: color var(--dur-fast) var(--ease-standard),
              border-color var(--dur-fast) var(--ease-standard),
              background var(--dur-fast) var(--ease-standard);
}

.ds-subtab:hover {
  color: var(--primary);
  background: var(--primary-soft);
}

.ds-subtab-active {
  color: var(--primary);
  border-bottom-color: var(--primary);
  font-weight: var(--fw-th);
}

/* ── Summary tables — mirrors .dash-summary-table-wrap, .dash-summary-table ─── */

.ds-summary-table-wrap {
  overflow-x: auto;
}

/* Сводка «Территорий», docs/design-system.md §11.71. */
.ds-below-count {
  display: inline-flex;
  align-items: center;
  justify-content: flex-end;
  gap: 6px;
}

.ds-below-bar {
  width: 40px;
  height: 6px;
  flex: none;
  overflow: hidden;
  border-radius: 1px;
  background: var(--border-muted);
}

.ds-below-bar-fill {
  display: block;
  height: 100%;
  background: var(--text-muted);
}

.ds-below-count-n {
  min-width: 2ch;
  text-align: right;
  font-size: var(--fs-label);
  font-weight: 500;
  color: var(--text);
  font-variant-numeric: tabular-nums;
}

.ds-below-count--zero .ds-below-count-n {
  font-weight: 400;
  color: var(--text-muted);
}

.ds-summary-table th[rowspan] {
  vertical-align: bottom;
}

/* Контейнерный селектор нужен: базовый .ds-summary-table th иначе сильнее;
   общая причина каскада отложена в PUM-1617. */
.ds-summary-table th.ds-th-group {
  text-align: center;
  border-bottom: 1px solid var(--border);
}

.ds-th-person {
  color: var(--text);
  white-space: normal;
}

.ds-territories-below-table .ds-territories-activity-col,
.ds-territories-below-table .ds-territories-activity-cell {
  min-width: 190px;
}

.ds-territories-below-table .ds-th-person,
.ds-territories-below-table .ds-th-caption {
  display: block;
}

.ds-territories-below-table .ds-activity-cell-main {
  display: grid;
  grid-template-columns: minmax(0, max-content);
}

.ds-territories-below-table thead tr:nth-child(2) > th {
  vertical-align: bottom;
}

.ds-territories-below-table .ds-territories-dynamics-col {
  min-width: 144px;
}

.ds-territories-caption .ds-table-count strong,
.ds-pharmacies-caption .ds-table-count strong {
  color: var(--text);
  font-weight: 500;
}

.ds-summary-table {
  width: 100%;
  border-collapse: collapse;
  font-size: var(--fs-label);
}

.ds-summary-table th {
  text-align: left;
  font-size: var(--fs-th);
  font-weight: var(--fw-th);
  color: var(--text-muted);
  padding: var(--table-header-padding-y) var(--space-3);
  border-bottom: 2px solid var(--border);
  white-space: nowrap;
  /* §13: убраны text-transform: uppercase и letter-spacing */
}

.ds-summary-table td {
  padding: var(--table-header-padding-y) var(--space-3);
  border-bottom: 1px solid var(--border-muted);
  white-space: nowrap;
}

.ds-summary-table tbody tr {
  cursor: pointer;
  transition: background var(--dur-fast) var(--ease-standard);
}

.ds-summary-table tbody tr:hover {
  background: var(--hover-row);
}

/* Active-row highlight для summary-таблиц (B3, PUM-527).
   Зеркалирует паттерн .ds-row.ds-row-active (lines 75–87):
   editorial brand-50 fill + inset 2px left edge (§8.1 design-system). */

.ds-summary-table tbody tr.ds-summary-row-active > td {
  background: var(--active-row);
}

.ds-summary-table tbody tr.ds-summary-row-active > td:first-child {
  box-shadow: inset 2px 0 0 var(--active-row-edge);
}

.ds-summary-table tbody tr.ds-summary-row-active:hover > td {
  background: var(--active-row);
}

.ds-summary-table tbody tr:focus-visible > td,
.ds-bands tbody tr.ds-band-pharmacy:focus-visible > td,
.ds-table tbody tr.ds-bridge-row:focus-visible > td,
.ds-table tbody tr.ds-bridge-staff-toggle:focus-visible > td,
.ds-table tbody tr.ds-hier-row-l0:focus-visible > td,
.ds-table tbody tr.ds-hier-row-l1:focus-visible > td,
.ds-table tbody tr.ds-hier-row-l2:focus-visible > td {
  box-shadow: inset 0 3px 0 var(--focus-ring-color), inset 0 -3px 0 var(--focus-ring-color);
}

.ds-summary-table tbody tr:focus-visible > td:first-child,
.ds-bands tbody tr.ds-band-pharmacy:focus-visible > td:first-child,
.ds-table tbody tr.ds-bridge-row:focus-visible > td:first-child,
.ds-table tbody tr.ds-bridge-staff-toggle:focus-visible > td:first-child,
.ds-table tbody tr.ds-hier-row-l0:focus-visible > td:first-child,
.ds-table tbody tr.ds-hier-row-l1:focus-visible > td:first-child,
.ds-table tbody tr.ds-hier-row-l2:focus-visible > td:first-child {
  box-shadow: inset 3px 0 0 var(--focus-ring-color), inset 0 3px 0 var(--focus-ring-color), inset 0 -3px 0 var(--focus-ring-color);
}

.ds-summary-table tbody tr:focus-visible > td:last-child,
.ds-bands tbody tr.ds-band-pharmacy:focus-visible > td:last-child,
.ds-table tbody tr.ds-bridge-row:focus-visible > td:last-child,
.ds-table tbody tr.ds-bridge-staff-toggle:focus-visible > td:last-child,
.ds-table tbody tr.ds-hier-row-l0:focus-visible > td:last-child,
.ds-table tbody tr.ds-hier-row-l1:focus-visible > td:last-child,
.ds-table tbody tr.ds-hier-row-l2:focus-visible > td:last-child {
  box-shadow: inset -3px 0 0 var(--focus-ring-color), inset 0 3px 0 var(--focus-ring-color), inset 0 -3px 0 var(--focus-ring-color);
}

.ds-summary-table tbody tr.ds-summary-row-active:focus-visible > td:first-child {
  box-shadow: inset 2px 0 0 var(--active-row-edge), inset 3px 0 0 var(--focus-ring-color), inset 0 3px 0 var(--focus-ring-color), inset 0 -3px 0 var(--focus-ring-color);
}

.ds-summary-table tbody tr:focus-visible,
.ds-bands tbody tr.ds-band-pharmacy:focus-visible,
.ds-table tbody tr.ds-bridge-row:focus-visible,
.ds-table tbody tr.ds-bridge-staff-toggle:focus-visible,
.ds-table tbody tr.ds-hier-row-l0:focus-visible,
.ds-table tbody tr.ds-hier-row-l1:focus-visible,
.ds-table tbody tr.ds-hier-row-l2:focus-visible {
  outline: none;
}

/* ── Editorial card — general atom (§11.8, PUM-531) ─── */
/* Консолидирует .ds-card-summary (PUM-495): правила идентичны.
   Одно использование .ds-card-summary в management.js обновлено на .ds-card. */

.ds-card {
  /* §13: без box-shadow на карточках */
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  padding: var(--space-6);
}

/* ── Block-header pattern — editorial section-header (§11.9, PUM-531) ─── */

.ds-block-header {
  display: flex;
  flex-direction: column;
  gap: var(--gap-metric-stack);    /* 4px */
  margin-bottom: var(--gap-section-items);
}

/* Action-variant (§11.9, CEO 2026-07-25): title слева + compact CTA справа
   natural-width — вместо базового full-width CTA на широкой поверхности. */
.ds-block-header--action {
  flex-direction: row;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-4);   /* 16px min-gap title↔CTA */
}

/* Scoped override: внутри .ds-block-header, .ds-block-title берёт H3-роль (Plex Sans 14px/600).
   Глобальный .ds-block-title ниже (Plex Serif / fw-th — section-label в network)
   остаётся без изменений. */
.ds-block-header > .ds-block-title {
  font-family: var(--font-sans);
  font-size: var(--fs-body);  /* 14px */
  font-weight: 600;
  color: var(--text);
  margin: 0;
}

.ds-block-context {
  font-family: var(--font-sans);
  font-size: var(--fs-label);
  font-weight: var(--fw-label);
  color: var(--text-muted);
}

/* ── Activity inline suffixes — group-selector kind / source (§11.9 / §11.34, PUM-678) ─── */
/* PUM-678 (2026-05-21): родственные suffix-atom'ы с identical visual treatment объединены
   через group-selector. Композиция в имени Активности — middle-dot chain («·»):
     {activity_name} [· {kind_label}] [· {source_label}]
   - kind-suffix (gap #4) — kind=other (например «· товар дня»); chain — silent baseline.
   - source-suffix (gap #7) — composition_source = live_period_forming («· период формируется»)
     с modifier --forming для warn-tint. Остальные 3 composition states (snapshot /
     live_period_active / live_background) — silent baseline.
   margin-left: 6px — заменяет leading-space pattern в JS callsite; spacing централизован.

   status-suffix (`.ds-activity-status-suffix`, «· фоновая» / «· отключена») снят из
   группы PUM-1261: последним потребителем была ячейка «Активность» «Записей»
   (checks_table.js), статус темы там теперь кодируется риской тултипа (.ds-tt-status,
   §11.39), не текстовым суффиксом. §12.3 design-system.md. */

.ds-activity-kind-suffix,
.ds-activity-source-suffix {
  font-size: var(--fs-label);     /* 13px */
  font-weight: var(--fw-label);   /* 400 */
  color: var(--text-muted);
  white-space: nowrap;
  margin-left: 6px;               /* design-spec exception §6.2 — точное значение по PUM-678 бри́фу */
}

.ds-activity-source-suffix--forming {
  color: var(--warn-500);         /* transient state — tan/honey, единственный visible composition_source */
}

/* ── Activity first-cell — первая ячейка строки Активности (§11.33, §11.35, PUM-843) ─── */
/* Обычная table-cell (не flex на <td>) — иначе браузер схлопывает бокс по высоте контента
   и active-row fill (§7.2/§8.1) красится только по этому укороченному боксу.
   Vertical-stack «имя + marginalia» обеспечивается вложенным .ds-activity-cell-main. */

.ds-activity-cell {
  vertical-align: middle;
}

/* ── Activity cell main-line — layout-wrapper для chevron + имя (§11.33, PUM-843) ─── */
/* block-level flex-строка: chevron (optional) слева + .ds-activity-name справа.
   Как block-element создаёт перенос строки перед сиблингом .ds-row-marginalia. */

.ds-activity-cell-main {
  display: flex;
  align-items: center;
  gap: var(--space-1);            /* 4px — tight spacing chevron ↔ имя (зеркалирует §11.37) */
}

/* ── Row marginalia — pending_backfill info-line (§11.35, PUM-678 gap #8) ─── */
/* Тон: graphite muted text + inline clock-icon + timestamp в mono.
   Без bg-tint — banner-like читается как error; здесь подсказка, не ошибка.
   Vertical footprint conditional — рендерится только при present pending_backfill. */

.ds-row-marginalia {
  display: inline-flex;
  align-items: center;
  gap: 6px;                       /* design-spec exception §6.2 — точное значение по PUM-678 бри́фу */
  font-size: var(--fs-microcopy);  /* 11px */
  font-weight: 400;
  color: var(--text-muted);
  margin-top: var(--space-1);     /* 4px */
}

.ds-row-marginalia-icon {
  display: inline-flex;
  width: 12px;
  height: 12px;
  flex-shrink: 0;
}

.ds-row-marginalia-icon svg {
  width: 12px;
  height: 12px;
  stroke-width: 1.25;
}

.ds-row-marginalia-time {
  font-family: var(--font-mono);
  font-size: var(--fs-microcopy);  /* 11px */
  font-variant-numeric: tabular-nums;
}

/* ── Activity role-chip — column «Роли» в саммари-таблице (§11.33, PUM-855) ─── */
/* RETIRED (PUM-855, §12.3): .ds-role-tag / --primary → .ds-role-chip / --signal / --quiet.
   Причина: near-black fill на выводимую константу (chain→upsell) нарушал data-ink + editorial-канон +
   gravity-инверсию (severity приглушалась; ADR-044). Новая ось: заметность ∝ иерархии роли в строке.
   Буквенный квадратный chip (19px) с asymmetric tone signal/quiet. NB: .ds-role-sub-name, .ds-role-row (§11.39, сетка ролей ячейки «Активность», PUM-1261,
   заменила .ds-opp-role) — роль словом, не трогать. */

.ds-role-chip {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 19px;
  height: 19px;
  border-radius: var(--radius-sm);       /* 4px */
  font-size: var(--fs-microcopy);        /* 11px */
  font-weight: 600;
  line-height: 1;
  font-feature-settings: "tnum";
}

.ds-role-chip + .ds-role-chip { margin-left: 5px; }  /* design-spec exception §6.2 — pre-flight tune 4–6px */

/* primary / single — заметный */
.ds-role-chip--signal {
  background: var(--surface-2);
  color: var(--graphite-700);
  box-shadow: inset 0 0 0 1px var(--border-strong);
}

/* secondary — тише */
.ds-role-chip--quiet {
  background: transparent;
  color: var(--text-subtle);
  box-shadow: inset 0 0 0 1px var(--border);
}

/* двубуквенный «Дл» (доля) и fallback-слово */
.ds-role-chip--two { width: auto; min-width: 19px; padding: 0 5px; }

/* ── Category attention adjacent atom (§11.10, PUM-678 gap #5) ─── */
/* «Мало данных» attention-marker — adjacent atom рядом с .ds-category-label-insuf.
   Полоска .ds-category-label-bar — fill --cat-*, текст — --cat-*-tx (fill/text split,
   §11.5 fill/text split; прежний инвариант text-color=bar-color снят);
   attention-signal через отдельный 14px warn-tinted icon, не через recolor text. */

.ds-cat-with-attention,
.ds-emp-identity {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);                 /* 8px */
}

.ds-attention-icon {
  display: inline-flex;
  width: 14px;
  height: 14px;
  color: var(--warn-500);
}

.ds-attention-icon svg {
  width: 14px;
  height: 14px;
  stroke-width: 1.5;
}

/* .ds-emp-identity — identity-ячейка сотрудника (§11.47); анатомия общая
   с .ds-cat-with-attention (§11.10), маркер — reuse .ds-attention-icon. */

/* ── Row clickable (§7.6 sub-row toggle, PUM-678/PUM-857) ─── */
/* RETIRED (PUM-857, §12.3): drill-кластер §11.37 (.ds-drill-row / .ds-drill-head /
   .ds-drill-table / .ds-drill-role-name / .ds-drill-role-excluded / .ds-drill-section)
   ретайрен — единственный консьюмер (Сотрудник multi-role) мигрировал на §7.6 sub-row
   pattern. .ds-row-clickable обслуживает строки с действием по щелчку. Причина retire: R1–R7 (WHY PUM-857). Supersede → §7.6. */

.ds-row-clickable {
  cursor: pointer;
}

.ds-row-clickable:hover td {
  background: var(--hover-row);
}

/* ── Collapsible multi-role rows в activity summary (§7.6 indent + §11.37 chevron, PUM-787) ─── */
/* Chevron-button для expand/collapse per-role sub-rows в activity summary таблицах.
   Scoped expand: chevron click → toggle, остальная часть строки → primary action (выбор Активности строки).
   Sub-rows используют inline style display:none/'' для toggle (не CSS-класс) — проще для columnar rows. */

.ds-summary-chev {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 12px;
  height: 12px;
  border: none;
  background: transparent;
  padding: 0;
  cursor: pointer;
  color: var(--text-muted);
  flex-shrink: 0;
  vertical-align: middle;
}

.ds-summary-chev:hover {
  color: var(--text);
}

.ds-summary-chev:focus-visible {
  outline: none;
  box-shadow: var(--focus-ring);
  border-radius: var(--radius-sm);
}

.ds-summary-chev svg {
  width: 12px;
  height: 12px;
  stroke-width: 1.25;
}

.ds-summary-sub-row:hover td {
  background: var(--hover-row);
}

.ds-summary-table td.ds-sub-row-indent,
.ds-bridge-table td.ds-sub-row-indent {
  padding-left: var(--space-6);
}

.ds-role-sub-name {
  color: var(--text-muted);
  font-weight: 400;
  font-size: var(--fs-label);
}

/* ── Widget-slot pattern для Уровня поведения/качества (§11.38, PUM-678 gap #1) ─── */
/* Слоты под виджеты Эпика 4 (PUM-619/620/621) — рендерятся в drill-down expand zone
   строки Активности, ниже секции «Детализация по ролям». На пилоте (BEHAVIOR_LEVEL_ENABLED
   /QUALITY_LEVEL_ENABLED = false) виджеты не видны; HTML-каркас готов принять content
   без переделки разметки. Conditional rendering (не display:none) — DOM clean. */

.ds-widget-grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: var(--space-4);                 /* 16px */
}

.ds-widget-grid--2col {
  grid-template-columns: repeat(2, 1fr);
  max-width: 720px;                    /* card не растягивается на всю ширину при 2-col */
}

/* Widget card — alternative structure к .ds-card (§11.8). Не modifier:
   flex-column header+body+placeholder + min-height для placeholder consistency —
   качественно отличается от surface-only modifier (CTO decision PUM-678 gap #1 open question). */
.ds-widget-card {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  padding: var(--space-5);             /* 20px */
  min-height: 140px;                   /* design-spec exception §6.2 — для placeholder/content consistency */
  display: flex;
  flex-direction: column;
  gap: var(--space-3);                 /* 12px */
}

.ds-widget-card-header {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
}

.ds-widget-card-title {
  font-family: var(--font-sans);
  font-size: var(--fs-label);          /* 13px */
  font-weight: 600;
  color: var(--text);
}

.ds-widget-card-context {
  font-size: var(--fs-microcopy);      /* 11px */
  color: var(--text-muted);
  text-transform: uppercase;
  letter-spacing: 0.04em;
}

.ds-widget-card-body {
  flex: 1;
  display: flex;
  align-items: center;
  justify-content: center;
}

.ds-widget-card-placeholder {
  font-size: var(--fs-label);
  color: var(--text-muted);
  text-align: center;
  font-style: italic;
}

/* RETIRED (PUM-857, §12.3): .ds-drill-section + .ds-drill-section — drill-separator
   ретайрен вместе с drill-кластером §11.37. */

/* ── Category-label — V6: horizontal bar + colored text (§11.10, PUM-535) ─── */
/* V6 bar 24×8 + colored text; fill/text split — bar=--cat-* (fill), text=--cat-*-tx.
   Модификатор категории задаётся на container (.ds-category-label-<cat>),
   bar получает background через descendant selector. */

.ds-category-label {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  font-family: var(--font-sans);
  font-size: var(--fs-body);   /* 14px — body level, читается на 50+ строк */
  font-weight: 500;
  /* color задаётся модификатором .ds-category-label-<cat> */
}

.ds-category-label-bar {
  width: 24px;
  height: 8px;
  border-radius: 2px;
  display: inline-block;
  flex-shrink: 0;
  /* background задаётся через descendant selector модификатора */
}

/* 6 модификаторов: color (текст, --cat-*-tx) на container + background (полоска, fill --cat-*) на child bar; на develop/insuf расходятся — §11.5 */
.ds-category-label-leader   { color: var(--cat-leader-tx); }
.ds-category-label-leader   .ds-category-label-bar { background: var(--cat-leader); }
.ds-category-label-prospect { color: var(--cat-prospect-tx); }
.ds-category-label-prospect .ds-category-label-bar { background: var(--cat-prospect); }
.ds-category-label-norm     { color: var(--cat-norm-tx); }
.ds-category-label-norm     .ds-category-label-bar { background: var(--cat-norm); }
.ds-category-label-develop  { color: var(--cat-develop-tx); }
.ds-category-label-develop  .ds-category-label-bar { background: var(--cat-develop); }
.ds-category-label-focus    { color: var(--cat-focus-tx); }
.ds-category-label-focus    .ds-category-label-bar { background: var(--cat-focus); }
.ds-category-label-insuf    { color: var(--cat-insuf-tx); }
.ds-category-label-insuf    .ds-category-label-bar { background: var(--cat-insuf); }

/* ── Hierarchy rows — mirrors .mgmt-cc-row-tu/manager/pharmacy, .mgmt-terr-row-tu/manager/pharmacy, .mgmt-cc-arrow ─── */
/* Две пятёрки legacy-классов объединены в единое семейство по семантическому уровню иерархии (ТУ → заведующий → аптека) */

/* Unified surface — иерархия НЕ через tint (PUM-503 §H).
   Три сигнала иерархии: indent (§G), font-weight/color (здесь), chevron policy (l0/l1 раскрываемые, l2 без). */

.ds-table tbody tr.ds-hier-row-l0 > td {
  background: var(--surface);
  font-weight: var(--fw-table-num);   /* 500 — ТУ bold */
}

.ds-table tbody tr.ds-hier-row-l1 > td {
  background: var(--surface);
  /* заведующий — regular body weight (400) */
}

.ds-table tbody tr.ds-hier-row-l2 > td {
  background: var(--surface);
  color: var(--text-muted);   /* аптека — tonal mute */
}

/* Группирующая отделённость ТУ-блоков через воздух + hairline */
.ds-table tbody tr.ds-hier-row-l0:not(:first-child) > td {
  border-top: 1px solid var(--border);
  padding-top: var(--space-3);
  padding-bottom: var(--space-3);
}

/* Clickable rows — ТУ, заведующий и аптека кликабельны */
.ds-table tbody tr.ds-hier-row-l0,
.ds-table tbody tr.ds-hier-row-l1,
.ds-table tbody tr.ds-hier-row-l2 {
  cursor: pointer;
}

/* Консолидированный hover для всех иерархических строк */
.ds-table tbody tr.ds-hier-row-l0:hover > td,
.ds-table tbody tr.ds-hier-row-l1:hover > td,
.ds-table tbody tr.ds-hier-row-l2:hover > td {
  background: var(--hover-row);
}

/* Отступы уровней (левый padding первой ячейки) — §G PUM-503: 12/24/48 удвоение на каждом шаге */
.ds-table tbody tr.ds-hier-row-l0 > td:first-child { padding-left: var(--space-3); }   /* 12px */
.ds-table tbody tr.ds-hier-row-l1 > td:first-child { padding-left: var(--space-6); }   /* 24px */
.ds-table tbody tr.ds-hier-row-l2 > td:first-child { padding-left: var(--space-12); }  /* 48px */

.ds-hier-arrow {
  display: inline-block;
  width: var(--space-3);
  font-size: var(--fs-microcopy);
  color: var(--text-muted);
  margin-right: var(--space-1);
}

/* ── Период фоновых Активностей — обёртка над сегментами блока фоновых
   (mirrors .ds-bg-activities-header sibling) ─── */
/* PUM-1614 (окно 2): второй потребитель группы, `.ds-period-btns` (кнопочная
   раскладка окна — блок фоновых и строка выбора Активности), снят вместе со
   старой разметкой: сегменты (.ds-seg, §11.62, background_window.js) заменили
   её везде. Групповая декларация схлопнута в одиночный селектор — было
   PUM-1184 «единый источник раскладки ряда для обеих живых семей». */

.ds-bg-activities-period {
  display: flex;
  gap: var(--gap-inline);
  margin-bottom: var(--space-3);
}

/* ── Shared accordion headers/content ─── */

.ds-cc-section {
  margin: var(--space-1) 0 0;
}

.ds-cc-toggle {
  background: transparent;
  border: none;
  display: flex;
  align-items: center;
  gap: var(--gap-inline);
  cursor: pointer;
  user-select: none;
  font-family: var(--font-serif);
  font-size: var(--fs-body);
  font-weight: var(--fw-th);
  color: var(--text);
  padding: var(--space-2) 0;
}

.ds-cc-toggle:hover {
  color: var(--primary);
}

.ds-bg-activities-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-3);
}

.ds-cc-content-wrap {
  margin-top: var(--space-2);
  overflow-x: auto;
}

/* ── Rate indicators (inline text) — mirrors .dash-rate-green, .dash-rate-red ─── */
/* 15px (--fs-severity, exception §5.3), weight 700 (--fw-severity-arrow), tabular-nums.
   Arrow prefix обязателен через child .ds-rate-arrow (§9). */

.ds-rate-pos {
  /* Отклонение ≥0, inline в ячейке таблицы. Землистый sage вместо saturated green. */
  color: var(--pos-700);
  font-size: var(--fs-severity);
  font-weight: var(--fw-severity-arrow);
  font-variant-numeric: tabular-nums;
}

.ds-rate-neg {
  /* Отклонение <0. Землистый terracotta вместо saturated red. */
  color: var(--neg-700);
  font-size: var(--fs-severity);
  font-weight: var(--fw-severity-arrow);
  font-variant-numeric: tabular-nums;
}

.ds-rate-quiet {
  font-size: var(--fs-label);
  font-weight: 500;
  color: var(--text-muted);
  font-variant-numeric: tabular-nums;
}

/* Arrow prefix для severity deviation text (§9).
 * Unicode U+2B06 (⬆) / U+2B07 (⬇) + VS15 (︎ ︎) — heavy black arrows.
 * font-size: 0.9em (~13.5px) — стрелка
 * чуть меньше цифры, компенсирует «узкую» форму глифа визуально. */
.ds-rate-arrow {
  font-weight: inherit;
  margin-right: 3px;
  display: inline-block;
  font-size: 0.9em;
}

/* ── Sparkline (editorial monochrome) ─── */

.ds-sparkline rect { fill: var(--text-muted); }
.ds-sparkline .ds-sparkline-week:hover + rect,
.ds-sparkline rect:hover { fill: var(--graphite-500); }
.ds-sparkline .ds-sparkline-ref {
  pointer-events: none;
  stroke: var(--graphite-500);
  stroke-width: 1;
  /* Ref-line темнее баров (#57534e ~35% L vs --text-muted ~46% L): hierarchy через
     darkness, не hue. Solid, без dasharray. Severity-info живёт на rate-числе header
     (.ds-rate-pos/-neg) — дубль в ref-line избыточен. */
}

.ds-sparkline-empty {
  color: var(--text-subtle);
  display: block;
  text-align: center;
  font-variant-numeric: tabular-nums;
}

/* Sparkline alignment safeguard — §7.3 строка 319, PUM-540.
 * Фиксирует «sparkline → left» на браузерном уровне: TD со sparkline
 * в .ds-table или .ds-summary-table принудительно получает text-align: left
 * даже при ошибочном .ds-col-right. TH защиты не имеет — синхронизация
 * TH/TD по §7.3 строка 326 проверяется в §13b post-flight. */
.ds-table td:has(> svg.ds-sparkline),
.ds-summary-table td:has(> svg.ds-sparkline) {
  text-align: left;
}

/* ── Rate context (справочное число в скобках) ─── */

.ds-rate-context {
  color: var(--text-muted);
  font-weight: var(--fw-body);
  font-variant-numeric: tabular-nums;
}

/* ── Accordion subtitle — §11.7 (PUM-525) ─── */
/* Используется как <span class="ds-accordion-subtitle"> · {subtitleHtml}</span>
   внутри .ds-cc-toggle.
   Middledot · — в HTML-разметке, не в ::before. */

.ds-accordion-subtitle {
  font-size: var(--fs-label);      /* 13px */
  font-weight: var(--fw-label);    /* 400 */
  color: var(--text-muted);
}

/* ── Period buttons — атом отдельной toggle-кнопки периода (общий: Записи / банк / плеер / калибровка) ─── */

.ds-period-btn {
  padding: var(--space-1) var(--space-3);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  background: var(--surface);
  cursor: pointer;
  font-size: var(--fs-label);
  transition: background var(--dur-fast) var(--ease-standard),
              border-color var(--dur-fast) var(--ease-standard),
              color var(--dur-fast) var(--ease-standard);
}

.ds-period-btn:hover {
  background: var(--hover-row);
  border-color: var(--border);
}

.ds-period-btn-active {
  /* §E PUM-503: editorial outline вместо filled primary.
     Активный период — текст+контур, не заливка цветом. */
  background: var(--surface);
  color: var(--text);
  border: 1px solid var(--text);
  font-weight: var(--fw-th);   /* 500 */
}

/* ── Chip-filter extensions — disabled state + .ds-chip-count counter (§11.23, PUM-678 gap #3) ─── */
/* Mini-atom .ds-chip-count — counter `(N)` справа от label в filter-chip
   (например `Цепочки 8`). Spec'ится сейчас, реализуется в Эпике 3 («Все Активности»
   pre-filter). Disabled-state — chip отображается с counter `0`, не скрывается:
   «вид существует, в текущем scope нет» — дисциплинирующий signal, UI-стабильность
   против layout-jump при изменении scope. */

.ds-chip-count {
  margin-left: var(--space-1);     /* 4px */
  font-size: var(--fs-microcopy);  /* 11px */
  font-weight: 500;
  color: var(--text-muted);
  font-variant-numeric: tabular-nums;
}

.ds-period-btn-active .ds-chip-count {
  color: var(--text);
}

.ds-period-btn:disabled {
  color: var(--text-subtle);
  cursor: not-allowed;
  pointer-events: none;
}

.ds-period-btn:disabled .ds-chip-count {
  color: var(--text-subtle);
}

/* PUM-1505 (окно 3), git-blame reference: .ds-records-period /
   .ds-records-period-presets / .ds-records-period-range (mirrors
   .records-period* — PUM-539 B4) сняты (§12.3 «Снято») — кнопочная
   раскладка периода потеряла последнего продуктового потребителя вместе с
   переездом обоих контуров («Записи» PUM-1470 окно 2, «Банк ЛП» PUM-1505
   окно 1) на общий носитель period_presets.js: сегментную плашку `.ds-seg`
   (§11.62). Ни один продуктовый .js больше не несёт литерал этих трёх
   классов разметкой — period_presets.js и
   employees/records_range_domain.test.mjs упоминают старое имя только
   прозой комментария («ушли вместе со старой карточной разметкой
   контрола»), не HTML. Адаптивный блок @media (max-width: 768px) в
   static/explorer.css («Records Tab period controls», PUM-438 → PUM-539
   B4), матчивший ровно эти три класса, снят тем же окном. Атом отдельно
   стоящей кнопки периода (`.ds-period-btn`/`.ds-period-btn-active`, выше в
   этом файле) этим окном НЕ затронут — у него живой потребитель вне зоны
   отбора (скорость плеера). Групповая обёртка `.ds-period-btns` — не путать
   с `.ds-period-btn` в единственном числе — снята позже, PUM-1614 (окно 2):
   ряды периодов дашбордов (блок фоновых «Аптек», строка выбора Активности),
   названные здесь живыми на момент этой записи, сами были её последними
   потребителями и переехали на сегменты (`.ds-seg`, §11.62).

   Действующее правило дизайн-системы ни на один из трёх снятых атомов не
   ссылалось — снятие не требует парной правки docs/design-system.md. */

/* ── §11.62 Сегментный переключатель (`.ds-seg`) — PUM-1470, окно 2 из
   четырёх плана records-filter-phrase-language. Канонический блок из
   docs/design-system.md §11.62, перенесён дословно и целиком: свойство,
   отсутствующее здесь, отсутствует намеренно (эскалация Tech Lead на
   недостающее, не дописывание по месту — прецедент цены К-1457-7).

   Форма целевая: новый выбор «один из N» пишется на ней, не на отдельно
   стоящих .ds-period-btn (уходящая форма — §11.62). Перечня потребителей
   комментарий не держит намеренно — он менялся бы с каждым переездом молча
   (ADR-083); правило от их числа не зависит. Видов два, выбирает их зона:
   базовая высота 30px у зоны-фразы, .ds-seg--field 38px у карточной зоны. */

.ds-seg {
  display: inline-flex; align-items: center; height: 30px; box-sizing: border-box;
  border: 1px solid var(--border); border-radius: var(--radius-sm);
  background: var(--surface-2); padding: 2px;
}
.ds-seg--field { height: 38px; }   /* вид поля — карточная зона с подписями над контролами (§11.21 .ds-select) */
.ds-seg--touch { height: 44px; }
.ds-seg > button {
  /* display/gap — базовое свойство, не модификатор: сегмент способен нести знак
     перед подписью. Получают и сегменты без знака, включая пресеты периода
     фразы «Записей» — радиус назван в §11.62, проверка посадки подписи там же
     объявлена пунктом pre-flight §13a. */
  display: inline-flex; align-items: center; gap: var(--space-2);
  font-family: var(--font-sans); font-size: var(--fs-label);
  color: var(--text-muted); background: none; border: none;
  border-radius: 3px; padding: 0 var(--space-3); height: 100%; cursor: pointer;
  transition: background var(--dur-fast) var(--ease-standard), color var(--dur-fast) var(--ease-standard);
}
.ds-seg > button:hover { color: var(--text); }
.ds-seg > button.ds-seg-active {
  background: var(--surface); color: var(--text);
  font-weight: var(--fw-th); box-shadow: 0 0 0 1px var(--border);
}
.ds-seg > button:focus-visible { outline: none; box-shadow: var(--focus-ring-graphite); }
/* Прошедший элемент — приглушённая подпись (temporal); готовность несёт знак в
   слоте, а не этот тон (§11.62 «Два независимых признака — два носителя»). */
.ds-seg > button.ds-seg-past { color: var(--text-subtle); }
/* Выключенный ряд (§11.62): после активного и прошедшего — тон выключенного у всех подписей. */
.ds-seg > button:disabled { color: var(--disabled-text); cursor: not-allowed; }
.ds-seg > button.ds-seg-active:disabled { background: var(--disabled-bg); box-shadow: inset 0 0 0 1px var(--border); }
/* Хвост ряда за hairline: называет отсечённое, выбором не является. Селектор
   не требует button — сегодня носитель неинтерактивный (приписка «+N раньше»),
   раскрывающая кнопка отложена до названного в §11.62 условия. */
.ds-seg > .ds-seg-more {
  display: inline-flex; align-items: center; font-family: var(--font-sans);
  font-size: var(--fs-label); color: var(--text-subtle);
  padding: 0 var(--space-3); height: 100%;
  border-left: 1px solid var(--border); border-radius: 0 3px 3px 0; margin-left: 2px;
}

/* Вырожденный ряд: единственный элемент рисуется подписью, а не контролом
   (§11.62). Видов два, тем же правилом, что и .ds-seg выше: база 30px —
   зона-фраза, .ds-seg-single--field 38px — карточная зона, как .ds-seg--field.
   PUM-1485 окно C — база СМЕНЕНА с 38 на 30, не добавлена величина рядом:
   прежняя редакция выводила 38px из «единственного потребителя — карточной
   зоны», а потребителя к тому моменту не существовало вовсе (найдено QA
   на приёмке PUM-1470) — первым потребителем этого атома стал вырожденный
   ряд ВО ФРАЗЕ (employees/records.js, слот «Активность не выбрана»). Обход
   дерева до правки подтвердил: у `.ds-seg-single` не было ни одного
   JS/HTML-потребителя (только определение здесь и упоминание в
   docs/design-system.md) — смена базы не задела ни один живой рендер. */
.ds-seg-single {                  /* подпись вместо ряда — голосом невыбранного сегмента */
  display: inline-flex; align-items: center; gap: var(--space-2); height: 30px;
  font-family: var(--font-sans); font-size: var(--fs-label); color: var(--text-muted);
}
.ds-seg-single--field { height: 38px; }   /* вид поля — карточная зона, как .ds-seg--field */

/* ── Пара дат сегментного периода (`.ds-fp-dates` / `.ds-fp-dash`) — подмножество
   канонического блока §11.61 docs/design-system.md, ровно те два селектора,
   которые производит period_presets.js (период — их единственный producer,
   §11.61 «Общий носитель периода»; носитель отдаёт ровно одну форму обоим
   контурам — PUM-1505, параметра выбора формы больше нет). Высоту паре дат
   даёт .ds-input--compact (30px, PUM-973) — эти два селектора её не несут,
   только gap и цвет тире. */
.ds-fp-dates { display: inline-flex; align-items: center; gap: var(--space-2); }
.ds-fp-dates > input[type=date] { font-variant-numeric: tabular-nums; }
.ds-fp-dash { color: var(--text-subtle); user-select: none; }

/* ── §11.61 Фильтр-фраза «Записей» — канонический блок (PUM-1470, окно 4 из
   четырёх плана records-filter-phrase-language). Перенесён из
   docs/design-system.md §11.61 дословно и целиком — свойство, отсутствующее
   здесь, отсутствует намеренно (эскалация Tech Lead на недостающее, не
   дописывание по месту — прецедент цены К-1457-7). .ds-fp-dates/.ds-fp-dash
   — окно 2, выше; .ds-fp-token* / .ds-fp-menu* (§11.59) — окно 3, ниже в
   этом файле; здесь — то, что производит routing/shell.js напрямую (связки,
   разделители, свободный ввод «Товар», зона действий, уведомление).

   Зона не несёт ни своего воздуха, ни своей линейки — `padding: 0`, нижней
   границы нет (PUM-1506, §11.68, окно B: закрыло остаточную дельту — до
   него gap/padding/border-bottom здесь ещё не были доведены до буквы
   §11.61). Это не экономия пикселей ради экономии: зона перестала быть
   блоком, а границу «запрос / выдача» держит рамка карточки таблицы и,
   выше, нижняя линия блока навигации (`.ds-navblock`, §11.68) — своей
   собственной зоне отбора заводить незачем.

   .ds-fp-input НЕ несёт transition — решение CEO 04.09 (§11.61 «Расширение
   мгновенное, motion зона не несёт вовсе»): §10.4 запрещает анимировать
   width, карвинг исключения требует ПРОВАЛА removability-теста, здесь тест
   ПРОХОДИТ (убрать анимацию — смысл цел), значит она была бы украшением. Не
   дописывать плавное раскрытие из брифа обратно.

   margin-bottom — величина, откалиброванная на живом экране (PUM-1481,
   слот твёрдости «отступ над подписью»): подпись обязана читаться
   принадлежащей своей таблице, а не висеть посередине между ней и фразой.
   Прижимает её сюда только этот отступ. До PUM-1505 (окно 3) снизу
   величина была заложницей инварианта «Банка ЛП» на `--space-3` (§11.63
   «Нижний отступ — заложник, а не замысел») и свободы не имела: §11.61
   объявляла переход на `--space-3` в предположении, что снизу встанет
   `--space-2` (пара 12 сверху / 8 снизу — перевес в 4px в пользу таблицы),
   но с закреплённым снизу `--space-3` тот же переход давал 12 и 12 —
   подпись равноудалена от обеих, ровно то положение, которое §11.63
   называет отказом; `--space-4` восстанавливал исходный перевес
   искусственно (16 сверху, 12 снизу — те же 4px разницы, что задумывал
   канон). Замер 05.09.2026 на 1440px зафиксировал это промежуточное
   состояние.

   PUM-1505 (окно 3): инвариант «ни один пиксель Банка не меняется» снят
   решением CEO 09.09.2026 в рамках хрома, а не наступлением события
   (§11.63, «условие-заложник снято решением»), и пара доведена до
   канонической: margin-bottom здесь — `--space-3`, `.ds-table-caption`
   (ниже в этом файле) — `--space-2`; разница 4px (12 сверху, 8 снизу) —
   замыслом §11.61/§11.63, не компенсацией. Величина принадлежит атому
   зоны-фразы, а не экрану, и приходит на каждый контур, который атом
   несёт; перечня контуров здесь нет намеренно — он снимок (ADR-083),
   расходящийся с деревом молча, ровно как разошёлся снимок потребителей в
   §11.63. Сличение величин на живом рендере — Tech Lead
   (docs/ui-checklists.md §13b), не это окно. */
.ds-filter-phrase {
  display: flex; flex-direction: column; gap: var(--space-2);
  padding: 0;
  margin-bottom: var(--space-3);
}
.ds-fp-line {
  display: flex; align-items: center; flex-wrap: wrap;
  gap: var(--space-2); row-gap: var(--space-2);
}
.ds-fp-conn { font-size: var(--fs-label); color: var(--text-subtle); }
.ds-fp-dot  { color: var(--border-strong); user-select: none; }
.ds-fp-pair { display: inline-flex; align-items: center; gap: var(--space-2); }

.ds-fp-input {
  font-family: var(--font-sans); font-size: var(--fs-body); font-weight: var(--fw-th);
  color: var(--text-muted); background: none; border: none;
  border-bottom: 1px dashed var(--border-strong);
  padding: 3px 2px 2px; width: 96px;
}
.ds-fp-input::placeholder { color: var(--text-muted); font-weight: var(--fw-th); opacity: 1; }
.ds-fp-input:focus { outline: none; border-bottom: 1px solid var(--text); color: var(--text); width: 220px; }
.ds-fp-input--set { color: var(--text); border-bottom: 1px solid var(--text); }

.ds-fp-actions { margin-left: auto; display: flex; align-items: center; gap: var(--space-3); }
.ds-fp-notice  { font-size: var(--fs-microcopy); color: var(--warn-700); padding-left: var(--space-1); }

/* ── Distribution bar (editorial, §11.5, PUM-527) ─── */
/* Заменяет legacy .cat-bar* (§12.3 — legacy не трогаем).
   Используется в renderCategoryBar() утилиты utils.js. */

.ds-dist-bar-wrap {
  display: inline-flex;
  align-items: center;
  /* gap: 6px — значение между --space-1 (4px) и --space-2 (8px);
     токен --space-1_5 отсутствует в tokens.css */
  gap: 6px;
}

.ds-dist-bar {
  display: flex;
  width: 160px;                         /* fixed §11.5 ~160-180px */
  height: 10px;                         /* §11.5 */
  border-radius: var(--radius-sm);      /* 4px — фактическое значение tokens.css; Notes упоминали 2px */
  overflow: hidden;
  background: var(--border-muted);      /* empty tail если сумма сегментов < width */
}

.ds-dist-bar-seg {
  display: inline-block;
  height: 100%;
  /* bg-gap 1px между сегментами в цвете --bg (§11.5 «бумажная щель») */
  box-shadow: inset -1px 0 0 var(--bg);
  /* цвет сегмента задаётся inline-стилем background:var(--cat-X) в renderCategoryBar */
}

/* ── Focus-ring — keyboard-navigation (PUM-499, §7; §C refinement PUM-507) ───────────────────── */
/*
 * :focus-visible — отображается ТОЛЬКО при keyboard-навигации, не при click мышью.
 * Это критично для UX: mouse-user не видит halo на каждом клике.
 *
 * Фокус строк summary-таблиц и hierarchy-rows определён единым блоком рядом с
 * .ds-summary-row-active: ореол рисуется изнутри на > td (§8.1 design-system.md).
 *
 * На остальных элементах (accordion headers, subtabs, buttons):
 *   box-shadow: var(--focus-ring) — burgundy halo 3px offset.
 *
 * outline: none на :focus (без visible) убирает default browser outline во всех режимах,
 * :focus-visible добавляет editorial ring только при keyboard.
 */

/* §B verified 2026-04-19 (PUM-507): halo через box-shadow корректно применяется
   на сгруппированных селекторах. Добавлен .ds-btn-retry. Дизайнер интерпретировал halo
   как solid outline из-за визуального сходства. */
.ds-cc-toggle:focus,
.ds-subtab:focus,
.ds-period-btn:focus,
.ds-btn-retry:focus,
.ds-summary-chev:focus {
  outline: none;
}

.ds-cc-toggle:focus-visible,
.ds-subtab:focus-visible,
.ds-period-btn:focus-visible,
.ds-btn-retry:focus-visible {
  outline: none;
  box-shadow: var(--focus-ring);
  border-radius: var(--radius-sm);
}

/* §11.49: инлайн-ссылка рейтинга и лотка Активности — единственный носитель формы
 * «текст остаётся текстом, но ведёт». Потребители и предикаты — §11.49.
 * Affordance: cursor:pointer + hover underline + focus-ring (§8.1 halo).
 * Цвет наследуется от контекста строки или лотка.
 * Элемент без этого класса ссылкой не становится и tab-stop'ом не является.
 */
.ds-activity-heading-link {
  color: inherit;          /* цвет наследуется от строки или лотка */
  text-decoration: none;
  cursor: pointer;
}
.ds-activity-heading-link:hover {
  text-decoration: underline;
}
.ds-activity-heading-link:focus-visible {
  outline: none;
  box-shadow: var(--focus-ring);
  border-radius: 2px;
}

.ds-activity-heading-link--underlined {
  text-decoration: underline;
  text-decoration-thickness: 1px;
  text-decoration-color: var(--border-strong);
  text-underline-offset: 3px;
}
.ds-activity-heading-link--underlined:hover { text-decoration-color: currentColor; }

/* ── Рейтинг «Результатов» (§11.49, PUM-1590) ───────────────────────── */

/* Тихий голос направления (§4.3) — общий атом: разрезы, счётчик «По целевым» (§11.53) */
.ds-slice-dev-neg,
.ds-slice-dev-pos {
  font-size: var(--fs-label);
  font-weight: 500;
  font-variant-numeric: tabular-nums;
}
.ds-slice-dev-neg { color: var(--neg-700); }
.ds-slice-dev-pos { color: var(--pos-700); }

/* Ширину «Активности» считаем от ширины зоны (контейнер), а не окна: медиа-запрос ужимал её
   ступенькой — на 1280 до 300 px вместо нужных 324, и «Динамика» получала лишнее (§11.49 «Ширины»). */
.ds-rank-zone { container-type: inline-size; }
.ds-rank-table {
  table-layout: fixed;
  min-width: 1206px;
}
.ds-rank-table > thead > tr > th { background: var(--surface); }
.ds-rank-table th.ds-rank-col-chev,
.ds-rank-table td.ds-rank-col-chev { width: 36px; padding-left: 0; padding-right: 0; text-align: center; }
/* 908px = остальные колонки 762px + минимум «Динамики» 144px + две линии рамки .ds-table-wrap. */
.ds-rank-table th.ds-rank-col-activity { width: clamp(300px, calc(100cqw - 908px), 340px); }
.ds-rank-table th.ds-rank-col-deviation { width: 168px; }
.ds-rank-table th.ds-rank-col-pharmacies { width: 132px; }
.ds-rank-table th.ds-rank-col-roles { width: 220px; }
.ds-rank-table th.ds-rank-col-checks { width: 96px; }
.ds-rank-table th.ds-rank-col-period { width: 110px; }
.ds-rank-table th.ds-rank-col-dynamics { min-width: 144px; }

.ds-rank-table td.ds-rank-col-activity,
.ds-rank-table td.ds-rank-col-deviation,
.ds-rank-table td.ds-rank-col-pharmacies,
.ds-rank-table td.ds-rank-col-roles,
.ds-rank-table td.ds-rank-col-checks,
.ds-rank-table td.ds-rank-col-period,
.ds-rank-table td.ds-rank-col-dynamics { vertical-align: middle; }
.ds-rank-table td.ds-rank-col-checks,
.ds-rank-table td.ds-rank-col-period { font-size: var(--fs-label); color: var(--text-muted); }
.ds-rank-table td.ds-rank-col-checks { font-variant-numeric: tabular-nums; }
.ds-rank-activity-name {
  display: flex;
  min-width: 0;
  align-items: baseline;
  font-size: var(--fs-body);
  font-weight: 500;
  color: var(--text);
}
.ds-rank-activity-label {
  flex: 0 1 auto;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.ds-rank-activity-name .ds-activity-kind-suffix { flex: none; }
.ds-rank-below-count { font-size: var(--fs-body); font-weight: 500; color: var(--text); }
.ds-rank-table .ds-row.ds-row-active > td { border-bottom-color: transparent; }

.ds-rank-group > td {
  padding: var(--space-3) var(--space-3) var(--space-1) var(--space-12);
  font-size: var(--fs-th);
  font-weight: 500;
  color: var(--text-muted);
  border-top: 1px solid var(--border);
  border-bottom: 1px solid var(--border);
}
.ds-rank-group > td > span { color: var(--text); font-weight: 500; }
.ds-rank-group--first > td { border-top: none; padding-top: var(--space-2); }

.ds-rank-role-list { display: flex; flex-direction: column; gap: var(--space-1); }
.ds-rank-role { display: flex; align-items: center; gap: var(--space-2); font-size: var(--fs-label); }
.ds-rank-role-dev { white-space: nowrap; }
.ds-rank-role-share { font-size: var(--fs-mono); color: var(--text-muted); white-space: nowrap; font-variant-numeric: tabular-nums; }
.ds-rank-role--excl { color: var(--text-subtle); }
.ds-rank-role--excl .ds-rank-role-share { color: var(--text-subtle); }

.ds-rank-tray > td {
  background: var(--surface-2);
  padding: var(--space-3) var(--space-3) var(--space-4);
  vertical-align: top;
  border-bottom: 1px solid var(--border);
}
.ds-rank-tray > td.ds-rank-tray-edge {
  padding: 0;
  box-shadow: inset 2px 0 0 var(--active-row-edge);
}
.ds-rank-slot-label {
  margin-bottom: var(--space-1);
  font-size: var(--fs-th);
  font-weight: 500;
  color: var(--text-muted);
}
.ds-rank-pharmacy-lists { display: grid; grid-template-columns: 1fr 1fr; gap: var(--space-6); }
.ds-rank-all-pharmacies { display: inline-block; font-size: var(--fs-label); color: var(--text-muted); }

.ds-activity-roles { width: 100%; border-collapse: collapse; font-variant-numeric: tabular-nums; }
.ds-activity-roles th { font-size: var(--fs-microcopy); font-weight: 500; color: var(--text-subtle); text-align: right; padding: 0 0 var(--space-1) var(--space-3); border-bottom: 1px solid var(--border); white-space: nowrap; }
.ds-activity-roles td { font-size: var(--fs-label); text-align: right; padding: var(--space-1) 0 var(--space-1) var(--space-3); border-bottom: 1px solid var(--border); white-space: nowrap; }
.ds-activity-roles th:first-child,
.ds-activity-roles td:first-child { text-align: left; padding-left: 0; }
.ds-activity-roles td.ds-activity-role-muted { color: var(--text-muted); }
.ds-activity-roles tr.ds-activity-role--excl td { color: var(--text-subtle); }
.ds-rank-tray .ds-activity-roles th {
  position: static;
  top: auto;
  z-index: auto;
  background: transparent;
  box-shadow: none;
}

.ds-period-dynamics-dates { margin-bottom: var(--space-1); font-size: var(--fs-microcopy); color: var(--text-subtle); font-variant-numeric: tabular-nums; }
.ds-period-dynamics-line { display: flex; align-items: center; justify-content: flex-start; gap: var(--space-1); height: 28px; border-bottom: 1px solid var(--border); font-size: var(--fs-label); color: var(--text-muted); }
.ds-period-dynamics-line .ds-period-dynamics-val { font-size: var(--fs-body); font-weight: 500; color: var(--text); }

/* ── Динамика периода — §11.49 «T4b/PUM-1015» ─────────────────────────── */
/* Тон ТОЛЬКО на .ds-period-dynamics-arrow (.is-up/.is-down/.is-flat);
 * числа-val монохром. НЕ вертикаль ↑↓ (занята severity §2b). */

.ds-period-dynamics-val { font-family: var(--font-mono); font-variant-numeric: tabular-nums; font-size: var(--fs-severity); font-weight: 500; color: var(--text); }
.ds-period-dynamics-arrow { font-weight: var(--fw-severity-arrow); line-height: 1; }   /* ↗ ↘ → Unicode-диагональ */
.ds-period-dynamics-arrow.is-up   { color: var(--pos-700); }
.ds-period-dynamics-arrow.is-down { color: var(--neg-700); }
.ds-period-dynamics-arrow.is-flat { color: var(--text-subtle); }

.ds-ph-title,
.ds-ph-row { display: grid; grid-template-columns: minmax(0, 1fr) 58px 44px; align-items: baseline; gap: var(--space-2); }
.ds-ph-title { padding-bottom: var(--space-1); border-bottom: 1px solid var(--border); font-size: var(--fs-microcopy); font-weight: 500; color: var(--text-subtle); }
.ds-ph-title > span:not(:first-child) { text-align: right; }
.ds-ph-row { padding: var(--space-1) 0; min-width: 0; }
/* Коробка имени обнимает текст — зона нажатия не тянется до края колонки (§11.49 «Ссылки»). */
.ds-ph-name { justify-self: start; max-width: 100%; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-size: var(--fs-label); color: var(--text); }
.ds-ph-name--orphan { color: var(--text-muted); }
.ds-ph-rate { text-align: right; font-size: var(--fs-label); font-weight: 500; color: var(--text); font-variant-numeric: tabular-nums; }
.ds-ph-checks { text-align: right; font-size: var(--fs-mono); color: var(--text-muted); font-variant-numeric: tabular-nums; }
/* ── Records Wave 2 atoms (§11.x, PUM-539) ─────────────────────────────────── */
/* 27 новых атомов: filter chrome / form controls / button / table cols / table cells /
   badges / row meta / audio cell / panel / shared null wrapper / a11y utility.
   Все значения — через var(--token) из tokens.css. */

/* ── Filter chrome — mirrors .exp-filters, .exp-filter-row, .exp-filter-group,
   .exp-filter-label, .exp-filter-actions ─── */

.ds-filters {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  padding: var(--space-4) var(--space-6);
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
  margin-bottom: var(--space-4);
}

.ds-filter-row {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-4);
  align-items: flex-end;
}

.ds-filter-group {
  display: flex;
  flex-direction: column;
  gap: var(--space-1);
}

.ds-filter-label {
  font-size: var(--fs-th);      /* 12px */
  font-weight: var(--fw-th);    /* 500 */
  color: var(--text-muted);
  /* Editorial: lowercase, без uppercase (§5.3) */
}

.ds-filter-actions {
  /* Отдельный wrapper для submit-кнопки — симметрично .ds-filter-group.
     Flexbox alignment по нижнему краю строки фильтров. */
  display: flex;
  flex-direction: column;
  gap: var(--space-1);
}

/* ── Form controls — mirrors .exp-input, .exp-select ─── */

.ds-input,
.ds-select {
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  background: var(--surface);
  font-family: var(--font-sans);
  font-size: var(--fs-body);    /* 14px */
  color: var(--text);
  padding: var(--space-2) var(--space-3);
  transition: border-color var(--dur-fast) var(--ease-standard),
              box-shadow var(--dur-fast) var(--ease-standard);
}

.ds-input:focus,
.ds-select:focus {
  outline: none;
}
.ds-input:focus-visible,
.ds-select:focus-visible {
  outline: none;
  /* Graphite focus-halo (§8.1) вместо burgundy/blue — precedent .ds-btn-retry (PUM-495). */
  border-color: var(--text-muted);
  box-shadow: var(--focus-ring-graphite);
}

/* Disabled — §8-доктрина, идентично .ds-textarea:disabled / .ds-checkbox:disabled.
   «Значение видно, ввод закрыт» (не RBAC-скрытие). Первый consumer — sealed-период §11.41. */
.ds-input:disabled,
.ds-select:disabled {
  background: var(--disabled-bg);
  color: var(--disabled-text);
  cursor: not-allowed;
}

/* ── Select optgroup editorial styling (§11.21 extension, PUM-678 gap #3 part A) ─── */
/* Native <select> в editorial-форме поддерживает <optgroup> для двойной группировки
   status × kind при выборе Активности (PUM-606/607/608 consumers). Concat-label pattern
   «status · kind (N)» через middle-dot (consistent с suffix-композицией §11.9 / §11.34).
   Override browser-default italic; caveat: macOS Safari применяет CSS к <optgroup>
   ограниченно — mid-tier editorial consistency. Custom dropdown — techdebt-триггер
   «>20 Активностей в scope», на пилоте ~10-15 native достаточен. */

.ds-select optgroup {
  font-family: var(--font-sans);
  font-size: var(--fs-microcopy);    /* 11px */
  font-weight: 500;
  font-style: normal;                 /* override browser default italic */
  color: var(--text-muted);
  text-transform: uppercase;
  letter-spacing: 0.04em;
  padding: var(--space-1) 0;          /* 4px 0 */
}

.ds-select option {
  font-weight: 400;
  color: var(--text);
}

/* ── Submit button — mirrors .exp-submit-btn, .exp-submit-loading, .exp-submit-changed ─── */

.ds-submit-btn {
  /* Secondary action outline (§10 ext, B1 CEO-approved 2026-04-24 PUM-541): «Сформировать».
     Outline default + fill-on-hover. Primary-CTA filled — резервируется для действий высшего уровня. */
  background: var(--surface);
  color: var(--primary);
  border: 1px solid var(--primary);
  border-radius: var(--radius-sm);
  font-family: var(--font-sans);
  font-size: var(--fs-label);
  font-weight: var(--fw-th);
  padding: var(--space-2) var(--space-5);
  cursor: pointer;
  position: relative;
  transition: background var(--dur-fast) var(--ease-standard),
              color var(--dur-fast) var(--ease-standard),
              border-color var(--dur-fast) var(--ease-standard),
              box-shadow var(--dur-fast) var(--ease-standard);
}

.ds-submit-btn:hover {
  background: var(--primary);
  color: var(--surface);
}

.ds-submit-btn:focus-visible {
  outline: none;
  box-shadow: var(--focus-ring);
}

.ds-submit-btn-loading {
  /* Кнопка в состоянии загрузки — muted, pointer-events off. */
  opacity: 0.65;
  cursor: not-allowed;
  pointer-events: none;
}

.ds-submit-btn-changed {
  /* A1 editorial-attenuation: static warn-outline без motion.
     Превалирует над :hover в default — при changed+hover срабатывает filled-warn rule ниже.
     aria-live="polite" на .sr-only#exp-submit-status обеспечивает AT-signal при transition. */
  border-color: var(--warn-500);
  color: var(--warn-700);
  box-shadow: 0 0 0 2px var(--warn-500);
}
.ds-submit-btn-changed:hover {
  background: var(--warn-500);
  color: var(--surface);
  border-color: var(--warn-500);
}

.ds-submit-btn:disabled {
  color: var(--disabled-text);
  border-color: var(--border);
  background: var(--surface);
  cursor: not-allowed;
}

/* ── Table col-atoms — mirrors .exp-col-icon, .exp-col-products ─── */

.ds-col-icon {
  /* Колонка иконки аудио — фиксированная ширина, центрирование. */
  width: 36px;
  text-align: center;
}

/* Компактные отступы колонки аудио: специфичность .ds-table th/td
   перебивает общие отступы таблицы и центрирует шапку вместе с ячейками. */
.ds-table th.ds-col-icon,
.ds-table td.ds-col-icon {
  text-align: center;
  padding-left: var(--space-1);
  padding-right: var(--space-1);
}

.ds-table th.ds-col-activity {
  /* Колонка «Активность» (§11.39, PUM-1261) — якорь ширины, заданной
     инлайн на <th> (та же конвенция, что у остальных колонок этой таблицы),
     и якорь тултипа подзадачи 3 (симметрия с .ds-col-products у соседней
     колонки). Override переноса заголовка — ТОЛЬКО здесь, scoped-селектором
     на этот класс, не правкой базового .ds-table th: тот несёт nowrap
     (§B PUM-503) и переиспользуется остальными колонками таблицы. В режиме
     фильтра по Активности заголовок несёт имя выбранной темы и переносится
     на вторую строку шапки, а не усекается (правило 16 CONTRACT PUM-1261) —
     не нужен на .ds-col-products: там усечение внутри ячейки, не заголовка. */
  white-space: normal;
}

.ds-th-caption {
  /* Подпись условий выдачи в заголовке «Активность» (§11.39 «Раскладка»,
     PUM-1549) — проекционные куски выбранного диапазона строкой под именем
     выбранной Активности. Тон и кегль — тот же ярус, что метка «по другим»
     (.ds-role-other, ниже): --fs-microcopy + --text-muted. Без
     padding-left: у .ds-role-other он держит маркер-слот сетки ролей,
     которого в заголовке нет — этот отступ принадлежит сетке, а не ярусу.
     Вертикальная отбивка яруса, наоборот, взята (margin-top ниже).
     white-space не задан намеренно: .ds-col-activity (выше)
     несёт scoped-override white-space:normal, дочерний элемент его
     наследует — перенос подписи работает сам, вторая запись того же
     правила здесь была бы дупликацией. */
  font-size: var(--fs-microcopy);
  color: var(--text-muted);
  /* Отбивка от имени — та же 2px, что у .ds-role-other. Без неё подпись
     встаёт вплотную к имени и читается его продолжением, а не отдельной
     строкой (калибровка Tech Lead — §11.39 оставляет пиксельные значения
     яруса открытыми к калибровке на §13a). */
  margin-top: 2px;
}

.ds-col-products {
  /* Колонка товаров — overflow ellipsis на content-span. */
  max-width: 0;   /* hack: позволяет overflow: hidden работать на table-cell */
  overflow: hidden;
}

/* ── Ячейка «Товары» — две строки (PUM-1258, §11.39) ────────────
   Заменяет однострочную .ds-products-text/.ds-products-badge (сняты — §12.3
   design-system.md «Снято — не воспроизводить»): renderGoodsCell
   (checks_table_pure.js) рендерит .ds-goods > 2× .ds-goods-row. */

.ds-goods {
  display: flex;
  flex-direction: column;
  gap: 2px;                       /* design-spec exception §6.2 — точное значение по брифу
                                     records-row-cells §6; шкала начинается с 4px, а на ней
                                     двухстрочная ячейка перестаёт держать высоту строки */
}

.ds-goods-row {
  display: flex;
  align-items: baseline;
  gap: var(--space-2);
  min-width: 0;
}

.ds-goods-arrow {
  /* Префикс «→» второй строки — рисуется только когда на ней есть позиция
     (правило 5 CONTRACT PUM-1258): «→ —» повторяло бы «ничего» дважды. */
  flex: 0 0 auto;
  color: var(--text-subtle);
  font-size: var(--fs-th);        /* 12px — тот же токен, что у счётчика .ds-goods-more */
}

.ds-goods-name {
  /* --fs-label (не унаследованный --fs-body таблицы, 14px) — design-system.md
     §11.39 «Наименование — --fs-label --text». */
  font-size: var(--fs-label);
  color: var(--text);
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;   /* усечение, не перенос — «НЕ делать» §11.39 */
}

.ds-goods-name--same {
  /* «та же» — представители обеих строк совпали по матч-ключу (правило 6) */
  color: var(--text-muted);
}

.ds-goods-more {
  /* Счётчик «+N» простым текстом без чипа (§11.39 — в двухстрочной ячейке
     чип становится третьим видом хрома) */
  flex: 0 0 auto;
  font-size: var(--fs-th);
  color: var(--text-subtle);
  font-variant-numeric: tabular-nums;
}

/* ── Ячейка «Заметка» (§11.57.B, PUM-1413) — вкладка «Банк лучших практик»
   на новом контуре. Одна строка, усечение многоточием; механика — та же,
   что у .ds-goods-name выше, но без flex-контекста (min-width:0 там нужен
   flex-предку; TD колонки задаёт ширину table-layout:fixed сама, block-
   уровневый элемент берёт её целиком). Единственный атом, который этот
   раздел вводит (§11.57.B «остальное reuse») — второго не заводится. */
/* ── §7.7 — Текстовая ячейка в одну строку (`.ds-cell-oneline`) ──────────
   Нейтральна к колонке: имя называет форму, а не предмет. Модификатор тона
   означает «на месте значения стоит заместитель», поэтому он часть ячейки,
   а не свободная утилита. Ширину задаёт колонка — атому усекать нечего,
   пока ширина не ограничена. Перенос запрещён: константная высота строки
   держит горизонтальные полосы соседних колонок (§11.39 «НЕ делать»).
   .ds-note-cell и .ds-goods-name ниже объявляют ту же форму и мигрируют
   на этот носитель отдельной задачей (К-1500-2). */
.ds-cell-oneline {
  font-size: var(--fs-label);
  color: var(--text);
  min-width: 0;                     /* без него усечение не срабатывает у flex/grid-потомка */
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;              /* усечение, не перенос: константная высота строки */
}
.ds-note-cell {
  display: block;
  font-size: var(--fs-label);
  color: var(--text);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;   /* усечение, не перенос — тот же довод, что .ds-goods-name */
}

/* ── Зона заметки панели, read-состояния (§11.57.C, PUM-1413 заход 2) ────
   Два атома зоны заметки; edit/saving reuse существующих
   .ds-form-field/.ds-textarea/.ds-btn-primary (§11.28/§11.21/§11.23,
   player.js::buildNoteEditFormHtml — единственный носитель этой формы).
   Текст и подпись внутри .ds-panel-note-read reuse .ds-closure-note-text/
   .ds-closure-note-meta (§11.27) — второй типографики здесь не заводится;
   триггер «✎ изменить» — .ds-btn-link (§11.25). Без глифа: pin — символ
   «Отложенных» (§11.48), заводить третий — §1.5. Имена атомов нейтральны к
   вкладке (не несут «отложено»/«банк») — общий дом зоны заметки
   панелей-на-снимке (panel_note_zone.js) параметризует форму под
   потребителя, не форк по имени класса. */
.ds-panel-note-read {
  /* §11.57.C: текст заметки — своей строкой, ПОД ним подпись автора, а
     триггер правки — РЯДОМ с подписью, не третьей строкой. Раскладка
     делается переносом строки у текста, а не обёрткой вокруг пары
     «подпись + триггер»: обёртка была бы новым атомом ради одной зоны
     (§1.5), а перенос обходится правилом внутри уже объявленного. */
  display: flex;
  flex-flow: row wrap;
  align-items: baseline;
  gap: var(--space-2);
}

.ds-panel-note-read > .ds-closure-note-text {
  flex: 0 0 100%;
}

/* Селектор — компаунд `.ds-btn-link.ds-panel-note-add`, НЕ голый
   `.ds-panel-note-add` (QA PUM-1413 W1, фикс-заход после QA): разметка
   (bank_records/bank_panel.js) несёт ОБА класса на одном элементе
   (`class="ds-btn-link ds-panel-note-add"`), а одноклассовый `.ds-btn-link`
   объявлен в файле НИЖЕ — при равной специфичности (0,1,0) побеждал более
   поздний по каскаду, и цвет/паддинг этого атома были мёртвыми строками
   (color/padding перекрывались .ds-btn-link, hover/focus-visible —
   .ds-btn-link:hover/:focus-visible). Компаунд-селектор поднимает
   специфичность до (0,2,0), не завися от порядка объявления в файле, и не
   заводит второго определения тех же свойств — .ds-btn-link остаётся
   нетронутым для своих 10+ прочих потребителей (focus.js/bank_record_dialog.js
   и др.), которые несут только его один класс. Разметку этого атома
   производит одна функция — panel_note_zone.js:_panelNoteReadInnerHtml
   (общий дом зоны заметки панелей-на-снимке, PUM-1416 окно 2) — она всегда
   ставит оба класса на одном элементе; компаунд-селектор остаётся верным
   для любого потребителя дома независимо от их числа, второго определения
   разметки не заводится. */
.ds-btn-link.ds-panel-note-add {
  /* Тихое приглашение «+ добавить заметку» в зоне .ds-panel-note (§11.48-E) —
     собственный минимальный ruleset. Прежде роль сравнивалась с
     .ds-defer-note-add («Отложенные», под-строка таблицы) — та форма снята
     целиком PUM-1416 окно 1 (§11.48-D «Снимается вместе с прежней формой»),
     сравнение устарело. */
  display: inline-flex;
  align-items: center;
  font-size: var(--fs-label);
  color: var(--text-subtle);
  background: none;
  border: none;
  padding: var(--space-1) 0;
  cursor: pointer;
}

.ds-btn-link.ds-panel-note-add:hover {
  color: var(--text-muted);
}

.ds-btn-link.ds-panel-note-add:focus-visible {
  outline: none;
  box-shadow: var(--focus-ring-graphite);
}

.ds-date {
  /* Дата (день.месяц) — microcopy 11px muted над временем. */
  display: block;
  font-size: var(--fs-microcopy);   /* 11px */
  font-weight: var(--fw-microcopy); /* 500 */
  color: var(--text-muted);
  line-height: 1.3;
}

.ds-time {
  /* Время — 13px tabular-nums основной строкой. */
  display: block;
  font-size: var(--fs-label);   /* 13px */
  font-variant-numeric: tabular-nums;
  line-height: 1.3;
}

/* ── Badges — mirrors .exp-badge-yes, .exp-badge-no ─── */

.ds-badge-pos {
  /* «Да» допродажа — sage pill (§11.4 asymmetric event marker).
     Property-merged from retired .ds-badge base (PUM-541). */
  display: inline-block;
  font-size: var(--fs-th);
  font-weight: var(--fw-th);
  border-radius: var(--radius-sm);
  padding: 2px var(--space-2);
  white-space: nowrap;
  background: var(--pos-100);
  color: var(--pos-700);
}

/* ── New atoms — PUM-541 Records polish-2 ─── */

.ds-bookmark-badge {
  /* In-bank marker — §11.x filled 16px, pos-700 (PUM-541). */
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 16px;
  height: 16px;
  color: var(--pos-700);
  flex-shrink: 0;
}
.ds-bookmark-badge svg {
  width: 16px;
  height: 16px;
}

.ds-date-cell {
  /* Date-cell layout pattern — inline-marker support (§11.x, PUM-541).
     Применяется в Records + Block 3 table cells. Bank tab остаётся legacy. */
  display: flex;
  flex-direction: column;
  gap: 2px;
}
.ds-date-row {
  display: flex;
  align-items: center;
  gap: 5px;
}

.ds-text-muted {
  /* Plain muted baseline text — §11.4 «Нет» допродажи silent baseline (PUM-541). */
  color: var(--text-muted);
  font-size: var(--fs-label);
}

/* ── Audio cell atoms — mirrors .exp-audio-expired, .exp-icon-btn ─── */

.ds-audio-expired {
  /* Иконка истёкшего аудио — muted, не кликабельна. */
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 24px;
  height: 24px;
  color: var(--text-subtle);
}

.ds-icon-btn {
  /* Кликабельная иконка (play-кнопка аудио) — editorial icon-button.
     Без фона в дефолте, subtle fill на hover.
     font-size 24px — SVG иконки width:1em;height:1em растягиваются до 24×24 (PUM-541 SA3). */
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 24px;
  height: 24px;
  font-size: 24px;
  line-height: 1;
  background: none;
  border: none;
  border-radius: var(--radius-sm);
  cursor: pointer;
  color: var(--text-muted);
  transition: color var(--dur-fast) var(--ease-standard),
              background var(--dur-fast) var(--ease-standard);
  padding: 0;
}

.ds-icon-btn:hover {
  color: var(--text);
  background: var(--surface-2);
}

.ds-icon-btn:focus-visible {
  outline: none;
  box-shadow: var(--focus-ring);
}

/* ── Panel frame (§11.x, PUM-539) — mirrors .exp-panel, .exp-panel-header ─── */
/* C2 scope: editorial frame. Controls/waveform/transcript — scope-guarded legacy. */

.ds-panel {
  /* Editorial inline-panel frame — карточка с hairline-border, без shadow. */
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  overflow: clip;
}

.ds-panel-header {
  /* Editorial row-action header внутри .ds-panel: context-slot слева + action-slot справа.
     Scoped: применяется только внутри .ds-panel (§11.x CEO-approved, PUM-539 Blocker C). */
  display: flex;
  flex-direction: row;
  justify-content: space-between;
  align-items: center;
  padding: var(--space-3) var(--space-4) 0;
  /* padding-bottom: 0 — дальнейшие ярусы отделяются ритмом каркаса панели */
}

.ds-panel-header-actions {
  display: flex;
  align-items: center;
  gap: var(--space-2);
}



/* §11.82 A–C — нижний ярус и чтение оценок. */
.ds-panel-lower { border-top: 1px solid var(--border-muted); }   /* колонки нет — одна ячейка, как сегодня */
.ds-panel-lower:has(> .ds-assess-col) { display: grid; grid-template-columns: minmax(0, 800px) minmax(400px, 1fr); }
.ds-panel-lower > .ds-panel-transcript { border-top: none; }   /* линию над ярусом несёт ярус */
.ds-assess-col  { grid-column: 2; min-width: 0; border-left: 1px solid var(--border-muted);
                  padding: var(--space-3) var(--space-5) var(--space-5); }
.ds-assess-col > * { max-width: 640px; }


.ds-assess-head { display: flex; justify-content: space-between; align-items: center;
                  gap: var(--space-3); min-height: 30px; margin-bottom: var(--space-2); }
.ds-assess-set  { color: var(--text-subtle); font-weight: 400; }


.ds-assess-table-wrap  { overflow-x: auto; }
.ds-assess-table       { border-collapse: collapse; font-size: var(--fs-label); }
.ds-assess-table th    { font-weight: 400; text-align: left; vertical-align: bottom;
                         padding: 0 var(--space-4) var(--space-2) 0; min-width: 84px;
                         font-size: inherit; color: inherit; white-space: normal; border-bottom: none; }
.ds-assess-table thead th { position: static; z-index: auto; background: none; box-shadow: none; }
.ds-assess-author      { display: block; font-weight: var(--fw-th); color: var(--text); white-space: nowrap; line-height: 1.3; }
.ds-assess-author-meta { display: block; font-size: var(--fs-microcopy); color: var(--text-muted);
                         line-height: 1.35; font-variant-numeric: tabular-nums; }
.ds-assess-table td    { padding: var(--space-2) var(--space-4) var(--space-2) 0;
                         border-top: 1px solid var(--border-muted); border-bottom: none; vertical-align: baseline; }
.ds-assess-table .ds-assess-q { white-space: nowrap; padding-right: var(--space-5);
                         position: sticky; left: 0; top: auto; z-index: 1; background: var(--surface); }   /* сильнее «thead th»: угол шапки стоит со столбцом */
.ds-assess-diverged    { display: inline-block; width: 16px; font-size: var(--fs-body);
                         font-weight: var(--fw-th); color: var(--warn-700); }
.ds-assess-ans         { font-size: var(--fs-body); font-weight: var(--fw-th); font-variant-numeric: tabular-nums; }
.ds-assess-ans--alt    { font-size: var(--fs-label); font-weight: 400; color: var(--text-muted); line-height: 1.3; }
.ds-assess-sub         { font-size: var(--fs-microcopy); color: var(--text-subtle); margin: var(--space-3) 0 var(--space-2); }

/* §11.82-D — форма своей оценки */
.ds-assess-item       { display: grid; grid-template-columns: minmax(0, 1fr) auto; column-gap: var(--space-3);
                        width: 100%; text-align: left; background: none; border: none;
                        border-top: 1px solid var(--border-muted);
                        padding: var(--space-2) var(--space-2) var(--space-2) 0; cursor: pointer; }
.ds-assess-item:hover { background: var(--surface-2); }
.ds-assess-item:focus-visible { outline: none; box-shadow: var(--focus-ring-graphite); }
.ds-assess-item:disabled,
.ds-assess-item:disabled > * { color: var(--disabled-text); cursor: not-allowed; }   /* погашенный вид §8 на время сохранения */
.ds-assess-item:disabled:hover { background: none; }
.ds-assess-item-name  { font-size: var(--fs-label); color: var(--text); }
.ds-assess-item-ans   { font-size: var(--fs-body); font-weight: var(--fw-th); font-variant-numeric: tabular-nums; text-align: right; }
.ds-assess-item-ans--alt  { font-size: var(--fs-label); font-weight: 400; color: var(--text-muted); }   /* особый ответ — тише уровня */
.ds-assess-item-ans--none { color: var(--text-subtle); }                                                 /* нет ответа — «—» */
.ds-assess-item-desc  { grid-column: 1 / -1; font-size: var(--fs-th); color: var(--text-muted);
                        white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.ds-assess-open       { border-top: 1px solid var(--border-muted); padding: var(--space-2) 0 var(--space-3); }
.ds-assess-open-name  { font-size: var(--fs-body); font-weight: var(--fw-th); }
.ds-assess-open-expl  { font-size: var(--fs-th); color: var(--text-muted); line-height: 1.45; margin: 2px 0 var(--space-2); }
.ds-assess-opts       { border: 1px solid var(--border); border-radius: var(--radius-md); overflow: hidden; }
.ds-assess-opt        { display: grid; grid-template-columns: 20px minmax(0, 1fr); column-gap: var(--space-2);
                        width: 100%; text-align: left; background: var(--surface); border: none;
                        border-top: 1px solid var(--border-muted); padding: var(--space-2) var(--space-3);
                        font-size: var(--fs-label); line-height: 1.45; color: var(--text); cursor: pointer; }
.ds-assess-opt:first-child    { border-top: none; }
.ds-assess-opt:hover          { background: var(--surface-2); }
.ds-assess-opt:focus-visible  { outline: none; box-shadow: inset var(--focus-ring-graphite); }   /* рамка списка срезает внешний ореол */
.ds-assess-opt--sel           { background: var(--surface-2); box-shadow: inset 2px 0 0 var(--graphite-700); }
.ds-assess-opt--sel:focus-visible { box-shadow: inset 2px 0 0 var(--graphite-700), inset var(--focus-ring-graphite); }
.ds-assess-opt-label  { font-size: var(--fs-body); font-weight: var(--fw-th); font-variant-numeric: tabular-nums; }
.ds-assess-alt        { display: flex; gap: var(--space-2); margin-top: var(--space-2); }
.ds-assess-chip       { height: 26px; padding: 0 var(--space-2); border: 1px solid var(--border); border-radius: var(--radius-sm);
                        background: var(--surface); font-size: var(--fs-label); color: var(--text-muted);
                        cursor: pointer; white-space: nowrap; }
.ds-assess-chip:hover         { background: var(--surface-2); color: var(--text); }
.ds-assess-chip:focus-visible { outline: none; box-shadow: var(--focus-ring-graphite); }
.ds-assess-chip--sel  { background: var(--surface-2); border-color: var(--graphite-500); color: var(--text); font-weight: var(--fw-th); }
.ds-assess-foot       { border-top: 1px solid var(--border-muted); padding-top: var(--space-2); }
.ds-assess-acts       { display: flex; align-items: center; gap: var(--space-3); margin-top: var(--space-3); }
.ds-assess-status     { font-size: var(--fs-th); color: var(--text-muted); }


.ds-panel--split         { display: grid; grid-template-columns: minmax(0, 1fr) clamp(360px, 26%, 440px); }
.ds-panel-conversation   { min-width: 0; }
.ds-panel-record         { min-width: 0; border-left: 1px solid var(--border-muted);
                           padding: var(--space-3) var(--space-4) var(--space-4);
                           display: flex; flex-direction: column; gap: var(--space-3); }
.ds-panel-record-head    { display: flex; justify-content: space-between; align-items: center; height: 24px; }
.ds-panel-record-actions { display: flex; align-items: center; gap: var(--space-2); }
.ds-panel-record > .ds-panel-note { padding: 0; border-top: 0; }

/* §11.70: поле колонки записи растёт по тексту; без поддержки остаётся ручка атома. */
@supports (field-sizing: content) {
  .ds-panel-record .ds-textarea { field-sizing: content; resize: none; }
}

/* §11.57-C — состояние фрагмента и вход в окно подрезки. */
.ds-panel-fragment { display: flex; align-items: baseline; gap: var(--space-2); }
.ds-panel-fragment > span { font-size: var(--fs-label); color: var(--text); }

.ds-panel-conversation > .ds-panel-audio-expired,
.ds-panel-conversation > .ds-state-error {
  border-top: 1px solid var(--border-muted);
}

.ds-panel-record [tabindex="-1"]:focus-visible {
  outline: none;
  box-shadow: var(--focus-ring);
}

/* ── Shared null wrapper — mirrors .exp-icon-none (§12.3 PUM-539, historical) ─── */
/* Перенесён в ds-utilities как атом. .exp-icon-none тогда сохранялся ради Bank
   scope-guard (bank.js — старая самодельная вкладка, не мигрировавшая в Wave 2);
   bank.js снесён целиком PUM-1413 (заход 3), .exp-icon-none retired вместе с ним
   (test_css_exp_icon_none_retired) — .ds-icon-none ниже единственный носитель. */

.ds-icon-none {
  /* Em-dash inline-wrapper для null/пустых значений (sparkline §11.3, audio cell null). */
  display: inline-block;
  color: var(--text-subtle);
  font-variant-numeric: tabular-nums;
}

/* ── Tooltip (§11.6) — PUM-545 ─────────────────────────────────────────── */
.ds-tooltip {
  position: fixed;
  z-index: var(--z-tooltip);
  background: var(--graphite-700);
  color: var(--bg);
  padding: var(--space-2) var(--space-3);
  border-radius: var(--radius-sm);
  box-shadow: var(--shadow-overlay);
  font-family: var(--font-sans);
  font-size: var(--fs-microcopy);
  font-weight: var(--fw-body);
  line-height: 1.5;
  max-width: 280px;
  white-space: nowrap;
  pointer-events: none;
  opacity: 0;
  transition: opacity var(--dur-fast) var(--ease-standard);
}
.ds-tooltip.ds-tooltip-visible {
  opacity: 1;
  transition: opacity var(--dur-base) var(--ease-standard);
}
.ds-tooltip-multiline { white-space: normal; }

/* ── Тултип «Товары» (PUM-1258, §11.6/§11.39) ───────────────────
   .ds-tooltip--wide — scope объявлен: тултипы «Активность»/«Товары» строки
   «Записей» (номенклатура клиента длиннее общего 280px max-width). .ds-tt-*
   — контент-паттерн «раздел с тихим заголовком», единственная группа —
   «прочее» (правило 18 CONTRACT PUM-1258). */

.ds-tooltip--wide {
  max-width: 380px;
}

.ds-tt-section {
  /* Тихий заголовок раздела (§11.6 «раздел с тихим заголовком»). Отбивка сверху —
     калибровка pre-flight §13a: на общем row-gap заголовок примыкал к группе выше
     и читался как её продолжение, а не как граница раздела. */
  font-size: 10px;
  color: var(--graphite-300);
  grid-column: 1 / -1;
  margin-top: var(--space-1);
}

.ds-tt-goods {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto auto;
  column-gap: var(--space-2);
  row-gap: 3px;                   /* design-spec exception §6.2 — точное значение по брифу
                                     records-row-cells §6 (то же, что у матрицы §11.39) */
  align-items: baseline;
}

.ds-tt-goods-name {
  /* Намеренно БЕЗ text-overflow/nowrap — усечение обязательно в ячейке
     (правило 7), запрещено по смыслу в тултипе: тултип существует, чтобы
     показать полное (правило 23). Не «доводить до единообразия» с ячейкой. */
  min-width: 0;
}

.ds-tt-goods-label {
  font-size: var(--fs-microcopy);
  color: var(--graphite-300);
  white-space: nowrap;
}

.ds-tt-goods-sum {
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
  text-align: right;
}

.ds-tt-dev {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  column-gap: var(--space-2);
  row-gap: 3px;                   /* design-spec exception §6.2 — значение задано §11.53 (как у .ds-tt-goods) */
  align-items: baseline;
}

.ds-tt-dev-name {
  min-width: 0;
}

.ds-tt-dev-value {
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
  text-align: right;
}

.ds-tt-quiet {
  /* Раздел «прочее» — приглушены и наименование, и сумма (правило 18) */
  color: var(--graphite-300);
}

@media (prefers-reduced-motion: reduce) {
  .ds-tooltip,
  .ds-tooltip.ds-tooltip-visible { transition: none; }
}

/* ── A11y utility — visually-hidden (§8 Accessibility, PUM-539) ─── */
/* Стандартный visually-hidden pattern: элемент невидим визуально, но доступен screen-reader.
   Используется для aria-live="polite" announce-span внутри .ds-submit-btn (#exp-submit-status).
   Override 2: compensating mechanism для A1 (статический warn-outline без pulse). */

.sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

/* ── Panel-chrome controls (Wave 2.5 — Bank + player.js, §11.16-19, PUM-544) ─── */
/* Mirror legacy .exp-panel-controls / .exp-panel-time / .exp-panel-speed /
   .exp-panel-waveform / .exp-panel-transcript / .exp-panel-audio-expired.
   Calibrated from existing .exp-panel-* properties (static/explorer.css lines 548-686). */

/* §11.16 — .ds-panel-controls editorial controls-row family */

.ds-panel-controls {
  /* Editorial controls-row внутри .ds-panel — 3-column grid.
     Левая колонка — слот симметрии (§11.16), центральная — media-group (Play+Time+Speed),
     правая — polymorphic action-slot.
     Симметрия 1fr-auto-1fr фиксирует media-group в pixel-center независимо от ширины
     action-slot (1 button / 2 buttons / single-pharmacy plain text). */
  display: grid;
  grid-template-columns: 1fr auto 1fr;
  align-items: center;
  padding: 0 var(--space-4) var(--space-3);
}

.ds-panel-media {
  /* Центральная media-group: Play + Time + Speed inline-flex.
     Calibrated from .exp-panel-controls gap: 12px → var(--space-3). */
  display: inline-flex;
  align-items: center;
  gap: var(--space-3);
}

.ds-panel-action-slot-left {
  /* Левый слот — симметрия grid; чем занят и когда несёт aria-hidden — §11.16.
     НЕ flex-container. */
  grid-column: 1;
}

.ds-panel-action-slot-right {
  /* Правый слот — polymorphic flex-container action-zone.
     player.js Records / Block 3: 2 children [flag-btn] [bank-btn].
     CTO decision 2026-04-26 Pre-flight #1: polymorphic flex (§11.16 spec).
     grid-column задан ЯВНО (§11.16), чтобы симметрия слотов не зависела от
     состава media-группы. */
  grid-column: 3;
  display: flex;
  gap: var(--space-2);
  align-items: center;
  justify-self: end;
}

/* §11.16 (абзац «Пиксельный центр держится, пока слот помещается в свою
   колонку; дальше приоритет — целостность ряда») — многодетный ряд §11.58-F
   не переносится на вторую строку при нехватке места: без этого правило
   кнопка «В банк» ломается на «В»/«банк» (высота 35 → 52px). Порога и
   @media нет намеренно (§6.4 объявляет breakpoint в data-content эскалацией
   к CTO) — правило самонастраивается от нехватки места, не от ширины окна;
   на 1320px+ и на однокамерной аптеке не смещает ничего (замер 03.09.2026,
   docs/design-system.md §11.16). */
.ds-panel-action-slot-right button {
  white-space: nowrap;
}

/* §11.17 — .ds-panel-time + .ds-panel-speed */

.ds-panel-time {
  /* Time display — IBM Plex Mono compact tabular numerics.
     Mirrors .ds-col-mono but in panel-context.
     Calibrated from .exp-panel-time: font-size 16px → var(--fs-mono) (12px per §5 mono role),
     font-family var(--font-mono), tabular-nums, color var(--text-muted). */
  font-family: var(--font-mono);
  font-size: var(--fs-mono);
  font-variant-numeric: tabular-nums;
  color: var(--text-muted);
  white-space: nowrap;
}

.ds-panel-speed {
  /* Wrapper для 3-speed-button group.
     ID-anchor #panel-speed нужен для toggle-handler в player.js.
     Calibrated from .exp-panel-speed gap: 4px → var(--space-2) (~8px) — calibration acceptable. */
  display: inline-flex;
  gap: var(--space-2);
}

/* §11.18 — .ds-panel-waveform + .ds-panel-transcript + .ds-panel-audio-expired */

.ds-panel-waveform {
  /* Wavesurfer.js canvas wrapper — surface-2 background, radius-sm.
     Calibrated from .exp-panel-waveform: background #eef2f9 → var(--surface-2),
     border-radius 6px → var(--radius-sm), min-height 80px preserved,
     margin: var(--space-3) var(--space-4) для gutter (бриф §2.4). */
  min-height: 80px;
  border-radius: var(--radius-sm);
  overflow: hidden;
  background: var(--surface-2);
  margin: var(--space-3) var(--space-4);
  /* position: relative (PUM-1404, §11.55) — контекст позиционирования для
     .ds-waveform-marks ниже: без него слой с position:absolute привязался
     бы к произвольному позиционированному предку выше по дереву, и метки
     уехали бы за пределы панели. Правка минимальна — раскладку не меняет
     (у элемента и так не было собственного смещения от static-потока). */
  position: relative;
}

/* wavesurfer рендерит canvas — нужна явная ширина (mirror .exp-panel-waveform > *) */
.ds-panel-waveform > * {
  width: 100% !important;
}

/* §11.55 — .ds-waveform-marks (слой) / .ds-waveform-mark (метка вежливой
   фразы), PUM-1404 окно «Метки на дорожке».

   .ds-waveform-marks несёт свой размер через inset: 0. Сами
   .ds-waveform-mark — потомки СЛОЯ, не прямые потомки #panel-waveform: иначе
   унаследовали бы ту же ширину и стали бы полосой во всю дорожку.

   overflow:hidden с .ds-panel-waveform (выше) намеренно не снимается — §11.55
   design-system: у краёв дорожки метка поджимается внутрь и остаётся
   видимой целиком (clamp() в .ds-waveform-mark ниже); снять обрезку ради
   одной метки означало бы править общий атом всех четырёх поверхностей
   панели — отвергнуто как более дорогое. */
.ds-waveform-marks {
  position: absolute;
  inset: 0;
  pointer-events: none;
}

.ds-waveform-mark {
  /* Треугольник вершиной вниз у верхней кромки дорожки (§11.55). Заливка,
     не stroke/иконка — это аннотация графика (§9 «иконка ↔ аннотация
     графики»), не icon-glyph. */
  position: absolute;
  top: 0;
  /* --mark-pos — доля длительности в процентах, единственное число,
     которое отдаёт JS (waveformMarkTimes в shared/transcript_track.js +
     renderWaveformMarks в explorer/player.js);
     вся остальная геометрия — здесь. "- 5px" центрирует вершину метки на
     позиции слова (половина ширины 10px); clamp() поджимает метку внутрь
     у краёв дорожки, не срезая её — край не редкий случай (приветствие
     открывает разговор, его метка стоит почти в нуле). Второго механизма
     центрирования (transform: translateX(-50%)) не заводим — сдвиг уже
     здесь. Геометрия метки живёт в CSS намеренно: JS отдаёт долю
     длительности и о ширине метки не знает. */
  left: clamp(0px, calc(var(--mark-pos) - 5px), calc(100% - 10px));
  width: 10px;
  height: 7px;
  background: var(--pos-700);
  clip-path: polygon(0 0, 100% 0, 50% 100%);
  pointer-events: none;
  z-index: var(--z-base);
}

.ds-panel-transcript {
  /* Editorial reading-passage typography — calibrated post-merge Wave 2.5
     visual review (CEO 2026-04-25). PUM-547 calibration §11.18.
     Mode A partial coverage 52.6% (§11.18).
     Typography applies к BOTH branches (with-dashes + no-dashes). */
  max-height: 320px;
  overflow-y: auto;
  padding: var(--space-4) var(--space-5);
  border-top: 1px solid var(--border-muted);
  font-family: var(--font-sans);
  font-size: 15px;                     /* §5.3 second scoped exception (PUM-547) */
  line-height: 1.65;
  color: var(--text);
}

.ds-panel-transcript:focus-visible {
  /* Окно расшифровки — остановка в порядке обхода панели (§11.55, A11Y),
     значит и фокус на нём показывается языком системы: halo --focus-ring
     (§8.1), не сплошная обводка UA по умолчанию, которую §8.1 запрещает.
     Тот же паттерн, что у .ds-transcript-follow ниже. */
  outline: none;
  box-shadow: var(--focus-ring);
}

.ds-panel-transcript > .ds-transcript-body {
  /* Inner wrapper для measure-constraint (~85-95 chars/line at 15px Plex Sans
     Cyrillic). Не на .ds-panel-transcript напрямую, чтобы scrollbar остался
     у panel-edge. Left-aligned (no margin auto) — короткие transcripts не
     выглядят awkward в центре large panel. PUM-547. */
  max-width: 760px;
}

.ds-transcript-body p {
  /* Paragraph per реплика (split per dash-prefix в renderPanelTranscript).
     Active в hasDashes=true case. PUM-547. */
  margin: 0 0 var(--space-3);
}

.ds-transcript-body p:last-child {
  margin-bottom: 0;
}

.ds-transcript-body p::before {
  /* Dash-prefix renders через CSS, не часть word-token text — сохраняет
     click-to-seek delegation на .word-token. With-dashes case (52.6%). PUM-547. */
  content: "— ";
  color: var(--text-muted);
}

.ds-transcript-body p.ds-transcript-flat::before {
  /* Q5 fix — Tech Lead session 2 follow-up. Suppress dash для no-dash case
     (47.4% records) — избегает semantic regression vs V0 (mock-dash префикс
     врёт о структуре, читатель ожидает dialogue → видит wall-of-text). PUM-547. */
  content: none;
}

/* §11.55 — .ds-transcript-follow-wrap / .ds-transcript-follow (кнопка
   «Вернуться к текущему слову»), PUM-1404 окно «Следование за словом»
   (контракт пп.2, 6-7; A11Y).

   .ds-transcript-follow-wrap — sticky-обёртка НУЛЕВОЙ высоты, последний
   потомок .ds-panel-transcript (player.js, renderPanelTranscript). Держит
   кнопку у нижней кромки ВИДИМОЙ области при прокрутке контейнера
   (position: sticky) без добавления геометрии в поток (height: 0) — §11.55
   «текста не смещает». */
.ds-transcript-follow-wrap {
  position: sticky;
  bottom: var(--space-2);
  height: 0;
}

.ds-transcript-follow {
  /* position: absolute ВНУТРИ sticky-обёртки-предка, не прямо в
     .ds-panel-transcript: контейнер расшифровки сам прокручиваемый
     (overflow-y: auto, выше) — абсолютный потомок САМОГО контейнера уехал бы
     вместе с текстом при прокрутке, а §11.55 требует обратного («при
     прокрутке окна остаётся на месте»). */
  position: absolute;
  right: var(--space-3);
  bottom: 0;
  font-family: var(--font-sans);
  font-size: var(--fs-label);
  font-weight: var(--fw-microcopy);
  padding: var(--space-1) var(--space-3);
  border-radius: var(--radius-sm);
  background: var(--surface);
  border: 1px solid var(--border);
  box-shadow: var(--shadow-overlay);
  color: var(--text);
  cursor: pointer;
}

.ds-transcript-follow:hover {
  background: var(--hover-row);
  color: var(--text);
}

.ds-transcript-follow:focus-visible {
  outline: none;
  box-shadow: var(--focus-ring);
}

.ds-panel-audio-expired {
  /* Muted-italic placeholder для expired audio (baseline-silent — audio expiration не error).
     Calibrated from .exp-panel-audio-expired: меняет visual pattern с «серый card-like блок»
     (legacy bg #f4f4f6, flex, icon) на «inline italic muted placeholder» (бриф §2.4).
     color var(--text-subtle) (tiхий уровень), font-style italic (editorial soft). */
  padding: var(--space-3) var(--space-4);
  font-size: var(--fs-label);
  color: var(--text-subtle);
  font-style: italic;
}

/* §11.19 — .ds-btn-destructive destructive secondary action
   Generalization от .ds-bank-btn-remove (PUM-544 single-consumer) → .ds-btn-destructive (PUM-553).
   Triggered: 3+ consumers (Bank remove + cancel-фокус-плана + delete-attachment).
   .ds-bank-btn-remove → .ds-btn-destructive + --filled modifier (§12.3). */

.ds-btn-destructive {
  /* Destructive secondary action — quiet outline-graphite default, warn-escalation on hover.
     Tier 3-destructive в 3-tier button taxonomy (§10 extension PUM-553).
     НЕ bg-fill hover — signalling hover-intent, не commit.
     Consumers: remove-from-bank, cancel-фокус-плана, modal-trigger Cancel-action. */
  background: var(--surface);
  color: var(--text-muted);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  font-family: var(--font-sans);
  font-size: var(--fs-label);
  font-weight: 500;
  line-height: 1.4;
  padding: var(--space-2) var(--space-4);
  cursor: pointer;
  transition: color var(--dur-fast) var(--ease-standard),
              border-color var(--dur-fast) var(--ease-standard),
              background var(--dur-fast) var(--ease-standard);
}

.ds-btn-destructive:hover {
  /* warn-outline-only — signalling hover-intent, не commit. БЕЗ bg-fill. */
  color: var(--warn-700);
  border-color: var(--warn-500);
}

.ds-btn-destructive:focus-visible {
  outline: none;
  box-shadow: var(--focus-ring-graphite);
}

.ds-btn-destructive:disabled {
  color: var(--disabled-text);
  border-color: var(--border);
  cursor: not-allowed;
}

.ds-btn-destructive--filled {
  /* Filled-warn modifier — для confirmation modal Submit (commit-cancel-action).
     Tier 3-destructive filled: visual gravity = «это финальное действие». */
  background: var(--warn-500);
  color: var(--surface);
  border-color: var(--warn-500);
}

.ds-btn-destructive--filled:hover {
  background: var(--warn-700);
  border-color: var(--warn-700);
  color: var(--surface);
}

.ds-btn-destructive--filled:focus-visible {
  box-shadow: 0 0 0 3px rgba(154, 109, 40, 0.2);   /* warn-500 alpha 20% halo */
}

/* ── §11.20-11.24 — PUM-558 Auth form atoms ──────────────────────────────────
   5 atom-семейств для editorial-auth-страниц (/login, /change-password):
   §11.20 .ds-auth-page + .ds-auth-wordmark + .ds-auth-card + .ds-auth-subtitle
          + .ds-auth-back-link — full-page layout pattern (G3)
   §11.21 .ds-form-field + .ds-form-field-label + .ds-form-field-hint
          + .ds-form-field-error + .ds-input--error — form-field layout (G5)
   §11.22 .ds-form-error — form-level alert banner (G2)
   §11.23 .ds-btn-primary + .ds-btn-primary--block — filled brand-burgundy
          primary CTA (G1, закрывает резерв из §11.x PUM-541 .ds-submit-btn)
   §11.24 .ds-input-with-suffix + .ds-input-suffix-btn — password show/hide
          eye toggle (G4)
   ─────────────────────────────────────────────────────────────────────────── */

/* §11.20 — Auth-page layout pattern */

.ds-auth-page {
  /* Full-page warmpaper layout: centered single-column карточка + wordmark.
     Используется на /login и /change-password (PUM-558). */
  min-height: 100vh;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  padding: var(--space-6);
  background: var(--bg);
}

@media (max-width: 480px) {
  .ds-auth-page {
    padding: var(--space-4);
  }
}

.ds-auth-wordmark {
  /* H1 brand mark — Plex Serif 28px graphite-900, centered, над карточкой.
     Симметрично §11.1 KPI-герой (тот же серифный регистр), но мельче. */
  font-family: var(--font-serif);
  font-size: var(--fs-h1);          /* 28px */
  font-weight: var(--fw-h1);        /* 500 */
  letter-spacing: var(--ls-h1);
  line-height: var(--lh-h1);
  color: var(--text);
  text-align: center;
  margin: 0 0 var(--space-6);
}

.ds-auth-subtitle {
  /* Подзаголовок над card в must_change_password сценарии:
     «При первом входе нужно задать новый пароль» (Plex Sans 14px muted).
     Используется вместо wordmark — fragment не дублируется. */
  font-family: var(--font-sans);
  font-size: var(--fs-body);
  font-weight: var(--fw-body);
  color: var(--text-muted);
  text-align: center;
  margin: 0 0 var(--space-6);
  max-width: 400px;
}

.ds-auth-card {
  /* Auth-context card — расширенный padding (32px) под form-density.
     Структурно — близкий родственник .ds-card (§11.8), но scope-isolated:
     auth-card не reuse .ds-card во избежание padding-override каскада. */
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  padding: var(--space-8);          /* 32px */
  width: 100%;
  max-width: 466px;                 /* поле формы 400px + отступы 2×32 + рамка 2×1 */
}

@media (max-width: 480px) {
  .ds-auth-card {
    padding: var(--space-6);        /* 24px на mobile — экономим место */
  }
}

.ds-auth-back-link {
  /* Ссылка «← Вернуться в Tenpharm» под card в обычном change-password
     сценарии (юзер сам зашёл сменить пароль). Не показывается в
     must_change_password flow — там нет пути обратно до смены. */
  display: inline-block;
  margin-top: var(--space-5);
  font-family: var(--font-sans);
  font-size: var(--fs-label);
  color: var(--text-muted);
  text-decoration: none;
  text-align: center;
  transition: color var(--dur-fast) var(--ease-standard);
}

.ds-auth-back-link:hover {
  color: var(--primary);
}

.ds-auth-back-link:focus-visible {
  outline: none;
  box-shadow: var(--focus-ring);
  border-radius: var(--radius-sm);
}

/* §11.21 — Form-field layout pattern */

.ds-form-field {
  /* Vertical stack: label → input → helper-text/error. */
  display: flex;
  flex-direction: column;
  gap: var(--space-1);              /* 4px между label/input/hint/error */
}

.ds-form-field + .ds-form-field {
  /* Vertical rhythm между соседними полями внутри одной формы — 20px. */
  margin-top: var(--space-5);
}

.ds-form-field-label {
  /* Label над input — Plex Sans 12px muted, без uppercase (editorial §5.3).
     Связывается с input через <label for="..."> или вложением. */
  font-family: var(--font-sans);
  font-size: var(--fs-th);          /* 12px */
  font-weight: var(--fw-th);        /* 500 */
  color: var(--text-muted);
}

.ds-form-field-hint {
  /* Helper-text под input (нейтральная подсказка: «Минимум 12 символов»).
     Скрывается, если на это поле появилась error (взаимоисключение —
     управляется на уровне разметки, не CSS). */
  font-family: var(--font-sans);
  font-size: var(--fs-microcopy);   /* 11px */
  font-weight: var(--fw-microcopy); /* 500 */
  color: var(--text-muted);
}

.ds-form-field-error {
  /* Per-field inline error под input.
     Используется вместе с .ds-input--error + aria-invalid="true" на input
     и aria-describedby="<error-id>" для связки. */
  font-family: var(--font-sans);
  font-size: var(--fs-microcopy);   /* 11px */
  font-weight: var(--fw-microcopy); /* 500 */
  color: var(--neg-700);
}

.ds-input.ds-input--error,
.ds-select.ds-input--error {
  /* Error-modifier на input — border переключается на neg-700.
     aria-invalid="true" — обязателен на разметке (a11y). */
  border-color: var(--neg-700);
}

.ds-input.ds-input--error:focus-visible,
.ds-select.ds-input--error:focus-visible {
  /* Error-state focus: сохраняем neg-700 border, halo нейтральный
     (graphite). Error-цветной halo при tab-navigation по нескольким
     невалидным полям читается слишком крикливо. */
  border-color: var(--neg-700);
  box-shadow: var(--focus-ring-graphite);
}

/* §11.22 — Form-level alert banner */

.ds-form-error {
  /* Form-level alert для server-errors (401/429) над/под формой.
     НЕ путать с .ds-state-error (отказ на месте таблицы, §11.65).
     Разметка: role="alert" + aria-live="polite". */
  display: flex;
  align-items: flex-start;
  gap: var(--space-2);
  background: var(--neg-50);
  border: 1px solid var(--neg-700);
  border-radius: var(--radius-sm);
  padding: var(--space-3) var(--space-4);
  font-family: var(--font-sans);
  font-size: var(--fs-label);       /* 13px */
  font-weight: var(--fw-label);     /* 400 */
  color: var(--neg-700);
}

.ds-form-error svg {
  /* Inline warning-triangle 16px (dsIcon('warning-triangle'), §9 PUM-539).
     Цвет наследуется через currentColor → --neg-700. */
  width: 16px;
  height: 16px;
  flex-shrink: 0;
  margin-top: 1px;                  /* визуальная привязка к baseline текста */
}

.ds-form-error--info {
  /* Info-tint модификатор (PUM-708): server-cap truncation, констатация
     состояния системы. Перекрывает только палитру (bg / border / text) —
     layout/typography общие с base. Cascade: class="ds-form-error ds-form-error--info".
     ARIA — caller использует role="status" + aria-live="polite" вместо role="alert". */
  background: var(--surface-2);
  border-color: var(--graphite-500);
  color: var(--graphite-700);
}

/* §11.23 — Primary CTA filled (закрывает резерв из §11.x PUM-541) */

.ds-btn-primary {
  /* Tier-1 filled-burgundy CTA для действий высшего уровня страницы:
     login submit, change-password submit, главные form-CTA в auth-flow.
     §10 3-tier button taxonomy:
       tier-1 .ds-btn-primary           filled-burgundy
       tier-2 .ds-submit-btn            outline-burgundy (filter / play / add-to-bank)
       tier-3 .ds-period-btn / etc.     outline-graphite (period / speed / remove). */
  display: inline-flex;
  align-items: center;
  justify-content: center;
  background: var(--primary);
  color: var(--surface);
  border: 1px solid var(--primary);
  border-radius: var(--radius-sm);
  font-family: var(--font-sans);
  font-size: var(--fs-label);       /* 13px — symmetric с .ds-submit-btn */
  font-weight: var(--fw-th);        /* 500 — symmetric с .ds-submit-btn */
  padding: var(--space-2) var(--space-5);
  cursor: pointer;
  transition: background var(--dur-fast) var(--ease-standard),
              border-color var(--dur-fast) var(--ease-standard),
              color var(--dur-fast) var(--ease-standard),
              box-shadow var(--dur-fast) var(--ease-standard);
}

.ds-btn-primary:hover {
  background: var(--primary-hover);
  border-color: var(--primary-hover);
}

.ds-btn-primary:focus-visible {
  outline: none;
  box-shadow: var(--focus-ring);
}

.ds-btn-primary:disabled,
.ds-btn-primary[aria-disabled="true"] {
  background: var(--disabled-bg);
  color: var(--disabled-text);
  border-color: var(--disabled-bg);
  cursor: not-allowed;
}

.ds-btn-primary:disabled:hover,
.ds-btn-primary[aria-disabled="true"]:hover {
  /* Disabled перекрывает hover — даже при mouse-over состояние не меняется. */
  background: var(--disabled-bg);
  border-color: var(--disabled-bg);
}

.ds-btn-primary[aria-busy="true"] {
  /* Loading: opacity-attenuation + pointer-events off.
     Без spinner-анимации (editorial §10.1: motion не привлекает внимание).
     Текст меняется (caller отвечает): «Войти» → «Вход…», «Сохранить» → «Сохранение…». */
  opacity: 0.65;
  pointer-events: none;
  cursor: not-allowed;
}

.ds-btn-primary--block {
  /* Full-width modifier — для form-context (auth submit). */
  display: flex;
  width: 100%;
}

/* §11.24 — Password show/hide eye toggle (input + suffix-button pattern) */

.ds-input-with-suffix {
  /* Wrapper для input + правой suffix-кнопки (например, password show/hide).
     Input получает padding-right через дочерний селектор, не модификатор —
     одно правило, не размножает API. */
  position: relative;
  display: block;
}

.ds-input-with-suffix > .ds-input {
  /* Резервируем место под suffix-кнопку: 24px иконка + 8px gap + 8px right-padding. */
  padding-right: 40px;
  width: 100%;
}

.ds-input-suffix-btn {
  /* Suffix-button внутри .ds-input-with-suffix. Default consumer — password
     show/hide eye (PUM-558). Generic — потенциально для clear-x, copy-icon. */
  position: absolute;
  top: 50%;
  right: var(--space-2);
  transform: translateY(-50%);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 24px;
  height: 24px;
  padding: 0;
  background: none;
  border: none;
  border-radius: var(--radius-sm);
  color: var(--text-muted);
  cursor: pointer;
  transition: color var(--dur-fast) var(--ease-standard),
              box-shadow var(--dur-fast) var(--ease-standard);
}

.ds-input-suffix-btn:hover {
  color: var(--text);
}

.ds-input-suffix-btn:focus-visible {
  outline: none;
  box-shadow: var(--focus-ring);
}

.ds-input-suffix-btn svg {
  width: 16px;
  height: 16px;
}

.ds-input-suffix-btn:disabled {
  color: var(--disabled-text);
  cursor: not-allowed;
}

/* ── §11.25 — PUM-553 Focus Plan UI atoms ─────────────────────────────────────
   9 atom-семейств для Focus Plan UI заведующей:
   §11.25 .ds-modal-overlay / .ds-modal / .ds-modal-header / .ds-modal-title /
          .ds-modal-body / .ds-modal-footer / .ds-modal-close — modal/dialog pattern
   §11.25 mini-atom .ds-btn-link — footer Cancel text-button
   §11.26 .ds-status-label / .ds-status-label--active / .ds-status-dot +
          3 modifiers (--closed, --cancelled, --extended) — lifecycle status
   §11.27 .ds-closure-note / .ds-closure-note-meta / .ds-closure-note-text — callout
   §11.28 .ds-textarea / .ds-textarea--error — textarea (extension §11.21)
   §11.29 .ds-checkbox / .ds-checkbox-field / .ds-checkbox-field-label — checkbox
   §11.31 .ds-attach-list / .ds-attach-item / .ds-attach-icon / .ds-attach-info /
          .ds-attach-filename / .ds-attach-meta / .ds-attach-actions — attachment list-item
   .ds-icon-btn--destructive — modifier для destructive icon-button (delete-attachment)
   ─────────────────────────────────────────────────────────────────────────── */

/* §11.25 — Modal / Dialog pattern */

.ds-modal-overlay {
  /* Full-screen dim layer. Fade-in transition per §10.5 (--dur-overlay, --ease-standard).
     Клик на overlay (вне .ds-modal) — закрыть модал. Клик внутри .ds-modal — не закрывает. */
  position: fixed; inset: 0;
  z-index: var(--z-overlay);                  /* 400 */
  background: rgba(28, 25, 23, 0.5);          /* graphite-700 alpha 50% */
  display: flex; align-items: center; justify-content: center;
  padding: var(--space-6);
  opacity: 0;
  transition: opacity var(--dur-overlay) var(--ease-standard);
}

.ds-modal-overlay--visible { opacity: 1; }

.ds-modal {
  /* Dialog box: max-width 480px, flex-column, enter-animation scale+fade.
     Body scroll lock при mount: body.has-modal { overflow: hidden } (caller-driven).
     Focus-trap + ESC-close + click-outside close — JS (caller). */
  position: relative;
  z-index: var(--z-modal);                    /* 500 */
  background: var(--surface);
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-overlay);
  width: 100%; max-width: 480px;
  max-height: calc(100vh - var(--space-12));
  display: flex; flex-direction: column;
  transform: scale(0.98); opacity: 0;
  transition: transform var(--dur-overlay) var(--ease-out),
              opacity var(--dur-overlay) var(--ease-standard);
}

.ds-modal--visible { transform: scale(1); opacity: 1; }

@media (prefers-reduced-motion: reduce) {
  .ds-modal {
    /* Без scale — только opacity per §10.5 reduced-motion rule. */
    transform: none;
  }
}

.ds-modal-header {
  /* Title + close-button row. padding: top/sides --space-6/--space-8, bottom --space-3. */
  display: flex; align-items: flex-start; justify-content: space-between;
  gap: var(--space-3);
  padding: var(--space-6) var(--space-8) var(--space-3);
}

.ds-modal-title {
  /* Serif H2 — Plex Serif 20px/500 (§5 typography, --fs-h2/--fw-h2/--lh-h2). */
  font-family: var(--font-serif);
  font-size: var(--fs-h2);
  font-weight: var(--fw-h2);
  line-height: var(--lh-h2);
  margin: 0; flex: 1; color: var(--text);
}

.ds-modal-close {
  /* Reuse .ds-icon-btn — icon-only square 32×32, hover bg --hover-row.
     Семантически: закрытие модала. aria-label="Закрыть". flex-shrink: 0. */
  flex-shrink: 0;
}

.ds-modal-body {
  /* Scrollable form/content area. overflow-y: auto при длинном контенте. */
  padding: 0 var(--space-8) var(--space-6);
  overflow-y: auto;
}

.ds-modal-footer {
  /* Action-row: Cancel (left) + Submit (right). border-top hairline. */
  display: flex; gap: var(--space-3);
  align-items: center; justify-content: flex-end;
  padding: var(--space-4) var(--space-8) var(--space-6);
  border-top: 1px solid var(--border-muted);
}

/* §11.25 mini-atom — .ds-btn-link (footer Cancel text-button)
   True text-link semantics для modal footer Cancel action.
   Tech Lead decision (бриф §5.6): .ds-btn-link принят, не reuse .ds-period-btn. */

.ds-btn-link {
  /* Text-button: transparent bg, no border, muted text.
     Hover: --text (subtle darkening, editorial). Focus: --focus-ring-graphite.
     Padding 8/12 (--space-2/--space-3) — symmetric с icon-btn square 32. */
  padding: var(--space-2) var(--space-3);
  background: transparent;
  border: none;
  color: var(--text-muted);
  font-family: var(--font-sans);
  font-size: var(--fs-label);
  font-weight: 500;
  cursor: pointer;
  line-height: 1.4;
  transition: color var(--dur-fast) var(--ease-standard);
}

/* §11.25: текст действия вровень с текстом колонки; поле и halo атома сохранены. */
.ds-btn-link--flush {
  margin-left: calc(-1 * var(--space-3));
}

.ds-btn-link:hover {
  color: var(--text);
}

.ds-btn-link:disabled { color: var(--disabled-text); background: none; cursor: not-allowed; }

.ds-btn-link:focus-visible {
  outline: none;
  box-shadow: var(--focus-ring-graphite);
}

/* §11.80 — окно подрезки: сцена поверх общего носителя панели. */
.ds-modal--wide       { max-width: 960px; }
.ds-trim-tool         { margin-top: var(--space-4); }
.ds-trim-tool .ds-panel-controls      { padding: 0 0 var(--space-3); }
.ds-trim-tool .ds-panel-transcript    { padding: var(--space-4) var(--space-3) var(--space-4) 0; }
.ds-trim-tool .ds-panel-audio-expired { padding: var(--space-3) 0 0; border-top: 1px solid var(--border-muted); }
.ds-modal-body:has(.ds-trim-tool--transcript) { padding-bottom: 0; }
.ds-trim-stage        { position: relative; margin-bottom: var(--space-3); }
.ds-trim-stage .ds-panel-waveform { margin: 0; cursor: pointer;
  /* Слои дорожки несут свои z-index: без своего контекста наложения они
     красятся поверх слоя сцены, и вуаль остаётся под барами. */
  isolation: isolate; }
.ds-trim-overlay      { position: absolute; inset: 0; pointer-events: none; }
.ds-trim-veil         { position: absolute; top: 0; bottom: 0; background: var(--surface-2); opacity: .75; }
.ds-trim-frame        { position: absolute; top: 0; bottom: 0;
                        border-top: 1px solid var(--graphite-700); border-bottom: 1px solid var(--graphite-700); }
.ds-trim-handle       { position: absolute; top: 0; bottom: 0; width: var(--space-6);
                        padding: 0; border: 0; background: none;
                        cursor: ew-resize; touch-action: none; pointer-events: auto; }
.ds-trim-handle-line  { position: absolute; top: 0; bottom: 0; width: 2px; background: var(--graphite-700); }
.ds-trim-handle-grip  { position: absolute; top: 50%; width: var(--space-3); height: var(--space-8);
                        margin-top: calc(-1 * var(--space-4)); background: var(--graphite-700);
                        border-radius: var(--radius-sm) 0 0 var(--radius-sm); }   /* у правой ручки зеркально */

.ds-trim-veil--start { left: 0; width: var(--trim-start); border-radius: var(--radius-sm) 0 0 var(--radius-sm); }
.ds-trim-veil--end   { left: var(--trim-end); right: 0; border-radius: 0 var(--radius-sm) var(--radius-sm) 0; }
.ds-trim-frame { left: var(--trim-start); width: calc(var(--trim-end) - var(--trim-start)); }
.ds-trim-handle--start { left: var(--trim-start); transform: translateX(calc(-100% + 1px)); }
.ds-trim-handle--end   { left: var(--trim-end); transform: translateX(-1px); }
.ds-trim-handle--start .ds-trim-handle-line { right: 0; }
.ds-trim-handle--end .ds-trim-handle-line   { left: 0; }
.ds-trim-handle--start .ds-trim-handle-grip { right: 2px; }
.ds-trim-handle--end .ds-trim-handle-grip   { left: 2px; border-radius: 0 var(--radius-sm) var(--radius-sm) 0; }
.ds-trim-handle-grip::after {
  content: ''; position: absolute; top: 50%; left: 50%;
  transform: translate(-50%, -50%); width: 2px; height: var(--space-3);
  background: var(--surface); opacity: .55;
}
.ds-trim-handle:hover .ds-trim-handle-line { box-shadow: 0 0 0 .5px var(--graphite-700); }
.ds-trim-handle--active .ds-trim-handle-line { background: var(--primary); box-shadow: none; }
.ds-trim-handle--active .ds-trim-handle-grip { background: var(--primary); }
.ds-trim-handle:focus-visible { outline: none; }
.ds-trim-handle:focus-visible .ds-trim-handle-grip { box-shadow: var(--focus-ring); }

/* §11.80-C — отметка границы: Tier-3 neutral в метриках §11.48-C. */
.ds-trim-mark {
  background: var(--surface); color: var(--graphite-700);
  border: 1px solid var(--border-strong); border-radius: var(--radius-sm);
  padding: 6px 12px; font-size: var(--fs-label); font-weight: 500;
  display: inline-flex; align-items: center; justify-content: center; cursor: pointer;
  min-width: calc(var(--space-8) * 4);
}
.ds-trim-mark:hover:not(:disabled) { background: var(--surface-2); }
.ds-trim-mark:disabled { color: var(--disabled-text); border-color: var(--border); cursor: not-allowed; }
.ds-trim-mark:focus-visible { outline: none; box-shadow: var(--focus-ring-graphite); }
.ds-trim-tool .ds-panel-action-slot-left { justify-self: start; }

/* §11.80-A — на низком окне сжимается текст, сцена и ряд сохраняют высоту. */
.ds-modal-body:has(.ds-trim-tool),
.ds-trim-tool { display: flex; flex-direction: column; min-height: 0; }
.ds-trim-tool .ds-trim-stage,
.ds-trim-tool .ds-panel-controls,
.ds-trim-tool .ds-panel-audio-expired { flex: none; }
.ds-trim-tool .ds-panel-transcript {
  max-height: 200px; flex: 0 1 auto; min-height: calc(var(--space-12) * 2);
}

/* §11.26 — Lifecycle status (`.ds-status-label` / `.ds-status-dot`) */

.ds-status-label {
  /* Asymmetric pattern: active — silent (no dot, weight 500, --text);
     terminal — colored 6px dot + muted text (--text-muted, weight 400).
     V3 CEO-approved 2026-04-29. Sparring: workdocs/design-variants/focus-status-variants.html. */
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);                /* 8px */
  font-family: var(--font-sans);
  font-size: var(--fs-label);         /* 13px */
  font-weight: var(--fw-label);       /* 400 */
  color: var(--text-muted);
  white-space: nowrap;
}

.ds-status-label--active {
  /* Active: no dot, weight 500, --text color (baseline без визуального шума). */
  color: var(--text);
  font-weight: 500;
}

.ds-status-dot {
  /* Terminal-state indicator: 6px circle. flex-shrink: 0.
     aria-hidden="true" обязателен — visual-only (текст самодостаточен для AT). */
  width: 6px;
  height: 6px;
  border-radius: 50%;
  flex-shrink: 0;
}

.ds-status-dot--closed    { background: var(--pos-700); }   /* sage green — успешное завершение */
.ds-status-dot--cancelled { background: var(--neg-700); }   /* terracotta red — отмена */
.ds-status-dot--extended  { background: var(--graphite-500); }  /* graphite — нейтральное продление */
.ds-status-dot--sealed    { background: var(--graphite-500); }  /* graphite — нейтральное терминальное: период запечатан (§11.41 reuse) */
/* Два тона ниже — ось ГОТОВНОСТИ, не temporal (§11.26 «Два graphite у одной точки»):
   --warn   = незаполненное там, где заполнить можно (панель §11.41, список и ряд §11.64);
   --dormant = незаполненное там, где заполнить уже нельзя (прошедший период, §11.64).
   graphite-300 у --dormant численно совпадает с --text-subtle намеренно: точка и
   приглушённая подпись прошедшего элемента рядом читаются одной группой. */
.ds-status-dot--warn      { background: var(--warn-500); }
.ds-status-dot--dormant   { background: var(--graphite-300); }

/* §11.27 — Closure-note callout (`.ds-closure-note*`)
   Territorial closure context: применяется только при closure_actor === 'terr_manager'.
   Editorial blockquote-pattern (HBR / Every.to precedent). */

.ds-closure-note {
  /* Left-border hairline 2px + warm subtle surface. margin: --space-4 0.
     border-radius: 0 на левом (border-side) / --radius-sm на правом (editorial). */
  border-left: 2px solid var(--border);
  background: var(--surface-2);
  padding: var(--space-3) var(--space-4);
  margin: var(--space-4) 0;
  border-radius: 0 var(--radius-sm) var(--radius-sm) 0;
}

.ds-closure-note-meta {
  /* Microcopy: uploader / date. 11px --fs-microcopy --text-muted. */
  font-family: var(--font-sans);
  font-size: var(--fs-microcopy);
  color: var(--text-muted);
  margin-bottom: var(--space-1);
}

.ds-closure-note-text {
  /* Body text: closure reason / note. 13px --fs-label --text line-height 1.55. */
  font-family: var(--font-sans);
  font-size: var(--fs-label);
  color: var(--text);
  line-height: 1.55;
}

/* §11.28 — Textarea (`.ds-textarea` / `.ds-textarea--error`)
   Extension §11.21 form-controls. Multi-line для plan_text / fact_text / closure_reason.
   Padding 12/16 (больше чем .ds-input 8/12 для multi-line readability comfort). */

.ds-textarea {
  font-family: var(--font-sans);
  font-size: var(--fs-body);                  /* 14px */
  line-height: 1.55;
  color: var(--text);
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  padding: var(--space-3) var(--space-4);     /* 12 vertical / 16 horizontal */
  min-height: 80px;
  resize: vertical;                            /* horizontal-resize ломает layout */
  width: 100%;
  transition: border-color var(--dur-fast) var(--ease-standard),
              box-shadow var(--dur-fast) var(--ease-standard);
}

.ds-textarea:focus-visible {
  outline: none;
  border-color: var(--primary);
  box-shadow: var(--focus-ring);
}

.ds-textarea--error {
  border-color: var(--neg-700);
}

.ds-textarea:disabled {
  background: var(--disabled-bg);
  color: var(--disabled-text);
  cursor: not-allowed;
}

.ds-textarea::placeholder { color: var(--text-subtle); }

/* §11.29 — Editorial checkbox (`.ds-checkbox*`)
   Custom appearance: 16×16, checked-fill = --graphite-700 (НЕ burgundy).
   Обоснование §4.2: burgundy budget «≤5 раз на экране»; checkbox — interaction-state.
   Focus-ring burgundy (--focus-ring) поверх checked-state — по §8.1 interactive control. */

.ds-checkbox {
  /* Custom checkbox: appearance none + custom bg/border + checked SVG. */
  appearance: none;
  -webkit-appearance: none;
  display: inline-block;
  width: 16px; height: 16px;
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  background: var(--surface);
  cursor: pointer;
  margin: 0;
  flex-shrink: 0;
  position: relative;
  transition: border-color var(--dur-fast) var(--ease-standard),
              background var(--dur-fast) var(--ease-standard);
}

.ds-checkbox:hover { border-color: var(--graphite-500); }

.ds-checkbox:focus-visible {
  outline: none;
  box-shadow: var(--focus-ring);
}

.ds-checkbox:checked {
  background: var(--graphite-700);   /* НЕ burgundy — обоснование §4.5 */
  border-color: var(--graphite-700);
}

.ds-checkbox:checked::after {
  /* Inline SVG checkmark (белый stroke на graphite-700 bg). */
  content: "";
  position: absolute; inset: 0;
  background-image: url("data:image/svg+xml,%3Csvg viewBox='0 0 12 12' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M2.5 6L5 8.5L9.5 4' stroke='%23ffffff' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round' fill='none'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: center;
  background-size: 12px 12px;
}

.ds-checkbox:disabled {
  background: var(--disabled-bg);
  border-color: var(--border);
  cursor: not-allowed;
}

.ds-checkbox-field {
  /* Flex-wrapper для checkbox + label. align-items: flex-start для multi-line label. */
  display: flex;
  align-items: flex-start;
  gap: var(--space-2);
  cursor: pointer;
}

.ds-checkbox-field-label {
  /* Label рядом с checkbox. 13px --fs-label --text, line-height 1.55. user-select: none. */
  font-family: var(--font-sans);
  font-size: var(--fs-label);
  color: var(--text);
  user-select: none;
  line-height: 1.55;
}

/* §11.31 — Attachment list-item (`.ds-attach-*`)
   List-item composition: icon + filename + meta + actions.
   НЕ table (TH-row для 1-3 attachments избыточен), НЕ card (излишний воздух).
   Right-ellipsis truncation для filename (не middle-ellipsis — overengineering Phase 1). */

.ds-attach-list {
  list-style: none;
  padding: 0; margin: 0;
  border-top: 1px solid var(--border-muted);
}

.ds-attach-item {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  padding: var(--space-3) 0;
  border-bottom: 1px solid var(--border-muted);
}

.ds-attach-icon {
  /* File-type icon контейнер: 20×20px. Цвет: --text-muted (neutral, не severity). */
  flex-shrink: 0;
  width: 20px; height: 20px;
  color: var(--text-muted);
  display: inline-flex;
  align-items: center; justify-content: center;
}

.ds-attach-icon svg {
  width: 20px; height: 20px;
}

.ds-attach-info {
  /* Flex-column: filename + meta. flex: 1; min-width: 0 для truncation. */
  flex: 1; min-width: 0;
  display: flex; flex-direction: column;
  gap: var(--gap-metric-stack);             /* 4px */
}

.ds-attach-filename {
  /* Right-ellipsis truncation для длинных имён. 14px --fs-body --text. */
  font-family: var(--font-sans);
  font-size: var(--fs-body);
  color: var(--text);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.ds-attach-meta {
  /* Secondary meta: дата · пользователь · размер. 13px --fs-label --text-muted. */
  font-family: var(--font-sans);
  font-size: var(--fs-label);
  color: var(--text-muted);
}

.ds-attach-actions {
  /* Action-buttons group: download + delete. flex-shrink: 0. gap: --space-1. */
  flex-shrink: 0;
  display: flex; gap: var(--space-1);
}

/* .ds-icon-btn--destructive modifier (для delete-attachment в §11.31)
   Semantic clarity (§11.31, §11.51): модификатор именуется по тому, что он
   окрашивает, — свойство самой вещи или действие кнопки. --destructive =
   «действие кнопки»; ветка «свойство вещи» сегодня потребителей не имеет
   (её единственный снят вместе с функцией «Плохое аудио», PUM-1449) —
   различение остаётся правилом именования, а не описанием пары. */

.ds-icon-btn--destructive {
  /* Default: --text-muted (neutral, icon-level). Hover: --warn-700 (warn-signal).
     Reuse .ds-icon-btn base styles (размер, transitions) — modifier расширяет. */
  color: var(--text-muted);
}

.ds-icon-btn--destructive:hover {
  color: var(--warn-700);
}

/* ── §11.39 Activity cell — сетка ролей + тултип-матрица (PUM-1261) ───
   Свёртка прежней стопки возможностей (Активность · роль, PUM-802) в
   фиксированную сетку ролей: строка на роль вместо строки на пару, имена
   тем ушли в тултип. Прежние атомы (.ds-opp-stack / .ds-opp / .ds-opp-text /
   .ds-opp-role / .ds-opp--bg / .ds-opp--done / .ds-opp--missed) и их маркер
   .ds-opp-marker сняты вместе с renderOpportunitiesCell (подключение —
   подзадача 2/3, потребителей не осталось ни на одной из трёх вкладок; §12.3
   design-system.md). .ds-role-marker ниже — не буквальное переиспользование
   класса .ds-opp-marker (тот снят целиком), а отдельный атом той же
   геометрии (10px inset-dot, dot-язык §11.9/§11.12 не меняется); служит ОБА
   контекста — строку сетки ячейки И клетку матрицы тултипа ниже
   (checks_table_pure.js _renderRoleMarker — общий рендер-хелпер обоих). */

.ds-role-grid {
  display: flex;
  flex-direction: column;
  gap: 2px;                        /* design-spec exception §6.2 — та же величина,
                                       что у .ds-goods (PUM-1258): константная высота
                                       двухстрочной ячейки-соседа держится тем же шагом */
}

.ds-role-row {
  display: flex;
  align-items: baseline;
  gap: var(--space-2);
  line-height: 1.45;                /* design-spec exception §6.2 — калибровка CEO 31.07,
                                       workdocs/design-variants/records-row-cells-variants.html */
  white-space: nowrap;
}

.ds-role-row--none {
  /* Третье состояние — тон ВСЕЙ строки, не новый знак (правило 4 CONTRACT
     PUM-1261): знак и слово роли приглушены одним правилом через
     наследование color, а не отдельными правилами на каждом ребёнке. */
  color: var(--text-subtle);
}

.ds-role-marker {
  /* 10px inset-dot (та же геометрия, что у снятого .ds-opp-marker, §12.3) —
     единственный атом маркера дот-языка в этой секции; служит и строке
     сетки ячейки, и клетке матрицы тултипа (.ds-tt-matrix переопределяет
     только justify-self, геометрия знака не дублируется). */
  display: inline-block;
  width: 10px;
  height: 10px;
  border-radius: 50%;
  flex-shrink: 0;
  position: relative;
  top: 1px;                         /* оптическая подстройка к baseline строки —
                                        пиксель открыт калибровке на визуальном smoke
                                        (Implementation Notes PUM-1261; ячейка не
                                        подключена в этой подзадаче, смотреть не на чем) */
  text-align: center;
  font-size: 9px;
  line-height: 10px;
}

.ds-role-marker--done {
  background: var(--pos-700);       /* ● реализовано */
}

.ds-role-marker--missed {
  background: transparent;
  box-shadow: inset 0 0 0 1.5px var(--text-subtle);   /* ○ упущено */
}

.ds-role-marker--none {
  color: var(--text-subtle);        /* — прочерк ТЕКСТОМ (правило 4), не CSS-точка —
                                        тот же приём, что у .ds-goods-name прочерка */
  top: -1px;                        /* снимает +1px базового .ds-role-marker и добавляет
                                        столько же вверх: при align-items:baseline пустой
                                        inline-block (●/○) встаёт по нижней кромке бокса,
                                        а несущий текст (—) — по базовой линии своего
                                        текста, и знаки расходятся по вертикали на 2px.
                                        Компенсация была и в CEO-эталоне (.m top:2px при
                                        .m.none top:0, records-row-cells-variants.html);
                                        значения здесь свои — маркер 10px, не 12px.
                                        Без неё столбец маркеров перестаёт быть
                                        вертикальным ровно на смешанных состояниях, а
                                        они норма: «Замена» — прочерк на 76.6% чеков. */
}

.ds-role-count {
  /* Ярус справочного числа (§7.3, прецедент .ds-rate-context) — тон
     несущий, не косметический: число рядом с ○ иначе читалось бы как
     «N провалов» (design-system.md §11.39). Не выводится при state=none —
     решение принято в JS (checks_table_pure.js _renderRoleRow), здесь
     только тон готового текста. */
  color: var(--text-subtle);
  font-variant-numeric: tabular-nums;
}

.ds-role-other {
  /* Метка «по другим: <роли>» (правила 11-13) — отступ под слово роли:
     ширина маркер-слота (10px) + gap строки (--space-2, 8px) = 18px;
     формула назначена Tech Lead (Implementation Notes PUM-1261 — «ширина
     маркер-слота + его gap»), число отличается от черновика design-брифа
     (20px = 12px+8px) потому что маркер-слот здесь 10px, не 12px. */
  font-size: var(--fs-microcopy);
  color: var(--text-muted);
  margin-top: 2px;
  padding-left: 18px;
  /* Переносится, а не усекается — тот же выбор и по тому же основанию, что у
     имени Активности в шапке колонки (правило 16): усечённое перечисление
     ролей сделало бы метку нечитаемой ровно там, ради чего она заведена.
     nowrap здесь не держится по ширине: перечисление из двух и более ролей
     шире того, что колонка «Активность» способна дать метке при действующей
     раскладке, — вылезал бы весь набор форм, кроме односложной, причём на
     карточке сотрудника режим сужения включён всегда. Числа потребности и
     доступного живут в §11.39 «Раскладка», второй их записи здесь не
     заводится: она разъезжается молча от чужой калибровки ширины колонки
     (PUM-1262, 150 → 170px — прежняя запись пережила своё число). Про высоту здесь
     утверждения нет намеренно: метка встаёт и под НЕсжатой сеткой (сужение
     статусом либо видом Активности не сжимает состав строк), поэтому цена
     по высоте — решение design-system, а не следствие вёрстки; действующая
     граница живёт в §11.39 «Максимум высоты ячейки», и второй её записи
     здесь не заводится. */
}

/* ── Тултип «Активность» — матрица «тема × роль» (§11.6, PUM-1261) ───
   .ds-tooltip--wide (§11.6, заведён PUM-1258) переиспользуется без правок —
   второго widescreen-варианта тултипа не заводится. */

.ds-tt-matrix {
  display: grid;
  grid-template-columns: minmax(0, 1fr) repeat(var(--tt-cols, 2), 34px);
  align-items: center;              /* Знаки матрицы — по вертикальному центру строки,
                                        как в CEO-эталоне (records-row-cells-variants.html).
                                        Прежняя baseline расходилась на тех же 2px, что и
                                        сетка ячейки: часть клеток несёт пустой бокс (●/○),
                                        часть — текстовый прочерк, и по базовой линии они
                                        не сходятся. В матрице это заметнее, чем в ячейке:
                                        прочерков там большинство клеток. */
  column-gap: var(--space-2);
  row-gap: 3px;                     /* design-spec exception §6.2 — тот же row-gap,
                                        что у .ds-tt-goods (§11.39 соседняя матрица) */
}

.ds-tt-matrix .ds-role-marker {
  top: 0;                           /* Оптическая подстройка (.ds-role-marker top:1px и
                                        парный -1px у --none) существует ради baseline
                                        строки ячейки; в матрице элементы центрируются
                                        гридом, и relative-смещение накладывается ПОВЕРХ
                                        центрирования — те же 2px расхождения, только
                                        источник другой. Здесь оно обнуляется для всех
                                        состояний сразу, поэтому знаки клеток стоят на
                                        одной горизонтали независимо от того, пустой это
                                        бокс или текстовый прочерк. */
}

.ds-tt-colhead {
  font-size: 10px;
  color: var(--graphite-300);
  text-align: center;
  padding-bottom: 2px;
}

.ds-tt-matrix > span:has(.ds-tt-status) {
  /* Ячейка имени темы — риска + имя. :has()-хук вместо третьего класса
     (прецедент ds-utilities.css :has(> svg.ds-sparkline), §7.3) — атомы
     .ds-tt-matrix/.ds-tt-status уже объявлены, отдельного класса для самой
     ячейки имени не заводится. Перенос, а не усечение (§11.39 «Атомы»:
     тултип существует, чтобы показать полное) — намеренно без
     white-space/ellipsis, в отличие от ячейки. */
  display: flex;
  align-items: center;
  gap: 6px;
  min-width: 0;
}

.ds-tt-matrix > span:not(:has(.ds-tt-status)) {
  /* Клетки знаков и заголовки колонок — центр колонки (§11.6 «по центру
     колонки знаков»). .ds-tt-colhead уже центрирует свой текст отдельным
     правилом выше — пересечение безвредно (то же значение). */
  text-align: center;
}

.ds-tt-matrix > .ds-role-marker {
  /* 10px точка/тире в 34px колонке — центрируется сам бокс (justify-self),
     не текст внутри: ширина бокса задана явно, text-align на нём самом
     себя не центрует. */
  justify-self: center;
}

.ds-tt-status {
  /* Риска-статус темы (правила 1-4 CONTRACT PUM-1263, полярность
     инвертирована относительно исходной PUM-1261) — 2×11px, border-radius
     1px; приём заимствован у полоски §11.10, новым атомом-паттерном не
     является. */
  flex: 0 0 2px;
  width: 2px;
  height: 11px;
  border-radius: 1px;
  background: transparent;          /* фоновая / без резолвленного статуса — слот пуст */
}

.ds-tt-status--target {
  background: var(--graphite-300);  /* целевая — сплошная (тон не изменился, правило 5) */
}

/* ─────────── §11.63 Records table-caption — счётчик и легенда над таблицей
   (PUM-803; PUM-1481 окна B/C) ───────────
   Подпись таблицы, не бар контролов: слева утверждение о выдаче (счётчик),
   справа приписка о состоянии AI-колонок (легенда), контролов не несёт ни
   одного. Тогглы «Аудио» (PUM-1427, ушла в запрос) и «AI-анализ» (окно B,
   снята с экрана целиком на обоих контурах) сняты раньше — их атомы
   `.ds-client-toggles`/`.ds-client-toggle` убраны следом за ними, потеряв
   последнего потребителя. Окно C переименовало сам атом — `.ds-table-bar`
   → `.ds-table-caption` (§12.3 «Снято»: старое имя врало о содержимом,
   контролов в полосе не осталось) — и перенесло значения дословно из
   канонического блока §11.63 `docs/design-system.md`: `align-items:
   baseline` вместо прежнего `center` (оба края текстовые и разного кегля —
   выравнивание по центру читалось бы двумя объектами, не одной строкой) и
   добавленный `gap: var(--space-4)`. `margin-bottom: var(--space-3)`
   тогда остался неизменным по значению — заложник инварианта «ни один
   пиксель Банка не меняется» (§11.63 «Нижний отступ `--space-3` —
   заложник, а не замысел»), не решение того окна.

   PUM-1505 (окно 3): инвариант снят решением CEO 09.09.2026 в рамках
   хрома, а не наступлением события (§11.63, «условие-заложник снято
   решением, а не наступлением события»), и `margin-bottom` уходит на
   `--space-2` вместе с парной величиной `.ds-filter-phrase` (§11.61, этот
   же файл выше) — несущая величина пары (разница в 4px) и её довод не
   повторяются здесь, только адрес. Правило показа «подписи нет, пока нет
   числа» и природа легенды — там же в §11.63. Действие над всей выдачей
   следует сразу за счётчиком у левого края (PUM-1829). */

.ds-table-caption {
  display: flex;
  align-items: baseline;
  justify-content: flex-start;
  gap: var(--space-4);
  padding: 0 var(--space-1);
  margin-bottom: var(--space-2);
}

.ds-table-caption .ds-btn-link {
  margin: calc(-1 * var(--space-2)) 0 calc(-1 * var(--space-2)) calc(-1 * var(--space-3));
}

.ds-table-count {
  font-size: var(--fs-label);
  color: var(--text-muted);
  font-variant-numeric: tabular-nums;
}

/* Модификатор: применяется ВМЕСТЕ с .ds-input (class="ds-input ds-input--compact") —
   несёт только height/padding/font-size; рамка/фон/focus-halo от базового .ds-input.
   Живой потребитель — пара дат периода (period_presets.js, §11.62 docs/design-system.md
   «Пара дат — не часть сегментов, но одной с ними высоты»): 30px совпадает с
   высотой .ds-seg, период читается одной группой, хотя состоит из двух объектов.
   .ds-select--compact, которому этот атом был зеркалом (высота компактных селектов
   бара карточки сотрудника), снят §12.3 — последний потребитель переехал на фразу
   §11.61, компактного вида поля в продукте не осталось (PUM-1485 окно B). Без
   min-width: text-input флексит по контенту, в отличие от select. PUM-973. */
.ds-input--compact {
  height: 30px;
  padding: var(--space-1) var(--space-2);
  font-size: var(--fs-label);
}

/* Общая компактная форма числовых полей калибровки (§11.21, PUM-1629).
   Остальные свойства полностью наследуются от .ds-input. */
.ds-input--number {
  padding-inline: var(--space-1);
}

/* ─────────── §11.41 Calibration scatter — атомы (PUM-859) ─────────────────────
   Каркас scatter-графика и стили точек-категорий.
   Палитра точек — единая fill-палитра --cat-* (tokens.css; §11.5).
   Нейтральная точка (NULL-состояние, пороги не заданы) — var(--graphite-300).
   Линии порогов в статическом рендере [3b] — var(--graphite-500) 1px;
   draggable-состояния hover/active — [3c]. */

.cal-scatter {
  display: block;
  background: var(--surface);
  overflow: visible;
}

/* Hairline-оси X и Y (1px, border-цвет) */
.cal-axis {
  stroke: var(--border);
  stroke-width: 1;
  fill: none;
}

/* Тик-метки осей (§11.41 — var(--fs-th)) */
.cal-tick-label {
  font-family: var(--font-sans);
  font-size: var(--fs-th);
  fill: var(--text-muted);
  font-variant-numeric: tabular-nums;
}

/* Подписи осей X/Y (§11.41 — 11-12px muted) */
.cal-axis-label {
  font-family: var(--font-sans);
  font-size: var(--fs-microcopy);
  fill: var(--text-muted);
}

.cal-scatter-overflow-sep {
  stroke: var(--border-strong);
  stroke-dasharray: 3 3;
}

.cal-scatter-overflow-lab {
  font-family: var(--font-sans);
  font-size: var(--fs-microcopy);
  fill: var(--text-subtle);
}

/* Линии порогов — статический рендер [3b] (graphite-500, 1px).
   Draggable-состояния hover/active (graphite-700 / primary) — [3c].
   PUM-945 A1: в покое — точечный пунктир (отступают в «леса»);
   hover/active — solid 1.5 (firming-up при захвате) — без изменений. */
.cal-thr-line {
  stroke: var(--graphite-500);
  stroke-width: 1;
  stroke-dasharray: 2 4;
  stroke-linecap: round;
  fill: none;
}

/* Точка-сотрудник: r=4, paper-щель stroke 1px */
.cal-pt,
.cal-point-exit--point {
  fill: var(--graphite-300);  /* neutral — NULL-состояние и fallback */
  stroke: var(--surface);
  stroke-width: 1;
  cursor: default;
}

/* Смена основания: только на время движения; правка цели/порогов мгновенна. */
svg.cal-scatter--moving .cal-pt,
svg.cal-scatter--moving .cal-pt-own,
svg.cal-scatter--moving line[data-ref],
svg.cal-scatter--moving g[data-ref-marker],
svg.cal-scatter--moving .cal-point-exit {
  transition: transform var(--dur-slow) var(--ease-standard),
              opacity var(--dur-base) var(--ease-standard),
              fill var(--dur-fast) var(--ease-standard);
}

/* 6 модификаторов по category → fill --cat-* */
.cal-pt--leader   { fill: var(--cat-leader); }
.cal-pt--prospect { fill: var(--cat-prospect); }
.cal-pt--norm     { fill: var(--cat-norm); }
.cal-pt--develop  { fill: var(--cat-develop); }
.cal-pt--focus    { fill: var(--cat-focus); }
.cal-pt--insuf    { fill: var(--cat-insuf); }
/* Кольцо своей точки: радиус задаёт разметка, указатель не перехватывает — под кольцом зона захвата линии порога. */
.cal-pt-own,
.cal-point-exit--own {
  fill: none;
  stroke: var(--graphite-700);
  stroke-width: 1;
  pointer-events: none;
}

.cal-pt-origin { fill: none; stroke: var(--graphite-500); stroke-width: 1.25; pointer-events: none; }
.cal-pt-origin-path { stroke: var(--graphite-500); stroke-width: 1; stroke-dasharray: 2 2; pointer-events: none; }

/* ── §11.41 Шапка вида — светлая карточка (PUM-945 C) ──────────────────────
   Прецедент: подпись таблицы «Записи» §11.63 (.ds-table-caption в карточке).
   Scoped-override: .ds-card дефолт padding 24px → заменяем на 16/20px.
   margin-bottom — шов до ряда блоков; убран прежний border-bottom-only.
   align-items: flex-start — строки под контролами не смещают их общую линию. */
.calib-header-row {
  position: sticky;
  top: var(--exp-player-height, 0px);
  z-index: var(--z-sticky);
  padding: var(--space-4) var(--space-5);
  margin-bottom: var(--space-5);
  display: flex;
  flex-wrap: wrap;   /* на 1280 при длинном ряду периодов правая группа уходит строкой ниже, а не в горизонтальную прокрутку */
  align-items: flex-start;
  gap: var(--space-5);
}

.cal-head-lens { display: flex; flex-direction: column; max-width: 400px; }
.cal-head-lens-row { display: flex; align-items: flex-end; gap: var(--space-5); }
.cal-head-why { font-size: var(--fs-microcopy); line-height: 16px; color: var(--text-subtle); padding-top: var(--space-1); }

/* ── §11.41 Калибровка: две строки по шагам работы ───────────────────── */
.cal-row {
  display: grid;
  grid-template-columns: 520px minmax(0, 1fr);
  gap: var(--space-5);
  align-items: stretch;
  margin-bottom: var(--space-6);
}
.cal-row > * { min-width: 0; }
.cal-row--roles { align-items: start; } /* Многоролевая строка целей: карточки не тянутся. */
.cal-pult { padding: var(--space-5); }
.cal-row--roles > .cal-pult-col { position: sticky; display: flex; flex-direction: column; gap: var(--space-4);
             top: calc(var(--exp-player-height, 0px) + var(--cal-head-h, 0px) + var(--space-4)); }   /* сводка ролей и счёт едут вместе; под плеером и шапкой, §7.4; --cal-head-h пишет view.js */
.cal-roles {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: var(--space-4);
}
.cal-roles-summary      { flex: none; display: grid; align-items: center;
                          grid-template-columns: max-content minmax(0, 1fr) minmax(0, 1fr);
                          column-gap: var(--space-6); row-gap: var(--space-1);
                          padding: var(--space-2) var(--space-5) var(--space-3); }   /* + .ds-card; боковые поля — как у счёта; первая клетка строки подписей пустая */
.cal-roles-summary-h    { font-size: var(--fs-microcopy); line-height: 1.3; font-weight: var(--fw-th);
                          color: var(--text-subtle); }                              /* как подписи колонок счёта */
.cal-roles-summary-role { display: inline-flex; align-items: center; gap: var(--space-2); height: 20px;
                          font-size: var(--fs-label); line-height: 1.2; color: var(--text); }   /* как роль в строке счёта */
.cal-roles-summary .ds-below-bar     { flex: 1 1 auto; width: auto; height: 8px; }   /* дорожка — на ширину колонки */
.cal-roles-summary .ds-below-count-n { min-width: 7ch; line-height: 1.2; }   /* 7 знаков: шестизначное число не укорачивает дорожку своей строки */
.cal-roles-summary-occasions    .ds-below-bar-fill { background: var(--graphite-300); }
.cal-roles-summary-realizations .ds-below-bar-fill { background: var(--graphite-700); }
.cal-roles-summary .ds-below-count:not(.ds-below-count--zero) .ds-below-bar-fill { min-width: 2px; }

.cal-role-block { padding: var(--space-3) var(--space-4); }
.cal-role-title {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  height: 30px;
  font-size: var(--fs-label);
  font-weight: var(--fw-th);
  color: var(--text);
}
.cal-role-title .ds-sparkline,
.cal-role-title .ds-sparkline-empty {
  margin-left: auto;
  flex-shrink: 0;
}
.cal-role-readout {
  font-size: var(--fs-label);
  color: var(--text-muted);
  margin-bottom: var(--space-3);
  font-variant-numeric: tabular-nums;
}
.cal-role-readout b { color: var(--text); font-weight: var(--fw-th); }
.cal-role-chart {
  margin-top: var(--space-2);
  padding-top: var(--space-2);
  border-top: 1px solid var(--border-muted);
}
.cal-cloud-readout {
  display: flex;
  justify-content: space-between;
  gap: var(--space-3);
}
.cal-cloud-caption {
  font-size: var(--fs-microcopy);
  color: var(--text-subtle);
  text-align: right;
}

.calib-header-actions {
  margin-left: auto;
  padding-top: calc(var(--fs-th) * var(--lh-body) + var(--space-1)); /* Высота .ds-filter-label и зазор группы поля. */
  display: flex;
  align-items: center;
  gap: var(--space-4);
}
.calib-header-actions .ds-btn-primary { height: 38px; }
.calib-header-actions .ds-btn-link { height: 38px; }
.calib-save-line {
  flex-basis: 100%;
  display: flex;
  justify-content: flex-end;
  margin-top: calc(var(--space-2) - var(--space-5));
}

#calib-goals-pult > #calib-categories {   /* однорольная: пороги под таблицей в той же карточке */
  border-top: 1px solid var(--border-muted);
  padding-top: var(--space-4);
  margin-top: var(--space-4);
}

.cal-cloud-card {
  padding: var(--space-3) var(--space-4);
  overflow: hidden;
}
#calib-scatter-placeholder {
  height: 480px;
  min-width: 0;
  display: flex;
  align-items: stretch;
}
.cal-cloud-card--single #calib-scatter-placeholder { height: 500px; }

.cal-plan-head,
.cal-zone-map-header {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--space-3);
  margin-bottom: var(--space-3);
  font-size: var(--fs-body);
  font-weight: var(--fw-th);
  color: var(--text);
}
.cal-zone-map-aside {
  font-size: var(--fs-microcopy);
  font-weight: var(--fw-label);
  color: var(--text-subtle);
}
/* ── §11.41 Счёт «Цели и план» (PUM-1637) ────────────────────────────────── */

/* Блок ниже — CSS §11.41 «Уровни и состояния» как записан в design-system. */
.cal-ledger-row {
  display: grid;
  grid-template-columns: 118px 56px 22px 76px minmax(0, 1fr) 72px;
  align-items: center;
  min-height: 42px;
  font-size: var(--fs-label);
  border-bottom: 1px solid var(--border-muted);
  font-variant-numeric: tabular-nums;
}
.cal-ledger-cols {
  font-size: var(--fs-microcopy);
  font-weight: var(--fw-th);
  color: var(--text-subtle);
  border-bottom: 1px solid var(--border);
}
.cal-ledger-v { text-align: right; font-family: var(--font-mono); }
.cal-ledger-zone {
  font-size: var(--fs-th);
  font-weight: 600;
  padding: var(--space-3) 0 var(--space-1);
}
.cal-ledger-zone span { color: var(--text-muted); font-weight: var(--fw-body); }
.cal-ledger-out {
  background: var(--surface-2);
  border-radius: var(--radius-sm);
  margin: var(--space-3) calc(-1 * var(--space-3)) 0;
  padding: 0 var(--space-3);
}
.cal-ledger-note {
  padding-bottom: var(--space-2);
  border-bottom: 1px solid var(--border-muted);
}
.cal-ledger-total { border-top: 1px solid var(--border); border-bottom: 0; font-weight: 600; }
.cal-ledger-total .cal-ledger-v { font-size: var(--fs-severity); }

/* Выравнивание и состояния по эталону, которых блок §11.41 не задаёт: только
   `.cal-ledger-*`, существующие токены. Слот твёрдости PUM-1637: в §11.41 вносит CTO. */
.cal-ledger-row > * { white-space: nowrap; }
.cal-ledger-cols { min-height: 0; padding-bottom: var(--space-1); }
.cal-ledger-colv { grid-column: 5 / span 2; text-align: right; }
.cal-ledger-f { text-align: right; }
.cal-ledger-a { text-align: center; color: var(--text-subtle); }
.cal-ledger-nm { display: flex; align-items: center; gap: var(--space-2); }
.cal-ledger-t { display: flex; align-items: center; }
.cal-ledger-t .ds-input { width: 56px; text-align: right; }
.cal-ledger-u { margin-left: var(--space-1); color: var(--text-muted); }
.cal-ledger-ro { font-weight: var(--fw-table-num); }
.cal-ledger-g { text-align: right; color: var(--text-muted); font-size: var(--fs-th); padding-right: var(--space-3); }
.cal-ledger-row--noted { border-bottom: 0; min-height: 40px; }
.cal-ledger-was { min-height: 38px; }
.cal-ledger-was > :first-child { color: var(--text-muted); }
.cal-ledger-amb { min-height: 38px; border-bottom: 0; font-weight: var(--fw-table-num); }
.cal-ledger-amb .cal-ledger-f,
.cal-ledger-amb .cal-ledger-v { font-weight: 600; }
.cal-ledger-amb .cal-ledger-g { color: var(--text); font-weight: 600; font-size: var(--fs-label); }
.cal-ledger-fact { min-height: 38px; border-bottom: 0; font-weight: var(--fw-table-num); }
.cal-ledger-fact .cal-ledger-f { font-weight: 600; }
.cal-ledger-fact-words {
  grid-column: 3 / 6;
  text-align: right;
  color: var(--text-muted);
  font-size: var(--fs-th);
  padding-right: var(--space-3);
}
.cal-ledger-per { white-space: normal; color: var(--text-muted); font-weight: var(--fw-body); font-size: var(--fs-th); }
.cal-ledger-per b { font-weight: 600; color: var(--text); }
.cal-ledger-per-rest { white-space: nowrap; }   /* перенос — после «Амбиция», не внутри «по новой базе» (слот PUM-1637) */
.cal-ledger-per em { display: block; font-style: normal; font-size: var(--fs-microcopy); color: var(--text-subtle); }
.cal-ledger-out .cal-ledger-zone { padding-top: var(--space-2); }
.cal-ledger-out .cal-ledger-row,
.cal-ledger-out .cal-ledger-note { border-bottom: 0; }
.cal-ledger-v--muted { color: var(--text-muted); }
.cal-ledger-total { margin-top: var(--space-3); }
.cal-ledger-foot { margin-top: var(--space-2); }

.cal-fulfil            { display: block; overflow: visible; margin: 0 0 var(--space-1); }
.cal-fulfil-axis       { stroke: var(--border); stroke-width: 1; }
.cal-fulfil-base       { fill: var(--border-strong); }
.cal-fulfil-base-glyph { fill: var(--graphite-700); }
.cal-fulfil-target     { stroke: var(--primary); stroke-width: 1.5; }
.cal-fulfil-target-glyph { fill: var(--primary); }
.cal-fulfil-fact       { fill: var(--graphite-700); stroke: var(--surface); stroke-width: 1.5; }
.cal-fulfil-over       { fill: var(--text-muted); font-size: var(--fs-microcopy); }

/* ── §11.41 Карта категорий и распределения (PUM-1629) ───────────────────── */

.cal-zone-map {
  width: 100%;
  border-collapse: separate;
  border-spacing: 0;
  table-layout: fixed;
  font-variant-numeric: tabular-nums;
}

.cal-zone-map col.cal-zone-map-row-head { width: 82px; }
.cal-zone-map col.cal-zone-map-mid { width: 72px; }

.cal-zone-map th {
  padding: 0 var(--space-2) var(--space-1);
  font-size: var(--fs-microcopy);
  font-weight: var(--fw-th);
  color: var(--text-muted);
  text-align: left;
  white-space: nowrap;
  vertical-align: bottom;
}

.cal-zone-map tbody th {
  padding: 0 var(--space-2) 0 0;
  vertical-align: middle;
  white-space: normal;
}

.cal-zone-map td {
  height: 38px;
  padding: 0 var(--space-2);
  border-top: 1px solid var(--border);
  border-left: 1px solid var(--border);
  white-space: nowrap;
}

.cal-zone-map td:last-child { border-right: 1px solid var(--border); }
.cal-zone-map tbody tr:last-child td { border-bottom: 1px solid var(--border); }

.cal-zone-map .ds-input {
  width: 60px;
  text-align: right;
}

.cal-zone-map-category {
  display: flex;
  align-items: center;
  gap: var(--space-2);
}

.cal-zone-map-dot {
  width: 9px;
  height: 9px;
  border-radius: 50%;
  flex: none;
}

.cal-zone-map-name {
  min-width: 0;
  color: var(--text);
  font-size: var(--fs-label);
}

.cal-zone-map-count {
  margin-left: auto;
  color: var(--text);
  font-family: var(--font-mono);
  font-size: var(--fs-mono);
  font-weight: 500;
  font-variant-numeric: tabular-nums;
}

.cal-zone-map-norm { vertical-align: middle; }
.cal-zone-map-norm .cal-zone-map-category {
  display: grid;
  grid-template-columns: 9px minmax(0, 1fr);
  column-gap: var(--space-2);
  align-items: center;
}
.cal-zone-map-norm .cal-zone-map-dot { grid-row: 1 / span 2; }
.cal-zone-map-norm .cal-zone-map-count {
  grid-column: 2;
  margin-left: 0;
}

.cal-zone-map-range { color: var(--text-subtle); font-weight: var(--fw-label); }
.cal-zone-map-baseline { color: var(--text-subtle); font-size: var(--fs-microcopy); }

.cal-zone-map--unavailable .cal-zone-map-name,
.cal-zone-map--unavailable .cal-zone-map-count { color: var(--text-subtle); }
.cal-zone-map--unavailable .cal-zone-map-dot { opacity: .35; }

.cal-category-bars {
  display: grid;
  grid-template-columns: minmax(0, 1fr) 160px max-content;
  column-gap: var(--space-2);
  row-gap: var(--space-3);
  margin-top: var(--space-4);
}

.cal-category-bars > .cal-zone-map-baseline { grid-column: 1 / -1; }

.cal-category-bar-row {
  display: grid;
  grid-template-columns: subgrid;
  grid-column: 1 / -1;
  align-items: center;
}

.cal-category-bar-label {
  min-width: 0;
  overflow: hidden;
  color: var(--text-muted);
  font-size: var(--fs-microcopy);
  text-overflow: ellipsis;
  white-space: nowrap;
}

.cal-category-bar-count {
  min-width: 2ch;
  color: var(--text);
  font-family: var(--font-mono);
  font-size: var(--fs-mono);
  font-weight: 500;
  text-align: right;
  font-variant-numeric: tabular-nums;
}

/* ── §11.41 draggable-пороги — hover/active состояния (PUM-860 срез 2a) ──────
   Линия порога: default graphite-500 1px (базовый .cal-thr-line выше).
   hover → graphite-700 1.5px + resize-cursor.
   active/drag → primary (burgundy) 1.5px. */

.cal-thr-line--hover {
  stroke: var(--graphite-700);
  stroke-width: 1.5;
}

.cal-thr-line--active {
  stroke: var(--primary);
  stroke-width: 1.5;
}

/* Hit-area: широкая прозрачная зона поверх линии порога.
   pointer-events: stroke → реагирует только на попадание в stroke.
   Прозрачный stroke + большой stroke-width = широкая интерактивная зона. */
.cal-thr-hit {
  stroke: transparent;
  fill: none;
  pointer-events: stroke;
}

/* Pill значения при drag: reuse токены .ds-tooltip (§11.6).
   Graphite-700 bg, inverted paper text, Plex Mono 11px. */
.cal-thr-pill-text {
  font-family: var(--font-mono);
  font-size: var(--fs-microcopy); /* 11px */
  fill: var(--surface);           /* paper/inverted */
  dominant-baseline: middle;
  font-variant-numeric: tabular-nums;
  pointer-events: none;
  /* Текстовый pill без rect-фона — максимально лёгкий.
     Если нужен фон — добавить <rect> в _updatePill. */
}

/* ── §11.41 Read-only reference-линии основание/цель (PUM-861 [3d] часть 1/2) ─────
 * Две вертикали во всю высоту plot (plot.bottom → plot.top).
 * Не draggable — только read-only визуализация.
 * Позиция = функция текущих весов → едут при смене весов вместе с точками.
 * При drag порогов (updateScatterThresholds) линии НЕ трогаются. */

/* Линия основания: graphite-700 1px solid (PUM-945 A2: было 1.25 → 1px, тоньше) */
.cal-basis-line {
  stroke: var(--graphite-700);
  stroke-width: 1;
  fill: none;
  pointer-events: none;
}

.cal-month-line { stroke: var(--graphite-700); stroke-width: 1.5; }
.cal-month-glyph { fill: var(--graphite-700); stroke: var(--surface); stroke-width: 1.5; }

/* Цель-линия: primary (burgundy) 1.5px solid (PUM-945 A3: было 1.25 dashed → 1.5 solid) */
.cal-target-line {
  stroke: var(--primary);
  stroke-width: 1.5;
  fill: none;
  pointer-events: none;
}

/* Тихие fallback-подписи reference-линий (10px, text-subtle).
   §13b-fallback и прецедент D3: не удалять. */
.cal-ref-label {
  font-family: var(--font-sans);
  font-size: 10px;
  fill: var(--text-subtle);
  font-variant-numeric: tabular-nums;
  pointer-events: none;
}

/* PUM-945 A4 — базовые глифы reference-линий основание/цель */
.cal-basis-glyph {
  fill: var(--graphite-700);
  pointer-events: none;
}

.cal-target-glyph {
  fill: var(--primary);
  pointer-events: none;
}

/* PUM-945 A4 — tag-подпись (бумажный чип + текст) */
.cal-ref-tag-chip {
  fill: var(--surface);
  stroke: var(--border-muted);
  pointer-events: none;
}

.cal-ref-tag-text {
  font-family: var(--font-sans);
  font-size: var(--fs-microcopy);
  font-weight: 500;
  font-variant-numeric: tabular-nums;
  pointer-events: none;
}

/* ── §11.41 Слот «Сохранить» в шапке калибровки ──────────────────────────── */

.cal-save-slot {
  display: flex;
}

/* ── §11.41 Per-role блоки диагностики (PUM-813) ──────────────────────────── */

/* cal-scatter--role: ролевой scatter внутри блока роли.
   PUM-942: px-размеры ставит JS (_fitRoleScatters, dims из измеренного слота).
   display:block — SVG как block-element (без inline-baseline gap). */
.cal-scatter--role {
  display: block;
}

/* Тихая строка счёта «Цели и план» (§11.41): причина под строкой роли и под амбицией,
   пометка некалендарного периода. Переносится: причины и пометка длиннее колонки. */
.cal-kpi-sub-hint {
  font-size: var(--fs-microcopy);
  color: var(--text-subtle);
}

/* ── §11.48 Отложенные — defer-маркер / кнопка / разделитель / заметка (PUM-941) ───────────────
   Приватная рабочая пометка. Семантика vs банк (§11.13): отложенное = stroke + graphite
   (нейтрально), банк = filled + pos-700 (положительный сигнал). Различие формой+заливкой+тоном
   (§9: hue ≤16px ненадёжен); приватность кодируется регистром+группировкой, не цветом. */

/* Маркер «отложен» в строке — зеркало .ds-bookmark-badge (§11.13), но stroke + graphite.
   Позиция в .ds-date-row (§11.14) ПОСЛЕ .ds-bookmark-badge: [дата][bookmark][pin]. */
.ds-defer-badge {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 16px;
  height: 16px;
  color: var(--graphite-500);   /* нейтральный — приватная пометка, не сигнал */
  flex-shrink: 0;
}
.ds-defer-badge svg { width: 16px; height: 16px; }

/* Кнопка «Отложить»↔«Отложено» в action-slot панели (§11.16) — Tier-3 neutral toggle
   (§10 taxonomy: outline-graphite, НЕ destructive §11.19). icon+text, не icon-only. */
.ds-btn-defer {
  background: var(--surface);
  color: var(--graphite-700);
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-sm);
  padding: 6px 12px;
  font-size: var(--fs-label);
  font-weight: 500;
  display: inline-flex;
  align-items: center;
  gap: 6px;
  cursor: pointer;
}
.ds-btn-defer svg { color: var(--graphite-500); width: 1em; height: 1em; }
.ds-btn-defer[aria-pressed="true"] {   /* «Отложено» — active toggle-state */
  background: var(--surface-2);
  border-color: var(--graphite-300);
}
.ds-btn-defer:focus-visible {
  outline: none;
  box-shadow: var(--focus-ring-graphite);
}

/* Hairline-разделитель в .ds-panel-action-slot-right. Группировка, не цвет
   (Gestalt-proximity). Состав ряда и смысл разделителя держит §11.58-F —
   единственное определение; здесь не повторяются намеренно (прежняя граница
   «приватное │ публично-системное» снята PUM-1455, копия разошлась бы молча). */
.ds-panel-act-divider {
  width: 1px;
  height: 18px;
  background: var(--border);
  margin: 0 var(--space-1);
  flex-shrink: 0;
}

/* Зона заметки в стопке панели чека — между controls (§11.16) и transcript (§11.18).
   Раскрывается при откладывании (read⇄edit). Hairline-separator сверху, gutter как transcript.
   В колонке панели записи линии и полей нет — правило у .ds-panel-record (§11.70). */
.ds-panel-note {
  padding: var(--space-3) var(--space-4);
  border-top: 1px solid var(--border-muted);
}

/* .ds-defer-note-row/-read/-read .ds-defer-note-pin/-read .ds-defer-note-edit/
   -add сняты целиком (PUM-1416 окно 1, §11.48-D «Снимается вместе с прежней
   формой»): под-строка заметки вкладки «Отложенные» снесена — заметка
   переехала в колонку таблицы (.ds-note-cell, note_cell_pure.js) и в
   .ds-panel-note (§11.48-E, панель). Последний потребитель этих шести
   атомов (.ds-defer-note-text не имела собственного правила) ушёл вместе с
   deferred.js:renderDeferredNoteSubrow/openNoteEdit/updateNoteSubrow —
   носитель без потребителя не остаётся. */

/* ── §11.58 Кнопка с выбором (`.ds-split-btn` / `-main` / `-toggle` / `-menu` /
   `-menu-item`) — план nvr-media-export, задача 2 (PUM-1455, окно 2) ─────────
   Атом «действие со стрелкой второго варианта»: одна рамка, разрезанная
   надвое, с необязательным сегментом стрелки и меню под ним. Носитель ни
   про какие камеры/чеки не знает — метки/глиф/title/меню приходят строками
   от вызывающего (static/explorer/split_btn.js). Tier 3-neutral-split (§10
   taxonomy) — outline-graphite, НЕ Tier-2 burgundy (бордовый в этом ряду
   занят кнопкой «В банк»). Метрики `-main`/`-toggle` повторяют .ds-btn-defer
   (§11.48-C) построчно — ряд действий панели читается одной высотой только
   при общих метриках. Сегменты не несут СВОЕГО border/радиуса-как-контура —
   рамку целиком держит обёртка; border-radius на крайних сегментах ниже —
   это подгонка формы под уже скруглённую обёртку (иначе квадратный угол
   кнопки торчал бы из-под скруглённой линии рамки), не второй контур. */
.ds-split-btn {
  display: inline-flex;
  align-items: stretch;
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-sm);
}

.ds-split-btn-main,
.ds-split-btn-toggle {
  background: var(--surface);
  color: var(--graphite-700);
  border: none;
  font-size: var(--fs-label);
  font-weight: 500;
  display: inline-flex;
  align-items: center;
  cursor: pointer;
}
.ds-split-btn-main svg,
.ds-split-btn-toggle svg { color: var(--graphite-500); width: 1em; height: 1em; }

.ds-split-btn-main {
  gap: 6px;
  padding: 6px 12px;
  border-radius: var(--radius-sm) 0 0 var(--radius-sm);
  /* §11.58-C: min-width под самую длинную форму метки сегодня — «Фото 6/6»
     (шестиканальных аптек две, больше каналов в сети нет — замер 01.09.2026,
     PUM-1455 § REFERENCES; недатированный снимок здесь разошёлся бы молча,
     ADR-083). Величина выведена
     из содержимого: 8ch = кегль восьмисимвольной метки, 1em = глиф, 6px/24px
     = gap/паддинг этого же правила выше — не магический пиксель. */
  min-width: calc(8ch + 1em + 6px + 24px);
}
.ds-split-btn-main:only-child {
  /* menuItems: [] — сегмента стрелки в разметке нет вовсе (§11.58-B
     «Выбора нет — стрелки нет»), атом вырождается в обычную кнопку с тем
     же контуром: -main получает оба скругления обёртки. */
  border-radius: var(--radius-sm);
}

.ds-split-btn-toggle {
  justify-content: center;
  width: 24px;   /* design-spec exception §6.2 — ширина сегмента предписана §11.58-A */
  border-left: 1px solid var(--border);
  border-radius: 0 var(--radius-sm) var(--radius-sm) 0;
}

.ds-split-btn-main:hover,
.ds-split-btn-toggle:hover {
  /* Посегментно — на наведённой половине, не на всём атоме (§11.58-B). */
  background: var(--surface-2);
}
.ds-split-btn--open .ds-split-btn-toggle {
  /* Пока меню раскрыто — состояние стрелки, не всего атома (§11.58-B).
     Стрелка НЕ поворачивается (§10.4): открытость выражена class-swap'ом
     на обёртке (.ds-split-btn--open), не transform: rotate на глифе. */
  background: var(--surface-2);
}

.ds-split-btn-main:focus-visible,
.ds-split-btn-toggle:focus-visible {
  outline: none;
  box-shadow: var(--focus-ring-graphite);   /* halo §8.1 — компактный контрол */
}

.ds-split-btn-main:disabled,
.ds-split-btn-toggle:disabled {
  /* Канон §8 на ОБА сегмента разом — полу-доступного вида у атома нет;
     busy своего вида не имеет, это тот же disabled (§11.58-C). */
  background: var(--disabled-bg);
  color: var(--disabled-text);
  cursor: not-allowed;
}
.ds-split-btn-main:disabled svg,
.ds-split-btn-toggle:disabled svg { color: var(--disabled-text); }

/* Меню — §11.58-D. Всегда смонтировано (когда есть menuItems), закрыто =
   opacity 0 + pointer-events none — не display:none, иначе entrance-переход
   не сыграл бы на первое открытие. Появление — канон §10.5.

   position: fixed, БЕЗ top/right здесь — их и min-width на время открытия
   вычисляет и ставит инлайн split_btn.js::_positionMenu, по прямоугольнику
   сегмента стрелки в координатах ОКНА (getBoundingClientRect), не потоком
   внутри обёртки: панель, в которой живёт атом, отсекает своё содержимое
   (`.ds-panel { overflow: clip }` — рамка панели, не трогаем), и у
   короткого чека меню, спозиционированное в потоке, уходило за нижнюю
   кромку и становилось недостижимым ни указателем, ни клавиатурой (замер
   01.09.2026 — PUM-1455 QA-фикс, §11.58-D «Меню позиционируется от окна, а
   не от обёртки»). Вид сохраняется дословно — под атомом, по правому краю;
   отступ — var(--space-1), зеркалом в JS (split_btn.js::_MENU_GAP_PX,
   комментарий над константой). Портал на уровень document.body НЕ заводится
   (§11.58-D «НЕ делать») — тот же результат без выигрыша, менее прозрачным
   способом; узел остаётся ребёнком .ds-split-btn. */
.ds-split-btn-menu {
  position: fixed;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-overlay);
  z-index: var(--z-dropdown);
  padding: var(--space-1) 0;
  opacity: 0;
  pointer-events: none;
  transform: translateY(-6px);
  transition: opacity var(--dur-slow) var(--ease-out),
              transform var(--dur-slow) var(--ease-out);
}
.ds-split-btn--open > .ds-split-btn-menu {
  opacity: 1;
  pointer-events: auto;
  transform: translateY(0);
}
@media (prefers-reduced-motion: reduce) {
  .ds-split-btn-menu {
    /* Без translateY — только opacity, тот же паттерн, что .ds-modal (§10.5). */
    transform: none;
  }
}

.ds-split-btn-menu-item {
  display: block;
  width: 100%;
  text-align: left;
  background: none;
  border: none;
  padding: var(--space-2) var(--space-3);
  font-size: var(--fs-label);
  color: var(--text);
  white-space: nowrap;
  cursor: pointer;
}
.ds-split-btn-menu-item:hover {
  background: var(--surface-2);
}
.ds-split-btn-menu-item:focus-visible {
  outline: none;
  box-shadow: var(--focus-ring-graphite);
}

/* ── §11.59 Токен-контрол выбора — слово с меню (`.ds-fp-token` / `.ds-fp-menu`)
   — план records-filter-phrase, PUM-1457 окно 3a-1 (первый реальный
   потребитель — три оси фильтр-бара «Записей» с неменяющимся составом)
   ──────────────────────────────────────────────────────────────────────────
   PUM-1470 окно 3 из четырёх: для «Записей» условие переезда триггера с
   переходного вида (рамка/фон/высота сегодняшнего .ds-select) на КОНЕЧНЫЙ
   наступило — зона переехала на фразу, карточка-подложка снята окном 4
   того же плана (§11.59 «Видов два, и выбирает их зона, а не стадия
   проекта»: имя «переходный» историческое — вид родился как стадия и был
   снят вместе с этим переездом, но у продукта он не единственный; PUM-1477
   вводит ВТОРОЙ, сосуществующий вид — «поле» — постоянную форму без срока
   для зон, остающихся карточкой с подписями над контролами (первый
   потребитель — «Калибровка»); его CSS — отдельный блок ниже, сразу после
   конца этого). Блок .ds-fp-token/.ds-fp-token--set/.ds-fp-caret/
   .ds-fp-clear ниже — КОНЕЧНЫЙ вид, перенесён из §11.59 docs/design-
   system.md «Конечный вид триггера» ДОСЛОВНО, кроме ОДНОГО объявленного
   решения самих Implementation Notes (п.3, «носитель линии»): в брифе
   крестик нарисован ВНУТРИ кнопки и там же несёт border-bottom, но
   контракт §11.59 требует крестик СОСЕДОМ триггера (интерактивный узел
   внутри кнопки невалиден) — носитель общей линии поэтому переезжает на
   обёртку. Отсюда:
     - border-bottom (пунктир/сплошная) переехал с .ds-fp-token на
       .ds-fp-token-wrap, переключается по :has(.ds-fp-token--set) — тот
       же приём, что :has(> svg.ds-sparkline) выше в этом файле (§7.3);
       признак остаётся ОДИН (класс на кнопке), второго носителя не
       заведено. .ds-fp-token:hover теряет border-bottom-color и
       .ds-fp-token — третий член transition (border-color) по той же
       причине: кнопке больше нечего красить по этому свойству.
     - .ds-fp-token--set сверх canonical (color) несёт padding-right: 0 —
       без него зазор слово↔крестик читался бы 13px (8px padding кнопки +
       5px gap обёртки), а Implementation Notes требуют ровно 5px, флекс-
       гэпом обёртки — тем же числом, что gap label↔caret нейтрального
       состояния («чтобы заданное и нейтральное состояния читались
       одинаково, а не съезжали на пиксель при смене значения»).
     - .ds-fp-clear сверх canonical несёт margin-right: var(--space-2) —
       8px после крестика до конца линии, симметрично 8px padding-left
       кнопки перед словом («на эталоне после него 8px до конца линии»).
       Второго числа не заведено — та же переменная, что уже несёт padding
       кнопки, не отдельно подобранное значение.
   .ds-fp-token:focus { outline: none; } СНЯТА (PUM-1470, решение CEO
   04.09.2026). Строка приехала с переходным видом триггера PUM-1457 как
   подавление дефолтного ореола на клике мышью; замер на живой странице
   показал, что подавлять нечего: клик мышью даёт :focus без :focus-visible,
   и вычисленный outline-style у кнопки — none, что со строкой, что без неё.
   Проверено двумя независимыми способами — сегмент .ds-seg > button строки
   не несёт и ореола не получает, и снятие строки из обеих таблиц стилей
   (исходник плюс бандл) поведение не меняет. В каноническом блоке §11.59
   строки нет намеренно: заведомо мёртвое свойство в нём обесценило бы
   соседнее живое, которое лишь выглядит мёртвым (font-size глифов).
   Остальные семь селекторов с тем же подавлением — К-1470-3, не этот тикет.
   .ds-fp-clear:focus-visible — из canonical-блока §11.59, дословно. Строки
   там не было: гейт §1.5 эскалирован этим окном и закрыт правкой канона
   (решение CEO 04.09.2026). Токен graphite, а не бордовый `--focus-ring`
   таблицы §8.1, — отклонение объявлено в §11.59 доводом: §11.61 держит зону
   отбора без цвета-акцента ни в одном состоянии, и бордовый ореол сделал бы
   сброс оси ярче кнопки «Сформировать».
   Крестик .ds-fp-clear — СОСЕД кнопки внутри .ds-fp-token-wrap, не её
   ребёнок; рисуется/удаляется fp_token.js:_syncFpAffordance ТОЙ ЖЕ
   операцией, что пишет значение и слово (§11.59 «Контракт компонента») —
   единственный писатель класса .ds-fp-token--set и присутствия крестика/
   каретки, второго сравнения value/neutralValue по месту не заводит
   (isAxisSet, records/filter_phrase_state.js).
   Ширина триггера НЕ задаётся ничем — по содержимому, решение CEO 02.09
   (§11.59 «Ширина триггера... вводится сразу, она конечная»): короткие оси
   становятся заметно уже сегодняшних select — ожидаемый исход, не регресс.
   Меню (.ds-fp-menu*) — вид КОНЕЧНЫЙ, CSS дословно из §11.59, значения не
   менять. .ds-fp-menu-body/.ds-fp-menu-it--active — служебные метки поверх
   §11.59, ввёл этот атом (fp_token.js): скролл длинного меню и клавиатурная
   подсветка (тот же тон, что :hover ниже — одно состояние «пункт под
   указателем внимания», указатель просто разный).
   PUM-1457 окно 3d (дефект 2, пост-конверсионный визуальный smoke):
   .ds-fp-menu несёт `position: fixed` — единственное свойство блока, не
   пришедшее дословно из §11.59 (та секция про `position` молчит вовсе).
   fp_token.js:_positionMenu считает top/right в координатах ОКНА по
   getBoundingClientRect() кнопки и пишет их инлайн-стилем — без
   `position: fixed` эти координаты ни на что не действуют, меню остаётся в
   потоке `.ds-fp-token-wrap` внутри `.ds-filter-group`, и раскрытие меню
   меняет высоту группы (`.ds-filter-row { align-items: flex-end }`
   пересобирает ряд, триггер уезжает вверх — QA post-flight). Тот же приём
   и то же обоснование, что соседний `.ds-split-btn-menu` выше в этом файле
   (прецедент PUM-1455). Второе правило `position` рядом не заводить — это
   свойство существующего `.ds-fp-menu`, не новое. PUM-1470 окно 3:
   `.ds-fp-token-wrap` стал `inline-flex` (decision п.3 выше) — `_positionMenu`
   по-прежнему считает координаты от КНОПКИ (`btnEl`, не обёртки, сигнатура
   не изменилась), и раскрытие меню строку не перекладывает (проверено
   наведением на живой странице 04.09.2026 — машинного свидетеля у этого
   класса нет) — обёртка не стала вторым носителем координат меню.
   PUM-1457 (повторный QA п.2): `.ds-fp-menu-search` несёт `width` — второе
   отступление от «значения не менять» по той же логике, что `position`
   выше. Поле поиска несёт атом `.ds-input` (разметка — fp_token.js), а
   ширину под свой margin (`var(--space-3)` слева/справа, уже стоял здесь)
   `.ds-input` дать не может — он не знает контейнера. Второго числа не
   заводится: ширина выведена из УЖЕ стоящего здесь margin, не из
   отдельно подобранного значения. */
.ds-fp-token-wrap {
  position: relative;
  display: inline-flex;
  align-items: center;
  gap: 5px;
  border-bottom: 1px dashed var(--border-strong);
}
.ds-fp-token-wrap:has(.ds-fp-token--set) { border-bottom: 1px solid var(--text); }

.ds-fp-token {
  font-family: var(--font-sans); font-size: var(--fs-body); font-weight: var(--fw-th);
  display: inline-flex; align-items: center; gap: 5px;
  background: none; border: none; border-radius: var(--radius-sm);
  padding: 3px var(--space-2) 2px;
  color: var(--text-muted); cursor: pointer;
  transition: background var(--dur-fast) var(--ease-standard),
              color var(--dur-fast) var(--ease-standard);
}
.ds-fp-token:hover { background: var(--surface-2); color: var(--text); }
.ds-fp-token:focus-visible { outline: none; box-shadow: var(--focus-ring-graphite); }
.ds-fp-token--set { color: var(--text); padding-right: 0; }

.ds-fp-caret { font-size: 9px; color: var(--text-subtle); position: relative; top: -1px; }
.ds-fp-clear {
  position: relative;
  font-size: 10px; color: var(--text-subtle); background: none; border: none;
  cursor: pointer; padding: 0; margin-right: var(--space-2);
}
.ds-fp-clear:hover { color: var(--text); }
.ds-fp-clear:focus-visible { outline: none; box-shadow: var(--focus-ring-graphite); }
/* Зона попадания 24×24 (WCAG 2.2 §2.5.8, PUM-1485 окно B, долг задачи 3а
   плана records-filter-phrase-language, решение CEO 07.09) — прозрачное
   расширение цели псевдоэлементом, НЕ изменение бокса кнопки: рисунок
   (глиф, линия под словом, зазор 5px обёртки) не меняется ни на пиксель,
   меняется только площадь попадания. `position: relative` на .ds-fp-clear
   выше бокс не двигает (кнопка не смещена) — это и есть приём «не меняет
   бокс» из критерия приёмки. Величина фиксирована (24×24px суммарно), не
   выведена расчётом от ширины глифа: критерий «не меньше 24×24»
   выполняется тогда ПО ПОСТРОЕНИЮ, а не арифметикой поверх измерения,
   которое в тестовой среде перепроверить нечем (геометрии там нет —
   попадание курсором проверяет Tech Lead в браузере, post-flight §13b).
   Псевдоэлемент принадлежит кнопке — попадание по нему есть попадание по
   кнопке, pointer-events не тронут.

   PUM-1485 окно C — расширение СТАЛО НЕСИММЕТРИЧНЫМ (§11.59 docs/design-
   system.md «Зона попадания крестика сброса», правка CTO): симметричное
   центрирование (`left: 50%` + `transform: translate(-50%, -50%)`)
   растягивало 24px поровну от центра глифа — по 7px за каждый край
   (глиф 10×11.5px), при зазоре слово↔крестик всего 5px это накрывало
   последние ~2px слова курсором (найдено QA на приёмке PUM-1470, долг
   этого окна). Новая форма: `left: -5px` — влево РОВНО на зазор обёртки
   (`.ds-fp-token-wrap { gap: 5px }` выше в этом файле, то же число, не
   вторая величина под ту же работу), `width: 24px` держит суммарную
   ширину зоны фиксированной — вправо остаётся ВЕСЬ ОСТАТОК до 24px САМ
   СОБОЙ, без отдельного расчёта. По вертикали — прежние `top: 50%` +
   `transform: translateY(-50%)`, центрирование не тронуто (несимметрично
   только горизонтальное расширение, не вертикальное). Слово остаётся
   нажимаемым по всей своей ширине — проверяемая формулировка правила
   (§11.59), не «расширение симметрично/несимметрично» само по себе. */
.ds-fp-clear::after {
  content: '';
  position: absolute;
  top: 50%;
  left: -5px;
  width: 24px;
  height: 24px;
  transform: translateY(-50%);
}

.ds-fp-menu {
  position: fixed;
  min-width: 230px; background: var(--surface);
  border: 1px solid var(--border); border-radius: var(--radius-md);
  box-shadow: var(--shadow-overlay); padding: var(--space-1) 0;
  font-size: var(--fs-label); z-index: var(--z-dropdown);
}
.ds-fp-menu-grp { font-size: var(--fs-microcopy); color: var(--text-subtle); letter-spacing: .04em; padding: 7px var(--space-4) 3px; }
.ds-fp-menu-it  { padding: 5px var(--space-4); color: var(--text); cursor: pointer; display: flex; justify-content: space-between; gap: 14px; }
.ds-fp-menu-it:hover { background: var(--surface-2); }
.ds-fp-menu-it--sel { font-weight: var(--fw-th); }
.ds-fp-menu-it--sel::after { content: '✓'; color: var(--text-muted); font-size: 11px; }
.ds-fp-menu-search { margin: var(--space-1) var(--space-3) var(--space-2); width: calc(100% - var(--space-3) - var(--space-3)); }

.ds-fp-menu-body { max-height: 320px; overflow-y: auto; }  /* §11.46 «≈320px» — порог прокрутки списка */
.ds-fp-menu-it--active { background: var(--surface-2); }

/* Необязательные слоты строки и заголовка группы — §11.59, буллет «У строки два
   необязательных слота». Имена называют МЕСТО, не смысл: что кладётся внутрь,
   решает потребитель (первый — маркер готовности калибровки, §11.64).
   .ds-fp-menu-it-main обязателен при занятом слоте: строка разложена
   space-between, и без обёртки слот, подпись и галочка стали бы тремя
   равноправными детьми — подпись уехала бы в середину строки. */
.ds-fp-menu-it-main  { display: inline-flex; align-items: center; gap: var(--space-2); min-width: 0; }
.ds-fp-menu-it-slot  { width: 6px; flex: 0 0 6px; display: inline-flex; align-items: center; justify-content: center; }
.ds-fp-menu-grp-tail { color: var(--text-muted); font-variant-numeric: tabular-nums; }

/* ── §11.59 Токен-контрол выбора — вид ПОЛЯ (`.ds-fp-token-wrap--field`)
   — PUM-1477, второй вид триггера («Видов два, и выбирает их зона, а не
   стадия проекта», §11.59) ──────────────────────────────────────────────
   Постоянная форма для зон, остающихся карточкой с подписями над
   контролами (первый потребитель — «Калибровка»), а не переходная стадия
   конечного вида выше — у неё срока нет. Значения замерены на реальном
   рендере 04.09.2026 (pre-flight §13a, Chrome, живые tokens.css +
   ds-utilities.css); блок целиком — воспроизведение вида, который уже жил
   в дереве до PUM-1470 (снятое правило #records-filters .ds-period-btn,
   .ds-input, .ds-select, .ds-fp-token { height: 38px }), своих чисел в нём
   нет.

   Линия — принадлежность конечного вида; в виде поля её нет ни в одном
   состоянии. Пара селекторов обязательна: базовое
   .ds-fp-token-wrap:has(.ds-fp-token--set) специфичнее (0,2,0) одиночного
   .ds-fp-token-wrap--field (0,1,0) и иначе вернёт сплошную линию у
   заданного значения.

   font-weight: 400 — конечный вид несёт var(--fw-th) (500), потому что там
   вес и линия несут признак заданности вместо рамки; поле берёт вес
   .ds-select.

   height: 38px — не новое число: ровно оно жило в снятом PUM-1470 правиле
   (pre-flight tune PUM-803, «36 vs 38»). Замер: .ds-select рендерится
   38.5px, .ds-input — 36.5px; расхождение нативное, у <select> внутренний
   блок текста на 2px выше. При align-items: flex-end ряда нижние кромки
   совпадают, верхние расходятся на полпикселя — невидимо. Токен
   --control-height не вводится (§11.40, «не вводился и не вводится»).

   :hover { background: var(--surface) } — базовый вид красит фон на
   наведении, потому что там нет постоянной рамки и аффорданс
   подтверждается движением мыши. У поля рамка постоянная, .ds-select на
   наведении не красится — поле повторяет его.

   :focus-visible { border-color: var(--text-muted) } — box-shadow:
   var(--focus-ring-graphite) уже приходит от базового .ds-fp-token; поле
   добавляет ровно ту рамку, что .ds-select:focus-visible, второго halo не
   заводится.

   Своего правила под .ds-fp-token--set вид поля НЕ несёт, и это проверено,
   а не предположено: базовый .ds-fp-token--set { padding-right: 0 } (0-1-0)
   проигрывает правилу вида поля выше (0-2-0), которое задаёт padding
   целиком, — отступ у заданного значения и так 12px. Правило-компенсация
   здесь стояло и было снято как мёртвое (замер QA-доворота: padding-right
   одинаков с ним и без него). Заводящему его заново: сначала замерь.

   white-space: nowrap — воспроизведение нативного поведения <select>,
   который не переносит текст никогда. Замер: без него длинное имя
   Активности переносится на вторую строку и переполняет фиксированную
   высоту 38px — контрол ломается; дефект виден только на длинном имени.
   Пары text-overflow: ellipsis рядом НЕТ намеренно — усечению не на чем
   сработать: ширину подписи ничто не ограничивает (min-width: auto у
   флекс-элемента при nowrap равен полной строке), контрол растёт по слову,
   как и предписывает §11.59. Замер: scrollWidth == clientWidth на имени в
   сто символов. Пара выглядит канонической и потому притягивает возврат —
   вернувший её получит мёртвое свойство, обещающее поведение, которого нет.

   .ds-fp-caret { font-size: var(--fs-body) } — 9px каретки принадлежит
   конечному виду (§11.59 «font-size глифов — несущее свойство»); в поле
   она читается заметно мельче нативной стрелки соседнего .ds-select. В
   принятом до PUM-1470 блоке каретка font-size не несла и наследовала
   --fs-body — воспроизводим это, а не заводим своё число.
   ─────────────────────────────────────────────────────────────────────── */
.ds-fp-token-wrap--field,
.ds-fp-token-wrap--field:has(.ds-fp-token--set) { border-bottom: none; }

.ds-fp-token-wrap--field .ds-fp-token {
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  background: var(--surface);
  font-family: var(--font-sans);
  font-size: var(--fs-body);
  font-weight: 400;
  color: var(--text);
  padding: var(--space-2) var(--space-3);
  height: 38px;
  justify-content: space-between;
  gap: var(--space-2);
  text-align: left;
  transition: border-color var(--dur-fast) var(--ease-standard),
              box-shadow var(--dur-fast) var(--ease-standard);
}
.ds-fp-token-wrap--field .ds-fp-token:hover { background: var(--surface); color: var(--text); }
.ds-fp-token-wrap--field .ds-fp-token:focus-visible { border-color: var(--text-muted); }
.ds-fp-token-wrap--field .ds-fp-token-label { white-space: nowrap; }
.ds-fp-token-wrap--field .ds-fp-caret { font-size: var(--fs-body); }

/* ── §11.42 — Operator shell + раздел-навигация (PUM-833) ────────────────────
   .ds-shell / .ds-shell-header / .ds-shell-brand / .ds-shell-header-actions
   / .ds-shell-nav / .ds-nav-tab / .ds-nav-tab--active — page-layout pattern
   для самостоятельной операторской поверхности (messaging-оператор), вне
   Explorer. Расширяет линию page-layout, начатую §11.20 auth-page: auth-page —
   single-card до сессии, operator-shell — рабочая поверхность после сессии
   с навигацией между разделами.
   CSS идентичен прежнему underline-атому вкладки Explorer (explorer.css,
   Phase 2 §12.3 — legacy, снят вместе с прежним хромом §11.68/PUM-1506) на
   тех же токенах; дельта — text-decoration:none (<a>-вариант навигации) +
   обязательный :focus-visible halo (тот прежний атом его не нёс).
   Консолидация Explorer-навигации на .ds-nav-tab — долг PUM-1176, закрыт
   §11.68/PUM-1506.
   ─────────────────────────────────────────────────────────────────────────── */

.ds-shell {
  min-height: 100vh;
  background: var(--bg);
  padding: var(--space-6);
}

@media (max-width: 480px) {
  .ds-shell {
    padding: var(--space-4);
  }
}

.ds-shell-content {
  /* Центрированный content-container §6.1 (1680px): на ≤1440 растягивается
     до краёв, на 1920/2560 центрируется с editorial-воздухом — иначе header
     и разделы «железнодорожным вагоном» тянутся во всю ширину (§13b). */
  max-width: var(--content-max-width);
  margin-inline: auto;
}

.ds-shell-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin-bottom: var(--space-5);
}

.ds-shell-brand {
  /* Мельче auth-wordmark (28px, §11.20) — рабочая поверхность, не точка входа. */
  font-family: var(--font-serif);
  font-size: var(--fs-h2);          /* 20px */
  font-weight: var(--fw-h1);        /* 500 */
  color: var(--text);
}

.ds-shell-header-actions {
  /* Правый слот хедера: имя оператора + выход. */
  display: flex;
  align-items: center;
  gap: var(--space-4);
}

.ds-shell-user-name {
  /* Имя оператора — вторичный контекст сессии, не акцент (§11.42 spec). */
  font-family: var(--font-sans);
  font-size: var(--fs-body);
  font-weight: var(--fw-body);
  color: var(--text-muted);
}

.ds-shell-nav {
  display: flex;
  gap: 0;
  border-bottom: 2px solid var(--border);
  margin-bottom: var(--space-5);
}

.ds-nav-tab {
  /* 2px / -2px — underline-tab trick: active tab overlaps .ds-shell-nav
     border-bottom (тот же приём, что нёс прежний underline-атом вкладки
     Explorer до консолидации, §11.68/PUM-1506). */
  background: none;
  border: none;
  text-decoration: none;            /* reset для <a>-варианта навигации (multi-page) */
  border-bottom: 2px solid transparent;
  margin-bottom: -2px;
  padding: var(--space-3) var(--space-6);
  font-family: var(--font-sans);
  font-size: var(--fs-body);
  font-weight: var(--fw-body);
  color: var(--text-muted);
  cursor: pointer;
  transition: color var(--dur-fast) var(--ease-standard),
              border-color var(--dur-fast) var(--ease-standard);
}

.ds-nav-tab:hover {
  color: var(--primary);
}

.ds-nav-tab--active {
  color: var(--primary);
  border-bottom-color: var(--primary);
  font-weight: var(--fw-th);
}

.ds-nav-tab:focus-visible {
  /* Burgundy halo — interactive button/link, §8.1 (не graphite: nav-tab не form-control). */
  outline: none;
  box-shadow: var(--focus-ring);
}

/* .operator-panel — контент-область под .ds-shell-nav (PUM-833). Пустая до
   PUM-876/877 (место-панель под разделы «Отправка» / «История» / «Шаблоны»).
   Не design-system atom — plumbing: страница /operator грузит только
   tokens.css + ds-utilities.css (не explorer.css, где живёт global
   body { font-family/color } reset), поэтому baseline для placeholder-текста
   задаём явно, а не полагаемся на наследование от body. */
.operator-panel {
  font-family: var(--font-sans);
  font-size: var(--fs-body);
  color: var(--text-muted);
}

/* ── §11.68 — Хром Explorer: блок навигации в две полосы (PUM-1506) ──────────
   .ds-navblock / .ds-topbar / .ds-wordmark / .ds-topnav / .ds-topbar-right /
   .ds-topbar-user — канонический CSS-блок docs/design-system.md §11.68,
   перенесён дословно и целиком. Свойство, отсутствующее здесь, отсутствует
   намеренно — дописывание по месту запрещено самой секцией (эскалация §1.5,
   прецедент цены — К-1457-7). Кнопка вкладки — .ds-nav-tab, общий канон
   §11.42 выше в этом файле; здесь только раскладка контейнера-навигации,
   второго атома underline-вкладки не заводится.
   ─────────────────────────────────────────────────────────────────────────── */

.ds-navblock {
  border-bottom: 1px solid var(--border);
  margin-bottom: var(--space-4);
}
.ds-topbar {
  display: flex;
  align-items: center;
  gap: var(--space-6);
  height: 40px;                     /* литерал высоты полосы, как у контролов §11.62; калибруется §13b */
}
.ds-wordmark {
  font-family: var(--font-serif);
  font-size: var(--fs-h2);
  font-weight: var(--fw-h2);
  letter-spacing: var(--ls-h1);
  color: var(--text);
  padding-right: var(--space-2);
}
.ds-topnav {
  display: flex;
  align-self: stretch;              /* подчёркивание активной — на всю высоту полосы */
}
.ds-topnav .ds-nav-tab {
  display: flex;
  align-items: center;
  padding: 0 var(--space-5);        /* один шаг на все ширины: девять вкладок при --space-6 не помещались */
  margin-bottom: 0;                 /* приём -2px §11.42 не нужен: линейки под рядом нет */
}
.ds-topbar-right {
  margin-left: auto;
  display: flex;
  align-items: center;
  gap: var(--space-4);
}
.ds-topbar-user {
  font-size: var(--fs-label);
  color: var(--text-muted);
}
.ds-navblock .ds-subtabs {
  padding-top: 0;                   /* боковой запас --space-1 остаётся: под сдвиг focus-ring, §11.54 */
  background-image: none;           /* линия одна на блок; у бара она — фон, не border */
}

/* ── §11.76 — Режим роли у админа (PUM-1655) ─────────────────────────── */

.ds-topbar-user-btn {                 /* поверх .ds-topbar-user: кегль и тон приходят оттуда */
  background: none; border: none; padding: 0; margin: 0;
  font-family: inherit; font-weight: inherit; line-height: inherit; cursor: pointer;
  border-radius: var(--radius-sm);
}                                     /* не font / color: inherit — на том же узле перебьют кегль и тон .ds-topbar-user */
.ds-topbar-user-btn:hover { color: var(--text); }
.ds-topbar-user-btn:focus-visible { outline: none; box-shadow: var(--focus-ring); }

.ds-mode-strip {
  display: flex; align-items: center; justify-content: space-between;
  gap: var(--space-4);
  padding-block: var(--space-1);            /* высота — калибровка §13b */
  padding-inline: max(var(--content-padding-x),
    calc((100% - var(--content-max-width)) / 2 + var(--content-padding-x)));  /* по колонке #app */
  background: var(--warn-50);
  border-bottom: 1px solid var(--warn-500);
  color: var(--warn-700);
  font-size: var(--fs-label);
}
.ds-mode-strip-text { min-width: 0; }                        /* переносится, не режется */
.ds-mode-strip-exit { color: var(--warn-700); flex: none; }  /* поверх .ds-btn-link §11.25 */
.ds-mode-strip-exit:hover { color: var(--text); }

/* ── §11.43 — Load-more пагинация (`.ds-load-more`) — messaging operator UI v1
   (PUM-833/877) ───────────────────────────────────────────────────────────
   UX-pattern (без нового button-atom'а) для инкрементальной подгрузки длинной
   ленты, когда общее число записей неизвестно на клиенте (источник отдаёт
   страницу limit/offset без total). Первый consumer — лента Истории рассылок
   (PUM-877 суб-задача 3). Сама кнопка «Показать ещё» — reuse .ds-submit-btn
   (Tier-2, §11.23 taxonomy); .ds-load-more — только обёртка-центрирование.
   ─────────────────────────────────────────────────────────────────────────── */

.ds-load-more {
  display: flex;
  justify-content: center;
  margin: var(--space-5) 0;
}

/* ── §11.46 — Audience picker (`.ds-audience-*`) — messaging operator UI v1
   (PUM-833/877, ADR-055) ───────────────────────────────────────────────────
   Поиск + мультивыбор получателей рассылки cross-аптека. Первый consumer —
   режим «Выбранные сотрудники» селектора аудитории раздела «Отправка»
   (PUM-877 суб-задача 2). Композиция: .ds-audience-picker (поле поиска +
   прокручиваемый список + лента чипов) > .ds-audience-search (reuse .ds-input)
   + .ds-audience-list (плоский список: sticky-заголовок .ds-audience-group и
   строки-сотрудники на .ds-checkbox-field §11.29 — прямые дети, без per-group
   контейнера) + .ds-audience-chips (лента .ds-audience-chip, каждый — имя +
   «×» на .ds-icon-btn §11.51, компактный внутри чипа). Бордовый бюджет (§4.2)
   не тратится — выбор кодируется checkbox-fill (--graphite-700, §11.29) и
   нейтральным чипом, не brand-акцентом. Этот срез (PUM-877 суб-задача 1) —
   только CSS-атомы; JS-потребитель (сам picker) — суб-задача 2.
   ─────────────────────────────────────────────────────────────────────────── */

.ds-audience-picker {
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
}

.ds-audience-search {
  /* reuse .ds-input (border/radius/typography наследуются от базового
     селектора) — доп. класс только нормализует native-декор type="search"
     (Safari/Chrome иначе рендерят скруглённый OS-chrome поверх border-radius,
     игнорируя .ds-input). */
  appearance: none;
  -webkit-appearance: none;
}

.ds-audience-list {
  max-height: 320px;   /* design-spec exception §6.2 — §11.46 spec «≈320px» scroll-порог списка */
  overflow-y: auto;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
}

.ds-audience-group {
  /* Sticky group-header (аптека) — заголовок и строки-сотрудники прямые дети
     .ds-audience-list (flat-list, не вложенный per-group контейнер); top:0
     «прилипает» к верху списка при скролле мимо своей группы. */
  position: sticky;
  top: 0;
  background: var(--surface-2);
  font-family: var(--font-sans);
  font-size: var(--fs-microcopy);   /* 11px */
  color: var(--text-muted);
  padding: var(--space-1) var(--space-2);
}

.ds-audience-list .ds-checkbox-field {
  /* Строка-сотрудник §11.29 в контексте списка получателей — padding для
     визуального выравнивания с .ds-audience-group + hover-подсветка ряда
     (спека §11.46: «hover --hover-row»); контекстная надстройка, не
     модификатор базового atom'а (.ds-checkbox-field используется и вне
     audience-picker). */
  padding: var(--space-1) var(--space-2);
}

.ds-audience-list .ds-checkbox-field:hover {
  background: var(--hover-row);
}

.ds-audience-chips {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2);
}

.ds-audience-chip {
  display: inline-flex;
  align-items: center;
  gap: var(--space-1);
  background: var(--surface-2);
  border: 1px solid var(--border);
  border-radius: var(--radius-pill);
  padding: var(--space-1) var(--space-2);
  font-family: var(--font-sans);
  font-size: var(--fs-microcopy);
  color: var(--text);
}

.ds-audience-chip .ds-icon-btn {
  /* «×»-кнопка убрать получателя — icon-btn-подобная (§11.51 базовый atom),
     но не в дефолтном 24×24 (избыточен для 11px-чипа): compact 16×16.
     design-spec exception §6.2 — off-scale по §11.46 (чип-компакт «×»). */
  width: 16px;
  height: 16px;
  font-size: 16px;
  color: var(--text-muted);
}

.ds-audience-chip .ds-icon-btn:hover {
  color: var(--text);
  background: transparent;   /* чип уже на --surface-2 — не дублировать фон */
}
/* §11.72 — строка выбора Активности, раскладка */
.ds-activity-choice {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  padding-block: var(--space-3);
  border-bottom: 1px solid var(--border);
}

.ds-activity-choice-window {
  margin-left: auto;
}

/* §11.72 — ряд выбранной Активности карточки */
.ds-activity-choice-spark {
  flex: none;
  width: 120px;
  margin-left: var(--space-3);
  margin-right: calc(var(--spark-column-inset) + 1px); /* рамка .ds-table-wrap обзора: строка выбора вне неё (§11.74) */
  align-self: flex-start;
  height: 38px; /* высота поля .ds-fp-token: ряд у первой строки при переносе итога */
  display: flex;
  align-items: center;
}

.ds-activity-choice-spark > .ds-sparkline-empty {
  flex: 1;
}

/* §11.72 — итог выбранной Активности карточки */
.ds-activity-choice-main {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  column-gap: var(--space-6);
  row-gap: var(--space-2);
  flex: 1;
  min-width: 0;
}

.ds-activity-choice-result {
  display: inline-flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: var(--space-1) var(--space-2);
  font-size: var(--fs-label);
  color: var(--text-muted);
}

.ds-activity-choice-result b {
  color: var(--text);
  font-weight: 500;
}

.ds-activity-choice-sep {
  color: var(--text-subtle);
}

/* Подпись остаётся у первой строки поля при переносе итога. */
.ds-activity-choice:has(> .ds-activity-choice-main) {
  align-items: baseline;
}

/* §11.75 — профиль Активностей: отметки, выбор и наведение */
/* подложка, рамка и концы линии выходят за поле SVG на 2,5 px — без visible
   у крайних столбцов срезается рамка выбранной отметки */
.ds-profile {
  overflow: visible;
}

.ds-profile .base {
  stroke: var(--border-strong);
  stroke-width: 1;
  pointer-events: none; /* линия лежит поверх зон наведения — без этого середина отметки глуха */
}

.ds-profile .neg {
  fill: var(--neg-500);
}

.ds-profile .pos {
  fill: var(--pos-500);
}

.ds-profile .low {
  fill: none;
  stroke-width: 1;
  stroke-dasharray: 2 1.5;
}

.ds-profile .neg.low {
  stroke: var(--neg-500);
}

.ds-profile .pos.low {
  stroke: var(--pos-500);
}

.ds-profile .band {
  fill: var(--surface-2);
  opacity: 0;
}

.ds-profile .ds-profile-col.is-hover .band {
  opacity: 1;
}

.ds-profile .sel .band {
  opacity: 1;
  fill: var(--brand-50);
}

.ds-profile .selmark {
  fill: none;
  stroke: var(--primary);
  stroke-width: 1.25;
  visibility: hidden;
}

.ds-profile .sel .selmark {
  visibility: visible;
}

.ds-profile .ds-profile-hit {
  fill: transparent;
  cursor: pointer;
}

/* §11.74 — карточка двух полос профиля «Аптек» */
.ds-bands {
  width: 100%;
  table-layout: fixed;
  border-collapse: separate;
  border-spacing: 0;
}

.ds-band > td {
  height: 44px; /* нормативная высота строки §11.74 */
  box-sizing: border-box;
  background: var(--surface);
  border-bottom: 1px solid var(--border-muted);
  padding-inline: var(--space-2);
  vertical-align: middle;
}

.ds-band--open > td {
  border-bottom: 2px solid var(--border);
}

.ds-band-title {
  padding-left: var(--space-3);
}

.ds-band-toggle {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  max-width: 100%;
  margin: 0;
  padding: 0;
  border: 0;
  border-radius: var(--radius-sm);
  background: none;
  color: var(--text);
  font-family: inherit;
  cursor: pointer;
}

.ds-band-toggle:focus-visible {
  outline: none;
  box-shadow: var(--focus-ring);
}

.ds-band-chevron {
  display: inline-flex;
  flex: none;
  width: var(--space-3);
  color: var(--text-muted);
}

.ds-band-chevron svg {
  width: var(--space-3);
  height: var(--space-3);
}

.ds-band-heading {
  font-size: var(--fs-h3);
  font-weight: var(--fw-h3);
  white-space: nowrap;
}

/* пустая полоса без шеврона: название держит колонку названий соседней полосы */
.ds-band-title > .ds-band-heading {
  padding-left: calc(var(--space-3) + var(--space-2));
}

.ds-band-window {
  color: var(--text-muted);
  font-size: var(--fs-label);
  font-weight: 400;
}

.ds-band-count {
  color: var(--text-muted);
  font-size: var(--fs-label);
  text-align: right;
  white-space: nowrap;
}

.ds-band-count b {
  color: var(--text);
  font-size: var(--fs-table-num);
  font-weight: var(--fw-table-num);
}

.ds-band-profile {
  text-align: left;
}

.ds-band-profile .ds-profile {
  display: block;
}

.ds-band > td:has(> .ds-band-attainment) {
  padding-right: var(--spark-column-inset);
}

.ds-band-attainment {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-3);
  container-type: inline-size;
}

.ds-band-attainment-spark {
  flex: none;
  width: 120px;
  margin-left: auto;
}

@container (max-width: 193px) {
  .ds-band-attainment-spark { display: none; }
}

.ds-band-loading,
.ds-band-empty {
  color: var(--text-muted);
}

.ds-band-state,
.ds-band-empty {
  font-size: var(--fs-label);
}

.ds-band-error {
  color: var(--neg-700);
  font-size: var(--fs-label);
}

.ds-band-expansion > td {
  padding: var(--space-3);
  background: var(--surface);
  border-bottom: 1px solid var(--border-muted);
}

/* §11.33 / §11.74 — «Динамика» таблицы раскрытия в столбце рядов */
.ds-band-expansion > td:has(> .ds-summary-table) {
  padding-right: var(--spark-column-inset);
}

.ds-band-expansion .ds-summary-table th:last-child,
.ds-band-expansion .ds-summary-table td:last-child {
  width: calc(120px + var(--space-3)); /* border-box: ряд и зазор слева — колонка в ряд и без рядов, при одних «—» */
  padding-right: 0;
}

/* Содержимое места сегментов задаёт отступ независимо от id потребителя;
   в раскрытии пустой полосы под сегментами ничего нет — отступ ячейки один. */
.ds-band-expansion > td > div:has(> [data-background-window-segments], > .ds-seg-single):not(:last-child) {
  margin-bottom: var(--space-3);
}

.ds-band-pharmacy > td {
  height: 42px;
  box-sizing: border-box;
  padding-inline: var(--space-2);
  vertical-align: middle;
  background: var(--surface);
  border-bottom: 1px solid var(--border-muted);
}

.ds-band-pharmacy-territory,
.ds-band-pharmacy-manager {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.ds-band-pharmacy-name {
  overflow: hidden;
}

.ds-band-pharmacy-name-text {
  display: block;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.ds-band-pharmacy-name > .ds-band-pharmacy-name-text {
  width: 100%;
}

.ds-band-pharmacy-name .ds-cat-with-attention {
  max-width: 100%;
}

.ds-band-pharmacy-name .ds-cat-with-attention .ds-band-pharmacy-name-text {
  flex: 1 1 auto;
}

.ds-band-pharmacy-name .ds-attention-icon {
  flex: 0 0 auto;
}

.ds-band-pharmacy-territory,
.ds-band-pharmacy-manager {
  color: var(--text-muted);
}

.ds-band-pharmacy-count {
  font-size: var(--fs-table-num);
  font-weight: var(--fw-table-num);
  text-align: right;
}

.ds-band-pharmacy-profile .ds-profile {
  display: block;
}

.ds-band-pharmacy[tabindex] {
  cursor: pointer;
}

.ds-band-pharmacy[tabindex]:hover > td {
  background: var(--hover-row);
}

/* аптека вне справочника: отметки никуда не ведут — без руки */
.ds-band-pharmacy:not([tabindex]) .ds-profile-hit {
  cursor: default;
}

/* Мост вклада — §11.77. Высоты принадлежат модификатору таблицы. */
.ds-bridge .ax  { stroke: var(--graphite-300); stroke-width: 1; }
.ds-bridge .tg  { stroke: var(--graphite-300); stroke-width: 1; stroke-dasharray: 2 3; }
.ds-bridge .cn  { stroke: var(--border-strong); stroke-width: 1; }
.ds-bridge .neg { fill: var(--neg-500); }
.ds-bridge .pos { fill: var(--pos-500); }
.ds-bridge .tot { fill: var(--graphite-700); }
.ds-bridge-value     { font-size: var(--fs-label); font-weight: 500; font-variant-numeric: tabular-nums; }
.ds-bridge-value.neg { color: var(--neg-700); }
.ds-bridge-value.pos { color: var(--pos-700); }
.ds-bridge-table { table-layout: fixed; }
.ds-bridge-table th, .ds-bridge-table td { padding-left: var(--space-2); padding-right: var(--space-2); }
.ds-bridge-table tbody tr { height: var(--table-row-height); }
.ds-bridge-table tbody td { padding-top: 0; padding-bottom: 0; }
.ds-bridge-table tbody tr.ds-bridge-total { height: 52px; }
/* SVG занимает строку без её нижней границы: 42/52 px остаются внешней высотой. */
.ds-bridge { display: block; height: calc(var(--table-row-height) - 1px); }
.ds-bridge-total .ds-bridge { height: 51px; }
.ds-bridge-pharmacy-name { display: block; }
.ds-bridge-name-cell .ds-cat-with-attention { max-width: 100%; }
.ds-bridge-name-cell .ds-cat-with-attention .ds-bridge-pharmacy-name { flex: 1 1 auto; }
.ds-bridge-name-cell .ds-attention-icon { flex: 0 0 auto; }
.ds-bridge-total-name { font-size: var(--fs-h3); font-weight: var(--fw-h3); }
.ds-bridge-total-note { font-size: var(--fs-microcopy); color: var(--text-subtle); }
.ds-bridge-manager, .ds-bridge-territory, .ds-bridge-attainment { color: var(--text-muted); }
.ds-bridge-row[data-pharmacy-key],
.ds-bridge-row[data-employee-code],
.ds-bridge-staff-toggle { cursor: pointer; }
/* Строка роли итога без действия по щелчку: подсветку подстроки §7.6 снимаем (§11.77). */
.ds-bridge-table tbody tr.ds-bridge-role:hover > td { background: none; }
.ds-bridge-staff-toggle { color: var(--text-muted); }
.ds-bridge-staff-chevron { display: inline-flex; vertical-align: middle; margin-right: var(--space-2); color: var(--text-muted); }
.ds-bridge-staff-chevron svg { width: 12px; height: 12px; stroke-width: 1.25; }
.ds-bridge-row:last-child > td { border-bottom: none; }
/* Свёрнутый переключатель штата — последняя видимая строка: нижняя линия слилась бы с рамкой таблицы (§11.77). */
.ds-bridge-staff-toggle[aria-expanded="false"] > td { border-bottom: none; }

/* §11.79 — плавающая кнопка «Сообщить»; геометрия — токены --fab-*. */
.ds-fab {
  position: fixed;
  z-index: var(--z-float);
  bottom: var(--fab-bottom);
  right: max(var(--space-6),
    calc((100% - var(--content-max-width)) / 2 - var(--space-4) - var(--fab-size)));
  width: var(--fab-size);
  height: var(--fab-size);
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 0;
  border: 1px solid var(--border);
  border-radius: 50%;
  background: var(--surface);
  box-shadow: var(--shadow-overlay);
  color: var(--text-muted);
  cursor: pointer;
}
.ds-fab svg { font-size: 20px; width: 20px; height: 20px; }
.ds-fab:hover,
.ds-fab:active { background: var(--surface-2); color: var(--text); }
.ds-fab:focus-visible {
  outline: none;
  box-shadow: var(--shadow-overlay), var(--focus-ring);
}

/* Карта зон месяца и узкая полоса состава (§11.41, §11.5). */
.cal-zone-map--fact td { height: 50px; }
.ds-dist-bar--cell { width: 100%; height: 6px; margin-top: var(--space-1); border-radius: 2px; }
.cal-zone-map--fact .cal-zone-map-dot { display: none; }
.cal-zone-map--fact .cal-zone-map-category {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  row-gap: 0;
}
.cal-zone-map--fact .cal-zone-map-count { grid-column: auto; margin-left: 0; }
/* «Норма» — узкая клетка: название и число столбиком, как вне «Факта». */
.cal-zone-map--fact .cal-zone-map-norm .cal-zone-map-category { grid-template-columns: minmax(0, 1fr); }
.cal-zone-map--fact .cal-zone-map-norm .cal-zone-map-count { grid-column: 1; }
.cal-zone-map--fact [data-zone-composition] { grid-column: 1 / -1; }
.cal-zone-map--fact .ds-dist-bar-wrap { display: flex; width: 100%; }

/* §11.83 — страницы записи и образца по ссылке */
.ds-pub-page  { min-height: 100vh; background: var(--bg); color: var(--text); font-family: var(--font-sans); }
.ds-pub-col   { max-width: 560px; margin: 0 auto;
                padding: calc(var(--space-5) + env(safe-area-inset-top)) var(--space-4)
                         calc(var(--space-10) + env(safe-area-inset-bottom)); }
.ds-pub-label { font-size: var(--fs-label); line-height: 1.4; color: var(--text-muted); }   /* «Запись консультации», «Лучшая практика» */
.ds-pub-title { margin-top: 2px; font-size: 16px; font-weight: 600; line-height: 1.4;      /* имя Активности; 16px — §5.3 */
                overflow-wrap: anywhere; }


.ds-pub-stick  { position: sticky; top: 0; z-index: var(--z-sticky); background: var(--bg);
                 margin: var(--space-2) calc(-1 * var(--space-4)) 0; padding: var(--space-2) var(--space-4); }
.ds-pub-player { display: grid; grid-template-columns: 56px minmax(0, 1fr); column-gap: var(--space-3);
                 align-items: center; padding: var(--space-3);
                 background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius-lg); }
.ds-pub-play   { width: 56px; height: 56px; border-radius: 50%; border: none; display: grid; place-items: center;
                 background: var(--primary); color: var(--surface); cursor: pointer;
                 transition: background var(--dur-fast) var(--ease-standard); }
.ds-pub-play:hover         { background: var(--primary-hover); }
.ds-pub-play:focus-visible { outline: none; box-shadow: var(--focus-ring); }
.ds-pub-play svg           { width: 22px; height: 22px; fill: currentColor; }
.ds-pub-track  { position: relative; height: 56px; overflow: hidden; border-radius: var(--radius-sm);
                 background: var(--surface-2); cursor: pointer; touch-action: pan-y; }   /* relative + hidden — несущие для меток, как у .ds-panel-waveform */
.ds-pub-track:focus-visible { outline: none; box-shadow: var(--focus-ring-graphite); }
.ds-pub-times  { grid-column: 2; display: flex; justify-content: space-between; margin-top: var(--space-1);
                 font-family: var(--font-mono); font-size: var(--fs-mono); font-variant-numeric: tabular-nums;
                 color: var(--text-muted); }
.ds-pub-times-cur  { color: var(--text); }
.ds-pub-times-note { font-family: var(--font-sans); font-size: var(--fs-label); }                         /* «Загружается…» */
.ds-pub-times-err  { font-family: var(--font-sans); font-size: var(--fs-label); color: var(--neg-700); }  /* «Не удалось загрузить звук.» + .ds-btn-retry */
.ds-pub-times-err .ds-btn-retry        { position: relative; }
.ds-pub-times-err .ds-btn-retry::after { content: ""; position: absolute; top: calc(-1 * var(--space-1)); height: 44px;
                                         left: calc(-1 * var(--space-2)); right: calc(-1 * var(--space-2)); }   /* цель нажатия класса (§6.4) без смены вида — вниз от кромки дорожки, на неё не заходит */


.ds-pub-aux    { display: flex; align-items: center; justify-content: space-between; margin-top: var(--space-2); }
.ds-pub-skips  { display: flex; gap: var(--space-2); }
.ds-pub-skip   { height: 44px; min-width: 56px; padding: 0 var(--space-3); border-radius: var(--radius-pill);
                 border: 1px solid var(--border); background: var(--surface); color: var(--graphite-700);
                 font-family: var(--font-sans); font-size: var(--fs-body); cursor: pointer; }   /* 56 — ряд в одну строку на экране 320 px */
.ds-pub-skip:hover         { background: var(--surface-2); }
.ds-pub-skip:focus-visible { outline: none; box-shadow: var(--focus-ring-graphite); }


.ds-pub-transcript { margin-top: var(--space-5); font-size: 16px; line-height: 1.65; }   /* абзацы и «— » — .ds-transcript-body, §11.18 */
.ds-pub-empty      { margin-top: var(--space-4); font-size: var(--fs-label); font-style: italic; color: var(--text-subtle); }
.ds-pub-note       { margin-top: var(--space-5); padding: 2px 0 2px var(--space-3); border-left: 2px solid var(--border-strong); }
.ds-pub-note-label { font-size: var(--fs-label); color: var(--text-muted); }
.ds-pub-note-text  { font-size: 16px; line-height: 1.55; white-space: pre-wrap; overflow-wrap: anywhere; }


.ds-pub-status { margin-top: var(--space-3); font-size: var(--fs-label); color: var(--text-muted); }   /* «Загружаем запись…» */


.ds-pub-msg       { padding: var(--space-5); background: var(--surface);
                    border: 1px solid var(--border); border-radius: var(--radius-lg); }
.ds-pub-msg-title { margin: 0; font-family: var(--font-serif); font-size: var(--fs-h2); font-weight: var(--fw-h2); }
.ds-pub-msg-text  { margin-top: var(--space-2); font-size: var(--fs-body); color: var(--text-muted); }
.ds-pub-retry     { margin-top: var(--space-4); height: 44px; padding: 0 var(--space-5);   /* Tier 3 neutral (§10.5), как .ds-trim-mark — в высоте цели нажатия класса */
                    border: 1px solid var(--border-strong); border-radius: var(--radius-sm); background: var(--surface);
                    color: var(--graphite-700); font-family: var(--font-sans); font-size: var(--fs-body); cursor: pointer; }
.ds-pub-retry:hover         { background: var(--surface-2); }
.ds-pub-retry:focus-visible { outline: none; box-shadow: var(--focus-ring-graphite); }

/* §11.50: справочник паспортов — оглавление и лист. */
.ds-passport-split {
  display: grid;
  grid-template-columns: 320px minmax(0, 1fr);
  gap: var(--space-8);
  flex: 1;
  min-height: 0;
  min-width: 0;
}
.ds-passport-toc {
  display: flex;
  flex-direction: column;
  min-height: 0;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  overflow: hidden;
}
.ds-passport-toc-search { flex: none; padding: var(--space-3); border-bottom: 1px solid var(--border); }
.ds-passport-toc-search input { width: 100%; }
.ds-passport-pane { min-height: 0; overflow-y: auto; }
.ds-passport-toc-list { flex: 1; position: relative; }
.ds-passport-pane::-webkit-scrollbar { width: 10px; }
.ds-passport-pane::-webkit-scrollbar-track { background: transparent; }
.ds-passport-pane::-webkit-scrollbar-thumb {
  background: var(--border-strong);
  border-radius: 5px;
  border: 3px solid transparent;
  background-clip: padding-box;
}
@supports not selector(::-webkit-scrollbar) {
  .ds-passport-pane { scrollbar-width: thin; scrollbar-color: var(--border-strong) transparent; }
}
.ds-passport-toc-group {
  position: sticky;
  top: 0;
  z-index: var(--z-sticky);
  background: var(--surface);
  padding: var(--space-3) var(--space-3) var(--space-2);
  border-bottom: 1px solid var(--border-muted);
  font-size: var(--fs-th);
  font-weight: 500;
  color: var(--text-muted);
}
.ds-passport-toc-group span { color: var(--text); }
.ds-passport-toc-group small { display: block; font-size: var(--fs-th); font-weight: 400; }
.ds-passport-toc-item {
  display: block;
  width: 100%;
  text-align: left;
  padding: var(--space-1) var(--space-3);
  font-family: var(--font-sans);
  font-size: var(--fs-body);
  line-height: 20px;
  color: var(--text);
  background: none;
  border: 0;
  cursor: pointer;
}
.ds-passport-toc-item:hover { background: var(--surface-2); }
.ds-passport-toc-item.is-active { background: var(--active-row); box-shadow: inset 2px 0 0 var(--active-row-edge); font-weight: 500; }
.ds-passport-toc-item:focus-visible { outline: none; box-shadow: inset 0 3px 0 var(--focus-ring-color), inset 0 -3px 0 var(--focus-ring-color), inset 3px 0 0 var(--focus-ring-color), inset -3px 0 0 var(--focus-ring-color); }
.ds-passport-toc-item.is-active:focus-visible { box-shadow: inset 2px 0 0 var(--active-row-edge), inset 0 3px 0 var(--focus-ring-color), inset 0 -3px 0 var(--focus-ring-color), inset 3px 0 0 var(--focus-ring-color), inset -3px 0 0 var(--focus-ring-color); }
.ds-passport-toc-foot {
  flex: none;
  padding: var(--space-2) var(--space-3);
  border-top: 1px solid var(--border);
  font-size: var(--fs-th);
  color: var(--text-muted);
  font-variant-numeric: tabular-nums;
}
.ds-passport-document { padding-right: var(--space-2); padding-bottom: calc(var(--fab-size) + var(--fab-bottom)); }
.ds-passport-document:focus-visible { outline: none; box-shadow: var(--focus-ring); }
.ds-passport-sheet {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  padding: var(--space-6) var(--space-8) var(--space-8);
}
.ds-passport-title { margin: 0; font-family: var(--font-serif); font-size: var(--fs-h2); font-weight: 500; line-height: 1.3; }
.ds-passport-meta { margin-top: var(--space-1); font-size: var(--fs-label); color: var(--text-muted); }
.ds-passport-description { margin: var(--space-3) 0 0; font-size: var(--fs-body); line-height: 1.6; max-width: 720px; }
.ds-passport-part-label { font-size: var(--fs-th); font-weight: 500; color: var(--text-muted); margin-bottom: var(--space-2); }
.ds-passport-rule { margin-top: var(--space-5); }
.ds-passport-rule-reasons { display: grid; grid-template-columns: repeat(var(--n), minmax(0, 1fr)); gap: var(--space-6); }
.ds-passport-rule-reasons--one { grid-template-columns: minmax(0, 520px); justify-content: center; }
.ds-passport-rule-card { border: 1px solid var(--border); border-radius: var(--radius-md); padding: var(--space-3) var(--space-4); font-size: var(--fs-body); line-height: 1.55; }
.ds-passport-rule-role { font-size: var(--fs-body); font-weight: 600; }
.ds-passport-rule-hint { font-size: var(--fs-th); color: var(--text-muted); margin: var(--space-1) 0 var(--space-2); }
.ds-passport-rule-join { position: relative; height: 40px; }
.ds-passport-rule-branch {
  position: absolute;
  left: calc((100% - (var(--n) - 1) * var(--space-6)) / var(--n) * (var(--i) + 0.5) + var(--i) * var(--space-6));
  top: 0;
  height: 20px;
  border-left: 1px solid var(--border-strong);
}
.ds-passport-rule-branch span { position: absolute; top: 3px; padding-left: var(--space-2); white-space: nowrap; font-size: var(--fs-th); color: var(--text-muted); }
.ds-passport-rule-join::before {
  content: "";
  position: absolute;
  top: 20px;
  left: calc((100% - (var(--n) - 1) * var(--space-6)) / var(--n) / 2);
  right: calc((100% - (var(--n) - 1) * var(--space-6)) / var(--n) / 2);
  border-top: 1px solid var(--border-strong);
}
.ds-passport-rule-join::after {
  content: "";
  position: absolute;
  left: 50%;
  top: 20px;
  height: 15px;
  border-left: 1px solid var(--border-strong);
}
.ds-passport-rule-join--one::before { display: none; }
/* Наконечник привязан к центру общей связки, а не к ответвлению. */
.ds-passport-rule-join > .ds-passport-rule-arrow {
  position: absolute;
  left: 50%;
  bottom: 0;
  transform: translateX(-4px);
  width: 0;
  height: 0;
  border-left: 4px solid transparent;
  border-right: 4px solid transparent;
  border-top: 5px solid var(--graphite-300);
}
.ds-passport-rule-offer { margin: 0 auto; max-width: 560px; background: var(--surface-2); border: 1px solid var(--border); border-radius: var(--radius-md); padding: var(--space-3) var(--space-5); text-align: center; }
.ds-passport-rule-offer .ds-passport-part-label { margin-bottom: 0; }
.ds-passport-rule-offer-lines { font-size: var(--fs-body); font-weight: 500; line-height: 1.6; }
.ds-passport-rule-offer-lines--large { font-family: var(--font-serif); font-size: var(--fs-h2); line-height: 1.35; }
.ds-passport-sayings { margin-top: var(--space-6); }
.ds-passport-sayings ol { margin: 0; padding: 0; list-style: none; display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: var(--space-3) var(--space-8); counter-reset: passport-phrase; }
.ds-passport-sayings li { counter-increment: passport-phrase; display: grid; grid-template-columns: 18px minmax(0, 1fr); gap: var(--space-2); font-size: var(--fs-body); line-height: 1.6; }
.ds-passport-sayings li::before { content: counter(passport-phrase); font-family: var(--font-serif); font-size: var(--fs-body); color: var(--text-subtle); }
.ds-passport-related { margin-top: var(--space-6); padding-top: var(--space-4); border-top: 1px solid var(--border); }
.ds-passport-related-row { display: grid; grid-template-columns: minmax(0, 300px) minmax(0, 1fr); gap: var(--space-6); font-size: var(--fs-label); line-height: 1.5; }
.ds-passport-related-row + .ds-passport-related-row { margin-top: var(--space-2); }
.ds-passport-related-reason { color: var(--text-muted); }
.ds-passport-hit { font-weight: 600; background: var(--warn-50); box-shadow: 0 0 0 1px var(--warn-200); border-radius: 2px; }
