/* ==========================================================================
   Open Sans - единственная гарнитура проекта.

   Шрифт лежит рядом (frontend/fonts), а не грузится с fonts.googleapis.com:
   приложение разворачивается во внутренней сети с LDAP/AD, где доступ к
   внешним CDN может быть закрыт, и тогда страница молча откатилась бы на
   системный шрифт. Переменное начертание 300-800, латиница и кириллица -
   155 КБ на всё, четырьмя подмножествами: браузер скачает только то,
   что реально нужно странице.
   ========================================================================== */

/* latin */
@font-face {
    font-family: 'Open Sans';
    font-style: normal;
    font-weight: 300 800;
    font-display: swap;
    src: url('/fonts/open-sans-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;
}

/* latin-ext */
@font-face {
    font-family: 'Open Sans';
    font-style: normal;
    font-weight: 300 800;
    font-display: swap;
    src: url('/fonts/open-sans-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;
}

/* cyrillic */
@font-face {
    font-family: 'Open Sans';
    font-style: normal;
    font-weight: 300 800;
    font-display: swap;
    src: url('/fonts/open-sans-cyrillic.woff2') format('woff2');
    unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
}

/* cyrillic-ext */
@font-face {
    font-family: 'Open Sans';
    font-style: normal;
    font-weight: 300 800;
    font-display: swap;
    src: url('/fonts/open-sans-cyrillic-ext.woff2') format('woff2');
    unicode-range: U+0460-052F, U+1C80-1C8A, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F;
}

/* ==========================================================================
   theme.css - единая тема status-board.
   Подключается ПОСЛЕ встроенного <style> страницы, поэтому переопределяет
   переменные, объявленные внутри index.html/analytics.html/и т.д.

   Тёмная тема - основная. Светлая включается [data-theme="light"].

   Контрасты измерены, а не подобраны на глаз:
     --ink   #EEF3F8 на --card  13.8:1
     --ink-2 #9FB0C4 на --card   6.9:1
     --ink-3 #7C8DA5 на --card   4.6:1
     --accent #4D9BF0 на --bg    6.3:1
   Белый на акценте даёт лишь 2.89:1, поэтому текст НА кнопке - тёмный
   (--accent-ink #0B1524 на акценте = 6.3:1).

   Цвета статусов проверены скриптом на различимость против фона #18253A:
   полоса светлоты, насыщенность, различимость при обычном зрении (15.2)
   и контраст - проходят. Пара «удалённо/на выезде» даёт 7.9 при пороге 8,
   что допустимо ТОЛЬКО потому, что статус везде дублируется иконкой и
   подписью. Убирать иконки нельзя - пара сольётся для протанопов.
   ========================================================================== */

:root {
    /* --- поверхности --- */
    --sb-bg: #0B1524;
    --sb-rail: #101C2E;
    --sb-card: #18253A;
    --sb-raised: #203047;
    --sb-line: #26364E;
    --sb-line-soft: #1E2C42;

    --sb-ink: #EEF3F8;
    --sb-ink-2: #9FB0C4;
    --sb-ink-3: #7C8DA5;

    --sb-accent: #4D9BF0;
    --sb-accent-ink: #0B1524;
    --sb-accent-soft: rgba(77, 155, 240, 0.15);
    --sb-accent-line: rgba(77, 155, 240, 0.42);

    --sb-shadow: 0 16px 40px rgba(0, 0, 0, 0.46);
    --sb-shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.30);
    --sb-mono: ui-monospace, SFMono-Regular, "Cascadia Mono", Consolas, monospace;

    /* --- состояния чек-ина --- */
    --sb-ok-bg: rgba(63, 160, 108, 0.16);
    --sb-ok-bd: rgba(63, 160, 108, 0.40);
    --sb-ok-ink: #7FD3A5;
    --sb-warn-bg: rgba(217, 161, 45, 0.16);
    --sb-warn-bd: rgba(217, 161, 45, 0.42);
    --sb-warn-ink: #E8C77A;
    --sb-bad-bg: rgba(224, 94, 94, 0.16);
    --sb-bad-bd: rgba(224, 94, 94, 0.42);
    --sb-bad-ink: #EFA0A0;

    /* --- переопределение переменных, объявленных внутри страниц --- */
    --bg-color: var(--sb-bg);
    --card-bg: var(--sb-card);
    --surface: var(--sb-card);
    --surface-2: var(--sb-rail);
    --surface-3: var(--sb-line);
    --text-primary: var(--sb-ink);
    --text-secondary: var(--sb-ink-2);
    --shadow: var(--sb-shadow-sm);
    --radius: 12px;

    /* analytics.html / reports.html / settings.html */
    --bg: var(--sb-bg);
    --panel: var(--sb-card);
    --panel-soft: var(--sb-rail);
    --line: var(--sb-line);
    --text: var(--sb-ink);
    --muted: var(--sb-ink-2);
    --input-bg: var(--sb-bg);
    --blue: var(--sb-accent);

    /* --- статусы: пять различимых тонов вместо одного #d4eaf7 --- */
    --status-office-bg: rgba(69, 153, 207, 0.15);
    --status-office-border: #4599cf;
    --status-office-text: #A8D2EE;
    --status-home-bg: rgba(63, 160, 108, 0.15);
    --status-home-border: #3fa06c;
    --status-home-text: #9DDCBB;
    --status-field-bg: rgba(184, 137, 28, 0.17);
    --status-field-border: #b8891c;
    --status-field-text: #E8C77A;
    --status-vacation-bg: rgba(138, 118, 212, 0.17);
    --status-vacation-border: #8a76d4;
    --status-vacation-text: #C4B8F0;
    --status-sick-bg: rgba(212, 106, 106, 0.15);
    --status-sick-border: #d46a6a;
    --status-sick-text: #EFAFAF;

    /* --- плотность доски --- */
    --employee-card-width: 265px;
    --employee-card-height: auto;
    --status-card-gap: 8px;
    --status-block-padding: 10px;
}

[data-theme="light"] {
    --sb-bg: #F4F6F9;
    --sb-rail: #ECEFF4;
    --sb-card: #FFFFFF;
    --sb-raised: #F0F3F7;
    --sb-line: #DCE2EA;
    --sb-line-soft: #E7ECF2;
    --sb-ink: #101826;
    --sb-ink-2: #46586E;
    --sb-ink-3: #64748B;
    --sb-accent: #1668C4;
    --sb-accent-ink: #FFFFFF;
    --sb-accent-soft: rgba(22, 104, 196, 0.10);
    --sb-accent-line: rgba(22, 104, 196, 0.34);
    --sb-shadow: 0 16px 40px rgba(16, 24, 38, 0.14);
    --sb-shadow-sm: 0 1px 2px rgba(16, 24, 38, 0.08);

    --sb-ok-ink: #12724C;
    --sb-warn-ink: #8A5A06;
    --sb-bad-ink: #A32B2B;

    --status-office-bg: #E4F1FA;
    --status-office-border: #2b7fb8;
    --status-office-text: #175F8C;
    --status-home-bg: #E5F2E9;
    --status-home-border: #34864f;
    --status-home-text: #22613A;
    --status-field-bg: #FBF1DC;
    --status-field-border: #c08a10;
    --status-field-text: #7D5806;
    --status-vacation-bg: #ECEBF8;
    --status-vacation-border: #6b5cb0;
    --status-vacation-text: #4B3F85;
    --status-sick-bg: #FBEAEA;
    --status-sick-border: #b84a4a;
    --status-sick-text: #8A3232;
}

/* ==========================================================================
   База
   ========================================================================== */

body {
    background-color: var(--sb-bg);
    color: var(--sb-ink);
}

/* Числа не должны прыгать при обновлении по Socket.IO */
.count-badge,
.clock-time,
.confirmation-caption,
[class*="time"],
[class*="count"],
td, th {
    font-variant-numeric: tabular-nums;
}

/* Раньше в проекте :focus-visible встречался ровно один раз. */
a:focus-visible,
button:focus-visible,
input:focus-visible,
select:focus-visible,
textarea:focus-visible,
[tabindex]:focus-visible {
    outline: 2px solid var(--sb-accent);
    outline-offset: 2px;
    border-radius: 4px;
}

@media (prefers-reduced-motion: reduce) {
    *, *::before, *::after {
        animation-duration: 0.01ms !important;
        animation-iteration-count: 1 !important;
        transition-duration: 0.01ms !important;
        scroll-behavior: auto !important;
    }
}

input, select, textarea {
    background: var(--sb-bg);
    color: var(--sb-ink);
    border-color: var(--sb-line);
}

input::placeholder, textarea::placeholder { color: var(--sb-ink-3); }

/* ==========================================================================
   Оболочка: боковая панель + верхняя строка. Разметку строит app-shell.js
   ========================================================================== */

body.sb-shelled { padding: 0 !important; display: block !important; }

.sb-shell {
    display: flex;
    min-height: 100vh;
    background: var(--sb-bg);
}

.sb-rail {
    display: flex;
    flex-direction: column;
    gap: 2px;
    width: 228px;
    flex: 0 0 228px;
    padding: 10px;
    background: var(--sb-rail);
    border-right: 1px solid var(--sb-line);
    height: 100vh;
    position: sticky;
    top: 0;
    overflow-y: auto;
}

.sb-brand {
    display: flex;
    align-items: center;
    gap: 9px;
    height: 36px;
    padding: 0 7px;
    flex: 0 0 auto;
    font-size: 13px;
    font-weight: 700;
    color: var(--sb-ink);
    letter-spacing: -0.01em;
}
.sb-brand img, .sb-brand svg { width: 21px; height: 21px; flex: 0 0 auto; }

.sb-cmdk {
    display: flex;
    align-items: center;
    gap: 8px;
    height: 32px;
    margin: 5px 0 8px;
    padding: 0 9px;
    flex: 0 0 auto;
    border: 1px solid var(--sb-line);
    border-radius: 7px;
    background: var(--sb-bg);
    color: var(--sb-ink-3);
    font: inherit;
    font-size: 12px;
    cursor: pointer;
    text-align: left;
}
.sb-cmdk:hover { background: var(--sb-raised); color: var(--sb-ink-2); }
.sb-cmdk span { flex: 1 1 auto; }

kbd.sb-key {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-width: 18px;
    height: 18px;
    padding: 0 5px;
    border: 1px solid var(--sb-line);
    border-radius: 4px;
    background: var(--sb-raised);
    color: var(--sb-ink-3);
    font-family: var(--sb-mono);
    font-size: 10px;
    line-height: 1;
}

.sb-nav-item {
    display: flex;
    align-items: center;
    gap: 9px;
    height: 32px;
    padding: 0 9px;
    border-radius: 7px;
    color: var(--sb-ink-2);
    font-size: 12.5px;
    font-weight: 500;
    text-decoration: none;
    border: none;
    background: transparent;
    cursor: pointer;
    width: 100%;
    font-family: inherit;
}
.sb-nav-item:hover { background: var(--sb-raised); color: var(--sb-ink); }
.sb-nav-item svg { flex: 0 0 auto; color: var(--sb-ink-3); }
.sb-nav-item span { flex: 1 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; text-align: left; }
.sb-nav-item[aria-current="page"] {
    background: var(--sb-accent-soft);
    color: var(--sb-ink);
    font-weight: 600;
}
.sb-nav-item[aria-current="page"] svg { color: var(--sb-accent); }

/* Внешняя ссылка в боковой панели: тот же ряд, что у разделов, но со
   стрелкой «уходит наружу» и приглушённой подписью - чтобы её не путали с
   навигацией по самому приложению. */
.sb-nav-ext { color: var(--sb-ink-2); }
.sb-nav-ext span { flex: 1 1 auto; }
.sb-nav-ext svg:last-child { flex: 0 0 auto; color: var(--sb-ink-3); opacity: 0.8; }
.sb-nav-ext:hover svg:last-child { opacity: 1; }

.sb-badge {
    flex: 0 0 auto;
    min-width: 18px;
    padding: 1px 6px;
    border-radius: 5px;
    text-align: center;
    background: var(--sb-raised);
    color: var(--sb-ink-3);
    font-size: 10.5px;
    font-weight: 700;
}
.sb-badge.warn { background: var(--sb-warn-bg); color: var(--sb-warn-ink); }
.sb-badge.bad { background: var(--sb-bad-bg); color: var(--sb-bad-ink); }

.sb-section {
    display: flex;
    align-items: center;
    height: 26px;
    padding: 0 9px;
    margin-top: 6px;
    font-size: 10px;
    font-weight: 700;
    letter-spacing: 0.07em;
    text-transform: uppercase;
    color: var(--sb-ink-3);
}

.sb-view {
    display: flex;
    align-items: center;
    gap: 9px;
    height: 29px;
    padding: 0 9px;
    border: none;
    border-radius: 7px;
    background: transparent;
    color: var(--sb-ink-2);
    font-family: inherit;
    font-size: 12px;
    cursor: pointer;
    width: 100%;
}
.sb-view:hover { background: var(--sb-raised); color: var(--sb-ink); }
.sb-view i { width: 7px; height: 7px; flex: 0 0 auto; margin: 0 4px; border-radius: 2px; }
.sb-view span { flex: 1 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; text-align: left; }
.sb-view b { flex: 0 0 auto; font-size: 10.5px; color: var(--sb-ink-3); }
.sb-view.is-active { background: var(--sb-accent-soft); color: var(--sb-ink); }

.sb-rail-spacer { flex: 1 1 auto; min-height: 8px; }

.sb-rail-foot {
    display: flex;
    align-items: center;
    gap: 8px;
    padding: 8px 7px 0;
    margin-top: 4px;
    flex: 0 0 auto;
    border-top: 1px solid var(--sb-line);
}
.sb-avatar {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 26px;
    height: 26px;
    flex: 0 0 auto;
    border-radius: 50%;
    background: var(--sb-accent-soft);
    color: var(--sb-accent);
    font-size: 10px;
    font-weight: 700;
}
.sb-rail-foot .sb-who { display: flex; flex-direction: column; flex: 1 1 auto; min-width: 0; }
.sb-rail-foot .sb-who b { font-size: 11.5px; font-weight: 600; color: var(--sb-ink); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.sb-rail-foot .sb-who i { font-style: normal; font-size: 10px; color: var(--sb-ink-3); }

.sb-icon-btn {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 30px;
    height: 30px;
    flex: 0 0 auto;
    padding: 0;
    border: 1px solid var(--sb-line);
    border-radius: 7px;
    background: var(--sb-card);
    color: var(--sb-ink-2);
    cursor: pointer;
}
.sb-icon-btn:hover { background: var(--sb-raised); color: var(--sb-ink); }
.sb-rail-foot .sb-icon-btn { border-color: transparent; background: transparent; }

.sb-main { display: flex; flex-direction: column; flex: 1 1 auto; min-width: 0; }

.sb-topbar {
    display: flex;
    align-items: center;
    gap: 10px;
    min-height: 52px;
    padding: 8px 16px;
    flex: 0 0 auto;
    background: var(--sb-bg);
    border-bottom: 1px solid var(--sb-line);
    flex-wrap: wrap;
}
.sb-title { margin: 0; flex: 0 0 auto; font-size: 14.5px; font-weight: 700; letter-spacing: -0.01em; color: var(--sb-ink); }
.sb-subtitle { flex: 0 0 auto; font-size: 11.5px; color: var(--sb-ink-3); }
.sb-spacer { flex: 1 1 auto; }

.sb-live {
    display: flex;
    align-items: center;
    gap: 6px;
    flex: 0 0 auto;
    height: 30px;
    padding: 0 10px;
    border-radius: 7px;
    background: var(--sb-ok-bg);
    color: var(--sb-ok-ink);
    font-size: 11.5px;
    font-weight: 600;
}
.sb-live i { width: 6px; height: 6px; border-radius: 50%; background: currentColor; }
.sb-live.is-off { background: var(--sb-bad-bg); color: var(--sb-bad-ink); }

.sb-content { flex: 1 1 auto; min-width: 0; padding: 14px 16px 20px; }

/* Бургер для узких экранов */
.sb-burger { display: none; }

/* ==========================================================================
   Палитра команд (Cmd/Ctrl+K)
   ========================================================================== */

.sb-cmd-overlay {
    position: fixed;
    inset: 0;
    z-index: 9000;
    display: none;
    align-items: flex-start;
    justify-content: center;
    padding-top: 12vh;
    background: rgba(4, 9, 17, 0.62);
}
.sb-cmd-overlay.is-open { display: flex; }

.sb-cmd {
    display: flex;
    flex-direction: column;
    width: min(624px, calc(100vw - 32px));
    max-height: 70vh;
    background: var(--sb-card);
    border: 1px solid var(--sb-line);
    border-radius: 14px;
    box-shadow: var(--sb-shadow);
    overflow: hidden;
}
.sb-cmd-head {
    display: flex;
    align-items: center;
    gap: 11px;
    height: 52px;
    flex: 0 0 auto;
    padding: 0 15px;
    border-bottom: 1px solid var(--sb-line);
}
.sb-cmd-head svg { flex: 0 0 auto; color: var(--sb-ink-3); }
.sb-cmd-head input {
    flex: 1 1 auto;
    min-width: 0;
    border: none;
    background: transparent;
    color: var(--sb-ink);
    font: inherit;
    font-size: 14px;
    outline: none;
}
.sb-cmd-list { flex: 1 1 auto; overflow-y: auto; padding: 4px 0; }
.sb-cmd-group {
    padding: 9px 13px 5px;
    font-size: 10px;
    font-weight: 700;
    letter-spacing: 0.07em;
    text-transform: uppercase;
    color: var(--sb-ink-3);
}
.sb-cmd-row {
    display: flex;
    align-items: center;
    gap: 10px;
    min-height: 38px;
    padding: 0 13px;
    border-left: 2px solid transparent;
    color: var(--sb-ink);
    font-size: 12.5px;
    cursor: pointer;
}
.sb-cmd-row svg { flex: 0 0 auto; color: var(--sb-ink-3); }
.sb-cmd-row .sb-cmd-label { flex: 1 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.sb-cmd-row .sb-cmd-hint { flex: 0 0 auto; font-size: 11px; color: var(--sb-ink-3); }
.sb-cmd-row.is-active {
    background: var(--sb-accent-soft);
    border-left-color: var(--sb-accent);
    font-weight: 600;
}
.sb-cmd-row.is-active svg { color: var(--sb-accent); }
.sb-cmd-empty { padding: 26px 13px; text-align: center; font-size: 12.5px; color: var(--sb-ink-3); }
.sb-cmd-foot {
    display: flex;
    align-items: center;
    gap: 16px;
    height: 38px;
    flex: 0 0 auto;
    padding: 0 15px;
    border-top: 1px solid var(--sb-line);
    background: var(--sb-bg);
    font-size: 11px;
    color: var(--sb-ink-3);
}
.sb-cmd-foot span { display: flex; align-items: center; gap: 6px; }

/* ==========================================================================
   Доска: плотные карточки
   ========================================================================== */

.board-container { gap: 10px !important; align-items: start; }

.column {
    background: var(--sb-rail) !important;
    border: 1px solid var(--sb-line-soft);
    border-radius: 10px !important;
    min-height: 0 !important;
}

.column-header {
    padding: 0 2px 2px !important;
    font-size: 10.5px !important;
    letter-spacing: 0.06em;
    color: var(--sb-ink-2) !important;
}
.count-badge {
    background: var(--sb-raised) !important;
    color: var(--sb-ink-3) !important;
    font-weight: 700;
}

.card {
    padding: 8px 9px !important;
    gap: 6px !important;
    border: 1px solid var(--sb-line);
    border-left-width: 2px;
    border-radius: 8px !important;
    box-shadow: var(--sb-shadow-sm) !important;
    min-height: 0 !important;
    height: auto !important;
}
.card:hover { border-color: var(--sb-accent-line); }

.card-content { gap: 7px; }
.employee-name { font-size: 12.5px !important; color: var(--sb-ink); }
.employee-role { font-size: 10.5px !important; color: var(--sb-ink-3) !important; }
.employee-domain { font-size: 10px !important; color: var(--sb-ink-3) !important; }

/* Точки загрузки были 10px - мало для попадания пальцем */
.load-dot { width: 11px !important; height: 11px !important; }
.load-indicator { gap: 6px; padding: 4px 0; }

/* ФИКС: правило .btn-office,.btn-home,... красило все пять статусов одним
   --status-button-bg, поэтому на живой доске у всех кнопок был одинаковый
   фон rgb(212,234,247). Здесь каждый статус получает свой цвет. */
.status-btn {
    padding: 5px 8px !important;
    font-size: 11px !important;
    border-radius: 5px !important;
    border: 1px solid transparent !important;
}
.status-btn.btn-office { background: var(--status-office-bg) !important; color: var(--status-office-text) !important; border-color: var(--status-office-border) !important; }
.status-btn.btn-home { background: var(--status-home-bg) !important; color: var(--status-home-text) !important; border-color: var(--status-home-border) !important; }
.status-btn.btn-field { background: var(--status-field-bg) !important; color: var(--status-field-text) !important; border-color: var(--status-field-border) !important; }
.status-btn.btn-vacation { background: var(--status-vacation-bg) !important; color: var(--status-vacation-text) !important; border-color: var(--status-vacation-border) !important; }
.status-btn.btn-sick { background: var(--status-sick-bg) !important; color: var(--status-sick-text) !important; border-color: var(--status-sick-border) !important; }

/* Подпись и причина дублируют кружок подтверждения на каждой карточке.
   Оставляем только когда есть что сказать сверх иконки. */
.confirmation-caption { display: none !important; }
.confirmation-reason {
    font-size: 10px !important;
    color: var(--sb-ink-3) !important;
    margin-top: 2px;
}
.confirmation-reason:empty { display: none !important; }

.confirmation-indicator {
    width: 18px !important;
    height: 18px !important;
    border-color: var(--sb-line) !important;
    background: var(--sb-raised) !important;
    color: var(--sb-ink-2) !important;
}
.confirmation-indicator.ok { background: var(--sb-ok-bg) !important; border-color: var(--sb-ok-bd) !important; color: var(--sb-ok-ink) !important; }
.confirmation-indicator.warn { background: var(--sb-warn-bg) !important; border-color: var(--sb-warn-bd) !important; color: var(--sb-warn-ink) !important; }
.confirmation-indicator.bad { background: var(--sb-bad-bg) !important; border-color: var(--sb-bad-bd) !important; color: var(--sb-bad-ink) !important; }

/* Заголовок «КОММЕНТАРИЙ» и всегда пустое поле повторялись на каждой из
   27 карточек. Поле сворачивается, пока в нём ничего нет. */
.comment-label { display: none !important; }
.comment-section { gap: 0 !important; }
.comment-input {
    height: 26px !important;
    min-height: 26px;
    padding: 5px 7px !important;
    font-size: 10.5px !important;
    background: transparent !important;
    border-color: transparent !important;
    color: var(--sb-ink-2) !important;
    resize: none;
    transition: height 0.12s ease, background 0.12s ease;
}
.comment-input:hover,
.comment-input:focus,
.comment-input.has-value {
    height: 40px !important;
    background: var(--sb-bg) !important;
    border-color: var(--sb-line) !important;
}

.dropdown-menu {
    background: var(--sb-card) !important;
    border: 1px solid var(--sb-line) !important;
    box-shadow: var(--sb-shadow) !important;
    border-radius: 10px !important;
}
.dropdown-item { color: var(--sb-ink) !important; font-size: 12.5px; }
.dropdown-item:hover { background: var(--sb-raised) !important; }
.dropdown-header { color: var(--sb-ink-3) !important; }

.modal, .modal-overlay > div {
    background: var(--sb-card) !important;
    color: var(--sb-ink);
    border: 1px solid var(--sb-line);
}

/* Карточка, подсвеченная переходом из палитры команд */
.card.sb-flash {
    outline: 2px solid var(--sb-accent);
    outline-offset: 2px;
}

/* ==========================================================================
   Узкие экраны. Раньше доска ломалась: карточки были жёстко 316px,
   а у diagnostics.html и schedule.html не было ни одного медиа-запроса.
   ========================================================================== */

/* Подложка под выехавшей панелью: гасит содержимое и ловит тап мимо.
   Скрывается атрибутом hidden из app-shell.js, поэтому на широком экране
   её нет в потоке вовсе. */
.sb-rail-backdrop {
    position: fixed;
    inset: 0;
    z-index: 7999;
    display: none;
    background: rgba(4, 10, 20, 0.5);
}

@media (max-width: 1024px) {
    .sb-rail {
        position: fixed;
        z-index: 8000;
        left: 0;
        top: 0;
        bottom: 0;
        transform: translateX(-100%);
        transition: transform 0.18s ease;
        box-shadow: var(--sb-shadow);
    }
    .sb-shell.sb-rail-open .sb-rail { transform: translateX(0); }
    .sb-shell.sb-rail-open .sb-rail-backdrop:not([hidden]) { display: block; }
    .sb-burger {
        display: flex;
        align-items: center;
        justify-content: center;
        width: 40px;
        height: 40px;
        margin-left: -6px;
        flex: 0 0 auto;
        padding: 0;
        border: none;
        background: transparent;
        color: var(--sb-ink-2);
        cursor: pointer;
    }
    .sb-content { padding: 12px; }
}

@media (max-width: 760px) {
    .board-container { flex-direction: column !important; display: flex !important; }
    .column, .status-stack {
        width: 100% !important;
        min-width: 0 !important;
        max-width: none !important;
    }
    .status-card-grid {
        grid-template-columns: 1fr !important;
        display: grid !important;
    }
    .card {
        width: 100% !important;
        min-width: 0 !important;
        max-width: none !important;
    }
    /* Цели нажатия на телефоне */
    .status-btn { min-height: 44px !important; }
    .load-dot { width: 14px !important; height: 14px !important; }
    .load-indicator { gap: 10px; padding: 8px 0; }
    .dropdown-item { min-height: 44px; display: flex; align-items: center; }
    .sb-topbar { gap: 8px; }
    .sb-subtitle { display: none; }

    /* Верхняя строка: элементы тянутся на всю ширину, а не жмутся к правому
       краю. Раньше ряд фильтров стоял справа, слева оставалась пустота, и
       шапка занимала 222px из 844 - четверть экрана телефона. */
    #sbTopSlot { justify-content: stretch; width: 100%; }
    #sbTopSlot > .sb-ms { flex: 1 1 140px; }
    #sbTopSlot > .sb-ms .sb-ms-btn { width: 100%; }
    #sbTopSlot > .search-box { flex: 1 1 100%; }
    .sb-topbar [data-sb-topbar] { width: 100%; }

    /* Чип чек-ина разворачивается в строку во всю ширину: иконка, текст,
       кнопка. До правки он вставал узким столбиком справа, кнопка
       «Изменить» была 28px высотой и растягивалась на всю его ширину. */
    /* Легенда состояний занимала две строки крупным кеглем. */
    .checkin-state-guide {
        gap: 6px 12px !important;
        font-size: 10.5px;
    }
}

/* ==========================================================================
   Сохранённые виды. Фильтр держится на CSS, а не на JS: доска
   перерисовывается по Socket.IO, и скрытие из JS слетало бы на каждом рендере.
   ========================================================================== */

body[data-sb-view="warn"] .card:not(:has(.confirmation-indicator.warn)) { display: none !important; }
body[data-sb-view="bad"] .card:not(:has(.confirmation-indicator.bad)) { display: none !important; }
body[data-sb-view="field"] .card:not([data-status="field"]) { display: none !important; }

/* ==========================================================================
   Чек-ин-виджет в верхней строке.
   Исходный блок - 360x126 с заголовком, датой, подписью и пояснением.
   В верхней строке нужен компактный чип: остаётся суть и кнопка.
   ========================================================================== */

.sb-topbar .checkin-pulse {
    width: auto !important;
    min-width: 0 !important;
    max-width: 340px;
    min-height: 0 !important;
    padding: 5px 6px 5px 10px !important;
    gap: 9px !important;
    border-radius: 8px !important;
    background: var(--sb-warn-bg) !important;
    border: 1px solid var(--sb-warn-bd) !important;
    box-shadow: none !important;
}
.sb-topbar .checkin-pulse[data-state="done"],
.sb-topbar .checkin-pulse[data-state="ok"] {
    background: var(--sb-ok-bg) !important;
    border-color: var(--sb-ok-bd) !important;
}

/* Кикер, дата и пояснение дублируют то, что и так видно; дата уезжает
   в подзаголовок страницы. */
.sb-topbar .checkin-kicker,
.sb-topbar .checkin-date,
.sb-topbar .checkin-detail { display: none !important; }

.sb-topbar .checkin-copy { gap: 0 !important; min-width: 0; }
.sb-topbar .checkin-title {
    font-size: 12px !important;
    font-weight: 600 !important;
    color: var(--sb-ink) !important;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}
.sb-topbar .checkin-orbit {
    width: 22px !important;
    height: 22px !important;
    min-width: 22px !important;
    font-size: 11px !important;
    flex: 0 0 auto;
}
.sb-topbar .checkin-action {
    min-height: 28px;
    padding: 0 11px !important;
    border-radius: 6px !important;
    background: var(--sb-accent) !important;
    color: var(--sb-accent-ink) !important;
    font-size: 11.5px !important;
    font-weight: 700 !important;
    border: none !important;
    flex: 0 0 auto;
}

/* Выдвижная форма становится выпадающей панелью под чипом */
.sb-topbar .checkin-drawer {
    position: absolute !important;
    top: calc(100% + 8px) !important;
    right: 0 !important;
    left: auto !important;
    width: 360px !important;
    max-width: calc(100vw - 32px);
    z-index: 7000;
    background: var(--sb-card) !important;
    border: 1px solid var(--sb-line) !important;
    border-radius: 12px !important;
    box-shadow: var(--sb-shadow) !important;
}

/* Легенда состояний - одной компактной строкой */
.checkin-state-guide {
    gap: 12px !important;
    margin-bottom: 10px !important;
    font-size: 11px;
    color: var(--sb-ink-3);
}
.checkin-state-guide-item {
    padding: 0 !important;
    border: none !important;
    background: transparent !important;
    font-size: 11px;
}

/* Пояснение под кнопкой статуса повторяет то, что уже говорит кружок,
   и раньше печаталось на каждой из 27 карточек. Кружок несёт aria-label
   и title, поэтому смысл не теряется. */
.card .confirmation-reason { display: none !important; }

.card .employee-domain { font-size: 9.5px !important; opacity: 0.75; }

/* ==========================================================================
   Рабочий календарь: пастельные тона.

   Было: насыщенные заливки (#D1FAE5, #FEF3C7, #DBEAFE, #F3E8FF, #FEE2E2),
   а праздники - чистый #ff1616. В сетке из сотен ячеек 43x43 такая
   насыщенность перекрикивает данные. В тёмной теме те же светлые плашки
   стояли на тёмном фоне.

   Стало: низкая насыщенность при сохранённом контрасте. Все пары проверены,
   минимум 5.5:1 в светлой и 7.0:1 в тёмной теме - то есть AA даже для
   мелкого текста, не только для иконок.
   ========================================================================== */

/* --- светлая тема --- */
html[data-theme="light"] td.status-office,
html[data-theme="light"] .swatch.status-office,
body .status-office { background: #E8F1F8; color: #1B5E86; }
html[data-theme="light"] td.status-home,
html[data-theme="light"] .swatch.status-home,
body .status-home { background: #E9F3EC; color: #245C3C; }
html[data-theme="light"] td.status-field,
html[data-theme="light"] .swatch.status-field,
body .status-field { background: #F7F0E2; color: #6E4E10; }
html[data-theme="light"] td.status-vacation,
html[data-theme="light"] .swatch.status-vacation,
body .status-vacation { background: #EFEDF7; color: #463C72; }
html[data-theme="light"] td.status-sick,
html[data-theme="light"] .swatch.status-sick,
body .status-sick { background: #F8EDED; color: #7A3636; }

/* --- тёмная тема --- */
html[data-theme="dark"] td.status-office,
html[data-theme="dark"] .swatch.status-office { background: #1B2E44; color: #A8D2EE; }
html[data-theme="dark"] td.status-home,
html[data-theme="dark"] .swatch.status-home { background: #1B3428; color: #9DDCBB; }
html[data-theme="dark"] td.status-field,
html[data-theme="dark"] .swatch.status-field { background: #332C18; color: #E8C77A; }
html[data-theme="dark"] td.status-vacation,
html[data-theme="dark"] .swatch.status-vacation { background: #272243; color: #C4B8F0; }
html[data-theme="dark"] td.status-sick,
html[data-theme="dark"] .swatch.status-sick { background: #362121; color: #EFAFAF; }

/* Праздники и нерабочие: был чистый #ff1616 */
body .nonwork,
body .holiday { background: #F3E7E7; color: #8A4A4A; }
html[data-theme="dark"] .nonwork,
html[data-theme="dark"] .holiday { background: #33232B; color: #E0A3A3; }

body .empty-workday { background: transparent; }

/* Поверхности календаря подтягиваем к общей теме */
html[data-theme="dark"] .wf-page,
html[data-theme="dark"] .topbar,
html[data-theme="dark"] .panel,
html[data-theme="dark"] .modal {
    background: var(--sb-card);
    border-color: var(--sb-line);
    color: var(--sb-ink);
}

/* Кисти статусов в панели - те же пастельные тона */
.brush-action[data-action-kind="status"][data-action-value="office"] { border-color: var(--status-office-border) !important; background: var(--status-office-bg) !important; color: var(--status-office-text) !important; }
.brush-action[data-action-kind="status"][data-action-value="home"] { border-color: var(--status-home-border) !important; background: var(--status-home-bg) !important; color: var(--status-home-text) !important; }
.brush-action[data-action-kind="status"][data-action-value="field"] { border-color: var(--status-field-border) !important; background: var(--status-field-bg) !important; color: var(--status-field-text) !important; }
.brush-action[data-action-kind="status"][data-action-value="vacation"] { border-color: var(--status-vacation-border) !important; background: var(--status-vacation-bg) !important; color: var(--status-vacation-text) !important; }
.brush-action[data-action-kind="status"][data-action-value="sick"] { border-color: var(--status-sick-border) !important; background: var(--status-sick-bg) !important; color: var(--status-sick-text) !important; }

/* Точка факта была чёрной (#111827) поверх пастели - теперь по теме */
.fact-dot { background: var(--sb-ink-2) !important; }
.duty-mark { filter: none !important; }

/* ==========================================================================
   Страница чек-ина.

   У неё раньше не было тёмной темы вообще - только светлый градиент
   #F3F6FB и своя палитра статусов (#D1FAE5/#FEF3C7/#DBEAFE/...),
   не совпадавшая с доской. Теперь тема общая, а статусы - те же, что везде.
   ========================================================================== */

body[data-sb-shell="minimal"] {
    background: var(--sb-bg) !important;
    color: var(--sb-ink);
}

.sb-shell-minimal .sb-topbar { justify-content: flex-start; }
.sb-shell-minimal .sb-content {
    display: flex;
    justify-content: center;
    padding: 20px 14px 32px;
}
.sb-shell-minimal .checkin-container {
    background: var(--sb-card) !important;
    border: 1px solid var(--sb-line);
    color: var(--sb-ink);
    box-shadow: var(--sb-shadow-sm) !important;
}
.sb-shell-minimal h1,
.sb-shell-minimal h2,
.sb-shell-minimal h3 { color: var(--sb-ink); }
.sb-shell-minimal .subtitle,
.sb-shell-minimal label { color: var(--sb-ink-2); }

.sb-shell-minimal .user-info,
.sb-shell-minimal .scheduled-panel,
.sb-shell-minimal .scheduled-content,
.sb-shell-minimal .checkin-banner {
    background: var(--sb-bg) !important;
    border: 1px solid var(--sb-line) !important;
    color: var(--sb-ink);
}

/* Текст баннера был задан значениями светлой темы (#111827 на заголовке,
   #6B7280 на подписях) и на тёмной подложке читался как пятно: главную
   строку «Вы ещё не отметились» было просто не видно. */
.sb-shell-minimal .checkin-banner-title { color: var(--sb-ink) !important; }
.sb-shell-minimal .checkin-banner-detail { color: var(--sb-ink-2) !important; }
.sb-shell-minimal .checkin-banner-kicker,
.sb-shell-minimal .checkin-banner-date { color: var(--sb-ink-3) !important; }

/* Статусы - общая палитра. Цель нажатия не меньше 44px: страница живёт
   в основном на телефоне. */
.sb-shell-minimal .status-btn {
    min-height: 48px;
    background: var(--sb-bg) !important;
    border: 1px solid var(--sb-line) !important;
    color: var(--sb-ink-2) !important;
}
.sb-shell-minimal .status-btn.office { border-color: var(--status-office-border) !important; background: var(--status-office-bg) !important; color: var(--status-office-text) !important; }
.sb-shell-minimal .status-btn.home { border-color: var(--status-home-border) !important; background: var(--status-home-bg) !important; color: var(--status-home-text) !important; }
.sb-shell-minimal .status-btn.field { border-color: var(--status-field-border) !important; background: var(--status-field-bg) !important; color: var(--status-field-text) !important; }
.sb-shell-minimal .status-btn.vacation { border-color: var(--status-vacation-border) !important; background: var(--status-vacation-bg) !important; color: var(--status-vacation-text) !important; }
.sb-shell-minimal .status-btn.sick { border-color: var(--status-sick-border) !important; background: var(--status-sick-bg) !important; color: var(--status-sick-text) !important; }

/* Была заливка linear-gradient(135deg,#667eea,#764ba2) - единственное место
   в проекте с таким градиентом. */
.sb-shell-minimal .btn-submit,
.sb-shell-minimal .btn-checkin {
    min-height: 48px;
    background: var(--sb-accent) !important;
    color: var(--sb-accent-ink) !important;
    border: none !important;
}

.sb-shell-minimal .success-message { background: var(--sb-ok-bg) !important; border: 1px solid var(--sb-ok-bd) !important; color: var(--sb-ok-ink) !important; }
.sb-shell-minimal .error-message { background: var(--sb-bad-bg) !important; border: 1px solid var(--sb-bad-bd) !important; color: var(--sb-bad-ink) !important; }
.sb-shell-minimal .debug-message,
.sb-shell-minimal .connection-banner { background: var(--sb-warn-bg) !important; border: 1px solid var(--sb-warn-bd) !important; color: var(--sb-warn-ink) !important; }

.sb-shell-minimal .btn-report,
.sb-shell-minimal .btn-muted {
    min-height: 44px;
    background: var(--sb-raised) !important;
    border: 1px solid var(--sb-line) !important;
    color: var(--sb-ink-2) !important;
}

/* ==========================================================================
   Перетаскивание карточек между колонками
   ========================================================================== */

.card[draggable="true"] { cursor: grab; }
.card.sb-dragging { opacity: 0.45; cursor: grabbing; }

.status-card-grid.sb-drop-target {
    outline: 1px dashed var(--sb-accent-line);
    outline-offset: 4px;
    border-radius: 8px;
    background: var(--sb-accent-soft);
}

.sb-drop-hint {
    display: flex;
    align-items: center;
    justify-content: center;
    height: 40px;
    border: 1px dashed var(--sb-accent-line);
    border-radius: 8px;
    background: var(--sb-accent-soft);
    color: var(--sb-accent);
    font-size: 11px;
    font-weight: 600;
}

/* ==========================================================================
   Плотная карточка. Было 178px фиксированной высоты.

   Экономия набирается из: должность и AD-логин в одной строке вместо двух,
   убраны подпись и причина подтверждения (кружок несёт title и aria-label),
   поле заметки показывается только когда в нём что-то есть.
   ========================================================================== */

.card .card-content {
    display: flex !important;
    align-items: flex-start;
    gap: 8px;
    width: 100%;
}

.sb-card-avatar {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 26px;
    height: 26px;
    flex: 0 0 auto;
    border-radius: 50%;
    font-size: 9.5px;
    font-weight: 700;
    letter-spacing: 0.01em;
}
.card[data-status="office"] .sb-card-avatar { background: var(--status-office-bg); color: var(--status-office-text); border: 1px solid var(--status-office-border); }
.card[data-status="home"] .sb-card-avatar { background: var(--status-home-bg); color: var(--status-home-text); border: 1px solid var(--status-home-border); }
.card[data-status="field"] .sb-card-avatar { background: var(--status-field-bg); color: var(--status-field-text); border: 1px solid var(--status-field-border); }
.card[data-status="vacation"] .sb-card-avatar { background: var(--status-vacation-bg); color: var(--status-vacation-text); border: 1px solid var(--status-vacation-border); }
.card[data-status="sick"] .sb-card-avatar { background: var(--status-sick-bg); color: var(--status-sick-text); border: 1px solid var(--status-sick-border); }

.card .employee-info { flex: 1 1 auto; min-width: 0; display: flex; flex-direction: column; gap: 1px; }
.card .load-indicator { flex: 0 0 auto; padding: 6px 0 0 !important; }

.card .status-btn { min-height: 24px; }

/* Заметка занимает место только когда в ней что-то есть. На тач-устройствах
   ховера нет, поэтому там она видна всегда. */
.card .comment-section { display: none !important; }
.card:hover .comment-section,
.card:focus-within .comment-section,
.card .comment-section:has(.comment-input.has-value) { display: flex !important; }

@media (hover: none) {
    .card .comment-section { display: flex !important; }
}

.card .comment-input { height: 22px !important; min-height: 22px; }
.card .comment-input:hover,
.card .comment-input:focus,
.card .comment-input.has-value { height: 38px !important; }

/* ==========================================================================
   Адаптивная доска.

   Было: ширина блока и карточки считалась в пикселях
   (getStatusBlockWidth -> --status-block-width, карточка -> фиксированные
   --employee-card-width), поэтому доска не помещалась в окно и появлялась
   горизонтальная прокрутка.

   Стало: решение JS о том, сколько карточек-колонок заслуживает статус,
   сохраняется полностью - оно уже лежит в --status-block-columns. Мы просто
   используем его как долю flex вместо пикселей. «В офисе» с двумя колонками
   по-прежнему вдвое шире остальных, но теперь тянется по ширине окна.
   Скрипт index.html править не пришлось.
   ========================================================================== */

/* Без min-width:0 флекс-ребёнок не даёт себя сжать, и фиксированная доска
   распирает страницу - отсюда и была горизонтальная прокрутка. */
.sb-main { min-width: 0; }
.sb-content { min-width: 0; max-width: 100%; }

.board-container {
    width: 100%;
    min-width: 0;
    gap: 12px !important;
}

.column,
.status-stack {
    width: auto !important;
    min-width: 0 !important;
    max-width: none !important;
    flex: var(--status-block-columns, 1) 1 0 !important;
}

.status-stack .column {
    width: 100% !important;
    flex: 0 0 auto !important;
}

/* Карточки заполняют выделенную блоку ширину, а не стоят фиксированными */
.status-card-grid {
    grid-template-columns: repeat(var(--status-card-columns, 1), minmax(0, 1fr)) !important;
    min-width: 0;
}

.card {
    width: 100% !important;
    min-width: 0 !important;
    max-width: none !important;
}

/* Имя больше не обрезается на полуслове там, где есть место */
.card .employee-name { font-size: 13px !important; }

/* Средние экраны: блокам разрешаем переноситься, чтобы карточка не
   сжималась до нечитаемой ширины. */
@media (max-width: 1400px) {
    .column,
    .status-stack { flex: var(--status-block-columns, 1) 1 265px !important; }
}

@media (max-width: 760px) {
    .column,
    .status-stack { flex: 1 1 100% !important; }
}

/* ==========================================================================
   Верхняя строка не должна распирать страницу.
   Перенесённые из старой шапки поля имеют свои ширины; без min-width:0
   флекс-контейнер раздувается до max-content и вылезает за окно.
   ========================================================================== */

.sb-topbar { min-width: 0; max-width: 100%; }

#sbTopSlot {
    flex: 1 1 auto;
    min-width: 0;
    justify-content: flex-end;
}

.sb-topbar .search-box {
    flex: 0 1 250px;
    min-width: 120px;
}
.sb-topbar .search-box input {
    width: 100%;
    min-width: 0;
}

.sb-topbar .department-filter { flex: 0 1 auto; min-width: 0; }
.sb-topbar .department-filter select { max-width: 190px; min-width: 0; }

.sb-topbar .checkin-pulse { flex: 0 1 auto; min-width: 0; }

/* На узком экране поля переносятся, а не сжимаются в нечитаемое */
@media (max-width: 1100px) {
    .sb-topbar .search-box { flex: 1 1 180px; }
}

/* ==========================================================================
   Элементы оболочки считаем по border-box.

   Причина переполнения на 32px: у .sb-topbar padding 8px/16px, а
   max-width:100% при content-box ограничивает только КОНТЕНТ - отступы
   прибавлялись сверху. По той же причине .sb-rail занимала 249px вместо
   заявленных 228px.

   Меняем только элементы оболочки, а не всё через `*`: страницы проекта
   местами рассчитывают на content-box, и глобальная замена сдвинула бы
   существующую вёрстку.
   ========================================================================== */

.sb-shell,
.sb-rail,
.sb-main,
.sb-topbar,
.sb-content,
.sb-brand,
.sb-cmdk,
.sb-nav-item,
.sb-view,
.sb-section,
.sb-rail-foot,
.sb-icon-btn,
.sb-cmd,
.sb-cmd-head,
.sb-cmd-list,
.sb-cmd-row,
.sb-cmd-foot {
    box-sizing: border-box;
}

/* Карточка не должна сжиматься до нечитаемой ширины.
   auto-fit сам решает, сколько карточек влезает в блок: широкому «В офисе»
   достанется две в ряд, узкому «Больничный» - одна. Логика «кому больше
   места» сохраняется, потому что ширину блока по-прежнему задаёт
   --status-block-columns, посчитанный скриптом страницы.
   min(100%, 230px) страхует от переполнения, когда блок уже самой карточки. */
.status-card-grid {
    grid-template-columns: repeat(auto-fit, minmax(min(100%, 230px), 1fr)) !important;
}

/* Кнопки статуса в выдвижной форме чек-ина остались с прежней яркой
   палитрой - приводим к общей, иначе один и тот же статус выглядит
   по-разному на доске и в форме. */
.checkin-drawer-status { border: 1px solid var(--sb-line) !important; background: var(--sb-bg) !important; color: var(--sb-ink-2) !important; }
.checkin-drawer-status.office { border-color: var(--status-office-border) !important; background: var(--status-office-bg) !important; color: var(--status-office-text) !important; }
.checkin-drawer-status.home { border-color: var(--status-home-border) !important; background: var(--status-home-bg) !important; color: var(--status-home-text) !important; }
.checkin-drawer-status.field { border-color: var(--status-field-border) !important; background: var(--status-field-bg) !important; color: var(--status-field-text) !important; }
.checkin-drawer-status.vacation { border-color: var(--status-vacation-border) !important; background: var(--status-vacation-bg) !important; color: var(--status-vacation-text) !important; }
.checkin-drawer-status.sick { border-color: var(--status-sick-border) !important; background: var(--status-sick-bg) !important; color: var(--status-sick-text) !important; }

.checkin-drawer-save { background: var(--sb-accent) !important; color: var(--sb-accent-ink) !important; border: none !important; }
.checkin-drawer-logout,
.checkin-drawer-cancel { background: var(--sb-raised) !important; color: var(--sb-ink-2) !important; border: 1px solid var(--sb-line) !important; }
.checkin-drawer-field input,
.checkin-drawer-field select { background: var(--sb-bg) !important; color: var(--sb-ink) !important; border: 1px solid var(--sb-line) !important; }
.checkin-drawer-title,
.checkin-drawer-field label { color: var(--sb-ink) !important; }
.checkin-drawer-meta { color: var(--sb-ink-3) !important; }

/* ==========================================================================
   Все карточки одной высоты.

   Раньше поле заметки пряталось, пока в нём пусто, и раскрывалось по
   наведению: сетка выглядела рваной, а высота карточки прыгала под курсором.
   Теперь заметка всегда занимает своё место - высота одинаковая и не
   меняется ни при наведении, ни при фокусе, ни когда появляется текст.
   ========================================================================== */

.card {
    min-height: 122px !important;
    justify-content: flex-start;
}

/* Заметка видна всегда - она и задаёт общую высоту */
.card .comment-section,
.card:hover .comment-section,
.card:focus-within .comment-section {
    display: flex !important;
}

/* Прижимаем заметку к низу: если строка карточек растянулась по самой
   высокой, лишнее место уходит наверх, а низ остаётся выровненным. */
.card .comment-section { margin-top: auto; }

/* Высота поля постоянна во всех состояниях. Длинный текст прокручивается
   внутри, а не раздвигает карточку. */
.card .comment-input,
.card .comment-input:hover,
.card .comment-input:focus,
.card .comment-input.has-value {
    height: 38px !important;
    min-height: 38px !important;
    max-height: 38px !important;
    overflow-y: auto;
}

/* Пустое поле остаётся тихим, но место занимает */
.card .comment-input {
    background: transparent !important;
    border-color: transparent !important;
}
.card .comment-input:hover,
.card .comment-input:focus,
.card .comment-input.has-value {
    background: var(--sb-bg) !important;
    border-color: var(--sb-line) !important;
}

/* Карточки в ряду выравниваются по одной высоте */
.status-card-grid { align-items: stretch !important; }

/* ==========================================================================
   Типографика: что каким начертанием набирать.

   Open Sans - гуманистический гротеск с открытыми апертурами: он разборчив
   в мелких кеглях, а это ровно наш случай - плотная доска, таблицы отчётов
   и сетка календаря.

   400 Regular  - весь основной текст: значения, ячейки таблиц, поля ввода,
                  подписи. База, от которой считается остальное.
   600 SemiBold - имена сотрудников, активный пункт меню, подписи кнопок.
                  Даёт нужный контраст к 400, не утяжеляя полосу так, как 700.
   700 Bold     - заголовки страниц и панелей, числа в плитках показателей.
   800          - не используем: на мелком кегле разница с 700 неразличима,
                  а на крупном выглядит крикливо.
   300 Light    - не используем в интерфейсе. На тёмном фоне тонкие штрихи
                  「съедаются」 подсветкой и текст теряет читаемость; светлое
                  начертание оправдано только на очень крупном кегле.

   ЗАГЛАВНЫЕ микроподписи (заголовки колонок, названия секций) требуют
   положительного межбуквенного просвета: у Open Sans прописные плотные,
   и без трекинга 0.06-0.07em они слипаются на 10-11px.

   ЦИФРЫ - только табличные. Доска обновляется по Socket.IO, и без tabular-nums
   время и счётчики дёргаются при каждом перерисовывании.

   МОНОШИРИННЫЙ - у Open Sans его нет, поэтому пути, маршруты и идентификаторы
   оставляем на системном моноширинном стеке.
   ========================================================================== */

:root {
    --sb-font: 'Open Sans', system-ui, -apple-system, 'Segoe UI', sans-serif;
}

html,
body,
input,
select,
textarea,
button,
table {
    font-family: var(--sb-font);
}

body {
    font-weight: 400;
    /* На тёмном фоне текст оптически полнеет; сглаживание возвращает
       задуманную толщину штриха. */
    -webkit-font-smoothing: antialiased;
    -moz-osx-font-smoothing: grayscale;
    font-variant-numeric: tabular-nums;
}

h1, h2, h3, h4 { font-weight: 700; letter-spacing: -0.01em; }
h1 { letter-spacing: -0.02em; }

/* Имена и прочие «якоря» взгляда */
.employee-name,
.sb-nav-item[aria-current="page"],
.sb-title,
.sb-cmd-row.is-active,
strong, b, th { font-weight: 600; }

.sb-title, h1, h2 { font-weight: 700; }

/* Заглавные микроподписи */
.column-header,
.sb-section,
.sb-cmd-group {
    font-weight: 700;
    letter-spacing: 0.07em;
    text-transform: uppercase;
}

/* Вторичный текст спокойнее не размером, а цветом - кегль ниже 10px
   в интерфейсе не опускаем. */
.employee-role,
.sb-subtitle,
.comment-input { font-weight: 400; }

/* Цифры не должны прыгать */
.count-badge,
.sb-badge,
.sb-view b,
.clock-time,
td, th,
input[type="date"],
input[type="time"] { font-variant-numeric: tabular-nums; }

/* ==========================================================================
   Единый фильтр с множественным выбором.

   Раньше отдел был обычным <select> с одним значением, а статусы - панелью
   с чекбоксами: два разных контрола для одной по сути задачи. Теперь вид и
   поведение общие, выбор в обоих - кликом, по нескольку вариантов.

   Исходные элементы остаются в DOM скрытыми: страница продолжает с ними
   работать, а новый контрол только надстроен сверху.
   ========================================================================== */

.sb-hidden-control {
    position: absolute !important;
    width: 1px;
    height: 1px;
    overflow: hidden;
    clip: rect(0 0 0 0);
    clip-path: inset(50%);
    white-space: nowrap;
}

.sb-ms { position: relative; flex: 0 0 auto; }

.sb-ms-btn {
    display: flex;
    align-items: center;
    gap: 7px;
    height: 34px;
    padding: 0 10px;
    border: 1px solid var(--sb-line);
    border-radius: 8px;
    background: var(--sb-card);
    color: var(--sb-ink-2);
    font-family: inherit;
    font-size: 12.5px;
    font-weight: 600;
    cursor: pointer;
    white-space: nowrap;
}
.sb-ms-btn:hover { background: var(--sb-raised); color: var(--sb-ink); }
.sb-ms-btn > svg:first-child { color: var(--sb-ink-3); flex: 0 0 auto; }
.sb-ms-title { color: var(--sb-ink); }

.sb-ms-count {
    padding: 1px 7px;
    border-radius: 999px;
    background: var(--sb-raised);
    color: var(--sb-ink-3);
    font-size: 11px;
    font-weight: 700;
    font-variant-numeric: tabular-nums;
}
/* Когда выбрано не всё - контрол должен сам об этом сообщать */
.sb-ms-count.is-filtered {
    background: var(--sb-accent-soft);
    color: var(--sb-accent);
}
.sb-ms-btn:has(.sb-ms-count.is-filtered) { border-color: var(--sb-accent-line); }

.sb-ms-panel {
    position: absolute;
    top: calc(100% + 6px);
    right: 0;
    z-index: 7500;
    min-width: 248px;
    max-width: 320px;
    padding: 6px;
    background: var(--sb-card);
    border: 1px solid var(--sb-line);
    border-radius: 11px;
    box-shadow: var(--sb-shadow);
}

.sb-ms-head {
    display: flex;
    gap: 6px;
    padding: 2px 2px 6px;
    margin-bottom: 4px;
    border-bottom: 1px solid var(--sb-line-soft);
}
.sb-ms-all {
    flex: 1 1 0;
    min-height: 28px;
    border: 1px solid var(--sb-line);
    border-radius: 6px;
    background: transparent;
    color: var(--sb-ink-2);
    font-family: inherit;
    font-size: 11.5px;
    font-weight: 600;
    cursor: pointer;
}
.sb-ms-all:hover { background: var(--sb-raised); color: var(--sb-ink); }

.sb-ms-list { display: flex; flex-direction: column; gap: 1px; max-height: 300px; overflow-y: auto; }

.sb-ms-row {
    display: flex;
    align-items: center;
    gap: 9px;
    width: 100%;
    min-height: 34px;
    padding: 0 8px;
    border: none;
    border-radius: 7px;
    background: transparent;
    color: var(--sb-ink-2);
    font-family: inherit;
    font-size: 12.5px;
    font-weight: 400;
    text-align: left;
    cursor: pointer;
}
.sb-ms-row:hover { background: var(--sb-raised); color: var(--sb-ink); }
.sb-ms-row.is-on { color: var(--sb-ink); font-weight: 600; }

.sb-ms-box {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 16px;
    height: 16px;
    flex: 0 0 auto;
    border: 1.5px solid var(--sb-line);
    border-radius: 5px;
    color: transparent;
}
.sb-ms-row.is-on .sb-ms-box {
    background: var(--sb-accent);
    border-color: var(--sb-accent);
    color: var(--sb-accent-ink);
}

.sb-ms-dot { width: 9px; height: 9px; flex: 0 0 auto; border-radius: 3px; }
.sb-ms-label { flex: 1 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.sb-ms-empty { padding: 18px 8px; text-align: center; font-size: 12px; color: var(--sb-ink-3); }

@media (max-width: 760px) {
    .sb-ms-row { min-height: 44px; }
    .sb-ms-all { min-height: 40px; }
    .sb-ms-panel { right: auto; left: 0; }
}

/* ==========================================================================
   Рабочий календарь: общий вид.

   Что чинится:
   - дублирующийся заголовок (свой h1 + заголовок в шапке оболочки);
   - ссылка «Закрыть» - навигация теперь в боковой панели;
   - три панели в ряд с большими пустотами;
   - кисти статусов разного размера и из разных палитр;
   - выходные заливались тяжёлым цветом на всю высоту таблицы, а в легенде
     «Выходной/праздник» оставался чистый #ff1616 - его правило
     [data-theme="dark"] .swatch.nonwork перебивало общую тему по
     специфичности, поэтому ниже селекторы намеренно длиннее.
   ========================================================================== */

/* Заголовок и «Закрыть» больше не нужны - их роль взяла оболочка */
.page > .topbar > h1 { display: none; }
.page > .topbar a[href="/"] { display: none; }

.page > .topbar {
    background: transparent !important;
    border-bottom: 1px solid var(--sb-line) !important;
    padding: 0 0 10px !important;
    margin-bottom: 12px;
}
.month-title {
    font-size: 15px;
    font-weight: 700;
    color: var(--sb-ink);
    letter-spacing: -0.01em;
}

/* Панели: вместо трёх колонок с пустотами - фильтры и выгрузка в одном
   ряду, кисть во всю ширину под ними. */
.content {
    padding: 0 !important;
    grid-template-columns: minmax(0, 1fr) minmax(0, auto) !important;
    grid-template-areas:
        "filters export"
        "brush brush"
        "legend legend"
        "calendar calendar" !important;
    gap: 10px !important;
}

.panel {
    background: var(--sb-card) !important;
    border: 1px solid var(--sb-line) !important;
    border-radius: 10px !important;
    padding: 12px !important;
}

/* Кисти статусов - одна форма, один размер, общая палитра */
.brush-action {
    min-height: 32px;
    padding: 0 12px !important;
    border-radius: 8px !important;
    font-size: 12.5px !important;
    font-weight: 600 !important;
    border: 1px solid var(--sb-line) !important;
    background: var(--sb-bg) !important;
    color: var(--sb-ink-2) !important;
}
.brush-action:hover { background: var(--sb-raised) !important; }

/* Дежурства - не статусы, поэтому нейтральный вид с акцентной рамкой */
.brush-action[data-action-kind="duty"] {
    border-style: dashed !important;
    color: var(--sb-ink-2) !important;
}
.brush-action.danger {
    border-color: var(--sb-bad-bd) !important;
    color: var(--sb-bad-ink) !important;
    background: var(--sb-bad-bg) !important;
}
/* Выбранная кисть видна кольцом, а не другим цветом */
.brush-action.active {
    outline: 2px solid var(--sb-accent);
    outline-offset: 1px;
}

.status-brushbar input,
.status-brushbar select {
    min-height: 32px;
    border-radius: 8px !important;
    background: var(--sb-bg) !important;
    border: 1px solid var(--sb-line) !important;
    color: var(--sb-ink) !important;
}
.status-brushbar button.primary,
.page .primary {
    background: var(--sb-accent) !important;
    color: var(--sb-accent-ink) !important;
    border: none !important;
    min-height: 32px;
    border-radius: 8px !important;
    font-weight: 700 !important;
}

/* Легенда - спокойный ряд, квадратики в тон таблицы */
.legend {
    display: flex !important;
    flex-wrap: wrap;
    gap: 8px 18px !important;
    align-items: center;
}
.legend-item { font-size: 11.5px; color: var(--sb-ink-2); }
.swatch { border-radius: 3px !important; border: 1px solid var(--sb-line); }

html[data-theme="dark"] .legend .swatch.nonwork,
html[data-theme="dark"] .legend .swatch.holiday,
body .legend .swatch.nonwork,
body .legend .swatch.holiday { background: #C98B8B !important; }

/* Выходные и праздники: лёгкая подложка вместо сплошной заливки.
   Полупрозрачный тон ложится поверх статусной ячейки, не забивая её. */
html[data-theme="dark"] td.nonwork,
html[data-theme="dark"] th.nonwork,
body td.nonwork,
body th.nonwork { background: rgba(201, 139, 139, 0.10) !important; }

html[data-theme="dark"] td.holiday,
html[data-theme="dark"] th.holiday,
body td.holiday,
body th.holiday { background: rgba(201, 139, 139, 0.20) !important; }

html[data-theme="dark"] th.nonwork,
html[data-theme="dark"] th.holiday { color: #E0A3A3 !important; }

/* Шапка таблицы: даты читаются, дни недели - подписью */
.day-head {
    font-size: 11px !important;
    font-weight: 700 !important;
    color: var(--sb-ink-2) !important;
    font-variant-numeric: tabular-nums;
}
.day-name {
    font-size: 9px !important;
    font-weight: 400 !important;
    color: var(--sb-ink-3) !important;
    text-transform: uppercase;
    letter-spacing: 0.04em;
}

.page table { border-color: var(--sb-line-soft) !important; }
.page th, .page td { border-color: var(--sb-line-soft) !important; }
.page thead th {
    background: var(--sb-rail) !important;
    color: var(--sb-ink-2) !important;
    position: sticky;
    top: 0;
    z-index: 3;
}
.page tbody tr:hover td { background: rgba(77, 155, 240, 0.05); }

.sticky-name b,
.page tbody td strong { font-weight: 600; color: var(--sb-ink); }

@media (max-width: 1100px) {
    .content {
        grid-template-columns: minmax(0, 1fr) !important;
        grid-template-areas:
            "filters"
            "export"
            "brush"
            "legend"
            "calendar" !important;
    }
}

/* ==========================================================================
   Общие примитивы разделов.

   Календарь, отчёты, аналитика и настройки писались в разное время и каждый
   со своей палитрой: свои .panel, свои .btn, свои #F9FAFB на тёмном фоне.
   Ниже - один набор поверхностей, кнопок, полей, плиток и таблиц; страницы
   переходят на него, оставляя собственные id и обработчики как есть.

   Правила живут здесь, а не в каждом файле: theme.css подключается после
   встроенного <style> страницы, поэтому вид раздела меняется в одном месте.
   ========================================================================== */

/* [hidden] проигрывал .sb-section/.sb-nav-item/.sb-badge по специфичности:
   пункт «Настройки» оставался виден без права, а подпись «Часы» висела над
   пустотой. display:none должен побеждать всегда. */
[hidden] { display: none !important; }

/* --- каркас раздела ----------------------------------------------------- */

.sb-page {
    display: flex;
    flex-direction: column;
    gap: 12px;
    max-width: 1560px;
    min-width: 0;
}

.sb-lead {
    margin: -2px 0 2px;
    font-size: 12.5px;
    line-height: 1.45;
    color: var(--sb-ink-2);
    max-width: 78ch;
}

/* --- панель ------------------------------------------------------------- */

.sb-panel {
    background: var(--sb-card);
    border: 1px solid var(--sb-line);
    border-radius: 10px;
    padding: 14px 16px 16px;
    min-width: 0;
}

.sb-panel-head {
    display: flex;
    align-items: baseline;
    gap: 10px;
    flex-wrap: wrap;
    margin-bottom: 12px;
}

.sb-panel-head h2,
.sb-panel > h2 {
    margin: 0;
    font-size: 13px;
    font-weight: 700;
    letter-spacing: 0.07em;
    text-transform: uppercase;
    color: var(--sb-ink-2);
}

.sb-panel-note {
    font-size: 11.5px;
    line-height: 1.45;
    color: var(--sb-ink-3);
}

.sb-panel-head .sb-spacer { flex: 1 1 auto; }

/* --- панель фильтров ---------------------------------------------------- */

.sb-toolbar {
    display: flex;
    flex-wrap: wrap;
    align-items: flex-end;
    gap: 10px 12px;
    padding: 12px 14px;
    background: var(--sb-card);
    border: 1px solid var(--sb-line);
    border-radius: 10px;
}

.sb-field {
    display: flex;
    flex-direction: column;
    gap: 5px;
    min-width: 0;
    flex: 1 1 160px;
}

.sb-field.is-wide { flex: 2 1 240px; max-width: 460px; }
.sb-field.is-tight { flex: 0 0 auto; max-width: none; }

/* В панели фильтров поле не растягивается на всю ширину экрана: один
   оставшийся select раньше разъезжался на 1300px и читался как ошибка. */
.sb-toolbar > .sb-field { max-width: 300px; }

.sb-field > label,
.sb-field > .sb-field-label {
    font-size: 10px;
    font-weight: 700;
    letter-spacing: 0.07em;
    text-transform: uppercase;
    color: var(--sb-ink-3);
}

/* Поля ввода одного роста и одной палитры - в разделах, которые явно
   подписались на общие примитивы контейнером .sb-page. Раньше здесь стоял
   .sb-content, то есть «любая страница»: рабочий календарь этого не просил,
   и его select'ы выгрузки растянулись на всю ширину и наехали друг на друга.

   Перечислять типы через input[type="text"] нельзя: у части полей в разметке
   атрибута type нет вовсе (<input value="...">), и такие поля оставались с
   видом по умолчанию - в настройках названия отделов выглядели подчёркнутой
   строкой без рамки. Поэтому исключаем то, что оформлять не нужно. */
.sb-page input:not([type="checkbox"]):not([type="radio"]):not([type="file"]),
.sb-page select,
.sb-page textarea,
.sb-modal-body input:not([type="checkbox"]):not([type="radio"]):not([type="file"]),
.sb-modal-body select,
.sb-modal-body textarea {
    width: 100%;
    min-height: 34px;
    padding: 6px 9px;
    /* Без этого ширина 100% складывается с padding и рамкой: на странице
       сброс box-sizing может и не дойти до полей, и форма уезжала за край
       окна горизонтальной полосой. */
    box-sizing: border-box;
    border: 1px solid var(--sb-line);
    border-radius: 7px;
    background: var(--sb-bg);
    color: var(--sb-ink);
    font-family: inherit;
    font-size: 12.5px;
    font-variant-numeric: tabular-nums;
}

.sb-page textarea,
.sb-modal-body textarea { min-height: 64px; resize: vertical; line-height: 1.45; }

.sb-page input:focus-visible,
.sb-page select:focus-visible,
.sb-page textarea:focus-visible,
.sb-modal-body input:focus-visible,
.sb-modal-body select:focus-visible,
.sb-modal-body textarea:focus-visible {
    outline: none;
    border-color: var(--sb-accent-line);
    box-shadow: 0 0 0 3px var(--sb-accent-soft);
}

.sb-page input::placeholder,
.sb-page textarea::placeholder,
.sb-modal-body input::placeholder,
.sb-modal-body textarea::placeholder { color: var(--sb-ink-3); }

.sb-page input:disabled,
.sb-page select:disabled,
.sb-modal-body input:disabled,
.sb-modal-body select:disabled,
.sb-modal-body textarea:disabled { opacity: 0.55; cursor: not-allowed; }

/* --- кнопки ------------------------------------------------------------- */

.sb-btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 6px;
    min-height: 34px;
    padding: 0 13px;
    border: 1px solid var(--sb-line);
    border-radius: 7px;
    background: var(--sb-raised);
    color: var(--sb-ink);
    font-family: inherit;
    font-size: 12.5px;
    font-weight: 600;
    white-space: nowrap;
    cursor: pointer;
    text-decoration: none;
}

.sb-btn:hover { border-color: var(--sb-accent-line); }

.sb-btn svg { flex: 0 0 auto; }

.sb-btn.is-primary {
    background: var(--sb-accent);
    border-color: var(--sb-accent);
    color: var(--sb-accent-ink);
}
.sb-btn.is-primary:hover { filter: brightness(1.08); }

.sb-btn.is-quiet { background: transparent; }

/* Разрушающее действие красное в наведении и фокусе, а не в покое: ряд из
   красных кнопок «Удалить» раньше перетягивал на себя весь раздел. */
.sb-btn.is-danger { color: var(--sb-bad-ink); }
.sb-btn.is-danger:hover {
    background: var(--sb-bad-bg);
    border-color: var(--sb-bad-bd);
}

.sb-btn.is-small { min-height: 28px; padding: 0 9px; font-size: 11.5px; }

.sb-btn:disabled { opacity: 0.5; cursor: not-allowed; }

.sb-btn-row {
    display: flex;
    flex-wrap: wrap;
    gap: 8px;
    align-items: center;
}

/* --- плитки показателей ------------------------------------------------- */

.sb-stats {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
    gap: 8px;
}

.sb-stat {
    display: flex;
    flex-direction: column;
    gap: 2px;
    padding: 11px 13px;
    background: var(--sb-card);
    border: 1px solid var(--sb-line);
    border-radius: 10px;
    min-width: 0;
}

.sb-stat b {
    font-size: 22px;
    font-weight: 700;
    line-height: 1.1;
    letter-spacing: -0.02em;
    color: var(--sb-ink);
    font-variant-numeric: tabular-nums;
}

.sb-stat span {
    font-size: 10.5px;
    font-weight: 700;
    letter-spacing: 0.06em;
    text-transform: uppercase;
    color: var(--sb-ink-3);
}

.sb-stat i {
    font-style: normal;
    font-size: 11px;
    color: var(--sb-ink-3);
    line-height: 1.4;
}

/* Плитка кричит только когда есть о чём: ноль опозданий красным цветом
   ничего не сообщает, а внимание забирает. */
.sb-stat.is-warn[data-nonzero="true"] b { color: var(--sb-warn-ink); }
.sb-stat.is-bad[data-nonzero="true"] b { color: var(--sb-bad-ink); }
.sb-stat.is-ok[data-nonzero="true"] b { color: var(--sb-ok-ink); }

/* --- таблицы ------------------------------------------------------------ */

.sb-table-wrap {
    overflow: auto;
    max-height: 62vh;
    border: 1px solid var(--sb-line);
    border-radius: 10px;
    background: var(--sb-card);
}

.sb-table {
    width: 100%;
    border-collapse: collapse;
    font-size: 12.5px;
}

.sb-table th,
.sb-table td {
    padding: 8px 11px;
    text-align: left;
    border-bottom: 1px solid var(--sb-line-soft);
    vertical-align: top;
    white-space: nowrap;
}

.sb-table th {
    position: sticky;
    top: 0;
    z-index: 2;
    background: var(--sb-rail);
    font-size: 10px;
    font-weight: 700;
    letter-spacing: 0.06em;
    text-transform: uppercase;
    color: var(--sb-ink-2);
    border-bottom: 1px solid var(--sb-line);
}

.sb-table td { color: var(--sb-ink-2); font-variant-numeric: tabular-nums; }
.sb-table td.is-name,
.sb-table td strong { color: var(--sb-ink); font-weight: 600; }
.sb-table td.is-wrap { white-space: normal; min-width: 200px; }
.sb-table tbody tr:last-child td { border-bottom: 0; }
.sb-table tbody tr:hover td { background: var(--sb-accent-soft); }

/* --- пилюли состояния --------------------------------------------------- */

.sb-pill {
    display: inline-flex;
    align-items: center;
    gap: 5px;
    padding: 2px 8px;
    border-radius: 999px;
    border: 1px solid var(--sb-line);
    background: var(--sb-raised);
    color: var(--sb-ink-2);
    font-size: 11px;
    font-weight: 600;
    white-space: nowrap;
}

.sb-pill.ok { background: var(--sb-ok-bg); border-color: var(--sb-ok-bd); color: var(--sb-ok-ink); }
.sb-pill.warn { background: var(--sb-warn-bg); border-color: var(--sb-warn-bd); color: var(--sb-warn-ink); }
.sb-pill.bad { background: var(--sb-bad-bg); border-color: var(--sb-bad-bd); color: var(--sb-bad-ink); }
.sb-pill.info { background: var(--sb-accent-soft); border-color: var(--sb-accent-line); color: var(--sb-accent); }

/* --- пустые состояния и загрузка --------------------------------------- */

.sb-empty {
    padding: 34px 18px;
    text-align: center;
    font-size: 12.5px;
    line-height: 1.5;
    color: var(--sb-ink-3);
}

.sb-empty b {
    display: block;
    margin-bottom: 4px;
    font-size: 13.5px;
    font-weight: 600;
    color: var(--sb-ink-2);
}

.sb-spinner {
    width: 22px;
    height: 22px;
    margin: 0 auto 10px;
    border: 2px solid var(--sb-line);
    border-top-color: var(--sb-accent);
    border-radius: 50%;
    animation: sb-spin 0.9s linear infinite;
}

@keyframes sb-spin { to { transform: rotate(360deg); } }

@media (prefers-reduced-motion: reduce) {
    .sb-spinner { animation-duration: 3s; }
}

/* --- уведомления -------------------------------------------------------- */

.sb-note {
    padding: 9px 12px;
    border-radius: 8px;
    border: 1px solid var(--sb-line);
    background: var(--sb-raised);
    color: var(--sb-ink-2);
    font-size: 12.5px;
    line-height: 1.45;
}

.sb-note.ok { background: var(--sb-ok-bg); border-color: var(--sb-ok-bd); color: var(--sb-ok-ink); }
.sb-note.warn { background: var(--sb-warn-bg); border-color: var(--sb-warn-bd); color: var(--sb-warn-ink); }
.sb-note.bad { background: var(--sb-bad-bg); border-color: var(--sb-bad-bd); color: var(--sb-bad-ink); }

/* --- действия раздела в верхней строке ---------------------------------- */

.sb-topbar [data-sb-topbar] {
    display: flex;
    align-items: center;
    gap: 8px;
    flex-wrap: wrap;
    min-width: 0;
}

/* Переключатель периода/месяца: три кнопки одной группой. */
.sb-stepper {
    display: inline-flex;
    align-items: stretch;
    border: 1px solid var(--sb-line);
    border-radius: 7px;
    overflow: hidden;
    background: var(--sb-raised);
}

.sb-stepper button {
    min-width: 30px;
    padding: 0 8px;
    border: 0;
    background: transparent;
    color: var(--sb-ink-2);
    font-family: inherit;
    font-size: 15px;
    line-height: 1;
    cursor: pointer;
}

.sb-stepper button:hover { background: var(--sb-accent-soft); color: var(--sb-ink); }

.sb-stepper .sb-stepper-label {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-width: 132px;
    padding: 0 10px;
    border-left: 1px solid var(--sb-line);
    border-right: 1px solid var(--sb-line);
    font-size: 12.5px;
    font-weight: 600;
    color: var(--sb-ink);
    font-variant-numeric: tabular-nums;
    white-space: nowrap;
}

/* --- легенда ------------------------------------------------------------ */

.sb-legend {
    display: flex;
    flex-wrap: wrap;
    gap: 6px 16px;
    align-items: center;
    font-size: 11.5px;
    color: var(--sb-ink-3);
}

.sb-legend span {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    white-space: nowrap;
}

.sb-legend i {
    width: 8px;
    height: 8px;
    border-radius: 2px;
    flex: 0 0 auto;
}

/* --- модальное окно раздела --------------------------------------------- */

.sb-modal {
    position: fixed;
    inset: 0;
    z-index: 900;
    display: none;
    align-items: center;
    justify-content: center;
    padding: 18px;
    background: rgba(4, 10, 20, 0.62);
}

.sb-modal.is-open { display: flex; }

.sb-modal-box {
    width: min(720px, 100%);
    max-height: 86vh;
    display: flex;
    flex-direction: column;
    background: var(--sb-card);
    border: 1px solid var(--sb-line);
    border-radius: 12px;
    box-shadow: var(--sb-shadow);
    overflow: hidden;
}

.sb-modal-head {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 13px 16px;
    border-bottom: 1px solid var(--sb-line);
}

.sb-modal-head h2 {
    margin: 0;
    flex: 1 1 auto;
    font-size: 14px;
    font-weight: 700;
    color: var(--sb-ink);
}

.sb-modal-body { padding: 14px 16px; overflow: auto; }

/* Форма внутри модального окна: те же поля и тот же шаг, что в разделах.
   Отдельный класс нужен потому, что тело окна бывает и списком, и текстом -
   колонку с шагом 12px просит именно форма. */
.sb-modal-body.sb-form {
    display: flex;
    flex-direction: column;
    gap: 12px;
}

/* .sb-field задумано для строки фильтров и растёт (flex: 1 1 160px). В
   колонке формы это растягивало каждое поле на свободную высоту окна:
   между подписью и полем зияло по полсотни пикселей. В колонке поле
   занимает ровно свою высоту, в .sb-form-row - делит ширину как прежде. */
.sb-modal-body.sb-form > .sb-field { flex: 0 0 auto; }

/* Два коротких поля в строку; на узком экране разъезжаются сами. */
.sb-form-row {
    display: flex;
    flex-wrap: wrap;
    gap: 12px;
}

.sb-modal-foot {
    display: flex;
    justify-content: flex-end;
    gap: 8px;
    padding: 12px 16px;
    border-top: 1px solid var(--sb-line);
}

@media (max-width: 720px) {
    .sb-stats { grid-template-columns: repeat(auto-fit, minmax(130px, 1fr)); }
    .sb-table-wrap { max-height: none; }
    .sb-field { flex: 1 1 100%; }
}

/* ==========================================================================
   Рабочий календарь: доводка раскладки.

   До правки страница тратила почти 390px до первой строки таблицы: своя
   строка с навигацией по месяцу, ряд фильтров, отдельная панель выгрузки в
   Excel, панель кистей и панель легенды - пять блоков подряд. Таблица, ради
   которой сюда приходят, начиналась ниже сгиба экрана.

   Теперь: месяц - в верхней строке оболочки, фильтры и выгрузка - одна
   строка (выгрузка под раскрывашкой), легенда - тонкая строка без панели,
   панель действий прижата к низу и появляется только когда есть что делать.
   ========================================================================== */

/* Раскладка стала линейной: раньше здесь была сетка из областей, где под
   выгрузку отводилась вторая колонка на всю высоту фильтров. */
.page > .content {
    display: flex !important;
    flex-direction: column;
    gap: 10px !important;
    grid-template-columns: none !important;
    grid-template-areas: none !important;
}

.page > .topbar { display: none; }

/* --- строка фильтров --- */

.panel.filters {
    display: flex !important;
    flex-wrap: wrap;
    align-items: center;
    gap: 8px 10px;
}

.panel.filters select { min-width: 160px; max-width: 240px; }
.wf-spacer { flex: 1 1 auto; }

/* Признак режима: «только просмотр» обязан быть заметен, иначе клик по
   ячейке молча ничего не делает и это читается как поломка. */
.wf-access {
    padding: 2px 9px;
    border-radius: 999px;
    border: 1px solid var(--sb-line);
    font-size: 11px;
    font-weight: 600;
    white-space: nowrap;
}

.wf-access.is-edit {
    border-color: var(--sb-ok-bd);
    background: var(--sb-ok-bg);
    color: var(--sb-ok-ink);
}

.wf-access.is-view {
    border-color: var(--sb-warn-bd);
    background: var(--sb-warn-bg);
    color: var(--sb-warn-ink);
}

/* --- выгрузка под раскрывашкой --- */

.wf-export { position: relative; flex: 0 0 auto; }

.wf-export > summary {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    min-height: 34px;
    padding: 0 13px;
    border: 1px solid var(--sb-line);
    border-radius: 7px;
    background: var(--sb-raised);
    color: var(--sb-ink-2);
    font-size: 12.5px;
    font-weight: 600;
    cursor: pointer;
    list-style: none;
    white-space: nowrap;
}

.wf-export > summary::-webkit-details-marker { display: none; }
.wf-export > summary::after { content: "▾"; font-size: 10px; opacity: 0.7; }
.wf-export[open] > summary::after { content: "▴"; }
.wf-export > summary:hover { border-color: var(--sb-accent-line); color: var(--sb-ink); }

.wf-export-body {
    position: absolute;
    right: 0;
    top: calc(100% + 6px);
    z-index: 40;
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 8px;
    padding: 12px;
    border: 1px solid var(--sb-line);
    border-radius: 10px;
    background: var(--sb-card);
    box-shadow: var(--sb-shadow);
}

.wf-export-body select { min-width: 96px; }

.export-range {
    display: flex;
    align-items: center;
    gap: 6px;
}

.export-range-label {
    font-size: 10px;
    font-weight: 700;
    letter-spacing: 0.07em;
    text-transform: uppercase;
    color: var(--sb-ink-3);
}

/* --- легенда --- */

/* Легенда - подпись к таблице, а не самостоятельный раздел: панель вокруг
   неё занимала целую полосу экрана. */
.legend {
    display: flex !important;
    flex-wrap: wrap;
    align-items: center;
    gap: 6px 14px;
    padding: 0 2px !important;
    background: transparent !important;
    border: 0 !important;
}

.legend-item {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    font-size: 11px;
    color: var(--sb-ink-3);
    white-space: nowrap;
}

.legend .swatch {
    width: 9px;
    height: 9px;
    border-radius: 2px;
    flex: 0 0 auto;
}

.legend .swatch.duty {
    background: transparent;
    border: 1px dashed var(--sb-ink-3);
}

/* Был белый квадрат с красной рамкой, заданный прямо в разметке: на тёмном
   фоне он светился дырой и не совпадал с цветом конфликта в таблице. */
.legend .swatch.conflict {
    background: var(--sb-bad-bg);
    border: 1px solid var(--sb-bad-ink);
}

/* --- панель действий --- */

/* Прижата к низу окна: выбирают даты в таблице, а кнопка «Применить» была
   вверху страницы - приходилось прокручивать туда-обратно. */
.panel.status-brushbar {
    position: sticky;
    bottom: 0;
    z-index: 30;
    display: flex !important;
    flex-wrap: wrap;
    align-items: center;
    gap: 8px;
    order: 99;
    box-shadow: 0 -10px 22px -14px rgba(0, 0, 0, 0.55);
}

/* Кисти занимают всю первую строку, вторая - комментарий, подпись и
   кнопки. Без явного переноса «Очистить выбор» уезжала на третью строку и
   панель забирала у таблицы ещё сорок пикселей. */
.panel.status-brushbar .brush-actions { flex: 1 0 100%; }

.panel.status-brushbar #brushComment {
    flex: 0 1 280px;
    min-width: 150px;
}

.brush-summary {
    flex: 1 1 120px;
    min-width: 0;
    font-size: 11.5px;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

/* Подпись про выбор важнее, когда выбор есть: пока его нет, она тихая. */
.panel.status-brushbar #brushApplyAction:not(:disabled) + #brushClearSelection,
.panel.status-brushbar #brushApplyAction:not(:disabled) { font-weight: 700; }

/* Пока ничего не выбрано, «Применить» ничего не сделает - подпись рядом
   объясняет это словами, а не только серой кнопкой. */
.panel.status-brushbar .brush-actions {
    display: flex;
    flex-wrap: wrap;
    gap: 6px;
}

/* --- таблица --- */

/* Таблица получает всю оставшуюся высоту: раньше её собственный
   max-height не зависел от того, сколько места отняли панели сверху. */
.panel.calendar-panel {
    flex: 1 1 auto;
    min-height: 0;
    padding: 0 !important;
    overflow: hidden;
}

.panel.calendar-panel .table-wrap {
    max-height: calc(100vh - 250px);
    overflow: auto;
}

@media (max-width: 900px) {
    .wf-export-body { right: auto; left: 0; }
    .panel.filters select { max-width: none; flex: 1 1 140px; }

    /* Таблица оставляет место панели действий и сама прокручивается внутри:
       иначе тридцать две строки уезжали вниз, а «Применить» оказывалось за
       ними - выбрал ячейки и листай весь календарь, чтобы их применить. */
    .panel.calendar-panel .table-wrap { max-height: calc(100vh - 330px); }

    /* Панель остаётся прижатой к низу и на телефоне. Раньше здесь стояло
       position: static - ровно та проблема, ради которой панель и делали
       липкой. Кисти умещаются в одну строку с горизонтальной прокруткой,
       чтобы панель не отъедала треть экрана. */
    .panel.status-brushbar {
        position: sticky;
        bottom: 0;
        padding: 8px 10px;
        gap: 6px;
    }

    .panel.status-brushbar .brush-actions {
        flex: 1 0 100%;
        flex-wrap: nowrap;
        overflow-x: auto;
        overscroll-behavior-x: contain;
        padding-bottom: 2px;
    }

    .panel.status-brushbar .brush-action { flex: 0 0 auto; }
    .panel.status-brushbar #brushComment { flex: 1 1 100%; }
    .panel.status-brushbar #brushSelectionSummary { flex: 1 1 100%; }
    .panel.status-brushbar #brushApplyAction,
    .panel.status-brushbar #brushClearSelection { flex: 1 1 calc(50% - 3px); }
}


/* ==========================================================================
   Телефон: чип чек-ина в верхней строке.

   Правила чипа объявлены выше и помечены !important, поэтому мобильные
   переопределения обязаны идти после них - иначе побеждает width: auto и
   max-width: 340px, и на экране в 390px чип вставал узким столбиком у
   правого края: иконка и текст в одну строку, кнопка «Изменить» - во
   вторую, а слева оставалось полтораста пикселей пустоты.
   ========================================================================== */

@media (max-width: 760px) {
    #sbTopSlot > .sb-pulse-slot {
        flex: 1 1 100% !important;
        width: 100%;
    }

    .sb-topbar .checkin-pulse {
        width: 100% !important;
        max-width: none !important;
        min-height: 44px !important;
        padding: 6px 8px 6px 10px !important;
        gap: 8px !important;
        /* index.html на узком экране схлопывает чип в две колонки и уводит
           кнопку в отдельную строку (grid-column: 1 / -1). Это верно для
           прежнего широкого блока в шапке страницы, но в строке оболочки
           даёт чип в две строки. Здесь он снова одна строка. */
        grid-template-columns: auto minmax(0, 1fr) auto !important;
    }

    /* Кнопка 28px высотой - не цель для пальца. */
    .sb-topbar .checkin-action {
        grid-column: auto !important;
        width: auto !important;
        min-height: 40px;
        padding: 0 14px !important;
    }

    /* Цели нажатия у общих примитивов. Блок обязан идти после их базовых
       правил: .sb-btn и .sb-stepper объявлены ниже по файлу, и более ранний
       мобильный @media они просто перебивали - переключатель месяца
       оставался 19 пикселей высотой. */
    .sb-btn,
    .sb-ms-btn { min-height: 40px; }
    .sb-btn.is-small { min-height: 36px; }

    .sb-stepper { min-height: 40px; }
    .sb-stepper button { min-width: 44px; font-size: 18px; }
    .sb-stepper .sb-stepper-label { min-width: 0; flex: 1 1 auto; }
}
