/* Тема веб-интерфейса. Палитра, типографика и плотность — дизайн-токены пакета
   от 22.08 (handoff/itset-control-system-server/INTEGRATION.md).

   Три вещи, о которых стоит знать, прежде чем что-то править:

   1. Цвет задаётся ТОЛЬКО переменной. Исключений три, и они намеренные:
      белая подложка под QR (в тёмной теме QR обязан остаться белым, иначе
      не считается), чёрный фон video и палитра предпросмотра «как на планшете»
      в admin_marketing_edit.html — там имитируется чужой экран, а не наша тема.
   2. Акцент клиента приходит из брендинга: --acc, --acc-ink, --acc-a12,
      --acc-a60, --brand, --link подставляет base.html из app/branding.py —
      и в :root, и в :root[data-theme="dark"]. Здесь их нет даже запасными:
      иначе запасное значение молча пережило бы поломку брендинга.
   3. Тема и плотность — атрибуты data-theme / data-density на <html>, сервер
      ставит их из cookie при рендере (app/web.py:render), чтобы страница
      не мигала светлым до срабатывания скрипта.

   Вёрстка страниц пока прежняя (верхнее меню); боковая панель, шапка раздела
   и таблицы дизайна приезжают следующими этапами. */

/* ── Шрифты (static/fonts, лицензии OFL — см. LICENSES и «О программе») ──
   Переменные начертания: одна ширина на все веса, отдельных файлов на 500/700
   не нужно. Подмножества разнесены по unicode-range: кириллица тянется только
   когда нужна кириллица. */
@font-face {
  font-family:'Golos Text'; font-style:normal; font-weight:400 900; font-display:swap;
  src:url(/static/fonts/golos-text-cyrillic.woff2) format('woff2');
  /* Казахские буквы (Ә Ғ Қ Ң Ө Ұ Ү Һ) — в основной подгруппе: установка
     казахстанская, и типичная фамилия должна рисоваться одним файлом */
  unicode-range:U+0301,U+0400-045F,U+0490-0493,U+049A-049B,U+04A2-04A3,
    U+04AE-04B1,U+04BA-04BB,U+04D8-04D9,U+04E8-04E9,U+2116;
}
@font-face {
  font-family:'Golos Text'; font-style:normal; font-weight:400 900; font-display:swap;
  src:url(/static/fonts/golos-text-cyrillic-ext.woff2) format('woff2');
  /* Казахские позиции вырезаны: при пересечении диапазонов побеждает
     последний @font-face, и они снова тянули бы этот файл */
  unicode-range:U+0460-048F,U+0494-0499,U+049C-04A1,U+04A4-04AD,U+04B2-04B9,
    U+04BC-04D7,U+04DA-04E7,U+04EA-052F,U+1C80-1C8A,U+20B4,U+2DE0-2DFF,
    U+A640-A69F,U+FE2E-FE2F;
}
@font-face {
  font-family:'Golos Text'; font-style:normal; font-weight:400 900; font-display:swap;
  src:url(/static/fonts/golos-text-latin.woff2) format('woff2');
  unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,
    U+0329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD;
}
@font-face {
  font-family:'Golos Text'; font-style:normal; font-weight:400 900; font-display:swap;
  src:url(/static/fonts/golos-text-latin-ext.woff2) format('woff2');
  unicode-range:U+0100-02BA,U+02BD-02C5,U+02C7-02CC,U+02CE-02D7,U+02DD-02FF,U+0304,U+0308,
    U+0329,U+1D00-1DBF,U+1E00-1E9F,U+1EF2-1EFF,U+2020,U+20A0-20AB,U+20AD-20C0,U+2113,
    U+2C60-2C7F,U+A720-A7FF;
}
@font-face {
  font-family:'JetBrains Mono'; font-style:normal; font-weight:400 700; font-display:swap;
  src:url(/static/fonts/jetbrains-mono-cyrillic.woff2) format('woff2');
  unicode-range:U+0301,U+0400-045F,U+0490-0491,U+04B0-04B1,U+2116;
}
@font-face {
  font-family:'JetBrains Mono'; font-style:normal; font-weight:400 700; font-display:swap;
  src:url(/static/fonts/jetbrains-mono-cyrillic-ext.woff2) format('woff2');
  unicode-range:U+0460-052F,U+1C80-1C8A,U+20B4,U+2DE0-2DFF,U+A640-A69F,U+FE2E-FE2F;
}
@font-face {
  font-family:'JetBrains Mono'; font-style:normal; font-weight:400 700; font-display:swap;
  src:url(/static/fonts/jetbrains-mono-latin.woff2) format('woff2');
  unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,
    U+0329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD;
}
@font-face {
  font-family:'JetBrains Mono'; font-style:normal; font-weight:400 700; font-display:swap;
  src:url(/static/fonts/jetbrains-mono-latin-ext.woff2) format('woff2');
  unicode-range:U+0100-02BA,U+02BD-02C5,U+02C7-02CC,U+02CE-02D7,U+02DD-02FF,U+0304,U+0308,
    U+0329,U+1D00-1DBF,U+1E00-1E9F,U+1EF2-1EFF,U+2020,U+20A0-20AB,U+20AD-20C0,U+2113,
    U+2C60-2C7F,U+A720-A7FF;
}

/* ── Токены: светлая тема ── */
:root {
  --paper:#ffffff;       /* поле контента, строки таблиц */
  --bg:#f2f2f4;          /* фон окна (боковая панель этапа 2) и наведение */
  --surface:#f7f7f8;     /* панели, карточки, плитки, шапки таблиц */
  --line:#ededef;        /* разделители строк и тонкие рамки */
  --outline:#c9c9ce;     /* границы контролов и блоков */
  --ink:#1a1a1a;         /* основной текст */
  --ink2:#3a3a3e;        /* вторичный текст */
  --muted:#6b6b70;       /* подписи, моно-метки; >=4.5:1 на всех поверхностях (AA) */
  --muted2:#5a5a5f;      /* приглушённый текст контролов; темнее --muted */
  --hov:#f2f2f4;         /* наведение на нейтральные поверхности */
  --outline-hi:#b4b4bb;  /* граница контрола под курсором */

  --ok:#0f7a3f;          /* только успех и «онлайн»; на белом #1fa85a давал 2.9:1 */
  --ok-bg:#eef8f2;       /* поверхность удачной операции */
  --ok-a12:rgba(31,168,90,.12);
  --err:#b62a2a;         /* только ошибки, офлайн и необратимые действия */
  --err-bg:#fdf0f0;      /* поверхность ошибки */
  --err-a12:rgba(182,42,42,.12);
  --err-row:rgba(182,42,42,.07);   /* строка офлайна в мониторинге */
  --err-line:#eeb9b9;    /* контур обратимого удаления */
  --ok-line:#a8d8bd;     /* контур утверждающего действия */
  --on-err:#ffffff;      /* текст на красной заливке */
  --warn-bg:#faf2e2;     /* строка разряда в мониторинге (легенда «жёлтым») */
  --warn-ink:#7a5b00;    /* текст на жёлтом */

  --sign-frame:#f5c842;  /* скобки знака IT-Set: цвет продукта, не клиента */

  /* Модальное окно: подложка гасит страницу, тень отрывает окно от неё */
  --scrim:rgba(0,0,0,.45);
  --scrim-hi:rgba(0,0,0,.75);
  --shadow:rgba(0,0,0,.28);

  --knob:#ffffff;        /* кружок тумблера */
  /* Плашка «Не сохранено» — инверсия темы: тёмная на светлом, светлая на тёмном */
  --panel:#1a1a1a; --panel-ink:#ffffff; --panel-mut:rgba(255,255,255,.62);
  --panel-line:rgba(255,255,255,.35); --panel-hov:rgba(255,255,255,.12);

  --font:'Golos Text',Roboto,-apple-system,'Segoe UI',sans-serif;
  --mono:'JetBrains Mono',ui-monospace,SFMono-Regular,Menlo,monospace;

  /* Плотность: базовый режим.
     Кроме кегля текста и строки таблицы, плотность обязана менять и САМИ
     контролы — поля, кнопки, чипы, кнопки страниц. Раньше их размеры стояли
     числами прямо в правилах, и «Плотный»/«Крупный» двигали текст и строки,
     а панель с полем поиска оставалась одна и та же: масштабирование
     выглядело наполовину сломанным. Теперь размеры контролов — тоже токены,
     и все три режима задают их у себя. Числа базового режима — из макета. */
  --fs:14px;             /* базовый кегль интерфейса */
  --navrow:40px;         /* высота строки навигации (панель этапа 2) */
  --navw:232px;          /* ширина боковой панели (этап 2) */
  --rowpad:11px;         /* вертикальный паддинг строки таблицы */
  --ctl:34px;            /* высота контрола: поле, кнопка, чип, кнопка страницы */
  --ctlpy:6px;           /* вертикальный отступ внутри контрола */
  --ctlpx:11px;          /* горизонтальный отступ внутри контрола */
  --fsc:13px;            /* кегль контрола */
  --fsm:12px;            /* кегль мелкой подписи: «Строк», счётчик, метки */
}

/* ── Тёмная тема ──
   Три состояния: «светлая», «тёмная» и «как в системе». Явный выбор сервер
   ставит атрибутом data-theme, «как в системе» — это отсутствие атрибута,
   и тогда решает prefers-color-scheme. Отсюда два одинаковых блока токенов:
   объединить их в один селектор нельзя — медиазапрос не смешивается с обычным
   правилом. Расхождение между копиями стережёт тест
   test_обе_записи_тёмной_темы_совпадают. */
:root[data-theme="dark"] {
  --paper:#1a1a1d; --bg:#101012; --surface:#232327; --line:#2e2e33; --outline:#3d3d44;
  --ink:#f4f4f6; --ink2:#dcdce0; --muted:#9a9aa2; --muted2:#a6a6ae;
  --hov:#2a2a2f; --outline-hi:#55555e;

  --ok-bg:#14261c; --ok-a12:rgba(31,168,90,.20);
  --err-bg:#2a1414; --err-a12:rgba(182,42,42,.28);
  /* на тёмном 7% заливки не видно — легенда «красным» перестала бы читаться */
  --err-row:rgba(182,42,42,.20); --err-line:#7a3232; --ok-line:#2f6b4a;
  --warn-bg:#2c2413; --warn-ink:#e8c86a;
  /* акценты состояний общие для тем ТОЛЬКО по светлоте фона: на тёмном
     #b62a2a даёт 2.1:1 в бейдже — осветляем, иначе ошибки не читаются */
  --ok:#4ec27f; --err:#f08a8a;
  /* Заливка кнопки необратимого действия стала светло-розовой — белый
     текст на ней даёт 2.4:1. Текст на красном тут тёмный, иначе
     «Стереть навсегда» не читается ровно там, где читать обязательно. */
  --on-err:#1a1a1d;

  /* на тёмном подложка гуще, а тень заметна только сильно чёрная */
  --scrim:rgba(0,0,0,.62); --scrim-hi:rgba(0,0,0,.82); --shadow:rgba(0,0,0,.55);
  --sign-frame:#f5c842;

  --knob:#f4f4f6;
  --panel:#f4f4f6; --panel-ink:#1a1a1d; --panel-mut:rgba(26,26,29,.62);
  --panel-line:rgba(26,26,29,.35); --panel-hov:rgba(26,26,29,.12);
}
@media (prefers-color-scheme: dark) {
  /* :not([data-theme="light"]) — явно выбранная светлая сильнее системной тёмной */
  :root:not([data-theme="light"]) {
    --paper:#1a1a1d; --bg:#101012; --surface:#232327; --line:#2e2e33; --outline:#3d3d44;
    --ink:#f4f4f6; --ink2:#dcdce0; --muted:#9a9aa2; --muted2:#a6a6ae;
    --hov:#2a2a2f; --outline-hi:#55555e;

    --ok-bg:#14261c; --ok-a12:rgba(31,168,90,.20);
    --err-bg:#2a1414; --err-a12:rgba(182,42,42,.28);
    /* на тёмном 7% заливки не видно — легенда «красным» перестала бы читаться */
    --err-row:rgba(182,42,42,.20); --err-line:#7a3232; --ok-line:#2f6b4a;
    --warn-bg:#2c2413; --warn-ink:#e8c86a;
    --ok:#4ec27f; --err:#f08a8a;
    --on-err:#1a1a1d;

    /* на тёмном подложка гуще, а тень заметна только сильно чёрная */
    --scrim:rgba(0,0,0,.62); --scrim-hi:rgba(0,0,0,.82); --shadow:rgba(0,0,0,.55);
    --sign-frame:#f5c842;

    --knob:#f4f4f6;
    --panel:#f4f4f6; --panel-ink:#1a1a1d; --panel-mut:rgba(26,26,29,.62);
    --panel-line:rgba(26,26,29,.35); --panel-hov:rgba(26,26,29,.12);
  }
}

/* ── Плотность ── */
:root[data-density="compact"] { --fs:13px; --navrow:34px; --navw:224px; --rowpad:8px;
  --ctl:30px; --ctlpy:4px; --ctlpx:9px; --fsc:12px; --fsm:11px; }
:root[data-density="large"]   { --fs:16px; --navrow:46px; --navw:256px; --rowpad:15px;
  --ctl:38px; --ctlpy:8px; --ctlpx:13px; --fsc:15px; --fsm:13px; }

* { box-sizing:border-box; }
html { scrollbar-color:var(--outline) var(--paper); }
body {
  margin:0; font-family:var(--font); font-size:var(--fs); line-height:1.55;
  color:var(--ink); background:var(--paper); min-height:100vh;
}
a { color:var(--link); text-decoration:none; }
a:hover { text-decoration:underline; text-underline-offset:3px; }

/* ── Каркас: боковая панель + рабочая область ──
   Окно не прокручивается целиком: панель стоит на месте, прокручивается только
   содержимое. Иначе при длинной таблице уезжали бы и разделы, и шапка. */
.shell { display:flex; height:100vh; overflow:hidden; }
.work { flex:1; min-width:0; display:flex; flex-direction:column; overflow:hidden; }
.work > main { flex:1; overflow:auto; padding:20px 28px 40px; background:var(--bg);
  scroll-behavior:smooth; }   /* прокручивается она, а не html — иначе якоря резкие */
main.plain { max-width:1120px; margin:0 auto; padding:26px 18px 60px; }

/* Панель */
.side { width:var(--navw); flex:none; display:flex; flex-direction:column;
  background:var(--surface); border-right:1px solid var(--outline); overflow:hidden; }
/* Шапка панели читается двумя ярусами. Верхний — НАШ продукт: знак и название
   одной строкой, знак по центру против двух строк названия. Нижний — КЛИЕНТ:
   его логотип или имя из настроек, во всю ширину и за разделительной линией,
   чтобы чужое имя не читалось продолжением нашего. */
.side-h { display:block; padding:14px 20px;
  background:var(--paper); border-bottom:1px solid var(--outline); color:var(--ink); }
.side-h:hover { text-decoration:none; }
.side-h-p { display:flex; align-items:center; gap:10px; }
.side-h-t { min-width:0; display:flex; flex-direction:column; line-height:1.15; }
.side-h-t b { font-size:calc(var(--fs) + 1px); font-weight:700; letter-spacing:-.01em; }
.side-h-s { font-weight:600; font-size:9px; letter-spacing:.16em; color:var(--muted); }  /* кегль намеренно фиксированный */
.side-h-b { display:block; margin-top:10px; padding-top:9px;
  border-top:1px solid var(--line); }
.side-h-c { display:block; font-size:calc(var(--fs) - 3px); color:var(--muted2);
  overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
.side-h-logo { max-height:20px; max-width:100%; object-fit:contain;
  object-position:left; display:block; }
/* Знак продукта: скобки в цвете IT-Set, галочка цветом текста. Он фиксирован
   и НЕ связан с логотипом клиента из настроек — тот стоит третьей строкой
   и меняется вместе с брендингом, а знак продукта одинаков у всех. */
.sign { width:30px; height:30px; flex:none; }

/* Монограмма продукта — знак в акценте клиента, когда логотипа нет.
   Класс исторический и означает именно монограмму: моноширинный шрифт
   в новых блоках задаётся через var(--mono), общего утилитарного класса нет. */
.mono { display:inline-flex; align-items:center; justify-content:center;
  background:var(--acc); color:var(--acc-ink);
  font-weight:800; letter-spacing:.5px; flex:none; }
.mono.big { width:56px; height:56px; font-size:calc(var(--fs) + 7px); }

/* Разделы */
.side-nav { display:flex; flex-direction:column; overflow-y:auto; padding-top:4px; }
.side-i { display:flex; align-items:center; gap:10px; min-height:var(--navrow);
  /* полосу рисуем ВНУТРИ панели: вынос через margin-left:-3px срезался
     overflow:hidden у .side, и активный пункт оставался без метки */
  padding:0 12px 0 20px; border-left:3px solid transparent;
  font-size:calc(var(--fs) - 1px); color:var(--muted2); font-weight:400;
  white-space:nowrap; }
.side-i:hover { background:var(--hov); text-decoration:none; }
.side-i .ic { flex:none; }
.side-i-t { flex:1; min-width:0; overflow:hidden; text-overflow:ellipsis; }
.side-i.on { background:var(--paper); border-left-color:var(--acc);
  padding-left:17px; color:var(--ink); font-weight:700; }

/* Низ панели: вид интерфейса и пользователь */
.side-b { margin-top:auto; border-top:1px solid var(--line); padding:12px 16px 14px; }
/* Одинокая ссылка внизу панели: тихий серый вместо цвета ссылки */
.side-b > a { font-size:var(--fsm); color:var(--muted2); }
.side-b > a:hover { color:var(--ink); }
.prefs { display:flex; flex-wrap:wrap; gap:2px; }
.prefs button { flex:1; min-width:0; padding:5px 6px; font-size:calc(var(--fs) - 3px); font-weight:600;
  border:1px solid var(--line);
  background:var(--paper); color:var(--muted2); }
.prefs button:hover { background:var(--hov); opacity:1; }
.prefs button[aria-pressed="true"] { background:var(--ink); color:var(--paper);
  border-color:var(--ink); }

/* ── Шапка страницы ── */
.page { display:flex; align-items:center; gap:16px; flex-wrap:wrap;
  padding:16px 28px; background:var(--paper); border-bottom:1px solid var(--line); }
.page-t { display:flex; align-items:center; gap:14px; flex:1; min-width:0; }
.page-ic { width:36px; height:36px; flex:none; border:1px solid var(--line);
  display:flex; align-items:center; justify-content:center; color:var(--ink2); }
/* «О программе» разделом не является — пустая рамка вместо значка не нужна */
.page-ic:empty { display:none; }
.page-tt { min-width:0; }
.page h1 { font-size:calc(var(--fs) + 7px); font-weight:800; letter-spacing:-.01em; margin:0;
  line-height:1.2; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
.page-meta { font-weight:600; font-size:10px; letter-spacing:.12em; color:var(--muted);  /* кегль намеренно фиксированный */
  text-transform:uppercase; margin-top:3px; }
.page-meta:empty { display:none; }
.page-act { display:flex; gap:8px; align-items:center; }
/* Шапка (C2, все страницы): счётчик — пилюлей в строке заголовка,
   пояснение — одной строкой с многоточием; полный текст — окно #note-dlg.
   Строка пояснения резервируется и пустой — шапка одной высоты везде,
   и полоса вкладок не прыгает. */
.page-h1r { display:flex; align-items:center; gap:10px; min-width:0; }
.page-h1r h1 { min-width:0; }
.page-pill { margin-top:0; padding:3px 11px; flex:none;
  border:1px solid var(--line); border-radius:999px; background:var(--surface); }
.page-note-l { display:flex; align-items:center; gap:6px; margin:3px 0 0;
  max-width:100%; min-width:0; padding:0; background:transparent; border:0;
  min-height:calc(1.4 * (var(--fs) - 1.5px)); color:var(--muted);
  font-weight:400; font-size:calc(var(--fs) - 1.5px); line-height:1.4;
  text-align:left; }
button.page-note-l:hover { color:var(--ink); background:transparent; opacity:1; }
.page-note-t { min-width:0; overflow:hidden; text-overflow:ellipsis; white-space:nowrap;
  max-width:41em;   /* ~75 знаков: длиннее строка не читается, полный текст — в окне */ }
.page-note-i { flex:none; }
/* Учётная область: «Вид», разделители, имя с ролью, «Выход» (окно #view-dlg) */
.page-u { display:flex; align-items:center; gap:12px; flex:none; }
.page-u-d { width:1px; align-self:stretch; background:var(--line); flex:none; }
.page-u-n { display:flex; flex-direction:column; line-height:1.25; min-width:0;
  font-size:var(--fsc); font-weight:600; color:var(--ink); }
.page-u-r { font-size:var(--fsm); font-weight:400; color:var(--muted2); }
/* «Вид» — тихая кнопка: рамки нет, подсветка только при наведении */
.page-u .btn-view { display:inline-flex; align-items:center; gap:7px;
  border-color:transparent; background:transparent; }
.page-u .btn-view:hover { background:var(--hov); border-color:transparent; opacity:1; }

/* Вкладки раздела: страницы, которым в макете не нашлось своего пункта */
.tabs { display:flex; gap:2px; padding:0 28px; background:var(--surface);
  border-bottom:1px solid var(--line); }
.tab { padding:9px 14px; font-size:calc(var(--fs) - 1px); color:var(--muted2);
  border-bottom:2px solid transparent; margin-bottom:-1px; }
.tab:hover { color:var(--ink); text-decoration:none; }
.tab.on { color:var(--ink); font-weight:700; border-bottom-color:var(--ink);
  background:var(--paper); }

/* ── Карточки ── */
.card {
  background:var(--paper); border:1px solid var(--line);
  padding:20px 28px; margin-bottom:16px;
  overflow-x:auto;                     /* широкие таблицы прокручиваются внутри карточки */
}
.card.good { background:var(--ok-bg); }           /* выпущен токен, всё удалось */
.card.bad { background:var(--err-bg); }           /* ошибка операции */

h1,h2,h3 { margin-top:0; color:var(--ink); letter-spacing:-.01em; }
h1 { font-size:calc(var(--fs) + 12px); font-weight:800; line-height:1.2; margin-bottom:10px; }
h2 { font-size:calc(var(--fs) + 5px); font-weight:700; margin-bottom:10px; }
h3 { font-size:calc(var(--fs) + 2px); font-weight:700; margin-bottom:8px; }
.h-note { font-size:var(--fs); font-weight:400; }      /* приписка в заголовке */

/* ── Формы ── */
/* :where(), а не :not() напрямую. Вес :not() равен весу его аргумента, и
   input:not([type=checkbox]):not([type=radio]) весит (0,2,1) — больше, чем
   .tsearch input (0,1,1). Из-за этого рамка базового поля перебивала «border:0»
   компонентов, и поле поиска рисовалось в рамке ВНУТРИ рамки обёртки; то же
   было у поля даты в чипе и у поля срока хранения (его padding 5px 8px тоже
   не действовал). :where() обнуляет вес исключений: правило остаётся тем же,
   но весит (0,0,1) и уступает любому компоненту, как и задумано. */
input:where(:not([type=checkbox]):not([type=radio])),select,textarea {
  font:inherit; padding:calc(var(--ctlpy) + 1px) var(--ctlpx);
  border:1px solid var(--outline); background:var(--paper); color:var(--ink);
  transition:border-color .16s;
  /* Одна высота у поля, даты, выпадашки и кнопки — по всему интерфейсу, а не
     только в панели списка. Высота едет вместе с режимом просмотра: --ctl
     задан у каждой плотности. Флажки и переключатели исключены — у них своя
     квадратная геометрия, а textarea растёт от строк, для неё это только
     нижняя граница. */
  min-height:var(--ctl); box-sizing:border-box;
}
input:focus,select:focus,textarea:focus {
  outline:2px solid var(--acc); outline-offset:1px; border-color:var(--acc); }
/* Одно кольцо фокуса на все нажимаемые вещи. У поля поиска собственная обводка
   снята (рамку рисует обёртка), у лупы и кнопок-значков её не было вовсе —
   с клавиатуры было не понять, где ты находишься. :focus-visible, а не :focus:
   кольцо нужно тому, кто идёт табом, и не нужно на щелчок мышью. */
a:focus-visible, button:focus-visible, .btn:focus-visible, summary:focus-visible,
[tabindex]:focus-visible { outline:2px solid var(--acc); outline-offset:2px; }
/* Кольцо внутрь: у .side стоит overflow:hidden, вынесенное наружу срезается */
.side-i:focus-visible { outline-offset:-2px; }
/* Видна только с клавиатуры: мышью на неё не попасть, и она не должна мешать */
.skip { position:absolute; left:-9999px; z-index:100; }
.skip:focus { left:8px; top:8px; padding:10px 14px; background:var(--paper);
  border:1px solid var(--acc); color:var(--ink); }
.tsearch:focus-within { outline:2px solid var(--acc); outline-offset:1px; }
input::placeholder { color:var(--muted); }
label { display:block; margin:12px 0 5px; color:var(--muted); font-size:var(--fsc); font-weight:500; }
/* Заголовок группы без поля (состояние + кнопка): выглядит как label,
   но им не является — щёлкать не по чему и связывать не с чем */
.fld-h { display:block; margin:12px 0 5px; color:var(--muted); font-size:var(--fsc); font-weight:500; }

/* Кнопки: основная — заливка акцентом, вторичная — контур, опасная — красная */
button,.btn {
  background:var(--acc); color:var(--acc-ink);
  border:1px solid var(--acc); cursor:pointer;
  padding:calc(var(--ctlpy) + 2px) calc(var(--ctlpx) + 5px);
  min-height:var(--ctl); box-sizing:border-box;
  font:inherit; font-weight:700; line-height:1.35;
  transition:opacity .16s, background-color .16s, border-color .16s, color .16s;
}
button:hover,.btn:hover { opacity:.88; text-decoration:none; }
button.sec,.btn.sec { background:var(--paper); color:var(--ink); border-color:var(--outline); }
button.sec:hover,.btn.sec:hover { opacity:1; background:var(--hov); border-color:var(--outline-hi); }
button.danger,.btn.danger { background:var(--err); color:var(--on-err); border-color:transparent; }
/* Утверждающее действие рядом с опасным: «Проверено» и «Ложное»,
   «Восстановить» и «Стереть». Цвет — не единственное различие (слова стоят
   рядом и остаются главными), но рядом стоящую пару он разводит с одного
   взгляда. Опасное — ниже, у .danger. */
button.sec.ok,.btn.sec.ok { background:transparent; color:var(--ok);
  border-color:var(--ok-line); }
button.sec.ok:hover,.btn.sec.ok:hover { background:var(--ok-a12); border-color:var(--ok); }

/* Обратимое удаление и отзыв — красный только в контуре, поверхность не красим */
button.sec.danger,.btn.sec.danger { background:transparent; color:var(--err); border-color:var(--err-line); }
button.sec.danger:hover,.btn.sec.danger:hover { background:var(--err-a12); border-color:var(--err); }
button:disabled { opacity:.5; cursor:not-allowed; }
a.btn { display:inline-block; }

/* ── Таблицы ── */
table { width:100%; border-collapse:collapse; }
th,td { text-align:left; padding:var(--rowpad) 14px; border-bottom:1px solid var(--line); }
th { color:var(--muted); font-weight:600; font-size:var(--fsc); }
tbody tr, table tr { transition:background-color .16s; }
tr:hover td { background:var(--hov); }
tr:last-child td, tr:last-child th { border-bottom:none; }

.row { display:flex; gap:16px; flex-wrap:wrap; }
.row > div, .row > form { flex:1; min-width:160px; }   /* форма-колонка (страница группы) — как div */

/* ── Список отделений: поиск, режимы, дерево групп ── */
.bar { display:flex; gap:10px; flex-wrap:wrap; align-items:center; margin:10px 0 4px; }
.bar .find { display:flex; gap:8px; flex:1; min-width:260px; }
.bar .find input { flex:1; min-width:180px; }
/* Сегменты режима — как переключатель вида в учётных программах */
.seg { display:inline-flex; gap:2px; padding:3px; margin-top:12px;
  background:var(--surface); border:1px solid var(--line); }
.seg a { padding:6px 14px; color:var(--ink); font-size:var(--fs); font-weight:500; }
.seg a:hover { background:var(--hov); text-decoration:none; }
.seg a.on { background:var(--acc-a12); color:var(--brand); }
/* Строка группы в таблице: заливка светлее строк отделений */
tr.grp td { background:var(--surface); font-weight:600; }   /* группа темнее строк */
tr.grp:hover td { background:var(--hov); }
.grp-n { display:inline-flex; align-items:center; gap:9px; flex-wrap:wrap; }
.grp-n .mut { font-weight:400; font-size:var(--fsc); }
.fold { width:22px; height:22px; padding:0; flex:none; font-size:var(--fsm); line-height:1;
  background:transparent; color:var(--muted); border:1px solid var(--line); }
.fold:hover { background:var(--hov); opacity:1; }
.fold.empty { border-color:transparent; background:none; display:inline-block; }
.sub { color:var(--muted); font-size:calc(var(--fs) - 1.5px); margin-top:2px; }
.crumbs { color:var(--muted); font-size:calc(var(--fs) - 0.5px); }

/* ── Бейджи статусов: зелёный — успех, красный — ошибка, остальное серое ── */
.badge { display:inline-flex; align-items:center; gap:6px;
  padding:6px 9px; font-size:var(--fsm); font-weight:600; line-height:1;
  background:var(--line); color:var(--muted2); }   /* без модификатора — нейтральный серый */
.badge::before { content:""; width:6px; height:6px; background:currentColor; }
.st-ready { background:var(--ok-a12); color:var(--ok); }
.st-error { background:var(--err-a12); color:var(--err); }
.st-processing { background:var(--line); color:var(--muted2); }
.st-uploading { background:var(--line); color:var(--muted2); }

/* ── Медиа ── */
/* Чёрный фон плеера — намеренный хардкод: это поле кадра, а не поверхность темы */
video { width:100%; background:#000; border:1px solid var(--line); }
.frames { display:flex; gap:14px; flex-wrap:wrap; }
.frame { text-align:center; }
.frame img { width:220px; border:1px solid var(--line);
  transition:border-color .16s; }
.frame a:hover img { border-color:var(--acc); }
.thumb { width:76px; height:auto; display:block;
  border:1px solid var(--line); transition:border-color .16s; }
td a:hover .thumb { border-color:var(--acc); }
.shot { max-width:260px; border:1px solid var(--line); display:block; }
.shot.sm { max-width:200px; }
/* Белая подложка QR — намеренный хардкод: в тёмной теме код обязан остаться
   чёрным по белому, иначе камера телефона его не прочитает */
.qr { width:200px; background:#fff; padding:8px; border:1px solid var(--line); }

.err { color:var(--err); } .mut { color:var(--muted); } .ok { color:var(--ok); }
.sp { flex:1; }                    /* распорка во флекс-строке */
/* Спокойное предупреждение: не дефект записи, а пояснение. Красный тут врал бы. */
.note-warn { color:var(--warn-ink); background:var(--warn-bg);
  padding:8px 12px; margin:6px 0; }
tr.row-offline td { background:var(--err-row); }
tr.row-lowbatt td { background:var(--warn-bg); }
code { background:var(--surface); border:1px solid var(--line); padding:2px 6px;
  color:var(--ink);
  font-family:var(--mono); font-size:.9em; }
code.token { font-size:calc(var(--fs) + 1px); }

/* ── Логин и 2FA ── */
.auth { max-width:380px; margin:9vh auto 0; text-align:center; padding:32px 30px 30px; }
/* Вход и 2FA: тот же знак продукта, что в панели, — монограмма-заглушка
   рядом с настоящим знаком читалась чужим логотипом */
.auth .sign { display:block; margin:0 auto 12px; width:56px; height:56px; }
.auth-logo { max-height:36px; max-width:220px; object-fit:contain; margin:0 0 14px; }
.auth h1 { font-size:calc(var(--fs) + 8px); margin-bottom:4px; }
.auth .sub { color:var(--muted); font-size:calc(var(--fs) - 0.5px); margin:0 0 18px; }
.auth form { text-align:left; }
.code-input { width:100%; text-align:center; font-family:var(--mono); font-size:22px;  /* кегль намеренно фиксированный */
  font-weight:700; letter-spacing:6px; margin-top:10px; }

/* ── Скроллбар ── */
::-webkit-scrollbar { width:10px; height:10px; }
::-webkit-scrollbar-thumb { background:var(--outline); }
::-webkit-scrollbar-track { background:transparent; }

/* ── Плитки админки ── */
.tiles { display:grid; grid-template-columns:repeat(auto-fill,minmax(240px,1fr)); gap:14px; margin-top:6px; }
.tile { display:block; background:var(--surface); border:1px solid var(--line);
  padding:16px 18px; color:var(--ink); transition:border-color .16s, background-color .16s; }
.tile:hover { border-color:var(--acc-a60); background:var(--acc-a12); text-decoration:none; }
.tile .t { font-weight:700; margin-bottom:3px; }
.tile .d { color:var(--muted); font-size:var(--fsc); line-height:1.4; }
.tile .ic { display:block; margin-bottom:9px; color:var(--muted2); }
.tile:hover .ic { color:var(--brand); }

/* ── Настройки: разделы вкладками, два вида раскладки ──
   Панель быстрых переходов и сворачиваемые карточки уступили место вкладкам:
   разделов тринадцать, и листать их подряд дольше, чем выбрать нужный. Адреса
   якорей сохранены — /admin/settings#brand по-прежнему открывает «Брендинг».
   Прячет разделы только скрипт (класс tabbed): без него страница остаётся
   прежней простынёй, а ссылки работают как якоря. */
.setnav { display:flex; flex-wrap:wrap; gap:2px; flex:1; min-width:0; }
.setnav a { display:inline-flex; align-items:center; gap:7px; padding:6px 10px;
  font-size:var(--fsc); color:var(--muted2); border:1px solid transparent; }
.setnav a:hover { color:var(--ink); background:var(--hov); text-decoration:none; }
.setnav a.on { color:var(--ink); font-weight:600; background:var(--paper);
  border-color:var(--outline); }
.setnav .ic { flex:none; }
.setview { flex:none; }
.setview button { flex:none; padding:6px 12px; font-size:calc(var(--fs) - 1.5px); }

.set { flex:1; min-height:0; overflow:auto; padding:20px 28px 40px; background:var(--bg); }
.setg { max-width:1080px; margin:0 0 26px; }
.set.tabbed .setg { margin-bottom:0; }        /* с вкладками раздел на экране один */
.setg h2 { display:flex; align-items:center; gap:9px; font-size:calc(var(--fs) + 3px); margin-bottom:6px; }
.setg h2 .ic { color:var(--muted2); }
.setg-h { margin:0 0 14px; max-width:78ch; color:var(--muted);
  font-size:var(--fsc); line-height:1.45; }

/* Вид «списком»: подпись и пояснение слева, поле справа — как в макете.
   Разметка та же, что у плиток, переставляет только сетка.
   Классы с префиксом set-: короткий .tiles уже занят плитками «Обзора»,
   и настройки молча получали чужую сетку — разделы разъезжались в колонки. */
.set-list .grid { display:block; }
.set-list .fld { display:grid; grid-template-columns:340px 1fr; column-gap:32px;
  align-items:center; padding:var(--rowpad) 0; border-bottom:1px solid var(--line); }
.set-list .fld > label { grid-column:1; grid-row:1; margin:0;
  color:var(--ink); font-weight:600; font-size:var(--fs); }
.set-list .fld > .hint { grid-column:1; grid-row:2; margin:2px 0 0; }
.set-list .fld > .fld-c { grid-column:2; grid-row:1 / span 2; max-width:420px; }
.set-list .fld.wide > .fld-c { max-width:none; }

/* ── Страница настроек: группы полей ── */
.grid { display:grid; grid-template-columns:repeat(auto-fit,minmax(230px,1fr)); gap:6px 18px; }
.fld { min-width:0; }
.fld.wide { grid-column:1/-1; }
.hint { color:var(--muted); font-size:calc(var(--fs) - 1.5px); margin:5px 0 0; line-height:1.4; }
/* Несколько правил подряд — списком: четыре .hint сливались в стену */
.hint-l { margin:5px 0 0; padding-left:18px; color:var(--muted);
  font-size:calc(var(--fs) - 1.5px); line-height:1.5; }
.hint-l li + li { margin-top:3px; }

/* Переключатель «вкл/выкл» */
.switch { display:inline-flex; align-items:center; gap:10px; cursor:pointer;
  font-size:var(--fs); font-weight:400; color:var(--ink); margin:6px 0 0; }
.switch input { position:absolute; opacity:0; width:0; height:0; }
/* Единственное скругление во всей системе — так задано в макете */
.switch .track { width:40px; height:22px; border-radius:11px; flex:none; position:relative;
  background:var(--outline); transition:background-color .16s; }
.switch .track::after { content:""; position:absolute; top:3px; left:3px; width:16px; height:16px;
  border-radius:50%; background:var(--knob); transition:transform .16s; }
.switch input:checked + .track { background:var(--ok); }
.switch input:checked + .track::after { transform:translateX(18px); }
.switch input:focus-visible + .track { outline:2px solid var(--acc); outline-offset:2px; }

/* Наборы галочек и радиокнопок */
.chips { display:flex; flex-wrap:wrap; gap:8px; margin-top:6px; }
.chip { display:inline-flex; align-items:center; gap:7px; margin:0; cursor:pointer;
  padding:6px 12px; border:1px solid var(--outline);
  background:var(--paper); color:var(--ink); font-size:calc(var(--fs) - 0.5px); font-weight:400;
  transition:border-color .16s, background-color .16s; }
.chip input { width:15px; height:15px; margin:0; accent-color:var(--acc); }
.chip:hover { border-color:var(--outline-hi); }
.chip:has(input:checked) { border-color:var(--acc); background:var(--acc-a12); }

/* Цвет бренда */
.colorbox { display:inline-flex; align-items:center; gap:10px; margin-top:6px; }
.colorbox input[type=color] { width:56px; height:38px; padding:3px; cursor:pointer; }
.logo-prev { max-height:56px; max-width:180px; object-fit:contain; display:block; }

/* ── Карточка отделения: переопределения ── */
.ovr { border:1px solid var(--line); background:var(--surface);
  padding:14px 16px; margin-bottom:12px; }
.ovr-h { display:flex; align-items:center; gap:10px; flex-wrap:wrap; }
.ovr-h .inherit { margin-left:auto; }
.ovr .row { margin-top:2px; }
.tag { display:inline-block; font-size:calc(var(--fs) - 3px); font-weight:600; padding:2px 7px;
  color:var(--muted); background:var(--surface); border:1px solid var(--line); }
.tag.on { color:var(--brand); border-color:var(--acc-a60); background:var(--acc-a12); }

/* Блок «как в общих»: поля заблокированы, значение приходит из общих настроек */
.ovr.inherited { background:var(--surface); }
.ovr.inherited input:disabled, .ovr.inherited select:disabled { opacity:.55; cursor:not-allowed; }
.ovr.inherited .chip:has(input:disabled) { opacity:.55; cursor:not-allowed; }
/* Тумблер в блоке «как в общих» тоже задизейблен скриптом — без этого
   правила он один во всём блоке выглядел рабочим. */
.ovr.inherited .switch:has(input:disabled) { opacity:.55; cursor:not-allowed; }

/* ── Код привязки планшета (карточка отделения) ── */
/* Цифры крупно: их диктуют монтажнику через полкомнаты или читают с экрана */
.enroll { display:flex; gap:18px; align-items:center; flex-wrap:wrap;
  border:1px solid var(--line); background:var(--surface);
  padding:14px 16px; margin:10px 0; }
.enroll > div { flex:1; min-width:230px; }
.enroll-code { font-family:var(--mono);
  font-size:40px; font-weight:700; letter-spacing:.16em; line-height:1.15; }  /* кегль намеренно фиксированный */
.enroll.gone .enroll-code { opacity:.35; text-decoration:line-through; }
.qr.sm { width:120px; padding:6px; }
/* Курсор ставился скриптом на каждую картинку; теперь клик слушает
   документ (QR перерисовывается), и подсказка курсором — на CSS. */
.qr-zoom { cursor:zoom-in; }

/* ── Плавающая панель «Не сохранено» (static/unsaved.js) ── */
/* Инверсия темы: тёмная плашка на светлом, светлая на тёмном. Классы .unsaved,
   .uh, .ul, .ub, .uw — контракт со скриптом, переименованию не подлежат. */
.unsaved { position:fixed; right:18px; bottom:18px; z-index:40; max-width:340px;
  background:var(--panel); color:var(--panel-ink); padding:16px 18px; }
.unsaved .uh { color:var(--panel-mut); font-weight:600; font-size:var(--fsm);
  text-transform:uppercase; letter-spacing:.06em; margin-bottom:8px; }
.unsaved .ul { margin:0 0 12px; padding-left:18px; color:var(--panel-ink);
  font-size:calc(var(--fs) - 0.5px); line-height:1.5; max-height:35vh; overflow:auto; }
.unsaved .uw { color:var(--panel-mut); font-size:var(--fsm); margin:0 0 10px; }
.unsaved .ub { display:flex; gap:8px; flex-wrap:wrap; }
.unsaved button { padding:8px 16px; font-size:calc(var(--fs) - 0.5px); }
.unsaved button.sec { color:var(--panel-ink); border-color:var(--panel-line); }
.unsaved button.sec:hover { background:var(--panel-hov); border-color:var(--panel-ink); }
/* Кнопка формы, к которой привела панель: видно, чем именно сохранять */
button.hi { animation:hi 1.1s ease-out 2; }
@keyframes hi { 50% { box-shadow:0 0 0 6px var(--acc-a60); } }

/* ── Списки: панель фильтров, таблица во всю высоту, подвал ──
   Страница-список не прокручивается целиком: панель фильтров и подвал
   с переходом по страницам стоят на месте, ездит только сама таблица.
   Отсюда и число строк — его считает по высоте окна static/table.js.
   Отступ 28px по краям повторяет шапку раздела: колонка «Время» должна
   начинаться там же, где заголовок страницы. */
.work > main.flush { padding:0; overflow:hidden; display:flex; flex-direction:column; }

.tbar { display:flex; align-items:center; gap:8px; flex-wrap:wrap; padding:9px 28px;
  background:var(--surface); border-bottom:1px solid var(--line); }
.tbar form { display:flex; align-items:center; gap:8px; flex-wrap:wrap; }
/* Форма фильтров забирает свободное место, поэтому соседи по панели (форма
   срока хранения и метка справа) стоят у правого края и не разъезжаются,
   когда чипов становится больше и они переносятся на вторую строку. */
.tbar-f { flex:1; min-width:280px; }
.tbar label { margin:0; font-size:var(--fsm); }
/* Кнопки, поля и чипы панели — одной высоты и по одной линии. Раньше высота
   складывалась у каждого своя (кнопка 6/12, чип 5/10, поле поиска 6/11) и
   панель шла ступеньками. 34px — высота поля поиска из макета (20 + 6+6 + рамки),
   к ней и подгоняются соседи. */
.tbar button, .tbar .btn { padding:var(--ctlpy) calc(var(--ctlpx) + 1px);
  font-size:var(--fsc); }
/* Кнопка-лупа живёт внутри поля и высоту ему не задаёт */
.tsearch-go { min-height:0; }
/* Высота ЗАДАНА, а не «не меньше». С min-height каждый контрол всё равно
   считал свою: поле поиска и дата упирались в собственную высоту поля ввода
   и выходили на 38px, а выпадашка и кнопка оставались 34px — рядом это
   заметно. Теперь все ростом ровно --ctl и растут только вместе с плотностью. */
.tbar > *, .tbar form > *, .tbar button, .tbar .btn, .tfoot .tchip {
  min-height:var(--ctl); box-sizing:border-box; }
.tbar .tsearch, .tbar .tchip, .tbar button, .tbar .btn,
.tfoot .tchip, .tfoot .pg, .pager .pg {
  height:var(--ctl); box-sizing:border-box; }
/* Внутренностям обёрток своя высота не нужна — её держит обёртка */
.tsearch input, .tchip input, .tchip select { height:auto; }
/* Кнопки-значки ростом со свой значок, а не с поле ввода. Общее правило
   «кнопка высотой --ctl» задумано про кнопки с подписью; стрелка сворачивания
   ветки, крестик окна, лупа в поле и значки действий в строке таблицы от него
   раздувались, а строка списка вырастала на треть. */
.fold, .mdl-x, .acts button, .icobtn, .tsearch-go {
  min-height:0; height:auto; }
.tbar button, .tbar .btn, .tbar .tchip, .tbar .tsearch, .tfoot .tchip {
  display:inline-flex; align-items:center; }
.tbar .tsearch-go { min-height:0; }
.tbar input[type=number] { width:64px; padding:calc(var(--ctlpy) - 1px) calc(var(--ctlpx) - 3px);
  font-size:var(--fsc); }

.tsearch { display:flex; align-items:center; width:300px; max-width:100%;
  border:1px solid var(--outline); background:var(--paper);
  padding:var(--ctlpy) var(--ctlpx); }
.tsearch:focus-within { border-color:var(--acc); }
.tsearch input, .tsearch input:focus { flex:1; min-width:0; border:0; outline:none;
  padding:2px 0; background:transparent; font-size:var(--fsc); min-height:0; }
/* Лупа — кнопка, а не значок: она отправляет форму. Стоит справа и растянута
   отрицательными полями до самой рамки, чтобы попадать в неё не целясь;
   внутренний отступ поля (6px 11px) она при этом съедает, а не удваивает. */
/* Кнопка, а не значок. Одного тёмного цвета не хватило: тонкий контурный
   значок на белом всё равно читался как подсказка внутри поля. Поэтому
   отделяем её от поля линией, кладём на свою поверхность и утолщаем штрих —
   получается правая половинка поля, по которой видно, что она нажимается. */
.tsearch-go { flex:none; padding:var(--ctlpy) var(--ctlpx);
  margin:calc(-1 * var(--ctlpy)) calc(-1 * var(--ctlpx)) calc(-1 * var(--ctlpy)) 0;
  display:inline-flex; align-items:center; border:0;
  border-left:1px solid var(--outline); background:var(--surface);
  color:var(--ink); }
.tsearch-go .ic { stroke-width:2.2; }
.tsearch-go:hover { background:var(--hov); color:var(--ink); opacity:1; }
.tsearch-go:active { background:var(--outline); }

/* Чип-выпадашка: тот же select с тем же именем параметра, только одет как чип.
   Выбранное значение — рамка и начертание потемнее, чтобы применённый фильтр
   был виден, не читая его значения. */
.tchip { display:inline-flex; align-items:center; gap:7px; border:1px solid var(--outline);
  background:var(--paper); padding:calc(var(--ctlpy) - 1px) calc(var(--ctlpx) - 1px);
  font-size:var(--fsc); color:var(--muted2); }
/* Рамка у чипа ровно та же, что у поля поиска. Тёмная рамка у выбранного
   выглядела чёрной коробкой рядом со светлым полем — особенно у дат, которые
   заполнены ВСЕГДА, то есть «выбраны» с первого открытия страницы. Что фильтр
   применён, показывает начертание значения, а не толщина границы. */
.tchip.on { color:var(--ink); font-weight:600; }
.tchip:focus-within { border-color:var(--acc); }
/* Одна высота строки на подпись и на значение. Без неё подпись 12px и поле
   13px выравнивались по центру СВОИХ блоков разной высоты, и «Строк» вставало
   выше «Авто (25)» — на глаз чуть-чуть, но видно. */
.tchip-l { color:var(--muted); font-size:var(--fsm); font-weight:400;
  line-height:calc(var(--fsc) + 7px); }
.tchip select, .tchip input, .tchip select:focus, .tchip input:focus {
  border:0; outline:none; padding:2px 0; background:transparent; min-height:0;
  line-height:calc(var(--fsc) + 7px);
  font-size:var(--fsc); font-weight:inherit; color:inherit; }

/* Действия НАД СПИСКОМ («Добавить отделение», «Корзина») — у правого края
   панели, а не в шапке раздела: они относятся к таблице, а не к странице.
   Высоту берут от общего правила панели, поэтому стоят на одной линии
   с полем поиска, а не выше него. */
.tbar-act { display:flex; align-items:center; gap:8px; flex-wrap:wrap; margin-left:auto; }

/* Полоса над панелью команд: выбор вида и путь по группам. У неё своя строка,
   поэтому переключатель не переносится вперемешку с поиском и кнопками. */
.tmodes { display:flex; align-items:center; gap:14px; flex-wrap:wrap;
  padding:8px 28px; background:var(--paper); border-bottom:1px solid var(--line); }
.tmodes .seg { margin:0; }
.tmodes .crumbs { font-size:var(--fsc); }

/* Кнопка-значок рядом с именем в строке: настройка группы. Квадрат по высоте
   строки, а не кнопка с подписью, — она стоит ВНУТРИ строки таблицы, и полная
   кнопка раздувала бы её. Подпись есть, но для читалки экрана и подсказкой. */
.icobtn { display:inline-flex; align-items:center; justify-content:center;
  width:24px; height:24px; flex:none; border:1px solid transparent;
  background:transparent; color:var(--muted); }
.icobtn:hover { background:var(--hov); border-color:var(--outline); color:var(--ink);
  text-decoration:none; opacity:1; }
.icobtn .ic { stroke-width:1.8; }

/* Форма-сосед в панели: срок хранения журнала. Отдельная форма, а не поле
   в фильтрах, — она шлёт POST. */
.tbar-r label { color:var(--muted); }
/* Размер страницы: в подвале он стоит вплотную к страницам, поэтому
   прижимать его к краю нечем и незачем. */
.tfoot .tperm { margin-left:0; }
.tfit { margin-left:auto; flex:none; font-weight:600; font-size:11px; letter-spacing:.09em;  /* кегль намеренно фиксированный */
  text-transform:uppercase; color:var(--muted); }
.tfit b { color:var(--muted2); font-weight:600; }

.tblw { flex:1; min-height:0; overflow:auto; background:var(--paper); }
.tbl { width:100%; border-collapse:collapse; --tblpad:28px; }
/* Шапка прибита к верху прокрутки. Линия под ней — тенью, а не рамкой:
   при border-collapse рамка липкой ячейки уезжает вместе с прокруткой. */
.tbl th { position:sticky; top:0; z-index:2; background:var(--paper); text-align:left;
  padding:10px 8px; white-space:nowrap; color:var(--muted);
  font-size:11px; font-weight:600; letter-spacing:.1em;  /* кегль намеренно фиксированный */
  text-transform:uppercase; border-bottom:0; box-shadow:inset 0 -1px 0 var(--outline); }
.tbl td { padding:var(--rowpad) 8px; border-bottom:1px solid var(--line);
  font-size:var(--fs); vertical-align:middle; }
.tbl th:first-child, .tbl td:first-child { padding-left:var(--tblpad); }
.tbl th:last-child, .tbl td:last-child { padding-right:var(--tblpad); }
.tbl tr:hover td { background:var(--hov); }
.tbl th.r, .tbl td.r { text-align:right; }
.tbl td.nw { white-space:nowrap; }
/* Колонка «на одну кнопку»: ширину делят между собой колонки с текстом,
   а не пустое место перед значком в конце строки. */
.tbl th.w1, .tbl td.w1 { width:1%; white-space:nowrap; }
/* Строка журнала: моноширинный шрифт держит колонки времени и кодов ровно */
.tbl td.msg { font-family:var(--mono); font-size:calc(var(--fs) - 1.5px); }
.tbl tr.empty:hover td { background:none; }
.tbl tr.empty td { color:var(--muted); }
.tbl th.s a { display:inline-flex; align-items:center; gap:6px; color:inherit; }
.tbl th.s a:hover { color:var(--ink); text-decoration:none; }
.tbl th.on { color:var(--ink); }
.sarr { display:inline-flex; flex-direction:column; gap:2px; }
.sarr span { width:0; height:0; border-left:3px solid transparent; border-right:3px solid transparent; }
.sarr span:first-child { border-bottom:4px solid var(--outline-hi); }
.sarr span:last-child { border-top:4px solid var(--outline-hi); }
.sarr.asc span:first-child { border-bottom-color:var(--ink); }
.sarr.desc span:last-child { border-top-color:var(--ink); }

/* Подвал — та же полоса, что и панель команд сверху: одинаковая заливка,
   одинаковый край 28px. Справа «Строк» и страницы стоят рядом: и то и другое
   про «покажи мне больше». */
.tfoot { display:flex; align-items:center; justify-content:space-between; gap:16px;
  flex-wrap:wrap; padding:12px 28px; background:var(--surface);
  border-top:1px solid var(--line); }
.tfoot-c { display:flex; align-items:center; gap:10px; flex-wrap:wrap; }
.tfoot-n { font-family:var(--mono); font-size:var(--fsm); font-weight:500; color:var(--muted); }
.pager { display:flex; gap:6px; font-size:var(--fsc); }
/* Кнопка страницы одной высоты с чипом «Строк», который стоит рядом: разница
   в пару пикселей рядом заметна, а порознь была незаметна. */
.pg { border:1px solid var(--outline); padding:var(--ctlpy) calc(var(--ctlpx) - 1px);
  min-width:32px; text-align:center;
  color:var(--muted2); min-height:var(--ctl); box-sizing:border-box;
  display:inline-flex; align-items:center; justify-content:center; }
a.pg:hover { background:var(--hov); border-color:var(--outline-hi); color:var(--ink);
  text-decoration:none; }
.pg.on { border-color:var(--ink); color:var(--ink); font-weight:700; }
.pg.off { opacity:.4; }
.pg.gap { border-color:transparent; min-width:0; padding:6px 2px; }

/* Сводка над таблицей: очередь обработки и ошибки сервера. Плитками, а не
   абзацами: это состояние всей установки, а не примечание к списку. */
.tsum { display:flex; flex-wrap:wrap; background:var(--paper);
  border-bottom:1px solid var(--line); }
.tsum-i { flex:1; min-width:300px; padding:11px 28px; font-size:calc(var(--fs) - 1.5px);
  line-height:1.5; color:var(--muted); }
.tsum-i + .tsum-i { border-left:1px solid var(--line); }
.tsum-i .badge { margin-right:8px; }

/* ── Раскрывающаяся полоса под панелью фильтров ──
   Форма, которой в макете списка места нет вовсе: «новый пользователь»,
   «организация», «загрузить слайд», «срок хранения». Прятать её нельзя —
   это единственный способ выполнить действие; занимать ею пол-экрана тоже
   нельзя — страница про список. Полоса с подписью открывается по щелчку
   и остаётся видимой строкой, когда закрыта. */
.tpan { border-bottom:1px solid var(--line); background:var(--surface); }
.tpan > summary { list-style:none; cursor:pointer; user-select:none; display:flex;
  align-items:center; gap:9px; padding:9px 28px; font-size:var(--fsc); font-weight:600;
  color:var(--ink); }
.tpan > summary::-webkit-details-marker { display:none; }
.tpan > summary::before { content:"+"; font-family:var(--mono); font-size:var(--fs);
  line-height:1; color:var(--muted); }
.tpan[open] > summary::before { content:"−"; }   /* минус, не дефис */
.tpan > summary:hover { background:var(--hov); }
.tpan-b { padding:2px 28px 18px; background:var(--paper);
  border-top:1px solid var(--line); }
.tpan-b > :first-child { margin-top:0; }
.tpan-b .row, .tpan-b form { max-width:760px; }

/* ── Блок с таблицей в обычном потоке ──
   Страница, где таблица не одна (очередь и решённые, отделения и кассиры) или
   где рядом живёт форма, не делит высоту окна — она прокручивается целиком.
   Таблица в ней та же, только прижата к краям блока, а не окна, и шапка
   не липкая: прибивать её не к чему, прокрутка тут общая. */
.blk { border:1px solid var(--line); background:var(--paper); margin-bottom:18px; }
.blk-h { display:flex; align-items:center; gap:12px; flex-wrap:wrap; padding:10px 16px;
  background:var(--surface); border-bottom:1px solid var(--line); }
.blk-h h2 { margin:0; font-size:var(--fs); font-weight:700; letter-spacing:0; }
.blk-h .mut, .blk-h .ok, .blk-h .err { font-size:calc(var(--fs) - 1.5px); }
.blk-n { padding:10px 16px; color:var(--muted); font-size:calc(var(--fs) - 1.5px); line-height:1.45;
  border-bottom:1px solid var(--line); }
.blk-n :last-child { margin-bottom:0; }
.blk-b { padding:14px 16px; }
.blk-b > :first-child { margin-top:0; }
.blk-h .sp-r { margin-left:auto; }

/* Пары «поле — значение» в карточке: два столбца по содержимому, а не таблица.
   Таблицей это было бы враньё — сортировать и листать тут нечего. */
.kv { display:grid; grid-template-columns:max-content 1fr; gap:5px 20px;
  align-items:baseline; margin:0 0 14px; font-size:var(--fs); }
.blk > .tbl, .blk-b > .tbl { --tblpad:16px; }
.blk .tbl th { position:static; }
.blk .tbl tr:last-child td { border-bottom:none; }
.blk + .blk { margin-top:0; }

/* Действия в конце строки. Два вида: .acts — значки без обводки (когда действий
   три и подписи не помещаются), .rowacts — обычные кнопки поменьше. Опасное
   действие в обоих остаётся красным: цвет тут единственный сигнал. */
/* В одну строку, без переноса. Колонка действий занимает 1% ширины, то есть
   сжимается по содержимому: с переносом её минимальной шириной была ширина
   ОДНОЙ кнопки, и кнопки вставали столбиком — «Открыть» над «Историей»,
   «Восстановить» над «Стереть», «Проверено» над «Ложным». Без переноса
   минимальная ширина считается по всему ряду, и колонка раздвигается. */
.rowacts { display:flex; align-items:center; justify-content:flex-end; gap:8px;
  flex-wrap:nowrap; }
.rowacts form { display:inline-flex; margin:0; }
.rowacts button, .rowacts .btn { padding:5px 10px; font-size:calc(var(--fs) - 1.5px); }
/* Ряд действий сжимается по содержимому: flex:0 из разметки оставлял
   min-width колонки формы, и подпись рвалась на две строки. Высоту берём
   от ряда, а не от --ctl: поле выше токена (line-height 1.55 от body),
   и кнопка, подогнанная под токен, вставала ступенькой — как было в .tbar. */
.row > .rowacts { flex:none; min-width:0; align-self:stretch; }
.row > .rowacts button, .row > .rowacts .btn { height:100%; }
.rowacts button, .rowacts .btn { white-space:nowrap; }
/* Ряд действий внутри блока: зазор задаётся gap, а не пробелом в шаблоне */
.actrow { display:flex; align-items:center; gap:8px; flex-wrap:wrap; }
.actrow form { display:inline-flex; margin:0; }

.acts { display:flex; align-items:center; justify-content:flex-end; gap:14px; }
.acts a, .acts button { display:inline-flex; align-items:center; color:var(--muted2);
  background:none; border:0; padding:0; cursor:pointer; }
.acts a:hover, .acts button:hover { color:var(--ink); text-decoration:none; opacity:1; }
.acts .danger, .acts button.danger { color:var(--err); background:none; border:0; }
.acts .danger:hover, .acts button.danger:hover { color:var(--err); opacity:.75; }
.acts form { display:inline-flex; margin:0; }

/* Пояснение под заголовком раздела: чем этот список наполняется и что в нём
   не хранится. В макете такой строки нет, а текст терять нельзя — карточки
   с абзацем над таблицей больше не будет. */
.page-note { margin:5px 0 0; max-width:78ch; color:var(--muted);
  font-size:calc(var(--fs) - 1.5px); line-height:1.45; }
.page-note:empty { display:none; }

/* ── Узкое окно ──
   Каркас рассчитан на 1440×900 из макета. На планшете и телефоне боковая
   панель шириной 232px съедала бы до трети ширины, а свернуть её нечем:
   раскладываем панель в строку сверху и отдаём прокрутку окну.
   100dvh, а не 100vh: на мобильных 100vh включает адресную строку, и низ
   панели с «Выходом» уезжал бы под край экрана. */
@media (max-width: 900px) {
  .shell { flex-direction:column; height:auto; min-height:100dvh; overflow:visible; }
  .side { width:auto; border-right:none; border-bottom:1px solid var(--outline); }
  .side-nav { flex-direction:row; overflow-x:auto; padding:0; }
  .side-i { border-left:0; border-bottom:3px solid transparent; padding:0 14px; }
  .side-i.on { padding-left:14px; border-left:0; border-bottom-color:var(--ink); }
  .work { overflow:visible; }
  .work > main { overflow:visible; padding:16px; }
  .page, .tabs, .tbar, .tfoot, .set { padding-left:16px; padding-right:16px; }
  .set { overflow:visible; }
  .set-list .fld { display:block; }
  .set-list .fld > .fld-c { max-width:none; }
  .card { padding:16px; }
  /* Список на узком окне: высоту окна больше не делим, таблица едет вбок */
  .work > main.flush { overflow:visible; }
  .tblw { overflow-x:auto; }
  .tbl th:first-child, .tbl td:first-child { padding-left:16px; }
  .tbl th:last-child, .tbl td:last-child { padding-right:16px; }
  .tfit { margin-left:0; }
}

/* Узкое окно. Правила ниже стоят ПОСЛЕ общего брейкпоинта намеренно: они
   про модальное окно и панель списка, которых на момент того брейкпоинта
   в файле ещё не было. На телефоне окно занимает экран целиком — иначе
   шапка «заголовок + действия + ×» с полями 28px разваливается, а под
   содержимое остаётся полоска. */
@media (max-width:900px) {
  .mdl { width:100vw; max-width:100vw; max-height:100dvh; height:100dvh;
    margin:0; border:0; }
  .mdl-c { max-height:100dvh; }
  .mdl-h { padding:12px 16px; gap:8px; flex-wrap:wrap; }
  .mdl-b { padding:14px 16px; }
  .tbar, .tfoot, .tmodes { padding-left:16px; padding-right:16px; }
  .tsearch { width:100%; }
  .tbar-act { margin-left:0; }
  .tbl { --tblpad:16px; }
}

/* ── Модальные окна ──────────────────────────────────────────────────────
   Штатный <dialog>: фокус, Esc и подложка достаются от браузера, а не
   переписываются скриптом. Две подачи, обе живут в этих же классах:
   диалог, нарисованный сервером прямо в странице (короткая форма), и общий
   пустой диалог #mdl, в который догружается содержимое отдельной ручкой
   (карточка отделения, разбор проверки лица — их в каждую строку списка
   не размножишь). Разметка внутри — обычная страница, поэтому формы,
   подтверждения и кнопки работают ровно так же, как на своей странице. */
.mdl { width:min(1040px, 94vw); max-height:90vh; padding:0; overflow:visible;
  border:1px solid var(--outline); background:var(--paper); color:var(--ink);
  box-shadow:0 24px 64px var(--shadow); }
.mdl::backdrop { background:var(--scrim); }
.mdl-c { max-height:90vh; overflow:auto; }
/* Короткая форма (завести пользователя, добавить слайд): широкое окно вокруг
   трёх полей выглядит пустым. */
.mdl-s { width:min(560px, 94vw); }
/* Переключатели окна «Вид». Вес (0,2,1): .prefs button весит (0,1,1), и
   полагаться на порядок правил в этом файле нельзя — он длинный и правила
   в нём переставляют. */
.prefs.prefs-lg button { flex:none; min-width:0; padding:0 14px;
  min-height:var(--ctl); font-size:var(--fsc); }
.viewg + .viewg { margin-top:20px; }
.viewg > label { margin:0 0 7px; }
/* Шапка окна повторяет шапку раздела: тот же край 28px и то же место
   заголовка, чтобы окно не выглядело чужой страницей. */
.mdl-h { display:flex; align-items:flex-start; gap:16px; padding:18px 28px;
  border-bottom:1px solid var(--line); background:var(--surface);
  position:sticky; top:0; z-index:3; }
.mdl-h h2 { font-size:calc(var(--fs) + 5px); font-weight:800; margin:0; }
.mdl-h .page-meta, .mdl-h .page-note { margin:0; }
.mdl-t { flex:1; min-width:0; }
.mdl-a { display:flex; align-items:center; gap:8px; flex-wrap:wrap; }
.mdl-b { padding:20px 28px; }
.mdl-x { flex:none; width:32px; height:32px; padding:0; line-height:1; font-size:calc(var(--fs) + 6px);
  background:transparent; color:var(--muted); border:1px solid transparent; }
.mdl-x:hover { background:var(--hov); color:var(--ink); border-color:var(--outline);
  opacity:1; }
.mdl-w { padding:32px 28px; color:var(--muted); }

/* Вкладки внутри окна: три вида слайда — три разные формы, показывать надо
   одну. Переключает :checked у радиокнопок, а не скрипт: форма добавления
   обязана работать и тогда, когда JS не доехал. Сами радиокнопки не в форме,
   поэтому в отправку они не попадают; спрятаны прозрачностью, а не display,
   чтобы оставаться доступными с клавиатуры. */
.tabsw > input[type=radio] { position:absolute; width:1px; height:1px; opacity:0; }
.tabsw-h { display:flex; gap:2px; flex-wrap:wrap; border-bottom:1px solid var(--line);
  margin-bottom:16px; }
.tabsw-h label { padding:9px 15px; cursor:pointer; color:var(--muted2);
  font-size:var(--fsc); font-weight:600; border-bottom:2px solid transparent;
  margin-bottom:-1px; }
.tabsw-h label:hover { color:var(--ink); background:var(--hov); }
.tabsw-b > div { display:none; }
.tabsw > input:nth-of-type(1):checked ~ .tabsw-h label:nth-child(1),
.tabsw > input:nth-of-type(2):checked ~ .tabsw-h label:nth-child(2),
.tabsw > input:nth-of-type(3):checked ~ .tabsw-h label:nth-child(3) {
  color:var(--ink); border-bottom-color:var(--ink); }
.tabsw > input:nth-of-type(1):checked ~ .tabsw-b > div:nth-child(1),
.tabsw > input:nth-of-type(2):checked ~ .tabsw-b > div:nth-child(2),
.tabsw > input:nth-of-type(3):checked ~ .tabsw-b > div:nth-child(3) { display:block; }
/* Клавиатурой видно, какая вкладка в фокусе */
.tabsw > input:focus-visible ~ .tabsw-h label { outline:none; }
.tabsw > input:nth-of-type(1):focus-visible ~ .tabsw-h label:nth-child(1),
.tabsw > input:nth-of-type(2):focus-visible ~ .tabsw-h label:nth-child(2),
.tabsw > input:nth-of-type(3):focus-visible ~ .tabsw-h label:nth-child(3) {
  outline:2px solid var(--acc); outline-offset:-2px; }
.tabsw-b h3 { margin-top:0; }

/* QR во весь экран: код сканируют телефоном, мелкий в карточке неудобен.
   Строится скриптом карточки (он же и открывает), поэтому здесь только вид. */
.qr-full { position:fixed; inset:0; z-index:100; display:flex; cursor:zoom-out;
  align-items:center; justify-content:center; background:var(--scrim-hi); }
.qr-full[hidden] { display:none; }
.qr-full img { width:min(80vw,80vh); height:min(80vw,80vh); padding:24px;
  background:#fff; image-rendering:pixelated; }

/* ── Печать ──────────────────────────────────────────────────────────────
   Журнал действий, журнал удалений и опросы печатают и сохраняют в PDF для
   проверяющих. На бумаге не нужны ни навигация, ни фильтры, ни переход по
   страницам — нужен сам список, весь и целиком. Прокрутку таблицы снимаем:
   иначе на лист попадает только видимая часть. */
@media print {
  .side, .tabs, .tbar, .tmodes, .pager, .page-act, .rowacts, .unsaved,
  .mdl, .qr-full { display:none !important; }
  .shell, .work { display:block; }
  .work > main.flush { overflow:visible; height:auto; }
  .tblw { overflow:visible; max-height:none; }
  .tbl th { position:static; box-shadow:none; border-bottom:1px solid #999; }
  .tbl tr { break-inside:avoid; }
  .tfoot { border-top:1px solid #999; }
  body { background:#fff; color:#000; }
  a { text-decoration:none; }
}
