/* =========================================================================
 * pages/tutorials.css — стили раздела «Курсы» (этап 10.3)
 *
 * Собран из четырёх инлайн-<style>, которые раньше жили прямо в шаблонах:
 *     tutorials/series_list.html      → секция 1
 *     tutorials/series_detail.html    → секция 2
 *     tutorials/bookmark_list.html    → секция 3
 *     tutorials/chapter_detail.html   → секция 4 (включая партиалы
 *         _playground.html, _chapter_tasks.html, _task_item.html — они
 *         инклюдятся в главу и своих <style> не имели)
 *
 * ПЕРЕНОС КАК ЕСТЬ: значения, порядок правил и медиазапросы сохранены.
 * Разрешённые (и единственные) отличия от оригинала:
 *   1) убраны fallback'и из var(--токен, #hex) — см. блок «про fallback'и» ниже;
 *   2) три группы селекторов доскоплены — см. блок «про склейку» ниже.
 *
 * ---------------------------------------------------------------------
 * ПРО FALLBACK'И (правило этапа).
 * В инлайн-стилях раздела было 19 записей вида var(--border, #dee2e6).
 * Именно этот паттерн спрятал баг главы курса (коммит c84bbc0): имя токена
 * было опечаткой, правило молча брало fallback, и глава в тёмной теме
 * оставалась со светлой плашкой при контрасте 1.16:1. Все токены, которые
 * тут используются, объявлены в static/css/00-tokens.css и в :root, и в
 * :root[data-theme="dark"] — то есть существуют гарантированно, и
 * «страховка» страховала только от опечатки в имени. Новые не добавлять.
 *
 * ---------------------------------------------------------------------
 * ПРО СКЛЕЙКУ ЧЕТЫРЁХ ФАЙЛОВ В ОДИН.
 * Пока стили жили в <style> внутри шаблонов, одинаковые имена классов на
 * разных страницах не пересекались. В общем файле пересекаются, поэтому три
 * группы пришлось доскопить предком — иначе правило с одной страницы утекло
 * бы на другую и это была бы регрессия переноса, а не перенос:
 *   .series-completion-bar   — в списке серий 4px, в детали серии 8px;
 *                              скоупим .series-card / .series-completion-block;
 *   .series-completion-value — правило есть только у детали серии, но класс
 *                              присутствует и в карточке списка;
 *                              скоупим .series-completion-label;
 *   .series-toc*             — правила есть только у главы, но тот же TOC
 *                              рендерит {% show_chapter_toc %} на детали
 *                              серии, где он никогда не был стилизован;
 *                              скоупим .chapter-toc-aside.
 * Переименований классов нет — только уточнение предка.
 * ========================================================================= */


/* =========================================================================
 * 1. СПИСОК СЕРИЙ — tutorials/series_list.html
 * ========================================================================= */

.series-hero {
    display: grid;
    grid-template-columns: 1.6fr 1fr;
    gap: 1.5rem;
    margin: 1.5rem 0 2rem;
    padding: 1.5rem;
    border-radius: 16px;
    /* Оба стопа градиента — фон под белым текстом, поэтому оба на
       --primary-surface, а не на --primary (акцентная роль светлее и
       давала в тёмной теме 2.63:1 при норме AA 4.5:1).
       Правый стоп раньше был захардкоженным rgba(13,110,253,0.7): он
       не реагировал на тему и в светлой давал 2.93:1. Полупрозрачность
       здесь тоже не годится — альфа подмешивает фон страницы, и в
       светлой теме даже 0.95 вытягивает лишь 4.65:1. Берём непрозрачный
       --primary-surface-hover-raw (светлая 0.45, тёмная 0.43): 7.01:1 и
       7.62:1, а перепад светлоты 0.53→0.45 / 0.50→0.43 сохраняет
       видимый градиент.
       Fallback #0d6efd убран намеренно: токен объявлен в :root всегда,
       а fallback лишь маскировал бы опечатку в имени переменной. */
    background: linear-gradient(135deg,
                var(--primary-surface) 0%,
                oklch(var(--primary-surface-hover-raw)) 100%);
    color: #fff;
}

/* Кольцо фокуса ВНУТРИ hero — белое, а не var(--ring).

   Базовое правило (03-components.css) красит обводку в --ring = --primary, и
   на обычной странице это верно: 4.86:1 к фону, 5.25:1 к карточке. Но hero
   залит --primary-surface, то есть ТОЙ ЖЕ краской, только на другой светлоте:
   --primary 0.52 против стопов градиента 0.53 → 0.45 в светлой теме. Замерено
   в браузере: контраст кольца к худшему стопу 1.04:1 в светлой теме и 2.18:1
   в тёмной при норме 3:1 (SC 1.4.11) — то есть на светлой теме обводки
   практически НЕ ВИДНО, она сливается с подложкой. Под удар попадали все
   интерактивные элементы блока: заголовок-ссылка, кнопки «Перейти к серии» и
   «Продолжить», список свежих глав.

   Белый здесь не хардкод «мимо темы», а тот же осознанный выбор, что и
   `color: #fff` строкой выше: hero — единственная поверхность сайта, которая
   тёмно-синяя в ОБЕИХ темах, поэтому её собственный передний план от темы не
   зависит. Замерено к ХУДШЕМУ стопу градиента (обводка пересекает его целиком,
   поэтому считать надо по худшему, а не по среднему): 5.07:1 в светлой теме,
   5.71:1 в тёмной.

   Селектор (0,2,0) равен `.btn:focus-visible` из ядра, но pages/tutorials.css
   подключается позже — значит выигрывает и перекрашивает кольцо кнопкам тоже.
   Переопределяем только outline-color: толщина и offset остаются общими. */
.series-hero :focus-visible {
    outline-color: #fff;
}

.series-hero-badge {
    display: inline-block;
    font-size: 0.75rem;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.05em;
    opacity: 0.9;
}
.series-hero-title { font-size: 1.8rem; margin: 0.5rem 0; }
.series-hero-title a { color: inherit; text-decoration: none; }
.series-hero-title a:hover { text-decoration: underline; }
.series-hero-desc { margin: 0.5rem 0 1rem; opacity: 0.95; }
.series-hero-actions { display: flex; gap: 0.75rem; flex-wrap: wrap; }
.series-hero-recent {
    background: rgba(255, 255, 255, 0.12);
    border-radius: 12px;
    padding: 1rem 1.25rem;
}
.series-hero-recent--full { grid-column: 1 / -1; }
.series-hero-recent-title { font-size: 1rem; margin: 0 0 0.75rem; }
.series-hero-recent-list { list-style: none; padding: 0; margin: 0; }
.series-hero-recent-list li { padding: 0.4rem 0; border-bottom: 1px solid rgba(255, 255, 255, 0.15); }
.series-hero-recent-list li:last-child { border-bottom: none; }
.series-hero-recent-list a { color: inherit; text-decoration: none; display: flex; flex-direction: column; }
.series-hero-recent-list a:hover .recent-chapter-title { text-decoration: underline; }
.recent-chapter-title { font-weight: 600; font-size: 0.95rem; }
.recent-chapter-meta { font-size: 0.8rem; opacity: 0.85; }

.series-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
    gap: 1.5rem;
    margin-top: 1.5rem;
}
.series-card {
    border: 1px solid var(--border);
    border-radius: 12px;
    padding: 1.25rem;
    background: var(--card);
    transition: transform 0.2s ease, box-shadow 0.2s ease;
}
.series-card:hover { transform: translateY(-3px); box-shadow: 0 4px 14px rgba(0,0,0,0.08); }
.series-card-link { text-decoration: none; color: inherit; display: block; }
.series-card-title { font-size: 1.25rem; margin: 0 0 0.5rem; }
.series-card-desc { color: var(--muted-foreground); margin: 0 0 1rem; }
.series-card-meta {
    display: flex; flex-wrap: wrap; gap: 0.75rem;
    font-size: 0.875rem; color: var(--muted-foreground);
}
/* Скоуп .series-card — на детали серии та же полоса толще (см. секцию 2). */
.series-card .series-completion-bar {
    margin-top: 0.75rem;
    height: 4px;
    border-radius: 2px;
    background: var(--border);
    overflow: hidden;
}
.series-resume-inline {
    display: inline-block;
    margin-top: 0.6rem;
    font-size: 0.8rem;
    color: var(--primary);
    font-weight: 600;
}
@media (max-width: 768px) {
    .series-hero { grid-template-columns: 1fr; }
}


/* =========================================================================
 * 2. ДЕТАЛЬ СЕРИИ — tutorials/series_detail.html
 * ========================================================================= */

.series-header { margin-bottom: 1.5rem; }
.series-title { font-size: 1.9rem; margin: 0 0 0.5rem; }
.series-description { color: var(--muted-foreground); }
.series-meta-row {
    display: flex; gap: 1.25rem; flex-wrap: wrap;
    margin: 0.75rem 0;
    font-size: 0.875rem;
    color: var(--muted-foreground);
}
.series-completion-block { margin: 1rem 0; max-width: 480px; }
.series-completion-label {
    display: flex; justify-content: space-between;
    font-size: 0.85rem; color: var(--muted-foreground);
    margin-bottom: 0.3rem;
}
/* Скоуп .series-completion-label — в карточке списка серий тот же класс
   .series-completion-value жил без собственного правила и красился как
   обычный текст; безусловное правило перекрасило бы его. */
.series-completion-label .series-completion-value { font-weight: 600; color: var(--primary); }
/* Скоуп .series-completion-block — в списке серий полоса тоньше (секция 1). */
.series-completion-block .series-completion-bar {
    height: 8px;
    border-radius: 4px;
    background: var(--border);
    overflow: hidden;
}
/* Заливка полосы в обоих шаблонах была описана одинаково — склеена без дельты. */
.series-completion-fill {
    height: 100%;
    background: var(--primary);
    transition: width 0.3s ease;
}
.series-resume { margin: 1rem 0; }
.series-chapters-list { list-style: none; padding: 0; margin-top: 1.5rem; }
.series-chapter-row { padding: 0.6rem 0; border-bottom: 1px solid var(--border); }
.series-chapter-row a { text-decoration: none; color: inherit; }
.series-chapter-row a:hover { color: var(--primary); }
.series-chapter-order { color: var(--muted-foreground); margin-right: 0.5rem; }
.series-chapter-badges { display: inline-flex; gap: 0.4rem; margin-left: 0.5rem; }
/* Этап 10.4 — долг, помеченный в 10.3 как «вход для 10.4», закрыт.
   Было: `background: rgba(25,135,84,0.12); color: #198754` — та же пара
   хардкодов, что уже чинили у .chapter-practice-badge. Дефект двойной:
     1) 3.89:1 в светлой и 4.12:1 в тёмной теме при норме AA 4.5:1 — бейдж
        «прочитано/практика» читался ХУЖЕ обычного текста рядом
        (после правки замерено 4.77:1 и 7.53:1);
     2) обе краски темонезависимы, поэтому в тёмной теме плашка оставалась
        светло-зелёной пятном на тёмной странице.
   Лечится ровно как у .chapter-practice-badge: цвет текста — из
   темозависимого --success-text, а тинт — ТА ЖЕ краска с альфой через
   «сырой» --success-text-raw. Один источник на обе роли: пара не разъедется
   при следующей правке палитры, чего два независимых хардкода не гарантируют. */
.series-chapter-badge {
    font-size: 0.72rem; padding: 0.1rem 0.45rem; border-radius: 999px;
    background: oklch(var(--success-text-raw) / 0.12);
    color: var(--success-text);
    white-space: nowrap;
}
.series-chapter-badge.is-off { background: var(--border); color: var(--muted-foreground); }


/* =========================================================================
 * 3. ЗАКЛАДКИ — tutorials/bookmark_list.html
 * ========================================================================= */

.bookmark-list { list-style: none; padding: 0; }
.bookmark-item { padding: 0.75rem 0; border-bottom: 1px solid var(--border); }
.bookmark-item a { text-decoration: none; color: inherit; }


/* =========================================================================
 * 4. ГЛАВА КУРСА — tutorials/chapter_detail.html
 *    (+ партиалы _playground.html, _chapter_tasks.html, _task_item.html)
 *
 * ВАЖНО про переменные в этой секции (этап 10.1-bis).
 *
 * Раньше вся страница красилась через `var(--card)`,
 * `var(--muted)` и `var(--muted-foreground)`.
 * Токенов с такими именами в проекте НЕТ и никогда не было — они
 * пришли из чужой дизайн-системы. Браузер на несуществующее имя молча
 * берёт fallback, поэтому фон плашек оставался белым В ОБЕИХ ТЕМАХ,
 * а текст поверх шёл из var(--foreground), в тёмной теме почти белого:
 * замеренный контраст 1.16:1 при норме 4.5:1. Глава курса — основной
 * обучающий контент сайта — в тёмной теме была нечитаема.
 *
 * Починено СОПОСТАВЛЕНИЕМ ПО СМЫСЛУ с существующими токенами, а не
 * объявлением алиасов --surface/--surface-muted/--text-muted: второе
 * оставило бы в системе два имени для одной роли, и следующий шаблон
 * снова угадывал бы, каким пользоваться.
 *     --surface       -> --card             (поверхность карточки)
 *     --surface-muted -> --muted            (приглушённая плашка)
 *     --text-muted    -> --muted-foreground (вторичный текст)
 * Исключение — консоль playground'а: см. .tut-pg-console ниже.
 *
 * FALLBACK'И ИЗ var() УБРАНЫ НАМЕРЕННО, и новые добавлять не нужно.
 * Именно они спрятали опечатку: без них правило просто не применилось
 * бы, блок остался бы прозрачным и баг увидели бы в первый же день.
 * Все используемые здесь токены объявлены в static/css/00-tokens.css в :root
 * и в :root[data-theme="dark"] — то есть существуют гарантированно,
 * и «страховка» на самом деле страхует только от опечатки в имени.
 * ========================================================================= */

/* Контент главы — на полной ширине; список глав живёт внизу
   (свёрнутый по умолчанию на мобильных, раскрытый на десктопе). */
.series-chapters-collapse {
    margin-top: 2.5rem;
    padding: 1.25rem 1.5rem;
    border: 1px solid var(--border);
    border-radius: 0.5rem;
    background: var(--card);
}
/* Десктоп: блок раскрыт всегда, toggle спрятан (визуально неудаляем —
   details/summary всё ещё контролируют состояние для accessibility). */
@media (min-width: 901px) {
    .series-chapters-collapse[open] > summary { display: none; }
}
.series-chapters-toggle {
    display: flex; align-items: center; gap: 0.5rem;
    cursor: pointer; list-style: none;
    font-weight: 600; font-size: 1.05rem;
    color: var(--primary);
}
.series-chapters-toggle::-webkit-details-marker { display: none; }
.series-chapters-toggle .series-chapters-count {
    color: var(--muted-foreground); font-weight: 400; font-size: 0.85rem;
}
.chapter-toc-aside { margin-top: 1rem; }
/* Список глав: на широких экранах — несколько колонок для компактности,
   на узких — одна.
   Скоуп .chapter-toc-aside — тот же {% show_chapter_toc %} рендерится и на
   детали серии, где TOC никогда не имел этих правил (жили в <style> главы). */
.chapter-toc-aside .series-toc { margin: 0; border-left: 3px solid var(--border); padding-left: 0.75rem; }
.chapter-toc-aside .series-toc-list { list-style: none; padding: 0; margin: 0; }
@media (min-width: 901px) {
    .chapter-toc-aside .series-toc-list {
        columns: 3; column-gap: 2rem;
    }
    .chapter-toc-aside .series-toc-item { break-inside: avoid; }
}
.chapter-toc-aside .series-toc-item a { display: flex; gap: 0.4rem; text-decoration: none; color: var(--muted-foreground); padding: 0.3rem 0; font-size: 0.9rem; }
.chapter-toc-aside .series-toc-item.current > a,
.chapter-toc-aside .series-toc-item.active > a { color: var(--primary); font-weight: 600; }
.chapter-toc-aside .series-toc-order { color: var(--muted-foreground); flex-shrink: 0; }

/* Reading-progress bar в шапке */
.chapter-progress-bar {
    position: fixed; top: 0; left: 0; right: 0; height: 4px;
    background: transparent; z-index: 1080; pointer-events: none;
}
.chapter-progress-bar-fill {
    height: 100%; background: var(--primary);
    transition: width 0.15s linear;
}

.chapter-nav { display: flex; justify-content: space-between; align-items: center; gap: 1rem; margin-top: 2.5rem; padding-top: 1.5rem; border-top: 1px solid var(--border); flex-wrap: wrap; }
.chapter-nav-prev, .chapter-nav-next { text-decoration: none; color: var(--primary); font-weight: 600; }
.chapter-series-complete { color: var(--success-text); font-weight: 700; }
.chapter-complete-section { margin: 1rem 0 1.5rem; }

/* УДАЛЕНЫ (этап 10.6) три местных блока @media (prefers-reduced-motion: reduce)
   — здесь и ещё в двух местах ниже по файлу. Они перечисляли поимённо
   .chapter-progress-bar-fill, .chapter-toc-aside .series-toc .active,
   .tut-diff-panel, .code-block-wrap > pre, .tut-pg-editor, .tut-pg-console и
   .chapter-complete-btn.celebrate-animate.

   Ровно то же самое теперь делает одно универсальное правило в 01-base.css:
   `*, *::before, *::after` со списком *-duration/-delay, iteration-count и
   scroll-behavior. Оно объявлено в базовом слое и бьёт по всему документу,
   поэтому постраничные копии стали дублем — а дубль в CSS не безобиден:
   поимённый список отстаёт от разметки молча (свойство добавили, в список не
   внесли — и движение осталось).

   Дубль был не только лишним, но и ВРЕДНЫМ. Пять из семи объявлений стояли
   с `transition: none !important` / `animation: none !important`, то есть
   перебивали базовое правило. Базовое намеренно сводит длительность к 0.01ms,
   а не к none: при `none` события animationend/transitionend не приходят
   вовсе, и скрипт, который ждёт конца анимации, оставляет интерфейс в
   промежуточном состоянии. Сегодня таких слушателей в static/js/ нет (проверено
   грепом animationend/transitionend/getAnimations на 2026-07), но именно
   поэтому расхождение и держалось незамеченным. Подробный разбор — в шапке
   блока в 01-base.css. */

/* === Diff-виджеты (этап 4) === */
.tut-diff {
    border: 1px solid var(--border);
    border-radius: 0.5rem;
    margin: 1.25rem 0;
    overflow: hidden;
    background: var(--card);
}
.tut-diff-panels { position: relative; }
.tut-diff-panel {
    transition: opacity 0.35s ease, transform 0.35s ease;
}
/* Показываем только текущую панель по data-state. */
.tut-diff[data-state="before"] .tut-diff-panel[data-diff-role="before"] { display: block; }
.tut-diff[data-state="before"] .tut-diff-panel[data-diff-role="after"]  { display: none; }
.tut-diff[data-state="after"]  .tut-diff-panel[data-diff-role="before"] { display: none; }
.tut-diff[data-state="after"]  .tut-diff-panel[data-diff-role="after"]  { display: block; }
.tut-diff-label {
    display: inline-block;
    font-size: 0.75rem; text-transform: uppercase; letter-spacing: 0.04em;
    color: var(--muted-foreground);
    padding: 0.25rem 0.5rem;
}
.tut-diff-pre { margin: 0; border-radius: 0; }
.tut-diff-toggle { margin: 0.5rem; }

/* Celebration: gold tick keyframes (этап 4.3) */
.chapter-complete-btn.celebrate-animate {
    animation: celebrate-pop 0.6s ease;
}
/* Золото на 40% кадра — вспышка на 0.24s, а не состояние: под нормы
   контраста не попадает (текст кнопки не «читают» в этот момент, а
   prefers-reduced-motion анимацию и вовсе снимает). Токеном не красим
   намеренно — это эффект, а не роль в палитре. */
@keyframes celebrate-pop {
    0%   { transform: scale(1); }
    40%  { transform: scale(1.18); color: #ffca2c; border-color: #ffca2c; }
    70%  { transform: scale(0.96); }
    100% { transform: scale(1); }
}

/* Copy-кнопка (этап 4.4) */
pre { position: relative; }
.code-copy-btn {
    position: absolute; top: 0.35rem; right: 0.35rem;
    opacity: 0; transition: opacity 0.15s ease; z-index: 2;
    background: var(--card) !important;
}
/* Проявление кнопки было завязано ТОЛЬКО на ховер родительского <pre>, то есть
   только на мышь. Клавиатурой кнопка достижима (обычный <button>, из потока не
   вынут) и обводку по :focus-visible получает — но при opacity: 0 не видно ни
   кнопки, ни обводки: opacity действует и на outline. Человек, идущий по Tab,
   упирался в невидимую остановку (WCAG 2.1 SC 2.4.7).

   Лечится не отдельным правилом, а добавлением :focus-visible в тот же список
   селекторов: так проявление кнопки и её обводка не могут разъехаться при
   следующей правке. */
pre:hover .code-copy-btn,
.code-copy-btn:focus-visible { opacity: 1; }
.code-copy-btn-done { color: var(--success-text) !important; border-color: var(--success-text) !important; }

/* Сворачиваемые длинные блоки кода (этап 4.4) */
.code-block-wrap { position: relative; }
.code-block-wrap > pre { margin: 0; max-height: 26rem; overflow: hidden; transition: max-height 0.3s ease; }
.code-block-wrap.code-block-expanded > pre { max-height: none; }
.code-block-toggle {
    display: block; width: 100%; border: none;
    border-top: 1px solid var(--border);
    background: var(--muted); color: var(--muted-foreground);
    padding: 0.4rem; cursor: pointer;
}
/* УДАЛЁН второй блок prefers-reduced-motion — см. комментарий выше по файлу. */

/* === Playground (этап 5) === */
.tut-playground {
    border: 1px solid var(--border);
    border-radius: 0.5rem;
    margin: 1.5rem 0;
    background: var(--card);
    overflow: hidden;
}
.tut-pg-toolbar {
    display: flex; gap: 0.4rem; flex-wrap: wrap;
    padding: 0.4rem; border-bottom: 1px solid var(--border);
    background: var(--muted);
}
.tut-pg-body {
    display: grid; grid-template-columns: 1fr 1fr;
    min-height: 280px; height: 360px;
}
@media (max-width: 760px) { .tut-pg-body { grid-template-columns: 1fr; height: auto; } }
.tut-pg-editors {
    display: flex; flex-direction: column;
    border-right: 1px solid var(--border);
}
@media (max-width: 760px) { .tut-pg-editors { border-right: none; border-bottom: 1px solid var(--border); } }
.tut-pg-editor-group { display: flex; flex-direction: column; flex: 1; min-height: 0; border-bottom: 1px solid var(--border); }
.tut-pg-editor-label {
    font-size: 0.7rem; text-transform: uppercase; letter-spacing: 0.05em;
    color: var(--muted-foreground); padding: 0.2rem 0.5rem;
    background: var(--muted);
}
/* Три <textarea> редактора (HTML/CSS/JS) внутри .tut-pg-editors.

   ЗДЕСЬ СТОЯЛ `outline: none` — БЕЗУСЛОВНО, не в :focus, и без замены. То есть
   поля, в которых человек и проводит всё время на странице главы, не имели
   индикатора фокуса вообще: браузерную обводку сняли, взамен не дали ничего
   (WCAG 2.1 SC 2.4.7 Focus Visible, уровень AA). Мимо этапа 10.6 правило
   прошло потому, что аудит смотрел ядро (00…04), а каталог pages/ — нет; при
   этом pages/*.css подключается ПОСЛЕ ядра, а специфичность у `.tut-pg-editor`
   (0,1,0) равна базовому `:focus-visible` (0,1,0) — то есть местное правило
   базовое как раз перебивало.

   `outline: none` снят целиком (как у .search-input и .header-search input в
   03-components.css), кольцо ниже — своё, потому что базовое не годится:
   у него offset +2px, а редакторы заполняют свои ячейки от края до края и
   лежат в .tut-playground с overflow: hidden. Внешняя обводка была бы обрезана
   контейнером и вдобавок наехала бы на соседний редактор. Отрицательный offset
   рисует её ВНУТРЬ поля.

   Цвет — var(--ring), как и везде. Замерено в браузере: 5.27:1 к фону поля в
   светлой теме и 7.75:1 в тёмной при норме 3:1 для нетекстовых индикаторов
   (SC 1.4.11). */
.tut-pg-editor {
    flex: 1; min-height: 80px; width: 100%;
    border: none; resize: none;
    font-family: 'JetBrains Mono', ui-monospace, monospace;
    font-size: 0.85rem; padding: 0.4rem; background: transparent;
    color: inherit;
}
.tut-pg-editor:focus-visible {
    outline: 2px solid var(--ring);
    outline-offset: -2px;
}
.tut-pg-editor-group .cm-editor { flex: 1; min-height: 0; max-height: 100%; overflow: hidden; }
.tut-pg-editor-group .cm-scroller { overflow: auto; }
.tut-pg-output-wrap { position: relative; display: flex; flex-direction: column; }
/* Предпросмотр остаётся белым в обеих темах намеренно: внутри iframe —
   HTML студента, который пишется в расчёте на белый лист. Красить его
   по теме сайта значило бы подменять результат работы студента. */
.tut-pg-output {
    flex: 1; width: 100%; border: none; background: #fff;
    min-height: 200px;
}
/* Консоль — единственное место, где смысловой замены на --muted НЕ
   было. Здесь фоном стоял тот же несуществующий токен, но с ТЁМНЫМ
   fallback'ом (#1e1e1e) — то есть замысел был «плашка тёмная всегда»,
   и он даже работал (14:1),
   просто держался на несуществующем имени. Подставить сюда --muted
   значило бы сломать работающее: в светлой теме плашка стала бы светлой,
   а строки консоли красятся акцентами под тёмный фон (см. ниже) — они
   упали бы с 6–10:1 до 1.4–2.1:1. Поэтому роль «терминал» вынесена в
   свои токены --console-* (00-tokens.css, :root, без переопределения
   в тёмной теме) — намерение стало явным и перестало зависеть от
   опечатки в имени переменной. */
.tut-pg-console {
    flex: 1; margin: 0; padding: 0.4rem;
    font-family: 'JetBrains Mono', ui-monospace, monospace;
    font-size: 0.78rem; overflow: auto;
    background: var(--console-bg); color: var(--console-fg);
    border-top: 1px solid var(--console-border);
}
/* Консоль прокручиваемая (overflow: auto), а прокручиваемую область Chrome
   делает достижимой с клавиатуры сам — по Tab на неё встаёт фокус, чтобы
   содержимое можно было пролистать стрелками. Значит ей нужен видимый
   индикатор. Кольцо внутрь по той же причине, что у редакторов: консоль
   занимает свою ячейку целиком, а .tut-playground объявлен с overflow: hidden
   и внешнюю обводку обрезал бы.

   Цвет — --console-fg, а не общий --ring, по той же причине, по какой у консоли
   вообще своя тройка токенов: её плашка тёмная в ОБЕИХ темах, и передний план
   на ней от темы зависеть не должен. --ring на --console-bg замерен и даёт
   3.27:1 в светлой теме — норму 3:1 проходит, но впритык, и любая будущая
   правка светлоты --primary съест этот запас молча (плашка-то за темой не
   идёт). --console-fg даёт 14.06:1 и одинаков в обеих темах. */
.tut-pg-console:focus-visible {
    outline: 2px solid var(--console-fg);
    outline-offset: -2px;
}
.tut-pg-console-line { white-space: pre-wrap; word-break: break-word; padding: 0.1rem 0; }
/* Акценты подобраны под --console-bg и вместе с ней темонезависимы:
   10.6:1 / 6.2:1 / 7.0:1. Токенами темы их красить нельзя — плашка
   под ними в светлой теме не светлеет. */
.tut-pg-console-warn { color: #ffc107; }
.tut-pg-console-error { color: #ff6b6b; }
.tut-pg-console-info { color: #4dabf7; }
/* Fullscreen: растягиваем layout под весь экран. */
.tut-playground:fullscreen { height: 100vh; border-radius: 0; }
.tut-playground:fullscreen .tut-pg-body { height: calc(100vh - 48px); }
/* УДАЛЁН третий блок prefers-reduced-motion — см. комментарий выше по файлу. */

/* === Интерактивные задания (этап 9) === */
.chapter-tasks {
    margin-top: 2.5rem;
    padding: 1.25rem 1.5rem 1.5rem;
    border: 1px solid var(--border);
    border-radius: 12px;
    background: var(--card);
}
.chapter-tasks-head {
    display: flex; align-items: baseline; justify-content: space-between;
    gap: 1rem; flex-wrap: wrap;
}
.chapter-tasks-head h2 { margin: 0; }
.chapter-tasks-progress {
    font-size: 0.9rem; font-weight: 600;
    color: var(--muted-foreground); white-space: nowrap;
}
.chapter-tasks-lead { margin: 0.5rem 0 1rem; font-size: 0.9rem; }
/* Плашка «практика пройдена». Хардкод #198754 на rgba(25,135,84,.12)
   давал 3.89:1 в светлой и 4.23:1 в тёмной — подтверждение выполненной
   работы читалось ХУЖЕ обычного текста. Оба цвета теперь идут из одного
   темозависимого токена: тинт — та же краска с альфой (для этого и
   нужен «сырой» --success-text-raw), поэтому пара не разъедется. */
.chapter-practice-badge {
    margin-bottom: 1rem; padding: 0.6rem 0.9rem;
    border-radius: 8px; font-weight: 600;
    background: oklch(var(--success-text-raw) / 0.12);
    color: var(--success-text);
}
.chapter-practice-badge.is-hidden { display: none; }
.chapter-tasks-list { margin: 0; padding-left: 1.4rem; }
.chapter-task { margin-bottom: 1.5rem; }
.chapter-task.is-solved .chapter-task-prompt { opacity: 0.75; }
.chapter-task-prompt { margin-bottom: 0.6rem; }
.chapter-task-form { display: flex; gap: 0.5rem; flex-wrap: wrap; }
.chapter-task-input { max-width: 320px; font-family: 'JetBrains Mono', ui-monospace, monospace; }
.chapter-task-ok { font-weight: 600; color: var(--success-text); }
.chapter-task-msg { margin-top: 0.5rem; font-size: 0.88rem; }
/* Было #dc3545 — 4.53:1 на белом, но на тёмной карточке цвет не менялся.
   --destructive-text для того и заведён: красный текста в каждой теме
   свой (5.96:1 / 5.36:1). */
.chapter-task-msg.is-error { color: var(--destructive-text); }
.chapter-task-hint { margin-top: 0.5rem; font-size: 0.88rem; }
.chapter-task-hint summary { cursor: pointer; color: var(--muted-foreground); }
.chapter-task-hint-body { margin-top: 0.4rem; padding-left: 0.6rem; border-left: 2px solid var(--border); }
.chapter-tasks-anon { margin-top: 1rem; margin-bottom: 0; }
@media (max-width: 600px) {
    .chapter-tasks { padding: 1rem; }
    .chapter-task-input { max-width: 100%; }
}
