/* v2 Фаза 11 (PLAN.md п.35) — extracted verbatim from templates.py::_LAYOUT's
   inline <style> block (was ~679 lines / 45.5 KB re-sent on every response).
   Cache-busted via ?v={config.APP_VERSION} on the <link> tag in _LAYOUT, so
   this file itself is served with a long, immutable Cache-Control.
   One rule (.shell's max-width) stayed behind in _LAYOUT as an inline
   style="" attribute — it varies per page (page(..., max_width=...)),
   everything else here is truly static across every page/request. */
  :root {
    --bg: #f5f6f8;
    --surface: #fff;
    --text: #1d2935;
    --muted: #667785;
    --border: #e5e9ed;
    --border-strong: #c7d0d8;
    --accent: #23425d;
    --accent-hover: #1a3247;
    --danger: #a43b3b;
    --danger-hover: #7e2c2c;
    --danger-bg: #fbeceb;
    --ok-bg: #dff3e3; --ok-fg: #1e6b3a;
    --pending-bg: #fff3b0; --pending-fg: #8a6d1a;
    --muted-badge-bg: #eef1f4; --muted-badge-fg: #667785;
    --edited-row-bg: #fdf6d8; --edited-row-bg-hover: #fbefc0;
    --deleted-row-bg: #fbeceb; --deleted-row-bg-hover: #f7dcda;
    --radius: 12px;
    --radius-sm: 8px;
  }
  * { box-sizing: border-box; }
  body {
    font-family: system-ui, -apple-system, "Segoe UI", sans-serif;
    background: var(--bg);
    color: var(--text);
    margin: 0;
    padding: 24px 20px 96px;
    line-height: 1.55;
    font-size: 14.5px;
  }
  .shell { margin: 0 auto; }
  /* Backlog п.138 — wrapper is what's sticky, not each banner, so the 2FA
     reminder and the "защита приостановлена" banner can stack without one
     overlapping the other (two independently-sticky siblings at top:0 would
     both dock at the very top of the viewport instead of stacking). */
  .page-banners { position: sticky; top: 0; z-index: 20; }
  .page-banner {
    display: block; width: 100%; color: #fff;
    text-align: center; padding: 10px 16px; font-weight: 700; letter-spacing: .03em;
    box-sizing: border-box; box-shadow: inset 0 -2px 0 rgba(0, 0, 0, .15);
    line-height: 1.2; font-size: 14px; white-space: nowrap; overflow-x: auto;
  }
  .totp-banner { background: #e21c1c; text-decoration: none; }
  .totp-banner:hover { background: #c81616; }
  .pause-banner {
    background: #e21c1c; display: flex; align-items: center; justify-content: center; gap: 10px;
  }
  .pause-banner form { display: inline; line-height: 1; }
  .pause-banner button {
    background: transparent; border: 1px solid #fff; color: #fff; border-radius: 4px;
    padding: 1px 10px; font-weight: 700; font-size: 12.5px; line-height: 1.4; cursor: pointer;
  }
  .pause-banner button:hover { background: rgba(255, 255, 255, .15); }
  .topnav { display: flex; align-items: center; justify-content: space-between; gap: 16px; margin-bottom: 11px; flex-wrap: wrap; }
  .brand {
    display: inline-block;
    font-size: 12.5px;
    font-weight: 700;
    letter-spacing: .06em;
    text-transform: uppercase;
    color: var(--muted);
    text-decoration: none;
  }
  .brand:hover { color: var(--accent); text-decoration: none; }
  .version-tag { font-size: 11px; color: var(--muted); }
  /* Backlog п.8 — global RU/EN switcher, same place on every page (anonymous
     or not): current language plain text, the other one a plain link to
     /lang/{code}, which bounces back via Referer. */
  .lang-switch { font-size: 12px; font-weight: 600; color: var(--muted); letter-spacing: .02em; }
  .lang-switch span { color: var(--text); }
  .lang-switch a { color: var(--muted); }
  .lang-switch a:hover { color: var(--accent); }
  .user-nav { display: flex; align-items: center; gap: 16px; flex-wrap: wrap; }
  .user-nav a, .user-nav .link-button {
    font-size: 12.5px; font-weight: 600; color: var(--muted); text-decoration: none;
    background: none; border: none; padding: 0; font-family: inherit; cursor: pointer;
  }
  .user-nav a:hover, .user-nav .link-button:hover { color: var(--accent); }
  .user-nav form { margin: 0; display: inline; }
  .nav-key-status { position: relative; display: inline-block; }
  .nav-key-label {
    font-size: 12.5px; font-weight: 700; cursor: default;
    padding: 3px 10px; border-radius: 999px; white-space: nowrap;
  }
  /* Backlog: главное меню — «Ключи шифрования» красным, если ни один рабочий
     аккаунт не имеет ни одного проверенного ключа; оранжевым, если хотя бы
     у одного есть; зелёным, только если у всех есть. */
  .nav-key-label--none { background: var(--danger-bg); color: var(--danger); }
  .nav-key-label--partial { background: var(--pending-bg); color: var(--pending-fg); }
  .nav-key-label--all { background: var(--ok-bg); color: var(--ok-fg); }
  /* Backlog п.146 — pulsing green pill on «Управление подписками» while the
     user has zero work accounts yet; stops as soon as the first one exists. */
  .nav-pulse-subscribe {
    padding: 3px 10px; border-radius: 999px;
    animation: pulse-green-bg 1.8s ease-in-out infinite;
  }
  @keyframes pulse-green-bg {
    0%, 100% { background-color: transparent; }
    50% { background-color: var(--ok-bg); color: var(--ok-fg); }
  }
  /* Backlog п.146 доп. — the pulsing background alone tested as too
     low-contrast to notice; bright solid badge (not the muted --ok-fg pill
     tone used elsewhere) makes the nudge legible at a glance. */
  .step-badge {
    display: inline-block; font-size: 11px; font-weight: 800; letter-spacing: .03em;
    color: #fff; background: #17a34a; padding: 2px 7px; border-radius: 999px;
    vertical-align: middle; align-self: center;
  }
  .nav-key-dropdown {
    display: none; position: absolute; top: 100%; left: 0; margin-top: 8px;
    background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius-sm);
    box-shadow: 0 4px 16px rgba(0,0,0,.08); padding: 8px 0; min-width: 220px; z-index: 10;
  }
  .nav-key-status:hover .nav-key-dropdown { display: block; }
  .nav-key-dropdown-row {
    display: flex; justify-content: space-between; gap: 16px; padding: 4px 14px;
    font-size: 12.5px; color: var(--text); white-space: nowrap;
  }
  .card {
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    padding: 32px 36px;
    box-shadow: 0 1px 2px rgba(15, 23, 42, .04);
  }
  .card-row { display: flex; gap: 24px; flex-wrap: wrap; }
  .card-half { flex: 1 1 360px; max-width: calc(50% - 12px); }
  /* /attach's "не привязан"/"Привязать через QR" body — .card itself now
     spans full width (max_width="100%" on the page), so this small bit of
     content needs its own centering instead of sitting stuck at the left
     edge of a wide empty card. */
  .attach-centered { max-width: 480px; margin: 40px auto; text-align: center; }
  /* text-align:center above has no effect on the button — form is
     `display: grid` (global rule) and button has an explicit
     `justify-self: start` (global rule), so it's a grid item, not inline
     content. justify-items:center on the form alone isn't enough either —
     an item's own justify-self always wins over the container's
     justify-items default (confirmed: computed justify-self stayed
     "start" even with justify-items:center set) — override justify-self
     on the button itself. */
  .attach-centered form { justify-items: center; }
  .attach-centered button { justify-self: center; }
  /* Backlog п.144 — /about's 4 feature bullets as a 2x2 grid of small cards
     (2 across, wrapping to 2 rows) instead of a plain <ul>. */
  .about-features { display: grid; grid-template-columns: repeat(2, 1fr); gap: 20px; margin: 24px 0; }
  .about-feature-card {
    background: var(--bg); border: 1px solid var(--border); border-radius: var(--radius-sm);
    padding: 20px 22px;
  }
  .about-feature-card h3 { margin: 0 0 8px; font-size: 15px; }
  .about-feature-card p { margin: 0; color: var(--muted); font-size: 13.5px; }
  @media (max-width: 640px) {
    .about-features { grid-template-columns: 1fr; }
  }
  .about-status { margin-top: 24px; }
  .about-status p { margin: 4px 0; font-size: 13px; }
  .about-status-current { color: var(--text); }
  .about-status-past { color: var(--muted); }
  /* Карточки 2FA / смена email-пароля на /security — короткого содержимого
     (заголовок, пара строк, кнопки), а базовый .card рассчитан на более
     длинные блоки: 32px padding сверху/снизу + h2's margin-top: 32px (у
     h2:first-of-type обнулены border-top/padding-top, но не margin-top) в
     сумме давали ~64px пустоты сверху и лишние 32px снизу под последним
     элементом. Здесь — меньший padding и обнуление внешних отступов первого/
     последнего дочернего элемента, не трогая .card в остальных местах
     (логин/статус-страница/и т.п.). */
  .security-card { padding: 18px 24px; }
  .security-card > *:first-child { margin-top: 0; }
  .security-card > *:last-child { margin-bottom: 0; }
  .settings-block { border-radius: var(--radius-sm); }
  .settings-block.flash-saved { animation: flash-green 1.6s ease-out; }
  .external-storage-stub-row {
    display: flex; align-items: center; justify-content: space-between;
    padding: 10px 0; border-top: 1px solid var(--border);
  }
  .external-storage-stub-row:first-of-type { border-top: none; }
  @keyframes flash-green {
    0% { background-color: var(--ok-bg); }
    100% { background-color: transparent; }
  }
  h1 { font-size: 21px; font-weight: 700; letter-spacing: -.01em; margin: 0 0 8px; color: #10151c; }
  h2 {
    font-size: 12px; font-weight: 700; text-transform: uppercase; letter-spacing: .06em;
    color: var(--muted); margin: 32px 0 14px; padding-top: 20px; border-top: 1px solid var(--border);
  }
  /* border-top/padding-top уже зануляются у первого h2 секции — margin-top
     оставался нетронутым, что вместе с .card's padding-top (32px) давало
     ~64px пустоты перед самым первым заголовком карточки (замечено на
     карточках 2FA/смена email-пароля на /security, тот же баг — во всех
     остальных карточках сайта с h2 первым элементом: rekey, /settings,
     сессии, ключи шифрования). :first-of-type смотрит на позицию среди
     родных братьев/сестёр, а не на глубину относительно .card — работает
     и для h2, обёрнутого в промежуточный div (.settings-block и т.п.). */
  h2:first-of-type { border-top: none; padding-top: 0; margin-top: 0; }
  /* Тот же класс проблем снизу: form/p с собственным margin-bottom поверх
     .card's padding-bottom (32px) — зануляем внешний отступ у последнего
     прямого дочернего элемента карточки. */
  .card > *:last-child { margin-bottom: 0; }
  p { margin: 0 0 12px; }
  a { color: var(--accent); text-decoration: none; }
  a:hover { text-decoration: underline; }
  /* (i) «Как выпустить ключ» в попапе «Добавление ключа» — маленькая кнопка
     внутри hint-строки, открывает независимый попап (key_help_modal_html),
     не hover: текст/команды в нём можно спокойно выделить и скопировать. */
  .key-help-open {
    background: none; border: none; padding: 0; color: var(--accent); font-size: 13px;
    font-weight: 400; cursor: pointer;
  }
  .key-help-open:hover { color: var(--accent-hover); text-decoration: underline; }
  .chat-settings-modal pre {
    margin: 0 0 12px; padding: 10px 12px; background: var(--bg); border: 1px solid var(--border-strong);
    border-radius: var(--radius-sm); font-size: 12.5px; overflow-x: auto; white-space: pre;
  }
  .chat-settings-modal code { background: var(--bg); padding: 1px 5px; border-radius: 4px; font-size: 12.5px; }
  form { display: grid; gap: 12px; margin: 16px 0; }
  label { display: block; font-size: 12.5px; font-weight: 600; color: var(--muted); margin-bottom: 4px; }
  /* Автосохранение (Ключи шифрования, /security) — по умолчанию каждый
     <label>+<select> занимает 2 строки (label {display:block} переносит
     select на новую строку); .field-row держит подпись и контрол в одной
     строке "Название — контрол" (контрол прижат вправо), тем же классом
     размечены и строки-чекбоксы, чтобы название/контрол были в одном месте
     во всех строках блока. Скопировано только сюда, а не в label вообще,
     чтобы не ломать остальные формы сайта. */
  .field-row { display: flex; align-items: center; justify-content: space-between; gap: 10px; }
  .field-row label { display: inline; margin-bottom: 0; white-space: normal; }
  /* width:auto — <select> сам сжимается под ширину текста самого длинного
     option (браузерное поведение), без flex-grow, который бы снова
     растянул его на всю строку. */
  .field-row select { width: auto; flex: 0 0 auto; }
  .field-row input[type="checkbox"] { flex: 0 0 auto; width: auto; }
  /* Тот же width:100% сброс задевает чекбоксы внутри попапов настроек
     (account-settings-modal/chat-settings-modal) — растягивает их на всю
     ширину label, из-за чего подпись переносится на новую строку вместо
     того чтобы стоять рядом с галкой (подтверждено скриншотом, 2026-07-31). */
  .chat-settings-modal input[type="checkbox"] { width: auto; }
  input, select {
    width: 100%; padding: 9px 11px; border: 1px solid var(--border-strong); border-radius: var(--radius-sm);
    font: inherit; font-size: 14px; background: #fff; color: var(--text);
  }
  input:focus, select:focus { outline: 2px solid rgba(35, 66, 93, .25); outline-offset: 1px; border-color: var(--accent); }
  button {
    padding: 10px 18px; border: none; border-radius: var(--radius-sm); background: var(--accent); color: #fff;
    font-size: 14px; font-weight: 600; cursor: pointer; justify-self: start;
  }
  button:hover { background: var(--accent-hover); }
  button.secondary { background: none; color: var(--text); border: 1px solid var(--border-strong); }
  button.secondary:hover { background: var(--bg); }
  button.danger { background: var(--danger); color: #fff; border: none; }
  button.danger:hover { background: var(--danger-hover); }
  /* Backlog — «Привязать Telegram» в «Управление подписками» как зелёная
     кнопка (не обычная ссылка), только для первой привязки, чтобы это
     читалось как основной призыв к действию для нового аккаунта. Размер —
     не базовый button (10px 18px/14px), а как у соседней «Удалить» (та
     сидит в .chat-inline-form, которая переопределяет на 7px 12px/13px) —
     иначе высоты двух кнопок в одном ряду не совпадали (подтверждено
     измерением bounding box: 41.7px против 29px). */
  a.btn-attach {
    display: inline-block; padding: 7px 12px; border-radius: var(--radius-sm);
    background: var(--ok-fg); color: #fff; font-size: 13px; font-weight: 600; text-decoration: none;
    line-height: normal;
  }
  a.btn-attach:hover { background: #163f22; color: #fff; }
  .error { color: var(--danger); background: var(--danger-bg); font-size: 13px; padding: 10px 12px; border-radius: var(--radius-sm); }
  .hint { color: var(--muted); font-size: 13px; }
  .badge { display: inline-block; padding: 3px 10px; border-radius: 999px; font-size: 11.5px; font-weight: 700; white-space: nowrap; }
  .badge-ok { background: var(--ok-bg); color: var(--ok-fg); }
  .badge-pending { background: var(--pending-bg); color: var(--pending-fg); }
  .badge-muted { background: var(--muted-badge-bg); color: var(--muted-badge-fg); }
  .badge-deleted { background: var(--deleted-row-bg); color: var(--danger); font-weight: 400; margin-left: -10px; }
  .qr-box { text-align: center; margin: 24px 0; }
  .qr-box img { width: 220px; height: 220px; border-radius: var(--radius-sm); border: 1px solid var(--border); }
  .status-line { text-align: center; color: var(--muted); font-size: 13px; margin-top: 8px; }
  /* Таблица рабочих аккаунтов на /cabinet — тот же приём, что и у таблицы
     чатов на /chats (--chat-cols/.chat-table/.chat-list-header): общий
     шаблон колонок на заголовок и строки, рамка вокруг всего, border-right
     на каждой ячейке — колонки выровнены независимо от длины названия
     аккаунта. */
  .account-table {
    /* NOT auto for the ✎/«Открыть · Настроить» columns, even though their
       content is fixed per-row — .account-list-header and .account-row are
       two SEPARATE grid containers (one header, one per row), each with
       its own independent auto-sizing pass. The header's cells for these
       two columns hold different content (empty div; short "Архив" label)
       than a row's (icon button; "Открыть · Настроить" links), so auto
       computed a different px width in each grid — header/row column
       borders visibly didn't line up (confirmed via getBoundingClientRect,
       2026-07-30). Fixed px here is identical in both grids regardless of
       their own content, which is exactly what .chat-section/--chat-cols
       already does for the same reason. Название capped at a fixed max
       (labels are short — an fr weight here vacuumed up all the leftover
       row width for a 3-char label). Статус/Действия are where content
       genuinely varies (S3 usage line, action button text), so they're the
       only two fr tracks splitting what's left. */
    --account-cols: minmax(140px, 260px) 80px minmax(130px, 1.2fr) 190px minmax(150px, 1fr);
    margin-top: 10px; border: 1px solid var(--border-strong); border-radius: var(--radius-sm);
    overflow: hidden;
  }
  .account-list-header {
    display: grid; grid-template-columns: var(--account-cols); column-gap: 0; align-items: center;
    background: var(--bg); border-bottom: 1px solid var(--border-strong);
    color: var(--text); font-size: 12.5px; font-weight: 800; text-transform: uppercase; letter-spacing: .02em;
  }
  .account-row {
    display: grid; grid-template-columns: var(--account-cols); column-gap: 0; row-gap: 6px; align-items: center;
    border-bottom: 1px solid var(--border);
  }
  .account-row:last-child { border-bottom: none; }
  .account-list-header > div, .account-row > div {
    padding: 10px 12px; border-right: 1px solid var(--border);
  }
  .account-list-header > div:last-child, .account-row > div:last-child { border-right: none; }
  /* min-width: 0 on every cell, not just the first — a grid item's default
     min-width is auto (= its content's intrinsic width), which a long
     nowrap badge (e.g. "До активации защиты осталось ...") ignores the
     column's own minmax() and pushes past it into the next column
     (confirmed via screenshot at narrow viewports, 2026-07-30). Letting the
     track actually shrink to its assigned width, combined with allowing
     badges here to wrap (below) instead of forcing one line, fixes it. */
  .account-row > div { min-width: 0; }
  .account-row > div:first-child { font-weight: 500; }
  .account-row .badge { white-space: normal; }
  /* Порог поднят с 640 до 820px (2026-07-30, проверено скриншотами) — с
     5 колонками и переносимым бейджем «До активации защиты осталось...»
     зона 640-820px сжимала колонки друг на друга ещё до коллапса. */
  @media (max-width: 820px) {
    .account-list-header { display: none; }
    .account-row { grid-template-columns: 1fr; }
    .account-row > div { border-right: none; }
  }
  /* Backlog — таблица чатов: единый шаблон колонок на заголовок и строки
     (переменная на .chat-section, чтобы .chat-list-header и .chat-row —
     разные уровни вложенности — всё равно выстраивались по одной сетке),
     чтобы бейдж статуса и блок действий были на одной вертикали у всех строк
     секции независимо от длины названия чата. .chat-table — общая рамка
     вокруг заголовка+строк, с border-right на каждой ячейке — видимая
     сетка, не только горизонтальные разделители между строками. */
  .chat-section { --chat-cols: 28px minmax(130px, 1.5fr) minmax(140px, 170px) minmax(64px, 90px) minmax(210px, 1fr); }
  .chat-table {
    margin-top: 10px; border: 1px solid var(--border-strong); border-radius: var(--radius-sm);
    overflow: hidden;
  }
  .chat-list-header {
    display: grid; grid-template-columns: var(--chat-cols); column-gap: 0; align-items: center;
    background: var(--bg); border-bottom: 1px solid var(--border-strong);
    color: var(--muted); font-size: 12.5px; font-weight: 700; text-transform: uppercase; letter-spacing: .02em;
  }
  .chat-row {
    display: grid; grid-template-columns: var(--chat-cols); column-gap: 0; row-gap: 6px; align-items: center;
    border-bottom: 1px solid var(--border);
  }
  .chat-row:last-child { border-bottom: none; }
  .chat-list-header > div, .chat-row > div {
    padding: 10px 12px; border-right: 1px solid var(--border);
  }
  .chat-list-header > div:last-child, .chat-row > div:last-child { border-right: none; }
  .chat-title {
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-weight: 500;
  }
  .chat-status { min-width: 0; }
  .chat-services { display: flex; gap: 8px; align-items: center; min-width: 0; }
  .service-icon { opacity: .65; font-size: 15px; cursor: default; }
  .service-icon:hover { opacity: 1; }
  /* Выключенная услуга (шифрование не настроено / вложения не сохраняются)
     — иконка перечёркнута красной чертой, а не просто приглушена/отсутствует:
     колонка «Услуги» одинакова по составу для всех сохраняемых чатов
     независимо от того, включена ли конкретная услуга — сам факт «выключено»
     должен быть виден сразу, не просто «тусклее» остальных иконок. Черта —
     псевдоэлемент поверх эмодзи, а не другой символ: так работает независимо
     от того, как конкретный браузер/ОС рисует сам эмодзи. */
  .service-icon--off { position: relative; opacity: 1; }
  .service-icon--off::after {
    content: ""; position: absolute; left: 12%; right: 12%; top: 50%; height: 2px;
    background: var(--danger); transform: rotate(-45deg); border-radius: 1px;
  }
  .chat-actions { display: flex; gap: 10px; align-items: center; flex-wrap: wrap; justify-content: flex-end; }
  /* Ключи шифрования на /security — .chat-row вне .chat-section, поэтому
     --chat-cols не определена (была бы 5-колоночная сетка чатов с /chats,
     сюда не подходит: тут ровно 3 ячейки — имя/статус/кнопки). Отдельный,
     более узкий шаблон колонок именно для этого списка, чтобы все три стояли
     строго в одну строку независимо от длины имени ключа. */
  .key-list .chat-row { grid-template-columns: minmax(140px, 1fr) auto auto; }
  /* Строка ключа теперь живёт в узкой card-half карточке «Рабочий аккаунт»
     (Backlog п.142) — "Переименовать" и "Удалить" делят один auto-столбец
     и без этого переносятся на вторую строку внутри него на такой ширине;
     четыре поля (имя, статус, «Переименовать», «Удалить») должны стоять
     строго в одну строку. */
  .key-list .chat-actions { flex-wrap: nowrap; }
  /* Активные Telegram-сессии на /security — тот же случай, но плашка
     статуса стоит первой колонкой (рядом с общей плашкой «Защита включена»
     над списком), затем устройство/ID сессии, затем кнопки. */
  .session-list .chat-row { grid-template-columns: auto minmax(140px, 1fr) auto; align-items: start; }
  /* .chat-title's nowrap/ellipsis (built for a single chat name) would clip
     the multi-line ID/device/app/IP/dates block п.66 now renders here — this
     column needs normal wrapping instead. */
  .session-list .chat-title { white-space: normal; overflow: visible; text-overflow: clip; }
  /* Backlog — ниже 640px пять жёстких колонок не помещаются: чекбокс,
     название, глубина хранения, услуги и действия начинают наезжать друг
     на друга. Тот же "collapse в drawer"-принцип, что уже применён к
     .archive-layout при 900px (см. ниже) — просто переключаемся на одну
     колонку и обычный вертикальный поток вместо грид-таблицы; статус/
     услуги/действия занимают всю ширину строки, поэтому им вертикальный
     разделитель справа больше не нужен (он бы просто дублировал внешнюю
     рамку .chat-table). */
  @media (max-width: 640px) {
    .chat-list-header { display: none; }
    .chat-row { grid-template-columns: 28px 1fr; }
    .chat-title { white-space: normal; overflow: visible; text-overflow: clip; }
    .chat-status, .chat-services, .chat-actions { grid-column: 1 / -1; justify-content: flex-start; }
  }
  .chat-inline-form { display: flex; flex-direction: row; flex-wrap: wrap; gap: 6px; margin: 0; }
  .chat-inline-form input { width: 80px; padding: 7px 9px; }
  .chat-inline-form select { width: auto; padding: 6px 8px; margin-left: 4px; }
  .chat-inline-form button { padding: 7px 12px; font-size: 13px; }
  /* Кнопки в .chat-actions строго в одну строку с названием/статусом,
     без переноса. */
  .chat-inline-form-nowrap { flex-wrap: nowrap; }
  .account-row-controls { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; }
  /* Шестерёнки в таблице аккаунтов (✎ «Название и тариф», ⚙ «Настроить
     чаты») были мельче остального интерфейса и легко терялись — крупнее и
     без притемнения, только в этой таблице (другие .topbar-gear — на
     /chats, в архиве — не трогаем). */
  .account-row .topbar-gear { font-size: 20px; opacity: .85; }
  /* «Действия»: ⚙ и красная «Удалить рабочий аккаунт» — .chat-inline-form
     блочный флекс-контейнер сам по себе переносится на новую строку после
     инлайновой ⚙; общий flex-обёртка держит их строго в одну строку. */
  .account-row-actions { display: inline-flex; align-items: center; gap: 10px; flex-wrap: wrap; }
  .account-create-form input { width: 40ch; min-width: 140px; }
  .account-create-form select { width: auto; min-width: 0; }
  .msg-list { margin-top: 8px; }
  .msg-thread { padding: 16px 0; border-bottom: 1px solid var(--border); }
  .msg-thread:last-child { border-bottom: none; }
  .msg-thread-deleted { background: var(--deleted-row-bg); padding-left: 12px; padding-right: 12px; margin: 0 -12px; border-radius: var(--radius-sm); }
  .msg-version { margin: 8px 0 0 14px; padding: 8px 0 8px 12px; border-left: 2px solid var(--border); }
  /* Backlog п.33 — та же мягкая подложка, что уже красит строку в списке
     сообщений (.msg-row-state-deleted) и ленту изменений (.msg-thread-deleted),
     теперь и в развёрнутой версии сообщения. */
  .msg-version-deleted { background: var(--deleted-row-bg); border-radius: var(--radius-sm); }
  .msg-meta { display: flex; align-items: center; gap: 8px; }
  .msg-meta-deleted { margin-top: 4px; }
  .msg-text { margin-top: 4px; white-space: pre-wrap; word-break: break-word; }
  .attachment-media { margin-top: 6px; }
  .attachment-media img, .attachment-media video {
    max-width: 100%; max-height: 420px; border-radius: var(--radius-sm); display: block;
  }
  .attachment-media img { cursor: zoom-in; }
  .attachment-pending .attachment-status { font-style: italic; }
  .lightbox-overlay {
    display: none; position: fixed; inset: 0; background: rgba(0, 0, 0, .85);
    z-index: 1000; align-items: center; justify-content: center; padding: 32px;
  }
  .lightbox-overlay.open { display: flex; }
  .lightbox-overlay img { max-width: 100%; max-height: 100%; border-radius: var(--radius-sm); }
  .lightbox-close {
    position: absolute; top: 16px; right: 24px; background: none; border: none;
    color: #fff; font-size: 32px; line-height: 1; cursor: pointer; padding: 8px;
  }
  .chat-settings-gear { background: none; border: none; padding: 0 0 0 8px; margin: 0; color: inherit; line-height: 1; }
  .encryption-provide-label { cursor: pointer; padding-left: 8px; }
  .encrypted-text { color: var(--muted); font-style: italic; }
  .chat-settings-overlay {
    display: none; position: fixed; inset: 0; background: rgba(0, 0, 0, .45);
    z-index: 1000; align-items: center; justify-content: center; padding: 20px;
  }
  .chat-settings-overlay.open { display: flex; }
  .chat-settings-modal {
    background: var(--surface); border-radius: var(--radius); padding: 28px 32px;
    max-width: 360px; width: 100%; box-shadow: 0 8px 24px rgba(15, 23, 42, .18); position: relative;
  }
  .chat-settings-modal h3 { margin: 0 0 16px; font-size: 15px; }
  /* Попап настроек сохранённого чата (/chats, шестерёнка) — срок хранения
     и ключ шифрования были каждый на всю ширину попапа, подпись отдельной
     строкой над полем. Теперь оба — в одной строке (пара), и внутри каждого
     подпись сразу же поле на одной линии (.field-row), а не подпись сверху/
     поле снизу. */
  .chat-update-pair { display: flex; gap: 16px; }
  .chat-update-pair > .field-row { flex: 1 1 0; min-width: 0; }
  .chat-update-pair input[type="number"] { width: 64px; flex: 0 0 auto; }
  /* Backlog п.120 — «Управление подписками»'s account-settings modal: 4
     independent parameter-value forms (rename/plan/retention/attachment
     limit), each with its own submit button, laid out as 4 cards in a 2x2
     grid instead of the base .chat-settings-modal's single stacked column.
     Widened from the base 360px (shared with the plain chat-settings popup
     elsewhere, not touched here) so two ~200px cards actually fit side by
     side rather than immediately wrapping to 1 column. */
  .chat-settings-modal.account-settings-modal { max-width: 560px; }
  .account-settings-card { flex: 1 1 200px; max-width: calc(50% - 8px); }
  @media (max-width: 480px) {
    .account-settings-card { max-width: 100%; }
  }
  .chat-settings-modal .modal-close {
    position: absolute; top: 10px; right: 14px; background: none; border: none;
    font-size: 22px; line-height: 1; cursor: pointer; color: var(--muted); padding: 4px;
  }
  /* Подтверждение завершения Telegram-сессии — красная рамка/предупреждение
     (действие необратимо), большая безопасная «Отменить» и маленькая
     «Да», чтобы случайный клик не оборвал живую сессию. */
  .terminate-confirm-modal { border: 2px solid var(--danger); }
  .terminate-confirm-warning {
    background: var(--danger-bg); color: var(--danger); padding: 12px 14px;
    border-radius: var(--radius-sm); margin-bottom: 20px;
  }
  .terminate-confirm-warning p { margin: 0 0 6px; }
  .terminate-confirm-warning p:last-child { margin-bottom: 0; }
  .terminate-confirm-actions { display: flex; align-items: center; justify-content: flex-end; gap: 16px; }
  #terminate-confirm-cancel { padding: 16px 40px; font-size: 16px; }
  #terminate-confirm-yes { padding: 5px 14px; font-size: 12px; font-weight: 600; }
  /* Тот же попап-паттерн, переиспользован accounts.py для удаления рабочего
     аккаунта (Backlog) — тот же .terminate-confirm-modal/-warning/-actions,
     свои id только у кнопок (большая безопасная «НЕТ», маленькая опасная «Да»). */
  #delete-account-confirm-cancel { padding: 16px 40px; font-size: 16px; }
  #delete-account-confirm-yes { padding: 5px 14px; font-size: 12px; font-weight: 600; }
  /* Шаги попапа «Добавление ключа» — обычные <div>, не <form> (нет form'овского
     display:grid; gap), из-за чего кнопка «слипалась» с полем ввода вплотную;
     здесь тот же отступ и центрирование по горизонтали. */
  .add-key-step { display: flex; flex-direction: column; gap: 12px; align-items: center; text-align: center; }
  /* [hidden] and .add-key-step both have specificity (0,1,0) — same-specificity
     ties go to whichever rule comes later, and this stylesheet always counts as
     "later" than the browser's own UA stylesheet, so display:flex above was
     silently winning over [hidden]'s display:none and showing all three steps
     at once. Bump specificity so [hidden] actually wins here. */
  .add-key-step[hidden] { display: none; }
  .add-key-step input { width: 100%; }
  .add-key-step button { justify-self: center; }
  .diff-ins { background: var(--edited-row-bg-hover); text-decoration: none; border-radius: 3px; padding: 0 1px; }
  .diff-del { background: var(--deleted-row-bg-hover); text-decoration: line-through; opacity: .75; border-radius: 3px; padding: 0 1px; }
  .pager { display: flex; flex-wrap: wrap; gap: 10px; align-items: center; justify-content: center; margin-top: 28px; font-size: 14px; }
  .pager strong { color: var(--text); }

  .topbar-gear { text-decoration: none; opacity: .65; font-size: 14px; }
  .topbar-gear:hover { opacity: 1; }
  .archive-search-bar { display: flex; align-items: center; gap: 12px; width: 50%; margin: 0 auto; }
  .archive-search-bar input[type="search"] { flex: 1 1 auto; width: auto; margin: 0; }
  .archive-search-bar label {
    display: flex; align-items: center; gap: 6px; font-size: 13px; font-weight: 400;
    color: var(--muted); white-space: nowrap; margin: 0;
  }
  .archive-search-bar input[type="checkbox"] { width: auto; }
  .archive-search-bar button { white-space: nowrap; justify-self: auto; }
  .archive-layout { display: flex; position: relative; margin: 16px -36px -32px; min-height: 65vh; }
  .archive-nav-toggle { display: none; }
  .archive-nav-backdrop { display: none; }
  /* Backlog п.96 — below this width the rail (60px) + sidebar (240px) +
     messages pane (min 260px) no longer fit; collapse rail+sidebar into a
     drawer opened by .archive-nav-toggle instead of letting them overlap. */
  @media (max-width: 900px) {
    .archive-nav-toggle {
      display: inline-flex; align-items: center; justify-content: center;
      width: 36px; height: 36px; position: absolute; top: 16px; left: 16px; z-index: 6;
      border: 1px solid var(--border-strong); border-radius: var(--radius-sm);
      background: var(--surface); color: var(--text); font-size: 16px; cursor: pointer;
    }
    .archive-rail, .archive-sidebar { display: none; }
    .archive-messages { padding-top: 60px; }
    .archive-layout.nav-open .archive-rail,
    .archive-layout.nav-open .archive-sidebar {
      display: flex; position: absolute; top: 0; bottom: 0; z-index: 5; background: var(--surface);
    }
    .archive-layout.nav-open .archive-rail { left: 0; }
    .archive-layout.nav-open .archive-sidebar {
      left: 60px; box-shadow: 4px 0 16px rgba(0, 0, 0, .2);
    }
    .archive-layout.nav-open .archive-nav-backdrop {
      display: block; position: absolute; inset: 0; z-index: 4; background: rgba(0, 0, 0, .25);
    }
    .archive-detail.open {
      position: fixed; inset: 0; z-index: 10; width: auto; max-height: none;
      border-left: none; background: var(--surface);
    }
    .archive-search-bar { width: 100%; }
  }
  .archive-rail {
    flex: 0 0 60px; width: 60px; border-right: 1px solid var(--border);
    padding: 16px 0; display: flex; flex-direction: column; align-items: center; gap: 10px;
  }
  .rail-icon {
    width: 36px; height: 36px; border-radius: 50%; display: flex; align-items: center; justify-content: center;
    font-size: 13px; font-weight: 700; color: var(--text); border: 2px solid var(--border-strong);
    background: var(--surface); text-decoration: none; flex: 0 0 auto;
  }
  .rail-icon:hover { text-decoration: none; border-color: var(--accent); }
  .rail-icon-active { background: var(--accent); border-color: var(--accent); color: #fff; }
  .rail-icon-pending { border-color: var(--pending-fg); color: var(--pending-fg); }
  .rail-icon-muted { border-color: var(--border-strong); color: var(--muted); }
  .rail-icon-current { box-shadow: 0 0 0 2px var(--surface), 0 0 0 4px var(--accent); }
  .rail-icon-create { border-style: dashed; color: var(--muted); font-size: 18px; }
  .rail-icon-disabled { opacity: .4; cursor: not-allowed; }
  .rail-icon-disabled:hover { border-color: var(--border-strong); }
  .archive-sidebar {
    flex: 0 0 240px; width: 240px; border-right: 1px solid var(--border);
    padding: 20px 16px; overflow-y: auto;
  }
  .archive-messages {
    flex: 1 1 auto; min-width: 260px; border-right: 1px solid var(--border);
    padding: 20px 0; overflow-y: auto; display: flex; flex-direction: column;
  }
  .archive-messages h2 { margin-top: 0; padding: 0 20px 14px; display: flex; align-items: center; }
  .chat-stats { color: var(--muted); font-size: 13px; font-weight: 400; }
  .archive-messages .hint { padding: 0 20px; }
  .archive-messages .pager { padding: 0 20px; }
  .msg-rows { display: flex; flex-direction: column; }
  .msg-row {
    display: flex; flex-direction: column; gap: 4px; width: 100%; padding: 10px 20px; border: none;
    border-bottom: 1px solid var(--border); background: none; text-align: left; cursor: pointer;
    font: inherit; color: var(--text); border-radius: 0; justify-self: auto;
  }
  .msg-row:hover { background: var(--bg); }
  .msg-row-state-edited { background: var(--edited-row-bg); }
  .msg-row-state-edited:hover { background: var(--edited-row-bg-hover); }
  .msg-row-state-deleted { background: var(--deleted-row-bg); }
  .msg-row-state-deleted:hover { background: var(--deleted-row-bg-hover); }
  .msg-row.active { background: var(--accent); color: #fff; }
  .msg-row-header { display: flex; align-items: center; gap: 8px; }
  .msg-row-flag { flex: 0 0 auto; width: 6px; height: 6px; border-radius: 50%; }
  .msg-row-flag-edited { background: var(--pending-fg); }
  .msg-row-flag-deleted { background: var(--danger); }
  .msg-row.active .msg-row-flag-edited { background: #fff; opacity: .8; }
  .msg-row.active .msg-row-flag-deleted { background: #fff; opacity: .8; }
  .msg-row-time { flex: 0 0 auto; font-size: 11.5px; opacity: .7; }
  .msg-row-sender {
    flex: 0 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
    font-size: 11.5px; opacity: .7;
  }
  .msg-row-first {
    display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical;
    overflow: hidden; font-size: 13.5px; font-weight: 700; line-height: 1.4;
  }
  .msg-row-first-deleted { font-weight: 400; font-style: italic; opacity: .6; }
  .msg-row-first-empty { font-weight: 400; font-style: italic; opacity: .6; }
  .msg-row-rest {
    display: -webkit-box; -webkit-line-clamp: 3; -webkit-box-orient: vertical;
    overflow: hidden; font-size: 13px; line-height: 1.4; opacity: .75; margin-top: 2px;
  }
  .archive-detail {
    flex: 0 0 auto; width: 0; padding: 0; overflow: hidden; border-left: none;
    transition: width .18s ease;
  }
  .archive-detail.open {
    width: 420px; padding: 28px 32px; overflow-y: auto; border-left: 1px solid var(--border);
    align-self: flex-start; position: sticky; top: 24px; max-height: calc(100vh - 48px);
  }
  .detail-close {
    display: none; margin: 0 0 12px auto; width: 28px; height: 28px; padding: 0; border-radius: 50%;
    background: none; border: 1px solid var(--border-strong); color: var(--muted); font-size: 16px;
    line-height: 1; cursor: pointer; align-items: center; justify-content: center;
  }
  .detail-close:hover { background: var(--bg); color: var(--text); }
  .archive-detail.open .detail-close { display: flex; }
  .archive-search input[type="search"] { margin-bottom: 10px; }
  .chat-list-controls { margin: 20px 0 4px; }
  .chat-list-controls input[type="search"] { margin-bottom: 10px; }
  .chat-select-cell { flex: 0 0 20px; }
  .chat-bulk-controls { display: flex; align-items: center; gap: 16px; flex-wrap: wrap; margin-top: 4px; font-size: 13.5px; }
  .chat-bulk-controls label { display: flex; align-items: center; gap: 6px; font-size: 13.5px; font-weight: 400; color: var(--text); margin: 0; }
  .chat-bulk-controls form { margin: 0; }
  .chat-bulk-controls button { padding: 7px 12px; font-size: 13px; }
  .filter-chips { display: flex; flex-wrap: wrap; gap: 6px; margin-bottom: 16px; }
  .filter-chip {
    padding: 5px 11px; border-radius: 999px; border: 1px solid var(--border-strong); background: none;
    color: var(--muted); font-size: 12px; font-weight: 600; cursor: pointer; justify-self: auto;
  }
  .filter-chip.active { background: var(--accent); border-color: var(--accent); color: #fff; }
  .sidebar-list { display: flex; flex-direction: column; gap: 2px; }
  .sidebar-item {
    display: flex; align-items: center; gap: 9px; padding: 8px 10px; border-radius: var(--radius-sm);
    color: var(--text); font-size: 13.5px; overflow: hidden;
  }
  .sidebar-item:hover { background: var(--bg); text-decoration: none; }
  .sidebar-item.active { background: var(--accent); color: #fff; }
  .sidebar-item-title { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
  .sidebar-dot { flex: 0 0 auto; width: 7px; height: 7px; border-radius: 50%; background: currentColor; opacity: .55; }
  .sidebar-item.active .sidebar-dot { opacity: .85; }
  .sidebar-dot-private { color: var(--accent); }
  .sidebar-item.active .sidebar-dot-private { color: #fff; }
  .sidebar-dot-group { color: var(--pending-fg); }
  .sidebar-item.active .sidebar-dot-group { color: #fff; }
  .sidebar-dot-channel { color: var(--ok-fg); }
  .sidebar-item.active .sidebar-dot-channel { color: #fff; }
