:root {
  --bg: #0a0a0c;
  --card: #1c1c1e;
  --card2: #26262a;
  --txt: #f5f5f7;
  --mut: #98989d;
  --sep: #2c2c2e;
  /* Стратегия, В2 30.08.2026: тёмная полупрозрачная подложка (кнопка ▶ видео главы,
     хронометраж на постере); темы её не красят — она лежит на картинке, не на фоне */
  --scrim: rgba(10, 10, 12, 0.45);
  --pc: var(--acc); /* цвет чипа периода в мостике «Стратегии»; на чипе задаётся инлайн через pCol() */
  --p: var(--acc); /* цвет текущего периода в карточке дня «Пути»; на карточке задаётся инлайн через pCol() */
  /* Акцент темы по умолчанию — «Оранжевый» (решение владельца 31.07.2026; выбор из четырёх
     тем живёт в профиле → «Оформление», каталог THEMES в app.core.js). Значения обязаны
     совпадать с записью amber в каталоге: до применения applyTheme страница рисуется
     этими токенами.
     ⚠️ У этой темы известная особенность: её тон совпадает с палитрой оценок --sc-*, поэтому
     в календарях «Прогресса» акцентный квадрат («рядом с нормой») похож на «отклонение».
     Развязки нет — это свойство самого цвета, а не дефект. У трёх остальных тем её нет. */
  --acc: #e8853b;
  --accd: #1a0f05;
  --accg: rgba(232, 133, 59, 0.16);
  --accb: rgba(232, 133, 59, 0.32);
  --grn: #30d158;
  /* Заливка чипа вложения в «Еде» (06.09.2026): тот же зелёный, что --grn, 12 %.
     Пара по образцу --acc/--accg. Разъедутся значения — кант и подложка станут разными
     зелёными, и чип начнёт выглядеть грязно. */
  --grng: rgba(48, 209, 88, 0.12);
  --red: #ff453a;
  /* --water и --blu равны по значению, но это РАЗНЫЕ роли, и менять их можно порознь.
     До 31.07.2026 один --blu означал пять вещей: воду, талию, этап «Вода и напитки»,
     заморозки и точку человека на карте — правка любой задевала остальные. Вода отделена
     потому, что владелец запретил трогать её цвет (30.07): теперь это физически
     невозможно сделать случайно. */
  --water: #7fa8e0;
  --blu: #7fa8e0;
  --pur: #c48fd4;
  /* Серии графика «Сравнить всё, %». Отдельные от --blu/--rgrn/--pur: те подобраны
     под дорогу «Пути» и на тёмной подложке карточки не различаются между собой
     (validate_palette.js: светлота вне полосы, у синего и зелёного хрома ниже порога —
     читаются серыми). Этот набор прошёл все пять проверок на #1c1c1e. */
  --s1: #cf7328;
  --s2: #4f8ad6;
  --s3: #3fa06f;
  --s4: #a061c6;
  --wht: #fff;
  --sheet: #141416;
  /* Шкала радиусов. Скилл описывал её с 17.07.2026, а в :root её ЗАБЫЛИ объявить —
     и три места с var(--r-m) (.rd-card и .rd-chart на «Пути», .soc) полтора месяца
     стояли с прямыми углами: несуществующая переменная делает свойство невалидным,
     браузер молча берёт 0. Найдено 30.07.2026. Значения — те же, что в скилле. */
  --r-s: 8px;
  --r-m: 12px;
  --r-l: 16px;
  --r-full: 999px;
  --mood-bad: #8e4444;
  --sc-ok: #a8e0c4;
  --sc-a: #c6d79f;
  --sc-b: #dccf8a;
  --sc-c: #e6c07e;
  --sc-d: #df925a;
  --sc-e: #cf7042;
  --sc-low: #c99b52;
  --lav: #c9a9e0;
  --rgrn: #8fd6a8;
  /* Периоды Пути v2 — единственный источник цвета периода (редизайн 30.08.2026,
     PLAN-редизайн-путь-исполнение.md §7.1). Подобраны в OKLCH: одна светлота, попарно ΔE ≥ 20,
     ни один не совпадает с акцентами четырёх тем; «Напитки» = --water. Ключ токена = program_stages.key,
     база хранит те же hex зеркалом. Красят полосу периодов в герое и кольца званий; НЕ красят
     календарь серии, кнопки и полосу до следующего звания — это акцент темы. */
  --p-morning: #e9b44b;
  --p-salad: #8fca7a;
  --p-meat: #f38971;
  --p-grain: #f1ad71;
  --p-plate: #fe93a1;
  --p-rhythm: #72cda5;
  --p-drinks: #7fa8e0;
  --p-fridge: #71d0d3;
  --p-store: #ada5f6;
  --p-street: #dc94d9;
  --p-body: #bfc56f;
  --p-road: #5acdf9;
  /* Те же отступы, но прочитанные из JS (app.platform.js пишет сюда число). Нужен потому,
     что 30.07.2026 на iPhone владельца клиент не прислал НИ ОДНОЙ из двух переменных ниже:
     --sat дал 0, полноэкранный лист начался в 20 px от верха, и стрелка «назад» встала на
     часы поверх шапки Telegram. max() берёт больший из двух источников — так ни один из них
     не может УМЕНЬШИТЬ отступ, посчитанный другим, и формула остаётся здесь в единственном
     экземпляре: JS её не дублирует. */
  --sat-js: 0px;
  --sat: max(
    calc(
      var(--tg-safe-area-inset-top, env(safe-area-inset-top)) +
        var(--tg-content-safe-area-inset-top, 0px)
    ),
    var(--sat-js)
  );
  /* То же самое снизу. На Android системная навигация (жестовая полоса или три кнопки)
     занимает нижнюю часть экрана, и голый env(safe-area-inset-bottom) в полноэкранном
     мини-аппе её не покрывает: таббар и кнопки камеры садились прямо на системную полосу
     (замечено владельцем 16.08.2026). Источников снова три + JS, max() берёт больший —
     ни один не может УМЕНЬШИТЬ отступ, посчитанный другим. Формула живёт здесь в
     единственном экземпляре: JS её не дублирует, он только пишет --sab-js.
     🚨 Числа «под этот телефон» тут запрещены: три кнопки и жест дают разный inset. */
  --sab-js: 0px;
  --sab: max(
    var(--tg-safe-area-inset-bottom, 0px),
    var(--tg-content-safe-area-inset-bottom, 0px),
    env(safe-area-inset-bottom),
    var(--sab-js)
  );
  /* Единая верхняя граница для ВСЕГО, что стоит у верхней кромки: стрелка «назад»,
     крестик, аватар, тост, верх полноэкранного листа. Отсчёт только от --sat —
     голый env(safe-area-inset-top) знает про вырез телефона, но НЕ про шапку
     Telegram (Close / ⌄ / …), и в полноэкранном режиме кнопки лезли под неё.
     Зазор 16 — тот же, что у .wrap: стрелка в шторке встаёт ровно на ту же линию,
     что стрелка в потоке экрана. */
  --top-btn: calc(var(--sat) + 16px);
  /* Насколько низ подписи «Профиль» опускается ниже --top-btn: кнопка 44 + 2 отступа
     подписи + 12 её строки. Отсюда считается запас .wrap и компенсация стрелки «назад» —
     два места, которые обязаны меняться вместе. */
  --av-lbl: 58px;
  /* Высота строки заголовка вкладки. Нужна ЯВНО, иначе линия с аватаром считается от
     line-height:normal, а он зависит от шрифта устройства — на одном телефоне совпало
     бы, на другом разъехалось. Работает в паре с --av-lbl (см. h1 и правило ниже). */
  --h1-line: 24px;

  /* Лестница слоёв. Единственный источник: своё число в z-index = ошибка.
     Порядок продуман, менять по одному нельзя — только всей лестницей.
     🚨 Лист (--z-sheet) ОБЯЗАН быть выше панели профиля (--z-drawer): до 30.07.2026
     оба стояли на 99/100, и справочник, уведомления, «Моя семья», удаление данных и
     меню шеринга открывались ЗА панелью — человек нажимал кнопку и не видел отклика. */
  --z-tabs: 50; /* таб-бар */
  --z-av: 60; /* аватар профиля */
  --z-mb: 90; /* главная кнопка в браузере (.webmb) — выше таб-бара, ниже панели профиля */
  --z-scrim: 99; /* затемнение под панелью */
  --z-drawer: 100; /* панель профиля */
  --z-sheet: 110; /* полноэкранный лист */
  --z-conf: 115; /* конфетти — поверх листа с наградой */
  --z-toast: 120;
  --z-lb: 130; /* лайтбокс фото */
  --z-lb-btn: 131; /* его кнопки */
  --z-mdone: 140; /* «Готово» над клавиатурой */
  --z-cam: 200; /* камера */
  --z-cam-top: 210; /* сканер и сравнение фото */
  --z-modal: 3000; /* экраны «Рядом», «Доставка», карточка места */
  --z-modal-btn: 3001;
}
/* Страховка на полный экран (класс вешает app.platform.js): если клиент не прислал
   --tg-content-safe-area-inset-top, --sat знает только про вырез телефона, и кнопки снова
   уехали бы под шапку Telegram. 48 — её фактическая высота. Когда переменная приходит,
   max() берёт точное значение и страховка не влияет ни на что. */
body.tgfull {
  --top-btn: max(calc(var(--sat) + 16px), calc(env(safe-area-inset-top) + 48px));
}
* {
  box-sizing: border-box;
  -webkit-tap-highlight-color: transparent;
}
body {
  margin: 0;
  background: var(--bg);
  color: var(--txt);
  font-family: -apple-system, system-ui, Roboto, sans-serif;
  padding-bottom: calc(100px + var(--sab));
  /* интерфейс не тянется: manipulation убирает зум по двойному тапу,
     text-size-adjust не даёт iOS самому увеличивать шрифты */
  touch-action: manipulation;
  -webkit-text-size-adjust: 100%;
  text-size-adjust: 100%;
}
h1 {
  font-size: 20px;
  font-weight: 600;
  line-height: var(--h1-line);
  margin: 2px 0 2px;
}
/* Запас сверху — место для подписи «Профиль» под аватаром. Считается, а не подбирается:
   кнопка 44 (.avbtn) + 2 отступа подписи + 12 её строки = 58 от --top-btn, дальше 8 зазора.
   До 16.08.2026 тут стояло «+24px» при подписи, уходящей на 58, — 34 px разницы и съедала
   первую карточку вкладки на Android (на iPhone спасал более крупный --sat). */
.wrap {
  padding: calc(var(--top-btn) + var(--av-lbl) + 8px) 16px 0;
  max-width: 520px;
  margin: 0 auto;
}
/* Стрелка «назад» подэкрана стоит первой в потоке, и запас .wrap опустил бы её ниже
   аватара, который прибит к --top-btn. Возвращаем на общую линию — тем же выражением,
   чтобы при правке запаса линия не разъехалась. */
#view > .fade > .miniback:first-child {
  margin-top: calc(-1 * (var(--av-lbl) + 8px));
}
/* Заголовок вкладки — на ту же линию, но по НИЖНЕЙ границе (решение владельца 30.08.2026:
   низ слова «Стратегия» и низ подписи «Профиль» на одной горизонтали). До этой правки
   заголовок стоял на 34 px ниже аватара — запас .wrap выше рассчитан на контент, а
   заголовок контентом не является, он делит строку с аватаром.
   Формула, а не подобранное число: запас .wrap = --av-lbl + 8, и правило ЗАМЕНЯЕТ у
   заголовка margin-top (двойки из `margin: 2px 0` тут уже нет — это и была ошибка первой
   редакции, поймана замером в headless-Chrome). Низ заголовка = (--av-lbl + 8) − (8 +
   --h1-line) + --h1-line = --av-lbl, то есть ровно низ блока аватара. Поменяется кегль
   заголовка или высота подписи — линия сойдётся сама.
   ⚠️ Мерить только в покое: у .fade анимация появления со сдвигом на 8 px, и замер на лету
   врёт ровно на эти 8 px. */
#view > .fade > h1:first-child {
  margin-top: calc(-1 * (8px + var(--h1-line)));
}
.card {
  background: var(--card);
  border-radius: 12px;
  padding: 16px;
  margin: 10px 0;
}
.sub {
  font-size: 12px;
  color: var(--mut);
}
.sub svg {
  vertical-align: -4px;
  margin-right: 6px;
}
.big {
  font-size: 28px;
  font-weight: 700;
  letter-spacing: -0.5px;
}
.chip {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  background: var(--card2);
  border-radius: 999px;
  padding: 6px 12px;
  font-size: 12px;
  margin-right: 6px;
}
.chip.tapc {
  min-height: 44px;
  padding: 0 16px;
  font-size: 14px;
  font-weight: 600;
  color: var(--txt);
  border: none;
  margin: 0;
  justify-content: center;
}
.chip svg {
  vertical-align: -4px;
}
.pos {
  color: var(--grn);
}
.neg {
  color: var(--red);
}
.row {
  display: flex;
  gap: 8px;
}
.row > * {
  flex: 1;
}
.qa {
  background: var(--card);
  border-radius: 16px;
  padding: 16px 6px;
  text-align: center;
  font-size: 12px;
  color: var(--txt);
  border: none;
}
.qa svg {
  display: block;
  margin: 0 auto 7px;
}
.btn {
  display: block;
  width: 100%;
  background: var(--acc);
  border: none;
  border-radius: 12px;
  padding: 15px;
  font-size: 16px;
  font-weight: 600;
  text-align: center;
  text-decoration: none;
  margin: 8px 0 0;
  transition: transform 0.12s;
}
/* 🚨 Заливка акцентом ⇒ текст --accd, без исключений. Акцент светлый: белым по нему
   1.5:1 при норме 4.5 — надпись пропадает. На прежнем оранжевом было 2.7:1, тоже ниже
   нормы, просто ещё читалось, поэтому четыре кнопки жили с --wht незамеченными.
   Заводишь новый элемент с background: var(--acc) — добавь его сюда же.
   Механически это стережёт node test_accent_contrast.js. */
.btn,
.webmb,
.mdone,
.cmpshare,
.chip.on,
.nbchip.on,
.nbnum,
.camseg button.on,
.bctor.on,
.chip.tapc.thfb.on,
/* Стратегия, В2 30.08.2026: кружок пройденной главы, полоса шагов, полоса чтения, чекбокс задания */
.cn.done,
.cjb i.d,
.cjb i b,
.rdp b,
.tk.on .tkb {
  color: var(--accd);
}
.btn:active {
  transform: scale(0.97);
}
/* Отклик на нажатие — на КАСАНИИ, а не на отпускании: элемент, который отвечает только по
   click, отвечает уже вслед ушедшему пальцу и читается мёртвым. Класс вешает один
   делегированный pointerdown (pressInit(), app.spring.js), поэтому 230 инлайновых onclick
   трогать не пришлось. Специфичность у него ниже, чем у любого `.x:active` (0,1,0 против
   0,2,0), — восемь селекторов со своими величинами остаются при своих.
   В системном «уменьшить движение» отклика нет вовсе: там сжатие и есть то самое движение,
   от которого человек отказался. */
.press {
  transform: scale(0.97);
  transition: transform 100ms ease-out;
}
@media (prefers-reduced-motion: reduce) {
  .press {
    transform: none;
  }
}
.btn:disabled {
  opacity: 0.5;
}
.btn2 {
  background: var(--card2);
  color: var(--txt);
}
/* Главная кнопка страницы вне Telegram (сессия С3). В браузере системной MainButton нет,
   а онбординг на неё рассчитывает: mbOn() прячет кнопку в потоке, когда платформа ответила
   «да». Рисуется заглушкой webTg() в app.platform.js, текст ставит она же.
   Цвет — токены темы, поэтому setParams заглушке не нужен; текст на акценте задаёт общее
   правило контраста выше, своего color здесь нет намеренно.
   Слой --z-mb выше таб-бара: сейчас кнопка показывается на единственном экране, где
   таб-бар скрыт (онбординг), но появится второй — она обязана лежать поверх, как
   системная кнопка Telegram. */
.webmb {
  position: fixed;
  left: 50%;
  transform: translateX(-50%);
  bottom: calc(16px + var(--sab));
  width: min(520px, calc(100% - 32px));
  /* 48, а не 44: тач-цель по системе — минимум 44, а высота .btn в потоке около 48, и две
     главные кнопки страницы не должны отличаться на глаз. Отступы по шкале, шаг 4. */
  min-height: 48px;
  display: none;
  padding: 12px 16px;
  border: none;
  border-radius: var(--r-m);
  background: var(--acc);
  font-family: inherit;
  font-size: 16px;
  font-weight: 600;
  text-align: center;
  z-index: var(--z-mb);
  transition: transform 0.1s ease-out;
}
.webmb.on {
  display: block;
}
.webmb:active {
  transform: translateX(-50%) scale(0.97);
}
.webmb:disabled {
  opacity: 0.5;
}
.btn svg {
  vertical-align: -5px;
  margin-right: 8px;
}
.moods {
  display: flex;
  gap: 8px;
  margin-top: 10px;
}
.moods button {
  flex: 1;
  background: var(--card2);
  border: none;
  border-radius: 12px;
  padding: 12px 0;
  font-size: 28px;
  transition: transform 0.12s;
}
.moods button svg {
  display: block;
  margin: 0 auto;
}
.moods button:active {
  transform: scale(0.9);
}
.seg {
  display: flex;
  background: var(--card);
  border-radius: 12px;
  padding: 3px;
  margin: 12px 0 4px;
}
.seg button {
  flex: 1;
  background: none;
  border: none;
  color: var(--mut);
  font-size: 14px;
  padding: 8px;
  border-radius: 8px;
  font-weight: 600;
}
.seg button.on {
  background: var(--card2);
  color: var(--txt);
}
.cal {
  display: grid;
  grid-template-columns: repeat(7, 1fr);
  gap: 6px;
  margin-top: 10px;
}
.cal div {
  aspect-ratio: 1;
  border-radius: 8px;
  background: var(--sep);
}
.cal .g {
  background: var(--grn);
}
.cal .o {
  background: var(--acc);
}
.cal .b {
  background: var(--mood-bad);
}
.leg {
  display: flex;
  flex-wrap: wrap;
  gap: 6px 12px;
  font-size: 12px;
  color: var(--mut);
  margin-top: 8px;
}
.dot {
  display: inline-block;
  width: 8px;
  height: 8px;
  border-radius: 999px;
  margin-right: 4px;
}
.cal .w {
  background: var(--water);
}
/* ── Экран «Тело»: герой-карточка веса и строки объёмов ─────────────────── */
.pwin {
  display: flex;
  gap: 6px;
  margin: 12px 0;
}
.pwin .chip {
  flex: 1;
}
.chip.on {
  background: var(--acc);
}
.hrow {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 8px;
  margin-bottom: 2px;
}
.hd {
  font-size: 14px;
  font-weight: 600;
}
.hempty {
  font-size: 12px;
  color: var(--mut);
  margin-top: 8px;
}
/* Цвет дельты по НАПРАВЛЕНИЮ К ЦЕЛИ, а не по знаку: тому, кто набирает массу,
   падение веса успехом не является. Рядом всегда стрелка — цвет не единственный
   носитель смысла. */
.goodv {
  color: var(--grn);
}
.badv {
  color: var(--red);
}
.flatv {
  color: var(--mut);
}
.ctr {
  text-align: center;
}
.num {
  font-size: 20px;
}
.pr {
  padding: 4px 16px;
}
.mrow {
  display: grid;
  grid-template-columns: 1fr auto auto;
  gap: 10px;
  align-items: center;
  min-height: 44px;
  padding: 10px 0;
  border-bottom: 0.5px solid var(--sep);
  cursor: pointer;
}
.mrow:last-child,
.mrow.on {
  border-bottom: none;
}
.mnm {
  font-size: 14px;
  min-width: 0;
}
.mdl {
  font-size: 12px;
  color: var(--mut);
  margin-top: 2px;
}
.msp {
  line-height: 0;
}
.mvl {
  font-size: 15px;
  font-weight: 600;
  text-align: right;
  white-space: nowrap;
}
.mexp {
  padding: 0 0 12px;
  border-bottom: 0.5px solid var(--sep);
}
.dotm {
  background: var(--mut);
  opacity: 0.55;
}
.dsh {
  display: inline-block;
  width: 12px;
  height: 0;
  border-top: 2px dashed var(--mut);
  margin-right: 4px;
  vertical-align: 3px;
}
/* След сохранения в форме: тост исчезает, а эта строка остаётся и отвечает на вопрос
   «записалось ли». Именно её отсутствие плодило дубли. */
/* Лист «Как мерить»: ссылка в форме и правила внутри листа */
.howl {
  display: flex;
  align-items: center;
  gap: 6px;
  min-height: 44px;
  font-size: 13px;
  font-weight: 600;
  color: var(--acc);
  cursor: pointer;
}
.hrules {
  margin-top: 14px;
}
.hrule {
  display: flex;
  gap: 10px;
  padding: 10px 0;
  border-bottom: 0.5px solid var(--sep);
  font-size: 13px;
  line-height: 1.45;
  color: var(--mut);
}
.hrule:last-child {
  border-bottom: none;
}
.hrule b {
  display: block;
  font-weight: 600;
  color: var(--txt);
  margin-bottom: 1px;
}
.hri {
  flex: none;
  width: 20px;
  color: var(--mut);
}
.hwhy {
  background: var(--card2);
  border-radius: 12px;
  padding: 12px 14px;
  font-size: 12px;
  color: var(--mut);
  line-height: 1.5;
  margin: 14px 0 12px;
}
/* Длинная пошаговая инструкция в листе (ярлык на домашнем экране). Основной текст 14px
   цветом --txt, а не 12px серым: по 12px владелец читать не смог. Каждый шаг — своей
   строкой с висячей цифрой; сплошным абзацем шаги теряются друг в друге. */
.hins {
  font-size: 14px;
  line-height: 1.6;
  color: var(--txt);
  margin-bottom: 10px;
}
.hins.hstep {
  padding-left: 22px;
  text-indent: -22px;
}
.mtoday {
  display: flex;
  align-items: center;
  gap: 6px;
  font-size: 12px;
  color: var(--sc-ok);
  margin-top: 10px;
}
.mtoday svg {
  flex: none;
}
.btn:disabled {
  opacity: 0.6;
}
/* Строка-аккордеон «Сравнить всё» — четвёртая в карточке объёмов */
.cmpr {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  min-height: 44px;
  padding: 12px 0 2px;
  border-top: 0.5px solid var(--sep);
  margin-top: 4px;
  cursor: pointer;
}
.cmpt {
  font-size: 14px;
  font-weight: 600;
}
.chev {
  color: var(--mut);
  font-size: 13px;
}
table.pct {
  width: 100%;
  border-collapse: collapse;
  font-size: 12px;
  margin: 10px 0 12px;
  font-variant-numeric: tabular-nums;
}
table.pct th,
table.pct td {
  text-align: right;
  padding: 5px 2px;
  border-bottom: 0.5px solid var(--sep);
}
table.pct tr:last-child td {
  border-bottom: none;
}
table.pct th:first-child,
table.pct td:first-child {
  text-align: left;
}
table.pct th {
  color: var(--mut);
  font-weight: 400;
}
.subsm {
  font-size: 12px;
  color: var(--mut);
  margin-top: 12px;
  font-weight: 600;
}
.hchips {
  display: flex;
  gap: 8px;
  margin-top: 8px;
}
.hchips button {
  flex: 1;
  min-width: 0;
  height: 44px;
  border-radius: 12px;
  background: var(--card2);
  color: var(--txt);
  border: 2px solid transparent;
  font-weight: 600;
  font-size: 16px;
}
.hchips button.on {
  border-color: var(--acc);
  color: var(--acc);
  box-shadow: 0 0 0 3px var(--accg);
}
.moods button.on {
  box-shadow: 0 0 0 2px var(--acc);
}
.hchips button.hint {
  border-color: var(--sc-ok);
  color: var(--sc-ok);
  box-shadow: 0 0 0 3px rgba(168, 224, 196, 0.16);
}
.hchips button.goal {
  background: var(--sc-ok);
  border-color: var(--sc-ok);
  color: var(--bg);
  box-shadow: 0 0 0 3px rgba(168, 224, 196, 0.35);
  transform: scale(1.04);
}
.kick {
  font-size: 12px;
  color: var(--mut);
  text-transform: uppercase;
  letter-spacing: 0.06em;
  font-weight: 600;
  display: block;
}
.foc {
  display: flex;
  align-items: center;
  gap: 12px;
  margin-top: 6px;
}
.foc .em svg {
  display: block;
}
.foc .tt {
  flex: 1;
  font-size: 14px;
  font-weight: 600;
  line-height: 1.35;
}
@keyframes cfb {
  to {
    transform: translateY(115vh) rotate(var(--rot));
    opacity: 0;
  }
}
.hwrow {
  display: flex;
  align-items: center;
  justify-content: space-between;
  width: 100%;
  background: none;
  border: 0;
  color: var(--txt);
  font-size: 14px;
  padding: 10px 0 2px;
  min-height: 44px;
  text-align: left;
}
.hck {
  width: 26px;
  height: 26px;
  border-radius: 999px;
  border: 2px solid var(--sep);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  color: var(--wht);
  flex: none;
}
.hck.on {
  background: var(--grn);
  border-color: var(--grn);
}
.tabs {
  position: fixed;
  left: 10px;
  right: 10px;
  bottom: calc(10px + var(--sab));
  display: flex;
  justify-content: space-around;
  background: rgba(6, 6, 7, 0.75);
  -webkit-backdrop-filter: blur(24px) saturate(160%);
  backdrop-filter: blur(24px) saturate(160%) url(#glassdistort);
  border: 0.5px solid rgba(255, 255, 255, 0.15);
  border-radius: 16px;
  padding: 8px 4px;
  z-index: var(--z-tabs);
  box-shadow:
    0 8px 24px rgba(0, 0, 0, 0.35),
    inset 0 1px 0 rgba(255, 255, 255, 0.08);
}
body.kb .tabs {
  display: none;
}
#mf_c,
#mf_w,
#mf_h,
#mf_kg {
  scroll-margin-bottom: 260px;
}
html {
  scroll-padding-bottom: 260px;
}
body.kb {
  padding-bottom: calc(var(--sab) + 4px);
}
.tabs button {
  position: relative;
  background: none;
  border: none;
  color: var(--mut);
  width: 66px;
  padding: 6px 2px;
  border-radius: 16px;
  transition: color 0.15s ease;
  isolation: isolate;
}
.tabs button svg {
  display: block;
  margin: 0 auto;
  position: relative;
  z-index: 1;
}
.tabs button span {
  display: block;
  font-size: 12px;
  margin-top: 3px;
  font-weight: 600;
  position: relative;
  z-index: 1;
}
.tabs button::before {
  content: '';
  position: absolute;
  inset: 0;
  border-radius: 16px;
  background: rgba(120, 120, 128, 0.32);
  -webkit-backdrop-filter: blur(10px) saturate(180%);
  backdrop-filter: blur(10px) saturate(180%) url(#glassdistort);
  border: 0.5px solid rgba(255, 255, 255, 0.12);
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.15);
  opacity: 0;
  transform: scale(0.85);
  transition:
    opacity 0.22s ease,
    transform 0.22s ease;
  z-index: 0;
}
.tabs button.on {
  color: var(--acc);
}
.tabs button.on::before {
  opacity: 1;
  transform: scale(1);
}
.sheet {
  position: fixed;
  inset: 0;
  background: rgba(0, 0, 0, 0.6);
  display: none;
  align-items: stretch;
  z-index: var(--z-sheet);
}
.sheet.on {
  display: flex;
}
.sh {
  background: var(--sheet);
  border-radius: 0;
  padding: max(20px, var(--top-btn)) 18px calc(24px + var(--sab));
  width: 100%;
  max-width: 520px;
  margin: 0 auto;
  animation: up 0.22s ease;
  height: 100vh;
  height: 100dvh;
  overflow-y: auto;
}
@keyframes up {
  from {
    transform: translateY(40px);
    opacity: 0.5;
  }
  to {
    transform: none;
    opacity: 1;
  }
}
.sh h3 {
  margin: 0 0 12px;
  font-size: 20px;
}
.inp {
  width: 100%;
  background: var(--card);
  border: 0.5px solid var(--sep);
  border-radius: 12px;
  color: var(--txt);
  font-size: 16px;
  padding: 13px;
  margin: 5px 0;
  outline: none;
  font-family: inherit;
}
.inp:focus {
  border-color: var(--acc);
}
textarea.inp {
  resize: none;
  font-size: 16px;
  max-height: 132px;
}
/* Сменная подсказка над пустым полем вопроса. Это НЕ placeholder: атрибуту нельзя задать
   перелив (background-clip:text по ::placeholder не работает), поэтому строка лежит своим
   слоем поверх поля. Отступы повторяют .inp (padding 13px + margin 5px), иначе текст
   встанет не туда, где начнётся набор. pointer-events:none — чтобы тап проходил в поле. */
.qwrap {
  position: relative;
}
.qhint {
  position: absolute;
  left: 13px;
  right: 13px;
  top: 18px;
  font-size: 16px;
  line-height: normal;
  color: var(--mut);
  pointer-events: none;
  transition: opacity 0.35s ease;
}
.qhint.off {
  opacity: 0;
}
/* Точка строки состояния записи (сессия Г2, 06.09.2026): пока идёт запись, подсказку
   заменяют точка, счётчик секунд и подсказка способа. Пульсирует той же анимацией, что
   и сама кнопка микрофона, — это один и тот же «идёт запись». */
.qhint .rdot {
  display: inline-block;
  width: 8px;
  height: 8px;
  margin-right: 8px;
  vertical-align: middle;
  border-radius: var(--r-full);
  background: var(--red);
  animation: pl 1.1s infinite;
}
.fade {
  animation: fd 0.22s ease;
}
@keyframes fd {
  from {
    opacity: 0;
    transform: translateY(8px);
  }
  to {
    opacity: 1;
    transform: none;
  }
}
.badges {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 8px;
  margin-top: 10px;
}
.bd {
  background: var(--card2);
  border-radius: 12px;
  padding: 13px 4px;
  text-align: center;
  color: var(--acc);
}
.bd svg {
  display: block;
  margin: 0 auto;
}
.bd span {
  font-size: 12px;
  color: var(--mut);
  display: block;
  margin-top: 6px;
}
.bd.off {
  opacity: 0.35;
  color: var(--mut);
}
.pbg {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 8px;
  margin-top: 10px;
}
.pb {
  background: var(--card2);
  border-radius: 12px;
  padding: 12px 4px;
  text-align: center;
}
.pb .pe {
  font-size: 20px;
  line-height: 1.15;
}
.pb b {
  display: block;
  font-size: 12px;
  font-weight: 400;
  color: var(--mut);
  margin-top: 4px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  padding: 0 2px;
}
.pb.off {
  opacity: 0.35;
  filter: grayscale(1);
}
.conf {
  position: fixed;
  inset: 0;
  pointer-events: none;
  overflow: hidden;
  z-index: var(--z-conf);
}
.conf i {
  position: absolute;
  top: -30px;
  font-size: 16px;
  animation: cfall 1.8s cubic-bezier(0.4, 0, 0.2, 1) forwards;
}
@keyframes cfall {
  to {
    transform: translateY(105vh) rotate(360deg);
    opacity: 0;
  }
}
.li {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 12px 0;
  border-bottom: 0.5px solid var(--sep);
  font-size: 14px;
}
.li:last-child {
  border: none;
}
.li span {
  color: var(--mut);
}
.tgl {
  position: relative;
  width: 48px;
  height: 28px;
  border-radius: 999px;
  background: var(--card2);
  border: 0.5px solid var(--sep);
  padding: 0;
  flex: none;
  cursor: pointer;
  transition: background 0.15s;
}
.tgl:after {
  content: '';
  position: absolute;
  top: 2.5px;
  left: 3px;
  width: 21px;
  height: 21px;
  border-radius: 50%;
  background: var(--mut);
  transition:
    transform 0.15s,
    background 0.15s;
}
.tgl.on {
  background: var(--acc);
  border-color: var(--acc);
}
.tgl.lk {
  opacity: 0.45;
  cursor: default;
}
.tgl.on:after {
  transform: translateX(19px);
  background: var(--accd);
}
.food {
  display: flex;
  align-items: center;
  gap: 12px;
  background: var(--card);
  border-radius: 16px;
  padding: 13px 14px;
  margin: 8px 0;
  border: none;
  width: 100%;
  color: var(--txt);
  text-align: left;
  font-size: 16px;
  font-weight: 600;
}
.food em {
  font-style: normal;
  display: block;
  font-size: 12px;
  color: var(--mut);
  font-weight: 400;
  margin-top: 2px;
}
/* Карточка «Знакомство»: значок, название, что делает, как зайти.
   НЕ кнопка и не тач-цель — человек идёт по описанному пути сам (решение владельца).
   Пройденных пунктов в разметке нет вовсе — они исчезают из списка, поэтому «зелёного»
   состояния у плитки не бывает (правка 08.08.2026). */
/* Неоновая рамка вокруг всей карточки: она одна такая на «Сегодня» и обязана тянуть взгляд.
   Цвет ФИКСИРОВАННЫЙ пастельно-фиолетовый (--pur), а не акцент темы: у «Пудры» и «Нюда»
   акцент бледный, свечения на нём почти не видно. Решение владельца 08.08.2026. */
.qcard {
  border: 1px solid var(--pur);
  animation: qglow 3s ease-in-out infinite;
}
@keyframes qglow {
  0%,
  100% {
    box-shadow:
      0 0 0 1px rgba(196, 143, 212, 0.16),
      0 0 10px rgba(196, 143, 212, 0.2);
  }
  50% {
    box-shadow:
      0 0 0 1px rgba(196, 143, 212, 0.4),
      0 0 22px rgba(196, 143, 212, 0.5);
  }
}
.qstep {
  display: flex;
  align-items: center; /* значок по центру плитки, а не прижат к её верху */
  gap: 12px;
  background: var(--card2);
  border-radius: 12px;
  padding: 12px;
  margin-top: 8px;
}
.qi {
  flex: none;
  width: 38px;
  height: 38px;
  border-radius: 12px;
  background: var(--card);
  color: var(--acc);
  display: flex;
  align-items: center;
  justify-content: center;
}
.qt {
  flex: 1;
  min-width: 0;
}
.qt b {
  display: flex;
  align-items: center;
  font-size: 15px;
  font-weight: 600;
}
.qt em,
.qt u {
  display: block;
  font-style: normal;
  text-decoration: none;
  line-height: 1.45;
}
.qt em {
  font-size: 13px;
  color: var(--txt);
  margin-top: 3px;
}
/* путь «как зайти» — вложенной плашкой, чтобы читался как отдельная инструкция.
   Плашка ДЫШИТ: это единственное действие в карточке, и люди его не замечали (08.08.2026).
   Меняются только фон и свечение — цвет текста неподвижен, читаемость не проседает ни в
   одной точке цикла. Пульс в цвет темы: плашка остаётся частью системы, неон снаружи. */
.qt u {
  font-size: 12px;
  color: var(--txt);
  background: var(--card);
  border-radius: 8px;
  padding: 7px 9px;
  margin-top: 8px;
  animation: qpath 3s ease-in-out infinite;
}
@keyframes qpath {
  0%,
  100% {
    background: var(--card);
    box-shadow: 0 0 0 0 rgba(0, 0, 0, 0);
  }
  50% {
    background: var(--accg);
    box-shadow:
      0 0 0 1px var(--accb),
      0 0 12px var(--accg);
  }
}
.qt u span {
  color: var(--acc);
  font-weight: 600;
}
.qt u span svg {
  vertical-align: -1px;
  margin-right: 4px;
}
/* Кому движение мешает — карточка стоит неподвижно, но выделенной: рамка и мягкое свечение
   остаются, гаснет только пульс. */
@media (prefers-reduced-motion: reduce) {
  .qcard {
    animation: none;
    box-shadow:
      0 0 0 1px rgba(196, 143, 212, 0.3),
      0 0 14px rgba(196, 143, 212, 0.28);
  }
  .qt u {
    animation: none;
  }
}
/* справочник функций: раскрытая часть строки-карточки (низ той же карточки) */
.food.op {
  border-radius: 16px 16px 0 0;
  margin-bottom: 0;
}
.featx {
  background: var(--card);
  border-radius: 0 0 16px 16px;
  margin: 0 0 8px;
  padding: 0 14px 14px;
}
.featx .fhow {
  font-size: 12px;
  color: var(--mut);
  line-height: 1.5;
  margin: 0 0 12px;
}
.fi {
  width: 56px;
  min-width: 56px;
  height: 56px;
  border-radius: 12px;
  background: var(--card2);
  display: flex;
  align-items: center;
  justify-content: center;
  color: var(--acc);
}
.bar {
  height: 8px;
  background: rgba(128, 128, 128, 0.18);
  border-radius: 999px;
  overflow: hidden;
}
.fill {
  height: 100%;
  background: var(--acc);
  border-radius: 999px;
}
.ph {
  display: flex;
  gap: 8px;
  margin-top: 10px;
}
.ph img {
  width: calc(50% - 4px);
  border-radius: 12px;
  background: var(--card2);
  min-height: 140px;
  object-fit: cover;
}
.lb {
  position: fixed;
  inset: 0;
  background: rgba(0, 0, 0, 0.93);
  display: none;
  align-items: center;
  justify-content: center;
  z-index: var(--z-lb);
}
/* Лайтбокс, Android-грабля (16.08.2026): у Android Chrome/WebView long-press на <img>
   открывает системное меню «Open in.../Download/Copy Link» независимо от JS-обработчиков —
   preventDefault на contextmenu/dragstart его не гасит, решение принимается раньше, на
   уровне рендерера картинки. У div такого меню нет вовсе, поэтому #lbi теперь div с
   background-image, а не img. */
.lb .lbimg {
  width: 96%;
  height: 92%;
  max-width: 96%;
  max-height: 92%;
  border-radius: 12px;
  background-size: contain;
  background-repeat: no-repeat;
  background-position: center;
}
.toast {
  position: fixed;
  top: var(--top-btn);
  left: 50%;
  transform: translateX(-50%);
  background: var(--card2);
  color: var(--txt);
  padding: 10px 18px;
  border-radius: 999px;
  font-size: 14px;
  z-index: var(--z-toast);
  display: none;
  max-width: 86%;
  text-align: center;
}
.arow {
  display: flex;
  flex-wrap: nowrap;
  gap: 8px;
  align-items: stretch;
  margin-top: 2px;
}
/* Ряд обязан помещаться в карточку на узком Android. Круглые кнопки ужимаются с 64px,
   но не ниже 44px — это тач-цель, меньше нельзя. Раньше у #askb стоял инлайновый
   width:auto;padding:14px 26px: ширина не зависела от экрана, и кнопка вылезала
   за карточку (найдено на Android 16.08.2026). */
.clip {
  background: var(--card2);
  border: none;
  border-radius: 12px;
  min-width: 44px;
  flex: 0 1 64px;
  color: var(--txt);
  display: flex;
  align-items: center;
  justify-content: center;
}
/* Кнопка прижата вправо и шириной по тексту, ряд не переносится вовсе: при нехватке
   места ужимаются круглые кнопки (64px→44px), а «Отправить» остаётся целой. Раньше у
   .arow стоял flex-wrap:wrap, и кнопка уезжала на вторую строку — при переносе браузер
   СНАЧАЛА рвёт строку и только потом сжимает, поэтому круглые оставались по 64px, а
   кнопке места не хватало (найдено владельцем 05.09.2026). Текст внутри не переносится. */
.arow .send {
  flex: 0 0 auto;
  min-width: 0;
  width: auto;
  margin: 0 0 0 auto;
  padding: 12px 16px;
  white-space: nowrap;
}
.clip:active {
  transform: scale(0.94);
}
/* Кнопка микрофона — НАМЕРЕННОЕ исключение из body { touch-action: manipulation }
   (сессия Г2, 06.09.2026): её держат пальцем, а это жест, а не тап. touch-action:none
   не даёт странице поехать под пальцем и рвать удержание через pointercancel, два
   остальных свойства глушат системное меню и выделение по долгому нажатию. На телефонах
   владельца меню и так не всплывало (Г0), но щиты дешёвые и страхуют от смены версии
   клиента. Не «чинить» — это не потерянный manipulation, см. test_zoom_lock.js. */
#micb {
  touch-action: none;
  -webkit-touch-callout: none;
  -webkit-user-select: none;
  user-select: none;
}
.clip.rec {
  background: var(--red);
  color: var(--wht);
  animation: pl 1.1s infinite;
}
@keyframes pl {
  50% {
    opacity: 0.55;
  }
}
/* Чип вложения — голос, фото, файл (перекрашен 06.09.2026). Зелёный тут означает «есть что
   отправить», а не «это голос»: серый чип рядом с серым полем ввода человек не замечал и
   отправлял пустой вопрос. Кант 2 px и заливка 12 % — выбор владельца по превью из восьми
   вариантов. Текст стал основным: --mut на зелёной подложке глохнет. */
.fchip {
  display: flex;
  align-items: center;
  gap: 8px;
  background: var(--grng);
  border: 2px solid var(--grn);
  border-radius: 12px;
  padding: 8px 10px;
  font-size: 12px;
  color: var(--txt);
  margin-top: 8px;
}
.fchip .fchimg {
  width: 34px;
  height: 34px;
  border-radius: 8px;
  background-size: cover;
  background-position: center;
  flex: 0 0 auto;
}
.fchip .x {
  margin-left: auto;
  background: none;
  border: none;
  color: var(--grn);
  font-size: 16px;
}
.ans {
  font-size: 14px;
  line-height: 1.55;
  margin-top: 10px;
  border-top: 0.5px solid var(--sep);
  padding-top: 10px;
}
/* Абзац ответа Sonny (04.09.2026). Отступ 12 — шаг шкалы; у последнего его нет, иначе под
   ответом появлялся лишний зазор перед кнопками «Это была моя еда» / «Сохранить». */
.ansp,
.ansl {
  margin: 0 0 12px;
}
.ansp:last-child,
.ansl:last-child {
  margin-bottom: 0;
}
/* Начертаний в системе два — 400 и 600; браузерный <b> рисует 700 и выбивается из шкалы. */
.ansp b,
.ans b,
pre b {
  font-weight: 600;
}
/* Тред Sonny. Вопрос человека — пузырь справа, ответ — во всю ширину с подписью:
   так видно, где чья реплика, без аватаров и лишнего шума. */
.thu {
  display: flex;
  justify-content: flex-end;
  margin-top: 12px;
}
.thub {
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  max-width: 85%;
}
.thq {
  background: var(--card2);
  border-radius: 12px;
  padding: 8px 12px;
  font-size: 14px;
}
.thwho {
  display: flex;
  align-items: center;
  gap: 4px;
  font-size: 12px;
  color: var(--mut);
  margin-bottom: 4px;
}
.thwho.thwme {
  padding-right: 4px;
}
.thwho svg {
  color: var(--acc);
}
/* Имя в индикаторе ожидания: стоит неподвижно, переливается только то, что Sonny делает. */
.thname {
  color: var(--mut);
  font-weight: 600;
}
.thact {
  display: flex;
  gap: 8px;
  align-items: center;
  flex-wrap: wrap;
  margin-top: 8px;
}
.throw {
  display: flex;
  gap: 8px;
  flex-wrap: wrap;
  margin-top: 10px;
}
.chip.tapc.thsm {
  font-size: 12px;
}
.chip.tapc.thfb {
  font-size: 12px;
  min-width: 44px;
}
.chip.tapc.thfb.on {
  background: var(--acc);
}
.chip.tapc.thfb:disabled {
  opacity: 0.45;
}
.chip.tapc.thfb.on:disabled {
  opacity: 1;
}
/* Прокручиваемая лента истории. Тонкий ползунок и мягкое затухание сверху — чтобы было
   видно, что выше есть текст. Затухание через sticky-псевдоэлемент, а не mask-image:
   маска обрезала бы сам ползунок. */
#ans.thsc {
  max-height: 45vh;
  overflow-y: auto;
  scrollbar-width: thin;
  scrollbar-color: var(--sep) transparent;
}
#ans.thsc::before {
  content: '';
  display: block;
  position: sticky;
  top: 0;
  height: 16px;
  margin-bottom: -16px;
  background: linear-gradient(var(--card), transparent);
  pointer-events: none;
}
#ans.thsc::-webkit-scrollbar {
  width: 3px;
}
#ans.thsc::-webkit-scrollbar-thumb {
  background: var(--sep);
  border-radius: 999px;
}
/* Ожидание ответа: по тексту бежит блик. Точки рядом — готовый .fdots. */
.thglow {
  background: linear-gradient(90deg, var(--mut) 30%, var(--txt) 50%, var(--mut) 70%);
  background-size: 220% 100%;
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
  animation: thg 2s linear infinite;
}
@keyframes thg {
  from {
    background-position: 120% 0;
  }
  to {
    background-position: -120% 0;
  }
}
.hero {
  max-width: 420px;
  margin: 6vh auto 0;
  text-align: center;
  padding: 0 16px;
}
.logo {
  color: var(--acc);
  margin-bottom: 2px;
}
.hero h2 {
  font-size: 20px;
  margin: 10px 0 6px;
}
.tagl {
  color: var(--mut);
  font-size: 14px;
  line-height: 1.55;
  margin: 0 0 14px;
}
.feat {
  display: flex;
  align-items: center;
  gap: 12px;
  text-align: left;
  background: var(--card);
  border-radius: 12px;
  padding: 12px 14px;
  margin: 8px 0;
  font-size: 14px;
}
.feat svg {
  color: var(--acc);
  min-width: 30px;
}
/* Форма входа на витрине (С6, 07.09.2026). Живёт внутри .hero, поэтому своей ширины и
   выравнивания не задаёт — только собственный ритм. Поля и кнопка берут готовые .inp и .btn:
   новых размеров и радиусов тут не заводится. */
.auth {
  margin-top: 24px;
  text-align: left;
}
.authh {
  font-size: 16px;
  font-weight: 600;
  margin-bottom: 8px;
}
/* Отказ сервера: красный тут по делу — это сбой действия, а не оценка человека. */
.autherr {
  color: var(--red);
  font-size: 14px;
  line-height: 1.5;
  min-height: 21px;
  margin-top: 8px;
}
/* Переключатель «вход ↔ регистрация» — строка, а не кнопка: второе действие на экране не
   должно спорить с первым. Тач-цель всё равно 44 (скилл §4). */
.authsw {
  display: flex;
  align-items: center;
  justify-content: center;
  min-height: 44px;
  color: var(--mut);
  font-size: 14px;
  text-align: center;
}
.authsw:active {
  background: var(--card2);
  border-radius: var(--r-m);
}
.nbgrid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 8px;
  margin: 12px 0;
}
.nbr {
  padding: 8px 6px;
  border-radius: 12px;
  background: var(--card2);
  color: var(--txt);
  text-align: center;
  font-weight: 600;
  border: 2px solid transparent;
  font-size: 16px;
  min-height: 52px;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 2px;
}
/* Название говорит, зачем радиус, цифра — сколько это в километрах. Одно без другого
   не работает: «Поеду» без числа непонятно, «15 км» без слова ни о чём не говорит. */
.nbrt {
  font-size: 14px;
  font-weight: 600;
}
.nbrd {
  font-size: 12px;
  font-weight: 400;
  color: var(--mut);
}
.nbr.on {
  border-color: var(--acc);
  color: var(--acc);
}
.nbr.on .nbrd {
  color: var(--acc);
}
.nbmapnote {
  margin: 24px 0 0;
  font-size: 12px;
  line-height: 1.5;
  color: var(--mut);
}
#nbmap {
  height: 320px;
  border-radius: 12px;
  margin-top: 12px;
  background: var(--sheet);
  overflow: hidden;
}
.nbbt {
  padding: 8px 11px;
  border: 0;
  border-radius: 8px;
  color: var(--wht);
  font-weight: 600;
  font-size: 14px;
}
.nbgalw {
  position: relative;
}
.nbgaln {
  position: absolute;
  right: 10px;
  bottom: 20px;
  padding: 2px 8px;
  border-radius: 999px;
  background: rgba(20, 20, 22, 0.78);
  color: var(--wht);
  font-size: 12px;
  pointer-events: none;
}
.nbgal {
  display: flex;
  gap: 6px;
  overflow-x: auto;
  scroll-snap-type: x mandatory;
  -webkit-overflow-scrolling: touch;
  margin-bottom: 10px;
  border-radius: 12px;
}
.nbgal::-webkit-scrollbar {
  display: none;
}
.nbgal .nbgimg {
  height: 200px;
  min-width: 84%;
  background-size: cover;
  background-position: center;
  border-radius: 12px;
  scroll-snap-align: center;
  flex: 0 0 auto;
}
.nbrow {
  display: flex;
  gap: 6px;
  flex-wrap: wrap;
  margin-top: 4px;
}
.nbcards {
  margin-bottom: 6px;
}
.nbcard {
  background: var(--card2);
  border-radius: 12px;
  padding: 11px 13px;
  margin-bottom: 8px;
}
/* Снимок заведения в списке. Полоса, а не миниатюра: карточка узкая, а по фото человек
   решает «зайти или нет» быстрее, чем по названию. */
.nbcimg {
  display: block;
  width: 100%;
  height: 120px;
  background-size: cover;
  background-position: center;
  border-radius: 8px;
  margin-bottom: 8px;
  background-color: var(--sep);
}
.nbcard:active {
  opacity: 0.65;
}
/* Группа выдачи доставки (Д2, 04.09.2026): заголовок-ресторан и найденные в нём блюда
   одним блоком. Блюда лежат ВНЕ .nbcard намеренно — карточка нажимается целиком и уводит
   в сервис, а у строки блюда своя кнопка «Подробнее», и её нажатие не должно открывать
   ресторан. Отступ по шкале (12), а не 13 из .nbcard: блюда стоят практически под
   названием заведения, а лишнего значения в шкалу отступов не заводим. */
.nbgrp {
  margin-bottom: 16px;
}
.nbgrp .nbcard {
  margin-bottom: 0;
}
.nbgdish {
  padding: 0 12px;
}
/* «Показать остальные (N)» у самой группы. Строка списка, поэтому отклик — подсветка,
   а не сжатие; 44px — тач-цель по системе. */
.nbgmore {
  width: 100%;
  min-height: 44px;
  border: 0;
  background: none;
  color: var(--acc);
  font-size: 14px;
  font-weight: 600;
  text-align: left;
  padding: 0;
  border-radius: var(--r-s);
}
.nbgmore:active {
  background: var(--card2);
}
.nbmrow {
  display: flex;
  justify-content: space-between;
  gap: 12px;
  font-size: 14px;
  padding: 7px 0;
  border-bottom: 0.5px solid var(--sep);
}
.nbmp {
  color: var(--acc);
  white-space: nowrap;
  font-weight: 600;
}
.nbmt {
  min-width: 0;
}
/* Меню доставки, волна 3 (04.08.2026). Оформление живёт только у подходящих блюд: картинка,
   рейтинг, метки и описание по тапу. У неподходящих остаётся прежняя строка .nbmrow —
   меню бывает 200 позиций, и картинки у всех подряд сделали бы экран неподъёмным. */
.nbdish {
  display: flex;
  gap: 12px;
  align-items: flex-start;
  padding: 8px 0;
  border-bottom: 0.5px solid var(--sep);
}
/* Закончившееся блюдо гаснет и теряет ссылку: вести человека на недоступное нечестно. */
.nbdish.off {
  opacity: 0.5;
}
.nbdish .nbmrow {
  border: 0;
  padding: 0;
}
.nbdimg {
  flex: 0 0 64px;
  width: 64px;
  height: 64px;
  border-radius: var(--r-m);
  background-size: cover;
  background-position: center;
  background-color: var(--card2);
  transition: transform 0.15s cubic-bezier(0.4, 0, 0.2, 1);
  /* Лупа держится удержанием, а не долгим тапом системы: без этого iOS показывает своё
     меню «Сохранить изображение» и жест перехватывается. На Android то же самое системное
     меню давал сам тег img независимо от JS — решение 16.08.2026: div с background-image
     вместо img, у div такого меню нет вовсе. */
  -webkit-touch-callout: none;
  user-select: none;
}
/* Удержание по картинке блюда (D6, 05.08.2026). Растёт от левого края, а не от центра:
   строка меню узкая, и увеличенный от центра снимок уезжал бы за край экрана.
   z-index — чтобы лечь поверх соседних строк, а не быть обрезанным ими. */
.nbdimg.nbdzoom {
  transform: scale(4);
  transform-origin: left center;
  position: relative;
  z-index: 2;
  /* Радиус по шкале. При scale(4) он тоже умножается, поэтому мелкий --r-s даёт на экране
     привычные ~8px, а не заваленный угол. */
  border-radius: var(--r-s);
  box-shadow: 0 8px 32px rgba(0, 0, 0, 0.6);
}
@media (prefers-reduced-motion: reduce) {
  .nbdimg {
    transition: none;
  }
}
.nbdmain {
  flex: 1;
  min-width: 0;
}
.nbdsub {
  font-size: 12px;
  color: var(--mut);
  margin-top: 4px;
  line-height: 1.5;
}
.nbdbadge {
  color: var(--acc);
  font-weight: 600;
}
.nbdpromo {
  color: var(--grn);
  font-weight: 600;
}
/* Описание раскрывается по тапу, а не занимает место сразу: у 86% позиций оно есть,
   и развёрнутые описания превращают список в простыню. */
.nbdmore {
  border: 0;
  background: none;
  padding: 4px 0;
  font-size: 12px;
  color: var(--acc);
  font-weight: 600;
}
.nbddesc {
  font-size: 12px;
  color: var(--mut);
  line-height: 1.5;
  padding-bottom: 4px;
}
.nbdgo {
  flex: 0 0 44px;
  width: 44px;
  height: 44px;
  border: 0;
  background: none;
  color: var(--mut);
  display: flex;
  align-items: center;
  justify-content: center;
  margin-right: -12px;
}
/* Разделы меню идут одним листом, все открыты (D8, 05.08.2026). Заголовок — разделитель:
   min-height 44px оставлен не как тач-цель, а чтобы разделы не слипались на длинном меню. */
.nbsec {
  margin-top: 8px;
}
.nbsech {
  width: 100%;
  min-height: 44px;
  display: flex;
  align-items: center;
  gap: 8px;
  border: 0;
  background: none;
  color: var(--txt);
  font-size: 14px;
  font-weight: 600;
  padding: 0;
  text-align: left;
}
.nbsech > span:first-child {
  flex: 1;
  min-width: 0;
}
/* Схлопывание разделов снято 05.08.2026 (D8): заголовок — разделитель, а не кнопка,
   поэтому правил для стрелки (.nbsech svg / .on) и счётчика (.nbsecn) больше нет. */
.nbsecb {
  padding-bottom: 4px;
}
/* Шапка доставки: время, «открыто», ценовой уровень. Плата за доставку сознательно
   не показывается — у сервисов она условная и без условий вводит в заблуждение. */
.nbdmeta {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 8px;
  font-size: 12px;
  color: var(--mut);
  margin: 8px 0;
}
.nbdmeta svg {
  vertical-align: -2px;
}
.nbdsep {
  color: var(--sep);
}
.nbopen {
  color: var(--grn);
  font-weight: 600;
}
.nbshut {
  color: var(--red);
  font-weight: 600;
}
.nbcuis {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  margin: 8px 0;
}
.onbf {
  margin-top: 6px;
}
.onbl {
  display: block;
  font-size: 12px;
  color: var(--mut);
  margin: 14px 0 6px;
}
.onbrow {
  display: flex;
  gap: 8px;
  flex-wrap: wrap;
}
.onbopt {
  flex: 1;
  min-width: 88px;
  padding: 12px;
  border-radius: 12px;
  background: var(--card2);
  color: var(--txt);
  border: 2px solid transparent;
  font-weight: 600;
  font-size: 14px;
}
.onbopt.on {
  border-color: var(--acc);
  color: var(--acc);
}
.onbopt.on {
  box-shadow: 0 0 0 3px var(--accg);
}
.rb-locked {
  opacity: 0.4;
  pointer-events: none;
}
.rbrow {
  display: flex;
  align-items: center;
  justify-content: flex-start;
  gap: 8px;
  text-align: left;
}
.rbrow > span:nth-child(2) {
  flex: 1;
}
.rbchk {
  width: 18px;
  height: 18px;
  border-radius: 8px;
  border: 2px solid var(--mut);
  display: inline-block;
  position: relative;
}
.rbchk.on {
  border-color: var(--acc);
}
.rbchk.on::after {
  content: '';
  position: absolute;
  left: 4px;
  top: 0px;
  width: 5px;
  height: 9px;
  border-right: 2px solid var(--acc);
  border-bottom: 2px solid var(--acc);
  transform: rotate(45deg);
}
.rbstar {
  color: var(--acc);
  font-size: 20px;
  line-height: 1;
}
/* Панель сохранения списка продуктов (С8 банка рецептов, 04.09.2026). В каталоге
   59 строк, и кнопка в самом конце означала бы прокрутку через весь каталог ради одного
   нажатия; счётчик отмеченного по той же причине живёт здесь, а не под заголовком.
   Липкая, а не прибитая: поля ввода на этом экране нет (поиска нет), дёргаться от
   клавиатуры нечему. Отрицательные поля гасят отступы .nbmc — панель идёт от края до
   края, а её нижний паддинг возвращает безопасную зону. */
.plbar {
  position: sticky;
  bottom: 0;
  z-index: 1;
  margin: 20px -16px calc(-30px - var(--sab));
  padding: 10px 16px calc(10px + var(--sab));
  background: var(--bg);
  border-top: 1px solid var(--sep);
  display: flex;
  flex-direction: column;
  gap: 8px;
}
/* Экран «Рецепты»: строка-часть приёма (крупа, салат, тёплый овощ, заправка). Подпись
   слева не выделяется цветом — она служебная, важно то, что справа. */
.rbnl {
  display: flex;
  align-items: baseline;
  gap: 8px;
  margin-top: 8px;
  font-size: 14px;
}
.rbnl > b {
  flex: none;
  min-width: 96px;
  color: var(--mut);
  font-weight: 400;
  font-size: 12px;
  line-height: 1.5;
}
/* «Готово, когда» — то, ради чего банк и писался, поэтому признак готовности не сливается
   с остальным текстом карточки. Плашка вторым уровнем, а не акцентная (.qx): акцентом на
   этом экране помечена формула тарелки, и две одинаковые плашки в одной карточке спорили
   бы друг с другом. */
.rbnd {
  background: var(--card2);
  border-radius: var(--r-m);
  padding: 12px 16px;
  margin-top: 12px;
  font-size: 14px;
}
.rb-empty .rbchk {
  border-color: var(--red);
}
.rb-disabled {
  opacity: 0.55;
}
/* Возраст — единственное числовое поле анкеты. Ширина по содержимому (двузначное число),
   единица «лет» лежит внутри рамки справа: подпись под полем добавила бы четвёртую строку
   ради двух букв. Поле поверх подписи по z-index не нужно — они не пересекаются, но
   pointer-events снят, чтобы тап по слову «лет» попадал в поле, а не в пустоту. */
.onbnum {
  position: relative;
  width: 132px;
}
.onbnum .inp {
  padding-right: 52px;
}
.onbunit {
  position: absolute;
  right: 13px;
  top: 50%;
  transform: translateY(-50%);
  font-size: 14px;
  color: var(--mut);
  pointer-events: none;
}
.nbload {
  margin-top: 16px;
}
.nbbar {
  height: 6px;
  background: var(--card2);
  border-radius: 999px;
  overflow: hidden;
  margin: 0 0 10px;
}
.nbbar i {
  display: block;
  height: 100%;
  width: 38%;
  border-radius: 999px;
  background: var(--acc);
  animation: nbslide 1.15s infinite ease-in-out;
}
@keyframes nbslide {
  0% {
    transform: translateX(-110%);
  }
  100% {
    transform: translateX(360%);
  }
}
/* Шаги ожидания доставки (15.08.2026). Бегущая полоса .nbbar одна показывала «что-то идёт»
   и больше ничего: на 80-й секунде она выглядит ровно так же, как на 5-й. Здесь появились
   четыре шага, и пройденные гаснут галочкой — видно не «ждите», а сколько осталось.
   🚨 .nbload/.nbbar НЕ трогать: полоса теперь стоит НАД шагами на ОБОИХ экранах ожидания
   (nbDelivSearch и nbSearch) и отдельно живёт в family/scan/progress. Её размеры держат
   test_page_load.js и test_accent_contrast.js. */
.dvst {
  margin-top: 16px;
}
.dvst b {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 8px 0;
  font-size: 14px;
  font-weight: 400;
  color: var(--mut);
}
.dvst b svg {
  flex: 0 0 auto;
}
/* Идущий шаг — цветом и весом: в списке из четырёх строк взгляд обязан находить текущую
   строку без чтения всех остальных. */
.dvst b.on {
  color: var(--acc);
  font-weight: 600;
}
.dvst b.dn {
  color: var(--grn);
}
.nbname {
  font-weight: 600;
  font-size: 16px;
}
.nbmeta {
  font-size: 12px;
  color: var(--mut);
  margin-top: 3px;
}
.nbaddr {
  font-size: 12px;
  color: var(--mut);
  margin-top: 2px;
}
/* Иконка, стоящая внутри строки текста: у сетки 256 весь бокс садится на baseline,
   поэтому её опускаем. Диаграммы (графики, кольцо, дорога) сюда не попадают —
   они рисуют свои SVG в собственных контейнерах. */
.nbdist svg,
.nbico svg,
#nbdet svg,
.onbopt svg,
.li > div > svg {
  vertical-align: -3px;
}
.nbdist {
  font-size: 14px;
  color: var(--acc);
  font-weight: 600;
  margin: 7px 0 2px;
}
.nbnote {
  background: var(--accg);
  border: 1px solid var(--accb);
  border-radius: 12px;
  padding: 12px 14px;
  margin: 12px 0 4px;
  font-size: 14px;
  line-height: 1.55;
  color: var(--txt);
}
/* Переключатель «Избранное» — единственная кнопка под поиском, по центру */
.nbfavrow {
  display: flex;
  justify-content: center;
  margin: 12px 0 4px;
}
/* Фильтры экрана «Поесть рядом»: нажимаемые чипы (.tapc по системе — 44px, 14px/600) */
.nbfhead {
  margin: 24px 0 4px;
  font-size: 12px;
  color: var(--mut);
}
.nbchips {
  display: flex;
  gap: 8px;
  overflow-x: auto;
  padding: 4px 0 8px;
  -webkit-overflow-scrolling: touch;
  scrollbar-width: none;
}
.nbchips::-webkit-scrollbar {
  display: none;
}
.nbchip {
  flex: 0 0 auto;
  min-height: 44px;
  padding: 0 16px;
  border: 0;
  border-radius: 999px;
  background: var(--card2);
  color: var(--txt);
  font-size: 14px;
  font-weight: 600;
  display: inline-flex;
  align-items: center;
  gap: 6px;
}
.nbchip.on {
  background: var(--acc);
}
.nbchip:disabled {
  opacity: 0.4;
}
.nbtier {
  font-size: 12px;
  color: var(--mut);
  margin: 16px 0 8px;
}
.nbmore {
  width: 100%;
  min-height: 44px;
  border: 0;
  border-radius: 12px;
  background: var(--card2);
  color: var(--txt);
  font-size: 14px;
  font-weight: 600;
  margin-top: 12px;
}
.nbfit {
  display: inline-block;
  padding: 2px 8px;
  border-radius: 8px;
  font-size: 12px;
  background: var(--card2);
  color: var(--mut);
}
/* Смысловая шкала: та же, что у вердикта продукта. --red тут НЕ используем — это не ошибка,
   а объяснение, и цвет обязан это отражать. */
.nbfit.f2 {
  background: var(--sc-ok);
  color: var(--bg);
}
.nbfit.f0 {
  background: var(--sc-e);
  color: var(--bg);
}
.nbtip {
  font-size: 12px;
  color: var(--mut);
  margin-top: 6px;
  line-height: 1.5;
}
/* «7 из 80 блюд по системе» — ради этой строки вкладка «Доставка» и существует, а в общем
   сером тексте она терялась. Зелёный тут по смыслу: это хорошая новость, а не предупреждение.
   Цвет не единственный носитель — сама фраза всё называет словами. */
.nbmfit {
  font-size: 12px;
  font-weight: 600;
  color: var(--grn);
  margin-top: 6px;
  line-height: 1.5;
}
/* Метки на карте — divIcon, то есть обычный HTML: токены здесь работают, литералы не нужны.
   Обводка цветом фона — не декоративная тень, а отбивка от подложки карты. */
.nbmk {
  background: none;
  border: 0;
}
.nbnum {
  width: 26px;
  height: 26px;
  border-radius: 999px;
  background: var(--acc);
  font-size: 12px;
  font-weight: 600;
  display: flex;
  align-items: center;
  justify-content: center;
  box-shadow: 0 0 0 2px var(--bg);
}
.nbdot {
  width: 12px;
  height: 12px;
  border-radius: 999px;
  background: var(--mut);
  box-shadow: 0 0 0 2px var(--bg);
}
/* Сердце избранного. Тач-цель 44×44 по системе; отрицательные поля возвращают её
   в габариты карточки, чтобы кнопка не раздувала строку с названием. */
.nbhead {
  display: flex;
  align-items: flex-start;
  gap: 8px;
}
.nbhead .nbname {
  flex: 1;
  min-width: 0;
}
.nbheart {
  flex: 0 0 44px;
  width: 44px;
  height: 44px;
  margin: -11px -13px 0 0;
  border: 0;
  background: none;
  color: var(--mut);
  display: flex;
  align-items: center;
  justify-content: center;
}
.nbheart.on {
  color: var(--acc);
}
.nbmodal {
  position: fixed;
  inset: 0;
  background: var(--bg);
  z-index: var(--z-modal);
  display: none;
  overflow-y: auto;
  -webkit-overflow-scrolling: touch;
  overscroll-behavior: contain;
}
.nbmodal.on {
  display: block;
}
.nbmc {
  max-width: 560px;
  margin: 0 auto;
  /* Стрелка теперь в потоке и сама занимает место — прежних «+52px» под неё больше не надо. */
  padding: max(16px, var(--top-btn)) 16px calc(30px + var(--sab));
}
/* Стрелка «назад» карточки места. Раньше тут был крестик справа — он налезал на сердце
   у мест без фото. Слева и стрелкой — как во всех остальных шторках.
   🚨 С 05.08.2026 (D4) она НЕ position:fixed: владелец решил, что она должна уезжать вверх
   вместе со страницей, а не висеть на месте. Прибитой её когда-то сделали ради мест БЕЗ фото
   (там она заезжала под заголовок) — теперь это решено иначе: в потоке она сама занимает
   строку и ни на что не налезает, а над фотографией поднимается поверх снимка (см. ниже). */
.nbback {
  position: relative;
  /* Тот же токен, что и раньше: кнопка по-прежнему верхний слой модалки — просто теперь
     внутри её потока, а не поверх экрана. Число вместо токена = ошибка (лестница слоёв). */
  z-index: var(--z-modal-btn);
  display: flex;
  margin: 0 0 8px;
  /* Тот же .miniback, что во всех шторках. Фон темнее штатного: над фотографией кнопка
     лежит поверх снимка, и полупрозрачный серый на светлом фото смывается с иконкой. */
  background: rgba(20, 20, 22, 0.78);
  box-shadow: 0 2px 12px rgba(0, 0, 0, 0.5);
}
/* Карточка с фотографией: стрелка ложится ПОВЕРХ снимка (решение владельца 05.08.2026),
   поэтому галерея подтягивается под неё отрицательным отступом — ровно на высоту кнопки
   и её зазор (44 + 8). Прокрутке это не мешает: оба элемента в потоке и едут вместе. */
.nbback + .nbgalw,
.nbback + .nbgal {
  margin-top: -52px;
}
.miniback {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 44px;
  height: 44px;
  border-radius: 50%;
  background: rgba(120, 120, 128, 0.28);
  color: var(--txt);
  border: none;
  margin-bottom: 10px;
  transition:
    transform 0.18s ease,
    background 0.18s ease,
    box-shadow 0.18s ease;
  box-shadow: 0 0 0 0 rgba(255, 255, 255, 0);
}
.miniback svg {
  transform: scaleX(-1);
}
.miniback:active {
  transform: scale(1.12);
  background: rgba(140, 140, 150, 0.45);
  box-shadow: 0 0 0 1px rgba(255, 255, 255, 0.35);
}
.skel {
  position: relative;
  overflow: hidden;
}
.skel::after {
  content: '';
  position: absolute;
  inset: 0;
  transform: translateX(-150%);
  background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.22), transparent);
  animation: shimmer 2s infinite;
}
@keyframes shimmer {
  100% {
    transform: translateX(150%);
  }
}
.skel-title {
  height: 22px;
  width: 70%;
  margin: 2px 0 14px;
  background: var(--card2);
  border-radius: 8px;
}
.skel-line {
  height: 13px;
  width: 100%;
  margin: 6px 0;
  background: var(--card2);
  border-radius: 8px;
}
.skel-card {
  height: 70px;
  border-radius: 16px;
  margin: 10px 0;
  background: var(--card2);
}
.skel-row {
  height: 44px;
  border-radius: 12px;
  margin: 6px 0;
  background: var(--card2);
}
.skel-pill {
  height: 38px;
  border-radius: 12px;
  background: var(--card2);
}
.fdots i {
  display: inline-block;
  width: 5px;
  height: 5px;
  border-radius: 50%;
  background: var(--acc);
  margin-left: 3px;
  animation: fdot 1.2s infinite;
}
.fdots i:nth-child(2) {
  animation-delay: 0.2s;
}
.fdots i:nth-child(3) {
  animation-delay: 0.4s;
}
@keyframes fdot {
  0%,
  80%,
  100% {
    opacity: 0.25;
    transform: translateY(0);
  }
  40% {
    opacity: 1;
    transform: translateY(-3px);
  }
}
.p20 {
  padding: 20px;
}
.subh {
  margin: 10px 0 4px;
  font-weight: 600;
  color: var(--txt);
}
.ul0 {
  margin: 0;
  padding-left: 18px;
}
.li14 {
  font-size: 14px;
}
.skelt {
  color: transparent;
}
@media (hover: hover) {
  .btn:hover {
    filter: brightness(1.08);
  }
  .qa:hover,
  .food:hover,
  .nbcard:hover {
    background: var(--card2);
  }
  .tabs button:hover {
    color: var(--txt);
  }
  .clip:hover {
    background: var(--sep);
  }
}
@media (prefers-reduced-motion: reduce) {
  * {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
  }
}
.awt {
  display: flex;
  align-items: center;
  gap: 16px;
}
/* Картинка награды (PLAN-награды-картинки.md): акварель в круглой маске. Размер задаётся
   инлайном из awImg() — мест показа пять, и у каждого свой кегль. Незаработанное не
   прячется под замок, а гаснет: видно, что ждёт впереди. */
.awpic {
  display: flex;
  align-items: center;
  justify-content: center;
  flex: none;
  overflow: hidden;
  border-radius: var(--r-full);
  background: var(--card2);
  margin: 0 auto;
}
.awpic img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
  /* картинка не перехватывает жесты: тап уходит на кнопку награды, долгое нажатие не
     открывает системное «сохранить изображение» */
  pointer-events: none;
}
.awpic i {
  display: none;
  align-items: center;
  justify-content: center;
  width: 100%;
  height: 100%;
  font-style: normal;
}
.awpic.sk {
  border: 1px dashed var(--sep);
  color: var(--mut);
}
.awpic.nx {
  box-shadow: 0 0 0 2px var(--accb);
}
.awpic.off {
  filter: grayscale(1) brightness(0.7);
  opacity: 0.5;
}
.awn {
  font-size: 20px;
  font-weight: 600;
}
.aws {
  font-size: 12px;
  color: var(--mut);
  margin-top: 2px;
}
.awnx {
  font-size: 12px;
  color: var(--mut);
  margin-top: 8px;
  text-align: center;
}
.awmd {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: 8px;
  margin-top: 10px;
}
.awmd.g3 {
  grid-template-columns: repeat(3, 1fr);
}
.awmd > * {
  background: var(--card2);
  border: 1px solid transparent;
  border-radius: var(--r-m);
  padding: 12px 4px;
  text-align: center;
  color: var(--txt);
  font: inherit;
}
.awmd .t {
  display: block;
  font-size: 12px;
  color: var(--mut);
  margin-top: 4px;
}
.awmd > .on .t,
.awmd > .nx .t {
  color: var(--txt);
}
.awmd > .nx {
  border-color: var(--sep);
}
.awmd > .off {
  opacity: 0.35;
  filter: grayscale(1);
}
.awmd .t b {
  font-weight: 600;
}
.awgh {
  font-size: 12px;
  color: var(--mut);
  font-weight: 600;
  margin: 12px 0 2px;
}
.awrw {
  text-align: center;
  padding: 8px 0;
}
.awrw .k {
  font-size: 12px;
  color: var(--acc);
  font-weight: 600;
  letter-spacing: 0.08em;
  margin-top: 16px;
}
.awrw .t {
  font-size: 28px;
  font-weight: 700;
  letter-spacing: -0.5px;
  margin: 4px 0 8px;
}
.awrw .x {
  font-size: 14px;
  line-height: 1.55;
  color: var(--mut);
}
.awrw .awln {
  margin-top: 8px;
}
/* ── экран «Награды» (редизайн 30.08.2026, §5.1 плана): счёт, фраза звания, сетка 12 званий,
   лесенки порогов. Плитки вех/серий/тела — .awmd выше. ── */
/* Шапка блока наград на вкладке «Путь» — та же строка, что у серии выше: с 30.08.2026
   награды часть экрана, а не дверь в отдельный лист. */
.awhd {
  display: flex;
  align-items: center;
  gap: 8px;
  margin: 24px 0 0;
}
.awhd .fl {
  display: flex;
  color: var(--acc);
}
.awhd .t {
  flex: 1;
  font-size: 16px;
  font-weight: 600;
}
.awhd .b {
  font-size: 12px;
  color: var(--mut);
}
.awph {
  font-size: 14px;
  line-height: 1.5;
  color: var(--mut);
  margin-top: 12px;
}
.awg {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 16px 8px;
  margin-top: 12px;
}
.awb {
  background: none;
  border: 0;
  padding: 0;
  color: var(--txt);
  text-align: center;
  font: inherit;
}
.awb .n {
  display: block;
  font-size: 12px;
  line-height: 1.25;
  margin-top: 8px;
}
.awb.off .n {
  color: var(--mut);
}
.awb .d {
  display: block;
  font-size: 12px;
  color: var(--mut);
}
.awhint {
  margin-top: 12px;
}
.ladr {
  display: flex;
  align-items: center;
  gap: 12px;
  margin-top: 16px;
}
.lad {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 8px;
  flex: 1;
  min-width: 0;
}
.lad .l {
  width: 100%;
  font-size: 14px;
}
.pill {
  min-width: 44px;
  height: 36px;
  padding: 0 12px;
  border-radius: var(--r-full);
  background: var(--card2);
  border: 1px solid transparent;
  color: var(--mut);
  font: inherit;
  font-size: 12px;
  font-weight: 600;
  display: inline-flex;
  align-items: center;
  justify-content: center;
}
.pill.on {
  background: var(--accg);
  border-color: var(--accb);
  color: var(--txt);
}
.pill.nx {
  border-color: var(--sep);
  color: var(--txt);
}
/* ── «Путь», редизайн 30.08.2026 (PLAN-редизайн-путь-исполнение.md §7): герой дня, полоса
   периодов, серия с заморозками. Кегли — по шкале; 16/400 у текста дня — единственное место
   длинного чтения в приложении. Цвета — только токены. ── */
.ph-hd {
  display: flex;
  align-items: center;
  gap: 8px;
}
.ph-em {
  font-size: 16px;
  line-height: 1;
}
.jb {
  display: flex;
  gap: 4px;
  height: 6px;
  margin-top: 12px;
}
.jb i {
  display: block;
  flex: 1;
  height: 6px;
  min-width: 6px;
  border-radius: var(--r-full);
  background: var(--sep);
  position: relative;
  overflow: hidden;
}
.jb i b {
  position: absolute;
  left: 0;
  top: 0;
  bottom: 0;
  border-radius: var(--r-full);
}
/* Карточка дня v4 (PLAN-путь-240-v4-пересборка.md §3.3, «Г в оформлении А», выбрано
   владельцем 02.09.2026): рамка и мягкое свечение цветом периода. Цвет приходит инлайновой
   переменной --p (её ставит heroCard через pCol) — своего hex тут нет, все двенадцать
   значений живут токенами --p-<key> в :root. Внутренний отступ и радиус — от .card.
   ⚠️ Кегли и отступы взяты по шкале скилла (§2 miniapp-design), а не пиксель в пиксель
   с эталонной страницей: там стояли 18/21/17/14/9 px, которых в системе нет. */
.ph-hero,
.ph-start {
  border: 1px solid color-mix(in srgb, var(--p) 36%, transparent);
  box-shadow: 0 10px 34px -16px color-mix(in srgb, var(--p) 70%, transparent);
}
.ph-hero .kick {
  color: var(--p);
}
/* Действие дня — первое, что видит глаз: крупно, сразу под полосой периодов. */
.ph-act {
  font-size: 20px;
  font-weight: 600;
  line-height: 1.25;
  letter-spacing: -0.01em;
  margin: 12px 0 4px;
}
/* Список пунктов дня (Путь v5, вид 4 макета preview-карточка-v5.html, выбран владельцем
   06.09.2026). Два столбца: квадрат и текст. 🚨 На самом пункте НЕЛЬЗЯ ставить display:flex —
   тогда каждый кусок текста («Выпейте», жирное «пол-литра воды», «до завтрака») становится
   отдельной колонкой и строка разъезжается столбцами. Отсюда grid и текст одним span. */
.ph-tasks {
  list-style: none;
  margin: 12px 0 0;
  padding: 0;
  counter-reset: pti;
}
.ph-tasks li {
  display: grid;
  grid-template-columns: 24px 1fr;
  column-gap: 12px;
  align-items: start;
  /* вертикальный padding вместо зазора между пунктами: строка вырастает до тач-цели (24 текст
     + 12 и 12 = 48), и подсветка нажатия занимает её целиком */
  margin: 0 -8px;
  padding: 12px 8px;
  border-radius: var(--r-s);
  counter-increment: pti;
}
.ph-tasks li:active {
  background: var(--card2);
}
/* Квадрат не закрашивается никогда: отметку показывает галочка поверх него. */
/* Квадрат ростом со строку (16 × 1.5 = 24) — тогда он стоит вровень с первой строкой текста
   сам, без подгонки отступом, и остаётся на месте, когда пункт переносится на две строки. */
.ph-tasks .bx {
  position: relative;
  width: 24px;
  height: 24px;
  border: 1.5px solid color-mix(in srgb, var(--p) 50%, transparent);
  border-radius: var(--r-s);
}
.ph-tasks li.done .bx {
  border-color: color-mix(in srgb, var(--grn) 34%, transparent);
}
/* 🚨 overflow:visible обязателен: галочка нарисована крупнее квадрата и обоими концами
   выходит за рамку — без него концы обрежет. */
.ph-tasks .bx svg {
  position: absolute;
  left: -4px;
  top: -6px;
  width: 31px;
  height: 31px;
  overflow: visible;
  pointer-events: none;
}
.ph-tasks .bx path {
  fill: none;
  stroke: var(--grn);
  stroke-width: 2.7;
  stroke-linecap: round;
  stroke-linejoin: round;
}
/* Галочка лежит в разметке всегда и показывается классом — так она появляется в том же
   кадре, что и нажатие, ещё до ответа сервера. */
.ph-tasks li:not(.done) .bx svg {
  display: none;
}
.ph-tasks .t {
  font-size: 16px;
  line-height: 1.5;
}
.ph-tasks li.done .t {
  color: var(--mut);
}
.ph-tasks b {
  font-weight: 600;
}
.ph-tasks i {
  color: var(--mut);
}
/* Картинка дня — полоса 16:7 между кнопками и разбором (место «В», решение владельца
   06.09.2026). Пропорция держится aspect-ratio, поэтому кнопки не прыгают, пока файл грузится;
   картинки у дня может не быть — тогда блока нет вовсе. */
.ph-pic {
  margin-top: 16px;
  border-radius: var(--r-m);
  overflow: hidden;
  background: var(--card2);
  aspect-ratio: 16 / 7;
}
.ph-pic img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
}
.ph-st-x {
  font-size: 16px;
  line-height: 1.6;
  color: var(--mut);
  margin: 8px 0 16px;
}
/* Две кнопки в строку, по половине ширины: «Выполнил» слева — основное действие. */
.ph-btns {
  display: flex;
  gap: 8px;
  margin-top: 12px;
}
.ph-btns .btn {
  flex: 1;
  margin-top: 0;
}
/* Ответ уже дан — на месте кнопок строка состояния той же высоты, экран не подпрыгивает. */
.ph-mark {
  display: flex;
  align-items: center;
  gap: 8px;
  min-height: 44px;
  font-size: 16px;
  font-weight: 600;
  color: var(--mut);
  margin-top: 12px;
}
.ph-why {
  font-size: 12px;
  font-weight: 600;
  line-height: 1.2;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--mut);
  margin-top: 16px;
}
/* Текст дня — единственное длинное чтение вкладки; 16/1.6, тот же кегль, что у глав
   «Стратегии» (в плане он назван «17, как в главах» — в главах фактически 16, и своего
   размера ради одного экрана система не заводит). */
.ph-txt {
  font-size: 16px;
  line-height: 1.6;
  margin-top: 8px;
}
.ph-txt p {
  margin: 0 0 12px;
}
.ph-txt p:last-child {
  margin-bottom: 0;
}
.ph-txt b {
  font-weight: 600;
}
.ph-txt i {
  color: var(--mut);
}
/* Пронумерованный шаг маршрута по приложению. Номер приходит из базы атрибутом data-n
   (<p class="st" data-n="1">) — его ставит заливка Пути, здесь только оформление. */
.ph-txt p.st {
  display: flex;
  gap: 8px;
  align-items: flex-start;
  line-height: 1.5;
  margin: 0 0 8px;
}
.ph-txt p.st::before {
  content: attr(data-n);
  flex: none;
  width: 24px;
  height: 24px;
  border-radius: var(--r-full);
  background: color-mix(in srgb, var(--p) 24%, transparent);
  color: var(--p);
  font-size: 12px;
  font-weight: 600;
  line-height: 24px;
  text-align: center;
}
.ph-hero .btn,
.ph-start .btn {
  margin-top: 12px;
}
.strk-hd {
  display: flex;
  align-items: center;
  gap: 8px;
}
.strk-hd .fl {
  color: var(--acc);
  display: flex;
}
.strk-hd .fl.off {
  color: var(--mut);
}
.strk-hd .t {
  flex: 1;
  font-size: 16px;
  font-weight: 600;
}
.strk-hd .t.zero {
  font-size: 14px;
  font-weight: 400;
  color: var(--mut);
}
.strk-hd .b {
  font-size: 12px;
  color: var(--mut);
}
.strk-msg {
  font-size: 14px;
  line-height: 1.45;
  margin-top: 12px;
}
.strk-msg .ok {
  color: var(--rgrn);
}
.ph-done {
  font-size: 20px;
  font-weight: 600;
  line-height: 1.3;
  margin-top: 12px;
}
.ph-fin {
  margin-top: 8px;
}
/* сегодняшняя клетка календаря серии, пока не отвечена. Путь v4: обводка --mut, а не
   акцентом, — акцентом теперь обведён ответ «Нет» (.cal .n), и два разных состояния одним
   цветом не различались бы. */
.cal .td {
  box-shadow: inset 0 0 0 1.5px var(--mut);
}
/* ответил «Нет»: день засчитан, задание не сделано — контур вместо заливки */
.cal .n {
  background: var(--card2);
  box-shadow: inset 0 0 0 1.5px var(--acc);
}
/* строка заморозок — кнопка во всю ширину: тап открывает подсказку fzTip() */
.fzl {
  display: flex;
  align-items: center;
  gap: 8px;
  width: 100%;
  min-height: 32px;
  margin-top: 12px;
  padding: 0;
  background: none;
  border: 0;
  font: inherit;
  font-size: 12px;
  color: var(--mut);
  text-align: left;
}
.fzl > svg:first-child {
  color: var(--blu);
  flex: none;
}
.fzl.off > svg:first-child {
  color: var(--mut);
}
.fzl span {
  flex: 1;
}
/* закрытая награда в листе: замок иконкой вместо эмодзи */
.rd-card {
  background: var(--card);
  border-radius: var(--r-m);
  padding: 16px;
  margin: 10px 0;
}
.rd-hd {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
}
.rd-hd b {
  font-size: 16px;
  font-weight: 600;
}
.rd-dn {
  font-size: 12px;
  color: var(--mut);
  white-space: nowrap;
}
.rd-dn b {
  font-size: 12px;
  color: var(--txt);
  font-weight: 600;
}
.rd-chart {
  background: var(--card2);
  border-radius: var(--r-m);
  overflow: hidden;
}
.rd-chart svg {
  display: block;
  width: 100%;
  height: auto;
}
.rd-leg {
  display: flex;
  gap: 16px;
  align-items: center;
  font-size: 12px;
  color: var(--mut);
  margin-top: 10px;
}
.rd-leg i {
  width: 8px;
  height: 8px;
  border-radius: 50%;
  display: inline-block;
  margin-right: 6px;
  vertical-align: -1px;
}
.rd-ms {
  margin-left: auto;
}
.rd-ms.on {
  color: var(--acc);
}
.rd-hint {
  display: none;
  font-size: 12px;
  color: var(--mut);
  text-align: center;
  margin-top: 8px;
}
.rd-low .rd-hint {
  display: block;
}
.rdpulse {
  transform-box: fill-box;
  transform-origin: center;
  animation: rdpu 2.4s cubic-bezier(0.4, 0, 0.2, 1) infinite;
}
@keyframes rdpu {
  0% {
    transform: scale(0.6);
    opacity: 0.55;
  }
  75%,
  100% {
    transform: scale(2.4);
    opacity: 0;
  }
}
@media (prefers-reduced-motion: reduce) {
  .rdpulse {
    animation: none;
    opacity: 0;
  }
}
#app {
  position: relative;
}
/* 🚨 Габариты ровно как у круглых кнопок верхней зоны — 44×44 на --top-btn. Иконка профиля
   и стрелка «назад» подэкрана видны на экране ОДНОВРЕМЕННО (стрелка слева, аватар справа),
   и их центры обязаны лежать на одной горизонтали: 48-я иконка опускала центр на 2px, и
   перекос было видно глазом (замечено владельцем 01.08.2026). */
.avbtn {
  position: absolute;
  right: 14px;
  top: var(--top-btn);
  z-index: var(--z-av);
  width: 44px;
  height: 44px;
  display: none;
  align-items: center;
  justify-content: center;
  border: none;
  background: none;
  padding: 0;
}
/* Подпись висит ПОД кнопкой и вне потока: попади она внутрь, кнопка стала бы выше стрелки
   и линия снова разъехалась бы. Место под неё даёт верхний отступ .wrap. */
.avlbl {
  position: absolute;
  top: 100%;
  left: 50%;
  transform: translateX(-50%);
  margin-top: 2px;
  font-size: 12px;
  line-height: 1;
  color: var(--mut);
}
.av {
  width: 44px;
  height: 44px;
  color: #ccccce;
  display: flex;
  align-items: center;
  justify-content: center;
  transition: transform 0.15s;
}
.avbtn:active .av {
  transform: scale(0.9);
}
.av svg {
  display: block;
}
.pfscrim {
  position: fixed;
  inset: 0;
  background: rgba(0, 0, 0, 0.55);
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.25s;
  z-index: var(--z-scrim);
}
.pfscrim.on {
  opacity: 1;
  pointer-events: auto;
}
.drawer {
  position: fixed;
  top: 0;
  right: 0;
  bottom: 0;
  width: 86%;
  max-width: 360px;
  background: var(--sheet);
  z-index: var(--z-drawer);
  transform: translateX(100%);
  transition: transform 0.28s cubic-bezier(0.4, 0, 0.2, 1);
  display: flex;
  flex-direction: column;
  box-shadow: -12px 0 40px rgba(0, 0, 0, 0.5);
}
.drawer.on {
  transform: translateX(0);
}
.dhead {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: var(--top-btn) 14px 4px;
}
.dhead .miniback {
  margin-bottom: 0;
}
.dhead b {
  font-size: 20px;
  font-weight: 600;
}
/* Стек уровней панели. Уровни лежат друг на друге и сменяются сдвигом, как в нативных
   настройках: корень уезжает влево и притухает, вложенный приезжает справа.
   У каждого уровня СВОЙ скролл — поэтому, вернувшись из справочника, человек видит
   профиль там же, где его оставил, и запоминать позицию руками (как NAVY) не нужно. */
.dstack {
  position: relative;
  flex: 1;
  overflow: hidden;
}
.dlvl {
  position: absolute;
  inset: 0;
  overflow-y: auto;
  -webkit-overflow-scrolling: touch;
  overscroll-behavior: contain;
  padding: 6px 16px calc(16px + var(--sab));
  transform: translateX(100%);
  opacity: 0;
  pointer-events: none;
  transition:
    transform 0.25s cubic-bezier(0.4, 0, 0.2, 1),
    opacity 0.25s;
}
.dlvl.cur {
  transform: none;
  opacity: 1;
  pointer-events: auto;
}
.dlvl.back {
  transform: translateX(-28%);
  opacity: 0.35;
}
@media (prefers-reduced-motion: reduce) {
  .dlvl {
    transition: none;
  }
}
/* Строка настроек: иконка, заголовок с подписью, значение, шеврон. Строки живут группами
   в одной карточке с разделителями — так список читается быстрее, чем колонка кнопок,
   и в него влезает втрое больше при той же высоте экрана. */
.gl {
  color: var(--mut);
  font-size: 12px;
  padding: 0 2px 6px;
  margin-top: 12px;
}
/* 12 снизу + 12 сверху у подписи = 24 между секциями, как велит шкала отступов.
   Группа без подписи (удаление данных, выход) отделяется теми же 12. */
.grp {
  background: var(--card);
  border-radius: var(--r-m);
  overflow: hidden;
  margin-bottom: 12px;
}
.prow {
  display: flex;
  align-items: center;
  gap: 12px;
  width: 100%;
  background: none;
  border: none;
  color: var(--txt);
  text-align: left;
  font-size: 14px;
  font-weight: 400;
  line-height: 1.35;
  padding: 11px 14px;
  min-height: 48px;
  border-bottom: 0.5px solid var(--sep);
}
.grp > *:last-child {
  border-bottom: none;
}
.prow:active {
  background: var(--card2);
}
.prow.red,
.prow.red .ri {
  color: var(--red);
}
.ri {
  width: 22px;
  min-width: 22px;
  display: flex;
  color: var(--acc);
}
.rt {
  flex: 1;
}
.rt em {
  font-style: normal;
  display: block;
  font-size: 12px;
  color: var(--mut);
  margin-top: 1px;
  font-weight: 400;
}
.rv {
  color: var(--mut);
  flex: none;
}
.rc {
  color: var(--mut);
  display: flex;
  flex: none;
}
/* Превью темы в строке выбора: кнопка с тёмной надписью, кольцо прогресса и активная
   вкладка — три места, где акцент виден больше всего. Цвета внутри задаются инлайном
   из THEMES: в списке показаны все четыре темы сразу, поэтому var(--acc) тут не годится. */
.thpv {
  display: flex;
  align-items: center;
  gap: 5px;
  flex: none;
  width: 62px;
}
.thpv i {
  display: block;
}
.thpb {
  width: 22px;
  height: 14px;
  border-radius: 4px;
  display: flex;
  align-items: center;
  justify-content: center;
}
.thpb b {
  width: 11px;
  height: 2px;
  border-radius: 1px;
}
.thpr {
  width: 14px;
  height: 14px;
  border-radius: 50%;
  border: 3px solid;
  box-sizing: border-box;
}
.thpt {
  display: flex;
  align-items: center;
}
.throw .rt {
  flex: 1;
}
.thok {
  color: var(--acc);
  display: flex;
  flex: none;
}
.rx {
  color: var(--mut);
  flex: none;
  font-size: 15px;
}
/* «Моя семья». Строка человека собрана на том же компоненте, что панель профиля
   (.grp/.prow/.rt/.rv/.rc) — отсюда всего три добавки. Имя famav, а не av: .av уже занят
   аватаром профиля. */
.rt b {
  display: block;
  font-size: 15px;
  font-weight: 600;
}
.famav {
  flex: none;
  width: 38px;
  height: 38px;
  border-radius: 50%;
  background: var(--card2);
  color: var(--txt);
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 16px;
  font-weight: 600;
}
/* Незаконченная настройка — НЕ ошибка, поэтому акцент, а не --red: красный в системе
   означает сбой. Цвет не единственный носитель — рядом всегда текст «Не хватает: …». */
.famav.wait {
  background: var(--accg);
  color: var(--acc);
}
.tag {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  flex: none;
  font-size: 12px;
  font-weight: 600;
  padding: 3px 9px;
  border-radius: var(--r-full);
}
.tag.wait {
  background: var(--accg);
  color: var(--acc);
}
.tag.ok {
  background: rgba(48, 209, 88, 0.14);
  color: var(--grn);
}
.seg {
  display: flex;
  gap: 8px;
  padding: 0 14px 12px;
}
.pfhead {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 14px;
  border-bottom: 0.5px solid var(--sep);
}
.pfhead b {
  font-size: 16px;
  font-weight: 600;
}
.pfav {
  width: 54px;
  height: 54px;
  min-width: 54px;
  border-radius: 50%;
  background: var(--card2);
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 20px;
}
.socbox {
  padding: 12px 14px;
}
.socrow {
  display: flex;
  gap: 10px;
  justify-content: space-between;
}
.soc {
  flex: 1;
  aspect-ratio: 1;
  border-radius: var(--r-m);
  background: var(--card2);
  display: flex;
  align-items: center;
  justify-content: center;
  border: none;
}
.soc svg {
  width: 22px;
  height: 22px;
  display: block;
}
.cam {
  position: fixed;
  inset: 0;
  z-index: var(--z-cam);
  background: #000;
  display: none;
}
.cam.on {
  display: block;
}
.camv {
  position: absolute;
  top: 50%;
  left: 0;
  right: 0;
  transform: translateY(-50%);
  width: 100%;
  aspect-ratio: 3/4;
  overflow: hidden;
  background: #000;
}
.camv video,
.camv .cg {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
}
.camv video {
  transform: scaleX(-1);
}
.camv .cg {
  opacity: 0.35;
  transform: scaleX(-1);
  pointer-events: none;
}
.camgrid {
  position: absolute;
  inset: 0;
  pointer-events: none;
  opacity: 0.4;
  background:
    linear-gradient(#fff2 1px, transparent 1px) 0 0/100% 33.33%,
    linear-gradient(90deg, #fff2 1px, transparent 1px) 0 0/33.33% 100%;
}
.camcnt {
  position: absolute;
  inset: 0;
  display: none;
  align-items: center;
  justify-content: center;
  font-size: 90px;
  font-weight: 700;
  color: #fff;
  text-shadow: 0 2px 20px #000a;
}
.camx {
  position: absolute;
  top: var(--top-btn);
  left: 12px;
  z-index: 2;
  width: 44px;
  height: 44px;
  border-radius: 50%;
  background: #0008;
  color: #fff;
  border: 0;
  font-size: 20px;
}
.camhint {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 12px;
  text-align: center;
  color: #fff;
  font-size: 13px;
  text-shadow: 0 1px 6px #000a;
  pointer-events: none;
  padding: 0 20px;
}
.cambar {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  background: #000;
  padding: 16px 20px calc(16px + var(--sab));
}
.camseg {
  display: flex;
  gap: 6px;
}
.camseg button {
  padding: 8px 14px;
  border-radius: 999px;
  border: 0;
  background: #222;
  color: #fff;
  font-size: 14px;
}
.camseg button.on {
  background: var(--acc);
}
/* Затвор: тёмный зазор между заливкой и внешним ободком — как в камере iOS. Раньше
   обводка была белой, но на светлом акценте белое кольцо с ним сливается (1.5:1),
   и кнопка превращалась в сплошное пятно. Чёрный зазор берёт цвет панели (#000),
   ободок — акцент, поэтому форма затвора читается на любой теме. */
.camshot {
  width: 68px;
  height: 68px;
  border-radius: 50%;
  border: 4px solid #000;
  background: var(--acc);
  box-shadow: 0 0 0 2px var(--acc);
}
.camsp {
  width: 78px;
}
.bcam {
  position: fixed;
  inset: 0;
  z-index: var(--z-cam-top);
  background: #000;
  display: none;
}
.bcam.on {
  display: block;
}
.bcam video {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
}
.bcf {
  position: absolute;
  left: 50%;
  top: 44%;
  transform: translate(-50%, -50%);
  width: 84vw;
  max-width: 440px;
  aspect-ratio: 1.55/1;
  border-radius: 14px;
  box-shadow: 0 0 0 100vmax rgba(0, 0, 0, 0.55);
  pointer-events: none;
}
.bcf b {
  position: absolute;
  width: 22px;
  height: 22px;
  border: 3px solid var(--acc);
}
.bcf b:nth-child(1) {
  left: -2px;
  top: -2px;
  border-right: 0;
  border-bottom: 0;
  border-radius: 12px 0 0 0;
}
.bcf b:nth-child(2) {
  right: -2px;
  top: -2px;
  border-left: 0;
  border-bottom: 0;
  border-radius: 0 12px 0 0;
}
.bcf b:nth-child(3) {
  left: -2px;
  bottom: -2px;
  border-right: 0;
  border-top: 0;
  border-radius: 0 0 0 12px;
}
.bcf b:nth-child(4) {
  right: -2px;
  bottom: -2px;
  border-left: 0;
  border-top: 0;
  border-radius: 0 0 12px 0;
}
.bcg {
  position: absolute;
  left: 9%;
  right: 9%;
  top: 15%;
  bottom: 32%;
  opacity: 0.28;
  background: repeating-linear-gradient(
    90deg,
    #fff 0 3px,
    transparent 3px 7px,
    #fff 7px 9px,
    transparent 9px 15px,
    #fff 15px 20px,
    transparent 20px 24px
  );
}
.bcn {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 7%;
  text-align: center;
  color: #fff;
  opacity: 0.32;
  font-size: 15px;
  letter-spacing: 3px;
  font-family: ui-monospace, monospace;
}
.bch {
  position: absolute;
  left: 0;
  right: 0;
  bottom: calc(122px + var(--sab));
  z-index: 2;
  text-align: center;
  color: #fff;
  font-size: 14px;
  line-height: 1.4;
  text-shadow: 0 1px 6px #000a;
  padding: 0 26px;
  pointer-events: none;
}
.bctor {
  padding: 9px 13px;
  border-radius: 999px;
  border: 0;
  background: #222;
  color: #fff;
  font-size: 13px;
}
/* Кнопка галереи в видоискателе — левый нижний угол, как в камере айфона (04.08.2026).
   Цвета — токенами: в обычном CSS var() резолвится, новых hex заводить незачем. Соседняя
   .bctor держит литералы по историческим причинам, повторять это не надо. */
.bcgal {
  width: 44px;
  height: 44px;
  border-radius: 50%;
  border: 0;
  background: var(--card2);
  color: var(--txt);
  display: flex;
  align-items: center;
  justify-content: center;
}
.bctor.on {
  background: var(--acc);
}
.ba {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-top: 8px;
}
.ba figure {
  margin: 0;
  flex: 1;
  text-align: center;
}
.ba .baimg {
  width: 100%;
  border-radius: 12px;
  display: block;
  aspect-ratio: 3/4;
  background-size: cover;
  background-position: center;
}
.ba figcaption {
  font-size: 12px;
  color: var(--mut);
  margin-top: 4px;
}
.baar {
  color: var(--mut);
  font-size: 20px;
  flex: none;
}
.basum {
  margin-top: 10px;
  font-size: 16px;
  font-weight: 600;
  text-align: center;
}
.galg {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: 10px;
  margin-top: 8px;
}
.galdt {
  font-size: 11px;
  color: var(--mut);
  text-align: center;
  margin-top: 4px;
}
.galc {
  position: relative;
}
.galc .galimg {
  width: 100%;
  border-radius: 10px;
  display: block;
  aspect-ratio: 3/4;
  background-size: cover;
  background-position: center;
}
.galb {
  position: absolute;
  left: 4px;
  bottom: 4px;
  font-size: 10px;
  background: #000a;
  color: #fff;
  padding: 1px 6px;
  border-radius: 999px;
}
.galstrip {
  text-align: center;
  font-size: 13px;
  color: var(--mut);
  margin: 0 0 10px;
  line-height: 1.5;
}
.galstrip b {
  color: var(--txt);
  font-weight: 600;
}
.lbback {
  position: fixed;
  top: var(--top-btn);
  left: 16px;
  z-index: var(--z-lb-btn);
  width: 44px;
  height: 44px;
  border-radius: 50%;
  background: rgba(120, 120, 128, 0.4);
  color: #fff;
  border: 0;
  display: flex;
  align-items: center;
  justify-content: center;
}
.lbdel {
  position: fixed;
  top: var(--top-btn);
  right: 16px;
  z-index: var(--z-lb-btn);
  width: 44px;
  height: 44px;
  border-radius: 50%;
  background: rgba(255, 69, 58, 0.9);
  color: #fff;
  border: 0;
  font-size: 20px;
  display: none;
  align-items: center;
  justify-content: center;
}
.cmp {
  position: fixed;
  inset: 0;
  z-index: var(--z-cam-top);
  background: #000;
  display: none;
  flex-direction: column;
}
.cmp.on {
  display: flex;
}
.cmpstage {
  position: relative;
  flex: 1;
  overflow: hidden;
  display: flex;
  align-items: center;
  justify-content: center;
  /* ниже кнопки «назад»: её верх --top-btn, высота 44, плюс 8 зазора */
  padding-top: calc(var(--top-btn) + 52px);
}
.cmpside {
  display: flex;
  gap: 8px;
  width: 100%;
  padding: 0 12px;
}
.cmpside figure {
  margin: 0;
  flex: 1;
}
.cmpside .cmpsimg {
  width: 100%;
  aspect-ratio: 3/4;
  background-size: cover;
  background-position: center;
  border-radius: 12px;
  display: block;
}
.cmpcap {
  text-align: center;
  color: #fff;
  font-size: 12px;
  margin-top: 6px;
}
.cmpwrap {
  position: relative;
  width: 100%;
  max-width: 520px;
  aspect-ratio: 3/4;
  overflow: hidden;
  background: #000;
  touch-action: none;
}
.cmpwrap .cmpwimg {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  background-size: cover;
  background-position: center;
  display: block;
}
.cmpcurt {
  position: absolute;
  inset: 0;
  overflow: hidden;
}
.cmphandle {
  position: absolute;
  top: 0;
  bottom: 0;
  left: 50%;
  width: 2px;
  background: #fff;
  z-index: 3;
  pointer-events: none;
}
.cmphandle::after {
  content: '\2194';
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  width: 40px;
  height: 40px;
  border-radius: 50%;
  background: #fff;
  color: #000;
  font-size: 20px;
  display: flex;
  align-items: center;
  justify-content: center;
  box-shadow: 0 1px 6px #0008;
}
.cmptag {
  position: absolute;
  top: 8px;
  z-index: 4;
  font-size: 11px;
  background: #000a;
  color: #fff;
  padding: 2px 8px;
  border-radius: 8px;
}
.cmprange {
  position: absolute;
  left: 16px;
  right: 16px;
  bottom: 12px;
  width: auto;
  z-index: 4;
}
.cmpbar {
  background: #000;
  padding: 12px 16px calc(12px + var(--sab));
  display: flex;
  flex-direction: column;
  gap: 10px;
  align-items: center;
}
.cmpsum {
  color: #fff;
  font-size: 16px;
  font-weight: 600;
  min-height: 20px;
  text-align: center;
}
.cmpshare {
  width: 100%;
  height: 44px;
  border: 0;
  border-radius: 12px;
  background: var(--acc);
  font-size: 16px;
  font-weight: 600;
}
.mdone {
  position: fixed;
  top: var(--top-btn);
  right: 12px;
  z-index: var(--z-mdone);
  display: none;
  align-items: center;
  height: 36px;
  padding: 0 16px;
  border: 0;
  border-radius: 999px;
  background: var(--acc);
  font-size: 14px;
  font-weight: 600;
}
body.kb .mdone {
  display: flex;
}
.dishwrap {
  margin: 10px 0;
  border-radius: 14px;
  overflow: hidden;
  background: var(--card2);
  aspect-ratio: 1/1;
}
.dishimg {
  display: block;
  width: 100%;
  height: 100%;
  background-size: cover;
  background-position: center;
  cursor: pointer;
}
/* Первый экран: развилка из трёх путей входа (07.08.2026). Карточка-путь — тот же
   .card, поэтому фон, радиус и отступ берутся оттуда; здесь только раскладка. */
.lksub {
  font-size: 14px;
  line-height: 1.5;
  color: var(--mut);
  margin: 8px 0 0;
}
/* Карточка промокода — первая на экране: у кого код есть, вводит сразу, без лишнего тапа. */
.lkpromo {
  margin-top: 24px;
}
/* Карточка истёкшей подписки: поле идёт следом за пояснением, ему нужен отступ. */
.lkpromo2 .inp {
  margin-top: 12px;
}
.lkplab {
  font-size: 14px;
  font-weight: 600;
  color: var(--txt);
  margin-bottom: 12px;
}
/* Разделитель «или»: линия — слева и справа от слова, поэтому псевдоэлементы, а не border. */
.lkor {
  display: flex;
  align-items: center;
  gap: 12px;
  margin: 24px 0;
  font-size: 12px;
  color: var(--mut);
}
.lkor::before,
.lkor::after {
  content: '';
  flex: 1;
  height: 1px;
  background: var(--sep);
}
.lkways {
  margin-top: 0;
}
.lkway {
  display: flex;
  align-items: center;
  gap: 12px;
  width: 100%;
  margin: 0 0 12px;
  padding: 16px;
  border: none;
  text-align: left;
  font-family: inherit;
  transition: transform 0.12s;
}
.lkway:active {
  transform: scale(0.98);
}
.lkwico {
  flex: none;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 44px;
  height: 44px;
  border-radius: var(--r-full);
  background: var(--accg);
  color: var(--acc);
}
.lkwtx {
  flex: 1;
  min-width: 0;
}
.lkwtx b {
  display: block;
  font-size: 16px;
  font-weight: 600;
  color: var(--txt);
}
.lkwtx i {
  display: block;
  margin-top: 4px;
  font-size: 12px;
  font-weight: 400;
  font-style: normal;
  line-height: 1.4;
  color: var(--mut);
}
.lkwch {
  flex: none;
  display: flex;
  color: var(--mut);
  transform: rotate(180deg);
}
.lknote {
  margin-top: 12px;
  font-size: 14px;
  line-height: 1.5;
  color: var(--mut);
}

/* ── Стратегия (В2 редизайна, 30.08.2026): список глав и книжное чтение ────────────── */
.cn {
  width: 32px;
  height: 32px;
  min-width: 32px;
  border-radius: var(--r-full);
  border: 1.5px solid var(--sep);
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 14px;
  font-weight: 600;
  color: var(--txt);
  box-sizing: border-box;
}
.cn.cur {
  border-color: var(--acc);
  border-width: 2px;
  color: var(--acc);
}
/* заливка акцентом ⇒ текст --accd — групповое правило за .btn это тоже красит */
.cn.done {
  background: var(--acc);
  border-color: var(--acc);
}
.cn.lock {
  color: var(--mut);
  border-style: dashed;
}
.cn.ic {
  border: 0;
  background: var(--card2);
  color: var(--acc);
}
.prow.off {
  opacity: 0.55;
}
.cs-hero .crspic {
  margin: -16px -16px 12px;
  width: calc(100% + 32px);
  max-width: calc(100% + 32px);
  border-radius: var(--r-m) var(--r-m) 0 0;
}
.crspic {
  display: block;
  width: 100%;
  aspect-ratio: 3 / 2;
  object-fit: cover;
  border-radius: var(--r-m);
  background: var(--card2);
  margin: 0 0 12px;
}
.cs-t {
  font-size: 20px;
  font-weight: 600;
  line-height: 1.25;
  margin-top: 4px;
}
.cs-x {
  font-size: 16px;
  line-height: 1.5;
  margin-top: 8px;
}
.cjb {
  display: flex;
  gap: 3px;
  height: 6px;
  margin-top: 12px;
}
.cjb i {
  display: block;
  flex: 1;
  height: 6px;
  border-radius: var(--r-full);
  background: var(--sep);
  position: relative;
  overflow: hidden;
  min-width: 4px;
}
.cjb i.d {
  background: var(--acc);
}
.cjb i b {
  position: absolute;
  left: 0;
  top: 0;
  bottom: 0;
  border-radius: var(--r-full);
  display: block;
  background: var(--acc);
}
.cs-hero .btn {
  margin-top: 12px;
}
.crserr {
  text-align: center;
  padding: 28px 16px;
}
.crserr .ce-i {
  color: var(--mut);
  margin-bottom: 8px;
}
.crserr .ce-t {
  font-size: 16px;
  line-height: 1.5;
  margin-bottom: 4px;
}
.skel-pic {
  aspect-ratio: 3 / 2;
  border-radius: var(--r-m) var(--r-m) 0 0;
  margin: -16px -16px 12px;
  background: var(--card2);
}
/* Шапка чтения — фиксированная (исключение «стрелки в потоке»: полоса прочитанного обязана
   быть видна всегда, это индикатор «сколько осталось»; принято владельцем с макетом 30.08).
   Верх — только от --top-btn (голый env() не знает про шапку Telegram, скилл §1). */
.rdh {
  position: fixed;
  left: 0;
  right: 0;
  top: 0;
  z-index: 2;
  max-width: 520px;
  margin: 0 auto;
  padding: var(--top-btn) 12px 0;
  background: var(--sheet);
  display: flex;
  align-items: center;
  gap: 8px;
  flex-wrap: wrap;
}
.rdh .miniback {
  margin-bottom: 0;
}
.rdk {
  flex: 1;
  min-width: 0;
}
.rdk .kick {
  display: block;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.rdaa {
  width: 44px;
  height: 44px;
  border: 0;
  border-radius: 50%;
  background: none;
  display: flex;
  align-items: center;
  justify-content: center;
  color: var(--mut);
}
.rdaa.on {
  color: var(--acc);
  background: var(--accg);
}
.rdp {
  display: block;
  width: 100%;
  height: 3px;
  background: var(--sep);
  border-radius: var(--r-full);
  margin: 8px 0 0;
  overflow: hidden;
}
.rdp b {
  display: block;
  height: 100%;
  background: var(--acc);
  border-radius: var(--r-full);
}
/* контент листа чтения отступает от фиксированной шапки: --top-btn + 44 кнопка + 8 + 3 полоса + 12 */
.rdc {
  padding-top: calc(var(--top-btn) + 67px);
}
.rt1 {
  font-size: 20px;
  font-weight: 600;
  line-height: 1.25;
  margin: 4px 0 8px;
}
.crsmeta {
  margin-bottom: 4px;
}
.rdx {
  font-size: 16px;
  line-height: 1.6;
  color: var(--txt);
}
/* 18/400 — шестой размер шкалы: крупное чтение по кнопке Aa (ответ владельца §17-3;
   масштаб страницы запрещён, поэтому размер даём кнопкой) */
.rdx.big {
  font-size: 18px;
}
.rdx p {
  margin: 0 0 16px;
}
.rdx b {
  font-weight: 600;
}
/* Врезка про функцию приложения: 600 и воздух 24 сверху/снизу вместо обычных 16 снизу —
   такой абзац обязан читаться отдельным блоком, а не жирным куском в потоке чтения
   (решение владельца 30.08.2026, он читал главы с телефона). Разметку ставит заливка
   контента: «>> » в начале абзаца → <p class="fx">, tools/course_v2_sql.js. */
.rdx .fx {
  margin: 24px 0;
  font-weight: 600;
  font-style: italic;
}
.rdx .fx:first-child {
  margin-top: 0;
}
.rdx i {
  color: var(--mut);
}
.rdx a {
  color: inherit;
  text-decoration: underline dotted var(--mut);
  text-underline-offset: 3px;
}
.rsm {
  display: flex;
  align-items: center;
  gap: 8px;
  font-size: 12px;
  font-weight: 600;
  color: var(--acc);
  margin: 0 0 12px;
  padding: 8px 0;
  border-top: 1px dashed var(--accb);
}
.crsvw {
  position: relative;
  margin: 0 0 12px;
}
.crsvw .pv {
  position: absolute;
  left: 50%;
  top: 50%;
  transform: translate(-50%, -50%);
  width: 56px;
  height: 56px;
  border-radius: 50%;
  background: var(--scrim);
  color: var(--wht);
  display: flex;
  align-items: center;
  justify-content: center;
  font-style: normal;
  pointer-events: none;
}
.crsvw .pvd {
  position: absolute;
  left: 12px;
  bottom: 8px;
  font-size: 12px;
  font-weight: 600;
  font-style: normal;
  color: var(--wht);
  background: var(--scrim);
  border-radius: var(--r-full);
  padding: 4px 8px;
}
.crsvw.playing .pv,
.crsvw.playing .pvd {
  display: none;
}
.crsvid {
  display: block;
  width: 100%;
  aspect-ratio: 16 / 9;
  border-radius: var(--r-m);
  background: var(--card2);
}
.brg {
  background: var(--card2);
  border-radius: var(--r-m);
  padding: 16px;
  margin: 8px 0 12px;
}
.brg > .kick {
  color: var(--acc);
}
.pchs {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  margin: 8px 0 12px;
}
.pch {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  font-size: 12px;
  font-weight: 600;
  padding: 4px 12px 4px 8px;
  border-radius: var(--r-full);
  background: var(--card);
}
.pch i {
  width: 8px;
  height: 8px;
  border-radius: var(--r-full);
  background: var(--pc, var(--acc));
  display: inline-block;
  flex: none;
  font-style: normal;
}
.brg-x {
  font-size: 16px;
  line-height: 1.5;
}
.brg-go {
  display: flex;
  align-items: center;
  gap: 8px;
  width: 100%;
  margin-top: 12px;
  min-height: 44px;
  font-size: 14px;
  font-weight: 600;
  font-family: inherit;
  color: var(--txt);
  background: none;
  border: 0;
  border-top: 0.5px solid var(--sep);
  padding: 8px 0 0;
  text-align: left;
}
.brg-go svg:first-child {
  color: var(--acc);
  flex: none;
}
.brg-go span {
  flex: 1;
}
.brg-go svg:last-child {
  color: var(--mut);
  flex: none;
}
.crstask .tk-p {
  font-size: 16px;
  font-weight: 600;
  margin-top: 4px;
}
.crstask .tk-x {
  font-size: 14px;
  line-height: 1.5;
  margin-top: 8px;
}
.crstask .btn {
  margin-top: 12px;
}
.tk {
  display: flex;
  align-items: center;
  gap: 12px;
  width: 100%;
  background: none;
  border: 0;
  padding: 12px 0 0;
  min-height: 44px;
  color: var(--txt);
  font-family: inherit;
  font-size: 14px;
  text-align: left;
}
.tkb {
  width: 24px;
  height: 24px;
  min-width: 24px;
  border-radius: var(--r-s);
  border: 1.5px solid var(--mut);
  display: flex;
  align-items: center;
  justify-content: center;
  box-sizing: border-box;
}
/* заливка акцентом ⇒ текст --accd (галочку красит групповое правило за .btn) */
.tk.on .tkb {
  background: var(--acc);
  border-color: var(--acc);
}
.tk.on span:last-child {
  color: var(--mut);
}
/* ── Стратегия, В3 30.08.2026: тест-самопроверка и итог ── */
.qq {
  font-size: 20px;
  font-weight: 600;
  line-height: 1.3;
  margin: 8px 0 16px;
}
.qo {
  display: flex;
  align-items: center;
  gap: 12px;
  width: 100%;
  background: var(--card2);
  border: 1.5px solid transparent;
  border-radius: var(--r-m);
  padding: 12px 16px;
  margin: 0 0 8px;
  color: var(--txt);
  font-family: inherit;
  font-size: 16px;
  line-height: 1.35;
  text-align: left;
  min-height: 56px;
  box-sizing: border-box;
}
.qol {
  font-size: 12px;
  font-weight: 600;
  color: var(--mut);
  width: 16px;
  flex: none;
}
.qot {
  flex: 1;
}
.qom {
  flex: none;
  width: 20px;
  display: flex;
  justify-content: center;
  color: var(--mut);
  font-size: 16px;
}
/* верно — только зелёная обводка; --red запрещён: ошибка человека — не ошибка приложения */
.qo.ok {
  border-color: var(--grn);
}
.qo.ok .qom {
  color: var(--grn);
}
.qo.no {
  border-color: var(--sep);
  color: var(--mut);
}
.qo.dim {
  opacity: 0.55;
}
/* опросник: выбранный вариант — акцентная заливка, «правильных» подсветок нет */
.qo.sel {
  background: var(--accg);
  border-color: var(--accb);
}
.qo.sel .qom {
  color: var(--acc);
}
.qx {
  background: var(--accg);
  border: 1px solid var(--accb);
  border-radius: var(--r-m);
  padding: 12px 16px;
  margin: 8px 0 4px;
}
.qx > .kick {
  color: var(--acc);
}
.qx-t {
  font-size: 16px;
  line-height: 1.5;
  margin-top: 4px;
}
.crsqh {
  text-align: center;
  margin-top: 12px;
}
.qres {
  text-align: center;
  padding: 16px 0 8px;
}
.qok {
  width: 88px;
  height: 88px;
  border-radius: var(--r-full);
  background: var(--accg);
  border: 2px solid var(--accb);
  color: var(--acc);
  display: flex;
  align-items: center;
  justify-content: center;
  margin: 0 auto 12px;
}
.qres .k {
  font-size: 12px;
  color: var(--acc);
  font-weight: 600;
  letter-spacing: 0.08em;
  text-transform: uppercase;
}
.qres .t {
  font-size: 28px;
  font-weight: 700;
  letter-spacing: -0.5px;
  margin: 4px 0 8px;
  line-height: 1.2;
}
.qres .x {
  font-size: 16px;
  line-height: 1.5;
}
/* ── Стратегия, В4 30.08.2026: опросник старта ── */
.crsqm {
  margin: 4px 0 0;
}
.crsqm svg {
  vertical-align: -3px;
}
.qzring {
  position: relative;
  margin: 0 auto 12px;
}
.qzrow .qzring {
  margin: 0;
  flex: none;
}
.qzn {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 4px;
}
.qzn b {
  font-size: 28px;
  font-weight: 700;
  letter-spacing: -0.5px;
}
.qzn span {
  font-size: 14px;
  color: var(--mut);
  margin-top: 8px;
}
.qzrow {
  display: flex;
  align-items: center;
  gap: 16px;
}
.qzrow .qzn b {
  font-size: 16px;
  font-weight: 600;
}
.qzrow .qzn span {
  display: none;
}
.qzbig {
  font-size: 20px;
  font-weight: 600;
}
.qzrx {
  margin-top: 8px;
}
.qzli {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 12px 0 4px;
  font-size: 14px;
}
.agein {
  display: flex;
  align-items: center;
  gap: 12px;
  background: var(--card2);
  border-radius: var(--r-m);
  padding: 0 16px;
  height: 52px;
  margin-top: 8px;
}
.ageu {
  color: var(--mut);
  font-size: 14px;
}
.qzagein {
  flex: 1;
  background: none;
  border: 0;
  color: var(--txt);
  font-family: inherit;
  font-size: 20px;
  font-weight: 600;
  outline: none;
  min-width: 0;
}
