/* Base: reset, typography, shared components (buttons, fields, theme switch, badges). */

*,
*::before,
*::after { box-sizing: border-box; }

html {
  -webkit-text-size-adjust: 100%;
  text-size-adjust: 100%;
  /* The scrollbar's place is kept on short pages too: otherwise the header shifts sideways by its width when a
     screen without scroll follows one with it (Max 13.10). */
  scrollbar-gutter: stable;
}

/* A modal window holds the page still (Max 29.10: the wheel over «Смотреть с этого места» scrolled the note behind).
   Lenis leaves an open dialog alone, but a dialog with nothing to scroll hands the wheel on to the page — the
   browser's scroll chaining, which showModal does not stop. One rule for every <dialog> opened with showModal; the
   gutter above keeps the layout from jumping sideways. A popover (the palette) is not modal: only its own wheel is
   contained — overflow: hidden under it threw scrollY to 0. Lenis' own .lenis-stopped is not needed. */
html:has(dialog:modal),
html:has(dialog:modal) body {
  overflow: hidden;
}
dialog, [popover] { overscroll-behavior: contain; }

body {
  margin: 0;
  min-height: 100dvh;
  background: var(--bg);
  color: var(--text);
  font-family: var(--font-ui);
  font-size: var(--fs-16);
  line-height: 1.5;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  overflow-x: hidden;
}

/* Атрибут hidden обязан выигрывать у display компонента. Правило браузера по
   умолчанию — `[hidden] { display: none }`, и любой класс со своим display
   (.btn, .field, .sc-export) его перебивает по специфичности: элемент честно
   помечен спрятанным и при этом виден. Так и жила кнопка «Эта неделя» на
   расписании — `hidden = true` при `display: flex`. Точечные заплатки на это
   в проекте уже есть (`.nt-layout > [hidden]`, `.fix-pop[hidden]`); правило
   здесь закрывает случай целиком, и заводить третью копию не придётся. */
[hidden] { display: none !important; }

/* A screen is exactly 100dvh when its content fits, so anything poking past its box — an entrance still shifted
   down, a hidden menu or popup, a glow — made the page scroll a few pixels back and forth (Max 13.10). Content in
   the flow still grows #app; only overhang is cut. clip, not hidden: no scroll container, sticky bars keep working,
   and position: fixed children are not clipped. */
#app { overflow: clip; }

img, svg { display: block; max-width: 100%; }
button, input, select { font: inherit; color: inherit; }
/* Без этого узкое `border`/`background` каждого класса кнопки (.btn, .icon-btn,
   .ac-tabs__btn и т.д.) не перебивает браузерный 3D-бевел <button> сам по себе —
   он остаётся, если конкретный класс забыл своё border/background объявить
   (так и было с .lib-note__pin и .ac-tabs__btn — «грани», 27.09). Здесь — один
   раз и для всех кнопок сразу; любой класс со своим border/background
   по-прежнему выигрывает по специфичности и рисует что хочет. */
button { border: 0; background: none; appearance: none; }
button { cursor: pointer; }
a { color: var(--accent-text); text-decoration: none; }
a:hover { text-decoration: underline; text-underline-offset: 3px; }

h1, h2, h3 {
  margin: 0;
  font-family: var(--font-display);
  font-weight: 800;
  line-height: 1.12;
  letter-spacing: -0.02em;
  text-wrap: balance;
}
p { margin: 0; text-wrap: pretty; }

::selection { background: var(--brand); color: #FFFFFF; }

:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
  border-radius: var(--r-sm);
}

.sr-only {
  position: absolute;
  width: 1px; height: 1px;
  padding: 0; margin: -1px;
  overflow: hidden;
  clip: rect(0 0 0 0);
  white-space: nowrap;
  border: 0;
}

.container {
  width: 100%;
  max-width: var(--container);
  margin-inline: auto;
  padding-inline: var(--gutter);
}

/* Пока идёт смена темы: ни одного перехода и ни одной бесконечной анимации.
   Класс ставит theme.js на время раскрытия и снимает после него. */
html.theme-switching *,
html.theme-switching *::before,
html.theme-switching *::after {
  transition: none !important;
  animation-play-state: paused !important;
}

/* Smooth theme fallback when View Transitions API is unavailable */
html.theme-fade,
html.theme-fade *,
html.theme-fade *::before,
html.theme-fade *::after {
  transition:
    background-color 300ms var(--ease-in-out),
    border-color 300ms var(--ease-in-out),
    color 300ms var(--ease-in-out),
    fill 300ms var(--ease-in-out),
    box-shadow 300ms var(--ease-in-out) !important;
}

::view-transition-old(root),
::view-transition-new(root) {
  animation: none;
  mix-blend-mode: normal;
}
/* The reveal overlay lets clicks through: otherwise a tap on the theme switch during it hit the overlay and was lost. */
::view-transition { pointer-events: none; }

/* ---------- Logo ---------- */
.logo {
  --logo-gap: 0.32em;
  display: inline-flex;
  align-items: baseline;
  gap: var(--logo-gap);
  color: var(--text);
  font-family: var(--font-display);
  font-size: var(--fs-18);
  font-weight: 800;
  letter-spacing: -0.02em;
  line-height: 1;
  text-decoration: none;
  white-space: nowrap;
}
.logo:hover { text-decoration: none; }
/* 900-1279px: шапка = лого + строка разделов (db-nav) + правая кучка в один
   ряд без переноса (dashboard.css, palette.css), и на этом диапазоне месту
   уже тесно даже без «Ctrl K» — Влад, 25.09, «у меня узкое окно» (не таббар,
   а именно этот зажатый десктопный ряд). Слово рядом со значком первое, что
   можно снять без потери смысла: значок сам по себе узнаваем. */
@media (min-width: 900px) and (max-width: 1279px) {
  .logo__word { display: none; }
}
/* A variable, not a descendant rule: surfaces (and the glitch channels) retint the mark
   by setting --logo-mark, without a specificity fight */
.logo__mark { color: var(--logo-mark, var(--accent-text)); }

/* The head mark (App.logo). Fills go through --lm-* so a surface can repaint it without a
   specificity fight: the brand stage turns it white, the glitch channels flatten it to one
   colour. Defaults are the critter's own tokens, so it follows the theme with him. */
.logo__mark { display: inline-flex; flex: none; align-self: center; position: relative; top: -0.12em; }
.lm { display: block; width: 1.29em; height: 1.5em; max-width: none; overflow: visible; }
.lm-B { fill: var(--lm-body, var(--mc-body)); }
.lm-D { fill: var(--lm-shade, var(--mc-shade)); }
.lm-W { fill: var(--lm-eye, var(--mc-eye)); }
.lm-E { fill: var(--lm-burnt, var(--mc-line)); }
.lm-K { fill: var(--lm-ink, var(--mc-ink)); }
.lm-C { fill: var(--lm-fringe, var(--mc-line)); }
.lm-A { fill: var(--lm-tearfringe, var(--brand-300)); }
.lm-L { fill: var(--lm-blush, var(--mc-blush)); }
.lm-P { fill: var(--lm-paper, var(--mc-paper)); }
.lm-pl { transform: translateX(var(--lm-lx, 0px)); }
.lm-pr { transform: translateX(var(--lm-rx, 0px)); }
.lm-pl,
.lm-pr { transition: transform 120ms steps(1, end); }
/* Hover glitch (glitch.js, level micro) throws the torn band around for a beat. */
.logo__mark.is-twitch .lm-tear { animation: lm-tear 180ms steps(1, end); }
.logo__mark.is-twitch .lm-E { animation: lm-burn 180ms steps(1, end); }
@keyframes lm-tear {
  0% { transform: translateX(3px); }
  33% { transform: translateX(-2px); }
  66% { transform: translateX(2px); }
}
@keyframes lm-burn {
  0% { fill: var(--lm-ink, var(--mc-ink)); }
  50% { fill: var(--lm-burnt, var(--mc-line)); }
}
@media (prefers-reduced-motion: reduce) {
  .logo__mark.is-twitch .lm-tear,
  .logo__mark.is-twitch .lm-E { animation: none; }
}

/* ---------- Buttons ---------- */
.btn {
  --btn-bg: var(--surface-2);
  --btn-fg: var(--text);
  position: relative;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--s-2);
  min-height: 48px;
  padding: 0 var(--s-5);
  border: 1px solid transparent;
  border-radius: var(--r-md);
  background: var(--btn-bg);
  color: var(--btn-fg);
  font-family: var(--font-ui);
  font-size: 0.9375rem;
  font-weight: 500;
  line-height: 1;
  white-space: nowrap;
  user-select: none;
  transition:
    background-color var(--dur-micro) var(--ease-out),
    box-shadow var(--dur-state) var(--ease-out),
    transform var(--dur-micro) var(--ease-out),
    opacity var(--dur-micro) var(--ease-out);
}
.btn svg { width: 18px; height: 18px; flex: none; }
/* Отдельное свойство scale, а не transform: transform у кнопки может
   принадлежать motion.magnetic, и два хозяина одного свойства дерутся. */
.btn:active { scale: 0.98; }
.btn:disabled,
.btn[aria-disabled="true"] {
  opacity: 0.45;
  cursor: not-allowed;
  transform: none;
}

.btn--primary {
  --btn-bg: var(--accent);
  --btn-fg: var(--on-accent);
}
.btn--primary:not(:disabled):hover {
  --btn-bg: var(--accent-hover);
  box-shadow: var(--glow-accent);
}
.btn--primary .btn__arrow { transition: transform var(--dur-state) var(--ease-out); }
.btn--primary:not(:disabled):hover .btn__arrow { transform: translateX(3px); }

.btn--ghost {
  --btn-bg: transparent;
  --btn-fg: var(--text-2);
  border-color: var(--border-strong);
}
.btn--ghost:hover { --btn-bg: var(--surface-2); --btn-fg: var(--text); }

.btn--lg { min-height: 56px; padding: 0 30px; border-radius: var(--r-lg); font-size: var(--fs-16); }
.btn--sm { min-height: 40px; padding: 0 18px; border-radius: var(--r-sm); font-size: var(--fs-14); }
.btn--block { width: 100%; }
/* Под пальцем 36px — промах через раз. Мышью маленькая кнопка удобна, поэтому
   растёт только на сенсорных экранах. */
@media (pointer: coarse) {
  .btn--sm { min-height: 44px; }
}

.btn.is-loading { color: transparent; pointer-events: none; }
.btn.is-loading::after {
  content: "";
  position: absolute;
  width: 20px; height: 20px;
  border: 2px solid var(--on-accent);
  border-right-color: transparent;
  border-radius: 50%;
  animation: spin 700ms linear infinite;
}
@keyframes spin { to { transform: rotate(360deg); } }

.icon-btn {
  display: inline-grid;
  place-items: center;
  flex-shrink: 0;
  width: 44px; height: 44px;
  border: 1px solid var(--border);
  border-radius: var(--r-md);
  background: var(--surface);
  color: var(--text-2);
  transition: color var(--dur-micro) var(--ease-out), border-color var(--dur-micro) var(--ease-out), background-color var(--dur-micro) var(--ease-out);
}
.icon-btn:hover { color: var(--text); border-color: var(--border-strong); }
.icon-btn svg { width: 18px; height: 18px; }
@media (pointer: coarse) {
  .icon-btn { width: 44px; height: 44px; }
}

/* ---------- Fields ---------- */
.field { display: grid; gap: var(--s-2); }
.field__label {
  font-size: var(--fs-14);
  font-weight: 600;
  color: var(--text);
}
.field__control { position: relative; }
.field__icon {
  position: absolute;
  left: 16px; top: 50%;
  width: 18px; height: 18px;
  transform: translateY(-50%);
  color: var(--text-3);
  pointer-events: none;
  transition: color var(--dur-micro) var(--ease-out);
}
.input {
  width: 100%;
  min-height: 48px;
  padding: 0 var(--s-4);
  border: 1px solid var(--border-strong);
  border-radius: var(--r-sm);
  background: var(--surface);
  color: var(--text);
  font-size: var(--fs-16);
  box-shadow: var(--shadow-inset);
  transition:
    border-color var(--dur-micro) var(--ease-out),
    box-shadow var(--dur-state) var(--ease-out),
    background-color var(--dur-micro) var(--ease-out);
}
.field__icon + .input { padding-left: 46px; }
.input::placeholder { color: var(--text-3); opacity: 1; }
.input:hover { border-color: var(--text-3); }
.input:focus {
  outline: none;
  border-color: var(--accent);
  box-shadow: var(--focus-ring);
}
.field__control:focus-within .field__icon { color: var(--accent-text); }
.input[aria-invalid="true"] { border-color: var(--danger); }
.input[aria-invalid="true"]:focus { box-shadow: 0 0 0 3px color-mix(in srgb, var(--danger) 25%, transparent); }
[data-theme="dark"] .input { background: var(--surface-2); }

.field__suffix {
  position: absolute;
  right: 6px; top: 50%;
  transform: translateY(-50%);
  border: 0;
  background: transparent;
  width: 40px; height: 40px;
  display: grid; place-items: center;
  border-radius: var(--r-sm);
  color: var(--text-3);
}
.field__suffix:hover { color: var(--text); }
.field__suffix svg { width: 18px; height: 18px; }

.field__error {
  min-height: 1.25em;
  font-size: var(--fs-14);
  color: var(--danger-text);
}
.field__hint { font-size: var(--fs-14); color: var(--text-3); }

/* ---------- Theme switch ---------- */
.theme-switch {
  position: relative;
  display: inline-grid;
  grid-template-columns: 1fr 1fr;
  align-items: center;
  width: 76px; height: 40px;
  padding: 4px;
  border: 1px solid var(--border);
  border-radius: var(--r-full);
  background: var(--surface);
  box-shadow: var(--shadow-sm);
  color: var(--text-3);
}
.theme-switch__knob {
  position: absolute;
  top: 3px; left: 3px;
  width: 32px; height: 32px;
  border-radius: 50%;
  background: var(--accent);
  box-shadow: var(--glow-accent);
  transition: transform 420ms var(--ease-out);
}
[data-theme="dark"] .theme-switch__knob { transform: translateX(36px); }
.theme-switch__icon {
  position: relative;
  display: grid; place-items: center;
  height: 100%;
  transition: color var(--dur-state) var(--ease-out), transform 420ms var(--ease-out);
}
.theme-switch__icon svg { width: 16px; height: 16px; }
:root:not([data-theme="dark"]) .theme-switch__icon--sun,
[data-theme="dark"] .theme-switch__icon--moon { color: #FFFFFF; }
[data-theme="dark"] .theme-switch__icon--sun { transform: rotate(-90deg) scale(0.85); }
:root:not([data-theme="dark"]) .theme-switch__icon--moon { transform: rotate(30deg) scale(0.85); }
.theme-switch:hover { color: var(--text); }
/* На телефоне в шапке пилюля 76px съедала место логотипа, а тему меняют раз
   в месяц. Там это одна круглая кнопка с иконкой текущей темы — тот же
   переключатель и тот же тап, просто без второй половины. На онбординге шапка
   пустая, и пилюля остаётся как есть. */
@media (max-width: 599px) {
  [class*="-top__right"] .theme-switch {
    width: 40px;
    grid-template-columns: 1fr;
    padding: 0;
    border-radius: var(--r-md);
    box-shadow: none;
  }
  [class*="-top__right"] .theme-switch__knob { display: none; }
  :root:not([data-theme="dark"]) [class*="-top__right"] .theme-switch__icon--moon,
  [data-theme="dark"] [class*="-top__right"] .theme-switch__icon--sun { display: none; }
  :root:not([data-theme="dark"]) [class*="-top__right"] .theme-switch__icon--sun,
  [data-theme="dark"] [class*="-top__right"] .theme-switch__icon--moon { color: var(--text-2); }
  [class*="-top__right"] .theme-switch__icon svg { width: 18px; height: 18px; }
}
@media (max-width: 599px) and (pointer: coarse) {
  [class*="-top__right"] .theme-switch { width: 44px; height: 44px; }
}

/* ---------- Badges ---------- */
.badge {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  min-height: 28px;
  padding: 0 12px;
  border-radius: var(--r-full);
  background: var(--accent-soft);
  color: var(--accent-ink);
  font-size: 0.8125rem;
  font-weight: 500;
  line-height: 1;
  white-space: nowrap;
}
.badge svg { width: 14px; height: 14px; }
.badge--neutral { background: var(--surface-2); color: var(--text-2); }
.badge--live { background: var(--live); color: var(--live-ink); }
/* Жили в homework.css, пока были нужны одному экрану. Теперь их рисуют три,
   и общий контрол в файле одного экрана ломается при первой же правке того экрана. */
.badge--ok { background: var(--success-soft); color: var(--text); }
.badge--ok svg { color: var(--success); }
.badge--warn { background: var(--warning-soft); color: var(--text); }
.badge--warn svg { color: var(--warning); }
/* «Новое» — фиолетовым акцентом: один акцент на продукт (редизайн 1.1). */
.badge--new { background: var(--accent-soft); color: var(--accent-ink); }

.pulse-dot {
  position: relative;
  width: 8px; height: 8px;
  border-radius: 50%;
  background: currentColor;
}
.pulse-dot::after {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: 50%;
  background: currentColor;
  animation: pulse 1.8s var(--ease-out) infinite;
}
@keyframes pulse {
  from { transform: scale(1); opacity: 0.6; }
  to { transform: scale(3); opacity: 0; }
}

/* Мелкие номера и коды. Раньше — пиксельный шрифт, но на 10–13px он ломает
   цифры (2 и 5 читаются как 8), а кириллицы в Silkscreen нет вовсе
   (ROADMAP-26.09 §13.6). Пиксельную фактуру дают картинки из js/pixel.js. */
.pixel {
  font-family: var(--font-display);
  font-weight: 800;
  font-size: var(--fs-12);
  letter-spacing: 0.04em;
}

/* ---------- Цвет предмета ---------- */
/* Класс ставит App.utils.subjectClass(subject). Цвет предмета — метка, а не
   заливка (редизайн 1.1): точка, тонкая полоса или код предмета. --subj —
   сам цвет (точки, полосы), --subj-text — он же, читаемый как текст на фоне
   темы. Карточки и шапки им не красятся. */
.s-iris   { --subj: var(--iris);  --subj-text: var(--iris-text);   --subj-on: #FFFFFF;    --subj-soft: var(--iris-soft); --subj-ink: var(--iris-ink); }
.s-teal   { --subj: var(--teal);  --subj-text: var(--teal-text);   --subj-on: var(--ink); --subj-soft: var(--teal-soft); --subj-ink: var(--teal-ink); }
.s-lime   { --subj: var(--lime);  --subj-text: var(--lime-text);   --subj-on: var(--ink); --subj-soft: var(--lime-soft); --subj-ink: var(--lime-ink); }
.s-heather { --subj: var(--heather);  --subj-text: var(--heather-text);   --subj-on: var(--ink); --subj-soft: var(--heather-soft); --subj-ink: var(--heather-ink); }
.s-tang   { --subj: var(--tang);  --subj-text: var(--tang-text);   --subj-on: var(--ink); --subj-soft: var(--tang-soft); --subj-ink: var(--tang-ink); }
.s-blue   { --subj: var(--blue);  --subj-text: var(--blue-text);   --subj-on: var(--ink); --subj-soft: var(--blue-soft); --subj-ink: var(--blue-ink); }

/* Плашка предмета (App.utils.subjectTile): код на нейтральном фоне, код —
   цветом предмета. Двойной класс перебивает фон плашки из файла экрана,
   размеры и скругление остаются её. */
.subj-tile.subj-tile {
  display: inline-grid;
  place-items: center;
  flex: none;
  background: var(--surface-2);
  color: var(--subj-text, var(--accent-text));
  font-family: var(--font-display);
  font-size: 0.75rem;
  font-weight: 800;
  letter-spacing: 0.02em;
}

/* Чип предмета: нейтральный, цвет — только точка перед названием. */
.subj-chip {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  padding: 3px 10px 3px 8px;
  border-radius: var(--r-full);
  background: var(--surface-2);
  color: var(--text);
  font-size: var(--fs-12);
  font-weight: 600;
  line-height: 1.4;
  white-space: nowrap;
}
.subj-chip::before { content: ""; width: 8px; height: 8px; border-radius: 50%; background: var(--subj, var(--text-3)); flex: none; }

/* Состояние агента (App.pixel.agentStage): дашборд, экран «Агент», мастер,
   кабинет. Редизайн 2: плитка с линейной иконкой вместо сцены с маскотом.
   Точка в углу — статус: серая ждёт пару, красная мигает — пишет. */
.agent-stage {
  position: relative;
  display: grid;
  place-items: center;
  width: 56px;
  height: 56px;
  border-radius: 16px;
  background: var(--surface-2);
  color: var(--text);
}
.agent-stage svg { width: 26px; height: 26px; }
.agent-stage.is-off { color: var(--text-3); }
.agent-stage__dot { position: absolute; top: 8px; right: 8px; width: 9px; height: 9px; border-radius: 50%; background: transparent; }
.agent-stage.is-wait .agent-stage__dot { background: var(--success); }
.agent-stage.is-live .agent-stage__dot { background: #FF453A; animation: agent-stage-rec 1.6s infinite; }
@keyframes agent-stage-rec { 50% { opacity: 0.25; } }
@media (prefers-reduced-motion: reduce) { .agent-stage__dot { animation: none !important; } }


/* ---------- Toggle switch ---------- */
/* Общий контрол: его рисуют дашборд, расписание, настройки и конспект.
   Жил в dashboard.css, из-за чего правка одного экрана задевала четыре. */
.toggle {
  display: inline-flex;
  align-items: center;
  gap: var(--s-3);
  border: 0;
  padding: 0;
  background: transparent;
  color: var(--text);
  font-size: var(--fs-16);
  font-weight: 400;
}
.toggle__track {
  position: relative;
  flex: none;
  width: 52px;
  height: 32px;
  border-radius: var(--r-full);
  background: var(--border-strong);
  transition: background-color var(--dur-state) var(--ease-out);
}
.toggle__track::after {
  content: "";
  position: absolute;
  top: 3px;
  left: 3px;
  width: 26px;
  height: 26px;
  border-radius: 50%;
  background: #FFFFFF;
  box-shadow: 0 2px 6px rgba(0, 0, 0, 0.2);
  transition: transform var(--dur-state) var(--ease-out);
}
.toggle[aria-checked="true"] .toggle__track { background: var(--accent); }
.toggle[aria-checked="true"] .toggle__track::after { transform: translateX(20px); }

/* ---------- Галочка согласия ---------- */
/* Одна на все места, где студент что-то разрешает: вход, онбординг, согласие агента,
   разрешения в мастере. Было три копии с тремя именами классов, и мастер ссылался на
   несуществующую — его галочки рисовались голыми. Плоскость без обводки (Редизайн 3). */
.agree {
  position: relative;
  display: grid;
  grid-template-columns: auto minmax(0, 1fr);
  align-items: start;
  gap: var(--s-3);
  padding: 14px var(--s-4);
  border-radius: var(--r-lg);
  background: var(--surface-2);
  cursor: pointer;
  transition: background-color var(--dur-micro) var(--ease-out);
}
.agree:hover { background: color-mix(in srgb, var(--surface-2) 86%, var(--text)); }
.agree input { position: absolute; opacity: 0; pointer-events: none; }
.agree__mark {
  display: grid;
  place-items: center;
  width: 22px;
  height: 22px;
  margin-top: 1px;
  border: 1.5px solid var(--border-strong);
  border-radius: 7px;
  background: var(--surface);
  color: transparent;
  transition: background-color var(--dur-micro) var(--ease-out), border-color var(--dur-micro) var(--ease-out), color var(--dur-micro) var(--ease-out);
}
.agree__mark svg { width: 14px; height: 14px; }
.agree__text { color: var(--text); font-size: var(--fs-14); line-height: 1.5; }
.agree__text small { display: block; margin-top: 2px; color: var(--text-2); font-size: var(--fs-12); }
.agree:has(input:checked) { background: var(--accent-soft); }
.agree:has(input:checked) .agree__mark {
  background: var(--accent);
  border-color: var(--accent);
  color: var(--on-accent);
}
.agree:has(input:focus-visible) { outline: 2px solid var(--accent); outline-offset: 2px; }
.agree.is-invalid .agree__mark { border-color: var(--danger); }
.agrees { display: grid; gap: var(--s-2); }

/* ---------- Segmented control ---------- */
/* Вторая форма выбора в продукте: тумблер отвечает «да/нет», сегмент — «одно из
   нескольких». Жил в notes.css как .lib-modes и переехал сюда, когда тот же
   контрол понадобился блоку задания: общие контролы живут в base.css, иначе
   правка одного экрана чинит или ломает второй вслепую. */
.seg {
  position: relative;
  isolation: isolate;
  display: inline-grid;
  grid-auto-flow: column;
  gap: var(--s-1);
  padding: var(--s-1);
  border: 1px solid var(--border);
  border-radius: var(--r-md);
  background: var(--surface);
}
.seg__btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--s-2);
  min-height: 36px;
  padding: 0 16px;
  border: 0;
  border-radius: var(--r-sm);
  background: transparent;
  color: var(--text-2);
  font-size: var(--fs-14);
  font-weight: 500;
  white-space: nowrap;
  transition: background-color var(--dur-micro) var(--ease-out), color var(--dur-micro) var(--ease-out);
}
.seg__btn svg { width: 16px; height: 16px; }
.seg__btn:hover { color: var(--text); }
.seg__btn[aria-pressed="true"],
.seg__btn[aria-selected="true"] { color: var(--text); }
/* Нажатие чувствуется пальцем. Отдельным свойством scale, не transform:
   transform у кнопок бывает занят (см. правило «один хозяин»). */
.seg__btn:not(:disabled):active { scale: 0.96; }
.seg__btn:disabled { opacity: 0.4; cursor: not-allowed; }
.seg__btn:disabled:hover { color: var(--text-2); }

/* Бегунок — выбранный пункт, который ездит. Рисуется под кнопками одним
   слоем на весь контрол: фон активной кнопки больше не красится сам, иначе
   на месте выбора оказалось бы две подложки — старая, гаснущая, и новая.
   Позиция, размер и transform принадлежат motion.segment() и больше никому:
   CSS их не анимирует, иначе два хозяина одного свойства дерутся.
   До первого замера бегунок прозрачен — он не должен мелькнуть в левом углу. */
.seg__pill {
  position: absolute;
  top: 0;
  left: 0;
  z-index: -1;
  width: 0;
  height: 0;
  border: 1px solid var(--border);
  border-radius: var(--r-sm);
  background: var(--surface-2);
  box-shadow: var(--shadow-sm), var(--shadow-inset);
  opacity: 0;
  pointer-events: none;
  will-change: transform, width, height;
}
/* Без JS или без замера — кнопка остаётся понятной и сама по себе. */
.seg:not(:has(.seg__pill)) > [aria-pressed="true"],
.seg:not(:has(.seg__pill)) > [aria-selected="true"] { background: var(--surface-2); }

/* Трек и бегунок разведены по высоте, а не по оттенку одного серого: в светлой
   теме трек утоплен (surface-2), бегунок поднят (surface-raised + тень) — это
   предмет, который лежит сверху и ездит. В тёмной теней нет по токенам, и
   подъём делается светлотой: трек — surface, бегунок — surface-2. */
.seg { background: var(--surface-2); }
.seg__pill { background: var(--surface-raised); border-color: transparent; }
[data-theme="dark"] .seg { background: var(--surface); }
[data-theme="dark"] .seg__pill { background: var(--surface-2); border-color: var(--border); }

/* Многострочное поле. Высота у .input фиксированная под одну строку, а
   многострочному тексту нужна своя — поэтому модификатор, а не второй контрол. */
.input--area {
  min-height: 120px;
  padding: var(--s-3) var(--s-4);
  font-family: var(--font-ui);
  font-size: var(--fs-14);
  line-height: 1.6;
  resize: vertical;
}

/* ---------- Spotlight surface (hover light follows the cursor) ---------- */
.spot {
  --mx: 50%;
  --my: 50%;
  position: relative;
  isolation: isolate;
}
.spot::before,
.spot::after {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: inherit;
  pointer-events: none;
  opacity: 0;
  transition: opacity var(--dur-state) var(--ease-out);
}
.spot::before {
  z-index: -1;
  background: radial-gradient(420px circle at var(--mx) var(--my), var(--spot), transparent 60%);
}
.spot::after {
  padding: 1px;
  background: radial-gradient(260px circle at var(--mx) var(--my), var(--spot-border), transparent 65%);
  -webkit-mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
  -webkit-mask-composite: xor;
  mask: linear-gradient(#000 0 0) content-box exclude, linear-gradient(#000 0 0);
}
@media (hover: hover) and (pointer: fine) {
  .spot:hover::before,
  .spot:hover::after { opacity: 1; }
}

/* ---------- Avatar image ---------- */
/* Своя аватарка заполняет тот же кружок, что и инициалы: контейнеры обрезают по
   своему радиусу, картинка просто растягивается на всю площадь. */
.avatar-btn,
.pf-id__avatar,
.st-id__avatar {
  position: relative;
  overflow: hidden;
}
/* Абсолютно и на всю площадь: в grid-контейнере с place-items: center картинка
   с width/height: 100% садилась в центр ячейки и оставляла по краям кайму —
   фото выходило чуть меньше кружка с инициалами. Так оно перекрывает его
   целиком, и кружок с буквами и с фото всегда одного размера. */
.avatar-img {
  position: absolute;
  inset: 0;
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
  border-radius: inherit;
}

/* ---------- Skeleton ---------- */
.skeleton {
  display: block;
  border-radius: var(--r-sm);
  background: linear-gradient(90deg, var(--surface-2) 0%, var(--border) 50%, var(--surface-2) 100%);
  background-size: 200% 100%;
  animation: shimmer 1.4s linear infinite;
}
@keyframes shimmer { to { background-position: -200% 0; } }

/* ---------- Toast ---------- */
.toast {
  position: fixed;
  left: 50%;
  bottom: calc(var(--s-5) + env(safe-area-inset-bottom, 0px));
  z-index: var(--z-toast);
  max-width: calc(100vw - 2 * var(--gutter));
  padding: var(--s-3) var(--s-4);
  border: 1px solid var(--border);
  border-radius: var(--r-md);
  background: var(--surface-raised);
  color: var(--text);
  box-shadow: var(--shadow-md);
  font-size: var(--fs-14);
  transform: translate(-50%, 16px);
  opacity: 0;
  pointer-events: none;
  transition: transform var(--dur-state) var(--ease-out), opacity var(--dur-state) var(--ease-out);
}
.toast.is-visible { transform: translate(-50%, 0); opacity: 1; }

/* ---------- Сервер не отвечает (App.platform, событие platform:reach) ---------- */
/* In the flow above #app, not over it: covers nothing on a phone or a desktop. */
.netdown {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: center;
  gap: var(--s-2) var(--s-4);
  padding: var(--s-2) var(--gutter);
  padding-top: calc(var(--s-2) + env(safe-area-inset-top, 0px));
  background: var(--warning-soft);
  color: var(--text);
  font-size: var(--fs-14);
  line-height: 1.4;
}
.netdown[hidden] { display: none; }
.netdown__text { min-width: 0; overflow-wrap: anywhere; }

/* ---------- «Почему так» (App.utils.why) ---------- */
.why {
  max-width: 62ch;
  color: var(--text-3);
  font-size: var(--fs-14);
  line-height: 1.6;
}
.why__sum {
  display: inline-flex;
  align-items: center;
  gap: var(--s-2);
  min-height: 32px;
  color: var(--text-2);
  font-weight: 600;
  cursor: pointer;
  list-style: none;
  user-select: none;
  transition: color var(--dur-micro) var(--ease-out);
}
.why__sum::-webkit-details-marker { display: none; }
.why__sum svg { width: 16px; height: 16px; flex: none; color: var(--text-3); }
/* The arrow turns up when open: the hint folds back the same way it opened (§47). */
.why__sum svg:last-child { transition: transform var(--dur-micro) var(--ease-out); }
.why[open] > .why__sum svg:last-child { transform: rotate(180deg); }
.why__sum:hover,
.why[open] > .why__sum { color: var(--text); }
.why__body { display: grid; gap: var(--s-2); padding-top: var(--s-1); }
.why__body strong { color: var(--text-2); }
@media (pointer: coarse) {
  .why__sum { min-height: 44px; }
}

/* ---------- Нижний таббар (телефон) ---------- */
/* Один на все корневые экраны разделов, рисует его App.nav.syncTabbar().
   --tabbar-offset — высота бара без safe-area: на неё поднимаются всё, что
   прижато к низу (тост, баннер установки), и отступ под контентом. */
.tabbar { display: none; }
@media (max-width: 899px) {
  /* The bar floats (iOS 26): its top is its gap from the edge + the 62px pill. Everyone adds the safe area to this
     offset on their own, so it is taken back out here: whatever is pinned to the bottom lands 10px over the bar. */
  body.has-tabbar { --tabbar-offset: calc(max(10px, env(safe-area-inset-bottom, 0px) - 14px) + 72px - env(safe-area-inset-bottom, 0px)); }
  body.has-tabbar #app { padding-bottom: calc(var(--tabbar-offset) + env(safe-area-inset-bottom, 0px)); }
  /* Every screen is at least 100dvh tall; with the bar's room added on top a page that fits still scrolled by the
     bar's height. The screen gives that room back. */
  body.has-tabbar #app > :first-child { min-height: calc(100dvh - var(--tabbar-offset) - env(safe-area-inset-bottom, 0px)); }
  body.has-tabbar .toast { bottom: calc(var(--tabbar-offset) + var(--s-3) + env(safe-area-inset-bottom, 0px)); }

  .tabbar {
    position: fixed;
    left: 12px;
    right: 12px;
    /* Into the home indicator's zone, as the iOS 26 bar sits (Max 27.10: 10px on top of the whole 34px safe area
       hung it too high): 20px from the edge on a notched iPhone, 10px where there is no safe area. */
    bottom: max(10px, calc(env(safe-area-inset-bottom, 0px) - 14px));
    z-index: var(--z-sticky);
    display: grid;
    grid-auto-flow: column;
    grid-auto-columns: minmax(0, 1fr);
    padding: 4px;
    border: 0;
    border-radius: var(--r-full);
    background: var(--surface);
    /* The finger drags along the bar (js/tabbar.js): no page scroll, no selection, no callout under it. */
    touch-action: none;
    user-select: none;
    -webkit-user-select: none;
    -webkit-touch-callout: none;
  }
  .tabbar[hidden] { display: none; }
}

/* The bubble under the current tab. At rest a soft tint; under the finger it swells into a clear lens and rides
   with it 1:1 (no transition while dragging), let go — it springs onto the tab. */
.tabbar__glass {
  position: absolute;
  top: 4px;
  bottom: 4px;
  left: 0;
  width: var(--tb-w, 0px);
  translate: var(--tb-x, 0px) 0;
  border-radius: var(--r-full);
  background: color-mix(in srgb, var(--accent) 13%, transparent);
  opacity: 0;
  pointer-events: none;
  transition:
    translate 520ms var(--ease-spring-soft),
    width 520ms var(--ease-spring-soft),
    scale 340ms var(--ease-spring-soft),
    background-color 200ms ease,
    box-shadow 200ms ease,
    opacity 160ms ease;
}
.tabbar.has-glass .tabbar__glass { opacity: 1; }
.tabbar.is-scrub .tabbar__glass {
  scale: 1.16 1.3;
  background: color-mix(in srgb, var(--surface-raised) 14%, transparent);
  -webkit-backdrop-filter: blur(2px) saturate(1.6) brightness(1.06);
  backdrop-filter: blur(2px) saturate(1.6) brightness(1.06);
  box-shadow:
    inset 0 1px 1px rgb(255 255 255 / 0.85),
    inset 0 -1px 1px rgb(255 255 255 / 0.4),
    inset 0 0 0 0.5px rgb(255 255 255 / 0.5),
    inset 0 0 14px rgb(255 255 255 / 0.25),
    0 6px 18px -6px color-mix(in srgb, var(--plum) 45%, transparent);
}
.tabbar.is-scrub.has-lens .tabbar__glass {
  -webkit-backdrop-filter: url(#tb-lens) saturate(1.6) brightness(1.06);
  backdrop-filter: url(#tb-lens) saturate(1.6) brightness(1.06);
}
.tabbar.is-dragging .tabbar__glass { transition: scale 340ms var(--ease-spring-soft), background-color 200ms ease, box-shadow 200ms ease; }
@media (prefers-reduced-motion: reduce) {
  .tabbar__glass { transition: opacity 160ms ease; }
  .tabbar.is-scrub .tabbar__glass { scale: 1; }
}

.tabbar__link {
  position: relative;
  z-index: 1;
  display: grid;
  justify-items: center;
  align-content: center;
  gap: 3px;
  min-height: 54px;
  padding: 0;
  border: 0;
  background: transparent;
  color: var(--text-2);
  font-size: 0.6875rem;
  font-weight: 500;
  letter-spacing: -0.01em;
  white-space: nowrap;
  -webkit-tap-highlight-color: transparent;
}
.tabbar__link svg { width: 22px; height: 22px; transition: transform var(--dur-micro) var(--ease-out); }
.tabbar__link.is-over { color: var(--accent-text); }
.tabbar.is-scrub .tabbar__link[aria-current="page"]:not(.is-over) { color: var(--text-2); font-weight: 500; }
.tabbar__link.is-over svg { transform: scale(1.12); }
.tabbar__link[aria-current="page"] { color: var(--accent-text); font-weight: 700; }
/* Активный пункт отмечен не только цветом: подложка под иконкой видна и тому,
   кто фиолетовый от серого на ярком солнце не отличит. */
.tabbar__link[aria-current="page"]::before {
  content: "";
  position: absolute;
  top: 5px;
  left: 50%;
  width: 48px;
  height: 30px;
  translate: -50% 0;
  border-radius: var(--r-full);
  background: var(--accent-soft);
  z-index: -1;
}
.tabbar__link { isolation: isolate; }
@media (min-width: 400px) {
  .tabbar__link { font-size: 0.75rem; }
}

/* ---------- Ambient brand glow ---------- */
.ambient {
  position: fixed;
  inset: 0;
  z-index: 0;
  overflow: hidden;
  pointer-events: none;
}
.ambient__blob {
  position: absolute;
  width: 60vmax; height: 60vmax;
  border-radius: 50%;
  background: radial-gradient(circle, var(--bg-glow), transparent 65%);
  will-change: transform;
}
.ambient__blob--a { top: -30vmax; right: -20vmax; }
.ambient__blob--b { bottom: -35vmax; left: -25vmax; opacity: 0.7; }

/* ---------- Reduced motion ---------- */
@media (prefers-reduced-motion: reduce) {
  *,
  *::before,
  *::after {
    animation-duration: 1ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 120ms !important;
    scroll-behavior: auto !important;
  }
  .pulse-dot::after { display: none; }
}
