/* NSP CRM — интерфейс для компьютера и телефона.
   Точки перелома: 1024px (боковое меню превращается в выдвижное),
   860px (таблицы превращаются в карточки), 640px (нижняя панель навигации). */

/* ---------------------------------------------------------------- шрифт

   Montserrat — один шрифт на весь интерфейс, и он лежит рядом с CRM, а не
   загружается со стороны. Причина простая: сервер и люди — в России, а внешние
   хранилища шрифтов оттуда открываются не всегда и не у всех. Не открылось бы —
   и вместо задуманного вида получился бы системный шрифт, разный на каждом
   компьютере. Свои файлы работают и без интернета, и в закрытой сети.

   Файл один на весь диапазон насыщенности (400–700): это переменный шрифт, из
   него браузер берёт и обычный текст, и полужирные заголовки, — вместо четырёх
   отдельных файлов. Наборы разделены по алфавитам (`unicode-range`), поэтому
   русскому интерфейсу не приходится грузить латиницу с диакритикой: браузер
   запрашивает только те файлы, буквы из которых на странице действительно есть.

   `font-display: swap` — текст виден сразу системным шрифтом и меняется на
   Montserrat, когда тот придёт. Иначе первые мгновения страница стояла бы пустой. */

@font-face {
  font-family: "Montserrat";
  font-style: normal;
  font-weight: 400 700;
  font-display: swap;
  src: url("fonts/montserrat-cyrillic.woff2") format("woff2");
  unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
}
@font-face {
  font-family: "Montserrat";
  font-style: normal;
  font-weight: 400 700;
  font-display: swap;
  src: url("fonts/montserrat-cyrillic-ext.woff2") format("woff2");
  unicode-range: U+0460-052F, U+1C80-1C8A, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F;
}
@font-face {
  font-family: "Montserrat";
  font-style: normal;
  font-weight: 400 700;
  font-display: swap;
  src: url("fonts/montserrat-latin.woff2") format("woff2");
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC,
    U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215,
    U+FEFF, U+FFFD;
}
@font-face {
  font-family: "Montserrat";
  font-style: normal;
  font-weight: 400 700;
  font-display: swap;
  src: url("fonts/montserrat-latin-ext.woff2") format("woff2");
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304,
    U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0,
    U+2113, U+2C60-2C7F, U+A720-A7FF;
}

:root {
  /* Шрифт интерфейса. Системный список остаётся запасным: пока файл не пришёл (и
     если он не придёт вовсе), текст читается тем, что есть в системе. */
  --font: "Montserrat", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto,
    "Helvetica Neue", Arial, sans-serif;
  --bg: #f3f6f9;
  --surface: #ffffff;
  --surface-2: #f7f9fc;
  --surface-3: #eef2f7;
  --border: #e2e8f0;
  --border-strong: #cbd5e1;
  --text: #16202b;
  --text-2: #3d4a5a;
  --muted: #6b7a8c;
  --accent: #1f7a53;
  --accent-hover: #196442;
  --accent-soft: #e6f4ed;
  --danger: #c0392b;
  --danger-soft: #fdecea;
  --warn: #a4690e;
  --warn-soft: #fdf3e2;
  --info: #2563a5;
  --info-soft: #e8f1fb;
  --radius: 14px;
  --radius-sm: 9px;
  --shadow-1: 0 1px 2px rgba(16, 24, 40, .05), 0 1px 3px rgba(16, 24, 40, .04);
  --shadow-2: 0 4px 16px rgba(16, 24, 40, .09);
  --shadow-3: 0 12px 40px rgba(16, 24, 40, .18);
  --sidebar-w: 248px;
  --topbar-h: 54px;
  --tabbar-h: 60px;
  --topbar-bg: rgba(255, 255, 255, .92);
  --login-bg: linear-gradient(160deg, #eef5f1 0%, #e9eff6 55%, #eef2f7 100%);
  --row-border: #eef2f6;
  --placeholder: #9aa8b6;
}

/* Тёмная тема. Включается не медиа-запросом, а признаком `data-theme` на странице:
   тему выбирают в меню («Как в системе», «Светлая», «Тёмная»), и выбор должен быть
   сильнее системной настройки. Признак ставится до первой отрисовки — маленьким
   сценарием в <head> (см. index.html), поэтому белой вспышки при тёмной теме нет.
   Меняются только цвета, разметка та же. */
:root[data-theme="dark"] {
  color-scheme: dark;
  --bg: #0f151c;
  --surface: #171f28;
  --surface-2: #1c2530;
  --surface-3: #232e3a;
  --border: #2a3644;
  --border-strong: #3a4857;
  --text: #e8eef4;
  --text-2: #c2ceda;
  --muted: #8c9cad;
  --accent: #45b587;
  --accent-hover: #57c497;
  --accent-soft: #16302a;
  --danger: #f08a7d;
  --danger-soft: #3a2220;
  --warn: #e0b062;
  --warn-soft: #372a17;
  --info: #6fb0ec;
  --info-soft: #16283a;
  --shadow-1: 0 1px 2px rgba(0, 0, 0, .4);
  --shadow-2: 0 4px 16px rgba(0, 0, 0, .5);
  --shadow-3: 0 12px 40px rgba(0, 0, 0, .6);
  --topbar-bg: rgba(23, 31, 40, .92);
  --login-bg: linear-gradient(160deg, #101821 0%, #0f151c 55%, #131c26 100%);
  --row-border: #232e3a;
  --placeholder: #6f7f90;
}

:root[data-theme="light"] { color-scheme: light; }

:root[data-theme="dark"] .login-art span { opacity: .22; }
:root[data-theme="dark"] .toast { background: #2b3846; }
:root[data-theme="dark"] select {
  background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='12' height='8'><path d='M1 1l5 5 5-5' stroke='%238c9cad' stroke-width='1.6' fill='none' stroke-linecap='round'/></svg>");
}

* { box-sizing: border-box; }
html, body { height: 100%; }

/* Размер шрифта — 14, а не 15 пикселей, и это про Montserrat, а не про мелкий текст.
   Montserrat — шрифт геометрический и широкий: буквы у него шире, а строчные выше,
   чем у системных Segoe UI и San Francisco, под которые была подобрана прежняя
   шкала. При одинаковой цифре размера строка Montserrat занимает примерно на
   десятую часть больше места — от этого интерфейс и выглядел громоздким, а надписи
   переставали помещаться в кнопки и заголовки колонок. 14 пикселей Montserrat
   читаются как 15 системных и занимают столько же места.

   Сглаживание — чтобы шрифт не выглядел жирнее, чем задуман: в macOS текст по
   умолчанию рисуется с утолщением, и Montserrat от этого заплывает. */
body {
  margin: 0;
  font-family: var(--font);
  background: var(--bg);
  color: var(--text);
  font-size: 14px;
  line-height: 1.5;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  -webkit-text-size-adjust: 100%;
  -webkit-tap-highlight-color: transparent;
}

/* 650 в Montserrat — почти полужирный: заголовки от него становились тяжелее
   текста настолько, что спорили с ним за внимание. 600 хватает. */
h1, h2, h3, h4 { margin: 0; font-weight: 600; line-height: 1.3; }
h1 { font-size: 17px; }
h2 { font-size: 15px; }
h3 { font-size: 14px; }
p { margin: 0 0 8px; }
a { color: var(--accent); }

.hidden { display: none !important; }
.muted { color: var(--muted); }
.small { font-size: 13px; }
.mono { font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; font-size: 13px; }
.nowrap { white-space: nowrap; }
.grow { flex: 1; min-width: 0; }
.center { text-align: center; }
.right { text-align: right; }
.ellipsis { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

/* ---------------------------------------------------------------- каркас */

.app { display: grid; grid-template-columns: var(--sidebar-w) minmax(0, 1fr); min-height: 100vh; }

/* Боковое меню целиком помещается на экран — это его главное свойство, и оно
   важнее любого украшения внутри.

   Прокрутка в меню — это не «немного неудобно». Меню отвечает на вопрос «что
   вообще есть в CRM», и отвечает оно только тем, что видно: до раздела,
   уехавшего за нижний край, не доберётся тот, кто не знает, что он там есть.
   Колесо мыши вдобавок прокручивает меню случайно — курсор просто оказался над
   ним, — и разделы уезжают сами собой.

   Отсюда все размеры ниже: одиннадцать разделов, четыре заголовка групп, шапка и
   строка учётной записи должны уложиться примерно в 600 пикселей — столько
   остаётся от экрана ноутбука 1366×768 после панелей браузера. Прокрутка у меню
   всё же есть (`overflow-y: auto`), но как последняя мера — для окна ростом с
   ладонь; `overscroll-behavior` не даёт ей утянуть за собой страницу. */

.sidebar {
  position: sticky;
  top: 0;
  height: 100vh;
  display: flex;
  flex-direction: column;
  padding: 10px;
  background: var(--surface);
  border-right: 1px solid var(--border);
  z-index: 60;
}

.brand {
  display: flex;
  align-items: center;
  gap: 9px;
  font-weight: 700;
  font-size: 15.5px;
  padding: 2px 10px 10px;
  white-space: nowrap;
}
.brand span { color: var(--accent); }
.brand .logo { font-size: 20px; }

.nav {
  display: flex;
  flex-direction: column;
  gap: 1px;
  min-height: 0;
  overflow-y: auto;
  overscroll-behavior: contain;
  scrollbar-width: thin;
}
/* Заголовки групп меню: десять разделов подряд читаются как список, а разбитые
   на «Работа», «Аналитика», «Рассылка» и «Настройка» — как оглавление. */
.nav-group {
  margin: 8px 0 2px;
  padding: 0 10px;
  font-size: 10.5px;
  font-weight: 600;
  letter-spacing: .04em;
  text-transform: uppercase;
  color: var(--muted);
}
.nav-group:first-child { margin-top: 0; }
.nav-item {
  display: flex;
  align-items: center;
  gap: 10px;
  width: 100%;
  padding: 6px 10px;
  border: none;
  border-radius: var(--radius-sm);
  background: transparent;
  color: var(--text-2);
  font: inherit;
  font-size: 13.5px;
  text-align: left;
  cursor: pointer;
  min-height: 32px;
}
.nav-item .ico { width: 18px; text-align: center; font-size: 16px; flex: none; }
.nav-item .ico svg { width: 18px; height: 18px; }
.nav-item:hover { background: var(--surface-3); color: var(--text); }
.nav-item.active { background: var(--accent-soft); color: var(--accent); font-weight: 600; }
.nav-item .badge { margin-left: auto; }

.badge {
  min-width: 20px;
  padding: 1px 7px;
  border-radius: 999px;
  background: var(--danger);
  color: #fff;
  font-size: 12px;
  font-weight: 600;
  text-align: center;
}
.badge.soft { background: var(--surface-3); color: var(--muted); }

/* Черта над строкой учётной записи, а не просто пустое место: когда разделов
   больше, чем помещается, и меню всё-таки прокручивается, черта показывает, где
   список кончился, — иначе последний пункт выглядит обрезанным. */
.sidebar-foot {
  margin-top: auto;
  padding-top: 8px;
  border-top: 1px solid var(--border);
}

/* ------------------------------------------------- учётная запись

   Одна строка внизу меню: кружок с инициалами, имя и роль. По нажатию —
   панель с темой оформления и действиями над своей учётной записью
   (app/web/js/app.js: openAccountMenu). */

.account-btn {
  display: flex;
  align-items: center;
  gap: 9px;
  width: 100%;
  padding: 6px 8px;
  border: 0;
  border-radius: var(--radius-sm);
  background: none;
  color: var(--text);
  font: inherit;
  text-align: left;
  cursor: pointer;
  min-width: 0;
}
.account-btn:hover { background: var(--surface-3); }
.account-btn[aria-expanded="true"] { background: var(--surface-3); }
.account-face {
  flex: none;
  width: 30px; height: 30px;
  border-radius: 50%;
  display: inline-flex; align-items: center; justify-content: center;
  background: var(--accent-soft);
  color: var(--accent);
  font-size: 12px;
  font-weight: 700;
  letter-spacing: .01em;
}
.account-id { display: grid; min-width: 0; flex: 1; line-height: 1.25; }
.account-name {
  font-size: 13.5px;
  font-weight: 600;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.account-role { color: var(--muted); font-size: 11.5px; }
.account-caret { flex: none; display: flex; color: var(--muted); }

/* Строка меню в панели: значок, подпись — и всё это от левого края, как пункт
   списка, а не как кнопка. Кнопкой она при этом остаётся: нажимается целиком. */
.account-item {
  display: flex;
  align-items: center;
  gap: 9px;
  width: 100%;
  padding: 8px;
  border: 0;
  border-radius: var(--radius-sm);
  background: none;
  color: var(--text-2);
  font: inherit;
  font-size: 13.5px;
  text-align: left;
  text-decoration: none;
  cursor: pointer;
}
.account-item .ico, .account-item svg { flex: none; color: var(--muted); }
.account-item:hover { background: var(--surface-3); color: var(--text); }
.account-item:hover .ico, .account-item:hover svg { color: inherit; }
.account-item.danger:hover { background: var(--danger-soft); color: var(--danger); }

/* Панель учётной записи: переключатель темы и три действия. Шире обычной панели —
   в переключателе три подписи в строку, и «Тёмная» не должна упираться в край. */
/* Двойным классом, а не одним: у `.popover` ниже своя ширина, и правило записано
   позже — при равной силе побеждало бы оно. */
.popover.popover-account { min-width: 252px; }
.popover-account .theme-switch { margin: 0 2px; }
.popover-account .pop-menu {
  display: grid;
  gap: 1px;
  margin-top: 6px;
  padding-top: 6px;
  border-top: 1px solid var(--border);
}

/* Переключатель темы: три состояния в одной строке — «как в системе», светлая,
   тёмная. Кнопками, а не выпадающим списком: выбор из трёх видно целиком, и до
   нужного значка — одно нажатие, а не два. */
.theme-switch { display: flex; gap: 2px; padding: 3px; border-radius: var(--radius-sm); background: var(--surface-3); }
.theme-switch button {
  flex: 1;
  min-width: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 4px;
  padding: 7px 4px;
  font: inherit;
  font-size: 11.5px;
  /* Подпись не переносится и не обрезается: три слова в строку — весь смысл
     переключателя, а «Тёмн…» вместо «Тёмная» читается как поломка */
  white-space: nowrap;
  text-overflow: clip;
  color: var(--muted);
  background: none;
  border: 0;
  border-radius: 6px;
  cursor: pointer;
}
.theme-switch button:hover { color: var(--text); }
.theme-switch button.active { background: var(--surface); color: var(--text); box-shadow: var(--shadow-1); }
.theme-switch button svg { flex: none; }

.backdrop {
  position: fixed;
  inset: 0;
  background: rgba(16, 24, 40, .45);
  z-index: 55;
  opacity: 0;
  pointer-events: none;
  transition: opacity .2s ease;
}
.backdrop.show { opacity: 1; pointer-events: auto; }

.main { display: flex; flex-direction: column; min-width: 0; }

.topbar {
  position: sticky;
  top: 0;
  z-index: 40;
  display: flex;
  align-items: center;
  gap: 10px;
  min-height: var(--topbar-h);
  /* Отступ по бокам тот же, что и у содержимого ниже: заголовок раздела и первая
     панель под ним должны начинаться на одной вертикали */
  padding: 8px 22px;
  background: var(--topbar-bg);
  backdrop-filter: blur(8px);
  border-bottom: 1px solid var(--border);
}
.topbar h1 { flex: 1; min-width: 0; }
.topbar-actions { display: flex; gap: 8px; align-items: center; flex-wrap: wrap; justify-content: flex-end; }

.icon-btn {
  display: none;
  align-items: center;
  justify-content: center;
  width: 40px;
  height: 40px;
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  background: var(--surface);
  font-size: 18px;
  cursor: pointer;
  flex: none;
}

.content { padding: 20px 22px 28px; max-width: 1560px; width: 100%; }
/* Подпись раздела: одна строка о том, что здесь делают. Стоит над содержимым, а не
   в шапке, — шапка липкая и должна оставаться в одну строку на телефоне. */
.view-hint { color: var(--muted); font-size: 13.5px; margin: -2px 0 14px; max-width: 90ch; }
.view { display: none; }
.view.active { display: block; }

.tabbar { display: none; }

/* ---------------------------------------------------------------- формы */

/* Шрифт страницы поля ввода и кнопки не наследуют — его им ставит браузер.
   `font: inherit` возвращает им шрифт интерфейса: Montserrat должен быть везде,
   а не «везде, кроме форм». Списки выбора вдобавок называют группы своим шрифтом. */
input, select, textarea, button, optgroup { font: inherit; }
input[type="text"], input[type="search"], input[type="password"], input[type="number"],
input[type="email"], input[type="tel"], input[type="date"], select, textarea {
  width: 100%;
  padding: 8px 12px;
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-sm);
  background: var(--surface);
  color: var(--text);
  outline: none;
  min-height: 38px;
}
input:focus, select:focus, textarea:focus {
  border-color: var(--accent);
  box-shadow: 0 0 0 3px rgba(31, 122, 83, .12);
}
input::placeholder, textarea::placeholder { color: var(--placeholder); }
textarea { min-height: 110px; resize: vertical; line-height: 1.45; }
textarea.code { font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: 13.5px; min-height: 150px; }
select { appearance: none; background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='12' height='8'><path d='M1 1l5 5 5-5' stroke='%236b7a8c' stroke-width='1.6' fill='none' stroke-linecap='round'/></svg>"); background-repeat: no-repeat; background-position: right 12px center; padding-right: 32px; }
input[type="checkbox"] { width: 18px; height: 18px; min-height: 0; accent-color: var(--accent); }

.field { display: block; margin-bottom: 12px; min-width: 0; }
.field > label { display: block; font-size: 13px; color: var(--muted); margin-bottom: 5px; font-weight: 500; }
/* Пояснение — везде одинаковое: мельче основного текста и приглушённое. Правило
   стояло только для полей ввода, и те же подписи в панелях набирались вровень с
   заголовками — панель настроек читалась как сплошной текст без старшинства. */
.hint { font-size: 12.5px; color: var(--muted); line-height: 1.5; }
.field .hint { margin-top: 4px; }
.hint + .hint { margin-top: 6px; }
/* Предупреждение со значком: значок стоит на первой строке, а не в столбце текста */
.warn-hint { display: flex; align-items: flex-start; gap: 6px; color: var(--warn); }
.warn-hint svg { flex: none; margin-top: 2px; }
.field.inline { display: flex; align-items: center; gap: 9px; }
.field.inline > label { margin: 0; font-size: 14.5px; color: var(--text-2); }
.form-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(210px, 1fr)); gap: 0 14px; }
.switch { display: flex; align-items: center; gap: 8px; cursor: pointer; user-select: none; padding: 6px 0; }

.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 7px;
  min-height: 36px;
  padding: 7px 14px;
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-sm);
  background: var(--surface);
  color: var(--text);
  cursor: pointer;
  white-space: nowrap;
  text-decoration: none;
  font-size: 13.5px;
  transition: background .15s ease, border-color .15s ease;
}
.btn:hover { background: var(--surface-2); }
.btn:active { transform: translateY(1px); }
.btn:disabled { opacity: .5; cursor: not-allowed; }
.btn.primary { background: var(--accent); border-color: var(--accent); color: #fff; font-weight: 550; }
.btn.primary:hover { background: var(--accent-hover); }
.btn.ghost { border-color: transparent; background: transparent; color: var(--muted); }
.btn.ghost:hover { background: var(--surface-3); color: var(--text); }
.btn.danger { color: var(--danger); border-color: var(--danger-soft); background: var(--surface); }
.btn.danger:hover { background: var(--danger-soft); }
.btn.small { min-height: 32px; padding: 5px 11px; font-size: 13px; }
.btn.block { width: 100%; }

.row { display: flex; gap: 10px; align-items: center; flex-wrap: wrap; }
.row.between { justify-content: space-between; }
.row.tight { gap: 6px; }
.stack { display: flex; flex-direction: column; gap: 14px; }

.toolbar {
  display: flex;
  gap: 9px;
  margin-bottom: 14px;
  flex-wrap: wrap;
  align-items: center;
}
.toolbar .search { flex: 1 1 260px; max-width: 420px; }
.toolbar select { width: auto; min-width: 150px; }
/* Поля дат («с» и «по» в разделе «Структура») стоят в строке рядом с остальными
   фильтрами, а не растягиваются на всю ширину: дата — короткое значение */
.toolbar input[type="date"] { width: auto; min-width: 150px; flex: 0 0 auto; }
/* Панель фильтров в узкой колонке карточки клиента */
.toolbar.tight { gap: 8px; margin-bottom: 12px; }
.toolbar.tight select { min-width: 130px; }

/* ------------------------------------------------- отбор списка

   Раньше все фильтры стояли в строке над списком: шесть выпадающих списков, каждый
   со своим «Любой…», — и на узком экране они занимали три строки ещё до первой
   фамилии. Прочитать по ним, что именно сейчас отобрано, было невозможно: значения
   по умолчанию выглядят так же, как выбранные.

   Теперь наверху только поиск и кнопка «Фильтры» с числом включённых. Сами
   фильтры — в панели, которая открывается по нажатию, а то, что уже включено,
   всегда видно строкой меток под поиском: каждую можно снять одним нажатием. */

.filter-bar { display: flex; gap: 9px; align-items: center; flex-wrap: wrap; margin-bottom: 12px; }
.filter-bar .search { flex: 1 1 260px; max-width: 420px; }
.filter-bar .spacer { flex: 1 1 auto; }

/* Счётчик включённых фильтров прямо на кнопке — иначе о них забывают и потом
   удивляются, почему в списке «пропали» клиенты */
.count-pill {
  min-width: 20px;
  padding: 0 6px;
  border-radius: 999px;
  background: var(--accent);
  color: #fff;
  font-size: 11.5px;
  font-weight: 600;
  line-height: 18px;
  text-align: center;
}
.btn.on { border-color: var(--accent); color: var(--accent); background: var(--accent-soft); }

.filter-panel {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  box-shadow: var(--shadow-1);
  padding: 14px 16px;
  margin-bottom: 12px;
}
.filter-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(206px, 1fr)); gap: 12px 14px; }
.filter-grid .field { margin: 0; }
.filter-grid .field > label { font-size: 12px; font-weight: 600; color: var(--muted); }
.filter-grid select, .filter-grid input { width: 100%; }
.filter-panel-foot {
  display: flex; align-items: center; gap: 10px; flex-wrap: wrap;
  margin-top: 14px; padding-top: 12px;
  border-top: 1px solid var(--border);
}

/* Что отобрано прямо сейчас: метка на каждый включённый фильтр, крестик снимает */
.filter-chips { display: flex; gap: 6px; flex-wrap: wrap; margin-bottom: 12px; }
.filter-chips:empty { display: none; }
.filter-chip { padding-right: 4px; }
.filter-chip b { font-weight: 600; }
.filter-chip .drop {
  display: inline-flex; align-items: center; justify-content: center;
  width: 18px; height: 18px;
  margin-left: 2px;
  border: 0; border-radius: 50%;
  background: none; color: inherit;
  cursor: pointer;
  opacity: .6;
}
.filter-chip .drop:hover { background: rgba(0, 0, 0, .08); opacity: 1; }

.filters { display: flex; gap: 6px; flex-wrap: wrap; }
.filter {
  padding: 5px 12px;
  border: 1px solid var(--border);
  border-radius: 999px;
  background: var(--surface);
  color: var(--muted);
  font-size: 13px;
  cursor: pointer;
  min-height: 32px;
}
.filter.active { background: var(--accent-soft); border-color: var(--accent-soft); color: var(--accent); font-weight: 600; }

/* Кнопка, которая выглядит и ведёт себя как ссылка: имя клиента в списке заказов
   открывает карточку, но самой строкой при этом остаётся заказ */
.link {
  border: none;
  background: none;
  padding: 0;
  color: var(--accent);
  font: inherit;
  font-weight: 600;
  cursor: pointer;
  text-align: left;
}
.link:hover { text-decoration: underline; }
.link.muted { color: var(--muted); font-weight: 400; }

/* Точные даты в фильтрах отчёта: подпись «с» и «по» рядом с полем, а не над ним —
   строка фильтров и так длинная */
.date-field {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  color: var(--muted);
  font-size: 13.5px;
}
.date-field input { width: auto; min-width: 148px; }
.reports-toolbar { align-items: center; row-gap: 10px; }

/* ---------------------------------------------------------------- блоки */

.panel {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 16px 18px;
  box-shadow: var(--shadow-1);
}
.panel + .panel { margin-top: 16px; }
#onboarding:not(:empty) { margin-bottom: 16px; }
.panel > header { display: flex; align-items: center; justify-content: space-between; gap: 10px; margin-bottom: 12px; flex-wrap: wrap; }
.panel-title { display: flex; align-items: center; gap: 8px; font-size: 14.5px; font-weight: 600; }

.cards { display: grid; grid-template-columns: repeat(auto-fill, minmax(190px, 1fr)); gap: 12px; margin-bottom: 16px; }
.card {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 14px 16px;
  box-shadow: var(--shadow-1);
  /* Плитка в сетке по умолчанию не уже своего содержимого, и длинная сумма
     растягивала бы не себя, а весь ряд — вместе с горизонтальной прокруткой
     страницы. Пусть лучше цифра ужимается внутри плитки (см. `.num-s`) */
  min-width: 0;
}
.card .value { font-size: 23px; font-weight: 700; letter-spacing: -.02em; overflow-wrap: anywhere; }
/* Ступени для длинных сумм: «595 226 449,61 ₽» в 23 пикселя не помещается (core.js: numSize) */
.card .value.num-s { font-size: 19px; }
.card .value.num-xs { font-size: 16px; letter-spacing: -.01em; }
.card .label { color: var(--muted); font-size: 13px; margin-top: 2px; }
.card.tap { cursor: pointer; }
.card.tap:hover { border-color: var(--accent); }

.chips { display: flex; gap: 6px; flex-wrap: wrap; }
.chip {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  background: var(--accent-soft);
  color: var(--accent);
  padding: 3px 10px;
  border-radius: 999px;
  font-size: 12.5px;
  font-weight: 500;
  white-space: nowrap;
}
.chip.gray { background: var(--surface-3); color: var(--muted); }
.chip.warn { background: var(--warn-soft); color: var(--warn); }
.chip.danger { background: var(--danger-soft); color: var(--danger); }
.chip.info { background: var(--info-soft); color: var(--info); }

/* Объяснение в несколько строк — например, отчёт «Проверить приём» и подсказки после
   подключения бота. Похоже на chip, но переносится по словам: там целые фразы, а не
   короткие метки, и в одну строку они не помещаются. */
.notice {
  padding: 10px 12px;
  border-radius: 10px;
  background: var(--accent-soft);
  color: var(--accent);
  line-height: 1.45;
}
.notice.warn { background: var(--warn-soft); color: var(--warn); }
.notice.danger { background: var(--danger-soft); color: var(--danger); }
.notice + .notice { margin-top: 8px; }

.checklist { list-style: none; margin: 14px 0; padding: 0; display: grid; gap: 10px; }
.checklist li { line-height: 1.45; }
.checklist li.danger { color: var(--danger); }

/* Столбец названий не тянется вместе с карточкой: доля от ширины разводила
   «Телефон» и его номер на треть экрана, и пару приходилось искать глазами. */
.kv { display: grid; grid-template-columns: minmax(130px, 220px) minmax(0, 1fr); gap: 8px 14px; }
.kv .k { color: var(--muted); font-size: 13px; }
.kv .v { word-break: break-word; }
.kv .v.empty { color: var(--placeholder); }

.empty-state { padding: 26px 16px; text-align: center; color: var(--muted); }
.empty-state .ico { font-size: 30px; display: block; margin-bottom: 8px; }

/* Чек-лист первичной настройки: пока CRM пустая, это главное, что нужно видеть */
.setup-step { display: flex; align-items: flex-start; gap: 10px; padding: 9px 0; }
.setup-step + .setup-step { border-top: 1px solid var(--row-border); }
.setup-step .mark {
  flex: none;
  width: 22px; height: 22px;
  margin-top: 1px;
  border-radius: 50%;
  display: flex; align-items: center; justify-content: center;
  background: var(--surface-3);
  color: var(--muted);
  font-size: 12px;
  font-weight: 600;
}
.setup-step.done .mark { background: var(--accent-soft); color: var(--accent); }
.setup-step.done .title { color: var(--muted); }
.setup-step .body { flex: 1; min-width: 0; }
.setup-step .title { font-weight: 600; }
.setup-step .note { color: var(--muted); font-size: 13px; }

/* ---------------------------------------------------------------- значки

   Вместо эмодзи — линейные рисунки: они наследуют цвет текста, поэтому одинаково
   выглядят в светлой и тёмной теме и не зависят от того, как эмодзи рисует система. */

.ico { flex: none; }
svg.ico { display: block; }
.nav-item .ico, .tabbar .ico { display: flex; justify-content: center; }
.panel-title svg.ico { color: var(--muted); }
.empty-state .ico { display: flex; justify-content: center; margin-bottom: 10px; color: var(--border-strong); }
.btn [data-icon], .btn [data-icon] svg { display: flex; }
/* Кнопка из одного значка: квадратная, чтобы не выглядела обрезанной подписью */
.btn.square { padding: 5px; width: 36px; min-width: 36px; }
.btn.small.square { width: 32px; min-width: 32px; }

.logo svg.mark { width: 30px; height: 30px; color: var(--accent); }
.brand .logo { display: inline-flex; align-items: center; }
.brand .logo svg.mark { width: 22px; height: 22px; }

/* ---------------------------------------------------------------- графики покупок

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

.chart {
  --chart-h: 200px;
  position: relative;
  display: flex;
  gap: 12px;
  margin: 24px 0 0;
}
.chart-scale {
  flex: none;
  width: 92px;
  height: var(--chart-h);
  margin-top: 22px;
  display: flex;
  flex-direction: column;
  justify-content: space-between;
  align-items: flex-end;
  font-size: 11.5px;
  color: var(--muted);
  line-height: 1;
}
/* Прокрутка — на обёртке, а не на самом поле графика: иначе подписи над столбиками
   и даты под ними обрезались бы (браузер не умеет прокручивать только вбок). */
.chart-scroll {
  flex: 1;
  min-width: 0;
  overflow-x: auto;
  padding: 22px 0 28px;
}
.chart-plot {
  position: relative;
  min-width: 100%;
  height: var(--chart-h);
  display: flex;
  align-items: flex-end;
  gap: 2px;
  border-bottom: 1px solid var(--border-strong);
}
/* Средняя линия — подсказка для глаза, поэтому она бледная и пунктирная */
.chart-plot::before {
  content: "";
  position: absolute;
  left: 0; right: 0; bottom: 50%;
  border-top: 1px dashed var(--border);
  pointer-events: none;
}
.bar-slot {
  position: relative;
  flex: 1 1 0;
  min-width: 5px;
  height: 100%;
  display: flex;
  align-items: flex-end;
  outline: none;
}
.bar {
  position: relative;
  width: 100%;
  background: var(--accent);
  border-radius: 4px 4px 0 0;
  transition: background .15s ease;
}
.bar.zero { background: var(--border-strong); height: 2px !important; border-radius: 2px; }
.bar-slot:hover .bar, .bar-slot:focus-visible .bar { background: var(--accent-hover); }
.bar-slot:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; border-radius: 4px; }
.bar .peak {
  position: absolute;
  bottom: 100%;
  left: 50%;
  transform: translateX(-50%);
  margin-bottom: 5px;
  font-size: 11.5px;
  font-weight: 600;
  white-space: nowrap;
  color: var(--text-2);
}
.bar-slot .tick {
  position: absolute;
  top: 100%;
  left: 50%;
  transform: translateX(-50%);
  margin-top: 7px;
  font-size: 11px;
  color: var(--muted);
  white-space: nowrap;
}
.chart-tip {
  position: absolute;
  z-index: 5;
  transform: translate(-50%, -100%);
  padding: 6px 10px;
  border-radius: var(--radius-sm);
  background: var(--text);
  color: var(--surface);
  font-size: 12.5px;
  white-space: nowrap;
  pointer-events: none;
  box-shadow: var(--shadow-2);
}

/* Список с полосками: «кто больше» читается быстрее, чем в таблице цифр */
.ranked { display: flex; flex-direction: column; gap: 12px; }
.ranked.tight { gap: 8px; }
.rank-row { display: grid; gap: 5px; }
.rank-head { display: flex; align-items: baseline; gap: 8px; }
.rank-bar { height: 6px; border-radius: 3px; background: var(--surface-3); overflow: hidden; }
.rank-bar span { display: block; height: 100%; border-radius: 3px; background: var(--accent); }
.rank-row[data-client] { cursor: pointer; border-radius: var(--radius-sm); }
.rank-row[data-client]:hover .rank-head, .rank-row[data-client]:focus-visible .rank-head { color: var(--accent); }
.rank-row[data-client]:focus-visible { outline: 2px solid var(--accent); outline-offset: 3px; }

.slices { display: grid; grid-template-columns: repeat(auto-fit, minmax(210px, 1fr)); gap: 20px; }
.slices h3 { margin-bottom: 8px; font-weight: 600; }

/* В карточке клиента тот же дашборд, но в узкой колонке: график ниже, шкала уже */
#client-purchases .chart { --chart-h: 150px; gap: 8px; margin-top: 16px; }
#client-purchases .chart-scale { width: 72px; font-size: 11px; }

/* ---------------------------------------------------------------- таблицы */

.list-wrap {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  overflow: hidden;
  box-shadow: var(--shadow-1);
}
.table-scroll { overflow-x: auto; }

table.grid { width: 100%; border-collapse: collapse; min-width: 620px; }
table.grid td.title { min-width: 190px; }
/* Заголовок колонки — прописными и вразрядку, но мельче текста: прописные буквы
   Montserrat шире строчных примерно в полтора раза, и «ДАТА ПОСЛЕДНЕЙ ПОКУПКИ»
   набранное вровень с содержимым занимало две строки, поднимая шапку таблицы. */
table.grid th {
  text-align: left;
  font-size: 11.5px;
  text-transform: uppercase;
  letter-spacing: .03em;
  color: var(--muted);
  font-weight: 600;
  line-height: 1.35;
  padding: 10px 16px;
  border-bottom: 1px solid var(--border);
  background: var(--surface-2);
  /* Длинные заголовки переносятся, а не растягивают таблицу */
  white-space: normal;
}
table.grid td { padding: 11px 16px; border-bottom: 1px solid var(--row-border); vertical-align: middle; }
table.grid td.nowrap { white-space: nowrap; }
table.grid td .sub { color: var(--muted); font-size: 12.5px; margin-top: 2px; }
table.grid tbody tr:last-child td { border-bottom: none; }
table.grid tbody tr.tap { cursor: pointer; }
table.grid tbody tr.tap:hover { background: var(--surface-2); }
table.grid tbody tr.selected { background: var(--accent-soft); }

.pager { display: flex; gap: 10px; align-items: center; justify-content: space-between; padding: 11px 16px; color: var(--muted); font-size: 13px; border-top: 1px solid var(--border); }

/* --------------------------------------------- настраиваемые колонки

   Ширину колонки меняют, потянув за правый край её заголовка. Пока за край не
   взялись, ширину считает браузер — по содержимому; после первого растягивания
   таблица переходит на заданные размеры (класс «sized»), иначе соседние колонки
   прыгали бы при каждом движении мыши, деля между собой остаток места.

   Заданные размеры не значат «таблица шире экрана нельзя»: она остаётся не уже
   своего места, а если колонки растянули — прокручивается вбок, как и раньше. */
table.grid th { position: relative; }
table.grid.sized { table-layout: fixed; width: auto; min-width: 100%; }
/* В таблице заданных размеров содержимому расширить колонку нечем: длинное слово
   («Санкт-Петербург» в узкой колонке) переносится, а не выезжает на соседнюю */
table.grid.sized td { overflow-wrap: break-word; }

.col-grip {
  position: absolute;
  top: 0; right: -3px; bottom: 0;
  width: 9px;
  cursor: col-resize;
  /* Полоска рисуется внутри, а не по всей ширине хвата: хват должен быть шире
     видимой черты, иначе в неё приходится целиться. */
  touch-action: none;
  user-select: none;
}
.col-grip::after {
  content: "";
  position: absolute;
  top: 6px; bottom: 6px; left: 3px;
  width: 2px;
  border-radius: 2px;
  background: var(--border-strong);
  opacity: 0;
  transition: opacity .15s ease;
}
table.grid th:hover .col-grip::after { opacity: .7; }
.col-grip:hover::after { opacity: 1; background: var(--accent); }
/* Пока тянут — курсор «растягивания» на всей странице, и текст не выделяется */
body.col-resizing { cursor: col-resize; user-select: none; }
body.col-resizing .col-grip::after { opacity: 1; background: var(--accent); }

/* --------------------------------------------- панель у кнопки

   Список колонок и другие короткие настройки: открывается рядом с кнопкой и не
   закрывает собой то, что настраивает, — в отличие от модального окна. */
.popover {
  position: fixed;
  z-index: 90;
  min-width: 232px;
  max-width: min(340px, calc(100vw - 16px));
  padding: 6px;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  box-shadow: var(--shadow-3);
  display: flex;
  flex-direction: column;
  gap: 2px;
  animation: pop-in .12s ease-out;
}
@keyframes pop-in {
  from { opacity: 0; transform: translateY(-4px); }
  to { opacity: 1; transform: none; }
}
.pop-head {
  padding: 6px 8px 4px;
  font-size: 10.5px;
  font-weight: 600;
  letter-spacing: .05em;
  text-transform: uppercase;
  color: var(--muted);
}
.pop-list { display: grid; gap: 1px; max-height: min(52vh, 420px); overflow-y: auto; }
.pop-check {
  display: flex;
  align-items: center;
  gap: 9px;
  padding: 7px 8px;
  border-radius: var(--radius-sm);
  font-size: 14px;
  cursor: pointer;
}
.pop-check:hover { background: var(--surface-2); }
.pop-check input { flex: none; }
.pop-check input:disabled { opacity: .5; }
.pop-check input:disabled + span { color: var(--muted); }
.pop-foot {
  display: grid;
  gap: 4px;
  padding: 8px 8px 4px;
  margin-top: 4px;
  border-top: 1px solid var(--border);
  justify-items: start;
}
.pop-foot .btn { width: 100%; justify-content: flex-start; }

/* ---------------------------------------------------------------- диалоги/переписка */

.inbox { display: grid; grid-template-columns: minmax(300px, 360px) minmax(0, 1fr); gap: 14px; height: calc(100vh - var(--topbar-h) - 36px); }
.inbox > * { min-height: 0; }

.thread-list {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  display: flex;
  flex-direction: column;
  overflow: hidden;
  box-shadow: var(--shadow-1);
}
.thread-list .head { padding: 12px; border-bottom: 1px solid var(--border); display: grid; gap: 9px; }
.thread-scroll { overflow-y: auto; flex: 1; }

.thread {
  display: flex;
  gap: 11px;
  padding: 12px;
  border-bottom: 1px solid var(--row-border);
  cursor: pointer;
  align-items: flex-start;
}
.thread:hover { background: var(--surface-2); }
.thread.active { background: var(--accent-soft); }
.avatar {
  width: 40px; height: 40px; flex: none;
  border-radius: 50%;
  background: var(--surface-3);
  color: var(--text-2);
  display: inline-flex; align-items: center; justify-content: center;
  font-weight: 600; font-size: 13.5px;
  overflow: hidden;
  position: relative;
}
/* Фотография перекрывает буквы, а не заменяет их: не загрузилась — под ней
   остаётся то же, что было бы и без неё, а не пустой кружок. */
.avatar img { width: 100%; height: 100%; object-fit: cover; display: block; }
.avatar i { font-style: normal; }
.avatar.has-photo i { position: absolute; inset: 0; display: flex;
  align-items: center; justify-content: center; z-index: -1; }
.thread.unread .avatar:not(.has-photo) { background: var(--accent); color: #fff; }
.chat-face { display: inline-flex; text-decoration: none; }
.chat-face:hover .avatar { outline: 2px solid var(--accent); outline-offset: 1px; }
.thread .body { flex: 1; min-width: 0; }
.thread .title { display: flex; align-items: center; gap: 6px; }
.thread .title b { font-weight: 600; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.thread .preview { color: var(--muted); font-size: 13.5px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.thread .meta { display: flex; flex-direction: column; align-items: flex-end; gap: 5px; flex: none; font-size: 12px; color: var(--muted); }

.chat {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  display: flex;
  flex-direction: column;
  overflow: hidden;
  box-shadow: var(--shadow-1);
}
.chat-head { padding: 12px 14px; border-bottom: 1px solid var(--border); display: flex; gap: 10px; align-items: center; flex-wrap: wrap; }
.chat-head .who { flex: 1; min-width: 0; }
.chat-body { flex: 1; overflow-y: auto; padding: 16px; background: var(--surface-2); display: flex; flex-direction: column; gap: 10px; }
.chat-foot { padding: 12px; border-top: 1px solid var(--border); display: grid; gap: 8px; }
.chat-foot .row { align-items: flex-end; }
.chat-foot textarea { min-height: 44px; max-height: 160px; }

/* Что приложено к ответу, но ещё не отправлено. Полоска стоит над строкой ввода:
   файл прикладывают к сообщению, и видеть их надо вместе. */
.attach-tray { display: flex; flex-wrap: wrap; gap: 6px; }
.attach-tray .chip.file { padding-right: 6px; }
.attach-tray .link { margin-left: 4px; font-size: 14px; line-height: 1; }

/* Файл тянут в разговор, а не в поле ввода: подсвечиваем всё окно диалога —
   иначе непонятно, куда именно отпускать. */
.chat.drop-target { outline: 2px dashed var(--accent); outline-offset: -4px; }
.chat.drop-target .chat-body { background: var(--accent-soft); }

.bubble {
  max-width: min(560px, 82%);
  padding: 9px 13px;
  border-radius: 14px;
  background: var(--surface);
  border: 1px solid var(--border);
  box-shadow: var(--shadow-1);
  word-break: break-word;
}
/* Переносы строк сохраняем только в тексте сообщения, иначе разметка добавляет пустоты */
.bubble .text { white-space: pre-wrap; }
.bubble.out { align-self: flex-end; background: var(--accent-soft); border-color: var(--accent-soft); }
.bubble.failed { background: var(--danger-soft); border-color: var(--danger-soft); }
.bubble .meta { margin-top: 5px; font-size: 11.5px; color: var(--muted); display: flex; gap: 7px; flex-wrap: wrap; }
.day-divider { align-self: center; font-size: 12px; color: var(--muted); background: var(--surface-3); padding: 3px 12px; border-radius: 999px; }
/* Кто написал — над текстом входящего. В переписке из группового чата иначе
   не понять, чья это реплика, а в личной строка просто не появляется. */
.bubble .who { font-size: 12px; font-weight: 600; color: var(--accent); margin-bottom: 3px; }
/* «Показать раньше» — вверху переписки: сколько её ещё осталось в CRM */
.chat-more { align-self: center; padding: 2px 0 6px; }

/* ---------------------------------------------------------------- карточки в окне

   Карточка клиента и карточка покупки живут в окне поверх интерфейса: шапка с
   действиями и вкладками остаётся на месте, а прокручивается только содержимое
   вкладки. Иначе, уйдя вниз по списку покупок, человек теряет и кнопки, и вкладки. */

.popup-head {
  position: sticky;
  top: -20px;
  z-index: 3;
  background: var(--surface);
  padding-top: 2px;
  margin: -2px -2px 0;
  padding-right: 40px;
}
.popup-head h2 { font-size: 19px; margin-bottom: 4px; }
.popup-head .tabs { margin-top: 14px; border-bottom: 1px solid var(--border); padding-bottom: 10px; }
.popup-body { padding-top: 14px; }
.tabs { display: flex; gap: 6px; flex-wrap: wrap; }
.tabs .count { color: inherit; opacity: .7; font-weight: 400; font-size: 12.5px; }
.block-title { font-size: 13px; color: var(--muted); font-weight: 600; margin: 16px 0 8px; }
.tab-panel > .block-title:first-child { margin-top: 0; }

.section { border-bottom: 1px solid var(--border); padding: 12px 0; }
.section:last-child { border-bottom: none; }
.section > summary {
  cursor: pointer;
  list-style: none;
  display: flex;
  align-items: center;
  gap: 8px;
  font-weight: 600;
  font-size: 14.5px;
  padding: 4px 0;
}
.section > summary::-webkit-details-marker { display: none; }
.section > summary::after {
  content: "";
  margin-left: auto;
  flex: none;
  width: 7px; height: 7px;
  border-right: 1.8px solid var(--muted);
  border-bottom: 1.8px solid var(--muted);
  transform: translateY(-2px) rotate(45deg);
  transition: transform .2s ease;
}
.section[open] > summary::after { transform: translateY(2px) rotate(225deg); }
.section .section-body { padding-top: 10px; }
.section .count { color: var(--muted); font-weight: 400; font-size: 13px; }

.mini-list { display: flex; flex-direction: column; gap: 8px; }
.mini-row {
  display: flex; justify-content: space-between; gap: 12px;
  padding: 6px 0; border-bottom: 1px solid var(--row-border);
  font-size: 14px;
}
.mini-row:last-child { border-bottom: none; }
.mini-row > span:last-child { text-align: right; overflow-wrap: anywhere; }
.mini {
  border: 1px solid var(--border);
  border-left: 3px solid var(--accent);
  border-radius: var(--radius-sm);
  padding: 9px 12px;
  background: var(--surface-2);
}
.mini .when { color: var(--muted); font-size: 12.5px; }
.mini.in { border-left-color: var(--info); }
.mini.fail { border-left-color: var(--danger); }
.mini.gray { border-left-color: var(--border-strong); }
/* Покупка в карточке клиента открывается нажатием — и должна выглядеть нажимаемой */
.mini.tap { cursor: pointer; }
.mini.tap:hover { background: var(--surface-3); border-color: var(--border-strong); }
.mini.tap:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

.stat-row { display: grid; grid-template-columns: repeat(auto-fit, minmax(148px, 1fr)); gap: 10px; }
.stat { background: var(--surface-2); border-radius: var(--radius-sm); padding: 10px 12px; min-width: 0; }
.stat b { display: block; font-size: 17px; overflow-wrap: anywhere; }
/* Те же ступени, что и у плиток сводки: товарооборот структуры бывает девятизначным */
.stat b.num-s { font-size: 14.5px; }
.stat b.num-xs { font-size: 13px; letter-spacing: -.01em; }
.stat span { color: var(--muted); font-size: 12.5px; }

/* ---------------------------------------------------------------- модальные окна */

.modal {
  position: fixed;
  inset: 0;
  background: rgba(16, 24, 40, .5);
  display: flex;
  align-items: center;
  justify-content: center;
  z-index: 80;
  padding: 18px;
}
.modal-body {
  background: var(--surface);
  border-radius: var(--radius);
  padding: 20px;
  width: min(680px, 100%);
  max-height: 88vh;
  overflow-y: auto;
  position: relative;
  box-shadow: var(--shadow-3);
}
.modal-body.wide { width: min(1020px, 100%); max-height: 92vh; }
.modal-body h2 { margin-bottom: 14px; padding-right: 34px; }
.modal-body.wide h2 { padding-right: 0; }
/* Пока окно открыто, страница под ним не прокручивается: прокрутка «проваливалась»
   на список за окном, и человек терял место, к которому вернётся после закрытия */
body.modal-open { overflow: hidden; }
.modal-close {
  position: absolute; right: 10px; top: 10px;
  width: 34px; height: 34px;
  border: none; border-radius: 50%;
  background: var(--surface-3);
  font-size: 19px; line-height: 1; cursor: pointer; color: var(--muted);
  /* Выше липкой шапки карточки: иначе она закрывает собой крестик,
     и окно, которое открылось поверх всего, становится нечем закрыть */
  z-index: 5;
}
.modal-close:hover { background: var(--border); color: var(--text); }

.toast {
  position: fixed;
  left: 50%;
  bottom: 22px;
  transform: translateX(-50%);
  background: #16202b;
  color: #fff;
  padding: 11px 18px;
  border-radius: 999px;
  z-index: 100;
  box-shadow: var(--shadow-2);
  max-width: min(520px, 92vw);
  text-align: center;
}
.toast.error { background: var(--danger); }

/* ---------------------------------------------------------------- строки покупки */

.items { display: flex; flex-direction: column; gap: 10px; }
.item-row {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  gap: 8px;
  align-items: center;
  padding: 10px;
  background: var(--surface-2);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
}
.item-numbers { display: flex; align-items: center; gap: 7px; }
.item-numbers .sign { color: var(--muted); }
.item-numbers .it-qty { width: 72px; text-align: center; }
.item-numbers .it-price { width: 104px; }
.item-numbers b { min-width: 96px; text-align: right; white-space: nowrap; }

@media (max-width: 640px) {
  .item-row { grid-template-columns: minmax(0, 1fr); }
  .item-numbers { flex-wrap: wrap; }
  .item-numbers b { flex: 1; }
}

pre.preview {
  background: var(--surface-2);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  padding: 12px;
  white-space: pre-wrap;
  word-break: break-word;
  margin: 0;
  font-size: 13.5px;
}

/* ---------------------------------------------------------------- вход */

/* Пока не вошли — CRM скрыта целиком: ни пустых списков, ни мигания разделами. */
body.signed-out .app,
body.signed-out .tabbar { display: none; }

.login {
  position: fixed;
  inset: 0;
  z-index: 90;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: max(20px, env(safe-area-inset-top, 0px)) 20px max(20px, env(safe-area-inset-bottom, 0px));
  overflow-y: auto;
  background: var(--login-bg);
}

/* Мягкие пятна на фоне — глубина без картинок и без загрузок со стороны */
.login-art { position: absolute; inset: 0; overflow: hidden; pointer-events: none; }
.login-art span {
  position: absolute;
  border-radius: 50%;
  filter: blur(70px);
  opacity: .5;
}
.login-art span:first-child {
  width: 420px; height: 420px;
  top: -120px; left: -100px;
  background: var(--accent);
}
.login-art span:last-child {
  width: 360px; height: 360px;
  right: -110px; bottom: -120px;
  background: var(--info);
}

.login-card {
  position: relative;
  z-index: 1;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 22px;
  box-shadow: var(--shadow-3);
  padding: 30px 26px 24px;
  width: min(430px, 100%);
  margin: auto;
}
.login-card .logo {
  width: 56px; height: 56px;
  margin: 0 auto;
  display: flex; align-items: center; justify-content: center;
  font-size: 30px;
  border-radius: 18px;
  background: var(--accent-soft);
}
.login-card h1 { text-align: center; margin: 14px 0 6px; font-size: 22px; letter-spacing: -.01em; }
.login-card .lead { text-align: center; color: var(--muted); font-size: 14px; margin-bottom: 22px; }
.login-card .field { margin-bottom: 14px; }
/* Вход · Регистрация · Забыли пароль — одна карточка в трёх режимах */
.login-modes { display: flex; flex-wrap: wrap; gap: 6px; justify-content: center; margin-bottom: 18px; }
.login-modes .filter { flex: none; }
/* Код восстановления показывается один раз — его переписывают с экрана */
.code-big {
  font-size: 19px;
  line-height: 1.5;
  letter-spacing: .09em;
  text-align: center;
  word-break: break-all;
  margin: 4px 0 12px;
}
.login-card .switch.remember { font-size: 14px; color: var(--text-2); margin-bottom: 6px; }
.btn.big { min-height: 48px; font-size: 15.5px; font-weight: 600; }

/* Ошибка живёт в карточке, а не во всплывающей подсказке: её можно перечитать */
.login-alert {
  display: flex;
  gap: 9px;
  padding: 11px 13px;
  margin-bottom: 16px;
  border-radius: var(--radius-sm);
  background: var(--danger-soft);
  color: var(--danger);
  font-size: 14px;
  line-height: 1.4;
}
.login-alert::before {
  content: "";
  flex: none;
  width: 3px;
  border-radius: 2px;
  background: currentColor;
  opacity: .6;
}
.login-alert.info { background: var(--info-soft); color: var(--info); }
.login-alert.ok { background: var(--accent-soft); color: var(--accent); }

.input-with-button { position: relative; }
.input-with-button input { padding-right: 46px; }
.input-with-button .peek {
  position: absolute;
  right: 4px; top: 50%;
  transform: translateY(-50%);
  width: 38px; height: 38px;
  border: none;
  border-radius: var(--radius-sm);
  background: transparent;
  cursor: pointer;
  font-size: 17px;
  opacity: .55;
}
.input-with-button .peek:hover { opacity: 1; background: var(--surface-3); }
.input-with-button .peek[aria-pressed="true"] { opacity: 1; color: var(--accent); }

/* Пояснение под кнопкой: что произойдёт после нажатия. Появляется только там,
   где это правда нужно, — при первом запуске и при доступе по коду. */
#login-note { margin-top: 14px; text-align: center; line-height: 1.5; }

.login-foot {
  margin-top: 16px;
  text-align: center;
  color: var(--muted);
  font-size: 12.5px;
  display: flex;
  gap: 7px;
  justify-content: center;
  flex-wrap: wrap;
}
.login-foot .dot { opacity: .5; }
.login-foot .offline { color: var(--danger); }

.noscript {
  position: fixed;
  inset: 0;
  z-index: 200;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 30px;
  text-align: center;
  background: var(--surface);
  color: var(--text);
}

/* ---------------------------------------------------------------- адаптив */

@media (max-width: 1024px) {
  .app { grid-template-columns: minmax(0, 1fr); }
  .sidebar {
    position: fixed;
    top: 0; left: 0; bottom: 0;
    width: min(284px, 84vw);
    transform: translateX(-101%);
    transition: transform .22s ease;
    box-shadow: var(--shadow-3);
  }
  .sidebar.open { transform: none; }
  /* В выдвижном меню по разделам попадают пальцем, а не курсором: строки выше и
     просторнее. Места на это хватает — меню занимает весь экран в высоту, а на
     узком внизу есть ещё и панель с частыми разделами. */
  .nav-item { min-height: 40px; padding: 9px 12px; font-size: 14px; }
  .account-btn { padding: 8px; }
  .icon-btn { display: inline-flex; }
  .inbox { grid-template-columns: minmax(0, 1fr); height: auto; }
  .inbox .chat { display: none; }
  .inbox.chat-open .thread-list { display: none; }
  .inbox.chat-open .chat { display: flex; }
  .chat, .thread-list { height: calc(100vh - var(--topbar-h) - 36px); }
  .thread-scroll { max-height: none; }
}

@media (max-width: 860px) {
  .content { padding: 14px 16px 20px; }
  /* Настройка колонок на телефоне ни к чему: строки таблиц здесь превращаются в
     карточки, и колонок как таковых у них нет */
  .only-wide { display: none !important; }
  .topbar { padding: 8px 16px; }
  .cards { grid-template-columns: repeat(auto-fill, minmax(160px, 1fr)); gap: 10px; }
  .card .value { font-size: 20px; }
  /* На телефоне плитка уже, поэтому и ступени для длинных сумм ниже */
  .card .value.num-s { font-size: 16.5px; }
  .card .value.num-xs { font-size: 13.5px; }
  .chart { --chart-h: 160px; gap: 8px; }
  .chart-scale { width: 66px; font-size: 11px; }

  /* Таблицы превращаются в карточки — на телефоне ничего не «уезжает» вбок */
  table.grid thead { display: none; }
  table.grid, table.grid tbody, table.grid tr, table.grid td { display: block; width: 100%; }
  table.grid { min-width: 0; }
  table.grid td.title { min-width: 0; }
  table.grid tr {
    border-bottom: 1px solid var(--border);
    padding: 12px 14px;
  }
  table.grid tbody tr:last-child { border-bottom: none; }
  table.grid td { border: none; padding: 3px 0; display: flex; gap: 12px; align-items: baseline; flex-wrap: wrap; }
  /* Пояснение под значением («телефон» под именем) уходит на свою строку */
  table.grid td > .sub { flex-basis: 100%; }
  table.grid td::before {
    content: attr(data-label);
    flex: none;
    width: 42%;
    color: var(--muted);
    font-size: 12.5px;
  }
  table.grid td.nowrap { white-space: normal; }
  table.grid td.wide { display: block; }
  table.grid td.wide::before { display: block; width: auto; margin-bottom: 2px; }
  table.grid td.title { font-weight: 600; font-size: 16px; padding-bottom: 6px; }
  table.grid td.title::before { display: none; }
  table.grid td.actions { padding-top: 8px; }
  table.grid td.actions::before { display: none; }

  .kv { grid-template-columns: 1fr; gap: 2px 0; }
  .kv .k { margin-top: 8px; }
  .modal { padding: 0; align-items: flex-end; }
  .modal-body { width: 100%; max-height: 92vh; border-radius: 18px 18px 0 0; padding: 18px 16px 24px; }
  /* Карточке на телефоне нужен весь экран: в ней вкладки, таблицы и график */
  .modal-body.wide { max-height: 100vh; height: 100%; border-radius: 0; }
  .popup-head { top: -18px; margin: -2px -2px 0; padding-right: 38px; }
}

@media (max-width: 640px) {
  body { padding-bottom: calc(var(--tabbar-h) + env(safe-area-inset-bottom, 0px)); }
  .tabbar {
    display: grid;
    /* Разделов в панели пять вместе с «Ещё»: в четырёх колонках последняя кнопка
       переносилась на вторую строку и панель занимала двойную высоту */
    grid-template-columns: repeat(5, 1fr);
    position: fixed;
    left: 0; right: 0; bottom: 0;
    z-index: 50;
    background: var(--surface);
    border-top: 1px solid var(--border);
    padding-bottom: env(safe-area-inset-bottom, 0px);
  }
  .tabbar button {
    border: none;
    background: none;
    padding: 8px 4px 9px;
    color: var(--muted);
    font-size: 11.5px;
    cursor: pointer;
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 3px;
    min-height: var(--tabbar-h);
    justify-content: center;
    position: relative;
  }
  .tabbar button .ico { font-size: 19px; }
  .tabbar button.active { color: var(--accent); font-weight: 600; }
  .tabbar .badge { position: absolute; top: 6px; left: 55%; }

  /* Кнопки раздела — своей строкой под названием, а не рядом с ним. Пять кнопок
     («Обновить», «Структура», «Порядок», «CSV», «Клиент») в строку с заголовком
     не помещаются: они сползали в два ряда, шапка вырастала вдвое и наезжала на
     название. В одну строку со сдвигом вбок они читаются как панель раздела. */
  .topbar { flex-wrap: wrap; padding-bottom: 8px; }
  .topbar h1 { flex: 1 1 auto; }
  .topbar-actions {
    order: 3;
    flex: 1 0 100%;
    justify-content: flex-start;
    flex-wrap: nowrap;
    overflow-x: auto;
    scrollbar-width: none;
  }
  .topbar-actions::-webkit-scrollbar { display: none; }
  .topbar-actions > * { flex: none; }

  .inbox, .chat, .thread-list { height: calc(100vh - var(--topbar-h) - var(--tabbar-h) - 24px); }
  /* Кнопки в шапке диалога переносятся на свою строку — заголовок не сжимается */
  .chat-head .row { width: 100%; }
  .chat-head .row .btn { flex: 1; }
  .toolbar .search { max-width: none; }
  .toolbar select { width: 100%; min-width: 0; }
  .btn { min-height: 42px; }
  /* iOS не увеличивает страницу при фокусе, если шрифт не меньше 16px */
  input, select, textarea { font-size: 16px; }
  .bubble { max-width: 92%; }
  .form-grid { grid-template-columns: 1fr; }
  .panel { padding: 14px; }
  /* Вкладки в карточке не переносятся на вторую строку, а прокручиваются вбок:
     иначе шапка съедает пол-экрана ещё до первой цифры */
  .tabs { flex-wrap: nowrap; overflow-x: auto; padding-bottom: 4px; }
  .tabs .filter { flex: none; }
  .reports-toolbar .date-field { width: 100%; }
}

@media (min-width: 1025px) {
  .sidebar { transform: none !important; }
  .backdrop { display: none; }
}

@media (prefers-reduced-motion: reduce) {
  * { transition: none !important; }
}


/* Меню по правой кнопке мыши в списке диалогов. Главное в нём — «отметить
   непрочитанным»: диалог помечается прочитанным в момент открытия, и одного
   случайного нажатия хватает, чтобы сообщение без ответа потерялось в списке. */
.ctx-menu {
  position: fixed;
  z-index: 120;
  min-width: 232px;
  padding: 5px;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  box-shadow: var(--shadow-2, 0 12px 32px rgba(0, 0, 0, .18));
  display: flex;
  flex-direction: column;
  gap: 1px;
}
.ctx-menu button {
  display: flex; align-items: center; gap: 9px;
  padding: 9px 11px;
  border: none; background: none; cursor: pointer;
  color: var(--text); font: inherit; font-size: 14px;
  border-radius: var(--radius-sm);
  text-align: left; width: 100%;
}
.ctx-menu button:hover { background: var(--surface-2); }
.ctx-menu button svg { flex: none; color: var(--muted); }
/* Удаление — единственное необратимое действие в меню, и выглядеть оно должно
   иначе, чем «скопировать»: промахнуться мимо соседнего пункта слишком легко */
.ctx-menu button.danger { color: var(--danger); }
.ctx-menu button.danger svg { color: var(--danger); }

/* ---------------------------------------------------------------- эмодзи

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

.emoji-panel {
  position: fixed;
  z-index: 130;
  width: min(340px, calc(100vw - 16px));
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  box-shadow: var(--shadow-2, 0 12px 32px rgba(0, 0, 0, .18));
  display: flex;
  flex-direction: column;
  overflow: hidden;
}
.emoji-search { padding: 8px; border-bottom: 1px solid var(--border); }
.emoji-search input { width: 100%; }
.emoji-tabs {
  display: flex; gap: 2px; padding: 6px 6px 0; overflow-x: auto;
  border-bottom: 1px solid var(--border);
}
.emoji-tabs button {
  border: none; background: none; cursor: pointer; white-space: nowrap;
  color: var(--muted); font: inherit; font-size: 12.5px;
  padding: 6px 9px; border-radius: var(--radius-sm) var(--radius-sm) 0 0;
}
.emoji-tabs button.active { color: var(--text); background: var(--surface-2); font-weight: 600; }
.emoji-grid {
  display: grid; grid-template-columns: repeat(8, 1fr); gap: 2px;
  padding: 8px; max-height: 240px; overflow-y: auto;
}
.emoji-grid button {
  border: none; background: none; cursor: pointer;
  font-size: 21px; line-height: 1; padding: 5px 0;
  border-radius: var(--radius-sm);
}
.emoji-grid button:hover { background: var(--surface-2); }
.emoji-empty { grid-column: 1 / -1; padding: 18px 8px; text-align: center; color: var(--muted); }

/* -------------------------------------------------- картинки и файлы в переписке

   Картинку отдаёт сама CRM: ссылку мессенджера браузер из России не откроет, а у
   вложений Telegram открытой ссылки нет вовсе (см. app/services/media_service.py). */

.bubble-pics { display: flex; flex-wrap: wrap; gap: 5px; margin-bottom: 6px; }
.bubble-pics img {
  display: block;
  max-width: 240px; max-height: 260px;
  border-radius: 10px;
  object-fit: cover;
  background: var(--surface-3);
}
/* Картинка в переписке — кнопка, а не ссылка: она открывает просмотрщик, а не
   соседнюю вкладку браузера. Кнопке снимаем всё «кнопочное», чтобы от картинки
   ничего не отвлекало. */
.bubble-pic { padding: 0; border: 0; background: none; cursor: zoom-in; line-height: 0; }
.bubble-pic:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; border-radius: 10px; }
.bubble-files { display: flex; flex-wrap: wrap; gap: 5px; margin-bottom: 6px; }
.bubble-files .chip { display: inline-flex; align-items: center; gap: 5px; }
/* Файл и ссылка — то, что открывают: значок остаётся значком, а подпись
   подчёркивается при наведении, как обычная ссылка */
.chip.file { background: var(--surface-3); color: var(--text-2); text-decoration: none; max-width: 100%; }
.chip.file:hover { background: var(--accent-soft); color: var(--accent); }
.chip.file span { text-overflow: ellipsis; overflow: hidden; }

/* -------------------------------------------------- просмотр картинок

   Галерея во весь экран: раньше картинка открывалась соседней вкладкой, и чтобы
   посмотреть три фотографии, приходилось открыть и закрыть три вкладки. Здесь
   стрелки между соседними картинками разговора и Escape на закрытие — то, чего
   ждут от любой галереи. */
.pic-viewer {
  position: fixed;
  inset: 0;
  z-index: 210;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  padding: 16px;
  background: rgba(8, 12, 17, .92);
}
.pic-viewer.hidden { display: none; }
.pic-viewer figure { margin: 0; display: flex; flex-direction: column; gap: 10px; max-height: 100%; }
.pic-viewer img {
  max-width: min(1200px, 88vw);
  max-height: 80vh;
  object-fit: contain;
  border-radius: 10px;
  background: #0b1016;
}
.pic-viewer figcaption {
  display: flex; align-items: center; gap: 10px;
  color: #e8eef4; font-size: 13.5px;
}
.pic-viewer figcaption .muted { color: #9aa8b6; }
.pic-viewer .btn { background: rgba(255, 255, 255, .12); color: #e8eef4; border-color: transparent; }
.pic-viewer .btn:hover { background: rgba(255, 255, 255, .2); }
.pic-viewer .pic-nav,
.pic-viewer .pic-close {
  display: flex; align-items: center; justify-content: center;
  width: 42px; height: 42px;
  border: 0; border-radius: 999px;
  background: rgba(255, 255, 255, .12);
  color: #fff;
  cursor: pointer;
}
.pic-viewer .pic-nav:hover, .pic-viewer .pic-close:hover { background: rgba(255, 255, 255, .24); }
.pic-viewer .pic-close { position: absolute; top: 14px; right: 14px; }
.pic-viewer .pic-nav.hidden { visibility: hidden; }

/* Полка «присланное в диалоге»: картинки плиткой, файлы и ссылки списком */
.media-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(120px, 1fr));
  gap: 8px;
}
.media-tile {
  position: relative;
  padding: 0; border: 0; line-height: 0;
  border-radius: 10px; overflow: hidden;
  background: var(--surface-3);
  cursor: zoom-in;
}
.media-tile img { width: 100%; height: 120px; object-fit: cover; display: block; }
.media-tile .when {
  position: absolute; left: 0; right: 0; bottom: 0;
  padding: 3px 6px;
  font-size: 11.5px; line-height: 1.4;
  color: #fff;
  background: linear-gradient(transparent, rgba(8, 12, 17, .75));
}
/* Ссылка внутри сообщения: подчёркнута, но не спорит с текстом цветом фона пузыря */
.bubble .text a { color: inherit; text-decoration: underline; text-underline-offset: 2px; }
.bubble .text a:hover { text-decoration-thickness: 2px; }
/* Сколько диалогов ещё не показано — подсказка внизу списка, пока он догружается */
.thread-more { padding: 10px 14px; text-align: center; }

/* -------------------------------------------------- заставка загрузки

   Видна только тем, кто уже вошёл, и живёт ровно то время, пока грузятся разделы.
   Раньше на её месте была форма входа: страница приходит с признаком «не вошли», и
   при каждом обновлении F5 человек на секунду видел приглашение войти заново. Сеанс
   при этом никуда не девался — просто о нём ещё не успели спросить (см. index.html). */

.booting-screen { display: none; }
body.booting .booting-screen {
  display: flex;
  position: fixed;
  inset: 0;
  z-index: 95;
  align-items: center;
  justify-content: center;
  background: var(--login-bg);
}
/* Форма входа под заставкой не нужна: сеанс есть, спрашивать нечего */
body.booting .login { display: none; }

.booting-card { display: flex; flex-direction: column; align-items: center; gap: 14px; }
.booting-card .logo {
  display: grid; place-items: center;
  width: 52px; height: 52px;
  border-radius: 15px;
  background: var(--accent); color: #fff;
}
.booting-card .logo .mark { width: 30px; height: 30px; }
.booting-card p { margin: 0; }

/* Полоска, которая едет сама: сколько осталось ждать, неизвестно, и рисовать
   проценты было бы выдумкой. Движение говорит одно — CRM жива и загружается. */
.booting-bar {
  width: 168px; height: 4px;
  border-radius: 3px;
  background: var(--surface-3);
  overflow: hidden;
}
.booting-bar span {
  display: block; width: 40%; height: 100%;
  border-radius: 3px;
  background: var(--accent);
  animation: booting-slide 1.1s ease-in-out infinite;
}
@keyframes booting-slide {
  0% { transform: translateX(-100%); }
  100% { transform: translateX(250%); }
}

/* Переход между разделами: короткое проявление вместо мгновенной подмены. Дольше
   ждать никого не заставляет — содержимое уже на месте, меняется только видимость. */
.view.active { animation: view-in .16s ease-out; }
@keyframes view-in {
  from { opacity: 0; transform: translateY(4px); }
  to { opacity: 1; transform: none; }
}

@media (prefers-reduced-motion: reduce) {
  .booting-bar span { animation: none; width: 100%; }
  .view.active { animation: none; }
}

/* -------------------------------------------------- дерево структуры MLM

   Отступ и вертикальная черта вместо схемы с квадратиками и стрелками: у партнёра в
   первой линии бывает и три человека, и сорок, и схема в ширину перестаёт помещаться
   на экране уже на втором уровне. Отступ читается при любой ширине ветки — и на
   телефоне тоже (см. app/web/js/clients.js). */

.tree { font-size: 14px; }
.tree-node { position: relative; }
.tree-row { display: flex; align-items: flex-start; gap: 8px; padding: 5px 0; }

.tree-kids {
  margin-left: 12px;
  padding-left: 16px;
  border-left: 1px solid var(--border);
}
.tree-kids.hidden { display: none; }

/* Кнопка со счётчиком: сколько человек в ветке под этим, и она же её сворачивает */
.tree-toggle {
  flex: none;
  display: inline-flex; align-items: center; justify-content: center;
  min-width: 26px; height: 24px; padding: 0 6px;
  margin-top: 3px;
  border: 1px solid var(--border-strong);
  border-radius: 999px;
  background: var(--surface);
  color: var(--muted);
  font: inherit; font-size: 12px; line-height: 1;
  cursor: pointer;
}
.tree-toggle:hover { border-color: var(--accent); color: var(--accent); }
.tree-toggle.open { background: var(--accent-soft); border-color: var(--accent-soft); color: var(--accent); }
.tree-toggle.empty { border: none; background: none; cursor: default; min-width: 26px; }

.tree-face {
  display: flex; align-items: flex-start; gap: 10px;
  flex: 1 1 auto; min-width: 0;
  padding: 5px 10px;
  border-radius: var(--radius-sm);
  background: var(--surface-2);
}
.tree-face:hover { background: var(--surface-3); }
.tree-name { display: flex; align-items: center; gap: 7px; flex-wrap: wrap; font-weight: 600; }
.tree-name .chip { font-weight: 400; }
.tree-face .sub { color: var(--muted); font-size: 12.5px; margin-top: 2px; }
.tree-team { flex: none; color: var(--text-2); font-size: 12.5px; text-align: right; white-space: nowrap; }
.tree-more { color: var(--muted); font-size: 12.5px; padding: 2px 0 6px 38px; }

@media (max-width: 640px) {
  .tree-face { flex-direction: column; gap: 2px; }
  .tree-team { text-align: left; }
  .tree-kids { margin-left: 6px; padding-left: 10px; }
}

/* Канал, которого у человека ещё нет: галочка не гаснет насовсем, а объясняет себя.
   Поставить её нечем — писать некуда, — но и прятать строку нельзя: тогда неясно,
   почему в списке четыре канала, а видно два (см. app/web/js/clients.js). */
.switch.disabled { color: var(--muted); cursor: default; }
.switch.disabled input { cursor: default; }
