/* ============================================================================
   Компоненты. Всё, что встречается больше чем на одном экране.

   Правило именования — БЭМ-подобное, без вложенности глубже одного уровня:
     .block            сам компонент
     .block__element   его часть
     .block--modifier  вариант
   Причина строгости: в прошлой версии классы назывались .crow, .titem, .cwi,
   .fs-fill — по имени невозможно было понять ни к чему относится правило, ни
   можно ли его трогать. Здесь имя компонента ищется поиском по проекту.
   ============================================================================ */

/* ================================================================= иконки
   Иконки — <svg><use href="/static/icons.svg#name"> из общего спрайта. Ни
   эмодзи, ни символов вроде «✕» в разметке: эмодзи рисуется шрифтом системы,
   выглядит по-разному на Windows и macOS и озвучивается скринридером как слово.

   currentColor — иконка всегда цвета своего текста, отдельно её красить не надо. */
.icon {
  width: 16px;
  height: 16px;
  flex: none;
  fill: none;
  stroke: currentColor;
  stroke-width: 1.75;
  stroke-linecap: round;
  stroke-linejoin: round;
}
.icon--sm { width: 14px; height: 14px; }
.icon--lg { width: 20px; height: 20px; }
.icon--xl { width: 24px; height: 24px; }
/* Иконка внутри строки текста: выравнивание по базовой линии, а не по центру
   блока — иначе она «всплывает» над строчными буквами. */
.icon--inline { vertical-align: -0.18em; display: inline-block; }

/* ================================================================== кнопки
   Иерархия строгая, и она отвечает на вопрос «что здесь главное»:
     primary — одно главное действие на экран. Двух primary не бывает.
     default — обычные действия, их может быть сколько угодно.
     ghost   — второстепенное, не отнимающее внимания у primary.
     soft-*  — действие, окрашенное смыслом (отправить, удалить).
   Разрушительное действие НИКОГДА не primary: одинаковый вид у «Отправить» и
   «Удалить» — это вопрос времени до случайного нажатия. */
.btn {
  --btn-height: 36px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-3);
  min-height: var(--btn-height);
  padding: 0 var(--space-5);
  border: 1px solid var(--line-strong);
  border-radius: var(--radius-md);
  background: var(--bg-surface);
  color: var(--fg-soft);
  font-size: var(--text-sm);
  font-weight: var(--weight-medium);
  white-space: nowrap;
  cursor: pointer;
  text-decoration: none;
  transition:
    background-color var(--motion-fast) var(--ease-out),
    border-color var(--motion-fast) var(--ease-out),
    color var(--motion-fast) var(--ease-out);
}
.btn:hover {
  background: var(--bg-hover);
  border-color: var(--line-strong);
  color: var(--fg);
  text-decoration: none;
}
.btn:active { background: var(--bg-active); }

.btn--primary {
  background: var(--accent);
  border-color: var(--accent);
  color: var(--accent-on);
  font-weight: var(--weight-semi);
}
.btn--primary:hover {
  background: var(--accent-hover);
  border-color: var(--accent-hover);
  color: var(--accent-on);
}

.btn--ghost {
  background: transparent;
  border-color: transparent;
  color: var(--fg-muted);
}
.btn--ghost:hover { background: var(--bg-hover); color: var(--fg); }

/* Мягкие варианты: смысл цветом, но без веса залитой кнопки. */
.btn--soft-accent { background: var(--accent-soft); border-color: transparent; color: var(--accent-fg); }
.btn--soft-accent:hover { background: var(--accent-softer); border-color: var(--accent); color: var(--accent-fg); }

.btn--soft-ok { background: var(--ok-soft); border-color: transparent; color: var(--ok-fg); }
.btn--soft-ok:hover { background: var(--ok-soft); border-color: var(--ok); color: var(--ok-fg); }

.btn--soft-danger { background: var(--danger-soft); border-color: transparent; color: var(--danger-fg); }
.btn--soft-danger:hover { background: var(--danger-soft); border-color: var(--danger); color: var(--danger-fg); }

.btn--soft-info { background: var(--info-soft); border-color: transparent; color: var(--info-fg); }
.btn--soft-info:hover { background: var(--info-soft); border-color: var(--info); color: var(--info-fg); }

.btn--sm { --btn-height: 30px; padding: 0 var(--space-4); font-size: var(--text-xs); }
.btn--lg { --btn-height: 42px; padding: 0 var(--space-6); font-size: var(--text-md); }

/* Кнопка-иконка обязана нести aria-label: без подписи она немая для
   скринридера. Квадратная, чтобы иконка стояла по центру. */
.btn--icon {
  padding: 0;
  width: var(--btn-height);
  min-width: var(--btn-height);
}
.btn--block { width: 100%; }
/* Действие в правой панели: подпись слева, иконка прижата к правому краю. */
.btn--between { justify-content: space-between; }

.btn:disabled,
.btn[aria-disabled="true"] {
  opacity: 0.55;
  cursor: not-allowed;
  pointer-events: none;
}

/* Кнопка в ожидании ответа. htmx сам вешает .htmx-request на то, что нажали,
   и снимает по ответу — отдельного скрипта не нужно.

   Раньше ожидание выглядело как запрет: hx-disabled-elt гасил кнопку до
   .btn:disabled, то есть до бледной кнопки с курсором «нельзя». Но идёт как
   раз то, о чём просили, и знак запрета в этот момент читается как «не
   нажалось» — сейлз на медленной сети жмёт второй раз.

   Полоса по нижнему краю, а не значок рядом с подписью: значок раздвинул бы
   кнопку в момент нажатия, а квадратные кнопки со значком (.btn--icon) он бы
   и вовсе растянул. Полоса ничего не двигает и одинаково работает на всех. */
.btn.htmx-request {
  position: relative;
  overflow: hidden;
  opacity: 1;
  /* disabled снимает события указателя, а вместе с ними и курсор. Возвращаем
     только ради курсора: кнопка всё равно disabled, нажатие ничего не делает. */
  pointer-events: auto;
  cursor: progress;
}
.btn.htmx-request::after {
  content: "";
  position: absolute;
  inset: auto 0 0 0;
  height: 2px;
  background: currentColor;
  opacity: 0.45;
  transform-origin: left;
  animation: btn-wait 1.1s var(--ease-out) infinite;
}
@keyframes btn-wait {
  0%   { transform: scaleX(0);   transform-origin: left; }
  50%  { transform: scaleX(1);   transform-origin: left; }
  51%  { transform: scaleX(1);   transform-origin: right; }
  100% { transform: scaleX(0);   transform-origin: right; }
}
/* Тем, кто просил меньше движения, полоса просто стоит на месте: ожидание
   всё равно видно, а бегающая черта в углу экрана — нет. */
@media (prefers-reduced-motion: reduce) {
  .btn.htmx-request::after { animation: none; opacity: 0.3; }
}

/* Пальцем в кнопку 30px не попасть. На сенсорных экранах поднимаем все размеры
   до 44px — порога, ниже которого промахи становятся систематическими. */
@media (pointer: coarse) {
  .btn { --btn-height: 44px; }
  .btn--sm { --btn-height: 44px; }
}

/* Кнопка, выглядящая ссылкой. Для третьестепенного в плотных местах — «Изменить»
   в ленте. Остаётся <button>, потому что действие, а не переход. */
.linkbtn {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  color: var(--accent-fg);
  font-size: var(--text-xs);
  font-weight: var(--weight-medium);
  cursor: pointer;
  border-radius: var(--radius-sm);
}
.linkbtn:hover { color: var(--accent-hover); text-decoration: underline; }
.linkbtn--danger { color: var(--danger-fg); }
.linkbtn--danger:hover { color: var(--danger); }

/* Индикатор ожидания на кнопке. htmx сам вешает .htmx-request на время
   запроса — отдельного скрипта для «идёт сохранение» не нужно. */
.spinner {
  width: 13px;
  height: 13px;
  border: 2px solid currentColor;
  border-top-color: transparent;
  border-radius: 50%;
  animation: spin 0.7s linear infinite;
}
@keyframes spin { to { transform: rotate(360deg); } }
@media (prefers-reduced-motion: reduce) {
  .spinner { animation: none; opacity: 0.5; }
}
.htmx-indicator { display: none; }
.htmx-request .htmx-indicator,
.htmx-request.htmx-indicator { display: inline-flex; }

/* ============================================================ метки состояния
   Пилюля отвечает на вопрос «в каком состоянии объект», а не украшает.
   Цвет всегда дублируется словом: показывать состояние ТОЛЬКО цветом нельзя —
   восемь процентов мужчин не различат зелёный и красный. */
.pill {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  padding: 2px var(--space-3);
  border-radius: var(--radius-sm);
  background: var(--bg-hover);
  color: var(--fg-soft);
  font-size: var(--text-xs);
  font-weight: var(--weight-medium);
  line-height: 1.5;
  white-space: nowrap;
}
/* Пилюля-кнопка (этап в списке лидов): выглядит как обычная пилюля,
   но кликается — сброс браузерных стилей кнопки обязателен. */
button.pill {
  border: 0;
  font: inherit;
  font-size: var(--text-xs);
  font-weight: var(--weight-medium);
  cursor: pointer;
}
button.pill:hover { filter: brightness(0.95); }

.pill--accent { background: var(--accent-soft); color: var(--accent-fg); }
.pill--ok     { background: var(--ok-soft); color: var(--ok-fg); }
.pill--warn   { background: var(--warn-soft); color: var(--warn-fg); }
.pill--danger { background: var(--danger-soft); color: var(--danger-fg); }
.pill--info   { background: var(--info-soft); color: var(--info-fg); }
.pill--lg { padding: var(--space-2) var(--space-4); font-size: var(--text-sm); }

/* Точка состояния перед подписью — помогает отличить пилюли одного тона
   в длинном списке, не считывая цвет фона. */
.pill__dot {
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: currentColor;
  flex: none;
}

/* Счётчик у заголовка: «Лиды 2168». Не пилюля — это число, а не состояние. */
.counter {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 22px;
  height: 20px;
  padding: 0 var(--space-3);
  border-radius: var(--radius-full);
  background: var(--bg-hover);
  color: var(--fg-soft);
  font-size: var(--text-xs);
  font-weight: var(--weight-semi);
}
.counter--danger { background: var(--danger); color: var(--danger-on); }
.counter--accent { background: var(--accent-soft); color: var(--accent-fg); }

/* Тир скоринга: буква в квадрате. Ширина фиксирована, чтобы буквы стояли
   столбцом в списке. */
.tier {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 24px;
  height: 24px;
  border-radius: var(--radius-sm);
  font-family: var(--font-mono);
  font-size: var(--text-sm);
  font-weight: var(--weight-bold);
}
.tier--lg { width: 40px; height: 40px; font-size: var(--text-xl); border-radius: var(--radius-lg); }
.tier--a { background: var(--ok-soft); color: var(--ok-fg); }
.tier--b { background: var(--accent-soft); color: var(--accent-fg); }
.tier--c { background: var(--warn-soft); color: var(--warn-fg); }
.tier--d { background: var(--bg-hover); color: var(--fg-muted); }
.tier--none { background: var(--bg-hover); color: var(--fg-muted); }

/* Чип-ссылка: сайт компании. Выделен, потому что по нему кликают, в отличие
   от соседних фактов. */
.chip-link {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  padding: 2px var(--space-3);
  border-radius: var(--radius-sm);
  background: var(--accent-soft);
  color: var(--accent-fg);
  font-size: var(--text-xs);
  font-weight: var(--weight-medium);
  border: 1px solid transparent;
  /* Чип не переносится на вторую строку: в узкой колонке подпись усекается
     многоточием (span.u-truncate внутри site_chip), перенос ломал высоту строк. */
  white-space: nowrap;
  max-width: 100%;
}
.chip-link:hover { border-color: var(--accent); color: var(--accent-fg); }
/* Сайта нет — тот же слот ведёт в поиск. Приглушён, чтобы не выдавать себя
   за настоящий сайт компании. */
.chip-link--unknown { background: var(--bg-hover); color: var(--fg-muted); font-weight: var(--weight-normal); }
.chip-link--unknown:hover { border-color: var(--line-strong); color: var(--fg-soft); }

/* ================================================================== карточка */
.card {
  background: var(--bg-surface);
  border: 1px solid var(--line);
  border-radius: var(--radius-xl);
  padding: var(--space-6);
}
.card--flush { padding: 0; overflow: hidden; }
.card--sunken { background: var(--bg-sunken); }
.card--danger { border-color: color-mix(in srgb, var(--danger) 35%, var(--line)); }

.card__head {
  display: flex;
  align-items: center;
  gap: var(--space-4);
  margin-bottom: var(--space-5);
}
.card--flush .card__head {
  margin-bottom: 0;
  padding: var(--space-5) var(--space-6);
  border-bottom: 1px solid var(--line);
}
.card__title {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  font-size: var(--text-lg);
  font-weight: var(--weight-semi);
}
/* Всё после заголовка уезжает вправо: действия карточки живут в её шапке. */
.card__tools { margin-left: auto; display: flex; align-items: center; gap: var(--space-3); }
.card__hint {
  color: var(--fg-muted);
  font-size: var(--text-xs);
  line-height: var(--leading-snug);
}
.card__foot {
  margin-top: var(--space-5);
  padding-top: var(--space-5);
  border-top: 1px solid var(--line);
  display: flex;
  align-items: center;
  gap: var(--space-4);
  flex-wrap: wrap;
}
/* Действие на правом краю подвала, отдельно от основных (стоп-лист рядом с отправкой). */
.card__foot-end { margin-left: auto; }

/* Редко используемая панель отправки: в покое только заголовок и стрелка. */
.message-panel > summary {
  cursor: pointer;
  list-style: none;
  margin-bottom: 0;
}
.message-panel > summary::-webkit-details-marker { display: none; }
.message-panel > summary:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 4px;
  border-radius: var(--radius-sm);
}
.message-panel[open] > summary { margin-bottom: var(--space-5); }
.message-panel__chevron {
  margin-left: auto;
  flex-shrink: 0;
  color: var(--fg-muted);
  transition: transform var(--motion-fast) var(--ease-out);
}
.message-panel[open] > summary .message-panel__chevron { transform: rotate(180deg); }

/* Карточка правой колонки — мельче и без лишнего веса: это справка,
   а не рабочая поверхность. */
.side-card {
  background: var(--bg-surface);
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  padding: var(--space-5);
}
.side-card__head {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  margin-bottom: var(--space-4);
  color: var(--fg-soft);
  font-size: var(--text-sm);
  font-weight: var(--weight-semi);
}
.side-card__head .icon { color: var(--fg-muted); }
.side-card__tools { margin-left: auto; }
.company-site-list { display: grid; gap: var(--space-3); min-width: 0; }
.company-site-list__item { display: flex; align-items: baseline; flex-wrap: wrap; gap: var(--space-2); min-width: 0; }
.company-site-list__item > a { overflow-wrap: anywhere; }
.company-site-list__primary { color: var(--fg-muted); font-size: var(--text-xs); }
.company-sites-editor { display: grid; gap: var(--space-3); }
.company-sites-editor__head { display: flex; align-items: center; justify-content: space-between; gap: var(--space-3); }
.company-sites-editor__extras { display: grid; gap: var(--space-3); }
.company-sites-editor__row { display: flex; align-items: center; gap: var(--space-2); }
.company-sites-editor__row .input { flex: 1; min-width: 0; }
.company-sites-editor__row .btn { flex-shrink: 0; }
.company-sites-editor > [data-add-company-site] { justify-self: start; }
.lead-tasks-card .side-card__head > span:first-of-type {
  display: inline-flex;
  align-items: center;
  gap: var(--space-3);
}
.lead-task-count {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: var(--space-6);
  padding: 0 var(--space-2);
  border-radius: var(--radius-sm);
  background: var(--bg-hover);
  color: var(--fg-muted);
  font-size: var(--text-xs);
  line-height: var(--space-6);
}
.lead-tasks-card { container-type: inline-size; }
.lead-tasks-table-wrap { width: 100%; min-width: 0; }
.lead-tasks-table { width: 100%; border-collapse: collapse; table-layout: fixed; text-align: left; font-size: var(--text-sm); }
.lead-tasks-table thead th { padding: 0 var(--space-3) var(--space-3); color: var(--fg-muted); font-size: var(--text-xs); font-weight: var(--weight-medium); }
.lead-tasks-table thead th:first-child { padding-left: 0; }
.lead-tasks-table thead th:nth-child(2) { width: 120px; }
.lead-tasks-table thead th:nth-child(3) { width: 130px; }
.lead-tasks-table thead th:nth-child(4) { width: 116px; }
.lead-tasks-table tbody th, .lead-tasks-table tbody td { padding: var(--space-4) var(--space-3); border-top: 1px solid var(--line); vertical-align: top; overflow-wrap: anywhere; }
.lead-tasks-table tbody th { padding-left: 0; font-weight: var(--weight-normal); }
.lead-tasks-table tbody td:last-child { padding-right: 0; }
.lead-tasks-table .pill { max-width: 100%; white-space: normal; }
.lead-task__link { display: grid; gap: var(--space-2); min-width: 0; color: var(--fg); border-radius: var(--radius-sm); }
.lead-task__link:hover { color: var(--accent-fg); }
.lead-task__link:focus-visible { outline: 2px solid var(--accent); outline-offset: 4px; }
.lead-task__top { display: flex; align-items: center; justify-content: space-between; gap: var(--space-3); }
.lead-task__key { color: var(--accent-fg); font-size: var(--text-xs); font-weight: var(--weight-medium); }
.lead-task__open { color: var(--fg-muted); flex-shrink: 0; }
.lead-task__title { font-weight: var(--weight-medium); line-height: var(--leading-snug); overflow-wrap: anywhere; }
.lead-task__due { display: inline-flex; align-items: center; gap: var(--space-2); color: var(--fg-muted); font-size: var(--text-xs); white-space: nowrap; }
.lead-task__due--overdue { color: var(--danger-fg); }
.lead-task__executors { color: var(--fg-soft); font-size: var(--text-xs); line-height: var(--leading-snug); overflow-wrap: anywhere; }
@container (max-width: 600px) {
  .lead-tasks-table, .lead-tasks-table tbody { display: block; }
  .lead-tasks-table thead { display: none; }
  .lead-tasks-table tbody { display: grid; gap: var(--space-3); }
  .lead-tasks-table .lead-task { display: grid; grid-template-columns: minmax(0,1fr) minmax(0,1fr); gap: var(--space-3) var(--space-4); padding: var(--space-4); background: var(--bg-sunken); border: 1px solid var(--line); border-radius: var(--radius-md); }
  .lead-tasks-table tbody th, .lead-tasks-table tbody td { padding: 0; border: 0; min-width: 0; }
  .lead-tasks-table tbody th { grid-column: 1/-1; }
  .lead-tasks-table tbody td { display: grid; justify-items: start; align-content: start; gap: var(--space-2); }
  .lead-tasks-table tbody td::before { content: attr(data-label); color: var(--fg-muted); font-size: var(--text-xs); }
  .lead-tasks-table tbody td:nth-child(3) { grid-column: 1/-1; }
  .lead-tasks-table tbody td:nth-child(4) { grid-column: 2; grid-row: 2; }
}
.lead-task-history { margin-top: var(--space-4); padding-top: var(--space-4); border-top: 1px solid var(--line); font-size: var(--text-xs); }
.lead-task-history summary { cursor: pointer; color: var(--fg-muted); }
.lead-task-history[open] summary { margin-bottom: var(--space-4); }
/* Смысловая часть внутри карточки: отделена чертой, а не пустотой — иначе
   «публичные контакты» читаются как продолжение списка реквизитов. */
.side-card__part {
  margin-top: var(--space-5);
  padding-top: var(--space-4);
  border-top: 1px solid var(--line);
  display: grid;
  gap: var(--space-3);
}

/* =========================================================== ключ → значение
   Блок «О компании»: подпись слева, значение справа. Сетка, а не флекс с
   отступами: значения обязаны стоять одним столбцом, иначе список читается
   как набор разрозненных строк. */
.facts { display: grid; gap: var(--space-4); }
.facts__row {
  display: grid;
  grid-template-columns: minmax(80px, 96px) minmax(0, 1fr);
  gap: var(--space-4);
  align-items: baseline;
}
.facts__key {
  color: var(--fg-muted);
  font-size: var(--text-xs);
  line-height: var(--leading-snug);
}
.facts__value {
  font-size: var(--text-sm);
  color: var(--fg);
  min-width: 0;
  overflow-wrap: break-word;
}
.facts__value--empty { color: var(--fg-muted); }

/* Ряд фактов в строку — для шапок, где значения короткие. */
.factline {
  display: flex;
  align-items: center;
  gap: var(--space-4);
  flex-wrap: wrap;
  color: var(--fg-muted);
  font-size: var(--text-sm);
}
.factline__item { display: inline-flex; align-items: center; gap: var(--space-2); min-width: 0; }

/* ==================================================================== поля
   Подпись всегда видима и всегда над полем. Плейсхолдер вместо подписи не
   годится: он исчезает при вводе, и человек, отвлёкшийся на середине формы,
   уже не знает, что заполняет. */
.field { display: grid; gap: var(--space-2); min-width: 0; }
.field__label {
  color: var(--fg-soft);
  font-size: var(--text-xs);
  font-weight: var(--weight-medium);
}
.field__hint { color: var(--fg-muted); font-size: var(--text-xs); }
.field__error {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  color: var(--danger-fg);
  font-size: var(--text-xs);
}
.field__row { display: flex; gap: var(--space-3); align-items: center; }
.field__row > * { min-width: 0; }

.input,
.select,
.textarea {
  width: 100%;
  min-height: 36px;
  padding: var(--space-3) var(--space-4);
  border: 1px solid var(--line-strong);
  border-radius: var(--radius-md);
  background: var(--bg-surface);
  color: var(--fg);
  font-size: var(--text-sm);
  transition:
    border-color var(--motion-fast) var(--ease-out),
    box-shadow var(--motion-fast) var(--ease-out);
}
.input::placeholder,
.textarea::placeholder { color: var(--fg-muted); }

/* Кольцо на активном поле мягче общего фокус-кольца: поле и так обведено
   рамкой, двойная обводка выглядит поломкой. */
.input:focus,
.select:focus,
.textarea:focus {
  outline: none;
  border-color: var(--accent);
  box-shadow: 0 0 0 3px var(--accent-soft);
}
.input:disabled,
.select:disabled,
.textarea:disabled,
.input[readonly],
.textarea[readonly] {
  background: var(--bg-sunken);
  color: var(--fg-muted);
  cursor: not-allowed;
}
.input--invalid { border-color: var(--danger); }

.textarea {
  resize: vertical;
  line-height: var(--leading-normal);
  min-height: 96px;
}

/* Своя стрелка у select: системная различается в трёх браузерах и ломает
   выравнивание правого края в ряду фильтров. */
.select {
  appearance: none;
  padding-right: var(--space-8);
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none' stroke='%2364748B' stroke-width='1.75' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M4 6l4 4 4-4'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right var(--space-4) center;
  background-size: 14px;
  cursor: pointer;
}

/* Поле поиска с иконкой внутри. Иконка декоративная — поле подписано
   плейсхолдером и aria-label. */
.search { position: relative; display: flex; align-items: center; }
.search__icon {
  position: absolute;
  left: var(--space-4);
  color: var(--fg-muted);
  pointer-events: none;
}
.search .input { padding-left: var(--space-9); }
/* Клавиша в подсказке: «Enter — отправить». Обычный inline-элемент, поэтому
   годится и внутри строки текста. */
.kbd {
  display: inline-flex;
  align-items: center;
  gap: 2px;
  padding: 1px var(--space-2);
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  background: var(--bg-sunken);
  color: var(--fg-soft);
  font-family: var(--font-sans);
  font-size: var(--text-2xs);
  line-height: 1.5;
  white-space: nowrap;
}

/* Та же клавиша, но прижатая к правому краю поля поиска. Отдельным классом,
   потому что абсолютное позиционирование внутри строки текста делает подсказку
   невидимой — она уезжает к краю ближайшего позиционированного предка. */
.search__kbd {
  position: absolute;
  right: var(--space-3);
  pointer-events: none;
}

/* Флажок и переключатель: системные слишком мелкие и не красятся. */
.checkbox {
  appearance: none;
  width: 16px;
  height: 16px;
  flex: none;
  border: 1.5px solid var(--line-strong);
  border-radius: var(--radius-sm);
  background: var(--bg-surface);
  cursor: pointer;
  transition:
    background-color var(--motion-fast) var(--ease-out),
    border-color var(--motion-fast) var(--ease-out);
}
.checkbox:hover { border-color: var(--accent); }
.checkbox:checked,
.checkbox:indeterminate {
  background-color: var(--accent);
  border-color: var(--accent);
  background-repeat: no-repeat;
  background-position: center;
  background-size: 12px;
}
.checkbox:checked {
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none' stroke='white' stroke-width='2.5' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M3 8.5l3.5 3.5L13 5'/%3E%3C/svg%3E");
}
.checkbox:indeterminate {
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none' stroke='white' stroke-width='2.5' stroke-linecap='round'%3E%3Cpath d='M4 8h8'/%3E%3C/svg%3E");
}

.radio-row { display: flex; align-items: center; gap: var(--space-5); flex-wrap: wrap; }
.radio-row label { display: inline-flex; align-items: center; gap: var(--space-2); cursor: pointer; font-size: var(--text-sm); }

/* ==================================================================== табы
   Два вида, и они не взаимозаменяемы:
     .tabs        — разделы страницы, подчёркивание снизу;
     .segmented   — выбор одного из немногих равноправных (канал связи),
                    вид переключателя.  */
.tabs {
  display: flex;
  gap: var(--space-2);
  border-bottom: 1px solid var(--line);
}
.tabs__item {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  padding: var(--space-4) var(--space-2);
  margin-bottom: -1px;
  border-bottom: 2px solid transparent;
  color: var(--fg-muted);
  font-size: var(--text-sm);
  font-weight: var(--weight-medium);
  cursor: pointer;
  transition: color var(--motion-fast) var(--ease-out);
}
.tabs__item:hover { color: var(--fg); }
.tabs__item[aria-selected="true"] {
  color: var(--accent-fg);
  border-bottom-color: var(--accent);
}

.segmented {
  display: flex;
  gap: var(--space-3);
}
.segmented__item {
  flex: 1;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-3);
  min-height: 40px;
  padding: 0 var(--space-4);
  border: 1px solid var(--line);
  border-radius: var(--radius-md);
  background: var(--bg-surface);
  color: var(--fg-soft);
  font-size: var(--text-sm);
  font-weight: var(--weight-medium);
  cursor: pointer;
  transition:
    border-color var(--motion-fast) var(--ease-out),
    background-color var(--motion-fast) var(--ease-out);
}
.segmented__item:hover { border-color: var(--line-strong); background: var(--bg-hover); }
.segmented__item[aria-selected="true"],
/* Тот же вид у выбранной кнопки-переключателя. aria-pressed, а не
   aria-selected: selected — про пункт списка или вкладку, а «Тёмная» здесь
   именно кнопка, и экранный диктор должен прочесть её как нажатую. */
.segmented__item[aria-pressed="true"] {
  border-color: var(--accent);
  background: var(--accent-soft);
  color: var(--accent-fg);
  font-weight: var(--weight-semi);
}
/* Мелкий вариант: три подписи в выпадающем меню шириной с кнопку шапки. */
.segmented--sm { gap: var(--space-2); }
.segmented--sm .segmented__item {
  min-height: 30px;
  padding: 0 var(--space-3);
  font-size: var(--text-2xs);
  font-weight: var(--weight-normal);
}
/* Канал недоступен: показываем, но не даём выбрать — исчезнувший таб
   заставляет гадать, был ли он вообще. */
.segmented__item[aria-disabled="true"] {
  opacity: 0.5;
  cursor: not-allowed;
  pointer-events: none;
}

/* Заголовок графы, по которой можно упорядочить. Ссылка, а не кнопка: порядок
   живёт в адресе, значит его можно открыть в новой вкладке, положить в закладки
   и переслать — как и фильтры рядом.

   Стрелка стоит ВСЕГДА, даже у неактивной графы: знак «здесь можно нажать»
   обязан быть виден до наведения, иначе о сортировке узнают случайно. Пока
   графа не выбрана, стрелка двусторонняя и бледная. */
.table__sort {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  color: inherit;
  text-decoration: none;
  white-space: nowrap;
}
.table__sort:hover { color: var(--accent-fg); text-decoration: none; }
.table__sort:hover .table__sort-mark { opacity: 1; }
.table__sort-mark {
  font-size: var(--text-2xs);
  line-height: 1;
  opacity: 0.4;
  font-variant-numeric: tabular-nums;
}
.table__sort--on { color: var(--accent-fg); }
.table__sort--on .table__sort-mark { opacity: 1; }
.table__sort:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; border-radius: var(--radius-sm); }
/* Давность касания в графе «Компания». Своя метка, а не просто приглушённый
   текст: она появляется только при сортировке «давно не касались», и должно
   быть видно, что это ответ на выбранный порядок, а не ещё одно свойство лида. */
.touchnote {
  display: block;
  width: fit-content;
  margin-top: var(--space-2);
  padding: 1px var(--space-2);
  border-radius: var(--radius-sm);
  background: var(--bg-active);
  color: var(--fg-muted);
  font-size: var(--text-2xs);
  line-height: var(--leading-snug);
  white-space: nowrap;
}

/* ================================================================== таблица
   Плотная сетка на много строк. Заголовок прилипает к верху области
   прокрутки: на 25 строках подписи столбцов уезжают с экрана первыми. */
.table-wrap { overflow-x: auto; }
.table {
  width: 100%;
  font-size: var(--text-sm);
}
.table th {
  position: sticky;
  top: 0;
  z-index: 1;
  padding: var(--space-4) var(--space-5);
  background: var(--bg-sunken);
  border-bottom: 1px solid var(--line);
  color: var(--fg-muted);
  font-size: var(--text-xs);
  font-weight: var(--weight-semi);
  text-align: left;
  white-space: nowrap;
}
.table td {
  padding: var(--space-4) var(--space-5);
  border-bottom: 1px solid var(--line);
  vertical-align: middle;
}
/* --row-bg — фон строки одним значением. Нужен закреплённой графе «Компания»:
   она едет поверх остальных колонок и обязана быть непрозрачной, иначе сквозь
   неё видно уезжающий текст. Держать её фон отдельным списком правил значило бы
   рано или поздно с ним разойтись — поэтому цвет объявляется один раз на строке,
   а ячейка его читает. */
.table tbody tr {
  --row-bg: var(--bg-surface);
  transition: background-color var(--motion-fast) var(--ease-out);
}
.table tbody tr:hover { background: var(--bg-hover); --row-bg: var(--bg-hover); }
.table tbody tr:last-child td { border-bottom: none; }
.table__num { text-align: right; }
.table__tight { width: 1%; white-space: nowrap; }
/* Таблицы, где ячейки высокие и разной длины (выдача поиска): содержимое
   прижимается к верху. При выравнивании по центру домен и возраст уплывали
   в середину строки с кандидатами, и строка теряла общую линию чтения. */
.table--top td { vertical-align: top; }

/* Фиксированная раскладка: ширины колонок берутся из thead и НЕ пересчитываются
   по содержимому. Без неё ленивые ячейки (ИНН/контакты грузятся по прокрутке)
   меняли ширину столбцов, и весь текст «съезжал» при каждой подгрузке (CLS).
   Длинный неразрывный текст (домен, почта, ИНН) переносится внутри своей
   колонки, а не распирает её. */
.table--fixed { table-layout: fixed; }
.table--fixed td { overflow-wrap: anywhere; word-break: break-word; }

/* Плавающая копия шапки таблицы (js/stickyhead.js): висит под шапкой приложения,
   пока таблица в кадре, — заголовки столбцов не теряются при прокрутке страницы.
   Слой фиксированный и обрезает копию по своей ширине; сама копия сдвигается по
   горизонтали вслед за прокруткой таблицы. Ниже шапки приложения по z, чтобы её
   меню оставались сверху. Ячейки копии наследуют вид .table th (класс .table). */
.table-stickyhead {
  position: fixed;
  z-index: calc(var(--z-sticky) - 1);
  overflow: hidden;
  background: var(--bg-sunken);
  box-shadow: var(--shadow-md);
}
.table-stickyhead__table {
  table-layout: fixed;
  margin: 0;
}
/* В копии закреплять нечем: её двигает transform, а не прокрутка. Заголовок
   графы «Компания» сдвигается обратно вручную (js/stickyhead.js), и для этого
   ему нужна обычная относительная позиция, а не sticky. */
.table-stickyhead__table th.table__col-company { position: relative; }
.table-stickyhead__table th.table__col-company::after { display: none; }

/* Минимальные ширины колонок.

   Без них браузер ужимает столбец под доступное место — и «Максим Волгин»
   превращается в «Мак…», а при десятке колонок и в восемь пикселей. Ячейка
   с именем не должна сжиматься: лучше горизонтальная прокрутка у таблицы
   (она в .table-wrap и для того и заведена), чем нечитаемые имена.

   Ширины подобраны под самое длинное реальное значение: ФИО из трёх слов
   и название компании с формой собственности. Компания уступила место
   объединённой графе «ЛПР / Контакты»: её имя может усечься многоточием,
   а ФИО ЛПР — нет (см. [data-col="lpr"] ниже). */
/* Ручка растягивания на правой кромке карточки таблицы лидов: потянуть —
   страница расширяется под таблицу (js/leadcols.js). Полоска-указатель
   проявляется при наведении и во время перетаскивания. */
.card--resizable { position: relative; }
.table-resizer {
  position: absolute;
  top: 0;
  bottom: 0;
  right: 0;
  width: 12px;
  cursor: col-resize;
  z-index: 5;
  display: flex;
  align-items: center;
  justify-content: center;
  touch-action: none; /* палец тянет кромку, а не прокручивает страницу */
}
.table-resizer::after {
  content: "";
  width: 4px;
  height: 48px;
  border-radius: var(--radius-sm);
  background: var(--line-strong);
  opacity: 0;
  transition: opacity var(--motion-fast) var(--ease-out);
}
.table-resizer:hover::after { opacity: 1; }
.table-resizer.is-resizing::after { opacity: 1; background: var(--accent); }

/* --col-company-max — общий потолок ширины для ВСЕГО, что стоит в графе
   «Компания»: названия, домена и закреплённой заметки. Одно число на всех,
   потому что распирает колонку любой из них, а не только самый длинный.

   Графа закреплена у левого края: таблица шире экрана на пятьсот пикселей
   (2051 против 1550), и при прокрутке вбок пропадало название компании —
   становилось непонятно, чью строку правишь. Теперь уезжает всё, кроме неё. */
.table__col-company {
  min-width: 240px;
  --col-company-max: 340px;
  position: sticky;
  left: 0;
  z-index: 2;
  background: var(--row-bg, var(--bg-surface));
}
/* Тонкая грань справа отделяет закреплённую графу от уезжающего содержимого:
   без неё при прокрутке текст «наползает» на неё без видимой границы. */
.table__col-company::after {
  content: "";
  position: absolute;
  top: 0;
  bottom: 0;
  right: -1px;
  width: 1px;
  background: var(--line);
}
/* Шапка закреплена сразу по двум осям: и сверху (как все th), и слева. */
.table th.table__col-company { z-index: 3; background: var(--bg-sunken); }
/* Потолок названию: u-truncate в таблице с auto-layout сам по себе не режет —
   nowrap-текст задаёт min-content ячейки, и длинное имя распирало колонку
   почти до 500px, выталкивая таблицу в горизонтальную прокрутку даже
   в режиме «на всю ширину». Полное имя — в подсказке при наведении. */
.table__col-company a.u-truncate { max-width: var(--col-company-max); }

/* Значок стоп-листа в углу закреплённой графы «Компания». Ячейке оставлено место справа
   (padding), чтобы он не налезал на название. Виден при наведении на строку и при
   фокусе с клавиатуры; у компании в стоп-листе — всегда, красным: это состояние, а не
   действие. Без мыши (сенсорный экран) наведения нет — значок виден всегда. */
td.table__col-company { padding-right: var(--space-9); }
.rowstop {
  position: absolute;
  top: 50%;
  right: var(--space-2);
  transform: translateY(-50%);
  opacity: 0;
  transition: opacity var(--motion-fast) var(--ease-out);
}
.table tbody tr:hover .rowstop,
.rowstop:focus-visible,
.rowstop--on { opacity: 1; }
.rowstop--on { color: var(--danger-fg); }
@media (hover: none) { .rowstop { opacity: 1; } }
.table__col-person { min-width: 260px; }
.table__col-owner { min-width: 160px; }
.table__col-plan { min-width: 150px; }

/* Касание запланировано на сегодня: тёплая полоса слева и лёгкая заливка —
   список видно сканированием, кого сегодня трогать. Сама дата в ячейке идёт
   акцентом с тегом «сегодня» (см. .datebtn--today и pill «сегодня»): цвет
   дублируется словом ради доступности.

   Стоит ДО правила выбранной строки: специфичность одинаковая, и порядок решает —
   синий выбор перекрывает янтарь, какой лид открыт сейчас важнее пометки «на
   сегодня». Полоса и заливка — box-shadow и background, поэтому наведение (оно
   меняет только background) полосу не гасит. */
.table tbody tr.table__row--today {
  background: color-mix(in srgb, var(--warn-soft) 65%, transparent);
  /* Непрозрачный близнец того же цвета — для закреплённой графы. */
  --row-bg: color-mix(in srgb, var(--warn-soft) 65%, var(--bg-surface));
  box-shadow: inset 3px 0 0 var(--warn);
}
.table tbody tr.table__row--today:hover {
  background: var(--warn-soft);
  --row-bg: var(--warn-soft);
}

/* Ниша определяется фоном: лёгкая холодная заливка связывает статус в дальней
   колонке с компанией слева. Она заметна, но слабее явного выбора строки. */
.table tbody tr[data-niche-queued="true"] {
  background: color-mix(in srgb, var(--accent-soft) 58%, transparent);
  --row-bg: color-mix(in srgb, var(--accent-soft) 58%, var(--bg-surface));
  box-shadow: inset 2px 0 0 color-mix(in srgb, var(--accent) 65%, transparent);
}
.table tbody tr[data-niche-queued="true"]:hover {
  background: var(--accent-soft);
  --row-bg: var(--accent-soft);
}

/* Выбранная строка: заливка И полоса слева. Только заливкой обошлись бы, но
   при десятке отмеченных строк полоса даёт увидеть выбор, не вчитываясь. */
.table tbody tr[aria-selected="true"] {
  background: var(--accent-soft);
  --row-bg: var(--accent-soft);
  box-shadow: inset 3px 0 0 var(--accent);
}
.table tbody tr[aria-selected="true"]:hover {
  background: var(--accent-softer);
  --row-bg: var(--accent-softer);
}

/* Закреплённая заметка в строке таблицы. Приглушена, но с иконкой: её задача —
   попасться на глаза до звонка, а не соперничать с названием компании.

   Ширина ограничена ЯВНО, тем же потолком, что и название. Прежнего
   max-width: 100% не хватало: в таблице с table-layout: auto ширину колонки
   задаёт её содержимое, и «сто процентов» считались от того же распёртого
   столбца. Реплика на три строки растягивала графу «Компания» почти на весь
   экран и выдавливала остальные вбок (жалоба сейлза, 20.08.2026).

   Свёрнутая — одна строка с многоточием, нажатие разворачивает её целиком
   прямо в строке: строка подрастает, ширина колонки не меняется. Тот же приём,
   что и у окошек правки комментария рядом, — всплывающий слой резался бы
   краем прокрутки таблицы. */
.table__note {
  display: inline-flex;
  align-items: flex-start;
  gap: var(--space-2);
  margin-top: var(--space-2);
  padding: 2px var(--space-3);
  border: 0;
  border-radius: var(--radius-sm);
  background: var(--accent-soft);
  color: var(--accent-fg);
  font: inherit;
  font-size: var(--text-xs);
  line-height: var(--leading-snug, 1.35);
  text-align: left;
  max-width: var(--col-company-max, 340px);
  cursor: pointer;
}
.table__note:hover { background: var(--accent-softer); }
.table__note .icon { flex: none; margin-top: 3px; }
/* min-width: 0 обязателен: без него flex-элемент не сжимается ниже своего
   текста, и многоточие не появляется вовсе. */
.table__note-text {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
/* Раскрытая заметка ЗАНИМАЕТ свою ширину, а не отдаёт её соседям. Без явной
   width колонка схлопывалась до минимума: перенос по словам делает min-content
   ячейки крошечным, и auto-layout тут же раздавал место другим графам —
   заметка вытягивалась в восемь строк по одному слову. */
.table__note[aria-expanded="true"] { width: var(--col-company-max, 340px); }
.table__note[aria-expanded="true"] .table__note-text {
  white-space: normal;
  overflow: visible;
}
/* Пунктир — подсказка «здесь есть продолжение», и нужна она только свёрнутой:
   под раскрытым текстом в пять строк она читается как ошибка вёрстки. */
.table__note[aria-expanded="false"]:hover .table__note-text {
  text-decoration: underline dotted;
}

/* Сортируемый заголовок — кнопка, а не div с обработчиком: так он доступен
   с клавиатуры и озвучивается как элемент управления. */
.table__sort {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  color: inherit;
  font: inherit;
  cursor: pointer;
}
.table__sort:hover { color: var(--fg); }
.table__sort .icon { opacity: 0.45; }
.table__sort[aria-sort]:not([aria-sort="none"]) .icon { opacity: 1; color: var(--accent); }

/* Панель массовых действий над таблицей. Занимает место всегда, даже когда
   ничего не выбрано: появляющаяся полоса сдвигала бы таблицу под курсором. */
/* Скрытая подсказка обязана быть скрытой: .linkbtn ниже задаёт display тем же
   весом, что и правило браузера для [hidden], и авторское правило побеждает —
   без этой строки подсказка про колонки оставалась висеть с прежним текстом. */
[data-cols-hint][hidden] { display: none; }

.bulkbar {
  display: flex;
  align-items: center;
  gap: var(--space-4);
  padding: var(--space-4) var(--space-5);
  border-bottom: 1px solid var(--line);
  font-size: var(--text-sm);
  min-height: 52px;
}
.bulkbar__count { font-weight: var(--weight-semi); }
/* Счётчик ленивых подсказок: полсотни строк читаются минутами, и без него
   непонятно, всё ли доехало или консоль замерла. */
.bulkbar__progress { display: inline-flex; align-items: center; gap: var(--space-2); color: var(--fg-muted); }
.bulkbar__progress[data-done="all"] { color: var(--ok-fg); }
.bulkbar__tools { margin-left: auto; display: flex; align-items: center; gap: var(--space-3); }

/* stickyhead.js фиксирует саму рабочую панель, а не её копию: так кнопки,
   выпадающие меню и связь checkbox-ов с bulk-form остаются настоящими. */
.bulkbar--sticky {
  position: fixed;
  z-index: calc(var(--z-sticky) - 1);
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  background: var(--bg-surface);
  box-shadow: var(--shadow-md);
}

/* Панель фильтров выдачи: «скрывать уже в базе» + тип компании. Клиентские,
   мгновенные; настройка живёт в localStorage. Скрытая фильтром строка не
   рендерит ленивую ячейку и снята с выделения. */
.searchfilters {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: var(--space-3) var(--space-5);
  padding: var(--space-3) var(--space-5);
  border-bottom: 1px solid var(--line);
  font-size: var(--text-sm);
}
.searchfilters__group { display: inline-flex; align-items: center; gap: var(--space-3); }
.searchfilters label { display: inline-flex; align-items: center; gap: var(--space-2); cursor: pointer; }
.searchfilters__sep { color: var(--fg-muted); }
#search-rows tr[hidden] { display: none; }

/* Легенда столбцов под формой поиска: что за данные, откуда и что почём.
   Раньше это был абзац-стена мелким шрифтом — его не читали.
   Имя своё, не `.legend`: та занята легендой календаря (calendar.css) и
   подключается позже — совпадение имён молча ломало бы раскладку. */
.helplegend {
  display: grid;
  grid-template-columns: minmax(140px, 210px) minmax(0, 1fr);
  gap: var(--space-2) var(--space-5);
  margin: 0;
  max-width: 1100px;
}
.helplegend dt { color: var(--fg); font-size: var(--text-xs); font-weight: var(--weight-semi); }
.helplegend dd { margin: 0; color: var(--fg-muted); font-size: var(--text-xs); line-height: var(--leading-normal); }
@media (max-width: 860px) {
  .helplegend { grid-template-columns: minmax(0, 1fr); gap: var(--space-1) 0; }
  .helplegend dd { margin-bottom: var(--space-3); }
}

/* ================================ ячейка «Реквизиты и контакты» (Поиск)
   Строка выдачи — не факт о компании, а гипотеза, собранная из разных
   источников: страница сайта, сниппет выдачи, каталоги, реестр по бренду.
   Поэтому ячейка устроена не столбиком значений, а тремя блоками, которые
   отвечают на вопросы сейлза по порядку: ЧТО нашли → ОТКУДА → ЧТО сделать.

   Метки слева держат сетку: глаз идёт по одному столбцу подписей вместо того,
   чтобы вычитывать каждую строку целиком. Раньше здесь всё было одного кегля
   и цвета — номер ИНН, подпись реестра, кнопка-кандидат и заметка обхода
   выглядели одинаково, и ячейка читалась как свалка. */
.lookup { display: grid; gap: var(--space-2); font-size: var(--text-xs); }
.lookup__line {
  display: grid;
  grid-template-columns: 52px minmax(0, 1fr);
  gap: var(--space-3);
  align-items: baseline;
  margin: 0;
}
.lookup__key {
  color: var(--fg-muted);
  font-size: var(--text-2xs);
  text-transform: uppercase;
  letter-spacing: 0.04em;
  line-height: var(--leading-snug);
}
/* Многословные подписи («контакты со страниц сайта», «выберите ИНН…») капсом
   не набираются: капс читается по буквам и на длинной фразе тормозит глаз. */
.lookup__key--block {
  display: block;
  text-transform: none;
  letter-spacing: normal;
  font-size: var(--text-xs);
}
.lookup__value {
  display: inline-flex;
  align-items: center;
  flex-wrap: wrap;
  gap: var(--space-2);
  min-width: 0;
  color: var(--fg-soft);
}
.lookup__inn {
  font-size: var(--text-sm);
  font-weight: var(--weight-semi);
  color: var(--fg);
}
.lookup__party { color: var(--fg-soft); min-width: 0; overflow-wrap: anywhere; }
.lookup__alive { color: var(--ok-fg); }

/* Источник — мера доверия, поэтому цветом: со страницы сайта зелёный, сниппет
   и каталоги жёлтые (там ловятся тёзки). Курсор-вопрос обещает подсказку. */
.lookup__src { cursor: help; }
.lookup__src--ok .lookup__value { color: var(--ok-fg); }
.lookup__src--warn .lookup__value { color: var(--warn-fg); }

/* Кандидаты: это выбор человека, а не найденный ответ. Кнопка обязана
   выглядеть кнопкой — раньше номер стоял голым текстом, и его не нажимали. */
.lookup__choice { display: grid; gap: var(--space-2); }
.lookup__cand { margin: 0; }
.lookup__pick {
  display: grid;
  gap: 2px;
  width: 100%;
  padding: var(--space-2) var(--space-3);
  border: 1px solid var(--line);
  border-radius: var(--radius-md);
  background: var(--bg-surface);
  text-align: left;
  cursor: pointer;
  transition: border-color var(--motion-fast) var(--ease-out),
    background-color var(--motion-fast) var(--ease-out);
}
.lookup__pick:hover { border-color: var(--accent); background: var(--bg-hover); }
.lookup__pick-num { font-size: var(--text-sm); color: var(--fg); }
.lookup__pick-cap {
  color: var(--fg-muted);
  font-size: var(--text-xs);
  line-height: var(--leading-snug);
}
.lookup__pick-cap--dead { color: var(--warn-fg); }

.lookup__contacts { display: grid; gap: 2px; margin: 0; padding: 0; list-style: none; }
.lookup__contact { display: flex; align-items: center; gap: var(--space-2); color: var(--fg-soft); }
.lookup__more { color: var(--fg-muted); font-size: var(--text-2xs); }

/* Пустая ячейка обязана объяснить себя и подсказать ход. */
.lookup__status { display: flex; align-items: center; gap: var(--space-2); margin: 0; color: var(--fg-muted); }
.lookup__status--warn { color: var(--warn-fg); }
/* Пояснение к кнопкам читают глазами, а не наводят курсор, — значит это
   обычный текст, а не мелкий шрифт под звёздочкой. */
.lookup__advice { margin: 0; color: var(--fg-muted); font-size: var(--text-xs); line-height: var(--leading-normal); }

/* Действия всегда в одном месте и по одной левой кромке: раньше кнопки стояли
   вразнобой посреди текста, и было неясно, к чему они относятся. */
.lookup__acts {
  display: grid;
  gap: var(--space-2);
  margin-top: var(--space-1);
  padding-top: var(--space-3);
  border-top: 1px dashed var(--line);
}
.lookup__buttons { display: flex; flex-wrap: wrap; gap: var(--space-2); }
.lookup__act { flex: 1 1 140px; justify-content: flex-start; }
.lookup__act .spinner { margin-left: auto; }
.lookup__wait { align-items: center; gap: var(--space-2); margin: 0; color: var(--accent-fg); }

/* Пока идёт запрос, содержимое ячейки гаснет, но НЕ схлопывается: строка не
   должна прыгать под курсором (htmx вешает .htmx-request на саму ячейку). */
td.htmx-request .lookup > *:not(.lookup__acts) { opacity: 0.4; }
td.htmx-request .lookup__buttons { opacity: 0.6; }

/* Ручной ввод ИНН: подпись видима всегда — плейсхолдер исчезает при вводе, и
   отвлёкшийся сейлз уже не знает, что заполняет. */
/* Ручной ввод свёрнут: он нужен редко, а места занимает как целая строка. */
.lookup__fallback > summary {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  color: var(--fg-muted);
  font-size: var(--text-xs);
  cursor: pointer;
  list-style: none;
}
.lookup__fallback > summary::-webkit-details-marker { display: none; }
.lookup__fallback > summary:hover { color: var(--fg); }
.lookup__fallback[open] > summary { margin-bottom: var(--space-2); }

.lookup__manual { display: grid; gap: var(--space-2); margin: 0; }
.lookup__manual-row { display: flex; gap: var(--space-2); align-items: center; }
.lookup__manual-input {
  flex: 1 1 auto;
  min-width: 0;
  padding: var(--space-1) var(--space-3);
  font-size: var(--text-xs);
  font-family: var(--font-mono);
}
.lookup__error { color: var(--danger-fg); font-size: var(--text-2xs); }

/* Скелет ленивой подсказки. Полоски занимают ровно то место, которое займёт
   ответ: карточка не прыгает, когда данные доезжают (CLS). Текстом рядом
   сказано, что именно сейчас происходит и сколько это стоит. */
.lookup__skeleton { display: grid; gap: var(--space-2); }
.lookup__skeleton span {
  height: 9px;
  border-radius: var(--radius-sm);
  background: linear-gradient(90deg, var(--bg-hover) 25%, var(--bg-active) 37%, var(--bg-hover) 63%);
  background-size: 400% 100%;
  animation: lookup-shimmer 1.4s ease-in-out infinite;
}
.lookup__skeleton span:nth-child(1) { width: 70%; }
.lookup__skeleton span:nth-child(2) { width: 90%; }
.lookup__skeleton span:nth-child(3) { width: 55%; }
@keyframes lookup-shimmer {
  0% { background-position: 100% 50%; }
  100% { background-position: 0 50%; }
}
@media (prefers-reduced-motion: reduce) {
  .lookup__skeleton span { animation: none; }
}
.lookup__loading { display: grid; gap: var(--space-3); }
.lookup__loading-note { display: flex; align-items: center; gap: var(--space-2); color: var(--fg-muted); font-size: var(--text-2xs); }

/* ============================================== попап ручной правки ЛПР */
.lprmodal {
  width: min(420px, calc(100vw - 32px));
  padding: 0;
  border: 1px solid var(--line-strong);
  border-radius: var(--radius-xl);
  box-shadow: var(--shadow-xl);
  background: var(--bg-surface);
  /* Без явного цвета <dialog> берёт браузерный canvastext (чёрный) и в
     тёмной теме текст пропадает — страница не объявляет color-scheme. */
  color: var(--fg);
}
.lprmodal::backdrop { background: rgb(15 23 42 / 0.35); }
.lprmodal__head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-4);
  padding: var(--space-4) var(--space-4) var(--space-4) var(--space-5);
  border-bottom: 1px solid var(--line);
}
#lpr-modal-host { padding: var(--space-5); }
.lprform__error { color: var(--danger-fg); font-size: var(--text-sm); }

/* Строка человека в списке контактных лиц попапа. color: inherit обязателен:
   без него <button> берёт системный цвет кнопки, и в тёмной теме ФИО
   становилось тёмным на тёмном. */
.lprperson {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  width: 100%;
  padding: var(--space-3) var(--space-4);
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  background: none;
  color: inherit;
  font: inherit;
  text-align: left;
  cursor: pointer;
  transition: border-color var(--motion-fast) var(--ease-out);
}
.lprperson:hover { border-color: var(--line-strong); }
.lprperson__body { flex: 1; min-width: 0; display: grid; gap: 2px; }
.lprperson__name { font-weight: var(--weight-semi); font-size: var(--text-sm); }
.lprperson__role,
.lprperson__channel { font-size: var(--text-xs); }
.lprperson__go { color: var(--fg-muted); flex: none; }
.lprperson-entry {
  display: grid;
  min-width: 0;
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
}
.lprperson-entry > .lprperson { border: 0; }
.lprperson-entry:hover { border-color: var(--line-strong); }
.lprperson-entry__max { min-width: 0; margin: 0 var(--space-4) var(--space-3); }

/* Блок «Контактные лица» карточки: все лица компании списком, каждый со своими
   каналами и метками. Разделены линией; основной подсвечен слева акцентом. */
.lprpanel { display: grid; gap: var(--space-4); }
.lprpanel__person {
  display: grid;
  gap: var(--space-3);
  padding-bottom: var(--space-4);
  border-bottom: 1px solid var(--line);
}
.lprpanel__person:last-child { padding-bottom: 0; border-bottom: none; }
.lprpanel__person--primary {
  padding-left: var(--space-3);
  border-left: 2px solid var(--accent);
}
.lprpanel__top {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: var(--space-2);
}
.lprpanel__top .person { min-width: 0; }
.lprpanel__edit { flex: none; color: var(--fg-muted); }
.lprpanel__tags { display: flex; flex-wrap: wrap; gap: var(--space-2); }
.lprpanel__tags:empty { display: none; }
.lprpanel__make-primary { font-size: var(--text-xs); justify-self: start; }
/* «Найти ТГ» / ссылка на Telegram стоит сразу под номером — тесно к нему,
   как продолжение телефона, а не отдельным блоком. */
.lprpanel__tg { display: grid; gap: var(--space-2); margin-top: calc(var(--space-2) * -1); }
.lprpanel__tg form { display: contents; }

/* «ещё N контактов» под ФИО в графе списка. */
.personcell__more { font-size: var(--text-2xs); color: var(--fg-muted); }
.personcell__more:hover { color: var(--accent-fg); }

/* Развёрнутый выбор этапа в ячейке списка: компактный select и крестик. */
.stagecell { display: inline-flex; align-items: center; gap: var(--space-1); }
.stagecell__select {
  width: auto;
  min-width: 150px;
  height: 30px;
  padding: 2px var(--space-2);
  font-size: var(--text-xs);
}

/* Личный канал в форме попапа: значение и крестик. */
.lprchannel {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-3);
  min-width: 0;
}
.lprchannel__value { font-size: var(--text-sm); }

/* ==================================================== попап карточки лида */
/* Почти весь экран: внутри полноценная трёхколоночная карточка, ей нужна
   ширина отдельной страницы. Отступ до краёв оставляет видимой подложку —
   по ней видно, что это окно поверх списка, и по ней же оно закрывается. */
.cardmodal {
  width: min(1560px, calc(100vw - 48px));
  height: calc(100vh - 48px);
  padding: 0;
  border: 1px solid var(--line-strong);
  border-radius: var(--radius-xl);
  overflow: hidden;
  box-shadow: var(--shadow-xl);
  background: var(--bg-page);
  color: var(--fg); /* иначе <dialog> берёт canvastext — чёрный и в тёмной теме */
}
.cardmodal[open] { display: grid; grid-template-rows: auto 1fr; }
.cardmodal::backdrop { background: rgb(15 23 42 / 0.45); }
.cardmodal__head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-4);
  padding: var(--space-2) var(--space-3) var(--space-2) var(--space-5);
  border-bottom: 1px solid var(--line);
  background: var(--bg-surface);
}
.cardmodal__content {
  width: 100%;
  height: 100%;
  min-width: 0;
  min-height: 0;
  overflow: auto;
  padding: 0 var(--space-7) var(--space-7);
  background: var(--bg-page);
  /* Прокрутка карточки не выходит за край окна и не цепляет фон под ним. */
  overscroll-behavior: contain;
}
.cardmodal__content > [data-live-page] {
  width: 100%;
  max-width: var(--shell-max);
  margin: 0 auto;
}
.cardmodal__content .record__rail--sticky { position: static; top: auto; }
.cardmodal__loading {
  min-height: 100%;
  display: grid;
  place-items: center;
}

/* Фон под открытым попапом заморожен: прокрутка внутри карточки не листает
   список лидов. Отступ компенсирует ширину исчезнувшей полосы прокрутки, чтобы
   страница не сдвигалась при открытии (--scrollbar-gap ставит js/cardmodal.js). */
html.is-modal-open {
  overflow: hidden;
  padding-right: var(--scrollbar-gap, 0px);
}

/* ============================================================== KPI-плитка */
.kpi {
  display: flex;
  align-items: flex-start;
  gap: var(--space-4);
  padding: var(--space-5);
  background: var(--bg-surface);
  border: 1px solid var(--line);
  border-radius: var(--radius-xl);
}
.kpi__body { min-width: 0; display: grid; gap: var(--space-1); }
.kpi__label { color: var(--fg-muted); font-size: var(--text-xs); }
.kpi__value {
  font-size: var(--text-2xl);
  font-weight: var(--weight-bold);
  line-height: var(--leading-tight);
  letter-spacing: var(--tracking-tight);
}
.kpi__note { color: var(--fg-muted); font-size: var(--text-xs); }
.kpi__delta { display: inline-flex; align-items: center; gap: var(--space-1); font-weight: var(--weight-medium); }
.kpi__delta--up { color: var(--ok-fg); }
.kpi__delta--down { color: var(--danger-fg); }
/* Значок в мягком квадрате — как на макете. Прижат вправо. */
.kpi__icon {
  margin-left: auto;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 36px;
  height: 36px;
  flex: none;
  border-radius: var(--radius-lg);
  background: var(--bg-hover);
  color: var(--fg-muted);
}
.kpi__icon--accent { background: var(--accent-soft); color: var(--accent); }
.kpi__icon--ok { background: var(--ok-soft); color: var(--ok); }
.kpi__icon--warn { background: var(--warn-soft); color: var(--warn); }
.kpi__icon--info { background: var(--info-soft); color: var(--info); }

/* ================================================================= степпер
   Вертикальные вехи сделки. Соединительная линия рисуется псевдоэлементом от
   кружка, а не отдельным блоком: так она не разъезжается при разной высоте
   подписей. У последнего шага линии нет. */
.stepper { display: grid; gap: 0; }
.step {
  display: grid;
  grid-template-columns: 24px minmax(0, 1fr);
  gap: var(--space-4);
  padding-bottom: var(--space-5);
  position: relative;
}
.step:last-child { padding-bottom: 0; }
.step::before {
  content: "";
  position: absolute;
  left: 11px;
  top: 24px;
  bottom: 0;
  width: 2px;
  background: var(--line);
}
.step:last-child::before { display: none; }
.step__mark {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 24px;
  height: 24px;
  border-radius: 50%;
  background: var(--bg-hover);
  color: var(--fg-muted);
  font-size: var(--text-xs);
  font-weight: var(--weight-semi);
  position: relative;
  z-index: 1;
}
.step__label {
  align-self: center;
  font-size: var(--text-sm);
  color: var(--fg-muted);
}
.step__note { display: block; font-size: var(--text-xs); color: var(--fg-muted); }

.step--done .step__mark { background: var(--ok-soft); color: var(--ok-fg); }
.step--done::before { background: var(--ok); opacity: 0.35; }
.step--done .step__label { color: var(--fg-soft); }
.step--current .step__mark { background: var(--accent); color: var(--accent-on); }
.step--current .step__label { color: var(--fg); font-weight: var(--weight-semi); }

/* ============================================================ воронка сделки
   Живая хронология этапов: тот же вертикальный коннектор, что у .stepper, но
   строка несёт дату, автора и действия (сделать текущим / удалить). */
.funnel__intro { font-size: var(--text-xs); margin: 0 0 var(--space-4); }
.funnel { display: grid; gap: 0; margin: 0; padding: 0; list-style: none; }
.funnel__step {
  display: grid;
  grid-template-columns: 24px minmax(0, 1fr) auto;
  gap: var(--space-4);
  padding-bottom: var(--space-4);
  position: relative;
}
.funnel__step:last-child { padding-bottom: 0; }
.funnel__step::before {
  content: "";
  position: absolute;
  left: 11px; top: 24px; bottom: 0;
  width: 2px;
  background: var(--line);
}
.funnel__step:last-child::before { display: none; }
.funnel__mark {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 24px; height: 24px;
  border-radius: 50%;
  background: var(--bg-hover);
  color: var(--fg-muted);
  font-size: var(--text-xs);
  font-weight: var(--weight-semi);
  position: relative;
  z-index: 1;
}
.funnel__body { min-width: 0; align-self: center; }
.funnel__head { display: flex; align-items: center; gap: var(--space-2); flex-wrap: wrap; }
.funnel__label {
  font-size: var(--text-sm);
  color: var(--fg-soft);
  font-weight: var(--weight-medium);
  overflow-wrap: anywhere;
}
.funnel__now { font-size: var(--text-2xs); }
.funnel__meta {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  margin-top: 2px;
  font-size: var(--text-2xs);
  color: var(--fg-muted);
}
.funnel__date { display: inline-flex; align-items: center; gap: var(--space-1); }
.funnel__date .icon { width: 12px; height: 12px; }
.funnel__author { white-space: nowrap; }
.funnel__actions {
  display: inline-flex;
  align-items: center;
  gap: var(--space-1);
  align-self: center;
  opacity: 0;
  transition: opacity 120ms ease;
}
.funnel__step:hover .funnel__actions,
.funnel__step:focus-within .funnel__actions { opacity: 1; }
.funnel__del:hover { color: var(--danger-fg); }

.funnel__step--done .funnel__mark { background: var(--ok-soft); color: var(--ok-fg); }
.funnel__step--done::before { background: var(--ok); opacity: 0.35; }
.funnel__step--current .funnel__mark {
  background: var(--accent);
  color: var(--accent-on);
  box-shadow: 0 0 0 4px var(--accent-soft);
}
.funnel__step--current .funnel__label { color: var(--fg); font-weight: var(--weight-semi); }

.funnel-add {
  margin-top: var(--space-5);
  padding-top: var(--space-5);
  border-top: 1px solid var(--line);
  display: grid;
  gap: var(--space-4);
}
.funnel-add__title { font-size: var(--text-sm); font-weight: var(--weight-semi); color: var(--fg); }
.funnel-add__flag {
  display: flex;
  align-items: flex-start;
  gap: var(--space-2);
  font-size: var(--text-sm);
  cursor: pointer;
}
.funnel-add__flag input { margin-top: 3px; flex: none; }
.funnel-add__flaghint { display: block; font-size: var(--text-2xs); margin-top: 2px; }

/* ================================================================== аватар */
.avatar {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 32px;
  height: 32px;
  flex: none;
  border-radius: 50%;
  background: var(--accent-soft);
  color: var(--accent-fg);
  font-size: var(--text-xs);
  font-weight: var(--weight-semi);
  text-transform: uppercase;
  letter-spacing: 0;
}
.avatar--sm { width: 24px; height: 24px; font-size: var(--text-2xs); }
.avatar--lg { width: 40px; height: 40px; font-size: var(--text-sm); }

/* Человек: аватар + имя + роль. */
.person { display: inline-flex; align-items: center; gap: var(--space-3); min-width: 0; }
.person__body { min-width: 0; display: grid; }
.person__name { font-size: var(--text-sm); font-weight: var(--weight-medium); }
.person__role { font-size: var(--text-xs); color: var(--fg-muted); }

/* ================================================================= выноска
   Блок, объясняющий что-то рядом с работой: подсказка модели, предупреждение,
   ошибка. Всегда с иконкой — по ней тон читается раньше, чем текст. */
.callout {
  display: flex;
  gap: var(--space-4);
  padding: var(--space-4) var(--space-5);
  border-radius: var(--radius-lg);
  background: var(--bg-sunken);
  border: 1px solid var(--line);
  font-size: var(--text-sm);
  line-height: var(--leading-snug);
}
.callout__icon { flex: none; margin-top: 2px; color: var(--fg-muted); }
.callout__body { min-width: 0; display: grid; gap: var(--space-2); overflow-wrap: anywhere; }
.callout__title { font-weight: var(--weight-semi); }
.callout--accent { background: var(--accent-soft); border-color: transparent; color: var(--accent-fg); }
.callout--accent .callout__icon { color: var(--accent); }
.callout--ok { background: var(--ok-soft); border-color: transparent; color: var(--ok-fg); }
.callout--ok .callout__icon { color: var(--ok); }
.callout--warn { background: var(--warn-soft); border-color: transparent; color: var(--warn-fg); }
.callout--warn .callout__icon { color: var(--warn); }
.callout--danger { background: var(--danger-soft); border-color: transparent; color: var(--danger-fg); }
.callout--danger .callout__icon { color: var(--danger); }

/* Домен, помеченный нецелевым: строка остаётся, но перестаёт звать в работу. */
.lookup__dismissed {
  color: var(--fg-muted);
  font-size: var(--text-xs);
  margin-right: var(--space-2);
}

/* ------------------------------------------------------ история запросов
   Повторить вчерашний запрос должно быть быстрее, чем набрать его заново:
   ответ лежит в кэше выдачи и не стоит ни копейки. */
.history { margin-top: var(--space-4); }
.history__label {
  display: block;
  color: var(--fg-muted);
  font-size: var(--text-xs);
  margin-bottom: var(--space-2);
}
.history__list {
  margin: 0;
  padding: 0;
  list-style: none;
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2);
}
.history__item {
  display: inline-flex;
  align-items: baseline;
  gap: var(--space-2);
  max-width: 100%;
  padding: 2px var(--space-3);
  border: 1px solid var(--line);
  border-radius: var(--radius-pill, 999px);
  background: var(--bg-sunken);
  color: var(--fg);
  font-size: var(--text-xs);
  text-decoration: none;
}
.history__item:hover { border-color: var(--accent); }
.history__query { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; max-width: 28ch; }
.history__meta { color: var(--fg-muted); }

/* ------------------------------------------------- гипотеза из файла
   Пункты идеи и недостатков несут ссылки на источники: сплошным абзацем
   они читаются как мусор, поэтому — список с маркером и переносом. */
.hypothesis {
  margin: var(--space-2) 0 0;
  padding-left: var(--space-4);
  display: grid;
  gap: var(--space-3);
  font-size: var(--text-sm);
  line-height: var(--leading-snug);
  list-style: disc;
}
.hypothesis li::marker { color: var(--fg-muted); }

/* ============================================================== пустой экран
   Пустота обязана говорить, ЧТО сделать, а не только что ничего нет.
   «Ничего не найдено» без следующего шага — тупик. */
.empty {
  display: grid;
  gap: var(--space-4);
  justify-items: start;
  padding: var(--space-8) var(--space-6);
  border: 1px dashed var(--line-strong);
  border-radius: var(--radius-lg);
  background: var(--bg-sunken);
}
.empty--center { justify-items: center; text-align: center; }
.empty__icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 40px;
  height: 40px;
  border-radius: var(--radius-lg);
  background: var(--bg-hover);
  color: var(--fg-muted);
}
.empty__title { font-size: var(--text-md); font-weight: var(--weight-semi); }
.empty__text { color: var(--fg-muted); font-size: var(--text-sm); max-width: 62ch; }

/* ================================================================= раскрытие
   Справочное, что нужно не каждый раз: каталог продуктов, история касаний.
   Свёрнуто по умолчанию, но подписано так, чтобы было ясно, что внутри. */
.disclose { border-top: 1px solid var(--line); }
.disclose:first-child { border-top: none; }
.disclose > summary {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  padding: var(--space-4) 0;
  cursor: pointer;
  list-style: none;
  font-size: var(--text-sm);
  font-weight: var(--weight-medium);
  color: var(--fg-soft);
}
.disclose > summary::-webkit-details-marker { display: none; }
.disclose > summary:hover { color: var(--fg); }
.disclose__chevron { transition: transform var(--motion-fast) var(--ease-out); color: var(--fg-muted); }
.disclose[open] .disclose__chevron { transform: rotate(90deg); }
.disclose__hint { margin-left: auto; color: var(--fg-muted); font-size: var(--text-xs); font-weight: var(--weight-normal); }
.disclose__body { padding-bottom: var(--space-5); }

/* ==================================================================== лента
   События по лиду: реплики людей и записи автоматики. Записи автоматики
   приглушены — их много, и они фон для человеческих. */
.feed { display: grid; gap: var(--space-2); list-style: none; }
.feed__item {
  display: grid;
  grid-template-columns: 28px minmax(0, 1fr);
  gap: var(--space-4);
  padding: var(--space-4) 0;
  border-top: 1px solid var(--line);
}
.feed__item:first-child { border-top: none; }
.feed__icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 28px;
  height: 28px;
  border-radius: var(--radius-md);
  background: var(--bg-hover);
  color: var(--fg-muted);
}
.feed__icon--accent { background: var(--accent-soft); color: var(--accent); }
.feed__icon--ok { background: var(--ok-soft); color: var(--ok); }
.feed__body { min-width: 0; display: grid; gap: var(--space-2); }
.feed__head { display: flex; align-items: baseline; gap: var(--space-3); flex-wrap: wrap; }
.feed__text { font-size: var(--text-sm); line-height: var(--leading-snug); white-space: pre-wrap; }
.feed__meta { margin-left: auto; display: flex; align-items: baseline; gap: var(--space-3); color: var(--fg-muted); font-size: var(--text-xs); }
.feed__at { font-variant-numeric: tabular-nums; white-space: nowrap; }
.feed__item--system .feed__text { color: var(--fg-muted); }
.feed__item--pinned { background: var(--accent-soft); border-radius: var(--radius-md); padding-inline: var(--space-4); }
.feed__actions { display: flex; gap: var(--space-4); flex-wrap: wrap; }

/* Технические записи скрыты, пока сейлз сам не включит тумблер: их в разы
   больше, чем реплик, и они забивают ленту. Правило живёт в CSS, а не в
   Jinja — включённое состояние (класс feed--show-tech) накатывает js/feed.js
   после каждой перерисовки ленты htmx-ом. */
.feed:not(.feed--show-tech) .feed__item--tech { display: none; }

.feed__toolbar {
  display: flex;
  align-items: center;
  justify-content: flex-end;
  gap: var(--space-3);
  margin-bottom: var(--space-4);
}
.feed__toolbar-label { font-size: var(--text-xs); color: var(--fg-muted); }

/* Тумблер общего вида — checkbox под капотом, трек и бегунок поверх него.
   Пригодится за пределами ленты, если появится ещё один переключатель
   вида «показывать/скрывать». */
.switch { position: relative; display: inline-flex; align-items: center; cursor: pointer; }
.switch input {
  position: absolute;
  opacity: 0;
  width: 100%;
  height: 100%;
  margin: 0;
  cursor: pointer;
}
.switch__track {
  width: 34px;
  height: 20px;
  border-radius: var(--radius-full);
  background: var(--bg-hover);
  border: 1px solid var(--line);
  transition: background-color 0.15s ease, border-color 0.15s ease;
}
.switch__thumb {
  display: block;
  width: 14px;
  height: 14px;
  margin: 2px;
  border-radius: 50%;
  background: var(--bg-surface);
  box-shadow: 0 1px 2px rgb(0 0 0 / 0.25);
  transition: transform 0.15s ease;
}
.switch input:checked + .switch__track { background: var(--accent); border-color: var(--accent); }
.switch input:checked + .switch__track .switch__thumb { transform: translateX(16px); }
.switch input:focus-visible + .switch__track { outline: 2px solid var(--accent); outline-offset: 2px; }

/* ---------------------------------------------------- запись разговора в ленте
   Плеер живёт прямо в записи ленты: сейлз слушает разговор там же, где читает
   историю лида, — без перехода в чужой кабинет и без скачивания файла.
   Диаграмму звука рисует js/player.js в canvas; здесь только её размеры и
   цвета, которые скрипт читает из переменных (--wave-*), чтобы тёмная тема
   не требовала второй реализации отрисовки. */
.call {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  padding: var(--space-2) var(--space-3);
  border: 1px solid var(--line);
  border-radius: var(--radius-md);
  background: var(--bg-hover);
  --wave-played: var(--accent);
  --wave-rest: var(--line-strong);
}
.call__play {
  flex: none;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 28px;
  height: 28px;
  border: none;
  border-radius: var(--radius-full);
  background: var(--accent);
  color: #fff;
  cursor: pointer;
}
.call__play:hover { filter: brightness(1.05); }
/* touch-action: none — палец тянет ползунок перемотки, а не прокручивает
   ленту под ним; без этого протяжка по диаграмме на сенсорном экране
   пролистывала карточку. */
.call__wave {
  flex: 1 1 auto;
  min-width: 0;
  height: 40px;
  cursor: pointer;
  touch-action: none;
  border-radius: var(--radius-sm);
}
.call__wave:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.call__native {
  display: none;
  flex: 1 1 auto;
  min-width: 0;
  height: 32px;
}
/* Встроенный <audio> — не второй способ слушать запись, а страховка. Если
   модуль JS не загрузился или упал на конкретном браузере, пользователь всё
   равно получает обычный, проверенный плеер вместо неактивной кнопки. */
.call[data-call]:not([data-player-ready="1"]) .call__play,
.call[data-call]:not([data-player-ready="1"]) .call__wave,
.call[data-call][data-player-fallback="1"] .call__play,
.call[data-call][data-player-fallback="1"] .call__wave {
  display: none;
}
.call[data-call]:not([data-player-ready="1"]) .call__native,
.call[data-call][data-player-fallback="1"] .call__native {
  display: block;
}
.call[data-call]:not([data-player-ready="1"]) .call__time,
.call[data-call][data-player-fallback="1"] .call__time {
  display: none;
}
.call__time {
  flex: none;
  font-size: var(--text-xs);
  color: var(--fg-muted);
  font-variant-numeric: tabular-nums;
}
.call__save { flex: none; color: var(--fg-muted); display: inline-flex; }
.call__save:hover { color: var(--accent); }
.call__icon { display: inline-flex; color: var(--fg-muted); }
.call__meta {
  font-size: var(--text-xs);
  display: flex;
  gap: var(--space-2);
  flex-wrap: wrap;
  font-variant-numeric: tabular-nums;
}

/* Кнопка звонка на месте номера: сам номер остаётся читаемым и копируемым,
   а состояние («звоним», причина отказа) встаёт под ним, не сдвигая панель. */
.callbtn { display: grid; gap: var(--space-2); }
/* Номер и кнопка «копировать» — в одну строку; сам номер тянется, значок
   копирования прижат к нему справа. В карточке номер-кнопка во всю ширину,
   поэтому flex:1; в строке таблицы номер короткий и значок стоит сразу за ним. */
.callbtn__line { display: flex; align-items: center; gap: var(--space-2); min-width: 0; }
.callbtn__line > .btn { flex: 1; min-width: 0; }
.callbtn__copy { flex: none; color: var(--fg-muted); }
.callbtn__copy:hover { color: var(--accent-fg); }
/* Идёт звонок: зелёный кружок с «пингом» вместо строки текста — синк с UIS
   активен. Пульс — псевдоэлемент (transform+opacity), чтобы не трогать поток. */
.callbtn__live {
  position: relative;
  flex: none;
  width: 9px;
  height: 9px;
  border-radius: 50%;
  background: var(--ok);
}
.callbtn__live::after {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: 50%;
  background: var(--ok);
  animation: callbtn-ping 1.5s cubic-bezier(0, 0, 0.2, 1) infinite;
}
@keyframes callbtn-ping {
  0% { transform: scale(1); opacity: 0.55; }
  80%, 100% { transform: scale(2.6); opacity: 0; }
}
@media (prefers-reduced-motion: reduce) {
  .callbtn__live::after { animation: none; }
}
/* Вариант-иконка (список задач): кружок стоит на своей строке — прижмём вправо,
   под кнопку-трубку, чтобы не болтался по центру. */
.callbtn--icon .callbtn__live { margin-left: auto; }
.callbtn__note { font-size: var(--text-xs); line-height: var(--leading-snug); }
.callbtn__note--ok { color: var(--ok); }
.callbtn__note--bad { color: var(--danger); }

/* Тот же звонок, но в строке таблицы и в боковой сводке: рядом стоят ещё три
   канала, и кнопка во всю ширину на каждый номер превратила бы ячейку
   в простыню. Номер остаётся строчкой текста — как раньше ссылка tel:, —
   а трубка проявляется при наведении и в фокусе: ячейка не должна пестреть
   значками, пока по ней просто ведут глазами.

   Высота под палец добирается вертикальными полями: рекомендованные 44px
   в строку таблицы не поставить, но 28px против прежних 16 — уже попадаемо
   мышью и на сенсорном экране. */
.callbtn--inline { gap: 0; }
.callbtn__inline {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  min-height: 24px;
  padding: 2px var(--space-2);
  margin-left: calc(var(--space-2) * -1);
  border: 0;
  border-radius: var(--radius-sm);
  background: none;
  color: var(--accent-fg);
  font: inherit;
  cursor: pointer;
}
.callbtn__inline:hover { background: var(--accent-soft); }
.callbtn__inline .icon { flex: none; opacity: 0; color: var(--fg-muted); }
.callbtn__inline:hover .icon,
.callbtn__inline:focus-visible .icon { opacity: 1; }
.callbtn__inline[disabled] { cursor: progress; opacity: 0.6; }
/* UIS не настроен — номер уводит в приложение по умолчанию. Вид тот же, но
   без заливки при наведении: обещать звонок из консоли здесь нечем. */
.callbtn__inline--tel { color: inherit; }
.callbtn--inline .callbtn__note { padding-left: var(--space-2); }

/* Только трубка — для списка задач. Номер уходит в подсказку: в строке задачи
   он не сведение, а действие, и одинаковые цифры в каждой строке читаются
   столбцом шума. 32px — компромисс между рекомендованными 44 и плотностью
   списка: пальцем попадаешь, строку не раздувает. */
.callbtn--icon { justify-items: end; }
.callbtn__icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 32px;
  height: 32px;
  border: 1px solid var(--line-strong);
  border-radius: var(--radius-md);
  background: var(--bg-surface);
  color: var(--fg-muted);
  cursor: pointer;
}
.callbtn__icon:hover { border-color: var(--accent); color: var(--accent); background: var(--accent-soft); }
.callbtn__icon:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.callbtn__icon[disabled] { cursor: progress; opacity: 0.6; }
.callbtn--icon .callbtn__note {
  font-size: var(--text-2xs);
  text-align: right;
  max-width: 160px;
}

/* ======================================================= вложения в ленте
   Файл к реплике: запись разговора, принесённая руками, снимок переписки,
   документ от клиента. Аудио разворачивается в тот же проигрыватель, что и
   запись из UIS, — сейлзу всё равно, кто её принёс, слушать он будет одинаково.

   Значок, имя и размер стоят в одну строку: в ленте у реплики уже есть шапка
   с автором и временем, и вложение не должно спорить с ней за внимание. */
.attachments { display: grid; gap: var(--space-2); margin-top: var(--space-3); }
.attachment {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  min-width: 0;
  padding: var(--space-2) var(--space-3);
  border: 1px solid var(--line);
  border-radius: var(--radius-md);
  background: var(--bg-surface);
  color: var(--fg-soft);
  font-size: var(--text-xs);
}
a.attachment:hover { border-color: var(--accent); color: var(--accent-fg); }
.attachment .icon { flex: none; color: var(--fg-muted); }
.attachment__name { min-width: 0; flex: 1 1 auto; }
.attachment__size { flex: none; font-variant-numeric: tabular-nums; }
.attachment__go { margin-left: auto; }
/* Аудио: подпись сверху, проигрыватель под ней — иначе имя файла отняло бы
   у диаграммы звука половину ширины. */
.attachment--audio {
  display: grid;
  gap: var(--space-2);
  align-items: stretch;
  border-color: var(--line);
}
.attachment--audio .attachment__name { display: flex; align-items: center; gap: var(--space-2); }
.attachment--audio .call { border: 0; padding: 0; background: none; }

/* ============================================================ снимки в ленте
   Сетка миниатюр одного размера. Квадрат, а не пропорции снимка: строка из
   разновысоких прямоугольников читается как сбой вёрстки, а найти нужную
   фотографию в ней не быстрее — узнают её по содержимому, а не по форме. */
.shots {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(110px, 1fr));
  gap: var(--space-2);
  margin-top: var(--space-3);
  /* Шире не растягиваем даже на широкой карточке: пять миниатюр во всю ширину
     ленты выглядят галереей, а это вложение к реплике, а не она сама. */
  max-width: 480px;
}
.shot {
  position: relative;
  display: block;
  /* Не квадрат: снимок экрана и скан документа почти всегда шире, чем выше,
     и в квадрате от них остаются поля сверху и снизу. */
  aspect-ratio: 4 / 3;
  padding: 0;
  border: 1px solid var(--line);
  border-radius: var(--radius-md);
  overflow: hidden;
  /* Подложка заметно темнее страницы. Снимок документа или переписки — почти
     белый лист, и вписанный он оставляет поля: на светлой подложке границы
     листа теряются, и плитка читается как пустая рамка. На серой — как лист. */
  background: var(--bg-active);
  cursor: zoom-in;
}
/* Снимок вписывается целиком (contain), а не заполняет плитку обрезкой (cover).
   Обрезка тут врёт тем сильнее, чем полезнее снимок: у широкой картинки cover
   подгоняет по высоте и срезает бока, и от листа формата А4 в плитке остаётся
   вертикальная полоса шириной около десятой части — то есть поле страницы.
   Вписанный снимок узнаётся по общему виду: столбик диалога, шапка документа,
   фотография детали. Читать текст с миниатюры всё равно никто не станет. */
.shot__img {
  width: 100%;
  height: 100%;
  object-fit: contain;
  display: block;
}
.shot:hover { border-color: var(--accent); }
.shot:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

/* ================================================== просмотрщик снимка
   Окно во весь экран: снимок вписан целиком, панель с именем и кнопками —
   поверх него, а не рядом, чтобы фотографии доставалась вся высота. */
.lightbox {
  width: 100vw;
  max-width: 100vw;
  height: 100dvh;
  max-height: 100dvh;
  margin: 0;
  padding: 0;
  border: 0;
  background: rgba(15, 23, 42, 0.94);
  color: #fff;
  overflow: hidden;
}
.lightbox::backdrop { background: rgba(15, 23, 42, 0.8); }
.lightbox__bar {
  position: absolute;
  inset: 0 0 auto 0;
  z-index: 2;
  display: flex;
  align-items: center;
  gap: var(--space-3);
  padding: var(--space-3) var(--space-4);
  /* Тень вместо сплошной полосы: подпись читается на любом снимке, а верх
     фотографии при этом не отрезан панелью. */
  background: linear-gradient(to bottom, rgba(15, 23, 42, 0.75), transparent);
}
.lightbox__name { font-size: var(--text-sm); font-weight: var(--weight-medium); }
.lightbox__meta { flex: none; font-size: var(--text-xs); color: rgba(255, 255, 255, 0.7); }
.lightbox__tools { margin-left: auto; display: flex; gap: var(--space-2); flex: none; }
.lightbox__tools .btn { color: #fff; }
.lightbox__tools .btn:hover { background: rgba(255, 255, 255, 0.16); }
.lightbox__stage {
  width: 100%;
  height: 100%;
  display: flex;
  padding: var(--space-6);
  overflow: auto;
}
/* margin:auto, а не justify-content:center. Оба центрируют, но центрирование
   раскладкой обрезает то, что больше контейнера: увеличенный снимок терял бы
   левый край, и до него было бы не домотать. С автополями он центрирован, пока
   влезает, и прокручивается целиком, когда перестал. */
.lightbox__img {
  margin: auto;
  max-width: 100%;
  max-height: 100%;
  object-fit: contain;
  cursor: zoom-in;
}
/* Во весь размер: ограничения снимаются, и по снимку можно возить прокруткой —
   так читают мелкий шрифт на скане счёта. */
.lightbox--zoom .lightbox__img { max-width: none; max-height: none; cursor: zoom-out; }
.lightbox__step {
  position: absolute;
  top: 50%;
  z-index: 2;
  transform: translateY(-50%);
  display: grid;
  place-items: center;
  width: 44px;
  height: 44px;
  border: 0;
  border-radius: 50%;
  background: rgba(15, 23, 42, 0.6);
  color: #fff;
  cursor: pointer;
}
.lightbox__step:hover { background: rgba(15, 23, 42, 0.85); }
.lightbox__step:focus-visible { outline: 2px solid #fff; outline-offset: 2px; }
.lightbox__step--prev { left: var(--space-4); }
.lightbox__step--next { right: var(--space-4); }
.lightbox__step[hidden] { display: none; }
/* Счётчик лежит поверх нижнего края снимка, а снимок бывает любым: на белом
   скане счёта белые цифры без подложки пропадают. Та же затемняющая полоса,
   что и под верхней панелью, — она же отделяет счётчик от края экрана. */
.lightbox__foot {
  position: absolute;
  inset: auto 0 0 0;
  z-index: 2;
  padding: var(--space-6) 0 var(--space-4);
  background: linear-gradient(to top, rgba(15, 23, 42, 0.75), transparent);
  text-align: center;
  font-size: var(--text-xs);
  color: rgba(255, 255, 255, 0.85);
  font-variant-numeric: tabular-nums;
  pointer-events: none;
}
.lightbox__foot:empty { display: none; }
@media (max-width: 640px) {
  .lightbox__stage { padding: var(--space-4); }
  .lightbox__step { width: 40px; height: 40px; }
}

/* ========================================================= композер реплики
   Форма комментария со скрепкой. Кнопка отправки, скрепка и подсказка стоят
   одной строкой и переносятся на узком экране: у панели лида ширина бывает
   в треть карточки. */
.composer__bar {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  flex-wrap: wrap;
}
/* Скрепка — <label> в облике кнопки: настоящий input[type=file] спрятан
   визуально, но остаётся в потоке фокуса, иначе с клавиатуры файл не выбрать. */
.composer__attach { position: relative; overflow: hidden; cursor: pointer; }
.composer__attach input[type="file"] {
  position: absolute;
  inset: 0;
  opacity: 0;
  cursor: pointer;
}
.composer__attach:focus-within { outline: 2px solid var(--accent); outline-offset: 2px; }
/* Выбранные файлы — до отправки, с возможностью убрать лишний. */
.composer__files { display: flex; flex-wrap: wrap; gap: var(--space-2); }
.composer__file {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  max-width: 100%;
  padding: 2px var(--space-2) 2px var(--space-3);
  border-radius: var(--radius-full);
  background: var(--accent-soft);
  color: var(--accent-fg);
  font-size: var(--text-2xs);
}
.composer__file-name { max-width: 220px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.composer__file-drop {
  display: inline-flex;
  border: 0;
  background: none;
  color: inherit;
  cursor: pointer;
  padding: 0;
  opacity: 0.7;
}
.composer__file-drop:hover { opacity: 1; }
/* Снимок показывается в самой метке: имя файла у фотографии ничего не
   говорит, а картинку размером с ноготь узнают мгновенно. Слева, скруглённой
   под форму метки, чтобы не разрывать её силуэт. */
.composer__file--shot { padding-left: 2px; }
.composer__file-shot {
  width: 26px;
  height: 26px;
  border-radius: var(--radius-full);
  object-fit: cover;
  display: block;
}
/* Файл тащат на композер — подсвечиваем цель. Пунктир, а не заливка: сплошной
   фон под набранным текстом мешает его читать в момент броска. */
.composer--drop {
  outline: 2px dashed var(--accent);
  outline-offset: var(--space-2);
  border-radius: var(--radius-md);
}
/* Полоса заливки: час разговора в mp3 уходит секундами, и молчаливая кнопка
   в это время читается как «зависло». */
.composer__progress {
  height: 4px;
  border-radius: var(--radius-full);
  background: var(--line);
  overflow: hidden;
}
.composer__progress-bar {
  height: 100%;
  width: 0;
  background: var(--accent);
  transition: width var(--motion-fast) var(--ease-out);
}

/* ================================================================ листалка */
.pager {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-3);
  padding: var(--space-5);
}
.pager__page {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 32px;
  height: 32px;
  padding: 0 var(--space-3);
  border-radius: var(--radius-md);
  color: var(--fg-soft);
  font-size: var(--text-sm);
  font-weight: var(--weight-medium);
  cursor: pointer;
}
.pager__page:hover { background: var(--bg-hover); color: var(--fg); }
.pager__page[aria-current="page"] {
  background: var(--accent);
  color: var(--accent-on);
}
.pager__gap { color: var(--fg-muted); padding: 0 var(--space-2); }
.pager__side { margin-left: auto; display: flex; align-items: center; gap: var(--space-3); font-size: var(--text-sm); color: var(--fg-muted); }

/* ================================================================ сообщения
   Всплывающее сообщение о результате. Появляется только когда результат
   неочевиден по самому экрану: удачное сохранение видно по данным, а вот
   неудачу нужно сказать словами.
   aria-live на контейнере — иначе скринридер не заметит появления. */
.toast-host {
  position: fixed;
  left: 50%;
  bottom: var(--space-7);
  transform: translateX(-50%);
  z-index: var(--z-toast);
  display: grid;
  gap: var(--space-3);
  justify-items: center;
  pointer-events: none;
}
.toast {
  display: flex;
  align-items: center;
  gap: var(--space-4);
  padding: var(--space-4) var(--space-5);
  border-radius: var(--radius-lg);
  background: var(--slate-900);
  color: var(--slate-50);
  font-size: var(--text-sm);
  box-shadow: var(--shadow-lg);
  pointer-events: auto;
  animation: toast-in var(--motion-base) var(--ease-out);
}
.toast--danger { background: var(--danger-700, #B91C1C); }
.toast__action {
  color: var(--blue-200);
  font-weight: var(--weight-semi);
  cursor: pointer;
  text-decoration: underline;
}

/* Определение ниши запускается фоном. Кнопка не меняет ширину при переходе
   «Определить» → «Запускаем», а такой же компактный статус живёт в ячейке до
   ответа воркера. */
[data-niche-selected] { min-width: 164px; justify-content: center; }
[data-niche-idle-icon] { display: inline-flex; }
[data-niche-idle-icon][hidden], [data-niche-spinner][hidden] { display: none; }
.niche-progress {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  margin-left: var(--space-2);
  color: var(--accent);
  font-size: var(--text-2xs);
  white-space: nowrap;
}
@keyframes toast-in {
  from { opacity: 0; transform: translateY(8px); }
  to { opacity: 1; transform: translateY(0); }
}

/* =================================================================== меню
   Выпадающее меню на <details>: браузер сам держит открытость и закрытие по
   Esc, своей реализации не нужно. */
.menu { position: relative; }
.menu > summary { list-style: none; cursor: pointer; }
.menu > summary::-webkit-details-marker { display: none; }
.menu__list {
  position: absolute;
  right: 0;
  top: calc(100% + var(--space-2));
  z-index: var(--z-drawer);
  min-width: 200px;
  padding: var(--space-2);
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  background: var(--bg-surface);
  box-shadow: var(--shadow-lg);
  display: grid;
  gap: 1px;
}

/* Переименование продукта — действие редкое, поэтому поле скрыто за
   карандашом. Выпадающее окно не растягивает карточку и не раздвигает
   линейку, пока пользователь его не откроет. */
.product-rename { margin-left: auto; flex: none; }
.product-rename__form { width: min(360px, calc(100vw - var(--space-8))); gap: var(--space-2); }
.product-rename__form .input { width: 100%; }
.product-rename__form .btn { justify-self: end; }
.menu__item {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  width: 100%;
  padding: var(--space-3) var(--space-4);
  border-radius: var(--radius-md);
  color: var(--fg-soft);
  font-size: var(--text-sm);
  text-align: left;
  cursor: pointer;
}
.menu__item:hover { background: var(--bg-hover); color: var(--fg); text-decoration: none; }
.menu__item--danger { color: var(--danger-fg); }
.menu__item--danger:hover { background: var(--danger-soft); color: var(--danger-fg); }
.menu__sep { height: 1px; background: var(--line); margin: var(--space-2) 0; }
/* Меню порядка стоит слева в панели над таблицей — панель раскрывается вправо,
   а не влево, иначе уезжает за край экрана. */
/* Шире обычного меню: у каждого пункта справа стоит пояснение («выручка ↓»,
   «просроченные сверху»), и на узкой панели подписи ломались на две строки. */
.menu__list--left { right: auto; left: 0; min-width: 340px; }
.menu__list--left .menu__item { white-space: nowrap; }
.menu__item--on { background: var(--accent-soft); color: var(--accent-fg); font-weight: var(--weight-semi); }
.menu__item--on:hover { background: var(--accent-softer); color: var(--accent-fg); }
/* Пояснение к пункту: «выручка ↓», «просроченные сверху». Прижато вправо —
   подписи слева тогда читаются столбцом, а не вперемешку с пояснениями. */
.menu__note { margin-left: auto; padding-left: var(--space-4); color: var(--fg-muted); font-size: var(--text-2xs); white-space: nowrap; }
.menu__item--on .menu__note { color: var(--accent-fg); }
.menu__hint { display: block; padding: var(--space-2) var(--space-4) var(--space-3); color: var(--fg-muted); font-size: var(--text-2xs); line-height: var(--leading-snug); }

/* Настройка внутри меню: подпись и сам переключатель. Не .menu__item —
   на неё не нажимают целиком, и подсветка при наведении врала бы. */
.menu__group { display: grid; gap: var(--space-2); padding: var(--space-2) var(--space-4); }
.menu__label {
  font-size: var(--text-2xs);
  font-weight: var(--weight-medium);
  color: var(--fg-muted);
  text-transform: uppercase;
  letter-spacing: 0.04em;
}

/* ================================================================= полоса
   Горизонтальный индикатор доли. Используется в воронке и в разрезе по тирам. */
.bar {
  position: relative;
  height: 8px;
  border-radius: var(--radius-full);
  background: var(--bg-hover);
  overflow: hidden;
}
.bar__fill {
  height: 100%;
  border-radius: var(--radius-full);
  background: var(--accent);
  min-width: 2px;
  transition: width var(--motion-slow) var(--ease-out);
}
.bar--tall { height: 28px; border-radius: var(--radius-md); }
.bar--tall .bar__fill { border-radius: var(--radius-md); }
.bar__value {
  position: absolute;
  left: var(--space-4);
  top: 0;
  height: 100%;
  display: flex;
  align-items: center;
  font-size: var(--text-xs);
  font-weight: var(--weight-semi);
}
.bar__fill--ok { background: var(--ok); }
.bar__fill--warn { background: var(--warn); }
.bar__fill--danger { background: var(--danger); }
.bar__fill--accent { background: var(--accent); }
.bar__fill--info { background: var(--info); }
.bar__fill--muted { background: var(--slate-400); }

/* ============================================================ живое обновление
   Блоки, которые дособирает воркер (см. js/live.js), и лента лида: подмена по
   htmx должна быть видна, но не назойлива. htmx на время settle вешает
   .htmx-added на вставленный элемент — стартуем из лёгкого сдвига
   с прозрачностью и приезжаем на место. Остальным подменам консоли (сетка
   календаря, панели списка) въезд не нужен — они происходят по клику человека,
   и внимание уже там. */
#panel-company, #panel-progress, #panel-lpr, #panel-channels, #panel-message,
[id^="feed-"] {
  transition:
    opacity var(--motion-slow) var(--ease-out),
    transform var(--motion-slow) var(--ease-out);
}
#panel-company.htmx-added, #panel-progress.htmx-added, #panel-lpr.htmx-added,
#panel-channels.htmx-added, #panel-message.htmx-added,
[id^="feed-"].htmx-added {
  opacity: 0;
  transform: translateY(6px);
}

/* Подпись, которую фоновый опрос только что переписал: короткая вспышка
   акцентным фоном говорит «здесь обновилось», не отбирая взгляд надолго.
   Вешается из js/live.js только на реально сменившийся текст. */
@keyframes live-flash {
  0% { background-color: color-mix(in srgb, var(--accent) 24%, transparent); }
  100% { background-color: transparent; }
}
.live-flash { animation: live-flash 1.4s var(--ease-out); }

/* ============================================================= подтверждение
   Маленький модальный вопрос поверх другого окна (несохранённые изменения
   в планировании). Высота и ширина явные по той же причине, что у .picker:
   fit-content у <dialog> с флекс-детьми в Safari считается ненадёжно. */
.confirm {
  width: min(440px, 92vw);
  padding: var(--space-6);
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  background: var(--bg-surface);
  color: var(--fg);
  box-shadow: var(--shadow-xl);
}
.confirm::backdrop {
  background: rgb(15 23 42 / 0.35);
  backdrop-filter: blur(1px);
}
.confirm__title {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  font-weight: var(--weight-semi);
}
.confirm__icon { color: var(--warn); }
.confirm__text {
  margin-top: var(--space-3);
  font-size: var(--text-sm);
  color: var(--fg-muted);
}
.confirm__actions {
  display: flex;
  justify-content: flex-end;
  gap: var(--space-3);
  margin-top: var(--space-5);
}

/* ------------------------------------------------------- список лидов: ячейки
   Кликабельный значок приоритета, ячейка заметки и пометки контактов —
   строке списка нужны компактные варианты, не расталкивающие таблицу. */
.pill--btn { border: 0; cursor: pointer; font: inherit; font-size: var(--text-xs); }
.pill--btn:hover { filter: brightness(1.12); }
.pill--ghost { background: transparent; border: 1px dashed var(--line-strong); color: var(--fg-muted); }

/* Заметка: текст слева, кнопка «+» справа, окошко ввода — третьим рядом на всю
   ширину. Окошко раскрывается в потоке (строка подрастает), а не поверх
   таблицы: у таблицы своя прокрутка, и слой резался бы её краем снизу. */
.notecell {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  gap: var(--space-2);
  align-items: start;
  min-width: 0;
  max-width: 260px;
}
.notecell__body { display: grid; gap: var(--space-2); min-width: 0; }
/* Запись ячейки: текст + подпись + своё окошко правки. Записей до двух —
   закреплённая и последняя, — и каждая складывается своей мини-колонкой. */
.notecell__entry { display: grid; gap: 2px; min-width: 0; }
/* Закреплённая запись отделяется тонкой чертой-акцентом слева: булавка в
   подписи есть, но при беглом просмотре списка ловится именно черта. */
.notecell__entry--pinned {
  border-left: 2px solid var(--accent);
  padding-left: var(--space-3);
}
.notecell__pin {
  width: 10px;
  height: 10px;
  color: var(--accent);
  vertical-align: -1px;
}
.notecell__text {
  font-size: var(--text-xs);
  overflow: hidden;
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
}
.notecell__meta { font-size: var(--text-2xs); color: var(--fg-muted); }
/* Лупа в подписи найденной реплики — рядом с булавкой закреплённой и того же
   размера: обе отвечают на вопрос «почему эта запись здесь». */
.notecell__found {
  width: 10px;
  height: 10px;
  color: var(--accent-fg);
  vertical-align: -1px;
}
/* Совпавшее слово внутри реплики. Не жёлтый маркер браузера по умолчанию:
   в тёмной теме он превращается в нечитаемую заливку, а ячейка и без того
   пёстрая от булавок и пилюль. */
.notecell__hit {
  background: var(--accent-softer);
  color: var(--accent-fg);
  border-radius: var(--radius-sm);
  padding: 0 2px;
}
/* Свой комментарий — кнопка в облике текста: нажатие открывает правку.
   Пунктир при наведении — единственная подсказка кликабельности, которую
   текст в две строки может себе позволить без визуального шума. */
.notecell__text--own {
  border: 0;
  background: none;
  padding: 0;
  text-align: left;
  color: inherit;
  font: inherit;
  font-size: var(--text-xs);
  cursor: pointer;
}
.notecell__text--own:hover { text-decoration: underline dotted; color: var(--accent-fg); }
.notecell__add { border: 1px solid var(--line-strong); }
.notecell__panel {
  grid-column: 1 / -1;
  display: grid;
  gap: var(--space-2);
  padding: var(--space-3);
  border: 1px solid var(--line-strong);
  border-radius: var(--radius-md);
  background: var(--bg-surface);
  min-width: 220px;
}
.notecell__panel[hidden] { display: none; }
.notecell__panel textarea { font-size: var(--text-xs); }
.notecell__panel-actions { display: flex; gap: var(--space-2); }

/* Лишние каналы связи спрятаны до нажатия: четыре строки контактов в каждой
   ячейке делали строку списка вдвое выше остальных. Раскрытие — внутри ячейки,
   ширина не меняется, растёт только высота этой строки. */
/* Специфичность здесь не для красоты: .contactcell__row ниже в файле задаёт
   display: flex тем же весом (один класс), и одиночный .contactcell__row--extra
   ему проигрывал по порядку — строки не прятались вовсе. */
.contactcell .contactcell__row--extra { display: none; }
[data-contacts].is-open .contactcell__row--extra { display: flex; }
.contactcell__more {
  font-size: var(--text-2xs);
  color: var(--fg-muted);
  align-self: start;
}
.contactcell__more:hover { color: var(--accent-fg); }

/* Объединённая графа «ЛПР / Контакты»: имя, контакты, кнопка поиска. */
.personcell { display: grid; gap: var(--space-1); min-width: 0; }
.personcell__name { display: block; font-weight: var(--weight-semi); }
.personcell__contacts { display: flex; align-items: flex-end; gap: var(--space-2); min-width: 0; }
.personcell__contacts .contactcell { flex: 1; min-width: 0; }
/* Кнопки поиска в ячейках — компактнее обычной btn--sm: это строка таблицы. */
.personcell__scan,
.sitecell__scan { --btn-height: 24px; color: var(--fg-muted); }
.personcell__scan[disabled],
.sitecell__scan[disabled] { cursor: progress; }
.personcell__note { font-size: var(--text-2xs); color: var(--fg-muted); }

/* Графа «Сайт»: чип и кнопка поиска в одну строку, итог запуска — строкой ниже.
   Обёртка чипа блочная: пометка «справочник» из site_chip встаёт под ссылку,
   inline-flex выталкивал её вбок поверх колонки «Этап». */
.sitecell { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-1) var(--space-2); min-width: 0; }
.sitecell__chip { display: block; min-width: 0; max-width: 100%; }
.sitecell__note { flex-basis: 100%; font-size: var(--text-2xs); color: var(--fg-muted); }

.contactcell { display: grid; gap: 2px; font-size: var(--text-xs); }
.contactcell__row { display: flex; align-items: center; gap: var(--space-2); min-width: 0; }
.contactcell__kind {
  flex: none;
  padding: 0 var(--space-2);
  border-radius: var(--radius-sm);
  font-size: var(--text-2xs);
  background: var(--bg-hover);
  color: var(--fg-muted);
}
.contactcell__kind--private { background: var(--warn-soft); color: var(--warn-fg); }

/* Кнопка-значение «нажми — скопируется»: выглядит как ссылка, но кладёт
   значение в буфер обмена (почта в контактах лида, js/copy.js). */
.copybtn {
  background: none;
  border: 0;
  padding: 0;
  font: inherit;
  color: var(--accent-fg);
  cursor: copy;
  text-align: left;
  transition: color var(--motion-fast) var(--ease-out);
}
.copybtn:hover { color: var(--accent-hover); }
.copybtn.is-copied { color: var(--ok-fg); font-family: var(--font-sans); }
/* Вариант-иконка (копировать телефон): значок вместо подписи, кликабельная
   зона под палец. При успехе листы сменяются галочкой — обработчик только
   вешает класс is-copied, текст не трогает. */
.copybtn--icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 24px;
  min-height: 24px;
  border-radius: var(--radius-sm);
  cursor: pointer;
}
.copybtn--icon:hover { background: var(--accent-soft); }
.copybtn__i--done { display: none; }
.copybtn--icon.is-copied .copybtn__i--copy { display: none; }
.copybtn--icon.is-copied .copybtn__i--done { display: inline-flex; color: var(--ok-fg); }
/* Почта-кнопка в карточке компании/ЛПР (.btn + data-copy-icon): та же
   зелёная отметка успеха, что у .copybtn, но без смены надписи — рядом
   иконка, которую textContent стёр бы. */
.btn.is-copied { color: var(--ok-fg); border-color: var(--ok-fg); }

/* Дата следующего касания — кнопка, открывающая окно календаря. */
.lead-plans { display: grid; gap: var(--space-3); }
.lead-plans__list { display: grid; gap: var(--space-2); }
.lead-plans__item {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr) auto;
  gap: var(--space-2);
  align-items: center;
  padding: var(--space-2);
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
}
.lead-plans__item--next { border-color: var(--accent-softer); background: var(--accent-soft); }
.lead-plans__when { display: grid; min-width: 46px; font-size: var(--text-xs); line-height: 1.25; }
.lead-plans__when span { color: var(--fg-muted); }
.lead-plans__body { min-width: 0; display: grid; gap: 2px; }
.lead-plans__kind { font-size: var(--text-sm); font-weight: var(--weight-semi); }
.lead-plans__note { color: var(--fg-muted); font-size: var(--text-xs); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.lead-plans__tools { display: flex; align-items: center; gap: 2px; }
.lead-plans__tools form { margin: 0; }
.lead-plans__editor { padding-top: var(--space-3); border-top: 1px solid var(--line); }
/* Свободное время в отказе «время занято»: кнопка подставляет его в поля. */
.lead-plans__slots { display: flex; flex-wrap: wrap; gap: var(--space-2); }
.lead-plans__slot { font-variant-numeric: tabular-nums; }
.lead-plans__slot.is-picked { background: var(--accent); border-color: var(--accent); color: var(--accent-on); }
.lead-plans__slot-note { color: var(--ok-fg); }
.lead-plans__editor-head, .lead-plans__actions { display: flex; align-items: center; gap: var(--space-2); flex-wrap: wrap; }
.lead-plans__editor-head strong { font-size: var(--text-sm); }
.lead-plans__actions form { margin: 0; }
.datebtn {
  border: 0;
  background: none;
  padding: 0;
  font: inherit;
  color: var(--fg);
  cursor: pointer;
  text-decoration: underline dotted var(--line-strong);
  text-underline-offset: 3px;
}
.datebtn:hover { color: var(--accent-fg); }
/* Дата касания на сегодня: тёплый акцент — «сделать сегодня». Подчёркивание
   тем же цветом, чтобы кнопка читалась как единое пятно, а не текст с чертой. */
.datebtn--today {
  color: var(--warn-fg);
  font-weight: var(--weight-semi);
  text-decoration-color: var(--warn);
}
.datebtn--today:hover { color: var(--warn-fg); }

/* Панель «Настроить таблицу»: список колонок с галочками и порядком. */
.colmenu { position: relative; }
.colmenu__panel {
  position: absolute;
  right: 0;
  top: calc(100% + 6px);
  z-index: var(--z-widget);
  width: 280px;
  padding: var(--space-4);
  border: 1px solid var(--line-strong);
  border-radius: var(--radius-lg);
  background: var(--bg-surface);
  box-shadow: var(--shadow-xl);
  display: none;
}
.colmenu.is-open .colmenu__panel { display: block; }
.colmenu__item {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  padding: var(--space-1) 0;
  font-size: var(--text-xs);
}
.colmenu__item label { flex: 1; display: flex; align-items: center; gap: var(--space-3); cursor: pointer; }
.colmenu__move { display: flex; gap: 2px; }
.colmenu__actions {
  display: flex;
  justify-content: space-between;
  gap: var(--space-3);
  margin-top: var(--space-4);
  padding-top: var(--space-4);
  border-top: 1px solid var(--line);
}

/* Объединённой графе нужен простор под ФИО и телефон целиком; длинная почта
   усечётся сама — адрес есть в mailto и в карточке. */
[data-col="lpr"] { min-width: 260px; }
.contactcell__row a,
.contactcell__row .copybtn { max-width: 195px; }
/* Графе комментария — под окошко ввода: без запаса оно вылезало бы на соседей. */
[data-col="comment"] { min-width: 230px; }
/* Сайт живёт в графе «Компания» над названием; чип не даёт ячейке распереться —
   длинный домен усекается в самом чипе. */
[data-col="company"] .sitecell { margin-bottom: 2px; }
[data-col="company"] .sitecell__chip { max-width: 280px; }

/* ============================================== окно «Настроить этапы»
   Та же основа, что у попапа ЛПР: нативный <dialog>, шапка, host для htmx. */
.stagesmodal {
  width: min(640px, calc(100vw - var(--space-8)));
  padding: 0;
  border: 1px solid var(--line-strong);
  border-radius: var(--radius-xl);
  box-shadow: var(--shadow-xl);
  background: var(--bg-surface);
  color: var(--fg);
}
.stagesmodal::backdrop { background: rgb(15 23 42 / 0.35); }
.stagesmodal__head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-4);
  padding: var(--space-4) var(--space-4) var(--space-4) var(--space-5);
  border-bottom: 1px solid var(--line);
}
#stages-modal-host { padding: var(--space-5); max-height: min(70vh, 640px); overflow-y: auto; }

/* Общая воронка — стандартные и свои этапы в выбранном порядке. */
.stagesmgr__pills {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: var(--space-2);
  margin-top: var(--space-3);
}
/* Pointer gestures use the same pills on mouse, touch and pen devices. */
.stagesmgr__chip { border: 1px solid transparent; min-height: 32px; cursor: grab; touch-action: none; user-select: none; font-family: inherit; font-weight: var(--weight-medium); gap: 6px; }
.stagesmgr__chip:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.stagesmgr__chip.is-dragging { opacity: .35; border-color: var(--accent); cursor: grabbing; }
.stagesmgr__grip { font-size: 16px; line-height: 1; opacity: .55; }
.stagesmgr__ghost { position: fixed; pointer-events: none; z-index: 100; opacity: .95; box-shadow: var(--shadow-lg); cursor: grabbing; }
.stagesmgr__drop-line { position: fixed; width: 3px; border-radius: 2px; background: var(--accent); pointer-events: none; z-index: 99; }
.stagesmgr__drop-line[hidden] { display: none; }
.stagesmgr__save-line { display: flex; align-items: center; gap: var(--space-3); flex-wrap: wrap; margin-top: var(--space-2); }
.stagesmgr__save-line:has([data-stages-save-status]:empty):has([data-stages-reload][hidden]) { display: none; }
[data-stages-reload][hidden] { display: none; }
[data-stages-save-status][data-tone="error"] { color: var(--danger-fg); }
[data-stages-save-status][data-tone="ok"] { color: var(--ok-fg); }
.stagesmgr__settings { border-top: 1px solid var(--line); padding-top: var(--space-4); }
.stagesmgr__settings > summary { cursor: pointer; font-size: var(--text-sm); color: var(--fg-soft); }
.stagesmgr__settings[open] > summary { margin-bottom: var(--space-3); }
@media (pointer: coarse) { .stagesmgr__chip { min-height: 40px; padding: 6px 10px; } }
.stagesmgr__arrow { color: var(--fg-muted); flex: none; }

/* Общий список этапов: порядок, подпись, счётчик и инструменты. */
.stagesmgr__list { list-style: none; display: grid; gap: var(--space-3); margin-top: var(--space-3); }
.stagesmgr__row {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: var(--space-3);
  padding: var(--space-3);
  border: 1px solid var(--line);
  border-radius: var(--radius-md);
  background: var(--bg-sunken);
}
.stagesmgr__row--archived { opacity: 0.55; }
.stagesmgr__edit { display: flex; align-items: center; gap: var(--space-2); flex: 1 1 260px; min-width: 0; }
.stagesmgr__edit .input { flex: 1 1 auto; min-width: 0; }
.stagesmgr__tone { width: auto; }
.stagesmgr__meta { color: var(--fg-muted); font-size: var(--text-xs); white-space: nowrap; }
.stagesmgr__tools { display: flex; align-items: center; gap: var(--space-1); margin-left: auto; }
.stagesmgr__number { width: 20px; text-align: center; flex: none; color: var(--fg-muted); font-size: var(--text-xs); font-variant-numeric: tabular-nums; }
.stagesmgr__caption { display: flex; align-items: center; flex-wrap: wrap; gap: var(--space-3); flex: 1 1 180px; min-width: 0; }
.stagesmgr__kind { font-size: var(--text-2xs); color: var(--fg-muted); }
.stagesmgr__place { width: 100%; font-size: var(--text-xs); }
.stagesmgr__row > form { margin: 0; }
.stagesmgr__list { padding: 0; }
@media (max-width: 600px) {
  .stagesmgr__edit { flex: 1 1 calc(100% - 36px); flex-wrap: wrap; }
  .stagesmgr__edit .input { flex-basis: 100%; }
  .stagesmgr__caption { flex-basis: calc(100% - 36px); }
  .stagesmgr__meta { white-space: normal; }
  .stagesmgr__add .field__row { flex-wrap: wrap; }
}
.stagesmgr__add { border-top: 1px solid var(--line); padding-top: var(--space-5); }

/* Воронка продаж на аналитике: пилюля этапа шире тира, своя сетка строки. */
.salesfunnel__label { min-width: 0; }
.tierbar:has(.salesfunnel__label) {
  grid-template-columns: minmax(120px, 180px) minmax(0, 1fr) 44px;
}

/* Помощник «Найти ИНН по названию» в попапе «Данные лида». */
.innhints { list-style: none; display: grid; gap: var(--space-2); margin-top: var(--space-3); }
.innhints__row { display: flex; align-items: baseline; gap: var(--space-3); min-width: 0; }
.innhints__caption { font-size: var(--text-xs); color: var(--fg-soft); min-width: 0; }

/* Причина падения строки под статусом: не безликое «ошибка», а понятная суть.
   Тон по роду ошибки — тупик (нейтральный), ход сейлза (внимание), сбой (опасно). */
.status-note {
  display: flex;
  align-items: flex-start;
  gap: var(--space-2);
  margin-top: var(--space-2);
  padding: var(--space-2) var(--space-3);
  border-radius: var(--radius-md);
  font-size: var(--text-sm);
  line-height: 1.45;
}
.status-note .icon { flex: none; margin-top: 2px; }
.status-note--dead   { color: var(--fg-muted);  background: var(--bg-sunken); }
.status-note--action { color: var(--warn-fg);   background: var(--warn-soft); }
.status-note--tech   { color: var(--danger-fg); background: var(--danger-soft); }

/* --------------------------------------------------- кому уйдёт сообщение
   Строка над темой письма: адрес получателя видно ДО отправки. Значение
   моноширинное — адрес и номер читают посимвольно, а не «в общем». */
.composer__to {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  flex-wrap: wrap;
  font-size: var(--text-xs);
  color: var(--fg-muted);
  padding: var(--space-2) var(--space-3);
  border: 1px dashed var(--line);
  border-radius: var(--radius-md);
  background: var(--bg-hover);
}
.composer__to-value { color: var(--fg); font-size: var(--text-sm); }
.composer__to-bad { color: var(--danger); }
/* Строка показывается только у своей вкладки. Правило обязательное: композер
   прячет блоки атрибутом hidden, а он слабее display:flex из класса выше —
   без этой строки все три адреса висели разом (случай 19.08.2026). */
.composer__to[hidden] { display: none; }

/* Местное время смотрящего рядом с московским (js/localtime.js). Появляется
   только у того, чьи часы расходятся с Москвой, — остальным его не видно. */
.localtime {
  margin-left: var(--space-2);
  font-size: var(--text-xs);
  color: var(--fg-muted);
  white-space: nowrap;
}

/* ============================================ строка списка ключевых клиентов
   Строка в одну линию: с двадцатью клиентами список обязан помещаться на экран
   целиком, иначе за прокруткой теряется именно то, ради чего экран и делался —
   кто ждёт касания. Подробности (этап, вид касания, чей лид) разворачиваются
   только у выбранного: там, куда сейчас смотрят, место есть.

   Рамки у каждой строки нет — только волосяной разделитель: девятнадцать рамок
   с закруглениями весят больше, чем содержимое, и съедают по два пикселя
   высоты каждая. Красный по-прежнему показан не только цветом: полоса слева
   и точка перед датой. */
.keyitem {
  position: relative;
  display: grid;
  gap: var(--space-1);
  padding: var(--space-2) var(--space-3);
  border-left: 3px solid transparent;
  border-bottom: 1px solid var(--line);
  background: var(--bg-surface);
  color: inherit;
  transition: background var(--motion-fast);
}
/* Скругление даёт контейнер (у него overflow: hidden), строкам оно не нужно —
   нужен только отказ от последнего разделителя, чтобы он не лёг на рамку. */
.keyitem:last-child { border-bottom: 0; }
.keyitem:hover { background: var(--bg-hover); }
.keyitem--current { background: var(--accent-soft); }
.keyitem--due { border-left-color: var(--danger); }

/* Растянутая ссылка: прозрачно перекрывает всю строку и ведёт на карточку —
   строка по-прежнему кликается целиком мышью, как обычная ссылка списка.
   z-index ниже, чем у keyitem__site, поэтому ссылка на домен перехватывает
   клик первой, а не проваливается в эту подложку. */
.keyitem__hit {
  position: absolute;
  inset: 0;
  z-index: 1;
}

.keyitem__top {
  display: flex;
  align-items: flex-start;
  gap: var(--space-2);
  justify-content: space-between;
}
/* Длинное название переносится на вторую строку, а не обрезается многоточием:
   «ООО "ИТГЛОБАЛКОМ СЕКЬЮРИТИ"» и «ООО "ЭНЕРГИЯ ВРЕМЕНИ"» в одну строку не
   влезают, а по обрубку их не различить. Две строки — потолок: третья означала
   бы, что в списке стоит не название, а описание. */
.keyitem__name {
  flex: 1;
  min-width: 0;
  font-weight: 600;
  font-size: var(--text-sm);
  display: -webkit-box;
  -webkit-line-clamp: 2;
  line-clamp: 2;
  -webkit-box-orient: vertical;
  line-height: var(--leading-snug);
  overflow: hidden;
  overflow-wrap: anywhere;
}
.keyitem__when {
  flex: 0 0 auto;
  display: flex;
  align-items: center;
  gap: var(--space-2);
  height: calc(var(--text-sm) * var(--leading-snug));
  font-size: var(--text-xs);
  color: var(--fg-muted);
  white-space: nowrap;
}
.keyitem__dot {
  flex: 0 0 auto;
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: var(--danger);
}
.keyitem__due-mark { color: var(--danger-fg); font-weight: 600; }

/* Вторая строка есть только у выбранного. Без переноса: перенос вернул бы
   третью строку, ради отсутствия которой всё и затевалось. */
.keyitem__meta {
  display: flex;
  align-items: baseline;
  gap: var(--space-2);
  font-size: var(--text-xs);
  color: var(--fg-soft);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.keyitem__stage { color: var(--fg-muted); }
.keyitem__plan--none { color: var(--fg-soft); }
/* Настоящая ссылка на сайт компании, а не просто текст меты: живёт над
   keyitem__hit (z-index выше), поэтому клик по домену открывает сайт, а не
   проваливается в растянутую ссылку строки. Открывается в новой вкладке —
   сейлз не должен терять список, кликнув мимо. */
.keyitem__site {
  position: relative;
  z-index: 2;
}
/* «Подсказка справочника»: ИНН на сайте не подтверждён, поэтому глуше
   остальных фактов и обычной ссылки — та же логика, что у chip-link--unknown. */
.keyitem__site--hinted { color: var(--fg-soft); font-style: italic; }
.keyitem__revenue {
  flex: 0 0 auto;
  max-width: 88px;
  padding: 0 var(--space-2);
  border-radius: var(--radius-sm);
  background: var(--bg-hover);
  color: var(--fg-soft);
  font-size: var(--text-xs);
  line-height: calc(var(--text-sm) * var(--leading-snug));
  white-space: nowrap;
  position: relative;
  z-index: 2;
}
.keyitem__menu {
  position: relative;
  z-index: 2;
  flex: 0 0 auto;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: var(--space-6);
  height: var(--space-6);
  margin-top: calc(-1 * var(--space-1));
  border-radius: var(--radius-sm);
  color: var(--fg-muted);
  cursor: pointer;
}
.keyitem__menu:hover { background: var(--bg-active); color: var(--fg); }
.keyitem__menu--pinned { color: var(--accent-fg); }
.keyitem__menu:focus-visible, .key-pin-menu__action:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.keyitem--pinned:not(.keyitem--due) { border-left-color: var(--accent); }
.key-pin-menu {
  position: fixed;
  z-index: var(--z-popover, 100);
  width: 240px;
  max-width: calc(100vw - var(--space-8));
  padding: var(--space-2);
  border: 1px solid var(--line);
  border-radius: var(--radius-md);
  background: var(--bg-surface);
  color: var(--fg);
  box-shadow: var(--shadow-lg);
}
.key-pin-menu__name { padding: var(--space-3); font-size: var(--text-xs); font-weight: var(--weight-medium); color: var(--fg-muted); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.key-pin-menu__action { display: flex; align-items: center; gap: var(--space-3); width: 100%; padding: var(--space-3); border-radius: var(--radius-sm); text-align: left; font-size: var(--text-sm); cursor: pointer; }
.key-pin-menu__action:hover { background: var(--bg-hover); }
.key-pin-menu__hint { padding: var(--space-2) var(--space-3) var(--space-3); color: var(--fg-muted); font-size: var(--text-xs); }
.key-pin-notice { position: fixed; right: var(--space-5); bottom: var(--space-5); z-index: var(--z-popover, 100); max-width: calc(100vw - var(--space-8)); padding: var(--space-4); border: 1px solid var(--danger); border-radius: var(--radius-md); background: var(--danger-soft); color: var(--danger-fg); box-shadow: var(--shadow-lg); font-size: var(--text-sm); }

.keyhead__due { color: var(--danger-fg); }

.keycard__head {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: var(--space-4);
  flex-wrap: wrap;
}
.keycard__title { font-size: var(--text-xl); margin: 0; }
.keycard__facts {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  flex-wrap: wrap;
  margin: var(--space-2) 0 0;
  font-size: var(--text-sm);
}

/* Шапка экрана «Ключевые»: название и счётчики одной строкой. Крупный заголовок
   в две строки здесь стоил бы трёх клиентов в списке. */
.keyhead { align-items: baseline; }
.keyhead__title {
  display: flex;
  align-items: baseline;
  gap: var(--space-3);
  flex-wrap: wrap;
  font-size: var(--text-lg);
  font-weight: var(--weight-bold);
  letter-spacing: var(--tracking-tight);
}
.keyhead__count {
  font-size: var(--text-sm);
  font-weight: 400;
  color: var(--fg-muted);
}

/* Мелкий вариант поля — для срезов, которые стоят над узкой колонкой и не
   должны съедать её высоту. */
.select--sm {
  min-height: 30px;
  padding: var(--space-2) var(--space-3);
  font-size: var(--text-xs);
}
/* Денежная графа: цифры вправо и не переносятся — так их сравнивают глазом
   по столбику, а не читают как текст. */
.table__col-money {
  width: 1%;
  text-align: right;
  white-space: nowrap;
  font-variant-numeric: tabular-nums;
}

/* Город: узкая графа с обрезкой — «Петропавловск-Камчатский» не должен
   растаскивать таблицу ради одной строки. */
.table__col-city {
  max-width: 140px;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* Основной адрес — отметка контактного лица; выбранный получатель — галочка. */
.btn.contact-email--primary,
.contact-email--primary {
  color: var(--ok-fg);
  background: var(--ok-soft);
  border-color: var(--ok);
}
.lprchannel__value { flex: 1; min-width: 0; }
.lprchannel__primary {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  font-size: var(--text-xs);
  color: var(--fg-muted);
  cursor: pointer;
}
.lprchannel__primary input { accent-color: var(--ok); flex: none; }
.lprchannel__primary:has(input:checked) { color: var(--ok-fg); }
.composer-recipients { border: 0; padding: 0; margin: 0; min-width: 0; }
.composer-recipients__chips { display: flex; flex-wrap: wrap; gap: var(--space-2); margin: var(--space-2) 0; }
.email-chip {
  position: relative;
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  max-width: 100%;
  border: 1px solid var(--line);
  border-radius: var(--radius-md);
  padding: var(--space-2) var(--space-3);
  background: var(--bg-surface);
  cursor: pointer;
  font-size: var(--text-sm);
}
.email-chip input { position: absolute; opacity: 0; width: 1px; height: 1px; }
.email-chip__body { display: grid; gap: 2px; min-width: 0; }
.email-chip__person { color: var(--fg-muted); font-size: var(--text-2xs); }
.email-chip__check { display: inline-flex; width: 16px; height: 16px; flex: none; border: 1px solid var(--line); border-radius: 50%; }
.email-chip__check .icon { visibility: hidden; }
.email-chip--primary { color: var(--ok-fg); background: var(--ok-soft); border-color: var(--ok); }
.email-chip--primary .email-chip__person { color: inherit; }
.email-chip:has(input:checked) { outline: 2px solid var(--accent); outline-offset: 2px; }
.email-chip:has(input:checked) .email-chip__check { background: var(--accent); border-color: var(--accent); color: var(--white); }
.email-chip:has(input:checked) .email-chip__check .icon { visibility: visible; }
.email-chip:has(input:focus-visible) { outline: 2px solid var(--accent); outline-offset: 2px; }
.email-chip--unavailable { opacity: .65; cursor: not-allowed; }
.email-chip:has(input:disabled) { cursor: default; }
@media (max-width: 600px) {
  #panel-message .segmented { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); }
  #panel-message .segmented__item { min-width: 0; }
}
/* Письма в хронологии активности: конверт, адресаты и текст. */
.feed__item--mail .feed__icon { color: var(--accent); background: var(--accent-soft); }
.feed__item--mail .feed__body { min-width: 0; }
.mail-entry {
  min-width: 0;
  overflow: hidden;
  border: 1px solid var(--line);
  border-left: 3px solid var(--accent);
  border-radius: var(--radius-lg);
  background: var(--bg-surface);
  box-shadow: 0 2px 8px rgb(0 0 0 / .025);
}
.mail-entry--outgoing { border-left-color: var(--ok); }
.mail-entry--pending { border-left-color: var(--warn); }
.mail-entry__header {
  padding: var(--space-4) var(--space-5);
  border-bottom: 1px solid var(--line);
  background: var(--bg-sunken);
}
.mail-entry__top { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-2) var(--space-4); }
.mail-entry__direction { display: inline-flex; align-items: center; gap: var(--space-2); color: var(--accent); font-size: var(--text-xs); font-weight: var(--weight-medium); }
.mail-entry__reply {
  display: grid;
  gap: var(--space-1);
  width: 100%;
  min-width: 0;
  margin-top: var(--space-3);
  padding: var(--space-3) var(--space-4);
  border: 0;
  border-left: 3px solid var(--accent);
  border-radius: var(--radius-sm);
  background: var(--accent-soft);
  color: var(--fg);
  text-align: left;
  cursor: pointer;
  font: inherit;
}
.mail-entry--outgoing .mail-entry__reply { background: var(--ok-soft); border-left-color: var(--ok); }
.mail-entry__reply:hover { filter: brightness(.97); }
.mail-entry__reply:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.mail-entry__reply-label { display: flex; align-items: center; gap: var(--space-2); font-size: var(--text-2xs); color: var(--fg-muted); }
.mail-entry__reply-author { color: var(--accent-fg); font-size: var(--text-xs); font-weight: var(--weight-semi); overflow-wrap: anywhere; }
.mail-entry--outgoing .mail-entry__reply-author { color: var(--ok-fg); }
.mail-entry__reply-text { display: block; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-size: var(--text-xs); line-height: 1.5; }
.mail-entry__reply--unavailable { cursor: default; }
.mail-entry__reply--unavailable:hover { filter: none; }
.mail-entry { scroll-margin-block: var(--space-8); }
.mail-entry:focus { outline: none; }
.mail-entry--highlight { animation: mail-highlight 1s ease-out; }
@keyframes mail-highlight {
  0%, 35% { box-shadow: 0 0 0 3px var(--accent), 0 0 24px var(--accent-soft); }
  100% { box-shadow: 0 0 0 0 transparent; }
}
@media (prefers-reduced-motion: reduce) {
  .mail-entry--highlight { animation: none; outline: 2px solid var(--accent); outline-offset: 2px; }
}
.mail-entry--outgoing .mail-entry__direction { color: var(--ok-fg); }
.mail-entry__date { margin-left: auto; color: var(--fg-muted); font-size: var(--text-xs); }
.mail-entry__subject { margin: var(--space-3) 0; font-size: var(--text-md); line-height: 1.4; overflow-wrap: anywhere; }
.mail-entry__addresses { display: grid; grid-template-columns: auto minmax(0, 1fr); gap: var(--space-1) var(--space-3); margin: 0; font-size: var(--text-xs); line-height: 1.6; }
.mail-entry__addresses dt { color: var(--fg-muted); }
.mail-entry__addresses dd { margin: 0; overflow-wrap: anywhere; }
.mail-entry__content { padding: var(--space-5); }
.mail-entry__text { white-space: pre-wrap; overflow-wrap: anywhere; font-size: var(--text-sm); line-height: 1.75; color: var(--fg); }
.mail-entry__preview { display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 10; overflow: hidden; }
.mail-entry__more { margin-top: var(--space-3); }
.mail-entry__more summary { display: flex; align-items: center; gap: var(--space-2); width: fit-content; list-style: none; cursor: pointer; color: var(--accent); font-size: var(--text-xs); font-weight: var(--weight-medium); }
.mail-entry__more summary::-webkit-details-marker { display: none; }
.mail-entry__more summary:focus-visible { outline: 2px solid var(--accent); outline-offset: 4px; }
.mail-entry__more .mail-entry__text { margin-top: var(--space-4); }
.mail-entry__collapse { display: none; }
.mail-entry__more[open] .mail-entry__expand { display: none; }
.mail-entry__more[open] .mail-entry__collapse { display: inline; }
.mail-entry__more[open] summary .icon { transform: rotate(180deg); }
.mail-entry__content:has(.mail-entry__more[open]) > .mail-entry__preview { display: none; }
.mail-entry__footer { padding: var(--space-3) var(--space-5); border-top: 1px solid var(--line); display: grid; gap: var(--space-3); }
.mail-entry__footer .attachment { min-width: 0; }
.mail-entry__footer .attachment__name { overflow-wrap: anywhere; }
.mail-entry__original { width: fit-content; color: var(--fg-muted); font-size: var(--text-xs); }
.mail-entry__binding { margin-bottom: var(--space-4); padding: var(--space-3); border-radius: var(--radius-md); background: var(--warn-soft); font-size: var(--text-xs); line-height: 1.6; }
.mail-entry__binding p { margin-bottom: var(--space-2); }
@media (max-width: 640px) {
  .mail-entry__header, .mail-entry__content, .mail-entry__footer { padding: var(--space-3); }
  .mail-entry__date { margin-left: 0; }
}

.lead-task__stage { display: block; margin-top: 6px; font-size: var(--text-xs); color: var(--fg-soft); overflow-wrap: anywhere; }

/* Assigned lead products, with one working primary. */
.lead-products__list { display: grid; gap: var(--space-2); }
.lead-products__item {
  display: flex; align-items: center; justify-content: space-between;
  gap: var(--space-2); padding: var(--space-2) var(--space-3);
  border: 1px solid var(--line); border-radius: var(--radius-sm);
  background: var(--bg-sunken); min-width: 0;
}
.lead-products__name {
  display: flex; flex-wrap: wrap; align-items: center;
  gap: var(--space-1) var(--space-2); min-width: 0;
  font-size: var(--text-sm); overflow-wrap: anywhere;
}
.lead-products__item form { flex-shrink: 0; }
.lead-products__status { display: flex; align-items: center; gap: var(--space-1); }
