/* pytask/static/css/app.css
   ------------------------------------------------------------------
   Оформление pytask.ru

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

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

   Шрифт — Golos Text: спроектирован под русский, кириллица родная,
   а не пририсованная к латинице. Код — JetBrains Mono.
   ------------------------------------------------------------------ */

@import url("fonts.css");

:root {
  color-scheme: dark;

  /* Четыре ступени глубины вместо двух. Плоскость из двух тонов и есть
     та пустота, из-за которой страница выглядит незаконченной. */
  --ink:        #0E1216;
  --surface:    #151B22;
  --surface-2:  #1B232B;
  --line:       #27313B;
  --line-soft:  #1F272F;

  --text:       #E7EDF3;
  --text-dim:   #8C9AA8;
  --text-faint: #5C6975;

  --accent:     #F0A43C;
  --accent-hi:  #FFBA5C;
  --accent-dim: rgba(240, 164, 60, 0.14);
  --on-accent:  #17110A;

  --ok:   #4CC38A;
  --fail: #F0655A;

  --font: "Golos Text", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
  --mono: "JetBrains Mono", ui-monospace, "SF Mono", Consolas, monospace;

  /* Семь ступеней размера. Больше на глаз не подбираем. */
  --t-xs: 12px;
  --t-sm: 14px;
  --t-md: 16px;
  --t-lg: 18px;
  --t-xl: 22px;
  --t-2xl: 28px;
  --t-3xl: clamp(30px, 4.2vw, 46px);

  --s1: 4px;  --s2: 8px;  --s3: 12px; --s4: 16px;
  --s5: 24px; --s6: 32px; --s7: 48px; --s8: 72px;

  --r-sm: 6px;
  --r-md: 10px;
  --r-lg: 14px;

  /* Глубина. Тень всегда двухслойная: короткая плотная — контакт с
     поверхностью, длинная мягкая — рассеянный свет. Одна тень выглядит
     наклейкой, две — предметом, лежащим на столе. */
  --edge:      inset 0 1px 0 rgba(255,255,255,.055);  /* свет по верхней кромке */
  --shadow:    0 1px 2px rgba(0,0,0,.35), 0 10px 24px -8px rgba(0,0,0,.55);
  --shadow-lift: 0 2px 4px rgba(0,0,0,.40), 0 22px 44px -14px rgba(0,0,0,.72);
  --shadow-inset: inset 0 1px 2px rgba(0,0,0,.30);    /* поля ввода — углубление */
  --ring: 0 0 0 3px rgba(240, 164, 60, 0.12);

  /* Показ результата (ТЗ §2в): цвета тел и линий — именами из данных,
     значения только здесь. Зелёного и красного нет: они значат «сошлось»
     и «ошибка», а картинка ни того ни другого не решает. */
  --pokaz-akcent: #F0A43C;
  --pokaz-vtoroy: #5AA9E6;
  --pokaz-tretiy: #B48CF2;
  --pokaz-ogon:   #E8784A;
  --pokaz-seryy:  #7D8B98;
  --pokaz-zemlya: #8FA36A;
}

:root[data-theme="light"] {
  color-scheme: light;
  --ink:        #FBFAF8;
  --surface:    #FFFFFF;
  --surface-2:  #F2F3F5;
  --line:       #DDE1E6;
  --line-soft:  #E9ECEF;
  --text:       #171C22;
  --text-dim:   #5D6873;
  --text-faint: #8B959F;
  --accent:     #B3720A;
  --accent-hi:  #96600A;
  --accent-dim: rgba(179, 114, 10, 0.10);
  --on-accent:  #FFFFFF;
  /* На светлой теме свет по кромке почти не читается, а тени мягче */
  --edge:      inset 0 1px 0 rgba(255,255,255,.9);
  --shadow:    0 1px 2px rgba(16,24,32,.06), 0 8px 20px -6px rgba(16,24,32,.10);
  --shadow-lift: 0 2px 4px rgba(16,24,32,.07), 0 18px 36px -12px rgba(16,24,32,.16);
  --shadow-inset: inset 0 1px 2px rgba(16,24,32,.05);
  --ring: 0 0 0 3px rgba(179, 114, 10, 0.14);
  --pokaz-akcent: #B3720A;
  --pokaz-vtoroy: #2B78B8;
  --pokaz-tretiy: #7A52C7;
  --pokaz-ogon:   #C2532A;
  --pokaz-seryy:  #8B959F;
  --pokaz-zemlya: #5E7438;
}

*, *::before, *::after { box-sizing: border-box; }

/* Скрытое скрыто. Любой display у класса (.btn — inline-flex) перебивает
   браузерное [hidden] { display: none }, и кнопка «Остановить» с атрибутом
   hidden стояла на виду рядом с «Запустить» и «Играть», ничего не
   останавливая (найдено 03.10 снимком). */
[hidden] { display: none !important; }

html { -webkit-text-size-adjust: 100%; }

body {
  margin: 0;
  background: var(--ink);
  color: var(--text);
  font-family: var(--font);
  font-size: var(--t-md);
  line-height: 1.6;
  -webkit-font-smoothing: antialiased;
}

code, pre, .mono { font-family: var(--mono); }
/* Лигатуры в коде — выключены. JetBrains Mono по умолчанию рисует `>=` как
   «≥», `!=` как «≠», `->` как стрелку. Новичок перепечатывает увиденное —
   и получает синтаксическую ошибку на знаке, которого нет на клавиатуре.
   Найдено 27.09 на снимке эталона «Как можно было иначе» (Д-11). */
code, pre, .mono, .cm-editor, .cm-content, textarea {
  font-variant-ligatures: none;
  font-feature-settings: "liga" 0, "calt" 0;
}

a { color: var(--accent); text-decoration: none; }
a:hover { color: var(--accent-hi); }

/* Видимый фокус с клавиатуры обязателен; мышью он не мешает */
:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
  border-radius: var(--r-sm);
}

/* Движение при загрузке: панель разбора и карточка формы поднимаются.
   Один раз, только на первом экране. С 16.10 (П-63) к ним добавились
   набор кода в разборе на главной и появление плиток каскадом — раздел
   «Живость» в конце файла; правило «одно движение» снято решением
   заказчика: сайт выглядел мёртвым. */
@keyframes lift-in {
  from { opacity: 0; transform: translateY(8px); }
  to   { opacity: 1; transform: none; }
}
.review, .auth__card {
  animation: lift-in .34s cubic-bezier(.2,.8,.3,1) both;
}
.auth__card { animation-delay: .06s; }

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

.container { max-width: 1140px; margin: 0 auto; padding: 0 var(--s5); }

/* ---------- Заголовки ---------- */

h1, h2, h3 {
  font-weight: 700;
  letter-spacing: -0.022em;
  line-height: 1.18;
  margin: 0 0 var(--s3);
}
h1 { font-size: var(--t-2xl); }
h2 { font-size: var(--t-xl); }
h3 { font-size: var(--t-lg); font-weight: 600; }
p  { margin: 0 0 var(--s4); }

.muted { color: var(--text-dim); }
.faint { color: var(--text-faint); }
.small { font-size: var(--t-sm); }

/* Надзаголовок: называет раздел там, где полноценный заголовок избыточен */
.eyebrow {
  font-size: var(--t-xs);
  font-weight: 600;
  letter-spacing: 0.09em;
  text-transform: uppercase;
  color: var(--text-faint);
  margin-bottom: var(--s2);
}

/* ---------- Шапка ---------- */

.header {
  border-bottom: 1px solid var(--line-soft);
  background: var(--surface);
  position: sticky;
  top: 0;
  z-index: 20;
}
.header__inner {
  display: flex;
  align-items: center;
  gap: var(--s5);
  height: 60px;
}
.header__logo {
  display: inline-flex;
  align-items: center;
  gap: var(--s2);
  font-weight: 700;
  font-size: var(--t-lg);
  letter-spacing: -0.03em;
  color: var(--text);
}
.header__logo:hover { color: var(--text); }
/* Единственная графика в шапке */
.header__mark {
  display: inline-grid;
  place-items: center;
  width: 26px; height: 26px;
  border-radius: var(--r-sm);
  background: var(--accent-dim);
  color: var(--accent);
  font-family: var(--mono);
  font-size: var(--t-sm);
}
.header__nav { display: flex; align-items: center; gap: var(--s5); }
/* На телефоне разделы — второй строкой, с прокруткой вбок, если не влезут:
   в одну строку шапка была шире экрана, и страница ездила вбок целиком. */
@media (max-width: 720px) {
  .header__inner { flex-wrap: wrap; height: auto; gap: var(--s3); padding-top: 10px; padding-bottom: 8px; row-gap: 8px; }
  .header__nav { order: 10; width: 100%; gap: var(--s4); overflow-x: auto; scrollbar-width: none; }
  .header__nav .header__link { white-space: nowrap; }
}
/* Кнопка «Разделы» (14.10, П-60): на широком экране её нет, на телефоне
   при работающем скрипте разделы сворачиваются в неё и раскрываются
   списком под шапкой. */
.header__razdely {
  display: none;
  align-items: center;
  gap: 6px;
  padding: 5px 10px;
  border: 1px solid var(--line);
  border-radius: var(--r-sm);
  background: transparent;
  color: var(--text-dim);
  font: 500 var(--t-sm)/1.2 var(--font);
  cursor: pointer;
}
.header__razdely::after {
  content: "";
  width: 7px; height: 7px;
  border-right: 1.6px solid currentColor;
  border-bottom: 1.6px solid currentColor;
  transform: translateY(-2px) rotate(45deg);
  transition: transform .12s ease;
}
.header__razdely[aria-expanded="true"] { color: var(--text); border-color: var(--text-faint); }
.header__razdely[aria-expanded="true"]::after { transform: translateY(2px) rotate(-135deg); }
@media (max-width: 720px) {
  .js .header__razdely { display: inline-flex; }
  .js .header__nav { display: none; }
  .js .header__nav.is-open {
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    gap: 0;
    overflow: visible;
    padding: 4px 0 6px;
    border-top: 1px solid var(--line-soft);
  }
  .js .header__nav.is-open .header__link { display: block; padding: 10px 0; font-size: var(--t-md); }
  /* Отметка «где я» в раскрытом списке — короткой чертой под словом:
     правило шапки ставит её на 20 пикселей ниже, то есть под соседний пункт. */
  .js body[data-razdel] .header__nav.is-open a[href]::after { bottom: 4px; right: auto; width: 24px; }
}
.header__link { color: var(--text-dim); font-size: var(--t-sm); font-weight: 500; }
.header__link:hover { color: var(--text); }
/* Где я. Раздел приходит на <body data-razdel> именем блюпринта, а ссылка
   узнаётся по адресу: строки ссылок в шапке не трогаются — их ищут
   проверки (page_test.py — порядок) и мутации (M119, M181). Без отметки
   человек на шаге игры не видел, что он в «Играх», — слова заказчика 03.10:
   «не поймёшь, что где». */
.header__nav .header__link,
.header__inner > .header__link { position: relative; }
body[data-razdel="tasks"] .header__nav a[href^="/tasks"],
body[data-razdel="grafika"] .header__nav a[href^="/grafika"],
body[data-razdel="projects"] .header__nav a[href^="/projects"],
body[data-razdel="igry"] .header__nav a[href^="/igry"],
body[data-razdel="bugs"] .header__nav a[href^="/naydi-oshibku"],
body[data-razdel="admin"] .header__inner > a[href^="/admin"] { color: var(--text); }
body[data-razdel="tasks"] .header__nav a[href^="/tasks"]::after,
body[data-razdel="grafika"] .header__nav a[href^="/grafika"]::after,
body[data-razdel="projects"] .header__nav a[href^="/projects"]::after,
body[data-razdel="igry"] .header__nav a[href^="/igry"]::after,
body[data-razdel="bugs"] .header__nav a[href^="/naydi-oshibku"]::after,
body[data-razdel="admin"] .header__inner > a[href^="/admin"]::after {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  bottom: -20px;
  height: 2px;
  border-radius: 2px;
  background: var(--accent);
}
@media (max-width: 720px) {
  body[data-razdel] .header__nav a::after { bottom: -8px; }
  body[data-razdel="admin"] .header__inner > a[href^="/admin"]::after { display: none; }
}
.header__spacer { margin-left: auto; }
.header__coins {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 4px 10px 4px 8px;
  border: 1px solid var(--line);
  border-radius: 999px;
  background: var(--surface-2);
  font-size: var(--t-sm);
  color: var(--text-dim);
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}
/* Монета — кружок акцентом: число в шапке читается как баланс, а не как
   ещё один пункт меню. */
.header__coins::before {
  content: "";
  width: 10px;
  height: 10px;
  border-radius: 50%;
  background: var(--accent);
  box-shadow: inset 0 0 0 2px rgba(0, 0, 0, .18);
}
a.header__coins:hover { color: var(--text); border-color: var(--text-faint); }
/* «Купить монеты» — знаком рядом с балансом (этап 9, ТЗ §4). Кружок того
   же размера, что пилюля баланса, и тише её: покупка доступна, а не
   предлагается. Акцентом — только при наведении. */
.header__kupit {
  display: inline-grid;
  place-items: center;
  width: 26px;
  height: 26px;
  margin-left: -12px;
  border: 1px solid var(--line);
  border-radius: 50%;
  background: var(--surface-2);
  color: var(--text-dim);
  font-size: 16px;
  font-weight: 600;
  line-height: 1;
}
.header__kupit:hover { color: var(--accent); border-color: var(--accent); }
body[data-razdel="profil"] .header__form .header__link { color: var(--text); }
.header__form { display: flex; align-items: center; gap: var(--s2); margin: 0; }

.main { padding: var(--s6) 0 var(--s8); }
/* Клетка первого экрана (.hero::before) выходит за края на полэкрана в обе
   стороны — нарочно, чтобы фон не обрывался у колонки. Без обрезки страница
   была шире окна: 1826 пикселей при 1280, 561 при 390, ездила вбок целиком
   (найдено 03.10 снимком). clip, а не hidden: hidden делает .main областью
   прокрутки, и липкие блоки внутри перестают липнуть. */
.main { overflow-x: clip; }

.footer {
  border-top: 1px solid var(--line-soft);
  padding: var(--s5) 0;
  color: var(--text-faint);
  font-size: var(--t-sm);
}

/* ---------- Первый экран ---------- */

.hero {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1.05fr);
  gap: var(--s7);
  align-items: center;
  padding: var(--s5) 0 var(--s8);
  position: relative;
}
/* Едва различимая клетка, как на странице тетради: на виду её нет,
   но плоскость перестаёт быть пустой. */
.hero::before {
  content: "";
  position: absolute;
  inset: -60px -50vw;
  background-image:
    linear-gradient(var(--line-soft) 1px, transparent 1px),
    linear-gradient(90deg, var(--line-soft) 1px, transparent 1px);
  background-size: 28px 28px;
  opacity: .55;
  -webkit-mask-image: radial-gradient(closest-side at 45% 40%, #000 15%, transparent 95%);
  mask-image: radial-gradient(closest-side at 45% 40%, #000 15%, transparent 95%);
  pointer-events: none;
  z-index: -1;
}
@media (max-width: 920px) {
  .hero { grid-template-columns: minmax(0, 1fr); gap: var(--s6); padding-bottom: var(--s6); }
}

.headline {
  font-size: var(--t-3xl);
  font-weight: 700;
  letter-spacing: -0.03em;
  line-height: 1.1;
  margin: 0 0 var(--s4);
}
.headline em { font-style: normal; color: var(--accent); }

/* Абзац под заголовком и строка под кнопками сняты 14.10 (П-59): заголовок
   стоит сразу над кнопками — отступ теперь у него. */
.hero .headline { margin-bottom: var(--s6); }
.cta-row { display: flex; align-items: center; gap: var(--s5); flex-wrap: wrap; }

/* ---------- Разбор на полях: главный элемент страницы ----------
   Это и есть продукт: строка кода помечена, рядом объяснение.
   Всё остальное на первом экране нарочно тихое. */

.review {
  border-radius: var(--r-lg);
  background: var(--surface);
  overflow: hidden;
  /* Лист, лежащий на стопке: за панелью виден край второй такой же.
     Приём применён ТОЛЬКО здесь. Если раздать его всем карточкам,
     страница начнёт рябить — одна яркая деталь работает, десять
     одинаковых превращаются в узор. */
  box-shadow:
    var(--edge),
    0 0 0 1px var(--line),
    7px 7px 0 -1px var(--surface),
    8px 8px 0 0 var(--line),
    var(--shadow-lift);
}
@media (max-width: 920px) {
  /* На узком экране стопка вылезает за край — оставляем обычную глубину */
  .review { box-shadow: var(--edge), 0 0 0 1px var(--line), var(--shadow-lift); }
}
.review__chrome {
  display: flex;
  align-items: center;
  gap: var(--s2);
  padding: var(--s3) var(--s4);
  border-bottom: 1px solid var(--line-soft);
  background: var(--surface-2);
}
.review__dots { display: flex; gap: 6px; }
.review__dot { width: 9px; height: 9px; border-radius: 50%; background: var(--line); }
.review__name {
  font-family: var(--mono);
  font-size: var(--t-xs);
  color: var(--text-faint);
  margin-left: var(--s2);
}
.review__code {
  margin: 0;
  padding: var(--s4) 0;
  font-family: var(--mono);
  font-size: var(--t-sm);
  line-height: 1.8;
  overflow-x: auto;
}
.review__code .line {
  display: block;
  padding: 0 var(--s5);
  border-left: 2px solid transparent;
  white-space: pre;
}
/* Помеченная строка — тот самый карандаш на полях */
.review__code .line--marked {
  border-left-color: var(--accent);
  background: var(--accent-dim);
}
.tok-key { color: #C792EA; }
.tok-fn  { color: #82AAFF; }
.tok-str { color: #C3E88D; }
.tok-com { color: var(--text-faint); }
.tok-out { color: var(--fail); }

.review__note {
  display: flex;
  gap: var(--s3);
  padding: var(--s4) var(--s5) var(--s5);
  border-top: 1px solid var(--line-soft);
  background: linear-gradient(180deg, transparent, var(--accent-dim));
}
.review__pen {
  flex: none;
  width: 24px; height: 24px;
  display: grid; place-items: center;
  border-radius: 50%;
  background: var(--accent);
  color: var(--on-accent);
  font-size: var(--t-xs);
  font-weight: 700;
}
.review__text { font-size: var(--t-sm); line-height: 1.65; }
.review__text p { margin: 0 0 var(--s2); }
.review__text p:last-child { margin: 0; }
.review__text code {
  background: var(--surface-2);
  border: 1px solid var(--line-soft);
  border-radius: 4px;
  padding: 1px 5px;
  font-size: 0.92em;
  color: var(--text);
}

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

.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--s2);
  padding: 11px 20px;
  border: 1px solid var(--line);
  border-radius: var(--r-sm);
  background: var(--surface-2);
  color: var(--text);
  font: 500 var(--t-sm)/1 var(--font);
  cursor: pointer;
  transition: background .12s ease, border-color .12s ease, transform .06s ease;
}
.btn:hover { border-color: var(--text-faint); color: var(--text); }
.btn:active { transform: translateY(1px); }
.btn:disabled { opacity: .4; cursor: not-allowed; transform: none; }

.btn--primary {
  background: var(--accent);
  border-color: var(--accent);
  color: var(--on-accent);
  font-weight: 600;
}
.btn--primary:hover {
  background: var(--accent-hi);
  border-color: var(--accent-hi);
  color: var(--on-accent);
}
/* Свечение только у крупных кнопок — на главной и в формах, где кнопка
   одна и нажимается один раз. Кнопку «Проверить» на странице задачи
   жмут десятки раз подряд, и постоянное свечение начнёт раздражать. */
.btn--lg { padding: 14px 26px; font-size: var(--t-md); }
.btn--lg.btn--primary {
  box-shadow: 0 1px 2px rgba(0,0,0,.35), 0 8px 20px -8px rgba(240,164,60,.5);
}
.btn--lg.btn--primary:hover {
  box-shadow: 0 1px 2px rgba(0,0,0,.35), 0 10px 24px -8px rgba(240,164,60,.6);
}
.btn--block { width: 100%; }

.link-btn {
  background: none;
  border: 0;
  padding: 0;
  color: var(--accent);
  font: 500 var(--t-sm)/1.5 var(--font);
  cursor: pointer;
}
.link-btn:hover { color: var(--accent-hi); }

/* ---------- Карточки и разделы ---------- */

.card {
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--r-md);
  padding: var(--s5);
  box-shadow: var(--edge), var(--shadow);
}
/* Плитки тем главной (.cards, .card--link) заменены 16.10 плиткой со знаком
   (.temy, .tema — раздел «Живость» в конце файла, П-63). */

.plain { margin: 0; padding-left: 20px; color: var(--text-dim); }
.plain li { margin-bottom: var(--s2); }

.section { padding-top: var(--s7); }
.section + .section { border-top: 1px solid var(--line-soft); }
.section__head { margin-bottom: var(--s4); }

/* ---------- Общие детали страниц (03.10) ----------
   Откуда этот раздел. Каталог и страница задачи сделаны тщательно, а
   страницы, пришедшие позже (профиль, обслуживание, проекты, игры, заявки),
   собирались из голых заголовков и абзацев одной колонкой — «всё в куче»,
   слова заказчика. Хуже того, их шаблоны ссылались на классы, которых здесь
   не было вовсе: btn--ghost, btn--accent, tag--ok, h3, project__result.
   Классы молча ничего не делали, и кнопка «взять готовым» выглядела так же,
   как «Собрать проект».

   Правило теперь одно для всех страниц: заголовок страницы (.page-head) —
   разделы в коробках (.box) с заголовком и телом — числа плитками (.stat) —
   пустое место словами (.empty). Новая страница собирается из них, а не
   из своих классов на один раз. */

/* Поля ввода по умолчанию. :where() даёт нулевой вес: любое правило своего
   места (поиск каталога, строка консоли, адрес личной страницы) перебивает
   это без !important и без гонки за весом. До 03.10 поле вне .field
   оставалось браузерным — серым прямоугольником с чёрным текстом на тёмной
   странице («Люди», «Настройки», ответ на вопрос). */
:where(input:not([type="checkbox"], [type="radio"], [type="hidden"], [type="submit"],
                 [type="button"], [type="range"], [type="color"], [type="file"]),
       select, textarea) {
  max-width: 100%;
  padding: 9px 12px;
  background-color: var(--surface-2);
  border: 1px solid var(--line);
  border-radius: var(--r-sm);
  color: var(--text);
  /* 16 px: при меньшем iOS увеличивает страницу при фокусе. */
  font: 400 var(--t-md)/1.4 var(--font);
  box-shadow: var(--shadow-inset);
  transition: border-color .12s ease, background-color .12s ease, box-shadow .12s ease;
}
:where(input, select, textarea):where(:focus) {
  outline: none;
  border-color: var(--accent);
  background-color: var(--ink);
  box-shadow: var(--shadow-inset), var(--ring);
}
:where(input, textarea)::placeholder { color: var(--text-faint); }
:where(textarea) { resize: vertical; line-height: 1.5; }
/* Стрелка выбора нарисована: браузерная на тёмном фоне то белая, то чёрная. */
:where(select) {
  appearance: none;
  -webkit-appearance: none;
  padding-right: 34px;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 12 12' fill='none' stroke='%238C9AA8' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M3 4.5l3 3 3-3'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right 12px center;
  cursor: pointer;
}
:where(input[type="checkbox"], input[type="radio"]) { accent-color: var(--accent); }

/* Классы, на которые шаблоны ссылались, а описаны они не были. */
.h3 { font-size: var(--t-lg); font-weight: 600; }
.btn--accent {
  background: var(--accent);
  border-color: var(--accent);
  color: var(--on-accent);
  font-weight: 600;
}
.btn--accent:hover { background: var(--accent-hi); border-color: var(--accent-hi); color: var(--on-accent); }
/* Второстепенное действие рядом с главным: «взять готовым», «выключить». */
.btn--ghost { background: transparent; border-color: var(--line); color: var(--text-dim); }
.btn--ghost:hover { background: var(--surface-2); color: var(--text); border-color: var(--text-faint); }
.btn--danger { background: transparent; border-color: rgba(240, 101, 90, .5); color: var(--fail); }
.btn--danger:hover { background: rgba(240, 101, 90, .1); border-color: var(--fail); color: var(--fail); }

/* Пометки состояния. Зелёный — только «сошлось, решено, собрано», красный —
   только «ошибка, отказ, закрыто»: как и везде на сайте. */
.badge, .tag.tag--ok {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  padding: 2px 9px;
  border: 1px solid var(--line);
  border-radius: 999px;
  background: var(--surface-2);
  color: var(--text-dim);
  font-size: var(--t-xs);
  font-weight: 500;
  line-height: 1.5;
  white-space: nowrap;
}
.badge--ok, .tag.tag--ok { color: var(--ok); border-color: rgba(76, 195, 138, .35); background: rgba(76, 195, 138, .08); }
.badge--fail { color: var(--fail); border-color: rgba(240, 101, 90, .35); background: rgba(240, 101, 90, .08); }
.badge--accent { color: var(--accent); border-color: rgba(240, 164, 60, .35); background: var(--accent-dim); }
.badge--plain { background: transparent; }

/* ---- Заголовок страницы ---- */

.page-head { margin-bottom: var(--s6); min-width: 0; }
.page-head h1 { margin: 0; font-size: var(--t-2xl); }
.page-head__row {
  display: flex;
  align-items: flex-end;
  justify-content: space-between;
  gap: var(--s3) var(--s5);
  flex-wrap: wrap;
}
.page-head__lede {
  margin: var(--s3) 0 0;
  max-width: 70ch;
  color: var(--text-dim);
  line-height: 1.6;
}
.page-head__meta { margin: var(--s2) 0 0; color: var(--text-faint); font-size: var(--t-sm); }
.page-head__actions { display: flex; align-items: center; gap: var(--s3); flex-wrap: wrap; }

/* Хлебные крошки — над заголовком, тихо: откуда пришёл и куда вернуться. */
.crumbs {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 6px;
  margin: 0 0 var(--s3);
  color: var(--text-faint);
  font-size: var(--t-sm);
}
.crumbs a { color: var(--text-dim); }
.crumbs a:hover { color: var(--text); }
.crumbs__sep { color: var(--text-faint); }

/* ---- Коробка: раздел страницы ---- */

.box {
  min-width: 0;
  /* clip, а не hidden: углы таблиц и полос не вылезают за скругление, а
     липкие блоки внутри продолжают липнуть. */
  overflow: clip;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--r-lg);
  box-shadow: var(--edge), var(--shadow);
}
.box__head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: var(--s2) var(--s3);
  min-height: 54px;
  padding: 12px var(--s5);
  border-bottom: 1px solid var(--line-soft);
}
.box__title {
  display: flex;
  align-items: baseline;
  gap: var(--s2);
  margin: 0;
  font-size: var(--t-md);
  font-weight: 600;
  letter-spacing: -0.01em;
}
.box__count { color: var(--text-faint); font-weight: 500; font-variant-numeric: tabular-nums; }
.box__aside { color: var(--text-dim); font-size: var(--t-sm); }
.box__body { padding: var(--s5); min-width: 0; }
.box__body > :last-child { margin-bottom: 0; }
.box__body--flush { padding: 0; }
.box__foot {
  display: flex;
  align-items: center;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: var(--s2) var(--s4);
  padding: 12px var(--s5);
  border-top: 1px solid var(--line-soft);
  font-size: var(--t-sm);
  color: var(--text-dim);
}
.box__foot p { margin: 0; }
/* Пояснение внутри коробки — мельче и тише содержания. */
.note { color: var(--text-dim); font-size: var(--t-sm); line-height: 1.6; }
.note p { margin: 0 0 var(--s2); }
.note p:last-child { margin: 0; }

/* Колонки: содержание и бок. Бок узкий — пароль, ссылки, сводка. */
.stack { display: grid; gap: var(--s5); align-content: start; min-width: 0; }
.cols {
  display: grid;
  grid-template-columns: minmax(0, 1fr) 340px;
  gap: var(--s5);
  align-items: start;
}
.cols--wide-side { grid-template-columns: minmax(0, 1fr) 380px; }
.cols__side--sticky { position: sticky; top: 84px; }
@media (max-width: 920px) {
  .cols, .cols--wide-side { grid-template-columns: minmax(0, 1fr); }
  .cols__side--sticky { position: static; }
}

/* ---- Плитки с числами ---- */

.stats {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(190px, 1fr));
  gap: var(--s3);
  margin-bottom: var(--s5);
}
/* На телефоне — по две в ряд: четыре плитки столбиком занимали весь
   первый экран профиля. */
@media (max-width: 560px) {
  .stats { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .stat { padding: var(--s3) var(--s4); }
  .stat__value { font-size: 22px; }
}
.stat {
  min-width: 0;
  padding: var(--s4) var(--s5);
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--r-md);
  box-shadow: var(--edge), var(--shadow);
  color: var(--text);
}
a.stat { display: block; transition: border-color .12s ease; }
a.stat:hover { border-color: var(--text-faint); color: var(--text); }
.stat__label {
  margin: 0 0 6px;
  color: var(--text-faint);
  font-size: var(--t-xs);
  font-weight: 600;
  letter-spacing: .08em;
  text-transform: uppercase;
}
.stat__value {
  margin: 0;
  font-size: 26px;
  font-weight: 700;
  letter-spacing: -0.02em;
  line-height: 1.15;
  font-variant-numeric: tabular-nums;
}
.stat__value small { margin-left: 4px; color: var(--text-dim); font-size: var(--t-sm); font-weight: 500; letter-spacing: 0; }
.stat__note { margin: 6px 0 0; color: var(--text-dim); font-size: var(--t-sm); line-height: 1.45; }
.stat--alert { border-color: rgba(240, 101, 90, .45); }
.stat--alert .stat__value { color: var(--fail); }

/* ---- Пусто ---- */

/* Пустой раздел говорит, почему пуст и куда идти, — крупно и посередине,
   а не серой строкой, которую принимают за поломку. */
.empty { padding: var(--s6) var(--s5); text-align: center; color: var(--text-dim); font-size: var(--t-sm); }
.empty__title { margin: 0 0 var(--s1); color: var(--text); font-size: var(--t-md); font-weight: 600; }
.empty p { margin: 0 auto var(--s2); max-width: 52ch; }
.empty .btn { margin-top: var(--s3); }
.empty--box { border: 1px dashed var(--line); border-radius: var(--r-lg); }

/* ---- Строки внутри коробки ---- */

.list { list-style: none; margin: 0; padding: 0; }
.list__item {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  align-items: center;
  gap: var(--s3);
  padding: 10px var(--s5);
  border-bottom: 1px solid var(--line-soft);
}
.list__item:last-child { border-bottom: 0; }
.list__main { min-width: 0; overflow-wrap: anywhere; }
.list__title { color: var(--text); font-weight: 500; }
a.list__title:hover { color: var(--accent); }
.list__meta { margin: 2px 0 0; color: var(--text-faint); font-size: var(--t-sm); }
/* Плотный список: пометка — в той же строке, что название. Для длинных
   списков (решённое), где вторая строка удваивала бы высоту. */
.list--plotno .list__meta { display: inline; margin: 0 0 0 var(--s2); }
.list__side { text-align: right; white-space: nowrap; font-size: var(--t-sm); font-variant-numeric: tabular-nums; }

/* Полоса готовности — общая для карточек, боковой сводки и профиля. */
.meter { height: 6px; border-radius: 3px; background: var(--surface-2); overflow: hidden; box-shadow: var(--shadow-inset); }
.meter > span { display: block; height: 100%; border-radius: 3px; background: var(--accent); }
.meter--done > span { background: var(--ok); }

/* Таблица шире телефона прокручивается сама, а не тянет страницу вбок. */
.table-wrap { overflow-x: auto; -webkit-overflow-scrolling: touch; }

/* Тревога: то, что надо чинить, — красной кромкой, словами. */
.alert {
  padding: var(--s3) var(--s4);
  margin: 0 0 var(--s5);
  border: 1px solid rgba(240, 101, 90, .35);
  border-left: 3px solid var(--fail);
  border-radius: var(--r-sm);
  background: rgba(240, 101, 90, .07);
  color: var(--text);
  font-size: var(--t-sm);
}
.alert p { margin: 0 0 var(--s1); }
.alert p:last-child { margin: 0; }
.alert--ok { border-color: rgba(76, 195, 138, .35); border-left-color: var(--ok); background: rgba(76, 195, 138, .07); }
.alert--info { border-color: var(--line); border-left-color: var(--accent); background: var(--surface); }

/* Вкладки: где я внутри раздела. Профиль и его три страницы. */
.tabs {
  display: flex;
  gap: 2px;
  margin: 0 0 var(--s5);
  border-bottom: 1px solid var(--line);
  overflow-x: auto;
  scrollbar-width: none;
}
.tabs::-webkit-scrollbar { display: none; }
.tabs__link {
  position: relative;
  padding: 10px 14px;
  border-radius: var(--r-sm) var(--r-sm) 0 0;
  color: var(--text-dim);
  font-size: var(--t-sm);
  font-weight: 500;
  white-space: nowrap;
}
.tabs__link:hover { color: var(--text); background: var(--surface); }
.tabs__link--on { color: var(--text); }
.tabs__link--on::after {
  content: "";
  position: absolute;
  left: 12px;
  right: 12px;
  bottom: -1px;
  height: 2px;
  border-radius: 2px;
  background: var(--accent);
}
.tabs__num { margin-left: 6px; color: var(--text-faint); font-variant-numeric: tabular-nums; }

/* ---------- Формы ---------- */

.auth { max-width: 400px; margin: var(--s5) auto var(--s8); }
.auth__card {
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--r-lg);
  padding: var(--s6);
  box-shadow: var(--edge), var(--shadow-lift);
}
.auth__title { font-size: var(--t-xl); margin-bottom: var(--s2); }
.auth__sub { color: var(--text-dim); font-size: var(--t-sm); margin-bottom: var(--s5); }
.auth__foot {
  margin: var(--s5) 0 0;
  padding-top: var(--s4);
  border-top: 1px solid var(--line-soft);
  font-size: var(--t-sm);
  color: var(--text-dim);
}
.auth__foot p { margin: 0 0 var(--s2); }
.auth__foot p:last-child { margin: 0; }
/* Яндекс ID и VK ID над формой (ТЗ §3: основной и второй способ). Кнопки
   — свои, нейтральные: фирменные по правилам служб ставятся вместе с
   ключами (PROGRESS.md, «К заливке»). */
.auth__vneshniy { display: flex; flex-direction: column; gap: var(--s3); }
.auth__vneshniy form { margin: 0; }
.auth__soglasie { margin: var(--s3) 0 0; font-size: var(--t-xs); color: var(--text-faint); line-height: 1.5; }
.auth__ili {
  display: flex; align-items: center; gap: var(--s3);
  margin: var(--s5) 0; color: var(--text-faint); font-size: var(--t-sm);
}
.auth__ili::before, .auth__ili::after { content: ""; flex: 1; border-top: 1px solid var(--line-soft); }

.form { display: flex; flex-direction: column; gap: var(--s4); }
.field { display: flex; flex-direction: column; gap: 6px; }
.field > span { font-size: var(--t-sm); font-weight: 500; color: var(--text-dim); }
.field input[type="text"],
.field input[type="email"],
.field input[type="password"] {
  width: 100%;
  padding: 11px 13px;
  background: var(--surface-2);
  border: 1px solid var(--line);
  border-radius: var(--r-sm);
  color: var(--text);
  font: 400 var(--t-md)/1.4 var(--font);
  box-shadow: var(--shadow-inset);
  transition: border-color .12s ease, background .12s ease, box-shadow .12s ease;
}
.field input::placeholder { color: var(--text-faint); }
.field input:focus {
  outline: none;
  border-color: var(--accent);
  background: var(--ink);
  box-shadow: var(--shadow-inset), var(--ring);
}
.field--inline { flex-direction: row; align-items: flex-start; gap: var(--s3); }
.field--inline > span { color: var(--text); font-weight: 400; line-height: 1.45; font-size: var(--t-sm); }
.field input[type="checkbox"] {
  width: 17px; height: 17px;
  margin: 2px 0 0;
  accent-color: var(--accent);
  flex: none;
}
/* Ошибка живёт под своим полем, а не общей строкой наверху */
.field-error { color: var(--fail); font-size: var(--t-sm); }

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

.flashes { margin-bottom: var(--s5); }
.flash {
  box-shadow: var(--edge), var(--shadow);
  border: 1px solid var(--line);
  border-left-width: 3px;
  border-radius: var(--r-sm);
  background: var(--surface);
  padding: var(--s3) var(--s4);
  margin: 0 0 var(--s2);
  font-size: var(--t-sm);
}
.flash--ok    { border-left-color: var(--ok); }
.flash--error { border-left-color: var(--fail); }
.flash--info  { border-left-color: var(--accent); }

/* ---------- Страница задачи ---------- */

.task {
  display: grid;
  grid-template-columns: minmax(320px, 4fr) minmax(0, 5fr);
  gap: var(--s6);
  align-items: start;
}
/* minmax(0, …), а не 1fr: у «1fr» нижняя граница — ширина содержимого, и
   длинная строка кода в примере растягивала всю страницу шире телефона
   (452 пикселя при экране 375; найдено 28.09 снимком шага игры). */
@media (max-width: 920px) { .task { grid-template-columns: minmax(0, 1fr); gap: var(--s5); } }

/* Текст условия — HTML из services/tekst.py: абзацы, списки, код. До 03.10
   здесь стоял `white-space: pre-wrap`, и переносы из YAML рвали фразу
   посреди строки колонки. */
.statement { line-height: 1.6; }
.statement p { margin: 0 0 var(--s3); }
.statement > :last-child { margin-bottom: 0; }
.statement ul, .statement ol { padding-left: var(--s5); margin: 0 0 var(--s3); }
.statement li { margin-bottom: var(--s1); }
.statement :not(pre) > code {
  padding: 1px 5px;
  background: var(--surface-2);
  border-radius: 4px;
  font-size: 0.92em;
}
.statement pre {
  padding: var(--s3) var(--s4);
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--r-sm);
  overflow-x: auto;
  margin: 0 0 var(--s3);
  font-size: var(--t-sm);
}
/* Короткое условие («Найди ошибку»): четыре строки и обрез. Высотой, а не
   line-clamp: внутри теперь абзацы, а clamp режет только строчный текст. */
.statement--short {
  max-height: calc(1.6em * 4);
  overflow: hidden;
}

/* «Что сделать» — задание на виду, сразу под историей (ТЗ §2.3, 03.10).
   Кромка цвета действия: глаз находит задание, не перечитывая историю. */
.chto-sdelat { margin: var(--s4) 0; border-left: 3px solid var(--accent); }
.chto-sdelat .box__body { padding: var(--s4) var(--s5); }
.chto-sdelat .eyebrow { color: var(--accent); }
.chto-sdelat__return { margin: var(--s3) 0 0; }
.task > section > .chto-sdelat:first-child { margin-top: 0; }
/* Формат «А» (ТЗ §2.3, П-47): «что приходит» и «что вернуть» — частями
   той же коробки. Подпись тише заголовка коробки, но жирнее текста: глаз
   идёт по трём подписям, не перечитывая содержимое. */
.chto-sdelat__chast { margin: var(--s3) 0 0; padding-top: var(--s3); border-top: 1px solid var(--line); }
.chto-sdelat__podpis { margin: 0 0 var(--s1); font-size: var(--t-sm); font-weight: 600; color: var(--text-dim); }
.statement .chto-sdelat__prihodit { margin: 0; padding-left: 1.2em; }
.chto-sdelat__prihodit li { margin-bottom: 0; }
.chto-sdelat__prihodit li p { margin: 0; }
.chto-sdelat__prihodit li + li { margin-top: var(--s1); }
.chto-sdelat__pusto { margin: 0; }
.chto-sdelat__chast .statement p { margin: 0; }
/* Ограничения на решение (ТЗ §2.13) — рядом с «что сделать», своей
   кромкой: это часть задания, но другого рода — правило о коде. */
.ogranicheniya { margin: var(--s4) 0; border-left: 3px dashed var(--accent); }
.ogranicheniya .box__body { padding: var(--s4) var(--s5); }
.ogranicheniya .eyebrow { color: var(--accent); }
.ogranicheniya__spisok { margin: var(--s2) 0 0; padding-left: 1.2em; }
.ogranicheniya__spisok li p { margin: 0; }
.ogranicheniya__spisok li + li { margin-top: var(--s2); }
.ogranicheniya__obhody { margin: var(--s3) 0 0; }
.ogranicheniya__obhody p { margin: 0; }
.narusheniya__head { margin: 0 0 var(--s1); font-weight: 600; }
.narusheniya ul { margin: 0; padding-left: 1.2em; }

/* Шаг игры: где функция в игре и что видно без неё. */
.v-igre {
  margin: var(--s4) 0;
  padding: var(--s3) var(--s4);
  border: 1px dashed var(--line);
  border-radius: var(--r-sm);
  color: var(--text-dim);
}
.v-igre .statement { color: var(--text); }
.v-igre p.muted { margin: var(--s2) 0 0; }

.primer { margin: var(--s4) 0; }
.primer .statement {
  margin-top: var(--s1);
  padding: var(--s3) var(--s4);
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--r-sm);
}

/* Под примером словами — тот же вызов (формат «А»): слова и код рядом,
   одной мыслью, поэтому без отдельной рамки. */
.primer__vyzov { margin: var(--s3) 0 0; }
.primer__vyzov .example-setup { margin-top: var(--s1); }
.primer__vyzov > .example:nth-child(2) { margin-top: var(--s1); }

.example { display: flex; gap: var(--s4); margin: var(--s4) 0; }
.example > div { flex: 1; min-width: 0; }
/* Подготовка к примеру. Стоит вплотную над вызовом: это одна мысль,
   разорванная на «сначала» и «потом», и воздух между ними читался бы
   как два разных блока. */
.example-setup { margin: var(--s4) 0 0; }
.example-setup + .example { margin-top: var(--s2); }

pre.code {
  background: var(--surface);
  border: 1px solid var(--line);
  box-shadow: var(--shadow-inset);
  border-radius: var(--r-sm);
  padding: var(--s3) var(--s4);
  margin: var(--s1) 0 0;
  overflow-x: auto;
  font-size: var(--t-sm);
  line-height: 1.6;
  white-space: pre-wrap;
  word-break: break-word;
}

.disclosure { border-top: 1px solid var(--line-soft); padding: var(--s3) 0; }
.hints { margin: var(--s2) 0; padding-left: 20px; color: var(--text-dim); }
.hints li { margin-bottom: var(--s2); }

.editor-wrap {
  border: 1px solid var(--line);
  border-radius: var(--r-md);
  overflow: hidden;
  height: 400px;
  background: var(--surface);
  box-shadow: var(--edge), var(--shadow);
}
.editor-wrap .cm-editor { height: 100%; }
.editor-wrap .cm-editor.cm-focused { outline: none; }
#code { display: none; }
#code.code-fallback {
  display: block;
  width: 100%; height: 100%;
  border: 0; padding: var(--s4);
  background: var(--surface);
  color: var(--text);
  font: 400 var(--t-sm)/1.6 var(--mono);
  resize: none;
  box-shadow: none;
}

.task__actions { display: flex; align-items: center; gap: var(--s3); margin: var(--s4) 0; }

/* ---------- Результат проверки ---------- */

.result { margin-top: var(--s2); }
.verdict {
  display: flex;
  align-items: baseline;
  gap: var(--s2);
  font-size: var(--t-lg);
  font-weight: 600;
  margin-bottom: var(--s4);
}
.status-ok   { color: var(--ok); }
.status-fail { color: var(--fail); }

.test {
  border-left: 2px solid var(--line);
  padding: var(--s2) 0 var(--s2) var(--s4);
  margin-bottom: var(--s3);
}
.test--passed  { border-left-color: var(--ok); }
.test--failed,
.test--error,
.test--timeout { border-left-color: var(--fail); }
.test__head { font-weight: 500; font-size: var(--t-sm); }
.test__row { margin-top: var(--s2); font-size: var(--t-sm); }
.test__error { color: var(--fail); }

/* ---------- Приглашение зарегистрироваться ---------- */

.prompt {
  margin-top: var(--s4);
  padding-top: var(--s4);
  border-top: 1px solid var(--line-soft);
  font-size: var(--t-sm);
}
.prompt--wall {
  border: 1px solid var(--accent);
  background: var(--accent-dim);
  border-radius: var(--r-md);
  padding: var(--s5);
  margin-top: var(--s5);
  box-shadow: var(--edge), var(--shadow);
}
.prompt .btn { margin-right: var(--s2); }

/* ---------- Список задач ---------- */

.task-list { list-style: none; margin: 0; padding: 0; }
.task-list__item {
  display: flex;
  align-items: baseline;
  gap: var(--s3);
  padding: var(--s4) 0;
  border-bottom: 1px solid var(--line-soft);
}
.task-list__item a { color: var(--text); font-weight: 500; }
.task-list__item a:hover { color: var(--accent); }
.tag {
  font-size: var(--t-xs);
  color: var(--text-faint);
  border: 1px solid var(--line);
  border-radius: 999px;
  padding: 2px 9px;
}

/* ---------- Страница ошибки ---------- */

.error-page { text-align: center; padding: var(--s8) 0; }
.error-page__code {
  font-family: var(--mono);
  font-size: 56px;
  color: var(--text-faint);
  line-height: 1;
  margin-bottom: var(--s4);
}

/* ---------- Каталог ---------- */

/* Каталог — главный экран продукта. Поиск и фильтры вынесены на всю
   ширину: в прошлой версии поиск стоял в колонке 240 px и после вычета
   кнопки оставлял поле в 146 px, около пятнадцати видимых символов. */
.catalog {
  display: grid;
  grid-template-columns: 240px minmax(0, 1fr);
  grid-template-areas:
    "search  search"
    "filters filters"
    "side    main";
  column-gap: var(--s7);
  row-gap: var(--s5);
  align-items: start;
}
@media (max-width: 920px) {
  .catalog {
    grid-template-columns: 1fr;
    grid-template-areas: "search" "filters" "side" "main";
    row-gap: var(--s4);
  }
}

.catalog__side { grid-area: side; position: sticky; top: 76px; }
@media (max-width: 920px) { .catalog__side { position: static; } }
.catalog__main { grid-area: main; }

/* ---------- Поиск ---------- */

.search {
  grid-area: search;
  display: flex;
  gap: var(--s3);
  align-items: stretch;
}
.search__field {
  flex: 1;
  min-width: 0;
  display: flex;
  align-items: center;
  gap: var(--s2);
  padding: 0 14px;
  background: var(--surface-2);
  border: 1px solid var(--line);
  border-radius: var(--r-md);
  box-shadow: var(--shadow-inset);
}
.search__icon { flex: none; opacity: .55; font-size: var(--t-sm); line-height: 1; }
.search input {
  flex: 1;
  min-width: 0;
  height: 44px;
  padding: 0;
  background: none;
  border: 0;
  color: var(--text);
  /* 16 px обязательны: при меньшем размере iOS зумит страницу при фокусе,
     и человек попадает на каталог в увеличенном виде. */
  font: 400 var(--t-md)/1.4 var(--font);
  box-shadow: none;
}
.search input::placeholder { color: var(--text-faint); }
.search input:focus { outline: none; }
.search__field:focus-within {
  border-color: var(--accent);
  background: var(--ink);
  box-shadow: var(--shadow-inset), var(--ring);
}
.search .btn { flex: none; }
.search input::-webkit-search-cancel-button { filter: invert(.6); }

/* ---------- Фильтры над списком ---------- */

.filters {
  grid-area: filters;
  display: flex;
  flex-wrap: wrap;
  gap: var(--s5);
  padding-bottom: var(--s4);
  border-bottom: 1px solid var(--line-soft);
}
.filters__group { display: flex; align-items: center; gap: var(--s3); flex-wrap: wrap; }
.filters .eyebrow { margin: 0; }

.side-block { margin-bottom: var(--s5); }
.side-total { margin-top: var(--s4); }

/* ---------- Группы тем ---------- */

.group { margin-top: 2px; }
.group__head {
  display: flex;
  align-items: center;
  gap: var(--s2);
  /* Крупнее и выше прежнего: по этой строке нажимают, а её высота была
     чуть больше строки текста. */
  padding: 10px 10px 10px 8px;
  border-radius: var(--r-sm);
  color: var(--text);
  font-size: var(--t-sm);
  font-weight: 500;
  cursor: pointer;
  list-style: none;
  user-select: none;
}
.group__head::-webkit-details-marker { display: none; }
.group__head:hover { background: var(--surface); }
.group__head:hover .group__arrow { color: var(--accent); }
.group__head:focus-visible { outline: none; box-shadow: var(--ring); }

/* Стрелка нарисована, а не набрана символом: у типографского знака ▸ нет ни
   толщины линии, ни размера, независимого от шрифта подписи. Как элемент
   управления он был почти не виден. */
.group__arrow {
  flex: none;
  color: var(--text-dim);
  transition: transform .15s ease, color .12s ease;
}
.group[open] > .group__head .group__arrow { transform: rotate(90deg); }

.group__title { flex: 1; min-width: 0; }
.group__count {
  color: var(--text-faint);
  font-size: var(--t-xs);
  font-variant-numeric: tabular-nums;
}

.group .side-link { padding-left: 32px; }
/* Вертикальная направляющая от стрелки вниз: показывает, что темы
   принадлежат группе, без отступов и рамок. */
.group[open] > .side-link,
.group[open] > .bar { position: relative; }
.group[open] > .side-link::before {
  content: "";
  position: absolute;
  left: 14px;
  top: 0;
  bottom: 0;
  width: 1px;
  background: var(--line-soft);
}

.side-link {
  display: flex;
  justify-content: space-between;
  gap: var(--s2);
  padding: 7px 10px;
  border-radius: var(--r-sm);
  color: var(--text-dim);
  font-size: var(--t-sm);
}
.side-link:hover { background: var(--surface); color: var(--text); }
.side-link--on { background: var(--surface); color: var(--text); font-weight: 500; }

/* Полоска прогресса по теме. Единственное место, где акцент означает
   не действие, а достижение — и он здесь приглушён до тонкой линии. */
.bar {
  height: 2px;
  background: var(--line-soft);
  border-radius: 2px;
  margin: 0 10px var(--s2);
  overflow: hidden;
}
.bar span { display: block; height: 100%; background: var(--accent); }
.bar--wide { margin: var(--s2) 0 var(--s5); height: 3px; }

.chips { display: flex; flex-wrap: wrap; gap: 6px; }
.chip {
  padding: 5px 11px;
  border: 1px solid var(--line);
  border-radius: 999px;
  color: var(--text-dim);
  font-size: var(--t-sm);
}
.chip:hover { border-color: var(--text-faint); color: var(--text); }
.chip--on {
  background: var(--accent-dim);
  border-color: var(--accent);
  color: var(--accent);
}
/* Число задач рядом с уровнем: без него фильтр уводит в пустой список,
   а пустой список читается как поломка сайта, а не как «здесь пока пусто». */
.chip__count { margin-left: 6px; opacity: .6; font-variant-numeric: tabular-nums; }

/* ---------- Строки задач ---------- */

.rows { list-style: none; margin: var(--s5) 0 0; padding: 0; }

/* Строка каталога.
   Колонки фиксированы, чтобы строки вставали друг под друга. Раньше правая
   часть плыла вслед за длиной названия темы, и список выглядел неопрятно
   именно поэтому, а не из-за оформления отдельных элементов. */
.row {
  display: grid;
  /* Первая колонка — поля с номером строки (П-63): до четырёх цифр. */
  grid-template-columns: 30px minmax(0, 1fr) 150px 108px;
  align-items: baseline;
  gap: var(--s3);
  /* Отступы внутрь, поля наружу: подсветка при наведении выходит за края
     колонки, а текст остаётся на месте.

     Раньше подсветка рисовалась псевдоэлементом поверх строки — и накрывала
     текст. Абсолютно позиционированный ::before красится выше обычных
     детей, так что при наведении название и описание пропадали. Уйти от
     этого через z-index: -1 нельзя: position: relative не создаёт контекст
     наложения, и подсветка ушла бы за фон страницы. Поэтому фон на самой
     строке, без псевдоэлемента. */
  padding: 13px 12px;
  margin: 0 -12px;
  border-radius: var(--r-sm);
  border-bottom: 1px solid var(--line-soft);
  position: relative;
  transition: background .12s ease;
}
/* Наведение — как помеченная строка разбора на главной (П-63): янтарная
   кромка слева и тот же фон. Кромка — тенью внутрь, а не border-left:
   рамка сдвинула бы текст на два пикселя при каждом наведении. */
.row:hover { background: var(--accent-dim); box-shadow: inset 2px 0 0 var(--accent); }
.row:last-child { border-bottom: 0; }

.row__mark { color: var(--ok); line-height: 1; }

/* ВАРИАНТЫ СТРОКИ. Все — здесь, рядом с сеткой, а не по своим разделам.
   Довод из поломки: сетка выше расписана под каталог задач и требует
   ЧЕТЫРЁХ детей. Список проектов отдавал двух — и они раскладывались по
   первым двум колонкам, то есть название с описанием попадали в 22 пикселя
   под галочку, а «9 из 9 шагов» вставало вплотную к ним. Со стороны это
   выглядело как «съехалась вёрстка», хотя вёрстка делала ровно то, что ей
   написано. Правило для приёмов существовало и лежало в разделе приёмов —
   за четыреста строк отсюда, поэтому при добавлении проектов его никто не
   увидел. Теперь список вариантов виден там же, где сетка.
   С 03.10 вариантов нет: проекты, игры и секреты — карточками (.pcards),
   прочие списки — строками в коробке (.list). Строка .row осталась одна —
   строка каталога задач. Новый вариант — здесь, и page_test.py
   (proverit_setki_spiskov) потребует от него сетку. */

.row__body { min-width: 0; }
.row__title {
  color: var(--text);
  font-weight: 500;
  /* Ссылка растянута на всю строку: попасть по ней должно быть легко. */
  position: static;
}
.row__title::after { content: ""; position: absolute; inset: 0; }
.row:hover .row__title { color: var(--accent); }
.row__desc {
  margin: 3px 0 0;
  color: var(--text-faint);
  font-size: var(--t-sm);
  line-height: 1.45;
  /* Не длиннее одной строки: описание помогает узнать задачу, а не заменяет
     условие. Две строки превращают список в стену текста. */
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.row--solved .row__title { color: var(--text-dim); }

/* Полное условие по наведению.
   Одной строки хватает, чтобы узнать задачу («там про счётчик воды»), но не
   чтобы решить, открывать ли её. Показываем целиком — только там, где есть
   мышь: на касании наведения не существует, и подсказка всплывала бы в
   ответ на попытку нажать. */
@media (hover: hover) {
  .row__desc::after {
    content: attr(data-full);
    position: absolute;
    left: 34px;
    right: 12px;
    top: calc(100% - 6px);
    z-index: 3;
    padding: var(--s3) var(--s4);
    background: var(--surface-2);
    border: 1px solid var(--line);
    border-radius: var(--r-sm);
    box-shadow: var(--shadow-2, 0 8px 24px rgba(0, 0, 0, .45));
    color: var(--text-dim);
    font-size: var(--t-sm);
    line-height: 1.5;
    white-space: normal;
    opacity: 0;
    visibility: hidden;
    /* Задержка обязательна: без неё подсказки вспыхивают под курсором,
       пока человек просто ведёт мышь вниз по списку. */
    transition: opacity .12s ease, visibility .12s ease;
    transition-delay: .45s;
    pointer-events: none;
  }
  /* Нет полного текста — нет и подсказки.
     Подсказка берёт текст из data-full. У списков, где его не проставили
     (проекты, приёмы), attr(data-full) давал ПУСТУЮ строку, и при наведении
     всплывала пустая рамка. Условие на атрибут надёжнее уговора «не забудь
     проставить»: забыли — подсказки просто нет, а не есть пустая. */
  .row__desc:not([data-full])::after { content: none; }
  .row:hover .row__desc::after { opacity: 1; visibility: visible; }
  /* Строка с раскрытой подсказкой должна лежать выше соседних. */
  .row:hover { z-index: 3; }
  /* У последней строки подсказка уходила бы за край списка. */
  .row:last-child .row__desc::after { top: auto; bottom: calc(100% - 6px); }
}

.row__topic {
  color: var(--text-dim);
  font-size: var(--t-sm);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
/* Пилюля осталась ровно одна — та, что означает «тем больше одной».
   Когда пилюлей отмечено всё подряд, не отмечено ничего. */
.row__plus {
  margin-left: 6px;
  padding: 1px 6px;
  border: 1px solid var(--accent-dim);
  border-radius: 999px;
  color: var(--accent);
  font-size: var(--t-xs);
  cursor: help;
}

/* Значок формы задачи (Р-18): словом, второй строкой колонки темы.
   Моноширинным — это подсказка «что писать», то есть про код. Цвет не
   вводим: зелёный и красный заняты статусом проверки, акцент — пилюлей. */
.row__form {
  display: block;
  margin-top: 3px;
  color: var(--text-faint);
  font-family: var(--mono);
  font-size: var(--t-xs);
  line-height: 1.2;
  cursor: help;
}
.row__ogr {
  margin-left: var(--s2);
  color: var(--accent);
  font-family: var(--mono);
  font-size: var(--t-xs);
  white-space: nowrap;
  cursor: help;
  /* Над растянутой ссылкой строки: иначе подсказка по наведению не видна. */
  position: relative;
  z-index: 1;
}
/* «С графикой» (П-64) — цветом картинки, а не акцентом: янтарный занят
   «с ограничением» и действием; синий — второй цвет рисовалок, им же
   нарисованы тела на холсте. Не зелёный и не красный — те заняты исходом. */
.row__graf { color: var(--pokaz-vtoroy); font-family: var(--mono); font-size: var(--t-xs); white-space: nowrap; }
.s-grafikoy { margin: var(--s3) 0; color: var(--text-dim); }
.s-grafikoy .row__graf { margin-right: var(--s2); }
@media (max-width: 720px) {
  /* Колонка темы на узком экране — строка под названием: значок встаёт
     рядом с темой, а не третьей строкой. */
  .row__form { display: inline; margin: 0 0 0 var(--s2); }
}

/* Строка «Найдено: N» и кнопка «Случайная задача» — одной строкой. */
.catalog__count {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: var(--s2) var(--s4);
}
.catalog__count p { margin: 0; }

/* ---------- Шкала уровня ---------- */

/* Пять ступеней различались только насыщенностью серой подписи — на глаз
   это не различалось вовсе, хотя уровень и есть главная ось каталога.
   Делений видно пять, заполненных столько, каков уровень: читается без
   чтения и выравнивается в колонку. Цвет не вводим: зелёный и красный
   заняты статусом проверки. */
.row__level { display: flex; flex-direction: column; gap: 5px; align-items: flex-start; }
.level { display: inline-flex; gap: 3px; align-items: flex-end; height: 11px; }
.level__step {
  width: 4px;
  height: 4px;
  border-radius: 1px;
  background: var(--line);
  transition: background .12s ease;
}
.level__step:nth-child(2) { height: 6px; }
.level__step:nth-child(3) { height: 8px; }
.level__step:nth-child(4) { height: 10px; }
.level__step:nth-child(5) { height: 12px; }
.level__step--on { background: var(--text-dim); }
.row:hover .level__step--on { background: var(--text); }
.row--solved .level__step--on { background: var(--text-faint); }

.row__level-name {
  color: var(--text-faint);
  font-size: var(--t-xs);
  line-height: 1;
}

@media (max-width: 720px) {
  /* На узком экране колонки схлопываются: название и описание сверху,
     тема и уровень одной строкой под ними. */
  .row {
    grid-template-columns: 22px minmax(0, 1fr);
    row-gap: 6px;
  }
  .row__topic, .row__level { grid-column: 2; }
  .row__level { flex-direction: row; align-items: center; gap: var(--s2); }
  .row__desc { white-space: normal; }
}

.tag {
  font-size: var(--t-xs);
  color: var(--text-faint);
  border: 1px solid var(--line);
  border-radius: 999px;
  padding: 2px 9px;
  white-space: nowrap;
}
a.tag:hover { border-color: var(--text-faint); color: var(--text-dim); }
/* Сложность различается насыщенностью подписи, а не новым цветом:
   вводить ради неё зелёный и красный нельзя — они заняты статусом проверки.
   Пять ступеней укладываются в ту же градацию, крайняя выделена рамкой. */
.tag--d1 { color: var(--text-faint); }
.tag--d2 { color: var(--text-faint); }
.tag--d3 { color: var(--text-dim); }
.tag--d4 { color: var(--text); border-color: var(--text-faint); }
.tag--d5 { color: var(--text); border-color: var(--text-dim); font-weight: 500; }

/* Задача, где задействовано несколько тем. Помечается числом, а не
   перечислением: четыре названия в строке каталога съедают саму задачу. */
.tag--mix { color: var(--accent); border-color: var(--accent-dim); cursor: help; }

.pager {
  display: flex;
  align-items: center;
  gap: var(--s4);
  margin-top: var(--s5);
}
.pager--task { margin-top: var(--s4); }
.pager--task .btn { max-width: 45%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; display: block; }

/* ---------- После решения: разбор и подсказки ---------- */

/* Показывается только при зелёных тестах. Оформление намеренно спокойное:
   это не предупреждение и не ошибка, решение верное. Акцентную рамку и
   красное здесь использовать нельзя — они означают другое. */
.after {
  margin-top: var(--s5);
  padding-top: var(--s4);
  border-top: 1px solid var(--line-soft);
}
.after__head {
  margin: 0 0 var(--s3);
  font-size: var(--t-sm);
  font-weight: 500;
  color: var(--text-dim);
}
.after__text {
  color: var(--text-dim);
  font-size: var(--t-sm);
  line-height: 1.6;
  white-space: pre-line;
}
/* Размеченный разбор: переносы делает HTML, а не pre-line. */
.after__text.statement { white-space: normal; }

.hint {
  padding: var(--s3) var(--s4);
  margin-bottom: var(--s3);
  background: var(--surface);
  border-radius: var(--r-sm);
  /* Левая кромка вместо полной рамки: подсказок бывает две подряд, и две
     полные рамки читаются как два предупреждения. */
  border-left: 2px solid var(--line);
  border-radius: 0 var(--r-sm) var(--r-sm) 0;
}
.hint:last-child { margin-bottom: 0; }
.hint__head { font-size: var(--t-sm); font-weight: 500; color: var(--text); }
.hint__text {
  margin-top: 4px;
  color: var(--text-dim);
  font-size: var(--t-sm);
  line-height: 1.55;
}

/* ---------- Переключатель темы ---------- */

/* Значок один, меняется в зависимости от текущей темы: показывается тот,
   куда переключит нажатие. Показывать значок текущего состояния — обычная
   путаница: человек видит солнце на светлой теме и не понимает, что нажатие
   включит тёмную. */
.theme-toggle {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 34px;
  height: 34px;
  padding: 0;
  border: 1px solid transparent;
  border-radius: var(--r-sm);
  background: none;
  color: var(--text-dim);
  cursor: pointer;
}
.theme-toggle:hover { background: var(--surface-2); color: var(--text); }
.theme-toggle:focus-visible { outline: none; box-shadow: var(--ring); }

:root[data-theme="dark"] .theme-toggle__moon,
:root[data-theme="light"] .theme-toggle__sun { display: none; }

.footer__row { display: flex; flex-wrap: wrap; gap: var(--s3) var(--s5); justify-content: space-between; }
/* Ссылок с этапа 9 восемь: без переноса строка подвала была бы шире
   телефона и тянула страницу вбок. */
.footer__links { display: flex; flex-wrap: wrap; gap: var(--s2) var(--s4); }
/* Вторая строка подвала — юридическое и реквизиты продавца (ТЗ §10). */
/* Без своей линии: у .container поля по бокам, и черта шла бы шире текста. */
.footer__row--yur { margin-top: var(--s4); font-size: var(--t-xs); }
.footer__rekvizity { overflow-wrap: anywhere; }

/* Плашка про куки (ТЗ §10): внизу, поверх, пока не нажали «Понятно».
   Узкая и тихая — она сообщает, а не загораживает работу. */
.kuki {
  position: fixed;
  left: 50%;
  bottom: var(--s4);
  z-index: 40;
  transform: translateX(-50%);
  display: flex;
  align-items: center;
  gap: var(--s4);
  width: min(720px, calc(100% - 2 * var(--s4)));
  padding: var(--s3) var(--s4);
  background: var(--surface-2);
  border: 1px solid var(--line);
  border-radius: var(--r-md);
  box-shadow: var(--edge), var(--shadow-lift);
}
.kuki__tekst { margin: 0; color: var(--text-dim); font-size: var(--t-sm); line-height: 1.5; }
@media (max-width: 560px) {
  .kuki { flex-direction: column; align-items: stretch; gap: var(--s2); }
}

/* ---------- Юридические страницы ---------- */

.legal { max-width: 68ch; }
.legal h2 { margin: var(--s6) 0 var(--s3); font-size: var(--t-lg); }
.legal p, .legal li { color: var(--text-dim); line-height: 1.65; }
.legal ul { padding-left: var(--s5); }
/* Перечень «что — зачем — основание» в политике (legal/privacy.html). */
.legal__dl dt { margin-top: var(--s4); font-weight: 600; color: var(--text); }
.legal__dl dd { margin: var(--s1) 0 0; color: var(--text-dim); line-height: 1.65; }

.legal ol { padding-left: var(--s5); }
.legal ol li { margin-bottom: var(--s2); }
.legal__primer {
  padding: var(--s3) var(--s4);
  border-left: 3px solid var(--accent);
  background: var(--surface);
  border-radius: 0 var(--r-sm) var(--r-sm) 0;
}
/* Реквизиты продавца — оферта и контакты (legal/_rekvizity.html). */
.rekvizity {
  display: grid;
  grid-template-columns: max-content minmax(0, 1fr);
  gap: var(--s2) var(--s5);
  margin: var(--s4) 0;
  padding: var(--s4) var(--s5);
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--r-md);
  font-size: var(--t-sm);
}
.rekvizity dt { color: var(--text-faint); }
.rekvizity dd { margin: 0; color: var(--text); overflow-wrap: anywhere; }
.rekvizity__net { color: var(--text-faint); font-style: italic; }
@media (max-width: 560px) {
  .rekvizity { grid-template-columns: minmax(0, 1fr); gap: 2px; }
  .rekvizity dd { margin-bottom: var(--s2); }
}

/* ---------- Монеты и цены (этап 9) ----------
   Четыре пакета карточками в ряд; второй выделен — не самый дорогой, а
   тот, с которого обычно начинают (ТЗ §4: порог входа — решение, которое
   принимают не думая). На телефоне — по две. */

.pakety {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: var(--s4);
  margin-bottom: var(--s3);
}
@media (max-width: 860px) { .pakety { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
.paket {
  display: flex;
  flex-direction: column;
  gap: var(--s1);
  min-width: 0;
  padding: var(--s5);
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--r-lg);
  box-shadow: var(--edge), var(--shadow);
}
.paket--vydelen { border-color: rgba(240, 164, 60, .45); box-shadow: var(--edge), var(--shadow-lift); }
.paket__monet { margin: 0; font-size: 30px; font-weight: 700; letter-spacing: -0.02em; font-variant-numeric: tabular-nums; }
.paket__monet span { color: var(--text-dim); font-size: var(--t-md); font-weight: 500; letter-spacing: 0; }
.paket__hvatit { margin: 0; color: var(--text-dim); font-size: var(--t-sm); }
.paket__cena { margin: var(--s3) 0 var(--s3); font-size: var(--t-xl); font-weight: 600; font-variant-numeric: tabular-nums; }
.paket__forma { margin: auto 0 0; }
.paket__knopka { width: 100%; justify-content: center; margin-top: auto; }
.pakety__oferta { margin: 0 0 var(--s6); }

.oplata-itog p:first-child { font-size: var(--t-md); }
.oplaty__chek { display: flex; gap: var(--s2); min-width: 280px; }
.oplaty__chek input { flex: 1; min-width: 0; padding: 5px 8px; font-size: var(--t-sm); }

/* Страница двойника ЮKassa — нарочито служебная, пунктиром. */
.dvoynik { border-style: dashed; max-width: 640px; }
.dvoynik__summa { margin: 0 0 var(--s2); font-size: 34px; font-weight: 700; font-variant-numeric: tabular-nums; }
.dvoynik__knopki { display: flex; flex-wrap: wrap; gap: var(--s3); margin-top: var(--s5); }
/* «Ящик» двойника почты людям (П-43): письма как есть, ссылки — рабочие. */
.dvoynik--shiroko { max-width: none; }
.pismo { margin: var(--s2) 0 0; white-space: pre-wrap; overflow-wrap: anywhere; font-family: var(--mono);
  font-size: var(--t-sm); color: var(--text-dim); }

/* ---------- Разбор кода и подсказки ---------- */

.review-bar { display: flex; align-items: center; gap: var(--s3); margin-top: var(--s4); flex-wrap: wrap; }
.btn--small { padding: 7px 14px; font-size: var(--t-sm); }

/* Здесь до 19.10 стояло правило `.review` — оформление разбора на
   странице задачи. Разбор давно рисуется в панели вывода, класс со
   страницы задачи ушёл, а правило осталось и легло на окно главной с тем
   же именем: отступ внутри и `white-space: pre-line` — каждый перенос
   шаблона стал пустой строкой, окно вытянулось на сто с лишним пикселей.
   Заказчик: «блок стал длинным, появились лишние пробелы». Проверка —
   page_test.py, proverit_okno_glavnoy. */
.hints__foot { margin-top: var(--s3); color: var(--text-faint); }

/* ---------- Песочница ---------- */

/* Отделена от проверки и визуально: она ничего не оценивает.
   Никаких зелёных и красных, никакого счёта. */
.sandbox {
  display: flex;
  align-items: flex-end;
  gap: var(--s3);
  margin-top: var(--s4);
  flex-wrap: wrap;
}
.sandbox__field { flex: 1; min-width: 180px; display: flex; flex-direction: column; gap: 4px; }
.sandbox__field textarea,
.sandbox__field input {
  width: 100%;
  padding: 8px 10px;
  background: var(--surface-2);
  border: 1px solid var(--line);
  border-radius: var(--r-sm);
  color: var(--text);
  font: 400 var(--t-sm)/1.4 var(--mono, ui-monospace, monospace);
  resize: vertical;
}
.sandbox__field textarea:focus,
.sandbox__field input:focus {
  outline: none;
  border-color: var(--accent);
  box-shadow: var(--ring);
}

.run-out { margin-top: var(--s3); }
.run-out__head {
  margin: var(--s3) 0 4px;
  color: var(--text-faint);
  font-size: var(--t-xs);
}
.run-out__head:first-child { margin-top: 0; }

/* Объяснение расхождения с условием.
   Не ошибка и не предупреждение: решение может быть верным по мысли,
   просто написанным не по условию. Поэтому спокойный тон, без красного. */
.dogovor {
  margin-top: var(--s4);
  padding: var(--s3) var(--s4);
  background: var(--surface);
  border-left: 2px solid var(--accent);
  border-radius: 0 var(--r-sm) var(--r-sm) 0;
  color: var(--text-dim);
  font-size: var(--t-sm);
  line-height: 1.55;
}

/* ---------- Один разобранный случай ---------- */

/* Вместо списка проверок с номерами. Подписи в колонку, значения моноширинно:
   человек сравнивает «ждали» и «вышло» глазами, и они должны стоять друг
   под другом посимвольно. */
.sluchay { margin-top: var(--s3); display: grid; gap: 6px; }
.sluchay__row { display: grid; grid-template-columns: 92px minmax(0, 1fr); align-items: start; gap: var(--s3); }
.sluchay__label { color: var(--text-faint); font-size: var(--t-xs); padding-top: 6px; }
.sluchay .code { margin: 0; }

/* Полный отчёт — по требованию. Не спрятан, но и не лезет в глаза. */
.vse-proverki { margin-top: var(--s4); }
.vse-proverki > summary {
  color: var(--text-faint);
  font-size: var(--t-sm);
  cursor: pointer;
  list-style: none;
  padding: 6px 0;
}
.vse-proverki > summary:hover { color: var(--text-dim); }
.vse-proverki > summary::-webkit-details-marker { display: none; }
.vse-proverki > summary::before { content: "▸ "; }
.vse-proverki[open] > summary::before { content: "▾ "; }
/* Эталон решившему: тот же свёрнутый вид, что «Показать все проверки», —
   не главное на экране, а по требованию. */
.etalon > .code { margin-top: var(--s2); }
/* Слева, под «Задача решена», раскрытый эталон прилипал к тексту условия:
   у блока там свой отступ снизу, чтобы код не сливался с ситуацией. */
.task section > .etalon { margin: var(--s2) 0 var(--s4); }

/* ---------- Обслуживание ---------- */

/* Каркас (03.10): меню слева, страница справа. Обслуживание — рабочее место
   одного человека, и место в нём должно быть видно сразу: до 03.10 меню
   было строкой капители над заголовком, без отметки, где ты. */
.admin { display: grid; grid-template-columns: 200px minmax(0, 1fr); gap: var(--s6); align-items: start; }
.admin__telo { min-width: 0; }
.admin__nav { position: sticky; top: 84px; display: flex; flex-direction: column; gap: 2px; }
.admin__nav-zag {
  margin: 0 0 var(--s2);
  padding: 0 12px;
  color: var(--text-faint);
  font-size: var(--t-xs);
  font-weight: 600;
  letter-spacing: .08em;
  text-transform: uppercase;
}
.admin__link {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--s2);
  padding: 8px 12px;
  border-radius: var(--r-sm);
  color: var(--text-dim);
  font-size: var(--t-sm);
  font-weight: 500;
}
.admin__link:hover { background: var(--surface); color: var(--text); }
.admin__link--on { background: var(--surface-2); color: var(--text); box-shadow: inset 2px 0 0 var(--accent); }
.admin__schet {
  min-width: 20px;
  padding: 0 6px;
  border-radius: 999px;
  background: var(--accent);
  color: var(--on-accent);
  font-size: 11px;
  font-weight: 700;
  line-height: 18px;
  text-align: center;
}
@media (max-width: 920px) {
  .admin { grid-template-columns: minmax(0, 1fr); gap: var(--s4); }
  .admin__nav {
    position: static;
    flex-direction: row;
    overflow-x: auto;
    scrollbar-width: none;
    padding-bottom: var(--s2);
    border-bottom: 1px solid var(--line);
  }
  .admin__nav::-webkit-scrollbar { display: none; }
  .admin__nav-zag { display: none; }
  .admin__link { white-space: nowrap; }
  .admin__link--on { box-shadow: inset 0 -2px 0 var(--accent); }
}

.admin-actions { display: flex; flex-wrap: wrap; gap: var(--s3); margin: var(--s4) 0 0; }

/* Вывод команды. Моноширинно и с прокруткой: это буквально то, что
   напечатала бы консоль, и переносить строки в нём нельзя. */
pre.admin-out {
  max-height: 420px;
  margin: 0;
  overflow: auto;
  white-space: pre;
  font-size: var(--t-sm);
  border: 0;
  border-radius: 0 0 var(--r-lg) var(--r-lg);
  box-shadow: none;
  background: var(--ink);
}

/* Ход работы обслуживания (П-44, 10.10): этап строкой, под ним полоса во
   всю ширину — «сколько из скольких» читается и с телефона. */
.rabota__etapy { margin: 0 0 var(--s3); padding: 0; }
.rabota__etapy .list__item--blok { padding-left: 0; padding-right: 0; }
.rabota__etapy .meter { display: block; margin-top: var(--s2); height: 8px; }
.rabota #r-seychas { min-height: 1.5em; overflow-wrap: anywhere; }
.rabota form { display: flex; flex-wrap: wrap; align-items: center; gap: var(--s3); margin-top: var(--s3); }

/* «Найди ошибку», под замком (П-46, 11.10): замок и «Решить задачу» рядом;
   на телефоне — под названием, а не шире экрана. */
.ohoty-zamok .list__side { display: flex; align-items: center; gap: var(--s2); }
@media (max-width: 560px) {
  .ohoty-zamok .list__item { grid-template-columns: minmax(0, 1fr); }
  .ohoty-zamok .list__side { justify-content: flex-start; margin-top: var(--s2); flex-wrap: wrap; }
}

/* Пачка через чат (П-45, 11.10): три шага, у второго — выбор файла. */
.napolnenie-shagi { margin: 0; padding: 0; }
.napolnenie-paket { display: flex; flex-wrap: wrap; align-items: center; gap: var(--s3); margin-top: var(--s3); }
.napolnenie-paket input[type="file"] { max-width: 100%; font-size: var(--t-sm); }
/* Итог для чата: текст целиком, моноширинно, выделяется одним движением. */
.otchet-chat { width: 100%; min-height: 12em; font-family: var(--mono, monospace); font-size: var(--t-sm);
  background: var(--ink); color: var(--text); border: 1px solid var(--line); border-radius: var(--r-md);
  padding: var(--s3); resize: vertical; }

/* Темы и уровни: число с полоской — видно, где густо, где пусто. */
.admin-temy .list__item { padding-top: 8px; padding-bottom: 8px; }
.admin-temy__chislo { display: inline-flex; align-items: center; gap: var(--s3); font-size: var(--t-sm); font-variant-numeric: tabular-nums; }
.admin-temy__chislo .meter { width: 90px; height: 4px; }
.admin-temy__chislo .faint { min-width: 2.5ch; text-align: right; }

/* Поиск над таблицей: та же строка, что в каталоге. */
.admin-poisk { margin-bottom: var(--s5); }
.admin-poisk select { flex: none; }

.admin-table { width: 100%; border-collapse: collapse; margin: 0; }
.admin-table th {
  text-align: left;
  padding: 10px 12px;
  border-bottom: 1px solid var(--line);
  background: var(--surface-2);
  color: var(--text-faint);
  font-size: var(--t-xs);
  font-weight: 600;
  letter-spacing: .05em;
  text-transform: uppercase;
  white-space: nowrap;
}
.admin-table td { padding: 11px 12px; border-bottom: 1px solid var(--line-soft); vertical-align: top; }
.admin-table tr:last-child td { border-bottom: 0; }
.admin-table tbody tr:hover td { background: var(--surface-2); }
.admin-table th:first-child, .admin-table td:first-child { padding-left: var(--s5); }
.admin-table th:last-child, .admin-table td:last-child { padding-right: var(--s5); }
.box > .table-wrap:first-child .admin-table th:first-child { border-top-left-radius: var(--r-lg); }
.box > .table-wrap:first-child .admin-table th:last-child { border-top-right-radius: var(--r-lg); }
.admin-table form { display: inline; margin: 0; }
.admin-table .chislo { text-align: right; font-variant-numeric: tabular-nums; }
.admin-table__deystvie { text-align: right; }

/* «Люди»: бан и удаление свёрнуты — разрушительное не лезет под руку,
   а раскрывается по одному на строку. Форма внутри — столбиком. */
.lyudi__imya { font-weight: 500; }
.lyudi td { vertical-align: middle; }
.lyudi-monety { display: inline-flex !important; align-items: center; gap: var(--s2); }
.lyudi-monety input { width: 76px; padding: 6px 8px; font-size: var(--t-sm); text-align: right; }
.lyudi-rol { cursor: pointer; font: inherit; font-size: var(--t-xs); }
.lyudi-rol:hover { border-color: var(--text-faint); }
.lyudi-vhod { min-width: 150px; }
.lyudi-deystvie > summary { padding: 2px 0; }
.admin-table .lyudi-forma { display: flex; flex-direction: column; gap: 6px; margin-top: 4px; max-width: 280px; }
.lyudi-forma input:not([type=checkbox]) { width: 100%; padding: 7px 10px; font-size: var(--t-sm); }
.lyudi-forma label { display: flex; align-items: center; gap: 6px; }
.lyudi-forma p { margin: 0; }
.box__foot.note { display: block; }
.box__foot.note p + p { margin-top: var(--s2); }

/* «Настройки»: строка — подпись с ключом слева, поле справа, одной ширины
   у всех. Ключ — под подписью: им пользуются, когда правят базу руками. */
.nastroyka {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto 200px;
  align-items: center;
  gap: var(--s2) var(--s4);
  padding: 12px var(--s5);
  border-bottom: 1px solid var(--line-soft);
}
.nastroyka:last-child { border-bottom: 0; }
.nastroyka__podpis { display: flex; flex-direction: column; gap: 1px; min-width: 0; cursor: pointer; }
.nastroyka__klyuch { color: var(--text-faint); font: 400 var(--t-xs)/1.4 var(--mono); }
.nastroyka__pole { grid-column: 3; width: 100%; font-variant-numeric: tabular-nums; }
.nastroyka .setting-zhdet { grid-column: 2; }
@media (max-width: 560px) {
  .nastroyka { grid-template-columns: minmax(0, 1fr); }
  .nastroyka__pole, .nastroyka .setting-zhdet { grid-column: 1; justify-self: start; }
}
.nastroyki__sohranit {
  position: sticky;
  bottom: 0;
  z-index: 5;
  display: flex;
  align-items: center;
  gap: var(--s4);
  margin-top: var(--s5);
  padding: var(--s3) 0;
  border-top: 1px solid var(--line-soft);
  background: var(--ink);
}

/* «Вопросы»: вопрос — коробкой, ответы — с кромкой, форма — подвалом. */
.vopros__tekst { white-space: pre-wrap; overflow-wrap: anywhere; }
.vopros__otvet {
  margin-top: var(--s3);
  padding: var(--s2) var(--s4);
  border-left: 2px solid var(--line);
  color: var(--text-dim);
}
.vopros__otvet p { margin: 0; white-space: pre-wrap; overflow-wrap: anywhere; }
.vopros__otvet .list__meta { margin-bottom: 2px; }
.vopros__forma { display: block; }
.vopros__otvetit { display: flex; align-items: flex-end; gap: var(--s3); margin: 0 0 var(--s2); }
.vopros__otvetit textarea { flex: 1; min-width: 0; }

/* Сведения: подпись — значение, в две колонки. */
.svedeniya {
  display: grid;
  grid-template-columns: max-content minmax(0, 1fr);
  gap: var(--s2) var(--s4);
  margin: 0;
  padding: var(--s4) var(--s5);
  font-size: var(--t-sm);
}
.svedeniya dt { color: var(--text-faint); }
.svedeniya dd { margin: 0; overflow-wrap: anywhere; }

/* ---------- Консоль песочницы ---------- */

.sandbox__bar { display: flex; align-items: center; gap: var(--s3); margin-bottom: var(--s3); }

/* Консоль выглядит как консоль: тёмное поле, моноширинный шрифт, вывод
   подряд. Это знакомая человеку картина, и она сразу говорит, что здесь
   ничего не оценивают — в отличие от блока проверки. */
.terminal {
  min-height: 120px;
  max-height: 320px;
  overflow: auto;
  padding: var(--s3) var(--s4);
  background: var(--ink);
  border: 1px solid var(--line);
  border-radius: var(--r-sm) var(--r-sm) 0 0;
  border-bottom: 0;
  color: var(--text);
  font: 400 var(--t-sm)/1.55 var(--mono, ui-monospace, monospace);
  white-space: pre-wrap;
  word-break: break-word;
}
.term-err { color: var(--fail); }
.term-tikho { color: var(--text-faint); }
/* Введённое человеком отличается от напечатанного программой: иначе в
   ленте вывода не разобрать, где чей текст. */
.term-vvod { color: var(--accent); }

.terminal__line {
  display: flex;
  align-items: center;
  gap: var(--s2);
  padding: 0 var(--s4);
  background: var(--surface-2);
  border: 1px solid var(--line);
  border-radius: 0 0 var(--r-sm) var(--r-sm);
}
.terminal__znak { color: var(--accent); font-family: var(--mono, monospace); }
.terminal__line input {
  flex: 1;
  min-width: 0;
  height: 38px;
  padding: 0;
  box-shadow: none;
  background: none;
  border: 0;
  color: var(--text);
  font: 400 var(--t-sm)/1.4 var(--mono, ui-monospace, monospace);
}
.terminal__line input:focus { outline: none; }
.terminal__line input:disabled { color: var(--text-faint); }

/* ---------- Одна панель на все ответы страницы ---------- */

/* Было четыре области вывода в разных местах — результат проверки, консоль,
   вывод запуска и разбор. Человек после нажатия не знал, куда смотреть.
   Теперь место одно, а заголовок говорит, что в нём сейчас. */
.panel {
  margin-top: var(--s4);
  border: 1px solid var(--line);
  border-radius: var(--r-md);
  background: var(--surface);
  overflow: hidden;
}
.panel__head {
  display: flex;
  align-items: center;
  gap: var(--s3);
  padding: 9px var(--s4);
  background: var(--surface-2);
  border-bottom: 1px solid var(--line);
}
.panel__title { font-size: var(--t-sm); font-weight: 500; color: var(--text); }
.panel__close {
  padding: 0 6px;
  border: 0;
  background: none;
  color: var(--text-faint);
  font-size: 18px;
  line-height: 1;
  cursor: pointer;
}
.panel__close:hover { color: var(--text); }

.panel__body { padding: var(--s4); max-height: 420px; overflow: auto; }
/* С картинкой (ТЗ §2в) — выше: прокручивать приговор с анимацией внутри
   панели неудобно, а разбор под ней человек прокрутит страницей. */
.panel__body--pokaz { max-height: none; }

/* Консоль занимает всю ширину панели и выглядит как консоль: тёмное поле,
   моноширинный шрифт, вывод подряд. Прежняя ютилась полоской справа от
   редактора — в неё не помещалось ничего длиннее трёх строк. */
.panel__body--konsol {
  min-height: 220px;
  background: var(--ink);
  color: var(--text);
  font: 400 var(--t-sm)/1.55 var(--mono, ui-monospace, monospace);
  white-space: pre-wrap;
  word-break: break-word;
}
.term-err { color: var(--fail); }
.term-tikho { color: var(--text-faint); }
/* Введённое человеком отличается от напечатанного программой: иначе в
   ленте вывода не разобрать, где чей текст. */
.term-vvod { color: var(--accent); }

.panel__line {
  display: flex;
  align-items: center;
  gap: var(--s2);
  padding: 0 var(--s4);
  border-top: 1px solid var(--line);
  background: var(--ink);
}
.panel__znak { color: var(--accent); font-family: var(--mono, monospace); }
.panel__line input {
  flex: 1;
  min-width: 0;
  height: 40px;
  padding: 0;
  box-shadow: none;
  background: none;
  border: 0;
  color: var(--text);
  font: 400 var(--t-sm)/1.4 var(--mono, ui-monospace, monospace);
}
.panel__line input:focus { outline: none; }
.panel__line input:disabled { color: var(--text-faint); }

/* Кнопка остановки отличается от остальных: она прекращает то, что уже
   идёт, и путать её с «Запустить» нельзя. */
.btn--stop { border-color: var(--fail); color: var(--fail); }

.task__actions { display: flex; flex-wrap: wrap; align-items: center; gap: var(--s3); margin-top: var(--s4); }

/* ---------- Разборы приёмов ---------- */

.hint__link { color: var(--accent); white-space: nowrap; }

.priem__body { color: var(--text-dim); line-height: 1.7; }
.priem__body h2 {
  margin: var(--s6) 0 var(--s3);
  color: var(--text);
  font-size: var(--t-lg);
}
.priem__body p { margin: 0 0 var(--s3); }
.priem__body ul, .priem__body ol { padding-left: var(--s5); margin: 0 0 var(--s3); }
.priem__body code {
  padding: 1px 5px;
  background: var(--surface-2);
  border-radius: 4px;
  font: 400 0.92em/1.4 var(--mono, ui-monospace, monospace);
}
/* Внутри блока кода фон уже есть — второй делает кашу. */
.priem__body pre {
  padding: var(--s3) var(--s4);
  background: var(--ink);
  border: 1px solid var(--line);
  border-radius: var(--r-sm);
  overflow-x: auto;
  margin: 0 0 var(--s4);
}
.priem__body pre code { padding: 0; background: none; }


/* Шаг проекта: готовое решение взятого шага и кнопка «взять готовым»
   под условием (П-12). Отступы — как у соседних блоков условия. */
.step-ready { margin: var(--s4) 0; }
.step-ready .code { margin: var(--s2) 0; }
.step-take { display: flex; flex-wrap: wrap; align-items: center; gap: var(--s3); margin: var(--s4) 0; }

/* ---------- Игры (ТЗ §2ж) ---------- */

/* Запись партии — тот же вид, что у консоли: человек должен узнать в ней
   то место, где будет играть сам. Высота постоянная, чтобы страница не
   прыгала, пока партия печатается. */
.igra__zapis { margin: var(--s5) 0; }
.igra__zapis-head { display: flex; align-items: baseline; justify-content: space-between; gap: var(--s3); }
.igra__ekran {
  height: 300px;
  margin: var(--s3) 0 0;
  padding: var(--s4);
  overflow: auto;
  border: 1px solid var(--line);
  border-radius: var(--r-md);
  background: var(--ink);
  color: var(--text);
  font: 400 var(--t-sm)/1.55 var(--mono, ui-monospace, monospace);
  white-space: pre-wrap;
  word-break: break-word;
}
.igra .task__actions { margin: 0; }
.box.igra__zapis { margin: 0; }
.box.igra__zapis .igra__zapis-head { align-items: center; }
.box.igra__zapis .igra__ekran { margin: 0; }
.igra__igrat .pole, .igra__igrat .panel { margin-top: var(--s4); }

/* ---------- Шаги проекта и игры: прогресс и список ----------
   Разметка страницы проекта с этапа 5.6 стояла без стилей вовсе: номер
   шага печатался дважды («1. 1»), полоса прогресса была невидима, отметка
   «решён» висела под названием. Замечено 28.09 на снимке страницы игры —
   у проекта то же самое, правится для обеих сразу.
   03.10: шаги — в коробке, номер — кружком, следующий шаг отмечен кромкой
   и кнопкой «Решать»: «где я остановился» видно без чтения. */
.project__count { font-size: var(--t-sm); color: var(--text-dim); white-space: nowrap; font-variant-numeric: tabular-nums; }
.steps { list-style: none; margin: 0; padding: 0; }
.step {
  display: grid;
  grid-template-columns: 30px minmax(0, 1fr) auto;
  align-items: center;
  gap: var(--s4);
  padding: 12px var(--s5);
  border-bottom: 1px solid var(--line-soft);
}
.step:last-child { border-bottom: 0; }
.step__num {
  display: grid;
  place-items: center;
  width: 30px;
  height: 30px;
  border: 1px solid var(--line);
  border-radius: 50%;
  color: var(--text-dim);
  font-size: var(--t-sm);
  font-weight: 600;
  font-variant-numeric: tabular-nums;
}
.step--solved .step__num { color: var(--ok); border-color: rgba(76, 195, 138, .45); background: rgba(76, 195, 138, .1); }
.step--unlocked .step__num { border-style: dashed; }
.step--next { background: linear-gradient(90deg, var(--accent-dim), transparent 70%); box-shadow: inset 2px 0 0 var(--accent); }
.step--next .step__num { color: var(--on-accent); border-color: var(--accent); background: var(--accent); }
.step__body { min-width: 0; }
.step__title { color: var(--text); font-weight: 500; }
.step__title:hover { color: var(--accent); }
.step__file { margin: 2px 0 0; font-family: var(--mono, monospace); font-size: var(--t-xs); }
.step__state { display: flex; align-items: center; gap: var(--s2); }
.step__state form { margin: 0; }
@media (max-width: 560px) {
  .step { grid-template-columns: 30px minmax(0, 1fr); row-gap: var(--s2); padding-left: var(--s4); padding-right: var(--s4); }
  .step__state { grid-column: 2; }
}

/* Бок страницы проекта: число готовности, полоса, сборка, ссылка. */
.gotovnost__chislo {
  margin: 0 0 var(--s3);
  font-size: 32px;
  font-weight: 700;
  letter-spacing: -0.02em;
  line-height: 1.1;
  font-variant-numeric: tabular-nums;
}
.gotovnost__chislo small { color: var(--text-dim); font-size: var(--t-md); font-weight: 500; letter-spacing: 0; }
.project__build { margin-top: var(--s5); }
.project__build .note { margin: var(--s3) 0 0; }
.gotovnost .lichnaya, .gotovnost .igra__svoe {
  margin-top: var(--s5);
  padding-top: var(--s4);
  border-top: 1px solid var(--line-soft);
}
.gotovnost .igra__svoe .lichnaya { margin-top: var(--s3); padding-top: 0; border-top: 0; }
.gotovnost .lichnaya--vklyuchit { flex-direction: column; align-items: stretch; }
.gotovnost .lichnaya--vklyuchit .btn { width: 100%; }

/* ---------- Оболочка «поле»: игры на поле (ТЗ §2ж) ----------
   Та же рамка, что у панели консоли: человек узнаёт место, где «что-то
   идёт». Поле своего цвета в обеих темах — у игры свой тёмный фон, и
   палитра из файла игры подобрана под него. */
.pole {
  margin-top: var(--s4);
  border: 1px solid var(--line);
  border-radius: var(--r-md);
  background: var(--surface);
  overflow: hidden;
}
.pole .panel__head { flex-wrap: wrap; }
.pole__telo { padding: var(--s3) var(--s4) var(--s4); }
.pole__zametka { margin: 0 0 var(--s3); }
.pole__zametka:empty { display: none; }
/* Место под холст: по ширине колонки, холст — по центру. Надпись поверх
   поля (пауза, итог, ошибка) — в той же клетке сетки, а не отдельным
   блоком: иначе поле прыгало бы вниз при каждой паузе. */
.pole__mesto {
  display: grid;
  justify-items: center;
  align-items: center;
  position: relative;
  width: 100%;
  max-width: 480px;
  margin: 0 auto;
}
.pole__mesto > * { grid-area: 1 / 1; }
.pole__holst {
  display: block;
  border-radius: var(--r-sm);
  background: #0b100d;
  touch-action: none;           /* смахивание — ход, а не прокрутка страницы */
  outline: none;
}
.pole__holst:focus-visible { box-shadow: 0 0 0 2px var(--accent); }
.pole__nadpis {
  max-width: 92%;
  max-height: 92%;
  overflow: auto;
  padding: var(--s3) var(--s4);
  border-radius: var(--r-sm);
  background: rgba(11, 16, 13, 0.88);
  color: #eef3ee;
  font-size: var(--t-sm);
  line-height: 1.5;
  text-align: center;
  white-space: pre-wrap;
}
.pole__nadpis--itog { font-size: var(--t-md); font-weight: 600; }
/* Ошибка — трассировка: моноширинно и по левому краю, как в консоли. */
.pole__nadpis--oshibka {
  color: #ffd6d3;
  font: 400 var(--t-xs)/1.5 var(--mono, ui-monospace, monospace);
  text-align: left;
}
/* Нажатая в записи клавиша — в углу поля, чтобы поворот не выглядел
   собственной волей змейки. */
.pole__klavisha {
  align-self: start;
  justify-self: end;
  margin: 6px;
  min-width: 30px;
  padding: 2px 8px;
  border-radius: var(--r-sm);
  background: var(--accent);
  color: var(--on-accent);
  font-weight: 700;
  text-align: center;
}

/* Кнопки-стрелки для телефона: крестом, крупные — под большой палец. */
.pole__knopki {
  display: grid;
  grid-template-columns: repeat(3, 56px);
  grid-template-areas: ". vverh probel" "vlevo vniz vpravo";
  gap: var(--s2);
  justify-content: center;
  margin: var(--s4) 0 var(--s2);
  user-select: none;
  -webkit-user-select: none;
}
.pole__knopka {
  height: 52px;
  border: 1px solid var(--line);
  border-radius: var(--r-md);
  background: var(--surface-2);
  color: var(--text);
  font-size: 22px;
  touch-action: manipulation;   /* без двойного касания-зума на кнопке */
}
.pole__knopka:active { background: var(--accent-dim); border-color: var(--accent); }
.pole__knopka--vverh { grid-area: vverh; }
.pole__knopka--vniz { grid-area: vniz; }
.pole__knopka--vlevo { grid-area: vlevo; }
.pole__knopka--vpravo { grid-area: vpravo; }
.pole__knopka--probel { grid-area: probel; }
/* Там, где есть мышь и клавиатура, кнопки — лишнее. */
@media (hover: hover) and (pointer: fine) {
  .pole__knopki { display: none; }
}

.pole__legenda {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: var(--s2) var(--s4);
  margin: var(--s3) 0 0;
  padding: 0;
  list-style: none;
  color: var(--text-dim);
  font-size: var(--t-sm);
}
.pole__legenda li { display: flex; align-items: center; gap: 6px; }
.pole__obrazec { width: 14px; height: 14px; border-radius: 3px; }
.pole__podskazka { margin: var(--s3) 0 0; text-align: center; }
.pole__pechat {
  max-height: 160px;
  margin: var(--s2) 0 0;
  padding: var(--s3);
  overflow: auto;
  border: 1px solid var(--line);
  border-radius: var(--r-sm);
  background: var(--ink);
  font: 400 var(--t-xs)/1.5 var(--mono, ui-monospace, monospace);
  white-space: pre-wrap;
  word-break: break-word;
}
.pole__mesto--zapis { margin-top: var(--s3); }

/* ---------- Задачи от людей (этап 7) ---------- */

/* Многострочные поля и выбор — как однострочные поля формы. До этапа 7
   textarea и select жили только в обслуживании и песочнице, со своими
   правилами. */
.field textarea,
.field select {
  width: 100%;
  padding: 10px 12px;
  background: var(--surface-2);
  border: 1px solid var(--line);
  border-radius: var(--r-sm);
  color: var(--text);
  font: 400 var(--t-md)/1.45 var(--font);
  box-shadow: var(--shadow-inset);
}
.field textarea.mono { font: 400 var(--t-sm)/1.55 var(--mono); tab-size: 4; }
.field textarea { resize: vertical; }
.field textarea:focus,
.field select:focus {
  outline: none;
  border-color: var(--accent);
  background: var(--ink);
  box-shadow: var(--shadow-inset), var(--ring);
}
.zayavka-forma { max-width: 760px; }
.zayavka-forma fieldset { border: 0; padding: 0; margin: 0; min-width: 0; }
.zayavka-forma__vybor { gap: var(--s2); }
.zayavka-forma__para { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); gap: var(--s4); }
@media (max-width: 560px) { .zayavka-forma__para { grid-template-columns: minmax(0, 1fr); } }
.zayavka-primer { margin-top: var(--s2); }
.zayavka-esche > summary { cursor: pointer; margin-top: var(--s3); color: var(--text-dim); }
.zayavka-bedy { margin-bottom: var(--s4); }
.zayavka-bedy ul { margin: var(--s2) 0 0; padding-left: 1.2em; }
.zayavka-otvet { margin: var(--s4) 0; }
/* Текст человека показывается как написан: переводы строк — его. */
.zayavka-tekst { white-space: pre-wrap; overflow-wrap: anywhere; }
.zayavka-pary { table-layout: fixed; }
.zayavka-pary th:first-child, .zayavka-pary td:first-child { width: 96px; padding-right: 4px; }
.zayavka-kod { margin: 0; white-space: pre-wrap; overflow-wrap: anywhere; }
.zayavka-reshenie { display: flex; flex-wrap: wrap; gap: var(--s5); align-items: flex-start; }
.zayavka-reshenie > form { flex: 1 1 300px; min-width: 0; }
.task-avtor { margin-top: var(--s2); }
.zayavka-etalon { margin-bottom: var(--s5); }


/* ---------- Профиль и личная страница по ссылке (этап 8, П-16, П-36) ---------- */

.profil-head__kto { display: flex; align-items: center; gap: var(--s4); min-width: 0; }
.profil-head__imya { min-width: 0; overflow-wrap: anywhere; }
.profil-head { margin-bottom: var(--s5); }
/* Буква имени в кружке: профиль узнаётся с первого взгляда как «мой». */
.avatar {
  flex: none;
  display: grid;
  place-items: center;
  width: 56px;
  height: 56px;
  border-radius: 50%;
  border: 1px solid rgba(240, 164, 60, .3);
  background: var(--accent-dim);
  color: var(--accent);
  font-size: 22px;
  font-weight: 700;
}
.profil__balans { font-size: var(--t-sm); font-weight: 600; font-variant-numeric: tabular-nums; }
.profil__privyazat { margin: var(--s3) 0 0; }
.profil__smena { margin-top: var(--s3); }
.profil__smena summary { cursor: pointer; color: var(--accent); font-size: var(--t-sm); }
.profil__smena .form { margin-top: var(--s3); }
.profil-plashka { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: var(--s3); }
.profil-plashka form { margin: 0; }

/* Сумма операции: приход — зелёным (заработано, «сошлось»), расход — тихо. */
.summa { font-weight: 600; font-variant-numeric: tabular-nums; }
.summa--plus { color: var(--ok); }
.summa--minus { color: var(--text-dim); }
.list__side .list__meta { margin-top: 0; font-size: var(--t-xs); }

/* Проект в профиле: название — полоса готовности — партия — ссылка. */
.profil-proekt { padding: var(--s4) var(--s5); border-bottom: 1px solid var(--line-soft); }
.profil-proekt:last-child { border-bottom: 0; }
.profil-proekt__verh { display: flex; align-items: center; justify-content: space-between; gap: var(--s3); }
.profil-proekt__verh .list__main { display: flex; align-items: center; gap: var(--s2); flex-wrap: wrap; }
.profil-proekt__hod { display: flex; align-items: center; gap: var(--s3); margin-top: var(--s2); }
.profil-proekt__hod .meter { flex: 1; }
.profil-proekt__schet { color: var(--text-dim); font-size: var(--t-sm); white-space: nowrap; font-variant-numeric: tabular-nums; }
.profil-proekt .lichnaya { margin-top: var(--s3); }
/* Не включённая ещё страница: кнопка и пояснение — одной строкой, а не
   кнопкой над абзацем: абзац под каждой собранной игрой делал профиль
   стеной текста. */
.lichnaya--vklyuchit { display: flex; align-items: center; gap: var(--s3) var(--s4); }
.lichnaya--vklyuchit form { margin: 0; flex: none; }
.lichnaya--vklyuchit p { margin: 0; font-size: var(--t-xs); line-height: 1.5; }
@media (max-width: 560px) { .lichnaya--vklyuchit { flex-direction: column; align-items: flex-start; } }

/* Список профиля «С прежнего сайта»: тело и правый бок. minmax(0, 1fr), а
   не 1fr: длинное название задачи иначе растягивает страницу шире
   телефона (Д-16). */
.spisok { list-style: none; margin: var(--s3) 0 0; padding: 0; }
.spisok__ryad {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  gap: var(--s3);
  align-items: baseline;
  padding: 8px 0;
  border-bottom: 1px solid var(--line-soft);
}
.spisok__ryad:last-child { border-bottom: 0; }
.spisok__telo { min-width: 0; overflow-wrap: anywhere; }
.spisok__bok { text-align: right; white-space: nowrap; font-variant-numeric: tabular-nums; }
.listat { justify-content: space-between; }

/* Разбор в списке: строка-заголовок раскрывается текстом. */
.list__item--blok { display: block; }
.razbor > summary {
  display: flex;
  flex-direction: column;
  gap: 2px;
  cursor: pointer;
  list-style: none;
}
.razbor > summary::-webkit-details-marker { display: none; }
.razbor__zag { min-width: 0; overflow-wrap: anywhere; }
.razbor__zag::before { content: "▸"; display: inline-block; width: 1em; color: var(--text-faint); transition: transform .12s ease; }
.razbor[open] .razbor__zag::before { transform: rotate(90deg); }
.razbor > summary .list__meta { margin: 0 0 0 1em; }
.razbor__tekst {
  white-space: pre-wrap;
  overflow-wrap: anywhere;
  margin-top: var(--s3);
  padding: var(--s3) var(--s4);
  border-left: 2px solid var(--line);
  background: var(--surface-2);
  border-radius: 0 var(--r-sm) var(--r-sm) 0;
  font-size: var(--t-sm);
  color: var(--text-dim);
}

.lichnaya { margin-top: var(--s3); }
.lichnaya__zag { margin: 0 0 var(--s2); font-size: var(--t-sm); font-weight: 500; }
.lichnaya__ryad { display: flex; flex-wrap: wrap; gap: var(--s2); align-items: center; }
.lichnaya__ryad form { margin: 0; }
.lichnaya__adres {
  flex: 1 1 220px;
  min-width: 0;
  font-family: var(--mono);
  font-size: var(--t-sm);
  padding: 6px 10px;
  background: var(--surface);
  color: var(--text);
  border: 1px solid var(--line);
  border-radius: var(--r-sm);
}

.partiya { display: flex; flex-wrap: wrap; gap: var(--s3); align-items: center; margin: var(--s3) 0; }
.lichnaya-str__kto { font-size: var(--t-md); }
.lichnaya-str__imya { font-weight: 600; }

/* ---------- Показ результата картинкой (ТЗ §2в, этап 5.5) ---------- */

/* Под картинкой — одна строка и «как это устроено», а не стена оговорок:
   стена убила бы ровно тот восторг, ради которого показ и сделан. */
.pokaz {
  margin: var(--s3) 0 var(--s4);
  padding: var(--s3);
  border: 1px solid var(--line);
  border-radius: var(--r-md);
  background: var(--surface-2);
  min-width: 0;
}
.pokaz__head { font-size: var(--t-sm); font-weight: 600; color: var(--text); }
.pokaz__podpis { margin: var(--s1) 0 var(--s2); font-size: var(--t-sm); color: var(--text-dim); }
.pokaz__mesto { width: 100%; min-width: 0; }
.pokaz__holst { display: block; max-width: 100%; border-radius: var(--r-sm); }
.pokaz__stroka {
  display: flex; flex-wrap: wrap; align-items: center; gap: var(--s3);
  margin-top: var(--s2); font-size: var(--t-sm); color: var(--text-dim);
}
.pokaz__legenda { display: flex; flex-wrap: wrap; gap: var(--s2) var(--s4); margin-top: var(--s2); }
.pokaz__chip { display: inline-flex; align-items: center; gap: 6px; font-size: var(--t-xs); color: var(--text-dim); }
.pokaz__chip i { width: 10px; height: 10px; border-radius: 50%; display: inline-block; }
.pokaz__obrezano { margin: var(--s2) 0 0; font-size: var(--t-xs); color: var(--text-faint); }
.pokaz__net { margin: var(--s2) 0 0; font-size: var(--t-sm); color: var(--text-dim); }

/* ---------- Карточки проектов и игр (03.10) ---------- */

.pcards {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(320px, 1fr));
  gap: var(--s4);
  margin: 0;
  padding: 0;
  list-style: none;
}
@media (max-width: 420px) { .pcards { grid-template-columns: minmax(0, 1fr); } }
.pcard {
  position: relative;
  display: flex;
  flex-direction: column;
  gap: var(--s3);
  min-width: 0;
  padding: var(--s5);
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--r-lg);
  box-shadow: var(--edge), var(--shadow);
  transition: border-color .12s ease, box-shadow .12s ease, transform .12s ease;
}
.pcard:hover { border-color: var(--text-faint); box-shadow: var(--edge), var(--shadow-lift); transform: translateY(-1px); }
.pcard--idet { border-color: rgba(240, 164, 60, .35); }
.pcard--gotov { border-color: rgba(76, 195, 138, .3); }
.pcard__verh { display: flex; align-items: center; gap: var(--s2); flex-wrap: wrap; }
.pcard__title {
  color: var(--text);
  font-size: var(--t-lg);
  font-weight: 600;
  letter-spacing: -0.015em;
  line-height: 1.3;
}
/* Ссылка — на всю карточку: попасть по ней должно быть легко. */
.pcard__title::after { content: ""; position: absolute; inset: 0; border-radius: inherit; }
.pcard:hover .pcard__title { color: var(--accent); }
.pcard__desc {
  flex: 1;
  margin: 0;
  color: var(--text-dim);
  font-size: var(--t-sm);
  line-height: 1.55;
  display: -webkit-box;
  -webkit-line-clamp: 3;
  -webkit-box-orient: vertical;
  overflow: hidden;
}
.pcard__niz {
  display: flex;
  align-items: center;
  gap: var(--s3);
  margin-top: auto;
  padding-top: var(--s3);
  border-top: 1px solid var(--line-soft);
}
.pcard__niz .meter { flex: 1; }
.pcard__schet { color: var(--text-dim); font-size: var(--t-sm); white-space: nowrap; font-variant-numeric: tabular-nums; }

/* ---- Витрина «Графика» (17.10, П-64) ----
   Карточка проекта с рамкой картинки сверху: в рамке — картинка эталона
   (static/js/grafika.js рисует её тем же pokaz.js, что страница задачи).
   Рамка — цвета холста, поэтому холст ниже рамки (ось в два ряда) не
   оставляет под собой полосы другого цвета. Высота рамки постоянная:
   карточки в ряду стоят на одной высоте, какой бы ни была картинка. */
.gcard { padding-top: var(--s4); }
.gcard__kadr {
  position: relative;
  display: flex;
  align-items: center;
  justify-content: center;
  height: 150px;
  overflow: hidden;
  border-radius: var(--r-md);
  background: var(--ink);
  border: 1px solid var(--line-soft);
}
/* holst() ставит ширину холста в пикселях на момент рисования; карточка
   сжимается вместе с окном — холст за ней, пропорции сохраняются. */
.gcard__kadr canvas { width: 100% !important; height: auto !important; max-height: 100%; display: block; }
.gcard__zaglushka { font-family: var(--mono); font-size: var(--t-sm); color: var(--text-faint); }
.gcard__kadr--narisovan .gcard__zaglushka { display: none; }
.gcard__tema { flex: 1; min-width: 0; color: var(--text-dim); font-size: var(--t-sm);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.gcard .level { flex: none; }
.gcard:hover .gcard__kadr { border-color: var(--line); }
/* На телефоне — карточка лёжа: картинка слева, название, тема и уровень
   справа, подпись показа — на странице задачи. Столбиком двадцать одна
   карточка — девять тысяч пикселей прокрутки (снимок 17.10, 390). */
@media (max-width: 520px) {
  .gcards { gap: var(--s2); }
  .gcard {
    display: grid;
    grid-template-columns: 42% minmax(0, 1fr);
    grid-template-rows: auto 1fr auto;
    column-gap: var(--s3);
    row-gap: var(--s1);
    padding: var(--s3);
  }
  .gcard .gcard__kadr { grid-column: 1; grid-row: 1 / 4; height: 104px; align-self: center; }
  .gcard .pcard__verh { display: none; }
  .gcard .pcard__title { grid-column: 2; grid-row: 1; font-size: var(--t-sm); }
  .gcard .pcard__desc { display: none; }
  .gcard .pcard__niz { grid-column: 2; grid-row: 3; padding-top: var(--s2); }
  .gcard__tema { font-size: var(--t-xs); }
}

/* ---------- Мелкие детали страниц (03.10) ---------- */

/* Секреты мастеров: карточки ниже и плотнее, чем у проектов. */
.pcards--plotno { grid-template-columns: repeat(auto-fill, minmax(280px, 1fr)); gap: var(--s3); }
.pcards--plotno .pcard { padding: var(--s4) var(--s5); gap: var(--s2); }
.pcards--plotno .pcard__title { font-size: var(--t-md); }

/* «Как это устроено» на странице заявок: шаги с номерами. */
.shagi-kak { margin: 0; padding: var(--s4) var(--s5) var(--s5) calc(var(--s5) + 1.2em); color: var(--text-dim); font-size: var(--t-sm); line-height: 1.55; }
.shagi-kak li { margin-bottom: var(--s3); padding-left: 4px; }
.shagi-kak li:last-child { margin-bottom: 0; }
.shagi-kak b { color: var(--text); font-weight: 600; }
.shagi-kak li::marker { color: var(--accent); font-weight: 700; }

.zayavka-korobka { max-width: 820px; }
.zayavka-korobka .zayavka-forma { max-width: none; }
.zayavka-bedy ul { margin: var(--s2) 0 0; padding-left: 1.2em; }
.list__item--gotovo .list__title { color: var(--text-dim); }

/* Личная страница по ссылке: кто собрал — кружком и строкой. */
.avatar--malo { width: 36px; height: 36px; font-size: var(--t-md); }
.lichnaya-str__kto { display: flex; align-items: center; gap: var(--s3); margin-top: var(--s4); }
.lichnaya-str__kto p { margin: 0; color: var(--text-dim); }
.lichnaya-str__imya { color: var(--text); font-weight: 600; }
.lichnaya-str__odna { max-width: 420px; }
.lichnaya-str__odna > .stack:empty { display: none; }

/* ---------- Обсуждение под задачей (03.10) ---------- */

.vetki { display: grid; gap: var(--s3); margin: var(--s4) 0 var(--s5); }
.vetka {
  padding: var(--s4) var(--s5);
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--r-md);
  box-shadow: var(--edge), var(--shadow);
}
.vetka__kto { display: flex; align-items: center; flex-wrap: wrap; gap: 6px; margin: 0 0 var(--s1); font-size: var(--t-sm); color: var(--text-dim); }
.vetka__kto b { color: var(--text); font-weight: 600; }
.vetka__tekst { margin: 0; white-space: pre-wrap; overflow-wrap: anywhere; }
.vetka__otvet { margin-top: var(--s3); padding: var(--s1) 0 var(--s1) var(--s4); border-left: 2px solid var(--line); }
.vetka__otvet .vetka__tekst { color: var(--text-dim); }
.vetka__forma { display: flex; align-items: flex-end; gap: var(--s2); margin: var(--s3) 0 0; padding-top: var(--s3); border-top: 1px solid var(--line-soft); }
.vetka__forma textarea { flex: 1; min-width: 0; min-height: 40px; padding: 8px 12px; font-size: var(--t-sm); }
.vopros-forma { margin-top: var(--s5); }
.vopros-forma + .muted { margin-top: var(--s2); }

/* Поддержка (12.10, П-48): переписка обращения. Слово поддержки — с чертой
   слева, как ответ под вопросом; текст человека — как написан. */
.obr-slovo { margin-top: var(--s3); }
.obr-slovo:first-child { margin-top: 0; }
.obr-slovo .list__meta { margin-bottom: 2px; }
.obr-slovo__tekst { margin: 0; white-space: pre-wrap; overflow-wrap: anywhere; }
.obr-slovo--nashe { padding: var(--s2) var(--s4); border-left: 2px solid var(--accent); }
.podderzhka-prilozhim code { overflow-wrap: anywhere; }
.obr-k-chemu span + span::before { content: " · "; }
.podderzhka-temy { margin-bottom: var(--s4); }

/* «Поддержать проект» (13.10, П-49, П-56, П-57): слева — что это (подарок,
   взамен ничего, чека нет), справа — одно поле суммы. На телефоне — одно
   под другим, сначала «что это»: прочесть его до суммы важнее. */
.podarok { display: grid; grid-template-columns: minmax(0, 3fr) minmax(0, 2fr); gap: var(--s5); align-items: start; }
.podarok__summa { max-width: 220px; font-size: var(--t-md); font-variant-numeric: tabular-nums; }
@media (max-width: 760px) { .podarok { grid-template-columns: minmax(0, 1fr); } }
.podarki__vernut { display: flex; flex-direction: column; align-items: flex-start; gap: var(--s2); margin-top: var(--s2); }

/* ---------- «Частые вопросы» (14.10, П-59) ----------
   Вопрос — строкой, ответ раскрывается: человек ищет свой вопрос, а не
   читает страницу подряд. */
.faq .box__body { padding-top: 0; padding-bottom: 0; }
.faq__v { border-top: 1px solid var(--line-soft); }
.faq__v:first-child { border-top: 0; }
.faq__v > summary {
  padding: 14px 0;
  cursor: pointer;
  font-weight: 500;
  list-style: none;
}
.faq__v > summary::-webkit-details-marker { display: none; }
.faq__v > summary::before {
  content: "+";
  display: inline-block;
  width: 1.2em;
  color: var(--text-faint);
}
.faq__v[open] > summary::before { content: "−"; }
.faq__o { padding: 0 0 var(--s4) 1.2em; color: var(--text-dim); line-height: 1.6; max-width: 72ch; }
.faq__o p { margin: 0 0 var(--s2); }
.faq__o ul { margin: 0 0 var(--s2); padding-left: 20px; }

/* ---------- «Куда дальше» (14.10, П-50, П-58) ----------
   Запись — название ссылкой, одна строка «что это», у рекламы — пометка
   мелко, но читаемо: маркировка обязана быть видна, а не спрятана. */
.dalshe-ssylka .list__main { display: block; line-height: 1.55; }
.dalshe-ssylka__nazvanie { font-weight: 600; }
.dalshe-ssylka__chto { color: var(--text-dim); }
.dalshe-ssylka__reklama {
  display: block;
  margin-top: 2px;
  color: var(--text-faint);
  font-size: var(--t-xs);
  overflow-wrap: anywhere;
}
.dalshe-ssylka--reklama { background: transparent; }
/* color-scheme: normal — иначе при тёмной теме сайта браузер подкладывает
   под рамку со «светлым» документом непрозрачный белый фон. */
.dalshe-ssylka__banner { display: block; width: 100%; max-width: 100%; border: 0; margin-top: 4px;
  color-scheme: normal; background: transparent; }
.oplata-podarok { display: block; width: 100%; margin: var(--s2) 0; }
.pakety__dlya { margin-bottom: var(--s3); }
.podarok-monet__knopki { display: flex; flex-wrap: wrap; align-items: center; gap: var(--s3) var(--s5); margin: 0; }
.dalshe-reshenie { margin: var(--s3) 0 0; padding: var(--s3) var(--s4); border: 1px solid var(--line-soft);
  border-radius: var(--r-md); line-height: 1.55; }
.dalshe-kartochka .box__body { display: flex; align-items: flex-start; justify-content: space-between; gap: var(--s4); }
.dalshe-kartochka form { margin: 0; flex: none; }
.dalshe-tema { margin: var(--s5) 0 0; padding: var(--s3) var(--s4); border: 1px solid var(--line-soft);
  border-radius: var(--r-md); background: var(--surface); }
.dalshe-razdel { display: flex; flex-wrap: wrap; gap: var(--s2); align-items: center; margin: 0; }
.dalshe-razdel input:first-of-type { flex: 1 1 140px; min-width: 0; }
.dalshe-razdel__poryadok { width: 72px; }
.dalshe-forma fieldset { border: 0; padding: 0; margin: 0; min-width: 0; gap: var(--s2); }
.dalshe-forma fieldset.dalshe-forma__partner { border: 1px dashed var(--line); border-radius: var(--r-md); padding: var(--s3) var(--s4); }
.dalshe-forma__knopki { margin-top: var(--s3); }
.dalshe-forma__kod { font-family: var(--mono); font-size: var(--t-sm); }
.dalshe-udalit { display: flex; align-items: center; gap: var(--s3); margin: 0; }


/* ------------------------------------------------------------------
   Живость (16.10, П-63)

   Слова заказчика: «сайт выглядит каким-то мёртвым — плитки, текст;
   сухой и мрачный». Главная деталь сайта — окно разбора с помеченной
   строкой — уже живая; остальные страницы говорили на другом языке:
   одинаковые коробки и серые строки. Всё ниже переносит язык разбора на
   остальное — код, номера строк, комментарии, вывод консоли, — не добавляя
   ни одного нового цвета и ни одного абзаца пояснений (П-59).
   ------------------------------------------------------------------ */

/* Появление каскадом: плитки тем, карточки проектов и игр встают по
   очереди, через 40 мс. От видимого почти сразу состояния: страница
   готова к чтению на первом кадре, а не ждёт анимацию. Порядковый номер
   — в style="--i: N" из шаблона. */
@keyframes vstat {
  from { opacity: .001; transform: translateY(6px); }
  to   { opacity: 1; transform: none; }
}
.tema, .pcards .pcard {
  animation: vstat .36s cubic-bezier(.2,.8,.3,1) both;
  animation-delay: calc(min(var(--i, 0), 12) * 40ms);
}

/* ---- Плитка темы на главной ----
   Знак темы сверху (строчка кода из topics.yaml), название и число задач,
   внизу полоса «решено из» тем же счётом, что в каталоге. Знак — тусклым
   моноширинным; при наведении загорается янтарным: это и есть «живое» —
   одна деталь, а не вся плитка. */
.temy {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(210px, 1fr));
  gap: var(--s3);
  margin: 0;
  padding: 0;
  list-style: none;
}
.tema {
  position: relative;
  display: flex;
  flex-direction: column;
  gap: var(--s4);
  min-width: 0;
  padding: var(--s5) var(--s5) var(--s4);
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--r-md);
  box-shadow: var(--edge), var(--shadow);
  transition: border-color .14s ease, box-shadow .14s ease, transform .14s ease;
}
.tema:hover { border-color: var(--text-faint); box-shadow: var(--edge), var(--shadow-lift); transform: translateY(-2px); }
.tema__znak, .pcard__znak {
  font-family: var(--mono);
  font-weight: 400;
  color: var(--text-faint);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  transition: color .14s ease;
}
.tema__znak { font-size: var(--t-xl); line-height: 1.1; }
.tema:hover .tema__znak, .pcard:hover .pcard__znak { color: var(--accent); }
.tema__title { color: var(--text); font-weight: 600; line-height: 1.3; }
/* Ссылка — на всю плитку: попасть по ней должно быть легко. */
.tema__title::after { content: ""; position: absolute; inset: 0; border-radius: inherit; }
.tema__schet { color: var(--text-faint); font-size: var(--t-sm); margin-top: 2px; }
.tema__niz { display: flex; align-items: center; gap: var(--s3); margin-top: auto; }
.tema__niz .meter { flex: 1; height: 4px; }
.tema__chislo {
  font-family: var(--mono);
  font-size: var(--t-xs);
  color: var(--text-faint);
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}
.tema--gotova .tema__chislo { color: var(--ok); }
/* На телефоне — две плитки в ряд, а не одна: двадцать плиток столбиком —
   три с половиной тысячи пикселей прокрутки до подвала (снимок 16.10).
   Знак мельче, чтобы 14 знаков влезали в половину экрана. */
@media (max-width: 520px) {
  .temy { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: var(--s2); }
  .tema { padding: var(--s4) var(--s3) var(--s3); gap: var(--s3); }
  .tema__znak { font-size: var(--t-sm); }
  .tema__title { font-size: var(--t-sm); }
}

/* Знак проекта и игры — в строке верха карточки, слева; плашки — справа. */
.pcard__znak { font-size: var(--t-lg); line-height: 1.2; margin-right: auto; min-width: 0; }
.pcard__verh { flex-wrap: nowrap; min-height: 26px; }
.pcard__verh .badge { flex: none; }

/* ---- Номер строки каталога ----
   Как поле номеров в редакторе: тусклый моноширинный, при наведении на
   строку — янтарный, вместе с её кромкой. */
.row__no {
  display: block;
  font-family: var(--mono);
  font-size: var(--t-xs);
  color: var(--text-faint);
  text-align: right;
  padding-right: 2px;
  font-variant-numeric: tabular-nums;
  transition: color .12s ease;
}
.row:hover .row__no { color: var(--accent); }

/* ---- Заголовок раздела комментарием ----
   Тусклый «#» перед заголовком раздела страницы и коробки. Читалке экрана
   он не нужен — пустая подпись после косой черты. Заголовок задачи под
   надзаголовком («Первая задача») знака не получает: это название, а не
   раздел, — поэтому :first-child. */
.section__head > h2:first-child::before,
.box__title::before {
  content: "#";
  content: "#" / "";
  font-family: var(--mono);
  font-weight: 400;
  color: var(--text-faint);
}
.section__head > h2:first-child::before { margin-right: .4em; }

/* ---- Пустое место — выводом программы ----
   «>>> igry» и «[]» (шаблон _vyvod.html), слова состояния — следом
   комментарием. Влево, а не по центру: так стоит вывод в консоли. */
.empty--vyvod { text-align: left; }
.empty--vyvod p { margin-left: 0; }
.vyvod {
  margin: 0 0 var(--s1);
  font-family: var(--mono);
  font-size: var(--t-sm);
  line-height: 1.8;
  color: var(--text);
}
.vyvod__prompt { color: var(--text-faint); }
.vyvod__otvet { color: var(--text-dim); }
.empty--vyvod .empty__title {
  margin-bottom: var(--s3);
  font-family: var(--mono);
  font-size: var(--t-sm);
  font-weight: 400;
  color: var(--text-dim);
}
.empty--vyvod .empty__title::before { content: "# "; content: "# " / ""; color: var(--text-faint); }

/* ---- Разбор на главной набирается сам (static/js/nabor.js) ----
   Пока идёт набор, помеченной строки и объяснения ещё нет: строка
   помечается после «запуска», объяснение поднимается последним. Место под
   ними держится — страница не прыгает. Без скрипта и при «меньше
   движения» разбор стоит готовым, как раньше. */
.review__code .line--marked { transition: background .4s ease, border-color .4s ease; }
.review--nabor .review__code .line--marked { border-left-color: transparent; background: transparent; }
.review__note { transition: opacity .4s ease, transform .4s ease; }
.review--nabor .review__note { opacity: 0; transform: translateY(6px); }
.nabor-kursor {
  display: inline-block;
  width: .55em;
  height: 1.15em;
  margin-left: 1px;
  vertical-align: -.2em;
  background: var(--accent);
  animation: nabor-migat 1s steps(1) infinite;
}
@keyframes nabor-migat { 50% { visibility: hidden; } }

/* ---------- Обслуживание → «Канал» (19.10, П-67…П-70) ----------
   Пост — так, как выйдет: два дня рядом, на телефоне друг под другом.
   Текст поста — с переносами строк, как во ВКонтакте. */
.alert--fail { border-color: rgba(240, 101, 90, .35); border-left-color: var(--fail); background: rgba(240, 101, 90, .07); }
.kanal-knopki { display: flex; gap: var(--s2); justify-content: flex-end; }
.kanal-dni { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: var(--s5); margin-bottom: var(--s5); }
@media (max-width: 920px) { .kanal-dni { grid-template-columns: minmax(0, 1fr); } }
.kanal-post__tekst { white-space: pre-line; overflow-wrap: anywhere; margin: 0 0 var(--s4); }
.kanal-post__kartinka { display: block; width: 100%; height: auto; border-radius: var(--r-md, 8px); border: 1px solid var(--line-soft); }
.kanal-opros { border: 1px solid var(--line); border-radius: var(--r-md, 8px); padding: var(--s3) var(--s4); }
.kanal-opros__vopros { margin: 0 0 var(--s2); font-weight: 600; }
.kanal-opros__varianty { margin: 0 0 var(--s2); padding-left: 1.4em; font-family: var(--mono); font-size: var(--t-sm); }
.kanal-opros__verno { color: var(--ok); }
.kanal-forma__polya { border: 0; padding: 0; margin: 0; display: flex; flex-direction: column; gap: var(--s4); min-width: 0; }
.kanal-forma__ryad { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: var(--s4); }
@media (max-width: 640px) { .kanal-forma__ryad { grid-template-columns: minmax(0, 1fr); } }
.kanal-forma .kanal-forma__kod { font-family: var(--mono); font-size: var(--t-sm); }
.kanal-forma fieldset { border: 0; padding: 0; margin: 0; min-width: 0; }
.kanal-forma input[type="text"], .kanal-forma textarea { width: 100%; }
.kanal-stat td { vertical-align: top; }

/* ---- «Графика»: две группы (19.10, П-71) ----
   Узоры и фракталы — первой группой, «движение и графики» — второй.
   Первая встаёт сразу под заголовком страницы: полный отступ раздела
   (s7) поверх отступа заголовка давал пустую полосу в пол-экрана. */
.page-head + .gruppa-grafiki { padding-top: var(--s2); }

