/* Детская веб-версия.
 *
 * Цвета взяты из `ui/theme/ShturmColors.kt` один в один, а не подобраны на
 * глаз: ребёнок, пришедший с телефона на компьютер, должен увидеть то же
 * приложение. Токены названы так же, чтобы правку палитры можно было перенести
 * поиском по имени.
 *
 * Тёмная тема — по системной настройке, а выбор ребёнка (`data-theme` на корне)
 * побеждает её в обе стороны.
 *
 * ## Что изменилось 05.09.2026 (осмотр оформления)
 *
 * До этой правки веб-версия брала из приложения ТОЛЬКО семь цветов: не было ни
 * фирменных шрифтов, ни «подошвы» под кнопкой, ни золота награды, ни иконок
 * предметов, ни чипов состояния. Совпадал фон — и на этом сходство кончалось.
 * Ребёнок, зашедший с чужого телефона, видел не «Штурм знаний» в браузере, а
 * блёклую анкету на песочном фоне. Здесь добавлено ровно то, что делает
 * приложение узнаваемым, и ни одного нового решения о цвете: всё, что ниже,
 * есть в `ShturmColors`, `ShturmType` и `Dimens`.
 */

/* ── Шрифты ──────────────────────────────────────────────────────────────
 *
 * Лежат рядом, а не берутся с CDN: сторонний шрифт — это ещё один сервис,
 * который однажды не ответит, и запрос, уходящий к нему с адресом каждого
 * ребёнка.
 *
 * Копия файлов на сервере — сознательная. В `lk.css` записан отказ («500 КБ
 * дублей»), и он был верен для TTF: детская версия открывается и с
 * api.-поддомена, где статики витрины нет, поэтому взять её оттуда нельзя.
 * Здесь тот же набор пережат в woff2 — 147 КБ на четыре начертания вместо 436,
 * и одну копию делят оба кабинета. Russo One несёт весь характер марки: без
 * него заголовки набираются системным шрифтом, и продукт перестаёт узнаваться
 * с первого взгляда.
 *
 * `font-display: swap` — текст читается ещё до того, как шрифт доехал: у
 * ребёнка канал бывает медленным, а пустой экран он читает как поломку. */
@font-face {
  font-family: "Russo One";
  src: url("/play/static/fonts/russo_one.woff2") format("woff2");
  font-weight: 400; font-style: normal; font-display: swap;
}
@font-face {
  font-family: "Nunito";
  src: url("/play/static/fonts/nunito_400.woff2") format("woff2");
  font-weight: 400; font-style: normal; font-display: swap;
}
@font-face {
  font-family: "Nunito";
  src: url("/play/static/fonts/nunito_700.woff2") format("woff2");
  font-weight: 700; font-style: normal; font-display: swap;
}
@font-face {
  font-family: "Nunito";
  src: url("/play/static/fonts/nunito_800.woff2") format("woff2");
  font-weight: 800; font-style: normal; font-display: swap;
}

/* ── Токены ──────────────────────────────────────────────────────────────
 * Светлая колонка таблицы 1.2 (README-007) целиком, а не выборка из семи
 * значений: золото награды, «подошва» кнопки и цвет запертого нужны здесь ровно
 * так же, как в приложении. */
:root {
  --ink:            #23261C;
  --bg:             #F2EDE3;
  --surface:        #FFFFFF;
  --surface-alt:    #EFE8D8;
  --border:         #E0D9C9;
  --accent:         #8A4B10;
  --accent-shadow:  #6B3A0C;
  --reward:         #E3A62B;
  --on-reward:      #4A3608;
  --on-accent:      #FFFFFF;
  --muted:          #4C5140;
  --text-disabled:  #6E7358;
  --success:        #4A7C3F;
  --success-text:   #3D6B33;
  --danger:         #B23A2E;
  --danger-text:    #A32E22;
  --locked:         #CFC5AE;
  --disabled-fill:  #EBE4D4;
  --progress-track: #E6DFCF;

  /* Шрифты. Заголовки, числа и надписи на кнопках — Russo One; всё остальное —
     Nunito. Разделение то же, что в `ShturmType`. */
  --display: "Russo One", "Segoe UI", system-ui, sans-serif;
  --body:    "Nunito", "Segoe UI", system-ui, -apple-system, sans-serif;

  /* Радиусы и размеры — `Dimens`. */
  --r-card:  18px;
  --r-small: 12px;
  --r-chip:  7px;
  --r-pill:  999px;
  --sole:    5px;   /* высота «подошвы» главной кнопки */

  /* Градиенты шапки — числа из `HeaderScrim` в приложении, не подобранные на
     глаз. Плотность здесь не украшение: по железному правилу 1 текст и кнопки
     лежат на градиенте, а не на чистой фотографии, иначе читаемость зависит от
     того, что попало в кадр. Тон — `headerScrimTop`/`headerScrimBottom`. */
  --scrim-tall:    linear-gradient(180deg, rgba(26,28,22,.45) 0%, rgba(26,28,22,.82) 50%, rgba(26,28,22,.96) 100%);
  --scrim-compact: linear-gradient(180deg, rgba(26,28,22,.80) 0%, rgba(26,28,22,.92) 100%);
  --scrim-result:  linear-gradient(180deg, rgba(26,28,22,.72) 0%, rgba(26,28,22,.94) 100%);

  --gap:     12px;
  --wrap:    520px;
}

/* Тёмная колонка той же таблицы.
 *
 * `--accent-shadow` здесь СВЕТЛЕЕ акцента, а не темнее: любой цвет темнее
 * акцента сливается с фоном, и подошва пропадает вместе с ощущением кнопки.
 * Так же сделано в приложении. */
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    --ink:            #F2EDE3;
    --bg:             #12150D;
    --surface:        #22261A;
    --surface-alt:    #2C3122;
    --border:         #4E553F;
    --accent:         #A85D14;
    --accent-shadow:  #C97F27;
    --muted:          #A8AE95;
    --text-disabled:  #8A9179;
    --success-text:   #8FBF7F;
    --danger:         #C24436;
    --danger-text:    #E98B7C;
    --locked:         #4A5040;
    --disabled-fill:  #2A2F20;
    --progress-track: #3E4433;
    /* В тёмной теме низ градиента уходит в `surface`, а не в `bg`: иначе
       тёмная шапка растворяется в тёмном полотне (README-007, 1.4). */
    --scrim-tall:    linear-gradient(180deg, rgba(18,21,13,.55) 0%, rgba(26,29,19,.84) 50%, rgba(34,38,26,.94) 100%);
    --scrim-compact: linear-gradient(180deg, rgba(18,21,13,.80) 0%, rgba(34,38,26,.92) 100%);
    --scrim-result:  linear-gradient(180deg, rgba(18,21,13,.72) 0%, rgba(34,38,26,.94) 100%);
  }
}

/* Выбор ребёнка побеждает систему в ОБЕ стороны. Без второго блока светлое
   оформление не включилось бы на устройстве с тёмной системой. */
:root[data-theme="dark"] {
  --ink:            #F2EDE3;
  --bg:             #12150D;
  --surface:        #22261A;
  --surface-alt:    #2C3122;
  --border:         #4E553F;
  --accent:         #A85D14;
  --accent-shadow:  #C97F27;
  --muted:          #A8AE95;
  --text-disabled:  #8A9179;
  --success-text:   #8FBF7F;
  --danger:         #C24436;
  --danger-text:    #E98B7C;
  --locked:         #4A5040;
  --disabled-fill:  #2A2F20;
  --progress-track: #3E4433;
  /* В тёмной теме низ градиента уходит в `surface`, а не в `bg`: иначе
     тёмная шапка растворяется в тёмном полотне (README-007, 1.4). */
  --scrim-tall:  linear-gradient(180deg, rgba(18,21,13,.55) 0%, rgba(26,29,19,.84) 50%, rgba(34,38,26,.94) 100%);
  --scrim-compact: linear-gradient(180deg, rgba(18,21,13,.80) 0%, rgba(34,38,26,.92) 100%);
  --scrim-result:  linear-gradient(180deg, rgba(18,21,13,.72) 0%, rgba(34,38,26,.94) 100%);
}

* { box-sizing: border-box; }

body {
  margin: 0;
  /* Поля объявлены переменными, потому что шапка с кадром их ГАСИТ: кадр идёт
     во всю ширину колонки, и отрицательные поля должны совпадать с этими
     числами, а не повторять их вручную в трёх местах. */
  --pad-x: 16px;
  --pad-top: 14px;
  padding: var(--pad-top) var(--pad-x) 16px;
  background: var(--bg);
  color: var(--ink);
  font-family: var(--body);
  font-weight: 700;          /* основной текст в приложении — Nunito Bold */
  font-size: 17px;
  line-height: 1.45;
  -webkit-font-smoothing: antialiased;
  text-underline-offset: 3px;
}

/* Ширина под телефон: веб-версию открывают в основном с чужого телефона, а не
   с монитора. На широком экране колонка просто встаёт по центру. */
.wrap { max-width: var(--wrap); margin: 0 auto; }
/* Место под нижнюю панель. Без него она накрывает последнюю строку длинного
   списка тем — а последняя тема в списке ровно та, до которой ребёнок дошёл. */
body:has(.tabs) { padding-bottom: calc(96px + env(safe-area-inset-bottom, 0px)); }

/* Видимая рамка фокуса — для тех, кто ходит по странице с клавиатуры: на
   цветных кнопках браузерное умолчание не видно. */
:focus-visible { outline: 3px solid var(--accent); outline-offset: 2px; }

/* Размер знака задаётся ВСЕГДА и здесь.
 *
 * У встроенного `<svg>` без ширины и высоты собственного размера нет, и браузер
 * подставляет ему стандартные 300 × 150 — знак в ссылке «← В штаб» разъезжается
 * на пол-экрана. Правило ниже — страховка: каждое место, где знак крупнее или
 * мельче (кнопка, вкладка, слот предмета), переопределяет её своим размером,
 * но забытый случай теперь выглядит просто знаком, а не аварией. */
svg { width: 20px; height: 20px; flex: none; vertical-align: -.18em; }

/* ── Марка ───────────────────────────────────────────────────────────────
 * Знак набирается шрифтом, а не картинкой: файл логотипа лежит в приложении в
 * пяти плотностях, и класть его копию сюда ради двух строк заголовка дорого. */
.logo {
  font-family: var(--display);
  font-weight: 400;
  letter-spacing: .04em;
  font-size: 26px;
  line-height: 1.05;
  color: var(--ink);
}
.logo span { display: block; color: var(--accent); font-size: 17px; }
.logo--small { font-size: 17px; }
.logo--small span { display: inline; font-size: 17px; margin-left: .35em; }

/* ── Шапка с кадром ──────────────────────────────────────────────────────
 *
 * То, чего вебу не хватало сильнее всего: в приложении экран открывается
 * фотографией под плотным градиентом, и по ней он узнаётся раньше, чем
 * прочитано хоть слово. Здесь та же конструкция и те же файлы: имена кадров
 * совпадают с `res/drawable-nodpi/` (см. `static/img/README.md`).
 *
 * Кадр идёт во всю ширину колонки — поэтому отрицательные поля гасят поля
 * страницы. Прямой нижний край, а не скруглённый: так же в приложении, и
 * скругление здесь читалось бы как ещё одна карточка.
 *
 * Файла нет — остаётся сплошная шапка цвета чернил. Это предусмотренное
 * состояние: пустая шапка выглядит как замысел, а не загрузившаяся картинка —
 * как сбой. */
.hero {
  position: relative;
  display: flex;
  flex-direction: column;
  justify-content: flex-end;
  gap: 4px;
  min-height: 132px;
  margin: calc(var(--pad-top) * -1) calc(var(--pad-x) * -1) 16px;
  padding: 16px var(--pad-x);
  color: var(--on-accent);
  background-color: var(--ink);
  background-image: var(--photo, none);
  background-size: cover;
  /* Кадр смещён к ВЕРХУ, а не по центру (05.09.2026). Сюжет в этих
     фотографиях живёт в верхней половине, а низ уходит под самую плотную часть
     градиента — при центрировании подрезалось именно то, ради чего кадр и
     снимали. 35 % — верх виден целиком, снизу срезается то, чего всё равно не
     видно под 96 % затемнения. */
  background-position: center 35%;
}
/* Градиент отдельным слоем, а не второй картинкой в `background-image`: так его
   плотность правится в одном месте на все экраны. */
.hero::before {
  content: "";
  position: absolute;
  inset: 0;
  background: var(--scrim-compact);
}
.hero > * { position: relative; }

/* Три высоты, как в `Dimens`: главный экран, ветка предмета, всё остальное.
   Четвёртой нет — иначе шапки начнут отличаться на десять пикселей без
   причины. */
.hero--tall   { min-height: 280px; }
.hero--tall::before { background: var(--scrim-tall); }
.hero--branch { min-height: 220px; }
.hero--branch::before { background: var(--scrim-tall); }
.hero--result::before { background: var(--scrim-result); }
.hero--center { align-items: center; justify-content: center; text-align: center; }

/* Текст на кадре — светлый в обеих темах: под градиентом плотностью 0,8 темно
   всегда, и «тёмный текст в тёмной теме» здесь означал бы нечитаемый. */
.hero .head { margin-bottom: 0; }
.hero .who,
.hero .logo,
.hero h1 { color: var(--on-accent); }
.hero .logo span { color: var(--reward); }
.hero h1 { margin: 6px 0 0; }
.hero .who small { color: rgba(255, 255, 255, .78); }
.hero .link { color: rgba(255, 255, 255, .92); }
.hero .lead { color: rgba(255, 255, 255, .82); margin: 4px 0 0; }
/* Аватар на кадре обводится: акцентный квадрат на тёмной фотографии теряет
   край, и буква повисает в воздухе. */
.hero .avatar { box-shadow: 0 0 0 2px rgba(255, 255, 255, .35); }
/* Чип серии на кадре — прозрачно-белый с золотым огоньком, ровно как в шапке
   приложения. Сплошное золото читалось бы третьим ярким пятном рядом с
   аватаром и кнопкой. */
.hero .chip--streak {
  background: rgba(255, 255, 255, .16);
  color: var(--on-accent);
  border-radius: var(--r-pill);
}
.hero .chip--streak svg { color: var(--reward); }

/* Шапка экрана: кто занимается — слева, состояние — справа. Та же расстановка,
   что в шапке приложения (#1a). */
.head {
  display: flex; align-items: center; gap: 10px;
  margin-bottom: 16px;
  min-height: 44px;
}
.head__spacer { margin-left: auto; }
.who { font-family: var(--display); font-size: 17px; color: var(--ink); }
.who small {
  display: block;
  font-family: var(--body); font-weight: 800;
  font-size: 11.5px; letter-spacing: .16em; text-transform: uppercase;
  color: var(--muted);
}

/* Квадратный аватар с буквой имени — `LetterAvatar` из приложения. Ребёнок
   узнаёт свой экран по нему раньше, чем прочитает имя. */
.avatar {
  flex: none;
  width: 44px; height: 44px;
  display: grid; place-items: center;
  border-radius: var(--r-small);
  background: var(--accent);
  color: var(--on-accent);
  font-family: var(--display);
  font-size: 20px;
  text-transform: uppercase;
}

/* ── Чипы состояния ──────────────────────────────────────────────────────
 * `StatusChip` приложения: короткое слово в рамке. Цвет — не единственный
 * носитель смысла (железное правило 006 №7), поэтому в чипе всегда есть текст. */
.chip {
  display: inline-flex; align-items: center; gap: 5px;
  padding: 5px 9px;
  border-radius: var(--r-chip);
  font-family: var(--display);
  font-size: 11px; letter-spacing: .08em;
  white-space: nowrap;
}
.chip--streak { background: var(--reward); color: var(--on-reward); }
.chip--open   { background: var(--accent); color: var(--on-accent); }
.chip--done   { background: var(--success); color: #FFFFFF; }
.chip--locked { background: var(--disabled-fill); color: var(--text-disabled); }
.chip svg { width: 13px; height: 13px; }

/* ── Карточки ────────────────────────────────────────────────────────────
 * Обводка 1.5 px и радиус 18 — `Dimens.BorderWidth` и `Dimens.CornerCard`.
 * Тени под карточками нет ни здесь, ни в приложении: тень отделяет карточку от
 * фона, а здесь их разделяет обводка. */
.card {
  background: var(--surface);
  border: 1.5px solid var(--border);
  border-radius: var(--r-card);
  padding: 16px;
  margin-bottom: var(--gap);
}
.card--center { text-align: center; padding: 26px 20px; }

h1 {
  font-family: var(--display); font-weight: 400;
  font-size: 26px; line-height: 1.15;
  margin: 14px 0 6px;
}
.lead { color: var(--muted); font-weight: 700; margin: 6px 0 16px; }

/* Заголовок секции: 11.5 px, капитель, трекинг 0.16em — `ShturmType.SectionLabel`. */
.label {
  font-family: var(--body); font-weight: 800;
  font-size: 11.5px; letter-spacing: .16em; text-transform: uppercase;
  color: var(--muted);
  margin-bottom: 6px;
}
.muted { color: var(--muted); font-size: 14px; font-weight: 700; margin-top: 6px; }

/* ── Копилка ─────────────────────────────────────────────────────────────
 * Самое крупное число экрана, как в приложении: ребёнок открывает штаб ради
 * него. Карточка подложена золотом награды — тем же `--reward`, которым в
 * приложении помечены деньги, а не акцентом: акцент здесь спорил бы с кнопкой
 * «ЗАНИМАТЬСЯ», а главная кнопка на экране одна. */
.money .sum {
  font-family: var(--display);
  font-size: 46px; line-height: 1;
  letter-spacing: .01em;
}
/* Знак ₽ мельче числа и цветом акцента — так же собран `MoneyText` в
   приложении (в Russo One глифа ₽ просто нет, и подставленный системный
   выбивался бы начертанием; здесь он честно набран Nunito). */
.money .sum span {
  font-family: var(--body); font-weight: 800;
  font-size: 26px; color: var(--accent);
}
.card--money {
  position: relative;
  overflow: hidden;
  background: linear-gradient(180deg, var(--surface) 0%, var(--surface-alt) 100%);
  border-color: var(--reward);
}
/* Монета в углу — единственная декорация экрана. Смысла не несёт и потому
   спрятана от скринридера; её задача — сделать карточку с деньгами непохожей
   на карточку со статистикой. */
.card--money .coin {
  position: absolute; right: -18px; top: -18px;
  width: 96px; height: 96px;
  border-radius: 50%;
  background: var(--reward);
  opacity: .18;
}
.money--small .sum { font-size: 30px; }
.money--small .sum span { font-size: 18px; }

/* ── Ряд показателей ─────────────────────────────────────────────────────
 * Три плитки в строку. `minmax(0, 1fr)` вместо `flex: 1` — длинное звание
 * («Младший сержант») иначе распирает свою колонку и сдвигает две соседние. */
.row {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 8px;
}
.row .stat { text-align: center; padding: 12px 8px; }
.stat .value {
  font-family: var(--display);
  font-size: 20px; line-height: 1.15;
  overflow-wrap: anywhere;
}
.stat .label { margin-bottom: 4px; }
.stat .muted { font-size: 12px; }

/* ── Поля ввода ──────────────────────────────────────────────────────────
 * Поле кода: латиница заглавными, широкие промежутки — чтобы шесть знаков
 * читались по одному, а не сливались. */
.code, .answer {
  width: 100%;
  font-family: var(--display);
  background: var(--bg);
  color: var(--ink);
  border: 2px solid var(--border);
  border-radius: 14px;
}
.code {
  font-size: 30px;
  letter-spacing: .22em;
  text-align: center;
  text-transform: uppercase;
  padding: 16px 10px;
}
.answer { font-size: 22px; padding: 15px 14px; }
.code:focus, .answer:focus {
  outline: none;
  border-color: var(--accent);
  /* Толще рамку не делаем — поле «прыгнуло» бы на пиксель при фокусе; вместо
     этого подсвечиваем ореолом. */
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--accent) 22%, transparent);
}

/* ── Кнопки ──────────────────────────────────────────────────────────────
 *
 * Главная кнопка стоит на «подошве» — сплошной полосе цвета `--accent-shadow`
 * под ней, — и при нажатии проседает на неё. Это не тень Material, а приём из
 * приложения (`PrimaryButton`): ребёнку 8 лет он читается как настоящая
 * кнопка, которую можно вдавить. Без подошвы кнопка выглядела плоской
 * наклейкой, и это была главная причина, по которой веб не походил на
 * приложение.
 *
 * Главная кнопка — ОДНА НА ЭКРАН (железное правило 3). Вторая по важности
 * берёт `.btn--ghost`: обводка без заливки и без подошвы. */
.btn {
  display: flex; align-items: center; justify-content: center; gap: 10px;
  width: 100%;
  min-height: 56px;
  margin-top: 14px;
  padding: 15px 18px;
  border: none;
  border-radius: var(--r-card);
  background: var(--accent);
  color: var(--on-accent);
  box-shadow: 0 var(--sole) 0 var(--accent-shadow);
  font-family: var(--display); font-weight: 400;
  font-size: 19px;
  letter-spacing: .02em;
  text-align: center;
  text-decoration: none;
  cursor: pointer;
  transition: transform .06s ease, box-shadow .06s ease;
}
.btn:active {
  transform: translateY(var(--sole));
  box-shadow: 0 0 0 var(--accent-shadow);
}
.btn--big { min-height: 64px; margin-top: 18px; font-size: 21px; }

/* Плашка «это я» на экране входа (06.09.2026).
 *
 * Это ГЛАВНАЯ кнопка экрана для того, кто здесь уже занимался: код привязки
 * одноразовый, и до появления памяти браузера любой выход стоил ребёнку похода
 * к родителю за новым кодом.
 *
 * Имя выровнено по левому краю, а не по центру: рядом стоит буква в квадрате, и
 * центрированное имя относительно ВСЕЙ кнопки уезжало бы от неё тем дальше, чем
 * длиннее. Ряд читается слева направо — буква, имя, — как строка в списке. */
.btn--who { justify-content: flex-start; gap: 14px; text-align: left; }
.who__letter {
  display: flex; align-items: center; justify-content: center;
  flex: none;
  width: 44px; height: 44px;
  border-radius: 12px;
  /* Полупрозрачная белая подложка, а не свой цвет: кнопка уже залита акцентом,
     и второй цвет поверх него в тёмной теме перестаёт читаться. */
  background: rgba(255, 255, 255, .18);
  font-family: var(--display); font-size: 24px; line-height: 1;
}
/* Длинное имя обрезается многоточием, а не ломает кнопку: имя ребёнку вписывает
   родитель, и «Александра-Мария» там появится раньше, чем об этом подумают. */
.who__name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.btn svg { width: 22px; height: 22px; flex: none; }

.btn--ghost {
  background: var(--surface);
  color: var(--accent);
  border: 2px solid var(--accent);
  box-shadow: none;
  font-size: 17px;
  min-height: 52px;
}
.btn--ghost:active { transform: translateY(2px); }

/* Вариант ответа — крупная мишень: по ней жмут пальцем на ходу.
 *
 * Рамка ЦВЕТА АКЦЕНТА и подпись акцентом, а не бледная рамка по краю: с бледной
 * (осмотр 04.09.2026) четыре варианта читались как четыре поля ввода, и ребёнок
 * искал, куда печатать. Тот же дефект, что 25.08 нашёлся в приложении на
 * невыбранной кнопке режима — невыбранное состояние обязано выглядеть
 * НАЖИМАЕМЫМ, а не подписью. Фон при этом светлый: залить все варианты акцентом
 * значило бы сказать «все верные».
 *
 * Текст варианта — Nunito, а не Russo One: в ответах бывают дроби, длинные
 * слова и знаки, которых в декоративном шрифте нет. */
.btn--option {
  justify-content: flex-start;
  background: var(--surface);
  color: var(--accent);
  border: 2px solid var(--accent);
  box-shadow: none;
  font-family: var(--body); font-weight: 800;
  font-size: 19px;
  letter-spacing: 0;
  text-align: left;
  margin-top: 10px;
  min-height: 60px;
}
.btn--option:active { background: var(--accent); color: var(--on-accent); transform: translateY(2px); }
/* Порядковая метка варианта: по ней ребёнок находит глазами, куда попал палец,
   и она же разводит два похожих ответа. */
.btn--option i {
  flex: none;
  display: grid; place-items: center;
  width: 30px; height: 30px;
  border-radius: 9px;
  background: color-mix(in srgb, var(--accent) 14%, transparent);
  font-family: var(--display); font-style: normal; font-size: 14px;
}

/* Отказ стоит НАД кнопкой, а не под ней: под кнопкой он на телефоне уезжает за
   край, и человек видит красную рамку без причины (найдено осмотром 25.08). */
.error {
  display: flex; align-items: center; gap: 8px;
  background: #F6E3DC;
  color: #7A2E14;
  border: 1.5px solid #E2B7A5;
  border-radius: var(--r-small);
  padding: 12px 14px;
  margin: 0 0 12px;
  font-weight: 800;
  text-align: left;
}
.error svg { width: 20px; height: 20px; flex: none; }
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .error {
    background: #3A211A; color: #F0C4B4; border-color: #6B3826;
  }
}
:root[data-theme="dark"] .error {
  background: #3A211A; color: #F0C4B4; border-color: #6B3826;
}

.quiet { margin-top: 22px; text-align: center; }
.link {
  display: inline-block;
  padding: 8px 4px;            /* палец попадает в ссылку, а не мимо неё */
  background: none; border: none;
  color: var(--muted); font: inherit; font-weight: 700;
  text-decoration: underline; cursor: pointer;
}
.link--back { color: var(--accent); }
.link svg { width: 18px; height: 18px; }

/* ── Плитки предметов и тем ──────────────────────────────────────────────
 * Строка предмета из приложения (#2b): слот иконки 46 × 46 залит акцентом,
 * справа название и подпись, что произойдёт по нажатию. */
.tile {
  display: flex; align-items: center; gap: 12px;
  width: 100%;
  padding: 14px;
  text-align: left;
  text-decoration: none;
  color: var(--ink);
  font: inherit;
  cursor: pointer;
}
.tile__icon {
  flex: none;
  width: 46px; height: 46px;
  display: grid; place-items: center;
  border-radius: var(--r-small);
  background: var(--accent);
  color: var(--on-accent);
}
.tile__icon svg { width: 26px; height: 26px; }
.tile__body { flex: 1; min-width: 0; }
.tile__title {
  display: block;
  font-family: var(--display); font-weight: 400;
  font-size: 18px; line-height: 1.2;
}
.tile__hint {
  display: block; margin-top: 4px;
  font-size: 12.5px; font-weight: 800; letter-spacing: .06em;
  text-transform: uppercase;
  color: var(--accent);
}
/* Стрелка справа — обещание перехода. Без неё плитка на светлой карточке
   читается как заголовок раздела, а не как дверь. */
.tile__go { flex: none; color: var(--accent); display: grid; place-items: center; }
.tile__go svg { width: 22px; height: 22px; }
.tile:active { border-color: var(--accent); }

/* Запертая тема гасится, но остаётся ЧИТАЕМОЙ: ребёнок должен видеть, что
   впереди есть ещё темы, иначе предмет выглядит законченным. */
.tile--locked { cursor: default; background: var(--disabled-fill); }
/* Замок в слоте — цветом «выключенного текста», а не белым: белое на
   `--locked` даёт 1,7:1, и знак пропадает на солнце вместе со словом
   «ЗАКРЫТО», которое он повторяет. */
.tile--locked .tile__icon { background: var(--locked); color: var(--text-disabled); }
.tile--locked .tile__title { color: var(--text-disabled); }
.tile--locked .tile__hint { color: var(--text-disabled); font-weight: 700; letter-spacing: 0; text-transform: none; }
.tile-form { margin: 0 0 var(--gap); }
.tile-form .card { margin-bottom: 0; }
/* Предмет, закрытый ротацией, объясняется спокойным тоном, а не акцентом:
   акцент здесь читался бы как «жми сюда». */
.tile__hint--quiet { color: var(--muted); font-weight: 700; letter-spacing: 0; text-transform: none; }
/* Сданная тема помечена зелёным слотом: список тем ребёнок просматривает
   сверху вниз и ищет, куда идти дальше. */
.tile--passed .tile__icon { background: var(--success); }

/* ── Бой ─────────────────────────────────────────────────────────────────
 * Полоса вопросов вместо строки «3 / 12»: ребёнок видит не число, а сколько
 * осталось. Пройденные шаги залиты акцентом, текущий — золотом, остальные
 * пустые. Число рядом всё равно есть — полоса не единственный носитель. */
.steps { display: flex; gap: 4px; margin: 0 0 12px; }
.steps i {
  flex: 1;
  height: 8px;
  border-radius: var(--r-pill);
  background: var(--progress-track);
}
.steps i.is-done { background: var(--accent); }
.steps i.is-now  { background: var(--reward); }

.question {
  font-family: var(--body); font-weight: 800;
  font-size: 22px; line-height: 1.3;
  margin: 4px 0 18px;
}

/* ── Результат ───────────────────────────────────────────────────────────
 * Вердикт живёт В ШАПКЕ, на кадре предмета: это единственное, за чем ребёнок
 * сюда пришёл. Сдал — золотом, цветом награды; не сдал — белым, спокойно.
 * Красного экрана после честной попытки здесь нет: рубли отложены до сдачи, и
 * наказывать цветом не за что. */
.hero__verdict {
  font-family: var(--display);
  font-size: 30px; line-height: 1.1;
  letter-spacing: .02em;
}
.hero__verdict--win { color: var(--reward); }

/* ── Награды ─────────────────────────────────────────────────────────── */
.rank {
  font-family: var(--display);
  font-size: 30px; line-height: 1.1;
  margin: 2px 0 8px;
}
.bar {
  height: 10px; margin: 14px 0 8px;
  background: var(--progress-track);
  border-radius: var(--r-pill);
  overflow: hidden;
}
.bar__fill {
  height: 100%;
  background: var(--accent);
  border-radius: var(--r-pill);
}
.bar__fill--reward { background: var(--reward); }

/* ── Выбор режима ────────────────────────────────────────────────────────
 * Невыбранная кнопка с рамкой — иначе читается как подпись. */
.modes { display: flex; gap: 8px; flex-wrap: wrap; margin: 10px 0 4px; }
.mode {
  flex: 1 1 30%;
  min-height: 52px;
  padding: 12px 10px;
  border: 2px solid var(--border);
  border-radius: var(--r-small);
  background: var(--surface);
  color: var(--ink);
  font: inherit; font-weight: 800;
  font-size: 15px;
  cursor: pointer;
}
.mode--on {
  border-color: var(--accent);
  background: var(--accent);
  color: var(--on-accent);
}

/* ── Подвал ──────────────────────────────────────────────────────────────
 * Тихая строка внизу каждого экрана: марка и выход для взрослого. Крупнее
 * делать нельзя — ребёнок пришёл заниматься, а не разглядывать подвал; убирать
 * тоже нельзя: без него взрослый, открывший детскую версию на своём телефоне,
 * заперт (единственный выход гасил сессию ребёнка). */
.foot {
  display: flex; align-items: center; justify-content: space-between;
  gap: 12px; flex-wrap: wrap;
  margin-top: 28px;
  padding-top: 14px;
  border-top: 1.5px solid var(--border);
}
.foot__mark {
  font-family: var(--display);
  font-size: 13px; letter-spacing: .06em;
  color: var(--muted);
}
.foot__mark span { color: var(--accent); margin-left: .35em; }
.foot .link { color: var(--muted); font-size: 14px; }

/* ── Панель разделов ─────────────────────────────────────────────────────
 * Прижата к низу экрана: на телефоне это единственное место, куда большой палец
 * дотягивается не глядя, и там же она стоит в приложении.
 * `env(safe-area-inset-bottom)` — под жестовую полосу iPhone: без неё нижний ряд
 * уезжает под системную черту, а веб-версию открывают с любого телефона. */
.tabs {
  position: fixed;
  left: 0; right: 0; bottom: 0;
  display: flex;
  gap: 6px;
  max-width: var(--wrap);
  margin: 0 auto;
  padding: 8px 8px calc(8px + env(safe-area-inset-bottom, 0px));
  background: var(--surface);
  border: 1.5px solid var(--border);
  border-bottom: none;
  border-radius: var(--r-card) var(--r-card) 0 0;
}
.tab {
  flex: 1;
  min-height: 56px;
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: 3px;
  padding: 6px 4px;
  border-radius: var(--r-small);
  text-align: center;
  text-decoration: none;
  font-family: var(--display);
  font-size: 11.5px;
  letter-spacing: .06em;
  color: var(--muted);
}
.tab svg { width: 22px; height: 22px; }
/* Текущий раздел помечен заливкой, а не только цветом текста: цвет один
   различают не все, и на солнце экран телефона показывает половину оттенков. */
.tab--on { background: var(--accent); color: var(--on-accent); }

/* ── Узкие и низкие экраны ───────────────────────────────────────────────
 *
 * Проверять надо не «на мониторе», а на телефоне: 320 px в ширину — это ещё
 * живые iPhone SE и дешёвые Android, с которых веб-версию и открывают. */
@media (max-width: 360px) {
  body { --pad-x: 12px; --pad-top: 12px; padding: var(--pad-top) var(--pad-x) 14px; font-size: 16px; }
  .money .sum { font-size: 38px; }
  .money .sum span { font-size: 21px; }
  .btn--big { font-size: 19px; min-height: 60px; }
  .row { gap: 6px; }
  .row .stat { padding: 10px 4px; }
  .stat .value { font-size: 17px; }
  .code { font-size: 25px; letter-spacing: .16em; }
  .hero__verdict { font-size: 25px; }
  .tile { gap: 10px; padding: 12px; }
  .tile__icon { width: 40px; height: 40px; }
  .tile__icon svg { width: 22px; height: 22px; }
  .hero { min-height: 116px; padding: 14px var(--pad-x); }
  .hero--tall { min-height: 230px; }
  .hero--branch { min-height: 180px; }
}

/* Телефон в горизонтальном положении: высоты остаётся ~320 px, и крупные числа
   съедают её целиком — до кнопки приходится листать. */
@media (max-height: 480px) and (orientation: landscape) {
  .money .sum { font-size: 34px; }
  .card--center { padding: 18px 20px; }
  .btn--big { min-height: 56px; }
  h1 { margin-top: 8px; }
  /* Лёжа высоты остаётся ~320 px, и шапка в 200 из них — это экран, на котором
     ничего, кроме шапки, не помещается. */
  .hero, .hero--tall, .hero--branch { min-height: 96px; }
}

/* Экран пошире телефона: колонка остаётся узкой (длинные строки детям читать
   тяжелее), но карточки дышат свободнее. */
@media (min-width: 560px) {
  body { --pad-x: 20px; --pad-top: 22px; padding: var(--pad-top) var(--pad-x) 24px; }
  .card { padding: 20px; }
}

/* ── Компьютер ───────────────────────────────────────────────────────────
 *
 * Веб-версию делали под чужой телефон, и на мониторе она выглядела полоской
 * шириной 520 px посреди пустоты, с панелью разделов, приклеенной к нижнему
 * краю экрана — там, куда на компьютере не тянется ни палец, ни взгляд.
 *
 * Разметка при этом не меняется НИ НА СТРОКУ: те же ссылки, те же формы, тот
 * же порядок. Меняется только раскладка — панель разделов встаёт слева
 * колонкой, колонка содержимого расширяется, кадр шапки становится выше. Так
 * же устроен планшетный вид приложения: те же экраны, иначе разложены.
 *
 * Порог 900 px, а не 768: на 768 (планшет стоймя) телефонная раскладка ещё
 * верна — палец там главный способ ткнуть, и нижняя панель под ним удобнее
 * боковой. */
@media (min-width: 900px) {
  body { --pad-x: 28px; --pad-top: 28px; }
  /* Место под боковую панель. Отступ на самом полотне, а не на `.wrap`:
     иначе кадр шапки, гасящий поля страницы, уехал бы под панель. */
  body:has(.tabs) { padding-left: calc(232px + var(--pad-x)); padding-bottom: 28px; }

  /* Колонка встаёт по центру свободного места справа от панели — и остаётся
     по центру экрана на страницах без панели (бой, разминка, вход). */
  .wrap { max-width: 680px; margin: 0 auto; }

  /* Панель разделов из нижней превращается в боковую. Порядок пунктов,
     подписи и адреса те же — меняется только направление. */
  .tabs {
    top: 0; bottom: 0; right: auto;
    width: 232px;
    max-width: none;
    flex-direction: column;
    justify-content: flex-start;
    gap: 6px;
    margin: 0;
    padding: 24px 16px;
    border: none;
    border-right: 1.5px solid var(--border);
    border-radius: 0;
  }
  .tab {
    flex: none;
    flex-direction: row;
    justify-content: flex-start;
    gap: 12px;
    min-height: 52px;
    padding: 12px 14px;
    font-size: 14px;
    text-align: left;
  }
  .tab svg { width: 24px; height: 24px; }

  /* Кадр шапки выше: на широком экране прежние 200 px превращаются в узкую
     ленту — кадр перестаёт читаться картинкой.

     И он больше не гасит поля страницы: слева стоит панель разделов, и кадр
     во всю ширину уезжал бы под неё. На компьютере он становится верхней
     карточкой полотна — со скруглением, как всё остальное. */
  .hero {
    min-height: 148px;
    margin: 0 0 16px;
    padding: 20px;
    border-radius: var(--r-card);
    overflow: hidden;
  }
  .hero--tall { min-height: 340px; }
  .hero--branch { min-height: 270px; }

  /* Главная кнопка не растягивается на всю ширину колонки: кнопка в 680 px
     шириной выглядит панелью, а не кнопкой, и мышью в неё целятся хуже, чем
     пальцем в телефонную. */
  .btn { width: auto; min-width: 280px; }
  .btn--option { width: 100%; }

  /* Списки предметов и тем — в две колонки: на широком экране список в один
     столбец заставляет листать там, где всё помещается на экран. */
  .tiles { display: grid; grid-template-columns: 1fr 1fr; gap: var(--gap); }
  .tiles .card, .tiles .tile-form { margin-bottom: 0; }
  .tiles .tile-form .card { height: 100%; }
}


/* Уважение к настройке «меньше движения»: у части детей от анимации укачивает,
   и системный флаг — единственный способ об этом узнать. */
@media (prefers-reduced-motion: reduce) {
  * { transition: none !important; animation: none !important; }
}
