/* ============================================================
   Светлая минималистичная тема с синим акцентом. Mobile-first.
   ============================================================ */
:root {
    /* Notion-style: светлый, почти белый фон с едва заметным голубым оттенком */
    --bg: #f6f8fc;
    --surface: #ffffff;
    --surface-2: #eef2fa;
    --border: #e2e8f3;
    --text: #16202e;
    --muted: #66728a;
    /* Насыщенный, достаточно тёмный синий — читается как акцент, не
       вымывается на белом фоне; пастельным остаётся только фон-подложка (--accent-bg) */
    --accent: #3457d5;
    --accent-dark: #2643b0;

    --green-dark: #15803d;
    --green: #16a34a;
    --orange: #e0993c;
    --red: #dc2626;
    --gray: #6b7280;

    --green-bg: #e1f6e7;
    --red-bg: #fdeaea;
    --orange-bg: #fbf0dc;
    --gray-bg: #eef1f6;
    --green-dark-bg: #cdefd6;
    --accent-bg: #e6ecfd;

    /* Второе семейство — для заголовков, цен и крупных цифр.
       Golos Text остаётся текстовым: он ровный и не мешает читать длинные
       списки. Onest плотнее и современнее в крупном кегле — там, где цифра
       или название уровня работает как картинка, а не как предложение.
       Если Onest не загрузился, всё честно откатывается на Golos Text. */
    --font-display: "Onest", "Golos Text", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;

    /* Шкала скруглений: мелкие элементы -> крупные поверхности */
    --radius-xs: 10px;
    --radius-sm: 14px;
    --radius: 20px;
    --radius-lg: 26px;

    /* Мягкие многослойные тени вместо жёстких границ */
    --shadow: 0 1px 2px rgba(22, 32, 46, 0.05), 0 8px 20px rgba(22, 32, 46, 0.06);
    --shadow-lift: 0 14px 34px rgba(22, 32, 46, 0.12);
}

* { box-sizing: border-box; -webkit-tap-highlight-color: transparent; }
/* Плавный переход между светлой и тёмной темой — только цветовые свойства,
   не "всё подряд" (иначе даже нажатие кнопки .btn:active{transform:scale}
   начинало бы тормозить, унаследовав ту же долгую длительность). */
*, *::before, *::after {
    transition: background-color .25s ease, border-color .25s ease, color .25s ease, box-shadow .25s ease, fill .25s ease, stroke .25s ease;
}
/* Явный сброс на интерактивных элементах — некоторые мобильные браузеры
   (особенно старые версии Chrome/WebView на Android) игнорируют это
   свойство на *, если оно не продублировано прямо на самом элементе:
   иначе при тапе по <select> внутри ячейки таблицы подсвечивается синим
   вся строка целиком, а не сам элемент. */
a, button, summary, select, tr, td, .btn, .cal-slot, .today-row { -webkit-tap-highlight-color: transparent; }
html { -webkit-text-size-adjust: 100%; height: 100%; }
/* text-size-adjust на html не всегда достаёт до текста внутри плотных
   flex-строк с обрезкой (min-width:0 + white-space:nowrap + ellipsis) —
   известная особенность WebKit на iPhone: он считает такую строку «слишком
   тесной для чтения» и раздувает шрифт САМ, безо всякого правила в нашем
   CSS, причём выборочно — соседний текст той же страницы остаётся обычного
   размера. Так «Проведено» и тема занятия в списке дня оказывались вдвое
   крупнее собственного font-size. Повторяем свойство прямо на тех
   элементах, где встречается эта комбинация. */
.today-row, .today-name, .badge { -webkit-text-size-adjust: 100%; }

body {
    margin: 0;
    font-family: "Golos Text", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
    background: var(--bg);
    color: var(--text);
    line-height: 1.5;
    font-size: 15.5px;
    min-height: 100%;
    display: flex;
    flex-direction: column;
}

/*
 * Обёртка шапка+контент+футер (см. includes/header.php/footer.php).
 * В обычном браузере — тот же самый столбец, каким был раньше body, ничего
 * не меняется. В установленном PWA (html.is-standalone) документ вообще не
 * прокручивается — прокручивается этот контейнер: position:fixed считается
 * от окна, а не от видимой области, и iOS двигает именно окно при скролле,
 * "резинке", открытии клавиатуры и возврате из фона — если окно вообще
 * никогда не двигается, таббару и шапке двигаться не от чего.
 */
.app-scroll {
    display: flex;
    flex-direction: column;
    flex: 1 0 auto;
    min-width: 0;
}
html.is-standalone,
html.is-standalone body { height: 100%; overflow: hidden; }
html.is-standalone body { min-height: 0; }
html.is-standalone .app-scroll {
    flex: 1 1 auto;
    min-height: 0;
    height: 100%;
    overflow-x: hidden;
    overflow-y: auto;
    overscroll-behavior-y: contain; /* "резинка" остаётся у списка, не у окна */
    -webkit-overflow-scrolling: touch;
}

a { color: var(--accent); text-decoration: none; }
a:hover { text-decoration: underline; }

h1, h2, h3 { font-family: var(--font-display); font-weight: 800; letter-spacing: -0.02em; color: var(--text); }

/* ===== Шапка =====
   Без backdrop-filter/blur: на слабых телефонах он подвешивает скролл —
   вместо "стеклянного" эффекта берём ровный непрозрачный фон + тень. */
.site-header {
    background: var(--surface);
    border-bottom: 1px solid var(--border);
    box-shadow: 0 1px 0 rgba(22,32,46,.02);
    position: sticky;
    top: 0;
    z-index: 20;
}
.header-inner {
    max-width: 1000px;
    margin: 0 auto;
    padding: 14px 18px;
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 14px;
    flex-wrap: wrap;
}
.header-right { display: flex; align-items: center; gap: 4px; }
.brand .logo { max-height: 52px; display: block; }
.brand-text { font-size: 21px; font-weight: 800; color: var(--text); letter-spacing: -0.02em; }

/* Пункты навигации — иконка + подпись, единый компонент .nav-link
   переиспользуется и в верхней строке (десктоп), и в нижнем таббаре (моб.) */
.main-nav { display: flex; align-items: center; gap: 4px; flex-wrap: wrap; }
.nav-icon { width: 19px; height: 19px; flex-shrink: 0; }
.nav-link {
    display: inline-flex;
    align-items: center;
    gap: 7px;
    color: var(--muted);
    font-weight: 600;
    font-size: 14px;
    padding: 9px 13px;
    border-radius: var(--radius-sm);
    position: relative;
    list-style: none;
    cursor: pointer;
    transition: background .12s ease, color .12s ease;
}
.nav-link:hover, .main-nav a:hover { color: var(--accent-dark); background: var(--accent-bg); text-decoration: none; }
.nav-link[aria-current="page"], .nav-link.is-active { color: var(--accent-dark); background: var(--accent-bg); }
summary.nav-link::-webkit-details-marker { display: none; }

/* ===== Аватарка с обводкой уровня =====
   Кружок с инициалами вместо трёх точек «Ещё». Обводка вокруг него — цвет
   купленного уровня (--ring приходит из шаблона), и это единственное место,
   где статус виден, не открывая меню и не занимая ни строчки на экране.
   Раньше на эту роль была бледная серая подпись под именем: её никто не
   читал, а платят как раз за то, что в ней написано.
   Двойной box-shadow — сначала кольцо цветом фона, потом цветное: между
   аватаркой и обводкой нужен зазор, иначе кольцо сливается с заливкой. */
.nav-ava {
    /* Своего --ring тут быть не должно: объявление на самом элементе
       перебило бы значение, унаследованное от .nav-profile / кнопки
       таббара, и кружок у всех уровней стал бы одинаково серым. Серый —
       только запасной вариант прямо в месте использования. */
    box-sizing: border-box;
    width: 22px; height: 22px;
    flex-shrink: 0;
    display: inline-flex; align-items: center; justify-content: center;
    border-radius: 50%;
    font-size: 9px; font-weight: 800; line-height: 1; letter-spacing: .02em;
    color: #fff;
    /* Заливка — приглушённый вариант цвета уровня, а не чистый цвет: на
       светлых тарифах (голубой Premium, розовый Creator) белые инициалы на
       нём переставали читаться. Цвет уровня во всей силе остаётся в самом
       кольце — именно оно и опознаётся. */
    background: linear-gradient(140deg,
        color-mix(in srgb, var(--ring, var(--gray)) 50%, #121722) 0%,
        color-mix(in srgb, var(--ring, var(--gray)) 78%, #121722) 100%);
    box-shadow: 0 0 0 2px var(--surface), 0 0 0 3.5px var(--ring, var(--gray));
}
/* С фотографией — тот же кружок и то же кольцо уровня, только внутри не
   буквы, а снимок. Градиентная заливка под ним не нужна и мешала бы, пока
   картинка грузится: вместо неё нейтральная подложка. */
.nav-ava.has-photo { background: var(--surface-2); overflow: hidden; }
.nav-ava.has-photo img {
    width: 100%; height: 100%;
    object-fit: cover;
    border-radius: 50%;
    display: block;
}

/* В самом таббаре обводке нужен воздух — иначе кольцо режется соседями */
.nav-profile-btn { --ring: var(--gray); }
.nav-profile-btn .nav-ava { margin: 2px; }
.nav-ava-lg {
    width: 40px; height: 40px;
    font-size: 15px;
    box-shadow: 0 0 0 2.5px var(--surface), 0 0 0 4.5px var(--ring, var(--gray));
    margin: 3px;
}

/* В тёмной теме почти чёрный уровень (Ultimate+) сливался с подложкой —
   кольцо было, но его не было видно. Тонкий светлый контур снаружи ставим
   всем: тёмным он возвращает границу, светлым не мешает. */
[data-theme="dark"] .nav-ava {
    box-shadow:
        0 0 0 2px var(--surface),
        0 0 0 3.5px var(--ring, var(--gray)),
        0 0 0 4.4px rgba(255, 255, 255, .34);
}
[data-theme="dark"] .nav-ava-lg {
    box-shadow:
        0 0 0 2.5px var(--surface),
        0 0 0 4.5px var(--ring, var(--gray)),
        0 0 0 5.5px rgba(255, 255, 255, .34);
}
[data-theme="dark"] .nav-dropdown-menu .nav-profile-tier { box-shadow: inset 0 0 0 1px rgba(255, 255, 255, .18); }

/* ===== Карточка профиля в шапке меню ===== */
.nav-profile {
    --ring: var(--gray);
    display: flex; align-items: center; gap: 12px;
    margin: 2px 4px 8px; padding: 8px 8px 12px;
    border-bottom: 1px solid var(--border);
}
.nav-profile-info { flex: 1 1 auto; min-width: 0; display: flex; flex-direction: column; gap: 4px; }
.nav-profile-name {
    font-size: 14.5px; font-weight: 700; color: var(--text);
    line-height: 1.2;
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.nav-profile-sub { font-size: 12px; color: var(--muted); line-height: 1.3; }
.nav-profile-sub b { color: var(--text); font-weight: 600; }
/* Плашка тарифа — заливкой цвета уровня, а не серым текстом: это то, за что
   заплачено, и выглядеть оно должно как купленное, а не как подпись. */
.nav-dropdown-menu .nav-profile-tier {
    display: inline-flex; align-items: baseline; gap: 6px;
    align-self: flex-start;
    padding: 3px 10px 4px;
    border-radius: 999px;
    background: linear-gradient(120deg,
        color-mix(in srgb, var(--ring) 45%, #121722) 0%,
        color-mix(in srgb, var(--ring) 68%, #121722) 100%);
    color: #fff;
    font-size: 11.5px; font-weight: 700; letter-spacing: .01em;
    text-decoration: none;
    line-height: 1.25;
}
.nav-dropdown-menu .nav-profile-tier:hover { text-decoration: none; filter: brightness(1.1); background-color: transparent; }
.nav-profile-tier-rest { font-weight: 600; opacity: .82; }
.nav-dropdown-menu .nav-profile-tier.is-off { background: var(--red); }
/* Шестерёнка справа: настройки учётной записи — часть профиля, а не пункт
   среди двух десятков строк ниже. */
.nav-dropdown-menu .nav-profile-gear {
    flex-shrink: 0;
    padding: 8px;
    border-radius: 50%;
    color: var(--muted);
}
.nav-dropdown-menu .nav-profile-gear .nav-icon { width: 20px; height: 20px; }
.nav-dropdown-theme { display: none; }
/* Включённый push — та же подсветка, что у активного пункта меню, чтобы
   было видно текущее состояние с первого взгляда, без чтения текста. */
.push-toggle-trigger.is-active { color: var(--accent-dark); }
.push-toggle-trigger.is-active .nav-icon { color: var(--accent); }
/* .nav-dropdown-theme (общий класс с переключателем темы) по умолчанию
   display:none — на десктопе тема переключается отдельной кнопкой в шапке,
   и без этого правила push тоже пропадал бы на десктопе, а отдельной
   кнопки для него там нет. !important — расположение самого правила
   .nav-dropdown-theme{display:none} ниже по файлу иначе перебивало бы это. */
button.push-toggle-trigger { display: flex !important; }
.nav-dot {
    display: inline-block;
    width: 7px;
    height: 7px;
    border-radius: 50%;
    background: var(--red);
    margin-left: 2px;
    vertical-align: top;
}

/* ===== Основной контейнер ===== */
.site-main {
    max-width: 960px;
    width: 100%;
    margin: 0 auto;
    padding: 18px 14px 70px;
    flex: 1 0 auto;
    box-sizing: border-box;
}
/* Немного воздуха перед футером на телефоне — саму защиту от нижнего
   таббара обеспечивает padding-bottom футера (см. ниже), не дублируем тут. */
/* Помимо ширины — ещё и по высоте: телефон в альбомной ориентации обычно
   шире 760px, но остаётся именно телефоном (мало места по высоте), а не
   планшетом/десктопом. Без второго условия при повороте телефона нижний
   таббар и весь мобильный вид пропадали, включалась десктопная раскладка,
   для которой на телефоне физически нет места — кнопки/пункты меню
   наезжали друг на друга или обрезались.
   (hover: none) and (pointer: coarse) — обязательно вместе с высотой:
   иначе это же условие ложно срабатывало и на компьютере, если окно
   браузера просто невысокое (не на весь экран) — тогда мобильная
   раскладка меню включалась поверх мышиного интерфейса и всё разъезжалось
   («то густо, то пусто» в отступах). hover/pointer отличают сенсорные
   устройства (телефон, где и нужна альбомная раскладка) от компьютера с
   мышью — на компьютере это условие теперь не срабатывает никогда. */
@media (max-width: 760px), ((max-height: 480px) and (hover: none) and (pointer: coarse)) {
    .site-main { padding-bottom: 26px; }
}

.site-footer {
    text-align: center;
    color: var(--muted);
    font-size: 12px;
    padding: 20px 0 40px;
}

/* ===== Заголовки страниц ===== */
.page-head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    flex-wrap: wrap;
    gap: 12px;
    margin-bottom: 18px;
}
.page-head h1 { margin: 0; font-size: 21px; }
.head-actions { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
.muted { color: var(--muted); font-weight: 400; font-size: 14px; }
.back-link { font-size: 13px; color: var(--muted); }
.back-link:hover { color: var(--accent); }

/* ===== Карточки форм ===== */
.form-card, .auth-box {
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    padding: 18px;
    max-width: 520px;
    box-shadow: var(--shadow);
}
/* Карточка во всю ширину рабочей области. Обычная .form-card ограничена
   520 пикселями — это ширина ФОРМЫ, за пределами которой поля становятся
   неудобно длинными. Но там, где в карточке не форма, а витрина тарифов
   или история, узкая колонка под широкой плашкой выглядела обрубком. */
.form-card.is-wide { max-width: none; }

.auth-box { margin: 8vh auto 40px; }
.auth-box h1 { margin-top: 0; font-size: 19px; }
.auth-hint { color: var(--muted); font-size: 13px; margin-top: 14px; text-align: center; }

form label {
    display: block;
    margin-bottom: 14px;
    font-size: 13px;
    font-weight: 600;
    color: var(--muted);
}
form input[type=text],
form input[type=password],
form input[type=date],
form input[type=time],
form input[type=number],
form input[type=url],
form input[type=file],
form select,
form textarea {
    display: block;
    width: 100%;
    margin-top: 6px;
    padding: 11px 12px;
    border: 1px solid var(--border);
    border-radius: 12px;
    font-size: 15px;
    font-family: inherit;
    background: var(--surface);
    color: var(--text);
    -webkit-appearance: none;
}
form input:focus, form select:focus, form textarea:focus {
    outline: none;
    border-color: var(--accent);
    box-shadow: 0 0 0 3px var(--accent-bg);
}
form textarea { resize: vertical; }
/* Чекбоксы/радио без явного размера рендерятся браузером "нативно" — а
   нативный размер на некоторых мобильных WebView зависит от font-size
   ОКРУЖАЮЩЕГО контекста (у него это относительные em-единицы), из-за чего
   один и тот же с виду чекбокс на одной странице оказывался то крупнее, то
   мельче в зависимости от того, где именно на странице он стоит. Фиксируем
   размер явно и одинаково везде — глобально, а не только внутри форм. */
input[type="checkbox"],
input[type="radio"] {
    width: 17px;
    height: 17px;
    min-width: 17px;
    flex-shrink: 0;
    margin: 0;
    cursor: pointer;
}
.checkbox-label { display: flex; align-items: center; gap: 8px; font-weight: 500; color: var(--text); cursor: pointer; }
.checkbox-label input { width: 17px; height: 17px; margin: 0; cursor: pointer; }
.field-hint { display: block; color: var(--muted); font-size: 12px; font-weight: 400; margin-top: 4px; }

.btn {
    display: inline-block;
    padding: 12px 20px;
    border-radius: var(--radius-sm);
    border: none;
    font-size: 14.5px;
    font-weight: 700;
    cursor: pointer;
    text-decoration: none;
    text-align: center;
    white-space: nowrap;
    transition: background .12s ease, border-color .12s ease, color .12s ease, transform .08s ease, box-shadow .12s ease;
}
.btn:active { transform: scale(.98); }
.btn-primary { background: var(--accent); color: #ffffff; box-shadow: 0 1px 2px rgba(22,32,46,.08); }
.btn-primary:hover { background: var(--accent-dark); text-decoration: none; color: #fff; box-shadow: 0 4px 14px rgba(52,87,213,.28); }
.btn-ghost { background: var(--surface); color: var(--text); border: 1px solid var(--border); }
.btn-ghost:hover { text-decoration: none; border-color: var(--accent); color: var(--accent-dark); background: var(--accent-bg); }
.btn-block { display: block; width: 100%; white-space: normal; }
/* Красная кнопка — только для действий, которые что-то отменяют или
   возвращают назад. Раньше такие действия оформлялись ссылкой, и рядом с
   синей кнопкой «сохранить» было непонятно, что из этого кнопка. */
.btn-danger { background: var(--red-bg); color: var(--red); border: 1px solid transparent; }
.btn-danger:hover { background: var(--red); color: #fff; text-decoration: none; }
.btn-sm { padding: 7px 12px; font-size: 12.5px; border-radius: var(--radius-xs); }

/* ===== Панель поиска/фильтров ===== */
.toolbar {
    display: flex;
    flex-wrap: wrap;
    gap: 10px;
    margin-bottom: 18px;
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    padding: 12px;
    box-shadow: var(--shadow);
}
.toolbar input[type=text], .toolbar select {
    display: inline-block;
    width: auto;
    margin-top: 0;
    padding: 9px 10px;
    border-radius: 12px;
    border: 1px solid var(--border);
    background: var(--surface);
    color: var(--text);
    font-size: 14px;
}
.toolbar form { display: flex; gap: 8px; flex-wrap: wrap; flex: 1; align-items: center; }
.toolbar input[type=text] { flex: 1 1 220px; min-width: 160px; }

/* ===== Общая статистика ===== */
.stats-row { display: flex; gap: 10px; margin-bottom: 20px; flex-wrap: wrap; }
.text-red { color: var(--red); }
.text-green { color: var(--green); }
.text-orange { color: var(--orange); }
.text-accent { color: var(--accent); }

/* ===== Список учеников (карточки) ===== */
.student-list { display: flex; flex-direction: column; gap: 10px; }
.student-card {
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    padding: 16px 18px;
    box-shadow: var(--shadow);
    transition: box-shadow .15s ease, border-color .15s ease;
}
@media (hover: hover) and (pointer: fine) {
    .student-card:hover { border-color: #d7e0f7; box-shadow: var(--shadow-lift); }
}
.student-name a { color: var(--text); }
.student-name a:hover { color: var(--accent-dark); }
.student-card-top {
    display: flex;
    justify-content: space-between;
    align-items: baseline;
    gap: 8px;
    flex-wrap: wrap;
}
.student-name { font-size: 16px; font-weight: 700; }
.student-login { color: var(--muted); font-size: 13px; }
.student-meta {
    display: flex;
    gap: 8px;
    flex-wrap: wrap;
    margin-top: 8px;
    font-size: 12.5px;
    color: var(--muted);
}
.student-actions {
    margin-top: 10px;
    display: flex;
    gap: 14px;
    flex-wrap: wrap;
    font-size: 13.5px;
    font-weight: 600;
}
.danger-link { color: var(--red); }
.next-lesson-form { display: inline-flex; align-items: center; gap: 6px; margin-top: 8px; flex-wrap: wrap; }
.next-lesson-form input[type=date] {
    width: auto;
    margin: 0;
    padding: 6px 8px;
    font-size: 13px;
}
.due-today { color: var(--orange); font-weight: 700; }
.overdue { color: var(--red); font-weight: 700; }

/* ===== Таблица ===== */
.data-table { width: 100%; border-collapse: collapse; }
.data-table th, .data-table td { padding: 10px 12px; text-align: left; font-size: 14px; }

/* ===== Бейджи ===== */
.badge {
    display: inline-block;
    padding: 3px 9px;
    border-radius: 20px;
    font-size: 11.5px;
    font-weight: 700;
    white-space: nowrap;
}
.badge-green { background: var(--green-bg); color: var(--green); }
.badge-red { background: var(--red-bg); color: var(--red); }
.badge-gray { background: var(--gray-bg); color: var(--muted); }
.badge-grade-5 { background: var(--green-dark-bg); color: var(--green-dark); }
.badge-grade-4 { background: var(--green-bg); color: var(--green); }
.badge-grade-3 { background: var(--orange-bg); color: var(--orange); }
.badge-grade-2 { background: var(--red-bg); color: var(--red); }
.badge-grade-n { background: var(--gray-bg); color: var(--gray); }
.badge-grade-none { background: var(--gray-bg); color: var(--muted); }

/* ===== Карточки занятий ===== */
.lesson-list { display: flex; flex-direction: column; gap: 12px; }
.lesson-card {
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    padding: 16px 18px;
    box-shadow: var(--shadow);
    transition: box-shadow .15s ease, border-color .15s ease;
}
.lesson-card:hover { border-color: #d7e0f7; box-shadow: var(--shadow-lift); }
.lesson-card-head {
    display: flex;
    align-items: center;
    gap: 8px;
    flex-wrap: wrap;
    margin-bottom: 8px;
    font-size: 14.5px;
}
.lesson-date { font-weight: 700; }
.lesson-card p { margin: 6px 0; font-size: 13.5px; color: var(--text); }
.lesson-card p b { color: var(--muted); font-weight: 600; }
.lesson-card-actions { margin-top: 10px; display: flex; gap: 14px; flex-wrap: wrap; font-size: 13px; font-weight: 600; }

.hw-quick-actions { display: flex; gap: 8px; flex-wrap: wrap; margin-top: 10px; }
.hw-quick-actions form { display: inline; }

.lesson-images { display: flex; gap: 8px; flex-wrap: wrap; margin-top: 8px; }
.thumb {
    width: 72px;
    height: 72px;
    object-fit: cover;
    border-radius: 10px;
    border: 1px solid var(--border);
}
.thumb-wrap { text-align: center; font-size: 11px; }

/* Маленькие миниатюры прямо в строке занятия (список у ученика/в карточке
   ученика у админа) — раньше там был только текст "N фото" без единой
   ссылки, теперь можно реально посмотреть вложение в один клик. */
.lesson-thumbs { display: flex; gap: 5px; flex-wrap: wrap; margin-top: 6px; }
.lesson-thumbs img {
    width: 34px;
    height: 34px;
    object-fit: cover;
    border-radius: 6px;
    border: 1px solid var(--border);
    display: block;
}
.logo-preview { max-height: 60px; }

/* ===== Прочее ===== */
.section-title { font-size: 15px; margin: 26px 0 12px; color: var(--muted); text-transform: uppercase; letter-spacing: .04em; }
.moodle-btn { display: inline-block; margin-top: 6px; }
.notes-box {
    background: var(--surface-2);
    border: 1px solid var(--border);
    border-radius: 12px;
    padding: 10px 12px;
    font-size: 13.5px;
    margin-bottom: 14px;
}

/* Короткие сообщения о результате действия («сохранено», «файл не тот»).
   Для чего-то большего — с заголовком, пояснением и кнопкой — есть
   .page-note ниже.

   Это ПРИМЕЧАНИЕ, а не плашка.
   ===========================================================================
   Раньше здесь была подложка в цвет смысла и трёхпиксельная полоска слева.
   Выглядело это ровно так, как выглядит машинно сгенерированный интерфейс:
   каждая фраза в собственной коробке, каждая коробка с ленточкой сбоку, и
   на странице настроек таких коробок по три подряд. Полоска при этом
   ничего не сообщала — цвет уже был в подложке, — а подложка отделяла
   строчку от формы, к которой строчка и относится.

   Теперь это обычный абзац: тот же кегль, тот же ритм, что у остального
   текста. Единственное отличие от соседнего текста — цвет: красный там,
   где не вышло, зелёный там, где вышло. Ни рамки, ни заливки, ни значка,
   ни точки слева — сообщению из одной фразы всего этого не нужно. */
.alert {
    margin: 0 0 12px;
    padding: 0;
    border: none;
    background: none;
    border-radius: 0;
    color: var(--muted);
    font-size: 13.5px;
    line-height: 1.5;
}
.alert-error { color: var(--red); }
.alert-success { color: var(--green-dark, var(--green)); }
.alert-warning { color: var(--muted); }
/* Ссылка и код внутри примечания остаются заметными: подложки, которая
   раньше их отделяла, больше нет. */
.alert a { color: inherit; text-decoration: underline; text-underline-offset: 2px; }
.alert code { font-size: .92em; }

/* Спокойная "инсайт"-плашка (например "падает рейтинг" на дашборде) —
   в отличие от .alert-warning (реальные проблемы — не запустился крон,
   сломалась интеграция), это просто наблюдение, не авария. Лёгкий жёлтый
   тон — чтобы не сливалась с обычными карточками и было видно, что это
   отдельная заметка, — но без яркого оранжевого текста/рамки, как у
   настоящего предупреждения: только мягкий фон, обычный цвет текста.
   Крестиком можно скрыть до следующего изменения списка (см. JS,
   admin/dashboard.php). */
.insight-card {
    position: relative;
    display: flex;
    flex-direction: column;
    gap: 6px;
    padding: 10px 34px 12px 14px;
    border-radius: 12px;
    background: var(--orange-bg);
    border: 1px solid transparent;
    font-size: 13.5px;
    margin-bottom: 14px;
    color: var(--text);
}
.insight-card b { font-weight: 600; color: var(--muted); font-size: 12.5px; text-transform: uppercase; letter-spacing: .02em; }
/* Список внутри — обычная строка текста со ссылками, НЕ крупные жирные
   .stat-chip (те рассчитаны на цифры в шапке страницы: белая плашка +
   огромное жирное число смотрелась аляповато на цветном фоне заметки). */
.insight-card-list { font-size: 13px; line-height: 1.7; color: var(--muted); }
.insight-card-list a { color: var(--text); font-weight: 600; text-decoration: underline; text-decoration-color: rgba(120, 95, 40, .3); text-underline-offset: 2px; }
.insight-card-list a:hover { text-decoration-color: currentColor; }
[data-theme="dark"] .insight-card-list a { text-decoration-color: rgba(242, 181, 99, .35); }
.insight-dismiss {
    position: absolute;
    top: 8px;
    right: 8px;
    width: 22px;
    height: 22px;
    display: flex;
    align-items: center;
    justify-content: center;
    border: none;
    background: none;
    color: var(--muted);
    font-size: 15px;
    line-height: 1;
    cursor: pointer;
    border-radius: 6px;
}
.insight-dismiss:hover { background: var(--surface-2); color: var(--text); }

.empty-state { color: var(--muted); font-style: italic; }

/* ===== Десктоп ===== */
@media (min-width: 720px) {
    body { font-size: 15.5px; }
    .site-main { padding: 30px 20px 80px; }
    .page-head h1 { font-size: 25px; }
    .form-card { padding: 24px; }
}

/* ===== v3: аватары, прогресс-бары, блок Moodle ===== */
:root {
    --orange-bg2: #ece9fb;
}
.badge-orange { background: var(--orange-bg); color: var(--orange); }
.badge-accent { background: var(--accent-bg); color: var(--accent-dark); }

.avatar {
    width: 44px;
    height: 44px;
    border-radius: 50%;
    background: var(--accent-bg);
    color: var(--accent-dark);
    display: flex;
    align-items: center;
    justify-content: center;
    font-weight: 800;
    font-size: 15px;
    flex-shrink: 0;
}
.avatar-sm { width: 34px; height: 34px; font-size: 13px; }

.progress-bar {
    background: var(--gray-bg);
    border-radius: 20px;
    height: 6px;
    overflow: hidden;
    margin-top: 6px;
}
.progress-bar-fill {
    background: var(--accent);
    height: 100%;
    border-radius: 20px;
}

.plan-picker-box {
    background: var(--gray-bg);
    border-radius: 12px;
    padding: 10px 12px;
    margin-bottom: 14px;
}
.plan-picker-box .checkbox-label { align-items: flex-start; }
.plan-picker-box .checkbox-label input { margin-top: 3px; }
.plan-picker-summary {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 10px;
    font-size: 13.5px;
}
.plan-picker-summary > span { min-width: 0; overflow-wrap: anywhere; }
.plan-picker-summary .btn { flex-shrink: 0; }
.plan-picker-icon {
    width: 14px;
    height: 14px;
    vertical-align: -2px;
    color: var(--muted);
    margin-right: 1px;
}

/* Группа чекбоксов "уровни/теги" (admin/library.php) — компактные пары
   чекбокс+бейдж в ряд, а не одна метка под другой на всю ширину, как у
   обычных form label. */
.level-checks { display: flex; flex-wrap: wrap; gap: 8px 14px; margin: 6px 0 4px; }
.level-check { margin-bottom: 0 !important; font-weight: 400 !important; }

/* ===== Перетаскиваемые списки (учебный план ученика, темы уровня в
   Библиотеке тем) — обычные строки simple-table/data-table, без отдельных
   "плашек" на каждую тему; порядок меняется мышкой, а не кнопками
   ↑/↓ с перезагрузкой страницы (см. ajax_reorder.php + JS внизу
   admin/student_plan.php и admin/library.php). */
.drag-handle {
    width: 28px;
    text-align: center;
    color: var(--muted);
    cursor: grab;
    -webkit-user-select: none;
    user-select: none;
}
.drag-handle:active { cursor: grabbing; }
.reorder-row.is-dragging { opacity: .35; }
.reorder-row td { cursor: default; }
.plan-edit-row td { padding-top: 0 !important; border-top: none !important; }
.plan-edit-row input[type=text] { margin: 0 !important; }
/* Кнопка массовой отметки "пройдено" — видна только в режиме выбора (когда
   видны и сами чекбоксы, см. .cell-check / body.select-mode выше по файлу),
   иначе просто бесполезно висела бы на странице без единого чекбокса. */
.plan-bulk-btn { display: none; }
body.select-mode .plan-bulk-btn { display: inline-block; }

.moodle-block {
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    padding: 14px 16px;
    margin-bottom: 16px;
    box-shadow: var(--shadow);
}
.moodle-block-head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    flex-wrap: wrap;
    gap: 8px;
}
.moodle-summary {
    display: flex;
    gap: 24px;
    flex-wrap: wrap;
    margin-top: 10px;
}
.moodle-summary > div { min-width: 140px; }

.code-box {
    background: var(--surface-2);
    border: 1px solid var(--border);
    border-radius: 12px;
    padding: 10px 12px;
    font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
    font-size: 12.5px;
    word-break: break-all;
    margin: 8px 0;
}

/* ===== v4: пробники, простая таблица, аккуратные бейджи ===== */
.badge-blue-outline { background: var(--accent-bg); color: var(--accent-dark); }

.simple-table {
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    overflow: hidden;
    box-shadow: var(--shadow);
    -webkit-user-select: none;
    user-select: none;
    -webkit-touch-callout: none;
}
/* Таблица ВНУТРИ карточки теряет собственную обводку: карточка уже есть,
   вторая рамка вокруг того же содержимого — то самое «блок в блоке». */
.form-card .simple-table,
.today-block .simple-table,
.pf-card .simple-table,
.form-card .data-table,
.today-block .data-table {
    background: transparent;
    border: none;
    border-radius: 0;
    box-shadow: none;
}
/* Заголовок таблицы внутри карточки — без плашки-подложки, просто линия */
.form-card .simple-table th,
.today-block .simple-table th,
.pf-card .simple-table th {
    background: transparent;
    border-bottom: 1px solid var(--border);
    padding-left: 0;
    padding-right: 14px;
}
.form-card .simple-table td:first-child,
.today-block .simple-table td:first-child,
.pf-card .simple-table td:first-child { padding-left: 0; }

.simple-table th {
    background: var(--surface-2);
    color: var(--muted);
    font-size: 12px;
    text-transform: uppercase;
    letter-spacing: .03em;
    padding: 10px 14px;
    text-align: left;
}
.simple-table td {
    padding: 10px 14px;
    border-top: 1px solid var(--border);
    font-size: 13.5px;
    vertical-align: middle;
    text-align: center;
}
.simple-table tr:first-child td { border-top: none; }
.simple-table tbody tr { transition: background .1s ease; }
/* Наведение — только у преподавателя на компьютере: строки этих таблиц не
   кликабельны целиком (только отдельные контролы внутри), а на телефоне
   :hover эмулируется тапом и "залипает" — поэтому эффект только там, где
   он честно означает "мышь навелась", и только в админке. */
@media (hover: hover) and (pointer: fine) {
    body.role-admin .simple-table tbody tr:hover td { background: var(--surface-2); }
}

.goal-box {
    background: linear-gradient(135deg, var(--accent-bg), var(--surface));
    border: 1px solid var(--border);
    border-radius: var(--radius);
    padding: 14px 16px;
    margin-bottom: 16px;
}
.goal-box .goal-label {
    font-size: 11px;
    text-transform: uppercase;
    letter-spacing: .04em;
    color: var(--accent-dark);
    font-weight: 700;
}
.goal-box p {
    margin: 6px 0 0;
    font-size: 14px;
    color: var(--text);
}

/* ===== Таблица занятий: закруглённые строки-карточки, всё по центру ===== */
.lessons-table {
    border-collapse: separate;
    border-spacing: 0 8px;
    /* На телефоне долгий тап по ячейке иначе выделяет текст вместо простого
       нажатия на контрол (селект статуса, меню действий) — визуально
       выглядит как "залипание" синим выделением. */
    -webkit-user-select: none;
    user-select: none;
    -webkit-touch-callout: none;
}
.lessons-table thead th {
    border: none;
    background: transparent;
    color: var(--muted);
    font-size: 11px;
    text-transform: uppercase;
    letter-spacing: .04em;
    font-weight: 700;
    padding: 0 14px 6px;
}
.lessons-table tbody tr {
    background: var(--surface);
    box-shadow: var(--shadow);
    transition: box-shadow .12s ease;
}
@media (hover: hover) and (pointer: fine) {
    body.role-admin .lessons-table tbody tr:hover { box-shadow: var(--shadow-lift); }
}
.lessons-table tbody td {
    border-top: none;
    padding: 12px 14px;
    text-align: center;
    vertical-align: middle;
    font-size: 13.5px;
}
.lessons-table tbody td:first-child { border-radius: 14px 0 0 14px; }
.lessons-table tbody td:last-child { border-radius: 0 14px 14px 0; }
.lessons-table .topic-cell { max-width: 320px; text-align: left; }
.lessons-table .topic-cell .topic-line { text-align: left; }
.lessons-table .hw-text { text-align: left; }

/* Селекты быстрого редактирования — компактные закруглённые "таблетки" с цветом статуса */
.lessons-table .hw-select {
    padding: 5px 10px;
    font-size: 11.5px;
    font-weight: 600;
    border-radius: 20px;
    border: 1px solid transparent;
    background: var(--gray-bg);
    color: var(--muted);
    text-align: center;
    text-align-last: center;
    cursor: pointer;
    transition: filter .1s ease;
}
.lessons-table .hw-select:hover { filter: brightness(0.97); }
.lessons-table .hw-select:focus { outline: none; box-shadow: 0 0 0 3px var(--accent-bg); }
.hw-state-done .hw-select, .hw-state-paid .hw-select { background: var(--green-bg); color: var(--green); }
.hw-state-not_done .hw-select, .hw-state-unpaid .hw-select { background: var(--red-bg); color: var(--red); }
.hw-state-pending .hw-select { background: var(--gray-bg); color: var(--muted); }
/* «Просрочено» — засчитано, но с опозданием; «переделать» — ждём новую сдачу */
.hw-state-late .hw-select { background: var(--orange-bg); color: var(--orange); }
.hw-state-redo .hw-select { background: var(--accent-bg); color: var(--accent-dark); }
.hw-state-grade-5 .hw-select { background: var(--green-dark-bg); color: var(--green-dark); }
.hw-state-grade-4 .hw-select { background: var(--green-bg); color: var(--green); }
.hw-state-grade-3 .hw-select { background: var(--orange-bg); color: var(--orange); }
.hw-state-grade-2 .hw-select { background: var(--red-bg); color: var(--red); }
.hw-state-grade-n .hw-select, .hw-state-grade-none .hw-select { background: var(--gray-bg); color: var(--gray); }

.tab-nav { display: flex; gap: 6px; margin-bottom: 18px; flex-wrap: wrap; }
.tab-nav a {
    padding: 9px 15px;
    border-radius: var(--radius-sm);
    font-size: 13.5px;
    font-weight: 600;
    color: var(--muted);
    background: var(--surface);
    border: 1px solid var(--border);
    transition: background .12s ease, color .12s ease, border-color .12s ease;
}
.tab-nav a:hover { color: var(--accent-dark); background: var(--accent-bg); border-color: var(--accent-bg); text-decoration: none; }
.tab-nav a.active { color: var(--accent-dark); background: var(--accent-bg); border-color: var(--accent-bg); font-weight: 700; }

/* ===== v5: донат-диаграмма, компактные плашки, подсветка строк, архив ===== */
.profile-top {
    display: flex;
    align-items: center;
    justify-content: space-between;
    flex-wrap: wrap;
    gap: 16px;
    margin-bottom: 16px;
}
.profile-identity { display: flex; align-items: center; gap: 14px; }
.profile-identity h1 { margin: 0 0 2px; font-size: 20px; }
.profile-sub { color: var(--muted); font-size: 13px; }
.profile-goal { color: var(--accent-dark); font-size: 13px; font-weight: 600; margin-top: 2px; }

.info-bar {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 10px;
    margin-bottom: 18px;
}
.info-pill {
    display: inline-flex;
    align-items: baseline;
    gap: 6px;
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: 20px;
    padding: 7px 14px;
    font-size: 12.5px;
    box-shadow: var(--shadow);
}
.info-pill .pill-label { color: var(--muted); }
.info-pill .pill-value { font-weight: 700; color: var(--text); }
.info-pill.pill-warn .pill-value { color: var(--red); }
.info-pill.pill-ok .pill-value { color: var(--green); }
.info-pill.pill-today .pill-value { color: var(--orange); }
/* Плашки-кнопки в профиле ученика: ведут к занятиям, платежам, планированию */
a.info-pill.is-link, summary.info-pill.is-link { color: inherit; text-decoration: none; cursor: pointer; transition: border-color .12s ease; }
a.info-pill.is-link:hover, summary.info-pill.is-link:hover { border-color: var(--accent); text-decoration: none; }
.info-pill .pill-arrow { color: var(--muted); font-weight: 700; margin-left: 2px; }
.pill-pop { position: relative; }
.pill-pop > summary { list-style: none; }
.pill-pop > summary::-webkit-details-marker { display: none; }
.pill-pop-body {
    position: absolute; top: calc(100% + 6px); left: 0; z-index: 30;
    width: 260px; max-width: calc(100vw - 32px);
    display: flex; flex-direction: column; gap: 8px;
    background: var(--surface); border: 1px solid var(--border); border-radius: 14px;
    padding: 12px; box-shadow: 0 8px 24px rgba(16, 24, 40, .14); font-size: 13px;
}
.pill-pop-body label { display: flex; flex-direction: column; gap: 3px; color: var(--muted); font-size: 12px; }
.pill-pop-body input { font: inherit; font-size: 14px; padding: 7px 9px; border: 1px solid var(--border); border-radius: 10px; background: var(--surface); color: var(--text); }
.pill-pop-body a { font-size: 12.5px; }
@media (max-width: 700px) {
    .pill-pop { position: static; }
    .pill-pop-body { left: 16px; right: 16px; width: auto; top: auto; margin-top: 6px; position: absolute; }
}


.archive-toggle {
    margin-top: 10px;
    font-size: 13px;
}
.archive-toggle summary {
    cursor: pointer;
    color: var(--muted);
    font-weight: 600;
    padding: 8px 2px;
}
.archive-toggle summary:hover { color: var(--accent); }

.photo-upload-form { display: flex; align-items: center; gap: 10px; margin-top: 6px; }
.photo-upload-form input[type=file] { font-size: 12px; }

/* Пунктов со временем стало больше, чем помещается в ширину телефона —
   без overflow-x они просто переносились/вылезали за пределы контейнера,
   и это "разъезжание" тянуло за собой горизонтальную прокрутку у ВСЕЙ
   страницы (не только у самого меню), отсюда ощущение "шатающегося" экрана
   и полоски для скролла вправо-влево внизу. Теперь лишнее уезжает вбок
   только внутри самого меню, скроллится пальцем, страница остаётся на месте. */
.secondary-nav {
    display: flex;
    gap: 16px;
    margin: 26px 0 4px;
    font-size: 13.5px;
    overflow-x: auto;
    white-space: nowrap;
    -webkit-overflow-scrolling: touch;
    scrollbar-width: thin;
    padding-bottom: 4px;
    padding-right: 12px; /* последний пункт не впритык к краю экрана при скролле */
}
.secondary-nav a { color: var(--muted); font-weight: 600; flex-shrink: 0; }
.secondary-nav a:hover { color: var(--accent); }
/* Переключатель между двумя видами одной страницы (например report.php /
   student_summary.php — "Для родителей" и "Для ученика, в баллах") —
   не список ссылок на разные разделы (для этого есть .secondary-nav), а
   именно состояние "где я сейчас": закруглённый сегмент-переключатель в
   стиле iOS/Notion, активная вкладка — на белой (тёмной в тёмной теме)
   подложке с лёгкой тенью, вторая — просто ссылка тем же размером рядом. */
.view-tabs {
    /* display:flex (не inline-flex) + width:max-content — блок на своей
       строке (inline-flex вёл себя как строчный элемент и на широком
       экране вставал В ОДНУ строку со следующим элементом формы,
       "уезжая" вправо — на телефоне из-за нехватки места это визуально
       пряталось само собой, поэтому баг был виден только на компьютере),
       но при этом не растягивается на всю ширину контейнера. */
    display: flex;
    width: max-content;
    gap: 3px;
    padding: 4px;
    background: var(--surface-2);
    border-radius: 999px;
    margin: 0 0 16px;
}
.view-tabs a, .view-tabs .tab-active {
    display: inline-flex;
    align-items: center;
    padding: 8px 16px;
    border-radius: 999px;
    font-size: 13px;
    font-weight: 600;
    color: var(--muted);
    text-decoration: none;
    white-space: nowrap;
}
.view-tabs a:hover { color: var(--text); text-decoration: none; }
/* Вкладка, которая ждёт действия («На проверку (3)») — заметна, но не
   кричит: это напоминание, а не ошибка */
.view-tabs a.tab-attention, .tab-nav a.tab-attention { color: var(--orange); font-weight: 700; }
.view-tabs .tab-active {
    background: var(--surface);
    color: var(--accent-dark);
    box-shadow: 0 1px 3px rgba(16, 24, 40, .1);
}
@media (max-width: 760px) {
    /* width:max-content (см. выше) на телефоне делал контейнер ШИРЕ экрана,
       когда вкладок много (пять на «Проверка работ и статистика»), а
       white-space:nowrap не давал тексту ужаться. Из-за этого горизонтальную
       прокрутку получала ВСЯ страница — она «ездила» вправо-влево, и всё
       остальное вместе с ней. Теперь лишнее прокручивается внутри самих
       вкладок, а страница стоит на месте (тот же приём, что и .secondary-nav).
       flex:1 0 auto — когда вкладок мало (две в кабинете ученика), они
       по-прежнему растягиваются на всю ширину, но ужиматься до нечитаемого
       уже не могут. */
    .view-tabs {
        width: auto;
        max-width: 100%;
        overflow-x: auto;
        -webkit-overflow-scrolling: touch;
        scrollbar-width: none;
    }
    .view-tabs::-webkit-scrollbar { display: none; }
    .view-tabs a, .view-tabs .tab-active { flex: 1 0 auto; justify-content: center; }
}

/* ===== v6: фоновая текстура, мобильная таблица, ДЗ-акцент, аватар по клику ===== */

/* Едва заметный фоновый узор, медленно смещается */
body::before {
    content: "";
    position: fixed;
    inset: 0;
    z-index: -1;
    background-image: url("../img/bg-pattern.svg");
    background-repeat: repeat;
    background-size: 260px 260px;
    opacity: 0.05;
    animation: bg-drift 90s linear infinite;
    pointer-events: none;
}
@keyframes bg-drift {
    0%   { background-position: 0 0; }
    100% { background-position: 520px 260px; }
}

/* Заголовки таблиц по центру */
.simple-table th, .data-table th { text-align: center; }

/* Тема занятия крупнее и жирнее, ДЗ — мельче и обычным текстом под ней */
.topic-line { font-weight: 700; color: var(--text); font-size: 14px; }
@media (max-width: 700px) {
    .topic-line { font-size: 13.5px; line-height: 1.3; }
    .hw-text { font-size: 12.5px; margin-top: 3px; }
    .lessons-table td.topic-cell { padding: 4px 0 6px; }
    .lessons-table .badge { font-size: 11px; padding: 2px 8px; }
}
.hw-text { font-size: 12px; color: var(--muted); margin-top: 4px; line-height: 1.4; }
.hw-text b { color: var(--text); font-weight: 600; }

/* Раскрытие длинного текста (ДЗ, заметки) — ограничение по высоте (в
   строках), а не по числу символов: одинаково аккуратно смотрится в узкой
   ячейке таблицы и в широком блоке заметок, не обрезает слова посередине. */
.expand-wrap .expand-body {
    max-height: 4.3em; /* около 3 строк при line-height 1.4 */
    overflow: hidden;
}
.expand-wrap.expand-open .expand-body { max-height: none; }
.expand-btn {
    display: block;
    margin-top: 4px;
    padding: 0;
    background: none;
    border: none;
    font: inherit;
    font-size: 11px;
    font-weight: 700;
    color: var(--accent);
    cursor: pointer;
    /* Иначе на телефоне при нажатии весь текст на миг заливало синей
       подсветкой браузера по умолчанию — выглядело как визуальный глюк */
    -webkit-tap-highlight-color: transparent;
}
.expand-btn:hover { text-decoration: underline; }
/* Текст физически не обрезан (уместился в отведённые 3 строки, часто
   бывает на широком экране или при коротковатом тексте чуть за порогом
   в 110 символов) — тогда кнопка "Развернуть" не нужна и только сбивает
   с толку: клик по ней ничего не меняет, ведь скрывать нечего. Прячем её
   через JS после отрисовки (см. header.php) сравнением scrollHeight. */
.expand-wrap.no-clip .expand-btn { display: none; }

/* Аватар/фото — клик открывает выбор файла */
.avatar-edit {
    position: relative;
    display: inline-block;
    cursor: pointer;
    border-radius: 50%;
}
.avatar-edit input[type=file] {
    position: absolute;
    inset: 0;
    opacity: 0;
    width: 100%;
    height: 100%;
    cursor: pointer;
}
.avatar-edit-overlay {
    position: absolute;
    inset: 0;
    display: flex;
    align-items: center;
    justify-content: center;
    background: rgba(15, 23, 42, 0.55);
    color: #fff;
    font-size: 10px;
    font-weight: 700;
    border-radius: 50%;
    opacity: 0;
    transition: opacity .15s ease;
    text-align: center;
    pointer-events: none;
}
.avatar-edit:hover .avatar-edit-overlay { opacity: 1; }
/* Значок фотоаппарата в углу аватарки ученика.
   Тот же кружок, что в профиле репетитора (.prof-ava-edit), но здесь он
   лежит ВНУТРИ <label>, а не отдельной формой рядом, — поэтому позицию
   задаём тут. pointer-events отключены: нажатие всё равно должно попасть
   в label, иначе значок перехватывал бы клик и файл не выбирался. */
.avatar-edit .prof-ava-edit {
    position: absolute;
    right: -1px; bottom: -1px;
    pointer-events: none;
}
/* Кружок с инициалами вместо фотографии — того же размера, что и фото:
   иначе при загрузке снимка аватарка скачком вырастала с 44 до 64. */
.avatar-64 { width: 64px; height: 64px; font-size: 21px; }
.avatar-photo {
    width: 64px;
    height: 64px;
    border-radius: 50%;
    object-fit: cover;
    display: block;
    border: 1px solid var(--border);
    flex-shrink: 0;
}
.avatar-photo-sm { width: 34px; height: 34px; }

/* Курсы Moodle — несколько строк на профиле */
.moodle-course-row { padding: 10px 0; border-top: 1px solid var(--border); }
.moodle-course-row:first-of-type { border-top: none; }
.moodle-course-label { font-weight: 700; font-size: 13px; color: var(--text); margin-bottom: 4px; }

/* Контейнер таблицы — на мобильном превращается в карточки */
.table-scroll { width: 100%; }

/* ===== Форма проверки работы =====
   Балл, файл и отзыв — разными строками с подписями, а не одной тесной
   лентой полей: в ленте было неясно, что куда вводить, а на телефоне она
   разъезжалась. */
.grade-form {
    margin-top: 12px;
    padding-top: 12px;
    border-top: 1px solid var(--border);
    display: flex;
    flex-direction: column;
    gap: 10px;
}
.grade-form label { font-size: 12.5px; font-weight: 600; color: var(--muted); }
.grade-form input[type=text],
.grade-form textarea {
    display: block;
    width: 100%;
    margin-top: 4px;
    padding: 9px 11px;
    border: 1px solid var(--border);
    border-radius: var(--radius-xs);
    background: var(--surface);
    color: var(--text);
    font: inherit;
    font-size: 14px;
}
.grade-form textarea { resize: vertical; min-height: 62px; line-height: 1.45; }
.grade-form input[type=file] { display: block; width: 100%; margin-top: 6px; font-size: 12px; max-width: 100%; }
.grade-form-row { display: flex; gap: 12px; flex-wrap: wrap; }
.grade-form-score { flex: 0 0 150px; }
.grade-form-file { flex: 1 1 220px; min-width: 0; }
.grade-form-actions { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; }
.grade-form-danger { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; margin-top: 10px; }

@media (max-width: 700px) {
    .grade-form-score, .grade-form-file { flex: 1 1 100%; }
    .grade-form-actions .btn, .grade-form-danger .btn { width: 100%; }
}

/* ===== Список оценок (кабинет ученика) =====
   Намеренно НЕ таблица: у таблицы на телефоне либо разъезжаются колонки,
   либо каждая строка превращается в отдельную карточку с рамкой — а тут
   строк много и они короткие. Здесь простой список «название слева, балл
   справа», который одинаково читается на любой ширине.

   Уровни, и у каждого своё оформление — раньше все они были одним и тем
   же белым цветом и сливались в одну простыню:
     0) .grade-hero    — ОБЩИЙ итог, залитая фирменным цветом полоса сверху;
     1) .grade-section — ОДНА группа (раздел Moodle / один месяц) — обычная
                         карточка, но с тонированной шапкой — видно, где
                         начинается и кончается группа;
     2) .grade-row      — ОТДЕЛЬНАЯ оценка внутри группы — без своего фона,
                         просто строка списка, самый тихий уровень. */
.grade-hero {
    display: flex;
    align-items: center;
    gap: 8px 16px;
    flex-wrap: wrap;
    background: var(--accent-bg);
    border: 1px solid var(--accent);
    border-radius: var(--radius-xs);
    padding: 10px 16px;
    margin-bottom: 14px;
}
.grade-hero-main { display: flex; align-items: baseline; gap: 8px; flex-wrap: wrap; min-width: 0; }
.grade-hero-label {
    font-size: 10.5px;
    font-weight: 800;
    color: var(--accent-dark);
    text-transform: uppercase;
    letter-spacing: .04em;
    opacity: .85;
    white-space: nowrap;
}
.grade-hero-value {
    font-size: 16px;
    font-weight: 800;
    color: var(--accent-dark);
    letter-spacing: -0.01em;
    line-height: 1.2;
    overflow-wrap: anywhere;
}
.grade-hero-side { display: flex; gap: 16px; flex-wrap: wrap; margin-left: auto; }
.grade-hero-stat { display: flex; align-items: baseline; gap: 5px; white-space: nowrap; }
.grade-hero-stat b { font-size: 14px; font-weight: 800; color: var(--accent-dark); }

.grade-section {
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--radius-sm);
    box-shadow: var(--shadow);
    margin-bottom: 12px;
    overflow: hidden;
}
.grade-section-head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 10px;
    flex-wrap: wrap;
    padding: 12px 14px;
    background: var(--surface-2);
    border-bottom: 1px solid var(--border);
}
.grade-section-name { font-size: 14.5px; font-weight: 700; color: var(--text); }
.grade-section-marks { display: flex; gap: 8px; flex-wrap: wrap; align-items: center; justify-content: flex-end; }

/* Подытог РАЗДЕЛА — сознательно другой стиль, чем у заливной плашки
   оценки за отдельное задание ниже: одинаковый процент (например "10 из
   10" у раздела и у единственного задания внутри) давал два одинаковых
   зелёных пятна подряд, было непонятно, где итог, а где просто первое
   задание списка. Здесь — контурная плашка (цветная рамка, без заливки):
   тот же цвет говорит "какой уровень оценки", а другая ФОРМА читается
   как "это итог", а не повтор. Точку-маркер перед числом убрали — она
   выглядела лишним значком, а не частью числа. Размер и отступы — те же,
   что у вторичного балла рядом, чтобы они смотрелись парой, а не "один
   большой жирный, другой едва заметный". */
.grade-tone {
    display: inline-flex;
    align-items: center;
    font-weight: 700;
    font-size: 12.5px;
    letter-spacing: -.005em;
    padding: 3px 10px;
    border-radius: 999px;
    border: 1.5px solid currentColor;
    background: none;
    white-space: nowrap;
}
.grade-tone.badge-grade-5 { color: var(--green-dark); }
.grade-tone.badge-grade-4 { color: var(--green); }
.grade-tone.badge-grade-3 { color: var(--orange); }
.grade-tone.badge-grade-2 { color: var(--red); }
.grade-tone.badge-grade-n,
.grade-tone.badge-grade-none,
.grade-tone.badge-blue-outline { color: var(--muted); }
/* Вторичный балл рядом с подытогом — та же плашка, но ЗАЛИТАЯ фирменным
   цветом, а не контурная: разница видна сразу (контур/заливка), а
   размер — специально один в один с .grade-tone, чтобы не получалось
   "один огромный, другой крошечный" */
.grade-secondary-chip {
    display: inline-flex;
    align-items: center;
    gap: 4px;
    font-size: 12.5px;
    font-weight: 700;
    letter-spacing: -.005em;
    color: var(--accent-dark);
    background: var(--accent-bg);
    padding: 3px 10px;
    border-radius: 999px;
    white-space: nowrap;
}
.grade-secondary-chip .ui-icon { width: .9em; height: .9em; }

.grade-list { display: flex; flex-direction: column; padding: 0 14px; }
.grade-row {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 12px;
    padding: 10px 0;
    border-bottom: 1px solid var(--border);
}
.grade-row:last-child { border-bottom: none; }
.grade-row-main { min-width: 0; flex: 1 1 auto; }
.grade-row-title { font-size: 13.5px; color: var(--text); overflow-wrap: anywhere; }
.grade-row-sub { font-size: 12px; color: var(--muted); margin-top: 2px; }
.grade-row .badge { flex-shrink: 0; }

/* Список без карточки-группы вокруг (не используется сейчас — оценки за
   занятия тоже сгруппированы по месяцам через .grade-section, — но
   оставлен для одиночного списка без заголовка). */
.grade-list-card {
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--radius-sm);
    box-shadow: var(--shadow);
    padding: 2px 14px;
}

@media (max-width: 700px) {
    .grade-hero { padding: 9px 14px; }
    .grade-hero-side { width: 100%; margin-left: 0; justify-content: space-between; }
    /* Оценки специально ОСТАЮТСЯ справа и на телефоне (не переносятся под
       название) — так же, как на компьютере: это работало нормально, а
       перенос вниз только портил вид. Название и так само перетечёт на
       вторую строку, если не помещается — переносить нужно было не его. */
    .grade-section-head { padding: 10px 12px; gap: 6px 10px; }
    .grade-section-name { font-size: 14px; }
    .grade-list { padding: 0 12px; }
    .grade-row { padding: 9px 0; }
    .grade-row-title { font-size: 13px; }
}

/* ===== Разбор теста по вопросам (admin/moodle_work.php) =====
   Вопрос — карточкой на всю ширину, а не в узкой ячейке таблицы: у
   вопросов по русскому языку сам текст часто держится на форматировании
   (таблица, несколько абзацев, выделения жирным/курсивом), а втиснутое в
   колонку сравнения оно превращалось в нечитаемую кашу. Формат внутри —
   тот же безопасно пересобранный HTML, что и в Moodle (см.
   moodle_extract_question_html()), поэтому стили ниже — на голые теги
   (p/table/ul), а не на классы: разметка приходит без классов. */
.quiz-review-list { display: flex; flex-direction: column; gap: 10px; margin-top: 10px; }
.quiz-review-item {
    background: var(--surface-2);
    border: 1px solid var(--border);
    border-radius: var(--radius-sm);
    padding: 12px 14px;
}
.quiz-review-label { font-size: 12px; font-weight: 800; color: var(--muted); margin-bottom: 6px; }
.quiz-review-question {
    font-size: 13.5px;
    line-height: 1.55;
    color: var(--text);
}
.quiz-review-question p { margin: 0 0 8px; }
.quiz-review-question p:last-child { margin-bottom: 0; }
.quiz-review-question ul, .quiz-review-question ol { margin: 0 0 8px; padding-left: 20px; }
.quiz-review-question table {
    border-collapse: collapse;
    margin: 6px 0 8px;
    max-width: 100%;
    display: block;
    overflow-x: auto; /* таблица из вопроса шире экрана — прокручивается внутри себя, а не ломает страницу */
}
.quiz-review-question td, .quiz-review-question th {
    border: 1px solid var(--border);
    padding: 5px 9px;
    font-size: 13px;
}
.quiz-review-answers {
    display: flex;
    flex-direction: column;
    gap: 4px;
    margin-top: 8px;
    padding-top: 8px;
    border-top: 1px dashed var(--border);
    font-size: 13px;
}
.quiz-review-answer-label { font-weight: 700; color: var(--muted); }

/* Карта попытки: клетка на вопрос, цвет = результат. Нужна, чтобы всю
   картину было видно одним взглядом, до чтения самих вопросов; клик —
   прыжок к вопросу в полном разборе. */
.quiz-map { display: flex; flex-wrap: wrap; gap: 5px; margin: 10px 0 2px; }
.quiz-map-cell {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-width: 30px;
    height: 30px;
    padding: 0 6px;
    border-radius: var(--radius-xs);
    border: 1px solid transparent;
    font-size: 12.5px;
    font-weight: 700;
    text-decoration: none;
}
.quiz-map-cell:hover { text-decoration: none; filter: brightness(.96); }
.quiz-map-cell.is-right   { background: var(--green-bg);  color: var(--green); }
.quiz-map-cell.is-partial { background: var(--orange-bg); color: var(--orange); }
.quiz-map-cell.is-wrong   { background: var(--red-bg);    color: var(--red); }

/* Цветная полоса слева у карточки вопроса — тот же язык цвета, что и в
   карте выше, чтобы при прокрутке не терять, о каком результате речь. */
.quiz-review-item.is-right   { border-left: 3px solid var(--green); }
.quiz-review-item.is-partial { border-left: 3px solid var(--orange); }
.quiz-review-item.is-wrong   { border-left: 3px solid var(--red); }
.quiz-review-label { display: flex; align-items: center; justify-content: space-between; gap: 10px; }
.quiz-status-tag {
    font-size: 11px;
    font-weight: 700;
    text-transform: lowercase;
    padding: 2px 8px;
    border-radius: 999px;
    flex-shrink: 0;
}
.quiz-status-tag.is-right   { background: var(--green-bg);  color: var(--green); }
.quiz-status-tag.is-partial { background: var(--orange-bg); color: var(--orange); }
.quiz-status-tag.is-wrong   { background: var(--red-bg);    color: var(--red); }

/* ===== Сроки у самого задания =====
   Свёрнутая карточка: главное (когда закрывается) видно сразу в заголовке,
   настройка раскрывается по клику и не занимает экран, пока не нужна. */
.deadline-card {
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    box-shadow: var(--shadow);
    margin-bottom: 16px;
}
.deadline-card > summary {
    cursor: pointer;
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: 10px;
    padding: 12px 14px;
    font-size: 13.5px;
}
.deadline-summary-title { font-weight: 700; color: var(--text); }
.deadline-summary-value { color: var(--accent-dark); font-weight: 600; }
.deadline-summary-value.is-past { color: var(--muted); }
.deadline-body { padding: 0 14px 14px; }
.deadline-base {
    font-size: 13px;
    color: var(--muted);
    line-height: 1.5;
    padding-bottom: 10px;
    margin-bottom: 12px;
    border-bottom: 1px solid var(--border);
}
.deadline-base b { color: var(--text); }
.deadline-hint { font-size: 12.5px; color: var(--muted); margin: 0 0 10px; }
.deadline-fields { display: flex; flex-wrap: wrap; gap: 14px; margin-bottom: 12px; }
.deadline-fields label { font-size: 12.5px; font-weight: 600; color: var(--muted); }
.deadline-fields input[type=datetime-local] {
    display: block;
    margin-top: 4px;
    padding: 8px 10px;
    border: 1px solid var(--border);
    border-radius: var(--radius-xs);
    background: var(--surface);
    color: var(--text);
    font: inherit;
    font-size: 13.5px;
    max-width: 100%;
}
.deadline-students {
    display: flex;
    flex-direction: column;
    gap: 2px;
    margin-bottom: 12px;
    max-height: 260px;
    overflow-y: auto;
}
.deadline-all, .deadline-student {
    display: flex;
    align-items: center;
    gap: 8px;
    font-size: 13.5px;
    padding: 5px 2px;
    cursor: pointer;
}
.deadline-all { font-weight: 700; border-bottom: 1px solid var(--border); margin-bottom: 4px; padding-bottom: 8px; }
.deadline-list { margin-top: 12px; border-top: 1px solid var(--border); padding-top: 10px; }
.deadline-row {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 10px;
    font-size: 13px;
    padding: 5px 0;
}

.deadline-scope { display: flex; flex-wrap: wrap; gap: 16px; margin-bottom: 12px; font-size: 13.5px; }
.deadline-scope label { display: flex; align-items: center; gap: 7px; cursor: pointer; }
.deadline-fields input[type=text] {
    display: block;
    margin-top: 4px;
    padding: 8px 10px;
    border: 1px solid var(--border);
    border-radius: var(--radius-xs);
    background: var(--surface);
    color: var(--text);
    font: inherit;
    font-size: 13.5px;
    min-width: 220px;
    max-width: 100%;
}

/* Уже оставленный отзыв преподавателя — визуально отделён от работы
   ученика: это две разные вещи, и путать их нельзя. */
.feedback-box {
    margin-top: 10px;
    padding: 10px 12px;
    background: var(--accent-bg);
    border: 1px solid var(--accent);
    border-radius: var(--radius-sm);
    font-size: 13.5px;
}
.feedback-box-title { font-size: 11.5px; font-weight: 700; color: var(--accent-dark); margin-bottom: 6px; }

/* Ближайшие дедлайны на «Сегодня» — по разделам курса, сверху вниз строго
   по дате. Прошлая попытка группировки не прижилась ровно потому, что
   теряла этот порядок; теперь раздел встаёт на место своего САМОГО
   раннего срока, поэтому «раньше — выше» сохраняется и для разделов, и
   для работ внутри. */
.deadline-panel { margin-top: 4px; }
.deadline-feed { display: flex; flex-direction: column; gap: 2px; }
.deadline-feed-row {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 12px;
    padding: 9px 2px;
    border-bottom: 1px solid var(--border);
    font-size: 13.5px;
}
.deadline-feed-row:last-child { border-bottom: none; }
.deadline-feed-name { font-weight: 600; color: var(--text); }
.deadline-feed-sub { font-size: 12px; color: var(--muted); }
/* Контурная плашка — тот же приём, что и .grade-tone на «Оценках»: рамка
   цветом смысла, без заливки. Колонка фиксированной ширины и центрирует
   плашку внутри себя (не жмёт её к правому краю) — иначе у короткого
   «завтра» и длинного «через 3 дня, 14.09» ровным был бы только один
   край, а другой — рваным. */
.deadline-feed-when {
    flex-shrink: 0;
    width: 158px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    padding: 3px 10px;
    border-radius: 999px;
    border: 1.5px solid var(--border);
    font-size: 12.5px;
    font-weight: 700;
    color: var(--muted);
    white-space: nowrap;
}
.deadline-feed-when.is-soon { border-color: var(--orange); color: var(--orange); }
.deadline-feed-when.is-past { border-color: var(--red); color: var(--red); }

/* Раздел курса как свёрнутая группа сроков: сверху строка «Курс · Раздел»
   со счётчиком и ближайшей датой, внутри — сами работы. Свёрнутое
   состояние — обычное; само раскрывается только то, где уже просрочено. */
.deadline-group { border-bottom: 1px solid var(--border); }
.deadline-group:last-child { border-bottom: none; }
.deadline-group-head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 12px;
    padding: 9px 2px;
    cursor: pointer;
    list-style: none;
    font-size: 13.5px;
}
/* Стрелка по умолчанию у <summary> в разных браузерах выглядит по-разному
   и ломает выравнивание — рисуем свою, слева, и поворачиваем при открытии. */
.deadline-group-head::-webkit-details-marker { display: none; }
.deadline-group-head::before {
    content: '';
    flex-shrink: 0;
    width: 0; height: 0;
    margin-right: 2px;
    border-left: 5px solid var(--muted);
    border-top: 4px solid transparent;
    border-bottom: 4px solid transparent;
    transition: transform .15s ease;
}
.deadline-group[open] > .deadline-group-head::before { transform: rotate(90deg); }
.deadline-group-head:hover { background: var(--surface-2); border-radius: 8px; }
.deadline-group-title {
    font-weight: 700;
    color: var(--text);
    display: flex;
    align-items: center;
    gap: 6px;
    min-width: 0;
    flex: 1 1 auto;
}
.deadline-group-count {
    flex-shrink: 0;
    min-width: 18px;
    padding: 0 5px;
    border-radius: 999px;
    background: var(--surface-2);
    color: var(--muted);
    font-size: 11px;
    font-weight: 700;
    text-align: center;
}
.deadline-group-head:hover .deadline-group-count { background: var(--surface); }
/* Работы внутри — с отступом слева, чтобы было видно, что они «в разделе» */
.deadline-group-body { padding-left: 14px; }
.deadline-group-body .deadline-feed-row:last-child { border-bottom: none; }
/* Раздел из одной работы: заголовок — просто подпись, разворачивать нечего */
.deadline-group-single > .deadline-group-title {
    padding: 8px 2px 0;
    font-size: 12px;
    font-weight: 600;
    color: var(--muted);
}
.deadline-group-single > .deadline-feed-row { border-bottom: none; padding-top: 2px; }

/* Сводка «остальное скрыто» под списком блока «Сегодня» */
.today-note { font-size: 12px; margin: 8px 2px 0; }

@media (max-width: 700px) {
    /* Раньше строка просто "переносилась" (flex-wrap), а куда денется
       плашка — решала длина конкретного названия: короткое оставляло ей
       место сбоку, длинное вытесняло вниз. На одном экране половина строк
       оказывалась с плашкой справа, половина — снизу, без всякой системы.
       Разница глазами читалась как поломка, хотя каждая строка сама по
       себе была правильной. Теперь строка ВСЕГДА в столбец — плашка
       всегда на своей строке, у всех работ одинаково, независимо от
       длины названия. */
    .deadline-feed-row { flex-direction: column; align-items: flex-start; gap: 4px; }
    .deadline-feed-when { width: auto; align-self: flex-start; }
}

/* Кнопка сохранения срока и галочка «сообщить ученикам» — в одну строку:
   решение «писать или не писать» принимается ровно в момент сохранения,
   и прятать его в другой конец формы было бы неудобно. */
.deadline-actions { display: flex; align-items: center; flex-wrap: wrap; gap: 14px; }
.deadline-notify {
    display: flex;
    align-items: center;
    gap: 7px;
    font-size: 13px;
    font-weight: 600;
    color: var(--muted);
    cursor: pointer;
    margin: 0;
}
.deadline-clear-form { margin: 10px 0 0; }

/* ===== Дедлайны ===== */
/* Название курса над группой разделов на странице ученика */
.dl-course {
    font-size: 12px;
    font-weight: 800;
    color: var(--muted);
    text-transform: uppercase;
    letter-spacing: .08em;
    margin: 26px 0 10px;
}
.dl-course:first-of-type { margin-top: 2px; }

/* Карточка раздела — тот же язык, что и «Оценки» (.grade-section):
   тонированная шапка отделяет группу от белого списка строк. Срочность
   несёт ЦВЕТ ТЕКСТА, а не заливка фона или плашка — заметно на нужных
   строках, но не превращает список в цветное полотно, когда просроченного
   вдруг оказывается несколько подряд. */
.dlc {
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--radius-sm);
    box-shadow: var(--shadow);
    margin-bottom: 10px;
    overflow: hidden;
}
.dlc-head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 8px 14px;
    flex-wrap: wrap;
    padding: 12px 14px;
    background: var(--surface-2);
    border-bottom: 1px solid var(--border);
}
.dlc-title { display: flex; align-items: baseline; gap: 8px; flex-wrap: wrap; min-width: 0; }
.dlc-name { font-size: 14.5px; font-weight: 700; color: var(--text); }
/* «1 из 2» — сколько частей раздела уже сдано. Простым текстом, не
   плашкой: это подпись к названию, а не отдельный статус. */
.dlc-progress { font-size: 12px; color: var(--muted); white-space: nowrap; }
.dlc-course-tag { font-size: 11.5px; color: var(--muted); }

/* «Когда» в шапке раздела — контурная плашка, тот же приём, что и
   .grade-tone на «Оценках»: рамка цветом смысла, без заливки, единый
   стиль с остальным сайтом. Текст собран ОДНОЙ строкой в PHP
   (deadline_when_text()), а не разведён на два элемента с CSS-отступом
   между ними: единственный способ гарантированно не потерять пробел ни
   при какой раскладке — не давать браузеру возможности их развести. */
.dlc-when {
    flex-shrink: 0;
    display: inline-flex;
    align-items: center;
    padding: 3px 10px;
    border-radius: 999px;
    border: 1.5px solid var(--border);
    font-size: 12.5px;
    font-weight: 700;
    color: var(--muted);
    white-space: nowrap;
}
.dlc-when.is-overdue { border-color: var(--red); color: var(--red); }
.dlc-when.is-today,
.dlc-when.is-soon    { border-color: var(--orange); color: var(--orange); }
.dlc-when.is-done    { border-color: var(--green); color: var(--green-dark); }

.dlc-items { list-style: none; margin: 0; padding: 0 14px; }
.dlc-item {
    display: flex;
    align-items: center;
    gap: 9px;
    padding: 9px 0;
    border-top: 1px solid var(--border);
    font-size: 13.5px;
}
.dlc-item:first-child { border-top: none; }

/* Галочка — маленькая, без кружка вокруг: просто зелёная отметка перед
   сданным. У несданного на её месте пусто, а не контур-заглушка — раз уж
   от неё явно решили отказаться, лучше не рисовать её вовсе, чем рисовать
   плохо. Ширина всё равно зарезервирована (min-width), чтобы названия
   работ в списке начинались с одной вертикальной линии, а не прыгали. */
.dlc-check {
    flex-shrink: 0;
    min-width: 16px;
    display: inline-flex;
    color: var(--green);
}
.dlc-check .ui-icon { width: 15px; height: 15px; stroke-width: 3; }

.dlc-item-name { flex: 1 1 auto; min-width: 0; overflow-wrap: anywhere; }
/* Сдано — приглушённый цвет и зачёркнутое название, как в любом чек-листе.
   Без заливки фона: полосу такой ширины (во всю строку) телефон не всегда
   укладывал ровно в границы карточки, да и выглядела она тяжелее, чем
   нужно для одной выполненной строчки в списке. */
.dlc-item.is-done .dlc-item-name { color: var(--muted); text-decoration: line-through; text-decoration-color: var(--border); }
.dlc-tag {
    display: inline-block;
    font-size: 10.5px;
    font-weight: 800;
    color: var(--accent);
    text-transform: uppercase;
    letter-spacing: .03em;
    margin-left: 6px;
}
.dlc-note { font-size: 11.5px; color: var(--muted); margin-left: 6px; }
/* Та же контурная плашка, что и в шапке (.dlc-when) — только чуть мельче:
   это уже частность (своя дата у одной работы), а не главный статус
   раздела. */
.dlc-item-when {
    flex-shrink: 0;
    display: inline-flex;
    align-items: center;
    padding: 2px 9px;
    border-radius: 999px;
    border: 1.5px solid var(--border);
    font-size: 11.5px;
    font-weight: 700;
    color: var(--muted);
    white-space: nowrap;
}
.dlc-item-when.is-overdue { border-color: var(--red); color: var(--red); }
.dlc-item-when.is-today,
.dlc-item-when.is-soon    { border-color: var(--orange); color: var(--orange); }
.dlc-item-when.is-done    { border-color: var(--green); color: var(--green-dark); }

.dlc.is-archived { opacity: .8; }

/* «Уже сдано» — свой треугольник вместо системного маркера <details>:
   стандартный «▸» в этом месте выглядел чужеродно среди карточек. */
.dl-done-wrap { margin-top: 26px; }
.dl-done-wrap > summary {
    display: flex;
    align-items: center;
    gap: 8px;
    cursor: pointer;
    list-style: none;
    font-size: 12px;
    font-weight: 800;
    text-transform: uppercase;
    letter-spacing: .08em;
    color: var(--muted);
    padding: 6px 2px;
    margin-bottom: 10px;
}
.dl-done-wrap > summary::-webkit-details-marker { display: none; }
.dl-done-chevron {
    width: 7px;
    height: 7px;
    border: solid var(--muted);
    border-width: 0 1.6px 1.6px 0;
    transform: rotate(-45deg);
    transition: transform .15s ease;
}
.dl-done-wrap[open] > summary .dl-done-chevron { transform: rotate(45deg); }
.dl-done-count {
    font-size: 11px;
    font-weight: 800;
    color: var(--muted);
    background: var(--surface-2);
    border-radius: 999px;
    padding: 2px 8px;
    letter-spacing: 0;
}

/* Работы внутри раздела на вкладке «Дедлайны» в админке.
   Колонки фиксированной ширины, а не «как получится»: названия работ
   разной длины, и при выравнивании по содержимому даты прыгали бы по
   строке туда-сюда — глазами такой столбец не читается. */
.dl-items { margin-top: 12px; border-top: 1px solid var(--border); padding-top: 8px; }
.dl-item {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 7px 0;
    font-size: 13px;
    border-bottom: 1px solid var(--border);
}
.dl-item:last-child { border-bottom: none; }
.dl-item-name { min-width: 0; flex: 1 1 auto; display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.dl-item-when {
    flex: 0 0 auto;
    width: 190px;
    display: flex;
    align-items: center;
    gap: 6px;
}
.dl-item-act { flex: 0 0 auto; width: 118px; display: flex; gap: 4px; justify-content: flex-end; }
.dl-inline-form { margin: 0; display: inline-flex; }
.dl-left { font-size: 12.5px; font-weight: 700; white-space: nowrap; }
.dl-left.is-overdue { color: var(--red); }
.dl-left.is-today   { color: var(--orange); }
.dl-left.is-soon    { color: var(--orange); }
.dl-left.is-ok      { color: var(--text); }

@media (max-width: 700px) {
    .deadline-fields { flex-direction: column; gap: 10px; }
    .deadline-fields input[type=datetime-local] { width: 100%; }
    .deadline-row { flex-direction: column; align-items: flex-start; gap: 4px; }
    /* На узком экране фиксированные колонки не помещаются — название
       занимает строку целиком, дата и кнопки уходят под него */
    .dl-item { flex-wrap: wrap; }
    .dl-item-name { width: 100%; flex: 1 1 100%; }
    .dl-item-when { width: auto; flex: 1 1 auto; }
    .dl-item-act { width: auto; }

    /* Название раздела и плашка срока в шапке в одну строку не помещаются
       вместе — плашка сама переносится на свою строку (обычный перенос
       flex-wrap), пилюлей, а не растянутой на всю ширину полосой. */
    .dlc-head { flex-direction: column; align-items: flex-start; gap: 6px; padding: 11px 13px; }
    .dlc-name { font-size: 14.5px; }
    .dlc-items { padding: 0 13px; }
    .dlc-item { font-size: 13px; gap: 8px; padding: 9px 0; }
    .dlc-check { min-width: 15px; }
    .dlc-check .ui-icon { width: 14px; height: 14px; }
    /* Плашка срока работы — тоже переносится своей строкой, если рядом с
       названием не помещается, а не сбоку ломается пополам */
    .dlc-item { flex-wrap: wrap; }
    .dlc-item-name { flex: 1 1 auto; }
}

.quiz-full { margin-top: 12px; }
.quiz-full > summary {
    cursor: pointer;
    font-size: 13px;
    font-weight: 600;
    color: var(--accent);
    padding: 8px 2px;
}
.quiz-full > summary:hover { color: var(--accent-dark); }

/* Значки в тексте и в бейджах (ui_icon() в includes/functions.php) —
   вместо смайлов: наследуют цвет текста и масштабируются вместе с ним,
   поэтому одинаково выглядят в любой системе и в любой теме. */
.ui-icon {
    width: 1em;
    height: 1em;
    flex-shrink: 0;
    vertical-align: -0.13em;
}
.badge .ui-icon { margin-right: 4px; }

/* ===== Матрица оценок Moodle (ученики × разделы или ученики × задания) =====
   Обычные таблицы на телефоне разворачиваются в карточки (см. медиазапрос
   ниже), но матрице это противопоказано: весь её смысл — сравнивать
   учеников между собой построчно. Поэтому у неё свой класс: она всегда
   остаётся таблицей и прокручивается вбок, а колонка с именем
   примагничена к левому краю, чтобы при прокрутке было видно, чья строка. */
.matrix-scroll {
    width: 100%;
    overflow-x: auto;
    -webkit-overflow-scrolling: touch;
    border: 1px solid var(--border);
    border-radius: 14px;
    background: var(--surface);
}
.grade-matrix {
    border-collapse: separate;
    border-spacing: 0;
    width: auto;
    min-width: 100%;
    font-size: 13px;
}
.grade-matrix th, .grade-matrix td {
    padding: 8px 10px;
    border-bottom: 1px solid var(--border);
    white-space: nowrap;
}
.grade-matrix thead th {
    background: var(--surface-2);
    font-size: 11.5px;
    font-weight: 700;
    color: var(--muted);
    text-align: center;
    vertical-align: bottom;
    max-width: 150px;
    white-space: normal;
    line-height: 1.3;
}
.grade-matrix thead th a { color: var(--muted); }
.grade-matrix tbody td { text-align: center; }
.grade-matrix tbody tr:last-child th,
.grade-matrix tbody tr:last-child td { border-bottom: none; }
.grade-matrix tbody tr:hover td,
.grade-matrix tbody tr:hover th.matrix-sticky { background: var(--accent-bg); }
.matrix-sticky {
    position: sticky;
    left: 0;
    z-index: 2;
    background: var(--surface);
    text-align: left;
    font-weight: 700;
    min-width: 150px;
    max-width: 220px;
    white-space: normal;
    box-shadow: 1px 0 0 var(--border);
}
.grade-matrix thead th.matrix-sticky { z-index: 3; background: var(--surface-2); }
.matrix-parent { display: block; font-weight: 400; font-size: 10px; color: var(--muted); opacity: .8; }
/* Вторичный балл рядом с первичным — намеренно тише основного числа:
   это пересчёт, а не сама оценка */
.matrix-secondary { display: block; font-size: 11px; font-weight: 700; color: var(--accent-dark); margin-top: 2px; }
.matrix-note { display: block; font-weight: 400; font-size: 11px; color: var(--red); }

@media (max-width: 700px) {
    .grade-matrix { font-size: 12px; }
    .grade-matrix th, .grade-matrix td { padding: 6px 8px; }
    .grade-matrix thead th { max-width: 110px; }
    .matrix-sticky { min-width: 118px; max-width: 140px; font-size: 12px; }
}

/* ===== Мобильная адаптация таблиц ===== */
@media (max-width: 700px) {
    .lessons-table { border-spacing: 0; }
    .lessons-table thead, .simple-table thead { display: none; }
    .lessons-table, .lessons-table tbody, .lessons-table tr, .lessons-table td,
    .simple-table, .simple-table tbody, .simple-table tr, .simple-table td { display: block; width: 100%; }
    .lessons-table tr, .simple-table tr {
        border: 1px solid var(--border);
        border-radius: 14px;
        margin-bottom: 8px;
        padding: 10px 12px;
        background: var(--surface);
        box-shadow: var(--shadow);
    }
    /* .cell-check исключён отсюда: вне режима выбора он должен быть скрыт
       (display:none), а это правило иначе перебивало бы скрытие своей
       специфичностью — из-за чего чекбоксы были видны на телефоне всегда,
       а не только при нажатой "Выбрать несколько" */
    .lessons-table td:not(.cell-check), .simple-table td:not(.cell-check) {
        border-top: none;
        padding: 2px 0;
        font-size: 13px;
        min-height: 0;
        line-height: 1.35;
        display: flex;
        justify-content: space-between;
        align-items: center;
        gap: 10px;
        text-align: right;
    }
    .lessons-table tbody td:first-child,
    .lessons-table tbody td:last-child { border-radius: 0; }
    .lessons-table td.topic-cell {
        display: block;
        text-align: left;
        padding: 2px 0 8px;
        margin-bottom: 4px;
        border-bottom: 1px solid var(--border);
    }
    .lessons-table td.topic-cell .topic-line { text-align: left; padding-right: 0; }
    .lessons-table td.topic-cell::before { content: none; }
    .lessons-table td.topic-cell .hw-text,
    .lessons-table td.topic-cell .muted { padding-right: 0; }
    .lessons-table .hw-text { margin-left: 0; text-align: left; max-width: none; }
    .lessons-table td::before, .simple-table td::before {
        content: attr(data-label);
        font-size: 10px;
        font-weight: 700;
        color: var(--muted);
        text-transform: uppercase;
        letter-spacing: .03em;
        text-align: left;
        flex-shrink: 0;
    }
    .lessons-table td[data-label=""]::before,
    .simple-table td[data-label=""]::before,
    .simple-table td:not([data-label])::before { display: none; }
    /* Здесь было 16px (порог, ниже которого iOS зумит страницу при фокусе
       на селекте) — таблетки статусов стали огромными, и Сергей попросил
       вернуть прежний компактный размер. Размер тот же, что на компьютере. */

    /* Компактные таблицы (учебная программа, темы уровня в Библиотеке тем) —
       короткие однострочные пункты, для которых "карточка на каждую строку"
       (общее мобильное правило выше) выглядит слишком тяжеловесно: одна
       строка текста внутри рамки со своими отступами и тенью, повторённая
       N раз подряд. Здесь вместо этого — плоский список с тонким
       разделителем, всё в одну строку, как и на компьютере. */
    .compact-table tr {
        border: none;
        border-radius: 0;
        margin-bottom: 0;
        padding: 9px 2px;
        box-shadow: none;
        background: none;
        border-bottom: 1px solid var(--border);
        display: flex;
        align-items: center;
        gap: 8px;
    }
    .compact-table tbody tr:last-child { border-bottom: none; }
    .compact-table td {
        display: block;
        width: auto;
        padding: 0;
        border-top: none;
        font-size: 13.5px;
    }
    .compact-table td::before { display: none; }
    .compact-table td.drag-handle,
    .compact-table td.cell-check,
    .compact-table td.cell-actions { flex: 0 0 auto; }
    .compact-table td:not(.drag-handle):not(.cell-check):not(.cell-actions) {
        flex: 1 1 auto;
        min-width: 0;
        overflow-wrap: anywhere;
    }
    /* Перетаскивание — только мышью (см. JS): на тач-экране ⠿ всё равно не
       работает, показывать его тут — просто лишняя "плашка" без функции. */
    .compact-table .drag-handle { display: none; }
    .compact-table .plan-edit-row { border-bottom: none; padding-top: 0; }
    .compact-table .plan-edit-row td { width: 100%; }
}

/* ===== Футер с контактами ===== */
.site-footer-contacts {
    text-align: center;
    padding: 22px 0 8px;
    color: var(--muted);
    font-size: 13px;
}
.site-footer-contacts .name { font-weight: 700; color: var(--text); }
.site-footer-contacts .site { margin-top: 2px; font-size: 11.5px; letter-spacing: .04em; }
/* Футер идёт сразу после .site-main — ему нужен тот же отступ снизу под
   нижний таббар (≈60-90px с учётом safe-area), иначе имя/домен прячутся
   под фиксированной панелью, когда страница короче экрана. */
/* Помимо ширины — ещё и по высоте: телефон в альбомной ориентации обычно
   шире 760px, но остаётся именно телефоном (мало места по высоте), а не
   планшетом/десктопом. Без второго условия при повороте телефона нижний
   таббар и весь мобильный вид пропадали, включалась десктопная раскладка,
   для которой на телефоне физически нет места — кнопки/пункты меню
   наезжали друг на друга или обрезались.
   (hover: none) and (pointer: coarse) — обязательно вместе с высотой:
   иначе это же условие ложно срабатывало и на компьютере, если окно
   браузера просто невысокое (не на весь экран) — тогда мобильная
   раскладка меню включалась поверх мышиного интерфейса и всё разъезжалось
   («то густо, то пусто» в отступах). hover/pointer отличают сенсорные
   устройства (телефон, где и нужна альбомная раскладка) от компьютера с
   мышью — на компьютере это условие теперь не срабатывает никогда. */
@media (max-width: 760px), ((max-height: 480px) and (hover: none) and (pointer: coarse)) {
    .site-footer-contacts { padding-bottom: calc(78px + env(safe-area-inset-bottom, 0px)); }
}


/* ===== Выпадающее меню в шапке "Ещё" =====
   Визуал самой кнопки задаёт .nav-link (см. выше) — тут только позиционирование
   и поведение самого раскрывающегося списка. */
.nav-dropdown { position: relative; display: inline-block; }
.nav-dropdown summary { list-style: none; }
.nav-dropdown summary::-webkit-details-marker { display: none; }
.nav-dropdown-menu {
    position: absolute;
    top: calc(100% + 6px);
    right: 0;
    z-index: 56;
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--radius-sm);
    box-shadow: var(--shadow-lift);
    padding: 6px;
    min-width: 220px;
    display: flex;
    flex-direction: column;
    /* Пунктов меню со временем стало много — без этого список обрезался
       краем экрана без возможности пролистать. overscroll-behavior не даёт
       прокрутке "перетечь" на страницу целиком, когда доскроллили до конца
       списка — иначе именно это и закрывало меню (см. JS ниже). */
    max-height: min(70vh, 480px);
    /* dvh — «динамическая» высота вьюпорта, учитывает реальный размер
       видимой области (без адресной строки/после поворота); vh выше —
       фолбэк для браузеров без поддержки dvh, переопределяется этой
       строкой, если она понятна браузеру. */
    max-height: min(70dvh, 480px);
    overflow-y: auto;
    overscroll-behavior: contain;
}
/* Граница смысловой группы пунктов — на самом первом пункте новой группы
   (border-top + отступ), а не отдельным пустым <div>-разделителем между
   пунктами: тот в этом же flex-списке почему-то вызывал непропорционально
   большие "пустые" промежутки в части браузеров — так безопаснее и даёт
   тот же зрительный эффект (см. класс "group-start" в includes/header.php). */
.nav-dropdown-menu a.group-start, .nav-dropdown-theme.group-start {
    border-top: 1px solid var(--border);
    margin-top: 6px;
    padding-top: 14px;
}
/* Подпись смысловой группы. Одних разделительных линий стало мало:
   пунктов в меню под два десятка, и без названий групп список читается
   как сплошная простыня. Подпись некликабельна — она только называет
   группу, поэтому и <span>, а не ссылка. */
.nav-dropdown-group {
    padding: 4px 12px 4px;
    font-size: 10.5px;
    font-weight: 800;
    color: var(--muted);
    text-transform: uppercase;
    letter-spacing: .06em;
    -webkit-user-select: none;
    user-select: none;
}
/* Отделяем группу от предыдущей — но только если перед ней действительно
   что-то было (у самой первой отступ сверху не нужен) */
.nav-dropdown-menu a + .nav-dropdown-group,
.nav-dropdown-menu button + .nav-dropdown-group {
    margin-top: 8px;
    padding-top: 12px;
    border-top: 1px solid var(--border);
}
.nav-dropdown-menu a, .nav-dropdown-theme {
    display: flex;
    align-items: center;
    gap: 9px;
    padding: 10px 12px;
    border-radius: var(--radius-xs);
    font-size: 14px;
    font-weight: 500;
    color: var(--text);
    transition: background .12s ease;
}
.nav-dropdown-menu a:hover, .nav-dropdown-theme:hover { background: var(--surface-2); text-decoration: none; }
.nav-dropdown-menu .nav-icon { width: 17px; height: 17px; flex-shrink: 0; color: var(--muted); }
.nav-dropdown-menu a.danger-link .nav-icon { color: var(--red); }
.nav-dropdown-menu a.is-active { background: var(--accent-bg); color: var(--accent-dark); font-weight: 700; }
.nav-dropdown-menu a.is-active .nav-icon { color: var(--accent); }
.nav-dropdown-theme {
    display: none;
    width: 100%;
    text-align: left;
    background: none;
    border: none;
    font: inherit;
    /* "font: inherit" — шорткод, который заодно сбрасывает font-weight и
       font-size обратно к значениям родителя, перебивая font-weight:500 и
       font-size:14px, заданные чуть выше общим правилом для этого же
       класса (см. ".nav-dropdown-menu a, .nav-dropdown-theme") — из-за
       этого кнопка Push выглядела другим шрифтом, не как остальные пункты
       меню. Возвращаем эти два свойства явно, уже после сброса. */
    font-weight: 500;
    font-size: 14px;
    cursor: pointer;
}
.nav-dropdown-theme .theme-icon-dark { display: none; }
[data-theme="dark"] .nav-dropdown-theme .theme-icon-light { display: none; }
[data-theme="dark"] .nav-dropdown-theme .theme-icon-dark { display: inline; }
.nav-dropdown-menu .danger-link { color: var(--red); }
.nav-dropdown-menu .danger-link:hover { background: var(--red-bg); }

/* ===== Сворачиваемый блок "Подробнее" ===== */
.details-toggle { margin-bottom: 16px; }
.details-toggle summary {
    cursor: pointer;
    list-style: none;
    font-size: 13px;
    font-weight: 600;
    color: var(--accent);
    display: inline-block;
}
.details-toggle summary::-webkit-details-marker { display: none; }
.details-toggle summary::before { content: "+ "; }
.details-toggle[open] summary::before { content: "− "; }

/* ===== Донат посещаемости ===== */
.donut-wrap { display: flex; align-items: center; gap: 12px; }
.donut {
    width: 72px; height: 72px; border-radius: 50%;
    display: flex; align-items: center; justify-content: center;
    flex-shrink: 0;
}
.donut-hole {
    width: 56px; height: 56px; border-radius: 50%;
    background: var(--surface);
    display: flex; align-items: center; justify-content: center;
    flex-direction: column;
}
.donut-hole span { font-weight: 800; font-size: 16px; color: var(--text); letter-spacing: -0.02em; }
.donut-caption { font-size: 12px; color: var(--muted); font-weight: 600; }

/* ===== Компактные плашки статистики (главная) ===== */
.stat-row { display: flex; gap: 8px; flex-wrap: wrap; margin-bottom: 20px; }
.stat-chip {
    display: flex;
    align-items: baseline;
    gap: 7px;
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: 999px;
    padding: 7px 15px;
    font-size: 13px;
    color: var(--muted);
    text-decoration: none;
}
.stat-chip b { font-size: 15px; font-weight: 800; color: var(--text); letter-spacing: -0.02em; }
a.stat-chip:hover { border-color: var(--accent); text-decoration: none; }
.stat-chip.is-warn b { color: var(--red); }
.stat-chip.is-ok b { color: var(--green); }

/* ===== Карточка группы: двухколоночная раскладка (admin/group_lessons.php) =====
   Правая колонка — не пустует: действия, финансы, план, участники. На узких
   экранах сайдбар уходит под основную колонку одной обычной колонкой. */
.group-layout {
    display: grid;
    grid-template-columns: minmax(0, 1fr) 300px;
    gap: 20px;
    align-items: start;
}
.group-main { min-width: 0; }
.group-aside {
    display: flex;
    flex-direction: column;
    gap: 14px;
    position: sticky;
    top: 16px;
}
.group-aside-card {
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--radius-sm);
    padding: 16px 18px;
}
.group-aside-card h3 {
    margin: 0 0 12px;
    font-size: 11.5px;
    text-transform: uppercase;
    letter-spacing: .04em;
    color: var(--muted);
    font-weight: 700;
}
.group-aside-actions { display: flex; flex-direction: column; gap: 8px; }
.group-period-inputs { display: flex; gap: 6px; }
/* font-size: 16px — не косметика: ниже iOS сама включает зум страницы при
   фокусе на поле (это input[type=date], фокусируемое поле). */
.group-period-inputs input { flex: 1 1 0; min-width: 0; padding: 6px 8px; font-size: 16px; border-radius: 8px; border: 1px solid var(--border); background: var(--surface); color: var(--text); }
.group-kv {
    display: flex;
    justify-content: space-between;
    gap: 10px;
    font-size: 13px;
    color: var(--muted);
    padding: 6px 0;
    border-bottom: 1px solid var(--border);
}
.group-kv:last-of-type { border-bottom: none; }
.group-kv b { font-size: 13.5px; font-weight: 700; color: var(--text); }
.group-member-row { padding: 9px 0; border-bottom: 1px solid var(--border); }
.group-member-row:last-child { border-bottom: none; padding-bottom: 0; }
.group-member-name { font-size: 13px; font-weight: 600; }
.group-member-meta { font-size: 11.5px; color: var(--muted); margin-top: 2px; line-height: 1.5; }

@media (max-width: 900px) {
    .group-layout { grid-template-columns: 1fr; }
    .group-aside { position: static; }
}

/* ===== Подсветка строки с невыполненным ДЗ ===== */
.lessons-table tbody tr.row-hw-pending td { background: #fffdf5; }
.lessons-table tbody tr.row-hw-pending td:first-child {
    box-shadow: inset 3px 0 0 var(--accent);
}
/* ===== Подсветка строки с неявкой (санкция) — едва заметная, чтобы не резала глаз ===== */
.lessons-table tbody tr.row-no-show td { background: rgba(220, 38, 38, 0.05); }
.lessons-table tbody tr.row-no-show td:first-child {
    box-shadow: inset 3px 0 0 rgba(220, 38, 38, 0.45);
}
.no-show-note {
    text-align: left;
    font-weight: 700;
    color: var(--red);
    font-size: 13px;
}
.link-btn {
    background: none; border: none; padding: 0; cursor: pointer;
    font: inherit; color: var(--muted); text-decoration: underline;
}
.link-btn:hover { color: var(--accent); }


/* ===== Календарь расписания (месяц, без горизонтальной прокрутки) ===== */
.cal { margin-bottom: 12px; }
.cal-head, .cal-grid {
    display: grid;
    grid-template-columns: repeat(7, minmax(0, 1fr));
    gap: 4px;
}
.cal-head {
    margin-bottom: 4px;
}
.cal-head div {
    text-align: center;
    font-size: 11px;
    font-weight: 700;
    color: var(--muted);
    text-transform: uppercase;
    letter-spacing: .04em;
    padding-bottom: 4px;
}
.cal-day {
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: 12px;
    padding: 5px 5px 6px;
    min-height: 74px;
    position: relative;
}
.cal-out { opacity: .4; }
.cal-past { background: #fafbfc; }
.cal-today { border-color: var(--accent); box-shadow: inset 0 0 0 1px var(--accent); }
.cal-date {
    position: relative;
    display: flex; align-items: center; justify-content: space-between;
    font-size: 11px; font-weight: 700; color: var(--muted); margin-bottom: 4px;
}
.cal-add {
    background: none; border: none; cursor: pointer; padding: 0 2px;
    color: var(--border); font-size: 14px; line-height: 1; font-weight: 700;
}
.cal-day:hover .cal-add { color: var(--accent); }
.cal-today .cal-date { color: var(--accent); }

.cal-slot {
    position: relative;
    display: flex;
    align-items: center;
    gap: 4px;
    font-size: 11px;
    line-height: 1.25;
    padding: 3px 5px;
    border-radius: 8px;
    margin-bottom: 3px;
    border-left: 3px solid transparent;
}
.cal-slot-name { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.cal-slot-time { font-size: 10px; font-weight: 700; opacity: .75; }
.cal-slot.slot-done  { background: var(--green-bg);  border-left-color: var(--green);  color: var(--green-dark); }
.cal-slot.slot-plan  { background: var(--gray-bg);   border-left-color: var(--gray);   color: var(--muted); }
/* "Запланировано", но дата уже прошла — вероятно, забыли отметить.
   Остаётся серым, как обычный план (не хочется ярких цветов), но чуть
   заметнее: более плотная полоса слева и текст чуть темнее нейтрального. */
.cal-slot.slot-plan-overdue {
    background: var(--gray-bg);
    border-left-color: var(--muted);
    border-left-width: 4px;
    color: var(--text);
}
.cal-slot.slot-off   { background: var(--red-bg);    border-left-color: var(--red);    color: var(--red); }
.cal-slot.slot-moved { background: var(--orange-bg); border-left-color: var(--orange); color: var(--orange); }
.cal-slot.slot-moved .cal-slot-name { text-decoration: line-through; opacity: .8; }
/* Неявка — не то же самое, что отмена (тут санкция, ученик был должен
   прийти и не пришёл), поэтому цвет отдельный, не путаем с обычным "off" */
.cal-slot.slot-no_show { background: var(--red-bg); border-left-color: var(--red); color: var(--red); }
.cal-slot.slot-no_show .cal-slot-name::after { content: " ⚠"; }
.cal-slot.slot-movedin { background: var(--accent-bg); border-left-color: var(--accent); color: var(--accent-dark); }
.cal-slot.slot-movedin .cal-slot-name::before { content: "→ "; font-weight: 700; }
.cal-slot-group { color: var(--accent); font-weight: 900; flex-shrink: 0; }

.cal-slot-menu {
    background: none; border: none; padding: 0 2px; cursor: pointer;
    color: var(--muted); font-size: 13px; line-height: 1; flex-shrink: 0;
}
.cal-slot-menu:hover { color: var(--accent); }

/* Всплывающая форма — поверх сетки, не растягивает ячейку */
.cal-pop {
    display: none;
    position: absolute;
    top: 100%;
    left: 0;
    z-index: 40;
    width: 190px;
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: 12px;
    box-shadow: 0 6px 20px rgba(16,24,40,.16);
    padding: 8px;
}
.cal-pop.open { display: block; }
.cal-pop-title { font-size: 12px; font-weight: 700; color: var(--text); margin-bottom: 4px; }
.cal-pop-note { font-size: 11px; color: var(--muted); margin-bottom: 6px; line-height: 1.35; }
.cal-pop hr {
    border: none;
    border-top: 1px solid var(--border);
    margin: 9px 0;
}
.cal-pop-label {
    font-size: 10px;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: .04em;
    color: var(--muted);
    margin-bottom: 5px;
}
.cal-day:nth-child(7n) .cal-pop,
.cal-day:nth-child(7n-1) .cal-pop { left: auto; right: 0; }
.cal-pop select, .cal-pop input {
    display: block; width: 100%; margin: 0 0 6px;
    padding: 5px 7px; font-size: 11.5px; border-radius: 8px;
    border: 1px solid var(--border); background: var(--surface); color: var(--text);
}

/* Триггер окна "причина" (см. reason_field_html() в admin/schedule.php) —
   маленькая ссылка внутри тесного 190px попапа, не полноразмерная кнопка. */
.reason-link {
    display: block; width: 100%; margin: 0 0 6px; padding: 0;
    background: none; border: none; text-align: left;
    font-size: 11px; line-height: 1.3; color: var(--accent);
    text-decoration: underline; text-underline-offset: 2px; cursor: pointer;
}
.reason-link.is-set { color: var(--text); font-weight: 600; text-decoration: none; }

.reason-dialog {
    border: none; padding: 20px; width: min(380px, 92vw);
    background: var(--surface); color: var(--text);
    border-radius: var(--radius-sm); box-shadow: var(--shadow-lift);
}
.reason-dialog::backdrop { background: rgba(15, 21, 32, .45); }
.reason-dialog h3 { margin: 0 0 12px; font-size: 15px; }
.reason-dialog textarea {
    width: 100%; box-sizing: border-box; min-height: 92px; resize: vertical;
    padding: 9px 11px; font: inherit; font-size: 13.5px;
    border: 1px solid var(--border); border-radius: var(--radius-xs);
    background: var(--bg); color: var(--text); margin-bottom: 14px;
}
.reason-dialog-actions { display: flex; justify-content: flex-end; gap: 8px; }

.cal-legend { display: flex; gap: 14px; flex-wrap: wrap; font-size: 11.5px; color: var(--muted); margin-bottom: 18px; }
.cal-legend .lg { display: inline-block; width: 10px; height: 10px; border-radius: 3px; margin-right: 4px; vertical-align: middle; }
.lg-done { background: var(--green); }
.lg-plan { background: var(--gray); }
.lg-off { background: var(--red); }
.lg-moved { background: var(--orange); }
.lg-overdue { background: var(--gray-bg); border-left: 4px solid var(--muted); }
.lg-movedin { background: var(--accent); }
.lg-no_show { background: var(--red); }

.done-row { display: flex; gap: 8px; align-items: baseline; flex-wrap: wrap; padding: 3px 0; font-size: 13px; }
.done-num { color: var(--muted); font-weight: 700; min-width: 18px; }

/* Переключатель раскладки календаря — на компьютере не нужен: там сетка
   и так помещается целиком, показываем его только в мобильной вёрстке */
.cal-mode-bar { display: none; }
.cal-mode-btn {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    padding: 7px 14px;
    border: 1px solid var(--border);
    border-radius: 999px;
    background: var(--surface);
    color: var(--text);
    font: inherit;
    font-size: 13px;
    font-weight: 700;
    cursor: pointer;
}
.cal-mode-btn:active { background: var(--accent-bg); }
/* Кнопка называет то, КУДА переключит, а не то, что сейчас: «Сеткой» в
   режиме списка и «Списком» в режиме сетки */
.cal-mode-btn .cal-mode-to-list { display: none; }
.cal-mode-btn[aria-pressed="true"] .cal-mode-to-grid { display: none; }
.cal-mode-btn[aria-pressed="true"] .cal-mode-to-list { display: inline; }

/* На телефоне календарь превращается в вертикальный список дней */
@media (max-width: 700px) {
    .cal-mode-bar {
        display: flex;
        align-items: center;
        gap: 10px;
        flex-wrap: wrap;
        margin-bottom: 10px;
    }

    .cal-head { display: none; }
    .cal-grid { display: block; }
    .cal-day {
        display: none;
        min-height: 0;
        margin-bottom: 6px;
        padding: 8px 10px;
    }
    /* показываем только дни, где что-то есть */
    .cal-day:has(.cal-slot) { display: block; }
    .cal-out { display: none !important; }
    .cal-date {
        font-size: 13px;
        color: var(--text);
        margin-bottom: 6px;
        padding-bottom: 5px;
        border-bottom: 1px solid var(--border);
    }
    .cal-date::after {
        content: attr(data-full);
        font-weight: 500;
        color: var(--muted);
        font-size: 11.5px;
        margin-left: 6px;
    }
    .cal-slot { font-size: 13px; padding: 6px 8px; margin-bottom: 4px; }
    .cal-slot-name { white-space: normal; }
    .cal-slot-time { display: inline; font-size: 11px; }
    .cal-pop {
        width: calc(100% - 20px); left: 10px; right: auto;
        /* Попап может открыться у самого низа списка дней — без ограничения
           высоты и скролла его нижняя часть (с кнопками действий) уходила
           под нижний таббар и была недоступна для нажатия. */
        max-height: min(60vh, 420px);
        max-height: min(60dvh, 420px);
        overflow-y: auto;
        margin-bottom: calc(70px + env(safe-area-inset-bottom, 0px));
    }
    .cal-add { font-size: 18px; color: var(--accent); }

    /* Меню дня на телефоне было заметно выше экрана: полноразмерные кнопки,
       крупные поля и щедрые отступы, рассчитанные на мышь. Из-за этого
       «отменить/перенести» уезжало вниз, и до него приходилось скроллить
       внутри попапа, не понимая, что там ещё что-то есть. Здесь всё то же
       самое, но плотнее — меню помещается целиком. */
    .cal-pop .btn { padding: 8px 10px; font-size: 12.5px; }
    .cal-pop .btn-block { margin-top: 0; }
    .cal-pop form { gap: 5px !important; margin-bottom: 6px !important; }
    .cal-pop hr { margin: 7px 0; }
    /* 16px, не меньше: ниже этого порога iOS сама включает зум страницы
       при фокусе на select/input (см. комментарий про time-picker
       попапа в includes/header.php). */
    .cal-pop select, .cal-pop input { padding: 7px 8px; font-size: 16px; margin-bottom: 5px; }
    /* Чьё это меню, видно и после прокрутки внутри него */
    .cal-pop-title {
        position: sticky;
        top: 0;
        z-index: 1;
        background: var(--surface);
        padding: 2px 0 6px;
        margin-bottom: 2px;
        border-bottom: 1px solid var(--border);
        font-size: 13px;
    }

    /* ===== Режим «сеткой» на телефоне (.cal.is-grid) =====
       Возвращает раскладку, ради которой телефон приходилось поворачивать:
       настоящие 7 колонок с неделями по строкам.

       Сетка ВПИСЫВАЕТСЯ в экран, а не листается вбок: месяц нужен как раз
       для того, чтобы охватить его целиком одним взглядом, и прокрутка
       вбок этот смысл убивала. Колонка получается узкой (~48px на обычном
       телефоне) — имя в ней помещается не целиком, но статус занятия
       читается цветом, а полное имя видно в меню дня.

       overflow-x оставлен на совсем узкий экран как аварийный выход. */
    .cal.is-grid { overflow-x: auto; -webkit-overflow-scrolling: touch; }
    .cal.is-grid .cal-head,
    .cal.is-grid .cal-grid {
        display: grid;
        grid-template-columns: repeat(7, minmax(44px, 1fr));
        gap: 3px;
    }
    .cal.is-grid .cal-head { display: grid; margin-bottom: 4px; }
    .cal.is-grid .cal-head div { font-size: 10px; }
    /* В сетке нужны ВСЕ дни, включая пустые и «хвосты» соседних месяцев:
       без них недели разъедутся и разметка перестанет быть календарём */
    .cal.is-grid .cal-day {
        display: block;
        min-height: 62px;
        margin-bottom: 0;
        padding: 3px;
        border-radius: 8px;
    }
    .cal.is-grid .cal-out { display: block !important; }
    .cal.is-grid .cal-date {
        font-size: 10px;
        margin-bottom: 2px;
        padding-bottom: 2px;
        border-bottom: none;
    }
    /* Полная подпись «14.09, пн» нужна была списку, где нет шапки с днями
       недели; в сетке шапка есть, и подпись только съедала бы место */
    .cal.is-grid .cal-date::after { content: none; }
    .cal.is-grid .cal-slot {
        position: relative;
        font-size: 9.5px;
        line-height: 1.25;
        padding: 2px 3px;
        margin-bottom: 2px;
        border-left-width: 2px;
        border-radius: 4px;
    }
    .cal.is-grid .cal-slot-name { white-space: nowrap; }
    .cal.is-grid .cal-slot-time { display: none; }
    /* Кнопка «⋯» съедала треть и без того узкой ячейки. В сетке она
       растягивается на всю плашку и становится прозрачной: нажатие в любое
       место занятия открывает то же самое меню, а место остаётся имени. */
    .cal.is-grid .cal-slot-menu {
        position: absolute;
        inset: 0;
        width: 100%;
        height: 100%;
        padding: 0;
        opacity: 0;
    }
    .cal.is-grid .cal-add { font-size: 13px; }

    /* Попап внутри .cal.is-grid оказался бы в контейнере с overflow-x: auto,
       а он обрезает и по вертикали тоже (CSS не позволяет скроллить одну ось
       и не обрезать другую) — меню действий просто не было бы видно.
       Поэтому в этом режиме попап становится листом снизу экрана, как это
       уже сделано у выпадающего меню в шапке. */
    .cal.is-grid .cal-pop {
        position: fixed;
        left: 8px;
        right: 8px;
        bottom: calc(74px + env(safe-area-inset-bottom, 0px));
        top: auto;
        width: auto;
        margin-bottom: 0;
        max-height: min(72vh, 560px);
        max-height: min(72dvh, 560px);
        overflow-y: auto;
        z-index: 60;
        box-shadow: var(--shadow-lift);
    }
}

/* Компактный инлайн-фильтр в строке статистики */
.inline-filter { display: inline-flex; }
.inline-filter select {
    display: inline-block;
    width: auto;
    margin: 0;
    padding: 6px 12px;
    font-size: 13px;
    font-weight: 600;
    border-radius: 999px;
    border: 1px solid var(--border);
    background: var(--surface);
    color: var(--text);
    cursor: pointer;
}
.inline-filter select:hover { border-color: var(--accent); }

/* Разовое занятие вне регулярного расписания */
.cal-slot.slot-extra .cal-slot-name::after {
    content: " •";
    color: var(--accent);
    font-weight: 700;
}

/* Инлайн-форма редактирования (список курсов Moodle) */
.inline-edit { display: none; margin-top: 10px; padding-top: 10px; border-top: 1px solid var(--border); }
.inline-edit.open { display: block; }
.inline-edit form { display: flex; gap: 8px; flex-wrap: wrap; align-items: center; }
.inline-edit input {
    display: inline-block; width: auto; margin: 0;
    padding: 7px 10px; font-size: 13px;
    border: 1px solid var(--border); border-radius: 10px;
    background: var(--surface); color: var(--text);
}
.inline-edit input[name="label"] { flex: 1 1 200px; }

/* ══════════════════════════════════════════════
   ТОСТЫ — подтверждение действий
   ══════════════════════════════════════════════ */
.toast-wrap {
    position: fixed;
    left: 50%;
    bottom: 24px;
    transform: translateX(-50%);
    z-index: 200;
    display: flex;
    flex-direction: column;
    gap: 8px;
    width: max-content;
    max-width: calc(100vw - 32px);
    transition: opacity .35s ease, transform .35s ease, bottom .2s ease;
}
/* Приподнимаем над нижним таббаром, чтобы тост не прятался под ним */
/* Помимо ширины — ещё и по высоте: телефон в альбомной ориентации обычно
   шире 760px, но остаётся именно телефоном (мало места по высоте), а не
   планшетом/десктопом. Без второго условия при повороте телефона нижний
   таббар и весь мобильный вид пропадали, включалась десктопная раскладка,
   для которой на телефоне физически нет места — кнопки/пункты меню
   наезжали друг на друга или обрезались.
   (hover: none) and (pointer: coarse) — обязательно вместе с высотой:
   иначе это же условие ложно срабатывало и на компьютере, если окно
   браузера просто невысокое (не на весь экран) — тогда мобильная
   раскладка меню включалась поверх мышиного интерфейса и всё разъезжалось
   («то густо, то пусто» в отступах). hover/pointer отличают сенсорные
   устройства (телефон, где и нужна альбомная раскладка) от компьютера с
   мышью — на компьютере это условие теперь не срабатывает никогда. */
@media (max-width: 760px), ((max-height: 480px) and (hover: none) and (pointer: coarse)) {
    .toast-wrap { bottom: calc(72px + env(safe-area-inset-bottom, 0px)); }
}
.toast-hide { opacity: 0; transform: translate(-50%, 12px); }
/* Тост — нейтральная карточка со светящейся точкой, а не заливка во всю
   ширину зелёным или красным. Цветной фон кричал о том, о чём кричать не
   надо: «занятие создано» — это подтверждение, а не событие. Смысл несёт
   точка слева, всё остальное молчит. */
.toast {
    display: flex;
    align-items: center;
    gap: 12px;
    padding: 13px 18px 13px 16px;
    border-radius: 16px;
    background: var(--surface);
    color: var(--text);
    border: 1px solid var(--border);
    font-size: 13.5px;
    font-weight: 600;
    box-shadow: 0 2px 6px rgba(16, 24, 40, .06), 0 18px 40px -14px rgba(16, 24, 40, .28);
    animation: toast-in .34s cubic-bezier(.16, 1, .3, 1);
}
/* Ни точки, ни заливки: подтверждение действия — самое частое сообщение
   в кабинете, и «занятие добавлено» не должно ничем мигать. Ошибку
   отличает только цвет текста. */
.toast-error { color: var(--red); }
@keyframes toast-in {
    from { opacity: 0; transform: translateY(16px) scale(.96); }
    to   { opacity: 1; transform: none; }
}
.toast-text { flex: 1; }
.toast-undo {
    color: var(--accent-dark);
    text-decoration: none;
    font-weight: 700;
    white-space: nowrap;
}
.toast-undo:hover { text-decoration: underline; }
.toast-close {
    background: none; border: none; color: var(--muted); opacity: .6;
    font-size: 18px; line-height: 1; cursor: pointer; padding: 0 2px;
}
.toast-close:hover { opacity: 1; }

/* ══════════════════════════════════════════════
   ПУСТЫЕ СОСТОЯНИЯ
   ══════════════════════════════════════════════ */
.empty-block {
    text-align: center;
    padding: 40px 20px;
    background: var(--surface);
    border: 1px dashed var(--border);
    border-radius: var(--radius);
    margin-bottom: 18px;
}
.empty-block .empty-title {
    font-size: 15px;
    font-weight: 700;
    color: var(--text);
    margin-bottom: 4px;
}
.empty-block .empty-sub {
    font-size: 13px;
    color: var(--muted);
    margin-bottom: 14px;
}

/* ══════════════════════════════════════════════
   ТИПОГРАФИКА — единая шкала
   ══════════════════════════════════════════════ */
body {
    font-family: "Golos Text", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
    font-feature-settings: "tnum" 1;   /* цифры одинаковой ширины — таблицы не «прыгают» */
}
h1 { font-size: 22px; letter-spacing: -0.02em; font-weight: 800; }
h2 { font-size: 16px; letter-spacing: -0.01em; font-weight: 700; }

/* ══════════════════════════════════════════════
   ГРАФИКИ
   ══════════════════════════════════════════════ */
.chart-card {
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    padding: 16px 18px 8px;
    margin-bottom: 20px;
    box-shadow: var(--shadow);
}
.chart-head {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 10px;
    flex-wrap: wrap;
    margin-bottom: 6px;
}
.chart-title { font-size: 14px; font-weight: 700; color: var(--text); }
.chart-note { font-size: 11.5px; color: var(--muted); }
.chart { width: 100%; height: auto; display: block; overflow: visible; }
.chart-grid { stroke: var(--border); stroke-width: 1; }
.chart-axis { font-size: 10px; fill: var(--muted); font-weight: 600; }
.chart-label { font-size: 10.5px; fill: var(--muted); }
.chart-value { font-size: 11px; fill: var(--text); font-weight: 700; }
.chart-line { fill: none; stroke: var(--accent); stroke-width: 2.5; stroke-linejoin: round; stroke-linecap: round; }
.chart-area { fill: var(--accent); opacity: .10; }
.chart-dot { fill: var(--surface); stroke: var(--accent); stroke-width: 2.5; }

@media (max-width: 700px) {
    .chart-card { padding: 12px 12px 4px; }
    .chart-value { font-size: 10px; }
    .chart-label { font-size: 9.5px; }
}

/* ══════════════════════════════════════════════
   МАССОВЫЕ ДЕЙСТВИЯ
   ══════════════════════════════════════════════ */
/* !important здесь осознанно: колонка выбора должна быть скрыта всегда,
   кроме явного режима "Выбрать несколько" — а мобильная адаптация таблиц
   (эта же страница) описывает display для td общими правилами, которые
   иначе перебивали бы скрытие на телефоне из-за порядка каскада. */
.cell-check { width: 34px; text-align: center; display: none !important; }
body.select-mode .cell-check { display: table-cell !important; }
.table-tools { display: flex; justify-content: flex-end; margin-bottom: 10px; }
.cell-check input { width: 16px; height: 16px; cursor: pointer; margin: 0; }

.bulk-bar {
    display: none;
    align-items: center;
    gap: 8px;
    flex-wrap: wrap;
    background: var(--surface);
    border: 1px solid var(--accent);
    border-radius: var(--radius);
    padding: 10px 14px;
    margin-bottom: 12px;
    box-shadow: var(--shadow);
}
.bulk-bar.open { display: flex; }
.bulk-count { font-size: 13px; color: var(--muted); margin-right: 4px; }
.bulk-count b { color: var(--text); font-size: 15px; }

@media (max-width: 700px) {
    body.select-mode .lessons-table td.cell-check {
        display: flex !important;
        justify-content: flex-start;
        gap: 8px;
        padding-bottom: 6px;
    }
    .lessons-table td.cell-check::before { content: "Выбрать"; }
    .bulk-bar {
        position: sticky;
        bottom: calc(72px + env(safe-area-inset-bottom, 0px));
        z-index: 50;
    }
}

/* ══════════════════════════════════════════════
   ТЁМНАЯ ТЕМА
   ══════════════════════════════════════════════ */
[data-theme="dark"] {
    --bg: #0f1520;
    --surface: #171f2e;
    --surface-2: #1e2738;
    --border: #2b3a52;
    --text: #e6ecf5;
    --muted: #94a3b8;
    --accent: #6f97fc;
    --accent-dark: #9db6fd;
    --accent-bg: rgba(111, 151, 252, .16);

    --green: #4ade80;
    --green-dark: #86efac;
    --green-bg: rgba(74, 222, 128, .15);
    --green-dark-bg: rgba(134, 239, 172, .18);
    --red: #f87171;
    --red-bg: rgba(248, 113, 113, .15);
    --orange: #f2b563;
    --orange-bg: rgba(242, 181, 99, .16);
    --gray: #94a3b8;
    --gray-bg: rgba(148, 163, 184, .14);
    --shadow: 0 1px 3px rgba(0, 0, 0, .35), 0 8px 20px rgba(0, 0, 0, .2);
}
[data-theme="dark"] body::before { opacity: .025; }
[data-theme="dark"] .cal-past { background: #131c2e; }
[data-theme="dark"] .cal-today { background: #16243d; }
[data-theme="dark"] .lessons-table tbody tr.row-hw-pending td { background: #1d2536; }
[data-theme="dark"] .lessons-table tbody tr.row-no-show td { background: rgba(248, 113, 113, .05); }
[data-theme="dark"] .btn-primary { color: #0b1220; }
[data-theme="dark"] .toast {
    background: #1c2333; color: #e6ecf5; border-color: #2b3a52;
    box-shadow: 0 2px 6px rgba(0, 0, 0, .3), 0 18px 40px -14px rgba(0, 0, 0, .6);
}

.theme-icon-dark { display: none; }
[data-theme="dark"] .theme-icon-light { display: none; }
[data-theme="dark"] .theme-icon-dark { display: inline; }

/* ══════════════════════════════════════════════
   НИЖНЯЯ НАВИГАЦИЯ (моб./PWA) — вместо гамбургера
   Тот же <nav id="mainNav">, что и на десктопе: на телефоне мы не
   показываем/скрываем его, а просто переверстываем в фиксированный
   таббар снизу — так пункты меню не дублируются в разметке.
   ══════════════════════════════════════════════ */
/* Помимо ширины — ещё и по высоте: телефон в альбомной ориентации обычно
   шире 760px, но остаётся именно телефоном (мало места по высоте), а не
   планшетом/десктопом. Без второго условия при повороте телефона нижний
   таббар и весь мобильный вид пропадали, включалась десктопная раскладка,
   для которой на телефоне физически нет места — кнопки/пункты меню
   наезжали друг на друга или обрезались.
   (hover: none) and (pointer: coarse) — обязательно вместе с высотой:
   иначе это же условие ложно срабатывало и на компьютере, если окно
   браузера просто невысокое (не на весь экран) — тогда мобильная
   раскладка меню включалась поверх мышиного интерфейса и всё разъезжалось
   («то густо, то пусто» в отступах). hover/pointer отличают сенсорные
   устройства (телефон, где и нужна альбомная раскладка) от компьютера с
   мышью — на компьютере это условие теперь не срабатывает никогда. */
@media (max-width: 760px), ((max-height: 480px) and (hover: none) and (pointer: coarse)) {
    .header-inner { padding: 12px 16px; }
    .brand .logo { max-height: 40px; }
    .brand-text { font-size: 18px; }



    .main-nav {
        position: fixed;
        left: 0;
        right: 0;
        bottom: 0;
        z-index: 45;
        display: flex;
        flex-direction: row;
        align-items: stretch;
        gap: 2px;
        margin: 0;
        padding: 6px 6px calc(6px + env(safe-area-inset-bottom, 0px));
        background: var(--surface);
        border-top: 1px solid var(--border);
        box-shadow: 0 -8px 24px rgba(22, 32, 46, 0.07);
        /* Отдельный слой композитора. В установленном PWA (Android WebView)
           position:fixed пересчитывается по размерам вьюпорта, а те после
           показа/скрытия клавиатуры и перехода на другую страницу иногда
           остаются от предыдущего состояния — таббар «отрывается» и едет со
           скроллом, и чинится это только перезапуском приложения. Свой слой
           (translateZ) выносит панель из общего потока перерисовки: её
           положение считает композитор от края экрана, а не layout от того,
           что браузер в этот момент думает о высоте страницы.

           На позицию выпадающего листа это не влияет: transform делает
           .main-nav containing block для fixed-потомков, но её padding-box
           снизу и по бокам совпадает с краями экрана — те же 64px до низа,
           что и раньше (см. .nav-dropdown-menu ниже). */
        transform: translateZ(0);
        -webkit-backface-visibility: hidden;
        backface-visibility: hidden;
    }
    /* Заглушка под панелью — на случай, когда браузер всё-таки промахнулся
       мимо нижнего края.

       Живучесть этой болячки в том, что причин у неё несколько и все
       внешние: установленное PWA считает высоту вьюпорта по прошлому
       состоянию, «умный» полноэкранный режим Android прячет и возвращает
       системную панель жестов, safe-area меняется на лету. Панель тогда
       оказывается на несколько пикселей выше края, и снизу видна полоска
       фона страницы — панель выглядит отклеившейся.

       Гоняться за каждой причиной бессмысленно: полоску видно, а не
       наличие/отсутствие пикселей. Псевдоэлемент продолжает панель вниз
       тем же цветом, поэтому разрыва не видно при любом промахе, а если
       промаха нет — заглушка просто уходит за край экрана. Клики она не
       перехватывает. */
    .main-nav::after {
        content: '';
        position: absolute;
        left: 0; right: 0; top: 100%;
        height: 140px;
        background: var(--surface);
        pointer-events: none;
    }
    .main-nav .nav-link {
        flex: 1 1 0;
        min-width: 0;
        flex-direction: column;
        align-items: center;
        justify-content: center;
        gap: 3px;
        padding: 7px 2px 6px;
        font-size: 10.5px;
        font-weight: 700;
        border-radius: 14px;
        text-align: center;
    }
    .main-nav .nav-link:hover { background: none; }
    /*
     * Нажатие — через собственный класс .is-pressed, а не :active: на iOS
     * системный жест смены приложения (свайп снизу вверх ровно над
     * таббаром) иногда не присылает pointerup тому пункту, на который
     * попадал палец, и нативный :active "залипает" до следующего касания
     * где угодно ещё (см. JS ниже в header.php) — снять его программно
     * нельзя, а свой класс снимается и без него.
     */
    .main-nav .nav-link.is-pressed { background: none; }
    .main-nav .nav-link.is-pressed .nav-icon { background: var(--accent-bg); }
    .main-nav .nav-link span { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; max-width: 100%; }
    .nav-icon { width: 22px; height: 22px; }
    /*
     * Подсветка текущего раздела — не вся высокая плитка (спорит с точкой-
     * уведомлением и смотрится тяжело), а «таблетка» вокруг иконки. Отступы
     * заданы у всех пунктов, не только активного, — иначе подписи прыгали
     * бы на пару пикселей при переключении.
     */
    .main-nav .nav-link .nav-icon {
        box-sizing: content-box;
        padding: 4px 16px;
        border-radius: 999px;
        transition: background-color .2s ease, color .2s ease;
    }
    .main-nav .nav-link.is-active,
    .main-nav .nav-link[aria-current="page"] { background: none; color: var(--accent-dark); }
    .main-nav .nav-link.is-active .nav-icon,
    .main-nav .nav-link[aria-current="page"] .nav-icon { background: var(--accent-bg); color: var(--accent); }
    .main-nav .nav-link.is-active span,
    .main-nav .nav-link[aria-current="page"] span { font-weight: 800; }
    .nav-dropdown { flex: 1 1 0; width: auto; }
    .nav-dropdown > summary.nav-link { width: 100%; justify-content: center; }
    .nav-dropdown[open] > summary { background: var(--accent-bg); color: var(--accent-dark); }

    /* Лист "Ещё" открывается ВВЕРХ от таббара, а не вниз за экран */
    .nav-dropdown-menu {
        position: fixed;
        top: auto;
        bottom: calc(64px + env(safe-area-inset-bottom, 0px));
        right: 10px;
        left: 10px;
        width: auto;
        max-height: min(65vh, 460px);
        max-height: min(65dvh, 460px); /* см. комментарий у .nav-dropdown-menu выше по файлу */
        border-radius: var(--radius-sm);
    }
    .nav-dropdown-theme { display: block; }

    /* Точка «есть новое» у кнопки профиля — уголком на аватарке, как значок
       на иконке приложения. В колонке таббара она иначе становилась третьей
       строкой под подписью и растила высоту всей панели. */
    .main-nav .nav-profile-btn { position: relative; }
    .main-nav .nav-profile-btn .nav-dot {
        position: absolute;
        top: 5px;
        left: calc(50% + 7px);
        margin: 0;
        box-shadow: 0 0 0 2px var(--surface);
    }
    /* Кружок той же высоты, что значок у соседей (22px + по 4px сверху и
       снизу, как .nav-icon выше), — иначе «Профиль» с подписью стоял выше
       остальных пунктов. У ученика кнопка — <summary> внутри <details>:
       растягиваем её на всю высоту панели, чтобы центровка была общей. */
    .main-nav .nav-profile-btn .nav-ava { margin: 4px 0; }
    .nav-dropdown > summary.nav-link { height: 100%; }
}


/* ══════════════════════════════════════════════
   МЕНЮ ДЕЙСТВИЙ В СТРОКЕ ТАБЛИЦЫ
   ══════════════════════════════════════════════ */
.cell-actions { position: relative; width: 40px; }
.row-menu[open] { position: relative; z-index: 61; }
.row-menu summary {
    list-style: none;
    cursor: pointer;
    color: var(--muted);
    font-size: 16px;
    line-height: 1;
    padding: 4px 8px;
    border-radius: 10px;
    display: inline-block;
}
.row-menu summary::-webkit-details-marker { display: none; }
.row-menu summary:hover { color: var(--accent); background: var(--surface-2); }
.row-menu-body {
    /*
     * position:fixed относительно окна (не относительно .cell-actions —
     * та колонка узкая, всего ~40px, и left/right внутри неё как
     * containing block ломают ширину). Запасные top/right — на случай,
     * если скрипт (placeMenu) на каком-то устройстве не выполнился: тогда
     * меню всё равно откроется в разумном видимом месте у верхнего края,
     * а не пропадёт. При успешном запуске скрипт просто перезаписывает
     * top/left/right точными координатами под конкретную кнопку.
     */
    position: fixed;
    top: 64px;
    right: 12px;
    z-index: 60;
    min-width: 180px;
    max-width: calc(100vw - 24px);
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: 14px;
    box-shadow: 0 8px 24px rgba(16, 24, 40, .14);
    padding: 6px;
    display: flex;
    flex-direction: column;
    text-align: left;
}
.row-menu-body a,
.row-menu-body button {
    display: block;
    width: 100%;
    text-align: left;
    padding: 8px 10px;
    border: none;
    background: none;
    border-radius: 10px;
    font: inherit;
    font-size: 13px;
    color: var(--text);
    cursor: pointer;
}
.row-menu-body a:hover,
.row-menu-body button:hover { background: var(--surface-2); text-decoration: none; }
.row-menu-body .danger-link { color: var(--red); }

@media (max-width: 700px) {
    /* Карточка-строка: подсветку даём самой карточке, иначе фон рвётся на куски */
    .lessons-table tbody tr.row-hw-pending {
        background: #fffdf5;
        border-color: #e8dcc0;
        box-shadow: inset 3px 0 0 var(--accent);
    }
    .lessons-table tbody tr.row-hw-pending td { background: transparent; }
    .lessons-table tbody tr.row-hw-pending td:first-child { box-shadow: none; }

    .lessons-table tbody tr.row-no-show {
        background: rgba(220, 38, 38, 0.05);
        border-color: #f4d6d6;
        box-shadow: inset 3px 0 0 rgba(220, 38, 38, 0.45);
    }
    .lessons-table tbody tr.row-no-show td { background: transparent; }
    .lessons-table tbody tr.row-no-show td:first-child { box-shadow: none; }

    .cell-actions { width: auto; position: static; }
    .lessons-table td.cell-actions { justify-content: flex-end; }
    .row-menu-body { left: 12px; right: 12px; width: auto; min-width: 0; }
}

[data-theme="dark"] .lessons-table tbody tr.row-hw-pending {
    background: #1d2536;
    border-color: #35415c;
}
[data-theme="dark"] .lessons-table tbody tr.row-no-show {
    background: rgba(248, 113, 113, .05);
    border-color: #4a2626;
}

/* ══════════════════════════════════════════════
   ОТЧЁТ ДЛЯ РОДИТЕЛЕЙ
   ══════════════════════════════════════════════ */
.report {
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    padding: 26px 28px;
    box-shadow: var(--shadow);
}
.report-head {
    display: flex;
    justify-content: space-between;
    align-items: flex-start;
    gap: 16px;
    padding-bottom: 16px;
    border-bottom: 2px solid var(--accent);
    margin-bottom: 18px;
}
.report-title { font-size: 22px; font-weight: 800; letter-spacing: -.02em; }
.report-period { font-size: 13px; color: var(--muted); margin-top: 2px; }
.report-goal { font-size: 13px; color: var(--accent-dark); font-weight: 600; margin-top: 6px; }
.report-logo { max-height: 46px; }

.report-kpi { display: flex; gap: 10px; flex-wrap: wrap; margin-bottom: 22px; }
.report-kpi div {
    flex: 1 1 130px;
    background: var(--surface-2);
    border-radius: 14px;
    padding: 12px 14px;
    font-size: 12px;
    color: var(--muted);
}
.report-kpi span {
    display: block;
    font-size: 24px;
    font-weight: 800;
    color: var(--text);
    letter-spacing: -.02em;
    margin-bottom: 2px;
}

.report-h {
    font-size: 14px;
    font-weight: 700;
    margin: 22px 0 8px;
    color: var(--text);
}
.report-table { width: 100%; border-collapse: collapse; font-size: 13px; }
.report-table th {
    text-align: left;
    font-size: 11px;
    text-transform: uppercase;
    letter-spacing: .04em;
    color: var(--muted);
    padding: 6px 8px;
    border-bottom: 1px solid var(--border);
}
.report-table td { padding: 7px 8px; border-bottom: 1px solid var(--border); }
.report-table tr:last-child td { border-bottom: none; }

.report-footer {
    margin-top: 26px;
    padding-top: 14px;
    border-top: 1px solid var(--border);
    font-size: 11.5px;
    color: var(--muted);
    text-align: center;
}

@media print {
    .site-header, .site-footer-contacts, .no-print, .toast-wrap { display: none !important; }
    body { background: #fff; font-size: 11pt; }
    body::before { display: none; }
    .site-main { padding: 0; max-width: none; }
    .report { border: none; box-shadow: none; padding: 0; }
    .report-kpi div { background: #f7f9fc; }
    .report-table { page-break-inside: auto; }
    .report-table tr { page-break-inside: avoid; }
}

/* Подсказки для комментария к отчёту */
.hint-chips { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 10px; }
.hint-chip {
    background: var(--surface-2);
    border: 1px solid var(--border);
    border-radius: 999px;
    padding: 6px 12px;
    font: inherit;
    font-size: 12px;
    color: var(--muted);
    cursor: pointer;
    text-align: left;
}
.hint-chip:hover { border-color: var(--accent); color: var(--accent); }

.report-comment {
    margin: 24px 0 0;
    padding: 16px 18px;
    background: var(--accent-bg);
    border-left: 4px solid var(--accent);
    border-radius: 14px;
    font-size: 13.5px;
    line-height: 1.55;
}
.report-comment-label {
    font-size: 10.5px;
    text-transform: uppercase;
    letter-spacing: .07em;
    font-weight: 700;
    color: var(--accent-dark);
    margin-bottom: 6px;
}

/* Группы подсказок для комментария */
.hint-group { margin-top: 12px; }
.hint-group-title {
    font-size: 10.5px;
    text-transform: uppercase;
    letter-spacing: .06em;
    font-weight: 700;
    color: var(--muted);
    margin-bottom: 5px;
}
.hint-chips { margin-top: 0; }

/* ══════════════════════════════════════════════
   ЭКРАН «СЕГОДНЯ»
   ══════════════════════════════════════════════ */
.today-block {
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    padding: 16px 18px;
    margin-bottom: 16px;
    box-shadow: var(--shadow);
}
/* Свой заголовок карточки — .section-title рассчитан на обычный поток
   страницы и добавляет большой верхний отступ (26px), из-за чего внутри
   карточки появлялась заметная пустота перед текстом */
.today-block-title {
    font-size: 13px;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: .04em;
    color: var(--muted);
    margin: 0 0 12px;
}
.today-grid {
    display: grid;
    grid-template-columns: repeat(2, 1fr);
    gap: 16px;
    /* margin-bottom у карточек внутри грида не создаёт видимого отступа
       ПОСЛЕ самого грид-контейнера (особенность CSS Grid) — поэтому отступ
       между строками задаём прямо здесь, а не полагаемся на детей */
    margin-bottom: 16px;
}
/* Без этого блоки внутри грида не сжимаются меньше своего "минимального
   содержимого" (например, двух кнопок в один ряд у "Просит отработку") —
   из-за чего вся страница на телефоне раздувалась шире экрана целиком. */
.today-grid > .today-block { min-width: 0; }
.today-list { display: flex; flex-direction: column; gap: 6px; }
.today-row {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 8px;
    padding: 9px 10px;
    border-radius: 12px;
    background: var(--surface-2);
}
.today-row-done { background: var(--green-bg); }
/* Отмена — красным, а не серым: серое читалось как «пусто, ничего нет»,
   хотя занятие было в расписании и не состоялось — это как раз то, что
   должно бросаться в глаза на странице дня. */
.today-row-off  { background: var(--red-bg); }
.today-row-moved{ background: var(--orange-bg); }
.today-row-no_show{ background: var(--red-bg); }

/* Левая часть (имя + подпись) не переносится и не толкает правую часть
   (сумму/кнопки) на отдельную строку — вместо этого длинное имя обрезается
   многоточием. На телефоне это выглядит намного аккуратнее, чем перенос. */
.today-row-main {
    display: flex;
    flex-direction: column;
    gap: 1px;
    min-width: 0;
    flex: 1 1 auto;
    overflow: hidden;
}
.today-row-main-line { display: flex; align-items: baseline; gap: 8px; min-width: 0; }
.today-name {
    font-weight: 700;
    font-size: 13.5px;
    color: var(--text);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    min-width: 0;
}
.today-name:hover { color: var(--accent); }
.today-time { font-size: 12px; font-weight: 700; color: var(--muted); flex-shrink: 0; }
.today-sub {
    font-size: 12px;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
/* Правая часть (сумма/бейдж/кнопки) никогда не сжимается и не переносится */
.today-row > .btn, .today-row > b, .today-row > .badge, .today-row > a.muted, .today-row-actions {
    flex-shrink: 0;
}
.today-row-actions { display: flex; gap: 6px; }
/* "Давно не появлялся": подпись идёт не внутри today-row-main, а рядом с
   ним (там нечего показывать вторым пунктом кроме имени) — не даём ей
   сжиматься в ноль и не разрешаем переносить слова посередине */
.today-row > .today-sub { flex-shrink: 0; text-align: right; white-space: nowrap; }
@media (max-width: 480px) {
    .today-row > .today-sub { font-size: 11px; }
}

@media (max-width: 420px) {
    /* На совсем узких экранах подпись убираем — оставляем только имя и суть */
    .today-sub { display: none; }
    .today-row-actions .btn { padding: 5px 8px; font-size: 12px; }
}

@media (max-width: 900px) {
    .today-grid { grid-template-columns: 1fr; }
}
/* Обёртка ячейки оплаты — на мобильном её содержимое (селект + цена)
   должно вести себя как один flex-элемент, а не два */
/* На десктопе колонка "Оплата" центрирована, как и остальные — align-items
   переключается на flex-end только в мобильной раскладке (там ячейки
   выстраиваются в строку "подпись слева - значение справа") */
.payment-cell { display: flex; flex-direction: column; align-items: center; }
@media (max-width: 700px) {
    .payment-cell { align-items: flex-end; width: 100%; }
}

/* ══════════════════════════════════════════════
   PWA: баннер установки
   ══════════════════════════════════════════════ */
.install-banner {
    display: none;
    position: fixed;
    left: 50%;
    bottom: calc(18px + env(safe-area-inset-bottom, 0px));
    transform: translateX(-50%);
    z-index: 190;
    align-items: center;
    gap: 12px;
    max-width: calc(100vw - 24px);
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: 14px;
    padding: 12px 14px;
    box-shadow: 0 10px 30px rgba(16, 24, 40, .16);
    font-size: 13px;
}
.install-banner.open { display: flex; }
.install-text { color: var(--text); flex: 1; min-width: 160px; }
.install-close {
    background: none;
    border: none;
    color: var(--muted);
    font-size: 18px;
    line-height: 1;
    cursor: pointer;
    padding: 0 2px;
    flex-shrink: 0;
}
.install-close:hover { color: var(--text); }

@media (max-width: 600px) {
    .install-banner { flex-wrap: wrap; bottom: calc(76px + env(safe-area-inset-bottom, 0px)); }
}

/* Невидимая подложка под открытыми меню/попапами — надёжно ловит клик
   мимо, даже если попап визуально накладывается на другой контент */
.ui-backdrop {
    position: fixed;
    inset: 0;
    z-index: 55;
    background: transparent;
}

/* ══════════════════════════════════════════════
   ФИНАНСЫ
   ══════════════════════════════════════════════ */
.finance-kpi { display: flex; gap: 10px; flex-wrap: wrap; }
.finance-kpi > div { flex: 1 1 140px; min-width: 0; }
.finance-kpi-value { display: block; font-size: 22px; font-weight: 800; letter-spacing: -.02em; color: var(--text); word-break: break-word; }
.finance-kpi-label { font-size: 12px; color: var(--muted); }
.finance-kpi-label b { display: inline-block; margin-left: 4px; font-weight: 700; }

.finance-goal { margin-top: 16px; }
.finance-goal-bar { height: 8px; border-radius: 8px; background: var(--gray-bg); overflow: hidden; margin-bottom: 6px; }
.finance-goal-fill { height: 100%; background: var(--accent); border-radius: 8px; }

/* Форма плана: на узком экране в один ряд не помещается (подпись + поле +
   кнопка) — оборачиваем и даём полю тянуться, а не фиксированные 150px,
   из-за которых кнопка "уезжала" за пределы экрана */
.finance-goal-form {
    display: flex;
    align-items: flex-end;
    gap: 10px;
    flex-wrap: wrap;
    margin-top: 14px;
    padding-top: 14px;
    border-top: 1px solid var(--border);
}
/* Выбор репетитора — только у администратора платформы и бухгалтера */
.finance-scope { margin: 0 0 14px; }
.finance-scope label { display: flex; flex-direction: column; gap: 4px; font-size: 12px; color: var(--muted); max-width: 420px; }
.finance-scope select { font: inherit; font-size: 14px; color: var(--text); padding: 8px 10px; border: 1px solid var(--border); border-radius: 10px; background: var(--surface); }
.finance-goal-form label { font-size: 12px; color: var(--muted); flex: 1 1 160px; min-width: 0; }
.finance-goal-form input {
    display: block;
    width: 100%;
    max-width: 200px;
    margin-top: 4px;
    padding: 7px 10px;
    border-radius: 10px;
    border: 1px solid var(--border);
    background: var(--surface);
    color: var(--text);
    box-sizing: border-box;
}
.finance-goal-form button { flex-shrink: 0; }

@media (max-width: 500px) {
    .finance-kpi > div { flex: 1 1 calc(50% - 5px); }
    .finance-goal-form label { flex-basis: 100%; }
    .finance-goal-form input { max-width: none; }
    .finance-goal-form button { width: 100%; }
}

/* ══════════════════════════════════════════════
   ПОЛОСА ЗАГРУЗКИ СВЕРХУ (переходы между страницами)
   ══════════════════════════════════════════════ */
.page-progress {
    position: fixed;
    top: 0;
    left: 0;
    height: 3px;
    width: 0%;
    z-index: 300;
    background: linear-gradient(90deg, var(--accent), #60a5fa);
    box-shadow: 0 0 8px var(--accent);
    opacity: 0;
    pointer-events: none;
}

/* Бейдж "Загрузка…" — появляется, только если переход между страницами
   реально затянулся (см. таймаут в includes/header.php), а не при каждом
   клике. Обычный быстрый переход его никогда не видит — страница успевает
   смениться раньше. */
.page-loading-badge {
    position: fixed;
    left: 50%;
    bottom: 24px;
    transform: translate(-50%, 8px);
    z-index: 300;
    display: flex;
    align-items: center;
    gap: 9px;
    padding: 10px 16px;
    border-radius: 999px;
    background: var(--surface);
    border: 1px solid var(--border);
    box-shadow: var(--shadow-lift, 0 8px 24px rgba(16,24,40,.14));
    color: var(--text);
    font-size: 13px;
    font-weight: 600;
    opacity: 0;
    pointer-events: none;
    transition: opacity .25s ease, transform .25s ease;
}
.page-loading-badge.show { opacity: 1; transform: translate(-50%, 0); }
.page-loading-spinner {
    width: 14px;
    height: 14px;
    flex-shrink: 0;
    border: 2px solid var(--border);
    border-top-color: var(--accent);
    border-radius: 50%;
    animation: btn-spin .7s linear infinite;
}
@media (max-width: 760px) {
    /* Над нижним таббаром, а не под ним */
    .page-loading-badge { bottom: calc(76px + env(safe-area-inset-bottom, 0px)); }
}
@media (prefers-reduced-motion: reduce) {
    .page-loading-badge { transition: none; }
    .page-loading-spinner { animation: none; }
}

/* ══════════════════════════════════════════════
   СОСТОЯНИЕ КНОПКИ ПРИ ОТПРАВКЕ ФОРМЫ
   ══════════════════════════════════════════════
   Раньше текст кнопки пропадал (color:transparent) и посередине повисала
   одинокая крутящаяся точка — на широких кнопках ("Отправить ученику" и
   т.п.) смотрелось странно, будто кнопка сломалась. Сам факт "идёт
   загрузка" и так уже виден отдельно — полоса сверху страницы и бейдж
   "загрузка…" снизу (см. includes/header.php) — кнопке этого дублировать
   не нужно, достаточно просто заблокировать повторный клик и слегка
   притушить, не трогая её содержимое. */
button[type="submit"].is-loading,
.btn.is-loading {
    opacity: .6;
    pointer-events: none;
}
@keyframes btn-spin { to { transform: rotate(360deg); } }
@media (prefers-reduced-motion: reduce) {
    .page-progress { transition: none !important; }
}

/* ══════════════════════════════════════════════
   УРОВНИ УЧЕНИКОВ — карточки настроек
   (сама метка рядом с именем рисуется инлайн-стилем в PHP —
   см. student_level_badge() — там она гарантированно круглая
   независимо от каскада; здесь только окружение витрины)
   ══════════════════════════════════════════════ */
.level-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
    gap: 8px;
    margin-bottom: 24px;
}
/* Карточка уровня — всё друг под другом, а не в строку: строчная
   раскладка (метка + счётчик + две ссылки в одном ряду) не помещалась
   почти ни при какой ширине и "уезжала". */
.level-card {
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: 14px;
    padding: 14px 16px;
    box-shadow: var(--shadow);
    border-left: 3px solid var(--lv-color, var(--accent));
}
.level-card-preview { margin-bottom: 8px; }
.level-card-count { font-size: 11.5px; color: var(--muted); margin-bottom: 10px; }
.level-card-actions {
    display: flex;
    gap: 14px;
    font-size: 12.5px;
    padding-top: 10px;
    border-top: 1px solid var(--border);
}

/* Выбор цвета — компактные кружки, кликабельны через JS (не через
   вложенный input), поэтому используется не <label>, а простой <span>.
   flex-shrink:0 — иначе на узкой форме кружки сплющивались в овалы. */
.color-swatches { display: flex; gap: 10px; flex-wrap: wrap; margin-top: 8px; align-items: center; }
.color-swatch { flex-shrink: 0; }
.color-swatch {
    position: relative;
    width: 26px;
    height: 26px;
    border-radius: 50%;
    cursor: pointer;
    display: flex;
    align-items: center;
    justify-content: center;
    border: 2px solid transparent;
    transition: transform .12s ease;
}
.color-swatch:hover { transform: scale(1.1); }
.color-swatch::after {
    content: "";
    width: 8px;
    height: 8px;
    border-radius: 50%;
    background: #fff;
    opacity: 0;
    box-shadow: 0 0 0 1.5px rgba(0,0,0,.2);
}
.color-swatch.is-selected { border-color: var(--text); }
.color-swatch.is-selected::after { opacity: 1; }
.color-swatch-custom {
    background: conic-gradient(red, yellow, lime, cyan, blue, magenta, red);
    padding: 0;
}
.color-swatch-custom::after { display: none; }
.color-swatch-custom.is-selected::after {
    opacity: 1;
    display: block;
}
.color-swatch-custom input[type="color"] {
    width: 100%;
    height: 100%;
    opacity: 0;
    cursor: pointer;
    position: absolute;
    inset: 0;
    padding: 0;
    border: none;
}

/* Страница "Удаление" — панель массовых действий и колонка действий строки */
.trash-bulk-bar { display: flex; gap: 10px; margin-bottom: 10px; }
.trash-actions { display: flex; gap: 8px; justify-content: flex-end; flex-wrap: wrap; }

/* ══════════════════════════════════════════════
   СТРАНИЦА "УВЕДОМЛЕНИЯ" — единообразные карточки-блоки
   ══════════════════════════════════════════════ */
.notify-card {
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    padding: 16px 18px;
    box-shadow: var(--shadow);
    max-width: 560px;
    margin-bottom: 16px;
}
.notify-card h2 { margin: 0 0 4px; font-size: 16px; }
.notify-card-warn { border-color: var(--red); }
.notify-row {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 12px;
    flex-wrap: wrap;
}
.notify-btn-row { display: flex; gap: 10px; flex-wrap: wrap; margin-top: 10px; }
.notify-personal {
    display: flex;
    align-items: center;
    gap: 10px;
    flex-wrap: wrap;
    margin-top: 14px;
    padding-top: 12px;
    border-top: 1px solid var(--border);
}
.notify-group-title {
    margin: 22px 0 10px;
    font-size: 13px;
    color: var(--muted);
    text-transform: uppercase;
    letter-spacing: .04em;
}
.notify-tpl-card { transition: opacity .15s ease; }
.notify-tpl-disabled { opacity: .5; }

/* Рассылка — выбор получателя в виде трёх плашек вместо голых radio-строк */
.target-picker { display: grid; grid-template-columns: repeat(3, 1fr); gap: 8px; margin-bottom: 14px; }
.target-option {
    display: flex;
    align-items: center;
    gap: 8px;
    border: 1px solid var(--border);
    border-radius: 12px;
    padding: 10px 12px;
    cursor: pointer;
    font-size: 13px;
    transition: border-color .12s ease, background .12s ease;
}
.target-option:has(input:checked) { border-color: var(--accent); background: var(--accent-bg); }
.target-option input { margin: 0; flex-shrink: 0; }
.target-sub-block {
    max-height: 240px;
    overflow-y: auto;
    border: 1px solid var(--border);
    border-radius: 12px;
    padding: 12px;
    margin-bottom: 14px;
}
@media (max-width: 600px) {
    .target-picker { grid-template-columns: 1fr; }
}

/* ══════════════════════════════════════════════
   Кабинет ученика — компактный ряд: Telegram/ВК/пароль одной строкой,
   а не тремя большими блоками друг под другом
   ══════════════════════════════════════════════ */
.quick-access-row { display: flex; flex-wrap: wrap; gap: 8px; margin: 10px 0 18px; }
/* position:relative — панель ниже позиционируется абсолютно относительно
   именно этого элемента, а не занимает место в потоке. Без этого
   раскрытая панель растягивала свой <details> под свою же ширину и
   расталкивала соседние чипы в ряду (на мобильном не так заметно, там
   чипы и так переносятся по одному в строку, а на широком экране это
   выглядело как "всё съезжает"). */
.quick-chip-toggle { display: inline-block; position: relative; }
.quick-chip {
    display: inline-flex;
    align-items: center;
    gap: 5px;
    padding: 5px 12px;
    border-radius: 999px;
    font-size: 12.5px;
    font-weight: 600;
    cursor: pointer;
    list-style: none;
    background: var(--accent-bg);
    color: var(--accent-dark);
    border: 1px solid transparent;
}
.quick-chip::-webkit-details-marker { display: none; }
.quick-chip-on { background: rgba(21,128,61,.12); color: #15803d; }
.quick-chip-muted {
    background: var(--surface-2);
    color: var(--muted);
    text-decoration: none;
}
.quick-chip-muted:hover { color: var(--text); }
.quick-chip-panel {
    position: absolute;
    top: calc(100% + 8px);
    left: 0;
    z-index: 30;
    width: max-content;
    max-width: min(320px, 85vw);
    padding: 12px;
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: 14px;
    box-shadow: var(--shadow);
    font-size: 13px;
}

/* ══════════════════════════════════════════════
   ПОШАГОВЫЙ МАСТЕР ПОДКЛЮЧЕНИЙ (admin/integrations.php)

   Настройка бота или Moodle — это не «форма с полями», а
   последовательность действий в чужом интерфейсе и здесь. Поэтому
   шаги пронумерованы, а пройденные отмечены галочкой: человек видит,
   где остановился, и не гадает, всё ли он сделал.
   ══════════════════════════════════════════════ */
.setup-block { margin-bottom: 18px; }
.setup-head {
    display: flex; align-items: center; gap: 10px; flex-wrap: wrap;
    margin-bottom: 14px;
}
.setup-step {
    display: flex;
    gap: 12px;
    padding: 14px 0;
    border-top: 1px solid var(--border);
}
.setup-step:first-of-type { border-top: none; padding-top: 4px; }
.setup-step-num {
    flex-shrink: 0;
    width: 26px; height: 26px;
    border-radius: 50%;
    background: var(--surface-2);
    color: var(--muted);
    display: flex; align-items: center; justify-content: center;
    font-size: 13px; font-weight: 800;
}
.setup-step-num.is-done { background: var(--green-bg); color: var(--green-dark); }

/* ══════════════════════════════════════════════
   Экран согласия (consent.php) — открывается сразу после входа и
   не пускает дальше, пока человек не согласился.

   Текст здесь читают, а не пробегают глазами, поэтому колонка узкая
   (около 70 символов в строке), кегль обычный и межстрочный интервал
   увеличен. Соблазн ужать всё в петит был велик — и именно ему нельзя
   поддаваться: согласие, которое невозможно прочесть, ничего не стоит.
   ══════════════════════════════════════════════ */
.consent-body {
    display: block;
    padding: 24px 16px 60px;
}
.consent-box {
    max-width: 640px;
    margin: 0 auto;
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--radius-sm);
    padding: 28px;
}
.consent-box h1 { margin: 0 0 12px; font-size: 22px; }
.consent-lead { font-size: 15px; line-height: 1.6; margin: 0 0 20px; }

/* Сам текст согласия. Отделён рамкой: видно, где кончается обращение
   к человеку и начинается документ, под которым он подписывается. */
.consent-doc {
    border: 1px solid var(--border);
    border-radius: var(--radius-xs);
    background: var(--surface-2);
    padding: 18px 20px;
    margin-bottom: 20px;
    font-size: 14px;
    line-height: 1.65;
    /* Длинное согласие не должно выталкивать кнопку за экран */
    max-height: 52vh;
    overflow-y: auto;
}
.consent-doc h2 {
    font-size: 14.5px;
    margin: 18px 0 6px;
    letter-spacing: -.01em;
}
.consent-doc h2:first-child { margin-top: 0; }
.consent-doc p { margin: 0 0 10px; }
.consent-doc ul { margin: 0 0 10px; padding-left: 20px; }
.consent-doc li { margin-bottom: 4px; }

/* Врезка для того, что человек обязан заметить: про несовершеннолетних
   у ученика и про роль оператора у репетитора. */
.consent-note {
    margin-top: 16px;
    padding: 12px 14px;
    border-radius: var(--radius-xs);
    background: var(--orange-bg);
    color: var(--text);
    font-size: 13.5px;
    line-height: 1.6;
}

/* Список документов у репетитора: название ссылкой, под ним — строка
   о том, что внутри. Без рамки и прокрутки: их всего два, и прятать их
   в скроллящийся блок, как текст согласия ученика, незачем. */
.consent-list { list-style: none; margin: 0 0 18px; padding: 0; }
.consent-list li {
    padding: 10px 0;
    border-top: 1px solid var(--border);
    font-size: 14.5px;
}
.consent-list li:first-child { border-top: none; }
.consent-list a { font-weight: 600; }
.consent-list span { display: block; color: var(--muted); font-size: 13px; margin-top: 2px; }

.consent-form { margin-bottom: 14px; }
.consent-form .legal-consent { font-size: 14px; margin-bottom: 16px; }
.consent-exit { font-size: 13px; color: var(--muted); text-align: center; margin: 0; }

@media (max-width: 560px) {
    .consent-box { padding: 20px 16px; border-radius: var(--radius-xs); }
    .consent-doc { padding: 14px; max-height: none; }
}

/* Галочка согласия с документами на странице входа.
   Текст мелкий, но не серый: это не сноска, а то, с чем человек
   соглашается, — и читаться должно нормально. Галочка прижата к первой
   строке, иначе на трёх строках текста она уезжает в середину. */
.legal-consent {
    display: flex;
    align-items: flex-start;
    gap: 9px;
    margin: 4px 0 16px;
    font-size: 12.5px;
    line-height: 1.5;
    cursor: pointer;
}
.legal-consent input { margin-top: 2px; flex-shrink: 0; }
.legal-consent a { text-decoration: underline; }

/* Таблица диагностики вебхука.
   Две колонки «что спросили — что ответили»: адреса длинные, поэтому
   значение переносится и на телефоне уезжает под подпись, а не за
   правый край экрана. */
.tg-diag { margin-top: 12px; }
.tg-diag-table { width: 100%; border-collapse: collapse; font-size: 13px; }
.tg-diag-table th {
    text-align: left;
    vertical-align: top;
    font-weight: 600;
    color: var(--muted);
    padding: 7px 12px 7px 0;
    width: 42%;
    border-top: 1px solid var(--border);
}
.tg-diag-table td {
    padding: 7px 0;
    border-top: 1px solid var(--border);
    word-break: break-all;
}
.tg-diag-table tr:first-child th,
.tg-diag-table tr:first-child td { border-top: none; }
@media (max-width: 560px) {
    .tg-diag-table th, .tg-diag-table td { display: block; width: auto; }
    .tg-diag-table td { padding-top: 0; border-top: none; }
}

.setup-step-body { min-width: 0; flex: 1; }
.setup-step-body b { display: block; margin-bottom: 2px; font-size: 14.5px; }
.setup-step-body p { margin: 4px 0 0; font-size: 13.5px; line-height: 1.55; }
.setup-step-body label { display: block; margin-bottom: 10px; font-size: 13px; font-weight: 600; }
.setup-step-body input[type=text],
.setup-step-body input[type=url] { width: 100%; max-width: 460px; }
.setup-step-body code {
    background: var(--surface-2);
    padding: 1px 5px;
    border-radius: 5px;
    font-size: 12.5px;
}
/* Ряд вспомогательных кнопок внутри шага («Курсы», «Диагностика»,
   «Обновить кэш»). Каждая кнопка-форма — свой элемент, поэтому обёртка
   флексовая: иначе формы встают друг под друга столбиком. */
.setup-tools { display: flex; flex-wrap: wrap; gap: 8px; align-items: center; margin-top: 10px; }
.setup-tools form { margin: 0; }

/* ===========================================================================
   Плашка о cookie
   ===========================================================================
   Внизу по центру, а не полосой во всю ширину: полоса закрывает первое,
   на что человек смотрит, и её закрывают не читая. У левого края плашка
   выглядела приклеенной сбоку — центр читается как обращение к человеку,
   а не как элемент интерфейса, забытый в углу. */
.cookie-note {
    position: fixed;
    left: 50%;
    transform: translateX(-50%);
    bottom: 20px;
    z-index: 900;
    width: max-content;
    max-width: min(520px, calc(100vw - 32px));
    display: flex;
    align-items: center;
    gap: 14px;
    padding: 14px 16px;
    background: var(--text);
    color: #fff;
    border-radius: var(--radius);
    box-shadow: var(--shadow-lift);
    font-size: 13px;
    line-height: 1.45;
    animation: cookieUp .35s ease both;
}
@keyframes cookieUp {
    from { opacity: 0; transform: translate(-50%, 14px); }
    to   { opacity: 1; transform: translate(-50%, 0); }
}
.cookie-note.is-hiding {
    opacity: 0;
    transform: translate(-50%, 14px);
    transition: opacity .25s ease, transform .25s ease;
}
.cookie-note a { color: #fff; text-decoration: underline; }
.cookie-note-btn {
    flex-shrink: 0;
    border: none;
    cursor: pointer;
    background: #fff;
    color: var(--text);
    font: inherit;
    font-weight: 700;
    padding: 8px 16px;
    border-radius: 999px;
}
.cookie-note-btn:hover { background: var(--accent-bg); }
@media (max-width: 760px), ((max-height: 480px) and (hover: none) and (pointer: coarse)) {
    /* Выше нижнего таббара — иначе плашка перекрывает кнопки навигации. */
    .cookie-note {
        left: 10px; right: 10px;
        width: auto; max-width: none;
        transform: none;
        bottom: calc(78px + env(safe-area-inset-bottom, 0px));
        flex-wrap: wrap;
    }
    @keyframes cookieUp { from { opacity: 0; transform: translateY(14px); } to { opacity: 1; transform: none; } }
    .cookie-note.is-hiding { transform: translateY(14px); }
}
/* Тост и плашка о cookie живут в одном месте экрана — пока плашка висит,
   поднимаем тосты над ней. */
body:has(.cookie-note) .toast-wrap { bottom: 96px; }

/* ===========================================================================
   Префикс автора в журнале действий
   ===========================================================================
   Намеренно НЕ похож на .badge: рядом в той же строке стоят пастельные
   пилюли «создано / изменено / удалено», и роль автора, оформленная так же,
   читалась как ещё одно непонятное состояние записи. Здесь другая форма
   (скруглённый прямоугольник, не пилюля), плотный цвет вместо пастельного,
   белый текст и точка-маркер слева — четыре отличия сразу, чтобы глаз не
   путал «кто это сделал» с «что произошло». */
/* Ролевой префикс: аккуратная подпись цветом роли, обычным шрифтом.
   Моноширинный «шрифт кодера» здесь читался как отладочный вывод. От
   пилюль состояния («создано», «изменено») отличается тем, что это не
   заливка, а тонкая рамка своим цветом — форма другая, спутать нельзя. */
.log-actor {
    display: inline-flex;
    align-items: center;
    padding: 1px 8px;
    border-radius: 999px;
    border: 1px solid currentColor;
    font-size: 11px;
    font-weight: 700;
    line-height: 1.6;
    white-space: nowrap;
    vertical-align: middle;
    color: var(--muted);
}
.log-actor-red { color: var(--red); }
.log-actor-green { color: var(--green); }
.log-actor-gray { color: var(--muted); }
.log-actor-note { color: var(--muted); font-size: 12px; }

/* ===========================================================================
   Список «что входит в доступ»
   ===========================================================================
   Живёт на странице тарифа под герой-карточкой: две колонки галочек, где
   выключенное администратором помечено крестиком и приглушено — обещать
   то, чего у человека нет, нельзя. */
.plan-perks { display: grid; grid-template-columns: repeat(auto-fit, minmax(190px, 1fr)); gap: 8px 18px; margin: 14px 0 0; padding: 0; list-style: none; }
.plan-perks li { display: flex; align-items: flex-start; gap: 9px; font-size: 13.5px; line-height: 1.5; }
.plan-perks li::before { content: '✓'; font-weight: 800; color: var(--green); }
.plan-perks li.is-off { color: var(--muted); }
.plan-perks li.is-off::before { content: '×'; color: var(--gray); }

/* Витрина уровней: карточка = цветная шапка с ценой + список того, что
   откроется. Прежняя версия (тонкая полоска сверху, цена мелким шрифтом
   сбоку и перечень функций без пояснения) не продавала ничего: по ней
   нельзя было за секунду понять ни сколько это стоит в месяц, ни что
   человек получит сверх того, что у него уже есть. */
.tier-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
    gap: 14px;
    margin-top: 12px;
}
/* Карточка уровня — светлое тело и ОДНОРОДНАЯ цветная шапка.
   ===========================================================================
   Три попытки до этой чинили ФОРМУЛУ заливки (проценты, подмешивание
   чёрного или белого) — и каждая проигрывала одному и тому же
   ограничению: пастельная палитра (мятный, светло-голубой) физически не
   даёт одновременно яркость и контраст. Либо заливка светлая и текст
   приходится делать тёмным (жаловались — «чёрный», «не видно»), либо
   заливку подмешивают к чёрному ради контраста белого текста — и тогда
   каждый цвет тускнеет и все пять карточек начинают сливаться в одно и то
   же тёмное пятно (жаловались — «градиент от светлого к тёмному»,
   «блёкло»).

   Верный ответ был не в CSS, а в самой ПАЛИТРЕ (plan_tiers_defaults):
   пять достаточно тёмных и насыщенных тонов, на которых белый текст
   держит контраст без единой примеси. Заливка шапки — буквально
   var(--tier), сырой цвет уровня, без единого процента смешивания: то,
   что и называют «монотонным ярким цветом». Все пять уровней оформлены
   ОДИНАКОВО, без исключений для конкретного тарифа — искать разницу
   между «Premium как он был» и «Ultimate+ не трогай» больше не нужно,
   разницы в оформлении нет вовсе, только в самом цвете.

   Тот же var(--tier) без примеси стоит в редакторе уровней (.pf-tier-head
   в platform.css) — владелец видит там ровно то, что получит клиент
   здесь. */
.tier-card {
    --tier: var(--accent);
    display: flex;
    flex-direction: column;
    border-radius: var(--radius);
    background: var(--surface);
    box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--tier) 26%, var(--border));
    overflow: hidden;
    transition: transform .16s ease, box-shadow .16s ease;
}
.tier-card:hover {
    transform: translateY(-2px);
    box-shadow: inset 0 0 0 1.5px color-mix(in srgb, var(--tier) 55%, var(--border)),
                0 10px 24px -14px color-mix(in srgb, var(--tier) 70%, transparent);
}
/* Верхний (самый дорогой) уровень — рамка вдвое толще плюс лёгкое свечение
   под карточкой, чтобы разница была видна даже мельком, а не только при
   пристальном взгляде на цвет рамки. Раньше здесь стояла радужная рамка
   градиентом (бирюза → индиго → розовый) — три произвольных цвета, не
   имеющих отношения ни к уровню, ни к остальному кабинету. */
.tier-card.is-top {
    box-shadow: inset 0 0 0 2.5px var(--tier),
                0 14px 28px -16px color-mix(in srgb, var(--tier) 65%, transparent);
    transform: translateY(-3px);
}
.tier-card.is-top:hover {
    box-shadow: inset 0 0 0 2.5px var(--tier),
                0 18px 34px -16px color-mix(in srgb, var(--tier) 70%, transparent);
    transform: translateY(-5px);
}
.tier-head {
    display: grid;
    grid-template-columns: minmax(0, 1fr) auto;
    align-items: center;
    gap: 8px 14px;
    padding: 16px 20px 15px;
    background: var(--tier);
    color: #fff;
}
.tier-title { display: flex; align-items: center; gap: 9px; flex-wrap: wrap; min-width: 0; }
.tier-name {
    font-family: var(--font-display);
    font-size: 21px; font-weight: 800; letter-spacing: -.04em;
    color: #fff;
}
/* «Всё сразу» — контурная пилюля: заливка на уже заливной шапке
   превратилась бы в плашку внутри плашки. */
.tier-flag {
    padding: 3px 10px 4px;
    border-radius: 999px;
    border: 1.5px solid rgba(255, 255, 255, .8);
    color: #fff;
    font-size: 10.5px; font-weight: 800; letter-spacing: .06em; text-transform: uppercase;
    white-space: nowrap;
}
.tier-cost { text-align: right; line-height: 1.1; font-family: var(--font-display); white-space: nowrap; }
.tier-cost b {
    display: block;
    font-size: 24px; font-weight: 800;
    /* Отрицательный трекинг ОСТАЁТСЯ — виноват был не он, а tabular-nums.
       ===================================================================
       Прошлый заход здесь снял и то, и другое разом, решив, что дело в
       трекинге. Проверил числами (getBoundingClientRect для «1» и «59» по
       отдельности и вместе, при разном letter-spacing) — CSS никакой
       лишний зазор не добавляет, ширины складываются ровно. «Дыра» в
       «159» — это собственная светлая часть глифа «1» в Onest 800: у
       цифры маленькое «очко» и широкий апрош, чтобы не путалась с «l» —
       так и задумано шрифтом, не баг вёрстки.
       Лечится ровно тем, для чего трекинг и существует: -.02em чуть
       сдвигает соседние цифры друг к другу и визуально закрывает эту
       собственную пустоту глифа. Именно tabular-nums был настоящим
       виновником «1 49» — он растягивал «1» на ширину «8», и тогда пустота
       становилась в разы больше любого трекинга. Его и не возвращаем. */
    letter-spacing: -.02em;
    color: #fff;
}
/* Подпись «в месяц» — сплошным белым, а не полупрозрачным: плашка теперь
   яркая (чистый цвет уровня), и полупрозрачный белый на ней уходил в
   молоко. Плата за яркую палитру — см. plan_tiers_defaults. */
.tier-cost span { font-size: 12px; font-weight: 700; color: #fff; }
.tier-body { flex: 1; display: flex; flex-direction: column; gap: 12px; padding: 16px 20px 18px; }
.tier-summary { margin: 0; font-size: 13px; line-height: 1.5; color: var(--muted); }
.tier-gains-title {
    margin: 0; font-size: 10.5px; font-weight: 800;
    letter-spacing: .07em; text-transform: uppercase; color: var(--muted);
}
.tier-gains { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 7px; }
.tier-gains li { display: flex; gap: 9px; font-size: 13.5px; line-height: 1.4; font-weight: 600; }
.tier-gains li::before { content: '✓'; color: var(--tier); font-weight: 800; }
[data-theme="dark"] .tier-gains li::before { color: color-mix(in srgb, var(--tier) 60%, #ffffff); }
.tier-card .btn { margin-top: auto; border-radius: 999px; }
.tier-card .btn-primary {
    /* Кнопку, наоборот, притемняем: она стоит на белом теле карточки, а не
       на цветной шапке, и чистый мятный или янтарный с белым текстом на
       белом фоне не читался бы. */
    background: color-mix(in srgb, var(--tier) 82%, #10131c);
    border-color: transparent;
    /* Цвет текста задаём явно: в тёмной теме .btn-primary красит его
       тёмным под свой светлый фон, а тут фон — цвет уровня. */
    color: #fff;
}
.tier-card .btn-primary:hover { background: color-mix(in srgb, var(--tier) 92%, #10131c); }
.tier-foot { font-size: 11.5px; line-height: 1.4; color: var(--muted); }

/* Компактная строка о тарифе в шапке настроек и на «Сегодня» */
.plan-strip {
    display: flex;
    align-items: center;
    gap: 10px;
    flex-wrap: wrap;
    padding: 10px 14px;
    border-radius: var(--radius-sm);
    background: var(--surface-2);
    font-size: 13px;
}
.plan-strip b { color: var(--text); }
.plan-strip .plan-kind { color: var(--accent); opacity: 1; }

/* ===========================================================================
   Переписка с поддержкой
   ===========================================================================
   Это чат, и выглядеть должен как чат: свои сообщения справа, ответы
   поддержки слева, скруглённые пузыри с «хвостиком» — угол, срезанный к
   своей стороне. Пробовал плоский вариант со всем текстом слева и
   разделительной линией: формально аккуратно, а на деле переписка
   перестала читаться как разговор.

   Ролевой префикс (S. Admin / Admin) стоит в шапке пузыря — тот же, что
   в журнале действий: кто пишет, видно одинаково во всём продукте. */
.support-thread { display: flex; flex-direction: column; gap: 10px; margin-top: 16px; }
.support-msg {
    max-width: 78%;
    padding: 11px 14px;
    border-radius: 18px 18px 18px 5px;
    background: var(--surface-2);
    align-self: flex-start;
}
.support-msg.is-mine {
    align-self: flex-end;
    border-radius: 18px 18px 5px 18px;
    background: var(--accent-bg);
}
.support-msg-head {
    display: flex; align-items: center; gap: 8px; flex-wrap: wrap;
    font-size: 12.5px; margin-bottom: 5px; min-width: 0;
}
.support-msg-head b { min-width: 0; overflow-wrap: anywhere; font-weight: 700; }
.support-msg-time { color: var(--muted); font-size: 11.5px; }
.support-msg-body { font-size: 14px; line-height: 1.55; word-break: break-word; }

/*
 * На телефоне рамка карточки вокруг переписки давала «коробку в коробке»:
 * скруглённые пузыри внутри скруглённой карточки, двойные отступы по краям
 * и пузырь шириной в половину экрана. Лента сообщений и есть содержимое
 * страницы — обрамлять её нечем, поэтому на узком экране внешняя карточка
 * распрямляется в обычный блок.
 */
@media (max-width: 620px) {
    .form-card.is-flat-mobile {
        border: none;
        background: transparent;
        box-shadow: none;
        padding: 0;
        border-radius: 0;
    }
    .form-card.is-flat-mobile > h2 { margin-top: 0; }
    .support-msg { max-width: 90%; padding: 10px 13px; }
    .support-thread { gap: 8px; }
}

/* Тема и статус обращения — компактные подписи со скруглением, чтобы не
   спорить с бейджами занятий рядом. */
.support-tag {
    display: inline-flex; align-items: center; gap: 5px;
    padding: 3px 11px; border-radius: 999px;
    background: var(--surface-2);
    font-size: 11.5px; font-weight: 700; color: var(--muted);
    white-space: nowrap;
}
.support-tag-open { background: var(--orange-bg); color: var(--orange); }
.support-tag-answered { background: var(--green-bg); color: var(--green); }

/* Ответ поддержки в общей ленте панели — то же самое, но в «плоском» виде */
/* Те же правила, что и у .support-msg в кабинете: ответы поддержки
   справа (здесь пишем мы), вопрос клиента слева. */
.pf-thread { display: flex; flex-direction: column; gap: 10px; }
.pf-msg {
    max-width: 78%;
    padding: 11px 14px;
    border-radius: 18px 18px 18px 5px;
    background: var(--surface-2);
    align-self: flex-start;
}
.pf-msg.is-staff {
    align-self: flex-end;
    border-radius: 18px 18px 5px 18px;
    background: var(--accent-bg);
}
.pf-msg-head { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; font-size: 12.5px; margin-bottom: 5px; min-width: 0; }
.pf-msg-head b { min-width: 0; overflow-wrap: anywhere; font-weight: 700; }
.pf-msg-time { color: var(--muted); font-size: 11.5px; }
.pf-msg-body { font-size: 14px; line-height: 1.55; word-break: break-word; }

/* ===========================================================================
   ШАПКА РАЗДЕЛА
   ===========================================================================
   Синяя карточка с цифрами и кнопками наверху экрана, светлые блоки под
   ней. Пробовал заменить её плоской панелью в духе промо-страницы —
   получилось хуже: на витрине плоские чёрные линии работают, потому что
   там читают текст, а в рабочем кабинете нужен ВЕС. Когда все блоки
   одинаковые, взгляд не цепляется ни за что и страница читается
   построчно, как договор.

   Один класс на все разделы (тариф, приглашения, поддержка, сводки)
   намеренно: три похожих, но чуть разных шапки — это три места, где
   вёрстка потом разъедется. */
/* ===========================================================================
   ШАПКА ГЛАВНОЙ («Сегодня», «Приглашения»)
   ===========================================================================
   Синяя — и это проверено дважды. Светлый вариант пробовали: он не спорил
   с остальным кабинетом, но и не работал — шапка переставала быть шапкой и
   сливалась с карточками под собой. Цвет здесь не украшение, а граница:
   выше неё «где я и что происходит», ниже — работа.

   Чего в ней больше нет — двух размытых белых кругов по углам, один из
   которых ещё и медленно «дышал». Свет ниоткуда, который сам по себе
   двигается, — приём из оформления пятилетней давности; он ничего не
   означал и тянул взгляд с цифр.

   Вместо них рисунок тонкой линией в правом нижнем углу (nav_art): ручка с
   росчерком на «Сегодня», два человека на «Приглашениях». Он про то, чем
   страница занята, уезжает за край карточки и не двигается.

   Раскладка — сверху вниз, а не в две колонки. Прежде цифры стояли
   отдельным столбцом справа от текста и на широком экране улетали к
   дальнему краю: «0 проведено» оказывалось в полуметре от заголовка, к
   которому относилось. Теперь это строка под текстом — с разделителями и
   общей базовой линией, читается как одна сводка, а не как три числа,
   рассыпанных по плашке. */
.hero {
    position: relative;
    overflow: hidden;
    border-radius: var(--radius-lg);
    padding: 22px 24px;
    margin-bottom: 16px;
    color: #fff;
    background: linear-gradient(135deg, #1b2a4a 0%, #2a3f6d 55%, #3457d5 130%);
    box-shadow: var(--shadow);
}
.hero .card-art {
    position: absolute;
    /* Снизу почти вровень с краем: иначе обрезался росчерк под ручкой и
       от него торчало «начало какой-то фигуры». */
    right: -8px; bottom: -4px;
    width: 152px; height: 152px;
    color: #fff;
    opacity: .17;
    pointer-events: none;
}

.hero-main { position: relative; max-width: 560px; }
/* Старая двухколоночная раскладка: осталась для страниц, которые ещё не
   переведены на .hero-main + .hero-stats. */
.hero-row { position: relative; display: flex; align-items: flex-start; justify-content: space-between; gap: 24px; flex-wrap: wrap; }

/* Надпись над заголовком — обычным шрифтом. Моноширинный «шрифт кодера»
   здесь смотрелся как отладочный вывод, а не как подпись. */
.hero-kicker {
    display: block;
    font-size: 12.5px; font-weight: 600; letter-spacing: .01em;
    opacity: .7; margin-bottom: 6px;
}
.hero h2 {
    margin: 0 0 6px;
    font-family: var(--font-display);
    font-size: 24px; font-weight: 800; letter-spacing: -.045em;
    color: #fff;
}
.hero p { margin: 0; opacity: .8; font-size: 13.5px; line-height: 1.5; max-width: 520px; }

/* Сводка одной строкой: подпись сверху мелким, число под ней крупным,
   между колонками тонкая линия. Подпись именно СВЕРХУ — так у всех чисел
   общая нижняя линия, и ряд читается как таблица, а не как набор
   разнокалиберных подписей. */
.hero-stats {
    position: relative;
    display: flex;
    flex-wrap: wrap;
    margin: 18px 0 0;
    padding-top: 16px;
    border-top: 1px solid rgba(255, 255, 255, .18);
}
.hero-stat {
    padding: 0 22px;
    border-left: 1px solid rgba(255, 255, 255, .18);
}
.hero-stat:first-child { padding-left: 0; border-left: none; }
a.hero-stat { color: inherit; text-decoration: none; }
a.hero-stat:hover b { text-decoration: underline; text-underline-offset: 3px; }
.hero-stat span {
    display: block;
    font-size: 10.5px; font-weight: 700; letter-spacing: .07em; text-transform: uppercase;
    opacity: .6;
    margin-bottom: 3px;
    white-space: nowrap;
}
.hero-stat b {
    display: block;
    font-family: var(--font-display);
    font-size: 25px; font-weight: 800; line-height: 1.05; letter-spacing: -.04em;
    white-space: nowrap;
}

/* Старые «фигуры» — та же роль, но без разделителей; оставлены для
   страниц на .hero-row. */
.hero-figures { position: relative; display: flex; gap: 24px; flex-wrap: wrap; }
.hero-fig { min-width: 84px; }
.hero-fig b {
    display: block;
    font-family: var(--font-display);
    font-size: 26px; font-weight: 800; line-height: 1.1; letter-spacing: -.04em;
}
.hero-fig span { font-size: 12px; opacity: .72; }

.hero-bar { position: relative; height: 6px; border-radius: 999px; background: rgba(255, 255, 255, .2); margin: 18px 0 0; overflow: hidden; }
.hero-bar span { display: block; height: 100%; border-radius: 999px; background: #fff; }
.hero-actions { position: relative; display: flex; gap: 8px; flex-wrap: wrap; margin-top: 18px; }
.hero-btn {
    display: inline-flex; align-items: center; gap: 6px;
    padding: 10px 20px; border-radius: 999px;
    background: #fff; color: #16202e; font-weight: 700; font-size: 13.5px;
    text-decoration: none; border: none; cursor: pointer; font-family: inherit;
    transition: background .12s ease;
}
.hero-btn:hover { background: #eef2ff; text-decoration: none; color: #16202e; }
.hero-btn-ghost { background: rgba(255, 255, 255, .16); color: #fff; }
.hero-btn-ghost:hover { background: rgba(255, 255, 255, .26); color: #fff; }

@media (max-width: 620px) {
    /* На телефоне рисунок мельче и глубже в углу: на узкой карточке он
       иначе подлезал под кнопку. */
    /* Уезжал за край почти наполовину и прятался под растянутой на всю
       ширину кнопкой. Теперь кнопка своей ширины (см. .hero-actions ниже),
       а ручка целиком помещается справа от неё. */
    .hero .card-art { width: 124px; height: 124px; right: -8px; bottom: -2px; }
    /* Строка сводки на телефоне — сетка 2×2, а не перенос флекс-строки.
       ===================================================================
       При четырёх плитках (Проведено/Ученик/За сегодня/Долг) в узкую
       строку помещались три, четвёртая переносилась сама по себе — но
       граница-разделитель (border-left) снимается только у САМОГО
       первого элемента во всём ряду, а не у первого в КАЖДОЙ визуальной
       строке. Перенесённая плитка получала чужой отступ и чужую линию
       слева, ничему не соответствующую, — отсюда и было «всё съезжает».
       Сетка с фиксированным числом колонок избавляет от переноса как
       явления: плитки всегда стоят в собственных ячейках, при любом их
       количестве (3 или 4). Разделители в виде границ на сетке смотрелись
       бы линиями через случайные ячейки, поэтому здесь их роль играет
       обычный зазор между ячейками. */
    .hero-stats {
        display: grid;
        grid-template-columns: 1fr 1fr;
        column-gap: 18px;
        row-gap: 12px;
        padding-top: 14px;
        margin-top: 14px;
    }
    .hero-stat { padding: 0; border-left: none; }
    .hero-stat b { font-size: 21px; }
}

/* ===========================================================================
   ВЫБОР СРОКА ПРОДЛЕНИЯ
   ===========================================================================
   Тарифы — это выбор, а выбор показывают рядом, а не списком в столбик:
   так видно, что месяц стоит столько, а полгода — вот столько, и второе
   выгоднее. Цены и сроки задаёт владелец платформы (platform/plans.php),
   здесь только витрина. */
/* Сроки — отдельными карточками, а не строками таблицы с линейками между
   ними. Линейки читались как «список настроек», хотя это витрина: выбор
   между месяцем и годом. Пастельная подложка, скруглённые углы, стрелка
   уезжает вправо при наведении — карточка ведёт себя как кнопка, потому
   что она и есть кнопка. */
/* Колонки общие на весь список, а не свои у каждой строки.
   ===========================================================================
   Прошлая попытка выровнять цены (min-width у колонки цены) держалась ровно
   до первой длинной суммы: каждая строка была отдельной сеткой, колонки у
   соседних строк не общие, и «14 900 ₽» раздвигал свою строку, а «149 ₽»
   оставлял узкой — стрелки в конце строк снова разъезжались.

   Здесь колонки задаёт СПИСОК, а строки их наследуют через subgrid. Ширина
   колонки цены считается по самой длинной цене и одинакова у всех строк,
   сколько бы разрядов в них ни было. min-width ниже оставлен запасным
   вариантом для браузеров без subgrid. */
.plan-grid {
    display: grid;
    grid-template-columns: minmax(0, 1fr) auto auto;
    gap: 8px;
}
.plan-opt {
    grid-column: 1 / -1;
    display: grid;
    grid-template-columns: minmax(0, 1fr) auto auto;
    align-items: center;
    gap: 14px;
    padding: 14px 18px;
    border-radius: var(--radius-sm);
    background: var(--surface);
    box-shadow: inset 0 0 0 1px var(--border);
    text-decoration: none;
    color: var(--text);
    transition: background .14s ease, box-shadow .14s ease, transform .14s ease;
}
.plan-opt:hover {
    background: color-mix(in srgb, var(--accent) 8%, var(--surface));
    box-shadow: inset 0 0 0 1.5px color-mix(in srgb, var(--accent) 34%, transparent);
    text-decoration: none;
    transform: translateY(-1px);
}
.plan-opt:hover .plan-opt-go { transform: translateX(3px); }
/* Строка истории не кликается — и подсвечиваться при наведении не должна:
   подсветка обещает переход, которого нет. */
.plan-opt.is-static { cursor: default; }
.plan-opt.is-static:hover { background: var(--surface); box-shadow: inset 0 0 0 1px var(--border); transform: none; }
.plan-opt-name { font-size: 15px; font-weight: 700; }
.plan-opt-tag {
    margin-left: 8px; padding: 2px 9px 3px; border-radius: 999px;
    background: color-mix(in srgb, var(--green) 16%, var(--surface));
    color: var(--green-dark);
    font-size: 10.5px; font-weight: 800; white-space: nowrap;
}
/* Старая цена рядом с новой: видно, от чего считали скидку. */
.plan-opt-price s { display: block; font-size: 12px; font-weight: 600; color: var(--muted); }
.plan-opt-note { font-size: 12.5px; color: var(--muted); display: block; font-weight: 400; }
/* Цена: моноширинные цифры и своя минимальная ширина.
   ===========================================================================
   Цифры «скакали» по двум причинам сразу.

   Первая: каждая строка — отдельная grid-сетка (карточка сама по себе), а
   значит колонки у соседних строк НЕ общие. «400 ₽» и «2 040 ₽» получали
   разную ширину, и вслед за ними разъезжались стрелки — глаз читал это
   как дрожание. Отсюда min-width: колонка цены одинаковая у всех строк.

   Вторая: в Onest цифры по умолчанию пропорциональные — единица уже
   восьмёрки, и число «1 200» выглядит рваным. tabular-nums включает
   моноширинный набор, для колонки цен он и нужен. Заодно ослаблен
   отрицательный трекинг: на цифрах он слипал разряды. */
.plan-opt-price {
    font-family: var(--font-display);
    font-size: 19px; font-weight: 800; letter-spacing: -.02em; white-space: nowrap;
    font-variant-numeric: tabular-nums;
    min-width: 104px;
    text-align: right;
}
/* Там, где subgrid поддерживается, строки берут колонки у списка — и
   выравнивание перестаёт зависеть от того, угадана ли min-width. */
@supports (grid-template-columns: subgrid) {
    .plan-opt { grid-template-columns: subgrid; }
}
.plan-opt-go {
    font-size: 15px; font-weight: 700; color: var(--accent); white-space: nowrap;
    transition: transform .14s ease;
}

/* ===========================================================================
   ПРИГЛАСИТЕЛЬНЫЕ ССЫЛКИ
   ===========================================================================
   Ссылку нужно скопировать и отправить — значит главное на карточке это
   сама ссылка и кнопка «Скопировать», а не таблица с восемью столбцами,
   в которой ссылка ужата до нечитаемого поля. */
.invite-list { display: flex; flex-direction: column; gap: 12px; }
.invite-card {
    border: 1px solid var(--border);
    border-radius: var(--radius);
    background: var(--surface);
    padding: 16px 18px;
}
.invite-card.is-dead { opacity: .6; }
.invite-top { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; margin-bottom: 12px; }
.invite-name { font-weight: 700; font-size: 15px; }
.invite-meta { color: var(--muted); font-size: 12.5px; margin-left: auto; }
.invite-copy { display: flex; gap: 8px; align-items: stretch; flex-wrap: wrap; }
.invite-copy input {
    flex: 1 1 240px; min-width: 0; margin: 0;
    font-family: ui-monospace, 'SF Mono', Consolas, monospace;
    font-size: 12.5px;
    background: var(--surface-2);
    border: 1px solid var(--border);
    border-radius: var(--radius-xs);
    padding: 9px 12px;
    color: var(--text);
}
.invite-actions { display: flex; gap: 8px; align-items: center; margin-top: 12px; flex-wrap: wrap; }
.invite-dead-note { font-size: 13px; color: var(--muted); }

/* ===========================================================================
   ПОЛЕ С СЕКРЕТОМ
   ===========================================================================
   Токен бота, ключ доступа к сообществу, пароль от веб-сервиса Moodle.
   Значение размыто, пока его не попросят показать: страницу «Подключения»
   открывают на созвоне и на записи экрана, а токен бота в записи — это
   чужой доступ к переписке со всеми учениками.

   Размытие, а не звёздочки: с ним видно, что поле заполнено и сколько там
   примерно символов, — этого достаточно, чтобы не гадать «сохранилось или
   нет». Как только в поле ставят курсор, оно проясняется само: править
   вслепую нельзя.

   Кнопка стоит РЯДОМ с полем, а не поверх него.
   ===========================================================================
   Раньше «Показать» лежала абсолютом внутри поля, а поле получало
   padding-right в 92 пикселя. На телефоне это ещё сходилось, на широком
   экране — нет: токен Moodle это 32 моноширинных знака, и последние из них
   уезжали под кнопку. Человек видел обрезанное значение и не понимал,
   сохранилось оно целиком или нет.

   Флекс-строка решает это без единой магической цифры: полю остаётся вся
   ширина, кнопка занимает ровно столько, сколько занимает её текст, и на
   узком экране переносится вниз сама.

   Пустое поле не размывается и кнопки не получает вовсе (см. is-empty
   в secret_field()): размывать нечего, показывать нечего. */
.secret-field { display: flex; align-items: stretch; gap: 8px; flex-wrap: wrap; }
.secret-input {
    flex: 1 1 240px;
    min-width: 0;
    margin: 0;
    /* Моноширинный: токен читают и сверяют посимвольно. */
    font-family: ui-monospace, 'SF Mono', Consolas, monospace;
    font-size: 13px;
    filter: blur(4.5px);
    transition: filter .14s ease;
}
.secret-input:focus,
.secret-field.is-empty .secret-input,
.secret-field.is-shown .secret-input { filter: none; }
/* Пустое поле размывать нечего — плейсхолдер должен читаться. Правило
   оставлено для полей, очищенных прямо в браузере: класс is-empty ставится
   на сервере и про такое не знает. */
.secret-input:placeholder-shown { filter: none; }
.secret-toggle {
    flex: 0 0 auto;
    padding: 5px 13px;
    border: none;
    border-radius: 999px;
    background: var(--surface-2);
    color: var(--muted);
    font: inherit; font-size: 12px; font-weight: 700;
    cursor: pointer;
    white-space: nowrap;
}
.secret-toggle:hover { background: var(--accent-bg); color: var(--accent-dark); }
.secret-field.is-empty .secret-toggle { display: none; }

/* ===========================================================================
   ЗАМЕТКА ВНУТРИ СТРАНИЦЫ
   ===========================================================================
   Родня .top-note (плашки в шапке — «вы в режиме поддержки», «доступ
   заканчивается»), но для сообщений внутри самой страницы: «упёрлись в
   лимит учеников», «раздел пока пуст, потому что...».

   Раньше такие сообщения писались через .alert — цветной абзац оранжевым
   текстом на оранжевом фоне, наследие первых версий кабинета. Он и правда
   выглядел старо: одна строка одного кегля, без заголовка, без действия,
   без места, за которое глаз мог бы зацепиться.

   Здесь та же структура, что и у плашек в шапке — значок, заголовок,
   пояснение, кнопка, — но оформление спокойнее: пастельная заливка в цвет
   смысла и тонкий внутренний контур вместо жёсткой смещённой тени.
   Смещённая тень уместна в шапке, где плашка перебивает работу; внутри
   страницы она превращала бы каждое замечание в наклейку. */
.page-note {
    --tone: var(--accent);
    display: flex; align-items: flex-start; gap: 14px; flex-wrap: wrap;
    padding: 16px 18px;
    margin: 0 0 16px;
    border-radius: var(--radius);
    background: color-mix(in srgb, var(--tone) 10%, var(--surface));
    box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--tone) 22%, transparent);
    font-size: 13.5px; line-height: 1.45;
}
.page-note.is-warn  { --tone: var(--orange); }
.page-note.is-alarm { --tone: var(--red); }
.page-note.is-good  { --tone: var(--green); }
/* Значок в круге цвета смысла: серьёзность сообщения видна раньше, чем
   прочитан заголовок. */
.page-note-icon {
    flex-shrink: 0;
    display: inline-flex; align-items: center; justify-content: center;
    width: 40px; height: 40px;
    /* Скруглённый квадрат, а не круг: та же плашка, что под значками
       плиток профиля — один язык на весь кабинет. */
    border-radius: 14px;
    background: color-mix(in srgb, var(--tone) 85%, var(--surface));
    color: #fff;
}
.page-note-icon .nav-icon { width: 20px; height: 20px; }
.page-note-body { flex: 1 1 240px; min-width: 0; }
.page-note-title {
    display: block;
    font-family: var(--font-display);
    font-size: 16.5px; font-weight: 700; letter-spacing: -.02em; line-height: 1.25;
    color: color-mix(in srgb, var(--tone) 62%, var(--text));
}
.page-note-sub { display: block; margin-top: 4px; font-size: 13.5px; color: var(--muted); }
.page-note-sub b { color: var(--text); font-weight: 600; }
.page-note-actions { display: flex; gap: 8px; align-items: center; margin-left: auto; }
.page-note-btn {
    display: inline-block; padding: 9px 17px;
    border-radius: 999px; border: none; cursor: pointer;
    font: inherit; font-size: 13px; font-weight: 700; line-height: 1.2;
    background: color-mix(in srgb, var(--tone) 88%, var(--surface));
    color: #fff;
    text-decoration: none; white-space: nowrap;
}
.page-note-btn:hover { text-decoration: none; filter: brightness(1.08); }
[data-theme="dark"] .page-note-title { color: color-mix(in srgb, var(--tone) 45%, var(--text)); }
[data-theme="dark"] .page-note-icon,
[data-theme="dark"] .page-note-btn { color: #0b1220; }
@media (max-width: 560px) {
    .page-note-actions { margin-left: 0; width: 100%; }
    .page-note-btn { width: 100%; text-align: center; }
}

/* ===========================================================================
   ПЛАШКИ НАД СТРАНИЦЕЙ
   ===========================================================================
   Одна коробка на все сообщения в шапке: «срок кончается», «доступ
   продлён», «интеграция сломалась». Раньше каждое рисовалось по-своему —
   разные отступы, радиус и размер шрифта, — и две плашки подряд выглядели
   как две разные системы уведомлений.

   Оформление — как на витрине (sixerp.ru/promo): контурная карточка с
   жёсткой смещённой тенью, без заливок и без цветной полосы сбоку.
   Полоса была ошибкой: узкий цветной прямоугольник у края ничего не
   значит для читателя и выглядит наклейкой. Серьёзность сообщения несёт
   цвет самого контура и кнопки. */
.top-note {
    display: flex; align-items: center; gap: 14px; flex-wrap: wrap;
    margin: 14px 16px 0;
    padding: 15px 18px;
    border-radius: 14px;
    border: 1.5px solid var(--text);
    background: var(--surface);
    box-shadow: 4px 4px 0 color-mix(in srgb, var(--text) 12%, transparent);
    font-size: 13.5px; line-height: 1.45;
}
/* Заголовок и пояснение, а не один абзац мелким кеглем: в плашке должна
   быть строка, которую читают, и строка, которую дочитывают. Шрифт —
   тот же Golos Text, что во всём кабинете, просто крупнее и плотнее. */
.top-note-body { flex: 1 1 280px; min-width: 0; }
.top-note-title {
    display: block;
    font-size: 17px;
    font-weight: 700;
    letter-spacing: -.015em;
    line-height: 1.3;
    color: var(--text);
}
.top-note-sub {
    display: block;
    margin-top: 4px;
    font-size: 13.5px;
    font-weight: 500;
    line-height: 1.45;
    color: var(--muted);
}
.top-note-body b { font-weight: 700; }
.top-note-actions { display: flex; gap: 8px; align-items: center; margin-left: auto; }
.top-note-btn {
    display: inline-block; padding: 9px 18px; border-radius: 10px;
    border: 1.5px solid var(--text); cursor: pointer;
    font: inherit; font-size: 13px; font-weight: 700; line-height: 1.2;
    background: var(--text); color: var(--surface);
    text-decoration: none; white-space: nowrap;
}
.top-note-btn:hover { text-decoration: none; opacity: .88; }
.top-note.is-good  { border-color: var(--green); box-shadow: 4px 4px 0 color-mix(in srgb, var(--green) 22%, transparent); }
.top-note.is-good .top-note-btn { background: var(--green); border-color: var(--green); color: #fff; }
.top-note.is-warn  { border-color: var(--orange); box-shadow: 4px 4px 0 color-mix(in srgb, var(--orange) 25%, transparent); }
.top-note.is-warn .top-note-btn { background: var(--orange); border-color: var(--orange); color: #fff; }
.top-note.is-alarm { border-color: var(--red); box-shadow: 4px 4px 0 color-mix(in srgb, var(--red) 22%, transparent); }
.top-note.is-alarm .top-note-btn { background: var(--red); border-color: var(--red); color: #fff; }
/* Второстепенная — тот же контур, но пустая внутри. Правило стоит ПОСЛЕ
   цветных вариантов: у них та же специфичность, и раньше они перекрашивали
   и её тоже — «Понятно» выходило зелёным на зелёном. */
.top-note .top-note-btn.is-ghost {
    background: transparent;
    color: var(--text);
    border-color: color-mix(in srgb, var(--text) 45%, transparent);
}
.top-note .top-note-btn.is-ghost:hover { background: var(--surface-2); opacity: 1; }

@media (max-width: 560px) {
    .top-note { margin: 12px 10px 0; padding: 14px 15px; gap: 12px; box-shadow: 3px 3px 0 color-mix(in srgb, var(--text) 12%, transparent); }
    .top-note-actions { margin-left: 0; width: 100%; }
    .top-note-actions form { flex: 1 1 auto; }
    .top-note-btn { flex: 1 1 auto; text-align: center; width: 100%; }
}

/* ===========================================================================
   ПРОФИЛЬ
   ===========================================================================
   Кто я — что куплено — куда идти. Три блока подряд, без карточек внутри
   карточек: вложенные рамки съедали половину узкого экрана на воздух. */
.nav-ava-xl {
    width: 56px; height: 56px;
    font-size: 20px;
    box-shadow: 0 0 0 3px var(--bg), 0 0 0 5px var(--ring, var(--gray));
    margin: 4px;
}
[data-theme="dark"] .nav-ava-xl {
    box-shadow: 0 0 0 3px var(--bg), 0 0 0 5px var(--ring, var(--gray)), 0 0 0 6px rgba(255, 255, 255, .34);
}
/* Аватарка в шапке профиля вместе с кнопкой «сменить фото».
   Кнопка — маленькая плашка в нижнем углу кружка, как в мессенджерах:
   отдельной строкой «Загрузить фотографию» она бы соперничала за внимание
   с именем, ради которого сюда и заходят. */
.prof-ava { position: relative; display: inline-flex; flex-shrink: 0; }
.prof-ava-form { position: absolute; right: -1px; bottom: -1px; margin: 0; line-height: 0; }
/* Кружок с фотоаппаратом, а не подпись «Фото»: на аватарке 56 пикселей
   слово занимало почти всю ширину и закрывало само лицо. */
.prof-ava-edit {
    display: inline-flex; align-items: center; justify-content: center;
    width: 24px; height: 24px;
    border-radius: 50%;
    background: var(--accent);
    color: #fff;
    box-shadow: 0 0 0 2.5px var(--bg);
    cursor: pointer;
}
.prof-ava-edit svg { width: 13px; height: 13px; }
.prof-ava-edit:hover { background: var(--accent-dark); }
/* Само поле выбора файла скрыто, но остаётся кликабельным через <label> и
   доступным с клавиатуры — display:none выкинул бы его из фокуса. */
.prof-ava-edit input[type="file"] {
    position: absolute; width: 1px; height: 1px;
    opacity: 0; overflow: hidden; clip: rect(0 0 0 0);
}
.prof-ava-edit:focus-within { outline: 2px solid var(--accent-dark); outline-offset: 2px; }

.prof-head { display: flex; align-items: center; gap: 14px; margin: 4px 0 18px; }
.prof-head-info { flex: 1 1 auto; min-width: 0; }
.prof-head h1 { margin: 0; font-size: 21px; line-height: 1.2; letter-spacing: -.3px; }
.prof-head-sub { display: block; margin-top: 3px; font-size: 13px; color: var(--muted); }
.prof-gear {
    flex-shrink: 0;
    display: inline-flex; align-items: center; justify-content: center;
    width: 44px; height: 44px;
    border-radius: 14px;
    background: var(--surface-2);
    color: var(--text);
}
.prof-gear:hover { background: var(--accent-bg); color: var(--accent-dark); }
.prof-gear .nav-icon { width: 22px; height: 22px; }

/* Карточка подписки — светлая и однотонная.
   ===========================================================================
   До этого она была тёмным градиентом в цвет уровня с бликом в углу. На
   фоне остального кабинета — светлого и спокойного — такой блок читался
   как чужая наклейка: тяжёлый, глубокий, «дорогой». Ровно то, от чего
   современные интерфейсы ушли.

   Теперь один тон: очень светлая заливка цвета уровня, тёмный текст,
   тонкий контур. Сам цвет никуда не делся, но работает точечно — в
   подписи «ваш уровень», в кольце остатка и в контуре кнопки. Так плашка
   остаётся частью страницы, а не пятном поверх неё. */
.sub-card {
    --tier: var(--gray);
    position: relative;
    display: flex; align-items: center; gap: 18px;
    padding: 22px 24px;
    border-radius: var(--radius-lg);
    color: #fff;
    text-decoration: none;
    /* Глубокая заливка цветом уровня и БЕЛЫЙ текст поверх.
       ===================================================================
       Путь был долгий: 11% → 32% → 78% на белом. Последний вариант дал
       наконец яркость, но вместе с ней и проблему: тёмный текст и тёмное
       кольцо на сиреневом или мятном выглядят грязно — чёрное пятно на
       цветном.

       Здесь, в отличие от карточек витрины, цвет один на весь блок и
       занимает половину экрана, поэтому логика обратная: не осветлять цвет
       под тёмный текст, а притемнять под белый. Подмешиваем к цвету уровня
       45% почти-чёрного — получается насыщенный, «глубокий» тон, на
       котором белый читается на любом из пяти уровней.

       Сколько именно тёмного подмешивать — вопрос палитры. Пока цвета
       уровней были светлыми (мятный, небесно-голубой), приходилось брать
       почти половину, и карточка выходила тёмной до угрюмости. С нынешней
       палитрой (насыщенные тона Tailwind 500–600) хватает 15%: белый текст
       держит контраст не ниже 4,6 на любом из пяти уровней, а сама
       карточка остаётся звонкой, а не «чёрной с оттенком». */
    background: color-mix(in srgb, var(--tier) 85%, #10131c);
    box-shadow: none;
    transition: background .16s ease, box-shadow .16s ease, transform .16s ease;
}
.sub-card:hover {
    text-decoration: none;
    /* Светлее, а не темнее: карточка «поднимается» к курсору и цветом тоже. */
    background: color-mix(in srgb, var(--tier) 94%, #10131c);
    transform: translateY(-2px);
}
.sub-card:active { transform: translateY(0); }
.sub-card-main { position: relative; flex: 1 1 auto; min-width: 0; }
/* Подпись «ваш уровень» — единственное место, где цвет тарифа звучит в
   полную силу. Крупное название рядом набрано обычным тёмным: читаемость
   важнее, чем ещё одно цветное пятно. */
/* Весь текст на карточке — белый и заданный явно: подложка глубокая и
   одинаковая в обеих темах, а var(--text) в светлой теме тёмный и на ней
   пропал бы. */
.sub-card-kicker {
    display: block;
    margin-bottom: 3px;
    font-size: 11px; font-weight: 700; letter-spacing: .07em; text-transform: uppercase;
    color: rgba(255, 255, 255, .88);
}
.sub-card-name {
    display: block;
    font-family: var(--font-display);
    font-size: 26px; font-weight: 800; letter-spacing: -.045em; line-height: 1.1;
    color: #fff;
}
.sub-card-note { display: block; margin-top: 5px; font-size: 13.5px; color: rgba(255, 255, 255, .92); }
/* «Подробнее» — контурная пилюля, как кнопка «Смотреть» в приложениях:
   карточка кликается целиком, и об этом должно быть понятно без наведения. */
.sub-card-more {
    display: inline-flex; align-items: center; gap: 6px;
    margin-top: 14px; padding: 9px 18px;
    border-radius: 999px;
    /* Белая кнопка на глубокой подложке: единственное светлое пятно на
       карточке — оно же и единственное действие. */
    background: #fff;
    box-shadow: none;
    font-size: 13px; font-weight: 700;
    color: #10131c;
}
.sub-card:hover .sub-card-more { background: #fff; box-shadow: 0 3px 12px -4px rgba(0, 0, 0, .45); }

/* Кольцо остатка — обычный SVG (см. plan_days_ring() в includes/plan_tiers.php).
   До этого дуга рисовалась одним conic-gradient, анимированным через
   @property --ring-pct, и не появлялась совсем там, где @property нет
   (Firefox ESR, вебвью старых Android): всё объявление background
   отбрасывалось вместе с нерегистрированным значением. Теперь верная дуга
   приходит уже в разметке — атрибутом stroke-dashoffset, — а CSS ниже
   только добавляет к ней движение и цвет. */
.sub-ring {
    flex-shrink: 0;
    position: relative;
    width: 88px; height: 88px;
    display: grid; place-items: center;
}
.sub-ring-svg {
    position: absolute; inset: 0;
    width: 100%; height: 100%;
    /* Начало дуги — сверху, а не справа: круг «убывает» по часовой
       стрелке от 12 часов, как на любом таймере. */
    transform: rotate(-90deg);
    overflow: visible;
}
.sub-ring-track, .sub-ring-arc {
    fill: none;
    stroke-width: 9;
    stroke-linecap: round;
}
/* Дуга — цветом уровня по светлой дорожке: карточка теперь светлая, и
   белое кольцо на ней просто исчезло бы. Дорожка заметно плотнее самой
   подложки карточки (24%), иначе пустая часть кольца сливалась бы с ней и
   было бы непонятно, где кольцо кончается. */
/* Кольцо на глубокой подложке: дорожка — полупрозрачный белый, дуга —
   сплошной белый. Тёмное кольцо на сиреневом читалось как дыра. */
.sub-ring-track { stroke: rgba(255, 255, 255, .3); }
.sub-ring-arc {
    stroke: #fff;
    animation: sub-ring-draw 1.2s cubic-bezier(.22, .61, .36, 1) .15s both;
}
/* Дуга «сдувается» от полного круга к текущему остатку: движение от
   «было столько» к «осталось столько» — половина сообщения. Конечное
   значение берём из инлайнового --ring-gap, чтобы одна и та же анимация
   работала для любого процента. */
@keyframes sub-ring-draw {
    from { stroke-dashoffset: 0; }
    to   { stroke-dashoffset: var(--ring-gap, 0); }
}
/* Кому движение мешает — тому сразу конечное состояние (оно и так стоит
   атрибутом на элементе, анимацию достаточно выключить). */
@media (prefers-reduced-motion: reduce) {
    .sub-ring-arc { animation: none; }
}
.sub-ring-num {
    position: relative;
    display: flex; flex-direction: column; align-items: center;
    font-family: var(--font-display);
    line-height: 1;
}
.sub-ring-num b {
    font-size: 26px; font-weight: 800; letter-spacing: -.02em;
    color: #fff;
}
.sub-ring-num span { font-size: 10.5px; font-weight: 700; color: rgba(255, 255, 255, .88); margin-top: 3px; }

.sub-card.is-static { cursor: default; }
.sub-card.is-static:hover { filter: none; transform: none; }

/* Три действия под карточкой — как в мобильных приложениях с подпиской:
   значок сверху, подпись снизу, ровные квадраты в ряд. */
/* Действия под карточкой — крупные светлые плитки.
   ===========================================================================
   Были три узких квадрата со значком в цветном кружке и подписью по
   центру. Значок-в-кружке — язык таббара, а не главного действия
   страницы: три одинаковых кружка в ряд читались как индикаторы, а не как
   кнопки, и всё вместе выглядело мелко и суетливо.

   Здесь другой приём, знакомый по мобильным приложениям: широкая светлая
   плитка, подпись крупно и слева сверху, а в правом нижнем углу — большой
   рисунок тонкой линией, наполовину уходящий за край. Он и делает плитку
   «интересной», не добавляя ни одного нового цвета. */
.sub-actions { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 10px; margin-top: 10px; }
/* Плиток бывает две или три (зависит от уровня). На двух колонках третья
   оставалась бы одна в ряду с дырой справа. Условие «последний ребёнок,
   стоящий на нечётном месте» истинно ровно тогда, когда общее число
   нечётное, — тогда и растягиваем. При чётном числе правило молчит. */
.sub-action:last-child:nth-child(odd) { grid-column: 1 / -1; }
@media (min-width: 620px) {
    .sub-actions { grid-template-columns: repeat(auto-fit, minmax(168px, 1fr)); }
    .sub-action:last-child:nth-child(odd) { grid-column: auto; }
}
.sub-action {
    position: relative;
    overflow: hidden;
    display: flex; align-items: flex-start;
    min-height: 104px;
    padding: 16px 17px;
    padding-right: 44px;
    border-radius: var(--radius);
    background: var(--surface-2);
    color: var(--text);
    font-family: var(--font-display);
    font-size: 16.5px; font-weight: 700; line-height: 1.25;
    letter-spacing: -.025em;
    text-decoration: none;
    transition: background .14s ease, transform .14s ease;
}
.sub-action > span { position: relative; }
.sub-action:hover {
    background: color-mix(in srgb, var(--accent) 12%, var(--surface-2));
    text-decoration: none; transform: translateY(-2px);
}
.sub-action:active { transform: translateY(0); }
/* Рисунок наполовину за краем плитки: так он читается как фактура, а не
   как ещё один элемент, который надо разглядывать. */
.sub-action .card-art {
    position: absolute;
    right: -8px; bottom: -8px;
    width: 84px; height: 84px;
    color: var(--muted);
    opacity: .38;
    pointer-events: none;
    transition: opacity .14s ease, transform .14s ease;
}
.sub-action:hover .card-art { opacity: .55; transform: translateY(-2px); }
/* Плитка-статус вместо кнопки: уровень уже максимум, нажимать некуда
   (Creator — см. admin/plan.php). Тот же размер и место в ряду, но без
   намёка на клик — курсор обычный и подсветки при наведении нет. */
.sub-action-static { color: var(--muted); cursor: default; }
.sub-action-static:hover { background: var(--surface-2); transform: none; }

/* Информация о подписке — справка, а не витрина.
   ===========================================================================
   Побывала в двух крайностях. Сначала — серая сетка ячеек, где всё весило
   одинаково. Потом — четыре карточки с цифрами в 26 пунктов и цветными
   подложками: на телефоне они занимали пол-экрана, и «Действует до
   04.11.2026» кричало ровно так же громко, как название уровня в шапке.

   Но главное уже сказано карточкой уровня выше. Это справка — куда
   посмотреть за точной датой или числом свободных мест. Поэтому обычные
   строки обычного кегля: подпись слева, значение справа, тонкая линейка
   между ними. Читается за секунду и не претендует на внимание.

   Цвет остаётся ровно один — красный там, где дело плохо. */
.info-rows {
    margin: 8px 0 0;
    padding: 0 16px;
    border-radius: var(--radius);
    background: var(--surface);
    box-shadow: inset 0 0 0 1px var(--border);
}
.info-row {
    display: flex; align-items: baseline; justify-content: space-between;
    gap: 16px;
    padding: 11px 0;
    border-top: 1px solid var(--border);
    font-size: 14px; line-height: 1.4;
}
.info-row:first-child { border-top: none; }
.info-rows dt { color: var(--muted); }
.info-rows dd {
    margin: 0;
    font-weight: 600;
    color: var(--text);
    text-align: right;
}
.info-rows dd.is-bad { color: var(--red); }

/* Что входит и чего нет — чек-лист.
   ===========================================================================
   Было полтора десятка плашек с пастельной заливкой: зелёные у своего,
   серые у чужого. На посветлевшей странице они стали самым цветным местом
   и притягивали взгляд сильнее, чем цена и кнопка, ради которых сюда и
   заходят, — при том что это справочный список, а не действие.

   Здесь та же мысль другим приёмом: список с отметками, как в чек-листе.
   Закрашенный кружок с галочкой — «есть», пустой контурный — «пока нет».
   Разница видна мгновенно и без единой заливки: глаз считывает полное и
   пустое раньше, чем цвет. Название уровня уезжает вправо отдельной
   пилюлей — это ответ на вопрос «а где это включается». */
/* Две колонки многоколоночной вёрсткой, а не сеткой. Разница
   принципиальная: grid раскладывает по строкам — слева направо, — и
   включённое с невключённым чередовались бы через одно. Колонки читаются
   сверху вниз, поэтому весь блок «уже есть» остаётся слева целиком, а
   «пока нет» — справа, как и задумано порядком в шаблоне. */
.feat-list {
    list-style: none; margin: 8px 0 0; padding: 0;
}
@media (min-width: 760px) {
    .feat-list { columns: 2; column-gap: 34px; }
}
.feat-list li {
    display: flex; align-items: center; gap: 12px;
    padding: 12px 2px;
    border-bottom: 1px solid var(--border);
    font-size: 14px; line-height: 1.35;
    /* Строка не должна разрываться между колонками: половина подписи
       внизу одной, половина вверху другой — это уже не список. */
    break-inside: avoid;
    -webkit-column-break-inside: avoid;
}
/* Хвост списка без линейки: висящая черта под последней строкой читается
   как «дальше что-то было, но не загрузилось». */
.feat-list li:last-child { border-bottom: none; }
.feat-list li > span { min-width: 0; }
.feat-list li::before {
    flex-shrink: 0; box-sizing: border-box;
    width: 22px; height: 22px;
    display: flex; align-items: center; justify-content: center;
    border-radius: 50%;
    font-size: 12px; font-weight: 800;
}
.feat-on { font-weight: 600; }
.feat-on::before { content: '✓'; background: var(--green); color: #fff; }
[data-theme="dark"] .feat-on::before { color: #0b1220; }
/* Селектор с двумя классами намеренно: у одиночного .feat-off
   специфичность ниже, чем у «.feat-list li» выше. */
.feat-list li.feat-off { color: var(--muted); }
.feat-off::before { content: ''; border: 1.6px solid var(--border); }
/* Пилюля с уровнем — контурная и в цвет этого уровня: она и подсказка,
   и ссылка на витрину ниже. */
.feat-tag {
    margin-left: auto; flex-shrink: 0;
    padding: 3px 11px 4px;
    border-radius: 999px;
    border: 1.5px solid color-mix(in srgb, var(--tier) 40%, transparent);
    background: transparent;
    color: color-mix(in srgb, var(--tier) 72%, var(--text));
    font-size: 11.5px; font-weight: 700; white-space: nowrap;
    text-decoration: none;
}
.feat-tag:hover { text-decoration: none; border-color: color-mix(in srgb, var(--tier) 75%, transparent); }
/* В тёмной теме подмешивать цвет уровня к тёмному тексту нельзя: у почти
   чёрного Ultimate+ пилюля сливалась с подложкой. Осветляем к белому. */
[data-theme="dark"] .feat-tag {
    color: color-mix(in srgb, var(--tier) 40%, #ffffff);
    border-color: color-mix(in srgb, var(--tier) 45%, var(--border));
}

/* ===========================================================================
   ИСТОРИЯ ПЛАТЕЖЕЙ
   ===========================================================================
   Сгруппировано по дню: заголовок с датой, под ним карточки операций.
   Плоский список одинаковых строк не читался — глаз ищет «когда», а дата
   была третьим столбцом внутри строки. Слева что произошло и подробности,
   справа сумма и статус: та же раскладка, к которой люди привыкли в
   банковских приложениях. */
.pay-history { display: flex; flex-direction: column; gap: 18px; margin-top: 12px; }
.pay-day {
    font-size: 13px;
    font-weight: 600;
    color: var(--muted);
    padding-left: 2px;
}
.pay-group {
    display: flex;
    flex-direction: column;
    background: var(--surface);
    box-shadow: inset 0 0 0 1px var(--border);
    border-radius: var(--radius);
    overflow: hidden;
    margin-top: 8px;
}
.pay-item {
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
    gap: 16px;
    padding: 15px 18px;
    border-top: 1px solid var(--border);
}
.pay-item:first-child { border-top: none; }
.pay-main { min-width: 0; }
.pay-title { display: block; font-size: 15.5px; font-weight: 600; line-height: 1.3; }
.pay-sub { display: block; margin-top: 3px; font-size: 12.5px; color: var(--muted); line-height: 1.4; }
.pay-side { flex-shrink: 0; text-align: right; }
.pay-sum {
    display: block;
    font-family: var(--font-display);
    font-size: 18px; font-weight: 800; letter-spacing: -.04em;
    font-variant-numeric: tabular-nums;
    white-space: nowrap;
}
.pay-sum.is-free { font-size: 14px; font-weight: 600; color: var(--muted); }
.pay-status { display: block; margin-top: 3px; font-size: 12px; color: var(--green); font-weight: 600; }
.pay-status.is-off { color: var(--red); }

/* Воронка приглашений: пришли → оплатили → начислено. Цифра крупно,
   подпись под ней; последняя карточка — результат, поэтому цветная. */
.ref-stats { display: grid; grid-template-columns: repeat(auto-fit, minmax(120px, 1fr)); gap: 10px; margin-top: 14px; }
.ref-stat {
    padding: 15px 17px 14px;
    border-radius: var(--radius);
    background: color-mix(in srgb, var(--accent) 8%, var(--surface));
}
.ref-stat b {
    display: block;
    font-family: var(--font-display);
    font-size: 28px; font-weight: 800; letter-spacing: -.02em; line-height: 1.05;
}
.ref-stat span { display: block; margin-top: 4px; font-size: 12px; color: var(--muted); line-height: 1.3; }
.ref-stat.is-result {
    background: color-mix(in srgb, var(--green) 13%, var(--surface));
}
.ref-stat.is-result b { color: var(--green-dark); }
[data-theme="dark"] .ref-stat.is-result b { color: color-mix(in srgb, var(--green) 55%, #ffffff); }

/* Разделы плитками *//* Разделы плитками */
.prof-group {
    display: flex; align-items: center; gap: 10px;
    margin: 24px 0 10px;
    font-size: 11px; font-weight: 800; letter-spacing: .07em;
    text-transform: uppercase; color: var(--muted);
}
/* Счётчик рядом с заголовком раздела — «7 из 11». Доля, выраженная
   словами: то же, что показала бы шкала, но без вида индикатора загрузки. */
.prof-group-count {
    padding: 3px 9px 4px;
    border-radius: 999px;
    background: var(--surface-2);
    color: var(--muted);
    font-size: 10.5px; font-weight: 700; letter-spacing: .03em;
    text-transform: none;
}
/* Плитки разделов.
   ===========================================================================
   Раньше это были ровные белые прямоугольники с серой рамкой и серым
   значком: восемнадцать одинаковых строк, по которым глаз скользил, ничего
   не различая, и ни намёка на то, что по ним вообще нажимают.

   Теперь у каждой плитки три опоры: значок в своей пастельной плашке
   (пятно цвета опознаётся раньше текста), название плотным шрифтом и
   шеврон справа — обещание перехода. Нажатие видно: плитка чуть
   приподнимается, плашка значка заливается акцентом, шеврон уезжает
   вправо. Тени нет — только заливка и тонкий внутренний контур. */
/* Колонка уже прежних 230px: плитки стали двумерными, и на широком
   экране растянутая на 300+ пикселей карточка с двумя словами внутри
   выглядела пустой. 146px — плитка размером с кнопку, а не с карточку:
   их тут восемнадцать, и каждая лишняя сотня пикселей высоты превращает
   профиль в две страницы прокрутки. */
.prof-tiles { display: grid; grid-template-columns: repeat(auto-fill, minmax(146px, 1fr)); gap: 9px; }
/* Плитки разделов кабинета — в том же ключе, что кнопки .sub-action.
   ===========================================================================
   Прежняя версия была списком настроек: у каждой строки слева цветной
   скруглённый квадрат со значком, справа шеврон. На двух-трёх пунктах это
   работает, на восемнадцати — нет: восемнадцать одинаковых голубых
   квадратов подряд, и глазу не за что зацепиться. Плюс шеврон у каждой
   строки — восемнадцать одинаковых стрелок, которые ничего не добавляют:
   что плитка нажимается, и так видно.

   Здесь наоборот: значок не в плашке, а крупным контуром в правом нижнем
   углу, наполовину за краем и почти прозрачный. Он работает как фактура —
   отличает плитки друг от друга боковым зрением, — а читается в первую
   очередь название, набранное крупно и слева сверху. Ровно так устроены
   кнопки в разделе «Подписка», и именно они в этом кабинете нравятся.

   Цвет появляется только при наведении и только у опасного действия
   («Выйти»). В спокойном состоянии вся сетка — ровный светло-серый, на
   котором видно текст, а не оформление. */
.prof-tile {
    position: relative;
    overflow: hidden;
    display: flex; flex-direction: column; justify-content: flex-start;
    min-height: 76px;
    padding: 12px 13px;
    /* Место под рисунок в углу: без него длинная подпись наезжает на него
       и обе вещи становятся нечитаемыми. */
    padding-right: 42px;
    border-radius: var(--radius);
    background: var(--surface-2);
    border: none;
    box-shadow: none;
    color: var(--text);
    font-family: var(--font-display);
    font-size: 14.5px; font-weight: 700; line-height: 1.25; letter-spacing: -.025em;
    text-align: left; text-decoration: none; cursor: pointer;
    transition: background .14s ease, transform .14s ease;
}
.prof-tile:hover {
    background: color-mix(in srgb, var(--accent) 12%, var(--surface-2));
    text-decoration: none;
    transform: translateY(-2px);
}
.prof-tile:active { transform: translateY(0); }

/* Крупный контурный значок в углу. Тот же nav_icon, что в меню, только
   растянутый: viewBox 24 при stroke-width 1.7 на 76 пикселях даёт как раз
   тонкую линию, ради которой обычно рисуют отдельный набор картинок. */
.prof-tile .nav-icon {
    position: absolute;
    right: -10px; bottom: -11px;
    width: 58px; height: 58px;
    padding: 0;
    border-radius: 0;
    background: none;
    color: var(--muted);
    opacity: .3;
    pointer-events: none;
    transition: opacity .14s ease, transform .14s ease;
}
.prof-tile:hover .nav-icon { opacity: .5; color: var(--accent); transform: translateY(-3px); }

.prof-tile-label { position: relative; flex: 1 1 auto; min-width: 0; }
.prof-tile-note {
    display: block; margin-top: 3px;
    font-family: "Golos Text", inherit;
    font-size: 12px; font-weight: 500; letter-spacing: 0;
    color: var(--muted);
}
/* Счётчик — в верхнем правом углу, а не в конце строки: строки больше
   нет, плитка двумерная. */
.prof-tile-badge {
    position: absolute;
    top: 8px; right: 8px;
    min-width: 22px; padding: 2px 7px;
    border-radius: 999px;
    background: var(--red); color: #fff;
    font-family: "Golos Text", inherit;
    font-size: 11.5px; font-weight: 800; text-align: center; letter-spacing: 0;
}
.prof-tile .nav-dot { position: absolute; top: 11px; right: 11px; }

.prof-tile.is-danger { color: var(--red); }
.prof-tile.is-danger .nav-icon { color: var(--red); }
.prof-tile.is-danger:hover {
    background: color-mix(in srgb, var(--red) 12%, var(--surface-2));
}
.prof-tile.is-danger:hover .nav-icon { color: var(--red); }
.prof-tile .theme-icon-dark { display: none; }
[data-theme="dark"] .prof-tile .theme-icon-light { display: none; }
[data-theme="dark"] .prof-tile .theme-icon-dark { display: inline; }
@media (max-width: 560px) {
    .sub-card { padding: 18px 17px; gap: 12px; }
    .sub-card-name { font-size: 21px; }
    .sub-ring { width: 76px; height: 76px; }
    .sub-ring-num b { font-size: 23px; }
}

/* ===========================================================================
   РЕКВИЗИТЫ ИСПОЛНИТЕЛЯ
   ===========================================================================
   По закону о защите прав потребителей исполнитель услуги обязан назвать
   себя. Место для этого — там, где человек принимает решение платить:
   страница входа и страница тарифа. */
.req-line {
    margin-top: 20px;
    font-size: 12px;
    line-height: 1.6;
    color: var(--muted);
}
.req-line b { color: var(--text); font-weight: 600; }
.req-line a { color: var(--muted); text-decoration: underline; }


/* ===========================================================================
   ТЕЛЕФОН
   ===========================================================================
   Новые разделы собраны на сетках auto-fit, и на узком экране они честно
   складываются в одну колонку — но только если минимальная ширина колонки
   меньше экрана. У «что входит в доступ» она была 190px, и на 360px две
   колонки всё-таки помещались, наезжая друг на друга текстом. Ниже — явные
   правила для узкого экрана вместо надежды на auto-fit. */
@media (max-width: 620px) {
    .hero { padding: 16px 16px; border-radius: var(--radius-sm); }
    .hero h2 { font-size: 18px; }
    .hero-row { gap: 14px; }
    .hero-figures { gap: 18px; width: 100%; }
    .hero-fig b { font-size: 21px; }
    /* Кнопка не растягивается на всю ширину: иначе она ложится поверх
       рисунка в правом нижнем углу. Правый отступ — место под рисунок. */
    .hero-actions { padding-right: 120px; }

    .plan-perks { grid-template-columns: 1fr; gap: 7px; }
    /* .plan-grid колонки на узком экране не меняет: они общие на весь
       список, и схлопывание их в одну ломало бы выравнивание цен. */
    .plan-opt { padding: 14px 16px; }
    .plan-opt-price { font-size: 22px; }

    .invite-card { padding: 14px 14px; }
    .invite-top { gap: 6px; }
    .invite-meta { margin-left: 0; width: 100%; }
    .invite-copy input { flex: 1 1 100%; }
    .invite-copy .btn { width: 100%; }


    .support-msg { max-width: 100%; }
}


/* ===========================================================================
   ВХОД
   ===========================================================================
   Разворот: слева тёмная половина с обещанием и тем, что человеку делать
   дальше, справа — форма. Пробовал свести к обычной карточке, как на
   «Забыли пароль», и это была ошибка: там служебный экран, куда попадают
   уже изнутри продукта, а сюда приходят СНАРУЖИ — по ссылке от коллеги
   или с витрины. Форма в пустоте не объясняет ни что это, ни что делать
   тому, у кого кабинета ещё нет.

   На телефоне левая половина не исчезает, а сжимается: остаются название
   и одна строка обещания. Прятать её целиком — снова получить форму без
   контекста, ровно то, от чего уходили. */
.auth-split {
    display: grid;
    grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
    max-width: 900px;
    margin: 5vh auto 40px;
    border-radius: var(--radius-lg);
    overflow: hidden;
    box-shadow: var(--shadow-lift);
    background: var(--surface);
}
/* Левая половина экрана входа — синяя, как и шапка главной, и по той же
   причине: это первое, что человек видит о продукте, и оно должно
   отличаться от белого листа с формой справа. Светлый вариант пробовали —
   разворот превращался в одну сплошную серую плоскость.

   Круга света в углу больше нет: вместо него рисунок тонкой линией
   (nav_art) — он про то, чем занимаются в кабинете, и не двигается. */
.auth-aside {
    position: relative;
    overflow: hidden;
    padding: 34px 32px;
    color: #fff;
    background: linear-gradient(150deg, #1b2a4a 0%, #2a3f6d 55%, #3457d5 130%);
    display: flex;
    flex-direction: column;
    gap: 18px;
}
.auth-aside .card-art {
    position: absolute;
    right: -22px; bottom: -26px;
    width: 176px; height: 176px;
    color: #fff;
    opacity: .15;
    pointer-events: none;
}
.auth-brand {
    position: relative;
    font-size: 15px; font-weight: 800; letter-spacing: -.2px;
    opacity: .85;
}
.auth-claim {
    position: relative;
    margin: 0;
    font-family: var(--font-display);
    font-size: 27px; font-weight: 800; line-height: 1.2; letter-spacing: -.045em;
}
.auth-points { position: relative; list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 9px; }
.auth-points li { display: flex; gap: 9px; align-items: flex-start; font-size: 13.5px; line-height: 1.45; opacity: .88; }
/* Тире вместо маркера-точки: точка в столбик читается как список настроек,
   тире — как перечисление в тексте, чем это и является. */
.auth-points li::before { content: '—'; opacity: .55; flex-shrink: 0; }
.auth-aside-foot { position: relative; margin-top: auto; font-size: 12.5px; opacity: .72; line-height: 1.55; }
.auth-aside-foot a { color: #fff; font-weight: 600; text-decoration: underline; text-underline-offset: 2px; }

.auth-main { padding: 34px 32px; display: flex; flex-direction: column; justify-content: center; }
.auth-main h1 { margin: 0 0 18px; font-size: 22px; font-weight: 800; letter-spacing: -.3px; }

@media (max-width: 780px) {
    .auth-split { grid-template-columns: 1fr; margin: 0 0 24px; border-radius: 0; box-shadow: none; }
    .auth-aside { padding: 20px 18px; gap: 10px; }
    /* Рисунок в углу на телефоне убираем: колонка становится узкой
       полосой над формой, и он лезет прямо под текст. */
    .auth-aside .card-art { display: none; }
    /*
     * Список преимуществ на телефоне пробовали превратить в ленту карточек
     * с горизонтальной прокруткой — по факту получалась тесная «квадратная»
     * плитка, которую ещё и надо было листать пальцем, чтобы прочитать все
     * четыре пункта. Ради строки контекста это лишнее: сама плашка и так
     * начинается с названия и с auth-claim (одна строка сути), этого
     * достаточно, чтобы форма ниже не выглядела висящей в пустоте — а
     * подробный список ценностей человек и так увидит на промо-странице.
     */
    .auth-points { display: none; }
    .auth-claim { font-size: 19px; }
    .auth-aside-foot { font-size: 12px; }
    .auth-main { padding: 22px 18px 26px; }
    .auth-main h1 { font-size: 20px; margin-bottom: 14px; }
}

/* Закрытое обращение: вместо формы ответа — строка с кнопкой «Открыть
   заново». Одна строка, а не карточка: это не раздел, а состояние. */
.support-closed {
    display: flex; align-items: center; gap: 12px; flex-wrap: wrap;
    margin-top: 16px; padding: 12px 14px;
    border-radius: var(--radius-sm);
    background: var(--surface-2);
    font-size: 13.5px;
}

/* ===========================================================================
   СЕРЫЙ МАКЕТ НА ВРЕМЯ ЗАГРУЗКИ
   ===========================================================================
   Страница отдаётся сразу, а куски, которые зависят от чужого сервера
   (оценки и прогресс из Moodle), приезжают следом отдельным запросом — см.
   lazy_part() в includes/functions.php. Пока они едут, их место занимает
   макет: серые полоски по форме будущего содержимого.

   Почему макет, а не «крутилка». Крутилка сообщает «идёт загрузка» и
   больше ничего: высоту блока она не держит, поэтому в момент прихода
   данных страница дёргается и всё под ней уезжает вниз — ровно тогда,
   когда человек уже начал читать. Макет занимает примерно столько же
   места, сколько займут данные, и заодно показывает, ЧТО именно там будет:
   список, карточки или строки таблицы.

   Блик слева направо — единственная анимация: она отличает «грузится» от
   «сломалось и осталось серым». При включённом системном «уменьшить
   движение» блик выключается, макет остаётся статичным. */
.skel-wrap { display: block; }
.skel {
    display: block;
    position: relative;
    overflow: hidden;
    background: var(--surface-2);
    border-radius: 8px;
}
.skel::after {
    content: '';
    position: absolute;
    inset: 0;
    transform: translateX(-100%);
    background: linear-gradient(90deg,
        transparent 0%,
        color-mix(in srgb, var(--surface) 70%, transparent) 50%,
        transparent 100%);
    animation: skel-shimmer 1.25s ease-in-out infinite;
}
@keyframes skel-shimmer {
    100% { transform: translateX(100%); }
}
.skel-line { height: 12px; margin-bottom: 9px; }
.skel-card { height: 76px; border-radius: var(--radius-sm); margin-bottom: 10px; }
.skel-row {
    display: flex;
    align-items: center;
    gap: 14px;
    padding: 11px 0;
    border-top: 1px solid var(--border);
}
.skel-row .skel-line { margin: 0; }
.skel-row:first-child { border-top: none; }
@media (prefers-reduced-motion: reduce) {
    .skel::after { animation: none; }
}

/* Каркас страницы на время долгого перехода (SIXERP_SK в includes/header.php).
   Встаёт ВНУТРЬ <main> вместо её содержимого: шапка и нижнее меню остаются
   на месте. Прежний каркас закрывал весь экран, и «остального не видно». */
.site-main.is-nav-loading > :not(.nav-skel) { display: none !important; }
.nav-skel { animation: nav-skel-in .2s ease both; }
@keyframes nav-skel-in { from { opacity: 0; } to { opacity: 1; } }
.nav-skel-head { margin: 4px 0 16px; }
.nav-skel-head .skel-line:last-child { margin-bottom: 0; }
.nav-skel-chips { display: flex; flex-wrap: wrap; gap: 8px; margin-bottom: 16px; }
.nav-skel-card {
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--radius-sm);
    padding: 12px 18px;
    margin-bottom: 14px;
}
.nav-skel-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); gap: 14px; }
.nav-skel-grid .nav-skel-card { margin-bottom: 0; }
.nav-skel-item { display: flex; align-items: center; gap: 12px; padding: 10px 0; border-top: 1px solid var(--border); }
.nav-skel-item:first-child, .nav-skel-plain { border-top: none; }
.nav-skel-plain { margin-bottom: 12px; }
.nav-skel-item > .skel { flex-shrink: 0; }
.nav-skel-grow { flex: 1; min-width: 0; }
.nav-skel-grow .skel-line:last-child { margin-bottom: 0; }
.skel-circle { border-radius: 50%; }
.nav-skel-cal { display: grid; grid-template-columns: repeat(7, minmax(0, 1fr)); gap: 6px; }
.nav-skel-tiles { display: grid; grid-template-columns: repeat(auto-fill, minmax(150px, 1fr)); gap: 10px; }
@media (max-width: 620px) {
    .nav-skel-cal .skel { height: 40px !important; }
}
@media (prefers-reduced-motion: reduce) {
    .nav-skel { animation: none; }
}

/* Отложенный кусок внутри ряда фишек (.stat-row): обёртка не должна сама
   становиться элементом ряда — заглушки и приехавшие фишки встают в ряд
   как обычные. */
.stat-row > .lazy-part, .stat-slot { display: contents; }
.skel-chip { width: 112px; height: 36px; border-radius: 999px; }

/* Текст только для экранных читалок: макет для них ничего не значит, а
   знать, что здесь идёт загрузка, им нужно. */
.visually-hidden {
    position: absolute;
    width: 1px; height: 1px;
    margin: -1px; padding: 0;
    overflow: hidden;
    clip: rect(0 0 0 0);
    clip-path: inset(50%);
    white-space: nowrap;
    border: 0;
}

/* ══════════════════════════════════════════════
   ВЫБОР УЧЕНИКА ПЕРЕД ЗАНЯТИЕМ (admin/lesson_form.php без student_id)
   ══════════════════════════════════════════════ */
.pick-search {
    width: 100%; box-sizing: border-box;
    font: inherit; font-size: 15px; color: var(--text);
    padding: 10px 14px; margin-bottom: 6px;
    border: 1px solid var(--border); border-radius: 12px; background: var(--surface);
}
.pick-search:focus { outline: none; border-color: var(--accent); }
.pick-sect { margin-top: 18px; }
.pick-list { display: flex; flex-direction: column; gap: 6px; }
.pick-item {
    display: flex; align-items: center; justify-content: space-between; gap: 12px;
    padding: 12px 14px; border-radius: 12px;
    background: var(--surface-2); color: var(--text); text-decoration: none;
}
.pick-item:hover { text-decoration: none; background: var(--accent-bg); }
.pick-name { font-weight: 600; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.pick-meta { color: var(--muted); font-size: 13px; flex-shrink: 0; }

/* ══════════════════════════════════════════════
   ТРЕК РЕПЕТИТОРА (includes/tutor_track.php)
   Ярче остального кабинета намеренно: это витрина достижений
   («как в онлайн-школе»), а не справка. Цвет — у значков и шкалы.
   ══════════════════════════════════════════════ */
.trk-badge { flex-shrink: 0; display: block; filter: drop-shadow(0 3px 6px rgba(16, 24, 40, .18)); }
.trk-bar {
    display: block; position: relative; overflow: hidden;
    height: 6px; border-radius: 99px;
    background: color-mix(in srgb, var(--muted) 22%, transparent);
}
.trk-bar > span { display: block; height: 100%; border-radius: inherit; transition: width .5s ease; }
.trk-bar-lg { height: 10px; background: rgba(255, 255, 255, .28); }

/* Плашка: профиль (светлая) и шапка «Сегодня» (.on-dark) */
.trk-chip {
    display: flex; align-items: center; gap: 12px;
    margin: 12px 0 0; padding: 12px 14px;
    border-radius: var(--radius); border: 1px solid var(--border);
    background: var(--surface); color: var(--text); text-decoration: none;
    transition: border-color .14s ease, transform .14s ease;
}
.trk-chip:hover { text-decoration: none; border-color: var(--accent); transform: translateY(-1px); }
.trk-chip-body { display: flex; flex-direction: column; min-width: 0; flex: 1; }
.trk-chip-kicker { font-size: 11px; font-weight: 700; letter-spacing: .06em; text-transform: uppercase; color: var(--muted); }
.trk-chip-name { font-family: var(--font-display); font-size: 18px; font-weight: 800; letter-spacing: -.02em; line-height: 1.2; }
.trk-chip-row { display: flex; align-items: center; gap: 8px; margin-top: 5px; }
.trk-chip-row .trk-bar { flex: 1; max-width: 180px; }
.trk-chip-pct { font-size: 12px; font-weight: 700; color: var(--muted); }
.trk-chip-top { font-size: 12.5px; color: var(--muted); }
.trk-chip-arrow { font-size: 22px; color: var(--muted); line-height: 1; }

.trk-chip.on-dark {
    margin: 0; padding: 6px 14px 6px 6px; gap: 8px;
    border: none; border-radius: 999px;
    background: rgba(255, 255, 255, .16); color: #fff;
}
.trk-chip.on-dark:hover { background: rgba(255, 255, 255, .24); transform: none; }
.trk-chip.on-dark .trk-badge { width: 30px; height: 30px; }
.trk-chip.on-dark .trk-chip-kicker { display: none; }
.trk-chip.on-dark .trk-chip-name { font-size: 13.5px; font-family: inherit; font-weight: 700; letter-spacing: 0; }
.trk-chip.on-dark .trk-chip-row { margin-top: 3px; }
.trk-chip.on-dark .trk-bar { width: 70px; flex: none; height: 4px; background: rgba(255, 255, 255, .25); }
.trk-chip.on-dark .trk-chip-pct,
.trk-chip.on-dark .trk-chip-top,
.trk-chip.on-dark .trk-chip-arrow { color: rgba(255, 255, 255, .8); }
.trk-chip.on-dark .trk-chip-pct { font-size: 11px; }

/* Окно трека */
.trk-sheet {
    width: min(560px, calc(100vw - 24px)); max-height: min(86vh, 900px);
    padding: 0; border: none; border-radius: 22px;
    background: var(--surface-2); color: var(--text);
    box-shadow: 0 24px 60px rgba(16, 24, 40, .35);
    overflow-y: auto; overscroll-behavior: contain;
}
.trk-sheet::backdrop { background: rgba(15, 20, 35, .5); }
.trk-sheet[open] { animation: trk-in .22s ease; }
@keyframes trk-in { from { opacity: 0; transform: translateY(16px); } to { opacity: 1; transform: none; } }
.trk-head {
    position: sticky; top: 0; z-index: 2;
    padding: 20px 22px 18px;
    background: linear-gradient(135deg, var(--accent), #7c5cf6);
    color: #fff;
}
.trk-close {
    position: absolute; top: 12px; right: 14px;
    width: 34px; height: 34px; border: none; border-radius: 50%;
    background: rgba(255, 255, 255, .16); color: #fff;
    font-size: 24px; line-height: 1; cursor: pointer;
}
.trk-close:hover { background: rgba(255, 255, 255, .28); }
.trk-head-top { display: flex; align-items: center; gap: 14px; padding-right: 40px; }
.trk-head-kicker { display: block; font-size: 13px; opacity: .85; }
.trk-head-name { display: block; font-family: var(--font-display); font-size: 24px; font-weight: 800; letter-spacing: -.02em; }
.trk-head-goal { display: flex; justify-content: space-between; gap: 10px; margin: 16px 0 8px; font-size: 13.5px; opacity: .95; }
.trk-head-bars { display: flex; flex-wrap: wrap; gap: 6px 18px; margin-top: 10px; font-size: 12.5px; }
.trk-head-bar span { opacity: .8; margin-right: 6px; }
.trk-body { padding: 6px 18px 22px; }
.trk-h { font-family: var(--font-display); font-size: 18px; font-weight: 800; letter-spacing: -.02em; margin: 22px 0 10px; }
.trk-sub { font-size: 13px; font-weight: 700; color: var(--muted); margin: 12px 0 8px; }
.trk-list { display: flex; flex-direction: column; gap: 8px; }
.trk-card {
    display: flex; gap: 14px; align-items: center;
    padding: 12px 14px; border-radius: 16px;
    background: var(--surface); box-shadow: 0 1px 2px rgba(16, 24, 40, .05);
}
.trk-card-ico {
    flex-shrink: 0; display: flex; align-items: center; justify-content: center;
    width: 62px; height: 62px; border-radius: 14px; background: var(--surface-2);
}
.trk-card.is-current { box-shadow: 0 0 0 2px var(--accent); }
.trk-card-main { min-width: 0; }
.trk-card-title { font-weight: 800; font-size: 15.5px; display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.trk-tag {
    font-size: 11.5px; font-weight: 600; padding: 2px 9px; border-radius: 99px;
    background: color-mix(in srgb, var(--accent) 14%, var(--surface)); color: var(--accent);
}
.trk-tag.is-done { background: color-mix(in srgb, var(--green) 14%, var(--surface)); color: var(--green); }
.trk-card-cond { font-size: 13px; margin-top: 3px; }
.trk-card-cond b { font-weight: 700; }
.trk-card-about { font-size: 12.5px; color: var(--muted); margin-top: 2px; }
.trk-rule {
    display: flex; flex-direction: column; gap: 3px;
    padding: 12px 14px; border-radius: 16px; background: var(--surface);
    font-size: 13px; color: var(--muted);
}
.trk-rule-num { font-size: 16px; color: var(--text); }
.trk-rule-num.is-plus { color: var(--green); }
.trk-fact {
    display: flex; gap: 12px; align-items: flex-start;
    padding: 14px; border-radius: 16px; background: var(--surface);
}
.trk-fact-ico {
    flex-shrink: 0; width: 34px; height: 34px; border-radius: 10px;
    display: flex; align-items: center; justify-content: center;
    background: color-mix(in srgb, var(--accent) 12%, var(--surface)); color: var(--accent); font-weight: 800;
}
.trk-fact > div b { display: block; font-size: 14px; }
.trk-fact > div span { display: block; font-size: 13px; color: var(--muted); margin-top: 2px; }
.trk-faq { border-radius: 16px; background: var(--surface); padding: 0 16px; }
.trk-faq details { border-bottom: 1px solid var(--border); }
.trk-faq details:last-child { border-bottom: none; }
.trk-faq summary {
    list-style: none; cursor: pointer; padding: 14px 24px 14px 0; position: relative;
    font-weight: 600; font-size: 14px;
}
.trk-faq summary::-webkit-details-marker { display: none; }
.trk-faq summary::after {
    content: ""; position: absolute; right: 4px; top: 18px;
    width: 7px; height: 7px; border-right: 2px solid var(--muted); border-bottom: 2px solid var(--muted);
    transform: rotate(45deg); transition: transform .15s;
}
.trk-faq details[open] summary::after { transform: rotate(-135deg); top: 21px; }
.trk-faq p { margin: 0 0 14px; font-size: 13.5px; color: var(--muted); line-height: 1.5; }

/* Та же вёрстка отдельной страницей (admin/track.php) */
.trk-page { border-radius: 22px; overflow: hidden; background: var(--surface-2); }
.trk-page .trk-head { position: static; }

/* Поздравление с треком */
.trk-hello {
    width: min(460px, calc(100vw - 32px));
    padding: 26px 24px 20px; border: none; border-radius: 22px;
    background: var(--surface); color: var(--text); text-align: center;
    box-shadow: 0 24px 60px rgba(16, 24, 40, .35);
}
.trk-hello::backdrop { background: rgba(15, 20, 35, .5); }
.trk-hello[open] { animation: trk-in .25s ease; }
.trk-hello-ico { display: flex; justify-content: center; margin-bottom: 10px; }
.trk-hello h2 { font-family: var(--font-display); font-size: 21px; font-weight: 800; margin: 0 0 8px; }
.trk-hello p { font-size: 14px; color: var(--muted); line-height: 1.5; margin: 0 0 18px; }
.trk-hello-actions { display: flex; gap: 8px; justify-content: center; }

@media (max-width: 700px) {
    /* На телефоне окно — лист снизу во всю ширину */
    .trk-sheet {
        width: 100vw; max-width: 100vw; max-height: 90vh;
        margin: auto 0 0; border-radius: 22px 22px 0 0;
    }
    .trk-head { padding: 18px 16px 16px; }
    .trk-body { padding: 4px 12px 26px; }
    .trk-card-ico { width: 54px; height: 54px; }
}

/* ══════════════════════════════════════════════
   НИЖНЕЕ МЕНЮ: активный и нажатый пункт — как в Telegram/VK
   Первая попытка (насыщенная синяя капсула вокруг значка) выглядела так,
   будто надо попасть пальцем в крошечную область. Теперь выбранный пункт —
   просто цвет: значок и подпись акцентные, без подложки. Нажатие —
   мягкая подсветка ВСЕЙ ячейки таббара, а не пятнышка вокруг значка.
   ══════════════════════════════════════════════ */
@media (max-width: 760px), ((max-height: 480px) and (hover: none) and (pointer: coarse)) {
    .main-nav .nav-link { color: var(--muted); transition: background-color .15s ease, color .15s ease; }
    .main-nav .nav-link:focus { outline: none; }
    .main-nav .nav-link:focus-visible { background: var(--accent-bg); }

    /* Значок без капсулы — ни у активного, ни у нажатого */
    .main-nav .nav-link .nav-icon,
    .main-nav .nav-link.is-active .nav-icon,
    .main-nav .nav-link[aria-current="page"] .nav-icon,
    .main-nav .nav-link.is-pressed .nav-icon {
        background: none; box-shadow: none; padding: 4px 0;
        transition: color .15s ease, transform .12s ease;
    }

    /* Где ты сейчас — цветом значка и подписи, значок чуть жирнее */
    .main-nav .nav-link.is-active,
    .main-nav .nav-link[aria-current="page"] { color: var(--accent); background: none; }
    .main-nav .nav-link.is-active .nav-icon,
    .main-nav .nav-link[aria-current="page"] .nav-icon { color: var(--accent); stroke-width: 2.2; }

    /* Нажатие — вся ячейка, со скруглением как у карточек */
    .main-nav .nav-link.is-pressed { background: var(--accent-bg); }
    .main-nav .nav-link.is-pressed .nav-icon,
    .main-nav .nav-link.is-pressed .nav-ava { transform: scale(.92); }
    .nav-dropdown[open] > summary.nav-link { background: var(--accent-bg); }

    /* Профиль: кольцо аватарки становится акцентным — как выбранный значок */
    .main-nav .nav-profile-btn .nav-ava { transition: transform .12s ease, box-shadow .15s ease; }
    .main-nav .nav-profile-btn.is-active .nav-ava,
    .main-nav .nav-profile-btn[aria-current="page"] .nav-ava {
        box-shadow: 0 0 0 2px var(--surface), 0 0 0 4px var(--accent);
    }
}

/* Трек в шапке «Сегодня»: две копии плашки — полная рядом с кнопкой на
   компьютере, компактный значок с процентом в углу на телефоне */
.hero .hero-trk-mob { display: none; }
@media (max-width: 700px) {
    .hero .hero-trk-desk { display: none; }
    .hero .hero-trk-mob {
        display: flex; position: absolute; top: 14px; right: 14px; z-index: 2;
        padding: 4px 10px 4px 4px; gap: 6px;
    }
    .hero .hero-trk-mob .trk-badge { width: 28px; height: 28px; }
    .hero .hero-trk-mob .trk-chip-name,
    .hero .hero-trk-mob .trk-bar,
    .hero .hero-trk-mob .trk-chip-arrow { display: none; }
    .hero .hero-trk-mob .trk-chip-row { margin: 0; }
    .hero .hero-trk-mob .trk-chip-pct,
    .hero .hero-trk-mob .trk-chip-top { font-size: 12.5px; font-weight: 800; color: #fff; }
    .hero .hero-trk-mob .trk-chip-top { font-size: 0; }
    .hero .hero-trk-mob .trk-chip-top::after { content: '★'; font-size: 13px; }
    .hero .hero-kicker, .hero .hero-main h2 { padding-right: 76px; }
}

/* ── Треки в панели платформы (platform/tracks.php) ── */
.trk-dist { display: flex; gap: 8px; flex-wrap: wrap; margin: 0 0 16px; }
.trk-dist-item {
    display: flex; align-items: center; gap: 8px;
    padding: 8px 14px 8px 8px; border-radius: 14px;
    background: var(--surface); border: 1px solid var(--border);
    color: var(--text); text-decoration: none;
}
.trk-dist-item:first-child { padding-left: 14px; }
.trk-dist-item:hover { border-color: var(--accent); text-decoration: none; }
.trk-dist-item.is-on { border-color: var(--accent); box-shadow: 0 0 0 1px var(--accent); }
.trk-dist-num { font-family: var(--font-display); font-size: 20px; font-weight: 800; }
.trk-dist-name { font-size: 13px; color: var(--muted); }
.trk-cell { display: inline-flex; align-items: center; gap: 8px; }
.trk-cell-progress { display: flex; align-items: center; gap: 8px; }
.trk-cell-progress .trk-bar { flex: 1; min-width: 90px; }
.trk-cell-progress b { font-size: 12.5px; }
.trk-set-row { display: flex; gap: 16px; padding: 16px 0; border-bottom: 1px solid var(--border); }
.trk-set-row:first-of-type { padding-top: 4px; }
.trk-set-badge { display: flex; flex-direction: column; align-items: center; gap: 4px; flex-shrink: 0; width: 60px; }
.trk-set-badge span { font-size: 12px; color: var(--muted); font-weight: 700; }
.trk-set-fields { flex: 1; min-width: 0; }
.trk-set-pair { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; }
.trk-set-first { margin: -6px 0 12px; }
.trk-set-actions { display: flex; gap: 8px; flex-wrap: wrap; margin-top: 16px; }
@media (max-width: 700px) {
    .trk-set-row { flex-direction: column; gap: 8px; }
    .trk-set-badge { flex-direction: row; width: auto; }
    .trk-set-pair { grid-template-columns: 1fr; gap: 0; }
}
