/* Наряд №140 — СВЕТЛЫЙ ФИРМЕННЫЙ СЛОЙ БЦСП ДЛЯ ЭКРАНА УЧЕНИКА.

   ЧТО ЭТО ЗА ФАЙЛ И ПОЧЕМУ ОН ОТДЕЛЬНЫЙ. Наряд №139 свёл цвета, кегли и
   радиусы в frontend/tokens.css — и сделал это ДЛЯ ВСЕХ ТРЁХ ЭКРАНОВ сразу:
   index.html (ученик), trainer.html (тренер), admin.html — общее основание у
   них одно, style.css. Значит переписать :root в tokens.css в светлую
   палитру нельзя: тем же движением перекрасились бы кабинет тренера и
   админка, у которых в trainer.css/admin.css свои тёмные литералы и которых
   наряд №140 не касается. Половина светлой темы поверх тёмных литералов —
   не «частично сделано», а сломанный экран.

   Поэтому фирменный слой подключается ТОЛЬКО в index.html и ТОЛЬКО после
   style.css. Устройство даёт три свойства, каждое из которых стоит дороже
   аккуратности «переписать один файл»:

     1. кабинет тренера и админка доказуемо не тронуты — они этот файл не
        грузят, и никакой правкой внутри него их сломать нельзя;
     2. наряд обратим одной строкой: снять <link> — вернулся тёмный №139;
     3. style.css не тронут ни строкой, поэтому все проверки наряда №139
        (в том числе «голых hex в style.css — только названные») остаются в
        силе как проверки, а не как переписанные под новый ответ.

   ПОРЯДОК. Переменные объявлены в :root и перебивают tokens.css по правилу
   «при равной специфичности выигрывает последний» — то есть подключение
   ПОСЛЕ style.css обязательно и для переменных не важно, а для правил-
   перекрытий ниже (их немного, только там, где style.css несёт литерал или
   тёмное решение по существу) важно ровно оно.

   ИСТОЧНИК ЗНАЧЕНИЙ — wiki/brand-system.md базы знаний, раздел
   «Digital-использование». Ни один оттенок здесь не придуман: всё либо из
   фирменной палитры логобука, либо из списка утилитарных нейтральных и
   сигнальных того же канона, либо уже жило в этом дереве. Правило канона
   («цветной оттенок берётся только из палитры; нужен фон, которого в палитре
   нет, — он нейтральный серый, а не осветлённая производная мятного»)
   соблюдается буквально: производных мятного вида #e8f8f5, изъятого Романом
   01.09.2026, здесь нет. */

:root {
  /* -- Поверхности ------------------------------------------------------- */
  /* brand-system.md, «Digital-использование»: фон страницы — очень светлый
     мятный, карточки и панели — белые. Три колонки экрана становятся белыми
     панелями, мятный виден там, где он и должен быть виден: под подложкой
     колонки материалов (вторичное) и вокруг карточки входа. */
  --bg:            #f0faf7;   /* фон страницы */
  --surface:       #ffffff;   /* панель, карточка */
  --surface-2:     #f0faf7;   /* утопленный элемент ВНУТРИ белой панели:
                                 поле ввода, реплика штурмана, шапка плашки */
  --surface-sunk:  #f0faf7;   /* панель вывода программы */
  --border:        #c5e9e2;   /* основная рамка и разделитель */
  --border-strong: #9ad1c7;   /* усиленная рамка */

  /* -- Текст: ровно три ступени канона ------------------------------------
     #10362e основной · #195e50 вторичный · #6aaa9a приглушённый.
     --text-dim (подписи полей, метрики, пояснения отказа — то, что ребёнок
     обязан ПРОЧИТАТЬ) взял вторичный, а не приглушённый: #6aaa9a на белом
     даёт 2,6:1 и годится для placeholder и подсказки, но не для строки,
     которую читают. Приглушённый достался --text-mute, где он и по смыслу:
     плейсхолдер, пустое состояние, «на задаче». */
  --text:      #10362e;
  --text-soft: #195e50;
  --text-dim:  #195e50;
  --text-mute: #6aaa9a;

  /* -- Акцент -------------------------------------------------------------
     ФИРМЕННЫЙ мятный #2fb295 — акцент и заголовки по канону. Но белый текст
     на нём даёт 2,7:1, то есть кнопка «Отправить» была бы фирменной и
     нечитаемой одновременно. Поэтому роли разведены, и обе внутри палитры
     логобука:
       --accent      #2fb295  — то, что НЕ несёт текста: рамка, линия, кольцо
                                фокуса, акцентная полоса карточки, активное
                                состояние;
       --accent-fill #1f856f  — заливка ПОД белым текстом (4,5:1) и цвет
                                самого текста-акцента на белом (5,9:1):
                                кнопка, пузырь ребёнка, подпись панели.
     #1f856f — «средний тёмный» той же мятно-зелёной шкалы, не производная и
     не изобретение. */
  --accent:      #2fb295;
  --accent-fill: #1f856f;
  --accent-text: #ffffff;

  /* Hover и pressed — из канона (#e0f7f2 hover-фон кнопки) и из палитры
     (#c5e9e2 — на шаг темнее, «нажато»). */
  --hover: #e0f7f2;
  --press: #c5e9e2;

  /* -- Исход попытки ------------------------------------------------------
     Подложка результата — ТОЛЬКО #9ad1c7 (самый светлый фирменный).
     Неудача подложки не получает: её несут цвет текста и знак, а фон берётся
     нейтральный #f5f5f5 из списка утилитарных канона. Так «Принято» остаётся
     единственным событием экрана, у которого есть своя плашка, — и это ровно
     то различие, которое нужно ребёнку.
     #c0392b и #b8860b — сигнальные утилитарные, уже жили в tokens.css
     (--st-needed-line/--st-check-line); палитру они не расширяют. */
  --ok:   #1f856f;   --ok-bg:   #9ad1c7;
  --fail: #c0392b;   --fail-bg: #f5f5f5;
  --warn: #b8860b;

  /* -- Продвигающее действие ----------------------------------------------
     «Дальше →», «Следующая задача →». Наряд №139 держал его цвет ОТДЕЛЬНЫМ
     от --accent намеренно: исход попытки и приглашение идти дальше — разные
     вещи. В моно-мятной палитре различие даётся не другим тоном, а другой
     ступенью той же шкалы: продвижение — яркий фирменный #2fb295 с тёмным
     текстом (4,9:1), рабочая отправка — тёмный #1f856f с белым. Ярче и
     светлее = «иди», глубже и темнее = «работай». */
  --go:      #2fb295;
  --go-text: #10362e;

  /* -- Внимание, но не тревога -------------------------------------------
     Оранжевый по-прежнему занят ровно двумя смыслами (наряд №30, шаг 4):
     непрочитанная карточка и «это форма записи, запускать нельзя». На светлом
     он остаётся ЛИНИЕЙ и ТОЧКОЙ, а фон плашки — нейтральный #f5f5f5: текст
     на оранжевом фоне читается плохо при любой яркости. */
  --attn:      #ffb020;
  --attn-bg:   #f5f5f5;
  --attn-text: #10362e;
  --attn-line: #ffb020;
  --question:  #b8860b;

  /* Подложка модального окна. Не чёрная: затемнение берётся из самого
     тёмного фирменного #10362e — на светлой странице чёрный скрим выглядит
     провалом, а не слоем. */
  --scrim: rgba(16, 54, 46, 0.32);

  /* -- Праздник: звёзды и салют отклика -----------------------------------
     Хвост №195.1, работа 12 — наряд называет цвета звёзд токенами этого
     слоя, а трёх из них и тёмно-синего подложки «Дожал» здесь не было.
     Все четыре — из логобука (wiki/brand-system.md): бирюзовый и
     синий-акцент — дополнительная палитра, тёмно-синий — конец градиента
     «стратегические», фиолетовый — тёмная ступень #8880bc для светлого фона
     (там же). Читает их только отклик: текст ими не красится. */
  --teal:   #2da1b0;
  --blue:   #2e78b2;
  --violet: #756bb1;
  --navy:   #14354f;

  /* -- Типографика --------------------------------------------------------
     Стеки, а не файлы: внешние CDN и новые файлы шрифтов наряд запрещает, а
     «если шрифт реально есть локально» — это решение, которое обязан
     принимать браузер на конкретной машине, не автор CSS. Фирменные имена
     стоят первыми и подхватываются сами, как только шрифты установят;
     дальше — ровно тот fallback, который называет наряд.
     ⚠️ На машине Романа 05.09.2026 не установлено ни одного из них
     (проверено по ~/Library/Fonts и /Library/Fonts) — фактически заголовки
     отдаются Helvetica Bold, текст Arial. */
  --font-h:    "Lemon Milk", "LemonMilk", "Lemon/Milk", "Montserrat",
               "Helvetica Neue", Helvetica, "Arial Black", Arial, sans-serif;
  --font-body: "Return to Classic", "ReturnToClassic", "Open Sans", "PT Sans",
               Arial, Helvetica, sans-serif;
  --font-mono: "SF Mono", ui-monospace, Menlo, Consolas, monospace;
  --fw-h: 700;

  /* -- Движение -----------------------------------------------------------
     Сквозной параметр easing (справочник UX-приёмов, раздел «Сквозной
     параметр»): от него зависит, ощущается движение естественным или
     дёрганым. Одна кривая и одна длительность на весь экран — 140 мс, ниже
     порога, на котором переход начинают ЖДАТЬ. */
  --ease: cubic-bezier(0.2, 0, 0.2, 1);
  --dur:  140ms;

  /* Кольцо фокуса — то же устройство наряда №139, но внутренний контур взят
     от белой панели, а не от мятного фона страницы: почти все интерактивные
     элементы экрана стоят на белом. */
  --focus: 0 0 0 2px var(--surface), 0 0 0 4px var(--accent);
}

/* == 1. Основание страницы ================================================= */

body {
  font-family: var(--font-body);
  /* Мятный фон страницы виден там, где панель его не закрывает: под колонкой
     материалов, вокруг карточки входа, в жёлобе первого кадра. */
  background: var(--bg);
  color: var(--text);
}

/* Три колонки — белые панели на мятной странице. Геометрия наряда №139 не
   тронута ни на пиксель: ни ширин, ни полов, ни потолков, ни отступов —
   только цвет подложки. */
.left-panel,
.workspace,
.chat {
  background: var(--surface);
}

/* Колонка материалов — мятная подложка под белыми плашками: «архив
   материалов и второстепенные панели вторичны» получает признак раньше, чем
   ребёнок прочитает заголовок. Условие задачи остаётся на белом. */
.cards-column {
  background: var(--bg);
}

/* ⚠️ Правило «пустая колонка не получает мятной подложки» (наряд №140, по
   снимку состояния «занятие недоступно») СНЯТО хвостом №140.2: колонка
   непуста по построению — разделы конспекта и словаря в ней постоянны. Само
   правило переехало в раздел 11 в виде, который остаётся верным. */

/* == 2. Заголовки и подписи ===============================================
   CAPS — только короткие статусные и навигационные подписи («УСЛОВИЕ
   ЗАДАЧИ», «ТВОЯ ПРОГРАММА», «ВЫВОД ПРОГРАММЫ», группы словарика). Условие
   задачи, реплики штурмана и конспект остаются нормальным регистром: это
   учебный текст, и CAPS его читаемость только рушит. */

.panel-label,
.editor-label,
.output-panel-header,
.glossary-group-title {
  font-family: var(--font-h);
  font-weight: var(--fw-h);
  color: var(--accent-fill);
}

/* Заголовки внутри учебного текста — фирменным шрифтом, но НЕ капителью и
   НЕ мятным: #2fb295 на белом даёт 2,7:1, а заголовок условия ребёнок
   читает, а не опознаёт. */
/* Заголовок условия. Межстрочный интервал свой: .statement несёт 1,55 для
   связного текста, и на кегле заголовка это давало 50 px между двумя
   строками одного названия задачи (поймано снимком, проба 5). */
.statement h1 { line-height: 1.2; margin: 0 0 var(--s4); }
.statement h2, .statement h3 { line-height: 1.3; }

.statement h1,
.statement h2,
.statement h3,
.card-tile-body h1,
.card-tile-body h2,
.card-tile-body h3,
.chat-message h1,
.chat-message h2,
.chat-message h3 {
  font-family: var(--font-h);
  font-weight: var(--fw-h);
  text-transform: none;
  color: var(--text);
}

/* Хвост №140.2: нижний отступ условия ужат с 24 до 12 px. Сверху его всё
   равно занимает липкая подпись «Условие задачи», а внизу 24 px пустого белого
   стоят ровно столько же, сколько строка текста, — и уходят из нулевого
   бюджета D16+D14. */
.statement { padding-bottom: var(--s3); }

/* Подпись «Условие задачи» липкая (наряд №139, работа 2) — её фон обязан
   совпадать с фоном ПАНЕЛИ, а не страницы: с наряда №140 условие стоит на
   белом, и мятная плашка при прокрутке выглядела бы чужой строкой. */
.statement > .panel-label {
  background: var(--surface);
  /* Фирменная линия под липкой подписью — 1 px рамки плюс 1 px тени, а НЕ
     2 px рамки. Разница ровно в том, что тень не занимает места в раскладке.
     Прибор наряда №139 показал, почему это важно: бюджет объединённой нормы
     D16+D14 на 1366×768 исчерпан ПОЛНОСТЬЮ — колонка карточек 162 px плюс
     условие 606 px это ровно 768 px высоты окна, запаса ноль. Второй пиксель
     рамки давал panel_obrezano_px = 1 и валил норму. Подпись липкая и несёт
     z-index: 1, поэтому тень рисуется поверх текста ниже, а не под ним. */
  border-bottom: 1px solid var(--accent);
  box-shadow: 0 1px 0 0 var(--accent);
}

/* Шапка колонки чата и разделители — фирменная линия. */
.session-info,
.chat-form,
.v5-formal-check {
  background: var(--surface);
}

/* == 3. Вход и стартовое состояние ========================================= */

/* Форма входа становится белой карточкой на мятной странице, с фирменной
   акцентной полосой сверху (brand-system.md, «Digital-использование»:
   «Верхний акцент карточки — #2fb295, 3px border-top»). Ширина формы (280 px)
   и её раскладка наряда №139 не тронуты — карточка обёрнута вокруг них
   отступами самой формы. */
.login form {
  align-items: stretch;
  width: 320px;
  padding: var(--s8) var(--s6) var(--s6);
  background: var(--surface);
  border: 1px solid var(--border);
  border-top: 3px solid var(--accent);
  border-radius: var(--r-md);
}

/* Логотип — только здесь и только канонический актив (PNG без фона из
   реестра бренд-активов). Размер скромный намеренно: это метка, чей это
   тренажёр, а не заглавие экрана. */
.login-logo {
  align-self: center;
  width: 88px;
  height: auto;
  margin-bottom: var(--s2);
}

.login h1 {
  align-self: center;
  font-family: var(--font-h);
  font-weight: var(--fw-h);
  /* CAPS уместен ровно здесь: одно короткое слово в роли заголовка экрана
     (правило наряда: капитель — коротким навигационным и статусным
     заголовкам, не учебному тексту). */
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--accent-fill);
}

.login label {
  font-size: var(--fs-sec);
  color: var(--text-dim);
}

.login input {
  background: var(--surface);
  border: 1px solid var(--border-strong);
  color: var(--text);
  transition: border-color var(--dur) var(--ease),
              box-shadow var(--dur) var(--ease);
}

.login input::placeholder { color: var(--text-soft); }

.login input:hover { border-color: var(--accent); }

/* Признак «поле не принято» — не только цвет: рамка вдвое толще, и она же
   служит тем, кто цвета не различает. Ставит его pokazatOshibkuVhoda
   (app.js) вместе с текстом сообщения, снимает первым же вводом. */
.login input[aria-invalid="true"] {
  border-color: var(--fail);
  border-width: 2px;
}

/* Подсказка «код даёт тренер» — приглушённая: она объясняет, а не требует
   действия. */
.login-podskazka {
  margin: 0;
  font-size: var(--fs-label);
  line-height: 1.45;
  color: var(--text-soft);
}

/* Сообщение о неудачном входе. Знак + цвет + отдельная подложка: три
   носителя смысла вместо одного красного текста (тот же приём, что у
   вердикта попытки, наряд №139, работа 4). Нейтральный #f5f5f5 под ним, а не
   светло-красный: подложка результата в этом интерфейсе одна и мятная. */
.login-error {
  font-size: var(--fs-sec);
  line-height: 1.45;
  color: var(--fail);
}

.login-error:not(:empty) {
  display: flex;
  gap: 6px;
  padding: var(--s2) 10px;
  border-radius: var(--r-sm);
  background: var(--fail-bg);
  border-left: 3px solid var(--fail);
}

.login-error:not(:empty)::before {
  content: "⚠";
  font-weight: bold;
  flex: 0 0 auto;
}

/* -- Первый кадр: скелет крупных областей --------------------------------- */

.pervyy-kadr {
  flex-direction: column;
  gap: var(--s6);
  background: var(--bg);
}

.pervyy-kadr-tekst {
  color: var(--text-dim);
  font-size: var(--fs-body);
}

/* Пропорции колонок скелета — те же, что у настоящей раскладки (левое поле
   уже, рабочая область шире, чат замыкает), чтобы кадр обещал ровно то, что
   приедет. Размеры фиксированные: скелет не участвует в раскладке занятия и
   не обязан её повторять пиксельно. */
.skelet {
  display: flex;
  gap: var(--s3);
  width: min(86vw, 1100px);
  height: min(52vh, 380px);
}

.skelet-kolonka {
  display: flex;
  flex-direction: column;
  gap: var(--s3);
  padding: var(--s3);
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--r-md);
}

.skelet-kolonka-materialy { flex: 0 0 26%; }
.skelet-kolonka-rabota    { flex: 1 1 auto; }
.skelet-kolonka-chat      { flex: 0 0 26%; }

/* Заготовки — фирменной светлой мятной ступени, не серые: страница уже
   светло-мятная, и серый на ней читается как «выключено», а не как
   «загружается». */
.skelet-blok {
  background: var(--hover);
  border-radius: var(--r-sm);
}

.skelet-plashka { height: 40px; flex: 0 0 auto; }
.skelet-stroka  { height: 12px; flex: 0 0 auto; }
.skelet-stroka-korotkaya { width: 62%; }
.skelet-redaktor { flex: 1 1 auto; min-height: 80px; }
.skelet-panel    { height: 56px; flex: 0 0 auto; }
.skelet-puzyr    { height: 64px; flex: 0 0 auto; }
.skelet-puzyr-korotkiy { height: 44px; width: 78%; }

/* Один спокойный проход волны по всем заготовкам. Не stagger и не
   scroll-reveal: движение здесь несёт смысл «идёт загрузка», а не украшает.
   1,4 с — заметно, но не суетливо. */
.skelet-blok {
  /* Светлая точка волны — не белая, а мятный фон страницы: с белой
     серединой заготовка на белой панели исчезала целиком (поймано снимком
     первого кадра, проба 2), и «загружается» превращалось в «пусто». */
  background-image: linear-gradient(90deg,
    var(--hover) 0%, var(--bg) 50%, var(--hover) 100%);
  background-size: 220% 100%;
  animation: skelet-volna 1.4s var(--ease) infinite;
}

@keyframes skelet-volna {
  0%   { background-position: 120% 0; }
  100% { background-position: -120% 0; }
}

/* -- Загрузка кнопки ------------------------------------------------------ */

/* Спиннер приезжает ПОСЛЕ текста кнопки, а не вместо него: подменённый текст
   у ребёнка отнимает единственное напоминание, что он нажал. Ставит атрибут
   app.js::pometitZagruzku, состояний в JS от него не зависит ни одно. */
button[data-zagruzka] {
  position: relative;
  cursor: progress;
}

button[data-zagruzka]::after {
  content: "";
  display: inline-block;
  width: 12px;
  height: 12px;
  margin-left: var(--s2);
  vertical-align: -2px;
  border: 2px solid currentColor;
  border-top-color: transparent;
  border-radius: 50%;
  animation: knopka-spinner 720ms linear infinite;
}

@keyframes knopka-spinner {
  to { transform: rotate(360deg); }
}

/* Выключенная кнопка со спиннером не должна ещё и гаснуть до неразличимости:
   пока она работает, её видно. */
button[data-zagruzka]:disabled { opacity: 1; }

/* prefers-reduced-motion: движение заменяется статикой, а не исчезает вовсе —
   иначе и скелет, и спиннер потеряли бы единственный признак «идёт». */
@media (prefers-reduced-motion: reduce) {
  .skelet-blok {
    animation: none;
    background-image: none;
    background: var(--hover);
  }
  button[data-zagruzka]::after {
    animation: none;
    border-top-color: currentColor;
    border-radius: 2px;
    width: 8px;
    height: 8px;
  }
}

/* == 4. Состояния интерактивных элементов =================================
   Справочник UX-приёмов, Feedback → Hover-эффект: «элемент оживает и
   подсказывает, что с ним можно взаимодействовать». До наряда №140 hover был
   ровно у ДВУХ элементов детского экрана из двух десятков (кнопка подсказки
   прокрутки и кнопка варианта ответа) — остальные не отвечали на курсор
   ничем, и «на что здесь можно нажать» ребёнок узнавал пробой.

   Ripple, like-burst и прочий декоративный отклик наряд запрещает и они не
   заведены: здесь ровно четыре состояния на элемент — покой, наведение,
   нажатие, недоступность, — и все четыре несут смысл.

   Три роли кнопок и почему их три:
     · РАБОЧАЯ (--accent-fill, белый текст) — «Проверить решение», «Войти»,
       кнопка модалки. Глубокий тон: это то, чем ребёнок работает. Наряд №142,
       работа 5 вывел отсюда кнопку чата: рабочих действий на экране обязано
       быть ОДНО, а их было два — обе с подписью «Отправить», обе этой
       заливкой, и чатовая вдобавок выше (63 px против 44), то есть заметнее
       той, что закрывает задачу;
     · ПРОДВИГАЮЩАЯ (--go, тёмный текст) — «Дальше →», «Следующая задача →».
       Яркий фирменный мятный: приглашение идти вперёд;
     · ВТОРИЧНАЯ (белая с мятной рамкой) — «Отправить сообщение», «Позвать
       тренера», «Отложить», «Пауза». Читается кнопкой, но по весу не спорит
       с двумя первыми;
     · ТИХАЯ (только текст) — «Завершить тренировку». Хвост №142.1, работа 3
       вывел её из вторичной роли: она встала в шапке рядом с «Позвать
       тренера», и две одинаковые кнопки подряд заставляли бы ребёнка
       ВЫБИРАТЬ между «мне нужна помощь» и «я ухожу» — выбор, которого у него
       нет. Одной он пользуется по ходу занятия, вторую нажимает один раз в
       конце. Ни заливки, ни рамки; защита `confirm()` перед выходом (наряд
       №71) на месте и от вида не зависит.
   Роль элемента ни одна из этих строк не меняет — только его вид. */

/* -- Рабочая ------------------------------------------------------------- */
.login button,
#submit-btn,
.modal-buttons button,
.sostoyanie button {
  background: var(--accent-fill);
  color: var(--accent-text);
  transition: background-color var(--dur) var(--ease),
              transform var(--dur) var(--ease);
}

.login button:hover:not(:disabled),
#submit-btn:hover:not(:disabled),
.modal-buttons button:hover:not(:disabled),
.sostoyanie button:hover:not(:disabled) {
  background: var(--text-soft);
}

.login button:active:not(:disabled),
#submit-btn:active:not(:disabled),
.modal-buttons button:active:not(:disabled),
.sostoyanie button:active:not(:disabled) {
  background: var(--text);
  transform: translateY(1px);
}

/* Недоступная кнопка — светлая мятная плашка с приглушённым текстом, не
   прозрачная. `opacity` до наряда №140 гасила и текст: «Отправить» в момент
   ожидания вердикта читалось хуже, чем фон под ним. */
#submit-btn:disabled,
.login button:disabled {
  background: var(--press);
  color: var(--text-soft);
  opacity: 1;
}

/* -- Продвигающая -------------------------------------------------------- */
#material-next-btn,
#scenario-advance-btn,
#next-task-btn,
#v5-next-task-btn,
#v5-next-topic-btn {
  background: var(--go);
  color: var(--go-text);
  font-weight: 600;
  transition: background-color var(--dur) var(--ease),
              transform var(--dur) var(--ease);
}

#material-next-btn:hover:not(:disabled),
#scenario-advance-btn:hover:not(:disabled),
#next-task-btn:hover:not(:disabled),
#v5-next-task-btn:hover:not(:disabled),
#v5-next-topic-btn:hover:not(:disabled) {
  background: #5abba7;   /* «средний светлый» той же фирменной шкалы */
}

#material-next-btn:active:not(:disabled),
#scenario-advance-btn:active:not(:disabled),
#next-task-btn:active:not(:disabled),
#v5-next-task-btn:active:not(:disabled),
#v5-next-topic-btn:active:not(:disabled) {
  background: var(--accent-fill);
  color: var(--accent-text);
  transform: translateY(1px);
}

#material-next-btn:disabled,
#scenario-advance-btn:disabled,
#v5-next-task-btn:disabled,
#v5-next-topic-btn:disabled {
  background: var(--press);
  color: var(--text-soft);
  opacity: 1;
}

/* -- Тихая (хвост №142.1) ------------------------------------------------ */
.logout-btn {
  background: none;
  color: var(--text-dim);
  border: none;
  transition: background-color var(--dur) var(--ease), color var(--dur) var(--ease);
}

.logout-btn:hover:not(:disabled) {
  color: var(--text);
  background: var(--hover);
}

.logout-btn:active:not(:disabled) {
  color: var(--text);
  background: var(--press);
}

.logout-btn:disabled {
  color: var(--text-soft);
  opacity: 1;
}

/* -- Вторичная ----------------------------------------------------------- */
#call-trainer-btn,
.chat-form button,
#abandon-btn,
#pause-btn,
.v5-formal-check-options button {
  background: var(--surface);
  color: var(--text);
  border: 1px solid var(--border-strong);
  transition: background-color var(--dur) var(--ease),
              border-color var(--dur) var(--ease),
              transform var(--dur) var(--ease);
}

#call-trainer-btn:hover:not(:disabled),
.chat-form button:hover:not(:disabled),
#abandon-btn:hover:not(:disabled),
#pause-btn:hover:not(:disabled),
.v5-formal-check-options button:hover:not(:disabled) {
  background: var(--hover);
  border-color: var(--accent);
}

#call-trainer-btn:active:not(:disabled),
.chat-form button:active:not(:disabled),
#abandon-btn:active:not(:disabled),
#pause-btn:active:not(:disabled),
.v5-formal-check-options button:active:not(:disabled) {
  background: var(--press);
  transform: translateY(1px);
}

#call-trainer-btn:disabled,
#abandon-btn:disabled,
#pause-btn:disabled,
.v5-formal-check-options button:disabled {
  background: var(--surface-2);
  color: var(--text-soft);
  border-color: var(--border);
  opacity: 1;
}

/* «Позвать тренера» — ВТОРИЧНОЕ действие (постановка хвоста №140.2, дефект 6).
   Наряд №140 дал ему рамку в 2 px и полужирный текст, чтобы «вызов человека
   не выглядел служебной кнопкой». На светлом экране это вышло вторым главным
   действием: единственная кнопка чата с толстой фирменной рамкой читалась
   призывом нажать её. Вес снят до вторичного — обычная рамка, обычный текст;
   кнопка остаётся детской по высоте (44 px, style.css) и находится там же,
   где ребёнок привык её искать. Заметность ей не нужна: она нужна ровно
   тогда, когда ребёнок уже решил позвать человека. */
#call-trainer-btn {
  border-width: 1px;
  border-color: var(--border-strong);
  font-weight: normal;
}

/* Кнопка подсказки прокрутки (наряд №30, шаг 4) — нейтральной она была
   потому, что оранжевый занят предупреждением; на светлом «нейтральный» это
   белый круг с мятной рамкой, а не серая заливка. */
.overflow-hint {
  background: var(--surface);
  border-color: var(--border-strong);
  color: var(--accent-fill);
  transition: background-color var(--dur) var(--ease),
              border-color var(--dur) var(--ease);
}

.overflow-hint:hover,
.overflow-hint:focus-visible {
  background: var(--hover);
  border-color: var(--accent);
  color: var(--accent-fill);
}

/* Поле ввода реплики штурману. Утопленное на белой панели, с фирменной
   рамкой в фокусе — это второй по частоте орган управления экрана после
   редактора. */
.chat-form textarea {
  background: var(--surface);
  border: 1px solid var(--border-strong);
  color: var(--text);
  transition: border-color var(--dur) var(--ease);
}

.chat-form textarea::placeholder { color: var(--text-soft); }
.chat-form textarea:hover { border-color: var(--accent); }

/* == 5. Остатки тёмного решения ============================================
   Всё, что style.css несёт литералом или тёмным решением по существу.
   Список закрытый: пять hex-литералов (их перечисляет и сторожит проверка
   наряда №139) плюс четыре rgba. Литералы в style.css НЕ правятся — они
   перекрываются здесь, потому что trainer.html и admin.html грузят тот же
   style.css и должны остаться тёмными. */

/* Хвост №142.1, работа 2: здесь стояли фирменные цвета `.v5-path-indicator`
   — прибора наряда №107, который наряд №140 приглушил до тихого чипа. Хвост
   снял сам прибор с детского экрана (причина — в index.html: красную плашку
   «откат на легаси» видел любой ученик вне allowlist, то есть большинство), и
   правила ушли вместе с ним. Наблюдение продолжает считаться и уходить в
   журнал; на экране ребёнка внутреннего диагноза больше нет. */

/* Полоса прокрутки. Постоянная и видимая — устройство наряда №30 (в macOS
   системные скроллбары overlay и невидимы, пока не крутишь) остаётся, меняются
   только цвета: тёмный ползунок #5a5a5a на светлой панели читался грязью. */
.left-panel::-webkit-scrollbar-track,
.cards-column::-webkit-scrollbar-track,
.card-tile-body::-webkit-scrollbar-track {
  background: var(--surface-2);
}

.left-panel::-webkit-scrollbar-thumb,
.cards-column::-webkit-scrollbar-thumb,
.card-tile-body::-webkit-scrollbar-thumb {
  background: var(--border-strong);
}

/* Затухание у нижней кромки прокручиваемой области (наряд №30, шаг 3).
   `transparent` вместо rgba тёмного фона: движки интерполируют градиент в
   premultiplied-alpha, серой каймы на светлом не даёт. */
.overflow-fade {
  background: linear-gradient(to bottom, transparent, var(--surface) 90%);
}

.left-panel > .overflow-fade,
.cards-column > .overflow-fade {
  background: linear-gradient(to bottom, transparent, var(--bg) 90%);
}

/* Модальное окно. Скрим не чёрный: на светлой странице чёрное затемнение
   читается провалом, а не слоем поверх. */
.modal-overlay { background: var(--scrim); }

.modal {
  background: var(--surface);
  border: 1px solid var(--border);
  border-top: 3px solid var(--accent);
  box-shadow: 0 10px 30px rgba(16, 54, 46, 0.14);
}

/* -- Наряд №195, работа 5: экран итогов темы и перевод тренером ------------ */
/* Хвост №195.1, работа 9 — цвета и шрифты по блоку 3 черновика-эталона,
   только токены этого файла (белый кружка — --surface). */
.itogi-temy-modal {
  background: var(--surface);
  border: 1px solid var(--border);
  border-top: 4px solid var(--accent);
  box-shadow: 0 12px 40px rgba(16, 54, 46, 0.25);
  color: var(--text);
}

/* Хвост №195.1, работа 11 п. 5 — экран под итогами приглушён, как в
   эталоне (разметка блока 3: saturate(.6) opacity(.55)); слой поверх —
   .modal-overlay с --scrim, тот же rgba(16,54,46,.32), что у эталона.
   Без перехода — при «уменьшить движение» ничего не меняется. */
#trainer-screen.pod-itogami { filter: saturate(0.6) opacity(0.55); }

.itogi-temy-nadzagolovok { color: var(--text-soft); }

.itogi-temy-zagolovok {
  font-family: var(--font-h);
  font-weight: var(--fw-h);
  color: var(--text);
}

.itogi-temy-zagolovok small {
  font-family: var(--font-h);
  font-weight: var(--fw-h);
  color: var(--accent-fill);
}

/* Линейка тем раздела — состояния различаются формой, не только цветом:
   подтверждена — кружок залит с галочкой, пройдена — залит наполовину,
   впереди — пустой, закрытая сейчас — с кольцом и жирной подписью. */
.itogi-temy-put-item::before { background: var(--border-strong); }
.itogi-temy-put-item.osvoena::before,
.itogi-temy-put-item.proydena::before { background: var(--accent-fill); }

.itogi-temy-put-tochka {
  background: var(--surface);
  border-color: var(--border-strong);
}
.itogi-temy-put-item.osvoena .itogi-temy-put-tochka {
  background: var(--accent-fill);
  border-color: var(--accent-fill);
}
.itogi-temy-put-item.osvoena .itogi-temy-put-tochka::after { border-color: var(--surface); }
.itogi-temy-put-item.proydena .itogi-temy-put-tochka {
  background: linear-gradient(90deg, var(--accent-fill) 50%, var(--surface) 50%);
  border-color: var(--accent-fill);
}
.itogi-temy-put-item.sejchas .itogi-temy-put-tochka {
  box-shadow: 0 0 0 4px var(--hover);
  /* Вспышка появления закрытой темы (эталон — lightUp). Анимация только на
     кружке: кнопка «Вперёд» нажимается во время вспышки. */
  animation: itogiVspyshka 900ms var(--ease) 300ms both;
}
@keyframes itogiVspyshka {
  0%   { transform: scale(0.6); }
  60%  { transform: scale(1.25); }
  100% { transform: scale(1); }
}
@media (prefers-reduced-motion: reduce) {
  .itogi-temy-put-item.sejchas .itogi-temy-put-tochka { animation: none; }
}

/* Хвост №195.1, работа 13 — на границе темы итоги поднимаются сами, на
   середине салюта (образец, кнопка «4б»): слой проявляется за 250 мс,
   карточка поднимается снизу на 24 px за 300 мс, экран под ней гаснет тем
   же темпом. При «уменьшить движение» — ни сдвига, ни перехода: карточка
   просто встаёт (app.js откладывает её на 300 мс без салюта). */
@keyframes itogiProyavlenie {
  from { opacity: 0; }
  to   { opacity: 1; }
}
@keyframes itogiPodem {
  from { transform: translateY(24px); }
  to   { transform: none; }
}
#itogi-temy-overlay.podem { animation: itogiProyavlenie 250ms var(--ease) both; }
#itogi-temy-overlay.podem .itogi-temy-modal { animation: itogiPodem 300ms var(--ease) both; }
@media (prefers-reduced-motion: no-preference) {
  #trainer-screen { transition: filter 250ms var(--ease); }
}
@media (prefers-reduced-motion: reduce) {
  #itogi-temy-overlay.podem,
  #itogi-temy-overlay.podem .itogi-temy-modal { animation: none; }
}

.itogi-temy-put-podpis { color: var(--text-soft); }
.itogi-temy-put-item.sejchas .itogi-temy-put-podpis { color: var(--text); }

.itogi-temy-put-skryto {
  background: var(--surface-2);
  border: 1px solid var(--border);
  color: var(--text-soft);
}

.itogi-temy-fakt {
  background: var(--surface-2);
  border-color: var(--border);
}
.itogi-temy-fakt-chislo {
  font-family: var(--font-h);
  font-weight: var(--fw-h);
  color: var(--text);
}
.itogi-temy-fakt-tekst { color: var(--text-soft); }

.itogi-temy-fraza { color: var(--accent-fill); font-weight: 600; }
.itogi-temy-ne-hvatilo { color: var(--text-dim); }

#itogi-temy-vpered-btn {
  background: var(--go);
  color: var(--go-text);
  transition: filter var(--dur) var(--ease);
}
#itogi-temy-vpered-btn:hover { filter: brightness(1.05); }

.itogi-temy-trener { color: var(--text-soft); }

.tema-perevod-nazvanie {
  font-family: var(--font-h);
  font-weight: var(--fw-h);
  color: var(--accent-fill);
}

/* == 6. Лента разговора ==================================================== */

/* Реплика штурмана — утопленная плашка на белой панели. Рамка обязательна:
   #f0faf7 на #ffffff различим, но границы пузыря без неё не видно, и три
   реплики подряд читаются одним полотном (поймано снимком, проба 1). */
.chat-message-agent {
  background: var(--surface-2);
  border: 1px solid var(--border);
}

/* Реплика ребёнка — фирменной заливкой под белым текстом. */
.chat-message-student {
  background: var(--accent-fill);
  color: var(--accent-text);
}

/* Служебное объявление («Ещё кусок в конспект») — тише содержательной
   реплики, как и было задумано нарядом №19: пунктир и приглушённый цвет. */
.chat-message-service {
  border-color: var(--border-strong);
  color: var(--text-soft);
}

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

   Наряд №144, работа 3. Одной рамки оказалось мало по построению: реплика
   штурмана с наряда №144 идёт во всю ширину ленты (работа 1), и рамка в
   1–2 px по её периметру — тонкий контур длиной в полколонки, который глаз
   читает как границу блока, а не как «сейчас твой ход». Иерархию создаёт
   КРОМКА: широкая полоса слева даёт вопросу вес, которого нет ни у одной
   другой реплики, и читается она с расстояния, с какого рамку уже не
   отличить. Заливка вопроса при этом остаётся светлой — на ней ребёнок
   пишет ответ глазами, и цветной фон под текстом только мешал бы. */
/* СЕЛЕКТОР ДВОЙНОЙ НАМЕРЕННО. Раздел 13 этого же файла объявляет
   `.chat-message-agent { border: 1px solid var(--border) }` НИЖЕ по файлу, и
   при равной специфичности он выигрывал у правила вопроса по одному лишь
   порядку — кромка приезжала 1 px вместо 6 (поймано браузерным прибором
   наряда №144, чтением файла не видно вовсе). `.chat-message` в селекторе
   поднимает вес до 0,2,0 и снимает зависимость от порядка разделов: правило
   вопроса перестаёт зависеть от того, где в файле окажется следующее
   правило реплики штурмана. */
.chat-message.chat-message-question {
  border: 2px solid var(--question);
  border-left: 6px solid var(--question);
  background: var(--surface);
}

/* Метка называет ХОД, а не жанр. «Вопрос:» — имя того, что штурман сделал;
   «ТВОЙ ХОД» — имя того, что теперь должен сделать ребёнок, и это разные
   сообщения. Тот же приём, что у «Проверить решение» вместо «Отправить». */
.chat-message-question-badge {
  color: var(--question);
  font-weight: 700;
}

/* КРАТКИЙ АКЦЕНТ НОВОГО ВОПРОСА — ровно две пульсации кромки, дальше тишина.
   Постановка работы 3 запрещает постоянное мигание, и запрет здесь не
   эстетический: непрерывное движение в поле зрения ребёнка, который читает
   вопрос и думает над ответом, отнимает у него то самое внимание, ради
   которого акцент и ставился.

   Пульсирует ТЕНЬ, а не размер и не положение: сдвиг двигал бы текст,
   который ребёнок в этот момент читает. Класс снимается по окончании
   анимации (app.js), поэтому «новым» вопрос остаётся ограниченное время, а
   не до конца занятия. */
.chat-message-question.novyy-vopros {
  animation: bcsp-novyy-vopros 900ms var(--ease) 2;
}

@keyframes bcsp-novyy-vopros {
  0%   { box-shadow: 0 0 0 0 rgba(184, 134, 11, 0); }
  45%  { box-shadow: 0 0 0 4px rgba(184, 134, 11, 0.28); }
  100% { box-shadow: 0 0 0 0 rgba(184, 134, 11, 0); }
}

/* Каркас программы («это форма записи, запускать нельзя»). Оранжевый остаётся
   ЛИНИЕЙ, подложка плашки — нейтральная: текст на оранжевом фоне читается
   плохо при любой яркости, а смысл несёт именно линия. */
.code-form-wrap { border: 1px dashed var(--attn-line); }
.code-form-badge {
  background: var(--attn-bg);
  color: var(--attn-text);
  border-bottom: 1px solid var(--attn-line);
}

/* Индикатор ожидания ответа штурмана — «Читаю твой вопрос…». opacity 0.6 на
   светлом делала его почти невидимым; приглушённый цвет и курсив ту же роль
   выполняют, не уводя строку в фон. */
.chat-message-thinking {
  opacity: 1;
  color: var(--text-soft);
}

/* Подсказка пустой ленты. */
.chat-messages:empty::before { color: var(--text-soft); }

/* Позывной ученика — ответ на «под кем я вошёл» (наряд №41; вернулся хвостом
   №142.2). Вторичная ступень, а не приглушённая: до хвоста №142.1 он стоял
   `--text-dim`, и в роли признака идентичности это слишком тихо. */
.kto-ya { color: var(--text-soft); }

/* Наряд №26, работа 9: «Тренер сейчас подойдёт» — обещание человека. Наряд
   №140 добавляет ему знак: краткое встроенное подтверждение выполненного
   действия (справочник UX-приёмов, Feedback) читается за один взгляд, а не
   вычитывается из приглушённой строки рядом с кнопкой. */
.child-buttons-ack {
  font-size: var(--fs-sec);
  color: var(--accent-fill);
  font-weight: 600;
}

.child-buttons-ack:not(:empty)::before {
  content: "✓ ";
  font-weight: bold;
}

/* == 7. Учебная поддержка: конспект и словарь =============================
   Отдельная работа наряда №140 по постановке Романа 05.09.2026. Конспект и
   словарь — не вспомогательная декоративная панель, а личная растущая база
   знаний ребёнка, к которой он обращается ВО ВРЕМЯ решения задачи.

   ЧЕГО ЗДЕСЬ НЕТ И ПОЧЕМУ. Ни одного нового прямого ребёнка у колонки карточек,
   ни одного переименованного класса, ни одного изменения вложенности. Не из
   осторожности: браузерный слой (harness/browser_layer.py) держит на этой
   структуре четыре инварианта — D5 и D16 читают
   `.card-tile-header[aria-expanded]` в колонке, D23 требует, чтобы ПЕРВЫМ
   прямым ребёнком колонки был `.card-tile-archived`, а внутри урока стояли
   `.card-tile-archived-card` и ни одного `:scope > .card-tile-body`. Обёртка
   «Сейчас изучаем» отдельным div'ом сделала бы `tiles[0]` заголовком, и D23
   покраснел бы, не найдя архива первым. Два режима разведены поэтому НАДПИСЬЮ
   И ВЕСОМ, а не новым узлом дерева. */

/* -- Два режима: где сейчас и где пройденное ----------------------------- */

/* Метка режима ЕДЕТ В ШАПКЕ ПЛАШКИ, а не стоит отдельной строкой над ней.
   Первая редакция этой работы поставила надписи-разделители («СЕЙЧАС ИЗУЧАЕМ»
   / «ПРОЙДЕННЫЕ ТЕМЫ») своими строками — и прибор наряда №139 сразу показал
   цену: колонка выросла со 162 до 234 px, условие задачи перестало помещаться
   на первый экран (panel_obrezano_px 0 → 123), то есть сломалась объединённая
   норма D16+D14. Причём сломалась с ускорением: переполнение включило правый
   жёлоб подсказки прокрутки, тот отнял 34 px ширины, узкое условие перенесло
   больше строк — ровно та петля, которую наряд №139 описал у левого поля.

   Метка в шапке стоит НОЛЬ пикселей высоты: шапка и так 40 px, чип в ней
   14 px. Порядок внутри флекса задан явно (название → чип → шеврон), потому
   что порождённое содержимое становится флекс-элементом и по умолчанию встаёт
   последним, за шевроном.

   ⚠️ innerText в Chromium НЕ включает содержимое ::after — заголовки плашек,
   которые читает браузерный слой (D18, D23, check_konspekt_sections), от этих
   чипов не меняются. Это не догадка, а условие, при котором работа сделана
   именно так. */
.card-tile-header::after {
  order: 1;
  flex: 0 0 auto;
  margin-left: auto;
  padding: 1px 7px;
  border-radius: var(--r-pill);
  font-family: var(--font-body);
  font-weight: normal;
  font-size: 10px;
  letter-spacing: 0.05em;
  text-transform: uppercase;
}

.card-tile-title { order: 0; }
.card-tile-znachok, .card-tile-podpisi { order: 0; }
.card-tile-chevron { order: 2; }

.card-tile-chteniya > .card-tile-header::after {
  content: "сейчас";
  color: var(--accent-fill);
  background: var(--surface);
  border: 1px solid var(--accent);
}

.card-tile-archive-root > .card-tile-header::after {
  content: "пройдено";
  color: var(--text-soft);
  background: var(--surface);
  border: 1px solid var(--border-strong);
}

/* Текущая тема — фирменный акцент, но не агрессивная заливка (постановка
   Романа, пункт 4): мятная полоса слева на всю высоту плашки. Полоса не
   занимает места (inset-тень, не рамка) и потому ничего не отодвигает вниз —
   объединённая норма D16+D14 держится тем же бюджетом высоты, что до наряда. */
.card-tile-chteniya {
  box-shadow: inset 3px 0 0 var(--accent);
  border-color: var(--border-strong);
}

/* Пройденные темы — спокойнее, но не «выключены»: та же белая карточка, та же
   форма, только акцента нет и заголовок тише. Доступность важнее тихости:
   ребёнок за ними приходит («я уже забыл команды», прогон 09.08). */
.card-tile-archive-root {
  border-color: var(--border);
}

.card-tile-archive-root > .card-tile-header {
  background: var(--surface-2);
  color: var(--text-soft);
}

.card-tile-archived > .card-tile-header {
  background: var(--surface);
  color: var(--text-soft);
}

/* Пунктир наряда №23 («это из прошлого») остаётся, но фирменной линией. */
.card-tile-archived { border-color: var(--border-strong); }

/* -- Наряд №142, работа 2: блоки учебного материала ------------------------
   Геометрия и роли — в style.css, здесь фирменные оттенки. Мятная подложка
   главной мысли (#9ad1c7 — самый светлый фирменный, тот же, что у плашки
   «Принято»), нейтральная #f5f5f5 у правила и примера, сигнальная оранжевая
   линия у ошибки — оранжевый на детском экране занят ровно двумя смыслами и
   этот третий им родственен («здесь можно споткнуться»). */
.material-blok { background: var(--fail-bg); }
.material-blok-glavnoe {
  background: var(--ok-bg);
  border-left-color: var(--accent-fill);
  color: var(--text);
}
.material-blok-pravilo { border-left-color: var(--accent); }
.material-blok-oshibka { border-left-color: var(--attn); background: var(--attn-bg); }
.material-blok-primer { border-left-color: var(--border-strong); }
.material-blok-podpis { color: var(--text-soft); }
.material-blok-telo-monoshirinnyy { font-family: var(--font-mono); }

.material-shag-nomer {
  background: var(--press);
  color: var(--accent-fill);
}
.material-shag-telo { background: var(--surface); border-color: var(--border); }
.material-shag-zagolovok { font-family: var(--font-mono); color: var(--text); }
.material-shag-poyasnenie { color: var(--text-soft); }
.material-shag:not(:last-child)::after { color: var(--accent); }

/* -- Наряд №142, работа 1: корешки вкладок левой колонки ------------------
   Геометрия — в style.css, здесь только фирменные цвета. Выбранная вкладка
   несёт мятную подпись на белом и «срослась» с областью под собой; невыбранная
   — вторичный текст на общем фоне колонки. Различие держится тремя вещами
   сразу (цвет подписи, фон, разрыв нижней границы), а не одним цветом. */
.left-tabs {
  background: var(--bg);
  border-bottom-color: var(--border);
}

/* Наряд №195, работа 2 — строка темы: имя темы капителью, как заголовки
   экрана (тот же приём, что .login h1/.uslovie-sekciya-podpis), номер
   задачи — нейтральный чип, вторичный текст. */
.tema-line {
  background: var(--bg);
  border-bottom-color: var(--border);
}

.tema-line-nazvanie {
  font-family: var(--font-h);
  font-weight: var(--fw-h);
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--accent-fill);
}

.tema-line-chip {
  color: var(--text-soft);
  background: var(--surface-2);
  border: 1px solid var(--border);
}

.left-tab {
  font-family: var(--font-h);
  font-weight: var(--fw-h);
  color: var(--text-soft);
}

.left-tab[aria-selected="true"] {
  color: var(--accent-fill);
  background: var(--surface);
  border-bottom-color: var(--surface);
}

.left-tab:hover { background: var(--hover); }

/* -- Заголовок карточки как орган управления ----------------------------- */

.card-tile { border-color: var(--border); }

.card-tile-header {
  background: var(--surface-2);
  color: var(--text);
  font-family: var(--font-h);
  font-weight: var(--fw-h);
  transition: background-color var(--dur) var(--ease),
              color var(--dur) var(--ease);
}

.card-tile-header:hover { background: var(--hover); }
.card-tile-header:active { background: var(--press); }

/* Открытая КАРТОЧКА ЧТЕНИЯ — единственная на экране, и это видно: мятная
   шапка с тёмным текстом. Признак берётся из aria-expanded, то есть из того
   же поля, которое читают D5/D16 и по которому работает клавиатура, — второго
   источника истины не заводится.
   Карточками чтения здесь ровно то же, что и в правиле «открыта одна»
   (cards-view.js): текущие «Конспект»/«Словарик» и карточка урока архива. */
/* Наряд №142, работа 2: раскрытая карточка ТЕКУЩЕЙ темы больше не несёт
   aria-expanded — она не переключатель, а содержимое вкладки, и кнопкой её
   шапка становится только когда открыта архивная тема (тогда шапка — дорога
   обратно). Признак взят тот, который верен в обоих случаях: карточка
   чтения БЕЗ класса .card-tile-svyornuta раскрыта, с ним — свёрнута. */
.card-tile-chteniya:not(.card-tile-svyornuta) > .card-tile-header,
.card-tile-archived-card > .card-tile-header[aria-expanded="true"] {
  background: var(--ok-bg);
  color: var(--text);
}

/* Уровни НАВИГАЦИИ (корень архива и урок внутри него) открываются тише: за
   ними список имён, а не текст, и мятная плашка на них перетягивала внимание
   с того, что ребёнок читает. Пройденные темы «спокойнее, при этом легко
   доступны» — постановка Романа, пункт 4. */
.card-tile-archive-root > .card-tile-header[aria-expanded="true"],
.card-tile-archived:not(.card-tile-archived-card) > .card-tile-header[aria-expanded="true"] {
  background: var(--hover);
  color: var(--text-soft);
}

.card-tile-chevron { color: var(--accent-fill); }

/* Метка темы на карточке. Второй строкой под названием, а не в одну строку с
   ним: название («Конспект») отвечает на «что это», тема — на «чей», и
   слитые в строку они читаются одним длинным заголовком, который на 360 px
   левого поля переносится в самом неудачном месте. */
/* Метка темы — В ОДНУ СТРОКУ с названием карточки, а не второй строкой под
   ним. Второй строкой она читалась лучше и стоила 16 px высоты на каждой из
   двух текущих плашек: вместе с надписями-разделителями это и дало те 72 px,
   которыми условие выбило с первого экрана. Точка-разделитель ставится CSS,
   чтобы разметка несла только данные. */
.card-tile-title {
  display: flex;
  align-items: baseline;
  gap: 6px;
  min-width: 0;
  overflow: hidden;
}

.card-tile-tema {
  font-family: var(--font-body);
  font-weight: normal;
  font-size: var(--fs-label);
  color: var(--text-soft);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.card-tile-tema::before {
  content: "· ";
  color: var(--border-strong);
}

/* Наряд №142, работа 2: у карточки чтения тема стоит ВТОРОЙ СТРОКОЙ под
   названием вида (.card-tile-podpisi — flex-колонка), и точка-разделитель
   там ни от чего не отделяет. Причина, по которой хвост №140.2 свёл их в
   одну строку — 16 px высоты на каждой из двух плашек над условием, — во
   вкладке отпала вместе с самой стопкой плашек над условием. */
.card-tile-chteniya .card-tile-tema::before { content: none; }

.card-tile-header[aria-expanded="true"] .card-tile-tema,
.card-tile-chteniya:not(.card-tile-svyornuta) .card-tile-tema { color: var(--text-soft); }

/* Непрочитанная карточка. Мерцание наряда №10 остаётся (и остаётся его
   статическая замена при reduced-motion, style.css), меняются цвета: на
   светлом сигналом служит тёплая подложка и оранжевая точка. */
@keyframes card-tile-blink {
  0%, 100% { background: var(--surface-2); }
  50%      { background: var(--attn-bg); }
}

/* -- Область чтения ------------------------------------------------------ */

/* Спокойная белая область, читаемый кегль, щедрый интервал (постановка
   Романа, пункт 3). 14 → 15 px и 1,5 → 1,65: ребёнок читает эти карточки
   абзацами и с расстояния. Объединённая норма D16+D14 от этого не страдает —
   тело карточки существует только у РАЗВЁРНУТОЙ плашки и прокручивается
   внутри своих 45vh; высоту свёрнутой колонки, из которой и состоит бюджет
   нормы, кегль тела не меняет вовсе. */
.card-tile-body {
  background: var(--surface);
  border-top: 1px solid var(--border);
  font-size: 15px;
  line-height: 1.65;
  color: var(--text);
}

/* Заголовки разделов внутри области чтения. */
.card-tile-body h1,
.card-tile-body h2,
.card-tile-body h3 {
  margin: var(--s4) 0 var(--s2);
  font-size: 15px;
  color: var(--accent-fill);
}

.card-tile-body h1:first-child,
.card-tile-body h2:first-child,
.card-tile-body h3:first-child { margin-top: 0; }

.card-tile-body p { margin: 0 0 var(--s3); }
.card-tile-body p:last-child { margin-bottom: 0; }

/* Отчётливо отделённые определения, примеры, код и выводы — если они есть в
   исходном материале. Слова, порядок и смысл контента не меняются ни на
   символ: размечено то, что методист уже разметил markdown'ом.
     · цитата (`> …`) — важный вывод, мятная полоса слева;
     · список — определения и перечисления, свой отступ и маркер;
     · блок кода — своя утопленная плашка моноширинным;
     · `код в строке` — выделен внутри абзаца. */
.card-tile-body blockquote {
  margin: 0 0 var(--s3);
  padding: var(--s2) var(--s3);
  border-left: 3px solid var(--accent);
  background: var(--surface-2);
  border-radius: 0 var(--r-sm) var(--r-sm) 0;
  color: var(--text);
}

.card-tile-body ul,
.card-tile-body ol {
  margin: 0 0 var(--s3);
  padding-left: 1.3em;
}

.card-tile-body li { margin-bottom: var(--s1); }

/* Наряд №142, работа 2: шаг процесса — не пункт списка по виду, и общий
   отступ пунктов ему мал. Правило выше специфичнее селектора .material-shag
   в style.css (0-2-1 против 0-1-0) и молча сбивало зазор с 22 px до 4 —
   стрелка ↓ между шагами при этом уезжала под плашку следующего шага и
   пропадала с экрана. Поймано замером, не глазом: на снимке отсутствие
   стрелки читалось как «её не нарисовали». */
.card-tile-body .material-shag { margin-bottom: 22px; }
.card-tile-body .material-shag:last-child { margin-bottom: 0; }

.card-tile-body pre {
  margin: 0 0 var(--s3);
  padding: var(--s2) 10px;
  background: var(--surface-2);
  border: 1px solid var(--border);
  border-radius: var(--r-sm);
  font-family: var(--font-mono);
  font-size: var(--fs-mono);
  line-height: 1.5;
}

.card-tile-body :not(pre) > code {
  padding: 1px 5px;
  border-radius: 3px;
  background: var(--surface-2);
  border: 1px solid var(--border);
  font-family: var(--font-mono);
  font-size: 0.92em;
}

.card-tile-body strong { color: var(--text); }

.card-tile-body table th,
.card-tile-body table td {
  padding: var(--s1) var(--s2);
  border-bottom: 1px solid var(--border);
  text-align: left;
}

/* Раздел конспекта — второй уровень внутри области чтения. */
.konspekt-section { border-bottom-color: var(--border); }

.konspekt-section-heading {
  color: var(--text);
  font-family: var(--font-h);
  font-weight: var(--fw-h);
  border-radius: var(--r-sm);
  transition: background-color var(--dur) var(--ease);
}

.konspekt-section-heading:hover { background: var(--hover); }
.konspekt-section-heading:active { background: var(--press); }
.konspekt-section-heading::before { color: var(--accent-fill); }
.konspekt-section-heading[aria-expanded="true"] { color: var(--accent-fill); }

.konspekt-section-body { color: var(--text); }

/* Подсказка пустого состояния. Не ошибка и не техническая пустота: свой знак,
   спокойный цвет, читаемый кегль. */
.konspekt-empty-hint {
  margin: var(--s2) var(--s1);
  padding: var(--s3);
  background: var(--surface-2);
  border: 1px dashed var(--border-strong);
  border-radius: var(--r-sm);
  color: var(--text-dim);
  font-size: var(--fs-sec);
  line-height: 1.5;
}

.konspekt-empty-hint::before {
  content: "🌱 ";
}

/* -- Словарь: своя форма, не конспект ------------------------------------ */

/* Группа («Понятия» / «Знаки кода») — короткий статусный заголовок, капитель
   уместна. */
.glossary-group-title {
  color: var(--accent-fill);
  border-bottom: 1px solid var(--border);
  margin-bottom: var(--s1);
}

.glossary-term-row {
  border-bottom-color: var(--border);
  border-radius: var(--r-sm);
  transition: background-color var(--dur) var(--ease);
}

.glossary-term-row:hover { background: var(--surface-2); }

/* Термин первичен: своя строка, тёмный, жирный, стрелка мятная. */
.glossary-term-heading {
  min-height: 32px;
  padding: var(--s1) var(--s1) 0;
}

.glossary-term-heading::before { color: var(--accent-fill); }

.glossary-term {
  font-size: var(--fs-sec);
  color: var(--text);
}

.glossary-term--concept {
  font-family: var(--font-h);
  font-weight: var(--fw-h);
}

.glossary-term--code {
  font-family: var(--font-mono);
  background: var(--surface-2);
  border: 1px solid var(--border);
  color: var(--accent-fill);
}

/* Определение — рядом с термином и всегда в разметке (app.js::
   buildGlossaryTermRow). Свёрнутое подрезано двумя строками: словарь
   сканируется по терминам, но каждое значение видно сразу, без клика.
   Клик снимает подрезку, а не проявляет текст. */
.glossary-definition {
  padding: 0 var(--s1) var(--s2) 1.4em;
  font-size: var(--fs-sec);
  line-height: 1.5;
  color: var(--text-soft);
}

.glossary-definition--kratko {
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}

/* Раскрытый термин — тот, на котором ребёнок остановился. */
.glossary-term-heading[aria-expanded="true"] + .glossary-definition {
  color: var(--text);
}

/* == 8. Состояния задачи ==================================================
   Иерархия экрана — задача → редактор → результат и следующее действие
   (наряд №139, работа 2). Наряд №140 её не переставляет, он делает её
   различимой на светлом: первичное получает вес, второстепенное — тишину. */

/* -- Рабочая зона редактора ----------------------------------------------
   ⚠️ ЭТО РЕШЕНИЕ ОТМЕНЕНО ХВОСТОМ №140.2. Наряд №140 держал здесь тёмный
   Monaco (`vs-dark`) и объяснял это тем, что редактор — «отдельная рабочая
   зона внутри светлой страницы». Живой просмотр Романа 05.09.2026 показал
   обратное: чёрный блок в середине светлого учебного экрана читается
   провалом, а не инструментом, и тянет на себя внимание, которое должно
   принадлежать условию и коду ребёнка. Тёмного на детском экране больше нет
   нигде.
   Тема Monaco — `bcsp-svetlaya` (app.js::initEditor, `base: "vs"` плюс
   фирменная обвязка). Здесь остаётся только рамка зоны: у редактора белый
   фон, и без линий он слился бы с панелью. */
.editor {
  border-top: 1px solid var(--border);
  border-bottom: 1px solid var(--border);
  background: var(--surface);
}

.editor-label {
  background: var(--surface);
  border-bottom: 1px solid var(--border);
}

/* -- Панель вывода программы ---------------------------------------------
   Светлая с наряда №140 и остаётся такой: моноширинный шрифт (это дословный
   вывод), фон — утопленная мятная ступень под белой рабочей областью. */
.output-panel {
  background: var(--surface-sunk);
  border-top: 1px solid var(--border);
}

.output-panel-header {
  background: var(--surface);
  border-bottom: 1px solid var(--border);
}

.output-panel-body {
  font-family: var(--font-mono);
  color: var(--text);
}

/* Хвост №140.2 — панель вывода стоит ПОД светлым теперь редактором, и её
   собственная утопленная ступень (--surface-sunk = #f0faf7) осталась
   единственным признаком «это другая область». Линия сверху усилена
   фирменной: без неё белый редактор и почти белая панель вывода читались бы
   одним полем. */
.output-panel { border-top: 1px solid var(--border-strong); }

/* «Показывать нечего» и подписи дифа — речь о выводе, а не сам вывод. */
.output-panel-body .output-pusto {
  font-family: var(--font-body);
  color: var(--text-dim);
}

.output-panel-body .output-diff-label { color: var(--text-dim); }

/* Расхождение: ожидание — спокойным фирменным (вот как надо), факт —
   красным (вот что вышло). Правило наряда №139, работы 4 сохранено дословно,
   поменялись только значения под светлый фон. */
.output-panel-body .output-mismatch {
  color: var(--fail);
  background: var(--fail-bg);
}

.output-panel-body .output-diff-expected .output-mismatch {
  color: var(--ok);
  background: var(--ok-bg);
}

/* Разбор ошибки компиляции — человеческая строка человеческим шрифтом,
   сырой вывод g++ под свёрнутым details. */
.output-panel-body .compile-error-summary {
  font-family: var(--font-body);
  color: var(--text);
}

.output-panel-body .compile-error-raw > summary {
  font-family: var(--font-body);
  color: var(--text-dim);
  border-radius: var(--r-sm);
  transition: color var(--dur) var(--ease);
}

.output-panel-body .compile-error-raw > summary:hover { color: var(--accent-fill); }

.output-panel-body .compile-error-raw > pre { color: var(--text-soft); }

/* -- Вердикт попытки ------------------------------------------------------
   Пилюля наряда №139, работы 4 остаётся: форма, знак и цвет вместе, три
   носителя смысла вместо одного. Подложка неудачи нейтральная (#f5f5f5):
   единственная РЕЗУЛЬТАТНАЯ подложка этого интерфейса — мятная #9ad1c7, и
   она принадлежит принятой попытке. */
.verdict:not(:empty) {
  background: var(--fail-bg);
  color: var(--text-dim);
  border: 1px solid var(--border);
}

.verdict.success { color: var(--ok); background: var(--ok-bg); border-color: var(--accent); }

.verdict.wrong_answer,
.verdict.runtime_error,
.verdict.compile_error,
.verdict.format_error,
.verdict.nuzhen_tsikl,
.verdict.verdict-otkaz {
  color: var(--fail);
  background: var(--fail-bg);
  border-color: var(--fail);
}

.verdict.time_limit { color: var(--warn); border-color: var(--warn); }
.verdict.pending { color: var(--text-dim); border-color: var(--border-strong); }
.verdict.pending::before { background: var(--accent); }

/* -- Зона итога и плашка принятой попытки --------------------------------- */

.itog { border-top: 1px solid var(--border); }

/* ⚠️ ХВОСТ №140.2, РАБОТА 6 — ПЛАШКА РЕЗУЛЬТАТА СЖАТА.
   Наряд №140 заливал мятным ВСЮ зону итога и объяснял это тем, что «момент
   успеха — это и вердикт, и время на задаче, и кнопка дальше; разнести их по
   двум блокам значило бы показать три вещи как три». Живой просмотр Романа
   05.09.2026 показал цену: зона итога — самый низ рабочей колонки, и мятный
   прямоугольник во всю её ширину перетягивал внимание с СЛЕДУЮЩЕГО учебного
   шага на уже случившийся результат. «Результат проверки не должен
   превращаться в огромную плашку».

   Стало: фирменная линия сверху остаётся (момент отмечен), заливка зоны —
   нет. Единственная результатная подложка (#9ad1c7) сжалась до того, чему
   она и принадлежит, — до пилюли вердикта «Попытка №N: Принято». Кнопка
   перехода рядом с ней остаётся самым заметным элементом зоны, потому что
   она и есть следующий шаг. */
.itog:has(> .next-task-control:not([hidden])) {
  background: var(--surface);
  border-top: 2px solid var(--accent);
}

/* Пилюля успеха — теперь единственный носитель мятной подложки. Кегль
   основного текста и тёмный фирменный текст: 4,9:1 на #9ad1c7. */
.itog:has(> .next-task-control:not([hidden])) .verdict.success {
  background: var(--ok-bg);
  border-color: var(--accent);
  color: var(--text);
  font-size: var(--fs-body);
  padding: 4px 12px;
}

/* ⚠️ Правило «кнопка перехода на подложке результата — тёмная» СНЯТО вместе
   с самой подложкой (работа 6): она заводилась ровно потому, что мятная
   кнопка сливалась с мятной заливкой зоны — 1,6:1. Зона теперь белая, и
   кнопка остаётся продвигающей, как везде: яркий фирменный #2fb295 с тёмным
   текстом. Одно правило меньше, и оно не описывает исключение, которого
   больше нет.

   Что остаётся здесь по существу: в момент успеха кнопка перехода —
   ЕДИНСТВЕННОЕ действие зоны, и это подчёркнуто размером, а не цветом. */
.itog:has(> .next-task-control:not([hidden])) #next-task-btn,
.itog:has(> .next-task-control:not([hidden])) #v5-next-task-btn,
.itog:has(> .next-task-control:not([hidden])) #v5-next-topic-btn {
  font-size: var(--fs-body);
  padding: 0 var(--s8);
}

#next-task-message { color: var(--text-dim); }

/* -- Наряд №195, работа 4; хвост №195.1, работы 12–13: отклик -------------
   Живёт здесь целиком (геометрия и цвет вместе), не в style.css: анимации
   читают var(--ease) — токен ЭТОГО слоя (наряд №140), его нет в tokens.css
   (общем на все три экрана) и не должно быть — trainer.html и admin.html
   этот слой не грузят и элементов #verdict/#itog не несут вовсе. Оранжевый
   (--attn, непрочитанная карточка и предупреждение) в эффектах не
   используется (хард-гейт наряда), чтобы праздник не путался с сигналом
   «внимание».

   Хвост №195.1, работа 12 (образец bcsp-kb/outputs/2026-09-25_Obrazets_
   Otklik-poverh-ekrana.html, кнопки «Сам» и «Дожал»): надпись ступени больше
   не висит рядом с пилюлей — она вылетает в центре экрана поверх всего и
   рассыпается звёздами; после эффекта на панели остаётся только пилюля.
   Звёзды и салют рисуются на одном холсте во весь экран (app.js — цикл
   кадров только пока есть частицы). Холст выше слоя итогов (z-index
   .modal-overlay — 100): салют темы догорает поверх поднявшейся карточки.

   Холст и надпись — pointer-events: none и в разметке (index.html), и
   здесь: «клик проходит сквозь слой эффекта» обязано быть верно по
   построению, а не зависеть от того, докатился ли класс вовремя. */
.otklik-holst {
  position: fixed;
  inset: 0;
  width: 100vw;
  height: 100vh;
  pointer-events: none;
  z-index: 120;
}

.otklik-nadpis {
  position: fixed;
  left: 50%;
  top: 40%;
  transform: translate(-50%, -50%);
  pointer-events: none;
  z-index: 119;
  font-family: var(--font-h);
  font-weight: var(--fw-h);
  font-size: 30px;
  line-height: 1;
  text-transform: uppercase;
  letter-spacing: 0.03em;
  white-space: nowrap;
  color: var(--accent-text);
  background: var(--accent-fill);
  border-radius: var(--r-pill);
  padding: 20px 38px;
  box-shadow: 0 14px 40px rgba(16, 54, 46, 0.28);
  opacity: 0;
}
.otklik-nadpis--dozhal {
  background: var(--navy);
  font-size: 36px;
  padding: 24px 46px;
}

/* Вдох пилюли и прорисовка галочки (~0,5 с) — у каждой ступени и на
   границе темы, как в образце; у «Принято» это весь эффект. */
@keyframes otklik-vdoh {
  0%, 100% { transform: scale(1); }
  55% { transform: scale(1.06); }
}
@keyframes otklik-galochka {
  0% { opacity: 0; transform: scale(0.3); }
  65% { opacity: 1; transform: scale(1.25); }
  100% { opacity: 1; transform: scale(1); }
}
.otklik-vdoh #verdict.success {
  animation: otklik-vdoh 0.5s var(--ease);
}
.otklik-vdoh #verdict.success::before {
  display: inline-block;
  animation: otklik-galochka 0.5s var(--ease);
}

/* ОБЯЗАТЕЛЬНЫЙ ОТКАТ: ни вдоха, ни звёзд, ни салюта; надпись ступени
   появляется и гаснет только прозрачностью (app.js). Холст скрыт и здесь —
   на случай, если частица всё-таки попала в очередь. */
@media (prefers-reduced-motion: reduce) {
  .otklik-holst { display: none !important; }
  .otklik-vdoh #verdict.success,
  .otklik-vdoh #verdict.success::before {
    animation: none;
  }
}

/* -- Второстепенные технические детали (progressive disclosure) -----------
   Наряд: «второстепенные технические детали не должны конкурировать с текущим
   учебным действием». Часы задачи ничего не ограничивают (наряд №139,
   работа 4 — они считают время НА ЗАДАЧЕ), позывной — опознавательная метка,
   индикатор пути v5 — прибор наблюдения. Все три остаются на экране и
   остаются читаемыми, но уходят на приглушённую ступень. */
.task-timer { color: var(--text-soft); }
.task-timer::before { color: var(--text-soft); }

/* Хвост №142.2, работа 3: здесь стояли правила чипа этапа («Практика» /
   «Разбор», наряд №140). Чип снят решением Романа 06.09.2026: слово
   «Практика» ребёнку ничего не сообщало — он и так видит перед собой задачу
   и редактор, — а место в шапке занимало.

   Приём наряда №140 при этом не опровергнут и стоит его записать: чип
   выводился из видимости #statement/#workspace, то есть из фактов DOM, и
   рассинхронизировать его с экраном было нельзя по построению. Если этап
   когда-нибудь понадобится обозначить снова — так и надо делать, а не
   заводить отдельное состояние в JS. */

/* -- Блок состояния: занятие недоступно, сеть не отвечает ----------------- */

/* Наряд №139, работа 5 завёл его как блок с собственной иерархией. Наряд
   №140 делает его КАРТОЧКОЙ: отказ — это состояние, у которого есть своё
   место, а не текст, просыпанный там, где обычно стоит задача. */
.sostoyanie {
  margin: var(--s3) 0;
  padding: var(--s6);
  background: var(--surface);
  border: 1px solid var(--border);
  border-top: 3px solid var(--attn);
  border-radius: var(--r-md);
}

.sostoyanie-znak { color: var(--attn); }

.sostoyanie-glavnoe {
  font-family: var(--font-h);
  font-weight: var(--fw-h);
  color: var(--text);
}

.sostoyanie-poyasnenie { color: var(--text-dim); }

/* -- Вопрос узла с вариантами -------------------------------------------- */

.v5-formal-check { border-top: 1px solid var(--border); }
.v5-formal-check-prompt { color: var(--text); }

/* == 9. Движение: одна кривая, одна длительность, обязательный откат ======
   Справочник UX-приёмов, сквозной параметр Easing: «от выбора easing зависит,
   ощущается ли движение естественным или дёрганым». Кривая и длительность в
   :root, здесь — переходы, которых у элементов не было, и обязательный откат.

   Ни scroll-triggered, ни stagger: всё движение экрана — отклик на действие
   ребёнка или признак идущей работы. Украшающей анимации не заведено ни
   одной. */

.card-tile-chevron,
.konspekt-section-heading::before,
.glossary-term-heading::before {
  transition: transform var(--dur) var(--ease);
}

/* Реплика штурмана и плашка результата появляются, а не возникают: 140 мс
   проявления убирают рывок, которым лента дёргалась на каждом ходе. */
.chat-message,
.itog:has(> .next-task-control:not([hidden])) {
  animation: bcsp-poyavlenie var(--dur) var(--ease);
}

@keyframes bcsp-poyavlenie {
  from { opacity: 0; transform: translateY(3px); }
  to   { opacity: 1; transform: none; }
}

/* ОБЯЗАТЕЛЬНЫЙ ОТКАТ. Перечислен поимённо, а не через `*`: глобальное
   `transition: none !important` погасило бы и то, чем существующие правила
   reduced-motion уже управляют осмысленно (мерцание непрочитанной карточки
   наряда №10 заменяется заливкой и рамкой, а не пропадает). Всё, что наряд
   №140 добавил в движение, здесь и снимается. */
@media (prefers-reduced-motion: reduce) {
  .login button, #submit-btn, .chat-form button, .modal-buttons button,
  .sostoyanie button,
  #material-next-btn, #scenario-advance-btn, #next-task-btn,
  #v5-next-task-btn, #v5-next-topic-btn,
  .logout-btn, #call-trainer-btn, #abandon-btn, #pause-btn,
  .v5-formal-check-options button, .overflow-hint,
  .chat-form textarea, .login input,
  .card-tile-header, .konspekt-section-heading, .glossary-term-row,
  .card-tile-chevron, .konspekt-section-heading::before,
  .glossary-term-heading::before,
  .output-panel-body .compile-error-raw > summary {
    transition: none;
  }

  .chat-message,
  .itog:has(> .next-task-control:not([hidden])) {
    animation: none;
  }

  /* Наряд №144, работа 3. Движение снимается, ПРИЗНАК ОСТАЁТСЯ: постоянная
     тень той же кромки вместо двух её вспышек. Тот же приём, которым этот
     блок уже пользуется двумя правилами ниже (нажатие кнопки без сдвига) и
     которым style.css заменяет мерцание непрочитанной карточки, — движение
     заменяется формой, а не убирается. Убрать его вовсе значило бы, что
     ребёнок с prefers-reduced-motion нового вопроса не отличит от прочитанного
     вовсе: у остальных реплик тени нет, у вопроса — есть. */
  .chat-message-question.novyy-vopros {
    animation: none;
    box-shadow: 0 0 0 4px rgba(184, 134, 11, 0.28);
  }

  /* Нажатие остаётся различимым без сдвига: тот же приём, что у мерцания
     карточки в style.css — движение заменяется формой, а не убирается. */
  .login button:active:not(:disabled),
  #submit-btn:active:not(:disabled),
  .chat-form button:active:not(:disabled),
  #material-next-btn:active:not(:disabled),
  #scenario-advance-btn:active:not(:disabled),
  #next-task-btn:active:not(:disabled),
  #v5-next-task-btn:active:not(:disabled),
  #v5-next-topic-btn:active:not(:disabled),
  .logout-btn:active:not(:disabled),
  #call-trainer-btn:active:not(:disabled),
  #abandon-btn:active:not(:disabled),
  #pause-btn:active:not(:disabled),
  .v5-formal-check-options button:active:not(:disabled) {
    transform: none;
    box-shadow: inset 0 0 0 2px var(--accent-fill);
  }
}

/* == 10. Шапка колонки чата: перенос вместо давления ======================
   Наряд №140 добавил в шапку чип этапа, и на 380 px (пол колонки чата,
   наряд №139) четыре элемента перестали помещаться: «Завершить тренировку»
   разорвалась на две строки, индикатор пути v5 — тоже. Механизм тот же, что
   наряд №139 разбирал у ряда контролов: флекс не переполнялся, он ДАВИЛ
   детей по ширине, и это не видно ни одному контролю, кроме глаза.

   Лечение то же, что там: перенос строки вместо разрыва слов. Колонка чата
   бюджета высоты не имеет (в отличие от левого поля с объединённой нормой
   D16+D14), поэтому вторая строка шапки не стоит ничего. */
/* Хвост №142.2: `flex-wrap: wrap` снят — шапка теперь колонка из двух
   заданных рядов (style.css::.session-info), и переносить нечего. Правило
   лечило разрыв слов в одну строку; теперь строк две по построению. */

#call-trainer-btn,
.logout-btn { white-space: nowrap; }

/* Хвост №142.1: правило подрезки индикатора пути v5 ушло вместе с самим
   индикатором. Шапка от этого стала свободнее на 108 px — ровно столько
   занимал технический чип, и ровно поэтому «Позвать тренера» помещается
   рядом с «Завершить тренировку» без второй строки. */

/* == 11. Хвост №140.2 — учебная поддержка на экране постоянно ==============
   Живой просмотр Романа 05.09.2026: «Конспект и словарь должны быть видимы
   как постоянные верхнеуровневые разделы в левой учебной колонке, а не
   исчезать». Постоянство сделано в данных (cards-view.js::cardsColumnTree —
   разделы больше не условны) и в видимости поля (app.js::
   updateLeftPanelVisibility). Здесь — то, что от этого нужно вёрстке. */

/* Пустой раздел — тоже раздел, и он не имеет права выглядеть тише
   наполненного: ребёнок должен видеть, что место есть и оно ждёт материала.
   Прежнее правило `.cards-column:empty` (наряд №140) снято за
   ненадобностью — колонка непуста по построению; правило осталось бы
   мёртвым кодом, который выглядит работающим. */
.cards-column:empty { background: var(--bg); }

/* ЧЬЁЙ ТЕМЫ ЭТОТ МАТЕРИАЛ — ВИДНО ВСЮ ДОРОГУ. Карточка чтения занимает всю
   высоту вкладки, и её шапка (вид материала плюс тема) уезжала бы вверх
   вместе с текстом: ребёнок, дочитавший до низа длинного конспекта, терял бы
   единственный ответ на «что я читаю».
   Хвост №140.2 держал этим же приёмом ВОЗВРАЩЕНИЕ К УСЛОВИЮ — заголовок был
   и кнопкой закрытия. Со вкладками возвращение к условию делает корешок
   «Задача», который и так на месте всегда, а липкость осталась ради второго
   своего смысла — подписи материала.
   Липкость только у РАСКРЫТОЙ карточки: у свёрнутой прилипать нечему, а
   sticky на всех заголовках склеил бы их в полосу при прокрутке. */
.card-tile-chteniya:not(.card-tile-svyornuta) > .card-tile-header,
.card-tile-archived-card > .card-tile-header[aria-expanded="true"] {
  position: sticky;
  top: 0;
  z-index: 2;
}

/* Открытая карточка чтения — акцент по всей рамке, а не только слева: она
   единственная открытая на экране (правило «одна карточка чтения»), и это
   стоит показать спокойно, но однозначно. */
.card-tile-chteniya:not(.card-tile-svyornuta) {
  border-color: var(--accent);
}

/* == 12. Хвост №140.2 — условие задачи как учебный текст ===================
   Блоки собирает app.js::sobratUslovie по разбору
   frontend/uslovie-razmetka.js. Здесь — то, что делает их РАЗНЫМИ на вид:
   постановка читается как текст, формат — как справка, пример — как данные.
   Ни одного слова контента эти правила не меняют. */

/* Постановка. Мера строки и интервал — те же, что у связного текста экрана. */
.uslovie-vvedenie {
  line-height: var(--lh-body);
}

.uslovie-vvedenie > :last-child { margin-bottom: 0; }

/* Секция формата — своя карточка на утопленной ступени. Она НЕ конкурирует с
   постановкой: тише по фону, мельче по подписи, но обособлена — ребёнок,
   которому нужен формат вывода, находит его боковым зрением, а не перечитывая
   условие. */
/* ПОДПИСЬ СЛЕВА, ТЕЛО СПРАВА — и это решение замера, не вкуса. Первая
   редакция работы 5 ставила подпись отдельной строкой над телом; прибор
   наряда №139 сразу показал цену: условие выросло на 106 px и перестало
   помещаться на первый экран (panel_obrezano_px 0 → 106), то есть сломалась
   объединённая норма D16+D14. Бюджет этой нормы на 1366×768 равен нулю —
   факт, замеренный ещё нарядом №140.
   Сетка в две колонки отдаёт подписи её собственную строку обратно телу:
   «ФОРМАТ ВВОДА. | Ввода нет.» занимает одну строку вместо двух. Требование
   «формат всегда начинается с новой строки» держится тем, что это отдельный
   БЛОК, а не тем, что подпись стоит над телом. */
.uslovie-sekciya {
  display: grid;
  grid-template-columns: minmax(84px, auto) 1fr;
  gap: 0 var(--s3);
  align-items: baseline;
  margin: var(--s3) 0 0;
  padding: var(--s2) var(--s3);
  background: var(--surface-2);
  border: 1px solid var(--border);
  border-left: 3px solid var(--accent);
  border-radius: 0 var(--r-md) var(--r-md) 0;
}

/* Подпись секции — короткий статусный заголовок, капитель уместна. Точка в
   конце сохранена: она написана в контенте, и трогать её нельзя. */
.uslovie-sekciya-podpis {
  grid-column: 1;
  margin: 0;
  font-family: var(--font-h);
  font-weight: var(--fw-h);
  font-size: 11px;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--accent-fill);
  line-height: 1.45;
}

.uslovie-sekciya-telo {
  grid-column: 2;
  min-width: 0;
  font-size: var(--fs-sec);
  line-height: 1.45;
  color: var(--text);
}

.uslovie-sekciya-telo > :first-child { margin-top: 0; }
.uslovie-sekciya-telo > :last-child { margin-bottom: 0; }

/* Пример ввода/вывода — отдельный моноширинный блок и внутри секции остаётся
   отдельным: белая плашка на утопленном фоне секции, то есть данные, а не
   продолжение фразы про формат. */
.uslovie-sekciya-telo pre,
.uslovie-vvedenie pre {
  margin: 6px 0 0;
  padding: 6px 10px;
  background: var(--surface);
  border: 1px solid var(--border-strong);
  border-radius: var(--r-sm);
  font-family: var(--font-mono);
  font-size: var(--fs-mono);
  line-height: 1.5;
  overflow-x: auto;
}

/* Таблица значений в постановке — данные, и читается сеткой, а не абзацем. */
.uslovie-vvedenie table {
  margin: var(--s2) 0;
  border-collapse: collapse;
}

/* Вертикальные отступы ячеек — 2 px, а не 4: таблица значений бывает на пять
   строк, и каждый пиксель строки уходит из бюджета D16+D14 вчетверне. */
.uslovie-vvedenie th,
.uslovie-vvedenie td {
  padding: 2px var(--s3) 2px 0;
  border-bottom: 1px solid var(--border);
  text-align: left;
  vertical-align: top;
}

.uslovie-vvedenie th {
  font-family: var(--font-h);
  font-weight: var(--fw-h);
  color: var(--accent-fill);
  font-size: var(--fs-sec);
}

.uslovie-vvedenie :not(pre) > code,
.uslovie-sekciya-telo :not(pre) > code {
  padding: 1px 5px;
  border-radius: 3px;
  background: var(--surface);
  border: 1px solid var(--border-strong);
  font-family: var(--font-mono);
  font-size: 0.92em;
  color: var(--text);
}

/* == 13. Хвост №140.2, работа 6 — чат и статусы вторичны ===================
   Постановка: «Чат, статусы и технические сообщения визуально вторичны по
   отношению к условию, редактору и действию ребёнка». На светлом экране
   наряда №140 они этого не соблюдали: колонка чата такая же белая панель, что
   и рабочая, реплики штурмана — мятные пузыри во всю её ширину, и глаз
   начинал чтение экрана справа.

   Чем это лечится и чем НЕ лечится. Не размером кегля: реплики штурмана —
   учебный текст, и ужимать его нельзя. Лечится подложкой колонки: чат уходит
   на мятную ступень страницы, а условие и редактор остаются белыми
   панелями. Белое на мятном читается «главным», мятное — «рядом». */
.chat {
  background: var(--bg);
}

/* Реплика на мятной подложке колонки становится белой карточкой — иначе
   мятный пузырь на мятном фоне потеряет границы. Заодно это ставит реплики в
   ту же форму, что и всё остальное на экране: белая карточка с фирменной
   рамкой. */
.chat-message-agent {
  background: var(--surface);
  border: 1px solid var(--border);
}

/* Реплика ребёнка остаётся заливкой — её узнают по цвету, не по форме. */
.chat-message-student {
  background: var(--accent-fill);
  color: var(--accent-text);
}

/* Шапка колонки, ряд действий и форма ответа стоят на белом: это органы
   управления, и они не должны сливаться с подложкой ленты. */
.session-info,
.chat-form,
.v5-formal-check {
  background: var(--surface);
}

/* Пустая лента на мятном — та же подсказка, тот же приглушённый цвет. */
.chat-messages:empty::before { color: var(--text-soft); }

/* == 14. Хвост №140.2, работа 4 — контраст метаданных ======================
   Роман назвал дефект прямо: «текст на мятной плашке не может быть
   светло-зелёным». Прибор наряда №140.2 посчитал WCAG-отношения по всем
   замеряемым комбинациям во всех состояниях и нашёл ровно четыре нарушителя —
   все четыре с приглушённого `--text-mute` (#6aaa9a) и с `--accent-fill` на
   светлой мятной подложке:

     · «на задаче 00:02»           2.68:1 на белом   (нужно 4.5)
     · метка темы на карточке      2.52:1 на мятном
     · метка темы на ОТКРЫТОЙ      4.47:1 на #9ad1c7 — тоже мимо
     · чип этапа «ПРАКТИКА»        4.04:1 на #e0f7f2

   ЧТО ИЗ ЭТОГО СЛЕДУЕТ ПРО САМ ТОКЕН. `--text-mute` = #6aaa9a — законный
   фирменный «приглушённый текст» канона, но канон отводит ему placeholder и
   meta, а не строку, которую ребёнок ЧИТАЕТ. Наряд №140 это уже знал и
   написал у объявления токена; ошибка была в применении: таймер и метка темы
   — не placeholder, их читают. Токен остаётся на своих законных местах
   (плейсхолдер, подсказка пустого состояния, чипы режима «сейчас/пройдено»,
   индикатор пути v5 — всё, что опознают, а не читают). */

/* Часы задачи ребёнок читает — значит вторичная ступень, не приглушённая. */
.task-timer,
.task-timer::before { color: var(--text-soft); }

/* Метка темы на карточке — тоже читаемая строка: она отвечает на «чьей темы
   этот конспект». На белой и мятной подложке хватает вторичной ступени; на
   подложке ОТКРЫТОЙ карточки (#9ad1c7) вторичная даёт 4.47:1, и там нужен
   основной цвет. Разница в один шаг, но она и есть разница между «прочёл» и
   «разобрал». */
.card-tile-tema { color: var(--text-soft); }

.card-tile-header[aria-expanded="true"] .card-tile-tema,
.card-tile-chteniya:not(.card-tile-svyornuta) .card-tile-tema { color: var(--text); }

/* Хвост №142.2: строка цвета чипа этапа ушла вместе с самим чипом. */

/* Подпись секции условия. До этой правки она получала цвет не своим правилом,
   а общим `.statement h2` (специфичность 1-1 против 1-0) — то есть выглядела
   как основной текст СЛУЧАЙНО, а не по решению. Ставится явно: вторичная
   ступень, 7:1 на подложке секции. Мятную роль несёт левая линия блока. */
.statement .uslovie-sekciya-podpis { color: var(--text-soft); }

/* == 15. Хвост №140.2, работа 4 — #6aaa9a больше не носит текста ===========
   ВЫВОД ЗАМЕРА, а не решение по вкусу. Прибор посчитал WCAG-отношения по 267
   комбинациям в пятнадцати состояниях и нашёл ОДИННАДЦАТЬ нарушителей. Все
   одиннадцать — один и тот же цвет: `--text-mute` = #6aaa9a, фирменный
   «приглушённый текст» канона. На белом он даёт 2,68:1, на светлом мятном
   2,52:1, на подложке выключенной кнопки 2,06:1 — при пороге 4,5:1.

   Самое дорогое нашлось там, куда глаз не смотрит: ВЫКЛЮЧЕННЫЕ кнопки.
   Наряд №140 заменил у них `opacity` на светлую мятную плашку с приглушённым
   текстом — и починил одно (текст больше не полупрозрачный), заведя другое:
   «Отправить» в момент ожидания вердикта даёт 2,06:1, то есть ребёнок, который
   ждёт ответа судьи, читает надпись на кнопке хуже, чем фон под ней. Ровно
   тот случай, который называется только числом: на снимке эта кнопка выглядит
   нормально.

   ПРАВИЛО, КОТОРОЕ ИЗ ЭТОГО СЛЕДУЕТ: на детском экране #6aaa9a не носит
   текста вообще. Канон отводит ему placeholder и meta — но и плейсхолдер
   ребёнок ЧИТАЕТ («Написать штурману…» — единственная подпись поля ответа), а
   meta вроде «на задаче 00:02» тем более. Все восемнадцать объявлений
   `color: var(--text-mute)` переведены НА МЕСТЕ на --text-soft (#195e50):
   7,6:1 на белом, 7,0:1 на мятном фоне страницы, 5,8:1 на подложке
   выключенной кнопки. Правились на месте, а не перекрывались ниже: тень
   объявления, которое молча перебивается в конце файла, читается следующим
   человеком как работающее правило.

   ⚠️ ОБЪЯВЛЕНИЕ ТОКЕНА ОСТАЁТСЯ, и это не мусор: `--text-mute` читает
   style.css (`.task-timer::before`, `.chat-messages:empty::before`), общий на
   три экрана. Снять объявление здесь значило бы оставить эти правила без
   значения на детском экране. Токен объявлен, законен для НЕтекстовых ролей и
   не используется слоем ни разу — это и проверяет
   frontend/test-naryad140-2-fokus.js.

   Ниже — единственное правило, которого до замера не существовало вовсе:
   цвет текста выключенной кнопки. */

/* Выключенные кнопки — все четыре вида. Различие «выключено» несут подложка
   и курсор, а не нечитаемый текст. */
#submit-btn:disabled,
.login button:disabled,
.chat-form button:disabled,
#material-next-btn:disabled,
#scenario-advance-btn:disabled,
#v5-next-task-btn:disabled,
#v5-next-topic-btn:disabled,
#call-trainer-btn:disabled,
#abandon-btn:disabled,
#pause-btn:disabled,
.v5-formal-check-options button:disabled {
  color: var(--text-soft);
}
