/* ============================================================
   TutorChief — визуальный язык

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

   Фиолетовый — действие. Золото — второй голос: агент, модель, деньги.
   Статусы отдельной группой и в общую палитру не заходят.

   Про размытие. backdrop-filter стоит дорого, поэтому он только на крупных
   поверхностях: рейл, шапка, окно, карточки верхнего уровня. Мелкие
   повторяющиеся плашки — занятия в календаре, строки списков — просто
   полупрозрачные. Это и быстрее, и честнее: размывать нечего, под ними
   такое же стекло.
   ============================================================ */

:root {
  /* Календарь у поля дедлайна рисует браузер, и он же решает, какого тот
     цвета. Без этих двух строк он приходил синим и всегда светлым — чужая
     вставка посреди своего диалога (владелец, 19.09.2026).
   *
   * `accent-color` наследуется, поэтому стоит один раз на корне и красит
     заодно галочки, переключатели и ползунки. `color-scheme` задан ЯВНО
     обеими темами, а не по настройке системы: светлая тема панели должна
     выглядеть одинаково у всех — ровно по той же причине, по которой здесь
     нет правила @media (prefers-color-scheme). */
  color-scheme: light;
  accent-color: var(--accent);

  /* Свечение фона. Три пятна: фиолетовое сверху слева, розово-красное
     сверху справа, золотое снизу. В светлой теме — намёк, в тёмной — цвет. */
  --aurora-1: rgba(124, 92, 255, .42);
  --aurora-2: rgba(255, 92, 138, .30);
  --aurora-3: rgba(255, 184, 77, .34);
  --aurora-4: rgba(92, 140, 255, .30);

  /* Подложка заметно темнее белого намеренно: стекло видно только тогда,
     когда под ним есть чему просвечивать. На белом фоне белая пластина —
     просто белый прямоугольник. */
  --bg:          #E8E4F6;
  --surface:     rgba(255, 255, 255, .78);
  --surface-2:   rgba(255, 255, 255, .5);
  --surface-3:   rgba(112, 100, 168, .12);
  --line:        rgba(26, 18, 54, .10);
  --line-2:      rgba(26, 18, 54, .20);

  /* Нейтральные с уклоном в фиолетовый: чистый серый рядом со свечением
     выглядит грязным пятном, а не спокойным цветом. */
  --text:        #171226;
  --text-2:      #4B4363;
  /* Темнее прежнего #6E6689: тот давал ~4.4:1 на светлом фоне — ниже AA,
     а этим цветом набраны самые мелкие подписи (часы сетки, дни пикера). */
  --text-3:      #615A7C;

  --accent:      #5B34E0;
  --accent-2:    #4525B8;
  --accent-soft: rgba(91, 52, 224, .13);
  --on-accent:   #FFFFFF;

  --brass:       #8A5D00;
  --brass-soft:  rgba(255, 184, 77, .22);

  --ok:          #0C7A5C;
  --ok-soft:     rgba(12, 122, 92, .14);
  --warn:        #8A5D00;
  --warn-soft:   rgba(255, 184, 77, .24);
  --crit:        #C22B45;
  --crit-soft:   rgba(194, 43, 69, .13);

  /* Верхняя кромка стекла: тонкая светлая линия, из-за которой пластина
     читается как объём, а не как выцветший прямоугольник. */
  --glass-edge:  rgba(255, 255, 255, .85);
  --glass-blur:  30px;

  /* Стекло — это не одно размытие.
   *
   * Кроме фроста на нём видно ещё три вещи, и без них поверхность читается
   * матовым пластиком: заливка с перепадом от угла к углу, кромка светлее самой
   * поверхности и косой блик на крупных панелях.
   *
   * Чего в браузере нет — преломления: backdrop-filter умеет размывать фон,
   * но не искажать, поэтому «линзы» по краю не получится. */
  /* Оттенок стекла — теперь еле уловимый. Голубизну ставили, пока окно терялось
     на фоне; отделять его взялось затемнение подложки, и цвет стал лишним.
     Оставлен намёк: чисто белая пластина читается бумагой. Держим числами,
     а не готовым цветом: сменить настроение — одна строка. */
  --glass-cast: 234, 243, 255;

  /* Перепад слабый намеренно. Он складывается с собственной заливкой поверхности,
     и стоит его усилить — стекло становится крашеным металлом: свет по нему
     идёт, а сквозь него уже ничего не видно. */
  --glass-tint: linear-gradient(145deg,
    rgba(var(--glass-cast), .34), rgba(var(--glass-cast), .08) 46%, rgba(var(--glass-cast), .22));
  --glass-line: rgba(255, 255, 255, .62);
  --glass-lit:
    inset 1px 1px 0 rgba(255, 255, 255, .55),
    inset -1px -1px 0 rgba(255, 255, 255, .14);
  /* Поверхность окна — почти прозрачная, и это весь смысл: сквозь неё обязан
     просвечивать размытый фон. Читаемость держит не заливка, а размытие. */
  --surface-glass: rgba(var(--glass-cast), .22);

  /* Поверхность кнопки — плотнее панели, на которой она лежит. Прозрачная
     кнопка на прозрачном окне сливается с ним. */
  --btn-face:    rgba(252, 252, 255, .82);
  --btn-face-hi: rgba(255, 255, 255, .95);
  --btn-edge:    rgba(26, 18, 54, .16);

  /* Плотная основа под плашкой занятия. Цвет плашки — прозрачная заливка
     в 13–24 %, и сквозь неё видна разметка часов, нарисованная фоном колонки.
     Основа закрывает разметку, заливка кладётся поверх и цвет сохраняет. */
  --plate-base: #F8F7FC;

  --scroll:    rgba(26, 18, 54, .22);
  --scroll-hi: rgba(26, 18, 54, .36);

  --shadow-card: 0 2px 10px -4px rgba(23, 18, 38, .16);
  --shadow-pop:  0 24px 60px -20px rgba(23, 18, 38, .36);

  --r-card: 18px;
  --r-ctl:  11px;
  --r-sm:   8px;
  --r-pill: 999px;

  /* Onest в заголовках, Golos Text в тексте — те же, что на лендинге.
     Системный стек стоял здесь с первых дней и был недоработкой, а не
     выбором. Моноширинный остаётся: цифры в расписании и деньгах стоят
     столбцами, и табличные цифры там условие, а не украшение. */
  --display: "Onest", "Segoe UI Variable Display", "Segoe UI", system-ui, sans-serif;
  --ui:   "Golos Text", "Segoe UI Variable Text", "Segoe UI", system-ui, -apple-system, "Helvetica Neue", Arial, sans-serif;
  --mono: "Cascadia Mono", Consolas, "SF Mono", ui-monospace, "Liberation Mono", monospace;
}

/* Тёмная тема включается только явно — кнопкой в шапке, которая ставит
   data-theme. Правила @media (prefers-color-scheme: dark) здесь нет намеренно:
   светлая тема должна быть одинаковой у всех, независимо от настроек системы.

   Это не инверсия светлой, а своя картина: чёрное с золотом, свечение
   выкручено до цвета. Стекло тут тёмное — светлые пластины на чёрном
   светились бы сами и забивали текст. */
:root[data-theme="dark"] {
  /* Календарь, полосы прокрутки и прочее системное — тоже тёмными: светлый
     календарь на чёрной панели виден как вспышка. */
  color-scheme: dark;

  /* Золото в фон почти не пускаем и держим его в дальнем углу: смешавшись с
     фиолетовым под полупрозрачной пластиной, оно даёт грязно-коричневый —
     тот самый цвет, которым выглядит «дорого» ровно до первого взгляда.
     Золото живёт в элементах: чипы агента, деньги, акценты. */
  --aurora-1: rgba(109, 59, 255, .52);
  --aurora-2: rgba(194, 31, 78, .30);
  --aurora-3: rgba(240, 168, 30, .20);
  --aurora-4: rgba(46, 96, 220, .34);

  --bg: #07060D;
  --surface: rgba(24, 21, 38, .66);
  --surface-2: rgba(38, 33, 58, .52);
  --surface-3: rgba(62, 54, 92, .5);
  --line: rgba(255, 255, 255, .10);
  --line-2: rgba(255, 255, 255, .20);

  --text: #F1EEFA; --text-2: #B7AFCE; --text-3: #9C94B8;

  --accent: #A98BFF; --accent-2: #C2ABFF;
  --accent-soft: rgba(169, 139, 255, .18); --on-accent: #0B0813;

  --brass: #F5C044; --brass-soft: rgba(245, 192, 68, .16);
  --ok: #3ED598; --ok-soft: rgba(62, 213, 152, .15);
  --warn: #F0B33F; --warn-soft: rgba(240, 179, 63, .15);
  --crit: #FF6B7E; --crit-soft: rgba(255, 107, 126, .15);

  --glass-edge: rgba(255, 255, 255, .13);

  /* В тёмной теме стекло держится на кромке, а не на заливке: белого здесь
     столько, чтобы поверхность отделилась от фона, и ни каплей больше. */
  /* В тёмной теме холодная нота ярче по цвету и слабее по плотности: на чёрном
     даже пять процентов голубого видно, а десять уже красят панель в синий. */
  --glass-cast: 205, 224, 255;
  --glass-tint: linear-gradient(145deg,
    rgba(var(--glass-cast), .11), rgba(var(--glass-cast), .03) 46%, rgba(var(--glass-cast), .07));
  --glass-line: rgba(255, 255, 255, .22);
  --glass-lit:
    inset 1px 1px 0 rgba(255, 255, 255, .14),
    inset -1px -1px 0 rgba(255, 255, 255, .04);
  --surface-glass: rgba(14, 18, 34, .28);

  --btn-face:    rgba(52, 47, 76, .78);
  --btn-face-hi: rgba(66, 60, 94, .88);
  --btn-edge:    rgba(255, 255, 255, .18);
  --plate-base:  #14111E;
  --scroll:      rgba(255, 255, 255, .17);
  --scroll-hi:   rgba(255, 255, 255, .30);

  --shadow-card: 0 2px 12px -4px rgba(0, 0, 0, .5);
  --shadow-pop: 0 28px 70px -22px rgba(0, 0, 0, .8);
}

:root[data-theme="light"] {
  --aurora-1: rgba(124, 92, 255, .28);
  --aurora-2: rgba(255, 92, 138, .18);
  --aurora-3: rgba(255, 184, 77, .22);

  --bg: #E8E4F6;
  --surface: rgba(255, 255, 255, .78);
  --surface-2: rgba(255, 255, 255, .5);
  --surface-3: rgba(112, 100, 168, .12);
  --line: rgba(26, 18, 54, .10);
  --line-2: rgba(26, 18, 54, .20);

  --text: #171226; --text-2: #4B4363; --text-3: #6E6689;

  --accent: #5B34E0; --accent-2: #4525B8;
  --accent-soft: rgba(91, 52, 224, .13); --on-accent: #FFFFFF;

  --brass: #8A5D00; --brass-soft: rgba(255, 184, 77, .22);
  --ok: #0C7A5C; --ok-soft: rgba(12, 122, 92, .14);
  --warn: #8A5D00; --warn-soft: rgba(255, 184, 77, .24);
  --crit: #C22B45; --crit-soft: rgba(194, 43, 69, .13);

  --glass-edge: rgba(255, 255, 255, .85);
  --shadow-card: 0 2px 10px -4px rgba(23, 18, 38, .16);
  --shadow-pop: 0 24px 60px -20px rgba(23, 18, 38, .36);
}

/* ── база ─────────────────────────────────────────────────── */
*, *::before, *::after { box-sizing: border-box; }
html, body { height: 100%; }

/* Полосы прокрутки.
 *
 * Системная полоса не знает про нашу тему: в тёмной она остаётся светлой
 * и оказывается самым ярким пятном на экране — ярче кнопок и заголовков.
 * scrollbar-color наследуется, поэтому хватает объявления на html; правила
 * ::-webkit-scrollbar нужны отдельно — там, где браузер слушает только их. */
html { scrollbar-width: thin; scrollbar-color: var(--scroll) transparent; }
::-webkit-scrollbar { width: 10px; height: 10px; }
::-webkit-scrollbar-track, ::-webkit-scrollbar-corner { background: transparent; }
::-webkit-scrollbar-thumb {
  /* Прозрачная рамка с background-clip даёт зазор от края без второго элемента:
     ползунок получается тонким, но попасть в него по-прежнему легко. */
  background: var(--scroll); background-clip: padding-box;
  border: 3px solid transparent; border-radius: 999px;
}
::-webkit-scrollbar-thumb:hover { background: var(--scroll-hi); background-clip: padding-box; }

/* Правило браузера для [hidden] идёт с нулевой специфичностью, поэтому любой
   display в классе его перебивает: .btn { display: inline-flex } — и спрятанная
   кнопка остаётся на экране. Возвращаем атрибуту силу один раз на весь проект. */
[hidden] { display: none !important; }

body {
  margin: 0;
  color: var(--text);
  font: 400 13.5px/1.5 var(--ui);
  -webkit-font-smoothing: antialiased;
  background-color: var(--bg);
}

/* Свечение прибито к окну: панель прокручивается внутри своих областей,
   и фон не должен ездить вместе с содержимым.
 *
 * Отдельным слоем, а не background-attachment: fixed у body. Фиксированный
 * фон вместе с backdrop-filter на крупных поверхностях браузер перерисовывает
 * неверно — на странице ученика это вылезло чёрным прямоугольником во весь
 * экран при прокрутке. Здесь та же связка, так что чиним одинаково, не дожидаясь
 * повторения. */
body::before {
  content: '';
  position: fixed;
  inset: 0;
  z-index: -1;
  pointer-events: none;
  /* Пятен пять, и два из них в середине экрана: по углам их было мало, чтобы
     стекло читалось стеклом. Фрост показывает то, что под ним, — значит цвет
     нужен там, где лежит содержимое, а не только по краям. */
  background-image:
    radial-gradient(1200px 760px at 6% -14%, var(--aurora-1), transparent 64%),
    radial-gradient(980px 660px at 98% 0%, var(--aurora-2), transparent 62%),
    radial-gradient(900px 620px at 4% 112%, var(--aurora-3), transparent 60%),
    radial-gradient(920px 680px at 56% 34%, var(--aurora-4), transparent 62%),
    radial-gradient(780px 580px at 88% 98%, var(--aurora-2), transparent 60%);
  background-repeat: no-repeat;
}

h1, h2, h3, p, ol, ul { margin: 0; }
ol, ul { list-style: none; padding: 0; }
button { font: inherit; color: inherit; }
.mono { font-family: var(--mono); font-variant-numeric: tabular-nums; }

:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
  border-radius: var(--r-sm);
}

/* Стекло. Одно правило на все крупные поверхности: полупрозрачная заливка,
   размытие того, что под ней, и светлая кромка сверху. Перечень намеренно
   короткий — см. шапку файла про цену backdrop-filter. */
/* Всплывающих слоёв здесь нет: у них своё правило в конце файла и другие
   числа — размытие сильнее, тень выше. Пока окно стояло и тут, и там, две
   записи спорили друг с другом молча, а выигрывала та, что ниже по файлу. */
.rail, .topbar, .kpi, .card, .roster__list, .roster__card,
.hwlist, .rule, .tg, .pcard {
  -webkit-backdrop-filter: blur(var(--glass-blur)) saturate(1.4);
  backdrop-filter: blur(var(--glass-blur)) saturate(1.4);
  box-shadow: var(--shadow-card), inset 0 1px 0 var(--glass-edge);
}

/* Календарь и конструктор — без размытия сознательно. Под ними всё равно
   ничего не видно: сетка закрывает поверхность целиком. Зато внутри них
   постоянно что-то перерисовывается — плашка едет за курсором, в поле идёт
   набор, — а перерисовка внутри backdrop-filter стоит кадров. */
.gridwrap, .builder {
  box-shadow: var(--shadow-card), inset 0 1px 0 var(--glass-edge);
}

svg { width: 100%; height: 100%; display: block; }
[data-icon] { display: inline-block; width: 16px; height: 16px; flex: 0 0 auto; }

/* ── каркас ───────────────────────────────────────────────── */
.app { display: grid; grid-template-columns: 236px minmax(0, 1fr); height: 100vh; height: 100dvh; }

.rail {
  display: flex; flex-direction: column;
  background: var(--surface);
  border-right: 1px solid var(--line);
  padding: 14px 12px 12px;
  /* Рельс целиком не прокручивается: марка, аккаунт и подвал стоят на месте,
     прокручивается только список разделов — и только если не влез. На
     планшете владельца рельс с overflow: auto уезжал вместе с пальцем
     (07.09.2026): марка и аккаунт скрывались за верхним краем. */
  overflow: hidden; min-height: 0;
}
.rail__brand { display: flex; align-items: center; gap: 9px; padding: 4px 8px 16px; }
.mark { width: 22px; height: 22px; color: var(--accent); }
.rail__name { font-size: 15px; font-weight: 650; letter-spacing: -.015em; }

.rail__nav {
  display: flex; flex-direction: column; gap: 1px;
  flex: 1 1 auto; min-height: 0; overflow-y: auto; overscroll-behavior: contain; scrollbar-width: thin;
}
/* Невысокий экран (планшет лёжа): ужимаем пункты, прячем подвал — чтобы
   список разделов влез целиком и прокручивать было нечего. */
@media (min-width: 981px) and (max-height: 720px) {
  /* .rail впереди — иначе базовое правило .navitem ниже по файлу перебивает. */
  .rail .navitem { padding: 5px 8px; }
  .rail .rail__label { padding: 10px 8px 5px; }
  .rail .rail__foot { display: none; }
}
.rail__label {
  font: 500 10.5px/1 var(--mono);
  letter-spacing: .1em; text-transform: uppercase;
  color: var(--text-3); padding: 16px 8px 7px;
}
.rail__nav .rail__label:first-child { padding-top: 0; }

/* ═══════════════ Спотлайт-тур новичка ═══════════════
   Затемнение — гигантской тенью «окна»: дырка в оверлее без масок и SVG. */
.tour { position: fixed; inset: 0; z-index: 200; }
.tour__hole {
  position: absolute; border-radius: 12px; pointer-events: none;
  border: 2px solid var(--accent);
  box-shadow: 0 0 0 9999px rgba(6, 5, 16, .6);
  transition: left .25s ease, top .25s ease, width .25s ease, height .25s ease;
}
.tour__card {
  position: absolute; width: min(340px, calc(100vw - 32px)); padding: 16px 17px;
  background: var(--surface); border: 1px solid var(--line);
  border-radius: var(--r-card); box-shadow: var(--shadow-pop);
  /* Стекло, как у окон: без размытия сквозь карточку читался текст
     панели (владелец с планшета, 10.09.2026). */
  -webkit-backdrop-filter: blur(18px) saturate(1.6);
  backdrop-filter: blur(18px) saturate(1.6);
}
.tour__card.is-center { left: 50%; top: 50%; transform: translate(-50%, -50%); }
.tour__step {
  font: 500 10px/1 var(--mono); letter-spacing: .09em;
  text-transform: uppercase; color: var(--text-3);
}
.tour__title { font-size: 16px; font-weight: 700; margin-top: 7px; }
.tour__text { font-size: 13px; line-height: 1.55; color: var(--text-2); margin-top: 6px; }
.tour__row { display: flex; align-items: center; gap: 8px; margin-top: 14px; }
.tour__skip { margin-left: auto; background: none; border: 0; color: var(--text-3); cursor: pointer; font-size: 12.5px; }
.tour__skip:hover { color: var(--text); }
@media (prefers-reduced-motion: reduce) { .tour__hole { transition: none; } }

/* Карточка-показатель как дверь: стрелка в углу обещает переход, наведение
   подтверждает. Цифры не трогаем — меняется только поведение рамки. */
.kpi--link { cursor: pointer; position: relative; transition: border-color .12s ease, transform .12s ease; }
.kpi--link:hover { border-color: var(--accent); transform: translateY(-1px); }
.kpi--link:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.kpi__go {
  position: absolute; top: 10px; right: 12px;
  font: 500 13px/1 var(--mono); color: var(--text-3);
  transition: color .12s ease, transform .12s ease;
}
.kpi--link:hover .kpi__go { color: var(--accent); transform: translateX(2px); }

/* Пальцу — мишени крупнее мышиных; вид на ПК не меняется. */
/* Прыжок к содержимому: с клавиатуры каждый экран начинался с прохода
   через весь рельс — десяток остановок табом до поля поиска. */
.skiplink {
  position: absolute; left: 10px; top: -48px; z-index: 100;
  padding: 9px 14px; border-radius: var(--r-ctl);
  background: var(--accent); color: var(--on-accent);
  font-size: 13px; text-decoration: none;
  transition: top .12s ease;
}
.skiplink:focus-visible { top: 10px; }

.navitem {
  display: flex; align-items: center; gap: 9px;
  width: 100%; padding: 7px 8px; text-align: left;
  background: none; border: 0; border-radius: var(--r-ctl);
  color: var(--text-2); cursor: pointer;
  transition: background .12s, color .12s;
  /* Пунктом бывает и ссылка (Доски) — ей нужно снять подчёркивание
     и вернуть размер шрифта кнопки. */
  text-decoration: none; font: inherit; box-sizing: border-box;
}
.navitem:hover { background: var(--surface-2); color: var(--text); }
.navitem.is-active { background: var(--accent-soft); color: var(--accent); font-weight: 600; }
.navitem__ico { width: 17px; height: 17px; flex: 0 0 auto; opacity: .9; }
.navitem__badge, .navitem__count {
  margin-left: auto;
  font: 500 11px/1 var(--mono); font-variant-numeric: tabular-nums;
  padding: 3px 6px; border-radius: 999px;
}
.navitem__badge { background: var(--accent); color: var(--on-accent); }
.navitem__badge--warn { background: var(--warn); color: var(--on-accent); }
.navitem__count { color: var(--text-3); }
.navitem.is-active .navitem__count { color: var(--accent); }

.rail__foot { margin-top: auto; padding-top: 18px; }

/* Карточка аккаунта стоит над меню и сама открывает профиль. Кнопка выхода
   внутри — вторая, соседняя: вложенных кнопок в HTML не бывает, а обе должны
   быть доступны с клавиатуры. */
.tenant {
  display: flex; align-items: center; gap: 4px;
  margin-bottom: 6px;
  padding: 4px; border: 1px solid var(--line); border-radius: var(--r-card);
  background: var(--surface-2);
}
.tenant__main {
  display: flex; align-items: center; gap: 9px;
  flex: 1 1 auto; min-width: 0;
  padding: 4px; border: 0; border-radius: var(--r-ctl);
  background: none; color: inherit; font: inherit; text-align: left; cursor: pointer;
  transition: background .12s;
}
.tenant__main:hover { background: var(--surface-3); }
.tenant.is-active { border-color: var(--accent); background: var(--accent-soft); }
.tenant.is-active .tenant__name { color: var(--accent); }
.tenant.is-active .tenant__main:hover { background: color-mix(in srgb, var(--accent) 10%, transparent); }
.avatar {
  display: grid; place-items: center;
  width: 28px; height: 28px; flex: 0 0 auto;
  border-radius: 999px; background: var(--accent); color: var(--on-accent);
  font: 600 10.5px/1 var(--mono); letter-spacing: .04em;
}
.avatar--tg { background: var(--brass); color: var(--surface); }
.tenant__txt { display: flex; flex-direction: column; min-width: 0; }
.tenant__name { font-size: 12.5px; font-weight: 600; }
.tenant__meta.is-empty { opacity: .7; font-style: italic; }
.tenant__meta { font-size: 11.5px; color: var(--text-3); }
.tenant__note {
  font-size: 11px; line-height: 1.45; color: var(--text-3);
  padding: 9px 8px 2px;
}

/* ── холст ────────────────────────────────────────────────── */
/* Холст прозрачен: свечение рисует body, и перекрывать его здесь нечем. */
/* min-height: 0 — иначе .views не сжимается ниже своего содержимого,
   колонка вырастает выше окна и прокручивается вся страница вместе с
   рельсой (владелец, 09.09.2026: «боковая панель листается вместе с
   сайтом»). Прокручиваться должен только .views. */
.canvas { display: flex; flex-direction: column; min-width: 0; min-height: 0; }

.topbar {
  display: flex; align-items: center; gap: 16px;
  height: 54px; flex: 0 0 auto; padding: 0 24px;
  border-bottom: 1px solid var(--line);
  background: color-mix(in srgb, var(--bg) 82%, transparent);
  backdrop-filter: blur(8px);
}
.topbar__search {
  position: relative; display: flex; align-items: center;
  width: min(420px, 46%);
}
.topbar__searchico {
  position: absolute; left: 9px; width: 15px; height: 15px; color: var(--text-3);
  pointer-events: none;
}
.topbar__search input {
  width: 100%; padding: 6px 34px 6px 30px;
  background: var(--surface); color: var(--text);
  border: 1px solid var(--line); border-radius: var(--r-ctl);
  font: inherit;
}
.topbar__search input::placeholder { color: var(--text-3); }
.topbar__search kbd {
  position: absolute; right: 8px;
  font: 500 10.5px/1 var(--mono); color: var(--text-3);
  border: 1px solid var(--line); border-radius: 3px; padding: 3px 5px;
}
.topbar__right { display: flex; align-items: center; gap: 10px; margin-left: auto; }

.pulse {
  display: inline-flex; align-items: center; gap: 7px;
  font-size: 12px; color: var(--text-2);
  padding: 4px 10px 4px 8px; border: 1px solid var(--line);
  border-radius: 999px; background: var(--surface);
}
.pulse i {
  width: 7px; height: 7px; border-radius: 999px; background: var(--ok);
  box-shadow: 0 0 0 0 color-mix(in srgb, var(--ok) 60%, transparent);
  animation: ping 2.4s ease-out infinite;
}
@keyframes ping {
  0%   { box-shadow: 0 0 0 0 color-mix(in srgb, var(--ok) 55%, transparent); }
  70%  { box-shadow: 0 0 0 6px transparent; }
  100% { box-shadow: 0 0 0 0 transparent; }
}

.iconbtn {
  display: grid; place-items: center;
  width: 30px; height: 30px; padding: 6px;
  background: var(--surface); border: 1px solid var(--line);
  border-radius: var(--r-ctl); color: var(--text-2); cursor: pointer;
}
.iconbtn:hover { color: var(--text); border-color: var(--line-2); }

.views { flex: 1 1 auto; overflow: auto; min-height: 0; }
/* Ширину не ограничиваем: экран у репетитора рабочий, и пустое поле справа на
   широком мониторе — потерянное место, а не воздух. За длину строки отвечают
   сами блоки внутри, а не общий потолок на весь экран. */
.view { display: none; padding: 24px 24px 56px; }
.view.is-active { display: block; animation: rise .22s ease-out; }
@keyframes rise { from { opacity: 0; transform: translateY(5px); } to { opacity: 1; transform: none; } }

/* ── шапка страницы ───────────────────────────────────────── */
.pagehead {
  display: flex; align-items: flex-end; gap: 20px; flex-wrap: wrap;
  padding-bottom: 18px;
}
.pagehead__title {
  font-size: 25px; font-weight: 650; letter-spacing: -.022em; line-height: 1.15;
  text-wrap: balance;
}
.pagehead__sub { font-size: 13px; color: var(--text-2); padding-top: 4px; }
.pagehead__actions { display: flex; gap: 8px; margin-left: auto; }

/* ── управляющие элементы ─────────────────────────────────── */
/* Кнопка — плоская и матовая.
 *
 * Прозрачная кнопка на прозрачной панели сливается с ней: два стекла подряд
 * глаз не различает, — поэтому поверхность плотная. Но объёма ей не нужно:
 * светлая грань сверху выдавливала кнопку из плоскости и спорила со стеклом,
 * которое само по себе плоское. Осталась мягкая тень — она про то, что кнопка
 * ближе к смотрящему, а не про её толщину. */
.btn {
  display: inline-flex; align-items: center; gap: 7px;
  padding: 7px 13px; border-radius: var(--r-ctl); cursor: pointer;
  border: 1px solid var(--btn-edge); background: var(--btn-face); color: var(--text);
  box-shadow: 0 2px 6px -2px rgba(23, 18, 38, .16);
  font-size: 13px; font-weight: 500; white-space: nowrap;
  transition: background .12s, border-color .12s, color .12s;
}
.btn:hover { border-color: var(--line-2); background: var(--btn-face-hi); }
.btn--primary { background: var(--accent); border-color: var(--accent); color: var(--on-accent); }
.btn--primary:hover { background: var(--accent-2); border-color: var(--accent-2); }
.btn--ghost { background: transparent; }
.btn--sm { padding: 5px 10px; font-size: 12.5px; }
.btn--warn { border-color: var(--warn); color: var(--warn); background: var(--warn-soft); }
.btn [data-icon] { width: 15px; height: 15px; }

.linkbtn {
  background: none; border: 0; padding: 0; cursor: pointer;
  color: var(--accent); font-size: 12.5px; font-weight: 500;
}
.linkbtn:hover { text-decoration: underline; }

/* align-items: подпись месяца между стрелками — не кнопка, и без выравнивания
   она прижималась к верху рамки (владелец, 12.09.2026). */
.segmented { display: inline-flex; align-items: center; border: 1px solid var(--line); border-radius: var(--r-ctl); overflow: hidden; }
.segmented button {
  padding: 6px 13px; background: var(--surface); border: 0; cursor: pointer;
  font-size: 12.5px; color: var(--text-2);
}
.segmented button + button { border-left: 1px solid var(--line); }
/* Средняя кнопка листалки расписания показывает отрезок и не рвётся: на
   телефоне ужимается с многоточием, стрелки по бокам остаются целыми. */
#weekToday { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-variant-numeric: tabular-nums; }
#weekToday.is-now { color: var(--accent); font-weight: 600; }
.segmented button.is-active { background: var(--accent-soft); color: var(--accent); font-weight: 600; }

/* ── статусные метки ──────────────────────────────────────── */
.chip {
  display: inline-flex; align-items: center; gap: 6px;
  padding: 3px 9px; border-radius: 999px;
  font-size: 11.5px; font-weight: 500; white-space: nowrap;
  background: var(--surface-3); color: var(--text-2);
}
.chip--ok   { background: var(--ok-soft);   color: var(--ok); }
.chip--warn { background: var(--warn-soft); color: var(--warn); }
.chip--crit { background: var(--crit-soft); color: var(--crit); }
.chip--info { background: var(--accent-soft); color: var(--accent); }
.chip--ai   { background: var(--brass-soft); color: var(--brass); }
.chip [data-icon] { width: 13px; height: 13px; }

.dot { width: 7px; height: 7px; border-radius: 999px; background: var(--text-3); flex: 0 0 auto; }
.dot--ok { background: var(--ok); }
.dot--warn { background: var(--warn); }
.dot--crit { background: var(--crit); }
.dot--done { background: var(--line-2); }

/* ── секции ───────────────────────────────────────────────── */
.sectionhead {
  display: flex; align-items: baseline; gap: 12px;
  /* Без переноса подсказка на телефоне ложилась в две строки ПОВЕРХ
     заголовка: у обоих выравнивание по базовой линии, и вторая строка
     подсказки уходила под первую строку «Ленты занятий». */
  flex-wrap: wrap;
  padding: 4px 0 10px;
}
.sectionhead h2 {
  font: 500 10.5px/1 var(--mono);
  letter-spacing: .12em; text-transform: uppercase; color: var(--text-3);
}
.sectionhead__hint { font-size: 11.5px; color: var(--text-3); margin-left: auto; }
/* На узком экране подсказке отдаётся своя строка целиком. */
@media (max-width: 760px) {
  .sectionhead__hint { margin-left: 0; flex: 1 0 100%; }
}
.sectionhead .linkbtn { margin-left: auto; }

.card {
  background: var(--surface); border: 1px solid var(--line);
  border-radius: var(--r-card);
}

/* ── KPI ──────────────────────────────────────────────────── */
.kpis { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 12px; padding-bottom: 22px; }
.kpi {
  background: var(--surface); border: 1px solid var(--line); border-radius: var(--r-card);
  padding: 13px 14px 12px; display: flex; flex-direction: column; gap: 8px; min-width: 0;
}
.kpi__label {
  font: 500 10.5px/1 var(--mono); letter-spacing: .1em; text-transform: uppercase; color: var(--text-3);
}
.kpi__row { display: flex; align-items: flex-end; gap: 8px; }
.kpi__value {
  font-family: var(--mono); font-variant-numeric: tabular-nums;
  font-size: 27px; font-weight: 600; letter-spacing: -.03em; line-height: 1;
}
/* Доход месяца — шестизначный: мельче остальных чисел, иначе «152 200 ₽»
   не входит в плитку. Классом, а не style: на телефоне размер свой. */
.kpi__value--money { font-size: 22px; }
.kpi__unit { font-size: 15px; color: var(--text-2); padding-bottom: 2px; }
.kpi__hint { font-size: 11.5px; color: var(--text-3); line-height: 1.4; }
.kpi__delta { font-size: 11.5px; color: var(--ok); font-weight: 500; }
.kpi__viz { height: 34px; margin-top: auto; }
.meter { height: 4px; background: var(--surface-3); border-radius: 999px; overflow: hidden; }
.meter i { display: block; height: 100%; background: var(--accent); border-radius: 999px; }

/* ── две колонки ──────────────────────────────────────────── */
.split { display: grid; grid-template-columns: minmax(0, 1fr) 350px; gap: 22px; align-items: start; }
.split__main, .split__side { min-width: 0; }
/* Уведомления не длиннее ленты занятий: на широком экране колонка
   растягивается в рост занятий, а сам список лежит в ней абсолютно и
   прокручивается внутри. Ниже — «Все» открывает окно со всем списком
   (владелец, 09.09.2026: «у меня что ли бесконечная страница будет?»).
   В одну колонку этот приём не работает — там список режется по счёту,
   см. FEED_PHONE в app.js. */
@media (min-width: 981px) {
  .split { align-items: stretch; }
  .split__side { position: relative; min-height: 440px; }
  .split__side .sectionhead { height: 44px; box-sizing: border-box; }
  .split__side .feed { position: absolute; left: 0; right: 0; top: 44px; bottom: 0; overflow-y: auto; scrollbar-width: thin; }
}
.feedmodal .feed { max-height: none; }

/* ── лента занятий ────────────────────────────────────────── */
.timeline { display: flex; flex-direction: column; gap: 8px; }
.lesson {
  position: relative; display: grid;
  grid-template-columns: 74px minmax(0, 1fr) auto;
  gap: 14px; align-items: center;
  background: var(--surface); border: 1px solid var(--line); border-radius: var(--r-card);
  padding: 12px 14px 12px 13px;
  border-left: 3px solid var(--line-2);
}
.lesson--live    { border-left-color: var(--accent); background: color-mix(in srgb, var(--accent-soft) 42%, var(--surface)); }
.lesson--warn    { border-left-color: var(--warn); }
.lesson--done    { border-left-color: transparent; opacity: .72; }
.lesson--done:hover { opacity: 1; }

.lesson__time { display: flex; flex-direction: column; gap: 2px; }
.lesson__from { font-family: var(--mono); font-variant-numeric: tabular-nums; font-size: 16px; font-weight: 600; letter-spacing: -.02em; }
.lesson__to   { font-family: var(--mono); font-size: 11.5px; color: var(--text-3); }

.lesson__body { min-width: 0; display: flex; flex-direction: column; gap: 5px; }
.lesson__who { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.lesson__name { font-size: 14px; font-weight: 600; letter-spacing: -.01em; }
.lesson__grade { font-size: 11.5px; color: var(--text-3); }
.lesson__topic { font-size: 12.5px; color: var(--text-2); }
.lesson__meta { display: flex; align-items: center; gap: 7px; flex-wrap: wrap; }
.lesson__fixed {
  display: inline-flex; align-items: center; gap: 6px;
  font-size: 11.5px; color: var(--brass);
}
.lesson__fixed [data-icon] { width: 13px; height: 13px; }
.lesson__acts { display: flex; align-items: center; gap: 7px; }

.live-tag {
  display: inline-flex; align-items: center; gap: 6px;
  font: 600 10.5px/1 var(--mono); letter-spacing: .1em; text-transform: uppercase;
  color: var(--accent);
}
.live-tag i { width: 6px; height: 6px; border-radius: 999px; background: var(--accent); animation: blink 1.6s ease-in-out infinite; }
@keyframes blink { 50% { opacity: .25; } }

/* ── лента агента ─────────────────────────────────────────── */
.feed {
  display: flex; flex-direction: column;
  background: var(--surface); border: 1px solid var(--line); border-radius: var(--r-card);
  overflow: hidden;
}
.fevent { display: grid; grid-template-columns: 26px minmax(0, 1fr); gap: 11px; padding: 12px 14px; }
.fevent + .fevent { border-top: 1px solid var(--line); }
.fevent__ico {
  display: grid; place-items: center; width: 26px; height: 26px; padding: 5px;
  border-radius: 999px; background: var(--surface-3); color: var(--text-2);
}
.fevent__ico--ai    { background: var(--brass-soft); color: var(--brass); }
.fevent__ico--ok    { background: var(--ok-soft); color: var(--ok); }
.fevent__ico--warn  { background: var(--warn-soft); color: var(--warn); }
.fevent__ico--info  { background: var(--accent-soft); color: var(--accent); }
.fevent__top { display: flex; align-items: baseline; gap: 8px; }
.fevent__title { font-size: 12.5px; font-weight: 600; line-height: 1.35; }
.fevent__t { margin-left: auto; font: 400 11px/1 var(--mono); color: var(--text-3); white-space: nowrap; }
.fevent__body { font-size: 12px; color: var(--text-2); line-height: 1.45; padding-top: 3px; }
.fevent__act { padding-top: 7px; }

/* ── недельная сетка ──────────────────────────────────────── */
.legend { display: flex; gap: 16px; padding-bottom: 12px; font-size: 12px; color: var(--text-2); }
.legend span { display: inline-flex; align-items: center; gap: 6px; }

/* Сетка прокручивается внутри себя, шапка с днями липнет к её верху.
   Шкала теперь полные сутки: показывать все 24 часа сразу — значит сделать час
   высотой в пиксель, а на такой сетке ничего не разобрать и никуда не попасть. */
.gridwrap {
  overflow: auto;
  border: 1px solid var(--line); border-radius: var(--r-card); background: var(--surface);
}
/* ── страница расписания не листается, листается только сетка ──
   Раньше сетка ограничивалась max-height «экран минус столько-то», а всё
   вокруг — заголовок, легенда, поля — жило своей высотой, и на телефоне
   палец не знал, что тянет: страницу или сетку (владелец, 28.09.2026).
   Теперь на активном «Расписании» каркас берёт высоту экрана, область
   разделов ничего не прокручивает, а сетка растягивается на остаток и
   прокручивается одна. Класс is-sched ставит go(). */
body.is-sched .views { display: flex; flex-direction: column; overflow: hidden; }
.view[data-view="schedule"].is-active { display: flex; flex-direction: column; flex: 1 1 auto; min-height: 0; padding-bottom: 24px; }
.view[data-view="schedule"] .pagehead, .legend--sched { flex: 0 0 auto; }
.view[data-view="schedule"] .gridwrap { flex: 1 1 auto; min-height: 0; }
/* Календарь. Колонки дней вместо часовых клеток: занятие позиционируется по
   минутам, а высота плашки равна длительности. Только так час отличается от
   полутора на глаз и появляется куда перетаскивать. */
.weekgrid { min-width: 880px; }
.cal { --cols: 54px repeat(7, minmax(112px, 1fr)); }

.cal__head {
  display: grid; grid-template-columns: var(--cols);
  position: sticky; top: 0; z-index: 3; background: var(--plate-base);
}
.cal__corner, .cal__day { border-right: 1px solid var(--line); border-bottom: 1px solid var(--line); }
.cal__day { padding: 9px 10px; display: flex; align-items: baseline; gap: 6px; }
.cal__day b { font-size: 12.5px; font-weight: 600; }
.cal__day span { font: 400 11px/1 var(--mono); color: var(--text-3); }
.cal__day.is-today { background: var(--accent-soft); }
.cal__day.is-today b, .cal__day.is-today span { color: var(--accent); }

.cal__body { display: grid; grid-template-columns: var(--cols); }

.cal__gutter { border-right: 1px solid var(--line); background: var(--surface-2); }
.cal__hour {
  height: var(--hour); padding: 3px 8px 0 0; text-align: right;
  font: 400 10.5px/1 var(--mono); color: var(--text-3);
  border-bottom: 1px solid var(--line);
}

/* Линии часов и получасов рисуем фоном, а не элементами: семьсот пустых div
   ради разметки — это то, что потом тормозит при перетаскивании. */
.cal__col {
  position: relative; border-right: 1px solid var(--line); cursor: cell;
  background-image:
    repeating-linear-gradient(to bottom,
      var(--line) 0 1px, transparent 1px var(--hour)),
    repeating-linear-gradient(to bottom,
      transparent 0 calc(var(--hour) / 2),
      color-mix(in srgb, var(--line) 45%, transparent) calc(var(--hour) / 2) calc(var(--hour) / 2 + 1px),
      transparent calc(var(--hour) / 2 + 1px) var(--hour));
}
.cal__col--alt { background-color: color-mix(in srgb, var(--surface-2) 55%, transparent); }

.cal__now { position: absolute; left: 0; right: 0; height: 2px; background: var(--crit); z-index: 2; pointer-events: none; }
.cal__now::before {
  content: ''; position: absolute; left: -3px; top: -3px;
  width: 8px; height: 8px; border-radius: 50%; background: var(--crit);
}

.ev {
  position: absolute; overflow: hidden;
  display: flex; flex-direction: column; gap: 1px;
  padding: 4px 6px; border-radius: var(--r-sm); cursor: grab;
  /* Цвет плашки — заливкой поверх плотной основы, а не одной прозрачной
     краской: иначе сквозь неё читается разметка часов из фона колонки.
     Модификаторы ниже меняют только background-image, основа общая. */
  background-color: var(--plate-base);
  background-image: linear-gradient(var(--accent-soft), var(--accent-soft));
  border: 0; border-left: 3px solid var(--accent);
  text-align: left; z-index: 1;
  /* Без этого браузер на сенсорном экране заберёт жест себе под прокрутку. */
  touch-action: none;
}
.ev:hover { filter: brightness(.97); z-index: 2; }
.ev.is-dragging { cursor: grabbing; z-index: 5; box-shadow: var(--shadow-pop); opacity: .92; }
.ev b { font-size: 11.5px; font-weight: 600; color: var(--text); }
.ev span { font: 400 10.5px/1.25 var(--mono); color: var(--text-2); }
.ev i {
  font-style: normal; font-size: 10.5px; color: var(--text-3);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.ev--warn { background-image: linear-gradient(var(--warn-soft), var(--warn-soft)); border-left-color: var(--warn); }
.ev--done { background-image: linear-gradient(var(--surface-3), var(--surface-3)); border-left-color: var(--line-2); }
.ev--done b { color: var(--text-2); font-weight: 500; }
.ev--live { background-image: none; background-color: var(--accent); border-left-color: var(--accent-2); }
.ev--live b, .ev--live span, .ev--live i { color: var(--on-accent); }

/* Отменённое занятие гасим цветом текста, а не прозрачностью всей плашки:
   прозрачная плашка снова показала бы разметку часов насквозь. */
/* Занятие через полночь — два куска одной плашки. На стыке скругления убраны:
   так видно, что это разрез по суткам, а не два разных занятия. */
.ev--head { border-bottom-left-radius: 0; border-bottom-right-radius: 0; }
.ev--tail { border-top-left-radius: 0; border-top-right-radius: 0; }
.ev--tail::before {
  content: ''; position: absolute; left: 0; right: 0; top: 0; height: 2px; opacity: .45;
  background: repeating-linear-gradient(90deg, currentColor 0 4px, transparent 4px 8px);
}

/* Плашка ниже двух строк — получас на этой шкале даёт 26 пикселей. Имя и время
   встают в строку, тема уходит совсем. Обрезанная подпись хуже отсутствующей:
   по «00:3» не видно, что это обрезок, и её читают как настоящее время. */
.ev--slim { flex-direction: row; align-items: center; gap: 6px; padding: 2px 6px; }
.ev--slim b { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.ev--slim span { flex: none; margin-left: auto; }
.ev--slim i { display: none; }

.ev--off { background-image: linear-gradient(var(--surface-2), var(--surface-2)); border-left-color: var(--line-2); }
.ev--off b { text-decoration: line-through; }
.ev--off b, .ev--off span, .ev--off i { color: var(--text-3); }

.formacts {
  grid-column: 1 / -1; display: flex; align-items: center; gap: 8px; flex-wrap: wrap;
  margin-top: 6px; padding-top: 12px; border-top: 1px solid var(--line);
}
.formacts .fld__hint { margin-left: auto; }

.btn--danger { color: var(--crit); border-color: color-mix(in srgb, var(--crit) 35%, var(--line)); }
.btn--danger:hover { background: var(--crit-soft); }

/* ── ученики ──────────────────────────────────────────────── */
.roster { display: grid; grid-template-columns: 290px minmax(0, 1fr); gap: 18px; align-items: start; }
.roster__side { min-width: 0; }
/* Список липнет к верху прокрутки: карточка ученика в разы длиннее списка,
   и без этого две трети экрана слева занимала пустота, а чтобы перейти
   к другому ученику, приходилось мотать наверх. Потолок высоты — чтобы
   длинный список прокручивался сам, а не распирал липкую позицию. */
.roster__list {
  display: flex; flex-direction: column;
  background: var(--surface); border: 1px solid var(--line); border-radius: var(--r-card); overflow: auto;
  position: sticky; top: 0; max-height: calc(100dvh - 170px);
}
.stu {
  display: grid; grid-template-columns: 30px minmax(0, 1fr); gap: 10px; align-items: center;
  padding: 11px 13px; background: none; border: 0; border-left: 3px solid transparent;
  cursor: pointer; text-align: left;
}
.stu + .stu { border-top: 1px solid var(--line); }
.stu:hover { background: var(--surface-2); }
.stu.is-active { background: var(--accent-soft); border-left-color: var(--accent); }
.stu__txt { min-width: 0; display: flex; flex-direction: column; gap: 1px; }
.stu__name { font-size: 13px; font-weight: 600; }
.stu__meta { font-size: 11.5px; color: var(--text-3); }
.stu.is-active .stu__name { color: var(--accent); }

.roster__card {
  background: var(--surface); border: 1px solid var(--line); border-radius: var(--r-card);
  padding: 20px 22px 22px; min-width: 0;
}
/* Имени — гарантированная ширина: без flex-основы колонку текста сжимали
   четыре кнопки, и «Артём Соколов» ломался на две строки при живом месте
   в ряду. Когда кнопкам тесно, они уходят второй строкой целиком — это
   ровнее, чем крошить имя или выпускать ряд за карточку. */
.sc-head { display: flex; flex-wrap: wrap; align-items: center; gap: 10px 14px; padding-bottom: 18px; border-bottom: 1px solid var(--line); }
.sc-head .avatar { width: 40px; height: 40px; font-size: 13px; }
.sc-head__txt { display: flex; flex-direction: column; gap: 3px; flex: 1 1 240px; min-width: 0; }
.sc-head__name { font-size: 19px; font-weight: 650; letter-spacing: -.02em; }
.sc-head__meta { font-size: 12.5px; color: var(--text-2); }
.sc-head__acts { margin-left: auto; display: flex; flex-wrap: wrap; justify-content: flex-end; gap: 6px 8px; }

/* Числа карточки — строкой, без плиток.
 *
 * Сетка из коробочек ставила цифру и кнопку в одинаковые рамки, и вся карточка
 * читалась как таблица настроек: глаз каждый раз заново разбирал, где здесь
 * показывают, а где предлагают нажать. Теперь цифры просто цифры — ряд значений
 * между двумя волосяными линиями, а всё нажимаемое ушло ниже, в список
 * подключений, где у каждой строки один и тот же вид. */
/* Раскладка гибкая, а не сеточная, и это принципиально. Сетка из равных колонок
   при переносе оставляет пустые клетки — ровно та дыра, из-за которой прошлый
   вариант выглядел недоделанным. Здесь числа занимают место по содержимому,
   а на переносе последняя строка растягивается: получается продолжение ряда,
   а не обрубок таблицы. */
.statline {
  display: grid; grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: 16px 22px; padding: 15px 0 16px; margin: 16px 0 2px;
  border-top: 1px solid var(--line); border-bottom: 1px solid var(--line);
}
@media (max-width: 640px) {
  .statline { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
/* Ширину задаёт сетка statline, а не само значение: во флексе семь ячеек
   вставали как попало — четыре и три разной ширины, ряды не совпадали
   колонками. Жёсткие четыре колонки дают одинаковую вертикаль обоим рядам. */
.stat { display: flex; flex-direction: column; gap: 6px; min-width: 0; }
.stat__k { font: 500 10px/1.35 var(--mono); letter-spacing: .1em; text-transform: uppercase; color: var(--text-3); }
/* Подписи разной высоты, поэтому числа прижаты к низу: иначе они гуляли бы
   по вертикали и ряд перестал бы читаться рядом. */
.stat__v {
  margin-top: auto;
  font: 600 17px/1.15 var(--mono); color: var(--text);
  font-variant-numeric: tabular-nums;
  display: flex; align-items: baseline; flex-wrap: wrap; gap: 6px;
}
/* «из 12» рядом с числом — подпись, а не второе число. */
.stat__v i { font: 500 11.5px/1 var(--mono); font-style: normal; color: var(--text-3); }
/* Плашке баланса и дате занятия кегль числа велик: это не величина. */
.stat__v--sm { font-size: 12.5px; font-weight: 500; align-items: center; }

/* Подключения ученика: чат, второй чат, доска, кабинет, свой вход. Одна строка —
   одно подключение: подпись, состояние, одна кнопка, которая его меняет. */
.conns { display: flex; flex-direction: column; margin: 18px 0 2px; }
/* Колонка подписи сжимаемая: карточка ужимается вдвое, когда рядом открыт
   список учеников, и жёсткие 172px забирали бы место у самого значения. */
.conn {
  display: grid; grid-template-columns: minmax(112px, 172px) minmax(0, 1fr) auto;
  align-items: center; gap: 14px;
  padding: 11px 0; border-bottom: 1px solid var(--line);
}
.conn__k { font: 500 10px/1.35 var(--mono); letter-spacing: .1em; text-transform: uppercase; color: var(--text-3); }
.conn__v {
  display: flex; align-items: center; flex-wrap: wrap; gap: 8px;
  min-width: 0; font-size: 12.5px;
}
.conn__v a { color: var(--accent); text-decoration: none; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.conn__v a:hover { text-decoration: underline; }
.conn__acts { display: flex; gap: 8px; justify-self: end; }

/* На узком экране подпись, состояние и кнопка встают друг под друга: три
   колонки там сжимаются до нечитаемого, а кнопка уезжает за край. */
@media (max-width: 760px) {
  .conn { grid-template-columns: minmax(0, 1fr); gap: 8px; padding: 13px 0; }
  .conn__acts { justify-self: start; }
}

/* Шаги подключения — в окне за кнопкой. В карточке это объяснение висело
   постоянно, а нужно оно один раз за всё время работы с учеником. */
.steps {
  margin: 14px 0 0; padding-left: 22px;
  display: flex; flex-direction: column; gap: 8px;
  font-size: 12.5px; line-height: 1.5; color: var(--text-2);
}
.steps li::marker { font: 600 11px var(--mono); color: var(--accent); }

.panels { display: grid; grid-template-columns: minmax(0, 1.15fr) minmax(0, 1fr); gap: 18px; }
.panels--one { grid-template-columns: minmax(0, 1fr); }
/* Один отступ на все блоки карточки ученика: раньше он задавался инлайном
   у каждого вызова и гулял между 18 и 20 пикселями. */
.panels--sec { padding-top: 20px; }
.panel { display: flex; flex-direction: column; gap: 10px; }
.panel__h {
  display: flex; align-items: center; gap: 8px;
  font: 500 10.5px/1 var(--mono); letter-spacing: .12em; text-transform: uppercase; color: var(--text-3);
}

/* Заметка правится прямо в карточке: она на то и заметка. Поле растёт под текст,
   поэтому собственная прокрутка ему не нужна и выключена. */
/* Профиль ученика — поля прямо в карточке, сеткой как в формах. Строки
   своих цен — узкие поля «мин — ₽» с крестиком. */
.profile__grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 12px 14px; }
.profile__grid .fld--wide { grid-column: 1 / -1; }
@media (max-width: 620px) { .profile__grid { grid-template-columns: 1fr; } }
.fld--prices .fld__hint { margin-top: 2px; }
.prices { display: flex; flex-direction: column; gap: 6px; }
.prices:empty { display: none; }
.pricerow { display: flex; align-items: center; gap: 7px; }
/* Через .fld: общее правило `.fld input { width: 100% }` стоит ниже по файлу
   и при равной специфичности перебивало бы узкое поле. */
.fld .pricerow input { width: 84px; flex: 0 0 auto; }
.pricerow span { font-size: 12.5px; color: var(--text-3); white-space: nowrap; }
.profile .linkbtn { align-self: flex-start; padding: 4px 0; }
.profile__acts { display: flex; gap: 8px; margin-top: 14px; }
.panel__h .notesaved { margin-left: 8px; }

.notebox {
  width: 100%; min-height: 76px; resize: none; overflow: hidden;
  font: 400 12.5px/1.55 var(--ui); color: var(--text);
  background: var(--surface-2); border: 1px solid var(--line);
  border-radius: var(--r-sm); padding: 10px 12px;
  transition: border-color .12s ease, background .12s ease;
}
.notebox:hover { border-color: var(--line-2); }
.notebox:focus { outline: none; border-color: var(--accent); background: var(--surface); }
.notebox::placeholder { color: var(--text-3); }

.notesaved {
  margin-left: auto;
  font: 500 9.5px/1 var(--mono); letter-spacing: .1em; text-transform: uppercase;
  color: var(--ok); opacity: 0; transition: opacity .18s ease;
}
.notesaved.is-on { opacity: 1; }
.panel__note {
  font-size: 12.5px; color: var(--text-2); line-height: 1.5;
  border-left: 2px solid var(--brass); padding: 2px 0 2px 11px;
}
.taglist { display: flex; flex-wrap: wrap; gap: 6px; }

/* ── графика данных ───────────────────────────────────────── */
.viz__cap { display: flex; align-items: baseline; gap: 8px; font-size: 11.5px; color: var(--text-3); }
.viz__cap b { font-family: var(--mono); font-size: 15px; font-weight: 600; color: var(--text); }
/* Точка легенды внутри строки подписи: у строчного <i> ширина и высота
   не действуют, и точки «в срок / с опозданием» не рисовались вовсе. */
.viz__cap .dot { display: inline-block; margin-right: 4px; vertical-align: 1px; }



/* Пробник — вид работы, а не её состояние, поэтому и выглядит иначе: обводка
   там, где у статусов заливка. Заводить ради него шестой цвет значило бы
   соревноваться за внимание со статусом, который стоит рядом и важнее. */
.chip--mock { background: transparent; border: 1px solid var(--accent); color: var(--accent); padding: 2px 8px; }
.btn--mock { border-color: var(--accent); color: var(--accent); background: var(--accent-soft); }
.builder__spent { margin-left: 8px; font-size: 12px; color: var(--text-3); }

/* ── задания ──────────────────────────────────────────────── */
.hwlayout { display: grid; grid-template-columns: 320px minmax(0, 1fr); gap: 18px; align-items: start; }
.hwlist { display: flex; flex-direction: column; background: var(--surface); border: 1px solid var(--line); border-radius: var(--r-card); overflow: hidden; }
.hwrow {
  display: flex; flex-direction: column; gap: 5px; padding: 11px 13px;
  background: none; border: 0; border-left: 3px solid transparent; cursor: pointer; text-align: left;
}
.hwrow + .hwrow { border-top: 1px solid var(--line); }
.hwrow:hover { background: var(--surface-2); }
.hwrow.is-active { background: var(--accent-soft); border-left-color: var(--accent); }
.hwrow__top { display: flex; align-items: baseline; gap: 8px; }
.hwrow__name { font-size: 12.5px; font-weight: 600; }
.hwrow__due { margin-left: auto; font: 400 10.5px/1 var(--mono); color: var(--text-3); }
.hwrow__sub { font-size: 11.5px; color: var(--text-2); }

/* Фильтр материалов по группе. Появляется только когда групп больше одной —
   строка кнопок, которая ничего не отсеивает, лишь занимает место. */
.matfilter { display: flex; flex-wrap: wrap; gap: 6px; padding-bottom: 10px; }
.matfilter:empty { display: none; }
.tagchip {
  font-size: 11px; font-weight: 500; line-height: 1; padding: 6px 11px;
  border-radius: var(--r-pill);
  border: 1px solid var(--line); background: var(--surface); color: var(--text-2); cursor: pointer;
}
.tagchip:hover { background: var(--surface-2); color: var(--text); }
.tagchip.is-on { background: var(--accent-soft); border-color: var(--accent); color: var(--accent); }
.tagchip__n { margin-left: 6px; font-weight: 400; opacity: .65; font-variant-numeric: tabular-nums; }
/* Полка общего банка — сплошной рамкой потемнее: своё и банк не путаются. */
.tagchip--bank { border-color: var(--line-2); color: var(--text); }

/* Материал в группе — одна строка: номер задания плашкой, тема, счёт справа. */
.matrow {
  display: flex; align-items: center; gap: 10px; width: 100%; min-height: 40px; padding: 8px 12px;
  background: none; border: 0; border-left: 3px solid transparent; cursor: pointer; text-align: left;
  color: var(--text); font: inherit;
}
.matrow + .matrow { border-top: 1px solid var(--line); }
.matrow:hover { background: var(--surface-2); }
.matrow.is-active { background: var(--accent-soft); border-left-color: var(--accent); }
.matrow__no {
  flex: 0 0 auto; min-width: 26px; height: 22px; padding: 0 6px; box-sizing: border-box;
  display: inline-grid; place-items: center; border-radius: 6px;
  background: var(--surface-2); color: var(--text);
  font-size: 12px; font-weight: 600; font-variant-numeric: tabular-nums;
}
.matrow.is-active .matrow__no { background: var(--accent); color: var(--on-accent); }
.matrow__name { flex: 1 1 auto; min-width: 0; font-size: 12.5px; font-weight: 500; line-height: 1.3; }
.matrow__meta { flex: 0 0 auto; font-size: 11px; color: var(--text-3); white-space: nowrap; font-variant-numeric: tabular-nums; }
.matrow .chip { flex: 0 0 auto; }
.matgroup {
  padding: 10px 12px 6px; font-size: 11.5px; font-weight: 600; color: var(--text-2);
  border-top: 1px solid var(--line);
}
.matgroup:first-child { border-top: 0; }
@media (max-width: 760px) {
  .matrow__meta { font-size: 10.5px; }
}

/* Предметы в профиле — те же кнопки-метки, строкой с переносом. */
.prow .fld--wide { grid-column: 1 / -1; }
.subjpick { display: flex; flex-wrap: wrap; gap: 6px; padding: 2px 0; }

/* Зов в профиль, когда общий банк есть, а предметы не отмечены. */
.libhint {
  display: flex; flex-direction: column; align-items: flex-start; gap: 8px;
  margin-bottom: 10px; padding: 14px 16px; border-radius: var(--r-card);
  border: 1px dashed var(--accent); background: var(--accent-soft);
  font-size: 12.5px; line-height: 1.55; color: var(--text-2);
}
.libhint b { color: var(--text); font-size: 13.5px; font-weight: 600; }

/* Короткий ответ без эталона — до выдачи, янтарём: вечером это придёт на проверку. */
.fld__hint.noref { padding: 0 0 10px; color: var(--warn); }

/* Правило сверки краткого ответа — рядом с эталоном, не шире нужного. */
.task__ans .task__rule { width: auto; max-width: 250px; min-width: 0; font-size: 12px; }

/* Развилка «откуда берём задание». Две карточки в ряд, на узком экране —
   в столбец: выбор из двух не должен требовать горизонтальной прокрутки. */
.pickcards { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; }
@media (max-width: 620px) { .pickcards { grid-template-columns: 1fr; } }

.pickcard {
  display: flex; flex-direction: column; align-items: flex-start; gap: 7px;
  padding: 18px; text-align: left; cursor: pointer;
  background: var(--surface-2); border: 1px solid var(--line); border-radius: var(--r-card);
  color: inherit; font: inherit;
}
.pickcard:hover:not([disabled]) { border-color: var(--accent); background: var(--accent-soft); }
.pickcard:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.pickcard[disabled] { cursor: default; opacity: .6; }
.pickcard__ico { color: var(--accent); }
.pickcard__t { font-size: 14.5px; font-weight: 650; letter-spacing: -.01em; }
.pickcard__d { font-size: 12px; line-height: 1.45; color: var(--text-2); }

/* Выбор учеников при раздаче материала. Список с галочками, а не select
   с множественным выбором: последний на мобильных превращается в лотерею,
   а раздать набор десятерым — основной сценарий вкладки. */
.picklist {
  display: flex; flex-direction: column; max-height: 230px; overflow-y: auto;
  border: 1px solid var(--line); border-radius: var(--r-ctl); background: var(--surface-2);
}
.pickitem {
  display: flex; align-items: center; gap: 9px; padding: 8px 11px;
  font-size: 12.5px; cursor: pointer;
}
.picklist .pickitem + .pickitem { border-top: 1px solid var(--line); }
.picklist .pickitem:hover { background: var(--surface); }

/* Селектор с типом намеренно: правило .fld input задаёт полям формы ширину
   во все сто процентов, и галочка внутри диалога растягивалась в полосу,
   утаскивая имя ученика к правому краю. Одного класса не хватает — вес тот же,
   а .fld ниже по файлу и потому побеждает. */
.pickitem input[type="checkbox"] {
  width: 15px; height: 15px; flex: none;
  accent-color: var(--accent); cursor: pointer;
}
.pickitem__name { flex: 1; min-width: 0; }
.pickitem .chip { flex: none; }

.builder { background: var(--surface); border: 1px solid var(--line); border-radius: var(--r-card); padding: 18px 20px 20px; }
/* Пока ничего не выбрано, конструктор — пустая коробка во весь экран. Лучше
   его не показывать вовсе, чем показывать рамку вокруг ничего. */
.builder:empty { display: none; }
/* Перенос обязателен. Без него строка была шириной со своё содержимое —
   заголовок работы плюс до шести кнопок, — и на телефоне конструктор уезжал
   за правый край на шестьсот пикселей. Страница при этом не прокручивалась:
   вбок ехал .views, и человек просто не видел кнопок, пока не догадается
   протащить экран (владелец, 18.09.2026). */
.builder__head {
  display: flex; align-items: flex-start; flex-wrap: wrap; gap: 14px;
  padding-bottom: 16px; border-bottom: 1px solid var(--line);
}
/* Заголовку разрешено сжиматься: без min-width: 0 длинное название работы
   не даёт колонке стать уже своего текста и распирает строку изнутри. */
.builder__head > div:first-child { min-width: 0; flex: 1 1 210px; }
.builder__title { font-size: 17px; font-weight: 650; letter-spacing: -.015em; }
.builder__sub { font-size: 12.5px; color: var(--text-2); padding-top: 3px; }
.builder__acts { margin-left: auto; display: flex; flex-wrap: wrap; gap: 8px; }
.tasks { display: flex; flex-direction: column; gap: 10px; padding: 16px 0; }

.task {
  display: grid; grid-template-columns: 26px minmax(0, 1fr); gap: 12px;
  border: 1px solid var(--line); border-radius: var(--r-card); padding: 12px 13px;
  background: var(--surface-2);
}
.task__n { font: 600 12px/26px var(--mono); text-align: center; color: var(--text-3); }
.task__b { display: flex; flex-direction: column; gap: 9px; min-width: 0; }
.task__toolbar { display: flex; align-items: center; flex-wrap: wrap; gap: 8px; }
.task__text {
  width: 100%; resize: vertical; min-height: 46px;
  background: var(--surface); color: var(--text);
  border: 1px solid var(--line); border-radius: var(--r-ctl);
  padding: 8px 10px; font: inherit; line-height: 1.5;
}
.task__ans { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.task__ans label { font-size: 11.5px; color: var(--text-3); }
.task__ans input {
  width: 120px; padding: 5px 9px; background: var(--surface); color: var(--text);
  border: 1px solid var(--line); border-radius: var(--r-ctl);
  font-family: var(--mono); font-size: 12.5px;
}
.task__del { margin-left: auto; }
.toggle2 { display: inline-flex; border: 1px solid var(--line); border-radius: 999px; overflow: hidden; background: var(--surface); }
.toggle2 button { padding: 4px 11px; border: 0; background: none; cursor: pointer; font-size: 11.5px; color: var(--text-2); }
.toggle2 button.is-on { background: var(--accent-soft); color: var(--accent); font-weight: 600; }
.builder__foot {
  display: flex; align-items: center; flex-wrap: wrap; gap: 10px;
  padding-top: 14px; border-top: 1px solid var(--line);
}
.builder__sum { margin-left: auto; font-size: 12px; color: var(--text-3); }

/* ── скриншот условия ─────────────────────────────────────── */
.task__picadd { display: flex; align-items: center; gap: 9px; margin-top: 8px; }
.task__pic { position: relative; margin-top: 9px; }
.task__pic img {
  display: block; max-width: 100%; max-height: 260px;
  border: 1px solid var(--line); border-radius: var(--r-ctl); background: #fff;
}
/* Кнопка поверх картинки, а не под ней: так она не растаскивает вертикальный
   ритм задач и не спорит взглядом с полем ответа. */
.task__picx { position: absolute; top: 6px; right: 6px; background: var(--surface); }

/* ── реквизиты для сверки чеков ───────────────────────────── */
.payid__row { display: flex; align-items: center; gap: 9px; flex-wrap: wrap; margin-top: 10px; }
.payid__row input {
  flex: 1; min-width: 170px; padding: 8px 11px;
  font-family: var(--mono); font-size: 13px;
  color: var(--text); background: var(--surface);
  border: 1px solid var(--line); border-radius: var(--r-ctl);
}
.payid__row input:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; border-color: transparent; }

/* ── ссылка на работу ─────────────────────────────────────── */
.hwlink {
  display: flex; align-items: center; gap: 9px; flex-wrap: wrap;
  margin-bottom: 14px; padding: 9px 12px;
  background: var(--surface-2); border: 1px solid var(--line);
  border-left: 2px solid var(--accent); border-radius: var(--r-ctl);
}
.hwlink [data-icon] { color: var(--accent); flex: none; }
.hwlink__a {
  font-family: var(--mono); font-size: 12.5px; color: var(--text-2);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap; min-width: 0; flex: 1;
}
.hwlink__a:hover { color: var(--accent); }

/* ── ИИ-агент ─────────────────────────────────────────────── */
.agentlayout { display: grid; grid-template-columns: minmax(0, 1fr) 420px; gap: 20px; align-items: start; }
.rules { display: flex; flex-direction: column; gap: 10px; }
.rule {
  display: grid; grid-template-columns: 30px minmax(0, 1fr); gap: 13px;
  background: var(--surface); border: 1px solid var(--line); border-radius: var(--r-card);
  padding: 14px 15px;
}
.rule.is-off { opacity: .55; }
.rule__ico { display: grid; place-items: center; width: 30px; height: 30px; padding: 6px; border-radius: 999px; }
.rule__ico--accent { background: var(--accent-soft); color: var(--accent); }
.rule__ico--ok { background: var(--ok-soft); color: var(--ok); }
.rule__ico--brass { background: var(--brass-soft); color: var(--brass); }
.rule__b { display: flex; flex-direction: column; gap: 7px; min-width: 0; }
.rule__top { display: flex; align-items: center; flex-wrap: wrap; gap: 10px; }
.rule__num { font: 500 10.5px/1 var(--mono); color: var(--text-3); }
.rule__title { font-size: 13.5px; font-weight: 600; letter-spacing: -.01em; }
.rule__flow { display: flex; align-items: baseline; gap: 8px; flex-wrap: wrap; font-size: 12.5px; color: var(--text-2); }
.rule__when { color: var(--text); font-weight: 500; }
.rule__arrow { color: var(--text-3); font-family: var(--mono); }
.rule__sample {
  font-size: 12px; color: var(--text-2); line-height: 1.5;
  background: var(--surface-2); border-left: 2px solid var(--brass);
  border-radius: 0 3px 3px 0; padding: 8px 11px;
}
.rule__sample em { font-style: normal; color: var(--text-3); font-size: 11px; display: block; padding-bottom: 3px; font-family: var(--mono); }
.rule__ctrls { display: flex; align-items: center; gap: 10px; margin-left: auto; }
.rule__param {
  padding: 4px 8px; background: var(--surface); color: var(--text-2);
  border: 1px solid var(--line); border-radius: var(--r-ctl);
  font: 400 11.5px var(--mono);
}
.sw {
  position: relative; width: 34px; height: 19px; flex: 0 0 auto;
  border-radius: 999px; border: 1px solid var(--line-2); background: var(--surface-3);
  cursor: pointer; padding: 0; transition: background .15s, border-color .15s;
}
.sw::after {
  content: ""; position: absolute; top: 2px; left: 2px;
  width: 13px; height: 13px; border-radius: 999px; background: var(--surface);
  box-shadow: 0 1px 2px rgba(0,0,0,.25); transition: transform .15s;
}
.sw.is-on { background: var(--accent); border-color: var(--accent); }
.sw.is-on::after { transform: translateX(15px); }

/* Настройки сценария — рядом, а не в строке заголовка: после слияния их стало
   до трёх, и в ряд с названием они не помещаются. У каждой своя подпись: без
   неё «24 часа» и «48 часов» рядом читаются как одно и то же. */
.rule__params { display: flex; flex-wrap: wrap; gap: 8px 14px; margin: 10px 0 2px; }
.rule__pset { display: flex; align-items: center; gap: 7px; }
.rule__pset > span {
  font: 500 10.5px/1 var(--mono); letter-spacing: .07em;
  text-transform: uppercase; color: var(--text-3);
}

/* ── выбор тарифа ─────────────────────────────────────────── */

.plans { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 10px; }
.planpick {
  display: flex; flex-direction: column; gap: 3px; align-items: flex-start;
  padding: 11px 13px; cursor: pointer; text-align: left;
  border: 1px solid var(--line); border-radius: var(--r-card);
  background: var(--surface-2); color: var(--text);
  transition: border-color .12s, background .12s;
}
.planpick:hover { border-color: var(--line-2); background: var(--surface-3); }
.planpick.is-on { border-color: var(--accent); background: var(--accent-soft); }
.planpick__n { font-size: 14px; font-weight: 650; letter-spacing: -.01em; }
.planpick.is-on .planpick__n { color: var(--accent); }
.planpick__p { font: 500 12px/1 var(--mono); color: var(--text-2); }
.planpick__s { font-size: 11.5px; color: var(--text-3); }
.planpick .btn { align-self: flex-start; }
/* Действие внизу плитки — кнопки четырёх тарифов в одну линию. */
.planpick__act { margin-top: auto; padding-top: 8px; display: flex; flex-direction: column; gap: 4px; align-items: flex-start; }
.ownerpay { display: flex; flex-wrap: wrap; align-items: center; gap: 6px 8px; margin-top: 12px; }
@media (max-width: 720px) { .plans { grid-template-columns: repeat(2, minmax(0, 1fr)); } }

/* ── замок тарифа ─────────────────────────────────────────── */

/* Плашка на закрытой функции: замок и название тарифа, которым она
   открывается. Стекло здесь то же, что у всплывающих слоёв, — и это не ради
   красоты: плашка лежит поверх содержимого, и сквозь неё должно быть видно,
   что именно закрыто. Матовый прямоугольник спрятал бы это, и осталось бы
   непонятно, за что предлагают платить. */
.lock {
  display: inline-flex; align-items: center; gap: 6px; flex: 0 0 auto;
  padding: 4px 10px 4px 8px; border-radius: 999px;
  background-color: var(--surface-glass);
  border: 1px solid var(--line);
  -webkit-backdrop-filter: blur(18px) saturate(1.8);
  backdrop-filter: blur(18px) saturate(1.8);
  box-shadow: var(--glass-lit), var(--shadow-card);
  font: 600 11.5px/1 var(--mono); letter-spacing: .04em;
  color: var(--text-2); white-space: nowrap; cursor: help;
}
.lock [data-icon] { width: 13px; height: 13px; opacity: .8; }
.lock b { color: var(--accent); font-weight: 700; }

/* Закрытая карточка сценария. Гасим содержимое, но не прячем: репетитор
   должен видеть, что он получит, — иначе замок продаёт кота в мешке. */
.rule.is-locked .rule__flow,
.rule.is-locked .rule__sample { opacity: .5; }
.rule.is-locked .rule__title { color: var(--text-2); }

/* ── кнопка с тумблером ───────────────────────────────────── */

/* Две разные вещи в одной обойме: подпись открывает настройки, тумблер
   включает. Раньше это была одна кнопка, и она делала то одно, то другое
   в зависимости от состояния — предсказать, что будет по щелчку, было нельзя.
 *
 * Обойма выглядит кнопкой, чтобы не выбиваться из ряда соседей, а разделитель
 * между половинами говорит, что половин две. */
.tgl {
  display: inline-flex; align-items: stretch; flex: 0 0 auto;
  border: 1px solid var(--btn-edge); border-radius: var(--r-ctl);
  background: var(--btn-face); overflow: hidden;
  box-shadow: 0 2px 6px -2px rgba(23, 18, 38, .16);
}
.tgl__b {
  display: inline-flex; align-items: center; gap: 7px;
  padding: 7px 11px; border: 0; background: none; cursor: pointer;
  font-size: 13px; font-weight: 500; color: var(--text); white-space: nowrap;
}
.tgl__b:hover { background: var(--btn-face-hi); }
.tgl__b [data-icon] { width: 15px; height: 15px; }
.tgl .sw {
  align-self: center; margin: 0 9px 0 3px;
}
/* Разделитель рисуем на тумблере, а не отдельным элементом: лишний узел ради
   одной линии — это лишний узел. */
.tgl .sw::before {
  content: ''; position: absolute; left: -8px; top: -5px; bottom: -5px;
  border-left: 1px solid var(--line);
}
/* Включённое состояние подсвечивает всю обойму, а не только тумблер: в ряду
   кнопок серый прямоугольник с цветной точкой не читается как «работает». */
.tgl.is-on { border-color: color-mix(in srgb, var(--accent) 45%, var(--btn-edge)); }
.tgl.is-on .tgl__b { color: var(--accent); }
/* Тишина — состояние временное и заметное, у него свой цвет: он же на плашке
   напоминания наверху. */
.tgl.is-hold { border-color: color-mix(in srgb, var(--warn) 55%, var(--btn-edge)); }
.tgl.is-hold .tgl__b { color: var(--warn); }
.tgl.is-hold .sw.is-on { background: var(--warn); border-color: var(--warn); }

/* ── песочница Telegram ───────────────────────────────────── */
.tg {
  display: flex; flex-direction: column;
  background: var(--surface); border: 1px solid var(--line); border-radius: var(--r-card);
  overflow: hidden;
}
.tg__head { display: flex; align-items: center; gap: 10px; padding: 11px 14px; border-bottom: 1px solid var(--line); background: var(--surface-2); }
.tg__headtxt { display: flex; flex-direction: column; }
.tg__headtxt b { font-size: 12.5px; font-weight: 600; }
.tg__headtxt i { font-style: normal; font-size: 11.5px; color: var(--text-3); }
.tg__body {
  display: flex; flex-direction: column; gap: 9px;
  padding: 14px; height: 452px; overflow-y: auto;
  background: var(--surface-2);
}

.msg { max-width: 88%; display: flex; flex-direction: column; gap: 3px; animation: pop .2s ease-out; }
@keyframes pop { from { opacity: 0; transform: translateY(6px); } to { opacity: 1; transform: none; } }
.msg__who { font: 500 10.5px/1 var(--mono); letter-spacing: .06em; color: var(--text-3); padding-left: 2px; }
.msg__bub {
  border-radius: 10px; padding: 8px 11px; font-size: 12.5px; line-height: 1.5;
  border: 1px solid var(--line); background: var(--surface);
}
.msg__t { font: 400 10px/1 var(--mono); color: var(--text-3); align-self: flex-end; }
.msg--tutor { align-self: flex-end; align-items: flex-end; }
.msg--tutor .msg__bub { background: var(--accent-soft); border-color: transparent; border-bottom-right-radius: 3px; }
.msg--student .msg__bub { border-bottom-left-radius: 3px; }
.msg--bot { max-width: 94%; }
.msg--bot .msg__who { color: var(--brass); }
.msg--bot .msg__bub { border-left: 2px solid var(--brass); border-bottom-left-radius: 3px; background: var(--surface); }

.shot { width: 220px; border-radius: 6px; border: 1px solid var(--line); overflow: hidden; background: var(--surface); }
.shot figcaption { font: 400 10.5px/1 var(--mono); color: var(--text-3); padding: 6px 8px; border-top: 1px solid var(--line); }

.digest { display: flex; flex-direction: column; gap: 9px; }
.digest__h { font-size: 12.5px; font-weight: 650; }
.digest__block { display: flex; flex-direction: column; gap: 3px; }
.digest__block b { font: 500 10px/1 var(--mono); letter-spacing: .09em; text-transform: uppercase; color: var(--brass); }
.digest__block li { font-size: 12px; color: var(--text-2); line-height: 1.5; padding-left: 12px; position: relative; }
.digest__block li::before { content: "—"; position: absolute; left: 0; color: var(--text-3); }

.typing { display: inline-flex; gap: 3px; padding: 3px 0; }
.typing i { width: 5px; height: 5px; border-radius: 999px; background: var(--text-3); animation: dots 1.1s infinite; }
.typing i:nth-child(2) { animation-delay: .15s; }
.typing i:nth-child(3) { animation-delay: .3s; }
@keyframes dots { 0%, 60%, 100% { opacity: .25; } 30% { opacity: 1; } }

.tg__sim { padding: 12px 14px 14px; border-top: 1px solid var(--line); background: var(--surface); }
.tg__simlabel { font: 500 10.5px/1 var(--mono); letter-spacing: .1em; text-transform: uppercase; color: var(--text-3); padding-bottom: 9px; }
.tg__simrow { display: flex; flex-wrap: wrap; gap: 7px; }

/* ── тост ─────────────────────────────────────────────────── */
/* Заливка и размытие — в общем правиле всплывающих слоёв в конце файла.
   Раньше сообщение было тёмной плашкой с вывернутым цветом текста: оно
   выпадало из остального интерфейса ровно потому, что делалось отдельно. */
.toast {
  position: fixed; left: 50%; bottom: 26px; transform: translate(-50%, 14px);
  display: flex; align-items: center; gap: 9px;
  padding: 9px 15px; border-radius: var(--r-ctl);
  font-size: 12.5px; opacity: 0; pointer-events: none;
  transition: opacity .18s, transform .18s; z-index: 40;
}
.toast.is-on { opacity: 1; transform: translate(-50%, 0); }
/* Спрятанное сообщение прозрачно, но не исчезло: без этой строки его кнопка
   продолжала бы ловить клики по календарю в том месте, где сообщения уже
   не видно. */
.toast:not(.is-on) .toast__do { pointer-events: none; }

/* ── формы в модальном окне ───────────────────────────────── */
/* Нативный <dialog>: перехват фокуса, Esc и подложка достаются бесплатно. */
/* Заливка, размытие и кромка — общие для всех всплывающих слоёв, они в конце
   файла. Здесь только размеры. */
.modal {
  /* fixed, а не relative: окно в потоке документа заставляло браузер
     отматывать страницу к нему — наверх (аудит 27.09.2026: «любое окно
     отматывает страницу в самый верх»). */
  position: fixed; inset: 0; margin: auto;
  width: min(560px, calc(100vw - 32px));
  padding: 0; border-radius: var(--r-card);
}
/* Подложка окна тоже размывается: без этого стеклянное окно висит над
   абсолютно чётким фоном, и иллюзия толщины ломается. */
/* Подложка притеняет и слегка размывает. Размытие слабое — два пикселя против
   восемнадцати у самого окна: вровень с ним оно убило бы стекло, остался бы
   равномерно мутный экран. Затемнение отвечает за другое — куда смотреть. */
.modal::backdrop {
  background: rgba(12, 8, 26, .17);
  -webkit-backdrop-filter: blur(2px);
  backdrop-filter: blur(2px);
}
/* В тёмной теме затемнение сильнее светлого — так было задумано с самого
   начала: на чёрном фоне слабая вуаль не читается вовсе. Пропорция сохранена
   при том же уменьшении вдвое, что и в светлой (.62 → .31). */
:root[data-theme="dark"] .modal::backdrop { background: rgba(0, 0, 0, .31); }

.modal__head {
  display: flex; align-items: center; gap: 12px;
  padding: 16px 18px; border-bottom: 1px solid var(--line);
}
.modal__title { font-size: 15px; font-weight: 620; letter-spacing: -.01em; }
.modal__head .iconbtn { margin-left: auto; }

.modal__body { padding: 18px; max-height: min(62vh, 560px); overflow-y: auto; }

/* Подвал без своей заливки: на матовом окне светлая плашка читалась белой
   полосой поперёк низа, а не подвалом. Отделяет его линия — этого достаточно. */
.modal__foot {
  display: flex; align-items: center; gap: 9px;
  padding: 14px 18px; border-top: 1px solid var(--line); background: transparent;
  border-radius: 0 0 var(--r-card) var(--r-card);
}
.modal__foot .btn:nth-last-child(2) { margin-left: auto; }
.modal__error {
  font-size: 12px; line-height: 1.4; color: var(--crit);
  flex: 1 1 auto; min-width: 0;
}
.modal__error:empty { display: none; }

.formgrid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 14px; }
.formgrid .fld--wide { grid-column: 1 / -1; }

.fld { display: flex; flex-direction: column; gap: 6px; min-width: 0; }
/* Подпись поля — и только она. Раньше здесь стояло `.fld > span`, а подсказка
   тоже прямой потомок `.fld`: она донашивала за подписью моноширинный капс
   с разрядкой. Перебить это в `.fld__hint` не выходило — у «класс + тег» вес
   больше, чем у одного класса, и порядок в файле роли не играет. Поэтому
   исключение прописано прямо в селекторе. */
.fld > span:not(.fld__hint) {
  font: 500 10.5px/1 var(--mono);
  letter-spacing: .07em; text-transform: uppercase; color: var(--text-3);
}
.fld input, .fld select, .fld textarea {
  width: 100%; padding: 8px 10px;
  font-family: inherit; font-size: 13px; color: var(--text);
  background: var(--surface-2); border: 1px solid var(--line); border-radius: var(--r-ctl);
}
.fld textarea { resize: vertical; min-height: 66px; line-height: 1.5; }
.fld input:focus-visible, .fld select:focus-visible, .fld textarea:focus-visible {
  outline: 2px solid var(--accent); outline-offset: 1px; border-color: transparent;
}
/* Подсказка — обычный текст обычным шрифтом. Капс уместен на подписи в одно
   слово и невыносим во фразе на три строки: он и внимание тянет на себя,
   и читается по буквам, а не словами. */
.fld__hint {
  font: 400 12px/1.5 var(--ui);
  letter-spacing: normal; text-transform: none; color: var(--text-2);
}

/* ── выпадающие списки в красках панели ──
   Родной список у <select> рисует система — белое окно с синей полосой
   поверх нашей формы (замечание владельца). Новый механизм браузера
   (appearance: base-select) отдаёт список под стили страницы; браузеры
   без него игнорируют эти правила и остаются с родным видом. */
select, ::picker(select) { appearance: base-select; }
/* В новом виде браузер делает сам <select> флекс-контейнером с выравниванием
   по верху: как только список выше своей строки (min-height под палец),
   надпись уезжает к верхней кромке — так было в «Оплатах», в правилах агента
   и в конструкторе (владелец, 28.09.2026). Центруем один раз для всех;
   браузеры со старым видом это свойство на списке просто не замечают. */
select { align-items: center; }

::picker(select) {
  /* Стекло, как у окон и карточек: подложка полупрозрачная, а то, что под
     ней, размыто — подписи полей сквозь список не читаются. Прежняя запись
     через color-mix на планшете владельца отбрасывалась целиком, и список
     оставался прозрачным без размытия (12.09.2026). */
  background: var(--surface);
  -webkit-backdrop-filter: blur(var(--glass-blur)) saturate(1.4);
  backdrop-filter: blur(var(--glass-blur)) saturate(1.4);
  border: 1px solid var(--line); border-radius: var(--r-ctl);
  box-shadow: var(--shadow-pop);
  padding: 5px; margin-top: 4px;
}
select::picker-icon { color: var(--text-3); }
select option {
  padding: 7px 10px; border-radius: var(--r-sm);
  font-size: 13px; color: var(--text-2); cursor: pointer;
}
select option:hover, select option:focus-visible { background: var(--surface-2); color: var(--text); }
select option:checked { background: var(--accent-soft); color: var(--accent); font-weight: 600; }
/* Галочка у выбранного не нужна: его и так красит акцент. */
select option::checkmark { display: none; }

.iconbtn--quiet { border-color: transparent; background: transparent; color: var(--text-3); }
.iconbtn--quiet:hover { color: var(--text); background: var(--surface-3); }

/* Приписка под заголовком песочницы: она обещает меньше, чем кажется на вид. */
.sandbox__note {
  margin: 0 0 10px; padding: 9px 11px;
  font-size: 11.5px; line-height: 1.5; color: var(--text-3);
  background: var(--surface-2); border: 1px solid var(--line);
  border-left: 2px solid var(--line-2); border-radius: var(--r-ctl);
}

/* Сданный ответ внутри карточки задачи — чтобы проверять, не уходя в чат. */
.task__sub {
  margin-top: 10px; padding: 9px 11px;
  background: var(--surface-2); border: 1px solid var(--line);
  border-left: 2px solid var(--accent); border-radius: var(--r-ctl);
}
.task__sublabel {
  font: 500 10px/1 var(--mono);
  letter-spacing: .07em; text-transform: uppercase; color: var(--text-3);
  margin-bottom: 6px;
}
.task__subtext { font-size: 12.5px; line-height: 1.5; color: var(--text-2); }
.task__review { display: flex; align-items: center; gap: 8px; margin-top: 9px; }
.task__review label { font-size: 11.5px; color: var(--text-3); }
.task__review input {
  padding: 5px 8px; font-family: var(--mono); font-size: 12.5px;
  color: var(--text); background: var(--surface);
  border: 1px solid var(--line); border-radius: var(--r-ctl);
}

/* Полоса остатка. Число рядом с полосой обязательно: полоса показывает
   пропорцию, а решение принимают по остатку — «половина» ничего не говорит,
   пока не известно, половина от чего. */
.meter { margin-top: 14px; }
.meter__head {
  display: flex; align-items: baseline; justify-content: space-between;
  gap: 10px; font-size: 12.5px; color: var(--text-2);
}
.meter__head b { font-family: var(--mono); font-size: 12px; color: var(--text); }
.meter__bar {
  margin-top: 6px; height: 6px; border-radius: 3px;
  background: var(--surface-3); overflow: hidden;
}
.meter__bar i { display: block; height: 100%; background: var(--accent); }

/* Листы ученика — полосой над задачами: они относятся ко всей работе. */
.sheetbar {
  margin: 12px 0; padding: 10px 12px;
  background: var(--surface-2); border: 1px solid var(--line);
  border-radius: var(--r-ctl);
}
.sheetbar__head {
  display: flex; flex-wrap: wrap; align-items: baseline; gap: 8px;
  font: 500 10px/1.4 var(--mono); letter-spacing: .06em;
  text-transform: uppercase; color: var(--text-3);
}
.sheetbar__note { margin-left: auto; text-transform: none; letter-spacing: 0; font-family: inherit; font-size: 11.5px; }
/* Кнопка всегда справа: когда подписи про разбор нет, `note` не занимает
   место, и без своего отступа кнопка прилипла бы к заголовку. */
.sheetbar__get { margin-left: auto; text-transform: none; letter-spacing: 0; font-family: inherit; }
.sheetbar__note + .sheetbar__get { margin-left: 0; }
.sheetbar .shots { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 8px; }
.sheetbar .shot__img {
  display: block; width: 96px; height: 96px; object-fit: cover;
  border: 1px solid var(--line); border-radius: var(--r-ctl);
  background: #fff; cursor: zoom-in;
}
.sheetbar .shot__img.is-zoom {
  width: auto; height: auto; max-width: 100%; object-fit: contain; cursor: zoom-out;
}

/* Снимки решения в панели. Те же классы, что и на странице ученика, но здесь
   они мельче: репетитору сначала нужен вывод, а лист — только если вывод
   спорный. Дублировать имена ради этого не стали — стили живут в двух
   таблицах, потому что таблицы делят страницы, а не имена. */
.task__sub .shots { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 8px; }
.task__sub .shot { display: inline-flex; }
.task__sub .shot__img {
  display: block; width: 64px; height: 64px; object-fit: cover;
  border: 1px solid var(--line); border-radius: var(--r-ctl);
  background: #fff; cursor: zoom-in;
}
.task__sub .shot__img.is-zoom {
  width: auto; height: auto; max-width: 100%; object-fit: contain; cursor: zoom-out;
}

/* Черновик разбора. */
.chk { margin-top: 9px; font-size: 12.5px; line-height: 1.5; }
.chk__head { display: flex; flex-wrap: wrap; align-items: center; gap: 7px; }
.chk__pts { font: 500 11px/1 var(--mono); color: var(--text-2); }
.chk__cost { font-size: 11px; color: var(--text-3); margin-left: auto; }
.chk__note { margin: 7px 0 0; color: var(--text-2); }
.chk__why { margin: 6px 0 0; color: var(--text); }
.chk__step { margin: 5px 0 0; color: var(--text-2); overflow-wrap: anywhere; }
.chk__step code, .chk__lines code { font-family: var(--mono); font-size: 11.5px; }
.chk__list { margin: 6px 0 0; padding-left: 17px; color: var(--text-2); }
.chk__more { margin-top: 8px; }
.chk__more summary { cursor: pointer; font-size: 11.5px; color: var(--text-3); }
.chk__lines {
  margin: 7px 0 0; padding-left: 20px; color: var(--text-2);
  /* Строка решения в LaTeX бывает длиннее колонки, и переносить её по словам
     нечем — пробелов в ней может не быть вовсе. */
  overflow-wrap: anywhere;
}
.chk__lines li { margin-bottom: 3px; }

/* Пустой экран: без данных интерфейс должен объяснять, что делать дальше. */
.empty {
  display: flex; flex-direction: column; align-items: flex-start; gap: 10px;
  padding: 22px; border: 1px dashed var(--line-2); border-radius: var(--r-card);
  background: var(--surface); color: var(--text-3); font-size: 12.5px; line-height: 1.6;
}
.empty b { display: block; color: var(--text); font-size: 13.5px; font-weight: 600; }

/* ── адаптив ──────────────────────────────────────────────── */
@media (max-width: 1240px) {
  /* Плиток три, и на две колонки их не делят: получилось бы 2 + 1, то есть
     ряд с дырой. Три узких читаются лучше, чем две широких и одна брошенная. */
  .split { grid-template-columns: minmax(0, 1fr); }
  .agentlayout { grid-template-columns: minmax(0, 1fr); }
  .panels { grid-template-columns: minmax(0, 1fr); }
}
@media (max-width: 980px) {
  .app { grid-template-columns: 1fr; height: auto; }
  /* На «Расписании» страница не листается: см. правило body.is-sched выше. */
  body.is-sched .app { height: 100vh; height: 100dvh; }
  body.is-sched .canvas { min-height: 0; }
  /* Рельс на узком экране — выдвижное меню за бургером; правила ящика
     собраны отдельным разделом внизу файла. Прежде он ложился строкой
     с горизонтальной прокруткой: половина пунктов пряталась за краем,
     и о том, что их надо тянуть пальцем, ничто не говорило. */
  .roster, .hwlayout { grid-template-columns: minmax(0, 1fr); }
  /* В одну колонку липкий список закрыл бы собой карточку. */
  .roster__list { position: static; max-height: none; }
  .kpis { grid-template-columns: minmax(0, 1fr); }
  .lesson { grid-template-columns: 66px minmax(0, 1fr); }
  /* Кнопки занятия переносятся строкой. Без переноса ряд из четырёх —
     «Доска», «Отметить проведённым», «Звонок», «Создать ДЗ» — вырастал до
     462 px при ширине карточки 281 и уезжал за край экрана: страница при
     этом вбок не прокручивалась, так что до «Создать ДЗ» было не добраться
     вовсе (владелец, 18.09.2026: «плашки находятся правее, чем должны»). */
  .lesson__acts { grid-column: 1 / -1; flex-wrap: wrap; }
  .formgrid { grid-template-columns: minmax(0, 1fr); }
}

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after { animation: none !important; transition: none !important; }
}

/* ── оплаты в карточке ученика ─────────────────────────────────────────── */

/* Приём оплат по ссылке: своя карточка над сводкой месяца. Ссылка и
   поступления по ней — здесь же, а не в профиле: это деньги за занятия,
   и смотрят их вместе с остальными оплатами. */
.paylink {
  background: var(--surface); border: 1px solid var(--line); border-radius: var(--r-card);
  padding: 16px 18px 18px; margin-bottom: 14px;
}
.paylink__head { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; margin-bottom: 8px; }
.paylink__t { font-size: 15px; font-weight: 620; letter-spacing: -.01em; margin: 0; }
.paylink__head .btn { margin-left: auto; }
.paylink__s { font-size: 12.5px; line-height: 1.5; color: var(--text-2); margin: 8px 0 0; }
.paylink__link { display: flex; gap: 8px; align-items: center; margin-top: 10px; }
.paylink__link input {
  flex: 1 1 auto; min-width: 0; border: 1px solid var(--line); border-radius: var(--r-ctl);
  background: var(--surface-2); color: var(--text); padding: 9px 12px;
  font: 500 13px/1.3 var(--mono);
}
.paylink .paylist { margin-top: 12px; }
.plrow {
  display: flex; align-items: center; gap: 10px; flex-wrap: wrap;
  padding: 9px 0; border-top: 1px solid var(--line);
}
.plrow__sum { font-weight: 600; min-width: 76px; }
.plrow__at { font-size: 12.5px; color: var(--text-3); min-width: 92px; }
.plrow__who { flex: 1 1 160px; min-width: 0; font-size: 12.5px; color: var(--text-2); overflow-wrap: anywhere; }
.plrow--wait { background: var(--warn-soft); border-radius: var(--r-sm); padding-left: 8px; padding-right: 8px; }
.plrow__pick { max-width: 220px; }
@media (max-width: 560px) {
  .plrow__pick { max-width: none; flex: 1 1 100%; }
}

/* ── экран оплат ──────────────────────────────────────────────
 *
 * Строка занятия читается слева направо как фраза: когда, во сколько, о чём,
 * оплачено ли, за сколько. Состояние — словом, а не только цветом: у цвета
 * нет подписи, а «зелёное» и «красное» человек трактует по-разному, особенно
 * когда речь про его деньги. */
.paycard {
  background: var(--surface); border: 1px solid var(--line);
  border-radius: var(--r-card); padding: 14px 16px 16px; margin-bottom: 14px;
}
.paycard__head { display: flex; align-items: center; flex-wrap: wrap; gap: 10px; margin-bottom: 12px; }
.paycard__name { font-size: 15px; font-weight: 650; letter-spacing: -.01em; }
.paycard__head .btn { margin-left: auto; }

.payrow {
  display: grid; grid-template-columns: 148px 52px minmax(0, 1fr) auto auto;
  align-items: baseline; gap: 12px;
  padding: 8px 10px; border-radius: 8px; font-size: 12.5px;
}
.payrow + .payrow { margin-top: 2px; }
.payrow__d { color: var(--text); }
.payrow__t { color: var(--text-3); font-size: 11.5px; }
.payrow__x { color: var(--text-2); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.payrow__s { font: 500 10px/1 var(--mono); letter-spacing: .08em; text-transform: uppercase; }
.payrow__m { font-weight: 600; font-variant-numeric: tabular-nums; }
/* Поле цены в строке оплат: обычная строка, пока в неё не метят. */
.paycell { display: inline-flex; align-items: center; gap: 3px; }
.paycell input {
  width: 78px; padding: 3px 6px; text-align: right;
  font: 600 12.5px/1.2 var(--mono); font-variant-numeric: tabular-nums;
  color: var(--text); background: transparent;
  border: 1px solid transparent; border-radius: 7px;
}
.paycell input:hover { border-color: var(--line); }
.paycell input:focus-visible { border-color: var(--accent); background: var(--surface); outline: none; }
.paycell input:disabled { opacity: .45; }
.paycell__r { color: var(--text-3); }
@media (pointer: coarse) { .paycell input { padding: 7px 8px; } }

.payrow--missed { background: var(--warn-soft); }
.payrow--missed .payrow__s { color: var(--warn); }
.payrow__mark {
  border: 0; background: none; padding: 0; cursor: pointer;
  font: 500 10px/1 var(--mono); letter-spacing: .08em; text-transform: uppercase;
  text-decoration: underline; text-underline-offset: 3px;
}
.payrow__mark:disabled { opacity: .5; cursor: default; }

.payrow--paid   { background: var(--ok-soft); }
.payrow--paid   .payrow__s { color: var(--ok); }
/* Неоплаченное — единственная строка, которая просит действия, и цепляет
   глаз полосой слева: заливки на длинном списке недостаточно. */
.payrow--unpaid { background: var(--crit-soft); box-shadow: inset 3px 0 0 var(--crit); }
.payrow--unpaid .payrow__s { color: var(--crit); }
.payrow--unpaid .payrow__m { color: var(--crit); }
.payrow--ahead  { background: var(--surface-2); }
.payrow--ahead  .payrow__s,
.payrow--ahead  .payrow__m { color: var(--text-3); }

.statline .is-debt { color: var(--crit); }

#moneyNav select {
  font: inherit; font-size: 12.5px; color: var(--text); padding: 7px 10px;
  background: var(--surface); border: 1px solid var(--line-2); border-radius: var(--r-ctl);
}
.segmented__t { padding: 0 10px; font-size: 12.5px; color: var(--text); white-space: nowrap; }

@media (max-width: 900px) {
  .payrow { grid-template-columns: minmax(0, 1fr) auto; gap: 4px 10px; }
  .payrow__t, .payrow__x { grid-column: 1; }
  .payrow__s { grid-column: 1; }
  .payrow__m { grid-row: 1; grid-column: 2; }
}

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

.payline {
  display: grid;
  grid-template-columns: auto auto 1fr auto;
  align-items: baseline;
  gap: 10px;
  padding: 7px 8px;
  border-radius: 8px;
}
.payline:hover { background: var(--surface-2); }

.payline__sum  { font-weight: 600; color: var(--ok); }
.payline__at   { color: var(--text-2); font-size: 13px; }
.payline__note {
  color: var(--text-2);
  font-size: 13px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* Кнопка удаления проявляется по наведению на строку: она нужна редко,
   а висеть перед глазами в списке из шести записей ей незачем. */
.payline .iconbtn { opacity: 0; transition: opacity .12s ease; }
.payline:hover .iconbtn,
.payline .iconbtn:focus-visible { opacity: 1; }
/* На сенсорных экранах наведения нет: кнопка видна всегда, но негромко —
   невидимая кликабельная область собирала случайные попадания. */
@media (hover: none) { .payline .iconbtn { opacity: .55; } }

/* Подсказка про управление календарём: уезжает вправо, чтобы не спорить
   с легендой, и гаснет на узком экране, где мыши всё равно нет. */
.legend__hint { margin-left: auto; color: var(--text-3); }
@media (max-width: 980px) { .legend__hint { display: none; } }

/* ═══════════════ Окна записи в календаре ═══════════════ */

/* Подложка, а не событие: окно говорит «сюда можно», и перехватывать указатель
   ему незачем — клик по нему должен ставить занятие ровно как на пустом месте.
   Живой остаётся одна кнопка. */
/* z-index не задан намеренно. С ним подложка окна создаёт свой контекст
   наложения, и ручка внутри неё не может подняться выше плашек занятий —
   а занятие, начинающееся в тот же час, что и окно, накрывает ручку целиком.
   Без z-index порядок решает разметка: окна рисуются раньше занятий и лежат
   под ними, а ручка всплывает наверх сама. */
.slot {
  position: absolute; left: 0; right: 0;
  cursor: grab; touch-action: none;
  border: 1px dashed color-mix(in srgb, var(--ok) 55%, transparent);
  border-radius: var(--r-sm);
  background: color-mix(in srgb, var(--ok-soft) 55%, transparent);
  transition: background .12s;
}
.slot:hover { background: color-mix(in srgb, var(--ok-soft) 85%, transparent); }
.slot--full {
  border-color: var(--line);
  background: color-mix(in srgb, var(--surface-3) 45%, transparent);
}
/* Ручка окна: за неё тащат и по ней открывают правку. Единственная часть
   подложки, которая ловит указатель, — всё остальное остаётся прозрачным,
   чтобы клик внутри окна по-прежнему ставил занятие. */
/* Подпись окна — только подпись. Указатель она не ловит: тащат и открывают
   само окно, за любое свободное место, и лишняя цель внутри него только
   создавала бы разницу между «попал в буквы» и «попал рядом». */
.slot__grip {
  position: absolute; top: 0; right: 0; height: 19px;
  display: flex; align-items: center; gap: 7px;
  padding: 0 7px; margin: 0; pointer-events: none;
  font: 500 9.5px/1 var(--mono); letter-spacing: .02em;
  color: var(--ok); text-align: left; white-space: nowrap;
  background: color-mix(in srgb, var(--ok) 15%, transparent);
  border: 0; border-radius: 0 var(--r-sm) 0 var(--r-sm);
}
.slot__seats { margin-left: auto; opacity: .75; }

.slot--full .slot__grip { color: var(--text-3); background: color-mix(in srgb, var(--line-2) 30%, transparent); }

/* На время перетаскивания окно поднимается над теми занятиями, которые с ним
   не едут: они к нему не относятся и закрывать его не должны. А записанные
   в окно едут вместе с ним и остаются сверху — как и всегда лежат. */
.slot.is-dragging { z-index: 4; opacity: .92; }
.ev.is-riding { z-index: 5; }
.slot.is-dragging .slot__grip { cursor: grabbing; }

/* ═══════════════ Профиль ═══════════════ */

/* Карточки профиля раскладываются в ряд и занимают ширину целиком. Одна
   растянутая колонка на широком мониторе выглядела бы как поле для ввода
   длиной в метр — здесь короткие поля, и им нужна не длина, а соседство. */
/* Две колонки с назначенными местами вместо auto-fit: авто-сетка ставила
   карточки разной высоты в общие ряды, и под каждой короткой зияла дыра
   в рост соседки. Колонки-стопки пакуют карточки встык, а деление по
   смыслу: слева аккаунт (кто вы, вход), справа связи наружу (Telegram,
   календарь). Тариф — во всю ширину: четырём плиткам в полколонки тесно. */
/* Профиль — страница настроек: слева оглавление, справа один столбец
   одинаковых по ширине карточек в заданном порядке. Две колонки с
   разновысокими карточками читались как хаос (владелец, 09.09.2026). */
.profile {
  display: grid; gap: 14px 28px; align-items: start;
  grid-template-columns: 188px minmax(0, 880px);
}
.profile__nav { position: sticky; top: 4px; display: flex; flex-direction: column; gap: 2px; }
.profile__nav a {
  padding: 7px 10px; border-radius: var(--r-ctl); color: var(--text-2);
  text-decoration: none; font-size: 13px; line-height: 1.3;
}
.profile__nav a:hover { background: var(--surface-3); color: var(--text); }
.profile__nav a:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
/* minmax(0, 1fr), а не 1fr: у неявной колонки минимум — min-content, и
   таблица отчёта с `white-space: nowrap` растягивала весь столбец карточек
   шире экрана. На телефоне из-за этого профиль ехал вбок, тарифы Pro и
   Expert обрезались, а кнопка «Версия для печати» лежала целиком за краем.
   Свой `overflow-x: auto` у .repwrap до этой правки не работал: сжиматься
   было некуда. */
.profile__stack { display: grid; grid-template-columns: minmax(0, 1fr); gap: 14px; min-width: 0; }
.profile__stack > .pcard { scroll-margin-top: 12px; }
@media (max-width: 900px) {
  .profile { grid-template-columns: minmax(0, 1fr); }
  .profile__nav { position: static; flex-direction: row; flex-wrap: wrap; }
}
.hwscope { display: flex; align-items: center; gap: 8px; margin: 0 0 10px; }
.hwscope select { flex: 1 1 auto; min-width: 0; }

.pcard {
  background: var(--surface); border: 1px solid var(--line);
  border-radius: var(--r-card); padding: 18px 20px 16px;
}
/* Заголовок над пояснением, а не рядом: в строку пояснение упиралось
   в правый край и заворачивало сам заголовок («Кто / вы» двумя строками). */
.pcard__head { display: flex; flex-direction: column; gap: 3px; margin-bottom: 14px; }
.pcard__head h2 { font-size: 14px; font-weight: 620; letter-spacing: -.01em; }
.pcard__head span { font-size: 12px; line-height: 1.45; color: var(--text-3); }

/* Подраздел внутри карточки — теми же чернилами, что подписи полей:
   у «Входа» две независимые формы, и без своих заголовков вторая пара
   «текущий пароль» читалась продолжением первой. */
.pcard__sub {
  font-size: 11px; text-transform: uppercase;
  color: var(--text-2); margin-bottom: 10px;
}
.pcard__sub--sep { margin-top: 16px; padding-top: 16px; border-top: 1px solid var(--line); }

.prow { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; }

.pacts { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; margin-top: 12px; }

.access__code {
  display: flex; align-items: center; gap: 8px; margin: 10px 0 0;
  font: 15px/1.2 var(--mono); letter-spacing: .12em; color: var(--text);
}

.linkrow { display: flex; flex-wrap: wrap; align-items: center; gap: 9px; margin-top: 11px; }

@media (max-width: 720px) {
  .prow { grid-template-columns: 1fr; }
}

/* ═══════════════ Меню по правой кнопке ═══════════════ */

/* Заливка и размытие — в общем правиле всплывающих слоёв. Раньше меню стояло
   на непрозрачном фоне, и его собственное размытие не делало ничего вовсе:
   размывать под сплошной заливкой нечего. */
.ctxmenu {
  position: fixed; z-index: 60; min-width: 196px; padding: 5px;
  border-radius: var(--r-ctl);
  animation: pop .12s ease-out;
}
.ctxmenu[hidden] { display: none; }
.ctxmenu button {
  display: flex; align-items: center; gap: 10px;
  width: 100%; padding: 8px 10px; text-align: left;
  background: none; border: 0; border-radius: var(--r-sm);
  color: var(--text-2); font-size: 13px; cursor: pointer;
}
.ctxmenu button:hover { background: var(--accent-soft); color: var(--accent); }
.ctxmenu button [data-icon] { width: 15px; height: 15px; opacity: .85; }

/* ── стекло: перепад заливки и блик ───────────────────────── */

/* Блок стоит в конце файла намеренно. Компоненты выше задают фон сокращённой
   записью `background`, а она сбрасывает background-image — перепад заливки,
   поставленный раньше, был бы молча стёрт. Разнеси мы это по компонентам,
   помнить об этом пришлось бы в каждом.
 *
 * Кромку здесь не трогаем: у части поверхностей она несёт состояние (активный
 * сценарий, выбранная строка), и общая кромка стекла закрасила бы его. */
/* Окна в списке нет: перепад заливки складывается с собственным фоном
   поверхности, а у прозрачной панели складывать нечего — любая добавка
   к шестнадцати процентам снова превращает стекло в пластик. */
.rail, .topbar, .kpi, .card, .roster__list, .roster__card,
.hwlist, .rule, .tg, .pcard {
  background-image: var(--glass-tint);
  box-shadow: var(--glass-lit), var(--shadow-card);
}

/* ── стекло: всплывающие слои ─────────────────────────────── */

/* Окно, меню по правой кнопке, всплывающее сообщение и подсказка диаграммы —
   один и тот же предмет: слой, поднятый над страницей. Значит и выглядеть
   он обязан одинаково; собранные порознь, они разошлись — у меню под размытием
   стояла непрозрачная заливка (размывать нечего), сообщение было тёмной
   плашкой с вывернутым текстом, подсказка — просто белым прямоугольником.
 *
 * Рецепт тот, что был у окна и всем понравился: полупрозрачная заливка,
 * сильное размытие того, что под ней, светлая кромка сверху. Размытие
 * восемнадцать, а не тридцать, как у карточек: под слоем не свечение,
 * а календарь и списки, и чужой текст из-под него читаться не должен.
 *
 * Перепада заливки здесь нет намеренно: он складывается с собственным фоном
 * поверхности, а у прозрачного слоя складывать не с чем — любая добавка снова
 * превращает стекло в пластик. Проверено дважды, оба раза одинаково. */
.modal, .ctxmenu, .toast, .vpick__menu, .gearpop, .fab__item, .spop, .helper {
  background-color: var(--surface-glass);
  background-image: none;
  color: var(--text);
  border: 1px solid var(--line);
  -webkit-backdrop-filter: blur(18px) saturate(1.8);
  backdrop-filter: blur(18px) saturate(1.8);
  box-shadow: var(--glass-lit), var(--shadow-pop);
}

/* Косого блика здесь нет намеренно: на панели интерфейса он читается белой
   полосой поперёк содержимого, а не бликом. Полосе света нужен фон, по которому
   она скользит, а у нас под ней подписи, поля и кнопки. */

/* ── проба: выпуклое стекло на управлении ─────────────────── */

/* Не матовость, а преломление: фон за кнопкой не размывается, а искривляется
   у кромки — как за толстой линзой. Как устроена карта смещения и почему
   размытие с ней не сочетается, написано в `glass.js`.
 *
 * Всплывающих окон здесь нет и не будет: у них своё матовое стекло, оно
 * владельцу нравится, и трогать его не нужно.
 *
 * Заливка кнопки под линзой заметно жиже обычной. Иначе преломлять нечего:
 * сквозь восемьдесят два процента белого не видно ни фона, ни искажения.
 * Отдельная переменная, а не правка основной, — чтобы отказаться от пробы
 * можно было снятием одного блока. */
@supports (backdrop-filter: url(#glass-sm)) {
  :root.has-lens { --btn-lens: rgba(252, 252, 255, .56); }
  :root.has-lens[data-theme="dark"] { --btn-lens: rgba(52, 47, 76, .52); }

  /* Главная кнопка остаётся сплошной. Она самая громкая на экране, и делать
     её просвечивающей значит отнимать у неё этот голос. */
  .has-lens .btn:not(.btn--primary):not(.btn--warn),
  .has-lens .iconbtn,
  .has-lens .segmented,
  .has-lens .tgl {
    -webkit-backdrop-filter: url(#glass-sm);
    backdrop-filter: url(#glass-sm);
    background-color: var(--btn-lens);
  }
  /* Наведение перебиваем отдельно: селектор с `.has-lens` весит больше
     обычного `.btn:hover`, и без этой строки кнопка перестала бы отзываться. */
  .has-lens .btn:not(.btn--primary):not(.btn--warn):hover {
    background-color: var(--btn-face-hi);
  }
  .has-lens .segmented button { background: transparent; }
  /* Половинки обоймы своей заливки не имеют — она у самой обоймы, иначе
     сквозь них не было бы видно ни фона, ни искажения. */
  .has-lens .tgl__b { background: transparent; }
  .has-lens .tgl__b:hover { background: var(--btn-face-hi); }

  /* Сетка полей в карточке ученика — панель из скриншота владельца. Линза
     на всю сетку, а ячейкам оставлена только половина их заливки: сквозь
     сплошную ничего бы не проступило. */
  .has-lens .metagrid {
    -webkit-backdrop-filter: url(#glass-md);
    backdrop-filter: url(#glass-md);
  }
  .has-lens .meta { background: color-mix(in srgb, var(--surface) 52%, transparent); }
}

/* Кнопка «передумал» внутри всплывающего сообщения. */
/* Кнопка «передумал» внутри всплывающего сообщения.
 *
 * Само сообщение сквозное для курсора (pointer-events: none выше) — и таким
 * должно остаться: оно всплывает над календарём, и ловить им клики по времени,
 * куда репетитор метил, нельзя. Поэтому нажатия принимает только кнопка,
 * а не вся плашка.
 *
 * Заливка акцентная, а не нейтральная: на тёмном сообщении светло-серый
 * прямоугольник читается частью фона, а не кнопкой. */
.toast__do {
  pointer-events: auto;
  margin-left: 4px; padding: 5px 12px; border-radius: var(--r-sm); cursor: pointer;
  border: 1px solid var(--accent); background: var(--accent); color: var(--on-accent);
  font-size: 12.5px; font-weight: 600; white-space: nowrap;
}
.toast__do:hover { background: var(--accent-2); border-color: var(--accent-2); }
.toast__do:active { transform: translateY(1px); }

/* Напоминание о включённой тишине. Жёлтое и на всю ширину над рабочей областью:
   оно и должно мешать — ровно затем, чтобы про выключенные уведомления нельзя
   было забыть. */
.holdbar {
  display: flex; align-items: center; gap: 12px;
  padding: 9px 24px; border-bottom: 1px solid var(--line);
  background: var(--warn-soft); color: var(--warn);
  font-size: 12.5px;
}
.holdbar [data-icon] { width: 16px; height: 16px; flex: 0 0 auto; }
.holdbar__t { flex: 1; min-width: 0; }
.holdbar__t b { font-family: var(--mono); }

/* Подписи капсом — одни чернила на все.
 *
 * Подняты на ступень: капс 10,5 px моноширинным на стекле растворяется. Буквы
 * редкие, штрих тонкий, а фон под ними больше не ровный — самые бледные чернила
 * годятся для текста на плотной поверхности и не годятся здесь. Собраны в одно
 * место, чтобы вес подписей менялся разом, а не в восьми правилах по очереди. */
.rail__label, .sectionhead h2, .kpi__label, .meta__k, .panel__h,
.tg__simlabel, .task__sublabel, .fld > span:not(.fld__hint) {
  color: var(--text-2); font-weight: 600;
}


/* ── Типографика заголовков и подписей ────────────────────────────────
   Единая для обеих тем, поэтому живёт здесь, а не в слое ночной.

   Заголовки — списком, а не правилом на h1–h3: голые h2 попадаются
   внутри карточек, где дисплейный шрифт не к месту. */
.pagehead__title,
.sectionhead h2,
.modal__title,
.rail__name,
.sc-head__name,
.builder__title,
.pcard__head h2,
.paycard__name,
.pickcard__t,
.sheet__t {
  font-family: var(--display);
  font-weight: 600;
}
/* На 25 пикселях шестисотый Onest выглядит вяло рядом с плотным
   текстом Golos — крупному заголовку вес побольше. */
.pagehead__title { font-weight: 700; letter-spacing: -.025em; }

/* Мелкие подписи верхним регистром шли моноширинным — это приём
   старого оформления, а не потребность в табличных цифрах: цифр в них
   нет. Список выведен по признаку: --mono вместе с text-transform:
   uppercase. Числовые места моноширинный сохраняют.

   Разрядка убавлена против старой (.1–.12em): у моноширинного буквы
   и так стоят широко, и разрядка компенсировала шрифт, а не регистр.
   Пропорциональному столько воздуха рассыпает слово. */
.rail__label,
.sectionhead h2,
.kpi__label,
.live-tag,
.stat__k,
.conn__k,
.panel__h,
.pcard__sub,
.notesaved,
.rule__pset > span,
.digest__block b,
.tg__simlabel,
.fld > span:not(.fld__hint),
.task__sublabel,
.sheetbar__head,
.payrow__s {
  font-family: var(--display);
  font-weight: 600;
  letter-spacing: .08em;
}

/* Подписи в 10–10.5 пикселей были на пределе чтения; состав строки
   у них короткий, и лишняя ширина никуда не упирается. */
.kpi__label, .stat__k, .conn__k, .panel__h, .payrow__s { font-size: 11px; }


/* ═══════════════ Выдвижное меню на узком экране ═══════════════

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

.burger {
  display: none;
  place-items: center; width: 34px; height: 34px; flex: 0 0 auto;
  background: none; border: 1px solid transparent; border-radius: var(--r-ctl);
  color: var(--text-2); cursor: pointer;
}
.burger:hover { color: var(--text); border-color: var(--line-2); }
.burger svg { width: 20px; height: 20px; }

/* Затемнение под ящиком. Прячется через [hidden], а гаснет через класс:
   двух состояний мало — нужно дать погаснуть до того, как слой исчезнет. */
.scrim {
  position: fixed; inset: 0; z-index: 40;
  background: color-mix(in srgb, #05060C 58%, transparent);
  opacity: 0; transition: opacity .18s;
}
.scrim.is-on { opacity: 1; }
.scrim[hidden] { display: none; }

@media (max-width: 980px) {
  .burger { display: grid; }

  /* Шапка липнет к верху: бургер — единственный вход в меню, и уезжать
     вместе со страницей ему нельзя. */
  .topbar {
    position: sticky; top: 0; z-index: 30;
    padding: 0 14px; gap: 10px;
  }
  .topbar__search { width: auto; flex: 1 1 auto; min-width: 0; }
  /* Кнопка помощника на телефоне — значком без подписи; её правила лежат
     в разделе помощника в конце файла, рядом с самой панелью. */

  .rail {
    position: fixed; top: 0; left: 0; z-index: 50;
    width: min(286px, 84vw); height: 100dvh;
    padding-top: max(14px, env(safe-area-inset-top));
    padding-bottom: max(12px, env(safe-area-inset-bottom));
    border-right: 1px solid var(--line);
    transform: translateX(-101%);
    transition: transform .22s ease;
    will-change: transform;
  }
  .rail.is-open { transform: none; box-shadow: 0 24px 60px rgba(0, 0, 0, .42); }

  /* Палец не мышь: пункты и карточка аккаунта крупнее сорока пикселей. */
  .navitem { padding: 11px 12px; }
  .tenant__main { padding: 10px; }
}

@media (prefers-reduced-motion: reduce) {
  .rail, .scrim { transition: none; }
}

/* ═══════════════ Выбор вида расписания ═══════════════

   Кнопка стоит вплотную к заголовку, а не среди кнопок справа: она говорит,
   ЧТО показано, а не что можно сделать. Так же это устроено в календарях,
   с которых снят образец. */

.pagehead__ttl { display: flex; align-items: flex-end; gap: 14px; flex-wrap: wrap; min-width: 0; }

.vpick { position: relative; flex: 0 0 auto; }
.vpick__b {
  display: flex; align-items: center; gap: 6px;
  padding: 7px 9px 7px 12px;
  font-size: 13px; font-weight: 600; color: var(--text);
  background: var(--surface); border: 1px solid var(--line-2);
  border-radius: var(--r-ctl); cursor: pointer;
}
.vpick__b:hover { border-color: var(--accent); }
.vpick__car { display: grid; transition: transform .16s; }
.vpick__car svg { width: 15px; height: 15px; color: var(--text-3); }
.vpick.is-open .vpick__car { transform: rotate(180deg); }

.vpick__menu {
  position: absolute; top: calc(100% + 6px); left: 0; z-index: 35;
  min-width: 196px; padding: 5px;
  border-radius: var(--r-ctl);
  animation: pop .12s ease-out;
}
.vpick__menu[hidden] { display: none; }
.vitem {
  display: grid; grid-template-columns: 20px minmax(0, 1fr); align-items: center; gap: 11px;
  width: 100%; padding: 9px 10px; text-align: left;
  background: none; border: 0; border-radius: var(--r-sm);
  color: var(--text-2); font-size: 13px; cursor: pointer;
}
.vitem svg { width: 17px; height: 17px; }
.vitem:hover { background: var(--accent-soft); color: var(--accent); }
.vitem.is-on { background: var(--accent-soft); color: var(--accent); font-weight: 600; }

@media (prefers-reduced-motion: reduce) {
  .vpick__car { transition: none; }
  .vpick__menu { animation: none; }
}

/* ═══════════════ Расписание списком ═══════════════

   Вид для телефона: сетка часов на узком экране отдаёт занятию полоску
   в три буквы, а список — всю ширину. Пустые дни пропускаются: листать
   ночами и выходными, в которых ничего нет, незачем. */

.agenda { display: flex; flex-direction: column; }
.agday {
  display: grid; grid-template-columns: 54px minmax(0, 1fr); gap: 14px;
  padding: 13px 0; border-top: 1px solid var(--line);
}
.agday:first-child { border-top: 0; }
.agday__d { display: flex; flex-direction: column; align-items: center; gap: 2px; padding-top: 2px; }
.agday__d b { font: 600 19px/1 var(--mono); color: var(--text); }
.agday__d span { font: 500 10.5px/1 var(--display); letter-spacing: .08em; text-transform: uppercase; color: var(--text-3); }
.agday.is-today .agday__d b,
.agday.is-today .agday__d span { color: var(--accent); }

.agday__items { display: flex; flex-direction: column; gap: 6px; min-width: 0; }
/* Колонка времени — по содержимому, а не фиксированная: на Android системный
   моноширинный шире, и «18:00–19:00» в 70–88 px не влезало и наезжало на
   имя — в списке и в попапе дня месяца (владелец, 28.09.2026). Ширина у
   всех строк одна: цифры табличные, а запись всегда «чч:мм–чч:мм». */
.agev {
  display: grid; grid-template-columns: max-content minmax(0, 1fr); gap: 12px; align-items: baseline;
  width: 100%; text-align: left; padding: 10px 12px;
  background: var(--surface-2); border: 0; border-left: 3px solid var(--accent);
  border-radius: var(--r-sm); cursor: pointer;
}
.agev:hover { background: var(--accent-soft); }
.agev--done { border-left-color: var(--text-3); }
.agev--warn { border-left-color: var(--warn); background-image: linear-gradient(var(--warn-soft), var(--warn-soft)); }
.agev--live { border-left-color: var(--accent-2); background-color: var(--accent); }
.agev--live .agev__t, .agev--live .agev__n, .agev--live .agev__n b, .agev--live .agev__n span { color: var(--on-accent); }
.agev--off { border-left-color: var(--line-2); opacity: .6; }
.agev--off .agev__n b { text-decoration: line-through; }
.agev__t { white-space: nowrap; }
/* Мягко удалённое — уже не на экране, хотя запрос ещё не ушёл. */
.is-gone { display: none !important; }
/* Окно-развилка: подвал без кнопок — пустая рамка, её не рисуем. */
.modal--pick .modal__foot { display: none; }
/* «Удалить» в форме занятия — отдельной строкой, не в 8 px от «Отменить». */
.formacts__danger { flex: 1 0 100%; margin-top: 8px; }
.agev--slot { border-left-style: dashed; border-left-color: var(--line-2); background: none; cursor: default; }
.agev__t { font: 500 12.5px/1.3 var(--mono); color: var(--text-2); font-variant-numeric: tabular-nums; }
.agev__n { min-width: 0; }
.agev__n b { display: block; font-size: 13.5px; font-weight: 600; }
.agev__n span { display: block; font-size: 12px; color: var(--text-3); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

/* Мини-агенда месяца: попап у клетки дня. Фон непрозрачный — под ним
   чужие клетки, стеклу тут просвечивать нечем. */
.magenda {
  position: fixed; z-index: 70; width: min(300px, calc(100vw - 20px));
  display: flex; flex-direction: column; gap: 6px;
  padding: 12px; background: var(--bg-2, var(--bg));
  border: 1px solid var(--line); border-radius: var(--r-card);
  box-shadow: var(--shadow-card);
}
.magenda__h { font-size: 13px; font-weight: 650; color: var(--text-2); }
.magenda__none { margin: 2px 0; font-size: 13px; color: var(--text-3); }
.magenda__go { margin-top: 4px; align-self: flex-start; }

/* ═══════════════ Расписание месяцем ═══════════════ */

.month {
  display: grid; grid-template-columns: repeat(7, minmax(0, 1fr));
  border: 1px solid var(--line); border-radius: var(--r-card); overflow: hidden;
  background: var(--surface);
}
.month__wd {
  padding: 8px 9px; font: 500 10.5px/1 var(--display);
  letter-spacing: .08em; text-transform: uppercase; color: var(--text-3);
  background: var(--surface-2); border-bottom: 1px solid var(--line);
}
.mday {
  min-height: 96px; padding: 6px 7px 8px;
  display: flex; flex-direction: column; gap: 3px; min-width: 0;
  border-top: 1px solid var(--line); border-right: 1px solid var(--line);
  cursor: pointer;
  /* Клетка стала кнопкой (доступ с клавиатуры) — родное кнопочное снимаем. */
  background: none; font: inherit; text-align: left; color: inherit; border-left: 0; border-bottom: 0;
}
.mday:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }
.mday:nth-child(7n) { border-right: 0; }
.mday:hover { background: var(--surface-2); }
/* Дни соседних месяцев показываем, но приглушаем: без них сетка рвётся,
   а в полную силу они спорят со своим месяцем. */
.mday--out { opacity: .42; }
.mday__n {
  align-self: flex-start; min-width: 21px; height: 21px; padding: 0 5px;
  display: grid; place-items: center; border-radius: 999px;
  font: 500 12px/1 var(--mono); color: var(--text-2);
}
.mday--today .mday__n { background: var(--accent); color: #fff; font-weight: 600; }
/* Месяц занимает сетку целиком: обёртка становится колонкой, месяц тянется
   на остаток, недели делят высоту поровну (владелец, 28.09.2026: «растяни
   расписание на всю высоту страницы»). Рамка у месяца своя не нужна —
   есть рамка обёртки. */
.gridwrap[data-cal="month"] { display: flex; flex-direction: column; }
.gridwrap[data-cal="month"] .weekgrid { flex: 1 1 auto; min-height: 0; display: flex; flex-direction: column; min-width: 0; }
/* min-height: 0 и flex-shrink нужны: без них 1fr-ряды берут высоту по
   содержимому и месяц вылезает за сетку. Когда экран ниже шести недель по
   минимуму клетки, ряды выходят за рамку месяца, и сетка прокручивается —
   поэтому overflow здесь видимый, а не обрезанный. */
.gridwrap[data-cal="month"] .month {
  flex: 1 1 auto; min-height: 0; overflow: visible; border: 0; border-radius: 0;
  /* Именно 1fr, а не minmax(68px, 1fr): у 1fr нижняя граница — содержимое
     клетки, и три плашки с «ещё 3» не вылезают в соседнюю неделю; лишнее
     место недели делят поровну. */
  grid-template-rows: auto; grid-auto-rows: 1fr;
}
/* Минимум клетки — её содержимое, а не 96/68 px: с числом ряд 1fr берёт
   число за нижнюю границу и сжимает плашки друг на друга (три занятия
   плюс «ещё 3» на 1280×800). Плашки не сжимаются: не влезли — сетка
   прокручивается. */
.gridwrap[data-cal="month"] .mday { min-height: auto; }
.gridwrap[data-cal="month"] .mday > * { flex-shrink: 0; }
.mchip__t, .mchip__n { font-style: normal; font-weight: inherit; }
.mchip__t { font-variant-numeric: tabular-nums; }
.mchip {
  display: block; width: 100%; text-align: left;
  padding: 3px 6px; border: 0; border-radius: 5px;
  background: var(--accent-soft); color: var(--accent);
  font-size: 11px; line-height: 1.35; cursor: pointer;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
/* Прошедшее — серая плашка с рамкой, а не голый текст: заливка «surface-2»
   почти сливалась с клеткой, и на телефоне, где раньше стояли полоски
   полного цвета, занятие выглядело подписью без рамки (владелец,
   28.09.2026: «почему убрал рамки, оставил только текст»). */
.mchip--done { background: var(--surface-3); color: var(--text-2); box-shadow: inset 0 0 0 1px var(--line-2); }
.mmore { font-size: 10.5px; color: var(--text-3); padding-left: 6px; }

@media (max-width: 700px) {
  .mday { min-height: 68px; padding: 4px 3px; gap: 2px; }
  /* Раньше плашка была полоской без текста. Теперь, когда месяц растянут
     на экран и клетка выросла, в неё входит имя мелким шрифтом; время
     на 52 px не помещается — оно в попапе дня (владелец, 28.09.2026:
     «впиши в плашки имена учеников мелким шрифтом»). */
  .mchip { padding: 1px 3px; border-radius: 3px; font-size: 9px; line-height: 1.3; font-weight: 500; letter-spacing: -.01em; }
  .mchip__t { display: none; }
  .month__wd { padding: 6px 4px; text-align: center; }
  .mday__n { font-size: 11px; }
}


/* ═══════════════ Расписание на телефоне ═══════════════ */

/* Подсказка про правый клик и перетаскивание пряталась на узком экране
   правилом .legend__hint { display: none } — и не пряталась: соседнее
   .legend span специфичнее (0,1,1 против 0,1,0) и возвращало flex. На
   телефоне она разворачивалась в узкий столбец на пол-экрана.

   Прячем и по ширине, и по отсутствию наведения: на планшете с пальцем
   правого клика тоже нет, а ширины хватает. */
@media (max-width: 980px), (hover: none) {
  .legend span.legend__hint { display: none; }
}

@media (max-width: 700px) {
  /* Кнопкам заголовка нужен перенос: «Текущая», два тумблера и две кнопки
     в одну строку не встают ни на одном телефоне, а без переноса уезжают
     за край вместе с половиной управления. */
  .pagehead { gap: 12px; padding-bottom: 14px; }
  .pagehead__actions { flex-wrap: wrap; margin-left: 0; width: 100%; }
  .pagehead__ttl { gap: 10px; width: 100%; }
  .pagehead__title { font-size: 22px; }

  /* Сегментированные стрелки и тумблеры растягиваем на строку: попадать
     пальцем в кнопку шириной с курсор мыши неудобно. */
  .segmented { flex: 1 1 auto; }
  .segmented button { flex: 1 1 auto; min-height: 38px; }
  .pagehead__actions .btn { min-height: 38px; }

  .legend--sched { gap: 8px 14px; }
}

/* ═══════════════ Вид расписания из бургер-меню ═══════════════

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

.navrow { display: flex; align-items: stretch; border-radius: var(--r-ctl); }
.navrow .navitem { flex: 1 1 auto; min-width: 0; }
.navrow__pick {
  display: none; align-items: center; gap: 5px;
  padding: 0 10px; background: none; border: 0; border-radius: var(--r-ctl);
  color: var(--text-3); font-size: 12px; cursor: pointer;
}
.navrow__pick:hover { background: var(--surface-2); color: var(--text); }

/* Подсветка и наведение — на всём ряду, а не на одном пункте: хвост с видом —
   часть той же плашки, и обводка половины ряда выглядела недоделкой. Старым
   браузерам без :has() остаётся прежняя подсветка самого пункта. */
@supports selector(:has(*)) {
  .navrow:hover { background: var(--surface-2); }
  .navrow.is-open { background: var(--surface-2); }
  .navrow:has(.navitem.is-active) { background: var(--accent-soft); }
  .navrow .navitem:hover,
  .navrow .navitem.is-active { background: none; }
  .navrow:has(.navitem.is-active) .navrow__pick { color: var(--accent); }
}
.navrow__car { display: grid; transition: transform .16s; }
.navrow__car svg { width: 14px; height: 14px; }
.navrow.is-open .navrow__car { transform: rotate(180deg); }
.navrow__menu { display: none; }

@media (max-width: 980px) {
  .navrow__pick { display: inline-flex; }
  .navrow__menu:not([hidden]) {
    display: flex; flex-direction: column; gap: 1px;
    margin: 2px 0 5px; padding-left: 26px;
  }
  /* Кнопка вида у заголовка на телефоне спрятана: она теперь в меню. */
  /* Заголовок «Расписание» повторяет вкладку внизу; на его месте — выбор
     вида, который говорит, ЧТО показано. Подпись отрезка стоит рядом. */
  .view[data-view="schedule"] .pagehead__title { display: none; }
  .cal__body { padding-bottom: 72px; }
  .pacts { flex-direction: column; align-items: stretch; }
  .pacts .btn { width: 100%; min-height: 44px; }
  /* Действия занятия — первой строкой формы: чтобы отметить проведённым,
     приходилось прокрутить окно на 400 px до самого низа. */
  .modal__body .formacts { order: -1; margin: 0 0 6px; padding: 0 0 12px; border-top: 0; border-bottom: 1px solid var(--line); }
}

/* ═══════════════ Карман настроек за шестерёнкой ═══════════════ */

.gearwrap { position: relative; }
.gearwrap > .iconbtn { position: relative; width: 34px; height: 34px; }
/* Идёт правка — точка на шестерёнке: сам тумблер спрятан в кармане,
   а видеть режим нужно и с закрытым. */
#gearBtn.is-hold { color: var(--warn); border-color: var(--warn); }
#gearBtn.is-hold::after {
  content: ''; position: absolute; top: 4px; right: 4px;
  width: 7px; height: 7px; border-radius: 999px; background: var(--warn);
}

.gearpop {
  position: absolute; top: calc(100% + 6px); right: 0; z-index: 35;
  width: 268px; padding: 10px;
  display: flex; flex-direction: column; gap: 8px;
  border-radius: var(--r-ctl);
  animation: pop .12s ease-out;
}
.gearpop[hidden] { display: none; }
.gearpop__label {
  font: 500 10.5px/1 var(--mono); letter-spacing: .1em;
  text-transform: uppercase; color: var(--text-3); padding: 2px 2px 3px;
}
.gearpop .tgl { display: flex; }
.gearpop .tgl__b { flex: 1 1 auto; justify-content: flex-start; }

.gearpop__legend { display: none; }
@media (max-width: 980px), (hover: none) {
  /* Легенда над сеткой на узком экране спрятана — цвета объясняются здесь. */
  .legend--sched { display: none; }
  .gearpop__legend {
    display: flex; flex-direction: column; gap: 7px;
    margin-top: 2px; padding: 10px 2px 2px; border-top: 1px solid var(--line);
    font-size: 12px; color: var(--text-2);
  }
  .gearpop__legend span { display: inline-flex; align-items: center; gap: 7px; }
}

/* ═══════════════ Плюс в углу расписания ═══════════════

   Единственная кнопка создания: спрашивает, что ставим — занятие или окно.
   Живёт в углу экрана, а не в заголовке: на телефоне до угла достаёт
   большой палец, а заголовок остаётся полю расписания. */

.fab {
  position: fixed; right: 24px; bottom: calc(20px + env(safe-area-inset-bottom, 0px));
  z-index: 25; display: flex; flex-direction: column; align-items: flex-end; gap: 8px;
}
.fab__b {
  width: 54px; height: 54px; border-radius: 999px;
  display: grid; place-items: center; cursor: pointer;
  background: var(--accent); border: 1px solid var(--accent); color: var(--on-accent);
  box-shadow: 0 12px 28px -10px color-mix(in srgb, var(--accent) 60%, transparent);
  transition: background .12s, border-color .12s;
}
.fab__b:hover { background: var(--accent-2); border-color: var(--accent-2); }
.fab__b [data-icon] { width: 22px; height: 22px; transition: transform .18s; }
.fab.is-open .fab__b [data-icon] { transform: rotate(45deg); }

.fab__menu {
  display: flex; flex-direction: column; align-items: flex-end; gap: 7px;
  animation: pop .12s ease-out;
}
.fab__menu[hidden] { display: none; }
.fab__item {
  display: inline-flex; align-items: center; gap: 8px;
  padding: 10px 15px; border-radius: 999px; cursor: pointer;
  font-size: 13px; font-weight: 500;
}
.fab__item:hover { color: var(--accent); border-color: var(--accent); }
.fab__item [data-icon] { width: 15px; height: 15px; }

@media (max-width: 700px) {
  .fab { right: 16px; bottom: calc(16px + env(safe-area-inset-bottom, 0px)); }
  .gearwrap > .iconbtn { width: 40px; height: 38px; }
}

@media (prefers-reduced-motion: reduce) {
  .navrow__car, .fab__b [data-icon] { transition: none; }
  .gearpop, .fab__menu { animation: none; }
}

/* В списке и месяце подсказки про жесты сетки нет: там не перетаскивают
   и не создают кликом. Два класса перевешивают .legend span (0,2,0 > 0,1,1). */
.legend--flat .legend__hint { display: none; }

/* ═══════════════ Лента: группы однотипных записей ═══════════════ */

.fevent__n {
  display: inline-block; margin-left: 7px; padding: 2px 7px;
  border-radius: 999px; background: var(--accent-soft); color: var(--accent);
  font: 600 11px/1 var(--mono); vertical-align: 1px;
}
.fgroup { display: flex; flex-direction: column; gap: 10px; margin-top: 4px; }
.fsub { border-left: 2px solid var(--line-2); padding-left: 10px; }
.fsub__who { font-size: 12.5px; font-weight: 600; }

/* ═══════════════ Ученики: выпадающий выбор на телефоне ═══════════════ */

.rpick {
  display: none; align-items: center; gap: 10px; width: 100%;
  padding: 9px 12px; text-align: left; cursor: pointer;
  background: var(--surface); border: 1px solid var(--line-2);
  border-radius: var(--r-ctl);
}
.rpick__txt { min-width: 0; flex: 1 1 auto; display: flex; flex-direction: column; gap: 1px; }
.rpick__name { font-size: 13.5px; font-weight: 600; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.rpick__meta { font-size: 11.5px; color: var(--text-3); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.rpick .vpick__car { transition: transform .16s; }
.roster__side.is-open .rpick .vpick__car { transform: rotate(180deg); }

@media (max-width: 980px) {
  .roster__side { position: relative; }
  .rpick { display: flex; }

  /* Список за кнопкой: выпадает сверху и прокручивается сам. Он и на
     широком экране прокручивался внутри себя — правило то же, короче предел. */
  .roster__side .roster__list { display: none; }
  .roster__side.is-open .roster__list {
    display: flex; position: absolute; top: calc(100% + 6px); left: 0; right: 0;
    z-index: 30; max-height: min(60dvh, 430px);
    box-shadow: var(--shadow-pop);
  }
}

/* ═══════════════ Поиск: выпадающие результаты ═══════════════ */

.topbar__search { position: relative; }
.spop {
  position: absolute; top: calc(100% + 8px); left: 0; right: 0; z-index: 45;
  max-height: min(64dvh, 480px); overflow: auto;
  padding: 7px; border-radius: var(--r-ctl);
  animation: pop .12s ease-out;
  /* Не стекло, как остальные слои: у шапки свой backdrop-filter, и внутри
     неё чужому blur нечего размывать — «стеклянная» подложка оставалась
     прозрачной, результаты сливались со страницей. Подложка плотная. */
  background: color-mix(in srgb, var(--surface) 30%, var(--bg));
  -webkit-backdrop-filter: none;
  backdrop-filter: none;
}
.spop[hidden] { display: none; }
.spop__h {
  font: 500 10px/1 var(--mono); letter-spacing: .1em; text-transform: uppercase;
  color: var(--text-3); padding: 8px 9px 5px;
}
.spop__h:first-child { padding-top: 4px; }
.spop__none { padding: 12px 9px; font-size: 12.5px; color: var(--text-3); }
.srow {
  display: flex; flex-direction: column; gap: 1px; width: 100%;
  padding: 8px 9px; text-align: left; cursor: pointer;
  background: none; border: 0; border-radius: var(--r-sm);
}
.srow b { font-size: 13px; font-weight: 600; color: var(--text); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.srow span { font-size: 11.5px; color: var(--text-3); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.srow:hover { background: var(--accent-soft); }
.srow:hover b { color: var(--accent); }

/* ═══════════════ Месячный отчёт в профиле ═══════════════ */

.repnav { display: flex; align-items: center; justify-content: space-between; gap: 10px; flex-wrap: wrap; }
.repwrap { overflow-x: auto; margin-top: 2px; }
.reptable { width: 100%; border-collapse: collapse; font-size: 12.5px; }
.reptable th {
  font: 500 10px/1.35 var(--mono); letter-spacing: .08em; text-transform: uppercase;
  color: var(--text-3); text-align: left; padding: 4px 10px 8px;
}
.reptable td {
  padding: 8px 10px; border-top: 1px solid var(--line);
  font-variant-numeric: tabular-nums; white-space: nowrap;
}
.reptable td:first-child { white-space: normal; min-width: 120px; }

/* ═══════════════ Рисование окна прямоугольником ═══════════════ */

.is-slotdraw .cal__col { cursor: crosshair; }
.slotghost {
  position: absolute; left: 2px; right: 2px; z-index: 3; pointer-events: none;
  border: 1.5px dashed var(--accent); border-radius: 7px;
  background: var(--accent-soft);
}

/* ═══════════════ Свой календарь у полей даты ═══════════════ */

/* Родной значок прячем: по нажатию открывается календарь панели. */
#modal input[type="date"]::-webkit-calendar-picker-indicator { display: none; }

.dpick {
  position: absolute; z-index: 30; width: 240px; padding: 10px;
  /* Подложка — как у .tpick ниже: запасной опак + слой с примесью. */
  background: var(--bg);
  background: linear-gradient(color-mix(in srgb, var(--surface) 30%, var(--bg)), color-mix(in srgb, var(--surface) 30%, var(--bg))), var(--bg);
  border: 1px solid var(--line); border-radius: var(--r-ctl);
  box-shadow: var(--shadow-pop);
  animation: pop .12s ease-out;
}
.dpick__head { display: flex; align-items: center; justify-content: space-between; margin-bottom: 6px; }
.dpick__head b { font-size: 13px; font-weight: 600; }
.dpick__nav {
  width: 27px; height: 27px; border: 0; background: none; cursor: pointer;
  color: var(--text-3); border-radius: var(--r-sm);
}
.dpick__nav:hover { background: var(--surface-2); color: var(--text); }
.dpick__grid { display: grid; grid-template-columns: repeat(7, 1fr); gap: 1px; }
.dpick__wd {
  font: 500 9.5px/1 var(--mono); letter-spacing: .05em; text-transform: uppercase;
  color: var(--text-3); text-align: center; padding: 5px 0 7px;
}
.dpick__d {
  height: 28px; border: 0; background: none; cursor: pointer;
  border-radius: 6px; color: var(--text-2); font-size: 12px;
  font-variant-numeric: tabular-nums;
}
.dpick__d:hover { background: var(--accent-soft); color: var(--accent); }
.dpick__d.is-today { box-shadow: inset 0 0 0 1px var(--accent); }
.dpick__d.is-on { background: var(--accent); color: var(--on-accent); font-weight: 600; }
.dpick__today { margin-top: 8px; }

@media (prefers-reduced-motion: reduce) { .dpick { animation: none; } }

/* ═══════════════ Свой выбор времени ═══════════════ */

/* Родной значок прячем: по нажатию открываются колонки панели. */
#modal input[type="time"]::-webkit-calendar-picker-indicator { display: none; }

.tpick {
  position: absolute; z-index: 30; display: flex; gap: 8px; padding: 10px;
  /* Подложка непрозрачная и в два слоя: сверху лёгкая примесь --surface,
     снизу плотный --bg. Одна строка с color-mix в браузере без его
     поддержки отбрасывалась целиком, и колонки времени висели стеклом
     поверх формы — сквозь них читалась кнопка «Отмена». Первая строка —
     запасной вариант для таких браузеров, вторая перекрывает её там, где
     color-mix понимают. */
  background: var(--bg);
  background: linear-gradient(color-mix(in srgb, var(--surface) 30%, var(--bg)), color-mix(in srgb, var(--surface) 30%, var(--bg))), var(--bg);
  border: 1px solid var(--line); border-radius: var(--r-ctl);
  box-shadow: var(--shadow-pop);
  animation: pop .12s ease-out;
}
.tpick__col { display: flex; flex-direction: column; gap: 5px; }
.tpick__t {
  font: 500 9.5px/1 var(--mono); letter-spacing: .05em; text-transform: uppercase;
  color: var(--text-3); text-align: center;
}
.tpick__list {
  width: 62px; max-height: 214px; overflow-y: auto;
  display: flex; flex-direction: column; gap: 1px;
  scrollbar-width: thin; overscroll-behavior: contain;
}
.tpick__v {
  flex: 0 0 auto; height: 30px; border: 0; background: none; cursor: pointer;
  border-radius: 6px; color: var(--text-2); font-size: 13px;
  font-variant-numeric: tabular-nums;
}
.tpick__v:hover { background: var(--accent-soft); color: var(--accent); }
.tpick__v.is-on { background: var(--accent); color: var(--on-accent); font-weight: 600; }

@media (prefers-reduced-motion: reduce) { .tpick { animation: none; } }

/* ═══════════════ Неделя целиком на телефоне ═══════════════

   Сравнение владельца с большим календарём: там семь узких колонок и почти
   весь экран — сетка, у нас же колонка держала 112px и неделя требовала
   прокрутки вбок. Теперь ширину колонок решает экран: инлайновые --cols
   перебиваются намеренно (число колонок берётся из data-days — repeat()
   не умеет var()). Данные, клики и перетаскивание не тронуты: колонки
   те же, только уже. */
@media (max-width: 700px) {
  .weekgrid { min-width: 0; }
  .cal[data-days="1"] { --cols: 34px repeat(1, minmax(0, 1fr)) !important; }
  .cal[data-days="3"] { --cols: 34px repeat(3, minmax(0, 1fr)) !important; }
  .cal[data-days="7"] { --cols: 34px repeat(7, minmax(0, 1fr)) !important; }

  /* Шапка дня — столбиком и по центру: «Пн» над числом, как в календарях. */
  .cal__day { flex-direction: column; align-items: center; gap: 1px; padding: 5px 2px; }
  .cal__day b { font-size: 10.5px; }
  .cal__day span { font-size: 9.5px; }
  .cal__hour { padding: 2px 3px 0 0; font-size: 9px; }

  /* В узкой колонке у плашки остаётся имя: длительность говорит высота,
     подробности — по нажатию, как и раньше. */
  .cal[data-days="3"] .ev, .cal[data-days="7"] .ev { padding: 2px 3px; border-left-width: 2px; gap: 0; }
  .cal[data-days="3"] .ev b, .cal[data-days="7"] .ev b {
    font-size: 9.5px; line-height: 1.2; font-weight: 500;
    white-space: normal; word-break: break-word;
  }
  .cal[data-days="3"] .ev span, .cal[data-days="7"] .ev span,
  .cal[data-days="3"] .ev i, .cal[data-days="7"] .ev i { display: none; }
  .cal[data-days="3"] .slot__when, .cal[data-days="7"] .slot__when { font-size: 8.5px; }
  .cal[data-days="3"] .slot__seats, .cal[data-days="7"] .slot__seats { display: none; }

  /* Шапке страницы — только необходимое: даты недели дублируют колонки. */
  [data-view="schedule"] .pagehead { padding-bottom: 8px; gap: 8px; }
  [data-view="schedule"] .pagehead__title { font-size: 19px; }
  [data-view="schedule"] .pagehead__sub { display: none; }
  /* Было 34 px ради лишней строки сетки. Четыре пикселя сетка переживёт, а
     перелистывание недели — главная кнопка этого экрана, и мазать по ней
     пальцем не должно быть привычкой (владелец, 18.09.2026). */
  [data-view="schedule"] .segmented button,
  [data-view="schedule"] .pagehead__actions .btn { min-height: 38px; }
}

/* ═══════════════ Просмотрщик листов ═══════════════ */

/* Миниатюра в полосе — сотня пикселей, посмотреть глазами через неё нельзя.
   Просмотрщик один на документ, поверх всего; закрывается кликом и Esc,
   между листами — стрелки. Подложка плотная: лист должен читаться, а не
   просвечивать панелью. */
.lightbox {
  position: fixed; inset: 0; z-index: 100; display: grid; place-items: center;
  background: rgba(8, 6, 16, .88); cursor: zoom-out; animation: pop .12s ease-out;
}
.lightbox__img {
  max-width: 96vw; max-height: 96vh; object-fit: contain;
  border-radius: 6px; background: #fff; box-shadow: var(--shadow-pop);
}
.lightbox__x, .lightbox__nav {
  position: absolute; width: 44px; height: 44px; display: grid; place-items: center;
  border: 0; border-radius: 999px; cursor: pointer;
  background: rgba(255, 255, 255, .14); color: #fff; font: 400 26px/1 var(--mono);
}
.lightbox__x:hover, .lightbox__nav:hover { background: rgba(255, 255, 255, .28); }
.lightbox__x { top: 14px; right: 14px; }
.lightbox__nav { top: 50%; transform: translateY(-50%); }
.lightbox__nav--prev { left: 14px; }
.lightbox__nav--next { right: 14px; }
.lightbox__n {
  position: absolute; bottom: 14px; left: 50%; transform: translateX(-50%);
  color: rgba(255, 255, 255, .85); font: 500 12px/1 var(--mono); font-variant-numeric: tabular-nums;
}
body.is-lightbox { overflow: hidden; }
@media (prefers-reduced-motion: reduce) { .lightbox { animation: none; } }

/* За что снято — в карточке проверки по критериям. */
.chk__ded { margin: 6px 0 8px; padding: 6px 10px; border-left: 2px solid var(--accent); font-size: 12.5px; line-height: 1.45; }
.chk__ded b { display: block; margin-bottom: 2px; }
.chk__k { color: var(--text-3); }

/* Подпись под миниатюрой листа: какие задачи на нём нашла разметка. */
.sheetbar .shot { display: inline-flex; flex-direction: column; align-items: center; gap: 3px; }
.sheetbar .shot__cap { font: 500 10.5px/1 var(--mono); color: var(--text-3); white-space: nowrap; }
/* Пункт засчитан — зелёная кромка вместо акцентной. */
.chk__ded.is-ok { border-left-color: var(--ok, #2e9e5b); }

/* ═══════════════ мини-апп Telegram ═══════════════ */

/* Класс ставит app.js, когда страница открыта внутри мессенджера. Здесь
   только то, что от Telegram и зависит: остальную раскладку панель делит
   с браузером, и разводить их по двум наборам правил — верный способ
   однажды починить одну и сломать другую. */

/* Высоту окна задаёт Telegram, а не браузер: в шторке мини-аппа 100dvh
   больше видимой части, и низ рельса уезжает под край. Переменную ставит
   telegram-web-app.js; запасное значение — прежнее поведение. */
.is-tma .rail { height: var(--tg-viewport-stable-height, 100dvh); }

/* Выход из мини-аппа бессмысленен: следующее открытие снова войдёт по
   подписи Telegram. Кнопка бы только пугала. */
.is-tma #logoutBtn,
.is-tma #logoutBtn2 { display: none; }

/* Сафари на айфоне зумит страницу, когда палец попадает в поле мельче
   16 px, и обратно не отматывает: шапка и кнопка в углу уезжают за край.
   Правило общее для всех сенсорных экранов — на планшете владельца оно
   тоже к месту. */
@media (pointer: coarse) {
  .fld input, .fld select, .fld textarea,
  .topbar__search input,
  .paycell input, .task__ans input, .notebox,
  .task__text, .task__mode, .task__review input, #helperText, #moneyWho, .rule__param, #payPhone { font-size: 16px; }
}

/* ── конструктор на телефоне ──────────────────────────────────────────── */

/* Кнопкам отдаётся отдельная строка целиком: прижатые вправо, они на узком
   экране жались в столбик по одной и рвали шапку. Сумма в подвале — туда же,
   иначе она отталкивает кнопки за край. */
@media (max-width: 760px) {
  .builder__acts { margin-left: 0; flex: 1 0 100%; }
  .builder__sum { margin-left: 0; flex: 1 0 100%; }
}

/* ── телефон: длинный текст переносится, а не обрезается ──────────────── */

/* На узком экране многоточие съедало смысл: у поступления пропадало, за
   сколько занятий перевод, у пробника — его название целиком. Строка
   короче экрана важнее, чем строка в один ряд. */
@media (max-width: 760px) {
  .payline { grid-template-columns: auto 1fr auto; gap: 4px 10px; }
  .payline__note {
    grid-column: 1 / -1;
    white-space: normal; overflow: visible; text-overflow: clip;
  }
  /* Тема занятия в строке оплат — туда же: «Тригонометрия: отбор корней на
     отрезке» в одну строку не помещается ни на одном телефоне. */
  .payrow__x { white-space: normal; }

  /* Кнопки в шапке карточки ученика вставали рваной лесенкой у правого края:
     две в одной строке, третья одна под ними. Отдельная строка во всю
     ширину и выравнивание влево читаются спокойнее. */
  .sc-head__acts { margin-left: 0; flex: 1 0 100%; justify-content: flex-start; }
}

/* ── телефон: список занятий и перелистывание ─────────────────────────── */

@media (max-width: 760px) {
  /* Тема занятия обрывалась многоточием на середине: «Тригонометрия: отбор
     корней на отр…». В списке занятий тема — главное, ради чего в него и
     смотрят, поэтому она переносится на вторую строку. */
  .agev__n span { white-space: normal; overflow: visible; text-overflow: clip; }
  .agev { gap: 10px; }

  /* Перелистывание недели пальцем: стрелки были 34 px в высоту. */
  .segmented button { min-height: 38px; }
}


/* ═══════════════ Пальцем, а не мышью ═══════════════ */

/* Блок стоит В КОНЦЕ ФАЙЛА, и это не вкусовщина.
 *
 * Раньше он жил в начале, среди базовых правил, — и не работал: у `.sw`,
 * `.linkbtn`, `.toggle2 button` собственные размеры описаны ниже по файлу,
 * а при равной специфичности побеждает последний объявленный. Медиазапрос
 * специфичности не добавляет. Получалось, что правила для касания в проекте
 * были, выглядели рабочими и молча проигрывали: переключатель оставался
 * 19 px, ссылки-кнопки — 19 px, кнопка вида задачи — 25 px.
 *
 * Отсюда правило: всё, что перекрывает размеры под палец, дописывать СЮДА,
 * а не в начало.
 */
@media (pointer: coarse) {
  button, .btn, .navitem { touch-action: manipulation; }

  .iconbtn { min-width: 40px; min-height: 40px; }
  .btn { min-height: 38px; }
  .btn--sm { min-height: 36px; padding: 7px 11px; }

  /* Ссылка-кнопка: выглядит текстом, а нажимают её как кнопку. Отбивка
     сверху и снизу не двигает соседей — строка и так выше своего текста. */
  .linkbtn { padding: 8px 0; }

  .dpick__d { height: 40px; }
  .tpick__v { height: 38px; }
  .tagchip { min-height: 36px; }

  /* Тумблер: 19 px в высоту — это полоска, а не переключатель. */
  .sw { width: 44px; height: 26px; }
  .sw::after { width: 20px; height: 20px; top: 2px; left: 2px; }
  .sw[aria-checked="true"]::after { transform: translateX(18px); }

  /* «Автопроверка / Развёрнутая» — одна из самых нажимаемых кнопок
     конструктора и была самой мелкой. */
  .toggle2 button { padding: 9px 13px; font-size: 12.5px; }

  /* Всё, что аудит 27.09.2026 намерил ниже 40 px: кнопки занятия и очереди,
     подвал окна, пункты «⋯», стрелки календаря, поля эталона и баллов,
     список проверки, «+ Своя цена», оглавление профиля, подсказки помощника. */
  .lesson__acts .btn, .btn--sm, .modal__foot .btn, .ctxmenu button { min-height: 40px; }
  .dpick__nav { min-width: 40px; min-height: 40px; }
  .task__ans input, .task__pts input, .task__review input { min-height: 40px; }
  .task__mode { min-height: 40px; }
  .linkbtn { min-height: 40px; display: inline-flex; align-items: center; }
  .profile__nav a, .profile__nav button { min-height: 40px; display: inline-flex; align-items: center; }
  .hchip { min-height: 40px; }
  .rule__param { min-height: 40px; }
}

/* На узком экране подросшая пара кнопок сама перестала помещаться в строку
   задачи — отыгрываем по бокам, высоту не трогаем. */
@media (pointer: coarse) and (max-width: 430px) {
  .toggle2 button { padding: 9px 8px; font-size: 11.5px; }
}

/* ── уведомления: хвост списка на телефоне ────────────────────────────── */

/* Кнопка — последняя строка самой ленты, поэтому без своей рамки и заливки:
   рамка внутри карточки читалась бы как вторая карточка. */
.feed__more {
  width: 100%; padding: 12px 14px; min-height: 44px;
  border: 0; border-top: 1px solid var(--line); background: none;
  font-size: 12.5px; font-weight: 500; color: var(--accent);
  text-align: center; cursor: pointer;
}
.feed__more:hover { background: var(--surface-2); }

@media (max-width: 980px) {
  /* «Все» в заголовке и «Ещё N» в хвосте ведут в одно окно. На телефоне
     хвост ближе к тому месту, где список обрывается, — заголовок к этому
     моменту уже ушёл вверх. */
  #feedAll { display: none; }
}

/* ── режим проверки задачи ────────────────────────────────────────────── */

/* Список, а не тумблер: два тумблера подряд не помещались в строку задачи
   ни на одном телефоне. Ширину не задаём — список сам по самой длинной
   подписи, а max-width не даёт ему распереть карточку. */
/* Подпись длинная — «Проверка решения (Самостоятельно)», — и на телефоне
   она переносилась внутри самого списка: строка задачи вырастала с 32 px до
   85 и вставала в два этажа, ровно то, на что владелец жаловался 18.09.2026.
   Обрезаем многоточием: в свёрнутом виде видно начало, а полный текст всё
   равно читается в раскрытом списке. */
.task__mode {
  /* Место для корзины: 37 px кнопки, 8 px зазора и пиксель запаса. Без
     него список занимал строку целиком и выталкивал корзину на второй
     этаж — ровно то, на что владелец жаловался 18.09.2026. */
  flex: 0 1 auto; min-width: 0; max-width: calc(100% - 124px); padding: 6px 9px;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
  font-family: inherit; font-size: 12px; color: var(--text-2);
  background: var(--surface); border: 1px solid var(--line); border-radius: var(--r-ctl);
}
/* Задача, которую вечером придётся проверять руками, — единственное здесь,
   что стоит времени. Цвет тот же, что у предупреждений в расписании. */
.task__mode--mine { color: var(--brass); }
/* Номер задания ЕГЭ — короткое поле рядом со списком проверки. */
.task__ege { display: inline-flex; align-items: center; gap: 5px; flex: 0 0 auto;
             font: 600 12.5px/1 var(--ui); color: var(--text-3); }
.task__ege input { width: 52px; padding: 5px 7px; font: 500 12.5px/1.2 var(--mono);
                   background: var(--surface); color: var(--text);
                   border: 1px solid var(--line); border-radius: var(--r-ctl);
                   -moz-appearance: textfield; }
.task__ege input::-webkit-inner-spin-button, .task__ege input::-webkit-outer-spin-button { -webkit-appearance: none; margin: 0; }
.task__ege input::placeholder { color: var(--text-3); opacity: .8; }

/* Подпись баллов и её поле — один кусок, переносятся вместе. */
.task__pts { margin-left: auto; display: inline-flex; align-items: center; gap: 8px; }
.task__pts input { width: 52px; }

/* ══════════════ помощник ══════════════ */

/* Панель сбоку, а не окно: спрашивают о том, что на экране, и закрывать экран
   ради ответа про экран — плохая мысль. Стекло общее со всплывающими слоями,
   оно описано выше одним правилом на всех. */
.helper {
  position: fixed; z-index: 55;
  right: 14px; top: 64px; bottom: 14px;
  width: 380px; max-width: calc(100vw - 28px);
  display: flex; flex-direction: column;
  border-radius: var(--r-card);
  animation: pop .14s ease-out;
}

.helper__head {
  display: flex; align-items: center; gap: 11px;
  padding: 13px 13px 12px 15px; border-bottom: 1px solid var(--line);
}
.helper__ico {
  display: grid; place-items: center; width: 30px; height: 30px; padding: 6px;
  border-radius: 999px; background: var(--brass-soft); color: var(--brass);
}
.helper__ttl { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.helper__ttl b { font-size: 13.5px; font-weight: 600; }
.helper__sub { font-size: 11.5px; color: var(--text-3); line-height: 1.35; }
.helper__head .iconbtn { margin-left: auto; flex: none; }

.helper__log {
  flex: 1 1 auto; min-height: 0; overflow-y: auto; scrollbar-width: thin;
  display: flex; flex-direction: column; gap: 9px;
  padding: 14px;
}

/* Реплики. Ответ модели ставится textContent'ом и переносами строк — отсюда
   pre-wrap: список из тире модель отдаёт обычным текстом, а не разметкой, и
   разбирать её HTML мы не будем никогда. */
.hmsg {
  max-width: 88%; padding: 9px 12px; border-radius: 13px;
  font-size: 13px; line-height: 1.5; white-space: pre-wrap; overflow-wrap: anywhere;
}
.hmsg--me {
  align-self: flex-end; border-bottom-right-radius: 5px;
  background: var(--accent-soft); color: var(--text);
}
.hmsg--bot {
  align-self: flex-start; border-bottom-left-radius: 5px;
  background: var(--surface-2); border: 1px solid var(--line); color: var(--text);
}
.hmsg--wait { color: var(--text-3); }
.hmsg--err { border-color: color-mix(in srgb, var(--crit) 40%, var(--line)); color: var(--crit); }

/* Примеры вопросов при первом открытии: подсказать, о чём вообще спрашивать,
   дешевле, чем объяснять это словами. */
.hchips { display: flex; flex-wrap: wrap; gap: 7px; }
.hchip {
  padding: 7px 11px; border-radius: 999px; cursor: pointer;
  background: var(--surface); border: 1px dashed var(--line-2);
  color: var(--text-2); font-size: 12px; text-align: left;
}
.hchip:hover { border-style: solid; border-color: var(--accent); color: var(--accent); }

/* Карточка действия. Помощник не делает, а предлагает — и предложение должно
   выглядеть как бумага на подпись, а не как сообщение в чате. */
.hact {
  align-self: stretch; padding: 11px 12px; border-radius: 12px;
  background: var(--surface); border: 1px solid var(--accent);
}
.hact__t { font-size: 12.5px; line-height: 1.45; }
.hact__row { display: flex; flex-wrap: wrap; gap: 8px; padding-top: 10px; }
.hact--done { border-color: var(--ok); background: var(--ok-soft); }
.hact--off { border-color: var(--line); opacity: .7; }

.helper__ask {
  display: flex; align-items: flex-end; gap: 8px;
  padding: 11px 12px; border-top: 1px solid var(--line);
}
.helper__ask textarea {
  flex: 1 1 auto; min-width: 0; resize: none; max-height: 150px;
  padding: 9px 11px; font: inherit; font-size: 13px; line-height: 1.45;
  color: var(--text); background: var(--surface-2);
  border: 1px solid var(--line); border-radius: var(--r-ctl);
}
.helper__ask textarea:focus-visible {
  outline: 2px solid var(--accent); outline-offset: 1px; border-color: transparent;
}
/* Вторым селектором с `.has-lens` — по тому же правилу, что и наведение у
   кнопок выше: линза красит все .iconbtn селектором тяжелее одного класса. */
.iconbtn--send,
.has-lens .iconbtn--send { flex: none; background: var(--accent); border-color: var(--accent); color: #fff; }
.iconbtn--send:hover { color: #fff; filter: brightness(1.08); }
.iconbtn--send[disabled] { opacity: .5; cursor: default; filter: none; }

/* Кнопка-дверь в шапке: была плашкой-состоянием, стала управлением. */
.pulse { position: relative; cursor: pointer; }
.pulse:hover { border-color: var(--accent); color: var(--accent); }
.pulse__ico { display: grid; place-items: center; width: 16px; height: 16px; }

@media (max-width: 980px) {
  /* На телефоне разговор занимает весь экран: панель в 380 px там и не
     помещается, и половину её всё равно съела бы клавиатура. */
  .helper {
    right: 0; left: 0; top: 0; bottom: 0;
    width: auto; max-width: none; border-radius: 0; border-width: 0;
  }
  .helper__head { padding-top: max(13px, env(safe-area-inset-top)); }
  .helper__ask { padding-bottom: max(11px, env(safe-area-inset-bottom)); }
  /* Кнопка помощника остаётся, но без подписи: место в шапке занято поиском.
     Показываем её снова — теперь это работающая кнопка, а не плашка. */
  /* Высоту задаём явно: у пилюли она складывалась из строки текста, а текста
     здесь больше нет — оставалось восемнадцать пикселей, в которые не попасть. */
  .pulse { display: inline-flex; padding: 0; width: 40px; height: 40px; justify-content: center; }
  /* Подписи нет — точка переезжает в угол значка, как метка уведомления. */
  .pulse__dot { position: absolute; top: 7px; right: 7px; }
  .pulse__t { display: none; }
}

/* Точка на кнопке помощника. Цвет акцентный, а не зелёный: зелёная точка на
   этом же месте два года означала «агент на связи» и ничего не предлагала
   нажать — ровно то, из-за чего кнопку и не замечали. Акцент в панели значит
   «сюда можно ткнуть». Мерцание гаснет само при prefers-reduced-motion:
   правило для всех анимаций стоит одно на весь файл. */
.pulse__dot {
  width: 7px; height: 7px; flex: none; border-radius: 999px;
  background: var(--accent);
  animation: ping-accent 2.4s ease-out infinite;
}
@keyframes ping-accent {
  0%   { box-shadow: 0 0 0 0 color-mix(in srgb, var(--accent) 55%, transparent); }
  70%  { box-shadow: 0 0 0 6px transparent; }
  100% { box-shadow: 0 0 0 0 transparent; }
}

/* Удаление ученика — в самом низу карточки, отдельной строкой с объяснением,
   что останется. Верхняя линия отделяет его от полей: это не ещё одно поле. */
.profile__acts--end {
  display: flex; align-items: center; flex-wrap: wrap; gap: 10px;
  margin-top: 16px; padding-top: 14px; border-top: 1px solid var(--line);
}

/* Строка удалённого ученика: третья колонка под кнопку «Вернуть». Сама строка
   не кнопка — карточки у удалённого нет, открывать нечего. */
.stu--gone { grid-template-columns: 30px minmax(0, 1fr) auto; cursor: default; }
.stu--gone:hover { background: none; }
.stu--gone .stu__name { color: var(--text-2); }

/* Выбор порядка в списке учеников стоит над самим списком, в той же рамке. */
.roster__side .hwscope { margin: 0 0 10px; }


/* ═══════════════ Нижнее меню (телефон и планшет) ═══════════════

   Правила лежат в конце файла намеренно: здесь же живут все размеры под
   палец, и при равной специфичности побеждает объявленный последним —
   медиазапрос веса не добавляет (урок 18.09.2026).

   Панель видна на той же ширине, на которой рельс превращается в ящик:
   до 980 px. Выше неё рельс стоит слева целиком, и второе меню было бы
   вторым способом сделать одно и то же. */

.tabbar { display: none; }

@media (max-width: 980px) {
  .tabbar {
    position: fixed; left: 0; right: 0; bottom: 0; z-index: 35;
    display: grid; grid-template-columns: repeat(5, 1fr);
    padding-bottom: env(safe-area-inset-bottom, 0px);
    border-top: 1px solid var(--glass-line, var(--line));
    /* Стекло: заливка вполсилы, чтобы сквозь панель читался нижний слой,
       размытие делает остальное (владелец, 28.09.2026: «совсем немного
       пропускает нижнего слоя»). */
    background: color-mix(in srgb, var(--surface) 58%, transparent);
    -webkit-backdrop-filter: blur(20px) saturate(1.4);
    backdrop-filter: blur(20px) saturate(1.4);
  }

  .tab {
    display: grid; justify-items: center; align-content: center; gap: 3px;
    position: relative; min-height: 56px; padding: 7px 2px 8px;
    background: none; border: 0; cursor: pointer;
    font: 500 10.5px/1.15 var(--ui); color: var(--text-3);
    -webkit-tap-highlight-color: transparent;
  }
  .tab__ico { display: grid; }
  .tab__ico svg { width: 20px; height: 20px; }
  .tab.is-active { color: var(--accent); }
  /* Цифра на жёлтом: белая читается (≈5,8:1), тёмно-синяя — нет (3,08). */
  .tab__badge--warn { color: #fff; }

  /* Счётчик — кружком на значке, как в мобильных приложениях: подпись
     внизу узкая, и число рядом с ней не помещается. */
  .tab__badge {
    position: absolute; top: 5px; left: 50%; margin-left: 5px;
    min-width: 16px; height: 16px; padding: 0 4px; border-radius: 999px;
    display: none; place-items: center;
    font: 600 10px/1 var(--ui); color: var(--on-accent); background: var(--accent);
  }
  .tab__badge--warn { background: var(--warn); color: #1a1236; }
  .tab__badge.is-on { display: grid; }

  /* Бургер больше не нужен: в ящик ведёт «Ещё». */
  .burger { display: none; }

  /* Запас под панелью, иначе она накрывает последнюю строку списка. */
  .views { padding-bottom: calc(64px + env(safe-area-inset-bottom, 0px)); }

  /* Плюс расписания и всплывающие сообщения поднимаются над панелью. */
  .fab { bottom: calc(76px + env(safe-area-inset-bottom, 0px)); }
  /* Сообщение — во всю ширину, а не столбиком в 180 px по центру:
     с left: 50% оно растягивалось на пять строк и закрывало сетку. */
  .toast {
    bottom: calc(80px + env(safe-area-inset-bottom, 0px));
    left: 16px; right: 16px; transform: translateY(14px);
    justify-content: center; font-size: 13.5px;
  }
  .toast.is-on { transform: translateY(0); }
  .toast .toast__do { margin-left: 6px; min-height: 32px; }

  /* Ящик «Ещё»: разделы, которые и так стоят в нижнем меню, не повторяем
     (владелец, 28.09.2026). Остальное прижато к низу — ближе к пальцу. */
  .rail__nav .navitem[data-view="today"], .rail__nav .navrow,
  .rail__nav .navitem[data-view="homework"], .rail__nav .navitem[data-view="money"],
  .rail__nav .rail__label:first-of-type { display: none; }
  .rail__nav { margin-top: auto; }
}

/* Оплаты: ученики, у которых всё закрыто, свёрнуты за одной строкой. */
.paidfold { margin-top: 10px; }
.paidfold > summary {
  cursor: pointer; list-style: none; padding: 12px 14px; border: 1px dashed var(--line-2);
  border-radius: var(--r-card); color: var(--text-2); font-size: 13px; font-weight: 500;
}
.paidfold > summary::-webkit-details-marker { display: none; }
.paidfold > summary:hover { border-color: var(--accent); color: var(--accent); }
.paidfold[open] > summary { margin-bottom: 10px; }

/* Кнопка-ссылка («Звонок») не подчёркивается, как и соседние кнопки. */
a.btn, a.btn:hover { text-decoration: none; }

/* Имя ученика в карточке занятия — кнопка, но выглядит как раньше. */
button.lesson__who { background: none; border: 0; padding: 0; text-align: left; cursor: pointer; font: inherit; color: inherit; }
button.lesson__who:hover .lesson__name { color: var(--accent); }
button.lesson__who:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; border-radius: 6px; }

/* Фильтр оплат — над карточками, рядом с месяцем. */
.mscope { display: flex; justify-content: flex-end; margin-bottom: 10px; }
.mscope select { min-height: 40px; padding: 0 28px 0 10px; border: 1px solid var(--line); border-radius: var(--r-ctl); background: var(--surface); color: var(--text); font: 500 12.5px var(--ui); }
.paycard__acts { display: flex; gap: 8px; margin-left: auto; }
.paycard__head .paycard__acts .btn { margin-left: 0; }

.lbl-short { display: none; }
/* Диаграммы: подписи не ломаются на две строки. */
.viz__cap span { white-space: nowrap; }
/* Длинная подсказка в подписи графика переносится, а не распирает карточку. */
.viz__cap .viz__wrap { white-space: normal; }
.builder { scroll-margin-top: 64px; }

/* ── телефон: правки по аудиту 27.09.2026 ─────────────────────────────── */
@media (max-width: 760px) {
  /* Плитки показателей — одной строкой, без подсказок и полосок: три плитки
     столбиком занимали весь первый экран «Сегодня». */
  .kpis { grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 8px; padding-bottom: 14px; }
  .kpi { padding: 10px 10px 9px; gap: 4px; }
  .kpi__hint, .kpi__viz, .kpi__delta, .kpi__unit { display: none; }
  /* Не моноширинным и не капсом: на Android системный моноширинный широкий
     и рваный, подпись ложилась в две строки, а «22 500 ₽» переносило рубль
     на новую (владелец, 28.09.2026: «вырвиглазные шрифты»). */
  .kpi__value { font: 650 20px/1 var(--ui); letter-spacing: -.02em; white-space: nowrap; font-variant-numeric: tabular-nums; }
  /* Плитка на телефоне — треть экрана: «152 200 ₽» в 20 px вылезал за край. */
  .kpi__value--money { font-size: clamp(15px, 4.4vw, 20px); }
  .kpi__label { font: 500 11px/1.2 var(--ui); letter-spacing: 0; text-transform: none; color: var(--text-2); padding-right: 14px; }
  .kpi__row { flex-wrap: nowrap; }
  #todaySub .mono { font-family: var(--ui); }
  /* Имя на плашке недели переносится по слогам, а не рвётся посреди слова. */
  .ev b { hyphens: auto; -webkit-hyphens: auto; overflow-wrap: anywhere; line-height: 1.1; }

  /* «Оплата по ссылке» на телефоне съедала экран: пояснение, длинная ссылка с
     подписанной кнопкой и по две строки на каждое поступление (владелец,
     28.09.2026). Пояснение спрятано, кнопка — значком, поступление — одной
     строкой «сумма · дата · ученик»; почта плательщика — в подсказке и у
     непривязанных, где она и нужна. */
  .paylink__s { display: none; }
  .paylink__head { margin-bottom: 4px; }
  .paylink__link { margin-top: 6px; }
  .paylink__link .btn { font-size: 0; gap: 0; padding: 0 12px; min-width: 40px; }
  .paylink__link .btn svg { width: 16px; height: 16px; }
  .paylink .paylist { margin-top: 8px; }
  .plrow { display: grid; grid-template-columns: auto auto minmax(0, 1fr); gap: 4px 10px; padding: 8px 0; align-items: center; }
  .plrow__sum, .plrow__at { min-width: 0; }
  .plrow .chip, .plrow__pick { grid-column: 3; justify-self: end; }
  .plrow__who { display: none; }
  .plrow--wait .plrow__who { display: block; grid-column: 1 / -1; font-size: 12px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
  .plrow--wait .plrow__pick { grid-column: 1 / -1; justify-self: stretch; width: 100%; min-height: 40px; }
  .view[data-view="today"] .sectionhead__hint { display: none; }
  /* Шапка «Заданий» и «Материалов» занимала треть первого экрана. */
  .view[data-view="homework"] .pagehead__sub, .view[data-view="materials"] .pagehead__sub { display: none; }
  /* Оплаты: строка занятия в две строки, «не отмечено» — настоящая кнопка. */
  .payrow { display: flex; flex-wrap: wrap; align-items: center; gap: 4px 10px; }
  .payrow__x { flex: 1 0 100%; order: 3; white-space: normal; }
  .payrow__m { margin-left: auto; }
  .payrow__mark {
    border: 1px solid var(--line); border-radius: 8px; padding: 6px 10px; min-height: 36px;
    background: var(--surface); text-transform: none; letter-spacing: 0; font: 500 12px var(--ui);
  }
  .paycard__acts { flex: 1 0 100%; margin-left: 0; }
  .payline { flex-wrap: wrap; }
  /* Сценарии агента: тумблер остаётся в строке заголовка, списки по 40 px. */
  .rule__top { flex-wrap: nowrap; }
  .rule__title { flex: 1 1 auto; min-width: 0; }
  .rule__ctrls { flex: 0 0 auto; margin-left: 8px; }
  /* Подпись и список в одну строку, а не влезли — список уходит на свою
     строку целиком: «Засчитывать сразу» иначе ломался на две строки внутри
     плашки (28.09.2026). */
  .rule__pset { min-height: 40px; flex-wrap: wrap; }
  .rule__param { white-space: nowrap; max-width: 100%; }
  /* Конструктор на узком экране. */
  .builder { padding: 12px; }
  .task { padding: 10px; }
  .task__text { overflow-wrap: anywhere; }
  /* Конструктор на телефоне: номер, вид проверки и корзина — одной строкой;
     эталон и баллы — одной; подписи короткие (владелец, 28.09.2026: «много
     места по вертикали, а поля небольшие»). */
  .builder__head { gap: 6px 10px; margin-bottom: 10px; }
  .builder__acts { margin-left: 0; }
  .hwlink { padding: 8px 10px; gap: 6px 8px; margin-bottom: 10px; }
  .hwlink .btn { font-size: 0; gap: 0; min-width: 40px; padding: 0 10px; }
  .hwlink .btn svg { width: 16px; height: 16px; }
  .task { grid-template-columns: 20px minmax(0, 1fr); gap: 8px; padding: 10px; }
  .task__n { line-height: 40px; }
  .task__b { gap: 6px; }
  .task__toolbar { flex-wrap: nowrap; }
  .task__mode { flex: 1 1 auto; min-width: 0; max-width: none; }
  .task__del { margin-left: 0; flex: 0 0 auto; }
  .task__text { min-height: 68px; }
  .task__picadd { margin-top: 0; }
  .task__ans { display: grid; grid-template-columns: auto minmax(0, 1fr) auto; gap: 6px 8px; align-items: center; }
  .task__ans > label { grid-column: 1; grid-row: 1; white-space: nowrap; }
  .task__ans > input { grid-column: 2; grid-row: 1; width: 100%; min-height: 40px; }
  .task__ans > .chip { grid-column: 1 / -1; grid-row: 2; justify-self: start; }
  .task__pts { grid-column: 3; grid-row: 1; margin-left: 0; }
  .task__pts input { width: 48px; min-height: 40px; }
  .lbl-long { display: none; }
  .lbl-short { display: inline; }
  /* Полезная ширина: поля страницы и карточек на телефоне были по 24 и 16 px
     с каждой стороны — 80 px из 390 уходили в воздух (владелец, 28.09.2026:
     «очень жирные боковые границы у блоков до краёв экрана»). */
  .view { padding: 12px 10px 56px; }
  .view[data-view="schedule"].is-active { padding-bottom: 8px; }
  .pagehead { margin-bottom: 10px; }
  .paycard { padding: 12px; margin-bottom: 10px; }
  .fevent { padding: 10px 12px; }
  .kpi { padding: 9px 9px 8px; }
  .hwlink { padding: 8px; }
  .builder { padding: 10px 8px; }
  .task { padding: 9px 8px; }
  .task__review { flex-wrap: wrap; }
  .chk__head > * { max-width: 100%; }
  .sheetbar .shot { width: auto; }
  .hint--kbd { display: none; }
}
/* Лента помощника прокручивается сама, а не тянет за собой страницу. */
.helper__log { overscroll-behavior: contain; }
/* Оглавление профиля: заголовок раздела не прячется под шапкой. */
.profile__stack > * { scroll-margin-top: 70px; }

/* Проверка работы — свой экран конструктора: условие, ответ ученика, разбор
   судей и балл кнопками (владелец, 07.10.2026). Размеры под палец — здесь
   же, в конце файла: при равной специфичности побеждает объявленное последним. */
.review__sum { display: flex; align-items: baseline; gap: 12px; flex-wrap: wrap; padding: 8px 0 6px; font-size: 13px; color: var(--text-2); }
.review__sum b { font-size: 20px; color: var(--text); font-variant-numeric: tabular-nums; }
.rtask { display: flex; gap: 10px; padding: 14px 0; border-top: 1px solid var(--line); }
.rtask__n {
  flex: 0 0 26px; height: 26px; display: grid; place-items: center; border-radius: 7px;
  background: var(--surface-2); font-size: 12.5px; font-weight: 600; font-variant-numeric: tabular-nums;
}
.rtask__b { flex: 1 1 auto; min-width: 0; }
.rtask__head { display: flex; gap: 8px; align-items: center; flex-wrap: wrap; margin-bottom: 6px; }
.rtask__kind { font-size: 11px; color: var(--text-3); }
.rtask__text { margin: 0 0 6px; font-size: 13px; line-height: 1.5; white-space: pre-wrap; overflow-wrap: anywhere; }
.rtask__pic img {
  display: block; max-width: 100%; max-height: 380px; border-radius: var(--r-ctl);
  border: 1px solid var(--line); background: #fff; cursor: zoom-in;
}
.rtask__ans { margin: 6px 0 0; font-size: 12.5px; display: flex; gap: 8px; align-items: baseline; flex-wrap: wrap; }
.rtask__ans span { color: var(--text-3); font-size: 10.5px; text-transform: uppercase; letter-spacing: .06em; }
.rtask__ans b { font-family: var(--mono); font-size: 13px; overflow-wrap: anywhere; }
.rtask__ans i { color: var(--text-3); }
.rtask__score { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; margin-top: 10px; }
.rtask__lbl, .rtask__of { font-size: 11.5px; color: var(--text-3); }
.spicks { display: inline-flex; gap: 4px; flex-wrap: wrap; }
.spick {
  min-width: 36px; height: 32px; padding: 0 8px; border-radius: 8px;
  border: 1px solid var(--line); background: var(--surface); color: var(--text-2);
  font: inherit; font-size: 13px; font-weight: 600; cursor: pointer; font-variant-numeric: tabular-nums;
}
.spick:hover { border-color: var(--accent); color: var(--text); }
.spick.is-on { background: var(--accent); border-color: var(--accent); color: var(--on-accent); }
.spick__in {
  width: 64px; padding: 5px 8px; font: inherit; font-size: 13px; color: var(--text);
  background: var(--surface); border: 1px solid var(--line); border-radius: var(--r-ctl);
}
@media (pointer: coarse) {
  .spick { min-width: 44px; height: 42px; }
  .spick__in { min-height: 42px; font-size: 16px; }
}

/* Автозаполнение браузера. Chrome и Safari красят подставленное поле своей
   заливкой и пишут текст ЧЁРНЫМ — в тёмной теме имя и почту на регистрации
   было не прочитать (владелец, 10.10.2026). Текст — цветом темы; фон поля
   остаётся своим: браузерная заливка «наступает» за сутки, то есть никогда.
   :root в селекторе — чтобы перебить переходы полей вроде .field input. */
:root input:-webkit-autofill,
:root input:-webkit-autofill:hover,
:root input:-webkit-autofill:focus,
:root textarea:-webkit-autofill,
:root select:-webkit-autofill {
  -webkit-text-fill-color: var(--text);
  caret-color: var(--text);
  transition: background-color 86400s 0s, color 86400s 0s, border-color .12s, box-shadow .12s;
}
:root input:autofill { filter: none; }

/* Число, которое требует внимания (долг в «Сводке»). */
.tone-warn { color: var(--warn); }
