/* ==========================================================================
   Дизайн-система школы.

   Палитра снята с главной фотографии преподавателя: алый фон студии, тёмный
   деним куртки, кремовая водолазка и янтарные блики боке. Все цвета живут
   здесь одним набором переменных — в шаблонах используются компонентные
   классы и точечные arbitrary-значения Tailwind, чтобы источник правды был
   один и на CDN, и в собранном билде.
   ========================================================================== */

:root {
  color-scheme: light;

  --crimson: #d81e27;
  --crimson-deep: #a8141c;
  --crimson-soft: #fbeaea;

  --ink: #14161f;
  --ink-soft: #2a2e3c;
  --ink-muted: #6a6f80;
  /* Синоним для читаемости в новых блоках: раньше `--muted` использовалась,
     но нигде не объявлялась, и текст красился цветом по умолчанию. */
  --muted: #6a6f80;
  --ink-faint: #9aa0b0;

  --cream: #faf6ef;
  --cream-deep: #f1e9dd;
  --paper: #ffffff;
  --line: #e6ddd0;

  --amber: #f0a03c;
  --amber-soft: #fdf1de;

  --success: #1f7a52;
  --success-soft: #e6f4ed;
  --warning: #b26a05;
  --warning-soft: #fdf1de;

  --radius-sm: 10px;
  --radius: 14px;
  --radius-lg: 22px;

  /* Вертикальный ритм. Секции дышат одинаково на всех страницах — разнобой
     в отступах и есть то самое ощущение «элементы висят в воздухе». */
  --section-y: clamp(40px, 4.6vw, 72px);
  --section-y-tight: clamp(28px, 3.4vw, 48px);
  /* Зазор между подложками. Половина сверху, половина снизу — между двумя
     соседними карточками получается двойное значение. */
  --stack-gap: clamp(10px, 1.2vw, 16px);
  --header-h: 69px;
  --gap-grid: clamp(16px, 1.8vw, 24px);
  --measure: 64ch;

  /* Мелкая сетка точек — задник для «шапочных» полос. Держит взгляд и
     показывает, что блок лежит на поверхности, а не в пустоте. */
  --dots: radial-gradient(
    circle at 1px 1px,
    color-mix(in srgb, var(--ink) 7%, transparent) 1px,
    transparent 0
  );

  /* Орнамент поля: звёздочки, галочки и плюсы — «баллы и правильные ответы».
     Крупные, полые внутри, разложены по диагонали. Решётка 45° с шагом,
     кратным плитке, поэтому орнамент стыкуется без швов. */
  --print: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='400' height='400' viewBox='0 0 400 400'%3E%3Cpath d='M0-1C.12-.36.36-.12 1 0C.36.12.12.36 0 1C-.12.36-.36.12-1 0C-.36-.12-.12-.36 0-1Z' fill='none' stroke='%2314161f' stroke-opacity='.16' stroke-width='0.0667' stroke-linejoin='round' transform='translate(50 350) rotate(-18) scale(24)'/%3E%3Cpath d='M-.75 0 L-.2 .6 L.8 -.7' fill='none' stroke='%23d81e27' stroke-opacity='.20' stroke-width='0.0810' stroke-linecap='round' stroke-linejoin='round' transform='translate(50 150) rotate(-9) scale(21)'/%3E%3Cpath d='M-.8 0H.8M0-.8V.8' fill='none' stroke='%2314161f' stroke-opacity='.14' stroke-width='0.0800' stroke-linecap='round' transform='translate(150 250) rotate(0) scale(20)'/%3E%3Ccircle cx='0' cy='0' r='.8' fill='none' stroke='%23f0a03c' stroke-opacity='.26' stroke-width='0.1000' transform='translate(150 50) scale(15)'/%3E%3Cpath d='M0-1C.12-.36.36-.12 1 0C.36.12.12.36 0 1C-.12.36-.36.12-1 0C-.36-.12-.12-.36 0-1Z' fill='none' stroke='%23d81e27' stroke-opacity='.18' stroke-width='0.0842' stroke-linejoin='round' transform='translate(250 150) rotate(18) scale(19)'/%3E%3Cpath d='M-.8 0H.8M0-.8V.8' fill='none' stroke='%2314161f' stroke-opacity='.13' stroke-width='0.0727' stroke-linecap='round' transform='translate(250 350) rotate(27) scale(22)'/%3E%3Cpath d='M-.75 0 L-.2 .6 L.8 -.7' fill='none' stroke='%2314161f' stroke-opacity='.15' stroke-width='0.0850' stroke-linecap='round' stroke-linejoin='round' transform='translate(350 50) rotate(36) scale(20)'/%3E%3Ccircle cx='0' cy='0' r='.8' fill='none' stroke='%23d81e27' stroke-opacity='.22' stroke-width='0.1154' transform='translate(350 250) scale(13)'/%3E%3C/svg%3E");
  --shadow-sm: 0 1px 2px rgb(20 22 31 / 0.06), 0 1px 3px rgb(20 22 31 / 0.04);
  --shadow-md: 0 4px 16px rgb(20 22 31 / 0.08);
  --shadow-lg: 0 18px 48px rgb(20 22 31 / 0.14);
}

/* --------------------------------------------------------------- основа */

html {
  scroll-behavior: smooth;
  height: 100%;
  /* Прокрутка «по карточкам»: колесо останавливается так, что верх следующего
     раздела оказывается под шапкой. proximity, а не mandatory: разделы бывают
     выше экрана, и с mandatory внутри такого раздела остановиться нельзя. */
  scroll-snap-type: y proximity;
  /* Safari на iPhone умеет сам увеличивать кегль в узких колонках — решает,
     что так будет читаться лучше. Решение это принимается мимо CSS, и
     починить последствия нечем: подписи перестают помещаться в карточки,
     а на широком экране всё выглядит целым. Запрещаем. */
  -webkit-text-size-adjust: 100%;
  text-size-adjust: 100%;
}

/* Длинное слово шире колонки — верный способ утащить страницу вбок. На
   телефоне таких слов хватает: адрес почты ученика, ОГРНИП, ссылка на
   классную комнату. Пусть лучше переносятся по буквам, чем ломают раскладку.
   `break-word`, а не `anywhere`: обычный текст переносится как переносился. */
body {
  overflow-wrap: break-word;
}

/* Картинка обязана вписываться в свою колонку, даже если её загрузили
   в исходном размере из админки. Дубль правила Tailwind — на случай, если
   сборка стилей не доехала: тогда широкое фото ломало бы страницу целиком. */
img,
video,
svg {
  max-width: 100%;
}

/* Каркас страницы держится на своём CSS, а не на утилитах Tailwind: подвал
   обязан прижиматься к низу даже если сборка стилей не доехала. */
body {
  min-height: 100%;
  display: flex;
  flex-direction: column;
}

main { flex: 1 0 auto; display: flex; flex-direction: column; }
main > .sheet { flex: 1; display: flex; flex-direction: column; }

/* Тёмная полоса — последняя: лишнюю высоту короткой страницы забирает лист
   над ней, а не зазор между полосой и подвалом. */
.sheet > .section--ink:last-child { margin-top: auto; }

body {
  background-color: var(--cream);
  background-image: var(--print);
  background-size: 400px 400px;
  color: var(--ink);
  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto,
    "Helvetica Neue", Arial, "Noto Sans", sans-serif;
  -webkit-font-smoothing: antialiased;
}

h1, h2, h3, h4 {
  letter-spacing: -0.022em;
  color: var(--ink);
}

::selection {
  background: var(--crimson);
  color: #fff;
}

a {
  color: inherit;
}

.link {
  color: var(--crimson);
  text-underline-offset: 3px;
  text-decoration: underline;
  text-decoration-color: color-mix(in srgb, var(--crimson) 35%, transparent);
}

.link:hover {
  text-decoration-color: var(--crimson);
}

/* Ссылка-действие, стоящая сама по себе: «← к расписанию», «открыть занятие →»,
   «сменить пароль». От обычной `.link` отличается не видом, а положением: она
   не внутри фразы, а отдельной строкой. Разница практическая — строчной ссылке
   в абзаце высоту не добавить, не разогнав межстрочный интервал, а этой можно.
   А добавить надо: в строку высотой 16–21 пиксель палец не попадает.

   Правило целиком под `pointer: coarse` — на широком экране ни одна из этих
   ссылок не меняется. `inline-flex` сужает ссылку до текста, и для тех, что
   были `block`, цель становится уже, но вдвое выше: мимо строки промахиваются
   по вертикали, а не по горизонтали. */
@media (pointer: coarse) {
  .link-action {
    display: inline-flex;
    align-items: center;
    min-height: 44px;
  }
}

/* Заголовок карточки или строки списка, который сам является ссылкой: имя
   ученика в журнале, название урока в программе, курс в кабинете.

   Здесь 24px, а не 44, и это осознанно. Сорок четыре — размер отдельно
   стоящей кнопки; заголовок же стоит вплотную к своим подписям, и лишние
   двадцать пять пикселей высоты растащили бы каждую строку списка. 24px —
   нижняя граница WCAG 2.2 (2.5.8) и ровно то, чего этим заголовкам не
   хватало: они были 17–19 пикселей высотой. По горизонтали они и так широкие
   (176–238px), а промахиваются по таким ссылкам именно по вертикали.

   Промах вдобавок не опасен: вокруг заголовка не соседняя ссылка, а поле
   карточки — палец мимо цели не нажимает ничего чужого. */
@media (pointer: coarse) {
  .link-title {
    display: inline-flex;
    align-items: center;
    min-height: 24px;
  }
}

.muted { color: var(--ink-muted); }
.faint { color: var(--ink-faint); }

/* ------------------------------------------------------------- раскладка */

/* Боковое поле страницы. В `max()` два слагаемых, и отвечают они за разное.

   16px — поле на телефоне. Двадцать пикселей с каждой стороны на экране в
   320px съедали восьмую часть ширины, и строки в карточках ломались по два
   слова. От 480px и шире поле снова 20px — там места достаточно (ниже).

   env(safe-area-inset-*) — вырез и скруглённые углы; обычно ноль, поэтому
   на телефоне без выреза побеждают те самые 16px. В base.html стоит
   viewport-fit=cover, значит содержимое от выреза система больше не
   отодвигает: без этой половины в альбомной ориентации начало строки
   оказалось бы под «островом», а на скруглённом углу — срезано. */
.wrap,
.wrap-narrow {
  width: 100%;
  margin-inline: auto;
  padding-left: max(16px, env(safe-area-inset-left));
  padding-right: max(16px, env(safe-area-inset-right));
}

.wrap { max-width: 1120px; }
.wrap-narrow { max-width: 780px; }

@media (min-width: 480px) {
  .wrap,
  .wrap-narrow {
    padding-left: max(20px, env(safe-area-inset-left));
    padding-right: max(20px, env(safe-area-inset-right));
  }
}

.section {
  padding-block: clamp(48px, 7vw, 88px);
}

.section-tight {
  padding-block: clamp(32px, 5vw, 56px);
}

.eyebrow {
  font-size: 12px;
  font-weight: 700;
  letter-spacing: 0.09em;
  text-transform: uppercase;
  color: var(--crimson);
}

.rule {
  height: 1px;
  background: var(--line);
  border: 0;
}

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

.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  border-radius: 999px;
  padding: 13px 26px;
  font-weight: 600;
  font-size: 15px;
  line-height: 1;
  border: 1px solid transparent;
  cursor: pointer;
  transition: transform 0.12s ease, background-color 0.15s ease,
    border-color 0.15s ease, color 0.15s ease, box-shadow 0.15s ease;
  text-align: center;
}

.btn:active { transform: translateY(1px); }

.btn-primary {
  background: var(--crimson);
  color: #fff;
  box-shadow: 0 6px 18px color-mix(in srgb, var(--crimson) 28%, transparent);
}

.btn-primary:hover { background: var(--crimson-deep); }

.btn-dark {
  background: var(--ink);
  color: var(--cream);
}

.btn-dark:hover { background: var(--ink-soft); }

.btn-ghost {
  background: transparent;
  border-color: var(--line);
  color: var(--ink);
}

.btn-ghost:hover {
  border-color: var(--ink);
  background: var(--paper);
}

/* Ставится и отдельно от `.btn` (в шапке — «Кабинет», «Выйти»), поэтому
   раскладку задаёт себе сам, а не наследует от него. */
.btn-quiet {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  background: transparent;
  color: var(--ink-muted);
  padding: 10px 14px;
  border-radius: 10px;
}

.btn-quiet:hover {
  background: var(--cream-deep);
  color: var(--ink);
}

.btn-sm { padding: 9px 18px; font-size: 14px; }
.btn-lg { padding: 16px 32px; font-size: 16px; }
.btn-block { display: flex; width: 100%; }

/* --------------------------------------------------- размер под палец */

/* Палец накрывает пятно около девяти миллиметров и, в отличие от курсора,
   не видит, куда именно попал. Apple и Google независимо сошлись на 44px
   как на нижней границе кнопки; всё, что мельче, человек не нажимает, а
   выцеливает — и промахивается по соседней ссылке.

   Условие — `pointer: coarse`, а не ширина экрана. Разница принципиальная:
   раздувать кнопки нужно там, где по ним стучат пальцем (телефон, планшет,
   ноутбук с сенсорным экраном), а не там, где кто-то сузил окно браузера
   мышкой — там останется прежняя плотная вёрстка.

   `min-height` вместо увеличенных padding: он добирает высоту до нормы, но
   не трогает те кнопки, которые и так крупные (`.btn-lg`, `.btn-block`). */
@media (pointer: coarse) {
  .btn,
  .btn-sm,
  .btn-quiet,
  .tab,
  .plan-tab,
  .slider-tab,
  .proof-link,
  .cal-nav,
  .chip-filter,
  .stream-legend__item {
    min-height: 44px;
  }

  /* Стрелки «предыдущий месяц» / «следующий» — квадратные, поэтому им нужна
     и ширина: иначе кнопка станет высокой полоской в сорок пикселей. */
  .cal-nav { min-width: 44px; }

  /* Логотип — это ещё и ссылка «на главную», а знак у него 38×38. Растим
     не знак, а область вокруг него: рисунок в шапке остаётся прежним.
     Ширина нужна отдельно — до 640px подпись рядом со знаком скрыта
     (`hidden sm:block`), и ссылка сжимается до одного квадратика. */
  .site-brand {
    min-height: 44px;
    min-width: 44px;
  }

  /* Строчные ссылки-действия («Все занятия →», «Каталог →») высоту набирают
     отступами: у них нет своего фона, поэтому лишняя высота не видна, а
     нажимаются они после этого с первого раза. */
  .section-head__action {
    display: inline-flex;
    align-items: center;
    min-height: 44px;
  }

  /* Поля ввода. Отдельные формы ужимают их своими `py-2` (журнал занятия,
     поиск по библиотеке) — до 37 пикселей высотой. Галочки и переключатели
     исключены: у них свой размер выше, и 44 сделали бы из них плашки. */
  .form-styled input:not([type="checkbox"]):not([type="radio"]):not([type="hidden"]),
  .form-styled select,
  .form-styled textarea {
    min-height: 44px;
  }

  /* Метка-переключатель: «был» / «не был» в журнале занятия. Сам кружок в
     разметке жёстко задан в 14px, но нажимают не в него, а в плашку вокруг —
     она и обязана дотягивать до нормы. Обычные плашки-бейджи (`.chip` без
     `label`) остаются прежними: их не нажимают. */
  label.chip { min-height: 44px; }
}

.btn[disabled],
.btn.is-disabled {
  background: var(--cream-deep);
  color: var(--ink-faint);
  box-shadow: none;
  cursor: not-allowed;
  border-color: transparent;
}

/* ------------------------------------------------------------ карточки */

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

/* Всё, что лежит на подложке, — кремовое: белое на белом не читается как
   отдельный объект. */
.section .card,
.section .review-card,
.section .row-list,
.section .panel-list,
.page-head .card,
.page-head .fact,
.section .fact { background: var(--cream); }

.hero .hero-card { background: var(--paper); }
.hero .hero-card .panel-list { background: var(--cream); }

.card-pad { padding: clamp(20px, 3vw, 28px); }

.card-hover {
  transition: transform 0.15s ease, box-shadow 0.15s ease,
    border-color 0.15s ease;
}

.card-hover:hover {
  transform: translateY(-2px);
  box-shadow: var(--shadow-md);
  border-color: color-mix(in srgb, var(--ink) 18%, var(--line));
}

.card-accent {
  border-color: var(--crimson);
  box-shadow: 0 0 0 1px var(--crimson), var(--shadow-md);
}

.panel {
  background: var(--cream-deep);
  border-radius: var(--radius);
  padding: 18px 20px;
}

/* --------------------------------------------------------------- бейджи */

.chip {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  border-radius: 999px;
  padding: 5px 12px;
  font-size: 12.5px;
  font-weight: 600;
  line-height: 1.2;
}

/* Заголовок-выделение: тот же цветной фон, что у метки, но размером
   с подзаголовок — он и есть название карточки. */
.chip-title {
  font-size: clamp(16px, 1.5vw, 19px);
  font-weight: 600;
  letter-spacing: -0.012em;
  text-transform: none;
  padding: 8px 17px;
  border-radius: 13px;
}

.chip-crimson { background: var(--crimson-soft); color: var(--crimson-deep); }
.chip-amber { background: var(--amber-soft); color: var(--warning); }
.chip-success { background: var(--success-soft); color: var(--success); }
.chip-ink { background: var(--cream-deep); color: var(--ink-soft); }
.chip-solid { background: var(--crimson); color: #fff; }

/* --------------------------------------------------------------- формы */

.field-label {
  display: block;
  font-size: 13.5px;
  font-weight: 600;
  color: var(--ink-soft);
  margin-bottom: 6px;
}

.field-hint { font-size: 12.5px; color: var(--ink-muted); margin-top: 6px; }
.field-error { font-size: 13px; color: var(--crimson-deep); margin-top: 6px; }

/* Размер шрифта в полях — 16px, и это не вкусовое решение.
   Safari на iPhone сам увеличивает страницу, когда палец попадает в поле с
   текстом мельче 16px. Выглядит это так: человек тапает в «email» на форме
   оплаты, страница прыгает и разъезжается, а обратно она уже не вернётся —
   уменьшать её приходится вручную. Ни один из наших макетов этого не ждёт.
   На широком экране автозума нет, и там 15px возвращаются ниже. */
.form-styled input[type="text"],
.form-styled input[type="email"],
.form-styled input[type="password"],
.form-styled input[type="tel"],
.form-styled input[type="url"],
.form-styled input[type="number"],
.form-styled input[type="date"],
.form-styled input[type="time"],
.form-styled input[type="search"],
.form-styled select,
.form-styled textarea {
  width: 100%;
  /* Без этой строки «Моя карточка» разъезжалась вбок на всех телефонах.
     У <textarea> есть врождённая ширина — атрибут `cols`, по умолчанию 40
     символов, около трёхсот пикселей. `width: 100%` её не отменяет: она
     продолжает участвовать в расчёте минимального размера. А элемент сетки
     (форма лежит в `grid`) по умолчанию не даёт себя сжать уже содержимого
     — и колонка растягивалась под эти триста пикселей, утаскивая за собой
     всю страницу. `min-width: 0` разрешает полю сжиматься до ширины экрана. */
  min-width: 0;
  border-radius: 12px;
  border: 1px solid var(--line);
  background: var(--paper);
  padding: 12px 14px;
  font-size: 16px;
  color: var(--ink);
  transition: border-color 0.15s ease, box-shadow 0.15s ease;
}

@media (min-width: 768px) {
  .form-styled input[type="text"],
  .form-styled input[type="email"],
  .form-styled input[type="password"],
  .form-styled input[type="tel"],
  .form-styled input[type="url"],
  .form-styled input[type="number"],
  .form-styled input[type="date"],
  .form-styled input[type="time"],
  .form-styled input[type="search"],
  .form-styled select,
  .form-styled textarea {
    font-size: 15px;
  }
}

/* Поле выбора файла под общую рамку не загоняем: кнопку «Выберите файл»
   рисует сам браузер, и наша обводка вокруг неё выглядит вторым полем.
   А вот ширину ограничить обязательно. По умолчанию такое поле занимает
   столько, сколько считает нужным, — около трёхсот пикселей, — и, будучи
   содержимым элемента сетки, растягивает под себя всю колонку. Из-за одного
   поля «Фото» карточка преподавателя уезжала вбок на каждом телефоне,
   утаскивая за собой страницу целиком. */
.form-styled input[type="file"] {
  width: 100%;
  min-width: 0;
  font-size: 14px;
}

.form-styled input:focus,
.form-styled select:focus,
.form-styled textarea:focus {
  outline: none;
  border-color: var(--crimson);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--crimson) 16%, transparent);
}

.form-styled input[type="checkbox"] {
  width: 18px;
  height: 18px;
  accent-color: var(--crimson);
  flex-shrink: 0;
}

/* Галочка пальцем — 24px, нижняя граница WCAG 2.2. Крупнее не делаем:
   квадрат начинает спорить с подписью, рядом с которой стоит, а промах не
   смертелен — подпись связана с полем через `for` и переключает его сама.

   Правило стоит здесь, сразу за размером на широкий экран, а не в общем
   блоке про размер под палец выше. Специфичность у них одинаковая, и тот
   блок эти восемнадцать пикселей просто перебили бы обратно — порядок в
   файле решает. Ровно на это я и наступил, когда написал его выше. */
@media (pointer: coarse) {
  .form-styled input[type="checkbox"],
  .form-styled input[type="radio"] {
    width: 24px;
    height: 24px;
  }
}

/* ---------------------------------------------------------------- шапка */

.site-header {
  position: sticky;
  top: 0;
  z-index: 40;
  background: color-mix(in srgb, var(--cream) 88%, transparent);
  backdrop-filter: saturate(1.4) blur(10px);
  border-bottom: 1px solid var(--line);
}

.brand-mark {
  display: grid;
  place-items: center;
  width: 38px;
  height: 38px;
  border-radius: 11px;
  background: var(--crimson);
  color: #fff;
  font-weight: 800;
  font-size: 14px;
  letter-spacing: -0.03em;
}

.nav-link {
  font-size: 14.5px;
  color: var(--ink-soft);
  padding: 8px 2px;
  border-bottom: 2px solid transparent;
  transition: color 0.15s ease, border-color 0.15s ease;
}

.nav-link:hover { color: var(--crimson); }
.nav-link.is-active { color: var(--ink); border-color: var(--crimson); }

/* ---------------------------------------------------------------- герой */

.hero {
  position: relative;
  overflow: hidden;
  border-bottom: 1px solid var(--line);
  background:
    radial-gradient(1000px 560px at 80% 8%, color-mix(in srgb, var(--crimson) 14%, transparent), transparent 62%),
    radial-gradient(560px 380px at 0% 100%, color-mix(in srgb, var(--amber) 9%, transparent), transparent 64%),
    var(--dots),
    var(--cream-deep);
  background-size: auto, auto, 22px 22px, auto;
}

/* Карточка первого экрана: содержимое стоит на белом листе, а не парит над
   фоном. Задник виден по краям и держит композицию. */
.hero-card {
  background: var(--paper);
  border: 1px solid var(--line);
  border-radius: clamp(16px, 1.8vw, 26px);
  padding: clamp(16px, 1.8vw, 24px) clamp(20px, 2.6vw, 34px);
  box-shadow: 0 1px 2px rgb(20 22 31 / 0.05), 0 12px 32px rgb(20 22 31 / 0.06);
}

.hero-card .panel-list { box-shadow: none; }
.hero-card .panel-list__head { background: var(--cream-deep); }
.hero-card .row-item:hover { background: var(--cream-deep); }

.hero-portrait {
  position: relative;
  border-radius: 26px;
  overflow: hidden;
  box-shadow: var(--shadow-lg);
  aspect-ratio: 4 / 5;
}

/* В слайдере портрет ограничен по высоте: он задаёт высоту всей области,
   а первый экран не должен уезжать за пределы окна. */
.slider .hero-portrait { max-height: 430px; }

/* Портрет вертикальный (4:5) и ограничен по высоте, поэтому в ширину он
   занимает 344 px. Пока колонка была шире, вокруг него оставались пустые
   полосы: справа — до края карточки, слева — между ним и текстом, который
   и так обрывается на длине строки (`--measure`).

   Поэтому колонка портрета задана ровно по его ширине: справа от фотографии
   остаётся только поле карточки, как и слева от текста. Сам портрет прежний —
   вертикальный, той же высоты. */
@media (min-width: 1024px) {
  .slide[data-slide="about"] > .grid {
    grid-template-columns: minmax(0, 1fr) calc(430px * 4 / 5);
    gap: 2.5rem;
  }
}


.hero-portrait img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: center 22%;
  display: block;
}

.hero-portrait::after {
  content: "";
  position: absolute;
  inset: 0;
  background: linear-gradient(180deg, transparent 55%, rgb(20 22 31 / 0.55));
}

.hero-caption {
  position: absolute;
  left: 20px;
  right: 20px;
  bottom: 18px;
  z-index: 2;
  color: #fff;
}

/* ------------------------------------------------------------- слайдер */

.slider-tabs {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 6px;
  margin-bottom: clamp(16px, 2vw, 24px);
}

.slider-tab {
  border: 1px solid var(--line);
  background: var(--paper);
  color: var(--ink-muted);
  border-radius: 999px;
  padding: 9px 18px;
  font-size: 14.5px;
  font-weight: 600;
  cursor: pointer;
  transition: background-color 0.15s ease, color 0.15s ease, border-color 0.15s ease;
}

.slider-tab:hover { color: var(--ink); border-color: var(--ink-faint); }

.slider-tab.is-active {
  background: var(--crimson);
  border-color: var(--crimson);
  color: #fff;
}

.slider-arrows { margin-left: auto; display: flex; gap: 6px; }

.slider-arrow {
  width: 38px;
  height: 38px;
  border-radius: 999px;
  border: 1px solid var(--line);
  background: var(--paper);
  color: var(--ink-soft);
  font-size: 20px;
  line-height: 1;
  cursor: pointer;
}

.slider-arrow:hover { border-color: var(--ink); color: var(--ink); }

/* Все слайды лежат в одной ячейке грида, поэтому высота области равна самому
   высокому из них: переключение вкладок не дёргает страницу. */
.slider-viewport { display: grid; }

.slide {
  grid-area: 1 / 1;
  min-width: 0;
  /* Высота области — по самому высокому слайду, поэтому содержимое коротких
     слайдов ставим по центру, а не прижимаем к верху. */
  display: grid;
  align-content: center;
}

.slide[hidden] {
  display: block;
  visibility: hidden;
  pointer-events: none;
}

/* На телефоне равная высота слайдов стоит слишком дорого: самый высокий
   слайд — со списком ближайших занятий, и из-за него у короткого слайда
   сверху оставалось больше сотни пустых пикселей на первом же экране.
   Здесь каждый слайд занимает свою высоту: подпрыгивание при переключении
   на телефоне незаметно, а пустой экран — очень. */
@media (max-width: 900px) {
  .slider-viewport { display: block; }
  .slide[hidden] { display: none; }
}

.slide.is-active {
  animation: slide-in 0.28s ease both;
}

@keyframes slide-in {
  from { opacity: 0; transform: translateY(8px); }
  to { opacity: 1; transform: none; }
}

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

.slider-dots {
  display: flex;
  justify-content: center;
  gap: 8px;
  margin-top: clamp(16px, 2vw, 24px);
}

.slider-dot {
  width: 8px;
  height: 8px;
  border-radius: 999px;
  background: var(--line);
  cursor: pointer;
  transition: background-color 0.15s ease, width 0.15s ease;
}

.slider-dot.is-active { background: var(--crimson); width: 22px; }

.stat-tile {
  text-align: center;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: var(--paper);
  padding: 16px 18px;
}

.stat-tile dt {
  font-size: 12.5px;
  color: var(--ink-muted);
}

.stat-tile dd {
  font-size: 22px;
  font-weight: 700;
  margin-top: 2px;
  letter-spacing: -0.02em;
}

/* -------------------------------------------------------------- отзывы */

.review-card {
  display: flex;
  flex-direction: column;
  background: var(--paper);
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  padding: 24px;
  break-inside: avoid;
}

.review-quote {
  font-size: 15px;
  line-height: 1.62;
  color: var(--ink-soft);
}

.stars {
  color: var(--amber);
  letter-spacing: 1px;
  font-size: 14px;
}

.proof-link {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font-size: 12.5px;
  color: var(--ink-muted);
  background: transparent;
  border: 1px dashed var(--line);
  border-radius: 999px;
  padding: 5px 11px;
  cursor: pointer;
  transition: border-color 0.15s ease, color 0.15s ease;
}

.proof-link::after { content: "🔍"; font-size: 11px; }
.proof-link:hover { color: var(--crimson); border-color: var(--crimson); }

/* Попап с оригиналом отзыва. Нативный <dialog>: Esc, фокус-ловушка и
   ::backdrop достаются бесплатно. */
.proof-dialog {
  border: 0;
  padding: 0;
  border-radius: var(--radius-lg);
  background: var(--paper);
  box-shadow: var(--shadow-lg);
  max-width: min(560px, 92vw);
  /* dvh, а не vh: на телефоне `vh` считается по экрану с убранной адресной
     строкой, и пока она развёрнута, окно в 92vh выше видимой области — низ
     картинки и кнопка закрытия оказываются под краем экрана. `dvh` меряет
     то, что видно сейчас. Строка с `vh` оставлена запасной для браузеров,
     которые про `dvh` ещё не знают. */
  max-height: 92vh;
  max-height: 92dvh;
  overflow: hidden;
}

.proof-dialog::backdrop {
  background: rgb(20 22 31 / 0.62);
  backdrop-filter: blur(2px);
}

.proof-dialog__bar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  padding: 14px 18px;
  border-bottom: 1px solid var(--line);
  background: var(--cream);
}

.proof-dialog__title {
  font-size: 14px;
  font-weight: 600;
  color: var(--ink);
}

/* Крестик — единственный способ закрыть окно пальцем (Esc есть только с
   клавиатуры), поэтому он полноразмерная цель в 44px, а не иконка в 24. */
.proof-dialog__close {
  border: 0;
  background: transparent;
  font-size: 16px;
  line-height: 1;
  color: var(--ink-muted);
  cursor: pointer;
  display: grid;
  place-items: center;
  width: 44px;
  height: 44px;
  margin: -10px -12px -10px 0;
  border-radius: 10px;
}

.proof-dialog__close:hover { background: var(--cream-deep); color: var(--ink); }

.proof-dialog img {
  display: block;
  width: 100%;
  max-height: calc(92vh - 56px);
  max-height: calc(92dvh - 56px);
  object-fit: contain;
  background: var(--cream-deep);
}

.masonry {
  column-gap: 20px;
}

.masonry > * { margin-bottom: 20px; }

@media (min-width: 720px) { .masonry { column-count: 2; } }
@media (min-width: 1040px) { .masonry { column-count: 3; } }

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

.plan-picker > input[type="radio"] {
  position: absolute;
  opacity: 0;
  pointer-events: none;
}

/* Переключатель живёт внизу белой карточки, под составом занятий. */
/* Подпись и пузыри прижаты к правому краю — там же, где цена и кнопка,
   к которым они относятся. Абсолютного позиционирования больше нет: подпись
   идёт обычным элементом ряда, и на узком экране ничего не налезает. */
.plan-tabs {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: flex-end;
  gap: 8px 14px;
  border-top: 1px solid var(--line);
  background: var(--cream);
  padding: clamp(16px, 2.4vw, 22px) clamp(22px, 3.5vw, 34px);
}

.plan-tabs__label {
  font-size: 12px;
  font-weight: 700;
  letter-spacing: 0.09em;
  text-transform: uppercase;
  color: var(--ink-soft);
}

/* Узкий экран: ряд не помещается — начинаем его слева, иначе перенос строк
   даёт рваную лесенку у правого края. */
@media (max-width: 720px) {
  .plan-tabs { justify-content: flex-start; }
}

.plan-tab {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  border: 1px solid var(--line);
  background: var(--paper);
  border-radius: 999px;
  padding: 10px 18px;
  font-size: 15px;
  font-weight: 600;
  color: var(--ink-soft);
  cursor: pointer;
  transition: border-color 0.15s ease, background-color 0.15s ease, color 0.15s ease;
}

.plan-tab:hover { border-color: var(--ink-faint); }

.plan-tab__off {
  font-size: 12.5px;
  font-weight: 700;
  color: var(--success);
  background: var(--success-soft);
  border-radius: 999px;
  padding: 2px 8px;
}

.plan-panel { display: none; }
.plan-panels { overflow: hidden; }

.plan-panel__grid {
  display: grid;
  gap: clamp(24px, 4vw, 48px);
  padding: clamp(22px, 3.5vw, 34px);
}

/* На узком экране колонка одна, и цена с кнопкой поднимается наверх: главное
   действие не должно уезжать под список. Правило стоит до медиазапроса —
   иначе оно перебило бы его по каскаду. */
.plan-panel__buy { order: -1; }

@media (min-width: 860px) {
  .plan-panel__grid { grid-template-columns: 1fr 1fr; align-items: start; }
  /* Цена справа, состав слева. Черта отделяет цену, а не список: она всегда
     у правого столбца, даже если состав занятий не заполнен и его нет. */
  .plan-panel__buy {
    order: 0;
    border-left: 1px solid var(--line);
    padding-left: clamp(24px, 4vw, 48px);
    /* Правый столбец выровнен по правому краю: у него ровный внешний край
       страницы, а левый и так рваный из-за строк разной длины. */
    text-align: right;
  }
  .plan-panel__buy:first-child { border-left: 0; padding-left: 0; }
}

/* Ценовой блок. Правила простые и не мои: в блоке ровно одно крупное число —
   цена месяца; всё остальное (сумма за период, ставка часа, нагрузка) тише её
   и не спорит за внимание. Цифры набраны табличными, поэтому суммы у разных
   сроков стоят колонка в колонку и не пляшут при переключении. Валюта и
   единица — не часть числа, поэтому мельче. Группы разделены паузой и
   волоском, а не рамками. Кнопка на всю ширину колонки: при выравнивании по
   правому краю висящая кнопка читалась бы как случайная. */
.price {
  font-variant-numeric: tabular-nums;
}

.price__eyebrow {
  font-size: 12px;
  font-weight: 700;
  letter-spacing: 0.09em;
  text-transform: uppercase;
  color: var(--ink-muted);
}

.price__main {
  margin-top: 10px;
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 8px;
}

.price__value {
  font-size: clamp(34px, 3.6vw, 44px);
  font-weight: 700;
  line-height: 1;
  letter-spacing: -0.03em;
  white-space: nowrap;
}

.price__unit {
  font-size: 15px;
  font-weight: 600;
  color: var(--ink-muted);
}

.price__total {
  margin-top: 10px;
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 6px 12px;
  font-size: 14.5px;
  color: var(--ink-soft);
}

.price__total s { color: var(--ink-faint); }

/* Сумма к оплате, когда период уже начался и часть занятий прошла. Выделена
   успехом, а не акцентом бренда: это выгода покупателю, и путать её с
   «купить» не нужно. */
.price__now {
  margin-top: 10px;
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 6px 10px;
  font-size: 15px;
}

.price__now-value {
  font-weight: 700;
  color: var(--success);
}

.price__now s { color: var(--ink-faint); }

.price__rate {
  margin-top: 18px;
  padding-top: 16px;
  border-top: 1px solid var(--line);
  font-size: 14.5px;
  color: var(--ink-muted);
}

.price__rate b {
  font-size: 18px;
  font-weight: 700;
  color: var(--ink);
}

.price__note {
  margin-top: 6px;
  font-size: 13px;
  color: var(--ink-muted);
  max-width: 34ch;
}

.price__action { margin-top: 20px; }

.price__fine {
  margin-top: 10px;
  font-size: 12.5px;
  color: var(--ink-faint);
}

/* Правый край: ряды прижимаются к нему, а не разъезжаются влево, а короткая
   строка про нагрузку не тянется на всю ширину колонки. */
@media (min-width: 860px) {
  .price__main,
  .price__total { justify-content: flex-end; }
  .price__note { margin-left: auto; }
}

/* Переключение без JS: выбранный radio подсвечивает свою вкладку и открывает
   свою панель. Правил столько, сколько тарифов может быть на витрине.
   Вкладки считаем через :nth-of-type — рядом с ними лежит подпись «Срок
   оплаты», и :nth-child сдвигал бы подсветку на соседний тариф. */
.plan-picker > input:nth-of-type(1):checked ~ .plan-panels .plan-tabs > .plan-tab:nth-of-type(1),
.plan-picker > input:nth-of-type(2):checked ~ .plan-panels .plan-tabs > .plan-tab:nth-of-type(2),
.plan-picker > input:nth-of-type(3):checked ~ .plan-panels .plan-tabs > .plan-tab:nth-of-type(3),
.plan-picker > input:nth-of-type(4):checked ~ .plan-panels .plan-tabs > .plan-tab:nth-of-type(4),
.plan-picker > input:nth-of-type(5):checked ~ .plan-panels .plan-tabs > .plan-tab:nth-of-type(5),
.plan-picker > input:nth-of-type(6):checked ~ .plan-panels .plan-tabs > .plan-tab:nth-of-type(6) {
  background: var(--crimson);
  border-color: var(--crimson);
  color: #fff;
}

.plan-picker > input:nth-of-type(1):checked ~ .plan-panels .plan-tabs > .plan-tab:nth-of-type(1) .plan-tab__off,
.plan-picker > input:nth-of-type(2):checked ~ .plan-panels .plan-tabs > .plan-tab:nth-of-type(2) .plan-tab__off,
.plan-picker > input:nth-of-type(3):checked ~ .plan-panels .plan-tabs > .plan-tab:nth-of-type(3) .plan-tab__off,
.plan-picker > input:nth-of-type(4):checked ~ .plan-panels .plan-tabs > .plan-tab:nth-of-type(4) .plan-tab__off,
.plan-picker > input:nth-of-type(5):checked ~ .plan-panels .plan-tabs > .plan-tab:nth-of-type(5) .plan-tab__off,
.plan-picker > input:nth-of-type(6):checked ~ .plan-panels .plan-tabs > .plan-tab:nth-of-type(6) .plan-tab__off {
  background: rgb(255 255 255 / 0.22);
  color: #fff;
}

.plan-picker > input:nth-of-type(1):checked ~ .plan-panels > .plan-panel:nth-child(1),
.plan-picker > input:nth-of-type(2):checked ~ .plan-panels > .plan-panel:nth-child(2),
.plan-picker > input:nth-of-type(3):checked ~ .plan-panels > .plan-panel:nth-child(3),
.plan-picker > input:nth-of-type(4):checked ~ .plan-panels > .plan-panel:nth-child(4),
.plan-picker > input:nth-of-type(5):checked ~ .plan-panels > .plan-panel:nth-child(5),
.plan-picker > input:nth-of-type(6):checked ~ .plan-panels > .plan-panel:nth-child(6) {
  display: block;
}

/* Клавиатура: рамка на вкладке выбранного радио. */
.plan-picker > input:focus-visible:nth-of-type(1) ~ .plan-panels .plan-tabs > .plan-tab:nth-of-type(1),
.plan-picker > input:focus-visible:nth-of-type(2) ~ .plan-panels .plan-tabs > .plan-tab:nth-of-type(2),
.plan-picker > input:focus-visible:nth-of-type(3) ~ .plan-panels .plan-tabs > .plan-tab:nth-of-type(3),
.plan-picker > input:focus-visible:nth-of-type(4) ~ .plan-panels .plan-tabs > .plan-tab:nth-of-type(4) {
  outline: 2px solid var(--ink);
  outline-offset: 2px;
}

/* ---------------------------------------------------------- календарь */

.cal-nav {
  display: grid;
  place-items: center;
  width: 40px;
  height: 40px;
  border-radius: 999px;
  border: 1px solid var(--line);
  background: var(--paper);
  font-size: 20px;
  line-height: 1;
  color: var(--ink-soft);
  flex-shrink: 0;
}

.cal-nav:hover { border-color: var(--ink); color: var(--ink); }

.cal-grid {
  display: grid;
  grid-template-columns: repeat(7, minmax(0, 1fr));
}

.cal-head { background: var(--cream); }

.cal-weekday {
  padding: 10px 8px;
  font-size: 12px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--ink-muted);
  text-align: center;
  border-bottom: 1px solid var(--line);
}

.cal-day {
  min-height: 104px;
  display: flex;
  flex-direction: column;
  gap: 3px;
  padding: 8px 8px 10px;
  border-right: 1px solid var(--line);
  border-bottom: 1px solid var(--line);
  background: var(--paper);
  transition: background-color 0.12s ease;
}

.cal-day:nth-child(7n) { border-right: 0; }
.cal-day:hover { background: var(--cream); }

.cal-day.is-outside { background: color-mix(in srgb, var(--cream) 60%, var(--paper)); }
.cal-day.is-outside .cal-daynum { color: var(--ink-faint); }

/* Выходные: занятий по ним не бывает, и две пустые колонки каждую неделю
   спорят за внимание с рабочими днями. Гасим их тем же приёмом, что и дни
   соседнего месяца — фоном и бледной цифрой, — но кликабельными оставляем:
   в субботу бывает дополнительное занятие, и попасть в него нужно. */
.cal-day.is-weekend { background: color-mix(in srgb, var(--cream) 45%, var(--paper)); }
.cal-day.is-weekend .cal-daynum { color: var(--ink-faint); }
.cal-weekday.is-weekend { color: var(--ink-faint); }

/* День с занятием важнее того, что он выходной: дополнительная суббота
   должна выглядеть рабочей. */
.cal-day.is-weekend:has(.cal-slot) { background: var(--paper); }
.cal-day.is-weekend:has(.cal-slot) .cal-daynum { color: var(--ink); }

.cal-day.is-selected {
  background: var(--crimson-soft);
  box-shadow: inset 0 0 0 2px var(--crimson);
}

.cal-daynum {
  font-size: 13px;
  font-weight: 700;
  color: var(--ink-soft);
  align-self: flex-start;
  min-width: 22px;
  text-align: center;
  padding: 2px 0;
  border-radius: 999px;
}

.cal-day.is-today .cal-daynum { background: var(--ink); color: var(--cream); }

.cal-slot {
  font-size: 11.5px;
  line-height: 1.35;
  padding: 3px 6px;
  border-radius: 6px;
  background: var(--cream-deep);
  color: var(--ink-soft);
  box-shadow: inset 3px 0 0 var(--stream, transparent);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.cal-slot.is-intensive { background: var(--amber-soft); color: var(--warning); }
.cal-slot.is-past { opacity: 0.5; }

/* Потоки идут параллельно, и преподаватель у них часто один, поэтому в сетке
   их различает цвет, а не подпись: четыре оттенка палитры школы и легенда над
   календарём. Пятый и дальше остаются нейтральными — цветов, которые человек
   удержит в голове, всё равно не больше четырёх. */
.stream-legend {
  display: flex;
  flex-wrap: wrap;
  gap: 8px 18px;
  padding: 12px clamp(16px, 2vw, 22px);
  border-bottom: 1px solid var(--line);
  background: var(--cream);
}

.stream-legend__item {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  font-size: 13px;
  color: var(--ink-soft);
}

.stream-legend__item:hover { color: var(--crimson); }

/* Число занятий в потоке. На телефоне название потока занимает две строки,
   и счётчик, оставшись обычным элементом flex, начинал переноситься внутри
   себя: точка уезжала на одну строку, число под неё. Запрещаем перенос и
   сжатие — «· 13» читается только целиком. */
.stream-legend__item .faint {
  white-space: nowrap;
  flex-shrink: 0;
}

/* Легенда служит и фильтром: клик по пункту оставляет в календаре один поток.
   Выбранный надо отличать не только цветом текста — половина пунктов и так
   разноцветная, — поэтому к нему добавляется рамка вокруг метки и подложка. */
.stream-legend__item.is-active {
  color: var(--ink);
  font-weight: 600;
  background: #fff;
  border-radius: 999px;
  padding: 3px 10px;
  margin: -3px 0;
  box-shadow: 0 0 0 1px var(--line);
}

/* Выход из отбора. Стоит последним в ряду и намеренно не похож на поток:
   это действие, а не ещё один пункт списка. */
.stream-legend__reset {
  font-size: 13px;
  color: var(--crimson);
  text-decoration: underline;
  text-underline-offset: 3px;
  align-self: center;
}

.stream-dot {
  width: 10px;
  height: 10px;
  border-radius: 3px;
  background: var(--ink-faint);
}

/* Цвет метки приходит из данных курса — здесь только форма. Исключение —
   зелёный бейдж «живые занятия» на витрине: это метка состояния, а не потока. */
.stream-dot--live { background: var(--success); }
.cal-more { font-size: 11px; color: var(--ink-muted); padding-left: 6px; }

/* Календарь на телефоне: точки вместо подписей.

   Семь колонок на экране в 320px дают клетку шириной с ноготь — сорок
   пикселей. Подпись «12:30 Летний интенсив» в неё не влезает никак: даже
   уменьшенная до 10px, она обрезалась до «1…», и вся сетка превращалась в
   поле одинаковых многоточий. Информации в ней не оставалось вовсе, а место
   она занимала.

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

   Текст подписи остаётся в разметке и скрыт нулевым кеглем, а не
   `display:none`: скринридер обязан прочитать, что за занятие в этот день. */
@media (max-width: 720px) {
  .cal-day {
    min-height: 56px;
    padding: 6px 3px;
    /* Число — во всю строку, точки под ним переносятся рядком по центру. */
    flex-direction: row;
    flex-wrap: wrap;
    align-content: flex-start;
    justify-content: center;
    gap: 3px;
  }

  .cal-daynum {
    flex: 1 0 100%;
    align-self: auto;
    text-align: center;
  }

  .cal-slot {
    width: 7px;
    height: 7px;
    padding: 0;
    border-radius: 999px;
    background: var(--stream, var(--ink-faint));
    box-shadow: none;
    font-size: 0;
    line-height: 0;
    flex: 0 0 auto;
  }

  /* Интенсив различается тем же цветом потока, что и в списке; отдельная
     заливка бейджа на точке размером в семь пикселей неразличима. */
  .cal-slot.is-intensive { background: var(--stream, var(--amber)); }

  /* «ещё 2» рядом с точками не помещается и ломает их ряд переносом. Счёт
     занятий в дне человек всё равно смотрит в списке ниже. */
  .cal-more { display: none; }

  /* Поля по 8px на колонке шириной в сорок оставляли под подпись двадцать
     четыре — и «ПН» переносилось по буквам, столбиком: «П», под ним «Н».
     Убираем поля и запрещаем перенос: две буквы должны стоять в строку. */
  .cal-weekday {
    padding: 9px 2px;
    white-space: nowrap;
  }
}

/* -------------------------------------------------- программа и уроки */

.lesson-row {
  display: flex;
  align-items: center;
  gap: 14px;
  padding: 13px 20px;
  border-top: 1px solid var(--line);
}

.lesson-row:first-child { border-top: 0; }

.lesson-mark {
  display: grid;
  place-items: center;
  width: 30px;
  height: 30px;
  border-radius: 999px;
  font-size: 12px;
  flex-shrink: 0;
}

.mark-open { background: var(--success-soft); color: var(--success); }
.mark-locked { background: var(--cream-deep); color: var(--ink-faint); }
.mark-late { background: var(--amber-soft); color: var(--warning); }

.progress-track {
  height: 7px;
  border-radius: 999px;
  background: var(--cream-deep);
  overflow: hidden;
}

.progress-fill {
  height: 100%;
  border-radius: 999px;
  background: var(--crimson);
}

.progress-fill.is-done { background: var(--success); }

/* Плеер живёт в iframe Kinescope. Блокировок выделения и правого клика нет
   намеренно — они ничего не защищают, но ломают копирование текста рядом. */

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

.table {
  width: 100%;
  border-collapse: collapse;
  font-size: 14.5px;
}

.table thead th {
  text-align: left;
  font-weight: 600;
  font-size: 12.5px;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  color: var(--ink-muted);
  padding: 12px 20px;
  background: var(--cream-deep);
}

.table td {
  padding: 15px 20px;
  border-top: 1px solid var(--line);
  vertical-align: middle;
}

/* ------------------------------------------- таблица карточками на телефоне

   Таблица на узком экране обычно живёт в контейнере с горизонтальной
   прокруткой. Формально всё видно — на деле нет: в «Моих учениках» на экране
   в 320px помещались две первые колонки, имя ломалось на четыре строки, а
   посещаемость, ради которой на страницу и заходят, оставалась за краем.
   Пролистывать вбок каждую строку никто не станет.

   Поэтому на телефоне каждая строка становится карточкой: заголовки колонок
   переезжают внутрь ячеек подписями из `data-label`, и таблица читается
   сверху вниз — как всё остальное на странице.

   Модификатор отдельный (`.table--cards`), а не общее правило для `.table`:
   так превращение остаётся осознанным выбором для конкретного экрана.
   Подписи дублируются в `data-label`, а не берутся из шапки: добраться из
   CSS до текста чужой ячейки нельзя, `content` умеет читать только атрибут
   того же элемента. Цена — подпись колонки написана в шаблоне дважды. */
@media (max-width: 640px) {
  /* Прокрутка контейнеру больше не нужна: карточки и так по ширине экрана.
     Если её оставить, у блока появляется лишний ползунок в один пиксель. */
  .table-scroll:has(.table--cards) { overflow-x: visible; }

  .table--cards,
  .table--cards tbody,
  .table--cards tr,
  .table--cards td {
    display: block;
    width: 100%;
  }

  /* Шапка не нужна — её подписи теперь внутри ячеек. `position:absolute`
     вместо `display:none`: скринридер продолжает связывать ячейку с
     колонкой, а зрячий читает подпись из ::before. */
  .table--cards thead {
    position: absolute;
    width: 1px;
    height: 1px;
    overflow: hidden;
    clip-path: inset(50%);
    white-space: nowrap;
  }

  /* Колонкой, а не блоком: главная ячейка почти никогда не стоит в разметке
     первой (в платежах «Покупка» — вторая колонка), а заголовку карточки
     место наверху. Порядок правит `order`, разметка остаётся прежней —
     переставлять колонки ради телефона значило бы ломать широкий экран. */
  .table--cards tr {
    display: flex;
    flex-direction: column;
    padding: 4px 0;
    border-top: 1px solid var(--line);
  }

  .table--cards tr:first-child { border-top: 0; }

  .table--cards td {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 16px;
    padding: 7px 16px;
    border-top: 0;
    text-align: right;
  }

  .table--cards td::before {
    content: attr(data-label);
    flex: 0 0 auto;
    font-size: 12.5px;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.05em;
    color: var(--ink-muted);
    text-align: left;
  }

  /* Ячейка, названная главной, — это заголовок карточки: подпись ей не
     нужна, она и так крупнее прочих и стоит первой. */
  .table--cards td[data-main]::before { content: none; }

  .table--cards td[data-main] {
    order: -1;
    display: block;
    padding-top: 13px;
    padding-bottom: 4px;
    font-size: 16px;
    text-align: left;
  }

  /* Правое выравнивание чисел в карточке не работает: значение и так у
     правого края, а класс с широкого экрана прижимал бы к нему и подпись. */
  .table--cards td.text-right { text-align: right; }
}

/* ------------------------------------------------------------- вкладки */

.tabs {
  display: flex;
  flex-wrap: wrap;
  gap: 4px;
  border-bottom: 1px solid var(--line);
}

.tab {
  padding: 12px 16px;
  font-size: 14.5px;
  color: var(--ink-muted);
  border-bottom: 2px solid transparent;
  margin-bottom: -1px;
}

.tab:hover { color: var(--ink); }

.tab.is-active {
  color: var(--crimson);
  border-color: var(--crimson);
  font-weight: 600;
}

/* Вкладки на узком экране: одна строка с прокруткой вместо переноса.

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

   Строка с прокруткой — тот же приём, что и у полосы разделов в шапке, и
   тени у краёв такие же: пока листать есть куда, край притенён. Держится это
   на разнице `background-attachment` — подробности в комментарии к
   `.nav-mobile__inner`. Фон под вкладками белый, поэтому и слой `local` белый.

   `overflow-y: hidden` обязателен: без него браузер делает прокручиваемой и
   вертикаль, а `margin-bottom: -1px`, которым активная вкладка садится на
   границу, превращается в лишний ползунок высотой в пиксель.

   Граница — 1023px, та же, что у меню в шапке. Замерено: семь вкладок
   кабинета преподавателя занимают 686 пикселей и ровно на 768 в строку уже
   не помещаются — сваливаются в два ряда высотой под сто пикселей. Там, где
   они помещаются (от 900), полоса просто не прокручивается: `nowrap` не
   мешает, а тени у краёв не появляются, потому что листать нечего. */
@media (max-width: 1023px) {
  .tabs {
    flex-wrap: nowrap;
    overflow-x: auto;
    overflow-y: hidden;
    overscroll-behavior-x: contain;
    scrollbar-width: none;
    background:
      linear-gradient(to right, var(--paper) 40%, transparent) left center / 32px 100% no-repeat local,
      linear-gradient(to left, var(--paper) 40%, transparent) right center / 32px 100% no-repeat local,
      radial-gradient(farthest-side at 0 50%, rgb(20 22 31 / 0.13), transparent) left center / 14px 100% no-repeat scroll,
      radial-gradient(farthest-side at 100% 50%, rgb(20 22 31 / 0.13), transparent) right center / 14px 100% no-repeat scroll;
  }

  .tabs::-webkit-scrollbar { display: none; }

  .tab {
    white-space: nowrap;
    flex: 0 0 auto;
    padding-inline: 12px;
  }
}

/* ------------------------------------------------------------ сообщения */

.alert {
  border-radius: var(--radius);
  border: 1px solid;
  padding: 14px 18px;
  font-size: 14.5px;
}

.alert-info { background: var(--paper); border-color: var(--line); color: var(--ink-soft); }
.alert-error { background: var(--crimson-soft); border-color: #f0c3c5; color: var(--crimson-deep); }
.alert-success { background: var(--success-soft); border-color: #bfe3d1; color: var(--success); }
.alert-warning { background: var(--amber-soft); border-color: #f3d6a8; color: var(--warning); }

/* ---------------------------------------------------------------- проза */

.prose-school {
  font-size: 16.5px;
  line-height: 1.72;
  color: var(--ink-soft);
  max-width: var(--measure);
}

.prose-school > :first-child { margin-top: 0; }

.prose-school h2 {
  font-size: clamp(20px, 2vw, 25px);
  line-height: 1.2;
  font-weight: 700;
  color: var(--ink);
  margin-top: 2em;
  margin-bottom: 0.6em;
  padding-top: 1.4em;
  border-top: 1px solid var(--line);
}

.prose-school > h2:first-child { padding-top: 0; border-top: 0; }
.prose-school h3 { font-size: 18px; font-weight: 600; color: var(--ink); margin-top: 1.5em; margin-bottom: 0.4em; }
.prose-school p { margin-bottom: 1em; }
.prose-school ul { list-style: disc; padding-left: 1.3em; margin-bottom: 1em; }
.prose-school ol { list-style: decimal; padding-left: 1.3em; margin-bottom: 1em; }
.prose-school li { margin-bottom: 0.35em; }
.prose-school a { color: var(--crimson); text-decoration: underline; }
.prose-school strong { color: var(--ink); }

/* ------------------------------------------------------------- подвал */

.site-footer {
  background: var(--ink);
  color: color-mix(in srgb, var(--cream) 75%, transparent);
  /* Тёмная полоса уходит под нижний край экрана целиком (viewport-fit=cover),
     а вместе с ней ушли бы и последние ссылки — под ту черту, которой iPhone
     показывает жест «домой». Отступ снизу возвращает их на видное место. */
  padding-bottom: env(safe-area-inset-bottom);
}

.site-footer a:hover { color: #fff; }

@media print {
  .site-header, .site-footer { display: none; }
}

/* ------------------------------------------------- меню курсов в шапке */

.nav-menu {
  position: relative;
  /* Обёртка тоже flex: иначе ссылка внутри неё остаётся строчной и садится
     на пару пикселей ниже соседних пунктов. */
  display: flex;
  align-items: center;
}

.nav-drop {
  position: absolute;
  top: 100%;
  left: -16px;
  z-index: 40;
  min-width: 300px;
  /* Прозрачная полоса сверху: курсор успевает дойти до меню и оно не гаснет. */
  padding-top: 14px;
  opacity: 0;
  visibility: hidden;
  transform: translateY(-6px);
  transition: opacity 0.15s ease, transform 0.15s ease, visibility 0.15s;
}

.nav-menu:hover .nav-drop,
.nav-menu:focus-within .nav-drop {
  opacity: 1;
  visibility: visible;
  transform: none;
}

.nav-drop__inner {
  background: var(--paper);
  border: 1px solid var(--line);
  border-radius: 16px;
  box-shadow: 0 18px 40px rgb(20 22 31 / 0.13);
  padding: 8px;
  display: grid;
  gap: 2px;
}

.nav-drop__item {
  display: grid;
  gap: 2px;
  border-radius: 11px;
  padding: 9px 12px;
  color: var(--ink);
  transition: background-color 0.15s ease;
}

.nav-drop__item:hover { background: var(--cream); }

.nav-drop__title { font-size: 14.5px; font-weight: 600; }
.nav-drop__note { font-size: 12.5px; color: var(--ink-soft); }

.nav-drop__all {
  border-top: 1px solid var(--line);
  margin-top: 6px;
  padding: 11px 12px 5px;
  font-size: 13.5px;
  font-weight: 600;
  color: var(--crimson);
}

/* Безлимитный пропуск: выделен рамкой, но не кричит — это не главный продукт. */
.all-access {
  border-color: var(--crimson);
  background: linear-gradient(180deg, var(--crimson-soft), var(--paper) 70%);
}

/* ==========================================================================
   Раскладка страниц

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

.section {
  padding-block: var(--section-y);
}

.section-tight {
  padding-block: var(--section-y-tight);
}

.section--deep {
  background: var(--cream-deep);
  border-block: 1px solid var(--line);
}

.section--ink {
  background: var(--ink);
  color: var(--cream);
}

.section--ink h1,
.section--ink h2,
.section--ink .h1,
.section--ink .h2 { color: #fff; }

.section--ink .muted { color: color-mix(in srgb, var(--cream) 72%, transparent); }

/* --------------------------------------------------------- типографика */

.h1 {
  font-size: clamp(30px, 4.4vw, 46px);
  line-height: 1.08;
  font-weight: 700;
  letter-spacing: -0.025em;
}

.h2 {
  font-size: clamp(24px, 2.8vw, 33px);
  line-height: 1.15;
  font-weight: 700;
  letter-spacing: -0.022em;
}

.h3 {
  font-size: clamp(17px, 1.5vw, 20px);
  line-height: 1.3;
  font-weight: 600;
  letter-spacing: -0.015em;
}

.lead {
  font-size: clamp(15.5px, 1.3vw, 17.5px);
  line-height: 1.6;
  color: var(--ink-muted);
  max-width: var(--measure);
}

.measure { max-width: var(--measure); }

/* ----------------------------------------------------- шапка секции */

.section-head {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-end;
  justify-content: space-between;
  gap: 14px 32px;
  margin-bottom: clamp(24px, 3vw, 40px);
}

.section-head__text { max-width: var(--measure); }
.section-head .eyebrow + .h2,
.section-head .eyebrow + .h1 { margin-top: 10px; }
.section-head .h2 + .lead,
.section-head .h1 + .lead { margin-top: 12px; }

.section-head__action {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font-size: 14.5px;
  font-weight: 600;
  color: var(--crimson);
  white-space: nowrap;
  padding-bottom: 2px;
  border-bottom: 1px solid transparent;
}

.section-head__action:hover { border-color: var(--crimson); }

/* ------------------------------------------------------------- сетки */

.grid-cards {
  display: grid;
  gap: var(--gap-grid);
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 280px), 1fr));
}

/* Для широких карточек: одна не превращается в узкий огрызок посреди пустоты. */
.grid-cards--wide {
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 420px), 1fr));
}

.grid-cards > * { min-width: 0; }

/* Карточка с растягивающимся телом прижимает низ к низу — но только там, где
   об этом попросили: у остальных карточек своя раскладка, и ломать её нельзя. */
.card:has(> .card-body) { display: flex; flex-direction: column; }
.card > .card-body { flex: 1; }

/* ------------------------------------------------------- пустые места */

.empty {
  display: grid;
  justify-items: center;
  gap: 14px;
  padding: clamp(36px, 6vw, 64px) 24px;
  text-align: center;
  border: 1px dashed var(--line);
  border-radius: var(--radius-lg);
  background: var(--paper);
  color: var(--ink-muted);
}

.empty__title { font-size: 17px; font-weight: 600; color: var(--ink); }

/* ---------------------------------------------------------- панель дел */

.toolbar {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 12px 20px;
  padding: 14px clamp(16px, 2vw, 22px);
  background: var(--cream-deep);
  border-bottom: 1px solid var(--line);
}

.toolbar__label {
  font-size: 12px;
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--ink-muted);
}

/* ------------------------------------------------------------ факты */

/* Плитки с числами. На телефоне — ровно две в ряд, и число задано явно,
   а не выведено из минимальной ширины колонки.

   Так пришлось сделать после промаха. `auto-fit` с `minmax` подбирает число
   колонок сам, и на первый взгляд достаточно уменьшить порог со 150px до
   112. Но внутри карточки на экране в 320px под сетку остаётся 238 пикселей,
   а двум колонкам по 112 с зазором в 16 нужно 240 — не хватило двух. Сетка
   молча свалилась в один столбец, и «Впереди», «Посетили», «Пропустили»,
   «В курсе» снова растянулись на четыре экрана прокрутки.

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

   С 640px возвращается прежнее поведение: там места хватает, и колонок
   столько, сколько поместится. */
.facts {
  display: grid;
  gap: var(--gap-grid);
  grid-template-columns: repeat(2, minmax(0, 1fr));
}

@media (min-width: 640px) {
  .facts { grid-template-columns: repeat(auto-fit, minmax(min(100%, 150px), 1fr)); }
}

.fact {
  padding: clamp(16px, 2vw, 22px);
  background: var(--paper);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  text-align: center;
}

.fact dt {
  font-size: 12.5px;
  color: var(--ink-muted);
}

.fact dd {
  margin-top: 6px;
  font-size: clamp(22px, 2.4vw, 28px);
  font-weight: 700;
  line-height: 1.1;
}

/* --------------------------------------------------------- строка-ряд */

.row-list {
  display: grid;
  background: var(--paper);
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  overflow: hidden;
}

.row-list > * + * { border-top: 1px solid var(--line); }

.row-item {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 12px 20px;
  padding: 16px clamp(16px, 2vw, 22px);
  transition: background-color 0.15s ease;
}

.row-item:hover { background: var(--cream); }
.row-item__main { min-width: 0; flex: 1; }
.row-item__time { font-variant-numeric: tabular-nums; font-weight: 700; }

/* --------------------------------------------------- панель со списком */

.panel-list {
  display: grid;
  align-content: start;
  background: var(--paper);
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  overflow: hidden;
  box-shadow: var(--shadow-sm);
}

.panel-list > * + * { border-top: 1px solid var(--line); }

.panel-list__head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  padding: 12px clamp(16px, 2vw, 20px);
  background: var(--cream-deep);
}

/* Кнопка на тёмной полосе: границы из крема, а не отдельный цвет. */
.btn-on-ink {
  background: transparent;
  border-color: color-mix(in srgb, var(--cream) 38%, transparent);
  color: var(--cream);
}

.btn-on-ink:hover {
  background: color-mix(in srgb, var(--cream) 12%, transparent);
  border-color: var(--cream);
}

/* --------------------------------------------------------- отзывы: сетка */

.review-card {
  height: 100%;
}

/* Длинная цитата не растягивает карточку: обрезаем и предлагаем оригинал. */
.review-quote {
  display: -webkit-box;
  -webkit-line-clamp: 11;
  -webkit-box-orient: vertical;
  overflow: hidden;
}

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

/* Секция того же цвета, что и соседняя, отделяется волоском. */
.section--edge { border-top: 1px solid var(--line); }

/* Тёмная полоса идёт вплотную к подвалу: воздух между ними читается как дыра. */
main:has(> .section--ink:last-child) + .site-footer { margin-top: 0; }

.section-hero { padding-block: var(--stack-gap); }

/* ------------------------------------------------------ шапка страницы */

/* Шапка страницы — тот же задник, что и у первого экрана: глубокий крем,
   тёплое свечение, сетка точек. Следом всегда идёт секция другого тона,
   поэтому два одинаковых фона подряд встретиться не могут. */
.page-head { padding-block: clamp(16px, 1.8vw, 24px); }

/* Заголовок внутренней страницы меньше, чем на первом экране: там он несёт
   рекламную нагрузку, здесь — только называет раздел. */
.page-head .h1 { font-size: clamp(27px, 3vw, 38px); }
.page-head .lead { margin-top: 12px; }
.page-head--tight { padding-block: var(--stack-gap) 0; }

/* ------------------------------------------------------------ документы */

.doc-card {
  padding: 0;
  overflow: hidden;
  text-align: left;
}

/* Документ показываем целиком: обрезанный по краю скан выглядит как ошибка. */
.doc-card__image {
  width: 100%;
  height: 210px;
  object-fit: contain;
  padding: 14px;
  background: var(--cream-deep);
  border-bottom: 1px solid var(--line);
}

.doc-card__body {
  display: block;
  padding: clamp(16px, 2vw, 22px);
}

/* После шапки страницы верхний отступ меньше: полоса уже задала паузу. */
.section-head { margin-bottom: clamp(16px, 1.8vw, 22px); }

.page-head--tight { padding-block: clamp(26px, 3.5vw, 40px) 0; }
.page-head--tight .tabs { margin-bottom: 0; }

/* ------------------------------------------------------- меню на мобиле */

.nav-mobile {
  border-top: 1px solid var(--line);
  background: var(--paper);
}

/* Полоса разделов прокручивается вбок, и до сих пор об этом ничто не
   говорило: на экране в 320px «Отзывы» и «О нас» просто обрывались у края,
   и выглядело это не как продолжение списка, а как криво сверстанный конец.

   Тень у края решает это без единой строки JS. Приём держится на разнице
   `background-attachment`: слои `local` едут вместе с содержимым, слои
   `scroll` остаются на месте. Пока список прокручен к началу, кремовый
   `local`-слой стоит ровно поверх левой тени и её не видно; стоит сдвинуть
   список — он уезжает, и тень открывается. Справа всё то же зеркально.

   Отсюда главное свойство: подсказка появляется сама и только тогда, когда
   листать действительно есть куда. На планшете, где все разделы помещаются,
   теней не будет вовсе. */
.nav-mobile__inner {
  display: flex;
  /* 8 + по 5px полей у соседних пунктов = прежние 18px между подписями. */
  gap: 8px;
  overflow-x: auto;
  scrollbar-width: none;
  -webkit-overflow-scrolling: touch;
  /* Смахивание в конце списка не должно уводить браузер «назад» по истории. */
  overscroll-behavior-x: contain;
  background:
    linear-gradient(to right, var(--paper) 40%, transparent) left center / 32px 100% no-repeat local,
    linear-gradient(to left, var(--paper) 40%, transparent) right center / 32px 100% no-repeat local,
    radial-gradient(farthest-side at 0 50%, rgb(20 22 31 / 0.13), transparent) left center / 14px 100% no-repeat scroll,
    radial-gradient(farthest-side at 100% 50%, rgb(20 22 31 / 0.13), transparent) right center / 14px 100% no-repeat scroll;
}

.nav-mobile__inner::-webkit-scrollbar { display: none; }

/* Поля крайних пунктов вынесены за край, иначе первый заголовок стоял бы на
   пять пикселей правее остального текста страницы. */
.nav-mobile__inner > :first-child { margin-left: -5px; }
.nav-mobile__inner > :last-child { margin-right: -5px; }

/* Короткие пункты («Курсы», «О нас») набирали в ширину всего 37 пикселей, и
   палец попадал между ними. Промежуток переехал из `gap` в собственные поля
   пункта: расстояние между подписями осталось прежним, но пустота теперь
   принадлежит ссылке и нажимается вместе с ней. */
.nav-mobile__link {
  padding: 11px 5px;
  font-size: 14px;
  font-weight: 500;
  white-space: nowrap;
  color: var(--ink-muted);
  border-bottom: 2px solid transparent;
}

.nav-mobile__link.is-active { color: var(--ink); border-color: var(--crimson); }

/* Узкий экран: действие уезжает на свою строку, а не сплющивает текст. */
@media (max-width: 560px) {
  .row-item { align-items: flex-start; }
  .row-item__main { flex: 1 0 100%; order: -1; }
  .slider-tabs {
    flex-wrap: nowrap;
    overflow-x: auto;
    scrollbar-width: none;
    padding-bottom: 4px;
  }
  .slider-tab { white-space: nowrap; }
  .slider-tabs::-webkit-scrollbar { display: none; }
  .slider-arrows { display: none; }
}

/* ==========================================================================
   Лист страницы

   Через все секции вертикально идёт один белый лист — он общий для всех
   страниц и задаётся в base.html. Содержимое лежит на нём, поэтому не висит
   в воздухе. Фоны секций остаются под листом и видны только по бокам, как
   поля вокруг страницы, а внутри листа границы разделов проведены волоском.
   ========================================================================== */

/* Каждый раздел лежит на своей подложке — как первый экран. Страница
   становится стопкой карточек на кремовом поле, и «висеть в воздухе»
   становится нечему. */
/* Боковой отступ страницы живёт на секции, а не на контейнере: сама подложка
   занимает всю ширину контейнера, поэтому у слайдера и у остальных разделов
   ширина одна. */
.section,
.page-head,
.hero {
  padding-inline: 20px;
  scroll-snap-align: start;
  /* Прокрутка не проскакивает раздел целиком: один жест — одна карточка. */
  scroll-snap-stop: always;
  /* Шапка липкая — иначе она накрыла бы верх карточки. */
  scroll-margin-top: var(--header-h);
}

.section > .wrap,
.section > .wrap-narrow,
.page-head > .wrap,
.page-head > .wrap-narrow,
.wrap.section,
.wrap-narrow.section,
.section-hero {
  padding-inline: 0;
}

.section > .wrap,
.section > .wrap-narrow,
.page-head > .wrap,
.page-head > .wrap-narrow,
.wrap.section,
.wrap-narrow.section {
  background: var(--paper);
  border: 1px solid var(--line);
  border-radius: clamp(16px, 1.8vw, 26px);
  /* По вертикали поля заметно уже, чем по горизонтали: сверху и снизу их
     держит ещё и зазор между подложками, и вместе получалось слишком пусто. */
  padding: clamp(16px, 1.8vw, 24px) clamp(20px, 2.6vw, 34px);
  box-shadow: 0 1px 2px rgb(20 22 31 / 0.05), 0 12px 32px rgb(20 22 31 / 0.06);
}

/* Между подложками — зазор, а не крупные поля секции. */
.section { padding-block: var(--stack-gap); }
.wrap.section,
.wrap-narrow.section { margin-block: var(--stack-gap); }
.page-head { padding-block: var(--stack-gap); }

/* Первая подложка страницы отбивается от шапки сайта, последняя — от подвала. */
main > .sheet > :first-child { padding-top: calc(var(--stack-gap) * 1.6); }
main > .sheet > :last-child { padding-bottom: calc(var(--stack-gap) * 1.6); }

/* Тёмный призыв — такая же подложка, только тёмная. */
.section--ink > .wrap {
  background: var(--ink);
  border-color: var(--ink);
  color: var(--cream);
}

/* Полосы, которые видно по краям листа. Светлая и тёмная чередуются. */
/* Фон страницы один — кремовый. Ритм держат подложки и зазоры между ними,
   а не заливка полос. */
.section,
.page-head,
.band-light,
.band-dark { background: transparent; }

.hero { background: none; border-bottom: 0; }

/* -------------------------------------------------------- поток-карточка */

/* Свёрнутый поток — карточка товара, а не строка списка: слева то, по чему
   поток выбирают, справа — сколько это стоит и кнопка. Цена стоит в свёрнутом
   виде намеренно: главный вопрос витрины не должен требовать клика.

   У каждого потока своя подложка, она и есть карточка, поэтому рамки и фона
   у самого <details> нет. Экспандер нативный, как аккордеон вопросов: без JS,
   с рабочей клавиатурой и поиском по странице. */
.stream__head {
  display: grid;
  gap: clamp(16px, 2vw, 22px);
  cursor: pointer;
  list-style: none;
}

.stream__head::-webkit-details-marker { display: none; }

.stream__top {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 16px;
}

/* Надзаголовок потока: зелёный квадрат — метка «идут живые занятия», одна и
   та же у всех потоков. Цвета, различающие потоки, живут в календаре, где они
   и нужны: здесь потоки разделены подложками. */
.stream__eyebrow {
  display: flex;
  align-items: center;
  gap: 9px;
  font-size: 12px;
  font-weight: 700;
  letter-spacing: 0.09em;
  text-transform: uppercase;
  color: var(--ink-muted);
}

.stream__title {
  margin-top: 10px;
  font-size: clamp(19px, 2vw, 24px);
  line-height: 1.2;
  font-weight: 700;
  letter-spacing: -0.02em;
}

.stream__note {
  margin-top: 8px;
  font-size: 14.5px;
  line-height: 1.5;
  color: var(--ink-muted);
  max-width: var(--measure);
}

.stream__grid {
  display: grid;
  gap: clamp(16px, 2vw, 24px);
  align-items: stretch;
}

@media (min-width: 780px) {
  .stream__grid { grid-template-columns: minmax(0, 1fr) minmax(240px, 300px); }
}

/* Факты парами «подпись — значение»: ровная сетка читается быстрее, чем ряд
   плиток, где значения разной длины прыгают по кеглю. */
.stream__specs {
  display: grid;
  gap: 10px 0;
  align-content: start;
}

.stream__specs > div {
  display: flex;
  gap: 14px;
  padding-bottom: 10px;
  border-bottom: 1px solid var(--line);
}

.stream__specs > div:last-child { border-bottom: 0; padding-bottom: 0; }

.stream__specs dt {
  flex: 0 0 108px;
  font-size: 13.5px;
  color: var(--ink-muted);
}

.stream__specs dd {
  font-size: 15px;
  font-weight: 600;
  color: var(--ink);
}

/* Правый столбец — то, ради чего карточку открывают. */
.stream__buy {
  display: flex;
  flex-direction: column;
  justify-content: center;
  gap: 14px;
  padding: clamp(16px, 2vw, 22px);
  border-radius: var(--radius);
  background: var(--cream);
  border: 1px solid var(--line);
  text-align: center;
}

.stream__price {
  font-size: clamp(22px, 2.4vw, 27px);
  font-weight: 700;
  line-height: 1.15;
  letter-spacing: -0.02em;
  white-space: nowrap;
}

.stream__price-unit {
  display: block;
  margin-top: 4px;
  font-size: 13px;
  font-weight: 500;
  color: var(--ink-muted);
}

.stream__toggle {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 10px;
  padding: 13px 22px;
  border-radius: 999px;
  background: var(--crimson);
  color: #fff;
  font-size: 15px;
  font-weight: 600;
  box-shadow: 0 6px 18px color-mix(in srgb, var(--crimson) 28%, transparent);
  transition: background-color 0.15s ease;
}

.stream__head:hover .stream__toggle { background: var(--crimson-deep); }
.stream__head:hover .stream__title { color: var(--crimson); }

.stream__chevron {
  width: 8px;
  height: 8px;
  border-right: 2px solid currentColor;
  border-bottom: 2px solid currentColor;
  transform: translateY(-2px) rotate(45deg);
  transition: transform 0.15s ease;
}

.stream[open] .stream__chevron { transform: translateY(1px) rotate(225deg); }

/* Подпись кнопки меняется показом второго span, а не подменой текста в CSS. */
.stream__label--less { display: none; }
.stream[open] .stream__label--more { display: none; }
.stream[open] .stream__label--less { display: inline; }

/* Раскрытая карточка: кнопка уже не главное действие — главным становится
   «Записаться» внутри, поэтому тон снимаем. */
.stream[open] .stream__toggle {
  background: transparent;
  color: var(--ink);
  border: 1px solid var(--line);
  box-shadow: none;
}

.stream[open] .stream__head:hover .stream__toggle {
  background: var(--paper);
  border-color: var(--ink);
}

.stream[open] .stream__head {
  padding-bottom: clamp(16px, 2vw, 22px);
  border-bottom: 1px solid var(--line);
}

/* Карточка на странице своего курса: та же вёрстка, но сворачивать нечего. */
.stream--static > .stream__head { cursor: default; pointer-events: none; }

.stream__body { padding-top: clamp(18px, 2.2vw, 26px); }

/* ------------------------------------------------------------- вопросы */

.faq {
  display: grid;
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  overflow: hidden;
  background: var(--cream);
}

.faq__item + .faq__item { border-top: 1px solid var(--line); }

.faq__question {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  padding: 16px clamp(16px, 2vw, 22px);
  font-size: 15.5px;
  font-weight: 600;
  cursor: pointer;
  list-style: none;
}

.faq__question::-webkit-details-marker { display: none; }

.faq__question::after {
  content: "+";
  font-size: 20px;
  font-weight: 400;
  color: var(--ink-muted);
  transition: transform 0.15s ease;
}

.faq__item[open] .faq__question::after { transform: rotate(45deg); }
.faq__question:hover { color: var(--crimson); }

.faq__answer {
  padding: 0 clamp(16px, 2vw, 22px) 18px;
  font-size: 15px;
  line-height: 1.65;
  color: var(--ink-soft);
  max-width: var(--measure);
}

/* Длинный ответ разбит на абзацы — без отбивки они слипаются в стену текста
   (сброс Tailwind обнуляет отступы у <p>). */
.faq__answer p + p { margin-top: 12px; }

/* На узком экране под шапкой ещё полоса навигации — привязку сдвигаем ниже.
   Граница парная к `lg:hidden` у полосы в base.html: пока видна полоса,
   липкая шапка выше на её высоту. Разъедутся — заголовок раздела будет
   останавливаться под шапкой, наполовину скрытым. */
@media (max-width: 1023px) {
  :root { --header-h: 114px; }
}

/* Фильтр в шапке раздела: он относится к списку ниже, но живёт на той же
   подложке, что заголовок и факты, — иначе висел бы между ними ничей. */
.head-filter {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  /* По правому краю: слева от него уже стоят факты и кнопки, и ещё один
     ряд у левого края читался бы их продолжением. */
  justify-content: flex-end;
  gap: 10px 16px;
  margin-top: 22px;
  padding-top: 20px;
  border-top: 1px solid var(--line);
}

.head-filter__label {
  font-size: 12px;
  font-weight: 700;
  letter-spacing: 0.09em;
  text-transform: uppercase;
  color: var(--ink-muted);
}

.head-filter__row { display: flex; flex-wrap: wrap; gap: 8px; }

.chip-filter {
  padding: 7px 14px;
  border: 1px solid var(--line);
  border-radius: 999px;
  background: var(--cream);
  font-size: 14px;
  font-weight: 500;
  color: var(--ink-soft);
  transition: border-color 0.15s ease, color 0.15s ease;
}

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

.chip-filter.is-active {
  border-color: var(--crimson);
  background: var(--crimson);
  color: #fff;
}

/* Приглашение в видеокурсы под списком потоков: тот же лист, но заметно
   спокойнее — это запасной путь, а не главный. */
.band-invite {
  background: var(--cream);
  border-color: var(--line);
}

/* Приглашение: слева объяснение, справа действие. Одна колонка на узком
   экране — кнопка тогда идёт под текстом, как и читается. */
.invite-split {
  display: grid;
  gap: 20px;
  align-items: center;
}

.invite-split__action { display: flex; justify-content: flex-start; }

@media (min-width: 860px) {
  .invite-split { grid-template-columns: minmax(0, 1fr) minmax(240px, 0.55fr); gap: 32px; }
  .invite-split__action { justify-content: center; }
}

/* Шапка раздела: текст слева, знак раздела справа. Картинка декоративная,
   поэтому на узком экране исчезает целиком — место нужно тексту. */
.head-art {
  display: grid;
  gap: 24px;
  align-items: center;
}

.head-art__svg { display: none; }

@media (min-width: 900px) {
  .head-art { grid-template-columns: minmax(0, 1fr) minmax(200px, 300px); }
  .head-art__svg {
    display: block;
    width: 100%;
    max-width: 260px;
    height: auto;
    margin-inline: auto;
  }
}

/* Номер темы в программе живого курса: у неё нет ни замка, ни даты выхода —
   это оглавление, а не календарь. */
.lesson-num {
  flex-shrink: 0;
  width: 26px;
  height: 26px;
  display: grid;
  place-items: center;
  border-radius: 8px;
  background: var(--cream-deep);
  font-size: 12.5px;
  font-weight: 700;
  color: var(--ink-muted);
}

/* Раздел из двух колонок, где заголовок принадлежит левой. Правая колонка
   тогда занимает всю высоту блока, и её содержимое можно центрировать —
   иначе оно начиналось бы под заголовком и «съезжало» вниз. */
.split-head {
  display: grid;
  gap: 24px;
  align-items: center;
}

@media (min-width: 900px) {
  .split-head { grid-template-columns: minmax(0, 1.1fr) minmax(280px, 0.9fr); gap: 48px; }
}

/* ==========================================================================
   Реквизиты исполнителя
   ========================================================================== */

/* Пара «что — значение» строкой: на широком экране подпись слева, значение
   справа; на телефоне подпись встаёт над значением, иначе ОГРНИП переносится
   по цифрам. */
.legal-list > div {
  display: grid;
  gap: 2px 24px;
  padding: 12px 0;
  border-top: 1px solid var(--line);
}
.legal-list > div:first-child { border-top: 0; padding-top: 0; }
.legal-list dt { font-size: 14px; color: var(--muted); }
.legal-list dd { font-size: 15.5px; }

@media (min-width: 640px) {
  .legal-list > div { grid-template-columns: 220px 1fr; align-items: baseline; }
}

/* Юридический текст: длинный, читают его по разделам, поэтому заголовки
   разделов заметнее обычных, а формула возврата выделена как блок. */
.prose-legal h2 { margin-top: 32px; font-size: 19px; }
.prose-legal ul { margin: 8px 0 14px; padding-left: 20px; list-style: disc; }
.prose-legal li { margin-bottom: 4px; }
.prose-legal p { margin-bottom: 10px; }

/* Формула возврата — единственное место оферты, где важна не фраза, а
   расчёт: клиент должен суметь повторить его на калькуляторе. */
.alert-neutral {
  border-left: 3px solid var(--crimson);
  background: var(--cream);
  padding: 14px 16px;
  border-radius: var(--radius);
}

/* Галочка согласия: квадрат слева, текст справа и на всю оставшуюся ширину.
   Текст в две строки — с ссылками на документы, поэтому выравниваем по верху. */
.field-check {
  display: flex;
  align-items: flex-start;
  gap: 10px;
}
.field-check input[type="checkbox"] {
  width: 18px;
  height: 18px;
  margin-top: 2px;
  flex-shrink: 0;
  accent-color: var(--crimson);
}

/* Та же галочка пальцем. Правило стоит именно здесь, а не в общем блоке про
   размер под палец: у `.field-check input` та же специфичность, и общее
   правило, объявленное выше по файлу, эта строка просто перебила бы.
   Галочка согласия — обязательное поле на форме оплаты: не попал по ней,
   не купил. */
@media (pointer: coarse) {
  .field-check input[type="checkbox"] {
    width: 24px;
    height: 24px;
    margin-top: 0;
  }
}
.field-check label {
  font-size: 14px;
  line-height: 1.45;
  color: var(--ink-soft);
}

/* Поле-приманка для скриптов: человеку его не видно и не поймать табом.
   `display: none` не годится — часть ботов такие поля пропускает. */
.antispam-field {
  position: absolute !important;
  left: -9999px !important;
  width: 1px !important;
  height: 1px !important;
  opacity: 0 !important;
  pointer-events: none;
}

/* ==========================================================================
   Домашняя работа: ответ ученика
   ========================================================================== */

/* Вариант теста — кликабельная строка целиком, а не только сам кружок:
   на телефоне попасть по нему пальцем иначе трудно. */
.answer-option {
  display: flex;
  align-items: flex-start;
  gap: 10px;
  padding: 6px 8px;
  margin-left: -8px;
  border-radius: var(--radius);
  cursor: pointer;
}
.answer-option:hover { background: var(--cream); }
.answer-option input { margin-top: 4px; accent-color: var(--crimson); flex-shrink: 0; }

/* Вариант ответа пальцем. Сам кружок остаётся браузерным и по умолчанию
   занимает тринадцать пикселей — целиться в него на телефоне бессмысленно.
   Растим и его, и строку целиком: нажимается вся строка, потому что <input>
   обёрнут в <label>, и попасть достаточно в текст варианта. */
@media (pointer: coarse) {
  .answer-option {
    min-height: 44px;
    align-items: center;
    padding: 8px;
  }

  .answer-option input {
    width: 22px;
    height: 22px;
    margin-top: 0;
  }
}

/* Поле живёт вне `.form-styled`, поэтому кегль ему приходится задавать
   отдельно — и по той же причине, что и там: мельче 16px Safari на iPhone
   увеличивает страницу, едва палец попадёт в поле. Домашнюю работу с
   телефона сдают чаще, чем с компьютера, так что место самое неудачное. */
.answer-input {
  width: 100%;
  max-width: 520px;
  padding: 10px 12px;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: #fff;
  font-size: 16px;
}

@media (min-width: 768px) {
  .answer-input { font-size: 15px; }
}
.answer-input:focus { outline: 2px solid var(--crimson); outline-offset: 1px; }

/* Состояние ответа: цвет несёт смысл, поэтому подписи всегда со словом —
   «верно» читается и без цвета. */
.answer-state {
  font-size: 13.5px;
  font-weight: 600;
  padding: 3px 10px;
  border-radius: 999px;
}
.answer-state--correct { background: var(--success-soft); color: var(--success); }
.answer-state--wrong { background: var(--crimson-soft); color: var(--crimson-deep); }
.answer-state--pending { background: var(--amber-soft); color: var(--warning); }
.answer-state--reviewed { background: var(--cream); color: var(--ink-soft); }

/* Счётчик непроверенных работ в закладке: цифра рядом с подписью, а не
   вместо неё — «Домашка 3» читается сразу, «3» само по себе нет. */
.tab-badge {
  display: inline-block;
  margin-left: 6px;
  padding: 1px 7px;
  border-radius: 999px;
  background: var(--crimson);
  color: #fff;
  font-size: 12px;
  font-weight: 600;
}

/* Работа ученика в очереди проверки: цитата с полосой, а не заливка. */
.answer-quote {
  border-left: 3px solid var(--crimson);
  padding: 4px 0 4px 14px;
  font-size: 15px;
  line-height: 1.55;
  white-space: pre-line;
}

/* ==========================================================================
   Клавиатура
   ========================================================================== */

/* Фокус виден и узнаваем: браузерная тонкая рамка на кремовом фоне теряется,
   а ходить по сайту с клавиатуры должно быть возможно не наощупь.
   `:focus-visible`, а не `:focus`: мышке обводка не нужна. */
:where(a, button, input, select, textarea, summary, [tabindex]):focus-visible {
  outline: 3px solid var(--crimson);
  outline-offset: 2px;
  border-radius: 6px;
}

/* На тёмной подложке алый по тёмному не читается — там обводка светлая. */
.section--ink :where(a, button):focus-visible,
.site-footer :where(a, button):focus-visible {
  outline-color: var(--cream);
}
