:root {
  color-scheme: light dark;
  --accent: #3390ec;          /* синий Telegram */
  --bg: #ffffff;
  --bg-chat: #e6ebee;         /* фон ленты сообщений */
  --bubble-in: #ffffff;
  --bubble-out: #eeffde;      /* зелёный «свои» как в Telegram */
  --text: #000000;
  --muted: #707579;
  --line: #e4e4e5;
  --header: #ffffff;
}
/* Тёмная тема. Значения одни и те же в двух местах, и это осознанно: своя
   тема должна побеждать системную, а через одну переменную такое в CSS не
   выражается. data-theme ставит короткий скрипт в шапке — до первой отрисовки,
   иначе на секунду мигает белым. */
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    --bg: #17212b;
    --bg-chat: #0e1621;
    --bubble-in: #182533;
    --bubble-out: #2b5278;
    --text: #ffffff;
    --muted: #8a949d;
    --line: #101921;
    --header: #17212b;
  }
}
:root[data-theme="dark"] {
  color-scheme: dark;
  --bg: #17212b;
  --bg-chat: #0e1621;
  --bubble-in: #182533;
  --bubble-out: #2b5278;
  --text: #ffffff;
  --muted: #8a949d;
  --line: #101921;
  --header: #17212b;
}
:root[data-theme="light"] { color-scheme: light; }

* { box-sizing: border-box; -webkit-tap-highlight-color: transparent; }
/* hidden обязан побеждать: любое display в правилах ниже перебивает его
   по специфичности, и «скрытый» элемент остаётся на экране пустым. */
[hidden] { display: none !important; }
/* Двойной тап больше не зумит страницу — это мессенджер, а не документ. */
html { touch-action: manipulation; }
/* Высота корня = настоящая высота окна. Нужна странице переписки: см. .chatpage. */
html { height: 100%; }
body {
  margin: 0; background: var(--bg); color: var(--text);
  font: 16px/1.35 -apple-system, system-ui, "Segoe UI", Roboto, sans-serif;
}

/* ---------- вход ---------- */
/* Вход: два способа друг под другом, а не бок о бок. В строку они не влезали
   на телефон и уезжали за край экрана. */
.center {
  display: flex; flex-direction: column; min-height: 100vh;
  align-items: center; justify-content: center;
  padding: 24px 16px; padding-bottom: max(24px, env(safe-area-inset-bottom));
}
.card {
  display: flex; flex-direction: column; gap: 12px; padding: 0;
  width: 100%; max-width: 320px;
}
.card + .or, .or + .card { margin-top: 18px; }
.or {
  display: flex; align-items: center; gap: 10px;
  width: 100%; max-width: 320px; color: var(--muted); font-size: 13px;
}
.or::before, .or::after {
  content: ""; flex: 1; height: 1px; background: var(--line);
}
.card h1 { text-align: center; }
.error { color: #e53935; margin: 0; text-align: center; }

/* Сообщение, от которого в базе не осталось ни текста, ни вложения. Бледно и
   курсивом: это не слова клиента, а наша приписка о том, что смотреть надо в
   самом мессенджере. */
.no-body { color: var(--muted); font-style: italic; }

/* «Написать» в карточке пересланного контакта и логин в тексте сообщения.
   Ведут в нашу же форму «написать первым», а не наружу в телеграм: снаружи
   переписка ушла бы мимо инбокса — без истории и без сменщика. */
.c-write { display: inline-block; margin-top: 2px; font-size: 13px; }
.tg-login { text-decoration: underline; text-underline-offset: 2px; }

/* ---------- шапка ---------- */
header {
  display: flex; gap: 10px; align-items: center;
  padding: 10px 14px; padding-top: max(10px, env(safe-area-inset-top));
  background: var(--header); border-bottom: 1px solid var(--line);
  position: sticky; top: 0; z-index: 5;
}
h1 { font-size: 19px; margin: 0; flex: 1; font-weight: 600; min-width: 0; }
/* Значков в шапке пять, и выпадающий список аккаунтов вытеснял последний за
   край экрана. Пусть ужимается он, а кнопки остаются целыми. */
#account-filter {
  min-width: 0; flex: 0 1 auto; max-width: 42vw;
  overflow: hidden; text-overflow: ellipsis;
}
header .gear { flex: none; }
.badge:not(:empty) {
  background: var(--accent); color: #fff; border-radius: 12px;
  padding: 1px 8px; font-size: 13px; font-weight: 500; vertical-align: middle;
}

input, button, select { font: inherit; }
input, select {
  padding: 10px 12px; border-radius: 10px;
  border: 1px solid var(--line); background: var(--bg); color: var(--text);
}
button {
  padding: 10px 14px; border: 0; border-radius: 10px;
  background: var(--accent); color: #fff; cursor: pointer;
}
select { padding: 6px 10px; font-size: 16px; }   /* 16px — без зума iPhone при фокусе */

#enable-push {
  display: block; width: calc(100% - 28px); margin: 10px 14px;
  background: var(--accent); font-size: 15px;
}
#enable-push.hint { background: transparent; color: var(--muted); font-size: 14px;
                    border: 1px solid var(--line); cursor: default; }

/* ---------- список чатов ---------- */
ul { list-style: none; margin: 0; padding: 0; }
.chat a {
  display: grid; grid-template-columns: 48px 1fr;
  grid-template-areas: "av row1" "av item" "av row2" "av acc";
  column-gap: 12px; align-items: center;
  padding: 9px 14px; text-decoration: none; color: inherit;
}
.chat + .chat a { border-top: 1px solid var(--line); }
.chat a:active { background: var(--bg-chat); }

.av-wrap { grid-area: av; position: relative; width: 48px; height: 48px;
           flex-shrink: 0; }
.chan {
  position: absolute; right: -3px; bottom: -3px;
  width: 20px; height: 20px; border-radius: 50%;
  background: var(--bg); padding: 1.5px;
  display: flex; align-items: center; justify-content: center;
}
/* img — для иконок, вшитых картинкой (Циан); остальные значки SVG. */
.chan svg, .chan img { width: 100%; height: 100%; display: block;
                       border-radius: 50%; }
.chatpage header .av-wrap { width: 36px; height: 36px; }
.chatpage header .chan { width: 15px; height: 15px; font-size: 8px; }

.avatar {
  width: 48px; height: 48px; border-radius: 50%;
  display: flex; align-items: center; justify-content: center;
  color: #fff; font-size: 17px; font-weight: 600; letter-spacing: .5px;
  flex-shrink: 0;
}
.avatar.photo { background-size: cover; background-position: center; }
.avatar.small { width: 36px; height: 36px; font-size: 14px; }
/* min-width:0 обязателен: без него flex-элемент с длинным текстом отказывается
   сжиматься, строка распирается и залезает на соседей. */
.row1 { grid-area: row1; display: flex; align-items: baseline; gap: 8px; min-width: 0; }
.row2 { grid-area: row2; display: flex; align-items: center; gap: 8px; min-width: 0; }
/* Обычный вес у всех: раньше было 600 против 700 у непрочитанного — разница
   на телефоне неразличима, и весь список выглядел непрочитанным.
   Признак непрочитанного — синий счётчик, как в Telegram. */
.chat .title { font-weight: 500; flex: 1; min-width: 0; overflow: hidden;
               text-overflow: ellipsis; white-space: nowrap; }
.chat .time { font-size: 12px; color: var(--muted); flex-shrink: 0; }
.chat .preview {
  flex: 1; min-width: 0; font-size: 15px; color: var(--muted);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.chat .preview .mine { color: var(--accent); }
.chat .count {
  background: var(--accent); color: #fff; border-radius: 11px;
  min-width: 22px; height: 22px; padding: 0 6px; font-size: 13px; font-weight: 500;
  display: flex; align-items: center; justify-content: center; flex-shrink: 0;
}
.chat .account { grid-area: acc; font-size: 12px; color: var(--muted); opacity: .8; }
.chat.unread .title { font-weight: 700; }
.chat.unread .preview { color: var(--text); }
/* Кнопка — сосед ссылки во flex, а не наложение поверх неё:
   иначе она перекрывает превью и счётчик непрочитанного. */
.chat { display: flex; align-items: center; }
.chat > a { flex: 1; min-width: 0; }
.noreply {
  flex-shrink: 0; margin: 0 14px 0 4px;
  width: 36px; height: 36px; padding: 0; border-radius: 50%;
  font-size: 17px; line-height: 1;
  background: transparent; color: var(--muted); border: 1px solid var(--line);
}
.noreply:active { background: var(--accent); color: #fff; border-color: var(--accent); }
.gear { text-decoration: none; font-size: 20px; color: var(--muted); padding: 0 2px; }

/* ---------- настройки ---------- */
.settings section { padding: 14px; border-bottom: 1px solid var(--line); }
.settings h2 { font-size: 14px; text-transform: uppercase; letter-spacing: .5px;
               color: var(--muted); margin: 0 0 10px; font-weight: 600; }
.settings .note { font-size: 13px; color: var(--muted); margin: 8px 0 0; }
.settings code { font-size: 12px; word-break: break-all; }
.settings #enable-push { width: 100%; margin: 0; }
.accounts li { display: grid; grid-template-columns: 10px 1fr auto;
               grid-template-areas: "dot label state" ". phone state";
               gap: 2px 10px; align-items: center; padding: 8px 0;
               border-bottom: 1px solid var(--line); }
.accounts li:last-child { border-bottom: 0; }
.accounts .dot { grid-area: dot; width: 8px; height: 8px; border-radius: 50%; }
.accounts .dot.ok { background: #4caf50; }
.accounts .dot.bad { background: #e53935; }
.accounts .label { grid-area: label; font-weight: 600; }
.accounts .phone { grid-area: phone; font-size: 13px; color: var(--muted); }
.accounts .state { grid-area: state; font-size: 13px; color: var(--muted); }
button.danger { background: transparent; color: #e53935;
                border: 1px solid var(--line); width: 100%; }

/* ---------- переписка ---------- */
/* Высоту берём из «видимой области», а не из окна. На айфоне полоса браузера
   прячется при прокрутке, и окно оказывается выше того, что реально видно:
   под строкой ввода зияла пустота, а стоило потянуть страницу — она
   схлопывалась. --вид ставит скрипт по visualViewport, 100dvh — запасной
   вариант для тех, у кого его нет. */
/* На андроиде в установленном приложении Chrome врёт: 100dvh = 787 при окне
   730 — он прибавляет высоту своей панели адреса, которой в приложении нет
   вовсе (снято с Samsung, Chrome 152, standalone, в апарт-инбоксе 11.09.2026).
   Страница выходила на 57px выше экрана, и строка ввода целиком уезжала за
   нижний край. Поэтому не выше настоящего окна: 100% от html — это
   documentElement.clientHeight, и он там честный. На айфоне в приложении обе
   величины равны, min ничего не меняет. */
.chatpage { display: flex; flex-direction: column; height: 100dvh;
            height: min(100dvh, 100%); }
.chatpage header .who { display: flex; flex-direction: column; line-height: 1.2; }
.chatpage header .name { font-weight: 600; font-size: 17px; }
.chatpage header .account { font-size: 12px; color: var(--muted); }
.back { text-decoration: none; font-size: 30px; color: var(--accent);
        line-height: 1; padding: 0 6px 4px 0; }

#feed {
  flex: 1; overflow-y: auto; background: var(--bg-chat);
  padding: 12px 10px; display: flex; flex-direction: column; gap: 4px;
  /* Дотянул ленту до края — прокрутка не должна переходить на страницу:
     при открытой клавиатуре это уводит шапку (см. touchmove в 15-клавиатура.js). */
  overscroll-behavior-y: contain;
}
.msg {
  position: relative; max-width: 80%; width: fit-content;
  padding: 6px 10px 6px 10px; border-radius: 12px;
  /* Долгое нажатие открывает меню — выделять текст при этом нельзя: лупа с
     «копировать» перекрывает кнопки. Копирование есть отдельным пунктом. */
  -webkit-user-select: none; user-select: none; -webkit-touch-callout: none;
  background: var(--bubble-in); box-shadow: 0 1px 1px rgba(0,0,0,.08);
  font-size: 16px; word-wrap: break-word; overflow-wrap: anywhere;
}
.msg.in  { align-self: flex-start; border-bottom-left-radius: 4px; }
.msg.out { align-self: flex-end; background: var(--bubble-out);
           border-bottom-right-radius: 4px; }
.msg .text { white-space: pre-wrap; }
.msg .meta { font-size: 11px; color: var(--muted); float: right;
             margin: 6px 0 0 8px; user-select: none; }
/* Ещё не ушедшее сообщение заметно бледнее отправленного: по одному только
   значку в углу разница не читается, а на бегу нужно видеть её боковым
   зрением. */
.msg.out.pending { opacity: .6; }

/* Настоящий вращающийся кружок вместо мигающего символа: мигание читается
   как ошибка, вращение — как работа. Рисуем рамкой, чтобы не тащить картинку. */
.ticks.sending {
  display: inline-block; width: 9px; height: 9px; vertical-align: -1px;
  border: 1.5px solid currentColor; border-top-color: transparent;
  border-radius: 50%; animation: вертушка .8s linear infinite;
  opacity: .8; margin-left: 2px;
}
@keyframes вертушка { to { transform: rotate(360deg); } }
/* Кто отключил анимации в системе — тому статичный кружок, а не мельтешение. */
@media (prefers-reduced-motion: reduce) {
  .ticks.sending { animation: none; border-top-color: currentColor; opacity: .5; }
}
.msg.out.failed { background: #e57373; color: #fff; }
.msg.out.failed .meta, .msg.out.failed .err { color: rgba(255,255,255,.85); }
/* Постоянный размер рамки: картинка грузится лениво, и без фиксированной
   высоты лента дёргалась бы под пальцем по мере подгрузки. */
img.media, video.media {
  width: 240px; max-width: 100%; height: 180px; object-fit: cover;
  border-radius: 8px; display: block; margin: -2px 0 4px;
  background: rgba(128,128,128,.18);
}

/* Внутри формы поля ввода, над её верхом: поле растёт с текстом, и отсчёт
   от низа страницы прятал кнопку под полем со второй строки (аудит
   17.09.2026). Селекторы с #composer — иначе побеждает «#composer button». */
#composer #scroll-down {
  position: absolute; right: 12px; bottom: calc(100% + 12px);
  width: 40px; height: 40px; padding: 0; border-radius: 50%;
  font-size: 20px; line-height: 1;
  background: var(--header); color: var(--accent);
  box-shadow: 0 2px 8px rgba(0,0,0,.2);
  opacity: 0; pointer-events: none; transition: opacity .15s;
}
#composer #scroll-down.shown { opacity: 1; pointer-events: auto; }
.chatpage { position: relative; top: 0; }
/* Под клавиатурой страницу сажает на видимую область скрипт (см.
   перепискаПодКлавиатурой в static/src/55-переписка-под-клавиатурой.js).
   Браузер рисует сдвиг области анимацией в четверть секунды, а сообщает о нём
   сразу конечный — переезжаем той же анимацией, чтобы шапка на экране не
   двигалась. Только на телефоне: на компьютере высота меняется от окна, и
   ехать ей незачем. */
@media (hover: none) and (pointer: coarse) {
  .chatpage { transition: top .25s ease-in-out, height .25s ease-in-out; }
}

/* Полоска «загружаю более раннее».

   Стилей у неё не было НИ ОДНОГО, и стояла она обычным блоком ПОСЛЕ ленты.
   Пока она видна, лента (flex:1) ровно на её высоту короче — в апарт-инбоксе
   это 22 пикселя. Содержимое, прижатое к низу, дёргается дважды: когда полоска
   появилась и когда исчезла. Между этими мгновениями проходит целый поход в
   сеть: показ идёт ДО fetch, скрытие в finally после него, то есть на телефоне
   она висит видимой сотни миллисекунд.

   Там это выглядело как «при открытии некоторых чатов сообщения один раз слегка
   подпрыгивают», и нашлось только записью экрана: все замеры искали изменение
   высоты СОДЕРЖИМОГО, а менялась высота самой ленты. Полоску показывают лишь
   там, где есть что подгружать сверху, — в переписках длиннее шестидесяти
   сообщений; короткие чаты не дёргались, отсюда и путаница.

   Теперь она висит НАД лентой и места в раскладке не занимает вовсе. Отступ
   снизу тот же, что у кнопки «вниз», — он уже выверен по высоте поля ввода. */
#composer #load-older {
  position: absolute; left: 50%; bottom: calc(100% + 12px); transform: translateX(-50%);
  z-index: 4; pointer-events: none;
  padding: 5px 12px; border-radius: 14px;
  background: var(--header); color: var(--muted); font-size: 13px;
  box-shadow: 0 2px 8px rgba(0, 0, 0, .18);
}

/* Стикер — без пузыря, как в Telegram */
.msg.bare { background: none; box-shadow: none; padding: 2px 0; max-width: 60%; }
.msg.bare .meta { color: var(--muted); }
.sticker { width: 160px; height: 160px; object-fit: contain; display: block; }
.sticker-stub {
  display: flex; flex-direction: column; align-items: center; gap: 4px;
  font-size: 56px; line-height: 1;
}
.sticker-stub small { font-size: 12px; color: var(--muted); }
.file { color: var(--accent); text-decoration: none; }
.msg .err { display: block; font-size: 12px; margin-top: 2px; }
.retry { font-size: 12px; padding: 3px 10px; margin-top: 6px;
         background: rgba(255,255,255,.25); }

#composer {
  display: flex; gap: 6px; align-items: center; padding: 4px 10px;
  /* Полоса под полем — место домашней «палочки» айфона. Полный отступ там
     лишний: строка ввода и без того отодвинута собственным полем, и внизу
     зияла пустая белая полоса в палец высотой. Оставляем ровно столько,
     чтобы палец не попадал в системный жест. */
  padding-bottom: max(4px, calc(env(safe-area-inset-bottom) - 22px));
  /* Подложка тянется до самого низа экрана: иначе под строкой видна белая
     полоса фона страницы — та самая, что не давала покоя. */
  box-shadow: 0 40px 0 var(--header);
  background: var(--header); border-top: 1px solid var(--line);
}
/* Поле растёт вместе с текстом, как в телеграме: длинную ссылку или абзац
   нельзя править вслепую в одну строку. Потолок — ниже (~15 видимых строк
   при border-box), дальше поле прокручивается внутри себя. */
#composer #text {
  flex: 1; min-width: 0; border-radius: 20px; padding: 11px 16px;
  /* До шестнадцати строк поле растёт, дальше прокручивается внутри себя.
     Тексты для клиентов длинные, с абзацами, и править их в окошко на шесть
     строк — значит не видеть, что пишешь. */
  overflow-y: auto; max-height: 21.6em; line-height: 1.35;
  font: inherit; font-size: 16px;   /* меньше 16px — iOS зумит страницу при фокусе */
  border: 1px solid var(--line); background: var(--bg); color: var(--text);
  white-space: pre-wrap; word-break: break-word; outline: none;
  -webkit-user-select: text; user-select: text;
}
/* Подсказка вместо placeholder: у contenteditable его нет. */
#composer #text:empty::before {
  content: attr(data-placeholder); color: var(--muted); pointer-events: none;
}
#composer { align-items: flex-end; }   /* кнопки держатся низа, поле растёт вверх */
#composer button { border-radius: 50%; width: 42px; height: 42px; padding: 0;
                   font-size: 17px; flex-shrink: 0; }
#composer #plus {
  background: transparent; color: var(--muted); font-size: 26px;
  font-weight: 300; line-height: 1; position: relative;
}

#attach-bar, #schedule-bar {
  display: flex; align-items: center; gap: 10px;
  padding: 8px 12px; font-size: 14px;
  background: var(--header); border-top: 1px solid var(--line); color: var(--muted);
}
#attach-bar span, #schedule-bar label { flex: 1; min-width: 0;
                                        overflow: hidden; text-overflow: ellipsis;
                                        white-space: nowrap; }
#attach-bar button, #schedule-bar button {
  width: 28px; height: 28px; padding: 0; font-size: 14px;
  background: transparent; color: var(--muted); border: 1px solid var(--line);
  border-radius: 50%; flex-shrink: 0;
}
#schedule-bar input { padding: 6px 8px; font-size: 14px; margin-left: 6px; }
.msg .scheduled { display: block; font-size: 12px; opacity: .85; margin-top: 2px; }

/* Отложенная отправка: таймер рядом с «отправить», как в Telegram */

#sched-badge {
  position: absolute; top: -2px; right: -4px;
  background: var(--accent); color: #fff; border-radius: 9px;
  font-size: 10px; line-height: 1; padding: 3px 5px; font-weight: 600;
}
#schedule-pop {
  position: absolute; right: 10px; bottom: 68px; z-index: 10;
  min-width: 210px; max-width: calc(100vw - 20px);
  background: var(--header); border: 1px solid var(--line); border-radius: 14px;
  padding: 10px; box-shadow: 0 6px 24px rgba(0,0,0,.28);
  display: flex; flex-direction: column; gap: 6px; font-size: 15px;
}
#schedule-pop .title { font-size: 12px; color: var(--muted);
                       text-transform: uppercase; letter-spacing: .4px;
                       padding: 2px 4px 4px; }
#schedule-pop .quick {
  background: transparent; color: var(--text); text-align: left;
  padding: 10px 12px; border-radius: 10px; font-size: 15px;
}
#schedule-pop .quick:active { background: var(--bg-chat); }
#schedule-pop .custom { display: flex; flex-direction: column; gap: 6px;
                        font-size: 12px; color: var(--muted);
                        padding: 6px 4px 2px; border-top: 1px solid var(--line); }
#schedule-pop .custom input { width: 100%; font-size: 16px; color: var(--text); }  /* 16px — без зума на iPhone */
#schedule-pop .row { display: flex; gap: 8px; }
#schedule-pop button { width: auto; height: auto; border-radius: 8px;
                       padding: 8px 14px; font-size: 14px; }
#sched-clear { background: transparent; color: var(--muted);
               border: 1px solid var(--line); }
.msg .scheduled button { width: auto; height: auto; border-radius: 8px;
                         padding: 2px 8px; font-size: 11px; margin-left: 6px;
                         background: rgba(255,255,255,.3); color: inherit; }

/* Настройки */
.toggle { display: flex; align-items: center; gap: 10px; font-size: 15px; }
.toggle input { width: 20px; height: 20px; }
button.wide { width: 100%; }
.settings textarea, .settings input:not([type=checkbox]), .settings select {
  width: 100%; font: inherit; padding: 10px 12px; border-radius: 10px;
  border: 1px solid var(--line); background: var(--bg); color: var(--text);
}

.msg .ticks { opacity: .7; }
.msg .ticks.read { color: #4fae4e; opacity: 1; }
/* Две галочки ставим внахлёст: врозь они читаются как два отдельных значка,
   а это один знак «прочитано». Правый отступ возвращаем, чтобы съеденное
   letter-spacing место не липло к краю пузыря. */
.msg .ticks.read { letter-spacing: -0.32em; padding-right: 0.32em; }
.msg.out .ticks.read { color: #59b96a; }
.msg .edited { font-size: 11px; color: var(--muted); margin-left: 6px; }
/* Кто отправил — тише времени: подпись нужна, когда её ищут глазами, и не
   должна спорить с текстом сообщения. */
.msg .by {
  font-size: 11px; color: var(--muted); opacity: .75;
  float: right; margin: 6px 0 0 8px; user-select: none;
}
/* Долгое нажатие вместо клика: курсор-указатель обманывал бы. */
.msg.out[data-sent="1"] { cursor: default; -webkit-touch-callout: none; }

/* Окно ввода: prompt() браузера однострочный и режет длинный текст */
.modal-back {
  position: fixed; inset: 0; z-index: 100;
  background: rgba(0,0,0,.45);
  display: flex; align-items: flex-end; justify-content: center;
  padding: 12px; padding-bottom: max(12px, env(safe-area-inset-bottom));
  /* Страница растянута под статус-бар (viewport-fit=cover): высокое окно,
     отцентрованное в видимой над клавиатурой области, верхом заезжало под
     часы. Сверху отступаем на высоту статус-бара. */
  padding-top: max(12px, env(safe-area-inset-top));
  /* На iPhone клавиатура не сжимает окно страницы, а сдвигает его вверх, и
     всё, что стоит fixed, уезжает под статус-бар. Подложку сажаем на
     видимую область (visualViewport, см. Клавиатура.окна в
     static/src/15-клавиатура.js) — окна внутри центрируются уже в ней и,
     если не влезают, прокручиваются. */
  box-sizing: border-box;
}
@media (min-height: 600px) { .modal-back { align-items: center; } }
/* Окна с полями ввода (заметка, пересылка) на телефоне прижаты к ВЕРХУ
   видимой области, а не к центру. Центрированное окно при клавиатуре
   приходилось везти вверх в такт её выезду — по памяти высоты, которая для
   полей формы не сходилась на панель «< > Готово», и окно дёргалось дважды.
   Прижатому к верху ехать некуда: клавиатура накрывает его низ, и только.
   См. Клавиатура.окна в static/src/15-клавиатура.js. На компьютере
   клавиатуры нет — по центру. */
@media (hover: none) and (pointer: coarse) {
  /* Сдвиг области за окном (36px, см. посадить в перепискаПодКлавиатурой)
     iPhone рисует за ~165 мс с быстрым стартом — подложка едет той же
     кривой, что и страница, иначе окно на кадр проседает или перелетает. */
  .modal-back { transition: top 165ms cubic-bezier(.25,.85,.35,1) 30ms; }
  .modal-back.form { align-items: flex-start; }
}
.modal {
  width: 100%; max-width: 460px;
  background: var(--bg); border-radius: 16px; padding: 16px;
  display: flex; flex-direction: column; gap: 12px;
  box-shadow: 0 10px 40px rgba(0,0,0,.35);
  /* Потолок высоты обязателен. Список действий по нажатию на имя растёт с
     каждой новой возможностью, и без потолка лист вылезает за края окна сразу
     вверх и вниз: он центрируется, а лишнее у центрированного элемента уходит
     в обе стороны. Первые и последние пункты становятся недоступны совсем. */
  max-height: 100%; overflow-y: auto; overscroll-behavior: contain;
}
.modal-title { font-weight: 600; font-size: 16px; }
/* Коробка окна поверх чата с полями — одна на все такие окна. Стоит обычным
   ребёнком подложки, не fixed: на iPhone клавиатура сдвигает страницу, и
   fixed-коробка уезжала под статус-бар (см. Клавиатура). */
.form-box {
  position: relative; margin: auto;
  max-height: 100%; overflow-y: auto; overscroll-behavior: contain;
  background: var(--bg); border-radius: 16px; padding: 16px;
  display: flex; flex-direction: column; gap: 10px;
  box-shadow: 0 12px 40px rgba(0,0,0,.25);
}
@media (hover: none) and (pointer: coarse) {
  .form-box { margin: 0 auto; }
}
.form-box input, .form-box select, .form-box textarea { font-size: 16px; }
/* Строка формы «подпись над полем» и маленькая кнопка — имена как в
   апарт-инбоксе, чтобы окна переносились без правок. */
.pay-row { display: flex; flex-direction: column; gap: 4px; font-size: 13px; color: var(--muted); }
.pay-row input, .pay-row select, .pay-row textarea { font-size: 16px; }
.pass-mini { width: auto; height: auto; padding: 4px 10px; font-size: 13px; border-radius: 12px;
  background: transparent; color: var(--accent); border: 1px solid var(--accent); cursor: pointer; }
.pay-head { display: flex; align-items: center; justify-content: space-between; font-weight: 600; gap: 8px; }
/* Крестик в шапке окна. */
.окно-закрыть { background: transparent; color: var(--muted); font-size: 18px; padding: 2px 6px; line-height: 1;
                width: auto; height: auto; }

/* Лист действий по долгому нажатию: пункты во всю ширину, палец попадает
   не целясь. Промах здесь дорог — рядом «отменить отправку». */
.modal.sheet { gap: 8px; }
.sheet-item {
  width: 100%; font: inherit; font-size: 16px; text-align: left;
  padding: 14px 16px; border-radius: 12px; border: 1px solid var(--line);
  background: var(--bg); color: var(--text); cursor: pointer;
}
.sheet-item.danger { color: #e53935; border-color: rgba(229,57,53,.35); }
.sheet-item.cancel { text-align: center; color: var(--muted); }
.modal textarea {
  width: 100%; font: inherit; font-size: 16px; line-height: 1.4;
  padding: 12px; border-radius: 10px; resize: vertical;
  border: 1px solid var(--line); background: var(--bg); color: var(--text);
}
.modal-row { display: flex; gap: 10px; justify-content: flex-end; }
.modal-row button { padding: 10px 18px; }
.modal-row .cancel { background: transparent; color: var(--muted);
                     border: 1px solid var(--line); }

/* Ссылки в сообщениях */
.msg a { color: var(--accent); word-break: break-all; }
.msg.out a { color: #1a6ea8; }
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .msg.out a { color: #8fd0ff; }
}
:root[data-theme="dark"] .msg.out a { color: #8fd0ff; }

/* Прежний текст отредактированного сообщения — раскрывается по стрелке */
.msg .orig { margin-top: 4px; }
.msg .orig summary { font-size: 11px; color: var(--muted); cursor: pointer;
                     list-style: none; user-select: none; }
.msg .orig summary::-webkit-details-marker { display: none; }
.msg .orig summary::before { content: "▸ "; }
.msg .orig[open] summary::before { content: "▾ "; }
.msg .was { display: block; font-size: 14px; opacity: .75;
            text-decoration: line-through; margin-top: 2px; white-space: pre-wrap; }

/* Страницы-списки в кабинете (скрипты и то, что придёт следом).
   Классы называются apt-*, а не advert-*: блокировщик рекламы в Chrome
   вырезает элементы, у которых в классе есть «advert», — список из-за этого
   не отображался вовсе, при живом HTML в DOM. */
.apt-page { max-width: 640px; margin: 0 auto; padding: 0 14px 24px; }
.apt-page .note { padding: 12px 0; }
.apt-page section { border: 0; padding: 0; margin-top: 18px; }
.apt-page h2 { display: flex; align-items: baseline; gap: 8px; padding: 0 0 6px; }
.apt-count { font-size: 12px; font-weight: 400; color: var(--muted);
             background: var(--line); border-radius: 10px; padding: 2px 8px;
             text-transform: none; letter-spacing: 0; }
.apt-count.done { color: #2e7d32; background: rgba(76,175,80,.16); }

.apt-list { background: var(--bg); border-radius: 14px; overflow: hidden; }
/* Название и поле в одну строку: места хватает, а «в столбик» на 17 позиций
   превращается в простыню. На узком экране строка переносится сама. */
.apt-list li { display: flex; flex-wrap: wrap; gap: 6px 12px;
               align-items: center; padding: 10px 12px;
               border-bottom: 1px solid var(--line); }
.apt-list li:last-child { border-bottom: 0; }
.apt-title { flex: 1 1 200px; font-size: 14px; line-height: 1.3; color: var(--text); }
.apt-list li .apt-no { flex: 0 0 116px; width: 116px; padding: 8px 10px;
                       font-size: 15px; text-align: center; }
.apt-no:placeholder-shown { background: transparent; }

/* Разделитель дня в переписке */
.day-sep {
  align-self: center; margin: 10px 0 6px;
  padding: 3px 12px; border-radius: 12px;
  background: rgba(0,0,0,.10); color: var(--muted);
  font-size: 12px; font-weight: 500;
}
@media (prefers-color-scheme: dark) { .day-sep { background: rgba(255,255,255,.10); } }

/* Автоответы */
.head-btn { padding: 8px 12px; font-size: 14px; border-radius: 10px; }
.ar-list { padding: 8px 0; }
.ar-item { display: grid; grid-template-columns: 10px 1fr auto; gap: 4px 10px;
           align-items: center; padding: 12px 14px;
           border-bottom: 1px solid var(--line); cursor: pointer; }
.ar-item.active { background: var(--bg-chat); }
.ar-item .dot { width: 8px; height: 8px; border-radius: 50%; }
.ar-item .dot.ok { background: #4caf50; }
.ar-item .dot.off { background: #bbb; }
.ar-item .ar-name { font-weight: 600; }
.ar-item .ar-kind { font-size: 12px; color: var(--muted); }
.row-field { display: flex; align-items: center; justify-content: space-between;
             gap: 12px; padding: 8px 0; font-size: 15px; }
.row-field input { width: 120px; }
.ar-preview { font-style: italic; }
.muted { color: var(--muted); }

.wide-link { display: block; text-align: center; padding: 12px;
             background: var(--accent); color: #fff; border-radius: 10px;
             text-decoration: none; font-size: 15px; }

/* Закрепление чата свайпом */
.chat { transition: transform .18s ease; position: relative; }
.chat.will-pin { background: rgba(51,144,236,.12); }
.chat.pinned { background: rgba(128,128,128,.06); }
.pin-mark { font-size: 11px; flex-shrink: 0; }

/* Статистика */
.tiles { display: grid; grid-template-columns: repeat(auto-fit, minmax(90px, 1fr));
         gap: 8px; margin: 4px 0 8px; }
.tile { background: var(--bg-chat); border-radius: 12px; padding: 12px 10px;
        text-align: center; }
.tile b { display: block; font-size: 20px; font-weight: 700; }
.tile span { font-size: 12px; color: var(--muted); }
.tile.warn b { color: #e8890c; }
.tile.bad b { color: #e53935; }
.bar-row { display: grid; grid-template-columns: 88px 1fr 40px; gap: 8px;
           align-items: center; padding: 5px 0; font-size: 14px; }
.bar-label { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.bar-track { background: var(--bg-chat); border-radius: 6px; height: 18px; }
.bar-fill { display: block; height: 100%; border-radius: 6px; background: var(--accent); }
.bar-fill.alt { background: #7bc862; }
.bar-value { text-align: right; color: var(--muted); font-size: 13px; }
.hours { display: flex; align-items: flex-end; gap: 2px; height: 70px; margin-top: 6px; }
.hour { flex: 1; height: 100%; display: flex; align-items: flex-end; }
.hour-fill { width: 100%; background: var(--accent); border-radius: 2px 2px 0 0;
             min-height: 1px; }
.hours-axis { display: flex; justify-content: space-between; font-size: 11px;
              color: var(--muted); margin-top: 4px; }
.ar-tabs a.ar-tab { text-decoration: none; }

/* Вкладки периода (стили потерялись при переделке экрана автоответов) */
.ar-tabs { display: flex; gap: 8px; padding: 12px 14px 4px; }
.ar-tab {
  flex: 1; text-align: center; padding: 10px 8px; font-size: 14px;
  border: 1px solid var(--line); border-radius: 10px;
  background: transparent; color: var(--muted); text-decoration: none;
  display: flex; align-items: center; justify-content: center; gap: 6px;
}
.ar-tab.active { background: var(--accent); color: #fff; border-color: var(--accent); }

/* Подпись НАД полосой: рядом с ней длинные названия каналов обрезались
   до неразличимого «Авито AV A…» — а начинаются они одинаково. */
.bar-block { padding: 6px 0; }
.bar-name { display: flex; justify-content: space-between; gap: 10px;
            font-size: 14px; margin-bottom: 4px; }
.bar-num { color: var(--muted); flex-shrink: 0; }
/* span внутри блока остаётся строчным, и height к нему не применяется —
   полосы просто исчезали. */
.bar-block .bar-track { display: block; width: 100%; }

/* Кадр из видео: у MAX прямой ссылки на файл нет, поэтому вместо плеера
   показываем картинку со значком — чтобы не выглядело сломанным видео. */
.video-frame { position: relative; display: inline-block; }
.video-frame .play-mark {
  position: absolute; left: 50%; top: 50%; transform: translate(-50%, -50%);
  width: 44px; height: 44px; border-radius: 50%;
  background: rgba(0, 0, 0, .55); color: #fff;
  display: flex; align-items: center; justify-content: center;
  font-size: 18px; padding-left: 3px; pointer-events: none;
}

/* Не отправленное сообщение видно прямо в списке: иначе про сорвавшуюся
   отправку узнаёшь, только открыв переписку. */
/* Кадр видео кликабелен — показываем это. */
.video-frame[data-play] { cursor: pointer; }
.failed-dot { color: #e53935; font-size: 15px; line-height: 1;
              margin-left: 6px; flex-shrink: 0; }

/* Скрипты: список над полем ввода. Вставляют текст, но НЕ отправляют —
   отправку оператор нажимает сам, посмотрев на подставленное. */
#composer #scripts-btn { font-weight: 700; font-size: 20px; line-height: 1; }
#scripts-pop {
  position: fixed; left: 0; right: 0; bottom: 0; z-index: 40;
  max-height: 70vh; display: flex; flex-direction: column;
  background: var(--bg); border-top: 1px solid var(--line);
  border-radius: 14px 14px 0 0; box-shadow: 0 -8px 30px rgba(0,0,0,.18);
}
#scripts-pop .sc-head { display: flex; gap: 8px; padding: 10px 12px;
                        border-bottom: 1px solid var(--line); }
#scripts-pop #sc-search { flex: 1; min-width: 0; border-radius: 12px; padding: 9px 12px; }
#scripts-pop #sc-close { width: 38px; background: transparent; color: var(--muted);
                         font-size: 17px; }
#sc-items { overflow-y: auto; padding: 4px 0 max(8px, env(safe-area-inset-bottom)); }
#sc-items li { padding: 10px 14px; border-bottom: 1px solid var(--line);
               cursor: pointer; }
#sc-items li:active { background: rgba(127,127,127,.12); }
#sc-items .sc-name { display: block; font-size: 15px; }
#sc-items .sc-preview, .sc-list .sc-preview {
  display: block; font-size: 13px; color: var(--muted);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* Разделитель «дальше — скрипты других каналов» */
#sc-items li.other-first { border-top: 1px solid var(--line); margin-top: 6px; }
#sc-items li.other-first::before, .sc-tag {
  display: inline-block; font-size: 11px; color: var(--muted);
  background: var(--line); border-radius: 8px; padding: 1px 7px; }
#sc-items li.other-first::before { content: "другие каналы"; margin-bottom: 6px; }
.sc-list li { padding: 12px 0; border-bottom: 1px solid var(--line); cursor: pointer; }
.sc-list .sc-name { font-size: 15px; margin-right: 6px; }

/* Один чип с тегом канала над списком — фильтр, а не подпись у каждой строки. */
.sc-tagbar { display: flex; align-items: center; gap: 8px;
             padding: 8px 14px 4px; }
.sc-chip { font-size: 13px; padding: 4px 12px; border-radius: 12px;
           background: var(--line); color: var(--text); }
.sc-chip.on { background: var(--accent, #3b82f6); color: #fff; }
.sc-hint { font-size: 12px; color: var(--muted); }

/* Браузер не считает раскладку строк за пределами экрана. На списке в три
   сотни чатов это разница между «сразу нажимается» и «пару секунд не реагирует».
   Высота-заглушка нужна, чтобы полоса прокрутки не скакала. */
.chat { content-visibility: auto; contain-intrinsic-size: 0 76px; }

/* «Ответ не требуется» — тонкая текстовая кнопка под полем ввода, слева.
   То же действие, что и ✓ в списке: снимает непрочитанное у нас и отправляет
   клиенту отчёт о прочтении. */
/* Полоска «ответ не требуется» прижата к строке ввода: раньше между ней и
   последним сообщением зияла пустая полоса в треть пальца. */
/* Подложка как у ленты сообщений, а не как у строки ввода. Прозрачным блоком
   сквозь него светился белый фон страницы, а с цветом строки ввода получалась
   та же белая полоса — просто на строку ниже. Серый фон ленты делает эту
   надпись частью переписки, чем она и является. */
/* Без подложки: любой фон здесь читался полосой поперёк экрана. Просто
   подпись в конце переписки. */
.under-composer {
  align-self: center; line-height: 1;
  padding: 2px 10px 6px; background: transparent;
}
.under-composer button {
  background: transparent; color: var(--muted); font-size: 13px;
  padding: 2px 0; border: 0; width: auto;
}
.under-composer button:disabled { opacity: .6; }
.under-composer button.done { color: #2e7d32; }

/* Свайп по строке чата — наш жест, а не браузерный «назад».
   touch-action: pan-y отдаёт браузеру только вертикальную прокрутку,
   overscroll-behavior-x гасит навигацию по горизонтальному оттягиванию. */
html, body { overscroll-behavior-x: none; }
#chats .chat { touch-action: pan-y; }

/* Статус собеседника в шапке. Есть только у Telegram и MAX — у Авито и Циан
   его в API нет, и строка просто не появляется. */
.presence { color: #4fae4e; }

/* Поиск по диалогам. Прячется за кнопкой: строка поиска, висящая всегда,
   съедает экран телефона, а ищут раз в день. */
#search-bar {
  display: flex; gap: 8px; align-items: center;
  padding: 8px 12px; border-bottom: 1px solid var(--line);
  background: var(--bg); position: sticky; top: 0; z-index: 5;
}
#search {
  flex: 1; font: inherit; font-size: 16px;   /* 16px — иначе iOS зумит поле */
  padding: 10px 12px; border-radius: 10px;
  border: 1px solid var(--line); background: var(--bg); color: var(--text);
}
#search-clear {
  border: none; background: none; color: var(--muted);
  font-size: 18px; padding: 6px 8px; cursor: pointer;
}

/* Пометка черновика в списке чатов: недописанное важнее последней реплики,
   иначе про него забываешь до следующего случайного захода в чат. */
.draft-mark { color: #e53935; }

/* Чужой черновик над полем ввода. Не запрещаем писать — просто показываем,
   что ответ уже готовят: жёсткая блокировка мешала бы в запаре сильнее. */
.others-draft {
  display: flex; gap: 6px; align-items: baseline;
  padding: 6px 14px; font-size: 13px; color: var(--muted);
  border-top: 1px solid var(--line);
}
.others-draft[hidden] { display: none; }
/* Полоска чужого черновика нажимается: текст встаёт в поле ввода. */
.others-draft { cursor: pointer; }

/* Двойное нажатие на пузырь — ❤️ как в Telegram: сердце всплывает над пузырём. */
.msg .dbl-heart {
  position: absolute; left: 50%; top: 40%; font-size: 34px; pointer-events: none;
  transform: translate(-50%, -50%) scale(.4); opacity: 0;
  animation: сердце .7s ease-out forwards;
}
@keyframes сердце {
  20% { opacity: 1; transform: translate(-50%, -60%) scale(1.15); }
  100% { opacity: 0; transform: translate(-50%, -140%) scale(1); }
}

/* «Ответить» над пузырём при наведении мышью — только там, где есть мышь. */
.msg .q-hover { display: none; }
@media (hover: hover) and (pointer: fine) {
  .msg .q-hover {
    display: block; position: absolute; top: -22px; right: 6px; z-index: 2;
    width: auto; height: auto; padding: 2px 9px; border-radius: 10px; font-size: 12px;
    background: var(--bg); color: var(--muted); border: 1px solid var(--line);
    box-shadow: 0 1px 3px rgba(0,0,0,.12); opacity: 0; pointer-events: none;
    transition: opacity .12s;
  }
  .msg.out .q-hover { right: auto; left: 6px; }
  .msg:hover .q-hover { opacity: 1; pointer-events: auto; }
  .msg .q-hover:hover { color: var(--accent); }
}
.others-draft b { color: var(--accent); font-weight: 600; }
.others-draft .what-text {
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap; flex: 1;
}
/* Крестик держим бледным: убирать чужой черновик — действие редкое, и
   заметная кнопка над полем ввода мешала бы больше, чем сам черновик. */
.others-draft .drop-draft {
  flex: none; border: 0; background: none; cursor: pointer;
  color: var(--muted); opacity: .6; font-size: 14px; line-height: 1;
  padding: 2px 4px; border-radius: 6px;
}
.others-draft .drop-draft:hover { opacity: 1; background: var(--bg-chat); }

/* Заголовок работает кнопкой — показываем это, но без вида ссылки. */
#inbox-title { cursor: pointer; user-select: none; }
#inbox-title.filtered { color: var(--accent); }

/* Операторы */
.op-list { list-style: none; margin: 0; padding: 0; }
.op {
  border: 1px solid var(--line); border-radius: 12px;
  padding: 12px; margin-bottom: 12px;
}
.op-head { display: flex; align-items: center; gap: 8px; }
.op-name {
  flex: 1; font: inherit; font-size: 16px; font-weight: 600;
  border: none; background: none; color: var(--text); padding: 4px 0;
}
.op-me { font-size: 12px; color: var(--muted); }
.op-rights { margin: 8px 0; display: flex; flex-direction: column; gap: 2px; }
.op-row { display: flex; gap: 8px; align-items: center; flex-wrap: wrap; }
.op-pin {
  flex: 1; min-width: 120px; font: inherit; font-size: 16px;
  padding: 8px 10px; border-radius: 8px;
  border: 1px solid var(--line); background: var(--bg); color: var(--text);
}
.op-state:empty { display: none; }

/* Объявление отдельной строкой между именем и превью — как в списке Авито:
   имя крупно, по какому объекту пишут — мельче, сама реплика ещё бледнее. */
.row-item {
  grid-area: item; font-size: 13px; color: var(--muted);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.row-item[hidden] { display: none; }

/* Внутренняя заметка: висит над перепиской мелко и бледно — не спорит с
   сообщениями, но всегда на глазах. Клиент её не видит. */
/* Обещания «сообщим, когда появится»: полоска над перепиской.
   Оранжевая, потому что это долг перед клиентом, а не справка. */
.waits { display: flex; flex-direction: column; gap: 4px;
         padding: 6px 14px; background: var(--bg-chat);
         border-bottom: 1px solid var(--line); }
.wait { display: flex; align-items: center; gap: 8px; font-size: 13px; }
.wait-what { flex: 1; min-width: 0; overflow: hidden;
             text-overflow: ellipsis; white-space: nowrap; color: #a35b17; }
.wait-fire { width: auto; padding: 3px 10px; border-radius: 8px;
             font-size: 12.5px; background: var(--accent); color: #fff;
             border: 0; cursor: pointer; }
.wait-drop { width: auto; padding: 3px 6px; background: transparent;
             border: 0; color: var(--muted); cursor: pointer; font-size: 14px; }
@media (prefers-color-scheme: dark) {
  html:not([data-theme="light"]) .wait-what { color: #ffbe7a; }
}
html[data-theme="dark"] .wait-what { color: #ffbe7a; }

.note-strip {
  padding: 6px 14px; font-size: 12.5px; line-height: 1.35;
  color: var(--muted); background: var(--bg-chat);
  border-bottom: 1px solid var(--line);
  cursor: pointer; white-space: pre-wrap;
}
.note-strip[hidden] { display: none; }
.note-add {
  align-self: flex-start; margin: 4px 14px 0; padding: 0;
  border: 0; background: none; color: var(--muted);
  font-size: 12px; opacity: .65; cursor: pointer; width: auto;
}
.note-add[hidden] { display: none; }

/* Строка со свёрнутыми выборками: одна кнопка и название включённой. */
#filter-line {
  display: flex; align-items: center; gap: 10px; padding: 6px 14px;
  border-bottom: 1px solid var(--line); background: var(--bg);
  font-size: 13px; color: var(--muted);
}
#filter-toggle {
  width: auto; height: auto; padding: 4px 10px; border-radius: 14px;
  border: 1px solid var(--line); background: var(--bg-chat);
  color: var(--text); font-size: 13px;
}
/* Общий .chip ниже по файлу выше на 4px — ряд #filter-line подпрыгивал. */
#filter-line .chip { padding: 4px 10px; }
#filter-toggle[aria-expanded="true"] .caret { display: inline-block;
                                              transform: rotate(180deg); }
#filter-now { color: var(--accent); font-weight: 600; }

/* Полоска выборок «по делу»: узкая, прокручивается вбок — на телефоне
   вертикальное место дороже горизонтального. */
#filter-bar {
  display: flex; gap: 8px; padding: 8px 14px;
  overflow-x: auto; scrollbar-width: none;
  /* Только горизонтальный жест: вертикальный с чипов уходил в страницу, а
     при клавиатуре ряд исключён из гашения touchmove (аудит 17.09.2026). */
  touch-action: pan-x;
  border-bottom: 1px solid var(--line); background: var(--bg);
}
#filter-bar::-webkit-scrollbar { display: none; }
#filter-bar[hidden] { display: none; }
.chip {
  flex: none; width: auto; padding: 6px 12px; border-radius: 16px;
  border: 1px solid var(--line); background: var(--bg-chat);
  color: var(--text); font-size: 13px; white-space: nowrap; cursor: pointer;
}
.chip.on { background: var(--accent); border-color: var(--accent); color: #fff; }

/* Ярлык этапа сделки во второй строке: обведённый овал без заливки — залитые
   плашки заняты метками. Цвет по этапу. */
.chat .lead {
  display: inline-block; padding: 0 7px; margin-right: 6px; border-radius: 10px;
  font-size: 11px; line-height: 16px; font-weight: 600; white-space: nowrap;
  border: 1px solid currentColor; vertical-align: 1px;
}
.chat .lead[hidden] { display: none; }
.lead-new { color: #2e7d32; }
.lead-thinking { color: #b8860b; }
.lead-payment { color: #d9731a; }
.lead-shipping { color: #2f6fd6; }
.lead-refused { color: #8a94a0; }
/* Чип «Новые N»: зелёная обводка, как у овала; включённый — залит. Два
   класса в селекторе не случайно: одиночный .lead-chip перебивался общим
   .chip ниже по файлу, и чип оставался серым. */
.chip.lead-chip { color: #2e7d32; border-color: #2e7d32; font-weight: 600; }
.chip.lead-chip.on { background: #2e7d32; border-color: #2e7d32; color: #fff; }
#leads-count:empty { display: none; }
#board-link, #list-link { text-decoration: none; }
#board-link { margin-left: auto; }
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .lead-new,
  :root:not([data-theme="light"]) .chip.lead-chip:not(.on) { color: #93d69a; border-color: #93d69a; }
  :root:not([data-theme="light"]) .lead-thinking { color: #f0c65a; }
  :root:not([data-theme="light"]) .lead-payment { color: #f2a05e; }
  :root:not([data-theme="light"]) .lead-shipping { color: #8db4f2; }
}
:root[data-theme="dark"] .lead-new,
:root[data-theme="dark"] .chip.lead-chip:not(.on) { color: #93d69a; border-color: #93d69a; }
:root[data-theme="dark"] .lead-thinking { color: #f0c65a; }
:root[data-theme="dark"] .lead-payment { color: #f2a05e; }
:root[data-theme="dark"] .lead-shipping { color: #8db4f2; }

/* Пузырь служебного сообщения: синий, слева, с кнопками. Технический — и
   должен читаться так, а не как слова клиента. */
.msg.system.crm-ask {
  align-self: flex-start; background: #e3efff; color: #123c73; max-width: 88%;
  border-bottom-left-radius: 4px;
}
.msg.system .crm-head { display: block; font-size: 11px; font-weight: 700;
  letter-spacing: .04em; color: #1a56c4; margin-bottom: 2px; }
.msg.system .crm-btns { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 8px; }
.msg.system .crm-btn { width: auto; height: auto; padding: 4px 12px; border-radius: 14px;
  font-size: 13px; border: 1px solid #1a56c4; background: #fff; color: #1a56c4; cursor: pointer; }
.msg.system .crm-btn:disabled { opacity: .6; }
.msg.system.crm-ask .meta { color: #4a6a99; }
/* После нажатия — серая строка-след по центру, как разделитель дня. */
.msg.system.crm-done {
  align-self: center; background: transparent; box-shadow: none;
  font-size: 12px; color: var(--muted); padding: 2px 8px; max-width: 95%;
}
.msg.system.crm-done .meta { float: none; margin: 0 0 0 6px; }
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .msg.system.crm-ask { background: #16304f; color: #cfe1ff; }
  :root:not([data-theme="light"]) .msg.system .crm-btn { background: transparent; color: #8fd0ff; border-color: #8fd0ff; }
  :root:not([data-theme="light"]) .msg.system .crm-head,
  :root:not([data-theme="light"]) .msg.system.crm-ask .meta { color: #8fd0ff; }
}
:root[data-theme="dark"] .msg.system.crm-ask { background: #16304f; color: #cfe1ff; }
:root[data-theme="dark"] .msg.system .crm-btn { background: transparent; color: #8fd0ff; border-color: #8fd0ff; }
:root[data-theme="dark"] .msg.system .crm-head, :root[data-theme="dark"] .msg.system.crm-ask .meta { color: #8fd0ff; }

/* Подпись под именем меняется: сперва «был в сети», затем объявление.
   Текст подменяем в той же строке — так шапка не дёргается и ничего не
   накладывается друг на друга. */
#sub-line {
  transition: opacity .4s ease; display: flex; min-width: 0;
  /* В одну строку: длинное название объявления иначе делает шапку выше и
     сдвигает всю переписку. */
  white-space: nowrap; overflow: hidden;
}
/* Урезаем название, а цену держим целой — она короткая и нужна чаще, чем
   хвост названия. */
.sub-title { overflow: hidden; text-overflow: ellipsis; min-width: 0; }
.sub-price { flex: none; }
#sub-line.faded { opacity: 0; }
/* Блок с именем должен уметь ужиматься: без min-width флекс-элемент
   раздувается по содержимому, и длинное название уезжает за край экрана. */
.who { min-width: 0; flex: 1; }
.who .name {
  display: block; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}

/* Колокольчик в шапке чата: зажигается, когда напоминание поставлено. */
/* Колокольчик без напоминания — просто значок, без голубой плашки (она
   доставалась от базового button). С напоминанием — лёгкая голубая
   подложка, чтобы было видно, что оно стоит. */
#remind { opacity: .55; background: transparent; border-radius: 8px; }
#remind.on { opacity: 1; background: #e3efff; }
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) #remind.on { background: #16304f; }
}
:root[data-theme="dark"] #remind.on { background: #16304f; }

/* «Печатает» — зелёным, как присутствие: это тоже про «человек здесь». */
.typing-mark { color: #2e7d32; }
#sub-line.typing { color: #2e7d32; }

/* Фото во весь экран. Поверх всего, тёмный фон — как в мессенджерах. */
.lightbox {
  position: fixed; inset: 0; z-index: 200;
  background: rgba(0, 0, 0, .92);
  display: flex; align-items: center; justify-content: center;
  padding: env(safe-area-inset-top) 0 env(safe-area-inset-bottom);
}
.lightbox img {
  max-width: 100%; max-height: 100%;
  object-fit: contain; user-select: none;
}
/* Аватарка меньше экрана: растягиваем её до разумного размера, иначе
   открытая «во весь экран» карточка показывает кружок с ноготь. */
.lightbox img.avatar-photo {
  width: min(100%, 480px); height: auto;
}
.lb-close {
  position: absolute; top: max(12px, env(safe-area-inset-top)); right: 12px;
  width: 40px; height: 40px; border-radius: 50%; border: 0;
  background: rgba(255, 255, 255, .15); color: #fff; font-size: 18px;
}
img.media { cursor: zoom-in; }
/* Фото с известными размерами: рамка по пропорциям (ширина и высота заданы
   inline из рамкаФото), как в Telegram — вертикальный скриншот высокий,
   горизонтальное фото широкое, ничего не обрезается. */
img.media.photo.sized { object-fit: contain; max-width: 100%; }

/* Реакции под пузырём — мелкой плашкой, как в мессенджерах. */
/* Реакция висит отдельной строкой под текстом и чуть растягивает пузырь —
   как в телеграме. Инлайновая плашка липла к последнему слову и терялась. */
.reacts {
  /* Строку плашке даёт <br> в самой разметке, а не display: block. Разница
     принципиальная: инлайновая плашка остаётся в потоке, и время (оно
     float: right) встаёт справа от неё, на той же строке. Блочная выталкивала
     время на третью строку — пузырь рос вдвое больше нужного.
     А без переноса вовсе плашка липла к последнему слову
     («Спасибо огромное!!) ❤️») и читалась как часть самого сообщения. */
  display: inline-flex; align-items: center; gap: 4px;
  margin: 6px 0 0; padding: 3px 9px; border-radius: 13px;
  background: rgba(0, 0, 0, .07); font-size: 15px; line-height: 1.1;
}
.msg.out .reacts { background: rgba(0, 0, 0, .09); }

/* Цитата в пузыре — как в телеграме: подложка, толстая полоска слева и имя
   автора сверху. Без имени по цитате не понять, кого именно цитируют, а это
   в переписке на четверых главное. */
.quote {
  display: block; margin-bottom: 4px; padding: 3px 8px 4px 8px;
  border-left: 3px solid var(--accent); border-radius: 4px;
  background: rgba(47, 127, 209, .10); color: var(--text);
  font-size: 13px; line-height: 1.25;
}
.quote .q-who { display: block; font-weight: 600; color: var(--accent);
                overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.quote .q-body { display: block; color: var(--muted);
                 overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.quote.mine { border-left-color: #2e7d32; background: rgba(46, 125, 50, .10); }
.quote.mine .q-who { color: #2e7d32; }

/* Пузырь на свайпе едет за пальцем; у порога чуть подсвечиваем, чтобы было
   видно, что отпускание сработает. */
.msg.will-quote { box-shadow: 0 1px 1px rgba(0,0,0,.08), -3px 0 0 var(--accent); }

/* На что отвечаем — над полем ввода. */
#quote-bar {
  display: flex; gap: 8px; align-items: center;
  padding: 6px 14px; font-size: 13px; color: var(--muted);
  border-top: 1px solid var(--line);
}
#quote-bar[hidden] { display: none; }
#quote-bar .q-text { flex: 1; overflow: hidden; text-overflow: ellipsis;
                     white-space: nowrap; }
#quote-bar .q-drop { border: 0; background: none; color: var(--muted);
                     font-size: 16px; width: auto; padding: 2px 4px; }

/* Закреплённое сообщение — узкой полоской под шапкой, всегда на виду. */
.pinned-strip {
  display: flex; gap: 8px; align-items: center;
  padding: 6px 14px; font-size: 13px; cursor: pointer;
  background: var(--bg-chat); border-bottom: 1px solid var(--line);
}
.pinned-strip[hidden] { display: none; }
.pinned-strip .pin-text { flex: 1; overflow: hidden; text-overflow: ellipsis;
                          white-space: nowrap; color: var(--muted); }
.pinned-strip .pin-drop { border: 0; background: none; color: var(--muted);
                          font-size: 15px; width: auto; padding: 2px 4px; }
/* Подсветка сообщения, к которому прыгнули по закреплению. */
.msg.flash { animation: вспышка 1.2s ease-out; }
@keyframes вспышка {
  0% { background: rgba(82, 148, 226, .35); }
  100% { background: inherit; }
}

/* Выбор чата для пересылки. */
.fwd-search {
  width: 100%; font: inherit; font-size: 16px; padding: 10px 12px;
  border-radius: 10px; border: 1px solid var(--line);
  background: var(--bg); color: var(--text);
}
/* flex: none: единственный прокручиваемый ребёнок flex-колонки окна под
   клавиатурой сжимался в полоску — набираешь имя, а чатов не видно (аудит
   апарт-инбокса 17.09.2026). 40vh, а не 46: с flex: none коробка без
   клавиатуры не влезала. */
.fwd-list { list-style: none; margin: 0; padding: 0; max-height: 40vh;
            overflow-y: auto; flex: none; }
.fwd-list li {
  padding: 11px 4px; border-bottom: 1px solid var(--line); cursor: pointer;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.fwd-list li:active { background: var(--bg-chat); }

/* Кнопка записи: во время записи заметно горит — иначе непонятно, идёт ли. */
/* touch-action и запрет выделения обязательны: иначе Safari на долгом
   нажатии показывает лупу с «копировать» и забирает жест себе. */
#mic { background: transparent; color: var(--muted); font-size: 19px;
       touch-action: none; -webkit-touch-callout: none;
       -webkit-user-select: none; user-select: none;
       transition: transform .14s, box-shadow .14s, background .14s; }
/* Во время записи кнопка вырастает: держать палец на маленьком кружке неудобно,
   и промах по нему обрывал запись. */
#mic.recording { background: #2f7fd1; color: #fff; transform: scale(1.5);
                 box-shadow: 0 0 0 12px rgba(47,127,209,.16); }
/* Ждём микрофон: кнопка уже выросла под палец, но серая — записи ещё нет. */
#mic.waiting { background: #9aa4ad; color: #fff; transform: scale(1.5);
               box-shadow: 0 0 0 12px rgba(154,164,173,.18); }
#mic.cancelling { background: #e53935;
                  box-shadow: 0 0 0 12px rgba(229,57,53,.16); }

/* Панель записи занимает место поля ввода, а не ложится поверх: абсолютная
   позиция промахивалась мимо строки — форма выше поля на safe-area снизу. */
/* Пока идёт запись, всё остальное прячем: писать текст всё равно нельзя. */
@keyframes rec-pulse {}

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

/* Карточка контакта: имя и нажимаемый телефон — как в мессенджерах. */

/* Окно пересылки: чипы каналов, заголовок списка и две строки на диалог. */

/* У кружка пузыря быть не должно вовсе — в мессенджере он висит прямо на фоне
   переписки. Прямоугольная подложка вокруг круга выглядит рамкой от картины. */

/* Кружок из телеграма: круглым он и записан, и показывать его прямоугольником
   значит обрезать человеку лицо по краям. */

/* Полоска выбранных файлов: миниатюры с крестиком и обрезкой по нажатию. */
/* Селектор с id намеренно: правило #attach-bar span { flex: 1 } старше
   классового и растягивало миниатюры в широкие полосы — по ним не понять,
   что за фото отправляешь. */

/* Обрезка фото. Рамку двигают пальцем, углы тянут — как в галерее телефона. */

/* Поле с кнопкой в одну строку: «Кому» + «Вставить». */

/* Пузырь загрузки: сам файл и кружок, который заполняется по кругу, — как в
   мессенджерах. Глаз в момент отправки смотрит в переписку, а не под неё. */
/* Отправляет воркер — процентов он не сообщает, поэтому просто крутим. */
@keyframes up-spin {}

/* Чем сейчас занято неотправленное сообщение: «сжимаю видео…». */

/* Полоска «непрочитанные»: во всю ширину, чтобы граница читалась сразу. */

/* Ручная пометка «вернуться к этому»: синяя точка без числа. */
/* Строка едет за пальцем, у порога подсвечивается справа. */

/* Рамка вместо превью: файл остался у воркера, показать нечего. */

/* Подключение канала по коду — прямо в настройках. */

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

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

/* ---------- найденное в тексте переписки ---------- */
/* Отдельным блоком под списком чатов: чат и фраза внутри чата — разные вещи,
   и в одной ленте оператор перестаёт понимать, на что смотрит. */

/* Куда прыгнули — из поиска или по цитате. Подсветка гаснет сама, чтобы не
   мешать читать дальше. */
/* Цитата кликабельна: ведёт к тому сообщению, на которое отвечают. */
@keyframes нашлось {
}

/* ---------- таблица «кто отвечал» ---------- */
/* Автоответы — не работа человека, и в таблице должны читаться как фон. */

/* ---------- дни недели у правила автоответа ---------- */

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

/* ---------- перевод ---------- */
/* Перевод под оригиналом и мельче: оригинал главный, перевод — подсказка. */
/* «en» — в правом верхнем углу поля ввода, мелко и полупрозрачно. Появляется
   только когда нужен: в переписке с иностранцем или когда набран латинский
   текст. Постоянно висящая кнопка в углу мешает читать написанное. */
#composer #tr-btn {
  position: absolute; top: 5px; right: 70px; z-index: 2;
  width: auto; height: auto; min-width: 0;
  padding: 2px 4px;
  font-size: 11px; font-weight: 700; letter-spacing: .5px; line-height: 1;
  text-transform: lowercase;
  color: var(--accent); background: transparent; border: 0; border-radius: 0;
  opacity: .45; cursor: pointer;
}
#composer #tr-btn:active, #composer #tr-btn.busy { opacity: 1; }
#composer #tr-btn[disabled] { opacity: .3; }
/* Пока кнопка видна — отступ под неё, чтобы текст не заезжал под буквы. */
#composer.with-tr #text { padding-right: 34px; }

/* Панель записи занимает место поля ввода, а не ложится поверх: абсолютная
   позиция промахивалась мимо строки — форма выше поля на safe-area снизу. */
#rec-bar {
  flex: 1; min-width: 0; height: 42px; display: flex; align-items: center;
  gap: 10px; padding: 0 14px; border-radius: 20px; font-size: 15px;
  color: var(--text); background: var(--bg); border: 1px solid var(--line);
}
#rec-bar[hidden] { display: none; }
/* Пока идёт запись, всё остальное прячем: писать текст всё равно нельзя. */
#composer.recording #text, #composer.recording #attach, #composer.recording #scroll-down,
#composer.recording #scripts-btn, #composer.recording #schedule { display: none; }
.rec-dot { width: 9px; height: 9px; border-radius: 50%; background: #e53935;
           flex-shrink: 0; animation: rec-pulse 1.3s infinite; }
@keyframes rec-pulse { 50% { opacity: .1; } }
.rec-time { font-variant-numeric: tabular-nums; min-width: 42px; }
/* Ждём микрофон: точка не мигает, счётчик приглушён — записи ещё нет. */
#rec-bar.waiting .rec-dot { animation: none; opacity: .35; }
#rec-bar.waiting .rec-time { opacity: .35; }
.rec-hint { flex: 1; text-align: center; color: var(--muted);
            white-space: nowrap; overflow: hidden; }
#composer #rec-bar button { width: auto; height: auto; border-radius: 8px;
  padding: 6px 10px; font-size: 15px; font-weight: 600; background: transparent; }
#composer #rec-bar .rec-cancel { color: #e53935; }
#composer #rec-bar .rec-send { color: #2f7fd1; margin-left: auto; }

/* Замок висит над кнопкой: ведём палец вверх — запись остаётся без руки. */
#rec-lock {
  /* Кнопка при записи вырастает в полтора раза и вылезает над строкой ввода —
     замок стоял на ней: поднимаем выше. */
  position: absolute; right: 12px; bottom: calc(100% + 14px); z-index: 3;
  display: flex; flex-direction: column; align-items: center; line-height: 1;
  gap: 3px; padding: 9px 7px; border-radius: 16px; font-size: 14px;
  color: var(--muted); background: var(--bg); border: 1px solid var(--line);
  transition: transform .14s, color .14s;
}
#rec-lock span { font-size: 11px; }
#rec-lock.armed { color: #2f7fd1; transform: translateY(-6px); }

#rec-toast {
  position: fixed; left: 50%; bottom: 84px; transform: translateX(-50%);
  z-index: 40; padding: 9px 15px; border-radius: 18px; font-size: 14px;
  color: #fff; background: rgba(0,0,0,.78); pointer-events: none;
}
/* На странице чата — от посаженной страницы, а не от окна: fixed при
   открытой клавиатуре iPhone стоял под ней, и «не отправилось: …» никто не
   видел. body.chatpage — position: relative и под клавиатурой сидит на
   видимой области (аудит 17.09.2026). */
body.chatpage #rec-toast { position: absolute; }

/* Карточка контакта: имя и нажимаемый телефон — как в мессенджерах. */
.contact-card {
  display: flex; flex-direction: column; gap: 2px;
  padding: 6px 10px; border-left: 3px solid var(--accent);
  background: rgba(0, 0, 0, .04); border-radius: 6px;
}
.contact-card .c-name { font-weight: 600; }
.contact-card .c-tel { color: var(--accent); text-decoration: none; }

/* Окно пересылки: чипы каналов, заголовок списка и две строки на диалог. */
.fwd-chips { display: flex; gap: 6px; flex-wrap: wrap; margin-bottom: 8px; }
.fwd-chips .chip {
  padding: 5px 10px; border-radius: 14px; font-size: 13px;
  border: 1px solid var(--line); background: var(--bg); color: var(--muted);
  width: auto; height: auto;
}
.fwd-chips .chip.on { background: var(--accent); border-color: var(--accent);
                      color: #fff; }
.fwd-head { font-size: 12px; color: var(--muted); margin: 8px 0 4px; }
.fwd-list li { display: flex; flex-direction: column; gap: 1px; }
.fwd-list .fwd-name { font-size: 15px; }
.fwd-list .fwd-where { font-size: 12px; color: var(--muted);
                       overflow: hidden; text-overflow: ellipsis;
                       white-space: nowrap; }

/* У кружка пузыря быть не должно вовсе — в мессенджере он висит прямо на фоне
   переписки. Прямоугольная подложка вокруг круга выглядит рамкой от картины. */
.msg.round-note {
  background: none; box-shadow: none; padding: 0; max-width: none;
}
.msg.round-note .meta { float: none; display: block; text-align: right;
                        margin: 2px 4px 0 0; }

/* Кружок из телеграма: круглым он и записан, и показывать его прямоугольником
   значит обрезать человеку лицо по краям. */
.video-frame.round, video.media.round {
  border-radius: 50%; overflow: hidden;
  width: 200px; height: 200px; max-width: 62vw; max-height: 62vw;
}
.video-frame.round img.media { width: 100%; height: 100%; object-fit: cover; }
video.media.round { object-fit: cover; background: #000; }

.theme-pick, .chip-row { display: flex; gap: 8px; flex-wrap: wrap; }

/* Полоска выбранных файлов: миниатюры с крестиком и обрезкой по нажатию. */
#attach-bar { flex-wrap: wrap; }
#attach-thumbs { display: flex; gap: 6px; flex-wrap: wrap; width: 100%; }
/* Селектор с id намеренно: правило #attach-bar span { flex: 1 } старше
   классового и растягивало миниатюры в широкие полосы — по ним не понять,
   что за фото отправляешь. */
#attach-thumbs .att {
  position: relative; flex: 0 0 64px; width: 64px; height: 64px;
  border-radius: 8px; overflow: hidden; background: var(--bg-chat);
}
#attach-thumbs .att img, #attach-thumbs .att video {
  width: 100%; height: 100%; object-fit: cover; display: block;
}
#attach-thumbs .att .x {
  position: absolute; top: 1px; right: 1px; width: 18px; height: 18px;
  border-radius: 50%; border: 0; padding: 0; line-height: 18px;
  background: rgba(0,0,0,.6); color: #fff; font-size: 12px;
}
#attach-thumbs .att .cut {
  position: absolute; bottom: 1px; left: 1px; width: 18px; height: 18px;
  border-radius: 50%; border: 0; padding: 0; line-height: 18px;
  background: rgba(0,0,0,.6); color: #fff; font-size: 11px;
}

/* Обрезка фото. Рамку двигают пальцем, углы тянут — как в галерее телефона. */
#crop-back {
  position: fixed; inset: 0; z-index: 60; background: rgba(0,0,0,.92);
  display: flex; flex-direction: column;
}
#crop-stage { flex: 1; position: relative; overflow: hidden; }
#crop-stage img { position: absolute; inset: 0; margin: auto;
                  max-width: 100%; max-height: 100%; }
#crop-frame {
  position: absolute; border: 2px solid #fff; box-sizing: border-box;
  box-shadow: 0 0 0 9999px rgba(0,0,0,.45); touch-action: none;
}
#crop-frame .h {
  position: absolute; width: 22px; height: 22px; background: transparent;
}
#crop-frame .h::after { content: ""; position: absolute; inset: 4px;
                        border: 2px solid #fff; border-radius: 2px; }
#crop-frame .nw { left: -11px; top: -11px; cursor: nwse-resize; }
#crop-frame .se { right: -11px; bottom: -11px; cursor: nwse-resize; }
#crop-bar {
  display: flex; gap: 10px; align-items: center; justify-content: space-between;
  padding: 12px 16px; padding-bottom: max(12px, env(safe-area-inset-bottom));
  background: #000; color: #fff;
}
#crop-bar button { width: auto; height: auto; padding: 8px 14px;
                   border-radius: 10px; background: transparent; color: #fff;
                   font-size: 15px; }
#crop-bar .ok { background: var(--accent); font-weight: 600; }

/* Поле с кнопкой в одну строку: «Кому» + «Вставить». */
.row-inline { display: flex; gap: 8px; align-items: center; }
.row-inline input { flex: 1; min-width: 0; }
.row-inline button { width: auto; height: auto; padding: 9px 14px;
                     border-radius: 10px; font-size: 15px; }

/* Пузырь загрузки: сам файл и кружок, который заполняется по кругу, — как в
   мессенджерах. Глаз в момент отправки смотрит в переписку, а не под неё. */
.msg.local-upload { position: relative; padding: 4px; }
.msg.local-upload .media { max-width: 220px; border-radius: 10px; display: block; }
.up-ring {
  position: absolute; inset: 0; display: flex; flex-direction: column;
  align-items: center; justify-content: center; gap: 2px;
  color: #fff; pointer-events: none;
}
.up-ring svg { width: 56px; height: 56px; transform: rotate(-90deg); }
.up-ring circle { fill: rgba(0, 0, 0, .45); stroke-width: 3.5; }
.up-ring circle.bg { stroke: rgba(255, 255, 255, .3); }
.up-ring circle.val { fill: none; stroke: #fff; stroke-linecap: round;
                      transition: stroke-dashoffset .2s linear; }
/* Отправляет воркер — процентов он не сообщает, поэтому просто крутим. */
.up-ring.wait svg { animation: up-spin 1.1s linear infinite; }
@keyframes up-spin { to { transform: rotate(270deg); } }
.up-ring .up-pct { position: absolute; font-size: 12px; font-weight: 600;
                   font-variant-numeric: tabular-nums; }

/* Чем сейчас занято неотправленное сообщение: «сжимаю видео…». */
.send-note { display: block; margin-top: 3px; font-size: 12px;
             color: var(--muted); font-style: italic; }

/* Полоска «непрочитанные»: во всю ширину, чтобы граница читалась сразу. */
.unread-sep {
  align-self: stretch; width: 100%; border-radius: 0;
  background: rgba(51, 144, 236, .12); color: var(--accent);
  font-size: 12px; text-align: center; padding: 3px 0;
}

/* Ручная пометка «вернуться к этому»: синяя точка без числа. */
.mark-dot {
  display: inline-block; width: 10px; height: 10px; border-radius: 50%;
  background: var(--accent); margin-left: 6px; vertical-align: middle;
}
.mark-dot[hidden] { display: none; }
/* Строка едет за пальцем, у порога подсвечивается справа. */
.chat.will-mark { box-shadow: inset -3px 0 0 var(--accent); }

/* Рамка вместо превью: файл остался у воркера, показать нечего. */
.up-stub { display: flex; align-items: center; justify-content: center;
           width: 200px; height: 130px; border-radius: 10px;
           background: var(--bg-chat); font-size: 34px; }

/* Подключение канала по коду — прямо в настройках. */
.connect { display: flex; gap: 8px; flex-wrap: wrap; align-items: center; }
.connect select, .connect input { flex: 1; min-width: 120px; }
.connect button { width: auto; padding: 9px 14px; border-radius: 10px; }
#connect-box { margin-top: 12px; text-align: center; }
#connect-qr { width: 240px; max-width: 80vw; image-rendering: pixelated;
              background: #fff; padding: 8px; border-radius: 10px; }

/* Метки диалога — подписями под строкой: цвет подсказывает, слово называет.
   Строка от этого чуть выше, но только у помеченных чатов. */
.row-labels { display: flex; gap: 4px; flex-wrap: wrap; margin-top: 3px; }
.row-labels[hidden] { display: none; }
.row-tag {
  font-size: 11px; line-height: 1.4; padding: 0 6px; border-radius: 8px;
  border: 1px solid currentColor; white-space: nowrap;
}
/* Точка в списке действий: связь «цвет — смысл» видна там же, где метку ставят. */
.sheet-dot { width: 10px; height: 10px; border-radius: 50%;
             display: inline-block; margin-right: 8px; vertical-align: middle; }
/* Метки в шапке чата — уже словами: там есть место и важен смысл. */
.chat-labels { display: flex; gap: 6px; flex-wrap: wrap; padding: 6px 14px;
               background: var(--header); border-bottom: 1px solid var(--line); }
.chat-labels .lbl {
  display: inline-flex; align-items: center; gap: 5px; font-size: 12px;
  padding: 3px 9px; border-radius: 12px; border: 1px solid var(--line);
  background: var(--bg); color: var(--text);
}
.chat-labels .lbl i { width: 8px; height: 8px; border-radius: 50%; }
.chat-labels .lbl.off { opacity: .45; }

/* Управление каналом в настройках. Выключенный виден, но приглушён: он
   остаётся в базе со всей перепиской, просто сообщения по нему не ходят. */
.accounts li.off { opacity: .55; }
.accounts li.tappable { cursor: pointer; }
.accounts li.tappable:active { background: var(--bg-chat); }
/* Признак «здесь есть что нажать» — тонкая стрелка, а не кнопки в строке. */
.accounts li.tappable::after { content: "›"; color: var(--muted); font-size: 18px;
                               margin-left: 2px; }

/* ---------- найденное в тексте переписки ---------- */
/* Отдельным блоком под списком чатов: чат и фраза внутри чата — разные вещи,
   и в одной ленте оператор перестаёт понимать, на что смотрит. */
.hits-title {
  padding: 14px 12px 6px;
  font-size: 13px;
  font-weight: 600;
  color: var(--muted);
  border-top: 1px solid var(--line);
}
#hits { list-style: none; margin: 0; padding: 0; }
.hit a {
  display: block;
  padding: 8px 12px 10px;
  border-bottom: 1px solid var(--line);
  color: var(--text);
  text-decoration: none;
}
.hit-head { display: flex; justify-content: space-between; gap: 8px; }
.hit-chat {
  font-weight: 600;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.hit-time { color: var(--muted); font-size: 13px; flex: none; }
.hit-text {
  display: block;
  margin-top: 2px;
  /* Три строки — предел: дальше уже не «где это было», а чтение переписки. */
  display: -webkit-box; -webkit-line-clamp: 3; -webkit-box-orient: vertical;
  overflow: hidden;
}
.hit-text b { background: rgba(51, 144, 236, .22); font-weight: 600; }
.hit-acc { display: block; margin-top: 2px; font-size: 12px; color: var(--muted); }

/* Куда прыгнули — из поиска или по цитате. Подсветка гаснет сама, чтобы не
   мешать читать дальше. */
.msg.found { animation: нашлось 2.4s ease-out; }
/* Цитата кликабельна: ведёт к тому сообщению, на которое отвечают. */
.quote { cursor: pointer; }
@keyframes нашлось {
  0%, 55% { box-shadow: 0 0 0 3px rgba(51, 144, 236, .55); }
  100%    { box-shadow: 0 0 0 3px rgba(51, 144, 236, 0); }
}

/* ---------- таблица «кто отвечал» ---------- */
table.ops { width: 100%; border-collapse: collapse; font-size: 15px; }
table.ops th {
  text-align: left; font-weight: 600; font-size: 13px; color: var(--muted);
  padding: 4px 6px; border-bottom: 1px solid var(--line);
}
table.ops td { padding: 7px 6px; border-bottom: 1px solid var(--line); }
table.ops th:not(:first-child), table.ops td:not(:first-child) { text-align: right; }
/* Автоответы — не работа человека, и в таблице должны читаться как фон. */
table.ops tr.auto td { color: var(--muted); }

/* ---------- дни недели у правила автоответа ---------- */
.days { padding: 6px 0 2px; }
.days-title { display: block; font-size: 13px; color: var(--muted); margin-bottom: 6px; }
.days-row { display: flex; gap: 6px; flex-wrap: wrap; }
.day { position: relative; }
.day input { position: absolute; opacity: 0; pointer-events: none; }
.day span {
  display: inline-block; min-width: 38px; padding: 7px 0;
  text-align: center; border: 1px solid var(--line); border-radius: 9px;
  color: var(--muted); background: transparent; cursor: pointer;
  user-select: none;
}
.day input:checked + span {
  background: var(--accent); border-color: var(--accent); color: #fff;
}

/* ---------- сообщение, удалённое собеседником ---------- */
/* Текст оставляем зачёркнутым, а не прячем: это часть разговора, и без него
   наш ответ висит без вопроса. */
.msg.peer-deleted .text, .msg.gone .text, .msg.deleting .text {
  text-decoration: line-through;
  opacity: .65;
}
/* Вложение зачеркнуть нельзя — гасим целиком, иначе удалённое фото выглядит
   таким же живым, как остальные. */
.msg.gone img, .msg.gone video, .msg.deleting img, .msg.deleting video,
.msg.gone .file, .msg.deleting .file { opacity: .45; }
.gone-mark.bad { color: #e53935; font-style: normal; }
.gone-mark {
  display: block;
  margin-top: 2px;
  font-size: 11px;
  color: var(--muted);
  font-style: italic;
}

/* ---------- перевод ---------- */
/* Перевод под оригиналом и мельче: оригинал главный, перевод — подсказка. */
.tr-text {
  display: block;
  margin-top: 3px;
  padding-top: 3px;
  border-top: 1px dashed rgba(128, 128, 128, .35);
  font-size: 13px;
  opacity: .8;
  white-space: pre-wrap;
}
/* Значок языка в углу поля ввода: заметен, когда ищешь, и не мешает, когда
   не нужен. */
#tr-btn {
  position: absolute;
  top: 2px;
  right: 4px;
  z-index: 2;
  padding: 1px 5px;
  font-size: 10px;
  font-weight: 700;
  letter-spacing: .5px;
  line-height: 1.4;
  color: var(--muted);
  background: transparent;
  border: 1px solid var(--line);
  border-radius: 6px;
  cursor: pointer;
}
#tr-btn:active, #tr-btn.busy { color: var(--accent); border-color: var(--accent); }
#tr-btn[disabled] { opacity: .5; }
#composer { position: relative; }

/* Маленькая кнопка перевода в пузыре — только у чужих сообщений без
   кириллицы. Автоперевода нет: платить за каждое сообщение незачем. */
.tr-ask {
  display: block;
  margin-top: 3px;
  padding: 0;
  font-size: 12px;
  color: var(--accent);
  background: transparent;
  border: none;
  cursor: pointer;
  opacity: .85;
}
.tr-ask[disabled] { color: var(--muted); }

/* Итоги поиска человека по всем аккаунтам. Список, а не одна строка,
   потому что ответ у каждого аккаунта свой: человека с закрытым поиском по
   номеру находят только те, у кого он записан в контактах. */
#found:not(:empty) { margin-top: 8px; }
#found .lk-yes, #found .lk-no, #found .lk-wait, #found .lk-hint {
  padding: 4px 0;
  font-size: 14px;
  line-height: 1.35;
}
#found .lk-yes { color: var(--text); }
#found .lk-no, #found .lk-wait { color: var(--muted); }
#found .lk-hint { color: var(--muted); font-size: 13px; padding-top: 8px; }
#found .lk-pick {
  margin-left: 6px;
  padding: 2px 8px;
  font-size: 13px;
  color: var(--accent);
  background: transparent;
  border: 1px solid var(--accent);
  border-radius: 12px;
  cursor: pointer;
}

/* ---------- оглавление настроек ---------- */
/* Девять разделов подряд на одной странице читались только пролистыванием
   целиком. Теперь оглавление: название, состояние подписью и стрелка.
   Подпись не украшение — по ней видно, надо ли вообще заходить внутрь. */
.settings.hub { background: var(--bg-chat); }
.hub-group {
  display: block;
  margin: 12px;
  background: var(--bg);
  border-radius: 14px;
  overflow: hidden;
}
.hub-item {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 11px 14px;
  color: var(--text);
  text-decoration: none;
  border-bottom: 1px solid var(--line);
}
.hub-item:last-child { border-bottom: 0; }
.hub-item:active { background: var(--bg-chat); }
/* Цветная плашка со значком: канал, люди, автоответы различаются с одного
   взгляда, без чтения. Размер 32 — крупнее теряет строку в высоте. */
.hub-ico {
  flex: 0 0 32px;
  width: 32px; height: 32px;
  display: flex; align-items: center; justify-content: center;
  border-radius: 9px;
  color: #fff;
}
.hub-ico svg { width: 20px; height: 20px; }
.ico-green  { background: #34c759; }
.ico-violet { background: #7d5fff; }
.ico-blue   { background: #3390ec; }
.ico-orange { background: #f5a623; }
.ico-teal   { background: #14b8a6; }
.ico-red    { background: #ef4444; }
.ico-indigo { background: #5b6ee1; }
.hub-text { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 1px; }
.hub-title { font-size: 16px; line-height: 1.25; }
.hub-sub { font-size: 13px; color: var(--muted); line-height: 1.25; }
.hub-go { color: var(--muted); font-size: 20px; line-height: 1; }
/* Оранжевая стрелка — единственное место, где список кричит: там что-то
   требует человека (канал отвалился, уведомления никому не приходят). */
.hub-go.warn { color: #e0803a; }
.hub-foot { padding: 6px 12px 24px; }
.hub-foot .note { padding: 0 2px 10px; }

/* Строка канала: логотип вместо цветной точки, номер под названием. */
.accounts .ch-ico { grid-area: dot; width: 28px; height: 28px; border-radius: 8px; }
.accounts .ch-text { grid-area: label; display: flex; flex-direction: column; gap: 1px;
                     min-width: 0; }
.accounts li:has(.ch-ico) {
  grid-template-columns: 28px 1fr auto 8px;
  /* Вторая строка — под пояснение «тихое подключение»: оно есть не всегда, а
     пустая строка грида места не занимает. Без своей области пояснение
     попадало в колонку значка шириной 28 px и рассыпалось по словам. */
  grid-template-areas: "dot label state dot2" ". note note note";
  padding: 9px 0;
}
.accounts .ch-note {
  grid-area: note; font-size: 12px; line-height: 1.3; color: var(--muted);
}
.accounts li:has(.ch-ico) .dot { grid-area: dot2; }
.accounts li:has(.ch-ico) .phone { grid-area: auto; font-size: 12px; }
.accounts li.tappable:has(.ch-ico)::after { content: none; }
button.wide.primary {
  width: 100%; margin-top: 12px;
  background: var(--accent); color: #fff; border: 0;
  border-radius: 12px; padding: 13px; font-size: 16px;
}
/* Значок канала в списке аккаунтов — тот же логотип, что в ленте чатов. */
.accounts .chan-slot { display: flex; align-items: center; justify-content: center; }
.accounts .chan-slot svg { width: 28px; height: 28px; border-radius: 50%; }
/* Список каналов карточкой, как оглавление: страница перестаёт быть
   «строками, приклеенными к краю экрана». */
.settings .accounts {
  background: var(--bg); border-radius: 14px; padding: 2px 12px; margin: 0;
}
.settings section:has(.accounts) { background: var(--bg-chat); border-bottom: 0; }
/* Название аккаунта — обычным весом: в списке из пятнадцати строк жирный
   шрифт перестаёт что-либо выделять и просто утяжеляет экран. */
.settings .accounts .label { font-weight: 500; font-size: 16px; }
.settings .accounts .state { font-size: 13px; }
.settings .connect {
  background: var(--bg); border-radius: 14px; padding: 12px; margin-top: 12px;
}
.settings .connect select, .settings .connect input { margin: 0 0 8px; }

/* ---------- операторы ---------- */
.op-face {
  flex: 0 0 34px; width: 34px; height: 34px;
  display: flex; align-items: center; justify-content: center;
  border-radius: 50%; color: #fff; font-size: 14px; font-weight: 600;
}
.op-face.big { width: 64px; height: 64px; font-size: 24px; margin: 0 auto 10px; }
.op-me {
  margin-left: 6px; padding: 1px 7px; border-radius: 9px;
  font-size: 11px; font-weight: 500;
  color: var(--accent); background: rgba(51,144,236,.14);
}
.hub-item .op-state { font-size: 13px; color: #34c759; }
.hub-item .op-state.off { color: var(--muted); }

.op-hero { padding: 18px 14px 6px; text-align: center; }
.op-hero #op-name {
  width: 100%; max-width: 320px; margin: 0 auto; text-align: center;
  font-size: 18px; font-weight: 600;
}
.op-hero .note { margin-top: 6px; }
.op-block { padding: 4px 14px 12px; }
.op-block h2 {
  font-size: 13px; text-transform: uppercase; letter-spacing: .4px;
  color: var(--muted); font-weight: 600; margin: 12px 0 6px;
}
.op-block .note { padding-bottom: 4px; }
/* Одно право — одна строка: название слева, переключатель справа. В прежнем
   виде галочка стояла ПЕРЕД текстом, и восемь строк читались как список
   покупок, а не как настройка доступа. */
.op-right {
  display: flex; align-items: center; gap: 12px;
  padding: 11px 0; border-bottom: 1px solid var(--line);
  font-size: 15px; cursor: pointer;
  /* Невидимый чекбокс ниже — position: absolute; без своей точки отсчёта он
     улетал к карточке, и при тапе по тумблеру Safari прокручивал страницу к
     нему — «всё пропало с экрана» (первый запуск у Асада, 19.09.2026). */
  position: relative;
}
.op-right:last-of-type { border-bottom: 0; }
.op-right > span:first-child { flex: 1; min-width: 0; }
.op-right .sw { position: absolute; opacity: 0; pointer-events: none; }
.op-right .sw-track {
  flex: 0 0 46px; width: 46px; height: 28px; border-radius: 14px;
  background: var(--line); position: relative; transition: background .15s;
}
.op-right .sw-track::after {
  content: ""; position: absolute; top: 2px; left: 2px;
  width: 24px; height: 24px; border-radius: 50%; background: #fff;
  box-shadow: 0 1px 3px rgba(0,0,0,.25); transition: transform .15s;
}
.op-right .sw:checked + .sw-track { background: var(--accent); }
.op-right .sw:checked + .sw-track::after { transform: translateX(18px); }
.op-card .op-row { display: flex; gap: 8px; align-items: center; }
.op-card .op-row input, .op-card .op-row select { margin: 0; }
a.wide.primary {
  display: block; text-align: center; text-decoration: none;
  width: 100%; margin-top: 12px; padding: 13px;
  background: var(--accent); color: #fff; border-radius: 12px; font-size: 16px;
}

/* ---------- переписка: геометрия по макету ---------- */
/* Одинаковый радиус без «хвоста» у последнего пузыря: хвост имеет смысл там,
   где сообщения идут вперемешку от разных людей, а в переписке с одним
   клиентом он только рвёт ритм ленты. */
.msg { border-radius: 18px; padding: 7px 12px; }
.msg.in { border-bottom-left-radius: 18px; }
.msg.out { border-bottom-right-radius: 18px; }
/* Сообщения одного отправителя стоят плотно, смена стороны — с воздухом:
   так видно реплики разговора, а не однородную ленту. */
#feed { gap: 2px; }
.msg.in + .msg.out, .msg.out + .msg.in { margin-top: 9px; }
.msg .meta { margin: 4px 0 0 10px; }
/* Картинка занимает пузырь целиком: рамка вокруг фото съедала ширину и
   делала вложение мельче, чем оно есть. */
.msg:has(.media) { padding: 3px; }
.msg:has(.media) .meta { margin: 2px 8px 4px 10px; }
.msg:has(.media) .text { display: block; padding: 4px 9px 2px; }

/* ---------- список чатов: плотнее ---------- */
/* Было до пяти строк на чат: имя, объявление, превью, метки, аккаунт. На
   экране помещалось восемь переписок из четырёхсот. Теперь две строки, а
   третья появляется только там, где есть метки или нужен ярлык аккаунта. */
.chat a {
  grid-template-areas: "av row1" "av row2" "av row3";
  padding: 8px 14px;
}
.chat .row1 { gap: 6px; }
/* Товар рядом с именем: «Ирина · обвес Camry» — одна мысль, и разносить её
   на две строки значило терять строку экрана на каждом чате. */
.chat .apt {
  flex: 0 1 auto; min-width: 0;
  font-size: 13px; color: var(--muted);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.chat .apt[hidden] { display: none; }
/* Порядок важности в строке: имя → товар → аккаунт → время. Ужимается всё
   в обратном порядке, поэтому у аккаунта самый большой коэффициент сжатия:
   название товара отвечает на вопрос «о чём речь», а ярлык аккаунта — только
   «кому отвечать», и его можно дочитать в самом чате. */
.chat .title { flex: 0 1 auto; min-width: 7em; }
.chat .apt { flex: 0 8 auto; }
.chat .row1 .account {
  margin-left: auto; flex: 0 200 auto; min-width: 0;
  font-size: 12px; color: var(--muted);
  overflow: hidden; text-overflow: clip; white-space: nowrap;
}
.chat .time { flex: none; }
.chat .row3 {
  grid-area: row3; display: flex; align-items: center; gap: 6px;
  min-width: 0; margin-top: 2px;
}
.chat .row3[hidden] { display: none; }
.chat .row3 .row-labels { margin: 0; }

/* Шапка: заголовок со счётчиком в одну строку и не переносится. */
h1 { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
#filter-line #account-filter {
  max-width: 46vw; font-size: 13px; padding: 4px 8px; border-radius: 14px;
}

/* ---------- шапка списка и панель каналов ---------- */
header .ico-btn, #menu-btn {
  flex: none; display: flex; align-items: center; justify-content: center;
  width: 34px; height: 34px; padding: 0;
  background: transparent; border: 0; color: var(--accent);
  text-decoration: none; cursor: pointer;
}
#menu-btn { color: var(--text); margin-left: -6px; }
header .ico-btn svg, #menu-btn svg {
  width: 23px; height: 23px; fill: none; stroke: currentColor;
  stroke-width: 1.9; stroke-linecap: round; stroke-linejoin: round;
}
header .ico-btn:active { opacity: .5; }

/* Панель выезжает слева и толкает список вправо — не на весь экран: видно,
   что список никуда не делся и панель закрывается касанием мимо. */
#side {
  position: fixed; top: 0; left: 0; bottom: 0; z-index: 20;
  width: 76vw; max-width: 300px;
  padding: max(12px, env(safe-area-inset-top)) 0 12px;
  background: var(--header); border-right: 1px solid var(--line);
  transform: translateX(-100%); transition: transform .2s ease;
  overflow-y: auto; overscroll-behavior: contain;
}
body.side-open #side { transform: none; }
#side-back {
  position: fixed; inset: 0; z-index: 15; background: rgba(0,0,0,.28);
  opacity: 0; transition: opacity .2s ease;
}
body.side-open #side-back { opacity: 1; }
/* Список остаётся на месте и виден полосой справа — по нему понятно, что
   панель наложена на то же окно и закрывается касанием мимо.
   Сдвигать сам body нельзя: он становится точкой отсчёта для position:fixed,
   и панель уезжает вместе с ним. */
body.side-open { overflow: hidden; }
#side { box-shadow: 0 0 24px rgba(0,0,0,.18); }
.side-head {
  padding: 6px 16px 10px; font-size: 13px; font-weight: 600;
  text-transform: uppercase; letter-spacing: .4px; color: var(--muted);
}
.side-item {
  display: flex; align-items: center; gap: 10px; width: 100%;
  padding: 11px 16px; border: 0; background: transparent;
  font: inherit; font-size: 16px; color: var(--text); text-align: left;
  cursor: pointer;
}
.side-item:active { background: var(--bg-chat); }
.side-item.on { color: var(--accent); font-weight: 600; }
.side-item .chan-slot { flex: none; width: 26px; height: 26px; display: flex; }
.side-item .chan-slot svg, .side-item .chan-slot img {
  width: 26px; height: 26px; border-radius: 50%;
}
.side-name { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis;
             white-space: nowrap; }

/* ---------- поиск внутри переписки ---------- */
#find-bar {
  display: flex; align-items: center; gap: 6px;
  padding: 7px 10px; background: var(--header);
  border-bottom: 1px solid var(--line);
}
#find-bar[hidden] { display: none; }
#find-input {
  flex: 1; min-width: 0; margin: 0; padding: 8px 12px;
  font-size: 16px;                /* меньше — iOS зумит страницу при фокусе */
  border: 1px solid var(--line); border-radius: 18px;
  background: var(--bg); color: var(--text);
}
#find-count { font-size: 13px; color: var(--muted); white-space: nowrap; }
#find-bar button {
  flex: none; width: 32px; height: 32px; padding: 0; margin: 0;
  background: transparent; border: 0; color: var(--accent); font-size: 16px;
}
/* Найденное — жёлтой заливкой по самому слову, а не по всему пузырю: так
   видно, за что зацепился поиск. Текущее совпадение обведено, чтобы не
   теряться среди десятка одинаковых. */
mark.find-hit {
  background: rgba(255,214,0,.55); color: inherit;
  border-radius: 3px; padding: 0 1px;
}
:root[data-theme="dark"] mark.find-hit { background: rgba(255,214,0,.35); }
.msg.found-now { outline: 2px solid var(--accent); outline-offset: 1px; }
/* Кнопка поиска в шапке чата — рисунком и без подложки: рядом с аватаркой
   любой фон читается как второй значок канала. */
#find-btn { background: transparent; border: 0; padding: 0 2px;
            color: var(--muted); line-height: 0; }
#find-btn svg { display: block; }
/* Крестик на пузыре загрузки: пока ролик ползёт вверх на телефонном
   интернете, передумать — обычное дело, а отменить было нечем. */
.local-upload { position: relative; }
.up-cancel {
  position: absolute; top: 6px; right: 6px;
  width: 30px; height: 30px; padding: 0; margin: 0;
  border: 0; border-radius: 50%;
  background: rgba(0,0,0,.45); color: #fff; font-size: 15px; line-height: 1;
  display: flex; align-items: center; justify-content: center;
}
.up-cancel:active { background: rgba(0,0,0,.65); }

/* Тишина по чату: значок рядом с именем и в строке списка. Не красный и не
   крупный — это состояние, а не тревога. */
.muted-mark { margin-left: 6px; font-size: 13px; opacity: .75; }
.chat .mute-mark, .chat .remind-mark, .chat .later-mark { font-size: 11px; flex-shrink: 0; opacity: .7; }

/* Голосовое: круглая кнопка, форма волны и время — как в мессенджерах.
   Системный <audio controls> занимал в сафари полпузыря и не показывал, о
   чём вообще речь: одна серая полоска на все сообщения. */
.voice {
  display: flex; align-items: center; gap: 10px;
  width: 268px; max-width: 100%; padding: 2px 0;
}
.voice .v-play {
  flex: none; width: 40px; height: 40px; border: 0; border-radius: 50%;
  background: var(--accent); cursor: pointer; padding: 0;
  display: flex; align-items: center; justify-content: center;
}
/* Треугольник и пауза рисуем рамками: две картинки ради двух состояний
   кнопки — лишние запросы на каждой переписке. */
.voice .v-play::before {
  content: ''; width: 0; height: 0; margin-left: 3px;
  border-left: 12px solid #fff;
  border-top: 8px solid transparent; border-bottom: 8px solid transparent;
}
.voice.playing .v-play::before {
  width: 12px; height: 14px; margin: 0; border: 0;
  border-left: 4px solid #fff; border-right: 4px solid #fff;
  box-sizing: border-box;
}
.voice .v-bars {
  flex: 1 1 auto; display: flex; align-items: center; gap: 2px;
  height: 26px; cursor: pointer; min-width: 0;
}
.voice .v-bars i {
  flex: 1 1 0; min-width: 2px; border-radius: 2px;
  background: var(--muted); opacity: .45;
}
.voice .v-bars i.on { background: var(--accent); opacity: 1; }
.voice .v-time {
  flex: none; font-size: 12px; color: var(--muted);
  font-variant-numeric: tabular-nums;
}

/* Аватарка в шапке раскрывается во весь экран — по ней иногда и узнают человека. */
#chat-avatar { cursor: zoom-in; }

/* Порядок скриптов: их набирается несколько десятков, и нужный приходится
   выискивать глазами. Двигать можно стрелками (точный шаг, удобно с телефона)
   и перетаскиванием за точки (утащить через полсписка, не нажимая стрелку
   двадцать раз). */
.sc-list li { display: flex; align-items: flex-start; gap: 8px; }

/* Ручка перетаскивания: шесть точек в две колонки. Рисуем узором, а не
   символом: брайлевская «⠿» в разных шрифтах то крупнее строки, то съезжает
   по базовой линии. `touch-action: none` обязателен — без него палец вместо
   строки тащит страницу. */
.sc-grip {
  flex: none; width: 12px; height: 18px; margin-top: 2px;
  cursor: grab; touch-action: none; opacity: .55;
  background-image: radial-gradient(circle, var(--muted) 1.4px, transparent 1.5px);
  background-size: 6px 6px; background-position: 1px 1px;
}
.sc-grip:active { cursor: grabbing; opacity: 1; }
/* Строка под пальцем: бледная, чтобы сквозь неё было видно, куда встанет. */
.sc-list li.sc-dragging { opacity: .55; }
.sc-move { display: flex; flex-direction: column; gap: 2px; flex: none; }
.sc-move button {
  width: 28px; height: 22px; padding: 0; line-height: 1;
  border: 1px solid var(--line); border-radius: 6px;
  background: var(--bg); color: var(--muted); font-size: 13px; cursor: pointer;
}
.sc-move button:active { background: var(--line); }
.sc-body { display: flex; flex-wrap: wrap; align-items: baseline; gap: 2px 8px;
           min-width: 0; flex: 1 1 auto; }
.sc-moved { background: rgba(51, 144, 236, .12); transition: background .4s; }

/* Каталог товаров: строка = фото, название, цена и кузова. */
.it-list { list-style: none; margin: 0; padding: 0; }
.it-list li { display: flex; align-items: center; gap: 10px;
              padding: 10px 14px; border-bottom: 1px solid var(--line); }
.it-photo { width: 44px; height: 44px; flex: none; border-radius: 8px;
            background: var(--bg-chat) center/cover no-repeat; }
.it-body { flex: 1; min-width: 0; display: flex; flex-direction: column;
           gap: 2px; }
.it-name { font-size: 15px; }
.it-sub { font-size: 12.5px; color: var(--muted);
          overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.it-drop { flex: none; width: auto; padding: 4px 8px; border: 0;
           background: transparent; color: var(--muted); cursor: pointer; }

/* Экран «ждут товар»: строка = деталь, под ней люди, справа кнопка.
   Деталь читают первой — с неё начинают, когда разбирают партию. */
.wait-list { list-style: none; margin: 0; padding: 0; }
.wait-list li { display: flex; align-items: center; gap: 10px;
                padding: 11px 14px; border-bottom: 1px solid var(--line); }
.wl-body { flex: 1; min-width: 0; display: flex; flex-direction: column;
           gap: 2px; }
.wl-name { font-size: 15px; }
.wl-people { font-size: 12.5px; color: var(--muted);
             overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.wl-fire { flex: none; width: auto; padding: 6px 14px; border: 0;
           border-radius: 9px; background: var(--accent); color: #fff;
           font-size: 13px; cursor: pointer; }
.wl-fire:disabled { opacity: .5; }

.wl-head { margin: 22px 14px 6px; font-size: 15px; }

/* ---------- на компьютере — окном, а не во весь экран ----------

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

   Кадром служит сам body: на странице переписки он и без того колонка во всю
   высоту (.chatpage), а на списке чатов достаточно дать ему свою прокрутку —
   тогда липкая шапка липнет к верху окна, а не экрана.

   Порог 900px намеренно высокий: на планшете и узком ноутбуке окно в рамке
   только отняло бы место, там по-прежнему во всю ширину. */
@media (min-width: 900px) {
  :root {
    --окно: 480px;          /* ширина «телефона»: шире делать нельзя, вёрстка мобильная */
    --поле: 20px;           /* воздух вокруг окна */
    --вокруг: #d9dde1;      /* фон за окном */
  }
  @media (prefers-color-scheme: dark) {
    :root:not([data-theme="light"]) { --вокруг: #0b1119; }
  }
  :root[data-theme="dark"] { --вокруг: #0b1119; }

  /* overflow: hidden на html обязателен: без него overflow-y: auto с body по
     правилам CSS переезжает на окно браузера — прокручивалась вся страница,
     содержимое настроек вылезало ниже коробки «телефона» без фона, а
     догрузка списка при поиске меряла не тот прокрутчик (апарт-инбокс,
     15.09.2026). */
  html { background: var(--вокруг); overflow: hidden; height: 100%; }
  body {
    width: var(--окно); margin: var(--поле) auto;
    height: calc(100dvh - var(--поле) * 2);
    overflow-y: auto; overscroll-behavior: contain;
    border: 1px solid var(--line); border-radius: 14px;
    box-shadow: 0 10px 40px rgba(0, 0, 0, .18);
  }
  /* Переписка сама держит высоту и прокручивает ленту внутри себя — своя
     прокрутка окну здесь только мешала бы, давая вторую полосу. */
  body.chatpage { height: calc(100dvh - var(--поле) * 2); overflow: hidden; }
  /* Вход занимал высоту ЭКРАНА и вылезал из окна на высоту полей. */
  .center { min-height: 100%; }

  /* Прибитое к экрану вписываем в окно вручную. Центруем полем, а не
     transform: сдвиг нужен шторке для выезда, и второй бы его перебил. */
  #side, #side-back, #scripts-pop, .modal-back {
    left: 50%; margin-left: calc(var(--окно) / -2);
  }
  #side {
    top: var(--поле); bottom: var(--поле);
    width: 300px; max-width: calc(var(--окно) - 60px);
    border-radius: 14px 0 0 14px;
  }
  #side-back, .modal-back {
    right: auto; width: var(--окно);
    top: var(--поле); bottom: var(--поле);
    border-radius: 14px;
  }
  #scripts-pop { right: auto; width: var(--окно); bottom: var(--поле); }

  /* Просмотр фото и обрезку НЕ сжимаем: картинку смотрят, а не читают, и
     большой экран здесь работает на нас. */

  /* Мерки в долях экрана считались от телефона. На мониторе 42vw — это
     восемьсот пикселей, втрое шире самого окна. */
  #account-filter { max-width: 190px; }
  #connect-qr { max-width: calc(var(--окно) - 80px); }

  /* Курсор мыши: на телефоне этого не видно, а мышью непонятно, что нажимается. */
  .chat, .side-item { cursor: pointer; }
}

/* ---------- две колонки на широком экране ----------

   От 1000px окно расширяется и делится надвое: слева список чатов, справа
   открытая переписка — как в почте. До этого порога остаётся одноколоночное
   окно 480px: на узком ноутбуке две колонки только сплющили бы обе.

   Переписка и список — по-прежнему две разные страницы, и переход по чату
   перезагружает окно целиком. Это видно глазом (список моргает), и это
   следующий шаг: подгружать правую колонку без перезагрузки. Разметка и
   стили при этом не изменятся — поменяется только способ подмены. */
.pane-list { display: block; }
.pane-chat { display: flex; flex-direction: column; flex: 1; min-height: 0; }
/* Заглушка правой колонки живёт только на широком экране. */
.pane-empty { display: none; }
/* Рамка с перепиской — только на широком экране: на телефоне переписка
   открывается обычным переходом, и рамка там лишний слой. */
.pane-frame { display: none; }
/* На телефоне страница переписки — это только переписка. */
@media (max-width: 999px) {
  .chatpage .pane-list { display: none; }
}

@media (min-width: 1000px) {
  /* Ширину держим на самой странице с колонками, а не в :root: иначе окно
     настроек и всех прочих страниц растягивалось бы до 1100px без всякой
     на то причины. */
  body.panes { --окно: 1100px; --список: 360px; }

  /* Именно `body.chatpage`, а не просто `body`: у правила `.chatpage`
     специфичность выше, и одиночный `body` ему проигрывал — страница
     переписки оставалась колонкой, список ложился сверху, а переписка
     уезжала под него во всю ширину. */
  body.panes, body.chatpage.panes {
    width: var(--окно);
    display: flex; flex-direction: row; align-items: stretch;
    overflow: hidden;                /* прокручиваются колонки, а не окно */
  }

  .panes .pane-list {
    width: var(--список); flex: none;
    display: flex; flex-direction: column; min-height: 0;
    border-right: 1px solid var(--line);
  }
  /* Список внутри колонки прокручивается сам: иначе шапка с поиском уезжала
     бы вверх вместе с чатами. */
  .panes .pane-list #chats { flex: 1; min-height: 0; overflow-y: auto; }
  .panes .pane-list header { position: static; }
  /* «Непрочитанные 24» в узкой колонке заплывало за многоточие. */
  .panes .pane-list header h1 { font-size: 16px; }
  .panes .pane-list header #inbox-title.filtered { font-size: 15px; letter-spacing: -.2px; }

  .panes .pane-chat { flex: 1; min-width: 0; }
  /* Кнопка «назад» ведёт к списку, а он и так слева: на широком экране она
     только занимает место в шапке и путает — уводит «назад» туда, где ты
     уже находишься. */
  .panes .pane-chat > header .back { display: none; }
  .panes .pane-empty {
    display: flex; align-items: center; justify-content: center;
    color: var(--muted); background: var(--bg-chat);
  }
  .panes .pane-frame {
    display: block; flex: 1; min-width: 0; border: 0; background: var(--bg-chat);
  }
  /* Скрытая рамка не должна занимать место: без этого пустая правая колонка
     ужималась вдвое, а надпись «выберите переписку» уезжала влево. */
  .panes .pane-frame[hidden] { display: none; }

  /* Прибитое к экрану считаем от окна в 1100px, а не от прежних 480. */
  .panes #side, .panes #side-back, .panes .modal-back {
    margin-left: calc(var(--окно) / -2); }
  .panes #side-back, .panes .modal-back { width: var(--окно); }
  /* Список скриптов — над строкой ввода, то есть в правой колонке. */
  .panes #scripts-pop {
    margin-left: calc(var(--окно) / -2 + var(--список));
    width: calc(var(--окно) - var(--список));
  }
}

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

   Только там, где наведение настоящее: на телефоне :hover прилипает после
   касания, и строка оставалась бы подсвеченной до следующего нажатия. */
@media (hover: hover) and (pointer: fine) {
  .chat:hover { background: rgba(128, 128, 128, .07); }
  .chat.pinned:hover { background: rgba(128, 128, 128, .12); }
}

/* Открытая переписка в списке. Нужна только там, где колонки две: на
   телефоне список и переписка — разные экраны, и подсвечивать нечего. */
@media (min-width: 1000px) {
  .panes .chat.open { background: rgba(128, 128, 128, .12); }
  .panes .chat.open:hover { background: rgba(128, 128, 128, .14); }
}

/* Кнопка «Выборки» с долгами: точка, а не число. Число сказало бы «сколько»,
   а от кнопки нужно только «есть» — за подробностями человек идёт в список. */
#filter-toggle.есть-долги { position: relative; }
#filter-toggle.есть-долги::after {
  content: ""; position: absolute; top: 2px; right: -2px;
  width: 7px; height: 7px; border-radius: 50%; background: #e05c4b;
}

/* Воронка сделки: полоска этапов под шапкой чата. Текущий этап залит цветом
   этапа, запертые (этап считается по товарам) — бледные. */
.funnel { display: flex; align-items: center; gap: 6px; flex-wrap: wrap;
          padding: 6px 14px; background: var(--bg-chat);
          border-bottom: 1px solid var(--line); font-size: 13px; }
/* background был var(--surface, #fff) — --surface нигде не определена,
   поэтому фон всегда падал на #fff; в тёмной теме --text: #ffffff → белая
   кнопка с белым текстом не видна. var(--bg) задана в обеих темах. */
.funnel .fn-stage, .funnel .fn-done, .funnel .fn-refuse, .funnel .fn-new, .funnel .fn-later {
  width: auto; height: auto; padding: 3px 10px; border: 1px solid var(--line);
  border-radius: 9px; background: var(--bg); color: var(--text);
  font-size: 12.5px; cursor: pointer; }
.funnel .fn-stage.on { color: #fff; border-color: transparent; }
.funnel .fn-stage.on[data-stage="new"] { background: #3f9e5a; }
.funnel .fn-stage.on[data-stage="thinking"] { background: #e8a33d; }
.funnel .fn-stage.on[data-stage="payment"] { background: #d9731a; }
.funnel .fn-stage.on[data-stage="shipping"] { background: #2f6fd6; }
.funnel .fn-stage:disabled { opacity: .55; cursor: default; }
/* Текущий этап залит и при счёте по товарам — .on не должен тускнеть. */
.funnel .fn-stage.on:disabled { opacity: 1; }
.funnel .fn-done { color: #2e7d32; }
.funnel .fn-refuse { color: #c0392b; margin-left: auto; }
.funnel .fn-later { color: var(--muted); }
.fn-now { padding: 3px 10px; border-radius: 9px; color: #fff; }
/* Свои fn-now-done/fn-now-refused/fn-now-never, а не fn-done/fn-refused:
   .fn-done — класс КНОПКИ «Закрыто + инвайт» (правило выше, .funnel
   .fn-done), и он на span'е с тем же классом перебивал .fn-now { color:
   #fff } своей более специфичной .funnel .fn-done { color: #2e7d32 }, да
   ещё тащил чужие border и cursor: pointer — тёмный текст на зелёной
   плашке и курсор-рука на некликабельной строке. */
.fn-now.fn-now-done { background: #3f9e5a; }
.fn-now.fn-now-refused, .fn-now.fn-now-never { background: #8a94a0; }

/* Товары сделки под полоской: название, две галочки. На телефоне свёрнуты
   в одну строку — переписку они заслонять не должны. */
.lead-items { padding: 4px 14px 6px; background: var(--bg-chat);
              border-bottom: 1px solid var(--line); font-size: 13px; }
.lead-items[hidden] { display: none; }
.lead-items .li-list { list-style: none; margin: 0; padding: 0; }
.lead-items li { display: flex; align-items: center; gap: 10px; padding: 2px 0; }
.lead-items .li-name { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis;
                       white-space: nowrap; cursor: pointer; }
.lead-items label { display: flex; align-items: center; gap: 4px; color: var(--muted);
                    font-size: 12px; white-space: nowrap; }
.lead-items .li-add, .lead-items .li-toggle {
  width: auto; height: auto; padding: 2px 8px; border: 1px dashed var(--line);
  border-radius: 9px; background: transparent; color: var(--accent); font-size: 12.5px; }
.lead-items .li-toggle { display: none; }
@media (max-width: 999px) {
  .lead-items.has-items .li-toggle { display: inline-block; margin-bottom: 4px; }
  .lead-items.has-items.collapsed .li-list, .lead-items.has-items.collapsed .li-add { display: none; }
}

/* Личные размеры оператора — шкала 1–10 (localStorage → --list-size-k и
   --msg-size-k на <html>, 5 = как было). Строки списка растут коэффициентом
   от базовых величин: все размеры строки считаются от него, поэтому строка
   на любой ступени пропорциональна. Доску это не трогает — --k задан только
   списку и образцу в настройках. */
#chats, .list-sample { --k: var(--list-size-k, 1); }
#chats .chat a, .list-sample .chat a {
  grid-template-columns: calc(48px * var(--k)) 1fr;
  column-gap: calc(12px * var(--k)); padding: calc(9px * var(--k)) calc(14px * var(--k));
}
#chats .av-wrap, #chats .avatar, .list-sample .av-wrap, .list-sample .avatar {
  width: calc(48px * var(--k)); height: calc(48px * var(--k)); font-size: calc(17px * var(--k));
}
#chats .chan, .list-sample .chan { width: calc(20px * var(--k)); height: calc(20px * var(--k)); }
#chats .title, .list-sample .title { font-size: calc(16px * var(--k)); }
#chats .apt, .list-sample .apt { font-size: calc(13px * var(--k)); }
#chats .account, .list-sample .account { font-size: calc(12px * var(--k)); }
#chats .preview, .list-sample .preview { font-size: calc(15px * var(--k)); }
#chats .time, .list-sample .time { font-size: calc(12px * var(--k)); }
#chats .stage, .list-sample .stage { font-size: calc(12px * var(--k)); line-height: calc(18px * var(--k)); }
#chats .lead, .list-sample .lead { font-size: calc(11px * var(--k)); line-height: calc(16px * var(--k)); }
#chats .count, .list-sample .count {
  min-width: calc(22px * var(--k)); height: calc(22px * var(--k)); font-size: calc(13px * var(--k));
}
#chats .noreply { width: calc(36px * var(--k)); height: calc(36px * var(--k)); font-size: calc(17px * var(--k)); }
#chats .chat { contain-intrinsic-size: 0 calc(76px * var(--k)); }

/* Пузыри переписки: только текст сообщений, поле ввода и шапка прежние. */
.msg { font-size: calc(16px * var(--msg-size-k, 1)); }
.msg .meta { font-size: calc(11px * var(--msg-size-k, 1)); }

/* Образцы на странице оформления: строка списка и пузырь, живут по тем же
   переменным — ползунок двигаешь, образец меняется. */
.list-sample { list-style: none; margin: 8px 0 0; padding: 0; border: 1px solid var(--line);
               border-radius: 12px; overflow: hidden; }
.list-sample .chat { content-visibility: visible; contain-intrinsic-size: auto; }
.msg-sample { margin-top: 8px; padding: 10px; border-radius: 12px; background: var(--bg-chat); }
.msg-sample .msg { max-width: 100%; }
.size-slider { display: flex; align-items: center; gap: 12px; margin-top: 6px; }
.size-slider input[type="range"] { flex: 1; min-width: 0; }
.size-slider output { min-width: 2.4em; text-align: right; font-weight: 600; font-variant-numeric: tabular-nums; }

/* Список причин отказа — как список скриптов: название, подпись, «убрать». */
.sc-list .lr-archived .sc-name { color: var(--muted); text-decoration: line-through; }
.lr-drop { width: auto; height: auto; padding: 4px 10px; border-radius: 10px;
          border: 1px solid var(--line); background: var(--bg-chat);
          color: var(--text); font-size: 13px; }

/* Доска CRM: шесть столбцов вбок. На телефоне — один столбец на экран, свайп с
   привязкой; на компьютере — все в ряд, каждый со своей прокруткой. Карточки —
   те же строки, что в списке чатов. */
body.boardpage { display: flex; flex-direction: column; height: 100dvh; margin: 0; }
/* Шапка под чёлкой айфона: общий `header` задаёт safe-area в padding-top, и
   сокращённый padding его стирал — шапка уезжала под часы. */
body.boardpage header {
  display: flex; align-items: center; gap: 10px; padding: 8px 12px; flex: none;
  padding-top: max(8px, env(safe-area-inset-top));
  border-bottom: 1px solid var(--line); background: var(--header);
  position: static;
}
body.boardpage header h1 { flex: 1; font-size: 18px; margin: 0; }
body.boardpage header .chip { width: auto; }
#board-tabs {
  display: flex; gap: 6px; padding: 6px 12px; overflow-x: auto; flex: none; touch-action: pan-x;
  scrollbar-width: none; border-bottom: 1px solid var(--line); background: var(--bg);
}
#board-tabs::-webkit-scrollbar { display: none; }
#board-tabs .tab {
  flex: none; width: auto; height: auto; padding: 4px 10px; border-radius: 14px;
  border: 1px solid var(--line); background: var(--bg-chat); color: var(--text); font-size: 13px;
}
#board-tabs .n, #board .col h3 .n { color: var(--muted); font-weight: 400; }
/* Доска и правая колонка с перепиской — рядом; на телефоне рамки нет. */
.board-body { flex: 1; min-height: 0; display: flex; }
#board {
  flex: 1; min-width: 0; min-height: 0; display: flex; gap: 10px; padding: 10px;
  padding-bottom: max(10px, env(safe-area-inset-bottom));
  overflow-x: auto; scroll-snap-type: x mandatory; background: var(--bg-chat);
}
#board-frame { display: none; }
@media (min-width: 1000px) {
  #board-frame:not([hidden]) {
    display: block; flex: 0 0 480px; border: 0; border-left: 1px solid var(--line);
    background: var(--bg-chat);
  }
}
#board .chat.open { background: var(--bg-chat); }
#board .col {
  flex: 0 0 88vw; scroll-snap-align: start; display: flex; flex-direction: column;
  background: var(--bg); border: 1px solid var(--line); border-radius: 12px; min-height: 0;
}
#board .col h3 { margin: 0; padding: 8px 12px; font-size: 14px; border-bottom: 1px solid var(--line); flex: none; }
#board .cards { overflow-y: auto; flex: 1; min-height: 0; }
#board .cards .chat { border-bottom: 1px solid var(--line); }
/* Карточки на доске компактнее строк списка: доска — обзор, в столбец
   должно влезать больше (просьба владельца 13.09). Аватарка 36px, шрифт
   на ступень мельче, отступы уже; галочка «ответ не требуется» тут не нужна. */
#board .cards .chat a { padding: 6px 10px; column-gap: 8px; }
#board .cards .av-wrap, #board .cards .avatar { width: 36px; height: 36px; font-size: 14px; }
#board .cards .chan { width: 15px; height: 15px; }
#board .cards .title { font-size: 14px; }
#board .cards .apt, #board .cards .account { font-size: 11px; }
#board .cards .preview { font-size: 13px; }
#board .cards .time { font-size: 11px; }
#board .cards .lead { font-size: 10px; line-height: 15px; }
#board .cards .count { min-width: 18px; height: 18px; font-size: 11px; }
#board .cards .noreply { display: none; }
#board .cards .chat { content-visibility: auto; contain-intrinsic-size: 0 58px; }
#board .more {
  width: auto; height: auto; margin: 6px auto; padding: 4px 14px; border-radius: 12px;
  border: 1px solid var(--line); background: var(--bg-chat); color: var(--text); font-size: 13px; flex: none;
}
#board .more[hidden] { display: none; }
@media (min-width: 1000px) {
  #board-tabs { display: none; }
  /* Столбцы делят ширину поровну и заполняют её до правого края; если шесть
     не помещаются — вбок с видимой полосой прокрутки (на маке она иначе
     спрятана, и обрезанный столбец выглядел как поломка). */
  #board .col { flex: 1 1 300px; min-width: 300px; }
  #board { scrollbar-width: thin; scroll-snap-type: none; }
  #board::-webkit-scrollbar { height: 10px; }
  #board::-webkit-scrollbar-thumb { background: var(--line); border-radius: 5px; }
}
/* На компьютере доске нужна вся ширина: «окно телефона» 480px вмещало бы
   полтора столбца. Ширина = 100vw минус рамка с двух сторон, потолка нет —
   на широком экране столбцы просто делят место поровну (см. #board .col
   выше), а не упираются в фиксированный максимум. */
@media (min-width: 900px) {
  body.boardpage {
    --окно: calc(100vw - var(--поле) * 2);
    width: var(--окно); height: calc(100dvh - var(--поле) * 2); overflow: hidden;
    /* `body.boardpage { margin: 0 }` (телефонное правило) перебивало
       `margin: var(--поле) auto` общей рамки — окно доски прижималось в
       левый верхний угол, серое поле --вокруг оставалось только справа и
       снизу. Возвращаем поле на компьютере. */
    margin: var(--поле) auto;
  }
}

/* Служебные сообщения — админка правил. Карточки с ярлыками условий и
   переключателем; окно правки — секциями, условия — чипами. */
.cr-list { list-style: none; margin: 0; padding: 0; }
.cr-item { display: flex; align-items: flex-start; gap: 10px; padding: 12px 0;
  border-bottom: 1px solid var(--line); }
.cr-item.cr-off .cr-body { opacity: .45; }
.cr-body { flex: 1 1 auto; min-width: 0; }
.cr-head { display: flex; align-items: baseline; gap: 8px; }
.cr-name { font-size: 15px; font-weight: 600; cursor: pointer; }
.cr-when { font-size: 12px; color: var(--muted); white-space: nowrap; }
.cr-tags { display: flex; flex-wrap: wrap; gap: 4px; margin-top: 5px; align-items: center; }
.cr-tag { font-size: 12px; padding: 2px 8px; border-radius: 10px; background: var(--chip, #eef1f5);
  color: var(--text, #222); }
.cr-tag.any { background: #e3efff; color: #123c73; }
.cr-tag.touch { background: #e6f6e8; color: #1e6b2e; }
.cr-tag.mute { background: transparent; color: var(--muted); border: 1px dashed var(--line); }
.cr-tag-sep { font-size: 12px; color: var(--muted); margin: 0 2px; }
.cr-text { font-size: 13px; color: var(--muted); margin-top: 5px; white-space: pre-line; }
.cr-switch { position: relative; flex-shrink: 0; width: 42px; height: 24px; margin-top: 2px; }
.cr-switch input { position: absolute; opacity: 0; width: 0; height: 0; }
.cr-knob { position: absolute; inset: 0; border-radius: 12px; background: #c9ced6; transition: background .15s; }
.cr-knob::after { content: ''; position: absolute; top: 3px; left: 3px; width: 18px; height: 18px;
  border-radius: 50%; background: #fff; transition: transform .15s; box-shadow: 0 1px 2px rgba(0,0,0,.25); }
.cr-switch input:checked + .cr-knob { background: #34c759; }
.cr-switch input:checked + .cr-knob::after { transform: translateX(18px); }
/* Ширина — как у остальных окон (у .form-box своей нет: без неё коробка
   растягивалась по содержимому чипов шире экрана телефона). Высоту не
   переопределяем: .form-box уже ограничен видимой областью и прокручивается
   внутри (апарт-инбокс, 15.09.2026). */
.cr-form { width: min(440px, calc(100vw - 32px)); box-sizing: border-box; }
.cr-form .modal-title { flex: none; }
.cr-section { margin-top: 12px; padding-top: 10px; border-top: 1px solid var(--line); }
.cr-section-title { font-size: 13px; font-weight: 600; margin-bottom: 6px; }
.cr-section-title small { font-weight: 400; color: var(--muted); margin-left: 4px; }
.cr-chips { display: flex; flex-wrap: wrap; gap: 6px; min-width: 0; }
.cr-chip { display: inline-flex; }
.cr-chip input { position: absolute; opacity: 0; width: 0; height: 0; }
.cr-chip span { font-size: 13px; padding: 5px 11px; border-radius: 14px; border: 1px solid var(--line);
  background: transparent; cursor: pointer; user-select: none; white-space: nowrap; }
.cr-chip input:checked + span { background: #1a56c4; border-color: #1a56c4; color: #fff; }
.cr-chip.any input:checked + span { background: #e3efff; border-color: #1a56c4; color: #123c73; }
.cr-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 8px 10px; }
.cr-check { flex-direction: row; align-items: center; gap: 6px; align-self: end; padding-bottom: 8px; }
#cr-f-text, #cr-f-insert { font-size: 16px; width: 100%; box-sizing: border-box; }
.cr-hint { font-size: 12px; color: var(--muted); margin-top: 4px; line-height: 1.5; }
/* Времена правила: строки «время ✕», кнопка «+ ещё время». */
.cr-times { align-items: flex-start; }
#cr-f-times { display: flex; flex-direction: column; gap: 6px; }
.cr-time { display: flex; align-items: center; gap: 6px; }
.cr-time input { flex: 1; min-width: 0; }
.cr-time-x { border: 0; background: transparent; color: var(--muted); font-size: 16px;
             padding: 4px 6px; width: auto; height: auto; cursor: pointer; }
#cr-time-add { align-self: flex-start; margin-top: 6px; }

/* Гифки: та же панель снизу, что у скриптов; сетка в три колонки, плитки —
   живые превью, как в Telegram; нажатие — отправка. */
#gif-pop {
  position: fixed; left: 0; right: 0; bottom: 0; z-index: 40;
  max-height: 70vh; display: flex; flex-direction: column;
  background: var(--bg); border-top: 1px solid var(--line);
  border-radius: 14px 14px 0 0; box-shadow: 0 -8px 30px rgba(0,0,0,.18);
}
/* На телефоне панели стоят от посаженной страницы (body.chatpage —
   position: relative, под клавиатурой сидит на видимой области), а не от
   окна: fixed при открытой клавиатуре iPhone стоял низом под ней — видны
   были только верхние ~280px, а короткая панель пряталась целиком (аудит
   17.09.2026). Потолок — в процентах от той же страницы. */
/* Без overflow на панели: overflow-x: hidden делал её прокручиваемой по
   вертикали, и WebKit при фокусе в поле поиска считал «видимую» часть
   вытянутого поля, а не всё поле — страница на 0,1 с уезжала на ~200px
   (симулятор 18.09.2026). Ширину держит min-width: 0 у поля. */
@media (hover: none) and (pointer: coarse) {
  #scripts-pop, #gif-pop { position: absolute; max-height: 70%; }
}
#gif-pop .sc-head { display: flex; gap: 8px; padding: 10px 12px; border-bottom: 1px solid var(--line); align-items: center; }
/* Вкладки GIF | Избранное — как в Telegram; во вкладке избранного вместо
   поиска — «+» (добавить с устройства). */
.gif-tabs { display: flex; gap: 2px; background: var(--bg-chat); border-radius: 10px; padding: 2px; flex: none; }
.gif-tab { width: auto; height: auto; padding: 6px 10px; border-radius: 8px; font-size: 13px;
           background: transparent; color: var(--muted); }
.gif-tab.on { background: var(--bg); color: var(--text); box-shadow: 0 1px 2px rgba(0,0,0,.12); }
#gif-pop[data-tab="fav"] #gif-hints,
#gif-pop[data-tab="st"] #gif-search, #gif-pop[data-tab="st"] #gif-hints { display: none; }
#gif-pop[data-tab="fav"] .gif-add { flex: none; width: 42px; }
#gif-grid .gif .fav-tag { position: absolute; right: 4px; bottom: 4px; font-size: 11px; font-weight: 700; padding: 1px 6px;
                          border-radius: 6px; background: rgba(255,255,255,.85); color: #222; max-width: 70%;
                          overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
#st-sets { flex-wrap: nowrap; overflow-x: auto; scrollbar-width: none; }
#st-sets::-webkit-scrollbar { display: none; }
#st-sets button { width: auto; height: auto; padding: 4px 10px; border-radius: 12px; font-size: 13px;
                  background: var(--bg-chat); color: var(--text); white-space: nowrap; flex: none; }
#st-sets button.on { background: var(--accent); color: #fff; }
#st-add { font-size: 14px; flex: 1; }
/* Стикеры мельче гифок: четыре в ряд, без обрезки. */
#gif-pop[data-tab="st"] #gif-grid { grid-template-columns: repeat(4, 1fr); grid-auto-rows: 84px; }
#gif-grid .gif.st { background: transparent; }
#gif-grid .gif.st img { object-fit: contain; }
.gif-add { flex: 1; display: flex; align-items: center; justify-content: center; height: 38px;
           border: 1px dashed var(--line); border-radius: 12px; color: var(--accent); font-size: 22px; cursor: pointer; }
#gif-grid .gif img { width: 100%; height: 100%; object-fit: cover; display: block; }
#gif-grid .gif .fav-kind { position: absolute; left: 4px; bottom: 4px; font-size: 10px; padding: 1px 5px;
                           border-radius: 6px; background: rgba(0,0,0,.55); color: #fff; }
#gif-grid .empty { grid-column: 1 / -1; color: var(--muted); font-size: 14px; padding: 20px 8px; text-align: center; }
/* min-width: 0 — иначе поле не ужимается ниже своей «природной» ширины, и на
   узком экране крестик уезжал за край. */
#gif-pop #gif-search { flex: 1; min-width: 0; border-radius: 12px; padding: 9px 12px; }
#gif-pop #gif-close { width: 38px; background: transparent; color: var(--muted); font-size: 17px; }
#gif-hints { display: flex; gap: 6px; flex-wrap: wrap; padding: 8px 12px 0; }
#gif-hints button { width: auto; height: auto; padding: 4px 10px; border-radius: 12px; font-size: 13px;
                    background: var(--bg-chat); color: var(--text); }
#gif-hints button.on { background: var(--accent); color: #fff; }
/* Ряды — фиксированной высоты: с aspect-ratio на плитках grid считал ряды
   нулевыми (0,47px), плитки ложились друг на друга, а наблюдатель видимости
   грузил все 50 роликов разом (проверено на бою 15.09.2026). */
#gif-grid {
  overflow-y: auto; padding: 8px 8px max(8px, env(safe-area-inset-bottom));
  display: grid; grid-template-columns: repeat(3, 1fr); grid-auto-rows: 112px; gap: 6px;
  min-height: 120px; align-content: start;
}
#gif-grid .gif {
  position: relative; border-radius: 8px; overflow: hidden; background: var(--bg-chat);
  cursor: pointer; padding: 0; border: 0; width: 100%; height: 100%;
  background-size: cover; background-position: center;
}
#gif-grid .gif video { width: 100%; height: 100%; object-fit: cover; display: block; position: absolute; inset: 0; }
#gif-grid .gif img.poster { width: 100%; height: 100%; object-fit: cover; display: block; }
#gif-grid .gif.sending { opacity: .5; }
#gif-state { padding: 0 12px 8px; margin: 0; }
/* Значок GIF — внутри поля справа, как значок стикеров в Telegram: не ест
   место в строке (владелец апарт-инбокса). При переводе (tr-btn) он сдвигается левее. */
#composer #gif-btn {
  position: absolute; top: 50%; transform: translateY(-50%); right: 66px; z-index: 2;
  width: 26px; height: 26px; min-width: 0; padding: 0; border: 0; border-radius: 6px;
  background: transparent; color: var(--muted); opacity: .6; display: grid; place-items: center;
}
#composer #gif-btn:active { opacity: 1; }
#composer.with-gif #text { padding-right: 40px; }
#composer.with-gif #tr-btn { right: 96px; }
#composer.with-gif.with-tr #text { padding-right: 66px; }
/* Часы отложенных — слева от значка стикера, в том же поле. */
#composer #sched-btn {
  position: absolute; top: 50%; transform: translateY(-50%); right: 96px; z-index: 2;
  width: 26px; height: 26px; min-width: 0; padding: 0; border: 0; border-radius: 6px;
  background: transparent; color: var(--accent); opacity: .8; display: grid; place-items: center;
}
#composer.with-sched.with-gif #text { padding-right: 70px; }
#composer.with-sched.with-gif #tr-btn { right: 126px; }
#composer.with-sched.with-gif.with-tr #text { padding-right: 96px; }
#composer:not(.with-gif) #sched-btn { right: 66px; }
#composer.with-sched:not(.with-gif) #text { padding-right: 40px; }
/* У .form-box своей ширины нет — коробке списка задаём свою, не шире экрана. */
#sched-back .form-box { width: min(420px, calc(100vw - 32px)); box-sizing: border-box; }
.sched-items { list-style: none; margin: 0; padding: 0; }
.sched-items li { padding: 10px 0; border-bottom: 1px solid var(--line); }
.sched-items li:last-child { border-bottom: 0; }
.sched-items .s-when { font-size: 12px; color: var(--accent); font-weight: 600; }
.sched-items .s-text { margin: 4px 0 8px; white-space: pre-wrap; word-break: break-word; font-size: 15px; }
.sched-items .s-acts { display: flex; flex-wrap: wrap; gap: 6px; }
.sched-items .s-acts button { width: auto; height: auto; padding: 5px 10px; font-size: 13px; border-radius: 10px;
                              background: var(--bg-chat); color: var(--text); }
.sched-items .s-acts button.danger { color: #d33a2c; }
.sched-items .empty { color: var(--muted); font-size: 14px; padding: 12px 0; }
@media (min-width: 900px) { #gif-pop { left: 50%; margin-left: calc(var(--окно) / -2); right: auto; width: var(--окно); bottom: var(--поле); } }
@media (min-width: 1000px) { .panes #gif-pop { margin-left: calc(var(--окно) / -2 + var(--список)); width: calc(var(--окно) - var(--список)); } }

/* Гифка в ленте: крутится сама, без плеера; на паузе — значок GIF ярче. */
.gif-frame { display: block; position: relative; border-radius: 10px; overflow: hidden;
             background: var(--bg-chat); max-width: 100%; }
.gif-frame video { width: 100%; height: 100%; object-fit: cover; display: block; cursor: pointer; }
.gif-frame .gif-mark { position: absolute; left: 6px; top: 6px; font-size: 10px; font-weight: 700;
                       padding: 1px 5px; border-radius: 6px; background: rgba(0,0,0,.45); color: #fff;
                       opacity: .5; pointer-events: none; }
.gif-frame.paused .gif-mark { opacity: 1; }
.gif-frame.paused::after { content: '▶'; position: absolute; inset: 0; display: grid; place-items: center;
                           color: #fff; font-size: 28px; text-shadow: 0 1px 4px rgba(0,0,0,.6); pointer-events: none; }

/* Число прямо в строке примечания настроек («до 300 МБ»). */
.settings input.num-inline, .settings .note input.num-inline { width: 4.5em; display: inline-block; padding: 2px 6px; margin: 0 2px; font-size: 16px; vertical-align: baseline; }

/* Окно «Товары»: строка = название + количество + ✕, «+ добавить» под ними. */
.li-form .li-rows { display: flex; flex-direction: column; gap: 8px; }
.li-form .li-row { display: flex; gap: 6px; align-items: center; }
.li-form .li-row input { flex: 1; min-width: 0; font-size: 16px; }
.li-form .li-row select { width: auto; flex: none; font-size: 16px; padding: 8px 6px; }
.li-form .li-x { width: 32px; height: 32px; flex: none; padding: 0; border: 0; border-radius: 8px;
  background: transparent; color: var(--muted); font-size: 15px; cursor: pointer; }
.li-form .li-more { width: auto; height: auto; align-self: flex-start; padding: 6px 12px; border-radius: 12px;
  background: transparent; color: var(--accent); border: 1px dashed var(--accent); font-size: 14px; }
.li-form .state:empty { display: none; }

/* ======================================================================
   Дизайн v2 (15.09.2026, перенос из апарт-инбокса). Каждая часть — своим
   классом на <html> (d-bubbles, d-bg, d-list, d-anim, d-icons), классы
   ставит _head.html из настроек; выключается в «Оформлении» без выкладки.
   Части «карточка брони» (d-card) здесь нет: у сделки полоска этапов, а не
   карточка. Полный откат — тег до-дизайна-15.09.
   ====================================================================== */

/* --- 1. Пузыри как в Telegram iOS: скругление, хвостик у последнего в
   серии, время в углу; у фото — время на плашке поверх картинки. --- */
.d-bubbles .msg {
  border-radius: 17px; padding: 6px 12px 6px 12px;
  box-shadow: 0 1px .5px rgba(0, 0, 0, .13); isolation: isolate;
}
.d-bubbles .msg.in { border-bottom-left-radius: 17px; }
.d-bubbles .msg.out { border-bottom-right-radius: 17px; }
/* Хвостик: один псевдоэлемент цвета пузыря, вырезанный clip-path (без
   «заплатки» цветом фона — на узоре она была бы видна). Только у последнего
   пузыря серии: если следом свой же — хвоста нет. */
.d-bubbles .msg.in::before, .d-bubbles .msg.out::before {
  content: ""; position: absolute; bottom: 0; width: 12px; height: 18px; z-index: -1;
}
/* Форма как у Telegram iOS: наружный край вогнутый и уходит в острие у самого
   низа, нижний край чуть приподнят к пузырю — «завиток», а не треугольник. */
.d-bubbles .msg.in::before {
  left: -8px; background: var(--bubble-in);
  clip-path: path("M12 0 C 12 8, 10 14, 0 18 C 5 17.2, 9 16.6, 12 16.6 Z");
}
.d-bubbles .msg.out::before {
  right: -8px; background: var(--bubble-out);
  clip-path: path("M0 0 C 0 8, 2 14, 12 18 C 7 17.2, 3 16.6, 0 16.6 Z");
}
.d-bubbles .msg.in:has(+ .msg.in)::before, .d-bubbles .msg.out:has(+ .msg.out)::before,
.d-bubbles .msg.system::before, .d-bubbles .msg.out.failed::before { display: none; }
.d-bubbles .msg.in:has(+ .msg.in) { border-bottom-left-radius: 6px; }
.d-bubbles .msg.out:has(+ .msg.out) { border-bottom-right-radius: 6px; }
.d-bubbles .msg .meta { margin: 6px 0 -1px 10px; font-size: 11px; }
/* Фото/гифка без подписи — время на тёмной плашке в углу картинки. */
.d-bubbles .msg:has(> img.media, > .gif-frame, > .video-frame):not(:has(.text)) {
  padding: 3px; background: transparent; box-shadow: none;
}
.d-bubbles .msg:has(> img.media, > .gif-frame, > .video-frame):not(:has(.text))::before { display: none; }
.d-bubbles .msg:has(> img.media, > .gif-frame, > .video-frame):not(:has(.text)) img.media,
.d-bubbles .msg:has(> img.media, > .gif-frame, > .video-frame):not(:has(.text)) .gif-frame,
.d-bubbles .msg:has(> img.media, > .gif-frame, > .video-frame):not(:has(.text)) .video-frame { margin: 0; border-radius: 14px; }
.d-bubbles .msg:has(> img.media, > .gif-frame, > .video-frame):not(:has(.text)) .meta {
  position: absolute; right: 9px; bottom: 9px; margin: 0; padding: 2px 7px; border-radius: 10px;
  background: rgba(0, 0, 0, .45); color: #fff; z-index: 1;
}
.d-bubbles .msg:has(> img.media, > .gif-frame, > .video-frame):not(:has(.text)) .meta .ticks { color: #fff; }
.d-bubbles .msg:has(> img.media, > .gif-frame, > .video-frame):not(:has(.text)) .meta .ticks.read { color: #9fe4ad; }
.d-bubbles .msg .quote { border-radius: 8px; }
.d-bubbles .msg.system { border-radius: 12px; box-shadow: none; }

/* --- 2. Фон переписки: мягкий градиент + лёгкий узор точек (в тёмной —
   свои цвета). Рисуется CSS, картинок не тянем. --- */
.d-bg #feed {
  background-color: #dfe6ec;
  background-image:
    radial-gradient(rgba(0, 0, 0, .045) 1.2px, transparent 1.3px),
    linear-gradient(160deg, #e3eaf0 0%, #d9e3ea 100%);
  background-size: 18px 18px, 100% 100%;
  background-attachment: local, scroll;
}
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]).d-bg #feed {
    background-color: #0e1621;
    background-image:
      radial-gradient(rgba(255, 255, 255, .05) 1.2px, transparent 1.3px),
      linear-gradient(160deg, #101a26 0%, #0b131c 100%);
  }
}
:root[data-theme="dark"].d-bg #feed {
  background-color: #0e1621;
  background-image:
    radial-gradient(rgba(255, 255, 255, .05) 1.2px, transparent 1.3px),
    linear-gradient(160deg, #101a26 0%, #0b131c 100%);
}
.d-bg .day-sep, .d-bg .msg.system { backdrop-filter: blur(2px); }

/* --- 5. Список чатов: точка непрочитанного у имени, превью в две строки,
   стадия и лид — чипы одной высоты. --- */
/* Точку непрочитанного у имени владелец убрал (15.09 вечер): счётчик
   справа и так говорит всё. Остаётся вес заголовка. */
.d-list .chat.unread .title { font-weight: 600; }
.d-list .chat .preview {
  white-space: normal; display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical;
  line-height: 1.25; overflow: hidden; text-overflow: ellipsis;
}
.d-list .chat .row2 { align-items: flex-start; }
.d-list .chat .count, .d-list .chat .noreply { align-self: center; }
.d-list #chats .chat { contain-intrinsic-size: 0 calc(84px * var(--k)); }
.d-list .stage, .d-list .chat .lead { border-radius: 8px; line-height: 18px; height: 18px; display: inline-flex; align-items: center; }

/* --- 6. Анимации: новый пузырь всплывает, окна и листы выезжают. Кто
   отключил анимации в системе — без них. --- */
@media (prefers-reduced-motion: no-preference) {
  .d-anim .msg.pop { animation: пузырь-появление .18s ease-out; }
  @keyframes пузырь-появление { from { opacity: 0; transform: translateY(8px) scale(.98); } }
  .d-anim .modal-back:not([hidden]) .modal,
  .d-anim .modal-back:not([hidden]) .form-box { animation: окно-выезд .2s cubic-bezier(.2, .8, .3, 1); }
  @keyframes окно-выезд { from { opacity: 0; transform: translateY(14px); } }
  .d-anim .modal-back:not([hidden]) { animation: подложка .18s ease-out; }
  @keyframes подложка { from { opacity: 0; } }
  .d-anim #gif-pop:not([hidden]), .d-anim #scripts-pop:not([hidden]) { animation: панель-снизу .2s cubic-bezier(.2, .8, .3, 1); }
  @keyframes панель-снизу { from { transform: translateY(24px); opacity: .6; } }
  .d-anim .b-pill, .d-anim .chat a, .d-anim .sheet-item { transition: background .12s, transform .12s; }
  .d-anim .sheet-item:active { transform: scale(.985); }
}

/* --- 7. Линейные значки вместо эмодзи (ставит JS по d-icons): общий вид. --- */
.d-icons .ic {
  width: 18px; height: 18px; vertical-align: -4px; margin-right: 8px; flex: none;
  fill: none; stroke: currentColor; stroke-width: 1.7; stroke-linecap: round; stroke-linejoin: round;
}
.d-icons .sheet-item { display: flex; align-items: center; }
.d-icons .sheet-item.cancel { justify-content: center; }
.d-icons #remind svg, .d-icons #mic svg { width: 22px; height: 22px; display: block; }
.d-icons #remind, .d-icons #mic { display: grid; place-items: center; }
.d-icons #plus { font-size: 0; }
.d-icons #plus svg { width: 24px; height: 24px; }

/* --- 8. Тёмная тема: жёстко заданные цвета новых окон. --- */
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .cr-tag.any { background: #1e3a5f; color: #cfe2ff; }
  :root:not([data-theme="light"]) .cr-tag.touch { background: #1f3d25; color: #b8ecc2; }
  :root:not([data-theme="light"]) .cr-chip.any input:checked + span { background: #1e3a5f; color: #cfe2ff; }
  :root:not([data-theme="light"]) .cr-knob { background: #3a4550; }
  :root:not([data-theme="light"]) #gif-grid .gif .fav-tag { background: rgba(0, 0, 0, .7); color: #fff; }
  :root:not([data-theme="light"]) .gif-tab.on { box-shadow: none; }
  :root:not([data-theme="light"]) .others-draft { background: rgba(255, 255, 255, .06); }
}
:root[data-theme="dark"] .cr-tag.any { background: #1e3a5f; color: #cfe2ff; }
:root[data-theme="dark"] .cr-tag.touch { background: #1f3d25; color: #b8ecc2; }
:root[data-theme="dark"] .cr-chip.any input:checked + span { background: #1e3a5f; color: #cfe2ff; }
:root[data-theme="dark"] .cr-knob { background: #3a4550; }
:root[data-theme="dark"] #gif-grid .gif .fav-tag { background: rgba(0, 0, 0, .7); color: #fff; }
:root[data-theme="dark"] .gif-tab.on { box-shadow: none; }
:root[data-theme="dark"] .others-draft { background: rgba(255, 255, 255, .06); }

/* ---------- цвет фона переписки (личный, data-feed-bg на <html>) ----------
   Переопределяем --bg-chat: от него считаются и лента, и узор дизайна v2, и
   хвостики. Тёмной теме — свои оттенки тех же цветов. */
:root[data-feed-bg="blue"] { --bg-chat: #d6e4f0; }
:root[data-feed-bg="green"] { --bg-chat: #d7e6d1; }
:root[data-feed-bg="sand"] { --bg-chat: #efe6d6; }
:root[data-feed-bg="lilac"] { --bg-chat: #e3dcef; }
:root[data-feed-bg="warm"] { --bg-chat: #e9e5e0; }
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"])[data-feed-bg="blue"] { --bg-chat: #0f1c2b; }
  :root:not([data-theme="light"])[data-feed-bg="green"] { --bg-chat: #0f1d16; }
  :root:not([data-theme="light"])[data-feed-bg="sand"] { --bg-chat: #221c14; }
  :root:not([data-theme="light"])[data-feed-bg="lilac"] { --bg-chat: #1a1526; }
  :root:not([data-theme="light"])[data-feed-bg="warm"] { --bg-chat: #1c1a18; }
}
:root[data-theme="dark"][data-feed-bg="blue"] { --bg-chat: #0f1c2b; }
:root[data-theme="dark"][data-feed-bg="green"] { --bg-chat: #0f1d16; }
:root[data-theme="dark"][data-feed-bg="sand"] { --bg-chat: #221c14; }
:root[data-theme="dark"][data-feed-bg="lilac"] { --bg-chat: #1a1526; }
:root[data-theme="dark"][data-feed-bg="warm"] { --bg-chat: #1c1a18; }
/* Узор дизайна v2 — от выбранного цвета, а не от зашитого серого. */
.d-bg[data-feed-bg] #feed {
  background-color: var(--bg-chat);
  background-image:
    radial-gradient(rgba(0, 0, 0, .045) 1.2px, transparent 1.3px),
    linear-gradient(160deg, color-mix(in srgb, var(--bg-chat) 94%, white) 0%, color-mix(in srgb, var(--bg-chat) 94%, black) 100%);
}
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]).d-bg[data-feed-bg] #feed {
    background-image:
      radial-gradient(rgba(255, 255, 255, .05) 1.2px, transparent 1.3px),
      linear-gradient(160deg, color-mix(in srgb, var(--bg-chat) 92%, white) 0%, color-mix(in srgb, var(--bg-chat) 90%, black) 100%);
  }
}
:root[data-theme="dark"].d-bg[data-feed-bg] #feed {
  background-image:
    radial-gradient(rgba(255, 255, 255, .05) 1.2px, transparent 1.3px),
    linear-gradient(160deg, color-mix(in srgb, var(--bg-chat) 92%, white) 0%, color-mix(in srgb, var(--bg-chat) 90%, black) 100%);
}
/* Выбор в настройках: кружки-образцы и живой образец пузырей на фоне. */
.bg-pick { display: flex; gap: 10px; flex-wrap: wrap; margin-top: 8px; }
.bg-swatch { display: flex; flex-direction: column; align-items: center; gap: 4px; width: auto; height: auto;
             padding: 6px; background: transparent; color: var(--muted); font-size: 11px; border-radius: 12px; }
.bg-swatch .sw { display: block; width: 34px; height: 34px; border-radius: 50%; border: 2px solid var(--line); box-sizing: border-box; }
.bg-swatch.on { color: var(--text); }
.bg-swatch.on .sw { border-color: var(--accent); box-shadow: 0 0 0 2px var(--bg), 0 0 0 4px var(--accent); }
.sw-default { background: #e6ebee; } .sw-blue { background: #d6e4f0; } .sw-green { background: #d7e6d1; }
.sw-sand { background: #efe6d6; } .sw-lilac { background: #e3dcef; } .sw-warm { background: #e9e5e0; }
.bg-sample { display: flex; flex-direction: column; gap: 4px; background: var(--bg-chat); }
.bg-sample .msg.out { align-self: flex-end; }
