/* ============================================================
   Analyst Way — дизайн-система (тёмная тема). Единственный css в проекте.
   Правила: цвет по роли объекта, панели управления в одну линию,
   насыщенные чистые акценты, никаких «грязных» полутонов.
   ============================================================ */

/* ---------- 0. Шрифты (лежат рядом, работают без сети) ---------- */
@font-face {
  font-family: "Inter"; font-style: normal; font-weight: 400 700; font-display: swap;
  src: url("fonts/inter-latin.woff2") format("woff2");
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: "Inter"; font-style: normal; font-weight: 400 700; font-display: swap;
  src: url("fonts/inter-cyrillic.woff2") format("woff2");
  unicode-range: U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
}
@font-face {
  font-family: "JetBrains Mono"; font-style: normal; font-weight: 400 500; font-display: swap;
  src: url("fonts/jbm-latin.woff2") format("woff2");
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: "JetBrains Mono"; font-style: normal; font-weight: 400 500; font-display: swap;
  src: url("fonts/jbm-cyrillic.woff2") format("woff2");
  unicode-range: U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
}

/* ---------- 1. Токены ---------- */
:root {
  /* Слои поверхности: фон → карточка → приподнятое → активное.
     «Тёмная хвоя»: подмес основания уведён из синей половины (было 216°) в зелёную
     (135°), хрома 1.6% против 3.8% — экран перестал читаться «серовато-синим».
     Ступени лестницы 1.06/1.08/1.10, весь диапазон paper→surface-3 1.26 — дальше темнить
     некуда, слои начнут слипаться. Поэтому граница держится отдельно и она же платит за
     темноту: `--line` поднят до 1.63 к карточке (замер — tools/contrast.mjs). */
  --paper:      #101311;
  --surface:    #161A17;
  --surface-2:  #1C211D;
  --surface-3:  #232925;
  --line:       #354038;
  --line-soft:  #1A1F1C;
  --sheet:      #FFFFFF;    /* подложка под растровым графиком: сама картинка светлая */

  /* Текст. Холодный белый, а не кремовый: у прежнего Everforest-`#D3C6AA` оттенок 41°
     при хроме 16% и он желтил всё, включая код. --ink-3 — самый светлый из «тихих»:
     он подписывает группы, метаданные строк и статус «пропущено» на --surface-3,
     поэтому держит 4.5:1 даже там. Оттенок рядовой — 130°, как у грунта. */
  --ink:        #E9EFEA;
  --ink-2:      #B6C0B7;
  --ink-3:      #A6B0A7;

  /* Действие: ссылочный/акцентный тон и кнопка (см. замеры в tools/contrast.mjs) */
  --accent:        #4FC98C;   /* 8.4:1 на paper — текст, ссылки, подсветка */
  --btn:           #15835A;   /* 4.7:1 с белым — залитая кнопка */
  --btn-hover:     #178B5C;
  --btn-press:     #0F6E4A;
  --accent-soft:   rgba(79,201,140,.15);
  --on-accent:     #FFFFFF;
  --on-accent-dim: rgba(255,255,255,.72);

  /* Редактор и консоль — самый тёмный слой, чтобы код читался как инструмент.
     --code-dim — номера строк и колонка индексов в таблицах: он декоративный,
     в гейт контраста не входит и ярче --code-cm не становится. */
  --code-bg:      #0B0D0B;
  --code-bg-2:    #131714;
  --code-line:    #1C211D;
  --code-ink:     #E9EFEA;
  --code-ink-2:   #A6B0A7;
  --code-dim:     #4E5A52;
  --code-kw:      #8BC1BA;
  --code-str:     #A8C181;
  --code-num:     #ECAC90;
  --code-fn:      #8DC59B;
  --code-cm:      #62706A;
  --code-var:     #DBBC7F;

  /* Статус. «Принято» — шалфейный (83°), акцент — мятный (150°): разнесены по оттенку,
     а не по яркости, иначе на тёмном грунте они сливаются в одну «зелень». */
  --ok:      #A8C181;
  --ok-soft: rgba(168,193,129,.14);
  --warn:    #ECAC90;
  --warn-soft: rgba(236,172,144,.14);
  --err:     #EA8A8C;
  --err-soft: rgba(234,138,140,.14);

  /* Направления: у каждого свой цвет и своя роль.
     Акцент занял зелёный, поэтому «Статистика» уехала в розовый —
     иначе чип направления читается как кнопка.
     py/sql/stat/math — цвета языков и старых срезов; --analyst — цвет единой
     программы. После перекраса грунта два направления совпали со статусами
     буквой в букву (math = --ok, sql = --warn), поэтому math посажен на фиолет
     261°, а sql — на оранжевый 17°: пики палитры теперь 2°, 17°, 40°, 83°, 135°,
     150°, 172°, 261°, 332°. Все пары меряет tools/contrast.mjs. */
  --py:   #8BC1BA;
  --sql:  #E69875;
  --stat: #E0ADC5;
  --math: #BCAED6;
  --case: #8DC59B;
  --analyst: #DBBC7F;

  /* Геометрия */
  --top: 52px;          /* высота шапки: от неё считаются рельс, рабочая зона и drawer-меню */
  --r-1: 4px;
  --r-2: 7px;
  --r-3: 10px;
  --shadow-1: 0 1px 2px rgba(0,0,0,.4);
  --shadow-2: 0 12px 32px rgba(0,0,0,.5), 0 2px 8px rgba(0,0,0,.4);

  /* Движение */
  --ease-out: cubic-bezier(.22,.80,.36,1);
  --ease-spring: cubic-bezier(.34,1.56,.64,1);
  /* Симметричное сглаживание: нужно там, где значение едет туда-обратно (недетерминированная
     полоса запуска) — с --ease-out такой цикл бился бы на стыке. */
  --ease-inout: cubic-bezier(.65,.05,.36,1);
  --t-fast: 120ms;
  --t: 200ms;
  --t-slow: 340ms;

  --font-ui: "Inter", "Segoe UI", system-ui, sans-serif;
  --font-mono: "JetBrains Mono", "Cascadia Mono", Consolas, monospace;
}

/* ---------- 2. База ---------- */
* { box-sizing: border-box; }
html, body { margin: 0; padding: 0; }
body {
  background: var(--paper); color: var(--ink);
  font-family: var(--font-ui); font-size: 14.5px; line-height: 1.55;
  -webkit-font-smoothing: antialiased; text-rendering: optimizeLegibility;
}
h1, h2, h3, h4 { margin: 0; font-weight: 600; letter-spacing: -.011em; line-height: 1.25; }
h1 { font-size: 23px; }
h2 { font-size: 17px; }
h3 { font-size: 15px; }
p { margin: 0 0 12px; }
a { color: inherit; text-decoration: none; }
button { font: inherit; color: inherit; }
::selection { background: color-mix(in srgb, var(--accent) 30%, transparent); }
:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; border-radius: var(--r-1); }
* { scrollbar-width: thin; scrollbar-color: var(--surface-3) transparent; }
*::-webkit-scrollbar { width: 10px; height: 10px; }
*::-webkit-scrollbar-thumb { background: var(--surface-3); border-radius: 99px; border: 3px solid transparent; background-clip: content-box; }
*::-webkit-scrollbar-track { background: transparent; }

.mono { font-family: var(--font-mono); font-variant-ligatures: none; }
.num { font-variant-numeric: tabular-nums; }
.muted { color: var(--ink-2); }
.dim { color: var(--ink-3); }

svg.ic { width: 16px; height: 16px; flex: 0 0 auto; fill: none; stroke: currentColor; stroke-width: 1.7; stroke-linecap: round; stroke-linejoin: round; display: block; }
svg.ic-lg { width: 20px; height: 20px; }
svg.ic-sm { width: 13px; height: 13px; stroke-width: 2; }

/* ---------- 3. Каркас ---------- */
/* minmax(0, 1fr), а не 1fr: иначе колонка не сжимается ниже min-content вложенных
   строк списка, и на узком экране вся страница разъезжается в горизонтальный скролл. */
.app { display: grid; grid-template-columns: 216px minmax(0, 1fr); min-height: 100vh; min-height: 100dvh; align-content: start; }
/* Открытое на телефоне выдвижное меню не должно давать странице прокручиваться. */
body.no-scroll { overflow: hidden; }

.topbar {
  grid-column: 1 / -1; display: flex; align-items: center; gap: 14px;
  height: var(--top); padding: 0 18px; background: var(--surface); border-bottom: 1px solid var(--line);
  position: sticky; top: 0; z-index: 30;
}
/* Бренд в одну строку всегда: на узком экране должна ужиматься поиск, а не подпись. */
.brand { display: flex; align-items: center; gap: 9px; font-weight: 650; letter-spacing: -.02em; font-size: 15px; flex: none; white-space: nowrap; }
.brand svg { width: 22px; height: 22px; color: var(--accent); }
.crumb { display: flex; align-items: center; gap: 8px; color: var(--ink-2); font-size: 13px; white-space: nowrap; overflow: hidden; }
.crumb b { color: var(--ink); font-weight: 600; }
.crumb a:hover { color: var(--ink); }
.sep { color: var(--ink-3); }
.topbar-right { margin-left: auto; display: flex; align-items: center; gap: 6px; }

.search {
  display: flex; align-items: center; gap: 8px; height: 32px; padding: 0 10px; min-width: 210px;
  background: var(--surface-2); border: 1px solid var(--line); border-radius: var(--r-2);
  color: var(--ink-3); font-size: 13px; transition: border-color var(--t-fast) var(--ease-out);
}
.search:hover { border-color: var(--surface-3); }
.search kbd { margin-left: auto; font-family: var(--font-mono); font-size: 10.5px; padding: 1px 5px; border: 1px solid var(--line); border-radius: var(--r-1); background: var(--surface); color: var(--ink-3); }

.rail {
  background: var(--surface); border-right: 1px solid var(--line); padding: 14px 10px;
  display: flex; flex-direction: column; gap: 2px; position: sticky; top: var(--top); align-self: start;
  height: calc(100vh - var(--top)); height: calc(100dvh - var(--top));
}
.rail-label {
  display: flex; align-items: center; gap: 6px; width: 100%; padding: 12px 10px 6px; border: 0; background: transparent;
  font-family: var(--font-ui); font-size: 11px; text-transform: uppercase; letter-spacing: .07em; color: var(--ink-3);
  font-weight: 650; text-align: left; cursor: pointer; transition: color var(--t-fast) var(--ease-out);
}
.rail-label:hover { color: var(--ink-2); }
.rail-label .gr-chev { margin-left: auto; transition: transform var(--t) var(--ease-out); }
/* Две буквы подписи — только для свёрнутого рельса (§7). */
.rail-label .g-abbr { display: none; }
.rail-grp { display: flex; flex-direction: column; min-width: 0; }
/* Автовысота анимируется только через 1fr → 0fr. */
.rail-items { display: grid; grid-template-rows: 1fr; transition: grid-template-rows var(--t) var(--ease-out); }
.rail-items > .rail-in { overflow: hidden; min-height: 0; display: flex; flex-direction: column; gap: 2px; }
.rail-grp.closed .rail-items { grid-template-rows: 0fr; }
.rail-grp.closed .gr-chev { transform: rotate(-90deg); }
.rail a, .rail button.nav {
  display: flex; align-items: center; gap: 10px; padding: 7px 10px; border-radius: var(--r-2);
  color: var(--ink-2); font-weight: 500; font-size: 13.5px; border: 0; background: transparent; width: 100%; text-align: left; cursor: pointer;
  transition: background var(--t-fast) var(--ease-out), color var(--t-fast) var(--ease-out);
}
.rail a:hover, .rail button.nav:hover { background: var(--surface-2); color: var(--ink); }
.rail a.active { background: var(--accent-soft); color: var(--accent); font-weight: 600; }
.rail a .lbl { min-width: 0; overflow: hidden; white-space: nowrap; text-overflow: ellipsis; }
.rail .dot { margin-left: auto; width: 7px; height: 7px; border-radius: 50%; background: currentColor; opacity: .5; }

main { min-width: 0; padding: 22px 26px 60px; }
.wrap { max-width: 1140px; margin: 0 auto; }
.reading { max-width: 680px; }

.page-head { display: flex; align-items: flex-end; gap: 16px; margin-bottom: 18px; }
.page-head .sub { color: var(--ink-2); font-size: 13.5px; margin-top: 3px; }
.page-head .actions.progress-actions { flex-wrap: nowrap; gap: 6px; }
.page-head .actions { margin-left: auto; display: flex; gap: 8px; align-items: center; }

/* ---------- 4. Управляемые элементы ---------- */
.btn {
  display: inline-flex; align-items: center; gap: 7px; height: 32px; padding: 0 12px;
  border: 1px solid var(--line); border-radius: var(--r-2); background: var(--surface-2); color: var(--ink);
  font-weight: 550; font-size: 13.5px; cursor: pointer; white-space: nowrap;
  transition: transform var(--t-fast) var(--ease-spring), background var(--t-fast) var(--ease-out), border-color var(--t-fast) var(--ease-out);
}
.btn:hover { background: var(--surface-3); border-color: var(--line); }
.btn:active { transform: scale(.955); }
.btn.primary { background: var(--btn); border-color: transparent; color: var(--on-accent); }
.btn.primary:hover { background: var(--btn-hover); }
.btn.primary:active { background: var(--btn-press); }
.btn.ghost { background: transparent; border-color: transparent; color: var(--ink-2); }
.btn.ghost:hover { background: var(--surface-2); color: var(--ink); }
.btn.sm { height: 26px; padding: 0 9px; font-size: 12.5px; }
.btn.icon { width: 32px; padding: 0; justify-content: center; }
.btn.icon.sm { width: 26px; }
.btn[disabled], .btn[aria-disabled="true"] { opacity: .45; pointer-events: none; }
.btn.danger { color: var(--err); border-color: color-mix(in srgb, var(--err) 35%, transparent); background: color-mix(in srgb, var(--err) 8%, transparent); }

.seg { display: inline-flex; gap: 2px; padding: 2px; background: var(--surface-2); border: 1px solid var(--line); border-radius: var(--r-2); }
.seg button { height: 24px; padding: 0 10px; border: 0; border-radius: 5px; background: transparent; color: var(--ink-2); font-size: 12.5px; font-weight: 550; cursor: pointer; }
.seg button:hover { color: var(--ink); }
.seg button.on { background: var(--surface-3); color: var(--ink); box-shadow: var(--shadow-1); }

.field { display: flex; flex-direction: column; gap: 6px; }
.field label, .field > span { font-size: 12.5px; color: var(--ink-2); font-weight: 550; }
input[type="text"], input[type="password"], input[type="number"], select, textarea {
  background: var(--surface-2); border: 1px solid var(--line); color: var(--ink);
  border-radius: var(--r-2); padding: 7px 10px; font: inherit; font-size: 13.5px;
}
input:focus, select:focus, textarea:focus { outline: none; border-color: var(--accent); box-shadow: 0 0 0 3px var(--accent-soft); }

/* Форма входа: две колонки полей, широкие — на всю ширину. */
.form { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; }
.form .wide { grid-column: 1 / -1; }
.form input { width: 100%; }
.form-note { color: var(--err); font-size: 13px; min-height: 18px; }
.auth { max-width: 460px; margin: 40px auto 0; }

/* График — фон экрана входа: растянут на всю область просмотра, приглушён и лежит под
   шапкой, рельсом и карточкой (z-index: -1), поэтому ни одна строка текста на него не
   ложится и контраст по нему не меряется. Сетка и ось статичны, оживает только рисунок
   текущего режима: вход — росчерк линии с точками, регистрация — столбцы растут от оси,
   восстановление — бегущий пунктир со сканером. Выбор рисунка делает v-if в шаблоне,
   JS к анимации не прикасается. Долгота цикла — в --cyc на группе режима, её наследуют
   все анимации внутри, так что режимы не расходятся по фазе.
   Базовые значения — конечные (росчерк целиком, столбцы выросли, сканер стоит на оси):
   при prefers-reduced-motion остаётся дорисованный график, а не пустое место. Заливок
   под линией нет: полупрозрачный зелёный на --paper даёт мутное пятно. */
.auth-bg { position: fixed; inset: 0; z-index: -1; pointer-events: none; }
.auth-bg > svg { display: block; width: 100%; height: 100%; }
.auth-bg .grid { stroke: var(--line-soft); stroke-width: 1; }
.auth-bg .axis { stroke: var(--line); stroke-width: 1; }
.auth-bg-art { opacity: .32; }
.auth-bg .g-login { --cyc: 7s; }
.auth-bg .g-signup { --cyc: 6s; }
.auth-bg .g-recover { --cyc: 9s; }
/* Группа гаснет в конце цикла и возвращается на нуле прозрачности — стык не мигает. */
.auth-bg-live { opacity: 1; animation: bg-life var(--cyc, 7s) linear infinite both; }
.auth-bg .line, .auth-bg .trace { fill: none; stroke: var(--accent); stroke-width: 2.5; stroke-linecap: round; stroke-linejoin: round; }
.auth-bg .dot { fill: var(--surface); stroke: var(--accent); stroke-width: 3; }
.auth-bg .peak, .auth-bg .mark, .auth-bg .cap { fill: var(--accent); stroke: none; }
.auth-bg .ring, .auth-bg .pulse { fill: none; stroke: var(--accent); stroke-width: 2; }
.auth-bg .dot, .auth-bg .peak, .auth-bg .cap, .auth-bg .mark, .auth-bg .ring, .auth-bg .pulse, .auth-bg .col { transform-box: fill-box; transform-origin: center; }
/* Залитые фигуры берут --btn: это тот же акцент, но «залитая» его роль, — при
   приглушении фона чистый --accent в площади выглядит пятном жирнее линии. */
.auth-bg .col { transform-origin: center bottom; fill: var(--btn); }
/* Вход: линия вычерчивается слева направо, точки загорают волной, пик даёт кольцо. */
.auth-bg .line { stroke-dasharray: 100; stroke-dashoffset: 0; animation: bg-draw var(--cyc, 7s) var(--ease-inout) infinite both; }
.auth-bg .dot, .auth-bg .peak { animation: bg-dot var(--cyc, 7s) var(--ease-out) infinite both; animation-delay: calc(var(--i) * .42s + .3s); }
.auth-bg .ring { animation: bg-ring var(--cyc, 7s) var(--ease-out) infinite both; }
/* Регистрация: столбцы поднимаются от оси по одному, крышечка на самом высоком — потом. */
.auth-bg .col { animation: bg-col var(--cyc, 6s) var(--ease-spring) infinite both; animation-delay: calc(var(--i) * .3s + .2s); }
.auth-bg .cap { animation: bg-dot var(--cyc, 6s) var(--ease-out) infinite both; animation-delay: calc(var(--cyc, 6s) * .55); }
/* Восстановление: данные бегут пунктиром, сканер идёт поперёк, прицел щёлкает на пике. */
.auth-bg .trace { stroke-dasharray: 14 18; stroke-dashoffset: 0; animation: bg-march var(--cyc, 9s) linear infinite both; }
.auth-bg .scan { stroke: var(--accent); stroke-width: 1.5; opacity: .35; animation: bg-scan var(--cyc, 9s) var(--ease-inout) infinite both; }
.auth-bg .pulse { animation: bg-aim var(--cyc, 9s) var(--ease-out) infinite both; }
@keyframes bg-life { 0%, 100% { opacity: 0; } 7%, 84% { opacity: 1; } }
@keyframes bg-draw { 0% { stroke-dashoffset: 100; } 38%, 100% { stroke-dashoffset: 0; } }
@keyframes bg-dot { 0% { opacity: 0; transform: scale(.4); } 10%, 88% { opacity: 1; transform: scale(1); } 100% { opacity: 0; } }
@keyframes bg-ring { 0%, 46% { opacity: 0; transform: scale(1); } 52% { opacity: .6; transform: scale(1); } 72%, 100% { opacity: 0; transform: scale(2.6); } }
@keyframes bg-col { 0% { opacity: 0; transform: scaleY(.04); } 34%, 84% { opacity: 1; transform: scaleY(1); } 92% { opacity: 0; transform: scaleY(1); } 100% { opacity: 0; transform: scaleY(.04); } }
/* 320 = десять периодов пунктира: цикл замыкается без скачка. */
@keyframes bg-march { 0% { stroke-dashoffset: 0; } 100% { stroke-dashoffset: -320; } }
@keyframes bg-scan { 0% { transform: translateX(0); opacity: 0; } 8%, 84% { opacity: .5; } 92%, 100% { transform: translateX(960px); opacity: 0; } }
@keyframes bg-aim { 0%, 58% { opacity: 0; transform: scale(1); } 84% { opacity: .7; transform: scale(1); } 96%, 100% { opacity: 0; transform: scale(2.4); } }

/* ---------- 5. Поверхности ---------- */
.card { background: var(--surface); border: 1px solid var(--line); border-radius: var(--r-3); }
.card.pad { padding: 16px 18px; }
.card-head { display: flex; align-items: center; gap: 10px; padding: 13px 16px; border-bottom: 1px solid var(--line-soft); }
.card-head h3 { font-size: 14px; }
.card-head .right { margin-left: auto; display: flex; align-items: center; gap: 8px; }

.grid-2 { display: grid; grid-template-columns: minmax(0, 1fr) 340px; gap: 20px; align-items: start; }
.stack { display: flex; flex-direction: column; gap: 14px; }
.split-h { display: flex; gap: 10px; align-items: center; }
.spacer { margin-left: auto; }

.row {
  display: flex; align-items: center; gap: 12px; padding: 11px 16px;
  border-bottom: 1px solid var(--line-soft); transition: background var(--t-fast) var(--ease-out);
}
.row:last-child { border-bottom: 0; }
.row:hover { background: var(--surface-2); }
.row .idx { width: 22px; text-align: right; color: var(--ink-3); font-size: 12px; font-variant-numeric: tabular-nums; }
.row .title { font-weight: 550; flex: 1 1 auto; min-width: 0; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.row .meta { margin-left: auto; display: flex; align-items: center; gap: 14px; color: var(--ink-3); font-size: 12.5px; white-space: nowrap; }
.row.locked .title { color: var(--ink-3); }
.tdot { width: 8px; height: 8px; border-radius: 50%; flex: none; }

.chip {
  display: inline-flex; align-items: center; gap: 6px; height: 21px; padding: 0 8px;
  border-radius: 999px; font-size: 11.5px; font-weight: 600; white-space: nowrap; border: 1px solid transparent;
}
.chip .swatch { width: 6px; height: 6px; border-radius: 50%; background: currentColor; }
.chip.py { color: var(--py); background: color-mix(in srgb, var(--py) 13%, transparent); }
.chip.sql { color: var(--sql); background: color-mix(in srgb, var(--sql) 13%, transparent); }
.chip.stat { color: var(--stat); background: color-mix(in srgb, var(--stat) 13%, transparent); }
.chip.math { color: var(--math); background: color-mix(in srgb, var(--math) 13%, transparent); }
.chip.analyst { color: var(--analyst); background: color-mix(in srgb, var(--analyst) 13%, transparent); }
.chip.case { color: var(--case); background: color-mix(in srgb, var(--case) 13%, transparent); }
.chip.neutral { color: var(--ink-2); background: var(--surface-2); border-color: var(--line); }
/* Чип датасета в шапке урока — кнопка: по ней открывают таблицы в консоли рабочей зоны.
   Размер и отступы общие с меткой трека рядом, поэтому их не трогаем: два чипа в одном ряду
   обязаны сидеть на одной линии, а кнопка по умолчанию ещё и тянется под lining-метрики. */
button.chip { font-family: var(--font-ui); cursor: pointer; transition: background var(--t-fast) var(--ease-out), transform var(--t-fast) var(--ease-spring); }
button.chip:active { transform: scale(.96); }
button.chip.case:hover { background: color-mix(in srgb, var(--case) 22%, transparent); }

.pill { display: inline-flex; align-items: center; gap: 6px; height: 22px; padding: 0 8px; border-radius: var(--r-1); font-size: 11.5px; font-weight: 600; white-space: nowrap; }
.pill.ok { color: var(--ok); background: var(--ok-soft); }
.pill.warn { color: var(--warn); background: var(--warn-soft); }
.pill.err { color: var(--err); background: var(--err-soft); }

.bar { height: 5px; border-radius: 999px; background: var(--surface-3); overflow: hidden; }
.bar i { display: block; height: 100%; border-radius: 999px; background: currentColor; transition: width var(--t-slow) var(--ease-out); }

.menu {
  position: absolute; z-index: 60; top: calc(100% + 6px); right: 0; min-width: 216px; padding: 5px;
  background: var(--surface-2); border: 1px solid var(--line); border-radius: var(--r-3); box-shadow: var(--shadow-2);
  animation: rise var(--t) var(--ease-out) both;
}
.menu .m-label { font-size: 10.5px; text-transform: uppercase; letter-spacing: .07em; color: var(--ink-3); padding: 8px 10px 4px; font-weight: 650; }
.menu button {
  display: flex; align-items: center; gap: 10px; width: 100%; padding: 7px 10px; border: 0;
  background: transparent; border-radius: var(--r-2); font-size: 13.5px; color: var(--ink); text-align: left; cursor: pointer;
}
.menu button:hover { background: var(--surface-3); }
.menu .hint { margin-left: auto; color: var(--ink-3); font-size: 11.5px; }
.menu hr { border: 0; border-top: 1px solid var(--line-soft); margin: 5px 0; }

/* ---------- 6. Код, вывод, таблицы ---------- */
.editor, .codebox { background: var(--code-bg); border: 1px solid var(--code-line); border-radius: var(--r-3); overflow: hidden; color: var(--code-ink); font-family: var(--font-mono); font-size: 13px; }
.ed-bar, .codebox .ed-bar {
  display: flex; align-items: center; gap: 8px; height: 38px; padding: 0 10px 0 14px;
  background: var(--code-bg-2); border-bottom: 1px solid var(--code-line);
}
.ed-bar .fname { font-size: 12px; color: var(--code-ink-2); }
.ed-bar .right { margin-left: auto; display: flex; align-items: center; gap: 6px; }
.ed-btn {
  display: inline-flex; align-items: center; gap: 6px; height: 26px; padding: 0 10px; border-radius: var(--r-2);
  border: 1px solid transparent; cursor: pointer; background: var(--surface-2); color: var(--ink); font-size: 12.5px; font-weight: 550; font-family: var(--font-ui); white-space: nowrap;
  transition: transform var(--t-fast) var(--ease-spring), background var(--t-fast) var(--ease-out);
}
.ed-btn:hover { background: var(--surface-3); }
.ed-btn:active { transform: scale(.94); }
.ed-btn.run { background: var(--btn); color: var(--on-accent); }
.ed-btn.run:hover { background: var(--btn-hover); }
.ed-btn[disabled] { opacity: .45; pointer-events: none; }
/* Знак загрузки вместо подписи «Загружаю python», когда на телефоне кнопки остаются без текста. */
.ed-btn.run.busy svg { animation: rot .8s linear infinite; }

pre.code { margin: 0; padding: 14px 16px 16px; line-height: 1.72; overflow-x: auto; color: var(--code-ink); font-family: var(--font-mono); font-size: 13px; }
pre.code .ln { display: inline-block; width: 22px; margin-right: 14px; color: var(--code-dim); text-align: right; user-select: none; }
.k { color: var(--code-kw); } .s { color: var(--code-str); } .n { color: var(--code-num); }
.f { color: var(--code-fn); } .c { color: var(--code-cm); font-style: italic; } .v { color: var(--code-var); }

.console { background: var(--code-bg); border: 1px solid var(--code-line); border-radius: var(--r-3); color: var(--code-ink); }
.console .c-head { display: flex; align-items: center; gap: 8px; padding: 8px 12px; border-bottom: 1px solid var(--code-line); color: var(--code-ink-2); font-size: 11.5px; text-transform: uppercase; letter-spacing: .06em; font-weight: 650; }
.console pre { margin: 0; padding: 12px; font-family: var(--font-mono); font-size: 12.5px; line-height: 1.7; color: var(--code-ink); white-space: pre-wrap; word-break: break-word; }
.console pre.err { color: var(--err); }

table.dt { width: 100%; border-collapse: collapse; font-size: 12.5px; font-family: var(--font-mono); }
table.dt th { text-align: left; padding: 7px 10px; background: var(--surface-2); border-bottom: 1px solid var(--line); font-weight: 600; color: var(--ink-2); white-space: nowrap; }
table.dt td { padding: 6px 10px; border-bottom: 1px solid var(--line-soft); color: var(--ink); white-space: nowrap; }
table.dt tbody tr:hover td { background: var(--surface-2); }
table.dt td.null { color: var(--ink-3); }
/* подпись таблицы в схеме данных: когда таблиц несколько, без подписи их не отличить */
.dt-cap { font-family: var(--font-mono); font-size: 11.5px; color: var(--ink-2); margin: 12px 0 4px; }
.dt-cap:first-child { margin-top: 0; }
/* Одна таблица датасета — один блок (главная + справочники); между ними нужен зазор,
   потому что подпись внутри блока всегда идёт первой и свой верхний отступ теряет. */
.dt-block + .dt-block { margin-top: 16px; }
.tscroll { overflow: auto; border: 1px solid var(--line); border-radius: var(--r-2); }
.dtype { font-size: 10.5px; font-weight: 650; padding: 1px 5px; border-radius: var(--r-1); background: var(--surface-3); color: var(--ink-2); }

.plot { background: var(--code-bg-2); border-top: 1px solid var(--code-line); padding: 12px 14px; color: var(--ink); }
.plot .p-title { font-size: 12px; color: var(--ink-2); font-weight: 600; margin-bottom: 6px; }
.plot img { display: block; max-width: 100%; border-radius: var(--r-2); background: var(--sheet); }

/* ---------- 7. Рабочие экраны ---------- */
main.full { padding: 0; }
main.full > div { max-width: none; }

.strip { display: flex; align-items: center; gap: 14px; padding: 14px 26px; background: var(--surface); border-bottom: 1px solid var(--line); flex-wrap: nowrap; }
.strip h1 { font-size: 19px; }
.strip .facts { display: flex; gap: 18px; color: var(--ink-2); font-size: 13px; }
.strip .facts b { font-variant-numeric: tabular-nums; font-weight: 650; color: var(--ink); }

/* Рельс живёт в одном из трёх режимов, его задаёт `data-rail` на `.app`:
   `icons` — узкая колонка (урок/кейс, узкий экран, рука пользователя),
   `drawer` — телефон: колонка уходит из сетки и выезжает по гамбургеру. */
.app[data-rail="icons"] { grid-template-columns: 54px minmax(0, 1fr); }
.app[data-rail="icons"] .rail { padding: 14px 8px; align-items: center; }
.app[data-rail="icons"] .rail a,
.app[data-rail="icons"] .rail button.nav { justify-content: center; padding: 9px 0; width: 38px; }
.app[data-rail="icons"] .rail .lbl,
.app[data-rail="icons"] .rail .dot { display: none; }
/* Подпись группы в свёрнутом рельсе не прячется: две буквы — единственное, чем группу
   снова развернуть. Галочка состояния здесь лишняя, её роль играет список под чипом.
   Кегль, межбуквие и вертикальные отступы НЕ трогаем: любой сдвиг здесь заставляет
   заголовки групп переезжать по вертикали в момент сворачивания рельса. */
.app[data-rail="icons"] .rail .rail-label { width: 38px; padding-inline: 0; justify-content: center; }
.app[data-rail="icons"] .rail .rail-label .g-full,
.app[data-rail="icons"] .rail .rail-label .gr-chev { display: none; }
.app[data-rail="icons"] .rail .rail-label .g-abbr { display: inline; }
.rail-foot { margin-top: auto; padding: 10px 6px 2px; display: flex; align-items: center; justify-content: space-between; gap: 6px; }
.app[data-rail="icons"] .rail-foot { justify-content: center; padding: 0 0 4px; }
.app[data-rail="icons"] .rail-foot .pill { display: none; }

.app[data-rail="drawer"] { grid-template-columns: minmax(0, 1fr); }
.app[data-rail="drawer"] .rail {
  position: fixed; top: var(--top); left: 0; z-index: 76; height: auto; bottom: 0;
  /* align-self: start наследуется от базового .rail, и для abspos-элемента сетки он
     вместо растяжки на высоту области даёт высоту по контенту: на коротком экране
     меню переполняет экран и не скроллится. */
  align-self: stretch;
  width: min(272px, 82vw); overflow-y: auto; box-shadow: var(--shadow-2);
  transform: translateX(-100%); transition: transform var(--t) var(--ease-out);
}
.app[data-rail="drawer"].drawer-open .rail { transform: none; }
.scrim { position: fixed; inset: var(--top) 0 0; z-index: 75; background: rgba(6, 8, 11, .55); animation: fade var(--t) var(--ease-out) both; }
.menu-btn { flex: none; }

/* Три колонки: боковые сворачиваются в рельс. Ширина — только в пикселях,
   иначе переход width не интерполируется и панель «прыгает». */
.lab { display: flex; height: calc(100vh - var(--top)); height: calc(100dvh - var(--top)); min-width: 0; }
.lab-mid { flex: 1 1 auto; min-width: 360px; padding: 22px 26px 60px; overflow: auto; margin-inline: auto; }

.outline {
  position: relative; flex: none; width: var(--lab-l, 240px); display: flex; flex-direction: column;
  border-right: 1px solid var(--line); background: var(--surface); overflow: hidden;
  transition: width var(--t) var(--ease-out);
}
.outline .o-head { flex: none; display: flex; align-items: center; gap: 8px; min-height: 40px; padding: 8px 10px 8px 12px; }
.outline .o-body { flex: 1 1 auto; min-height: 0; width: 240px; padding: 4px 12px 20px; overflow-y: auto; transition: opacity var(--t-fast) var(--ease-out); }
.outline.folded .o-body { opacity: 0; pointer-events: none; }

.lab-right {
  position: relative; flex: none; width: var(--lab-r, 560px); max-width: 44%; overflow: hidden;
  background: var(--code-bg); border-left: 1px solid var(--code-line);
  display: flex; flex-direction: column; min-width: 0; color: var(--code-ink);
  transition: width var(--t) var(--ease-out);
}
.lab-right.folded > .wb { opacity: 0; pointer-events: none; }
.p-rail { position: absolute; top: 0; left: 0; z-index: 2; display: flex; flex-direction: column; align-items: center; gap: 4px; padding: 4px 10px; animation: rise var(--t) var(--ease-out) both; }

.s-fold {
  flex: none; display: inline-flex; align-items: center; justify-content: center; width: 26px; height: 26px;
  border: 0; border-radius: var(--r-2); background: transparent; color: var(--ink-3); cursor: pointer;
  transition: color var(--t-fast) var(--ease-out), background var(--t-fast) var(--ease-out);
}
.s-fold:hover { color: var(--accent); background: var(--surface-2); }
.s-fold .ic { width: 15px; height: 15px; }
.wb-tabs .s-fold { padding: 0; color: var(--code-ink-2); }
.wb-tabs .s-fold:hover { color: var(--accent); background: var(--surface-2); }
.v-label { writing-mode: vertical-rl; transform: rotate(180deg); font-size: 11px; letter-spacing: .07em; text-transform: uppercase; font-weight: 650; color: var(--ink-3); padding-top: 6px; }
.outline .v-label { position: absolute; top: 46px; left: 14px; padding-top: 0; }
.lab-right .v-label, .p-rail .v-label { color: var(--code-ink-2); }

.outline .o-title { font-size: 11px; text-transform: uppercase; letter-spacing: .07em; color: var(--ink-3); font-weight: 650; padding: 4px 8px 10px; }
.outline .o-head .o-title { padding: 0; min-width: 0; white-space: normal; line-height: 1.45; }
.outline a, .outline button.nav {
  display: flex; gap: 9px; align-items: center; padding: 6px 8px; border-radius: var(--r-2);
  color: var(--ink-2); font-size: 13px; border: 0; background: transparent; width: 100%; text-align: left; cursor: pointer;
  overflow: hidden; white-space: nowrap; text-overflow: ellipsis;
}
.outline a:hover, .outline button.nav:hover { background: var(--surface-2); color: var(--ink); }
.outline a.on, .outline button.nav.on { background: var(--accent-soft); color: var(--accent); font-weight: 600; }
.outline a > .t { min-width: 0; white-space: normal; line-height: 1.45; overflow-wrap: anywhere; }
.outline .n { font-family: var(--font-mono); font-size: 11px; color: var(--ink-3); width: 14px; flex: none; }
.outline .col { display: flex; flex-direction: column; gap: 1px; font-size: 12.5px; }
.outline .col .split-h { padding: 5px 8px; }

.ltabs { display: flex; gap: 2px; padding: 0 4px; height: 30px; border-bottom: 1px solid var(--line); }
.ltabs button { padding: 3px 9px; border: 0; background: transparent; color: var(--ink-2); font-size: 12.5px; font-weight: 550; cursor: pointer; border-radius: var(--r-2) var(--r-2) 0 0; box-shadow: inset 0 -2px 0 transparent; font-family: var(--font-ui); }
.ltabs button:hover { color: var(--ink); background: var(--surface-2); }
.ltabs button.on { color: var(--ink); box-shadow: inset 0 -2px 0 var(--accent); }

.wb-tabs { display: flex; align-items: center; gap: 2px; padding: 0 8px; height: 34px; background: var(--code-bg-2); border-bottom: 1px solid var(--code-line); }
.wb-tabs button { padding: 4px 10px; border: 0; background: transparent; color: var(--ink-2); font-size: 12.5px; border-radius: var(--r-2) var(--r-2) 0 0; cursor: pointer; font-family: var(--font-ui); }
.wb-tabs button:hover { color: var(--code-ink); }
.wb-tabs button.on { background: var(--code-bg); color: var(--ink); box-shadow: inset 0 -2px 0 var(--accent); }
.wb-tabs .push { margin-left: auto; display: flex; gap: 6px; align-items: center; }
.wb-pane { display: flex; flex-direction: column; min-height: 0; }
.wb-pane.grow { flex: 1 1 auto; }

/* ---------- 8. Текст урока ---------- */
.prose h2 { margin: 26px 0 10px; }
.prose h3 { margin: 20px 0 8px; }
.prose p { max-width: 68ch; }
.prose ul, .prose ol { margin: 0 0 14px; padding-left: 20px; max-width: 68ch; }
.prose li { margin-bottom: 5px; }
.prose code:not(pre code) { font-family: var(--font-mono); font-size: .92em; background: var(--surface-2); border: 1px solid var(--line); padding: 1px 5px; border-radius: var(--r-1); color: var(--code-ink); }
.prose .lede { color: var(--ink-2); }
.callout { display: flex; gap: 11px; padding: 12px 14px; border-radius: var(--r-2); border: 1px solid var(--line); background: var(--surface); margin: 16px 0; max-width: 68ch; }
.callout .ck { flex: none; margin-top: 2px; }
.callout.tip { background: var(--accent-soft); border-color: color-mix(in srgb, var(--accent) 30%, transparent); }
.callout.warn { background: var(--warn-soft); border-color: color-mix(in srgb, var(--warn) 28%, transparent); }

/* ---------- 9. Проверка задания ---------- */
.assert { display: flex; align-items: center; gap: 10px; padding: 8px 12px; border-bottom: 1px solid var(--code-line); font-family: var(--font-mono); font-size: 12.5px; color: var(--code-ink); }
.assert:last-child { border-bottom: 0; }
.assert .st { flex: none; width: 16px; height: 16px; border-radius: 50%; display: grid; place-items: center; }
.assert .st svg { width: 11px; height: 11px; stroke-width: 2.6; }
.assert.ok .st { background: var(--ok-soft); color: var(--ok); }
.assert.err .st { background: var(--err-soft); color: var(--err); }
.assert.skip .st { background: var(--surface-3); color: var(--ink-3); }
.assert .exp { margin-left: auto; color: var(--code-ink-2); white-space: nowrap; }
.assert-note { padding: 10px 12px; border-top: 1px solid var(--code-line); color: var(--warn); font-family: var(--font-mono); font-size: 12px; }
/* Кнопки подсказок — одной линией: «открыть ещё» и «скрыть» не должны разъезжаться по вертикали. */
.hint-btns { display: flex; gap: 6px; flex-wrap: wrap; }
.assert-note .hints { margin-top: 8px; }

/* Тесты и срез уровня */
.quiz { display: flex; flex-direction: column; gap: 10px; }
.quiz .opt {
  display: flex; align-items: center; gap: 11px; padding: 11px 14px; border: 1px solid var(--line);
  border-radius: var(--r-2); background: var(--surface); cursor: pointer; text-align: left; width: 100%;
  transition: border-color var(--t-fast) var(--ease-out), background var(--t-fast) var(--ease-out), transform var(--t-fast) var(--ease-spring);
}
.quiz .opt:hover { border-color: var(--line); background: var(--surface-2); }
.quiz .opt:active { transform: scale(.995); }
.quiz .opt .mk { width: 17px; height: 17px; border-radius: 50%; border: 1.5px solid var(--surface-3); flex: none; display: grid; place-items: center; }
.quiz .opt.sel { border-color: var(--accent); background: var(--accent-soft); }
.quiz .opt.sel .mk { border-color: var(--accent); }
.quiz .opt.sel .mk::after { content: ""; width: 8px; height: 8px; border-radius: 50%; background: var(--accent); }
.quiz .opt.right { border-color: var(--ok); background: var(--ok-soft); }
.quiz .opt.wrong { border-color: var(--err); background: var(--err-soft); }
.quiz .q { font-size: 16px; font-weight: 600; margin-bottom: 2px; }

/* ---------- 10. Прогресс ---------- */
.matrix { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 0 26px; }
.skill { display: grid; grid-template-columns: minmax(0, 1fr) 120px 46px; align-items: center; gap: 12px; padding: 9px 0; border-bottom: 1px solid var(--line-soft); }
.skill .s-name { font-size: 13.5px; font-weight: 550; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.skill .s-val { text-align: right; font-size: 12.5px; color: var(--ink-2); font-variant-numeric: tabular-nums; }
.heat { display: grid; grid-template-rows: repeat(7, 13px); grid-auto-flow: column; grid-auto-columns: 13px; gap: 3px; }
.heat i { border-radius: 2.5px; background: var(--surface-2); }
/* Шкала прогресса выводится из акцента, а не из пяти подобраннных вручную hex-ов:
   перекрас акцента раньше молча оставлял сетку старой зелёной. */
.heat i.l1 { background: color-mix(in srgb, var(--accent) 22%, var(--surface-2)); }
.heat i.l2 { background: color-mix(in srgb, var(--accent) 45%, var(--surface-2)); }
.heat i.l3 { background: color-mix(in srgb, var(--accent) 70%, var(--surface-2)); }
.heat i.l4 { background: var(--accent); }

.stat-big { font-size: 26px; font-weight: 650; letter-spacing: -.02em; font-variant-numeric: tabular-nums; }
.stat-lbl { font-size: 12px; color: var(--ink-3); }

/* ---------- 11. Служебное ---------- */
.empty { display: flex; flex-direction: column; align-items: center; gap: 10px; padding: 40px 20px; color: var(--ink-3); text-align: center; border: 1px dashed var(--line); border-radius: var(--r-3); }
.skel { background: linear-gradient(90deg, var(--surface-2) 25%, var(--surface-3) 50%, var(--surface-2) 75%); background-size: 300% 100%; animation: shimmer 1.3s linear infinite; border-radius: var(--r-1); }
@keyframes shimmer { from { background-position: 150% 0; } to { background-position: -150% 0; } }
.spin { width: 14px; height: 14px; border-radius: 50%; border: 2px solid var(--surface-3); border-top-color: var(--accent); animation: rot .7s linear infinite; }
@keyframes rot { to { transform: rotate(360deg); } }

.veil { position: fixed; inset: 0; background: rgba(6,8,11,.66); display: grid; place-items: center; z-index: 80; animation: fade var(--t) var(--ease-out) both; }
.dialog { width: min(520px, calc(100vw - 40px)); max-height: calc(100vh - 32px); max-height: calc(100dvh - 32px); overflow: auto; background: var(--surface); border: 1px solid var(--line); border-radius: var(--r-3); box-shadow: var(--shadow-2); animation: rise var(--t-slow) var(--ease-spring) both; }
.dialog .d-head { display: flex; align-items: center; gap: 10px; padding: 14px 16px; border-bottom: 1px solid var(--line-soft); }
/* Тело внутри карточки (`.d-body` пришёл из диалогов, в карточке он сам по себе не стилизован). */
.card .d-body { padding: 16px 18px; }
.dialog .d-body { padding: 16px; }
.dialog .d-foot { display: flex; gap: 8px; justify-content: flex-end; padding: 12px 16px; border-top: 1px solid var(--line-soft); }
/* Окно таблиц: схема датасета в 240 px консоли читается через два вложенных скролла,
   поэтому ей дают ширину экрана. Скроллится только тело — шапка с названием датасета
   обязана оставаться видимой, иначе на середине списка не понять, о какой таблице речь. */
.dialog.wide { width: min(1040px, calc(100vw - 32px)); display: flex; flex-direction: column; overflow: hidden; }
.dialog.wide .d-body { overflow: auto; flex: 1 1 auto; }
.dialog .d-head .btn { margin-left: auto; }
.wb-console .c-head .wide-btn { margin-left: auto; }

.toasts { position: fixed; right: 18px; bottom: 18px; z-index: 90; display: flex; flex-direction: column; gap: 8px; }
.toast { display: flex; align-items: center; gap: 9px; padding: 10px 13px; border-radius: var(--r-2); background: var(--surface-2); border: 1px solid var(--line); box-shadow: var(--shadow-2); font-size: 13.5px; animation: rise var(--t-slow) var(--ease-spring) both; }
.toast.ok { border-color: color-mix(in srgb, var(--ok) 40%, transparent); } .toast.ok svg { color: var(--ok); }
.toast.err { border-color: color-mix(in srgb, var(--err) 40%, transparent); } .toast.err svg { color: var(--err); }

@keyframes rise { from { opacity: 0; transform: translateY(7px) scale(.99); } to { opacity: 1; transform: none; } }
@keyframes fade { from { opacity: 0; } to { opacity: 1; } }
@keyframes pop { 0% { transform: scale(.82); } 60% { transform: scale(1.05); } 100% { transform: scale(1); } }
.anim-rise { animation: rise var(--t-slow) var(--ease-out) both; }
.anim-pop { animation: pop var(--t-slow) var(--ease-spring) both; }
@media (prefers-reduced-motion: reduce) { * { animation: none !important; transition: none !important; } }

/* ---------- 12. Каркас приложения ---------- */
/* Экран запуска живёт до монтирования Vue (разметка в index.html): монограмма рисуется
   штрихом, полоса под ней — недетерминированный «путь», который разгоняется и растворяется
   по краям, поэтому у цикла нет скачка. Жест целиком — 0.68 с (росчерк 0.5 + вторая буква
   через 0.18), и ровно столько экран обязан висеть: минимум держит BOOT_MIN в app/main.js,
   иначе на быстрой загрузке росчерк обрывается на середине. Базовые состояния — конечные
   (росчерк целиком, подпись непрозрачная): при prefers-reduced-motion анимация выключается
   и остаётся дорисованный знак, а не пустое место. */
.boot {
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  min-height: 100vh; min-height: 100dvh; color: var(--ink-3); font-size: 12.5px;
}
.boot-mark { width: 62px; height: 62px; color: var(--accent); }
.boot-mark path { stroke-dasharray: 100; stroke-dashoffset: 0; animation: draw .5s var(--ease-out) both; }
.boot-mark path + path { animation-delay: .18s; }
.boot-way { position: relative; width: 178px; height: 2px; margin: 22px 0 12px; border-radius: 1px; background: var(--line); overflow: hidden; }
.boot-way i { position: absolute; inset: 0; border-radius: 1px; background: var(--accent); transform: translateX(-100%) scaleX(.12); animation: way 1.2s var(--ease-inout) infinite; }
/* Вторая полоса со сдвигом фазы на полцикла: с одной между прогонами повисает
   пустой трек, и загрузка выглядит законченной. Отрицательная задержка сдвигает фазу,
   не сдвигая период, — полосы не расходятся со временем. */
.boot-way i + i { animation-delay: -.6s; }
.boot-txt { animation: fade .45s var(--ease-out) .12s both; }
@keyframes draw { from { stroke-dashoffset: 100; } to { stroke-dashoffset: 0; } }
@keyframes way {
  0% { transform: translateX(-100%) scaleX(.12); }
  50% { transform: translateX(0) scaleX(.5); }
  100% { transform: translateX(100%) scaleX(.12); }
}
.menu-host { position: relative; display: inline-flex; }
/* Меню у нижней кромки панели открывается вверх: вниз ему не хватает консоли. */
.menu-host.up .menu { top: auto; bottom: calc(100% + 6px); }
.search-host { position: relative; margin-left: auto; display: flex; }
.field-search {
  display: flex; align-items: center; gap: 8px; height: 32px; padding: 0 10px; width: 288px;
  background: var(--surface-2); border: 1px solid var(--line); border-radius: var(--r-2); color: var(--ink-3);
  transition: border-color var(--t-fast) var(--ease-out), box-shadow var(--t-fast) var(--ease-out);
}
.field-search:focus-within { border-color: var(--accent); box-shadow: 0 0 0 3px var(--accent-soft); }
.field-search input { flex: 1; min-width: 0; border: 0; background: transparent; padding: 0; color: var(--ink); font-size: 13px; }
.field-search input:focus { outline: none; box-shadow: none; }
.field-search ::placeholder { color: var(--ink-3); }
.field-search kbd { font-family: var(--font-mono); font-size: 10.5px; padding: 1px 5px; border: 1px solid var(--line); border-radius: var(--r-1); background: var(--surface); color: var(--ink-3); }
.field-search .menu { top: 38px; left: 0; right: 0; min-width: 0; }
.field-search .menu button { align-items: flex-start; }
.crumbs { display: flex; align-items: center; gap: 7px; color: var(--ink-2); font-size: 13px; min-width: 0; overflow: hidden; white-space: nowrap; }
.crumbs b { color: var(--ink); font-weight: 600; }
.rail { overflow-y: auto; }

/* ---------- 13. Рабочая зона и редактор ---------- */
.wb { display: flex; flex-direction: column; flex: 1 1 auto; min-height: 0; }
.wb-split { display: flex; flex-direction: column; flex: none; min-height: 0; }
.cm-host { flex: 1 1 auto; min-height: 150px; overflow: hidden; font-family: var(--font-mono); }
.cm-host .CodeMirror { height: 100%; font-family: var(--font-mono); font-size: 13px; line-height: 1.7; }
.wb-ms { color: var(--code-ink-2); font-size: 11.5px; font-variant-numeric: tabular-nums; }
.wb-scroll { flex: 1 1 auto; min-height: 0; overflow: auto; display: flex; flex-direction: column; gap: 12px; padding: 12px; background: var(--code-bg); }
.wb-scroll .console, .wb-scroll .check { flex: none; }
.check { border: 1px solid var(--code-line); border-radius: var(--r-3); overflow: hidden; background: var(--code-bg-2); }
.wb-bar { border-top: 1px solid var(--code-line); border-bottom: 0; }
.wb-bar .kbd { color: var(--on-accent-dim); font-family: var(--font-mono); font-size: 10.5px; }
.wb-resizer { flex: none; position: relative; height: 8px; background: var(--code-bg); border-top: 1px solid var(--code-line); cursor: ns-resize; touch-action: none; }
.wb-resizer::after { content: ""; position: absolute; left: 50%; top: 3px; width: 46px; height: 3px; margin-left: -23px; border-radius: 999px; background: var(--surface-3); transition: width var(--t-fast) var(--ease-out), background var(--t-fast) var(--ease-out); }
.wb-resizer:hover::after, body.resizing .wb-resizer::after { width: 76px; background: var(--accent); }
body.resizing { cursor: ns-resize; user-select: none; }
body.resizing .cm-host, body.resizing .c-body { pointer-events: none; }
.wb-console { flex: 0 0 auto; height: var(--con-h, 240px); max-height: calc(100% - 200px); display: flex; flex-direction: column; min-height: 0; border-top: 1px solid var(--code-line); background: var(--code-bg); }
.wb-console .c-head { padding: 0 8px; height: 32px; gap: 2px; background: var(--code-bg-2); letter-spacing: .04em; }
.c-tab { display: inline-flex; align-items: center; gap: 6px; border: 0; background: transparent; padding: 4px 10px; border-radius: var(--r-2); color: var(--ink-2); font: 650 11.5px/1.4 var(--font-ui); text-transform: uppercase; letter-spacing: .04em; cursor: pointer; transition: color var(--t-fast) var(--ease-out), background var(--t-fast) var(--ease-out); }
.c-tab:hover { color: var(--code-ink); }
.c-tab.on { color: var(--accent); background: var(--accent-soft); }
.wb-console .c-head .num { margin-left: auto; text-transform: none; letter-spacing: 0; font-variant-numeric: tabular-nums; }
.c-body { flex: 1 1 auto; min-height: 0; overflow: auto; }
.c-body .assert { border-bottom-color: var(--line-soft); }
.c-more { padding: 8px 12px; border-top: 1px solid var(--code-line); color: var(--code-ink-2); font-family: var(--font-mono); font-size: 11.5px; }
table.dt-out { background: var(--code-bg); }
table.dt-out th { background: var(--code-bg-2); color: var(--code-ink-2); border-bottom-color: var(--code-line); position: sticky; top: 0; }
table.dt-out td { border-bottom-color: var(--code-line); color: var(--code-ink); }
table.dt-out tbody tr:hover td { background: var(--surface-2); }
table.dt-out .idx, table.dt-out th.idx { color: var(--code-dim); text-align: right; background: var(--code-bg-2); }
.mono-block { padding: 12px; font-family: var(--font-mono); font-size: 12.5px; white-space: pre-wrap; color: var(--code-ink); }
.wb-console .empty { margin: 12px; border: 1px dashed var(--code-line); color: var(--code-ink-2); }
.wb-hints { display: flex; flex-direction: column; gap: 8px; }
.hints { margin: 0; padding: 10px 14px 10px 30px; border: 1px solid var(--code-line); border-radius: var(--r-2); background: var(--code-bg-2); color: var(--code-ink); font-size: 12.5px; line-height: 1.7; animation: rise var(--t) var(--ease-out) both; }
.hints li { margin-bottom: 4px; }
.hints li p { display: inline; margin: 0; }
.hints code { font-family: var(--font-mono); color: var(--code-str); }

/* Тема CodeMirror = токены редактора */
.cm-s-ac { background: var(--code-bg); color: var(--code-ink); }
.cm-s-ac .CodeMirror-gutters { background: var(--code-bg); border-right: 1px solid var(--code-line); }
.cm-s-ac .CodeMirror-linenumber { color: var(--code-dim); }
.cm-s-ac .CodeMirror-cursor { border-left: 2px solid var(--accent); }
.cm-s-ac .CodeMirror-selected, .cm-s-ac.cm-s-ac .CodeMirror-focused .CodeMirror-selected { background: color-mix(in srgb, var(--accent) 18%, transparent); }
.cm-s-ac .CodeMirror-activeline-background { background: rgba(255,255,255,.035); }
.cm-s-ac .CodeMirror-activeline .CodeMirror-linenumber { color: var(--ink-2); }
.cm-s-ac .cm-keyword { color: var(--code-kw); }
.cm-s-ac .cm-def, .cm-s-ac .cm-variable-2 { color: var(--code-fn); }
.cm-s-ac .cm-string, .cm-s-ac .cm-string-2 { color: var(--code-str); }
.cm-s-ac .cm-number { color: var(--code-num); }
.cm-s-ac .cm-comment { color: var(--code-cm); font-style: italic; }
.cm-s-ac .cm-operator, .cm-s-ac .cm-punctuation { color: var(--code-ink-2); }
.cm-s-ac .cm-property { color: var(--code-var); }
.cm-s-ac .cm-error { color: var(--err); }

/* ---------- 14. Текст уроков, таблицы, тесты ---------- */
.prose h4 { margin: 16px 0 6px; font-size: 14px; }
.prose table { margin: 12px 0; }
.prose blockquote { margin: 14px 0; padding: 2px 0 2px 14px; border-left: 2px solid var(--line); color: var(--ink-2); max-width: 68ch; }
.prose pre { background: var(--code-bg); border: 1px solid var(--code-line); border-radius: var(--r-2); padding: 12px 14px; overflow-x: auto; font-family: var(--font-mono); font-size: 12.5px; line-height: 1.7; }
.link { color: var(--accent); cursor: pointer; display: inline-flex; align-items: center; gap: 5px; font-size: 12.5px; font-weight: 550; }
.link:hover { text-decoration: underline; text-underline-offset: 3px; }
.table-wrap { margin: 16px 0; overflow-x: auto; }
.t-cap { font-size: 12px; color: var(--ink-3); margin-bottom: 6px; font-weight: 550; }
.o-progress { margin-top: 16px; }
.outcome { margin: 0; padding: 12px 16px 14px 34px; display: flex; flex-direction: column; gap: 7px; font-size: 13.5px; color: var(--ink-2); }
.outcome li { padding-left: 2px; }
.outcome li::marker { color: var(--accent); }
.stat-row { display: flex; gap: 14px; padding: 10px 16px; border-bottom: 1px solid var(--line-soft); }
.stat-row:last-child { border-bottom: 0; }
.stat-row > div { flex: 1; }

.quiz-wrap { display: flex; flex-direction: column; gap: 14px; }
.q-head { display: flex; align-items: center; gap: 12px; margin-bottom: 4px; }
.q-num { font-size: 12px; text-transform: uppercase; letter-spacing: .06em; color: var(--ink-3); font-weight: 650; white-space: nowrap; }
.row-btns { display: flex; align-items: center; gap: 8px; margin-top: 14px; flex-wrap: wrap; }
.result { display: flex; flex-direction: column; gap: 12px; }
.result .stat-big { color: var(--accent); }
.result-list { margin-top: 6px; }
.result-list .opt { cursor: default; }
.result-list .opt:hover { border-color: inherit; background: inherit; }
.pill.neutral { color: var(--ink-2); background: var(--surface-2); border: 1px solid var(--line); }

/* ---------- 15. Прогресс ---------- */
.grp-lbl { color: var(--ink-3); font-size: 11px; text-transform: uppercase; letter-spacing: .07em; font-weight: 650; padding: 10px 0 6px; }
.matrix > div { min-width: 0; }
.mod-head { cursor: pointer; user-select: none; }
.mod-head:hover h3 { color: var(--accent); }
.mod-head .chev { color: var(--ink-3); }
.heat-scroll { overflow-x: auto; padding-bottom: 4px; }


.module-summary { margin-top: 20px; }
 .module-timeline-scroll { overflow-x: auto; }
.module-timeline { min-width: 680px; position: relative; display: flex; justify-content: space-between; gap: 4px; align-items: center; padding: 25px 18px 12px; }
.module-timeline-bar { position: absolute; left: 38px; right: 38px; top: 43px; height: 4px; border-radius: 4px; background: var(--surface-3); }
.module-timeline-bar i { display: block; height: 100%; border-radius: inherit; background: var(--accent); }
.module-point { position: relative; z-index: 1; flex: 0 0 36px; width: 36px; height: 36px; border: 1px solid var(--line); border-radius: 50%; display: inline-flex; align-items: center; justify-content: center; background: var(--surface); color: var(--ink-2); font: 600 12px var(--font-mono); }
.module-point.done { background: var(--accent); border-color: var(--accent); color: var(--paper); }
.module-point.current { border-color: var(--accent); color: var(--accent); }
.module-point:hover { border-color: var(--accent); color: var(--accent); }
.module-timeline-ends { display: flex; justify-content: space-between; padding: 0 18px 16px; color: var(--ink-3); font-size: 12px; }
.skill-profile { margin-bottom: 20px; }
.skill-profile-body { display: grid; grid-template-columns: minmax(0, 380px) minmax(0, 1fr); gap: 22px; padding: 10px 20px 16px; align-items: center; }
.skill-radar { display: block; width: min(100%, 380px); height: auto; margin: auto; }
.skill-radar text { fill: var(--ink); font: 12px var(--font); }
.radar-axis { stroke: var(--line-soft); stroke-width: 1; }
.radar-level { fill: none; stroke-width: 1.5; }
.radar-level.level-1 { stroke: var(--sql); stroke-dasharray: 3 4; }
.radar-level.level-2 { stroke: var(--py); stroke-dasharray: 7 4; }
.radar-level.level-3 { stroke: var(--stat); }
.radar-personal { fill: color-mix(in srgb, var(--accent) 18%, transparent); stroke: var(--accent); stroke-width: 2.5; }
.radar-dot { fill: var(--accent); stroke: var(--surface); stroke-width: 1.5; }
.radar-legend { display: flex; justify-content: center; gap: 5px 12px; flex-wrap: wrap; font-size: 11.5px; }
.radar-legend .personal { color: var(--accent); }
.radar-legend .level-1 { color: var(--sql); }
.radar-legend .level-2 { color: var(--py); }
.radar-legend .level-3 { color: var(--stat); }
.skill-breakdown { min-width: 0; }
.skill-breakdown-toggle { display: none; }
.skill-breakdown-grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); column-gap: 20px; }
.skill-breakdown-grid details { min-width: 0; border-bottom: 1px solid var(--line-soft); padding: 7px 0; }
.skill-breakdown-grid summary { cursor: pointer; display: flex; justify-content: space-between; gap: 8px; font-size: 13px; }
.skill-breakdown-grid summary > span:first-child { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.skill-breakdown-grid summary .num { color: var(--ink-2); }
.skill-level-row { display: flex; justify-content: space-between; gap: 12px; margin-top: 10px; font-size: 13px; color: var(--ink-2); }
.radar-note { grid-column: 1 / -1; color: var(--ink-2); font-size: 12px; margin: 12px 0 0; }

/* ---------- 16. Приспособление к ширине ----------
   Блок намеренно последний: он перебивает базовые правила §3–§15 одной спецификностью,
   поэтому выше в файле его ставить нельзя — вёрстка узкого экрана молча не сработает. */
@media (max-width: 1240px) {
  .grid-2 { grid-template-columns: minmax(0, 1fr); }
  .outline { max-width: 200px; }
  .lab-right { max-width: 42%; }
}

/* Планшет: рельс уходит в иконки (режим задаёт data-rail), колонки урока складываются в столбец. */
@media (max-width: 1020px) {
  .lab { flex-direction: column; height: auto; }
  /* width: 100% обязательно: в столбце auto-маргиналы .lab-mid отменяют растяжение,
     и колонка сама по себе получает ширину min-content — на телефоне это вылезает за экран */
  .lab-mid { flex: none; min-width: 0; width: 100%; }
  .lab > .outline { display: none; }
  /* на узком экране колонки складываются в столбец — сворачивать нечего */
  .lab-right { flex: none; width: auto; max-width: none; min-height: 640px; border-left: 0; border-top: 1px solid var(--code-line); }
  .lab-right.folded > .wb { opacity: 1; pointer-events: auto; }
  .lab-right .p-rail, .lab-right .s-fold { display: none; }
  .matrix { grid-template-columns: minmax(0, 1fr); }
}

/* Телефон: меню — выдвижная колонка, поэтому шапка и панели управления переносятся. */
@media (max-width: 900px) {
  main { padding: 18px 16px 48px; }
  .topbar { gap: 10px; padding: 0 12px; }
  /* крошки на телефоне не помещаются; заголовок экрана и так крупный в тексте страницы */
  .crumbs { display: none; }
  .search-host { flex: 1 1 auto; min-width: 0; margin-left: 0; }
  .field-search { width: auto; min-width: 0; }
  .field-search kbd { display: none; }
  .page-head { flex-wrap: wrap; align-items: flex-start; }
  .page-head .actions { margin-left: 0; flex-wrap: wrap; }
  .split-h { flex-wrap: wrap; }
  .hero-next { flex-wrap: wrap; }
  /* кнопки «продолжить» на телефоне — во всю ширину ряда, по ним попадают большим пальцем */
  .hero-next .split-h { flex: 1 1 100%; }
  .hero-next .btn { flex: 1 1 auto; justify-content: center; }
  .card.pad { padding: 14px; }
  .row { padding: 10px 12px; gap: 9px; }
}

/* Узкий телефон: полосы запуска и вкладок консоли дышат, строки списков не везут лишнего. */
@media (max-width: 640px) {
  main { padding: 14px 12px 40px; }
  .lab-mid { padding: 16px 14px 32px; }
  /* кнопки под тестом и уроком становятся крупнее: по ним попадают пальцем, а не курсором */
  .row-btns .btn { flex: 1 1 auto; justify-content: center; }
  .lab-right { min-height: 560px; }
  .wb-tabs { flex-wrap: wrap; height: auto; padding: 4px 8px; }
  .wb-bar { flex-wrap: wrap; height: auto; padding: 6px 10px; row-gap: 6px; }
  /* запуск и проверка на телефоне — иконки: те же знаки, что в пустых состояниях консоли */
  .wb-bar .lbl, .wb-bar .kbd { display: none; }
  .wb-bar .ed-btn { width: 34px; padding: 0; justify-content: center; }
  .toasts { left: 12px; right: 12px; bottom: 12px; }
  .toast { align-self: stretch; }
  .skill { grid-template-columns: minmax(0, 1fr) 84px 44px; }
  .row .meta { gap: 9px; }
  /* мини-полоса прогресса в строке списка на узком экране съедает название */
  .row .meta .bar { display: none; }
  .heat { grid-template-rows: repeat(7, 11px); grid-auto-columns: 11px; }
}

/* 360 px и меньше: из шапки уходит быстрый срез (он есть в меню), поля формы — в одну колонку. */
@media (max-width: 430px) {
  .cut-btn { display: none; }
  /* вкладок консоли четыре, подписи в ряд не влезают — остаются знакомые знаки */
  .c-tab .lbl { display: none; }
  .c-tab { padding: 5px 9px; }
  .form { grid-template-columns: 1fr; }
  .skill { grid-template-columns: minmax(0, 1fr) 64px 40px; }
  .menu { min-width: min(216px, calc(100vw - 24px)); }
  h1 { font-size: 20px; }
  .stat-big { font-size: 22px; }
}




@media (max-width: 768px) {
  .skill-profile-body { grid-template-columns: minmax(0, 1fr); gap: 4px; padding: 8px 12px 12px; }
  .skill-breakdown-toggle { display: block; cursor: pointer; color: var(--ink-2); font-size: 13px; padding: 6px 4px; }
  .skill-breakdown-grid { margin-top: 4px; }
}
