/* ============================================================
   FGS · «ПРО Будущее» — компоненты превью (этап 1)
   ------------------------------------------------------------
   ❌ Ни одного hex-значения в этом файле. Только var(--...).
   Проверка: grep -rE "#[0-9a-fA-F]{3,8}" preview/css/app.css
             preview/screens preview/js  → должно быть пусто.
   ============================================================ */

/* ---------- База ---------- */

* { box-sizing: border-box; }

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

body {
  margin: 0;
  padding: 0;
  overflow-x: hidden;               /* страховка от горизонтального скролла */
  -webkit-font-smoothing: antialiased;
  transition: background-color var(--dur-base) var(--ease), color var(--dur-base) var(--ease),
              opacity var(--dur-fast) var(--ease);
}

/* Экран, который ещё собирается, не показывается: иначе на долю секунды
   виден каркас без шапки, текста и картинок (js/theme.js, «Экран
   показывается уже собранным»). Фон страницы при этом остаётся — он
   нарисован под body, а не на нём. Появляется экран коротким проявлением,
   без него готовый экран вставал бы рывком. */
html.is-booting body[data-screen] { opacity: 0; }
@media (prefers-reduced-motion: reduce) {
  body { transition-property: background-color, color; }
}

h1, h2, h3 { font-weight: 700; letter-spacing: -.01em; line-height: var(--lh-tight); margin: 0; }
p  { margin: 0 0 var(--sp-3); }
p:last-child { margin-bottom: 0; }
img, svg { max-width: 100%; }
img { height: auto; }
a { color: var(--accent-think); }

/* hidden должен побеждать любой display компонента: иначе скрытая
   плашка остаётся на экране пустой рамкой (так вылезала пустая
   красная плашка надёжности на карте роста). */
[hidden] { display: none !important; }

/* Толщина линии иконок — одна на весь продукт (ART_TODO §2) */
.icon { stroke-width: var(--icon-stroke); }

/* Иконка всегда стоит по центру своего места. <span data-icon> — обычный
   инлайн-бокс, и <svg> внутри садится на базовую линию строки: под иконкой
   оставался хвост в 2–3 px, из-за которого она висела выше середины текста
   на кнопках, в чипах и в шапках карточек. Свой flex-контекст этот хвост
   убирает. vertical-align центрирует сам span, когда он стоит прямо в тексте;
   внутри flex-контейнера это свойство не действует и ничему не мешает.

   [data-theme-icon] — то же место под иконку, только заполняет его theme.js:
   солнце или луна на кнопке переключения темы. */
[data-icon], [data-theme-icon] {
  display: inline-flex; align-items: center; justify-content: center;
  vertical-align: middle; flex: none;
}

.wrap { max-width: var(--wrap-max); margin: 0 auto; padding: 0 var(--sp-5); }
.wrap-narrow { max-width: 760px; margin: 0 auto; padding: 0 var(--sp-5); }
.measure { max-width: var(--measure); }

.visually-hidden {
  position: absolute; width: 1px; height: 1px; overflow: hidden;
  clip: rect(0 0 0 0); clip-path: inset(50%); white-space: nowrap;
}

/* ---------- Шапка экрана ---------- */

.topbar {
  position: sticky; top: 0; z-index: 50;
  background: var(--topbar-bg);
  backdrop-filter: blur(12px);
  border-bottom: 1px solid var(--topbar-border);
}
.topbar-in {
  display: flex; align-items: center; gap: var(--sp-3);
  min-height: var(--topbar-h);
  width: 100%;
  padding: var(--sp-2) var(--sp-6);
  flex-wrap: wrap;
}
.brand {
  display: inline-flex; align-items: center; gap: var(--sp-2);
  font-family: var(--font-serif); font-weight: 700; font-size: var(--fs-h3);
  color: var(--text); text-decoration: none;
}
.brand svg { color: var(--accent-grow); flex: none; }
.topbar-spacer { flex: 1 1 auto; }

/* ---------- Шапка на телефоне ----------
   Меню роли на узком экране — одна-две строки пунктов, и кнопка темы
   уходила под них третьей строкой: шапка занимала четверть экрана
   и не уходила никогда (`DECISIONS` 398). Теперь кнопка темы — справа
   от названия, меню — строкой ниже, а при прокрутке вниз меню
   сворачивается вверх, и остаётся одна строка: «ПРО Будущее» и кнопка
   темы. Потянули страницу вверх — меню разворачивается, с любого места
   (`DECISIONS` 399).

   Сворачивание не двигает страницу: сколько высоты уходит из меню,
   столько же в той же анимации набирает распорка .topbar-fold сразу
   под шапкой. Без этого текст под шапкой прыгал бы вверх на высоту
   меню, а браузер, поправляя прокрутку, разворачивал бы её обратно.
   Распорка — отдельный блок, а не нижний отступ самой шапки: отступ
   складывается с верхним отступом первого блока страницы (у карты
   это 40 px), и текст всё равно уезжал на эти 40 px. У распорки есть
   высота, и отступы через неё не складываются.

   --topbar-nav-h — высота меню, её ставит preview.js (topbarFold)
   на body, чтобы видели и шапка, и распорка; data-collapsible — шапка,
   в которой меню есть. Витрину (`nav=1`) и шапки без меню не трогаем.

   Только до 900 px: на широком экране шапка та же, что была, а
   распорки нет вовсе. */
.topbar-fold { display: none; }
@media (max-width: 900px) {
  /* 10 + 44 + 10 — те же 64 px строки названия, что у шапки без меню,
     но без min-height: свёрнутая шапка должна быть ровно такой. */
  .topbar[data-collapsible] .topbar-in {
    min-height: 0; padding-top: 10px; padding-bottom: 10px; row-gap: 0;
  }
  .topbar[data-collapsible] .topbar-spacer { order: 1; }
  .topbar[data-collapsible] [data-theme-toggle] { order: 2; }
  .topbar[data-collapsible] .rolenav {
    order: 3; flex-basis: 100%; margin-top: var(--sp-2);
    max-height: var(--topbar-nav-h, none); overflow: hidden;
    transition: max-height var(--dur-base) var(--ease), margin-top var(--dur-base) var(--ease),
                opacity var(--dur-base) var(--ease), visibility 0s;
  }
  .topbar-fold { display: block; height: 0; transition: height var(--dur-base) var(--ease); }
  .topbar.is-compact + .topbar-fold { height: calc(var(--topbar-nav-h, 0px) + var(--sp-2)); }
  .topbar[data-collapsible].is-compact .rolenav {
    max-height: 0; margin-top: 0; opacity: 0; visibility: hidden;
    transition-delay: 0s, 0s, 0s, var(--dur-base);
  }
}
/* Где кнопка с подписью рядом с названием не помещается — на 320 px,
   а у админа с эмблемой уже на 375, — от неё остаётся иконка. Иначе
   кнопка уходила бы отдельной строкой, и свёрнутая шапка оставалась бы
   в две строки. Подпись не удаляется, а прячется: её читает экранный
   диктор, и её же меняет theme.js. */
@media (max-width: 340px) {
  .topbar[data-collapsible] [data-theme-label] {
    position: absolute; width: 1px; height: 1px; overflow: hidden;
    clip: rect(0 0 0 0); clip-path: inset(50%); white-space: nowrap;
  }
}
@media (max-width: 380px) {
  .topbar[data-collapsible] .emblem ~ [data-theme-toggle] [data-theme-label] {
    position: absolute; width: 1px; height: 1px; overflow: hidden;
    clip: rect(0 0 0 0); clip-path: inset(50%); white-space: nowrap;
  }
}

/* ---------- Плашка «ПРЕВЬЮ · демо-данные» ---------- */

.ribbon {
  display: inline-flex; align-items: center; gap: var(--sp-2);
  padding: var(--sp-1) var(--sp-3);
  border: 1px dashed var(--ribbon-border);
  border-radius: var(--chip-radius);
  background: var(--ribbon-bg); color: var(--ribbon-fg);
  font-size: var(--fs-xs); letter-spacing: .04em; white-space: nowrap;
}
.ribbon svg { flex: none; }

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

.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: var(--sp-2);
  min-height: var(--btn-min-h); padding: 0 var(--btn-pad-x);
  border: 1px solid var(--btn-secondary-border);
  border-radius: var(--btn-radius);
  background: var(--btn-secondary-bg); color: var(--btn-secondary-fg);
  font: inherit; font-size: var(--fs-small); text-decoration: none;
  cursor: pointer;
  transition: background-color var(--dur-fast) var(--ease),
              border-color var(--dur-fast) var(--ease),
              transform var(--dur-fast) var(--ease);
}
.btn:hover { background: var(--btn-secondary-bg-hover); border-color: var(--accent-grow); }
.btn:active { transform: translateY(1px); }
.btn svg { flex: none; }

.btn-primary {
  background: var(--btn-primary-bg); border-color: var(--btn-primary-bg);
  color: var(--btn-primary-fg); font-weight: 600;
}
.btn-primary:hover { background: var(--btn-primary-bg-hover); border-color: var(--btn-primary-bg-hover); }

.btn-ghost { border-color: transparent; background: transparent; }
.btn-ghost:hover { background: var(--surface-2); border-color: transparent; }

.btn-lg { min-height: var(--tap-child); padding: 0 var(--sp-5); font-size: var(--fs-body); }
.btn-child { min-height: var(--tap-child); font-size: var(--fs-body-child); }

.btn[disabled], .btn[aria-disabled="true"] {
  background: var(--btn-disabled-bg); color: var(--btn-disabled-fg);
  border-color: var(--btn-disabled-border); cursor: not-allowed;
  transform: none;
}

/* ---------- Карточка ---------- */

.card {
  background: var(--card-bg);
  border: 1px solid var(--card-border);
  border-radius: var(--card-radius);
  padding: var(--card-pad-lg);
  box-shadow: var(--card-shadow);
  /* Грид-элемент по умолчанию не ужимается уже своего содержимого.
     Без min-width:0 широкая таблица внутри карточки растягивает всю
     колонку и ломает мобильную ширину. */
  min-width: 0;
  transition: background-color var(--dur-base) var(--ease),
              border-color var(--dur-base) var(--ease);
}
.card-flat { box-shadow: var(--shadow-1); }
.card-head { display: flex; align-items: center; gap: var(--sp-3); margin-bottom: var(--sp-4); }
.card-head h2, .card-head h3 { font-size: var(--fs-h3); }

/* круглый бейдж с номером блока отчёта — часть навигации, не декор */
.badge-num {
  flex: none;
  width: var(--badge-num-size); height: var(--badge-num-size);
  display: grid; place-items: center;
  border-radius: var(--r-full);
  background: var(--badge-num-bg); color: var(--badge-num-fg);
  border: 1px solid var(--badge-num-border);
  font-size: var(--fs-small); font-weight: 700;
  font-variant-numeric: tabular-nums;
}

/* тот же бейдж на тёмной подложке героя */
.badge-num-hero {
  background: var(--badge-num-bg-hero);
  color: var(--badge-num-fg-hero);
  border-color: var(--badge-num-fg-hero);
}

/* ---------- Сетка ---------- */

.grid { display: grid; gap: var(--sp-5); }
.g-2 { grid-template-columns: 1fr 1fr; }
.g-3 { grid-template-columns: repeat(3, 1fr); }
.g-side { grid-template-columns: 1.15fr .85fr; }
.g-map  { grid-template-columns: 1.5fr 1fr; }
.stack { display: grid; gap: var(--sp-4); }
.stack-sm { display: grid; gap: var(--sp-2); }
section { margin-bottom: var(--gap-block); }

/* ---------- Золотая нить между блоками ---------- */

.thread { display: flex; justify-content: center; padding: var(--sp-2) 0 var(--sp-5); }
.thread svg { color: var(--thread); opacity: .55; }

/* ---------- Блок-герой: ВСЕГДА тёмный, в обеих темах ---------- */

.hero {
  position: relative; overflow: hidden;
  background: var(--hero-gradient);
  border-radius: var(--hero-radius);
  color: var(--hero-text);
  box-shadow: var(--shadow-3);
}
.hero::after {
  content: ""; position: absolute; right: 6%; top: -18%;
  width: 520px; height: 520px; max-width: 90%;
  background: var(--hero-glow); pointer-events: none;
}
.hero-grid {
  position: relative; z-index: 1;
  display: grid; grid-template-columns: 1.15fr .85fr;
  gap: var(--sp-6); padding: var(--sp-8);
  align-items: end;
}
.hero h1, .hero h2 { color: var(--hero-text); }
.hero p { color: var(--hero-text); }
.hero-sub { color: var(--hero-text-2); font-size: var(--fs-small); }
.eyebrow {
  display: inline-flex; align-items: center; gap: var(--sp-2);
  font-size: var(--fs-xs); letter-spacing: var(--tracking-eyebrow);
  text-transform: uppercase; color: var(--hero-accent);
  margin-bottom: var(--sp-3);
}
.hero-quote {
  margin-top: var(--sp-5);
  padding: var(--sp-4) var(--sp-5);
  border-left: var(--thread-width) solid var(--hero-accent);
  border-radius: 0 var(--r-md) var(--r-md) 0;
  background: var(--hero-quote-bg);
  font-family: var(--font-serif); font-size: var(--fs-h3);
  color: var(--hero-text);
}
.hero-quote p { color: var(--hero-text); margin-bottom: var(--sp-1); }
.hero-figure { display: flex; align-items: flex-end; justify-content: center; }
.hero-figure img {
  display: block; width: 100%; max-width: 340px;
  filter: drop-shadow(0 18px 40px rgba(0,0,0,.5));
}
.hero-panel {
  padding: var(--sp-6);
  background: var(--hero-panel-bg);
  border-left: 1px solid var(--hero-border);
  display: flex; flex-direction: column; justify-content: center; gap: var(--sp-3);
}
.hero-counter {
  display: inline-flex; align-items: center; gap: var(--sp-3);
  align-self: flex-start;
  padding: var(--sp-3) var(--sp-5);
  border: 1px solid var(--hero-border); border-radius: var(--r-md);
  background: var(--hero-quote-bg);
  font-family: var(--font-serif); font-size: var(--fs-h3);
  color: var(--hero-text);
}
.hero-counter b { color: var(--hero-accent); font-variant-numeric: tabular-nums; }
.hero-counter svg { color: var(--hero-accent); }

/* ---------- Статусы: иконка + слово + цвет. Никогда только цвет ---------- */

.status {
  display: inline-flex; align-items: center; gap: var(--sp-2);
  font-size: var(--fs-xs); font-weight: 600; line-height: 1.3;
}
.status svg { flex: none; }
.status-strong  { color: var(--status-strong-fg); }
.status-growing { color: var(--status-growing-fg); }
.status-hard    { color: var(--status-hard-fg); }

.status-pill {
  padding: var(--sp-1) var(--sp-3); border-radius: var(--chip-radius);
  border: 1px solid currentColor;
}
.status-strong.status-pill  { background: var(--status-strong-bg); }
.status-growing.status-pill { background: var(--status-growing-bg); }
.status-hard.status-pill    { background: var(--status-hard-bg); }

/* те же статусы поверх тёмного героя */
.on-hero .status-strong  { color: var(--status-strong-fg-hero); }
.on-hero .status-growing { color: var(--status-growing-fg-hero); }

/* ---------- Плитка иконки мира ---------- */

.tile {
  flex: none;
  width: var(--tile-size); height: var(--tile-size);
  display: grid; place-items: center;
  border: 1px solid currentColor; border-radius: var(--tile-radius);
}
.tile-sm { width: 28px; height: 28px; }

/* строка «мир + статус» */
.world { display: flex; gap: var(--sp-3); align-items: flex-start; padding: var(--sp-2) 0; }
.world-body { min-width: 0; }
.world-name { display: block; font-weight: 600; font-size: var(--fs-small); color: var(--text); }
/* Подпись категории под названием мира: «Башня Знаний / Когнитивные навыки
   и мышление». Была локальной у блока 3 письма (P35), стала общей — те же
   две строки стоят теперь и в «Нашем маршруте» на P23, P35 и P35b. */
.world-note { display: block; font-size: var(--fs-xs); color: var(--text-muted); }
.world-state { display: block; font-size: var(--fs-xs); }
.world-score {
  font-variant-numeric: tabular-nums; font-weight: 700;
  font-size: var(--fs-small); color: var(--text);
}

.world-list { list-style: none; margin: 0; padding: 0; display: grid; gap: var(--sp-2); }

/* ---------- Чип / легенда ---------- */

.chip {
  display: inline-flex; align-items: center; gap: var(--sp-2);
  padding: var(--sp-1) var(--sp-3);
  border: 1px solid var(--chip-border); border-radius: var(--chip-radius);
  background: var(--chip-bg); color: var(--chip-fg);
  font-size: var(--fs-xs);
  transition: border-color var(--dur-base) var(--ease),
              background-color var(--dur-base) var(--ease),
              color var(--dur-base) var(--ease),
              opacity var(--dur-base) var(--ease);
}

/* ---------- Отметка прогресса ----------
   Шаг пройден / идёт сейчас / ещё впереди. Одна манера на весь
   продукт: раньше каждый экран красил свои шаги инлайновым стилем,
   и «сейчас» на одном экране выглядело как «готово» на другом.

   Цветом отмечается только состояние шага. Ни числа, ни процента
   рядом не появляется — на детских экранах это запрещено (правило 1),
   а на взрослых просто не нужно. */

.chip-done { border-color: var(--accent-grow); color: var(--status-strong-fg);
             background: var(--status-strong-bg); }
.chip-now  { border-color: var(--thread); color: var(--status-growing-fg);
             background: var(--status-growing-bg); }
.chip-next { opacity: .55; }
.chip-off  { opacity: .45; }
/* Шаг, который не состоялся: связку отклонили, разбор упал. */
.chip-stop { border-color: var(--accent-care); color: var(--status-hard-fg);
             background: var(--status-hard-bg); }
.legend {
  display: flex; flex-wrap: wrap; gap: var(--sp-4);
  margin-top: var(--sp-5); padding-top: var(--sp-4);
  border-top: 1px solid var(--rule);
  font-size: var(--fs-xs);
}
.legend-item { display: inline-flex; align-items: center; gap: var(--sp-2); }
.legend-item svg { flex: none; }

/* ---------- Заглушка иллюстрации (только в финальном стиле) ---------- */

.placeholder {
  display: grid; place-items: center; text-align: center;
  padding: var(--sp-5);
  min-height: 200px;
  border: 1px dashed var(--ph-border);
  border-radius: var(--ph-radius);
  background: var(--ph-glow), var(--ph-bg);
  color: var(--ph-fg); font-size: var(--fs-xs);
}
.placeholder b { display: block; color: var(--text-2); font-size: var(--fs-small); margin-bottom: var(--sp-1); }
/* та же заглушка поверх тёмной сцены: светлая плита там била бы по глазам */
.placeholder-on-dark {
  background: none; border-color: var(--hero-border); color: var(--hero-text-2);
}
.placeholder-on-dark b { color: var(--hero-text); }
.placeholder-on-dark svg { color: var(--hero-accent); }
.scene-bg .placeholder { position: absolute; inset: var(--sp-4); }
.placeholder svg { color: var(--thread); opacity: .7; margin-bottom: var(--sp-2); }
.caption { font-size: var(--fs-xs); color: var(--text-muted); text-align: center; margin-top: var(--sp-3); }

/* ---------- Вариант ответа теста (порядок 10/7/3/0 — жёсткий) ---------- */

.option {
  display: flex; align-items: flex-start; gap: var(--sp-3);
  width: 100%; text-align: left;
  min-height: var(--option-min-h);
  padding: var(--option-pad);
  border: 1px solid var(--option-border); border-radius: var(--option-radius);
  background: var(--option-bg); color: var(--text);
  font: inherit; font-size: var(--fs-body-child); line-height: 1.45;
  cursor: pointer;
  transition: background-color var(--dur-fast) var(--ease), border-color var(--dur-fast) var(--ease);
}
.option:hover { background: var(--option-bg-hover); border-color: var(--border-strong); }
.option-marker {
  flex: none; width: var(--option-marker-size); height: var(--option-marker-size);
  margin-top: 2px;
  border: 2px solid var(--border-strong); border-radius: var(--r-full);
  display: grid; place-items: center;
}
.option[aria-checked="true"] {
  border-color: var(--option-border-selected);
  background: var(--option-bg-selected);
}
.option[aria-checked="true"] .option-marker { border-color: var(--option-border-selected); color: var(--option-border-selected); }
.option-list { display: grid; gap: var(--sp-3); }

/* ---------- Выбор из двух ----------
   Пол на P03 и P04. Не список: вариантов ровно два, и выпадающий список
   из двух пунктов стоит человеку лишнего нажатия. Не радиокнопки
   браузера: их вид не поддаётся теме, а обе темы обязаны работать
   на каждом экране (FGS_02 §3.1 п.5).

   Кегль здесь общий, а не детский: на P03 поля вокруг набраны так же,
   и переключатель, набранный крупнее соседнего «Класс», выбивался бы
   из формы. */
.choice { display: flex; gap: var(--sp-3); flex-wrap: wrap; }
.choice-option {
  flex: 1 1 0; min-width: 120px;
  padding: var(--sp-3) var(--sp-4);
  border: 1px solid var(--option-border); border-radius: var(--option-radius);
  background: var(--option-bg); color: var(--text);
  font: inherit; cursor: pointer; text-align: center;
  transition: background-color var(--dur-fast) var(--ease), border-color var(--dur-fast) var(--ease);
}
.choice-option:hover { background: var(--option-bg-hover); border-color: var(--border-strong); }
.choice-option[aria-checked="true"] {
  border-color: var(--option-border-selected);
  background: var(--option-bg-selected);
  font-weight: 600;
}

/* ---------- Поля ввода ---------- */

.field { display: grid; gap: var(--sp-2); }
.field label { font-size: var(--fs-small); font-weight: 600; color: var(--text); }
.field .hint { font-size: var(--fs-xs); color: var(--text-muted); }
.input {
  width: 100%; min-height: var(--input-min-h);
  padding: var(--sp-2) var(--input-pad-x);
  border: 1px solid var(--input-border); border-radius: var(--input-radius);
  background: var(--input-bg); color: var(--text);
  font: inherit; font-size: var(--fs-body);
}
.input::placeholder { color: var(--input-placeholder); }
.input:focus { border-color: var(--input-border-focus); }
.input[disabled] { background: var(--surface-2); color: var(--text-muted); cursor: not-allowed; }
.field-error .input { border-color: var(--input-error); background: var(--input-error-bg); }
/* Выбор из двух краснеет так же, как поле ввода: у пола нет `.input`,
   а ошибка на нём такая же обычная, как «Выбери класс.» рядом. */
.field-error .choice-option { border-color: var(--input-error); }
.error-text {
  display: flex; align-items: center; gap: var(--sp-2);
  font-size: var(--fs-xs); color: var(--input-error);
}
.error-text svg { flex: none; }

/* поле пароля с кнопкой «показать» */
.pw { position: relative; display: block; }
.pw .input { padding-right: calc(var(--input-pad-x) + 40px); }
.pw-toggle {
  position: absolute; right: 6px; top: 50%; transform: translateY(-50%);
  width: 36px; height: 36px; display: grid; place-items: center;
  border: 0; border-radius: var(--r-sm); background: transparent;
  color: var(--text-muted); cursor: pointer; padding: 0;
}
.pw-toggle:hover { color: var(--text); background: var(--surface-2); }
.pw-toggle:focus-visible { outline: var(--focus-ring); outline-offset: var(--focus-offset); }

/* ---------- Слот под текст, который придёт от заказчика ---------- */

.copy-slot {
  display: block; color: var(--text-muted); font-style: italic;
  border-left: var(--thread-width) solid var(--border-strong);
  padding-left: var(--sp-4); max-width: var(--measure);
}
.on-dark .copy-slot, .scene-body .copy-slot {
  color: var(--hero-text-2); border-left-color: var(--hero-border);
}

/* ---------- Прогресс главы ---------- */

.progress {
  height: var(--progress-h); border-radius: var(--r-full);
  background: var(--progress-track); overflow: hidden;
}
.progress > i {
  display: block; height: 100%; background: var(--progress-fill); border-radius: inherit;
  transition: width var(--dur-base) var(--ease);
}
.progress-label { font-size: var(--fs-xs); color: var(--text-muted); }

/* ---------- Чат с Хранителем ---------- */

.bubble {
  max-width: var(--bubble-max);
  padding: var(--sp-3) var(--sp-4);
  border-radius: var(--bubble-radius);
  font-size: var(--fs-body-child); line-height: 1.5;
}
.bubble-ai    { background: var(--bubble-ai-bg); color: var(--bubble-ai-fg); border-bottom-left-radius: var(--r-sm); }
.bubble-child { background: var(--bubble-child-bg); color: var(--bubble-child-fg); margin-left: auto; border-bottom-right-radius: var(--r-sm); }

/* ---------- Таблица (только родитель и админка) ---------- */

/* Широкая таблица прокручивается внутри своего контейнера,
   а не гонит горизонтальный скролл всей страницы */
.table-scroll { overflow-x: auto; max-width: 100%; min-width: 0; }
.table { width: 100%; border-collapse: collapse; font-size: var(--fs-small); }
.table th, .table td {
  text-align: left; padding: var(--sp-3);
  border-bottom: 1px solid var(--table-border); vertical-align: top;
}
.table thead th { background: var(--table-head-bg); font-size: var(--fs-xs); color: var(--text-2); font-weight: 600; }
.table tbody tr:hover { background: var(--table-row-hover); }
.table .num { font-variant-numeric: tabular-nums; text-align: right; white-space: nowrap; }

/* ---------- Мелочи ---------- */

.rule { height: 1px; background: var(--rule); border: 0; margin: var(--sp-5) 0; }
.muted { color: var(--text-muted); }
.text-2 { color: var(--text-2); }
.small { font-size: var(--fs-small); }
.xs { font-size: var(--fs-xs); }
.serif { font-family: var(--font-serif); }
.center { text-align: center; }
.nowrap { white-space: nowrap; }

.page-footer {
  padding: var(--sp-8) 0 var(--sp-10);
  color: var(--text-muted); font-size: var(--fs-xs); text-align: center;
}

/* ============================================================
   ЭКРАННЫЕ КАРКАСЫ — общие для путей ребёнка и родителя
   ============================================================ */

/* колонка контента экрана: у ребёнка кегль крупнее */
.shell { max-width: 780px; margin: 0 auto; padding: var(--sp-7) var(--sp-5) var(--sp-9); }
.shell-wide { max-width: 1000px; }
.shell-child { font-size: var(--fs-body-child); }

/* ---------- Сцена: тёмный кадр с иллюстрацией на всю ширину ---------- */

.scene {
  position: relative; overflow: hidden;
  border-radius: var(--hero-radius);
  background: var(--hero-gradient);
  color: var(--hero-text);
  box-shadow: var(--shadow-3);
  min-height: 420px;
  display: flex; align-items: flex-end;
}
.scene-bg {
  position: absolute; inset: 0;
  background-size: cover; background-position: center;
}
.scene-bg::after {
  /* затемнение под текст: без него белый текст на светлых участках не читается */
  content: ""; position: absolute; inset: 0;
  background: linear-gradient(90deg,
              var(--scene-veil-strong) 0%, var(--scene-veil) 45%, transparent 75%);
}
.scene-veil-right .scene-bg::after {
  background: linear-gradient(270deg,
              var(--scene-veil-strong) 0%, var(--scene-veil) 45%, transparent 75%);
}
.scene-body { position: relative; z-index: 1; padding: var(--sp-8); max-width: 620px; }
.scene-body.is-right { margin-left: auto; }
.scene-figure {
  position: absolute; right: var(--sp-6); bottom: 0; z-index: 1;
  width: 34%; max-width: 300px; pointer-events: none;
}
.scene-figure img,
.scene-figure-narrow img { display: block; width: 100%; filter: drop-shadow(0 12px 30px rgba(0,0,0,.55)); }
/* Та же история, что с яйцом ниже (`DECISIONS` 393 и 397): на телефоне
   фигура из угла вставала в строку рядом с текстом, ужималась до 38 px
   и сидела у нижнего края. Там показывается .scene-figure-narrow —
   внутри текста, над кнопкой, по центру; на широком экране её нет. */
.scene-figure-narrow { display: none; }

/* ---------- Яйцо Хранителя ----------
   Весь первый акт ребёнок видит не Хранителя, а свет внутри скорлупы.
   Сила света — это и есть прогресс: он растёт от главы к главе и при
   этом не выражен ни одним числом (обещание из path-child).
   Ступень задаётся атрибутом data-glow="0…5" на самом элементе.
   Хранитель не показывается ни на одном экране до P20 — см. DECISIONS. */

.egg { position: relative; display: inline-block; line-height: 0; }
.egg img { position: relative; z-index: 1; display: block; width: 100%; height: auto; }
.egg::before {
  content: ""; position: absolute; z-index: 0;
  left: 50%; top: 54%; width: 130%; height: 130%;
  transform: translate(-50%, -50%);
  border-radius: var(--r-full);
  background: radial-gradient(circle, var(--thread) 0%, transparent 62%);
  opacity: var(--egg-glow, .18);
  filter: blur(26px);
  transition: opacity var(--dur-slow, .6s) ease;
  pointer-events: none;
}
/* Ступени свечения. 0 — почти спит, 5 — вот-вот треснет. */
.egg[data-glow="0"] { --egg-glow: .10; }
.egg[data-glow="1"] { --egg-glow: .20; }
.egg[data-glow="2"] { --egg-glow: .32; }
.egg[data-glow="3"] { --egg-glow: .46; }
.egg[data-glow="4"] { --egg-glow: .62; }
.egg[data-glow="5"] { --egg-glow: .82; }

/* Медленное дыхание света. Выключается вместе с остальной анимацией. */
.egg-breathe::before { animation: egg-breathe 5.5s ease-in-out infinite; }
@keyframes egg-breathe {
  0%, 100% { transform: translate(-50%, -50%) scale(1); }
  50%      { transform: translate(-50%, -50%) scale(1.12); }
}
@media (prefers-reduced-motion: reduce) {
  .egg-breathe::before { animation: none; }
}

/* Яйцо, стоящее в сцене вместо фигуры персонажа. */
.scene-egg {
  position: absolute; right: var(--sp-7); bottom: var(--sp-6); z-index: 1;
  width: 22%; max-width: 210px; pointer-events: none;
}
.scene-veil-right .scene-egg { right: auto; left: var(--sp-7); }

/* На телефоне угла у сцены нет: она — столбец текста, и яйцо из угла
   вставало в строку рядом с ним, ужималось до 30–40 px и пряталось
   у нижнего края (`DECISIONS` 393). Поэтому у экрана с яйцом их два:
   угловое .scene-egg — для широкого экрана и .scene-egg-narrow внутри
   текста, прямо над кнопкой, — для узкого. Каждая ширина видит только
   своё, и широкий экран выглядит ровно как раньше: второго яйца на нём нет. */
.scene-egg-narrow { display: none; }
@media (max-width: 900px) {
  .scene-egg { display: none; }
  .scene-egg-narrow {
    display: block; width: 180px; max-width: 75%;
    margin: var(--sp-5) auto 0; pointer-events: none;
  }
}

/* реплики персонажей и ремарки */
.speech { font-family: var(--font-serif); font-size: var(--fs-h3); line-height: 1.5; }
.speech-line { font-family: var(--font-serif); font-weight: 700; }
.stage-direction { color: var(--hero-text-2); font-style: italic; font-size: var(--fs-small); }
.card .stage-direction, .shell .stage-direction { color: var(--text-muted); }
.on-dark .stage-direction { color: var(--hero-text-2); }

/* ---------- Формы входа и регистрации ---------- */

.auth { max-width: 460px; margin: 0 auto; }
.auth .card { padding: var(--sp-6); }
.auth-title { font-size: var(--fs-h2); margin-bottom: var(--sp-2); }
.auth-sub { color: var(--text-muted); font-size: var(--fs-small); margin-bottom: var(--sp-5); }
.auth-foot { margin-top: var(--sp-5); padding-top: var(--sp-4); border-top: 1px solid var(--rule);
             font-size: var(--fs-small); color: var(--text-muted); }
.role-pick { display: grid; gap: var(--sp-3); grid-template-columns: 1fr 1fr; }
.role-card {
  display: grid; gap: var(--sp-2); justify-items: center; text-align: center;
  padding: var(--sp-5) var(--sp-4); min-height: 140px;
  border: 1px solid var(--border-strong); border-radius: var(--r-card);
  background: var(--surface); color: var(--text); text-decoration: none;
  transition: background-color var(--dur-fast) var(--ease), border-color var(--dur-fast) var(--ease);
}
.role-card:hover { background: var(--surface-2); border-color: var(--accent-grow); }
.role-card b { font-family: var(--font-serif); font-size: var(--fs-h3); }
.role-card span { font-size: var(--fs-xs); color: var(--text-muted); }
.role-card svg { color: var(--accent-grow); }

/* чекбокс согласия */
.check-row { display: flex; gap: var(--sp-3); align-items: flex-start; }
.check-row input[type="checkbox"] { width: 22px; height: 22px; margin-top: 2px; flex: none;
                                    accent-color: var(--accent-grow); }
.check-row label { font-size: var(--fs-small); }

/* ---------- Код Хранителя ---------- */

.code-box {
  display: flex; gap: var(--sp-3); align-items: center; justify-content: center;
  font-family: var(--font-serif); font-size: var(--fs-h1); letter-spacing: .12em;
  padding: var(--sp-5); border-radius: var(--r-card);
  border: 1px dashed var(--thread); background: var(--surface-2); color: var(--text);
}

/* ---------- Карта десяти миров ---------- */

.map-grid { display: grid; gap: var(--sp-3); grid-template-columns: repeat(auto-fill, minmax(200px, 1fr)); }
.map-place {
  display: flex; gap: var(--sp-3); align-items: center;
  padding: var(--sp-3) var(--sp-4); min-height: var(--tap-child);
  border: 1px solid var(--border); border-radius: var(--r-md);
  background: var(--surface); color: var(--text-muted);
}
.map-place b { font-size: var(--fs-small); color: var(--text); font-weight: 600; }
.map-place-open { border-color: var(--accent-grow); color: var(--accent-grow); background: var(--status-strong-bg); }
.map-place-done { border-color: var(--border-strong); color: var(--accent-win); }

/* карточка главы с всплывающим описанием */
.map-place-btn {
  position: relative; width: 100%; text-align: left;
  font: inherit; cursor: pointer;
  transition: border-color var(--dur-fast) var(--ease), background-color var(--dur-fast) var(--ease);
}
.map-place-btn:hover,
.map-place-btn:focus-visible { border-color: var(--accent-grow); background: var(--surface-2); }
.map-place-btn .map-place-num {
  flex: none; width: 26px; height: 26px; border-radius: var(--r-full);
  display: grid; place-items: center; font-size: var(--fs-xs); font-weight: 700;
  background: var(--surface-2); color: var(--text-muted); border: 1px solid var(--border-strong);
}
.map-place-text { display: grid; gap: 2px; min-width: 0; }

.map-pop {
  position: absolute; left: 50%; bottom: calc(100% + 12px); transform: translateX(-50%);
  width: min(340px, 78vw); z-index: 30;
  padding: var(--sp-4); border-radius: var(--r-card);
  border: 1px solid var(--border-strong); background: var(--surface);
  box-shadow: var(--shadow-3); color: var(--text);
  opacity: 0; visibility: hidden; pointer-events: none;
  transition: opacity var(--dur-fast) var(--ease);
}
.map-place-btn::after {
  content: ""; position: absolute; left: 50%; bottom: calc(100% + 4px);
  transform: translateX(-50%); z-index: 31;
  border: 8px solid transparent; border-top-color: var(--border-strong);
  opacity: 0; visibility: hidden; transition: opacity var(--dur-fast) var(--ease);
}
.map-place-btn:hover .map-pop,
.map-place-btn:focus-visible .map-pop,
.map-place-btn.is-open .map-pop,
.map-place-btn:hover::after,
.map-place-btn:focus-visible::after,
.map-place-btn.is-open::after { opacity: 1; visibility: visible; }
.map-pop b { display: block; font-family: var(--font-serif); font-size: var(--fs-h3);
             margin-bottom: var(--sp-2); color: var(--text); }
.map-pop p { font-size: var(--fs-small); color: var(--text-2); margin: 0; }
.map-pop .copy-slot { padding-left: var(--sp-3); font-size: var(--fs-small); }

/* подсказка «здесь есть подробный разбор» — только там, где он написан */
.map-pop-more {
  display: flex; gap: var(--sp-2); align-items: center;
  margin-top: var(--sp-3); padding-top: var(--sp-3);
  border-top: 1px solid var(--border);
  font-size: var(--fs-xs); color: var(--accent-grow);
}

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

/* Кадр шире колонки текста: карта — главный объект экрана,
   ей нужен воздух, иначе локации стоят вплотную друг к другу.

   Пропорции кадра жёстко 16:9 — те же, что у картинки мира (map.bg).
   Это не украшение: при любых других пропорциях `cover` срезает мир
   по краям, и локации, привязанные к процентам, съезжают с озера,
   гор и города на другую местность. С 16:9 процент в PLACES — это
   ровно процент по картинке, на любом экране.

   Поэтому карта тянется не только по ширине, но и по высоте: она
   берёт всё свободное место экрана и при этом остаётся видна целиком.
   --map-h — сколько высоты ей досталось после шапки, отступов, кнопок
   под картой и полосы просмотра; его считает FGS.fitWorldMap по факту
   вёрстки конкретного экрана. Значение ниже — запас на случай, когда
   скрипт ещё не отработал: карта сразу рисуется примерно нужного
   размера и не прыгает. Ширину выбирает min(): что меньше — экран,
   предел макета или высота, пересчитанная в 16:9. */
.worldmap {
  --map-h: max(320px, calc(100vh - 15rem));
  position: relative; overflow: hidden;
  width: min(calc(100vw - 2 * var(--sp-5)),
             calc(var(--wrap-max) * 1.3),
             calc(var(--map-h) * 16 / 9));
  margin-left: 50%; margin-top: var(--sp-7); margin-bottom: var(--sp-6);
  transform: translateX(-50%);
  border-radius: var(--hero-radius);
  background: var(--hero-gradient);
  box-shadow: var(--shadow-3);
  aspect-ratio: 16 / 9;
  /* Кадр — единица измерения для всего, что на нём стоит: локации заданы
     процентами, подписи — в cqw. Иначе при сужении карты текст оставался
     прежнего кегля, длинные названия начинали переноситься на две строки,
     локация вырастала вниз — и подпись ложилась на картинку соседа.
     Ширина карты от содержимого не зависит (см. width выше), поэтому
     inline-size здесь безопасен. */
  container-type: inline-size;
}
@supports (height: 100svh) {
  .worldmap { --map-h: max(320px, calc(100svh - 15rem)); }
}
.worldmap-bg { position: absolute; inset: 0; background-size: cover; background-position: center; }
.worldmap-bg::after {
  content: ""; position: absolute; inset: 0;
  background: linear-gradient(180deg, var(--scene-veil) 0%, transparent 35%, var(--scene-veil) 100%);
}
.worldmap-bg .placeholder { position: absolute; inset: var(--sp-5); }

.worldmap-places { position: absolute; inset: 0; }

/* Размер локации — доля кадра, а не пиксели: кадр 16:9, поэтому
   9 % ширины — это всегда одна и та же доля мира. Со старым
   `15% / max-width:150px` на узком экране объекты становились
   в полтора раза крупнее относительно карты и налезали друг
   на друга; предел в px нужен только самому широкому кадру.

   Долю уменьшили с 10.5 % до 9 %: локация вместе с подписью стала
   ниже, и подписи верхнего ряда перестали доставать до картинок
   нижнего (см. .place-name). */
.place {
  position: absolute; transform: translate(-50%, -50%);
  width: 9%; min-width: 80px; max-width: 132px;
  /* minmax(0, 1fr), а не auto: колонка обязана остаться шириной самой
     локации. Иначе длинная подпись (см. .place-name) растягивает колонку
     по max-content, а вместе с ней и картинку — она задана в 100 %. */
  display: grid; grid-template-columns: minmax(0, 1fr);
  justify-items: center; gap: var(--sp-2);
  padding: 0; border: 0; background: none; color: var(--hero-text);
  font: inherit; cursor: pointer;
  transition: opacity var(--dur-base) var(--ease), filter var(--dur-base) var(--ease),
              transform var(--dur-base) var(--ease);
}
.worldmap.is-focus .place { opacity: .35; filter: saturate(.35); }
.worldmap.is-focus .place.is-active { opacity: 1; filter: none; }
/* локация под курсором заметно выходит вперёд: по ней выбирают, куда идти */
.place.is-active { transform: translate(-50%, -50%) scale(1.25); z-index: 10; }
.place:focus-visible { outline: var(--focus-ring); outline-offset: var(--focus-offset); border-radius: var(--r-md); }

.place-art { position: relative; width: 100%; aspect-ratio: 1 / 1; display: grid; place-items: center; }
.place-art img { width: 100%; height: 100%; object-fit: contain;
                 filter: drop-shadow(0 10px 22px rgba(0,0,0,.45)); }

/* пока картинки локации нет — медальон с иконкой мира */
.place-badge {
  width: 78%; aspect-ratio: 1 / 1; border-radius: var(--r-full);
  display: grid; place-items: center;
  background: var(--hero-quote-bg); border: 2px solid var(--hero-border);
  box-shadow: var(--shadow-2); color: var(--hero-accent);
}
/* Подпись шире самой локации и потому умещается в одну строку — включая
   «Ярмарка Возможностей» и «Кузница Дисциплины». Раньше она была
   ограничена шириной картинки, эти два названия переносились на две
   строки, локация вырастала вниз — и подпись ложилась прямо на картинку
   соседа снизу. По ширине подписи расходятся свободно: соседи по
   горизонтали стоят далеко друг от друга.

   Кегль — в долях кадра (cqw), а не в пикселях: только так точка переноса
   строки остаётся одной и той же на любой ширине карты. */
.place-name {
  width: max-content; max-width: 200%;
  font-family: var(--font-serif); font-size: clamp(11px, 1.35cqw, 15px);
  font-weight: 700;
  text-align: center; line-height: 1.2; text-shadow: 0 2px 10px rgba(0,0,0,.75);
}
.place.is-locked .place-badge { border-style: dashed; color: var(--hero-text-2); }

/* Описание всплывает у самой точки — справа от неё, а если справа
   уже нет места, слева. Без наведения его на карте нет вовсе. */
.place-pop {
  position: absolute; left: calc(100% + var(--sp-3)); top: 50%;
  transform: translateY(-50%);
  width: 300px; max-width: 76vw; z-index: 20; text-align: left;
  padding: var(--sp-4); border-radius: var(--r-card);
  border: 1px solid var(--hero-border); background: var(--hero-panel);
  color: var(--hero-text);
  box-shadow: var(--shadow-3);
  opacity: 0; visibility: hidden; pointer-events: none;
  transition: opacity var(--dur-fast) var(--ease);
}
.place.is-flip .place-pop { left: auto; right: calc(100% + var(--sp-3)); }
.place.is-active .place-pop { opacity: 1; visibility: visible; }
/* окно не должно расти вместе с локацией — гасим её увеличение обратной шкалой */
.place.is-active .place-pop { transform: translateY(-50%) scale(.8); transform-origin: left center; }
.place.is-flip.is-active .place-pop { transform-origin: right center; }
.place-pop h3 { font-family: var(--font-serif); font-size: var(--fs-h3);
                color: var(--hero-text); margin-bottom: var(--sp-2); }
.place-pop p { font-size: var(--fs-small); color: var(--hero-text); margin: 0; }
/* как обстоят дела в этой главе — видит только родитель */
.place-pop .place-status { margin-bottom: var(--sp-3); }
.place-pop .place-hint {
  display: flex; align-items: center; gap: var(--sp-2);
  margin-top: var(--sp-3); padding-top: var(--sp-3);
  border-top: 1px solid var(--hero-border);
  font-size: var(--fs-xs); color: var(--hero-accent);
}
.place-pop .place-locked {
  display: block; margin-top: var(--sp-3); padding-top: var(--sp-3);
  border-top: 1px solid var(--hero-border);
  font-size: var(--fs-xs); color: var(--hero-text-2);
}

/* Название карты — в левом нижнем углу кадра, подписью на карте:
   серифный курсив, как на старых картах. Гаснет вместе с локациями. */
.worldmap-title {
  position: absolute; left: var(--sp-7); bottom: var(--sp-6); z-index: 2;
  font-family: var(--font-serif); font-style: italic; font-weight: 400;
  /* Не --fs-display: у родителя надпись длиннее на треть («Карта
     взросления ребёнка») и на display-кегле наезжала на подпись
     локации переднего плана. */
  font-size: var(--fs-h2); line-height: 1;
  letter-spacing: .01em;
  color: var(--hero-text); text-shadow: 0 2px 18px rgba(0,0,0,.85);
  pointer-events: none;
  transition: opacity var(--dur-base) var(--ease);
}
.worldmap.is-focus .worldmap-title { opacity: .35; }

/* Кнопка «Начать путешествие» — в правом ВЕРХНЕМ углу кадра. Под картой
   она была за нижним краем окна: карта растянута на весь экран от шапки
   до низа, и кнопку под ней видел только тот, кто догадался прокрутить
   страницу.

   Наверху, а не внизу (правка 7 сентября, `DECISIONS` 352): в нижнем
   углу она стояла на земле, и ради неё пришлось подвинуть «Кузницу
   Дисциплины» — локации внизу кадра встали теснее, чем ложится
   местность. Сверху небо: земли выше ~30 % нет, локаций там не бывает,
   и двигать под кнопку некого.

   z-index выше локации под курсором (.place.is-active, 10): увеличенная
   локация не должна накрывать кнопку. */
.worldmap-cta {
  position: absolute; right: var(--sp-7); top: var(--sp-6); z-index: 12;
}

@media (max-width: 900px) {
  /* Столбцом: в разметке кнопка стоит последней, а на телефоне её место —
     между списком локаций и названием карты (`DECISIONS` 394). Порядок
     задаёт order ниже, разметка и широкий кадр не тронуты. */
  .worldmap {
    aspect-ratio: auto; width: auto; margin-left: 0; transform: none;
    display: flex; flex-direction: column;
  }
  /* relative, а не static: фон карты (.worldmap-bg) лежит поверх всего
     неподнятого в кадре. Со static под ним пропадали подписи локаций —
     проступали, только пока одна из локаций выбрана, — и кнопка
     «Начать путешествие» целиком: родителю с телефона было не уйти
     с карты в тест. */
  .worldmap-places {
    position: relative; display: grid; grid-template-columns: 1fr 1fr;
    gap: var(--sp-3); padding: var(--sp-4) var(--sp-4) var(--sp-5);
  }
  .place { position: static; transform: none; width: auto; max-width: none; }
  .place.is-active { transform: none; }
  /* В списке подпись живёт по обычным правилам: ширина по колонке,
     кегль как у остального мелкого текста. */
  .place-name { width: auto; max-width: none; font-size: var(--fs-small); }
  .place-pop {
    position: static; transform: none; width: auto; max-width: none;
    margin-top: var(--sp-3); box-shadow: none;
    display: none;
  }
  .place.is-active .place-pop { display: block; transform: none; }
  .place.is-flip .place-pop { right: auto; }
  /* Название — последней строкой кадра, в потоке: угла, к которому его
     прижать, у списка нет, а над ним должна поместиться кнопка. */
  .worldmap-title {
    position: relative; order: 2; left: auto; bottom: auto;
    font-size: var(--fs-h2); padding: 0 var(--sp-4) var(--sp-4);
  }
  /* На узком экране кадра нет: карта разложена списком, и углов, в которые
     можно поставить кнопку, у неё тоже нет. Кнопка встаёт сразу под
     списком, над названием карты. */
  .worldmap-cta {
    position: relative; order: 1; top: auto; right: auto;
    display: flex; justify-content: center;
    margin: 0 var(--sp-4) var(--sp-5);
  }
}

/* ---------- Блоки отчёта ----------
   Мандала десяти миров, «куда направим внимание» и маршрут стоят на двух
   экранах сразу: в письме Советника родителю и в кабинете ребёнка, где
   те же выводы написаны с обращением к нему. Поэтому стили общие, а не
   в разметке одного экрана. */

.mandala { display: grid; grid-template-columns: 1fr 1.15fr 1fr; gap: var(--sp-5); align-items: center; }
.mandala-viz { display: grid; place-items: center; }
.mandala-frame { width: 100%; max-width: 360px; display: grid; place-items: center; }
.mandala-frame img { width: 100%; height: auto; }
.viz-caption { font-size: var(--fs-xs); color: var(--text-muted); text-align: center; margin-top: var(--sp-3); }

/* На каждый мир — по две формулировки из банка зон роста,
   поэтому строки внутри пункта блочные. */
.growth { list-style: none; margin: var(--sp-4) 0 0; padding: 0; display: grid; gap: var(--sp-4); }
.growth li { display: flex; gap: var(--sp-3); align-items: flex-start; }
.growth b { display: block; font-size: var(--fs-small); }
.growth span { font-size: var(--fs-xs); color: var(--text-muted); }
.growth-note { display: block; margin-top: var(--sp-2); }

.route { display: grid; grid-template-columns: 1.55fr .95fr; gap: var(--sp-5); align-items: center; }

/* Под каждым миром маршрута — подпись категории (правка 6 сентября).
   Строк в пункте стало две, и чтобы блок от этого не вырос на десять
   строк, собственная отбивка пункта (8 px сверху и снизу) уходит в
   промежуток между пунктами, а подпись набирается плотной строкой.
   Высота списка после этого та же с точностью до пары пикселей:
   было 10 × 44 + 9 × 8 = 512, стало 10 × 39 + 9 × 8 + 3 × 16 = 510 —
   три подписи из десяти не умещаются в строку и переносятся. */
.route .world-list { gap: var(--sp-2); }
.route .world { padding: 0; }
.route .world-note { line-height: var(--lh-tight); }
.route-frame {
  border: 1px solid var(--border); border-radius: var(--r-md);
  overflow: hidden; line-height: 0;
}
.route-frame img { width: 100%; height: auto; }
.route-pins { display: flex; flex-wrap: wrap; gap: var(--sp-2); margin-top: var(--sp-4); }
.route-pin {
  display: inline-flex; align-items: center; gap: var(--sp-2);
  padding: var(--sp-1) var(--sp-3);
  border: 1px solid var(--border-strong); border-radius: var(--chip-radius);
  background: var(--surface); font-size: var(--fs-xs); color: var(--text-2);
}
.route-pin-open { border-color: var(--accent-grow); color: var(--accent-grow); font-weight: 600; }

@media (max-width: 900px) {
  .mandala, .route { grid-template-columns: 1fr; }
  .mandala-viz { order: -1; }
}

/* ---------- Подробный разбор главы: отдельное окно ---------- */

.chapter-modal { position: fixed; inset: 0; z-index: 100; display: grid; place-items: center; }
.chapter-modal[hidden] { display: none; }
.chapter-modal-back { position: absolute; inset: 0; background: var(--scrim); }
.chapter-modal-card {
  position: relative; z-index: 1;
  width: min(920px, calc(100vw - 2 * var(--sp-4)));
  max-height: min(86vh, 760px); overflow: auto;
  padding: var(--sp-6); border-radius: var(--r-card);
  border: 1px solid var(--border-strong); background: var(--surface);
  box-shadow: var(--shadow-3); color: var(--text);
}
.chapter-modal-card:focus { outline: none; }
.chapter-modal-card h2 {
  font-size: var(--fs-h2); margin: var(--sp-2) 0 var(--sp-4); padding-right: var(--sp-6);
}
.chapter-modal-x {
  position: absolute; top: var(--sp-4); right: var(--sp-4);
  width: 36px; height: 36px; display: grid; place-items: center;
  border: 1px solid var(--border); border-radius: var(--r-full);
  background: var(--surface-2); color: var(--text-muted); cursor: pointer;
}
.chapter-modal-x:hover { color: var(--text); border-color: var(--border-strong); }
.chapter-modal-lead { color: var(--text-2); max-width: var(--measure); margin: 0 0 var(--sp-5); }

/* шапка разбора: картинка локации рядом с названием главы */
.chapter-modal-top {
  display: flex; gap: var(--sp-5); align-items: center;
  margin-bottom: var(--sp-5); padding-bottom: var(--sp-5);
  border-bottom: 1px solid var(--border);
}
.chapter-modal-art {
  flex: none; width: 132px; aspect-ratio: 1 / 1;
  display: grid; place-items: center;
  border-radius: var(--r-card); background: var(--hero-gradient);
  border: 1px solid var(--hero-border); color: var(--hero-accent);
  overflow: hidden;
}
.chapter-modal-art img { width: 100%; height: 100%; object-fit: contain; }
.chapter-modal-art .placeholder { border: 0; background: none; min-height: 0; padding: var(--sp-2); }
.chapter-modal-art .placeholder b, .chapter-modal-art .placeholder { font-size: 0; }
.chapter-modal-art .placeholder svg { font-size: initial; margin: 0; opacity: 1; }
.chapter-modal-top h2 { margin: var(--sp-2) 0 0; padding-right: var(--sp-6); }
@media (max-width: 600px) {
  .chapter-modal-top { flex-direction: column; align-items: flex-start; gap: var(--sp-4); }
}
.chapter-modal-sub {
  font-size: var(--fs-h3); margin: 0 0 var(--sp-3);
  padding-top: var(--sp-4); border-top: 1px solid var(--border);
}
/* Нумерация идёт сверху вниз, а не слева направо: 1–5 в левом столбце,
   6–10 в правом. Номер едет вместе со своим навыком — счётчик считает
   по порядку в разметке, а разметку раскладываем по столбцам. */
.chapter-skills { list-style: none; counter-reset: skill; margin: 0 0 var(--sp-5); padding: 0;
                  display: grid; gap: var(--sp-3) var(--sp-5); grid-template-columns: 1fr 1fr;
                  grid-template-rows: repeat(5, auto); grid-auto-flow: column; }
@media (max-width: 700px) {
  .chapter-skills { grid-template-columns: 1fr; grid-template-rows: none; grid-auto-flow: row; }
}
.chapter-skills li {
  counter-increment: skill; position: relative;
  padding-left: calc(var(--sp-6) + var(--sp-2));
  font-size: var(--fs-small); color: var(--text-2); max-width: var(--measure);
}
.chapter-skills li::before {
  content: counter(skill); position: absolute; left: 0; top: 0;
  width: 24px; height: 24px; border-radius: var(--r-full);
  display: grid; place-items: center; font-size: var(--fs-xs); font-weight: 700;
  background: var(--surface-2); color: var(--text-muted); border: 1px solid var(--border-strong);
}
.chapter-skills b { color: var(--text); font-weight: 600; }
.chapter-modal-close-note {
  margin: 0 0 var(--sp-5); padding: var(--sp-4) var(--sp-5);
  border-left: var(--thread-width) solid var(--thread);
  background: var(--surface-2); border-radius: 0 var(--r-md) var(--r-md) 0;
  font-family: var(--font-serif); color: var(--text-2); max-width: var(--measure);
}
body.is-modal-open { overflow: hidden; }
/* размытие шапки берёт «фон под собой» без учёта затемнения — при открытом
   окне выключаем его, иначе шапка остаётся яркой поверх затемнённой страницы */
body.is-modal-open .topbar { backdrop-filter: none; }

/* ---------- Видео-сцена ---------- */

.video-frame {
  position: relative; aspect-ratio: 16 / 9; width: 100%;
  border-radius: var(--hero-radius); overflow: hidden;
  background: var(--hero-gradient); box-shadow: var(--shadow-3);
  display: grid; place-items: center; text-align: center;
}
.video-frame video { width: 100%; height: 100%; object-fit: cover; display: block; }
.video-empty { display: grid; gap: var(--sp-3); justify-items: center; padding: var(--sp-7);
               color: var(--hero-text-2); }
.video-empty svg { color: var(--hero-accent); }
.video-empty b { font-family: var(--font-serif); font-size: var(--fs-h3); color: var(--hero-text); }

/* ---------- Прохождение теста ---------- */

.quiz-head { display: flex; gap: var(--sp-3); align-items: center; flex-wrap: wrap; margin-bottom: var(--sp-3); }
.quiz-stem {
  font-family: var(--font-serif); font-size: var(--fs-h2);
  line-height: 1.35; margin: var(--sp-5) 0 var(--sp-6);
}
.quiz-nav { display: flex; gap: var(--sp-3); justify-content: space-between; margin-top: var(--sp-6); flex-wrap: wrap; }

/* ---------- Хранитель в кабинете ---------- */

.dragon-stage { display: grid; place-items: center; padding: var(--sp-5); }
.dragon-stage img { max-height: 320px; width: auto; }
.mood-row { display: flex; gap: var(--sp-2); flex-wrap: wrap; justify-content: center; }
.mood-btn {
  min-height: var(--tap-child); padding: 0 var(--sp-4);
  border: 1px solid var(--border-strong); border-radius: var(--chip-radius);
  background: var(--surface); color: var(--text); font: inherit; font-size: var(--fs-small);
  cursor: pointer;
}
.mood-btn[aria-pressed="true"] { border-color: var(--accent-grow); color: var(--accent-grow);
                                 background: var(--status-strong-bg); font-weight: 600; }
.stage-track { display: flex; align-items: center; gap: var(--sp-2); }
.stage-dot {
  flex: 1 1 0; height: var(--progress-h); border-radius: var(--r-full);
  background: var(--progress-track);
}
.stage-dot.is-done { background: var(--progress-fill); }
.stage-dot.is-now  { background: var(--thread); }

/* ---------- Чат ---------- */

.chat-log { display: grid; gap: var(--sp-3); }
.chat-row { display: flex; gap: var(--sp-3); align-items: flex-end; }
.chat-row.is-child { flex-direction: row-reverse; }
.chat-avatar { flex: none; width: 40px; height: 40px; border-radius: var(--r-full);
               overflow: hidden; background: var(--surface-2); display: grid; place-items: center; }
.chat-avatar img { width: 100%; height: 100%; object-fit: cover; object-position: top center; }
.chat-form { display: flex; gap: var(--sp-3); margin-top: var(--sp-5); align-items: flex-end; }
.chat-form .input { flex: 1 1 auto; }
.typing { display: inline-flex; gap: 4px; align-items: center; }
.typing i { width: 6px; height: 6px; border-radius: var(--r-full); background: var(--text-muted);
            animation: fgs-blink 1.2s infinite; }
.typing i:nth-child(2) { animation-delay: .2s; }
.typing i:nth-child(3) { animation-delay: .4s; }
@keyframes fgs-blink { 0%, 60%, 100% { opacity: .25 } 30% { opacity: 1 } }

/* ---------- Админка ---------- */

.kpi { display: grid; gap: var(--sp-3); grid-template-columns: repeat(auto-fit, minmax(190px, 1fr)); }
.kpi .card { padding: var(--sp-4); }
.kpi b { display: block; font-family: var(--font-serif); font-size: var(--fs-h2); }
.kpi span { font-size: var(--fs-xs); color: var(--text-muted); }
.sev { font-weight: 700; }
.sev-high { color: var(--accent-care); }
.sev-mid  { color: var(--accent-win); }
.sev-low  { color: var(--text-muted); }

/* ---------- Мелкие общие узлы ---------- */

.notice {
  display: flex; gap: var(--sp-3); align-items: flex-start;
  padding: var(--sp-4); border-radius: var(--r-md);
  border: 1px solid var(--border); background: var(--surface-2);
  font-size: var(--fs-small); color: var(--text-2);
}
.notice svg { flex: none; color: var(--accent-think); margin-top: 2px; }
.notice-care { border-color: var(--accent-care); background: var(--status-hard-bg); }
.notice-care svg { color: var(--accent-care); }
/* «Ждёт вашего ответа» — не то же самое, что «что-то пошло не так».
   Коралловая рамка notice-care читается как ошибка человека, и на P30
   взрослый видел её там, где он ничего не нарушил: ребёнок ввёл код и
   ждёт. Здесь нужен цвет внимания, а не тревоги, — тот же золотой,
   которым размечено «развивается» и «сейчас ваш ход». */
.notice-wait { border-color: var(--accent-win); background: var(--status-growing-bg); }
.notice-wait svg { color: var(--accent-win); }

.todo-text {
  display: inline-flex; align-items: center; gap: var(--sp-2);
  padding: var(--sp-1) var(--sp-2); border-radius: var(--r-sm);
  border: 1px dashed var(--accent-care); color: var(--accent-care);
  font-size: var(--fs-xs); font-weight: 600;
}

.actions { display: flex; gap: var(--sp-3); flex-wrap: wrap; }
.actions-center { justify-content: center; }
.actions-between { justify-content: space-between; }

/* ---------- Адаптив: один брейкпоинт 900px ---------- */

@media (max-width: 900px) {
  .g-2, .g-3, .g-side, .g-map, .hero-grid { grid-template-columns: 1fr; }
  .hero-grid { padding: var(--sp-6); }
  .hero-figure img { max-width: 240px; }
  .hero-panel { border-left: none; border-top: 1px solid var(--hero-border); }
  .card { padding: var(--card-pad); }
  .wrap, .wrap-narrow { padding: 0 var(--sp-4); }
  .role-pick { grid-template-columns: 1fr; }
  .scene { min-height: 0; }
  .scene-body { padding: var(--sp-6); max-width: none; }
  .scene-bg::after,
  .scene-veil-right .scene-bg::after {
    background: linear-gradient(180deg, var(--scene-veil) 0%, var(--scene-veil-strong) 55%);
  }
  .scene-figure { display: none; }
  .scene-figure-narrow {
    display: block; width: 200px; max-width: 80%;
    margin: var(--sp-5) auto 0; pointer-events: none;
  }
  .shell { padding: var(--sp-6) var(--sp-4) var(--sp-8); }
}

@media (max-width: 420px) {
  .topbar-in { padding: var(--sp-2) var(--sp-4); }
  .btn { padding: 0 var(--sp-3); }
}

/* ---------- Продуктовый вид ----------
   Так экран выглядит для человека, а не для команды: ни перекрёстных
   переходов превью внизу экрана, ни полосы просмотра со счётчиком
   шагов — её убрала правка «тотал финал». Вперёд человек идёт
   продуктовыми кнопками самого экрана.

   Класс ставится всюду, кроме витрины (`nav=1` — навигатор экранов).
   Раньше было наоборот: служебный слой показывался по умолчанию, а
   продуктовый вид включался только ?flow=. Пока корень сервера уводил
   в оглавление, это сходилось; теперь корень уводит на лендинг, и по
   тем же экранам ходят живые люди. */

body.is-flow .actions-between { display: none; }

/* Исключение: на некоторых экранах эта пара — не служебный переход
   превью, а настоящая навигация продукта. Карта роста именно такая:
   письмо и карта — два экрана одного отчёта, и «Вернуться к письму /
   В кабинет» родитель увидит там же в готовом продукте.

   Меню роли на этих экранах ещё не открыто — у родителя оно
   появляется только с кабинета, — поэтому без этой пары с Карты
   роста было некуда идти вообще. */
body.is-flow .actions-between.is-product { display: flex; }

/* Эмблема команды слева от названия — только в шапке админа */
.emblem { display: inline-flex; flex: none; color: var(--emblem); }
.emblem svg { display: block; }

.rolenav { display: flex; gap: var(--sp-2); flex-wrap: wrap; }
/* Пункт меню не переносится внутри себя. В тесной шапке «Карта
   взросления» ломалась в две строки и рядом с однострочным «Кабинетом»
   выглядела набранной другим, мелким шрифтом, хотя размер у всех
   пунктов один. Теперь в тесноте на новую строку уходит пункт целиком. */
.rolenav a {
  display: inline-flex; align-items: center; gap: var(--sp-2);
  white-space: nowrap;
  min-height: 34px; padding: 0 var(--sp-3);
  border-radius: var(--chip-radius); border: 1px solid transparent;
  font-size: var(--fs-small); color: var(--text-muted); text-decoration: none;
  transition: color var(--dur-fast) var(--ease), background-color var(--dur-fast) var(--ease);
}
.rolenav a:hover { color: var(--text); background: var(--surface-2); }
.rolenav a.is-current {
  color: var(--accent-grow); border-color: var(--accent-grow); background: var(--status-strong-bg);
}

/* страница пути роли: обложка со списком шагов */
.path-hero {
  background: var(--hero-gradient); border-radius: var(--hero-radius);
  color: var(--hero-text); padding: var(--sp-8);
  margin: var(--sp-7) 0 var(--sp-6); box-shadow: var(--shadow-3);
}
.path-hero h1 { font-size: var(--fs-h1); color: var(--hero-text); }
.path-hero p { color: var(--hero-text-2); max-width: var(--measure); margin-top: var(--sp-3); }
.path-steps { display: grid; gap: var(--sp-2); }
.path-step {
  display: flex; gap: var(--sp-3); align-items: center;
  padding: var(--sp-3) var(--sp-4);
  border: 1px solid var(--border); border-radius: var(--r-md);
  background: var(--surface); color: var(--text); text-decoration: none;
  transition: background-color var(--dur-fast) var(--ease), border-color var(--dur-fast) var(--ease);
}
a.path-step:hover { background: var(--surface-2); border-color: var(--accent-grow); }
.path-step-num {
  flex: none; width: 28px; height: 28px; border-radius: var(--r-full);
  display: grid; place-items: center; font-size: var(--fs-xs); font-weight: 700;
  background: var(--surface-2); border: 1px solid var(--border-strong); color: var(--text-muted);
}
.path-step-code {
  flex: none; min-width: 46px; height: 26px; padding: 0 var(--sp-2);
  display: grid; place-items: center;
  border-radius: var(--r-sm); background: var(--surface-2);
  border: 1px solid var(--border-strong);
  font-size: var(--fs-xs); font-weight: 700; letter-spacing: .04em;
}
.path-step-text { font-size: var(--fs-small); }
@media (max-width: 600px) { .path-step { align-items: flex-start; flex-wrap: wrap; } }

/* глава, которая в пилоте ещё закрыта */
.map-place-btn.is-locked { opacity: .6; cursor: default; }
.map-place-btn.is-locked:hover { border-color: var(--border); background: var(--surface); }
.map-lock { font-size: var(--fs-xs); color: var(--text-muted); }

/* ---------- Печать: родитель сохранит отчёт в PDF ---------- */

@media print {
  .topbar, .no-print { display: none !important; }
  body { background: var(--c-white); color: var(--c-ink-900); }
  .card, .hero { box-shadow: none; break-inside: avoid; }
  section { break-inside: avoid; }
  .hero { background: var(--c-night-800); }
  a { text-decoration: none; }
}
