/* ============================================================
   Курс "Алгоритмы и структуры данных" - стили поверх /base.css
   ============================================================ */

/* ==================== ХЛЕБНЫЕ КРОШКИ ==================== */
.crumbs {
    max-width: 800px;
    margin: 24px auto 0;
    padding: 0 24px;
    font-size: 0.85rem;
    color: var(--text-tertiary);
}

/* На странице курса (витрина) крошки выравниваются по контенту шириной 1100 */
.crumbs.crumbs-wide {
    max-width: 1100px;
}

.crumbs a {
    color: var(--text-secondary);
    text-decoration: none;
}

.crumbs a:hover {
    color: var(--color-primary);
}

/* Бренд в начале крошек - айдентика на страницах Класса */
.crumbs .brand {
    font-weight: 700;
    color: var(--color-primary);
}

.crumbs .brand:hover {
    color: var(--color-primary-dark);
}

/* ==================== ОБЩАЯ КОЛОНКА ==================== */
.wrap {
    max-width: 800px;
    margin: 0 auto;
    padding: 16px 24px 48px;
}

/* ==================== СТРАНИЦА КУРСА ==================== */
/* Единый вертикальный ритм шапки курса: 16px между всеми блоками
   (крошки - h1 - лид - плашки - "Программа курса" - таблица).
   Верхний отступ дает .course-layout, здесь 0 - чтобы h1 и правая
   карточка начинались на одном уровне. */
.course-header {
    padding: 0;
}

.course-header h1 {
    font-size: 2.1rem;
    font-weight: 800;
    line-height: 1.25;
    margin-bottom: 16px;
}

.course-header .lead {
    color: var(--text-secondary);
    font-size: 1.05rem;
    max-width: 640px;
}

.meta-chips {
    display: flex;
    gap: 10px;
    flex-wrap: wrap;
    margin: 16px 0 0;
}

.meta-chip {
    font-size: 0.85rem;
    color: var(--text-secondary);
    background: var(--bg-card);
    border: 1px solid var(--border-color);
    border-radius: 20px;
    padding: 4px 14px;
}

.meta-chip.free {
    color: var(--color-success);
    background: #f0fdf4;
    border-color: #bbf7d0;
    font-weight: 600;
}

/* ==================== РАСКЛАДКА СТРАНИЦЫ КУРСА (витрина + боковой блок) ====================
   Только для index.html курса; страницы уроков используют узкий .wrap. */
.course-index {
    max-width: 1100px;
    margin: 0 auto;
    padding: 0 24px 48px;
}

/* Единственный отступ крошки -> контент (16px); обе колонки стартуют с одной линии */
.course-layout {
    display: grid;
    grid-template-columns: 1fr 300px;
    gap: 32px;
    align-items: start;
    margin-top: 16px;
}

/* Боковой блок "Бесплатно / Продолжить / прогресс" */
.enroll-card {
    background: var(--bg-card);
    border: 1px solid var(--border-color);
    border-radius: var(--radius-lg);
    /* Верхний паддинг подобран так, чтобы визуальный верх буквы "Б" совпадал
       с верхом буквы "А" в h1 (у price тот же line-height:1.25, что и у h1,
       поэтому остаточную разницу компенсирует только сам padding-top). */
    padding: 4px 24px 24px;
    position: sticky;
    top: 20px;
    box-shadow: var(--shadow-sm);
}

.enroll-card .price {
    font-size: 1.5rem;
    font-weight: 800;
    line-height: 1.25;
    margin-bottom: 4px;
}

.enroll-card .price-note {
    font-size: 0.85rem;
    color: var(--text-secondary);
    margin-bottom: 16px;
}

.enroll-card .btn {
    text-decoration: none;
    text-align: center;
}

.enroll-hint {
    margin-top: 14px;
    padding-top: 12px;
    border-top: 1px solid var(--border-color);
    font-size: 0.82rem;
    color: var(--text-secondary);
    line-height: 1.5;
}

.progress-bar {
    height: 6px;
    border-radius: 3px;
    background: var(--bg-tertiary);
    overflow: hidden;
    margin-top: 14px;
}

.progress-bar i {
    display: block;
    height: 100%;
    background: var(--color-primary);
    border-radius: 3px;
    transition: width 0.3s ease;
}

.progress-note {
    font-size: 0.8rem;
    color: var(--text-secondary);
    margin-top: 6px;
}

@media (max-width: 900px) {
    .course-layout {
        grid-template-columns: 1fr;
    }

    .enroll-card {
        position: static;
        order: -1;
        margin-bottom: 8px;
    }
}

/* Программа курса */
.program {
    margin-top: 0;
}


.program details {
    background: var(--bg-card);
    border: 1px solid var(--border-color);
    border-radius: var(--radius-md);
    margin-bottom: 10px;
    overflow: hidden;
    scroll-margin-top: 70px;
}

.program summary {
    padding: 16px 20px;
    font-weight: 600;
    cursor: pointer;
    list-style: none;
    display: flex;
    align-items: center;
    gap: 12px;
}

.program summary::-webkit-details-marker { display: none; }
.program summary::after { content: '▾'; margin-left: 12px; color: var(--text-tertiary); }
.program details[open] summary::after { content: '▴'; }

.topic-num {
    width: 28px;
    height: 28px;
    border-radius: 50%;
    background: var(--color-primary-bg);
    color: var(--color-primary-dark);
    font-size: 0.85rem;
    font-weight: 700;
    display: flex;
    align-items: center;
    justify-content: center;
    flex-shrink: 0;
}

.sum-count {
    margin-left: auto;
    font-size: 0.78rem;
    font-weight: 500;
    color: var(--text-tertiary);
    white-space: nowrap;
}

/* Список уроков - ДОСЛОВНО из prototype/proto.css + variants.css (c-course.html).
   Не менять здесь ничего без сверки с прототипом. */
/* padding-bottom - наше отличие от прототипа: немного воздуха после последнего
   урока (10px было тесно к краю карточки, 10+10 делало нижнюю строку выше остальных) */
.lesson-list { list-style: none; border-top: 1px solid var(--border-color); padding-bottom: 5px; }

.lesson-list li a {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 10px 20px 10px 60px;
    color: var(--text-secondary);
    text-decoration: none;
    font-size: 0.92rem;
    border-bottom: 1px solid var(--bg-tertiary);
}

.lesson-list li:last-child a { border-bottom: none; }
.lesson-list li a:hover { background: var(--bg-secondary); color: var(--color-primary); }

.lesson-list .done-mark {
    width: 16px;
    flex-shrink: 0;
    font-weight: 700;
    color: var(--border-color);
}

.lesson-list a.is-done .done-mark { color: var(--color-success); }
/* ==================== СТРАНИЦА УРОКА ==================== */
.lesson h1 {
    font-size: 1.9rem;
    font-weight: 800;
    line-height: 1.25;
    margin: 8px 0 6px;
}

.lesson-meta {
    font-size: 0.85rem;
    color: var(--text-tertiary);
    margin-bottom: 24px;
}

.lesson h2 {
    font-size: 1.3rem;
    font-weight: 700;
    margin: 34px 0 12px;
}

.lesson h3 {
    font-size: 1.05rem;
    font-weight: 700;
    margin: 24px 0 10px;
}

.lesson p {
    margin-bottom: 14px;
}

.lesson ul,
.lesson ol {
    margin: 0 0 14px 0;
    padding-left: 26px;
}

.lesson li {
    margin-bottom: 6px;
}

.lesson code {
    font-family: var(--font-mono);
    font-size: 0.9em;
    background: var(--bg-tertiary);
    border: 1px solid var(--border-color);
    border-radius: 4px;
    padding: 1px 5px;
}

.lesson pre {
    background: var(--bg-card);
    border: 1px solid var(--border-color);
    border-radius: var(--radius-md);
    padding: 16px;
    overflow-x: auto;
    margin-bottom: 16px;
    line-height: 1.5;
}

.lesson pre code {
    background: none;
    border: none;
    padding: 0;
    font-size: 0.88rem;
}

/* Плашка "Суть" в начале урока */
.core {
    background: var(--color-primary-bg);
    border-left: 3px solid var(--color-primary);
    border-radius: var(--radius-sm);
    padding: 14px 16px;
    margin: 18px 0 24px;
}

/* Блок-отступление (сравнение с Python и т.п.) */
.aside-note {
    background: var(--bg-secondary);
    border: 1px solid var(--border-color);
    border-radius: var(--radius-md);
    padding: 14px 16px;
    margin: 18px 0;
    font-size: 0.95rem;
    color: var(--text-secondary);
}

.aside-note strong {
    color: var(--text-primary);
}

/* Итоги урока */
.keypoints {
    background: var(--bg-card);
    border: 1px solid var(--border-color);
    border-radius: var(--radius-lg);
    padding: 20px 24px;
    margin: 32px 0;
}

.keypoints h2 {
    margin: 0 0 12px;
    font-size: 1.1rem;
}

.keypoints ul {
    margin-bottom: 0;
}

/* Схема (svg) */
figure.scheme {
    border: 1px solid var(--border-color);
    border-radius: var(--radius-md);
    background: var(--bg-card);
    padding: 20px;
    margin: 18px 0;
    text-align: center;
}

/* Прямой img в figure.scheme - масштабируем под ширину, иначе SVG с большим
   intrinsic-размером вылезает за экран на 320px. (SVG схем должны иметь viewBox,
   обрезанный по контенту, без пустых полей - иначе на мобиле все крошечное.) */
figure.scheme img {
    max-width: 100%;
    height: auto;
    display: block;
    margin: 0 auto;
}

figure.scheme figcaption {
    font-size: 0.85rem;
    color: var(--text-secondary);
    margin-top: 10px;
}

.scheme-grid {
    display: flex;
    flex-direction: column;
    gap: 24px;
    align-items: center;
    margin: 16px 0 8px;
}

.scheme-grid img {
    width: 100%;
    max-width: 680px;
    height: auto;
    display: block;
}

/* ==================== АДАПТИВНЫЕ ТАБЛИЦЫ СРАВНЕНИЯ ==================== */
.adaptive-table {
    width: 100%;
    border-collapse: collapse;
    margin: 20px 0;
    font-size: 0.95em;
    box-shadow: var(--shadow-sm);
    border-radius: var(--radius-md);
    overflow: hidden;
    background: var(--bg-card);
}

.adaptive-table th, .adaptive-table td {
    padding: 12px 15px;
    text-align: center;
    border-bottom: 1px solid var(--border-color);
}

.adaptive-table th {
    background: var(--bg-card);
    font-weight: 600;
}

.adaptive-table td:first-child {
    text-align: left;
    font-weight: 600;
}

.adaptive-table .good { color: var(--color-success); }
.adaptive-table .bad { color: var(--color-danger); }

@media (max-width: 768px) {
    .adaptive-table, .adaptive-table tbody, .adaptive-table tr, .adaptive-table td {
        display: block;
        width: 100%;
    }
    .adaptive-table thead {
        display: none;
    }
    .adaptive-table tr {
        margin-bottom: 16px;
        border: 1px solid var(--border-color);
        border-radius: var(--radius-md);
        overflow: hidden;
        background: var(--bg-card);
    }
    .adaptive-table td {
        text-align: right;
        padding-left: 50%;
        position: relative;
        border: none;
        border-bottom: 1px solid var(--border-color);
    }
    .adaptive-table td:last-child {
        border-bottom: none;
    }
    .adaptive-table td:first-child {
        background: var(--bg-primary);
        color: var(--text-primary);
        font-weight: bold;
        font-size: 1.1em;
        text-align: center;
        padding: 12px;
        border-bottom: 2px solid var(--border-color);
    }
    .adaptive-table td:not(:first-child)::before {
        content: attr(data-label);
        position: absolute;
        left: 12px;
        width: 45%;
        padding-right: 10px;
        white-space: nowrap;
        text-align: left;
        font-weight: normal;
        color: var(--text-secondary);
    }
}

/* ==================== ЗАДАЧИ ТЕМЫ ==================== */
.task {
    background: var(--bg-card);
    border: 1px solid var(--border-color);
    border-radius: var(--radius-lg);
    padding: 6px 24px 18px;
    margin: 22px 0;
}

.task h2 {
    margin-top: 18px;
    font-size: 1.15rem;
}

.task details {
    border: 1px solid var(--border-color);
    border-radius: var(--radius-md);
    margin: 12px 0;
    background: var(--bg-secondary);
    overflow: hidden;
}

.task summary {
    cursor: pointer;
    font-weight: 600;
    font-size: 0.95rem;
    padding: 10px 14px;
    color: var(--color-primary-dark);
}

.task summary:hover {
    color: var(--color-primary);
}

.task details[open] > summary {
    border-bottom: 1px solid var(--border-color);
}

.task .task-body {
    padding: 12px 16px 2px;
}

.task .task-body pre {
    background: var(--bg-primary);
}

/* ==================== КВИЗ ==================== */
.quiz {
    background: var(--bg-card);
    border: 1px solid var(--border-color);
    border-radius: var(--radius-lg);
    padding: 22px 24px;
    margin: 28px 0;
}

.quiz h2 {
    margin: 0 0 6px;
    font-size: 1.1rem;
}

.quiz h3 {
    margin-top: 0;
}

.quiz .quiz-question {
    margin-bottom: 14px;
}

/* Вариант ответа - БЛОК, а не флекс-контейнер. При display: flex каждый <code>
   внутри варианта становился самостоятельным флекс-элементом, текст вокруг него
   разваливался на отдельные элементы, и все они были обязаны встать в одну строку:
   на 320px вариант вылезал за экран (05-lists/05-iterators.html, 2026-08-16).
   Висячий отступ (padding-left плюс отрицательный text-indent) держит прежний вид -
   вторая и следующие строки выровнены по тексту, а не по радиокнопке. */
.quiz label {
    display: block;
    padding: 10px 14px 10px 37px;
    text-indent: -23px;
    border: 1px solid var(--border-color);
    border-radius: var(--radius-md);
    margin-bottom: 8px;
    cursor: pointer;
    font-size: 0.95rem;
}

.quiz label input[type="radio"] {
    margin: 0 10px 0 0;
    vertical-align: middle;
}

.quiz label:hover {
    border-color: var(--color-primary);
}

.quiz-result {
    margin-top: 12px;
    font-weight: 600;
    display: none;
}

.quiz-result.ok {
    color: var(--color-success);
    display: block;
}

.quiz-result.fail {
    color: var(--color-danger);
    display: block;
}

/* ==================== ОЦЕНКА УРОКА ==================== */
.lesson-feedback {
    display: flex;
    align-items: center;
    gap: 12px;
    flex-wrap: wrap;
    margin-top: 36px;
    padding: 16px 20px;
    background: var(--bg-card);
    border: 1px solid var(--border-color);
    border-radius: var(--radius-lg);
    font-size: 0.95rem;
    color: var(--text-secondary);
}

.fb-btn {
    width: 38px;
    height: 38px;
    display: flex;
    align-items: center;
    justify-content: center;
    border: 1px solid #cbd5e1;
    border-radius: 50%;
    background: var(--bg-card);
    color: var(--text-secondary);
    cursor: pointer;
    transition: all 0.2s ease;
}

.fb-btn:hover {
    border-color: var(--color-primary);
    color: var(--color-primary);
}

.fb-btn svg {
    width: 18px;
    height: 18px;
    fill: currentColor;
}

.lesson-feedback.voted .fb-btn {
    pointer-events: none;
    opacity: 0.4;
}

.lesson-feedback.voted .fb-btn.selected {
    opacity: 1;
    border-color: var(--color-success);
    color: var(--color-success);
}

.fb-thanks {
    display: none;
    color: var(--color-success);
    font-weight: 600;
}

.lesson-feedback.voted .fb-thanks {
    display: inline;
}

/* ==================== НАВИГАЦИЯ УРОКА ====================
   Грид из трех колонок: длинная подпись переносится ВНУТРИ кнопки,
   а не выталкивает соседнюю кнопку на новую строку */
.lesson-nav {
    display: grid;
    grid-template-columns: 1fr auto 1fr;
    gap: 12px;
    align-items: center;
    margin-top: 40px;
    padding-top: 24px;
    border-top: 1px solid var(--border-color);
}

.lesson-nav .btn {
    text-decoration: none;
    white-space: normal;
    text-align: center;
}

.lesson-nav > :first-child {
    justify-self: start;
}

.lesson-nav > :last-child {
    justify-self: end;
}

.btn-done.is-done {
    color: var(--color-success);
    border-color: #bbf7d0;
    background: #f0fdf4;
}

/* ==================== БОКОВАЯ РЕЙКА И СОДЕРЖАНИЕ ==================== */
body.has-rail {
    padding-left: 64px;
}

.rail {
    position: fixed;
    left: 0;
    top: 0;
    bottom: 0;
    width: 64px;
    background: var(--bg-card);
    border-right: 1px solid var(--border-color);
    display: flex;
    flex-direction: column;
    align-items: center;
    padding: 14px 0;
    gap: 8px;
    z-index: 110;
}

.rail a,
.rail button {
    width: 40px;
    height: 40px;
    display: flex;
    align-items: center;
    justify-content: center;
    border-radius: var(--radius-md);
    border: none;
    background: transparent;
    color: var(--text-secondary);
    cursor: pointer;
    text-decoration: none;
}

.rail a:hover,
.rail button:hover {
    background: var(--bg-tertiary);
    color: var(--color-primary);
}

.rail svg {
    width: 22px;
    height: 22px;
    fill: currentColor;
}

.drawer {
    position: fixed;
    left: 64px;
    top: 0;
    bottom: 0;
    width: 340px;
    background: var(--bg-card);
    border-right: 1px solid var(--border-color);
    box-shadow: var(--shadow-lg);
    padding: 24px;
    overflow-y: auto;
    transform: translateX(-110%);
    transition: transform 0.25s ease;
    z-index: 105;
}

.drawer.open {
    transform: translateX(0);
}

.drawer h2 {
    font-size: 1.1rem;
    margin: 0 0 4px;
}

.drawer-brand {
    margin-bottom: 10px;
}

.drawer-brand a {
    font-weight: 700;
    font-size: 0.9rem;
    color: var(--color-primary);
    text-decoration: none;
}

.drawer-brand a:hover {
    color: var(--color-primary-dark);
}

.drawer-sub {
    font-size: 0.8rem;
    margin-bottom: 16px;
}

.drawer-sub a {
    color: var(--color-primary);
    text-decoration: none;
}

.drawer-topic {
    font-weight: 600;
    font-size: 0.9rem;
    margin: 16px 0 6px;
    color: var(--text-secondary);
    cursor: pointer;
    user-select: none;
    display: flex;
    align-items: center;
    gap: 6px;
}

.drawer-topic::before {
    content: '▾';
    font-size: 0.7rem;
    color: var(--text-tertiary);
    transition: transform 0.2s ease;
}

.drawer-topic.collapsed::before {
    transform: rotate(-90deg);
}

.drawer-topic.collapsed + .drawer-lessons {
    display: none;
}

.drawer-lessons {
    list-style: none;
    margin: 0;
    padding: 0;
}

.drawer-lessons a,
.drawer-lessons .drawer-soon {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 8px 10px;
    border-radius: var(--radius-sm);
    color: var(--text-secondary);
    text-decoration: none;
    font-size: 0.9rem;
}

.drawer-lessons a:hover {
    background: var(--bg-secondary);
}

.drawer-lessons a.current {
    background: var(--color-primary-bg);
    color: var(--color-primary-dark);
    font-weight: 600;
}

.drawer-lessons .drawer-soon {
    color: var(--text-tertiary);
}

.drawer-lessons .mark {
    width: 14px;
    flex-shrink: 0;
    font-weight: 700;
    color: var(--border-color);
}

.drawer-lessons .mark.done {
    color: var(--color-success);
}

.drawer-overlay {
    position: fixed;
    inset: 0;
    background: rgba(15, 23, 42, 0.3);
    z-index: 100;
    display: none;
}

.drawer-overlay.open {
    display: block;
}

/* ==================== АДАПТИВ ==================== */
@media (max-width: 768px) {
    body.has-rail {
        padding-left: 0;
        padding-top: 56px;
    }

    .rail {
        flex-direction: row;
        width: 100%;
        height: 56px;
        bottom: auto;
        border-right: none;
        border-bottom: 1px solid var(--border-color);
        padding: 0 16px;
        justify-content: space-between;
    }

    .drawer {
        left: 0;
        top: 56px;
        width: 100vw;
    }

    .wrap {
        padding: 16px 12px 32px;
    }

    .task {
        padding: 6px 12px 18px;
    }

    .task .task-body {
        padding: 12px 8px 2px;
    }

    .lesson pre {
        padding: 12px;
        white-space: pre-wrap;
        word-wrap: break-word;
    }

    .lesson pre code {
        font-size: 0.8rem;
        white-space: pre-wrap;
    }

    .course-header h1 {
        font-size: 1.6rem;
    }

    .lesson h1 {
        font-size: 1.45rem;
    }

    .lesson-nav {
        grid-template-columns: 1fr;
        text-align: center;
    }

    .lesson-nav > :first-child,
    .lesson-nav > :last-child {
        justify-self: stretch;
    }
}

/* ==================== ВСТРОЕННЫЙ ТРЕНАЖЕР УРОКА (.lesson-viz) ====================
   Стандарт: workshop/course-viz-guide.md, эталон: prototype/course-viz.html.
   Панель управления (.anim-controls / .control-group / .play-btn) уже в base.css.
   Протокол, чипы и карточка анимации СКОПИРОВАНЫ из animations/style.css
   (курс его не подключает); при переносе в base.css копии здесь удалить.
   Старая промо-карточка (.promo-card/.promo-float) удалена: float сужал
   тренажер, ссылку на большую анимацию дает карточка .anim-card. */
.lesson-viz {
    background: var(--bg-card);
    border: 1px solid var(--border-color);
    border-radius: var(--radius-lg);
    padding: 18px 20px;
    margin: 22px 0;
    box-shadow: var(--shadow-sm);
}

/* Заголовок тренажера - полноценный раздел урока (размер .lesson h2),
   но внутри карточки без верхнего отступа (как .keypoints h2) */
.lesson-viz h2 {
    margin: 0 0 12px;
}

.lesson-viz-head {
    margin-bottom: 12px;
}

/* Условие - видимая задача: выражение крупно в моно на синей плашке,
   значения переменных - моно жирным. Пилюля не рвется (nowrap), но и не
   тянет страницу вправо: если шире экрана - скроллится внутри себя */
.lv-task {
    margin: 0;
    font-size: 1rem;
    color: var(--text-primary);
}

.lv-task .lv-expr {
    font-family: var(--font-mono);
    font-weight: 700;
    font-size: 1.02rem;
    background: var(--color-primary-bg);
    border: 1px solid var(--color-primary-border);
    color: var(--color-primary-dark);
    border-radius: var(--radius-sm);
    padding: 2px 8px;
    white-space: nowrap;
    display: inline-block;
    max-width: 100%;
    overflow-x: auto;
    scrollbar-width: none;
    vertical-align: bottom;
}

.lv-task .lv-expr::-webkit-scrollbar { display: none; }

.lv-task .lv-vals {
    font-family: var(--font-mono);
    font-weight: 600;
    color: var(--text-primary);
}

/* Сцена адаптивная (flex-wrap, без скролла); стабильную высоту дает резерв:
   JS ставит min-height по самому полному состоянию (шаг 0) и пересчитывает
   его на resize. Слот уровня под символом занимает место даже пустой */
.lv-stage {
    display: flex;
    flex-wrap: wrap;
    gap: 5px;
    align-items: flex-start;
    align-content: flex-start;
    padding: 4px 0;
}

.lv-cell {
    min-width: 32px;
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 3px;
}

.lv-cell .tok {
    min-width: 30px;
    height: 38px;
    padding: 0 6px;
    display: flex;
    align-items: center;
    justify-content: center;
    font-family: var(--font-mono);
    font-size: 1rem;
    font-weight: 600;
    background: var(--bg-secondary);
    border: 1px solid var(--border-color);
    border-radius: var(--radius-sm);
    color: var(--text-primary);
    transition: background 0.25s, border-color 0.25s, color 0.25s;
}

/* Подпись мельче 1rem - только --text-secondary (контраст, правило разд. 7
   ani_guidelines: tertiary валит Lighthouse на мелком тексте) */
.lv-cell .lvl {
    font-family: var(--font-mono);
    font-size: 0.72rem;
    color: var(--text-secondary);
    height: 1.2em;
    line-height: 1.2;
}

.lv-cell.max .tok { background: #fffbeb; border-color: var(--color-warning); color: #92400e; }
.lv-cell.max .lvl { color: var(--color-warning); font-weight: 700; }
.lv-cell.result .tok { background: #f0fdf4; border-color: var(--color-success); color: #166534; }
.lv-cell.result .lvl { color: var(--color-success); }

/* Легенда тренажера - компактная строка цветных точек под сценой */
.lv-legend {
    display: flex;
    flex-wrap: wrap;
    gap: 14px;
    margin: 8px 0 12px;
    font-size: 0.8rem;
    color: var(--text-secondary);
}

.lv-legend span { display: inline-flex; align-items: center; gap: 6px; }
.lv-legend i { width: 11px; height: 11px; border-radius: 3px; border: 1px solid; }
.lv-legend .b-base { background: var(--bg-secondary); border-color: var(--border-color); }
.lv-legend .b-max { background: #fffbeb; border-color: var(--color-warning); }
.lv-legend .b-res { background: #f0fdf4; border-color: var(--color-success); }
.lv-legend .b-stk { background: var(--color-primary-bg); border-color: var(--color-primary); }
/* Точки для столбиковой сцены сортировок - в цвет заливки самого столбика
   (насыщенные, как в большой анимации, а не пастель ячеек) */
.lv-legend .b-bar { background: var(--color-primary-light); border-color: var(--color-primary); }
.lv-legend .b-bar-cmp { background: #f59e0b; border-color: #f59e0b; }
.lv-legend .b-bar-min { background: #a855f7; border-color: #a855f7; }
.lv-legend .b-bar-pivot { background: #7c3aed; border-color: #7c3aed; }
.lv-legend .b-bar-done { background: var(--color-success); border-color: var(--color-success); }
.lv-legend .b-bar-faded { background: var(--color-primary-light); border-color: var(--color-primary); opacity: 0.32; }

/* Сцена "дорожки" (Дейкстра и родня): ряды Вход / Стек / Выход. Ячейка .lv-box -
   как .tok Рутисхаузера; переменные по числу ячеек ряды (стек, выход) держит
   JS-резерв min-height по самому полному шагу КАЖДОГО ряда (И1) */
.lv-lanes { padding: 4px 0; }
.lv-lane { margin-bottom: 10px; }
.lv-lane:last-child { margin-bottom: 0; }
.lv-lane-label {
    font-size: 0.8rem;
    color: var(--text-secondary);
    margin-bottom: 4px;
}
.lv-lane-row {
    display: flex;
    flex-wrap: wrap;
    gap: 5px;
    align-items: flex-start;
    align-content: flex-start;
}
.lv-box {
    min-width: 30px;
    height: 38px;
    padding: 0 6px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    font-family: var(--font-mono);
    font-size: 1rem;
    font-weight: 600;
    background: var(--bg-secondary);
    border: 1px solid var(--border-color);
    border-radius: var(--radius-sm);
    color: var(--text-primary);
    transition: background 0.25s, border-color 0.25s, color 0.25s, opacity 0.25s;
}
/* Состояния - легенда анимаций: желтый - текущий, синий - в стеке, зеленый - в выходе/результат */
.lv-box.cur { background: #fffbeb; border-color: var(--color-warning); color: #92400e; }
.lv-box.stk { background: var(--color-primary-bg); border-color: var(--color-primary); color: var(--color-primary); }
.lv-box.out { background: #f0fdf4; border-color: var(--color-success); color: #166534; }
.lv-box.done { opacity: 0.4; }
/* Заглушка пустого ряда - той же высоты, что ячейка: появление ячеек не двигает сцену */
.lv-empty {
    height: 38px;
    display: inline-flex;
    align-items: center;
    font-size: 0.85rem;
    color: var(--text-secondary);
}

/* Сцена "столбики" (сортировки): бар фиксированной ширины, высота задается по
   значению (как AnimBars на больших страницах). Сортировка лишь переставляет
   столбики - набор высот и их число постоянны, поэтому высота сцены стабильна
   при фиксированной высоте контейнера, без JS-резерва и чтения геометрии (И1). */
.lv-bars {
    display: flex;
    align-items: flex-end;
    gap: 8px;
    height: 156px;
    padding: 6px 0 0;
    margin-bottom: 4px;
}
.lv-bar {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: flex-end;
    width: 44px;
}
/* Базовое состояние - синий (как AnimBars), высоту ставит JS инлайном по значению */
.lv-bar-fill {
    width: 100%;
    background: var(--color-primary-light);
    border: 1px solid var(--color-primary);
    border-radius: 4px 4px 0 0;
    transition: height 0.25s, background-color 0.25s, border-color 0.25s;
}
/* Подпись под столбиком - секондари (контраст WCAG на мелком тексте) */
.lv-bar-val {
    margin-top: 5px;
    font-family: var(--font-mono);
    font-size: 0.85rem;
    font-weight: 600;
    color: var(--text-secondary);
    height: 1.2em;
    line-height: 1.2;
}
/* Индекс под столбиком (Heapsort - связь массив<->дерево). Скругляем высоту сцены
   под лишнюю строку, чтобы столбики не упирались в фикс. высоту .lv-bars (И1) */
.lv-bars.lv-idx { height: 180px; }
.lv-bar-idx {
    margin-top: 1px;
    font-family: var(--font-mono);
    font-size: 0.72rem;
    color: var(--text-secondary);
    height: 1.1em;
    line-height: 1.1;
}
/* Состояния - легенда анимаций: желтый - сравниваемый/сканируемый/указатель,
   фиолетовый - текущий минимум (выбор) или опорный (быстрая, свой оттенок как в
   большой анимации), зеленый - на своем месте, faded - вне текущей части (рекурсия) */
.lv-bar.cmp .lv-bar-fill { background: #f59e0b; border-color: #f59e0b; }
.lv-bar.min .lv-bar-fill { background: #a855f7; border-color: #a855f7; }
.lv-bar.pivot .lv-bar-fill { background: #7c3aed; border-color: #7c3aed; }
.lv-bar.done .lv-bar-fill { background: var(--color-success); border-color: var(--color-success); }
/* Приглушение вне активного подмассива - opacity не трогает геометрию (И1) */
.lv-bar.faded { opacity: 0.32; }

/* Сцена-дерево для турнирной сортировки (06-tournament): инлайновый SVG с
   фиксированным viewBox - масштабируется без сдвига пикселей и без чтения
   геометрии (И1). Узлы: листья - скругленный прямоугольник, внутренние - круг.
   Состояния теми же цветами, что у столбиков: амбер - текущий матч, зеленый -
   минимум уходит в результат, faded - выбывший лист (бесконечность). */
.lv-tree { width: 100%; margin: 4px 0 10px; }
.lv-tree svg { display: block; width: 100%; max-width: 460px; height: auto; margin: 0 auto; }
/* Ребра и рамки узлов - брендовый синий (контраст к белому ~6.3:1, SC 1.4.11 >=3:1;
   border-color #e2e8f0 давал ~1.2:1 - a11y-нарушение). Синий совпадает с легендой
   "узел турнира". */
.lv-edge { stroke: var(--color-primary); stroke-width: 2.25; }
.tn-shape { fill: var(--bg-card); stroke: var(--color-primary); stroke-width: 2.25; transition: fill 0.25s, stroke 0.25s, opacity 0.25s; }
.tn-text { fill: var(--text-primary); font-family: var(--font-mono); font-size: 20px; font-weight: 600; }
/* Неразыгранный внутренний узел: пусто, серый пунктир - "матч предстоит" (не выбыл);
   text-secondary #475569 ~7:1, отличим от активных синих узлов */
.lv-tnode.pending .tn-shape { fill: var(--bg-secondary); stroke: var(--text-secondary); stroke-dasharray: 5 3; }
.lv-tnode.cmp .tn-shape { fill: #f59e0b; stroke: #f59e0b; }
.lv-tnode.cmp .tn-text { fill: #ffffff; }
.lv-tnode.root .tn-shape { fill: var(--color-success); stroke: var(--color-success); }
.lv-tnode.root .tn-text { fill: #ffffff; }
.lv-tnode.done .tn-shape { fill: var(--color-success); stroke: var(--color-success); }
.lv-tnode.done .tn-text { fill: #ffffff; }
.lv-tnode.gone { opacity: 0.32; }
/* Индекс узла в массиве (Heapsort - связь дерево<->массив), text-secondary ~7:1 */
.tn-idx { fill: var(--text-secondary); font-family: var(--font-mono); font-size: 12px; font-weight: 600; }

/* Строка результата под деревом - зеленые ячейки, как готовые столбики */
.lv-result-label { font-size: 0.85rem; color: var(--text-secondary); text-align: center; margin-bottom: 6px; }
/* Вторая лента под первой (06-trees/08: порядок обработки и стек вызовов) */
.lv-result-label.lv-second { margin-top: 14px; }
.lv-result { display: flex; gap: 6px; justify-content: center; min-height: 40px; }
.lv-rcell {
    width: 34px; height: 34px;
    display: flex; align-items: center; justify-content: center;
    background: var(--color-success); border: 1px solid var(--color-success);
    border-radius: 4px;
    font-family: var(--font-mono); font-weight: 600; color: #ffffff;
}

/* Сцена-ячейки для сортировки подсчетом (12-counting): два ряда - массив A и
   count[0..K]. Значение = индекс count. Амбер - активная пара A<->count, зеленый -
   собранный элемент, пунктир - пустая ячейка перед перезаписью. Рамки синие
   (a11y ~6.3:1). Классы СВОИ (.lv-ccell), чтобы не конфликтовать с .lv-cell из
   сцены .lv-stage выше. align-items:flex-start - ряды сжимаются по контенту, тогда
   overflow-x на .lv-cells реально прокручивает (а не распирает карточку). */
.lv-cells { display: flex; flex-direction: column; align-items: flex-start; gap: 24px; margin: 4px 0 6px; padding-bottom: 22px; max-width: 100%; min-width: 0; overflow-x: auto; }
.lv-crow { display: flex; align-items: center; gap: 8px; }
.lv-cline { display: flex; align-items: center; gap: 6px; }
.lv-clabel { width: 74px; flex-shrink: 0; font-family: var(--font-mono); font-size: 0.82rem; color: var(--text-secondary); }
.lv-ccell {
    position: relative; flex-shrink: 0;
    width: 34px; height: 34px;
    display: flex; align-items: center; justify-content: center;
    background: var(--bg-card); border: 2px solid var(--color-primary); border-radius: 6px;
    font-family: var(--font-mono); font-weight: 600; color: var(--text-primary);
    transition: background .25s, border-color .25s, color .25s;
}
.lv-ccell.cmp { background: #f59e0b; border-color: #f59e0b; color: #ffffff; }
.lv-ccell.done { background: var(--color-success); border-color: var(--color-success); color: #ffffff; }
.lv-ccell.empty { border-style: dashed; border-color: var(--text-secondary); background: var(--bg-secondary); color: var(--text-secondary); }
.lv-ccell.faded { opacity: 0.38; }
.lv-ccell-idx { position: absolute; bottom: -20px; left: 0; right: 0; text-align: center; font-size: 0.7rem; color: var(--text-secondary); font-family: var(--font-mono); }

/* Поиск в массиве (10-search/01): тот же ряд .lv-ccell. Барьер - запасная ячейка
   A[N]: до установки пустая (.empty), после - с ключом и пунктирной СИНЕЙ рамкой,
   чтобы отличаться от настоящих данных, но не терять контраст рамки (a11y). */
.lv-ccell.barrier { border-style: dashed; background: var(--color-primary-bg); color: var(--color-primary); }
.lv-legend .b-barrier { background: var(--color-primary-bg); border-color: var(--color-primary); border-style: dashed; }

/* Сцена поразрядной сортировки (13-radix): ряд массива (ячейки .lv-ccell) + ряд из
   10 корзин (0-9). Числа падают в корзину по текущему разряду; активная цифра
   подчеркнута (.lv-digit-hi - работает на любом фоне). Обе полосы со своей
   горизонтальной прокруткой (10 корзин широкие). Высота корзин зарезервирована
   min-height под самую глубокую (И1 - не прыгает при сборке/раскладке). */
.lv-radix { margin: 4px 0 8px; }
.lv-radix .lv-crow { overflow-x: auto; padding-bottom: 4px; }
.lv-digit-hi { text-decoration: underline; text-decoration-thickness: 2px; text-underline-offset: 2px; font-weight: 800; }
/* Radix (13): Очереди горизонтальные. Максимум 3 элемента в очереди.
   Ширина = 146px, высота = 44px фиксированные. 
   В десктопе группируем по 3 в ряд: 3 * 146 + 2 * 8 = 454px. */
.lv-buckets { 
    display: flex; flex-wrap: wrap; justify-content: center; 
    gap: 8px; margin: 16px auto 0; padding-bottom: 4px; 
    max-width: 454px; 
}
.lv-bucket {
    flex-shrink: 0; width: 146px; height: 44px;
    display: flex; flex-direction: row; align-items: center; justify-content: flex-start; gap: 4px;
    padding: 4px 6px; border: 1px dashed var(--color-primary); border-radius: 6px;
    background: var(--bg-secondary);
    box-sizing: border-box;
}
.lv-bucket-lbl { 
    font-family: var(--font-mono); font-size: 0.82rem; font-weight: 700; color: var(--color-primary); 
    width: 14px; text-align: right; margin-right: 2px; flex-shrink: 0;
}
.lv-bucket-lbl.range { width: auto; font-size: 0.72rem; margin-right: 4px; letter-spacing: -0.2px; text-align: left; }
.lv-bucket.wide { width: 160px; }
.lv-buckets.wide-buckets { max-width: 510px; }
.lv-bucket .lv-ccell { width: 34px; height: 32px; font-size: 0.82rem; line-height: 1; }

/* Bucket Sort (.lv-bucket-scene): 4-character float elements (0.XX) sizing */
.lv-bucket-scene .lv-ccell { width: 44px; height: 34px; font-size: 0.82rem; line-height: 32px; }
.lv-bucket-scene .lv-bucket .lv-ccell { width: 38px; height: 30px; font-size: 0.78rem; line-height: 28px; }

/* ============ Общая сцена строкового поиска (10-search/02-05) ============
   Ряд символов текста, под ним ряд образца, который ЕДЕТ по тексту, и панель
   "знания алгоритма" (префикс-функция, таблица сдвигов, хеши - своя в каждом
   уроке). Сцена одна на четыре алгоритма, поэтому их видно как один и тот же
   процесс с разной сообразительностью.
   Ячейки образца ВНЕ окна не исчезают, а становятся прозрачными (.blank):
   ширина ряда постоянна, окно едет, строка не пересобирается (И1).
   Индексы стоят ПОД своей ячейкой, как .lv-ccell-idx у массива (то же
   смещение -20px и тот же зазор между рядами 24px/22px) - раньше стояли
   сверху и на узком экране наезжали и на подпись ряда, и на свою же
   ячейку (.lv-crow схлопывает подпись и ряд до 5px, а смещению -17px
   нужно куда больше места).
   Ячейка узкая (один символ) - 12 символов влезают в 320px без прокрутки. */
.lv-str { margin: 8px 0 10px; }
.lv-str .lv-crow + .lv-crow { margin-top: 24px; }
.lv-str .lv-cline { gap: 3px; }
.lv-sc {
    position: relative; flex-shrink: 0;
    width: 26px; height: 32px;
    display: flex; align-items: center; justify-content: center;
    background: var(--bg-card); border: 2px solid var(--color-primary); border-radius: 5px;
    font-family: var(--font-mono); font-weight: 700; font-size: 0.92rem; color: var(--text-primary);
    transition: background .2s, border-color .2s, color .2s;
}
.lv-sc.win { background: var(--color-primary-bg); }
.lv-sc.cmp { background: #f59e0b; border-color: #f59e0b; color: #ffffff; }
.lv-sc.ok { background: var(--color-success); border-color: var(--color-success); color: #ffffff; }
.lv-sc.bad { background: var(--color-danger); border-color: var(--color-danger); color: #ffffff; }
.lv-sc.faded { opacity: 0.34; }
.lv-sc.blank { border-color: transparent; background: transparent; }
/* Символы, которые алгоритм взял ГОТОВЫМИ, не сравнивая (переиспользованный
   префикс в КМП): фиолетовый, как опорный в quick-sort. Бирюзовый
   --color-secondary тут пробовали - рядом с зеленым "совпало" не различается */
.lv-sc.reuse { background: #7c3aed; border-color: #7c3aed; color: #ffffff; }
.lv-sc-idx {
    /* ширина по содержимому и запрет переноса - "len" (три буквы против
       одной-двух у остальных меток) не влезает в ширину ячейки и без
       nowrap переносится на две строки, наезжая верхом на саму ячейку */
    position: absolute; bottom: -20px; left: 50%; transform: translateX(-50%);
    white-space: nowrap;
    font-size: 0.66rem; color: var(--text-secondary); font-family: var(--font-mono);
}
.lv-legend .b-sc { background: var(--color-primary-bg); border-color: var(--color-primary); }
.lv-legend .b-sc-bad { background: var(--color-danger); border-color: var(--color-danger); }
.lv-legend .b-sc-reuse { background: #7c3aed; border-color: #7c3aed; }

/* Панель "что алгоритм знает об образце" под сценой: префикс-функция (03),
   таблица сдвигов (04), хеши (05). Внутри те же ряды .lv-crow с ячейками
   .lv-sc, поэтому панель читается как продолжение сцены. Метки указателей
   (k, len, j) - тот же .lv-sc-idx, что и номера позиций, и стоят под
   своей ячейкой первого ряда. */
.lv-str-panel {
    margin-top: 14px; padding: 10px 12px 12px;
    background: var(--bg-secondary);
    border: 1px solid var(--color-primary-border); border-radius: 6px;
}
.lv-str-panel-t { font-size: 0.82rem; color: var(--text-secondary); margin: 0 0 20px; }
.lv-str-panel .lv-crow + .lv-crow { margin-top: 24px; }

/* Значение хеша в панели (05-rk): состояния как у ячеек сцены, но ширина под
   трехзначный остаток и фиксирована - смена 7 на 196 не двигает строку (И1). */
.lv-hval {
    display: inline-flex; align-items: center; justify-content: center;
    min-width: 46px; height: 30px; padding: 0 6px;
    background: var(--bg-card); border: 2px solid var(--color-primary); border-radius: 5px;
    font-family: var(--font-mono); font-weight: 700; font-size: 0.88rem; color: var(--text-primary);
}
.lv-hval.cmp { background: #f59e0b; border-color: #f59e0b; color: #ffffff; }
.lv-hval.ok { background: var(--color-success); border-color: var(--color-success); color: #ffffff; }
.lv-hform { font-family: var(--font-mono); font-size: 0.8rem; color: var(--text-secondary); }

/* Сцена хеш-таблицы (11-hash-tables/02): семь строк-корзин одна под другой, ключи
   внутри строки растут ВПРАВО (как очереди в 13-radix). Раскладка одна и та же для
   цепочек и для открытой адресации: переключение метода меняет содержимое, а не
   геометрию, поэтому методы сравнимы глазом. И1 держится сам собой - число строк
   и их высота постоянны, цепочка удлиняется только вбок. Строки НЕ переносятся
   (nowrap): самая длинная цепочка тренажера - 5 ключей, на 320px это ~207px. */
.lv-hash { display: flex; flex-direction: column; gap: 5px; margin: 4px 0 10px; }
.lv-hrow {
    display: flex; align-items: center; gap: 5px;
    min-height: 42px; padding: 3px 6px;
    border: 1px dashed var(--border-color); border-radius: var(--radius-sm);
    background: var(--bg-secondary);
}
/* Корзина, с которой алгоритм работает на этом шаге */
.lv-hrow.tgt { border-style: solid; border-color: var(--color-primary); background: var(--color-primary-bg); }
.lv-hidx { flex-shrink: 0; width: 28px; font-family: var(--font-mono); font-size: 0.8rem; font-weight: 700; color: var(--color-primary); }
.lv-harrow { flex-shrink: 0; color: var(--color-primary); font-weight: 700; font-size: 0.78rem; }
/* Пустая корзина цепочек - это NULL-указатель, пустая ячейка массива - пустой слот:
   разные структуры показываем разными знаками, а не одним словом "пусто" */
.lv-hnull { font-family: var(--font-mono); font-size: 0.8rem; color: var(--text-secondary); }
.lv-hash .lv-ccell { width: 34px; height: 32px; font-size: 0.85rem; }

/* Надгробие (тренажер удаления): третье состояние ячейки - не пустая и не
   занятая. Рамка СПЛОШНАЯ в отличие от пунктирной пустой: для поиска надгробие
   не конец цепочки проб, и разница между двумя видами "тут ничего нет" должна
   быть видна глазом. Ключ показан зачеркнутым - его уже нет, метка о нем есть. */
.lv-ccell.tomb {
    background: var(--bg-secondary);
    border-color: var(--text-secondary);
    color: var(--text-secondary);
    text-decoration: line-through;
}

/* Пробуем надгробие: янтарный акцент, но зачеркнутый ключ остается читаем */
.lv-ccell.tomb.cmp { background: #fffbeb; border-color: #f59e0b; color: var(--text-secondary); }

/* Плашка легенды заливная серая: со светлым фоном она сливалась бы с плашкой
   пустой ячейки, а разница между "пусто" и "надгробие" - суть этого тренажера */
.lv-legend .b-tomb { background: var(--text-secondary); border-color: var(--text-secondary); }

/* Сцена "стек" (03-stack/01): вертикальная колонка, элементы растут снизу вверх,
   от дна к вершине. Число строк ПОСТОЯННО (строка выхода + MAX мест колонки),
   свободные места занимают невидимые заглушки - высота сцены не зависит от шага
   (И1 держится сама, JS-резерв и чтение геометрии не нужны). Снятый элемент
   показан в строке выхода над колонкой и сдвинут вправо трансформом: transform
   раскладку не трогает, а "элемент уже вне стека" видно сразу. */
.lv-stack { margin: 6px 0 10px; }
.lv-srow { display: flex; align-items: center; gap: 20px; height: 38px; }
.lv-srow + .lv-srow { margin-top: 6px; }
.lv-srow.lv-sout { margin-bottom: 12px; }
.lv-scell {
    width: 64px; height: 38px; flex-shrink: 0;
    display: flex; align-items: center; justify-content: center;
    background: var(--color-primary-bg);
    border: 2px solid var(--color-primary);
    border-radius: 6px;
    font-family: var(--font-mono); font-weight: 600; font-size: 1rem;
    color: var(--color-primary-dark);
    transition: background .25s, border-color .25s, color .25s;
}
/* Операция читает ячейку (ВЕРХ, ГЛУБИНА) - амбер, как активная ячейка других сцен */
.lv-scell.cmp { background: #f59e0b; border-color: #f59e0b; color: #ffffff; }
/* Снятый элемент: он уже вне стека, поэтому и цвет, и сдвиг вправо */
.lv-scell.out {
    background: var(--color-success); border-color: var(--color-success); color: #ffffff;
    transform: translateX(16px);
}
/* Пустой стек: пунктирная заглушка на месте дна */
.lv-scell.empty {
    background: var(--bg-secondary); border-style: dashed;
    border-color: var(--text-secondary); color: var(--text-secondary); font-size: 0.8rem;
}
/* Свободное место колонки - держит высоту строки, но ничего не рисует */
.lv-scell.hole { visibility: hidden; }
.lv-smark { font-size: 0.82rem; color: var(--text-secondary); }
/* Дно стека - опора под колонкой, статична и шага не меняет */
.lv-sfloor { display: flex; align-items: center; gap: 20px; margin-top: 4px; }
.lv-sfloor i { width: 64px; height: 0; flex-shrink: 0; border-top: 2px solid var(--text-secondary); }

/* Сцена "очередь" (04-queue/01): горизонтальный ряд, голова слева, хвост справа -
   очередь и в жизни горизонтальна (правило ориентации, course-viz-guide.md разд. 3).
   Ячейки и их состояния - готовые `.lv-ccell` из counting, здесь только размер под
   сцену, подписи концов и две мелочи. Мест в ряду ПОСТОЯННОЕ число (вышедший +
   3 места очереди), свободные - пунктирные, поэтому высота и ширина сцены от шага
   не зависят (И1 без JS-резерва). */
.lv-queue { margin: 6px 0 10px; }
.lv-queue .lv-ccell { width: 48px; height: 40px; font-size: 1rem; }
/* Подписи концов стоят НАД рядом и статичны: они называют роли концов, а не
   конкретные ячейки, поэтому не прыгают вслед за длиной очереди. Отступ слева
   пропускает слот вышедшего элемента (48 + зазор 6), ширина - ровно по трем
   местам очереди (3 * 48 + 2 * 6), поэтому подписи стоят над своими концами */
.lv-qends {
    display: flex; justify-content: space-between;
    margin: 0 0 6px 54px;
    max-width: 156px;
    font-size: 0.78rem; color: var(--text-secondary);
}
/* Место, куда еще никто не встал (или откуда уже ушли) - невидимая заглушка */
.lv-ccell.hole { visibility: hidden; }
/* Вышедший элемент: он уже вне очереди, поэтому зеленый (.done) и сдвинут ВЛЕВО -
   ушел через голову. Трансформ раскладку не трогает */
.lv-ccell.exited { transform: translateX(-10px); }

/* Сцена "кольцевой буфер" (04-queue/03): ряд ячеек массива с индексами. Указатель
   first стоит НАД рядом, хвост - ПОД рядом: они регулярно смотрят на одну ячейку
   (пустой буфер и полный), и в одной строке подписи сталкивались бы. Ячеек всегда
   POOL_SIZE, строки подписей своей высоты - сцена от шага не зависит (И1). */
.lv-ring { margin: 6px 0 10px; }
.lv-ring .lv-ccell { width: 44px; height: 40px; font-size: 1rem; }
/* Свободная ячейка, в которой еще лежит старое значение: пунктир плюс приглушение.
   Показывает правду о Pop - данные не стираются, сдвигается только first */
.lv-ring .lv-ccell.empty.faded { opacity: 0.45; }
.lv-ring-marks { display: flex; gap: 6px; font-family: var(--font-mono); font-size: 0.72rem; color: var(--color-primary); }
.lv-ring-mark { width: 44px; flex-shrink: 0; text-align: center; height: 1.3em; line-height: 1.3; }
/* Нижний ряд подписей отодвинут от строки индексов ячеек (.lv-ccell-idx) */
.lv-ring-marks.below { margin-top: 22px; }

/* Сцена "связный список" (05-lists/04): узлы лежат в куче, порядок задают только
   стрелки. Вся сцена - один инлайновый SVG с фиксированным viewBox: связи идут
   ПРЯМЫМИ линиями (кривые дуги запрещены, structure-plan-addendum разд. 0.2), а
   геометрию читать не нужно - viewBox масштабируется сам и И1 держится (так же
   устроены .lv-tree и сцена турнира). Узел - две половины: данные и указатель
   next (точка, из которой выходит стрелка). Вставленный узел стоит НИЖЕ ряда:
   malloc дал ему свое место в куче, а в список его ставят одни лишь стрелки. */
.lv-list { width: 100%; margin: 4px 0 10px; }
.lv-list svg { display: block; width: 100%; max-width: 480px; height: auto; margin: 0 auto; }
.lv-node-box { fill: var(--color-primary-bg); stroke: var(--color-primary); stroke-width: 2; }
.lv-node-sep { stroke: var(--color-primary); stroke-width: 2; }
.lv-node-val { fill: var(--text-primary); font-family: var(--font-mono); font-size: 21px; font-weight: 600; }
.lv-node-next { fill: var(--color-primary); }
/* Текущий узел - амбер, как активная ячейка других сцен; найденный - зеленый */
.lv-node.cmp .lv-node-box { fill: #f59e0b; stroke: #f59e0b; }
.lv-node.cmp .lv-node-sep { stroke: #ffffff; }
.lv-node.cmp .lv-node-val { fill: #ffffff; }
.lv-node.cmp .lv-node-next { fill: #ffffff; }
.lv-node.done .lv-node-box { fill: var(--color-success); stroke: var(--color-success); }
.lv-node.done .lv-node-sep { stroke: #ffffff; }
.lv-node.done .lv-node-val { fill: #ffffff; }
.lv-node.done .lv-node-next { fill: #ffffff; }
/* Освобожденный узел: из цепочки он уже выпал, память вернулась в кучу */
.lv-node.gone { opacity: 0.32; }
.lv-node.gone .lv-node-box { stroke-dasharray: 5 3; }
/* Связь: обычная синяя, только что переброшенная - амбер (видно, что менялось) */
.lv-link { stroke: var(--color-primary); stroke-width: 2; fill: none; }
.lv-link.hi { stroke: #f59e0b; }
.lv-ahead { fill: var(--color-primary); }
.lv-ahead-hi { fill: #f59e0b; }
.lv-lbl { fill: var(--text-secondary); font-family: var(--font-mono); font-size: 14px; }

/* Выбор метода: активная кнопка помечена aria-pressed, а не отдельным классом -
   одно и то же состояние для глаза и для экранного диктора */
.lv-modes { display: flex; flex-wrap: wrap; gap: 6px; margin-bottom: 12px; }
.lv-modes .btn[aria-pressed="true"] {
    background: var(--color-primary);
    border-color: var(--color-primary);
    color: #ffffff;
}

/* Плашка с формулой в раскрывающемся блоке (была инлайн-стилями на странице) */
.formula-box {
    background: var(--bg-main);
    border: 1px solid var(--border-color);
    border-radius: var(--radius-sm);
    padding: 14px;
    margin: 14px 0;
    text-align: center;
    font-family: var(--font-mono);
    font-size: 1.1em;
    font-weight: 600;
}

/* Отступление-предупреждение: тот же блок, но янтарной полосой (было инлайном) */
.aside-note.warn {
    border-left: 3px solid var(--color-warning);
    background: #fffbeb;
}

.aside-note.warn > strong:first-child { color: #92400e; }

/* Копия из animations/style.css: протокол */
.anim-status {
    background: var(--color-primary-bg);
    border-left: 3px solid var(--color-primary);
    padding: var(--spacing-md);
    border-radius: 0 var(--radius-sm) var(--radius-sm) 0;
    font-size: 1rem;
    color: var(--text-primary);
    line-height: 1.6;
    margin-bottom: var(--spacing-md);
    min-height: 76px; /* две строки протокола */
}

.anim-status strong {
    font-family: var(--font-mono);
    color: var(--color-primary-dark);
    /* И1-страховка (нет в animations/style.css): у моношрифта другие метрики,
       строка со strong выходит на 1-2px выше строки без него - протокол дышит.
       В анимациях не стреляло: там b() есть в каждой строке каждого шага */
    line-height: 1;
}

/* Копия из animations/style.css: счетчики-чипы */
.anim-counters {
    display: flex;
    flex-wrap: wrap;
    gap: var(--spacing-sm);
    margin-bottom: var(--spacing-md);
}

.anim-counters:last-child { margin-bottom: 0; }

.counter-chip {
    padding: 4px 12px;
    background: var(--bg-secondary);
    border: 1px solid var(--border-color);
    border-radius: var(--radius-sm);
    font-size: 0.88rem;
    color: var(--text-secondary);
}

.counter-chip strong {
    color: var(--text-primary);
    font-family: var(--font-mono);
    /* И1-страховка: слот значения фиксированной ширины - "-" и число занимают
       одинаково, чип не меняет ширину и ряд чипов не пересобирается */
    display: inline-block;
    min-width: 2ch;
    text-align: center;
}

.counter-chip strong.chip-wide { min-width: 7ch; /* под самое длинное "12 / 12" */ }

.counter-chip.lit {
    background: #f0fdf4;
    border-color: var(--color-success);
    color: #15803d;
}

.counter-chip.lit strong { color: #15803d; }

/* Копия из animations/style.css: карточка анимации (ссылка на большую версию;
   в уроке стоит в потоке, а не в сетке хаба - добавлен вертикальный отступ) */
.anim-card {
    display: flex;
    gap: var(--spacing-md);
    align-items: flex-start;
    background: var(--bg-card);
    border: 1px solid var(--border-color);
    border-radius: var(--radius-lg);
    padding: var(--spacing-lg);
    margin: 24px 0;
    text-decoration: none;
    color: var(--text-primary);
    box-shadow: var(--shadow-sm);
    transition: border-color var(--transition-base), box-shadow var(--transition-base);
}

.anim-card:hover {
    border-color: var(--color-primary);
    box-shadow: var(--shadow-lg);
}

.anim-card-icon {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 48px;
    height: 48px;
    flex-shrink: 0;
    background: var(--color-primary-bg);
    color: var(--color-primary-dark);
    border-radius: var(--radius-md);
    font-family: var(--font-mono);
    font-size: 1.05rem;
    font-weight: 700;
}

.anim-card-title {
    font-size: 1.1rem;
    font-weight: 700;
    margin-bottom: 4px;
}

.anim-card-desc {
    font-size: 0.92rem;
    color: var(--text-secondary);
    line-height: 1.5;
}

/* Строка-действие: карточка в уроке читается как переход, а не как заголовок */
.anim-card-go {
    margin-top: 8px;
    font-size: 0.9rem;
    font-weight: 600;
    color: var(--color-primary);
}

.anim-card:hover .anim-card-go { color: var(--color-primary-dark); }

@media (max-width: 768px) {
    .anim-status {
        min-height: 150px; /* до четырех строк протокола на 320px */
        display: flex;
        flex-direction: column;
        justify-content: center;
    }
}

@media (max-width: 480px) {
    .lv-stage { gap: 4px; }
    .lv-cell { min-width: 26px; }
    .lv-cell .tok { min-width: 24px; height: 32px; padding: 0 4px; font-size: 0.9rem; }
    .lv-cell .lvl { font-size: 0.66rem; }
    .lv-task .lv-expr { font-size: 0.9rem; padding: 2px 6px; }
    .lv-lane-row { gap: 4px; }
    .lv-box { min-width: 24px; height: 32px; padding: 0 4px; font-size: 0.9rem; }
    .lv-empty { height: 32px; }
    .lv-bars { gap: 5px; }
    .lv-bar { width: 34px; }
    .lv-bar-val { font-size: 0.78rem; }
    /* Counting (12) и Radix (13): на узких подпись уходит НАД рядом ячеек (ячейки в
       обертке .lv-cline, поэтому не складываются столбиком), ячейки ужимаем до 30px */
    .lv-cells { gap: 18px; }
    .lv-crow { flex-direction: column; align-items: flex-start; gap: 5px; }
    .lv-str .lv-crow + .lv-crow, .lv-str-panel .lv-crow + .lv-crow { margin-top: 22px; }
    .lv-clabel { width: auto; font-size: 0.74rem; }
    .lv-cline { gap: 5px; }
    .lv-ccell { width: 30px; height: 30px; }
    .lv-ccell-idx { font-size: 0.66rem; }

    /* Поиск (10-search/01): ряд из 12-13 ячеек в прокрутку прятать нельзя - активная
       ячейка уезжает за край и шаг не виден. Такой ряд переносится по строкам
       (row-gap оставляет место подписям индексов), высота меняется только на
       брейкпоинте, не на шаге, поэтому И1 не страдает. */
    .lv-cells.lv-wrap { overflow-x: visible; }
    .lv-cells.lv-wrap .lv-cline { flex-wrap: wrap; row-gap: 26px; }

    /* Строковая сцена (10-search/02-05): ряд текста переносить НЕЛЬЗЯ - образец
       под ним потеряет выравнивание, поэтому ужимаем сами ячейки. Подписи рядов
       уходят над рядами по общему правилу .lv-crow. */
    .lv-sc { width: 22px; height: 30px; font-size: 0.84rem; }
    .lv-sc-idx { font-size: 0.6rem; }

    /* Radix (13): На мобилке сильно ужимаем горизонтальные очереди, чтобы точно влезло 2 в ряд.
       2 * 116px + 6px gap = 238px (влезет даже при больших отступах на 320px экранах). */
    .lv-buckets { gap: 6px; max-width: 238px; }
    /* .wide (Bucket Sort, диапазон слева + 2 ячейки): контент карточки на 320 = 254px
       (замерено). Для 2 в ряд корзина <= 124 (2*124+6=254). В 124px раскладка:
       подпись(~44) + gap3 + ячейка31 + gap3 + ячейка31 = 112 <= контент 115 (пад3+рамка).
       Подпись мелкая, ячейки 31 с внутренним паддингом - цифры не жмутся к краям. */
    .lv-buckets.wide-buckets { max-width: 254px; gap: 6px; }
    .lv-bucket { width: 116px; height: 36px; padding: 2px 4px; gap: 3px; }
    .lv-bucket.wide { width: 123px; height: 36px; padding: 2px 3px; gap: 3px; }
    .lv-bucket-lbl { width: 10px; font-size: 0.72rem; margin-right: 0; }
    .lv-bucket-lbl.range { width: auto; font-size: 0.56rem; margin-right: 0; letter-spacing: -0.4px; }
    .lv-bucket .lv-ccell { width: 26px; height: 26px; font-size: 0.72rem; line-height: 24px; }
    .lv-bucket-scene .lv-ccell { width: 36px; height: 30px; font-size: 0.72rem; line-height: 28px; }
    .lv-bucket-scene .lv-bucket .lv-ccell { width: 31px; height: 27px; font-size: 0.56rem; line-height: 25px; border-width: 1px; padding: 0; }

    /* Хеш-таблица (11/02): строку-корзину ужимаем так, чтобы самая длинная цепочка
       тренажера (5 ключей) влезла в 320px без скролла:
       20 + 5*24 + 4*8 + 9*3 + 8 = 207px при доступных ~250px */
    .lv-hash .lv-ccell { width: 24px; height: 24px; font-size: 0.7rem; }
    .lv-hrow { min-height: 34px; gap: 3px; padding: 2px 4px; }
    .lv-hidx { width: 20px; font-size: 0.7rem; }
    .lv-harrow { font-size: 0.68rem; }
    .lv-hnull { font-size: 0.72rem; }

    /* Стек (03-stack/01): колонка и подписи мельче - строка сцены становится
       52 + 14 + подпись (~110) = 176px при доступных ~250px на 320px */
    .lv-srow { height: 32px; gap: 14px; }
    .lv-sfloor { gap: 14px; }
    .lv-scell { width: 52px; height: 32px; font-size: 0.9rem; }
    .lv-scell.empty { font-size: 0.72rem; }
    .lv-sfloor i { width: 52px; }
    .lv-smark { font-size: 0.76rem; }

    /* Очередь (04-queue/01): ряд 4 * 40 + 3 * 5 = 175px при доступных ~250px;
       подписи концов пересчитаны под мобильную ячейку (отступ 40 + зазор 5) */
    .lv-queue .lv-ccell { width: 40px; height: 36px; font-size: 0.9rem; }
    .lv-qends { margin-left: 45px; max-width: 130px; font-size: 0.68rem; }

    /* Лента результата (06-tournament, 06-trees/08): семь ячеек по 34px с зазорами
       6px дают 274px против ~254px контента карточки на 320px - ужимаем ленту:
       7 * 30 + 6 * 4 = 234px */
    .lv-result { gap: 4px; }
    .lv-rcell { width: 30px; height: 30px; font-size: 0.9rem; }

    /* Кольцевой буфер (04-queue/03): ряд 5 * 38 + 4 * 5 = 210px при доступных ~250px */
    .lv-ring .lv-ccell { width: 38px; height: 36px; font-size: 0.9rem; }
    .lv-ring-marks { gap: 5px; font-size: 0.66rem; }
    .lv-ring-mark { width: 38px; }
}

/* Elegant Card for Historical Notes (matching animations) */
.elegant-card {
    background: var(--bg-card);
    border: 1px solid var(--border-color);
    border-radius: 8px;
    margin: 20px 0;
    overflow: hidden;
}
.elegant-teaser {
    padding: 20px 20px 0 20px;
}
.elegant-teaser p { margin-top: 8px; margin-bottom: 20px; }
details.elegant-details > summary {
    display: flex;
    justify-content: space-between;
    align-items: center;
    background: var(--bg-hover);
    padding: 12px 20px;
    cursor: pointer;
    font-weight: 500;
    color: var(--color-primary);
    border-top: 1px solid var(--border-color);
    list-style: none;
    transition: background 0.2s;
}
details.elegant-details > summary::-webkit-details-marker { display: none; }
details.elegant-details > summary:hover { background: var(--bg-card); }
details.elegant-details[open] > summary { border-bottom: 1px solid var(--border-color); }
details.elegant-details .chev { transition: transform 0.3s ease; }
details.elegant-details[open] .chev { transform: rotate(180deg); }
.elegant-content {
    padding: 20px;
}

/* ==================== СВОРАЧИВАЛКА КОДА (.code-fold) ====================
   Стандартный блок для длинных листингов в уроках: шапка с названием и
   объемом видна всегда, сам код по умолчанию свернут. Оформление повторяет
   details.elegant-details (историческая и математическая врезки), чтобы в
   уроке не заводился третий вид раскрывашки. Внутри - обычный <pre><code>,
   подсветка highlight.js работает и в свернутом виде: она отрабатывает по
   DOM на DOMContentLoaded, а не по видимости. Короткие листинги (до ~25
   строк) не сворачивать - там раскрывашка только мешает. */
.code-fold {
    background: var(--bg-card);
    border: 1px solid var(--border-color);
    border-radius: var(--radius-md);
    margin: 18px 0 24px;
    overflow: hidden;
}

.code-fold > summary {
    display: flex;
    align-items: center;
    gap: 10px;
    background: var(--bg-hover);
    padding: 12px 16px;
    cursor: pointer;
    font-weight: 500;
    color: var(--color-primary);
    list-style: none;
    transition: background 0.2s;
}

.code-fold > summary::-webkit-details-marker { display: none; }
.code-fold > summary:hover { background: var(--bg-card); }
.code-fold[open] > summary { border-bottom: 1px solid var(--border-color); }
.code-fold .chev { flex-shrink: 0; transition: transform 0.3s ease; }
.code-fold[open] .chev { transform: rotate(180deg); }

/* Объем листинга: язык и число строк, чтобы решение "разворачивать или нет"
   принималось до клика. Прижат вправо, шеврон идет за ним */
.code-fold .code-fold-meta {
    margin-left: auto;
    font-family: var(--font-mono);
    font-size: 0.82rem;
    font-weight: 400;
    color: var(--text-secondary);
}

/* Листинг уже обрамлен раскрывашкой - снимаем вторую рамку и скругления */
.lesson .code-fold pre {
    margin: 0;
    border: 0;
    border-radius: 0;
}

@media (max-width: 480px) {
    .code-fold > summary { padding: 10px 12px; gap: 8px; font-size: 0.94rem; }
    .code-fold .code-fold-meta { font-size: 0.74rem; }
}

/* ==================== МОБИЛЬНЫЙ БРЕНД (RAIL) ==================== */
.rail .rail-brand {
    display: none;
}

@media (max-width: 768px) {
    .rail .rail-brand {
        display: flex;
        align-items: center;
        gap: 10px;
        text-decoration: none;
        position: absolute;
        left: 50%;
        transform: translateX(-50%);
        width: auto;
        height: auto;
    }
}

@media (max-width: 359px) {
    .rail .rail-brand .wordmark {
        display: none;
    }

    /* На 320px в карточку урока влезает ~254px: 12 ячеек по 19px с зазором 2px
       дают ровно 250px - строковая сцена помещается целиком, без прокрутки. */
    .lv-str .lv-cline { gap: 2px; }
    .lv-sc { width: 19px; height: 28px; font-size: 0.78rem; }
}


details[id^="module-"] {
    scroll-margin-top: 70px;
}

/* ==================== ПАМЯТКА-СРАВНЕНИЕ АЛГОРИТМОВ (.algo-recall) ====================
   Карточки "что запомнить": год, имя, крючок-образ, плюсы и минусы.
   Введено в закреплении модуля "Выражения"; переиспользуемо в других
   закреплениях. Грид складывается в столбик на мобильном - таблиц
   с горизонтальным скроллом не нужно. */
.algo-recall {
    display: grid;
    grid-template-columns: repeat(3, 1fr);
    gap: 14px;
    margin: 18px 0 24px;
}

@media (max-width: 700px) {
    .algo-recall {
        grid-template-columns: 1fr;
    }
}

.algo-card {
    background: var(--bg-card);
    border: 1px solid var(--border-color);
    border-radius: var(--radius-lg);
    padding: 16px 18px;
    box-shadow: var(--shadow-sm);
}

.algo-year {
    display: inline-block;
    font-family: var(--font-mono);
    font-weight: 700;
    font-size: 0.85rem;
    color: var(--color-primary-dark);
    background: var(--color-primary-bg);
    border: 1px solid var(--color-primary-border);
    border-radius: var(--radius-sm);
    padding: 2px 10px;
}

.algo-name {
    font-size: 1.05rem;
    font-weight: 700;
    margin: 8px 0 2px;
}

/* Крючок-образ: одна фраза, за которую цепляется память */
.algo-hook {
    font-size: 0.9rem;
    color: var(--text-secondary);
    font-style: italic;
    margin-bottom: 10px;
}

.algo-card ul {
    list-style: none;
    margin: 0;
    padding: 0;
}

.algo-card li {
    position: relative;
    padding-left: 22px;
    margin-bottom: 6px;
    font-size: 0.92rem;
    line-height: 1.45;
}

.algo-card li::before {
    position: absolute;
    left: 0;
    font-family: var(--font-mono);
    font-weight: 700;
}

.algo-plus {
    margin-bottom: 6px;
}

.algo-plus li::before {
    content: '+';
    color: var(--color-success);
}

.algo-minus li::before {
    content: '-';
    color: var(--color-danger);
}

/* ==================== КНОПКА "СКОПИРОВАТЬ КОД" ==================== */
/* Обертку и кнопку вставляет course.js в каждый листинг pre > code урока */

.code-copy-wrap {
    position: relative;
}

.copy-btn {
    position: absolute;
    top: 12px;
    right: 12px;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 6px;
    background: var(--bg-card);
    border: 1px solid var(--border-color);
    border-radius: 4px;
    color: var(--text-secondary);
    cursor: pointer;
}

.copy-btn:hover {
    color: var(--text-primary);
}

.copy-btn.copied {
    color: var(--color-success);
    border-color: var(--color-success);
}
