/* Единственный файл стилей кабинета. Вид — макета партнёра-дизайнера:
   эталон docs/design/reference/2026-10-28-prepatools-mock.html (версия
   105, ссылки «макет v105:N»); «макет:N» — строки прежней версии 58,
   docs/design/reference/2026-10-26-prepatools-mock.html. Правила — спеки
   docs/superpowers/specs/2026-10-26-studio-redesign-design.md §6.1.1 и
   §6.1.11 и 2026-10-29-mock-v105-design.md, стражи —
   tests/test_web_design.py. Цвета объявлены ниже в :root, вне него
   литералы цвета запрещены. Длины пишутся литералами, как в макете. */
:root {
  /* цвет — макет:12-17 и литералы его правил */
  --bg: #F3F3F3;
  --panel: #FFFFFF;
  --soft: #F6F6F5;
  --soft2: #EEEEEC;
  --ink: #111111;
  --ink2: #5E5E5E;
  --ink3: #9A9A9A;
  --ink4: #C9C9C9;
  --ink35: #B3B3B3;
  --ink-h: #2B2B2B;
  --on: #FFFFFF;
  --on-dim: rgba(255, 255, 255, .65);
  --green: #52AD86;
  --green-d: #3F9670;
  --green-track: rgba(82, 173, 134, .2);
  --green-spin: rgba(82, 173, 134, .3);
  --mint-t: #E9F7EF;
  --green-text: #2E7D5B;
  --orange-t: #FDF0DD;
  /* число дней полоски ученика меньше трёх (макет v105:16, 519) */
  --orange-d: #E8862A;
  --orange-text: #A35A0E;
  /* клетки «Успеваемости» (макет:392-393); третий уровень — --green */
  --st0: rgba(82, 173, 134, .1);
  --st1: rgba(82, 173, 134, .32);
  --st2: rgba(82, 173, 134, .6);
  --red: #E9584B;
  --red-d: #C9483C;
  --red-text: #B23A2F;
  --red-t: #FCE9E7;
  --ring-field: 0 0 0 1.5px var(--green), 0 0 0 4px rgba(82, 173, 134, .15);
  --shadow-pop: 0 0 0 1px rgba(0, 0, 0, .05), 0 8px 24px rgba(0, 0, 0, .08);
  /* строка карточки: пилюля действий (макет:193), рамка правки (200) */
  --shadow-acts: 0 0 0 1px rgba(0, 0, 0, .05), 0 2px 6px rgba(0, 0, 0, .05);
  --shadow-edit: 0 0 0 1.5px var(--green), 0 2px 8px rgba(0, 0, 0, .05);
  --scrim: rgba(20, 20, 20, .18);
  --shadow-dock: 0 4px 12px rgba(0, 0, 0, .12);
  /* пузырь подсказки клетки дня (макет v105:510) */
  --shadow-tip: 0 2px 6px rgba(0, 0, 0, .1);
  --line: rgba(0, 0, 0, .06);
  --line2: rgba(0, 0, 0, .1);
  --line3: rgba(0, 0, 0, .2);
  --fill: rgba(0, 0, 0, .05);
  --shadow: 0 1px 2px rgba(0, 0, 0, .04), 0 2px 6px rgba(0, 0, 0, .04);
  --mint: #BCE8D1;
  --orange: #F2A03D;
  /* точки типов карточки, которых у макета нет (макет:18), и запасная (1065) */
  --violet: #8B7CF6;
  --blue: #5B8DEF;
  /* уроки и выходные в сетке «3 дня» и «Неделя» (макет v105:18-19, 600) */
  --violet-t: #EFEDFE;
  --blue-t: #E8EFFD;
  --weekend: #FBFBFA;
  /* календарь прототипа (oct29-Prepa.Tools.html:592, 594, 596, 602, 567) */
  --mint-col: rgba(188, 232, 209, .14);
  --shadow-wk: inset 0 0 0 1px rgba(0, 0, 0, .04);
  --shadow-wk-h: 0 2px 6px rgba(0, 0, 0, .07);
  --shadow-drag: 0 0 0 2px var(--ink), 0 6px 16px rgba(0, 0, 0, .1);
  --shadow-seg: 0 1px 3px rgba(0, 0, 0, .08);
  --type-other: #999;
  /* левая колонка — литералы макета:51-97 и скрипта макета:865, 879 */
  --shadow-h: 0 1px 2px rgba(0, 0, 0, .04), 0 4px 10px rgba(0, 0, 0, .06);
  --heat0: rgba(82, 173, 134, .12);
  --heat1: rgba(82, 173, 134, .3);
  --heat2: rgba(82, 173, 134, .55);
  --fill2: rgba(0, 0, 0, .09);
  --hover: rgba(0, 0, 0, .035);
  --shadow-day: inset 0 0 0 2px var(--mint), 0 0 0 4px rgba(188, 232, 209, .35);
  --shadow-ev: 0 0 0 1px rgba(0, 0, 0, .06);
  --shadow-ev-h: 0 0 0 1px rgba(0, 0, 0, .07), 0 2px 8px rgba(0, 0, 0, .06);
  --shadow-ev-on: 0 0 0 2px var(--green);

  /* шрифт — Inter из репозитория (макет:23); длины в :root не живут */
  --font: "Inter", "Inter Fallback", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
}

/* == шрифт == */
@font-face {
  font-family: "Inter";
  src: url("fonts/InterVariable.woff2") format("woff2");
  font-weight: 100 900;
  font-style: normal;
  font-display: swap;
}
/* Запасной шрифт с метриками Inter: текст не прыгает, когда файл доезжает. */
@font-face {
  font-family: "Inter Fallback";
  src: local("Arial");
  size-adjust: 107%;
  ascent-override: 90%;
  descent-override: 22%;
  line-gap-override: 0%;
}

/* == база == */
*, *::before, *::after { box-sizing: border-box; }
html {
  color-scheme: only light;
  font-optical-sizing: none;
  -webkit-text-size-adjust: 100%;
}
/* Фон — у тела: у корня своего нет, и канва берёт фон тела на всё окно. */
body {
  margin: 0;
  font: 400 13px/1.45 var(--font);
  color: var(--ink);
  background: var(--bg);
  -webkit-font-smoothing: antialiased;
  letter-spacing: -0.005em;
  overflow-x: clip;
}
/* Длинное имя или тема без пробелов переносится, а не раздвигает центр:
   правило вида не задаёт. */
h1, h2, p, li { overflow-wrap: anywhere; }
a {
  color: inherit;
  text-decoration: underline;
  text-decoration-color: var(--line3);
  text-underline-offset: .2em;
}
a, button { touch-action: manipulation; }
button, input, select, textarea { font: inherit; color: inherit; letter-spacing: inherit; }
button { cursor: pointer; }
::placeholder { color: var(--ink4); opacity: 1; }
a:hover { text-decoration-color: currentColor; }
:target { scroll-margin-top: 16px; }
:focus-visible { outline: 2px solid var(--green); outline-offset: 2px; }
:focus:not(:focus-visible) { outline: none; }
.pre { white-space: pre-wrap; }
.muted { color: var(--ink3); }

/* == btn == */
/* Кнопки макета:466-475. Наведение — своё у каждого вида: общее
   `.btn:hover` перекрасило бы красную кнопку подтверждения, которую макет
   при наведении не меняет. Кнопкой бывает и ссылка — без подчёркивания. */
.btn { height: 32px; padding: 0 14px; border-radius: 10px; border: 0; background: var(--soft); display: inline-flex; align-items: center; justify-content: center; gap: 6px; font-weight: 500; white-space: nowrap; text-decoration: none; }
.btn-secondary:hover { background: var(--soft2); }
.btn-primary { background: var(--ink); color: var(--on); }
.btn-primary:hover { background: var(--ink-h); }
.btn-accent { background: var(--green); color: var(--on); }
.btn-accent:hover { background: var(--green-d); }
.btn-danger { background: var(--red); color: var(--on); }
.btn-quiet, .btn-danger-quiet { background: none; }
.btn-quiet:hover, .btn-danger-quiet:hover { background: var(--soft); }
.btn-icon { width: 32px; padding: 0; }
.btn-small { height: 28px; padding: 0 10px; font-size: 12px; border-radius: 9px; }
.btn-danger-quiet { color: var(--red-d); }
.btn:disabled { opacity: .4; pointer-events: none; }
.btn[hidden] { display: none; }

/* == icon, logo == */
/* Размер и контур задаёт класс; `stroke-width="2.6"` у точек «ещё» (макет:552) — атрибут фигуры и сильнее унаследованного 1.6. */
.icon { width: 16px; height: 16px; flex: none; fill: none; stroke: currentColor; stroke-width: 1.6; stroke-linecap: round; stroke-linejoin: round; }
.icon-s { width: 13px; height: 13px; }
.logo-mark { width: 18px; height: 18px; color: var(--ink); flex: none; display: block; transform-origin: 50% 85%; transition: transform .3s ease; }

/* == персонаж и буква преподавателя == */
/* Правила макета: .av (117), размеры (119, 126-127, 138), .me-av (100). Размер ставит класс av-*; фигура — картинка модели. */
.av { flex: none; display: inline-grid; place-items: center; position: relative; overflow: visible; }
/* Модель вписана в коробку размера целиком: картинка — по краям обёртки,
   иначе её высота не считается от сетки и фигура вылезает из рамки. */
.av img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: contain; display: block; }
.av-xs { width: 20px; height: 20px; margin-right: 2px; }
.av-s { width: 24px; height: 24px; }
.av-m { width: 34px; height: 34px; }
.av-l { width: 48px; height: 48px; }
.av-xl { width: 72px; height: 72px; }
.me-av { width: 32px; height: 32px; border-radius: 50%; background: var(--ink); color: var(--on); display: grid; place-items: center; font-weight: 600; font-size: 13px; flex: none; }

/* == предупреждение: коробка `.parsing`, цвета `.parsing-stale` (задача 22) == */
.caution { padding: 16px; border-radius: 16px; background: var(--orange-t); color: var(--orange-text); }

/* == тост: .banner макета v105:656-660 — вверху по центру окна; кнопки действия у этапа 1 нет == */
.banner { position: fixed; left: 50%; top: calc(6px + env(safe-area-inset-top, 0px)); transform: translateX(-50%); z-index: 80; display: flex; align-items: center; gap: 12px; padding: 8px 8px 8px 16px; border-radius: 14px; background: var(--ink); color: var(--on); box-shadow: var(--shadow-pop); animation: up .2s ease; max-width: calc(100% - 32px); }
.banner[hidden] { display: none; }
@keyframes up { from { opacity: 0; transform: translate(-50%, -8px); } }
.banner .tx { font-size: 12.5px; }
.banner .tx span { color: var(--on-dim); margin-left: 6px; }
.banner.err { background: var(--red-text); }

/* == поповер и меню: макет:483-484, 494-498, 501-503 == */
/* Отличия от макета. Поповер — серверная разметка в своей части, а не в
   `#layer` у `body`: без скрипта он стоит по центру окна (`inset: 0`,
   `height: fit-content`, `margin: auto` — решение плана 5), а шрифт и
   цвет берёт сам (у `.left-foot` 12px и серый). Поставленный на место
   (`data-placed`) центр отпускает. Пункт бывает ссылкой — без
   подчёркивания; колонка галочки — `::before`, а не `.mi .ck2`;
   `.mi.danger` — `.mi-danger`, черта — `<hr>` без рамки. `display` у
   `.pop` нет: прячет его атрибут `hidden`. */
.pop { position: fixed; inset: 0; z-index: 60; width: 320px; max-width: calc(100vw - 16px); height: fit-content; max-height: calc(100vh - 16px); margin: auto; overflow: auto; overscroll-behavior: contain; background: var(--panel); border-radius: 18px; box-shadow: var(--shadow-pop); padding: 16px; font-size: 13px; color: var(--ink); animation: popin .14s ease; }
@keyframes popin { from { opacity: 0; transform: scale(.97); } }
.pop[data-placed] { inset: auto; margin: 0; }
.pop.menu { width: 230px; padding: 6px; border-radius: 14px; }
.pop.menu.wide { width: 320px; }
.mi { display: flex; align-items: center; gap: 8px; width: 100%; height: 32px; padding: 0 10px; border: 0; border-radius: 9px; background: none; text-align: left; text-decoration: none; }
.mi[hidden] { display: none; }
.mi::before { content: ""; width: 14px; flex: none; }
.mi:hover, .mi:focus-visible { background: var(--soft); outline: none; }
.mi-tx { flex: 1; min-width: 0; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.mi-danger { color: var(--red-d); }
.msep { height: 1px; margin: 4px 8px; border: 0; background: var(--line); }
/* Форма в поповере (макет:485-490): `.fr` — `.field` (колонку и зазор
   5px он уже даёт), ошибка — `.p-hint` цветом ошибки: в макете её нет. */
.pop h3 { margin: 0 0 4px; font-size: 14px; font-weight: 600; }
.pop .field { margin: 10px 0 0; }
/* Дата и время урока в одном ряду (спека регулярных уроков §5.2): два
   `.field` рядом, у ряда отступ, который у поля — свой. На 560 поля
   форм 16px и в 140px дата не умещается — ряд встаёт в одну колонку. */
.pop .pair { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); gap: 8px; margin: 10px 0 0; }
.pop .pair .field { margin: 0; }
@media (max-width: 560px) { .pop .pair { grid-template-columns: minmax(0, 1fr); gap: 10px; } }
.pop .p-error { margin: 4px 0 0; font-size: 12px; line-height: 1.45; color: var(--red-d); }
.pop .acts { display: flex; align-items: center; justify-content: flex-end; gap: 8px; margin-top: 16px; }
.mi[aria-checked="true"]::before { content: "✓"; color: var(--green); }
.mi-tx b { font-weight: 500; margin-right: 6px; }
.mi-tx small { font-size: 12px; color: var(--ink3); }
.pop .p-hint { margin: 4px 0 0; font-size: 12px; color: var(--ink3); line-height: 1.45; }
/* Выбор в поповере (макет:491-492) — тот же `.ck`, что у подтверждения
   ниже. Строка регулярных уроков: флажок дня слева, время справа, обе
   половины на одной линии. */
.pop .ck { display: flex; align-items: flex-start; gap: 8px; margin-top: 10px; font-size: 12.5px; }
.pop .ck input { accent-color: var(--green); margin-top: 2px; }
.tt-row { display: flex; align-items: center; justify-content: space-between; gap: 12px; margin-top: 8px; }
.tt-row .ck { flex: 1; min-width: 0; align-items: center; margin-top: 0; }
.tt-row .ck input { margin: 0; }
.tt-row .field-input { flex: none; width: 140px; }
/* == подтверждение со скримом (макет:504-508; выбор — .pop .ck
   макета:492-493). Без скрипта стоит так же — по центру. == */
.scrim { position: fixed; inset: 0; z-index: 70; background: var(--scrim); display: grid; place-items: center; padding: 16px; }
.scrim[hidden] { display: none; }
.alert { width: 320px; background: var(--panel); border-radius: 20px; box-shadow: var(--shadow-pop); padding: 22px; }
.alert h3 { margin: 0; font-size: 15px; font-weight: 600; }
.alert p { margin: 6px 0 0; color: var(--ink2); line-height: 1.5; }
.alert .ck { display: flex; align-items: flex-start; gap: 8px; margin-top: 10px; font-size: 12.5px; }
.alert .ck input { accent-color: var(--green); margin-top: 2px; }
.alert .acts { display: flex; justify-content: flex-end; gap: 8px; margin-top: 18px; }
/* Страница удаления без скрипта (§6.1.5): та же плашка в центре, заголовок
   страницы — `h1` с видом `.alert h3` (макет:506). */
.alert-page { margin: 40px auto 0; }
.alert-page h1 { margin: 0; font-size: 15px; font-weight: 600; }

/* == док: «Новый урок» внизу центра (макет:259-263, 529, 538) == */
/* Отличия от макета. `.dock-b` — это `.dock .btn` вида `primary`: фон
   `--ink`, белый текст и наведение `--ink-h` уже даёт `btn-primary`
   (макет:262-263; правило 260-261 макет перекрывает сам). По центру —
   `width: max-content; margin: 0 auto` вместо `left: 50%; transform`:
   якорное позиционирование поповера не учитывает `transform` якоря.
   Значок 16px, как `ic('i-edit')` макета:905. `.dock-rec` нет до этапа 5.
   До 980px док стоит у низа окна (макет v105:676-677) тем же центрированием,
   без `transform`; запас 84px снизу — только у `.app` с доком, на 560px его
   держит специфичность `:has`. */
.dock { position: absolute; left: 0; right: 0; bottom: 18px; width: max-content; margin: 0 auto; display: flex; gap: 8px; z-index: 5; }
.dock .btn { height: 46px; gap: 8px; padding: 0 20px; border-radius: 16px; box-shadow: var(--shadow-dock); }
.dock .icon { width: 16px; height: 16px; }

/* Место у кнопки — CSS anchor positioning, где браузер его знает (спека
   §6.1.7); стороны и отступы — арифметика placePop() макета:1194-1198.
   Правила включает `data-anchored`, который ставит скрипт; без него
   место — `left`/`top` скрипта. `margin-inline: 8px` у сторон по центру
   кнопки — прижим к краю окна 8px, как `Math.max(8, left)` макета:1198:
   без него браузер прижал бы широкий поповер к самому краю. У `down-end`
   и `down-start` его нет: он сдвинул бы край поповера с края кнопки. */
@supports (position-area: top) {
  [data-anchor] { anchor-name: --pop; }
  .pop[data-anchored] { position-anchor: --pop; position-try-fallbacks: flip-block, flip-inline; }
  .pop[data-anchored][data-placed="down"] { position-area: bottom; margin-top: 8px; margin-inline: 8px; }
  .pop[data-anchored][data-placed="down-end"] { position-area: bottom span-left; margin-top: 8px; }
  .pop[data-anchored][data-placed="down-start"] { position-area: bottom span-right; margin-top: 8px; }
  .pop[data-anchored][data-placed="up"] { position-area: top; margin-bottom: 10px; margin-inline: 8px; }
  .pop[data-anchored][data-placed="right"] { position-area: right span-bottom; margin-left: 12px; margin-top: -8px; }
}

/* == оболочка == */
/* Три области макета (макет:22, 34, 37-39, 141-147, 402-408, 517-534).
   Скрытое расписание — атрибут `hidden` на `aside` и `:has` вместо класса
   `.cal-off`: скрипт классов не трогает. Ручки ширины нет (§6.1.9),
   поэтому 300px литералом вместо `--rw`. `.c-top .ctx:hover` (макет:144)
   приносит студия для ссылки `a.ctx`. Не переносится `.right { position:
   relative; overflow: visible }` (макет:414): он для ручки ширины. Правила виджетов, дока, карточек и
   заголовков из тех же брейкпоинтов приносят их страницы. */
html, body { height: 100%; }
.app { height: 100%; display: grid; grid-template-columns: 330px minmax(0, 1fr) var(--rw, 300px); gap: 12px; padding: 14px; }
.app:has(> .right[hidden]) { grid-template-columns: 330px minmax(0, 1fr); }
.panel { background: var(--panel); border-radius: 22px; min-height: 0; display: flex; flex-direction: column; overflow: hidden; }
/* Поля `.left-in` нужны прокрутке колонки: без них она обрезала бы тени
   виджетов. Отрицательные поля колонки их возвращают — контент стоит в
   14px от окна и в 12px от центра, как края панелей центра и расписания. */
.left { background: transparent; box-shadow: none; overflow: visible; margin: -14px -12px -14px -12px; }
/* `position: relative` — своё: скрытые подписи точек (`visually-hidden`) держатся
   внутри прокрутки колонки, а не листают страницу. */
.left-in { flex: 1; min-height: 0; overflow: auto; padding: 14px 12px 0; display: flex; flex-direction: column; position: relative; }
.center { position: relative; container-type: inline-size; }
.c-top { height: 56px; flex: none; display: flex; align-items: center; gap: 8px; padding: 0 16px 0 18px; }
.c-top .ctx { display: inline-flex; align-items: center; gap: 6px; height: 30px; padding: 0 10px; border-radius: 10px; border: 0; background: none; color: var(--ink2); font-size: 13px; min-width: 0; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
/* Своё: длинное имя ученика в переключателе уроков обрезается, а не
   выталкивает «…» и крестик за край центра. */
.ctx-tx { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.c-top .sp { flex: 1; }
/* Стрелка «Назад» (макет v105:192-196). Узкий центр — запрос к контейнеру
   `.center` (макет v105:197-200). Контейнер не делает центр опорой для
   `position: fixed`: поповеры, подтверждение, тост и док до 980px стоят от
   окна (спека 2026-10-29 §3.4). */
.nav-back { display: inline-flex; align-items: center; gap: 4px; height: 30px; max-width: 180px; padding: 0 10px 0 6px; margin-left: -6px; border-radius: 10px; color: var(--ink2); font-size: 13px; text-decoration: none; flex: none; }
.nav-tx { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.nav-back:hover { background: var(--soft); color: var(--ink); }
.nav-back .icon { width: 14px; height: 14px; }
.nav-sep { width: 1px; height: 16px; background: var(--line2); flex: none; margin: 0 2px; }
@container (max-width: 560px) {
  .nav-back { padding-right: 6px; }
  .nav-tx, .nav-sep { display: none; }
}
.c-body { flex: 1; min-height: 0; overflow: auto; padding: 0 40px 110px; }
.c-inner { max-width: 760px; margin: 0 auto; }
.r-top { padding: 0 10px 0 14px; }
/* У `.panel` есть `display: flex`: без этого правила атрибут `hidden`
   панель не спрятал бы. */
.right[hidden] { display: none; }
/* Контейнер вида расписания и опора ручки ширины (макет v105 §11.3, 11.6). */
.right { container: sched / inline-size; position: relative; }
/* Своё: экрана входа у макета нет. Плашка стоит по центру серого фона
   (§4), поле — как у `.app`. */
.solo { min-height: 100vh; display: grid; align-content: center; justify-items: center; padding: 14px; }
@media (max-width: 1180px) { .app { grid-template-columns: 290px minmax(0, 1fr) var(--rw, 300px); } .app:has(> .right[hidden]) { grid-template-columns: 290px minmax(0, 1fr); } .c-body { padding: 0 24px 110px; } }
/* `.app:has(…)` специфичнее `.app`: без него скрытое расписание оставило
   бы на узком экране две колонки. `minmax(0, 1fr)` вместо `1fr` макета:
   имя ученика без пробелов не раздвигает колонку шире окна. */
@media (max-width: 980px) {
  html, body { height: auto; }
  .app, .app:has(> .right[hidden]) { grid-template-columns: minmax(0, 1fr); height: auto; }
  .left { margin: 0; }
  .left-in { overflow: visible; padding: 0; }
  .center { min-height: 80vh; }
  .c-body { overflow: visible; }
  .right { max-height: 640px; }
  .dock { position: fixed; bottom: calc(12px + env(safe-area-inset-bottom, 0px)); z-index: 50; }
  .app:has(.dock) { padding-bottom: 84px; }
}
@media (max-width: 560px) {
  .app { padding: 8px; }
  .c-body { padding: 0 16px 24px; }
  /* Макет:538 прячет `span` подписи, у нас подпись — текст кнопки: вид
     тот же (56×46), читалка подпись слышит. */
  .dock .btn { font-size: 0; gap: 0; }
}

/* == студия: верхняя строка и тема (макет:120-122, 127, 144, 161-163;
   padding-top — инлайн макета:923). Статуса записи в макете нет: тихий,
   серый, красный — когда не сохранено (§6.1.5). == */
/* Отличия от макета. Тона чипа — токенами вместо литералов `#A35A0E` и
   `#B23A2F`; языковых `.chip.fr/.en/.es/.ma` (макет:121) нет — языков у
   учеников нет. Наведение — только у ссылки `a.ctx`: `span.ctx` правой
   панели и страницы ученика — неподвижные подписи. */
.chip { display: inline-flex; align-items: center; gap: 5px; height: 22px; padding: 0 9px; border-radius: 11px; background: var(--soft); color: var(--ink2); font-size: 11.5px; white-space: nowrap; }
.chip.warn { background: var(--orange-t); color: var(--orange-text); }
.chip.ok { background: var(--mint-t); color: var(--green-text); }
.chip.off { background: var(--red-t); color: var(--red-text); }
a.chip { text-decoration: none; }
.c-top .chip { margin-left: 4px; }
/* чип не сжимается в узком центре (макет v105:198) */
.c-top > .chip { flex: none; }
.c-top a.ctx { text-decoration: none; }
.c-top a.ctx:hover { background: var(--soft); color: var(--ink); }

/* == ученик: шапка (макет:237-239; поля персонажа — .av-btn
   макета:129, выбора персонажа до этапа 3 нет) == */
.s-head { display: flex; align-items: center; gap: 16px; margin: 8px 0 24px; }
.s-head .av { margin: 4px; }
/* Подсветка — квадрат по большей стороне: 72px модели и по 6px полей.
   Сетка вместо строчного блока: высоту не добавляет межстрочный зазор. */
.s-face { display: grid; place-items: center; width: 84px; height: 84px; flex: none; border-radius: 16px; transition: background .15s; }
.s-face:hover { background: var(--hover); }
/* Сетка выбора персонажа — `.chars` макета v105:176-181, пять моделей в ряд. */
.faces { display: grid; grid-template-columns: repeat(5, 1fr); gap: 5px; margin-top: 10px; }
.face-pick { display: grid; place-items: center; aspect-ratio: 1; min-width: 0; padding: 5px; overflow: hidden; border: 0; border-radius: 12px; background: var(--soft); cursor: pointer; }
.face-pick:hover { background: var(--soft2); }
.face-pick[aria-current] { background: var(--panel); box-shadow: 0 0 0 2px var(--green); }
.s-head h1 { margin: 0; font-weight: 600; font-size: 28px; line-height: 1.1; letter-spacing: -0.025em; }
/* Своё: карандаш у имени ученика проявляется при наведении на имя и
   открывает «Изменить имя»; без наведения (палец) виден всегда. */
.s-title { display: flex; align-items: center; gap: 6px; min-width: 0; }
.s-title h1 { min-width: 0; overflow-wrap: anywhere; }
.s-title .btn { flex: none; color: var(--ink3); opacity: 0; transition: opacity .15s; }
.s-title:hover .btn, .s-title .btn:focus-visible, .s-title .btn[aria-expanded="true"] { opacity: 1; }
@media (hover: none) { .s-title .btn { opacity: 1; } }
.s-head .chips { display: flex; gap: 6px; flex-wrap: wrap; margin-top: 8px; }
/* Колонка имени и чипов сжимается до ширины экрана: иначе её ширину
   задаёт самый длинный чип. */
.s-head > div { min-width: 0; }
/* После правила шапки: в общем блоке 560px оно проиграло бы порядку.
   Чип пяти дней регулярных уроков шире телефона: на узком экране чипы
   шапки переносят строку, а не режутся. */
@media (max-width: 560px) {
  .s-head h1 { font-size: 24px; }
  .s-head .chips .chip { height: auto; min-height: 22px; padding: 3px 9px; white-space: normal; }
}
.c-top .autosave { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-size: 11.5px; color: var(--ink3); }
.c-top .autosave[data-state="failed"], .c-top .autosave[data-state="gone"], .c-top .autosave[data-state="expired"] { color: var(--red-text); }
.c-inner:has(> .l-title) { padding-top: 24px; }
.l-title { display: block; width: 100%; border: 0; background: none; font-weight: 600; font-size: 28px; line-height: 1.2; padding: 0; margin: 0 0 18px; letter-spacing: -0.025em; resize: none; overflow: hidden; height: 34px; }
.l-title::placeholder { color: var(--ink4); }
.l-title:focus { outline: none; }
/* Тема на телефоне (макет:535). Свой блок после правил раздела: в блоке
   оболочки выше её перебило бы правило `.l-title`. */
@media (max-width: 560px) { .l-title { font-size: 24px; } }

/* == расписание: календарь прототипа дизайнера (oct29-Prepa.Tools.html) == */
/* Перенос CSS прототипа: лента дней (339-349), шкала дня `.tl*` (351-369),
   «3 дня» и «Неделя» `.wk*` (570-608), ручка `.rz` и переключатель
   `.rmode` (561-569). Отличия: дни, стрелки и уроки — ссылки (отсюда
   `text-decoration: none`); позицию урока, линии «сейчас» и «призрака»
   ставит скрипт по `data-min` (`style=` в шаблонах нет), он же отмечает
   `.right[data-placed]`; без скрипта уроки дня стоят списком. Подписи и
   линии часов — блоки по 54px, а не абсолютные `top`. Классы перетаскивания
   прототипа — атрибуты `data-moving` и `data-dragging`: скрипт классов не
   добавляет. Цвет точки — классы `.flag-*` вместо `style=`. */
.days { display: flex; align-items: center; justify-content: space-between; padding: 4px 10px 10px; flex: none; }
.days > .arrow { width: 24px; height: 24px; flex: none; border-radius: 8px; color: var(--ink3); display: grid; place-items: center; }
.days > .arrow:hover { background: var(--soft); color: var(--ink); }
.day { width: 44px; height: 54px; border-radius: 22px; display: flex; flex-direction: column; align-items: center; justify-content: center; line-height: 1.1; color: var(--ink4); position: relative; text-decoration: none; }
.day b { font-size: 15px; font-weight: 500; font-variant-numeric: tabular-nums; }
.day small { font-size: 10.5px; }
.day.near { color: var(--ink2); }
.day:hover { background: var(--soft); }
.day.on { width: 64px; height: 64px; flex: none; border-radius: 50%; box-shadow: var(--shadow-day); color: var(--ink); }
.day.on b { font-size: 18px; font-weight: 600; }
.day .has { position: absolute; bottom: 6px; width: 4px; height: 4px; border-radius: 50%; background: var(--green); }
.tl-wrap { flex: 1; min-height: 0; overflow: auto; padding: 4px 14px 20px 0; position: relative; }
.tl { margin-left: 14px; }
.tl-hours { display: none; }
.tl-list { display: flex; flex-direction: column; gap: 6px; margin: 0; padding: 0; list-style: none; }
.right[data-placed] .tl { position: relative; height: 864px; }
.right[data-placed] .tl-hours { display: block; position: absolute; inset: 0; pointer-events: none; }
.right[data-placed] .tl-list { display: block; }
.right[data-placed] .tl-ev[data-min] { position: absolute; left: 0; right: 0; }
.tl-hours i { display: block; height: 54px; border-top: 1px solid var(--fill); position: relative; }
.tl-hours i:last-child { height: 0; }
.tl-hours span { position: absolute; left: 50%; top: 0; transform: translate(-50%, -50%); padding: 0 8px; background: var(--panel); font-size: 10.5px; font-style: normal; color: var(--ink4); font-variant-numeric: tabular-nums; }
.tl-ev { display: flex; align-items: center; gap: 10px; padding: 10px 12px; border-radius: 14px; background: var(--panel); box-shadow: var(--shadow-ev); min-height: 48px; text-decoration: none; }
.tl-ev:hover { box-shadow: var(--shadow-ev-h); }
.tl-ev.on { box-shadow: var(--shadow-ev-on); }
.tl-ev.past { opacity: .55; }
.tl-ev .main { flex: 1; min-width: 0; }
.tl-ev .main b { display: block; font-size: 12.5px; font-weight: 500; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.tl-ev .main span { display: block; font-size: 11px; color: var(--ink3); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.tl-ev .dot { width: 7px; height: 7px; border-radius: 50%; flex: none; }
.flag-parse { background: var(--orange); }
/* Прошедший пустой урок — оранжевый, как «нужно разобрать» (спека
   регулярных уроков §5.4). */
.flag-empty { background: var(--orange); }
.flag-drafts { background: var(--green); }
.tl-now { position: absolute; left: -2px; right: 0; height: 0; border-top: 1.5px solid var(--green); z-index: 2; pointer-events: none; }
.tl-now::before { content: ""; position: absolute; left: -3px; top: -4.5px; width: 7px; height: 7px; border-radius: 50%; background: var(--green); }
.tl-ghost { position: absolute; left: 0; right: 0; height: 48px; border-radius: 14px; border: 1.5px dashed var(--green); background: var(--mint-t); pointer-events: none; }
.tl-empty { position: absolute; left: 0; right: 0; top: 40%; text-align: center; color: var(--ink3); font-size: 12px; }
/* Ссылка «Добавить урок» под «Уроков нет» — путь без скрипта; со скриптом
   урок ставят щелчком по шкале. Без `color` подпись унаследовала бы серый. */
.tl-empty .btn { margin-top: 10px; color: var(--ink); }
.right[data-placed] .tl-empty .btn { display: none; }

/* «3 дня» и «Неделя». */
.wk { display: none; }
@container sched (min-width: 430px) {
  .days, .right > .tl-wrap { display: none; }
  .wk { display: flex; flex-direction: column; flex: 1; min-height: 0; }
}
/* `.wk .wk-top` — сильнее `.wk-top .arrow` ниже: иначе стрелки обоих видов
   остались бы на месте. */
@container sched (min-width: 430px) and (max-width: 599.98px) {
  .wk-col:not(.near), .wk-dh:not(.near), .wk .wk-top .wk-7 { display: none; }
}
@container sched (min-width: 600px) {
  .wk-col:not(.week), .wk-dh:not(.week), .wk .wk-top .wk-3 { display: none; }
}
.wk-top { display: flex; align-items: center; gap: 4px; padding: 4px 12px 8px 14px; flex: none; }
.wk-top b { font-size: 13px; font-weight: 600; padding: 0 4px; white-space: nowrap; }
.wk-top .sp { flex: 1; }
.wk-top .arrow { width: 26px; height: 26px; flex: none; border-radius: 8px; color: var(--ink3); display: grid; place-items: center; }
.wk-top .arrow:hover { background: var(--soft); color: var(--ink); }
/* правый край — по иконке × над ним */
.wk-load { font-size: 11.5px; color: var(--ink3); white-space: nowrap; margin-right: 6px; }
.wk-cols { display: grid; grid-auto-flow: column; grid-template-columns: 42px; grid-auto-columns: minmax(0, 1fr); padding: 0 12px 8px 10px; border-bottom: 1px solid var(--line); flex: none; }
.wk-dh { display: flex; flex-direction: column; align-items: center; gap: 1px; padding: 4px 0 6px; border-radius: 12px; min-width: 0; color: var(--ink); text-decoration: none; }
.wk-dh:hover { background: var(--soft); }
.wk-dh small { font-size: 10.5px; color: var(--ink3); }
.wk-dh b { font-size: 14px; font-weight: 500; width: 28px; height: 28px; border-radius: 14px; display: grid; place-items: center; font-variant-numeric: tabular-nums; }
.wk-dh.today b { box-shadow: inset 0 0 0 2px var(--mint); font-weight: 600; }
.wk-dh.sel { background: var(--soft); }
.wk-dh.we small, .wk-dh.we b { color: var(--ink4); }
.wk-dh em { font-style: normal; font-size: 10.5px; color: var(--ink3); white-space: nowrap; }
.wk .wk-scroll { padding: 0; }
.wk-grid { display: grid; grid-auto-flow: column; grid-template-columns: 42px; grid-auto-columns: minmax(0, 1fr); height: 864px; position: relative; padding: 0 12px 0 10px; }
/* Подпись часа — по центру своей линии: нулевая высота строки ставит
   текст серединой на верх блока. */
.wk-times { padding-top: 54px; }
.wk-times span { display: block; height: 54px; line-height: 0; text-align: right; padding-right: 8px; font-size: 10px; color: var(--ink4); font-variant-numeric: tabular-nums; }
.wk-col { position: relative; border-left: 1px solid var(--line); background-image: linear-gradient(var(--line) 1px, transparent 1px); background-size: 100% 54px; min-width: 0; }
.wk-col.today { background-color: var(--mint-col); }
.wk-col.we { background-color: var(--weekend); }
.wk-ev { height: 50px; margin: 2px 3px; border-radius: 10px; padding: 5px 7px; display: flex; flex-direction: column; gap: 1px; overflow: hidden; position: relative; background: var(--mint-t); box-shadow: var(--shadow-wk); color: var(--ink); text-decoration: none; }
.right[data-placed] .wk-ev[data-min] { position: absolute; left: 3px; right: 3px; margin: 0; }
.wk-ev.tone-mint { background: var(--mint-t); }
.wk-ev.tone-blue { background: var(--blue-t); }
.wk-ev.tone-orange { background: var(--orange-t); }
.wk-ev.tone-violet { background: var(--violet-t); }
.wk-ev:hover { box-shadow: var(--shadow-wk-h); z-index: 2; }
.wk-ev b { font-size: 11.5px; font-weight: 600; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.wk-ev > span { font-size: 10.5px; color: var(--ink2); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.wk-ev.past { opacity: .5; }
.wk-ev.on { box-shadow: 0 0 0 2px var(--green); }
.wk-ev .dot { position: absolute; top: 6px; right: 6px; width: 6px; height: 6px; border-radius: 50%; }
.wk-col .tl-ghost { left: 3px; right: 3px; height: 50px; border-radius: 10px; }
.wk-col .tl-now { left: 0; }

/* Перенос перетаскиванием (прототип 600-605). */
.right[data-placed] :is(.tl-ev, .wk-ev)[data-move] { cursor: grab; }
.right[data-placed] :is(.tl-ev, .wk-ev)[data-moving] { cursor: grabbing; z-index: 5; box-shadow: var(--shadow-drag); transition: none; opacity: 1; }
body[data-dragging], body[data-dragging] * { cursor: grabbing; user-select: none; }
.drag-t { position: absolute; top: -9px; right: 6px; background: var(--ink); color: var(--on); font-size: 10.5px; font-weight: 600; padding: 2px 6px; border-radius: 6px; white-space: nowrap; pointer-events: none; }
.wk-ev .drag-t { top: auto; bottom: 4px; right: 4px; }

/* Переключатель «День / Неделя» и ручка ширины (прототип 561-569). Оба —
   только со скриптом: без него ширина панели постоянная. */
.r-foot { padding: 10px 14px 14px; flex: none; display: flex; gap: 8px; align-items: center; justify-content: center; }
.rmode { display: inline-flex; padding: 3px; border-radius: 10px; background: var(--soft); flex: none; }
.rmode .btn { height: 26px; padding: 0 10px; border-radius: 8px; font-size: 12px; font-weight: 500; color: var(--ink2); }
.rmode .btn:hover { background: none; }
.rmode .btn[aria-pressed="true"] { background: var(--panel); color: var(--ink); box-shadow: var(--shadow-seg); }
/* Ручка стоит в зазоре 12px между центром и панелью: у `.right` видимое
   переполнение (прототип 561). */
.right:has(> .rz:not([hidden])) { overflow: visible; }
.rz { position: absolute; left: -12px; top: 24px; bottom: 24px; width: 12px; cursor: col-resize; z-index: 6; display: grid; place-items: center; touch-action: none; }
.rz::after { content: ""; width: 4px; height: 44px; border-radius: 2px; background: var(--ink4); opacity: .45; transition: opacity .15s, height .15s, background .15s; }
.rz:hover::after, .rz[data-dragging]::after, .rz:focus-visible::after { opacity: 1; height: 64px; background: var(--green); }
.rz:focus-visible { outline: none; }
body[data-resizing], body[data-resizing] * { cursor: col-resize; user-select: none; }
/* Без скрипта прячет атрибут `hidden`: `display` правил выше сильнее него. */
.r-foot[hidden], .rz[hidden], .tl-now[hidden], .drag-t[hidden], .tl-ghost[hidden] { display: none; }
/* До 980px панель под центром во всю ширину, но вид — всегда «День»
   (прототип 610): ручки и переключателя нет. `.right` впереди — сильнее
   правил запроса к контейнеру. */
@media (max-width: 980px) {
  .right .days { display: flex; }
  .right > .tl-wrap { display: block; }
  .right .wk, .right .r-foot, .right .rz { display: none; }
}

/* == левая колонка: верх и аккаунт (макет v105:42-58, макет:98-108) == */
/* Шапка — ссылка-бренд на главную и часы на одной оси 20px; дата и число
   уроков — в листке. Переход знака — на нём самом: лист и возвращается за
   .3s (в макете переход только в `:hover`). У `.me` нет `height: auto` и
   `text-align: left` макета: кнопок-значков в подвале нет. Строка
   аккаунта — ссылка-триггер меню, наведение — `.left-foot button:hover`
   макета:112; `.me-tx small` (макет:103) не переносится: второй строки
   нет. */
.hello { padding: 0 4px; }
.hello-top { display: flex; align-items: center; gap: 10px; line-height: 1.25; padding: 12px 0 16px; }
.hello-top .sp { flex: 1; }
.brand { display: inline-flex; align-items: center; gap: 9px; padding: 4px 8px; margin: -4px 0 -4px -8px; border-radius: 10px; color: var(--ink); text-decoration: none; }
.brand:hover { background: var(--fill); }
.brand b { font-size: 16px; font-weight: 700; letter-spacing: -0.02em; line-height: 20px; flex: none; }
.hello .clock { font-size: 15px; font-weight: 500; color: var(--ink3); font-variant-numeric: tabular-nums; line-height: 20px; }
.left-foot { display: flex; align-items: center; gap: 8px; padding: 8px 12px 14px; color: var(--ink3); font-size: 12px; }
.me { width: 100%; display: flex; align-items: center; gap: 10px; padding: 8px 10px; border-radius: 14px; color: var(--ink2); text-decoration: none; }
.me:hover { background: var(--fill); color: var(--ink); }
/* Шеврон показывает состояние меню: открыто — смотрит вверх. Переход
   гасит сброс `prefers-reduced-motion`, поворот остаётся. */
.me > .icon { transition: transform .2s ease; }
.me[aria-expanded="true"] > .icon { transform: rotate(180deg); }
.me-tx { flex: 1; min-width: 0; display: flex; flex-direction: column; line-height: 1.25; }
.me-tx b { color: var(--ink); font-weight: 600; font-size: 13px; }

/* == левая колонка: виджеты и ученики (макет:49-97) == */
/* Литералы макета — токены, `.nx-av` — `.av`, `.on` — `[aria-current]`,
   `button` — `.btn`. Виджеты и строки — ссылки, отсюда `color` и
   `text-decoration`. Подъём только у ссылок: «Уроков нет» не щёлкается.
   Рекордер и скрытый график (макет:65-77) не переносятся. Цвета точек —
   `.flag-parse`, `.flag-empty` и `.flag-drafts` расписания. Порядок
   виджетов — порядок DOM, `order` макета не нужен (спека 2026-10-29
   §3.2 п. 4). */
.widgets { display: grid; grid-template-columns: 1fr 1fr; gap: 10px; }
.w { position: relative; border-radius: 18px; padding: 12px; height: 148px; overflow: hidden; background: var(--panel); border: 0; text-align: left; display: flex; flex-direction: column; color: inherit; text-decoration: none; transition: box-shadow .15s; }
a.w:hover { box-shadow: var(--shadow-h); }
.w-cal { align-items: center; text-align: center; }
a.w-cal:hover { box-shadow: 0 0 0 1px var(--line), var(--shadow); }
.w-cal .w-label { color: var(--red); font-weight: 500; }
.cal-num { font-variant-numeric: tabular-nums; font-size: 52px; line-height: 50px; font-weight: 400; letter-spacing: -0.045em; margin: 2px 0 0 -0.04em; }
.cal-mon { font-size: 12px; color: var(--ink3); }
.cal-n { margin-top: auto; font-size: 11.5px; font-weight: 500; color: var(--ink); background: var(--soft); border-radius: 8px; padding: 3px 8px; }
.w-label { font-size: 11px; color: var(--ink3); }
.w-mint { grid-column: 1 / -1; height: 118px; }
.heat { display: grid; grid-template-columns: repeat(14, 1fr); gap: 3px; margin: auto 0; }
.heat i { aspect-ratio: 1; border-radius: 3px; background: var(--heat0); }
.heat i.l1 { background: var(--heat1); }
.heat i.l2 { background: var(--heat2); }
.heat i.l3 { background: var(--green); }
.w-big { display: flex; align-items: baseline; gap: 4px; }
.w-big b { font-size: 30px; font-weight: 500; letter-spacing: -0.03em; line-height: 1; color: var(--ink); font-variant-numeric: tabular-nums; }
.w-big span { font-size: 10.5px; color: var(--ink3); line-height: 1.2; }
/* «Следующее занятие» (макет v105:70-89): имя 28px в одну строку, тема
   11px, персонаж уходит низом за край на 24px. Фон белый, как у
   остальных виджетов (цвет по персонажу дизайнер сняла). */
.w-next { align-items: center; text-align: center; }
.w-next .who { font-size: 28px; font-weight: 600; line-height: 32px; letter-spacing: -0.025em; margin-top: 4px; position: relative; z-index: 1; max-width: 100%; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.w-next .w-label { position: relative; z-index: 1; max-width: 100%; line-height: 1.3; }
.w-next .nw { white-space: nowrap; }
.w-next .nx-what { position: relative; z-index: 1; max-width: 100%; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; color: var(--ink2); font-size: 11px; }
/* Модель выше прежних рисунков: коробка 108×96 под именем, фигура стоит
   на её низу и уходит за край карточки на 24px. */
.w-next .av { position: absolute; left: 50%; bottom: -24px; width: 108px; height: 96px; transform: translateX(-50%); line-height: 0; pointer-events: none; }
.w-next .av img { object-position: center bottom; }
.w-empty .who { color: var(--ink3); font-weight: 500; font-size: 17px; line-height: 1.45; }
.sec { display: flex; align-items: center; justify-content: space-between; margin: 22px 4px 6px; font-size: 12px; color: var(--ink3); }
.sec .btn { border: 0; background: var(--fill); color: var(--ink2); width: 30px; height: 30px; border-radius: 10px; display: grid; place-items: center; }
.sec .btn:hover { background: var(--fill2); color: var(--ink); }
.sec .icon { width: 15px; height: 15px; }
.people { display: flex; flex-direction: column; gap: 2px; padding-bottom: 12px; }
.person { display: flex; align-items: center; gap: 10px; padding: 8px 10px; border-radius: 12px; border: 0; background: none; text-align: left; width: 100%; color: inherit; text-decoration: none; }
.person:hover { background: var(--hover); }
.person[aria-current] { background: var(--panel); }
.person[aria-current]:hover { box-shadow: var(--shadow); }
.person .main { flex: 1; min-width: 0; }
.person .main b { display: block; font-weight: 600; font-size: 13px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.person .main span { display: block; font-size: 11.5px; color: var(--ink3); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.person .flag { width: 7px; height: 7px; border-radius: 50%; flex: none; }
/* Модель в списке учеников — в рамке 30×30. */
.person .av { width: 30px; height: 30px; }
/* Три виджета в ряд (макет:519-522). Свой блок после правил раздела:
   в блоке оболочки выше их перебили бы те же селекторы раздела. */
@media (max-width: 980px) {
  .widgets { grid-template-columns: repeat(3, minmax(0, 1fr)); }
  .w-mint { grid-column: auto; height: 148px; }
  .left-foot { padding: 8px 0 0; }
  .heat { grid-template-columns: repeat(7, 1fr); max-width: 150px; }
}

.visually-hidden {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip: rect(0 0 0 0);
  clip-path: inset(50%);
  white-space: nowrap;
}
/* Ссылка «К содержимому» видна только в фокусе с клавиатуры. */
.skip:focus {
  position: static;
  display: inline-block;
  width: auto;
  height: auto;
  margin: 8px 0;
  padding: 8px 16px;
  overflow: visible;
  clip: auto;
  clip-path: none;
  white-space: normal;
  background: var(--ink);
  color: var(--on);
  border-radius: 40px;
}

/* == главная: приветствие и быстрые действия (макет:148-154) ==
   Правила макета как есть, с двумя отступлениями. `.c-inner` стоит между
   `.c-body` и `.idle`: без высоты у него `min-height: 100%` не центрировал
   бы приветствие. Пилюля —
   ссылка, а не кнопка: ей нужны inline-flex и снятое подчёркивание.
   Брейкпоинтов у главной в макете нет: с 980px `.c-body` перестаёт быть
   высотой окна, и приветствие встаёт сверху. */
.c-inner:has(> .idle) { height: 100%; }
.idle { container-type: inline-size; width: 100%; min-height: 100%; display: flex; flex-direction: column; align-items: center; justify-content: center; text-align: center; padding-bottom: 40px; }
.idle h1 { white-space: nowrap; font-weight: 600; font-size: min(30px, 7.4cqi); line-height: 1.15; margin: 0 0 8px; letter-spacing: -0.025em; }
.idle p { margin: 0 0 22px; color: var(--ink3); max-width: none; }
.sugs { display: flex; flex-wrap: wrap; justify-content: center; gap: 8px; max-width: 460px; }
.sug { display: inline-flex; align-items: center; height: 32px; padding: 0 14px; border-radius: 16px; border: 1px solid var(--line2); background: var(--panel); color: var(--ink2); font-size: 12.5px; text-decoration: none; }
.sug:hover { border-color: var(--line3); color: var(--ink); box-shadow: var(--shadow); }
.sug b { font-weight: 600; color: var(--ink); }

/* == ряд действий == */
.actions { display: flex; flex-wrap: wrap; align-items: center; gap: 8px 12px; margin: 0 0 16px; }
.actions .btn-danger, .actions .btn-danger-quiet { margin-left: auto; }

/* == карточка входа и ошибки == */
/* Экрана входа в макете нет: вид взят от `.panel`, размеры и текст — от
   подтверждения `.alert` (макет:505-508). */
.auth { width: 100%; max-width: 320px; padding: 22px; background: var(--panel); border-radius: 22px; }
.auth h1 { margin: 14px 0 0; font-size: 15px; font-weight: 600; line-height: 1.25; letter-spacing: -0.01em; }
.auth p { margin: 6px 0 0; color: var(--ink2); line-height: 1.5; }
/* Поле почты на входе отстоит от текста так же, как кнопка от поля. */
.auth .field { margin: 18px 0 0; }
.auth .actions { margin: 18px 0 0; }
.auth .btn { width: 100%; height: auto; padding-top: 12px; padding-bottom: 12px; white-space: normal; }
/* Ошибка под полем регистрации — значения `.tm-invite + .p-error`. `.auth p`
   её не перебивает: класс весит больше тега. */
.auth .p-error { margin: 4px 0 0; font-size: 12px; line-height: 1.45; color: var(--red-d); }

/* == поле формы == */
.field { display: flex; flex-direction: column; gap: 5px; margin: 0 0 12px; }
.field-label { font-size: 11.5px; color: var(--ink3); }
.field-input { height: 32px; width: 100%; border: 0; border-radius: 9px; background: var(--soft); padding: 0 10px; }
.field-area { width: 100%; border: 0; border-radius: 9px; background: var(--soft); padding: 8px 10px; resize: vertical; min-height: 54px; line-height: 1.45; }
.field-input:focus, .field-area:focus { outline: none; background: var(--panel); box-shadow: var(--ring-field); }
.field-input[aria-invalid="true"], .field-area[aria-invalid="true"] { box-shadow: 0 0 0 1.5px var(--red); }
/* Двойной шеврон макета:479 — файлом: `data:` в `url()` не пускает страж
   статики. На фокусе стрелку стирает фон `.field-input:focus`
   (специфичность выше), как `.in:focus` в макете; своего `:focus` у
   `select` нет. Та же стрелка — у выбора в `lesson_delete.html`. */
select.field-input { appearance: none; -webkit-appearance: none; padding-right: 26px; background: var(--soft) no-repeat right 9px center / 10px 14px url("select.svg"); }
/* Своё: поле мельче 16px Safari на iPhone при фокусе увеличивает вместе со
   страницей. На телефоне поля форм — 16px вместо 13px макета. */
@media (max-width: 560px) { .field-input, .field-area { font-size: 16px; } }

/* == вкладка «Запись»: строки файлов, загрузка, пустое состояние
   (макет:183-188, 227-229, 338-339) == */
.records { border-radius: 12px; outline: 2px dashed transparent; outline-offset: 8px; transition: outline-color .15s; }
.records[data-over] { outline-color: var(--green); }
.file { display: flex; align-items: center; gap: 10px; padding: 10px 12px; border-radius: 14px; background: var(--soft); margin-bottom: 8px; }
.file .main { flex: 1; min-width: 0; }
.file .main b { display: block; font-weight: 500; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.file .main span { font-size: 11.5px; color: var(--ink3); }
.file .fi { width: 30px; height: 30px; border-radius: 9px; display: grid; place-items: center; background: var(--panel); color: var(--ink2); flex: none; }
.file .fi.rec { color: var(--red); }
.upl { display: inline-flex; align-items: center; gap: 6px; color: var(--ink2); font-size: 12.5px; cursor: pointer; padding: 6px 2px; margin-top: 14px; }
.upl:hover { color: var(--ink); }
.upl:has(:focus-visible) { outline: 2px solid var(--green); outline-offset: 2px; }
.big-cta { display: flex; flex-direction: column; align-items: center; text-align: center; gap: 10px; padding: 44px 20px; border-radius: 20px; background: var(--soft); }
.big-cta b { font-size: 15px; font-weight: 600; }
.big-cta p { margin: 0; color: var(--ink3); max-width: 42ch; }
.big-cta[hidden] { display: none; }
/* Своё: пустая «Запись» — одна строка файла вместо большого пустого
   состояния; вся строка — поле выбора файла. */
.upl-drop { cursor: pointer; margin-bottom: 0; }
.upl-drop:hover { background: var(--fill); }
.upl-drop:has(:focus-visible) { outline: 2px solid var(--green); outline-offset: 2px; }

/* == студия: вкладки (макет:164-170), плашка разбора (230-234), «Конспект» (338-351) == */
/* Перенос макета с отличиями, которых требует спека: вкладка — ссылка
   (text-decoration); полоса разбора бесконечная — шагов у разбора нет
   (§5 п. 14), отрезок бежит по кругу вместо transition: width; плашки
   «не удался», «изменились», «новых нет» — та же коробка на своих
   токенах; строка разбора — бывший style= макета:943; подсказку пустого
   конспекта прячет атрибут hidden, а не класс is-empty. Рамка
   перетаскивания у .doc — этап 5. */
.tabs { display: flex; gap: 4px; border-bottom: 1px solid var(--line); margin-bottom: 18px; }
.tabs .tab:first-child { padding-left: 0; }
.tabs .tab:first-child[aria-selected="true"]::after { left: 0; }
.tab { height: 36px; padding: 0 12px; border: 0; background: none; color: var(--ink3); font-weight: 500; position: relative; display: inline-flex; align-items: center; gap: 6px; text-decoration: none; }
.tab span { font-size: 11px; color: var(--ink3); background: var(--soft); border-radius: 9px; padding: 1px 6px; }
.tab[aria-selected="true"] { color: var(--ink); }
.tab[aria-selected="true"]::after { content: ""; position: absolute; left: 10px; right: 10px; bottom: -1px; height: 2px; border-radius: 2px; background: var(--ink); }
.parsing { display: flex; align-items: center; gap: 12px; padding: 16px; border-radius: 16px; background: var(--mint-t); color: var(--green-text); margin-bottom: 12px; }
.parsing .pb { flex: 1; height: 4px; border-radius: 2px; background: var(--green-track); overflow: hidden; }
.parsing .pb i { display: block; width: 40%; height: 100%; background: var(--green); animation: pb-run 1.4s ease-in-out infinite; }
@keyframes pb-run { from { transform: translateX(-100%); } to { transform: translateX(250%); } }
.spin { width: 16px; height: 16px; border-radius: 50%; border: 2px solid var(--green-spin); border-top-color: var(--green); animation: spin .8s linear infinite; flex: none; }
@keyframes spin { to { transform: rotate(360deg); } }
.parsing-failed { background: var(--red-t); color: var(--red-text); }
.parsing-stale { background: var(--orange-t); color: var(--orange-text); }
.parsing-empty { background: var(--soft); color: var(--ink2); }
.parsing-tx { flex: 1; min-width: 0; }
.parse-row { display: flex; align-items: center; gap: 10px; margin-top: 14px; }
.parse-row[hidden] { display: none; }
.doc { display: flex; flex-direction: column; min-height: 320px; border-radius: 12px; position: relative; }
.doc-empty { position: absolute; left: 0; right: 0; top: 0; pointer-events: none; color: var(--ink4); font-size: 14px; line-height: 1.55; }
.doc-empty b { display: block; color: var(--ink35); font-weight: 400; font-size: 15px; margin-bottom: 16px; }
.doc-empty ul { margin: 0; padding: 0; list-style: none; display: grid; gap: 10px; }
.doc-empty li { display: flex; align-items: center; gap: 10px; font-size: 13px; color: var(--ink35); }
.doc-empty li .icon { color: var(--ink4); opacity: .8; }
.doc-empty:not([hidden]) + .doc-t { min-height: 220px; }
.doc-t { display: block; width: 100%; border: 0; resize: none; background: none; font-size: 15px; line-height: 1.7; padding: 0; overflow: hidden; min-height: 26px; color: var(--ink); }
.doc-t:focus { outline: none; }
/* Как у полей форм: мельче 16px Safari на iPhone увеличивает страницу. */
@media (max-width: 560px) { .doc-t { font-size: 16px; } }

/* == вкладка «Домашка»: статус, миниатюры, панель, слайд (спека 2026-10-27
   §6.1) == */
/* Редактора слайдов в макете нет: значения — его правил домашки. Строка
   статуса — `.hw-foot` (макет v105:484), список миниатюр — `.hw-list`
   (446), миниатюра и слайд — плашка `.hw-item` (447) с номером `.hw-n`
   (450), панель — кнопки `.b ghost icon` (макет v105:1179) на мягкой
   плашке. Документ слайда — наши элементы под `.hw-doc`: остров классов
   не пишет. Остров прячет и показывает части атрибутом `hidden`, поэтому
   `display` здесь только у классов, которые им не прячутся. */
.hw-bar { display: flex; align-items: center; flex-wrap: wrap; gap: 8px 12px; margin-bottom: 14px; }
.hw-bar span { font-weight: 500; }
.hw-bar .hint { font-weight: 400; }
.hw-bar .caution { margin: 0; padding: 8px 12px; border-radius: 12px; }
.hw-bar form, .hw-next .btn { margin-left: auto; }
.hw > .hint { margin: -6px 0 14px; }
.hw-grid { display: grid; grid-template-columns: 180px minmax(0, 1fr); gap: 20px; align-items: start; }
.hw-grid > div { display: flex; flex-direction: column; gap: 10px; min-width: 0; }
.hw-thumbs { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 8px; }
.hw-thumb { display: flex; align-items: center; gap: 10px; width: 100%; padding: 8px 10px; border: 0; border-radius: 14px; background: var(--soft); text-align: left; font-size: 12.5px; color: var(--ink2); }
.hw-thumb:hover { background: var(--soft2); }
.hw-thumb[aria-current="true"] { background: var(--panel); color: var(--ink); box-shadow: 0 0 0 1.5px var(--green); }
.hw-thumb b, .hw-slide > b { width: 22px; height: 22px; flex: none; border-radius: 50%; background: var(--panel); display: grid; place-items: center; font-size: 11.5px; font-weight: 600; color: var(--ink2); }
.hw-thumb span { min-width: 0; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.hw-tools { display: flex; flex-wrap: wrap; align-items: center; gap: 2px; padding: 4px; border-radius: 12px; background: var(--soft); }
.hw-tools .sp { flex: 1; }
.hw-doc { min-height: 320px; padding: 16px 18px; border-radius: 14px; background: var(--panel); box-shadow: 0 0 0 1px var(--line2); font-size: 15px; line-height: 1.6; }
.hw-doc:focus-within { box-shadow: var(--ring-field); }
.hw-doc > :first-child { margin-top: 0; }
.hw-doc h3 { margin: 16px 0 6px; font-size: 17px; font-weight: 600; line-height: 1.3; }
.hw-doc p { margin: 0 0 8px; }
.hw-doc ul, .hw-doc ol { margin: 0 0 8px; padding-left: 22px; }
.hw-doc table { margin: 0 0 8px; font-size: 14px; }
.hw-doc td { padding: 6px 8px; border: 1px solid var(--line2); vertical-align: top; }
.hw-doc td p { margin: 0; }
.hw-doc img { display: block; max-width: 100%; height: auto; margin: 0 0 8px; border-radius: 10px; }
.hw-doc audio { display: block; width: 100%; margin: 0 0 8px; }
.hw-slide { display: flex; gap: 12px; padding: 12px 14px; margin-bottom: 8px; border-radius: 14px; background: var(--soft); }
.hw-slide .hw-doc { flex: 1; min-width: 0; min-height: 0; padding: 0; background: none; box-shadow: none; }
/* Редактор острова: Tiptap кладёт в `.hw-doc` свой корень `.ProseMirror`
   (классы ProseMirror — его, остров их не ставит). Корень растягивается на
   всю плашку, чтобы щелчок в пустоту ставил курсор; рамку фокуса даёт
   `.hw-doc:focus-within`. Нажатая кнопка панели — `aria-pressed`, его
   ставит остров по выделению. */
.hw-doc .ProseMirror { min-height: 288px; outline: none; }
.hw-doc .ProseMirror > :first-child { margin-top: 0; }
.hw-doc .ProseMirror-selectednode { outline: 2px solid var(--green); outline-offset: 2px; }
.hw-doc table { table-layout: fixed; }
.hw-tools [aria-pressed="true"] { background: var(--soft2); color: var(--ink); }
/* Одна колонка — на узком окне и в узком центре: при открытом
   расписании центр уже 560px и на широком окне. */
@media (max-width: 760px) {
  .hw-grid { grid-template-columns: minmax(0, 1fr); }
  .hw-thumbs { flex-direction: row; overflow-x: auto; padding-bottom: 4px; }
  .hw-thumbs li { flex: none; width: 160px; }
}
@container (max-width: 560px) {
  .hw-grid { grid-template-columns: minmax(0, 1fr); }
  .hw-thumbs { flex-direction: row; overflow-x: auto; padding-bottom: 4px; }
  .hw-thumbs li { flex: none; width: 160px; }
}
@media (max-width: 560px) { .hw-doc { font-size: 16px; } }

/* == кабинет ученика: главный экран и домашка (спека 2026-10-27 §5, §6.4) == */
/* Отдельного макета нет: язык — плашки, номера и кнопки кабинета, размеры
   — мобильные (§5, решение 13). Текст слайда и подписи — не меньше 15px,
   редактируемый — 16px, цели нажатия 44px; цвет текста — только `--ink` и
   `--ink2`: светлые серые не проходят AA. Главный экран — белая плашка по
   содержимому; домашка — до 1080px, от 980px список слайдов слева.
   Остров прячет части атрибутом `hidden`, поэтому `display` — только у
   классов, которые им не прячутся, или через `:not([hidden])`. */
.solo:has(> .sh) { align-content: start; }
.sh-home { width: 100%; max-width: 520px; padding: 22px; background: var(--panel); border-radius: 22px; box-shadow: var(--shadow); font-size: 15px; line-height: 1.5; }
.sh-home h1 { margin: 14px 0 0; font-size: 20px; font-weight: 600; line-height: 1.25; letter-spacing: -0.01em; }
.sh-home h2 { margin: 22px 0 0; font-size: 17px; font-weight: 600; line-height: 1.3; }
.sh-home p { margin: 6px 0 0; color: var(--ink2); }
.sh-slides, .sh-past, .sh-list { list-style: none; margin: 12px 0 0; padding: 0; display: flex; flex-direction: column; gap: 6px; }
.sh-slides a, .sh-past a, .sh-item { display: flex; align-items: center; gap: 10px; width: 100%; min-height: 44px; padding: 8px 12px; border: 0; border-radius: 14px; background: var(--soft); color: var(--ink); font-size: 15px; text-align: left; text-decoration: none; }
.sh-slides a:hover, .sh-past a:hover, .sh-item:hover { background: var(--soft2); }
.sh-list .sh-item { background: none; }
.sh-list .sh-item:hover { background: var(--soft2); }
.sh-list .sh-item[aria-current="true"] { background: var(--panel); box-shadow: 0 0 0 1.5px var(--green); }
.sh-slides b, .sh-item b, .sh-slide > b { width: 26px; height: 26px; flex: none; border-radius: 50%; background: var(--panel); display: grid; place-items: center; font-size: 13px; font-weight: 600; color: var(--ink2); }
.sh-slides span, .sh-past span { flex: 1; min-width: 0; overflow-wrap: anywhere; }
.sh-item span { flex: 1; min-width: 0; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.sh-slides small, .sh-past small { flex: none; font-size: 13px; color: var(--ink2); }
.sh-actions { display: flex; flex-wrap: wrap; align-items: center; gap: 10px; margin: 16px 0 0; }
.sh-home .sh-done { margin: 0; font-weight: 500; color: var(--ink); }
/* Ссылка передачи в другой браузер (§10.2): поле выделяют пальцем, когда
   «Скопировать» без JS скрыта; 16px — меньше iOS увеличил бы страницу. */
.sh-link { display: block; margin: 16px 0 0; }
.sh-link input { width: 100%; height: 44px; padding: 0 12px; border: 1px solid var(--line2); border-radius: 12px; background: var(--soft); color: var(--ink); font: inherit; font-size: 16px; text-overflow: ellipsis; }
.sh-link input:focus { outline: none; box-shadow: var(--ring-field); }
.sh-home .btn, .sh .btn { height: 44px; padding: 0 16px; border-radius: 12px; font-size: 15px; }
.sh .btn-icon { width: 44px; padding: 0; }
.sh { width: 100%; max-width: 1080px; font-size: 15px; line-height: 1.5; }
.sh-top { display: flex; align-items: center; flex-wrap: wrap; gap: 8px 12px; margin-bottom: 14px; }
.sh-top h1 { margin: 0; font-size: 20px; font-weight: 600; line-height: 1.25; letter-spacing: -0.01em; }
.sh-note { margin: 0 0 14px; padding: 10px 14px; border-radius: 14px; background: var(--panel); color: var(--ink2); }
.sh-slide { display: flex; gap: 12px; padding: 14px 16px; margin-bottom: 10px; border-radius: 18px; background: var(--panel); box-shadow: var(--shadow); }
.sh-app:not([hidden]) { display: grid; grid-template-columns: 220px minmax(0, 1fr); gap: 20px; align-items: start; }
.sh-list { margin: 0; position: sticky; top: 14px; }
.sh-main { display: flex; flex-direction: column; gap: 10px; min-width: 0; }
.sh-bar { position: sticky; top: 0; z-index: 5; display: flex; align-items: center; gap: 4px; padding: 4px; border-radius: 14px; background: var(--panel); box-shadow: var(--shadow); }
.sh-sp { flex: 1; }
.sh-bar [aria-pressed="true"] { background: var(--soft2); color: var(--ink); }
.sh-menu { position: absolute; right: 4px; top: calc(100% + 4px); z-index: 6; min-width: 260px; padding: 6px; border-radius: 14px; background: var(--panel); box-shadow: var(--shadow-pop); }
.sh-menu .mi { height: 44px; font-size: 15px; }
.sh-conflict { padding: 12px 14px; border-radius: 14px; background: var(--orange-t); color: var(--ink); }
.sh-conflict p { margin: 0; }
.sh-conflict .sh-actions { margin-top: 10px; }
.sh-mic { padding: 12px 14px; border-radius: 14px; background: var(--panel); box-shadow: var(--shadow); color: var(--ink); }
.sh-mic p { margin: 0 0 10px; }
.sh-timer { padding: 0 6px; color: var(--ink); font-variant-numeric: tabular-nums; white-space: nowrap; }
.sh-doc { flex: 1; min-width: 0; font-size: 16px; line-height: 1.6; color: var(--ink); }
.sh-main > .sh-doc { min-height: 320px; padding: 16px 18px; border-radius: 18px; background: var(--panel); box-shadow: var(--shadow); }
.sh-main > .sh-doc:focus-within { box-shadow: var(--ring-field); }
.sh-doc > :first-child { margin-top: 0; }
.sh-doc h3 { margin: 16px 0 6px; font-size: 18px; font-weight: 600; line-height: 1.3; }
.sh-doc p { margin: 0 0 8px; }
.sh-doc ul, .sh-doc ol { margin: 0 0 8px; padding-left: 22px; }
.sh-doc table { margin: 0 0 8px; table-layout: fixed; width: 100%; }
.sh-doc td { padding: 6px 8px; border: 1px solid var(--line2); vertical-align: top; }
.sh-doc td p { margin: 0; }
.sh-doc img { display: block; max-width: 100%; height: auto; margin: 0 0 8px; border-radius: 10px; }
.sh-doc audio { display: block; width: 100%; margin: 0 0 8px; }
/* Редактор острова student: Tiptap кладёт в `[data-sh-editor]` свой корень
   `.ProseMirror` (классы ProseMirror — его). Пропуск `___` — декорация
   `.sh-gap`: касание выделяет его целиком. */
.sh-doc .ProseMirror { min-height: 288px; outline: none; overflow-wrap: break-word; }
.sh-doc .ProseMirror > :first-child { margin-top: 0; }
.sh-doc .ProseMirror-selectednode { outline: 2px solid var(--green); outline-offset: 2px; }
.sh-doc .sh-gap { border-radius: 3px; background: var(--orange-t); }
/* Плеер преподавателя (спека §6.4) не уходит, пока ученик листает текст
   слайда: липнет под панелью — её высота 52px (кнопка 44px и поля 4px).
   Белое кольцо 8px закрывает зазор и края строк под плеером. Пометку
   `data-sh-teacher` ставит схема острова, не класс. */
.sh-doc audio[data-sh-teacher] { position: sticky; top: 60px; z-index: 4; background: var(--panel); box-shadow: 0 0 0 8px var(--panel); }
.sh-status { min-height: 22px; color: var(--ink2); }
.sh-nav { position: sticky; bottom: 0; display: flex; flex-wrap: wrap; align-items: center; gap: 8px; padding: 8px 0 calc(8px + env(safe-area-inset-bottom, 0px)); background: var(--bg); }
.sh-nav span { flex: 1; text-align: center; white-space: nowrap; color: var(--ink2); }
/* Телефон и узкое окно: слайд во всю ширину, список прячется — слайды
   листаются «назад / дальше» и свайпом. */
@media (max-width: 980px) {
  .sh-app:not([hidden]) { grid-template-columns: minmax(0, 1fr); }
  .sh-list { display: none; }
}
@media (max-width: 560px) {
  .sh-home { padding: 18px; border-radius: 18px; }
  .sh-slide { padding: 12px; gap: 10px; }
  .sh-main > .sh-doc { padding: 14px; }
  .sh-nav .btn-primary { flex: 1 0 100%; }
}

/* == карточки: строка, правка, шапка (макет:189-226) == */
/* Перенос макета построчно с отличиями спеки: строка — `form.card`, её
   верх — `div.card-main` (не кнопка); действия — наши `.btn` в пилюле;
   правка прячется атрибутом `hidden` и подменяет строку (`:has`), а не
   перерисовкой; тип текстом вместо `.ce-types`; точка типа — классом по
   `data-category`, а не `style=`. */
.cards { display: flex; flex-direction: column; gap: 8px; }
.card { position: relative; border-radius: 16px; background: var(--soft); transition: background .15s; }
.card:hover { background: var(--soft2); }
/* Своё: колонка типа и ступени — постоянной ширины, иначе `auto` у
   каждой строки своя, и ответы разных строк не встают в одну линию. */
.card-main { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr) 136px; gap: 16px; align-items: start; padding: 14px 16px; border-radius: 16px; cursor: pointer; }
.card-acts { position: absolute; top: 50%; right: 10px; transform: translateY(-50%); display: flex; gap: 2px; padding: 3px; border-radius: 11px; background: var(--panel); box-shadow: var(--shadow-acts); opacity: 0; pointer-events: none; transition: opacity .12s; }
.card:hover .card-acts, .card:focus-within .card-acts { opacity: 1; pointer-events: auto; }
.card:hover .t, .card:focus-within .t { opacity: 0; }
.card-acts .btn { width: 30px; height: 30px; padding: 0; border: 0; border-radius: 8px; background: none; display: grid; place-items: center; color: var(--ink2); }
/* Наше «Принять» — словом, а не значком. */
.card-acts .btn:not(.btn-icon) { width: auto; padding: 0 10px; }
.card-acts .btn:hover { background: var(--soft); color: var(--ink); }
.card-acts .btn[value="drop"]:hover { background: var(--red-t); color: var(--red-d); }
@media (hover: none) {
  .card-acts { opacity: 1; pointer-events: auto; position: static; transform: none; margin: 0 10px 10px auto; width: max-content; }
  .card:hover .t, .card:focus-within .t { opacity: 1; }
}
/* Открытая правка: строка белая с зелёной рамкой (макет:200; 201 не
   нужен — специфичность `:has(…)` выше `.card:hover`), верх строки
   уступает место форме. У `.card-edit` своего `display` нет: его прячет
   атрибут `hidden`. */
.card:has(> .card-edit:not([hidden])) { background: var(--panel); box-shadow: var(--shadow-edit); }
.card-edit { padding: 14px 16px; }
.card:has(> .card-edit:not([hidden])) > .card-main,
.card:has(> .card-edit:not([hidden])) > .card-from,
.card:has(> .card-edit:not([hidden])) > .card-acts { display: none; }
.ce-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; }
.ce { display: flex; flex-direction: column; gap: 4px; min-width: 0; }
.ce > span { font-size: 11px; color: var(--ink3); }
.ce-in { width: 100%; border: 0; border-radius: 9px; background: var(--soft); padding: 8px 10px; font-size: 14px; resize: none; line-height: 1.4; }
.ce-in.a { font-weight: 600; }
.ce-in:focus { outline: none; background: var(--panel); box-shadow: var(--ring-field); }
textarea.ce-in { font-size: 13px; color: var(--ink2); overflow: hidden; }
@media (max-width: 560px) { .ce-in, textarea.ce-in { font-size: 16px; } }
.ce-grid + .ce { margin-top: 10px; }
.ce-in[aria-invalid="true"] { box-shadow: 0 0 0 1.5px var(--red); }
.ce-foot { display: flex; align-items: center; gap: 8px; margin-top: 12px; flex-wrap: wrap; }
.ce-foot .sp { flex: 1; }
.ce-note { font-size: 11.5px; color: var(--ink3); margin: 8px 0 0; max-width: none; }
.ce-kind { font-size: 11.5px; color: var(--ink3); }
@media (max-width: 560px) { .ce-grid { grid-template-columns: 1fr; } }
.card .q { font-size: 14px; overflow-wrap: anywhere; }
.card .q small { display: block; font-size: 12px; color: var(--ink3); margin-top: 3px; }
.card .q small[hidden] { display: none; }
.card .a { font-size: 14px; font-weight: 600; overflow-wrap: anywhere; }
.card .t { display: flex; flex-direction: column; align-items: flex-end; gap: 4px; text-align: right; }
.tdot { display: inline-flex; align-items: center; gap: 6px; font-size: 11.5px; color: var(--ink2); white-space: nowrap; }
.tdot i { width: 8px; height: 8px; border-radius: 3px; background: var(--type-other); }
.card[data-category="vocab"] .tdot i { background: var(--green); }
.card[data-category="idiom"] .tdot i { background: var(--orange); }
.card[data-category="grammar"] .tdot i { background: var(--ink); }
.card[data-category="error"] .tdot i { background: var(--red); }
.card[data-category="exception"] .tdot i { background: var(--violet); }
.card[data-category="realia"] .tdot i { background: var(--blue); }
.card-stage { font-size: 11px; }
/* Наше: убранная строка серая и правку не открывает; откуда карточка — на
   странице ученика; метки попыток под статусом. */
.card[data-stage="retired"] { color: var(--ink3); }
.card[data-stage="retired"] .card-main { cursor: default; }
/* Погашенным кнопкам убранной строки не место в пилюле: при наведении
   остаются тип и «убрана». */
.card[data-stage="retired"] .card-acts { display: none; }
.card[data-stage="retired"]:hover .t, .card[data-stage="retired"]:focus-within .t { opacity: 1; }
.card-from { margin: -6px 16px 0; padding-bottom: 12px; font-size: 11.5px; color: var(--ink3); }
.card-from a { color: var(--ink2); }
.attempt-marks { display: flex; gap: 2px; margin: 0; padding: 0; list-style: none; }
.attempt-mark { width: 6px; height: 10px; border-radius: 2px; background: var(--ink4); }
.attempt-mark[data-verdict="correct"] { background: var(--green); }
.attempt-mark[data-verdict="partial"] { background: var(--orange); }
.attempt-mark[data-verdict="wrong"] { background: var(--red); }
/* `flex-wrap` — своё: на телефоне кнопки шапки уходят на вторую строку, а
   не за край центра. */
.cards-head { display: flex; align-items: center; gap: 10px; margin-bottom: 12px; flex-wrap: wrap; }
.cards-head .sp { flex: 1; }
/* Своё: пустые «Карточки» — шапка на месте, под ней короткая плашка:
   своя карточка или материал для разбора. */
.cards-empty { padding: 28px 20px; border-radius: 16px; background: var(--soft); text-align: center; }
.cards-empty b { display: block; font-size: 14px; font-weight: 600; }
.cards-empty p { margin: 6px auto 0; color: var(--ink3); max-width: 46ch; line-height: 1.45; }
.cards-empty a { color: var(--ink2); text-decoration: underline; text-underline-offset: 2px; }
.cards-empty a:hover { color: var(--ink); }
.cards-head + .hint { margin: -4px 0 12px; }
/* Своя карточка (макет v105 §7.7): форма — карточка в правке (рамка
   `--shadow-edit`), тип — чипы-переключатели, выбранный чёрный. */
.own-card > summary { list-style: none; }
.own-card > summary::-webkit-details-marker { display: none; }
.own-card[open] { order: 9; flex-basis: 100%; }
.own-form { margin-top: 8px; border-radius: 16px; background: var(--panel); box-shadow: var(--shadow-edit); }
.own-types { display: flex; flex-wrap: wrap; gap: 4px; margin: 12px 0 0; padding: 0; border: 0; }
.own-types input { position: absolute; opacity: 0; }
.own-types span { display: inline-flex; align-items: center; height: 26px; padding: 0 10px; border-radius: 13px; background: var(--soft); color: var(--ink2); font-size: 12px; cursor: pointer; }
.own-types input:checked + span { background: var(--ink); color: var(--on); }
.own-types input:focus-visible + span { outline: 2px solid var(--green); outline-offset: 2px; }
/* счёт черновиков под списком — `.cards-foot` макета v105:285, имя своё:
   `.cards-foot` у нас подвал */
.cards-tally { margin: 12px 2px 0; }
.cards-status { min-height: 16px; margin: 8px 0 0; font-size: 11.5px; color: var(--ink3); }
.cards-status[data-state="failed"], .cards-status[data-state="gone"],
.cards-status[data-state="missing"], .cards-status[data-state="expired"] { color: var(--red-d); }
.cards-foot { margin-top: 24px; }
.cards-foot .hint { margin: 0 0 16px; max-width: none; }
/* Заголовок раздела макета (макет:247-248) на `<h2>`: кегль, поля и
   высоту строки тела правило задаёт само. */
.h2 { display: flex; align-items: center; gap: 8px; font-size: 13px; font-weight: 600; margin: 0 0 10px; line-height: 1.45; }
.h2 span { color: var(--ink3); font-weight: 400; }
.scroll { overflow-x: auto; }
table { width: 100%; border-collapse: collapse; font-size: 12px; }
/* Слова в ячейках не рвутся: узкая таблица листается в `.scroll`. */
th, td { padding: 8px 10px 8px 0; text-align: left; vertical-align: top; border-top: 1px solid var(--line); overflow-wrap: normal; }
th { border-top: 0; color: var(--ink3); font-weight: 400; }
/* Фильтры ученика — вид `.ce-types label` макета (макет:210, 213). */
.card-filters { display: flex; flex-wrap: wrap; gap: 4px; margin: 0 0 8px; }
.card-filters .btn { height: 26px; border-radius: 13px; font-weight: 400; color: var(--ink2); background: var(--soft); }
.card-filters .btn:not([aria-current="page"]):hover { background: var(--soft2); }
.card-filters [aria-current="page"] { background: var(--ink); color: var(--on); }
.card-filters + .hint { margin: 0 0 12px; }

/* == «Успеваемость»: подсказка, плашки с полосой, таблица (макет:174, 240-246, 379-399) == */
/* Подсказка — общий класс: его берут и другие страницы. */
.hint { color: var(--ink3); font-size: 12px; }
.stats { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 10px; margin-bottom: 28px; }
.stat { padding: 14px; border-radius: 16px; background: var(--soft); }
.stat span { font-size: 11.5px; color: var(--ink3); display: block; }
.stat b { font-size: 24px; font-weight: 500; letter-spacing: -0.02em; font-variant-numeric: tabular-nums; display: block; margin-top: 6px; }
.stat b small { font-size: 12px; color: var(--ink3); font-weight: 400; margin-left: 3px; letter-spacing: 0; }
/* `.bar` макета стал `<meter>`: значение ставит сервер, а `style=` шаблонам
   запрещён. Каждое правило с псевдоэлементом — отдельно: чужой браузер
   выбросил бы всё правило целиком. */
.stat meter { display: block; width: 100%; height: 4px; margin-top: 10px; border: 0; border-radius: 2px; overflow: hidden; appearance: none; background: var(--line); }
.stat meter::-webkit-meter-bar { height: 4px; border: 0; border-radius: 2px; background: var(--line); }
.stat meter::-webkit-meter-optimum-value { background: var(--green); }
.stat meter::-moz-meter-bar { background: var(--green); }
@media (max-width: 980px) { .stats { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
/* `style=` макета:1090 и 1100. Низ подвала — `1em` подсказки, как у `p`
   макета. */
.c-inner:has(.st-page) { max-width: 860px; }
.st-period { margin: 4px 0 20px; }
.st-foot { margin: 12px 0; }
/* У `h1` макета высота строки тела (макет:23, 384). */
.st-h1 { margin: 6px 0 0; font-size: 28px; font-weight: 600; letter-spacing: -0.025em; line-height: 1.45; }
.st-table { display: flex; flex-direction: column; margin-top: 4px; }
/* Строка — ссылка на ученика. У числа дней нет `warn`: строки одинаковы
   при любом числе дней (решение владельца 6). */
.st-row { display: grid; grid-template-columns: minmax(150px, 1.3fr) minmax(0, 2fr) 64px 72px 80px; align-items: center; gap: 14px; padding: 10px 8px; border: 0; background: none; text-align: left; border-radius: 12px; width: 100%; color: inherit; text-decoration: none; }
a.st-row:hover { background: var(--soft); }
.st-row + .st-row { border-top: 1px solid var(--line); }
a.st-row:hover + .st-row { border-top-color: transparent; }
.st-headrow { font-size: 11px; color: var(--ink3); padding-block: 0 8px; }
.st-who { display: flex; align-items: center; gap: 10px; min-width: 0; }
.st-who > span { min-width: 0; display: flex; flex-direction: column; }
.st-who b { font-weight: 600; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.st-who small { font-size: 11.5px; color: var(--ink3); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.st-days { display: grid; grid-template-columns: repeat(14, 1fr); gap: 3px; }
.st-days i { aspect-ratio: 1; border-radius: 4px; background: var(--st0); max-height: 22px; }
.st-days i.l1 { background: var(--st1); }
.st-days i.l2 { background: var(--st2); }
.st-days i.l3 { background: var(--green); }
.st-headrow .st-days i { background: none; aspect-ratio: auto; font-style: normal; text-align: center; font-size: 10px; color: var(--ink3); }
.st-row .num { text-align: right; font-variant-numeric: tabular-nums; }
.st-row .num b { font-weight: 600; }
.st-row .num small { font-size: 11px; color: var(--ink3); margin-left: 2px; }
@media (max-width: 760px) { .st-row { grid-template-columns: minmax(0, 1fr) 56px 64px; } .st-row .st-days, .st-row > .num:last-child { display: none; } }

/* Мгновенная подсказка клетки дня — макет v105:508-511; три крайние клетки
   с каждой стороны прижимают пузырь к краю (спека 2026-10-29 §3.6 п. 3). */
[data-tip] { position: relative; }
[data-tip]:hover { outline: 1.5px solid var(--ink); outline-offset: 1px; }
[data-tip]:hover::after { content: attr(data-tip); position: absolute; left: 50%; bottom: calc(100% + 6px); transform: translateX(-50%); z-index: 30; white-space: nowrap; }
/* Своё: подсказки кабинета едины — клетки дней и пузырь `title`
   (`.tip`, ставит скрипт): чёрный фон, скругление 4px, поля по 16px. */
[data-tip]:hover::after, .tip { padding: 4px 16px; border-radius: 4px; background: var(--ink); color: var(--on); font: 500 11px/1.3 var(--font); font-style: normal; pointer-events: none; box-shadow: var(--shadow-tip); }
.tip { position: fixed; z-index: 90; max-width: min(280px, calc(100vw - 16px)); overflow-wrap: break-word; }
[data-tip]:nth-child(-n+3):hover::after { left: 0; transform: none; }
[data-tip]:nth-child(n+12):hover::after { left: auto; right: 0; transform: none; }

/* == Ученик: показатели и уроки (макет:250-256, 1108-1118; полоска — макет v105:513-523) == */
.s-act { margin: 4px 0 28px; }
.s-act-top { display: flex; align-items: baseline; justify-content: space-between; gap: 12px; margin-bottom: 8px; flex-wrap: wrap; }
.s-act-h { margin: 0; font-size: 12px; font-weight: 400; color: var(--ink3); }
.s-act-meta { margin: 0; font-size: 12.5px; color: var(--ink3); font-variant-numeric: tabular-nums; }
.s-act-meta b { color: var(--ink); font-weight: 600; }
.s-act-meta b.warn { color: var(--orange-d); }
.s-act-days { display: grid; gap: 4px; grid-template-columns: repeat(14, minmax(0, 1fr)); }
.s-act-days i { height: 26px; border-radius: 6px; background: var(--st0); }
.s-act-days i.l1 { background: var(--st1); }
.s-act-days i.l2 { background: var(--st2); }
.s-act-days i.l3 { background: var(--green); }
.s-act-axis { display: flex; justify-content: space-between; margin-top: 5px; font-size: 10.5px; color: var(--ink4); }
.s-lessons { margin-bottom: 20px; }
.s-past { margin-bottom: 24px; }
.lrow { display: flex; align-items: center; gap: 14px; width: 100%; border: 0; background: none; text-align: left; padding: 12px 6px; border-radius: 12px; color: inherit; text-decoration: none; position: relative; }
.lrow:hover { background: var(--soft); }
/* ровный разделитель поверх скругления — макет v105:311-313; на :focus-visible
   линия тоже скрыта, чтобы не резать кольцо фокуса (спека 2026-10-29 §3.6 п. 4) */
.lrow + .lrow::before { content: ""; position: absolute; left: 6px; right: 6px; top: 0; height: 1px; background: var(--line); }
.lrow:hover::before, .lrow:hover + .lrow::before, .lrow:focus-visible::before, .lrow:focus-visible + .lrow::before { opacity: 0; }
.lrow .d { width: 44px; flex: none; text-align: center; line-height: 1.05; }
.lrow .d b { display: block; font-size: 20px; font-weight: 500; font-variant-numeric: tabular-nums; }
.lrow .d small { font-size: 10.5px; color: var(--ink3); }
.lrow .main { flex: 1; min-width: 0; }
.lrow .main b { display: block; font-weight: 500; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.lrow .main span { font-size: 12px; color: var(--ink3); }
.lrow .main small { display: block; font-size: 12px; color: var(--ink3); }

/* == «Преподаватели» == */
/* Своего экрана у макета нет: заголовки и строки — страницы ученика
   (`.h2`, `.lrow`, макет:247-256), поле — `.in` (макет:476-478), плашка
   готовой ссылки — `.stat` (макет:241). Строка не ссылка: без `:hover`
   и без даты `.d`. */
.tm-sec { margin-top: 24px; }
.tm-list { margin: 0; padding: 0; list-style: none; }
.tm-row { display: flex; align-items: center; gap: 14px; padding: 12px 6px; border-radius: 12px; }
.tm-row + .tm-row { border-top: 1px solid var(--line); }
.tm-row .main { flex: 1; min-width: 0; }
.tm-row .main b { display: block; font-weight: 500; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.tm-row .main span { font-size: 12px; color: var(--ink3); }
.tm-row form { margin: 0; flex: none; }
/* Ряд поля и кнопки — как `.pop .acts` (макет:490); 220px — минимум поля
   до переноса кнопки. */
.tm-invite { display: flex; flex-wrap: wrap; align-items: flex-end; gap: 8px; }
.tm-invite .field { flex: 1 1 220px; margin: 0; }
/* Значения `.pop .p-error`; в группу с ним не входит, чтобы ключ его
   правила остался прежним. */
.tm-invite + .p-error { margin: 4px 0 0; font-size: 12px; line-height: 1.45; color: var(--red-d); }
.tm-ready { margin-top: 24px; padding: 14px; border-radius: 16px; background: var(--soft); }
.tm-ready .hint { margin: 0 0 10px; }
.tm-ready .field { margin: 0; }
/* Поле `--soft` на плашке `--soft` пропало бы: белое на сером, как
   `.file .fi` (макет:185). */
.tm-ready .field-input { background: var(--panel); }

@media (prefers-reduced-motion: reduce) {
  /* Правило макета (макет:540) и псевдоэлементы: до них `*` не достаёт,
     а анимации у них есть. `!important` — потому что `*` не даёт
     специфичности и иначе уступил бы любому классу. Страж —
     test_reduced_motion_stills_every_animation. */
  *, *::before, *::after { animation: none !important; transition: none !important; }
  /* Своё (спека 2026-10-29 §3.2 п. 2): поворот знака при наведении — не
     анимация, сброс выше его не гасит. `!important` — правило наведения
     контейнера специфичнее. Страж — test_reduced_motion_keeps_the_mark_still. */
  .logo-mark { transform: none !important; }
}
