/* bdnp.ru — интерфейс. Дизайн-система «Меридиан»: тёмный командный центр,
   циан-акцент, техно-детали (уголки-скобки, срез угла панели, фон-сетка).
   Шрифты: Inter (везде) + Commissioner (только заголовки страниц/блоков и меню), оба с
   кириллицей и свободные (OFL), локально в /static/fonts (self-hosted, офлайн).

   ЕДИНЫЙ ИСТОЧНИК ПРАВДЫ. Всё — палитра, кегль, отступы, форма — через
   CSS-переменные ниже. Перекрашиваем/переразмечаем в ОДНОМ месте. Компоненты —
   классами (.panel/.btn/.chip/.status/.data-table), инлайн-стилей в разметке нет.
   Скруглений нет (острые углы + фаска clip-path). Капс — только у ярлыков,
   заголовков, кнопок; данные и ввод — обычным регистром (читаемость). */

:root {
  /* ── Базовые тона (R G B без alpha) ────────────────────────────────────────
     На них построено ВСЁ остальное: прозрачные заливки берутся как
     rgb(var(--X-rgb) / .10). Смена темы = смена этих девяти строк (+ текст ниже),
     а не правка сотни мест. */
  --accent-rgb:   95 216 232;   /* циан — главный акцент */
  --accent2-rgb: 245 135 43;    /* оранжевый — второй акцент / «опасное» */
  --amber-rgb:   232 163 61;    /* янтарь — предупреждение */
  --danger-rgb:  224 96 74;     /* красный — ошибка / возврат */
  --green-rgb:    74 190 130;   /* зелёный — доходы в ДДС */
  --line-rgb:    110 160 180;   /* линии и рамки */
  --surface-rgb:  18 26 33;     /* поверхность панели/чипа */
  --surface2-rgb: 12 18 23;     /* поверхность, низ градиента панели */
  --deep-rgb:     10 14 19;     /* самый глубокий фон (поля ввода, трек ползунка) */
  /* Категорийный ряд: для долей в пироге (источники лидов). Не «статусы», а просто
     различимые между собой тона — первый совпадает с главным акцентом. */
  --cat-1-rgb:    95 216 232;   /* циан */
  --cat-2-rgb:   232 163 61;    /* янтарь */
  --cat-3-rgb:    74 190 130;   /* зелёный */
  --cat-4-rgb:   125 155 176;   /* сине-серый */
  --cat-5-rgb:   143 127 208;   /* лиловый */
  --cat-6-rgb:    91 139 208;   /* синий (НЕ оттенок циана: рядом с --cat-1 не различить) */

  /* ── Поверхности (тёмные, сине-зелёные) ── */
  --bg:          #0a0e13;
  --panel-solid: #121a21;
  --panel-top:   rgb(var(--surface-rgb) / .92);   /* верх градиента панели */
  --panel-bot:   rgb(var(--surface2-rgb) / .90);   /* низ градиента панели */
  --panel-top-dim: rgb(var(--surface-rgb) / .55); /* приглушённая (неактивная) панель */
  --panel-bot-dim: rgb(var(--surface2-rgb) / .50);

  /* ── Акценты и статусы (выводятся из базовых тонов выше) ── */
  --accent:        rgb(var(--accent-rgb));   /* циан — главный акцент */
  --accent-bright: #8ee6f2;                  /* ховер по акценту */
  --accent-2:      rgb(var(--accent2-rgb));  /* оранжевый — второй акцент / «опасное» */
  --amber:         rgb(var(--amber-rgb));    /* янтарь — предупреждение / «в пути» */
  --danger:        rgb(var(--danger-rgb));   /* красный — ошибка / возврат */

  /* ── Цвет текста в цветных блоках ДДС (заголовки блоков) ── */
  --dds-income-text:  #8fe3b2;
  --dds-expense-text: #eaa294;
  --dds-credit-text:  #e6c489;
  --dds-profit-text:  #a9dfe8;

  /* ── Текст ── */
  --text:  #d6e4ea;                       /* основной */
  --text-2:#8fa7b5;                       /* вторичный */
  --muted: #5c7482;                       /* подписи / метки */
  --faint: #465964;                       /* самый тихий */

  /* ── Линии и заливки ── */
  --line:        rgb(var(--line-rgb) / .20);
  --line-soft:   rgb(var(--line-rgb) / .12);
  --line-faint:  rgb(var(--line-rgb) / .045);
  /* Линии ТАБЛИЦ — плотные, не полупрозрачные. Полупрозрачная линия смешивается с тем,
     что под ней, а под липкой колонкой фон другой, чем под прокручиваемой частью, — и
     одна и та же линия выглядела в первой колонке иначе. Цвет подобран как результат
     прежнего смешения над панелью, поэтому вид не поменялся, а поведение стало ровным. */
  --row-line:    #1f2c35;   /* линия между строками */
  --head-line:   #26363f;   /* линия под шапкой и вертикальные разделители колонок */
  --month-col:   118px;     /* ширина столбца месяца в помесячных таблицах */
  --accent-line: rgb(var(--accent-rgb) / .60);
  --accent-fill: rgb(var(--accent-rgb) / .10);
  --accent-fill-2: rgb(var(--accent-rgb) / .12);
  --accent-glow: rgb(var(--accent-rgb) / .07);
  --backdrop:    rgb(var(--deep-rgb) / .74);   /* затемнение под модальным окном */

  /* Значок поверх цветной плашки календаря (трубка у дистанционной встречи). Картинка
     работает МАСКОЙ — задаёт форму, а цвет берётся отсюда: на тусклой плашке значок
     светлее фона, на яркой — темнее. Днём тона меняются местами. */
  --icon-phone: url("/static/img/phone.png");
  --slot-icon-dim:    rgb(255 255 255 / .55);
  --slot-icon-bright: rgb(var(--deep-rgb) / .55);

  /* Шеврон выпадающего списка (форма — как в макете). Цвет зашит в SVG, поэтому
     держим тут: в дневной теме переопределяется на тёмный. */
  --chevron: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 10 6'><path d='M1 1 L5 5 L9 1' fill='none' stroke='%238fa7b5' stroke-width='1.5'/></svg>");

  /* ── Шрифты ──
     ⚠ Имя `--font-mono` историческое: раньше текст был обязан быть моноширинным.
     Сейчас там Inter (пропорциональный) — читать переменную как «шрифт ТЕКСТА».
     Цифры в колонках держит `font-variant-numeric: tabular-nums` у `.data-table .num`,
     а не ширина глифов, поэтому суммы по-прежнему стоят столбиком. */
  --font-body: 'Commissioner', system-ui, -apple-system, sans-serif;
  --font-mono: 'Inter', ui-monospace, 'Cascadia Code', monospace;
  /* Разрядка легенд (подписи цветных квадратов под графиками и пирогами) — мягче
     общего капса: строк много и они рядом, .1em сливалась в забор (решение владельца). */
  --ls-legend: .05em;

  /* ── Шкала кегля ── */
  --fs-kpi:     46px;   /* крупная цифра KPI (шрифт заголовков, 500) */
  --fs-h1:      22px;   /* заголовок страницы (шрифт заголовков, 400) */
  --fs-h2:      15px;   /* заголовок панели (шрифт заголовков, 400) */
  --fs-body:    15px;   /* основной текст */
  --fs-data:    13px;   /* данные (mono) */
  --fs-label:   11px;   /* метка/подпись */

  /* ── Разрядка (letter-spacing): три ступени, вводимое — без разрядки ── */
  --ls-wide:   .15em;   /* заголовок страницы */
  --ls-mid:    .05em;   /* всё остальное капсом: ярлыки, заголовки блоков и таблиц,
                           меню, кнопки, чипы, вкладки, телеметрия */
  /* Усиленная разрядка (решение владельца): прежняя .1em оставлена ровно в двух местах —
     на странице входа (там надписей мало и они «парадные») и у кнопок Профиль/Выход
     в шапке. Держим отдельным токеном, а не числом по месту: иначе при следующей правке
     разрядки эти два места разъедутся с остальным порталом молча. */
  --ls-strong: .1em;

  /* ── Шкала отступов ── */
  --space-1:  4px;
  --space-2:  8px;
  --space-4:  14px;
  --space-5:  16px;
  --space-6:  18px;
  --space-7:  20px;
  --space-8:  24px;
  --space-9:  28px;
  --space-10: 40px;
  --panel-py: 22px;     /* вертикальный отступ панели */
  --panel-px: 24px;     /* горизонтальный отступ панели */
  --gap:      20px;     /* шаг сетки страницы */
  --edge:     0px;      /* 0 = контент и линии-разделители заподлицо с краями колонки */

  /* ── Форма ── */
  --ctl-h:   34px;      /* высота органов управления: чип = кнопка = поле (8px отступы +
                           строка 16px + рамка). Обычные поля выходят на неё сами,
                           явно нужна только там, где браузер рисует нутро сам
                           (date-инпут) или высота задана числом (.hbtn) */
  --row-h:   44px;      /* высота строки данных — ОДНА для всех таблиц портала */
  --cell-px: 16px;      /* горизонтальный отступ внутри ячейки таблицы */
  --edge-r:  36px;      /* отступ ПРАВОГО края таблицы: числа выровнены вправо и без
                           запаса упираются в границу карты (слева текст, там хватает
                           --panel-px, и левый край совпадает с заголовком панели) */
  --chevron-gap: 12px;  /* отступ шеврона выпадашки от правой границы поля. Один на все
                           списки портала: было 6px у своей выпадашки и 11px у нативного
                           select — значок липнул к рамке и стоял вразнобой. Соразмерен
                           вертикальному отступу поля (12px), поэтому воздух вокруг ровный */
  --notch:   18px;      /* срез правого верхнего угла панели контента */
  --chamfer: 8px;       /* фаска кнопки/вкладки */
  --bracket: 12px;      /* циан-уголок KPI-карточки */
  --scrollbar: 14px;    /* толщина ползунка прокрутки (страница + таблицы) */
  --maxw:    1440px;    /* макс. ширина контента */

  font-synthesis: none;
  color-scheme: dark;
}

/* ══════════════════════════════════════════════════════════════════════════
   ДНЕВНАЯ ТЕМА. Переопределяем ТЕ ЖЕ переменные — ни один компонент не трогаем.
   Включается атрибутом data-theme="day" на <html> (кнопка в шапке; выбор личный,
   хранится в браузере). Палитра — по макету «Меридиан светлый» в духе дизайн-
   системы госуслуг (guides.gosuslugi.ru): фон не белый, а светлый «лист» #F2F5F9,
   акцент — плотный синий #0D4CD3 (циан #5fd8e8 на светлом не читается), статусы
   насыщенные. Линии — от тёмно-синего чернильного #0B1F33, прозрачностями.
   ══════════════════════════════════════════════════════════════════════════ */
[data-theme="day"] {
  --accent-rgb:   13 76 211;    /* синий госуслуг вместо циана */
  --accent2-rgb: 224 124 0;     /* оранжевый — внимание/«опасное» */
  --amber-rgb:   199 119 0;
  --danger-rgb:  238 63 88;
  --green-rgb:    35 163 83;
  --line-rgb:     11 31 51;     /* линии — чернильный тон текста, тёмные на светлом */
  --surface-rgb: 255 255 255;   /* панели белые */
  --surface2-rgb: 248 250 252;
  --deep-rgb:    230 237 243;   /* поля ввода и трек ползунка — чуть утоплены */
  /* Категорийный ряд плотнее: светлые тона на белой панели не различаются.
     Первый — синий (= акцент, как циан ночью), поэтому шестой здесь БИРЮЗА:
     второй синий рядом с первым было бы не различить (зеркально ночному ряду). */
  --cat-1-rgb:    13 76 211;
  --cat-2-rgb:   199 119 0;
  --cat-3-rgb:    35 163 83;
  --cat-4-rgb:    90 120 150;
  --cat-5-rgb:    98 80 168;
  --cat-6-rgb:    13 130 150;

  --bg:          #f2f5f9;
  --panel-solid: #fcfdfe;       /* липкие колонки: под цвет панели, непрозрачно */
  --accent-bright: #1d5deb;     /* ховер по акценту — светлее, как в макете */
  /* Плотные линии таблиц (см. ночную тему) — тот же тон, что давало смешение над листом. */
  --row-line:    #dfe2e6;
  --head-line:   #ccd1d5;
  --month-col:   118px;

  --text:  #0b1f33;
  --text-2:#5a7896;
  --muted: #7b96ad;
  --faint: #aec2d2;

  --dds-income-text:  #1b7a42;
  --dds-expense-text: #a82b3d;
  --dds-credit-text:  #8a5b00;
  --dds-profit-text:  #0d3f9e;

  --chevron: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 10 6'><path d='M1 1 L5 5 L9 1' fill='none' stroke='%235a7896' stroke-width='1.5'/></svg>");

  /* Днём тусклая плашка светлая, а яркая — насыщенная: значок-трубка меняется местами. */
  --slot-icon-dim:    rgb(11 31 51 / .45);
  --slot-icon-bright: rgb(255 255 255 / .8);

  /* Затемнение под модальным окном: днём --deep-rgb светлый, поэтому берём
     тёмный тон и слабее — иначе «завеса» не читается как завеса. */
  --backdrop: rgb(11 31 51 / .42);

  color-scheme: light;
}

* { box-sizing: border-box; }

/* Атрибут hidden должен прятать ВСЁ. Браузер задаёт ему display: none самым слабым
   правилом, и любой наш класс с display (напр. .btn — inline-flex) его перебивал:
   галочка «сохранить» в реестрах и кнопки в окнах оставались на виду, хотя код их
   прятал. Поэтому одно правило на весь портал, а не [hidden] у каждого компонента. */
[hidden] { display: none !important; }

/* База фона — на html; сам фон (свечение, сетка, луч) рисует слой .bg-fx ниже.
   Поэтому body прозрачный: иначе он перекрыл бы слой. */
html { margin: 0; padding: 0; background-color: var(--bg); }

body {
  margin: 0; padding: 0; background: transparent;
  font-family: var(--font-mono);
  font-size: var(--fs-body);
  line-height: 1.5;
  color: var(--text);
  min-height: 100vh;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}

/* ── Живой фон ──────────────────────────────────────────────────────────────
   Два слоя под содержимым: статичное циан-свечение сверху и техно-сетка, которая
   медленно дрейфует. Движение — через transform: рисует видеокарта, процессор и
   прокрутка не нагружаются. Параметры приходят из настроек (Админка → Стиль)
   переменными --glow-size/--glow-a/--bg-drift; --bg-drift: 0s = сетка не движется.
   z-index: -1 — выше фона страницы, но ниже любого содержимого. */
.bg-fx { position: fixed; inset: 0; z-index: -1; overflow: hidden; pointer-events: none; }

.bg-fx::before {                                   /* свечение сверху — статичное */
  content: ''; position: absolute; inset: 0;
  background: radial-gradient(var(--glow-size, 1200px 500px) at 50% -10%,
    rgb(var(--accent-rgb) / var(--glow-a, .07)), transparent 70%);
}
.bg-grid {                                         /* техно-сетка 52×52 — дрейф */
  position: absolute; inset: -60px;
  background-image:
    linear-gradient(var(--line-faint) 1px, transparent 1px),
    linear-gradient(90deg, var(--line-faint) 1px, transparent 1px);
  background-size: 52px 52px;
  animation: bg-drift var(--bg-drift, 20s) linear infinite;
}
/* Сдвиг ровно на одну клетку — стык незаметен, движение выглядит бесконечным. */
@keyframes bg-drift { to { transform: translate(52px, 52px); } }
/* Кому системно «поменьше движения» (или укачивает) — фон замирает. */
@media (prefers-reduced-motion: reduce) {
  .bg-grid { animation: none; }
}

a { color: var(--accent); text-decoration: none; }
a:hover { color: var(--accent-bright); }
::selection { background: rgb(var(--accent-rgb) / .3); }

/* ── Типографика ── */
h1, .h1 {
  font-family: var(--font-body); font-weight: 400; font-size: var(--fs-h1);
  letter-spacing: var(--ls-wide); text-transform: uppercase; color: var(--text); margin: 0;
}
h2, .h2 {
  font-family: var(--font-body); font-weight: 400; font-size: var(--fs-h2);
  letter-spacing: var(--ls-mid); text-transform: uppercase; color: var(--text); margin: 0;
}
.eyebrow, .label {
  font-family: var(--font-mono); font-size: var(--fs-label); letter-spacing: var(--ls-mid);
  text-transform: uppercase; color: var(--muted);
}
.mono   { font-family: var(--font-mono); }
.data   { font-family: var(--font-mono); font-size: var(--fs-data); color: var(--text); }
.muted  { color: var(--text-2); }
.faint  { color: var(--muted); }
.accent { color: var(--accent); }
/* Значение «хорошо/плохо» вне таблиц (в таблицах — cell-good/cell-bad, у них
   специфичность под ячейку). Пример: средняя просрочка в карточке сделки.
   Пара .kv-val.* обязательна: у .kv-val свой цвет, и позже по файлу он перебивал бы
   одиночный класс утилиты. */
.good, .kv-val.good { color: rgb(var(--green-rgb)); }
.bad, .kv-val.bad   { color: var(--danger); }

/* ── Верхняя акцент-полоса (общая для всех страниц, включая вход) ── */
.topline {
  height: 2px;
  background: linear-gradient(90deg, transparent, rgb(var(--accent-rgb) / .7) 30%, rgb(var(--accent-rgb) / .7) 70%, transparent);
}

/* ── Живой индикатор «сеть»: РОМБ (просьба владельца — привычный знак статуса),
      пульсирует. Циан по умолчанию, .net-dot-warn — оранжевый. Шапка и вход. ── */
.net-dot { display: inline-block; flex-shrink: 0; width: 7px; height: 7px;
  transform: rotate(45deg); background: var(--accent);
  box-shadow: 0 0 8px rgb(var(--accent-rgb) / .9); animation: ds-pulse 1.8s infinite; }
.net-dot-warn { background: var(--accent-2); box-shadow: 0 0 8px rgb(var(--accent2-rgb) / .9); }
/* В заголовке входа опускаем ромб к оптическому центру капс-текста. */
.auth-title .net-dot { transform: translateY(2px) rotate(45deg); }

/* ── Каркас страницы (для страниц после входа — наполним разметкой на Шаге 2) ── */
.container { width: 100%; max-width: var(--maxw); margin: 0 auto; padding: var(--space-9) var(--edge) var(--space-10); }

.appbar {
  display: flex; align-items: center; gap: 28px;
  max-width: var(--maxw); margin: 0 auto; padding: var(--space-9) var(--edge) var(--panel-py);
  border-bottom: 1px solid var(--line);
}
/* Логотип по ЦЕНТРУ шапки, текстовый блок единым контейнером тоже центрован
   (пересмотр владельца): лого чуть больше текста, но торчит СИММЕТРИЧНО.
   top 2px — компенсация асимметрии line-height текста: над «МЕРИДИАН» пустого
   запаса строки больше, чем под подписью, и центр рамки блока выше центра
   видимых букв (по скриншоту владельца свесы были 6px сверху / 2px снизу). */
.brand { display: flex; align-items: center; gap: var(--space-4); }
.brand-logo { display: block; flex-shrink: 0; height: 48px; width: auto;
  position: relative; top: 2px; }
.brand-text { display: flex; flex-direction: column; }
/* Капс — часть логотипа, поэтому он в стиле, а не в тексте настройки: владелец пишет
   название как удобно («Меридиан»), в шапке оно всё равно встанет как «МЕРИДИАН». */
.brand-name { font-family: var(--font-body); font-weight: 400; font-size: 20px; letter-spacing: .32em;
  text-transform: uppercase; color: var(--text); }
/* letter-spacing персональный (.19em, просьба владельца) — не общий --ls-mid. */
.brand-sub  { font-family: var(--font-mono); font-size: var(--fs-label); letter-spacing: .19em;
  text-transform: uppercase; color: var(--muted); }

.appbar-right { margin-left: auto; display: flex; align-items: center; gap: 24px; font-family: var(--font-mono); font-size: var(--fs-label); flex-wrap: wrap; justify-content: flex-end; }
/* tabular-nums: цифры одной ширины независимо от выбранного шрифта (в Настройки → Стиль
   можно поставить пропорциональный) — тикающие часы не меняют ширину и не дёргают дату
   слева. У шрифта без табличных цифр правило просто ничего не меняет. */
.tele { color: var(--muted); letter-spacing: var(--ls-mid); white-space: nowrap;
  font-variant-numeric: tabular-nums; }
.tele-time { color: var(--accent); letter-spacing: var(--ls-mid); }
.net { display: inline-flex; align-items: center; gap: 8px; color: var(--text-2); letter-spacing: var(--ls-mid); }
/* Кнопки шапки (профиль/выход/войти) — размер и стиль как в макете */
.hbtn {
  display: inline-flex; align-items: center; gap: 8px; height: var(--ctl-h); padding: 0 14px;
  font-family: var(--font-body); font-size: var(--fs-label); letter-spacing: var(--ls-mid); text-transform: uppercase;
  white-space: nowrap; background: transparent; color: var(--text-2);
  border: 1px solid rgb(var(--line-rgb) / .25); cursor: pointer;
  transition: border-color .15s, color .15s;
}
/* Профиль и Выход — усиленная разрядка (решение владельца): это две «парадные» кнопки
   шапки, они стоят особняком от служебных (CRM, «← К списку» и прочих на той же .hbtn). */
.hbtn-profile, .hbtn-logout { letter-spacing: var(--ls-strong); }
.hbtn:hover { border-color: var(--accent-line); color: var(--accent); }
.hbtn-logout:hover { border-color: rgb(var(--accent2-rgb) / .7); color: var(--accent-2); }
/* Переключатель темы: солнце — без рамки и подписи. Ночью круг пустой и циановый,
   днём залитый и оранжевый. Состояние читается из [data-theme] на <html>:
   JS лишь переключает атрибут, вид меняет CSS. */
.theme-toggle { display: inline-flex; align-items: center; padding: 0; background: transparent;
  border: none; cursor: pointer; color: var(--accent); transition: color .15s; }
.theme-toggle:hover { color: var(--accent-bright); }
.theme-toggle svg { display: block; width: 20px; height: 20px; }
.theme-toggle .sun-core { fill: transparent; stroke: currentColor; stroke-width: 1.4; }
[data-theme="day"] .theme-toggle { color: var(--accent-2); }
[data-theme="day"] .theme-toggle .sun-core { fill: currentColor; }
.hbtn .ico { display: block; }
.ico-user { display: flex; flex-direction: column; align-items: center; gap: 2px; width: 14px; }
.ico-user span:first-child { width: 6px; height: 6px; border-radius: 50%; border: 1.5px solid currentColor; }
.ico-user span:last-child { width: 12px; height: 5px; border: 1.5px solid currentColor; border-bottom: none; border-radius: 6px 6px 0 0; }

/* Меню модулей */
.modnav { display: flex; gap: var(--space-8); flex-wrap: wrap; max-width: var(--maxw); margin: 0 auto; padding: var(--space-8) var(--edge) 0; }

/* Заголовок страницы */
.page-head { display: flex; align-items: baseline; gap: 16px; flex-wrap: wrap; margin-bottom: var(--space-7); }
/* Подпись под заголовком — счётчики и пояснения: это ДАННЫЕ, поэтому без разрядки.
   Зазор между датой и счётчиками — тот же, что между заголовком и датой (16px). */
.page-sub { font-family: var(--font-mono); font-size: var(--fs-label); letter-spacing: normal;
  color: var(--muted); display: inline-flex; align-items: baseline; gap: 16px; }
/* Дата/диапазон в подзаголовке — кеглем и цветом ЗАГОЛОВКА h2 (просьба владельца),
   без разрядки. */
.page-sub-date { font-family: var(--font-body); font-size: var(--fs-h2);
  letter-spacing: normal; color: var(--text); }
.role-group { font-family: var(--font-mono); font-size: var(--fs-label); letter-spacing: var(--ls-mid); text-transform: uppercase; color: var(--accent); margin: var(--space-6) 0 var(--space-4); padding-bottom: var(--space-2); border-bottom: 1px solid var(--line); }
.page-actions { margin-left: auto; display: flex; gap: 12px; }
.edit-actions { display: flex; gap: 12px; margin-top: var(--space-8); }
.edit-actions.to-right { justify-content: flex-end; }   /* главная кнопка у правого края */

/* Сетка карточек */
.cards { display: grid; grid-template-columns: repeat(auto-fit, minmax(240px, 1fr)); gap: var(--gap); }

/* Вертикальная стопка панелей (карточка сделки) */
.stack { display: flex; flex-direction: column; gap: var(--gap); }
/* Профиль: четыре панели четырьмя колонками (решение владельца); грид растягивает каждую
   под самую высокую в ряду — нижние края сходятся сами. */
.profile-cols { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: var(--gap); }
/* Таблицы профиля (права, зарплата): две колонки БЕЗ фона — только тонкие линии строк. */
.profile-table { width: 100%; border-collapse: separate; border-spacing: 0; }
.profile-table td { padding: 9px 0; border-bottom: 1px solid var(--line-soft);
  background: none; vertical-align: middle; }
.profile-table tr:last-child td { border-bottom: none; }
.profile-table .pt-label { font-family: var(--font-body); font-size: var(--fs-label);
  letter-spacing: var(--ls-mid); text-transform: uppercase; color: var(--muted);
  padding-right: var(--space-4); }
.profile-table .pt-val { font-family: var(--font-mono); font-size: var(--fs-data);
  color: var(--text); text-align: right; white-space: nowrap; }
/* Итоговые строки зарплатной таблицы — акцентом, как .fot-emph на странице ФОТ. */
.profile-table tr.pt-emph .pt-label, .profile-table tr.pt-emph .pt-val { color: var(--accent); }
.page-back { margin-left: auto; }

/* Пары «метка — значение» (карточка сделки) */
.kv { display: grid; grid-template-columns: repeat(auto-fill, minmax(230px, 1fr)); gap: var(--space-4) var(--space-8); }
.kv-item { display: flex; flex-direction: column; gap: 4px; }
/* Поля карточки: подписи и значения — Roboto Mono (как везде). */
/* Подпись поля — шрифтом заголовков: это ярлык, а не данные (данные — .kv-val, моно). */
.kv-label { font-family: var(--font-body); font-size: var(--fs-label); letter-spacing: var(--ls-mid); text-transform: uppercase; color: var(--muted); }
.kv-val { font-family: var(--font-mono); font-size: var(--fs-data); color: var(--text); word-break: break-word; }

.app-footer {
  display: flex; justify-content: space-between; gap: 16px 40px; flex-wrap: wrap;
  max-width: var(--maxw); margin: var(--space-9) auto 0;
  padding: var(--space-4) var(--edge) var(--space-9);
  border-top: 1px solid var(--line-soft);
  font-family: var(--font-mono); font-size: var(--fs-label); letter-spacing: var(--ls-mid); color: var(--faint);
  /* Табличные цифры: секундомер «СЕССИЯ» не расталкивает соседей при тиканьи. */
  font-variant-numeric: tabular-nums;
}
/* Блоки инфы разведены (просьба владельца; после примерки — вполовину меньше):
   зазор с обеих сторон разделителя «//». */
.foot-side { display: inline-flex; flex-wrap: wrap; gap: 6px 9px; }
.foot-item { white-space: nowrap; }
.foot-item + .foot-item::before { content: "//"; margin-right: 9px; color: var(--faint); }
.app-footer b { font-weight: 400; color: var(--text-2); }
/* Обмен с CRM молчит дольше нескольких интервалов (или последний прогон упал) —
   значение в подвале горит оранжевым: тем же цветом, что и прочие «посмотри сюда». */
.app-footer b.is-stale { color: var(--accent-2); }

/* ── Панели ── */
.panel {
  background: linear-gradient(180deg, var(--panel-top), var(--panel-bot));
  border: 1px solid var(--line);
  padding: var(--panel-py) var(--panel-px);
  /* срез правого верхнего угла */
  clip-path: polygon(0 0, calc(100% - var(--notch)) 0, 100% var(--notch), 100% 100%, 0 100%);
}
/* KPI-карточка: без среза, но с циан-уголками-скобками */
.panel-kpi {
  position: relative;
  background: linear-gradient(180deg, var(--panel-top), var(--panel-bot));
  border: 1px solid var(--line);
  padding: var(--panel-py) var(--panel-px);
}
.panel-kpi::before, .panel-kpi::after { content: ''; position: absolute; width: var(--bracket); height: var(--bracket); }
.panel-kpi::before { top: -1px; left: -1px; border-top: 2px solid var(--accent); border-left: 2px solid var(--accent); }
.panel-kpi::after  { bottom: -1px; right: -1px; border-bottom: 2px solid var(--accent); border-right: 2px solid var(--accent); }

/* ── Модальное окно ──
   Нативный <dialog class="modal">: форма, которой не нужно место на странице (пример:
   ввод операций — открывается кнопкой в шапке). Поверхность как у панели (срез угла),
   но НЕпрозрачная: под ней затемнение, сквозь которое сетка фона не должна просвечивать.
   Открытие/закрытие — js/modal.js по data-атрибутам (Esc — нативно у showModal). */
.modal {
  padding: 0; color: var(--text); border: 1px solid var(--line);
  background: var(--panel-solid) linear-gradient(180deg, var(--panel-top), var(--panel-bot));
  width: min(1240px, calc(100vw - 2 * var(--space-8)));
  max-height: calc(100vh - 2 * var(--space-8));
  clip-path: polygon(0 0, calc(100% - var(--notch)) 0, 100% var(--notch), 100% 100%, 0 100%);
}
.modal[open] { display: flex; flex-direction: column; }   /* шапка · тело · кнопки */
.modal::backdrop { background: var(--backdrop); }
.modal > form { flex: 1 1 auto; display: flex; flex-direction: column; min-height: 0; }
/* Шапка окна — тот же порядок, что у заголовка страницы и панели: название, следом
   приписка (дата встречи) через --space-5, а к правому краю уходит только крестик. */
.modal-head {
  display: flex; align-items: baseline; gap: var(--space-5); flex: none;
  padding: var(--space-6) var(--panel-px);
  border-bottom: 1px solid var(--line);
}
.modal-body { flex: 1 1 auto; min-height: 0; overflow: auto; padding: var(--panel-py) var(--panel-px); }
/* Таблица во всю ширину окна: снизу отступ не нужен — иначе между последней строкой и
   панелью кнопок висит пустая полоса. Сверху он остаётся: подписи колонок не должны
   липнуть к линии шапки. */
.modal-body:has(> .table-wrap) { padding-bottom: 0; }
.modal-actions {
  display: flex; gap: 10px; justify-content: flex-end; flex: none;
  padding: var(--space-5) var(--panel-px);
  border-top: 1px solid var(--line);
}
.modal-actions .mod-left { margin-right: auto; }          /* «+ строки» — к левому краю */
/* Крестик окна — всегда у правого края шапки (margin-left: auto), одинаковый во всех
   окнах портала. При наведении — оранжевым: это «закрыть, ничего не сохранив», то есть
   тот же смысл, что у кнопки «Отмена» (.btn-cancel), и цвет должен совпадать. */
.modal-close {
  flex: none; margin-left: auto; background: none; border: 0; cursor: pointer; padding: 0 0 0 4px;
  font-family: var(--font-mono); font-size: 18px; line-height: 1; color: var(--muted);
}
.modal-close:hover { color: var(--accent-2); }
/* Окно подтверждения (js/modal.js, data-confirm) — узкое, только вопрос и две кнопки. */
.modal-ask { width: min(460px, calc(100vw - 2 * var(--space-8))); }
/* Кнопка согласия в окне подтверждения красная, как все удаляющие, — общий .btn-danger
   (свои правила были у окна, пока красным был только он; теперь это один стиль). */
.ask-text { margin: 0; font-family: var(--font-mono); font-size: var(--fs-body); color: var(--text-2); }

/* Заголовок панели: название, за ним приписка через тот же отступ, что у заголовка
   страницы (--space-5), а код-подпись — у правого края. Раньше стояло
   justify-content: space-between, и приписка между двумя краями оказывалась посередине,
   оторванная от названия. */
.panel-head { display: flex; align-items: baseline; gap: var(--space-5); margin-bottom: var(--space-6); }
.panel-head .panel-code { margin-left: auto; }
.panel-title { font-family: var(--font-body); font-weight: 400; font-size: var(--fs-h2); letter-spacing: var(--ls-mid); text-transform: uppercase; color: var(--text); }
.panel-code  { font-family: var(--font-mono); font-size: var(--fs-label); letter-spacing: var(--ls-mid); color: var(--muted); white-space: nowrap; }
/* Приписка у заголовка панели («активных: 65») — про содержимое, поэтому стоит рядом с
   названием, а не в конце панели фильтров. Тише заголовка, как код справа. */
.panel-sub { font-family: var(--font-mono); font-size: var(--fs-label);
  letter-spacing: var(--ls-mid); text-transform: uppercase; color: var(--muted); white-space: nowrap; }

/* ── Метрика (крупная цифра) ── */
.kpi-label { display: flex; justify-content: space-between; font-family: var(--font-mono); font-size: var(--fs-label); letter-spacing: var(--ls-mid); color: var(--muted); }
/* Крупные цифры KPI — шрифтом ЗАГОЛОВКОВ, как в макете (там 700 / 46px). Моно того
   же веса выглядит вдвое жирнее: у него все знаки одной ширины, и цифра набирает массу.
   Осознанное исключение из правила «данные пишем моно»: цифра одна, в колонку ни с чем не
   выравнивается, а плотность мешает читать.
   ⚠ Вес 500, а не 700 из макета: у плотного гротеска 700 выглядит тяжелее, чем у
   геометрической Jura, которой набран макет; 500 держит «макетную» лёгкость. */
.kpi-value { font-family: var(--font-body); font-weight: 500; font-size: var(--fs-kpi); line-height: 1; letter-spacing: .02em; margin-top: var(--space-4); color: var(--text); }
.kpi-value.accent { color: var(--accent); }
/* Тревожная метрика (минусовый баланс): красным, но только когда беда есть. */
.kpi-value.kpi-bad { color: var(--danger); }
/* Подстрока KPI (разбивка ИП/нал, план дебиторки): тем же голосом, что ярлык
   сверху, — это пояснение к цифре, а не вторая цифра. Края — как у ярлыка. */
.kpi-sub { display: flex; justify-content: space-between; gap: 10px;
  margin-top: var(--space-4); font-family: var(--font-mono); font-size: var(--fs-label);
  letter-spacing: var(--ls-mid); color: var(--muted); }

/* ── Кнопки и вкладки ── */
/* Кнопки носят дизайн ЧИПА (прямоугольник, компактный размер) — в отличие от
   вкладок меню/подменю (.tab), которые сохраняют фаску-«кнопку». */
.btn {
  font-family: var(--font-body); font-size: var(--fs-label); font-weight: 400; letter-spacing: var(--ls-mid);
  text-transform: uppercase; white-space: nowrap;
  display: inline-flex; align-items: center; justify-content: center; gap: 8px;
  /* Строка как у чипа (1.45): кнопка и чип стоят в одних рядах и обязаны быть одного
     роста — см. «единую высоту» у полей ввода. */
  padding: 8px 14px; line-height: 1.45; cursor: pointer;
  background: rgb(var(--surface-rgb) / .8); color: var(--text-2); border: 1px solid rgb(var(--line-rgb) / .22);
  transition: background .15s, border-color .15s, color .15s;
}
.btn:hover { border-color: var(--accent-line); color: var(--accent); }
.btn-primary { background: var(--accent-fill); color: var(--accent); border-color: var(--accent-line); }
.btn-primary:hover { background: rgb(var(--accent-rgb) / .2); color: var(--accent-bright); }
/* Кнопка удаления — КРАСНАЯ в покое (решение владельца): красный = «сотрёт данные»,
   один язык на весь портал — ✕ в строках реестров, «Удалить» в окнах встречи и
   платежа, ✕ планировщика, кнопка согласия в окне подтверждения. */
.btn-danger { color: var(--danger); border-color: rgb(var(--danger-rgb) / .5); }
.btn-danger:hover { color: var(--danger); border-color: var(--danger);
  background: rgb(var(--danger-rgb) / .12); }
/* «Отмена» — оранжевым ПРИ НАВЕДЕНИИ (в покое как обычная кнопка): она ничего не стирает,
   постоянная подсветка тянула бы взгляд зря. Тон тот же, что у несохранённой правки.
   Правило стоит ПОСЛЕ `.btn:hover`: у них равная специфичность, и иначе наведение
   красило бы «Отмену» в циан. */
.btn-cancel:hover { color: var(--accent-2); border-color: var(--accent-2);
  background: rgb(var(--accent2-rgb) / .12); }
/* «Предупреждение» — шаг К удалению, но ещё не оно (решение владельца: «Удалить» в
   тикете лишь открывает подтверждение): в покое обычная, оранжевая при наведении. */
.btn-warn:hover { color: var(--accent-2); border-color: var(--accent-2);
  background: rgb(var(--accent2-rgb) / .12); }
.btn-block { width: 100%; }

/* Вкладка навигации (Шаг 2) */
.tab {
  font-family: var(--font-body); font-size: var(--fs-data); font-weight: 400; letter-spacing: var(--ls-mid);
  text-transform: uppercase; white-space: nowrap;
  padding: 9px 22px; cursor: pointer;
  background: transparent; color: var(--muted); border: 1px solid var(--line);
  clip-path: polygon(var(--chamfer) 0, 100% 0, 100% calc(100% - var(--chamfer)), calc(100% - var(--chamfer)) 100%, 0 100%, 0 var(--chamfer));
  transition: background .15s, border-color .15s, color .15s;
}
.tab:hover { color: var(--text); border-color: var(--accent-line); }
.tab.is-active { background: var(--accent-fill); color: var(--accent); border-color: var(--accent-line); }

/* Чип-фильтр */
/* Чип бывает ссылкой, кнопкой и просто текстом — inline-flex и общая высота строки
   нужны, чтобы все три выглядели одинаково (у <button> иначе высота на 2px меньше). */
.chip {
  display: inline-flex; align-items: center; line-height: 1.45;
  /* Единый рост даже без текста: чип из одного красного квадратика (без счётчика)
     схлопывался ниже соседей — текстовой строки, держащей высоту, в нём нет. */
  min-height: var(--ctl-h); box-sizing: border-box;
  /* Шрифт заголовков, не моно: чипы — это подписи, а не данные. Разрядка та же, что у
     прочих капс-надписей (--ls-mid). */
  font-family: var(--font-body); font-size: var(--fs-label); letter-spacing: var(--ls-mid);
  text-transform: uppercase;
  padding: 8px 14px; cursor: pointer; white-space: nowrap;
  background: rgb(var(--surface-rgb) / .8); color: var(--text-2); border: 1px solid rgb(var(--line-rgb) / .22);
  transition: background .15s, border-color .15s, color .15s;
}
/* Наведение — циан, РОВНО как у кнопки: чип и кнопка выглядят одинаково, и разный
   отклик (белый там, циановый тут) читался как разнобой. Активный чип отличим и так —
   у него циановая заливка, а не только цвет текста. */
.chip:hover { border-color: var(--accent-line); color: var(--accent); }
.chip.is-active { background: var(--accent-fill-2); color: var(--accent); border-color: rgb(var(--accent-rgb) / .65); }
.chip-count { margin-left: 10px; opacity: .55; }

/* ── Выбор колонок таблицы (кнопка-глаз, js/columns.js) ──
   Список висит в обычном потоке страницы (absolute внутри .cols), поэтому едет вместе с
   ней при прокрутке и никаких пересчётов не требует. */
.cols { position: relative; display: inline-flex; }
/* Глаз — такой же чип, как соседние: отступы и высоту не переопределяем, иконку
   подгоняем под строку подписи чипа, чтобы кнопка не была ниже остальных. */
.chip-eye { display: inline-flex; align-items: center; }
/* Размер иконки — в em от кегля чипа: тогда её высота совпадает со строкой подписи
   соседних чипов, и кнопка получается ровно такой же высоты при любом шрифте. */
.chip-eye svg { display: block; height: 1.45em; width: 1.5em; }   /* 1.45em = line-height чипа */
.cols.is-open .chip-eye { background: var(--accent-fill-2); color: var(--accent); border-color: rgb(var(--accent-rgb) / .65); }
.cols-pop {
  display: none; position: absolute; z-index: 40; top: calc(100% + 6px); left: 0; min-width: 210px;
  padding: var(--space-4); background: var(--panel-solid); border: 1px solid var(--accent-line);
  box-shadow: 0 12px 28px rgb(var(--deep-rgb) / .55);
}
.cols.is-open .cols-pop { display: block; }
.cols-list { display: flex; flex-direction: column; gap: var(--space-2); margin-bottom: var(--space-4); }
.cols-item {
  display: flex; align-items: center; gap: 8px; cursor: pointer;
  font-family: var(--font-mono); font-size: var(--fs-data); color: var(--text-2); white-space: nowrap;
}
.cols-item:hover { color: var(--accent); }
/* Вид самой галочки — общий, в блоке «Поля ввода» ниже (здесь он был точечной заплаткой). */
.cols-all { width: 100%; justify-content: center; }
/* «Показать всё» и «Скрыть всё» — с просветом (замечание владельца). */
.cols-all + .cols-all { margin-top: var(--space-2); }

/* Чип «все статусы» встреч — красный квадратик вместо текста (просьба владельца):
   неактивный — бледный, активный — яркий. */
.chip .sq-red { display: inline-block; width: 10px; height: 10px;
  background: rgb(var(--danger-rgb) / .35); vertical-align: middle; }
.chip.is-active .sq-red { background: var(--danger); }
/* Спрятанная колонка — и заголовок, и все ячейки. */
.is-col-off { display: none; }

/* ── Поля ввода ── */
/* Вводимое и выбираемое — БЕЗ разрядки (обычная): логин/пароль, даты, ячейки
   таблиц, выпадашки. Разрядка остаётся у ярлыков, заголовков и кнопок. */
/* ЕДИНАЯ ВЫСОТА ОРГАНОВ УПРАВЛЕНИЯ (решение владельца): поле, кнопка и чип — одного
   роста, 34px = 8px отступы + строка 16px + рамка. Строка задана В ПИКСЕЛЯХ, а не
   множителем: поля бывают с кеглем 13px (данные) и 11px (даты фильтров, .cal-date), и
   относительный line-height давал бы им разные высоты — края в смешанных рядах
   (поле + кнопка рядом) гуляли бы. 16px совпадает со строкой чипа (11px × 1.45).
   Вне стандарта только меню (.tab) и клеточные поля таблиц (.gcell — их высоту задаёт
   строка --row-h). */
input, select, textarea {
  font-family: var(--font-mono); font-size: var(--fs-data); letter-spacing: normal; color: var(--text);
  background: rgb(var(--deep-rgb) / .8); border: 1px solid rgb(var(--line-rgb) / .25);
  padding: 8px 14px; line-height: 16px; outline: none; width: 100%;
}
/* Галочка — не поле ввода: ширина по содержимому, без подложки и рамки. Общее правило
   выше растягивало её на всю ширину клетки (299px в окне «Новое дело» — подпись «да»
   уезжала к правому краю), и это уже приходилось лечить точечно у списка колонок.
   Лечим один раз здесь, чтобы галочка везде выглядела одинаково. */
input[type="checkbox"], input[type="radio"] {
  width: 14px; height: 14px; flex: none; padding: 0; background: none; border: 0;
  accent-color: var(--accent); cursor: pointer;
}
/* Флажки — свои, а не системные (просьба владельца): тонкая рамка портала, отметка —
   акцентный квадрат внутри. accent-color выше остаётся страховкой для radio. */
input[type="checkbox"] {
  appearance: none; -webkit-appearance: none;
  border: 1px solid var(--line); background: transparent;
  display: inline-grid; place-content: center;
}
input[type="checkbox"]:hover { border-color: var(--accent-line); }
input[type="checkbox"]:checked { border-color: var(--accent); }
input[type="checkbox"]:checked::before {
  content: ""; width: 8px; height: 8px; background: var(--accent);
}
/* Нативного выбора файла на портале не видно нигде: и во вложениях тикетов, и у записи
   встречи поле спрятано (`hidden`), а жмётся своей кнопкой через `label for`. Поэтому
   стилей для `input[type=file]` здесь нет — красить нечего. */
/* Многострочному комментарию тесная строка поля не подходит — возвращаем обычную. */
textarea { line-height: 1.45; }
input::placeholder, textarea::placeholder { color: var(--muted); }
input:focus, select:focus, textarea:focus { border-color: var(--accent-line); }

/* ── Выпадающий список ──
   Системную стрелку убираем, рисуем свой шеврон (форма из макета). Сам ВЫПАДАЮЩИЙ
   СПИСОК рисует браузер/ОС — CSS его не оформляет; задаём хотя бы цвета строк
   (Windows их подхватывает) и держим их в теме. */
select {
  appearance: none; -webkit-appearance: none; cursor: pointer;
  padding-right: 30px;
  background-image: var(--chevron);
  background-repeat: no-repeat;
  background-position: right var(--chevron-gap) center;
  background-size: 10px 6px;
}
select:hover { border-color: var(--accent-line); }
select::-ms-expand { display: none; }
option { background: var(--panel-solid); color: var(--text); }
option:checked { color: var(--accent); }

/* Поле даты: системный календарь оформить нельзя (его рисует браузер), но иконку
   и поведение приводим к стилю — светлеет при наведении; светлый/тёмный вид
   календаря переключается вместе с темой через color-scheme. */
/* Нутро date-инпута браузер верстает сам и line-height не слушает — без явной высоты
   дата выходила на 2px выше соседних полей. Клеточные (.gcell) не трогаем: их высоту
   держит строка таблицы (--row-h). */
input[type="date"]:not(.gcell) { height: var(--ctl-h); }
input[type="date"]::-webkit-calendar-picker-indicator { cursor: pointer; opacity: .5; }
input[type="date"]:hover::-webkit-calendar-picker-indicator,
input[type="date"]:focus::-webkit-calendar-picker-indicator { opacity: 1; }
.field { display: flex; flex-direction: column; gap: var(--space-2); }
.field-hint { font-family: var(--font-mono); font-size: var(--fs-label); letter-spacing: normal;
  color: var(--faint); }
.field > label, .field-label {
  /* Шрифт заголовков — как у .kv-label: подпись поля это ярлык, а не данные. */
  font-family: var(--font-body); font-size: var(--fs-label); letter-spacing: var(--ls-mid);
  text-transform: uppercase; color: var(--muted);
}

/* ── Статус (ромб-маркер + подпись) ── */
.status { display: inline-flex; align-items: center; gap: 10px; font-family: var(--font-mono); font-size: var(--fs-label); letter-spacing: var(--ls-mid); text-transform: uppercase; color: var(--text-2); }
.status::before { content: ''; width: 6px; height: 6px; transform: rotate(45deg); background: currentColor; flex-shrink: 0; }
.status-ok, .status-delivered { color: var(--accent); }
.status-wait, .status-transit { color: var(--amber); }
.status-neutral, .status-processing { color: var(--text-2); }
.status-bad, .status-returned { color: var(--danger); }

/* ── Таблица данных ── */
/* ⚠ `separate`, а НЕ `collapse` — принципиально для таблиц с липкой колонкой.
   При `collapse` границу между строками рисует САМА ТАБЛИЦА, а не ячейка: линия лежит
   на границе двух клеток, «половинками» в каждой. Липкая колонка позиционирована
   (sticky + z-index) и красит свой непрозрачный фон ПОВЕРХ таблицы — её половина линии
   пропадала, и разделители в первой колонке выглядели то смещёнными на пиксель, то
   исчезнувшими, то другой толщины. При `separate` каждая клетка рисует свою границу
   сама и поверх собственного фона: липкая ведёт себя ровно как обычная.
   `border-spacing: 0` — клетки по-прежнему впритык; двойных линий нет, потому что
   границу задаём только с ОДНОЙ стороны (снизу у строк, справа у колонок-разделителей). */
.data-table { width: 100%; border-collapse: separate; border-spacing: 0; }
.data-table thead th {
  font-family: var(--font-body); font-size: var(--fs-label); font-weight: 500; letter-spacing: var(--ls-mid);
  text-transform: uppercase; color: var(--muted); text-align: left; white-space: nowrap;
  /* Сверху отступ даёт сама карта (--panel-py), поэтому снизу задаём сопоставимый —
     иначе подпись прижимается к линии и выглядит съехавшей вниз. */
  padding: 0 var(--cell-px) var(--space-7);
  border-bottom: 1px solid var(--head-line);
}
/* Высота строки — одна на весь портал (--row-h): вертикальные отступы не задаём, вместо
   них фиксируем высоту клетки, а содержимое центрируется само (vertical-align: middle).
   Для клеток, которым нужно больше (перенос комментария, столбик флажков), height —
   минимум: содержимое раздвинет строку. Спейсеры (.dds-spacer, .fot-sep) задают свою
   высоту ниже, и их правила сильнее. */
.data-table tbody td { font-family: var(--font-mono); font-size: var(--fs-data); color: var(--text);
  height: var(--row-h); padding: 0 var(--cell-px); border-bottom: 1px solid var(--row-line); }
/* Светло-циановая подсветка строки (как в макете). Кладём СЛОЕМ (background-image),
   а не шорткатом: иначе сбрасывается непрозрачная подложка липких колонок и сквозь
   них просвечивает прокручиваемое содержимое. */
.data-table tbody tr:hover td { background-image: linear-gradient(rgb(var(--accent-rgb) / .06), rgb(var(--accent-rgb) / .06)); }
.data-table .num { text-align: right; font-variant-numeric: tabular-nums; }
/* Сортировка по клику на заголовок. Указатель — только у таблиц с сортировкой
   (класс вешает их скрипт), у активной колонки — циан + треугольник направления. */
.data-table.is-sortable thead th { cursor: pointer; user-select: none; }
.data-table thead th.sorted { color: var(--accent); }
.data-table thead th.sorted::after { content: '▲'; margin-left: 6px; font-size: 10px; vertical-align: 1px; }
.data-table thead th.sorted.sort-desc::after { content: '▼'; }

/* Широкая таблица (много колонок, напр. Дебиторка): горизонтальная прокрутка,
   ячейки не переносятся, кроме колонки комментария. */
.table-wrap { overflow-x: auto; }

/* ── Ползунок прокрутки в стиле «Меридиан» (вместо системного) ──
   Один стиль на всё: вертикальный ползунок страницы (html) + горизонтальные у
   таблиц и липкого дубля. Firefox — scrollbar-width/color; Chrome/Edge —
   ::-webkit-scrollbar. Углы острые, как во всей системе. */
html, .table-wrap, .hscroll-proxy {
  scrollbar-width: auto; scrollbar-color: rgb(var(--accent-rgb) / .45) rgb(var(--deep-rgb) / .6);
}
html::-webkit-scrollbar, .table-wrap::-webkit-scrollbar, .hscroll-proxy::-webkit-scrollbar {
  width: var(--scrollbar); height: var(--scrollbar); }
html::-webkit-scrollbar-track, .table-wrap::-webkit-scrollbar-track, .hscroll-proxy::-webkit-scrollbar-track {
  background: rgb(var(--deep-rgb) / .6); }
html::-webkit-scrollbar-thumb, .table-wrap::-webkit-scrollbar-thumb, .hscroll-proxy::-webkit-scrollbar-thumb {
  background: rgb(var(--accent-rgb) / .35); border: 1px solid var(--accent-line); min-height: 40px; }
html::-webkit-scrollbar-thumb:hover, .table-wrap::-webkit-scrollbar-thumb:hover, .hscroll-proxy::-webkit-scrollbar-thumb:hover {
  background: rgb(var(--accent-rgb) / .55); }
html::-webkit-scrollbar-corner { background: rgb(var(--deep-rgb) / .6); }

/* Липкий дубль-ползунок (создаётся js/tables.js): когда таблица шире экрана, её
   собственный ползунок уезжает вниз за пределы окна — эта копия прилипает к низу
   окна и синхронизирована с таблицей. Прячется, когда виден настоящий. */
.hscroll-proxy {
  position: sticky; bottom: 0; z-index: 4; overflow-x: auto; overflow-y: hidden;
  margin-left: calc(-1 * var(--panel-px)); margin-right: calc(-1 * var(--panel-px));
  width: calc(100% + 2 * var(--panel-px));
  background: rgb(var(--deep-rgb) / .85); border-top: 1px solid var(--line-soft);
}
.hscroll-proxy > i { display: block; height: 1px; }
.hscroll-proxy.is-hidden { display: none; }
.data-table.wide tbody td, .data-table.wide thead th { white-space: nowrap; }
/* Точечный запрет переноса для короткой ячейки в обычной таблице («Б24 ↗» в Сделках). */
.data-table td.cell-nowrap { white-space: nowrap; }
.data-table.wide td.cell-comment { white-space: normal; min-width: 200px; max-width: 340px; }
.cell-overdue { color: var(--danger); }
/* Дата = сегодня — циан (просьба владельца): день платежа/расхода настал. */
.cell-today { color: var(--accent); }
.cell-muted { color: var(--muted); }
/* Колонки-даты (Дебиторка: Ручная/Плановая/Итоговая): табличные цифры — даты стоят
   столбиком, как суммы в .num (настоящего моно в наборе шрифтов нет, см. --font-mono). */
.data-table td.cell-date { font-variant-numeric: tabular-nums; }

/* Таблица во всю ширину карты (как в макете «Заказы»): строки, границы и ховер
   доходят до краёв панели, горизонтальный отступ уносим в крайние ячейки.
   Работает и для таблиц внутри <form> (гриды ввода, редакторы настроек). */
.panel > .data-table, .panel .table-wrap,
.modal-body > .data-table, .modal-body .table-wrap {
  margin-left: calc(-1 * var(--panel-px)); margin-right: calc(-1 * var(--panel-px));
  width: calc(100% + 2 * var(--panel-px));
}
/* Если таблица прокручивается вбок и стоит последней в карте, её ползунок и есть
   «подвал»: убираем нижний отступ карты, чтобы под таблицей не оставалось пустой
   полосы выше самого ползунка. Класс вешает tables.js. */
.table-wrap.is-scrollable:last-child { margin-bottom: calc(-1 * var(--panel-py)); }
/* В модальном окне низ рисует полоса кнопок — «съедать» отступ не нужно. */
.modal-body .table-wrap.is-scrollable:last-child { margin-bottom: 0; }

.panel .data-table th:first-child, .panel .data-table td:first-child,
.modal-body .data-table th:first-child, .modal-body .data-table td:first-child { padding-left: var(--panel-px); }
.panel .data-table th:last-child,  .panel .data-table td:last-child,
.modal-body .data-table th:last-child, .modal-body .data-table td:last-child { padding-right: var(--edge-r); }
/* …кроме колонки действий: --edge-r рассчитан на числа у правого края, а тут стоят кнопки,
   и запас в 36px делал отступ справа вдвое шире, чем слева от кнопок. Берём отступ ячейки —
   воздух вокруг блока кнопок получается симметричным. */
:is(.panel, .modal-body) .data-table th.col-actions,
:is(.panel, .modal-body) .data-table td.col-actions { padding-right: var(--cell-px); }

/* Инлайн-правка в строке (Дебиторка, шаг ввода): компактные поля + мини-кнопка.
   Поле занимает клетку по высоте — строка остаётся ровно --row-h, как в остальных
   таблицах, и поле не «плавает» внутри с зазором. */
/* Колонки, которые правятся по клику, не должны быть уже своего поля: пока в них стоят
   прочерки, ширина заморозится по «—» и обрежет «дд.мм.гггг» в открытой строке.
   Помечаем заголовок классом: col-date — под дату, col-mid — под короткий текст или
   сумму (номер дела, «Списано ₽»), col-wide — под длинный текст. */
/* 152px: дата-поле с иконкой календаря шире текста «дд.мм.гггг» — при 132px открытая
   строка обрезала введённую дату (владелец поймал на «Обещанной» в Дебиторке). */
.data-table th.col-date { min-width: 152px; }
/* col-amt — денежная колонка с полем в открытой строке: 6 знаков с разрядкой + паддинги
   поля (замер: полю нужно ~100px, иначе «20 000» режется в редакторе). */
.data-table th.col-amt { min-width: 104px; }
/* Узкая числовая колонка с редактором (№ платежа): без запаса поле после заморозки
   ширин ужималось до считаных пикселей, и цифра не отрисовывалась вовсе. */
.data-table th.col-num { min-width: 76px; }
.data-table th.col-mid  { min-width: 160px; }
.data-table th.col-wide { min-width: 220px; }
.inp-cell { padding: 0; height: 100%; }
.inp-date { width: 148px; }
.inp-comment { min-width: 220px; }
/* Кнопка-значок в строке (✕/✓/↺): высота общая (8px), по бокам компактнее — она
   почти квадратная, и 14px делали бы её широкой. */
.btn-mini { padding: 8px 12px; font-size: var(--fs-label); gap: 0; }

/* Панель фильтров (Дебиторка): чипы статуса/обещания + выпадашка МОП + счётчик. */
.filterbar { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; margin-bottom: var(--space-6); }
.filter-label { font-family: var(--font-mono); font-size: var(--fs-label); letter-spacing: var(--ls-mid);
  text-transform: uppercase; color: var(--muted); }
.filter-select { width: auto; min-width: 150px; padding: 8px 26px 8px 14px; }  /* справа — место под шеврон */
.filter-count { margin-left: auto; font-family: var(--font-mono); font-size: var(--fs-label);
  letter-spacing: var(--ls-mid); text-transform: uppercase; color: var(--text-2); white-space: nowrap; }

/* Под-навигация внутри модуля (напр. воронки КЦ/ОП/МАР). */
.subnav { display: flex; gap: 8px; flex-wrap: wrap; margin-bottom: var(--space-6); }
.funnel-entity { margin-left: auto; }

/* Отчётные таблицы (месяцы по столбцам) НЕ растягиваются на всю ширину: колонки
   идут по содержимому и просто добавляются справа по мере роста периода. Ширину
   колонок-месяцев выравнивает tables.js — по самой широкой из них. */
/* Помесячные таблицы РАСТУТ ВПРАВО, а не растягиваются (решение владельца): месяц —
   это столбец постоянной ширины, и при коротком периоде таблица просто короче, а не
   расползается на всю карту. Ровность колонок обеспечивает общая минимальная ширина
   столбца месяца — без неё соседние месяцы мерились по своему содержимому («1 554 855»
   против «0») и таблица выглядела рваной. */
.funnel, .dds, .fot-table { width: auto; }
.funnel td:not(.stick), .funnel th:not(.stick),
.dds:not(.summary-table) td:not(:first-child),
.dds:not(.summary-table) th:not(:first-child) { min-width: var(--month-col); }
.funnel .stick-1 { width: var(--c1); }
.funnel .stick-2 { width: 118px; }
/* Сводная — исключение: месяцев в ней ВСЕГДА двенадцать, поэтому она занимает карту
   целиком и БЕЗ ползунка (просьба владельца — не оставлять пустоту справа, но и не
   заводить прокрутку). Минимальная ширина столбца месяца ей поэтому не задаётся:
   двенадцать по 118px в карту не влезают. */
.summary-table { min-width: 100%; }

/* Воронка: месяцы по столбцам, липкие «Метрика» и «ИТОГО» слева. */
.funnel td, .funnel th { white-space: nowrap; }
.funnel tbody td:first-child { text-align: left; }
/* Липкая колонка: непрозрачная подложка (background-COLOR — поверх неё ложатся
   слои-тоны: ховер, цветные блоки) + z-index выше обычных ячеек. */
.funnel .stick { position: sticky; background-color: var(--panel-solid); z-index: 1; }
.funnel thead .stick { z-index: 3; }
/* Ширина 1-й колонки = контент 188px + отступы (слева panel-px, справа cell-px):
   на неё завязан left у stick-2, поэтому обе величины держим одной переменной. */
.funnel { --c1: calc(188px + var(--panel-px) + var(--cell-px)); }
.funnel .stick-1 { left: 0; min-width: var(--c1); text-align: left; }
.funnel .stick-2 { left: var(--c1); min-width: 118px; border-right: 1px solid var(--line); color: var(--accent); }
.funnel thead .stick-2 { color: var(--accent); }
/* Граница колонки ИТОГО — до самого верха карты: обёртка поднимает таблицу под верхний
   отступ панели, а отступ возвращается ВНУТРЬ шапки — граница th тянется вместе с ней. */
.funnel-wrap { margin-top: calc(-1 * var(--panel-py)); }
.funnel thead th { padding-top: var(--panel-py); }
/* Разделитель смысловых групп метрик — ВИДИМАЯ строка-полоска, а не просто просвет:
   в воронке строки не окрашены (в отличие от ДДС), и один зазор читался как «эта строка
   почему-то выше». Полоска утоплена цветом фона страницы и обведена линиями сверху и
   снизу. Фон непрозрачный (background-color, а не заливка с alpha): полоса проходит и по
   липким колонкам, сквозь прозрачную было бы видно прокручиваемое. */
.funnel tr.funnel-sep td { height: 8px; padding: 0; background-color: var(--bg);
  border-top: 1px solid var(--line); border-bottom: 1px solid var(--line); }
.funnel tr.funnel-sep:hover td { background-image: none; }   /* разделитель — не строка данных */

/* ── ДДС: цветные смысловые блоки, липкая колонка статей ── */
/* Строки здесь подкрашены, и линия «как на панели» в заливке тонула. Переопределяем сам
   ТОКЕН на таблице — так же, как это делает тема: правило одно, а работает и на линиях
   строк, и на вертикальных разделителях, и в сводной (у неё тот же класс). */
.dds { --row-line: #33454f; --head-line: #3d525d; }
[data-theme="day"] .dds { --row-line: #ccd4da; --head-line: #b9c4cc; }
/* ВСЯ таблица стоит на одной непрозрачной подложке, а не только липкая первая колонка.
   Причина шва, который был виден по её краю: панель под таблицей — полупрозрачный
   ГРАДИЕНТ, и цветной слой строки ложился на разное — на плоский --panel-solid в первой
   колонке и на градиент (плюс фон страницы) в остальных. Чем ниже строка, тем сильнее
   расходился оттенок. Ровная подложка снимает саму причину; прокрутка под липкой
   колонкой по-прежнему не просвечивает. */
.dds td, .dds th { white-space: nowrap; background-color: var(--panel-solid); }
.dds td:first-child, .dds thead th:first-child { position: sticky; left: 0; z-index: 1; text-align: left;
  min-width: calc(198px + var(--panel-px)); }
/* Вертикальная граница после колонки статей — как в сводной таблице дашборда:
   отделяет ярлыки от чисел. Линии строк под липкой колонкой рисовать отдельно НЕ нужно:
   таблица переведена на `border-collapse: separate`, и каждая клетка (в том числе
   липкая) рисует свою границу сама — см. комментарий у `.data-table`. */
.dds :is(td, th):first-child { border-right: 1px solid var(--head-line); }
.dds tbody tr.dds-block td { font-weight: 700; }
.dds tbody tr.dds-cat td { font-weight: 600; }
.dds tbody tr.dds-item td:first-child { padding-left: calc(var(--panel-px) + 18px); }  /* вложенная статья — с отступом от края карты */
.dds tbody tr.dds-item td { color: var(--text-2); }
/* Нулевую клетку приглушаем ЦВЕТОМ ТЕКСТА, а не прозрачностью. `opacity` на <td> гасила
   вместе с цифрой и цветной слой строки, и её линии: по таблице шли ступеньки выцветших
   клеток (у каждой статьи — своя длина хвоста нулей), а границы в них пропадали. Это и
   было «поехавшими границами» в ДДС. */
.dds .z { color: var(--faint); }
/* dds-spacer — просвет между большими блоками; dds-catsep — УЗКИЙ просвет между
   категориями внутри Расходов (сплошная красная простыня читалась одним блоком). */
/* Просвет — это отсутствие ЦВЕТНОГО слоя, а не дырка в подложке: строка-разделитель
   остаётся непрозрачной, как и вся таблица (иначе в зазор видно прокручиваемые колонки
   под липкой колонкой, а сам зазор отличался бы оттенком от соседних строк). */
.dds tr.dds-spacer td, .dds tr.dds-catsep td {
  background-image: none !important; border: none; height: 10px; padding: 0; }
.dds tr.dds-catsep td { height: 6px; }
/* Цвет блока кладём СЛОЕМ (background-image) поверх подложки ячейки: у липкой
   первой колонки под ним остаётся непрозрачный background-color, поэтому сквозь
   неё не просвечивает прокручиваемое. Отдельные правила для td:first-child больше
   не нужны — слой ложится на все ячейки строки. */
/* Доходы — зелёный */
.dds-income td { background-image: linear-gradient(rgb(var(--green-rgb) / .10), rgb(var(--green-rgb) / .10)); }
.dds-income.dds-block td { background-image: linear-gradient(rgb(var(--green-rgb) / .20), rgb(var(--green-rgb) / .20)); color: var(--dds-income-text); }
/* Расходы — красный */
.dds-expense td { background-image: linear-gradient(rgb(var(--danger-rgb) / .09), rgb(var(--danger-rgb) / .09)); }
.dds-expense.dds-cat td { background-image: linear-gradient(rgb(var(--danger-rgb) / .15), rgb(var(--danger-rgb) / .15)); }
.dds-expense.dds-block td { background-image: linear-gradient(rgb(var(--danger-rgb) / .22), rgb(var(--danger-rgb) / .22)); color: var(--dds-expense-text); }
/* Кредиторка — жёлтый */
.dds-credit td { background-image: linear-gradient(rgb(var(--amber-rgb) / .10), rgb(var(--amber-rgb) / .10)); }
.dds-credit.dds-block td { background-image: linear-gradient(rgb(var(--amber-rgb) / .20), rgb(var(--amber-rgb) / .20)); color: var(--dds-credit-text); }
/* Операц. прибыль — синий/циан */
.dds-profit td { background-image: linear-gradient(rgb(var(--accent-rgb) / .09), rgb(var(--accent-rgb) / .09)); }
.dds-profit.dds-block td { background-image: linear-gradient(rgb(var(--accent-rgb) / .18), rgb(var(--accent-rgb) / .18)); color: var(--dds-profit-text); }
.dds tr.dds-emph td { font-weight: 700; }
.dds td.neg { color: var(--danger); }

/* ── Сводная таблица (дашборд): 12 месяцев БЕЗ ползунка ──
   Первая колонка НЕ липкая и без своей подложки: прокрутки здесь нет, а sticky с
   непрозрачным фоном только ломал вид — фон её шапки отличался от остальной шапки,
   и линии строк поверх solid-подложки читались иначе, чем в прочих колонках.
   Статика + прозрачность = окраска и границы строк одинаковы во всей таблице.
   Ширину колонке показателей меряет её текст (min-width ддс не нужен). */
.summary-table td:first-child, .summary-table thead th:first-child {
  position: static; background-color: transparent; min-width: 0; }
/* Вертикальная граница после колонки показателей — отделяет ярлыки от чисел. */
.summary-table :is(td, th):first-child { border-right: 1px solid var(--head-line); }
/* Смысловые блоки красим ТЕКСТОМ, а не фоном (решение владельца): 12 месяцев × полтора
   десятка строк сплошной заливкой превращались в цветное одеяло, а цвет цифры читается
   не хуже. Палитра та же, что в ДДС, — одна на весь портал. */
.summary-table tbody td { background-image: none !important; }
/* И без непрозрачной подложки клеток: она нужна ТОЛЬКО липким колонкам под прокруткой
   (унаследована от .dds), а здесь прокрутки нет — плоский цвет поверх градиента панели
   читался как заливка данных. Линии плотные, прозрачная клетка им не мешает. */
.summary-table td, .summary-table th { background-color: transparent; }
.summary-table tbody tr.dds-income  td { color: var(--dds-income-text); }
.summary-table tbody tr.dds-expense td { color: var(--dds-expense-text); }
.summary-table tbody tr.dds-credit  td { color: var(--dds-credit-text); }
.summary-table tbody tr.dds-profit  td { color: var(--dds-profit-text); }
/* Пустая клетка и минус говорят сами за себя и цвет блока перебивают. */
.summary-table tbody tr td.z { color: var(--faint); }
.summary-table tbody tr td.neg { color: var(--danger); }
/* Сводная — ВСЯ без жирного (просьба владельца): смысловые блоки различает цвет.
   Селектор с двумя классами (.dds.summary-table) — иначе он слабее правил жирности
   блоков ДДС (.dds tr.dds-block td и родни) и строки «Доходы»/«Расходы»/«Первичка»
   оставались жирными. */
.dds.summary-table tbody tr td { font-weight: 400; }

/* ── Операции: грид ввода в стиле Excel + реестр ── */
/* Отступы ячеек грида и заголовков задаёт общее правило .data-table (0 12px), а поле
   внутри ячейки своих горизонтальных отступов НЕ добавляет — иначе текст в ячейке
   съезжал бы относительно подписи колонки. */
/* Строка реестра под правкой (поля подставил скрипт) — курсор в остальных подсказывает,
   что по клетке можно кликнуть. `content-visibility` тут не применяли намеренно: для
   строк таблицы браузер его игнорирует, лечили саму причину — поля ввода. */
.ops-grid tbody td { cursor: text; }
.ops-grid tbody td:last-child, .ops-grid tbody td a { cursor: default; }
/* Ячейка грида: поле ВСТРОЕНО в клетку — без своей рамки, подчёркивания и зазора
   (иначе на экране рябит: полосы строк + полосы полей). Разделители рисует только
   таблица; активная ячейка подсвечивается циан-рамкой поверх клетки.
   Отступ текста живёт ВНУТРИ поля (padding), а само поле растянуто на всю клетку
   отрицательными полями: так рамка идёт по границам клетки, соседние поля стыкуются
   без зазора, а текст стоит ровно под подписью колонки. */
.gcell { display: block; width: 100%; height: 100%; background: transparent;
  border: 1px solid transparent; padding: 0 var(--cell-px); font-size: var(--fs-data); }
/* Поле в УЗКОЙ колонке (№ платежа): боковые отступы 16px съедали всю ширину, и цифра
   не отрисовывалась вовсе — оставляем по 6px. */
.gcell-tight { padding: 0 6px; }

/* ── Дата-поля: своя иконка календаря + попап «Меридиан» (js/calendar.js) ──
   Родной пикер браузера прячем (это последнее системное окно у дат), свою иконку
   рисуем фоном в правом краю поля; клик по ней открывает наш календарь. */
input[type="date"]::-webkit-calendar-picker-indicator { display: none; }
input[type="date"] {
  background-image: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" fill="none" stroke="%237e93a8" stroke-width="1.2"><rect x="1.5" y="2.5" width="13" height="12" rx="1"/><line x1="1.5" y1="6" x2="14.5" y2="6"/><line x1="5" y1="1" x2="5" y2="4"/><line x1="11" y1="1" x2="11" y2="4"/></svg>');
  background-repeat: no-repeat;
  background-position: right 10px center;
  background-size: 15px 15px;
  padding-right: 32px;
}
/* Навёл на поле — иконка ярче (просьба владельца): тот же рисунок цветом текста. */
input[type="date"]:hover {
  background-image: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" fill="none" stroke="%23c9d8e2" stroke-width="1.2"><rect x="1.5" y="2.5" width="13" height="12" rx="1"/><line x1="1.5" y1="6" x2="14.5" y2="6"/><line x1="5" y1="1" x2="5" y2="4"/><line x1="11" y1="1" x2="11" y2="4"/></svg>');
}
/* Дата в окнах (встреча, повтор): кегль ДАННЫХ, как у соседних полей — 11px там
   выглядели чужими (замечание владельца). В полосе фильтров дата остаётся 11px:
   там она стоит в ряду чипов и меряется их кеглем. */
.modal .cal-date { font-size: var(--fs-data); }
.cal-pop {
  position: fixed; z-index: 90; width: 292px; padding: 14px;
  background: var(--panel-solid, var(--panel-top)); background-color: var(--panel-solid);
  border: 1px solid var(--line); box-shadow: 0 12px 32px rgb(0 0 0 / .45);
}
.cal-head { display: flex; align-items: center; justify-content: space-between; margin-bottom: 10px; }
/* calp-nav, а не cal-nav: имя .cal-nav занято контейнером стрелок на встречах. */
.calp-nav { background: none; border: none; color: var(--muted); font-size: 16px;
  width: 32px; height: 28px; cursor: pointer; }
.calp-nav:hover { color: var(--accent); }
.cal-title { display: flex; gap: 8px; }
.cal-title-btn { background: none; border: none; color: var(--text); cursor: pointer;
  font-family: var(--font-body); font-size: var(--fs-data); letter-spacing: var(--ls-mid);
  text-transform: uppercase; padding: 2px 4px; }
.cal-title-btn:hover { color: var(--accent); }
.cal-grid { display: grid; grid-template-columns: repeat(7, 1fr); gap: 2px; }
.cal-dow { text-align: center; font-family: var(--font-mono); font-size: var(--fs-label);
  color: var(--muted); padding: 4px 0 8px; }
.cal-day { background: none; border: 1px solid transparent; color: var(--text);
  font-family: var(--font-mono); font-size: var(--fs-data); padding: 6px 0;
  text-align: center; cursor: pointer; }
.cal-day:hover { border-color: var(--accent-line); color: var(--accent); }
.cal-day.is-out { color: var(--faint); }
.cal-dow.is-we, .cal-day.is-we { color: var(--danger); }
.cal-day.is-we.is-out { color: rgb(var(--danger-rgb, 224 82 82) / .45); }
.cal-day.is-today { border-color: var(--line); }
.cal-day.is-sel { border-color: var(--accent); color: var(--accent); }
.cal-tiles { display: grid; grid-template-columns: repeat(3, 1fr); gap: 6px; }
/* Прямые углы, не капсулы (замечание владельца): круглая обводка выпадала из стиля. */
.cal-tile { background: none; border: 1px solid transparent; color: var(--text);
  font-family: var(--font-body); font-size: var(--fs-data); padding: 12px 4px;
  text-align: center; cursor: pointer; }
.cal-tile:hover { border-color: var(--accent-line); color: var(--accent); }
.cal-tile.is-sel { border-color: var(--accent); color: var(--accent); }

/* Чип-дата в полосе фильтров (Дата1/Дата2): дата-поле, одетое чипом. Заполненное —
   подсвечено как активный чип; крестик сброса даёт сам календарь (повторный выбор). */
.chip-date { height: var(--ctl-h); padding: 0 10px; padding-right: 30px;
  background: transparent; border: 1px solid var(--line); color: var(--text-2);
  font-family: var(--font-mono); font-size: var(--fs-label); letter-spacing: var(--ls-mid);
  width: 138px; }
.chip-date.is-set { border-color: var(--accent-line); color: var(--accent); }
/* Счётчик в чип-фильтре («Источники 3») и в строках списка — приглушённым. */
.fpop .chip b { font-weight: 400; color: var(--accent); margin-left: 6px; }
.fpop .cols-item b { font-weight: 400; color: var(--muted); margin-left: auto; padding-left: 12px; }
.fpop .cols-item { display: flex; align-items: center; }
/* Растягиваем ТОЛЬКО поля ввода: под `> *` попадали и кнопки строки — блок ✕/✓ уезжал
   из своей колонки за край таблицы. Текст в ячейке (ФИО-ссылка и пр.) живёт по отступам
   самой ячейки, ему вылет не нужен.
   Вылет на 1px БОЛЬШЕ отступа ячейки с каждой стороны: у поля рамка 1px, и без запаса
   его содержимое на 2px уже текста ячейки — в открытой строке дата теряла последнюю
   цифру. Текст при этом стоит ровно под подписью (рамка возвращает этот пиксель),
   а рамка фокуса ложится точно на линии сетки. */
.ops-grid td > :is(.gcell, .combo, .combo-slot, .client-cell) {
  margin-left: calc(-1 * var(--cell-px) - 1px); margin-right: calc(-1 * var(--cell-px) - 1px);
  width: calc(100% + 2 * var(--cell-px) + 2px);
}
/* Крайние колонки прижаты к краям карты (--panel-px), поле тянем на тот же вылет. */
:is(.panel, .modal-body) .ops-grid td:first-child > :is(.gcell, .combo, .combo-slot, .client-cell) {
  margin-left: calc(-1 * var(--panel-px) - 1px);
  width: calc(100% + var(--panel-px) + var(--cell-px) + 2px);
}
:is(.panel, .modal-body) .ops-grid td:first-child .gcell { padding-left: var(--panel-px); }
:is(.panel, .modal-body) .ops-grid td:last-child > :is(.gcell, .combo, .combo-slot, .client-cell) {
  margin-right: calc(-1 * var(--edge-r) - 1px);
  width: calc(100% + var(--edge-r) + var(--cell-px) + 2px);
}
:is(.panel, .modal-body) .ops-grid td:last-child .gcell { padding-right: var(--edge-r); }
/* background-COLOR, не shorthand: shorthand стирал background-image — у дата-полей
   при наведении пропадала иконка календаря (поймал владелец). */
.gcell:focus, .gcell:hover { background-color: rgb(var(--accent-rgb) / .05); }
/* Несохранённая правка — оранжевым (цвет «внимание»), пока не нажата галочка в строке.
   Класс ставит скрипт страницы, сравнивая поле с тем, что пришло с сервера. */
.gcell.is-dirty, .combo.is-dirty .combo-input { color: var(--accent-2); }
.btn-mini.is-dirty { color: var(--accent-2); border-color: rgb(var(--accent2-rgb) / .5); }
.gcell:focus { border-color: var(--accent-line); background-color: rgb(var(--accent-rgb) / .08); outline: none; }
.gcell[readonly] { cursor: pointer; }
/* В таблице ширину колонки задаёт текст строки, а поле появляется только у открытой:
   системная иконка календаря съедала бы эти 20px и обрезала дату. Дата вводится с
   клавиатуры и стрелками — в карточках и формах (не .gcell) иконка остаётся. */
.gcell[type="date"]::-webkit-calendar-picker-indicator { display: none; }
.gcell:disabled, .combo.is-disabled .gcell { color: var(--faint); cursor: not-allowed; background: transparent; }
/* Ячейки-гриды красят фон шорткатом (он гасит шеврон) — возвращаем его компактно. */
select.gcell { background-image: var(--chevron); background-repeat: no-repeat;
  background-position: right 6px center; background-size: 9px 6px; padding-right: 20px; }
select.gcell:focus { background-image: var(--chevron); }

/* ── Выпадающий список «Меридиан» (js/combo.js) ──
   Свой список вместо <datalist>/<select>: тот был белым, с иконками файлов и не давал
   переизбрать значение вторым кликом. Значение уходит в скрытое поле — форма обычная.
   Сам список — .combo-pop: рисуется в конце <body> (или внутри <dialog>) с
   position: fixed, иначе его срезала бы прокрутка таблицы. */
/* Обёртке нужна высота клетки: иначе поле внутри (height: 100 %) считает процент от
   авто-высоты, съезжает в 19px и подсвечивается узкой полоской вместо всей ячейки. */
.combo { position: relative; display: block; height: 100%; }
.combo-slot { height: 100%; }
.combo .combo-input { padding-right: 14px; text-overflow: ellipsis; }
/* В реестре ширину колонки задаёт текст строки, места «про запас» нет: резерв под шеврон
   обрезал бы даже короткое «Расход». Поэтому там шеврон рисуется поверх правого края и
   проявляется, когда ячейка под курсором или в работе. */
#ops-table .combo .combo-input { padding-right: 0; }
#ops-table .combo::after { opacity: 0; transition: opacity .12s; }
#ops-table tr:hover .combo::after, #ops-table .combo:focus-within::after { opacity: 1; }
/* Ширины колонок ОКНА ВВОДА: строки там пустые, и без минимумов таблица сжала бы поле
   до пары букв («Ра…» вместо «Расход»). В реестре минимумы не нужны и вредны: ширину
   колонок задаёт текст строк, а поле появляется только в открытой строке — с минимумами
   таблица дёргалась бы вширь при каждом открытии. */
#ops-add-table .combo { min-width: 100px; }
#ops-add-table .combo[data-combo-role="category"] { min-width: 120px; }
#ops-add-table .combo[data-combo-role="section"] { min-width: 136px; }
#ops-add-table .combo[data-combo-role="client"] { min-width: 164px; }
#ops-add-table input[data-money] { min-width: 108px; }
#ops-add-table input[name="op_comment"] { min-width: 152px; }
/* Клиент в реестре: поле выбора + переход на сделку в одной ячейке (колонка «Привязка»
   убрана — сделка теперь живёт здесь). Стрелка появляется только у привязанных.
   Раскладка — во ВНУТРЕННЕМ блоке: сделай flex саму ячейку, и она перестанет тянуться
   до высоты строки — линия-разделитель в этой колонке пойдёт ступенькой. */
.client-cell { display: flex; align-items: stretch; gap: 4px; height: 100%; }
.client-cell .combo-slot { flex: 1 1 auto; min-width: 0; }
/* ФИО привязанной сделки — ссылка на карточку; поле выбора спрятано до клика по шеврону. */
.client-fio {
  display: flex; align-items: center; flex: 1 1 auto; min-width: 0; padding: 0;
  color: var(--accent); text-decoration: none;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.client-fio:hover { color: var(--accent-bright); text-decoration: underline; }
/* Кнопка «сменить клиента» — тот же шеврон, что открывает выпадашку (--chevron,
   9×6 в 6px от правого края): по виду ячейка не отличается от остальных списков.
   Высоту берём растяжением по ячейке (align-self), а не height: 100 %: кнопка пустая,
   и внутри блока с авто-высотой такой процент даёт ноль — шеврон исчезал. */
.client-edit {
  flex: none; align-self: stretch; width: 21px; min-height: 20px; padding: 0; border: 0;
  cursor: pointer; background: var(--chevron) no-repeat right 6px center / 9px 6px;
}
.client-cell .badge { flex: none; margin-right: 0; }
/* Выпадашка ВНЕ таблицы (панель фильтров, форма): клетки, от которой брать высоту, нет —
   поэтому высота своя, а видимое поле получает обычный вид поля (класс filter-select). */
.combo-filter { height: auto; display: inline-block; }
.combo-filter .combo-input { padding-right: 26px; }
/* То же в форме, но на всю ширину поля (карточка сделки, новая встреча, «Стиль»). */
.combo-field { height: auto; }
.combo-field .combo-input { padding-right: 30px; }
.combo::after {                                  /* шеврон, как у select */
  content: ''; position: absolute; right: var(--chevron-gap); top: 50%; width: 9px; height: 6px;
  transform: translateY(-50%); pointer-events: none;
  background: var(--chevron) no-repeat center / 9px 6px;
}
.combo.is-disabled::after { opacity: .3; }
/* Обязательное поле оставили пустым: браузер такую выпадашку не проверяет (видимое поле
   readonly, значение в скрытом), поэтому подсвечиваем сами — красным, как ошибку. */
.combo.is-invalid .combo-input { border-color: var(--danger); }
.combo-pop {
  position: fixed; z-index: 60; max-height: 280px; overflow-y: auto;
  /* докрутил список до конца — страница за ним НЕ едет (иначе список уезжал от ячейки) */
  overscroll-behavior: contain;
  background: var(--panel-solid); border: 1px solid var(--accent-line);
  box-shadow: 0 12px 28px rgb(var(--deep-rgb) / .55);
}
.combo-opt {
  padding: 8px 12px; font-family: var(--font-mono); font-size: var(--fs-data);
  color: var(--text-2); white-space: nowrap; cursor: pointer;
}
.combo-opt:hover, .combo-opt.is-active { background: var(--accent-fill); color: var(--accent); }
.combo-opt.is-chosen { color: var(--accent); }
/* Вариант виден, но выбрать нельзя (занятый час в календаре): убрать его из списка было
   бы хуже — человек не понял бы, куда делось время. Подсветку под курсором снимаем,
   иначе он выглядел бы нажимаемым. */
.combo-opt.is-off { color: var(--faint); cursor: not-allowed; }
.combo-opt.is-off:hover, .combo-opt.is-off.is-active { background: transparent; color: var(--faint); }
.combo-empty { padding: 8px 12px; font-family: var(--font-mono); font-size: var(--fs-label);
  letter-spacing: var(--ls-mid); text-transform: uppercase; color: var(--faint); }
.grid-actions { display: flex; gap: 10px; margin-top: var(--space-6); }
/* Подкраска строк по типу операции — теми же приглушёнными оттенками, что у ПРОСТЫХ
   строк ДДС (не у блоков-разделов): доход зелёный, перенос — циановый. Красим фоном
   (background-color), а не картинкой: подсветка строки под курсором рисуется картинкой
   и ложится сверху, поэтому наведение остаётся видимым. */
.ops-grid tbody tr.op-income td { background-color: rgb(var(--green-rgb) / .10); }
.ops-grid tbody tr.op-transfer td { background-color: rgb(var(--accent-rgb) / .09); }
.badge { display: inline-block; padding: 2px 8px; margin-right: 4px; font-family: var(--font-mono);
  font-size: var(--fs-label); letter-spacing: var(--ls-mid); color: var(--text-2); border: 1px solid var(--line);
  white-space: nowrap; }   /* «В работе» не переносим: рамка в две строки читается как поломка */
.badge-warn { color: var(--amber); border-color: rgb(var(--amber-rgb) / .5); }
.badge-danger { color: var(--danger); border-color: rgb(var(--danger-rgb) / .5); }
/* Поиск + переключатель охвата — заподлицо с чипами, в одну строку */
.ops-search-wrap { display: inline-flex; align-items: stretch; gap: 8px; margin-left: auto; }
.ops-search-form { display: flex; margin: 0; }
.ops-search { width: 240px; padding: 8px 14px; font-family: var(--font-mono); font-size: var(--fs-label);
  letter-spacing: normal; color: var(--text); background: rgb(var(--surface-rgb) / .8);
  border: 1px solid rgb(var(--line-rgb) / .22); }
.ops-search:focus { border-color: var(--accent-line); outline: none; }
/* Переключатель охвата поиска: три точки, горит 1 (период) или 3 (вся база) */
.dot-scope { display: inline-flex; align-items: center; gap: 6px; padding: 8px 12px;
  background: rgb(var(--surface-rgb) / .8); border: 1px solid rgb(var(--line-rgb) / .22); cursor: pointer; }
.dot-scope i { width: 7px; height: 7px; border-radius: 50%; background: var(--text-2); opacity: .3;
  transition: opacity .15s, background .15s; }
.dot-scope.scope-1 i:first-child { background: var(--accent); opacity: 1; }
.dot-scope.scope-3 i { background: var(--accent); opacity: 1; }
.dot-scope.is-locked { cursor: default; }
.dot-scope:not(.is-locked):hover { border-color: var(--accent-line); }

/* Встреча с «красным» статусом (Недозвон/Перенос/Отмена): слот не занимает, поэтому в
   реестре приглушена — видно, что строка есть, но она не про состоявшуюся работу.
   Селектор длиннее нужного намеренно: у `.data-table tbody td` специфичность выше. */
.data-table tbody tr.meet-red td { color: var(--muted); }

/* Подсветка значений во встречах: зелёное — состоялось/подтверждено, красное — сорвалось
   или отказ (как в привычной таблице). Правило идёт ПОСЛЕ приглушения красной строки:
   специфичность равная, и цвет самого статуса не должен теряться в приглушённой строке. */
.data-table tbody tr td.cell-good { color: rgb(var(--green-rgb)); }
.data-table tbody tr td.cell-bad { color: var(--danger); }
/* Встреча дошла до денег («Оплата» = есть чистовая сделка по этому лиду): вся строка
   зелёным фоном, как доходные строки ДДС. Красим background-color, а не картинкой:
   подсветка под курсором рисуется картинкой и ложится сверху. */
.data-table tbody tr.meet-done td { background-color: rgb(var(--green-rgb) / .10); }

/* Сетка мест в срезе «День»: час отделён от часа линией поярче, свободное место —
   пустая строка (кликом по ней заводят встречу), поэтому она подсвечивается как живая. */
.data-table tbody tr.hour-start td { border-top: 1px solid var(--line); }
.slot-free td { height: var(--row-h); }
.slot-free:hover td { background-color: rgb(var(--accent-rgb) / .04); cursor: text; }

/* ── Календарь встреч (модуль «Встречи») ── */
.cal-daytitle { font-family: var(--font-body); font-weight: 400; font-size: var(--fs-h2);
  letter-spacing: var(--ls-mid); color: var(--text); }
.cal-toolbar { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; margin-bottom: var(--space-6); }
.cal-nav { display: inline-flex; gap: 6px; }
.cal-date-form { display: flex; margin: 0; }
.cal-date { padding: 8px 14px; font-family: var(--font-mono); font-size: var(--fs-label); color: var(--text);
  background: rgb(var(--surface-rgb) / .8); border: 1px solid rgb(var(--line-rgb) / .22); }
.cal-date:focus { border-color: var(--accent-line); outline: none; }
.cal-count { margin-left: auto; font-family: var(--font-mono); font-size: var(--fs-label);
  letter-spacing: var(--ls-mid); color: var(--muted); }
/* Одна строка = один час; в час до 3 активных встреч, каждая — своя строка текста в
   каждой колонке (число строк во всех колонках равно → выровнены). */
.cal-table td { vertical-align: top; }
.cal-hour { color: var(--accent); letter-spacing: var(--ls-mid); white-space: nowrap; font-variant-numeric: tabular-nums; }
.cal-table .cl { line-height: 1.75; min-height: 1.4em; white-space: nowrap; overflow: hidden;
  text-overflow: ellipsis; max-width: 180px; }
.cal-table .cl-comment { max-width: 260px; }
.cal-table .cl-red { color: var(--muted); opacity: .6; }
.cal-table .cl-mkc-red { color: var(--danger); }
.cal-other .cal-hour { color: var(--muted); }
.cal-table .cl[data-mid] { cursor: pointer; }
.cal-table .cl[data-mid]:hover { color: var(--accent-bright); }
/* Инлайн-редактор встречи (раскрывается под часом по клику) */
.cal-editrow > td { background: var(--accent-glow); border-bottom: 1px solid var(--line); padding: 14px 12px; }
.medit-grid { display: flex; flex-wrap: wrap; gap: 10px 14px; }
.medit-grid label { display: flex; flex-direction: column; gap: 4px; font-family: var(--font-mono);
  font-size: var(--fs-label); letter-spacing: var(--ls-mid); text-transform: uppercase; color: var(--muted); }
.medit-grid .medit-wide { flex: 1 1 100%; }
.medit-grid input, .medit-grid select { min-width: 120px; }
.medit-actions { display: flex; gap: 8px; margin-top: 12px; }
.medit-del { margin-left: auto; }

/* Админка · сотрудники: действия в строке рядом, архивные строки приглушены */
/* Колонка действий (✕/✓) — всегда у правого края: она последняя и служебная, слева от
   неё данные, и «плавающие» кнопки посреди пустой колонки читались бы как мусор. */
.col-actions { text-align: right; }
.col-actions .row-actions { justify-content: flex-end; }
/* Ширина — по содержимому (width: 1% — колонка сжимается до контента): когда таблица
   растянута шире своих колонок, браузер раздавал излишек и последней колонке, а кнопки
   прижаты вправо — весь излишек ложился СЛЕВА от них, и отступы выходили кривыми
   (Встречи: слева 27px при правых 16). Излишек теперь достаётся колонкам данных. */
.data-table th.col-actions { width: 1%; }
.row-actions { display: flex; gap: 6px; align-items: center; }
.row-actions form { display: inline; margin: 0; }
/* Кнопки строки видны ВСЕГДА: галочка «сохранить» до открытия строки стоит на месте,
   но неактивна (disabled, см. .btn:disabled). Раньше она пряталась невидимостью и
   «появлялась из ниоткуда» за краем прокрутки — приходилось искать её ползунком.
   Заодно колонка не меняет ширину при открытии строки — таблица не дёргается. */
/* В прокручиваемых таблицах колонка действий ЛИПНЕТ к правому краю: у широких реестров
   (Встречи, Дебиторка) она уезжала за край, и кнопки приходилось докручивать ползунком.
   Подложка непрозрачная, как у липких колонок воронки; цвет смысловых строк возвращает
   слой-картинка ниже. Когда таблица влезает целиком, липкость просто не срабатывает. */
.table-wrap .data-table th.col-actions, .table-wrap .data-table td.col-actions {
  position: sticky; right: 0; z-index: 1; background-color: var(--panel-solid);
}
/* Подложки липких колонок — ДО ВЕРХА КАРТЫ (решение владельца): таблица начиналась ниже
   на внутренний отступ панели, и над колонкой «Сохр» оставалась полоска панельного
   градиента — столбец выглядел обрубленным. Приём тот же, что у воронки: если таблица —
   первое в карте (реестры), её обёртка поднимается под верх карты, а визуальный отступ
   возвращает padding самой шапки. Тогда фон шапки честно доходит до края.
   ⚠ Псевдоэлементом ПОВЕРХ таблицы это не сделать: .table-wrap — контейнер прокрутки
   (overflow-x: auto), и всё, что торчит выше его рамки, обрезается — стили считаются,
   а пикселей нет (наступили). В карточке сделки над таблицей заголовок панели с
   кнопкой — там wrap не первый, и правило сознательно не действует. */
.panel > .table-wrap:first-child { margin-top: calc(-1 * var(--panel-py)); }
.panel > .table-wrap:first-child .data-table thead th { padding-top: var(--panel-py); }
.table-wrap .data-table tr.meet-done td.col-actions,
.table-wrap .data-table tr.op-income td.col-actions {
  background-image: linear-gradient(rgb(var(--green-rgb) / .10), rgb(var(--green-rgb) / .10)); }
.table-wrap .data-table tr.op-transfer td.col-actions {
  background-image: linear-gradient(rgb(var(--accent-rgb) / .09), rgb(var(--accent-rgb) / .09)); }
.row-archived td { opacity: .55; }
.pw-form { display: flex; gap: 6px; align-items: center; margin: 0; }
.modchecks { display: flex; flex-wrap: wrap; gap: 4px 10px; max-width: 340px; white-space: normal; }
.modck { display: inline-flex; align-items: center; gap: 4px; font-family: var(--font-mono);
  font-size: var(--fs-label); color: var(--text-2); white-space: nowrap; }

/* ── Журнал действий (Настройки → Журнал) ── */
/* Изменения в клетке — столбиком «Поле: было → стало»; длинная строка переносится,
   а не распирает таблицу. Имя поля приглушено: главное — сами значения. */
.jr-changes { white-space: normal; }
.jr-change { line-height: 1.6; }
.jr-field { color: var(--muted); }

/* ── ФОТ: плитки сотрудников ── */
.fot-add { display: flex; gap: 8px; margin: 0; }
.tiles { display: grid; grid-template-columns: repeat(auto-fill, minmax(240px, 1fr));
  gap: 14px; margin-top: var(--space-6); }
.tile { display: block; background: linear-gradient(180deg, var(--panel-top), var(--panel-bot));
  border: 1px solid var(--line); padding: 18px 20px; color: var(--text);
  clip-path: polygon(0 0, calc(100% - 16px) 0, 100% 16px, 100% 100%, 0 100%); }
.tile:hover { border-color: var(--accent-line); color: var(--text); }
.tile-soon { opacity: .5; }
/* Название плитки набрано как заголовок панели (тот же кегль и капс) — иначе на одной
   странице два вида заголовков. Подпись под ним — БЕЗ разрядки (решение владельца): это
   строчный пояснительный текст, а не капс-ярлык, и разрядка его только разреживала. */
.tile-name { font-family: var(--font-body); font-weight: 400; font-size: var(--fs-h2);
  letter-spacing: var(--ls-mid); text-transform: uppercase; }
.tile-sub { font-family: var(--font-mono); font-size: var(--fs-label); letter-spacing: normal; color: var(--muted); margin-top: 4px; }
.tile-kpi { display: flex; justify-content: space-between; align-items: baseline; margin-top: 14px;
  border-top: 1px solid var(--line-soft); padding-top: 10px; }
.tile-kpi-label { font-family: var(--font-mono); font-size: var(--fs-label); letter-spacing: var(--ls-mid);
  text-transform: uppercase; color: var(--muted); }
.tile-kpi-val { font-family: var(--font-mono); font-size: var(--fs-data); color: var(--accent); }

/* ── ФОТ: таблица метрики × месяцы ── */
.fot-bar { display: flex; align-items: center; gap: 12px; margin-bottom: var(--space-6); }
/* Ширина по содержимому (см. выше), но не уже панели: у сотрудника с парой месяцев
   таблица обрывалась на середине карты, а справа зияла пустота. */
.fot-table { min-width: 100%; }
.fot-table td, .fot-table th { white-space: nowrap; }
.fot-table .fot-head { position: sticky; left: 0; z-index: 1; background-color: var(--panel-solid);
  font-family: var(--font-mono); font-size: var(--fs-label); letter-spacing: normal;
  color: var(--muted); text-align: left; text-transform: none; }
/* Поле занимает клетку целиком: иначе на растянутой таблице введённое прижималось к
   левому краю, а авто-строки — к правому, и цифры одного месяца шли двумя колонками. */
.fot-table .gcell { width: 100%; min-width: 88px; }
.fot-table .fot-note { min-width: 160px; }
.fot-auto td, .fot-auto .num { color: var(--muted); }
.fot-emph td { color: var(--accent); font-weight: 600; }
.fot-emph .fot-head { color: var(--accent); }
.fot-sep td { padding: 0; height: 10px; border-bottom: 1px solid var(--line); }
.fot-remove { margin-top: 12px; }
/* Страница заведения встречи (форма .mnew с чипами часов) удалена: встречу заводят
   всплывающим окном — тем же, что правит её в календаре. Её стили ушли вместе с ней. */
.chip.is-disabled { opacity: .35; cursor: not-allowed; }
.kv-wide { grid-column: 1 / -1; }
/* «До конца строки»: поле занимает остаток ряда, а не всю ширину — так комментарий
   встаёт рядом со статусом, а не под ним отдельной строкой. */
.kv-rest { grid-column: 2 / -1; }

/* ── График потока лидов (дашборд) ──
   Одна линия = сумма выбранных источников; прошлый период — тонкий пунктир.
   Рисует js/leadchart.js: SVG строится в пикселях по ширине панели, поэтому
   размеров тут не задаём — только цвета, отступы и вид подписей. */
/* График слева, доли пирогом справа. Сетка ТА ЖЕ, что у KPI-панелей сверху (четыре
   равные колонки), а панели занимают 3 и 1 — тогда их края встают вровень с KPI.
   Пропорции 3fr/1fr тут не годятся: они делят и промежуток, и стык уезжает. */
.chart-row { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: var(--gap); }
/* График занимает три колонки из четырёх, пирог — одну. Считаем по ПАНЕЛЯМ, а не по
   «первому ребёнку»: рядом с ними в блоке лежит <script> с данными периода, и он
   когда-то уже утащил это правило на себя — график тогда сжался до четверти. */
.chart-row > .panel:first-of-type { grid-column: span 3; }
/* position: relative обязателен: внутри живёт подсказка (.chart-tip, absolute) —
   без него она позиционировалась от страницы и улетала за экран (прогноз баланса). */
.chart-bar { margin: var(--space-5) 0 var(--space-2); position: relative; }

/* ══ Обратная связь: жучок в шапке + окно тикетов ══════════════════════════════
   Из макета взята СТРУКТУРА (полоса тикетов слева, тикет справа, чат внизу), вид —
   наши компоненты и токены: свои кегли/цвета тут не заводим. */
/* Жучок — кнопка шапки без подписи (квадрат, как переключатель темы). Кнопка чата —
   тоже опора для метки (bug-badge абсолютный: без relative он уплывал в угол шапки). */
.hbtn-bug { position: relative; width: var(--ctl-h); padding: 0; justify-content: center; }
.hbtn-chat { position: relative; }
/* Метка непрочитанных: оранжевая, как в макете — она про «тебе ответили», а не
   про ошибку, поэтому не красная. Висит углом на кнопке. */
.bug-badge { position: absolute; top: -6px; right: -6px; min-width: 16px; height: 16px;
  padding: 0 3px; display: flex; align-items: center; justify-content: center;
  font-family: var(--font-mono); font-size: 9px; line-height: 1;
  color: var(--bg); background: var(--accent-2);
  box-shadow: 0 0 8px rgb(var(--accent2-rgb) / .8); }

/* Окно шире обычного: внутри две колонки и переписка. */
.modal-tickets { width: min(1180px, 94vw); }
.tk-wrap { display: flex; flex-direction: column; min-height: 0; }
#tickets-body.is-loading { opacity: .55; transition: opacity .12s; }

/* Кнопки «Админ» и «+ Новый тикет» — в шапке окна, между подписью и крестиком.
   margin-left: auto прижимает блок вправо; шапке — выравнивание по центру, а не по
   базовой линии: кнопки выше строки текста, по базовой они висели бы вразнобой. */
.modal-tickets .modal-head { align-items: center; }
.tk-head-actions { margin-left: auto; display: flex; align-items: center; gap: 10px; }
/* Свободное место шапки забирает блок кнопок; крестику остаётся обычный отступ —
   иначе два margin-left:auto поделили бы пустоту пополам и кнопки повисли в середине. */
.modal-tickets .modal-close { margin-left: 14px; }
.tk-error { margin: var(--space-4) var(--panel-px) 0; color: var(--accent-2);
  font-family: var(--font-mono); font-size: var(--fs-label); }

/* Две колонки: полоса фиксированной ширины (в макете 288px) + всё остальное.
   min-height: 0 обязателен — иначе прокрутка появляется у окна, а не внутри колонок. */
.tk-grid { display: grid; grid-template-columns: 288px minmax(0, 1fr);
  min-height: 0; max-height: 62vh; }
.tk-list { display: flex; flex-direction: column; min-height: 0;
  border-right: 1px solid var(--line-soft); }
.tk-list-head { display: flex; align-items: baseline; justify-content: space-between;
  gap: var(--space-4); padding: var(--space-5) var(--space-4) var(--space-2); }
.tk-label { font-family: var(--font-mono); font-size: var(--fs-label);
  letter-spacing: var(--ls-mid); text-transform: uppercase; color: var(--muted); }
.tk-count { font-family: var(--font-mono); font-size: var(--fs-label); color: var(--faint); }
.tk-list-body { overflow-y: auto; min-height: 0; }

/* Строка тикета: кнопка на всю ширину — по ней кликают целиком. */
.tk-item { display: flex; flex-direction: column; gap: 6px; width: 100%;
  padding: var(--space-4); background: none; border: 0;
  border-bottom: 1px solid var(--line-faint); text-align: left; cursor: pointer; }
.tk-item:hover { background: rgb(var(--accent-rgb) / .05); }
.tk-item.is-active { background: rgb(var(--accent-rgb) / .10);
  box-shadow: inset 2px 0 0 var(--accent); }
.tk-item-top { display: flex; align-items: center; gap: 9px; width: 100%; }
.tk-no { font-family: var(--font-mono); font-size: var(--fs-label);
  letter-spacing: var(--ls-mid); color: var(--text-2); }
.tk-no-accent { color: var(--accent); }
.tk-date { margin-left: auto; font-family: var(--font-mono); font-size: var(--fs-label);
  color: var(--faint); }
.tk-item-title { font-family: var(--font-body); font-size: var(--fs-data);
  line-height: 1.4; color: var(--text); }
.tk-item-who { font-family: var(--font-mono); font-size: var(--fs-label); color: var(--muted); }
.tk-unread { margin-left: 0; min-width: 15px; height: 15px; padding: 0 3px;
  display: flex; align-items: center; justify-content: center;
  font-family: var(--font-mono); font-size: 9px; color: var(--bg); background: var(--accent-2); }
/* У непрочитанного дата уезжает влево от метки — иначе метка липнет к краю. */
.tk-item-top .tk-unread { margin-left: 0; }

/* Ромб статуса: одна фигура, цвет по классу (в разметке русских слов нет). */
.tk-diamond { width: 9px; height: 9px; flex: none; transform: rotate(45deg);
  border: 1px solid currentColor; background: currentColor; }
.tk-st-new  { color: var(--accent); }
.tk-st-work { color: var(--amber); }
.tk-st-done { color: rgb(var(--green-rgb)); }
.tk-st-text-new  { color: var(--accent); }
.tk-st-text-work { color: var(--amber); }
.tk-st-text-done { color: rgb(var(--green-rgb)); }

.tk-detail { display: flex; flex-direction: column; min-height: 0; overflow-y: auto; }
.tk-detail-head { padding: var(--space-5) var(--panel-px) var(--space-5);
  border-bottom: 1px solid var(--line-faint); }
.tk-detail-meta { display: flex; align-items: center; gap: var(--space-4); }
.tk-detail-meta .tk-date { margin-left: 0; }
/* Заголовок тикета — кеглем заголовка страницы, но без разрядки: это текст
   человека, а не ярлык (капс и разрядка сделали бы его нечитаемым). */
.tk-title { margin: var(--space-2) 0 0; font-size: var(--fs-h1); font-weight: 500;
  line-height: 1.3; letter-spacing: normal; text-transform: none; color: var(--text); }
.tk-status { display: flex; align-items: center; gap: var(--space-4);
  margin-top: var(--space-4); padding: 10px var(--space-4);
  border: 1px solid var(--line-soft); background: rgb(var(--deep-rgb) / .6); }
.tk-status form { display: flex; align-items: center; gap: var(--space-2); }
.tk-status-combo .combo-input { width: 150px; }
.tk-status-name { font-family: var(--font-mono); font-size: var(--fs-label);
  letter-spacing: var(--ls-mid); text-transform: uppercase; white-space: nowrap; }
.tk-status-desc { font-family: var(--font-mono); font-size: var(--fs-label); color: var(--muted); }

.tk-block { padding: var(--space-5) var(--panel-px); border-bottom: 1px solid var(--line-faint); }
.tk-block:last-child { border-bottom: 0; }
/* Сообщение человека: перенос по словам и СОХРАНЕНИЕ переводов строк — люди пишут
   абзацами, а без white-space всё склеивалось бы в один кусок. */
.tk-message { margin: var(--space-2) 0 0; font-size: var(--fs-body); line-height: 1.6;
  color: var(--text-2); white-space: pre-wrap; overflow-wrap: anywhere; }
.tk-files-label { display: block; margin-top: var(--space-5); }
.tk-files { display: flex; gap: 10px; flex-wrap: wrap; margin-top: var(--space-2); }
.tk-file { display: flex; align-items: center; gap: 10px; height: var(--ctl-h);
  padding: 0 var(--space-4); font-family: var(--font-mono); font-size: var(--fs-label);
  color: var(--text-2); background: rgb(var(--deep-rgb) / .7);
  border: 1px solid var(--line-soft); text-decoration: none; }
.tk-file:hover { border-color: var(--accent-line); color: var(--accent); }
.tk-file-size { color: var(--faint); }

/* Переписка: свои сообщения слева, ответы поддержки справа — как в мессенджере.
   align-items: flex-start обязателен — без него пузырь-флекс растягивался на всю
   ширину колонки (max-width), а не обрезался по своему тексту. */
.tk-chat-block { display: flex; flex-direction: column; gap: var(--space-4); }
.tk-msg { display: flex; flex-direction: column; gap: 6px; max-width: 78%;
  align-self: flex-start; align-items: flex-start; }
.tk-msg-admin { align-self: flex-end; align-items: flex-end; }
.tk-msg-meta { font-family: var(--font-mono); font-size: var(--fs-label); color: var(--faint); }
/* Отступы и строка — как у чипов/полей (8×14, строка 16px): однострочный пузырь
   выходит стандартные 34px высоты (--ctl-h), многострочный растёт той же сеткой. */
.tk-bubble { padding: 8px 14px; font-size: var(--fs-data); line-height: 16px;
  color: var(--text); background: rgb(var(--deep-rgb) / .7);
  border: 1px solid var(--line-soft); white-space: pre-wrap; overflow-wrap: anywhere; }
.tk-msg-admin .tk-bubble { background: rgb(var(--accent-rgb) / .10);
  border-color: var(--accent-line); }
.tk-reply { display: flex; gap: 10px; margin-top: var(--space-2); }
.tk-reply input { flex: 1; min-width: 0; }

/* Форма нового обращения: поля столбиком во всю ширину — сетка .kv здесь не годится,
   она раскладывает поля по колонкам, и «Тема» с «Сообщением» уезжали вправо. */
.tk-new { padding: var(--space-5) var(--panel-px);
  display: flex; flex-direction: column; gap: var(--space-5); }
.tk-new textarea { width: 100%; min-height: 120px; resize: vertical; }
.tk-new-actions { margin-top: 0; }
/* Выбор файла: нативную серую кнопку Windows прячем (hidden в разметке), нажимается
   <label class="btn"> — она выглядит как все кнопки портала. Сам системный диалог
   выбора файла остаётся, его подменить нельзя и не нужно. */
.tk-filepick { display: flex; align-items: center; gap: var(--space-4); }
.tk-filenames { font-family: var(--font-mono); font-size: var(--fs-label);
  color: var(--muted); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

/* ── Прогноз баланса (дашборд) ──
   Планировщик расходов (1 колонка) и график (3) — та же сетка, что у KPI и лидов. */
.forecast-row { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: var(--gap); }
.forecast-row > .panel:last-of-type { grid-column: span 3; }
/* Строки планировщика: прошедшая дата — тревога (расход случился или просрочен),
   сегодняшняя — акцент. Красим строку целиком: смысл у даты, а читают сумму.
   Клетки — открытые поля (.gcell), поэтому цвет вешаем на них. */
.plan-table .plan-past .gcell, .plan-table .plan-past td { color: var(--danger); }
.plan-table .plan-today .gcell, .plan-table .plan-today td { color: var(--accent); }
/* Сохранённая строка живёт текстом: при наведении подсвечивается ЦЕЛИКОМ и открывается
   по клику (механика Операций, просьба владельца). Хвост-добавление остаётся полями. */
.plan-table tbody tr[data-id]:not([data-id=""]):not(.is-editing) { cursor: pointer; }
.plan-table tbody tr[data-id]:not([data-id=""]):not(.is-editing):hover td {
  background-color: rgb(var(--accent-rgb) / .05); }
/* Планировщик — таблица открытых полей на механике ops-grid (поля растянуты на
   клетку с вылетом и стыкуются БЕЗ зазоров; крайние — на линии контента панели).
   Ширины колонок заданы ЯВНО: у таблицы из одних полей браузеру не по чему мерить
   колонки, и «Тип» схлопывался в ноль. Панель узкая (одна колонка сетки ≈ 300px),
   поэтому отступ клетки 8px вместо 16, а календарик у даты спрятан — пикер
   открывает js/forecast.js (showPicker) по клику в само поле. */
.plan-table { table-layout: fixed; --cell-px: 8px; }
.plan-table th:nth-child(2) { width: 118px; }    /* дата моно + иконка впритык — запас «Типу» */
/* Дата планировщика — моно (ширина у дат всегда одна, ничего не поедет), иконка
   прижата к тексту: зазор текст-иконка = зазор иконка-край (8px). */
.plan-table input[name="on_date"] { font-family: var(--font-mono);
  background-position: right 8px center; padding-right: 31px; }
.plan-table th:nth-child(3) { width: 90px; }     /* сумма с разрядами */
.plan-table th:last-child { width: 58px; }       /* ✕ + отступ до края контента */
.plan-table input[type="date"]::-webkit-calendar-picker-indicator { display: none; }
/* Кнопка ✕ (красная — общий .btn-danger) НЕ прижата к границе панели: у колонки
   действий отступ до линии контента, как у первой колонки слева. */
.panel .plan-table td.col-actions, .panel .plan-table th:last-child {
  padding-right: var(--panel-px); }
/* Легенда графика прогноза — в строку под графиком, точки цветов линии/столбиков. */
.forecast-legend { display: flex; gap: var(--space-7); margin-top: var(--space-4); flex-wrap: wrap; }
.fc-dot-line { background: var(--accent); }
.fc-dot-in   { background: rgb(var(--green-rgb)); }
.fc-dot-out  { background: var(--danger); }
/* Подсказка графика прогноза: доход зелёным, расход красным. */
.tip-in  { color: rgb(var(--green-rgb)); margin-left: 10px; }
.tip-out { color: var(--danger); margin-left: 10px; }
.chart-total { margin-left: auto; font-family: var(--font-mono); font-size: var(--fs-label);
  letter-spacing: var(--ls-mid); text-transform: uppercase; color: var(--muted); white-space: nowrap; }
.chart-total b { color: var(--text); font-weight: 400; }
.chart-total i { font-style: normal; margin-left: 8px; }
/* Мусор — оранжевым во всех трёх местах сразу: чип, итог и вторая линия графика. */
.chart-total em { font-style: normal; margin-left: 8px; color: var(--accent-2); }
.chart-trash.is-active { color: var(--accent-2); border-color: rgb(var(--accent2-rgb) / .65);
  background: rgb(var(--accent2-rgb) / .12); }
.chart-up { color: rgb(var(--green-rgb)); }
.chart-down { color: var(--accent-2); }
/* Недоступная кнопка: видно, что она есть, но нажать нельзя. Класс — для ссылок-кнопок
   (стрелка «вперёд» в текущем периоде), :disabled — для настоящих кнопок (галочка
   «сохранить» до открытия строки). Кнопку не прячем: спрятанная за краем прокрутки
   галочка «появлялась из ниоткуда», и её приходилось искать ползунком. */
.btn.is-disabled, .btn:disabled { opacity: .35; cursor: default; pointer-events: none; }
.chart-wrap { position: relative; }
.chart-svg { display: block; width: 100%; }
/* Подсказка при наведении: висит над графиком, мышь не ловит (иначе моргала бы). */
.chart-tip { position: absolute; top: 0; transform: translateX(-50%); pointer-events: none;
  padding: 6px 12px; background: var(--panel-solid); border: 1px solid var(--accent-line);
  font-family: var(--font-mono); font-size: var(--fs-label); color: var(--text); white-space: nowrap; }
.chart-tip b { color: var(--accent); font-weight: 400; margin-right: 8px; }
.tip-prev { color: var(--faint); margin-left: 10px; }
.tip-trash { color: var(--accent-2); margin-left: 10px; }
/* Чипы источников под графиком: выключенный приглушён, но читаем — по нему кликают обратно. */
.chart-sources { display: flex; flex-wrap: wrap; gap: 8px; margin-top: var(--space-5);
  padding-top: var(--space-4); border-top: 1px solid var(--line-faint); }
.chip-src.is-off { opacity: .4; }
.chip-src.is-off .chip-count { text-decoration: line-through; }
/* «Все источники» — первым чипом, в общем ритме (свой отбив только сбивал строку). */

/* ── Пирог долей (правая панель «Разбивка») ──
   Кольцо рисует js/leadchart.js теми же данными, что и линию, поэтому сумма долей
   всегда равна итогу графика. Цвета — категорийный ряд --cat-1..6 (§3), мусор
   оранжевый, «Прочие» приглушённые. */
.donut { position: relative; width: 172px; margin: var(--space-5) auto var(--space-2); }
.donut-svg { display: block; width: 100%; height: auto; }
/* В центре кольца только число — по оси, без подписи (её роль играет заголовок панели). */
.donut-center { position: absolute; inset: 0; display: flex;
  align-items: center; justify-content: center; pointer-events: none; }
/* Число в центре — тем же шрифтом и весом, что крупные цифры KPI (см. .kpi-value). */
.donut-value { font-family: var(--font-body); font-weight: 500; font-size: 32px; line-height: 1; color: var(--text); }
.legend { display: flex; flex-direction: column; gap: 10px; margin-top: var(--space-5); }
.legend-row { display: flex; align-items: center; gap: 10px; font-family: var(--font-mono);
  font-size: var(--fs-label); letter-spacing: var(--ls-legend); }
.legend-dot { width: 8px; height: 8px; flex: none; }
/* Длинное название источника обрезаем, а не переносим: строка легенды держит ритм. */
.legend-name { color: var(--text-2); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.legend-pct { margin-left: auto; color: var(--text); }
/* Наведение: доля кольца и её строка в легенде подсвечиваются ВМЕСТЕ — в кольце из
   восьми кусков иначе не понять, какая строка чему соответствует. */
.donut-arc { transition: stroke-width .12s ease; cursor: default; }
.donut-arc.is-hot { stroke-width: 18; }
.legend-row { cursor: default; }
.legend-row.is-hot .legend-name, .legend-row.is-hot .legend-pct { color: var(--accent); }
/* Подсказка доли — тот же вид, что у подсказки графика, но под кольцом по центру:
   у узкой панели ей некуда ехать за курсором. */
.donut-tip { left: 50%; top: auto; bottom: -6px; }
.tip-pct { color: var(--accent); margin-left: 10px; }

/* ── Недельный календарь встреч (дашборд) ──
   Семь колонок-дней; в колонке строка на каждый рабочий час, в строке — широкие слоты
   (по SLOTS_PER_HOUR активных мест). Ширину слота держит flex: 1 1 0 — если чип «Все
   статусы» добавит в час сорвавшиеся встречи, плашки просто ужмутся, а число свободных
   окон останется честным. Цвет слота = вид встречи, считает сервер (weekcal.py). */
.wcal-grid { display: grid; grid-template-columns: repeat(7, minmax(0, 1fr)); gap: 8px; }
.wcal-day { min-width: 0; display: flex; flex-direction: column; gap: 9px;
  padding: 12px 10px 10px; border: 1px solid var(--line-soft); }
.wcal-day.is-weekend { background: rgb(var(--deep-rgb) / .5); }
.wcal-day.is-today { border-color: var(--accent-line); background: rgb(var(--accent-rgb) / .04); }
/* Подпись дня — теми же параметрами, что заголовок колонки таблицы (это тоже шапка
   колонки, только календарной): шрифт заголовков, 500, капс, приглушённый цвет.
   Длинную обрезаем — колонка узкая, перенос ломал бы ритм. */
.wcal-title { font-family: var(--font-body); font-size: var(--fs-label); font-weight: 500;
  letter-spacing: var(--ls-mid); text-transform: uppercase; color: var(--muted);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
  padding-bottom: 8px; border-bottom: 1px solid var(--line-faint); }
.wcal-day.is-today .wcal-title { color: var(--accent); }
.wcal-row { display: flex; align-items: center; gap: 8px; min-width: 0; }
/* Час — данными (моно, --fs-label): ширина под «10:00» с запасом, чтобы подпись не
   жалась к плашкам и колонка часов везде начиналась одинаково. */
.wcal-hour { flex: none; width: 40px; font-family: var(--font-mono); font-size: var(--fs-label);
  letter-spacing: var(--ls-mid); color: var(--text-2); font-variant-numeric: tabular-nums; }
.wcal-day.is-today .wcal-hour { color: var(--accent); }
.wcal-slots { display: flex; gap: 4px; flex: 1 1 auto; min-width: 0; }
.wcal-slot { flex: 1 1 0; min-width: 0; height: 20px; padding: 0; cursor: pointer;
  background: transparent; border: 1px solid rgb(var(--line-rgb) / .16); }
/* Виды: свободно (пусто) · назначена (полупрозрачная) · подтверждена (циан ярче) ·
   прошла (зелёная) · отменена (красная). Тона — из базовых, литералов здесь нет. */
.wcal-slot.is-planned { background: rgb(var(--line-rgb) / .22); border-color: rgb(var(--line-rgb) / .55); }
.wcal-slot.is-confirmed { background: rgb(var(--accent-rgb) / .45); border-color: var(--accent);
  box-shadow: 0 0 6px rgb(var(--accent-rgb) / .35); }
.wcal-slot.is-done { background: rgb(var(--green-rgb)); border-color: rgb(var(--green-rgb));
  box-shadow: 0 0 6px rgb(var(--green-rgb) / .35); }
.wcal-slot.is-cancelled { background: rgb(var(--danger-rgb)); border-color: rgb(var(--danger-rgb)); }
/* Дистанционная встреча — трубка поверх плашки: место видно, не открывая карточку.
   Значок рисуется МАСКОЙ (картинка задаёт только форму, цвет — свой), поэтому он не
   спорит с цветом статуса, а проступает сквозь него, как матовое стекло: на тусклых
   плашках светлее фона, на ярких — темнее. Тона — токены темы, литералов здесь нет. */
.wcal-slot.is-dist { position: relative; }
.wcal-slot.is-dist::after {
  content: ""; position: absolute; inset: 0; background: var(--slot-icon-dim);
  -webkit-mask: var(--icon-phone) no-repeat center / 10px 10px;
  mask: var(--icon-phone) no-repeat center / 10px 10px;
}
.wcal-slot.is-confirmed.is-dist::after, .wcal-slot.is-done.is-dist::after,
.wcal-slot.is-cancelled.is-dist::after { background: var(--slot-icon-bright); }
/* Чипы «Все статусы» и «Мои» переключаются БЕЗ перезагрузки: сервер рисует всё сразу,
   режим меняет класс на панели (js/weekcal.js). Сорвавшиеся встречи занимают обычные
   места часа, поэтому при их показе прячется столько же лишних свободных окон — иначе
   час выглядел бы просторнее, чем он есть. Когда красными забит весь час, одно пустое
   место всё равно остаётся: без него в этот час нельзя было бы никого записать. */
.wcal:not(.is-red-on) .wcal-grid .wcal-slot.is-red { display: none; }
.wcal.is-red-on .wcal-grid .wcal-slot.is-extra-free { display: none; }
/* Чип «Мои»: свои встречи (назначил или принял) остаются в цвете, чужие гаснут. Чужие
   именно ГАСНУТ, а не пропадают — иначе занятое окно выглядело бы свободным, и человек
   назначил бы встречу на занятое место. Свободные окна не трогаем: они ничьи. */
/* Подсветка выбранного сотрудника (чип «Мои» — это он же, только выбранный одним
   нажатием): его встречи остаются в цвете, чужие гаснут. Чужие именно ГАСНУТ, а не
   пропадают — иначе занятые окна выглядели бы свободными, и человек назначил бы встречу
   на занятое место. Свободные окна не трогаем: они ничьи.
   Только в сетке: образцы в легенде — те же плашки, и без этого они гасли заодно. */
.wcal.is-hl-on .wcal-grid .wcal-slot:not(.is-hl):not(.is-free) { opacity: .22; }
.wcal.is-hl-on .wcal-grid .wcal-slot.is-hl { box-shadow: 0 0 0 1px var(--accent); }
/* Пока едет соседняя неделя, панель чуть гаснет — видно, что клик услышан. */
.wcal.is-loading .wcal-grid { opacity: .55; transition: opacity .12s; }
/* То же у блока «Объём лидов», когда меняют срез или листают период. */
.chart-row.is-loading { opacity: .55; transition: opacity .12s; }
/* Выбор сотрудника — фиксированной ширины, чтобы длинное ФИО не растягивало панель.
   Ширину задаём САМОМУ полю, а не обёртке: шеврон висит на обёртке, и когда та была
   шире поля, значок оказывался за рамкой. */
.wcal-who .combo-input { width: 240px; }
/* «Реестр →» — у правого края панели фильтров: это выход из модуля, а не фильтр. */
.wcal-registry { margin-left: auto; }
.wcal-slot:hover { outline: 1px solid rgb(var(--accent-rgb) / .8); outline-offset: 1px; }
.wcal-slot:focus-visible { outline: 1px solid var(--accent); outline-offset: 1px; }

/* Короткий чип-переключатель капсом («МОИ»): одно слово строчными теряется среди
   длинных чипов, капс делает из него ярлык. Капс — в CSS, а не в тексте шаблона. */
.chip-caps { text-transform: uppercase; }

/* Без линии над легендой (решение владельца): сетка календаря сама заканчивается
   ровным краем, и разделитель был лишним. */
.wcal-legend { display: flex; gap: 24px; flex-wrap: wrap; margin-top: var(--space-6); }
.wcal-leg { display: flex; align-items: center; gap: 9px; font-family: var(--font-mono);
  font-size: var(--fs-label); letter-spacing: var(--ls-legend); text-transform: uppercase;
  color: var(--text-2); }
/* Образец в легенде — тот же слот, только КВАДРАТ 12×12 (как точки прочих легенд)
   и без реакции на мышь. */
.wcal-leg .wcal-slot { flex: none; width: 12px; height: 12px; cursor: default; }
.wcal-leg .wcal-slot:hover { outline: none; }

/* Карточка слота (наведение). Ставится к слоту скриптом (position: fixed), мышь её не
   ловит — иначе она моргала бы, попадая под курсор у края плашки. */
.wcal-card { position: fixed; z-index: 60; width: 236px; padding: 14px;
  display: flex; flex-direction: column; gap: 8px; pointer-events: none;
  background: var(--panel-solid); border: 1px solid var(--accent-line);
  box-shadow: 0 12px 34px rgb(0 0 0 / .6); }
.wcal-card-top { display: flex; justify-content: space-between; align-items: center; gap: 10px;
  font-family: var(--font-mono); font-size: var(--fs-label); letter-spacing: var(--ls-mid);
  color: var(--accent); white-space: nowrap; }
/* Статус — ромб-маркер и подпись, как у .status в реестрах. */
.wcal-card-kind { display: flex; align-items: center; gap: 6px; font-size: 10px; }
.wcal-card-kind::before { content: ""; width: 5px; height: 5px; background: currentColor;
  transform: rotate(45deg); }
.wcal-card-kind.is-planned { color: var(--text-2); }
.wcal-card-kind.is-confirmed { color: var(--accent); }
.wcal-card-kind.is-done { color: rgb(var(--green-rgb)); }
.wcal-card-kind.is-cancelled { color: var(--danger); }
.wcal-card-kind.is-free { color: var(--faint); }
.wcal-card-name { font-family: var(--font-body); font-size: 14px; font-weight: 500;
  color: var(--text); line-height: 1.35; }
.wcal-card-row { font-family: var(--font-mono); font-size: 10px; letter-spacing: var(--ls-mid);
  color: var(--text-2); }
.wcal-card-note { font-family: var(--font-mono); font-size: 10px; color: var(--muted);
  letter-spacing: normal; }
.wcal-card-hint { font-family: var(--font-mono); font-size: 9px; letter-spacing: var(--ls-mid);
  text-transform: uppercase; color: var(--faint); }

/* Окно встречи: поля в две колонки — их пятнадцать, в одну окно уезжало бы за экран. */
.modal-meet { width: min(720px, calc(100vw - 2 * var(--space-8))); }
/* Окно платежа: полей всего три (номер, сумма, дата) — на всю ширину карты оно выглядело
   бы пустым. */
.modal-pay { width: min(640px, calc(100vw - 2 * var(--space-8))); }
/* Выбранное значение говорит цветом, как и в реестре: состоялось/подтверждено — зелёным,
   сорвалось/отказ — красным, «Дист» — янтарным (не беда и не победа, просто «не в офисе»).
   Списки значений приходят с сервера (те же, что у фильтра meetcls), в скрипте их нет. */
.combo-input.val-good { color: rgb(var(--green-rgb)); }
.combo-input.val-bad { color: var(--danger); }
.combo-input.val-warn { color: var(--amber); }
/* Разделитель между «про встречу» и «чем кончилась» — та же линия, что перед блоком
   повторного назначения (решение владельца: порядок полей в окне разбит надвое). */
.meet-sep { margin: var(--space-7) 0 var(--space-6); border-top: 1px solid var(--line); }
/* Загруженная запись: ссылка на файл и галочка «убрать» СТОЯТ НА МЕСТЕ поля выбора
   (второе состояние той же клетки, решение владельца), поэтому держат ту же высоту
   строки — при смене состояний окно не дёргается. Ссылка ужимается с многоточием:
   длинное имя файла не должно распирать клетку. */
.meet-file-have { align-items: center; gap: var(--space-4);
  min-height: var(--ctl-h); font-size: var(--fs-label); }
.meet-file-have a { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.meet-file-have .modck { flex: none; }
/* Повторное назначение — отдельной секцией внизу окна, за линией: это не поле встречи,
   а действие над ней. Видно только у завершённой (js/meetmodal.js). */
.meet-repeat { margin-top: var(--space-7); padding-top: var(--space-6);
  border-top: 1px solid var(--line); }
.repeat-head { font-family: var(--font-body); font-size: var(--fs-label);
  letter-spacing: var(--ls-mid); text-transform: uppercase; color: var(--muted);
  margin-bottom: var(--space-4); }
.repeat-row { display: flex; align-items: center; gap: var(--space-4); flex-wrap: wrap; }
/* Поле даты — по содержимому: глобальное правило растягивает input на всю ширину, и
   без этого дата занимала всю строку, а час с кнопкой уезжали под неё. */
.repeat-row .cal-date { flex: none; width: auto; }
.repeat-time .combo-input { width: 150px; }
.meet-repeat .field-hint { margin-top: var(--space-2); }

/* Два поля со своими ярлыками в ОДНОЙ клетке сетки (дата + время в окне встречи):
   половина строки занята парой, вторая половина остаётся под ID лида с кнопкой. */
.pair-row { display: flex; gap: var(--space-4); }
.pair-col { display: flex; flex-direction: column; gap: 4px; flex: 1; min-width: 0; }
.pair-col .cal-date { width: 100%; height: var(--ctl-h); }
/* Дата встречи заперта: правится она не здесь, а слотом календаря или блоком переноса
   внизу окна. Запираем ИМЕННО так — `pointer-events` + `tabindex="-1"`, без `readonly`
   и без `disabled`: Chrome у обоих прячет иконку календаря, и поле переставало быть
   похожим на поле даты (владелец просил иконку оставить). Проверено вживую: снимаешь
   readonly — иконка появляется. В форму значение не уходит: у поля нет `name`,
   настоящую дату везёт скрытое `meeting_date`. */
.cal-date.is-locked { color: var(--text-2); pointer-events: none; }
.cal-date.is-locked::-webkit-calendar-picker-indicator { opacity: .35; }

/* ID лида и кнопка «Загрузить» — одной строкой: подтяжка из CRM стоит вплотную к полю. */
.lead-row { display: flex; gap: var(--space-2); }
.lead-row input { min-width: 0; }
.lead-row .btn { flex: none; }

/* Мелкие утилиты формы новой сделки */
.inp-narrow { max-width: 220px; }
.section-gap { margin-top: var(--space-7); }
/* Сверка с Битриксом (кнопка CRM в карточке): чистовое → битриксовое */
.crm-arrow { color: var(--muted); margin: 0 4px; }
.crm-diff { color: var(--accent-2); }

/* ── Карточка сделки ──
   Верх (решение владельца): поля сделки — узкой вертикальной колонкой слева, а график и
   операции — широкими блоками справа, друг под другом (у них много колонок, в трети
   ширины таблицы приходилось крутить). Панель полей занимает обе строки сетки.
   Низ: рефералы · пирог платежей · дорожная карта — три равные колонки.
   min-width: 0 обязателен: без него широкая таблица распирает свою колонку, и сетка
   разъезжается — прокрутка должна оставаться внутри карты (.table-wrap).
   align-items: start — панели по своему содержимому, а не растянуты на всю строку. */
.deal-top { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 2fr);
  gap: var(--gap); align-items: start; }
/* Обе половины — СТОПКИ панелей, а не строки сетки: панели идут друг за другом каждая
   своей высоты и ничем не выровнены (решение владельца). Строки сетки тут не годятся —
   они растягиваются под самую высокую панель и разносят соседей пустотой на пол-экрана. */
.deal-main, .deal-side { display: flex; flex-direction: column; gap: var(--gap); min-width: 0; }
.deal-main > .panel, .deal-side > .panel { min-width: 0; }
/* Левая колонка тянется до низа правой (решение владельца — симметрия): рамки всех трёх
   колонок кончаются на одной линии. Содержимое остаётся сверху — это список полей, и
   разгонять его по высоте, как ленту вех в дорожной карте, незачем. */
.deal-main { align-self: stretch; }
.deal-main > .panel { flex: 1; }
/* Третий ярус правой колонки: пирог платежей и дорожная карта делят ряд пополам
   и выровнены ПО ВЫСОТЕ (решение владельца): строка сетки тянет обе панели под
   более высокую, пирог занимает освободившееся место по центру своей панели. */
.deal-pair { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: var(--gap); }
.deal-pair > .panel { min-width: 0; display: flex; flex-direction: column; }
/* Дорожная карта по высоте пары НЕ тянется (решение владельца): если «Дело» выше,
   цепочка вех заканчивается там, где заканчивается, — без пустого хвоста панели. */
.deal-pair > .rmap-panel { align-self: start; }
/* Пирог: кольцо слева, легенда рядом — столбиком она растянулась бы на всю ширину карты,
   и процент уехал бы от названия доли. В тесной панели легенда переносится под кольцо. */
.pie-body { display: flex; align-items: center; gap: var(--space-8); flex-wrap: wrap; }
/* В выровненной паре пирог забирает всю высоту панели и центрируется в ней —
   без этого выравнивание давало бы пустой подвал под кольцом. */
.deal-pair .pie-body { flex: 1; align-content: center; }
.pie-body .donut { margin: var(--space-2) 0; flex: none; }
.pie-body .legend { flex: 1 1 180px; min-width: 0; max-width: 420px; margin-top: 0; }
/* Номер платежа — кнопка-ссылка «#3»: по клику открывается окно правки строки графика.
   Решётка перед числом (а не просто цифра) показывает, что это нажимаемое, и заодно
   расширяет зону клика. Вид как у ссылки (циан + подчёркивание при наведении), а не как
   у кнопки: в ячейке таблицы рамка кнопки была бы шумом. Отступы поднимают высоту зоны
   нажатия до половины строки, оставаясь внутри клетки. */
.pay-no { padding: 6px 2px; background: none; border: 0; cursor: pointer;
  font-family: var(--font-mono); font-size: var(--fs-data); color: var(--accent); }
.pay-no:hover { color: var(--accent-bright); text-decoration: underline; }

/* Пирог платежей: цвет доли = судьба платежа. Один токен --pay-c на вид — им красятся
   и дуга кольца, и точка легенды (пары «цвет тут, цвет там» разъезжались бы). */
.pay-late    { --pay-c: var(--danger); }                 /* оплачен с просрочкой */
.pay-ontime  { --pay-c: rgb(var(--green-rgb)); }         /* оплачен в срок */
.pay-early   { --pay-c: var(--accent); }                 /* оплачен заранее */
.pay-partial { --pay-c: var(--amber); }                  /* оплачен частично */
.pay-unpaid  { --pay-c: rgb(var(--line-rgb) / .45); }    /* ещё не оплачен */
.pay-arc { fill: none; stroke: var(--pay-c); stroke-width: 14;
  transition: stroke-width .12s ease; cursor: default; }  /* живёт как дашбордное кольцо */
.pay-arc.is-hot { stroke-width: 18; }
.legend-dot.pay-dot { background: var(--pay-c); }

/* Дорожная карта (заготовка): вертикальная цепочка ромбов-вех с линией. Пройденные
   вехи циановые, текущая горит; будущие — пустые ромбы, как «ещё не случилось». */
.rmap { display: flex; flex-direction: column; margin-top: var(--space-2); }
/* В выровненной паре (рядом с делом о банкротстве) карта тянется на всю высоту панели, а
   вехи расходятся по ней: иначе под последней вехой оставалась бы пустая треть панели.
   `1 0 auto` — раздаём ТОЛЬКО лишнее место поверх содержимого, поэтому длинная веха не
   обрежется, если пара окажется тесной. Соединительная линия растёт вместе с шагом. */
.deal-pair .rmap { flex: 1; }
.deal-pair .rmap-step { flex: 1 0 auto; }
.rmap-step { position: relative; padding: 0 0 var(--space-6) 26px; }
.rmap-step::before { content: ''; position: absolute; left: 5px; top: 16px; bottom: -2px;
  width: 1px; background: var(--line); }
.rmap-step:last-child { padding-bottom: 0; }
.rmap-step:last-child::before { display: none; }
.rmap-dot { position: absolute; left: 1px; top: 4px; width: 9px; height: 9px;
  transform: rotate(45deg); border: 1px solid var(--muted); background: transparent; }
.rmap-step.is-done .rmap-dot { border-color: var(--accent-line); background: var(--accent-fill-2); }
.rmap-step.is-now .rmap-dot { border-color: var(--accent); background: var(--accent);
  box-shadow: 0 0 8px rgb(var(--accent-rgb) / .7); }
.rmap-name { font-family: var(--font-body); font-size: var(--fs-label);
  letter-spacing: var(--ls-mid); text-transform: uppercase; color: var(--text-2); }
.rmap-step.is-now .rmap-name { color: var(--accent); }
.rmap-meta { font-family: var(--font-mono); font-size: var(--fs-label); color: var(--muted);
  margin-top: 2px; }

/* Поиск над таблицей (в шапке страницы) — размер как у чипа/кнопки (стоит рядом с ними) */
.table-search { margin-left: auto; max-width: 300px; padding: 8px 14px;
  font-size: var(--fs-label); letter-spacing: normal; }

/* ── Сообщение об ошибке и об успехе ── */
/* Живут ВНУТРИ своей формы (как на странице входа), а не плашкой над всей страницей:
   сообщение относится к одному блоку, и рядом с ним его и читают. */
.error {
  font-family: var(--font-mono); font-size: var(--fs-data); color: var(--danger);
  background: rgb(var(--danger-rgb) / .1); border: 1px solid rgb(var(--danger-rgb) / .5);
  padding: 10px 14px;
}
.ok {
  font-family: var(--font-mono); font-size: var(--fs-data); color: var(--accent);
  background: var(--accent-fill); border: 1px solid var(--accent-line);
  padding: 10px 14px;
}

/* ══════════════════════════════════════════════════════════════════════════
   Страница входа (без шапки и подвала). Две панели: форма + декоративный блок.
   ══════════════════════════════════════════════════════════════════════════ */
.bare { display: flex; flex-direction: column; min-height: 100vh; }
.bare-main { flex: 1; display: flex; flex-direction: column; }

/* ── Заставка входа (boot): терминальный прогон реальных проверок системы ──
   Голый каркас (без шапки/подвала), строки выводятся лентой из JS. */
/* Лента идёт СВЕРХУ ВНИЗ (как кусок терминала), а не по центру экрана.
   Колонка — ровно как у контента сайта (.container): та же ширина и края. */
.boot { flex: 1; display: flex; flex-direction: column; justify-content: flex-start;
  width: 100%; max-width: var(--maxw); margin: 0 auto; padding: var(--space-9) var(--edge) var(--space-10); }
.boot-head { font-family: var(--font-mono); font-size: var(--fs-label); letter-spacing: var(--ls-mid);
  text-transform: uppercase; color: var(--accent); margin-bottom: var(--space-6); }
.boot-log { font-family: var(--font-mono); font-size: var(--fs-data); line-height: 2;
  color: var(--text-2); }
.boot-log b { font-weight: 400; color: var(--accent); }
.boot-ts { color: var(--faint); }
.boot-status { margin-top: var(--space-8); font-family: var(--font-body); font-size: var(--fs-h1);
  letter-spacing: var(--ls-wide); text-transform: uppercase; color: var(--accent);
  opacity: 0; transition: opacity .12s; }
.boot-status.is-on { opacity: 1; }
.boot-hint { margin-top: var(--space-4); font-family: var(--font-mono); font-size: var(--fs-label);
  letter-spacing: var(--ls-mid); text-transform: uppercase; color: var(--faint); }

/* Центрируем по блоку «Аутентификация + 2 панели» (как было без лого):
   поднимаем всё на половину высоты лого+отступа, лого нависает сверху. */
/* Вся страница входа держит прежнюю разрядку .1em — переопределяем ТОКЕН на корне
   страницы, а не правим её надписи поимённо: их с десяток, и любую новую пришлось бы
   не забыть. `body.bare` бывает только на входе. */
body.bare { --ls-mid: var(--ls-strong); }
.auth { flex: 1; display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 36px; padding: 40px 20px; transform: translateY(calc((156px + var(--space-8)) / -2)); }
.auth-head { text-align: center; }
.auth-title { display: inline-flex; align-items: center; gap: 12px; font-family: var(--font-body); font-weight: 400; font-size: var(--fs-h1); letter-spacing: var(--ls-wide); text-transform: uppercase; color: var(--text); }
.auth-sub { font-family: var(--font-mono); font-size: var(--fs-label); letter-spacing: var(--ls-mid); text-transform: uppercase; color: var(--muted); margin-top: 10px; }

/* Бренд-локап на входе: детальный логотип покрупнее + название + подпись */
.auth-top { display: flex; flex-direction: column; align-items: center; gap: var(--space-8); }
.auth-brand { display: inline-flex; align-items: center; }
/* Знак входа: цвет граней/узлов — акцент темы (внутри SVG это currentColor),
   ядро — второй акцент. Движение узлов по орбите — js/logo-orbit.js. */
.auth-logo { width: 156px; height: 156px; display: block; flex-shrink: 0; color: var(--accent); overflow: visible; }

.auth-panels { display: grid; grid-template-columns: repeat(2, minmax(280px, 420px)); gap: var(--gap); align-items: stretch; }
.auth-panels .panel { display: flex; flex-direction: column; gap: var(--space-6); padding: var(--space-9) 32px; }

.auth-form { display: flex; flex-direction: column; gap: var(--space-6); }
.auth-form .btn { width: 100%; margin-top: var(--space-1); }

/* Декоративная (неактивная) панель «биометрия» */
.auth-secondary { background: linear-gradient(180deg, var(--panel-top-dim), var(--panel-bot-dim)); border-color: var(--line-soft); align-items: center; justify-content: center; text-align: center; gap: var(--space-5); cursor: not-allowed; }
.auth-secondary-title { font-family: var(--font-body); font-weight: 400; font-size: var(--fs-h2); letter-spacing: var(--ls-mid); text-transform: uppercase; color: var(--muted); }
.auth-secondary-sub { font-family: var(--font-mono); font-size: var(--fs-label); letter-spacing: var(--ls-mid); text-transform: uppercase; color: var(--faint); }
.auth-scanner { position: relative; width: 120px; height: 120px; display: flex; align-items: center; justify-content: center; margin-bottom: 20px; }
.auth-scanner svg { width: 64px; height: 61px; display: block; opacity: .6; }
.auth-scanner .corner { position: absolute; width: 8px; height: 8px; opacity: .7; }
.auth-scanner .c-tl { top: 0; left: 0; border-top: 1px solid var(--muted); border-left: 1px solid var(--muted); }
.auth-scanner .c-tr { top: 0; right: 0; border-top: 1px solid var(--muted); border-right: 1px solid var(--muted); }
.auth-scanner .c-bl { bottom: 0; left: 0; border-bottom: 1px solid var(--muted); border-left: 1px solid var(--muted); }
.auth-scanner .c-br { bottom: 0; right: 0; border-bottom: 1px solid var(--muted); border-right: 1px solid var(--muted); }

/* ── Резиновость: на узком экране (планшет/телефон) — одна колонка,
      декоративную панель прячем, форма занимает ширину. ── */
@media (max-width: 900px) {
  .auth-panels { grid-template-columns: minmax(280px, 420px); }
  .auth-secondary { display: none; }
}

/* ── Анимации (индикатор «сеть» и т.п., Шаг 2) ── */
@keyframes ds-pulse { 0%, 100% { opacity: 1; } 50% { opacity: .25; } }

/* ── Сверка графиков (ВРЕМЕННОЕ, Сессия 16): три источника графика рядом ──
   Снести вместе со страницей admin_graph_compare.html и app/web/compare_graphs.py. */
.gcmp-head td { padding-top: 18px; background: rgb(var(--deep-rgb) / .45); }
.gcmp-meta { margin-left: 10px; font-family: var(--font-mono); font-size: var(--fs-label);
  color: var(--muted); }
.gcmp-flag, .gcmp-ok, .gcmp-disc { margin-left: 14px; font-family: var(--font-mono);
  font-size: var(--fs-label); letter-spacing: var(--ls-mid); text-transform: uppercase; }
.gcmp-flag { color: var(--accent-2); }
.gcmp-ok { color: rgb(var(--green-rgb)); }
.gcmp-disc { color: var(--accent); }
.gcmp-src { color: var(--text-2); white-space: nowrap; }
/* Слот платежа: сумма и дата в два этажа; расхождение — оранжевая подложка и текст,
   чтобы глаз сравнивал колонку внутри группы, не вчитываясь. */
td.gcmp-slot { white-space: nowrap; text-align: right; font-variant-numeric: tabular-nums; }
.gcmp-slot .gcmp-sum { display: block; }
.gcmp-slot .gcmp-date, .gcmp-total .gcmp-date { display: block; font-size: var(--fs-label);
  color: var(--muted); }
.gcmp-slot.is-diff { background: rgb(var(--accent2-rgb) / .07); }
.gcmp-slot.is-diff .gcmp-sum, .gcmp-slot.is-diff .gcmp-date { color: var(--accent-2); }
.gcmp-none { color: var(--faint); }
/* Строка «Истина» — черновик владельца: подпись и суммы цианом, строка кликабельна.
   В режиме правки клетки — компактные поля (сумма + дата в два этажа). */
.gcmp-truth-label { color: var(--accent); text-transform: uppercase;
  letter-spacing: var(--ls-mid); font-size: var(--fs-label); }
.gcmp-truth .gcmp-sum { color: var(--accent); }
.gcmp-truth-mark { margin-left: 14px; font-family: var(--font-mono); font-size: var(--fs-label);
  letter-spacing: var(--ls-mid); text-transform: uppercase; color: var(--accent); }
.gcmp-err { margin-left: 14px; font-family: var(--font-mono); font-size: var(--fs-label);
  color: var(--danger); }
.gcmp-truth:not(.is-editing) { cursor: pointer; }
.gcmp-truth:not(.is-editing):hover td {
  background-image: linear-gradient(rgb(var(--accent-rgb) / .06), rgb(var(--accent-rgb) / .06)); }
.gcmp-truth input.gcmp-in, .gcmp-truth.is-editing input[type="date"] {
  display: block; width: 100%; min-width: 96px; height: 24px; padding: 2px 6px;
  margin: 1px 0; font-size: var(--fs-label); }
.gcmp-truth.is-editing input[type="date"] { min-width: 116px; }
/* Кнопка «→ в истину» — не выше строки-источника. */
.gcmp-adopt { margin-left: 8px; }

/* ── Diff-вью блока «Сделка» (Сессия 17): заголовок поля строкой, под ним две
   ячейки — портал у ЛЕВОГО края, Битрикс у ПРАВОГО (вёрстка владельца).
   Совпадение — полупрозрачный циан (глаз спокоен), расхождение — оранжевый,
   поля без пары в CRM (ручные поля портала) — нейтральный прочерк. */
.deal-kv { width: 100%; border-collapse: collapse; }
.deal-kv td { padding: 0; vertical-align: top; }
.deal-kv .dkv-label { font-family: var(--font-mono); font-size: var(--fs-label);
  letter-spacing: var(--ls-mid); text-transform: uppercase; color: var(--faint);
  padding: 12px 0 3px; }
.deal-kv tr:first-child .dkv-label { padding-top: 0; }
.deal-kv .dkv-core, .deal-kv .dkv-crm { font-family: var(--font-mono);
  font-size: var(--fs-data); word-break: break-word; }
.deal-kv .dkv-core { text-align: left; padding-right: 10px; }
.deal-kv .dkv-crm { text-align: right; white-space: nowrap; max-width: 150px;
  overflow: hidden; text-overflow: ellipsis; }
.deal-kv .dkv-crm.is-same { color: rgb(var(--accent-rgb) / .55); }
.deal-kv .dkv-crm.is-diff { color: var(--accent-2); }
.deal-kv .dkv-crm.is-none { color: var(--faint); }

/* ── Чат сотрудников, дизайн v2 по макету «Меридиан чат» (первый вариант —
   docs/design/chat_v1). Слегка прозрачный фон, циановая рамка, активность
   полосками со свечением, сообщения одной колонкой с цветной полоской слева. ── */
.chat-drawer { position: fixed; top: 0; right: 0; bottom: 0; z-index: 40;
  width: min(570px, 96vw); display: flex; flex-direction: column;
  /* Градиент как у панелей (сверху светлее), но НЕПРОЗРАЧНЫЙ: просвет контента
     отвлекал от переписки (пересмотр владельца). */
  background: linear-gradient(180deg, rgb(var(--surface-rgb)), rgb(var(--surface2-rgb)));
  border-left: 1px solid rgb(var(--accent-rgb) / .35);
  box-shadow: -24px 0 60px rgb(0 0 0 / .55); }
.chat-head { display: flex; align-items: center; gap: 14px;
  padding: 29px var(--panel-px); border-bottom: 1px solid var(--line); }
.chat-secure { font-family: var(--font-mono); font-size: 10px; letter-spacing: .18em;
  text-transform: uppercase; color: var(--muted); white-space: nowrap; }
.chat-head .modal-close { margin-left: auto; }
.chat-panel-body, .chat-body { display: flex; flex-direction: column; flex: 1 1 auto; min-height: 0; }
.chat-hint { padding: 16px var(--panel-px); }

/* Карусель (макет): КВАДРАТЫ со срезанным правым нижним углом, инициалы внутри,
   воздуха больше. Бейдж и знак активности живут на КНОПКЕ, не внутри квадрата:
   clip-path срезал бы их вместе с углом. */
.chat-strip { display: flex; gap: 16px; padding: 16px var(--panel-px) 12px;
  overflow-x: auto; flex: none; border-bottom: 1px solid var(--line-soft); }
.chat-dot { position: relative; flex: none; width: 58px; background: none; border: none;
  padding: 0; cursor: pointer; text-align: center; color: var(--text-2); }
/* Срезы как у вкладок меню: верхний левый + нижний правый. */
.chat-dot-circle { position: relative; display: flex; align-items: center; justify-content: center;
  width: 46px; height: 46px; margin: 0 auto;
  clip-path: polygon(12px 0, 100% 0, 100% calc(100% - 12px), calc(100% - 12px) 100%, 0 100%, 0 12px);
  border: 1px solid rgb(var(--line-rgb) / .5); background: rgb(var(--deep-rgb) / .8);
  font-family: var(--font-mono); font-size: var(--fs-data); letter-spacing: .06em;
  color: var(--text-2); }
.chat-dot:hover .chat-dot-circle { border-color: rgb(var(--accent-rgb) / .6); }
/* Текущий собеседник — обводка как у активной вкладки меню: --accent-line
   (приглушённый циан), не чистый --accent (просьба владельца, стиль портала). */
.chat-dot.is-active .chat-dot-circle { border-color: var(--accent-line);
  background: rgb(var(--accent-rgb) / .08); }
.chat-dot-name { display: block; margin-top: 6px; font-family: var(--font-mono);
  font-size: 10px; letter-spacing: .06em; color: var(--muted); white-space: nowrap;
  overflow: hidden; text-overflow: ellipsis; }
.chat-dot-badge { position: absolute; top: -5px; right: 1px; min-width: 17px; height: 16px;
  padding: 0 4px; background: var(--accent-2); color: var(--bg);
  font-family: var(--font-mono); font-size: 10px; line-height: 16px; font-style: normal;
  box-shadow: 0 0 8px rgb(var(--accent2-rgb) / .8); }
.chat-dot-new .chat-dot-circle { border-style: dashed; color: var(--accent); font-size: 18px; }
/* Знак активности — цветная полоска ВДОЛЬ среза нижнего правого угла, дотянута
   до контура: лежит внутри квадрата, clip-path родителя ровняет её края по срезу. */
.chat-presence { position: absolute; right: -4px; bottom: 4px; width: 22px; height: 3px;
  transform: rotate(-45deg); }
.chat-presence.p-on { background: rgb(var(--green-rgb));
  box-shadow: 0 0 8px rgb(var(--green-rgb) / .9); }
.chat-presence.p-recent { background: var(--amber); box-shadow: 0 0 8px rgb(var(--amber-rgb) / .8); }
.chat-presence.p-off { background: rgb(var(--line-rgb) / .8); box-shadow: none; }

/* Форма новой группы (только у админа). */
.chat-group-form { flex: none; padding: 10px var(--panel-px); border-bottom: 1px solid var(--line-soft);
  display: flex; flex-direction: column; gap: 8px; }
.chat-group-people { display: flex; flex-wrap: wrap; gap: 4px 14px; max-height: 130px; overflow-y: auto; }

/* Открытый чат — флекс-колонка на всю оставшуюся высоту: лента прокручивается
   ВНУТРИ, панель отправки приклеена к низу (без этого тред рос по контенту и
   выталкивал форму за экран). */
.chat-thread { display: flex; flex-direction: column; flex: 1 1 auto; min-height: 0; }

/* Плашка текущего собеседника (макет): имя цианом, под ним отдел, справа статус
   со светящейся точкой. */
.chat-peer { flex: none; display: flex; align-items: center; justify-content: space-between;
  gap: 16px; padding: 12px var(--panel-px); border-bottom: 1px solid var(--line-soft);
  background: rgb(var(--deep-rgb) / .4); }
.chat-peer-names { display: flex; flex-direction: column; gap: 4px; min-width: 0; }
.chat-peer-name { font-size: var(--fs-data); font-weight: 400; letter-spacing: .14em;
  text-transform: uppercase; color: var(--accent); white-space: nowrap;
  overflow: hidden; text-overflow: ellipsis; }
.chat-peer-sub { font-family: var(--font-mono); font-size: 9px; letter-spacing: .16em;
  text-transform: uppercase; color: var(--muted); }
.chat-peer-state { display: flex; align-items: center; gap: 9px; flex: none;
  font-family: var(--font-mono); font-size: 10px; letter-spacing: .14em;
  text-transform: uppercase; white-space: nowrap; }
.chat-peer-state i { width: 7px; height: 7px; transform: rotate(45deg); }  /* ромб (макет) */
.chat-peer-state.p-on { color: rgb(var(--green-rgb)); }
.chat-peer-state.p-on i { background: rgb(var(--green-rgb)); box-shadow: 0 0 8px rgb(var(--green-rgb) / .9); }
.chat-peer-state.p-recent { color: var(--amber); }
.chat-peer-state.p-recent i { background: var(--amber); box-shadow: 0 0 8px rgb(var(--amber-rgb) / .8); }
.chat-peer-state.p-off { color: var(--faint); }
.chat-peer-state.p-off i { background: rgb(var(--line-rgb) / .8); }

/* Лента (макет): чужие слева, свои справа. Блок сообщения — лёгкий фон и тонкие
   линии сверху/снизу; толстая цветная полоска ТОЛЬКО по внешнему краю (у чужих
   слева серая, у своих справа циановая со свечением), к центру обводки нет.
   Кружки-инициалы: в личке нет вовсе, в группе — только у чужих. */
.chat-msgs { flex: 1 1 auto; min-height: 0; overflow-y: auto; padding: 16px var(--panel-px);
  display: flex; flex-direction: column; gap: 14px; }
.chat-day { display: flex; align-items: center; gap: 14px; padding: 4px 0; }
.chat-day i { flex: 1; height: 1px; background: var(--line-soft); }
.chat-day span { font-family: var(--font-mono); font-size: 9px; letter-spacing: .24em;
  text-transform: uppercase; color: var(--faint); white-space: nowrap; }
.chat-msg { display: flex; gap: 10px; align-items: flex-start; }
.chat-msg.is-mine { justify-content: flex-end; }
.chat-ava { flex: none; width: 28px; height: 28px; margin-top: 6px;
  display: flex; align-items: center; justify-content: center;
  clip-path: polygon(7px 0, 100% 0, 100% calc(100% - 7px), calc(100% - 7px) 100%, 0 100%, 0 7px);
  border: 1px solid rgb(var(--line-rgb) / .5); background: rgb(var(--deep-rgb) / .8);
  font-family: var(--font-mono); font-size: 9px; color: var(--text-2); }
/* Контур сообщения полный: тонкие полупрозрачные линии со всех сторон, толстая
   цветная полоска — по внешнему краю. Зазор до текста со стороны ЦЕНТРА — двойной
   (просьба владельца). */
.chat-bubble { min-width: 0; max-width: 88%; padding: 8px 24px 8px 12px;
  background: rgb(var(--line-rgb) / .07);
  border: 1px solid rgb(var(--line-rgb) / .12);
  border-left: 2px solid rgb(var(--line-rgb) / .6); }
.chat-msg.is-mine .chat-bubble { padding: 8px 12px 8px 24px;
  border: 1px solid rgb(var(--line-rgb) / .12);
  border-right: 2px solid rgb(var(--accent-rgb) / .8);
  background: rgb(var(--accent-rgb) / .05); }
.chat-meta { font-family: var(--font-mono); font-size: 9px; letter-spacing: .12em;
  text-transform: uppercase; color: var(--faint); margin-bottom: 3px; }
.chat-msg.is-mine .chat-meta { text-align: right; }
.chat-text { font-family: var(--font-mono); font-size: var(--fs-data); color: var(--text);
  white-space: pre-wrap; word-break: break-word; }
.chat-img { max-width: 100%; max-height: 220px; border-radius: 4px; display: block; margin: 4px 0; }
/* Плашка файла с размером (макет: имя слева, размер серым справа). */
.chat-file { display: flex; align-items: center; gap: 10px; margin-top: 4px;
  padding: 8px 10px; background: rgb(var(--deep-rgb) / .7);
  border: 1px solid rgb(var(--line-rgb) / .3); color: var(--text-2); }
.chat-file:hover { border-color: rgb(var(--accent-rgb) / .55); }
.chat-file svg { flex: none; color: var(--accent); }
.chat-file-title { font-family: var(--font-mono); font-size: 10px; letter-spacing: .08em;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.chat-file-size { margin-left: auto; flex: none; font-family: var(--font-mono);
  font-size: 9px; letter-spacing: .1em; color: var(--faint); }

/* Ввод: скрепка · поле · эмодзи · отправить — ОДНОЙ высоты (--ctl-h, замечание
   владельца: высоты были разные). Поле растёт только от переноса строки.
   margin-top:auto ПРИКЛЕИВАЕТ панель к низу: без него при пустой ленте она
   висела под сообщениями посреди окна (замечание владельца). */
.chat-form { flex: none; margin-top: auto; border-top: 1px solid var(--line);
  padding: 16px var(--panel-px) 18px; background: rgb(var(--deep-rgb) / .5); }
.chat-input-row { display: flex; gap: 8px; align-items: flex-end; }
.chat-input-row textarea { flex: 1 1 auto; resize: none; box-sizing: border-box;
  height: var(--ctl-h); min-height: var(--ctl-h); max-height: 120px; padding-top: 7px;
  padding-bottom: 7px; }
.chat-attach, .chat-emoji-btn { flex: none; display: flex; align-items: center; justify-content: center;
  box-sizing: border-box; width: var(--ctl-h); height: var(--ctl-h);
  border: 1px solid rgb(var(--line-rgb) / .25); background: rgb(var(--deep-rgb) / .8);
  color: var(--muted); cursor: pointer; font-size: 15px; }
.chat-attach:hover, .chat-emoji-btn:hover { color: var(--accent); border-color: rgb(var(--accent-rgb) / .5); }
.chat-send { flex: none; box-sizing: border-box; height: var(--ctl-h); }
.chat-emoji { display: flex; flex-wrap: wrap; gap: 2px; padding-top: 8px; }
.chat-emoji button { background: none; border: none; font-size: 17px; cursor: pointer; padding: 3px; }
.chat-file-name { padding-top: 6px; font-size: var(--fs-label); }

