/* =========================================================================
   TamosEdu — веб-кабинет сотрудников

   Оформление рабочего инструмента, а не витрины. Секретарь смотрит в этот
   экран весь день и переписывает данные с бумаги, поэтому решения приняты
   в таком порядке: сначала читаемость, потом спокойствие фона, и только
   потом всё остальное.

   ЦВЕТ НЕСЁТ СМЫСЛ. Акцент — один и означает «сюда можно нажать».
   Жёлтый, красный и зелёный означают состояние записи и больше ничего;
   украшать ими нельзя, иначе они перестанут работать как предупреждение.

   ГЛУБИНА — ЕДВА ЗАМЕТНАЯ. Тени мягкие и в два слоя: близкая даёт
   очертание, дальняя — отрыв от фона. Резкая тень на светлом фоне
   выглядит грязно и утомляет за день сильнее, чем кажется.
   ========================================================================= */

:root {
    color-scheme: light;

    /* Поверхности: фон холоднее и темнее карточек, чтобы белое поле
       читалось как лист бумаги, а не сливалось со страницей. */
    --bg:            #eef1f5;
    --surface:       #ffffff;
    --surface-sunken:#f4f6f9;
    --surface-raised:#ffffff;
    --border:        #dfe4ea;
    --border-strong: #c4ccd6;
    /* Рамка поля ввода, флажка, переключателя. Темнее --border-strong:
       рамка — единственное, по чему видно, что сюда можно писать, и ей
       нужен контраст 3 к фону (WCAG 1.4.11). #c4ccd6 давал 1,6 — пустое
       поле на белом читалось как пустое место. Декоративные рамки
       (карточки, пунктир) остаются на --border-strong. */
    --border-field:  #858f9e;

    --ink:           #131a24;
    --ink-soft:      #48566a;
    /* Приглушённый текст: 5,04 на белом и 4,66 на подложке. Прежний
       #8592a3 давал 3,16 и 2,92 — то есть подписи под заголовками и
       пояснения под полями формально были нечитаемы, а на плохом мониторе
       и фактически. Требование к обычному тексту — 4,5. */
    --ink-muted:     #64707f;

    /* Палитра — из логотипа Тамос: три шестигранника, красный, зелёный и
       синий. Каждому цвету — своя работа, и распределены они не поровну:

         СИНИЙ  — всё нажимаемое: кнопки, ссылки, отбор, обводка фокуса;
         КРАСНЫЙ — имя школы и опасность, больше ничего;
         ЗЕЛЁНЫЙ — состояние «хорошо»: подписан, совпало, получилось.

       Почему акцентом не красный, хотя в логотипе он главный: в учётном
       кабинете красный уже занят — им помечены расторгнутые договоры и
       ошибки. Сделай красным ещё и кнопки — и каждая кнопка будет кричать
       об опасности, а каждый «расторгнут» выглядеть нажимаемым. Синий из
       того же логотипа несёт фирменный цвет, ни с чем не споря. */
    --accent:        #3253a8;
    --accent-hover:  #29438c;
    --accent-soft:   #e9eef9;
    --accent-ink:    #1f356f;
    --accent-ring:   rgb(50 83 168 / 20%);
    /* Текст поверх акцентной заливки. В светлой теме акцент тёмный, и
       белый на нём читается; в тёмной акцент светлый, и белый по нему
       даёт контраст 2,2 при норме 4,5 — то есть не читается вовсе.
       Поэтому цвет надписи задан токеном, а не словом «белый». */
    --on-accent:     #ffffff;

    --warn:          #b45309;
    --warn-soft:     #fdf4e8;
    /* Красный и зелёный состояний подведены под оттенки логотипа, но
       затемнены: фирменный #e3242b на белом даёт 4,0 при норме 4,5 —
       значком годится, текстом нет. */
    --danger:        #c11f1a;
    --danger-soft:   #fceceb;
    --ok:            #2b7d2f;
    --ok-soft:       #e9f5e7;
    /* Текст поверх красной заливки (кнопка «Да, удалить», число на
       колокольчике) — по той же причине, что --on-accent: в тёмной теме
       красный светлый, и белый по нему даёт 2,3 при норме 4,5. */
    --on-danger:     #ffffff;

    /* Подложки сумм «меньше / больше / посмотреть» (ДЕНЬГИ В ЦВЕТЕ).
       Плотнее --ok-soft и соседей: те почти одной светлоты (1,02 между
       собой), и зелёную ячейку от красной было не отличить. Цвет — не
       единственный признак: у суммы ещё и значок ▼ ▲ !. */
    --sum-cut-bg:    #d6eed2;
    --sum-over-bg:   #f9d6d3;
    --sum-watch-bg:  #fbe2bf;

    /* Те же цвета тройкой чисел — для полупрозрачных оттенков MudBlazor:
       rgba(var(--accent-rgb), .06). Меняя цвет выше, поменяйте и тройку. */
    --accent-rgb:    50, 83, 168;
    --accent-ink-rgb:31, 53, 111;
    --ink-rgb:       19, 26, 36;
    --ink-soft-rgb:  72, 86, 106;
    --ink-muted-rgb: 100, 112, 127;
    --surface-rgb:   255, 255, 255;
    --border-rgb:    223, 228, 234;
    --ok-rgb:        43, 125, 47;
    --warn-rgb:      180, 83, 9;
    --danger-rgb:    193, 31, 26;

    /* Чистые цвета логотипа — только для знака-эмблемы. От темы не
       зависят: знак один и тот же на любом фоне, как и положено знаку. */
    --tamos-red:     #e3242b;
    --tamos-green:   #3ea23c;
    --tamos-blue:    #3253a8;

    /* Красный слова «Tamos» в шапке. Одной семьи с --danger, но живёт
       отдельно: смена оттенка опасности не должна перекрашивать имя. */
    --brand-word:    #c11f1a;

    /* Шкала шрифтов: шесть размеров на весь кабинет. Раньше в файле
       жило шестьдесят разных значений — 0,78, 0,8, 0,82, 0,83rem и так
       далее, — и соседние подписи различались на полпикселя без смысла.
       Насыщенности — только 400, 600 и 700. Вне шкалы оставлены значки
       и иконки (их размер задаёт кружок или глиф), размеры в em внутри
       строки и лист акта для печати в пунктах. */
    --fs-title:   26px;    /* заголовок страницы, крупные цифры — 600 */
    --fs-section: 17px;    /* заголовок блока — 600 */
    --fs-body:    15px;    /* основной текст, кнопки, поля — 400 */
    --fs-table:   14px;    /* ячейки таблиц — 400 */
    --fs-small:   13px;    /* пояснения и подсказки — 400 */
    --fs-label:   12.5px;  /* подписи полей, шапки столбцов, метки — 600 */

    /* Шаг отступов кратен четырём: глазу нужна повторяемость, а не
       подобранные на глаз значения. */
    --s1: 4px;
    --s2: 8px;
    --s3: 12px;
    --s4: 16px;
    --s5: 24px;
    --s6: 32px;
    --s7: 48px;

    --radius:        12px;
    --radius-sm:     8px;
    --radius-pill:   999px;

    --shadow:        0 1px 2px rgb(19 26 36 / 5%),
                     0 4px 12px -4px rgb(19 26 36 / 8%);
    --shadow-lift:   0 2px 4px rgb(19 26 36 / 7%),
                     0 16px 32px -12px rgb(19 26 36 / 16%);

    --sans: "Segoe UI Variable Text", "Segoe UI", Roboto, "Helvetica Neue",
            system-ui, sans-serif;
    --mono: "Cascadia Mono", "Consolas", "SF Mono", ui-monospace, monospace;
}

/* ЧЬЁ СЛОВО ПОСЛЕДНЕЕ.

   Оформление выбирается в двух местах, и порядок правил ниже задаёт, кто
   кого перебивает:

     1. :root                     — светлые значения, основа;
     2. @media prefers-color-scheme — тёмные, если так решено в системе,
        но ТОЛЬКО когда человек не выбрал светлое явно;
     3. :root[data-theme="dark"]  — тёмные, если человек выбрал их сам.

   Атрибут data-theme проставляет сервер в App.razor до отправки страницы.
   Значение «auto» означает «человек не выбирал» и под условие :not() не
   попадает, поэтому системная настройка продолжает работать как прежде.

   Тёмные значения ниже повторены дважды намеренно: в чистом CSS нельзя
   объявить набор переменных один раз и применить к двум разным условиям,
   а препроцессор ради двадцати строк заводить незачем. */

@media (prefers-color-scheme: dark) {
    /* Системная тёмная — кроме тех, кто выбрал светлое сам. */
    :root:not([data-theme="light"]) {
        color-scheme: dark;

        --bg:            #0f141a;
        --surface:       #181e26;
        --surface-sunken:#131920;
        --surface-raised:#1e252e;
        --border:        #29313b;
        --border-strong: #3a4451;
        --border-field:  #66717f;

        --ink:           #e9eef4;
        --ink-soft:      #aab6c4;
        --ink-muted:     #7b8898;

        --accent:        #8caaf2;
        --accent-hover:  #a3bcf6;
        --accent-soft:   #1d2842;
        --accent-ink:    #b9c9f8;
        --accent-ring:   rgb(140 170 242 / 26%);
        --on-accent:     #0c1730;

        --warn:          #e2a862;
        --warn-soft:     #2c2517;
        --danger:        #f09087;
        --danger-soft:   #2e1c1b;
        --ok:            #6ec468;
        --ok-soft:       #172b15;
        --on-danger:     #2a0f0d;

        --sum-cut-bg:    #1f3a1c;
        --sum-over-bg:   #4a2220;
        --sum-watch-bg:  #45361a;

        --accent-rgb:    140, 170, 242;
        --accent-ink-rgb:185, 201, 248;
        --ink-rgb:       233, 238, 244;
        --ink-soft-rgb:  170, 182, 196;
        --ink-muted-rgb: 123, 136, 152;
        --surface-rgb:   24, 30, 38;
        --border-rgb:    41, 49, 59;
        --ok-rgb:        110, 196, 104;
        --warn-rgb:      226, 168, 98;
        --danger-rgb:    240, 144, 135;

        --brand-word:    #f2887f;

        --shadow:        0 1px 2px rgb(0 0 0 / 45%),
                         0 4px 12px -4px rgb(0 0 0 / 55%);
        --shadow-lift:   0 2px 4px rgb(0 0 0 / 50%),
                         0 16px 32px -12px rgb(0 0 0 / 65%);
    }
}

/* Выбрана тёмная вручную. */
:root[data-theme="dark"] {
        color-scheme: dark;

        --bg:            #0f141a;
        --surface:       #181e26;
        --surface-sunken:#131920;
        --surface-raised:#1e252e;
        --border:        #29313b;
        --border-strong: #3a4451;
        --border-field:  #66717f;

        --ink:           #e9eef4;
        --ink-soft:      #aab6c4;
        --ink-muted:     #7b8898;

        --accent:        #8caaf2;
        --accent-hover:  #a3bcf6;
        --accent-soft:   #1d2842;
        --accent-ink:    #b9c9f8;
        --accent-ring:   rgb(140 170 242 / 26%);
        --on-accent:     #0c1730;

        --warn:          #e2a862;
        --warn-soft:     #2c2517;
        --danger:        #f09087;
        --danger-soft:   #2e1c1b;
        --ok:            #6ec468;
        --ok-soft:       #172b15;
        --on-danger:     #2a0f0d;

        --sum-cut-bg:    #1f3a1c;
        --sum-over-bg:   #4a2220;
        --sum-watch-bg:  #45361a;

        --accent-rgb:    140, 170, 242;
        --accent-ink-rgb:185, 201, 248;
        --ink-rgb:       233, 238, 244;
        --ink-soft-rgb:  170, 182, 196;
        --ink-muted-rgb: 123, 136, 152;
        --surface-rgb:   24, 30, 38;
        --border-rgb:    41, 49, 59;
        --ok-rgb:        110, 196, 104;
        --warn-rgb:      226, 168, 98;
        --danger-rgb:    240, 144, 135;

        --brand-word:    #f2887f;

        --shadow:        0 1px 2px rgb(0 0 0 / 45%),
                         0 4px 12px -4px rgb(0 0 0 / 55%);
        --shadow-lift:   0 2px 4px rgb(0 0 0 / 50%),
                         0 16px 32px -12px rgb(0 0 0 / 65%);
    }

/* ---------- MudBlazor: совместимость со старыми экранами ----------

   MudBlazor.min.css подключён раньше этого файла и сбрасывает оформление
   элементов браузера: у всех — отступы и рамку (* { margin: 0; padding: 0;
   border-style: solid }), у полей и кнопок — шрифт (inherit), у кнопок —
   вид целиком (display: inline-flex, цвет, фон). Экраны кабинета,
   написанные до MudBlazor, на эти умолчания рассчитывают: после сброса
   поле поиска вырастало на три пикселя, кнопки-фильтры становились
   flex-блоками, и всё ниже съезжало.

   Правила ниже возвращают браузерные умолчания — только элементам БЕЗ
   классов mud-*: компоненты MudBlazor остаются со своим оформлением.
   :where() не добавляет точности, поэтому любое правило этого файла,
   в том числе стоящее ниже, по-прежнему сильнее этих. Когда все экраны
   переедут на MudBlazor, блок можно будет убрать. */

*:where(:not([class*="mud-"])) {
    margin: revert;
    padding: revert;
    border-style: revert;
    border-width: revert;
}

input:where(:not([class*="mud-"])),
button:where(:not([class*="mud-"])),
select:where(:not([class*="mud-"])),
optgroup:where(:not([class*="mud-"])),
textarea:where(:not([class*="mud-"])) {
    font-family: revert;
    font-size: revert;
    line-height: revert;
}

button:where(:not([class*="mud-"])) {
    display: revert;
    align-items: revert;
    justify-content: revert;
    vertical-align: revert;
    position: revert;
    user-select: revert;
    color: revert;
    background-color: revert;
    border-radius: revert;
    appearance: revert;
    cursor: revert;
}

label:where(:not([class*="mud-"])) { display: revert; }

a:where(:not([class*="mud-"])):focus-visible,
button:where(:not([class*="mud-"])):focus { outline: revert; }

/* Окно «Соединение потеряно» (ReconnectModal): у MudBlazor для него свои
   правила — фон, шрифт и отступы кнопок, часть с !important. Возвращаем
   вид из ReconnectModal.razor.css. Фон и текст — из токенов: белое окно
   в тёмной теме получало светлый системный текст и не читалось. */
#components-reconnect-modal {
    z-index: revert !important;
    background-color: var(--surface) !important;
    color: var(--ink);
}

#components-reconnect-modal button {
    font-size: revert;
    font-weight: revert;
    line-height: revert;
    letter-spacing: revert;
    text-transform: revert;
    min-width: revert;
    transition: revert;
    margin: revert !important;
}

* { box-sizing: border-box; }

html, body {
    margin: 0;
    padding: 0;
    height: 100%;
}

body {
    background: var(--bg);
    color: var(--ink);
    font-family: var(--sans);
    font-size: var(--fs-body);
    line-height: 1.55;
    -webkit-font-smoothing: antialiased;
    text-rendering: optimizeLegibility;
}

/* Заголовки чуть плотнее по межбуквенному просвету: крупный текст с
   обычным просветом кажется рыхлым. */
h1, h2, h3 { margin: 0; font-weight: 600; letter-spacing: -0.018em; }
h1 { font-size: var(--fs-title); line-height: 1.25; }
h2 { font-size: var(--fs-section); letter-spacing: -0.01em; }
h3 { font-size: var(--fs-body); letter-spacing: -0.005em; }

/* Цифры в одну ширину везде, где их сравнивают глазами: суммы, ИИН,
   счётчики. Пропорциональные цифры в столбце «пляшут». */
.iin, .stat-value, .class-count strong, td, th { font-variant-numeric: tabular-nums; }

a { color: var(--accent); text-decoration: none; }
a:hover { text-decoration: underline; }

/* ---------- каркас ---------- */

.shell { display: flex; min-height: 100vh; }

/*  Меню прилипает к окну и прокручивается само: раньше оно уезжало
    вверх вместе с длинным реестром, и чтобы перейти в другой раздел из
    середины списка, приходилось листать к началу страницы. Ширина —
    переменной: её же берёт двор за «Приёмной» (Home.razor.css), чтобы
    не залезть под меню, свёрнутое оно или нет. */
:root { --sidebar-w: 236px; }
:root:has(.sidebar-mini) { --sidebar-w: 64px; }

.sidebar {
    width: var(--sidebar-w);
    flex: 0 0 var(--sidebar-w);
    background: var(--surface);
    border-right: 1px solid var(--border);
    display: flex;
    flex-direction: column;
    padding: var(--s4) var(--s3);
    gap: var(--s5);
    position: sticky;
    top: 0;
    align-self: flex-start;
    height: 100vh;
    overflow-y: auto;
    overscroll-behavior: contain;
}

/* Кнопка «свернуть до значков» — справа от знака; на телефоне её нет. */
.nav-mini-toggle {
    margin-left: auto;
    display: grid;
    place-items: center;
    width: 30px;
    height: 30px;
    padding: 0;
    border: 0;
    border-radius: var(--radius-sm);
    background: none;
    color: var(--ink-muted);
    cursor: pointer;
}

.nav-mini-toggle:hover { background: var(--surface-sunken); color: var(--ink); }
.nav-mini-toggle:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.nav-mini-toggle .mud-icon-root { font-size: 20px; }

/*  СВЁРНУТОЕ ДО ЗНАЧКОВ. Остаются знак, значки пунктов (подпись — в
    подсказке при наведении, её ставит app.js, и для чтения с экрана —
    скрытым текстом), колокольчик и кружок с инициалами. Разделы
    разделены чертой вместо заголовков; тема, окошко двора, «Сменить
    пароль» и «Выйти» — в развёрнутом меню. */
@media (min-width: 861px) {
    .sidebar.sidebar-mini { padding: var(--s3) var(--s2); gap: var(--s4); }
    .sidebar-mini .brand { flex-direction: column; gap: var(--s2); padding: 0; }
    .sidebar-mini .brand-text { display: none; }
    .sidebar-mini .nav-mini-toggle { margin: 0; transform: scaleX(-1); }
    .sidebar-mini .nav-group > summary.nav-label { display: none; }
    .sidebar-mini .nav-group:not(:first-child) { border-top: 1px solid var(--border); padding-top: var(--s1); margin-top: 2px; }
    .sidebar-mini .nav .mud-nav-link { justify-content: center; gap: 0; padding: var(--s2) 0; }
    .sidebar-mini .nav .mud-nav-link .mud-nav-link-text {
        position: absolute;
        width: 1px;
        height: 1px;
        overflow: hidden;
        clip: rect(0 0 0 0);
        white-space: nowrap;
    }
    .sidebar-mini .sidebar-foot { padding: var(--s2) 0 0; }
    .sidebar-mini .season-window,
    .sidebar-mini .who-actions,
    .sidebar-mini .who > div:last-child,
    .sidebar-mini .bell-text { display: none; }
    .sidebar-mini .who { justify-content: center; }
    .sidebar-mini .bell { justify-content: center; gap: var(--s1); padding: var(--s2) 0; }
}

.brand {
    display: flex;
    align-items: center;
    gap: var(--s3);
    padding: var(--s1) var(--s2) 0;
}

/* Знак — три шестигранника логотипа, отрисованные в BrandMark.razor.
   Размер приходит переменной --mark оттуда же. */
.brand-mark {
    width: var(--mark, 32px);
    height: var(--mark, 32px);
    flex: 0 0 var(--mark, 32px);
    display: block;
}

.brand-text { display: flex; flex-direction: column; line-height: 1.2; }
.brand-name { font-weight: 600; font-size: var(--fs-body); }
.brand-sub { font-size: var(--fs-small); color: var(--ink-muted); white-space: nowrap; }

/* «Tamos» — фирменным красным, «Edu» — обычными чернилами. */
.wordmark-t { color: var(--brand-word); }

.nav { display: flex; flex-direction: column; gap: 2px; }

.nav-label {
    font-size: var(--fs-label);
    text-transform: uppercase;
    letter-spacing: 0.06em;
    color: var(--ink-muted);
    padding: var(--s2) var(--s2) var(--s1);
    font-weight: 600;
    display: block;
    line-height: inherit;
}

/* Поле поиска с историей за день (SearchField.razor): список «Сегодня
   искали» — под полем, поверх таблицы. */
.search-field { position: relative; min-width: 0; }
.search-field > .mud-input-control { width: 100%; }
.search-history {
    position: absolute;
    top: calc(100% - 2px);
    left: 0;
    right: 0;
    z-index: 40;
    max-height: 340px;
    overflow-y: auto;
    padding: var(--s1);
    background: var(--surface-raised);
    border: 1px solid var(--border-strong);
    border-radius: var(--radius-sm);
    box-shadow: var(--shadow-lift);
}
.search-history-head {
    padding: var(--s1) var(--s2) var(--s1);
    font-size: var(--fs-label);
    text-transform: uppercase;
    letter-spacing: 0.06em;
    color: var(--ink-muted);
    font-weight: 600;
}
.search-history-item {
    display: flex;
    align-items: center;
    gap: var(--s2);
    padding: var(--s1) var(--s2);
    border-radius: var(--radius-sm);
    color: var(--ink-soft);
    cursor: pointer;
}
.search-history-item:hover,
.search-history-item.active {
    background: var(--accent-soft);
    color: var(--accent-ink);
}
.search-history-item .mud-icon-root { opacity: 0.7; }
.search-history-text {
    flex: 1;
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
.search-history-remove {
    border: 0;
    background: none;
    color: var(--ink-muted);
    font-size: 1.1rem;
    line-height: 1;
    padding: 0 var(--s1);
    border-radius: var(--radius-sm);
    cursor: pointer;
}
.search-history-remove:hover { color: var(--danger); background: var(--danger-soft); }
.search-history-clear {
    display: block;
    width: 100%;
    margin-top: 2px;
    padding: var(--s2) var(--s2);
    border: 0;
    border-top: 1px solid var(--border);
    background: none;
    text-align: left;
    font: inherit;
    font-size: var(--fs-small);
    color: var(--ink-muted);
    cursor: pointer;
}
.search-history-clear:hover { color: var(--ink); }

/* Таблица «Игроки» (игра «Искра»): своя строка — подсвечена. */
.grid-mud tr.row-me > td { background: var(--accent-soft); }

/* Разделы меню сворачиваются (<details>, см. NavMenu.razor): заголовок
   раздела — кнопка с уголком справа; открытый раздел — уголок вниз. */
.nav-group > summary.nav-label {
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding: var(--s2) var(--s2);
    border-radius: var(--radius-sm);
    cursor: pointer;
    list-style: none;
    user-select: none;
    transition: background 0.12s ease, color 0.12s ease;
}
.nav-group > summary.nav-label::-webkit-details-marker { display: none; }
.nav-group > summary.nav-label::after {
    content: "";
    width: 6px;
    height: 6px;
    margin-right: 3px;
    border-right: 1.5px solid currentColor;
    border-bottom: 1.5px solid currentColor;
    transform: rotate(-45deg);
    transition: transform 0.15s ease;
}
.nav-group[open] > summary.nav-label::after { transform: rotate(45deg); }
.nav-group > summary.nav-label:hover {
    background: var(--surface-sunken);
    color: var(--ink);
}
.nav-group > summary.nav-label:focus-visible,
.sidebar .nav .mud-nav-link:focus-visible {
    outline: 2px solid var(--accent);
    outline-offset: -2px;
}
.nav-items {
    display: flex;
    flex-direction: column;
    gap: 2px;
    padding-bottom: var(--s1);
}

/* Пункты меню — MudNavLink. У MudBlazor на них свои правила, местами
   точнее этих и с !important (жирность текущего пункта), поэтому
   селекторы здесь длиннее обычного: иначе вид поменялся бы. */
.sidebar .nav .mud-nav-link {
    display: flex;
    align-items: center;
    gap: var(--s3);
    padding: var(--s2) var(--s2);
    border-radius: var(--radius-sm);
    color: var(--ink-soft);
    font-weight: 400;
    line-height: inherit;
    transition: background 0.12s ease, color 0.12s ease;
}

.sidebar .nav .mud-nav-link:hover:not(.mud-nav-link-disabled) {
    background: var(--surface-sunken);
    color: var(--ink);
    text-decoration: none;
}

.sidebar .nav.mud-navmenu .mud-nav-link.active:not(.mud-nav-link-disabled),
.sidebar .nav.mud-navmenu .mud-nav-link.active:not(.mud-nav-link-disabled):hover:not(.mud-nav-link-disabled),
/* Страница без своего пункта (карточка договора, приём документов)
   подсвечивает пункт, откуда в неё пришли (nav-here, NavMenu.razor).
   Класс ложится на обёртку пункта или на саму ссылку — смотря куда
   MudNavLink его отдаёт; правило — для обоих случаев. */
.sidebar .nav.mud-navmenu .nav-here .mud-nav-link:not(.mud-nav-link-disabled),
.sidebar .nav.mud-navmenu .mud-nav-link.nav-here:not(.mud-nav-link-disabled) {
    background: var(--accent-soft);
    color: var(--accent-ink);
    font-weight: 600 !important;
}

/* Значок — цветом пункта, как был символ до MudBlazor. */
.sidebar .nav .mud-nav-link:not(.mud-nav-link-disabled) .mud-nav-link-icon.mud-nav-link-icon-default {
    color: inherit;
    opacity: 0.85;
    font-size: 18px;
}

/* Отступ между значком и текстом даёт gap выше. */
.sidebar .nav .mud-nav-link .mud-nav-link-text { margin: 0; }

.sidebar-foot { margin-top: auto; padding: var(--s2); border-top: 1px solid var(--border); }
.who { display: flex; align-items: center; gap: var(--s3); }
.who .who-avatar.mud-avatar {
    width: 30px; height: 30px; border-radius: 50%;
    background: var(--surface-sunken); color: var(--ink-soft);
    display: grid; place-items: center; font-size: 12px; font-weight: 600;
    flex: 0 0 30px;
}
.who-name { font-size: var(--fs-small); font-weight: 600; }
.who-role { font-size: var(--fs-small); color: var(--ink-muted); }

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

/*  Колонка одна на все страницы — 1 460 px (N-6 UX-аудита). Было 1 120
    у девяти страниц и 1 460 у остальных: колонка по центру разной
    ширины, и заголовок при переходе прыгал на 170 px. Узкие формы — те
    же 1 460, а их содержимое не шире 1 120 (смена пароля — 560) и
    прижато к левому краю колонки. */
.page {
    padding: var(--s5) var(--s6) 60px;
    max-width: 1460px;
    width: 100%;
    margin: 0 auto;
}
.page:not(.page-wide) > * { max-width: 1120px; }
.page.page-narrow > * { max-width: 560px; }

.page-head { margin-bottom: var(--s5); }

/*  Знак раздела в заголовке страницы — тот же, что в пункте меню слева
    (справочник — SectionIcons).

    ЗАЧЕМ ОН. Заголовки в кабинете похожи: «Начисления» и «Начисления по
    договору», «Оплаты» и «Платежи по договору». Знак различает их
    раньше, чем прочитан заголовок, и подтверждает, что открылось
    именно то, по чему нажали в меню.

    Мягкий квадрат под знаком — того же цвета, что и подсветка текущего
    пункта меню: страница и пункт, из которого в неё пришли, выглядят
    одной парой.                                                       */
h1 .page-icon.mud-icon-root {
    box-sizing: content-box;
    flex: 0 0 auto;
    font-size: 22px;
    width: 1em;
    height: 1em;
    padding: var(--s2);
    border-radius: 10px;
    background: var(--accent-soft);
    color: var(--accent-ink);
    /*  Запас для браузеров без :has() — там заголовок остаётся строкой,
        и знак надо опустить на строку руками.                         */
    vertical-align: -0.34em;
}

h1:has(.page-icon) {
    display: flex;
    align-items: center;
    gap: var(--s3);
}
.page-head p { margin: var(--s2) 0 0; color: var(--ink-soft); max-width: 62ch; }

/* ---------- поиск ---------- */

.search-card {
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    box-shadow: var(--shadow);
    padding: var(--s4);
    margin-bottom: var(--s5);
}

/* Перенос — когда поиск, отбор и кнопка в строку не помещаются (справочник
   «Классы» при окне ~950 px обрезал поиск и кнопку «Добавить»). */
.search-row { display: flex; flex-wrap: wrap; gap: var(--s3); align-items: stretch; }

.search-input-wrap { position: relative; flex: 1; }

.search-input-wrap .glyph {
    position: absolute;
    left: 14px; top: 50%;
    transform: translateY(-50%);
    color: var(--ink-muted);
    pointer-events: none;
    font-size: 1rem;
}

input.search {
    width: 100%;
    padding: var(--s3) var(--s3) var(--s3) var(--s7);
    font-size: var(--fs-section);
    font-family: var(--sans);
    border: 1.5px solid var(--border-field);
    border-radius: var(--radius-sm);
    background: var(--surface);
    color: var(--ink);
    transition: border-color 0.12s ease, box-shadow 0.12s ease;
}

input.search::placeholder { color: var(--ink-muted); }

input.search:focus {
    border-color: var(--accent);
    outline: 2px solid var(--accent);
    outline-offset: 2px;
}

/* Цифры набирают с бумаги — моноширинный шрифт и разрядка снижают опечатки. */
input.search.digits, .iin {
    font-family: var(--mono);
    letter-spacing: 0.06em;
    font-variant-numeric: tabular-nums;
}

.search-meta {
    display: flex;
    align-items: center;
    gap: var(--s2);
    margin-top: var(--s3);
    font-size: var(--fs-small);
    color: var(--ink-soft);
    min-height: 20px;
}

/* ---------- значки состояния ---------- */

.pill {
    display: inline-flex;
    align-items: center;
    gap: var(--s1);
    padding: 2px var(--s2);
    border-radius: 999px;
    font-size: var(--fs-label);
    font-weight: 600;
    line-height: 1.6;
    white-space: nowrap;
}

.pill.ok    { background: var(--ok-soft);     color: var(--ok); }
.pill.warn  { background: var(--warn-soft);   color: var(--warn); }
.pill.bad   { background: var(--danger-soft); color: var(--danger); }
.pill.info  { background: var(--accent-soft); color: var(--accent-ink); }
.pill.mute  { background: var(--surface-sunken); color: var(--ink-muted); }

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

.btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: var(--s2);
    padding: var(--s2) var(--s4);
    border-radius: var(--radius-sm);
    border: 1px solid transparent;
    font: inherit;
    font-weight: 600;
    font-size: var(--fs-body);
    cursor: pointer;
    white-space: nowrap;
    transition: background 0.14s ease, border-color 0.14s ease,
                box-shadow 0.14s ease, transform 0.08s ease;
}

/* Нажатие видно смещением на пиксель — этого достаточно, чтобы палец
   почувствовал отклик, и недостаточно, чтобы страница «дёргалась». */
.btn:active:not(:disabled) { transform: translateY(1px); }

/* Обводка при переходе с клавиатуры. Не убираем её никогда: без неё
   кабинетом нельзя пользоваться, не глядя на мышь.

   ТОЛЬКО ПОЛЯ ВНЕ MUDBLAZOR. У поля MudBlazor рамку рисует не сам
   input, а рамка-обёртка вокруг него. Без этой оговорки кольцо
   ложилось ВНУТРЬ рамки — и в ответ на щелчок поле обводилось дважды,
   рамкой и кольцом внутри неё. Своя обводка полей MudBlazor — ниже, в
   разделе моста.

   ОБВОДКА — ЛИНИЕЙ, А НЕ ТЕНЬЮ. Прежнее мягкое кольцо (акцент на 20 %)
   давало контраст около 1,3 к фону: на хорошем мониторе видно, на
   плохом и при ярком солнце — нет, а клавиатурой работают как раз весь
   день. Сплошная линия акцентного цвета с зазором видна на любом фоне,
   в том числе на кнопке того же синего цвета. */
.btn:focus-visible,
.link-btn:focus-visible,
.mud-button-root:focus-visible,
.mud-nav-link:focus-visible,
.mud-chip:focus-visible,
.confirm-strip:focus-visible,
a:focus-visible,
summary:focus-visible,
input:where(:not([class*="mud-"])):focus-visible,
select:where(:not([class*="mud-"])):focus-visible,
textarea:where(:not([class*="mud-"])):focus-visible {
    outline: 2px solid var(--accent);
    outline-offset: 2px;
    box-shadow: none;
}

/* Режим высокой контрастности Windows: цвета страницы заменяет система,
   и обводку, и рамки надо рисовать её цветами — иначе они пропадают. */
@media (forced-colors: active) {
    .btn:focus-visible,
    .link-btn:focus-visible,
    .mud-button-root:focus-visible,
    .mud-nav-link:focus-visible,
    .mud-chip:focus-visible,
    .confirm-strip:focus-visible,
    a:focus-visible,
    summary:focus-visible,
    input:focus-visible,
    select:focus-visible,
    textarea:focus-visible,
    .mud-input-control:focus-within .mud-input-outlined-border {
        outline: 2px solid Highlight;
        outline-offset: 2px;
    }

    /* Текущий пункт меню и выбранная строка держались на заливке,
       которую система стирает, — им рамка. */
    .sidebar .nav .mud-nav-link.active,
    .sidebar .nav .nav-here .mud-nav-link,
    .sidebar .nav .mud-nav-link.nav-here {
        outline: 2px solid Highlight;
        outline-offset: -2px;
    }

    /* Суммы в цвете: подложки исчезают, остаются значки ▼ ▲ ! — и рамка. */
    .sum-cut, .sum-over, .sum-watch { border: 1px solid CanvasText; }
}

.btn-primary {
    background: var(--accent);
    color: var(--on-accent);
    box-shadow: 0 1px 2px rgb(19 26 36 / 12%);
}
.btn-primary:hover:not(:disabled) { background: var(--accent-hover); }

/* Опасное действие — отмена, расторжение. Красная кнопка появляется
   только на шаге подтверждения, когда человек уже видит, что произойдёт. */
.btn-danger {
    background: var(--danger);
    color: var(--on-danger);
}
.btn-danger:hover:not(:disabled) { filter: brightness(1.08); }

/* Полоска подтверждения смены статуса: дата, что произойдёт, две кнопки. */
/* «Отменить» в ряду смены статуса — неяркая: это исправление ошибки,
   а не обычный ход договора. */
.status-flow .status-cancel { color: var(--ink-muted) !important; }

.confirm-strip {
    padding: var(--s3);
    border: 1px solid var(--border-strong);
    border-radius: var(--radius-sm);
    background: var(--surface-sunken);
    display: grid;
    gap: var(--s3);
}

/* Панель действий длинной формы (FormBar.razor): прилипает к низу окна,
   пока форма на экране. Слева «Отмена», посередине — состояние
   формы, справа — главная кнопка. Тень сверху отделяет её от полей,
   которые уезжают под неё. */
.form-bar {
    position: sticky;
    bottom: 0;
    z-index: 30;
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--s2) var(--s3);
    margin-top: var(--s4);
    padding: var(--s3) var(--s4);
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    box-shadow: 0 -8px 20px -12px rgb(19 26 36 / 28%);
}

.form-bar-state {
    flex: 1 1 220px;
    min-width: 0;
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    gap: 2px var(--s3);
    font-size: var(--fs-table);
    color: var(--ink-soft);
}

/* Точка — тот же знак «не сохранено», что у вкладок редакторов. */
.form-bar-dirty { color: var(--warn); font-weight: 600; }
.form-bar-dirty::before { content: "● "; }

.form-bar-problem { color: var(--ink); }
.form-bar-go { margin-left: var(--s1); font-size: inherit; }

.form-bar-actions {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--s2) var(--s3);
}

.form-bar-actions .keys { margin-left: 0; }

@media print { .form-bar { display: none; } }

/* «* — обязательное поле» — одной строкой над формой (F-3). Звёздочку у
   подписи ставит MudBlazor (Required), здесь — её расшифровка. */
.req-legend { margin: 0 0 var(--s3); font-size: var(--fs-small); color: var(--ink-muted); }
.req-legend .req { color: var(--danger); font-weight: 700; }

/* Сводка ошибок у кнопки в небольших формах (правка ученика, родителя). */
.form-problems { color: var(--danger); font-size: var(--fs-table); }

/* «Уйти без сохранения?» (LeaveGuard.razor) — окно поверх страницы:
   переход стоит, пока человек не ответит. Главная кнопка — «Остаться». */
.leave-backdrop {
    position: fixed;
    inset: 0;
    z-index: 1400;
    background: rgba(15, 23, 42, 0.45);
}

.leave-dialog {
    position: fixed;
    z-index: 1401;
    top: 18vh;
    left: 50%;
    transform: translateX(-50%);
    width: min(440px, calc(100vw - 32px));
    padding: var(--s5);
    background: var(--surface-raised);
    color: var(--ink);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    box-shadow: var(--shadow-lift);
    display: grid;
    gap: var(--s3);
}

.leave-what p { margin: 0 0 var(--s1); color: var(--ink-soft); }
.leave-what ul { margin: 0; padding-left: 1.2em; }
.leave-dialog .actions { margin-top: var(--s2); }

.btn-ghost {
    background: var(--surface);
    color: var(--ink-soft);
    border-color: var(--border-strong);
}
.btn-ghost:hover:not(:disabled) {
    background: var(--surface-sunken);
    color: var(--ink);
    border-color: var(--ink-muted);
}

.btn-small { padding: var(--s1) var(--s3); font-size: var(--fs-table); }

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

/* ---------- карточки результатов ---------- */

.results { display: flex; flex-direction: column; gap: var(--s3); }

.result-group-label {
    font-size: var(--fs-label);
    text-transform: uppercase;
    letter-spacing: 0.05em;
    color: var(--ink-muted);
    font-weight: 600;
    margin: var(--s4) 0 2px;
}

.card {
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    padding: var(--s3) var(--s4);
    display: flex;
    gap: var(--s4);
    align-items: flex-start;
    transition: border-color 0.12s ease, box-shadow 0.12s ease;
}

.card:hover { border-color: var(--border-strong); box-shadow: var(--shadow); }

.avatar {
    width: 40px; height: 40px;
    border-radius: 50%;
    display: grid; place-items: center;
    font-weight: 600; font-size: 0.9rem;
    flex: 0 0 40px;
    background: var(--surface-sunken);
    color: var(--ink-soft);
}

/* Кружок с инициалами — MudAvatar: свой размер у него другой. */
.card-head .avatar.mud-avatar {
    width: 40px;
    height: 40px;
    font-size: 0.9rem;
    font-weight: 600;
}

.avatar.male   { background: #e5eefb; color: #1f4e8c; }
.avatar.female { background: #fceef3; color: #96305c; }

@media (prefers-color-scheme: dark) {
    :root:not([data-theme="light"]) .avatar.male   { background: #1b2b40; color: #8dbaf0; }
    :root:not([data-theme="light"]) .avatar.female { background: #351f2b; color: #f0a2c2; }
}

:root[data-theme="dark"] .avatar.male   { background: #1b2b40; color: #8dbaf0; }
:root[data-theme="dark"] .avatar.female { background: #351f2b; color: #f0a2c2; }

.card-body { flex: 1; min-width: 0; }

.card-title {
    display: flex; align-items: center; gap: var(--s2);
    flex-wrap: wrap;
    font-weight: 600;
    font-size: var(--fs-section);
}

.card-facts {
    display: flex; flex-wrap: wrap;
    gap: var(--s1) var(--s4);
    margin-top: var(--s1);
    font-size: var(--fs-table);
    color: var(--ink-soft);
}

.fact { display: inline-flex; align-items: center; gap: var(--s1); }
.fact-key { color: var(--ink-muted); }

.card-notes { margin-top: var(--s2); display: flex; flex-wrap: wrap; gap: var(--s2); }

.empty {
    text-align: center;
    padding: var(--s7) var(--s4);
    color: var(--ink-muted);
    background: var(--surface);
    border: 1px dashed var(--border-strong);
    border-radius: var(--radius);
}

.empty-glyph { font-size: 2rem; opacity: 0.55; display: block; margin-bottom: var(--s3); }

/* Главная строка пустого экрана и пояснение под ней. Строка говорит,
   чего нет, пояснение — что с этим делать. */
.empty-title { color: var(--ink); }
.empty-note { margin-top: var(--s2); }
.empty-note .actions { justify-content: center; margin-top: var(--s3); }

/* Кружок ожидания стоит на месте знака и той же высоты: ожидание и
   пустота идут подряд, и от разной высоты страница прыгает. */
.empty .empty-spin.mud-progress-circular {
    width: 2.6rem;
    height: 2.6rem;
    margin: 0 auto var(--s3);
    display: block;
}

/*  Знак пустого экрана — значок, а не символ из таблицы Unicode. Символы
    вроде «▦» и «⌕» рисуются шрифтом системы: на одной машине это ровная
    фигура, на другой — то, что нашлось в запасном шрифте. Значок
    приходит вектором и выглядит одинаково везде.

    Размер задаётся здесь: у MudIcon он свой, и без этого правила знак
    получался с текстовую строку.                                       */
.empty .empty-glyph.mud-icon-root {
    font-size: 2.6rem;
    width: 1em;
    height: 1em;
    margin: 0 auto var(--s3);
    color: var(--ink-muted);
}

/* ---------- разное ---------- */

.hint-bad  { color: var(--danger); }
.hint-ok   { color: var(--ok); }
.hint-warn { color: var(--warn); }

.spinner {
    width: 15px; height: 15px;
    border: 2px solid var(--border-strong);
    border-top-color: var(--accent);
    border-radius: 50%;
    animation: spin 0.7s linear infinite;
    flex: 0 0 15px;
}

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

kbd {
    font-family: var(--mono);
    font-size: var(--fs-small);
    padding: 1px var(--s1);
    border: 1px solid var(--border-strong);
    border-bottom-width: 2px;
    border-radius: 4px;
    background: var(--surface-sunken);
    color: var(--ink-soft);
}

/*  Выдвижная часть меню. На широком экране её не видно: пункты и низ
    меню стоят в боковой панели как раньше, обёртка только держит их
    столбиком. Кнопка «☰», затемнение и крестик — только для телефона. */
.nav-drawer {
    display: flex;
    flex-direction: column;
    gap: var(--s5);
    flex: 1;
    min-height: 0;
}

.nav-toggle,
.nav-burger,
.nav-backdrop,
.nav-drawer-head { display: none; }

/* =========================================================================
   МЕНЮ НА ТЕЛЕФОНЕ

   Раньше боковая панель на узком экране ложилась в строку: два десятка
   пунктов уезжали вбок, а низ меню — уведомления, тема, «Сменить
   пароль» и «Выйти» — прятался совсем: выйти с телефона было нечем.

   Теперь сверху полоса: знак, колокольчик и «☰». Меню целиком, с
   подписями разделов и низом, выдвигается слева поверх страницы.
   Состояние держит флажок #nav-open — от него зависит весь вид ниже.
   «☰» и крестик — настоящие кнопки (до них доходят клавишей Tab), их
   нажатие переводит флажок через app.js; затемнение — подпись к
   флажку и закрывает меню без скрипта. Закрывают ещё Esc и переход по
   пункту (app.js).

   Закрытое меню — visibility: hidden: оно не только уехало за край, но
   и выпало из обхода клавишей Tab и из чтения с экрана. Иначе Tab
   водил бы по двум десяткам невидимых пунктов. Скрывается с задержкой
   в длину выезда, чтобы меню успело уехать на глазах.

   Выдвигается сдвигом left, а не transform: колокольчик живёт внутри
   выдвижной части, а на телефоне стоит в верхней полосе position:
   fixed — transform сделал бы его частью выдвижной панели и увёз бы
   вместе с ней.
   ========================================================================= */
@media (max-width: 860px) {
    /* Шапки таблиц липнут ниже верхней полосы, а не прячутся за ней. */
    :root { --sticky-top: 57px; }

    .shell { flex-direction: column; }

    .sidebar {
        position: sticky;
        top: 0;
        z-index: 1100;
        width: 100%;
        flex: none;
        height: auto;
        overflow: visible;
        align-self: stretch;
        flex-direction: row;
        align-items: center;
        gap: var(--s3);
        min-height: 56px;
        padding: var(--s2) var(--s2) var(--s2) var(--s3);
        border-right: none;
        border-bottom: 1px solid var(--border);
    }

    .sidebar .brand { padding: 0; gap: var(--s2); }

    /* Сворачивать до значков на телефоне нечего: меню и так выдвижное. */
    .nav-mini-toggle { display: none; }
    .sidebar .brand-sub { display: none; }
    .sidebar .brand .brand-mark { width: 28px; height: 28px; flex-basis: 28px; }

    .nav-burger {
        display: grid;
        place-items: center;
        width: 44px;
        height: 44px;
        margin-left: auto;
        padding: 0;
        border: 0;
        border-radius: 12px;
        background: none;
        color: var(--ink);
        font: inherit;
        cursor: pointer;
    }

    .nav-burger:hover,
    .nav-close:hover { background: var(--surface-sunken); }

    .nav-burger:focus-visible,
    .nav-close:focus-visible {
        outline: 2px solid var(--accent);
        outline-offset: 2px;
    }

    .nav-backdrop {
        position: fixed;
        inset: 0;
        z-index: 1250;
        background: rgba(15, 23, 42, 0.45);
    }

    .nav-toggle:checked ~ .nav-backdrop { display: block; }

    .nav-drawer {
        position: fixed;
        top: 0;
        bottom: 0;
        left: -340px;
        z-index: 1300;
        width: min(320px, 86vw);
        gap: var(--s3);
        padding: var(--s2) var(--s3) var(--s4);
        background: var(--surface);
        border-right: 1px solid var(--border);
        box-shadow: var(--shadow-lift);
        overflow-y: auto;
        overscroll-behavior: contain;
        visibility: hidden;
        transition: left 0.2s ease, visibility 0s linear 0.2s;
    }

    .nav-toggle:checked ~ .nav-drawer {
        left: 0;
        visibility: visible;
        transition: left 0.2s ease, visibility 0s;
    }

    /* Пока меню открыто, страница под ним не прокручивается. */
    body:has(.nav-toggle:checked) { overflow: hidden; }

    .nav-drawer-head {
        display: flex;
        align-items: center;
        justify-content: space-between;
        padding: 2px 0 var(--s1) var(--s2);
        border-bottom: 1px solid var(--border);
    }

    .nav-close {
        display: grid;
        place-items: center;
        width: 40px;
        height: 40px;
        padding: 0;
        border: 0;
        border-radius: 10px;
        background: none;
        color: var(--ink-soft);
        font: inherit;
        cursor: pointer;
    }

    /* Пункты — крупнее: по ним попадают пальцем. */
    .sidebar .nav .mud-nav-link { padding: var(--s3) var(--s3); font-size: var(--fs-body); }
    /* Заголовок раздела — под палец, как и пункты. */
    .nav-group > summary.nav-label { padding: var(--s3) var(--s3); }

    .sidebar-foot { margin-top: var(--s2); }

    /* Колокольчик — в верхней полосе, рядом с «☰»: число непрочитанных
       видно, не открывая меню. Когда меню открыто, он возвращается на
       своё место внизу меню, со словом «Уведомления», — иначе стоял бы
       вплотную к крестику. */
    .nav-toggle:not(:checked) ~ .nav-drawer .bell {
        position: fixed;
        top: 6px;
        right: 58px;
        z-index: 1150;
        margin: 0;
        padding: var(--s2);
        border-radius: 12px;
        /* Выдвижная часть скрыта целиком, а колокольчик из неё — на виду. */
        visibility: visible;
    }

    .nav-toggle:not(:checked) ~ .nav-drawer .bell .bell-text { display: none; }
    .nav-toggle:not(:checked) ~ .nav-drawer .bell .bell-glyph { font-size: 22px; }

    .page { padding: var(--s4) var(--s3) var(--s7); }
}

#blazor-error-ui {
    display: none;
    position: fixed;
    left: 0; right: 0; bottom: 0;
    z-index: 1000;
    padding: var(--s3) var(--s4);
    background: var(--warn-soft);
    color: var(--warn);
    border-top: 1px solid var(--warn);
    /* MudBlazor оформляет эту полосу по-своему (тень, ширина) — оставляем свою. */
    box-shadow: none;
    width: auto;
}

#blazor-error-ui .dismiss { cursor: pointer; float: right; font-weight: 700; position: static; }

/* =========================================================================
   МАСТЕР ПРИЁМА
   Секретарь переписывает данные с бумаги и почти не отрывает рук от
   клавиатуры. Поэтому: крупные поля, явный порядок обхода, подсказки под
   полем вместо модальных окон, и ни одного поля, которое можно вывести
   из другого.
   ========================================================================= */

.stepper {
    display: flex;
    align-items: center;
    gap: 0;
    margin-bottom: var(--s5);
}

.step {
    display: flex;
    align-items: center;
    gap: var(--s2);
    font-size: var(--fs-table);
    font-weight: 600;
    color: var(--ink-muted);
    white-space: nowrap;
}

.step-num {
    width: 26px; height: 26px;
    border-radius: 50%;
    display: grid; place-items: center;
    font-size: var(--fs-label); font-weight: 700;
    background: var(--surface-sunken);
    color: var(--ink-muted);
    border: 1.5px solid var(--border-strong);
    flex: 0 0 26px;
}

.step.current { color: var(--ink); }
.step.current .step-num {
    background: var(--accent); color: var(--on-accent); border-color: var(--accent);
}

.step.done { color: var(--ink-soft); }
.step.done .step-num {
    background: var(--accent-soft); color: var(--accent-ink); border-color: var(--accent);
}

.step-line {
    flex: 1;
    height: 1.5px;
    background: var(--border-strong);
    margin: 0 var(--s4);
    min-width: 20px;
}

.step-line.done { background: var(--accent); }

.panel {
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    box-shadow: var(--shadow);
    padding: var(--s5);
}

.panel + .panel { margin-top: var(--s4); }

.panel-head {
    display: flex; align-items: baseline; justify-content: space-between;
    gap: var(--s3); margin-bottom: var(--s4);
    padding-bottom: var(--s3);
    border-bottom: 1px solid var(--border);
}
.panel-head .sub { font-size: var(--fs-small); color: var(--ink-muted); }

.grid {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: var(--s4) var(--s4);
}

.grid .wide { grid-column: 1 / -1; }

@media (max-width: 720px) {
    /* minmax(0, …), а не 1fr: иначе длинное значение списка («3Н ·
       Кекилбайулы · Физико-математический · …») распирало колонку, и
       форма правки договора уезжала за край телефона. */
    .grid { grid-template-columns: minmax(0, 1fr); }
    .grid > * { min-width: 0; }
}

.field { display: flex; flex-direction: column; gap: var(--s1); min-width: 0; }

.field label {
    font-size: var(--fs-small);
    font-weight: 600;
    color: var(--ink-soft);
    display: flex; align-items: center; gap: var(--s2);
}

.field label .req { color: var(--danger); font-weight: 700; }

.field input, .field select, .field textarea {
    padding: var(--s2) var(--s3);
    font-size: var(--fs-body);
    font-family: var(--sans);
    border: 1px solid var(--border-field);
    border-radius: var(--radius-sm);
    background: var(--surface);
    color: var(--ink);
    width: 100%;
    transition: border-color 0.14s ease, box-shadow 0.14s ease;
}

.field input:focus, .field select:focus, .field textarea:focus {
    border-color: var(--accent);
    outline: 2px solid var(--accent);
    outline-offset: 2px;
}

.field input.mono {
    font-family: var(--mono);
    letter-spacing: 0.08em;
    font-variant-numeric: tabular-nums;
    font-size: var(--fs-section);
}

.field.bad input, .field.bad select { border-color: var(--danger); }
.field.good input { border-color: var(--ok); }

/* То же для полей MudBlazor: рамку рисует не сам ввод, а обводка. */
.field.bad .mud-input-outlined .mud-input-outlined-border { border-color: var(--danger); }
.field.good .mud-input-outlined .mud-input-outlined-border { border-color: var(--ok); }

.field .help { font-size: var(--fs-small); color: var(--ink-muted); min-height: 1.1em; }
.field .help.bad { color: var(--danger); }
.field .help.ok { color: var(--ok); }
.field .help.warn { color: var(--warn); }

.field input:disabled, .field select:disabled {
    background: var(--surface-sunken);
    color: var(--ink-muted);
    cursor: not-allowed;
}

/* поле, значение которого выведено из ИИН, а не введено руками */
.derived {
    display: flex; align-items: center; gap: var(--s2);
    padding: var(--s2) var(--s3);
    border: 1.5px dashed var(--border-strong);
    border-radius: var(--radius-sm);
    background: var(--surface-sunken);
    color: var(--ink-soft);
    font-size: var(--fs-body);
    min-height: 42px;
}

.derived.filled { border-style: solid; border-color: var(--ok); color: var(--ink); }
.derived .from { margin-left: auto; font-size: var(--fs-small); color: var(--ink-muted); }

/* ---------- предупреждение о найденных совпадениях ---------- */

.alert {
    border-radius: var(--radius);
    padding: var(--s3) var(--s4);
    margin: var(--s4) 0 0;
    border: 1px solid;
    font-size: var(--fs-table);
}

.alert-warn { background: var(--warn-soft); border-color: var(--warn); color: var(--warn); }
.alert-bad  { background: var(--danger-soft); border-color: var(--danger); color: var(--danger); }
.alert-info { background: var(--accent-soft); border-color: var(--accent); color: var(--accent-ink); }

.alert strong { display: block; margin-bottom: var(--s1); font-size: var(--fs-body); }

/* ---------- выбор существующего человека ---------- */

.pick { display: flex; flex-direction: column; gap: var(--s2); margin-top: var(--s3); }

.pick-item {
    display: flex; align-items: flex-start; gap: var(--s3);
    padding: var(--s3) var(--s3);
    border: 1.5px solid var(--border);
    border-radius: var(--radius-sm);
    background: var(--surface);
    cursor: pointer;
    text-align: left;
    font: inherit;
    color: inherit;
    width: 100%;
    transition: border-color 0.12s ease, background 0.12s ease;
}

.pick-item:hover { border-color: var(--accent); background: var(--accent-soft); }

.pick-item.chosen {
    border-color: var(--accent);
    background: var(--accent-soft);
    box-shadow: 0 0 0 1px var(--accent);
}

.pick-radio {
    width: 18px; height: 18px; border-radius: 50%;
    border: 2px solid var(--border-field);
    flex: 0 0 18px; margin-top: 2px;
    display: grid; place-items: center;
}

.pick-item.chosen .pick-radio { border-color: var(--accent); }
.pick-item.chosen .pick-radio::after {
    content: ''; width: 9px; height: 9px; border-radius: 50%; background: var(--accent);
}

.pick-main { min-width: 0; flex: 1; }
.pick-name { font-weight: 600; }
.pick-facts {
    font-size: var(--fs-small); color: var(--ink-soft);
    display: flex; flex-wrap: wrap; gap: 3px var(--s4); margin-top: 3px;
}

/* ---------- сводка на последнем шаге ---------- */

.summary { display: grid; grid-template-columns: repeat(2, minmax(0,1fr)); gap: var(--s4); }
@media (max-width: 720px) { .summary { grid-template-columns: minmax(0, 1fr); } }

.summary-block {
    border: 1px solid var(--border);
    border-radius: var(--radius-sm);
    padding: var(--s3) var(--s4);
    background: var(--surface-sunken);
}

.summary-block h3 { margin-bottom: var(--s3); color: var(--ink-soft); }

.summary-row {
    display: flex; gap: var(--s3); padding: var(--s1) 0;
    font-size: var(--fs-table); border-bottom: 1px solid var(--border);
}
.summary-row:last-child { border-bottom: none; }
.summary-key { color: var(--ink-muted); flex: 0 0 40%; }
.summary-val { flex: 1; min-width: 0; word-break: break-word; }
/* «не указано» — строка, а не пустой экран: общий .empty давал ей отступ
   в 46 точек и пунктирную рамку, и шаг «Проверка» приёма документов
   раздувался пустыми коробками. */
.summary-val.empty {
    color: var(--ink-muted); font-style: italic;
    padding: 0; border: 0; background: none; border-radius: 0; text-align: left;
}

/* ---------- низ мастера ---------- */

.wizard-foot {
    display: flex; align-items: center; gap: var(--s3);
    margin-top: var(--s5); padding-top: var(--s4);
    border-top: 1px solid var(--border);
}

.wizard-foot .spacer { flex: 1; }
.wizard-foot .note { font-size: var(--fs-small); color: var(--ink-muted); }

.done-screen { text-align: center; padding: var(--s6) var(--s4); }
.done-mark {
    width: 56px; height: 56px; border-radius: 50%;
    background: var(--ok-soft); color: var(--ok);
    display: grid; place-items: center; font-size: 1.7rem;
    margin: 0 auto var(--s4);
    line-height: 1;
}

/* =========================================================================
   ВХОД, АДМИНКА
   ========================================================================= */

.auth-screen {
    min-height: 100vh;
    display: grid;
    place-items: center;
    padding: var(--s5);
    background: var(--bg);
}

.auth-card {
    width: 100%;
    max-width: 400px;
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    box-shadow: var(--shadow-lift);
    padding: var(--s5);
    display: flex;
    flex-direction: column;
    gap: var(--s4);
    position: relative;
    overflow: hidden;
}

/* Трёхцветная лента логотипа по верхнему краю. Только здесь: вход —
   парадная дверь, и один фирменный жест ей к лицу; внутри кабинета
   такая полоса мозолила бы глаза весь день. */
.auth-card::before {
    content: '';
    position: absolute;
    top: 0; left: 0; right: 0;
    height: 4px;
    background: linear-gradient(90deg,
        var(--tamos-red)   0 34%,
        var(--tamos-green) 34% 67%,
        var(--tamos-blue)  67% 100%);
}

/* Мультик за карточкой входа (wwwroot/login-scenes): холст во весь экран
   под карточкой. Нажатия проходят сквозь него; без скрипта он пустой, и
   вход выглядит как раньше — на фоне --bg. */
tamos-scene {
    position: fixed;
    inset: 0;
    z-index: 0;
    pointer-events: none;
}

tamos-scene canvas {
    display: block;
    width: 100%;
    height: 100%;
}

.auth-screen > .auth-card { z-index: 1; }

/* В тёмной теме двор приглушён: яркое небо вокруг тёмной карточки
   слепило бы глаза. Тетрадь и совёнок рисуют себя тёмными сами, доска и
   соты тёмные и так. Два правила — по той же причине, что и у самих
   тёмных переменных выше. */
@media (prefers-color-scheme: dark) {
    :root:not([data-theme="light"]) tamos-scene canvas[data-scene="yard"] { filter: brightness(0.7) saturate(0.85); }
}

:root[data-theme="dark"] tamos-scene canvas[data-scene="yard"] { filter: brightness(0.7) saturate(0.85); }

.auth-brand { display: flex; align-items: center; gap: var(--s3); margin-bottom: var(--s1); }
.auth-title { font-weight: 700; font-size: var(--fs-section); }
.auth-sub { font-size: var(--fs-small); color: var(--ink-muted); }

.auth-note {
    font-size: var(--fs-small);
    color: var(--ink-muted);
    border-top: 1px solid var(--border);
    padding-top: var(--s3);
    margin-top: var(--s1);
    line-height: 1.45;
}

/* Вход при работающем сеансе на другом устройстве (Login.razor). */
.auth-card.auth-busy { max-width: 460px; }

/* Заголовок окна «Нет доступа», «Страница не найдена», «Ошибка» — того же
   размера, что заголовок окна входа: окно маленькое, крупный h1 в нём кричит. */
.auth-card h1 { font-size: var(--fs-section); }

.busy-head {
    display: flex;
    gap: var(--s3);
    align-items: flex-start;
    font-weight: 600;
    line-height: 1.35;
    color: var(--warn);
}

.busy-facts {
    display: grid;
    grid-template-columns: auto 1fr;
    gap: var(--s1) var(--s4);
    margin: 0;
    padding: var(--s2) var(--s3);
    background: var(--warn-soft);
    border: 1px solid rgba(var(--warn-rgb), 0.35);
    border-radius: 8px;
    font-size: var(--fs-table);
}

.busy-facts dt { color: var(--ink-muted); }
.busy-facts dd { margin: 0; font-weight: 600; }

.busy-text { margin: 0; font-size: var(--fs-table); line-height: 1.45; }
.busy-warn { color: var(--ink-muted); }

.who-actions {
    display: flex;
    align-items: center;
    gap: var(--s3);
    margin-top: var(--s3);
    font-size: var(--fs-small);
    color: var(--ink-muted);
}

/* «Сменить пароль» — MudLink цвета «inherit»: цвет берёт отсюда. Его
   правило цвета с !important, поэтому и наведение — с ним. */
.who-actions .mud-link:hover { color: var(--accent) !important; }

.link-btn {
    background: none;
    border: none;
    padding: 0;
    font: inherit;
    color: var(--accent);
    cursor: pointer;
    text-align: left;
}

.link-btn:hover { text-decoration: underline; }
.link-btn.danger { color: var(--danger); }

.sidebar-foot .link-btn { color: var(--ink-muted); font-size: var(--fs-small); }
.sidebar-foot .link-btn:hover { color: var(--danger); }

/* MudButton в виде ссылки («Выйти», «Показать их»): без его ширины,
   отступов, разрядки и фона. */
.link-btn.mud-button {
    min-width: 0;
    padding: 0;
    letter-spacing: normal;
    text-transform: none;
}

.link-btn.mud-button:hover { background: none; }

/* В строке таблицы и в тексте сообщения — по базовой линии, как
   прежняя ссылка; MudBlazor ставит кнопку по середине строки. */
.grid-mud .link-btn.mud-button,
.page-alert .link-btn.mud-button { vertical-align: baseline; }

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

.table-wrap {
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    overflow-x: auto;
}

table.grid-table {
    width: 100%;
    border-collapse: collapse;
    font-size: var(--fs-table);
}

/* Шапка — строчными, 12,5 px и цветом --ink-soft. Прежняя (11 px,
   капсом, приглушённая) выглядела аккуратно, но читалась хуже данных
   под ней: капс съедает очертания слов, и «ОПЛАЧЕНО» от «НАЧИСЛЕНО»
   глаз отличает дольше, чем «Оплачено» от «Начислено». */
.grid-table th {
    text-align: left;
    padding: var(--s2) var(--s3);
    font-size: var(--fs-label);
    letter-spacing: 0.005em;
    color: var(--ink-soft);
    font-weight: 600;
    border-bottom: 1px solid var(--border);
    white-space: nowrap;
    /* Шапка держится при прокрутке длинного списка: без неё на середине
       класса уже не помнишь, какой столбец где. */
    position: sticky;
    top: 0;
    background: var(--surface);
    z-index: 1;
}

.grid-table td {
    padding: var(--s3) var(--s3);
    border-bottom: 1px solid var(--border);
    vertical-align: top;
}

.grid-table tbody tr:last-child td { border-bottom: none; }

.grid-table tbody tr { transition: background 0.1s ease; }
/* Строки простых таблиц не нажимаются — и не подсвечиваются при наведении (T-4). */

.grid-table tr.row-off td { opacity: 0.55; }

/* Отмеченная строка: полоска слева заметнее заливки и не спорит с
   цветными значками состояния внутри строки. */
.grid-table tr.row-picked { background: var(--accent-soft); }
.grid-table tr.row-picked:hover { background: var(--accent-soft); }
.grid-table tr.row-picked td:first-child {
    box-shadow: inset 3px 0 0 var(--accent);
}

.cell-main { font-weight: 600; }
.muted-cell { color: var(--ink-muted); white-space: nowrap; }

/*  Приглушённый абзац — пояснение под таблицей или блоком. Цвет тот же,
    что у .muted-cell, но перенос разрешён: с запретом строка пояснения
    («Это остаток. Его разбирают вручную…») уходила за край, и при окне
    ~950 px прокручивалась вся страница «Разбор истории».           */
.muted-note { color: var(--ink-muted); }

/*  Приглушённая ячейка, которой ПОЛОЖЕНО переноситься.

    .muted-cell запрещает перенос — и правильно делает: даты и числа,
    разорванные посередине, читаются плохо. Но там, где в ту же ячейку
    кладут примеры живого текста, запрет оборачивается бедой: строка не
    переносится, ширина ограничена, и хвост уезжает поверх соседнего
    столбца. Так «Что захватило» наезжало на «Записей».                 */
.cell-samples {
    white-space: normal;
    word-break: break-word;
    line-height: 1.4;
}

.grid-table td.actions {
    text-align: right;
    white-space: nowrap;
}

.grid-table td.actions .link-btn + .link-btn { margin-left: var(--s3); }

/* ---------- временный пароль ---------- */

.temp-password {
    font-family: var(--mono);
    font-size: var(--fs-title);
    letter-spacing: 0.12em;
    padding: var(--s3) var(--s4);
    background: var(--surface);
    border: 1.5px dashed var(--accent);
    border-radius: var(--radius-sm);
    margin-top: var(--s2);
    display: inline-block;
    color: var(--ink);
    user-select: all;
}

/* ---------- фишки ролей ---------- */

.chips { display: flex; flex-wrap: wrap; gap: var(--s2); }

.chip {
    padding: var(--s1) var(--s3);
    border-radius: 999px;
    border: 1.5px solid var(--border-strong);
    background: var(--surface);
    color: var(--ink-soft);
    font: inherit;
    font-size: var(--fs-small);
    cursor: pointer;
    transition: all 0.12s ease;
}

.chip:hover { border-color: var(--accent); color: var(--ink); }

.chip.on {
    background: var(--accent-soft);
    border-color: var(--accent);
    color: var(--accent-ink);
    font-weight: 600;
}

/* ---------- права роли ---------- */

.perm-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(260px, 1fr));
    gap: var(--s2);
}

.perm {
    display: flex;
    align-items: flex-start;
    gap: var(--s3);
    padding: var(--s2) var(--s3);
    border: 1.5px solid var(--border);
    border-radius: var(--radius-sm);
    background: var(--surface);
    cursor: pointer;
    font: inherit;
    color: inherit;
    text-align: left;
    transition: all 0.12s ease;
}

.perm:hover { border-color: var(--accent); }

.perm.on { border-color: var(--accent); background: var(--accent-soft); }

.perm-box {
    width: 18px; height: 18px;
    border: 1.5px solid var(--border-field);
    border-radius: 4px;
    flex: 0 0 18px;
    display: grid; place-items: center;
    font-size: var(--fs-small);
    margin-top: 1px;
}

.perm.on .perm-box {
    background: var(--accent);
    border-color: var(--accent);
    color: var(--on-accent);
}

.perm-text { display: flex; flex-direction: column; min-width: 0; }
.perm-name { font-size: var(--fs-table); font-weight: 600; }
.perm-code {
    font-family: var(--mono);
    font-size: var(--fs-small);
    color: var(--ink-muted);
}

.search-meta .spacer { flex: 1; }

/* галочка выбора поля при слиянии — та же коробочка, что у прав */
button.perm-box {
    cursor: pointer;
    background: var(--surface);
    color: var(--ink);
    font: inherit;
    font-size: var(--fs-small);
    padding: 0;
}

button.perm-box.picked {
    background: var(--accent);
    border-color: var(--accent);
    color: var(--on-accent);
}

/* ---------- полоски в статистике ---------- */

.stat-row { margin-bottom: var(--s3); }

.stat-head {
    display: flex; justify-content: space-between; gap: var(--s3);
    font-size: var(--fs-table); margin-bottom: var(--s1);
}

.stat-num { color: var(--ink-muted); white-space: nowrap; font-variant-numeric: tabular-nums; }

.stat-bar {
    height: 6px;
    background: var(--border);
    border-radius: 3px;
    overflow: hidden;
    display: block;
}

.stat-fill { display: block; height: 100%; border-radius: 3px; }
.stat-fill.ok   { background: var(--ok); }
.stat-fill.warn { background: var(--warn); }
.stat-fill.bad  { background: var(--danger); }
.stat-fill.info { background: var(--accent); }

/* ---------- журнал событий ---------- */

.grid-table tr.row-error td { background: var(--danger-soft); }
.grid-table tr.row-error:hover td { background: var(--danger-soft); }

pre.details {
    margin: var(--s2) 0 0;
    padding: var(--s2) var(--s3);
    background: var(--surface-sunken);
    border: 1px solid var(--border);
    border-radius: var(--radius-sm);
    font-family: var(--mono);
    font-size: var(--fs-small);
    line-height: 1.5;
    white-space: pre-wrap;
    word-break: break-word;
    max-width: 60ch;
    max-height: 320px;
    overflow: auto;
    color: var(--ink-soft);
}

/* ---------- отбор, сортировка, листание ---------- */

.filter-row {
    display: flex;
    flex-wrap: wrap;
    gap: var(--s3);
    margin-top: var(--s3);
    padding-top: var(--s3);
    border-top: 1px solid var(--border);
}

.filter-field { display: flex; flex-direction: column; gap: var(--s1); min-width: 140px; }

.filter-field label {
    font-size: var(--fs-label);
    text-transform: uppercase;
    letter-spacing: 0.04em;
    color: var(--ink-muted);
    font-weight: 600;
}

.filter-field input,
.filter-field select {
    padding: var(--s1) var(--s2);
    font-size: var(--fs-table);
    font-family: var(--sans);
    border: 1.5px solid var(--border-field);
    border-radius: var(--radius-sm);
    background: var(--surface);
    color: var(--ink);
}

.filter-field input:focus,
.filter-field select:focus {
    border-color: var(--accent);
    outline: 2px solid var(--accent);
    outline-offset: 2px;
}

/* заголовок-сортировщик */
.grid-table th.sortable {
    cursor: pointer;
    user-select: none;
    white-space: nowrap;
}

.grid-table th.sortable:hover { color: var(--accent); }

.sort-mark { color: var(--accent); font-size: var(--fs-small); }

/* листалка */
.pager {
    display: flex;
    align-items: center;
    gap: var(--s2);
    margin-top: var(--s4);
    flex-wrap: wrap;
}

/* ---------- карточка ученика ---------- */

.card-head {
    display: flex;
    align-items: flex-start;
    gap: var(--s4);
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    box-shadow: var(--shadow);
    padding: var(--s5) var(--s5);
    margin-bottom: var(--s4);
}

.avatar.big {
    width: 64px; height: 64px;
    flex: 0 0 64px;
    font-size: 1.3rem;
}

.card-head-facts {
    display: flex; flex-wrap: wrap; gap: var(--s1) var(--s4);
    margin-top: var(--s2);
    font-size: var(--fs-table);
    color: var(--ink-soft);
}

/* Две колонки: слева семья и движение, справа договоры и история.
   На узком экране складываются в одну. */
.card-columns {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: var(--s4);
    align-items: start;
}

@media (max-width: 1000px) {
    .card-columns { grid-template-columns: minmax(0, 1fr); }
    .card-head { flex-wrap: wrap; }
}

.card-columns > div { display: flex; flex-direction: column; gap: var(--s4); }
.card-columns > * { min-width: 0; }
.card-columns .panel { margin: 0; }

.person-block { padding: var(--s2) 0; border-bottom: 1px solid var(--border); }
.person-block:last-child { border-bottom: none; }

.sibling-list { display: flex; flex-direction: column; gap: var(--s2); }

.sibling {
    display: flex; flex-direction: column; gap: 2px;
    padding: var(--s2) var(--s3);
    border: 1px solid var(--border);
    border-radius: var(--radius-sm);
    background: var(--surface-sunken);
    text-decoration: none;
    color: inherit;
    transition: border-color 0.12s ease, background 0.12s ease;
}

.sibling:hover { border-color: var(--accent); background: var(--accent-soft); }
.sibling-name { font-weight: 600; font-size: var(--fs-body); }
.sibling-facts { font-size: var(--fs-small); color: var(--ink-muted); }

/* ---------- лента событий ---------- */

.timeline { display: flex; flex-direction: column; }

.tl-item {
    display: flex;
    gap: var(--s3);
    padding-bottom: var(--s3);
    position: relative;
}

/* Линия между точками — соединяет события в непрерывную историю. */
.tl-item:not(:last-child)::before {
    content: '';
    position: absolute;
    left: 5px;
    top: 16px;
    bottom: 0;
    width: 1.5px;
    background: var(--border);
}

.tl-dot {
    width: 12px; height: 12px;
    border-radius: 50%;
    flex: 0 0 12px;
    margin-top: var(--s1);
    border: 2.5px solid var(--surface);
    box-shadow: 0 0 0 1.5px var(--border-strong);
    background: var(--ink-muted);
    position: relative;
    z-index: 1;
}

.tl-dot.ok   { background: var(--ok); box-shadow: 0 0 0 1.5px var(--ok); }
.tl-dot.warn { background: var(--warn); box-shadow: 0 0 0 1.5px var(--warn); }
.tl-dot.info { background: var(--accent); box-shadow: 0 0 0 1.5px var(--accent); }
.tl-dot.mute { background: var(--border-strong); }

.tl-body { min-width: 0; flex: 1; font-size: var(--fs-table); }

.tl-head {
    display: flex; justify-content: space-between; gap: var(--s3);
    margin-bottom: 2px;
}

.tl-date { color: var(--ink-muted); font-size: var(--fs-small); white-space: nowrap; }

/*  Имя человека как ссылка.

    Раньше было цветом обычного текста с еле заметным пунктиром снизу —
    ради того, чтобы список не рябил. Расчёт не оправдался: с боевой
    проверки сообщили, что карточку ученика «не нашли», хотя ссылка была
    прямо под курсором. Ссылка, которую не видно, — это отсутствующая
    ссылка.

    Теперь цветом действия, как все прочие ссылки кабинета. Рябить не
    станет: в карточке имя одно, а не в каждой ячейке.                  */
a.card-link {
    color: var(--accent);
    text-decoration: none;
}

a.card-link:hover { text-decoration: underline; }

/* --- обзор справочников --------------------------------------------- */
/* Плитка, а не список: справочников двадцать, и у каждого есть строка
   пояснения — списком это читается как сплошная стена. */
.ref-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
    gap: var(--s3);
}

.ref-card {
    display: block;
    padding: var(--s3) var(--s4);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    background: var(--surface);
    text-decoration: none;
    color: inherit;
    transition: border-color 0.15s, box-shadow 0.15s;
}

.ref-card:hover {
    border-color: var(--accent);
    box-shadow: var(--shadow);
}

.ref-card-head {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: var(--s3);
    margin-bottom: var(--s2);
}

.ref-card-title { font-weight: 600; }

.ref-card-text {
    font-size: var(--fs-small);
    color: var(--ink-muted);
    line-height: 1.45;
}

.ref-card-locked { background: var(--surface-sunken); }

/* --- классы ---------------------------------------------------------- */
/* Плитка, а не таблица: у класса три числа (сколько, из скольки, чем
   заполнен), и полоска заполненности читается быстрее колонки цифр. */
.class-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(190px, 1fr));
    gap: var(--s3);
}

.class-card {
    display: block;
    padding: var(--s3) var(--s4);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    background: var(--surface);
    text-decoration: none;
    color: inherit;
    transition: border-color 0.15s, box-shadow 0.15s;
}

.class-card:hover { border-color: var(--accent); box-shadow: var(--shadow); }
.class-card-empty { background: var(--surface-sunken); }

.class-card-head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--s2);
    margin-bottom: var(--s2);
}

.class-label { font-size: var(--fs-section); font-weight: 600; }

.class-count { display: flex; align-items: baseline; gap: var(--s2); margin-bottom: var(--s2); }
.class-count strong { font-size: var(--fs-title); font-variant-numeric: tabular-nums; }

.class-bar {
    height: 5px;
    border-radius: 3px;
    background: var(--surface-sunken);
    overflow: hidden;
    margin-bottom: var(--s2);
}

.class-bar-fill { height: 100%; background: var(--accent); border-radius: 3px; }
.class-bar-fill.over { background: var(--warn); }

.class-school {
    font-size: var(--fs-small);
    color: var(--ink-muted);
    line-height: 1.35;
}



/* --- карточка родителя ------------------------------------------------ */
/* Определения в две колонки: слева подпись, справа значение. Данных о
   родителе немного, и таблица под них была бы тяжеловата. */
.facts {
    display: grid;
    grid-template-columns: minmax(120px, max-content) 1fr;
    gap: var(--s2) var(--s4);
    margin: 0;
}

.facts dt {
    color: var(--ink-muted);
    font-size: var(--fs-small);
}

.facts dd { margin: 0; }

/* Пояснение рядом со значением — с отбивкой. Без неё ИИН прилипал к
   фамилии и читался как её продолжение: «Иванов Иван000000000000». */
.facts dd .muted-cell { margin-left: var(--s2); }

/* Насколько изменилась сумма (ChangeMark): ▲ зелёным — выросла,
   ▼ оранжевым — уменьшилась. Плашкой, а не просто цветом текста: в
   «Что изменится» рядом серые «было …», и цветная цифра среди них
   терялась. */
.change {
    display: inline-block;
    padding: 0 6px;
    border-radius: 4px;
    font-weight: 600;
    white-space: nowrap;
    font-variant-numeric: tabular-nums;
}

.change-up   { color: var(--ok);   background: var(--ok-soft); }
.change-down { color: var(--warn); background: var(--warn-soft); }

.facts dd .change, .hint .change { margin-left: var(--s2); }

.hint.change-legend .change { margin: 0 var(--s1) 0 0; }

/* «Станет» в таблице «было → станет» — цветом разницы. */
.now-up   { color: var(--ok); }
.now-down { color: var(--warn); }

/* Строка во всю ширину сетки фактов: «Класс: 6С · школа … было 6А ·
   школа …» в половине ширины ломалась на три строки. */
.facts .fact-wide { grid-column: 1 / -1; flex-wrap: wrap; }

/* Что меняется, кроме денег: другая школа при смене класса. */
.change-note {
    display: inline-block;
    margin-left: var(--s2);
    padding: 0 6px;
    border-radius: 4px;
    font-size: var(--fs-small);
    font-weight: 600;
    color: var(--warn);
    background: var(--warn-soft);
}

/* Полный список полей договора: пар много, и на широком экране они
   ложатся в несколько колонок вместо одной длинной лестницы. */
.facts.facts-wide {
    grid-template-columns: repeat(auto-fit, minmax(320px, 1fr));
    align-items: start;
}

.facts.facts-wide .fact { align-items: baseline; flex-wrap: wrap; }

/* Пояснения в строке пишутся подряд («номер», «состояние», «обновлено»)
   — без отбивки они слипались в одно слово. */
.facts.facts-wide dd .muted-cell { margin-left: var(--s2); }

.facts.facts-wide dd .muted-cell + .muted-cell::before { content: "· "; }

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

   Обычная сетка .facts раскладывает пары в две колонки — это верно для
   коротких значений вроде «Вид оплаты: наличные». Для абзацев она
   ставила первый пункт слева, второй справа, третий снова слева, и
   читать их приходилось змейкой. */
.facts.facts-lines {
    grid-template-columns: 1fr;
    gap: var(--s3);
}

.facts.facts-lines .fact {
    display: grid;
    grid-template-columns: minmax(110px, 150px) 1fr;
    gap: 2px var(--s4);
    align-items: start;
}

.facts.facts-lines dt { padding-top: 2px; font-weight: 600; color: var(--ink-soft); }
.facts.facts-lines dd { line-height: 1.5; }

@media (max-width: 640px) {
    .facts.facts-lines .fact { grid-template-columns: minmax(0, 1fr); }
}

/* =========================================================================
   КОМПЛЕКТОВАНИЕ

   Две панели и стрелки между ними — та же расстановка, что в Delphi, и это
   правильная расстановка: человек видит одновременно, откуда берёт и куда
   кладёт. Всё остальное сделано иначе.

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

   Счётчик мест виден всегда и краснеет при переборе. В старой программе
   узнать, что в классе 46 человек при 28 местах, можно было только
   пересчитав строки.
   ========================================================================= */

.page-wide { max-width: 1460px; }

.roster-head {
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
    gap: var(--s5);
    flex-wrap: wrap;
    margin-bottom: var(--s5);
}

.roster-title { display: flex; align-items: baseline; gap: var(--s3); margin-top: var(--s2); }

.roster-label {
    font-size: var(--fs-title);
    font-weight: 700;
    letter-spacing: -0.025em;
    line-height: 1;
}

.roster-year { font-size: var(--fs-body); color: var(--ink-muted); }
.roster-school { color: var(--ink-soft); font-size: var(--fs-table); margin-top: var(--s1); }

/* ---------- счётчик мест ---------- */

.capacity {
    min-width: 190px;
    padding: var(--s3) var(--s4);
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    box-shadow: var(--shadow);
}

.capacity-numbers { display: flex; align-items: baseline; gap: var(--s1); }
.capacity-now { font-size: var(--fs-title); font-weight: 700; line-height: 1; }
.capacity-cap { color: var(--ink-muted); font-size: var(--fs-section); }

.capacity-bar {
    height: 6px;
    border-radius: var(--radius-pill);
    background: var(--surface-sunken);
    overflow: hidden;
    margin: var(--s2) 0 var(--s1);
}

.capacity-fill {
    height: 100%;
    background: var(--accent);
    border-radius: var(--radius-pill);
    transition: width 0.3s ease;
}

.capacity-note { font-size: var(--fs-small); color: var(--ink-muted); }
.capacity-over .capacity-fill { background: var(--danger); }
.capacity-over .capacity-now { color: var(--danger); }
.ink-danger { color: var(--danger); font-weight: 600; }

/* ---------- сами панели ---------- */

.transfer {
    display: grid;
    grid-template-columns: 1fr auto 1fr;
    gap: var(--s4);
    align-items: start;
}

/* Просмотр без права комплектования: пула и стрелок нет, класс один. */
.transfer-single {
    grid-template-columns: 1fr;
}

.transfer-pane {
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    box-shadow: var(--shadow);
    display: flex;
    flex-direction: column;
    min-width: 0;
    overflow: hidden;
}

/* Панель класса обведена цветом акцента: она — цель переноса. */
.transfer-pane-target { border-color: var(--accent); }

.transfer-head {
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
    gap: var(--s3);
    padding: var(--s4) var(--s4) var(--s3);
    border-bottom: 1px solid var(--border);
}

.transfer-head h2 { font-size: var(--fs-section); }
.transfer-head .sub { display: block; font-size: var(--fs-small); color: var(--ink-muted); margin-top: 2px; }

.counter {
    background: var(--accent);
    color: var(--on-accent);
    font-size: var(--fs-small);
    font-weight: 600;
    padding: 2px var(--s2);
    border-radius: var(--radius-pill);
    font-variant-numeric: tabular-nums;
}

.transfer-filter {
    position: relative;
    padding: var(--s3) var(--s4);
    border-bottom: 1px solid var(--border);
}

.transfer-filter .glyph {
    position: absolute;
    left: calc(var(--s4) + 10px);
    top: 50%;
    transform: translateY(-50%);
    color: var(--ink-muted);
    pointer-events: none;
}

.transfer-filter input {
    width: 100%;
    padding: var(--s2) var(--s3) var(--s2) var(--s6);
    font: inherit;
    font-size: var(--fs-body);
    border: 1px solid var(--border-field);
    border-radius: var(--radius-sm);
    background: var(--surface-sunken);
    color: var(--ink);
}

.transfer-move {
    display: flex;
    gap: var(--s2);
    padding: var(--s3) var(--s4);
    border-bottom: 1px solid var(--border);
    background: var(--accent-soft);
}

.transfer-move select {
    flex: 1;
    min-width: 0;
    padding: var(--s2) var(--s2);
    font: inherit;
    font-size: var(--fs-table);
    border: 1px solid var(--border-field);
    border-radius: var(--radius-sm);
    background: var(--surface);
    color: var(--ink);
}

/* Список прокручивается внутри себя: при трёхстах нераспределённых
   страница иначе уезжает вниз на экраны, и стрелки переноса исчезают. */
.transfer-list {
    max-height: 58vh;
    overflow-y: auto;
    padding: var(--s2);
}

.transfer-empty {
    padding: var(--s6) var(--s4);
    text-align: center;
    color: var(--ink-muted);
    font-size: var(--fs-table);
}

/* ---------- строка выбора ---------- */

.tr-row {
    display: flex;
    align-items: flex-start;
    gap: var(--s3);
    width: 100%;
    padding: var(--s2) var(--s2);
    border: 1px solid transparent;
    border-radius: var(--radius-sm);
    background: transparent;
    font: inherit;
    color: inherit;
    text-align: left;
    cursor: pointer;
    transition: background 0.1s ease, border-color 0.1s ease;
}

.tr-row:hover { background: var(--surface-sunken); }

/* Строка-ссылка: тот же ряд, но без колонки галочки и без выбора.
   Нужна в режиме «в классе, а договора нет» — там отмечать некого,
   люди уже распределены, и единственное осмысленное действие —
   открыть карточку. Раскладка у .tr-row гибкая, так что отсутствие
   .tr-check ничего не ломает; убираем только подчёркивание ссылки. */
.tr-link { text-decoration: none; }
.tr-link:hover { text-decoration: none; }
.tr-link .tr-open { opacity: 0.55; }
.tr-link:hover .tr-open { opacity: 1; }
.tr-on { background: var(--accent-soft); border-color: var(--accent); }
.tr-on:hover { background: var(--accent-soft); }

.tr-check {
    flex: 0 0 18px;
    width: 18px; height: 18px;
    margin-top: 1px;
    border: 1.5px solid var(--border-field);
    border-radius: 5px;
    display: grid;
    place-items: center;
    font-size: 12px;
    line-height: 1;
    color: var(--on-accent);
    background: var(--surface);
}

.tr-on .tr-check { background: var(--accent); border-color: var(--accent); }

.tr-body { display: flex; flex-direction: column; gap: 2px; min-width: 0; flex: 1; }

.tr-name {
    font-weight: 600;
    display: flex;
    align-items: center;
    gap: var(--s2);
    flex-wrap: wrap;
}

.tr-facts {
    font-size: var(--fs-small);
    color: var(--ink-muted);
    display: flex;
    gap: var(--s2);
    flex-wrap: wrap;
}

.tr-was { color: var(--accent-ink); }
.tr-gone .tr-name { color: var(--ink-muted); }

.tr-open {
    flex: 0 0 auto;
    color: var(--ink-muted);
    text-decoration: none;
    padding: 0 var(--s1);
    opacity: 0;
    transition: opacity 0.1s ease;
}

.tr-row:hover .tr-open { opacity: 1; }
.tr-open:hover { color: var(--accent); }

/* ---------- стрелки переноса ---------- */

.transfer-mid {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: var(--s3);
    padding-top: 90px;
    width: 92px;
}

.move-btn {
    position: relative;
    width: 52px;
    height: 44px;
    border-radius: var(--radius-sm);
    border: 1px solid var(--border-strong);
    background: var(--surface);
    color: var(--ink-soft);
    font-size: 1.25rem;
    cursor: pointer;
    display: grid;
    place-items: center;
    box-shadow: var(--shadow);
    transition: background 0.14s ease, border-color 0.14s ease,
                color 0.14s ease, transform 0.08s ease;
}

.move-btn:hover:not(:disabled) {
    background: var(--accent);
    border-color: var(--accent);
    color: var(--on-accent);
}

.move-btn:active:not(:disabled) { transform: translateY(1px); }
.move-btn:disabled { opacity: 0.35; cursor: not-allowed; box-shadow: none; }

.move-count {
    position: absolute;
    top: -7px;
    right: -7px;
    min-width: 20px;
    padding: 0 var(--s1);
    height: 20px;
    border-radius: var(--radius-pill);
    background: var(--accent);
    color: var(--on-accent);
    font-size: 0.6875rem;
    font-weight: 700;
    display: grid;
    place-items: center;
    font-variant-numeric: tabular-nums;
}

.transfer-hint {
    font-size: var(--fs-small);
    color: var(--ink-muted);
    text-align: center;
    line-height: 1.35;
    margin-top: var(--s2);
}

/* На узком экране панели встают друг под друга, а стрелки разворачиваются
   поперёк — иначе они уезжают за край. */
@media (max-width: 1080px) {
    .transfer { grid-template-columns: minmax(0, 1fr); }
    .transfer-mid {
        flex-direction: row;
        justify-content: center;
        padding-top: 0;
        width: auto;
    }
    .transfer-mid .move-btn span:first-child { transform: rotate(90deg); }
    .transfer-hint { display: none; }
}

/* Выбор оформления — меню «Вид», см. «меню „Вид“ (NavMenu)» ниже. */

/* =========================================================================
   СВЕРКА И ПРИЗНАК ДОГОВОРА

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

   Плашка договора — знак перед фамилией, а не колонка: взгляд идёт по
   левому краю списка, и состояние читается, не доходя до конца строки.
   ========================================================================= */

.tally {
    display: flex;
    align-items: stretch;
    flex-wrap: wrap;
    gap: var(--s5);
    padding: var(--s3) var(--s4);
    margin-bottom: var(--s4);
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    box-shadow: var(--shadow);
}

.tally-item { display: flex; flex-direction: column; gap: 2px; }

.tally-num {
    font-size: var(--fs-title);
    font-weight: 700;
    line-height: 1;
    font-variant-numeric: tabular-nums;
}

.tally-label { font-size: var(--fs-small); color: var(--ink-muted); }

/*  Вторая часть подписи — своей строкой. В плитке подпись состоит из
    двух кусков: чего это число («Школа», «Сумма договоров») и чем оно
    уточняется («просрочено 242 380 915 ₸»). Без переноса они слипались
    в одну фразу, и читалось «Сумма договоров 5408 договоров». */
.tally-label .muted-cell {
    display: block;
    margin-top: 1px;
}

.tally-ok   .tally-num { color: var(--ok); }
.tally-warn .tally-num { color: var(--warn); }
.tally-bad  .tally-num { color: var(--danger); }

/* Разделитель отделяет «что внутри классов» от «что мимо классов». */
.tally-sep {
    width: 1px;
    align-self: stretch;
    background: var(--border);
}

.ink-ok   { color: var(--ok); }
.ink-warn { color: var(--warn); }

/* ---------- задолженность по месяцам ---------- */
/*  Цвета задолженности — как в отчёте старой программы, к ним привыкли:
    долг синим, переплата красным, ровно — зелёным. Под таблицей — их
    расшифровка: в остальном кабинете красный значит «плохо».          */
.arr-debt { color: var(--accent); font-weight: 600; }
.arr-over { color: var(--danger); font-weight: 600; }
.arr-zero { color: var(--ok); }
.grid-mud .mud-table-body .mud-table-cell.arr-debt { color: var(--accent); }
.grid-mud .mud-table-body .mud-table-cell.arr-over { color: var(--danger); }
.grid-mud .mud-table-body .mud-table-cell.arr-zero { color: var(--ok); }
.arr-legend { margin-top: var(--s2); }

/* Итоги группы реестра — по видам, каждый своим куском после подписи. */
.group-kind { margin-left: var(--s3); color: var(--ink-muted); font-size: var(--fs-small); }
.group-kind::before { content: "· "; }

/* Переключатель «на сегодня / по месяцам» над отчётом задолженности. */
.mode-switch { display: flex; flex-wrap: wrap; gap: var(--s2); margin: 0 0 var(--s3); }

/* Выбор месяцев: подпись выбора — в строку с быстрыми кнопками. */
.month-picker .chips-pane .sub { align-self: center; margin-left: var(--s2); }

/* ---------- знак договора ---------- */

.dot {
    flex: 0 0 auto;
    font-size: var(--fs-small);
    line-height: 1;
}

/* Цвет берём от НОМЕРА состояния, а не от русского текста статуса.
   В Delphi раскраска сравнивала слова ('Подписан', 'Расторгнут',
   unFinance.pas:3897) — переименование строки в справочнике молча ломало
   цвет, а справочник теперь открыт для правки. */
.dot-ok   { color: var(--ok); }
.dot-warn { color: var(--warn); }
.dot-bad  { color: var(--danger); }
.dot-none { color: var(--ink-muted); }

/* Метка вида договора: питание, сад. Мелко, чтобы не спорить со знаком. */
.mark {
    font-size: 0.625rem;
    font-weight: 700;
    line-height: 1;
    padding: 2px var(--s1);
    border-radius: 4px;
    background: var(--surface-sunken);
    color: var(--ink-muted);
    border: 1px solid var(--border);
}

/* Школа не заполнена. Не ошибка — напоминание: при распределении
   школа проставится сама, из класса. */
.flag { color: var(--warn); font-size: var(--fs-small); }

/* ---------- ряд фишек отбора внутри панели ---------- */
/* Сами .chip и .chips уже описаны выше (их используют журнал событий и
   расчистка ИИН). Здесь только обрамление ряда: в панели переноса он
   стоит под строкой поиска и нуждается в отбивке. */

.chips-pane {
    padding: var(--s2) var(--s4) var(--s3);
    border-bottom: 1px solid var(--border);
}

/* ============================================================
   КОМПЛЕКТОВАНИЕ: ДЕРЕВО КЛАССОВ, ОТМЕНА, ПЕРЕТАСКИВАНИЕ
   ============================================================

   Имена с приставкой roster-tree-, undo-bar-, roster-shell- взяты
   намеренно длинными. Двухбуквенные приставки в этом файле уже
   исчерпаны: tr- занята строкой комплектования, tl- лентой событий, и
   они отличаются одной буквой — третью такую заводить нельзя. */

/* ---------- каркас экрана: дерево слева, работа справа ---------- */

.roster-shell {
    display: grid;
    grid-template-columns: 232px minmax(0, 1fr);
    gap: var(--s4);
    align-items: start;
}

.roster-tree {
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    box-shadow: var(--shadow);
    overflow: hidden;
    position: sticky;
    top: var(--s4);
    max-height: calc(100vh - 2 * var(--s4));
    display: flex;
    flex-direction: column;
}

.roster-tree-head {
    padding: var(--s3) var(--s4);
    border-bottom: 1px solid var(--border);
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--s2);
}

.roster-tree-head h2 {
    margin: 0;
    font-size: var(--fs-label);
    text-transform: uppercase;
    letter-spacing: 0.04em;
    color: var(--ink-muted);
}

.roster-tree-body { overflow-y: auto; padding: var(--s2) 0 var(--s3); }

.roster-tree-school {
    width: 100%;
    display: flex;
    align-items: center;
    gap: var(--s2);
    padding: var(--s2) var(--s4);
    background: none;
    border: 0;
    text-align: left;
    cursor: pointer;
    font: inherit;
    font-size: var(--fs-small);
    font-weight: 600;
    color: var(--ink-soft);
}

.roster-tree-school:hover { color: var(--ink); }
.roster-tree-school .roster-tree-turn { color: var(--ink-muted); font-size: var(--fs-small); }

.roster-tree-item {
    width: 100%;
    display: grid;
    grid-template-columns: 1fr auto;
    align-items: center;
    gap: var(--s2);
    padding: var(--s1) var(--s4) var(--s1) var(--s5);
    background: none;
    border: 0;
    border-left: 3px solid transparent;
    text-align: left;
    cursor: pointer;
    font: inherit;
    font-size: var(--fs-table);
    color: var(--ink-soft);
    text-decoration: none;
}

.roster-tree-item:hover { background: var(--surface-sunken); color: var(--ink); }

/* Класс, который сейчас открыт. Имя here, а не active/current: оба
   односложных модификатора в этом файле уже заняты другими блоками. */
.roster-tree-item.here {
    background: var(--surface-sunken);
    border-left-color: var(--accent);
    color: var(--ink);
    font-weight: 600;
}

.roster-tree-name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

.roster-tree-count {
    font-size: var(--fs-small);
    color: var(--ink-muted);
    font-variant-numeric: tabular-nums;
}

.roster-tree-item.full .roster-tree-count { color: var(--warn); }
.roster-tree-item.over .roster-tree-count { color: var(--danger); font-weight: 600; }

.roster-tree-empty {
    padding: var(--s3) var(--s4);
    font-size: var(--fs-small);
    color: var(--ink-muted);
}

/* ---------- полоска отмены ---------- */
/* Появляется под сообщением о том, что действие прошло. Не всплывающая
   и не исчезающая по таймеру: это Blazor Server, обрыв соединения —
   обычное дело, и отмена, живущая в памяти вкладки, потерялась бы. */

.undo-bar {
    display: flex;
    align-items: center;
    gap: var(--s3);
    flex-wrap: wrap;
    padding: var(--s3) var(--s4);
    margin: 0 0 var(--s4);
    background: var(--surface-sunken);
    border: 1px solid var(--border);
    border-radius: var(--radius);
}

.undo-bar-text { flex: 1; min-width: 0; font-size: var(--fs-table); color: var(--ink-soft); }
.undo-bar-text strong { color: var(--ink); }
.undo-bar-when { font-size: var(--fs-small); color: var(--ink-muted); }
.undo-bar-done { font-size: var(--fs-small); color: var(--ink-muted); font-style: italic; }

/* ---------- перетаскивание ---------- */

.tr-row[draggable="true"] { cursor: grab; }
.tr-row.tr-drag { opacity: 0.45; }

.transfer-pane.drop-here {
    border-color: var(--accent);
    box-shadow: 0 0 0 3px var(--accent-soft);
}

.transfer-pane.drop-here .transfer-head { background: var(--surface-sunken); }

/* ---------- подсказка про Shift и выбор всего найденного ---------- */

.pool-head-tools {
    display: flex;
    align-items: center;
    gap: var(--s3);
    flex-wrap: wrap;
    padding: 0 var(--s4) var(--s3);
    font-size: var(--fs-small);
}

.pool-head-tools .sub { color: var(--ink-muted); }

@media (max-width: 1240px) {
    .roster-shell { grid-template-columns: minmax(0, 1fr); }
    .roster-tree { position: static; max-height: 320px; }
}

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

.undo-list { margin: 0 0 var(--s4); }
.undo-list .undo-bar { margin: 0; border-radius: 0; border-top-width: 0; }
.undo-list .undo-bar:first-child {
    border-top-width: 1px;
    border-radius: var(--radius) var(--radius) 0 0;
}
.undo-list .undo-bar:last-child { border-radius: 0 0 var(--radius) var(--radius); }
.undo-list .undo-bar:only-child { border-radius: var(--radius); }

/* Строка дерева: сам класс плюс кнопка «положить сюда отмеченных».
   Кнопка появляется только когда в пуле кто-то отмечен — иначе она
   занимала бы место и сбивала с толку. */

.roster-tree-line { display: flex; align-items: stretch; }
.roster-tree-line .roster-tree-item { flex: 1; min-width: 0; }

.roster-tree-put {
    flex: 0 0 auto;
    width: 26px;
    background: none;
    border: 0;
    border-left: 1px solid var(--border);
    color: var(--accent);
    cursor: pointer;
    font: inherit;
    font-size: var(--fs-table);
    line-height: 1;
}

.roster-tree-put:hover:not(:disabled) { background: var(--accent); color: var(--on-accent); }
.roster-tree-put:disabled { color: var(--ink-muted); cursor: default; }

/* ============================================================
   Реестр договоров
   ============================================================ */

/*  Поле поиска шире двух соседних: в нём номер договора вида
    «2026ДК-15275», а не выбор из списка. */
.field-wide { flex: 1 1 260px; }

/*  Номера договоров и суммы — цифрами одной ширины, иначе столбец
    «Сумма» гуляет от строки к строке. Правило .mono в проекте уже
    было, но только для input внутри .field. */
.grid-table td.mono {
    font-family: var(--mono);
    letter-spacing: 0.02em;
}

.grid-table th.digits,
.grid-table td.digits {
    text-align: right;
    font-variant-numeric: tabular-nums;
}

/*  Пояснение под полосой итогов. Однословного .hint в файле не было —
    только .hint-bad, .hint-ok и .hint-warn. */
.hint {
    margin: var(--s3) 0 0;
    font-size: var(--fs-small);
    color: var(--ink-soft);
}

/*  Цвет важности — после .hint.

    Правила .hint-bad, .hint-ok и .hint-warn описаны выше, а .hint — ниже
    них, и при одинаковой точности побеждало последнее: все девятнадцать
    предупреждений в подсказках показывались обычным серым. */
.hint.hint-bad  { color: var(--danger); }
.hint.hint-ok   { color: var(--ok); }
.hint.hint-warn { color: var(--warn); }

/* ---------- лист договора ---------- */

/* Предпросмотр печати: это проверка содержания, а не копия вёрстки Word.
   Точная вёрстка живёт в .docx — о чём сказано рядом с кнопкой. */
/*  Просмотр как в Word: серое поле фиксированной высоты, внутри — белый
    лист, прокручивается содержимое поля, а не вся страница.            */
.doc-viewport {
    max-height: 72vh;
    overflow-y: auto;
    background: var(--surface-sunken);
    border: 1px solid var(--border);
    border-radius: var(--radius-sm);
    padding: var(--s5) var(--s4);
}

.doc-sheet {
    background: #fff;
    color: #131a24;
    padding: var(--s6) var(--s7);
    border: 1px solid var(--border-strong);
    border-radius: var(--radius-sm);
    max-width: 210mm;
    margin: 0 auto;
    box-shadow: 0 2px 12px rgba(15, 23, 42, 0.14);
    font-family: "Times New Roman", Georgia, serif;
    font-size: var(--fs-body);
    line-height: 1.45;
}

.doc-sheet p { margin: 0 0 var(--s2); }

.doc-sheet .doc-table {
    border-collapse: collapse;
    width: 100%;

    /*  Обязательно fixed. Для авторазметки width — это минимум, а не
        предел: таблица с неразрывным куском в ячейке (реквизиты,
        ИИК KZ…) законно вылезает за лист — так договор и выходил за
        рамки. Fixed делит ширину по колонкам жёстко, а длинные куски
        переносит break-word ниже.                                      */
    table-layout: fixed;
    margin: 0;
}

/*  Рамку и ширины столбцов ставит сам документ (DocxPreview): у таблиц
    без рамки в Word её нет и здесь. Поля ячейки — как в Word, 0,19 см
    слева и справа, сверху и снизу ноль.                                */
.doc-sheet .doc-table td {
    padding: 0 5.4pt;
    vertical-align: top;
    overflow-wrap: break-word;
}

.doc-sheet .doc-table td.doc-gap { border: none; padding: 0; }

/*  Кегль ставит документ; «одинарный» интервал Word — около 1,15.     */
.doc-sheet .doc-body { line-height: 1.15; }

/* Что бы ни пришло из шаблона, за лист оно выйти не должно. */
.doc-sheet { overflow-wrap: break-word; }

.doc-sheet img { max-width: 100%; height: auto; }

.doc-sheet .doc-table td p { margin: 0; }

/* Незаполненный тег — главное, что должно броситься в глаза. */
.doc-tag {
    background: var(--danger-soft);
    color: var(--danger);
    padding: 0 3px;
    border-radius: 3px;
    font-weight: 600;
}

.doc-cut, .doc-empty { color: var(--ink-muted); font-style: italic; }

@media print {
    .no-print, .sidebar, .topbar, .app-nav { display: none !important; }

    /*  Обязательно visible: обрезанное полем ушло бы на печать
        обрезанным — напечатался бы только видимый кусок договора.      */
    .doc-viewport {
        max-height: none;
        overflow: visible;
        border: none;
        background: none;
        padding: 0;
    }

    .doc-sheet {
        border: none;
        box-shadow: none;
        padding: 0;
        max-width: none;
        margin: 0;
    }

    .page, .print-page { padding: 0; margin: 0; }

    /*  Строку таблицы можно делить между страницами — как в Word: иначе
        браузер вправе перенести длинный пункт целиком на следующий лист
        и оставить на этом пустоту.                                     */
    .doc-sheet .doc-table tr,
    .doc-sheet .doc-table td {
        break-inside: auto;
        page-break-inside: auto;
    }

    /* Тег, не заменённый значением, на бумаге не должен выглядеть
       нарядным пятном — но и прятать его нельзя. */
    .doc-tag { background: none; color: inherit; font-weight: inherit; }
}

/* ---------- таблица шаблонов ---------- */

/* Клетка «школа × год»: главное — с одного взгляда видеть, где пусто.
   Пустая клетка на текущий год значит, что договор не напечатается. */
.cell-btn {
    display: flex;
    flex-direction: column;
    align-items: flex-end;
    gap: 2px;
    width: 100%;
    padding: var(--s1) var(--s2);
    border: 1px solid transparent;
    border-radius: var(--radius-sm);
    background: none;
    font: inherit;
    cursor: pointer;
    text-align: right;
}

.cell-btn:hover { border-color: var(--border-strong); background: var(--surface-sunken); }

.cell-btn .cell-mark { font-size: var(--fs-small); line-height: 1; }

.cell-btn.has .cell-mark { color: var(--ok); }
.cell-btn.none .cell-mark { color: var(--ink-muted); }

/*  ОДНА ПРОКРУТКА. Раньше каждая таблица жила в ящике высотой в две
    трети экрана со своей прокруткой: колесо крутило то таблицу, то
    страницу, а снизу под ящиком оставалась треть пустого места.

    Теперь таблица, которая влезает по ширине, прокручивается вместе со
    страницей, а шапка липнет к верхнему краю окна (fits-x). Широкой
    таблице нужна своя прокрутка вбок, а при ней браузер держит шапку
    только внутри рамки, — ей оставлен прежний ящик (wide-x). Влезает
    ли таблица, известно только после раскладки, поэтому класс ставит
    app.js («таблицы: одна прокрутка»). Пока он не поставлен —
    прокрутка вбок на всякий случай, без ящика.

    overflow: clip, а не hidden: hidden делает обёртку прокручиваемой,
    и шапка липла бы к ней, а не к окну.                               */
:root { --sticky-top: 0px; }

.table-scroll,
.table-wrap { overflow-x: auto; }

.table-scroll.fits-x,
.table-wrap.fits-x { overflow: clip; }

.table-scroll.wide-x,
.table-wrap.wide-x {
    overflow: auto;
    max-height: 64vh;
}

.table-scroll thead th,
.table-wrap thead th {
    position: sticky;
    top: 0;
    z-index: 1;
    background: var(--surface);
    box-shadow: 0 1px 0 var(--border);
}

.table-scroll.fits-x thead th,
.table-wrap.fits-x thead th { top: var(--sticky-top); }

/* На бумаге ящиков нет: напечаталась бы только видимая часть списка. */
@media print {
    .table-scroll,
    .table-wrap,
    .grid-mud .mud-table-container {
        max-height: none !important;
        overflow: visible !important;
    }

    .grid-mud.mud-table-outlined { overflow: visible; }
}

.review {
    margin-top: var(--s3);
    padding: var(--s2) var(--s3);
    border: 1px solid var(--border-strong);
    border-radius: var(--radius-sm);
    background: var(--surface-sunken);
}

.review p:last-child { margin-bottom: 0; }

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

.bell {
    display: flex;
    align-items: center;
    gap: var(--s2);
    padding: var(--s2) var(--s2);
    margin-bottom: var(--s2);
    border-radius: var(--radius-sm);
    color: var(--ink-soft);
    text-decoration: none;
    font-size: var(--fs-table);
}

.bell:hover { background: var(--surface-sunken); color: var(--ink); }

/* Открыта страница уведомлений — колокольчик горит, как текущий пункт
   меню: у этой страницы своего пункта нет. */
.bell.bell-here { background: var(--accent-soft); color: var(--accent-ink); font-weight: 600; }

.bell .bell-glyph { font-size: 18px; }
.bell-text { flex: 1; min-width: 0; }

/* Число непрочитанного — единственное место, где кабинет сам просит
   внимания. Поэтому оно и заметное. */
.bell-count {
    min-width: 1.4rem;
    padding: 1px var(--s1);
    border-radius: 999px;
    background: var(--danger);
    color: var(--on-danger);
    font-size: var(--fs-label);
    font-weight: 700;
    text-align: center;
}

.bell-on { color: var(--ink); }

.notes { list-style: none; margin: 0; padding: 0; display: grid; gap: var(--s2); }

.note {
    display: flex;
    gap: var(--s3);
    padding: var(--s3) var(--s3);
    border: 1px solid var(--border);
    border-left-width: 3px;
    border-radius: var(--radius-sm);
    background: var(--surface);
}

/* Непрочитанное отличается фоном, а не только точкой: список читается
   краем глаза, и цвет замечают раньше значка. */
.note-fresh { background: var(--surface-sunken); border-color: var(--border-strong); }

.note-info { border-left-color: var(--border-strong); }
.note-warn { border-left-color: var(--warn); }
.note-bad  { border-left-color: var(--danger); }

.note-mark { font-size: var(--fs-small); line-height: 1.6; }
.note-info .note-mark { color: var(--ink-muted); }
.note-warn .note-mark { color: var(--warn); }
.note-bad  .note-mark { color: var(--danger); }

.note-body { min-width: 0; flex: 1; }
.note-title { font-weight: 600; }
.note-text { color: var(--ink-soft); margin-top: 2px; font-size: var(--fs-body); }

.note-foot {
    margin-top: var(--s1);
    display: flex;
    align-items: center;
    gap: var(--s3);
    color: var(--ink-muted);
    font-size: var(--fs-small);
}

/* ---------- полоса объявлений ---------- */

.announces { display: grid; gap: var(--s2); margin: 0 0 var(--s4); }

/* Объявление читают краем глаза и один раз, поэтому оно шире и заметнее
   обычного уведомления — но закрывается насовсем, чтобы не мозолить. */
.announce {
    display: flex;
    align-items: flex-start;
    gap: var(--s3);
    padding: var(--s3) var(--s3);
    border: 1px solid var(--border-strong);
    border-left-width: 4px;
    border-radius: var(--radius-sm);
    background: var(--surface);
    color: var(--ink);
}

.announce-info { border-left-color: var(--accent); }
.announce-warn { border-left-color: var(--warn); background: var(--warn-soft); }
.announce-bad  { border-left-color: var(--danger); background: var(--danger-soft); }

/* Полоса — MudAlert: значок и цвет по важности ставит он сам, отступы
   и размеры — кабинетные. */
.announce .mud-alert-icon {
    padding: 0;
    margin: 0 var(--s3) 0 0;
    opacity: 1;
}

.announce .mud-alert-icon .mud-icon-root { font-size: 1.15rem; margin-top: 2px; }
.announce .mud-alert-message { padding: 0; min-width: 0; flex: 1; }
.announce-text { margin-top: 2px; color: var(--ink-soft); }

.announce .mud-alert-close { margin-left: var(--s2); align-items: flex-start; }
.announce .mud-alert-close .mud-icon-button { padding: 2px; color: var(--ink-muted); }
.announce .mud-alert-close .mud-icon-button:hover { color: var(--ink); }

/* Строка уведомлений (NoticeLine.razor): видно одно — самое важное, —
   «ещё N — показать» раскрывает остальные. Поздравление в праздник в
   такой день уступает строке: над страницей — одна полоса, не стопка. */
.notice-more { margin-top: var(--s1); }
.notice-more .link-btn { font-size: var(--fs-table); }
.notice-rest { margin: 0; }
.main:has(.notice-line) tamos-holiday { display: none; }

/* Снятое объявление в списке админки — бледнее живого. */
.row-muted td { opacity: 0.55; }

@media print { .announces { display: none; } }

/* ---------- матрица подписок ---------- */

/* Строка области отделена фоном: она главнее своих видов, а не рядом. */
.row-area td { background: var(--surface-sunken); }

/* Виды сдвинуты вправо — видно, чьи они. */
.cell-nested { padding-left: var(--s5) !important; }

/* Галочка, выставленная отдельно, а не унаследованная от области.
   Без пометки не отличить «включено само» от «включено по области», и
   человек не поймёт, почему снятие галочки в области ничего не дало. */
.grid-table input[type=checkbox].own {
    outline: 2px solid var(--accent);
    outline-offset: 1px;
    border-radius: 2px;
}

/* То же для флажка MudBlazor: рамку держит сама коробочка, а не поле. */
.grid-table .mud-input-control.own .mud-icon-button {
    outline: 2px solid var(--accent);
    outline-offset: -6px;
    border-radius: 4px;
}

/* Галочки матрицы — по центру ячейки и без отступов поля. */
.grid-table .mud-input-control:is(.own, .mud-checkbox-input) { display: inline-flex; }

.grid-table .mud-checkbox { margin: 0; }

.link-btn.tiny { font-size: var(--fs-small); padding: 0 2px; }

/* ---------- Задолженность ---------- */

/* Ширину для страниц-реестров задаёт .page-wide (см. выше): десять
   колонок с деньгами в обычные 1120px не помещаются. */

/* Компактнее обычной таблицы: выигрыш по 8px на ячейку — это ровно та
   ширина, которой не хватало последней колонке. */
.debt-table th,
.debt-table td { padding: var(--s2) var(--s2); }

/* Названия школ и видов длинные («Нурлытау · Физико-математический ·
   Русское отделение»); целиком они распирают таблицу за край экрана.
   Обрезаем с многоточием, полное название — во всплывающей подсказке. */
.debt-table .cell-clip {
    max-width: 24ch;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.debt-days {
    display: block;
    margin-top: 2px;
    font-size: 0.8em;
    white-space: nowrap;
}

/* ---------- Поле поиска с очисткой ---------- */

/* Крестик появляется только когда есть что стирать; Esc делает то же
   с клавиатуры — рука не тянется к мыши. */
.search-clear { position: relative; }

.search-clear input {
    width: 100%;
    padding-right: var(--s6);
}

.search-clear .clear-btn {
    position: absolute;
    right: 5px;
    top: 50%;
    transform: translateY(-50%);
    border: 0;
    background: none;
    color: var(--ink-soft);
    font: inherit;
    font-size: var(--fs-body);
    line-height: 1;
    padding: 3px var(--s2);
    border-radius: var(--radius-sm);
    cursor: pointer;
}

.search-clear .clear-btn:hover {
    color: var(--ink);
    background: var(--surface-sunken);
}

/* Заголовок группы внутри таблицы: строки одной группы идут подряд —
   их разделяет тонкая плашка с названием. */
.group-row td {
    background: var(--surface-sunken);
    color: var(--ink-soft);
    font-size: var(--fs-label);
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.06em;
    padding: var(--s1) var(--s3);
}

/* ---------- Подпись с флажком ---------- */

/* Флажок в .field наследовал width:100% от общего правила для input —
   и растягивался на всю строку, отбрасывая свой текст к дальнему краю.
   Здесь он возвращается к естественному размеру и встаёт рядом с
   текстом, за который отвечает. */
label.check {
    display: inline-flex;
    align-items: center;
    gap: var(--s2);
    cursor: pointer;
}

label.check input[type="checkbox"] {
    width: 16px;
    height: 16px;
    margin: 0;
    flex: none;
    accent-color: var(--accent);
}

/* ---------- Строка фактов формы заведения ---------- */

/* Номер и суммы — рядом, подписи мелко сверху. Пояснения к ним живут
   одной строкой-подсказкой ниже, а не внутри ячеек: длинный текст в
   сетке dl.facts наезжал на соседнюю колонку. */
.draft-facts {
    display: flex;
    flex-wrap: wrap;
    gap: var(--s3) var(--s6);
    margin-top: var(--s4);
    padding-top: var(--s3);
    border-top: 1px dashed var(--border);
}

.draft-facts .df {
    display: flex;
    flex-direction: column;
    gap: 2px;
}

.draft-facts .df-label {
    color: var(--ink-muted);
    font-size: var(--fs-small);
}

.draft-facts .df-value {
    font-variant-numeric: tabular-nums;
    font-weight: 600;
}

/* Крестик очистки в большом поле поиска справочника. */
.search-input-wrap input.search { padding-right: var(--s6); }

.search-input-wrap .clear-btn {
    position: absolute;
    right: 8px;
    top: 50%;
    transform: translateY(-50%);
    border: 0;
    background: none;
    color: var(--ink-soft);
    font: inherit;
    line-height: 1;
    padding: var(--s1) var(--s2);
    border-radius: var(--radius-sm);
    cursor: pointer;
}

.search-input-wrap .clear-btn:hover {
    color: var(--ink);
    background: var(--surface-sunken);
}

/* Фильтры-селекты в строке поиска справочника: год и школа у классов. */
.search-row select.head-filter {
    max-width: 340px;
    padding: var(--s2) var(--s3);
    border: 1.5px solid var(--border-field);
    border-radius: var(--radius-sm);
    background: var(--surface);
    color: var(--ink);
    font: inherit;
}

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

.act-sheet {
    font-family: "Times New Roman", Times, serif;
    font-size: 12pt;
    line-height: 1.35;
    color: #111;
}

.act-sheet .act-title {
    text-align: center;
    font-size: 15pt;
    font-weight: 700;
    margin: 0 0 var(--s1);
}

.act-sheet .act-lead { text-align: center; margin: 0 0 var(--s4); }

.act-sheet .act-text { text-align: justify; margin: 0 0 var(--s3); }

.act-table {
    width: 100%;
    border-collapse: collapse;
    margin: var(--s3) 0 var(--s5);
    font-size: 11pt;
}

.act-table th,
.act-table td {
    border: 1px solid #222;
    padding: 3px var(--s1);
    vertical-align: top;
}

.act-table th { text-align: center; font-weight: 700; }

.act-table td.num {
    text-align: right;
    white-space: nowrap;
    font-variant-numeric: tabular-nums;
}

.act-table tfoot td { font-weight: 700; }

.act-sign { display: flex; justify-content: space-between; margin-top: var(--s6); }

.act-stamp { margin-top: var(--s3); padding-left: var(--s1); }

.act-table .act-col-date { width: 16%; }
.act-table .act-col-sum { width: 19%; }

/* ---------- правка акта ----------
   Строки правятся прямо на листе: поля подсвечены, как бланк «впишите
   сюда». Шапка длинная — для неё панель над листом. Служебная колонка
   справа только на экране: печать во время правки выключена. Лист белый
   в обеих темах, поэтому и цвета полей — светлые, без переменных темы. */

.act-fields {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(260px, 1fr));
    gap: var(--s3) var(--s4);
    margin: 0 0 var(--s2);
}

.act-editing .act-table td { padding: 2px 3px; }
.act-editing .act-table .act-col-date { width: 21%; }

.act-sheet .act-input {
    width: 100%;
    min-width: 0;
    box-sizing: border-box;
    font: inherit;
    color: inherit;
    background: #fff8dc;
    border: 1px dashed #c9a227;
    border-radius: 2px;
    padding: 1px var(--s1);
}

.act-sheet .act-input:focus {
    outline: 2px solid var(--accent);
    outline-offset: 0;
    background: #fff;
}

.act-sheet .act-input.num { text-align: right; font-variant-numeric: tabular-nums; }
.act-sheet .act-input.bad { border: 1px solid #c0362c; background: #fdeceb; }

.act-table th.act-tools,
.act-table td.act-tools {
    border: none;
    width: 1%;
    white-space: nowrap;
    padding: 2px 0 2px var(--s1);
    vertical-align: middle;
}

.act-tools button {
    width: 24px;
    height: 24px;
    padding: 0;
    font: 600 0.8125rem/1 system-ui, sans-serif;
    color: #334155;
    background: #fff;
    border: 1px solid #cbd5e1;
    border-radius: 4px;
    cursor: pointer;
}

.act-tools button:hover:not(:disabled) { border-color: var(--accent); color: var(--accent); }
.act-tools button.danger:hover:not(:disabled) { border-color: #c0362c; color: #c0362c; }
.act-tools button:disabled { opacity: 0.35; cursor: default; }

.act-table tr.act-add td { border: none; padding: var(--s1) 0 var(--s2); }

.act-state {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--s2);
    margin: 0 0 var(--s3);
}

.act-history { margin: 0 0 var(--s4); }
.act-history .table-scroll { overflow-x: auto; }
.act-history table { width: 100%; border-collapse: collapse; font-size: var(--fs-table); }
.act-history th,
.act-history td {
    text-align: left;
    padding: var(--s1) var(--s2);
    border-bottom: 1px solid var(--border);
    vertical-align: top;
}
.act-history .num { text-align: right; white-space: nowrap; font-variant-numeric: tabular-nums; }
.act-history tr.on td { background: var(--accent-soft); }

/* ---------- корректировка начислений: сетка дней месяца ---------- */
.day-grid {
    /*  Ширина ячейки посчитана, а не подобрана: флажок MudBlazor — около
        2,4 rem, номер дня — 1,4 rem, сумма вида «11 363,64» в
        моноширинных цифрах — около 4,6 rem, плюс отбивки и поля. При
        6,2 rem сумма вылезала за рамку ячейки и наезжала на соседнюю. */
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(11rem, 1fr));
    gap: var(--s1);
}

.day-cell {
    display: flex;
    align-items: center;
    gap: var(--s1);
    padding: 2px var(--s2) 2px 2px;
    border: 1px solid var(--border);
    border-radius: 6px;
    background: var(--surface);
    cursor: pointer;
    font-variant-numeric: tabular-nums;
    font-size: var(--fs-small);
    min-width: 0;
    overflow: hidden;
}

.day-cell.on { border-color: var(--accent); background: var(--accent-soft); }
.day-cell.zero .day-sum { color: var(--ink-muted); }
.day-cell.row-off { opacity: 0.55; cursor: default; }
.day-cell .day-no { font-weight: 600; min-width: 1.4rem; }

/*  Сумма прижата вправо и не переносится: перенос внутри ячейки делал
    её вдвое выше соседних, и сетка дней шла ступеньками. */
.day-cell .day-sum {
    margin-left: auto;
    white-space: nowrap;
    font-variant-numeric: tabular-nums;
}

/*  Флажок внутри ячейки — без собственных отступов: они прибавлялись к
    отступам ячейки и съедали место у суммы. */
.day-cell .mud-checkbox { margin: 0; }
.day-cell .mud-checkbox .mud-icon-button { padding: var(--s1); }

/* ---------- счёт на оплату ----------
   Тот же лист, что у акта сверки; образец платёжного поручения — рамкой,
   коды КБе и КНП — узкой колонкой справа, как в макете Delphi. */

.invoice-sheet .invoice-caption { margin: var(--s4) 0 0; font-weight: 700; }
.invoice-sheet .invoice-bank { margin: var(--s1) 0 var(--s4); }
.invoice-sheet .invoice-bank td { width: 30%; }
.invoice-sheet .invoice-bank td.invoice-code { width: 10%; text-align: center; }
.invoice-sheet .invoice-title { margin: var(--s5) 0 var(--s1); }
.invoice-sheet .invoice-gap { margin-left: var(--s6); }


/* ---------- MudBlazor: компоненты в виде кабинета ----------

   Размеры, отступы и шрифты компонентов MudBlazor — как у прежних
   элементов (.btn, .chip, .grid-table), чтобы переведённый экран не
   выделялся среди непереведённых. Цвета приходят из моста ниже. */

/* Кнопки — рост и скругление .btn. */
.mud-button-filled.mud-button-filled-size-medium,
.mud-button-outlined.mud-button-outlined-size-medium {
    padding: var(--s2) var(--s4);
    border: 1px solid transparent;
    border-radius: var(--radius-sm);
    font-size: var(--fs-body);
    line-height: 1.55;
}

.mud-button-outlined.mud-button-outlined-size-medium { border-color: var(--border-strong); }

/* Мелкая кнопка — как .btn.btn-small: своя рамка у MudBlazor почти
   не видна, а рядом с ней стоят прежние кнопки той же высоты. */
.mud-button-filled.mud-button-filled-size-small,
.mud-button-outlined.mud-button-outlined-size-small {
    padding: var(--s1) var(--s3);
    border: 1px solid transparent;
    border-radius: var(--radius-sm);
    font-size: var(--fs-table);
    line-height: 1.55;
}

.mud-button-outlined.mud-button-outlined-size-small { border-color: var(--border-strong); }

/* Фишки отбора — как .chip. */
.chips .mud-chip.mud-chip-outlined {
    height: auto;
    margin: 0;
    padding: var(--s1) var(--s3);
    border-radius: 999px;
    border: 1.5px solid var(--border-strong);
    background: var(--surface);
    color: var(--ink-soft);
    font-size: var(--fs-small);
    line-height: 1.55;
}

.chips .mud-chip.mud-chip-outlined:hover { border-color: var(--accent); color: var(--ink); }

.chips .mud-chip.mud-chip-outlined.chip-on {
    background: var(--accent-soft);
    border-color: var(--accent);
    color: var(--accent-ink);
    font-weight: 600;
}

/* Ряд полей отбора: три списка и поиск, забирающий остаток. */
.filter-grid {
    display: grid;
    grid-template-columns: repeat(3, minmax(150px, 200px)) minmax(240px, 1fr);
    gap: var(--s1) var(--s3);
    align-items: start;
}

/* Пять полей: два календаря, два списка и поиск. */
.filter-grid.filter-grid-5 {
    grid-template-columns: repeat(2, minmax(140px, 170px)) repeat(2, minmax(150px, 220px)) minmax(220px, 1fr);
}

/* Сверка начислений: год, школа и поиск — поиску отдан весь остаток. */
.filter-grid.filter-grid-audit {
    grid-template-columns: minmax(150px, 200px) minmax(150px, 260px) minmax(240px, 1fr);
}

/* Задолженность: год, школа, поиск и флажок «только с просрочкой». */
.filter-grid.filter-grid-debts {
    grid-template-columns: minmax(150px, 200px) minmax(150px, 260px) minmax(220px, 1fr) auto;
    align-items: center;
}

/* Льготы: год, школа, льгота и поиск. */
.filter-grid.filter-grid-discounts {
    grid-template-columns: minmax(150px, 200px) minmax(150px, 240px) minmax(200px, 1fr) minmax(220px, 1fr);
}

/* Журнал сервера: две даты и размер страницы. */
.filter-grid.filter-grid-server {
    grid-template-columns: repeat(2, minmax(140px, 170px)) minmax(110px, 140px);
    margin-top: var(--s3);
}

/* Журнал событий: две даты, сотрудник, раздел и размер страницы. */
.filter-grid.filter-grid-audit {
    grid-template-columns: repeat(2, minmax(140px, 170px)) minmax(160px, 1fr) minmax(160px, 1fr) minmax(110px, 140px);
    margin-top: var(--s3);
}

/* Поиск в карточке — во всю ширину, кнопка рядом. */
.search-row .search-main { flex: 1; }

/* Справочники: поиск занимает строку, отбор шапки — прежней узкой
   колонкой. Список MudBlazor иначе растягивается наравне с поиском. */
.search-row .mud-input-control.search-main { flex: 1 1 260px; }

.search-row .mud-input-control.head-filter { flex: 0 1 340px; max-width: 340px; }

/* Кнопка «подробности» в строке журнала — мельче текста события. */
.grid-mud .details-btn.mud-button { font-size: var(--fs-small); }

/* Непривязанные платежи: юрлицо (если их два), дата и поиск. */
.filter-grid.filter-grid-unlinked {
    grid-template-columns: minmax(200px, 300px) minmax(150px, 190px) minmax(240px, 1fr);
}

.filter-grid.filter-grid-unlinked-one {
    grid-template-columns: minmax(150px, 190px) minmax(240px, 1fr);
}

/* Классы в договорах: год и поиск. */
.filter-grid.filter-grid-classes {
    grid-template-columns: minmax(150px, 200px) minmax(240px, 1fr);
}

/* Удержание: переход между годами, класс и школа. */
.filter-grid.filter-grid-retention {
    grid-template-columns: minmax(200px, 240px) minmax(130px, 160px) minmax(240px, 1fr);
}

/*  Меню слева (236 px) ширину не отдаёт: точка 860 px считает всё окно,
    а полям отбора остаётся окно минус ~350 px (меню и отступы). При окне
    950 px «Задолженности» оставалось ~600 px на 750 px полей — флажки
    уезжали за край, и прокручивалась вся страница. Поэтому сетки уходят
    в два столбца раньше: каждая — с той ширины окна, где её столбцы
    рядом с меню уже не помещаются. Ниже 860 px меню выдвижное — там
    своё правило ниже.                                                 */
@media (min-width: 861px) and (max-width: 1200px) {
    .filter-grid.filter-grid-5 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
    .filter-grid.filter-grid-5 .filter-wide { grid-column: 1 / -1; }
}

@media (min-width: 861px) and (max-width: 1110px) {
    .filter-grid,
    .filter-grid.filter-grid-debts,
    .filter-grid.filter-grid-discounts,
    .filter-grid.filter-grid-audit { grid-template-columns: repeat(2, minmax(0, 1fr)); }
    .filter-grid .filter-wide { grid-column: 1 / -1; }
}

@media (min-width: 861px) and (max-width: 970px) {
    .filter-grid.filter-grid-unlinked,
    .filter-grid.filter-grid-retention { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

@media (max-width: 860px) {
    .filter-grid,
    .filter-grid.filter-grid-5,
    .filter-grid.filter-grid-debts,
    .filter-grid.filter-grid-discounts,
    .filter-grid.filter-grid-unlinked,
    .filter-grid.filter-grid-unlinked-one,
    .filter-grid.filter-grid-classes,
    .filter-grid.filter-grid-retention,
    .filter-grid.filter-grid-audit,
    .filter-grid.filter-grid-server { grid-template-columns: repeat(2, minmax(0, 1fr)); }
    .filter-grid .filter-wide { grid-column: 1 / -1; }
}

@media (max-width: 520px) {
    .filter-grid,
    .filter-grid.filter-grid-5,
    .filter-grid.filter-grid-debts,
    .filter-grid.filter-grid-discounts,
    .filter-grid.filter-grid-unlinked,
    .filter-grid.filter-grid-unlinked-one,
    .filter-grid.filter-grid-classes,
    .filter-grid.filter-grid-retention,
    .filter-grid.filter-grid-audit,
    .filter-grid.filter-grid-server { grid-template-columns: minmax(0, 1fr); }
}

/* Корректировка: действие, значение и причина — в одну строку. */
.filter-grid.filter-grid-correction {
    /* Нижние границы — чтобы ряд помещался рядом с меню и при окне 900 px
       (было 180 + 140 + 260: поле причины уезжало за край на 12 px). */
    grid-template-columns: minmax(150px, 240px) minmax(120px, 180px) minmax(200px, 1fr);
}

/* Пересборка графика: дата и причина. */
.filter-grid.filter-grid-rebuild {
    grid-template-columns: minmax(140px, 190px) minmax(260px, 1fr);
}

/* Правка проводки: сумма, возврат, дата и банк — в одну строку;
   ниже описание, плательщик, вид оплаты и взнос. */
.filter-grid.filter-grid-ledger {
    grid-template-columns: minmax(140px, 200px) auto minmax(140px, 170px) minmax(160px, 240px);
    align-items: center;
}

.filter-grid.filter-grid-ledger2 {
    grid-template-columns: minmax(240px, 1fr) minmax(150px, 220px) minmax(150px, 220px) auto;
    align-items: center;
}

/* Счёт: номер, дата, количество, цена и итог — в одну строку. */
.filter-grid.filter-grid-invoice {
    grid-template-columns: minmax(140px, 200px) minmax(140px, 170px) minmax(110px, 140px) minmax(140px, 180px) auto;
}

/* Поле формы MudBlazor в панели: тот же вертикальный шаг, что у
   прежнего .field. */
.form-field { margin-bottom: var(--s4); }

/* Форма в панели: поля одно под другим с общим шагом. */
.link-form { display: grid; gap: var(--s2); max-width: 760px; }

/* Сообщение над содержимым страницы (MudAlert) — в виде прежнего
   .alert: рамка и мягкая заливка цвета важности. Значок — от MudBlazor. */
.page-alert.mud-alert {
    margin: 0 0 var(--s4);
    padding: var(--s3) var(--s4);
    border: 1px solid;
    border-radius: var(--radius);
    font-size: var(--fs-table);
}

.page-alert.mud-alert-text-error   { background: var(--danger-soft); border-color: var(--danger); color: var(--danger); }
.page-alert.mud-alert-text-warning { background: var(--warn-soft);   border-color: var(--warn);   color: var(--warn); }
.page-alert.mud-alert-text-info    { background: var(--accent-soft); border-color: var(--accent); color: var(--accent-ink); }

.page-alert .mud-alert-message { padding: 0; }

.page-alert .mud-alert-icon {
    padding: 0;
    margin: 0 var(--s3) 0 0;
    opacity: 1;
}

.page-alert .mud-alert-icon .mud-icon-root { font-size: 1.15rem; margin-top: 1px; }

/* Таблица реестра (MudTable с классом grid-mud) — как .grid-table. */
.grid-mud.mud-table.mud-table-outlined {
    border-color: var(--border);
    border-radius: var(--radius);
}

/* Прокрутка — как у .table-wrap («одна прокрутка»): влезающая по
   ширине таблица едет со страницей, и закреплённая шапка (FixedHeader)
   липнет к окну; широкая — в прежнем ящике в две трети экрана.
   Полоса загрузки — под шапкой. */
.grid-mud .mud-table-container { overflow-x: auto; }
.grid-mud .mud-table-container.fits-x { overflow: clip; }
.grid-mud .mud-table-container.wide-x { max-height: 64vh; overflow: auto; }

.grid-mud .mud-table-container.fits-x .mud-table-root .mud-table-head .mud-table-cell { top: var(--sticky-top); }

.grid-mud.mud-table-sticky-header .mud-table-root .mud-table-head .mud-table-loading { top: 40px; }
.grid-mud.mud-table-sticky-header .mud-table-container.fits-x .mud-table-root .mud-table-head .mud-table-loading {
    top: calc(var(--sticky-top) + 40px);
}

/* Закреплённая шапка MudBlazor скругляет крайние ячейки по-своему, и
   над шапкой проступал уголок рамки. Скругление держит сама таблица —
   обрезкой clip: при hidden шапка липла бы к таблице, а не к окну. */
.grid-mud.mud-table-outlined { overflow: clip; }

.grid-mud.mud-table-sticky-header .mud-table-root .mud-table-head .mud-table-cell:is(:first-child, :last-child) {
    border-radius: 0;
}

/* Таблица-разрез внутри панели — без своей рамки и фона. */
.grid-mud.grid-flat.mud-table { background: transparent; }

/* Реестр договоров — плотнее: тринадцать столбцов, со скидкой и
   процентом. Длинные названия (отделение, скидка) обрезаются раньше
   обычного — целиком они в подсказке, — иначе последний столбец уходит
   за край страницы. */
.grid-mud.contracts-table .mud-table-root .mud-table-head .mud-table-cell,
.grid-mud.contracts-table .mud-table-root .mud-table-body .mud-table-cell,
.grid-mud.audit-table .mud-table-root .mud-table-head .mud-table-cell,
.grid-mud.audit-table .mud-table-root .mud-table-body .mud-table-cell { padding: var(--s2) var(--s2); }
.grid-mud.contracts-table .mud-table-cell.cell-clip,
.grid-mud.audit-table .mud-table-cell.cell-clip { max-width: 18ch; }

/* Ведомость долгов — плотнее: десять столбцов. */
.grid-mud.debt-table .mud-table-root .mud-table-head .mud-table-cell,
.grid-mud.debt-table .mud-table-root .mud-table-body .mud-table-cell { padding: var(--s2) var(--s2); }

/* Выбранная строка — как .row-picked в старых таблицах: заливка и
   полоска слева. Наведение MudTable её не перекрашивает. */
.grid-mud .mud-table-root .mud-table-body .mud-table-row.row-picked,
.grid-mud.mud-table-hover .mud-table-container .mud-table-root .mud-table-body .mud-table-row.row-picked:hover {
    background-color: var(--accent-soft);
}

.grid-mud .mud-table-root .mud-table-body .mud-table-row.row-picked .mud-table-cell:first-child {
    box-shadow: inset 3px 0 0 var(--accent);
}

.grid-mud .mud-table-cell.actions { text-align: right; white-space: nowrap; }

/* Несколько ссылок-действий в одной ячейке разделяем, иначе они
   слипаются в одно слово. */
.grid-mud .mud-table-cell.actions .link-btn + .link-btn { margin-left: var(--s3); }

/* Строка со сбоем — как .row-error в старых таблицах. */
.grid-mud .mud-table-root .mud-table-body .mud-table-row.row-error .mud-table-cell,
.grid-mud.mud-table-hover .mud-table-container .mud-table-root .mud-table-body .mud-table-row.row-error:hover .mud-table-cell {
    background-color: var(--danger-soft);
}

/* Строка, о которой ничего не известно: ушёл, а записи нет (Удержание). */
.grid-mud .mud-table-root .mud-table-body .mud-table-row.row-warn .mud-table-cell,
.grid-mud.mud-table-hover .mud-table-container .mud-table-root .mud-table-body .mud-table-row.row-warn:hover .mud-table-cell {
    background-color: var(--warn-soft);
}

.grid-mud .mud-table-cell.cell-clip {
    max-width: 24ch;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

/* В узких разрезах (два столбца панелей) название переносится, а не
   обрезается: там оно одно на строку, и ширины на него не хватает. */
.grid-mud.grid-flat .mud-table-cell.cell-clip {
    max-width: none;
    white-space: normal;
}

/* Шапка — как у .grid-table: строчными, 12,5 px, --ink-soft. */
.grid-mud .mud-table-root .mud-table-head .mud-table-cell {
    padding: var(--s2) var(--s3);
    font-size: var(--fs-label);
    letter-spacing: 0.005em;
    color: var(--ink-soft);
    font-weight: 600;
    line-height: 1.55;
    white-space: nowrap;
    border-bottom-color: var(--border);
}

.grid-mud .mud-table-root .mud-table-body .mud-table-cell {
    padding: var(--s3) var(--s3);
    font-size: var(--fs-table);
    line-height: 1.55;
    letter-spacing: normal;
    vertical-align: top;
    border-bottom-color: var(--border);
}

.grid-mud .mud-table-head .mud-table-cell.sortable { cursor: pointer; user-select: none; }
.grid-mud .mud-table-head .mud-table-cell.sortable:hover { color: var(--accent); }
.grid-mud .sort-mark { font-size: var(--fs-small); color: var(--accent); vertical-align: -2px; margin-left: 2px; }

/* Кнопка сортировки в шапке (SortTh.razor) — выглядит как подпись
   столбца, но берёт фокус и нажимается Enter и пробелом. */
.sort-btn {
    display: inline-flex;
    align-items: center;
    gap: 2px;
    margin: 0;
    padding: 0;
    border: 0;
    background: none;
    font: inherit;
    color: inherit;
    letter-spacing: inherit;
    text-align: inherit;
    white-space: nowrap;
    cursor: pointer;
}

.sort-btn:hover { color: var(--accent); }

.sort-btn:focus-visible {
    outline: 2px solid var(--accent);
    outline-offset: 2px;
    border-radius: 4px;
}

.sort-btn .sort-mark { margin-left: 2px; }

.grid-mud .mud-table-cell.mono { font-family: var(--mono); letter-spacing: 0.02em; }
.grid-mud .mud-table-cell.digits { text-align: right; font-variant-numeric: tabular-nums; }
.grid-mud .mud-table-body .mud-table-cell.muted-cell { color: var(--ink-muted); }

/* Цветные ячейки: правило цвета MudTable точнее .ink-*, и без этого
   возврат в «Оплатах» или 13+ месяцев в «Начислениях» были бы чёрными. */
.grid-mud .mud-table-body .mud-table-cell.ink-danger { color: var(--danger); }
.grid-mud .mud-table-body .mud-table-cell.ink-warn   { color: var(--warn); }
.grid-mud .mud-table-body .mud-table-cell.ink-ok     { color: var(--ok); }

/* Заголовок группы — плашка, как .group-row в старых таблицах. */
.grid-mud .mud-table-root .mud-table-body .group-row .mud-table-cell {
    background: var(--surface-sunken);
    color: var(--ink-soft);
    font-size: var(--fs-label);
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.06em;
    padding: var(--s1) var(--s3);
}

/* У MudBlazor блок листалки строчный — граница сверху рисовалась
   обрывком шириной в первую кнопку. */
.grid-mud .mud-table-pagination {
    display: block;
    border-top: 1px solid var(--border);
    padding: var(--s2) var(--s3);
}

.grid-mud .pager { margin-top: 0; }

/* Листалка (Pager, MudPagination) — кнопки в рамке, текущая залита
   акцентом. Узкий экран таблица не превращает в карточки
   (Breakpoint.None): страница и так шире телефона, и значения карточек
   уезжали бы за край. */
.pager .mud-pagination { flex-wrap: wrap; }

.pager .mud-pagination .mud-pagination-item > .mud-button-root {
    height: auto;
    min-width: 34px;
    margin: 0;
    padding: var(--s1) var(--s2);
    border: 1.5px solid var(--border-strong);
    border-radius: var(--radius-sm);
    background: var(--surface);
    color: var(--ink-soft);
    font-size: var(--fs-table);
    font-weight: 400;
    line-height: 1.55;
    font-variant-numeric: tabular-nums;
}

.pager .mud-pagination .mud-pagination-item > .mud-button-root:hover:not(:disabled) {
    border-color: var(--accent);
    color: var(--ink);
}

.pager .mud-pagination .mud-pagination-item > .mud-button-root:disabled { opacity: 0.45; }

.pager .mud-pagination .mud-pagination-item-selected > .mud-button-root {
    background: var(--accent);
    border-color: var(--accent);
    color: var(--on-accent);
    font-weight: 600;
}

.empty-inline {
    padding: var(--s6) var(--s3);
    text-align: center;
    color: var(--ink-muted);
}

/* ---------- MudBlazor ↔ токены кабинета ----------

   Палитра MudBlazor привязана к тем же переменным, что и всё остальное
   оформление, поэтому светлая, тёмная и «как в системе» переключаются для
   компонентов MudBlazor тем же атрибутом data-theme и той же настройкой
   системы — без скриптов. MudThemeProvider пишет свои значения в :root;
   :root:root точнее, и привязка побеждает при любом порядке подключения.
   Цвета меняются в токенах выше, здесь — только соответствие. */

:root:root {
    --mud-palette-primary:          var(--accent);
    --mud-palette-primary-rgb:      var(--accent-rgb);
    --mud-palette-primary-text:     var(--on-accent);
    --mud-palette-primary-darken:   var(--accent-hover);
    --mud-palette-primary-lighten:  rgba(var(--accent-rgb), 0.72);
    --mud-palette-primary-hover:    rgba(var(--accent-rgb), 0.06);

    --mud-palette-secondary:         var(--ink-soft);
    --mud-palette-secondary-rgb:     var(--ink-soft-rgb);
    --mud-palette-secondary-text:    var(--surface);
    --mud-palette-secondary-darken:  var(--ink);
    --mud-palette-secondary-lighten: var(--ink-muted);
    --mud-palette-secondary-hover:   rgba(var(--ink-soft-rgb), 0.06);

    --mud-palette-tertiary:          var(--accent-ink);
    --mud-palette-tertiary-rgb:      var(--accent-ink-rgb);
    --mud-palette-tertiary-text:     var(--surface);
    --mud-palette-tertiary-darken:   var(--accent-ink);
    --mud-palette-tertiary-lighten:  rgba(var(--accent-ink-rgb), 0.72);
    --mud-palette-tertiary-hover:    rgba(var(--accent-ink-rgb), 0.06);

    --mud-palette-info:          var(--accent);
    --mud-palette-info-rgb:      var(--accent-rgb);
    --mud-palette-info-text:     var(--on-accent);
    --mud-palette-info-darken:   var(--accent-hover);
    --mud-palette-info-lighten:  rgba(var(--accent-rgb), 0.72);
    --mud-palette-info-hover:    rgba(var(--accent-rgb), 0.06);

    --mud-palette-success:          var(--ok);
    --mud-palette-success-rgb:      var(--ok-rgb);
    --mud-palette-success-text:     var(--surface);
    --mud-palette-success-darken:   color-mix(in srgb, var(--ok) 85%, var(--ink));
    --mud-palette-success-lighten:  rgba(var(--ok-rgb), 0.72);
    --mud-palette-success-hover:    rgba(var(--ok-rgb), 0.06);

    --mud-palette-warning:          var(--warn);
    --mud-palette-warning-rgb:      var(--warn-rgb);
    --mud-palette-warning-text:     var(--surface);
    --mud-palette-warning-darken:   color-mix(in srgb, var(--warn) 85%, var(--ink));
    --mud-palette-warning-lighten:  rgba(var(--warn-rgb), 0.72);
    --mud-palette-warning-hover:    rgba(var(--warn-rgb), 0.06);

    --mud-palette-error:          var(--danger);
    --mud-palette-error-rgb:      var(--danger-rgb);
    --mud-palette-error-text:     var(--on-danger);
    --mud-palette-error-darken:   color-mix(in srgb, var(--danger) 85%, var(--ink));
    --mud-palette-error-lighten:  rgba(var(--danger-rgb), 0.72);
    --mud-palette-error-hover:    rgba(var(--danger-rgb), 0.06);

    --mud-palette-dark:          var(--ink);
    --mud-palette-dark-rgb:      var(--ink-rgb);
    --mud-palette-dark-text:     var(--surface);
    --mud-palette-dark-darken:   var(--ink);
    --mud-palette-dark-lighten:  var(--ink-soft);
    --mud-palette-dark-hover:    rgba(var(--ink-rgb), 0.06);

    --mud-palette-text-primary:       var(--ink);
    --mud-palette-text-primary-rgb:   var(--ink-rgb);
    --mud-palette-text-secondary:     var(--ink-soft);
    --mud-palette-text-secondary-rgb: var(--ink-soft-rgb);
    --mud-palette-text-disabled:      var(--ink-muted);
    --mud-palette-text-disabled-rgb:  var(--ink-muted-rgb);

    --mud-palette-action-default:             var(--ink-soft);
    --mud-palette-action-default-hover:       rgba(var(--ink-rgb), 0.06);
    --mud-palette-action-disabled:            rgba(var(--ink-rgb), 0.30);
    --mud-palette-action-disabled-background: rgba(var(--ink-rgb), 0.08);

    --mud-palette-surface:            var(--surface);
    --mud-palette-surface-rgb:        var(--surface-rgb);
    --mud-palette-background:         var(--bg);
    --mud-palette-background-gray:    var(--surface-sunken);
    --mud-palette-drawer-background:  var(--surface);
    --mud-palette-drawer-text:        var(--ink);
    --mud-palette-drawer-icon:        var(--ink-soft);
    --mud-palette-appbar-background:  var(--surface);
    --mud-palette-appbar-text:        var(--ink);

    --mud-palette-lines-default:  var(--border);
    --mud-palette-lines-inputs:   var(--border-field);
    --mud-palette-table-lines:    var(--border);
    --mud-palette-table-striped:  var(--surface-sunken);
    --mud-palette-table-hover:    var(--surface-sunken);
    --mud-palette-divider:        var(--border);
    --mud-palette-divider-rgb:    var(--border-rgb);
    --mud-palette-divider-light:  var(--border);
    --mud-palette-skeleton:       rgba(var(--ink-rgb), 0.08);

    --mud-palette-gray-default:  var(--ink-muted);
    --mud-palette-gray-light:    var(--border-strong);
    --mud-palette-gray-lighter:  var(--border);
    --mud-palette-gray-dark:     var(--ink-soft);
    --mud-palette-gray-darker:   var(--ink);
    --mud-palette-overlay-dark:  rgba(15, 20, 26, 0.5);
    --mud-palette-overlay-light: rgba(var(--surface-rgb), 0.5);

    /* Тени — те же два уровня, что у карточек кабинета: мелкие
       возвышения как карточка, всплывающие окна и диалоги — как
       приподнятая. */
    --mud-elevation-1:  var(--shadow);
    --mud-elevation-2:  var(--shadow);
    --mud-elevation-3:  var(--shadow);
    --mud-elevation-4:  var(--shadow);
    --mud-elevation-6:  var(--shadow-lift);
    --mud-elevation-8:  var(--shadow-lift);
    --mud-elevation-12: var(--shadow-lift);
    --mud-elevation-16: var(--shadow-lift);
    --mud-elevation-24: var(--shadow-lift);
    --mud-elevation-25: var(--shadow-lift);
}

/* Кнопки кабинета плоские — у залитых кнопок MudBlazor материальную тень
   убираем, чтобы старые и новые кнопки на одном экране не спорили. */
.mud-button-filled,
.mud-button-filled:hover,
.mud-button-filled:active { box-shadow: none; }

/* Выбор файла бланка: кнопка по содержимому, а не во всю панель. */
.mud-input-control.mud-file-upload { display: inline-block; width: auto; }

/* Главная ячейка строки — полужирной, как .cell-main в старых таблицах:
   собственный шрифт ячейки MudTable перебивает общее правило. */
.grid-mud .mud-table-cell.cell-main { font-weight: 600; }

/* Логин и номер документа — моноширинными, как в старых таблицах:
   свой шрифт ячейки MudTable перебивает общее правило .iin. */
.grid-mud .mud-table-cell.iin {
    font-family: var(--mono);
    letter-spacing: 0.06em;
}

/* Погашенная строка — как .row-off и .row-muted в старых таблицах. */
.grid-mud .mud-table-root .mud-table-body .mud-table-row:is(.row-off, .row-muted) .mud-table-cell {
    opacity: 0.55;
}

/* Роли и права: плитка права — флажок MudBlazor в прежней рамке.
   Своей рамки и отступов у флажка нет, их держит плитка. */
.perm-grid .mud-input-control.perm { padding: var(--s1) var(--s3) var(--s1) var(--s1); }

.perm-grid .mud-input-control.perm .mud-input-control-input-container > div { margin: 0; }

.perm-grid .mud-input-control.perm .mud-checkbox { margin: 0; align-items: flex-start; }

.perm-grid .mud-input-control.perm .mud-icon-button { padding: var(--s1); }

.perm-grid .mud-input-control.perm .mud-typography { display: contents; }

/* Обводка поля MudBlazor при наборе.

   Материальная рамка сама по себе толстеет и синеет на фокусе, и этого
   мало: за день бухгалтер щёлкает по десяткам полей и глазами ищет, где
   сейчас курсор. Обводка вокруг всей рамки — та же, что у кнопок и
   полей вне MudBlazor: сплошная линия с зазором, а не мягкое кольцо
   (у кольца контраст был около 1,3 — см. «обводка при переходе с
   клавиатуры»). */
.mud-input-control:focus-within .mud-input-outlined-border {
    outline: 2px solid var(--accent);
    outline-offset: 2px;
}

/* =========================================================================
   ДЕНЬГИ В ЦВЕТЕ

   Бухгалтер ищет глазами не строку, а расхождение: где скидка съела
   сумму, где начислено больше прайса, где долг просрочен. До сих пор всё
   это было одинаково чёрными цифрами, и расхождение приходилось искать
   вычитанием в уме.

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

   Заливка мягкая, цифра остаётся тёмной и читаемой. Именно поэтому не
   взят цветной текст: колонка цветных цифр читается хуже чёрной, а
   разряды в ней сливаются.

   ОДНОГО ЦВЕТА МАЛО. Зелёную и красную подложку путает каждый
   двенадцатый мужчина, а на тусклом мониторе их не различает никто.
   Поэтому за суммой стоит ещё и значок: ▼ — меньше, ▲ — больше,
   ! — посмотреть. Значок — после числа: перед ним, в ::before, на
   телефоне стоит подпись столбца (таблицы карточками). Чтение с экрана
   произносит не «треугольник», а слово — после косой черты.
   ========================================================================= */

.sum-cut, .sum-over, .sum-watch {
    border-radius: var(--radius-sm);
    padding: 1px var(--s1);
    font-variant-numeric: tabular-nums;
}

.sum-cut   { background: var(--sum-cut-bg); }
.sum-over  { background: var(--sum-over-bg); }
.sum-watch { background: var(--sum-watch-bg); }

.sum-cut::after, .sum-over::after, .sum-watch::after {
    margin-left: 0.3em;
    font-size: 0.78em;
    font-weight: 700;
}

/* Первое объявление — для браузеров без «/ текст» после содержимого:
   второе они отбросят целиком и оставят первое. */
.sum-cut::after   { content: "▼"; content: "▼" / " меньше"; color: var(--ok); }
.sum-over::after  { content: "▲"; content: "▲" / " больше"; color: var(--danger); }
.sum-watch::after { content: "!"; content: "!" / " проверить"; color: var(--warn); }

/* Знак рядом с суммой — цветом, а не заливкой: он и так мелкий. */
.sum-note { margin-left: var(--s2); font-size: var(--fs-small); }
.sum-note.cut   { color: var(--ok); }
.sum-note.over  { color: var(--danger); }
.sum-note.watch { color: var(--warn); }

/* В таблице подкрашивается вся ячейка: число в ней прижато к правому
   краю, и пилюля вокруг него читалась бы как заплатка. */
.grid-mud .mud-table-cell.sum-cut,
.grid-mud .mud-table-cell.sum-over,
.grid-mud .mud-table-cell.sum-watch { padding-inline: var(--s3); border-radius: 0; }

.grid-mud .mud-table-root .mud-table-body .mud-table-row .mud-table-cell.sum-cut   { background: var(--sum-cut-bg); }
.grid-mud .mud-table-root .mud-table-body .mud-table-row .mud-table-cell.sum-over  { background: var(--sum-over-bg); }
.grid-mud .mud-table-root .mud-table-body .mud-table-row .mud-table-cell.sum-watch { background: var(--sum-watch-bg); }

/* Пара «подпись — сумма» в панелях черновика и карточки. */
.df-value .sum-cut, .df-value .sum-over, .df-value .sum-watch { display: inline-block; }

/* =========================================================================
   ИТОГ ПО УЧЕНИКУ (ведомость задолженности)

   Когда бухгалтер ищет одного ученика, ему нужен не список строк, а
   ответ: сколько этот ребёнок должен всего. Строки остаются — по ним
   видно, из чего сложилась сумма.
   ========================================================================= */

.one-student {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    gap: var(--s2) var(--s5);
    margin: var(--s4) 0 0;
    padding: var(--s3) var(--s4);
    background: var(--surface);
    border: 1px solid var(--border);
    border-left: 3px solid var(--accent);
    border-radius: var(--radius);
}

.one-student .os-name { font-weight: 600; font-size: var(--fs-section); }
.one-student .os-item { display: flex; align-items: baseline; gap: var(--s2); }
.one-student .os-label { color: var(--ink-muted); font-size: var(--fs-small); }

.one-student .os-sum {
    font-variant-numeric: tabular-nums;
    font-size: var(--fs-section);
    font-weight: 600;
}

.one-student .os-sum.over { color: var(--danger); }
.one-student .os-kinds { flex-basis: 100%; color: var(--ink-muted); font-size: var(--fs-small); }

/* День, начисленный не по прайсу. Ячейка маленькая, заливать её целиком
   нельзя — потеряется отметка выбора, — поэтому цветом помечена только
   сумма и левый край. */
.day-cell.cut  { border-left: 3px solid var(--ok); }
.day-cell.over { border-left: 3px solid var(--danger); }
.day-cell.cut .day-sum  { color: var(--ok); }
.day-cell.over .day-sum { color: var(--danger); }

/* =========================================================================
   ПЛОТНЫЕ СТРОКИ

   Бухгалтер и секретарь смотрят не в одну строку, а в список: реестр из
   пяти тысяч договоров, ведомость долгов, журнал событий. Для них высота
   строки — это сколько видно за раз, то есть сколько раз придётся
   прокрутить и сколько держать в голове.

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

   ПОЧЕМУ ЭТО НАСТРОЙКА, А НЕ РЕШЕНИЕ ЗА ЧЕЛОВЕКА. На широком мониторе
   бухгалтерии плотный вид выигрывает, на ноутбуке секретаря — нет:
   строки сливаются, и попасть в нужную становится труднее. Выбор
   оставлен человеку и живёт в cookie его рабочего места.

   МЕНЯЕТСЯ ТОЛЬКО ВОЗДУХ. Шрифт, цвета и порядок столбцов те же:
   плотный вид — не другой кабинет, а тот же, сжатый по вертикали.
   ========================================================================= */

[data-density="dense"] .grid-mud .mud-table-root .mud-table-head .mud-table-cell {
    padding: var(--s1) var(--s3);
    line-height: 1.4;
}

[data-density="dense"] .grid-mud .mud-table-root .mud-table-body .mud-table-cell {
    padding: var(--s1) var(--s3);
    line-height: 1.4;
}

[data-density="dense"] .grid-mud.contracts-table .mud-table-root .mud-table-head .mud-table-cell,
[data-density="dense"] .grid-mud.contracts-table .mud-table-root .mud-table-body .mud-table-cell,
[data-density="dense"] .grid-mud.audit-table .mud-table-root .mud-table-head .mud-table-cell,
[data-density="dense"] .grid-mud.audit-table .mud-table-root .mud-table-body .mud-table-cell,
[data-density="dense"] .grid-mud.debt-table .mud-table-root .mud-table-head .mud-table-cell,
[data-density="dense"] .grid-mud.debt-table .mud-table-root .mud-table-body .mud-table-cell {
    padding: var(--s1) var(--s2);
}

[data-density="dense"] .grid-table th { padding: var(--s1) var(--s3); }
[data-density="dense"] .grid-table td { padding: var(--s1) var(--s3); }

/* Список длиннее — значит и окно под него выше: прокручивать полупустой
   экран незачем. */
[data-density="dense"] .grid-mud .mud-table-container,
[data-density="dense"] .table-wrap { max-height: 74vh; }

/* Кнопки внутри строки съедают выигрыш, если оставить им прежние поля. */
[data-density="dense"] .grid-mud .mud-table-cell .mud-button-root { padding: 2px var(--s2); }

/* Панели вокруг таблиц тоже подтягиваются: иначе сжатая таблица сидит в
   прежней раме и половина выигрыша уходит на поля. */
[data-density="dense"] .panel { padding: var(--s3) var(--s4); }
[data-density="dense"] .panel-table { padding-bottom: var(--s2); }
[data-density="dense"] .tally { padding: var(--s2) var(--s4); gap: var(--s4); }

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

/* Строка-легенда под таблицей: квадратик цвета и что он значит. */
.legend {
    display: flex;
    flex-wrap: wrap;
    gap: var(--s1) var(--s5);
    align-items: center;
}

.legend-item { display: inline-flex; align-items: center; gap: var(--s2); }

.legend-swatch {
    width: 14px;
    height: 14px;
    border-radius: 4px;
    border: 1px solid var(--border);
    flex: 0 0 auto;
    padding: 0;
}

/* Образец суммы в легенде — та же подложка с тем же значком, что в
   таблице: «▼ — меньше прайса», а не просто цветной квадратик. */
.legend-swatch:is(.sum-cut, .sum-over, .sum-watch) {
    width: auto;
    min-width: 24px;
    height: auto;
    padding: 0 var(--s1);
    display: inline-grid;
    place-items: center;
    line-height: 1.45;
}

.legend-swatch:is(.sum-cut, .sum-over, .sum-watch)::after { margin-left: 0; }

/* Подсказка про клавиши: рядом с кнопками, но тише их. Отступ — чтобы не
   сливалась с соседней подсказкой («Номер присвоится при сохранении»). */
.keys { white-space: nowrap; margin-left: var(--s3); }

/* =========================================================================
   ОБУЧЕНИЕ
   Инструкции по ролям (LearnPage, LearnGuidePage). Читают их не в спешке,
   а по шагам, повторяя за текстом, — поэтому крупнее и просторнее
   рабочих экранов: строка до 72 знаков, снимки во всю ширину колонки.
   ========================================================================= */

.learn-cards {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
    gap: var(--s4);
}

.learn-card {
    display: flex;
    gap: var(--s4);
    padding: var(--s4);
    border: 1px solid var(--border);
    border-radius: 10px;
    background: var(--surface);
    color: inherit;
    text-decoration: none;
    transition: border-color .15s, box-shadow .15s;
}

.learn-card:hover { border-color: var(--accent); box-shadow: 0 2px 10px rgba(0, 0, 0, .06); }
.learn-card-icon { color: var(--accent); flex: 0 0 auto; font-size: 2rem !important; }
.learn-card-body { display: flex; flex-direction: column; gap: var(--s1); }
.learn-card-title { font-weight: 700; font-size: var(--fs-section); }
.learn-card-who { color: var(--ink-muted); font-size: var(--fs-small); }
.learn-card-summary { font-size: var(--fs-body); }

.learn-layout {
    display: grid;
    grid-template-columns: 240px minmax(0, 1fr);
    gap: var(--s6);
    align-items: start;
}

/*  Уже 1600 оглавление уходит над текстом. Сбоку оно отнимает у статьи
    240 точек, и на обычном экране 1440 снимок кабинета ужимается почти
    вдвое — мелкие надписи на нём не прочесть. */
@media (max-width: 1600px) {
    .learn-layout { grid-template-columns: minmax(0, 1fr); }
    .learn-toc { position: static; }
    .learn-toc ol { columns: 3 220px; column-gap: var(--s6); }
    .learn-toc li { break-inside: avoid; }
}

.learn-toc {
    position: sticky;
    top: 12px;
    padding: var(--s3) var(--s4);
    border: 1px solid var(--border);
    border-radius: 10px;
    background: var(--surface);
    font-size: var(--fs-table);
}

.learn-toc-title { font-weight: 700; margin-bottom: var(--s2); }
.learn-toc ol { margin: 0; padding-left: 1.2em; }
.learn-toc li { margin: var(--s1) 0; }
.learn-toc a { color: inherit; text-decoration: none; }
.learn-toc a:hover { color: var(--accent); text-decoration: underline; }

/*  Ширину текста держат абзацы (72ch), а снимки — во всю колонку: кабинет
    на них должен читаться без увеличения. */
.learn {
    font-size: var(--fs-body);
    line-height: 1.6;
}

.learn h2 {
    margin: var(--s6) 0 var(--s3);
    padding-top: var(--s3);
    border-top: 1px solid var(--border);
    font-size: var(--fs-section);
    scroll-margin-top: var(--s4);
}

.learn h2:first-child { margin-top: 0; border-top: 0; padding-top: 0; }
.learn h3 { margin: var(--s5) 0 var(--s2); font-size: var(--fs-body); }
.learn p, .learn li { max-width: 72ch; }
.learn ol, .learn ul { padding-left: 1.4em; }
.learn li { margin: var(--s2) 0; }

/* Надпись на экране — `Завести договор` — выглядит кнопкой: по ней ищут глазами. */
.learn code {
    padding: 1px var(--s2);
    border: 1px solid var(--border);
    border-radius: 5px;
    background: var(--surface-sunken);
    font-family: var(--sans);
    font-size: .92em;
    font-weight: 600;
    white-space: nowrap;
}

.learn table { border-collapse: collapse; margin: var(--s3) 0; }
.learn th, .learn td { border: 1px solid var(--border); padding: var(--s1) var(--s2); text-align: left; vertical-align: top; }
.learn th { background: var(--surface-sunken); }

.learn-shot { margin: var(--s4) 0 var(--s5); }
.learn-shot img {
    display: block;
    max-width: 100%;
    border: 1px solid var(--border);
    border-radius: 8px;
    box-shadow: 0 2px 12px rgba(0, 0, 0, .08);
    cursor: zoom-in;
}
/* Снимок ссылается сам на себя: в колонке статьи он уменьшен, по нажатию
   открывается в полный размер. */
.learn-shot figcaption { margin-top: var(--s2); color: var(--ink-muted); font-size: var(--fs-table); }
.learn-shot figcaption::after { content: " · нажмите, чтобы увеличить"; opacity: .7; }

/* Подсказки: > [!TIP] и т. п. — заголовки переведены в LearningMarkdown. */
.learn .markdown-alert {
    max-width: 80ch;
    margin: var(--s4) 0;
    padding: var(--s2) var(--s3);
    border-left: 4px solid var(--accent);
    border-radius: 6px;
    background: var(--accent-soft);
}
.learn .markdown-alert p { margin: var(--s1) 0; }
.learn .markdown-alert-title { font-weight: 700; }
.learn .markdown-alert-tip       { border-color: var(--ok);     background: color-mix(in srgb, var(--ok) 10%, transparent); }
.learn .markdown-alert-important { border-color: var(--accent); }
.learn .markdown-alert-warning,
.learn .markdown-alert-caution   { border-color: var(--danger); background: color-mix(in srgb, var(--danger) 9%, transparent); }

/* ---------- перевод оплаты на другой договор ---------- */

/* Договоры-получатели — крупными кнопками-карточками: выбирают один, и
   выбранный должен читаться издалека. */
.transfer-targets {
    display: flex;
    flex-direction: column;
    gap: var(--s2);
}

.transfer-target {
    display: flex;
    flex-direction: column;
    gap: 2px;
    width: 100%;
    padding: var(--s2) var(--s3);
    text-align: left;
    font: inherit;
    color: var(--ink);
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--radius-sm);
    cursor: pointer;
}

.transfer-target:hover { border-color: var(--border-strong); background: var(--surface-sunken); }

.transfer-target.picked {
    border-color: var(--accent);
    background: var(--accent-soft);
    box-shadow: 0 0 0 1px var(--accent);
}

.transfer-target-head { display: flex; align-items: center; gap: var(--s2); flex-wrap: wrap; font-weight: 600; }
.transfer-target-who { color: var(--ink-soft); font-size: .92em; }
.transfer-target-money { font-size: .92em; }

.transfer-preview {
    width: 100%;
    border-collapse: collapse;
    margin: var(--s1) 0 var(--s2);
}

.transfer-preview th,
.transfer-preview td {
    padding: var(--s2) var(--s2);
    border-bottom: 1px solid var(--border);
    text-align: left;
    vertical-align: top;
}

.transfer-preview th { font-weight: 600; color: var(--ink-soft); }
.transfer-preview td:first-child { color: var(--ink-soft); white-space: nowrap; }

/* Латинская буква в названии класса-двойника. «7A» и «7А» на экране
   неотличимы: подсвечиваем чужую букву и подписываем «lat», чтобы было
   видно, какой из двух классов записан не теми буквами. */
.ch-lat {
    background: var(--warn-soft);
    box-shadow: inset 0 -2px 0 var(--warn);
    border-radius: 3px;
    padding: 0 1px;
}

.ch-lat::after {
    content: "лат";
    margin-left: 1px;
    font-size: 0.55em;
    font-weight: 600;
    letter-spacing: 0.02em;
    vertical-align: super;
    color: var(--warn);
}

/* Исправить платежи: что можно, что закрыто и почему. Раньше закрытая
   строка несла только значок 🔒 с причиной во всплывающей подсказке —
   бухгалтерия видела замки и считала, что правка не работает. */
.ledger-rules .rules {
    list-style: none;
    margin: var(--s2) 0 0;
    padding: 0;
    display: grid;
    gap: var(--s2);
}

.ledger-rules .rules li {
    display: flex;
    gap: var(--s3);
    align-items: flex-start;
    font-size: var(--fs-table);
    line-height: 1.45;
}

.ledger-rules .rules .mud-icon-root { flex: none; margin-top: 2px; }
.ledger-rules .rule-ok .mud-icon-root { color: var(--ok); }
.ledger-rules .rule-lock .mud-icon-root { color: var(--ink-muted); }

/* Свой платёж, который можно исправить, — зелёная полоса слева. */
.grid-mud tr.row-own > td:first-child { box-shadow: inset 3px 0 0 var(--ok); }

.lock-pill {
    display: inline-flex;
    align-items: center;
    gap: var(--s1);
    padding: 2px var(--s2);
    border: 1px solid var(--border);
    border-radius: 999px;
    background: var(--surface-sunken);
    color: var(--ink-muted);
    font: inherit;
    font-size: var(--fs-small);
    white-space: nowrap;
    cursor: pointer;
}

.lock-pill:hover { border-color: var(--border-strong); color: var(--ink-soft); }
.lock-pill .mud-icon-root { font-size: 15px; }
.lock-pill .lock-why { color: var(--accent); text-decoration: underline dotted; }

.grid-mud td.lock-explain {
    background: var(--surface-sunken);
    color: var(--ink-soft);
    font-size: var(--fs-small);
    line-height: 1.45;
    padding: var(--s2) var(--s4);
    white-space: normal;
}

.lock-explain .lock-go {
    display: flex;
    flex-wrap: wrap;
    gap: var(--s2);
    margin-top: var(--s2);
}

/* Новый договор: сумма, которую надо ввести руками (у класса нет цены в
   прайсе, у питания прайса нет вовсе). Обычным полем среди прочих его не
   замечали — пока сумма не введена, блок жёлтый и с заголовком. */
.manual-price {
    display: flex;
    flex-direction: column;
    gap: var(--s2);
    margin: var(--s4) 0 var(--s1);
    padding: var(--s3) var(--s3);
    border: 1px solid var(--border);
    border-left-width: 4px;
    border-radius: 10px;
}

.manual-price.need {
    background: var(--warn-soft);
    border-color: rgba(var(--warn-rgb), 0.45);
    border-left-color: var(--warn);
}

.manual-price.done {
    border-left-color: var(--ok);
}

.manual-price-head {
    display: flex;
    align-items: center;
    gap: var(--s2);
    font-weight: 600;
    font-size: var(--fs-body);
}

.manual-price.need .manual-price-head { color: var(--warn); }
.manual-price.done .manual-price-head { color: var(--ok); }

.manual-price .hint { margin-top: 0; }

/* Само поле — на белом и с толстой рамкой, чтобы в жёлтом блоке было
   видно, куда вводить. */
.manual-price.need .manual-price-input .mud-input.mud-input-outlined {
    background: var(--surface);
}

.manual-price.need .manual-price-input .mud-input-outlined-border {
    border-width: 2px;
    border-color: var(--warn);
}

/* «введите вручную ↓» вместо прочерка в строке сумм. */
.df-need {
    padding: 0;
    border: 0;
    background: none;
    font: inherit;
    font-weight: 600;
    color: var(--warn);
    text-decoration: underline dotted;
    cursor: pointer;
}


/* ---------- договор без графика начислений ----------
   Долг без графика считается от суммы договора и почти наверняка
   неверен (Arrears.Base) — бухгалтера просят построить график. Жёлтый
   блок того же вида, что ручная сумма в новом договоре. */
.no-schedule {
    display: flex;
    flex-direction: column;
    gap: var(--s2);
    margin: var(--s1) 0;
    padding: var(--s3) var(--s3);
    background: var(--warn-soft);
    border: 1px solid rgba(var(--warn-rgb), 0.45);
    border-left: 4px solid var(--warn);
    border-radius: 10px;
    font-size: var(--fs-body);
    line-height: 1.45;
}

.no-schedule p { margin: 0; }
.no-schedule strong:first-child { color: var(--warn); }
.no-schedule .actions { margin: 0; }

/* Пометка в строке: «нет графика», «график > суммы». */
.sched-mark {
    display: inline-block;
    margin-left: var(--s2);
    padding: 0 var(--s2);
    border: 1px solid rgba(var(--warn-rgb), 0.5);
    border-radius: 999px;
    background: var(--warn-soft);
    color: var(--warn);
    font-size: var(--fs-label);
    font-weight: 600;
    line-height: 1.5;
    white-space: nowrap;
    text-decoration: none;
    vertical-align: middle;
}

a.sched-mark:hover { border-color: var(--warn); }

/* Два флажка отбора в одной ячейке сетки — друг под другом. */
.filter-checks {
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    gap: 0;
}

/* ---------- Сеансы и входы (Admin/Sessions.razor) ---------- */
.filter-grid.filter-grid-sessions {
    grid-template-columns: minmax(140px, 180px) auto;
    align-items: center;
    margin-bottom: var(--s2);
}

.grid-mud tr.row-picked > td { background: var(--accent-soft); }
.grid-table tr.row-warn > td { background: var(--warn-soft); }

.session-details { padding: var(--s1) var(--s1) var(--s2); }
.session-details h3 { font-size: var(--fs-body); margin: var(--s3) 0 var(--s2); }

.session-conflicts {
    margin: 0 0 var(--s2);
    padding-left: var(--s4);
    font-size: var(--fs-table);
    line-height: 1.5;
}

.session-history { font-size: var(--fs-small); }

/* =========================================================================
   ТЕЛЕФОН (до 600 px)

   Кабинет рассчитан на рабочий стол, но открывают его и с телефона:
   найти ученика, проверить долг, посмотреть платежи. Правила ниже
   ничего не меняют на широком экране.
   ========================================================================= */
@media (max-width: 600px) {
    .page { padding: var(--s3) var(--s3) var(--s7); }

    h1 { font-size: calc(var(--fs-title) * 0.8); }
    /* Заголовок со знаком — обычной строкой: во флекс-строке «Платёж по
       договору 2026Д-4831» распадался на куски, и номер рвался. */
    h1:has(.page-icon) { display: block; }
    h1 .page-icon.mud-icon-root { font-size: 20px; padding: var(--s1); margin-right: var(--s2); }
    .page-head { margin-bottom: var(--s4); }
    .page-head p { font-size: var(--fs-table); }

    .panel { padding: var(--s3); }
    .panel-head { flex-wrap: wrap; gap: var(--s1) var(--s3); }

    /* Приглушённый текст на телефоне переносится: на широком экране он
       держится в строку, а здесь уезжал за край. */
    .muted-cell { white-space: normal; }

    /* Итоги — плитками по две в ряд, а не лестницей на весь экран. */
    .tally {
        display: grid;
        grid-template-columns: repeat(2, minmax(0, 1fr));
        gap: var(--s4) var(--s3);
        padding: var(--s3) var(--s3);
    }

    .tally-item { min-width: 0; }
    .tally-num { font-size: calc(var(--fs-title) * 0.75); overflow-wrap: anywhere; }
    .tally-sep { display: none; }

    /* Поля отбора и форм — в столбик, каждое на всю ширину. */
    .filter-grid,
    .filter-grid[class*="filter-grid-"] { grid-template-columns: minmax(0, 1fr) !important; }
    .filter-grid > * { min-width: 0; }

    /* Поиск и кнопка рядом (Приёмная, Сотрудники) — одно под другим.
       Размеры «flex: 1 1 260px» рассчитаны на строку; в столбце они
       растягивали поле на 260 px в высоту — под ним зияла дыра. */
    .search-row { flex-direction: column; }
    .search-row > * { flex: 0 0 auto !important; max-width: none !important; width: 100%; }

    /* Пояснение у кнопок («весь отбор…», «Укажите сумму») — своей
       строкой под ними, а не обрывком между кнопками. */
    .actions > .sub { display: block; margin: var(--s2) 0 0; }

    /* Строка под поиском (флажок, счётчики, «В Excel») — с переносом:
       в одну строку кнопка уезжала за край. */
    .search-meta { flex-wrap: wrap; row-gap: var(--s2); }
    .search-meta .spacer { display: none; }

    /* Пары «подпись — значение»: подпись над значением. */
    .facts,
    .facts.facts-wide { grid-template-columns: minmax(0, 1fr); gap: 2px; }
    .facts dt { margin-top: var(--s2); }
    .facts dt:first-child { margin-top: 0; }

    /* Длинное без пробелов (номера, адреса, код) — переносится, а не
       раздвигает страницу. */
    code, .mono, .learn code { overflow-wrap: anywhere; white-space: normal; }
    .pill { white-space: normal; }

    /* Шаги приёма документов: подписи шагов не влезают — остаются
       номера, подпись только у текущего. */
    .stepper .step:not(.current) .step-text { display: none; }
    .stepper .step-line { margin: 0 var(--s2); min-width: 12px; }

    /* Кнопки в ряду — крупнее, по ним попадают пальцем. */
    .mud-button-root.mud-button { min-height: 40px; }
}

/* Только на телефоне: слово вместо подсказки по наведению. */
.phone-only { display: none; }

@media (max-width: 600px) {
    .phone-only { display: inline; margin-left: var(--s2); }
}

/* Подсказки про клавиши (Ctrl+Enter, Esc) там, где клавиатуры нет. */
@media (hover: none) and (pointer: coarse) {
    .keys { display: none; }
}

/* =========================================================================
   ТАБЛИЦЫ НА ТЕЛЕФОНЕ — КАРТОЧКАМИ

   Таблица в пять и больше столбцов на экране телефона видна на треть и
   прокручивается вбок, шапка уезжает — какой столбец где, уже не
   понять. Здесь каждая строка становится карточкой: «подпись: значение»
   сверху вниз. Подписи проставляет app.js из шапки таблицы (data-label).

   Не трогаем: узкие таблицы до трёх столбцов (few-cols — им и так
   хватает места) и таблицы, помеченные no-cards (матрицы вроде «виды ×
   роли», где смысл — в пересечении строк и столбцов).
   ========================================================================= */
@media (max-width: 600px) {
    .grid-mud:not(.no-cards) .mud-table-container:has(.mud-table-root:not(.few-cols)),
    .table-scroll:has(table.grid-table:not(.few-cols):not(.no-cards)),
    .table-wrap:has(table.grid-table:not(.few-cols):not(.no-cards)) {
        max-height: none;
        overflow: visible;
    }

    .grid-mud:not(.no-cards) .mud-table-root:not(.few-cols),
    table.grid-table:not(.few-cols):not(.no-cards) {
        display: block;
        width: 100%;
    }

    .grid-mud:not(.no-cards) .mud-table-root:not(.few-cols) > thead,
    table.grid-table:not(.few-cols):not(.no-cards) > thead { display: none; }

    .grid-mud:not(.no-cards) .mud-table-root:not(.few-cols) > tbody,
    table.grid-table:not(.few-cols):not(.no-cards) > tbody { display: block; }

    .grid-mud:not(.no-cards) .mud-table-root:not(.few-cols) > tbody > tr,
    table.grid-table:not(.few-cols):not(.no-cards) > tbody > tr {
        display: block;
        padding: var(--s2) var(--s3);
        border-bottom: 1px solid var(--border);
    }

    .grid-mud:not(.no-cards) .mud-table-root:not(.few-cols) > tbody > tr:last-child,
    table.grid-table:not(.few-cols):not(.no-cards) > tbody > tr:last-child { border-bottom: none; }

    /*  Подпись — плавающая слева, значение обтекает её справа. Не флекс:
        во флекс-строке каждый кусок значения («2 424 400» и «по договору
        2 552 000») становился отдельной колонкой, и число рвалось. */
    .grid-mud:not(.no-cards) .mud-table-root:not(.few-cols) > tbody > tr > td.mud-table-cell,
    table.grid-table:not(.few-cols):not(.no-cards) > tbody > tr > td {
        display: flow-root;
        min-height: 0;
        padding: 3px 0;
        border: none;
        text-align: right;
        white-space: normal;
        overflow-wrap: break-word;
        max-width: none;
    }

    .grid-mud:not(.no-cards) .mud-table-root:not(.few-cols) > tbody > tr > td.mud-table-cell::before,
    table.grid-table:not(.few-cols):not(.no-cards) > tbody > tr > td::before {
        content: attr(data-label);
        float: left;
        width: 38%;
        margin-right: var(--s3);
        padding-top: 2px;
        text-align: left;
        font-size: var(--fs-label);
        font-weight: 600;
        letter-spacing: 0.02em;
        color: var(--ink-muted);
    }

    /* Кнопки строки («Править», «Сбросить пароль») — рядом на всю
       ширину карточки, без подписи и без переноса посреди слова. */
    .grid-mud:not(.no-cards) .mud-table-root:not(.few-cols) > tbody > tr > td.mud-table-cell.actions,
    table.grid-table:not(.few-cols):not(.no-cards) > tbody > tr > td.actions {
        display: flex;
        flex-wrap: wrap;
        gap: var(--s1) var(--s4);
        padding-top: var(--s1);
        text-align: left;
    }

    .grid-mud:not(.no-cards) .mud-table-root:not(.few-cols) > tbody > tr > td.mud-table-cell.actions::before,
    table.grid-table:not(.few-cols):not(.no-cards) > tbody > tr > td.actions::before { display: none; }

    .grid-mud:not(.no-cards) .mud-table-root:not(.few-cols) .mud-table-cell.actions .link-btn + .link-btn,
    table.grid-table:not(.few-cols):not(.no-cards) td.actions .link-btn + .link-btn { margin-left: 0; }

    .grid-mud:not(.no-cards) .mud-table-root:not(.few-cols) .link-btn { white-space: nowrap; }

    /* Без подписи (столбец кнопок, галочка) — значение на всю ширину. */
    .grid-mud:not(.no-cards) .mud-table-root:not(.few-cols) > tbody > tr > td.mud-table-cell:is([data-label=""], :not([data-label]))::before,
    table.grid-table:not(.few-cols):not(.no-cards) > tbody > tr > td:is([data-label=""], :not([data-label]))::before { display: none; }

    .grid-mud:not(.no-cards) .mud-table-root:not(.few-cols) > tbody > tr > td.mud-table-cell:is([data-label=""], :not([data-label])),
    table.grid-table:not(.few-cols):not(.no-cards) > tbody > tr > td:is([data-label=""], :not([data-label])) {
        text-align: left;
    }

    /* Первая ячейка — заголовок карточки: номер договора, дата, имя. */
    .grid-mud:not(.no-cards) .mud-table-root:not(.few-cols) > tbody > tr > td.mud-table-cell:first-child,
    table.grid-table:not(.few-cols):not(.no-cards) > tbody > tr > td:first-child { font-weight: 600; }

    /* Пустые ячейки карточке не нужны. */
    .grid-mud:not(.no-cards) .mud-table-root:not(.few-cols) > tbody > tr > td.mud-table-cell:empty,
    table.grid-table:not(.few-cols):not(.no-cards) > tbody > tr > td:empty { display: none; }

    /* Обрезка длинного текста многоточием рассчитана на столбец — в
       карточке текст просто переносится. */
    .grid-mud:not(.no-cards) .mud-table-root:not(.few-cols) .mud-table-cell.cell-clip {
        max-width: none;
        overflow: visible;
        text-overflow: clip;
    }

    /* Отметка строки полосой слева — у карточки та же. */
    .grid-mud:not(.no-cards) .mud-table-root:not(.few-cols) > tbody > tr.row-picked { box-shadow: inset 3px 0 0 var(--accent); }
}

/* ---------- приём и продажи: телефоны родителей ---------- */
/*  Номер — ссылка «позвонить», значок рядом — чат WhatsApp. Номера одного
    родителя идут строкой и переносятся целиком, не по цифрам.          */
.phone-links { display: flex; flex-wrap: wrap; gap: 2px var(--s3); }
.phone-link { display: inline-flex; align-items: center; gap: var(--s1); white-space: nowrap; }
.phone-link a { text-decoration: none; }
.phone-link a:hover { text-decoration: underline; }
.phone-wa { color: var(--ok); display: inline-flex; }
.phone-wa:hover { color: var(--ok); opacity: .8; }

/* Журнал разговоров с семьёй: кто, когда, чем кончилось. */
.touch-list { display: flex; flex-direction: column; gap: var(--s3); }
.touch { border-left: 3px solid var(--border); padding: 2px 0 2px var(--s2); }
.touch-head { display: flex; flex-wrap: wrap; gap: var(--s1) var(--s2); align-items: center; font-size: var(--fs-small); color: var(--ink-muted); }
.touch-text { margin-top: 3px; white-space: pre-wrap; }

/* Пояснение под значением ячейки — с переносом и не шире колонки (Перезаключение). */
.grid-mud .mud-table-cell .cell-note {
    white-space: normal;
    max-width: 30ch;
    font-size: var(--fs-small);
    color: var(--ink-muted);
}

/* ---------- ряд кнопок ---------- */
/*  Общего правила для div.actions не было вовсе: кнопки MudBlazor
    вставали вплотную, рамка к рамке («Расторгнуть|Завершить»). Ячейки
    таблиц td.actions живут по своим правилам — их не трогаем. */
div.actions { display: flex; flex-wrap: wrap; align-items: center; gap: var(--s2); }

/* ---------- шапка страницы с действиями ---------- */
/*  Номер и состояние слева, действия справа: одна залитая кнопка —
    главное действие, остальные контурные, редкое — в «Ещё». На узком
    экране действия уходят под заголовок. */
.head-with-actions {
    display: flex;
    flex-wrap: wrap;
    align-items: flex-start;
    justify-content: space-between;
    gap: var(--s3) var(--s4);
}
.head-with-actions .head-text { min-width: 0; }
.head-actions { display: flex; flex-wrap: wrap; align-items: center; gap: var(--s2); }
.page-head .sub .pill { margin-right: var(--s2); }

/* Заголовок группы в меню («Сменить состояние») и опасные пункты. */
.menu-group {
    padding: var(--s2) var(--s4) var(--s1);
    font-size: var(--fs-label);
    font-weight: 600;
    color: var(--ink-muted);
}
.mud-menu-item.menu-danger,
.mud-menu-item.menu-danger .mud-icon-root { color: var(--danger); font-weight: 600; }

/* ---------- вкладки (Layout/TabStrip) ----------
   Серая черта под вкладками — тенью внутрь, а не рамкой: раньше кнопка
   заходила на рамку (margin-bottom: -1px), полоса становилась на пиксель
   выше своего окна, и overflow-x: auto (он включает и вертикальную)
   рисовал справа полосу прокрутки со стрелками. Тень лежит под кнопками —
   подчёркивание активной её перекрывает. По ширине прокрутка остаётся. */
.tab-strip {
    display: flex;
    gap: var(--s1);
    margin-bottom: var(--s4);
    box-shadow: inset 0 -1px 0 var(--border-strong);
    overflow-x: auto;
    overflow-y: hidden;
    scrollbar-width: thin;
}
.tab-btn {
    appearance: none;
    display: inline-flex;
    align-items: center;
    gap: var(--s2);
    min-height: 44px;
    padding: 0 var(--s3);
    border: 0;
    border-bottom: 3px solid transparent;
    border-radius: var(--radius-sm) var(--radius-sm) 0 0;
    background: transparent;
    color: var(--ink-muted);
    font: inherit;
    font-weight: 600;
    white-space: nowrap;
    cursor: pointer;
}
.tab-btn:hover { color: var(--ink); background: var(--surface-sunken); }
.tab-btn.on { color: var(--accent-ink); border-bottom-color: var(--accent); }
.tab-btn:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }
.tab-count {
    min-width: 22px;
    padding: 0 var(--s2);
    border-radius: var(--radius-pill);
    background: var(--surface-sunken);
    color: var(--ink-muted);
    font-size: var(--fs-label);
    text-align: center;
}
.tab-btn.on .tab-count { background: var(--accent-soft); color: var(--accent-ink); }
/* Вкладка, в которой что-то не так (нет графика): точка, а не цвет текста. */
.tab-btn.tab-warn::after {
    content: "";
    width: 8px;
    height: 8px;
    border-radius: 50%;
    background: var(--warn);
}
.tab-panel:focus { outline: none; }
.tab-panel:focus-visible { outline: 2px solid var(--accent); outline-offset: var(--s1); border-radius: var(--radius); }

/* ---------- карточка договора: деньги одной строкой ---------- */
.money-row {
    display: grid;
    grid-template-columns: repeat(4, minmax(0, 1fr));
    gap: var(--s3) var(--s4);
    margin-bottom: var(--s3);
    padding: var(--s4) var(--s5);
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    box-shadow: var(--shadow);
}
.money-cell { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.money-label { font-size: var(--fs-small); color: var(--ink-muted); }
.money-num {
    font-size: var(--fs-title);
    font-weight: 600;
    line-height: 1.2;
    white-space: nowrap;
    font-variant-numeric: tabular-nums;
}
.money-ok { color: var(--ok); }
.money-warn { color: var(--warn); }
.money-bar {
    grid-column: 1 / -1;
    height: 8px;
    border-radius: var(--radius-pill);
    background: var(--accent-soft);
    overflow: hidden;
}
.money-bar > span { display: block; height: 100%; background: var(--ok); }
@media (max-width: 860px) {
    .money-row { grid-template-columns: repeat(2, minmax(0, 1fr)); padding: var(--s3) var(--s4); }
    .money-num { font-size: var(--fs-section); }
}

/* Подпись — значение, по паре на строку: карточки «Договор» и «Деньги»
   на обзоре. Обычная сетка .facts ставит пары в две колонки, и длинное
   «Оплата помесячно» ломалось в столбик по слову. */
.facts.facts-list { grid-template-columns: minmax(0, 1fr); gap: var(--s2); }
.facts.facts-list .fact {
    display: grid;
    grid-template-columns: minmax(96px, 128px) minmax(0, 1fr);
    gap: 2px var(--s3);
    align-items: baseline;
}

/* Заголовок полосы подтверждения: что сейчас будет с договором. */
.confirm-title { margin: 0; font-size: var(--fs-body); }

/* Две карточки одной высоты; суммы не рвутся переносом между цифрами и «₸». */
.card-columns-even { align-items: stretch; }
.panel .digits, .facts dd.digits { white-space: nowrap; }

/* «Служебное» — свёрнутый блок для разбора обращений. */
.service-block > summary {
    display: list-item;
    min-height: 44px;
    padding: var(--s2) 0;
    color: var(--ink-soft);
    font-weight: 600;
    cursor: pointer;
}
.service-block > summary:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; border-radius: var(--radius-sm); }

/* «Счета и акт»: кнопка и одна строка о том, что за ней. */
.doc-links { display: flex; flex-direction: column; gap: var(--s3); }
.doc-link { display: flex; flex-wrap: wrap; align-items: center; gap: var(--s2) var(--s3); }
.doc-link .sub { color: var(--ink-muted); font-size: var(--fs-small); }

/* ---------- комплектование: строка с карточкой, стрелки, откат ---------- */
/*  Строка класса — кнопка (отметить) и рядом ссылка на карточку. Раньше
    ссылка лежала внутри кнопки — недопустимая разметка. */
.tr-line { position: relative; }
.tr-line > .tr-row { padding-right: calc(var(--s6) + var(--s2)); }
.tr-line > .tr-open {
    position: absolute;
    top: var(--s2);
    right: var(--s2);
    min-width: 32px;
    min-height: 32px;
    display: grid;
    place-items: center;
    border-radius: var(--radius-sm);
}
.tr-line:hover > .tr-open,
.tr-line > .tr-open:focus-visible { opacity: 1; }
.tr-line > .tr-open:focus-visible { outline: 2px solid var(--accent); outline-offset: 0; }

/* Стрелки переноса — с подписью: «→» без слов читали как «вперёд». */
.move-btn {
    width: 76px;
    height: auto;
    min-height: 52px;
    padding: var(--s1) 0;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 2px;
}
.move-label { font-size: var(--fs-label); font-weight: 600; line-height: 1.2; }

/* Последние действия: одна полоса и раскрываемый список. */
.undo-more {
    list-style: none;
    margin: 0;
    padding: var(--s2) var(--s4);
    background: var(--surface);
    border: 1px solid var(--border);
    border-top: 0;
    border-radius: 0 0 var(--radius) var(--radius);
}
.undo-more-row {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--s3);
    padding: var(--s2) 0;
}
.undo-more-row + .undo-more-row { border-top: 1px solid var(--border); }
.undo-list:has(.undo-more) > .undo-bar { border-radius: var(--radius) var(--radius) 0 0; }

/* Текст только для чтения с экрана. */
.sr-only {
    position: absolute;
    width: 1px;
    height: 1px;
    margin: -1px;
    padding: 0;
    overflow: hidden;
    clip: rect(0 0 0 0);
    white-space: nowrap;
    border: 0;
}

/* ---------- подсказка: строка и «Подробнее» (Layout/Hint) ---------- */
.hint-more > summary {
    display: block;
    list-style: none;
    cursor: pointer;
    border-radius: var(--radius-sm);
}
.hint-more > summary::-webkit-details-marker { display: none; }
.hint-more > summary:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.hint-toggle { color: var(--accent); font-weight: 600; white-space: nowrap; }
.hint-toggle::after { content: " ▾"; }
.hint-more[open] .hint-toggle::after { content: " ▴"; }
.hint-detail {
    margin-top: var(--s1);
    padding-left: var(--s3);
    border-left: 2px solid var(--border);
}
.hint-learn { margin-left: var(--s1); white-space: nowrap; }
.hint-detail .hint-learn { display: inline-block; margin: var(--s1) 0 0; }

/* ---------- меню «Вид» (NavMenu) ---------- */
/*  Одно меню с подписями вместо пяти значков: тема, плотность строк,
    двор. details/summary — раскрывается и без скрипта. */
.view-menu { margin: 0 0 var(--s3); }
.view-toggle {
    display: flex;
    align-items: center;
    gap: var(--s2);
    min-height: 40px;
    padding: var(--s1) var(--s3);
    border: 1px solid var(--border);
    border-radius: var(--radius-sm);
    background: var(--surface-sunken);
    color: var(--ink-soft);
    font-weight: 600;
    list-style: none;
    cursor: pointer;
}
.view-toggle::-webkit-details-marker { display: none; }
.view-toggle::after { content: "▾"; margin-left: auto; color: var(--ink-muted); }
.view-menu[open] > .view-toggle::after { content: "▴"; }
.view-toggle:hover { color: var(--ink); background: var(--surface); }
.view-toggle:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.view-panel {
    display: flex;
    flex-direction: column;
    gap: var(--s3);
    margin-top: var(--s2);
    padding: var(--s3);
    border: 1px solid var(--border);
    border-radius: var(--radius-sm);
    background: var(--surface);
}
.view-group { margin: 0; }
.view-title { margin-bottom: var(--s1); font-size: var(--fs-label); font-weight: 600; color: var(--ink-muted); }
.view-options {
    display: flex;
    gap: 2px;
    padding: 3px;
    background: var(--surface-sunken);
    border: 1px solid var(--border);
    border-radius: var(--radius-sm);
}
.view-option {
    flex: 1 1 0;
    display: inline-flex;
    flex-direction: column;
    align-items: center;
    gap: 2px;
    min-height: 44px;
    padding: var(--s1) 2px;
    border: 0;
    border-radius: 6px;
    background: transparent;
    color: var(--ink-muted);
    font: inherit;
    font-size: var(--fs-label);
    line-height: 1.2;
    cursor: pointer;
}
.view-option:hover { color: var(--ink); background: var(--surface); }
.view-option.on { background: var(--surface); color: var(--accent); font-weight: 600; box-shadow: var(--shadow); }
.view-option:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }

/* Свёрнутое меню: значок «Вид», панель — сбоку от меню, поверх страницы. */
@media (min-width: 861px) {
    .sidebar-mini .view-word { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; }
    .sidebar-mini .view-toggle { justify-content: center; padding: var(--s1) 0; }
    .sidebar-mini .view-toggle::after { display: none; }
    .sidebar-mini .view-panel {
        position: fixed;
        left: calc(var(--sidebar-w) + var(--s2));
        bottom: var(--s4);
        width: 260px;
        z-index: 30;
        box-shadow: var(--shadow-lift);
    }
}

/* ---------- меньше движения ---------- */
/*  Человек попросил систему не двигать картинки (prefers-reduced-motion):
    переходы, вращение «Считаем…», выезд меню на телефоне и окно потери
    связи (ReconnectModal) — без анимации. Двор на холсте следит за этим
    сам (scene.js). */
@media (prefers-reduced-motion: reduce) {
    *,
    *::before,
    *::after {
        animation-duration: 0.01ms !important;
        animation-iteration-count: 1 !important;
        animation-delay: 0s !important;
        transition-duration: 0.01ms !important;
        transition-delay: 0s !important;
        scroll-behavior: auto !important;
    }
}

/* ---------- «К содержимому» (MainLayout) ---------- */
/*  Первая в обходе Tab; видна только в фокусе — поверх меню, слева
    сверху. <main id="content"> в фокусе рамки не рисует: фокус туда
    переводит ссылка, а не человек. */
.skip-link {
    position: absolute;
    top: var(--s2);
    left: var(--s2);
    z-index: 100;
    padding: var(--s2) var(--s4);
    border-radius: var(--radius-sm);
    background: var(--accent);
    color: var(--on-accent);
    font-weight: 600;
    transform: translateY(-200%);
}
.skip-link:focus,
.skip-link:focus-visible { transform: none; outline: 2px solid var(--ink); outline-offset: 2px; text-decoration: none; }
main.main:focus { outline: none; }

/* ---------- таблицы: строка, которая нажимается ---------- */
/*  Подсветка при наведении — только у строк, которые что-то делают по
    нажатию (сеансы, игроки). Для клавиатуры у такой строки — кнопка в
    первой ячейке: строку целиком Tab не достать. */
.row-open {
    padding: 0;
    border: 0;
    background: none;
    color: var(--accent);
    font: inherit;
    font-weight: 600;
    text-align: left;
    cursor: pointer;
}
.row-open:hover { text-decoration: underline; }
.row-open:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; border-radius: 4px; }

/* Вторая строка ячейки: школа под учеником (Задолженность) — без обрезки. */
.cell-sub { font-size: var(--fs-small); color: var(--ink-muted); white-space: normal; }

/*  Ученик со школой — не уже 14rem. Таблица шире окна, и браузер сжимал
    переносимый столбец до самого длинного слова: ФИО и школа ломались в
    пять строк, а пустые «Скидка» и «%» стояли широкими. Ширину берём у
    скидки (как в реестре договоров, 18ch) и у метки «нет графика» — она
    уходит под сумму, как давность под просрочкой. На телефоне строки —
    карточками, там ограничение не нужно. */
@media (min-width: 601px) {
    .grid-mud.debt-table .mud-table-cell.cell-student { min-width: 14rem; }
    .grid-mud.debt-table .mud-table-cell.cell-clip { max-width: 18ch; }
    .grid-mud.debt-table .sched-mark { display: table; margin: 2px 0 0 auto; }
}

/* Состояние договора словом рядом со значком. */
.state-word { margin-left: var(--s1); white-space: nowrap; }

/* ---------- пусто — внутри панели и таблицы ---------- */
/*  Тот же EmptyState, но поменьше: в ячейке таблицы и в карточке
    крупный знак и сорок восемь пикселей полей раздували блок. */
.empty.empty-compact { padding: var(--s4) var(--s3); }
.empty.empty-compact .empty-glyph { font-size: var(--fs-title); margin-bottom: var(--s1); }

/* ---------- подпись на рамке у блоков, которые не поля ввода ---------- */
/*  «Дата рождения» и «Пол» из ИИН на приёме документов — не поля, а
    выведенные значения, но подпись у них — на рамке, как у полей MudBlazor
    рядом (F-4): три стиля подписей на одной форме читались как три формы. */
.field.framed { position: relative; padding-top: var(--s1); }
.field.framed > label {
    position: absolute;
    top: -2px;
    left: var(--s2);
    z-index: 1;
    padding: 0 var(--s1);
    background: var(--surface);
    color: var(--ink-muted);
    font-size: var(--fs-label);
    font-weight: 400;
    line-height: 1;
}
.field.framed > .derived,
.field.framed > .chips {
    min-height: 40px;
    padding: var(--s2) var(--s3);
    border: 1px solid var(--border-field);
    border-radius: 4px;
}

/* Список найденных под полем «Ученик» (новый договор). */
.pick-line { display: inline-flex; flex-wrap: wrap; gap: var(--s1) var(--s2); align-items: baseline; }
.pick-empty { padding: var(--s2) var(--s4); color: var(--ink-muted); font-size: var(--fs-small); }

/* ---------- успех — зелёным (A-4) ---------- */
/*  Сообщения о сделанном теперь Success; у полос .page-alert для него
    не было своего цвета — рамка и фон, как у остальных уровней. */
.page-alert.mud-alert-text-success { background: var(--ok-soft); border-color: var(--ok); color: var(--ok); }

/* ---------- цели касания не меньше 44 px (D-7) ---------- */
/*  .link-btn — ссылка-кнопка в строке текста. Высота 44 px — полями, а
    отрицательные поля сверху и снизу возвращают строку к прежней
    высоте: попадать пальцем легче, а текст не раздвигается. */
.link-btn.mud-button-root,
button.link-btn {
    min-height: 44px;
    margin-top: -10px;
    margin-bottom: -10px;
}
.act-tools button { min-width: 44px; min-height: 44px; }

/* Метки «питание», «шлюз» — словом, а не одной буквой: строчными, чуть крупнее. */
.mark.mark-word { font-size: var(--fs-label); font-weight: 600; text-transform: none; }

/* Полоса уведомлений и поздравление — по ширине той же колонки, что страница. */
.main > .announces { width: calc(100% - 2 * var(--s6)); max-width: calc(1460px - 2 * var(--s6)); margin: var(--s4) auto 0; }

/* ---------- отступы и подписи вместо style= в разметке ---------- */
/*  Раньше в .razor жило под три сотни style="margin-top:10px" и подобных —
    десятки разных значений мимо шкалы --s1…--s7. Теперь разметка берёт
    шаг классом: mt-s3 — отступ сверху в третий шаг, m-0 — без внешних
    отступов, p-s4 — поле в четвёртый шаг. !important — потому что они
    заменяют style=, а тот перебивал любое правило из этого файла; иначе
    отступ молча проиграл бы, например, .page-alert.mud-alert. */
.m-0 { margin: 0 !important; }
.p-0 { padding: 0 !important; }
.mt-0 { margin-top: 0 !important; }
.mt-s1 { margin-top: var(--s1) !important; }
.mt-s2 { margin-top: var(--s2) !important; }
.mt-s3 { margin-top: var(--s3) !important; }
.mt-s4 { margin-top: var(--s4) !important; }
.mt-s5 { margin-top: var(--s5) !important; }
.mt-s6 { margin-top: var(--s6) !important; }
.mt-s7 { margin-top: var(--s7) !important; }
.mb-0 { margin-bottom: 0 !important; }
.mb-s1 { margin-bottom: var(--s1) !important; }
.mb-s2 { margin-bottom: var(--s2) !important; }
.mb-s3 { margin-bottom: var(--s3) !important; }
.mb-s4 { margin-bottom: var(--s4) !important; }
.mb-s5 { margin-bottom: var(--s5) !important; }
.mb-s6 { margin-bottom: var(--s6) !important; }
.mb-s7 { margin-bottom: var(--s7) !important; }
.ml-0 { margin-left: 0 !important; }
.ml-s1 { margin-left: var(--s1) !important; }
.ml-s2 { margin-left: var(--s2) !important; }
.ml-s3 { margin-left: var(--s3) !important; }
.ml-s4 { margin-left: var(--s4) !important; }
.ml-s5 { margin-left: var(--s5) !important; }
.ml-s6 { margin-left: var(--s6) !important; }
.ml-s7 { margin-left: var(--s7) !important; }
.mr-0 { margin-right: 0 !important; }
.mr-s1 { margin-right: var(--s1) !important; }
.mr-s2 { margin-right: var(--s2) !important; }
.mr-s3 { margin-right: var(--s3) !important; }
.mr-s4 { margin-right: var(--s4) !important; }
.mr-s5 { margin-right: var(--s5) !important; }
.mr-s6 { margin-right: var(--s6) !important; }
.mr-s7 { margin-right: var(--s7) !important; }
.pt-0 { padding-top: 0 !important; }
.pt-s1 { padding-top: var(--s1) !important; }
.pt-s2 { padding-top: var(--s2) !important; }
.pt-s3 { padding-top: var(--s3) !important; }
.pt-s4 { padding-top: var(--s4) !important; }
.pt-s5 { padding-top: var(--s5) !important; }
.pt-s6 { padding-top: var(--s6) !important; }
.pt-s7 { padding-top: var(--s7) !important; }
.pb-0 { padding-bottom: 0 !important; }
.pb-s1 { padding-bottom: var(--s1) !important; }
.pb-s2 { padding-bottom: var(--s2) !important; }
.pb-s3 { padding-bottom: var(--s3) !important; }
.pb-s4 { padding-bottom: var(--s4) !important; }
.pb-s5 { padding-bottom: var(--s5) !important; }
.pb-s6 { padding-bottom: var(--s6) !important; }
.pb-s7 { padding-bottom: var(--s7) !important; }
.pl-0 { padding-left: 0 !important; }
.pl-s1 { padding-left: var(--s1) !important; }
.pl-s2 { padding-left: var(--s2) !important; }
.pl-s3 { padding-left: var(--s3) !important; }
.pl-s4 { padding-left: var(--s4) !important; }
.pl-s5 { padding-left: var(--s5) !important; }
.pl-s6 { padding-left: var(--s6) !important; }
.pl-s7 { padding-left: var(--s7) !important; }
.pr-0 { padding-right: 0 !important; }
.pr-s1 { padding-right: var(--s1) !important; }
.pr-s2 { padding-right: var(--s2) !important; }
.pr-s3 { padding-right: var(--s3) !important; }
.pr-s4 { padding-right: var(--s4) !important; }
.pr-s5 { padding-right: var(--s5) !important; }
.pr-s6 { padding-right: var(--s6) !important; }
.pr-s7 { padding-right: var(--s7) !important; }
.p-s4 { padding: var(--s4) !important; }
.ml-auto { margin-left: auto !important; }
/* Пояснение, подтянутое к строке над ним. */
.mt-pull { margin-top: calc(var(--s2) * -1) !important; }

/* Строки и столбцы: кнопки в ряд, ряд с переносом, столбец. Промежуток
   по умолчанию — второй шаг, другой — классом gap-sN. */
.row-flex { display: flex !important; gap: var(--s2); }
.row-wrap { display: flex !important; flex-wrap: wrap !important; gap: var(--s2); }
.col-stack { display: flex !important; flex-direction: column !important; gap: var(--s2); }
.wrap { flex-wrap: wrap !important; }
.items-center { align-items: center !important; }
.justify-center { justify-content: center !important; }
.gap-s1 { gap: var(--s1) !important; }
.gap-s2 { gap: var(--s2) !important; }
.gap-s3 { gap: var(--s3) !important; }
.gap-s4 { gap: var(--s4) !important; }
.gap-s5 { gap: var(--s5) !important; }

/* Слово, которое нельзя рвать переносом: учебный год «2026-2027». */
.nowrap { white-space: nowrap; }

/* Подписи по шкале шрифтов. */
.text-small { font-size: var(--fs-small) !important; }
.w-normal { font-weight: 400 !important; }
/* Приписка к заголовку блока («· только просмотр»). */
.h-aside { font-size: var(--fs-small); font-weight: 400; }
/* Крупная цифра сводки: число событий, ошибок, прибывших. */
.figure { font-size: var(--fs-title); font-weight: 700; line-height: 1.2; font-variant-numeric: tabular-nums; }
/* Цифра-строка помельче: время последней ошибки и подобное. */
.figure-line { font-size: var(--fs-section); font-weight: 600; padding: var(--s1) 0; }
