/* =========================================================
   Дизайн-система «Графит»
   Все цвета — только через переменные. Тёмная тема меняет
   значения токенов, компоненты ниже её не касаются.
   ========================================================= */

:root {
  /* поверхности */
  --bg: #FCFCFD;
  --surface: #FFFFFF;
  --surface-2: #F4F4F6;
  --surface-3: #EBEBEF;
  /* блик, пробегающий по заглушкам на время загрузки */
  --skel-glare: rgba(255, 255, 255, .78);
  --border: #E4E4E9;
  --border-strong: #D3D4DB;

  /* текст */
  --text: #1C2024;
  --text-soft: #60646C;
  --text-faint: #8B8D98;

  /* акцент */
  --accent: #5B5BD6;
  --accent-hover: #5151CD;
  --accent-soft: #ECECFE;
  --accent-text: #4A47C4;
  --on-accent: #FFFFFF;

  /* состояния */
  --chat-pattern-opacity: .05;
  --green: #2C9A63;   --green-soft: #E9F9EE;
  --red: #DC3E44;     --red-soft: #FEEDEE;
  --amber: #A56400;   --amber-soft: #FFF5D8;

  /* сайдбар следует за темой */
  --sidebar: #FFFFFF;
  --sidebar-2: #EFEFF2;
  --sidebar-text: #60646C;
  --sidebar-text-strong: #1C2024;
  --sidebar-line: #E4E4E9;
  --toast-bg: #1C2024;

  /* чат */
  --chat-bg: #F1F1F3;
  --chat-bubble: #FFFFFF;

  /* Свой пузырь. Раньше он заливался акцентом целиком — на длинной переписке
     полоса насыщенного индиго тянет взгляд на себя сильнее самого текста,
     заказчик так и сказал: «фиолетовый бросается в глаза». Теперь, как в
     телеграме, это спокойная подложка на полтона от фона, а акцент остаётся
     кнопкам и ссылкам. Всё, что лежит внутри пузыря, красится этими токенами:
     хардкод белого возвращает прежнюю проблему при смене темы. */
  --chat-out: #E7F0FA;
  --chat-out-line: #D6E5F4;
  --chat-out-text: #1C2024;
  --chat-out-faint: #4C6E8A;      /* время и подписи: на светлой подложке нужен контраст */
  --chat-out-soft: rgba(47, 126, 196, .12);
  --chat-out-mark: #2F7EC4;      /* галочки, ссылки, полоса цитаты */
  --chat-out-btn: #2F7EC4;       /* кружок плеера */
  --chat-out-btn-text: #FFFFFF;
  --chat-out-wave: rgba(47, 126, 196, .32);
  --chat-out-wave-on: #2F7EC4;

  /* градиенты: знак, основные кнопки, аватары */
  --logo-from: #6E6ADE;
  --logo-to: #8E4EC6;
  --grad-accent: linear-gradient(135deg, #6260D4 0%, #7357C6 100%);
  --grad-accent-hover: linear-gradient(135deg, #5654CA 0%, #664CB9 100%);
  --grad-soft: linear-gradient(135deg, rgba(110, 106, 222, .13), rgba(142, 78, 198, .10));
  --grad-glow: radial-gradient(120% 60% at 50% 0%, rgba(110, 106, 222, .16), transparent 70%);

  /* стекло: только для всплывающих слоёв, см. комментарий у .topbar */
  --glass: rgba(255, 255, 255, .72);
  --glass-line: rgba(255, 255, 255, .6);
  --glass-blur: saturate(180%) blur(14px);
  --scrim: rgba(12, 13, 16, .42);

  /* геометрия */
  --r-lg: 10px;
  --r: 8px;
  --r-sm: 6px;
  /* Общая высота шапок трёх колонок чата: списка, переписки и «О чате».
     Стоят рядом, поэтому верхняя граница должна совпадать до пикселя. */
  --chat-head-h: 56px;
  --shadow: 0 1px 2px rgba(16, 20, 28, .05);
  --shadow-pop: 0 8px 28px rgba(16, 20, 28, .13), 0 2px 6px rgba(16, 20, 28, .07);
  --ring: 0 0 0 3px rgba(91, 91, 214, .22);

  --font: system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
  --speed: 120ms;
  --ease: cubic-bezier(.22, 1, .36, 1);

  /* Системные элементы (календарь, выпадашки) рисуются браузером —
     эта строка заставляет их следовать теме. */
  color-scheme: light;
}

[data-theme="dark"] { color-scheme: dark; }

[data-theme="dark"] {
  --bg: #111114;
  --surface: #18191B;
  --surface-2: #212225;
  --surface-3: #292A2E;
  --skel-glare: rgba(255, 255, 255, .06);
  --border: #2A2B2F;
  --border-strong: #3B3C41;

  --text: #EDEEF0;
  --text-soft: #B0B4BA;
  --text-faint: #7E8186;

  --accent: #6E6ADE;
  --accent-hover: #7D79E8;
  --accent-soft: #262358;
  --accent-text: #A5A2F3;
  --on-accent: #FFFFFF;

  --chat-pattern-opacity: .085;
  --green: #3DD68C;   --green-soft: #10291D;
  --red: #FF6369;     --red-soft: #341417;
  --amber: #FFC53D;   --amber-soft: #33270C;

  --sidebar: #0C0D10;
  --sidebar-2: #1D1E23;
  --sidebar-text: #8B8D98;
  --sidebar-text-strong: #FFFFFF;
  --sidebar-line: rgba(255, 255, 255, .08);
  --toast-bg: #2A2B2F;

  --glass: rgba(20, 21, 24, .72);
  --glass-line: rgba(255, 255, 255, .07);
  --scrim: rgba(0, 0, 0, .6);

  --chat-bg: #0E0E11;
  --chat-bubble: #1D1E22;

  /* Тёмная тема повторяет телеграмовскую: своё сообщение — приглушённый
     синий, чужое остаётся графитовым. */
  --chat-out: #2B5278;
  --chat-out-line: #2B5278;
  --chat-out-text: #EDEEF0;
  --chat-out-faint: rgba(224, 236, 248, .62);
  --chat-out-soft: rgba(255, 255, 255, .13);
  --chat-out-mark: #8FD3FF;
  --chat-out-btn: #E9F2FB;
  --chat-out-btn-text: #2B5278;
  --chat-out-wave: rgba(255, 255, 255, .36);
  --chat-out-wave-on: #FFFFFF;

  --shadow: 0 1px 2px rgba(0, 0, 0, .4);
  --shadow-pop: 0 10px 34px rgba(0, 0, 0, .55);
  --ring: 0 0 0 3px rgba(110, 106, 222, .3);
}

/* Метка версии стилей. Нужна не для красоты: у приложения с домашнего экрана
   нет способа показать, какой файл оно на самом деле применило, а по внешнему
   виду это выясняется спорами. Страница /diag.html печатает эту строку. */
:root { --build-marker: "2026-08-19-23:05"; }

* { box-sizing: border-box; }

html, body {
  margin: 0;
  height: 100%;
  font-family: var(--font);
  font-size: 14px;
  line-height: 1.45;
  color: var(--text);
  background: var(--bg);
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}

/* Прокручивается только рабочая область — сам документ неподвижен.
   На айфоне это единственный способ удержать шапку и нижнюю панель на месте:
   пока страница целиком способна сдвинуться хоть на пиксель, iOS тащит вместе
   с ней и то, что прибито к краям экрана. Резинка в конце списка, жест «назад»,
   открытие клавиатуры — каждый раз плашки уезжали вместе с содержимым.
   position: fixed на body — только на телефоне: на компьютере страница и так
   никуда не девается, а фиксация мешала бы браузерным дополнениям. */
html, body { overflow: hidden; overscroll-behavior: none; }
@media (max-width: 820px) {
  body { position: fixed; inset: 0; width: 100%; }
}

button, input, select, textarea { font: inherit; color: inherit; }
a { color: var(--accent-text); text-decoration: none; }
a:hover { text-decoration: underline; }
.hidden { display: none !important; }

/* Интерфейс мышью не выделяется: подписи полей, заголовки, вкладки, значения
   служебных полей — это не текст, а органы управления, и текстовый курсор над
   ними только мешает.
   Таблицы сюда тоже входят, хотя в них и данные: строка списка — это ссылка,
   по ней кликают, а не выделяют. Мигающая каретка поверх кликабельной строки
   сбивает с толку сильнее, чем помогает возможность выделить текст.
   Побочный эффект намеренный: над невыделяемым текстом браузер показывает
   обычную стрелку, а не «палку», и режим каретки (F7) в неё не встаёт. */
body { user-select: none; -webkit-user-select: none; }

/* Выделять можно там, где действительно копируют: поля ввода, переписка
   и комментарии в ленте. Не вся лента: её заголовки — системные формулировки
   вроде «Стадия: Замер → Согласование КП», в буфере они не нужны. */
input, textarea, [contenteditable="true"],
.tl-item__note, .msg__text, .msg__quote-text, .selectable {
  user-select: text; -webkit-user-select: text;
}

::selection { background: var(--accent-soft); color: var(--accent-text); }

/* Полос прокрутки не видно нигде — прокрутка при этом работает как обычно.
   В колонках канбана полоса вставала поверх карточек и читалась как элемент
   вёрстки, а не как индикатор; сколько карточек в стадии, и так написано
   в шапке колонки. Оставлять её в одних местах и прятать в других — хуже
   всего: разнобой выглядит недоделкой. */
* { scrollbar-width: none; -ms-overflow-style: none; }
*::-webkit-scrollbar { width: 0; height: 0; display: none; }

/* ---------- вход ---------- */

.login {
  /* Документ теперь не прокручивается, поэтому экран входа прокручивает себя
     сам: на маленьком экране с открытой клавиатурой карточка перестаёт
     помещаться, и без этого до кнопки «Войти» было бы не добраться. */
  height: 100%;
  overflow-y: auto;
  display: grid;
  place-items: center;
  background: var(--bg);
  padding: 24px;
}

.login__card {
  width: min(384px, 100%);
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 14px;
  padding: 34px 30px;
  box-shadow: var(--shadow-pop);
  display: grid;
  gap: 16px;
}

.login__brand { display: flex; align-items: center; gap: 15px; margin-bottom: 6px; }
.login__brand > span:first-child { color: var(--accent); display: grid; }
.login__name { display: flex; align-items: flex-end; gap: 2px; color: var(--text); }
.login__title { margin: 0; font-size: 19px; font-weight: 620; letter-spacing: -.2px; }
.login__sub { margin: -10px 0 4px; font-size: 13px; color: var(--text-soft); }
.login__error {
  background: var(--red-soft); color: var(--red);
  border-radius: var(--r-sm); padding: 9px 12px; font-size: 13px;
}

/* ---------- каркас ---------- */

.app { display: flex; height: 100%; }
/* dvh — про Safari без режима приложения: там часть экрана занимают панели
   браузера, которые то появляются, то исчезают, и 100% отмеряется в том числе
   по невидимой полосе. С ярлыка на домашнем экране разницы нет. */
@supports (height: 100dvh) { .app { height: 100dvh; } }

/* Масштаб интерфейса.
   zoom, а не font-size: размеры в системе заданы в пикселях, и шрифт сам по
   себе потянул бы за собой только текст, оставив отступы прежними.
   Масштабируем содержимое, а не .app: шапка и панель внизу привязаны к краям
   экрана и к безопасным зонам айфона, а zoom умножал бы и вырез под камеру,
   и полосу жестов — верх уезжал под чёлку, снизу росла пустота.

   База разная. За монитором плотность — смысл этой вёрстки: при 1.15 в списке
   дел на 1440×900 помещается 15 строк вместо 18. На телефоне та же плотность
   оказалась мелковата, поэтому там комфортный размер сделан обычным.
   Тумблер «Крупный шрифт» добавляет ещё шаг на любом устройстве. */
:root {
  --ui-scale: 1;
  /* Полоса жестов у айфона занимает 34 пикселя, но держать под панелью столько
     же пустого фона незачем: сама полоса тонкая, а тёмная плашка в 42 пикселя
     под подписями читается как ошибка вёрстки. Оставляем клиренс, срезая 20.
     Значение одно на всех, чтобы высота панели и место под неё не разъехались. */
  --safe-bottom: max(6px, calc(env(safe-area-inset-bottom) - 20px));
}
:root.is-large-ui { --ui-scale: 1.15; }
@media (max-width: 820px) {
  :root { --ui-scale: 1.15; }
  :root.is-large-ui { --ui-scale: 1.3; }
}
.view { zoom: var(--ui-scale, 1); }

/* Ширина подобрана по самому широкому содержимому, а не на глаз: подпись
   «Рабочий стол» занимает 86px, строка с именем в подвале — около 148px
   вместе с аватаркой. При 180px и внутренних отступах остаётся 160px —
   ничего не обрезается, а рабочей области прибавилось 32 пикселя. */
.sidebar {
  width: 180px;
  flex: 0 0 180px;
  background: var(--sidebar);
  color: var(--sidebar-text);
  display: flex;
  flex-direction: column;
  padding: 12px 10px;
  gap: 6px;
  border-right: 1px solid var(--sidebar-line);
  overflow: hidden;
  /* Ширина — свойство раскладки: каждый её кадр пересчитывает страницу целиком,
     и на чате это заметно. Совсем убрать анимацию — потерять понятность
     (панель должна «сложиться», а не моргнуть), поэтому держим её короткой. */
  transition: width 180ms var(--ease), flex-basis 180ms var(--ease);
}

/* Бургер стоит первым и по той же вертикали, что иконки разделов —
   при сворачивании меню он остаётся ровно на месте. */
.sidebar__brand {
  display: flex; align-items: center; gap: 9px;
  padding: 0 0 12px; min-height: 40px;
}
/* Шрифты для написания названия. Лежат рядом, внешних загрузок нет. */
@font-face {
  font-family: "Manrope"; src: url("/assets/fonts/manrope-800-cyrillic.woff2") format("woff2");
  font-weight: 800; font-display: swap;
}
@font-face {
  font-family: "Unbounded"; src: url("/assets/fonts/unbounded-700-cyrillic.woff2") format("woff2");
  font-weight: 700; font-display: swap;
}
@font-face {
  font-family: "Onest"; src: url("/assets/fonts/onest-800-cyrillic.woff2") format("woff2");
  /* block, а не swap: этим шрифтом набрано название системы, и мелькание
     системного начертания вместо логотипа заметнее, чем задержка в 6 КБ. */
  font-weight: 800; font-display: block;
}
@font-face {
  font-family: "Rubik"; src: url("/assets/fonts/rubik-700-cyrillic.woff2") format("woff2");
  font-weight: 700; font-display: swap;
}

/* ---------- фирменный знак «Флоу» ----------
   Геометрия и правила — из дизайн-системы. Внутри знака запрещены
   градиенты, тени и обводки: только сплошная заливка. */

.flow-mark { display: block; color: currentColor; }
.flow-tile {
  display: inline-flex; align-items: center; justify-content: center;
  background: #5B5BD6; color: #FFFFFF; flex: 0 0 auto;
}

/* Появление: плашки выходят снизу по очереди. */
@keyframes flow-in { from { transform: translateY(16px); } to { transform: translateY(0); } }
.flow-mark--in rect { animation: flow-in 720ms cubic-bezier(.22, .7, .2, 1) both; }
.flow-mark--in rect:nth-child(2) { animation-delay: 120ms; }
.flow-mark--in rect:nth-child(3) { animation-delay: 240ms; }

/* Наведение: плашки ходят вверх-вниз со сдвигом фаз, как шатуны — неспешно,
   чтобы это читалось как «система работает», а не как требование внимания.
   Сдвиг небольшой: знак стоит рядом с названием и не должен его задевать. */
@keyframes flow-piston { 0%, 100% { transform: translateY(0); } 50% { transform: translateY(-5px); } }
.sidebar__brand:hover .flow-mark rect,
.login__brand:hover .flow-mark rect,
.flow-mark--piston rect {
  animation: flow-piston 2.2s ease-in-out infinite;
}
.sidebar__brand:hover .flow-mark rect:nth-child(2),
.login__brand:hover .flow-mark rect:nth-child(2),
.flow-mark--piston rect:nth-child(2) { animation-delay: .28s; }
.sidebar__brand:hover .flow-mark rect:nth-child(3),
.login__brand:hover .flow-mark rect:nth-child(3),
.flow-mark--piston rect:nth-child(3) { animation-delay: .56s; }

/* Ожидание: бесконечный подъём со сдвигом фаз — вместо крутилки. */
@keyframes flow-loop { 0%, 100% { transform: translateY(0); } 45% { transform: translateY(-7px); } }
.flow-mark--loading rect { animation: flow-loop 1.6s ease-in-out infinite; }
.flow-mark--loading rect:nth-child(2) { animation-delay: 130ms; }
.flow-mark--loading rect:nth-child(3) { animation-delay: 260ms; }

@media (prefers-reduced-motion: reduce) {
  .flow-mark--in rect, .flow-mark--loading rect, .flow-mark--piston rect,
  .splash__mark rect, .splash__word, .splash__sub,
  .sidebar__brand:hover .flow-mark rect, .login__brand:hover .flow-mark rect { animation: none; }
  .splash { transition: none; }
}

/* Локап: знак, слово, точка. Знак и точка — фирменным индиго. */
.sidebar__brand { gap: 6px; }
.sidebar__logo { display: grid; place-items: center; flex: 0 0 auto; color: var(--accent); }
.sidebar__name {
  display: flex; align-items: flex-end; gap: 1px;
  color: var(--sidebar-text-strong);
}
/* Название системы. Метрики Onest: подъём 970, спуск −305 при кегле 1000,
   а контур «Флоу» занимает 983 единицы. При line-height .983 блок получается
   ровно по высоте слова, но само слово садится на .087em ниже — сдвигом
   возвращаем его в границы блока, чтобы знак, слово и точка стояли по одной
   линии, как раньше со SVG.
   Отрицательный margin-right гасит трекинг, который CSS добавляет и после
   последней буквы: без него точка отъезжала вправо. */
.wordmark {
  display: block;
  font-family: "Onest", var(--font);
  font-weight: 800;
  line-height: .983;
  letter-spacing: -.02em;
  margin-right: -.02em;
  position: relative;
  top: -.087em;
  white-space: nowrap;
}

.brand-dot {
  width: 5px; height: 5px; border-radius: 50%;
  background: var(--accent); flex: 0 0 5px; margin-bottom: 2px;
}
.brand-dot--lg { width: 7px; height: 7px; flex-basis: 7px; margin-bottom: 3px; }
.sidebar__toggle {
  border: 0; background: none; cursor: pointer;
  width: 32px; height: 32px; flex: 0 0 32px; margin-left: 0;
  border-radius: var(--r-sm); color: var(--sidebar-text);
  display: grid; place-items: center;
  transition: background-color var(--speed), color var(--speed);
}
.sidebar__toggle:hover { background: var(--sidebar-2); color: var(--sidebar-text-strong); }

.nav { display: grid; gap: 1px; flex: 1; align-content: start; }

.nav__item {
  display: flex; align-items: center; gap: 10px;
  padding: 8px 10px;
  border: 0; background: none; cursor: pointer;
  border-radius: var(--r-sm);
  color: var(--sidebar-text); font-size: 13.5px; font-weight: 500;
  text-align: left; width: 100%; position: relative;
  transition: background-color var(--speed), color var(--speed);
}
.nav__item:hover { background: var(--sidebar-2); color: var(--sidebar-text-strong); }
.nav__item.is-active { background: var(--sidebar-2); color: var(--sidebar-text-strong); font-weight: 550; }
.nav__item.is-active::before {
  content: ""; position: absolute; left: 0; top: 8px; bottom: 8px;
  width: 2px; border-radius: 2px; background: var(--accent);
}
.nav__icon { width: 18px; display: grid; place-items: center; }
.nav__label { white-space: nowrap; overflow: hidden; }
.nav__badge {
  margin-left: auto; background: var(--accent); color: #fff;
  border-radius: 20px; padding: 1px 7px; font-size: 11px; font-weight: 600;
}

/* ---- свёрнутое меню ----
   Иконки остаются на своих местах, уезжают только подписи: ширина 58px
   подобрана так, что центр иконки совпадает с центром свёрнутой панели.
   Поэтому при сворачивании ничего не прыгает. */

/* Отступы слева и справа одинаковые, иначе подсветка пунктов съезжает.
   По центру панели бургер выводится своим полем, а не отступом колонки. */
.sidebar.is-collapsed { width: 58px; flex: 0 0 58px; }
.sidebar.is-collapsed .sidebar__toggle { margin-left: 3px; }

.sidebar__logo, .sidebar__name, .nav__label, .user-chip__body {
  max-width: 160px; overflow: hidden;
  transition: opacity 140ms var(--ease), max-width 280ms var(--ease);
}
.sidebar.is-collapsed .sidebar__logo,
.sidebar.is-collapsed .sidebar__name,
.sidebar.is-collapsed .nav__label,
.sidebar.is-collapsed .user-chip__body { opacity: 0; max-width: 0; pointer-events: none; }

/* Отступы схлопываются вместе с подписями, поэтому иконки остаются на месте. */
.nav__item, .user-chip, .sidebar__brand { transition: gap 280ms var(--ease); }
.sidebar.is-collapsed .nav__item,
.sidebar.is-collapsed .user-chip,
.sidebar.is-collapsed .sidebar__brand { gap: 0; }

.sidebar.is-collapsed .nav__badge {
  position: absolute; top: 4px; right: 6px; margin: 0;
  min-width: 8px; width: 8px; height: 8px; padding: 0; font-size: 0; border-radius: 50%;
  transition: none;
}

/* Аватар (30px) шире, чем поле кнопки при свёрнутом меню, и с обычными
   отступами вылезает вправо — подсветка кажется съехавшей влево от кружка.
   Отступы 4px оставляют ровно ширину аватара, и он встаёт по центру. */
.sidebar.is-collapsed .user-chip { justify-content: center; padding: 7px 4px; }

.sidebar__foot { border-top: 1px solid var(--sidebar-line); padding-top: 8px; }

.user-chip {
  display: flex; align-items: center; gap: 10px; width: 100%;
  background: none; border: 0; cursor: pointer; padding: 7px 8px;
  border-radius: var(--r-sm); color: inherit; text-align: left;
  transition: background-color var(--speed);
}
.user-chip:hover { background: var(--sidebar-2); }
.user-chip__body { min-width: 0; }
.user-chip__name {
  display: block; color: var(--sidebar-text-strong); font-size: 13px; font-weight: 550;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.user-chip__role { display: block; color: var(--sidebar-text); font-size: 11px; }

.main { flex: 1; min-width: 0; min-height: 0; display: flex; flex-direction: column;
        background: var(--bg); position: relative; overflow: hidden; }

/* Шапка лежит поверх контента, содержимое проезжает под ней — только так
   размытие вообще имеет смысл.

   Размытие отсюда снималось при разборе просадок прокрутки и возвращено:
   виноват был не оно, а монитор на 60 Гц, на котором смотрели. Замер на
   ленте чата со стеклом: медиана кадра 3,4 мс, длинных задач ноль. */
/* Размытие под шапкой пересчитывается всякий раз, когда меняется картинка
   за ней. Пока меню выезжает, за шапкой меняется всё и каждый кадр — это
   самая дорогая операция в анимации. На эти 160 мс подменяем стекло сплошной
   заливкой: разницу на глаз не видно, а кадры перестают проседать. */
html.nav-moving .topbar,
html.nav-moving .chat__date {
  -webkit-backdrop-filter: none;
  backdrop-filter: none;
  background: var(--surface);
}

.topbar {
  position: absolute; top: 0; left: 0; right: 0; z-index: 20;
  height: 54px;
  background: var(--glass);
  -webkit-backdrop-filter: var(--glass-blur);
  backdrop-filter: var(--glass-blur);
  border-bottom: 1px solid var(--glass-line);
  display: flex; align-items: center; gap: 12px;
  padding: 0 16px;
}
/* Бургер в шапке нужен только на телефоне — на десктопе меню сворачивает
   кнопка внутри самого меню. Селектор с .topbar, чтобы перебить .icon-btn. */
.topbar > .topbar__burger { display: none; }
.topbar__search { position: relative; flex: 1; max-width: 440px; }
.topbar__actions { margin-left: auto; display: flex; gap: 8px; }

/* Колонка контента: flex вместо расчётов по 100vh — высоты канбана и чата
   больше не завязаны на магические числа. */
.view {
  flex: 1; overflow: auto; min-height: 0;
  /* contain обрывает передачу прокрутки наверх: докрутив список до конца,
     палец больше не начинает тянуть за собой страницу. */
  overscroll-behavior: contain;
  -webkit-overflow-scrolling: touch;
  padding: 74px 22px 44px;
  display: flex; flex-direction: column;
}

/* ---------- формы ---------- */

.input {
  width: 100%;
  padding: 7px 11px;
  border: 1px solid var(--border-strong);
  border-radius: var(--r-sm);
  background: var(--surface);
  color: var(--text);
  outline: none;
  transition: border-color var(--speed), box-shadow var(--speed);
}
.input::placeholder { color: var(--text-faint); }
.input:hover { border-color: var(--text-faint); }
.input:focus { border-color: var(--accent); box-shadow: var(--ring); }
.input--search { background: var(--surface-2); border-color: transparent; }
.input--search:focus { background: var(--surface); }
/* Своя стрелка: браузерная выглядит по-разному в каждой системе и не
   попадает в тему. Иконка — та же, что в наборе Tabler. */
select.input {
  cursor: pointer;
  appearance: none; -webkit-appearance: none;
  padding-right: 32px;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%238B8D98' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M6 9l6 6l6 -6'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right 9px center;
  background-size: 16px 16px;
}
select.input::-ms-expand { display: none; }

/* Кнопка календаря во встроенном поле даты — под цвет текста. */
[data-theme="dark"] input[type="datetime-local"]::-webkit-calendar-picker-indicator,
[data-theme="dark"] input[type="date"]::-webkit-calendar-picker-indicator { filter: invert(1) opacity(.65); }
input[type="datetime-local"]::-webkit-calendar-picker-indicator,
input[type="date"]::-webkit-calendar-picker-indicator { cursor: pointer; opacity: .6; }
input[type="datetime-local"]::-webkit-calendar-picker-indicator:hover,
input[type="date"]::-webkit-calendar-picker-indicator:hover { opacity: 1; }

/* Поля с текстом растут сами — ручного растягивания нет. */
textarea.input { resize: none; min-height: 38px; overflow-y: hidden; line-height: 1.45; }

.field { display: grid; gap: 5px; }
.field__label { font-size: 12px; color: var(--text-soft); font-weight: 500; }
.form-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 13px; }
.form-grid .span-2 { grid-column: 1 / -1; }

.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: 6px;
  padding: 7px 13px;
  border-radius: var(--r-sm);
  border: 1px solid var(--border-strong);
  background: var(--surface);
  color: var(--text);
  cursor: pointer;
  font-weight: 520;
  white-space: nowrap;
  transition: background-color var(--speed), border-color var(--speed), color var(--speed);
}
.btn:hover { background: var(--surface-2); border-color: var(--text-faint); }
.btn:focus-visible { outline: none; box-shadow: var(--ring); }
.btn--primary {
  background: var(--grad-accent); border-color: transparent; color: var(--on-accent);
  box-shadow: 0 1px 2px rgba(70, 60, 180, .25);
}
.btn--primary:hover { background: var(--grad-accent-hover); border-color: transparent; }
.btn--ghost { border-color: transparent; background: var(--surface-2); }
.btn--ghost:hover { background: var(--surface-3); border-color: transparent; }
.btn--danger { color: var(--red); border-color: transparent; background: var(--red-soft); }
.btn--danger:hover { background: var(--red-soft); border-color: var(--red); }
.btn--block { width: 100%; }
.btn--sm { padding: 4px 10px; font-size: 13px; }
.btn:disabled { opacity: .5; cursor: default; }

.icon-btn {
  border: 0; background: none; cursor: pointer; padding: 6px;
  border-radius: var(--r-sm); color: var(--text-faint);
  display: inline-grid; place-items: center;
  transition: background-color var(--speed), color var(--speed);
}
.icon-btn:hover { background: var(--surface-2); color: var(--text); }
.icon-btn:focus-visible { outline: none; box-shadow: var(--ring); }
/* Удаление красным только под мышью: в спокойном состоянии корзина не должна
   притягивать взгляд сильнее, чем «выполнено» рядом. */
.icon-btn--danger:hover { background: var(--red-soft); color: var(--red); }

.icon { display: block; flex: 0 0 auto; }

/* Тумблер темы */
.switch { display: flex; align-items: center; justify-content: space-between; gap: 12px; }
.switch__track {
  width: 42px; height: 24px; border-radius: 20px; background: var(--surface-3);
  border: 1px solid var(--border-strong); position: relative; cursor: pointer; flex: 0 0 42px;
  transition: background-color var(--speed), border-color var(--speed);
}
.switch__track.is-on { background: var(--accent); border-color: var(--accent); }
.switch__knob {
  position: absolute; top: 2px; left: 2px; width: 18px; height: 18px; border-radius: 50%;
  background: #fff; box-shadow: 0 1px 2px rgba(0, 0, 0, .25);
  transition: transform var(--speed);
}
.switch__track.is-on .switch__knob { transform: translateX(18px); }

/* ---------- общие блоки ---------- */

.page-head { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; margin-bottom: 18px; }
.page-title { margin: 0; font-size: 21px; font-weight: 620; letter-spacing: -.35px; }
.page-head__actions { margin-left: auto; display: flex; gap: 8px; flex-wrap: wrap; }

.card {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--r);
  box-shadow: var(--shadow);
}
.card__head {
  padding: 12px 16px; border-bottom: 1px solid var(--border);
  display: flex; align-items: center; gap: 10px;
}
.card__title { font-weight: 580; font-size: 13.5px; }
.card-icon {
  display: grid; place-items: center; width: 26px; height: 26px; flex: 0 0 26px;
  border-radius: var(--r-sm); background: var(--accent-soft); color: var(--accent-text);
}
.card__body { padding: 16px; }

.tiles { display: grid; grid-template-columns: repeat(auto-fit, minmax(172px, 1fr)); gap: 12px; margin-bottom: 18px; }
.tile {
  background: var(--surface); border: 1px solid var(--border); border-radius: var(--r);
  padding: 14px 16px; box-shadow: var(--shadow);
}
.tile__label { font-size: 12px; color: var(--text-soft); }
.tile__value { font-size: 24px; font-weight: 620; margin-top: 3px; letter-spacing: -.6px; }
.tile__hint { font-size: 12px; color: var(--text-faint); margin-top: 2px; }
.tile--alert .tile__value { color: var(--red); }
.tile--good .tile__value { color: var(--green); }

.cols { display: grid; grid-template-columns: 1.4fr 1fr; gap: 16px; align-items: start; }
/* Элемент грида по умолчанию не может стать уже своего содержимого
   (min-width: auto). На узком экране из-за этого колонка распирала страницу,
   хотя сама сетка уже была ограничена. Обнуляем — переносы сделают остальное. */
.cols > *, .entity > *, .stack > * { min-width: 0; }
.stack { grid-template-columns: minmax(0, 1fr); }

.table { width: 100%; border-collapse: collapse; }
.table th {
  text-align: left; font-size: 11.5px; color: var(--text-faint); font-weight: 550;
  letter-spacing: .3px; text-transform: uppercase;
  padding: 9px 14px; border-bottom: 1px solid var(--border); white-space: nowrap;
}
.table td { padding: 11px 14px; border-bottom: 1px solid var(--border); vertical-align: middle; }
.table tbody tr:last-child td { border-bottom: 0; }
.table tbody tr { cursor: pointer; transition: background-color var(--speed); }
.table tbody tr:hover { background: var(--surface-2); }
.table td.num, .table th.num { text-align: right; white-space: nowrap; }

/* Заголовок-переключатель сортировки. Стрелка стоит всегда — иначе ширина
   столбца прыгает при переключении и таблица «дёргается». */
.th-sort { cursor: pointer; user-select: none; white-space: nowrap; }
.th-sort:hover { color: var(--text); }
.th-sort__arrow { margin-left: 5px; opacity: .35; font-size: 11px; }
.th-sort.is-sorted { color: var(--text); }
.th-sort.is-sorted .th-sort__arrow { opacity: 1; color: var(--accent-text); }
.table tfoot td { padding: 11px 14px; border-top: 1px solid var(--border); }

.avatar {
  /* aspect-ratio и flex-shrink обязательны: в узкой строке — в шапке чата,
     в списке участников — flex сжимал аватар по ширине, оставляя высоту,
     и кружок превращался в вертикальный овал. */
  width: 30px; height: 30px; flex: 0 0 30px; flex-shrink: 0; aspect-ratio: 1;
  border-radius: 50%; display: inline-grid; place-items: center;
  color: #fff; font-size: 11px; font-weight: 620; letter-spacing: .2px;
  background: var(--accent);
}
.avatar--sm { width: 24px; height: 24px; flex-basis: 24px; font-size: 10px; }
.avatar--lg { width: 40px; height: 40px; flex-basis: 40px; font-size: 14px; }
.avatar-row { display: inline-flex; align-items: center; gap: 8px; }

.badge {
  display: inline-flex; align-items: center; gap: 5px;
  padding: 2px 9px; border-radius: 20px; font-size: 12px; font-weight: 520;
  background: var(--surface-2); color: var(--text-soft); border: 1px solid var(--border);
}
.badge--dot::before { content: ""; width: 6px; height: 6px; border-radius: 50%; background: currentColor; }
.badge--red { background: var(--red-soft); color: var(--red); border-color: transparent; }
.badge--green { background: var(--green-soft); color: var(--green); border-color: transparent; }
.badge--amber { background: var(--amber-soft); color: var(--amber); border-color: transparent; }

/* Кнопка учёта времени. В спокойном виде — обычная, на работе — зелёная:
   это состояние, а не действие, и спорить за внимание с «+ Сделка» ей незачем. */
.btn--work.is-on { background: var(--green-soft); color: var(--green); border-color: transparent; }
/* Пауза и закрытый день: цифра на кнопке остаётся, но зелёного «идёт работа»
   больше нет — иначе не отличить «на работе два часа» от «сегодня отработал два». */
.btn--work.is-paused { background: var(--surface-2); color: var(--text-soft); border-color: transparent; }
/* Синий — контуром: срок на этой неделе заметен, но не спорит за внимание
   с просрочкой и сегодняшними делами. */
.badge--blue { background: transparent; color: var(--accent-text); border-color: var(--accent); }
.badge--grey { background: var(--surface-2); color: var(--text-faint); border-color: transparent; }

/* Лента переключателей раздела. На широком экране просто строка, на узком
   прокручивается вбок — см. медиазапрос ниже. */
.filter-bar { gap: 10px; margin-bottom: 12px; }
/* Срок, который переносится кликом. Без подчёркивания: бейдж и так читается
   как элемент управления, а пунктир под цветной плашкой выглядел мусором.
   Что он кликабельный, показывает курсор и подсветка под мышью. */
.btn__narrow { display: none; }
.badge--due { cursor: pointer; transition: filter var(--speed); }
.badge--due:hover { filter: brightness(1.14); }

.muted { color: var(--text-soft); }
.faint { color: var(--text-faint); }
.small { font-size: 12px; }
.nowrap { white-space: nowrap; }
.right { text-align: right; }
.empty { padding: 38px 16px; text-align: center; color: var(--text-faint); }
.row { display: flex; align-items: center; gap: 8px; }
.stack { display: grid; gap: 12px; }
.spacer { flex: 1; }

/* ---------- поиск ---------- */

/* ---------- всплывающее меню ---------- */

.popover {
  position: fixed; z-index: 95; min-width: 190px; padding: 4px;
  background: var(--surface); border: 1px solid var(--border);
  border-radius: var(--r); box-shadow: var(--shadow-pop);
}
.popover__item {
  display: flex; align-items: center; gap: 9px; width: 100%;
  padding: 8px 10px; border: 0; background: none; cursor: pointer;
  border-radius: var(--r-sm); text-align: left; font-size: 13px; color: var(--text);
}
.popover__item:hover { background: var(--surface-2); }
.popover__item.is-current { color: var(--accent-text); font-weight: 550; }
.popover__item.is-danger { color: var(--red); }
.popover__item.is-danger:hover { background: var(--red-soft); }
.popover__item.has-divider { margin-top: 5px; padding-top: 9px; border-top: 1px solid var(--border); border-radius: 0 0 var(--r-sm) var(--r-sm); }

/* Панель вместо меню: внутри форма, поэтому ширина больше и есть отступы. */
.popover--panel { min-width: 250px; padding: 6px; }
.due-pick .popover__item { justify-content: space-between; }
.due-pick__exact {
  display: flex; gap: 6px; align-items: center;
  margin-top: 5px; padding-top: 8px; border-top: 1px solid var(--border);
}
.due-pick__exact .input { flex: 1; min-width: 0; }

/* ---------- пикер сотрудников и объектов ---------- */

.picker { display: flex; flex-direction: column; gap: 6px; min-width: 264px; }
.picker__list { max-height: 268px; overflow-y: auto; display: flex; flex-direction: column; }
.picker__row {
  display: flex; align-items: center; gap: 8px; width: 100%;
  padding: 6px 8px; border: 0; background: none; cursor: pointer;
  border-radius: var(--r-sm); text-align: left; font-size: 13px; color: var(--text);
}
.picker__row:hover { background: var(--surface-2); }
.picker__row.is-on { background: var(--accent-soft); color: var(--accent-text); }
.picker__name { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.picker__foot {
  display: flex; align-items: center; gap: 8px;
  padding-top: 7px; border-top: 1px solid var(--border);
}

/* Поле с людьми: чипсы с аватарками, всё поле — кнопка открытия пикера. */
.people-field {
  display: flex; flex-wrap: wrap; gap: 5px; align-items: center;
  cursor: pointer; border-radius: var(--r-sm);
  margin: -3px -5px; padding: 3px 5px;
  transition: background-color var(--speed);
}
.people-field:hover { background: var(--surface-2); }
.people-field.is-locked { cursor: default; margin: 0; padding: 0; }
.people-field.is-locked:hover { background: none; }
.people-chip { display: inline-flex; align-items: center; gap: 6px; }

/* ---------- контактные лица ---------- */

.contact { display: grid; grid-template-columns: 1fr auto; gap: 4px 8px; align-items: start; }
.contact + .contact { border-top: 1px solid var(--border); padding-top: 10px; margin-top: 10px; }
.contact__name { display: flex; align-items: center; gap: 7px; flex-wrap: wrap; font-weight: 520; }
.contact__meta { grid-column: 1 / -1; font-size: 12px; color: var(--text-faint); line-height: 1.5; }
.contact__actions { display: flex; gap: 2px; }

/* ---------- поиск в шапке ---------- */

.finder { position: relative; display: flex; align-items: center; }
.finder__btn { flex: 0 0 auto; }

/* Строка раскрывается из иконки, а не открывает отдельное окно. */
.finder__input {
  width: 0; padding-left: 0; padding-right: 0;
  border-color: transparent; background: transparent;
  opacity: 0; pointer-events: none;
  transition: width 220ms var(--ease), opacity 160ms var(--ease),
              padding 220ms var(--ease), background-color var(--speed);
}
.finder.is-open .finder__input {
  width: min(420px, 42vw);
  padding-left: 11px; padding-right: 11px;
  margin-left: 8px;
  background: var(--surface-2); border-color: var(--border-strong);
  opacity: 1; pointer-events: auto;
}
.finder.is-open .finder__btn { color: var(--accent-text); }

/* Подсказки поиска — непрозрачные: сквозь них не должен просвечивать текст страницы. */
.finder__drop {
  position: absolute; top: calc(100% + 8px); left: 38px; z-index: 45;
  width: min(460px, 60vw); max-height: 62vh; overflow: auto; padding: 6px;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--r); box-shadow: var(--shadow-pop);
}

.search-list { max-height: 52vh; overflow: auto; margin: 0 -6px; }
.search-group + .search-group { margin-top: 10px; }
.search-group__title {
  font-size: 11.5px; text-transform: uppercase; letter-spacing: .4px;
  color: var(--text-faint); font-weight: 550; padding: 4px 10px;
}
.search-item { padding: 8px 10px; cursor: pointer; border-radius: var(--r-sm); }
.search-item:hover { background: var(--surface-2); }
.search-item__title { font-weight: 520; }

/* ---------- канбан ---------- */

/* ---------- переключатель направлений ---------- */

.pipelines {
  display: flex; gap: 4px; flex-wrap: wrap;
  margin: -6px 0 14px;
  padding: 3px; border-radius: var(--r);
  background: var(--surface-2); align-self: flex-start;
}
.pipeline-tab {
  border: 0; background: none; cursor: pointer;
  display: inline-flex; align-items: center; gap: 6px;
  padding: 6px 14px; border-radius: var(--r-sm);
  font-size: 13px; font-weight: 520; color: var(--text-soft);
  transition: background-color var(--speed), color var(--speed);
}
.pipeline-tab:hover { color: var(--text); }
.pipeline-tab.is-active { background: var(--surface); color: var(--text); box-shadow: var(--shadow); }

/* ---------- канбан ---------- */

.board {
  display: flex; gap: 24px; overflow-x: auto; padding-bottom: 12px;
  align-items: stretch; flex: 1; min-height: 0;
}
/* Автоширина: пока колонок не больше шести, они делят экран поровну.
   Ширину не отпускаем совсем — ниже 210 карточка со сделкой перестаёт
   читаться, поэтому при узком окне доска снова прокручивается. */
.board--fit .board-col { flex: 1 1 0; width: auto; min-width: 210px; }

.board-col {
  position: relative;
  flex: 0 0 268px; width: 268px;
  display: flex; flex-direction: column; max-height: 100%;
  border-radius: var(--r);
  transition: background-color var(--speed);
}

/* Тонкая линия в промежутке между колонками. */
.board-col:not(:last-child)::after {
  content: ""; position: absolute; right: -12px; top: 4px; bottom: 4px;
  width: 1px; background: var(--border);
}

.board-col.is-over { background: var(--surface-2); }

/* Шапка стадии — прозрачная заливка её цветом и цветная линия снизу.
   Цвет читается, но не бьёт по глазам. */
.board-col__head {
  padding: 10px 12px;
  border-radius: var(--r) var(--r) 0 0;
  border-bottom: 2px solid var(--stage, var(--border-strong));
  background: var(--stage-tint, var(--surface-2));
}
.board-col__title { font-weight: 600; font-size: 13px; color: var(--text); }
.board-col__meta { font-size: 11.5px; margin-top: 2px; color: var(--text-soft); }
.board-col__list {
  padding: 10px 0; overflow-y: auto;
  display: grid; gap: 8px; align-content: start;
}

.deal-card {
  background: var(--surface); border: 1px solid var(--border);
  border-radius: var(--r-sm); padding: 10px 11px; cursor: grab;
  box-shadow: var(--shadow);
  transition: border-color var(--speed);
}
.deal-card:hover { border-color: var(--border-strong); }
.deal-card.is-dragging { opacity: .4; }
.deal-card__title { font-weight: 520; font-size: 13px; line-height: 1.35; }
.deal-card__client { font-size: 12px; color: var(--text-faint); margin-top: 3px; }
.deal-card__foot { display: flex; align-items: center; gap: 8px; margin-top: 10px; }
.deal-card__amount { font-weight: 600; font-size: 13px; }

/* Заголовок группы внутри таблицы — например, отдел в списке сотрудников. */
.group-row td {
  background: var(--surface-2); padding: 7px 12px;
  font-size: 11.5px; text-transform: uppercase; letter-spacing: .4px;
  color: var(--text-faint); font-weight: 600;
}
.group-row:hover td { background: var(--surface-2); }
.group-row__count { margin-left: 6px; opacity: .7; }

/* ---------- карточка сущности ---------- */

.entity { display: grid; grid-template-columns: 336px 1fr; gap: 16px; align-items: start; }
.props { display: grid; }
.prop {
  display: grid; grid-template-columns: 122px 1fr; gap: 10px; padding: 8px 0;
  border-bottom: 1px solid var(--border); font-size: 13px;
}
.prop:first-child { padding-top: 0; }
.prop:last-child { border-bottom: 0; padding-bottom: 0; }
.prop__key { color: var(--text-faint); font-size: 12px; padding-top: 1px; }

/* Правка на месте: значение само становится полем ввода. */
.editable { display: flex; align-items: flex-start; gap: 2px; min-width: 0; }
.editable__value {
  /* Курсор обычный, хотя текст и выделяется: «палка» тут означала бы поле
     ввода, а до нажатия это ещё надпись. Что значение правится, показывает
     карандаш, появляющийся при наведении. */
  flex: 1; min-width: 0; cursor: default; border-radius: var(--r-sm);
  padding: 2px 6px; margin: -2px -6px;
  white-space: pre-wrap; word-break: break-word;
  transition: background-color var(--speed);
}
.editable:hover > .editable__value { background: var(--surface-2); }
.editable__pen {
  flex: 0 0 auto; padding: 3px; margin-left: 6px; opacity: 0;
  transition: opacity var(--speed), background-color var(--speed), color var(--speed);
}
.editable:hover .editable__pen, .editable__pen:focus-visible { opacity: 1; }
.editable__ok { flex: 0 0 auto; padding: 3px; margin-left: 6px; color: var(--green); }
.editable.is-editing { display: block; }
.editable.is-saving { opacity: .5; }
.editable .input { padding: 4px 8px; font-size: 13px; }

.tabs { display: flex; gap: 2px; border-bottom: 1px solid var(--border); margin-bottom: 16px; }
.tab {
  border: 0; background: none; cursor: pointer; padding: 9px 13px;
  color: var(--text-soft); font-weight: 520; border-bottom: 2px solid transparent;
  margin-bottom: -1px; transition: color var(--speed), border-color var(--speed);
}
.tab:hover { color: var(--text); }
.tab.is-active { color: var(--accent-text); border-bottom-color: var(--accent); }

.timeline { display: grid; gap: 13px; }
.tl-item { display: grid; grid-template-columns: 26px minmax(0, 1fr); gap: 10px; }
.tl-item__body { font-size: 13px; }
/* Комментарий к событию — отдельным блоком под заголовком. */
.tl-item__note {
  margin-top: 5px; padding: 6px 10px;
  border-left: 2px solid var(--accent);
  background: var(--surface-2); border-radius: 0 var(--r-sm) var(--r-sm) 0;
  font-size: 13px; white-space: pre-wrap; color: var(--text);
}
.tl-item__meta { font-size: 11.5px; color: var(--text-faint); margin-top: 2px; }
.tl-item--system .tl-item__body { color: var(--text-soft); }

/* Переключатель из двух-трёх состояний: «В работе / Все». Не выпадающий
   список — вариантов мало, и выбор должен быть виден без нажатия. */
.seg { display: inline-flex; padding: 2px; border-radius: var(--r); background: var(--surface-2); }
.seg__btn {
  border: 0; background: none; cursor: pointer;
  padding: 6px 12px; border-radius: calc(var(--r) - 3px);
  color: var(--text-soft); font-size: 12.5px; font-weight: 520;
  transition: background-color var(--speed), color var(--speed);
}
.seg__btn:hover { color: var(--text); }
.seg__btn.is-active { background: var(--surface); color: var(--text); box-shadow: var(--shadow); }

/* ---------- стадии сделки ---------- */

/* Ряд кнопок и ничего больше. Пробовал стрелки во всю ширину и линию прогресса
   снизу — и то и другое утяжеляло шапку: элемент, который смотрят полсекунды,
   перетягивал на себя внимание всей карточки.

   Состояние читается цветом текста, а не заливкой: пройденные стадии
   подписаны цветом стадии на нейтральном фоне, текущая — единственная плотно
   залитая. Заливать пройденные полупрозрачным цветом нельзя: на тёмной теме
   янтарный поверх графита даёт грязно-коричневый, и ряд выглядит грязным. */
.stage-track {
  display: flex; align-items: stretch; gap: 6px;
  overflow-x: auto; scrollbar-width: none;
}
.stage-track::-webkit-scrollbar { display: none; }

.stage-seg {
  flex: 1 1 0; min-width: 104px;
  border: 0; border-radius: 10px; cursor: pointer;
  padding: 10px 14px;
  background: var(--surface-2); color: var(--text-faint);
  font-size: 12.5px; font-weight: 520; text-align: left; white-space: nowrap;
  transition: background-color var(--speed), color var(--speed);
}
.stage-seg:hover { background: var(--surface-3); color: var(--text); }

/* Пройденное: нейтральный фон, но подпись цветом стадии — видно, что позади,
   и ряд остаётся спокойным. */
.stage-seg.is-passed { color: var(--seg); font-weight: 560; }
.stage-seg.is-current {
  background: var(--seg); color: #fff; font-weight: 620;
}
.stage-seg.is-current:hover { background: var(--seg); color: #fff; }

/* Завершение — выход из воронки, а не следующий шаг: отделено промежутком. */
.stage-seg--close {
  flex: 0 0 auto; margin-left: 6px;
  background: var(--surface); color: var(--text-soft);
  box-shadow: inset 0 0 0 1px var(--border);
}
.stage-seg--close:hover { color: var(--text); box-shadow: inset 0 0 0 1px var(--border-strong); }
.stage-seg--close.is-passed { background: var(--seg); color: #fff; box-shadow: none; }

@media (max-width: 820px) {
  /* На телефоне сегменты по содержимому, полоса прокручивается вбок. */
  .stage-seg { flex: 0 0 auto; min-width: 0; }
}

/* Выбор исхода в окне закрытия: две кнопки, каждая своего цвета. Кружок
   переключателя не нужен — выбранное видно по заливке, а вариантов всего два,
   и оба на экране одновременно. */
.outcome-row { display: flex; gap: 10px; }
.outcome {
  flex: 1 1 0;
  display: flex; align-items: center; justify-content: center;
  min-height: 46px; padding: 10px 14px;
  border: 1px solid var(--border); border-radius: var(--r);
  background: var(--surface-2); color: var(--text-soft);
  font-size: 13.5px; font-weight: 560; text-align: center; cursor: pointer;
  transition: background-color var(--speed), color var(--speed), border-color var(--speed);
}
/* Переключатель скрыт, но остаётся в разметке: без него не работали бы ни
   клавиатура, ни программы чтения с экрана. */
.outcome input { position: absolute; opacity: 0; pointer-events: none; }
.outcome:hover { color: var(--text); border-color: var(--border-strong); }
.outcome--won.is-picked {
  background: var(--green-soft); border-color: var(--green); color: var(--green);
}
.outcome--lost.is-picked {
  background: var(--red-soft); border-color: var(--red); color: var(--red);
}

/* Причины проигрыша: список выбора одной строкой на пункт. */
.radio-list { display: grid; gap: 2px; padding: 4px 2px; }
.radio-row {
  display: flex; align-items: center; gap: 10px;
  padding: 8px 10px; border-radius: var(--r-sm); cursor: pointer; font-size: 13.5px;
}
.radio-row:hover { background: var(--surface-2); }

@media (max-width: 820px) {
  /* На телефоне сегменты по содержимому, вся полоса прокручивается вбок. */
  .stage-seg { flex: 0 0 auto; min-width: 0; }
}

/* Переключатель из двух-трёх состояний: «В работе / Все». Не выпадающий
   список — вариантов мало, и выбор должен быть виден без нажатия. */
.seg { display: inline-flex; padding: 2px; border-radius: var(--r); background: var(--surface-2); }
.seg__btn {
  border: 0; background: none; cursor: pointer;
  padding: 6px 12px; border-radius: calc(var(--r) - 3px);
  color: var(--text-soft); font-size: 12.5px; font-weight: 520;
  transition: background-color var(--speed), color var(--speed);
}
.seg__btn:hover { color: var(--text); }
.seg__btn.is-active { background: var(--surface); color: var(--text); box-shadow: var(--shadow); }

/* ---------- чат ---------- */

/* В разделе чата шапка скрыта, отступы убраны — переписка во весь экран. */
.app.is-chat .topbar { display: none; }
.app.is-chat .view { padding: 0; }
.app.is-chat .chat { border: 0; border-radius: 0; box-shadow: none; }

/* Поиск по списку чатов */
/* Линия идёт под всей шапкой списка — вместе с поиском, а не между ними. */
.chat__search { padding: 0 12px 10px; border-bottom: 1px solid var(--border); }

/* ---------- правая панель чата ---------- */

.chat__side {
  border-left: 1px solid var(--border); background: var(--surface);
  display: flex; flex-direction: column; overflow: hidden;
}
.chat__side-head {
  display: flex; align-items: center; gap: 8px;
  padding: 8px 14px; min-height: var(--chat-head-h);
  border-bottom: 1px solid var(--border);
}
.chat__side-body { overflow-y: auto; padding: 14px; display: grid; gap: 18px; align-content: start; }
.chat__side-block { display: grid; gap: 10px; }
.chat__side-title {
  display: flex; align-items: center; justify-content: space-between; gap: 8px;
  font-size: 11.5px; text-transform: uppercase; letter-spacing: .4px;
  color: var(--text-faint); font-weight: 550;
}
.chat.has-side { grid-template-columns: 266px 1fr 268px; }
.msg__edited { font-size: 10px; opacity: .7; }

.chat {
  display: grid; grid-template-columns: 266px 1fr;
  flex: 1; min-height: 0;
  background: var(--surface); border: 1px solid var(--border); border-radius: var(--r);
  overflow: hidden; box-shadow: var(--shadow);
}
/* Прокручивается только сам список: вкладки, «Написать» и поиск остаются
   на месте, иначе поле поиска уезжает вверх ровно тогда, когда оно нужно. */
.chat__tabs { padding: 10px 12px 0; }

.chat__list {
  border-right: 1px solid var(--border); background: var(--surface);
  display: flex; flex-direction: column; overflow: hidden;
}
.chat__list-body { flex: 1; min-height: 0; overflow-y: auto; }
.chat__list-head {
  padding: 8px 12px; display: flex; align-items: center; gap: 8px;
  min-height: var(--chat-head-h);
}
/* Заголовок экрана, иконки поиска и «написать», кнопка «…» и «назад» в шапке
   переписки — всё это про телефон. На широком экране список и переписка стоят
   рядом, и заголовок с кнопкой возврата там не нужны. */
.chat__list-title, .chat__list-icon, .chat__head-more, .chat__back, .chat__side-back {
  display: none;
}

.room {
  padding: 10px 12px; cursor: pointer;
  display: grid; grid-template-columns: 34px 1fr auto; gap: 10px; align-items: center;
  transition: background-color var(--speed);
}
.room:hover { background: var(--surface-2); }
.room.is-active { background: var(--surface-3); }
.room__name { font-size: 13px; font-weight: 540; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.room__last { font-size: 12px; color: var(--text-faint); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
/* Правый столбец строки: время сверху, счётчик под ним.

   Раньше счётчик стоял в одну строку со временем и при появлении сдвигал его
   влево — время «прыгало» ровно у тех чатов, куда и смотрят в первую очередь.
   Теперь это две строки фиксированной колонки: время всегда на одном месте,
   счётчик появляется под ним и ничего не двигает. */
.room__meta {
  display: grid; grid-template-rows: auto auto; gap: 5px;
  justify-items: end; align-content: start;
  min-width: 34px;
}
.room__time { font-size: 11.5px; color: var(--text-faint); line-height: 1.2; white-space: nowrap; }
.room.has-unread .room__time { color: var(--accent-text); font-weight: 600; }

.room__unread {
  min-width: 20px; height: 20px; padding: 0 6px;
  border-radius: 20px; background: var(--accent); color: #fff;
  font-size: 11px; font-weight: 700; line-height: 20px; text-align: center;
}

/* min-height: 0 обязателен: без него колонка растёт под содержимое ленты
   и поле ввода уезжает под нижний край экрана. */
.chat__main { display: flex; flex-direction: column; min-width: 0; min-height: 0; position: relative; }
.chat__list { min-height: 0; }
.chat__side { min-height: 0; }
.chat__head {
  padding: 8px 16px; display: flex; align-items: center; gap: 10px;
  min-height: var(--chat-head-h);
  /* Здесь стекло не давало вообще ничего: шапка стоит в потоке, под ней
     ничего не проезжает — размывался неподвижный фон. А фильтр всё равно
     пересчитывался при каждой перерисовке ленты. */
  background: var(--surface);
  border-bottom: 1px solid var(--border);
}
/* Узор на фоне переписки.

   Рисунок подключён маской, а не картинкой: в самом файле цвета чёрные, и
   как картинка он был бы пятном поверх тёмной темы. Маска оставляет от него
   только форму, а красит её градиент в фирменных цветах — один и тот же файл
   работает в обеих темах, меняется лишь прозрачность.

   Слой лежит на .chat__main, который не прокручивается: узор стоит на месте,
   как обои в телеграме, а не едет вместе с лентой. */
/* Оникс с фиолетовыми бликами. Основа — почти чёрный фон переписки, поверх
   него два мягких пятна акцентного цвета по диагонали и лёгкая подцветка
   всего полотна. Пятна намеренно крупные и размытые: мелкие читались бы как
   пятна на экране, а большие — как освещение. */
.chat__main {
  position: relative;
  background:
    radial-gradient(125% 85% at 100% 0%,
      color-mix(in srgb, var(--accent) 24%, transparent), transparent 58%),
    radial-gradient(105% 75% at 0% 100%,
      color-mix(in srgb, var(--accent) 16%, transparent), transparent 55%),
    linear-gradient(170deg,
      color-mix(in srgb, var(--accent) 7%, var(--chat-bg)),
      var(--chat-bg) 55%,
      color-mix(in srgb, var(--accent) 4%, var(--chat-bg)));
}
/* На мониторе — тихий градиент вместо узора. Рисунок хорош на телефоне, где
   переписка занимает весь экран и читается как отдельное приложение; в широком
   окне он превращается в ковёр за спиной у текста и мешает читать. Градиент
   уводит углы в тёмно-фиолетовый — заметно, только если приглядываться. */
.chat__main::before {
  content: ""; position: absolute; inset: 0; z-index: 0; pointer-events: none;
  background:
    radial-gradient(115% 85% at 100% 0%,
      color-mix(in srgb, var(--accent) 14%, transparent), transparent 62%),
    radial-gradient(95% 75% at 0% 100%,
      color-mix(in srgb, var(--accent) 9%, transparent), transparent 58%);
}

@media (max-width: 820px) {
  /* Узор остаётся телефону. */
  .chat__main::before {
    background: linear-gradient(150deg, var(--accent), #29C3E8 55%, var(--accent) 100%);
    -webkit-mask-image: url("/assets/img/chat-pattern.svg");
    mask-image: url("/assets/img/chat-pattern.svg");
    -webkit-mask-size: cover; mask-size: cover;
    -webkit-mask-position: center; mask-position: center;
    opacity: var(--chat-pattern-opacity, .07);
    /* Растеризуем маску один раз: пересчитывать её при каждом движении поверх
       ленты незачем, а стоит она дорого — это полноэкранный SVG. */
    transform: translateZ(0);
  }
}
.chat__feed {
  position: relative; z-index: 1;
  flex: 1; min-height: 0; overflow-y: auto; padding: 14px 18px 10px;
  display: flex; flex-direction: column; align-items: stretch;
  background: transparent;
  overscroll-behavior: contain;
}
/* Шапка переписки — над слоем с узором, как лента и поле ввода.
   Заливка у неё сплошная, но слой узора рисуется absolute поверх обычных
   блоков, и рисунок проступал сквозь шапку: казалось, что она прозрачная. */
.chat__head, .chat__composer, .composer-reply { position: relative; z-index: 1; }

/* Метка конца ленты: за ней следит наблюдатель, показывающий кнопку «вниз». */
.chat__end { flex: 0 0 1px; height: 1px; }

/* Пропуск отрисовки невидимых сообщений (content-visibility) здесь пробовался
   и убран: лента отдаёт максимум 60 сообщений, для такого объёма выигрыша нет,
   а высота невидимых считается по оценке — из-за этого scrollHeight сразу
   после перерисовки был занижен, и лента не уезжала вниз к новому сообщению. */

/* Кнопка «к последним сообщениям».

   Не нажималась по одной причине: у неё не было z-index. Когда под ленту
   подложили фон, лента получила z-index: 1 и оказалась НАД кнопкой — палец
   попадал в сообщения, а сама кнопка просвечивала сквозь них. Отсюда и
   «полупрозрачная», и «не нажимается».

   z-index с запасом, плотная заливка вместо полупрозрачной и touch-action,
   чтобы браузер не придерживал касание, приняв его за начало прокрутки. */
.chat__down {
  position: absolute; right: 14px; bottom: 96px; z-index: 30;
  touch-action: manipulation;
  width: 42px; height: 42px; border-radius: 50%;
  background: var(--surface-3); border: 1px solid var(--border-strong); color: var(--text);
  box-shadow: 0 6px 20px rgba(0, 0, 0, .45);
  display: grid; place-items: center; cursor: pointer;
  opacity: 0; pointer-events: none;
  transition: opacity var(--speed), transform var(--speed);
}
.chat__down:hover { background: var(--surface-2); transform: translateY(-1px); }
.chat__down.is-visible { opacity: 1; pointer-events: auto; }

.chat__day {
  /* Липнет под шапкой: пролистывая переписку за неделю, человек всегда видит,
     какой день читает, не отматывая назад к разделителю. */
  position: sticky; top: 0; z-index: 5;
  align-self: center; margin: 6px 0;
  padding: 3px 12px; border-radius: 20px;
  background: var(--glass); -webkit-backdrop-filter: var(--glass-blur);
  backdrop-filter: var(--glass-blur);
  font-size: 11.5px; color: var(--text-faint);
}

/* Полоса «Новые сообщения»: линия во всю ширину с подписью посередине. */
.chat__unread-line {
  display: flex; align-items: center; gap: 10px;
  margin: 10px 0 4px; color: var(--accent-text); font-size: 11.5px; font-weight: 560;
}
.chat__unread-line::before, .chat__unread-line::after {
  content: ""; flex: 1; height: 1px; background: var(--accent);
  opacity: .45;
}

.msg { display: flex; align-items: flex-end; gap: 8px; max-width: min(78%, 620px); margin-top: 10px; }
.msg--grouped { margin-top: 2px; }
.msg--own { align-self: flex-end; flex-direction: row-reverse; }
.msg__gap { width: 24px; flex: 0 0 24px; }

.msg__bubble {
  background: var(--chat-bubble); border: 1px solid var(--border);
  border-radius: 14px; padding: 6px 11px 5px;
  min-width: 78px; max-width: 100%;
}
.msg--own .msg__bubble {
  background: var(--chat-out); border-color: var(--chat-out-line); color: var(--chat-out-text);
}
.msg:not(.msg--own):not(.msg--grouped) .msg__bubble { border-bottom-left-radius: 5px; }
.msg--own:not(.msg--grouped) .msg__bubble { border-bottom-right-radius: 5px; }

.msg__author { font-size: 12.5px; font-weight: 580; color: var(--accent-text); margin-bottom: 1px; }
.msg--own .msg__author, .msg--grouped .msg__author { display: none; }
.msg__text { font-size: 14px; line-height: 1.42; white-space: pre-wrap; word-break: break-word; }
/* Время с галочками прижато к правому нижнему углу пузыря.
   Место под него резервирует .msg__time-gap в конце текста —
   так короткие сообщения не растягиваются на лишнюю строку. */
/* Время с галочками — отдельной строкой, прижато вправо.
   Пробовал наложение поверх текста, как в телеграме, — на коротких
   сообщениях время налезало на слова, поэтому здесь простая строка. */
.msg__time {
  display: flex; align-items: center; justify-content: flex-end; gap: 3px;
  margin-top: 1px; font-size: 11px; line-height: 1.3;
  color: var(--text-faint); font-variant-numeric: tabular-nums;
}
.msg--own .msg__time { color: var(--chat-out-faint); }
.msg--own a { color: var(--chat-out-mark); }

/* Отметка пересылки — над текстом, как в мессенджерах. */
.msg__forward {
  display: flex; align-items: center; gap: 4px;
  font-size: 11.5px; color: var(--text-faint); margin: 1px 0 3px;
}
.msg__forward b { font-weight: 580; }
.msg--own .msg__forward { color: var(--chat-out-faint); }

.msg__quote {
  display: block; cursor: pointer;
  border-left: 3px solid var(--accent);
  background: var(--accent-soft);
  border-radius: 4px; padding: 3px 8px; margin: 2px 0 5px;
}
.msg--own .msg__quote { border-left-color: var(--chat-out-mark); background: var(--chat-out-soft); }
.msg__quote-author { display: block; font-size: 12px; font-weight: 580; }
.msg--own .msg__quote-author { color: var(--chat-out-mark); }
.msg__quote-text { display: block; font-size: 12.5px; opacity: .82; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }

/* Галочки прочтения: одна — доставлено, две — все прочитали. */
.msg__receipt { display: inline-grid; place-items: center; opacity: .8; }
.msg__receipt.is-read { opacity: 1; color: var(--chat-out-mark); }

/* Реакции — только знак и счётчик, без аватаров: в рабочей переписке важно,
   согласовано или нет, а не чьё лицо стоит рядом; на телефоне ряд лиц ещё и
   разъезжает пузырь по ширине. Кто именно отметил — во всплывающей подсказке. */
.msg__reactions { display: flex; gap: 5px; flex-wrap: wrap; margin-top: 5px; }
.reaction {
  display: inline-flex; align-items: center; gap: 3px;
  padding: 1px 7px 1px 5px; border-radius: 20px; cursor: pointer;
  font-size: 12px; font-weight: 550; line-height: 1.5;
  border: 1px solid var(--border); background: var(--surface-2); color: var(--text-soft);
}
.reaction--ok.is-mine { background: var(--green-soft); border-color: var(--green); color: var(--green); }
.reaction--no.is-mine { background: var(--red-soft); border-color: var(--red); color: var(--red); }
.msg--own .reaction {
  background: var(--chat-out-soft); border-color: transparent; color: var(--chat-out-text);
}
.msg--own .reaction--ok.is-mine { background: var(--green-soft); border-color: var(--green); color: var(--green); }
.msg--own .reaction--no.is-mine { background: var(--red-soft); border-color: var(--red); color: var(--red); }

/* Картинка показывается сразу, клик открывает оригинал. */
.msg__image { display: block; margin-top: 5px; border-radius: 10px; overflow: hidden; }
.msg__image img {
  display: block; max-width: 280px; max-height: 300px;
  width: auto; height: auto; object-fit: cover;
}

/* Сообщение из одной картинки: пузырь убираем совсем, картинка сама себе
   пузырь. Иначе вокруг фото остаётся широкая цветная рамка. */
.msg__bubble--media,
.msg--own .msg__bubble--media {
  position: relative; background: none; border-color: transparent;
  padding: 0; min-width: 0; overflow: hidden;
}
.msg__bubble--media .msg__author { padding: 0 2px 3px; }
.msg__bubble--media .msg__image { margin-top: 0; border-radius: 14px; }
.msg:not(.msg--own):not(.msg--grouped) .msg__bubble--media .msg__image { border-bottom-left-radius: 5px; }
.msg--own:not(.msg--grouped) .msg__bubble--media .msg__image { border-bottom-right-radius: 5px; }

/* Время и галочки плашкой поверх картинки — читаются и на светлом, и на
   тёмном снимке, потому что подложка своя, а не цвет пузыря. */
.msg__time--over,
.msg--own .msg__time--over {
  position: absolute; right: 8px; bottom: 8px; margin: 0;
  padding: 1px 8px; border-radius: 20px;
  background: rgba(12, 13, 16, .55); color: #fff;
  backdrop-filter: blur(3px);
}

/* Голосовое сообщение — плеер свой: браузерный controls в каждой системе
   свой собственный и в тему не попадает. */
.voice {
  display: flex; align-items: center; gap: 9px;
  width: 254px; max-width: 100%; margin: 5px 0 2px;
}
.voice__play {
  flex: 0 0 30px; width: 30px; height: 30px; border: 0; cursor: pointer;
  border-radius: 50%; display: grid; place-items: center;
  background: var(--accent); color: #fff;
  transition: background-color var(--speed), opacity var(--speed);
}
.voice__play:hover { background: var(--accent-hover); }
.voice__play:focus-visible { outline: none; box-shadow: var(--ring); }
.voice__play .icon { margin-left: 1px; }

.voice__wave {
  flex: 1; min-width: 0; height: 26px; cursor: pointer;
  display: flex; align-items: center; gap: 2px;
}
/* Столбики — реальная громкость записи. Двигаться им незачем: при
   проигрывании бежит только заливка, всё остальное стоит на месте. */
.voice__bar {
  flex: 1; min-width: 2px; border-radius: 2px;
  background: var(--border-strong);
  transition: background-color var(--speed);
}
.voice__bar.is-played { background: var(--accent); }

/* Старые записи, снятые до появления формы волны, — ровная дорожка. */
.voice__wave--flat .voice__bar { border-radius: 1px; }

.voice__time {
  font-size: 12px; font-variant-numeric: tabular-nums; color: var(--text-faint);
  flex: 0 0 auto; min-width: 30px; text-align: right;
}
.voice__rate {
  border: 0; background: none; cursor: pointer; padding: 2px 4px;
  font-size: 11px; font-weight: 600; color: var(--text-faint);
  border-radius: 4px; flex: 0 0 auto;
  transition: background-color var(--speed), color var(--speed);
}
.voice__rate:hover { background: var(--surface-2); color: var(--text); }

/* В своём пузыре плеер живёт на спокойной подложке — цвета берём из тех же
   токенов, иначе в светлой теме белый кружок пропадает на светло-голубом. */
.msg--own .voice__play { background: var(--chat-out-btn); color: var(--chat-out-btn-text); }
.msg--own .voice__play:hover { background: var(--chat-out-mark); }
.msg--own .voice__bar { background: var(--chat-out-wave); }
.msg--own .voice__bar.is-played { background: var(--chat-out-wave-on); }
.msg--own .voice__time { color: var(--chat-out-faint); }
.msg--own .voice__rate { color: var(--chat-out-faint); }
.msg--own .voice__rate:hover { background: var(--chat-out-soft); color: var(--chat-out-text); }

/* Выбор чата при пересылке */
.pick-list {
  display: flex; flex-direction: column; gap: 2px;
  max-height: 46vh; overflow: auto; margin: 0 -4px; padding: 0 4px;
}
.pick {
  display: flex; align-items: center; gap: 10px; width: 100%;
  border: 0; background: none; cursor: pointer; text-align: left;
  padding: 7px 9px; border-radius: var(--r-sm); color: var(--text);
  transition: background-color var(--speed);
}
.pick:hover { background: var(--surface-2); }
.pick.is-picked { background: var(--accent-soft); }
.pick:focus-visible { outline: none; box-shadow: var(--ring); }
.pick__group { background: var(--surface-3); color: var(--text-soft); }
.pick__body { display: grid; gap: 1px; min-width: 0; flex: 1; }
.pick__title {
  font-size: 13.5px; font-weight: 520;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.pick__meta { font-size: 11.5px; color: var(--text-faint); }
.pick__mark { color: var(--accent-text); opacity: 0; transition: opacity var(--speed); }
.pick.is-picked .pick__mark { opacity: 1; }

/* Запись голосового */
.rec-timer {
  font-variant-numeric: tabular-nums; font-size: 13px; color: var(--red);
  padding: 0 4px; align-self: center;
}
.icon-btn.is-recording { color: var(--red); animation: rec-pulse 1.1s ease-in-out infinite; }
@keyframes rec-pulse { 50% { opacity: .35; } }

/* Шапка чата кликабельна — открывает настройки */
.chat__head-info {
  display: flex; align-items: center; gap: 10px; flex: 1; min-width: 0;
  border: 0; background: none; cursor: pointer; color: inherit;
  padding: 4px 8px; margin: -4px -8px; border-radius: var(--r-sm);
  transition: background-color var(--speed);
}
.chat__head-info:hover { background: var(--surface-2); }

/* Присутствие: зелёная точка на аватаре и такая же подпись под именем.
   Точка обведена цветом подложки — иначе на тёмном аватаре теряется. */
.presence { position: relative; display: inline-flex; flex: 0 0 auto; }
.presence.is-online::after {
  content: ''; position: absolute; right: -1px; bottom: -1px;
  width: 10px; height: 10px; border-radius: 50%;
  background: var(--green); border: 2px solid var(--surface);
}
/* В шапке аватар мельче — точка тоже. */
.chat__head-info .presence.is-online::after { width: 9px; height: 9px; }
.chat__head-info .small.is-online { color: var(--green); }

.msg__act {
  opacity: 0; transition: opacity var(--speed); flex: 0 0 auto;
  border: 0; background: none; cursor: pointer; padding: 5px;
  border-radius: 50%; color: var(--text-faint);
}
.msg:hover .msg__act { opacity: 1; }
.msg__act:hover { background: var(--surface-3); color: var(--text); }

.msg.is-flash .msg__bubble { animation: msg-flash 1.4s ease-out; }
@keyframes msg-flash {
  0%, 35% { box-shadow: var(--ring); }
  100% { box-shadow: none; }
}

/* Полоса ответа: акцентная линия, стрелка, имя автора и цитата.
   Линия нарисована absolute, поэтому левый отступ строки обязан её обходить.
   Было 14 пикселей при линии на 12–15 — стрелка ложилась прямо на линию
   и казалась сдвинутой вверх и влево. */
.composer-reply {
  display: flex; align-items: center; gap: 10px;
  padding: 7px 12px 7px 27px;
  border-top: 1px solid var(--border);
  background: var(--surface-2);
  position: relative;
}
.composer-reply::before {
  content: ""; position: absolute; left: 12px; top: 7px; bottom: 7px;
  width: 3px; border-radius: 2px; background: var(--accent);
}
.composer-reply__body { min-width: 0; flex: 1; }
.composer-reply__author { font-size: 12px; font-weight: 580; color: var(--accent-text); }
.composer-reply__text { font-size: 12.5px; color: var(--text-soft); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }

/* Обсуждение внутри карточки задачи — та же панель, только компактнее. */
.task-chat { display: flex; flex-direction: column; overflow: hidden; }
.task-chat .chat__main { min-height: 340px; max-height: 460px; }
.task-chat .chat__feed { padding: 12px 14px 8px; }

.chat__composer {
  border-top: 1px solid var(--border); padding: 9px 12px;
  display: flex; gap: 8px; align-items: flex-end; background: var(--surface);
}
.chat__composer textarea { max-height: 160px; }

/* Кнопки у поля ввода — ровные квадраты по высоте однострочного поля,
   иконка внутри строго по центру. */
.chat__composer .icon-btn,
.chat__composer .btn {
  width: 38px; height: 38px; flex: 0 0 38px; padding: 0;
  display: grid; place-items: center;
}
/* Коробка под иконку фиксированная: без неё высота считалась по svg,
   и стрелка вставала выше середины, когда цитата занимала две строки. */
.composer-reply__icon {
  width: 20px; height: 20px; flex: 0 0 20px;
  display: grid; place-items: center; color: var(--accent-text);
}

/* ---------- модальное окно ---------- */

.modal-root { position: fixed; inset: 0; z-index: 90; display: grid; place-items: center; padding: 20px; }
.modal-backdrop {
  position: absolute; inset: 0; background: var(--scrim);
  -webkit-backdrop-filter: blur(5px);
  backdrop-filter: blur(5px);
}
.modal {
  position: relative; background: var(--surface); border: 1px solid var(--border);
  border-radius: 12px; width: min(640px, 100%); max-height: 88vh; overflow: auto;
  box-shadow: var(--shadow-pop);
}
.modal--wide { width: min(860px, 100%); }
.modal__head {
  padding: 16px 20px; border-bottom: 1px solid var(--border); display: flex;
  align-items: center; gap: 10px; position: sticky; top: 0; background: var(--surface); z-index: 2;
}
.modal__title { font-weight: 620; font-size: 16px; letter-spacing: -.2px; }
.modal__body { padding: 20px; display: grid; gap: 14px; }
.modal__foot {
  padding: 14px 20px; border-top: 1px solid var(--border); display: flex; gap: 8px;
  justify-content: flex-end; position: sticky; bottom: 0; background: var(--surface);
}

/* ---------- уведомления ---------- */

.toasts { position: fixed; right: 16px; bottom: 16px; display: grid; gap: 8px; z-index: 120; }
.toast--action { cursor: pointer; border-color: var(--accent); }
.toast--action::after { content: " →"; opacity: .7; }
.toast {
  background: var(--toast-bg); color: #fff; padding: 11px 15px; border-radius: var(--r-sm);
  box-shadow: var(--shadow-pop); font-size: 13px; max-width: 340px;
  animation: toast-in 140ms ease-out;
}
.toast--error { background: var(--red); }
.toast--ok { background: var(--green); }
@keyframes toast-in { from { opacity: 0; transform: translateY(6px); } }

/* ---------- адаптив ---------- */


/* ---------- рабочий стол ---------- */

/* Заголовок с датой над ним: дата и имя — контекст, а не команда, поэтому
   мелко и приглушённо, а «Рабочий стол» остаётся заголовком страницы. */
/* Шапка повторяет сетку страницы: те же 1.55fr / 1fr и тот же зазор, что у
   .cols--dash ниже. Плашки живут в левой ячейке и прижаты к её правому краю —
   то есть встают ровно на правый край «Очереди на сегодня», а переключатель —
   на правый край страницы.

   Привязка именно к левой колонке, а не к правой: левая широкая (1.55fr) и
   вмещает заголовок с плашками на любой рабочей ширине. Когда плашки стояли
   в правой ячейке, на ноутбучных 1280 они в неё не влезали, распирали сетку
   и выравнивание уезжало вместе с ней — при масштабировании это и было видно. */
.dash-head {
  display: grid; grid-template-columns: 1.55fr 1fr; gap: 16px;
  /* stretch, а не center: панель дня тянется на высоту ряда и совпадает
     с плашками чисел ровно, а не «примерно похоже». */
  align-items: stretch; margin-bottom: 14px;
}
.dash-head > * { min-width: 0; }
.dash-head__meta { font-size: 12px; color: var(--text-faint); margin-bottom: 2px; }
.dash-head__left { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; }
/* Плашки тянутся на высоту ряда наравне с панелью дня: иначе одна сторона
   шапки центрируется по своей высоте, другая по своей, и они расходятся на
   несколько пикселей — глазу это заметно, а причина неочевидна. */
.dash-head__left .metrics { margin-left: auto; align-self: stretch; }
/* Правая ячейка: панель дня занимает всё свободное место, переключатель
   прижат к правому краю страницы. */
.dash-head__right { display: flex; align-items: stretch; gap: 10px; }
.dash-head__right .scope-switch { flex: 0 0 auto; align-self: center; }
.dash-head__day { flex: 1; min-width: 0; display: flex; }

/* Показатели — в одну линию с заголовком, справа. Своей строкой они забирали
   полторы сотни пикселей высоты и отодвигали за сгиб очередь дня, «Ритм» и
   «Важное» — то, ради чего рабочий стол и открывают. Поэтому здесь они мельче,
   чем были: три числа, на которые смотрят раз в утро, не обязаны быть
   крупнее того, с чем работают весь день. */
.metrics { display: flex; gap: 8px; align-items: stretch; }
.metric {
  background: var(--surface); border: 1px solid var(--border); border-radius: var(--r);
  padding: 5px 11px; min-width: 96px; box-shadow: var(--shadow);
}
.metric__value { font-size: 17px; font-weight: 620; letter-spacing: -.4px; line-height: 1.25; }
.metric__label { font-size: 11.5px; color: var(--text-soft); line-height: 1.25; }
.metric__hint { font-size: 11px; color: var(--text-faint); line-height: 1.25; }
.metric--alert .metric__value { color: var(--red); }

/* На узком экране шапка в одну строку не помещается: заголовок и три плашки
   дерутся за ширину, и «30,8 млн ₽» переносится по слогам. Там показатели
   снова уходят под заголовок и занимают всю ширину — раскладку в три равные
   колонки задаёт правило в блоке 820px ниже, здесь только отдаём ей строку
   целиком. Выигрыш высоты — история про компьютер, на телефоне рабочий стол
   и так листают. */
@media (max-width: 900px) {
  /* Сетка в две колонки на узком экране не нужна: возвращаем обычную строку,
     а правую ячейку распускаем через display: contents — её дети становятся
     детьми шапки, и порядком можно управлять как раньше. Переключатель
     поднимается к заголовку, показатели уходят строкой ниже: иначе он уезжает
     третьей строкой и шапка съедает четверть экрана. */
  .dash-head { display: flex; flex-wrap: wrap; align-items: flex-start; gap: 8px; }
  .dash-head__right { display: contents; }
  .dash-head__left, .dash-head__right { display: contents; }
  .dash-head__right .scope-switch { order: 1; margin-left: auto; }
  /* Плашки с числами на телефоне не показываем вовсе: их место занимает
     панель дня, а «Просрочено» и «На сегодня» она проговаривает точнее. */
  .dash-head__left .metrics { display: none; }
  /* flex: none обязателен: без него панель делит строку с переключателем и
     сжимается до нечитаемой колонки. */
  .dash-head__day { order: 2; width: 100%; flex: none; }
}

.scope-switch { align-self: center; }

/* ---------- «Ритм»: шкала эффективности ----------

   Тон один на всю карточку и живёт в --tone: его берут и кольцо, и полоса
   дней, и шкалы в списке отдела. Красить их по отдельности нельзя — при
   смене порога цвета разъезжаются, и карточка начинает противоречить себе.
   Движения здесь нет намеренно: заполняющееся кольцо и бегущие числа
   выглядят нарядно ровно один раз, а видят их каждое утро. */

/* --tone приходит из скрипта растяжкой red → amber → green по оценке.
   Значение здесь — только запасное, на случай пустой карточки. */
.rhythm { --tone: var(--accent); }

/* Сегодняшний день. Стоит выше кольца: месячная оценка — это про «как я
   работаю вообще», а утром спрашивают «как идёт сейчас». */
.rhythm__day { padding: 12px 16px 4px; }
/* Копия в шапке: без отступов карточки и без подложки — она стоит не в карточке,
   а прямо на странице. */
.rhythm__day--compact { padding: 2px 0 0; width: 100%; }
.rhythm__day--compact .rhythm__chips { margin-top: 6px; }
.rhythm__day-head { display: flex; align-items: baseline; gap: 8px; margin-bottom: 7px; }
.rhythm__day-title { font-size: 13px; font-weight: 600; }
.rhythm__day-count { margin-left: auto; font-size: 12.5px; color: var(--text-soft); }

.day-bar {
  display: flex; gap: 2px; height: 10px; border-radius: 6px; overflow: hidden;
  background: var(--surface-3);
}
.day-bar__seg { display: block; min-width: 3px; transition: opacity 140ms ease; }
.day-bar__seg:hover { opacity: .85; }
.day-bar__seg--done { background: var(--green); }
.day-bar__seg--over { background: var(--red); }
.day-bar__seg--left { background: var(--border-strong); }
.day-bar__seg--empty { background: var(--surface-3); }
.rhythm__day .rhythm__chips { margin-top: 8px; }

.rhythm-chip--warn { background: var(--surface-3); color: var(--text-soft); }

.rhythm__body { display: flex; align-items: center; gap: 16px; padding: 10px 16px 8px; }

.rhythm__ring { position: relative; width: 88px; height: 88px; flex: 0 0 88px; }
.rhythm__ring svg { display: block; }
.rhythm__track { stroke: var(--surface-3); }
.rhythm__arc { stroke: var(--tone); }


.rhythm__score {
  position: absolute; inset: 0; display: flex; align-items: center; justify-content: center;
  font-size: 26px; font-weight: 640; letter-spacing: -.8px; color: var(--text);
}
.rhythm__about { min-width: 0; }
.rhythm__level { font-size: 17px; font-weight: 620; letter-spacing: -.2px; color: var(--tone); }
.rhythm__caption { font-size: 12.5px; color: var(--text-soft); margin-top: 1px; }
.rhythm__chips { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 8px; }

.rhythm-chip {
  display: inline-flex; align-items: center; gap: 4px; padding: 2px 8px;
  border-radius: 999px; font-size: 11.5px; line-height: 18px;
  background: var(--surface-2); color: var(--text-soft);
}
.rhythm-chip--fire { background: var(--amber-soft); color: var(--amber); }
.rhythm-chip--alert { background: var(--red-soft); color: var(--red); }
.rhythm-chip--good { background: var(--green-soft); color: var(--green); }

/* Полоса дней: не сумма, а регулярность. Три пустых дня подряд видно сразу,
   а среднее за месяц их прячет. */
.rhythm__strip {
  display: flex; align-items: flex-end; gap: 4px; height: 62px; padding: 0 16px 16px;
}
.rhythm-bar {
  flex: 1; min-width: 0; border-radius: 3px; background: var(--tone);
  opacity: .85; transition: opacity 140ms ease;
}
.rhythm-bar:hover { opacity: 1; }
.rhythm-bar.is-weekend { opacity: .45; }
.rhythm-bar.is-empty { background: var(--border-strong); opacity: .5; }

.rhythm__team { border-top: 1px solid var(--border); padding: 10px 16px 12px; }
.rhythm__team-head { font-size: 11.5px; color: var(--text-faint); margin-bottom: 6px; }
.rhythm-row { display: flex; align-items: center; gap: 8px; padding: 3px 0; font-size: 12.5px; }
.rhythm-row__rank { flex: 0 0 14px; text-align: right; font-size: 11.5px; color: var(--text-faint); }
.rhythm-row__name {
  flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  color: var(--text-soft);
}
.rhythm-row__bar {
  flex: 0 0 74px; height: 5px; border-radius: 3px;
  background: var(--surface-3); overflow: hidden;
}
.rhythm-row__bar > span { display: block; height: 100%; border-radius: 3px; background: var(--tone); }
.rhythm-row__score { flex: 0 0 24px; text-align: right; font-weight: 560; }
.rhythm-row.is-me .rhythm-row__name { color: var(--text); font-weight: 560; }

@media (max-width: 820px) {
  .rhythm__ring { width: 76px; height: 76px; flex-basis: 76px; }
  .rhythm__ring svg { width: 76px; height: 76px; }
  .rhythm__score { font-size: 23px; }
}


.cols--dash { grid-template-columns: 1.55fr 1fr; }

/* ---------- карта активности и время на работе ----------

   Клетка красится одним цветом с переменной прозрачностью, а не четырьмя
   разными: так ступени заведомо не спорят друг с другом и одинаково работают
   в обеих темах. Насыщенность приходит из скрипта в --fill. */

.heat { display: flex; gap: 8px; align-items: flex-start; overflow-x: auto; padding-bottom: 4px; }
.heat__days {
  display: grid; grid-template-rows: repeat(7, 13px); gap: 3px;
  margin-top: 18px; font-size: 9.5px; color: var(--text-faint); line-height: 13px;
}
.heat__plot { min-width: 0; }
.heat__months {
  display: grid; grid-auto-flow: column; grid-auto-columns: 13px; gap: 3px;
  height: 14px; font-size: 9.5px; color: var(--text-faint); white-space: nowrap;
}
.heat__grid {
  display: grid; grid-auto-flow: column; grid-template-rows: repeat(7, 13px);
  grid-auto-columns: 13px; gap: 3px;
}
.heat__cell {
  width: 13px; height: 13px; border-radius: 3px;
  background: color-mix(in oklab, var(--accent), transparent calc(100% - var(--fill, 1) * 100%));
  transition: opacity 140ms ease;
}
.heat__cell:hover { opacity: .75; }
.heat__cell.is-empty { background: var(--surface-3); }
.heat__legend {
  display: flex; align-items: center; gap: 4px; margin-top: 10px;
  font-size: 11px; color: var(--text-faint);
}
.heat__legend span:first-child { margin-right: 2px; }
.heat__legend span:last-child { margin-left: 2px; }

.wt-select {
  height: 28px; padding: 0 6px; font-size: 12px;
  background: var(--surface-2); border: 1px solid var(--border); border-radius: var(--r-sm);
  color: var(--text);
}
.wt-tiles { display: flex; flex-wrap: wrap; gap: 10px; }
.wt-tile {
  flex: 1 1 130px; min-width: 0;
  background: var(--surface-2); border-radius: var(--r); padding: 8px 12px;
}
.wt-tile__value { font-size: 18px; font-weight: 620; letter-spacing: -.4px; }
.wt-tile__label { font-size: 12px; color: var(--text-soft); }
.wt-tile__hint { font-size: 11px; color: var(--text-faint); }

.wt-people { margin-top: 12px; border-top: 1px solid var(--border); padding-top: 8px; }
.wt-person { display: flex; align-items: center; gap: 8px; padding: 3px 0; font-size: 12.5px; }
.wt-person__name { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; color: var(--text-soft); }
.wt-person__hours { font-weight: 560; }

@media (max-width: 820px) {
  /* На телефоне карта не помещается никогда — оставляем прокрутку вбок и
     сжимаем клетку: четыре месяца в 360 пикселей иначе не встанут. */
  .heat__days { grid-template-rows: repeat(7, 11px); line-height: 11px; }
  .heat__grid { grid-template-rows: repeat(7, 11px); grid-auto-columns: 11px; }
  .heat__months { grid-auto-columns: 11px; }
  .heat__cell { width: 11px; height: 11px; }
}

/* ---------- панель дня ----------

   Заливка идёт двумя слоями: снизу растяжка от фирменного индиго к зелёному,
   сверху мягкий блик. Оба цвета — из токенов через color-mix, поэтому в
   светлой теме полоса светлеет вместе с интерфейсом, а не остаётся тёмной
   заплаткой. Ширину не анимируем: правило проекта про анимации (только цвет и
   прозрачность, 120–150 мс) здесь работает на пользу — полоса, доезжающая
   при каждом обновлении, на рабочем столе быстро надоедает. */

.today {
  --day-accent: var(--green);
  flex: 1; min-width: 0;
  display: flex; flex-direction: column; justify-content: center; gap: 7px;
  background: var(--surface); border: 1px solid var(--border);
  border-radius: var(--r); box-shadow: var(--shadow);
  padding: 8px 14px;
}
.today--win { --day-accent: var(--green); }
.today--warn { --day-accent: var(--amber); }
.today--alert { --day-accent: var(--red); }
.today--calm { --day-accent: var(--text-faint); }

.today__head { display: flex; align-items: baseline; gap: 9px; }
.today__title { font-size: 13px; font-weight: 620; }
.today__verdict {
  font-size: 11.5px; font-weight: 550; line-height: 17px;
  color: var(--day-accent); padding: 0 8px; border-radius: 999px;
  background: color-mix(in oklab, var(--day-accent), transparent 88%);
}
.today__count { margin-left: auto; font-size: 15.5px; font-weight: 640; letter-spacing: -.3px; }

.today__bar {
  position: relative; display: flex; height: 18px;
  border-radius: 999px; background: var(--surface-3); overflow: hidden;
}
.today__fill {
  background:
    linear-gradient(180deg, color-mix(in oklab, var(--on-accent), transparent 84%), transparent 60%),
    linear-gradient(90deg, color-mix(in oklab, var(--logo-from), var(--green) 25%), var(--green));
  box-shadow: 0 0 14px color-mix(in oklab, var(--green), transparent 60%);
}
.today__over {
  background:
    linear-gradient(180deg, color-mix(in oklab, var(--on-accent), transparent 84%), transparent 60%),
    linear-gradient(90deg, color-mix(in oklab, var(--red), var(--amber) 30%), var(--red));
}

/* Метка «сейчас»: сколько рабочего дня прошло. Без неё «4 из 5» ничего не
   говорит — четыре из пяти в десять утра и в шесть вечера это разные дни. */
.today__now {
  position: absolute; top: 0; bottom: 0; width: 2px; margin-left: -1px;
  background: var(--text); opacity: .55;
}

/* Переносы — единственное, что стоит рядом с вердиктом: остальное видно
   по самой полосе и в подсказке к ней. */
.today__moved {
  font-size: 11.5px; line-height: 17px; color: var(--amber);
  padding: 0 8px; border-radius: 999px;
  background: color-mix(in oklab, var(--amber), transparent 88%);
}

/* ---------- очередь дня и группы по срокам ---------- */

.queue-group + .queue-group { border-top: 1px solid var(--border); }
.queue-group__head {
  display: flex; align-items: center; gap: 8px;
  padding: 6px 16px; background: var(--surface-2);
  font-size: 12px; font-weight: 560; color: var(--text-soft);
}
.queue-group__head.is-over { background: var(--red-soft); color: var(--red); }
.queue-group__hint { margin-left: auto; font-weight: 450; opacity: .85; }

.queue-list { display: flex; flex-direction: column; }
.queue-more {
  border: 0; background: none; cursor: pointer; width: 100%;
  padding: 7px 16px; text-align: left;
  font-size: 12px; color: var(--accent-text);
}
.queue-more:hover { background: var(--surface-2); }

.qrow {
  display: flex; align-items: center; gap: 10px;
  padding: 8px 16px; cursor: pointer;
  border-bottom: 1px solid var(--border);
  transition: background-color var(--speed);
}
.qrow:last-child { border-bottom: 0; }
.qrow:hover { background: var(--surface-2); }
.qrow.is-muted { opacity: .7; }
.qrow__main { flex: 1; min-width: 0; }
.qrow__title { font-size: 13.5px; line-height: 1.3; }
.qrow__meta { font-size: 11.5px; color: var(--text-faint); margin-top: 1px; }

/* Чекбокс рисуем сами: нативный не даёт нужного размера и радиуса,
   а закрытие дела — главное действие строки, оно должно быть крупным. */
.qcheck {
  width: 18px; height: 18px; flex: 0 0 18px; padding: 0;
  border: 1.5px solid var(--border-strong); border-radius: var(--r-sm);
  background: none; cursor: pointer;
  transition: border-color var(--speed), background-color var(--speed);
}
.qcheck:hover { border-color: var(--green); background: var(--green-soft); }

.qdue { font-size: 12px; color: var(--text-faint); white-space: nowrap; text-align: right; }
.qdue.is-over { color: var(--red); font-weight: 550; }
.qdue.is-today { color: var(--green); font-weight: 550; }
.qrow__move { opacity: 0; transition: opacity var(--speed); }
.qrow:hover .qrow__move, .qrow__move:focus-visible { opacity: 1; }

/* ---------- сделки, где я нужен ---------- */

.att-row {
  display: flex; align-items: center; gap: 10px; cursor: pointer;
  padding: 7px 10px; border-radius: var(--r-sm);
  border-left: 4px solid var(--border-strong);
  background: var(--surface-2);
  transition: background-color var(--speed);
}
.att-row:hover { background: var(--surface-3); }
.att-row.is-stale { border-left-color: var(--red); }
.att-row.is-closing { border-left-color: var(--amber); }
.att-row__main { flex: 1; min-width: 0; }
.att-row__title { font-size: 13px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.att-row__meta { font-size: 11.5px; color: var(--text-faint); }
.att-row__sum { font-size: 12.5px; font-weight: 560; white-space: nowrap; }

/* ---------- лента «что произошло» ---------- */

.ev {
  display: grid; grid-template-columns: 22px minmax(0, 1fr); gap: 10px;
  cursor: pointer; padding: 4px; margin: -4px; border-radius: var(--r-sm);
  transition: background-color var(--speed);
}
.ev:hover { background: var(--surface-2); }
.ev__mark {
  width: 22px; height: 22px; border-radius: 50%;
  display: grid; place-items: center;
  background: var(--surface-2); color: var(--text-faint);
}
.ev--done .ev__mark { background: var(--green-soft); color: var(--green); }
.ev--stage .ev__mark { background: var(--accent-soft); color: var(--accent-text); }
.ev--lost .ev__mark { background: var(--red-soft); color: var(--red); }
.ev--note .ev__mark { background: var(--amber-soft); color: var(--amber); }
.ev__link {
  display: block; font-size: 12px; margin-top: 2px;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}


/* ---------- плотный список дел ---------- */

/* Высота строки ~34px: на 1440×900 в список помещается около двадцати дел
   вместо одиннадцати, которые влезали в таблицу с двумя строками текста. */
.trow {
  display: flex; align-items: center; gap: 10px;
  padding: 8px 16px; border-bottom: 1px solid var(--border);
  /* Межстрочный интервал сжат до 1.3: со стандартными 1.45 строка выходила
     на 38px, а нужны 34 — иначе на 1440×900 не набирается 18 дел. */
  line-height: 1.3;
  transition: background-color var(--speed);
}
.trow:last-child { border-bottom: 0; }
.trow:hover { background: var(--surface-2); }
.trow.is-done .trow__title { text-decoration: line-through; opacity: .6; }

.trow__main { flex: 1.4 1 0; min-width: 0; display: flex; align-items: center; gap: 7px; cursor: pointer; }
.trow__title { font-size: 13.5px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.trow__title.is-low { color: var(--text-soft); }
/* Высокий приоритет — точка перед названием. Цветное название читалось бы
   как ссылка, а бейдж «важно» в плотном списке занимает целую колонку. */
.trow__dot {
  display: inline-block; width: 6px; height: 6px; border-radius: 50%;
  background: var(--amber); margin-right: 6px; vertical-align: middle;
}
.trow__status { flex: 0 0 auto; }

/* Описание отдельной колонкой: в списке из двадцати строк оно помогает
   вспомнить, о чём дело, не открывая карточку. Обрезается многоточием,
   полный текст — во всплывающей подсказке. */
.trow__desc {
  flex: 1 1 0; min-width: 0; font-size: 12px; color: var(--text-soft);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.trow__link {
  width: 200px; flex: 0 0 200px; font-size: 12px; color: var(--text-faint);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
/* Колонка срока фиксированной ширины — иначе строки не выстраиваются в столбик.
   Но подсветка при наведении красится не по колонке, а по самому сроку:
   раньше серая плашка занимала все 96 пикселей и на узком экране, где соседние
   колонки скрыты, выглядела так, будто она наползла на название дела. */
.trow__due {
  width: 96px; flex: 0 0 96px;
  border: 0; background: none; cursor: pointer; padding: 0;
  display: flex; justify-content: flex-end; align-items: center;
}
.trow__due-text {
  padding: 2px 7px; border-radius: var(--r-sm);
  transition: background-color var(--speed);
}
.trow__due:hover .trow__due-text { background: var(--surface-3); }
.trow__who { width: 108px; flex: 0 0 108px; font-size: 12px; color: var(--text-soft); text-align: right; }

/* Инлайновый итог после закрытия дела. */
.trow__result { flex: 1; display: flex; gap: 6px; align-items: center; }
.trow__result .input { flex: 1; min-width: 0; padding: 4px 8px; font-size: 12.5px; }

.qcheck.is-checked {
  display: grid; place-items: center;
  border-color: var(--green); background: var(--green-soft); color: var(--green);
}
.queue-list.is-muted .trow { opacity: .7; }

@media (max-width: 1080px) {
  /* minmax(0, 1fr), а не 1fr: иначе колонка не может стать уже своего
     содержимого и растягивает страницу за край экрана. */
  .cols, .cols--dash, .entity { grid-template-columns: minmax(0, 1fr); }
  /* На узком экране колонки строки дела схлопываются: остаются название,
     срок и исполнитель — привязка уезжает, её видно в карточке. */
  .trow__link, .trow__desc { display: none; }
  .trow__who { width: 84px; flex-basis: 84px; }
  /* Показатели уходят под заголовок: втроём в строку с ним они не помещаются. */
  .page-head__actions { margin-left: 0; width: 100%; }
}

@media (max-width: 820px) {
  /* will-change поднимает меню на собственный слой композитора. Без него
     каждый кадр выезда перерисовывался кусок страницы под тенью меню —
     а тень тут в 34 пикселя размытия, и на чате под ней лежит слой с узором,
     который заново считает маску. Слой рисуется один раз, дальше только едет. */
  .sidebar {
    position: fixed; inset: 0 auto 0 0; z-index: 60; transform: translateX(-100%);
    transition: transform 160ms ease; box-shadow: var(--shadow-pop);
    will-change: transform;
  }
  .sidebar.is-open { transform: none; }
  /* На телефоне меню всегда полное — сворачивать там нечего. */
  .sidebar.is-collapsed { width: 180px; flex-basis: 180px; padding: 12px 10px; }
  .sidebar.is-collapsed .sidebar__logo,
  .sidebar.is-collapsed .nav__label,
  .sidebar.is-collapsed .user-chip__body { display: block; }
  .sidebar.is-collapsed .nav__item { justify-content: flex-start; padding: 8px 10px; gap: 10px; }
  .sidebar.is-collapsed .user-chip { justify-content: flex-start; padding: 7px 8px; gap: 10px; }
  .sidebar.is-collapsed .nav__badge {
    position: static; min-width: auto; height: auto; padding: 1px 7px;
    font-size: 11px; border-radius: 20px; margin-left: auto;
  }
  .sidebar__toggle { display: none; }
  .topbar > .topbar__burger { display: grid; }
  .topbar__actions .btn { padding: 6px 10px; font-size: 13px; }
  .view { padding: 64px 10px 28px; }
  .form-grid { grid-template-columns: 1fr; }
  .chat { grid-template-columns: 1fr; overflow: hidden; }
  .chat__list { display: none; }

  /* Переписка приезжает справа и уезжает туда же — шаг вглубь и обратно.
     transform, а не left: он считается видеокартой и не пересобирает
     раскладку ленты сообщений на каждом кадре. */
  .chat__main { transition: transform 200ms var(--ease); }
  .chat__main.is-entering { animation: chat-slide-in 240ms var(--ease); }
  .chat__main.is-leaving { transform: translateX(100%); }

  @keyframes chat-slide-in {
    from { transform: translateX(100%); }
    to { transform: translateX(0); }
  }

  @media (prefers-reduced-motion: reduce) {
    .chat__main, .chat__main.is-entering { transition: none; animation: none; }
  }
  .chat.show-list .chat__list { display: flex; }
  .chat.show-list .chat__main { display: none; }

  /* --- экран списка --- */

  /* Заголовок раздела вместо ряда кнопок: человек должен понимать, где он,
     не считывая это по содержимому. */
  .chat__list-title { display: block; flex: 1; min-width: 0; }
  .chat__list-name { font-size: 24px; font-weight: 660; letter-spacing: -.4px; }
  .chat__list-sub { font-size: 11.5px; color: var(--text-faint); margin-top: 1px; }
  .chat__list-btn { display: none; }
  .chat__list-icon { display: grid; width: 44px; height: 44px; }
  /* Плюс — единственное действие в шапке, с которого что-то начинается,
     поэтому он и единственный цветной. */
  .chat__list-add { color: var(--accent-text); }
  .chat__list-head { padding: 6px 14px 8px; align-items: flex-start; }

  /* Поиск открывается кнопкой: постоянное поле съедает строку списка,
     а ищут в чатах заметно реже, чем просто заходят. */
  /* Список чатов — на том же фиолетовом дыхании, что и переписка: ровный
     серый рядом с узорным фоном комнаты выглядел как незаконченный экран.
     Пятно уходит из верхнего левого угла вниз, шапка чуть плотнее — так
     заголовок отделяется от строк без лишней линии.

     Насыщенность нарочно мизерная: это фон под текстом, а не украшение.
     Проверял на строках с непрочитанными — акцентный бейдж по-прежнему
     читается как самый яркий элемент экрана. */
  .chat__list {
    background:
      radial-gradient(125% 75% at 0% 0%,
        color-mix(in srgb, var(--accent) 20%, transparent), transparent 58%),
      radial-gradient(95% 65% at 100% 100%,
        color-mix(in srgb, var(--accent) 12%, transparent), transparent 60%),
      linear-gradient(170deg,
        color-mix(in srgb, var(--accent) 6%, var(--chat-bg)), var(--chat-bg));
  }
  /* Строки поверх градиента — без своей заливки, чтобы фон просвечивал;
     подсветка активной и нажатой остаётся. */
  .room { background: transparent; }
  .room.is-active { background: color-mix(in srgb, var(--accent) 16%, transparent); }

  /* Поиск скрыт, пока не понадобился: постоянное поле съедает строку списка,
     а ищут в чатах реже, чем просто заходят. Открытый — часть колонки, а не
     наложение: у него своя заливка и линия снизу, иначе он просвечивал
     сквозь строки списка и выглядел как поверх интерфейса. */
  .chat__search { display: none; }
  .chat__list.search-open .chat__search {
    display: block; padding: 8px 14px 10px;
    background: var(--surface); border-bottom: 1px solid var(--border);
  }

  /* Лента фильтров: прокручивается только она, у страницы горизонтальной
     прокрутки быть не должно. */
  /* Лента тем уехала вниз, к большому пальцу: наверху она отжимала первую
     переписку на треть экрана, а переключают темы заметно реже, чем открывают
     чаты. Порядок задаём order — в разметке она по-прежнему идёт за шапкой,
     потому что на компьютере остаётся наверху. */
  /* display здесь не трогаем: выше по файлу список скрывается в переписке
     через .chat__list { display: none }, и повторное display: flex ниже
     перебивало это правило — переписка открывалась под списком, а экран
     оставался на месте. Колонкой список и так сделан в общем стиле. */
  /* flex: 0 0 auto обязателен обоим. Колонка списка ровно в высоту экрана,
     и при длинном списке flex сжимал шапку: коробка уезжала ниже своей
     высоты, а заголовок с подписью оставались прежними — строки чатов лезли
     поверх «всё прочитано». На пустой вкладке этого не было видно, потому
     что сжимать было нечего. */
  .chat__list-head { order: 1; flex: 0 0 auto; }
  .chat__search { order: 2; flex: 0 0 auto; }
  .chat__list-body { order: 3; flex: 1 1 auto; }
  /* Лента тем — последней, у самого низа экрана, над панелью разделов. */
  .chat__tabs {
    order: 4; flex: 0 0 auto;
    padding: 6px 10px 8px;
    border-top: 1px solid var(--border);
  }
  .chat__list .pipelines {
    flex: 0 0 auto; margin: 0; padding: 3px;
    background: var(--surface-2); border-radius: 12px;
    overflow-x: auto; scrollbar-width: none; flex-wrap: nowrap;
  }
  .chat__list .pipeline-tab { border-radius: 9px; }
  .chat__list .pipeline-tab.is-active { background: var(--surface); }
  .chat__list .pipelines::-webkit-scrollbar { display: none; }
  .chat__list .pipeline-tab { white-space: nowrap; padding: 6px 13px; }

  /* Строка списка: аватар 48, время справа, вторая строка — «Автор: текст».
     Непрочитанные подсвечиваются временем и бейджем, а не жирным именем:
     жирный шрифт в списке из двадцати строк читается как шум. */
  .room { grid-template-columns: 48px 1fr auto; gap: 12px; padding: 10px 14px; min-height: 68px; }
  /* flex-basis у .avatar задан вместе с шириной — без него аватар остаётся
     прежним, сколько ширину ни меняй. */
  .room .avatar, .room .presence .avatar {
    width: 48px; height: 48px; flex: 0 0 48px; font-size: 15px;
  }
  .room__name { font-size: 15.5px; font-weight: 580; }
  .room__last { font-size: 13.5px; }

  /* Кнопка «ответить» у каждого сообщения на телефоне не нужна: её заменяет
     долгое нажатие, а на узком экране она отъедает место у текста. */
  .msg__act { display: none; }

  /* --- экран переписки --- */

  .chat__head { gap: 6px; padding: 6px 8px; min-height: 58px; }
  .chat__back { display: grid; position: relative; width: 44px; height: 44px; }
  /* Число остальных непрочитанных рядом со стрелкой: видно, ждёт ли кто-то
     ещё, не выходя из переписки. */
  .chat__back-count {
    position: absolute; top: 4px; right: 2px;
    min-width: 18px; height: 18px; padding: 0 4px;
    border-radius: 20px; background: var(--accent); color: #fff;
    font-size: 10.5px; font-weight: 700; line-height: 18px; text-align: center;
  }
  .chat__head-more { display: grid; width: 44px; height: 44px; margin-left: auto; }
  .chat__head-info > .icon { display: none; }
  .chat__head-info .avatar { width: 36px; height: 36px; flex: 0 0 36px; font-size: 13px; }

  /* --- «О чате» отдельным экраном --- */

  .chat__side {
    position: absolute; inset: 0; z-index: 30;
    border-left: 0; background: var(--bg);
    /* Экран открывается поверх переписки, у которой отступ под вырез уже
       учтён выше по дереву. Своего у него не было — стрелка «назад» и
       заголовок налезали на часы и вырез. */
    padding-top: env(safe-area-inset-top);
  }
  .chat__side-back { display: grid; width: 44px; height: 44px; }
  .chat__side-close { display: none; }
  .chat.has-side { grid-template-columns: 1fr; }
  .msg { max-width: 92%; }

  /* Горизонтальной прокрутки в приложении быть не должно: на телефоне это
     всегда ошибка вёрстки, а не способ уместить содержимое. */
  .btn__wide { display: none; }
  .btn__narrow { display: inline; }

  /* Показатели в три равные колонки: во flex они держали минимальную ширину
     и распирали страницу шире экрана. */
  .metrics { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 8px; }
  .metric { min-width: 0; padding: 8px 10px; }
  .metric__value { font-size: 17px; }
  .metric__label, .metric__hint { font-size: 11px; }

  /* Пункты меню и кнопки-иконки под палец: 36 и 32 пикселя — это промах
     мимо цели, минимум по любым рекомендациям — 44. */
  .nav__item { padding: 11px 10px; }
  .icon-btn { padding: 10px; }
  /* Рисунок чекбокса остаётся мелким — иначе он перетянет на себя всю строку, —
     но область нажатия расширяется до 44px невидимым слоем поверх.
     То же для аватара в шапке: 30 пикселей пальцем не берутся. */
  .qcheck { width: 22px; height: 22px; flex-basis: 22px; position: relative; }
  .qcheck::after { content: ""; position: absolute; inset: -11px; }
  .topbar__avatar { position: relative; }
  .topbar__avatar::after { content: ""; position: absolute; inset: -7px; }

  /* Стадии сделки прокручиваются вбок внутри своей карточки, а не ломают экран. */
  .card__body > .row { overflow-x: auto; flex-wrap: nowrap; }

  /* --- процессы на телефоне: стадия сверху, список под ней --- */

  .board { display: block; overflow: visible; padding-bottom: 0; }

  /* Лента стадий: активная залита цветом стадии, у остальных цвет только
     в счётчике — иначе пять разноцветных пилюль подряд читаются как гирлянда. */
  .stage-picker {
    display: flex; gap: 6px; margin-bottom: 10px;
    overflow-x: auto; scrollbar-width: none;
  }
  .stage-picker::-webkit-scrollbar { display: none; }
  .stage-pill {
    flex: 0 0 auto; display: flex; align-items: center; gap: 7px;
    padding: 8px 12px; border: 0; border-radius: 999px; cursor: pointer;
    background: var(--surface-2); color: var(--text-soft);
    font-size: 13px; font-weight: 540; white-space: nowrap;
  }
  .stage-pill__count {
    min-width: 20px; padding: 0 5px; border-radius: 999px;
    background: var(--surface-3); color: var(--text-faint);
    font-size: 11.5px; font-weight: 700; text-align: center;
  }
  .stage-pill.is-active { background: var(--stage); color: #fff; }
  .stage-pill.is-active .stage-pill__count { background: rgba(255, 255, 255, .22); color: #fff; }

  .stage-sum { font-size: 12px; color: var(--text-faint); margin-bottom: 8px; padding: 0 2px; }
  .stage-deals { display: grid; gap: 8px; }
  /* Карточка на всю ширину: в списке ей больше не с кем делить строку. */
  .stage-deals .deal-card { width: 100%; cursor: pointer; }

  /* Исполнитель из строки убран: на телефоне список открыт на своих делах,
     и колонка «Роман К.» повторялась в каждой строке, отнимая треть ширины
     у названия. Кому поручено — видно в карточке и в фильтре сверху. */
  .trow__who { display: none; }
  .trow { padding: 10px 14px; gap: 8px; }
  .trow__title { font-size: 14px; white-space: normal; }
  .trow__main { flex: 1 1 auto; }

  /* Название забирает строку целиком, плашка статуса уходит под него. На узком
     экране плашка стоит рядом и не сжимается (flex: 0 0 auto), поэтому весь
     недостаток ширины доставался названию: «Согласовать макет упаковки…»
     ужималось до шести десятков пикселей и переставало читаться. Название
     здесь главное — статус подождёт следующей строки. */
  .trow__main { flex-wrap: wrap; }
  .trow__title { flex: 1 1 100%; }

  /* Строка очереди: «Перенести» в подпись срока не влезает, а срок и так
     кликабелен в списке дел — здесь оставляем только его. */
  .qrow { padding: 10px 12px; }
  .qrow__move { display: none; }
  .queue-group__hint { display: none; }

  /* Таблицы шире экрана в принципе: у клиентов пять столбцов, у сотрудников
     семь. Пусть прокручиваются внутри своей карточки — это честнее, чем
     сжимать колонки до нечитаемости. */
  .card:has(> .table) { overflow-x: auto; }

  /* Окно по центру видимой области, а не layout-окна. Отступы считаются от
     безопасных зон: без них заголовок окна уходил под вырез, а кнопки
     «Отмена / Сохранить» — под полосу жестов. */
  .modal-root {
    padding: calc(env(safe-area-inset-top) + 12px) 12px
             calc(env(safe-area-inset-bottom) + 12px);
    height: var(--vvh, 100dvh);
  }
  .modal { max-height: 100%; width: 100%; }

  .page-head { gap: 8px; }
  .page-head__actions { width: 100%; margin-left: 0; }
  .page-head__actions .input, .page-head__actions select { width: 100% !important; }

  /* Чат живёт в своём масштабе. Общий --ui-scale 1.15 раздувал поле ввода
     и пузырьки: размеры чата подобраны в физических пикселях под 390, и
     умножение на 1.15 делало композер вдвое толще телеграмовского.
     «Крупный шрифт» на чат по-прежнему действует. */
  /* Чат чуть крупнее единицы: размеры подбирались под макет в 390 пикселей и
     на живом экране оказались мелковаты — особенно текст сообщений и время.
     1.15, как у остального интерфейса, наоборот раздувало поле ввода, поэтому
     свой множитель. */
  .app.is-chat .view { zoom: var(--chat-scale, 1.1); }
  :root.is-large-ui { --chat-scale: 1.25; }

  /* В разделе «Чат» шапки нет, и без этого отступа вкладки «Личные / Группы /
     Задачи» залезали под часы и вырез айфона. */
  /* Раньше вырез просто отодвигали отступом, и над содержимым оставалась
     полоса голого фона — на других вкладках её закрывает шапка, и вопросов
     не возникало. Теперь то же и здесь: вырез закрашивают сами шапки чата. */
  .app.is-chat .view { padding-top: 0; }
  /* Шапка чата — та же, что у остальных разделов: то же стекло, то же
     размытие, та же линия. Раньше она была плотным --surface и на фоне
     градиента выглядела чужой панелью, приклеенной сверху. */
  .chat__list-head, .chat__head {
    padding-top: calc(env(safe-area-inset-top) / var(--chat-scale, 1.1) + 6px);
    background: var(--glass);
    -webkit-backdrop-filter: var(--glass-blur);
    backdrop-filter: var(--glass-blur);
    border-bottom: 1px solid var(--glass-line);
  }

  /* Пока клавиатура открыта, каркас живёт в видимой высоте, а не в layout-окне.
     Так сжимается сам контейнер, а всё, что внутри, — плашка, лента, поле
     ввода — остаётся на своих местах и не уезжает под клавиатуру. Правило
     общее, не только для чата: формы и карточки страдали тем же.

     Высоту берём готовым числом от visualViewport (--vvh), а не 100dvh: dvh
     учитывает панели браузера, но про клавиатуру не знает вовсе. */
  /* Полоса внизу, где жестовая черта. Кто её красит — корень, тело страницы
     или система за границей окна — зависит от версии iOS и от того, накрыл ли
     webview безопасную зону. Поэтому закрашиваем всеми способами сразу:
     фон корня, фон тела и физическая заглушка поверх самой зоны. Проще один
     раз перекрыть все варианты, чем гадать по скриншотам, какой сработал. */
  html, body { background: var(--surface); }
  body::after {
    content: ""; position: fixed; left: 0; right: 0; bottom: 0; z-index: 0;
    height: env(safe-area-inset-bottom);
    background: var(--surface); pointer-events: none;
  }

  /* Документ прибит к экрану. Без этого Сафари при фокусе в поле утягивает
     всю страницу вверх — на скриншотах поле ввода оказывалось у самых часов,
     а переписка исчезала. position: fixed у body лишает браузер самой
     возможности прокрутить документ, и сдвигать становится нечего.
     Прокрутка остаётся там, где ей и место: внутри ленты и списков. */
  html, body {
    overflow: hidden; overscroll-behavior: none;
    position: fixed; inset: 0; width: 100%;
  }

  /* Каркас всегда равен видимой высоте, а не layout-окну — и без клавиатуры
     тоже. Так было надёжнее: правило, включавшееся только по классу
     html.kb-open, зависело от того, успел ли скрипт распознать клавиатуру, и
     на айфоне в режиме приложения не срабатывало — экран оставался прежней
     высоты, а композер уезжал под клавиатуру.

     Теперь источник один: --vvh, который скрипт обновляет на каждое изменение
     визуального окна. Клавиатура появилась — окно стало ниже — каркас сжался,
     и плашка с полем ввода сами встали над ней, потому что оба лежат в потоке. */
  .app { height: var(--vvh, 100dvh); }

  /* Место под полосу жестов нужно, только пока её видно. Клавиатура занимает
     это место сама, и лишний отступ поднимал бы поле ввода над её краем. */
  /* В переписке нижней панели нет, и зону полосы жестов закрашивает поле
     ввода — тем же цветом, что и панель в остальных разделах. */
  .chat__composer { padding-bottom: calc(6px + env(safe-area-inset-bottom)); }
  /* Клавиатура занимает место полосы жестов сама — запас становится лишним. */
  html.kb-open .chat__composer { padding-bottom: 8px; }

  /* Пока клавиатура открыта, нижняя панель разделов не нужна: она занимает
     ровно ту полосу, куда встаёт поле ввода. */
  html.kb-open .tabbar { display: none; }
  /* В списке чатов панель видна, а список тянется во всю высоту — без запаса
     последняя переписка пряталась под панелью. В самой переписке панели нет,
     там запас не нужен. */
  /* Запаса под нижнюю панель не нужно: она давно в общем потоке, а не поверх
     содержимого, и список заканчивается ровно над ней. */

  /* Кнопку создания в заголовке раздела убираем: то же самое делает круглая
     кнопка над панелью, а две одинаковые кнопки на экране только путают. */
  .page-head__actions .btn--primary { display: none; }

  /* Горизонтальная прокрутка разрешена только лентам фильтров и стадий.
     У рабочей области её быть не должно: содержимое, вылезшее за край (длинное
     название сделки, широкая карточка), утягивало вбок весь экран вместе с
     шапкой, и страница «гуляла». Прячем переполнение здесь, а не подпираем
     каждый список по отдельности. */
  .view { overflow-x: hidden; overflow-y: auto; }
  .board { overflow-x: hidden; }
  .deal-card, .stage-deals .deal-card { max-width: 100%; }
  .deal-card__title, .deal-card__client { overflow-wrap: anywhere; }

  /* Ряд переключателей не должен схлопываться. .view — колонка flex, а у лент
     внутри стоит overflow-x: auto, из-за чего их минимальная высота обнуляется
     и flex сжимает весь ряд в ноль: вкладки «Дела / Задачи» были в разметке,
     занимали место, но не показывались вовсе. */
  .view > .filter-bar, .view > .pipelines {
    flex: 0 0 auto;
    /* Ширину задаём явно. Лента направлений — flex-элемент колонки, и по
       содержимому она растягивалась до 407 пикселей на экране в 375:
       прокручивалась не сама лента, а рабочая область под ней. */
    width: 100%; max-width: 100%; min-width: 0;
    overflow-x: auto; scrollbar-width: none;
  }
  .view > .filter-bar::-webkit-scrollbar { display: none; }
  /* Внутренние группы не прокручиваются по отдельности и не сжимаются:
     иначе «Задачи» и «Выполненные» обрезались на середине слова, каждая
     в своём окошке. Едет весь ряд целиком. */
  .filter-bar > * { flex: 0 0 auto; overflow: visible; }
  .filter-bar .pipelines { overflow: visible; width: auto; max-width: none; }

  /* Переключатели раздела едут одной прокручиваемой лентой: двумя блоками
     они занимали пол-экрана до первой строки списка. */
  .pipelines, .filter-bar {
    flex-wrap: nowrap; overflow-x: auto; scrollbar-width: none;
    -webkit-overflow-scrolling: touch;
  }
  .filter-bar::-webkit-scrollbar { display: none; }

  .pipelines::-webkit-scrollbar { display: none; }
  .pipeline-tab { flex: 0 0 auto; }

  /* Фильтры в шапке раздела: поиск на всю ширину, выпадающие списки по двое
     в ряд. Раньше каждый занимал свою строку, и до списка нужно было
     проматывать половину экрана. */
  .page-head__actions { display: flex; flex-wrap: wrap; gap: 8px; }
  .page-head__actions > .input:not(select) { flex: 1 1 100%; }
  /* Каждый список на своей строке. По двое в ряд им доставалось по 45%
     ширины, и «Роман Куприянов» превращался в «Роман Куприян», а «Все
     открытые дела» — в «Все открытые»: выбранное значение прочитать нельзя,
     а это единственное, ради чего фильтр и показывают. */
  .page-head__actions > select { flex: 1 1 100%; width: 100% !important; min-width: 0; }

  /* Показатели: подписи в две-три строки при крупном масштабе распирали
     карточки по-разному. Мельче кегль и одинаковая высота у всех трёх. */
  .metrics { align-items: stretch; }
  .metric { display: flex; flex-direction: column; justify-content: flex-start; }
  .metric__value { font-size: 19px; line-height: 1.15; }
  .metric__label { font-size: 11px; line-height: 1.2; }
  .metric__hint { font-size: 10.5px; line-height: 1.2; }

  /* viewport-fit=cover пускает страницу под вырез и под полосу жестов.
     Без этих отступов шапка уезжает под «чёлку», а нижний край содержимого
     оказывается под полосой, по которой смахивают приложение. */
  /* Шапка чуть выше, а содержимое начинается не вплотную под ней: было 10
     пикселей зазора, и весь экран выглядел задранным вверх. Стало 26 —
     заголовок раздела получает воздух и читается как заголовок, а не как
     продолжение шапки. */
  .topbar { height: calc(56px + env(safe-area-inset-top)); padding-top: env(safe-area-inset-top); }
  /* Деление на масштаб обязательно: .view отмасштабирован, и без него отступ
     под шапку и над панелью растёт вместе с кеглем, хотя сами шапка и панель
     остаются прежней высоты. */
  /* 74 при шапке в 56 — 18 пикселей воздуха между шапкой и заголовком раздела.
     Пробовал 118: заголовок вставал по центру пустоты, и на экране помещалось
     на две строки списка меньше — на 844 пикселях это заметная потеря. */
  .view { padding-top: calc((74px + env(safe-area-inset-top)) / var(--ui-scale, 1)); }
  .view { padding-bottom: calc((28px + var(--safe-bottom)) / var(--ui-scale, 1)); }
  .sidebar { padding-top: calc(12px + env(safe-area-inset-top)); }
  .sidebar__foot { padding-bottom: env(safe-area-inset-bottom); }
  /* Отступ под полосу жестов держит ровно один элемент — поле ввода.
     Здесь раньше стояло правило, добавлявшее его ещё и .chat__main поверх
     собственного отступа композера: под полем ввода набегало под сотню
     пустых пикселей, и именно они выглядели дырой внизу экрана. */
}

/* Там, где нет мыши, наведение не наступает никогда: кнопки, показанные
   только по hover, на телефоне недоступны в принципе. */
/* Строки с контекстным меню: на сенсорном экране долгое нажатие иначе
   отдаётся системе — iOS показывает лупу и выделение текста поверх нашего
   меню. Ограничено сенсорными: за компьютером текст в строках выделять можно. */
@media (hover: none) {
  .has-menu {
    -webkit-touch-callout: none;
    -webkit-user-select: none;
    user-select: none;
  }
}

@media (hover: none) {
  .qrow__move { opacity: 1; }
}

/* iOS увеличивает страницу, когда фокус попадает в поле мельче 16px.
   Выглядит как самопроизвольный зум и ломает вёрстку — поэтому на телефоне
   поля ровно 16px, а не 14. */
@media (max-width: 820px) {
  .input, select.input, textarea.input { font-size: 16px; }
}

@media (prefers-reduced-motion: reduce) {
  * { transition: none !important; animation: none !important; }
}

/* ---------- экран запуска ---------- */

/* Лежит поверх всего до первой отрисовки маршрута. Цвета — из переменных,
   тема выставлена инлайновым скриптом, поэтому вспышки чужого фона нет. */
.splash {
  position: fixed; inset: 0; z-index: 200;
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: 10px; color: var(--text);
  /* Заставка уходит в цвет панели у нижнего края. Служебную полосу под
     страницей система красит цветом панели ещё до того, как страница
     нарисуется, и на ровном тёмном фоне заставки этот стык был виден
     светлой ступенькой. Градиент на последней четверти экрана прячет шов:
     сверху родной фон, внизу ровно тот же тон, что у полосы. */
  background: linear-gradient(180deg,
    var(--bg) 0%, var(--bg) 72%, var(--surface) 100%);
  transition: opacity 160ms var(--ease);
}
/* Растворение, а не мгновенное исчезновение: интерфейс должен проступить
   сквозь заставку, а не подменить её кадром. */
.splash.is-gone { opacity: 0; pointer-events: none; transform: scale(1.04); }
/* На компьютере заставки нет вовсе — см. скрипт в index.html. */
:root.no-splash .splash { display: none; }
.splash { transition: opacity 560ms var(--ease), transform 560ms var(--ease); }

.splash__lockup { display: flex; align-items: flex-end; gap: 12px; }
.splash__mark { color: var(--accent); display: block; }

/* Порядок: сначала плашки выходят снизу и продолжают ходить шатунами,
   через полсекунды из-под знака выезжает название, следом подпись. */
.splash__mark rect { animation: flow-in 860ms cubic-bezier(.22, .7, .2, 1) both; }
.splash__mark rect:nth-child(2) { animation-delay: 170ms; }
.splash__mark rect:nth-child(3) { animation-delay: 340ms; }
.splash.is-idle .splash__mark rect { animation: flow-piston 2.6s ease-in-out infinite; }
.splash.is-idle .splash__mark rect:nth-child(2) { animation-delay: .34s; }
.splash.is-idle .splash__mark rect:nth-child(3) { animation-delay: .68s; }

@keyframes splash-slide {
  from { opacity: 0; transform: translateX(-14px); }
  to { opacity: 1; transform: none; }
}
.splash__word {
  font-family: "Onest", var(--font); font-weight: 800; font-size: 56px;
  line-height: .983; letter-spacing: -.02em; position: relative; top: -.087em;
  animation: splash-slide 700ms var(--ease) 760ms both;
}
.splash__sub { animation: splash-slide 700ms var(--ease) 1080ms both; font-size: 13px; }
.splash__dot {
  display: inline-block; width: 11px; height: 11px; border-radius: 50%;
  background: var(--accent); margin-left: 4px;
}
.splash__sub { font-size: 12.5px; color: var(--text-faint); }
.splash__fail {
  display: flex; flex-direction: column; align-items: center; gap: 12px;
  margin-top: 18px; font-size: 13.5px; color: var(--text-soft);
}

/* ---------- навигация внизу (телефон) ---------- */

/* Шире 820px её нет вовсе: там работает меню слева. */
.tabbar { display: none; }
.fab { display: none; }

@media (max-width: 820px) {
  /* Постоянная панель вместо выдвижного меню: переход в любой раздел —
     одно касание, а не два, и экран при этом не перекрывается. */
  /* Панель — часть каркаса, а не всплывающий слой: строка в колонке рядом с
     рабочей областью. Раньше она была position: fixed и пряталась при прокрутке
     вниз; и то и другое давало ощущение, что панель ездит вместе со списком.
     В потоке она физически не может сдвинуться, а .view сам получает ровно ту
     высоту, что осталась, — вычитать её высоту отступом больше не нужно. */
  .app { flex-direction: column; }
  .tabbar {
    flex: 0 0 auto; z-index: 40;
    /* Столбцы по числу вкладок, а не жёсткие пять: раздел клиентов с телефона
       убран, и на пяти колонках оставалась пустая ячейка. */
    display: grid; grid-auto-flow: column; grid-auto-columns: 1fr;
    /* Плотный фон, а не стекло. Полупрозрачная панель поверх страницы того же
       цвета неотличима от самой страницы: полоса под подписями читалась как
       «панель висит, а под ней виден фон», хотя это была её собственная
       заливка над полосой жестов.
       Заливка доходит до нижнего края экрана — иначе щель появляется снова. */
    background: var(--surface);
    border-top: 1px solid var(--border);
    /* Панель больше не обязана сама закрашивать всю зону жестов — под ней
       лежит заглушка body::after того же цвета. Поэтому оставляем клиренс
       над жестовой чертой, а не все её 34 пикселя: содержимое опускается
       ниже, панель выглядит тоньше, а цвет по-прежнему доходит до края. */
    padding-bottom: max(2px, calc(env(safe-area-inset-bottom) - 22px));
  }

  .tabbar__item {
    display: flex; flex-direction: column; align-items: center; justify-content: center;
    gap: 2px; min-height: 42px; padding: 5px 2px 1px;
    border: 0; background: none; cursor: pointer;
    color: var(--text-faint); font-size: 10px; font-weight: 500; letter-spacing: -.1px;
  }
  /* Ни заливок, ни «пилюль»: активный раздел показываем цветом и весом —
     на пяти ячейках подряд любая плашка выглядит шумом. */
  /* Подписи вернулись — по одному рисунку раздел угадывается не всегда,
     особенно «Процессы» и «Дела». Иконки при этом крупнее: 28 вместо 25. */
  .tabbar__label { display: block; font-size: 10px; line-height: 1.1; }
  .tabbar__item .icon { stroke-width: 1.8; width: 24px; height: 24px; }
  .tabbar__item.is-active { color: var(--accent-text); font-weight: 600; }
  .tabbar__item.is-active .icon { stroke-width: 2.1; }

  .tabbar__icon { position: relative; display: grid; place-items: center; }
  /* Обводка цветом фона отделяет бейдж от иконки, иначе на тонких линиях
     они сливаются в одно пятно. Размеры и посадка — из макета. */
  .tabbar__badge {
    position: absolute; top: 1px; left: calc(50% + 7px);
    min-width: 17px; height: 17px; padding: 0 5px;
    border-radius: 20px; border: 2px solid var(--bg);
    display: grid; place-items: center;
    font-size: 10.5px; font-weight: 700; line-height: 1;
  }
  .tabbar__badge--accent { background: var(--accent); color: #fff; }
  /* Красный у нас яркий, белым по нему читается хуже, чем цветом фона. */
  .tabbar__badge--red { background: var(--red); color: var(--bg); }

  /* Меню слева и бургер на телефоне не нужны совсем. */
  .sidebar, .topbar > .topbar__burger { display: none !important; }

  /* Отступ снизу — только воздух под последней строкой. Высоту панели и
     полосу жестов вычитать больше не надо: панель стоит в потоке и забирает
     своё место сама. */
  .view { padding-bottom: calc(16px / var(--ui-scale, 1)); }

  /* Последний блок растягивается до низа экрана. Иначе на коротком содержимом
     («Клиентов не найдено», «Дел не найдено») карточка заканчивалась высоко,
     а до панели оставалась мёртвая полоса в треть экрана — читалось как
     огромный отступ, хотя это была просто пустота. */
  .view > .card:last-child,
  .view > .cols:last-child { flex: 1 1 auto; }
  .view > .card:last-child > .empty { display: grid; place-content: center; min-height: 100%; }
  .toasts { bottom: calc(62px + var(--safe-bottom)); }

  /* В переписке панель мешает: под ней поле ввода, и два ряда кнопок подряд
     съедают пол-экрана. На списке чатов она нужна, поэтому смотрим не на
     раздел, а на то, открыта ли конкретная комната. */
  .app.is-chat-room .tabbar { display: none; }

  /* Одна кнопка вместо двух primary в шапке. */
  .fab {
    position: fixed; right: 16px; z-index: 41;
    bottom: calc(52px + 16px + var(--safe-bottom));
    width: 52px; height: 52px; border-radius: 50%;
    border: 0; cursor: pointer;
    display: grid; place-items: center;
    background: var(--grad-accent); color: var(--on-accent);
    box-shadow: var(--shadow-pop);
  }
  .fab.hidden { display: none; }
  .app.is-chat .fab { display: none; }
  .app.is-chat-room .view { padding-bottom: 0; }
  .topbar__actions .btn--primary { display: none; }

  /* Лист снизу вместо диалога по центру: на телефоне окно посередине
     оказывается либо под клавиатурой, либо под пальцем. */
  .modal--sheet {
    position: fixed; left: 0; right: 0; bottom: 0; top: auto;
    width: 100%; max-width: none; transform: none;
    border-radius: var(--r-lg) var(--r-lg) 0 0;
    padding-bottom: env(safe-area-inset-bottom);
    max-height: 86vh;
  }
  /* Ручка-полоска: показывает, что лист приехал снизу и закрывается вниз. */
  .modal--sheet::before {
    content: ""; display: block; width: 36px; height: 4px; border-radius: 3px;
    background: var(--border-strong); margin: 8px auto 0;
  }
}

/* Аватар и знак в шапке — только на телефоне. На широком экране для профиля
   есть карточка в подвале меню, а логотип стоит в самом меню. */
.topbar__avatar { display: none; border: 0; cursor: pointer; }
.topbar__logo { display: none; color: var(--accent); }
@media (max-width: 820px) {
  .topbar__avatar { display: inline-grid; }
  .topbar__logo { display: grid; place-items: center; flex: 0 0 auto; }
}

/* Плитки вложений: четыре сценария в один ряд, зона нажатия 56 — палец
   попадает не целясь. */
.attach-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 10px; }
.attach-tile {
  display: grid; justify-items: center; gap: 7px;
  padding: 12px 4px; border: 0; border-radius: var(--r);
  background: var(--surface-2); color: var(--text); cursor: pointer;
  font-size: 12px;
}
.attach-tile__icon {
  width: 56px; height: 56px; border-radius: 50%;
  display: grid; place-items: center;
  background: var(--accent-soft); color: var(--accent-text);
}

/* Быстрые реакции над листом действий — то, что нажимают чаще всего,
   должно быть под большим пальцем и не требовать чтения. */
.msg-sheet__reacts { display: flex; gap: 10px; padding-bottom: 4px; }
.msg-sheet__react {
  width: 44px; height: 44px; border-radius: 50%;
  border: 1px solid var(--border); background: var(--surface-2);
  color: var(--text); cursor: pointer; display: grid; place-items: center;
}
.msg-sheet__react:hover { border-color: var(--accent); color: var(--accent-text); }

/* Микрофон и отправка живут в одной ячейке и подменяют друг друга. */
.chat__act-cell { display: grid; }
.chat__act-cell > * { grid-area: 1 / 1; }

.sheet-row {
  display: flex; align-items: center; justify-content: space-between;
  width: 100%; min-height: 44px; padding: 10px 2px;
  border: 0; background: none; cursor: pointer;
  color: var(--text); font-size: 14px; text-align: left;
  border-top: 1px solid var(--border);
}
.sheet-row:hover { color: var(--accent-text); }


/* ---------- заглушки на время загрузки ---------- */

/* Вместо крутящегося кружка показываем форму того, что грузится: колонки
   канбана, строки списка, плитки показателей. Человек видит будущий экран
   раньше данных и не гадает, сломалось оно или думает.

   Блик пробегает слева направо — это единственная анимация, которую здесь
   можно оправдать: она сообщает «идёт работа», а не украшает. При
   prefers-reduced-motion остаётся ровная заливка. */
.skel {
  position: relative; overflow: hidden;
  background: var(--surface-2);
  border-radius: var(--r-sm);
}
.skel::after {
  content: ""; position: absolute; inset: 0;
  transform: translateX(-100%);
  background: linear-gradient(90deg, transparent, var(--skel-glare), transparent);
  animation: skel-run 1.3s var(--ease) infinite;
}
/* Блики соседних заглушек намеренно не синхронны: одинаковая волна по всему
   экрану читается как один большой объект, а не как несколько блоков. */
.skel:nth-child(2n)::after { animation-delay: .12s; }
.skel:nth-child(3n)::after { animation-delay: .24s; }

@keyframes skel-run { to { transform: translateX(100%); } }

@media (prefers-reduced-motion: reduce) {
  .skel::after { animation: none; }
}

.skel--text { height: 12px; border-radius: 6px; }
.skel--title { height: 15px; border-radius: 6px; width: 45%; }
.skel--pill { height: 28px; border-radius: 999px; }
.skel--avatar { border-radius: 50%; flex: 0 0 auto; }

/* Карточка-заглушка повторяет карточку сделки: рамка и тень на месте,
   внутри — полосы вместо текста. Без рамки список выглядел бы серой кашей. */
.skel-card {
  background: var(--surface); border: 1px solid var(--border);
  border-radius: var(--r-sm); padding: 10px 11px;
  display: grid; gap: 8px;
}
.skel-row {
  display: flex; align-items: center; gap: 10px;
  padding: 9px 12px; border-bottom: 1px solid var(--border);
}
.skel-metrics { display: grid; grid-template-columns: repeat(auto-fit, minmax(180px, 1fr)); gap: 12px; }

/* ---------- напоминания задачи ---------- */
.reminders { display: grid; gap: 8px; }
.reminders__list:empty { display: none; }
.reminders__list { display: flex; flex-wrap: wrap; gap: 6px; }
.reminders__add { display: flex; flex-wrap: wrap; gap: 6px; }
.reminder-chip {
  display: inline-flex; align-items: center; gap: 6px;
  padding: 4px 6px 4px 10px; border-radius: 20px;
  background: var(--accent-soft); color: var(--accent-text);
  font-size: 12.5px; font-weight: 520;
}
.reminder-chip__x {
  display: grid; place-items: center; width: 18px; height: 18px; padding: 0;
  border: 0; border-radius: 50%; cursor: pointer;
  background: transparent; color: inherit;
}
.reminder-chip__x:hover { background: rgba(0, 0, 0, .12); }
/* Сработавшее напоминание не убираем из списка: видно, что о задаче уже
   напоминали, — но и не выделяем, оно позади. */
.reminder-chip.is-fired { background: var(--surface-2); color: var(--text-faint); }

/* ---------- привязка дела к сделке или клиенту ---------- */

/* Поле составное: сверху то, к чему привязано сейчас, ниже строка поиска и
   подсказки. Подсказки лежат в потоке, а не всплывают: форма и так в модалке,
   и второй слой поверх неё на телефоне перекрывает половину полей. */
.crm-link { display: grid; gap: 7px; }
.crm-link__current { min-height: 26px; display: flex; align-items: center; }
.crm-link__empty { font-size: 12.5px; color: var(--text-faint); }
.crm-link__chip {
  display: inline-flex; align-items: center; gap: 7px; max-width: 100%;
  padding: 4px 6px 4px 10px; border-radius: 20px;
  background: var(--accent-soft); color: var(--accent-text);
  font-size: 12.5px; font-weight: 520;
}
.crm-link__label { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.crm-link__meta { color: var(--text-faint); font-weight: 450; }
.crm-link__x {
  display: grid; place-items: center; width: 18px; height: 18px; padding: 0;
  border: 0; border-radius: 50%; cursor: pointer;
  background: transparent; color: inherit; flex: 0 0 auto;
}
.crm-link__x:hover { background: rgba(0, 0, 0, .12); }

.crm-link__results {
  max-height: 210px;
  border: 1px solid var(--border); border-radius: var(--r-sm);
  padding: 4px; margin: 0;
}
.crm-link__none { padding: 8px 10px; font-size: 12.5px; color: var(--text-faint); }

/* ---------- аватарки картинкой ----------

   Инициалы остаются под фотографией: пока она грузится — а в списке чатов их
   сразу два десятка, — кружок не должен быть пустым. Картинка ложится поверх
   в absolute, поэтому смены размера кружка не происходит и строка не дёргается. */

.avatar--photo { position: relative; overflow: hidden; }
.avatar--photo img {
  position: absolute; inset: 0; width: 100%; height: 100%;
  object-fit: cover; border-radius: inherit;
}
.avatar--xl { width: 72px; height: 72px; flex-basis: 72px; font-size: 24px; }

/* Карандаш сидит на кромке кружка и слегка выступает наружу: внутри он
   закрывал бы лицо, а рядом — перестал бы читаться как «поменять вот это». */
.avatar-editor { position: relative; display: inline-block; line-height: 0; }
.avatar-editor__pen {
  position: absolute; right: -2px; bottom: -2px;
  width: 26px; height: 26px; padding: 0;
  display: grid; place-items: center; cursor: pointer;
  border-radius: 50%; border: 2px solid var(--surface);
  background: var(--surface-3); color: var(--text);
  transition: background .12s ease, color .12s ease;
}
.avatar-editor__pen:hover { background: var(--accent); color: #fff; }
.avatar-editor.is-busy { opacity: .55; pointer-events: none; }

/* ---------- вставленное из буфера, до отправки ----------

   Полоса над полем ввода: миниатюры того, что вставили, каждая со своим
   крестиком. Поле остаётся на месте — подпись набирают в нём же, а отправляет
   обычная отправка. */

.composer-paste {
  display: flex; gap: 10px; flex-wrap: wrap; align-items: center;
  padding: 10px 12px; border-top: 1px solid var(--border); background: var(--surface-2);
}
.paste-chip { position: relative; line-height: 0; }
.paste-chip__img {
  display: block; width: 62px; height: 62px; object-fit: cover;
  border-radius: var(--r-sm); border: 1px solid var(--border);
}
.paste-chip__file {
  display: inline-flex; align-items: center; gap: 6px; max-width: 220px;
  padding: 9px 11px; border-radius: var(--r-sm); border: 1px solid var(--border);
  background: var(--surface); font-size: 12.5px; line-height: 1.2;
}
.paste-chip__file span { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.paste-chip__drop {
  position: absolute; top: -7px; right: -7px; width: 21px; height: 21px; padding: 0;
  display: grid; place-items: center; cursor: pointer;
  border-radius: 50%; border: 1px solid var(--border);
  background: var(--surface-3); color: var(--text);
  transition: background .12s ease, color .12s ease;
}
.paste-chip__drop:hover { background: var(--red); color: #fff; border-color: var(--red); }

/* ---------- раздел «Результаты» ---------- */

/* Подпись над плитками: слева чьи это числа, справа — за какой период.
   Без неё «выиграно 3,2 млн» одинаково выглядит и за день, и за квартал. */
.section-line {
  display: flex; align-items: baseline; gap: 10px;
  margin: 2px 0 10px; font-size: 13px; font-weight: 550;
}
.section-line .faint { font-weight: 450; color: var(--text-faint); }

/* Таблицы отделов и людей: строки некликабельные — открывать нечего,
   поэтому убираем курсор-руку, который обещает переход. */
.results-table tbody tr { cursor: default; }
.results-table td.num { font-variant-numeric: tabular-nums; }

/* Текст отчёта показываем как есть, моноширинным: это готовое сообщение,
   которое уйдёт в чат, и перенос строк в нём значащий. */
.report-preview {
  margin: 0; padding: 12px 14px; max-height: 46vh; overflow: auto;
  background: var(--surface-2); border: 1px solid var(--border);
  border-radius: var(--r-sm);
  font: 12.5px/1.55 var(--font); white-space: pre-wrap; word-break: break-word;
  color: var(--text);
}

.note-line {
  display: flex; align-items: center; gap: 8px;
  padding: 8px 10px; border-radius: var(--r-sm);
  background: var(--amber-soft); color: var(--amber); font-size: 12.5px;
}

.report-row {
  display: flex; align-items: center; gap: 12px; width: 100%;
  border: 0; background: none; cursor: pointer; text-align: left;
  padding: 9px 10px; border-radius: var(--r-sm); color: var(--text);
  transition: background-color var(--speed);
}
.report-row:hover { background: var(--surface-2); }
.report-row__day {
  flex: 0 0 96px; font-weight: 560; font-size: 13px;
}
.report-row__body { display: grid; gap: 1px; flex: 1; min-width: 0; }
.results-scroll { overflow-x: auto; }
.results-table { min-width: 620px; }

/* Блок, который перезагружается по фильтру: старое содержимое остаётся на
   месте и гаснет. Ни размытия, ни подмены серыми полосами — человек видит
   прежние строки, пока едут новые, и не теряет место, на котором читал. */
.is-loading { opacity: .5; transition: opacity 120ms var(--ease); pointer-events: none; }

/* ---------- что можно выделить и скопировать ----------

   Общее правило системы — не выделять ничего (`body` в начале файла): строки
   списков кликабельны, и выделение поверх ссылки только мешает. Выделяется
   ровно то, что человек копирует: заполняемые поля и названия объектов —
   сделок, клиентов, дел и задач. Подписи полей, заголовки, кнопки, вкладки,
   плашки, суммы и даты остаются невыделяемыми: в буфере они не нужны, а
   попадая в выделение, превращают скопированное в кашу.

   Класс `is-data` — пометка «здесь название объекта». Вешается там, где
   подходящего класса нет: название клиента в списке, название дела в карточке.

   Только для мыши. На сенсорном выделяемый текст перехватывает долгое
   нажатие: iOS показывает лупу и свою панель поверх нашего листа действий. */

@media (hover: hover) {
  .is-data,
  .editable__value,
  .trow__title,
  .deal-card__title {
    user-select: text; -webkit-user-select: text;
  }

  /* Прочерк вместо незаполненного значения и системные плашки внутри
     помеченных узлов копировать незачем: «— задача Согласовать макет». */
  .editable__value .faint,
  .is-data .badge {
    user-select: none; -webkit-user-select: none;
  }
}

/* ---------- каретка ----------

   В браузере может быть включён режим каретки (F7 в Хроме). Пока не
   выделялось ничего, он себя не проявлял; стоило разрешить выделение — и в
   сообщениях чата, в пустой ленте, в любом тексте по клику встаёт моргающая
   палка, будто это поле ввода. Гасим её везде и возвращаем только там, где
   она означает то, что должна: в полях ввода. Само выделение и копирование
   при этом работают как обычно — caret-color красит каретку, а не текст. */

html { caret-color: transparent; }
input, textarea, [contenteditable="true"] { caret-color: auto; }

/* Курсор по умолчанию — стрелка, а не «авто»: над выделяемым текстом браузер
   сам показывает «палку», а она обещает поле ввода, которого нет. Настоящим
   полям курсор возвращаем. Кнопкам, строкам и ссылкам он задан у них самих,
   и наследование его не трогает. */

html { cursor: default; }
input, textarea, [contenteditable="true"] { cursor: text; }
