/* Протокол — кит компонентов («Тихая ясность», волна 1).
   Всё на токенах tokens.css: цвет, кегль, отступ, радиус, тень, движение.
   Имена кита (.btn .card .chip .tag .panel .msg .empty-state .skeleton .list
   .tabs .seg …) объявляются только здесь; страница добавляет модификатор со
   своим префиксом. Специфичность правил кита держим низкой (класс или два),
   а наведение карточек — через :where(): локальное правило страницы,
   подключённое позже, всегда побеждает, и страницы переходят на кит по одной.
   app.css — раскладка основной оболочки и стили созвона. */

/* ── подпись капсом в 1–2 слова: «СЕЙЧАС», «ИТОГ», дата группы ──
   Капс только здесь: 11 px читается лишь в капсе, 650 и с разрядкой. */
.overline {
  margin: 0;
  font-size: var(--fs-2xs); font-weight: var(--fw-bold);
  line-height: var(--lh-snug);
  letter-spacing: var(--ls-caps); text-transform: uppercase;
  color: var(--muted);
}

/* ── аватар: инициалы, тон детерминирован именем (data-tone=s1..s6) ── */
.avatar {
  display: inline-grid;
  place-items: center;
  flex: none;
  border-radius: 50%;
  font-weight: var(--fw-bold);
  letter-spacing: .01em;
  user-select: none;
  width: 32px; height: 32px; font-size: var(--fs-xs);
}
.avatar[data-tone="s1"] { background: var(--s1-bg); color: var(--s1-fg); }
.avatar[data-tone="s2"] { background: var(--s2-bg); color: var(--s2-fg); }
.avatar[data-tone="s3"] { background: var(--s3-bg); color: var(--s3-fg); }
.avatar[data-tone="s4"] { background: var(--s4-bg); color: var(--s4-fg); }
.avatar[data-tone="s5"] { background: var(--s5-bg); color: var(--s5-fg); }
.avatar[data-tone="s6"] { background: var(--s6-bg); color: var(--s6-fg); }
/* фото поверх инициалов (shell.withPhoto): не загрузилось — видны инициалы.
   Глобально, а не в странице: фото своей карточки есть и в рейле (SCRUM-51). */
.avatar.has-photo { position: relative; overflow: hidden; }
.avatar .ava-photo { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; border-radius: inherit; }

/* несколько аватаров внахлёст (участники звонка) */
.avatar-stack { display: inline-flex; align-items: center; }
.avatar-stack .avatar {
  border: 2px solid var(--win);
  margin-left: calc(-1 * var(--sp-2));
}
.avatar-stack .avatar:first-child { margin-left: 0; }
.avatar-stack .more {
  display: inline-grid; place-items: center;
  border: 2px solid var(--win);
  margin-left: calc(-1 * var(--sp-2));
  border-radius: 50%;
  background: var(--sunk); color: var(--muted);
  font-size: var(--fs-2xs); font-weight: var(--fw-semibold);
  font-variant-numeric: tabular-nums;
  width: 32px; height: 32px;
}

/* ── кнопки ──
   По умолчанию .btn — ghost: фон окна и рамка. Без варианта кнопка раньше
   получала системный фон (светлая плашка в тёмной теме). primary — одна на
   экран или панель; опасное действие живёт в меню «⋯» или в диалоге.
   .primary / .secondary / .quiet без .btn — алиасы для продуктовых страниц
   («Развитие», «Работа»), которые переходят на кит. */
:is(.btn, .primary, .secondary, .quiet) {
  display: inline-flex; align-items: center; justify-content: center; gap: var(--sp-2);
  min-height: var(--control-h);
  padding: 0 var(--sp-4);
  font: inherit; font-size: var(--fs-md); font-weight: var(--fw-semibold);
  line-height: var(--lh-tight);
  color: var(--ink);
  background: var(--win);
  border: 1px solid var(--line);
  border-radius: var(--control-radius);
  cursor: pointer;
  white-space: nowrap;
  text-decoration: none;
  transition: background-color var(--dur-1) var(--ease), border-color var(--dur-1) var(--ease), color var(--dur-1) var(--ease);
}
/* Наведение — --hover, а не --sunk: в тёмной теме --sunk темнее окна и
   наведение «проваливало» кнопку вместо подсветки (ревью A, A-1). */
:is(.btn, .secondary):not(:disabled, [aria-disabled="true"]):hover { background: var(--hover); }
:is(.btn, .primary, .secondary, .quiet):not(:disabled, [aria-disabled="true"]):active { transform: translateY(1px); }

:is(.btn.primary, .primary) { background: var(--accent); border-color: var(--accent); color: var(--accent-ink); }
:is(.btn.primary, .primary):not(:disabled, [aria-disabled="true"]):hover { background: var(--accent-press); border-color: var(--accent-press); }

:is(.btn.quiet, .quiet) { background: transparent; border-color: transparent; color: var(--accent); }
:is(.btn.quiet, .quiet):not(:disabled, [aria-disabled="true"]):hover { background: var(--hover); color: var(--accent-press); }

.btn.danger { background: var(--win); border-color: var(--bad-br); color: var(--bad-fg); }
/* Цвет текста задаём вместе с фоном: наведение (0,4,0) перебивает и
   локальную заливку страницы («Удалить созвон» в call.html — белый текст
   на --bad-fg), и без него белый ложился на --bad-bg с контрастом 1.2:1. */
.btn.danger:not(:disabled, [aria-disabled="true"]):hover { background: var(--bad-bg); color: var(--bad-fg); }

:is(.btn, .primary, .secondary, .quiet):is(.sm, .small) {
  min-height: var(--control-h-sm);
  padding: 0 var(--sp-3);
  font-size: var(--fs-sm);
}
.btn.wide { width: 100%; }

/* Выключенная: серая без прозрачности. Раньше opacity .7 поверх заливки
   давала бледно-зелёную «сломанную» primary («Сохранить имя»). Прозрачность
   запрещена с !important: локальные «[disabled] { opacity: .45 }» страниц
   (профиль, комната, собесы) поверх серой заливки делали подпись
   нечитаемой, а «выключено» теперь и так говорят цвета --disabled-*.
   :is() повторён намеренно: специфичность 0,3,0 выше локальных вариантов
   страниц (0,2,0: «.btn.primary» в собесах, «.btn.on» в комнате). Иначе
   такой вариант, подключённый позже, возвращал выключенной кнопке заливку,
   а прозрачности, которая раньше выдавала выключенность, уже нет. */
:is(.btn, .primary, .secondary, .quiet):is(.btn, .primary, .secondary, .quiet):is(:disabled, [aria-disabled="true"]) {
  background: var(--disabled-bg);
  border-color: var(--line);
  color: var(--disabled-fg);
  opacity: 1 !important;
  cursor: not-allowed;
}
/* Занятая: цвет варианта остаётся, крутится спиннер. */
:is(.btn, .primary, .secondary, .quiet)[aria-busy="true"] { cursor: progress; }
.btn .kbd { font-family: var(--mono); font-size: var(--fs-xs); font-weight: var(--fw-medium); opacity: .72; }
.btn .spin {
  display: none;
  width: 14px; height: 14px; flex: none;
  border: 2px solid currentColor; border-right-color: transparent;
  border-radius: 50%;
  animation: ui-spin .7s linear infinite;
}
.btn[aria-busy="true"] .spin { display: block; }

/* иконочная кнопка: aria-label обязателен */
.icon-btn {
  flex: none;
  width: var(--icon-btn); height: var(--icon-btn);
  display: inline-grid; place-items: center;
  padding: 0;
  border: 0; border-radius: var(--control-radius);
  background: none; color: var(--muted);
  cursor: pointer;
  transition: background-color var(--dur-1) var(--ease), color var(--dur-1) var(--ease);
}
.icon-btn:hover { background: var(--hover); color: var(--ink); }
.icon-btn.danger:hover { color: var(--bad-fg); }
/* Кнопка «до наведения»: прячем только там, где наведение есть; на касании
   она видна всегда, иначе до неё не добраться. */
@media (hover: hover) {
  .icon-btn.reveal { opacity: 0; }
  :is(:hover, :focus-within) > .icon-btn.reveal, .icon-btn.reveal:focus-visible { opacity: 1; }
}

/* ── поля ──
   Подпись над полем — обычный текст 13/600, без капса и моно. Фон контрола
   всегда --win: плейсхолдер --faint проходит 4.5:1 только на --win и --page
   (на --sunk 4.34, ревью A, A-3). Фокус — одно кольцо: рамка акцента и
   ореол --accent-sunk, без outline поверх. */
.field { display: flex; flex-direction: column; gap: var(--sp-2); }
.field > label, .field-label {
  display: flex; align-items: baseline; gap: var(--sp-2);
  font-size: var(--fs-sm); font-weight: var(--fw-semibold);
  line-height: var(--lh-snug);
  color: var(--ink-2);
}
.field > label .side, .field-label .side {
  margin-left: auto;
  font-size: var(--fs-xs); font-weight: var(--fw-regular);
  color: var(--muted);
}
.field .hint { font-size: var(--fs-xs); color: var(--muted); }
.field .err { display: flex; align-items: center; gap: var(--sp-1); font-size: var(--fs-xs); color: var(--bad-fg); }

.control {
  display: flex; align-items: center; gap: var(--sp-2);
  min-height: var(--control-h);
  padding: 0 var(--sp-1) 0 var(--sp-3);
  background: var(--win);
  border: 1px solid var(--line);
  border-radius: var(--control-radius);
  transition: border-color var(--dur-1) var(--ease), box-shadow var(--dur-1) var(--ease);
}
.control:focus-within { border-color: var(--accent); box-shadow: 0 0 0 3px var(--accent-sunk); }
.control input, .control select, .control textarea {
  flex: 1; min-width: 0; align-self: stretch;
  font: inherit; font-size: var(--fs-md); font-weight: var(--fw-regular);
  color: var(--ink);
  background: none; border: 0;
  padding: 0;
  outline: none;
}
.control textarea { padding: var(--sp-2) 0; resize: vertical; }
.control.mono input { font-family: var(--mono); font-size: var(--fs-sm); }
.control .ghost-btn {
  flex: none;
  min-height: 28px; padding: 0 var(--sp-2);
  font: inherit; font-size: var(--fs-xs); font-weight: var(--fw-semibold);
  color: var(--muted); background: none; border: 0;
  border-radius: var(--r-xs); cursor: pointer;
}
.control .ghost-btn:hover { color: var(--ink); background: var(--hover); }
.control:has(> :is(input, select, textarea):disabled) { background: var(--disabled-bg); }

/* одиночный контрол без обёртки .control: поле, список, многострочное */
.input {
  width: 100%;
  min-height: var(--control-h);
  padding: 0 var(--sp-3);
  font: inherit; font-size: var(--fs-md); font-weight: var(--fw-regular);
  color: var(--ink);
  background: var(--win);
  border: 1px solid var(--line);
  border-radius: var(--control-radius);
  outline: none;
  transition: border-color var(--dur-1) var(--ease), box-shadow var(--dur-1) var(--ease);
}
.input:focus { border-color: var(--accent); box-shadow: 0 0 0 3px var(--accent-sunk); }
.input:disabled { background: var(--disabled-bg); color: var(--disabled-fg); }
.input.sm { min-height: var(--control-h-sm); padding: 0 var(--sp-2); font-size: var(--fs-sm); }
.input.mono { font-family: var(--mono); font-size: var(--fs-sm); }
textarea.input { min-height: 88px; padding: var(--sp-2) var(--sp-3); line-height: var(--lh-ui); resize: vertical; }

/* ошибка: рамка --bad-fg и ореол --bad-bg, фон остаётся --win */
.field.bad .control, .input.bad, .input[aria-invalid="true"] { border-color: var(--bad-fg); box-shadow: 0 0 0 3px var(--bad-bg); }
.field.bad > label { color: var(--bad-fg); }

/* флажок и переключатель: зелёные через accent-color, подпись 14 */
.check-row { display: inline-flex; align-items: center; gap: var(--sp-2); font-size: var(--fs-md); color: var(--ink); cursor: pointer; }
.check-row input { width: 16px; height: 16px; margin: 0; flex: none; accent-color: var(--accent); }

/* ── чипы статуса: пилюля 22 px без рамки, точка 6 px ── */
.chip {
  display: inline-flex; align-items: center; gap: var(--sp-1);
  min-height: 22px;
  padding: 0 var(--sp-2);
  font-size: var(--fs-xs); font-weight: var(--fw-semibold);
  border-radius: var(--r-pill);
  white-space: nowrap;
}
.chip.ok    { background: var(--ok-bg);   color: var(--ok-fg); }
.chip.warn  { background: var(--warn-bg); color: var(--warn-fg); }
.chip.bad   { background: var(--bad-bg);  color: var(--bad-fg); }
.chip.neu   { background: var(--neu-bg);  color: var(--neu-fg); }
.chip.acc   { background: var(--selected); color: var(--accent); }
/* «идёт созвон»: текст --bad-fg на --bad-bg (5.26:1); --live только у точки,
   текстом он даёт 4.39 в светлой теме (ревью A, A-4). */
.chip.live  { background: var(--bad-bg);  color: var(--bad-fg); }
.chip .dot { width: 6px; height: 6px; flex: none; border-radius: 50%; background: currentColor; }
.chip.live .dot { background: var(--live); animation: ui-pulse 1.4s ease-in-out infinite; }
.chip b { font-weight: var(--fw-bold); font-variant-numeric: tabular-nums; }

/* ── теги: метаданные, без моно ── */
.tag {
  display: inline-flex; align-items: center; gap: var(--sp-1);
  min-height: 22px;
  padding: 0 var(--sp-2);
  font-family: var(--sans); font-size: var(--fs-xs); font-weight: var(--fw-medium);
  border: 1px solid var(--neu-br);
  border-radius: var(--r-xs);
  background: var(--neu-bg);
  color: var(--neu-fg);
  white-space: nowrap;
}
.tag.ok   { background: var(--ok-bg);   color: var(--ok-fg);   border-color: var(--ok-br); }
.tag.warn { background: var(--warn-bg); color: var(--warn-fg); border-color: var(--warn-br); }
.tag.bad  { background: var(--bad-bg);  color: var(--bad-fg);  border-color: var(--bad-br); }
.tag.acc  { background: var(--selected); color: var(--accent); border-color: transparent; }
/* hard и soft одного цвета во всём продукте (пара Оли из «Карты навыков») */
.tag.hard    { background: var(--skill-hard-bg); color: var(--skill-hard-fg); border-color: transparent; }
.tag.soft    { background: var(--skill-soft-bg); color: var(--skill-soft-fg); border-color: transparent; }
.tag.project { background: var(--s3-bg); color: var(--s3-fg); border-color: transparent; }
.tag .pulse {
  width: 7px; height: 7px; flex: none; border-radius: 50%;
  background: var(--live);
  animation: ui-pulse 1.6s ease-in-out infinite;
}

/* ── фильтр и якорь: переключаемая пилюля ──
   Выбранный — тоном --selected с рамкой акцента, не заливкой: заливка
   акцента только у главной кнопки. */
.toggle-chip {
  display: inline-flex; align-items: center; gap: var(--sp-1);
  min-height: var(--control-h-sm);
  padding: 0 var(--sp-3);
  font: inherit; font-size: var(--fs-sm); font-weight: var(--fw-medium);
  color: var(--ink-2);
  background: var(--win);
  border: 1px solid var(--line);
  border-radius: var(--r-pill);
  cursor: pointer;
  white-space: nowrap;
  text-decoration: none;
  transition: background-color var(--dur-1) var(--ease), border-color var(--dur-1) var(--ease), color var(--dur-1) var(--ease);
}
.toggle-chip:hover { background: var(--hover); color: var(--ink); }
.toggle-chip:is([aria-pressed="true"], [aria-selected="true"], [aria-current]:not([aria-current="false"]), .on) {
  background: var(--selected);
  border-color: var(--accent);
  color: var(--accent);
  font-weight: var(--fw-semibold);
}
.toggle-chip .chip { min-height: 18px; }

/* ── вкладки: разделы страницы ──
   Активная — текст --ink и подчёркивание 2 px акцента. На телефоне вкладки
   в одну строку с прокруткой внутри и затуханием справа; маска работает по
   альфе, цвет в ней не участвует.
   .tabs прокручивается (overflow-x: auto, а значит и по вертикали не
   visible) и режет всё, что за краем: внешнее кольцо фокуса обрезалось до
   двух вертикальных черт, а кольцо внутри вкладки без полей ложилось на
   буквы. Поэтому кольцо рисует ::after — по вертикали внутри вкладки, по
   горизонтали на --sp-1 наружу, в зазор между вкладками. Первой вкладке
   это место даёт поле .tabs, которое снимает отрицательный margin: текст
   стоит на вертикали контента. Линия под вкладками — фоном в пределах
   поля, а не рамкой: рамка легла бы на всю ширину с полями и вылезла бы
   за вертикаль контента. Фон задан подсвойствами, чтобы background-color
   страницы его не сбросил. */
.tabs {
  display: flex; align-items: stretch; gap: var(--sp-6);
  margin-inline: calc(-1 * var(--sp-1));
  padding: 0 var(--sp-1) 1px;
  background-image: linear-gradient(var(--line-soft), var(--line-soft));
  background-repeat: no-repeat;
  background-position: var(--sp-1) 100%;
  background-size: calc(100% - 2 * var(--sp-1)) 1px;
  overflow-x: auto;
  scrollbar-width: none;
  overscroll-behavior-x: contain;
}
.tabs::-webkit-scrollbar { display: none; }
.tabs > :is(a, button, [role="tab"]) {
  position: relative;
  display: inline-flex; align-items: center; gap: var(--sp-2); flex: none;
  min-height: var(--control-h);
  padding: 0;
  font: inherit; font-size: var(--fs-md); font-weight: var(--fw-semibold);
  color: var(--muted);
  background: none; border: 0; border-radius: 0;
  box-shadow: inset 0 -2px 0 transparent;
  cursor: pointer;
  white-space: nowrap;
  text-decoration: none;
}
.tabs > :is(a, button, [role="tab"]):hover { color: var(--ink); }
.tabs > :is(a, button, [role="tab"]):focus-visible { outline: none; }
.tabs > :is(a, button, [role="tab"]):focus-visible::after {
  content: "";
  position: absolute; inset: var(--sp-1) calc(-1 * var(--sp-1));
  border: var(--focus-w) solid var(--focus);
  border-radius: var(--r-xs);
  pointer-events: none;
}
.tabs > :is([aria-selected="true"], [aria-current]:not([aria-current="false"]), .on) {
  color: var(--ink);
  box-shadow: inset 0 -2px 0 var(--accent);
}
@media (max-width: 640px) {
  .tabs { gap: var(--sp-5); mask-image: linear-gradient(90deg, var(--ink) 85%, transparent); }
}

/* ── сегменты: режим внутри блока («Дерево | Список», период, тема) ──
   Колодец --sunk внутри --win — только с рамкой --line: без неё в тёмной
   теме он не отличается от окна (ревью A, A-2). Выбранный пункт — окно с
   тенью, не заливка акцента. */
.seg {
  display: inline-flex; align-items: stretch; gap: 2px;
  min-height: var(--control-h-sm);
  padding: 2px;
  background: var(--sunk);
  border: 1px solid var(--line);
  border-radius: var(--control-radius);
}
.seg > :is(button, a, label) {
  position: relative;
  display: inline-flex; align-items: center; justify-content: center; gap: var(--sp-1);
  padding: 0 var(--sp-3);
  font: inherit; font-size: var(--fs-sm); font-weight: var(--fw-medium);
  color: var(--muted);
  background: none; border: 0;
  border-radius: var(--r-xs);
  cursor: pointer;
  white-space: nowrap;
  text-decoration: none;
}
.seg > :is(button, a, label):hover { color: var(--ink); }
/* Выбранный: окно, тонкая рамка --line (тенью inset, без сдвига соседей) и
   тень. В тёмной теме --win на колодце --sunk даёт лишь 1.10:1, а
   --shadow-sm на тёмном фоне не видна: без рамки выбор держался только на
   цвете и начертании текста. */
.seg > :is([aria-pressed="true"], [aria-selected="true"], [aria-checked="true"], [aria-current]:not([aria-current="false"]), .on, :has(> input:checked)) {
  background: var(--win);
  color: var(--ink);
  font-weight: var(--fw-semibold);
  box-shadow: inset 0 0 0 1px var(--line), var(--shadow-sm);
}
/* Вариант с radio внутри label: сам radio прозрачен, поэтому кольцо фокуса
   с клавиатуры (Tab, стрелки) рисует label. position: relative у пункта
   держит абсолютный radio внутри него. */
.seg > label > input { position: absolute; opacity: 0; pointer-events: none; }
.seg > label:has(> input:focus-visible) {
  outline: var(--focus-w) solid var(--focus);
  outline-offset: var(--focus-offset);
}

/* ── карточка: в покое без тени ── */
.cards { display: grid; grid-template-columns: repeat(auto-fill, minmax(240px, 1fr)); gap: var(--sp-3); }
.card {
  display: flex; flex-direction: column; gap: var(--sp-2);
  padding: var(--sp-5);
  background: var(--win);
  border: 1px solid var(--line);
  border-radius: var(--panel-radius);
  color: inherit;
}
.card.compact { padding: var(--sp-4); }
.card h3 { margin: 0; font-size: var(--fs-h3); font-weight: var(--fw-bold); line-height: var(--lh-snug); }
.card .sub { font-size: var(--fs-sm); color: var(--muted); }
/* Кликабельная карточка: рамка в сторону акцента и лёгкая тень, без сдвига.
   :where держит специфичность на уровне «.card:hover» — своё наведение
   страницы (карта навыков Оли) побеждает. */
.card:where(a, .is-link, :has(.card-link)) { transition: border-color var(--dur-1) var(--ease), box-shadow var(--dur-1) var(--ease); }
.card:where(a, .is-link, :has(.card-link)):hover {
  border-color: color-mix(in srgb, var(--accent) 45%, var(--line));
  box-shadow: var(--shadow-sm);
}
/* Одна цель на карточку: ссылка-заголовок растянута на всю карточку,
   вторичные ссылки и кнопки лежат поверх неё. */
.card:has(.card-link) { position: relative; }
.card-link { color: inherit; }
.card-link::after { content: ""; position: absolute; inset: 0; border-radius: inherit; }
.card:has(.card-link) :is(a, button, input, select, textarea, summary, label):not(.card-link) { position: relative; z-index: 1; }

/* ── KPI: значение 22/650 и подпись под ним ──
   Дельта — отдельный .chip.ok / .chip.bad, число само не красится. */
.stat-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(160px, 1fr)); gap: var(--sp-3); }
.stat {
  display: flex; flex-direction: column;
  min-width: 0;
  padding: var(--sp-4);
  background: var(--win);
  border: 1px solid var(--line);
  border-radius: var(--panel-radius);
}
.stat .v {
  font-size: var(--fs-h1); font-weight: var(--fw-bold); line-height: var(--lh-tight);
  color: var(--ink);
  font-variant-numeric: tabular-nums;
}
.stat .k { margin-top: var(--sp-1); font-size: var(--fs-xs); color: var(--muted); }
.stat .chip { align-self: flex-start; margin-top: var(--sp-2); }

/* ── карточка-строка списка (звонки, люди, записи): аватар + текст + мета ── */
.row-card {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr) auto;
  align-items: center;
  gap: var(--sp-3);
  min-height: var(--sp-12);
  padding: var(--sp-3) var(--sp-4);
  background: var(--win);
  border: 1px solid var(--line-soft);
  border-radius: var(--panel-radius);
  text-decoration: none;
  color: inherit;
  transition: border-color var(--dur-1) var(--ease), box-shadow var(--dur-1) var(--ease);
}
.row-card:hover {
  border-color: var(--line);
  box-shadow: var(--shadow-sm);
  text-decoration: none;
}
.row-card .rc-main { min-width: 0; display: flex; flex-direction: column; gap: 2px; }
.row-card .rc-title {
  font-size: var(--fs-md); font-weight: var(--fw-semibold); color: var(--ink);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.row-card .rc-sub {
  font-size: var(--fs-sm); color: var(--muted);
  display: flex; align-items: center; gap: var(--sp-1) var(--sp-2); flex-wrap: wrap;
}
.row-card .rc-side {
  display: flex; flex-direction: column; align-items: flex-end; gap: var(--sp-1);
  flex: none;
}
.row-card .rc-duration {
  font-family: var(--mono); font-size: var(--fs-xs); color: var(--muted);
  font-variant-numeric: tabular-nums;
}

/* ── плотный список данных: дайджест, доступы, журнал, факты ── */
.list {
  display: flex; flex-direction: column;
  border: 1px solid var(--line);
  border-radius: var(--panel-radius);
  overflow: hidden;
}
.list-row {
  display: flex; align-items: center; gap: var(--sp-3);
  min-height: var(--sp-12);
  padding: var(--sp-3) var(--sp-4);
  background: var(--win);
  border-bottom: 1px solid var(--line-soft);
  color: inherit;
  text-decoration: none;
}
.list-row:last-child { border-bottom: 0; }
.list-row:where(a, button, .is-link):hover { background: var(--hover); }
/* Кольцо фокуса строки-ссылки — внутри строки: .list режет всё за своей
   границей (overflow: hidden ради скругления), и кольцо с внешним отступом
   не было видно ни с одной стороны. Крайние строки упираются в скругление
   списка — там кольцо берёт его радиус, иначе углы срезались бы. */
:is(.list-row:where(a, button, .is-link), .list a.item):focus-visible { outline-offset: calc(-1 * var(--focus-w)); }
.list-row:where(a, button, .is-link):first-child:focus-visible { border-top-left-radius: inherit; border-top-right-radius: inherit; }
.list-row:where(a, button, .is-link):last-child:focus-visible { border-bottom-left-radius: inherit; border-bottom-right-radius: inherit; }
/* .main — ещё и рабочая область оболочки (shell.css: высота окна, flex-колонка,
   фон страницы). В строке списка это только текстовая колонка — без сброса
   строка вырастала на всё окно. */
.list-row .main { flex: 1; min-width: 0; min-height: auto; display: block; background: none; }
.list-row .k { font-size: var(--fs-sm); color: var(--muted); }
.list-row .v {
  margin-left: auto;
  font-size: var(--fs-md); color: var(--ink);
  text-align: right;
  font-variant-numeric: tabular-nums;
}
/* прежняя строка списка созвонов: заголовок, подстрока и теги справа */
.list .item {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  gap: var(--sp-1) var(--sp-3);
  padding: var(--sp-3) var(--sp-4);
  border-bottom: 1px solid var(--line-soft);
  background: var(--win);
  color: inherit;
}
.list .item:last-child { border-bottom: 0; }
.list a.item:hover { background: var(--hover); }
.list .item .t { font-size: var(--fs-md); font-weight: var(--fw-semibold); }
.list .item .s { font-size: var(--fs-xs); color: var(--muted); font-variant-numeric: tabular-nums; }
.list .item .tags { grid-column: 2; grid-row: 1 / span 2; display: flex; align-items: center; gap: var(--sp-2); }

/* ── ключ–значение: серый колодец с рамкой (бывший .panel) ──
   .panel — прежнее имя, пока страницы не перешли на .kv. */
:is(.kv, .panel) {
  display: flex; flex-direction: column; gap: var(--sp-2);
  padding: var(--sp-3);
  background: var(--sunk);
  border: 1px solid var(--line);
  border-radius: var(--control-radius);
}

/* ── таблица: журнал, пользователи ──
   Шапка липкая внутри прокручиваемой обёртки. На телефоне строка таблицы
   складывается в строку списка, подпись столбца берётся из data-label. */
.table-wrap {
  max-height: 60vh; overflow: auto;
  background: var(--win);
  border: 1px solid var(--line);
  border-radius: var(--panel-radius);
  overscroll-behavior: contain;
}
.table { width: 100%; border-collapse: collapse; font-size: var(--fs-sm); color: var(--ink); }
.table th {
  position: sticky; top: 0; z-index: 1;
  padding: var(--sp-2) var(--sp-4);
  background: var(--win);
  border-bottom: 1px solid var(--line);
  text-align: left;
  font-size: var(--fs-xs); font-weight: var(--fw-semibold); color: var(--muted);
  white-space: nowrap;
}
.table td {
  height: var(--sp-12);
  padding: var(--sp-2) var(--sp-4);
  border-bottom: 1px solid var(--line-soft);
  vertical-align: middle;
}
.table tbody tr:last-child td { border-bottom: 0; }
.table tbody tr:hover { background: var(--hover); }
.table .num { text-align: right; font-variant-numeric: tabular-nums; }
@media (max-width: 640px) {
  .table, .table tbody, .table tr, .table td { display: block; }
  .table thead { position: absolute; width: 1px; height: 1px; overflow: hidden; clip-path: inset(50%); }
  .table tr { padding: var(--sp-3) var(--sp-4); border-bottom: 1px solid var(--line-soft); }
  .table tbody tr:last-child { border-bottom: 0; }
  .table td { height: auto; padding: 2px 0; border: 0; }
  .table td[data-label]::before { content: attr(data-label) " · "; color: var(--muted); font-size: var(--fs-xs); }
  .table .num { text-align: left; }
}

/* ── действия строки ──
   Переносятся, а не выталкивают страницу вбок (админка на 390 была 625 px).
   На телефоне вторичные (.ra-wide) уходят, остаётся «⋯» (.ra-narrow). */
.row-actions { display: flex; flex-wrap: wrap; align-items: center; gap: var(--sp-2); margin-left: auto; min-width: 0; }
.row-actions .ra-narrow { display: none; }
@media (max-width: 640px) {
  .row-actions .ra-wide { display: none; }
  .row-actions .ra-narrow { display: revert; }
}
/* Пагинация одна: «Показать ещё» (.btn.sm) и счётчик «20 из 662». */
.more-row { display: flex; align-items: center; gap: var(--sp-3); }
.more-row .count { font-size: var(--fs-xs); color: var(--muted); font-variant-numeric: tabular-nums; }

/* ── группировка списков по датам ──
   Дата группы — .overline. Раньше правило ждало h3 прямо в .list-group,
   а он лежит в .group: «20 сентября» становился браузерным h3 17.5 px с
   полями по 1em (47 px от шапки до первой карточки). */
.list-group { display: flex; flex-direction: column; gap: var(--sp-4); }
.list-group .group { display: flex; flex-direction: column; gap: var(--sp-2); }
:is(.list-group > h3, .list-group .group > h3) {
  margin: 0;
  font-size: var(--fs-2xs); font-weight: var(--fw-bold);
  line-height: var(--lh-snug);
  letter-spacing: var(--ls-caps); text-transform: uppercase;
  color: var(--muted);
}
.list-group .rows { display: flex; flex-direction: column; gap: var(--sp-2); }

/* ── пустое состояние: иконка в круге + заголовок + подсказка + до двух кнопок ── */
.empty-state {
  display: flex; flex-direction: column; align-items: center; gap: var(--sp-2);
  padding: var(--sp-10) var(--sp-6);
  text-align: center;
  border: 1px dashed var(--line);
  border-radius: var(--panel-radius);
  background: var(--win);
}
.empty-state .ic-wrap {
  width: 48px; height: 48px;
  display: grid; place-items: center;
  border-radius: 50%;
  background: var(--accent-sunk);
  color: var(--accent);
  margin-bottom: var(--sp-1);
}
.empty-state h3 { margin: 0; font-size: var(--fs-h3); font-weight: var(--fw-bold); line-height: var(--lh-snug); color: var(--ink); }
.empty-state p { margin: 0; font-size: var(--fs-sm); color: var(--muted); max-width: 40ch; }
/* Блок внутри страницы: без иконки и рамки, слева. */
.empty-state.compact { align-items: flex-start; padding: var(--sp-4); text-align: left; border: 0; }
.empty-state.compact .ic-wrap { display: none; }
/* прежнее пустое состояние: рамка пунктиром и текст по центру */
.empty {
  display: flex; flex-direction: column; align-items: center; gap: var(--sp-2);
  padding: var(--sp-6) var(--sp-5);
  text-align: center;
  color: var(--muted);
  border: 1px dashed var(--line);
  border-radius: var(--panel-radius);
}
.empty b { color: var(--ink); font-size: var(--fs-md); }

/* ── скелетон: один, с шиммером ──
   На --track и --line-soft, не на --sunk: в тёмной теме --sunk к окну и
   странице даёт 1.05:1 и не читается (ревью A, A-2). Появляется через
   300 мс, чтобы быстрый ответ не мигал заглушкой. */
.skeleton {
  position: relative;
  overflow: hidden;
  height: 12px;
  background: var(--track);
  border-radius: var(--r-xs);
  animation: ui-appear var(--dur-2) var(--ease) .3s both;
}
.skeleton::after {
  content: "";
  position: absolute; inset: 0;
  background: linear-gradient(90deg, transparent, var(--line-soft), transparent);
  transform: translateX(-100%);
  animation: ui-shimmer 1.6s infinite;
}

/* ── сообщение: ошибка, предупреждение, успех, информация ──
   Базовый .msg пока красный: страницы ещё не везде пометили ошибки .msg.bad,
   а нейтральный базовый на время лишил бы их красного. */
.msg {
  display: flex; gap: var(--sp-2); align-items: flex-start;
  padding: var(--sp-2) var(--sp-3);
  font-size: var(--fs-sm); line-height: var(--lh-ui);
  background: var(--bad-bg);
  border: 1px solid var(--bad-br);
  border-radius: var(--control-radius);
  color: var(--bad-fg);
}
.msg svg { flex: none; margin-top: 2px; }
.msg a { color: inherit; font-weight: var(--fw-bold); text-decoration: underline; }
/* Хвост — пояснение человеку («что осталось, что делать»), а не машинное
   значение: обычным шрифтом, как весь текст сообщения. Моно превращало его
   вместе с кнопкой в строку кода (CALLS-04). */
.msg .tail {
  display: block; margin-top: 2px;
  font-size: var(--fs-xs); opacity: .85;
  font-variant-numeric: tabular-nums;
}
.msg.bad  { background: var(--bad-bg);  border-color: var(--bad-br);  color: var(--bad-fg); }
.msg.warn { background: var(--warn-bg); border-color: var(--warn-br); color: var(--warn-fg); }
.msg.ok   { background: var(--ok-bg);   border-color: var(--ok-br);   color: var(--ok-fg); }
:is(.msg.neu, .msg.neutral) { background: var(--neu-bg); border-color: var(--neu-br); color: var(--neu-fg); }

/* ── диалог: вместо системных confirm()/alert(), которые не знают о теме ──
   Кнопки справа, primary последней. Заливка danger — только здесь, у кнопки
   подтверждения. Хелпер — confirmDialog() из /js/dialog.js. */
dialog.dlg {
  width: min(480px, calc(100% - 2 * var(--sp-4)));
  max-height: calc(100dvh - 2 * var(--sp-4));
  overflow: auto;
  padding: var(--sp-5);
  color: var(--ink);
  background: var(--win);
  border: 1px solid var(--line);
  border-radius: var(--panel-radius);
  box-shadow: var(--shadow);
}
dialog.dlg.wide { width: min(720px, calc(100% - 2 * var(--sp-4))); }
dialog.dlg::backdrop { background: var(--dialog-backdrop); }
.dlg h2 { margin: 0 0 var(--sp-2); font-size: var(--fs-read); font-weight: var(--fw-bold); line-height: var(--lh-snug); overflow-wrap: anywhere; }
.dlg .dlg-text { margin: 0; font-size: var(--fs-md); color: var(--ink-2); overflow-wrap: anywhere; white-space: pre-line; }
.dlg-actions { display: flex; flex-wrap: wrap; justify-content: flex-end; gap: var(--sp-2); margin-top: var(--sp-5); }
.dlg-actions .btn.danger { background: var(--bad-fg); border-color: var(--bad-fg); color: var(--win); }
.dlg-actions .btn.danger:not(:disabled, [aria-disabled="true"]):hover { background: var(--bad-fg); color: var(--win); filter: brightness(.92); }

/* ── меню «⋯» и «Экспорт ▾»: <details class="menu"> + .menu-pop ── */
.menu { position: relative; }
details.menu > summary { list-style: none; cursor: pointer; user-select: none; }
details.menu > summary::-webkit-details-marker { display: none; }
details.menu > summary::marker { content: ""; }
details.menu[open] > summary { background: var(--hover); }
.menu-pop {
  position: absolute; top: calc(100% + var(--sp-1)); right: 0; z-index: var(--z-menu);
  min-width: 200px; max-width: calc(100vw - 2 * var(--sp-4));
  display: flex; flex-direction: column;
  padding: var(--sp-1);
  background: var(--win);
  border: 1px solid var(--line);
  border-radius: var(--control-radius);
  box-shadow: var(--shadow-pop);
}
.menu-pop.to-left { right: auto; left: 0; }
.menu-item {
  display: flex; align-items: center; gap: var(--sp-2);
  min-height: 36px;
  padding: 0 var(--sp-3);
  font: inherit; font-size: var(--fs-md); text-align: left;
  color: var(--ink);
  background: none; border: 0; border-radius: var(--r-xs);
  cursor: pointer; white-space: nowrap; text-decoration: none;
}
.menu-item:hover, .menu-item:focus-visible { background: var(--hover); }
.menu-item.danger-text { color: var(--bad-fg); }
.menu-item.danger-text:hover, .menu-item.danger-text:focus-visible { background: var(--bad-bg); }
.menu-item[aria-busy="true"] { cursor: progress; }
.menu-sep { height: 1px; margin: var(--sp-1) 0; background: var(--line-soft); }

/* ── боковая панель: справа 380 px, на узком экране снизу ── */
.sheet {
  position: fixed; top: 0; right: 0; bottom: 0; z-index: var(--z-dialog);
  width: min(380px, 100vw);
  display: flex; flex-direction: column; gap: var(--sp-4);
  padding: var(--sp-5);
  overflow: auto;
  background: var(--win);
  border-left: 1px solid var(--line);
  box-shadow: var(--shadow);
  color: var(--ink);
}
dialog.sheet { margin: 0 0 0 auto; height: 100dvh; max-height: none; border-top: 0; border-right: 0; border-bottom: 0; }
dialog.sheet::backdrop { background: var(--dialog-backdrop); }
.sheet-head { display: flex; align-items: center; gap: var(--sp-2); }
.sheet-head h2 { flex: 1; min-width: 0; margin: 0; font-size: var(--fs-h3); font-weight: var(--fw-bold); }
@media (max-width: 860px) {
  .sheet {
    top: auto; left: 0; width: 100%; max-height: 85dvh;
    border-left: 0; border-top: 1px solid var(--line);
    border-radius: var(--panel-radius) var(--panel-radius) 0 0;
  }
  dialog.sheet { margin: auto 0 0; height: auto; max-width: none; }
}

/* ── плавающая кнопка: место под неё резервирует .content, поверх
   содержимого она не встаёт («● Стрим» перекрывал «Завершить созвон») ── */
.fab {
  position: fixed; right: var(--gutter); bottom: var(--sp-4); z-index: var(--z-fab);
  box-shadow: var(--shadow-md);
}
:is(.main, body):has(> .fab) .content,
.content:has(> .fab) { padding-bottom: calc(var(--control-h) + 2 * var(--sp-4)); }

/* ── текст для чтения: урок, теория, пояснения, лид ── */
.prose {
  max-width: var(--measure);
  font-size: var(--fs-read); line-height: var(--lh-read);
  color: var(--ink);
}
.prose > * { margin: 0 0 var(--sp-3); }
.prose > :last-child { margin-bottom: 0; }
.prose :is(ul, ol) { padding-left: var(--sp-5); }
.prose li + li { margin-top: var(--sp-1); }
.prose a { color: var(--accent); font-weight: var(--fw-semibold); }
.prose h2 { margin-top: var(--sp-6); font-size: var(--fs-h2); font-weight: var(--fw-bold); line-height: var(--lh-tight); }
.prose h3 { margin-top: var(--sp-5); font-size: var(--fs-h3); font-weight: var(--fw-bold); line-height: var(--lh-snug); }

/* ── адаптив ── */
@media (max-width: 640px) {
  .row-card { grid-template-columns: auto minmax(0, 1fr); }
  .row-card .rc-side { grid-column: 2; flex-direction: row; align-items: center; justify-content: flex-start; }
  .list-row { flex-wrap: wrap; row-gap: var(--sp-1); }
  .list-row .v { flex-basis: 100%; margin-left: 0; text-align: left; }
}

/* Одна пульсация, один шиммер, одно вращение на весь продукт. При
   prefers-reduced-motion их снимает общее правило tokens.css. */
@keyframes ui-pulse { 0%, 100% { opacity: 1; } 50% { opacity: .25; } }
@keyframes ui-shimmer { to { transform: translateX(100%); } }
@keyframes ui-spin { to { transform: rotate(360deg); } }
@keyframes ui-appear { from { opacity: 0; } }
