* { box-sizing: border-box; }
  :root {
    --bg:#f3f4f7; --card-bg:#fff; --card-border:#e8eaef; --text:#1b1e24; --border:#eceef2; --header-border:#e5e6e9;
    /* Серый мелкого текста затемнён 03.09: #8a8f98 давал 3,25 : 1 на белом
       при кегле 11–13 px (норма 4,5), а им набраны ВСЕ объяснения — подписи
       плиток, легенды, даты, «база: сумма договора минус НДС». Оба критика
       нашли это порознь. #6a6f78 = 5,05 на карточке и 4,6 на фоне страницы. */
    --muted:#6a6f78; --th-color:#6a6f78; --link:#2f6fed; --pos:#1a8a4c; --neg:#d9364a; --warn:#b06a00;
    --badge-bg:#eef1fb; --badge-color:#2f6fed; --hover-bg:#f6f8fb; --zebra:#fafbfc;
    --err-bg:#fdecee; --msg-bg:#e9f9ee; --warn-bg:#fdf6e3;
    --input-border:#d9dce3; --input-bg:#fff; --focus-ring:rgba(47,111,237,.16);
    --button-bg:#2f6fed; --button-hover:#265cc7; --danger-border:#f0c4ca; --danger-hover:#fdf0f1;
    --icon-link:#a6aab2; --code-bg:#f4f5f7; --total-border:#d5d8df;
    --shadow-card:0 1px 2px rgba(16,24,40,.04), 0 1px 5px rgba(16,24,40,.05);
    --shadow-card-hover:0 4px 14px rgba(16,24,40,.09);
    --shadow-stat:0 2px 4px rgba(16,24,40,.05), 0 6px 18px rgba(16,24,40,.07);
  }
  :root[data-theme="dark"] {
    --bg:#14161b; --card-bg:#1e2127; --card-border:#2a2d34; --text:#e6e6e6; --border:#2c2f36; --header-border:#2c2f36;
    --muted:#8a8f98; --th-color:#8a8f98; --link:#6ea1ff; --pos:#3ecf7e; --neg:#ff6b7a; --warn:#e0a030;
    --badge-bg:#26304a; --badge-color:#6ea1ff; --hover-bg:#262a32; --zebra:#22252b;
    --err-bg:#3a2226; --msg-bg:#193324; --warn-bg:#33301c;
    --input-border:#3a3d44; --input-bg:#1a1d23; --focus-ring:rgba(110,161,255,.22);
    --button-bg:#2f6fed; --button-hover:#265cc7; --danger-border:#5a3038; --danger-hover:#3a2226;
    --icon-link:#8a8f98; --code-bg:#14161a; --total-border:#454a54;
    --shadow-card:0 1px 3px rgba(0,0,0,.35);
    --shadow-card-hover:0 4px 14px rgba(0,0,0,.45);
    --shadow-stat:0 2px 6px rgba(0,0,0,.35), 0 8px 22px rgba(0,0,0,.35);
  }
  body { font-family:-apple-system,Segoe UI,Roboto,sans-serif; background:var(--bg); margin:0; color:var(--text);
    -webkit-font-smoothing:antialiased; }
  header { background:var(--card-bg); padding:14px 24px; display:flex; justify-content:space-between; align-items:center; border-bottom:1px solid var(--header-border); }
  header .title { font-weight:650; font-size:16px; letter-spacing:-.2px; }
  header .user { font-size:13px; color:var(--muted); }
  header a { color:var(--link); text-decoration:none; font-size:13px; margin-left:12px; }
  main { max-width:960px; margin:24px auto 48px; padding:0 16px; }
  a { color:var(--link); }
  a:focus-visible, button:focus-visible, summary:focus-visible { outline:2px solid var(--link); outline-offset:2px; border-radius:4px; }

  /* --- Карточки --- */
  .card { background:var(--card-bg); border:1px solid var(--card-border); border-radius:12px; padding:20px; margin-bottom:20px;
    box-shadow:var(--shadow-card); overflow-x:auto; transition:box-shadow .2s; }
  @media (hover:hover) { .card:hover { box-shadow:var(--shadow-card-hover); } }
  .card h3 { margin:0 0 12px; font-size:15px; font-weight:650; letter-spacing:-.1px; }

  /* --- Тумблер как на телефоне ---
     Настоящий <input type="checkbox">, спрятанный под ползунком: так
     работают клавиатура, чтение с экрана и обычный submit формы, а
     видимая часть — просто две подкрашенные фигуры. Отдельная точка
     касания 44 px по высоте: палец в 20 px не попадает.
     Никакой картинки и никакого шрифта — только фон и сдвиг. */
  .sw { display:inline-flex; align-items:center; gap:10px; cursor:pointer;
    user-select:none; min-height:44px; }
  .sw input { position:absolute; opacity:0; width:0; height:0; }
  .sw .sw-slot { position:relative; flex:0 0 auto; width:50px; height:30px; border-radius:999px;
    background:var(--muted); opacity:.55; transition:background .18s, opacity .18s; }
  .sw .sw-slot::after { content:''; position:absolute; top:3px; left:3px; width:24px; height:24px;
    border-radius:50%; background:#fff; box-shadow:0 1px 3px rgba(0,0,0,.35);
    transition:transform .18s; }
  .sw input:checked + .sw-slot { background:var(--pos); opacity:1; }
  .sw input:checked + .sw-slot::after { transform:translateX(20px); }
  .sw input:focus-visible + .sw-slot { outline:2px solid var(--link); outline-offset:2px; }
  .sw .sw-lbl { font-size:13.5px; }
  /* Малый размер ТОГО ЖЕ тумблера (владелец 01.10.2026: «сделать чуть
     меньше»): подчинённая строка не должна спорить с галочками услуг.
     Это не второй вид — та же семья и те же токены, только дорожка 40×24
     и ползунок 18 px. Точка касания остаётся у ВСЕЙ строки-подписи:
     на телефоне палец попадает в текст, а не в ползунок. */
  .sw-sm { min-height:32px; gap:8px; }
  .sw-sm .sw-slot { width:40px; height:24px; }
  .sw-sm .sw-slot::after { top:3px; left:3px; width:18px; height:18px; }
  .sw-sm input:checked + .sw-slot::after { transform:translateX(16px); }
  .sw-sm input:disabled + .sw-slot { opacity:.35; }
  .sw-sm input:disabled ~ .sw-lbl { color:var(--muted); }

  /* --- Плашки сводки (grid3) — главные цифры по сотруднику --- */
  .grid3 { display:grid; grid-template-columns:repeat(3,1fr); gap:16px; margin-bottom:24px; }
  .grid3 .card { position:relative; margin-bottom:0; padding:18px 20px 16px; overflow:hidden;
    border-bottom:3px solid var(--muted); box-shadow:var(--shadow-stat); }
  .grid3 .card::before { content:''; position:absolute; inset:auto 0 0 0; height:44%;
    background:linear-gradient(to top, var(--stat-tint, transparent), transparent); pointer-events:none; }
  .grid3 .card:nth-child(1) { border-bottom-color:var(--pos); --stat-tint:rgba(26,138,76,.05); }
  .grid3 .card:nth-child(2) { border-bottom-color:var(--link); --stat-tint:rgba(47,111,237,.05); }
  .grid3 .card:nth-child(3) { border-bottom-color:var(--warn); --stat-tint:rgba(199,119,0,.05); }
  :root[data-theme="dark"] .grid3 .card:nth-child(1) { --stat-tint:rgba(62,207,126,.09); }
  :root[data-theme="dark"] .grid3 .card:nth-child(2) { --stat-tint:rgba(110,161,255,.09); }
  :root[data-theme="dark"] .grid3 .card:nth-child(3) { --stat-tint:rgba(224,160,48,.09); }
  @media (hover:hover) { .grid3 .card:hover { box-shadow:var(--shadow-card-hover); } }
  .stat-label { font-size:11.5px; color:var(--th-color); text-transform:uppercase; margin-bottom:6px; letter-spacing:.5px; font-weight:600; }
  .big { font-size:24px; font-weight:650; letter-spacing:-.4px; font-variant-numeric:tabular-nums; line-height:1.2; }

  /* --- Таблицы --- */
  table { width:100%; border-collapse:collapse; }
  th, td { text-align:left; padding:10px 8px; font-size:14px; border-bottom:1px solid var(--border); }
  th { color:var(--th-color); font-weight:600; font-size:11.5px; text-transform:uppercase; letter-spacing:.4px;
    background:var(--card-bg); position:sticky; top:0; z-index:1; }
  tbody tr:last-child td { border-bottom:none; }
  tbody tr:nth-child(even) { background:var(--zebra); }
  tr.clickable { cursor:pointer; }
  tr.clickable:hover { background:var(--hover-bg); }
  tr.total-row { font-weight:600; }
  tr.total-row td { border-top:2px solid var(--total-border); }
  .num { text-align:right; font-variant-numeric:tabular-nums; white-space:nowrap; }

  /* --- Цвета --- */
  .pos { color:var(--pos); } .neg { color:var(--neg); } .warn { color:var(--warn); }
  .muted { color:var(--muted); font-size:13px; }
  .badge { display:inline-block; padding:2px 9px; border-radius:999px; font-size:12px; font-weight:500;
    background:var(--badge-bg); color:var(--badge-color); }

  /* --- Уведомления --- */
  .msg, .ok { background:var(--msg-bg); color:var(--pos); border-left:3px solid var(--pos); padding:10px 14px; border-radius:8px; margin-bottom:16px; font-size:14px; }
  .err { background:var(--err-bg); color:var(--neg); border-left:3px solid var(--neg); padding:10px 14px; border-radius:8px; margin-bottom:16px; font-size:14px; }
  .warn-block { background:var(--warn-bg); color:var(--warn); border-left:3px solid var(--warn); padding:10px 14px; border-radius:8px; margin-bottom:16px; font-size:14px; }

  /* --- Формы --- */
  input, select, textarea { width:100%; box-sizing:border-box; padding:9px 10px; margin-bottom:10px;
    border:1px solid var(--input-border); background:var(--input-bg); color:var(--text); border-radius:8px; font-size:14px; font-family:inherit;
    transition:border-color .15s, box-shadow .15s; }
  input:focus, select:focus, textarea:focus { outline:none; border-color:var(--link); box-shadow:0 0 0 3px var(--focus-ring); }
  /* Подсказка в пустом поле обязана выглядеть подсказкой: браузер красит её
     почти цветом текста, и поле «ID контакта» с примером 19776 читалось как
     уже заполненное — в форме, которая шлёт человеку письмо (критик 03.09). */
  /* ПОДСКАЗКА ЧИТАЕМА: opacity .75 поверх --muted давала 3,08 : 1 в светлой
     теме и 3,5 в тёмной (критик 17.10.2026) — подсказки в поле поиска кода
     ОК и «подобрать похожего на» были бледнее нормы. --muted сам даёт 5,05/4,9,
     курсив и так отличает подсказку от введённого. */
  input::placeholder, textarea::placeholder { color:var(--muted); opacity:1; font-style:italic; }
  label { font-size:12px; color:var(--th-color); display:block; margin-bottom:4px; }
  button { padding:9px 16px; background:var(--button-bg); color:#fff; border:none; border-radius:8px; font-size:14px; cursor:pointer;
    transition:background .15s, transform .05s; }
  button:hover { background:var(--button-hover); }
  button:active { transform:translateY(1px); }
  button.danger { background:var(--card-bg); color:var(--neg); border:1px solid var(--danger-border); padding:5px 10px; font-size:12px; }
  button.danger:hover { background:var(--danger-hover); }
  button.plain { background:var(--card-bg); color:var(--link); border:1px solid var(--input-border); padding:5px 10px; font-size:12px; }
  button.plain:hover { background:var(--hover-bg); }
  button.small { padding:6px 12px; font-size:12px; }
  /* Ссылка, выглядящая кнопкой: переход (скачать файл, перейти на свод),
     а не отправка формы — семантически это <a>, а не <button>. */
  a.btn { display:inline-block; padding:9px 16px; background:var(--button-bg); color:#fff;
    border-radius:8px; font-size:14px; text-decoration:none; transition:background .15s; }
  a.btn:hover { background:var(--button-hover); color:#fff; }

  /* --- Ссылки-действия --- */
  .deal-link { color:var(--icon-link); text-decoration:none; font-size:15px; }
  .deal-link:hover { color:var(--link); }
  .approve-link { display:inline-block; padding:6px 12px; background:var(--button-bg); color:#fff; border-radius:6px; font-size:13px; text-decoration:none; white-space:nowrap; transition:background .15s; }
  .approve-link:hover { background:var(--button-hover); }

  /* --- Вкладки --- */
  .tab-bar { display:flex; flex-wrap:wrap; gap:2px; margin-bottom:20px; border-bottom:1px solid var(--border); }
  .tab-bar a { padding:9px 14px; font-size:13px; text-decoration:none; color:var(--muted);
    border-bottom:2px solid transparent; border-radius:8px 8px 0 0; margin-bottom:-1px;
    white-space:nowrap; transition:color .15s, background .15s; }
  .tab-bar a:hover { color:var(--text); background:var(--hover-bg); }
  .tab-bar a.active { color:var(--link); border-bottom-color:var(--link); font-weight:600; }

  /* --- Навигация (nav_html в lib/nav.php) ---
     Полоса .nav-bar: кнопка «☰» (только на телефоне), само меню и
     колокольчик. Пункты меню — прямые ссылки и разделы на нативном
     details: без JavaScript раздел всё равно раскрывается, скрипт даёт лишь
     удобства. На телефоне .portal-nav превращается в выдвижную панель —
     но только там, где есть что выдвигать (класс .nav-drawer): у
     сотрудника пунктов два, и панель ему ни к чему. */
  .nav-bar { background:var(--card-bg); border-bottom:1px solid var(--header-border);
    padding:8px 24px; display:flex; align-items:center; gap:4px; }
  /* Логотип владельца — первым в полосе (12.10). Полоса есть на КАЖДОЙ
     странице, поэтому герб виден везде; на телефоне он лежит ВНЕ выдвижной
     панели и остаётся на виду, когда меню уехало за «☰». Высоту задаём
     картинке, а не полосе: полоса растёт под неё сама, и второго размера
     герба заводить не нужно (исходник 188×200 — с запасом на retina). */
  .nav-logo { display:inline-flex; align-items:center; flex:0 0 auto; margin-right:8px; }
  /* 34 точки, а не 28 (владелец 12.10: «герб мелковат, подними немного»).
     Полоса растёт под картинку сама: 34 + 8+8 отступов = 50 точек против
     46 прежних — на строку меню это не влияет, а герб читается. */
  .nav-logo img { display:block; height:34px; width:auto; }
  .portal-nav { display:flex; flex-wrap:wrap; align-items:center; gap:4px; flex:1; min-width:0; }
  .portal-nav a, .nav-group > summary { color:var(--muted); text-decoration:none; font-size:13px;
    padding:6px 11px; border-radius:8px; white-space:nowrap; transition:color .15s, background .15s;
    display:inline-flex; align-items:center; gap:4px; cursor:pointer; list-style:none; }
  .nav-group > summary::-webkit-details-marker { display:none; }
  .portal-nav a:hover, .nav-group > summary:hover { background:var(--bg); color:var(--link); }
  .portal-nav a.nav-active, .nav-group.nav-here > summary { background:var(--badge-bg); color:var(--badge-color); font-weight:600; }

  /* Значки меню — контурные SVG (`nav_ico()`, 25.10), цветом подписи. */
  .nav-ico { flex-shrink:0; display:block; opacity:.9; }
  .portal-nav a .nav-ico, .nav-group > summary .nav-ico { margin-right:2px; }
  .nav-burger .nav-ico { opacity:1; }
  .nav-bell { display:inline-flex; align-items:center; justify-content:center; }
  /* Кнопки страницы у правого края полосы (`nav_html($active, $right)`):
     на переписке — тема и «Выйти» вместо отдельной шапки. Тот же размер
     и вид, что у колокольчика: один ряд, одна система. */
  .nav-tool { display:inline-flex; align-items:center; justify-content:center; width:34px; height:34px;
    border-radius:9px; color:var(--muted); background:none; border:1px solid transparent; cursor:pointer;
    padding:0; text-decoration:none; transition:background .15s, color .15s; }
  .nav-tool + .nav-tool { margin-left:2px; }
  /* «Выйти» — со словом, как на остальных страницах портала (критик 25.10:
     голая стрелка рядом с колокольчиком — промах сбрасывает вход). На
     телефоне слово уходит, цель растёт до 40 px и отступает от соседей. */
  .nav-tool.nav-exit { width:auto; padding:0 10px; gap:6px; font-size:13px; margin-right:6px; }
  .nav-here-lbl { display:none; }
  .nav-right { display:inline-flex; align-items:center; margin-left:auto; }
  .nav-right + .nav-bell { margin-left:0; }
  .nav-tool:hover { background:var(--bg); color:var(--link); }
  .nav-tool:focus-visible { outline:2px solid var(--link); outline-offset:2px; }
  /* Тема: в светлой — луна («включить тёмную»), в тёмной — солнце. */
  .theme-toggle .ico-sun { display:none; }
  :root[data-theme="dark"] .theme-toggle .ico-sun { display:block; }
  :root[data-theme="dark"] .theme-toggle .ico-moon { display:none; }

  /* Раздел с выпадашкой */
  .nav-group { position:relative; }
  .nav-group > summary::after { content:'▾'; font-size:10px; opacity:.65; margin-left:1px; }
  .nav-group[open] > summary::after { opacity:1; }
  .nav-sub { position:absolute; z-index:60; top:calc(100% + 6px); left:0; min-width:216px;
    background:var(--card-bg); border:1px solid var(--card-border); border-radius:12px;
    box-shadow:var(--shadow-card-hover); padding:6px; display:flex; flex-direction:column; gap:2px; }
  .nav-sub a { justify-content:space-between; padding:8px 10px; }

  /* Кнопка «☰» и затемнение под выдвижной панелью */
  .nav-burger { display:none; background:none; border:1px solid var(--input-border); color:var(--text);
    font-size:15px; line-height:1; padding:6px 10px; border-radius:8px; cursor:pointer;
    align-items:center; gap:5px; }
  .nav-burger:hover { background:var(--hover-bg); }
  .nav-scrim { display:none; position:fixed; inset:0; z-index:70; background:rgba(12,14,18,.42); }
  /* Красный кружок с числом (как на иконках телефона). Живёт здесь, а не
     в nav_html(): те же бейджи нужны на странице бухгалтера, где меню
     не рисуется вовсе. */
  .nav-badge { display:inline-block; min-width:18px; padding:0 5px; margin-left:2px;
    background:#e5484d; color:#fff; border-radius:9px; font-size:11px; line-height:18px;
    text-align:center; font-weight:700; vertical-align:top; }

  /* --- Кнопка темы --- */
  .theme-toggle { background:none; border:1px solid var(--input-border); color:var(--text);
    border-radius:8px; padding:4px 10px; font-size:13px; cursor:pointer; margin-left:12px; }
  .theme-toggle:hover { background:var(--hover-bg); }
  /* Тема значком в полосе меню (переписка, 25.10): общий вид кнопки выше
     — с рамкой и полями — ей не годится, а вес у правил равный. */
  .nav-tool.theme-toggle { border-color:transparent; padding:0; background:none; color:var(--muted);
    margin-left:0; border-radius:9px; font-size:inherit; }
  .nav-tool.theme-toggle:hover { background:var(--bg); color:var(--link); }

  /* --- Аккордеоны (единый стиль) --- */
  details.accordion { margin-top:10px; }
  details.accordion summary { cursor:pointer; font-weight:600; font-size:14px;
    padding:2px 0 8px; list-style:none; transition:color .15s; }
  details.accordion summary:hover { color:var(--link); }
  details.accordion summary::-webkit-details-marker { display:none; }
  details.accordion summary::before { content:'▸'; display:inline-block; margin-right:6px;
    transition:transform .15s; color:var(--muted); }
  details.accordion[open] summary::before { transform:rotate(90deg); }
  details.accordion table { margin-top:6px; }

  /* --- Справка под знаком вопроса ---
     Длинное объяснение рядом с полем мешает: оно нужно раз в полгода, а
     место занимает всегда. Прячем под маленький «?» у подписи — открыл,
     прочитал, закрыл. Родной <details>, без скрипта: работает и в рамке
     Bitrix, и с клавиатуры, и когда JS фрагмента упал. */
  /* Подпись, рядом с которой стоит «?»: обе части в одну строку, а
     раскрытая справка уходит под них во всю ширину. */
  label.lbl-help { display:inline-block; margin-bottom:0; }
  details.help { display:inline; }
  details.help > summary { display:inline-flex; align-items:center;
    justify-content:center; width:16px; height:16px; margin-left:6px;
    border-radius:50%; background:var(--badge-bg); color:var(--badge-color);
    font-size:11px; font-weight:700; cursor:pointer; user-select:none;
    vertical-align:1px; list-style:none; transition:background .15s; }
  details.help > summary::-webkit-details-marker { display:none; }
  details.help > summary:hover { background:var(--link); color:#fff; }
  details.help[open] > summary { background:var(--link); color:#fff; }
  details.help .help-body { display:block; margin:8px 0 2px; padding:10px 12px;
    background:var(--badge-bg); border-radius:10px; font-size:12.5px;
    line-height:1.5; color:var(--text); }
  details.help .help-body p { margin:0 0 7px; }
  details.help .help-body p:last-child { margin-bottom:0; }
  details.help .help-body dl { margin:0 0 7px; display:grid;
    grid-template-columns:auto 1fr; gap:3px 10px; align-items:baseline; }
  details.help .help-body dt { font-weight:700; white-space:nowrap; }
  details.help .help-body dd { margin:0; }
  @media (max-width: 700px) {
    details.help .help-body dl { grid-template-columns:1fr; gap:1px; }
    details.help .help-body dd { margin:0 0 5px; }
  }

  /* --- Row (сетка строк формы) --- */
  form .row { display:grid; grid-template-columns:1fr 1fr; gap:12px; }
  form .row.row3 { grid-template-columns:1fr 1fr 1fr; }

  /* --- Моноширинный код --- */
  code, .formula { font-family:SFMono-Regular,Consolas,Liberation Mono,Menlo,monospace;
    background:var(--code-bg); border-radius:4px; padding:1px 4px; font-size:13px; }

  /* --- Мобильная версия --- */
  @media (max-width: 640px) {
    header { flex-wrap:wrap; row-gap:8px; padding:12px 16px; }
    header .user { width:100%; }
    main { padding:0 10px; margin-top:16px; }
    .card { padding:14px; }
    .grid3 { grid-template-columns:1fr; gap:10px; margin-bottom:16px; }
    form .row { grid-template-columns:1fr; }
    th, td { padding:8px 6px; font-size:13px; }
    .big { font-size:20px; }
  }

  /* --- Меню на телефоне: выдвижная панель ---
     Порог здесь свой (760, а не 640): шесть разделов в строку перестают
     помещаться раньше, чем ломается остальная вёрстка. */
  @media (max-width: 760px) {
    .nav-bar { padding:6px 10px; }
    .nav-drawer .nav-burger { display:inline-flex; }
    /* На телефоне у «☰» — название текущей страницы: другой шапки там нет. */
    .nav-drawer .nav-here-lbl { display:inline; font-size:14px; font-weight:600; margin-left:2px; }
    .nav-tool { width:40px; height:40px; }
    .nav-tool.nav-exit { width:40px; padding:0; margin:0 8px 0 4px; }
    .nav-exit-lbl { display:none; }
    /* Первый пункт выдвижной панели получает фокус при открытии — без
       клавиатуры подсветка фокуса читалась как второй активный пункт. */
    .nav-drawer .portal-nav a:focus:not(:focus-visible) { background:none; }
    /* Герб на телефоне чуть меньше и жмётся к кнопке меню: место в полосе
       делят ещё кружки и колокольчик. */
    .nav-logo { margin-right:2px; }
    .nav-logo img { height:30px; }
    .nav-drawer .portal-nav { position:fixed; z-index:80; top:0; left:0; bottom:0;
      width:min(84vw, 320px); background:var(--card-bg); border-right:1px solid var(--card-border);
      flex:none; flex-direction:column; flex-wrap:nowrap; align-items:stretch; gap:2px;
      padding:14px 12px calc(14px + env(safe-area-inset-bottom, 0px)); overflow-y:auto;
      transform:translateX(-100%); transition:transform .22s ease-out;
      box-shadow:0 0 40px rgba(0,0,0,.28); }
    .nav-open .nav-drawer .portal-nav { transform:none; }
    /* Затемнение и замок прокрутки — только здесь: на широком экране
       панели нет, и оставленный класс `nav-open` (например, после
       поворота планшета) не должен гасить страницу. */
    .nav-open .nav-scrim { display:block; }
    .nav-open body { overflow:hidden; }
    /* Палец толще курсора — цели крупнее, и подписи читаемого размера. */
    .nav-drawer .portal-nav a, .nav-drawer .nav-group > summary { font-size:15px; padding:11px 12px; }
    .nav-drawer .nav-group { position:static; }
    .nav-drawer .nav-sub { position:static; min-width:0; padding:0 0 6px 10px;
      background:none; border:none; box-shadow:none; }
    .nav-drawer .nav-sub a { font-size:14px; padding:10px 12px; }
  }
  @media (prefers-reduced-motion:reduce) {
    .nav-drawer .portal-nav { transition:none; }
  }

  /* --- Плашка ожидания («часики») — см. busy_overlay_html() ---
     Долгий запрос (выписка банка — 10–20 с) без признака жизни читается
     как «страница зависла». Индикатор — узнаваемый iOS-шный: 12 лучей,
     гаснущих по кругу. Плюс счётчик секунд: видно, что процесс идёт. */
  .busy { position:fixed; inset:0; z-index:9999; display:none; align-items:center; justify-content:center;
    background:rgba(12,14,18,.42); backdrop-filter:blur(3px); -webkit-backdrop-filter:blur(3px); }
  .busy.on { display:flex; animation:busy-fade .18s ease-out; }
  @keyframes busy-fade { from { opacity:0; } to { opacity:1; } }
  .busy-box { background:var(--card-bg); color:var(--text); border:1px solid var(--card-border); border-radius:14px;
    padding:26px 30px; min-width:250px; max-width:min(90vw,380px); text-align:center; box-shadow:0 12px 40px rgba(0,0,0,.30); }
  .busy-title { font-size:15px; font-weight:600; margin-top:16px; }
  .busy-hint { font-size:13px; color:var(--muted); margin-top:7px; line-height:1.45; }
  /* Строка времени — стопкой: «прошло NN с» и сменяющее её «идёт больше
     минуты» лежат в одной ячейке сетки, поэтому подмена одного другим не
     двигает разметку. Это обязательное условие: сдвинуть её на замершей
     странице некому (см. busy_overlay_html()). */
  .busy-elapsed { font-size:12px; line-height:15px; color:var(--muted); margin-top:12px;
    font-variant-numeric:tabular-nums; display:grid; justify-items:center; }
  .busy-elapsed > * { grid-area:1/1; }
  /* Счётчик устроен РОВНО как лучи индикатора: цифры лежат стопкой одна на
     другой и зажигаются прозрачностью, каждая в свою секунду. JavaScript
     тут не при делах — уходящую страницу он рисовать уже не может. Первая
     попытка (столбик цифр, едущий вверх на `transform`) на живом айфоне не
     поехала: считать можно только тем, про что известно, что оно работает,
     а известно это про лучи — они крутятся у владельца на глазах. */
  /* Ряд выравниваем по центру, а не по базовой линии: у ячейки с абсолютно
     позиционированными цифрами базовой линии нет, и цифра всплывала. */
  .be-sec { display:inline-flex; align-items:center; }
  .busy-clock { display:inline-flex; align-items:center; height:15px; }
  .busy-clock .bc-d { position:relative; display:inline-block; width:1ch; height:15px; }
  .busy-clock .bc-d i { position:absolute; left:0; top:0; width:100%; height:15px;
    line-height:15px; text-align:center; font-style:normal; opacity:0; }
  /* ВСЕ задержки здесь ОТРИЦАТЕЛЬНЫЕ — и это не стиль записи, а условие
     работоспособности. Анимация с положительной задержкой ждёт своего часа,
     а «час» наступает по кадру отрисовки — которого на уходящей странице не
     будет: она так и не начнётся. Владелец видел ровно это (10.08): цифра 0
     зажигалась (её анимация стартует сразу), а 1–9 сливались с фоном, и
     через десять секунд снова показывался ноль. Отрицательная задержка
     запускает анимацию НЕМЕДЛЕННО, просто с середины — так же заведены
     двенадцать лучей индикатора, потому они и крутятся.
     Разряд единиц: цикл 10 с, цифра горит свою секунду. */
  .busy-clock .bc-one i { animation:bc-one-on 10s linear infinite; }
  .busy-clock .bc-ten i { animation:bc-ten-on 60s linear infinite; }
  @keyframes bc-one-on { 0%,9.9% { opacity:1; } 10%,100% { opacity:0; } }
  @keyframes bc-ten-on { 0%,16.6% { opacity:1; } 16.7%,100% { opacity:0; } }
  .busy-clock .bc-one i:nth-child(1)  { animation-delay:-10s; }
  .busy-clock .bc-one i:nth-child(2)  { animation-delay:-9s; }
  .busy-clock .bc-one i:nth-child(3)  { animation-delay:-8s; }
  .busy-clock .bc-one i:nth-child(4)  { animation-delay:-7s; }
  .busy-clock .bc-one i:nth-child(5)  { animation-delay:-6s; }
  .busy-clock .bc-one i:nth-child(6)  { animation-delay:-5s; }
  .busy-clock .bc-one i:nth-child(7)  { animation-delay:-4s; }
  .busy-clock .bc-one i:nth-child(8)  { animation-delay:-3s; }
  .busy-clock .bc-one i:nth-child(9)  { animation-delay:-2s; }
  .busy-clock .bc-one i:nth-child(10) { animation-delay:-1s; }
  /* Десятки: цикл 60 с, разряд горит свои десять секунд. До десятой секунды
     разряда нет вовсе — «прошло 7 с», а не «07». */
  .busy-clock .bc-ten i:nth-child(1) { animation-delay:-50s; }
  .busy-clock .bc-ten i:nth-child(2) { animation-delay:-40s; }
  .busy-clock .bc-ten i:nth-child(3) { animation-delay:-30s; }
  .busy-clock .bc-ten i:nth-child(4) { animation-delay:-20s; }
  .busy-clock .bc-ten i:nth-child(5) { animation-delay:-10s; }
  /* Поздние строки — по той же причине без задержек: анимация идёт с первой
     секунды, а нужный момент задан ВНУТРИ раскадровки. Отсчёт 120 с: 40-я
     секунда — это 33,3 %, 60-я — 50 %; дальше состояние держится (forwards).
     На 60-й цифры уступают место словам: дальше они пошли бы по второму
     кругу и врали бы («прошло 5 с» на пятой минуте). */
  .be-sec { animation:busy-sec-out 120s linear forwards; }
  .be-min { opacity:0; animation:busy-min-in 120s linear forwards; }
  @keyframes busy-sec-out { 0%,50% { opacity:1; } 50.3%,100% { opacity:0; } }
  @keyframes busy-min-in  { 0%,50% { opacity:0; } 50.3%,100% { opacity:1; } }
  /* Слова «дольше обычного» проявляются на 40-й секунде тем же способом, и
     место под них занято сразу — по той же причине, что и у строки выше. */
  .busy-long { font-size:12px; color:var(--muted); margin-top:9px; line-height:1.4;
    opacity:0; animation:busy-long-in 120s linear forwards; }
  @keyframes busy-long-in { 0%,33.3% { opacity:0; } 33.6%,100% { opacity:1; } }
  .busy-spin { position:relative; width:32px; height:32px; margin:0 auto; }
  .busy-spin i { position:absolute; left:50%; top:0; width:3px; height:9px; margin-left:-1.5px; border-radius:2px;
    background:var(--text); opacity:.12; transform-origin:50% 16px; animation:busy-spoke 1s linear infinite; }
  @keyframes busy-spoke { 0% { opacity:.95; } 100% { opacity:.12; } }
  .busy-spin i:nth-child(1)  { transform:rotate(0deg);   animation-delay:-1.000s; }
  .busy-spin i:nth-child(2)  { transform:rotate(30deg);  animation-delay:-0.917s; }
  .busy-spin i:nth-child(3)  { transform:rotate(60deg);  animation-delay:-0.833s; }
  .busy-spin i:nth-child(4)  { transform:rotate(90deg);  animation-delay:-0.750s; }
  .busy-spin i:nth-child(5)  { transform:rotate(120deg); animation-delay:-0.667s; }
  .busy-spin i:nth-child(6)  { transform:rotate(150deg); animation-delay:-0.583s; }
  .busy-spin i:nth-child(7)  { transform:rotate(180deg); animation-delay:-0.500s; }
  .busy-spin i:nth-child(8)  { transform:rotate(210deg); animation-delay:-0.417s; }
  .busy-spin i:nth-child(9)  { transform:rotate(240deg); animation-delay:-0.333s; }
  .busy-spin i:nth-child(10) { transform:rotate(270deg); animation-delay:-0.250s; }
  .busy-spin i:nth-child(11) { transform:rotate(300deg); animation-delay:-0.167s; }
  .busy-spin i:nth-child(12) { transform:rotate(330deg); animation-delay:-0.083s; }
  @media (prefers-reduced-motion:reduce) {
    .busy-spin i { animation:none; opacity:.4; }
    .busy-spin { animation:busy-pulse 1.8s ease-in-out infinite; }
    @keyframes busy-pulse { 0%,100% { opacity:1; } 50% { opacity:.35; } }
  }

  /* --- Поле «перетащи файл» — см. file_drop_html() ---
     Штатный <input type="file"> выглядит чужеродно в любой вёрстке и не
     подсказывает, что файл можно бросить мышью. Рамка пунктиром — общий
     для всех систем знак «сюда можно бросить»; форматы подписаны внутри
     курсивом, чтобы не гадать, примут ли скан. Само поле не спрятано
     display:none — иначе теряется фокус с клавиатуры. */
  .drop-wrap { position:relative; }
  /* Крестик «убрать файл» — поверх рамки, но вне <label>: иначе клик по нему
     активирует поле и снова открывает выбор файла. */
  .drop-clear { position:absolute; top:8px; right:8px; z-index:2; width:28px; height:28px; padding:0;
    margin:0; display:flex; align-items:center; justify-content:center; font-size:14px; line-height:1;
    border-radius:50%; border:1px solid var(--card-border); background:var(--card-bg); color:var(--muted);
    cursor:pointer; box-shadow:0 1px 3px rgba(16,24,40,.10); }
  .drop-clear:hover { color:var(--neg); border-color:var(--danger-border); background:var(--danger-hover); }
  /* display:flex выше перебивает UA-правило [hidden]{display:none} — иначе
     крестик остаётся висеть на пустой рамке (поймано скриншотом). */
  .drop-clear[hidden] { display:none; }
  /* color/font-size — явно: рамка это <label>, а он в портале мелкий и серый. */
  .drop { position:relative; display:flex; flex-direction:column; align-items:center; justify-content:center;
    gap:6px; text-align:center; padding:22px 18px; margin-bottom:12px; cursor:pointer;
    color:var(--text); font-size:14px;
    border:2px dashed var(--input-border); border-radius:14px; background:var(--input-bg);
    transition:border-color .15s, background .15s, transform .15s; }
  .drop input[type="file"] { position:absolute; inset:0; width:100%; height:100%; opacity:0; cursor:pointer; }
  @media (hover:hover) { .drop:hover { border-color:var(--link); background:var(--hover-bg); } }
  .drop:focus-within { border-color:var(--link); box-shadow:0 0 0 4px var(--focus-ring); }
  .drop.over { border-color:var(--link); background:var(--hover-bg); border-style:solid; transform:scale(1.01); }
  .drop.filled { border-style:solid; border-color:var(--pos); background:var(--msg-bg); }
  .drop.bad { border-color:var(--neg); background:var(--err-bg); }
  .drop-icon { font-size:26px; line-height:1; }
  .drop.over .drop-icon, .drop.filled .drop-icon { transform:translateY(-2px); }
  .drop-title { font-size:14px; font-weight:600; }
  .drop-title .drop-or { font-weight:400; color:var(--muted); }
  .drop-formats { font-size:12px; font-style:italic; color:var(--muted); line-height:1.4; }
  /* Выбранное — ПЛАШКОЙ НА КАЖДЫЙ файл и вне <label> (09.09). Одна строка
     «имя и ещё 4» не давала снять лишний файл: приходилось сбрасывать всю
     пачку и выбирать заново. Крестик у плашки внутри label открывал бы
     выбор файла — та же грабля, что у .drop-clear выше. */
  .drop-chosen { display:flex; flex-wrap:wrap; gap:6px; margin:-6px 0 10px; }
  .drop-chosen[hidden] { display:none; }
  .dchip { display:inline-flex; align-items:center; gap:6px; max-width:100%;
    padding:4px 6px 4px 9px; border-radius:999px; font-size:12.5px; line-height:1.5;
    background:var(--zebra); border:1px solid var(--border); color:var(--text); }
  .dchip .dn { overflow:hidden; text-overflow:ellipsis; white-space:nowrap; max-width:190px; }
  .dchip .ds { color:var(--muted); }
  .dchip .dx { background:none; border:0; cursor:pointer; color:var(--muted);
    font-size:13px; line-height:1; padding:2px 4px; border-radius:50%; }
  .dchip .dx:hover { color:var(--neg); background:var(--danger-hover); }
  .drop-err { font-size:12px; color:var(--neg); font-weight:600; margin-bottom:10px; }
  .drop-err[hidden] { display:none; }

  /* Узкая рамка (`'compact' => true`): одна строка вместо коробки. Нужна
     там, где приём файлов — не главное действие экрана, а приписка к полю
     ввода: в поле записи заметки большая коробка перетянула бы всё на себя. */
  .drop.compact { flex-direction:row; gap:9px; padding:9px 14px; margin-bottom:8px;
    border-width:1px; border-radius:999px; text-align:left; }
  .drop.compact .drop-icon { font-size:16px; }
  .drop.compact .drop-title { font-size:13px; font-weight:500; }
  .drop.compact .drop-formats { display:none; }
  .drop[data-disabled] { opacity:.55; cursor:not-allowed; }
  .drop[data-disabled] input[type="file"] { cursor:not-allowed; }
  @media (max-width: 640px) { .drop { padding:16px 12px; } .drop-title { font-size:13px; } }
  @media (prefers-reduced-motion:reduce) { .drop, .drop-icon { transition:none; transform:none; } }

  /* Строка настройки с тумблером — как на телефоне (просьба владельца
     13.08 про уведомления). САМ переключатель здесь не переопределяется:
     он уже есть выше (`.sw` + `.sw-slot`), и второго вида тумблера в
     портале быть не должно. Новое только раскладка: подпись с объяснением
     слева, переключатель справа, вся строка — один <label>, чтобы на
     телефоне попадать в неё пальцем, а не в ползунок. */
  .sw-row { display:flex; align-items:flex-start; gap:14px; padding:10px 0;
    border-top:1px solid var(--border); cursor:pointer; }
  .sw-row:first-of-type { border-top:none; }
  .sw-text { flex:1; min-width:0; }
  /* display:block обеим строкам: <span> строчный, и подпись с объяснением
     слипались в один абзац («Сообщения в перепискеНовое сообщение в…») —
     видно только на снимке экрана, разметка при этом безупречна. */
  .sw-title { display:block; font-size:14px; font-weight:600; }
  .sw-hint { display:block; font-size:12px; color:var(--muted); line-height:1.45; margin-top:3px; }
  .sw-row .sw { flex:none; }
  .sw input:disabled + .sw-slot { opacity:.3; }
  .sw input:disabled { cursor:not-allowed; }
  .sw-row[data-off] .sw-title { color:var(--muted); }